X-Git-Url: https://git.saurik.com/apple/mdnsresponder.git/blobdiff_plain/7f0064bd55e3fa98568d2c359429ff8a38b23a6c..51e746cfedf68920647bd7ab197adb37f67b5209:/Clients/PrinterSetupWizard/PrinterSetupWizardSheet.cpp?ds=inline diff --git a/Clients/PrinterSetupWizard/PrinterSetupWizardSheet.cpp b/Clients/PrinterSetupWizard/PrinterSetupWizardSheet.cpp index 02b2952..e912716 100644 --- a/Clients/PrinterSetupWizard/PrinterSetupWizardSheet.cpp +++ b/Clients/PrinterSetupWizard/PrinterSetupWizardSheet.cpp @@ -1,28 +1,78 @@ -/* +/* -*- 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.35 2006/08/14 23:24:09 cheshire +Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 + +Revision 1.34 2005/10/05 17:32:51 herscher + Use a case insensitive compare operation to check whether a printer with the same name has already been installed. + +Revision 1.33 2005/07/11 20:17:15 shersche + UI fixes associated with CUPS printer workaround fix. + +Revision 1.32 2005/07/07 17:53:20 shersche +Fix problems associated with the CUPS printer workaround fix. + +Revision 1.31 2005/06/30 18:02:54 shersche + Workaround for Mac OS X Printer Sharing bug + +Revision 1.30 2005/04/13 17:46:22 shersche + Generic PCL not selected when printers advertise multiple text records + +Revision 1.29 2005/02/14 20:48:37 shersche + Default pdl key to "application/postscript" + +Revision 1.28 2005/02/14 20:37:53 shersche + Populate comment field with the model name that users see in the wizard UI. + +Revision 1.27 2005/02/09 05:04:03 shersche + Use TXTRecordGetValuePtr() API in ParseTextRecord + +Revision 1.26 2005/02/08 21:45:06 shersche + Default to Generic PostScript or PCL if unable to match driver + +Revision 1.25 2005/02/08 18:54:17 shersche + Default queue name is "lp" when rp key is not specified. + +Revision 1.24 2005/02/01 02:15:55 shersche + Use TXTRecord parsing APIs in ParseTextRecord + +Revision 1.23 2005/01/31 23:54:30 shersche + Start browsing when printer wizard starts. Move browsing logic from CSecondPage object to CPrinterSetupWizardSheet object. + +Revision 1.22 2005/01/25 18:49:43 shersche +Get icon resources from resource DLL + +Revision 1.21 2005/01/10 01:09:32 shersche +Use the "note" key to populate pLocation field when setting up printer + +Revision 1.20 2005/01/03 19:05:01 shersche +Store pointer to instance of wizard sheet so that print driver install thread sends a window message to the correct window + +Revision 1.19 2004/12/31 07:23:53 shersche +Don't modify the button setting in SetSelectedPrinter() + +Revision 1.18 2004/12/29 18:53:38 shersche + + Added support for LPR and IPP protocols as well as support for obtaining multiple text records. Reorganized and simplified codebase. +Bug #: 3725106, 3737413 + Revision 1.17 2004/10/12 18:02:53 shersche Escape '/', '@', '"' characters in printui command. Bug #: 3764873 @@ -110,158 +160,226 @@ First checked in // Private Messages -#define WM_SERVICE_EVENT ( WM_USER + 0x100 ) -#define WM_PROCESS_EVENT ( WM_USER + 0x101 ) - -// Service Types - -#define kPDLDataStreamServiceType "_pdl-datastream._tcp" -#define kLPRServiceType "_printer._tcp" -#define kIPPServiceType "_ipp._tcp" +#define WM_SOCKET_EVENT ( WM_USER + 0x100 ) +#define WM_PROCESS_EVENT ( WM_USER + 0x101 ) // 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; - } + m_self = NULL; } // ------------------------------------------------------ -// InstallEventHandler +// SetSelectedPrinter // -// Installs an event handler for DNSService events. +// Manages setting a printer as the printer to install. Stops +// any pending resolves. // -int -CPrinterSetupWizardSheet::InstallEventHandler(EventHandler * handler) +void +CPrinterSetupWizardSheet::SetSelectedPrinter(Printer * printer) { - PrinterList::iterator iter; - - m_eventHandlerList.push_back(handler); + 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; - iter = m_printerList.begin(); + BOOL ok = EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 4, NULL, 0, &dwNeeded, &dwNumPrinters); + err = translate_errno( ok, errno_compat(), kUnknownErr ); - while (iter != m_printerList.end()) + if ((err == ERROR_INSUFFICIENT_BUFFER) && (dwNeeded > 0)) { - Printer * printer = *iter++; + 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 ); - handler->OnAddPrinter(printer, iter != m_printerList.end()); + 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 ); + } } - - return kNoErr; -} +exit: -// ------------------------------------------------------ -// RemoveEventHandler -// -// Removes an event handler for DNSService events. -// -int -CPrinterSetupWizardSheet::RemoveEventHandler(EventHandler * handler) -{ - m_eventHandlerList.remove(handler); + if (buffer != NULL) + { + delete [] buffer; + } - return kNoErr; + return err; } // ------------------------------------------------------ -// SetSelectedPrinter +// InstallPrinter // -// Manages setting a printer as the printer to install. Stops -// any pending resolves. +// 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::SetSelectedPrinter(Printer * printer) +CPrinterSetupWizardSheet::InstallPrinter(Printer * printer) { - OSStatus err; + Service * service; + BOOL ok; + OSStatus err; + + service = printer->services.front(); + check( service ); // - // we only want one resolve going on at a time, so we check - // state of the m_selectedPrinter + // if the driver isn't installed, then install it // - if (m_selectedPrinter != NULL) + + 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( err, exit ); + // - // if we're currently resolving, then stop the resolve + // go modal // - if (m_selectedPrinter->serviceRef) + while (!m_driverThreadFinished) { - err = StopResolve(m_selectedPrinter); - require_noerr(err, exit); + 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 ); - delete m_selectedPrinter; - m_selectedPrinter = NULL; - } + // + // check the return value of thread + // + require_noerr( m_driverThreadExitCode, exit ); - check( m_selectedPrinter == NULL ); + // + // now we know that the driver was successfully installed + // + printer->driverInstalled = true; + } - try + if ( service->type == kPDLServiceType ) { - m_selectedPrinter = new Printer; + err = InstallPrinterPDLAndLPR( printer, service, PROTOCOL_RAWTCP_TYPE ); + require_noerr( err, exit ); } - catch (...) + else if ( service->type == kLPRServiceType ) { - m_selectedPrinter = NULL; + err = InstallPrinterPDLAndLPR( printer, service, PROTOCOL_LPR_TYPE ); + require_noerr( err, exit ); + } + else if ( service->type == kIPPServiceType ) + { + err = InstallPrinterIPP( printer, service ); + require_noerr( err, exit ); + } + else + { + err = kUnknownErr; + require_noerr( err, exit ); } - 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); + 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( err, exit ); + } exit: @@ -269,23 +387,8 @@ exit: } -// ------------------------------------------------------ -// 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) +CPrinterSetupWizardSheet::InstallPrinterPDLAndLPR(Printer * printer, Service * service, DWORD protocol ) { PRINTER_DEFAULTS printerDefaults = { NULL, NULL, SERVER_ACCESS_ADMINISTER }; DWORD dwStatus; @@ -293,18 +396,23 @@ CPrinterSetupWizardSheet::InstallPrinter(Printer * printer) PBYTE pOutputData = NULL; DWORD cbOutputNeeded = 0; PORT_DATA_1 portData; + PRINTER_INFO_2 pInfo; HANDLE hXcv = NULL; HANDLE hPrinter = NULL; + Queue * q; BOOL ok; OSStatus err; check(printer != NULL); check(printer->installed == false); + 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 ); - + // // BUGBUG: MSDN said this is not required, but my experience shows it is required // @@ -325,98 +433,49 @@ CPrinterSetupWizardSheet::InstallPrinter(Printer * printer) ZeroMemory(&portData, sizeof(PORT_DATA_1)); wcscpy(portData.sztPortName, printer->portName); - portData.dwPortNumber = printer->portNumber; + portData.dwPortNumber = service->portNumber; portData.dwVersion = 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); - + wcscpy(portData.sztQueue, q->name); + wcscpy(portData.sztIPAddress, service->hostname); + wcscpy(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( 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; // - // if the user specified a default printer, set it + // add the printer // - if (printer->deflt) - { - ok = SetDefaultPrinter(printer->actualName); - err = translate_errno( ok, errno_compat(), err = kUnknownErr ); - require_noerr( err, exit ); - } + 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( err, exit ); exit: @@ -439,10 +498,51 @@ exit: } +OSStatus +CPrinterSetupWizardSheet::InstallPrinterIPP(Printer * printer, Service * service) +{ + 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( err, exit ); + +exit: + + if ( hPrinter != NULL ) + { + ClosePrinter(hPrinter); + } + + return err; +} + + BEGIN_MESSAGE_MAP(CPrinterSetupWizardSheet, CPropertySheet) -ON_MESSAGE( WM_SERVICE_EVENT, OnServiceEvent ) +ON_MESSAGE( WM_SOCKET_EVENT, OnSocketEvent ) ON_MESSAGE( WM_PROCESS_EVENT, OnProcessEvent ) ON_WM_SETCURSOR() +ON_WM_TIMER() END_MESSAGE_MAP() @@ -468,39 +568,41 @@ BOOL CPrinterSetupWizardSheet::OnCommand(WPARAM wParam, LPARAM lParam) // ------------------------------------------------------ // 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); + CPropertySheet::OnInitDialog(); - 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(); - exc.text.LoadString(IDS_NO_MDNSRESPONDER_SERVICE_TEXT); - exc.caption.LoadString(IDS_NO_MDNSRESPONDER_SERVICE_CAPTION); + if ( err == kDNSServiceErr_Firewall ) + { + CString text, caption; + + text.LoadString( IDS_FIREWALL ); + caption.LoadString( IDS_FIREWALL_CAPTION ); - throw(exc); + MessageBox(text, caption, MB_OK|MB_ICONEXCLAMATION); + } + else + { + CPrinterSetupWizardSheet::WizardException exc; + + exc.text.LoadString( IDS_NO_MDNSRESPONDER_SERVICE_TEXT ); + exc.caption.LoadString( IDS_ERROR_CAPTION ); + + throw(exc); + } } return TRUE; @@ -565,6 +667,8 @@ CPrinterSetupWizardSheet::OnOK() MessageBox(message, caption, MB_OK|MB_ICONEXCLAMATION); } + + StopBrowse(); } @@ -583,14 +687,14 @@ void CPrinterSetupWizardSheet::Init(void) 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) +CPrinterSetupWizardSheet::OnSocketEvent(WPARAM inWParam, LPARAM inLParam) { if (WSAGETSELECTERROR(inLParam) && !(HIWORD(inLParam))) { @@ -625,10 +729,77 @@ CPrinterSetupWizardSheet::OnServiceEvent(WPARAM inWParam, LPARAM inLParam) LONG 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; } @@ -636,141 +807,77 @@ CPrinterSetupWizardSheet::OnProcessEvent(WPARAM inWParam, LPARAM inLParam) 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 ( inContext ); require_quiet( self, exit ); - printer = self->LookUp(inName); + active = self->GetActivePage(); + require_quiet( active, exit ); - if (inFlags & kDNSServiceFlagsAdd) + // Have we seen this printer before? + + printer = self->Lookup( inName ); + + if ( printer ) + { + service = printer->LookupService( inType ); + } + + if ( inFlags & kDNSServiceFlagsAdd ) { - OSStatus err; + if (printer == NULL) + { + // If not, then create a new one + + printer = self->OnAddPrinter( inInterfaceIndex, inName, inType, inDomain, moreComing ); + require_action( printer, exit, err = kUnknownErr ); + } - if (printer != NULL) + if ( !service ) { - printer->refs++; + err = self->OnAddService( printer, inInterfaceIndex, inName, inType, inDomain ); + require_noerr( err, exit ); } else { - 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; - - handler->OnAddPrinter(printer, moreComing); - } + 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; + check( service ); - handler->OnRemovePrinter(printer, moreComing); - } - - self->m_printerList.remove(printer); - - // - // 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; } @@ -793,103 +900,90 @@ CPrinterSetupWizardSheet::OnResolve( 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( inContext ); - require_quiet( printer, exit); + service = reinterpret_cast( inContext ); + require_quiet( service, exit); + + check( service->refs != 0 ); - self = printer->window; + 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 ); // // 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); - - // - // parse the text record. we create a stringlist of text record - // entries that can be interrogated later - // - while (inTXTSize) - { - char buf[256]; + service->portNumber = ntohs(inPort); - unsigned char num = *inTXT; - check( (int) num < inTXTSize ); + if ( service->qtotal == 1 ) + { + // + // create a new queue + // + try + { + q = new Queue; + } + catch (...) + { + q = NULL; + } - memset(buf, 0, sizeof(buf)); - memcpy(buf, inTXT + 1, num); - - inTXTSize -= (num + 1); - inTXT += (num + 1); + require_action( q, exit, err = E_OUTOFMEMORY ); - CString elem; + // + // parse the text record. + // - err = UTF8StringToStringObject( buf, elem ); + err = self->ParseTextRecord( service, q, inTXTSize, inTXT ); require_noerr( err, exit ); - int curPos = 0; + service->queues.push_back( q ); - CString key = elem.Tokenize(L"=", curPos); - CString val = elem.Tokenize(L"=", curPos); - - key.MakeLower(); + // + // we've completely resolved this service + // - if ((key == L"usb_mfg") || (key == L"usb_manufacturer")) - { - printer->usb_MFG = val; - } - else if ((key == L"usb_mdl") || (key == L"usb_model")) - { - printer->usb_MDL = val; - } - else if (key == L"description") - { - printer->description = val; - } - else if (key == L"product") - { - printer->product = val; - } + self->OnResolveService( service ); } - - // - // now invoke event handlers for Resolve event - // - it1 = self->m_eventHandlerList.begin(); - it2 = self->m_eventHandlerList.end(); - - while (it1 != it2) + else { - EventHandler * handler = *it1++; + // + // 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 ); - handler->OnResolvePrinter(printer); + err = self->StartOperation( service->serviceRef ); + require_noerr( err, exit ); } exit: @@ -898,188 +992,761 @@ exit: } -OSStatus -CPrinterSetupWizardSheet::LoadPrinterNames() +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) { - PBYTE buffer = NULL; - OSStatus err = 0; + 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; - // - // 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; + require_noerr( inErrorCode, exit ); - BOOL ok = EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 4, NULL, 0, &dwNeeded, &dwNumPrinters); - err = translate_errno( ok, errno_compat(), kUnknownErr ); + service = reinterpret_cast( inContext ); + require_quiet( service, exit); - if ((err == ERROR_INSUFFICIENT_BUFFER) && (dwNeeded > 0)) + 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 { - buffer = new unsigned char[dwNeeded]; + q = new Queue; } catch (...) { - buffer = NULL; + q = 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_action( q, exit, err = E_OUTOFMEMORY ); + + err = service->printer->window->ParseTextRecord( service, q, inRDLen, inTXT ); require_noerr( err, exit ); - for (DWORD index = 0; index < dwNumPrinters; index++) + // + // add this queue + // + + service->queues.push_back( q ); + + if ( service->queues.size() == service->qtotal ) { - PRINTER_INFO_4 * lppi4 = (PRINTER_INFO_4*) (buffer + index * sizeof(PRINTER_INFO_4)); + // + // else if moreComing is not set, then we're going + // to assume that we're done + // + + self->StopOperation( service->serviceRef ); + + // + // sort the queues + // - m_printerNames[lppi4->pPrinterName] = lppi4->pPrinterName; + service->queues.sort( OrderQueueFunc ); + + // + // we've completely resolved this service + // + + self->OnResolveService( service ); } } exit: - if (buffer != NULL) + if ( err && service && ( service->serviceRef != NULL ) ) { - delete [] buffer; + service->printer->window->StopOperation( service->serviceRef ); } - return err; + return; } -OSStatus -CPrinterSetupWizardSheet::StartResolve(Printer * printer) +Printer* +CPrinterSetupWizardSheet::OnAddPrinter( + uint32_t inInterfaceIndex, + const char * inName, + const char * inType, + const char * inDomain, + bool moreComing) { - OSStatus err; + Printer * printer = NULL; + DWORD printerNameCount; + OSStatus err; - check( printer ); + DEBUG_UNUSED( inInterfaceIndex ); + DEBUG_UNUSED( inType ); + DEBUG_UNUSED( inDomain ); - 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); + try + { + printer = new Printer; + } + catch (...) + { + printer = NULL; + } - m_serviceRefList.push_back(printer->serviceRef); + require_action( printer, exit, err = E_OUTOFMEMORY ); - err = WSAAsyncSelect((SOCKET) DNSServiceRefSockFD(printer->serviceRef), m_hWnd, WM_SERVICE_EVENT, FD_READ|FD_CLOSE); - require_noerr( err, exit ); + 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; - // - // set the cursor to arrow+hourglass - // - m_active = m_wait; - SetCursor(m_active); + // Compare this name against printers that are already installed + // to avoid name clashes. Rename as necessary + // to come up with a unique name. -exit: + printerNameCount = 2; - return err; -} + for (;;) + { + CPrinterSetupWizardSheet::PrinterNames::iterator it; + // Don't use find to do comparisons because we need to + // do a case insensitive string comparison -OSStatus -CPrinterSetupWizardSheet::StopResolve(Printer * printer) -{ - OSStatus err; + for ( it = m_printerNames.begin(); it != m_printerNames.end(); it++ ) + { + if ( (*it).CompareNoCase( printer->actualName ) == 0 ) + { + break; + } + } - check( printer ); - check( printer->serviceRef ); + if (it != m_printerNames.end()) + { + printer->actualName.Format(L"%s (%d)", printer->displayName, printerNameCount); + } + else + { + break; + } - m_serviceRefList.remove( printer->serviceRef ); + printerNameCount++; + } - err = WSAAsyncSelect((SOCKET) DNSServiceRefSockFD(printer->serviceRef), m_hWnd, 0, 0); - check(err == 0); + m_printers.push_back( printer ); - DNSServiceRefDeallocate( printer->serviceRef ); + if ( GetActivePage() == &m_pgSecond ) + { + m_pgSecond.OnAddPrinter( printer, moreComing ); + } - printer->serviceRef = NULL; - - // - // set the cursor back to normal - // - m_active = m_arrow; - SetCursor(m_active); +exit: - return kNoErr; + return printer; } -Printer* -CPrinterSetupWizardSheet::LookUp(const char * inName) +OSStatus +CPrinterSetupWizardSheet::OnAddService( + Printer * printer, + uint32_t inInterfaceIndex, + const char * inName, + const char * inType, + const char * inDomain) { - PrinterList::iterator it1 = m_printerList.begin(); - PrinterList::iterator it2 = m_printerList.end(); + Service * service = NULL; + OSStatus err = kNoErr; - while (it1 != it2) - { - Printer * printer = *it1++; + DEBUG_UNUSED( inName ); + DEBUG_UNUSED( inDomain ); - if (printer->name == inName) - { - return printer; - } + try + { + service = new Service; + } + catch (...) + { + service = NULL; } - return 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 ); -unsigned WINAPI -CPrinterSetupWizardSheet::InstallPrinterThread( LPVOID inParam ) -{ - check( inParam ); + // + // 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 - Printer * printer = (Printer*) inParam; - CString actualName; - CString command; - DWORD exitCode = 0; - DWORD dwResult; - OSStatus err; - STARTUPINFO si; - PROCESS_INFORMATION pi; - BOOL ok; + m_active = m_arrow; + + // And tell page 2 about it + + m_pgSecond.OnResolveService( service ); + } + +exit: + + return; +} + - ZeroMemory( &si, sizeof(si) ); - si.cb = sizeof(si); - ZeroMemory( &pi, sizeof(pi) ); +OSStatus +CPrinterSetupWizardSheet::StartBrowse() +{ + OSStatus err; // - // Escape '\', '@', '"' characters which seem to cause problems for printui + // setup the DNS-SD browsing // + err = DNSServiceBrowse( &m_pdlBrowser, 0, 0, kPDLServiceType, NULL, OnBrowse, this ); + require_noerr( err, exit ); - actualName = printer->actualName; + err = StartOperation( m_pdlBrowser ); + require_noerr( err, exit ); - actualName.Replace(L"\\", L"\\\\"); - actualName.Replace(L"@", L"\\@"); - actualName.Replace(L"\"", L"\\\""); - - 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); + err = DNSServiceBrowse( &m_lprBrowser, 0, 0, kLPRServiceType, NULL, OnBrowse, this ); + require_noerr( err, exit ); - ok = CreateProcess(NULL, command.GetBuffer(), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); - err = translate_errno( ok, errno_compat(), kUnknownErr ); + err = StartOperation( m_lprBrowser ); require_noerr( err, exit ); - dwResult = WaitForSingleObject( pi.hProcess, INFINITE ); - translate_errno( dwResult == WAIT_OBJECT_0, errno_compat(), err = kUnknownErr ); + err = DNSServiceBrowse( &m_ippBrowser, 0, 0, kIPPServiceType, NULL, OnBrowse, this ); require_noerr( err, exit ); - ok = GetExitCodeProcess( pi.hProcess, &exitCode ); - err = translate_errno( ok, errno_compat(), kUnknownErr ); + 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 ); + // - // Close process and thread handles. + // clean out any queues that were collected during a previous + // resolve // - CloseHandle( pi.hProcess ); - CloseHandle( pi.hThread ); -exit: + service->EmptyQueues(); // - // alert the main thread + // now start the new resolve // - printer->window->PostMessage( WM_PROCESS_EVENT, err, exitCode ); - return 0; + 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 ); + + // Use TXTRecord APIs declared in dns_sd.h + + bool qtotalDefined = false; + const void * val; + char buf[256]; + uint8_t len; + OSStatus err = kNoErr; + + // Default to queue "lp" + + q->name = L"lp"; + + // 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 ); + } + + // Was this printer discovered via OS X Printer Sharing? + + if ( TXTRecordContainsKey( inTXTSize, inTXT, "printer-state" ) || TXTRecordContainsKey( inTXTSize, inTXT, "printer-type" ) ) + { + service->printer->isSharedFromOSX = 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; +} + + +