Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1997 lines
59 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. util.c
  5. Abstract:
  6. This module contains various utility functions.
  7. Author:
  8. Wesley Witt (wesw) 16-Jan-1996
  9. Revision History:
  10. BoazF 24-May-1999 - Added GetDevStatus
  11. --*/
  12. #include "faxsvc.h"
  13. #include "faxreg.h"
  14. #include <comenum.h>
  15. #define STRSAFE_NO_DEPRECATE
  16. #include <strsafe.h>
  17. #pragma hdrstop
  18. #ifdef EnterCriticalSection
  19. #undef EnterCriticalSection
  20. #endif
  21. #ifdef LeaveCriticalSection
  22. #undef LeaveCriticalSection
  23. #endif
  24. DWORDLONG g_dwLastUniqueId;
  25. STRING_TABLE g_ServiceStringTable[] =
  26. {
  27. { IDS_SVC_DIALING, FPS_DIALING, NULL },
  28. { IDS_SVC_SENDING, FPS_SENDING, NULL },
  29. { IDS_SVC_RECEIVING, FPS_RECEIVING, NULL },
  30. { IDS_COMPLETED, FPS_COMPLETED, NULL },
  31. { IDS_HANDLED, FPS_HANDLED, NULL },
  32. { IDS_BUSY, FPS_BUSY, NULL },
  33. { IDS_NO_ANSWER, FPS_NO_ANSWER, NULL },
  34. { IDS_BAD_ADDRESS, FPS_BAD_ADDRESS, NULL },
  35. { IDS_NO_DIAL_TONE, FPS_NO_DIAL_TONE, NULL },
  36. { IDS_DISCONNECTED, FPS_DISCONNECTED, NULL },
  37. { IDS_FATAL_ERROR, FPS_FATAL_ERROR, NULL },
  38. { IDS_NOT_FAX_CALL, FPS_NOT_FAX_CALL, NULL },
  39. { IDS_CALL_DELAYED, FPS_CALL_DELAYED, NULL },
  40. { IDS_CALL_BLACKLISTED, FPS_CALL_BLACKLISTED, NULL },
  41. { IDS_UNAVAILABLE, FPS_UNAVAILABLE, NULL },
  42. { IDS_AVAILABLE, FPS_AVAILABLE, NULL },
  43. { IDS_ABORTING, FPS_ABORTING, NULL },
  44. { IDS_ROUTING, FPS_ROUTING, NULL },
  45. { IDS_INITIALIZING, FPS_INITIALIZING, NULL },
  46. { IDS_SENDFAILED, FPS_SENDFAILED, NULL },
  47. { IDS_SENDRETRY, FPS_SENDRETRY, NULL },
  48. { IDS_BLANKSTR, FPS_BLANKSTR, NULL },
  49. { IDS_ROUTERETRY, FPS_ROUTERETRY, NULL },
  50. { IDS_CALL_COMPLETED, IDS_CALL_COMPLETED, NULL },
  51. { IDS_CALL_ABORTED, IDS_CALL_ABORTED, NULL },
  52. { IDS_ANSWERED, FPS_ANSWERED, NULL },
  53. { IDS_DR_SUBJECT, IDS_DR_SUBJECT, NULL },
  54. { IDS_DR_FILENAME, IDS_DR_FILENAME, NULL },
  55. { IDS_NDR_SUBJECT, IDS_NDR_SUBJECT, NULL },
  56. { IDS_NDR_FILENAME, IDS_NDR_FILENAME, NULL },
  57. { IDS_SERVICE_NAME, IDS_SERVICE_NAME, NULL },
  58. { IDS_NO_MAPI_LOGON, IDS_NO_MAPI_LOGON, NULL },
  59. { IDS_DEFAULT, IDS_DEFAULT, NULL },
  60. { IDS_FAX_LOG_CATEGORY_INIT_TERM, IDS_FAX_LOG_CATEGORY_INIT_TERM, NULL },
  61. { IDS_FAX_LOG_CATEGORY_OUTBOUND, IDS_FAX_LOG_CATEGORY_OUTBOUND, NULL },
  62. { IDS_FAX_LOG_CATEGORY_INBOUND, IDS_FAX_LOG_CATEGORY_INBOUND, NULL },
  63. { IDS_FAX_LOG_CATEGORY_UNKNOWN, IDS_FAX_LOG_CATEGORY_UNKNOWN, NULL },
  64. { IDS_SET_CONFIG, IDS_SET_CONFIG, NULL },
  65. { IDS_PARTIALLY_RECEIVED, IDS_PARTIALLY_RECEIVED, NULL },
  66. { IDS_FAILED_SEND, IDS_FAILED_SEND, NULL },
  67. { IDS_FAILED_RECEIVE, IDS_FAILED_RECEIVE, NULL },
  68. { IDS_CANCELED, IDS_CANCELED, NULL },
  69. { IDS_RECEIPT_RECIPIENT_NUMBER, IDS_RECEIPT_RECIPIENT_NUMBER, NULL },
  70. { IDS_RECEIPT_RECIPIENT_NUMBER_WIDTH, IDS_RECEIPT_RECIPIENT_NUMBER_WIDTH, NULL },
  71. { IDS_RECEIPT_RECIPIENT_NAME, IDS_RECEIPT_RECIPIENT_NAME, NULL },
  72. { IDS_RECEIPT_RECIPIENT_NAME_WIDTH, IDS_RECEIPT_RECIPIENT_NAME_WIDTH, NULL },
  73. { IDS_RECEIPT_START_TIME, IDS_RECEIPT_START_TIME, NULL },
  74. { IDS_RECEIPT_START_TIME_WIDTH, IDS_RECEIPT_START_TIME_WIDTH, NULL },
  75. { IDS_RECEIPT_END_TIME, IDS_RECEIPT_END_TIME, NULL },
  76. { IDS_RECEIPT_END_TIME_WIDTH, IDS_RECEIPT_END_TIME_WIDTH, NULL },
  77. { IDS_RECEIPT_RETRIES, IDS_RECEIPT_RETRIES, NULL },
  78. { IDS_RECEIPT_RETRIES_WIDTH, IDS_RECEIPT_RETRIES_WIDTH, NULL },
  79. { IDS_RECEIPT_LAST_ERROR, IDS_RECEIPT_LAST_ERROR, NULL },
  80. { IDS_RECEIPT_LAST_ERROR_WIDTH, IDS_RECEIPT_LAST_ERROR_WIDTH, NULL },
  81. { IDS_COMPLETED_RECP_LIST_HEADER, IDS_COMPLETED_RECP_LIST_HEADER, NULL },
  82. { IDS_FAILED_RECP_LIST_HEADER, IDS_FAILED_RECP_LIST_HEADER, NULL },
  83. { IDS_RECEIPT_NO_CP_AND_BODY_ATTACH, IDS_RECEIPT_NO_CP_AND_BODY_ATTACH, NULL },
  84. { IDS_RECEIPT_NO_CP_ATTACH, IDS_RECEIPT_NO_CP_ATTACH, NULL },
  85. { IDS_HTML_RECEIPT_HEADER, IDS_HTML_RECEIPT_HEADER, NULL }
  86. };
  87. const DWORD gc_dwCountServiceStringTable = (sizeof(g_ServiceStringTable)/sizeof(g_ServiceStringTable[0]));
  88. #ifdef DBG
  89. //*********************************************************************************
  90. //* Name: DebugDateTime()
  91. //* Author:
  92. //* Date:
  93. //*********************************************************************************
  94. //* DESCRIPTION:
  95. //* Accepts a 64 bit file time and generates a string with its content.
  96. //* The format is Date Time (GMT). Date and Time format are system settings
  97. //* specific.
  98. //* PARAMETERS:
  99. //* [IN] DWORD DateTime
  100. //* 64 bit file time value
  101. //* [OUT] LPTSTR lptstrDateTime
  102. //* A pointer to a string buffer where the resulting string will
  103. //* be placed.
  104. //* [IN] UINT cchstrDateTime
  105. //* The number of TCHARs in the lptstrDateTime buffer.
  106. //* RETURN VALUE:
  107. //* TRUE
  108. //*
  109. //* FALSE
  110. //*
  111. //*********************************************************************************
  112. BOOL DebugDateTime( IN DWORDLONG DateTime,
  113. OUT LPTSTR lptstrDateTime,
  114. IN UINT cchstrDateTime)
  115. {
  116. SYSTEMTIME SystemTime;
  117. TCHAR DateBuffer[256] = TEXT("NULL");
  118. TCHAR TimeBuffer[256] = TEXT("NULL");
  119. DEBUG_FUNCTION_NAME(TEXT("DebugDateTime"));
  120. if (!FileTimeToSystemTime( (LPFILETIME) &DateTime, &SystemTime ))
  121. {
  122. return FALSE;
  123. }
  124. GetY2KCompliantDate (
  125. LOCALE_SYSTEM_DEFAULT,
  126. 0,
  127. &SystemTime,
  128. DateBuffer,
  129. sizeof(DateBuffer)/sizeof(DateBuffer[0])
  130. );
  131. FaxTimeFormat(
  132. LOCALE_SYSTEM_DEFAULT,
  133. 0,
  134. &SystemTime,
  135. NULL,
  136. TimeBuffer,
  137. sizeof(TimeBuffer)/sizeof(TimeBuffer[0])
  138. );
  139. HRESULT hRc = StringCchPrintf(lptstrDateTime, cchstrDateTime,
  140. TEXT("%s %s (GMT)"),DateBuffer, TimeBuffer);
  141. if(FAILED(hRc))
  142. {
  143. ASSERT_FALSE
  144. return FALSE;
  145. }
  146. return TRUE;
  147. }
  148. VOID
  149. DebugPrintDateTime(
  150. LPTSTR Heading,
  151. DWORDLONG DateTime
  152. )
  153. {
  154. SYSTEMTIME SystemTime;
  155. TCHAR DateBuffer[256] = TEXT("NULL");
  156. TCHAR TimeBuffer[256] = TEXT("NULL");
  157. if (!FileTimeToSystemTime( (LPFILETIME) &DateTime, &SystemTime ))
  158. {
  159. return;
  160. }
  161. GetY2KCompliantDate (
  162. LOCALE_SYSTEM_DEFAULT,
  163. 0,
  164. &SystemTime,
  165. DateBuffer,
  166. sizeof(TimeBuffer)/sizeof(TimeBuffer[0])
  167. );
  168. FaxTimeFormat(
  169. LOCALE_SYSTEM_DEFAULT,
  170. 0,
  171. &SystemTime,
  172. NULL,
  173. TimeBuffer,
  174. sizeof(TimeBuffer)/sizeof(TimeBuffer[0])
  175. );
  176. if (Heading) {
  177. DebugPrint((TEXT("%s %s %s (GMT)"), Heading, DateBuffer, TimeBuffer));
  178. } else {
  179. DebugPrint((TEXT("%s %s (GMT)"), DateBuffer, TimeBuffer));
  180. }
  181. }
  182. //*********************************************************************************
  183. //* Name: SystemTimeToStr()
  184. //* Author:
  185. //* Date:
  186. //*********************************************************************************
  187. //* DESCRIPTION:
  188. //* Accepts a pointer to a system time structure and generates a string with its content.
  189. //* The format is Date Time (GMT). Date and Time format are system settings
  190. //* specific.
  191. //* PARAMETERS:
  192. //* [IN] SYSTEMTIME * lptmTime
  193. //* Pointer to SYSTEMTIME structure to convet to string
  194. //* [OUT] LPTSTR lptstrDateTime
  195. //* A pointer to a string buffer where the resulting string will
  196. //* be placed.
  197. //* [IN] UINT cchstrDateTime
  198. //* The number of TCHARs in the lptstrDateTime out buffer.
  199. //* RETURN VALUE:
  200. //* TRUE
  201. //*
  202. //* FALSE
  203. //*
  204. //*********************************************************************************
  205. BOOL SystemTimeToStr( IN const SYSTEMTIME * lptmTime,
  206. OUT LPTSTR lptstrDateTime,
  207. IN UINT cchstrDateTime)
  208. {
  209. TCHAR DateBuffer[256] = TEXT("NULL");
  210. TCHAR TimeBuffer[256] = TEXT("NULL");
  211. GetY2KCompliantDate (
  212. LOCALE_SYSTEM_DEFAULT,
  213. 0,
  214. lptmTime,
  215. DateBuffer,
  216. sizeof(TimeBuffer)/sizeof(TimeBuffer[0])
  217. );
  218. FaxTimeFormat(
  219. LOCALE_SYSTEM_DEFAULT,
  220. 0,
  221. lptmTime,
  222. NULL,
  223. TimeBuffer,
  224. sizeof(TimeBuffer)/sizeof(TimeBuffer[0])
  225. );
  226. HRESULT hRc = StringCchPrintf(lptstrDateTime, cchstrDateTime,
  227. TEXT("%s %s (GMT)"),DateBuffer, TimeBuffer);
  228. if(FAILED(hRc))
  229. {
  230. ASSERT_FALSE
  231. return FALSE;
  232. }
  233. return TRUE;
  234. }
  235. #endif
  236. BOOL
  237. InitializeStringTable(
  238. VOID
  239. )
  240. {
  241. DWORD i;
  242. TCHAR Buffer[512];
  243. DWORD ec = ERROR_SUCCESS;
  244. for (i = 0; i < gc_dwCountServiceStringTable; i++)
  245. {
  246. if (LoadString(
  247. g_hResource,
  248. g_ServiceStringTable[i].ResourceId,
  249. Buffer,
  250. sizeof(Buffer)/sizeof(TCHAR)
  251. ))
  252. {
  253. g_ServiceStringTable[i].String = (LPTSTR) MemAlloc( StringSize( Buffer ) );
  254. if (!g_ServiceStringTable[i].String)
  255. {
  256. ec = ERROR_OUTOFMEMORY;
  257. goto Error;
  258. }
  259. else
  260. {
  261. _tcscpy( g_ServiceStringTable[i].String, Buffer );
  262. }
  263. }
  264. else
  265. {
  266. ec = GetLastError();
  267. goto Error;
  268. }
  269. }
  270. return TRUE;
  271. Error:
  272. Assert (ERROR_SUCCESS != ec);
  273. for (i = 0; i < gc_dwCountServiceStringTable; i++)
  274. {
  275. MemFree (g_ServiceStringTable[i].String);
  276. g_ServiceStringTable[i].String = NULL;
  277. }
  278. SetLastError(ec);
  279. return FALSE;
  280. }
  281. LPTSTR
  282. GetString(
  283. DWORD InternalId
  284. )
  285. /*++
  286. Routine Description:
  287. Loads a resource string and returns a pointer to the string.
  288. The caller must free the memory.
  289. Arguments:
  290. ResourceId - resource string id
  291. Return Value:
  292. pointer to the string
  293. --*/
  294. {
  295. DWORD i;
  296. for (i=0; i<gc_dwCountServiceStringTable; i++) {
  297. if (g_ServiceStringTable[i].InternalId == InternalId) {
  298. return g_ServiceStringTable[i].String;
  299. }
  300. }
  301. return NULL;
  302. }
  303. BOOL
  304. InitializeFaxQueue(
  305. PREG_FAX_SERVICE pFaxReg
  306. )
  307. /*++
  308. Routine Description:
  309. Initializes the queue directory that fax will use.
  310. The administrator can configure the queue directory using the registry.
  311. This function does not create the queue directory.
  312. Arguments:
  313. pFaxReg - pointer to the fax registry data.
  314. Return Value:
  315. TRUE if successful. modifies path globals
  316. --*/
  317. {
  318. DWORD dwRet;
  319. WCHAR FaxDir[MAX_PATH] = {0};
  320. DEBUG_FUNCTION_NAME(TEXT("InitializeFaxQueue"));
  321. SetGlobalsFromRegistry( pFaxReg ); // Can not fail.
  322. // sets the following globals from registry -
  323. // g_dwFaxSendRetries
  324. // g_dwFaxSendRetryDelay
  325. // g_dwFaxDirtyDays
  326. // g_dwNextJobId
  327. // g_dwQueueState
  328. // g_fFaxUseDeviceTsid
  329. // g_fFaxUseBranding
  330. // g_fServerCp
  331. // g_StartCheapTime
  332. // g_StopCheapTime
  333. //
  334. if (NULL != pFaxReg->lptstrQueueDir)
  335. {
  336. //
  337. // The administrator changed the queue directory
  338. //
  339. wcsncpy (g_wszFaxQueueDir, pFaxReg->lptstrQueueDir, ARR_SIZE(g_wszFaxQueueDir)-1);
  340. }
  341. else
  342. {
  343. //
  344. // Get the default queue directory
  345. //
  346. if (!GetSpecialPath( CSIDL_COMMON_APPDATA, FaxDir, ARR_SIZE(FaxDir) ) )
  347. {
  348. DebugPrintEx(
  349. DEBUG_ERR,
  350. TEXT("Couldn't GetSpecialPath, ec = %d\n"),
  351. GetLastError());
  352. return FALSE;
  353. }
  354. if (wcslen(FaxDir) + wcslen(FAX_QUEUE_DIR) + 1 >= ARR_SIZE(g_wszFaxQueueDir)) // 1 for '\'
  355. {
  356. DebugPrintEx(
  357. DEBUG_ERR,
  358. TEXT("Queue folder exceeds MAX_PATH"));
  359. SetLastError(ERROR_BUFFER_OVERFLOW);
  360. return FALSE;
  361. }
  362. _sntprintf( g_wszFaxQueueDir,
  363. ARR_SIZE(g_wszFaxQueueDir) -1,
  364. TEXT("%s\\%s"),
  365. FaxDir,
  366. FAX_QUEUE_DIR);
  367. }
  368. g_wszFaxQueueDir[ARR_SIZE(g_wszFaxQueueDir) -1] = _T('\0');
  369. dwRet = IsValidFaxFolder(g_wszFaxQueueDir);
  370. if(ERROR_SUCCESS != dwRet)
  371. {
  372. DebugPrintEx(DEBUG_ERR,
  373. TEXT("IsValidFaxFolder failed for folder : %s (ec=%lu)."),
  374. g_wszFaxQueueDir,
  375. dwRet);
  376. SetLastError(dwRet);
  377. }
  378. return (dwRet == ERROR_SUCCESS);
  379. }
  380. //*********************************************************************************
  381. //* Name: GenerateUniqueQueueFile()
  382. //* Author: Ronen Barenboim
  383. //* Date: April 19, 1999
  384. //*********************************************************************************
  385. //* DESCRIPTION:
  386. //* Generates a unique QUEUE file in the queue directory.
  387. //* returns a UNIQUE id for the file based on the job type. (see the remarks
  388. //* section for more details).
  389. //* PARAMETERS:
  390. //* [IN] DWORD dwJobType
  391. //* The job type for which a file is to be generated
  392. //* [OUT] LPTSTR lptstrFileName
  393. //* A pointer to the buffer where the resulting file name (including path)
  394. //* will be placed.
  395. //* [IN] DWORD dwFileNameSize
  396. //* The size of the output file name buffer.
  397. //* RETURN VALUE:
  398. //* If successful the function returns A DWORDLONG with the unique id for the file.
  399. //* On failure it returns 0.
  400. //* REMARKS:
  401. //* The generated unique id is derived from the unique name of the generated
  402. //* file (which is only unique within files with the same extension) and the
  403. //* type of the job for which the file was generated.
  404. //* Thus it is ensured that there can be no two jobs with the same unique id
  405. //* although there can be two jobs with the same unique file name which are
  406. //* different only by the file extension.
  407. //* The 64 bit unique file id is the result of SystemTimeToFileTime.
  408. //* This is the number of 100 nano seconds intervals since 1-1-1601
  409. //* In year 3000 it will be approximately 5BC826A600000 i.e. 52 bites long.
  410. // We use the left most 8 bits for the job type. Leaving extra 4 more bits
  411. //* |-----------------3----------------2----------------1----------------|
  412. //* |FEDCBA98|76543210|FEDCBA9876543210|FEDCBA9876543210|FEDCBA9876543210|
  413. //* |-----------------|----------------|----------------|----------------|
  414. //* | JobType| 56 LSB bits of SystemTimeToFileTime |
  415. //* |-----------------|----------------|----------------|----------------|
  416. //* Job Type:
  417. //* The JT_* value of the job type.
  418. //*********************************************************************************
  419. DWORDLONG GenerateUniqueQueueFile(
  420. DWORD dwJobType,
  421. LPTSTR lptstrFileName,
  422. DWORD dwFileNameSize)
  423. {
  424. DWORD dwUniqueIdHigh;
  425. DWORD dwUniqueIdLow;
  426. DWORDLONG dwlUniqueId = 0 ;
  427. FILETIME FileTime;
  428. SYSTEMTIME SystemTime;
  429. LPTSTR lpszExt=NULL;
  430. DEBUG_FUNCTION_NAME(TEXT("GenerateUniqueQueueFile"));
  431. EnterCriticalSection(&g_csUniqueQueueFile);
  432. GetSystemTime( &SystemTime ); // returns VOID
  433. if (!SystemTimeToFileTime( &SystemTime, &FileTime ))
  434. {
  435. DebugPrintEx(DEBUG_ERR, TEXT("SystemTimeToFileTime() failed (ec: %ld)"), GetLastError());
  436. goto Error;
  437. }
  438. dwlUniqueId = MAKELONGLONG(FileTime.dwLowDateTime, FileTime.dwHighDateTime);
  439. dwlUniqueId = dwlUniqueId >> 8;
  440. if(dwlUniqueId == g_dwLastUniqueId)
  441. {
  442. //
  443. // Not enough time has passed since the last generation to ensure
  444. // out time based unique id algorithm will produce a unique id
  445. // (in case the already generated file was deleted from the queue directory).
  446. // Let some more time pass to ensure uniqueness.
  447. //
  448. Sleep(1);
  449. }
  450. //
  451. // Note that dwlUniqueId might be smaller than g_dwLastUniqueId if the system time was moved
  452. // back during the service operation.
  453. //
  454. switch (dwJobType)
  455. {
  456. case JT_SEND:
  457. {
  458. lpszExt=TEXT("FQE");
  459. }
  460. break;
  461. case JT_BROADCAST:
  462. {
  463. lpszExt=TEXT("FQP");
  464. }
  465. break;
  466. case JT_RECEIVE:
  467. {
  468. lpszExt=FAX_TIF_FILE_EXT;
  469. }
  470. break;
  471. case JT_ROUTING:
  472. {
  473. lpszExt=TEXT("FQR");
  474. }
  475. break;
  476. default:
  477. Assert(FALSE);
  478. }
  479. dwlUniqueId=GenerateUniqueFileName(
  480. g_wszFaxQueueDir,
  481. lpszExt,
  482. lptstrFileName,
  483. dwFileNameSize);
  484. if (!dwlUniqueId) {
  485. goto Error;
  486. }
  487. g_dwLastUniqueId = dwlUniqueId;
  488. dwUniqueIdHigh = (DWORD) (dwlUniqueId >> 32);
  489. dwUniqueIdLow = (DWORD) dwlUniqueId;
  490. //
  491. // Set the 8 MSB bits to zero.
  492. //
  493. dwUniqueIdHigh &= 0x00FFFFFF;
  494. //
  495. // skip past the 56 bits of SystemTimeToFileTime and put the job type at the high 8 MSB bits.
  496. //
  497. dwUniqueIdHigh |= (dwJobType << 24) ;
  498. dwlUniqueId = MAKELONGLONG(dwUniqueIdLow,dwUniqueIdHigh);
  499. Error:
  500. LeaveCriticalSection(&g_csUniqueQueueFile);
  501. return dwlUniqueId;
  502. }
  503. //*********************************************************************************
  504. //* Name: GenerateUniqueArchiveFileName()
  505. //* Author: Oded Sacher
  506. //* Date: 7/11/99
  507. //*********************************************************************************
  508. //* DESCRIPTION:
  509. //* Generates a unique file name and creates an archived file.
  510. //* PARAMETERS:
  511. //* [IN] LPTSTR Directory
  512. //* The path where the file is to be created.
  513. //* [OUT] LPTSTR FileName
  514. //* The buffer where the resulting file name (including path) will be
  515. //* placed. FileName size must not exceed MAX_PATH
  516. //* [IN] UINT cchFileName
  517. //* The size of FileName buffer in TCHARs.
  518. //* [IN] DWORDLONG JobId
  519. //* Input for the file name.
  520. //* [IN] LPTSTR lptstrUserSid
  521. //* Input for the file name.
  522. //* RETURN VALUE:
  523. //* If successful the function returns TRUE.
  524. //* REMARKS:
  525. //* FileName size must not exceed MAX_PATH
  526. BOOL
  527. GenerateUniqueArchiveFileName(
  528. IN LPTSTR Directory,
  529. OUT LPTSTR FileName,
  530. IN UINT cchFileName,
  531. IN DWORDLONG JobId,
  532. IN LPTSTR lptstrUserSid
  533. )
  534. {
  535. DEBUG_FUNCTION_NAME(TEXT("GenerateUniqueArchiveFileName"));
  536. if(!Directory || Directory[0] == TEXT('\0'))
  537. {
  538. DebugPrintEx(DEBUG_ERR, TEXT("Archive folder directory should be supplied"));
  539. ASSERT_FALSE
  540. SetLastError(ERROR_INVALID_PARAMETER);
  541. return FALSE;
  542. }
  543. if (Directory[_tcslen(Directory)-1] == TEXT('\\')) {
  544. Directory[_tcslen(Directory)-1] = 0;
  545. }
  546. HANDLE hFile = INVALID_HANDLE_VALUE;
  547. HRESULT hRc = E_FAIL;
  548. if (lptstrUserSid != NULL)
  549. {
  550. hRc = StringCchPrintf( FileName,
  551. cchFileName,
  552. TEXT("%s\\%s$%I64x.%s"),
  553. Directory,
  554. lptstrUserSid,
  555. JobId,
  556. FAX_TIF_FILE_EXT);
  557. }
  558. else
  559. {
  560. hRc = StringCchPrintf( FileName,
  561. cchFileName,
  562. TEXT("%s\\%I64x.%s"),
  563. Directory,
  564. JobId,
  565. FAX_TIF_FILE_EXT
  566. );
  567. }
  568. if(FAILED(hRc))
  569. {
  570. DebugPrintEx(DEBUG_ERR, TEXT("File Name exceeded buffer length"));
  571. SetLastError(HRESULT_CODE(hRc));
  572. return FALSE;
  573. }
  574. hFile = SafeCreateFile(
  575. FileName,
  576. GENERIC_WRITE,
  577. 0,
  578. NULL,
  579. CREATE_NEW,
  580. FILE_ATTRIBUTE_NORMAL,
  581. NULL);
  582. if (hFile == INVALID_HANDLE_VALUE) {
  583. DebugPrintEx(DEBUG_ERR,
  584. TEXT("CreateFile Failed, err : %ld"),
  585. GetLastError());
  586. return FALSE;
  587. }
  588. CloseHandle( hFile );
  589. return TRUE;
  590. }
  591. DWORD
  592. MapFSPIJobStatusToEventId(
  593. LPCFSPI_JOB_STATUS lpcFSPIJobStatus
  594. )
  595. {
  596. DEBUG_FUNCTION_NAME(TEXT("MapFSPIJobStatusToEventId"));
  597. DWORD EventId = 0;
  598. DebugPrintEx(
  599. DEBUG_MSG,
  600. TEXT("lpcFSPIJobStatus->dwJobStatus: 0x%08X lpcFSPIJobStatus->dwExtendedStatus: 0x%08X"),
  601. lpcFSPIJobStatus->dwJobStatus,
  602. lpcFSPIJobStatus->dwExtendedStatus
  603. );
  604. switch (lpcFSPIJobStatus->dwJobStatus)
  605. {
  606. case FSPI_JS_INPROGRESS:
  607. {
  608. switch( lpcFSPIJobStatus->dwExtendedStatus) {
  609. case FSPI_ES_INITIALIZING:
  610. EventId = FEI_INITIALIZING;
  611. break;
  612. case FSPI_ES_DIALING:
  613. EventId = FEI_DIALING;
  614. break;
  615. case FSPI_ES_TRANSMITTING:
  616. EventId = FEI_SENDING;
  617. break;
  618. case FSPI_ES_RECEIVING:
  619. EventId = FEI_RECEIVING;
  620. break;
  621. case FSPI_ES_HANDLED:
  622. EventId = FEI_HANDLED;
  623. break;
  624. case FSPI_ES_ANSWERED:
  625. EventId = FEI_ANSWERED;
  626. break;
  627. default:
  628. //
  629. // In W2K Fax a proprietry code generated an event with EventId ==0
  630. //
  631. EventId = 0;
  632. break;
  633. }
  634. }
  635. break;
  636. case FSPI_JS_COMPLETED:
  637. EventId = FEI_COMPLETED;
  638. break;
  639. case FSPI_JS_FAILED_NO_RETRY:
  640. case FSPI_JS_FAILED:
  641. case FSPI_JS_RETRY:
  642. case FSPI_JS_DELETED:
  643. switch( lpcFSPIJobStatus->dwExtendedStatus)
  644. {
  645. case FSPI_ES_LINE_UNAVAILABLE:
  646. EventId = FEI_LINE_UNAVAILABLE;
  647. break;
  648. case FSPI_ES_BUSY:
  649. EventId = FEI_BUSY;
  650. break;
  651. case FSPI_ES_NO_ANSWER:
  652. EventId = FEI_NO_ANSWER;
  653. break;
  654. case FSPI_ES_BAD_ADDRESS:
  655. EventId = FEI_BAD_ADDRESS;
  656. break;
  657. case FSPI_ES_NO_DIAL_TONE:
  658. EventId = FEI_NO_DIAL_TONE;
  659. break;
  660. case FSPI_ES_DISCONNECTED:
  661. EventId = FEI_DISCONNECTED;
  662. break;
  663. case FSPI_ES_FATAL_ERROR:
  664. EventId = FEI_FATAL_ERROR;
  665. break;
  666. case FSPI_ES_NOT_FAX_CALL:
  667. EventId = FEI_NOT_FAX_CALL;
  668. break;
  669. case FSPI_ES_CALL_DELAYED:
  670. EventId = FEI_CALL_DELAYED;
  671. break;
  672. case FSPI_ES_CALL_BLACKLISTED:
  673. EventId = FEI_CALL_BLACKLISTED;
  674. break;
  675. default:
  676. //
  677. // In W2K Fax a proprietry code generated an event with EventId ==0
  678. //
  679. EventId = 0;
  680. break;
  681. }
  682. break;
  683. case FSPI_JS_ABORTED:
  684. case FSPI_JS_ABORTING:
  685. EventId = FEI_ABORTING;
  686. break;
  687. case FSPI_JS_UNKNOWN:
  688. case FSPI_JS_RESUMING:
  689. case FSPI_JS_SUSPENDED:
  690. case FSPI_JS_SUSPENDING:
  691. //
  692. // No legacy notification for these states
  693. //
  694. EventId = 0;
  695. break;
  696. default:
  697. DebugPrintEx(
  698. DEBUG_ERR,
  699. TEXT("Invalid FSPI_JS: 0x%08X"),
  700. lpcFSPIJobStatus->dwJobStatus);
  701. Assert(FSPI_JS_ABORTED == lpcFSPIJobStatus->dwJobStatus); // ASSERT_FALSE
  702. break;
  703. }
  704. return EventId;
  705. }
  706. void
  707. FaxLogSend(
  708. const JOB_QUEUE * lpcJobQueue, BOOL bRetrying
  709. )
  710. /*++
  711. Routine Description:
  712. Log a fax send event.
  713. Arguments:
  714. lpcJobQueue - Pointer to the recipient job to log send information for.
  715. (It must be in a running state).
  716. Return Value:
  717. VOID
  718. --*/
  719. {
  720. DWORD Level;
  721. DWORD FormatId;
  722. TCHAR PageCountStr[64];
  723. TCHAR TimeStr[128];
  724. BOOL fLog = TRUE;
  725. TCHAR strJobID[20]={0};
  726. PJOB_ENTRY lpJobEntry;
  727. Assert(lpcJobQueue);
  728. lpJobEntry = lpcJobQueue->JobEntry;
  729. Assert(lpJobEntry);
  730. //
  731. // Convert Job ID into a string. (the string is 18 TCHARs long !!!)
  732. //
  733. _sntprintf(strJobID,ARR_SIZE(strJobID)-1,TEXT("0x%016I64x"), lpcJobQueue->UniqueId);
  734. FormatElapsedTimeStr(
  735. (FILETIME*)&lpJobEntry->ElapsedTime,
  736. TimeStr,
  737. ARR_SIZE(TimeStr)
  738. );
  739. _ltot((LONG) lpJobEntry->FSPIJobStatus.dwPageCount, PageCountStr, 10);
  740. if ( FSPI_JS_COMPLETED == lpJobEntry->FSPIJobStatus.dwJobStatus ) {
  741. FaxLog(
  742. FAXLOG_CATEGORY_OUTBOUND,
  743. FAXLOG_LEVEL_MAX,
  744. 10,
  745. MSG_FAX_SEND_SUCCESS,
  746. lpcJobQueue->SenderProfile.lptstrName,
  747. lpcJobQueue->SenderProfile.lptstrBillingCode,
  748. lpcJobQueue->SenderProfile.lptstrCompany,
  749. lpcJobQueue->SenderProfile.lptstrDepartment,
  750. lpJobEntry->FSPIJobStatus.lpwstrRemoteStationId,
  751. PageCountStr,
  752. TimeStr,
  753. lpJobEntry->LineInfo->DeviceName,
  754. strJobID,
  755. lpcJobQueue->lpParentJob->UserName
  756. );
  757. return;
  758. }
  759. else
  760. {
  761. if (FSPI_JS_ABORTED == lpJobEntry->FSPIJobStatus.dwJobStatus )
  762. {
  763. Level = FAXLOG_LEVEL_MAX;
  764. FormatId = MSG_FAX_SEND_USER_ABORT;
  765. }
  766. else if (lstrlen(lpJobEntry->ExStatusString))
  767. {
  768. //
  769. // We have a FSP proprietary extended status.
  770. //
  771. Level = bRetrying ? FAXLOG_LEVEL_MED : FAXLOG_LEVEL_MIN;
  772. FormatId = bRetrying ? MSG_FAX_PROPRIETARY_RETRY : MSG_FAX_PROPRIETARY_ABORT;
  773. FaxLog(
  774. FAXLOG_CATEGORY_OUTBOUND,
  775. Level,
  776. 8,
  777. FormatId,
  778. lpJobEntry->ExStatusString,
  779. lpcJobQueue->SenderProfile.lptstrName,
  780. lpcJobQueue->SenderProfile.lptstrBillingCode,
  781. lpcJobQueue->SenderProfile.lptstrCompany,
  782. lpcJobQueue->SenderProfile.lptstrDepartment,
  783. lpJobEntry->LineInfo->DeviceName,
  784. strJobID,
  785. lpcJobQueue->lpParentJob->UserName
  786. );
  787. return;
  788. }
  789. else
  790. {
  791. //
  792. // well known extended status
  793. //
  794. switch (lpJobEntry->FSPIJobStatus.dwExtendedStatus)
  795. {
  796. case FSPI_ES_FATAL_ERROR:
  797. Level = bRetrying ? FAXLOG_LEVEL_MED : FAXLOG_LEVEL_MIN;
  798. FormatId = bRetrying ? MSG_FAX_SEND_FATAL_RETRY : MSG_FAX_SEND_FATAL_ABORT;
  799. break;
  800. case FSPI_ES_NO_DIAL_TONE:
  801. Level = bRetrying ? FAXLOG_LEVEL_MED : FAXLOG_LEVEL_MIN;
  802. FormatId = bRetrying ? MSG_FAX_SEND_NDT_RETRY : MSG_FAX_SEND_NDT_ABORT;
  803. break;
  804. case FSPI_ES_NO_ANSWER:
  805. Level = bRetrying ? FAXLOG_LEVEL_MED : FAXLOG_LEVEL_MIN;
  806. FormatId = bRetrying ? MSG_FAX_SEND_NA_RETRY : MSG_FAX_SEND_NA_ABORT;
  807. break;
  808. case FSPI_ES_DISCONNECTED:
  809. Level = bRetrying ? FAXLOG_LEVEL_MED : FAXLOG_LEVEL_MIN;
  810. FormatId = bRetrying ? MSG_FAX_SEND_INTERRUPT_RETRY : MSG_FAX_SEND_INTERRUPT_ABORT;
  811. break;
  812. case FSPI_ES_NOT_FAX_CALL:
  813. Level = bRetrying ? FAXLOG_LEVEL_MED : FAXLOG_LEVEL_MIN;
  814. FormatId = bRetrying ? MSG_FAX_SEND_NOTFAX_RETRY : MSG_FAX_SEND_NOTFAX_ABORT;
  815. break;
  816. case FSPI_ES_BUSY:
  817. Level = bRetrying ? FAXLOG_LEVEL_MAX : FAXLOG_LEVEL_MIN;
  818. FormatId = bRetrying ? MSG_FAX_SEND_BUSY_RETRY : MSG_FAX_SEND_BUSY_ABORT;
  819. break;
  820. case FSPI_ES_CALL_BLACKLISTED:
  821. Level = FAXLOG_LEVEL_MIN;
  822. FormatId = MSG_FAX_CALL_BLACKLISTED_ABORT;
  823. break;
  824. case FSPI_ES_CALL_DELAYED:
  825. Level = FAXLOG_LEVEL_MIN;
  826. FormatId = MSG_FAX_CALL_DELAYED_ABORT;
  827. break;
  828. case FSPI_ES_BAD_ADDRESS:
  829. Level = FAXLOG_LEVEL_MIN;
  830. FormatId = MSG_FAX_BAD_ADDRESS_ABORT;
  831. break;
  832. default:
  833. fLog = FALSE;
  834. }
  835. }
  836. if(fLog)
  837. {
  838. FaxLog(
  839. FAXLOG_CATEGORY_OUTBOUND,
  840. Level,
  841. 7,
  842. FormatId,
  843. lpcJobQueue->SenderProfile.lptstrName,
  844. lpcJobQueue->SenderProfile.lptstrBillingCode,
  845. lpcJobQueue->SenderProfile.lptstrCompany,
  846. lpcJobQueue->SenderProfile.lptstrDepartment,
  847. lpJobEntry->LineInfo->DeviceName,
  848. strJobID,
  849. lpcJobQueue->lpParentJob->UserName
  850. );
  851. }
  852. }
  853. return;
  854. }
  855. DWORD MyGetFileSize(LPCTSTR FileName)
  856. {
  857. HANDLE hFile = INVALID_HANDLE_VALUE;
  858. DWORD sizelow=0, sizehigh=0;
  859. DWORD ec = ERROR_SUCCESS;
  860. hFile = SafeCreateFile(
  861. FileName,
  862. GENERIC_READ,
  863. FILE_SHARE_READ,
  864. NULL,
  865. OPEN_EXISTING,
  866. 0,
  867. NULL);
  868. if (hFile == INVALID_HANDLE_VALUE)
  869. {
  870. return 0;
  871. }
  872. sizelow = GetFileSize(hFile,&sizehigh);
  873. if (sizelow == 0xFFFFFFFFF)
  874. {
  875. ec = GetLastError();
  876. sizelow = 0;
  877. }
  878. else if (sizehigh != 0)
  879. {
  880. sizelow=0xFFFFFFFF;
  881. }
  882. CloseHandle(hFile);
  883. if (ERROR_SUCCESS != ec)
  884. {
  885. SetLastError(ec);
  886. }
  887. return sizelow;
  888. }
  889. LPCWSTR szCsClients = L"g_CsClients";
  890. LPCWSTR szCsHandleTable = L"g_CsHandleTable";
  891. LPCWSTR szCsJob = L"g_CsJob";
  892. LPCWSTR szCsLine = L"g_CsLine";
  893. LPCWSTR szCsPerfCounters = L"g_CsPerfCounters";
  894. LPCWSTR szCsQueue = L"g_CsQueue";
  895. LPCWSTR szCsRouting = L"g_CsRouting";
  896. LPCWSTR szCsConfig = L"g_CsConfig";
  897. LPCWSTR szCsInboundActivityLogging = L"g_CsInboundActivityLogging";
  898. LPCWSTR szCsOutboundActivityLogging = L"g_CsOutboundActivityLogging";
  899. LPCWSTR szCsActivity = L"g_CsActivity";
  900. LPCWSTR szCsUnknown = L"Other CS";
  901. LPCWSTR GetSzCs(
  902. LPCRITICAL_SECTION cs
  903. )
  904. {
  905. if (cs == &g_CsClients) {
  906. return szCsClients;
  907. } else if (cs == &g_CsHandleTable) {
  908. return szCsHandleTable;
  909. } else if (cs == &g_CsLine) {
  910. return szCsLine;
  911. } else if (cs == &g_CsJob) {
  912. return szCsJob;
  913. } else if (cs == &g_CsPerfCounters) {
  914. return szCsPerfCounters;
  915. } else if (cs == &g_CsQueue) {
  916. return szCsQueue;
  917. } else if (cs == &g_CsRouting) {
  918. return szCsRouting;
  919. } else if (cs == &g_CsConfig) {
  920. return szCsConfig;
  921. } else if (cs == &g_CsInboundActivityLogging) {
  922. return szCsInboundActivityLogging;
  923. } else if (cs == &g_CsOutboundActivityLogging) {
  924. return szCsOutboundActivityLogging;
  925. } else if (cs == &g_CsActivity) {
  926. return szCsActivity;
  927. }
  928. return szCsUnknown;
  929. }
  930. #if DBG
  931. VOID AppendToLogFile(
  932. LPWSTR String
  933. )
  934. {
  935. DWORD BytesWritten;
  936. LPSTR AnsiBuffer = UnicodeStringToAnsiString( String );
  937. if (g_hCritSecLogFile != INVALID_HANDLE_VALUE) {
  938. WriteFile(g_hCritSecLogFile,(LPBYTE)AnsiBuffer,strlen(AnsiBuffer) * sizeof(CHAR),&BytesWritten,NULL);
  939. }
  940. MemFree(AnsiBuffer);
  941. }
  942. VOID AppendFuncToLogFile(
  943. LPCRITICAL_SECTION cs,
  944. LPTSTR szFunc,
  945. DWORD line,
  946. LPTSTR file,
  947. PDBGCRITSEC CritSec
  948. )
  949. {
  950. WCHAR Buffer[300];
  951. LPWSTR FileName;
  952. LPCWSTR szcs = GetSzCs(cs);
  953. FileName = wcsrchr(file,'\\');
  954. if (!FileName) {
  955. FileName = TEXT("Unknown ");
  956. } else {
  957. FileName += 1;
  958. }
  959. if (CritSec) {
  960. wsprintf(Buffer,TEXT("%d\t%p\t%s\t%s\t%s\t%d\t%d\r\n"),
  961. GetTickCount(),
  962. (PULONG_PTR)cs,
  963. szcs,
  964. szFunc,
  965. FileName,
  966. line,
  967. CritSec->ReleasedTime - CritSec->AquiredTime);
  968. } else {
  969. wsprintf(Buffer,TEXT("%d\t%p\t%s\t%s\t%s\t%d\r\n"),GetTickCount(),(PULONG_PTR)cs,szcs,szFunc, FileName,line);
  970. }
  971. AppendToLogFile( Buffer );
  972. return;
  973. }
  974. VOID pEnterCriticalSection(
  975. LPCRITICAL_SECTION cs,
  976. DWORD line,
  977. LPTSTR file
  978. )
  979. {
  980. PDBGCRITSEC pCritSec = (PDBGCRITSEC)MemAlloc(sizeof(DBGCRITSEC));
  981. if( pCritSec == NULL )
  982. {
  983. // memory allocation failed, do the actual work and exit...
  984. EnterCriticalSection(cs);
  985. return;
  986. }
  987. pCritSec->CritSecAddr = (ULONG_PTR) cs;
  988. pCritSec->AquiredTime = GetTickCount();
  989. pCritSec->ThreadId = GetCurrentThreadId();
  990. EnterCriticalSection(&g_CsCritSecList);
  991. InsertHeadList( &g_CritSecListHead, &pCritSec->ListEntry );
  992. AppendFuncToLogFile(cs,TEXT("EnterCriticalSection"), line, file, NULL );
  993. //
  994. // check ordering of threads. ALWAYS aquire g_CsLine before aquiring g_CsQueue!!!
  995. //
  996. if ((LPCRITICAL_SECTION)cs == (LPCRITICAL_SECTION)&g_CsQueue)
  997. {
  998. if ((DWORD)GetCurrentThreadId() != PtrToUlong(g_CsJob.OwningThread()))
  999. {
  1000. WCHAR DebugBuf[300];
  1001. wsprintf(DebugBuf, TEXT("%d : Attempting to aquire g_CsQueue (thread %x) without aquiring g_CsJob (thread %p, lock count %x) first, possible deadlock!\r\n"),
  1002. GetTickCount(),
  1003. GetCurrentThreadId(),
  1004. g_CsJob.OwningThread(),
  1005. g_CsJob.LockCount());
  1006. AppendToLogFile( DebugBuf );
  1007. }
  1008. }
  1009. LeaveCriticalSection(&g_CsCritSecList);
  1010. EnterCriticalSection(cs);
  1011. }
  1012. VOID pLeaveCriticalSection(
  1013. LPCRITICAL_SECTION cs,
  1014. DWORD line,
  1015. LPTSTR file
  1016. )
  1017. {
  1018. PDBGCRITSEC CritSec = NULL;
  1019. BOOL fRemove = FALSE;
  1020. EnterCriticalSection(&g_CsCritSecList);
  1021. PLIST_ENTRY Next = g_CritSecListHead.Flink;
  1022. while ((ULONG_PTR)Next != (ULONG_PTR) &g_CritSecListHead)
  1023. {
  1024. CritSec = CONTAINING_RECORD( Next, DBGCRITSEC, ListEntry );
  1025. if ((ULONG_PTR)CritSec->CritSecAddr == (ULONG_PTR) cs &&
  1026. ( GetCurrentThreadId() == CritSec->ThreadId ) )
  1027. {
  1028. CritSec->ReleasedTime = GetTickCount();
  1029. fRemove = TRUE;
  1030. break;
  1031. }
  1032. Next = Next->Flink;
  1033. }
  1034. AppendFuncToLogFile(cs,TEXT("LeaveCriticalSection"),line, file, CritSec );
  1035. if (fRemove) {
  1036. RemoveEntryList( &CritSec->ListEntry );
  1037. MemFree( CritSec );
  1038. }
  1039. LeaveCriticalSection(&g_CsCritSecList);
  1040. LeaveCriticalSection(cs);
  1041. }
  1042. BOOL
  1043. ThreadOwnsCs(
  1044. VOID
  1045. )
  1046. {
  1047. PDBGCRITSEC pCritSec = NULL;
  1048. DWORD dwThreadId = GetCurrentThreadId();
  1049. EnterCriticalSection(&g_CsCritSecList);
  1050. PLIST_ENTRY Next = g_CritSecListHead.Flink;
  1051. while ((ULONG_PTR)Next != (ULONG_PTR) &g_CritSecListHead)
  1052. {
  1053. pCritSec = CONTAINING_RECORD( Next, DBGCRITSEC, ListEntry );
  1054. if (dwThreadId == pCritSec->ThreadId )
  1055. {
  1056. LeaveCriticalSection(&g_CsCritSecList);
  1057. return TRUE;
  1058. }
  1059. Next = Next->Flink;
  1060. }
  1061. LeaveCriticalSection(&g_CsCritSecList);
  1062. return FALSE;
  1063. }
  1064. #endif
  1065. DWORD
  1066. ValidateTiffFile(
  1067. LPCWSTR TifFileName
  1068. )
  1069. {
  1070. HANDLE hTiff;
  1071. DWORD rc = ERROR_SUCCESS;
  1072. TIFF_INFO TiffInfo;
  1073. //
  1074. // Validate tiff format
  1075. //
  1076. hTiff = TiffOpen( (LPWSTR)TifFileName, &TiffInfo, FALSE, FILLORDER_MSB2LSB );
  1077. if (!hTiff) {
  1078. rc = GetLastError();
  1079. return rc;
  1080. }
  1081. TiffClose( hTiff );
  1082. return ERROR_SUCCESS;
  1083. }
  1084. //
  1085. // Function:
  1086. // LegacyJobStatusToStatus
  1087. //
  1088. // Parameters:
  1089. // dwLegacyStatus - Legacy job status (FS_*)
  1090. // pdwStatus - A pointer to a DWORD that receives the new job status.
  1091. // pdwExtendedStatus - A pointer to a DWORD that receives the extended
  1092. // job status.
  1093. //
  1094. // Return Value:
  1095. // If the function succeeds, the return value is ERROR_SUCCESS, else the
  1096. // return value is an error code.
  1097. //
  1098. // Description:
  1099. // The function converts legacy FSP job status values to new job status.
  1100. //
  1101. //
  1102. DWORD
  1103. LegacyJobStatusToStatus(
  1104. DWORD dwLegacyStatus,
  1105. PDWORD pdwStatus,
  1106. PDWORD pdwExtendedStatus,
  1107. PBOOL pbPrivateStatusCode)
  1108. {
  1109. Assert(pdwStatus);
  1110. Assert(pdwExtendedStatus);
  1111. Assert(pbPrivateStatusCode);
  1112. *pbPrivateStatusCode = FALSE;
  1113. switch (dwLegacyStatus)
  1114. {
  1115. case FS_INITIALIZING:
  1116. *pdwStatus = FSPI_JS_INPROGRESS;
  1117. *pdwExtendedStatus = FSPI_ES_INITIALIZING;
  1118. break;
  1119. case FS_DIALING:
  1120. *pdwStatus = FSPI_JS_INPROGRESS;
  1121. *pdwExtendedStatus = FSPI_ES_DIALING;
  1122. break;
  1123. case FS_TRANSMITTING:
  1124. *pdwStatus = FSPI_JS_INPROGRESS;
  1125. *pdwExtendedStatus = FSPI_ES_TRANSMITTING;
  1126. break;
  1127. case FS_RECEIVING:
  1128. *pdwStatus = FSPI_JS_INPROGRESS;
  1129. *pdwExtendedStatus = FSPI_ES_RECEIVING;
  1130. break;
  1131. case FS_COMPLETED:
  1132. *pdwStatus = FSPI_JS_COMPLETED;
  1133. *pdwExtendedStatus = FSPI_ES_CALL_COMPLETED;
  1134. break;
  1135. case FS_HANDLED:
  1136. *pdwStatus = FSPI_JS_INPROGRESS;
  1137. *pdwExtendedStatus = FSPI_ES_HANDLED;
  1138. break;
  1139. case FS_LINE_UNAVAILABLE:
  1140. *pdwStatus = FSPI_JS_FAILED;
  1141. *pdwExtendedStatus = FSPI_ES_LINE_UNAVAILABLE;
  1142. break;
  1143. case FS_BUSY:
  1144. *pdwStatus = FSPI_JS_FAILED;
  1145. *pdwExtendedStatus = FSPI_ES_BUSY;
  1146. break;
  1147. case FS_NO_ANSWER:
  1148. *pdwStatus = FSPI_JS_FAILED;
  1149. *pdwExtendedStatus = FSPI_ES_NO_ANSWER;
  1150. break;
  1151. case FS_BAD_ADDRESS:
  1152. *pdwStatus = FSPI_JS_FAILED;
  1153. *pdwExtendedStatus = FSPI_ES_BAD_ADDRESS;
  1154. break;
  1155. case FS_NO_DIAL_TONE:
  1156. *pdwStatus = FSPI_JS_FAILED;
  1157. *pdwExtendedStatus = FSPI_ES_NO_DIAL_TONE;
  1158. break;
  1159. case FS_DISCONNECTED:
  1160. *pdwStatus = FSPI_JS_FAILED;
  1161. *pdwExtendedStatus = FSPI_ES_DISCONNECTED;
  1162. break;
  1163. case FS_FATAL_ERROR:
  1164. *pdwStatus = FSPI_JS_FAILED;
  1165. *pdwExtendedStatus = FSPI_ES_FATAL_ERROR;
  1166. break;
  1167. case FS_NOT_FAX_CALL:
  1168. *pdwStatus = FSPI_JS_FAILED;
  1169. *pdwExtendedStatus = FSPI_ES_NOT_FAX_CALL;
  1170. break;
  1171. case FS_CALL_DELAYED:
  1172. *pdwStatus = FSPI_JS_FAILED;
  1173. *pdwExtendedStatus = FSPI_ES_CALL_DELAYED;
  1174. break;
  1175. case FS_CALL_BLACKLISTED:
  1176. *pdwStatus = FSPI_JS_FAILED;
  1177. *pdwExtendedStatus = FSPI_ES_CALL_BLACKLISTED;
  1178. break;
  1179. case FS_USER_ABORT:
  1180. *pdwStatus = FSPI_JS_ABORTED;
  1181. *pdwExtendedStatus = FSPI_ES_CALL_ABORTED;
  1182. break;
  1183. case FS_ANSWERED:
  1184. *pdwStatus = FSPI_JS_INPROGRESS;
  1185. *pdwExtendedStatus = FSPI_ES_ANSWERED;
  1186. break;
  1187. default:
  1188. //
  1189. // The FSP reports a status code which is not one of the predefined status codes.
  1190. // This can be a proprietry status code (in this case the StringId must not be zero)
  1191. // or a TAPI line error (one of LINEERR_constants). Note that all LINERR_constants
  1192. // are negative numbers (documented in MSDN).
  1193. // We mark the fact that it is not one of the stock values so we can map it back
  1194. // to legacy Fax API status codes. Otherwise we might get confused and think that
  1195. // a FSP proprietry code is one of the EFSPI extended status codes.
  1196. //
  1197. // Note that we don't have a way to correctly map the proprietry code
  1198. // to a FSPI_JS status code since we do not know the semantics of the
  1199. // proprietry code. We choose to report it as FSPI_JS_INPROGRESS.
  1200. //
  1201. *pdwStatus = FSPI_JS_INPROGRESS;
  1202. *pdwExtendedStatus = dwLegacyStatus;
  1203. *pbPrivateStatusCode = TRUE;
  1204. break;
  1205. }
  1206. return(ERROR_SUCCESS);
  1207. }
  1208. //
  1209. // Function:
  1210. // GetDevStatus
  1211. //
  1212. // Parameters:
  1213. // hFaxJob - the job handle that FaxDevStartJob returned.
  1214. // LineInfo - Ther line information structure.
  1215. // ppFaxStatus - A pointer to a buffer that receives the address of the
  1216. // FSPI_JOB_STATUS that contains the status.
  1217. //
  1218. // Return Value:
  1219. // If the function succeeds, the return value is ERROR_SUCCESS, else the
  1220. // return value is an error code.
  1221. //
  1222. // Description:
  1223. // The function allocates a FSPI_JOB_STATUS structure and calls the FSP
  1224. // for final job status report.
  1225. // If the FSP is a legacy FSP, the function allocates first a
  1226. // FAX_DEV_STATUS structure, calls the legacy FSP status report function
  1227. // and mapps the returned values into the FSPI_JOB_STATUS structure.
  1228. //
  1229. DWORD
  1230. GetDevStatus(
  1231. HANDLE hFaxJob,
  1232. PLINE_INFO LineInfo,
  1233. LPFSPI_JOB_STATUS *ppFaxStatus)
  1234. {
  1235. DEBUG_FUNCTION_NAME(TEXT("GetDevStatus"));
  1236. DWORD dwRet = ERROR_SUCCESS;
  1237. LPWSTR szStatusStr = NULL;
  1238. DWORD dwSize = 0;
  1239. BOOL bPrivateStatusCode = FALSE;
  1240. Assert(hFaxJob);
  1241. Assert(LineInfo);
  1242. Assert(ppFaxStatus);
  1243. Assert(LineInfo->Provider->dwAPIVersion == FSPI_API_VERSION_1);
  1244. //
  1245. // We're have a legacy FSP to deal with.
  1246. //
  1247. PFAX_DEV_STATUS pLegacyFaxStatus = NULL;
  1248. LPFSPI_JOB_STATUS pFaxStatus = NULL;
  1249. //
  1250. // Allocate memory for the status packet this is a variable size packet
  1251. // based on the size of the strings contained withing the packet.
  1252. //
  1253. DWORD StatusSize = sizeof(FAX_DEV_STATUS) + FAXDEVREPORTSTATUS_SIZE;
  1254. pLegacyFaxStatus = (PFAX_DEV_STATUS) MemAlloc( StatusSize );
  1255. if (!pLegacyFaxStatus)
  1256. {
  1257. DebugPrintEx(DEBUG_ERR,
  1258. TEXT("Failed to allocate memory for FAX_DEV_STATUS"));
  1259. dwRet = ERROR_NOT_ENOUGH_MEMORY;
  1260. goto Exit;
  1261. }
  1262. //
  1263. // Setup the status packet
  1264. //
  1265. pLegacyFaxStatus->SizeOfStruct = StatusSize;
  1266. Assert(LineInfo->Provider->FaxDevReportStatus);
  1267. __try
  1268. {
  1269. //
  1270. // Call the FSP
  1271. //
  1272. DWORD BytesNeeded;
  1273. if (!LineInfo->Provider->FaxDevReportStatus(
  1274. hFaxJob,
  1275. pLegacyFaxStatus,
  1276. StatusSize,
  1277. &BytesNeeded
  1278. )) {
  1279. DebugPrintEx(DEBUG_ERR,
  1280. TEXT("FaxDevReportStatus() failed - %d"),
  1281. dwRet);
  1282. dwRet = GetLastError();
  1283. // catch the case in which FaxDevReportStatus() failded but doesn't
  1284. // report an error
  1285. Assert (ERROR_SUCCESS != dwRet);
  1286. // in case the provider did not set last error
  1287. if ( dwRet == ERROR_SUCCESS )
  1288. {
  1289. // force it to report an error
  1290. dwRet = ERROR_INVALID_FUNCTION;
  1291. }
  1292. goto Exit;
  1293. }
  1294. }
  1295. __except (HandleFaxExtensionFault(EXCEPTION_SOURCE_FSP, LineInfo->Provider->FriendlyName, GetExceptionCode()))
  1296. {
  1297. ASSERT_FALSE;
  1298. }
  1299. //
  1300. // Map FAX_DEV_STATUS into FSPI_JOB_STATUS.
  1301. //
  1302. //
  1303. // Compute the extra space that is needed after the structure for the
  1304. // various strings.
  1305. //
  1306. dwSize = sizeof(FSPI_JOB_STATUS);
  1307. if (pLegacyFaxStatus->CSI)
  1308. {
  1309. dwSize += sizeof(WCHAR) * (wcslen(pLegacyFaxStatus->CSI) + 1);
  1310. }
  1311. if (pLegacyFaxStatus->CallerId)
  1312. {
  1313. dwSize += sizeof(WCHAR) * (wcslen(pLegacyFaxStatus->CallerId) + 1);
  1314. }
  1315. if (pLegacyFaxStatus->RoutingInfo)
  1316. {
  1317. dwSize += sizeof(WCHAR) * (wcslen(pLegacyFaxStatus->RoutingInfo) + 1);
  1318. }
  1319. //
  1320. // Allocate the FSPI_JOB_STATUS structure with extra space for the strings.
  1321. //
  1322. pFaxStatus = (LPFSPI_JOB_STATUS)MemAlloc(dwSize);
  1323. if (!pFaxStatus)
  1324. {
  1325. DebugPrintEx(DEBUG_ERR,
  1326. TEXT("Failed to allocate memory for FSPI_JOB_STATUS"));
  1327. dwRet = ERROR_NOT_ENOUGH_MEMORY;
  1328. goto Exit;
  1329. }
  1330. //
  1331. // Zero-out the structure.
  1332. //
  1333. memset(pFaxStatus, 0, dwSize);
  1334. pFaxStatus->dwSizeOfStruct = sizeof(FSPI_JOB_STATUS);
  1335. //
  1336. // Map the legacy status into new EFSPI status.
  1337. //
  1338. dwRet = LegacyJobStatusToStatus(pLegacyFaxStatus->StatusId,
  1339. &(pFaxStatus->dwJobStatus),
  1340. &(pFaxStatus->dwExtendedStatus),
  1341. &bPrivateStatusCode);
  1342. if (dwRet != ERROR_SUCCESS)
  1343. {
  1344. DebugPrintEx(DEBUG_ERR,
  1345. TEXT("LegacyJobStatusToStatus failed - %d"),
  1346. dwRet);
  1347. goto Exit;
  1348. }
  1349. if (bPrivateStatusCode)
  1350. {
  1351. //
  1352. // The FSP reported a private status code (not one of the FS_* status codes).
  1353. // We mark this in the returned FSPI_JOB_STATUS by turning on the private flag
  1354. // FSPI_JOB_STATUS_INFO_FSP_PRIVATE_STATUS_CODE.
  1355. // We will check this flag when converting the FSPI_JOB_STATUS
  1356. // back to FPS_ device status so we won't get confused by an FSP that returned
  1357. // a proprietry status code which is equal to one the new FSPI_JS_* codes.
  1358. //
  1359. pFaxStatus->fAvailableStatusInfo |= FSPI_JOB_STATUS_INFO_FSP_PRIVATE_STATUS_CODE;
  1360. #if DEBUG
  1361. if (0 == pLegacyFaxStatus->StringId && pLegacyFaxStatus->StatusId < LINEERR_ALLOCATED)
  1362. {
  1363. //
  1364. // The status reported is not one of the stock status codes and is not a TAPI Error code.
  1365. // pLegacyFaxStatus->StringId must not be 0.
  1366. //
  1367. DebugPrintEx(
  1368. DEBUG_WRN,
  1369. TEXT("Provider [%s] has reported an illegal FAX_DEV_STATUS for device [%s]\n. ")
  1370. TEXT("Although the reported status code (0x%08X) is proprietry the string id is 0"),
  1371. LineInfo->Provider->FriendlyName,
  1372. LineInfo->DeviceName,
  1373. pLegacyFaxStatus->StatusId);
  1374. }
  1375. #endif
  1376. }
  1377. pFaxStatus->dwExtendedStatusStringId = pLegacyFaxStatus->StringId;
  1378. szStatusStr = (LPWSTR)(((PBYTE)pFaxStatus) + sizeof(FSPI_JOB_STATUS));
  1379. //
  1380. // Copy CSI into lpwstrRemoteStationId
  1381. //
  1382. if (pLegacyFaxStatus->CSI)
  1383. {
  1384. pFaxStatus->lpwstrRemoteStationId = szStatusStr;
  1385. wcscpy(szStatusStr, pLegacyFaxStatus->CSI);
  1386. szStatusStr += wcslen(pLegacyFaxStatus->CSI) + 1;
  1387. }
  1388. //
  1389. // Copy the Caller ID string.
  1390. //
  1391. if (pLegacyFaxStatus->CallerId)
  1392. {
  1393. pFaxStatus->lpwstrCallerId = szStatusStr;
  1394. wcscpy(szStatusStr, pLegacyFaxStatus->CallerId);
  1395. szStatusStr += wcslen(pLegacyFaxStatus->CallerId) + 1;
  1396. }
  1397. //
  1398. // Copy the Routing Info string.
  1399. //
  1400. if (pLegacyFaxStatus->RoutingInfo)
  1401. {
  1402. pFaxStatus->lpwstrRoutingInfo = szStatusStr;
  1403. wcscpy(szStatusStr, pLegacyFaxStatus->RoutingInfo);
  1404. }
  1405. //
  1406. // Copy Page Count.
  1407. //
  1408. pFaxStatus->dwPageCount = pLegacyFaxStatus->PageCount;
  1409. pFaxStatus->fAvailableStatusInfo |= FSPI_JOB_STATUS_INFO_PAGECOUNT;
  1410. Exit:
  1411. if (dwRet == ERROR_SUCCESS)
  1412. {
  1413. *ppFaxStatus = pFaxStatus;
  1414. }
  1415. else
  1416. {
  1417. MemFree(pFaxStatus);
  1418. }
  1419. MemFree(pLegacyFaxStatus);
  1420. return(dwRet);
  1421. }
  1422. BOOL
  1423. GetRealFaxTimeAsSystemTime (
  1424. const PJOB_ENTRY lpcJobEntry,
  1425. FAX_ENUM_TIME_TYPES TimeType,
  1426. SYSTEMTIME* lpFaxTime
  1427. )
  1428. {
  1429. DEBUG_FUNCTION_NAME(TEXT("GetRealFaxTimeAsSystemTime)"));
  1430. Assert (lpcJobEntry);
  1431. Assert (lpFaxTime);
  1432. PJOB_QUEUE pJobQueue = lpcJobEntry->lpJobQueueEntry;
  1433. Assert (pJobQueue);
  1434. DWORDLONG dwlFileTime;
  1435. dwlFileTime = ((TimeType == FAX_TIME_TYPE_START) ? lpcJobEntry->StartTime : lpcJobEntry->EndTime);
  1436. if (dwlFileTime == 0)
  1437. {
  1438. DebugPrintEx(
  1439. DEBUG_ERR,
  1440. TEXT("JonEntry contains invalid time (=0) "));
  1441. SetLastError (ERROR_INVALID_DATA);
  1442. return FALSE;
  1443. }
  1444. if (!FileTimeToSystemTime ((FILETIME*)&dwlFileTime, lpFaxTime))
  1445. {
  1446. DebugPrintEx(
  1447. DEBUG_ERR,
  1448. TEXT("FileTimeToSystemTime failed (ec: %ld)"),
  1449. GetLastError());
  1450. return FALSE;
  1451. }
  1452. return TRUE;
  1453. }
  1454. BOOL
  1455. GetRealFaxTimeAsFileTime (
  1456. const PJOB_ENTRY lpcJobEntry,
  1457. FAX_ENUM_TIME_TYPES TimeType,
  1458. FILETIME* lpFaxTime
  1459. )
  1460. {
  1461. DEBUG_FUNCTION_NAME(TEXT("GetRealFaxTimeAsSystemTime)"));
  1462. Assert (lpcJobEntry);
  1463. Assert (lpFaxTime);
  1464. PJOB_QUEUE pJobQueue = lpcJobEntry->lpJobQueueEntry;
  1465. Assert (pJobQueue);
  1466. DWORDLONG dwlFileTime;
  1467. dwlFileTime = ((TimeType == FAX_TIME_TYPE_START) ? lpcJobEntry->StartTime : lpcJobEntry->EndTime);
  1468. if (dwlFileTime == 0)
  1469. {
  1470. DebugPrintEx(
  1471. DEBUG_ERR,
  1472. TEXT("JonEntry contains invalid time (=0) "));
  1473. SetLastError (ERROR_INVALID_DATA);
  1474. return FALSE;
  1475. }
  1476. *lpFaxTime = *((FILETIME*)&dwlFileTime);
  1477. return TRUE;
  1478. }
  1479. VOID
  1480. FaxExtFreeBuffer(
  1481. LPVOID lpvBuffer
  1482. )
  1483. {
  1484. MemFree( lpvBuffer );
  1485. }
  1486. //
  1487. // Service threads count functions.
  1488. // The service is terminated only when service threads refernce count is 0.
  1489. // When the count is 0 the g_hThreadCountEvent is set.
  1490. // When the count is greater than 0, the g_hThreadCountEvent is reset.
  1491. // EndFaxSvc() waits on g_hThreadCountEvent before starting to cleanup.
  1492. //
  1493. BOOL
  1494. IncreaseServiceThreadsCount(
  1495. VOID
  1496. )
  1497. /*++
  1498. Routine name : IncreaseServiceThreadsCount
  1499. Routine description:
  1500. Safetly increments the service threads reference count
  1501. Author:
  1502. Oded Sacher (OdedS), Dec, 2000
  1503. Arguments:
  1504. VOID
  1505. Return Value:
  1506. BOOL
  1507. --*/
  1508. {
  1509. BOOL bRet = TRUE;
  1510. DEBUG_FUNCTION_NAME(TEXT("IncreaseServiceThreadsCount"));
  1511. EnterCriticalSection (&g_CsServiceThreads);
  1512. g_lServiceThreadsCount++;
  1513. DebugPrintEx(
  1514. DEBUG_MSG,
  1515. TEXT("Current service threads count is %ld"),
  1516. g_lServiceThreadsCount);
  1517. if (1 == g_lServiceThreadsCount)
  1518. {
  1519. bRet = ResetEvent (g_hThreadCountEvent);
  1520. if (FALSE == bRet)
  1521. {
  1522. DebugPrintEx(
  1523. DEBUG_ERR,
  1524. TEXT("ResetEvent failed (g_hThreadCountEvent) (ec: %ld"),
  1525. GetLastError());
  1526. }
  1527. }
  1528. LeaveCriticalSection (&g_CsServiceThreads);
  1529. return bRet;
  1530. }
  1531. BOOL
  1532. DecreaseServiceThreadsCount(
  1533. VOID
  1534. )
  1535. /*++
  1536. Routine name : DecreaseServiceThreadsCount
  1537. Routine description:
  1538. Safetly decrements the service threads reference count
  1539. Author:
  1540. Oded Sacher (OdedS), Dec, 2000
  1541. Arguments:
  1542. VOID
  1543. Return Value:
  1544. BOOL
  1545. --*/
  1546. {
  1547. BOOL bRet = TRUE;
  1548. DEBUG_FUNCTION_NAME(TEXT("DecreaseServiceThreadsCount"));
  1549. Assert (!ThreadOwnsCs()); // verify that the terminating thread does not own a critical section!!!
  1550. EnterCriticalSection (&g_CsServiceThreads);
  1551. g_lServiceThreadsCount--;
  1552. Assert (g_lServiceThreadsCount >= 0);
  1553. DebugPrintEx(
  1554. DEBUG_MSG,
  1555. TEXT("Current service threads count is %ld"),
  1556. g_lServiceThreadsCount);
  1557. if (0 == g_lServiceThreadsCount)
  1558. {
  1559. bRet = SetEvent (g_hThreadCountEvent);
  1560. if (FALSE == bRet)
  1561. {
  1562. DebugPrintEx(
  1563. DEBUG_ERR,
  1564. TEXT("SetEvent failed (g_hThreadCountEvent) (ec: %ld"),
  1565. GetLastError());
  1566. }
  1567. }
  1568. LeaveCriticalSection (&g_CsServiceThreads);
  1569. return bRet;
  1570. }
  1571. HANDLE CreateThreadAndRefCount(
  1572. LPSECURITY_ATTRIBUTES lpThreadAttributes, // SD
  1573. DWORD dwStackSize, // initial stack size
  1574. LPTHREAD_START_ROUTINE lpStartAddress, // thread function
  1575. LPVOID lpParameter, // thread argument
  1576. DWORD dwCreationFlags, // creation option
  1577. LPDWORD lpThreadId // thread identifier
  1578. )
  1579. /*++
  1580. Routine name : CreateThreadAndRefCount
  1581. Routine description:
  1582. Creates a thread and saftely increments the service threads reference count.
  1583. All function parameters and return value are IDENTICAL to CreateThread().
  1584. Author:
  1585. Oded Sacher (OdedS), Dec, 2000
  1586. Arguments:
  1587. lpThreadAttributes [ ] -
  1588. dwStackSize [ ] -
  1589. lpStartAddress [ ] -
  1590. lpParameter [ ] -
  1591. dwCreationFlags [ ] -
  1592. lpThreadId [ ] -
  1593. Return Value:
  1594. HANDLE
  1595. --*/
  1596. {
  1597. HANDLE hThread;
  1598. DWORD ec;
  1599. DEBUG_FUNCTION_NAME(TEXT("CreateThreadAndRefCount"));
  1600. //
  1601. // First enter g_CsServiceThreads so the threads reference counter is always ssynced!
  1602. //
  1603. EnterCriticalSection (&g_CsServiceThreads);
  1604. hThread = CreateThread( lpThreadAttributes,
  1605. dwStackSize,
  1606. lpStartAddress,
  1607. lpParameter,
  1608. dwCreationFlags,
  1609. lpThreadId
  1610. );
  1611. if (NULL == hThread)
  1612. {
  1613. ec = GetLastError();
  1614. DebugPrintEx(
  1615. DEBUG_ERR,
  1616. TEXT("CreateThread failed (ec: %ld"),
  1617. ec);
  1618. }
  1619. else
  1620. {
  1621. if (!IncreaseServiceThreadsCount())
  1622. {
  1623. DebugPrintEx(
  1624. DEBUG_ERR,
  1625. TEXT("IncreaseServiceThreadsCount failed (ec: %ld"),
  1626. GetLastError());
  1627. }
  1628. }
  1629. LeaveCriticalSection (&g_CsServiceThreads);
  1630. if (NULL == hThread)
  1631. {
  1632. SetLastError(ec);
  1633. }
  1634. return hThread;
  1635. }
  1636. LPTSTR
  1637. MapFSPIJobExtendedStatusToString (DWORD dwFSPIExtendedStatus)
  1638. //*********************************************************************************
  1639. //* Name: MapFSPIJobExtendedStatusToString()
  1640. //* Author: Oded sacher
  1641. //* Date: Jan 2002
  1642. //*********************************************************************************
  1643. //* DESCRIPTION:
  1644. //* Maps FSPI extended job status codes to a displayable string
  1645. //* PARAMETERS:
  1646. //* [IN ] DWORD dwFSPIExtendedStatus
  1647. //* The FSPI extended Status code.
  1648. //*
  1649. //* RETURN VALUE:
  1650. //* Pointer to a string describing the well known extended status
  1651. //*
  1652. //*********************************************************************************
  1653. {
  1654. struct _ExStatusStringsMapEntry
  1655. {
  1656. DWORD dwFSPIExtendedStatus;
  1657. DWORD dwStringResourceId;
  1658. } ExStatusStringsMap [] =
  1659. {
  1660. {FSPI_ES_DISCONNECTED, FPS_DISCONNECTED },
  1661. {FSPI_ES_INITIALIZING, FPS_INITIALIZING },
  1662. {FSPI_ES_DIALING, FPS_DIALING },
  1663. {FSPI_ES_TRANSMITTING, FPS_SENDING },
  1664. {FSPI_ES_ANSWERED, FPS_ANSWERED },
  1665. {FSPI_ES_RECEIVING, FPS_RECEIVING },
  1666. {FSPI_ES_LINE_UNAVAILABLE, FPS_UNAVAILABLE },
  1667. {FSPI_ES_BUSY, FPS_BUSY },
  1668. {FSPI_ES_NO_ANSWER, FPS_NO_ANSWER },
  1669. {FSPI_ES_BAD_ADDRESS, FPS_BAD_ADDRESS },
  1670. {FSPI_ES_NO_DIAL_TONE, FPS_NO_DIAL_TONE },
  1671. {FSPI_ES_FATAL_ERROR, FPS_FATAL_ERROR },
  1672. {FSPI_ES_CALL_DELAYED, FPS_CALL_DELAYED },
  1673. {FSPI_ES_CALL_BLACKLISTED, FPS_CALL_BLACKLISTED },
  1674. {FSPI_ES_NOT_FAX_CALL, FPS_NOT_FAX_CALL },
  1675. {FSPI_ES_PARTIALLY_RECEIVED, IDS_PARTIALLY_RECEIVED },
  1676. {FSPI_ES_HANDLED, FPS_HANDLED },
  1677. {FSPI_ES_CALL_COMPLETED, IDS_CALL_COMPLETED },
  1678. {FSPI_ES_CALL_ABORTED, IDS_CALL_ABORTED }
  1679. };
  1680. LPTSTR lptstrString = NULL;
  1681. for (DWORD dwIndex = 0; dwIndex < sizeof (ExStatusStringsMap) / sizeof (_ExStatusStringsMapEntry); dwIndex++)
  1682. {
  1683. if (ExStatusStringsMap[dwIndex].dwFSPIExtendedStatus == dwFSPIExtendedStatus)
  1684. {
  1685. lptstrString = GetString (ExStatusStringsMap[dwIndex].dwStringResourceId);
  1686. break;
  1687. }
  1688. }
  1689. return lptstrString;
  1690. }