////////////////////////////////////////////////////////////////////////////////
//
//	MacPrint - Windows NT Print Server for Macintosh Clients
//		Copyright (c) Microsoft Corp., 1991, 1992, 1993
//
//	Author: Frank D. Byrum
//		adapted from MacPrint for LAN Manager Services for Macintosh
//		which was adapted from the 3Com product
//
////////////////////////////////////////////////////////////////////////////////

/*
	File Name: MacPS.C

	General Description:

	This is the main module for the WNTSFM (Grimace) Print Service.
	The main routine is registered with the NT Service Controller by the
	Grimace Service Process, and is invoked when a SERVICE_START
	request is received for MacPrint.

	The routine immediately registers a service control handler
	to field service control requests from the NT Service
	Controller.  It then retreives configuration information
	from the registry and starts a thread for each configured
	print queue to manage the print jobs for the queue.

	Spooling is done by making each shared NT print queue appear
	as a LaserWriter on the AppleTalk network.  Each queue is
	shared by using AppleTalk PAP to register the queue on
	the AppleTalk network.  Once the queue name is published, a
	read driven loop is entered to support any connections/requests
	from AppleTalk clients.

	The flow of the Print Service threads is as follows:


	main ()
		NT Service Control dispatch thread for MacPrint.

	MacPrintMain()
		Registers a service control handler routine with
		the NT Service Controller.	If there is an error
		registering the handler, MacPrintMain logs a
		critical error message and returns.	This indicates
		to the NT Service Controller that MacPrint has
		stopped.

		Initializes per queue data structures based on
		information from the NT Registry.  For any queue
		that data structures cannot be initialized, a
		warning message is logged, and the control thread
		for that queue is not started.

		Spawns a thread for each queue that handles print
		jobs for that queue

		Enters a loop on a flag that is changed when a
		service stop request is received. This loop traverses
		the list of queues to see if they are still shared
		by NT and enumerates the list of shared queues to
		see if any new queues need to be published on
		the AppleTalk network.

	Each service thread:

		Each service thread supports a single print queue
		on the AppleTalk network.

		It publishes the NBP name of the printer on the
		AppleTalk network to allow Macintosh clients to
		see the print queue from the Chooser.

		It posts an ATalkPAPGetNextJob request to service
		a print request.  This allows Macintosh clients to
		connect to the print queue.

		It enters a service loop and remains there until
		the service is stopped, or that particular queue
		is 'unshared' by the NT Print Manager.	This service
		loop handles all states of a print job (OPEN, READ,
		WRITE, CLOSE), and transfers data for the print job
		to the NT Print Manager.
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <windows.h>
#include <winsvc.h>
#include <winspool.h>

#include <macps.h>
#include <macpsmsg.h>
#include <debug.h>

#define PRINTER_ENUM_BUFFER_SIZE	1024
#define	MACPRINT_WAIT_HINT			25*60000	// 25 minutes

BOOL	fLocalSystem;

SERVICE_STATUS			MacPrintStatus;
/*  MacPrintStatus is the global service status structure.  It is
	read by multiple threads and written ONLY by the service control
	handler (it is initialized by MacPrintMain before the service
	control handler is started)		*/

SERVICE_STATUS_HANDLE	hMacPrintService;
/*  hMacPrintService is the handle to the MacPrint service used in
	calls to SetServiceStatus to change the state of the MacPrint
	service.  It is initialized when the service control handler is
	created by MacPrintMain(), and is used only by MacPrintHandler()  */

#if DBG
HANDLE	hDumpFile = INVALID_HANDLE_VALUE;
#endif
HANDLE	hLogFile = INVALID_HANDLE_VALUE;

HANDLE	hEventLog = NULL;
ULONG	ShareCheckInterval;	// number of miliseconds between polls of the NT Print Manager
							// to update our queue list
HANDLE	mutexQueueList;		// provide mutual exclusion to the linked list of active queues
							// need to change to be a critical section
HANDLE  mutexFlCache;      // mutual exclusion for the failCache queue
HANDLE	hevStopRequested;	// event that is signalled when a stop request is received from
							// the service controller. The main thread that dispatches queue
							// threads waits on this event (with a timeout) to signal all
							// queue threads to die.
PQR	 pqrHead = NULL;
PFAIL_CACHE FlCacheHead = NULL;

//
// Function Prototypes for MacPS.c
//
VOID	MacPrintMain(DWORD dwNumServicesArgs, LPTSTR * lpServiceArgs);
VOID	UpdateQueueInfo(PQR * ppqrHead);
VOID	MacPrintHandler(IN DWORD dwControl);
BOOLEAN	PScriptQInit(PQR pqr, LPPRINTER_INFO_2 pPrinter);
PQR		FindPrinter(LPPRINTER_INFO_2 pSearch, PQR pqrHead);
void	ReadRegistryParameters(void);
#define	IsRemote(pPrinter)	(((pPrinter)->Attributes & PRINTER_ATTRIBUTE_NETWORK) ? TRUE : FALSE)

/*  main()

	Purpose:
	This is the service control dispatcher thread.	It connects
	to the NT Service Controller and provides the mechanism to
	start the MacPrint service.

	Entry:
	Standard C arguments that are ignored

	Exit:
	Exits on service stop with return of 0
*/

_CRTAPI1
main(
	int		argc,
	char **	argv
)
{

	SERVICE_TABLE_ENTRY		 ServiceTable[2];

	/* initialize the service table for MacPrint */
	ServiceTable[0].lpServiceName = MACPRINT_NAME;
	ServiceTable[0].lpServiceProc = &MacPrintMain;
	ServiceTable[1].lpServiceName = NULL;
	ServiceTable[1].lpServiceProc = NULL;

	StartServiceCtrlDispatcher(ServiceTable);

	return(0);
}


/*  MacPrintMain()

	Purpose:

	This is the 'service main function' described in the NT
	Service Control model, and it is invoked by the Service
	Control Dispatcher for the MacPrint service.  It initializes
	data structures for the MacPrint service, registers a service
	control handler, and dispatches threads to support print
	queues shared on the AppleTalk network.

	Entry:

	dwNumServiceArgs: undefined
	lpServiceArgs: undefined

	The main function implements the standard service main function
	interface, but uses no arguments.  The standard arguments are
	ignored.

	Exit:

	The routine returns no argument.  It terminates when the MacPrint
	service stops.
*/

#define ALLOCATED_QUEUE_MUTEX		0x00000001
#define ALLOCATED_SERVICE_STARTED	0x00000002
#define ALLOCATED_STOP_EVENT		0x00000004

