]> git.saurik.com Git - apple/mdnsresponder.git/blobdiff - Clients/PrinterSetupWizard/ThirdPage.cpp
mDNSResponder-878.200.35.tar.gz
[apple/mdnsresponder.git] / Clients / PrinterSetupWizard / ThirdPage.cpp
index ac5ff1fa5474e1032c2aa2627fd985c3169f354e..6caf55cb29207c46313c46ccd23c12a75ad476f0 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.
-
-    Change History (most recent first):
-    
-$Log: ThirdPage.cpp,v $
-Revision 1.28  2006/08/14 23:24:09  cheshire
-Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
-
-Revision 1.27  2005/10/05 21:41:45  herscher
-<rdar://problem/4190104> Use "application/octet-stream" to determine if CUPS shared queue supports raw
-
-Revision 1.26  2005/07/11 20:17:15  shersche
-<rdar://problem/4124524> UI fixes associated with CUPS printer workaround fix.
-
-Revision 1.25  2005/07/07 17:53:20  shersche
-Fix problems associated with the CUPS printer workaround fix.
-
-Revision 1.24  2005/06/30 18:02:54  shersche
-<rdar://problem/4124524> Workaround for Mac OS X Printer Sharing bug
-
-Revision 1.23  2005/04/18 02:33:47  shersche
-<rdar://problem/4091216> Default printer option cannot be deselected
-
-Revision 1.22  2005/04/13 17:46:22  shersche
-<rdar://problem/4082122> Generic PCL not selected when printers advertise multiple text records
-
-Revision 1.21  2005/03/30 02:09:55  shersche
-Auto-resize the column width to account for differing fonts and font sizes
-
-Revision 1.20  2005/03/05 02:27:45  shersche
-<rdar://problem/4030388> Generic drivers don't do color
-
-Revision 1.19  2005/02/23 02:08:51  shersche
-<rdar://problem/4012275> If we can't match the manufacturer, and select a generic printer, then show all the manufacturers in the manufacturer pane, not just "Generic".
-
-Revision 1.18  2005/02/15 07:02:51  shersche
-<rdar://problem/4003724> Display different UI text when generic printer drivers are selected
-
-Revision 1.17  2005/02/08 21:45:06  shersche
-<rdar://problem/3947490> Default to Generic PostScript or PCL if unable to match driver
-
-Revision 1.16  2005/02/08 18:56:03  shersche
-Fix generated IPP url so that it doesn't add "/printers" string
-
-Revision 1.15  2005/02/01 01:44:07  shersche
-Load ntprint.inf at startup.  This will cause the wizard to take a second or two longer to come up, but will eliminate the pause when auto-selecting the print drivers.
-
-Revision 1.14  2005/01/25 08:55:54  shersche
-<rdar://problem/3911084> Load icons at run-time from resource DLL
-Bug #: 3911084
-
-Revision 1.13  2005/01/06 08:15:45  shersche
-Append queue name to end of LPR port name, correctly build port name when queue name is absent
-
-Revision 1.12  2005/01/05 01:06:12  shersche
-<rdar://problem/3841218> Strip the first substring off the product key if an initial match can't be found with the whole product key.
-Bug #: 3841218
-
-Revision 1.11  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.10  2004/10/11 22:55:34  shersche
-<rdar://problem/3827624> Use the IP port number when deriving the printer port name.
-Bug #: 3827624
-
-Revision 1.9  2004/06/27 23:08:00  shersche
-code cleanup, make sure EnumPrintDrivers returns non-zero value, ignore comments in inf files
-
-Revision 1.8  2004/06/27 08:06:45  shersche
-Parse [Strings] section of inf file
-
-Revision 1.7  2004/06/26 04:00:05  shersche
-fix warnings compiling in debug mode
-Submitted by: herscher
-
-Revision 1.6  2004/06/26 03:19:57  shersche
-clean up warning messages
-
-Submitted by: herscher
-
-Revision 1.5  2004/06/25 05:06:02  shersche
-Trim whitespace from key/value pairs when parsing inf files
-Submitted by: herscher
-
-Revision 1.4  2004/06/25 02:44:13  shersche
-Tweaked code to handle Xerox Phaser printer identification
-Submitted by: herscher
-
-Revision 1.3  2004/06/25 02:27:58  shersche
-Do a CListCtrl::FindItem() before calling CListCtrl::SetItemState().
-Submitted by: herscher
-
-Revision 1.2  2004/06/23 18:09:23  shersche
-Normalize tag names when parsing inf files.
-Submitted by: herscher
-
-Revision 1.1  2004/06/18 04:36:58  rpantos
-First checked in
-
-
-*/
+ */
 
 #include "stdafx.h"
 #include "PrinterSetupWizardApp.h"
 #include "PrinterSetupWizardSheet.h"
 #include "ThirdPage.h"
