]> git.saurik.com Git - apple/mdnsresponder.git/blobdiff - Clients/PrinterSetupWizard/PrinterSetupWizardSheet.cpp
mDNSResponder-176.3.tar.gz
[apple/mdnsresponder.git] / Clients / PrinterSetupWizard / PrinterSetupWizardSheet.cpp
index 6b026e1fad0e695d788184f255693bbe5136c943..e912716b3a2e0284a0f2976ac8c7ec086c415e3b 100644 (file)
@@ -1,28 +1,61 @@
-/*
+/* -*- Mode: C; tab-width: 4 -*-
+ *
  * 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.
- * 
- * @APPLE_LICENSE_HEADER_END@
 
     Change History (most recent first):
     
 $Log: PrinterSetupWizardSheet.cpp,v $
+Revision 1.35  2006/08/14 23:24:09  cheshire
+Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
+
+Revision 1.34  2005/10/05 17:32:51  herscher
+<rdar://problem/4141221> Use a case insensitive compare operation to check whether a printer with the same name has already been installed.
+
+Revision 1.33  2005/07/11 20:17:15  shersche
+<rdar://problem/4124524> UI fixes associated with CUPS printer workaround fix.
+
+Revision 1.32  2005/07/07 17:53:20  shersche
+Fix problems associated with the CUPS printer workaround fix.
+
+Revision 1.31  2005/06/30 18:02:54  shersche
+<rdar://problem/4124524> Workaround for Mac OS X Printer Sharing bug
+
+Revision 1.30  2005/04/13 17:46:22  shersche
+<rdar://problem/4082122> Generic PCL not selected when printers advertise multiple text records
+
+Revision 1.29  2005/02/14 20:48:37  shersche
+<rdar://problem/4003710> Default pdl key to "application/postscript"
+
+Revision 1.28  2005/02/14 20:37:53  shersche
+<rdar://problem/4003944> Populate comment field with the model name that users see in the wizard UI.
+
+Revision 1.27  2005/02/09 05:04:03  shersche
+<rdar://problem/3946587> Use TXTRecordGetValuePtr() API in ParseTextRecord
+
+Revision 1.26  2005/02/08 21:45:06  shersche
+<rdar://problem/3947490> Default to Generic PostScript or PCL if unable to match driver
+
+Revision 1.25  2005/02/08 18:54:17  shersche
+<rdar://problem/3987680> Default queue name is "lp" when rp key is not specified.
+
+Revision 1.24  2005/02/01 02:15:55  shersche
+<rdar://problem/3946587> Use TXTRecord parsing APIs in ParseTextRecord
+
+Revision 1.23  2005/01/31 23:54:30  shersche
+<rdar://problem/3947508> Start browsing when printer wizard starts. Move browsing logic from CSecondPage object to CPrinterSetupWizardSheet object.
+
 Revision 1.22  2005/01/25 18:49:43  shersche
 Get icon resources from resource DLL
 
@@ -127,7 +160,8 @@ First checked in
 
 // Private Messages
 
-#define WM_PROCESS_EVENT       ( WM_USER + 0x100 )
+#define WM_SOCKET_EVENT                ( WM_USER + 0x100 )
+#define WM_PROCESS_EVENT       ( WM_USER + 0x101 )
 
 
 // CPrinterSetupWizardSheet
@@ -138,7 +172,11 @@ CPrinterSetupWizardSheet::CPrinterSetupWizardSheet(UINT nIDCaption, CWnd* pParen
        :CPropertySheet(nIDCaption, pParentWnd, iSelectPage),
        m_selectedPrinter(NULL),
        m_driverThreadExitCode( 0 ),
-       m_driverThreadFinished( false )
+       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);
@@ -146,15 +184,21 @@ CPrinterSetupWizardSheet::CPrinterSetupWizardSheet(UINT nIDCaption, CWnd* pParen
        m_self          =       this;
        
        Init();
+
+       LoadPrinterNames();
 }
 
 
 CPrinterSetupWizardSheet::~CPrinterSetupWizardSheet()
 {
-       if ( m_selectedPrinter != NULL )
+       Printer * printer;
+
+       while ( m_printers.size() > 0 )
        {
-               delete m_selectedPrinter;
-               m_selectedPrinter = NULL;
+               printer = m_printers.front();
+               m_printers.pop_front();
+
+               delete printer;
        }
 
        m_self = NULL;
@@ -176,6 +220,60 @@ CPrinterSetupWizardSheet::SetSelectedPrinter(Printer * printer)
 }
 
 
+OSStatus
+CPrinterSetupWizardSheet::LoadPrinterNames()
+{
+       PBYTE           buffer  =       NULL;
+       OSStatus        err             = 0;
+
+       //
+       // 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;
+
+       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))
+       {
+               try
+               {
+                       buffer = new unsigned char[dwNeeded];
+               }
+               catch (...)
+               {
+                       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 );
+
+               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 );
+               }
+       }
+
+exit:
+
+       if (buffer != NULL)
+       {
+               delete [] buffer;
+       }
+
+       return err;
+}
+
+
+
 // ------------------------------------------------------
 // InstallPrinter
 //
@@ -204,6 +302,7 @@ CPrinterSetupWizardSheet::InstallPrinter(Printer * printer)
        //
        // if the driver isn't installed, then install it
        //
+
        if ( !printer->driverInstalled )
        {
                DWORD           dwResult;
@@ -313,7 +412,7 @@ CPrinterSetupWizardSheet::InstallPrinterPDLAndLPR(Printer * printer, Service * s
        ok = OpenPrinter(L",XcvMonitor Standard TCP/IP Port", &hXcv, &printerDefaults);
        err = translate_errno( ok, errno_compat(), kUnknownErr );
        require_noerr( err, exit );
-       
+
        //
        // BUGBUG: MSDN said this is not required, but my experience shows it is required
        //
@@ -344,7 +443,7 @@ CPrinterSetupWizardSheet::InstallPrinterPDLAndLPR(Printer * printer, Service * s
        wcscpy(portData.sztQueue, q->name);
        wcscpy(portData.sztIPAddress, service->hostname); 
        wcscpy(portData.sztHostAddress, service->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 );
@@ -358,9 +457,9 @@ CPrinterSetupWizardSheet::InstallPrinterPDLAndLPR(Printer * printer, Service * s
        pInfo.pServerName                       =       NULL;
        pInfo.pShareName                        =       NULL;
        pInfo.pPortName                         =       printer->portName.GetBuffer();
-       pInfo.pDriverName                       =       printer->model.GetBuffer();
-       pInfo.pComment                          =       printer->model.GetBuffer();
-       pInfo.pLocation                         =       service->location.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"";
@@ -404,9 +503,12 @@ CPrinterSetupWizardSheet::InstallPrinterIPP(Printer * printer, Service * service
 {
        DEBUG_UNUSED( service );
 
+       Queue           *       q                = service->SelectedQueue();
        HANDLE                  hPrinter = NULL;
        PRINTER_INFO_2  pInfo;
        OSStatus                err;
+
+       check( q );
        
        //
        // add the printer
@@ -415,9 +517,10 @@ CPrinterSetupWizardSheet::InstallPrinterIPP(Printer * printer, Service * service
        
        pInfo.pPrinterName              = printer->actualName.GetBuffer();
        pInfo.pPortName                 = printer->portName.GetBuffer();
-       pInfo.pDriverName               = printer->model.GetBuffer();
+       pInfo.pDriverName               = printer->modelName.GetBuffer();
        pInfo.pPrintProcessor   = L"winprint";
-       pInfo.pLocation                 = service->location.GetBuffer();
+       pInfo.pLocation                 = q->location.GetBuffer();
+       pInfo.pComment                  = printer->displayModelName.GetBuffer();
        pInfo.Attributes                = PRINTER_ATTRIBUTE_NETWORK | PRINTER_ATTRIBUTE_LOCAL;
        
        hPrinter = AddPrinter(NULL, 2, (LPBYTE)&pInfo);
@@ -436,6 +539,7 @@ exit:
 
 
 BEGIN_MESSAGE_MAP(CPrinterSetupWizardSheet, CPropertySheet)
+ON_MESSAGE( WM_SOCKET_EVENT, OnSocketEvent )
 ON_MESSAGE( WM_PROCESS_EVENT, OnProcessEvent )
 ON_WM_SETCURSOR()
 ON_WM_TIMER()
@@ -468,8 +572,39 @@ BOOL CPrinterSetupWizardSheet::OnCommand(WPARAM wParam, LPARAM lParam)
 //     
 BOOL CPrinterSetupWizardSheet::OnInitDialog()
 {
+       OSStatus err;
+       
        CPropertySheet::OnInitDialog();
 
+       err = StartBrowse();
+       require_noerr( err, exit );
+
+exit:
+
+       if ( err )
+       {
+               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
+               {
+                       CPrinterSetupWizardSheet::WizardException exc;
+                       
+                       exc.text.LoadString( IDS_NO_MDNSRESPONDER_SERVICE_TEXT );
+                       exc.caption.LoadString( IDS_ERROR_CAPTION );
+                       
+                       throw(exc);
+               }
+       }
+
        return TRUE;
 }
 
@@ -532,6 +667,8 @@ CPrinterSetupWizardSheet::OnOK()
 
                MessageBox(message, caption, MB_OK|MB_ICONEXCLAMATION);
        }
+
+       StopBrowse();
 }
 
 
@@ -556,6 +693,39 @@ void CPrinterSetupWizardSheet::Init(void)
 }
 
 
+LONG
+CPrinterSetupWizardSheet::OnSocketEvent(WPARAM inWParam, LPARAM inLParam)
+{
+       if (WSAGETSELECTERROR(inLParam) && !(HIWORD(inLParam)))
+    {
+               dlog( kDebugLevelError, "OnServiceEvent: window error\n" );
+    }
+    else
+    {
+               SOCKET sock = (SOCKET) inWParam;
+
+               // iterate thru list
+               ServiceRefList::iterator begin = m_serviceRefList.begin();
+               ServiceRefList::iterator end   = m_serviceRefList.end();
+
+               while (begin != end)
+               {
+                       DNSServiceRef ref = *begin++;
+
+                       check(ref != NULL);
+
+                       if ((SOCKET) DNSServiceRefSockFD(ref) == sock)
+                       {
+                               DNSServiceProcessResult(ref);
+                               break;
+                       }
+               }
+       }
+
+       return ( 0 );
+}
+
+
 LONG
 CPrinterSetupWizardSheet::OnProcessEvent(WPARAM inWParam, LPARAM inLParam)
 {
@@ -594,7 +764,7 @@ CPrinterSetupWizardSheet::InstallDriverThread( LPVOID inParam )
                si.cb = sizeof(si);
                ZeroMemory( &pi, sizeof(pi) );
 
-               command.Format(L"rundll32.exe printui.dll,PrintUIEntry /ia /m \"%s\" /f \"%s\"", (LPCTSTR) printer->model, (LPCTSTR) printer->infFileName );
+               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 );
@@ -633,3 +803,950 @@ exit:
 
        return 0;
 }
+
+
+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 )
+{
+       DEBUG_UNUSED(inRef);
+
+       CPrinterSetupWizardSheet        *       self;
+       bool                                                    moreComing = (bool) (inFlags & kDNSServiceFlagsMoreComing);
+       CPropertyPage                           *       active;
+       Printer                                         *       printer = NULL;
+       Service                                         *       service = NULL;
+       OSStatus                                                err = kNoErr;
+
+       require_noerr( inErrorCode, exit );
+       
+       self = reinterpret_cast <CPrinterSetupWizardSheet*>( inContext );
+       require_quiet( self, exit );
+
+       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 (printer == NULL)
+               {
+                       // If not, then create a new one
+
+                       printer = self->OnAddPrinter( inInterfaceIndex, inName, inType, inDomain, moreComing );
+                       require_action( printer, exit, err = kUnknownErr );
+               }
+
+               if ( !service )
+               {
+                       err = self->OnAddService( printer, inInterfaceIndex, inName, inType, inDomain );
+                       require_noerr( err, exit );
+               }
+               else
+               {
+                       service->refs++;
+               }
+       }
+       else if ( printer )
+       {
+               check( service );
+
+               err = self->OnRemoveService( service );
+               require_noerr( err, exit );
+
+               if ( printer->services.size() == 0 )
+               {
+                       err = self->OnRemovePrinter( printer, moreComing );
+                       require_noerr( err, exit );
+               }
+       }
+
+exit:
+       
+       return;
+}
+
+
+void DNSSD_API
+CPrinterSetupWizardSheet::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 )
+{
+       DEBUG_UNUSED(inFullName);
+       DEBUG_UNUSED(inInterfaceIndex);
+       DEBUG_UNUSED(inFlags);
+       DEBUG_UNUSED(inRef);
+
+       CPrinterSetupWizardSheet        *       self;
+       Service                                         *       service;
+       Queue                                           *       q;
+       int                                                             idx;
+       OSStatus                                                err;
+
+       require_noerr( inErrorCode, exit );
+
+       service = reinterpret_cast<Service*>( inContext );
+       require_quiet( service, exit);
+
+       check( service->refs != 0 );
+
+       self = service->printer->window;
+       require_quiet( self, exit );
+
+       err = self->StopOperation( service->serviceRef );
+       require_noerr( err, exit );
+       
+       //
+       // hold on to the hostname...
+       //
+       err = UTF8StringToStringObject( inHostName, service->hostname );
+       require_noerr( err, exit );
+
+       //
+       // <rdar://problem/3739200> remove the trailing dot on hostname
+       //
+       idx = service->hostname.ReverseFind('.');
+
+       if ((idx > 1) && ((service->hostname.GetLength() - 1) == idx))
+       {
+               service->hostname.Delete(idx, 1);
+       }
+
+       //
+       // hold on to the port
+       //
+       service->portNumber = ntohs(inPort);
+
+       if ( service->qtotal == 1 )
+       {       
+               //
+               // create a new queue
+               //
+               try
+               {
+                       q = new Queue;
+               }
+               catch (...)
+               {
+                       q = NULL;
+               }
+
+               require_action( q, exit, err = E_OUTOFMEMORY );
+
+               //
+               // parse the text record.
+               //
+
+               err = self->ParseTextRecord( service, q, inTXTSize, inTXT );
+               require_noerr( err, exit );
+
+               service->queues.push_back( q );
+
+               //
+               // we've completely resolved this service
+               //
+
+               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 );
+       }
+
+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;
+               }
+
+               require_action( q, exit, err = E_OUTOFMEMORY );
+
+               err = service->printer->window->ParseTextRecord( service, q, inRDLen, inTXT );
+               require_noerr( err, exit );
+
+               //
+               // add this queue
+               //
+
+               service->queues.push_back( q );
+
+               if ( service->queues.size() == service->qtotal )
+               {
+                       //
+                       // 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 );
+               }
+       }
+
+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 );
+
+       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++ )
+               {
+                       if ( (*it).CompareNoCase( printer->actualName ) == 0 )
+                       {
+                               break;
+                       }
+               }
+
+               if (it != m_printerNames.end())
+               {
+                       printer->actualName.Format(L"%s (%d)", printer->displayName, printerNameCount);
+               }
+               else
+               {
+                       break;
+               }
+
+               printerNameCount++;
+       }
+
+       m_printers.push_back( printer );
+
+       if ( GetActivePage() == &m_pgSecond )
+       {
+               m_pgSecond.OnAddPrinter( printer, moreComing );
+       }
+
+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 );
+
+       //
+       // if the printer is selected, then we'll want to start a
+       // resolve on this guy
+       //
+
+       if ( printer == m_selectedPrinter )
+       {
+               StartResolve( service );
+       }
+
+exit:
+
+       return err;
+}
+
+
+OSStatus
+CPrinterSetupWizardSheet::OnRemovePrinter( Printer * printer, bool moreComing )
+{
+       CPropertyPage   *       active  = GetActivePage();
+       OSStatus                        err             = kNoErr;
+
+       if ( active == &m_pgSecond )
+       {
+               m_pgSecond.OnRemovePrinter( printer, moreComing );
+       }
+
+       m_printers.remove( printer );
+
+       if ( m_selectedPrinter == printer )
+       {
+               m_selectedPrinter = NULL;
+
+               if ( ( active == &m_pgThird ) || ( active == &m_pgFourth ) )
+               {
+                       CString caption;
+                       CString message;
+
+                       caption.LoadString( IDS_ERROR_CAPTION );
+                       message.LoadString( IDS_PRINTER_UNAVAILABLE );
+
+                       MessageBox(message, caption, MB_OK|MB_ICONEXCLAMATION);
+
+                       SetActivePage( &m_pgSecond );
+               }
+       }
+
+       delete printer;
+
+       return err;
+}
+
+
+OSStatus
+CPrinterSetupWizardSheet::OnRemoveService( Service * service )
+{
+       OSStatus err = kNoErr;
+
+       if ( service && ( --service->refs == 0 ) )
+       {
+               if ( service->serviceRef != 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 );
+       
+               // Reset the cursor     
+               
+               m_active = m_arrow;
+
+               // 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 );
+
+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:
+
+       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++ )
+       {
+               if ( (*it)->serviceRef == NULL )
+               {
+                       err = StartResolve( *it );
+                       require_noerr( err, exit );
+               }
+       }
+
+       m_selectedPrinter = printer;
+
+exit:
+
+       return err;
+}
+
+
+OSStatus
+CPrinterSetupWizardSheet::StartResolve( Service * service )
+{
+       OSStatus err = kNoErr;
+
+       check( service->serviceRef == NULL );
+
+       //
+       // clean out any queues that were collected during a previous
+       // resolve
+       //
+
+       service->EmptyQueues();
+
+       //
+       // 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 );
+
+       //
+       // If we're not currently resolving, then disable the next button
+       // and set the cursor to hourglass
+       //
+
+       if ( !service->printer->resolving )
+       {
+               SetWizardButtons( PSWIZB_BACK );
+
+               m_active = m_wait;
+               SetCursor(m_active);
+       }
+
+       service->printer->resolving++;
+
+exit:
+
+       return err;
+}
+
+
+OSStatus
+CPrinterSetupWizardSheet::StopResolve(Printer * printer)
+{
+       OSStatus err = kNoErr;
+
+       check( printer );
+
+       Services::iterator it;
+
+       for ( it = printer->services.begin(); it != printer->services.end(); it++ )
+       {
+               if ( (*it)->serviceRef )
+               {
+                       err = StopResolve( *it );
+                       require_noerr( err, exit );
+               }
+       }
+
+exit:
+
+       return err;
+}
+
+
+OSStatus
+CPrinterSetupWizardSheet::StopResolve( Service * service )
+{
+       OSStatus err;
+
+       check( service->serviceRef );
+
+       err = StopOperation( service->serviceRef );
+       require_noerr( err, exit );
+
+       service->printer->resolving--;
+
+exit:
+
+       return err;
+}
+
+
+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 )
+{
+       OSStatus err = kNoErr;
+
+       if ( ref )
+       {
+               m_serviceRefList.remove( ref );
+
+               if ( IsWindow( m_hWnd ) )
+               {
+                       err = WSAAsyncSelect((SOCKET) DNSServiceRefSockFD( ref ), m_hWnd, 0, 0 );
+                       require_noerr( err, exit );
+               }
+
+               DNSServiceRefDeallocate( ref );
+               ref = NULL;
+       }
+
+exit:
+
+       return err;
+}
+
+
+OSStatus
+CPrinterSetupWizardSheet::ParseTextRecord( Service * service, Queue * q, uint16_t inTXTSize, const char * inTXT )
+{
+       check( service );
+       check( q );
+
+       // <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/3987680> Default to queue "lp"
+
+       q->name = L"lp";
+
+       // <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 );
+       }
+       
+       if ( ( val = TXTRecordGetValuePtr( inTXTSize, inTXT, "pdl", &len ) ) != NULL )
+       {
+               // Stringize val ( doesn't have trailing '\0' yet )
+
+               memcpy( buf, val, len );
+               buf[len] = '\0';
+
+               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 )
+
+               memcpy( buf, val, len );
+               buf[len] = '\0';
+
+               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->isSharedFromOSX = true;
+       }
+
+exit:
+
+       // 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
+
+       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;
+}
+
+
+