-/*
+/* -*- Mode: C; tab-width: 4 -*-
+ *
* Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved.
*
- * @APPLE_LICENSE_HEADER_START@
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
*
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
+ * http://www.apache.org/licenses/LICENSE-2.0
*
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
* limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
-
- Change History (most recent first):
-
-$Log: PrinterSetupWizardSheet.cpp,v $
-Revision 1.17 2004/10/12 18:02:53 shersche
-<rdar://problem/3764873> Escape '/', '@', '"' characters in printui command.
-Bug #: 3764873
-
-Revision 1.16 2004/09/13 21:27:22 shersche
-<rdar://problem/3796483> Pass the moreComing flag to OnAddPrinter and OnRemovePrinter callbacks
-Bug #: 3796483
-
-Revision 1.15 2004/09/11 05:59:06 shersche
-<rdar://problem/3785766> Fix code that generates unique printer names based on currently installed printers
-Bug #: 3785766
-
-Revision 1.14 2004/09/02 01:57:58 cheshire
-<rdar://problem/3783611> Fix incorrect testing of MoreComing flag
-
-Revision 1.13 2004/07/26 21:06:29 shersche
-<rdar://problem/3739200> Removing trailing '.' in hostname
-Bug #: 3739200
-
-Revision 1.12 2004/07/13 21:24:23 rpantos
-Fix for <rdar://problem/3701120>.
-
-Revision 1.11 2004/06/28 00:51:47 shersche
-Move call to EnumPrinters out of browse callback into standalone function
-
-Revision 1.10 2004/06/27 23:06:47 shersche
-code cleanup, make sure EnumPrinters returns non-zero value
-
-Revision 1.9 2004/06/27 15:49:31 shersche
-clean up some cruft in the printer browsing code
-
-Revision 1.8 2004/06/27 08:04:51 shersche
-copy selected printer to prevent printer being deleted out from under
-
-Revision 1.7 2004/06/26 23:27:12 shersche
-support for installing multiple printers of the same name
-
-Revision 1.6 2004/06/26 21:22:39 shersche
-handle spaces in file names
-
-Revision 1.5 2004/06/26 03:19:57 shersche
-clean up warning messages
-
-Submitted by: herscher
-
-Revision 1.4 2004/06/25 02:26:52 shersche
-Normalize key fields in text record entries
-Submitted by: herscher
-
-Revision 1.3 2004/06/24 20:12:07 shersche
-Clean up source code
-Submitted by: herscher
-
-Revision 1.2 2004/06/23 17:58:21 shersche
-<rdar://problem/3701837> eliminated memory leaks on exit
-<rdar://problem/3701926> installation of a printer that is already installed results in a no-op
-Bug #: 3701837, 3701926
-Submitted by: herscher
-
-Revision 1.1 2004/06/18 04:36:57 rpantos
-First checked in
-
-
-*/
+ */
#include "stdafx.h"
#include "PrinterSetupWizardApp.h"
#include "DebugServices.h"
#include "WinServices.h"
#include "About.h"
+#include "tcpxcv.h"
#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_SERVICE_EVENT ( WM_USER + 0x100 )
-#define WM_PROCESS_EVENT ( WM_USER + 0x101 )
+#define WM_SOCKET_EVENT ( WM_USER + 0x100 )
+#define WM_PROCESS_EVENT ( WM_USER + 0x101 )
+
+
+static BOOL
+Is64BitWindows()
+{
+#if defined(_WIN64)
+ return TRUE; // 64-bit programs run only on Win64
+#else
+ typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS)( HANDLE, PBOOL );
+ LPFN_ISWOW64PROCESS fnIsWow64Process;
+ BOOL bIsWow64 = FALSE;
+
+ fnIsWow64Process = ( LPFN_ISWOW64PROCESS ) GetProcAddress( GetModuleHandle( TEXT( "kernel32" ) ), "IsWow64Process" );
+
+ if ( fnIsWow64Process != NULL )
+ {
+ BOOL ok;
+
+ ok = fnIsWow64Process( GetCurrentProcess(), &bIsWow64 );
-// Service Types
+ if ( !ok )
+ {
+ bIsWow64 = FALSE;
+ }
+ }
-#define kPDLDataStreamServiceType "_pdl-datastream._tcp"
-#define kLPRServiceType "_printer._tcp"
-#define kIPPServiceType "_ipp._tcp"
+ return bIsWow64;
+#endif
+}
// CPrinterSetupWizardSheet
+CPrinterSetupWizardSheet * CPrinterSetupWizardSheet::m_self;
IMPLEMENT_DYNAMIC(CPrinterSetupWizardSheet, CPropertySheet)
CPrinterSetupWizardSheet::CPrinterSetupWizardSheet(UINT nIDCaption, CWnd* pParentWnd, UINT iSelectPage)
:CPropertySheet(nIDCaption, pParentWnd, iSelectPage),
- m_selectedPrinter(NULL)
+ m_selectedPrinter(NULL),
+ m_driverThreadExitCode( 0 ),
+ m_driverThreadFinished( false ),
+ m_pdlBrowser( NULL ),
+ m_ippBrowser( NULL ),
+ m_lprBrowser( NULL ),
+ m_lastPage( NULL )
{
m_arrow = LoadCursor(0, IDC_ARROW);
m_wait = LoadCursor(0, IDC_APPSTARTING);
m_active = m_arrow;
+ m_self = this;
Init();
+
+ LoadPrinterNames();
}
CPrinterSetupWizardSheet::~CPrinterSetupWizardSheet()
{
- //
- // rdar://problem/3701837 memory leaks
- //
- // Clean up the ServiceRef and printer list on exit
- //
- if (m_pdlBrowser != NULL)
- {
- DNSServiceRefDeallocate(m_pdlBrowser);
- m_pdlBrowser = NULL;
- }
+ Printer * printer;
- while (m_printerList.size() > 0)
+ while ( m_printers.size() > 0 )
{
- Printer * printer = m_printerList.front();
-
- m_printerList.pop_front();
+ printer = m_printers.front();
+ m_printers.pop_front();
delete printer;
}
- if (m_selectedPrinter != NULL)
- {
- delete m_selectedPrinter;
- }
-}
-
-
-// ------------------------------------------------------
-// InstallEventHandler
-//
-// Installs an event handler for DNSService events.
-//
-int
-CPrinterSetupWizardSheet::InstallEventHandler(EventHandler * handler)
-{
- PrinterList::iterator iter;
-
- m_eventHandlerList.push_back(handler);
-
- iter = m_printerList.begin();
-
- while (iter != m_printerList.end())
- {
- Printer * printer = *iter++;
-
- handler->OnAddPrinter(printer, iter != m_printerList.end());
- }
-
- return kNoErr;
+ m_self = NULL;
}
// ------------------------------------------------------
-// RemoveEventHandler
+// SetSelectedPrinter
//
-// Removes an event handler for DNSService events.
+// Manages setting a printer as the printer to install. Stops
+// any pending resolves.
//
-int
-CPrinterSetupWizardSheet::RemoveEventHandler(EventHandler * handler)
+void
+CPrinterSetupWizardSheet::SetSelectedPrinter(Printer * printer)
{
- m_eventHandlerList.remove(handler);
+ check( !printer || ( printer != m_selectedPrinter ) );
- return kNoErr;
+ m_selectedPrinter = printer;
}
-
-// ------------------------------------------------------
-// SetSelectedPrinter
-//
-// Manages setting a printer as the printer to install. Stops
-// any pending resolves.
-//
OSStatus
-CPrinterSetupWizardSheet::SetSelectedPrinter(Printer * printer)
+CPrinterSetupWizardSheet::LoadPrinterNames()
{
- OSStatus err;
+ PBYTE buffer = NULL;
+ OSStatus err = 0;
//
- // we only want one resolve going on at a time, so we check
- // state of the m_selectedPrinter
+ // rdar://problem/3701926 - Printer can't be installed twice
+ //
+ // First thing we want to do is make sure the printer isn't already installed.
+ // If the printer name is found, we'll try and rename it until we
+ // find a unique name
//
- if (m_selectedPrinter != NULL)
+ DWORD dwNeeded = 0, dwNumPrinters = 0;
+
+ BOOL ok = EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 4, NULL, 0, &dwNeeded, &dwNumPrinters);
+ err = translate_errno( ok, errno_compat(), kUnknownErr );
+
+ if ((err == ERROR_INSUFFICIENT_BUFFER) && (dwNeeded > 0))
{
- //
- // if we're currently resolving, then stop the resolve
- //
- if (m_selectedPrinter->serviceRef)
+ try
+ {
+ buffer = new unsigned char[dwNeeded];
+ }
+ catch (...)
{
- err = StopResolve(m_selectedPrinter);
- require_noerr(err, exit);
+ buffer = NULL;
}
+
+ require_action( buffer, exit, kNoMemoryErr );
+ ok = EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 4, buffer, dwNeeded, &dwNeeded, &dwNumPrinters);
+ err = translate_errno( ok, errno_compat(), kUnknownErr );
+ require_noerr( err, exit );
- delete m_selectedPrinter;
- m_selectedPrinter = NULL;
+ for (DWORD index = 0; index < dwNumPrinters; index++)
+ {
+ PRINTER_INFO_4 * lppi4 = (PRINTER_INFO_4*) (buffer + index * sizeof(PRINTER_INFO_4));
+
+ m_printerNames.push_back( lppi4->pPrinterName );
+ }
}
- check( m_selectedPrinter == NULL );
+exit:
- try
- {
- m_selectedPrinter = new Printer;
- }
- catch (...)
+ if (buffer != NULL)
{
- m_selectedPrinter = NULL;
+ delete [] buffer;
}
- require_action( m_selectedPrinter, exit, err = E_OUTOFMEMORY );
-
- m_selectedPrinter->window = printer->window;
- m_selectedPrinter->serviceRef = NULL;
- m_selectedPrinter->item = NULL;
- m_selectedPrinter->ifi = printer->ifi;
- m_selectedPrinter->name = printer->name;
- m_selectedPrinter->displayName = printer->displayName;
- m_selectedPrinter->actualName = printer->actualName;
- m_selectedPrinter->type = printer->type;
- m_selectedPrinter->domain = printer->domain;
- m_selectedPrinter->installed = printer->installed;
- m_selectedPrinter->deflt = printer->deflt;
- m_selectedPrinter->refs = 1;
-
- err = StartResolve(m_selectedPrinter);
- require_noerr(err, exit);
-
-exit:
-
return err;
}
+
// ------------------------------------------------------
// InstallPrinter
//
// Installs a printer with Windows.
//
-// NOTE: this works one of two ways, depending on whether
+// Note: this works one of two ways, depending on whether
// there are drivers already installed for this printer.
// If there are, then we can just create a port with XcvData,
// and then call AddPrinter. If not, we use the printui.dll
OSStatus
CPrinterSetupWizardSheet::InstallPrinter(Printer * printer)
+{
+ Logger log;
+ CUPSLibrary cupsLib;
+ Service * service = NULL;
+ BOOL ok;
+ OSStatus err = 0;
+
+ service = printer->services.front();
+ check( service );
+
+ if ( printer->isCUPSPrinter && cupsLib.IsInstalled() )
+ {
+ err = InstallPrinterCUPS( printer, service, cupsLib );
+ require_noerr( err, exit );
+ }
+ else
+ {
+ //
+ // if the driver isn't installed, then install it
+ //
+
+ if ( !printer->driverInstalled )
+ {
+ 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
+ //
+ while (!m_driverThreadFinished)
+ {
+ MSG msg;
+
+ GetMessage( &msg, m_hWnd, 0, 0 );
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+
+ //
+ // Wait until child process exits.
+ //
+ dwResult = WaitForSingleObject( hThread, INFINITE );
+ err = translate_errno( dwResult == WAIT_OBJECT_0, errno_compat(), err = kUnknownErr );
+ require_noerr_with_log( log, "WaitForSingleObject()", err, exit );
+
+ //
+ // check the return value of thread
+ //
+ require_noerr_with_log( log, "thread exit code", m_driverThreadExitCode, exit );
+
+ //
+ // now we know that the driver was successfully installed
+ //
+ printer->driverInstalled = true;
+ }
+
+ if ( service->type == kPDLServiceType )
+ {
+ err = InstallPrinterPort( printer, service, PROTOCOL_RAWTCP_TYPE, log );
+ require_noerr_with_log( log, "InstallPrinterPort()", err, exit );
+ err = InstallPrinterPDLAndLPR( printer, service, log );
+ require_noerr_with_log( log, "InstallPrinterPDLAndLPR()", err, exit );
+ }
+ else if ( service->type == kLPRServiceType )
+ {
+ err = InstallPrinterPort( printer, service, PROTOCOL_LPR_TYPE, log );
+ require_noerr_with_log( log, "InstallPrinterPort()", err, exit );
+ err = InstallPrinterPDLAndLPR( printer, service, log );
+ require_noerr_with_log( log, "InstallPrinterPDLAndLPR()", err, exit );
+ }
+ else if ( service->type == kIPPServiceType )
+ {
+ // There's no need to install a printer port for IPP printers, because
+ // the call to AddPrinter() will do that for us.
+
+ err = InstallPrinterIPP( printer, service, log );
+ require_noerr_with_log( log, "InstallPrinterIPP()", err, exit );
+ }
+ else
+ {
+ require_action_with_log( log, ( service->type == kPDLServiceType ) || ( service->type == kLPRServiceType ) || ( service->type == kIPPServiceType ), exit, err = kUnknownErr );
+ }
+ }
+
+ printer->installed = true;
+
+ //
+ // if the user specified a default printer, set it
+ //
+ if (printer->deflt)
+ {
+ ok = SetDefaultPrinter( printer->actualName );
+ err = translate_errno( ok, errno_compat(), err = kUnknownErr );
+ require_noerr_with_log( log, "SetDefaultPrinter()", err, exit );
+ }
+
+exit:
+
+ return err;
+}
+
+
+OSStatus
+CPrinterSetupWizardSheet::InstallPrinterPort( Printer * printer, Service * service, DWORD protocol, Logger & log )
{
PRINTER_DEFAULTS printerDefaults = { NULL, NULL, SERVER_ACCESS_ADMINISTER };
+ PORT_DATA_1 portData;
DWORD dwStatus;
DWORD cbInputData = 100;
PBYTE pOutputData = NULL;
DWORD cbOutputNeeded = 0;
- PORT_DATA_1 portData;
HANDLE hXcv = NULL;
- HANDLE hPrinter = NULL;
+ Queue * q;
BOOL ok;
OSStatus err;
- check(printer != NULL);
- check(printer->installed == false);
+ ZeroMemory(&portData, sizeof(PORT_DATA_1));
+
+ require_action_with_log( log, wcslen(printer->portName) < sizeof_array(portData.sztPortName), exit, err = kSizeErr );
+ wcscpy_s(portData.sztPortName, printer->portName);
+
+ q = service->queues.front();
+ check( q );
ok = OpenPrinter(L",XcvMonitor Standard TCP/IP Port", &hXcv, &printerDefaults);
err = translate_errno( ok, errno_compat(), kUnknownErr );
- require_noerr( err, exit );
-
+ require_noerr_with_log( log, "OpenPrinter()", err, exit );
+
//
// BUGBUG: MSDN said this is not required, but my experience shows it is required
//
pOutputData = NULL;
}
- require_action( pOutputData, exit, err = kNoMemoryErr );
-
- //
- // setup the port
- //
- ZeroMemory(&portData, sizeof(PORT_DATA_1));
- wcscpy(portData.sztPortName, printer->portName);
+ require_action_with_log( log, pOutputData, exit, err = kNoMemoryErr );
- portData.dwPortNumber = printer->portNumber;
+ portData.dwPortNumber = service->portNumber;
portData.dwVersion = 1;
+ portData.dwDoubleSpool = 1;
- portData.dwProtocol = PROTOCOL_RAWTCP_TYPE;
+ portData.dwProtocol = protocol;
portData.cbSize = sizeof PORT_DATA_1;
portData.dwReserved = 0L;
-
- wcscpy(portData.sztQueue, printer->hostname);
- wcscpy(portData.sztIPAddress, printer->hostname);
- wcscpy(portData.sztHostAddress, printer->hostname);
-
- ok = XcvData(hXcv, L"AddPort", (PBYTE) &portData, sizeof(PORT_DATA_1), pOutputData, cbInputData, &cbOutputNeeded, &dwStatus);
- err = translate_errno( ok, errno_compat(), kUnknownErr );
- require_noerr( err, exit );
-
- if (printer->driverInstalled)
- {
- PRINTER_INFO_2 pInfo;
-
- ZeroMemory(&pInfo, sizeof(pInfo));
-
- pInfo.pPrinterName = printer->actualName.GetBuffer();
- pInfo.pServerName = NULL;
- pInfo.pShareName = NULL;
- pInfo.pPortName = printer->portName.GetBuffer();
- pInfo.pDriverName = printer->model.GetBuffer();
- pInfo.pComment = printer->model.GetBuffer();
- pInfo.pLocation = L"";
- pInfo.pDevMode = NULL;
- pInfo.pDevMode = NULL;
- pInfo.pSepFile = L"";
- pInfo.pPrintProcessor = L"winprint";
- pInfo.pDatatype = L"RAW";
- pInfo.pParameters = L"";
- pInfo.pSecurityDescriptor = NULL;
- pInfo.Attributes = PRINTER_ATTRIBUTE_QUEUED;
- pInfo.Priority = 0;
- pInfo.DefaultPriority = 0;
- pInfo.StartTime = 0;
- pInfo.UntilTime = 0;
-
- hPrinter = AddPrinter(NULL, 2, (LPBYTE) &pInfo);
- err = translate_errno( hPrinter, errno_compat(), kUnknownErr );
- require_noerr( err, exit );
- }
- else
- {
- DWORD dwResult;
- HANDLE hThread;
- unsigned threadID;
-
-
- m_processFinished = false;
-
- //
- // create the thread
- //
- hThread = (HANDLE) _beginthreadex_compat( NULL, 0, InstallPrinterThread, printer, 0, &threadID );
- err = translate_errno( hThread, (OSStatus) GetLastError(), kUnknownErr );
- require_noerr( err, exit );
-
- //
- // go modal
- //
- while (!m_processFinished)
- {
- MSG msg;
-
- GetMessage( &msg, m_hWnd, 0, 0 );
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- }
-
- //
- // Wait until child process exits.
- //
- dwResult = WaitForSingleObject( hThread, INFINITE );
- err = translate_errno( dwResult == WAIT_OBJECT_0, errno_compat(), err = kUnknownErr );
- require_noerr( err, exit );
- }
- printer->installed = true;
+ require_action_with_log( log, wcslen(q->name) < sizeof_array(portData.sztQueue), exit, err = kSizeErr );
+ wcscpy_s(portData.sztQueue, q->name);
- //
- // if the user specified a default printer, set it
- //
- if (printer->deflt)
- {
- ok = SetDefaultPrinter(printer->actualName);
- err = translate_errno( ok, errno_compat(), err = kUnknownErr );
- require_noerr( err, exit );
- }
+ require_action_with_log( log, wcslen( service->hostname ) < sizeof_array(portData.sztHostAddress), exit, err = kSizeErr );
+ wcscpy_s( portData.sztHostAddress, service->hostname );
-exit:
+ ok = XcvData(hXcv, L"AddPort", (PBYTE) &portData, sizeof(PORT_DATA_1), pOutputData, cbInputData, &cbOutputNeeded, &dwStatus);
+ err = translate_errno( ok, errno_compat(), kUnknownErr );
+ require_noerr_with_log( log, "XcvData()", err, exit );
- if (hPrinter != NULL)
- {
- ClosePrinter(hPrinter);
- }
+exit:
if (hXcv != NULL)
{
}
-BEGIN_MESSAGE_MAP(CPrinterSetupWizardSheet, CPropertySheet)
-ON_MESSAGE( WM_SERVICE_EVENT, OnServiceEvent )
-ON_MESSAGE( WM_PROCESS_EVENT, OnProcessEvent )
-ON_WM_SETCURSOR()
-END_MESSAGE_MAP()
+OSStatus
+CPrinterSetupWizardSheet::InstallPrinterPDLAndLPR(Printer * printer, Service * service, Logger & log )
+{
+ PRINTER_INFO_2 pInfo;
+ HANDLE hPrinter = NULL;
+ Queue * q;
+ OSStatus err;
+ check(printer != NULL);
+ check(printer->installed == false);
-// ------------------------------------------------------
-// OnCommand
-//
-// Traps when the user hits Finish
-//
-BOOL CPrinterSetupWizardSheet::OnCommand(WPARAM wParam, LPARAM lParam)
+ q = service->queues.front();
+ check( q );
+
+ //
+ // add the printer
+ //
+ ZeroMemory(&pInfo, sizeof(pInfo));
+
+ pInfo.pPrinterName = printer->actualName.GetBuffer();
+ pInfo.pServerName = NULL;
+ pInfo.pShareName = NULL;
+ pInfo.pPortName = printer->portName.GetBuffer();
+ pInfo.pDriverName = printer->modelName.GetBuffer();
+ pInfo.pComment = printer->displayModelName.GetBuffer();
+ pInfo.pLocation = q->location.GetBuffer();
+ pInfo.pDevMode = NULL;
+ pInfo.pDevMode = NULL;
+ pInfo.pSepFile = L"";
+ pInfo.pPrintProcessor = L"winprint";
+ pInfo.pDatatype = L"RAW";
+ pInfo.pParameters = L"";
+ pInfo.pSecurityDescriptor = NULL;
+ pInfo.Attributes = PRINTER_ATTRIBUTE_QUEUED;
+ pInfo.Priority = 0;
+ pInfo.DefaultPriority = 0;
+ pInfo.StartTime = 0;
+ pInfo.UntilTime = 0;
+
+ hPrinter = AddPrinter(NULL, 2, (LPBYTE) &pInfo);
+ err = translate_errno( hPrinter, errno_compat(), kUnknownErr );
+ require_noerr_with_log( log, "AddPrinter()", err, exit );
+
+exit:
+
+ if (hPrinter != NULL)
+ {
+ ClosePrinter(hPrinter);
+ }
+
+ return err;
+}
+
+
+OSStatus
+CPrinterSetupWizardSheet::InstallPrinterIPP(Printer * printer, Service * service, Logger & log)
+{
+ DEBUG_UNUSED( service );
+
+ Queue * q = service->SelectedQueue();
+ HANDLE hPrinter = NULL;
+ PRINTER_INFO_2 pInfo;
+ OSStatus err;
+
+ check( q );
+
+ //
+ // add the printer
+ //
+ ZeroMemory(&pInfo, sizeof(PRINTER_INFO_2));
+
+ pInfo.pPrinterName = printer->actualName.GetBuffer();
+ pInfo.pPortName = printer->portName.GetBuffer();
+ pInfo.pDriverName = printer->modelName.GetBuffer();
+ pInfo.pPrintProcessor = L"winprint";
+ pInfo.pLocation = q->location.GetBuffer();
+ pInfo.pComment = printer->displayModelName.GetBuffer();
+ pInfo.Attributes = PRINTER_ATTRIBUTE_NETWORK | PRINTER_ATTRIBUTE_LOCAL;
+
+ hPrinter = AddPrinter(NULL, 2, (LPBYTE)&pInfo);
+ err = translate_errno( hPrinter, errno_compat(), kUnknownErr );
+ require_noerr_with_log( log, "AddPrinter()", err, exit );
+
+exit:
+
+ if ( hPrinter != NULL )
+ {
+ ClosePrinter(hPrinter);
+ }
+
+ return err;
+}
+
+
+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 )
+ON_WM_SETCURSOR()
+ON_WM_TIMER()
+END_MESSAGE_MAP()
+
+
+// ------------------------------------------------------
+// OnCommand
+//
+// Traps when the user hits Finish
+//
+BOOL CPrinterSetupWizardSheet::OnCommand(WPARAM wParam, LPARAM lParam)
{
//
// Check if this is OK
// ------------------------------------------------------
// OnInitDialog
//
-// Initializes this Dialog object. We start the browse here,
-// so that printers show up instantly when the user clicks
-// the next button.
+// Initializes this Dialog object.
//
BOOL CPrinterSetupWizardSheet::OnInitDialog()
{
OSStatus err;
CPropertySheet::OnInitDialog();
-
- //
- // setup the DNS-SD browsing
- //
- err = DNSServiceBrowse( &m_pdlBrowser, 0, 0, kPDLDataStreamServiceType, NULL, OnBrowse, this );
- require_noerr( err, exit );
-
- m_serviceRefList.push_back(m_pdlBrowser);
- err = WSAAsyncSelect((SOCKET) DNSServiceRefSockFD(m_pdlBrowser), m_hWnd, WM_SERVICE_EVENT, FD_READ|FD_CLOSE);
+ err = StartBrowse();
require_noerr( err, exit );
- LoadPrinterNames();
-
exit:
- if (err != kNoErr)
+ if ( err )
{
- WizardException exc;
+ StopBrowse();
+
+ if ( err == kDNSServiceErr_Firewall )
+ {
+ CString text, caption;
+
+ text.LoadString( IDS_FIREWALL );
+ caption.LoadString( IDS_FIREWALL_CAPTION );
+
+ MessageBox(text, caption, MB_OK|MB_ICONEXCLAMATION);
+ }
+ else
+ {
+ CString text, caption;
+
+ text.LoadString( IDS_NO_MDNSRESPONDER_SERVICE_TEXT );
+ caption.LoadString( IDS_ERROR_CAPTION );
- exc.text.LoadString(IDS_NO_MDNSRESPONDER_SERVICE_TEXT);
- exc.caption.LoadString(IDS_NO_MDNSRESPONDER_SERVICE_CAPTION);
+ MessageBox(text, caption, MB_OK|MB_ICONEXCLAMATION);
- throw(exc);
+ _exit( 0 );
+ }
}
return TRUE;
void
CPrinterSetupWizardSheet::OnOK()
{
+ CWnd * window;
+ OSStatus err;
+
check ( m_selectedPrinter != NULL );
SetWizardButtons( PSWIZB_DISABLEDFINISH );
- if ( InstallPrinter( m_selectedPrinter ) != kNoErr )
+ window = GetDlgItem( IDCANCEL );
+
+ if ( window )
+ {
+ window->EnableWindow( FALSE );
+ }
+
+ m_pgFourth.StartActivityIndicator();
+
+ err = InstallPrinter( m_selectedPrinter );
+
+ m_pgFourth.StopActivityIndicator();
+
+ if ( err != kNoErr )
{
CString caption;
CString message;
caption.LoadString(IDS_INSTALL_ERROR_CAPTION);
+ caption.AppendFormat( TEXT( " (%d)" ), err );
message.LoadString(IDS_INSTALL_ERROR_MESSAGE);
-
MessageBox(message, caption, MB_OK|MB_ICONEXCLAMATION);
}
+
+ StopBrowse();
}
void CPrinterSetupWizardSheet::Init(void)
{
- AddPage(&m_pgFirst);
AddPage(&m_pgSecond);
AddPage(&m_pgThird);
AddPage(&m_pgFourth);
m_psh.pszbmWatermark = MAKEINTRESOURCE(IDB_WATERMARK);
m_psh.pszbmHeader = MAKEINTRESOURCE(IDB_BANNER_ICON);
- m_psh.hInstance = AfxGetInstanceHandle();
+ m_psh.hInstance = GetNonLocalizedResources();
SetWizardMode();
}
-LONG
-CPrinterSetupWizardSheet::OnServiceEvent(WPARAM inWParam, LPARAM inLParam)
+LRESULT
+CPrinterSetupWizardSheet::OnSocketEvent(WPARAM inWParam, LPARAM inLParam)
{
if (WSAGETSELECTERROR(inLParam) && !(HIWORD(inLParam)))
{
}
-LONG
+LRESULT
CPrinterSetupWizardSheet::OnProcessEvent(WPARAM inWParam, LPARAM inLParam)
{
- DEBUG_UNUSED(inWParam);
DEBUG_UNUSED(inLParam);
- m_processFinished = true;
+ m_driverThreadExitCode = (DWORD) inWParam;
+ m_driverThreadFinished = true;
+
+ return 0;
+}
+
+
+unsigned WINAPI
+CPrinterSetupWizardSheet::InstallDriverThread( LPVOID inParam )
+{
+ Printer * printer = (Printer*) inParam;
+ DWORD exitCode = 0;
+ DWORD dwResult;
+ OSStatus err;
+ STARTUPINFO si;
+ PROCESS_INFORMATION pi;
+ BOOL ok;
+
+ check( printer );
+ check( m_self );
+
+ //
+ // because we're calling endthreadex(), C++ objects won't be cleaned up
+ // correctly. we'll nest the CString 'command' inside a block so
+ // that it's destructor will be invoked.
+ //
+ {
+ CString command;
+
+ ZeroMemory( &si, sizeof(si) );
+ si.cb = sizeof(si);
+ ZeroMemory( &pi, sizeof(pi) );
+
+ command.Format(L"rundll32.exe printui.dll,PrintUIEntry /ia /m \"%s\" /f \"%s\"", (LPCTSTR) printer->modelName, (LPCTSTR) printer->infFileName );
+
+ ok = CreateProcess(NULL, command.GetBuffer(), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
+ err = translate_errno( ok, errno_compat(), kUnknownErr );
+ require_noerr( err, exit );
+
+ dwResult = WaitForSingleObject( pi.hProcess, INFINITE );
+ translate_errno( dwResult == WAIT_OBJECT_0, errno_compat(), err = kUnknownErr );
+ require_noerr( err, exit );
+
+ ok = GetExitCodeProcess( pi.hProcess, &exitCode );
+ err = translate_errno( ok, errno_compat(), kUnknownErr );
+ require_noerr( err, exit );
+ }
+
+exit:
+
+ //
+ // Close process and thread handles.
+ //
+ if ( pi.hProcess )
+ {
+ CloseHandle( pi.hProcess );
+ }
+
+ if ( pi.hThread )
+ {
+ CloseHandle( pi.hThread );
+ }
+
+ //
+ // alert the main thread
+ //
+ m_self->PostMessage( WM_PROCESS_EVENT, err, exitCode );
+
+ _endthreadex_compat( 0 );
return 0;
}
void DNSSD_API
CPrinterSetupWizardSheet::OnBrowse(
- DNSServiceRef inRef,
- DNSServiceFlags inFlags,
- uint32_t inInterfaceIndex,
- DNSServiceErrorType inErrorCode,
- const char * inName,
- const char * inType,
- const char * inDomain,
- void * inContext )
+ DNSServiceRef inRef,
+ DNSServiceFlags inFlags,
+ uint32_t inInterfaceIndex,
+ DNSServiceErrorType inErrorCode,
+ const char * inName,
+ const char * inType,
+ const char * inDomain,
+ void * inContext )
{
DEBUG_UNUSED(inRef);
CPrinterSetupWizardSheet * self;
- Printer * printer;
- EventHandlerList::iterator it;
- DWORD printerNameCount;
bool moreComing = (bool) (inFlags & kDNSServiceFlagsMoreComing);
+ CPropertyPage * active;
+ Printer * printer = NULL;
+ Service * service = NULL;
+ OSStatus err = kNoErr;
require_noerr( inErrorCode, exit );
-
+
self = reinterpret_cast <CPrinterSetupWizardSheet*>( inContext );
require_quiet( self, exit );
- printer = self->LookUp(inName);
+ active = self->GetActivePage();
+ require_quiet( active, exit );
+
+ // Have we seen this printer before?
+
+ printer = self->Lookup( inName );
+
+ if ( printer )
+ {
+ service = printer->LookupService( inType );
+ }
- if (inFlags & kDNSServiceFlagsAdd)
+ if ( inFlags & kDNSServiceFlagsAdd )
{
- OSStatus err;
+ BOOL newPrinter = FALSE;
- if (printer != NULL)
+ if ( !printer )
{
- printer->refs++;
+ printer = self->OnAddPrinter( inInterfaceIndex, inName, inType, inDomain, moreComing );
+ require_action( printer, exit, err = kUnknownErr );
+ newPrinter = TRUE;
}
- else
+
+ // If we're looking at the browse list on page 2, then we need to call
+ // CPage2::OnAddPrinter() regardless of whether we've seen the printer
+ // or not because the moreComing flag might have changed from a previous
+ // call. If we only call CPage2::OnAddPrinter() when there's a new printer,
+ // we might not correctly update our UI, so if we've seen the printer before,
+ // call OnAddPrinter with a NULL parameter.
+
+ if ( self->GetActivePage() == &self->m_pgSecond )
{
- try
- {
- printer = new Printer;
- }
- catch (...)
- {
- printer = NULL;
- }
-
- require_action( printer, exit, err = E_OUTOFMEMORY );
-
- printer->window = self;
- printer->ifi = inInterfaceIndex;
- printer->name = inName;
- err = UTF8StringToStringObject(inName, printer->displayName);
- check_noerr( err );
- printer->actualName = printer->displayName;
-
- //
- // Compare this name against printers that are already installed
- // to avoid name clashes. Rename as necessary
- // to come up with a unique name.
- //
- printerNameCount = 2;
-
- for (;;)
- {
- PrinterNameMap::iterator it;
-
- it = self->m_printerNames.find(printer->actualName);
-
- if (it != self->m_printerNames.end())
- {
- printer->actualName.Format(L"%s (%d)", printer->displayName, printerNameCount);
- }
- else
- {
- break;
- }
-
- printerNameCount++;
- }
-
- printer->type = inType;
- printer->domain = inDomain;
- printer->installed = false;
- printer->deflt = false;
- printer->refs = 1;
-
- self->m_printerList.push_back( printer );
-
- //
- // now invoke event handlers for AddPrinter event
- //
- for (it = self->m_eventHandlerList.begin(); it != self->m_eventHandlerList.end(); it++)
- {
- EventHandler * handler = *it;
+ self->m_pgSecond.OnAddPrinter( newPrinter ? printer : NULL, moreComing );
+ }
- handler->OnAddPrinter(printer, moreComing);
- }
+ if ( !service )
+ {
+ err = self->OnAddService( printer, inInterfaceIndex, inName, inType, inDomain );
+ require_noerr( err, exit );
+ }
+ else
+ {
+ service->refs++;
}
}
- else
+ else if ( printer )
{
- if ((printer != NULL) && (--printer->refs == 0))
- {
- //
- // now invoke event handlers for RemovePrinter event
- //
- for (it = self->m_eventHandlerList.begin(); it != self->m_eventHandlerList.end(); it++)
- {
- EventHandler * handler = *it;
-
- handler->OnRemovePrinter(printer, moreComing);
- }
-
- self->m_printerList.remove(printer);
+ check( service );
- //
- // check to see if we've selected this printer
- //
- if (self->m_selectedPrinter == printer)
- {
- //
- // this guy is being removed while we're resolving it...so let's
- // stop the resolve
- //
- if (printer->serviceRef != NULL)
- {
- self->StopResolve(printer);
- }
-
- self->m_selectedPrinter = NULL;
- }
+ err = self->OnRemoveService( service );
+ require_noerr( err, exit );
- delete printer;
+ if ( printer->services.size() == 0 )
+ {
+ err = self->OnRemovePrinter( printer, moreComing );
+ require_noerr( err, exit );
}
}
exit:
-
+
return;
}
DEBUG_UNUSED(inFlags);
DEBUG_UNUSED(inRef);
- Printer * printer;
CPrinterSetupWizardSheet * self;
- EventHandlerList::iterator it1;
- EventHandlerList::iterator it2;
+ Service * service;
+ Queue * q;
int idx;
OSStatus err;
require_noerr( inErrorCode, exit );
- printer = reinterpret_cast<Printer*>( inContext );
- require_quiet( printer, exit);
+ service = reinterpret_cast<Service*>( inContext );
+ require_quiet( service, exit);
- self = printer->window;
+ check( service->refs != 0 );
+
+ self = service->printer->window;
require_quiet( self, exit );
- err = self->StopResolve(printer);
- require_noerr(err, exit);
+ err = self->StopOperation( service->serviceRef );
+ require_noerr( err, exit );
//
// hold on to the hostname...
//
- err = UTF8StringToStringObject( inHostName, printer->hostname );
+ err = UTF8StringToStringObject( inHostName, service->hostname );
require_noerr( err, exit );
//
// <rdar://problem/3739200> remove the trailing dot on hostname
//
- idx = printer->hostname.ReverseFind('.');
+ idx = service->hostname.ReverseFind('.');
- if ((idx > 1) && ((printer->hostname.GetLength() - 1) == idx))
+ if ((idx > 1) && ((service->hostname.GetLength() - 1) == idx))
{
- printer->hostname.Delete(idx, 1);
+ service->hostname.Delete(idx, 1);
}
//
// hold on to the port
//
- printer->portNumber = ntohs(inPort);
+ service->portNumber = ntohs(inPort);
- //
- // parse the text record. we create a stringlist of text record
- // entries that can be interrogated later
- //
- while (inTXTSize)
- {
- char buf[256];
+ if ( service->qtotal == 1 )
+ {
+ //
+ // create a new queue
+ //
+ try
+ {
+ q = new Queue;
+ }
+ catch (...)
+ {
+ q = NULL;
+ }
- unsigned char num = *inTXT;
- check( (int) num < inTXTSize );
+ require_action( q, exit, err = E_OUTOFMEMORY );
- memset(buf, 0, sizeof(buf));
- memcpy(buf, inTXT + 1, num);
-
- inTXTSize -= (num + 1);
- inTXT += (num + 1);
+ //
+ // parse the text record.
+ //
+
+ err = self->ParseTextRecord( service, q, inTXTSize, inTXT );
+ require_noerr( err, exit );
+
+ service->queues.push_back( q );
- CString elem;
+ //
+ // we've completely resolved this service
+ //
- err = UTF8StringToStringObject( buf, elem );
+ self->OnResolveService( service );
+ }
+ else
+ {
+ //
+ // if qtotal is more than 1, then we need to get additional
+ // text records. if not, then this service is considered
+ // resolved
+ //
+
+ err = DNSServiceQueryRecord(&service->serviceRef, 0, inInterfaceIndex, inFullName, kDNSServiceType_TXT, kDNSServiceClass_IN, OnQuery, (void*) service );
+ require_noerr( err, exit );
+
+ err = self->StartOperation( service->serviceRef );
require_noerr( err, exit );
+ }
+
+exit:
+
+ return;
+}
+
+
+void DNSSD_API
+CPrinterSetupWizardSheet::OnQuery(
+ DNSServiceRef inRef,
+ DNSServiceFlags inFlags,
+ uint32_t inInterfaceIndex,
+ DNSServiceErrorType inErrorCode,
+ const char * inFullName,
+ uint16_t inRRType,
+ uint16_t inRRClass,
+ uint16_t inRDLen,
+ const void * inRData,
+ uint32_t inTTL,
+ void * inContext)
+{
+ DEBUG_UNUSED( inTTL );
+ DEBUG_UNUSED( inRRClass );
+ DEBUG_UNUSED( inRRType );
+ DEBUG_UNUSED( inFullName );
+ DEBUG_UNUSED( inInterfaceIndex );
+ DEBUG_UNUSED( inRef );
+
+ Service * service = NULL;
+ Queue * q;
+ CPrinterSetupWizardSheet * self;
+ OSStatus err = kNoErr;
+
+ require_noerr( inErrorCode, exit );
+
+ service = reinterpret_cast<Service*>( inContext );
+ require_quiet( service, exit);
+
+ self = service->printer->window;
+ require_quiet( self, exit );
+
+ if ( ( inFlags & kDNSServiceFlagsAdd ) && ( inRDLen > 0 ) && ( inRData != NULL ) )
+ {
+ const char * inTXT = ( const char * ) inRData;
+
+ //
+ // create a new queue
+ //
+ try
+ {
+ q = new Queue;
+ }
+ catch (...)
+ {
+ q = NULL;
+ }
- int curPos = 0;
+ require_action( q, exit, err = E_OUTOFMEMORY );
- CString key = elem.Tokenize(L"=", curPos);
- CString val = elem.Tokenize(L"=", curPos);
+ err = service->printer->window->ParseTextRecord( service, q, inRDLen, inTXT );
+ require_noerr( err, exit );
+
+ //
+ // add this queue
+ //
- key.MakeLower();
+ service->queues.push_back( q );
- if ((key == L"usb_mfg") || (key == L"usb_manufacturer"))
+ if ( service->queues.size() == service->qtotal )
{
- printer->usb_MFG = val;
+ //
+ // else if moreComing is not set, then we're going
+ // to assume that we're done
+ //
+
+ self->StopOperation( service->serviceRef );
+
+ //
+ // sort the queues
+ //
+
+ service->queues.sort( OrderQueueFunc );
+
+ //
+ // we've completely resolved this service
+ //
+
+ self->OnResolveService( service );
}
- else if ((key == L"usb_mdl") || (key == L"usb_model"))
+ }
+
+exit:
+
+ if ( err && service && ( service->serviceRef != NULL ) )
+ {
+ service->printer->window->StopOperation( service->serviceRef );
+ }
+
+ return;
+}
+
+
+Printer*
+CPrinterSetupWizardSheet::OnAddPrinter(
+ uint32_t inInterfaceIndex,
+ const char * inName,
+ const char * inType,
+ const char * inDomain,
+ bool moreComing)
+{
+ Printer * printer = NULL;
+ DWORD printerNameCount;
+ OSStatus err;
+
+ DEBUG_UNUSED( inInterfaceIndex );
+ DEBUG_UNUSED( inType );
+ DEBUG_UNUSED( inDomain );
+ DEBUG_UNUSED( moreComing );
+
+ try
+ {
+ printer = new Printer;
+ }
+ catch (...)
+ {
+ printer = NULL;
+ }
+
+ require_action( printer, exit, err = E_OUTOFMEMORY );
+
+ printer->window = this;
+ printer->name = inName;
+
+ err = UTF8StringToStringObject(inName, printer->displayName);
+ check_noerr( err );
+ printer->actualName = printer->displayName;
+ printer->installed = false;
+ printer->deflt = false;
+ printer->resolving = 0;
+
+ // Compare this name against printers that are already installed
+ // to avoid name clashes. Rename as necessary
+ // to come up with a unique name.
+
+ printerNameCount = 2;
+
+ for (;;)
+ {
+ CPrinterSetupWizardSheet::PrinterNames::iterator it;
+
+ // <rdar://problem/4141221> Don't use find to do comparisons because we need to
+ // do a case insensitive string comparison
+
+ for ( it = m_printerNames.begin(); it != m_printerNames.end(); it++ )
{
- printer->usb_MDL = val;
+ if ( (*it).CompareNoCase( printer->actualName ) == 0 )
+ {
+ break;
+ }
}
- else if (key == L"description")
+
+ if (it != m_printerNames.end())
{
- printer->description = val;
+ printer->actualName.Format(L"%s (%d)", printer->displayName, printerNameCount);
}
- else if (key == L"product")
+ else
{
- printer->product = val;
+ break;
}
+
+ printerNameCount++;
}
+ m_printers.push_back( printer );
+
+exit:
+
+ return printer;
+}
+
+
+OSStatus
+CPrinterSetupWizardSheet::OnAddService(
+ Printer * printer,
+ uint32_t inInterfaceIndex,
+ const char * inName,
+ const char * inType,
+ const char * inDomain)
+{
+ Service * service = NULL;
+ OSStatus err = kNoErr;
+
+ DEBUG_UNUSED( inName );
+ DEBUG_UNUSED( inDomain );
+
+ try
+ {
+ service = new Service;
+ }
+ catch (...)
+ {
+ service = NULL;
+ }
+
+ require_action( service, exit, err = E_OUTOFMEMORY );
+
+ service->printer = printer;
+ service->ifi = inInterfaceIndex;
+ service->type = inType;
+ service->domain = inDomain;
+ service->qtotal = 1;
+ service->refs = 1;
+ service->serviceRef = NULL;
+
+ printer->services.push_back( service );
+
//
- // now invoke event handlers for Resolve event
+ // if the printer is selected, then we'll want to start a
+ // resolve on this guy
//
- it1 = self->m_eventHandlerList.begin();
- it2 = self->m_eventHandlerList.end();
- while (it1 != it2)
+ if ( printer == m_selectedPrinter )
{
- EventHandler * handler = *it1++;
-
- handler->OnResolvePrinter(printer);
+ StartResolve( service );
}
exit:
- return;
+ return err;
}
OSStatus
-CPrinterSetupWizardSheet::LoadPrinterNames()
+CPrinterSetupWizardSheet::OnRemovePrinter( Printer * printer, bool moreComing )
{
- PBYTE buffer = NULL;
- OSStatus err = 0;
+ CPropertyPage * active = GetActivePage();
+ OSStatus err = kNoErr;
- //
- // rdar://problem/3701926 - Printer can't be installed twice
- //
- // First thing we want to do is make sure the printer isn't already installed.
- // If the printer name is found, we'll try and rename it until we
- // find a unique name
- //
- DWORD dwNeeded = 0, dwNumPrinters = 0;
+ if ( active == &m_pgSecond )
+ {
+ m_pgSecond.OnRemovePrinter( printer, moreComing );
+ }
- BOOL ok = EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 4, NULL, 0, &dwNeeded, &dwNumPrinters);
- err = translate_errno( ok, errno_compat(), kUnknownErr );
+ m_printers.remove( printer );
- if ((err == ERROR_INSUFFICIENT_BUFFER) && (dwNeeded > 0))
+ if ( m_selectedPrinter == printer )
{
- try
+ m_selectedPrinter = NULL;
+
+ if ( ( active == &m_pgThird ) || ( active == &m_pgFourth ) )
{
- buffer = new unsigned char[dwNeeded];
+ CString caption;
+ CString message;
+
+ caption.LoadString( IDS_ERROR_CAPTION );
+ message.LoadString( IDS_PRINTER_UNAVAILABLE );
+
+ MessageBox(message, caption, MB_OK|MB_ICONEXCLAMATION);
+
+ SetActivePage( &m_pgSecond );
}
- catch (...)
+ }
+
+ delete printer;
+
+ return err;
+}
+
+
+OSStatus
+CPrinterSetupWizardSheet::OnRemoveService( Service * service )
+{
+ OSStatus err = kNoErr;
+
+ if ( service && ( --service->refs == 0 ) )
+ {
+ if ( service->serviceRef != NULL )
{
- buffer = NULL;
+ err = StopResolve( service );
+ require_noerr( err, exit );
}
+
+ service->printer->services.remove( service );
+
+ delete service;
+ }
+
+exit:
+
+ return err;
+}
+
+
+void
+CPrinterSetupWizardSheet::OnResolveService( Service * service )
+{
+ // Make sure that the active page is page 2
+
+ require_quiet( GetActivePage() == &m_pgSecond, exit );
+
+ if ( !--service->printer->resolving )
+ {
+ // sort the services now. we want the service that
+ // has the highest priority queue to be first in
+ // the list.
+
+ service->printer->services.sort( OrderServiceFunc );
+
+ // Now we can hit next
+
+ SetWizardButtons( PSWIZB_BACK|PSWIZB_NEXT );
- require_action( buffer, exit, kNoMemoryErr );
- ok = EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 4, buffer, dwNeeded, &dwNeeded, &dwNumPrinters);
- err = translate_errno( ok, errno_compat(), kUnknownErr );
- require_noerr( err, exit );
+ // Reset the cursor
+
+ m_active = m_arrow;
- for (DWORD index = 0; index < dwNumPrinters; index++)
- {
- PRINTER_INFO_4 * lppi4 = (PRINTER_INFO_4*) (buffer + index * sizeof(PRINTER_INFO_4));
+ // And tell page 2 about it
+
+ m_pgSecond.OnResolveService( service );
+ }
+
+exit:
+
+ return;
+}
+
+
+OSStatus
+CPrinterSetupWizardSheet::StartBrowse()
+{
+ OSStatus err;
+
+ //
+ // setup the DNS-SD browsing
+ //
+ err = DNSServiceBrowse( &m_pdlBrowser, 0, 0, kPDLServiceType, NULL, OnBrowse, this );
+ require_noerr( err, exit );
+
+ err = StartOperation( m_pdlBrowser );
+ require_noerr( err, exit );
+
+ err = DNSServiceBrowse( &m_lprBrowser, 0, 0, kLPRServiceType, NULL, OnBrowse, this );
+ require_noerr( err, exit );
+
+ err = StartOperation( m_lprBrowser );
+ require_noerr( err, exit );
+
+ err = DNSServiceBrowse( &m_ippBrowser, 0, 0, kIPPServiceType, NULL, OnBrowse, this );
+ require_noerr( err, exit );
+
+ err = StartOperation( m_ippBrowser );
+ require_noerr( err, exit );
- m_printerNames[lppi4->pPrinterName] = lppi4->pPrinterName;
+exit:
+
+ return err;
+}
+
+
+OSStatus
+CPrinterSetupWizardSheet::StopBrowse()
+{
+ OSStatus err;
+
+ err = StopOperation( m_pdlBrowser );
+ require_noerr( err, exit );
+
+ err = StopOperation( m_lprBrowser );
+ require_noerr( err, exit );
+
+ err = StopOperation( m_ippBrowser );
+ require_noerr( err, exit );
+
+ while ( m_printers.size() > 0 )
+ {
+ Printer * printer = m_printers.front();
+
+ m_printers.pop_front();
+
+ if ( printer->resolving )
+ {
+ StopResolve( printer );
}
+
+ delete printer;
}
exit:
- if (buffer != NULL)
+ return err;
+}
+
+
+OSStatus
+CPrinterSetupWizardSheet::StartResolve( Printer * printer )
+{
+ OSStatus err = kNoErr;
+ Services::iterator it;
+
+ check( printer );
+
+ for ( it = printer->services.begin(); it != printer->services.end(); it++ )
{
- delete [] buffer;
+ if ( (*it)->serviceRef == NULL )
+ {
+ err = StartResolve( *it );
+ require_noerr( err, exit );
+ }
}
+ m_selectedPrinter = printer;
+
+exit:
+
return err;
}
OSStatus
-CPrinterSetupWizardSheet::StartResolve(Printer * printer)
+CPrinterSetupWizardSheet::StartResolve( Service * service )
{
- OSStatus err;
+ OSStatus err = kNoErr;
- check( printer );
+ check( service->serviceRef == NULL );
- err = DNSServiceResolve( &printer->serviceRef, 0, 0, printer->name.c_str(), printer->type.c_str(), printer->domain.c_str(), (DNSServiceResolveReply) OnResolve, printer );
- require_noerr( err, exit);
+ //
+ // clean out any queues that were collected during a previous
+ // resolve
+ //
- m_serviceRefList.push_back(printer->serviceRef);
+ service->EmptyQueues();
- err = WSAAsyncSelect((SOCKET) DNSServiceRefSockFD(printer->serviceRef), m_hWnd, WM_SERVICE_EVENT, FD_READ|FD_CLOSE);
+ //
+ // now start the new resolve
+ //
+
+ err = DNSServiceResolve( &service->serviceRef, 0, 0, service->printer->name.c_str(), service->type.c_str(), service->domain.c_str(), (DNSServiceResolveReply) OnResolve, service );
+ require_noerr( err, exit );
+
+ err = StartOperation( service->serviceRef );
require_noerr( err, exit );
//
- // set the cursor to arrow+hourglass
+ // If we're not currently resolving, then disable the next button
+ // and set the cursor to hourglass
//
- m_active = m_wait;
- SetCursor(m_active);
+
+ if ( !service->printer->resolving )
+ {
+ SetWizardButtons( PSWIZB_BACK );
+
+ m_active = m_wait;
+ SetCursor(m_active);
+ }
+
+ service->printer->resolving++;
exit:
OSStatus
CPrinterSetupWizardSheet::StopResolve(Printer * printer)
{
- OSStatus err;
+ OSStatus err = kNoErr;
check( printer );
- check( printer->serviceRef );
- m_serviceRefList.remove( printer->serviceRef );
+ Services::iterator it;
- err = WSAAsyncSelect((SOCKET) DNSServiceRefSockFD(printer->serviceRef), m_hWnd, 0, 0);
- check(err == 0);
+ for ( it = printer->services.begin(); it != printer->services.end(); it++ )
+ {
+ if ( (*it)->serviceRef )
+ {
+ err = StopResolve( *it );
+ require_noerr( err, exit );
+ }
+ }
- DNSServiceRefDeallocate( printer->serviceRef );
+exit:
+
+ return err;
+}
- printer->serviceRef = NULL;
-
- //
- // set the cursor back to normal
- //
- m_active = m_arrow;
- SetCursor(m_active);
- return kNoErr;
+OSStatus
+CPrinterSetupWizardSheet::StopResolve( Service * service )
+{
+ OSStatus err;
+
+ check( service->serviceRef );
+
+ err = StopOperation( service->serviceRef );
+ require_noerr( err, exit );
+
+ service->printer->resolving--;
+
+exit:
+
+ return err;
}
-Printer*
-CPrinterSetupWizardSheet::LookUp(const char * inName)
+OSStatus
+CPrinterSetupWizardSheet::StartOperation( DNSServiceRef ref )
+{
+ OSStatus err;
+
+ err = WSAAsyncSelect((SOCKET) DNSServiceRefSockFD(ref), m_hWnd, WM_SOCKET_EVENT, FD_READ|FD_CLOSE);
+ require_noerr( err, exit );
+
+ m_serviceRefList.push_back( ref );
+
+exit:
+
+ return err;
+}
+
+
+OSStatus
+CPrinterSetupWizardSheet::StopOperation( DNSServiceRef & ref )
{
- PrinterList::iterator it1 = m_printerList.begin();
- PrinterList::iterator it2 = m_printerList.end();
+ OSStatus err = kNoErr;
- while (it1 != it2)
+ if ( ref )
{
- Printer * printer = *it1++;
+ m_serviceRefList.remove( ref );
- if (printer->name == inName)
+ if ( IsWindow( m_hWnd ) )
{
- return printer;
+ err = WSAAsyncSelect((SOCKET) DNSServiceRefSockFD( ref ), m_hWnd, 0, 0 );
+ require_noerr( err, exit );
}
+
+ DNSServiceRefDeallocate( ref );
+ ref = NULL;
}
- return NULL;
+exit:
+
+ return err;
}
-unsigned WINAPI
-CPrinterSetupWizardSheet::InstallPrinterThread( LPVOID inParam )
+OSStatus
+CPrinterSetupWizardSheet::ParseTextRecord( Service * service, Queue * q, uint16_t inTXTSize, const char * inTXT )
{
- check( inParam );
-
- Printer * printer = (Printer*) inParam;
- CString actualName;
- CString command;
- DWORD exitCode = 0;
- DWORD dwResult;
- OSStatus err;
- STARTUPINFO si;
- PROCESS_INFORMATION pi;
- BOOL ok;
+ check( service );
+ check( q );
- ZeroMemory( &si, sizeof(si) );
- si.cb = sizeof(si);
- ZeroMemory( &pi, sizeof(pi) );
+ // <rdar://problem/3946587> Use TXTRecord APIs declared in dns_sd.h
+
+ bool qtotalDefined = false;
+ const void * val;
+ char buf[256];
+ uint8_t len;
+ OSStatus err = kNoErr;
- //
- // <rdar://problem/3764873> Escape '\', '@', '"' characters which seem to cause problems for printui
- //
+ // <rdar://problem/3987680> Default to queue "lp"
- actualName = printer->actualName;
+ q->name = L"lp";
- actualName.Replace(L"\\", L"\\\\");
- actualName.Replace(L"@", L"\\@");
- actualName.Replace(L"\"", L"\\\"");
+ // <rdar://problem/4003710> Default pdl key to be "application/postscript"
+
+ q->pdl = L"application/postscript";
+
+ if ( ( val = TXTRecordGetValuePtr( inTXTSize, inTXT, "rp", &len ) ) != NULL )
+ {
+ // Stringize val ( doesn't have trailing '\0' yet )
+
+ memcpy( buf, val, len );
+ buf[len] = '\0';
+
+ err = UTF8StringToStringObject( buf, q->name );
+ require_noerr( err, exit );
+ }
- command.Format(L"rundll32.exe printui.dll,PrintUIEntry /if /b \"%s\" /f \"%s\" /r \"%s\" /m \"%s\"", (LPCTSTR) actualName, (LPCTSTR) printer->infFileName, (LPCTSTR) printer->portName, (LPCTSTR) printer->model);
+ if ( ( val = TXTRecordGetValuePtr( inTXTSize, inTXT, "pdl", &len ) ) != NULL )
+ {
+ // Stringize val ( doesn't have trailing '\0' yet )
- ok = CreateProcess(NULL, command.GetBuffer(), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
- err = translate_errno( ok, errno_compat(), kUnknownErr );
- require_noerr( err, exit );
+ memcpy( buf, val, len );
+ buf[len] = '\0';
- dwResult = WaitForSingleObject( pi.hProcess, INFINITE );
- translate_errno( dwResult == WAIT_OBJECT_0, errno_compat(), err = kUnknownErr );
- require_noerr( err, exit );
+ err = UTF8StringToStringObject( buf, q->pdl );
+ require_noerr( err, exit );
+ }
+
+ if ( ( ( val = TXTRecordGetValuePtr( inTXTSize, inTXT, "usb_mfg", &len ) ) != NULL ) ||
+ ( ( val = TXTRecordGetValuePtr( inTXTSize, inTXT, "usb_manufacturer", &len ) ) != NULL ) )
+ {
+ // Stringize val ( doesn't have trailing '\0' yet )
- ok = GetExitCodeProcess( pi.hProcess, &exitCode );
- err = translate_errno( ok, errno_compat(), kUnknownErr );
- require_noerr( err, exit );
+ memcpy( buf, val, len );
+ buf[len] = '\0';
- //
- // Close process and thread handles.
- //
- CloseHandle( pi.hProcess );
- CloseHandle( pi.hThread );
+ err = UTF8StringToStringObject( buf, q->usb_MFG );
+ require_noerr( err, exit );
+ }
+
+ if ( ( ( val = TXTRecordGetValuePtr( inTXTSize, inTXT, "usb_mdl", &len ) ) != NULL ) ||
+ ( ( val = TXTRecordGetValuePtr( inTXTSize, inTXT, "usb_model", &len ) ) != NULL ) )
+ {
+ // Stringize val ( doesn't have trailing '\0' yet )
+
+ memcpy( buf, val, len );
+ buf[len] = '\0';
+
+ err = UTF8StringToStringObject( buf, q->usb_MDL );
+ require_noerr( err, exit );
+ }
+
+ if ( ( val = TXTRecordGetValuePtr( inTXTSize, inTXT, "ty", &len ) ) != NULL )
+ {
+ // Stringize val ( doesn't have trailing '\0' yet )
+
+ memcpy( buf, val, len );
+ buf[len] = '\0';
+
+ err = UTF8StringToStringObject( buf, q->description );
+ require_noerr( err, exit );
+ }
+
+ if ( ( val = TXTRecordGetValuePtr( inTXTSize, inTXT, "product", &len ) ) != NULL )
+ {
+ // Stringize val ( doesn't have trailing '\0' yet )
+
+ memcpy( buf, val, len );
+ buf[len] = '\0';
+
+ err = UTF8StringToStringObject( buf, q->product );
+ require_noerr( err, exit );
+ }
+
+ if ( ( val = TXTRecordGetValuePtr( inTXTSize, inTXT, "note", &len ) ) != NULL )
+ {
+ // Stringize val ( doesn't have trailing '\0' yet )
+
+ memcpy( buf, val, len );
+ buf[len] = '\0';
+
+ err = UTF8StringToStringObject( buf, q->location );
+ require_noerr( err, exit );
+ }
+
+ if ( ( val = TXTRecordGetValuePtr( inTXTSize, inTXT, "qtotal", &len ) ) != NULL )
+ {
+ // Stringize val ( doesn't have trailing '\0' yet )
+
+ memcpy( buf, val, len );
+ buf[len] = '\0';
+
+ service->qtotal = (unsigned short) atoi( buf );
+ qtotalDefined = true;
+ }
+
+ if ( ( val = TXTRecordGetValuePtr( inTXTSize, inTXT, "priority", &len ) ) != NULL )
+ {
+ // Stringize val ( doesn't have trailing '\0' yet )
+
+ memcpy( buf, val, len );
+ buf[len] = '\0';
+
+ q->priority = atoi( buf );
+ }
+
+ // <rdar://problem/4124524> Was this printer discovered via OS X Printer Sharing?
+
+ if ( TXTRecordContainsKey( inTXTSize, inTXT, "printer-state" ) || TXTRecordContainsKey( inTXTSize, inTXT, "printer-type" ) )
+ {
+ service->printer->isCUPSPrinter = true;
+ }
exit:
- //
- // alert the main thread
- //
- printer->window->PostMessage( WM_PROCESS_EVENT, err, exitCode );
+ // The following code is to fix a problem with older HP
+ // printers that don't include "qtotal" in their text
+ // record. We'll check to see if the q->name is "TEXT"
+ // and if so, we're going to modify it to be "lp" so
+ // that we don't use the wrong queue
- return 0;
+ if ( !err && !qtotalDefined && ( q->name == L"TEXT" ) )
+ {
+ q->name = "lp";
+ }
+
+ return err;
+}
+
+
+Printer*
+CPrinterSetupWizardSheet::Lookup(const char * inName)
+{
+ check( inName );
+
+ Printer * printer = NULL;
+ Printers::iterator it;
+
+ for ( it = m_printers.begin(); it != m_printers.end(); it++ )
+ {
+ if ( (*it)->name == inName )
+ {
+ printer = *it;
+ break;
+ }
+ }
+
+ return printer;
+}
+
+
+bool
+CPrinterSetupWizardSheet::OrderServiceFunc( const Service * a, const Service * b )
+{
+ Queue * q1, * q2;
+
+ q1 = (a->queues.size() > 0) ? a->queues.front() : NULL;
+
+ q2 = (b->queues.size() > 0) ? b->queues.front() : NULL;
+
+ if ( !q1 && !q2 )
+ {
+ return true;
+ }
+ else if ( q1 && !q2 )
+ {
+ return true;
+ }
+ else if ( !q1 && q2 )
+ {
+ return false;
+ }
+ else if ( q1->priority < q2->priority )
+ {
+ return true;
+ }
+ else if ( q1->priority > q2->priority )
+ {
+ return false;
+ }
+ else if ( ( a->type == kPDLServiceType ) || ( ( a->type == kLPRServiceType ) && ( b->type == kIPPServiceType ) ) )
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
}
+
+
+bool
+CPrinterSetupWizardSheet::OrderQueueFunc( const Queue * q1, const Queue * q2 )
+{
+ return ( q1->priority <= q2->priority ) ? true : false;
+}
+
+
+