<rdar://problem/5903084> Bundle CUPS PostScript driver with Bonjour for Windows
authorsherscher@apple.com <sherscher@apple.com@214c2c4a-bf3b-4dcf-9390-e4dd3010487d>
Tue, 18 Aug 2009 20:47:23 +0000 (20:47 +0000)
committersherscher@apple.com <sherscher@apple.com@214c2c4a-bf3b-4dcf-9390-e4dd3010487d>
Tue, 18 Aug 2009 20:47:23 +0000 (20:47 +0000)
git-svn-id: http://svn.macosforge.org/repository/mdnsresponder/trunk@6613 214c2c4a-bf3b-4dcf-9390-e4dd3010487d

Clients/PrinterSetupWizard/PrinterSetupWizardSheet.cpp
Clients/PrinterSetupWizard/PrinterSetupWizardSheet.h
Clients/PrinterSetupWizard/ThirdPage.cpp
Clients/PrinterSetupWizard/UtilTypes.h

index 20178f4..018d135 100644 (file)
@@ -25,6 +25,7 @@
 #include <winspool.h>
 #include <tcpxcv.h>
 #include <string>
+#include <shlwapi.h>
 
 // unreachable code
 #pragma warning(disable:4702)
 #      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 )
+\r
+
+static BOOL\r
+Is64BitWindows()\r
+{\r
+#if defined(_WIN64)\r
+       return TRUE;  // 64-bit programs run only on Win64\r
+#else\r
+       typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS)( HANDLE, PBOOL );\r
+       LPFN_ISWOW64PROCESS fnIsWow64Process;\r
+       BOOL bIsWow64 = FALSE;\r
+\r
+    fnIsWow64Process = ( LPFN_ISWOW64PROCESS ) GetProcAddress( GetModuleHandle( TEXT( "kernel32" ) ), "IsWow64Process" );\r
+  \r
+    if ( fnIsWow64Process != NULL )\r
+    {\r
+               BOOL ok;\r
+\r
+        ok = fnIsWow64Process( GetCurrentProcess(), &bIsWow64 );\r
+\r
+               if ( !ok )\r
+               {\r
+                       bIsWow64 = FALSE;\r
+               }\r
+       }\r
+\r
+       return bIsWow64;\r
+#endif\r
+}\r
 
 
 // CPrinterSetupWizardSheet
@@ -170,80 +220,89 @@ OSStatus
 CPrinterSetupWizardSheet::InstallPrinter(Printer * printer)
 {
        Logger          log;
-       Service *       service;
+       CUPSLibrary     cupsLib;
+       Service *       service         = NULL;
        BOOL            ok;
        OSStatus        err = 0;
 
        service = printer->services.front();
        check( service );
 
-       //
-       // if the driver isn't installed, then install it
-       //
-
-       if ( !printer->driverInstalled )
+       if ( printer->isCUPSPrinter && cupsLib.IsInstalled() )
+       {
+               err = InstallPrinterCUPS( printer, service, cupsLib );
+               require_noerr( err, exit );
+       }
+       else
        {
-               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
+               // 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);
-               }
+                       DWORD           dwResult;
+                       HANDLE          hThread;
+                       unsigned        threadID;
 
-               //
-               // 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 );
+                       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);
+                       }
 
-               //
-               // check the return value of thread
-               //
-               require_noerr_with_log( log, "thread exit code", m_driverThreadExitCode, exit );
+                       //
+                       // 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 );
 
-               //
-               // now we know that the driver was successfully installed
-               //
-               printer->driverInstalled = true;
-       }
+                       //
+                       // check the return value of thread
+                       //
+                       require_noerr_with_log( log, "thread exit code", m_driverThreadExitCode, exit );
 
-       if ( service->type == kPDLServiceType )
-       {
-               err = InstallPrinterPDLAndLPR( printer, service, PROTOCOL_RAWTCP_TYPE, log );
-               require_noerr_with_log( log, "InstallPrinterPDLAndLPR()", err, exit );
-       }
-       else if ( service->type == kLPRServiceType )
-       {
-               err = InstallPrinterPDLAndLPR( printer, service, PROTOCOL_LPR_TYPE, log );
-               require_noerr_with_log( log, "InstallPrinterPDLAndLPR()", err, exit );
-       }
-       else if ( service->type == kIPPServiceType )
-       {
-               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 );
+                       //
+                       // now we know that the driver was successfully installed
+                       //
+                       printer->driverInstalled = true;
+               }
+
+               if ( service->type == kPDLServiceType )
+               {
+                       err = InstallPrinterPDLAndLPR( printer, service, PROTOCOL_RAWTCP_TYPE, log );
+                       require_noerr_with_log( log, "InstallPrinterPDLAndLPR()", err, exit );
+               }
+               else if ( service->type == kLPRServiceType )
+               {
+                       err = InstallPrinterPDLAndLPR( printer, service, PROTOCOL_LPR_TYPE, log );
+                       require_noerr_with_log( log, "InstallPrinterPDLAndLPR()", err, exit );
+               }
+               else if ( service->type == kIPPServiceType )
+               {
+                       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;
@@ -420,6 +479,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 )
@@ -541,6 +811,8 @@ CPrinterSetupWizardSheet::OnOK()
 
        SetWizardButtons( PSWIZB_DISABLEDFINISH );
 