VOID
MacPrintMain(
	DWORD		dwNumServicesArgs,
	LPTSTR *	lpServiceArgs
)
{
	DWORD	fAllocated = 0;
	PQR		pqr = NULL;
	DWORD	dwError;
	WSADATA WsaData;
	DWORD	cbSizeNeeded;
	HANDLE	hProcessToken = INVALID_HANDLE_VALUE;
	SID		LocalSystemSid = { 1, 1, SECURITY_NT_AUTHORITY, SECURITY_LOCAL_SYSTEM_RID };
	BYTE 	TokenUserBuffer[100];
    PFAIL_CACHE pFlCache, nextFlCache;
	TOKEN_USER * pTokenUser  = (TOKEN_USER *)TokenUserBuffer;

	do
	{
		//
		// prepare the event log.  If it doesn't register, there is nothing
		// we can do anyway.  All calls to ReportEvent will be with a NULL
		// handle and will probably fail.
		//
		hEventLog = RegisterEventSource(NULL, MACPRINT_EVENT_SOURCE);

		//
		// Initialize global data
		//
		ReadRegistryParameters();

		if ((hevStopRequested = CreateEvent(NULL, TRUE, FALSE, NULL)) == NULL)
		{
			dwError = GetLastError();
			DBGPRINT(("ERROR: unable to create stop request event, error = %d\n", dwError));

			ReportEvent(hEventLog,
						EVENTLOG_ERROR_TYPE,
						EVENT_CATEGORY_INTERNAL,
						EVENT_SERVICE_OUT_OF_RESOURCES,
						NULL,
						0,
						sizeof(DWORD),
						NULL,
						&dwError);
			break;
		}
		else
		{
			fAllocated |= ALLOCATED_STOP_EVENT;
		}

		if ((mutexQueueList = CreateMutex(NULL, FALSE, NULL)) == NULL)
		{
			dwError = GetLastError();
			DBGPRINT(("ERROR: Unable to create queue mutex object, error = %d\n",
					dwError));

			ReportEvent(hEventLog,
						EVENTLOG_ERROR_TYPE,
						EVENT_CATEGORY_INTERNAL,
						EVENT_SERVICE_OUT_OF_RESOURCES,
						NULL,
						0,
						sizeof(DWORD),
						NULL,
						&dwError);
			break;
		}
		else
		{
			fAllocated |= ALLOCATED_QUEUE_MUTEX;
		}

		if ((mutexFlCache = CreateMutex(NULL, FALSE, NULL)) == NULL)
		{
			dwError = GetLastError();
			DBGPRINT(("ERROR: Unable to create FailCache mutex object, error = %d\n",
					dwError));

			ReportEvent(hEventLog,
						EVENTLOG_ERROR_TYPE,
						EVENT_CATEGORY_INTERNAL,
						EVENT_SERVICE_OUT_OF_RESOURCES,
						NULL,
						0,
						sizeof(DWORD),
						NULL,
						&dwError);
			break;
		}

		DBGPRINT(("\nMacPrint starting\n"));

		//
		// initialize Windows Sockets
		//
		if (WSAStartup(0x0101, &WsaData) == SOCKET_ERROR)
		{
			dwError = GetLastError();
			DBGPRINT(("WSAStartup fails with %d\n", dwError));
			break;
		}

		//
		//	register service control handler
		//
		MacPrintStatus.dwServiceType = SERVICE_WIN32;
		MacPrintStatus.dwCurrentState = SERVICE_START_PENDING;
		MacPrintStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
		MacPrintStatus.dwWin32ExitCode = NO_ERROR;
		MacPrintStatus.dwServiceSpecificExitCode = NO_ERROR;
		MacPrintStatus.dwCheckPoint = 1;
		MacPrintStatus.dwWaitHint = MACPRINT_WAIT_HINT;

		if ((hMacPrintService = RegisterServiceCtrlHandler(MACPRINT_NAME,
														   &MacPrintHandler)) == (SERVICE_STATUS_HANDLE) NULL)
		{

			dwError = GetLastError();
			DBGPRINT(("ERROR: failed to register service control handler, error=%d\n",dwError));
			ReportEvent(hEventLog,
						EVENTLOG_ERROR_TYPE,
						EVENT_CATEGORY_INTERNAL,
						EVENT_SERVICE_CONTROLLER_ERROR,
						NULL,
						0,
						sizeof(DWORD),
						NULL,
						&dwError);
			break;
		}

		//
		// Determine if we are running in LocalSystem context
		//
		if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hProcessToken))
		{
			dwError = GetLastError();

			DBGPRINT(("MacPrintMain: OpenProcessToken returns %d\n", dwError));

			if (dwError == ERROR_ACCESS_DENIED)
				 fLocalSystem = FALSE;
			else break;
		}

		if (!GetTokenInformation(hProcessToken,
								 TokenUser,
								 pTokenUser,
								 sizeof(TokenUserBuffer),
								 &cbSizeNeeded))
		{
			dwError = GetLastError();

			DBGPRINT(("MacPrintMain:GetTokenInformation returns%d\n",dwError));

			if (dwError == ERROR_INSUFFICIENT_BUFFER)
				 fLocalSystem = FALSE;
			else break;
		}
		else
		{
			fLocalSystem = EqualSid(pTokenUser->User.Sid, &LocalSystemSid);
		}

		DBGPRINT(("MacPrintMain:fLocalSystem %d\n", fLocalSystem));

		// Create a security object. This is really just a security descriptor
		// is self-relative form. This procedure will allocate memory for this
		// security descriptor and copy all in the information passed in. This

		DBGPRINT(("MacPrintMain: registered service control handler\n"));

		//
		// show service started
		//

		MacPrintStatus.dwCurrentState = SERVICE_RUNNING;
		if (!SetServiceStatus(hMacPrintService, &MacPrintStatus))
		{
			DBGPRINT(("MacPrintHandler: FAIL - unable to change state.  err = %d\n", GetLastError()));
			break;
		}

		DBGPRINT(("changed to SERVICE_RUNNING\n"));
		fAllocated |= ALLOCATED_SERVICE_STARTED;
#if 0
		ReportEvent(hEventLog,
					EVENTLOG_INFORMATION_TYPE,
					EVENT_CATEGORY_ADMIN,
					EVENT_SERVICE_STARTED,
					NULL,
					0,
					0,
					NULL,
					NULL);
