]> 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 e912716b3a2e0284a0f2976ac8c7ec086c415e3b..47e5f9110bc0df459c61717c6aed17fb6bbf3ca7 100644 (file)
  * 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.
  * 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.
-
-    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
-
-Revision 1.21  2005/01/10 01:09:32  shersche
-Use the "note" key to populate pLocation field when setting up printer
-
-Revision 1.20  2005/01/03 19:05:01  shersche
-Store pointer to instance of wizard sheet so that print driver install thread sends a window message to the correct window
-
-Revision 1.19  2004/12/31 07:23:53  shersche
-Don't modify the button setting in SetSelectedPrinter()
-
-Revision 1.18  2004/12/29 18:53:38  shersche
-<rdar://problem/3725106>
-<rdar://problem/3737413> Added support for LPR and IPP protocols as well as support for obtaining multiple text records. Reorganized and simplified codebase.
-Bug #: 3725106, 3737413
-
-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"
@@ -145,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)
@@ -158,12 +36,61 @@ 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
 
 #define WM_SOCKET_EVENT                ( WM_USER + 0x100 )
 #define WM_PROCESS_EVENT       ( WM_USER + 0x101 )
 
 
 // Private Messages
 
 #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 );
+
+               if ( !ok )
+               {
+                       bIsWow64 = FALSE;
+               }
+       }
+
+       return bIsWow64;
+#endif
+}
+
+
 // CPrinterSetupWizardSheet
 CPrinterSetupWizardSheet * CPrinterSetupWizardSheet::m_self;
 
 // CPrinterSetupWizardSheet
 CPrinterSetupWizardSheet * CPrinterSetupWizardSheet::m_self;
 
@@ -279,7 +206,7 @@ exit:
 //
 // Installs a printer with Windows.
 //
 //
 // 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
@@ -292,81 +219,97 @@ exit:
 OSStatus
 CPrinterSetupWizardSheet::InstallPrinter(Printer * printer)
 {
 OSStatus
 CPrinterSetupWizardSheet::InstallPrinter(Printer * printer)
 {
-       Service *       service;
+       Logger          log;
+       CUPSLibrary     cupsLib;
+       Service *       service         = NULL;
        BOOL            ok;
        BOOL            ok;
-       OSStatus        err;
+       OSStatus        err = 0;
 
        service = printer->services.front();
        check( service );
 
 
        service = printer->services.front();
        check( service );
 
-       //
-       // if the driver isn't installed, then install it
-       //
-
-       if ( !printer->driverInstalled )
+       if ( printer->isCUPSPrinter && cupsLib.IsInstalled() )
        {
        {
-               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 );
+               err = InstallPrinterCUPS( printer, service, cupsLib );
                require_noerr( err, exit );
                require_noerr( err, exit );
-                       
+       }
+       else
+       {
                //
                //
-               // go modal
+               // if the driver isn't installed, then install it
                //
                //
-               while (!m_driverThreadFinished)
+
+               if ( !printer->driverInstalled )
                {
                {
-                       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 );
+                       DWORD           dwResult;
+                       HANDLE          hThread;
+                       unsigned        threadID;
 
 
-               //
-               // check the return value of thread
-               //
-               require_noerr( m_driverThreadExitCode, exit );
+                       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);
+                       }
 
 
-               //
-               // now we know that the driver was successfully installed
-               //
-               printer->driverInstalled = true;
-       }
+                       //
+                       // 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 );
 
 
-       if ( service->type == kPDLServiceType )
-       {
-               err = InstallPrinterPDLAndLPR( printer, service, PROTOCOL_RAWTCP_TYPE );
-               require_noerr( err, exit );
-       }
-       else if ( service->type == kLPRServiceType )
-       {
-               err = InstallPrinterPDLAndLPR( printer, service, PROTOCOL_LPR_TYPE );
-               require_noerr( err, exit );
-       }
-       else if ( service->type == kIPPServiceType )
-       {
-               err = InstallPrinterIPP( printer, service );
-               require_noerr( err, exit );
-       }
-       else
-       {
-               err = kUnknownErr;
-               require_noerr( 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;
        }
 
        printer->installed = true;
@@ -378,7 +321,7 @@ CPrinterSetupWizardSheet::InstallPrinter(Printer * printer)
        {
                ok = SetDefaultPrinter( printer->actualName );
                err = translate_errno( ok, errno_compat(), err = kUnknownErr );
        {
                ok = SetDefaultPrinter( printer->actualName );
                err = translate_errno( ok, errno_compat(), err = kUnknownErr );
-               require_noerr( err, exit );
+               require_noerr_with_log( log, "SetDefaultPrinter()", err, exit );
        }
 
 exit:
        }
 
 exit:
@@ -388,30 +331,30 @@ exit:
 
 
 OSStatus
 
 
 OSStatus
-CPrinterSetupWizardSheet::InstallPrinterPDLAndLPR(Printer * printer, Service * service, DWORD protocol )
+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;
-       PRINTER_INFO_2          pInfo;
        HANDLE                          hXcv                    =       NULL;
        HANDLE                          hXcv                    =       NULL;
-       HANDLE                          hPrinter                =       NULL;
        Queue                   *       q;
        BOOL                            ok;
        OSStatus                        err;
 
        Queue                   *       q;
        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 );
 
        q = service->queues.front();
        check( q );
 
        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
@@ -425,28 +368,55 @@ CPrinterSetupWizardSheet::InstallPrinterPDLAndLPR(Printer * printer, Service * s
                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   =       service->portNumber;
        portData.dwVersion              =       1;
        
        portData.dwPortNumber   =       service->portNumber;
        portData.dwVersion              =       1;
+       portData.dwDoubleSpool  =       1;
        
        portData.dwProtocol     = protocol;
        portData.cbSize         = sizeof PORT_DATA_1;
        portData.dwReserved     = 0L;
        
        portData.dwProtocol     = protocol;
        portData.cbSize         = sizeof PORT_DATA_1;
        portData.dwReserved     = 0L;
-       
-       wcscpy(portData.sztQueue, q->name);
-       wcscpy(portData.sztIPAddress, service->hostname); 
-       wcscpy(portData.sztHostAddress, service->hostname);
+
+       require_action_with_log( log, wcslen(q->name) < sizeof_array(portData.sztQueue), exit, err = kSizeErr );
+       wcscpy_s(portData.sztQueue, q->name);
+
+       require_action_with_log( log, wcslen( service->hostname ) < sizeof_array(portData.sztHostAddress), exit, err = kSizeErr );
+       wcscpy_s( 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 );
 
        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 );
+       require_noerr_with_log( log, "XcvData()", err, exit );
+
+exit:
+
+       if (hXcv != NULL)
+       {
+               ClosePrinter(hXcv);
+       }
+
+       if (pOutputData != NULL)
+       {
+               delete [] pOutputData;
+       }
+
+       return err;
+}
+
+
+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);
+
+       q = service->queues.front();
+       check( q );
 
        //
        // add the printer
 
        //
        // add the printer
