]> git.saurik.com Git - apple/mdnsresponder.git/blobdiff - Clients/PrinterSetupWizard/PrinterSetupWizardSheet.cpp
mDNSResponder-878.200.35.tar.gz
[apple/mdnsresponder.git] / Clients / PrinterSetupWizard / PrinterSetupWizardSheet.cpp
index 02b2952ca0cbee95180ab6fc655c3854419703bd..47e5f9110bc0df459c61717c6aed17fb6bbf3ca7 100644 (file)
@@ -1,92 +1,19 @@
-/*
+/* -*- Mode: C; tab-width: 4 -*-
+ *
  * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved.
  *
  * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved.
  *
- * @APPLE_LICENSE_HEADER_START@
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
  * 
  * 
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
+ *     http://www.apache.org/licenses/LICENSE-2.0
  * 
  * 
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
  * limitations under the License.
  * limitations under the License.
- * 
- * @APPLE_LICENSE_HEADER_END@
-
-    Change History (most recent first):
-    
-$Log: PrinterSetupWizardSheet.cpp,v $
-Revision 1.17  2004/10/12 18:02:53  shersche
-<rdar://problem/3764873> Escape '/', '@', '"' characters in printui command.
-Bug #: 3764873
-
-Revision 1.16  2004/09/13 21:27:22  shersche
-<rdar://problem/3796483> Pass the moreComing flag to OnAddPrinter and OnRemovePrinter callbacks
-Bug #: 3796483
-
-Revision 1.15  2004/09/11 05:59:06  shersche
-<rdar://problem/3785766> Fix code that generates unique printer names based on currently installed printers
-Bug #: 3785766
-
-Revision 1.14  2004/09/02 01:57:58  cheshire
-<rdar://problem/3783611> Fix incorrect testing of MoreComing flag
-
-Revision 1.13  2004/07/26 21:06:29  shersche
-<rdar://problem/3739200> Removing trailing '.' in hostname
-Bug #: 3739200
-
-Revision 1.12  2004/07/13 21:24:23  rpantos
-Fix for <rdar://problem/3701120>.
-
-Revision 1.11  2004/06/28 00:51:47  shersche
-Move call to EnumPrinters out of browse callback into standalone function
-
-Revision 1.10  2004/06/27 23:06:47  shersche
-code cleanup, make sure EnumPrinters returns non-zero value
-
-Revision 1.9  2004/06/27 15:49:31  shersche
-clean up some cruft in the printer browsing code
-
-Revision 1.8  2004/06/27 08:04:51  shersche
-copy selected printer to prevent printer being deleted out from under
-
-Revision 1.7  2004/06/26 23:27:12  shersche
-support for installing multiple printers of the same name
-
-Revision 1.6  2004/06/26 21:22:39  shersche
-handle spaces in file names
-
-Revision 1.5  2004/06/26 03:19:57  shersche
-clean up warning messages
-
-Submitted by: herscher
-
-Revision 1.4  2004/06/25 02:26:52  shersche
-Normalize key fields in text record entries
-Submitted by: herscher
-
-Revision 1.3  2004/06/24 20:12:07  shersche
-Clean up source code
-Submitted by: herscher
-
-Revision 1.2  2004/06/23 17:58:21  shersche
-<rdar://problem/3701837> eliminated memory leaks on exit
-<rdar://problem/3701926> installation of a printer that is already installed results in a no-op
-Bug #: 3701837, 3701926
-Submitted by: herscher
-
-Revision 1.1  2004/06/18 04:36:57  rpantos
-First checked in
-
-
-*/
+ */
 
 #include "stdafx.h"
 #include "PrinterSetupWizardApp.h"
 
 #include "stdafx.h"
 #include "PrinterSetupWizardApp.h"
@@ -95,9 +22,10 @@ First checked in
 #include "DebugServices.h"
 #include "WinServices.h"
 #include "About.h"
 #include "DebugServices.h"
 #include "WinServices.h"
 #include "About.h"
+#include "tcpxcv.h"
 #include <winspool.h>
 #include <winspool.h>
-#include <tcpxcv.h>
 #include <string>
 #include <string>
+#include <shlwapi.h>
 
 // unreachable code
 #pragma warning(disable:4702)
 
 // unreachable code
 #pragma warning(disable:4702)
@@ -108,173 +36,177 @@ First checked in
 #      include <process.h>
 #endif
 
 #      include <process.h>
 #endif
 
+
+#if defined( UNICODE ) || defined( _UNICODE )
+#      define GetEnv   _wgetenv
+#else
+#      define GetEnv   getenv
+#endif
+
+static TCHAR*
+g_printerDriverFiles[] =               // Printer driver files
+{
+       TEXT( "ps5ui.dll" ),
+       TEXT( "pscript.hlp" ),
+       TEXT( "pscript.ntf" ),
+       TEXT( "pscript5.dll" ),
+       TEXT( "cups6.ini" ),
+       TEXT( "cupsui6.dll" ),
+       TEXT( "cupsps6.dll" )
+};
+
+
 // Private Messages
 
 // Private Messages
 
-#define WM_SERVICE_EVENT                               ( WM_USER + 0x100 )
-#define WM_PROCESS_EVENT                               ( WM_USER + 0x101 )
+#define WM_SOCKET_EVENT                ( WM_USER + 0x100 )
+#define WM_PROCESS_EVENT       ( WM_USER + 0x101 )
+
+
+static BOOL
+Is64BitWindows()
+{
+#if defined(_WIN64)
+       return TRUE;  // 64-bit programs run only on Win64
+#else
+       typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS)( HANDLE, PBOOL );
+       LPFN_ISWOW64PROCESS fnIsWow64Process;
+       BOOL bIsWow64 = FALSE;
+
+    fnIsWow64Process = ( LPFN_ISWOW64PROCESS ) GetProcAddress( GetModuleHandle( TEXT( "kernel32" ) ), "IsWow64Process" );
+  
+    if ( fnIsWow64Process != NULL )
+    {
+               BOOL ok;
+
+        ok = fnIsWow64Process( GetCurrentProcess(), &bIsWow64 );
 
 
-// Service Types
+               if ( !ok )
+               {
+                       bIsWow64 = FALSE;
+               }
+       }
 
 
-#define        kPDLDataStreamServiceType               "_pdl-datastream._tcp"
-#define kLPRServiceType                                        "_printer._tcp"
-#define kIPPServiceType                                        "_ipp._tcp"
+       return bIsWow64;
+#endif
+}
 
 
 // CPrinterSetupWizardSheet
 
 
 // CPrinterSetupWizardSheet
+CPrinterSetupWizardSheet * CPrinterSetupWizardSheet::m_self;
 
 IMPLEMENT_DYNAMIC(CPrinterSetupWizardSheet, CPropertySheet)
 CPrinterSetupWizardSheet::CPrinterSetupWizardSheet(UINT nIDCaption, CWnd* pParentWnd, UINT iSelectPage)
        :CPropertySheet(nIDCaption, pParentWnd, iSelectPage),
 
 IMPLEMENT_DYNAMIC(CPrinterSetupWizardSheet, CPropertySheet)
 CPrinterSetupWizardSheet::CPrinterSetupWizardSheet(UINT nIDCaption, CWnd* pParentWnd, UINT iSelectPage)
        :CPropertySheet(nIDCaption, pParentWnd, iSelectPage),
-       m_selectedPrinter(NULL)
+       m_selectedPrinter(NULL),
+       m_driverThreadExitCode( 0 ),
+       m_driverThreadFinished( false ),
+       m_pdlBrowser( NULL ),
+       m_ippBrowser( NULL ),
+       m_lprBrowser( NULL ),
+       m_lastPage( NULL )
 {
        m_arrow         =       LoadCursor(0, IDC_ARROW);
        m_wait          =       LoadCursor(0, IDC_APPSTARTING);
        m_active        =       m_arrow;
 {
        m_arrow         =       LoadCursor(0, IDC_ARROW);
        m_wait          =       LoadCursor(0, IDC_APPSTARTING);
        m_active        =       m_arrow;
+       m_self          =       this;
        
        Init();
        
        Init();
+
+       LoadPrinterNames();
 }
 
 
 CPrinterSetupWizardSheet::~CPrinterSetupWizardSheet()
 {
 }
 
 
 CPrinterSetupWizardSheet::~CPrinterSetupWizardSheet()
 {
-       //
-       // rdar://problem/3701837 memory leaks
-       //
-       // Clean up the ServiceRef and printer list on exit
-       //
-       if (m_pdlBrowser != NULL)
-       {
-               DNSServiceRefDeallocate(m_pdlBrowser);
-               m_pdlBrowser = NULL;
-       }
+       Printer * printer;
 
 
-       while (m_printerList.size() > 0)
+       while ( m_printers.size() > 0 )
        {
        {
-               Printer * printer = m_printerList.front();
-
-               m_printerList.pop_front();
+               printer = m_printers.front();
+               m_printers.pop_front();
 
                delete printer;
        }
 
 
                delete printer;
        }
 
-       if (m_selectedPrinter != NULL)
-       {
-               delete m_selectedPrinter;
-       }
-}
-
-
-// ------------------------------------------------------
-// InstallEventHandler
-//
-// Installs an event handler for DNSService events.  
-//     
-int
-CPrinterSetupWizardSheet::InstallEventHandler(EventHandler * handler)
-{
-       PrinterList::iterator iter;
-       
-       m_eventHandlerList.push_back(handler);
-
-       iter = m_printerList.begin();
-
-       while (iter != m_printerList.end())
-       {
-               Printer * printer = *iter++;
-
-               handler->OnAddPrinter(printer, iter != m_printerList.end());
-       }
-       
-       return kNoErr;
+       m_self = NULL;
 }
 
 
 // ------------------------------------------------------
 }
 
 
 // ------------------------------------------------------
-// RemoveEventHandler
+// SetSelectedPrinter
 //
 //
-// Removes an event handler for DNSService events.  
+// Manages setting a printer as the printer to install.  Stops
+// any pending resolves.  
 //     
 //     
-int
-CPrinterSetupWizardSheet::RemoveEventHandler(EventHandler * handler)
+void
+CPrinterSetupWizardSheet::SetSelectedPrinter(Printer * printer)
 {
 {
-       m_eventHandlerList.remove(handler);
+       check( !printer || ( printer != m_selectedPrinter ) );
 
 
-       return kNoErr;
+       m_selectedPrinter = printer;
 }
 
 
 }
 
 
-
-// ------------------------------------------------------
-// SetSelectedPrinter
-//
-// Manages setting a printer as the printer to install.  Stops
-// any pending resolves.  
-//     
 OSStatus
 OSStatus