#endif

		// poll print manager for install/removal of printer objects
		while (MacPrintStatus.dwCurrentState == SERVICE_RUNNING)
		{
			UpdateQueueInfo(&pqrHead);

			DBGPRINT(("Waiting for a ShareCheckInterval\n"));
			WaitForSingleObject(hevStopRequested, ShareCheckInterval);
		}
	} while (FALSE);

	//
	//	wait for all worker threads to die
	//

	if (fAllocated & ALLOCATED_QUEUE_MUTEX)
	{
		while (pqrHead != NULL)
		{
			DBGPRINT(("Sleeping 100ms while threads die\n"));
			MacPrintStatus.dwCheckPoint++;
			SetServiceStatus(hMacPrintService, &MacPrintStatus);
			Sleep(100);
		}
		CloseHandle(mutexQueueList);
	}


    // if there were any entries in the failed cache, free them now
    for ( pFlCache=FlCacheHead; pFlCache != NULL; pFlCache = nextFlCache )
    {
        nextFlCache = pFlCache->Next;
        LocalFree( pFlCache );
    }

    if (mutexFlCache != NULL)
    {
        CloseHandle(mutexFlCache);
    }
	//
	// disconnect from Windows Sockets
	//

	WSACleanup();

	//
	//	change service state to stopped
	//

	MacPrintStatus.dwCurrentState = SERVICE_STOPPED;

	if (!SetServiceStatus(hMacPrintService, &MacPrintStatus))
	{
		DBGPRINT(("ERROR: unable to change status to SERVICE_STOPPED.%d\n",
		GetLastError()));
	}
	else
	{
		DBGPRINT(("changed state to SERVICE_STOPPED\n"));
#if 0
		ReportEvent(hEventLog,
					EVENTLOG_INFORMATION_TYPE,
					EVENT_CATEGORY_ADMIN,
					EVENT_SERVICE_STOPPED,
					NULL,
					0,
					0,
					NULL,
					NULL);
#endif
	}

	if (hProcessToken != INVALID_HANDLE_VALUE)
		CloseHandle(hProcessToken);


	if (fAllocated & ALLOCATED_STOP_EVENT)
	{
		CloseHandle(hevStopRequested);
	}

	if (hLogFile != INVALID_HANDLE_VALUE)
	{
		CloseHandle(hLogFile);
	}

#if DBG
	if (hDumpFile != INVALID_HANDLE_VALUE)
	{
		CloseHandle(hDumpFile);
	}
#endif
}



////////////////////////////////////////////////////////////////////////////////
// 	ReadRegistryParameters()
//
//  DESCRIPTION:  This routine reads all configuration parameters from
//		the registry and modifies global variables to make those parameters
//		available to the rest of the service.  They include:
//
//			ShareCheckInterval
//			hLogFile
//			hDumpFile
////////////////////////////////////////////////////////////////////////////////
void
ReadRegistryParameters(
	void
)
{
	HKEY	hkeyMacPrintRoot = INVALID_HANDLE_VALUE;
	HKEY	hkeyParameters = INVALID_HANDLE_VALUE;
	LONG	Status;
	DWORD	cbShareCheckInterval = sizeof(DWORD);
	LPWSTR	pszLogPath = NULL;
	DWORD	cbLogPath = 0;
	LPWSTR	pszDumpPath = NULL;
	DWORD	cbDumpPath = 0;
	DWORD	dwValueType;
	DWORD	dwError;

	//
	// resource allocation 'loop'
	//

	do
	{
		//
		// initialize to defaults
		//

		ShareCheckInterval = PRINT_SHARE_CHECK_DEF;
#if DBG
		hLogFile = (HANDLE)STD_OUTPUT_HANDLE;
		hDumpFile = INVALID_HANDLE_VALUE;
#endif

		//
		// Open the service control key
		//

		if ((Status = RegOpenKeyEx(
				HKEY_LOCAL_MACHINE,
				HKEY_MACPRINT,
				0,
				KEY_READ,
				&hkeyMacPrintRoot)) != ERROR_SUCCESS)
			{

			dwError = GetLastError();
			if (dwError == ERROR_ACCESS_DENIED)
			{
				ReportEvent(hEventLog,
							EVENTLOG_ERROR_TYPE,
							EVENT_CATEGORY_INTERNAL,
							EVENT_REGISTRY_ACCESS_DENIED,
							NULL,
							0,
							0,
							NULL,
							NULL);

			}
			else
			{
				ReportEvent(hEventLog,
							EVENTLOG_ERROR_TYPE,
							EVENT_CATEGORY_INTERNAL,
							EVENT_REGISTRY_ERROR,
							NULL,
							0,
							sizeof(DWORD),
							NULL,
							&dwError);
			}

			hkeyMacPrintRoot = INVALID_HANDLE_VALUE;
			break;
		}

		//
		// Open the parameters key
		//

		if ((Status = RegOpenKeyEx(
				hkeyMacPrintRoot,
				HKEY_PARAMETERS,
				0,
				KEY_READ,
				&hkeyParameters)) != ERROR_SUCCESS)
			{

			dwError = GetLastError();

			if (dwError == ERROR_ACCESS_DENIED)
			{
				ReportEvent(hEventLog,
							EVENTLOG_ERROR_TYPE,
							EVENT_CATEGORY_INTERNAL,
							EVENT_REGISTRY_ACCESS_DENIED,
							NULL,
							0,
							0,
							NULL,
							NULL);
			}
			else
			{
				ReportEvent(hEventLog,
							EVENTLOG_ERROR_TYPE,
							EVENT_CATEGORY_INTERNAL,
							EVENT_REGISTRY_ERROR,
							NULL,
							0,
							sizeof(DWORD),
							NULL,
							&dwError);
			}

			hkeyParameters = INVALID_HANDLE_VALUE;
			break;
		}

		//
		// get the share check interval
		//

		RegQueryValueEx(
				hkeyParameters,
				HVAL_SHARECHECKINTERVAL,
				NULL,
				&dwValueType,
				(LPBYTE) &ShareCheckInterval,
				&cbShareCheckInterval);

#if DBG
		//
		// get the log file path
		//

		RegQueryValueEx(hkeyParameters,
				HVAL_LOGFILE,
				NULL,
				&dwValueType,
				(LPBYTE) pszLogPath,
				&cbLogPath);
		if (cbLogPath > 0)
		{
			// cbLogPath is really a count of characters
			pszLogPath = (LPWSTR)LocalAlloc(LPTR, (cbLogPath + 1) * sizeof(WCHAR));
			if (pszLogPath == NULL)
			{
				ReportEvent(hEventLog,
							EVENTLOG_ERROR_TYPE,
							EVENT_CATEGORY_INTERNAL,
							EVENT_SERVICE_OUT_OF_MEMORY,
							NULL,
							0,
							0,
							NULL,
							NULL);
				break;
			}
		}


		if ((Status = RegQueryValueEx(hkeyParameters,
									HVAL_LOGFILE,
									NULL,
									&dwValueType,
									(LPBYTE) pszLogPath,
									&cbLogPath)) == ERROR_SUCCESS)
		{
			//
			// open the log file
			//

			hLogFile = CreateFile(pszLogPath,
								GENERIC_WRITE,
								FILE_SHARE_READ,
								NULL,
								CREATE_ALWAYS,
								FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH,
								NULL);

			if (hLogFile == INVALID_HANDLE_VALUE)
			{
				dwError = GetLastError();
				ReportEvent(hEventLog,
							EVENTLOG_ERROR_TYPE,
							EVENT_CATEGORY_INTERNAL,
							EVENT_SERVICE_CREATE_FILE_ERROR,
							NULL,
							1,
							sizeof(DWORD),
							&pszLogPath,
							&dwError);
			}
			else
			{
				ReportEvent(hEventLog,
							EVENTLOG_INFORMATION_TYPE,
							EVENT_CATEGORY_INTERNAL,
							EVENT_SERVICE_CREATE_LOG_FILE,
							NULL,
							1,
							0,
							&pszLogPath,
							NULL);
			}

		}
		else
		{
			hLogFile = INVALID_HANDLE_VALUE;
		}


		DBGPRINT(("MACPRINT LOG FLE OPENED\n\n"));

		//
		// get the dump file path
		//

		RegQueryValueEx(hkeyParameters,
						HVAL_DUMPFILE,
						NULL,
						&dwValueType,
						(LPBYTE) pszDumpPath,
						&cbDumpPath);

		if (cbDumpPath > 0)
		{
			// cbDumpPath is really a count of characters
			pszDumpPath = (LPWSTR)LocalAlloc(LPTR, (cbDumpPath + 1) * sizeof(WCHAR));
			if (pszDumpPath == NULL)
			{
				DBGPRINT(("ERROR: cannot allocate buffer for dump file path\n"));
				ReportEvent(hEventLog,
							EVENTLOG_ERROR_TYPE,
							EVENT_CATEGORY_INTERNAL,
							EVENT_SERVICE_OUT_OF_MEMORY,
							NULL,
							0,
							0,
							NULL,
							NULL);
				break;
			}
		}

		if ((Status = RegQueryValueEx(hkeyParameters,
									HVAL_DUMPFILE,
									NULL,
									&dwValueType,
									(LPBYTE) pszDumpPath,
									&cbDumpPath)) != ERROR_SUCCESS)
		{
			DBGPRINT(("ERROR: no dump path, rc = 0x%lx\n", Status));
		}
		else
		{
			//
			// open the dump file
			//

			hDumpFile = CreateFile(pszDumpPath,
									GENERIC_WRITE,
									FILE_SHARE_READ,
									NULL,
									CREATE_ALWAYS,
									FILE_ATTRIBUTE_NORMAL,
									NULL);
		}
#endif
	} while (FALSE);

	//
	// resource cleanup
	//

	if (hkeyParameters != INVALID_HANDLE_VALUE)
	{
		RegCloseKey(hkeyParameters);
	}

	if (hkeyMacPrintRoot != INVALID_HANDLE_VALUE)
	{
		RegCloseKey(hkeyMacPrintRoot);
	}

#if DBG
	if (pszLogPath != NULL)
	{
		LocalFree (pszLogPath);
	}

	if (pszDumpPath != NULL)
	{
		LocalFree (pszDumpPath);
	}
#endif
}