+       ShowWindow( SW_HIDE );
+       
        if ( InstallPrinter( m_selectedPrinter ) != kNoErr )
        {
                CString caption;
@@ -1544,7 +1816,7 @@ CPrinterSetupWizardSheet::ParseTextRecord( Service * service, Queue * q, uint16_
 
        if ( TXTRecordContainsKey( inTXTSize, inTXT, "printer-state" ) || TXTRecordContainsKey( inTXTSize, inTXT, "printer-type" ) )
        {
-               service->printer->isSharedFromOSX = true;
+               service->printer->isCUPSPrinter = true;
        }
 
 exit:
index 7f2e63e..9cc5599 100644 (file)
@@ -118,6 +118,67 @@ protected:
 
 private:
 
+       // This is from <cups/http.h>
+       typedef enum http_encryption_e          /**** HTTP encryption values ****/
+       {
+               HTTP_ENCRYPT_IF_REQUESTED,              /* Encrypt if requested (TLS upgrade) */
+               HTTP_ENCRYPT_NEVER,                     /* Never encrypt */
+               HTTP_ENCRYPT_REQUIRED,          /* Encryption is required (TLS upgrade) */
+               HTTP_ENCRYPT_ALWAYS                     /* Always encrypt (SSL) */
+       } http_encryption_t;
+
+       typedef void*                           ( *httpConnectEncryptFunc )( const char* host, int port, http_encryption_t encryption );
+       typedef http_encryption_t       ( *cupsEncryptionFunc )( void );
+       typedef void                            ( *cupsSetEncryptionFunc )( http_encryption_t e );
+       typedef char*                           ( *cupsAdminCreateWindowsPPDFunc )( void * http, const char *dest, char *buffer, int bufsize );
+
+       class CUPSLibrary
+       {
+       public:
+
+               CUPSLibrary()
+                       :
+                       httpConnectEncrypt( NULL ),
+                       cupsEncryption( NULL ),
+                       cupsSetEncryption( NULL ),
+                       cupsAdminCreateWindowsPPD( NULL ),
+                       library( NULL )
+               {
+                       if ( ( library = LoadLibrary( TEXT( "libcups2.dll" ) ) ) != NULL )
+                       {
+                               httpConnectEncrypt = ( httpConnectEncryptFunc ) GetProcAddress( library, "httpConnectEncrypt" );
+                               cupsEncryption = ( cupsEncryptionFunc ) GetProcAddress( library, "cupsEncryption" );
+                               cupsSetEncryption = ( cupsSetEncryptionFunc ) GetProcAddress( library, "cupsSetEncryption" );
+                               cupsAdminCreateWindowsPPD = ( cupsAdminCreateWindowsPPDFunc ) GetProcAddress( library, "cupsAdminCreateWindowsPPD" );
+                       }
+               }
+
+               ~CUPSLibrary()
+               {
+                       if ( library )
+                       {
+                               FreeLibrary( library );
+                               library = NULL;
+                       }
+               }
+
+               BOOL
+               IsInstalled()
+               {
+                       return ( ( httpConnectEncrypt != NULL ) && ( cupsEncryption != NULL ) && ( cupsSetEncryption != NULL ) && ( cupsAdminCreateWindowsPPD != NULL ) );
+               }
+
+               httpConnectEncryptFunc                  httpConnectEncrypt;
+               cupsEncryptionFunc                              cupsEncryption;
+               cupsSetEncryptionFunc                   cupsSetEncryption;
+               cupsAdminCreateWindowsPPDFunc   cupsAdminCreateWindowsPPD;
+
+       private:
+
+               HMODULE                                                 library;
+       };
+
+
        static void DNSSD_API
        OnBrowse(
                DNSServiceRef                   inRef,
@@ -226,6 +287,12 @@ private:
 
        OSStatus
        InstallPrinterIPP(Printer * printer, Service * service, Logger & log);
+       
+       OSStatus
+       InstallPrinterCUPS( Printer * printer, Service * service, CUPSLibrary & cupsLib );
+
+       OSStatus
+       InstallPrinterCUPS(Printer * printer, Service * service, CUPSLibrary & cupsLib, TCHAR * env );
 
        static unsigned WINAPI
        InstallDriverThread( LPVOID inParam );
index 902daca..0261bf2 100644 (file)
@@ -969,7 +969,7 @@ OSStatus CThirdPage::MatchPrinter(Manufacturers & manufacturers, Printer * print
                                useCUPSWorkaround = false;
                        }
 
-                       if ( useCUPSWorkaround && printer->isSharedFromOSX && hasGenericDriver )
+                       if ( useCUPSWorkaround && printer->isCUPSPrinter && hasGenericDriver )
                        {
                                //
                                // <rdar://problem/4496652> mDNS: Don't allow user to choose non-working driver
@@ -1000,7 +1000,7 @@ OSStatus CThirdPage::MatchPrinter(Manufacturers & manufacturers, Printer * print
        }
        else if ( MatchGeneric( manufacturers, printer, service, &genericManufacturer, &genericModel ) )
        {
-               if ( printer->isSharedFromOSX )
+               if ( printer->isCUPSPrinter )
                {
                        //
                        // <rdar://problem/4496652> mDNS: Don't allow user to choose non-working driver
index 5bda946..084832c 100644 (file)
@@ -89,7 +89,7 @@ namespace PrinterSetupWizard
                // This let's us know that this printer was discovered via OSX Printer Sharing.
                // We use this knowledge to workaround a problem with OS X Printer sharing.
 
-               bool                    isSharedFromOSX;
+               bool                    isCUPSPrinter;
                
                //
                // state
@@ -178,7 +178,7 @@ namespace PrinterSetupWizard
        inline
        Printer::Printer()
        :
-               isSharedFromOSX( false )
+               isCUPSPrinter( false )
        {
        }