-CPrinterSetupWizardSheet::SetSelectedPrinter(Printer * printer)
+CPrinterSetupWizardSheet::LoadPrinterNames()
 {
 {
-       OSStatus err;
+       PBYTE           buffer  =       NULL;
+       OSStatus        err             = 0;
 
        //
 
        //
-       // we only want one resolve going on at a time, so we check
-       // state of the m_selectedPrinter
+       // rdar://problem/3701926 - Printer can't be installed twice
+       //
+       // First thing we want to do is make sure the printer isn't already installed.
+       // If the printer name is found, we'll try and rename it until we
+       // find a unique name
        //
        //
-       if (m_selectedPrinter != NULL)
+       DWORD dwNeeded = 0, dwNumPrinters = 0;
+
+       BOOL ok = EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 4, NULL, 0, &dwNeeded, &dwNumPrinters);
+       err = translate_errno( ok, errno_compat(), kUnknownErr );
+
+       if ((err == ERROR_INSUFFICIENT_BUFFER) && (dwNeeded > 0))
        {
        {
-               //
-               // if we're currently resolving, then stop the resolve
-               //
-               if (m_selectedPrinter->serviceRef)
+               try
+               {
+                       buffer = new unsigned char[dwNeeded];
+               }
+               catch (...)
                {
                {
-                       err = StopResolve(m_selectedPrinter);
-                       require_noerr(err, exit);
+                       buffer = NULL;
                }
                }
+       
+               require_action( buffer, exit, kNoMemoryErr );
+               ok = EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 4, buffer, dwNeeded, &dwNeeded, &dwNumPrinters);
+               err = translate_errno( ok, errno_compat(), kUnknownErr );
+               require_noerr( err, exit );
 
 
-               delete m_selectedPrinter;
-               m_selectedPrinter = NULL;
+               for (DWORD index = 0; index < dwNumPrinters; index++)
+               {
+                       PRINTER_INFO_4 * lppi4 = (PRINTER_INFO_4*) (buffer + index * sizeof(PRINTER_INFO_4));
+
+                       m_printerNames.push_back( lppi4->pPrinterName );
+               }
        }
 
        }
 
-       check( m_selectedPrinter == NULL );
+exit:
 
 
-       try
-       {
-               m_selectedPrinter = new Printer;
-       }
-       catch (...)
+       if (buffer != NULL)
        {
        {
-               m_selectedPrinter = NULL;
+               delete [] buffer;
        }
 
        }
 
-       require_action( m_selectedPrinter, exit, err = E_OUTOFMEMORY );
-
-       m_selectedPrinter->window               =       printer->window;
-       m_selectedPrinter->serviceRef   =       NULL;
-       m_selectedPrinter->item                 =       NULL;
-       m_selectedPrinter->ifi                  =       printer->ifi;
-       m_selectedPrinter->name                 =       printer->name;
-       m_selectedPrinter->displayName  =       printer->displayName;
-       m_selectedPrinter->actualName   =       printer->actualName;
-       m_selectedPrinter->type                 =       printer->type;
-       m_selectedPrinter->domain               =       printer->domain;
-       m_selectedPrinter->installed    =       printer->installed;
-       m_selectedPrinter->deflt                =       printer->deflt;
-       m_selectedPrinter->refs                 =       1;
-                       
-       err = StartResolve(m_selectedPrinter);
-       require_noerr(err, exit);
-
-exit:
-
        return err;
 }
 
 
        return err;
 }
 
 
+
 // ------------------------------------------------------
 // InstallPrinter
 //
 // Installs a printer with Windows.
 //
 // ------------------------------------------------------
 // InstallPrinter
 //
 // Installs a printer with Windows.
 //
-// NOTE: this works one of two ways, depending on whether
+// Note: this works one of two ways, depending on whether
 // there are drivers already installed for this printer.
 // If there are, then we can just create a port with XcvData,
 // and then call AddPrinter.  If not, we use the printui.dll
 // there are drivers already installed for this printer.
 // If there are, then we can just create a port with XcvData,
 // and then call AddPrinter.  If not, we use the printui.dll
@@ -286,25 +218,144 @@ exit:
 
 OSStatus
 CPrinterSetupWizardSheet::InstallPrinter(Printer * printer)
 
 OSStatus
 CPrinterSetupWizardSheet::InstallPrinter(Printer * printer)
+{
+       Logger          log;
+       CUPSLibrary     cupsLib;
+       Service *       service         = NULL;
+       BOOL            ok;
+       OSStatus        err = 0;
+
+       service = printer->services.front();
+       check( service );
+
+       if ( printer->isCUPSPrinter && cupsLib.IsInstalled() )
+       {
+               err = InstallPrinterCUPS( printer, service, cupsLib );
+               require_noerr( err, exit );
+       }
+       else
+       {
+               //
+               // if the driver isn't installed, then install it
+               //
+
+               if ( !printer->driverInstalled )
+               {
+                       DWORD           dwResult;
+                       HANDLE          hThread;
+                       unsigned        threadID;
+
+                       m_driverThreadFinished = false;
+               
+                       //
+                       // create the thread
+                       //
+                       hThread = (HANDLE) _beginthreadex_compat( NULL, 0, InstallDriverThread, printer, 0, &threadID );
+                       err = translate_errno( hThread, (OSStatus) GetLastError(), kUnknownErr );
+                       require_noerr_with_log( log, "_beginthreadex_compat()", err, exit );
+                               
+                       //
+                       // go modal
+                       //
+                       while (!m_driverThreadFinished)
+                       {
+                               MSG msg;
+               
+                               GetMessage( &msg, m_hWnd, 0, 0 );
+                               TranslateMessage(&msg);
+                               DispatchMessage(&msg);
+                       }
+
+                       //
+                       // Wait until child process exits.
+                       //
+                       dwResult = WaitForSingleObject( hThread, INFINITE );
+                       err = translate_errno( dwResult == WAIT_OBJECT_0, errno_compat(), err = kUnknownErr );
+                       require_noerr_with_log( log, "WaitForSingleObject()", err, exit );
+
+                       //
+                       // check the return value of thread
+                       //
+                       require_noerr_with_log( log, "thread exit code", m_driverThreadExitCode, exit );
+
+                       //
+                       // now we know that the driver was successfully installed
+                       //
+                       printer->driverInstalled = true;
+               }
+
+               if ( service->type == kPDLServiceType )
+               {
+                       err = InstallPrinterPort( printer, service, PROTOCOL_RAWTCP_TYPE, log );
+                       require_noerr_with_log( log, "InstallPrinterPort()", err, exit );
+                       err = InstallPrinterPDLAndLPR( printer, service, log );
+                       require_noerr_with_log( log, "InstallPrinterPDLAndLPR()", err, exit );
+               }
+               else if ( service->type == kLPRServiceType )
+               {
+                       err = InstallPrinterPort( printer, service, PROTOCOL_LPR_TYPE, log );
+                       require_noerr_with_log( log, "InstallPrinterPort()", err, exit );
+                       err = InstallPrinterPDLAndLPR( printer, service, log );
+                       require_noerr_with_log( log, "InstallPrinterPDLAndLPR()", err, exit );
+               }
+               else if ( service->type == kIPPServiceType )
+               {
+                       // There's no need to install a printer port for IPP printers, because
+                       // the call to AddPrinter() will do that for us.
+
+                       err = InstallPrinterIPP( printer, service, log );
+                       require_noerr_with_log( log, "InstallPrinterIPP()", err, exit );
+               }
+               else
+               {
+                       require_action_with_log( log, ( service->type == kPDLServiceType ) || ( service->type == kLPRServiceType ) || ( service->type == kIPPServiceType ), exit, err = kUnknownErr );
+               }
+       }
+
+       printer->installed = true;
+
+       //
+       // if the user specified a default printer, set it
+       //
+       if (printer->deflt)
+       {
+               ok = SetDefaultPrinter( printer->actualName );
+               err = translate_errno( ok, errno_compat(), err = kUnknownErr );
+               require_noerr_with_log( log, "SetDefaultPrinter()", err, exit );
+       }
+
+exit:
+
+       return err;
+}
+
+
+OSStatus
+CPrinterSetupWizardSheet::InstallPrinterPort( Printer * printer, Service * service, DWORD protocol, Logger & log )
 {
        PRINTER_DEFAULTS        printerDefaults =       { NULL,  NULL, SERVER_ACCESS_ADMINISTER };
 {
        PRINTER_DEFAULTS        printerDefaults =       { NULL,  NULL, SERVER_ACCESS_ADMINISTER };
+       PORT_DATA_1                     portData;
        DWORD                           dwStatus;
        DWORD                           cbInputData             =       100;
        PBYTE                           pOutputData             =       NULL;
        DWORD                           cbOutputNeeded  =       0;
        DWORD                           dwStatus;
        DWORD                           cbInputData             =       100;
        PBYTE                           pOutputData             =       NULL;
        DWORD                           cbOutputNeeded  =       0;
-       PORT_DATA_1                     portData;
        HANDLE                          hXcv                    =       NULL;
        HANDLE                          hXcv                    =       NULL;
-       HANDLE                          hPrinter                =       NULL;
+       Queue                   *       q;
        BOOL                            ok;
        OSStatus                        err;
 
        BOOL                            ok;
        OSStatus                        err;
 
-       check(printer != NULL);
-       check(printer->installed == false);
+       ZeroMemory(&portData, sizeof(PORT_DATA_1));
+
+       require_action_with_log( log, wcslen(printer->portName) < sizeof_array(portData.sztPortName), exit, err = kSizeErr );
+       wcscpy_s(portData.sztPortName, printer->portName);
+
+       q = service->queues.front();
+       check( q );
 
        ok = OpenPrinter(L",XcvMonitor Standard TCP/IP Port", &hXcv, &printerDefaults);
        err = translate_errno( ok, errno_compat(), kUnknownErr );
 
        ok = OpenPrinter(L",XcvMonitor Standard TCP/IP Port", &hXcv, &printerDefaults);
        err = translate_errno( ok, errno_compat(), kUnknownErr );
-       require_noerr( err, exit );
-       
+       require_noerr_with_log( log, "OpenPrinter()", err, exit );
+
        //
        // BUGBUG: MSDN said this is not required, but my experience shows it is required
        //
        //
        // BUGBUG: MSDN said this is not required, but my experience shows it is required
        //
@@ -317,113 +368,27 @@ CPrinterSetupWizardSheet::InstallPrinter(Printer * printer)
                pOutputData = NULL;
        }
 
                pOutputData = NULL;
        }
 
-       require_action( pOutputData, exit, err = kNoMemoryErr );
-       
-       //
-       // setup the port
-       //
-       ZeroMemory(&portData, sizeof(PORT_DATA_1));
-       wcscpy(portData.sztPortName, printer->portName);
+       require_action_with_log( log, pOutputData, exit, err = kNoMemoryErr );
        
        
-       portData.dwPortNumber   =       printer->portNumber;
+       portData.dwPortNumber   =       service->portNumber;
        portData.dwVersion              =       1;
        portData.dwVersion              =       1;