////////////////////////////////////////////////////////////////////////////////
// 	FindPrinter() - locate a printer in our list of printers
//
//  DESCRIPTION:
//
//		Given an NT printer information structure and a pointer to the head of
//		a list of our printer structures, this routine will return a pointer
//		to our printer structure that corresponds to the printer described by
//		the NT printer information structure.  If no such printer is found in
//		our list, this routine returns NULL.
//
////////////////////////////////////////////////////////////////////////////////
PQR
FindPrinter(
	LPPRINTER_INFO_2	pSearch,
	PQR 				pqrHead
)
{
	PQR status = NULL;
	PQR pqrCurrent;

	for (pqrCurrent = pqrHead; pqrCurrent != NULL; pqrCurrent = pqrCurrent->pNext)
	{
		if (_wcsicmp(pSearch->pPrinterName, pqrCurrent->pPrinterName) == 0)
		{
			return (pqrCurrent);
		}
	}
	return (NULL);
}


////////////////////////////////////////////////////////////////////////////////
//
//	UpdateQueueInfo() - get new list of printers from NT
//
//	DESCRIPTION:
//		This routine is called periodically to see if any new NT Printer Objects
//		have been created or if any old ones have been destroyed since the last
//		time this routine was called.  For each new printer object discovered,
//		a thread is started to manage that printer object.  For each printer object
//		destroyed, the thread corresponding to that printer object is signalled
//		to quit.
//
//		This routine takes a pointer to the head of a list of printers and
//		makes certain that the list corresponds to the set of currently
//		defined NT Printer Objects.
//
////////////////////////////////////////////////////////////////////////////////
#define ALLOCATED_RELEASE_MUTEX	 0x00000001
#define ALLOCATED_ENUM_BUFFER	0x00000002

