diff options
Diffstat (limited to 'mDNSResponder/Clients/PrinterSetupWizard/PrinterSetupWizardSheet.cpp')
-rw-r--r-- | mDNSResponder/Clients/PrinterSetupWizard/PrinterSetupWizardSheet.cpp | 1949 |
1 files changed, 1949 insertions, 0 deletions
diff --git a/mDNSResponder/Clients/PrinterSetupWizard/PrinterSetupWizardSheet.cpp b/mDNSResponder/Clients/PrinterSetupWizard/PrinterSetupWizardSheet.cpp new file mode 100644 index 00000000..47e5f911 --- /dev/null +++ b/mDNSResponder/Clients/PrinterSetupWizard/PrinterSetupWizardSheet.cpp @@ -0,0 +1,1949 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "stdafx.h" +#include "PrinterSetupWizardApp.h" +#include "PrinterSetupWizardSheet.h" +#include "CommonServices.h" +#include "DebugServices.h" +#include "WinServices.h" +#include "About.h" +#include "tcpxcv.h" +#include <winspool.h> +#include <string> +#include <shlwapi.h> + +// unreachable code +#pragma warning(disable:4702) + + +#if( !TARGET_OS_WINDOWS_CE ) +# include <mswsock.h> +# include <process.h> +#endif + + +#if defined( UNICODE ) || defined( _UNICODE ) +# define GetEnv _wgetenv +#else +# define GetEnv getenv +#endif + +static TCHAR* +g_printerDriverFiles[] = // Printer driver files +{ + TEXT( "ps5ui.dll" ), + TEXT( "pscript.hlp" ), + TEXT( "pscript.ntf" ), + TEXT( "pscript5.dll" ), + TEXT( "cups6.ini" ), + TEXT( "cupsui6.dll" ), + TEXT( "cupsps6.dll" ) +}; + + +// Private Messages + +#define WM_SOCKET_EVENT ( WM_USER + 0x100 ) +#define WM_PROCESS_EVENT ( WM_USER + 0x101 ) + + +static BOOL +Is64BitWindows() +{ +#if defined(_WIN64) + return TRUE; // 64-bit programs run only on Win64 +#else + typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS)( HANDLE, PBOOL ); + LPFN_ISWOW64PROCESS fnIsWow64Process; + BOOL bIsWow64 = FALSE; + + fnIsWow64Process = ( LPFN_ISWOW64PROCESS ) GetProcAddress( GetModuleHandle( TEXT( "kernel32" ) ), "IsWow64Process" ); + + if ( fnIsWow64Process != NULL ) + { + BOOL ok; + + ok = fnIsWow64Process( GetCurrentProcess(), &bIsWow64 ); + + if ( !ok ) + { + bIsWow64 = FALSE; + } + } + + return bIsWow64; +#endif +} + + +// CPrinterSetupWizardSheet +CPrinterSetupWizardSheet * CPrinterSetupWizardSheet::m_self; + +IMPLEMENT_DYNAMIC(CPrinterSetupWizardSheet, CPropertySheet) +CPrinterSetupWizardSheet::CPrinterSetupWizardSheet(UINT nIDCaption, CWnd* pParentWnd, UINT iSelectPage) + :CPropertySheet(nIDCaption, pParentWnd, iSelectPage), + 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() +{ + Printer * printer; + + while ( m_printers.size() > 0 ) + { + printer = m_printers.front(); + m_printers.pop_front(); + + delete printer; + } + + m_self = NULL; +} + + +// ------------------------------------------------------ +// SetSelectedPrinter +// +// Manages setting a printer as the printer to install. Stops +// any pending resolves. +// +void +CPrinterSetupWizardSheet::SetSelectedPrinter(Printer * printer) +{ + check( !printer || ( printer != m_selectedPrinter ) ); + + m_selectedPrinter = printer; +} + + +OSStatus +CPrinterSetupWizardSheet::LoadPrinterNames() +{ + PBYTE buffer = NULL; + OSStatus err = 0; + + // + // 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; + + 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)) + { + try + { + buffer = new unsigned char[dwNeeded]; + } + catch (...) + { + 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 ); + + 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 ); + } + } + +exit: + + if (buffer != NULL) + { + delete [] buffer; + } + + return err; +} + + + +// ------------------------------------------------------ +// InstallPrinter +// +// Installs a printer with Windows. +// +// 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 +// to install the printer. Actually installing drivers that +// are not currently installed is painful, and it's much +// easier and less error prone to just let printui.dll do +// the hard work for us. +// + +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; + HANDLE hXcv = NULL; + Queue * q; + BOOL ok; + OSStatus err; + + 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_with_log( log, "OpenPrinter()", err, exit ); + + // + // BUGBUG: MSDN said this is not required, but my experience shows it is required + // + try + { + pOutputData = new BYTE[cbInputData]; + } + catch (...) + { + pOutputData = NULL; + } + + require_action_with_log( log, pOutputData, exit, err = kNoMemoryErr ); + + portData.dwPortNumber = service->portNumber; + portData.dwVersion = 1; + portData.dwDoubleSpool = 1; + + portData.dwProtocol = protocol; + portData.cbSize = sizeof PORT_DATA_1; + portData.dwReserved = 0L; + + require_action_with_log( log, wcslen(q->name) < sizeof_array(portData.sztQueue), exit, err = kSizeErr ); + wcscpy_s(portData.sztQueue, q->name); + + require_action_with_log( log, wcslen( service->hostname ) < sizeof_array(portData.sztHostAddress), exit, err = kSizeErr ); + wcscpy_s( portData.sztHostAddress, service->hostname ); + + ok = XcvData(hXcv, L"AddPort", (PBYTE) &portData, sizeof(PORT_DATA_1), pOutputData, cbInputData, &cbOutputNeeded, &dwStatus); + err = translate_errno( ok, errno_compat(), kUnknownErr ); + require_noerr_with_log( log, "XcvData()", err, exit ); + +exit: + + if (hXcv != NULL) + { + ClosePrinter(hXcv); + } + + if (pOutputData != NULL) + { + delete [] pOutputData; + } + + return err; +} + + +OSStatus +CPrinterSetupWizardSheet::InstallPrinterPDLAndLPR(Printer * printer, Service * service, Logger & log ) +{ + PRINTER_INFO_2 pInfo; + HANDLE hPrinter = NULL; + Queue * q; + OSStatus err; + + check(printer != NULL); + check(printer->installed == false); + + q = service->queues.front(); + check( q ); + + // + // add the printer + // + 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 + // + if (wParam == ID_WIZFINISH) // If OK is hit... + { + OnOK(); + } + + return CPropertySheet::OnCommand(wParam, lParam); +} + + +// ------------------------------------------------------ +// OnInitDialog +// +// Initializes this Dialog object. +// +BOOL CPrinterSetupWizardSheet::OnInitDialog() +{ + OSStatus err; + + CPropertySheet::OnInitDialog(); + + err = StartBrowse(); + require_noerr( err, exit ); + +exit: + + if ( err ) + { + 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 ); + + MessageBox(text, caption, MB_OK|MB_ICONEXCLAMATION); + + _exit( 0 ); + } + } + + return TRUE; +} + + +// ------------------------------------------------------ +// OnSetCursor +// +// This is called when Windows wants to know what cursor +// to display. So we tell it. +// +BOOL +CPrinterSetupWizardSheet::OnSetCursor(CWnd * pWnd, UINT nHitTest, UINT message) +{ + DEBUG_UNUSED(pWnd); + DEBUG_UNUSED(nHitTest); + DEBUG_UNUSED(message); + + SetCursor(m_active); + return TRUE; +} + + +// ------------------------------------------------------ +// OnContextMenu +// +// This is not fully implemented yet. +// + +void +CPrinterSetupWizardSheet::OnContextMenu(CWnd * pWnd, CPoint pos) +{ + DEBUG_UNUSED(pWnd); + DEBUG_UNUSED(pos); + + CAbout dlg; + + dlg.DoModal(); +} + + +// ------------------------------------------------------ +// OnOK +// +// This is called when the user hits the "Finish" button +// +void +CPrinterSetupWizardSheet::OnOK() +{ + CWnd * window; + OSStatus err; + + check ( m_selectedPrinter != NULL ); + + SetWizardButtons( PSWIZB_DISABLEDFINISH ); + + 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(); +} + + +// CPrinterSetupWizardSheet message handlers + +void CPrinterSetupWizardSheet::Init(void) +{ + AddPage(&m_pgSecond); + AddPage(&m_pgThird); + AddPage(&m_pgFourth); + + m_psh.dwFlags &= (~PSH_HASHELP); + + m_psh.dwFlags |= PSH_WIZARD97|PSH_WATERMARK|PSH_HEADER; + m_psh.pszbmWatermark = MAKEINTRESOURCE(IDB_WATERMARK); + m_psh.pszbmHeader = MAKEINTRESOURCE(IDB_BANNER_ICON); + + m_psh.hInstance = GetNonLocalizedResources(); + + SetWizardMode(); +} + + +LRESULT +CPrinterSetupWizardSheet::OnSocketEvent(WPARAM inWParam, LPARAM inLParam) +{ + if (WSAGETSELECTERROR(inLParam) && !(HIWORD(inLParam))) + { + dlog( kDebugLevelError, "OnServiceEvent: window error\n" ); + } + else + { + SOCKET sock = (SOCKET) inWParam; + + // iterate thru list + ServiceRefList::iterator begin = m_serviceRefList.begin(); + ServiceRefList::iterator end = m_serviceRefList.end(); + + while (begin != end) + { + DNSServiceRef ref = *begin++; + + check(ref != NULL); + + if ((SOCKET) DNSServiceRefSockFD(ref) == sock) + { + DNSServiceProcessResult(ref); + break; + } + } + } + + return ( 0 ); +} + + +LRESULT +CPrinterSetupWizardSheet::OnProcessEvent(WPARAM inWParam, LPARAM inLParam) +{ + DEBUG_UNUSED(inLParam); + + 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 ) +{ + DEBUG_UNUSED(inRef); + + CPrinterSetupWizardSheet * self; + 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 ); + + 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 ) + { + BOOL newPrinter = FALSE; + + if ( !printer ) + { + printer = self->OnAddPrinter( inInterfaceIndex, inName, inType, inDomain, moreComing ); + require_action( printer, exit, err = kUnknownErr ); + newPrinter = TRUE; + } + + // If we're looking at the browse list on page 2, then we need to call + // CPage2::OnAddPrinter() regardless of whether we've seen the printer + // or not because the moreComing flag might have changed from a previous + // call. If we only call CPage2::OnAddPrinter() when there's a new printer, + // we might not correctly update our UI, so if we've seen the printer before, + // call OnAddPrinter with a NULL parameter. + + if ( self->GetActivePage() == &self->m_pgSecond ) + { + self->m_pgSecond.OnAddPrinter( newPrinter ? printer : NULL, moreComing ); + } + + if ( !service ) + { + err = self->OnAddService( printer, inInterfaceIndex, inName, inType, inDomain ); + require_noerr( err, exit ); + } + else + { + service->refs++; + } + } + else if ( printer ) + { + check( service ); + + err = self->OnRemoveService( service ); + require_noerr( err, exit ); + + if ( printer->services.size() == 0 ) + { + err = self->OnRemovePrinter( printer, moreComing ); + require_noerr( err, exit ); + } + } + +exit: + + return; +} + + +void DNSSD_API +CPrinterSetupWizardSheet::OnResolve( + DNSServiceRef inRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + DNSServiceErrorType inErrorCode, + const char * inFullName, + const char * inHostName, + uint16_t inPort, + uint16_t inTXTSize, + const char * inTXT, + void * inContext ) +{ + DEBUG_UNUSED(inFullName); + DEBUG_UNUSED(inInterfaceIndex); + DEBUG_UNUSED(inFlags); + DEBUG_UNUSED(inRef); + + CPrinterSetupWizardSheet * self; + Service * service; + Queue * q; + int idx; + OSStatus err; + + require_noerr( inErrorCode, exit ); + + service = reinterpret_cast<Service*>( inContext ); + require_quiet( service, exit); + + check( service->refs != 0 ); + + self = service->printer->window; + require_quiet( self, exit ); + + err = self->StopOperation( service->serviceRef ); + require_noerr( err, exit ); + + // + // hold on to the hostname... + // + err = UTF8StringToStringObject( inHostName, service->hostname ); + require_noerr( err, exit ); + + // + // <rdar://problem/3739200> remove the trailing dot on hostname + // + idx = service->hostname.ReverseFind('.'); + + if ((idx > 1) && ((service->hostname.GetLength() - 1) == idx)) + { + service->hostname.Delete(idx, 1); + } + + // + // hold on to the port + // + service->portNumber = ntohs(inPort); + + if ( service->qtotal == 1 ) + { + // + // create a new queue + // + try + { + q = new Queue; + } + catch (...) + { + q = NULL; + } + + require_action( q, exit, err = E_OUTOFMEMORY ); + + // + // parse the text record. + // + + err = self->ParseTextRecord( service, q, inTXTSize, inTXT ); + require_noerr( err, exit ); + + service->queues.push_back( q ); + + // + // we've completely resolved this service + // + + 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; + } + + require_action( q, exit, err = E_OUTOFMEMORY ); + + err = service->printer->window->ParseTextRecord( service, q, inRDLen, inTXT ); + require_noerr( err, exit ); + + // + // add this queue + // + + service->queues.push_back( q ); + + if ( service->queues.size() == service->qtotal ) + { + // + // 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 ); + } + } + +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++ ) + { + if ( (*it).CompareNoCase( printer->actualName ) == 0 ) + { + break; + } + } + + if (it != m_printerNames.end()) + { + printer->actualName.Format(L"%s (%d)", printer->displayName, printerNameCount); + } + else + { + 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 ); + + // + // if the printer is selected, then we'll want to start a + // resolve on this guy + // + + if ( printer == m_selectedPrinter ) + { + StartResolve( service ); + } + +exit: + + return err; +} + + +OSStatus +CPrinterSetupWizardSheet::OnRemovePrinter( Printer * printer, bool moreComing ) +{ + CPropertyPage * active = GetActivePage(); + OSStatus err = kNoErr; + + if ( active == &m_pgSecond ) + { + m_pgSecond.OnRemovePrinter( printer, moreComing ); + } + + m_printers.remove( printer ); + + if ( m_selectedPrinter == printer ) + { + m_selectedPrinter = NULL; + + if ( ( active == &m_pgThird ) || ( active == &m_pgFourth ) ) + { + CString caption; + CString message; + + caption.LoadString( IDS_ERROR_CAPTION ); + message.LoadString( IDS_PRINTER_UNAVAILABLE ); + + MessageBox(message, caption, MB_OK|MB_ICONEXCLAMATION); + + SetActivePage( &m_pgSecond ); + } + } + + delete printer; + + return err; +} + + +OSStatus +CPrinterSetupWizardSheet::OnRemoveService( Service * service ) +{ + OSStatus err = kNoErr; + + if ( service && ( --service->refs == 0 ) ) + { + if ( service->serviceRef != 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 ); + + // Reset the cursor + + m_active = m_arrow; + + // 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 ); + +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: + + 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++ ) + { + if ( (*it)->serviceRef == NULL ) + { + err = StartResolve( *it ); + require_noerr( err, exit ); + } + } + + m_selectedPrinter = printer; + +exit: + + return err; +} + + +OSStatus +CPrinterSetupWizardSheet::StartResolve( Service * service ) +{ + OSStatus err = kNoErr; + + check( service->serviceRef == NULL ); + + // + // clean out any queues that were collected during a previous + // resolve + // + + service->EmptyQueues(); + + // + // 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 ); + + // + // If we're not currently resolving, then disable the next button + // and set the cursor to hourglass + // + + if ( !service->printer->resolving ) + { + SetWizardButtons( PSWIZB_BACK ); + + m_active = m_wait; + SetCursor(m_active); + } + + service->printer->resolving++; + +exit: + + return err; +} + + +OSStatus +CPrinterSetupWizardSheet::StopResolve(Printer * printer) +{ + OSStatus err = kNoErr; + + check( printer ); + + Services::iterator it; + + for ( it = printer->services.begin(); it != printer->services.end(); it++ ) + { + if ( (*it)->serviceRef ) + { + err = StopResolve( *it ); + require_noerr( err, exit ); + } + } + +exit: + + return err; +} + + +OSStatus +CPrinterSetupWizardSheet::StopResolve( Service * service ) +{ + OSStatus err; + + check( service->serviceRef ); + + err = StopOperation( service->serviceRef ); + require_noerr( err, exit ); + + service->printer->resolving--; + +exit: + + return err; +} + + +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 ) +{ + OSStatus err = kNoErr; + + if ( ref ) + { + m_serviceRefList.remove( ref ); + + if ( IsWindow( m_hWnd ) ) + { + err = WSAAsyncSelect((SOCKET) DNSServiceRefSockFD( ref ), m_hWnd, 0, 0 ); + require_noerr( err, exit ); + } + + DNSServiceRefDeallocate( ref ); + ref = NULL; + } + +exit: + + return err; +} + + +OSStatus +CPrinterSetupWizardSheet::ParseTextRecord( Service * service, Queue * q, uint16_t inTXTSize, const char * inTXT ) +{ + check( service ); + check( q ); + + // <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/3987680> Default to queue "lp" + + q->name = L"lp"; + + // <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 ); + } + + if ( ( val = TXTRecordGetValuePtr( inTXTSize, inTXT, "pdl", &len ) ) != NULL ) + { + // Stringize val ( doesn't have trailing '\0' yet ) + + memcpy( buf, val, len ); + buf[len] = '\0'; + + 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 ) + + memcpy( buf, val, len ); + buf[len] = '\0'; + + 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: + + // 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 + + 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; +} + + + |