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.

1997 lines
61 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. queue.c
  5. Abstract:
  6. This module implements the jobqueue
  7. Author:
  8. Wesley Witt (wesw) 22-Jan-1996
  9. Revision History:
  10. --*/
  11. #include "faxsvc.h"
  12. #pragma hdrstop
  13. #if DBG
  14. VOID
  15. DebugPrintDateTime(
  16. LPTSTR Heading,
  17. DWORDLONG DateTime
  18. )
  19. {
  20. SYSTEMTIME SystemTime;
  21. TCHAR DateBuffer[256];
  22. TCHAR TimeBuffer[256];
  23. FileTimeToSystemTime( (LPFILETIME) &DateTime, &SystemTime );
  24. GetDateFormat(
  25. LOCALE_SYSTEM_DEFAULT,
  26. 0,
  27. &SystemTime,
  28. NULL,
  29. DateBuffer,
  30. sizeof(TimeBuffer)
  31. );
  32. GetTimeFormat(
  33. LOCALE_SYSTEM_DEFAULT,
  34. 0,
  35. &SystemTime,
  36. NULL,
  37. TimeBuffer,
  38. sizeof(TimeBuffer)
  39. );
  40. if (Heading) {
  41. DebugPrint((TEXT("%s %s %s (GMT)"), Heading, DateBuffer, TimeBuffer));
  42. } else {
  43. DebugPrint((TEXT("%s %s (GMT)"), DateBuffer, TimeBuffer));
  44. }
  45. }
  46. #define PrintJobQueue( str, Queue ) \
  47. { \
  48. PLIST_ENTRY Next; \
  49. PJOB_QUEUE QueueEntry; \
  50. \
  51. Next = (Queue).Flink; \
  52. if ((ULONG_PTR)Next == (ULONG_PTR)&(Queue)) { \
  53. DebugPrint(( TEXT("Queue empty") )); \
  54. } else { \
  55. while ((ULONG_PTR)Next != (ULONG_PTR)&(Queue)) { \
  56. QueueEntry = CONTAINING_RECORD( Next, JOB_QUEUE, ListEntry ); \
  57. Next = QueueEntry->ListEntry.Flink; \
  58. DebugPrint(( \
  59. TEXT("'%s' JobId = %d JobType = %d ScheduleAction = %d ScheduleTime = "), \
  60. (str), \
  61. QueueEntry->JobId, \
  62. QueueEntry->JobType, \
  63. QueueEntry->JobParams.ScheduleAction \
  64. )); \
  65. DebugPrintDateTime( NULL, QueueEntry->ScheduleTime ); \
  66. } \
  67. } \
  68. }
  69. #else
  70. #define PrintJobQueue( str, Queue )
  71. #define DebugPrintDateTime( Heading, DateTime )
  72. #endif
  73. extern ULONG ConnectionCount;
  74. extern BOOL RoutingIsInitialized;
  75. LIST_ENTRY QueueListHead;
  76. LIST_ENTRY RescheduleQueueHead;
  77. CRITICAL_SECTION CsQueue;
  78. DWORD QueueCount;
  79. BOOL QueuePaused;
  80. HANDLE QueueTimer;
  81. HANDLE IdleTimer;
  82. HANDLE JobQueueSemaphore = INVALID_HANDLE_VALUE;
  83. DWORD JobQueueTrips;
  84. DWORD SemaphoreSignaled;
  85. VOID
  86. StartIdleTimer(
  87. VOID
  88. )
  89. {
  90. LARGE_INTEGER DueTime;
  91. if (TerminationDelay == (DWORD)-1) {
  92. return;
  93. }
  94. DueTime.QuadPart = -(LONGLONG)(SecToNano( TerminationDelay ));
  95. SetWaitableTimer( IdleTimer, &DueTime, 0, NULL, NULL, FALSE );
  96. }
  97. VOID
  98. StopIdleTimer(
  99. VOID
  100. )
  101. {
  102. CancelWaitableTimer( IdleTimer );
  103. }
  104. LARGE_INTEGER LastDueTime;
  105. VOID
  106. StartJobQueueTimer(
  107. PJOB_QUEUE JobQueue
  108. )
  109. {
  110. PLIST_ENTRY Next;
  111. PJOB_QUEUE QueueEntry;
  112. SYSTEMTIME CurrentTime;
  113. LARGE_INTEGER DueTime;
  114. BOOL Found = FALSE;
  115. EnterCriticalSection( &CsQueue );
  116. if ((ULONG_PTR) QueueListHead.Flink == (ULONG_PTR) &QueueListHead) {
  117. //
  118. // empty list, cancel the timer
  119. //
  120. CancelWaitableTimer( QueueTimer );
  121. LastDueTime.QuadPart = 0;
  122. StartIdleTimer();
  123. LeaveCriticalSection( &CsQueue );
  124. return;
  125. }
  126. if (!JobQueue) {
  127. if (QueuePaused) {
  128. CancelWaitableTimer( QueueTimer );
  129. LastDueTime.QuadPart = 0;
  130. LeaveCriticalSection( &CsQueue );
  131. return;
  132. }
  133. PrintJobQueue( TEXT("StartJobQueueTimer"), QueueListHead );
  134. //
  135. // set the timer so that the job will get started
  136. //
  137. Next = QueueListHead.Flink;
  138. while ((ULONG_PTR)Next != (ULONG_PTR)&QueueListHead) {
  139. QueueEntry = CONTAINING_RECORD( Next, JOB_QUEUE, ListEntry );
  140. Next = QueueEntry->ListEntry.Flink;
  141. if (QueueEntry->JobType == JT_ROUTING && QueueEntry->SendRetries < FaxSendRetries ) {
  142. Found = TRUE;
  143. break;
  144. }
  145. //
  146. // Don't bother setting the queue timer if we have any jobs in the NOLINE state
  147. // we should just wait for the job queue semaphore to get set, which will allow
  148. // those jobs to be sent.
  149. //
  150. //BUGBUG: You can get into the NO_LINE case if you already have a device sending
  151. // to the number you want to send to. So some jobs will not get serviced until
  152. // that job completes, and there can be a situation where not all of the lines
  153. // are being used. This case should be rare, however, and is better than
  154. // having the queue scheduler constantly getting signalled when no practical
  155. // work can be completed
  156. //
  157. if (QueueEntry->JobType == JT_SEND && (QueueEntry->JobStatus & JS_NOLINE)) {
  158. Found = FALSE;
  159. break;
  160. }
  161. if (
  162. QueueEntry->JobType == JT_SEND &&
  163. !(QueueEntry->JobStatus & (JS_NOLINE | JS_RETRIES_EXCEEDED)) &&
  164. QueueEntry->JobEntry == NULL &&
  165. QueueEntry->Paused == FALSE
  166. ) {
  167. Found = TRUE;
  168. break;
  169. }
  170. }
  171. if (!Found) {
  172. //
  173. // all jobs in the queue are paused
  174. //
  175. //
  176. // cause queue to get processed regularly so nothing gets stuck
  177. //
  178. GetSystemTime( &CurrentTime );
  179. SystemTimeToFileTime( &CurrentTime, (LPFILETIME)&DueTime.QuadPart );
  180. DueTime.QuadPart += SecToNano( (DWORDLONG) FaxSendRetryDelay ? (FaxSendRetryDelay * 60) : (2 * 60) );
  181. SetWaitableTimer( QueueTimer, &DueTime, 0, NULL, NULL, FALSE );
  182. LastDueTime = DueTime;
  183. LeaveCriticalSection( &CsQueue );
  184. return;
  185. }
  186. } else {
  187. QueueEntry = JobQueue;
  188. }
  189. if (QueueEntry->BroadcastJob && QueueEntry->BroadcastOwner == NULL) {
  190. LeaveCriticalSection( &CsQueue );
  191. return;
  192. }
  193. switch (QueueEntry->JobParams.ScheduleAction) {
  194. case JSA_NOW:
  195. DueTime.QuadPart = -(LONGLONG)(SecToNano( 1 ));
  196. break;
  197. case JSA_SPECIFIC_TIME:
  198. DueTime.QuadPart = QueueEntry->ScheduleTime;
  199. break;
  200. case JSA_DISCOUNT_PERIOD:
  201. GetSystemTime( &CurrentTime );
  202. SetDiscountTime( &CurrentTime );
  203. SystemTimeToFileTime( &CurrentTime, (LPFILETIME)&QueueEntry->ScheduleTime );
  204. SystemTimeToFileTime( &CurrentTime, (LPFILETIME)&DueTime.QuadPart );
  205. break;
  206. }
  207. // send a handoff job immediately
  208. if (QueueEntry->DeviceId) {
  209. DueTime.QuadPart = -(LONGLONG)(SecToNano( 1 ));
  210. }
  211. SetWaitableTimer( QueueTimer, &DueTime, 0, NULL, NULL, FALSE );
  212. LastDueTime = DueTime;
  213. DebugPrint(( TEXT("Scheduling JobId %d at "), QueueEntry->JobId ));
  214. DebugPrintDateTime( NULL, DueTime.QuadPart );
  215. LeaveCriticalSection( &CsQueue );
  216. }
  217. int
  218. __cdecl
  219. QueueCompare(
  220. const void *arg1,
  221. const void *arg2
  222. )
  223. {
  224. if (((PQUEUE_SORT)arg1)->ScheduleTime < ((PQUEUE_SORT)arg2)->ScheduleTime) {
  225. return -1;
  226. }
  227. if (((PQUEUE_SORT)arg1)->ScheduleTime > ((PQUEUE_SORT)arg2)->ScheduleTime) {
  228. return 1;
  229. }
  230. return 0;
  231. }
  232. VOID
  233. SortJobQueue(
  234. VOID
  235. )
  236. /*++
  237. Routine Description:
  238. Sorts the job queue list, ostensibly because the discount rate time has changed.
  239. Arguments:
  240. none.
  241. Return Value:
  242. none. modifies JobQueue linked list.
  243. --*/
  244. {
  245. DWORDLONG DiscountTime;
  246. SYSTEMTIME CurrentTime;
  247. PLIST_ENTRY Next;
  248. PJOB_QUEUE QueueEntry;
  249. DWORD JobCount=0, i = 0;
  250. BOOL SortNeeded = FALSE;
  251. PQUEUE_SORT QueueSort;
  252. GetSystemTime( &CurrentTime );
  253. SetDiscountTime( &CurrentTime );
  254. SystemTimeToFileTime( &CurrentTime, (LPFILETIME)&DiscountTime );
  255. EnterCriticalSection( &CsQueue );
  256. Next = QueueListHead.Flink;
  257. while ((ULONG_PTR)Next != (ULONG_PTR)&QueueListHead) {
  258. QueueEntry = CONTAINING_RECORD( Next, JOB_QUEUE, ListEntry );
  259. Next = QueueEntry->ListEntry.Flink;
  260. JobCount++;
  261. if (!SortNeeded && QueueEntry->JobParams.ScheduleAction != JSA_NOW) {
  262. SortNeeded = TRUE;
  263. }
  264. }
  265. //
  266. // optimization...if there are no jobs, or if there aren't any jobs with a
  267. // schedule time then we don't need to sort anything
  268. //
  269. if (!SortNeeded) {
  270. goto exit;
  271. }
  272. Assert( JobCount != 0 );
  273. QueueSort = MemAlloc (JobCount * sizeof(QUEUE_SORT));
  274. if (!QueueSort) {
  275. goto exit;
  276. }
  277. Next = QueueListHead.Flink;
  278. while ((ULONG_PTR)Next != (ULONG_PTR)&QueueListHead) {
  279. QueueEntry = CONTAINING_RECORD( Next, JOB_QUEUE, ListEntry );
  280. Next = QueueEntry->ListEntry.Flink;
  281. QueueSort[i].ScheduleTime = QueueEntry->ScheduleTime;
  282. QueueSort[i].QueueEntry = QueueEntry;
  283. if (QueueEntry->JobParams.ScheduleAction == JSA_DISCOUNT_PERIOD) {
  284. QueueEntry->ScheduleTime = DiscountTime;
  285. }
  286. i += 1;
  287. }
  288. Assert (i == JobCount);
  289. qsort(
  290. (PVOID)QueueSort,
  291. (int)JobCount,
  292. sizeof(QUEUE_SORT),
  293. QueueCompare
  294. );
  295. InitializeListHead(&QueueListHead);
  296. for (i = 0; i < JobCount; i++) {
  297. QueueSort[i].QueueEntry->ListEntry.Flink = QueueSort[i].QueueEntry->ListEntry.Blink = NULL;
  298. InsertTailList( &QueueListHead, &QueueSort[i].QueueEntry->ListEntry );
  299. }
  300. MemFree( QueueSort );
  301. exit:
  302. LeaveCriticalSection( &CsQueue );
  303. }
  304. VOID
  305. PauseServerQueue(
  306. VOID
  307. )
  308. {
  309. EnterCriticalSection( &CsQueue );
  310. if (QueuePaused) {
  311. LeaveCriticalSection( &CsQueue );
  312. return;
  313. }
  314. QueuePaused = TRUE;
  315. CancelWaitableTimer( QueueTimer );
  316. LastDueTime.QuadPart = 0;
  317. LeaveCriticalSection( &CsQueue );
  318. }
  319. VOID
  320. ResumeServerQueue(
  321. VOID
  322. )
  323. {
  324. EnterCriticalSection( &CsQueue );
  325. if (!QueuePaused) {
  326. LeaveCriticalSection( &CsQueue );
  327. return;
  328. }
  329. QueuePaused = FALSE;
  330. StartJobQueueTimer( NULL );
  331. LeaveCriticalSection( &CsQueue );
  332. }
  333. BOOL
  334. RestoreFaxQueue(
  335. VOID
  336. )
  337. {
  338. PLIST_ENTRY Next;
  339. PJOB_QUEUE_FILE JobQueueFile;
  340. PJOB_QUEUE JobQueue;
  341. WIN32_FIND_DATA FindData;
  342. HANDLE hFind;
  343. WCHAR FileName[MAX_PATH];
  344. HANDLE hFile;
  345. DWORD Size;
  346. FAX_JOB_PARAMW JobParams;
  347. PJOB_QUEUE JobQueueBroadcast;
  348. DWORD i;
  349. PGUID Guid;
  350. LPTSTR FaxRouteFileName;
  351. PFAX_ROUTE_FILE FaxRouteFile;
  352. WCHAR FullPathName[MAX_PATH];
  353. LPWSTR fnp;
  354. BOOL bAnyFailed = FALSE;
  355. _stprintf( FileName, TEXT("%s\\*.fqe"), FaxQueueDir );
  356. hFind = FindFirstFile( FileName, &FindData );
  357. if (hFind == INVALID_HANDLE_VALUE) {
  358. //
  359. // succeed at doing nothing
  360. //
  361. return TRUE;
  362. }
  363. do {
  364. _stprintf( FileName, TEXT("%s\\%s"), FaxQueueDir, FindData.cFileName );
  365. hFile = CreateFile(
  366. FileName,
  367. GENERIC_READ,
  368. 0,
  369. NULL,
  370. OPEN_EXISTING,
  371. FILE_ATTRIBUTE_NORMAL,
  372. NULL
  373. );
  374. if (hFile == INVALID_HANDLE_VALUE) {
  375. bAnyFailed = TRUE;
  376. continue;
  377. }
  378. Size = GetFileSize( hFile, NULL );
  379. if (Size < sizeof(JOB_QUEUE_FILE) ) {
  380. //
  381. // we've got some funky downlevel file, let's skip it rather than choke on it.
  382. //
  383. CloseHandle( hFile );
  384. DeleteFile( FileName );
  385. bAnyFailed = TRUE;
  386. continue;
  387. }
  388. JobQueueFile = (PJOB_QUEUE_FILE) MemAlloc( Size );
  389. if (!JobQueueFile) {
  390. bAnyFailed = TRUE;
  391. CloseHandle( hFile );
  392. continue;
  393. }
  394. if (!ReadFile( hFile, JobQueueFile, Size, &Size, NULL )) {
  395. bAnyFailed = TRUE;
  396. CloseHandle( hFile );
  397. MemFree( JobQueueFile );
  398. continue;
  399. }
  400. CloseHandle( hFile );
  401. FixupString(JobQueueFile, JobQueueFile->FileName);
  402. FixupString(JobQueueFile, JobQueueFile->QueueFileName);
  403. FixupString(JobQueueFile, JobQueueFile->UserName);
  404. FixupString(JobQueueFile, JobQueueFile->RecipientNumber);
  405. FixupString(JobQueueFile, JobQueueFile->RecipientName);
  406. FixupString(JobQueueFile, JobQueueFile->Tsid);
  407. FixupString(JobQueueFile, JobQueueFile->SenderName);
  408. FixupString(JobQueueFile, JobQueueFile->SenderCompany);
  409. FixupString(JobQueueFile, JobQueueFile->SenderDept);
  410. FixupString(JobQueueFile, JobQueueFile->BillingCode);
  411. FixupString(JobQueueFile, JobQueueFile->DeliveryReportAddress);
  412. FixupString(JobQueueFile, JobQueueFile->DocumentName);
  413. if (GetFileAttributes(JobQueueFile->FileName)==0xFFFFFFFF) {
  414. DebugPrint(( TEXT("fqe file pointing to missing .tif file\n") ));
  415. bAnyFailed = TRUE;
  416. CloseHandle( hFile );
  417. DeleteFile( FileName );
  418. MemFree( JobQueueFile );
  419. continue;
  420. }
  421. JobParams.SizeOfStruct = sizeof(FAX_JOB_PARAM);
  422. JobParams.RecipientNumber = JobQueueFile->RecipientNumber;
  423. JobParams.RecipientName = JobQueueFile->RecipientName;
  424. JobParams.Tsid = JobQueueFile->Tsid;
  425. JobParams.SenderName = JobQueueFile->SenderName;
  426. JobParams.SenderCompany = JobQueueFile->SenderCompany;
  427. JobParams.SenderDept = JobQueueFile->SenderDept;
  428. JobParams.BillingCode = JobQueueFile->BillingCode;
  429. JobParams.ScheduleAction = JobQueueFile->ScheduleAction;
  430. JobParams.DeliveryReportType = JobQueueFile->DeliveryReportType;
  431. JobParams.DeliveryReportAddress = JobQueueFile->DeliveryReportAddress;
  432. JobParams.DocumentName = JobQueueFile->DocumentName;
  433. JobParams.CallHandle = 0;
  434. JobParams.Reserved[0] = 0;
  435. JobParams.Reserved[1] = 0;
  436. JobParams.Reserved[2] = 0;
  437. if (JobQueueFile->ScheduleTime == 0) {
  438. ZeroMemory( &JobParams.ScheduleTime, sizeof(SYSTEMTIME) );
  439. } else {
  440. FileTimeToSystemTime( (LPFILETIME)&JobQueueFile->ScheduleTime, &JobParams.ScheduleTime );
  441. }
  442. JobQueue = AddJobQueueEntry(
  443. JobQueueFile->JobType,
  444. JobQueueFile->FileName,
  445. &JobParams,
  446. JobQueueFile->UserName,
  447. FALSE,
  448. NULL
  449. );
  450. if (!JobQueue) {
  451. bAnyFailed = TRUE;
  452. MemFree( JobQueueFile );
  453. continue;
  454. }
  455. JobQueue->PageCount = JobQueueFile->PageCount;
  456. JobQueue->FileSize = JobQueueFile->FileSize;
  457. JobQueue->QueueFileName = StringDup( FileName );
  458. JobQueue->UniqueId = JobQueueFile->UniqueId;
  459. JobQueue->BroadcastJob = JobQueueFile->BroadcastJob;
  460. JobQueue->BroadcastOwnerUniqueId = JobQueueFile->BroadcastOwner;
  461. JobQueue->SendRetries = JobQueueFile->SendRetries;
  462. if (JobQueue->SendRetries >= FaxSendRetries) {
  463. JobQueue->JobStatus |= JS_RETRIES_EXCEEDED;
  464. }
  465. JobQueue->CountFailureInfo = JobQueueFile->CountFailureInfo;
  466. //
  467. // we don't necessarily allocate enough space for a job queue entry when we restore the fax queue
  468. // since the routing engine isn't initialized. We add some more space here. we have to patch up
  469. // the list entry members by hand
  470. if (JobQueue->CountFailureInfo > 1) {
  471. EnterCriticalSection( &CsQueue );
  472. RemoveEntryList( &JobQueue->ListEntry );
  473. JobQueue = MemReAlloc(
  474. JobQueue,
  475. sizeof(JOB_QUEUE) +
  476. (sizeof(ROUTE_FAILURE_INFO) * (JobQueueFile->CountFailureInfo -1) ));
  477. if (!JobQueue) {
  478. bAnyFailed = TRUE;
  479. continue;
  480. }
  481. InitializeCriticalSection( &JobQueue->CsFileList );
  482. InitializeCriticalSection( &JobQueue->CsRoutingDataOverride );
  483. InitializeListHead( &JobQueue->RoutingDataOverride );
  484. InitializeListHead( &JobQueue->FaxRouteFiles );
  485. InsertTailList( &QueueListHead, &JobQueue->ListEntry );
  486. LeaveCriticalSection( &CsQueue );
  487. }
  488. //
  489. // handle the failure data, which must be alloc'd with LocalAlloc.
  490. //
  491. for (i = 0; i < JobQueue->CountFailureInfo; i++) {
  492. CopyMemory(
  493. &JobQueue->RouteFailureInfo[i],
  494. &JobQueueFile->RouteFailureInfo[i],
  495. sizeof(ROUTE_FAILURE_INFO)
  496. );
  497. JobQueue->RouteFailureInfo[i].FailureData = LocalAlloc(LPTR,
  498. JobQueueFile->RouteFailureInfo[i].FailureSize);
  499. if (JobQueue->RouteFailureInfo[i].FailureData) {
  500. CopyMemory(
  501. JobQueue->RouteFailureInfo[i].FailureData,
  502. (LPBYTE) JobQueueFile + (ULONG_PTR) JobQueueFile->RouteFailureInfo[i].FailureData,
  503. JobQueueFile->RouteFailureInfo[i].FailureSize
  504. );
  505. } else {
  506. bAnyFailed = TRUE;
  507. }
  508. }
  509. if (JobQueueFile->FaxRoute) {
  510. JobQueue->FaxRoute = MemAlloc( JobQueueFile->FaxRouteSize );
  511. if (JobQueue->FaxRoute) {
  512. CopyMemory(
  513. JobQueue->FaxRoute,
  514. (LPBYTE) JobQueueFile + (ULONG_PTR) JobQueueFile->FaxRoute,
  515. JobQueueFile->FaxRouteSize
  516. );
  517. JobQueue->FaxRoute = DeSerializeFaxRoute( JobQueue->FaxRoute );
  518. if (JobQueue->FaxRoute) {
  519. JobQueue->FaxRoute->JobId = JobQueue->JobId;
  520. } else {
  521. bAnyFailed = TRUE;
  522. }
  523. } else {
  524. bAnyFailed = TRUE;
  525. }
  526. }
  527. Guid = (PGUID) (((LPBYTE) JobQueueFile) + JobQueueFile->FaxRouteFileGuid);
  528. FaxRouteFileName = (LPTSTR) (((LPBYTE) JobQueueFile) + JobQueueFile->FaxRouteFiles);
  529. for (i = 0; i < JobQueueFile->CountFaxRouteFiles; i++) {
  530. if (GetFullPathName( FaxRouteFileName, sizeof(FullPathName)/sizeof(WCHAR), FullPathName, &fnp )) {
  531. FaxRouteFile = (PFAX_ROUTE_FILE) MemAlloc( sizeof(FAX_ROUTE_FILE) );
  532. if (FaxRouteFile) {
  533. FaxRouteFile->FileName = StringDup( FullPathName );
  534. CopyMemory( &FaxRouteFile->Guid, &Guid, sizeof(GUID) );
  535. InsertTailList( &JobQueue->FaxRouteFiles, &FaxRouteFile->ListEntry );
  536. JobQueue->CountFaxRouteFiles += 1;
  537. } else {
  538. bAnyFailed = TRUE;
  539. }
  540. }
  541. Guid++;
  542. while(*FaxRouteFileName++)
  543. ;
  544. }
  545. MemFree( JobQueueFile );
  546. } while(FindNextFile( hFind, &FindData ));
  547. FindClose( hFind );
  548. //
  549. // fixup the broadcast pointers
  550. //
  551. Next = QueueListHead.Flink;
  552. while ((ULONG_PTR)Next != (ULONG_PTR)&QueueListHead) {
  553. JobQueue = CONTAINING_RECORD( Next, JOB_QUEUE, ListEntry );
  554. Next = JobQueue->ListEntry.Flink;
  555. if (JobQueue->BroadcastJob) {
  556. if (JobQueue->BroadcastOwnerUniqueId) {
  557. JobQueueBroadcast = FindJobQueueEntryByUniqueId( JobQueue->BroadcastOwnerUniqueId );
  558. if (JobQueueBroadcast) {
  559. JobQueue->BroadcastOwner = JobQueueBroadcast;
  560. JobQueueBroadcast->BroadcastCount += 1;
  561. } else {
  562. JobQueue->BroadcastOwner = NULL;
  563. }
  564. }
  565. }
  566. }
  567. PrintJobQueue( TEXT("RestoreFaxQueue"), QueueListHead );
  568. return bAnyFailed ? FALSE : TRUE;
  569. }
  570. BOOL
  571. CommitQueueEntry(
  572. PJOB_QUEUE JobQueue,
  573. LPTSTR QueueFileName,
  574. DWORDLONG UniqueId
  575. )
  576. {
  577. HANDLE hFile;
  578. DWORD Size = 0;
  579. PJOB_QUEUE_FILE JobQueueFile;
  580. ULONG_PTR Offset;
  581. DWORD i;
  582. PFAX_ROUTE FaxRoute = NULL;
  583. DWORD RouteSize;
  584. PLIST_ENTRY Next;
  585. PFAX_ROUTE_FILE FaxRouteFile;
  586. BOOL rVal = TRUE;
  587. hFile = CreateFile(
  588. QueueFileName,
  589. GENERIC_WRITE,
  590. 0,
  591. NULL,
  592. OPEN_EXISTING,
  593. FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH,
  594. NULL
  595. );
  596. if (hFile == INVALID_HANDLE_VALUE) {
  597. return FALSE;
  598. }
  599. //
  600. // calculate the size
  601. //
  602. Size = sizeof(JOB_QUEUE_FILE);
  603. Size += StringSize( JobQueue->FileName );
  604. Size += StringSize( JobQueue->QueueFileName );
  605. Size += StringSize( JobQueue->UserName );
  606. Size += StringSize( JobQueue->JobParams.RecipientNumber );
  607. Size += StringSize( JobQueue->JobParams.RecipientName );
  608. Size += StringSize( JobQueue->JobParams.Tsid );
  609. Size += StringSize( JobQueue->JobParams.SenderName );
  610. Size += StringSize( JobQueue->JobParams.SenderCompany );
  611. Size += StringSize( JobQueue->JobParams.SenderDept );
  612. Size += StringSize( JobQueue->JobParams.BillingCode );
  613. Size += StringSize( JobQueue->JobParams.DocumentName );
  614. Size += StringSize( JobQueue->DeliveryReportAddress );
  615. for (i = 0; i < JobQueue->CountFailureInfo; i++) {
  616. Size += JobQueue->RouteFailureInfo[i].FailureSize;
  617. if (i > 0) { // Allocate more space if it's not the first one
  618. Size += sizeof(ROUTE_FAILURE_INFO);
  619. }
  620. }
  621. Next = JobQueue->FaxRouteFiles.Flink;
  622. while ((ULONG_PTR)Next != (ULONG_PTR)&JobQueue->FaxRouteFiles) {
  623. FaxRouteFile = CONTAINING_RECORD( Next, FAX_ROUTE_FILE, ListEntry );
  624. Next = FaxRouteFile->ListEntry.Flink;
  625. Size += sizeof(GUID);
  626. Size += StringSize( FaxRouteFile->FileName );
  627. }
  628. if (JobQueue->JobType == JT_ROUTING) {
  629. FaxRoute = SerializeFaxRoute( JobQueue->FaxRoute, &RouteSize );
  630. Size += RouteSize;
  631. }
  632. JobQueueFile = (PJOB_QUEUE_FILE) MemAlloc( Size );
  633. if (!JobQueueFile) {
  634. CloseHandle( hFile );
  635. DeleteFile( QueueFileName );
  636. return FALSE;
  637. }
  638. ZeroMemory( JobQueueFile, Size );
  639. Offset = sizeof(JOB_QUEUE_FILE);
  640. if (JobQueue->CountFailureInfo) {
  641. Offset += sizeof(ROUTE_FAILURE_INFO) * (JobQueue->CountFailureInfo - 1);
  642. }
  643. JobQueueFile->SizeOfStruct = sizeof(JOB_QUEUE_FILE);
  644. JobQueueFile->JobType = JobQueue->JobType;
  645. JobQueueFile->PageCount = JobQueue->PageCount;
  646. JobQueueFile->FileSize = JobQueue->FileSize;
  647. JobQueueFile->DeliveryReportType = JobQueue->DeliveryReportType;
  648. JobQueueFile->ScheduleAction = JobQueue->JobParams.ScheduleAction;
  649. JobQueueFile->ScheduleTime = JobQueue->ScheduleTime;
  650. JobQueueFile->SendRetries = JobQueue->SendRetries;
  651. JobQueueFile->BroadcastJob = JobQueue->BroadcastJob;
  652. JobQueueFile->UniqueId = JobQueue->UniqueId;
  653. if (JobQueue->BroadcastJob && JobQueue->BroadcastOwner) {
  654. JobQueueFile->BroadcastOwner = JobQueue->BroadcastOwner->UniqueId;
  655. }
  656. StoreString(
  657. JobQueue->QueueFileName,
  658. (PULONG_PTR)&JobQueueFile->QueueFileName,
  659. (LPBYTE)JobQueueFile,
  660. &Offset
  661. );
  662. StoreString(
  663. JobQueue->FileName,
  664. (PULONG_PTR)&JobQueueFile->FileName,
  665. (LPBYTE)JobQueueFile,
  666. &Offset
  667. );
  668. StoreString(
  669. JobQueue->UserName,
  670. (PULONG_PTR)&JobQueueFile->UserName,
  671. (LPBYTE)JobQueueFile,
  672. &Offset
  673. );
  674. StoreString(
  675. JobQueue->DeliveryReportAddress,
  676. (PULONG_PTR)&JobQueueFile->DeliveryReportAddress,
  677. (LPBYTE)JobQueueFile,
  678. &Offset
  679. );
  680. StoreString(
  681. JobQueue->JobParams.RecipientNumber,
  682. (PULONG_PTR)&JobQueueFile->RecipientNumber,
  683. (LPBYTE)JobQueueFile,
  684. &Offset
  685. );
  686. StoreString(
  687. JobQueue->JobParams.RecipientName,
  688. (PULONG_PTR)&JobQueueFile->RecipientName,
  689. (LPBYTE)JobQueueFile,
  690. &Offset
  691. );
  692. StoreString(
  693. JobQueue->JobParams.Tsid,
  694. (PULONG_PTR)&JobQueueFile->Tsid,
  695. (LPBYTE)JobQueueFile,
  696. &Offset
  697. );
  698. StoreString(
  699. JobQueue->JobParams.SenderName,
  700. (PULONG_PTR)&JobQueueFile->SenderName,
  701. (LPBYTE)JobQueueFile,
  702. &Offset
  703. );
  704. StoreString(
  705. JobQueue->JobParams.SenderCompany,
  706. (PULONG_PTR)&JobQueueFile->SenderCompany,
  707. (LPBYTE)JobQueueFile,
  708. &Offset
  709. );
  710. StoreString(
  711. JobQueue->JobParams.SenderDept,
  712. (PULONG_PTR)&JobQueueFile->SenderDept,
  713. (LPBYTE)JobQueueFile,
  714. &Offset
  715. );
  716. StoreString(
  717. JobQueue->JobParams.BillingCode,
  718. (PULONG_PTR)&JobQueueFile->BillingCode,
  719. (LPBYTE)JobQueueFile,
  720. &Offset
  721. );
  722. StoreString(
  723. JobQueue->JobParams.DocumentName,
  724. (PULONG_PTR)&JobQueueFile->DocumentName,
  725. (LPBYTE)JobQueueFile,
  726. &Offset
  727. );
  728. if (FaxRoute) {
  729. JobQueueFile->CountFailureInfo = JobQueue->CountFailureInfo;
  730. for (i = 0; i < JobQueue->CountFailureInfo; i++) {
  731. CopyMemory(
  732. &JobQueueFile->RouteFailureInfo[i],
  733. &JobQueue->RouteFailureInfo[i],
  734. sizeof(ROUTE_FAILURE_INFO)
  735. );
  736. JobQueueFile->RouteFailureInfo[i].FailureData = (PVOID) Offset;
  737. //
  738. // protect ourselves since this comes from a routing extension that may be misbehaving
  739. //
  740. __try {
  741. CopyMemory(
  742. (LPBYTE) JobQueueFile + Offset,
  743. JobQueue->RouteFailureInfo[i].FailureData,
  744. JobQueue->RouteFailureInfo[i].FailureSize
  745. );
  746. } __except(EXCEPTION_EXECUTE_HANDLER) {
  747. }
  748. Offset += JobQueue->RouteFailureInfo[i].FailureSize;
  749. }
  750. JobQueueFile->FaxRoute = (PFAX_ROUTE) Offset;
  751. CopyMemory(
  752. (LPBYTE) JobQueueFile + Offset,
  753. FaxRoute,
  754. RouteSize
  755. );
  756. JobQueueFile->FaxRouteSize = RouteSize;
  757. Offset += RouteSize;
  758. }
  759. JobQueueFile->CountFaxRouteFiles = 0;
  760. Next = JobQueue->FaxRouteFiles.Flink;
  761. while ((ULONG_PTR)Next != (ULONG_PTR)&JobQueue->FaxRouteFiles) {
  762. DWORD TmpSize;
  763. FaxRouteFile = CONTAINING_RECORD( Next, FAX_ROUTE_FILE, ListEntry );
  764. Next = FaxRouteFile->ListEntry.Flink;
  765. CopyMemory( (LPBYTE) JobQueueFile + Offset, (LPBYTE) &FaxRouteFile->Guid, sizeof(GUID) );
  766. if (JobQueueFile->CountFaxRouteFiles == 0) {
  767. JobQueueFile->FaxRouteFileGuid = (ULONG)Offset;
  768. }
  769. Offset += sizeof(GUID);
  770. TmpSize = StringSize( FaxRouteFile->FileName );
  771. CopyMemory( (LPBYTE) JobQueueFile + Offset, FaxRouteFile->FileName, TmpSize );
  772. if (JobQueueFile->CountFaxRouteFiles == 0) {
  773. JobQueueFile->FaxRouteFiles = (ULONG)Offset;
  774. }
  775. Offset += TmpSize;
  776. JobQueueFile->CountFaxRouteFiles++;
  777. }
  778. if (!WriteFile( hFile, JobQueueFile, Size, &Size, NULL )) {
  779. DeleteFile( QueueFileName );
  780. rVal = FALSE;
  781. }
  782. CloseHandle( hFile );
  783. MemFree( JobQueueFile );
  784. return rVal;
  785. }
  786. VOID
  787. RescheduleJobQueueEntry(
  788. IN PJOB_QUEUE JobQueue
  789. )
  790. {
  791. FILETIME CurrentFileTime;
  792. LARGE_INTEGER NewTime;
  793. PLIST_ENTRY Next;
  794. PJOB_QUEUE QueueEntry;
  795. EnterCriticalSection( &CsQueue );
  796. RemoveEntryList( &JobQueue->ListEntry );
  797. GetSystemTimeAsFileTime( &CurrentFileTime );
  798. NewTime.LowPart = CurrentFileTime.dwLowDateTime;
  799. NewTime.HighPart = CurrentFileTime.dwHighDateTime;
  800. NewTime.QuadPart += SecToNano( (DWORDLONG)(FaxSendRetryDelay * 60) );
  801. JobQueue->ScheduleTime = NewTime.QuadPart;
  802. JobQueue->JobParams.ScheduleAction = JSA_SPECIFIC_TIME;
  803. //
  804. // insert the queue entry into the list in a sorted order
  805. //
  806. Next = QueueListHead.Flink;
  807. while ((ULONG_PTR)Next != (ULONG_PTR)&QueueListHead) {
  808. QueueEntry = CONTAINING_RECORD( Next, JOB_QUEUE, ListEntry );
  809. Next = QueueEntry->ListEntry.Flink;
  810. if (JobQueue->ScheduleTime <= QueueEntry->ScheduleTime) {
  811. InsertTailList( &QueueEntry->ListEntry, &JobQueue->ListEntry );
  812. Next = NULL;
  813. break;
  814. }
  815. }
  816. if ((ULONG_PTR)Next == (ULONG_PTR)&QueueListHead) {
  817. InsertTailList( &QueueListHead, &JobQueue->ListEntry );
  818. }
  819. CommitQueueEntry( JobQueue, JobQueue->QueueFileName, JobQueue->UniqueId );
  820. DebugPrintDateTime( TEXT("Rescheduling JobId %d at"), JobQueue->JobId );
  821. StartJobQueueTimer( NULL );
  822. LeaveCriticalSection( &CsQueue );
  823. }
  824. PJOB_QUEUE
  825. AddJobQueueEntry(
  826. IN DWORD JobType,
  827. IN LPCTSTR FileName,
  828. IN const FAX_JOB_PARAMW *JobParams,
  829. IN LPCWSTR UserName,
  830. IN BOOL CreateQueueFile,
  831. IN PJOB_ENTRY JobEntry // receive only
  832. )
  833. {
  834. PLIST_ENTRY Next;
  835. PJOB_QUEUE JobQueue;
  836. PJOB_QUEUE JobQueueBroadcast;
  837. PJOB_QUEUE QueueEntry;
  838. WCHAR QueueFileName[MAX_PATH];
  839. HANDLE hTiff;
  840. TIFF_INFO TiffInfo;
  841. LPLINEDEVCAPS LineDevCaps;
  842. DWORD Size = sizeof(JOB_QUEUE);
  843. if (JobType == JT_RECEIVE || JobType == JT_ROUTING) {
  844. if (CountRoutingMethods > 1) {
  845. Size += (sizeof(ROUTE_FAILURE_INFO)*(CountRoutingMethods-1));
  846. }
  847. }
  848. JobQueue = MemAlloc( Size );
  849. if (!JobQueue) {
  850. return NULL;
  851. }
  852. ZeroMemory( JobQueue, Size );
  853. JobQueue->JobId = InterlockedIncrement( &NextJobId );
  854. JobQueue->FileName = StringDup( FileName );
  855. JobQueue->JobType = JobType;
  856. JobQueue->BroadcastCount = 0;
  857. JobQueue->RefCount = 0;
  858. if (JobType != JT_RECEIVE && JobType != JT_FAIL_RECEIVE ) {
  859. JobQueue->UserName = StringDup( UserName );
  860. JobQueue->JobParams.SizeOfStruct = JobParams->SizeOfStruct;
  861. JobQueue->JobParams.RecipientNumber = StringDup( JobParams->RecipientNumber );
  862. JobQueue->JobParams.RecipientName = StringDup( JobParams->RecipientName );
  863. JobQueue->JobParams.Tsid = StringDup( JobParams->Tsid );
  864. JobQueue->JobParams.SenderName = StringDup( JobParams->SenderName );
  865. JobQueue->JobParams.SenderCompany = StringDup( JobParams->SenderCompany );
  866. JobQueue->JobParams.SenderDept = StringDup( JobParams->SenderDept );
  867. JobQueue->JobParams.BillingCode = StringDup( JobParams->BillingCode );
  868. JobQueue->JobParams.DocumentName = StringDup( JobParams->DocumentName );
  869. JobQueue->JobParams.ScheduleAction = JobParams->ScheduleAction;
  870. JobQueue->JobParams.ScheduleTime = JobParams->ScheduleTime;
  871. JobQueue->DeliveryReportAddress = StringDup( JobParams->DeliveryReportAddress );
  872. JobQueue->DeliveryReportType = JobParams->DeliveryReportType;
  873. JobQueue->JobStatus = JS_PENDING;
  874. } else {
  875. LPTSTR TempFileName = _tcsrchr( FileName, '\\' ) + 1;
  876. //JobQueue->DocumentName = StringDup( GetString( (JobType == JT_RECEIVE) ? IDS_RECEIVE_DOCUMENT : IDS_RECEIVE_FAILURE ) );
  877. JobQueue->UserName = StringDup( GetString( IDS_SERVICE_NAME ) );
  878. JobQueue->JobParams.DocumentName = StringDup( TempFileName );
  879. JobQueue->JobStatus = JS_INPROGRESS;
  880. JobQueue->JobEntry = JobEntry;
  881. JobQueue->JobEntry->JobId = JobQueue->JobId;
  882. }
  883. InitializeListHead( &JobQueue->FaxRouteFiles );
  884. InitializeCriticalSection( &JobQueue->CsFileList );
  885. InitializeListHead( &JobQueue->RoutingDataOverride );
  886. InitializeCriticalSection( &JobQueue->CsRoutingDataOverride );
  887. if (JobType == JT_RECEIVE || JobType == JT_FAIL_RECEIVE) {
  888. EnterCriticalSection( &CsQueue );
  889. InsertHeadList( &QueueListHead, &JobQueue->ListEntry );
  890. LeaveCriticalSection( &CsQueue );
  891. QueueCount += 1;
  892. SetFaxJobNumberRegistry( NextJobId );
  893. return JobQueue;
  894. }
  895. if (JobParams->CallHandle) {
  896. DebugPrint((TEXT("getting permanent device id for deviceId %d\n"),JobParams->Reserved[2]));
  897. LineDevCaps = MyLineGetDevCaps ((DWORD)JobParams->Reserved[2]);
  898. if (LineDevCaps) {
  899. JobQueue->DeviceId = LineDevCaps->dwPermanentLineID;
  900. MemFree( LineDevCaps ) ;
  901. } else {
  902. MemFree( (LPBYTE) JobQueue->DeliveryReportAddress );
  903. MemFree( (LPBYTE) JobQueue->FileName );
  904. MemFree( (LPBYTE) JobQueue->UserName );
  905. MemFree( (LPBYTE) JobQueue->QueueFileName );
  906. MemFree( (LPBYTE) JobQueue->JobParams.RecipientNumber );
  907. MemFree( (LPBYTE) JobQueue->JobParams.RecipientName );
  908. MemFree( (LPBYTE) JobQueue->JobParams.Tsid );
  909. MemFree( (LPBYTE) JobQueue->JobParams.SenderName );
  910. MemFree( (LPBYTE) JobQueue->JobParams.SenderCompany );
  911. MemFree( (LPBYTE) JobQueue->JobParams.SenderDept );
  912. MemFree( (LPBYTE) JobQueue->JobParams.BillingCode );
  913. MemFree( (LPBYTE) JobQueue->JobParams.DeliveryReportAddress );
  914. MemFree( (LPBYTE) JobQueue->JobParams.DocumentName );
  915. MemFree( JobQueue );
  916. return NULL;
  917. }
  918. }
  919. if (JobParams->Reserved[0] == 0xfffffffe) {
  920. JobQueue->BroadcastJob = TRUE;
  921. if (JobParams->Reserved[1] == 2) {
  922. JobQueueBroadcast = FindJobQueueEntry( (DWORD)JobParams->Reserved[2] );
  923. if (JobQueueBroadcast == NULL) {
  924. MemFree( (LPBYTE) JobQueue->DeliveryReportAddress );
  925. MemFree( (LPBYTE) JobQueue->FileName );
  926. MemFree( (LPBYTE) JobQueue->UserName );
  927. MemFree( (LPBYTE) JobQueue->QueueFileName );
  928. MemFree( (LPBYTE) JobQueue->JobParams.RecipientNumber );
  929. MemFree( (LPBYTE) JobQueue->JobParams.RecipientName );
  930. MemFree( (LPBYTE) JobQueue->JobParams.Tsid );
  931. MemFree( (LPBYTE) JobQueue->JobParams.SenderName );
  932. MemFree( (LPBYTE) JobQueue->JobParams.SenderCompany );
  933. MemFree( (LPBYTE) JobQueue->JobParams.SenderDept );
  934. MemFree( (LPBYTE) JobQueue->JobParams.BillingCode );
  935. MemFree( (LPBYTE) JobQueue->JobParams.DeliveryReportAddress );
  936. MemFree( (LPBYTE) JobQueue->JobParams.DocumentName );
  937. MemFree( JobQueue );
  938. return NULL;
  939. }
  940. JobQueue->BroadcastOwner = JobQueueBroadcast;
  941. JobQueueBroadcast->BroadcastCount += 1;
  942. }
  943. }
  944. //
  945. // get the page count and file size
  946. //
  947. if (FileName) {
  948. hTiff = TiffOpen( (LPWSTR) FileName, &TiffInfo, TRUE, FILLORDER_MSB2LSB );
  949. if (hTiff) {
  950. JobQueue->PageCount = TiffInfo.PageCount;
  951. TiffClose( hTiff );
  952. JobQueue->FileSize = MyGetFileSize(FileName) ;
  953. }
  954. }
  955. if (JobQueue->DeliveryReportAddress && JobQueue->DeliveryReportType == DRT_INBOX) {
  956. JobQueue->DeliveryReportProfile = AddNewMapiProfile( JobQueue->DeliveryReportAddress, FALSE, FALSE );
  957. } else {
  958. JobQueue->DeliveryReportProfile = NULL;
  959. }
  960. if (JobQueue->JobParams.ScheduleAction == JSA_SPECIFIC_TIME) {
  961. SystemTimeToFileTime( &JobQueue->JobParams.ScheduleTime, (FILETIME*) &JobQueue->ScheduleTime );
  962. } else if (JobQueue->JobParams.ScheduleAction == JSA_DISCOUNT_PERIOD) {
  963. SYSTEMTIME CurrentTime;
  964. GetSystemTime( &CurrentTime );
  965. SetDiscountTime( &CurrentTime );
  966. SystemTimeToFileTime( &CurrentTime, (LPFILETIME)&JobQueue->ScheduleTime );
  967. }
  968. EnterCriticalSection( &CsQueue );
  969. if ((JobQueue->JobParams.ScheduleAction == JSA_NOW) ||
  970. ((ULONG_PTR) QueueListHead.Flink == (ULONG_PTR)&QueueListHead) ||
  971. (JobQueue->DeviceId != 0)) {
  972. //
  973. // just put it at the head of the list
  974. //
  975. InsertHeadList( &QueueListHead, &JobQueue->ListEntry );
  976. } else {
  977. //
  978. // insert the queue entry into the list in a sorted order
  979. //
  980. Next = QueueListHead.Flink;
  981. while ((ULONG_PTR)Next != (ULONG_PTR)&QueueListHead) {
  982. QueueEntry = CONTAINING_RECORD( Next, JOB_QUEUE, ListEntry );
  983. Next = QueueEntry->ListEntry.Flink;
  984. if (JobQueue->ScheduleTime <= QueueEntry->ScheduleTime) {
  985. InsertTailList( &QueueEntry->ListEntry, &JobQueue->ListEntry );
  986. Next = NULL;
  987. break;
  988. }
  989. }
  990. if ((ULONG_PTR)Next == (ULONG_PTR)&QueueListHead) {
  991. InsertTailList( &QueueListHead, &JobQueue->ListEntry );
  992. }
  993. }
  994. //
  995. // this is a persistent queue, so commit the data to a disk file
  996. //
  997. // don't commit a handoff job to the queue
  998. if (CreateQueueFile && JobQueue->DeviceId == 0) {
  999. JobQueue->UniqueId = GenerateUniqueFileName( FaxQueueDir, TEXT("fqe"), QueueFileName, sizeof(QueueFileName)/sizeof(WCHAR) );
  1000. JobQueue->QueueFileName = StringDup( QueueFileName );
  1001. CommitQueueEntry( JobQueue, QueueFileName, JobQueue->UniqueId );
  1002. }
  1003. DebugPrint(( TEXT("Added JobId %d"), JobQueue->JobId ));
  1004. //
  1005. // set the timer so that the job will get started
  1006. //
  1007. StartJobQueueTimer( NULL );
  1008. LeaveCriticalSection( &CsQueue );
  1009. QueueCount += 1;
  1010. SetFaxJobNumberRegistry( NextJobId );
  1011. StopIdleTimer();
  1012. return JobQueue;
  1013. }
  1014. BOOL
  1015. RemoveJobQueueEntry(
  1016. IN PJOB_QUEUE JobQueueEntry
  1017. )
  1018. {
  1019. PJOB_QUEUE JobQueue, JobQueueBroadcast = NULL;
  1020. BOOL RemoveMasterBroadcast = FALSE;
  1021. PROUTING_DATA_OVERRIDE RoutingDataOverride;
  1022. PFAX_ROUTE_FILE FaxRouteFile;
  1023. PLIST_ENTRY Next;
  1024. DWORD i, JobId;
  1025. if (JobQueueEntry == NULL) {
  1026. return TRUE;
  1027. }
  1028. EnterCriticalSection( &CsQueue );
  1029. __try {
  1030. //
  1031. // need to make sure that the job queue entry we want to remove
  1032. // is still in the list of job queue entries
  1033. //
  1034. JobQueue = FindJobQueueEntryByJobQueueEntry( JobQueueEntry );
  1035. if (JobQueue == NULL) {
  1036. LeaveCriticalSection( &CsQueue );
  1037. return TRUE;
  1038. }
  1039. DebugPrint(( TEXT("Removing JobId %d"), JobQueue->JobId ));
  1040. JobId = JobQueue->JobId;
  1041. if (JobQueue->RefCount == 0) {
  1042. if (JobQueue->BroadcastJob && JobQueue->BroadcastOwner) {
  1043. JobQueueBroadcast = JobQueue->BroadcastOwner;
  1044. JobQueueBroadcast->BroadcastCount -= 1;
  1045. if (JobQueueBroadcast->BroadcastCount == 0) {
  1046. RemoveMasterBroadcast = TRUE;
  1047. }
  1048. }
  1049. RemoveEntryList( &JobQueue->ListEntry );
  1050. CancelWaitableTimer( QueueTimer );
  1051. StartJobQueueTimer( NULL );
  1052. DebugPrint(( TEXT("Deleting QueueFileName %s\n"), JobQueue->QueueFileName ));
  1053. DeleteFile( JobQueue->QueueFileName );
  1054. DebugPrint(( TEXT("Deleting FileName %s\n"), JobQueue->FileName ));
  1055. DeleteFile( JobQueue->FileName );
  1056. DebugPrint(( TEXT("Freeing JobQueue.JobParams...") ));
  1057. MemFree( (LPBYTE) JobQueue->DeliveryReportAddress );
  1058. MemFree( (LPBYTE) JobQueue->FileName );
  1059. MemFree( (LPBYTE) JobQueue->UserName );
  1060. MemFree( (LPBYTE) JobQueue->QueueFileName );
  1061. MemFree( (LPBYTE) JobQueue->JobParams.RecipientNumber );
  1062. MemFree( (LPBYTE) JobQueue->JobParams.RecipientName );
  1063. MemFree( (LPBYTE) JobQueue->JobParams.Tsid );
  1064. MemFree( (LPBYTE) JobQueue->JobParams.SenderName );
  1065. MemFree( (LPBYTE) JobQueue->JobParams.SenderCompany );
  1066. MemFree( (LPBYTE) JobQueue->JobParams.SenderDept );
  1067. MemFree( (LPBYTE) JobQueue->JobParams.BillingCode );
  1068. MemFree( (LPBYTE) JobQueue->JobParams.DeliveryReportAddress );
  1069. MemFree( (LPBYTE) JobQueue->JobParams.DocumentName );
  1070. if (JobQueue->FaxRoute) {
  1071. PFAX_ROUTE FaxRoute = JobQueue->FaxRoute;
  1072. DebugPrint(( TEXT("Freeing JobQueue.FaxRoute...") ));
  1073. MemFree( (LPBYTE) FaxRoute->Csid );
  1074. MemFree( (LPBYTE) FaxRoute->Tsid );
  1075. MemFree( (LPBYTE) FaxRoute->CallerId );
  1076. MemFree( (LPBYTE) FaxRoute->ReceiverName );
  1077. MemFree( (LPBYTE) FaxRoute->ReceiverNumber );
  1078. MemFree( (LPBYTE) FaxRoute->RoutingInfo );
  1079. MemFree( (LPBYTE) FaxRoute );
  1080. }
  1081. //
  1082. // walk the file list and remove any files
  1083. //
  1084. DebugPrint(( TEXT("Freeing JobQueue.FaxRouteFiles...") ));
  1085. Next = JobQueue->FaxRouteFiles.Flink;
  1086. if (Next != NULL) {
  1087. while ((ULONG_PTR)Next != (ULONG_PTR)&JobQueue->FaxRouteFiles) {
  1088. FaxRouteFile = CONTAINING_RECORD( Next, FAX_ROUTE_FILE, ListEntry );
  1089. Next = FaxRouteFile->ListEntry.Flink;
  1090. DeleteFile( FaxRouteFile->FileName );
  1091. MemFree( FaxRouteFile->FileName );
  1092. MemFree( FaxRouteFile );
  1093. }
  1094. }
  1095. //
  1096. // walk the routing data override list and free all memory
  1097. //
  1098. DebugPrint(( TEXT("Freeing JobQueue.RoutingDataOverride...") ));
  1099. Next = JobQueue->RoutingDataOverride.Flink;
  1100. if (Next != NULL) {
  1101. while ((ULONG_PTR)Next != (ULONG_PTR)&JobQueue->RoutingDataOverride) {
  1102. RoutingDataOverride = CONTAINING_RECORD( Next, ROUTING_DATA_OVERRIDE, ListEntry );
  1103. Next = RoutingDataOverride->ListEntry.Flink;
  1104. MemFree( RoutingDataOverride->RoutingData );
  1105. MemFree( RoutingDataOverride );
  1106. }
  1107. }
  1108. //
  1109. // free any routing failure data
  1110. //
  1111. for (i =0; i<JobQueue->CountFailureInfo; i++) {
  1112. DebugPrint(( TEXT("Freeing JobQueue.RouteFailureInfo...") ));
  1113. if ( JobQueue->RouteFailureInfo[i].FailureData ) {
  1114. //
  1115. // memory was allocated with local alloc
  1116. //
  1117. __try {
  1118. LocalFree(JobQueue->RouteFailureInfo[i].FailureData);
  1119. } __except (EXCEPTION_EXECUTE_HANDLER) {
  1120. DebugPrint(( TEXT("Couldn't LocalFree routing failure data, ec = %x\n"), GetExceptionCode() ));
  1121. }
  1122. }
  1123. }
  1124. DebugPrint(( TEXT("Freeing JobQueue") ));
  1125. MemFree( JobQueue );
  1126. CreateFaxEvent(0, FEI_DELETED, JobId);
  1127. QueueCount -= 1;
  1128. if (RemoveMasterBroadcast) {
  1129. RemoveJobQueueEntry( JobQueueBroadcast );
  1130. }
  1131. }
  1132. } __except (EXCEPTION_EXECUTE_HANDLER) {
  1133. DebugPrint(( TEXT("RemoveJobQueueEntry exception, ec = 0x%08x\n"), GetExceptionCode() ));
  1134. Assert(FALSE);
  1135. }
  1136. LeaveCriticalSection( &CsQueue );
  1137. return TRUE;
  1138. }
  1139. BOOL
  1140. PauseJobQueueEntry(
  1141. IN PJOB_QUEUE JobQueue
  1142. )
  1143. {
  1144. EnterCriticalSection( &CsQueue );
  1145. CancelWaitableTimer( QueueTimer );
  1146. JobQueue->Paused = TRUE;
  1147. JobQueue->JobStatus |= JS_PAUSED;
  1148. StartJobQueueTimer( NULL );
  1149. LeaveCriticalSection( &CsQueue );
  1150. StartIdleTimer();
  1151. return TRUE;
  1152. }
  1153. BOOL
  1154. ResumeJobQueueEntry(
  1155. IN PJOB_QUEUE JobQueue
  1156. )
  1157. {
  1158. EnterCriticalSection( &CsQueue );
  1159. CancelWaitableTimer( QueueTimer );
  1160. JobQueue->Paused = FALSE;
  1161. JobQueue->JobStatus &= ~JS_PAUSED;
  1162. //
  1163. // BugBug Should we allow "resume" of jobs whose retries have been exceeded?
  1164. // This would be like "restarting" the job.
  1165. //
  1166. StartJobQueueTimer( JobQueue );
  1167. LeaveCriticalSection( &CsQueue );
  1168. StopIdleTimer();
  1169. return TRUE;
  1170. }
  1171. PJOB_QUEUE
  1172. FindJobQueueEntryByJobQueueEntry(
  1173. IN PJOB_QUEUE JobQueueEntry
  1174. )
  1175. {
  1176. PLIST_ENTRY Next;
  1177. PJOB_QUEUE JobQueue;
  1178. Next = QueueListHead.Flink;
  1179. while ((ULONG_PTR)Next != (ULONG_PTR)&QueueListHead) {
  1180. JobQueue = CONTAINING_RECORD( Next, JOB_QUEUE, ListEntry );
  1181. Next = JobQueue->ListEntry.Flink;
  1182. if ((ULONG_PTR)JobQueue == (ULONG_PTR)JobQueueEntry) {
  1183. return JobQueue;
  1184. }
  1185. }
  1186. return NULL;
  1187. }
  1188. PJOB_QUEUE
  1189. FindJobQueueEntry(
  1190. DWORD JobId
  1191. )
  1192. {
  1193. PLIST_ENTRY Next;
  1194. PJOB_QUEUE JobQueue;
  1195. Next = QueueListHead.Flink;
  1196. while ((ULONG_PTR)Next != (ULONG_PTR)&QueueListHead) {
  1197. JobQueue = CONTAINING_RECORD( Next, JOB_QUEUE, ListEntry );
  1198. Next = JobQueue->ListEntry.Flink;
  1199. if (JobQueue->JobId == JobId) {
  1200. return JobQueue;
  1201. }
  1202. }
  1203. return NULL;
  1204. }
  1205. PJOB_QUEUE
  1206. FindJobQueueEntryByUniqueId(
  1207. DWORDLONG UniqueId
  1208. )
  1209. {
  1210. PLIST_ENTRY Next;
  1211. PJOB_QUEUE JobQueue;
  1212. Next = QueueListHead.Flink;
  1213. while ((ULONG_PTR)Next != (ULONG_PTR)&QueueListHead) {
  1214. JobQueue = CONTAINING_RECORD( Next, JOB_QUEUE, ListEntry );
  1215. Next = JobQueue->ListEntry.Flink;
  1216. if (JobQueue->UniqueId == UniqueId) {
  1217. return JobQueue;
  1218. }
  1219. }
  1220. return NULL;
  1221. }
  1222. DWORD
  1223. JobQueueThread(
  1224. LPVOID UnUsed
  1225. )
  1226. {
  1227. DWORD Rslt;
  1228. SYSTEMTIME CurrentTime;
  1229. DWORDLONG DueTime;
  1230. PLIST_ENTRY Next;
  1231. PJOB_QUEUE JobQueue;
  1232. PJOB_ENTRY JobEntry;
  1233. PLINE_INFO LineInfo;
  1234. HANDLE Handles[3];
  1235. HANDLE hLineMutex;
  1236. WCHAR LineMutexName[64];
  1237. DWORD WaitObject;
  1238. WCHAR TempFile[MAX_PATH];
  1239. static DWORDLONG DirtyDays = 0;
  1240. BOOL InitializationOk = TRUE;
  1241. QueueTimer = CreateWaitableTimer( NULL, FALSE, NULL );
  1242. IdleTimer = CreateWaitableTimer( NULL, FALSE, NULL );
  1243. JobQueueSemaphore = CreateSemaphore( NULL, 0, 1024, NULL );
  1244. Handles[0] = IdleTimer;
  1245. Handles[1] = QueueTimer;
  1246. Handles[2] = JobQueueSemaphore;
  1247. InitializeListHead( &RescheduleQueueHead );
  1248. StartIdleTimer();
  1249. __try{
  1250. InitializationOk = RestoreFaxQueue();
  1251. } __except (EXCEPTION_EXECUTE_HANDLER) {
  1252. DebugPrint(( TEXT("RestoreFaxQueue() crashed, ec = %x\n"), GetExceptionCode() ));
  1253. InitializationOk = FALSE;
  1254. }
  1255. if (!Handles[0] || !Handles[1] || !Handles[2]) {
  1256. InitializationOk = FALSE;
  1257. }
  1258. if (!InitializationOk) {
  1259. FaxLog(
  1260. FAXLOG_CATEGORY_INIT,
  1261. FAXLOG_LEVEL_NONE,
  1262. 0,
  1263. MSG_QUEUE_INIT_FAILED
  1264. );
  1265. }
  1266. //
  1267. // sort the job queue just in case our discount time has changed for the restored jobs
  1268. //
  1269. SortJobQueue();
  1270. while (TRUE) {
  1271. WaitObject = WaitForMultipleObjects( 3, Handles, FALSE, INFINITE );
  1272. if (WaitObject == WAIT_OBJECT_0) {
  1273. if (ConnectionCount != 0) {
  1274. StartIdleTimer();
  1275. continue;
  1276. } else {
  1277. EndFaxSvc(TRUE,FAXLOG_LEVEL_MAX);
  1278. }
  1279. }
  1280. //
  1281. // find the jobs that need servicing in the queue
  1282. //
  1283. JobQueueTrips++;
  1284. EnterCriticalSection( &CsJob );
  1285. EnterCriticalSection( &CsQueue );
  1286. GetSystemTime( &CurrentTime );
  1287. SystemTimeToFileTime( &CurrentTime, (LPFILETIME) &DueTime );
  1288. if (WaitObject - WAIT_OBJECT_0 == 2) {
  1289. SemaphoreSignaled++;
  1290. DebugPrintDateTime( TEXT("Semaphore signaled at "), DueTime );
  1291. } else {
  1292. DebugPrintDateTime( TEXT("Timer signaled at "), DueTime );
  1293. }
  1294. PrintJobQueue( TEXT("JobQueueThread"), QueueListHead );
  1295. Next = QueueListHead.Flink;
  1296. while ((ULONG_PTR)Next != (ULONG_PTR)&QueueListHead) {
  1297. JobQueue = CONTAINING_RECORD( Next, JOB_QUEUE, ListEntry );
  1298. Next = JobQueue->ListEntry.Flink;
  1299. if (JobQueue->Paused || JobQueue->JobType == JT_RECEIVE || JobQueue->JobType == JT_FAIL_RECEIVE) {
  1300. continue;
  1301. }
  1302. if (JobQueue->JobStatus & JS_RETRIES_EXCEEDED){
  1303. //
  1304. // recalculate dirty days
  1305. //
  1306. if (FaxDirtyDays == (DWORD) -1) {
  1307. //
  1308. // this means disable dirty days functionality
  1309. //
  1310. DirtyDays = (DWORDLONG) -1;
  1311. }
  1312. else {
  1313. DirtyDays = FaxDirtyDays * 24I64 * 60I64 * 60I64 * 1000I64 * 1000I64 * 10I64;
  1314. }
  1315. if ( DirtyDays != (DWORDLONG)-1 &&
  1316. (JobQueue->ScheduleTime + DirtyDays < DueTime)){
  1317. RemoveJobQueueEntry( JobQueue );
  1318. }
  1319. continue;
  1320. }
  1321. if (JobQueue->JobType == JT_ROUTING) {
  1322. DWORD i;
  1323. BOOL Routed = TRUE;
  1324. if (JobQueue->ScheduleTime != 0 && DueTime < JobQueue->ScheduleTime){
  1325. continue;
  1326. }
  1327. if (!RoutingIsInitialized) {
  1328. RemoveEntryList( &JobQueue->ListEntry );
  1329. InsertTailList( &RescheduleQueueHead, &JobQueue->ListEntry );
  1330. continue;
  1331. }
  1332. JobQueue->SendRetries++;
  1333. JobQueue->JobStatus = JS_RETRYING ;
  1334. for (i = 0; i < JobQueue->CountFailureInfo; i++) {
  1335. Routed &= FaxRouteRetry( JobQueue->FaxRoute, &JobQueue->RouteFailureInfo[i] );
  1336. }
  1337. if ( Routed ) {
  1338. RemoveJobQueueEntry( JobQueue );
  1339. } else {
  1340. RemoveEntryList( &JobQueue->ListEntry );
  1341. InsertTailList( &RescheduleQueueHead, &JobQueue->ListEntry );
  1342. if (JobQueue->SendRetries >= FaxSendRetries) {
  1343. //
  1344. // retries exceeded, mark job as expired
  1345. //
  1346. JobQueue->JobStatus = JS_RETRIES_EXCEEDED ;
  1347. }
  1348. }
  1349. continue;
  1350. }
  1351. //
  1352. // outbound job
  1353. //
  1354. //
  1355. // if the queue is paused or the job is already in progress, don't send it again
  1356. //
  1357. if (QueuePaused || ((JobQueue->JobStatus & JS_INPROGRESS) == JS_INPROGRESS)) {
  1358. continue;
  1359. }
  1360. if (JobQueue->BroadcastJob && JobQueue->BroadcastOwner == NULL) {
  1361. continue;
  1362. }
  1363. if (JobQueue->DeviceId || JobQueue->ScheduleTime == 0 || DueTime >= JobQueue->ScheduleTime) {
  1364. //
  1365. // start the job
  1366. //
  1367. if (JobQueue->DeviceId != 0) {
  1368. //
  1369. // we're doing a handoff job, create a mutex based on deviceId
  1370. //
  1371. DebugPrint((TEXT("Creating a handoff job for device %d\n"),JobQueue->DeviceId));
  1372. wsprintf(LineMutexName,L"FaxLineHandoff%d",JobQueue->DeviceId);
  1373. hLineMutex = CreateMutex(NULL,TRUE,LineMutexName);
  1374. if (!hLineMutex) {
  1375. DebugPrint((TEXT("CreateMutex failed, ec = %d\n"),GetLastError() ));
  1376. continue;
  1377. }
  1378. else {
  1379. JobEntry = StartJob( JobQueue->DeviceId, JobQueue->JobType, (LPTSTR) JobQueue->JobParams.RecipientNumber );
  1380. // startjob will take ownership of the line
  1381. DebugPrint((TEXT("Signalling line ownership mutex \"FaxLineHandoff%d\""),JobQueue->DeviceId));
  1382. ReleaseMutex(hLineMutex);
  1383. CloseHandle(hLineMutex);
  1384. }
  1385. } else {
  1386. JobEntry = StartJob( USE_SERVER_DEVICE, JobQueue->JobType, (LPTSTR) JobQueue->JobParams.RecipientNumber );
  1387. }
  1388. if (!JobEntry) {
  1389. JobQueue->JobStatus |= JS_NOLINE;
  1390. DebugPrint(( TEXT("Job Id %d no line"), JobQueue->JobId));
  1391. break;
  1392. } else {
  1393. JobQueue->JobStatus &= (0xFFFFFFFF ^ JS_NOLINE);
  1394. }
  1395. if (JobQueue->BroadcastJob) {
  1396. GenerateUniqueFileName( FaxQueueDir, TEXT("tif"), TempFile, sizeof(TempFile)/sizeof(WCHAR) );
  1397. if (JobQueue->FileName) {
  1398. CopyFile( JobQueue->FileName, TempFile, FALSE );
  1399. MergeTiffFiles( TempFile, JobQueue->BroadcastOwner->FileName );
  1400. } else {
  1401. CopyFile( JobQueue->BroadcastOwner->FileName, TempFile, FALSE );
  1402. }
  1403. }
  1404. LineInfo = JobEntry->LineInfo;
  1405. JobQueue->JobEntry = JobEntry;
  1406. //
  1407. // set the job type
  1408. //
  1409. JobEntry->JobType = JobQueue->JobType;
  1410. JobEntry->JobId = JobQueue->JobId;
  1411. //
  1412. // save the job params
  1413. //
  1414. JobEntry->JobParam.SizeOfStruct = JobQueue->JobParams.SizeOfStruct;
  1415. JobEntry->JobParam.RecipientNumber = StringDup( JobQueue->JobParams.RecipientNumber );
  1416. JobEntry->JobParam.RecipientName = StringDup( JobQueue->JobParams.RecipientName );
  1417. JobEntry->JobParam.Tsid = StringDup( JobQueue->JobParams.Tsid );
  1418. JobEntry->JobParam.SenderName = StringDup( JobQueue->JobParams.SenderName );
  1419. JobEntry->JobParam.SenderCompany = StringDup( JobQueue->JobParams.SenderCompany );
  1420. JobEntry->JobParam.SenderDept = StringDup( JobQueue->JobParams.SenderDept );
  1421. JobEntry->JobParam.BillingCode = StringDup( JobQueue->JobParams.BillingCode );
  1422. JobEntry->JobParam.Reserved[0] = JobQueue->JobParams.Reserved[0];
  1423. JobEntry->JobParam.Reserved[1] = JobQueue->JobParams.Reserved[1];
  1424. JobEntry->JobParam.Reserved[2] = JobQueue->JobParams.Reserved[2];
  1425. JobEntry->PageCount = JobQueue->PageCount;
  1426. JobEntry->FileSize = JobQueue->FileSize; //only meaningful for outbound job
  1427. JobEntry->UserName = StringDup ( JobQueue->UserName );
  1428. JobEntry->DeliveryReportType = JobQueue->DeliveryReportType;
  1429. JobEntry->DeliveryReportProfile = JobQueue->DeliveryReportProfile;
  1430. JobEntry->DeliveryReportAddress = StringDup ( JobQueue->DeliveryReportAddress );
  1431. if (JobQueue->BroadcastJob) {
  1432. JobEntry->BroadcastJob = TRUE;
  1433. }
  1434. //
  1435. // start the send job
  1436. //
  1437. Rslt = SendDocument(
  1438. JobEntry,
  1439. JobQueue->BroadcastJob ? TempFile : JobQueue->FileName,
  1440. &JobQueue->JobParams,
  1441. JobQueue
  1442. );
  1443. }
  1444. }
  1445. Next = RescheduleQueueHead.Flink;
  1446. while ((ULONG_PTR)Next != (ULONG_PTR)&RescheduleQueueHead) {
  1447. JobQueue = CONTAINING_RECORD( Next, JOB_QUEUE, ListEntry );
  1448. Next = JobQueue->ListEntry.Flink;
  1449. RescheduleJobQueueEntry( JobQueue );
  1450. }
  1451. //
  1452. // restart the timer
  1453. //
  1454. StartJobQueueTimer( NULL );
  1455. LeaveCriticalSection( &CsQueue );
  1456. LeaveCriticalSection( &CsJob );
  1457. }
  1458. return 0;
  1459. }
  1460. VOID
  1461. SetDiscountTime(
  1462. LPSYSTEMTIME CurrentTime
  1463. )
  1464. /*++
  1465. Routine Description:
  1466. Sets the passed in systemtime to a time inside the discount rate period.
  1467. Some care must be taken here because the time passed in is in UTC time and the discount rate is
  1468. for the current time zone. Delineating a day must be done using the current time zone. We convert the
  1469. current time into the time zone specific time, run our time-setting algorithm, and then use an offset
  1470. of the change in the time-zone specific time to set the passed in UTC time.
  1471. Also, note that there are a few subtle subcases that depend on the order of the start and ending time
  1472. for the discount period.
  1473. Arguments:
  1474. CurrentTime - the current time of the job
  1475. Return Value:
  1476. none. modifies CurrentTime.
  1477. --*/
  1478. {
  1479. // nano microsec millisec sec min hours
  1480. #define ONE_DAY 10I64 *1000I64* 1000I64 * 60I64 * 60I64 * 24I64
  1481. LONGLONG Time, TzTimeBefore, TzTimeAfter,ftCurrent;
  1482. SYSTEMTIME tzTime;
  1483. //
  1484. // convert our discount rates into UTC rates
  1485. //
  1486. SystemTimeToTzSpecificLocalTime(NULL, CurrentTime, &tzTime);
  1487. SystemTimeToFileTime(&tzTime, (FILETIME * )&TzTimeBefore);
  1488. //
  1489. // there are 2 general cases with several subcases
  1490. //
  1491. //
  1492. // case 1: discount start time is before discount stop time (don't overlap a day)
  1493. //
  1494. if ( StartCheapTime.Hour < StopCheapTime.Hour ||
  1495. (StartCheapTime.Hour == StopCheapTime.Hour && StartCheapTime.Minute < StopCheapTime.Minute )) {
  1496. //
  1497. // subcase 1: sometime before cheap time starts in the current day.
  1498. // just set it to the correct hour and minute today.
  1499. //
  1500. if ( tzTime.wHour < StartCheapTime.Hour ||
  1501. (tzTime.wHour == StartCheapTime.Hour && tzTime.wMinute <= StartCheapTime.Minute) ) {
  1502. tzTime.wHour = StartCheapTime.Hour;
  1503. tzTime.wMinute = StartCheapTime.Minute;
  1504. goto convert;
  1505. }
  1506. //
  1507. // subcase 2: inside the current cheap time range
  1508. // don't change anything, just send immediately
  1509. if ( tzTime.wHour < StopCheapTime.Hour ||
  1510. (tzTime.wHour == StopCheapTime.Hour && tzTime.wMinute <= StopCheapTime.Minute)) {
  1511. goto convert;
  1512. }
  1513. //
  1514. // subcase 3: we've passed the cheap time range for today.
  1515. // Increment 1 day and set to the start of the cheap time period
  1516. //
  1517. SystemTimeToFileTime(&tzTime, (FILETIME * )&Time);
  1518. Time += ONE_DAY;
  1519. FileTimeToSystemTime((FILETIME *)&Time, &tzTime);
  1520. tzTime.wHour = StartCheapTime.Hour;
  1521. tzTime.wMinute = StartCheapTime.Minute;
  1522. goto convert;
  1523. } else {
  1524. //
  1525. // case 2: discount start time is after discount stop time (we overlap over midnight)
  1526. //
  1527. //
  1528. // subcase 1: sometime aftert cheap time ended today, but before it starts later in the current day.
  1529. // set it to the start of the cheap time period today
  1530. //
  1531. if ( ( tzTime.wHour > StopCheapTime.Hour ||
  1532. (tzTime.wHour == StopCheapTime.Hour && tzTime.wMinute >= StopCheapTime.Minute) ) &&
  1533. ( tzTime.wHour < StartCheapTime.Hour ||
  1534. (tzTime.wHour == StartCheapTime.Hour && tzTime.wMinute <= StartCheapTime.Minute) )) {
  1535. tzTime.wHour = StartCheapTime.Hour;
  1536. tzTime.wMinute = StartCheapTime.Minute;
  1537. goto convert;
  1538. }
  1539. //
  1540. // subcase 2: sometime after cheap time started today, but before midnight.
  1541. // don't change anything, just send immediately
  1542. if ( ( tzTime.wHour >= StartCheapTime.Hour ||
  1543. (tzTime.wHour == StartCheapTime.Hour && tzTime.wMinute >= StartCheapTime.Minute) )) {
  1544. goto convert;
  1545. }
  1546. //
  1547. // subcase 3: somtime in next day before cheap time ends
  1548. // don't change anything, send immediately
  1549. //
  1550. if ( ( tzTime.wHour <= StopCheapTime.Hour ||
  1551. (tzTime.wHour == StopCheapTime.Hour && tzTime.wMinute <= StopCheapTime.Minute) )) {
  1552. goto convert;
  1553. }
  1554. //
  1555. // subcase 4: we've passed the cheap time range for today.
  1556. // since start time comes after stop time, just set it to the start time later on today.
  1557. tzTime.wHour = StartCheapTime.Hour;
  1558. tzTime.wMinute = StartCheapTime.Minute;
  1559. goto convert;
  1560. }
  1561. convert:
  1562. SystemTimeToFileTime(&tzTime, (FILETIME * )&TzTimeAfter);
  1563. SystemTimeToFileTime(CurrentTime, (FILETIME * )&ftCurrent);
  1564. ftCurrent += (TzTimeAfter - TzTimeBefore);
  1565. FileTimeToSystemTime((FILETIME *)&ftCurrent, CurrentTime);
  1566. return;
  1567. }