VOID
UpdateQueueInfo(
	PQR *	ppqrHead
)
{
	DWORD		fAllocated = 0;
	PQR			pqrCurrent;
	PQR			pqrTemp;
	DWORD		i;
	DWORD		dwThreadId;
	DWORD		cbNeeded = 0;
	DWORD		cPrinters = 0;
	LPBYTE		pPrinters = NULL;
	LPPRINTER_INFO_2	pinfo2Printer;
	HANDLE		ahWaitList[MAXIMUM_WAIT_OBJECTS];
	DWORD		dwReturn;
	DWORD		dwError;
	BOOLEAN		boolEnumOK = TRUE;

	DBGPRINT(("Entering UpdateQueueInfo\n"));

	do
	{
		//
		//	take the QueueList mutex
		//

		if (WaitForSingleObject(mutexQueueList, INFINITE) == 0)
		{
			fAllocated |= ALLOCATED_RELEASE_MUTEX;
			DBGPRINT(("UpdateQueueInfo takes mutexQueueList\n"));
		}
		else
		{
			//
			// fatal error - log a message and stop the service
			//

			DBGPRINT(("ERROR: problem waiting for queue list mutex, error = %d\n",  GetLastError()));
			dwReturn = 0;
			break;
		}

		//
		//	Mark all the queues NOT FOUND
		//

		for (pqrCurrent = *ppqrHead;
			 pqrCurrent != NULL;
			 pqrCurrent = pqrCurrent->pNext)
		{
			pqrCurrent->bFound = FALSE;
		}

		DBGPRINT(("queues marked not found\n"));

		//
		//	Enumerate the local printers
		//

		cPrinters = 0;
		if ((pPrinters = (LPBYTE)LocalAlloc(LPTR, PRINTER_ENUM_BUFFER_SIZE)) != NULL)
		{
			fAllocated |= ALLOCATED_ENUM_BUFFER;
		}
		else
		{
			//
			// out of resources - let service continue running
			//

			DBGPRINT(("ERROR: unable to allocated buffer for printer enum.  error = %d\n", GetLastError()));
			ReportEvent(hEventLog,
						EVENTLOG_ERROR_TYPE,
						EVENT_CATEGORY_INTERNAL,
						EVENT_SERVICE_OUT_OF_MEMORY,
						NULL,
						0,
						0,
						NULL,
						NULL);
			dwReturn = 1;
			break;
		}

		dwReturn = 0;
		cbNeeded = PRINTER_ENUM_BUFFER_SIZE;

		while (!EnumPrinters(PRINTER_ENUM_SHARED | PRINTER_ENUM_LOCAL,
							NULL,
							2,
							pPrinters,
							cbNeeded,
							&cbNeeded,
							&cPrinters))
		{
			//
			// enum failed - allocate more data if we need it, or fail
			//

			if ((dwError = GetLastError()) != ERROR_INSUFFICIENT_BUFFER)
			{
				//
				// the NT spooler is probably dead - stop the service
				//
				ReportEvent(hEventLog,
							EVENTLOG_ERROR_TYPE,
							EVENT_CATEGORY_INTERNAL,
							EVENT_SPOOLER_NOT_RESPONDING,
							NULL,
							0,
							sizeof(DWORD),
							NULL,
							&dwError);

				DBGPRINT (("ERROR:  Unable to enumerate printers, error = %d\n",dwError));
				boolEnumOK = FALSE;
				dwReturn = 0;
				break;
			}

			//
			// allocate a larger buffer
			//

			LocalFree(pPrinters);
			cPrinters = 0;
			if ((pPrinters = (LPBYTE)LocalAlloc(LPTR, cbNeeded)) == NULL)
			{
				//
				// out of resources, see if service will continue to run
				//

				fAllocated &= ~ALLOCATED_ENUM_BUFFER;
				dwError = GetLastError();
				DBGPRINT(("ERROR: unable to reallocate printer enum buffer, error = %d\n",dwError));
				ReportEvent(hEventLog,
							EVENTLOG_ERROR_TYPE,
							EVENT_CATEGORY_INTERNAL,
							EVENT_SERVICE_OUT_OF_MEMORY,
							NULL,
							0,
							0,
							NULL,
							NULL);
				boolEnumOK = FALSE;
				dwReturn = 1;
				break;
			}
		}

		if (!boolEnumOK)
		{
			break;
		}

		DBGPRINT(("%d printers enumerated\n", cPrinters));

		//
		//	For each LOCAL_PRINTER, attempt to find it in the
		//	queue list and change its status to FOUND.	If it
		//	can't be found in the list that add it and start
		//	a service thread for it
		//
		dwReturn = 1;
		for (i = 0, pinfo2Printer = (LPPRINTER_INFO_2)pPrinters;
			 i < cPrinters;
			 i++, pinfo2Printer++)
		{
			//
			// do not count pending_deletion printers as found
			//

            if (MacPrintStatus.dwCurrentState != SERVICE_RUNNING)
            {
                DBGPRINT(("Service stopping: quitting UpdateQueueInfo\n"));
                break;
            }

			if (pinfo2Printer->Status & PRINTER_STATUS_PENDING_DELETION)
				continue;

			pqrCurrent = FindPrinter(pinfo2Printer,*ppqrHead);

			if ((pqrCurrent != NULL) &&
				(_wcsicmp(pqrCurrent->pDriverName, pinfo2Printer->pDriverName) == 0))
			{
				//
				// printer already going, mark it found
				//

				pqrCurrent->bFound = TRUE;
			}
			else
			{
				//
				// start a new thread, but first make sure we are still running
				//

				DBGPRINT(("Discovered new printer, starting thread\n"));

				//
				// allocate a new queue record
				//

				if ((pqrCurrent = (PQR)LocalAlloc(LPTR, sizeof(QUEUE_RECORD))) == NULL)
				{
					//
					// out of memory, but can still enumerate printers, so don't stop
					// the service, just quit
					//

					DBGPRINT(("ERROR: cannont allocate queue record, error = %d\n", GetLastError()));
					ReportEvent(hEventLog,
								EVENTLOG_ERROR_TYPE,
								EVENT_CATEGORY_INTERNAL,
								EVENT_SERVICE_OUT_OF_MEMORY,
								NULL,
								0,
								0,
								NULL,
								NULL);
					dwReturn = 1;
					break;
				}

				//
				// initialize it
				//
				if (!PScriptQInit(pqrCurrent, pinfo2Printer))
				{
					LocalFree (pqrCurrent);
					continue;
				}

				//
				// add it to the head of the list
				//

				pqrCurrent->pNext = *ppqrHead;
				*ppqrHead = pqrCurrent;

				//
				// start a service thread for the queue
				//

				if ((pqrCurrent->hThread = CreateThread(NULL,
														STACKSIZE,
														(LPTHREAD_START_ROUTINE)QueueServiceThread,
														(LPVOID)pqrCurrent,
														0,
														&dwThreadId)) == 0)
				{
					DBGPRINT(("ERROR: unable to start thread routine for %ws\n", pqrCurrent->pPrinterName));
					dwError = GetLastError();
					ReportEvent(hEventLog,
								EVENTLOG_ERROR_TYPE,
								EVENT_CATEGORY_INTERNAL,
								EVENT_SERVICE_OUT_OF_RESOURCES,
								NULL,
								0,
								sizeof(DWORD),
								NULL,
								&dwError);

					*ppqrHead = pqrCurrent->pNext;
					// BUG BUG - memory leak (pqrCurrent->pszXXXX) if can't start threads.
					LocalFree(pqrCurrent);
				} // end of CreateThread()
			} //end of discovering a new printer
		} // loop walking list of printers

		//
		//	Walk the list of queues for NOT_FOUND ones and signal
		//	the service thread for that queue to terminate.  Each
		//	thread will remove itself from the queue list and free
		//	its own queue entry.
		//

		DBGPRINT(("removing lost printers\n"));
		pqrCurrent = *ppqrHead;
		i = 0;
		while (pqrCurrent != NULL)
		{
			//
			// get the address of the next queue record and signal this
			// queue record to terminate if necessary.  Must save the
			// address before requesting termination as once ExitThread
			// is set to TRUE, the data structure is no longer accessible
			// (the queue thread could free it)
			//
			pqrTemp = pqrCurrent->pNext;
			if (!pqrCurrent->bFound)
			{
                // terminate the thread
				DBGPRINT(("signalling %ws to terminate\n", pqrCurrent->pPrinterName));
				ahWaitList[i++] = pqrCurrent->hThread;
				pqrCurrent->ExitThread = TRUE;
			}

			pqrCurrent = pqrTemp;

			if (i==MAXIMUM_WAIT_OBJECTS)
			{
				//
				//	release the queue mutex so that threads can remove
				//	themselves from the queue list.  The list beyond
				//	pqrCurrent should not be modified, so it's ok to
				//	continue from there when we're done
				//

				ReleaseMutex(mutexQueueList);
				WaitForMultipleObjects(i, ahWaitList, TRUE, INFINITE);

				// take the mutex again and continue
				WaitForSingleObject(mutexQueueList, INFINITE);
				i = 0;
			}
		} // end of walking the queue list for not found printers

		//
		// wait for all remaining worker threads to die
		//

		ReleaseMutex(mutexQueueList);
		fAllocated &= ~ALLOCATED_RELEASE_MUTEX;
		if (i > 0)
		{
			DBGPRINT(("waiting for terminated queues to die\n"));
			WaitForMultipleObjects(i, ahWaitList, TRUE, INFINITE);
		}
		dwReturn = 1;
	} while (FALSE);

	//
	// resource cleanup
	//

	if (fAllocated & ALLOCATED_RELEASE_MUTEX)
	{
		DBGPRINT(("UpdateQueueInfo releases mutexQueueList\n"));
		ReleaseMutex(mutexQueueList);
	}

	if (fAllocated & ALLOCATED_ENUM_BUFFER)
	{
		LocalFree(pPrinters);
	}

	if (dwReturn == 0)
	{
		//
		// unrecoverable error - stop the service
		//

		MacPrintStatus.dwCurrentState = SERVICE_STOP_PENDING;
		if (!SetServiceStatus(hMacPrintService, &MacPrintStatus))
		{
			DBGPRINT(("UpdateQueueInfo: FAIL - unable to change state.  err = %ld\n", GetLastError()));
		}
		else
		{
			DBGPRINT(("UpdateQueueInfo: changed to SERVICE_STOP_PENDING\n"));
		}
		SetEvent(hevStopRequested);
	}
}



////////////////////////////////////////////////////////////////////////////////
//
//	MacPrintHandler() - handles service control requests
//
//	DESCRIPTION:
//		This routine receives and processes service requests from the NT
//		Service Controller.  Supported requests include:
//
//			SERVICE_CONTROL_STOP
//			SERVICE_CONTROL_INTERROGATE
//
//		dwControl is the service control request.
////////////////////////////////////////////////////////////////////////////////
VOID
MacPrintHandler (
	IN DWORD dwControl
)
{

	switch (dwControl)
	{
	  case SERVICE_CONTROL_STOP:
 		DBGPRINT(("MacPrintHandler: received SERVICE_CONTROL_STOP\n"));
		MacPrintStatus.dwCurrentState = SERVICE_STOP_PENDING;
		MacPrintStatus.dwCheckPoint = 1;
		MacPrintStatus.dwWaitHint = MACPRINT_WAIT_HINT;

		if (!SetServiceStatus(hMacPrintService, &MacPrintStatus))
		{
			DBGPRINT(("MacPrintHandler: FAIL - unable to change state.  err = %ld\n", GetLastError()));
		}
		else
		{
			DBGPRINT(("changed to SERVICE_STOP_PENDING\n"));
		}

		SetEvent(hevStopRequested);
		break;

	  case SERVICE_CONTROL_INTERROGATE:
		DBGPRINT(("MacPrintHandler: received SERVICE_CONTROL_INTERROGATE\n"));
		if (!SetServiceStatus(hMacPrintService, &MacPrintStatus))
		{
			DBGPRINT(("MacPrintHandler: FAIL - unable to report state.  err = %ld\n", GetLastError()));
		}
		else
		{
			DBGPRINT(("returned status on interrogate\n"));
		}

		break;
	}
}


////////////////////////////////////////////////////////////////////////////////
//
//	PScriptQInit() - Initialize a Queue Record
//
//	DESCRIPTION:
//		This routine initializes a queue record with the PostScript
//		capabilities of an NT Printer Object as well as allocates
//		the events and system resources necessary to control the
//		queue.
//
////////////////////////////////////////////////////////////////////////////////

BOOLEAN
PScriptQInit(
	PQR					pqr,
	LPPRINTER_INFO_2	pPrinter
)
{
	BOOLEAN			status = TRUE;
	PDRIVER_INFO_2	padiThisPrinter = NULL;
	DWORD			cbDriverInfoBuffer;
	HANDLE			hPrinter = NULL;
	DWORD			cbDataFileName;
	DWORD			rc;

	//
	// resource allocation 'loop'
	//
	do
	{
		pqr->bFound = TRUE;
		pqr->hThread = NULL;
		pqr->JobCount = 0;			// No jobs yet.
		pqr->PendingJobs = NULL;	// Set pending jobs to none.
		pqr->ExitThread = FALSE;	// Set thread control.
		pqr->sListener = INVALID_SOCKET;
		pqr->fonts	  = NULL;
		pqr->MaxFontIndex = 0;
        pqr->pPrinterName = NULL;
        pqr->pMacPrinterName = NULL;
        pqr->pDriverName = NULL;
        pqr->IdleStatus = NULL;
        pqr->SpoolingStatus = NULL;

		//
		// convert printer name to Mac ANSI
		//

		pqr->pMacPrinterName = (LPSTR)LocalAlloc(LPTR, wcslen(pPrinter->pPrinterName) + 1);
		if (pqr->pMacPrinterName == NULL)
		{
			DBGPRINT(("out of memory for pMacPrinterName\n"));
			status = FALSE;
			break;
		}
		CharToOem(pPrinter->pPrinterName, pqr->pMacPrinterName);

		pqr->pPrinterName = (LPWSTR)LocalAlloc(LPTR,
											   (wcslen(pPrinter->pPrinterName) + 1) * sizeof(WCHAR));

		if (pqr->pPrinterName == NULL)
		{
			DBGPRINT(("out of memory for pPrinterName\n"));
			status = FALSE;
			break;
		}
		wcscpy (pqr->pPrinterName, pPrinter->pPrinterName);

		pqr->pDriverName = (LPWSTR)LocalAlloc(LPTR,
											  (wcslen(pPrinter->pDriverName) + 1) * sizeof(WCHAR));

		if (pqr->pDriverName == NULL)
		{
			DBGPRINT(("out of memory for pDriverName\n"));
			status = FALSE;
			break;
		}
		wcscpy (pqr->pDriverName, pPrinter->pDriverName);

		pqr->pPortName = (LPWSTR)LocalAlloc(LPTR,
											(wcslen(pPrinter->pPortName) + 1) * sizeof(WCHAR));

		if (pqr->pPortName == NULL)
		{
			DBGPRINT(("out of memory for pPortName\n"));
			status = FALSE;
			break;
		}
		wcscpy (pqr->pPortName, pPrinter->pPortName);

		//
		// determine the datatype to use
		//

		if (!OpenPrinter(pqr->pPrinterName, &hPrinter, NULL))
		{
			status = FALSE;
			DBGPRINT(("ERROR: OpenPrinter() fails with %d\n", GetLastError()));
			break;
		}

		//
		// start with a guess as to size of driver info buffer
		//

		cbDriverInfoBuffer = 2*sizeof(DRIVER_INFO_2);
		padiThisPrinter = (PDRIVER_INFO_2)LocalAlloc(LPTR, cbDriverInfoBuffer);

		if (!GetPrinterDriver(hPrinter,
							  NULL,
							  2,
							  (LPBYTE)padiThisPrinter,
							  cbDriverInfoBuffer,
							  &cbDriverInfoBuffer))
		{
			rc = GetLastError();
			DBGPRINT(("WARNING: first GetPrinterDriver call fails with %d\n", rc));
			LocalFree(padiThisPrinter);
                        padiThisPrinter = NULL;

			if (rc != ERROR_INSUFFICIENT_BUFFER)
			{
				status = FALSE;
				break;
			}

			//
			// failed with buffer size error.  Reallocate and retry
			//
			padiThisPrinter = (PDRIVER_INFO_2)LocalAlloc(LPTR, cbDriverInfoBuffer);

			if (padiThisPrinter == NULL)
			{
				status = FALSE;
				DBGPRINT(("out of memory for second padiThisPrinter\n"));
				break;
			}

			if (!GetPrinterDriver(hPrinter,
								  NULL,
								  2,
								  (LPBYTE)padiThisPrinter,
								  cbDriverInfoBuffer,
								  &cbDriverInfoBuffer))
			{
				DBGPRINT(("ERROR: final GetPrinterDriverA call fails with %d\n", GetLastError()));
				status = FALSE;
				break;
			}
		}
		//
		// driver takes postscript if the datafile is a .PPD file
		// otherwise, we'll send it ps2dib
		//
		pqr->pDataType = NULL;

		SetDefaultPPDInfo(pqr);

		if (padiThisPrinter->pDataFile != NULL)
		{
			if ((cbDataFileName = wcslen(padiThisPrinter->pDataFile)) > 3)
			{
				if (_wcsicmp(padiThisPrinter->pDataFile + cbDataFileName - 3, L"PPD") == 0)
				{
					if (IsRemote(pPrinter) && fLocalSystem)
					{
						DBGPRINT(("%ws is remote\n", pPrinter->pPrinterName));
						status = FALSE;
						break;
					}

					//
					// we are postscript
					//
					pqr->pDataType = (LPWSTR)LocalAlloc(LPTR,
														(wcslen(MACPS_DATATYPE_RAW) + 1) * sizeof(WCHAR));

					if (pqr->pDataType == NULL)
					{
						DBGPRINT(("out of memory for pDataType\n"));
						status = FALSE;
						break;
					}

					wcscpy (pqr->pDataType, MACPS_DATATYPE_RAW);
					DBGPRINT(("postscript printer, using RAW\n"));

					if (!GetPPDInfo(pqr))
					{
						DBGPRINT(("ERROR: unable to get PPD info for %ws\n", pqr->pPrinterName));
						status = FALSE;
						break;
					}
				} // ends in PPD
			} // filename longer than 3
		} // filename exists

		if (pqr->pDataType == NULL)
		{
			if (IsRemote(pPrinter))
			{
				DBGPRINT(("%ws is remote\n", pPrinter->pPrinterName));
				status = FALSE;
				break;
			}

			//
			// we are not postscript
			//
			pqr->pDataType = (LPWSTR)LocalAlloc(LPTR,
												(wcslen(MACPS_DATATYPE_PS2DIB) + 1) * sizeof(WCHAR));

			if (pqr->pDataType == NULL)
			{
				DBGPRINT(("out of memory for PSTODIB pDataType\n"));
				status = FALSE;
				break;
			}

			wcscpy (pqr->pDataType, MACPS_DATATYPE_PS2DIB);
			DBGPRINT(("non postscript printer, using PS2DIB\n"));

			if (!SetDefaultFonts(pqr))
			{
				DBGPRINT(("ERROR: cannot set to laserwriter PPD info for %ws\n",
						pqr->pPrinterName));
				status = FALSE;
				break;
			}
		}

	}  while (FALSE);

	//
	// resource cleanup
	//

	if (!status)
	{
		if (pqr->pPrinterName != NULL)
		{
			LocalFree(pqr->pPrinterName);
		}
		if (pqr->pMacPrinterName != NULL)
		{
			LocalFree(pqr->pMacPrinterName);
		}
		if (pqr->pDriverName != NULL)
		{
			LocalFree(pqr->pDriverName);
		}
		if (pqr->pPortName != NULL)
		{
			LocalFree(pqr->pPortName);
		}

		if (pqr->pDataType != NULL)
		{
			LocalFree(pqr->pDataType);
		}

		if (pqr->fonts != NULL)
		{
			LocalFree(pqr->fonts);
		}
	}

	if (hPrinter != NULL)
	{
		ClosePrinter(hPrinter);
	}

	if (padiThisPrinter != NULL)
	{
		LocalFree(padiThisPrinter);
	}

	return (status);
}




