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 
46 static TCHAR*
47 g_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 
65 static BOOL
Is64BitWindows()66 Is64BitWindows()
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
95 CPrinterSetupWizardSheet * CPrinterSetupWizardSheet::m_self;
96 
IMPLEMENT_DYNAMIC(CPrinterSetupWizardSheet,CPropertySheet)97 IMPLEMENT_DYNAMIC(CPrinterSetupWizardSheet, CPropertySheet)
98 CPrinterSetupWizardSheet::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 
~CPrinterSetupWizardSheet()119 CPrinterSetupWizardSheet::~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 //
141 void
SetSelectedPrinter(Printer * printer)142 CPrinterSetupWizardSheet::SetSelectedPrinter(Printer * printer)
143 {
144 	check( !printer || ( printer != m_selectedPrinter ) );
145 
146 	m_selectedPrinter = printer;
147 }
148 
149 
150 OSStatus
LoadPrinterNames()151 CPrinterSetupWizardSheet::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 
192 exit:
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 
219 OSStatus
InstallPrinter(Printer * printer)220 CPrinterSetupWizardSheet::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 
327 exit:
328 
329 	return err;
330 }
331 
332 
333 OSStatus
InstallPrinterPort(Printer * printer,Service * service,DWORD protocol,Logger & log)334 CPrinterSetupWizardSheet::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 
391 exit:
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 
407 OSStatus
InstallPrinterPDLAndLPR(Printer * printer,Service * service,Logger & log)408 CPrinterSetupWizardSheet::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 
450 exit:
451 
452 	if (hPrinter != NULL)
453 	{
454 		ClosePrinter(hPrinter);
455 	}
456 
457 	return err;
458 }
459 
460 
461 OSStatus
InstallPrinterIPP(Printer * printer,Service * service,Logger & log)462 CPrinterSetupWizardSheet::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 
490 exit:
491 
492 	if ( hPrinter != NULL )
493 	{
494 		ClosePrinter(hPrinter);
495 	}
496 
497 	return err;
498 }
499 
500 
501 OSStatus
InstallPrinterCUPS(Printer * printer,Service * service,CUPSLibrary & cupsLib)502 CPrinterSetupWizardSheet::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 
519 exit:
520 
521 	return err;
522 }
523 
524 
525 OSStatus
InstallPrinterCUPS(Printer * printer,Service * service,CUPSLibrary & cupsLib,TCHAR * env)526 CPrinterSetupWizardSheet::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 
701 exit:
702 
703 	if ( printerHandle != NULL )
704 	{
705 		ClosePrinter( printerHandle );
706 		printerHandle = NULL;
707 	}
708 
709 	return err;
710 }
711 
BEGIN_MESSAGE_MAP(CPrinterSetupWizardSheet,CPropertySheet)712 BEGIN_MESSAGE_MAP(CPrinterSetupWizardSheet, CPropertySheet)
713 ON_MESSAGE( WM_SOCKET_EVENT, OnSocketEvent )
714 ON_MESSAGE( WM_PROCESS_EVENT, OnProcessEvent )
715 ON_WM_SETCURSOR()
716 ON_WM_TIMER()
717 END_MESSAGE_MAP()
718 
719 
720 // ------------------------------------------------------
721 // OnCommand
722 //
723 // Traps when the user hits Finish
724 //
725 BOOL 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 //
OnInitDialog()744 BOOL CPrinterSetupWizardSheet::OnInitDialog()
745 {
746 	OSStatus err;
747 
748 	CPropertySheet::OnInitDialog();
749 
750 	err = StartBrowse();
751 	require_noerr( err, exit );
752 
753 exit:
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 //
791 BOOL
OnSetCursor(CWnd * pWnd,UINT nHitTest,UINT message)792 CPrinterSetupWizardSheet::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 
809 void
OnContextMenu(CWnd * pWnd,CPoint pos)810 CPrinterSetupWizardSheet::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 //
826 void
OnOK()827 CPrinterSetupWizardSheet::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 
Init(void)866 void 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 
884 LRESULT
OnSocketEvent(WPARAM inWParam,LPARAM inLParam)885 CPrinterSetupWizardSheet::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 
917 LRESULT
OnProcessEvent(WPARAM inWParam,LPARAM inLParam)918 CPrinterSetupWizardSheet::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 
929 unsigned WINAPI
InstallDriverThread(LPVOID inParam)930 CPrinterSetupWizardSheet::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 
970 exit:
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 
996 void DNSSD_API
OnBrowse(DNSServiceRef inRef,DNSServiceFlags inFlags,uint32_t inInterfaceIndex,DNSServiceErrorType inErrorCode,const char * inName,const char * inType,const char * inDomain,void * inContext)997 CPrinterSetupWizardSheet::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 
1080 exit:
1081 
1082 	return;
1083 }
1084 
1085 
1086 void DNSSD_API
OnResolve(DNSServiceRef inRef,DNSServiceFlags inFlags,uint32_t inInterfaceIndex,DNSServiceErrorType inErrorCode,const char * inFullName,const char * inHostName,uint16_t inPort,uint16_t inTXTSize,const char * inTXT,void * inContext)1087 CPrinterSetupWizardSheet::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 
1190 exit:
1191 
1192 	return;
1193 }
1194 
1195 
1196 void DNSSD_API
OnQuery(DNSServiceRef inRef,DNSServiceFlags inFlags,uint32_t inInterfaceIndex,DNSServiceErrorType inErrorCode,const char * inFullName,uint16_t inRRType,uint16_t inRRClass,uint16_t inRDLen,const void * inRData,uint32_t inTTL,void * inContext)1197 CPrinterSetupWizardSheet::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 
1280 exit:
1281 
1282 	if ( err && service && ( service->serviceRef != NULL ) )
1283 	{
1284 		service->printer->window->StopOperation( service->serviceRef );
1285 	}
1286 
1287 	return;
1288 }
1289 
1290 
1291 Printer*
OnAddPrinter(uint32_t inInterfaceIndex,const char * inName,const char * inType,const char * inDomain,bool moreComing)1292 CPrinterSetupWizardSheet::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 
1364 exit:
1365 
1366 	return printer;
1367 }
1368 
1369 
1370 OSStatus
OnAddService(Printer * printer,uint32_t inInterfaceIndex,const char * inName,const char * inType,const char * inDomain)1371 CPrinterSetupWizardSheet::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 
1415 exit:
1416 
1417 	return err;
1418 }
1419 
1420 
1421 OSStatus
OnRemovePrinter(Printer * printer,bool moreComing)1422 CPrinterSetupWizardSheet::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 
1458 OSStatus
OnRemoveService(Service * service)1459 CPrinterSetupWizardSheet::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 
1476 exit:
1477 
1478 	return err;
1479 }
1480 
1481 
1482 void
OnResolveService(Service * service)1483 CPrinterSetupWizardSheet::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 
1510 exit:
1511 
1512 	return;
1513 }
1514 
1515 
1516 OSStatus
StartBrowse()1517 CPrinterSetupWizardSheet::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 
1542 exit:
1543 
1544 	return err;
1545 }
1546 
1547 
1548 OSStatus
StopBrowse()1549 CPrinterSetupWizardSheet::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 
1576 exit:
1577 
1578 	return err;
1579 }
1580 
1581 
1582 OSStatus
StartResolve(Printer * printer)1583 CPrinterSetupWizardSheet::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 
1601 exit:
1602 
1603 	return err;
1604 }
1605 
1606 
1607 OSStatus
StartResolve(Service * service)1608 CPrinterSetupWizardSheet::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 
1646 exit:
1647 
1648 	return err;
1649 }
1650 
1651 
1652 OSStatus
StopResolve(Printer * printer)1653 CPrinterSetupWizardSheet::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 
1670 exit:
1671 
1672 	return err;
1673 }
1674 
1675 
1676 OSStatus
StopResolve(Service * service)1677 CPrinterSetupWizardSheet::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 
1688 exit:
1689 
1690 	return err;
1691 }
1692 
1693 
1694 OSStatus
StartOperation(DNSServiceRef ref)1695 CPrinterSetupWizardSheet::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 
1704 exit:
1705 
1706 	return err;
1707 }
1708 
1709 
1710 OSStatus
StopOperation(DNSServiceRef & ref)1711 CPrinterSetupWizardSheet::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 
1729 exit:
1730 
1731 	return err;
1732 }
1733 
1734 
1735 OSStatus
ParseTextRecord(Service * service,Queue * q,uint16_t inTXTSize,const char * inTXT)1736 CPrinterSetupWizardSheet::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 
1864 exit:
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 
1881 Printer*
Lookup(const char * inName)1882 CPrinterSetupWizardSheet::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 
1902 bool
OrderServiceFunc(const Service * a,const Service * b)1903 CPrinterSetupWizardSheet::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 
1942 bool
OrderQueueFunc(const Queue * q1,const Queue * q2)1943 CPrinterSetupWizardSheet::OrderQueueFunc( const Queue * q1, const Queue * q2 )
1944 {
1945 	return ( q1->priority <= q2->priority ) ? true : false;
1946 }
1947 
1948 
1949 
1950