-#include "StdioFileEx.h"
+#include "tcpxcv.h"
 #include <dns_sd.h>
-#include <tcpxcv.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
+// 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
 //
@@ -150,29 +53,24 @@ First checked in
 #define kGenericPCLDriver                      L"HP LaserJet 4050 Series PCL"
 
 
-//
-// states for parsing ntprint.inf
-//
-enum PrinterParsingState
-{
-       Looking,
-       ParsingManufacturers,
-       ParsingModels,
-       ParsingStrings
-};
-
-
 // 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;
@@ -190,9 +88,38 @@ CThirdPage::CThirdPage()
        err = translate_errno( ok, errno_compat(), kUnknownErr );
        require_noerr( err, exit );
  
-       ntPrint.Format(L"%s\\%s", windowsDirectory, kNTPrintFile);
-       err = LoadPrintDriverDefsFromFile( m_manufacturers, ntPrint, false );
-       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
@@ -212,39 +139,32 @@ exit:
 }
 
 
-CThirdPage::~CThirdPage()
+void
+CThirdPage::FreeManufacturers( Manufacturers & manufacturers )
 {
-       //
-       // clean up all the printer manufacturers
-       //
-       while (m_manufacturers.size())
+       for ( Manufacturers::iterator it = manufacturers.begin(); it != manufacturers.end(); it++ )
        {
-               Manufacturers::iterator iter = m_manufacturers.begin();
-
-               while (iter->second->models.size())
+               for ( Models::iterator it2 = it->second->models.begin(); it2 != it->second->models.end(); it2++ )
                {
-                       Models::iterator it = iter->second->models.begin();
-
-                       Model * model = *it;
-
-                       delete model;
-
-                       iter->second->models.erase(it);
+                       delete *it2;
                }
 
-               delete iter->second;
-
-               m_manufacturers.erase(iter);
+               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 
+// makes sure the printer object is update with the
 // latest settings
 //
 // ----------------------------------------------------
@@ -269,7 +189,10 @@ CThirdPage::SelectMatch(Printer * printer, Service * service, Manufacturer * man
        if (nIndex != -1)
        {
                m_manufacturerListCtrl.SetItemState(nIndex, LVIS_SELECTED, LVIS_SELECTED);
-               m_manufacturerListCtrl.EnsureVisible(nIndex, FALSE);
+               //
+               //<rdar://problem/4528853> mDNS: When auto-highlighting items in lists, scroll list so highlighted item is in the middle
+               //
+               AutoScroll(m_manufacturerListCtrl, nIndex);
        }
 
        //
@@ -283,7 +206,7 @@ CThirdPage::SelectMatch(Printer * printer, Service * service, Manufacturer * man
        if (nIndex != -1)
        {
                m_modelListCtrl.SetItemState(nIndex, LVIS_SELECTED, LVIS_SELECTED);
-               m_modelListCtrl.EnsureVisible(nIndex, FALSE);
+               AutoScroll( m_modelListCtrl, nIndex );
 
                m_modelListCtrl.SetFocus();
        }
@@ -291,7 +214,6 @@ CThirdPage::SelectMatch(Printer * printer, Service * service, Manufacturer * man
        CopyPrinterSettings( printer, service, manufacturer, model );
 }
 
-
 void
 CThirdPage::SelectMatch(Manufacturers & manufacturers, Printer * printer, Service * service, Manufacturer * manufacturer, Model * model)
 {
@@ -300,7 +222,6 @@ CThirdPage::SelectMatch(Manufacturers & manufacturers, Printer * printer, Servic
        SelectMatch( printer, service, manufacturer, model );
 }
 
-
 // --------------------------------------------------------
 // CopyPrinterSettings
 //
@@ -311,6 +232,8 @@ CThirdPage::SelectMatch(Manufacturers & manufacturers, Printer * printer, Servic
 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;
@@ -354,31 +277,80 @@ CThirdPage::CopyPrinterSettings( Printer * printer, Service * service, Manufactu
 
                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
 //
-// This function does all the heavy lifting in parsing inf
-// files.  It is called to parse both ntprint.inf, and driver
-// files that might be shipped on a printer's installation
-// disk
-//
-// The inf file is not totally parsed.  I only want to determine
-// the manufacturer and models that are involved. I leave it
-// to printui.dll to actually copy the driver files to the
-// right places.
-//
-// I was aiming to parse as little as I could so as not to
-// duplicate the parsing code that is contained in Windows.  There
-// are no public APIs for parsing inf files.
-//
-// That part of the inf file that we're interested in has a fairly
-// easy format.  Tags are strings that are enclosed in brackets.
-// We are only interested in [MANUFACTURERS] and models.
-//
-// The only potentially opaque thing about this function is the 
+// 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,
@@ -390,305 +362,190 @@ CThirdPage::CopyPrinterSettings( Printer * printer, Service * service, Manufactu
 OSStatus
 CThirdPage::LoadPrintDriverDefsFromFile(Manufacturers & manufacturers, const CString & filename, bool checkForDuplicateModels )
 {
-       PrinterParsingState             state           = Looking;
-       Manufacturers::iterator iter            = manufacturers.end();
-       CStdioFileEx                    file;
-       CFileException                  feError;
-       CString                                 s;
-       OSStatus                                err;
-       BOOL                                    ok;
-
-       typedef std::map<CString, CString> StringMap;
+       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 );
 
-       StringMap                               strings;
-       ok = file.Open( filename,  CFile::modeRead|CFile::typeText, &feError);
-       err = translate_errno( ok, errno_compat(), kUnknownErr );
+       // 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 );
 
-       check ( state == Looking );
-       check ( iter == manufacturers.end() );
+       sectionCount = SetupGetLineCount( handle, section );
+       translate_errno( sectionCount != -1, GetLastError(), kUnknownErr );
+       require_noerr( err, exit );
 
-       //
-       // first, parse the file looking for string sections
-       //
-       while (file.ReadString(s))
+       memset( &manufacturerContext, 0, sizeof( manufacturerContext ) );
+                       
+       for ( LONG i = 0; i < sectionCount; i++ )
        {
-               //
-               // check for comment
-               //
-               if (s.Find(';') == 0)
+               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 )
                {
-                       continue;
-               }
-
-               //
-               // check for tag
-               //
-               else if (s.Find('[') == 0)
-               {
-                       //
-                       // handle any capitalization issues here
-                       //
-                       CString tag = s;
-
-                       tag.MakeLower();
-
-                       if (tag == L"[strings]")
-                       {
-                               state = ParsingStrings;
-                       }
-                       else
-                       {
-                               state = Looking;
-                       }
+                       ok = SetupFindFirstLine( handle, section, NULL, &manufacturerContext );
+                       err = translate_errno( ok, GetLastError(), kUnknownErr );
+                       require_noerr( err, exit );
                }
                else
                {
-                       switch (state)
-                       {
-                               case ParsingStrings:
-                               {
-                                       int     curPos = 0;
-
-                                       if (s.GetLength() > 0)
-                                       {
-                                               CString key = s.Tokenize(L"=",curPos);
-                                               CString val = s.Tokenize(L"=",curPos);
-
-                                               //
-                                               // get rid of all delimiters
-                                               //
-                                               key.Trim();
-                                               val.Remove('"');
-       
-                                               //
-                                               // and store it
-                                               //
-                                               strings[key] = val;
-                                       }
-                               }
-                               break;
-                       }
+                       ok = SetupFindNextLine( &manufacturerContext, &manufacturerContext );
+                       err = translate_errno( ok, GetLastError(), kUnknownErr );
+                       require_noerr( err, exit );
                }
-       }
-
-       file.Close();
 
-       ok = file.Open( filename,  CFile::modeRead|CFile::typeText, &feError);
-       err = translate_errno( ok, errno_compat(), kUnknownErr );
-       require_noerr( err, exit );
-
-       state = Looking;
+               ok = SetupGetStringField( &manufacturerContext, 0, line, sizeof( line ), NULL );
+               err = translate_errno( ok, GetLastError(), kUnknownErr );
+               require_noerr( err, exit );
+               manufacturerName = line;
 
-       check ( iter == manufacturers.end() );
+               ok = SetupGetLineText( &manufacturerContext, handle, NULL, NULL, line, sizeof( line ), NULL );
+               err = translate_errno( ok, GetLastError(), kUnknownErr );
+               require_noerr( err, exit );
 
-       while (file.ReadString(s))
-       {
+               // Try to find some model section name that has entries. Explanation of int file structure
+               // can be found at:
                //
-               // check for comment
-               //
-               if (s.Find(';') == 0)
+               // <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 )
                {
-                       continue;
+                       CString targetOSVersion;
+
+                       targetOSVersion         = modelSectionNameDecl.GetNext( p );
+                       modelSectionName        = baseModelName + TEXT( "." ) + targetOSVersion;
+                       modelCount                      = SetupGetLineCount( handle, modelSectionName );
                }
 
-               //
-               // check for tag
-               //
-               else if (s.Find('[') == 0)
+               if ( modelCount > 0 )
                {
-                       //
-                       // handle any capitalization issues here
-                       //
-                       CString tag = s;
+                       manufacturerName = NormalizeManufacturerName( manufacturerName );
 
-                       tag.MakeLower();
+                       iter = manufacturers.find( manufacturerName );
 
-                       if (tag == L"[manufacturer]")
+                       if ( iter != manufacturers.end() )
                        {
-                               state = ParsingManufacturers;
+                               manufacturer = iter->second;
+                               require_action( manufacturer, exit, err = kUnknownErr );
                        }
                        else
                        {
-                               // remove the leading and trailing delimiters
-                               //
-                               s.Remove('[');
-                               s.Remove(']');
-
-                               // check to see if this is a printer entry
-                               //
-                               iter = manufacturers.find(s);
-
-                               if (iter != manufacturers.end())
+                               try
                                {
-                                       state = ParsingModels;
+                                       manufacturer = new Manufacturer;
                                }
-                               else
+                               catch (...)
                                {
-                                       state = Looking;
+                                       manufacturer = NULL;
                                }
-                       }
-               }
-               //
-               // only look at this if the line isn't empty, or
-               // if it isn't a comment
-               //
-               else if ((s.GetLength() > 0) && (s.Find(';') != 0))
-               {
-                       switch (state)
-                       {
-                               //
-                               // if we're parsing manufacturers, then we will parse
-                               // an entry of the form key=val, where key is a delimited
-                               // string specifying a manufacturer name, and val is
-                               // a tag that is used later in the file.  the key is 
-                               // delimited by either '"' (quotes) or '%' (percent sign).
-                               //
-                               // the tag is used further down the file when models are
-                               // declared.  this allows multiple manufacturers to exist
-                               // in a single inf file.
-                               //
-                               case ParsingManufacturers:
-                               {
-                                       Manufacturer    *       manufacturer;
-                                       int                                     curPos = 0;
-
-                                       CString key = s.Tokenize(L"=",curPos);
-                                       CString val = s.Tokenize(L"=",curPos);
-
-                                       try
-                                       {
-                                               manufacturer = new Manufacturer;
-                                       }
-                                       catch (...)
-                                       {
-                                               manufacturer = NULL;
-                                       }
-
-                                       require_action( manufacturer, exit, err = kNoMemoryErr );
 
-                                       //
-                                       // if it's a variable, look it up
-                                       //
-                                       if (key.Find('%') == 0)
-                                       {
-                                               StringMap::iterator it;
-
-                                               key.Remove('%');
-
-                                               it = strings.find(key);
-
-                                               if (it != strings.end())
-                                               {
-                                                       key = it->second;
-                                               }
-                                       }
-                                       else
-                                       {
-                                               key.Remove('"');
-                                       }
-
-                                       val.TrimLeft();
-                                       val.TrimRight();
+                               require_action( manufacturer, exit, err = kNoMemoryErr );
 
-                                       //
-                                       // why is there no consistency in inf files?
-                                       //
-                                       if (val.GetLength() == 0)
-                                       {
-                                               val = key;
-                                       }
+                               manufacturer->name                                      = manufacturerName;
+                               manufacturers[ manufacturerName ]       = manufacturer;
+                       }
 
-                                       //
-                                       // fix the manufacturer name if necessary
-                                       //
-                                       curPos  =       0;
-                                       val             =       val.Tokenize(L",", curPos);
+                       memset( &modelContext, 0, sizeof( modelContext ) );
 
-                                       manufacturer->name = NormalizeManufacturerName( key );
-                                       manufacturer->tag  = val;
+                       for ( LONG j = 0; j < modelCount; j++ )
+                       {
+                               CString modelName;
+                               Model * model;
 
-                                       manufacturers[val] = manufacturer;
+                               if ( j == 0 )
+                               {
+                                       ok = SetupFindFirstLine( handle, modelSectionName, NULL, &modelContext );
+                                       err = translate_errno( ok, GetLastError(), kUnknownErr );
+                                       require_noerr( err, exit );
                                }
-                               break;
-
-                               case ParsingModels:
+                               else
                                {
-                                       check( iter != manufacturers.end() );
-
-                                       Model   *       model;
-                                       int                     curPos = 0;
-
-                                       CString name            = s.Tokenize(L"=",curPos);
-                                       CString description = s.Tokenize(L"=",curPos);
-                                       
-                                       if (name.Find('%') == 0)
-                                       {
-                                               StringMap::iterator it;
+                                       SetupFindNextLine( &modelContext, &modelContext );
+                                       err = translate_errno( ok, GetLastError(), kUnknownErr );
+                                       require_noerr( err, exit );
+                               }
 
-                                               name.Remove('%');
+                               ok = SetupGetStringField( &modelContext, 0, line, sizeof( line ), NULL );
+                               err = translate_errno( ok, GetLastError(), kUnknownErr );
+                               require_noerr( err, exit );
 
-                                               it = strings.find(name);
+                               modelName = line;
 
-                                               if (it != strings.end())
-                                               {
-                                                       name = it->second;
-                                               }
-                                       }
-                                       else
-                                       {
-                                               name.Remove('"');
-                                       }
-
-                                       name.Trim();
-                                       description.Trim();
-                                       
-                                       //
-                                       // If true, see if we've seen this guy before
-                                       //
-                                       if (checkForDuplicateModels == true)
-                                       {
-                                               if ( MatchModel( iter->second, ConvertToModelName( name ) ) != NULL )
-                                               {
-                                                       continue;
-                                               }
-                                       }
-
-                                       try
-                                       {
-                                               model = new Model;
-                                       }
-                                       catch (...)
+                               if (checkForDuplicateModels == true)
+                               {
+                                       if ( MatchModel( manufacturer, ConvertToModelName( modelName ) ) != NULL )
                                        {
-                                               model = NULL;
+                                               continue;
                                        }
+                               }
 
-                                       require_action( model, exit, err = kNoMemoryErr );
-
-                                       model->infFileName              =       filename;
-                                       model->displayName              =       name;
-                                       model->name                             =       name;
-                                       model->driverInstalled  =       false;
-
-                                       iter->second->models.push_back(model);
+                               //
+                               // 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;
                                }
-                               break;
 
-                               default:
+                               try
                                {
-                                       // pay no attention if we are in any other state
+                                       model = new Model;
                                }
-                               break;
+                               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:
 
-       file.Close();
+       if ( handle != INVALID_HANDLE_VALUE )
+       {
+               SetupCloseInfFile( handle );
+               handle = NULL;
+       }
 
-       return (err);
+       return err;
 }
 
 
@@ -744,7 +601,7 @@ CThirdPage::LoadPrintDriverDefs( Manufacturers & manufacturers )
        
                        //
                        // skip over anything that doesn't have a manufacturer field.  This
-                       // fixes a bug that I noticed that occurred after I installed 
+                       // 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.
                        //
@@ -826,7 +683,6 @@ exit:
        return err;
 }
 
-
 // -------------------------------------------------------
 // LoadGenericPrintDriverDefs
 //
@@ -850,10 +706,10 @@ CThirdPage::LoadGenericPrintDriverDefs( Manufacturers & manufacturers )
        // First try and find our generic driver names
 
        iter = m_manufacturers.find(L"HP");
-       require_action( iter != manufacturers.end(), exit, err = kUnknownErr );
+       require_action( iter != m_manufacturers.end(), exit, err = kUnknownErr );
        manufacturer = iter->second;
 
-       // Look for Postscript 
+       // Look for Postscript
 
        model = manufacturer->find( kGenericPSColorDriver );
 
@@ -865,7 +721,6 @@ CThirdPage::LoadGenericPrintDriverDefs( Manufacturers & manufacturers )
        if ( model )
        {
                psDriverName = model->name;
-       
        }
 
        // Look for PCL
@@ -879,7 +734,7 @@ CThirdPage::LoadGenericPrintDriverDefs( Manufacturers & manufacturers )
 
        if ( model )
        {
-               pclDriverName = model->name;    
+               pclDriverName = model->name;
        }
 
        // If we found either a generic PS driver, or a generic PCL driver,
@@ -961,7 +816,7 @@ exit:
 // ------------------------------------------------------
 // ConvertToManufacturerName
 //
-// This function is responsible for tweaking the 
+// 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)
@@ -998,7 +853,6 @@ CThirdPage::ConvertToManufacturerName( const CString & name )
        return lower;
 }
 
-
 // ------------------------------------------------------
 // ConvertToModelName
 //
@@ -1019,7 +873,6 @@ CThirdPage::ConvertToModelName( const CString & name )
        return lower;
 }
 
-
 // ------------------------------------------------------
 // NormalizeManufacturerName
 //
@@ -1044,7 +897,6 @@ CThirdPage::NormalizeManufacturerName( const CString & name )
        return normalized;
 }
 
-
 // -------------------------------------------------------
 // MatchPrinter
 //
@@ -1117,7 +969,7 @@ OSStatus CThirdPage::MatchPrinter(Manufacturers & manufacturers, Printer * print
                                hasGenericDriver = true;
                        }
 
-                       // <rdar://problem/4190104> Use "application/octet-stream" to determine if CUPS 
+                       // <rdar://problem/4190104> Use "application/octet-stream" to determine if CUPS
                        // shared queue supports raw
 
                        if ( q->pdl.Find( L"application/octet-stream" ) != -1 )
@@ -1125,9 +977,18 @@ OSStatus CThirdPage::MatchPrinter(Manufacturers & manufacturers, Printer * print
                                useCUPSWorkaround = false;
                        }
 
-                       if ( useCUPSWorkaround && printer->isSharedFromOSX && hasGenericDriver )
+                       if ( useCUPSWorkaround && printer->isCUPSPrinter && hasGenericDriver )
                        {
-                               SelectMatch(manufacturers, printer, service, genericManufacturer, genericModel );
+                               //
+                               // <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
                        {
@@ -1145,19 +1006,32 @@ OSStatus CThirdPage::MatchPrinter(Manufacturers & manufacturers, Printer * print
        if (found)
        {
                text.LoadString(IDS_PRINTER_MATCH_GOOD);
+               err = kNoErr;
        }
        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
+                       //
+                       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);
                }
 
-               SelectMatch( manufacturers, printer, service, genericManufacturer, genericModel );
+               err = kNoErr;
        }
        else
        {
@@ -1187,9 +1061,15 @@ OSStatus CThirdPage::MatchPrinter(Manufacturers & manufacturers, Printer * print
                        if (nIndex != -1)
                        {
                                m_manufacturerListCtrl.SetItemState(nIndex, LVIS_SELECTED, LVIS_SELECTED);
-                               m_manufacturerListCtrl.EnsureVisible(nIndex, FALSE);
+
+                               //
+                               //<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);
@@ -1197,7 +1077,6 @@ OSStatus CThirdPage::MatchPrinter(Manufacturers & manufacturers, Printer * print
        return err;
 }
 
-
 // ------------------------------------------------------
 // MatchManufacturer
 //
@@ -1234,7 +1113,6 @@ CThirdPage::MatchManufacturer( Manufacturers & manufacturers, const CString & na
        return NULL;
 }
 
-
 // -------------------------------------------------------
 // MatchModel
 //
@@ -1285,7 +1163,6 @@ CThirdPage::MatchModel(Manufacturer * manufacturer, const CString & name)
        return NULL;
 }
 
-
 // -------------------------------------------------------
 // MatchGeneric
 //
@@ -1315,12 +1192,12 @@ CThirdPage::MatchGeneric( Manufacturers & manufacturers, Printer * printer, Serv
        pdl = q->pdl;
        pdl.MakeLower();
 
-       if ( pdl.Find( kPDLPCLKey ) != -1 )
+       if ( m_genericPCL && ( pdl.Find( kPDLPCLKey ) != -1 ) )
        {
                *model  = m_genericPCL;
                ok              = TRUE;
        }
-       else if ( pdl.Find( kPDLPostscriptKey ) != -1 )
+       else if ( m_genericPostscript && ( pdl.Find( kPDLPostscriptKey ) != -1 ) )
        {
                *model  = m_genericPostscript;
                ok              = TRUE;
@@ -1331,7 +1208,6 @@ exit:
        return ok;
 }
 
-
 // -----------------------------------------------------------
 // OnInitPage
 //
@@ -1346,12 +1222,9 @@ OSStatus CThirdPage::OnInitPage()
        OSStatus        err = kNoErr;
 
        // Load printer icon
-
-
-
        check( m_printerImage == NULL );
        
-       m_printerImage = (CStatic*) GetDlgItem( IDR_MANIFEST );
+       m_printerImage = (CStatic*) GetDlgItem( 1 );    // 1 == IDR_MANIFEST
        check( m_printerImage );
 
        if ( m_printerImage != NULL )
@@ -1369,7 +1242,7 @@ OSStatus CThirdPage::OnInitPage()
        //
 
        //
-       // we have to make sure that we only do this once.  Typically, 
+       // 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
@@ -1386,7 +1259,6 @@ OSStatus CThirdPage::OnInitPage()
        return (err);
 }
 
-
 void CThirdPage::DoDataExchange(CDataExchange* pDX)
 {
        CPropertyPage::DoDataExchange(pDX);
@@ -1398,7 +1270,6 @@ void CThirdPage::DoDataExchange(CDataExchange* pDX)
 
 }
 
-
 // ----------------------------------------------------------
 // OnSetActive
 //
@@ -1433,6 +1304,20 @@ CThirdPage::OnSetActive()
                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
        //
@@ -1448,9 +1333,14 @@ CThirdPage::OnSetActive()
        // and try and match the printer
        //
 
-       if ( psheet->GetLastPage() == psheet->GetPage(1) )
+       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
        {
@@ -1462,14 +1352,13 @@ exit:
        return CPropertyPage::OnSetActive();
 }
 
-
 BOOL
 CThirdPage::OnKillActive()
 {
        CPrinterSetupWizardSheet * psheet;
 
        psheet = reinterpret_cast<CPrinterSetupWizardSheet*>(GetParent());
-       require_quiet( psheet, exit );   
+       require_quiet( psheet, exit );
    
        psheet->SetLastPage(this);
 
@@ -1478,7 +1367,6 @@ exit:
        return CPropertyPage::OnKillActive();
 }
 
-
 // -------------------------------------------------------
 // PopulateUI
 //
@@ -1507,7 +1395,6 @@ CThirdPage::PopulateUI(Manufacturers & manufacturers)
        return 0;
 }
 
-
 BEGIN_MESSAGE_MAP(CThirdPage, CPropertyPage)
        ON_NOTIFY(LVN_ITEMCHANGED, IDC_PRINTER_MANUFACTURER, OnLvnItemchangedManufacturer)
        ON_NOTIFY(LVN_ITEMCHANGED, IDC_PRINTER_MODEL, OnLvnItemchangedPrinterModel)
@@ -1515,7 +1402,6 @@ BEGIN_MESSAGE_MAP(CThirdPage, CPropertyPage)
        ON_BN_CLICKED(IDC_HAVE_DISK, OnBnClickedHaveDisk)
 END_MESSAGE_MAP()
 
-
 // CThirdPage message handlers
 void CThirdPage::OnLvnItemchangedManufacturer(NMHDR *pNMHDR, LRESULT *pResult)
 {
@@ -1592,7 +1478,6 @@ exit:
        *pResult = 0;
 }
 
-
 void CThirdPage::OnBnClickedDefaultPrinter()
 {
        CPrinterSetupWizardSheet        *       psheet;
@@ -1643,10 +1528,29 @@ void CThirdPage::OnBnClickedHaveDisk()
                        {
                                PopulateUI( manufacturers );
 
-                               MatchPrinter( manufacturers, printer, service, false );
+                               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
                {
@@ -1656,5 +1560,24 @@ void CThirdPage::OnBnClickedHaveDisk()
 
 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 );
+}