DWORD
CreateListenerSocket(
	PQR			pqr
)
{
	DWORD		rc = NO_ERROR;
	SOCKADDR_AT address;
	WSH_REGISTER_NAME	reqRegister;
	DWORD		cbWritten;
	ULONG		fNonBlocking;
	LPWSTR		pszuStatus = NULL;
	LPWSTR		apszArgs[1] = {NULL};
	DWORD		cbMessage;

	DBGPRINT(("enter CreateListenerSocker()\n"));

	//
	// resource allocation 'loop'
	//

	do
	{
		//
		// create a socket
		//

		pqr->sListener = socket(AF_APPLETALK, SOCK_RDM, ATPROTO_PAP);
		if (pqr->sListener == INVALID_SOCKET)
		{
			rc = GetLastError();
			DBGPRINT(("socket() fails with %d\n", rc));
			break;
		}

		//
		// bind the socket
		//

		address.sat_family = AF_APPLETALK;
		address.sat_net = 0;
		address.sat_node = 0;
		address.sat_socket = 0;

		if (bind(pqr->sListener, (PSOCKADDR) &address, sizeof(address)) == SOCKET_ERROR)
		{
			rc = GetLastError();
			DBGPRINT(("bind() fails with %d\n", rc));
			break;
		}

		//
		// post a listen on the socket
		//

		if (listen(pqr->sListener, 5) == SOCKET_ERROR)
		{
			rc = GetLastError();
			DBGPRINT(("listen() fails with %d\n", rc));
			break;
		}

		//
		// set the PAP Server Status
		//

		if ((apszArgs[0] = LocalAlloc(LPTR, sizeof(WCHAR) * (strlen(pqr->pMacPrinterName) + 1))) == NULL)
		{
			rc = GetLastError();
			DBGPRINT(("LocalAlloc(args) fails with %d\n", rc));
			break;
		}

		OemToChar(pqr->pMacPrinterName, apszArgs[0]);

		if ((cbMessage = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
											FORMAT_MESSAGE_FROM_HMODULE |
											FORMAT_MESSAGE_ARGUMENT_ARRAY,
										NULL,
										STRING_SPOOLER_ACTIVE,
										LANG_NEUTRAL,
										(LPWSTR)&pszuStatus,
										128,
										(va_list *)apszArgs)) == 0)
		{

			rc = GetLastError();
			DBGPRINT(("FormatMessage() fails with %d\n", rc));
			break;
		}

		//
		// remove trailing ret/lf
		//
		pszuStatus[cbMessage - 2] = 0;

/* MSKK NaotoN modified for MBCS system 11/13/93 */
#ifdef JAPAN
		if ((pqr->SpoolingStatus = LocalAlloc(LPTR, cbMessage * sizeof(USHORT))) == NULL)
#else
		if ((pqr->SpoolingStatus = LocalAlloc(LPTR, cbMessage)) == NULL)
#endif
		{
			rc = GetLastError();
			DBGPRINT(("LocalAlloc(SpoolingStatus) fails with %d\n", rc));
			break;
		}

		CharToOem(pszuStatus, pqr->SpoolingStatus);

		if ((cbMessage = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
											FORMAT_MESSAGE_FROM_HMODULE |
											FORMAT_MESSAGE_ARGUMENT_ARRAY,
										NULL,
										STRING_SPOOLER_IDLE,
										LANG_NEUTRAL,
										(LPWSTR)&pszuStatus,
										128,
										(va_list *)apszArgs)) == 0)
		{

			rc = GetLastError();
			DBGPRINT(("FormatMessage() fails with %d\n", rc));
			break;
		}

		//
		// remove trailing ret/lf
		//
		pszuStatus[cbMessage - 2] = 0;

		if ((pqr->IdleStatus = LocalAlloc(LPTR, cbMessage)) == NULL)
		{
			rc = GetLastError();
			DBGPRINT(("LocalAlloc(IdleStatus) fails with %d\n", rc));
			break;
		}

		CharToOem(pszuStatus, pqr->IdleStatus);

		DBGPRINT(("setting status to %s\n", pqr->IdleStatus));
		if ((setsockopt(pqr->sListener,
						SOL_APPLETALK,
						SO_PAP_SET_SERVER_STATUS,
						pqr->IdleStatus,
						strlen(pqr->IdleStatus))) == SOCKET_ERROR)
		{
			rc = GetLastError();
			DBGPRINT(("setsockopt(status) fails with %d\n", rc));
			break;
		}

		//
		// register a name on the socket
		//
		reqRegister.ZoneNameLen = sizeof(DEF_ZONE) - 1;
		reqRegister.TypeNameLen = sizeof(LW_TYPE) - 1;
		reqRegister.ObjectNameLen = strlen(pqr->pMacPrinterName);

		// Silently truncate the name if it exceeds the max. allowed
		if (reqRegister.ObjectNameLen > MAX_ENTITY)
			reqRegister.ObjectNameLen = MAX_ENTITY;

		memcpy (reqRegister.ZoneName, DEF_ZONE, sizeof(DEF_ZONE) - 1);
		memcpy (reqRegister.TypeName, LW_TYPE, sizeof(LW_TYPE) - 1);
		memcpy (reqRegister.ObjectName, pqr->pMacPrinterName, reqRegister.ObjectNameLen);

		cbWritten = sizeof(reqRegister);
		if (setsockopt(pqr->sListener,
					   SOL_APPLETALK,
					   SO_REGISTER_NAME,
					   (char *) &reqRegister,
					   cbWritten) == SOCKET_ERROR)
		{
			rc = GetLastError();

            if (CheckFailedCache(pqr->pPrinterName, PSP_ADD) != PSP_ALREADY_THERE)
            {
                DWORD   dwEvent;

                dwEvent = (rc == WSAEADDRINUSE)? EVENT_NAME_DUPNAME_EXISTS :
                                                EVENT_NAME_REGISTRATION_FAILED;

			    ReportEvent(hEventLog,
			           		EVENTLOG_ERROR_TYPE,
				        	EVENT_CATEGORY_INTERNAL,
					        dwEvent,
					        NULL,
					        1,
					        0,
					        &apszArgs[0],
					        NULL);
            }
			DBGPRINT(("setsockopt(SO_REGISTER_NAME) fails with %d\n", rc));
			break;
		}

		//
		// make the socket non-blocking
		//
		fNonBlocking = 1;
		if (ioctlsocket(pqr->sListener, FIONBIO, &fNonBlocking) == SOCKET_ERROR)
		{
			rc = GetLastError();
			DBGPRINT(("ioctlsocket(FIONBIO) fails with %d\n", rc));
			break;
		}

	} while (FALSE);

	//
	// resource cleanup
	//

	if (apszArgs[0] != NULL)
	{
		LocalFree (apszArgs[0]);
	}

	if (pszuStatus != NULL)
	{
		LocalFree (pszuStatus);
	}

    //
    // if this printer had failed previous initialization attempts, it will be in our
    // failed cache: remove it here.
    //
    if ((rc == NO_ERROR))
    {
        CheckFailedCache(pqr->pPrinterName, PSP_DELETE);
    }
    else
    {
	    // close the listener
	    DBGPRINT(("%ws: closing listener socket, error = %d\n", pqr->pPrinterName,rc));
        if (pqr->sListener != INVALID_SOCKET)
        {
	        closesocket(pqr->sListener);
            pqr->sListener = INVALID_SOCKET;
        }
    }

	return rc;
}