+       portData.dwDoubleSpool  =       1;
        
        
-       portData.dwProtocol     = PROTOCOL_RAWTCP_TYPE;
+       portData.dwProtocol     = protocol;
        portData.cbSize         = sizeof PORT_DATA_1;
        portData.dwReserved     = 0L;
        portData.cbSize         = sizeof PORT_DATA_1;
        portData.dwReserved     = 0L;
-       
-       wcscpy(portData.sztQueue, printer->hostname);
-       wcscpy(portData.sztIPAddress, printer->hostname); 
-       wcscpy(portData.sztHostAddress, printer->hostname);
-               
-       ok = XcvData(hXcv, L"AddPort", (PBYTE) &portData, sizeof(PORT_DATA_1), pOutputData, cbInputData,  &cbOutputNeeded, &dwStatus);
-       err = translate_errno( ok, errno_compat(), kUnknownErr );
-       require_noerr( err, exit );
-       
-       if (printer->driverInstalled)
-       {
-               PRINTER_INFO_2 pInfo;
-       
-               ZeroMemory(&pInfo, sizeof(pInfo));
-                       
-               pInfo.pPrinterName                      =       printer->actualName.GetBuffer();
-               pInfo.pServerName                       =       NULL;
-               pInfo.pShareName                        =       NULL;
-               pInfo.pPortName                         =       printer->portName.GetBuffer();
-               pInfo.pDriverName                       =       printer->model.GetBuffer();
-               pInfo.pComment                          =       printer->model.GetBuffer();
-               pInfo.pLocation                         =       L"";
-               pInfo.pDevMode                          =       NULL;
-               pInfo.pDevMode                          =       NULL;
-               pInfo.pSepFile                          =       L"";
-               pInfo.pPrintProcessor           =       L"winprint";
-               pInfo.pDatatype                         =       L"RAW";
-               pInfo.pParameters                       =       L"";
-               pInfo.pSecurityDescriptor       =       NULL;
-               pInfo.Attributes                        =       PRINTER_ATTRIBUTE_QUEUED;
-               pInfo.Priority                          =       0;
-               pInfo.DefaultPriority           =       0;
-               pInfo.StartTime                         =       0;
-               pInfo.UntilTime                         =       0;
-       
-               hPrinter = AddPrinter(NULL, 2, (LPBYTE) &pInfo);
-               err = translate_errno( hPrinter, errno_compat(), kUnknownErr );
-               require_noerr( err, exit );
-       }
-       else
-       {
-               DWORD           dwResult;
-               HANDLE          hThread;
-               unsigned        threadID;
-                       
-       
-               m_processFinished = false;
-       
-               //
-               // create the thread
-               //
-               hThread = (HANDLE) _beginthreadex_compat( NULL, 0, InstallPrinterThread, printer, 0, &threadID );
-               err = translate_errno( hThread, (OSStatus) GetLastError(), kUnknownErr );
-               require_noerr( err, exit );
-                       
-               //
-               // go modal
-               //
-               while (!m_processFinished)
-               {
-                       MSG msg;
-       
-                       GetMessage( &msg, m_hWnd, 0, 0 );
-                       TranslateMessage(&msg);
-                       DispatchMessage(&msg);
-               }
-       
-               //
-               // Wait until child process exits.
-               //
-               dwResult = WaitForSingleObject( hThread, INFINITE );
-               err = translate_errno( dwResult == WAIT_OBJECT_0, errno_compat(), err = kUnknownErr );
-               require_noerr( err, exit );
-       }
 
 
-       printer->installed = true;
+       require_action_with_log( log, wcslen(q->name) < sizeof_array(portData.sztQueue), exit, err = kSizeErr );
+       wcscpy_s(portData.sztQueue, q->name);
 
 
-       //
-       // if the user specified a default printer, set it
-       //
-       if (printer->deflt)
-       {
-               ok = SetDefaultPrinter(printer->actualName);
-               err = translate_errno( ok, errno_compat(), err = kUnknownErr );
-               require_noerr( err, exit );
-       }
+       require_action_with_log( log, wcslen( service->hostname ) < sizeof_array(portData.sztHostAddress), exit, err = kSizeErr );
+       wcscpy_s( portData.sztHostAddress, service->hostname );
 
 
-exit:
+       ok = XcvData(hXcv, L"AddPort", (PBYTE) &portData, sizeof(PORT_DATA_1), pOutputData, cbInputData,  &cbOutputNeeded, &dwStatus);
+       err = translate_errno( ok, errno_compat(), kUnknownErr );
+       require_noerr_with_log( log, "XcvData()", err, exit );
 
 
-       if (hPrinter != NULL)
-       {
-               ClosePrinter(hPrinter);
-       }
+exit:
 
        if (hXcv != NULL)
        {
 
        if (hXcv != NULL)
        {
@@ -439,19 +404,325 @@ exit:
 }
 
 
 }
 
 
