1/* -*- Mode: C; tab-width: 4 -*-
2 *
3 * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18#include "stdafx.h"
19#include "PrinterSetupWizardApp.h"
20#include "PrinterSetupWizardSheet.h"
21#include "CommonServices.h"
22#include "DebugServices.h"
23#include "WinServices.h"
24#include "About.h"
25#include "tcpxcv.h"
26#include <winspool.h>
27#include <string>
28#include <shlwapi.h>
29
30// unreachable code
31#pragma warning(disable:4702)
32
33
34#if( !TARGET_OS_WINDOWS_CE )
35#	include	<mswsock.h>
36#	include	<process.h>
37#endif
38
39
40#if defined( UNICODE ) || defined( _UNICODE )
41#	define GetEnv	_wgetenv
42#else
43#	define GetEnv	getenv
44#endif
45
46static TCHAR*
47g_printerDriverFiles[] =		// Printer driver files
48{
49	TEXT( "ps5ui.dll" ),
50	TEXT( "pscript.hlp" ),
51	TEXT( "pscript.ntf" ),
52	TEXT( "pscript5.dll" ),
53	TEXT( "cups6.ini" ),
54	TEXT( "cupsui6.dll" ),
55	TEXT( "cupsps6.dll" )
56};
57
58
59// Private Messages
60
61#define WM_SOCKET_EVENT		( WM_USER + 0x100 )
62#define WM_PROCESS_EVENT	( WM_USER + 0x101 )
63
64
65static BOOL
66Is64BitWindows()
67{
68#if defined(_WIN64)
69	return TRUE;  // 64-bit programs run only on Win64
70#else
71	typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS)( HANDLE, PBOOL );
72	LPFN_ISWOW64PROCESS fnIsWow64Process;
73	BOOL bIsWow64 = FALSE;
74
75    fnIsWow64Process = ( LPFN_ISWOW64PROCESS ) GetProcAddress( GetModuleHandle( TEXT( "kernel32" ) ), "IsWow64Process" );
76
77    if ( fnIsWow64Process != NULL )
78    {
79		BOOL ok;
80
81        ok = fnIsWow64Process( GetCurrentProcess(), &bIsWow64 );
82
83		if ( !ok )
84		{
85			bIsWow64 = FALSE;
86		}
87	}
88
89	return bIsWow64;
90#endif
91}
92
93
94// CPrinterSetupWizardSheet
95CPrinterSetupWizardSheet * CPrinterSetupWizardSheet::m_self;
96
97IMPLEMENT_DYNAMIC(CPrinterSetupWizardSheet, CPropertySheet)
98CPrinterSetupWizardSheet::CPrinterSetupWizardSheet(UINT nIDCaption, CWnd* pParentWnd, UINT iSelectPage)
99	:CPropertySheet(nIDCaption, pParentWnd, iSelectPage),
100	m_selectedPrinter(NULL),
101	m_driverThreadExitCode( 0 ),
102	m_driverThreadFinished( false ),
103	m_pdlBrowser( NULL ),
104	m_ippBrowser( NULL ),
105	m_lprBrowser( NULL ),
106	m_lastPage( NULL )
107{
108	m_arrow		=	LoadCursor(0, IDC_ARROW);
109	m_wait		=	LoadCursor(0, IDC_APPSTARTING);
110	m_active	=	m_arrow;
111	m_self		=	this;
112
113	Init();
114
115	LoadPrinterNames();
116}
117
118
119CPrinterSetupWizardSheet::~CPrinterSetupWizardSheet()
120{
121	Printer * printer;
122
123	while ( m_printers.size() > 0 )
124	{
125		printer = m_printers.front();
126		m_printers.pop_front();
127
128		delete printer;
129	}
130
131	m_self = NULL;
132}
133
134
135// ------------------------------------------------------
136// SetSelectedPrinter
137//
138// Manages setting a printer as the printer to install.  Stops
139// any pending resolves.
140//
141void
142CPrinterSetupWizardSheet::SetSelectedPrinter(Printer * printer)
143{
144	check( !printer || ( printer != m_selectedPrinter ) );
145
146	m_selectedPrinter = printer;
147}
148
149
150OSStatus
151CPrinterSetupWizardSheet::LoadPrinterNames()
152{
153	PBYTE		buffer	=	NULL;
154	OSStatus	err		= 0;
155
156	//
157	// rdar://problem/3701926 - Printer can't be installed twice
158	//
159	// First thing we want to do is make sure the printer isn't already installed.
160	// If the printer name is found, we'll try and rename it until we
161	// find a unique name
162	//
163	DWORD dwNeeded = 0, dwNumPrinters = 0;
164
165	BOOL ok = EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 4, NULL, 0, &dwNeeded, &dwNumPrinters);
166	err = translate_errno( ok, errno_compat(), kUnknownErr );
167
168	if ((err == ERROR_INSUFFICIENT_BUFFER) && (dwNeeded > 0))
169	{
170		try
171		{
172			buffer = new unsigned char[dwNeeded];
173		}
174		catch (...)
175		{
176			buffer = NULL;
177		}
178
179		require_action( buffer, exit, kNoMemoryErr );
180		ok = EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 4, buffer, dwNeeded, &dwNeeded, &dwNumPrinters);
181		err = translate_errno( ok, errno_compat(), kUnknownErr );
182		require_noerr( err, exit );
183
184		for (DWORD index = 0; index < dwNumPrinters; index++)
185		{
186			PRINTER_INFO_4 * lppi4 = (PRINTER_INFO_4*) (buffer + index * sizeof(PRINTER_INFO_4));
187
188			m_printerNames.push_back( lppi4->pPrinterName );
189		}
190	}
191
192exit:
193
194	if (buffer != NULL)
195	{
196		delete [] buffer;
197	}
198
199	return err;
200}
201
202
203
204// ------------------------------------------------------
205// InstallPrinter
206//
207// Installs a printer with Windows.
208//
209// Note: this works one of two ways, depending on whether
210// there are drivers already installed for this printer.
211// If there are, then we can just create a port with XcvData,
212// and then call AddPrinter.  If not, we use the printui.dll
213// to install the printer. Actually installing drivers that
214// are not currently installed is painful, and it's much
215// easier and less error prone to just let printui.dll do
216// the hard work for us.
217//
218
219OSStatus
220CPrinterSetupWizardSheet::InstallPrinter(Printer * printer)
221{
222	Logger		log;
223	CUPSLibrary	cupsLib;
224	Service	*	service		= NULL;
225	BOOL		ok;
226	OSStatus	err = 0;
227
228	service = printer->services.front();
229	check( service );
230
231	if ( printer->isCUPSPrinter && cupsLib.IsInstalled() )
232	{
233		err = InstallPrinterCUPS( printer, service, cupsLib );
234		require_noerr( err, exit );
235	}
236	else
237	{
238		//
239		// if the driver isn't installed, then install it
240		//
241
242		if ( !printer->driverInstalled )
243		{
244			DWORD		dwResult;
245			HANDLE		hThread;
246			unsigned	threadID;
247
248			m_driverThreadFinished = false;
249
250			//
251			// create the thread
252			//
253			hThread = (HANDLE) _beginthreadex_compat( NULL, 0, InstallDriverThread, printer, 0, &threadID );
254			err = translate_errno( hThread, (OSStatus) GetLastError(), kUnknownErr );
255			require_noerr_with_log( log, "_beginthreadex_compat()", err, exit );
256
257			//
258			// go modal
259			//
260			while (!m_driverThreadFinished)
261			{
262				MSG msg;
263
264				GetMessage( &msg, m_hWnd, 0, 0 );
265				TranslateMessage(&msg);
266				DispatchMessage(&msg);
267			}
268
269			//
270			// Wait until child process exits.
271			//
272			dwResult = WaitForSingleObject( hThread, INFINITE );
273			err = translate_errno( dwResult == WAIT_OBJECT_0, errno_compat(), err = kUnknownErr );
274			require_noerr_with_log( log, "WaitForSingleObject()", err, exit );
275
276			//
277			// check the return value of thread
278			//
279			require_noerr_with_log( log, "thread exit code", m_driverThreadExitCode, exit );
280
281			//
282			// now we know that the driver was successfully installed
283			//
284			printer->driverInstalled = true;
285		}
286
287		if ( service->type == kPDLServiceType )
288		{
289			err = InstallPrinterPort( printer, service, PROTOCOL_RAWTCP_TYPE, log );
290			require_noerr_with_log( log, "InstallPrinterPort()", err, exit );
291			err = InstallPrinterPDLAndLPR( printer, service, log );
292			require_noerr_with_log( log, "InstallPrinterPDLAndLPR()", err, exit );
293		}
294		else if ( service->type == kLPRServiceType )
295		{
296			err = InstallPrinterPort( printer, service, PROTOCOL_LPR_TYPE, log );
297			require_noerr_with_log( log, "InstallPrinterPort()", err, exit );
298			err = InstallPrinterPDLAndLPR( printer, service, log );
299			require_noerr_with_log( log, "InstallPrinterPDLAndLPR()", err, exit );
300		}
301		else if ( service->type == kIPPServiceType )
302		{
303			// There's no need to install a printer port for IPP printers, because
304			// the call to AddPrinter() will do that for us.
305
306			err = InstallPrinterIPP( printer, service, log );
307			require_noerr_with_log( log, "InstallPrinterIPP()", err, exit );
308		}
309		else
310		{
311			require_action_with_log( log, ( service->type == kPDLServiceType ) || ( service->type == kLPRServiceType ) || ( service->type == kIPPServiceType ), exit, err = kUnknownErr );
312		}
313	}
314
315	printer->installed = true;
316
317	//
318	// if the user specified a default printer, set it
319	//
320	if (printer->deflt)
321	{
322		ok = SetDefaultPrinter( printer->actualName );
323		err = translate_errno( ok, errno_compat(), err = kUnknownErr );
324		require_noerr_with_log( log, "SetDefaultPrinter()", err, exit );
325	}
326
327exit:
328
329	return err;
330}
331
332
333OSStatus
334CPrinterSetupWizardSheet::InstallPrinterPort( Printer * printer, Service * service, DWORD protocol, Logger & log )
335{
336	PRINTER_DEFAULTS	printerDefaults =	{ NULL,  NULL, SERVER_ACCESS_ADMINISTER };
337	PORT_DATA_1			portData;
338	DWORD				dwStatus;
339	DWORD				cbInputData		=	100;
340	PBYTE				pOutputData		=	NULL;
341	DWORD				cbOutputNeeded	=	0;
342	HANDLE				hXcv			=	NULL;
343	Queue			*	q;
344	BOOL				ok;
345	OSStatus			err;
346
347	ZeroMemory(&portData, sizeof(PORT_DATA_1));
348
349	require_action_with_log( log, wcslen(printer->portName) < sizeof_array(portData.sztPortName), exit, err = kSizeErr );
350	wcscpy_s(portData.sztPortName, printer->portName);
351
352	q = service->queues.front();
353	check( q );
354
355	ok = OpenPrinter(L",XcvMonitor Standard TCP/IP Port", &hXcv, &printerDefaults);
356	err = translate_errno( ok, errno_compat(), kUnknownErr );
357	require_noerr_with_log( log, "OpenPrinter()", err, exit );
358
359	//
360	// BUGBUG: MSDN said this is not required, but my experience shows it is required
361	//
362	try
363	{
364		pOutputData = new BYTE[cbInputData];
365	}
366	catch (...)
367	{
368		pOutputData = NULL;
369	}
370
371	require_action_with_log( log, pOutputData, exit, err = kNoMemoryErr );
372
373	portData.dwPortNumber	=	service->portNumber;
374	portData.dwVersion		=	1;
375	portData.dwDoubleSpool	=	1;
376
377	portData.dwProtocol	= protocol;
378	portData.cbSize		= sizeof PORT_DATA_1;
379	portData.dwReserved	= 0L;
380
381	require_action_with_log( log, wcslen(q->name) < sizeof_array(portData.sztQueue), exit, err = kSizeErr );
382	wcscpy_s(portData.sztQueue, q->name);
383
384	require_action_with_log( log, wcslen( service->hostname ) < sizeof_array(portData.sztHostAddress), exit, err = kSizeErr );
385	wcscpy_s( portData.sztHostAddress, service->hostname );
386
387	ok = XcvData(hXcv, L"AddPort", (PBYTE) &portData, sizeof(PORT_DATA_1), pOutputData, cbInputData,  &cbOutputNeeded, &dwStatus);
388	err = translate_errno( ok, errno_compat(), kUnknownErr );
389	require_noerr_with_log( log, "XcvData()", err, exit );
390
391exit:
392
393	if (hXcv != NULL)
394	{
395		ClosePrinter(hXcv);
396	}
397
398	if (pOutputData != NULL)
399	{
400		delete [] pOutputData;
401	}
402
403	return err;
404}
405
406
407OSStatus
408CPrinterSetupWizardSheet::InstallPrinterPDLAndLPR(Printer * printer, Service * service, Logger & log )
409{
410	PRINTER_INFO_2		pInfo;
411	HANDLE				hPrinter = NULL;
412	Queue			*	q;
413	OSStatus			err;
414
415	check(printer != NULL);
416	check(printer->installed == false);
417
418	q = service->queues.front();
419	check( q );
420
421	//
422	// add the printer
423	//
424	ZeroMemory(&pInfo, sizeof(pInfo));
425
426	pInfo.pPrinterName			=	printer->actualName.GetBuffer();
427	pInfo.pServerName			=	NULL;
428	pInfo.pShareName			=	NULL;
429	pInfo.pPortName				=	printer->portName.GetBuffer();
430	pInfo.pDriverName			=	printer->modelName.GetBuffer();
431	pInfo.pComment				=	printer->displayModelName.GetBuffer();
432	pInfo.pLocation				=	q->location.GetBuffer();
433	pInfo.pDevMode				=	NULL;
434	pInfo.pDevMode				=	NULL;
435	pInfo.pSepFile				=	L"";
436	pInfo.pPrintProcessor		=	L"winprint";
437	pInfo.pDatatype				=	L"RAW";
438	pInfo.pParameters			=	L"";
439	pInfo.pSecurityDescriptor	=	NULL;
440	pInfo.Attributes			=	PRINTER_ATTRIBUTE_QUEUED;
441	pInfo.Priority				=	0;
442	pInfo.DefaultPriority		=	0;
443	pInfo.StartTime				=	0;
444	pInfo.UntilTime				=	0;
445
446	hPrinter = AddPrinter(NULL, 2, (LPBYTE) &pInfo);
447	err = translate_errno( hPrinter, errno_compat(), kUnknownErr );
448	require_noerr_with_log( log, "AddPrinter()", err, exit );
449
450exit:
451
452	if (hPrinter != NULL)
453	{
454		ClosePrinter(hPrinter);
455	}
456
457	return err;
458}
459
460
461OSStatus
462CPrinterSetupWizardSheet::InstallPrinterIPP(Printer * printer, Service * service, Logger & log)
463{
464	DEBUG_UNUSED( service );
465
466	Queue		*	q		 = service->SelectedQueue();
467	HANDLE			hPrinter = NULL;
468	PRINTER_INFO_2	pInfo;
469	OSStatus		err;
470
471	check( q );
472
473	//
474	// add the printer
475	//
476	ZeroMemory(&pInfo, sizeof(PRINTER_INFO_2));
477
478	pInfo.pPrinterName		= printer->actualName.GetBuffer();
479	pInfo.pPortName			= printer->portName.GetBuffer();
480	pInfo.pDriverName		= printer->modelName.GetBuffer();
481	pInfo.pPrintProcessor	= L"winprint";
482	pInfo.pLocation			= q->location.GetBuffer();
483	pInfo.pComment			= printer->displayModelName.GetBuffer();
484	pInfo.Attributes		= PRINTER_ATTRIBUTE_NETWORK | PRINTER_ATTRIBUTE_LOCAL;
485
486	hPrinter = AddPrinter(NULL, 2, (LPBYTE)&pInfo);
487	err = translate_errno( hPrinter, errno_compat(), kUnknownErr );
488	require_noerr_with_log( log, "AddPrinter()", err, exit );
489
490exit:
491
492	if ( hPrinter != NULL )
493	{
494		ClosePrinter(hPrinter);
495	}
496
497	return err;
498}
499
500
501OSStatus
502CPrinterSetupWizardSheet::InstallPrinterCUPS(Printer * printer, Service * service, CUPSLibrary & cupsLib )
503{
504	OSStatus err = kNoErr;
505
506	check( printer );
507	check( service );
508	check( cupsLib.IsInstalled() );
509
510	err = InstallPrinterCUPS( printer, service, cupsLib, TEXT( "Windows NT x86" ) );
511	require_noerr( err, exit );
512
513	if ( Is64BitWindows() )
514	{
515		err = InstallPrinterCUPS( printer, service, cupsLib, TEXT( "Windows x64" ) );
516		require_noerr( err, exit );
517	}
518
519exit:
520
521	return err;
522}
523
524
525OSStatus
526CPrinterSetupWizardSheet::InstallPrinterCUPS(Printer * printer, Service * service, CUPSLibrary & cupsLib, TCHAR * env )
527{
528
529	Queue		*	q;
530	CString			ppdfile;				// PPD file for printer drivers
531	TCHAR			driverdir[1024];		// Directory for driver files
532	DWORD			needed;					// Bytes needed
533	DRIVER_INFO_3	driverinfo;				// Driver information
534	PRINTER_INFO_2	printerinfo;			// Printer information
535	HANDLE			printerHandle = NULL;	// Handle to printer
536	CString			filename;				// Driver filename
537	CString			dependentFiles;			// List of dependent files
538	CString			portName;				// Port Name
539	int				bytes;					// Bytes copied
540	TCHAR			datadir[ MAX_PATH ];	// Driver files location
541	CFile			in;						// Input file
542	CFile			out;					// Output file
543	void		*	http;					// Connection to server
544	char			buffer[4096];			// Copy/error buffer
545	CString			platform;
546	char			hostname[ 1024 ];
547	CString			dest;
548	char			destANSI[ 1024 ];
549	int				i;
550	DWORD			num;
551	OSStatus		err	= 0;
552	BOOL			ok;
553
554	check( printer );
555	check( service );
556	check( cupsLib.IsInstalled() );
557	check( env );
558
559	// What do we do here for multiple queues?
560	q = service->queues.front();
561	require_action( q != NULL, exit, err = kUnknownErr );
562
563	num = GetModuleFileName( NULL, datadir, MAX_PATH );
564	err = translate_errno( num > 0, GetLastError(), kUnknownErr );
565	require_noerr( err, exit );
566	ok = PathRemoveFileSpec( datadir );
567	require_action( ok, exit, err = kUnknownErr );
568
569	ok = GetPrinterDriverDirectory(NULL, env, 1, ( LPBYTE ) driverdir, sizeof( driverdir ), &needed );
570	err = translate_errno( ok, GetLastError(), kUnknownErr );
571	require_noerr( err, exit );
572
573	platform = env;
574	platform = platform.Right( 3 );
575
576	// Append the supported banner pages to the PPD file...
577	err = StringObjectToUTF8String( service->hostname, hostname, sizeof( hostname ) );
578	require_noerr( err, exit );
579	http = cupsLib.httpConnectEncrypt( hostname, service->portNumber, cupsLib.cupsEncryption() );
580	err = translate_errno( http != NULL, errno, kUnknownErr );
581	require_noerr( err, exit );
582
583	if ( ( service->portNumber == 443 ) || ( cupsLib.cupsEncryption() >= HTTP_ENCRYPT_REQUIRED ) )
584	{
585		// This forces the use the https: URLs below...
586		cupsLib.cupsSetEncryption( HTTP_ENCRYPT_ALWAYS );
587	}
588
589	// Strip the leading "printers/" or "classes/" from the beginning
590	// of the name
591
592	dest = q->name;
593	dest.Replace( TEXT( "printers/" ), TEXT( "" ) );
594	dest.Replace( TEXT( "classes/" ), TEXT( "" ) );
595
596	err = StringObjectToUTF8String( dest, destANSI, sizeof( destANSI ) );
597	require_noerr( err, exit );
598
599	// Get the PPD file...
600	for ( i = 0; i < 10; i++ )
601	{
602		char ppdfileANSI[ 1024 ];
603
604		if ( cupsLib.cupsAdminCreateWindowsPPD( http, destANSI, ppdfileANSI, sizeof( ppdfileANSI ) ) )
605		{
606			err = UTF8StringToStringObject( ppdfileANSI, ppdfile );
607			require_noerr( err, exit );
608			break;
609		}
610	}
611
612	err = translate_errno( i < 10, errno, kUnknownErr );
613	require_noerr( err, exit );
614
615	// Copy the PPD file to the Windows driver directory...
616	filename.Format( TEXT( "%s/%s.ppd" ), driverdir, dest );
617
618	ok = in.Open( ppdfile, CFile::modeRead | CFile::typeBinary );
619	translate_errno( ok, GetLastError(), kUnknownErr );
620	require_noerr( err, exit );
621
622	ok = out.Open( filename, CFile::modeCreate | CFile::modeWrite | CFile::typeBinary );
623	translate_errno( ok, GetLastError(), kUnknownErr );
624	require_noerr( err, exit );
625
626	while ( ( bytes = in.Read( buffer, sizeof(buffer) ) ) > 0 )
627	{
628		out.Write(buffer, bytes );
629	}
630
631	in.Close();
632	out.Close();
633
634	// Cleanup temp file...
635	CFile::Remove( ppdfile );
636
637	// Copy the driver files to the driver directory...
638	for ( i = 0; i < ( sizeof( g_printerDriverFiles ) / sizeof( g_printerDriverFiles[0] ) ); i++ )
639	{
640		filename.Format( TEXT( "%s/drivers/%s/%s" ), datadir, platform, g_printerDriverFiles[i]);
641
642		ok = in.Open(filename, CFile::modeRead | CFile::typeBinary );
643		err = translate_errno( ok, GetLastError(), kUnknownErr );
644		require_noerr( err, exit );
645
646		filename.Format( TEXT( "%s/%s" ), driverdir, g_printerDriverFiles[i] );
647		ok = out.Open(filename, CFile::modeCreate | CFile::modeWrite | CFile::typeBinary );
648		err = translate_errno( ok, errno, kUnknownErr );
649
650		while ( ( bytes = in.Read(buffer, sizeof( buffer ) ) ) > 0 )
651		{
652			out.Write( buffer, bytes );
653		}
654
655		in.Close();
656		out.Close();
657	}
658
659	// Do the Windows system calls needed to add the printer driver...
660	filename.Format( TEXT( "%s.ppd" ), dest);
661	dependentFiles.Format( TEXT( "pscript5.dll%c" ) TEXT( "%s.ppd%c" ) TEXT( "ps5ui.dll%c" ) TEXT( "pscript.hlp%c" ) TEXT( "pscript.ntf%c" ) TEXT( "cups6.ini%c" ) TEXT( "cupsps6.dll%c" ) TEXT( "cupsui6.dll%c" ), 0, dest, 0, 0, 0, 0, 0, 0, 0);
662
663	driverinfo.cVersion         = 3;
664	driverinfo.pName            = printer->actualName.GetBuffer();
665	driverinfo.pEnvironment     = env;
666	driverinfo.pDriverPath      = TEXT( "pscript5.dll" );
667	driverinfo.pDataFile        = filename.GetBuffer();
668	driverinfo.pConfigFile      = TEXT( "ps5ui.dll" );
669	driverinfo.pHelpFile        = TEXT( "pscript.hlp" );
670	driverinfo.pDependentFiles  = dependentFiles.GetBuffer();
671	driverinfo.pMonitorName     = NULL;
672	driverinfo.pDefaultDataType = TEXT( "raw" );
673
674	ok = AddPrinterDriverEx(NULL, 3, (LPBYTE) &driverinfo, APD_COPY_ALL_FILES );
675	err = translate_errno( ok, GetLastError(), kUnknownErr );
676	require_noerr( err, exit );
677
678	// See if the printer has already been added?
679	if ( OpenPrinter( printer->actualName.GetBuffer(), &printerHandle, NULL ) )
680    {
681		// Printer already exists, so we are done now...
682		goto exit;
683    }
684
685    // Add the printer using the HTTP/IPP port...
686	portName.Format( TEXT( "%s://%s:%d/printers/%s" ), cupsLib.cupsEncryption() == HTTP_ENCRYPT_ALWAYS ? TEXT( "https" ) : TEXT( "http" ), service->hostname.GetBuffer(), service->portNumber, dest );
687
688    memset(&printerinfo, 0, sizeof(printerinfo));
689    printerinfo.pPrinterName	= printer->actualName.GetBuffer();
690    printerinfo.pPortName		= portName.GetBuffer();
691    printerinfo.pDriverName		= printer->actualName.GetBuffer();
692    printerinfo.Attributes		= PRINTER_ATTRIBUTE_NETWORK | PRINTER_ATTRIBUTE_LOCAL;
693	printerinfo.pComment		= q->description.GetBuffer();
694	printerinfo.pLocation		= q->location.GetBuffer();
695	printerinfo.pPrintProcessor = TEXT( "winprint" );
696
697    printerHandle = AddPrinter( NULL, 2, (LPBYTE) &printerinfo );
698	err = translate_errno( printerHandle, GetLastError(), kUnknownErr );
699	require_noerr( err, exit );
700
701exit:
702
703	if ( printerHandle != NULL )
704	{
705		ClosePrinter( printerHandle );
706		printerHandle = NULL;
707	}
708
709	return err;
710}
711
712BEGIN_MESSAGE_MAP(CPrinterSetupWizardSheet, CPropertySheet)
713ON_MESSAGE( WM_SOCKET_EVENT, OnSocketEvent )
714ON_MESSAGE( WM_PROCESS_EVENT, OnProcessEvent )
715ON_WM_SETCURSOR()
716ON_WM_TIMER()
717END_MESSAGE_MAP()
718
719
720// ------------------------------------------------------
721// OnCommand
722//
723// Traps when the user hits Finish
724//
725BOOL CPrinterSetupWizardSheet::OnCommand(WPARAM wParam, LPARAM lParam)
726{
727	//
728	// Check if this is OK
729	//
730	if (wParam == ID_WIZFINISH)              // If OK is hit...
731	{
732		OnOK();
733	}
734
735	return CPropertySheet::OnCommand(wParam, lParam);
736}
737
738
739// ------------------------------------------------------
740// OnInitDialog
741//
742// Initializes this Dialog object.
743//
744BOOL CPrinterSetupWizardSheet::OnInitDialog()
745{
746	OSStatus err;
747
748	CPropertySheet::OnInitDialog();
749
750	err = StartBrowse();
751	require_noerr( err, exit );
752
753exit:
754
755	if ( err )
756	{
757		StopBrowse();
758
759		if ( err == kDNSServiceErr_Firewall )
760		{
761			CString text, caption;
762
763			text.LoadString( IDS_FIREWALL );
764			caption.LoadString( IDS_FIREWALL_CAPTION );
765
766			MessageBox(text, caption, MB_OK|MB_ICONEXCLAMATION);
767		}
768		else
769		{
770			CString text, caption;
771
772			text.LoadString( IDS_NO_MDNSRESPONDER_SERVICE_TEXT );
773			caption.LoadString( IDS_ERROR_CAPTION );
774
775			MessageBox(text, caption, MB_OK|MB_ICONEXCLAMATION);
776
777			_exit( 0 );
778		}
779	}
780
781	return TRUE;
782}
783
784
785// ------------------------------------------------------
786// OnSetCursor
787//
788// This is called when Windows wants to know what cursor
789// to display.  So we tell it.
790//
791BOOL
792CPrinterSetupWizardSheet::OnSetCursor(CWnd * pWnd, UINT nHitTest, UINT message)
793{
794	DEBUG_UNUSED(pWnd);
795	DEBUG_UNUSED(nHitTest);
796	DEBUG_UNUSED(message);
797
798	SetCursor(m_active);
799	return TRUE;
800}
801
802
803// ------------------------------------------------------
804// OnContextMenu
805//
806// This is not fully implemented yet.
807//
808
809void
810CPrinterSetupWizardSheet::OnContextMenu(CWnd * pWnd, CPoint pos)
811{
812	DEBUG_UNUSED(pWnd);
813	DEBUG_UNUSED(pos);
814
815	CAbout dlg;
816
817	dlg.DoModal();
818}
819
820
821// ------------------------------------------------------
822// OnOK
823//
824// This is called when the user hits the "Finish" button
825//
826void
827CPrinterSetupWizardSheet::OnOK()
828{
829	CWnd * window;
830	OSStatus err;
831
832	check ( m_selectedPrinter != NULL );
833
834	SetWizardButtons( PSWIZB_DISABLEDFINISH );
835
836	window = GetDlgItem( IDCANCEL );
837
838	if ( window )
839	{
840		window->EnableWindow( FALSE );
841	}
842
843	m_pgFourth.StartActivityIndicator();
844
845	err = InstallPrinter( m_selectedPrinter );
846
847	m_pgFourth.StopActivityIndicator();
848
849	if ( err != kNoErr )
850	{
851		CString caption;
852		CString message;
853
854		caption.LoadString(IDS_INSTALL_ERROR_CAPTION);
855		caption.AppendFormat( TEXT( " (%d)" ), err );
856		message.LoadString(IDS_INSTALL_ERROR_MESSAGE);
857		MessageBox(message, caption, MB_OK|MB_ICONEXCLAMATION);
858	}
859
860	StopBrowse();
861}
862
863
864// CPrinterSetupWizardSheet message handlers
865
866void CPrinterSetupWizardSheet::Init(void)
867{
868	AddPage(&m_pgSecond);
869	AddPage(&m_pgThird);
870	AddPage(&m_pgFourth);
871
872	m_psh.dwFlags &= (~PSH_HASHELP);
873
874	m_psh.dwFlags |= PSH_WIZARD97|PSH_WATERMARK|PSH_HEADER;
875	m_psh.pszbmWatermark = MAKEINTRESOURCE(IDB_WATERMARK);
876	m_psh.pszbmHeader = MAKEINTRESOURCE(IDB_BANNER_ICON);
877
878	m_psh.hInstance = GetNonLocalizedResources();
879
880	SetWizardMode();
881}
882
883
884LRESULT
885CPrinterSetupWizardSheet::OnSocketEvent(WPARAM inWParam, LPARAM inLParam)
886{
887	if (WSAGETSELECTERROR(inLParam) && !(HIWORD(inLParam)))
888    {
889		dlog( kDebugLevelError, "OnServiceEvent: window error\n" );
890    }
891    else
892    {
893		SOCKET sock = (SOCKET) inWParam;
894
895		// iterate thru list
896		ServiceRefList::iterator begin = m_serviceRefList.begin();
897		ServiceRefList::iterator end   = m_serviceRefList.end();
898
899		while (begin != end)
900		{
901			DNSServiceRef ref = *begin++;
902
903			check(ref != NULL);
904
905			if ((SOCKET) DNSServiceRefSockFD(ref) == sock)
906			{
907				DNSServiceProcessResult(ref);
908				break;
909			}
910		}
911	}
912
913	return ( 0 );
914}
915
916
917LRESULT
918CPrinterSetupWizardSheet::OnProcessEvent(WPARAM inWParam, LPARAM inLParam)
919{
920	DEBUG_UNUSED(inLParam);
921
922	m_driverThreadExitCode	=	(DWORD) inWParam;
923	m_driverThreadFinished	=	true;
924
925	return 0;
926}
927
928
929unsigned WINAPI
930CPrinterSetupWizardSheet::InstallDriverThread( LPVOID inParam )
931{
932	Printer			*	printer = (Printer*) inParam;
933	DWORD				exitCode = 0;
934	DWORD				dwResult;
935	OSStatus			err;
936	STARTUPINFO			si;
937	PROCESS_INFORMATION pi;
938	BOOL				ok;
939
940	check( printer );
941	check( m_self );
942
943	//
944	// because we're calling endthreadex(), C++ objects won't be cleaned up
945	// correctly.  we'll nest the CString 'command' inside a block so
946	// that it's destructor will be invoked.
947	//
948	{
949		CString command;
950
951		ZeroMemory( &si, sizeof(si) );
952		si.cb = sizeof(si);
953		ZeroMemory( &pi, sizeof(pi) );
954
955		command.Format(L"rundll32.exe printui.dll,PrintUIEntry /ia /m \"%s\" /f \"%s\"", (LPCTSTR) printer->modelName, (LPCTSTR) printer->infFileName );
956
957		ok = CreateProcess(NULL, command.GetBuffer(), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
958		err = translate_errno( ok, errno_compat(), kUnknownErr );
959		require_noerr( err, exit );
960
961		dwResult = WaitForSingleObject( pi.hProcess, INFINITE );
962		translate_errno( dwResult == WAIT_OBJECT_0, errno_compat(), err = kUnknownErr );
963		require_noerr( err, exit );
964
965		ok = GetExitCodeProcess( pi.hProcess, &exitCode );
966		err = translate_errno( ok, errno_compat(), kUnknownErr );
967		require_noerr( err, exit );
968	}
969
970exit:
971
972	//
973	// Close process and thread handles.
974	//
975	if ( pi.hProcess )
976	{
977		CloseHandle( pi.hProcess );
978	}
979
980	if ( pi.hThread )
981	{
982		CloseHandle( pi.hThread );
983	}
984
985	//
986	// alert the main thread
987	//
988	m_self->PostMessage( WM_PROCESS_EVENT, err, exitCode );
989
990	_endthreadex_compat( 0 );
991
992	return 0;
993}
994
995
996void DNSSD_API
997CPrinterSetupWizardSheet::OnBrowse(
998							DNSServiceRef 			inRef,
999							DNSServiceFlags 		inFlags,
1000							uint32_t 				inInterfaceIndex,
1001							DNSServiceErrorType 	inErrorCode,
1002							const char *			inName,
1003							const char *			inType,
1004							const char *			inDomain,
1005							void *					inContext )
1006{
1007	DEBUG_UNUSED(inRef);
1008
1009	CPrinterSetupWizardSheet	*	self;
1010	bool							moreComing = (bool) (inFlags & kDNSServiceFlagsMoreComing);
1011	CPropertyPage				*	active;
1012	Printer						*	printer = NULL;
1013	Service						*	service = NULL;
1014	OSStatus						err = kNoErr;
1015
1016	require_noerr( inErrorCode, exit );
1017
1018	self = reinterpret_cast <CPrinterSetupWizardSheet*>( inContext );
1019	require_quiet( self, exit );
1020
1021	active = self->GetActivePage();
1022	require_quiet( active, exit );
1023
1024	// Have we seen this printer before?
1025
1026	printer = self->Lookup( inName );
1027
1028	if ( printer )
1029	{
1030		service = printer->LookupService( inType );
1031	}
1032
1033	if ( inFlags & kDNSServiceFlagsAdd )
1034	{
1035		BOOL newPrinter = FALSE;
1036
1037		if ( !printer )
1038		{
1039			printer = self->OnAddPrinter( inInterfaceIndex, inName, inType, inDomain, moreComing );
1040			require_action( printer, exit, err = kUnknownErr );
1041			newPrinter = TRUE;
1042		}
1043
1044		// If we're looking at the browse list on page 2, then we need to call
1045		// CPage2::OnAddPrinter() regardless of whether we've seen the printer
1046		// or not because the moreComing flag might have changed from a previous
1047		// call. If we only call CPage2::OnAddPrinter() when there's a new printer,
1048		// we might not correctly update our UI, so if we've seen the printer before,
1049		// call OnAddPrinter with a NULL parameter.
1050
1051		if ( self->GetActivePage() == &self->m_pgSecond )
1052		{
1053			self->m_pgSecond.OnAddPrinter( newPrinter ? printer : NULL, moreComing );
1054		}
1055
1056		if ( !service )
1057		{
1058			err = self->OnAddService( printer, inInterfaceIndex, inName, inType, inDomain );
1059			require_noerr( err, exit );
1060		}
1061		else
1062		{
1063			service->refs++;
1064		}
1065	}
1066	else if ( printer )
1067	{
1068		check( service );
1069
1070		err = self->OnRemoveService( service );
1071		require_noerr( err, exit );
1072
1073		if ( printer->services.size() == 0 )
1074		{
1075			err = self->OnRemovePrinter( printer, moreComing );
1076			require_noerr( err, exit );
1077		}
1078	}
1079
1080exit:
1081
1082	return;
1083}
1084
1085
1086void DNSSD_API
1087CPrinterSetupWizardSheet::OnResolve(
1088								DNSServiceRef			inRef,
1089								DNSServiceFlags			inFlags,
1090								uint32_t				inInterfaceIndex,
1091								DNSServiceErrorType		inErrorCode,
1092								const char *			inFullName,
1093								const char *			inHostName,
1094								uint16_t 				inPort,
1095								uint16_t 				inTXTSize,
1096								const char *			inTXT,
1097								void *					inContext )
1098{
1099	DEBUG_UNUSED(inFullName);
1100	DEBUG_UNUSED(inInterfaceIndex);
1101	DEBUG_UNUSED(inFlags);
1102	DEBUG_UNUSED(inRef);
1103
1104	CPrinterSetupWizardSheet	*	self;
1105	Service						*	service;
1106	Queue						*	q;
1107	int								idx;
1108	OSStatus						err;
1109
1110	require_noerr( inErrorCode, exit );
1111
1112	service = reinterpret_cast<Service*>( inContext );
1113	require_quiet( service, exit);
1114
1115	check( service->refs != 0 );
1116
1117	self = service->printer->window;
1118	require_quiet( self, exit );
1119
1120	err = self->StopOperation( service->serviceRef );
1121	require_noerr( err, exit );
1122
1123	//
1124	// hold on to the hostname...
1125	//
1126	err = UTF8StringToStringObject( inHostName, service->hostname );
1127	require_noerr( err, exit );
1128
1129	//
1130	// <rdar://problem/3739200> remove the trailing dot on hostname
1131	//
1132	idx = service->hostname.ReverseFind('.');
1133
1134	if ((idx > 1) && ((service->hostname.GetLength() - 1) == idx))
1135	{
1136		service->hostname.Delete(idx, 1);
1137	}
1138
1139	//
1140	// hold on to the port
1141	//
1142	service->portNumber = ntohs(inPort);
1143
1144	if ( service->qtotal == 1 )
1145	{
1146		//
1147		// create a new queue
1148		//
1149		try
1150		{
1151			q = new Queue;
1152		}
1153		catch (...)
1154		{
1155			q = NULL;
1156		}
1157
1158		require_action( q, exit, err = E_OUTOFMEMORY );
1159
1160		//
1161		// parse the text record.
1162		//
1163
1164		err = self->ParseTextRecord( service, q, inTXTSize, inTXT );
1165		require_noerr( err, exit );
1166
1167		service->queues.push_back( q );
1168
1169		//
1170		// we've completely resolved this service
1171		//
1172
1173		self->OnResolveService( service );
1174	}
1175	else
1176	{
1177		//
1178		// if qtotal is more than 1, then we need to get additional
1179		// text records.  if not, then this service is considered
1180		// resolved
1181		//
1182
1183		err = DNSServiceQueryRecord(&service->serviceRef, 0, inInterfaceIndex, inFullName, kDNSServiceType_TXT, kDNSServiceClass_IN, OnQuery, (void*) service );
1184		require_noerr( err, exit );
1185
1186		err = self->StartOperation( service->serviceRef );
1187		require_noerr( err, exit );
1188	}
1189
1190exit:
1191
1192	return;
1193}
1194
1195
1196void DNSSD_API
1197CPrinterSetupWizardSheet::OnQuery(
1198							DNSServiceRef		inRef,
1199							DNSServiceFlags		inFlags,
1200							uint32_t			inInterfaceIndex,
1201							DNSServiceErrorType inErrorCode,
1202							const char		*	inFullName,
1203							uint16_t			inRRType,
1204							uint16_t			inRRClass,
1205							uint16_t			inRDLen,
1206							const void		*	inRData,
1207							uint32_t			inTTL,
1208							void			*	inContext)
1209{
1210	DEBUG_UNUSED( inTTL );
1211	DEBUG_UNUSED( inRRClass );
1212	DEBUG_UNUSED( inRRType );
1213	DEBUG_UNUSED( inFullName );
1214	DEBUG_UNUSED( inInterfaceIndex );
1215	DEBUG_UNUSED( inRef );
1216
1217	Service						*	service = NULL;
1218	Queue						*	q;
1219	CPrinterSetupWizardSheet	*	self;
1220	OSStatus						err = kNoErr;
1221
1222	require_noerr( inErrorCode, exit );
1223
1224	service = reinterpret_cast<Service*>( inContext );
1225	require_quiet( service, exit);
1226
1227	self = service->printer->window;
1228	require_quiet( self, exit );
1229
1230	if ( ( inFlags & kDNSServiceFlagsAdd ) && ( inRDLen > 0 ) && ( inRData != NULL ) )
1231	{
1232		const char * inTXT = ( const char * ) inRData;
1233
1234		//
1235		// create a new queue
1236		//
1237		try
1238		{
1239			q = new Queue;
1240		}
1241		catch (...)
1242		{
1243			q = NULL;
1244		}
1245
1246		require_action( q, exit, err = E_OUTOFMEMORY );
1247
1248		err = service->printer->window->ParseTextRecord( service, q, inRDLen, inTXT );
1249		require_noerr( err, exit );
1250
1251		//
1252		// add this queue
1253		//
1254
1255		service->queues.push_back( q );
1256
1257		if ( service->queues.size() == service->qtotal )
1258		{
1259			//
1260			// else if moreComing is not set, then we're going
1261			// to assume that we're done
1262			//
1263
1264			self->StopOperation( service->serviceRef );
1265
1266			//
1267			// sort the queues
1268			//
1269
1270			service->queues.sort( OrderQueueFunc );
1271
1272			//
1273			// we've completely resolved this service
1274			//
1275
1276			self->OnResolveService( service );
1277		}
1278	}
1279
1280exit:
1281
1282	if ( err && service && ( service->serviceRef != NULL ) )
1283	{
1284		service->printer->window->StopOperation( service->serviceRef );
1285	}
1286
1287	return;
1288}
1289
1290
1291Printer*
1292CPrinterSetupWizardSheet::OnAddPrinter(
1293								uint32_t 		inInterfaceIndex,
1294								const char *	inName,
1295								const char *	inType,
1296								const char *	inDomain,
1297								bool			moreComing)
1298{
1299	Printer	*	printer = NULL;
1300	DWORD		printerNameCount;
1301	OSStatus	err;
1302
1303	DEBUG_UNUSED( inInterfaceIndex );
1304	DEBUG_UNUSED( inType );
1305	DEBUG_UNUSED( inDomain );
1306	DEBUG_UNUSED( moreComing );
1307
1308	try
1309	{
1310		printer = new Printer;
1311	}
1312	catch (...)
1313	{
1314		printer = NULL;
1315	}
1316
1317	require_action( printer, exit, err = E_OUTOFMEMORY );
1318
1319	printer->window		=	this;
1320	printer->name		=	inName;
1321
1322	err = UTF8StringToStringObject(inName, printer->displayName);
1323	check_noerr( err );
1324	printer->actualName	=	printer->displayName;
1325	printer->installed	=	false;
1326	printer->deflt		=	false;
1327	printer->resolving	=	0;
1328
1329	// Compare this name against printers that are already installed
1330	// to avoid name clashes.  Rename as necessary
1331	// to come up with a unique name.
1332
1333	printerNameCount = 2;
1334
1335	for (;;)
1336	{
1337		CPrinterSetupWizardSheet::PrinterNames::iterator it;
1338
1339		// <rdar://problem/4141221> Don't use find to do comparisons because we need to
1340		// do a case insensitive string comparison
1341
1342		for ( it = m_printerNames.begin(); it != m_printerNames.end(); it++ )
1343		{
1344			if ( (*it).CompareNoCase( printer->actualName ) == 0 )
1345			{
1346				break;
1347			}
1348		}
1349
1350		if (it != m_printerNames.end())
1351		{
1352			printer->actualName.Format(L"%s (%d)", printer->displayName, printerNameCount);
1353		}
1354		else
1355		{
1356			break;
1357		}
1358
1359		printerNameCount++;
1360	}
1361
1362	m_printers.push_back( printer );
1363
1364exit:
1365
1366	return printer;
1367}
1368
1369
1370OSStatus
1371CPrinterSetupWizardSheet::OnAddService(
1372								Printer		*	printer,
1373								uint32_t 		inInterfaceIndex,
1374								const char	*	inName,
1375								const char	*	inType,
1376								const char	*	inDomain)
1377{
1378	Service	*	service = NULL;
1379	OSStatus	err     = kNoErr;
1380
1381	DEBUG_UNUSED( inName );
1382	DEBUG_UNUSED( inDomain );
1383
1384	try
1385	{
1386		service = new Service;
1387	}
1388	catch (...)
1389	{
1390		service = NULL;
1391	}
1392
1393	require_action( service, exit, err = E_OUTOFMEMORY );
1394
1395	service->printer	=	printer;
1396	service->ifi		=	inInterfaceIndex;
1397	service->type		=	inType;
1398	service->domain		=	inDomain;
1399	service->qtotal		=	1;
1400	service->refs		=	1;
1401	service->serviceRef	=	NULL;
1402
1403	printer->services.push_back( service );
1404
1405	//
1406	// if the printer is selected, then we'll want to start a
1407	// resolve on this guy
1408	//
1409
1410	if ( printer == m_selectedPrinter )
1411	{
1412		StartResolve( service );
1413	}
1414
1415exit:
1416
1417	return err;
1418}
1419
1420
1421OSStatus
1422CPrinterSetupWizardSheet::OnRemovePrinter( Printer * printer, bool moreComing )
1423{
1424	CPropertyPage	*	active	= GetActivePage();
1425	OSStatus			err		= kNoErr;
1426
1427	if ( active == &m_pgSecond )
1428	{
1429		m_pgSecond.OnRemovePrinter( printer, moreComing );
1430	}
1431
1432	m_printers.remove( printer );
1433
1434	if ( m_selectedPrinter == printer )
1435	{
1436		m_selectedPrinter = NULL;
1437
1438		if ( ( active == &m_pgThird ) || ( active == &m_pgFourth ) )
1439		{
1440			CString caption;
1441			CString message;
1442
1443			caption.LoadString( IDS_ERROR_CAPTION );
1444			message.LoadString( IDS_PRINTER_UNAVAILABLE );
1445
1446			MessageBox(message, caption, MB_OK|MB_ICONEXCLAMATION);
1447
1448			SetActivePage( &m_pgSecond );
1449		}
1450	}
1451
1452	delete printer;
1453
1454	return err;
1455}
1456
1457
1458OSStatus
1459CPrinterSetupWizardSheet::OnRemoveService( Service * service )
1460{
1461	OSStatus err = kNoErr;
1462
1463	if ( service && ( --service->refs == 0 ) )
1464	{
1465		if ( service->serviceRef != NULL )
1466		{
1467			err = StopResolve( service );
1468			require_noerr( err, exit );
1469		}
1470
1471		service->printer->services.remove( service );
1472
1473		delete service;
1474	}
1475
1476exit:
1477
1478	return err;
1479}
1480
1481
1482void
1483CPrinterSetupWizardSheet::OnResolveService( Service * service )
1484{
1485	// Make sure that the active page is page 2
1486
1487	require_quiet( GetActivePage() == &m_pgSecond, exit );
1488
1489	if ( !--service->printer->resolving )
1490	{
1491		// sort the services now.  we want the service that
1492		// has the highest priority queue to be first in
1493		// the list.
1494
1495		service->printer->services.sort( OrderServiceFunc );
1496
1497		// Now we can hit next
1498
1499		SetWizardButtons( PSWIZB_BACK|PSWIZB_NEXT );
1500
1501		// Reset the cursor
1502
1503		m_active = m_arrow;
1504
1505		// And tell page 2 about it
1506
1507		m_pgSecond.OnResolveService( service );
1508	}
1509
1510exit:
1511
1512	return;
1513}
1514
1515
1516OSStatus
1517CPrinterSetupWizardSheet::StartBrowse()
1518{
1519	OSStatus err;
1520
1521	//
1522	// setup the DNS-SD browsing
1523	//
1524	err = DNSServiceBrowse( &m_pdlBrowser, 0, 0, kPDLServiceType, NULL, OnBrowse, this );
1525	require_noerr( err, exit );
1526
1527	err = StartOperation( m_pdlBrowser );
1528	require_noerr( err, exit );
1529
1530	err = DNSServiceBrowse( &m_lprBrowser, 0, 0, kLPRServiceType, NULL, OnBrowse, this );
1531	require_noerr( err, exit );
1532
1533	err = StartOperation( m_lprBrowser );
1534	require_noerr( err, exit );
1535
1536	err = DNSServiceBrowse( &m_ippBrowser, 0, 0, kIPPServiceType, NULL, OnBrowse, this );
1537	require_noerr( err, exit );
1538
1539	err = StartOperation( m_ippBrowser );
1540	require_noerr( err, exit );
1541
1542exit:
1543
1544	return err;
1545}
1546
1547
1548OSStatus
1549CPrinterSetupWizardSheet::StopBrowse()
1550{
1551	OSStatus err;
1552
1553	err = StopOperation( m_pdlBrowser );
1554	require_noerr( err, exit );
1555
1556	err = StopOperation( m_lprBrowser );
1557	require_noerr( err, exit );
1558
1559	err = StopOperation( m_ippBrowser );
1560	require_noerr( err, exit );
1561
1562	while ( m_printers.size() > 0 )
1563	{
1564		Printer * printer = m_printers.front();
1565
1566		m_printers.pop_front();
1567
1568		if ( printer->resolving )
1569		{
1570			StopResolve( printer );
1571		}
1572
1573		delete printer;
1574	}
1575
1576exit:
1577
1578	return err;
1579}
1580
1581
1582OSStatus
1583CPrinterSetupWizardSheet::StartResolve( Printer * printer )
1584{
1585	OSStatus			err = kNoErr;
1586	Services::iterator	it;
1587
1588	check( printer );
1589
1590	for ( it = printer->services.begin(); it != printer->services.end(); it++ )
1591	{
1592		if ( (*it)->serviceRef == NULL )
1593		{
1594			err = StartResolve( *it );
1595			require_noerr( err, exit );
1596		}
1597	}
1598
1599	m_selectedPrinter = printer;
1600
1601exit:
1602
1603	return err;
1604}
1605
1606
1607OSStatus
1608CPrinterSetupWizardSheet::StartResolve( Service * service )
1609{
1610	OSStatus err = kNoErr;
1611
1612	check( service->serviceRef == NULL );
1613
1614	//
1615	// clean out any queues that were collected during a previous
1616	// resolve
1617	//
1618
1619	service->EmptyQueues();
1620
1621	//
1622	// now start the new resolve
1623	//
1624
1625	err = DNSServiceResolve( &service->serviceRef, 0, 0, service->printer->name.c_str(), service->type.c_str(), service->domain.c_str(), (DNSServiceResolveReply) OnResolve, service );
1626	require_noerr( err, exit );
1627
1628	err = StartOperation( service->serviceRef );
1629	require_noerr( err, exit );
1630
1631	//
1632	// If we're not currently resolving, then disable the next button
1633	// and set the cursor to hourglass
1634	//
1635
1636	if ( !service->printer->resolving )
1637	{
1638		SetWizardButtons( PSWIZB_BACK );
1639
1640		m_active = m_wait;
1641		SetCursor(m_active);
1642	}
1643
1644	service->printer->resolving++;
1645
1646exit:
1647
1648	return err;
1649}
1650
1651
1652OSStatus
1653CPrinterSetupWizardSheet::StopResolve(Printer * printer)
1654{
1655	OSStatus err = kNoErr;
1656
1657	check( printer );
1658
1659	Services::iterator it;
1660
1661	for ( it = printer->services.begin(); it != printer->services.end(); it++ )
1662	{
1663		if ( (*it)->serviceRef )
1664		{
1665			err = StopResolve( *it );
1666			require_noerr( err, exit );
1667		}
1668	}
1669
1670exit:
1671
1672	return err;
1673}
1674
1675
1676OSStatus
1677CPrinterSetupWizardSheet::StopResolve( Service * service )
1678{
1679	OSStatus err;
1680
1681	check( service->serviceRef );
1682
1683	err = StopOperation( service->serviceRef );
1684	require_noerr( err, exit );
1685
1686	service->printer->resolving--;
1687
1688exit:
1689
1690	return err;
1691}
1692
1693
1694OSStatus
1695CPrinterSetupWizardSheet::StartOperation( DNSServiceRef ref )
1696{
1697	OSStatus err;
1698
1699	err = WSAAsyncSelect((SOCKET) DNSServiceRefSockFD(ref), m_hWnd, WM_SOCKET_EVENT, FD_READ|FD_CLOSE);
1700	require_noerr( err, exit );
1701
1702	m_serviceRefList.push_back( ref );
1703
1704exit:
1705
1706	return err;
1707}
1708
1709
1710OSStatus
1711CPrinterSetupWizardSheet::StopOperation( DNSServiceRef & ref )
1712{
1713	OSStatus err = kNoErr;
1714
1715	if ( ref )
1716	{
1717		m_serviceRefList.remove( ref );
1718
1719		if ( IsWindow( m_hWnd ) )
1720		{
1721			err = WSAAsyncSelect((SOCKET) DNSServiceRefSockFD( ref ), m_hWnd, 0, 0 );
1722			require_noerr( err, exit );
1723		}
1724
1725		DNSServiceRefDeallocate( ref );
1726		ref = NULL;
1727	}
1728
1729exit:
1730
1731	return err;
1732}
1733
1734
1735OSStatus
1736CPrinterSetupWizardSheet::ParseTextRecord( Service * service, Queue * q, uint16_t inTXTSize, const char * inTXT )
1737{
1738	check( service );
1739	check( q );
1740
1741	// <rdar://problem/3946587> Use TXTRecord APIs declared in dns_sd.h
1742
1743	bool			qtotalDefined	= false;
1744	const void	*	val;
1745	char			buf[256];
1746	uint8_t			len;
1747	OSStatus		err				= kNoErr;
1748
1749	// <rdar://problem/3987680> Default to queue "lp"
1750
1751	q->name = L"lp";
1752
1753	// <rdar://problem/4003710> Default pdl key to be "application/postscript"
1754
1755	q->pdl = L"application/postscript";
1756
1757	if ( ( val = TXTRecordGetValuePtr( inTXTSize, inTXT, "rp", &len ) ) != NULL )
1758	{
1759		// Stringize val ( doesn't have trailing '\0' yet )
1760
1761		memcpy( buf, val, len );
1762		buf[len] = '\0';
1763
1764		err = UTF8StringToStringObject( buf, q->name );
1765		require_noerr( err, exit );
1766	}
1767
1768	if ( ( val = TXTRecordGetValuePtr( inTXTSize, inTXT, "pdl", &len ) ) != NULL )
1769	{
1770		// Stringize val ( doesn't have trailing '\0' yet )
1771
1772		memcpy( buf, val, len );
1773		buf[len] = '\0';
1774
1775		err = UTF8StringToStringObject( buf, q->pdl );
1776		require_noerr( err, exit );
1777	}
1778
1779	if ( ( ( val = TXTRecordGetValuePtr( inTXTSize, inTXT, "usb_mfg", &len ) ) != NULL ) ||
1780	     ( ( val = TXTRecordGetValuePtr( inTXTSize, inTXT, "usb_manufacturer", &len ) ) != NULL ) )
1781	{
1782		// Stringize val ( doesn't have trailing '\0' yet )
1783
1784		memcpy( buf, val, len );
1785		buf[len] = '\0';
1786
1787		err = UTF8StringToStringObject( buf, q->usb_MFG );
1788		require_noerr( err, exit );
1789	}
1790
1791	if ( ( ( val = TXTRecordGetValuePtr( inTXTSize, inTXT, "usb_mdl", &len ) ) != NULL ) ||
1792	     ( ( val = TXTRecordGetValuePtr( inTXTSize, inTXT, "usb_model", &len ) ) != NULL ) )
1793	{
1794		// Stringize val ( doesn't have trailing '\0' yet )
1795
1796		memcpy( buf, val, len );
1797		buf[len] = '\0';
1798
1799		err = UTF8StringToStringObject( buf, q->usb_MDL );
1800		require_noerr( err, exit );
1801	}
1802
1803	if ( ( val = TXTRecordGetValuePtr( inTXTSize, inTXT, "ty", &len ) ) != NULL )
1804	{
1805		// Stringize val ( doesn't have trailing '\0' yet )
1806
1807		memcpy( buf, val, len );
1808		buf[len] = '\0';
1809
1810		err = UTF8StringToStringObject( buf, q->description );
1811		require_noerr( err, exit );
1812	}
1813
1814	if ( ( val = TXTRecordGetValuePtr( inTXTSize, inTXT, "product", &len ) ) != NULL )
1815	{
1816		// Stringize val ( doesn't have trailing '\0' yet )
1817
1818		memcpy( buf, val, len );
1819		buf[len] = '\0';
1820
1821		err = UTF8StringToStringObject( buf, q->product );
1822		require_noerr( err, exit );
1823	}
1824
1825	if ( ( val = TXTRecordGetValuePtr( inTXTSize, inTXT, "note", &len ) ) != NULL )
1826	{
1827		// Stringize val ( doesn't have trailing '\0' yet )
1828
1829		memcpy( buf, val, len );
1830		buf[len] = '\0';
1831
1832		err = UTF8StringToStringObject( buf, q->location );
1833		require_noerr( err, exit );
1834	}
1835
1836	if ( ( val = TXTRecordGetValuePtr( inTXTSize, inTXT, "qtotal", &len ) ) != NULL )
1837	{
1838		// Stringize val ( doesn't have trailing '\0' yet )
1839
1840		memcpy( buf, val, len );
1841		buf[len] = '\0';
1842
1843		service->qtotal = (unsigned short) atoi( buf );
1844		qtotalDefined = true;
1845	}
1846
1847	if ( ( val = TXTRecordGetValuePtr( inTXTSize, inTXT, "priority", &len ) ) != NULL )
1848	{
1849		// Stringize val ( doesn't have trailing '\0' yet )
1850
1851		memcpy( buf, val, len );
1852		buf[len] = '\0';
1853
1854		q->priority = atoi( buf );
1855	}
1856
1857	// <rdar://problem/4124524> Was this printer discovered via OS X Printer Sharing?
1858
1859	if ( TXTRecordContainsKey( inTXTSize, inTXT, "printer-state" ) || TXTRecordContainsKey( inTXTSize, inTXT, "printer-type" ) )
1860	{
1861		service->printer->isCUPSPrinter = true;
1862	}
1863
1864exit:
1865
1866	// The following code is to fix a problem with older HP
1867	// printers that don't include "qtotal" in their text
1868	// record.  We'll check to see if the q->name is "TEXT"
1869	// and if so, we're going to modify it to be "lp" so
1870	// that we don't use the wrong queue
1871
1872	if ( !err && !qtotalDefined && ( q->name == L"TEXT" ) )
1873	{
1874		q->name = "lp";
1875	}
1876
1877	return err;
1878}
1879
1880
1881Printer*
1882CPrinterSetupWizardSheet::Lookup(const char * inName)
1883{
1884	check( inName );
1885
1886	Printer			*	printer = NULL;
1887	Printers::iterator	it;
1888
1889	for ( it = m_printers.begin(); it != m_printers.end(); it++ )
1890	{
1891		if ( (*it)->name == inName )
1892		{
1893			printer = *it;
1894			break;
1895		}
1896	}
1897
1898	return printer;
1899}
1900
1901
1902bool
1903CPrinterSetupWizardSheet::OrderServiceFunc( const Service * a, const Service * b )
1904{
1905	Queue * q1, * q2;
1906
1907	q1 = (a->queues.size() > 0) ? a->queues.front() : NULL;
1908
1909	q2 = (b->queues.size() > 0) ? b->queues.front() : NULL;
1910
1911	if ( !q1 && !q2 )
1912	{
1913		return true;
1914	}
1915	else if ( q1 && !q2 )
1916	{
1917		return true;
1918	}
1919	else if ( !q1 && q2 )
1920	{
1921		return false;
1922	}
1923	else if ( q1->priority < q2->priority )
1924	{
1925		return true;
1926	}
1927	else if ( q1->priority > q2->priority )
1928	{
1929		return false;
1930	}
1931	else if ( ( a->type == kPDLServiceType ) || ( ( a->type == kLPRServiceType ) && ( b->type == kIPPServiceType ) ) )
1932	{
1933		return true;
1934	}
1935	else
1936	{
1937		return false;
1938	}
1939}
1940
1941
1942bool
1943CPrinterSetupWizardSheet::OrderQueueFunc( const Queue * q1, const Queue * q2 )
1944{
1945	return ( q1->priority <= q2->priority ) ? true : false;
1946}
1947
1948
1949
1950