2 * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
23 Change History (most recent first):
25 $Log: PrinterSetupWizardSheet.cpp,v $
26 Revision 1.34 2005/10/05 17:32:51 herscher
27 <rdar://problem/4141221> Use a case insensitive compare operation to check whether a printer with the same name has already been installed.
29 Revision 1.33 2005/07/11 20:17:15 shersche
30 <rdar://problem/4124524> UI fixes associated with CUPS printer workaround fix.
32 Revision 1.32 2005/07/07 17:53:20 shersche
33 Fix problems associated with the CUPS printer workaround fix.
35 Revision 1.31 2005/06/30 18:02:54 shersche
36 <rdar://problem/4124524> Workaround for Mac OS X Printer Sharing bug
38 Revision 1.30 2005/04/13 17:46:22 shersche
39 <rdar://problem/4082122> Generic PCL not selected when printers advertise multiple text records
41 Revision 1.29 2005/02/14 20:48:37 shersche
42 <rdar://problem/4003710> Default pdl key to "application/postscript"
44 Revision 1.28 2005/02/14 20:37:53 shersche
45 <rdar://problem/4003944> Populate comment field with the model name that users see in the wizard UI.
47 Revision 1.27 2005/02/09 05:04:03 shersche
48 <rdar://problem/3946587> Use TXTRecordGetValuePtr() API in ParseTextRecord
50 Revision 1.26 2005/02/08 21:45:06 shersche
51 <rdar://problem/3947490> Default to Generic PostScript or PCL if unable to match driver
53 Revision 1.25 2005/02/08 18:54:17 shersche
54 <rdar://problem/3987680> Default queue name is "lp" when rp key is not specified.
56 Revision 1.24 2005/02/01 02:15:55 shersche
57 <rdar://problem/3946587> Use TXTRecord parsing APIs in ParseTextRecord
59 Revision 1.23 2005/01/31 23:54:30 shersche
60 <rdar://problem/3947508> Start browsing when printer wizard starts. Move browsing logic from CSecondPage object to CPrinterSetupWizardSheet object.
62 Revision 1.22 2005/01/25 18:49:43 shersche
63 Get icon resources from resource DLL
65 Revision 1.21 2005/01/10 01:09:32 shersche
66 Use the "note" key to populate pLocation field when setting up printer
68 Revision 1.20 2005/01/03 19:05:01 shersche
69 Store pointer to instance of wizard sheet so that print driver install thread sends a window message to the correct window
71 Revision 1.19 2004/12/31 07:23:53 shersche
72 Don't modify the button setting in SetSelectedPrinter()
74 Revision 1.18 2004/12/29 18:53:38 shersche
75 <rdar://problem/3725106>
76 <rdar://problem/3737413> Added support for LPR and IPP protocols as well as support for obtaining multiple text records. Reorganized and simplified codebase.
77 Bug #: 3725106, 3737413
79 Revision 1.17 2004/10/12 18:02:53 shersche
80 <rdar://problem/3764873> Escape '/', '@', '"' characters in printui command.
83 Revision 1.16 2004/09/13 21:27:22 shersche
84 <rdar://problem/3796483> Pass the moreComing flag to OnAddPrinter and OnRemovePrinter callbacks
87 Revision 1.15 2004/09/11 05:59:06 shersche
88 <rdar://problem/3785766> Fix code that generates unique printer names based on currently installed printers
91 Revision 1.14 2004/09/02 01:57:58 cheshire
92 <rdar://problem/3783611> Fix incorrect testing of MoreComing flag
94 Revision 1.13 2004/07/26 21:06:29 shersche
95 <rdar://problem/3739200> Removing trailing '.' in hostname
98 Revision 1.12 2004/07/13 21:24:23 rpantos
99 Fix for <rdar://problem/3701120>.
101 Revision 1.11 2004/06/28 00:51:47 shersche
102 Move call to EnumPrinters out of browse callback into standalone function
104 Revision 1.10 2004/06/27 23:06:47 shersche
105 code cleanup, make sure EnumPrinters returns non-zero value
107 Revision 1.9 2004/06/27 15:49:31 shersche
108 clean up some cruft in the printer browsing code
110 Revision 1.8 2004/06/27 08:04:51 shersche
111 copy selected printer to prevent printer being deleted out from under
113 Revision 1.7 2004/06/26 23:27:12 shersche
114 support for installing multiple printers of the same name
116 Revision 1.6 2004/06/26 21:22:39 shersche
117 handle spaces in file names
119 Revision 1.5 2004/06/26 03:19:57 shersche
120 clean up warning messages
122 Submitted by: herscher
124 Revision 1.4 2004/06/25 02:26:52 shersche
125 Normalize key fields in text record entries
126 Submitted by: herscher
128 Revision 1.3 2004/06/24 20:12:07 shersche
130 Submitted by: herscher
132 Revision 1.2 2004/06/23 17:58:21 shersche
133 <rdar://problem/3701837> eliminated memory leaks on exit
134 <rdar://problem/3701926> installation of a printer that is already installed results in a no-op
135 Bug #: 3701837, 3701926
136 Submitted by: herscher
138 Revision 1.1 2004/06/18 04:36:57 rpantos
145 #include "PrinterSetupWizardApp.h"
146 #include "PrinterSetupWizardSheet.h"
147 #include "CommonServices.h"
148 #include "DebugServices.h"
149 #include "WinServices.h"
151 #include <winspool.h>
156 #pragma warning(disable:4702)
159 #if( !TARGET_OS_WINDOWS_CE )
160 # include <mswsock.h>
161 # include <process.h>
166 #define WM_SOCKET_EVENT ( WM_USER + 0x100 )
167 #define WM_PROCESS_EVENT ( WM_USER + 0x101 )
170 // CPrinterSetupWizardSheet
171 CPrinterSetupWizardSheet
* CPrinterSetupWizardSheet::m_self
;
173 IMPLEMENT_DYNAMIC(CPrinterSetupWizardSheet
, CPropertySheet
)
174 CPrinterSetupWizardSheet::CPrinterSetupWizardSheet(UINT nIDCaption
, CWnd
* pParentWnd
, UINT iSelectPage
)
175 :CPropertySheet(nIDCaption
, pParentWnd
, iSelectPage
),
176 m_selectedPrinter(NULL
),
177 m_driverThreadExitCode( 0 ),
178 m_driverThreadFinished( false ),
179 m_pdlBrowser( NULL
),
180 m_ippBrowser( NULL
),
181 m_lprBrowser( NULL
),
184 m_arrow
= LoadCursor(0, IDC_ARROW
);
185 m_wait
= LoadCursor(0, IDC_APPSTARTING
);
195 CPrinterSetupWizardSheet::~CPrinterSetupWizardSheet()
199 while ( m_printers
.size() > 0 )
201 printer
= m_printers
.front();
202 m_printers
.pop_front();
211 // ------------------------------------------------------
212 // SetSelectedPrinter
214 // Manages setting a printer as the printer to install. Stops
215 // any pending resolves.
218 CPrinterSetupWizardSheet::SetSelectedPrinter(Printer
* printer
)
220 check( !printer
|| ( printer
!= m_selectedPrinter
) );
222 m_selectedPrinter
= printer
;
227 CPrinterSetupWizardSheet::LoadPrinterNames()
233 // rdar://problem/3701926 - Printer can't be installed twice
235 // First thing we want to do is make sure the printer isn't already installed.
236 // If the printer name is found, we'll try and rename it until we
237 // find a unique name
239 DWORD dwNeeded
= 0, dwNumPrinters
= 0;
241 BOOL ok
= EnumPrinters(PRINTER_ENUM_LOCAL
, NULL
, 4, NULL
, 0, &dwNeeded
, &dwNumPrinters
);
242 err
= translate_errno( ok
, errno_compat(), kUnknownErr
);
244 if ((err
== ERROR_INSUFFICIENT_BUFFER
) && (dwNeeded
> 0))
248 buffer
= new unsigned char[dwNeeded
];
255 require_action( buffer
, exit
, kNoMemoryErr
);
256 ok
= EnumPrinters(PRINTER_ENUM_LOCAL
, NULL
, 4, buffer
, dwNeeded
, &dwNeeded
, &dwNumPrinters
);
257 err
= translate_errno( ok
, errno_compat(), kUnknownErr
);
258 require_noerr( err
, exit
);
260 for (DWORD index
= 0; index
< dwNumPrinters
; index
++)
262 PRINTER_INFO_4
* lppi4
= (PRINTER_INFO_4
*) (buffer
+ index
* sizeof(PRINTER_INFO_4
));
264 m_printerNames
.push_back( lppi4
->pPrinterName
);
280 // ------------------------------------------------------
283 // Installs a printer with Windows.
285 // NOTE: this works one of two ways, depending on whether
286 // there are drivers already installed for this printer.
287 // If there are, then we can just create a port with XcvData,
288 // and then call AddPrinter. If not, we use the printui.dll
289 // to install the printer. Actually installing drivers that
290 // are not currently installed is painful, and it's much
291 // easier and less error prone to just let printui.dll do
292 // the hard work for us.
296 CPrinterSetupWizardSheet::InstallPrinter(Printer
* printer
)
302 service
= printer
->services
.front();
306 // if the driver isn't installed, then install it
309 if ( !printer
->driverInstalled
)
315 m_driverThreadFinished
= false;
320 hThread
= (HANDLE
) _beginthreadex_compat( NULL
, 0, InstallDriverThread
, printer
, 0, &threadID
);
321 err
= translate_errno( hThread
, (OSStatus
) GetLastError(), kUnknownErr
);
322 require_noerr( err
, exit
);
327 while (!m_driverThreadFinished
)
331 GetMessage( &msg
, m_hWnd
, 0, 0 );
332 TranslateMessage(&msg
);
333 DispatchMessage(&msg
);
337 // Wait until child process exits.
339 dwResult
= WaitForSingleObject( hThread
, INFINITE
);
340 err
= translate_errno( dwResult
== WAIT_OBJECT_0
, errno_compat(), err
= kUnknownErr
);
341 require_noerr( err
, exit
);
344 // check the return value of thread
346 require_noerr( m_driverThreadExitCode
, exit
);
349 // now we know that the driver was successfully installed
351 printer
->driverInstalled
= true;
354 if ( service
->type
== kPDLServiceType
)
356 err
= InstallPrinterPDLAndLPR( printer
, service
, PROTOCOL_RAWTCP_TYPE
);
357 require_noerr( err
, exit
);
359 else if ( service
->type
== kLPRServiceType
)
361 err
= InstallPrinterPDLAndLPR( printer
, service
, PROTOCOL_LPR_TYPE
);
362 require_noerr( err
, exit
);
364 else if ( service
->type
== kIPPServiceType
)
366 err
= InstallPrinterIPP( printer
, service
);
367 require_noerr( err
, exit
);
372 require_noerr( err
, exit
);
375 printer
->installed
= true;
378 // if the user specified a default printer, set it
382 ok
= SetDefaultPrinter( printer
->actualName
);
383 err
= translate_errno( ok
, errno_compat(), err
= kUnknownErr
);
384 require_noerr( err
, exit
);
394 CPrinterSetupWizardSheet::InstallPrinterPDLAndLPR(Printer
* printer
, Service
* service
, DWORD protocol
)
396 PRINTER_DEFAULTS printerDefaults
= { NULL
, NULL
, SERVER_ACCESS_ADMINISTER
};
398 DWORD cbInputData
= 100;
399 PBYTE pOutputData
= NULL
;
400 DWORD cbOutputNeeded
= 0;
401 PORT_DATA_1 portData
;
402 PRINTER_INFO_2 pInfo
;
404 HANDLE hPrinter
= NULL
;
409 check(printer
!= NULL
);
410 check(printer
->installed
== false);
412 q
= service
->queues
.front();
415 ok
= OpenPrinter(L
",XcvMonitor Standard TCP/IP Port", &hXcv
, &printerDefaults
);
416 err
= translate_errno( ok
, errno_compat(), kUnknownErr
);
417 require_noerr( err
, exit
);
420 // BUGBUG: MSDN said this is not required, but my experience shows it is required
424 pOutputData
= new BYTE
[cbInputData
];
431 require_action( pOutputData
, exit
, err
= kNoMemoryErr
);
436 ZeroMemory(&portData
, sizeof(PORT_DATA_1
));
437 wcscpy(portData
.sztPortName
, printer
->portName
);
439 portData
.dwPortNumber
= service
->portNumber
;
440 portData
.dwVersion
= 1;
442 portData
.dwProtocol
= protocol
;
443 portData
.cbSize
= sizeof PORT_DATA_1
;
444 portData
.dwReserved
= 0L;
446 wcscpy(portData
.sztQueue
, q
->name
);
447 wcscpy(portData
.sztIPAddress
, service
->hostname
);
448 wcscpy(portData
.sztHostAddress
, service
->hostname
);
450 ok
= XcvData(hXcv
, L
"AddPort", (PBYTE
) &portData
, sizeof(PORT_DATA_1
), pOutputData
, cbInputData
, &cbOutputNeeded
, &dwStatus
);
451 err
= translate_errno( ok
, errno_compat(), kUnknownErr
);
452 require_noerr( err
, exit
);
457 ZeroMemory(&pInfo
, sizeof(pInfo
));
459 pInfo
.pPrinterName
= printer
->actualName
.GetBuffer();
460 pInfo
.pServerName
= NULL
;
461 pInfo
.pShareName
= NULL
;
462 pInfo
.pPortName
= printer
->portName
.GetBuffer();
463 pInfo
.pDriverName
= printer
->modelName
.GetBuffer();
464 pInfo
.pComment
= printer
->displayModelName
.GetBuffer();
465 pInfo
.pLocation
= q
->location
.GetBuffer();
466 pInfo
.pDevMode
= NULL
;
467 pInfo
.pDevMode
= NULL
;
468 pInfo
.pSepFile
= L
"";
469 pInfo
.pPrintProcessor
= L
"winprint";
470 pInfo
.pDatatype
= L
"RAW";
471 pInfo
.pParameters
= L
"";
472 pInfo
.pSecurityDescriptor
= NULL
;
473 pInfo
.Attributes
= PRINTER_ATTRIBUTE_QUEUED
;
475 pInfo
.DefaultPriority
= 0;
479 hPrinter
= AddPrinter(NULL
, 2, (LPBYTE
) &pInfo
);
480 err
= translate_errno( hPrinter
, errno_compat(), kUnknownErr
);
481 require_noerr( err
, exit
);
485 if (hPrinter
!= NULL
)
487 ClosePrinter(hPrinter
);
495 if (pOutputData
!= NULL
)
497 delete [] pOutputData
;
505 CPrinterSetupWizardSheet::InstallPrinterIPP(Printer
* printer
, Service
* service
)
507 DEBUG_UNUSED( service
);
509 Queue
* q
= service
->SelectedQueue();
510 HANDLE hPrinter
= NULL
;
511 PRINTER_INFO_2 pInfo
;
519 ZeroMemory(&pInfo
, sizeof(PRINTER_INFO_2
));
521 pInfo
.pPrinterName
= printer
->actualName
.GetBuffer();
522 pInfo
.pPortName
= printer
->portName
.GetBuffer();
523 pInfo
.pDriverName
= printer
->modelName
.GetBuffer();
524 pInfo
.pPrintProcessor
= L
"winprint";
525 pInfo
.pLocation
= q
->location
.GetBuffer();
526 pInfo
.pComment
= printer
->displayModelName
.GetBuffer();
527 pInfo
.Attributes
= PRINTER_ATTRIBUTE_NETWORK
| PRINTER_ATTRIBUTE_LOCAL
;
529 hPrinter
= AddPrinter(NULL
, 2, (LPBYTE
)&pInfo
);
530 err
= translate_errno( hPrinter
, errno_compat(), kUnknownErr
);
531 require_noerr( err
, exit
);
535 if ( hPrinter
!= NULL
)
537 ClosePrinter(hPrinter
);
544 BEGIN_MESSAGE_MAP(CPrinterSetupWizardSheet
, CPropertySheet
)
545 ON_MESSAGE( WM_SOCKET_EVENT
, OnSocketEvent
)
546 ON_MESSAGE( WM_PROCESS_EVENT
, OnProcessEvent
)
552 // ------------------------------------------------------
555 // Traps when the user hits Finish
557 BOOL
CPrinterSetupWizardSheet::OnCommand(WPARAM wParam
, LPARAM lParam
)
560 // Check if this is OK
562 if (wParam
== ID_WIZFINISH
) // If OK is hit...
567 return CPropertySheet::OnCommand(wParam
, lParam
);
571 // ------------------------------------------------------
574 // Initializes this Dialog object.
576 BOOL
CPrinterSetupWizardSheet::OnInitDialog()
580 CPropertySheet::OnInitDialog();
583 require_noerr( err
, exit
);
591 if ( err
== kDNSServiceErr_Firewall
)
593 CString text
, caption
;
595 text
.LoadString( IDS_FIREWALL
);
596 caption
.LoadString( IDS_FIREWALL_CAPTION
);
598 MessageBox(text
, caption
, MB_OK
|MB_ICONEXCLAMATION
);
602 CPrinterSetupWizardSheet::WizardException exc
;
604 exc
.text
.LoadString( IDS_NO_MDNSRESPONDER_SERVICE_TEXT
);
605 exc
.caption
.LoadString( IDS_ERROR_CAPTION
);
615 // ------------------------------------------------------
618 // This is called when Windows wants to know what cursor
619 // to display. So we tell it.
622 CPrinterSetupWizardSheet::OnSetCursor(CWnd
* pWnd
, UINT nHitTest
, UINT message
)
625 DEBUG_UNUSED(nHitTest
);
626 DEBUG_UNUSED(message
);
633 // ------------------------------------------------------
636 // This is not fully implemented yet.
640 CPrinterSetupWizardSheet::OnContextMenu(CWnd
* pWnd
, CPoint pos
)
651 // ------------------------------------------------------
654 // This is called when the user hits the "Finish" button
657 CPrinterSetupWizardSheet::OnOK()
659 check ( m_selectedPrinter
!= NULL
);
661 SetWizardButtons( PSWIZB_DISABLEDFINISH
);
663 if ( InstallPrinter( m_selectedPrinter
) != kNoErr
)
668 caption
.LoadString(IDS_INSTALL_ERROR_CAPTION
);
669 message
.LoadString(IDS_INSTALL_ERROR_MESSAGE
);
671 MessageBox(message
, caption
, MB_OK
|MB_ICONEXCLAMATION
);
678 // CPrinterSetupWizardSheet message handlers
680 void CPrinterSetupWizardSheet::Init(void)
683 AddPage(&m_pgSecond
);
685 AddPage(&m_pgFourth
);
687 m_psh
.dwFlags
&= (~PSH_HASHELP
);
689 m_psh
.dwFlags
|= PSH_WIZARD97
|PSH_WATERMARK
|PSH_HEADER
;
690 m_psh
.pszbmWatermark
= MAKEINTRESOURCE(IDB_WATERMARK
);
691 m_psh
.pszbmHeader
= MAKEINTRESOURCE(IDB_BANNER_ICON
);
693 m_psh
.hInstance
= GetNonLocalizedResources();
700 CPrinterSetupWizardSheet::OnSocketEvent(WPARAM inWParam
, LPARAM inLParam
)
702 if (WSAGETSELECTERROR(inLParam
) && !(HIWORD(inLParam
)))
704 dlog( kDebugLevelError
, "OnServiceEvent: window error\n" );
708 SOCKET sock
= (SOCKET
) inWParam
;
711 ServiceRefList::iterator begin
= m_serviceRefList
.begin();
712 ServiceRefList::iterator end
= m_serviceRefList
.end();
716 DNSServiceRef ref
= *begin
++;
720 if ((SOCKET
) DNSServiceRefSockFD(ref
) == sock
)
722 DNSServiceProcessResult(ref
);
733 CPrinterSetupWizardSheet::OnProcessEvent(WPARAM inWParam
, LPARAM inLParam
)
735 DEBUG_UNUSED(inLParam
);
737 m_driverThreadExitCode
= (DWORD
) inWParam
;
738 m_driverThreadFinished
= true;
745 CPrinterSetupWizardSheet::InstallDriverThread( LPVOID inParam
)
747 Printer
* printer
= (Printer
*) inParam
;
752 PROCESS_INFORMATION pi
;
759 // because we're calling endthreadex(), C++ objects won't be cleaned up
760 // correctly. we'll nest the CString 'command' inside a block so
761 // that it's destructor will be invoked.
766 ZeroMemory( &si
, sizeof(si
) );
768 ZeroMemory( &pi
, sizeof(pi
) );
770 command
.Format(L
"rundll32.exe printui.dll,PrintUIEntry /ia /m \"%s\" /f \"%s\"", (LPCTSTR
) printer
->modelName
, (LPCTSTR
) printer
->infFileName
);
772 ok
= CreateProcess(NULL
, command
.GetBuffer(), NULL
, NULL
, FALSE
, 0, NULL
, NULL
, &si
, &pi
);
773 err
= translate_errno( ok
, errno_compat(), kUnknownErr
);
774 require_noerr( err
, exit
);
776 dwResult
= WaitForSingleObject( pi
.hProcess
, INFINITE
);
777 translate_errno( dwResult
== WAIT_OBJECT_0
, errno_compat(), err
= kUnknownErr
);
778 require_noerr( err
, exit
);
780 ok
= GetExitCodeProcess( pi
.hProcess
, &exitCode
);
781 err
= translate_errno( ok
, errno_compat(), kUnknownErr
);
782 require_noerr( err
, exit
);
788 // Close process and thread handles.
792 CloseHandle( pi
.hProcess
);
797 CloseHandle( pi
.hThread
);
801 // alert the main thread
803 m_self
->PostMessage( WM_PROCESS_EVENT
, err
, exitCode
);
805 _endthreadex_compat( 0 );
812 CPrinterSetupWizardSheet::OnBrowse(
814 DNSServiceFlags inFlags
,
815 uint32_t inInterfaceIndex
,
816 DNSServiceErrorType inErrorCode
,
819 const char * inDomain
,
824 CPrinterSetupWizardSheet
* self
;
825 bool moreComing
= (bool) (inFlags
& kDNSServiceFlagsMoreComing
);
826 CPropertyPage
* active
;
827 Printer
* printer
= NULL
;
828 Service
* service
= NULL
;
829 OSStatus err
= kNoErr
;
831 require_noerr( inErrorCode
, exit
);
833 self
= reinterpret_cast <CPrinterSetupWizardSheet
*>( inContext
);
834 require_quiet( self
, exit
);
836 active
= self
->GetActivePage();
837 require_quiet( active
, exit
);
839 // Have we seen this printer before?
841 printer
= self
->Lookup( inName
);
845 service
= printer
->LookupService( inType
);
848 if ( inFlags
& kDNSServiceFlagsAdd
)
852 // If not, then create a new one
854 printer
= self
->OnAddPrinter( inInterfaceIndex
, inName
, inType
, inDomain
, moreComing
);
855 require_action( printer
, exit
, err
= kUnknownErr
);
860 err
= self
->OnAddService( printer
, inInterfaceIndex
, inName
, inType
, inDomain
);
861 require_noerr( err
, exit
);
872 err
= self
->OnRemoveService( service
);
873 require_noerr( err
, exit
);
875 if ( printer
->services
.size() == 0 )
877 err
= self
->OnRemovePrinter( printer
, moreComing
);
878 require_noerr( err
, exit
);
889 CPrinterSetupWizardSheet::OnResolve(
891 DNSServiceFlags inFlags
,
892 uint32_t inInterfaceIndex
,
893 DNSServiceErrorType inErrorCode
,
894 const char * inFullName
,
895 const char * inHostName
,
901 DEBUG_UNUSED(inFullName
);
902 DEBUG_UNUSED(inInterfaceIndex
);
903 DEBUG_UNUSED(inFlags
);
906 CPrinterSetupWizardSheet
* self
;
912 require_noerr( inErrorCode
, exit
);
914 service
= reinterpret_cast<Service
*>( inContext
);
915 require_quiet( service
, exit
);
917 check( service
->refs
!= 0 );
919 self
= service
->printer
->window
;
920 require_quiet( self
, exit
);
922 err
= self
->StopOperation( service
->serviceRef
);
923 require_noerr( err
, exit
);
926 // hold on to the hostname...
928 err
= UTF8StringToStringObject( inHostName
, service
->hostname
);
929 require_noerr( err
, exit
);
932 // <rdar://problem/3739200> remove the trailing dot on hostname
934 idx
= service
->hostname
.ReverseFind('.');
936 if ((idx
> 1) && ((service
->hostname
.GetLength() - 1) == idx
))
938 service
->hostname
.Delete(idx
, 1);
942 // hold on to the port
944 service
->portNumber
= ntohs(inPort
);
946 if ( service
->qtotal
== 1 )
949 // create a new queue
960 require_action( q
, exit
, err
= E_OUTOFMEMORY
);
963 // parse the text record.
966 err
= self
->ParseTextRecord( service
, q
, inTXTSize
, inTXT
);
967 require_noerr( err
, exit
);
969 service
->queues
.push_back( q
);
972 // we've completely resolved this service
975 self
->OnResolveService( service
);
980 // if qtotal is more than 1, then we need to get additional
981 // text records. if not, then this service is considered
985 err
= DNSServiceQueryRecord(&service
->serviceRef
, 0, inInterfaceIndex
, inFullName
, kDNSServiceType_TXT
, kDNSServiceClass_IN
, OnQuery
, (void*) service
);
986 require_noerr( err
, exit
);
988 err
= self
->StartOperation( service
->serviceRef
);
989 require_noerr( err
, exit
);
999 CPrinterSetupWizardSheet::OnQuery(
1000 DNSServiceRef inRef
,
1001 DNSServiceFlags inFlags
,
1002 uint32_t inInterfaceIndex
,
1003 DNSServiceErrorType inErrorCode
,
1004 const char * inFullName
,
1008 const void * inRData
,
1012 DEBUG_UNUSED( inTTL
);
1013 DEBUG_UNUSED( inRRClass
);
1014 DEBUG_UNUSED( inRRType
);
1015 DEBUG_UNUSED( inFullName
);
1016 DEBUG_UNUSED( inInterfaceIndex
);
1017 DEBUG_UNUSED( inRef
);
1019 Service
* service
= NULL
;
1021 CPrinterSetupWizardSheet
* self
;
1022 OSStatus err
= kNoErr
;
1024 require_noerr( inErrorCode
, exit
);
1026 service
= reinterpret_cast<Service
*>( inContext
);
1027 require_quiet( service
, exit
);
1029 self
= service
->printer
->window
;
1030 require_quiet( self
, exit
);
1032 if ( ( inFlags
& kDNSServiceFlagsAdd
) && ( inRDLen
> 0 ) && ( inRData
!= NULL
) )
1034 const char * inTXT
= ( const char * ) inRData
;
1037 // create a new queue
1048 require_action( q
, exit
, err
= E_OUTOFMEMORY
);
1050 err
= service
->printer
->window
->ParseTextRecord( service
, q
, inRDLen
, inTXT
);
1051 require_noerr( err
, exit
);
1057 service
->queues
.push_back( q
);
1059 if ( service
->queues
.size() == service
->qtotal
)
1062 // else if moreComing is not set, then we're going
1063 // to assume that we're done
1066 self
->StopOperation( service
->serviceRef
);
1072 service
->queues
.sort( OrderQueueFunc
);
1075 // we've completely resolved this service
1078 self
->OnResolveService( service
);
1084 if ( err
&& service
&& ( service
->serviceRef
!= NULL
) )
1086 service
->printer
->window
->StopOperation( service
->serviceRef
);
1094 CPrinterSetupWizardSheet::OnAddPrinter(
1095 uint32_t inInterfaceIndex
,
1096 const char * inName
,
1097 const char * inType
,
1098 const char * inDomain
,
1101 Printer
* printer
= NULL
;
1102 DWORD printerNameCount
;
1105 DEBUG_UNUSED( inInterfaceIndex
);
1106 DEBUG_UNUSED( inType
);
1107 DEBUG_UNUSED( inDomain
);
1111 printer
= new Printer
;
1118 require_action( printer
, exit
, err
= E_OUTOFMEMORY
);
1120 printer
->window
= this;
1121 printer
->name
= inName
;
1123 err
= UTF8StringToStringObject(inName
, printer
->displayName
);
1125 printer
->actualName
= printer
->displayName
;
1126 printer
->installed
= false;
1127 printer
->deflt
= false;
1128 printer
->resolving
= 0;
1130 // Compare this name against printers that are already installed
1131 // to avoid name clashes. Rename as necessary
1132 // to come up with a unique name.
1134 printerNameCount
= 2;
1138 CPrinterSetupWizardSheet::PrinterNames::iterator it
;
1140 // <rdar://problem/4141221> Don't use find to do comparisons because we need to
1141 // do a case insensitive string comparison
1143 for ( it
= m_printerNames
.begin(); it
!= m_printerNames
.end(); it
++ )
1145 if ( (*it
).CompareNoCase( printer
->actualName
) == 0 )
1151 if (it
!= m_printerNames
.end())
1153 printer
->actualName
.Format(L
"%s (%d)", printer
->displayName
, printerNameCount
);
1163 m_printers
.push_back( printer
);
1165 if ( GetActivePage() == &m_pgSecond
)
1167 m_pgSecond
.OnAddPrinter( printer
, moreComing
);
1177 CPrinterSetupWizardSheet::OnAddService(
1179 uint32_t inInterfaceIndex
,
1180 const char * inName
,
1181 const char * inType
,
1182 const char * inDomain
)
1184 Service
* service
= NULL
;
1185 OSStatus err
= kNoErr
;
1187 DEBUG_UNUSED( inName
);
1188 DEBUG_UNUSED( inDomain
);
1192 service
= new Service
;
1199 require_action( service
, exit
, err
= E_OUTOFMEMORY
);
1201 service
->printer
= printer
;
1202 service
->ifi
= inInterfaceIndex
;
1203 service
->type
= inType
;
1204 service
->domain
= inDomain
;
1205 service
->qtotal
= 1;
1207 service
->serviceRef
= NULL
;
1209 printer
->services
.push_back( service
);
1212 // if the printer is selected, then we'll want to start a
1213 // resolve on this guy
1216 if ( printer
== m_selectedPrinter
)
1218 StartResolve( service
);
1228 CPrinterSetupWizardSheet::OnRemovePrinter( Printer
* printer
, bool moreComing
)
1230 CPropertyPage
* active
= GetActivePage();
1231 OSStatus err
= kNoErr
;
1233 if ( active
== &m_pgSecond
)
1235 m_pgSecond
.OnRemovePrinter( printer
, moreComing
);
1238 m_printers
.remove( printer
);
1240 if ( m_selectedPrinter
== printer
)
1242 m_selectedPrinter
= NULL
;
1244 if ( ( active
== &m_pgThird
) || ( active
== &m_pgFourth
) )
1249 caption
.LoadString( IDS_ERROR_CAPTION
);
1250 message
.LoadString( IDS_PRINTER_UNAVAILABLE
);
1252 MessageBox(message
, caption
, MB_OK
|MB_ICONEXCLAMATION
);
1254 SetActivePage( &m_pgSecond
);
1265 CPrinterSetupWizardSheet::OnRemoveService( Service
* service
)
1267 OSStatus err
= kNoErr
;
1269 if ( service
&& ( --service
->refs
== 0 ) )
1271 if ( service
->serviceRef
!= NULL
)
1273 err
= StopResolve( service
);
1274 require_noerr( err
, exit
);
1277 service
->printer
->services
.remove( service
);
1289 CPrinterSetupWizardSheet::OnResolveService( Service
* service
)
1291 // Make sure that the active page is page 2
1293 require_quiet( GetActivePage() == &m_pgSecond
, exit
);
1295 if ( !--service
->printer
->resolving
)
1297 // sort the services now. we want the service that
1298 // has the highest priority queue to be first in
1301 service
->printer
->services
.sort( OrderServiceFunc
);
1303 // Now we can hit next
1305 SetWizardButtons( PSWIZB_BACK
|PSWIZB_NEXT
);
1311 // And tell page 2 about it
1313 m_pgSecond
.OnResolveService( service
);
1323 CPrinterSetupWizardSheet::StartBrowse()
1328 // setup the DNS-SD browsing
1330 err
= DNSServiceBrowse( &m_pdlBrowser
, 0, 0, kPDLServiceType
, NULL
, OnBrowse
, this );
1331 require_noerr( err
, exit
);
1333 err
= StartOperation( m_pdlBrowser
);
1334 require_noerr( err
, exit
);
1336 err
= DNSServiceBrowse( &m_lprBrowser
, 0, 0, kLPRServiceType
, NULL
, OnBrowse
, this );
1337 require_noerr( err
, exit
);
1339 err
= StartOperation( m_lprBrowser
);
1340 require_noerr( err
, exit
);
1342 err
= DNSServiceBrowse( &m_ippBrowser
, 0, 0, kIPPServiceType
, NULL
, OnBrowse
, this );
1343 require_noerr( err
, exit
);
1345 err
= StartOperation( m_ippBrowser
);
1346 require_noerr( err
, exit
);
1355 CPrinterSetupWizardSheet::StopBrowse()
1359 err
= StopOperation( m_pdlBrowser
);
1360 require_noerr( err
, exit
);
1362 err
= StopOperation( m_lprBrowser
);
1363 require_noerr( err
, exit
);
1365 err
= StopOperation( m_ippBrowser
);
1366 require_noerr( err
, exit
);
1368 while ( m_printers
.size() > 0 )
1370 Printer
* printer
= m_printers
.front();
1372 m_printers
.pop_front();
1374 if ( printer
->resolving
)
1376 StopResolve( printer
);
1389 CPrinterSetupWizardSheet::StartResolve( Printer
* printer
)
1391 OSStatus err
= kNoErr
;
1392 Services::iterator it
;
1396 for ( it
= printer
->services
.begin(); it
!= printer
->services
.end(); it
++ )
1398 if ( (*it
)->serviceRef
== NULL
)
1400 err
= StartResolve( *it
);
1401 require_noerr( err
, exit
);
1405 m_selectedPrinter
= printer
;
1414 CPrinterSetupWizardSheet::StartResolve( Service
* service
)
1416 OSStatus err
= kNoErr
;
1418 check( service
->serviceRef
== NULL
);
1421 // clean out any queues that were collected during a previous
1425 service
->EmptyQueues();
1428 // now start the new resolve
1431 err
= DNSServiceResolve( &service
->serviceRef
, 0, 0, service
->printer
->name
.c_str(), service
->type
.c_str(), service
->domain
.c_str(), (DNSServiceResolveReply
) OnResolve
, service
);
1432 require_noerr( err
, exit
);
1434 err
= StartOperation( service
->serviceRef
);
1435 require_noerr( err
, exit
);
1438 // If we're not currently resolving, then disable the next button
1439 // and set the cursor to hourglass
1442 if ( !service
->printer
->resolving
)
1444 SetWizardButtons( PSWIZB_BACK
);
1447 SetCursor(m_active
);
1450 service
->printer
->resolving
++;
1459 CPrinterSetupWizardSheet::StopResolve(Printer
* printer
)
1461 OSStatus err
= kNoErr
;
1465 Services::iterator it
;
1467 for ( it
= printer
->services
.begin(); it
!= printer
->services
.end(); it
++ )
1469 if ( (*it
)->serviceRef
)
1471 err
= StopResolve( *it
);
1472 require_noerr( err
, exit
);
1483 CPrinterSetupWizardSheet::StopResolve( Service
* service
)
1487 check( service
->serviceRef
);
1489 err
= StopOperation( service
->serviceRef
);
1490 require_noerr( err
, exit
);
1492 service
->printer
->resolving
--;
1501 CPrinterSetupWizardSheet::StartOperation( DNSServiceRef ref
)
1505 err
= WSAAsyncSelect((SOCKET
) DNSServiceRefSockFD(ref
), m_hWnd
, WM_SOCKET_EVENT
, FD_READ
|FD_CLOSE
);
1506 require_noerr( err
, exit
);
1508 m_serviceRefList
.push_back( ref
);
1517 CPrinterSetupWizardSheet::StopOperation( DNSServiceRef
& ref
)
1519 OSStatus err
= kNoErr
;
1523 m_serviceRefList
.remove( ref
);
1525 if ( IsWindow( m_hWnd
) )
1527 err
= WSAAsyncSelect((SOCKET
) DNSServiceRefSockFD( ref
), m_hWnd
, 0, 0 );
1528 require_noerr( err
, exit
);
1531 DNSServiceRefDeallocate( ref
);
1542 CPrinterSetupWizardSheet::ParseTextRecord( Service
* service
, Queue
* q
, uint16_t inTXTSize
, const char * inTXT
)
1547 // <rdar://problem/3946587> Use TXTRecord APIs declared in dns_sd.h
1549 bool qtotalDefined
= false;
1553 OSStatus err
= kNoErr
;
1555 // <rdar://problem/3987680> Default to queue "lp"
1559 // <rdar://problem/4003710> Default pdl key to be "application/postscript"
1561 q
->pdl
= L
"application/postscript";
1563 if ( ( val
= TXTRecordGetValuePtr( inTXTSize
, inTXT
, "rp", &len
) ) != NULL
)
1565 // Stringize val ( doesn't have trailing '\0' yet )
1567 memcpy( buf
, val
, len
);
1570 err
= UTF8StringToStringObject( buf
, q
->name
);
1571 require_noerr( err
, exit
);
1574 if ( ( val
= TXTRecordGetValuePtr( inTXTSize
, inTXT
, "pdl", &len
) ) != NULL
)
1576 // Stringize val ( doesn't have trailing '\0' yet )
1578 memcpy( buf
, val
, len
);
1581 err
= UTF8StringToStringObject( buf
, q
->pdl
);
1582 require_noerr( err
, exit
);
1585 if ( ( ( val
= TXTRecordGetValuePtr( inTXTSize
, inTXT
, "usb_mfg", &len
) ) != NULL
) ||
1586 ( ( val
= TXTRecordGetValuePtr( inTXTSize
, inTXT
, "usb_manufacturer", &len
) ) != NULL
) )
1588 // Stringize val ( doesn't have trailing '\0' yet )
1590 memcpy( buf
, val
, len
);
1593 err
= UTF8StringToStringObject( buf
, q
->usb_MFG
);
1594 require_noerr( err
, exit
);
1597 if ( ( ( val
= TXTRecordGetValuePtr( inTXTSize
, inTXT
, "usb_mdl", &len
) ) != NULL
) ||
1598 ( ( val
= TXTRecordGetValuePtr( inTXTSize
, inTXT
, "usb_model", &len
) ) != NULL
) )
1600 // Stringize val ( doesn't have trailing '\0' yet )
1602 memcpy( buf
, val
, len
);
1605 err
= UTF8StringToStringObject( buf
, q
->usb_MDL
);
1606 require_noerr( err
, exit
);
1609 if ( ( val
= TXTRecordGetValuePtr( inTXTSize
, inTXT
, "ty", &len
) ) != NULL
)
1611 // Stringize val ( doesn't have trailing '\0' yet )
1613 memcpy( buf
, val
, len
);
1616 err
= UTF8StringToStringObject( buf
, q
->description
);
1617 require_noerr( err
, exit
);
1620 if ( ( val
= TXTRecordGetValuePtr( inTXTSize
, inTXT
, "product", &len
) ) != NULL
)
1622 // Stringize val ( doesn't have trailing '\0' yet )
1624 memcpy( buf
, val
, len
);
1627 err
= UTF8StringToStringObject( buf
, q
->product
);
1628 require_noerr( err
, exit
);
1631 if ( ( val
= TXTRecordGetValuePtr( inTXTSize
, inTXT
, "note", &len
) ) != NULL
)
1633 // Stringize val ( doesn't have trailing '\0' yet )
1635 memcpy( buf
, val
, len
);
1638 err
= UTF8StringToStringObject( buf
, q
->location
);
1639 require_noerr( err
, exit
);
1642 if ( ( val
= TXTRecordGetValuePtr( inTXTSize
, inTXT
, "qtotal", &len
) ) != NULL
)
1644 // Stringize val ( doesn't have trailing '\0' yet )
1646 memcpy( buf
, val
, len
);
1649 service
->qtotal
= (unsigned short) atoi( buf
);
1650 qtotalDefined
= true;
1653 if ( ( val
= TXTRecordGetValuePtr( inTXTSize
, inTXT
, "priority", &len
) ) != NULL
)
1655 // Stringize val ( doesn't have trailing '\0' yet )
1657 memcpy( buf
, val
, len
);
1660 q
->priority
= atoi( buf
);
1663 // <rdar://problem/4124524> Was this printer discovered via OS X Printer Sharing?
1665 if ( TXTRecordContainsKey( inTXTSize
, inTXT
, "printer-state" ) || TXTRecordContainsKey( inTXTSize
, inTXT
, "printer-type" ) )
1667 service
->printer
->isSharedFromOSX
= true;
1672 // The following code is to fix a problem with older HP
1673 // printers that don't include "qtotal" in their text
1674 // record. We'll check to see if the q->name is "TEXT"
1675 // and if so, we're going to modify it to be "lp" so
1676 // that we don't use the wrong queue
1678 if ( !err
&& !qtotalDefined
&& ( q
->name
== L
"TEXT" ) )
1688 CPrinterSetupWizardSheet::Lookup(const char * inName
)
1692 Printer
* printer
= NULL
;
1693 Printers::iterator it
;
1695 for ( it
= m_printers
.begin(); it
!= m_printers
.end(); it
++ )
1697 if ( (*it
)->name
== inName
)
1709 CPrinterSetupWizardSheet::OrderServiceFunc( const Service
* a
, const Service
* b
)
1713 q1
= (a
->queues
.size() > 0) ? a
->queues
.front() : NULL
;
1715 q2
= (b
->queues
.size() > 0) ? b
->queues
.front() : NULL
;
1721 else if ( q1
&& !q2
)
1725 else if ( !q1
&& q2
)
1729 else if ( q1
->priority
< q2
->priority
)
1733 else if ( q1
->priority
> q2
->priority
)
1737 else if ( ( a
->type
== kPDLServiceType
) || ( ( a
->type
== kLPRServiceType
) && ( b
->type
== kIPPServiceType
) ) )
1749 CPrinterSetupWizardSheet::OrderQueueFunc( const Queue
* q1
, const Queue
* q2
)
1751 return ( q1
->priority
<= q2
->priority
) ? true : false;