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.

716 lines
22 KiB

  1. /*************************************************************************
  2. * Microsoft Windows NT *
  3. * *
  4. * Copyright(c) Microsoft Corp., 1994 *
  5. * *
  6. * Revision History: *
  7. * *
  8. * Jan. 24,94 Koti Created *
  9. * Jan. 29,96 JBallard Modified *
  10. * *
  11. * Description: *
  12. * *
  13. * This file contains functions for carrying out LPD printing *
  14. * *
  15. *************************************************************************/
  16. #include "lpd.h"
  17. extern FILE * pErrFile; // Debug output log file.
  18. BOOL GetSpoolFileName(
  19. HANDLE hPrinter,
  20. PSOCKCONN pscConn,
  21. PCHAR *ppwchSpoolPath
  22. );
  23. VOID CleanupConn( PSOCKCONN pscConn);
  24. /*****************************************************************************
  25. * *
  26. * ProcessJob(): *
  27. * This function receives the subcommand from the client to expect the *
  28. * control file, then accepts the control file, then the subcommand to *
  29. * expect the data file, then accepts the data and then hands it over to *
  30. * the spooler to print. *
  31. * If the very first subcommand was to abort the job, we just return. *
  32. * *
  33. * Returns: *
  34. * Nothing *
  35. * *
  36. * Parameters: *
  37. * pscConn (IN-OUT): pointer to SOCKCONN structure for this connection *
  38. * *
  39. * History: *
  40. * Jan.24 94 Koti Created *
  41. * *
  42. *****************************************************************************/
  43. VOID ProcessJob( PSOCKCONN pscConn )
  44. {
  45. // the main functionality of LPD implemented in this function!
  46. CHAR chSubCmdCode;
  47. DWORD cbTotalDataLen;
  48. DWORD cbBytesSpooled;
  49. DWORD cbBytesToRead;
  50. DWORD cbDataBufLen;
  51. DWORD cbBytesRemaining;
  52. DWORD dwErrcode;
  53. CHAR chAck;
  54. HANDLE hDFile;
  55. PDFILE_ENTRY pDFile;
  56. PCHAR lpFileName;
  57. PCHAR pchDataBuf;
  58. DWORD ClientCmd;
  59. // initialize the printer that the client wants to use
  60. #ifdef DBG
  61. if( !pscConn || !pscConn->pchPrinterName
  62. || strstr( pscConn->pchPrinterName, "debug" )
  63. ){
  64. print__sockconn( "ProcessJob: entered", pscConn );
  65. }
  66. #endif
  67. if ( InitializePrinter( pscConn ) != NO_ERROR )
  68. {
  69. PCHAR aszStrings[2];
  70. aszStrings[0] = pscConn->pchPrinterName;
  71. aszStrings[1] = pscConn->szIPAddr;
  72. LpdReportEvent( LPDLOG_NONEXISTENT_PRINTER, 2, aszStrings, 0 );
  73. pscConn->fLogGenericEvent = FALSE;
  74. return; // fatal error: exit
  75. }
  76. // thank the client for the command. If we couldn't send, quit
  77. if ( ReplyToClient( pscConn, LPD_ACK ) != NO_ERROR )
  78. {
  79. LPD_DEBUG( "ProcessJob(): couldn't ACK to \"receive job\"\n" );
  80. return;
  81. }
  82. // 2 subcommands expected: "receive control file" and "receive data file"
  83. // They can come in any order. (One of the two subcommands can also be
  84. // "abort this job" in which case we abort the job and return).
  85. for ( ; ; )
  86. {
  87. // don't need the previous one (in fact, pchCommand field is reused)
  88. if ( pscConn->pchCommand != NULL )
  89. {
  90. LocalFree( pscConn->pchCommand );
  91. pscConn->pchCommand = NULL;
  92. }
  93. // get the first subcommand from the client
  94. // ------------------------------ N = 02, 03, or 01
  95. // | N | Count | SP | Name | LF | Count => control file length
  96. // ------------------------------ Name => controlfile name
  97. ClientCmd = GetCmdFromClient( pscConn );
  98. switch ( ClientCmd )
  99. {
  100. case CONNECTION_CLOSED:
  101. // performance enhancement: close the socket here and then start
  102. // printing: printing could take several seconds,
  103. // so don't tie up client
  104. if ( pscConn->sSock != INVALID_SOCKET )
  105. {
  106. SureCloseSocket( pscConn->sSock );
  107. pscConn->sSock = INVALID_SOCKET;
  108. }
  109. // if we came this far, everything went as planned.
  110. // tell spooler that we are done spooling: go ahead and print!
  111. PrintData( pscConn );
  112. pscConn->wState = LPDS_ALL_WENT_WELL;
  113. return;
  114. case NO_ERROR:
  115. // Not yet done, back to outer loop.
  116. break;
  117. case SOCKET_ERROR:
  118. default:
  119. // If we didn't get a subcommand from client, it's bad news!
  120. // client died or something catastophic like that!
  121. LOGIT(("ProcessJob:GetCmdFromClient %d failed %d.\n",
  122. ClientCmd, GetLastError() ));
  123. return; // the thread exits without doing anything
  124. }
  125. chSubCmdCode = pscConn->pchCommand[0];
  126. switch (chSubCmdCode) {
  127. case LPDCS_RECV_CFILE: // N = 02 ("receive control file")
  128. // client is going to give us a control file: prepare for it
  129. pscConn->wState = LPDSS_RECVD_CFILENAME;
  130. // get the controlfile name, file size out of the command
  131. if ( ParseSubCommand( pscConn, &cbTotalDataLen, &lpFileName ) != NO_ERROR )
  132. {
  133. PCHAR aszStrings[2]={ pscConn->szIPAddr, NULL };
  134. LpdReportEvent( LPDLOG_BAD_FORMAT, 1, aszStrings, 0 );
  135. pscConn->fLogGenericEvent = FALSE;
  136. return; // fatal error: exit
  137. }
  138. // tell client we got the name of the controlfile ok
  139. if ( ReplyToClient( pscConn, LPD_ACK ) != NO_ERROR )
  140. {
  141. return; // fatal error: exit
  142. }
  143. // Get the control file (we already know how big it is)
  144. if ( GetControlFileFromClient( pscConn, cbTotalDataLen, lpFileName ) != NO_ERROR )
  145. {
  146. LPD_DEBUG( "GetControlFileFromClient() failed in ProcessJob()\n" );
  147. return;
  148. }
  149. pscConn->wState = LPDSS_RECVD_CFILE;
  150. // tell client we got the controlfile and things look good so far!
  151. if ( ReplyToClient( pscConn, LPD_ACK ) != NO_ERROR )
  152. {
  153. LOGIT(("ProcessJob:%d: ReplyToClient failed %d\n",
  154. __LINE__, GetLastError() ));
  155. return; // fatal error: exit
  156. }
  157. break;
  158. case LPDCS_RECV_DFILE: // N = 03 ("receive data file")
  159. pscConn->wState = LPDSS_RECVD_DFILENAME;
  160. // tell client we got the name of the datafile ok
  161. if ( ReplyToClient( pscConn, LPD_ACK ) != NO_ERROR )
  162. {
  163. LOGIT(("ProcessJob:%d: ReplyToClient failed %d\n",
  164. __LINE__, GetLastError() ));
  165. return; // fatal error: exit
  166. }
  167. // get the datafile name, data size out of the command
  168. if ( ParseSubCommand( pscConn, &cbTotalDataLen, &lpFileName ) != NO_ERROR )
  169. {
  170. PCHAR aszStrings[2]={ pscConn->szIPAddr, NULL };
  171. LpdReportEvent( LPDLOG_BAD_FORMAT, 1, aszStrings, 0 );
  172. pscConn->fLogGenericEvent = FALSE;
  173. LOGIT(("ProcessJob:%d: ParseSubCommand failed %d\n",
  174. __LINE__, GetLastError() ));
  175. return; // fatal error: exit
  176. }
  177. // at this point, we know exactly how much data is coming.
  178. // Allocate buffer to hold the data. If data is more than
  179. // LPD_BIGBUFSIZE, keep reading and spooling several times
  180. // over until data is done
  181. pscConn->wState = LPDSS_SPOOLING;
  182. pDFile = LocalAlloc( LMEM_FIXED, sizeof(DFILE_ENTRY) );
  183. if (pDFile == NULL) {
  184. LocalFree( lpFileName );
  185. LOGIT(("ProcessJob:%d: LocalAlloc failed %d\n",
  186. __LINE__, GetLastError() ));
  187. return; // Fatal Error
  188. }
  189. pDFile->cbDFileLen = cbTotalDataLen;
  190. pDFile->pchDFileName = lpFileName;
  191. if ( !GetSpoolFileName( pscConn->hPrinter, pscConn, &lpFileName ) )
  192. {
  193. LPD_DEBUG( "ERROR: GetSpoolFileName() failed in ProcessJob\n" );
  194. LocalFree( pDFile->pchDFileName );
  195. LocalFree( pDFile );
  196. return;
  197. }
  198. //
  199. // GetTempFileName has already created this file, so use OPEN_ALWAYS.
  200. // Also, use FILE_ATTRIBUTE_TEMPORARY so that it will be faster
  201. // FILE_FLAG_SEQUENTIAL_SCAN, ntbug 79854, MohsinA, 03-Jun-97.
  202. //
  203. pDFile->hDataFile = CreateFile( lpFileName,
  204. GENERIC_READ|GENERIC_WRITE,
  205. FILE_SHARE_READ,
  206. NULL,
  207. OPEN_ALWAYS,
  208. FILE_ATTRIBUTE_NORMAL
  209. |FILE_ATTRIBUTE_TEMPORARY
  210. |FILE_FLAG_DELETE_ON_CLOSE
  211. |FILE_FLAG_SEQUENTIAL_SCAN
  212. ,
  213. NULL );
  214. LocalFree( lpFileName );
  215. if ( pDFile->hDataFile == INVALID_HANDLE_VALUE )
  216. {
  217. LPD_DEBUG( "ERROR: CreatFile() failed in ProcessJob \n" );
  218. LocalFree( pDFile->pchDFileName );
  219. LocalFree( pDFile );
  220. return;
  221. }
  222. cbBytesToRead = (cbTotalDataLen > LPD_BIGBUFSIZE ) ?
  223. LPD_BIGBUFSIZE : cbTotalDataLen;
  224. pchDataBuf = LocalAlloc( LMEM_FIXED, cbBytesToRead );
  225. if ( pchDataBuf == NULL )
  226. {
  227. LOGIT(("ProcessJob:%d: LocalAlloc failed %d\n",
  228. __LINE__, GetLastError() ));
  229. CloseHandle(pDFile->hDataFile);
  230. pDFile->hDataFile = INVALID_HANDLE_VALUE;
  231. LocalFree( pDFile->pchDFileName );
  232. LocalFree( pDFile );
  233. return; // fatal error: exit
  234. }
  235. cbBytesSpooled = 0;
  236. cbBytesRemaining = cbTotalDataLen;
  237. // keep receiving until we have all the data client said it
  238. // would send
  239. while( cbBytesSpooled < cbTotalDataLen )
  240. {
  241. if ( ReadData( pscConn->sSock, pchDataBuf,
  242. cbBytesToRead ) != NO_ERROR )
  243. {
  244. LPD_DEBUG( "ProcessJob:ReadData failed, job aborted)\n" );
  245. LocalFree( pchDataBuf );
  246. CloseHandle(pDFile->hDataFile);
  247. pDFile->hDataFile = INVALID_HANDLE_VALUE;
  248. LocalFree( pDFile->pchDFileName );
  249. LocalFree( pDFile );
  250. return; // fatal error: exit
  251. }
  252. cbDataBufLen = cbBytesToRead;
  253. if ( SpoolData( pDFile->hDataFile, pchDataBuf, cbDataBufLen ) != NO_ERROR )
  254. {
  255. LPD_DEBUG( "SpoolData() failed in ProcessJob(): job aborted)\n" );
  256. LocalFree( pchDataBuf );
  257. CloseHandle(pDFile->hDataFile);
  258. pDFile->hDataFile = INVALID_HANDLE_VALUE;
  259. LocalFree( pDFile->pchDFileName );
  260. LocalFree( pDFile );
  261. return; // fatal error: exit
  262. }
  263. cbBytesSpooled += cbBytesToRead;
  264. cbBytesRemaining -= cbBytesToRead;
  265. cbBytesToRead = (cbBytesRemaining > LPD_BIGBUFSIZE ) ?
  266. LPD_BIGBUFSIZE : cbBytesRemaining;
  267. }
  268. LocalFree( pchDataBuf );
  269. InsertTailList( &pscConn->DFile_List, &pDFile->Link );
  270. // LPR client sends one byte (of 0 bits) after sending data
  271. dwErrcode = ReadData( pscConn->sSock, &chAck, 1 );
  272. if ( ( dwErrcode != NO_ERROR ) || (chAck != LPD_ACK ) )
  273. {
  274. return;
  275. }
  276. // tell client we got the data and things look good so far!
  277. if ( ReplyToClient( pscConn, LPD_ACK ) != NO_ERROR )
  278. {
  279. LOGIT(("ProcessJob:%d: ReplyToClient failed %d\n",
  280. __LINE__, GetLastError() ));
  281. return; // fatal error: exit
  282. }
  283. break;
  284. case LPDCS_ABORT_JOB: // N = 01 ("abort this job")
  285. // client asked us to abort the job: tell him "ok" and quit!
  286. ReplyToClient( pscConn, LPD_ACK );
  287. pscConn->wState = LPDS_ALL_WENT_WELL; // we did what client wanted
  288. return;
  289. // unknown subcommand: log the event and quit
  290. default:
  291. {
  292. PCHAR aszStrings[2]={ pscConn->szIPAddr, NULL };
  293. LpdReportEvent( LPDLOG_MISBEHAVED_CLIENT, 1, aszStrings, 0 );
  294. pscConn->fLogGenericEvent = FALSE;
  295. LPD_DEBUG( "ProcessJob(): invalid subcommand, request rejected\n" );
  296. return;
  297. }
  298. }
  299. } // done processing both subcommands
  300. } // end ProcessJob()
  301. /*****************************************************************************
  302. * *
  303. * GetControlFileFromClient(): *
  304. * This function receives the control file from the client. In the *
  305. * previsous subcommand, the client told us how many bytes there are in *
  306. * the control file. *
  307. * Also,after reading all the bytes, we read the 1 byte "ack" from client *
  308. * *
  309. * Returns: *
  310. * NO_ERROR if everything went well *
  311. * ErrorCode if something went wrong somewhere *
  312. * *
  313. * Parameters: *
  314. * pscConn (IN-OUT): pointer to SOCKCONN structure for this connection *
  315. * *
  316. * History: *
  317. * Jan.24, 94 Koti Created *
  318. * *
  319. *****************************************************************************/
  320. DWORD
  321. GetControlFileFromClient( PSOCKCONN pscConn, DWORD FileSize, PCHAR FileName )
  322. {
  323. PCHAR pchAllocBuf;
  324. DWORD cbBytesToRead;
  325. PCFILE_ENTRY pCFile;
  326. if (FileSize > LPD_MAX_CONTROL_FILE_LEN)
  327. {
  328. return( (DWORD)LPDERR_NOBUFS );
  329. }
  330. pCFile = LocalAlloc( LMEM_FIXED, sizeof(CFILE_ENTRY) );
  331. if (pCFile == NULL) {
  332. return( (DWORD)LPDERR_NOBUFS );
  333. }
  334. pCFile->cbCFileLen = FileSize;
  335. pCFile->pchCFileName = FileName;
  336. // we know how big the control file is going to be: alloc space for it
  337. // Client sends one byte after sending the control file: read it along
  338. // with the rest of the data
  339. cbBytesToRead = FileSize + 1;
  340. pchAllocBuf = LocalAlloc( LMEM_FIXED, cbBytesToRead );
  341. if (pchAllocBuf == NULL)
  342. {
  343. LocalFree( pCFile );
  344. return( (DWORD)LPDERR_NOBUFS );
  345. }
  346. // now read the data (and the trailing byte) into this allocated buffer
  347. if ( ReadData( pscConn->sSock, pchAllocBuf, cbBytesToRead ) != NO_ERROR )
  348. {
  349. LocalFree( pCFile );
  350. LocalFree( pchAllocBuf );
  351. return( LPDERR_NORESPONSE );
  352. }
  353. // if the trailing byte is not zero, treat it as job aborted (though
  354. // we don't expect this to happen really)
  355. if ( pchAllocBuf[cbBytesToRead-1] != 0 )
  356. {
  357. LocalFree( pchAllocBuf );
  358. LocalFree( pCFile );
  359. LPD_DEBUG( "GetControlFileFromClient: got data followed by a NAK!\n");
  360. return( LPDERR_JOBABORTED );
  361. }
  362. pCFile->pchCFile = pchAllocBuf;
  363. InsertTailList( &pscConn->CFile_List, &pCFile->Link );
  364. return( NO_ERROR );
  365. } // end GetControlFileFromClient()
  366. /*****************************************************************************
  367. * *
  368. * GetSpoolFileName(): *
  369. * This function figures out where to put the spool file. *
  370. * *
  371. * Returns: *
  372. * TRUE if spool location found. *
  373. * FALSE if no spool location available. *
  374. * *
  375. * Parameters: *
  376. * hPrinter (IN): Handle to printer for which we are spooling *
  377. * pscConn (IN-OUT): pointer to SOCKCONN structure for this connection *
  378. * ppchSpoolPath (IN-OUT): Address of pointer which will receive the *
  379. * spool path. *
  380. * *
  381. * History: *
  382. * Nov.21, 94 JBallard *
  383. * *
  384. *****************************************************************************/
  385. BOOL
  386. GetSpoolFileName
  387. (
  388. HANDLE hPrinter,
  389. PSOCKCONN pscConn,
  390. PCHAR *ppchSpoolPath
  391. )
  392. /*++
  393. Routine Description:
  394. This function comes up with a name for a spool file that we should be
  395. able to write to.
  396. Note: The file name returned has already been created.
  397. Arguments:
  398. hPrinter - handle to the printer that we want a spool file for.
  399. ppchSpoolFileName: pointer that will receive an allocated buffer
  400. containing the file name to spool to. CALLER
  401. MUST FREE. Use LocalFree().
  402. Return Value:
  403. TRUE if everything goes as expected.
  404. FALSE if anything goes wrong.
  405. --*/
  406. {
  407. PBYTE pBuffer = NULL;
  408. DWORD dwAllocSize;
  409. DWORD dwNeeded;
  410. PCHAR pchSpoolPath = NULL;
  411. DWORD dwRetval;
  412. pchSpoolPath = LocalAlloc( LMEM_FIXED, 2 * MAX_PATH + 1 );
  413. if ( pchSpoolPath == NULL )
  414. {
  415. goto Failure;
  416. }
  417. //
  418. // In order to find out where the spooler's directory is, we add
  419. // call GetPrinterData with DefaultSpoolDirectory.
  420. //
  421. dwAllocSize = WCS_LEN( MAX_PATH + 1 );
  422. for (;;)
  423. {
  424. pBuffer = LocalAlloc( LMEM_FIXED, dwAllocSize );
  425. if ( pBuffer == NULL )
  426. {
  427. goto Failure;
  428. }
  429. if ( GetPrinterData( hPrinter,
  430. SPLREG_DEFAULT_SPOOL_DIRECTORY,
  431. NULL,
  432. pBuffer,
  433. dwAllocSize,
  434. &dwNeeded ) == ERROR_SUCCESS )
  435. {
  436. break;
  437. }
  438. if ( ( dwNeeded < dwAllocSize ) ||( GetLastError() != ERROR_MORE_DATA ))
  439. {
  440. goto Failure;
  441. }
  442. //
  443. // Free the current buffer and increase the size that we try to allocate
  444. // next time around.
  445. //
  446. LocalFree( pBuffer );
  447. dwAllocSize = dwNeeded;
  448. }
  449. if( !GetTempFileName( (LPSTR)pBuffer, "LprSpl", 0, pchSpoolPath ))
  450. {
  451. goto Failure;
  452. }
  453. //
  454. // At this point, the spool file name should be done. Free the structure
  455. // we used to get the spooler temp dir and return.
  456. //
  457. LocalFree( pBuffer );
  458. *ppchSpoolPath = pchSpoolPath;
  459. return( TRUE );
  460. Failure:
  461. //
  462. // Clean up and fail.
  463. //
  464. if ( pBuffer != NULL )
  465. {
  466. LocalFree( pBuffer );
  467. }
  468. if ( pchSpoolPath != NULL )
  469. {
  470. LocalFree( pchSpoolPath );
  471. }
  472. return( FALSE );
  473. }
  474. VOID CleanupCFile( PCFILE_ENTRY pCFile )
  475. {
  476. if (pCFile->pchCFileName != NULL) {
  477. LocalFree( pCFile->pchCFileName );
  478. pCFile->pchCFileName = NULL;
  479. }
  480. if (pCFile->pchCFile != NULL) {
  481. LocalFree( pCFile->pchCFile );
  482. pCFile->pchCFile = NULL;
  483. }
  484. LocalFree( pCFile );
  485. }
  486. VOID CleanupDFile( PDFILE_ENTRY pDFile )
  487. {
  488. if (pDFile->pchDFileName != NULL) {
  489. LocalFree( pDFile->pchDFileName );
  490. pDFile->pchDFileName = NULL;
  491. }
  492. if (pDFile->hDataFile != INVALID_HANDLE_VALUE) {
  493. CloseHandle(pDFile->hDataFile);
  494. }
  495. LocalFree( pDFile );
  496. }
  497. VOID CleanupConn( PSOCKCONN pscConn)
  498. {
  499. LIST_ENTRY *pTmpList;
  500. CFILE_ENTRY *pCFile;
  501. DFILE_ENTRY *pDFile;
  502. while ( !IsListEmpty( &pscConn->CFile_List ) ) {
  503. pTmpList = RemoveHeadList( &pscConn->CFile_List );
  504. pCFile = CONTAINING_RECORD( pTmpList,
  505. CFILE_ENTRY,
  506. Link );
  507. CleanupCFile( pCFile );
  508. }
  509. while ( !IsListEmpty( &pscConn->DFile_List ) ) {
  510. pTmpList = RemoveHeadList( &pscConn->DFile_List );
  511. pDFile = CONTAINING_RECORD( pTmpList,
  512. DFILE_ENTRY,
  513. Link );
  514. CleanupDFile( pDFile );
  515. }
  516. return;
  517. }
  518.