@@ -475,7 +445,7 @@ CPrinterSetupWizardSheet::InstallPrinterPDLAndLPR(Printer * printer, Service * s
 
        hPrinter = AddPrinter(NULL, 2, (LPBYTE) &pInfo);
        err = translate_errno( hPrinter, errno_compat(), kUnknownErr );
 
        hPrinter = AddPrinter(NULL, 2, (LPBYTE) &pInfo);
        err = translate_errno( hPrinter, errno_compat(), kUnknownErr );
-       require_noerr( err, exit );
+       require_noerr_with_log( log, "AddPrinter()", err, exit );
 
 exit:
 
 
 exit:
 
@@ -484,22 +454,12 @@ exit:
                ClosePrinter(hPrinter);
        }
 
                ClosePrinter(hPrinter);
        }
 
-       if (hXcv != NULL)
-       {
-               ClosePrinter(hXcv);
-       }
-
-       if (pOutputData != NULL)
-       {
-               delete [] pOutputData;
-       }
-
        return err;
 }
 
 
 OSStatus
        return err;
 }
 
 
 OSStatus
-CPrinterSetupWizardSheet::InstallPrinterIPP(Printer * printer, Service * service)
+CPrinterSetupWizardSheet::InstallPrinterIPP(Printer * printer, Service * service, Logger & log)
 {
        DEBUG_UNUSED( service );
 
 {
        DEBUG_UNUSED( service );
 
@@ -525,7 +485,7 @@ CPrinterSetupWizardSheet::InstallPrinterIPP(Printer * printer, Service * service
        
        hPrinter = AddPrinter(NULL, 2, (LPBYTE)&pInfo);
        err = translate_errno( hPrinter, errno_compat(), kUnknownErr );
        
        hPrinter = AddPrinter(NULL, 2, (LPBYTE)&pInfo);
        err = translate_errno( hPrinter, errno_compat(), kUnknownErr );
-       require_noerr( err, exit );
+       require_noerr_with_log( log, "AddPrinter()", err, exit );
 
 exit:
 
 
 exit:
 
@@ -538,6 +498,217 @@ exit:
 }
 
 
 }
 
 
+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 )
 BEGIN_MESSAGE_MAP(CPrinterSetupWizardSheet, CPropertySheet)
 ON_MESSAGE( WM_SOCKET_EVENT, OnSocketEvent )
 ON_MESSAGE( WM_PROCESS_EVENT, OnProcessEvent )
