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.

2161 lines
49 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. faxmon.c
  5. Abstract:
  6. Implementation of the following print monitor entry points:
  7. InitializePrintMonitor
  8. OpenPort
  9. ClosePort
  10. StartDocPort
  11. EndDocPort
  12. WritePort
  13. ReadPort
  14. Environment:
  15. Windows NT fax print monitor
  16. Revision History:
  17. 05/09/96 -davidx-
  18. Remove caching of ports from the monitor.
  19. 02/22/96 -davidx-
  20. Created it.
  21. mm/dd/yy -author-
  22. description
  23. --*/
  24. #include "faxmon.h"
  25. #include "tiff.h"
  26. #include "faxreg.h"
  27. #include <splapip.h>
  28. //
  29. // Determine whether we're at the beginning of a TIFF file
  30. //
  31. #define ValidTiffFileHeader(p) \
  32. (((LPSTR) (p))[0] == 'I' && ((LPSTR) (p))[1] == 'I' && \
  33. ((PBYTE) (p))[2] == 42 && ((PBYTE) (p))[3] == 0)
  34. //
  35. // Read a DWORD value from an unaligned address
  36. //
  37. #define ReadUnalignedDWord(p) *((DWORD UNALIGNED *) (p))
  38. //
  39. // Write a DWORD value to an unaligned address
  40. //
  41. #define WriteUnalignedDWord(p, value) (*((DWORD UNALIGNED *) (p)) = (value))
  42. //
  43. // Fax monitor name string
  44. //
  45. TCHAR faxMonitorName[CCHDEVICENAME] = TEXT("Windows NT Fax Monitor");
  46. //
  47. // DLL instance handle
  48. //
  49. HANDLE ghInstance = NULL;
  50. //
  51. // Retry parameters when failing to connect to the fax service
  52. // default = infinite retry with 5 seconds interval
  53. //
  54. DWORD connectRetryCount = 0;
  55. DWORD connectRetryInterval = 5;
  56. BOOL
  57. DllEntryPoint(
  58. HANDLE hModule,
  59. ULONG ulReason,
  60. PCONTEXT pContext
  61. )
  62. /*++
  63. Routine Description:
  64. DLL initialization procedure.
  65. Arguments:
  66. hModule - DLL instance handle
  67. ulReason - Reason for the call
  68. pContext - Pointer to context (not used by us)
  69. Return Value:
  70. TRUE if DLL is initialized successfully, FALSE otherwise.
  71. --*/
  72. {
  73. switch (ulReason) {
  74. case DLL_PROCESS_ATTACH:
  75. ghInstance = hModule;
  76. LoadString(ghInstance, IDS_FAX_MONITOR_NAME, faxMonitorName, CCHDEVICENAME);
  77. break;
  78. case DLL_PROCESS_DETACH:
  79. break;
  80. }
  81. return TRUE;
  82. }
  83. LPMONITOREX
  84. InitializePrintMonitor(
  85. LPTSTR pRegistryRoot
  86. )
  87. /*++
  88. Routine Description:
  89. Initialize the print monitor
  90. Arguments:
  91. pRegistryRoot = Points to a string that specifies the registry root for the monitor
  92. Return Value:
  93. Pointer to a MONITOREX structure which contains function pointers
  94. to other print monitor entry points. NULL if there is an error.
  95. --*/
  96. {
  97. static MONITOREX faxmonFuncs = {
  98. sizeof(MONITOR),
  99. {
  100. FaxMonEnumPorts,
  101. FaxMonOpenPort,
  102. NULL, // OpenPortEx
  103. FaxMonStartDocPort,
  104. FaxMonWritePort,
  105. FaxMonReadPort,
  106. FaxMonEndDocPort,
  107. FaxMonClosePort,
  108. FaxMonAddPort,
  109. FaxMonAddPortEx,
  110. FaxMonConfigurePort,
  111. FaxMonDeletePort,
  112. NULL, // GetPrinterDataFromPort
  113. NULL, // SetPortTimeOuts
  114. }
  115. };
  116. Trace("InitializePrintMonitor");
  117. //
  118. // Get fax service connection retry parameters from the registry
  119. //
  120. if (pRegistryRoot) {
  121. HKEY hRegKey;
  122. LONG status;
  123. status = RegCreateKeyEx(HKEY_LOCAL_MACHINE,
  124. pRegistryRoot,
  125. 0,
  126. NULL,
  127. 0,
  128. KEY_READ | KEY_WRITE,
  129. NULL,
  130. &hRegKey,
  131. NULL);
  132. if (status == ERROR_SUCCESS) {
  133. connectRetryCount =
  134. GetRegistryDWord(hRegKey, REGVAL_CONNECT_RETRY_COUNT, connectRetryCount);
  135. connectRetryInterval =
  136. GetRegistryDWord(hRegKey, REGVAL_CONNECT_RETRY_INTERVAL, connectRetryInterval);
  137. RegCloseKey(hRegKey);
  138. }
  139. }
  140. return &faxmonFuncs;
  141. }
  142. BOOL
  143. FaxMonOpenPort(
  144. LPTSTR pPortName,
  145. PHANDLE pHandle
  146. )
  147. /*++
  148. Routine Description:
  149. Provides a port for a newly connected printer
  150. Arguments:
  151. pName - Points to a string that specifies the port name
  152. pHandle - Returns a handle to the port
  153. Return Value:
  154. TRUE if successful, FALSE if there is an error
  155. --*/
  156. {
  157. PFAXPORT pFaxPort = NULL;
  158. Trace("OpenPort");
  159. Assert(pHandle != NULL && pPortName != NULL);
  160. //
  161. // Get information about the specified port
  162. //
  163. if ((pFaxPort = MemAllocZ(sizeof(FAXPORT))) &&
  164. (pPortName = DuplicateString(FAX_PORT_NAME)))
  165. {
  166. pFaxPort->startSig = pFaxPort->endSig = pFaxPort;
  167. pFaxPort->pName = pPortName;
  168. pFaxPort->hFile = INVALID_HANDLE_VALUE;
  169. } else {
  170. MemFree(pFaxPort);
  171. pFaxPort = NULL;
  172. }
  173. *pHandle = (HANDLE) pFaxPort;
  174. return (*pHandle != NULL);
  175. }
  176. VOID
  177. FreeFaxJobInfo(
  178. PFAXPORT pFaxPort
  179. )
  180. /*++
  181. Routine Description:
  182. Free up memory used for maintaining information about the current job
  183. Arguments:
  184. pFaxPort - Points to a fax port structure
  185. Return Value:
  186. NONE
  187. --*/
  188. {
  189. //
  190. // Close and delete the temporary file if necessary
  191. //
  192. if (pFaxPort->hFile != INVALID_HANDLE_VALUE) {
  193. CloseHandle(pFaxPort->hFile);
  194. pFaxPort->hFile = INVALID_HANDLE_VALUE;
  195. }
  196. if (pFaxPort->pFilename) {
  197. DeleteFile(pFaxPort->pFilename);
  198. MemFree(pFaxPort->pFilename);
  199. pFaxPort->pFilename = NULL;
  200. }
  201. if (pFaxPort->hPrinter) {
  202. ClosePrinter(pFaxPort->hPrinter);
  203. pFaxPort->hPrinter = NULL;
  204. }
  205. MemFree(pFaxPort->pPrinterName);
  206. pFaxPort->pPrinterName = NULL;
  207. MemFree(pFaxPort->pParameters);
  208. pFaxPort->pParameters = NULL;
  209. ZeroMemory(&pFaxPort->jobParam, sizeof(pFaxPort->jobParam));
  210. //
  211. // Disconnect from the fax service if necessary
  212. //
  213. if (pFaxPort->hFaxSvc) {
  214. if (! pFaxPort->pFaxClose(pFaxPort->hFaxSvc)) {
  215. Error(("FaxClose failed: %d\n", GetLastError()));
  216. }
  217. FreeLibrary( pFaxPort->hWinFax );
  218. pFaxPort->hFaxSvc = NULL;
  219. pFaxPort->pFaxConnectFaxServerW = NULL;
  220. pFaxPort->pFaxClose = NULL;
  221. pFaxPort->pFaxSendDocumentW = NULL;
  222. pFaxPort->pFaxAccessCheck = NULL;
  223. }
  224. }
  225. BOOL
  226. FaxMonClosePort(
  227. HANDLE hPort
  228. )
  229. /*++
  230. Routine Description:
  231. Closes the port specified by hPort when there are no printers connected to it
  232. Arguments:
  233. hPort - Specifies the handle of the port to be close
  234. Return Value:
  235. TRUE if successful, FALSE if there is an error
  236. --*/
  237. {
  238. PFAXPORT pFaxPort = (PFAXPORT) hPort;
  239. Trace("ClosePort");
  240. //
  241. // Make sure we have a valid handle
  242. //
  243. if (! ValidFaxPort(pFaxPort)) {
  244. Error(("Trying to close an invalid fax port handle\n"));
  245. SetLastError(ERROR_INVALID_HANDLE);
  246. return FALSE;
  247. }
  248. //
  249. // Free up memory used for maintaining information about the current job
  250. //
  251. FreeFaxJobInfo(pFaxPort);
  252. MemFree(pFaxPort->pName);
  253. MemFree(pFaxPort);
  254. return TRUE;
  255. }
  256. LPTSTR
  257. CreateTempFaxFile(
  258. VOID
  259. )
  260. /*++
  261. Routine Description:
  262. Create a temporary file in the system spool directory for storing fax data
  263. Arguments:
  264. NONE
  265. Return Value:
  266. Pointer to the name of the newly created temporary file
  267. NULL if there is an error
  268. --*/
  269. {
  270. //TCHAR spoolDir[MAX_PATH];
  271. //HANDLE hServer;
  272. LPTSTR pFilename;
  273. TCHAR TempDir[MAX_PATH];
  274. TCHAR FileName[MAX_PATH];
  275. //
  276. // Allocate a memory buffer for holding the temporary filename
  277. //
  278. if (pFilename = MemAlloc(sizeof(TCHAR) * MAX_PATH)) {
  279. if (!GetTempPath(sizeof(TempDir)/sizeof(TCHAR),TempDir)||
  280. !GetTempFileName(TempDir, TEXT("fax"), 0, FileName))
  281. {
  282. MemFree(pFilename);
  283. pFilename = NULL;
  284. } else {
  285. lstrcpy(pFilename,FileName);
  286. }
  287. }
  288. if (! pFilename)
  289. Error(("Failed to create temporary file in the spool directory\n"));
  290. return pFilename;
  291. }
  292. BOOL
  293. OpenTempFaxFile(
  294. PFAXPORT pFaxPort,
  295. BOOL doAppend
  296. )
  297. /*++
  298. Routine Description:
  299. Open a handle to the current fax job file associated with a port
  300. Arguments:
  301. pFaxPort - Points to a fax port structure
  302. doAppend - Specifies whether to discard existing data in the file or
  303. append new data to it
  304. Return Value:
  305. TRUE if successful, FALSE otherwise
  306. --*/
  307. {
  308. DWORD creationFlags;
  309. Assert(pFaxPort->pFilename && pFaxPort->hFile == INVALID_HANDLE_VALUE);
  310. Verbose(("Temporary fax job file: %ws\n", pFaxPort->pFilename));
  311. //
  312. // Open the file for reading and writing
  313. //
  314. creationFlags = doAppend ? OPEN_ALWAYS : (OPEN_ALWAYS | TRUNCATE_EXISTING);
  315. pFaxPort->hFile = CreateFile(pFaxPort->pFilename,
  316. GENERIC_READ | GENERIC_WRITE,
  317. 0,
  318. NULL,
  319. creationFlags,
  320. FILE_ATTRIBUTE_NORMAL,
  321. NULL);
  322. //
  323. // If we're appending, then move the file pointer to end of file
  324. //
  325. if (doAppend && pFaxPort->hFile != INVALID_HANDLE_VALUE &&
  326. SetFilePointer(pFaxPort->hFile, 0, NULL, FILE_END) == 0xffffffff)
  327. {
  328. Error(("SetFilePointer failed: %d\n", GetLastError()));
  329. CloseHandle(pFaxPort->hFile);
  330. pFaxPort->hFile = INVALID_HANDLE_VALUE;
  331. }
  332. return (pFaxPort->hFile != INVALID_HANDLE_VALUE);
  333. }
  334. LPCTSTR
  335. ExtractFaxTag(
  336. LPCTSTR pTagKeyword,
  337. LPCTSTR pTaggedStr,
  338. INT *pcch
  339. )
  340. /*++
  341. Routine Description:
  342. Find the value of for the specified tag in a tagged string.
  343. Arguments:
  344. pTagKeyword - specifies the interested tag keyword
  345. pTaggedStr - points to the tagged string to be searched
  346. pcch - returns the length of the specified tag value (if found)
  347. Return Value:
  348. Points to the value for the specified tag.
  349. NULL if the specified tag is not found
  350. NOTE:
  351. Tagged strings have the following form:
  352. <tag>value<tag>value
  353. The format of tags is defined as:
  354. <$FAXTAG$ tag-name>
  355. There is exactly one space between the tag keyword and the tag name.
  356. Characters in a tag are case-sensitive.
  357. --*/
  358. {
  359. LPCTSTR pValue;
  360. if (pValue = _tcsstr(pTaggedStr, pTagKeyword)) {
  361. pValue += _tcslen(pTagKeyword);
  362. if (pTaggedStr = _tcsstr(pValue, FAXTAG_PREFIX))
  363. *pcch = (INT)(pTaggedStr - pValue);
  364. else
  365. *pcch = _tcslen(pValue);
  366. }
  367. return pValue;
  368. }
  369. BOOL
  370. GetJobInfo(
  371. PFAXPORT pFaxPort,
  372. DWORD jobId
  373. )
  374. /*++
  375. Routine Description:
  376. Retrieve recipient information from the devmode associated with the job
  377. Arguments:
  378. pFaxPort - Points to a fax port structure
  379. jobId - Specifies the current job ID
  380. Return Value:
  381. TRUE if the job parameters are successfully retrieved or
  382. the fax job is from a downlevel fax client.
  383. FALSE if there is an error.
  384. --*/
  385. {
  386. JOB_INFO_2 *pJobInfo2;
  387. LPCTSTR pParameters = NULL;
  388. //
  389. // Retrieve the parameter string associated with the specified job.
  390. // If there is no job parameter or the parameter contains the tag
  391. // <$FAXTAG$ DOWNLEVEL>, then we assume the job comes from downlevel client.
  392. //
  393. ZeroMemory(&pFaxPort->jobParam, sizeof(pFaxPort->jobParam));
  394. pFaxPort->jobParam.SizeOfStruct = sizeof( FAX_JOB_PARAM );
  395. if ((pJobInfo2 = MyGetJob(pFaxPort->hPrinter, 2, jobId)) == NULL ||
  396. (pParameters = pJobInfo2->pParameters) == NULL ||
  397. _tcsstr(pParameters, FAXTAG_DOWNLEVEL_CLIENT) != NULL)
  398. {
  399. MemFree(pJobInfo2);
  400. return TRUE;
  401. }
  402. if ((pFaxPort->pParameters = DuplicateString(pParameters)) != NULL) {
  403. //
  404. // Tags used to pass information about fax jobs
  405. //
  406. static LPCTSTR faxtagNames[NUM_JOBPARAM_TAGS] = {
  407. FAXTAG_RECIPIENT_NUMBER,
  408. FAXTAG_RECIPIENT_NAME,
  409. FAXTAG_TSID,
  410. FAXTAG_SENDER_NAME,
  411. FAXTAG_SENDER_COMPANY,
  412. FAXTAG_SENDER_DEPT,
  413. FAXTAG_BILLING_CODE,
  414. FAXTAG_WHEN_TO_SEND,
  415. FAXTAG_SEND_AT_TIME,
  416. FAXTAG_PROFILE_NAME,
  417. FAXTAG_EMAIL_NAME
  418. };
  419. LPTSTR WhenToSend = NULL;
  420. LPTSTR SendAtTime = NULL;
  421. LPTSTR DeliveryReportType = NULL;
  422. LPTSTR *fieldStr[NUM_JOBPARAM_TAGS] = {
  423. (LPTSTR *)&pFaxPort->jobParam.RecipientNumber,
  424. (LPTSTR *)&pFaxPort->jobParam.RecipientName,
  425. (LPTSTR *)&pFaxPort->jobParam.Tsid,
  426. (LPTSTR *)&pFaxPort->jobParam.SenderName,
  427. (LPTSTR *)&pFaxPort->jobParam.SenderCompany,
  428. (LPTSTR *)&pFaxPort->jobParam.SenderDept,
  429. (LPTSTR *)&pFaxPort->jobParam.BillingCode,
  430. &WhenToSend,
  431. &SendAtTime,
  432. (LPTSTR *)&pFaxPort->jobParam.DeliveryReportAddress,
  433. &DeliveryReportType
  434. };
  435. INT fieldLen[NUM_JOBPARAM_TAGS];
  436. INT count;
  437. pParameters = pFaxPort->pParameters;
  438. Verbose(("JOB_INFO_2.pParameter = %ws\n", pParameters));
  439. //
  440. // Extract individual fields out of the tagged string
  441. //
  442. for (count=0; count < NUM_JOBPARAM_TAGS; count++) {
  443. *fieldStr[count] = (LPTSTR)ExtractFaxTag(faxtagNames[count],
  444. pParameters,
  445. &fieldLen[count]);
  446. }
  447. //
  448. // Null-terminate each field
  449. //
  450. for (count=0; count < NUM_JOBPARAM_TAGS; count++) {
  451. if (*fieldStr[count]) {
  452. (*fieldStr[count])[fieldLen[count]] = NUL;
  453. }
  454. }
  455. if (WhenToSend) {
  456. if (_tcsicmp( WhenToSend, TEXT("cheap") ) == 0) {
  457. pFaxPort->jobParam.ScheduleAction = JSA_DISCOUNT_PERIOD;
  458. } else if (_tcsicmp( WhenToSend, TEXT("at") ) == 0) {
  459. pFaxPort->jobParam.ScheduleAction = JSA_SPECIFIC_TIME;
  460. }
  461. }
  462. if (SendAtTime) {
  463. if (_tcslen(SendAtTime) == 5 && SendAtTime[2] == L':' &&
  464. _istdigit(SendAtTime[0]) && _istdigit(SendAtTime[1]) &&
  465. _istdigit(SendAtTime[3]) && _istdigit(SendAtTime[4]))
  466. {
  467. DWORDLONG FileTime;
  468. SYSTEMTIME LocalTime;
  469. INT Minutes;
  470. INT SendMinutes;
  471. SendAtTime[2] = 0;
  472. //
  473. // Calculate the number of minutes from now to send and add that to the current time.
  474. //
  475. GetLocalTime( &LocalTime );
  476. SystemTimeToFileTime( &LocalTime, (LPFILETIME) &FileTime );
  477. SendMinutes = min(23,_ttoi( &SendAtTime[0] )) * 60 + min(59,_ttoi( &SendAtTime[3] ));
  478. Minutes = LocalTime.wHour * 60 + LocalTime.wMinute;
  479. Minutes = SendMinutes - Minutes;
  480. // Account for passing midnight
  481. //
  482. if (Minutes < 0) {
  483. Minutes += 24 * 60;
  484. }
  485. FileTime += (DWORDLONG)(Minutes * 60I64 * 1000I64 * 1000I64 * 10I64);
  486. FileTimeToSystemTime((LPFILETIME) &FileTime, &pFaxPort->jobParam.ScheduleTime );
  487. SendAtTime[2] = L':';
  488. }
  489. }
  490. if (DeliveryReportType) {
  491. if (_tcsicmp( DeliveryReportType, TEXT("email") ) == 0) {
  492. pFaxPort->jobParam.DeliveryReportType = DRT_EMAIL;
  493. } else if (_tcsicmp( DeliveryReportType, TEXT("inbox") ) == 0) {
  494. pFaxPort->jobParam.DeliveryReportType = DRT_INBOX;
  495. }
  496. }
  497. if (pFaxPort->jobParam.RecipientNumber == NULL) {
  498. Error(("Missing recipient phone number.\n"));
  499. SetJob(pFaxPort->hPrinter, jobId, 0, NULL, JOB_CONTROL_PAUSE);
  500. SetLastError(ERROR_INVALID_PARAMETER);
  501. }
  502. }
  503. MemFree(pJobInfo2);
  504. return (pFaxPort->jobParam.RecipientNumber != NULL);
  505. }
  506. BOOL
  507. FaxMonStartDocPort(
  508. HANDLE hPort,
  509. LPTSTR pPrinterName,
  510. DWORD JobId,
  511. DWORD Level,
  512. LPBYTE pDocInfo
  513. )
  514. /*++
  515. Routine Description:
  516. Spooler calls this function to start a new print job on the port
  517. Arguments:
  518. hPort - Identifies the port
  519. pPrinterName - Specifies the name of the printer to which the job is being sent
  520. JobId - Identifies the job being sent by the spooler
  521. Level - Specifies the DOC_INFO_x level
  522. pDocInfo - Points to the document information
  523. Return Value:
  524. TRUE if successful, FALSE if there is an error
  525. --*/
  526. {
  527. PFAXPORT pFaxPort = (PFAXPORT) hPort;
  528. Verbose(("Entering StartDocPort: %d ...\n", JobId));
  529. //
  530. // Make sure we have a valid handle
  531. //
  532. if (! ValidFaxPort(pFaxPort)) {
  533. Error(("StartDocPort is given an invalid fax port handle\n"));
  534. SetLastError(ERROR_INVALID_HANDLE);
  535. return FALSE;
  536. }
  537. //
  538. // Check if we're at the beginning of a series of chained jobs
  539. //
  540. if (! pFaxPort->hFaxSvc) {
  541. PJOB_INFO_1 pJobInfo1;
  542. PORT_INFO_3 portInfo3;
  543. HANDLE hPrinter = NULL;
  544. BOOL offline = FALSE;
  545. DWORD count = connectRetryCount;
  546. DWORD jobStatus = 0;
  547. Assert(pFaxPort->pPrinterName == NULL &&
  548. pFaxPort->hPrinter == NULL &&
  549. pFaxPort->pParameters == NULL &&
  550. pFaxPort->pFilename == NULL &&
  551. pFaxPort->hFile == INVALID_HANDLE_VALUE);
  552. //
  553. // load the winfax dll
  554. //
  555. pFaxPort->hWinFax = LoadLibrary( L"winfax.dll" );
  556. if (pFaxPort->hWinFax == NULL) {
  557. Error(("LoadLibrary failed loading winfax.dll\n"));
  558. return FALSE;
  559. }
  560. //
  561. // get the function addresses
  562. //
  563. pFaxPort->pFaxConnectFaxServerW = (PFAXCONNECTFAXSERVERW) GetProcAddress( pFaxPort->hWinFax, "FaxConnectFaxServerW" );
  564. pFaxPort->pFaxClose = (PFAXCLOSE) GetProcAddress( pFaxPort->hWinFax, "FaxClose" );
  565. pFaxPort->pFaxSendDocumentW = (PFAXSENDDOCUMENTW) GetProcAddress( pFaxPort->hWinFax, "FaxSendDocumentW" );
  566. pFaxPort->pFaxAccessCheck = (PFAXACCESSCHECK) GetProcAddress( pFaxPort->hWinFax, "FaxAccessCheck" );
  567. if (pFaxPort->pFaxConnectFaxServerW == NULL ||
  568. pFaxPort->pFaxClose == NULL ||
  569. pFaxPort->pFaxSendDocumentW == NULL ||
  570. pFaxPort->pFaxAccessCheck == NULL) {
  571. Error(("GetProcAddress failed loading winfax.dll\n"));
  572. return FALSE;
  573. }
  574. //
  575. // Connect to the fax service and obtain a session handle
  576. //
  577. while (! pFaxPort->pFaxConnectFaxServerW(NULL, &pFaxPort->hFaxSvc)) {
  578. Error(("FaxConnectFaxServer failed: %d\n", GetLastError()));
  579. pFaxPort->hFaxSvc = NULL;
  580. if (! offline) {
  581. portInfo3.dwStatus = PORT_STATUS_OFFLINE;
  582. portInfo3.pszStatus = NULL;
  583. portInfo3.dwSeverity = PORT_STATUS_TYPE_WARNING;
  584. if (! SetPort(NULL, pFaxPort->pName, 3, (LPBYTE) &portInfo3))
  585. Error(("SetPort failed: %d\n", GetLastError()));
  586. }
  587. offline = TRUE;
  588. Sleep(connectRetryInterval * 1000);
  589. //
  590. // Check if the job has been deleted or restarted
  591. //
  592. if (!hPrinter && !OpenPrinter(pPrinterName, &hPrinter, NULL)) {
  593. Error(("OpenPrinter failed: %d\n", GetLastError()));
  594. hPrinter = NULL;
  595. } else if (pJobInfo1 = MyGetJob(hPrinter, 1, JobId)) {
  596. jobStatus = pJobInfo1->Status;
  597. }
  598. MemFree(pJobInfo1);
  599. if (--count == 0 || (jobStatus & (JOB_STATUS_DELETING|
  600. JOB_STATUS_DELETED|
  601. JOB_STATUS_RESTART)))
  602. {
  603. break;
  604. }
  605. }
  606. //
  607. // Remove the offline status on the port
  608. //
  609. if (offline) {
  610. portInfo3.dwStatus = 0;
  611. portInfo3.pszStatus = NULL;
  612. portInfo3.dwSeverity = PORT_STATUS_TYPE_INFO;
  613. if (! SetPort(NULL, pFaxPort->pName, 3, (LPBYTE) &portInfo3)) {
  614. Error(("SetPort failed: %d\n", GetLastError()));
  615. }
  616. }
  617. if (hPrinter) {
  618. ClosePrinter(hPrinter);
  619. }
  620. if (pFaxPort->hFaxSvc) {
  621. if (!pFaxPort->pFaxAccessCheck(pFaxPort->hFaxSvc, FAX_JOB_SUBMIT) ) {
  622. FreeFaxJobInfo(pFaxPort);
  623. Error(("FaxAccessCheck failed : %d\n", GetLastError() ));
  624. SetLastError(ERROR_ACCESS_DENIED);
  625. return FALSE;
  626. }
  627. // HANDLE hToken;
  628. //
  629. // The monitor runs in the context of the current job's owner.
  630. // In order to create temporary files in the spool directory,
  631. // we need to revert to the spooler context first.
  632. //
  633. /* if (! (hToken = RevertToPrinterSelf()))
  634. Error(("RevertToPrinterSelf failed: %d\n", GetLastError()));
  635. */
  636. //
  637. // Remember the printer name because we'll need it at EndDocPort time.
  638. // Get a temporary filename and open it for reading and writing.
  639. // Remember other job related information
  640. //
  641. if (! (pFaxPort->pPrinterName = DuplicateString(pPrinterName)) ||
  642. ! OpenPrinter(pPrinterName, &pFaxPort->hPrinter, NULL) ||
  643. ! GetJobInfo(pFaxPort, JobId) ||
  644. ! (pFaxPort->pFilename = CreateTempFaxFile()) ||
  645. ! OpenTempFaxFile(pFaxPort, FALSE))
  646. {
  647. FreeFaxJobInfo(pFaxPort);
  648. } else
  649. pFaxPort->jobId = pFaxPort->nextJobId = JobId;
  650. //
  651. // Switch back to the original context if necessary
  652. //
  653. /* if (hToken && !ImpersonatePrinterClient(hToken))
  654. Error(("ImpersonatePrinterClient failed: %d\n", GetLastError())); */
  655. }
  656. } else {
  657. Assert(pFaxPort->jobId == JobId);
  658. }
  659. return (pFaxPort->hFaxSvc != NULL);
  660. }
  661. INT
  662. FixUpFaxFile(
  663. PFAXPORT pFaxPort
  664. )
  665. /*++
  666. Routine Description:
  667. Fixed up the saved print job data into a well-formed TIFF file
  668. Arguments:
  669. pFaxPort - Points to a fax port structure
  670. Return Value:
  671. error code FAXERR_*
  672. --*/
  673. {
  674. DWORD fileSize;
  675. PBYTE pFileEnd, pFileHdr;
  676. PBYTE pFileView = NULL;
  677. HANDLE hFileMap = NULL;
  678. INT result = FAXERR_BAD_TIFF;
  679. //
  680. // Get the size of print job file
  681. //
  682. FlushFileBuffers(pFaxPort->hFile);
  683. if ((fileSize = GetFileSize(pFaxPort->hFile, NULL)) == 0)
  684. return FAXERR_IGNORE;
  685. if (fileSize == 0xffffffff)
  686. return FAXERR_FATAL;
  687. __try {
  688. //
  689. // Map the fax job file into memory
  690. //
  691. if ((hFileMap = CreateFileMapping(pFaxPort->hFile, NULL, PAGE_READWRITE, 0, 0, NULL)) &&
  692. (pFileHdr = pFileView = MapViewOfFile(hFileMap, FILE_MAP_WRITE, 0, 0, fileSize)) &&
  693. ValidTiffFileHeader(pFileHdr))
  694. {
  695. DWORD ifdOffset, maxOffset, fileOffset;
  696. PBYTE pIfdOffset;
  697. //
  698. // A fax print job may contain multiple TIFF files. Each each iteration
  699. // of the outer loop below deals with a single embedded TIFF file.
  700. //
  701. pFileEnd = pFileHdr + fileSize;
  702. ifdOffset = ReadUnalignedDWord(pFileHdr + sizeof(DWORD));
  703. do {
  704. Verbose(("Reading embedded TIFF file ...\n"));
  705. maxOffset = 0;
  706. fileOffset = (DWORD)(pFileHdr - pFileView);
  707. //
  708. // Each iteration of the following loops processes one IFD
  709. // from an embedded TIFF file.
  710. //
  711. do {
  712. PTIFF_IFD pIfd;
  713. PTIFF_TAG pIfdEntry;
  714. INT ifdCount;
  715. DWORD size, index, stripCount = 0;
  716. PDWORD pStripOffsets = NULL;
  717. pIfd = (PTIFF_IFD) (pFileHdr + ifdOffset);
  718. Assert( (PBYTE) pIfd < pFileEnd);
  719. if ((PBYTE) pIfd >= pFileEnd) {
  720. result = FAXERR_FATAL;
  721. __leave;
  722. }
  723. ifdOffset += sizeof(WORD) + pIfd->wEntries * sizeof(TIFF_TAG);
  724. pIfdOffset = pFileHdr + ifdOffset;
  725. Assert(pIfdOffset < pFileEnd);
  726. if (pIfdOffset >= pFileEnd) {
  727. result = FAXERR_FATAL;
  728. __leave;
  729. }
  730. if ((ifdOffset + sizeof(DWORD)) > maxOffset)
  731. maxOffset = ifdOffset + sizeof(DWORD);
  732. //
  733. // We should add the file offset to any non-zero IFD offset
  734. //
  735. if ((ifdOffset = ReadUnalignedDWord(pIfdOffset)) != 0)
  736. WriteUnalignedDWord(pIfdOffset, ifdOffset + fileOffset);
  737. //
  738. // Now go through each IFD entry and calculate the largest offset
  739. //
  740. pIfdEntry = (PTIFF_TAG) ((PBYTE) pIfd + sizeof(WORD));
  741. ifdCount = pIfd->wEntries;
  742. Verbose((" Reading IFD: %d entries ...\n", ifdCount));
  743. while (ifdCount-- > 0) {
  744. //
  745. // Figure the size of various TIFF data types
  746. //
  747. switch (pIfdEntry->DataType) {
  748. case TIFF_ASCII:
  749. case TIFF_BYTE:
  750. case TIFF_SBYTE:
  751. case TIFF_UNDEFINED:
  752. size = 1;
  753. break;
  754. case TIFF_SHORT:
  755. case TIFF_SSHORT:
  756. size = 2;
  757. break;
  758. case TIFF_LONG:
  759. case TIFF_SLONG:
  760. case TIFF_FLOAT:
  761. size = 4;
  762. break;
  763. case TIFF_RATIONAL:
  764. case TIFF_SRATIONAL:
  765. case TIFF_DOUBLE:
  766. size = 8;
  767. break;
  768. default:
  769. Warning(("Unknown TIFF data type: %d\n", pIfdEntry->DataType));
  770. size = 1;
  771. break;
  772. }
  773. //
  774. // Look for StripOffsets and StripByteCounts tags
  775. //
  776. if (pIfdEntry->TagId == TIFFTAG_STRIPOFFSETS ||
  777. pIfdEntry->TagId == TIFFTAG_STRIPBYTECOUNTS)
  778. {
  779. DWORD n = pIfdEntry->DataCount;
  780. if ((pIfdEntry->DataType == TIFF_LONG) &&
  781. (stripCount == 0 || stripCount == n) &&
  782. (pStripOffsets || (pStripOffsets = MemAllocZ(sizeof(DWORD)*n))))
  783. {
  784. if ((stripCount = n) == 1) {
  785. pStripOffsets[0] += pIfdEntry->DataOffset;
  786. if (pIfdEntry->TagId == TIFFTAG_STRIPOFFSETS)
  787. pIfdEntry->DataOffset += fileOffset;
  788. } else {
  789. DWORD UNALIGNED *p;
  790. Verbose(("Multiple strips per page: %d\n", n));
  791. p = (DWORD UNALIGNED *) (pFileHdr + pIfdEntry->DataOffset);
  792. for (index=0; index < stripCount; index++) {
  793. n = *p;
  794. pStripOffsets[index] += n;
  795. if (pIfdEntry->TagId == TIFFTAG_STRIPOFFSETS)
  796. *p = n + fileOffset;
  797. p = (DWORD UNALIGNED *) ((LPBYTE) p + sizeof(DWORD));
  798. }
  799. }
  800. } else
  801. Error(("Bad StripOffsets/StripByteCounts tag\n"));
  802. }
  803. //
  804. // For a composite value, IFDENTRY.value is an offset
  805. //
  806. if (size * pIfdEntry->DataCount > sizeof(DWORD)) {
  807. if (pIfdEntry->DataOffset > maxOffset)
  808. maxOffset = pIfdEntry->DataOffset;
  809. pIfdEntry->DataOffset += fileOffset;
  810. }
  811. pIfdEntry++;
  812. }
  813. //
  814. // Make sure to skip the image data when search for the next file
  815. //
  816. if (pStripOffsets) {
  817. for (index=0; index < stripCount; index++) {
  818. if (pStripOffsets[index] > maxOffset)
  819. maxOffset = pStripOffsets[index];
  820. }
  821. MemFree(pStripOffsets);
  822. }
  823. } while (ifdOffset);
  824. //
  825. // Search for the beginning of next TIFF file
  826. //
  827. pFileHdr += maxOffset;
  828. while (pFileHdr < pFileEnd) {
  829. if (ValidTiffFileHeader(pFileHdr)) {
  830. //
  831. // Modify the offset in the last IFD
  832. //
  833. ifdOffset = ReadUnalignedDWord(pFileHdr + sizeof(DWORD));
  834. WriteUnalignedDWord(pIfdOffset, ifdOffset + (DWORD)(pFileHdr - pFileView));
  835. break;
  836. }
  837. pFileHdr++;
  838. }
  839. } while (pFileHdr < pFileEnd);
  840. result = FAXERR_NONE;
  841. }
  842. } __finally {
  843. //
  844. // Perform necessary cleanup before returning to caller
  845. //
  846. if (pFileView)
  847. UnmapViewOfFile(pFileView);
  848. if (hFileMap)
  849. CloseHandle(hFileMap);
  850. CloseHandle(pFaxPort->hFile);
  851. pFaxPort->hFile = INVALID_HANDLE_VALUE;
  852. }
  853. return result;
  854. }
  855. INT
  856. CheckJobRestart(
  857. PFAXPORT pFaxPort
  858. )
  859. /*++
  860. Routine Description:
  861. Check if the job has been restarted.
  862. If not, get the ID of the next job in the chain.
  863. Arguments:
  864. pFaxPort - Points to a fax port structure
  865. Return Value:
  866. FAXERR_RESTART or FAXERR_NONE
  867. --*/
  868. {
  869. JOB_INFO_3 *pJobInfo3;
  870. JOB_INFO_2 *pJobInfo2;
  871. INT status = FAXERR_NONE;
  872. //
  873. // If not, get the ID of the next job in the chain.
  874. //
  875. Verbose(("Job chain: id = %d\n", pFaxPort->nextJobId));
  876. if (pJobInfo3 = MyGetJob(pFaxPort->hPrinter, 3, pFaxPort->jobId)) {
  877. pFaxPort->nextJobId = pJobInfo3->NextJobId;
  878. MemFree(pJobInfo3);
  879. } else
  880. pFaxPort->nextJobId = 0;
  881. //
  882. // Determine whether the job has been restarted or deleted
  883. //
  884. if (pJobInfo2 = MyGetJob(pFaxPort->hPrinter, 2, pFaxPort->jobId)) {
  885. if (pJobInfo2->Status & (JOB_STATUS_RESTART | JOB_STATUS_DELETING))
  886. status = FAXERR_RESTART;
  887. MemFree(pJobInfo2);
  888. }
  889. return status;
  890. }
  891. BOOL
  892. FaxMonEndDocPort(
  893. HANDLE hPort
  894. )
  895. /*++
  896. Routine Description:
  897. Spooler calls this function at the end of a print job
  898. Arguments:
  899. hPort - Identifies the port
  900. Return Value:
  901. TRUE if successful, FALSE if there is an error
  902. --*/
  903. {
  904. PFAXPORT pFaxPort = (PFAXPORT) hPort;
  905. INT status;
  906. LPTSTR pAtSign, pNewRecipName = NULL;
  907. //HANDLE hToken;
  908. DWORD FaxJobId;
  909. BOOL Rslt;
  910. JOB_INFO_2 *pJobInfo2;
  911. Trace("EndDocPort");
  912. //
  913. // Make sure we have a valid handle
  914. //
  915. if (! ValidFaxPort(pFaxPort) || ! pFaxPort->hFaxSvc) {
  916. Error(("EndDocPort is given an invalid fax port handle\n"));
  917. SetLastError(ERROR_INVALID_HANDLE);
  918. return FALSE;
  919. }
  920. //
  921. // Check if the job has been restarted. If not, get the ID of
  922. // the next job in the chain.
  923. //
  924. if ((status = CheckJobRestart(pFaxPort)) != FAXERR_NONE)
  925. goto ExitEndDocPort;
  926. //
  927. // Check if we're at the end of a job chain
  928. //
  929. if (pFaxPort->nextJobId != 0 && pFaxPort->pParameters != NULL) {
  930. SetJob(pFaxPort->hPrinter, pFaxPort->jobId, 0, NULL, JOB_CONTROL_SENT_TO_PRINTER);
  931. return TRUE;
  932. }
  933. //
  934. // The monitor runs in the context of the current job's owner.
  935. // In order to create temporary files in the spool directory,
  936. // we need to revert to the spooler context first.
  937. //
  938. /* if (! (hToken = RevertToPrinterSelf()))
  939. Error(("RevertToPrinterSelf failed: %d\n", GetLastError()));
  940. */
  941. //
  942. // Check if we're dealing with fax jobs from win31 or win95 clients
  943. //
  944. if ((pFaxPort->pParameters == NULL) &&
  945. (status = ProcessDownlevelFaxJob(pFaxPort)) != FAXERR_NONE)
  946. {
  947. goto ExitEndDocPort;
  948. }
  949. //
  950. // Fix up the temporary fax data into a properly formatted TIFF file.
  951. //
  952. if ((status = FixUpFaxFile(pFaxPort)) != FAXERR_NONE) {
  953. goto ExitEndDocPort;
  954. }
  955. //
  956. // Call the fax service to send the TIFF file
  957. //
  958. #if DBG
  959. if (_debugLevel > 0) {
  960. DbgPrint("Send document to fax service:\n");
  961. DbgPrint(" Printer Name: %ws\n", pFaxPort->pPrinterName);
  962. DbgPrint(" Job ID: %d\n", pFaxPort->jobId);
  963. DbgPrint(" File Name: %ws\n", pFaxPort->pFilename);
  964. DbgPrint(" Recipient Number: %ws\n", pFaxPort->jobParam.RecipientNumber);
  965. DbgPrint(" Recipient Name: %ws\n", pFaxPort->jobParam.RecipientName);
  966. DbgPrint(" TSID: %ws\n", pFaxPort->jobParam.Tsid);
  967. DbgPrint(" Sender Name: %ws\n", pFaxPort->jobParam.SenderName);
  968. DbgPrint(" Sender Company: %ws\n", pFaxPort->jobParam.SenderCompany);
  969. DbgPrint(" Sender Dept: %ws\n", pFaxPort->jobParam.SenderDept);
  970. DbgPrint(" Billing Code: %ws\n", pFaxPort->jobParam.BillingCode);
  971. }
  972. #endif
  973. //
  974. // fixup the fax address
  975. //
  976. if (pAtSign = _tcschr(pFaxPort->jobParam.RecipientNumber, TEXT('@'))) {
  977. *pAtSign++ = NUL;
  978. if (pFaxPort->jobParam.RecipientName == NULL)
  979. pNewRecipName = (LPTSTR) pFaxPort->jobParam.RecipientName = (LPTSTR)DuplicateString(pFaxPort->jobParam.RecipientNumber);
  980. _tcscpy((LPTSTR)pFaxPort->jobParam.RecipientNumber, pAtSign);
  981. }
  982. //
  983. // send the fax
  984. //
  985. pJobInfo2 = MyGetJob( pFaxPort->hPrinter, 2, pFaxPort->jobId );
  986. if (pJobInfo2) {
  987. pFaxPort->jobParam.DocumentName = pJobInfo2->pDocument;
  988. } else {
  989. pFaxPort->jobParam.DocumentName = NULL;
  990. }
  991. /* if (hToken && !ImpersonatePrinterClient(hToken)) {
  992. Error(("ImpersonatePrinterClient failed: %d\n", GetLastError()));
  993. } */
  994. pFaxPort->jobParam.Reserved[0] = 0xffffffff;
  995. pFaxPort->jobParam.Reserved[1] = pFaxPort->jobId;
  996. Rslt = pFaxPort->pFaxSendDocumentW( pFaxPort->hFaxSvc, pFaxPort->pFilename, &pFaxPort->jobParam, NULL, &FaxJobId );
  997. /* if (! (hToken = RevertToPrinterSelf())) {
  998. Error(("RevertToPrinterSelf failed: %d\n", GetLastError()));
  999. } */
  1000. if (pJobInfo2) {
  1001. MemFree( pJobInfo2 );
  1002. pFaxPort->jobParam.DocumentName = NULL;
  1003. }
  1004. if (Rslt) {
  1005. status = FAXERR_NONE;
  1006. SetJob(pFaxPort->hPrinter, pFaxPort->jobId, 0, NULL, JOB_CONTROL_SENT_TO_PRINTER);
  1007. DeleteFile( pFaxPort->pFilename );
  1008. } else {
  1009. status = GetLastError();
  1010. Error(("FaxSendDocumentForSpooler failed: %d\n", GetLastError()));
  1011. }
  1012. ExitEndDocPort:
  1013. if (status == FAXERR_NONE) {
  1014. //
  1015. // If the job was successfully sent to the fax service, then
  1016. // the service will delete the temporary file when it's done
  1017. // with it. So we don't need to delete it here.
  1018. //
  1019. MemFree(pFaxPort->pFilename);
  1020. pFaxPort->pFilename = NULL;
  1021. } else {
  1022. //
  1023. // If the job wasn't successfully sent to the fax service,
  1024. // inform the spooler that there is an error on the job.
  1025. //
  1026. // Or if the print job has no data, simply ignore it.
  1027. //
  1028. switch (status) {
  1029. case FAXERR_RESTART:
  1030. Warning(("Job restarted or deleted: id = %d\n", pFaxPort->jobId));
  1031. case FAXERR_IGNORE:
  1032. SetJob(pFaxPort->hPrinter, pFaxPort->jobId, 0, NULL, JOB_CONTROL_SENT_TO_PRINTER);
  1033. break;
  1034. default:
  1035. Error(("Error sending fax job: id = %d\n", pFaxPort->jobId));
  1036. SetJob(pFaxPort->hPrinter, pFaxPort->jobId, 0, NULL, JOB_CONTROL_PAUSE);
  1037. SetJobStatus(pFaxPort->hPrinter, pFaxPort->jobId, status);
  1038. break;
  1039. }
  1040. }
  1041. if (pNewRecipName) {
  1042. MemFree(pNewRecipName);
  1043. pFaxPort->jobParam.RecipientName = NULL;
  1044. }
  1045. FreeFaxJobInfo(pFaxPort);
  1046. //
  1047. // Switch back to the original context if necessary
  1048. //
  1049. /* if (hToken && !ImpersonatePrinterClient(hToken))
  1050. Error(("ImpersonatePrinterClient failed: %d\n", GetLastError()));*/
  1051. return (status < FAXERR_SPECIAL);
  1052. }
  1053. BOOL
  1054. FaxMonWritePort(
  1055. HANDLE hPort,
  1056. LPBYTE pBuffer,
  1057. DWORD cbBuf,
  1058. LPDWORD pcbWritten
  1059. )
  1060. /*++
  1061. Routine Description:
  1062. Writes data to a port
  1063. Arguments:
  1064. hPort - Identifies the port
  1065. pBuffer - Points to a buffer that contains data to be written to the port
  1066. cbBuf - Specifies the size in bytes of the buffer
  1067. pcbWritten - Returns the count of bytes successfully written to the port
  1068. Return Value:
  1069. TRUE if successful, FALSE if there is an error
  1070. --*/
  1071. {
  1072. PFAXPORT pFaxPort = (PFAXPORT) hPort;
  1073. //
  1074. // Make sure we have a valid handle
  1075. //
  1076. if (! ValidFaxPort(pFaxPort) || ! pFaxPort->hFaxSvc) {
  1077. Error(("WritePort is given an invalid fax port handle\n"));
  1078. SetLastError(ERROR_INVALID_HANDLE);
  1079. return FALSE;
  1080. }
  1081. Assert(pFaxPort->hFile != INVALID_HANDLE_VALUE);
  1082. return WriteFile(pFaxPort->hFile, pBuffer, cbBuf, pcbWritten, NULL);
  1083. }
  1084. BOOL
  1085. FaxMonReadPort(
  1086. HANDLE hPort,
  1087. LPBYTE pBuffer,
  1088. DWORD cbBuf,
  1089. LPDWORD pcbRead
  1090. )
  1091. /*++
  1092. Routine Description:
  1093. Reads data from the port
  1094. Arguments:
  1095. hPort - Identifies the port
  1096. pBuffer - Points to a buffer where data read from the printer can be written
  1097. cbBuf - Specifies the size in bytes of the buffer pointed to by pBuffer
  1098. pcbRead - Returns the number of bytes successfully read from the port
  1099. Return Value:
  1100. TRUE if successful, FALSE if there is an error
  1101. --*/
  1102. {
  1103. Trace("ReadPort");
  1104. SetLastError(ERROR_NOT_SUPPORTED);
  1105. return FALSE;
  1106. }
  1107. BOOL
  1108. FaxMonEnumPorts(
  1109. LPTSTR pServerName,
  1110. DWORD Level,
  1111. LPBYTE pPorts,
  1112. DWORD cbBuf,
  1113. LPDWORD pcbNeeded,
  1114. LPDWORD pReturned
  1115. )
  1116. /*++
  1117. Routine Description:
  1118. Enumerates the ports available on the specified server
  1119. Arguments:
  1120. pServerName - Specifies the name of the server whose ports are to be enumerated
  1121. dwLevel - Specifies the version of the structure to which pPorts points
  1122. pPorts - Points to an array of PORT_INFO_1 structures where data describing
  1123. the available ports will be writteno
  1124. cbBuf - Specifies the size in bytes of the buffer to which pPorts points
  1125. pcbNeeded - Returns the required buffer size identified by pPorts
  1126. pReturned - Returns the number of PORT_INFO_1 structures returned
  1127. Return Value:
  1128. TRUE if successful, FALSE if there is an error
  1129. --*/
  1130. #define MAX_DESC_LEN 64
  1131. {
  1132. TCHAR portDescStr[MAX_DESC_LEN];
  1133. INT descStrSize, faxmonNameSize;
  1134. DWORD cbNeeded;
  1135. BOOL status = TRUE;
  1136. PORT_INFO_1 *pPortInfo1 = (PORT_INFO_1 *) pPorts;
  1137. PORT_INFO_2 *pPortInfo2 = (PORT_INFO_2 *) pPorts;
  1138. INT strSize;
  1139. Trace("EnumPorts");
  1140. if (pcbNeeded == NULL || pReturned == NULL || (pPorts == NULL && cbBuf != 0)) {
  1141. Error(("Invalid input parameters\n"));
  1142. SetLastError(ERROR_INVALID_PARAMETER);
  1143. return FALSE;
  1144. }
  1145. //
  1146. // Load the fax port description string
  1147. //
  1148. if (! LoadString(ghInstance, IDS_FAX_PORT_DESC, portDescStr, MAX_DESC_LEN))
  1149. portDescStr[0] = NUL;
  1150. descStrSize = SizeOfString(portDescStr);
  1151. faxmonNameSize = SizeOfString(faxMonitorName);
  1152. switch (Level) {
  1153. case 1:
  1154. cbNeeded = sizeof(PORT_INFO_1) + SizeOfString(FAX_PORT_NAME);
  1155. break;
  1156. case 2:
  1157. cbNeeded = sizeof(PORT_INFO_2) + descStrSize + faxmonNameSize + SizeOfString(FAX_PORT_NAME);
  1158. break;
  1159. }
  1160. *pReturned = 1;
  1161. *pcbNeeded = cbNeeded;
  1162. if (cbNeeded > cbBuf) {
  1163. //
  1164. // Caller didn't provide a big enough buffer
  1165. //
  1166. SetLastError(ERROR_INSUFFICIENT_BUFFER);
  1167. status = FALSE;
  1168. } else {
  1169. //
  1170. // Strings must be packed at the end of the caller provided buffer.
  1171. // Otherwise, spooler will mess up.
  1172. //
  1173. pPorts += cbBuf;
  1174. //
  1175. // Copy the requested port information to the caller provided buffer
  1176. //
  1177. strSize = SizeOfString(FAX_PORT_NAME);
  1178. pPorts -= strSize;
  1179. CopyMemory(pPorts, FAX_PORT_NAME, strSize);
  1180. switch (Level) {
  1181. case 1:
  1182. pPortInfo1->pName = (LPTSTR) pPorts;
  1183. Verbose(("Port info 1: %ws\n", pPortInfo1->pName));
  1184. pPortInfo1++;
  1185. break;
  1186. case 2:
  1187. pPortInfo2->pPortName = (LPTSTR) pPorts;
  1188. //
  1189. // Copy the fax monitor name string
  1190. //
  1191. pPorts -= faxmonNameSize;
  1192. pPortInfo2->pMonitorName = (LPTSTR) pPorts;
  1193. CopyMemory(pPorts, faxMonitorName, faxmonNameSize);
  1194. //
  1195. // Copy the fax port description string
  1196. //
  1197. pPorts -= descStrSize;
  1198. pPortInfo2->pDescription = (LPTSTR) pPorts;
  1199. CopyMemory(pPorts, portDescStr, descStrSize);
  1200. pPortInfo2->fPortType = PORT_TYPE_WRITE;
  1201. pPortInfo2->Reserved = 0;
  1202. Verbose(("Port info 2: %ws, %ws, %ws\n",
  1203. pPortInfo2->pPortName,
  1204. pPortInfo2->pMonitorName,
  1205. pPortInfo2->pDescription));
  1206. pPortInfo2++;
  1207. break;
  1208. }
  1209. }
  1210. return status;
  1211. }
  1212. BOOL
  1213. DisplayErrorNotImplemented(
  1214. HWND hwnd,
  1215. INT titleId
  1216. )
  1217. /*++
  1218. Routine Description:
  1219. Display an error dialog to tell the user that he cannot manage
  1220. fax devices in the Printers folder.
  1221. Arguments:
  1222. hwnd - Specifies the parent window for the message box
  1223. titleId - Message box title string resource ID
  1224. Return Value:
  1225. FALSE
  1226. --*/
  1227. {
  1228. TCHAR title[128];
  1229. TCHAR message[256];
  1230. LoadString(ghInstance, titleId, title, 128);
  1231. LoadString(ghInstance, IDS_CONFIG_ERROR, message, 256);
  1232. MessageBox(hwnd, message, title, MB_OK|MB_ICONERROR);
  1233. SetLastError(ERROR_SUCCESS);
  1234. return FALSE;
  1235. }
  1236. BOOL
  1237. FaxMonAddPort(
  1238. LPTSTR pServerName,
  1239. HWND hwnd,
  1240. LPTSTR pMonitorName
  1241. )
  1242. /*++
  1243. Routine Description:
  1244. Adds the name of a port to the list of supported ports
  1245. Arguments:
  1246. pServerName - Specifies the name of the server to which the port is to be added
  1247. hwnd - Identifies the parent window of the AddPort dialog box
  1248. pMonitorName - Specifies the monitor associated with the port
  1249. Return Value:
  1250. TRUE if successful, FALSE if there is an error
  1251. --*/
  1252. {
  1253. Trace("AddPort");
  1254. return DisplayErrorNotImplemented(hwnd, IDS_ADD_PORT);
  1255. }
  1256. BOOL
  1257. FaxMonAddPortEx(
  1258. LPTSTR pServerName,
  1259. DWORD level,
  1260. LPBYTE pBuffer,
  1261. LPTSTR pMonitorName
  1262. )
  1263. /*++
  1264. Routine Description:
  1265. Adds the name of a port to the list of supported ports
  1266. Arguments:
  1267. pServerName - Specifies the name of the server to which the port is to be added
  1268. hwnd - Identifies the parent window of the AddPort dialog box
  1269. pMonitorName - Specifies the monitor associated with the port
  1270. Return Value:
  1271. TRUE if successful, FALSE if there is an error
  1272. --*/
  1273. {
  1274. Trace("AddPortEx");
  1275. SetLastError(ERROR_NOT_SUPPORTED);
  1276. return FALSE;
  1277. }
  1278. BOOL
  1279. FaxMonDeletePort(
  1280. LPTSTR pServerName,
  1281. HWND hwnd,
  1282. LPTSTR pPortName
  1283. )
  1284. /*++
  1285. Routine Description:
  1286. Delete the specified port from the list of supported ports
  1287. Arguments:
  1288. pServerName - Specifies the name of the server from which the port is to be removed
  1289. hwnd - Identifies the parent window of the port-deletion dialog box
  1290. pPortName - Specifies the name of the port to be deleted
  1291. Return Value:
  1292. TRUE if successful, FALSE if there is an error
  1293. --*/
  1294. {
  1295. Trace("DeletePort");
  1296. return DisplayErrorNotImplemented(hwnd, IDS_CONFIGURE_PORT);
  1297. }
  1298. BOOL
  1299. FaxMonConfigurePort(
  1300. LPWSTR pServerName,
  1301. HWND hwnd,
  1302. LPWSTR pPortName
  1303. )
  1304. /*++
  1305. Routine Description:
  1306. Display a dialog box to allow user to configure the specified port
  1307. Arguments:
  1308. pServerName - Specifies the name of the server on which the given port exists
  1309. hwnd - Identifies the parent window of the port-configuration dialog
  1310. pPortName - Specifies the name of the port to be configured
  1311. Return Value:
  1312. TRUE if successful, FALSE if there is an error
  1313. --*/
  1314. {
  1315. Trace("ConfigurePort");
  1316. return DisplayErrorNotImplemented(hwnd, IDS_CONFIGURE_PORT);
  1317. }
  1318. LPTSTR
  1319. DuplicateString(
  1320. LPCTSTR pSrcStr
  1321. )
  1322. /*++
  1323. Routine Description:
  1324. Make a duplicate of the given character string
  1325. Arguments:
  1326. pSrcStr - Specifies the string to be duplicated
  1327. Return Value:
  1328. Pointer to the duplicated string, NULL if there is an error
  1329. --*/
  1330. {
  1331. LPTSTR pDestStr;
  1332. INT strSize;
  1333. if (pSrcStr != NULL) {
  1334. strSize = SizeOfString(pSrcStr);
  1335. if (pDestStr = MemAlloc(strSize))
  1336. CopyMemory(pDestStr, pSrcStr, strSize);
  1337. else
  1338. Error(("Memory allocation failed\n"));
  1339. } else
  1340. pDestStr = NULL;
  1341. return pDestStr;
  1342. }
  1343. PVOID
  1344. MyGetJob(
  1345. HANDLE hPrinter,
  1346. DWORD level,
  1347. DWORD jobId
  1348. )
  1349. /*++
  1350. Routine Description:
  1351. Wrapper function for spooler API GetJob
  1352. Arguments:
  1353. hPrinter - Handle to the printer object
  1354. level - Level of JOB_INFO structure interested
  1355. jobId - Specifies the job ID
  1356. Return Value:
  1357. Pointer to a JOB_INFO structure, NULL if there is an error
  1358. --*/
  1359. {
  1360. PBYTE pJobInfo = NULL;
  1361. DWORD cbNeeded;
  1362. if (!GetJob(hPrinter, jobId, level, NULL, 0, &cbNeeded) &&
  1363. GetLastError() == ERROR_INSUFFICIENT_BUFFER &&
  1364. (pJobInfo = MemAlloc(cbNeeded)) &&
  1365. GetJob(hPrinter, jobId, level, pJobInfo, cbNeeded, &cbNeeded))
  1366. {
  1367. return pJobInfo;
  1368. }
  1369. Error(("GetJob failed: %d\n", GetLastError()));
  1370. MemFree(pJobInfo);
  1371. return NULL;
  1372. }
  1373. BOOL
  1374. SetJobStatus(
  1375. HANDLE hPrinter,
  1376. DWORD jobId,
  1377. INT statusStrId
  1378. )
  1379. /*++
  1380. Routine Description:
  1381. Update the status information of a print job
  1382. Arguments:
  1383. hPrinter - Specifies the printer on which the job is printed
  1384. jobId - Specifies the job identifier
  1385. statusStrID - Specifies the status string resource ID
  1386. Return Value:
  1387. TRUE if successful, FALSE if there is an error
  1388. --*/
  1389. #define MAX_MESSAGE_LEN 256
  1390. {
  1391. JOB_INFO_1 *pJobInfo1;
  1392. BOOL result = FALSE;
  1393. TCHAR message[MAX_MESSAGE_LEN];
  1394. //
  1395. // Get the current job information
  1396. //
  1397. if (pJobInfo1 = MyGetJob(hPrinter, 1, jobId)) {
  1398. //
  1399. // Update the status field
  1400. //
  1401. if (LoadString(ghInstance, statusStrId, message, MAX_MESSAGE_LEN))
  1402. pJobInfo1->pStatus = message;
  1403. else {
  1404. pJobInfo1->pStatus = NULL;
  1405. pJobInfo1->Status = JOB_STATUS_ERROR;
  1406. }
  1407. pJobInfo1->Position = JOB_POSITION_UNSPECIFIED;
  1408. if (! (result = SetJob(hPrinter, jobId, 1, (PBYTE) pJobInfo1, 0)))
  1409. Error(("SetJob failed: %d\n", GetLastError()));
  1410. MemFree(pJobInfo1);
  1411. }
  1412. return result;
  1413. }
  1414. DWORD
  1415. GetRegistryDWord(
  1416. HKEY hRegKey,
  1417. LPTSTR pValueName,
  1418. DWORD defaultValue
  1419. )
  1420. /*++
  1421. Routine Description:
  1422. Retrieve a DWORD value from the registry
  1423. Arguments:
  1424. hRegKey - Handle to the user info registry key
  1425. pValueName - Specifies the name of the string value in registry
  1426. defaultValue - Specifies the default value to be used in case of an error
  1427. Return Value:
  1428. Requested DWORD value from the user info registry key
  1429. --*/
  1430. {
  1431. DWORD size, type, value;
  1432. //
  1433. // Retrieve the country code value from the registry.
  1434. // Use the default value if none exists.
  1435. //
  1436. size = sizeof(value);
  1437. if (RegQueryValueEx(hRegKey, pValueName, NULL, &type, (PBYTE) &value, &size) != ERROR_SUCCESS ||
  1438. type != REG_DWORD)
  1439. {
  1440. value = defaultValue;
  1441. }
  1442. return value;
  1443. }
  1444. #if DBG
  1445. //
  1446. // Variable for controlling the amount of debug messages generated
  1447. //
  1448. INT _debugLevel = 1;
  1449. LPCSTR
  1450. StripDirPrefixA(
  1451. LPCSTR pFilename
  1452. )
  1453. /*++
  1454. Routine Description:
  1455. Strip the directory prefix off a filename
  1456. Arguments:
  1457. pFilename - Pointer to filename string
  1458. Return Value:
  1459. Pointer to the last component of a filename (without directory prefix)
  1460. --*/
  1461. {
  1462. LPCSTR pstr;
  1463. if (pstr = strrchr(pFilename, PATH_SEPARATOR))
  1464. return pstr + 1;
  1465. return pFilename;
  1466. }
  1467. #endif