-BEGIN_MESSAGE_MAP(CPrinterSetupWizardSheet, CPropertySheet)
-ON_MESSAGE( WM_SERVICE_EVENT, OnServiceEvent )
-ON_MESSAGE( WM_PROCESS_EVENT, OnProcessEvent )
-ON_WM_SETCURSOR()
-END_MESSAGE_MAP()
+OSStatus
+CPrinterSetupWizardSheet::InstallPrinterPDLAndLPR(Printer * printer, Service * service, Logger & log )
+{
+       PRINTER_INFO_2          pInfo;
+       HANDLE                          hPrinter = NULL;
+       Queue                   *       q;
+       OSStatus                        err;
 
 
+       check(printer != NULL);
+       check(printer->installed == false);
 
 
-// ------------------------------------------------------
-// OnCommand
-//
-// Traps when the user hits Finish  
-//     
-BOOL CPrinterSetupWizardSheet::OnCommand(WPARAM wParam, LPARAM lParam)
+       q = service->queues.front();
+       check( q );
+
+       //
+       // add the printer
+       //
+       ZeroMemory(&pInfo, sizeof(pInfo));
+               
+       pInfo.pPrinterName                      =       printer->actualName.GetBuffer();
+       pInfo.pServerName                       =       NULL;
+       pInfo.pShareName                        =       NULL;
+       pInfo.pPortName                         =       printer->portName.GetBuffer();
+       pInfo.pDriverName                       =       printer->modelName.GetBuffer();
+       pInfo.pComment                          =       printer->displayModelName.GetBuffer();
+       pInfo.pLocation                         =       q->location.GetBuffer();
+       pInfo.pDevMode                          =       NULL;
+       pInfo.pDevMode                          =       NULL;
+       pInfo.pSepFile                          =       L"";
+       pInfo.pPrintProcessor           =       L"winprint";
+       pInfo.pDatatype                         =       L"RAW";
+       pInfo.pParameters                       =       L"";
+       pInfo.pSecurityDescriptor       =       NULL;
+       pInfo.Attributes                        =       PRINTER_ATTRIBUTE_QUEUED;
+       pInfo.Priority                          =       0;
+       pInfo.DefaultPriority           =       0;
+       pInfo.StartTime                         =       0;
+       pInfo.UntilTime                         =       0;
+
+       hPrinter = AddPrinter(NULL, 2, (LPBYTE) &pInfo);
+       err = translate_errno( hPrinter, errno_compat(), kUnknownErr );
+       require_noerr_with_log( log, "AddPrinter()", err, exit );
+
+exit:
+
+       if (hPrinter != NULL)
+       {
+               ClosePrinter(hPrinter);
+       }
+
+       return err;
+}
+
+
+OSStatus
+CPrinterSetupWizardSheet::InstallPrinterIPP(Printer * printer, Service * service, Logger & log)
+{
+       DEBUG_UNUSED( service );
+
+       Queue           *       q                = service->SelectedQueue();
+       HANDLE                  hPrinter = NULL;
+       PRINTER_INFO_2  pInfo;
+       OSStatus                err;
+
+       check( q );
+       
+       //
+       // add the printer
+       //
+       ZeroMemory(&pInfo, sizeof(PRINTER_INFO_2));
+       
+       pInfo.pPrinterName              = printer->actualName.GetBuffer();
+       pInfo.pPortName                 = printer->portName.GetBuffer();
+       pInfo.pDriverName               = printer->modelName.GetBuffer();
+       pInfo.pPrintProcessor   = L"winprint";
+       pInfo.pLocation                 = q->location.GetBuffer();
+       pInfo.pComment                  = printer->displayModelName.GetBuffer();
+       pInfo.Attributes                = PRINTER_ATTRIBUTE_NETWORK | PRINTER_ATTRIBUTE_LOCAL;
+       
+       hPrinter = AddPrinter(NULL, 2, (LPBYTE)&pInfo);
+       err = translate_errno( hPrinter, errno_compat(), kUnknownErr );
+       require_noerr_with_log( log, "AddPrinter()", err, exit );
+
+exit:
+
+       if ( hPrinter != NULL )
+       {
+               ClosePrinter(hPrinter);
+       }
+
+       return err;
+}
+
+
+OSStatus
+CPrinterSetupWizardSheet::InstallPrinterCUPS(Printer * printer, Service * service, CUPSLibrary & cupsLib )
+{
+       OSStatus err = kNoErr;
+
+       check( printer );
+       check( service );
+       check( cupsLib.IsInstalled() );
+
+       err = InstallPrinterCUPS( printer, service, cupsLib, TEXT( "Windows NT x86" ) );
+       require_noerr( err, exit );
+
+       if ( Is64BitWindows() )
+       {
+               err = InstallPrinterCUPS( printer, service, cupsLib, TEXT( "Windows x64" ) );
+               require_noerr( err, exit );
+       }
+
+exit:
+
+       return err;
+}
+
+
+OSStatus
+CPrinterSetupWizardSheet::InstallPrinterCUPS(Printer * printer, Service * service, CUPSLibrary & cupsLib, TCHAR * env )
+{
+       
+       Queue           *       q;
+       CString                 ppdfile;                                // PPD file for printer drivers
+       TCHAR                   driverdir[1024];                // Directory for driver files
+       DWORD                   needed;                                 // Bytes needed
+       DRIVER_INFO_3   driverinfo;                             // Driver information
+       PRINTER_INFO_2  printerinfo;                    // Printer information
+       HANDLE                  printerHandle = NULL;   // Handle to printer
+       CString                 filename;                               // Driver filename
+       CString                 dependentFiles;                 // List of dependent files
+       CString                 portName;                               // Port Name
+       int                             bytes;                                  // Bytes copied
+       TCHAR                   datadir[ MAX_PATH ];    // Driver files location
+       CFile                   in;                                             // Input file
+       CFile                   out;                                    // Output file
+       void            *       http;                                   // Connection to server
+       char                    buffer[4096];                   // Copy/error buffer
+       CString                 platform;
+       char                    hostname[ 1024 ];
+       CString                 dest;
+       char                    destANSI[ 1024 ];
+       int                             i;
+       DWORD                   num;
+       OSStatus                err     = 0;
+       BOOL                    ok;
+
+       check( printer );
+       check( service );
+       check( cupsLib.IsInstalled() );
+       check( env );
+
+       // What do we do here for multiple queues?
+       q = service->queues.front();
+       require_action( q != NULL, exit, err = kUnknownErr );
+
+       num = GetModuleFileName( NULL, datadir, MAX_PATH );
+       err = translate_errno( num > 0, GetLastError(), kUnknownErr );
+       require_noerr( err, exit );
+       ok = PathRemoveFileSpec( datadir );
+       require_action( ok, exit, err = kUnknownErr );
+
+       ok = GetPrinterDriverDirectory(NULL, env, 1, ( LPBYTE ) driverdir, sizeof( driverdir ), &needed );
+       err = translate_errno( ok, GetLastError(), kUnknownErr );
+       require_noerr( err, exit );
+
+       platform = env;
+       platform = platform.Right( 3 );
+
+       // Append the supported banner pages to the PPD file...
+       err = StringObjectToUTF8String( service->hostname, hostname, sizeof( hostname ) );
+       require_noerr( err, exit );
+       http = cupsLib.httpConnectEncrypt( hostname, service->portNumber, cupsLib.cupsEncryption() );
+       err = translate_errno( http != NULL, errno, kUnknownErr );
+       require_noerr( err, exit );
+
+       if ( ( service->portNumber == 443 ) || ( cupsLib.cupsEncryption() >= HTTP_ENCRYPT_REQUIRED ) )
+       {
+               // This forces the use the https: URLs below...
+               cupsLib.cupsSetEncryption( HTTP_ENCRYPT_ALWAYS );
+       }
+
+       // Strip the leading "printers/" or "classes/" from the beginning
+       // of the name
+
+       dest = q->name;
+       dest.Replace( TEXT( "printers/" ), TEXT( "" ) );
+       dest.Replace( TEXT( "classes/" ), TEXT( "" ) );
+
+       err = StringObjectToUTF8String( dest, destANSI, sizeof( destANSI ) );
+       require_noerr( err, exit );
+
+       // Get the PPD file...
+       for ( i = 0; i < 10; i++ )
+       {
+               char ppdfileANSI[ 1024 ];
+
+               if ( cupsLib.cupsAdminCreateWindowsPPD( http, destANSI, ppdfileANSI, sizeof( ppdfileANSI ) ) )
+               {
+                       err = UTF8StringToStringObject( ppdfileANSI, ppdfile );
+                       require_noerr( err, exit );
+                       break;
+               }
+       }
+
+       err = translate_errno( i < 10, errno, kUnknownErr );
+       require_noerr( err, exit );
+
+       // Copy the PPD file to the Windows driver directory...
+       filename.Format( TEXT( "%s/%s.ppd" ), driverdir, dest );
+
+       ok = in.Open( ppdfile, CFile::modeRead | CFile::typeBinary );
+       translate_errno( ok, GetLastError(), kUnknownErr );
+       require_noerr( err, exit );
+
+       ok = out.Open( filename, CFile::modeCreate | CFile::modeWrite | CFile::typeBinary );
+       translate_errno( ok, GetLastError(), kUnknownErr );
+       require_noerr( err, exit );
+
+       while ( ( bytes = in.Read( buffer, sizeof(buffer) ) ) > 0 )
+       {
+               out.Write(buffer, bytes );
+       }
+
+       in.Close();
+       out.Close();
+
+       // Cleanup temp file...
+       CFile::Remove( ppdfile );
+
+       // Copy the driver files to the driver directory...
+       for ( i = 0; i < ( sizeof( g_printerDriverFiles ) / sizeof( g_printerDriverFiles[0] ) ); i++ )
+       {
+               filename.Format( TEXT( "%s/drivers/%s/%s" ), datadir, platform, g_printerDriverFiles[i]);
+       
+               ok = in.Open(filename, CFile::modeRead | CFile::typeBinary );
+               err = translate_errno( ok, GetLastError(), kUnknownErr );
+               require_noerr( err, exit );
+
+               filename.Format( TEXT( "%s/%s" ), driverdir, g_printerDriverFiles[i] );
+               ok = out.Open(filename, CFile::modeCreate | CFile::modeWrite | CFile::typeBinary );
+               err = translate_errno( ok, errno, kUnknownErr );
+
+               while ( ( bytes = in.Read(buffer, sizeof( buffer ) ) ) > 0 )
+               {
+                       out.Write( buffer, bytes );
+               }
+
+               in.Close();
+               out.Close();
+       }
+
+       // Do the Windows system calls needed to add the printer driver...
+       filename.Format( TEXT( "%s.ppd" ), dest);
+       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);
+
+       driverinfo.cVersion         = 3;
+       driverinfo.pName            = printer->actualName.GetBuffer();
+       driverinfo.pEnvironment     = env;
+       driverinfo.pDriverPath      = TEXT( "pscript5.dll" );
+       driverinfo.pDataFile        = filename.GetBuffer();
+       driverinfo.pConfigFile      = TEXT( "ps5ui.dll" );
+       driverinfo.pHelpFile        = TEXT( "pscript.hlp" );
+       driverinfo.pDependentFiles  = dependentFiles.GetBuffer();
+       driverinfo.pMonitorName     = NULL;
+       driverinfo.pDefaultDataType = TEXT( "raw" );
+
+       ok = AddPrinterDriverEx(NULL, 3, (LPBYTE) &driverinfo, APD_COPY_ALL_FILES );
+       err = translate_errno( ok, GetLastError(), kUnknownErr );
+       require_noerr( err, exit );
+
+       // See if the printer has already been added?
+       if ( OpenPrinter( printer->actualName.GetBuffer(), &printerHandle, NULL ) )
+    {
+               // Printer already exists, so we are done now...
+               goto exit;
+    }
+
+    // Add the printer using the HTTP/IPP port...
+       portName.Format( TEXT( "%s://%s:%d/printers/%s" ), cupsLib.cupsEncryption() == HTTP_ENCRYPT_ALWAYS ? TEXT( "https" ) : TEXT( "http" ), service->hostname.GetBuffer(), service->portNumber, dest );
+
+    memset(&printerinfo, 0, sizeof(printerinfo));
+    printerinfo.pPrinterName   = printer->actualName.GetBuffer();
+    printerinfo.pPortName              = portName.GetBuffer();
+    printerinfo.pDriverName            = printer->actualName.GetBuffer();
+    printerinfo.Attributes             = PRINTER_ATTRIBUTE_NETWORK | PRINTER_ATTRIBUTE_LOCAL;
+       printerinfo.pComment            = q->description.GetBuffer();
+       printerinfo.pLocation           = q->location.GetBuffer();
+       printerinfo.pPrintProcessor = TEXT( "winprint" );
+
+    printerHandle = AddPrinter( NULL, 2, (LPBYTE) &printerinfo );
+       err = translate_errno( printerHandle, GetLastError(), kUnknownErr );
+       require_noerr( err, exit );
+
+exit:
+
+       if ( printerHandle != NULL )
+       {
+               ClosePrinter( printerHandle );
+               printerHandle = NULL;
+       }
+
+       return err;
+}
+
+BEGIN_MESSAGE_MAP(CPrinterSetupWizardSheet, CPropertySheet)
+ON_MESSAGE( WM_SOCKET_EVENT, OnSocketEvent )
+ON_MESSAGE( WM_PROCESS_EVENT, OnProcessEvent )
+ON_WM_SETCURSOR()
+ON_WM_TIMER()
+END_MESSAGE_MAP()
+
+
+// ------------------------------------------------------
+// OnCommand
+//
+// Traps when the user hits Finish  
+//     
+BOOL CPrinterSetupWizardSheet::OnCommand(WPARAM wParam, LPARAM lParam)
 {
        //
        // Check if this is OK
 {
        //
        // Check if this is OK
@@ -468,39 +739,43 @@ BOOL CPrinterSetupWizardSheet::OnCommand(WPARAM wParam, LPARAM lParam)
 // ------------------------------------------------------
 // OnInitDialog
 //
 // ------------------------------------------------------
 // OnInitDialog
 //
-// Initializes this Dialog object.  We start the browse here,
-// so that printers show up instantly when the user clicks
-// the next button.  
+// Initializes this Dialog object.
 //     
 BOOL CPrinterSetupWizardSheet::OnInitDialog()
 {
        OSStatus err;
 
        CPropertySheet::OnInitDialog();
 //     
 BOOL CPrinterSetupWizardSheet::OnInitDialog()
 {
        OSStatus err;
 
        CPropertySheet::OnInitDialog();
-       
-       //
-       // setup the DNS-SD browsing
-       //
-       err = DNSServiceBrowse( &m_pdlBrowser, 0, 0, kPDLDataStreamServiceType, NULL, OnBrowse, this );
-       require_noerr( err, exit );
-
-       m_serviceRefList.push_back(m_pdlBrowser);
 
 
-       err = WSAAsyncSelect((SOCKET) DNSServiceRefSockFD(m_pdlBrowser), m_hWnd, WM_SERVICE_EVENT, FD_READ|FD_CLOSE);
+       err = StartBrowse();
        require_noerr( err, exit );
 
        require_noerr( err, exit );
 
-       LoadPrinterNames();
-
 exit:
 
 exit:
 
-       if (err != kNoErr)
+       if ( err )
        {
        {
-               WizardException exc;
+               StopBrowse();
+
+               if ( err == kDNSServiceErr_Firewall )
+               {
+                       CString text, caption;
+
+                       text.LoadString( IDS_FIREWALL );
+                       caption.LoadString( IDS_FIREWALL_CAPTION );
+
+                       MessageBox(text, caption, MB_OK|MB_ICONEXCLAMATION);
+               }
+               else
+               {
+                       CString text, caption;
+
+                       text.LoadString( IDS_NO_MDNSRESPONDER_SERVICE_TEXT );
+                       caption.LoadString( IDS_ERROR_CAPTION );
 
 
-               exc.text.LoadString(IDS_NO_MDNSRESPONDER_SERVICE_TEXT);
-               exc.caption.LoadString(IDS_NO_MDNSRESPONDER_SERVICE_CAPTION);
+                       MessageBox(text, caption, MB_OK|MB_ICONEXCLAMATION);
 
 
-               throw(exc);
+                       _exit( 0 );
+               }
        }
 
        return TRUE;
        }
 
        return TRUE;
@@ -551,20 +826,38 @@ CPrinterSetupWizardSheet::OnContextMenu(CWnd * pWnd, CPoint pos)
 void
 CPrinterSetupWizardSheet::OnOK()
 {
 void
 CPrinterSetupWizardSheet::OnOK()
 {
+       CWnd * window;
+       OSStatus err;
+
        check ( m_selectedPrinter != NULL );
 
        SetWizardButtons( PSWIZB_DISABLEDFINISH );
 
        check ( m_selectedPrinter != NULL );
 
        SetWizardButtons( PSWIZB_DISABLEDFINISH );
 
-       if ( InstallPrinter( m_selectedPrinter ) != kNoErr )
+       window = GetDlgItem( IDCANCEL );
+       if ( window )
+       {
+               window->EnableWindow( FALSE );
+       }
+
+       m_pgFourth.StartActivityIndicator();
+       
+       err = InstallPrinter( m_selectedPrinter );
+
+       m_pgFourth.StopActivityIndicator();
+
+       if ( err != kNoErr )
        {
                CString caption;
                CString message;
 
                caption.LoadString(IDS_INSTALL_ERROR_CAPTION);
        {
                CString caption;
                CString message;
 
                caption.LoadString(IDS_INSTALL_ERROR_CAPTION);
+               caption.AppendFormat( TEXT( " (%d)" ), err );
                message.LoadString(IDS_INSTALL_ERROR_MESSAGE);
                message.LoadString(IDS_INSTALL_ERROR_MESSAGE);
-
                MessageBox(message, caption, MB_OK|MB_ICONEXCLAMATION);
        }
                MessageBox(message, caption, MB_OK|MB_ICONEXCLAMATION);
        }
+
+       StopBrowse();
 }
 
 
 }
 
 
@@ -572,7 +865,6 @@ CPrinterSetupWizardSheet::OnOK()
 
 void CPrinterSetupWizardSheet::Init(void)
 {
 
 void CPrinterSetupWizardSheet::Init(void)
 {
-       AddPage(&m_pgFirst);
        AddPage(&m_pgSecond);
        AddPage(&m_pgThird);
        AddPage(&m_pgFourth);
        AddPage(&m_pgSecond);
        AddPage(&m_pgThird);
        AddPage(&m_pgFourth);
@@ -583,14 +875,14 @@ void CPrinterSetupWizardSheet::Init(void)
        m_psh.pszbmWatermark = MAKEINTRESOURCE(IDB_WATERMARK);
        m_psh.pszbmHeader = MAKEINTRESOURCE(IDB_BANNER_ICON);
 
        m_psh.pszbmWatermark = MAKEINTRESOURCE(IDB_WATERMARK);
        m_psh.pszbmHeader = MAKEINTRESOURCE(IDB_BANNER_ICON);
 
-       m_psh.hInstance = AfxGetInstanceHandle();
+       m_psh.hInstance = GetNonLocalizedResources();
 
        SetWizardMode();
 }
 
 
 
        SetWizardMode();
 }
 
 
-LONG
-CPrinterSetupWizardSheet::OnServiceEvent(WPARAM inWParam, LPARAM inLParam)
+LRESULT
+CPrinterSetupWizardSheet::OnSocketEvent(WPARAM inWParam, LPARAM inLParam)
 {
        if (WSAGETSELECTERROR(inLParam) && !(HIWORD(inLParam)))
     {
 {
        if (WSAGETSELECTERROR(inLParam) && !(HIWORD(inLParam)))
     {
@@ -622,13 +914,80 @@ CPrinterSetupWizardSheet::OnServiceEvent(WPARAM inWParam, LPARAM inLParam)
 }
 
 
 }
 
 
-LONG
+LRESULT
 CPrinterSetupWizardSheet::OnProcessEvent(WPARAM inWParam, LPARAM inLParam)
 {
 CPrinterSetupWizardSheet::OnProcessEvent(WPARAM inWParam, LPARAM inLParam)
 {
-       DEBUG_UNUSED(inWParam);
        DEBUG_UNUSED(inLParam);
 
        DEBUG_UNUSED(inLParam);
 
-       m_processFinished = true;
+       m_driverThreadExitCode  =       (DWORD) inWParam;
+       m_driverThreadFinished  =       true;
+
+       return 0;
+}
+
+
+unsigned WINAPI
+CPrinterSetupWizardSheet::InstallDriverThread( LPVOID inParam )
+{      
+       Printer                 *       printer = (Printer*) inParam;
+       DWORD                           exitCode = 0;
+       DWORD                           dwResult;
+       OSStatus                        err;
+       STARTUPINFO                     si;
+       PROCESS_INFORMATION pi;
+       BOOL                            ok;
+
+       check( printer );
+       check( m_self );
+
+       //
+       // because we're calling endthreadex(), C++ objects won't be cleaned up
+       // correctly.  we'll nest the CString 'command' inside a block so
+       // that it's destructor will be invoked.
+       //
+       {
+               CString command;
+
+               ZeroMemory( &si, sizeof(si) );
+               si.cb = sizeof(si);
+               ZeroMemory( &pi, sizeof(pi) );
+
+               command.Format(L"rundll32.exe printui.dll,PrintUIEntry /ia /m \"%s\" /f \"%s\"", (LPCTSTR) printer->modelName, (LPCTSTR) printer->infFileName );
+
+               ok = CreateProcess(NULL, command.GetBuffer(), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
+               err = translate_errno( ok, errno_compat(), kUnknownErr );
+               require_noerr( err, exit );
+
+               dwResult = WaitForSingleObject( pi.hProcess, INFINITE );
+               translate_errno( dwResult == WAIT_OBJECT_0, errno_compat(), err = kUnknownErr );
+               require_noerr( err, exit );
+
+               ok = GetExitCodeProcess( pi.hProcess, &exitCode );
+               err = translate_errno( ok, errno_compat(), kUnknownErr );
+               require_noerr( err, exit );
+       }
+
+exit:
+
+       //
+       // Close process and thread handles. 
+       //
+       if ( pi.hProcess )
+       {
+               CloseHandle( pi.hProcess );
+       }
+
+       if ( pi.hThread )
+       {
+               CloseHandle( pi.hThread );
+       }
+
+       //
+       // alert the main thread
+       //
+       m_self->PostMessage( WM_PROCESS_EVENT, err, exitCode );
+
+       _endthreadex_compat( 0 );
 
        return 0;
 }
 
        return 0;
 }
@@ -636,141 +995,90 @@ CPrinterSetupWizardSheet::OnProcessEvent(WPARAM inWParam, LPARAM inLParam)
 
 void DNSSD_API
 CPrinterSetupWizardSheet::OnBrowse(
 
 void DNSSD_API
 CPrinterSetupWizardSheet::OnBrowse(
-                                                               DNSServiceRef                   inRef,
-                                                               DNSServiceFlags                 inFlags,
-                                                               uint32_t                                inInterfaceIndex,
-                                                               DNSServiceErrorType     inErrorCode,
-                                                               const char *                    inName, 
-                                                               const char *                    inType, 
-                                                               const char *                    inDomain,       
-                                                               void *                                  inContext )
+                                                       DNSServiceRef                   inRef,
+                                                       DNSServiceFlags                 inFlags,
+                                                       uint32_t                                inInterfaceIndex,
+                                                       DNSServiceErrorType     inErrorCode,
+                                                       const char *                    inName, 
+                                                       const char *                    inType, 
+                                                       const char *                    inDomain,       
+                                                       void *                                  inContext )
 {
        DEBUG_UNUSED(inRef);
 
        CPrinterSetupWizardSheet        *       self;
 {
        DEBUG_UNUSED(inRef);
 
        CPrinterSetupWizardSheet        *       self;
-       Printer                                         *       printer;
-       EventHandlerList::iterator              it;
-       DWORD                                                   printerNameCount;
        bool                                                    moreComing = (bool) (inFlags & kDNSServiceFlagsMoreComing);
        bool                                                    moreComing = (bool) (inFlags & kDNSServiceFlagsMoreComing);
+       CPropertyPage                           *       active;
+       Printer                                         *       printer = NULL;
+       Service                                         *       service = NULL;
+       OSStatus                                                err = kNoErr;
 
        require_noerr( inErrorCode, exit );
 
        require_noerr( inErrorCode, exit );
-       
+
        self = reinterpret_cast <CPrinterSetupWizardSheet*>( inContext );
        require_quiet( self, exit );
 
        self = reinterpret_cast <CPrinterSetupWizardSheet*>( inContext );
        require_quiet( self, exit );
 
-       printer = self->LookUp(inName);
+       active = self->GetActivePage();
+       require_quiet( active, exit );
+
+       // Have we seen this printer before?
+
+       printer = self->Lookup( inName );
+
+       if ( printer )
+       {
+               service = printer->LookupService( inType );
+       }
 
 
-       if (inFlags & kDNSServiceFlagsAdd)
+       if ( inFlags & kDNSServiceFlagsAdd )
        {
        {
-               OSStatus err;
+               BOOL newPrinter = FALSE;
 
 
-               if (printer != NULL)
+               if ( !printer )
                {
                {
-                       printer->refs++;
+                       printer = self->OnAddPrinter( inInterfaceIndex, inName, inType, inDomain, moreComing );
+                       require_action( printer, exit, err = kUnknownErr );
+                       newPrinter = TRUE;
                }
                }
-               else
+               
+               // If we're looking at the browse list on page 2, then we need to call
+               // CPage2::OnAddPrinter() regardless of whether we've seen the printer
+               // or not because the moreComing flag might have changed from a previous
+               // call. If we only call CPage2::OnAddPrinter() when there's a new printer,
+               // we might not correctly update our UI, so if we've seen the printer before,
+               // call OnAddPrinter with a NULL parameter.
+
+               if ( self->GetActivePage() == &self->m_pgSecond )
                {
                {
-                       try
-                       {
-                               printer = new Printer;
-                       }
-                       catch (...)
-                       {
-                               printer = NULL;
-                       }
-
-                       require_action( printer, exit, err = E_OUTOFMEMORY );
-
-                       printer->window         =       self;
-                       printer->ifi            =       inInterfaceIndex;
-                       printer->name           =       inName;
-                       err = UTF8StringToStringObject(inName, printer->displayName);
-                       check_noerr( err );
-                       printer->actualName     =       printer->displayName;
-
-                       //
-                       // Compare this name against printers that are already installed
-                       // to avoid name clashes.  Rename as necessary
-                       // to come up with a unique name.
-                       //
-                       printerNameCount = 2;
-
-                       for (;;)
-                       {
-                               PrinterNameMap::iterator it;
-
-                               it = self->m_printerNames.find(printer->actualName);
-
-                               if (it != self->m_printerNames.end())
-                               {
-                                       printer->actualName.Format(L"%s (%d)", printer->displayName, printerNameCount);
-                               }
-                               else
-                               {
-                                       break;
-                               }
-
-                               printerNameCount++;
-                       }
-
-                       printer->type           =       inType;
-                       printer->domain         =       inDomain;
-                       printer->installed      =       false;
-                       printer->deflt          =       false;
-                       printer->refs           =       1;
-                       
-                       self->m_printerList.push_back( printer );
-
-                       //
-                       // now invoke event handlers for AddPrinter event
-                       //
-                       for (it = self->m_eventHandlerList.begin(); it != self->m_eventHandlerList.end(); it++)
-                       {
-                               EventHandler * handler = *it;
+                       self->m_pgSecond.OnAddPrinter( newPrinter ? printer : NULL, moreComing );
+               }
 
 
-                               handler->OnAddPrinter(printer, moreComing);
-                       }
+               if ( !service )
+               {
+                       err = self->OnAddService( printer, inInterfaceIndex, inName, inType, inDomain );
+                       require_noerr( err, exit );
+               }
+               else
+               {
+                       service->refs++;
                }
        }
                }
        }
-       else
+       else if ( printer )
        {
        {
-               if ((printer != NULL) && (--printer->refs == 0))
-               {
-                       //
-                       // now invoke event handlers for RemovePrinter event
-                       //
-                       for (it = self->m_eventHandlerList.begin(); it != self->m_eventHandlerList.end(); it++)
-                       {
-                               EventHandler * handler = *it;
-
-                               handler->OnRemovePrinter(printer, moreComing);
-                       }
-
-                       self->m_printerList.remove(printer);
+               check( service );
 
 
-                       //
-                       // check to see if we've selected this printer
-                       //
-                       if (self->m_selectedPrinter == printer)
-                       {
-                               //
-                               // this guy is being removed while we're resolving it...so let's 
-                               // stop the resolve
-                               //
-                               if (printer->serviceRef != NULL)
-                               {
-                                       self->StopResolve(printer);
-                               }
-
-                               self->m_selectedPrinter = NULL;
-                       }
+               err = self->OnRemoveService( service );
+               require_noerr( err, exit );
 
 
-                       delete printer;
+               if ( printer->services.size() == 0 )
+               {
+                       err = self->OnRemovePrinter( printer, moreComing );
+                       require_noerr( err, exit );
                }
        }
 
 exit:
                }
        }
 
 exit:
-
+       
        return;
 }
 
        return;
 }
 
@@ -793,184 +1101,547 @@ CPrinterSetupWizardSheet::OnResolve(
        DEBUG_UNUSED(inFlags);
        DEBUG_UNUSED(inRef);
 
        DEBUG_UNUSED(inFlags);
        DEBUG_UNUSED(inRef);
 
-       Printer                                         *       printer;
        CPrinterSetupWizardSheet        *       self;
        CPrinterSetupWizardSheet        *       self;
-       EventHandlerList::iterator              it1;
-       EventHandlerList::iterator              it2;
+       Service                                         *       service;
+       Queue                                           *       q;
        int                                                             idx;
        OSStatus                                                err;
 
        require_noerr( inErrorCode, exit );
 
        int                                                             idx;
        OSStatus                                                err;
 
        require_noerr( inErrorCode, exit );
 
-       printer = reinterpret_cast<Printer*>( inContext );
-       require_quiet( printer, exit);
+       service = reinterpret_cast<Service*>( inContext );
+       require_quiet( service, exit);
 
 
-       self = printer->window;
+       check( service->refs != 0 );
+
+       self = service->printer->window;
        require_quiet( self, exit );
 
        require_quiet( self, exit );
 
-       err = self->StopResolve(printer);
-       require_noerr(err, exit);
+       err = self->StopOperation( service->serviceRef );
+       require_noerr( err, exit );
        
        //
        // hold on to the hostname...
        //
        
        //
        // hold on to the hostname...
        //
-       err = UTF8StringToStringObject( inHostName, printer->hostname );
+       err = UTF8StringToStringObject( inHostName, service->hostname );
        require_noerr( err, exit );
 
        //
        // <rdar://problem/3739200> remove the trailing dot on hostname
        //
        require_noerr( err, exit );
 
        //
        // <rdar://problem/3739200> remove the trailing dot on hostname
        //
-       idx = printer->hostname.ReverseFind('.');
+       idx = service->hostname.ReverseFind('.');
 
 
-       if ((idx > 1) && ((printer->hostname.GetLength() - 1) == idx))
+       if ((idx > 1) && ((service->hostname.GetLength() - 1) == idx))
        {
        {
-               printer->hostname.Delete(idx, 1);
+               service->hostname.Delete(idx, 1);
        }
 
        //
        // hold on to the port
        //
        }
 
        //
        // hold on to the port
        //
-       printer->portNumber = ntohs(inPort);
+       service->portNumber = ntohs(inPort);
 
 
-       //
-       // parse the text record.  we create a stringlist of text record
-       // entries that can be interrogated later
-       //
-       while (inTXTSize)
-       {
-               char buf[256];
+       if ( service->qtotal == 1 )
+       {       
+               //
+               // create a new queue
+               //
+               try
+               {
+                       q = new Queue;
+               }
+               catch (...)
+               {
+                       q = NULL;
+               }
 
 
-               unsigned char num = *inTXT;
-               check( (int) num < inTXTSize );
+               require_action( q, exit, err = E_OUTOFMEMORY );
 
 
-               memset(buf, 0, sizeof(buf));
-               memcpy(buf, inTXT + 1, num);
-               
-               inTXTSize -= (num + 1);
-               inTXT += (num + 1);
+               //
+               // parse the text record.
+               //
+
+               err = self->ParseTextRecord( service, q, inTXTSize, inTXT );
+               require_noerr( err, exit );
+
+               service->queues.push_back( q );
 
 
-               CString elem;
+               //
+               // we've completely resolved this service
+               //
 
 
-               err = UTF8StringToStringObject( buf, elem );
+               self->OnResolveService( service );
+       }
+       else
+       {
+               //
+               // if qtotal is more than 1, then we need to get additional
+               // text records.  if not, then this service is considered
+               // resolved
+               //
+
+               err = DNSServiceQueryRecord(&service->serviceRef, 0, inInterfaceIndex, inFullName, kDNSServiceType_TXT, kDNSServiceClass_IN, OnQuery, (void*) service );
+               require_noerr( err, exit );
+
+               err = self->StartOperation( service->serviceRef );
                require_noerr( err, exit );
                require_noerr( err, exit );
+       }
+
+exit:
+
+       return;
+}
+
+
+void DNSSD_API
+CPrinterSetupWizardSheet::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)
+{
+       DEBUG_UNUSED( inTTL );
+       DEBUG_UNUSED( inRRClass );
+       DEBUG_UNUSED( inRRType );
+       DEBUG_UNUSED( inFullName );
+       DEBUG_UNUSED( inInterfaceIndex );
+       DEBUG_UNUSED( inRef );
+
+       Service                                         *       service = NULL;
+       Queue                                           *       q;
+       CPrinterSetupWizardSheet        *       self;
+       OSStatus                                                err = kNoErr;
+
+       require_noerr( inErrorCode, exit );
+
+       service = reinterpret_cast<Service*>( inContext );
+       require_quiet( service, exit);
+
+       self = service->printer->window;
+       require_quiet( self, exit );
+
+       if ( ( inFlags & kDNSServiceFlagsAdd ) && ( inRDLen > 0 ) && ( inRData != NULL ) )
+       {
+               const char * inTXT = ( const char * ) inRData;
+
+               //
+               // create a new queue
+               //
+               try
+               {
+                       q = new Queue;
+               }
+               catch (...)
+               {
+                       q = NULL;
+               }
 
 
-               int curPos = 0;
+               require_action( q, exit, err = E_OUTOFMEMORY );
 
 
-               CString key = elem.Tokenize(L"=", curPos);
-               CString val = elem.Tokenize(L"=", curPos);
+               err = service->printer->window->ParseTextRecord( service, q, inRDLen, inTXT );
+               require_noerr( err, exit );
+
+               //
+               // add this queue
+               //
 
 
-               key.MakeLower();
+               service->queues.push_back( q );
 
 
-               if ((key == L"usb_mfg") || (key == L"usb_manufacturer"))
+               if ( service->queues.size() == service->qtotal )
                {
                {
-                       printer->usb_MFG = val;
+                       //
+                       // else if moreComing is not set, then we're going
+                       // to assume that we're done
+                       //
+
+                       self->StopOperation( service->serviceRef );
+
+                       //
+                       // sort the queues
+                       //
+
+                       service->queues.sort( OrderQueueFunc );
+
+                       //
+                       // we've completely resolved this service
+                       //
+
+                       self->OnResolveService( service );
                }
                }
-               else if ((key == L"usb_mdl") || (key == L"usb_model"))
+       }
+
+exit:
+
+       if ( err && service && ( service->serviceRef != NULL ) )
+       {
+               service->printer->window->StopOperation( service->serviceRef );
+       }
+
+       return;
+}
+
+
+Printer*
+CPrinterSetupWizardSheet::OnAddPrinter(
+                                                               uint32_t                inInterfaceIndex,
+                                                               const char *    inName, 
+                                                               const char *    inType, 
+                                                               const char *    inDomain,
+                                                               bool                    moreComing)
+{
+       Printer *       printer = NULL;
+       DWORD           printerNameCount;
+       OSStatus        err;
+
+       DEBUG_UNUSED( inInterfaceIndex );
+       DEBUG_UNUSED( inType );
+       DEBUG_UNUSED( inDomain );
+       DEBUG_UNUSED( moreComing );
+
+       try
+       {
+               printer = new Printer;
+       }
+       catch (...)
+       {
+               printer = NULL;
+       }
+
+       require_action( printer, exit, err = E_OUTOFMEMORY );
+
+       printer->window         =       this;
+       printer->name           =       inName;
+       
+       err = UTF8StringToStringObject(inName, printer->displayName);
+       check_noerr( err );
+       printer->actualName     =       printer->displayName;
+       printer->installed      =       false;
+       printer->deflt          =       false;
+       printer->resolving      =       0;
+
+       // Compare this name against printers that are already installed
+       // to avoid name clashes.  Rename as necessary
+       // to come up with a unique name.
+
+       printerNameCount = 2;
+
+       for (;;)
+       {
+               CPrinterSetupWizardSheet::PrinterNames::iterator it;
+
+               // <rdar://problem/4141221> Don't use find to do comparisons because we need to
+               // do a case insensitive string comparison
+
+               for ( it = m_printerNames.begin(); it != m_printerNames.end(); it++ )
                {
                {
-                       printer->usb_MDL = val;
+                       if ( (*it).CompareNoCase( printer->actualName ) == 0 )
+                       {
+                               break;
+                       }
                }
                }
-               else if (key == L"description")
+
+               if (it != m_printerNames.end())
                {
                {
-                       printer->description = val;
+                       printer->actualName.Format(L"%s (%d)", printer->displayName, printerNameCount);
                }
                }
-               else if (key == L"product")
+               else
                {
                {
-                       printer->product = val;
+                       break;
                }
                }
+
+               printerNameCount++;
        }
 
        }
 
+       m_printers.push_back( printer );
+
+exit:
+
+       return printer;
+}
+
+
+OSStatus
+CPrinterSetupWizardSheet::OnAddService(
+                                                               Printer         *       printer,
+                                                               uint32_t                inInterfaceIndex,
+                                                               const char      *       inName, 
+                                                               const char      *       inType, 
+                                                               const char      *       inDomain)
+{
+       Service *       service = NULL;
+       OSStatus        err     = kNoErr;
+
+       DEBUG_UNUSED( inName );
+       DEBUG_UNUSED( inDomain );
+
+       try
+       {
+               service = new Service;
+       }
+       catch (...)
+       {
+               service = NULL;
+       }
+
+       require_action( service, exit, err = E_OUTOFMEMORY );
+       
+       service->printer        =       printer;
+       service->ifi            =       inInterfaceIndex;
+       service->type           =       inType;
+       service->domain         =       inDomain;
+       service->qtotal         =       1;
+       service->refs           =       1;
+       service->serviceRef     =       NULL;
+
+       printer->services.push_back( service );
+
        //
        //
-       // now invoke event handlers for Resolve event
+       // if the printer is selected, then we'll want to start a
+       // resolve on this guy
        //
        //
-       it1 = self->m_eventHandlerList.begin();
-       it2 = self->m_eventHandlerList.end();
 
 
-       while (it1 != it2)
+       if ( printer == m_selectedPrinter )
        {
        {
-               EventHandler * handler = *it1++;
-
-               handler->OnResolvePrinter(printer);
+               StartResolve( service );
        }
 
 exit:
 
        }
 
 exit:
 
-       return;
+       return err;
 }
 
 
 OSStatus
 }
 
 
 OSStatus
-CPrinterSetupWizardSheet::LoadPrinterNames()
+CPrinterSetupWizardSheet::OnRemovePrinter( Printer * printer, bool moreComing )
 {
 {
-       PBYTE           buffer  =       NULL;
-       OSStatus        err             = 0;
+       CPropertyPage   *       active  = GetActivePage();
+       OSStatus                        err             = kNoErr;
 
 
-       //
-       // rdar://problem/3701926 - Printer can't be installed twice
-       //
-       // First thing we want to do is make sure the printer isn't already installed.
-       // If the printer name is found, we'll try and rename it until we
-       // find a unique name
-       //
-       DWORD dwNeeded = 0, dwNumPrinters = 0;
+       if ( active == &m_pgSecond )
+       {
+               m_pgSecond.OnRemovePrinter( printer, moreComing );
+       }
 
 
-       BOOL ok = EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 4, NULL, 0, &dwNeeded, &dwNumPrinters);
-       err = translate_errno( ok, errno_compat(), kUnknownErr );
+       m_printers.remove( printer );
 
 
-       if ((err == ERROR_INSUFFICIENT_BUFFER) && (dwNeeded > 0))
+       if ( m_selectedPrinter == printer )
        {
        {
-               try
+               m_selectedPrinter = NULL;
+
+               if ( ( active == &m_pgThird ) || ( active == &m_pgFourth ) )
                {
                {
-                       buffer = new unsigned char[dwNeeded];
+                       CString caption;
+                       CString message;
+
+                       caption.LoadString( IDS_ERROR_CAPTION );
+                       message.LoadString( IDS_PRINTER_UNAVAILABLE );
+
+                       MessageBox(message, caption, MB_OK|MB_ICONEXCLAMATION);
+
+                       SetActivePage( &m_pgSecond );
                }
                }
-               catch (...)
+       }
+
+       delete printer;
+
+       return err;
+}
+
+
+OSStatus
+CPrinterSetupWizardSheet::OnRemoveService( Service * service )
+{
+       OSStatus err = kNoErr;
+
+       if ( service && ( --service->refs == 0 ) )
+       {
+               if ( service->serviceRef != NULL )
                {
                {
-                       buffer = NULL;
+                       err = StopResolve( service );
+                       require_noerr( err, exit );
                }
                }
+
+               service->printer->services.remove( service );
+
+               delete service;
+       }
+
+exit:
+
+       return err;     
+}
+
+
+void
+CPrinterSetupWizardSheet::OnResolveService( Service * service )
+{
+       // Make sure that the active page is page 2
+
+       require_quiet( GetActivePage() == &m_pgSecond, exit );
+
+       if ( !--service->printer->resolving )
+       {
+               // sort the services now.  we want the service that
+               // has the highest priority queue to be first in
+               // the list.
+
+               service->printer->services.sort( OrderServiceFunc );
+
+               // Now we can hit next
+
+               SetWizardButtons( PSWIZB_BACK|PSWIZB_NEXT );
        
        
-               require_action( buffer, exit, kNoMemoryErr );
-               ok = EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 4, buffer, dwNeeded, &dwNeeded, &dwNumPrinters);
-               err = translate_errno( ok, errno_compat(), kUnknownErr );
-               require_noerr( err, exit );
+               // Reset the cursor     
+               
+               m_active = m_arrow;
 
 
-               for (DWORD index = 0; index < dwNumPrinters; index++)
-               {
-                       PRINTER_INFO_4 * lppi4 = (PRINTER_INFO_4*) (buffer + index * sizeof(PRINTER_INFO_4));
+               // And tell page 2 about it
+
+               m_pgSecond.OnResolveService( service );
+       }               
+
+exit:
+
+       return;
+}
+
+
+OSStatus
+CPrinterSetupWizardSheet::StartBrowse()
+{
+       OSStatus err;
+
+       //
+       // setup the DNS-SD browsing
+       //
+       err = DNSServiceBrowse( &m_pdlBrowser, 0, 0, kPDLServiceType, NULL, OnBrowse, this );
+       require_noerr( err, exit );
+
+       err = StartOperation( m_pdlBrowser );
+       require_noerr( err, exit );
+
+       err = DNSServiceBrowse( &m_lprBrowser, 0, 0, kLPRServiceType, NULL, OnBrowse, this );
+       require_noerr( err, exit );
+
+       err = StartOperation( m_lprBrowser );
+       require_noerr( err, exit );
+
+       err = DNSServiceBrowse( &m_ippBrowser, 0, 0, kIPPServiceType, NULL, OnBrowse, this );
+       require_noerr( err, exit );
+
+       err = StartOperation( m_ippBrowser );
+       require_noerr( err, exit );
 
 
-                       m_printerNames[lppi4->pPrinterName] = lppi4->pPrinterName;
+exit:
+
+       return err;
+}
+
+
+OSStatus
+CPrinterSetupWizardSheet::StopBrowse()
+{
+       OSStatus err;
+
+       err = StopOperation( m_pdlBrowser );
+       require_noerr( err, exit );
+
+       err = StopOperation( m_lprBrowser );
+       require_noerr( err, exit );
+
+       err = StopOperation( m_ippBrowser );
+       require_noerr( err, exit );
+
+       while ( m_printers.size() > 0 )
+       {
+               Printer * printer = m_printers.front();
+
+               m_printers.pop_front();
+
+               if ( printer->resolving )
+               {
+                       StopResolve( printer );
                }
                }
+
+               delete printer;
        }
 
 exit:
 
        }
 
 exit:
 
-       if (buffer != NULL)
+       return err;
+}
+
+
+OSStatus
+CPrinterSetupWizardSheet::StartResolve( Printer * printer )
+{
+       OSStatus                        err = kNoErr;
+       Services::iterator      it;
+
+       check( printer );
+
+       for ( it = printer->services.begin(); it != printer->services.end(); it++ )
        {
        {
-               delete [] buffer;
+               if ( (*it)->serviceRef == NULL )
+               {
+                       err = StartResolve( *it );
+                       require_noerr( err, exit );
+               }
        }
 
        }
 
+       m_selectedPrinter = printer;
+
+exit:
+
        return err;
 }
 
 
 OSStatus
        return err;
 }
 
 
 OSStatus
-CPrinterSetupWizardSheet::StartResolve(Printer * printer)
+CPrinterSetupWizardSheet::StartResolve( Service * service )
 {
 {
-       OSStatus err;
+       OSStatus err = kNoErr;
 
 
-       check( printer );
+       check( service->serviceRef == NULL );
 
 
-       err = DNSServiceResolve( &printer->serviceRef, 0, 0, printer->name.c_str(), printer->type.c_str(), printer->domain.c_str(), (DNSServiceResolveReply) OnResolve, printer );
-       require_noerr( err, exit);
+       //
+       // clean out any queues that were collected during a previous
+       // resolve
+       //
 
 
-       m_serviceRefList.push_back(printer->serviceRef);
+       service->EmptyQueues();
 
 
-       err = WSAAsyncSelect((SOCKET) DNSServiceRefSockFD(printer->serviceRef), m_hWnd, WM_SERVICE_EVENT, FD_READ|FD_CLOSE);
+       //
+       // now start the new resolve
+       //
+
+       err = DNSServiceResolve( &service->serviceRef, 0, 0, service->printer->name.c_str(), service->type.c_str(), service->domain.c_str(), (DNSServiceResolveReply) OnResolve, service );
+       require_noerr( err, exit );
+
+       err = StartOperation( service->serviceRef );
        require_noerr( err, exit );
 
        //
        require_noerr( err, exit );
 
        //
-       // set the cursor to arrow+hourglass
+       // If we're not currently resolving, then disable the next button
+       // and set the cursor to hourglass
        //
        //
-       m_active = m_wait;
-       SetCursor(m_active);
+
+       if ( !service->printer->resolving )
+       {
+               SetWizardButtons( PSWIZB_BACK );
+
+               m_active = m_wait;
+               SetCursor(m_active);
+       }
+
+       service->printer->resolving++;
 
 exit:
 
 
 exit:
 
@@ -981,105 +1652,298 @@ exit:
 OSStatus
 CPrinterSetupWizardSheet::StopResolve(Printer * printer)
 {
 OSStatus
 CPrinterSetupWizardSheet::StopResolve(Printer * printer)
 {
-       OSStatus err;
+       OSStatus err = kNoErr;
 
        check( printer );
 
        check( printer );
-       check( printer->serviceRef );
 
 
-       m_serviceRefList.remove( printer->serviceRef );
+       Services::iterator it;
 
 
-       err = WSAAsyncSelect((SOCKET) DNSServiceRefSockFD(printer->serviceRef), m_hWnd, 0, 0);
-       check(err == 0);        
+       for ( it = printer->services.begin(); it != printer->services.end(); it++ )
+       {
+               if ( (*it)->serviceRef )
+               {
+                       err = StopResolve( *it );
+                       require_noerr( err, exit );
+               }
+       }
 
 
-       DNSServiceRefDeallocate( printer->serviceRef );
+exit:
+
+       return err;
+}
 
 
-       printer->serviceRef = NULL;
-       
-       //
-       // set the cursor back to normal
-       //
-       m_active = m_arrow;
-       SetCursor(m_active);
 
 
-       return kNoErr;
+OSStatus
+CPrinterSetupWizardSheet::StopResolve( Service * service )
+{
+       OSStatus err;
+
+       check( service->serviceRef );
+
+       err = StopOperation( service->serviceRef );
+       require_noerr( err, exit );
+
+       service->printer->resolving--;
+
+exit:
+
+       return err;
 }
 
 
 }
 
 
-Printer*
-CPrinterSetupWizardSheet::LookUp(const char * inName)
+OSStatus
+CPrinterSetupWizardSheet::StartOperation( DNSServiceRef ref )
+{
+       OSStatus err;
+
+       err = WSAAsyncSelect((SOCKET) DNSServiceRefSockFD(ref), m_hWnd, WM_SOCKET_EVENT, FD_READ|FD_CLOSE);
+       require_noerr( err, exit );
+
+       m_serviceRefList.push_back( ref );
+
+exit:
+
+       return err;
+}
+
+
+OSStatus
+CPrinterSetupWizardSheet::StopOperation( DNSServiceRef & ref )
 {
 {
-       PrinterList::iterator it1 = m_printerList.begin();
-       PrinterList::iterator it2 = m_printerList.end();
+       OSStatus err = kNoErr;
 
 
-       while (it1 != it2)
+       if ( ref )
        {
        {
-               Printer * printer = *it1++;
+               m_serviceRefList.remove( ref );
 
 
-               if (printer->name == inName)
+               if ( IsWindow( m_hWnd ) )
                {
                {
-                       return printer;
+                       err = WSAAsyncSelect((SOCKET) DNSServiceRefSockFD( ref ), m_hWnd, 0, 0 );
+                       require_noerr( err, exit );
                }
                }
+
+               DNSServiceRefDeallocate( ref );
+               ref = NULL;
        }
 
        }
 
-       return NULL;
+exit:
+
+       return err;
 }
 
 
 }
 
 
-unsigned WINAPI
-CPrinterSetupWizardSheet::InstallPrinterThread( LPVOID inParam )
+OSStatus
+CPrinterSetupWizardSheet::ParseTextRecord( Service * service, Queue * q, uint16_t inTXTSize, const char * inTXT )
 {
 {
-       check( inParam );
-               
-       Printer                 *       printer = (Printer*) inParam;
-       CString                         actualName;
-       CString                         command;
-       DWORD                           exitCode = 0;
-       DWORD                           dwResult;
-       OSStatus                        err;
-       STARTUPINFO                     si;
-       PROCESS_INFORMATION pi;
-       BOOL                            ok;
+       check( service );
+       check( q );
 
 
-       ZeroMemory( &si, sizeof(si) );
-       si.cb = sizeof(si);
-       ZeroMemory( &pi, sizeof(pi) );
+       // <rdar://problem/3946587> Use TXTRecord APIs declared in dns_sd.h
+       
+       bool                    qtotalDefined   = false;
+       const void      *       val;
+       char                    buf[256];
+       uint8_t                 len;
+       OSStatus                err                             = kNoErr;
 
 
-       //
-       // <rdar://problem/3764873> Escape '\', '@', '"' characters which seem to cause problems for printui
-       //
+       // <rdar://problem/3987680> Default to queue "lp"
 
 
-       actualName = printer->actualName;
+       q->name = L"lp";
 
 
-       actualName.Replace(L"\\", L"\\\\");
-       actualName.Replace(L"@", L"\\@");
-       actualName.Replace(L"\"", L"\\\"");
+       // <rdar://problem/4003710> Default pdl key to be "application/postscript"
+
+       q->pdl = L"application/postscript";
+
+       if ( ( val = TXTRecordGetValuePtr( inTXTSize, inTXT, "rp", &len ) ) != NULL )
+       {
+               // Stringize val ( doesn't have trailing '\0' yet )
+
+               memcpy( buf, val, len );
+               buf[len] = '\0';
+
+               err = UTF8StringToStringObject( buf, q->name );
+               require_noerr( err, exit );
+       }
        
        
-       command.Format(L"rundll32.exe printui.dll,PrintUIEntry /if /b \"%s\" /f \"%s\" /r \"%s\" /m \"%s\"", (LPCTSTR) actualName, (LPCTSTR) printer->infFileName, (LPCTSTR) printer->portName, (LPCTSTR) printer->model);
+       if ( ( val = TXTRecordGetValuePtr( inTXTSize, inTXT, "pdl", &len ) ) != NULL )
+       {
+               // Stringize val ( doesn't have trailing '\0' yet )
 
 
-       ok = CreateProcess(NULL, command.GetBuffer(), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
-       err = translate_errno( ok, errno_compat(), kUnknownErr );
-       require_noerr( err, exit );
+               memcpy( buf, val, len );
+               buf[len] = '\0';
 
 
-       dwResult = WaitForSingleObject( pi.hProcess, INFINITE );
-       translate_errno( dwResult == WAIT_OBJECT_0, errno_compat(), err = kUnknownErr );
-       require_noerr( err, exit );
+               err = UTF8StringToStringObject( buf, q->pdl );
+               require_noerr( err, exit );
+       }
+       
+       if ( ( ( val = TXTRecordGetValuePtr( inTXTSize, inTXT, "usb_mfg", &len ) ) != NULL ) ||
+            ( ( val = TXTRecordGetValuePtr( inTXTSize, inTXT, "usb_manufacturer", &len ) ) != NULL ) )
+       {
+               // Stringize val ( doesn't have trailing '\0' yet )
 
 
-       ok = GetExitCodeProcess( pi.hProcess, &exitCode );
-       err = translate_errno( ok, errno_compat(), kUnknownErr );
-       require_noerr( err, exit );
+               memcpy( buf, val, len );
+               buf[len] = '\0';
 
 
-       //
-       // Close process and thread handles. 
-       //
-       CloseHandle( pi.hProcess );
-       CloseHandle( pi.hThread );
+               err = UTF8StringToStringObject( buf, q->usb_MFG );
+               require_noerr( err, exit );
+       }
+       
+       if ( ( ( val = TXTRecordGetValuePtr( inTXTSize, inTXT, "usb_mdl", &len ) ) != NULL ) ||
+            ( ( val = TXTRecordGetValuePtr( inTXTSize, inTXT, "usb_model", &len ) ) != NULL ) )
+       {
+               // Stringize val ( doesn't have trailing '\0' yet )
+
+               memcpy( buf, val, len );
+               buf[len] = '\0';
+
+               err = UTF8StringToStringObject( buf, q->usb_MDL );
+               require_noerr( err, exit );
+       }
+
+       if ( ( val = TXTRecordGetValuePtr( inTXTSize, inTXT, "ty", &len ) ) != NULL )
+       {
+               // Stringize val ( doesn't have trailing '\0' yet )
+
+               memcpy( buf, val, len );
+               buf[len] = '\0';
+
+               err = UTF8StringToStringObject( buf, q->description );
+               require_noerr( err, exit );
+       }
+               
+       if ( ( val = TXTRecordGetValuePtr( inTXTSize, inTXT, "product", &len ) ) != NULL )
+       {
+               // Stringize val ( doesn't have trailing '\0' yet )
+
+               memcpy( buf, val, len );
+               buf[len] = '\0';
+
+               err = UTF8StringToStringObject( buf, q->product );
+               require_noerr( err, exit );
+       }
+
+       if ( ( val = TXTRecordGetValuePtr( inTXTSize, inTXT, "note", &len ) ) != NULL )
+       {
+               // Stringize val ( doesn't have trailing '\0' yet )
+
+               memcpy( buf, val, len );
+               buf[len] = '\0';
+
+               err = UTF8StringToStringObject( buf, q->location );
+               require_noerr( err, exit );
+       }
+
+       if ( ( val = TXTRecordGetValuePtr( inTXTSize, inTXT, "qtotal", &len ) ) != NULL )
+       {
+               // Stringize val ( doesn't have trailing '\0' yet )
+
+               memcpy( buf, val, len );
+               buf[len] = '\0';
+
+               service->qtotal = (unsigned short) atoi( buf );
+               qtotalDefined = true;
+       }
+
+       if ( ( val = TXTRecordGetValuePtr( inTXTSize, inTXT, "priority", &len ) ) != NULL )
+       {
+               // Stringize val ( doesn't have trailing '\0' yet )
+
+               memcpy( buf, val, len );
+               buf[len] = '\0';
+
+               q->priority = atoi( buf );
+       }
+
+       // <rdar://problem/4124524> Was this printer discovered via OS X Printer Sharing?
+
+       if ( TXTRecordContainsKey( inTXTSize, inTXT, "printer-state" ) || TXTRecordContainsKey( inTXTSize, inTXT, "printer-type" ) )
+       {
+               service->printer->isCUPSPrinter = true;
+       }
 
 exit:
 
 
 exit:
 
-       //
-       // alert the main thread
-       //
-       printer->window->PostMessage( WM_PROCESS_EVENT, err, exitCode );
+       // The following code is to fix a problem with older HP 
+       // printers that don't include "qtotal" in their text
+       // record.  We'll check to see if the q->name is "TEXT"
+       // and if so, we're going to modify it to be "lp" so
+       // that we don't use the wrong queue
 
 
-       return 0;
+       if ( !err && !qtotalDefined && ( q->name == L"TEXT" ) )
+       {
+               q->name = "lp";
+       }
+
+       return err;
+}
+
+
+Printer*
+CPrinterSetupWizardSheet::Lookup(const char * inName)
+{
+       check( inName );
+
+       Printer                 *       printer = NULL;
+       Printers::iterator      it;
+
+       for ( it = m_printers.begin(); it != m_printers.end(); it++ )
+       {
+               if ( (*it)->name == inName )
+               {
+                       printer = *it;
+                       break;
+               }
+       }
+
+       return printer;
+}
+
+
+bool
+CPrinterSetupWizardSheet::OrderServiceFunc( const Service * a, const Service * b )
+{
+       Queue * q1, * q2;
+
+       q1 = (a->queues.size() > 0) ? a->queues.front() : NULL;
+
+       q2 = (b->queues.size() > 0) ? b->queues.front() : NULL;
+
+       if ( !q1 && !q2 )
+       {
+               return true;
+       }
+       else if ( q1 && !q2 )
+       {
+               return true;
+       }
+       else if ( !q1 && q2 )
+       {
+               return false;
+       }
+       else if ( q1->priority < q2->priority )
+       {
+               return true;
+       }
+       else if ( q1->priority > q2->priority )
+       {
+               return false;
+       }
+       else if ( ( a->type == kPDLServiceType ) || ( ( a->type == kLPRServiceType ) && ( b->type == kIPPServiceType ) ) )
+       {
+               return true;
+       }
+       else
+       {
+               return false;
+       }
 }
 }
+
+
+bool
+CPrinterSetupWizardSheet::OrderQueueFunc( const Queue * q1, const Queue * q2 )
+{
+       return ( q1->priority <= q2->priority ) ? true : false;
+}
+
+
+