////////////////////////////////////////////////////////////////////////////////
//
//	CheckFailedCache(): find an entry, and add/delete the entry depending on dwAction
//
//	DESCRIPTION:
//		This routine caches all names of all printers that fail to initialize.  The current use
//      of such a cache is to avoid logging too many entries in event log for the same printer
//      which fails over and over.
//      In reality, we don't expect more than 1 (usually 0) entries in this cache!
////////////////////////////////////////////////////////////////////////////////

DWORD
CheckFailedCache(LPWSTR pPrinterName, DWORD dwAction)
{

    PFAIL_CACHE pFlCache, prevFlCache;
    BOOLEAN     bFound=FALSE;
    DWORD       dwRetCode;
    DWORD       dwSize;


    WaitForSingleObject(mutexFlCache, INFINITE);

    for ( pFlCache=prevFlCache=FlCacheHead; pFlCache != NULL; pFlCache = pFlCache->Next )
    {
        if (_wcsicmp(pFlCache->PrinterName, pPrinterName) == 0)
        {
            bFound = TRUE;
            break;
        }
        prevFlCache = pFlCache;
    }

    switch( dwAction )
    {
        case PSP_ADD:

            if (bFound)
            {
                ReleaseMutex(mutexFlCache);
                return(PSP_ALREADY_THERE);
            }

            dwSize = sizeof(FAIL_CACHE) + (wcslen(pPrinterName)+1)*sizeof(WCHAR);

            pFlCache = (PFAIL_CACHE)LocalAlloc(LPTR, dwSize);
            if (pFlCache == NULL)
            {
			    DBGPRINT(("CheckFailedCache: LocalAlloc failed!\n"));

                ReleaseMutex(mutexFlCache);
                // nothing evil should happen if we fail here, other than may be multiple
                // event log entries (which is what we are fixing now!)

                return(PSP_OPERATION_FAILED);
            }

            wcscpy (pFlCache->PrinterName, pPrinterName);

            pFlCache->Next = FlCacheHead;

            FlCacheHead = pFlCache;

            dwRetCode = PSP_OPERATION_SUCCESSFUL;

            break;

        case PSP_DELETE:

            if (!bFound)
            {
                ReleaseMutex(mutexFlCache);
                return(PSP_NOT_FOUND);
            }

            if (pFlCache == FlCacheHead)
            {
                FlCacheHead = pFlCache->Next;
            }
            else
            {
                prevFlCache->Next = pFlCache->Next;
            }

            LocalFree(pFlCache);

            dwRetCode = PSP_OPERATION_SUCCESSFUL;

            break;
    }

    ReleaseMutex(mutexFlCache);

    return( dwRetCode );

}
