1 /* -*- Mode: C; tab-width: 4 -*-
3 * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
17 Change History (most recent first):
19 $Log: PrinterSetupWizardSheet.cpp,v $
20 Revision 1.35 2006/08/14 23:24:09 cheshire
21 Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
23 Revision 1.34 2005/10/05 17:32:51 herscher
24 <rdar://problem/4141221> Use a case insensitive compare operation to check whether a printer with the same name has already been installed.
26 Revision 1.33 2005/07/11 20:17:15 shersche
27 <rdar://problem/4124524> UI fixes associated with CUPS printer workaround fix.
29 Revision 1.32 2005/07/07 17:53:20 shersche
30 Fix problems associated with the CUPS printer workaround fix.
32 Revision 1.31 2005/06/30 18:02:54 shersche
33 <rdar://problem/4124524> Workaround for Mac OS X Printer Sharing bug
35 Revision 1.30 2005/04/13 17:46:22 shersche
36 <rdar://problem/4082122> Generic PCL not selected when printers advertise multiple text records
38 Revision 1.29 2005/02/14 20:48:37 shersche
39 <rdar://problem/4003710> Default pdl key to "application/postscript"
41 Revision 1.28 2005/02/14 20:37:53 shersche
42 <rdar://problem/4003944> Populate comment field with the model name that users see in the wizard UI.
44 Revision 1.27 2005/02/09 05:04:03 shersche
45 <rdar://problem/3946587> Use TXTRecordGetValuePtr() API in ParseTextRecord
47 Revision 1.26 2005/02/08 21:45:06 shersche
48 <rdar://problem/3947490> Default to Generic PostScript or PCL if unable to match driver
50 Revision 1.25 2005/02/08 18:54:17 shersche
51 <rdar://problem/3987680> Default queue name is "lp" when rp key is not specified.
53 Revision 1.24 2005/02/01 02:15:55 shersche
54 <rdar://problem/3946587> Use TXTRecord parsing APIs in ParseTextRecord
56 Revision 1.23 2005/01/31 23:54:30 shersche
57 <rdar://problem/3947508> Start browsing when printer wizard starts. Move browsing logic from CSecondPage object to CPrinterSetupWizardSheet object.
59 Revision 1.22 2005/01/25 18:49:43 shersche
60 Get icon resources from resource DLL
62 Revision 1.21 2005/01/10 01:09:32 shersche
63 Use the "note" key to populate pLocation field when setting up printer
65 Revision 1.20 2005/01/03 19:05:01 shersche
66 Store pointer to instance of wizard sheet so that print driver install thread sends a window message to the correct window
68 Revision 1.19 2004/12/31 07:23:53 shersche
69 Don't modify the button setting in SetSelectedPrinter()
71 Revision 1.18 2004/12/29 18:53:38 shersche
72 <rdar://problem/3725106>
73 <rdar://problem/3737413> Added support for LPR and IPP protocols as well as support for obtaining multiple text records. Reorganized and simplified codebase.
74 Bug #: 3725106, 3737413
76 Revision 1.17 2004/10/12 18:02:53 shersche
77 <rdar://problem/3764873> Escape '/', '@', '"' characters in printui command.
80 Revision 1.16 2004/09/13 21:27:22 shersche
81 <rdar://problem/3796483> Pass the moreComing flag to OnAddPrinter and OnRemovePrinter callbacks
84 Revision 1.15 2004/09/11 05:59:06 shersche
85 <rdar://problem/3785766> Fix code that generates unique printer names based on currently installed printers
88 Revision 1.14 2004/09/02 01:57:58 cheshire
89 <rdar://problem/3783611> Fix incorrect testing of MoreComing flag
91 Revision 1.13 2004/07/26 21:06:29 shersche
92 <rdar://problem/3739200> Removing trailing '.' in hostname
95 Revision 1.12 2004/07/13 21:24:23 rpantos
96 Fix for <rdar://problem/3701120>.
98 Revision 1.11 2004/06/28 00:51:47 shersche
99 Move call to EnumPrinters out of browse callback into standalone function
101 Revision 1.10 2004/06/27 23:06:47 shersche
102 code cleanup, make sure EnumPrinters returns non-zero value
104 Revision 1.9 2004/06/27 15:49:31 shersche
105 clean up some cruft in the printer browsing code
107 Revision 1.8 2004/06/27 08:04:51 shersche
108 copy selected printer to prevent printer being deleted out from under
110 Revision 1.7 2004/06/26 23:27:12 shersche
111 support for installing multiple printers of the same name
113 Revision 1.6 2004/06/26 21:22:39 shersche
114 handle spaces in file names
116 Revision 1.5 2004/06/26 03:19:57 shersche
117 clean up warning messages
119 Submitted by: herscher
121 Revision 1.4 2004/06/25 02:26:52 shersche
122 Normalize key fields in text record entries
123 Submitted by: herscher
125 Revision 1.3 2004/06/24 20:12:07 shersche
127 Submitted by: herscher
129 Revision 1.2 2004/06/23 17:58:21 shersche
130 <rdar://problem/3701837> eliminated memory leaks on exit
131 <rdar://problem/3701926> installation of a printer that is already installed results in a no-op
132 Bug #: 3701837, 3701926
133 Submitted by: herscher
135 Revision 1.1 2004/06/18 04:36:57 rpantos
142 #include "PrinterSetupWizardApp.h"
143 #include "PrinterSetupWizardSheet.h"
144 #include "CommonServices.h"
145 #include "DebugServices.h"
146 #include "WinServices.h"
148 #include <winspool.h>
153 #pragma warning(disable:4702)
156 #if( !TARGET_OS_WINDOWS_CE )
157 # include <mswsock.h>
158 # include <process.h>
163 #define WM_SOCKET_EVENT ( WM_USER + 0x100 )
164 #define WM_PROCESS_EVENT ( WM_USER + 0x101 )
167 // CPrinterSetupWizardSheet
168 CPrinterSetupWizardSheet
* CPrinterSetupWizardSheet::m_self
;
170 IMPLEMENT_DYNAMIC(CPrinterSetupWizardSheet
, CPropertySheet
)
171 CPrinterSetupWizardSheet::CPrinterSetupWizardSheet(UINT nIDCaption
, CWnd
* pParentWnd
, UINT iSelectPage
)
172 :CPropertySheet(nIDCaption
, pParentWnd
, iSelectPage
),
173 m_selectedPrinter(NULL
),
174 m_driverThreadExitCode( 0 ),
175 m_driverThreadFinished( false ),
176 m_pdlBrowser( NULL
),
177 m_ippBrowser( NULL
),
178 m_lprBrowser( NULL
),
181 m_arrow
= LoadCursor(0, IDC_ARROW
);
182 m_wait
= LoadCursor(0, IDC_APPSTARTING
);
192 CPrinterSetupWizardSheet::~CPrinterSetupWizardSheet()
196 while ( m_printers
.size() > 0 )
198 printer
= m_printers
.front();
199 m_printers
.pop_front();
208 // ------------------------------------------------------
209 // SetSelectedPrinter
211 // Manages setting a printer as the printer to install. Stops
212 // any pending resolves.
215 CPrinterSetupWizardSheet::SetSelectedPrinter(Printer
* printer
)
217 check( !printer
|| ( printer
!= m_selectedPrinter
) );
219 m_selectedPrinter
= printer
;
224 CPrinterSetupWizardSheet::LoadPrinterNames()
230 // rdar://problem/3701926 - Printer can't be installed twice
232 // First thing we want to do is make sure the printer isn't already installed.
233 // If the printer name is found, we'll try and rename it until we
234 // find a unique name
236 DWORD dwNeeded
= 0, dwNumPrinters
= 0;
238 BOOL ok
= EnumPrinters(PRINTER_ENUM_LOCAL
, NULL
, 4, NULL
, 0, &dwNeeded
, &dwNumPrinters
);
239 err
= translate_errno( ok
, errno_compat(), kUnknownErr
);
241 if ((err
== ERROR_INSUFFICIENT_BUFFER
) && (dwNeeded
> 0))
245 buffer
= new unsigned char[dwNeeded
];
252 require_action( buffer
, exit
, kNoMemoryErr
);
253 ok
= EnumPrinters(PRINTER_ENUM_LOCAL
, NULL
, 4, buffer
, dwNeeded
, &dwNeeded
, &dwNumPrinters
);
254 err
= translate_errno( ok
, errno_compat(), kUnknownErr
);
255 require_noerr( err
, exit
);
257 for (DWORD index
= 0; index
< dwNumPrinters
; index
++)
259 PRINTER_INFO_4
* lppi4
= (PRINTER_INFO_4
*) (buffer
+ index
* sizeof(PRINTER_INFO_4
));
261 m_printerNames
.push_back( lppi4
->pPrinterName
);
277 // ------------------------------------------------------
280 // Installs a printer with Windows.
282 // NOTE: this works one of two ways, depending on whether
283 // there are drivers already installed for this printer.
284 // If there are, then we can just create a port with XcvData,
285 // and then call AddPrinter. If not, we use the printui.dll
286 // to install the printer. Actually installing drivers that
287 // are not currently installed is painful, and it's much
288 // easier and less error prone to just let printui.dll do
289 // the hard work for us.
293 CPrinterSetupWizardSheet::InstallPrinter(Printer
* printer
)
299 service
= printer
->services
.front();
303 // if the driver isn't installed, then install it
306 if ( !printer
->driverInstalled
)
312 m_driverThreadFinished
= false;
317 hThread
= (HANDLE
) _beginthreadex_compat( NULL
, 0, InstallDriverThread
, printer
, 0, &threadID
);
318 err
= translate_errno( hThread
, (OSStatus
) GetLastError(), kUnknownErr
);
319 require_noerr( err
, exit
);
324 while (!m_driverThreadFinished
)
328 GetMessage( &msg
, m_hWnd
, 0, 0 );
329 TranslateMessage(&msg
);
330 DispatchMessage(&msg
);
334 // Wait until child process exits.
336 dwResult
= WaitForSingleObject( hThread
, INFINITE
);
337 err
= translate_errno( dwResult
== WAIT_OBJECT_0
, errno_compat(), err
= kUnknownErr
);
338 require_noerr( err
, exit
);
341 // check the return value of thread
343 require_noerr( m_driverThreadExitCode
, exit
);
346 // now we know that the driver was successfully installed
348 printer
->driverInstalled
= true;
351 if ( service
->type
== kPDLServiceType
)
353 err
= InstallPrinterPDLAndLPR( printer
, service
, PROTOCOL_RAWTCP_TYPE
);
354 require_noerr( err
, exit
);
356 else if ( service
->type
== kLPRServiceType
)
358 err
= InstallPrinterPDLAndLPR( printer
, service
, PROTOCOL_LPR_TYPE
);
359 require_noerr( err
, exit
);
361 else if ( service
->type
== kIPPServiceType
)
363 err
= InstallPrinterIPP( printer
, service
);
364 require_noerr( err
, exit
);
369 require_noerr( err
, exit
);
372 printer
->installed
= true;
375 // if the user specified a default printer, set it
379 ok
= SetDefaultPrinter( printer
->actualName
);
380 err
= translate_errno( ok
, errno_compat(), err
= kUnknownErr
);
381 require_noerr( err
, exit
);
391 CPrinterSetupWizardSheet::InstallPrinterPDLAndLPR(Printer
* printer
, Service
* service
, DWORD protocol
)
393 PRINTER_DEFAULTS printerDefaults
= { NULL
, NULL
, SERVER_ACCESS_ADMINISTER
};
395 DWORD cbInputData
= 100;
396 PBYTE pOutputData
= NULL
;
397 DWORD cbOutputNeeded
= 0;
398 PORT_DATA_1 portData
;
399 PRINTER_INFO_2 pInfo
;
401 HANDLE hPrinter
= NULL
;
406 check(printer
!= NULL
);
407 check(printer
->installed
== false);
409 q
= service
->queues
.front();
412 ok
= OpenPrinter(L
",XcvMonitor Standard TCP/IP Port", &hXcv
, &printerDefaults
);
413 err
= translate_errno( ok
, errno_compat(), kUnknownErr
);
414 require_noerr( err
, exit
);
417 // BUGBUG: MSDN said this is not required, but my experience shows it is required
421 pOutputData
= new BYTE
[cbInputData
];
428 require_action( pOutputData
, exit
, err
= kNoMemoryErr
);
433 ZeroMemory(&portData
, sizeof(PORT_DATA_1
));
434 wcscpy(portData
.sztPortName
, printer
->portName
);
436 portData
.dwPortNumber
= service
->portNumber
;
437 portData
.dwVersion
= 1;
439 portData
.dwProtocol
= protocol
;
440 portData
.cbSize
= sizeof PORT_DATA_1
;
441 portData
.dwReserved
= 0L;
443 wcscpy(portData
.sztQueue
, q
->name
);
444 wcscpy(portData
.sztIPAddress
, service
->hostname
);
445 wcscpy(portData
.sztHostAddress
, service
->hostname
);
447 ok
= XcvData(hXcv
, L
"AddPort", (PBYTE
) &portData
, sizeof(PORT_DATA_1
), pOutputData
, cbInputData
, &cbOutputNeeded
, &dwStatus
);
448 err
= translate_errno( ok
, errno_compat(), kUnknownErr
);
449 require_noerr( err
, exit
);
454 ZeroMemory(&pInfo
, sizeof(pInfo
));
456 pInfo
.pPrinterName
= printer
->actualName
.GetBuffer();
457 pInfo
.pServerName
= NULL
;
458 pInfo
.pShareName
= NULL
;
459 pInfo
.pPortName
= printer
->portName
.GetBuffer();
460 pInfo
.pDriverName
= printer
->modelName
.GetBuffer();
461 pInfo
.pComment
= printer
->displayModelName
.GetBuffer();
462 pInfo
.pLocation
= q
->location
.GetBuffer();
463 pInfo
.pDevMode
= NULL
;
464 pInfo
.pDevMode
= NULL
;
465 pInfo
.pSepFile
= L
"";
466 pInfo
.pPrintProcessor
= L
"winprint";
467 pInfo
.pDatatype
= L
"RAW";
468 pInfo
.pParameters
= L
"";
469 pInfo
.pSecurityDescriptor
= NULL
;
470 pInfo
.Attributes
= PRINTER_ATTRIBUTE_QUEUED
;
472 pInfo
.DefaultPriority
= 0;
476 hPrinter
= AddPrinter(NULL
, 2, (LPBYTE
) &pInfo
);
477 err
= translate_errno( hPrinter
, errno_compat(), kUnknownErr
);
478 require_noerr( err
, exit
);
482 if (hPrinter
!= NULL
)
484 ClosePrinter(hPrinter
);
492 if (pOutputData
!= NULL
)
494 delete [] pOutputData
;
502 CPrinterSetupWizardSheet::InstallPrinterIPP(Printer
* printer
, Service
* service
)
504 DEBUG_UNUSED( service
);
506 Queue
* q
= service
->SelectedQueue();
507 HANDLE hPrinter
= NULL
;
508 PRINTER_INFO_2 pInfo
;
516 ZeroMemory(&pInfo
, sizeof(PRINTER_INFO_2
));
518 pInfo
.pPrinterName
= printer
->actualName
.GetBuffer();
519 pInfo
.pPortName
= printer
->portName
.GetBuffer();
520 pInfo
.pDriverName
= printer
->modelName
.GetBuffer();
521 pInfo
.pPrintProcessor
= L
"winprint";
522 pInfo
.pLocation
= q
->location
.GetBuffer();
523 pInfo
.pComment
= printer
->displayModelName
.GetBuffer();
524 pInfo
.Attributes
= PRINTER_ATTRIBUTE_NETWORK
| PRINTER_ATTRIBUTE_LOCAL
;
526 hPrinter
= AddPrinter(NULL
, 2, (LPBYTE
)&pInfo
);
527 err
= translate_errno( hPrinter
, errno_compat(), kUnknownErr
);
528 require_noerr( err
, exit
);
532 if ( hPrinter
!= NULL
)
534 ClosePrinter(hPrinter
);
541 BEGIN_MESSAGE_MAP(CPrinterSetupWizardSheet
, CPropertySheet
)
542 ON_MESSAGE( WM_SOCKET_EVENT
, OnSocketEvent
)
543 ON_MESSAGE( WM_PROCESS_EVENT
, OnProcessEvent
)
549 // ------------------------------------------------------
552 // Traps when the user hits Finish
554 BOOL
CPrinterSetupWizardSheet::OnCommand(WPARAM wParam
, LPARAM lParam
)
557 // Check if this is OK
559 if (wParam
== ID_WIZFINISH
) // If OK is hit...
564 return CPropertySheet::OnCommand(wParam
, lParam
);
568 // ------------------------------------------------------
571 // Initializes this Dialog object.
573 BOOL
CPrinterSetupWizardSheet::OnInitDialog()
577 CPropertySheet::OnInitDialog();
580 require_noerr( err
, exit
);
588 if ( err
== kDNSServiceErr_Firewall
)
590 CString text
, caption
;
592 text
.LoadString( IDS_FIREWALL
);
593 caption
.LoadString( IDS_FIREWALL_CAPTION
);
595 MessageBox(text
, caption
, MB_OK
|MB_ICONEXCLAMATION
);
599 CPrinterSetupWizardSheet::WizardException exc
;
601 exc
.text
.LoadString( IDS_NO_MDNSRESPONDER_SERVICE_TEXT
);
602 exc
.caption
.LoadString( IDS_ERROR_CAPTION
);
612 // ------------------------------------------------------
615 // This is called when Windows wants to know what cursor
616 // to display. So we tell it.
619 CPrinterSetupWizardSheet::OnSetCursor(CWnd
* pWnd
, UINT nHitTest
, UINT message
)
622 DEBUG_UNUSED(nHitTest
);
623 DEBUG_UNUSED(message
);
630 // ------------------------------------------------------
633 // This is not fully implemented yet.
637 CPrinterSetupWizardSheet::OnContextMenu(CWnd
* pWnd
, CPoint pos
)
648 // ------------------------------------------------------
651 // This is called when the user hits the "Finish" button
654 CPrinterSetupWizardSheet::OnOK()
656 check ( m_selectedPrinter
!= NULL
);
658 SetWizardButtons( PSWIZB_DISABLEDFINISH
);
660 if ( InstallPrinter( m_selectedPrinter
) != kNoErr
)
665 caption
.LoadString(IDS_INSTALL_ERROR_CAPTION
);
666 message
.LoadString(IDS_INSTALL_ERROR_MESSAGE
);
668 MessageBox(message
, caption
, MB_OK
|MB_ICONEXCLAMATION
);
675 // CPrinterSetupWizardSheet message handlers
677 void CPrinterSetupWizardSheet::Init(void)
680 AddPage(&m_pgSecond
);
682 AddPage(&m_pgFourth
);
684 m_psh
.dwFlags
&= (~PSH_HASHELP
);
686 m_psh
.dwFlags
|= PSH_WIZARD97
|PSH_WATERMARK
|PSH_HEADER
;
687 m_psh
.pszbmWatermark
= MAKEINTRESOURCE(IDB_WATERMARK
);
688 m_psh
.pszbmHeader
= MAKEINTRESOURCE(IDB_BANNER_ICON
);
690 m_psh
.hInstance
= GetNonLocalizedResources();
697 CPrinterSetupWizardSheet::OnSocketEvent(WPARAM inWParam
, LPARAM inLParam
)
699 if (WSAGETSELECTERROR(inLParam
) && !(HIWORD(inLParam
)))
701 dlog( kDebugLevelError
, "OnServiceEvent: window error\n" );
705 SOCKET sock
= (SOCKET
) inWParam
;
708 ServiceRefList::iterator begin
= m_serviceRefList
.begin();
709 ServiceRefList::iterator end
= m_serviceRefList
.end();
713 DNSServiceRef ref
= *begin
++;
717 if ((SOCKET
) DNSServiceRefSockFD(ref
) == sock
)
719 DNSServiceProcessResult(ref
);
730 CPrinterSetupWizardSheet::OnProcessEvent(WPARAM inWParam
, LPARAM inLParam
)
732 DEBUG_UNUSED(inLParam
);
734 m_driverThreadExitCode
= (DWORD
) inWParam
;
735 m_driverThreadFinished
= true;
742 CPrinterSetupWizardSheet::InstallDriverThread( LPVOID inParam
)
744 Printer
* printer
= (Printer
*) inParam
;
749 PROCESS_INFORMATION pi
;
756 // because we're calling endthreadex(), C++ objects won't be cleaned up
757 // correctly. we'll nest the CString 'command' inside a block so
758 // that it's destructor will be invoked.
763 ZeroMemory( &si
, sizeof(si
) );
765 ZeroMemory( &pi
, sizeof(pi
) );
767 command
.Format(L
"rundll32.exe printui.dll,PrintUIEntry /ia /m \"%s\" /f \"%s\"", (LPCTSTR
) printer
->modelName
, (LPCTSTR
) printer
->infFileName
);
769 ok
= CreateProcess(NULL
, command
.GetBuffer(), NULL
, NULL
, FALSE
, 0, NULL
, NULL
, &si
, &pi
);
770 err
= translate_errno( ok
, errno_compat(), kUnknownErr
);
771 require_noerr( err
, exit
);
773 dwResult
= WaitForSingleObject( pi
.hProcess
, INFINITE
);
774 translate_errno( dwResult
== WAIT_OBJECT_0
, errno_compat(), err
= kUnknownErr
);
775 require_noerr( err
, exit
);
777 ok
= GetExitCodeProcess( pi
.hProcess
, &exitCode
);
778 err
= translate_errno( ok
, errno_compat(), kUnknownErr
);
779 require_noerr( err
, exit
);
785 // Close process and thread handles.
789 CloseHandle( pi
.hProcess
);
794 CloseHandle( pi
.hThread
);
798 // alert the main thread
800 m_self
->PostMessage( WM_PROCESS_EVENT
, err
, exitCode
);
802 _endthreadex_compat( 0 );
809 CPrinterSetupWizardSheet::OnBrowse(
811 DNSServiceFlags inFlags
,
812 uint32_t inInterfaceIndex
,
813 DNSServiceErrorType inErrorCode
,
816 const char * inDomain
,
821 CPrinterSetupWizardSheet
* self
;
822 bool moreComing
= (bool) (inFlags
& kDNSServiceFlagsMoreComing
);
823 CPropertyPage
* active
;
824 Printer
* printer
= NULL
;
825 Service
* service
= NULL
;
826 OSStatus err
= kNoErr
;
828 require_noerr( inErrorCode
, exit
);
830 self
= reinterpret_cast <CPrinterSetupWizardSheet
*>( inContext
);
831 require_quiet( self
, exit
);
833 active
= self
->GetActivePage();
834 require_quiet( active
, exit
);
836 // Have we seen this printer before?
838 printer
= self
->Lookup( inName
);
842 service
= printer
->LookupService( inType
);
845 if ( inFlags
& kDNSServiceFlagsAdd
)
849 // If not, then create a new one
851 printer
= self
->OnAddPrinter( inInterfaceIndex
, inName
, inType
, inDomain
, moreComing
);
852 require_action( printer
, exit
, err
= kUnknownErr
);
857 err
= self
->OnAddService( printer
, inInterfaceIndex
, inName
, inType
, inDomain
);
858 require_noerr( err
, exit
);
869 err
= self
->OnRemoveService( service
);
870 require_noerr( err
, exit
);
872 if ( printer
->services
.size() == 0 )
874 err
= self
->OnRemovePrinter( printer
, moreComing
);
875 require_noerr( err
, exit
);
886 CPrinterSetupWizardSheet::OnResolve(
888 DNSServiceFlags inFlags
,
889 uint32_t inInterfaceIndex
,
890 DNSServiceErrorType inErrorCode
,
891 const char * inFullName
,
892 const char * inHostName
,
898 DEBUG_UNUSED(inFullName
);
899 DEBUG_UNUSED(inInterfaceIndex
);
900 DEBUG_UNUSED(inFlags
);
903 CPrinterSetupWizardSheet
* self
;
909 require_noerr( inErrorCode
, exit
);
911 service
= reinterpret_cast<Service
*>( inContext
);
912 require_quiet( service
, exit
);
914 check( service
->refs
!= 0 );
916 self
= service
->printer
->window
;
917 require_quiet( self
, exit
);
919 err
= self
->StopOperation( service
->serviceRef
);
920 require_noerr( err
, exit
);
923 // hold on to the hostname...
925 err
= UTF8StringToStringObject( inHostName
, service
->hostname
);
926 require_noerr( err
, exit
);
929 // <rdar://problem/3739200> remove the trailing dot on hostname
931 idx
= service
->hostname
.ReverseFind('.');
933 if ((idx
> 1) && ((service
->hostname
.GetLength() - 1) == idx
))
935 service
->hostname
.Delete(idx
, 1);
939 // hold on to the port
941 service
->portNumber
= ntohs(inPort
);
943 if ( service
->qtotal
== 1 )
946 // create a new queue
957 require_action( q
, exit
, err
= E_OUTOFMEMORY
);
960 // parse the text record.
963 err
= self
->ParseTextRecord( service
, q
, inTXTSize
, inTXT
);
964 require_noerr( err
, exit
);
966 service
->queues
.push_back( q
);
969 // we've completely resolved this service
972 self
->OnResolveService( service
);
977 // if qtotal is more than 1, then we need to get additional
978 // text records. if not, then this service is considered
982 err
= DNSServiceQueryRecord(&service
->serviceRef
, 0, inInterfaceIndex
, inFullName
, kDNSServiceType_TXT
, kDNSServiceClass_IN
, OnQuery
, (void*) service
);
983 require_noerr( err
, exit
);
985 err
= self
->StartOperation( service
->serviceRef
);
986 require_noerr( err
, exit
);
996 CPrinterSetupWizardSheet::OnQuery(
998 DNSServiceFlags inFlags
,
999 uint32_t inInterfaceIndex
,
1000 DNSServiceErrorType inErrorCode
,
1001 const char * inFullName
,
1005 const void * inRData
,
1009 DEBUG_UNUSED( inTTL
);
1010 DEBUG_UNUSED( inRRClass
);
1011 DEBUG_UNUSED( inRRType
);
1012 DEBUG_UNUSED( inFullName
);
1013 DEBUG_UNUSED( inInterfaceIndex
);
1014 DEBUG_UNUSED( inRef
);
1016 Service
* service
= NULL
;
1018 CPrinterSetupWizardSheet
* self
;
1019 OSStatus err
= kNoErr
;
1021 require_noerr( inErrorCode
, exit
);
1023 service
= reinterpret_cast<Service
*>( inContext
);
1024 require_quiet( service
, exit
);
1026 self
= service
->printer
->window
;
1027 require_quiet( self
, exit
);
1029 if ( ( inFlags
& kDNSServiceFlagsAdd
) && ( inRDLen
> 0 ) && ( inRData
!= NULL
) )
1031 const char * inTXT
= ( const char * ) inRData
;
1034 // create a new queue
1045 require_action( q
, exit
, err
= E_OUTOFMEMORY
);
1047 err
= service
->printer
->window
->ParseTextRecord( service
, q
, inRDLen
, inTXT
);
1048 require_noerr( err
, exit
);
1054 service
->queues
.push_back( q
);
1056 if ( service
->queues
.size() == service
->qtotal
)
1059 // else if moreComing is not set, then we're going
1060 // to assume that we're done
1063 self
->StopOperation( service
->serviceRef
);
1069 service
->queues
.sort( OrderQueueFunc
);
1072 // we've completely resolved this service
1075 self
->OnResolveService( service
);
1081 if ( err
&& service
&& ( service
->serviceRef
!= NULL
) )
1083 service
->printer
->window
->StopOperation( service
->serviceRef
);
1091 CPrinterSetupWizardSheet::OnAddPrinter(
1092 uint32_t inInterfaceIndex
,
1093 const char * inName
,
1094 const char * inType
,
1095 const char * inDomain
,
1098 Printer
* printer
= NULL
;
1099 DWORD printerNameCount
;
1102 DEBUG_UNUSED( inInterfaceIndex
);
1103 DEBUG_UNUSED( inType
);
1104 DEBUG_UNUSED( inDomain
);
1108 printer
= new Printer
;
1115 require_action( printer
, exit
, err
= E_OUTOFMEMORY
);
1117 printer
->window
= this;
1118 printer
->name
= inName
;
1120 err
= UTF8StringToStringObject(inName
, printer
->displayName
);
1122 printer
->actualName
= printer
->displayName
;
1123 printer
->installed
= false;
1124 printer
->deflt
= false;
1125 printer
->resolving
= 0;
1127 // Compare this name against printers that are already installed
1128 // to avoid name clashes. Rename as necessary
1129 // to come up with a unique name.
1131 printerNameCount
= 2;
1135 CPrinterSetupWizardSheet::PrinterNames::iterator it
;
1137 // <rdar://problem/4141221> Don't use find to do comparisons because we need to
1138 // do a case insensitive string comparison
1140 for ( it
= m_printerNames
.begin(); it
!= m_printerNames
.end(); it
++ )
1142 if ( (*it
).CompareNoCase( printer
->actualName
) == 0 )
1148 if (it
!= m_printerNames
.end())
1150 printer
->actualName
.Format(L
"%s (%d)", printer
->displayName
, printerNameCount
);
1160 m_printers
.push_back( printer
);
1162 if ( GetActivePage() == &m_pgSecond
)
1164 m_pgSecond
.OnAddPrinter( printer
, moreComing
);
1174 CPrinterSetupWizardSheet::OnAddService(
1176 uint32_t inInterfaceIndex
,
1177 const char * inName
,
1178 const char * inType
,
1179 const char * inDomain
)
1181 Service
* service
= NULL
;
1182 OSStatus err
= kNoErr
;
1184 DEBUG_UNUSED( inName
);
1185 DEBUG_UNUSED( inDomain
);
1189 service
= new Service
;
1196 require_action( service
, exit
, err
= E_OUTOFMEMORY
);
1198 service
->printer
= printer
;
1199 service
->ifi
= inInterfaceIndex
;
1200 service
->type
= inType
;
1201 service
->domain
= inDomain
;
1202 service
->qtotal
= 1;
1204 service
->serviceRef
= NULL
;
1206 printer
->services
.push_back( service
);
1209 // if the printer is selected, then we'll want to start a
1210 // resolve on this guy
1213 if ( printer
== m_selectedPrinter
)
1215 StartResolve( service
);
1225 CPrinterSetupWizardSheet::OnRemovePrinter( Printer
* printer
, bool moreComing
)
1227 CPropertyPage
* active
= GetActivePage();
1228 OSStatus err
= kNoErr
;
1230 if ( active
== &m_pgSecond
)
1232 m_pgSecond
.OnRemovePrinter( printer
, moreComing
);
1235 m_printers
.remove( printer
);
1237 if ( m_selectedPrinter
== printer
)
1239 m_selectedPrinter
= NULL
;
1241 if ( ( active
== &m_pgThird
) || ( active
== &m_pgFourth
) )
1246 caption
.LoadString( IDS_ERROR_CAPTION
);
1247 message
.LoadString( IDS_PRINTER_UNAVAILABLE
);
1249 MessageBox(message
, caption
, MB_OK
|MB_ICONEXCLAMATION
);
1251 SetActivePage( &m_pgSecond
);
1262 CPrinterSetupWizardSheet::OnRemoveService( Service
* service
)
1264 OSStatus err
= kNoErr
;
1266 if ( service
&& ( --service
->refs
== 0 ) )
1268 if ( service
->serviceRef
!= NULL
)
1270 err
= StopResolve( service
);
1271 require_noerr( err
, exit
);
1274 service
->printer
->services
.remove( service
);
1286 CPrinterSetupWizardSheet::OnResolveService( Service
* service
)
1288 // Make sure that the active page is page 2
1290 require_quiet( GetActivePage() == &m_pgSecond
, exit
);
1292 if ( !--service
->printer
->resolving
)
1294 // sort the services now. we want the service that
1295 // has the highest priority queue to be first in
1298 service
->printer
->services
.sort( OrderServiceFunc
);
1300 // Now we can hit next
1302 SetWizardButtons( PSWIZB_BACK
|PSWIZB_NEXT
);
1308 // And tell page 2 about it
1310 m_pgSecond
.OnResolveService( service
);
1320 CPrinterSetupWizardSheet::StartBrowse()
1325 // setup the DNS-SD browsing
1327 err
= DNSServiceBrowse( &m_pdlBrowser
, 0, 0, kPDLServiceType
, NULL
, OnBrowse
, this );
1328 require_noerr( err
, exit
);
1330 err
= StartOperation( m_pdlBrowser
);
1331 require_noerr( err
, exit
);
1333 err
= DNSServiceBrowse( &m_lprBrowser
, 0, 0, kLPRServiceType
, NULL
, OnBrowse
, this );
1334 require_noerr( err
, exit
);
1336 err
= StartOperation( m_lprBrowser
);
1337 require_noerr( err
, exit
);
1339 err
= DNSServiceBrowse( &m_ippBrowser
, 0, 0, kIPPServiceType
, NULL
, OnBrowse
, this );
1340 require_noerr( err
, exit
);
1342 err
= StartOperation( m_ippBrowser
);
1343 require_noerr( err
, exit
);
1352 CPrinterSetupWizardSheet::StopBrowse()
1356 err
= StopOperation( m_pdlBrowser
);
1357 require_noerr( err
, exit
);
1359 err
= StopOperation( m_lprBrowser
);
1360 require_noerr( err
, exit
);
1362 err
= StopOperation( m_ippBrowser
);
1363 require_noerr( err
, exit
);
1365 while ( m_printers
.size() > 0 )
1367 Printer
* printer
= m_printers
.front();
1369 m_printers
.pop_front();
1371 if ( printer
->resolving
)
1373 StopResolve( printer
);
1386 CPrinterSetupWizardSheet::StartResolve( Printer
* printer
)
1388 OSStatus err
= kNoErr
;
1389 Services::iterator it
;
1393 for ( it
= printer
->services
.begin(); it
!= printer
->services
.end(); it
++ )
1395 if ( (*it
)->serviceRef
== NULL
)
1397 err
= StartResolve( *it
);
1398 require_noerr( err
, exit
);
1402 m_selectedPrinter
= printer
;
1411 CPrinterSetupWizardSheet::StartResolve( Service
* service
)
1413 OSStatus err
= kNoErr
;
1415 check( service
->serviceRef
== NULL
);
1418 // clean out any queues that were collected during a previous
1422 service
->EmptyQueues();
1425 // now start the new resolve
1428 err
= DNSServiceResolve( &service
->serviceRef
, 0, 0, service
->printer
->name
.c_str(), service
->type
.c_str(), service
->domain
.c_str(), (DNSServiceResolveReply
) OnResolve
, service
);
1429 require_noerr( err
, exit
);
1431 err
= StartOperation( service
->serviceRef
);
1432 require_noerr( err
, exit
);
1435 // If we're not currently resolving, then disable the next button
1436 // and set the cursor to hourglass
1439 if ( !service
->printer
->resolving
)
1441 SetWizardButtons( PSWIZB_BACK
);
1444 SetCursor(m_active
);
1447 service
->printer
->resolving
++;
1456 CPrinterSetupWizardSheet::StopResolve(Printer
* printer
)
1458 OSStatus err
= kNoErr
;
1462 Services::iterator it
;
1464 for ( it
= printer
->services
.begin(); it
!= printer
->services
.end(); it
++ )
1466 if ( (*it
)->serviceRef
)
1468 err
= StopResolve( *it
);
1469 require_noerr( err
, exit
);
1480 CPrinterSetupWizardSheet::StopResolve( Service
* service
)
1484 check( service
->serviceRef
);
1486 err
= StopOperation( service
->serviceRef
);
1487 require_noerr( err
, exit
);
1489 service
->printer
->resolving
--;
1498 CPrinterSetupWizardSheet::StartOperation( DNSServiceRef ref
)
1502 err
= WSAAsyncSelect((SOCKET
) DNSServiceRefSockFD(ref
), m_hWnd
, WM_SOCKET_EVENT
, FD_READ
|FD_CLOSE
);
1503 require_noerr( err
, exit
);
1505 m_serviceRefList
.push_back( ref
);
1514 CPrinterSetupWizardSheet::StopOperation( DNSServiceRef
& ref
)
1516 OSStatus err
= kNoErr
;
1520 m_serviceRefList
.remove( ref
);
1522 if ( IsWindow( m_hWnd
) )
1524 err
= WSAAsyncSelect((SOCKET
) DNSServiceRefSockFD( ref
), m_hWnd
, 0, 0 );
1525 require_noerr( err
, exit
);
1528 DNSServiceRefDeallocate( ref
);
1539 CPrinterSetupWizardSheet::ParseTextRecord( Service
* service
, Queue
* q
, uint16_t inTXTSize
, const char * inTXT
)
1544 // <rdar://problem/3946587> Use TXTRecord APIs declared in dns_sd.h
1546 bool qtotalDefined
= false;
1550 OSStatus err
= kNoErr
;
1552 // <rdar://problem/3987680> Default to queue "lp"
1556 // <rdar://problem/4003710> Default pdl key to be "application/postscript"
1558 q
->pdl
= L
"application/postscript";
1560 if ( ( val
= TXTRecordGetValuePtr( inTXTSize
, inTXT
, "rp", &len
) ) != NULL
)
1562 // Stringize val ( doesn't have trailing '\0' yet )
1564 memcpy( buf
, val
, len
);
1567 err
= UTF8StringToStringObject( buf
, q
->name
);
1568 require_noerr( err
, exit
);
1571 if ( ( val
= TXTRecordGetValuePtr( inTXTSize
, inTXT
, "pdl", &len
) ) != NULL
)
1573 // Stringize val ( doesn't have trailing '\0' yet )
1575 memcpy( buf
, val
, len
);
1578 err
= UTF8StringToStringObject( buf
, q
->pdl
);
1579 require_noerr( err
, exit
);
1582 if ( ( ( val
= TXTRecordGetValuePtr( inTXTSize
, inTXT
, "usb_mfg", &len
) ) != NULL
) ||
1583 ( ( val
= TXTRecordGetValuePtr( inTXTSize
, inTXT
, "usb_manufacturer", &len
) ) != NULL
) )
1585 // Stringize val ( doesn't have trailing '\0' yet )
1587 memcpy( buf
, val
, len
);
1590 err
= UTF8StringToStringObject( buf
, q
->usb_MFG
);
1591 require_noerr( err
, exit
);
1594 if ( ( ( val
= TXTRecordGetValuePtr( inTXTSize
, inTXT
, "usb_mdl", &len
) ) != NULL
) ||
1595 ( ( val
= TXTRecordGetValuePtr( inTXTSize
, inTXT
, "usb_model", &len
) ) != NULL
) )
1597 // Stringize val ( doesn't have trailing '\0' yet )
1599 memcpy( buf
, val
, len
);
1602 err
= UTF8StringToStringObject( buf
, q
->usb_MDL
);
1603 require_noerr( err
, exit
);
1606 if ( ( val
= TXTRecordGetValuePtr( inTXTSize
, inTXT
, "ty", &len
) ) != NULL
)
1608 // Stringize val ( doesn't have trailing '\0' yet )
1610 memcpy( buf
, val
, len
);
1613 err
= UTF8StringToStringObject( buf
, q
->description
);
1614 require_noerr( err
, exit
);
1617 if ( ( val
= TXTRecordGetValuePtr( inTXTSize
, inTXT
, "product", &len
) ) != NULL
)
1619 // Stringize val ( doesn't have trailing '\0' yet )
1621 memcpy( buf
, val
, len
);
1624 err
= UTF8StringToStringObject( buf
, q
->product
);
1625 require_noerr( err
, exit
);
1628 if ( ( val
= TXTRecordGetValuePtr( inTXTSize
, inTXT
, "note", &len
) ) != NULL
)
1630 // Stringize val ( doesn't have trailing '\0' yet )
1632 memcpy( buf
, val
, len
);
1635 err
= UTF8StringToStringObject( buf
, q
->location
);
1636 require_noerr( err
, exit
);
1639 if ( ( val
= TXTRecordGetValuePtr( inTXTSize
, inTXT
, "qtotal", &len
) ) != NULL
)
1641 // Stringize val ( doesn't have trailing '\0' yet )
1643 memcpy( buf
, val
, len
);
1646 service
->qtotal
= (unsigned short) atoi( buf
);
1647 qtotalDefined
= true;
1650 if ( ( val
= TXTRecordGetValuePtr( inTXTSize
, inTXT
, "priority", &len
) ) != NULL
)
1652 // Stringize val ( doesn't have trailing '\0' yet )
1654 memcpy( buf
, val
, len
);
1657 q
->priority
= atoi( buf
);
1660 // <rdar://problem/4124524> Was this printer discovered via OS X Printer Sharing?
1662 if ( TXTRecordContainsKey( inTXTSize
, inTXT
, "printer-state" ) || TXTRecordContainsKey( inTXTSize
, inTXT
, "printer-type" ) )
1664 service
->printer
->isSharedFromOSX
= true;
1669 // The following code is to fix a problem with older HP
1670 // printers that don't include "qtotal" in their text
1671 // record. We'll check to see if the q->name is "TEXT"
1672 // and if so, we're going to modify it to be "lp" so
1673 // that we don't use the wrong queue
1675 if ( !err
&& !qtotalDefined
&& ( q
->name
== L
"TEXT" ) )
1685 CPrinterSetupWizardSheet::Lookup(const char * inName
)
1689 Printer
* printer
= NULL
;
1690 Printers::iterator it
;
1692 for ( it
= m_printers
.begin(); it
!= m_printers
.end(); it
++ )
1694 if ( (*it
)->name
== inName
)
1706 CPrinterSetupWizardSheet::OrderServiceFunc( const Service
* a
, const Service
* b
)
1710 q1
= (a
->queues
.size() > 0) ? a
->queues
.front() : NULL
;
1712 q2
= (b
->queues
.size() > 0) ? b
->queues
.front() : NULL
;
1718 else if ( q1
&& !q2
)
1722 else if ( !q1
&& q2
)
1726 else if ( q1
->priority
< q2
->priority
)
1730 else if ( q1
->priority
> q2
->priority
)
1734 else if ( ( a
->type
== kPDLServiceType
) || ( ( a
->type
== kLPRServiceType
) && ( b
->type
== kIPPServiceType
) ) )
1746 CPrinterSetupWizardSheet::OrderQueueFunc( const Queue
* q1
, const Queue
* q2
)
1748 return ( q1
->priority
<= q2
->priority
) ? true : false;