@@ -573,7 +744,7 @@ BOOL CPrinterSetupWizardSheet::OnCommand(WPARAM wParam, LPARAM lParam)
 BOOL CPrinterSetupWizardSheet::OnInitDialog()
 {
        OSStatus err;
 BOOL CPrinterSetupWizardSheet::OnInitDialog()
 {
        OSStatus err;
-       
+
        CPropertySheet::OnInitDialog();
 
        err = StartBrowse();
        CPropertySheet::OnInitDialog();
 
        err = StartBrowse();
@@ -596,12 +767,14 @@ exit:
                }
                else
                {
                }
                else
                {
-                       CPrinterSetupWizardSheet::WizardException exc;
-                       
-                       exc.text.LoadString( IDS_NO_MDNSRESPONDER_SERVICE_TEXT );
-                       exc.caption.LoadString( IDS_ERROR_CAPTION );
-                       
-                       throw(exc);
+                       CString text, caption;
+
+                       text.LoadString( IDS_NO_MDNSRESPONDER_SERVICE_TEXT );
+                       caption.LoadString( IDS_ERROR_CAPTION );
+
+                       MessageBox(text, caption, MB_OK|MB_ICONEXCLAMATION);
+
+                       _exit( 0 );
                }
        }
 
                }
        }
 
@@ -653,18 +826,34 @@ 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);
        }
 
@@ -676,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);
@@ -693,7 +881,7 @@ void CPrinterSetupWizardSheet::Init(void)
 }
 
 
 }
 
 
-LONG
+LRESULT
 CPrinterSetupWizardSheet::OnSocketEvent(WPARAM inWParam, LPARAM inLParam)
 {
        if (WSAGETSELECTERROR(inLParam) && !(HIWORD(inLParam)))
 CPrinterSetupWizardSheet::OnSocketEvent(WPARAM inWParam, LPARAM inLParam)
 {
        if (WSAGETSELECTERROR(inLParam) && !(HIWORD(inLParam)))
@@ -726,7 +914,7 @@ CPrinterSetupWizardSheet::OnSocketEvent(WPARAM inWParam, LPARAM inLParam)
 }
 
 
 }
 
 
-LONG
+LRESULT
 CPrinterSetupWizardSheet::OnProcessEvent(WPARAM inWParam, LPARAM inLParam)
 {
        DEBUG_UNUSED(inLParam);
 CPrinterSetupWizardSheet::OnProcessEvent(WPARAM inWParam, LPARAM inLParam)
 {
        DEBUG_UNUSED(inLParam);
@@ -826,7 +1014,7 @@ CPrinterSetupWizardSheet::OnBrowse(
        OSStatus                                                err = kNoErr;
 
        require_noerr( inErrorCode, exit );
        OSStatus                                                err = kNoErr;
 
        require_noerr( inErrorCode, exit );
-       
+
        self = reinterpret_cast <CPrinterSetupWizardSheet*>( inContext );
        require_quiet( self, exit );
 
        self = reinterpret_cast <CPrinterSetupWizardSheet*>( inContext );
        require_quiet( self, exit );
 
@@ -844,12 +1032,25 @@ CPrinterSetupWizardSheet::OnBrowse(
 
        if ( inFlags & kDNSServiceFlagsAdd )
        {
 
        if ( inFlags & kDNSServiceFlagsAdd )
        {
-               if (printer == NULL)
-               {
-                       // If not, then create a new one
+               BOOL newPrinter = FALSE;
 
 
+               if ( !printer )
+               {
                        printer = self->OnAddPrinter( inInterfaceIndex, inName, inType, inDomain, moreComing );
                        require_action( printer, exit, err = kUnknownErr );
                        printer = self->OnAddPrinter( inInterfaceIndex, inName, inType, inDomain, moreComing );
                        require_action( printer, exit, err = kUnknownErr );
+                       newPrinter = TRUE;
+               }
+               
+               // 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 )
+               {
+                       self->m_pgSecond.OnAddPrinter( newPrinter ? printer : NULL, moreComing );
                }
 
                if ( !service )
                }
 
                if ( !service )
@@ -1102,6 +1303,7 @@ CPrinterSetupWizardSheet::OnAddPrinter(
        DEBUG_UNUSED( inInterfaceIndex );
        DEBUG_UNUSED( inType );
        DEBUG_UNUSED( inDomain );
        DEBUG_UNUSED( inInterfaceIndex );
        DEBUG_UNUSED( inType );
        DEBUG_UNUSED( inDomain );
+       DEBUG_UNUSED( moreComing );
 
        try
        {
 
        try
        {
@@ -1159,11 +1361,6 @@ CPrinterSetupWizardSheet::OnAddPrinter(
 
        m_printers.push_back( printer );
 
 
        m_printers.push_back( printer );
 
-       if ( GetActivePage() == &m_pgSecond )
-       {
-               m_pgSecond.OnAddPrinter( printer, moreComing );
-       }
-
 exit:
 
        return printer;
 exit:
 
        return printer;
@@ -1661,7 +1858,7 @@ CPrinterSetupWizardSheet::ParseTextRecord( Service * service, Queue * q, uint16_
 
        if ( TXTRecordContainsKey( inTXTSize, inTXT, "printer-state" ) || TXTRecordContainsKey( inTXTSize, inTXT, "printer-type" ) )
        {
 
        if ( TXTRecordContainsKey( inTXTSize, inTXT, "printer-state" ) || TXTRecordContainsKey( inTXTSize, inTXT, "printer-type" ) )
        {
-               service->printer->isSharedFromOSX = true;
+               service->printer->isCUPSPrinter = true;
        }
 
 exit:
        }
 
 exit: