summaryrefslogtreecommitdiffstats
path: root/mDNSResponder/Clients/PrinterSetupWizard/ThirdPage.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'mDNSResponder/Clients/PrinterSetupWizard/ThirdPage.cpp')
-rw-r--r--mDNSResponder/Clients/PrinterSetupWizard/ThirdPage.cpp1583
1 files changed, 1583 insertions, 0 deletions
diff --git a/mDNSResponder/Clients/PrinterSetupWizard/ThirdPage.cpp b/mDNSResponder/Clients/PrinterSetupWizard/ThirdPage.cpp
new file mode 100644
index 00000000..6caf55cb
--- /dev/null
+++ b/mDNSResponder/Clients/PrinterSetupWizard/ThirdPage.cpp
@@ -0,0 +1,1583 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * 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
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.
+ */
+
+#include "stdafx.h"
+#include "PrinterSetupWizardApp.h"
+#include "PrinterSetupWizardSheet.h"
+#include "ThirdPage.h"
+#include "tcpxcv.h"
+#include <dns_sd.h>
+#include <winspool.h>
+#include <setupapi.h>
+
+// local variable is initialize but not referenced
+#pragma warning(disable:4189)
+
+//
+// This is the printer description file that is shipped
+// with Windows XP and below
+//
+#define kNTPrintFile L"inf\\ntprint.inf"
+
+//
+// Windows Vista ships with a set of prn*.inf files
+//
+#define kVistaPrintFiles L"inf\\prn*.inf"
+
+//
+// These are pre-defined names for Generic manufacturer and model
+//
+#define kGenericManufacturer L"Generic"
+#define kGenericText L"Generic / Text Only"
+#define kGenericPostscript L"Generic / Postscript"
+#define kGenericPCL L"Generic / PCL"
+#define kPDLPostscriptKey L"application/postscript"
+#define kPDLPCLKey L"application/vnd.hp-pcl"
+#define kGenericPSColorDriver L"HP Color LaserJet 4550 PS"
+#define kGenericPSDriver L"HP LaserJet 4050 Series PS"
+#define kGenericPCLColorDriver L"HP Color LaserJet 4550 PCL"
+#define kGenericPCLDriver L"HP LaserJet 4050 Series PCL"
+
+
+// CThirdPage dialog
+
+IMPLEMENT_DYNAMIC(CThirdPage, CPropertyPage)
+CThirdPage::CThirdPage()
+ : CPropertyPage(CThirdPage::IDD),
+ m_manufacturerSelected( NULL ),
+ m_modelSelected( NULL ),
+ m_genericPostscript( NULL ),
+ m_genericPCL( NULL ),
+ m_initialized(false),
+ m_printerImage( NULL )
+{
+ static const int bufferSize = 32768;
+ TCHAR windowsDirectory[bufferSize];
+ CString header;
+ WIN32_FIND_DATA findFileData;
+ HANDLE findHandle;
+ CString prnFiles;
+ CString ntPrint;
+ OSStatus err;
+ BOOL ok;
+
+ m_psp.dwFlags &= ~(PSP_HASHELP);
+ m_psp.dwFlags |= PSP_DEFAULT|PSP_USEHEADERTITLE|PSP_USEHEADERSUBTITLE;
+
+ m_psp.pszHeaderTitle = MAKEINTRESOURCE(IDS_INSTALL_TITLE);
+ m_psp.pszHeaderSubTitle = MAKEINTRESOURCE(IDS_INSTALL_SUBTITLE);
+
+ //
+ // load printers from ntprint.inf
+ //
+ ok = GetWindowsDirectory( windowsDirectory, bufferSize );
+ err = translate_errno( ok, errno_compat(), kUnknownErr );
+ require_noerr( err, exit );
+
+ //
+ // <rdar://problem/4826126>
+ //
+ // If there are no *prn.inf files, we'll assume that the information
+ // is in ntprint.inf
+ //
+ prnFiles.Format( L"%s\\%s", windowsDirectory, kVistaPrintFiles );
+ findHandle = FindFirstFile( prnFiles, &findFileData );
+
+ if ( findHandle != INVALID_HANDLE_VALUE )
+ {
+ CString absolute;
+
+ absolute.Format( L"%s\\inf\\%s", windowsDirectory, findFileData.cFileName );
+ err = LoadPrintDriverDefsFromFile( m_manufacturers, absolute, false );
+ require_noerr( err, exit );
+
+ while ( FindNextFile( findHandle, &findFileData ) )
+ {
+ absolute.Format( L"%s\\inf\\%s", windowsDirectory, findFileData.cFileName );
+ err = LoadPrintDriverDefsFromFile( m_manufacturers, absolute, false );
+ require_noerr( err, exit );
+ }
+
+ FindClose( findHandle );
+ }
+ else
+ {
+ ntPrint.Format(L"%s\\%s", windowsDirectory, kNTPrintFile);
+ err = LoadPrintDriverDefsFromFile( m_manufacturers, ntPrint, false );
+ require_noerr(err, exit);
+ }
+
+ //
+ // load printer drivers that have been installed on this machine
+ //
+ err = LoadPrintDriverDefs( m_manufacturers );
+ require_noerr(err, exit);
+
+ //
+ // load our own special generic printer defs
+ //
+ err = LoadGenericPrintDriverDefs( m_manufacturers );
+ require_noerr( err, exit );
+
+exit:
+
+ return;
+}
+
+
+void
+CThirdPage::FreeManufacturers( Manufacturers & manufacturers )
+{
+ for ( Manufacturers::iterator it = manufacturers.begin(); it != manufacturers.end(); it++ )
+ {
+ for ( Models::iterator it2 = it->second->models.begin(); it2 != it->second->models.end(); it2++ )
+ {
+ delete *it2;
+ }
+
+ delete it->second;
+ }
+}
+
+
+CThirdPage::~CThirdPage()
+{
+ FreeManufacturers( m_manufacturers );
+}
+
+// ----------------------------------------------------
+// SelectMatch
+//
+// SelectMatch will do all the UI work associated with
+// selected a manufacturer and model of printer. It also
+// makes sure the printer object is update with the
+// latest settings
+//
+// ----------------------------------------------------
+void
+CThirdPage::SelectMatch(Printer * printer, Service * service, Manufacturer * manufacturer, Model * model)
+{
+ LVFINDINFO info;
+ int nIndex;
+
+ check( printer != NULL );
+ check( manufacturer != NULL );
+ check( model != NULL );
+
+ //
+ // select the manufacturer
+ //
+ info.flags = LVFI_STRING;
+ info.psz = manufacturer->name;
+
+ nIndex = m_manufacturerListCtrl.FindItem(&info);
+
+ if (nIndex != -1)
+ {
+ m_manufacturerListCtrl.SetItemState(nIndex, LVIS_SELECTED, LVIS_SELECTED);
+ //
+ //<rdar://problem/4528853> mDNS: When auto-highlighting items in lists, scroll list so highlighted item is in the middle
+ //
+ AutoScroll(m_manufacturerListCtrl, nIndex);
+ }
+
+ //
+ // select the model
+ //
+ info.flags = LVFI_STRING;
+ info.psz = model->displayName;
+
+ nIndex = m_modelListCtrl.FindItem(&info);
+
+ if (nIndex != -1)
+ {
+ m_modelListCtrl.SetItemState(nIndex, LVIS_SELECTED, LVIS_SELECTED);
+ AutoScroll( m_modelListCtrl, nIndex );
+
+ m_modelListCtrl.SetFocus();
+ }
+
+ CopyPrinterSettings( printer, service, manufacturer, model );
+}
+
+void
+CThirdPage::SelectMatch(Manufacturers & manufacturers, Printer * printer, Service * service, Manufacturer * manufacturer, Model * model)
+{
+ PopulateUI( manufacturers );
+
+ SelectMatch( printer, service, manufacturer, model );
+}
+
+// --------------------------------------------------------
+// CopyPrinterSettings
+//
+// This function makes sure that the printer object has the
+// latest settings from the manufacturer and model objects
+// --------------------------------------------------------
+
+void
+CThirdPage::CopyPrinterSettings( Printer * printer, Service * service, Manufacturer * manufacturer, Model * model )
+{
+ DWORD portNameLen;
+
+ printer->manufacturer = manufacturer->name;
+ printer->displayModelName = model->displayName;
+ printer->modelName = model->name;
+ printer->driverInstalled = model->driverInstalled;
+ printer->infFileName = model->infFileName;
+
+ if ( service->type == kPDLServiceType )
+ {
+ printer->portName.Format(L"IP_%s.%d", static_cast<LPCTSTR>(service->hostname), service->portNumber);
+ service->protocol = L"Raw";
+ }
+ else if ( service->type == kLPRServiceType )
+ {
+ Queue * q = service->queues.front();
+ check( q );
+
+ if ( q->name.GetLength() > 0 )
+ {
+ printer->portName.Format(L"LPR_%s.%d.%s", static_cast<LPCTSTR>(service->hostname), service->portNumber, static_cast<LPCTSTR>(q->name) );
+ }
+ else
+ {
+ printer->portName.Format(L"LPR_%s.%d", static_cast<LPCTSTR>(service->hostname), service->portNumber);
+ }
+
+ service->protocol = L"LPR";
+ }
+ else if ( service->type == kIPPServiceType )
+ {
+ Queue * q = service->queues.front();
+ check( q );
+
+ if ( q->name.GetLength() > 0 )
+ {
+ printer->portName.Format(L"http://%s:%d/%s", static_cast<LPCTSTR>(service->hostname), service->portNumber, static_cast<LPCTSTR>(q->name) );
+ }
+ else
+ {
+ printer->portName.Format(L"http://%s:%d/", static_cast<LPCTSTR>(service->hostname), service->portNumber );
+ }
+
+ service->protocol = L"IPP";
+ }
+
+ // If it's not an IPP printr, truncate the portName so that it's valid
+
+ if ( service->type != kIPPServiceType )
+ {
+ portNameLen = printer->portName.GetLength() + 1;
+
+ if ( portNameLen > MAX_PORTNAME_LEN )
+ {
+ printer->portName.Delete( MAX_PORTNAME_LEN - 1, ( portNameLen - MAX_PORTNAME_LEN ) );
+ }
+ }
+}
+
+// --------------------------------------------------------
+// DefaultPrinterExists
+//
+// Checks to see if a default printer has been configured
+// on this machine
+// --------------------------------------------------------
+BOOL
+CThirdPage::DefaultPrinterExists()
+{
+ CPrintDialog dlg(FALSE);
+
+ dlg.m_pd.Flags |= PD_RETURNDEFAULT;
+
+ return dlg.GetDefaults();
+}
+
+// --------------------------------------------------------
+// AutoScroll
+//
+// Ensure selected item is in middle of list
+// --------------------------------------------------------
+void
+CThirdPage::AutoScroll( CListCtrl & list, int nIndex )
+{
+ //
+ //<rdar://problem/4528853> mDNS: When auto-highlighting items in lists, scroll list so highlighted item is in the middle
+ //
+
+ int top;
+ int count;
+
+ list.EnsureVisible( nIndex, FALSE );
+
+ top = list.GetTopIndex();
+ count = list.GetCountPerPage();
+
+ if ( ( nIndex == top ) || ( ( nIndex + 1 ) == ( top + count ) ) )
+ {
+ CRect rect;
+ int rows;
+
+ rows = ( count / 2 );
+
+ if ( nIndex == top )
+ {
+ list.GetItemRect(0, rect, LVIR_BOUNDS);
+ list.Scroll( CPoint( 0, rows * rect.Height() * -1 ) );
+ }
+ else
+ {
+ list.GetItemRect(0, rect, LVIR_BOUNDS);
+ list.Scroll( CPoint( 0, rows * rect.Height() ) );
+ }
+ }
+}
+
+// ------------------------------------------------------
+// LoadPrintDriverDefsFromFile
+//
+// The only potentially opaque thing about this function is the
+// checkForDuplicateModels flag. The problem here is that ntprint.inf
+// doesn't contain duplicate models, and it has hundreds of models
+// listed. You wouldn't check for duplicates there. But oftentimes,
+// loading different windows print driver files contain multiple
+// entries for the same printer. You don't want the UI to display
+// the same printer multiple times, so in that case, you would ask
+// this function to check for multiple models.
+
+OSStatus
+CThirdPage::LoadPrintDriverDefsFromFile(Manufacturers & manufacturers, const CString & filename, bool checkForDuplicateModels )
+{
+ HINF handle = INVALID_HANDLE_VALUE;
+ const TCHAR * section = TEXT( "Manufacturer" );
+ LONG sectionCount;
+ TCHAR line[ 1000 ];
+ CString klass;
+ INFCONTEXT manufacturerContext;
+ BOOL ok;
+ OSStatus err = 0;
+
+ // Make sure we can open the file
+ handle = SetupOpenInfFile( filename, NULL, INF_STYLE_WIN4, NULL );
+ translate_errno( handle != INVALID_HANDLE_VALUE, GetLastError(), kUnknownErr );
+ require_noerr( err, exit );
+
+ // Make sure it's a printer file
+ ok = SetupGetLineText( NULL, handle, TEXT( "Version" ), TEXT( "Class" ), line, sizeof( line ), NULL );
+ translate_errno( ok, GetLastError(), kUnknownErr );
+ require_noerr( err, exit );
+ klass = line;
+ require_action( klass == TEXT( "Printer" ), exit, err = kUnknownErr );
+
+ sectionCount = SetupGetLineCount( handle, section );
+ translate_errno( sectionCount != -1, GetLastError(), kUnknownErr );
+ require_noerr( err, exit );
+
+ memset( &manufacturerContext, 0, sizeof( manufacturerContext ) );
+
+ for ( LONG i = 0; i < sectionCount; i++ )
+ {
+ Manufacturers::iterator iter;
+ Manufacturer * manufacturer;
+ CString manufacturerName;
+ CString temp;
+ CStringList modelSectionNameDecl;
+ CString modelSectionName;
+ CString baseModelName;
+ CString model;
+ INFCONTEXT modelContext;
+ LONG modelCount;
+ POSITION p;
+
+ if ( i == 0 )
+ {
+ ok = SetupFindFirstLine( handle, section, NULL, &manufacturerContext );
+ err = translate_errno( ok, GetLastError(), kUnknownErr );
+ require_noerr( err, exit );
+ }
+ else
+ {
+ ok = SetupFindNextLine( &manufacturerContext, &manufacturerContext );
+ err = translate_errno( ok, GetLastError(), kUnknownErr );
+ require_noerr( err, exit );
+ }
+
+ ok = SetupGetStringField( &manufacturerContext, 0, line, sizeof( line ), NULL );
+ err = translate_errno( ok, GetLastError(), kUnknownErr );
+ require_noerr( err, exit );
+ manufacturerName = line;
+
+ ok = SetupGetLineText( &manufacturerContext, handle, NULL, NULL, line, sizeof( line ), NULL );
+ err = translate_errno( ok, GetLastError(), kUnknownErr );
+ require_noerr( err, exit );
+
+ // Try to find some model section name that has entries. Explanation of int file structure
+ // can be found at:
+ //
+ // <http://msdn.microsoft.com/en-us/library/ms794359.aspx>
+ Split( line, ',', modelSectionNameDecl );
+
+ p = modelSectionNameDecl.GetHeadPosition();
+ modelSectionName = modelSectionNameDecl.GetNext( p );
+ modelCount = SetupGetLineCount( handle, modelSectionName );
+ baseModelName = modelSectionName;
+
+ while ( modelCount <= 0 && p )
+ {
+ CString targetOSVersion;
+
+ targetOSVersion = modelSectionNameDecl.GetNext( p );
+ modelSectionName = baseModelName + TEXT( "." ) + targetOSVersion;
+ modelCount = SetupGetLineCount( handle, modelSectionName );
+ }
+
+ if ( modelCount > 0 )
+ {
+ manufacturerName = NormalizeManufacturerName( manufacturerName );
+
+ iter = manufacturers.find( manufacturerName );
+
+ if ( iter != manufacturers.end() )
+ {
+ manufacturer = iter->second;
+ require_action( manufacturer, exit, err = kUnknownErr );
+ }
+ else
+ {
+ try
+ {
+ manufacturer = new Manufacturer;
+ }
+ catch (...)
+ {
+ manufacturer = NULL;
+ }
+
+ require_action( manufacturer, exit, err = kNoMemoryErr );
+
+ manufacturer->name = manufacturerName;
+ manufacturers[ manufacturerName ] = manufacturer;
+ }
+
+ memset( &modelContext, 0, sizeof( modelContext ) );
+
+ for ( LONG j = 0; j < modelCount; j++ )
+ {
+ CString modelName;
+ Model * model;
+
+ if ( j == 0 )
+ {
+ ok = SetupFindFirstLine( handle, modelSectionName, NULL, &modelContext );
+ err = translate_errno( ok, GetLastError(), kUnknownErr );
+ require_noerr( err, exit );
+ }
+ else
+ {
+ SetupFindNextLine( &modelContext, &modelContext );
+ err = translate_errno( ok, GetLastError(), kUnknownErr );
+ require_noerr( err, exit );
+ }
+
+ ok = SetupGetStringField( &modelContext, 0, line, sizeof( line ), NULL );
+ err = translate_errno( ok, GetLastError(), kUnknownErr );
+ require_noerr( err, exit );
+
+ modelName = line;
+
+ if (checkForDuplicateModels == true)
+ {
+ if ( MatchModel( manufacturer, ConvertToModelName( modelName ) ) != NULL )
+ {
+ continue;
+ }
+ }
+
+ //
+ // Stock Vista printer inf files embed guids in the model
+ // declarations for Epson printers. Let's ignore those.
+ //
+ if ( modelName.Find( TEXT( "{" ), 0 ) != -1 )
+ {
+ continue;
+ }
+
+ try
+ {
+ model = new Model;
+ }
+ catch (...)
+ {
+ model = NULL;
+ }
+
+ require_action( model, exit, err = kNoMemoryErr );
+
+ model->infFileName = filename;
+ model->displayName = modelName;
+ model->name = modelName;
+ model->driverInstalled = false;
+
+ manufacturer->models.push_back(model);
+ }
+ }
+ }
+
+exit:
+
+ if ( handle != INVALID_HANDLE_VALUE )
+ {
+ SetupCloseInfFile( handle );
+ handle = NULL;
+ }
+
+ return err;
+}
+
+
+// -------------------------------------------------------
+// LoadPrintDriverDefs
+//
+// This function is responsible for loading the print driver
+// definitions of all print drivers that have been installed
+// on this machine.
+// -------------------------------------------------------
+OSStatus
+CThirdPage::LoadPrintDriverDefs( Manufacturers & manufacturers )
+{
+ BYTE * buffer = NULL;
+ DWORD bytesReceived = 0;
+ DWORD numPrinters = 0;
+ OSStatus err = 0;
+ BOOL ok;
+
+ //
+ // like a lot of win32 calls, we call this first to get the
+ // size of the buffer we need.
+ //
+ EnumPrinterDrivers(NULL, L"all", 6, NULL, 0, &bytesReceived, &numPrinters);
+
+ if (bytesReceived > 0)
+ {
+ try
+ {
+ buffer = new BYTE[bytesReceived];
+ }
+ catch (...)
+ {
+ buffer = NULL;
+ }
+
+ require_action( buffer, exit, err = kNoMemoryErr );
+
+ //
+ // this call gets the real info
+ //
+ ok = EnumPrinterDrivers(NULL, L"all", 6, buffer, bytesReceived, &bytesReceived, &numPrinters);
+ err = translate_errno( ok, errno_compat(), kUnknownErr );
+ require_noerr( err, exit );
+
+ DRIVER_INFO_6 * info = (DRIVER_INFO_6*) buffer;
+
+ for (DWORD i = 0; i < numPrinters; i++)
+ {
+ Manufacturer * manufacturer;
+ Model * model;
+ CString name;
+
+ //
+ // skip over anything that doesn't have a manufacturer field. This
+ // fixes a bug that I noticed that occurred after I installed
+ // ProComm. This program add a print driver with no manufacturer
+ // that screwed up this wizard.
+ //
+ if (info[i].pszMfgName == NULL)
+ {
+ continue;
+ }
+
+ //
+ // look for manufacturer
+ //
+ Manufacturers::iterator iter;
+
+ //
+ // save the name
+ //
+ name = NormalizeManufacturerName( info[i].pszMfgName );
+
+ iter = manufacturers.find(name);
+
+ if (iter != manufacturers.end())
+ {
+ manufacturer = iter->second;
+ }
+ else
+ {
+ try
+ {
+ manufacturer = new Manufacturer;
+ }
+ catch (...)
+ {
+ manufacturer = NULL;
+ }
+
+ require_action( manufacturer, exit, err = kNoMemoryErr );
+
+ manufacturer->name = name;
+
+ manufacturers[name] = manufacturer;
+ }
+
+ //
+ // now look to see if we have already seen this guy. this could
+ // happen if we have already installed printers that are described
+ // in ntprint.inf. the extant drivers will show up in EnumPrinterDrivers
+ // but we have already loaded their info
+ //
+ //
+ if ( MatchModel( manufacturer, ConvertToModelName( info[i].pName ) ) == NULL )
+ {
+ try
+ {
+ model = new Model;
+ }
+ catch (...)
+ {
+ model = NULL;
+ }
+
+ require_action( model, exit, err = kNoMemoryErr );
+
+ model->displayName = info[i].pName;
+ model->name = info[i].pName;
+ model->driverInstalled = true;
+
+ manufacturer->models.push_back(model);
+ }
+ }
+ }
+
+exit:
+
+ if (buffer != NULL)
+ {
+ delete [] buffer;
+ }
+
+ return err;
+}
+
+// -------------------------------------------------------
+// LoadGenericPrintDriverDefs
+//
+// This function is responsible for loading polymorphic
+// generic print drivers defs. The UI will read
+// something like "Generic / Postscript" and we can map
+// that to any print driver we want.
+// -------------------------------------------------------
+OSStatus
+CThirdPage::LoadGenericPrintDriverDefs( Manufacturers & manufacturers )
+{
+ Manufacturer * manufacturer;
+ Model * model;
+ Manufacturers::iterator iter;
+ CString psDriverName;
+ CString pclDriverName;
+ OSStatus err = 0;
+
+ // <rdar://problem/4030388> Generic drivers don't do color
+
+ // First try and find our generic driver names
+
+ iter = m_manufacturers.find(L"HP");
+ require_action( iter != m_manufacturers.end(), exit, err = kUnknownErr );
+ manufacturer = iter->second;
+
+ // Look for Postscript
+
+ model = manufacturer->find( kGenericPSColorDriver );
+
+ if ( !model )
+ {
+ model = manufacturer->find( kGenericPSDriver );
+ }
+
+ if ( model )
+ {
+ psDriverName = model->name;
+ }
+
+ // Look for PCL
+
+ model = manufacturer->find( kGenericPCLColorDriver );
+
+ if ( !model )
+ {
+ model = manufacturer->find( kGenericPCLDriver );
+ }
+
+ if ( model )
+ {
+ pclDriverName = model->name;
+ }
+
+ // If we found either a generic PS driver, or a generic PCL driver,
+ // then add them to the list
+
+ if ( psDriverName.GetLength() || pclDriverName.GetLength() )
+ {
+ // Try and find generic manufacturer if there is one
+
+ iter = manufacturers.find(L"Generic");
+
+ if (iter != manufacturers.end())
+ {
+ manufacturer = iter->second;
+ }
+ else
+ {
+ try
+ {
+ manufacturer = new Manufacturer;
+ }
+ catch (...)
+ {
+ manufacturer = NULL;
+ }
+
+ require_action( manufacturer, exit, err = kNoMemoryErr );
+
+ manufacturer->name = "Generic";
+ manufacturers[manufacturer->name] = manufacturer;
+ }
+
+ if ( psDriverName.GetLength() > 0 )
+ {
+ try
+ {
+ m_genericPostscript = new Model;
+ }
+ catch (...)
+ {
+ m_genericPostscript = NULL;
+ }
+
+ require_action( m_genericPostscript, exit, err = kNoMemoryErr );
+
+ m_genericPostscript->displayName = kGenericPostscript;
+ m_genericPostscript->name = psDriverName;
+ m_genericPostscript->driverInstalled = false;
+
+ manufacturer->models.push_back( m_genericPostscript );
+ }
+
+ if ( pclDriverName.GetLength() > 0 )
+ {
+ try
+ {
+ m_genericPCL = new Model;
+ }
+ catch (...)
+ {
+ m_genericPCL = NULL;
+ }
+
+ require_action( m_genericPCL, exit, err = kNoMemoryErr );
+
+ m_genericPCL->displayName = kGenericPCL;
+ m_genericPCL->name = pclDriverName;
+ m_genericPCL->driverInstalled = false;
+
+ manufacturer->models.push_back( m_genericPCL );
+ }
+ }
+
+exit:
+
+ return err;
+}
+
+// ------------------------------------------------------
+// ConvertToManufacturerName
+//
+// This function is responsible for tweaking the
+// name so that subsequent string operations won't fail because
+// of capitalizations/different names for the same manufacturer
+// (i.e. Hewlett-Packard/HP/Hewlett Packard)
+//
+CString
+CThirdPage::ConvertToManufacturerName( const CString & name )
+{
+ //
+ // first we're going to convert all the characters to lower
+ // case
+ //
+ CString lower = name;
+ lower.MakeLower();
+
+ //
+ // now we're going to check to see if the string says "hewlett-packard",
+ // because sometimes they refer to themselves as "hewlett-packard", and
+ // sometimes they refer to themselves as "hp".
+ //
+ if ( lower == L"hewlett-packard")
+ {
+ lower = "hp";
+ }
+
+ //
+ // tweak for Xerox Phaser, which doesn't announce itself
+ // as a xerox
+ //
+ else if ( lower.Find( L"phaser", 0 ) != -1 )
+ {
+ lower = "xerox";
+ }
+
+ return lower;
+}
+
+// ------------------------------------------------------
+// ConvertToModelName
+//
+// This function is responsible for ensuring that subsequent
+// string operations don't fail because of differing capitalization
+// schemes and the like
+// ------------------------------------------------------
+
+CString
+CThirdPage::ConvertToModelName( const CString & name )
+{
+ //
+ // convert it to lowercase
+ //
+ CString lower = name;
+ lower.MakeLower();
+
+ return lower;
+}
+
+// ------------------------------------------------------
+// NormalizeManufacturerName
+//
+// This function is responsible for tweaking the manufacturer
+// name so that there are no aliases for vendors
+//
+CString
+CThirdPage::NormalizeManufacturerName( const CString & name )
+{
+ CString normalized = name;
+
+ //
+ // now we're going to check to see if the string says "hewlett-packard",
+ // because sometimes they refer to themselves as "hewlett-packard", and
+ // sometimes they refer to themselves as "hp".
+ //
+ if ( normalized == L"Hewlett-Packard")
+ {
+ normalized = "HP";
+ }
+
+ return normalized;
+}
+
+// -------------------------------------------------------
+// MatchPrinter
+//
+// This function is responsible for matching a printer
+// to a list of manufacturers and models. It calls
+// MatchManufacturer and MatchModel in turn.
+//
+
+OSStatus CThirdPage::MatchPrinter(Manufacturers & manufacturers, Printer * printer, Service * service, bool useCUPSWorkaround)
+{
+ CString normalizedProductName;
+ Manufacturer * manufacturer = NULL;
+ Manufacturer * genericManufacturer = NULL;
+ Model * model = NULL;
+ Model * genericModel = NULL;
+ bool found = false;
+ CString text;
+ OSStatus err = kNoErr;
+
+ check( printer );
+ check( service );
+
+ Queue * q = service->SelectedQueue();
+
+ check( q );
+
+ //
+ // first look to see if we have a usb_MFG descriptor
+ //
+ if ( q->usb_MFG.GetLength() > 0)
+ {
+ manufacturer = MatchManufacturer( manufacturers, ConvertToManufacturerName ( q->usb_MFG ) );
+ }
+
+ if ( manufacturer == NULL )
+ {
+ q->product.Remove('(');
+ q->product.Remove(')');
+
+ manufacturer = MatchManufacturer( manufacturers, ConvertToManufacturerName ( q->product ) );
+ }
+
+ //
+ // if we found the manufacturer, then start looking for the model
+ //
+ if ( manufacturer != NULL )
+ {
+ if ( q->usb_MDL.GetLength() > 0 )
+ {
+ model = MatchModel ( manufacturer, ConvertToModelName ( q->usb_MDL ) );
+ }
+
+ if ( ( model == NULL ) && ( q->product.GetLength() > 0 ) )
+ {
+ q->product.Remove('(');
+ q->product.Remove(')');
+
+ model = MatchModel ( manufacturer, ConvertToModelName ( q->product ) );
+ }
+
+ if ( model != NULL )
+ {
+ // <rdar://problem/4124524> Offer Generic printers if printer advertises Postscript or PCL. Workaround
+ // bug in OS X CUPS printer sharing by selecting Generic driver instead of matched printer.
+
+ bool hasGenericDriver = false;
+
+ if ( MatchGeneric( manufacturers, printer, service, &genericManufacturer, &genericModel ) )
+ {
+ hasGenericDriver = true;
+ }
+
+ // <rdar://problem/4190104> Use "application/octet-stream" to determine if CUPS
+ // shared queue supports raw
+
+ if ( q->pdl.Find( L"application/octet-stream" ) != -1 )
+ {
+ useCUPSWorkaround = false;
+ }
+
+ if ( useCUPSWorkaround && printer->isCUPSPrinter && hasGenericDriver )
+ {
+ //
+ // <rdar://problem/4496652> mDNS: Don't allow user to choose non-working driver
+ //
+ Manufacturers genericManufacturers;
+
+ LoadGenericPrintDriverDefs( genericManufacturers );
+
+ SelectMatch( genericManufacturers, printer, service, genericManufacturer, genericModel );
+
+ FreeManufacturers( genericManufacturers );
+ }
+ else
+ {
+ SelectMatch(manufacturers, printer, service, manufacturer, model);
+ }
+
+ found = true;
+ }
+ }
+
+ //
+ // display a message to the user based on whether we could match
+ // this printer
+ //
+ if (found)
+ {
+ text.LoadString(IDS_PRINTER_MATCH_GOOD);
+ err = kNoErr;
+ }
+ else if ( MatchGeneric( manufacturers, printer, service, &genericManufacturer, &genericModel ) )
+ {
+ if ( printer->isCUPSPrinter )
+ {
+ //
+ // <rdar://problem/4496652> mDNS: Don't allow user to choose non-working driver
+ //
+ Manufacturers genericManufacturers;
+
+ LoadGenericPrintDriverDefs( genericManufacturers );
+
+ SelectMatch( genericManufacturers, printer, service, genericManufacturer, genericModel );
+
+ text.LoadString(IDS_PRINTER_MATCH_GOOD);
+
+ FreeManufacturers( genericManufacturers );
+ }
+ else
+ {
+ SelectMatch( manufacturers, printer, service, genericManufacturer, genericModel );
+ text.LoadString(IDS_PRINTER_MATCH_MAYBE);
+ }
+
+ err = kNoErr;
+ }
+ else
+ {
+ text.LoadString(IDS_PRINTER_MATCH_BAD);
+
+ //
+ // if there was any crud in this list from before, get rid of it now
+ //
+ m_modelListCtrl.DeleteAllItems();
+
+ //
+ // select the manufacturer if we found one
+ //
+ if (manufacturer != NULL)
+ {
+ LVFINDINFO info;
+ int nIndex;
+
+ //
+ // select the manufacturer
+ //
+ info.flags = LVFI_STRING;
+ info.psz = manufacturer->name;
+
+ nIndex = m_manufacturerListCtrl.FindItem(&info);
+
+ if (nIndex != -1)
+ {
+ m_manufacturerListCtrl.SetItemState(nIndex, LVIS_SELECTED, LVIS_SELECTED);
+
+ //
+ //<rdar://problem/4528853> mDNS: When auto-highlighting items in lists, scroll list so highlighted item is in the middle
+ //
+ AutoScroll(m_manufacturerListCtrl, nIndex);
+ }
+ }
+
+ err = kUnknownErr;
+ }
+
+ m_printerSelectionText.SetWindowText(text);
+
+ return err;
+}
+
+// ------------------------------------------------------
+// MatchManufacturer
+//
+// This function is responsible for finding a manufacturer
+// object from a string name. It does a CString::Find, which
+// is like strstr, so it doesn't have to do an exact match
+//
+// If it can't find a match, NULL is returned
+// ------------------------------------------------------
+
+Manufacturer*
+CThirdPage::MatchManufacturer( Manufacturers & manufacturers, const CString & name)
+{
+ Manufacturers::iterator iter;
+
+ for (iter = manufacturers.begin(); iter != manufacturers.end(); iter++)
+ {
+ //
+ // we're going to convert all the manufacturer names to lower case,
+ // so we match the name passed in.
+ //
+ CString lower = iter->second->name;
+ lower.MakeLower();
+
+ //
+ // now try and find the lowered string in the name passed in.
+ //
+ if (name.Find(lower) != -1)
+ {
+ return iter->second;
+ }
+ }
+
+ return NULL;
+}
+
+// -------------------------------------------------------
+// MatchModel
+//
+// This function is responsible for matching a model from
+// a name. It does a CString::Find(), which works like strstr,
+// so it doesn't rely on doing an exact string match.
+//
+
+Model*
+CThirdPage::MatchModel(Manufacturer * manufacturer, const CString & name)
+{
+ Models::iterator iter;
+
+ iter = manufacturer->models.begin();
+
+ for (iter = manufacturer->models.begin(); iter != manufacturer->models.end(); iter++)
+ {
+ Model * model = *iter;
+
+ //
+ // convert the model name to lower case
+ //
+ CString lowered = model->name;
+ lowered.MakeLower();
+
+ if (lowered.Find( name ) != -1)
+ {
+ return model;
+ }
+
+ //
+ // <rdar://problem/3841218>
+ // try removing the first substring and search again
+ //
+
+ if ( name.Find(' ') != -1 )
+ {
+ CString altered = name;
+ altered.Delete( 0, altered.Find(' ') + 1 );
+
+ if ( lowered.Find( altered ) != -1 )
+ {
+ return model;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+// -------------------------------------------------------
+// MatchGeneric
+//
+// This function will attempt to find a generic printer
+// driver for a printer that we weren't able to match
+// specifically
+//
+BOOL
+CThirdPage::MatchGeneric( Manufacturers & manufacturers, Printer * printer, Service * service, Manufacturer ** manufacturer, Model ** model )
+{
+ CString pdl;
+ BOOL ok = FALSE;
+
+ DEBUG_UNUSED( printer );
+
+ check( service );
+
+ Queue * q = service->SelectedQueue();
+
+ check( q );
+
+ Manufacturers::iterator iter = manufacturers.find( kGenericManufacturer );
+ require_action_quiet( iter != manufacturers.end(), exit, ok = FALSE );
+
+ *manufacturer = iter->second;
+
+ pdl = q->pdl;
+ pdl.MakeLower();
+
+ if ( m_genericPCL && ( pdl.Find( kPDLPCLKey ) != -1 ) )
+ {
+ *model = m_genericPCL;
+ ok = TRUE;
+ }
+ else if ( m_genericPostscript && ( pdl.Find( kPDLPostscriptKey ) != -1 ) )
+ {
+ *model = m_genericPostscript;
+ ok = TRUE;
+ }
+
+exit:
+
+ return ok;
+}
+
+// -----------------------------------------------------------
+// OnInitPage
+//
+// This function is responsible for doing initialization that
+// only occurs once during a run of the wizard
+//
+
+OSStatus CThirdPage::OnInitPage()
+{
+ CString header;
+ CString ntPrint;
+ OSStatus err = kNoErr;
+
+ // Load printer icon
+ check( m_printerImage == NULL );
+
+ m_printerImage = (CStatic*) GetDlgItem( 1 ); // 1 == IDR_MANIFEST
+ check( m_printerImage );
+
+ if ( m_printerImage != NULL )
+ {
+ m_printerImage->SetIcon( LoadIcon( GetNonLocalizedResources(), MAKEINTRESOURCE( IDI_PRINTER ) ) );
+ }
+
+ //
+ // The CTreeCtrl widget automatically sends a selection changed
+ // message which initially we want to ignore, because the user
+ // hasn't selected anything
+ //
+ // this flag gets reset in the message handler. Every subsequent
+ // message gets handled.
+ //
+
+ //
+ // we have to make sure that we only do this once. Typically,
+ // we would do this in something like OnInitDialog, but we don't
+ // have this in Wizards, because the window is a PropertySheet.
+ // We're considered fully initialized when we receive the first
+ // selection notice
+ //
+ header.LoadString(IDS_MANUFACTURER_HEADING);
+ m_manufacturerListCtrl.InsertColumn(0, header, LVCFMT_LEFT, -1 );
+ m_manufacturerSelected = NULL;
+
+ header.LoadString(IDS_MODEL_HEADING);
+ m_modelListCtrl.InsertColumn(0, header, LVCFMT_LEFT, -1 );
+ m_modelSelected = NULL;
+
+ return (err);
+}
+
+void CThirdPage::DoDataExchange(CDataExchange* pDX)
+{
+ CPropertyPage::DoDataExchange(pDX);
+ DDX_Control(pDX, IDC_PRINTER_MANUFACTURER, m_manufacturerListCtrl);
+ DDX_Control(pDX, IDC_PRINTER_MODEL, m_modelListCtrl);
+ DDX_Control(pDX, IDC_PRINTER_NAME, m_printerName);
+ DDX_Control(pDX, IDC_DEFAULT_PRINTER, m_defaultPrinterCtrl);
+ DDX_Control(pDX, IDC_PRINTER_SELECTION_TEXT, m_printerSelectionText);
+
+}
+
+// ----------------------------------------------------------
+// OnSetActive
+//
+// This function is called by MFC after the window has been
+// activated.
+//
+
+BOOL
+CThirdPage::OnSetActive()
+{
+ CPrinterSetupWizardSheet * psheet;
+ Printer * printer;
+ Service * service;
+
+ psheet = reinterpret_cast<CPrinterSetupWizardSheet*>(GetParent());
+ require_quiet( psheet, exit );
+
+ psheet->SetWizardButtons( PSWIZB_BACK );
+
+ printer = psheet->GetSelectedPrinter();
+ require_quiet( printer, exit );
+
+ service = printer->services.front();
+ require_quiet( service, exit );
+
+ //
+ // call OnInitPage once
+ //
+ if (!m_initialized)
+ {
+ OnInitPage();
+ m_initialized = true;
+ }
+
+ //
+ // <rdar://problem/4580061> mDNS: Printers added using Bonjour should be set as the default printer.
+ //
+ if ( DefaultPrinterExists() )
+ {
+ m_defaultPrinterCtrl.SetCheck( BST_UNCHECKED );
+ printer->deflt = false;
+ }
+ else
+ {
+ m_defaultPrinterCtrl.SetCheck( BST_CHECKED );
+ printer->deflt = true;
+ }
+
+ //
+ // update the UI with the printer name
+ //
+ m_printerName.SetWindowText(printer->displayName);
+
+ //
+ // populate the list controls with the manufacturers and models
+ // from ntprint.inf
+ //
+ PopulateUI( m_manufacturers );
+
+ //
+ // and try and match the printer
+ //
+
+ if ( psheet->GetLastPage() == psheet->GetPage(0) )
+ {
+ MatchPrinter( m_manufacturers, printer, service, true );
+
+ if ( ( m_manufacturerSelected != NULL ) && ( m_modelSelected != NULL ) )
+ {
+ GetParent()->PostMessage(PSM_SETCURSEL, 2 );
+ }
+ }
+ else
+ {
+ SelectMatch(printer, service, m_manufacturerSelected, m_modelSelected);
+ }
+
+exit:
+
+ return CPropertyPage::OnSetActive();
+}
+
+BOOL
+CThirdPage::OnKillActive()
+{
+ CPrinterSetupWizardSheet * psheet;
+
+ psheet = reinterpret_cast<CPrinterSetupWizardSheet*>(GetParent());
+ require_quiet( psheet, exit );
+
+ psheet->SetLastPage(this);
+
+exit:
+
+ return CPropertyPage::OnKillActive();
+}
+
+// -------------------------------------------------------
+// PopulateUI
+//
+// This function is called to populate the list of manufacturers
+//
+OSStatus
+CThirdPage::PopulateUI(Manufacturers & manufacturers)
+{
+ Manufacturers::iterator iter;
+
+ m_manufacturerListCtrl.DeleteAllItems();
+
+ for (iter = manufacturers.begin(); iter != manufacturers.end(); iter++)
+ {
+ int nIndex;
+
+ Manufacturer * manufacturer = iter->second;
+
+ nIndex = m_manufacturerListCtrl.InsertItem(0, manufacturer->name);
+
+ m_manufacturerListCtrl.SetItemData(nIndex, (DWORD_PTR) manufacturer);
+
+ m_manufacturerListCtrl.SetColumnWidth( 0, LVSCW_AUTOSIZE_USEHEADER );
+ }
+
+ return 0;
+}
+
+BEGIN_MESSAGE_MAP(CThirdPage, CPropertyPage)
+ ON_NOTIFY(LVN_ITEMCHANGED, IDC_PRINTER_MANUFACTURER, OnLvnItemchangedManufacturer)
+ ON_NOTIFY(LVN_ITEMCHANGED, IDC_PRINTER_MODEL, OnLvnItemchangedPrinterModel)
+ ON_BN_CLICKED(IDC_DEFAULT_PRINTER, OnBnClickedDefaultPrinter)
+ ON_BN_CLICKED(IDC_HAVE_DISK, OnBnClickedHaveDisk)
+END_MESSAGE_MAP()
+
+// CThirdPage message handlers
+void CThirdPage::OnLvnItemchangedManufacturer(NMHDR *pNMHDR, LRESULT *pResult)
+{
+ LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
+
+ POSITION p = m_manufacturerListCtrl.GetFirstSelectedItemPosition();
+ int nSelected = m_manufacturerListCtrl.GetNextSelectedItem(p);
+
+ if (nSelected != -1)
+ {
+ m_manufacturerSelected = (Manufacturer*) m_manufacturerListCtrl.GetItemData(nSelected);
+
+ m_modelListCtrl.SetRedraw(FALSE);
+
+ m_modelListCtrl.DeleteAllItems();
+ m_modelSelected = NULL;
+
+ Models::iterator iter;
+
+ for (iter = m_manufacturerSelected->models.begin(); iter != m_manufacturerSelected->models.end(); iter++)
+ {
+ Model * model = *iter;
+
+ int nItem = m_modelListCtrl.InsertItem( 0, model->displayName );
+
+ m_modelListCtrl.SetItemData(nItem, (DWORD_PTR) model);
+
+ m_modelListCtrl.SetColumnWidth( 0, LVSCW_AUTOSIZE_USEHEADER );
+ }
+
+ m_modelListCtrl.SetRedraw(TRUE);
+ }
+
+ *pResult = 0;
+}
+
+void CThirdPage::OnLvnItemchangedPrinterModel(NMHDR *pNMHDR, LRESULT *pResult)
+{
+ LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
+
+ CPrinterSetupWizardSheet * psheet;
+ Printer * printer;
+ Service * service;
+
+ psheet = reinterpret_cast<CPrinterSetupWizardSheet*>(GetParent());
+ require_quiet( psheet, exit );
+
+ printer = psheet->GetSelectedPrinter();
+ require_quiet( printer, exit );
+
+ service = printer->services.front();
+ require_quiet( service, exit );
+
+ check ( m_manufacturerSelected );
+
+ POSITION p = m_modelListCtrl.GetFirstSelectedItemPosition();
+ int nSelected = m_modelListCtrl.GetNextSelectedItem(p);
+
+ if (nSelected != -1)
+ {
+ m_modelSelected = (Model*) m_modelListCtrl.GetItemData(nSelected);
+
+ CopyPrinterSettings( printer, service, m_manufacturerSelected, m_modelSelected );
+
+ psheet->SetWizardButtons(PSWIZB_BACK|PSWIZB_NEXT);
+ }
+ else
+ {
+ psheet->SetWizardButtons(PSWIZB_BACK);
+ }
+
+exit:
+
+ *pResult = 0;
+}
+
+void CThirdPage::OnBnClickedDefaultPrinter()
+{
+ CPrinterSetupWizardSheet * psheet;
+ Printer * printer;
+
+ psheet = reinterpret_cast<CPrinterSetupWizardSheet*>(GetParent());
+ require_quiet( psheet, exit );
+
+ printer = psheet->GetSelectedPrinter();
+ require_quiet( printer, exit );
+
+ printer->deflt = ( m_defaultPrinterCtrl.GetCheck() == BST_CHECKED ) ? true : false;
+
+exit:
+
+ return;
+}
+
+void CThirdPage::OnBnClickedHaveDisk()
+{
+ CPrinterSetupWizardSheet * psheet;
+ Printer * printer;
+ Service * service;
+ Manufacturers manufacturers;
+
+ CFileDialog dlg(TRUE, NULL, NULL, OFN_HIDEREADONLY|OFN_FILEMUSTEXIST, L"Setup Information (*.inf)|*.inf||", this);
+
+ psheet = reinterpret_cast<CPrinterSetupWizardSheet*>(GetParent());
+ require_quiet( psheet, exit );
+
+ printer = psheet->GetSelectedPrinter();
+ require_quiet( printer, exit );
+
+ service = printer->services.front();
+ require_quiet( service, exit );
+
+ for ( ;; )
+ {
+ if ( dlg.DoModal() == IDOK )
+ {
+ CString filename = dlg.GetPathName();
+
+ LoadPrintDriverDefsFromFile( manufacturers, filename, true );
+
+ // Sanity check
+
+ if ( manufacturers.size() > 0 )
+ {
+ PopulateUI( manufacturers );
+
+ if ( MatchPrinter( manufacturers, printer, service, false ) != kNoErr )
+ {
+ CString errorMessage;
+ CString errorCaption;
+
+ errorMessage.LoadString( IDS_NO_MATCH_INF_FILE );
+ errorCaption.LoadString( IDS_NO_MATCH_INF_FILE_CAPTION );
+
+ MessageBox( errorMessage, errorCaption, MB_OK );
+ }
+
+ break;
+ }
+ else
+ {
+ CString errorMessage;
+ CString errorCaption;
+
+ errorMessage.LoadString( IDS_BAD_INF_FILE );
+ errorCaption.LoadString( IDS_BAD_INF_FILE_CAPTION );
+
+ MessageBox( errorMessage, errorCaption, MB_OK );
+ }
+ }
+ else
+ {
+ break;
+ }
+ }
+
+exit:
+
+ FreeManufacturers( manufacturers );
+ return;
+}
+
+
+void
+CThirdPage::Split( const CString & string, TCHAR ch, CStringList & components )
+{
+ CString temp;
+ int n;
+
+ temp = string;
+
+ while ( ( n = temp.Find( ch ) ) != -1 )
+ {
+ components.AddTail( temp.Left( n ) );
+ temp = temp.Right( temp.GetLength() - ( n + 1 ) );
+ }
+
+ components.AddTail( temp );
+}