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.

810 lines
19 KiB

  1. /*****************************************************************/
  2. /** Copyright(c) 1989 Microsoft Corporation. **/
  3. /*****************************************************************/
  4. //***
  5. //
  6. // Filename: job.c
  7. //
  8. // Description: This module contains the entry points for the AppleTalk
  9. // monitor that manipulate jobs.
  10. //
  11. // The following are the functions contained in this module.
  12. // All these functions are exported.
  13. //
  14. // StartDocPort
  15. // ReadPort
  16. // WritePort
  17. // EndDocPort
  18. // History:
  19. //
  20. // Aug 26,1992 frankb Initial version
  21. // June 11,1993. NarenG Bug fixes/clean up
  22. //
  23. #include <windows.h>
  24. #include <winspool.h>
  25. #include <winsplp.h>
  26. #include <winsock.h>
  27. #include <atalkwsh.h>
  28. #include <stdlib.h>
  29. #include <stdio.h>
  30. #include <string.h>
  31. #include <lmcons.h>
  32. #include <prtdefs.h>
  33. #include "atalkmon.h"
  34. #include "atmonmsg.h"
  35. #include <bltrc.h>
  36. #include "dialogs.h"
  37. //**
  38. //
  39. // Call: StartDocPort
  40. //
  41. // Returns: TRUE - Success
  42. // FALSE - Failure
  43. //
  44. // Description:
  45. // This routine is called by the print manager to
  46. // mark the beginning of a job to be sent to the printer on
  47. // this port. Any performance monitoring counts are cleared,
  48. // a check is made to insure that the printer is still open,
  49. //
  50. // open issues:
  51. //
  52. // In order to allow for the stack to be shutdown when printing is not
  53. // happening, the first access to the AppleTalk stack happens in this
  54. // call. A socket is created and bound to a dynamic address, and an
  55. // attempt to connect to the NBP name of the port is made here. If
  56. // the connection succeeds, this routine returns TRUE. If it fails, the
  57. // socket is cleaned up and the routine returns FALSE. It is assumed that
  58. // Winsockets will set the appropriate Win32 failure codes.
  59. //
  60. // Do we want to do any performance stuff? If so, what?
  61. //
  62. BOOL
  63. StartDocPort(
  64. IN HANDLE hPort,
  65. IN LPWSTR pPrinterName,
  66. IN DWORD JobId,
  67. IN DWORD Level,
  68. IN LPBYTE pDocInfo
  69. )
  70. {
  71. PATALKPORT pWalker;
  72. PATALKPORT pPort;
  73. DWORD dwRetCode;
  74. DBGPRINT(("Entering StartDocPort\n")) ;
  75. pPort = (PATALKPORT)hPort;
  76. if (pPort == NULL)
  77. {
  78. SetLastError(ERROR_INVALID_HANDLE);
  79. return(FALSE);
  80. }
  81. //
  82. // Make sure the job is valid and not marked for deletion
  83. //
  84. dwRetCode = ERROR_UNKNOWN_PORT;
  85. WaitForSingleObject(hmutexPortList, INFINITE);
  86. for (pWalker = pPortList; pWalker != NULL; pWalker = pWalker->pNext)
  87. {
  88. if (pWalker == pPort)
  89. {
  90. if (pWalker->fPortFlags & SFM_PORT_IN_USE)
  91. dwRetCode = ERROR_DEVICE_IN_USE;
  92. else
  93. {
  94. dwRetCode = NO_ERROR;
  95. pWalker->fPortFlags |= SFM_PORT_IN_USE;
  96. }
  97. break;
  98. }
  99. }
  100. ReleaseMutex(hmutexPortList);
  101. if (dwRetCode != NO_ERROR)
  102. {
  103. SetLastError(dwRetCode);
  104. return(FALSE);
  105. }
  106. do
  107. {
  108. //
  109. // get a handle to the printer. Used to delete job and
  110. // update job status
  111. //
  112. if (!OpenPrinter(pPrinterName, &(pWalker->hPrinter), NULL))
  113. {
  114. dwRetCode = GetLastError();
  115. break;
  116. }
  117. pWalker->dwJobId = JobId;
  118. pWalker->fJobFlags |= (SFM_JOB_FIRST_WRITE | SFM_JOB_OPEN_PENDING);
  119. //
  120. // open and bind status socket
  121. //
  122. dwRetCode = OpenAndBindAppleTalkSocket(&(pWalker->sockStatus));
  123. if (dwRetCode != NO_ERROR)
  124. {
  125. ReportEvent(
  126. hEventLog,
  127. EVENTLOG_WARNING_TYPE,
  128. EVENT_CATEGORY_USAGE,
  129. EVENT_ATALKMON_STACK_NOT_STARTED,
  130. NULL,
  131. 0,
  132. 0,
  133. NULL,
  134. NULL) ;
  135. break;
  136. }
  137. //
  138. // get a socket for I/O
  139. //
  140. dwRetCode = OpenAndBindAppleTalkSocket(&(pWalker->sockIo));
  141. if (dwRetCode != NO_ERROR)
  142. {
  143. ReportEvent(
  144. hEventLog,
  145. EVENTLOG_WARNING_TYPE,
  146. EVENT_CATEGORY_USAGE,
  147. EVENT_ATALKMON_STACK_NOT_STARTED,
  148. NULL,
  149. 0,
  150. 0,
  151. NULL,
  152. NULL);
  153. break;
  154. }
  155. } while(FALSE);
  156. if (dwRetCode != NO_ERROR)
  157. {
  158. if (pWalker->hPrinter != INVALID_HANDLE_VALUE)
  159. ClosePrinter(pWalker->hPrinter);
  160. if (pWalker->sockStatus != INVALID_SOCKET)
  161. closesocket(pWalker->sockStatus);
  162. if (pWalker->sockIo != INVALID_SOCKET)
  163. closesocket(pWalker->sockIo);
  164. pWalker->hPrinter = INVALID_HANDLE_VALUE;
  165. pWalker->dwJobId = 0;
  166. pWalker->fJobFlags = 0;
  167. WaitForSingleObject(hmutexPortList, INFINITE);
  168. pWalker->fPortFlags &= ~SFM_PORT_IN_USE;
  169. ReleaseMutex(hmutexPortList);
  170. SetLastError(dwRetCode);
  171. return(FALSE);
  172. }
  173. return(TRUE);
  174. }
  175. //**
  176. //
  177. // Call: ReadPort
  178. //
  179. // Returns: TRUE - Success
  180. // FALSE - Failure
  181. //
  182. // Description:
  183. // Synchronously reads data from the printer.
  184. //
  185. // open issues:
  186. // the DLC implementation does not implement reads.
  187. // The local implementation implements reads with generic ReadFile
  188. // semantics. It's not clear from the winhelp file if ReadPort
  189. // should return an error if there is no data to read from
  190. // the printer. Also, since PAP is read driven, there will be no
  191. // data waiting until a read is posted. Should we pre-post a
  192. // read on StartDocPort?
  193. //
  194. BOOL
  195. ReadPort(
  196. IN HANDLE hPort,
  197. IN LPBYTE pBuffer,
  198. IN DWORD cbBuffer,
  199. IN LPDWORD pcbRead
  200. ){
  201. DBGPRINT(("Entering ReadPort\n")) ;
  202. //
  203. // if data not available, wait up to a few seconds for a read to complete
  204. //
  205. //
  206. // copy requested amount of data to caller's buffer
  207. //
  208. //
  209. // if all data copied, post another read
  210. //
  211. return(TRUE);
  212. }
  213. //**
  214. //
  215. // Call: WritePort
  216. //
  217. // Returns: TRUE - Success
  218. // FALSE - Failure
  219. //
  220. // Description:
  221. // Synchronously writes data to the printer.
  222. //
  223. BOOL
  224. WritePort(
  225. IN HANDLE hPort,
  226. IN LPBYTE pBuffer,
  227. IN DWORD cbBuffer,
  228. IN LPDWORD pcbWritten
  229. )
  230. {
  231. LPBYTE pchTemp;
  232. PATALKPORT pPort;
  233. DWORD dwIndex;
  234. DWORD dwRetCode;
  235. INT wsErr;
  236. fd_set writefds;
  237. fd_set readfds;
  238. struct timeval timeout;
  239. INT Flags = 0;
  240. LPBYTE pBufToSend;
  241. DWORD cbTotalBytesToSend;
  242. BOOLEAN fJobCameFromMac;
  243. BOOLEAN fPostScriptJob;
  244. pPort = (PATALKPORT)hPort;
  245. // Set this to zero. We add incrementally later.
  246. *pcbWritten = 0;
  247. if (pPort == NULL)
  248. {
  249. SetLastError(ERROR_INVALID_HANDLE);
  250. return(FALSE);
  251. }
  252. pBufToSend = pBuffer;
  253. cbTotalBytesToSend = cbBuffer;
  254. //
  255. // Maximum number of bytes we can write in one send is 4K. This is the
  256. // limit in the AppleTalk (PAP) protocol.
  257. //
  258. if (cbTotalBytesToSend > 4096)
  259. {
  260. cbTotalBytesToSend = 4096;
  261. }
  262. // If we have not connected to the printer yet.
  263. if (pPort->fJobFlags & SFM_JOB_OPEN_PENDING)
  264. {
  265. // Make sure that the capture thread is done with this job.
  266. WaitForSingleObject(pPort->hmutexPort, INFINITE);
  267. ReleaseMutex(pPort->hmutexPort);
  268. // set status to connecting
  269. DBGPRINT(("no connection yet, retry connect\n")) ;
  270. dwRetCode = ConnectToPrinter(pPort, ATALKMON_DEFAULT_TIMEOUT);
  271. if (dwRetCode != NO_ERROR)
  272. {
  273. DBGPRINT(("Connect returns %d\n", dwRetCode)) ;
  274. //
  275. // Wait 15 seconds before trying to reconnect. Each
  276. // ConnectToPrinter does an expensive NBPLookup
  277. //
  278. Sleep(ATALKMON_DEFAULT_TIMEOUT*3);
  279. *pcbWritten = 0;
  280. return(TRUE);
  281. }
  282. else
  283. {
  284. pPort->fJobFlags &= ~SFM_JOB_OPEN_PENDING;
  285. WaitForSingleObject(hmutexPortList, INFINITE);
  286. pPort->fPortFlags |= SFM_PORT_POST_READ;
  287. ReleaseMutex(hmutexPortList);
  288. SetEvent(hevPrimeRead);
  289. SetPrinterStatus(pPort, wchPrinting);
  290. }
  291. }
  292. // if first write, determine filter control. We filter
  293. // CTRL-D from non-mac jobs, and leave them in from Macintosh
  294. // originated jobs
  295. if (pPort->fJobFlags & SFM_JOB_FIRST_WRITE)
  296. {
  297. DBGPRINT(("first write for this job. Do filter test\n")) ;
  298. fJobCameFromMac = IsJobFromMac(pPort);
  299. // Consume the FILTERCONTROL string
  300. //
  301. // the older spoolers will put this string in: go ahead and leave
  302. // this code in so if this job came from an older SFM spooler, we
  303. // strip that line!
  304. //
  305. if ((cbTotalBytesToSend >= SIZE_FC) &&
  306. (strncmp(pBufToSend, FILTERCONTROL, SIZE_FC) == 0))
  307. {
  308. *pcbWritten += SIZE_FC;
  309. pBufToSend += SIZE_FC;
  310. cbTotalBytesToSend -= SIZE_FC;
  311. fJobCameFromMac = TRUE;
  312. }
  313. else if ((cbTotalBytesToSend >= SIZE_FCOLD) &&
  314. strncmp(pBufToSend, FILTERCONTROL_OLD, SIZE_FCOLD) == 0)
  315. {
  316. *pcbWritten += SIZE_FCOLD;
  317. pBufToSend += SIZE_FCOLD;
  318. cbTotalBytesToSend -= SIZE_FCOLD;
  319. fJobCameFromMac = TRUE;
  320. }
  321. //
  322. // Need for hack: there are two reasons:
  323. // 1) control characters (most commonly ctrl-d, but ctrl-c, etc. too)
  324. // cause postscript printers to choke. we need to "filter" them out
  325. // 2) if we're printing to a dual-mode HP printer then it's
  326. // driver puts in a bunch of PJL commands that causes printer to go to
  327. // postscript mode etc. It works great if this goes over lpt or com port
  328. // but if it goes over appletalk (which is what we do) then the printer
  329. // expects *only* postscript and seeing the PJL commands, it chokes!
  330. // The output that goes out to the printer looks like this:
  331. //
  332. // <....separator page data....>
  333. //
  334. // $%-12345X@PJL JOB
  335. // @PJL SET RESOLUTION=600
  336. // @PJL ENTER LANGUAGE = POSTSCRIPT
  337. // %!PS-Adobe-3.0
  338. //
  339. // <.... Postscript data....>
  340. //
  341. // $%-12345X@PJL EOJ
  342. //
  343. // (The escape character is denoted by the '$' sign above.)
  344. // The first 3 lines and the last line are the ones that cause problem
  345. //
  346. // Since it's a pain in the neck to parse all of the data and try and
  347. // remove the unwanted characters, we just prepend a few postscript
  348. // commands to the data that tell the printer to ignore ctrl-d,
  349. // ctrl-c etc. characters, and to ignore any line(s) starting with @PJL.
  350. //
  351. //
  352. // Begin filtering hack
  353. //
  354. //
  355. // make sure the string doesn't already exist (it can if the job goes
  356. // monitor->spooler->monitor->printer instead of monitor->printer)
  357. //
  358. // Again, older SFM monitors would prepend this string: since we got a
  359. // chance here, strip that out!
  360. //
  361. if ((cbTotalBytesToSend >= SIZE_PS_HEADER) &&
  362. strncmp(pBufToSend, PS_HEADER, SIZE_PS_HEADER) == 0)
  363. {
  364. *pcbWritten += SIZE_PS_HEADER;
  365. pBufToSend += SIZE_PS_HEADER;
  366. cbTotalBytesToSend -= SIZE_PS_HEADER;
  367. }
  368. //
  369. // WfW starts its job with a CTRL_D. Replace it with a space
  370. //
  371. if (pBufToSend[0] == CTRL_D)
  372. {
  373. *pcbWritten += 1;
  374. pBufToSend += 1;
  375. cbTotalBytesToSend -= 1;
  376. }
  377. //
  378. // see if this job has a hdr that looks like a conventional postscript hdr
  379. //
  380. fPostScriptJob = TRUE;
  381. if (cbTotalBytesToSend > 2)
  382. {
  383. if (pBufToSend[0] == '%' && pBufToSend[1] == '!')
  384. {
  385. fPostScriptJob = TRUE;
  386. }
  387. else
  388. {
  389. fPostScriptJob = FALSE;
  390. }
  391. }
  392. //
  393. // Mac always sends a postscript job. Also, we peeked at the data to
  394. // see if we recognize a postscript hdr. If the job came from a non-Mac
  395. // client and doesn't look like a conventional postscript job, send a
  396. // control string telling the printer to ignore the PJL commands.
  397. //
  398. if (!fJobCameFromMac && !fPostScriptJob)
  399. {
  400. //
  401. // Now send the PS header
  402. //
  403. FD_ZERO(&writefds);
  404. FD_SET(pPort->sockIo, &writefds);
  405. //
  406. // can I send?
  407. //
  408. timeout.tv_sec = ATALKMON_DEFAULT_TIMEOUT_SEC;
  409. timeout.tv_usec = 0;
  410. wsErr = select(0, NULL, &writefds, NULL, &timeout);
  411. if (wsErr == 1)
  412. {
  413. // can send, send the data & set return count
  414. wsErr = send(pPort->sockIo,
  415. PS_HEADER,
  416. SIZE_PS_HEADER,
  417. MSG_PARTIAL);
  418. }
  419. }
  420. //
  421. // End filtering hack
  422. //
  423. pPort->fJobFlags &= ~SFM_JOB_FIRST_WRITE;
  424. }
  425. // many postscript jobs from pc's end with a ctrl-d which we don't want to send.
  426. // Since we are given only 1 byte and it is ctrl-d, we assume (FOR NOW) that it's the
  427. // last byte of the job. So lie to the spooler that we sent it.
  428. //
  429. if (cbTotalBytesToSend == 1)
  430. {
  431. if (pBufToSend[0] == CTRL_D)
  432. {
  433. *pcbWritten = 1;
  434. pPort->OnlyOneByteAsCtrlD++;
  435. return(TRUE);
  436. }
  437. else
  438. {
  439. cbTotalBytesToSend += 1; // we subtract 1 in the next line, so adjust here
  440. }
  441. }
  442. //
  443. // if this job is for dual-mode printer, there is that $%-12345X@PJL EOJ command
  444. // at the end. There is a ctrl-d just before that (which is really the end
  445. // of the actual job).
  446. //
  447. if (cbTotalBytesToSend > PJL_ENDING_COMMAND_LEN)
  448. {
  449. if (strncmp(&pBufToSend[cbTotalBytesToSend - PJL_ENDING_COMMAND_LEN],
  450. PJL_ENDING_COMMAND,
  451. PJL_ENDING_COMMAND_LEN) == 0)
  452. {
  453. if (pBufToSend[cbTotalBytesToSend-PJL_ENDING_COMMAND_LEN-1] == CTRL_D)
  454. {
  455. pBufToSend[cbTotalBytesToSend-PJL_ENDING_COMMAND_LEN-1] = CR;
  456. }
  457. }
  458. }
  459. //
  460. // send 1 less byte so eventually we'll catch the last byte (and see if it's ctrl-D)
  461. //
  462. cbTotalBytesToSend -= 1;
  463. //
  464. // Earlier we may have got just 1 byte which was ctrl-D but was not really the last byte!
  465. // This is a very rare case, but in theory possible. If that's what happened, send
  466. // that one ctrl-D byte now, and continue on with the rest of the job
  467. // (Actually being paranoid here and making provision for the spooler handing us a series
  468. // of ctrl-D bytes, 1 at a time!!)
  469. //
  470. if (pPort->OnlyOneByteAsCtrlD != 0)
  471. {
  472. BYTE TmpArray[20];
  473. DWORD i;
  474. i=0;
  475. while (i < pPort->OnlyOneByteAsCtrlD)
  476. {
  477. TmpArray[i++] = CTRL_D;
  478. }
  479. FD_ZERO(&writefds);
  480. FD_SET(pPort->sockIo, &writefds);
  481. timeout.tv_sec = ATALKMON_DEFAULT_TIMEOUT_SEC;
  482. timeout.tv_usec = 0;
  483. wsErr = select(0, NULL, &writefds, NULL, &timeout);
  484. if (wsErr == 1)
  485. {
  486. TmpArray[0] = CTRL_D;
  487. wsErr = send(pPort->sockIo,
  488. TmpArray,
  489. pPort->OnlyOneByteAsCtrlD,
  490. MSG_PARTIAL);
  491. }
  492. pPort->OnlyOneByteAsCtrlD = 0;
  493. }
  494. //
  495. // can I send?
  496. //
  497. FD_ZERO(&writefds);
  498. FD_SET(pPort->sockIo, &writefds);
  499. timeout.tv_sec = ATALKMON_DEFAULT_TIMEOUT_SEC;
  500. timeout.tv_usec = 0;
  501. wsErr = select(0, NULL, &writefds, NULL, &timeout);
  502. if (wsErr == 1)
  503. {
  504. // can send, send the data & set return count
  505. wsErr = send(pPort->sockIo,
  506. pBufToSend,
  507. cbTotalBytesToSend,
  508. MSG_PARTIAL);
  509. if (wsErr != SOCKET_ERROR)
  510. {
  511. *pcbWritten += cbTotalBytesToSend;
  512. if (pPort->fJobFlags & SFM_JOB_ERROR)
  513. {
  514. pPort->fJobFlags &= ~SFM_JOB_ERROR;
  515. SetPrinterStatus(pPort, wchPrinting);
  516. }
  517. }
  518. }
  519. //
  520. // can I read? - check for disconnect
  521. //
  522. FD_ZERO(&readfds);
  523. FD_SET(pPort->sockIo, &readfds);
  524. timeout.tv_sec = 0;
  525. timeout.tv_usec = 0;
  526. wsErr = select(0, &readfds, NULL, NULL, &timeout);
  527. if (wsErr == 1)
  528. {
  529. wsErr = WSARecvEx(pPort->sockIo,
  530. pPort->pReadBuffer,
  531. PAP_DEFAULT_BUFFER,
  532. &Flags);
  533. if (wsErr == SOCKET_ERROR)
  534. {
  535. dwRetCode = GetLastError();
  536. DBGPRINT(("recv returns %d\n", dwRetCode));
  537. if ((dwRetCode == WSAEDISCON) || (dwRetCode == WSAENOTCONN))
  538. {
  539. pPort->fJobFlags |= SFM_JOB_DISCONNECTED;
  540. //
  541. // Try to restart the job
  542. //
  543. SetJob(pPort->hPrinter,
  544. pPort->dwJobId,
  545. 0,
  546. NULL,
  547. JOB_CONTROL_RESTART);
  548. SetLastError(ERROR_DEV_NOT_EXIST);
  549. return(FALSE);
  550. }
  551. }
  552. else
  553. {
  554. if (wsErr < PAP_DEFAULT_BUFFER)
  555. pPort->pReadBuffer[wsErr] = '\0';
  556. else pPort->pReadBuffer[PAP_DEFAULT_BUFFER-1] = '\0';
  557. DBGPRINT(("recv returns %s\n", pPort->pReadBuffer));
  558. pPort->fJobFlags |= SFM_JOB_ERROR;
  559. ParseAndSetPrinterStatus(pPort);
  560. }
  561. WaitForSingleObject(hmutexPortList, INFINITE);
  562. pPort->fPortFlags |= SFM_PORT_POST_READ;
  563. ReleaseMutex(hmutexPortList);
  564. SetEvent(hevPrimeRead);
  565. }
  566. return(TRUE);
  567. }
  568. //**
  569. //
  570. // Call: EndDocPort
  571. //
  572. // Returns: TRUE - Success
  573. // FALSE - Failure
  574. //
  575. // Description:
  576. // This routine is called to mark the end of the
  577. // print job. The spool file for the job is deleted by
  578. // this routine.
  579. //
  580. // open issues:
  581. // Do we want to do performance stuff? If so, now's the time
  582. // to save off any performance counts.
  583. //
  584. BOOL
  585. EndDocPort(
  586. IN HANDLE hPort
  587. ){
  588. PATALKPORT pPort;
  589. fd_set writefds;
  590. fd_set readfds;
  591. struct timeval timeout;
  592. INT wsErr;
  593. INT Flags = 0;
  594. DBGPRINT(("Entering EndDocPort\n")) ;
  595. pPort = (PATALKPORT)hPort;
  596. if (pPort == NULL)
  597. {
  598. SetLastError(ERROR_INVALID_HANDLE);
  599. return(FALSE);
  600. }
  601. //
  602. // send the last write
  603. //
  604. FD_ZERO(&writefds);
  605. FD_SET(pPort->sockIo, &writefds);
  606. //
  607. // If the job was not able to connect to the printer.
  608. if ((pPort->fJobFlags & (SFM_JOB_OPEN_PENDING | SFM_JOB_DISCONNECTED)) == 0)
  609. {
  610. timeout.tv_sec = 90;
  611. timeout.tv_usec = 0;
  612. wsErr = select(0, NULL, &writefds, NULL, &timeout);
  613. if (wsErr == 1)
  614. {
  615. //
  616. // Send EOF
  617. //
  618. send(pPort->sockIo, NULL, 0, 0);
  619. }
  620. //
  621. // Our socket is non-blocking. If we close down the socket, we could potentially
  622. // abort the last page. A good thing to do is to wait for a reasonable amount of
  623. // time out for the printer to send EOF, or request for more data.
  624. //
  625. FD_ZERO(&writefds);
  626. FD_SET(pPort->sockIo, &writefds);
  627. FD_ZERO(&readfds);
  628. FD_SET(pPort->sockIo, &readfds);
  629. timeout.tv_sec = 30;
  630. timeout.tv_usec = 0;
  631. wsErr = select(0, &readfds, &writefds, NULL, &timeout);
  632. if (wsErr == 1 && FD_ISSET(pPort->sockIo, &readfds))
  633. {
  634. // read printer's EOF. We don't care about an error here
  635. wsErr = WSARecvEx(pPort->sockIo, pPort->pReadBuffer, PAP_DEFAULT_BUFFER, &Flags);
  636. }
  637. }
  638. //
  639. // delete the print job
  640. //
  641. if (pPort->hPrinter != INVALID_HANDLE_VALUE)
  642. {
  643. if (!SetJob(pPort->hPrinter,
  644. pPort->dwJobId,
  645. 0,
  646. NULL,
  647. JOB_CONTROL_SENT_TO_PRINTER))
  648. DBGPRINT(("fail to setjob for delete with %d\n", GetLastError())) ;
  649. ClosePrinter(pPort->hPrinter);
  650. pPort->hPrinter = INVALID_HANDLE_VALUE;
  651. }
  652. //
  653. // close the PAP connections
  654. //
  655. if (pPort->sockStatus != INVALID_SOCKET)
  656. {
  657. closesocket(pPort->sockStatus);
  658. pPort->sockStatus = INVALID_SOCKET;
  659. }
  660. if (pPort->sockIo != INVALID_SOCKET)
  661. {
  662. closesocket(pPort->sockIo);
  663. pPort->sockIo = INVALID_SOCKET;
  664. }
  665. pPort->dwJobId = 0;
  666. pPort->fJobFlags = 0;
  667. pPort->OnlyOneByteAsCtrlD = 0;
  668. WaitForSingleObject(hmutexPortList, INFINITE);
  669. pPort->fPortFlags &= ~SFM_PORT_IN_USE;
  670. ReleaseMutex(hmutexPortList);
  671. return(TRUE);
  672. }
  673.