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.30 2005/04/13 17:46:22 shersche
27 <rdar://problem/4082122> Generic PCL not selected when printers advertise multiple text records
29 Revision 1.29 2005/02/14 20:48:37 shersche
30 <rdar://problem/4003710> Default pdl key to "application/postscript"
32 Revision 1.28 2005/02/14 20:37:53 shersche
33 <rdar://problem/4003944> Populate comment field with the model name that users see in the wizard UI.
35 Revision 1.27 2005/02/09 05:04:03 shersche
36 <rdar://problem/3946587> Use TXTRecordGetValuePtr() API in ParseTextRecord
38 Revision 1.26 2005/02/08 21:45:06 shersche
39 <rdar://problem/3947490> Default to Generic PostScript or PCL if unable to match driver
41 Revision 1.25 2005/02/08 18:54:17 shersche
42 <rdar://problem/3987680> Default queue name is "lp" when rp key is not specified.
44 Revision 1.24 2005/02/01 02:15:55 shersche
45 <rdar://problem/3946587> Use TXTRecord parsing APIs in ParseTextRecord
47 Revision 1.23 2005/01/31 23:54:30 shersche
48 <rdar://problem/3947508> Start browsing when printer wizard starts. Move browsing logic from CSecondPage object to CPrinterSetupWizardSheet object.
50 Revision 1.22 2005/01/25 18:49:43 shersche
51 Get icon resources from resource DLL
53 Revision 1.21 2005/01/10 01:09:32 shersche
54 Use the "note" key to populate pLocation field when setting up printer
56 Revision 1.20 2005/01/03 19:05:01 shersche
57 Store pointer to instance of wizard sheet so that print driver install thread sends a window message to the correct window
59 Revision 1.19 2004/12/31 07:23:53 shersche
60 Don't modify the button setting in SetSelectedPrinter()
62 Revision 1.18 2004/12/29 18:53:38 shersche
63 <rdar://problem/3725106>
64 <rdar://problem/3737413> Added support for LPR and IPP protocols as well as support for obtaining multiple text records. Reorganized and simplified codebase.
65 Bug #: 3725106, 3737413
67 Revision 1.17 2004/10/12 18:02:53 shersche
68 <rdar://problem/3764873> Escape '/', '@', '"' characters in printui command.
71 Revision 1.16 2004/09/13 21:27:22 shersche
72 <rdar://problem/3796483> Pass the moreComing flag to OnAddPrinter and OnRemovePrinter callbacks
75 Revision 1.15 2004/09/11 05:59:06 shersche
76 <rdar://problem/3785766> Fix code that generates unique printer names based on currently installed printers
79 Revision 1.14 2004/09/02 01:57:58 cheshire
80 <rdar://problem/3783611> Fix incorrect testing of MoreComing flag
82 Revision 1.13 2004/07/26 21:06:29 shersche
83 <rdar://problem/3739200> Removing trailing '.' in hostname
86 Revision 1.12 2004/07/13 21:24:23 rpantos
87 Fix for <rdar://problem/3701120>.
89 Revision 1.11 2004/06/28 00:51:47 shersche
90 Move call to EnumPrinters out of browse callback into standalone function
92 Revision 1.10 2004/06/27 23:06:47 shersche
93 code cleanup, make sure EnumPrinters returns non-zero value
95 Revision 1.9 2004/06/27 15:49:31 shersche
96 clean up some cruft in the printer browsing code
98 Revision 1.8 2004/06/27 08:04:51 shersche
99 copy selected printer to prevent printer being deleted out from under
101 Revision 1.7 2004/06/26 23:27:12 shersche
102 support for installing multiple printers of the same name
104 Revision 1.6 2004/06/26 21:22:39 shersche
105 handle spaces in file names
107 Revision 1.5 2004/06/26 03:19:57 shersche
108 clean up warning messages
110 Submitted by: herscher
112 Revision 1.4 2004/06/25 02:26:52 shersche
113 Normalize key fields in text record entries
114 Submitted by: herscher
116 Revision 1.3 2004/06/24 20:12:07 shersche
118 Submitted by: herscher
120 Revision 1.2 2004/06/23 17:58:21 shersche
121 <rdar://problem/3701837> eliminated memory leaks on exit
122 <rdar://problem/3701926> installation of a printer that is already installed results in a no-op
123 Bug #: 3701837, 3701926
124 Submitted by: herscher
126 Revision 1.1 2004/06/18 04:36:57 rpantos
133 #include "PrinterSetupWizardApp.h"
134 #include "PrinterSetupWizardSheet.h"
135 #include "CommonServices.h"
136 #include "DebugServices.h"
137 #include "WinServices.h"
139 #include <winspool.h>
144 #pragma warning(disable:4702)
147 #if( !TARGET_OS_WINDOWS_CE )
148 # include <mswsock.h>
149 # include <process.h>
154 #define WM_SOCKET_EVENT ( WM_USER + 0x100 )
155 #define WM_PROCESS_EVENT ( WM_USER + 0x101 )
158 // CPrinterSetupWizardSheet
159 CPrinterSetupWizardSheet
* CPrinterSetupWizardSheet::m_self
;
161 IMPLEMENT_DYNAMIC(CPrinterSetupWizardSheet
, CPropertySheet
)
162 CPrinterSetupWizardSheet::CPrinterSetupWizardSheet(UINT nIDCaption
, CWnd
* pParentWnd
, UINT iSelectPage
)
163 :CPropertySheet(nIDCaption
, pParentWnd
, iSelectPage
),
164 m_selectedPrinter(NULL
),
165 m_driverThreadExitCode( 0 ),
166 m_driverThreadFinished( false ),
167 m_pdlBrowser( NULL
),
168 m_ippBrowser( NULL
),
171 m_arrow
= LoadCursor(0, IDC_ARROW
);
172 m_wait
= LoadCursor(0, IDC_APPSTARTING
);
182 CPrinterSetupWizardSheet::~CPrinterSetupWizardSheet()
186 while ( m_printers
.size() > 0 )
188 printer
= m_printers
.front();
189 m_printers
.pop_front();
198 // ------------------------------------------------------
199 // SetSelectedPrinter
201 // Manages setting a printer as the printer to install. Stops
202 // any pending resolves.
205 CPrinterSetupWizardSheet::SetSelectedPrinter(Printer
* printer
)
207 check( !printer
|| ( printer
!= m_selectedPrinter
) );
209 m_selectedPrinter
= printer
;
214 CPrinterSetupWizardSheet::LoadPrinterNames()
220 // rdar://problem/3701926 - Printer can't be installed twice
222 // First thing we want to do is make sure the printer isn't already installed.
223 // If the printer name is found, we'll try and rename it until we
224 // find a unique name
226 DWORD dwNeeded
= 0, dwNumPrinters
= 0;
228 BOOL ok
= EnumPrinters(PRINTER_ENUM_LOCAL
, NULL
, 4, NULL
, 0, &dwNeeded
, &dwNumPrinters
);
229 err
= translate_errno( ok
, errno_compat(), kUnknownErr
);
231 if ((err
== ERROR_INSUFFICIENT_BUFFER
) && (dwNeeded
> 0))
235 buffer
= new unsigned char[dwNeeded
];
242 require_action( buffer
, exit
, kNoMemoryErr
);
243 ok
= EnumPrinters(PRINTER_ENUM_LOCAL
, NULL
, 4, buffer
, dwNeeded
, &dwNeeded
, &dwNumPrinters
);
244 err
= translate_errno( ok
, errno_compat(), kUnknownErr
);
245 require_noerr( err
, exit
);
247 for (DWORD index
= 0; index
< dwNumPrinters
; index
++)
249 PRINTER_INFO_4
* lppi4
= (PRINTER_INFO_4
*) (buffer
+ index
* sizeof(PRINTER_INFO_4
));
251 m_printerNames
[lppi4
->pPrinterName
] = lppi4
->pPrinterName
;
267 // ------------------------------------------------------
270 // Installs a printer with Windows.
272 // NOTE: this works one of two ways, depending on whether
273 // there are drivers already installed for this printer.
274 // If there are, then we can just create a port with XcvData,
275 // and then call AddPrinter. If not, we use the printui.dll
276 // to install the printer. Actually installing drivers that
277 // are not currently installed is painful, and it's much
278 // easier and less error prone to just let printui.dll do
279 // the hard work for us.
283 CPrinterSetupWizardSheet::InstallPrinter(Printer
* printer
)
289 service
= printer
->services
.front();
293 // if the driver isn't installed, then install it
295 if ( !printer
->driverInstalled
)
301 m_driverThreadFinished
= false;
306 hThread
= (HANDLE
) _beginthreadex_compat( NULL
, 0, InstallDriverThread
, printer
, 0, &threadID
);
307 err
= translate_errno( hThread
, (OSStatus
) GetLastError(), kUnknownErr
);
308 require_noerr( err
, exit
);
313 while (!m_driverThreadFinished
)
317 GetMessage( &msg
, m_hWnd
, 0, 0 );
318 TranslateMessage(&msg
);
319 DispatchMessage(&msg
);
323 // Wait until child process exits.
325 dwResult
= WaitForSingleObject( hThread
, INFINITE
);
326 err
= translate_errno( dwResult
== WAIT_OBJECT_0
, errno_compat(), err
= kUnknownErr
);
327 require_noerr( err
, exit
);
330 // check the return value of thread
332 require_noerr( m_driverThreadExitCode
, exit
);
335 // now we know that the driver was successfully installed
337 printer
->driverInstalled
= true;
340 if ( service
->type
== kPDLServiceType
)
342 err
= InstallPrinterPDLAndLPR( printer
, service
, PROTOCOL_RAWTCP_TYPE
);
343 require_noerr( err
, exit
);
345 else if ( service
->type
== kLPRServiceType
)
347 err
= InstallPrinterPDLAndLPR( printer
, service
, PROTOCOL_LPR_TYPE
);
348 require_noerr( err
, exit
);
350 else if ( service
->type
== kIPPServiceType
)
352 err
= InstallPrinterIPP( printer
, service
);
353 require_noerr( err
, exit
);
358 require_noerr( err
, exit
);
361 printer
->installed
= true;
364 // if the user specified a default printer, set it
368 ok
= SetDefaultPrinter( printer
->actualName
);
369 err
= translate_errno( ok
, errno_compat(), err
= kUnknownErr
);
370 require_noerr( err
, exit
);
380 CPrinterSetupWizardSheet::InstallPrinterPDLAndLPR(Printer
* printer
, Service
* service
, DWORD protocol
)
382 PRINTER_DEFAULTS printerDefaults
= { NULL
, NULL
, SERVER_ACCESS_ADMINISTER
};
384 DWORD cbInputData
= 100;
385 PBYTE pOutputData
= NULL
;
386 DWORD cbOutputNeeded
= 0;
387 PORT_DATA_1 portData
;
388 PRINTER_INFO_2 pInfo
;
390 HANDLE hPrinter
= NULL
;
395 check(printer
!= NULL
);
396 check(printer
->installed
== false);
398 q
= service
->queues
.front();
401 ok
= OpenPrinter(L
",XcvMonitor Standard TCP/IP Port", &hXcv
, &printerDefaults
);
402 err
= translate_errno( ok
, errno_compat(), kUnknownErr
);
403 require_noerr( err
, exit
);
406 // BUGBUG: MSDN said this is not required, but my experience shows it is required
410 pOutputData
= new BYTE
[cbInputData
];
417 require_action( pOutputData
, exit
, err
= kNoMemoryErr
);
422 ZeroMemory(&portData
, sizeof(PORT_DATA_1
));
423 wcscpy(portData
.sztPortName
, printer
->portName
);
425 portData
.dwPortNumber
= service
->portNumber
;
426 portData
.dwVersion
= 1;
428 portData
.dwProtocol
= protocol
;
429 portData
.cbSize
= sizeof PORT_DATA_1
;
430 portData
.dwReserved
= 0L;
432 wcscpy(portData
.sztQueue
, q
->name
);
433 wcscpy(portData
.sztIPAddress
, service
->hostname
);
434 wcscpy(portData
.sztHostAddress
, service
->hostname
);
436 ok
= XcvData(hXcv
, L
"AddPort", (PBYTE
) &portData
, sizeof(PORT_DATA_1
), pOutputData
, cbInputData
, &cbOutputNeeded
, &dwStatus
);
437 err
= translate_errno( ok
, errno_compat(), kUnknownErr
);
438 require_noerr( err
, exit
);
443 ZeroMemory(&pInfo
, sizeof(pInfo
));
445 pInfo
.pPrinterName
= printer
->actualName
.GetBuffer();
446 pInfo
.pServerName
= NULL
;
447 pInfo
.pShareName
= NULL
;
448 pInfo
.pPortName
= printer
->portName
.GetBuffer();
449 pInfo
.pDriverName
= printer
->modelName
.GetBuffer();
450 pInfo
.pComment
= printer
->displayModelName
.GetBuffer();
451 pInfo
.pLocation
= q
->location
.GetBuffer();
452 pInfo
.pDevMode
= NULL
;
453 pInfo
.pDevMode
= NULL
;
454 pInfo
.pSepFile
= L
"";
455 pInfo
.pPrintProcessor
= L
"winprint";
456 pInfo
.pDatatype
= L
"RAW";
457 pInfo
.pParameters
= L
"";
458 pInfo
.pSecurityDescriptor
= NULL
;
459 pInfo
.Attributes
= PRINTER_ATTRIBUTE_QUEUED
;
461 pInfo
.DefaultPriority
= 0;
465 hPrinter
= AddPrinter(NULL
, 2, (LPBYTE
) &pInfo
);
466 err
= translate_errno( hPrinter
, errno_compat(), kUnknownErr
);
467 require_noerr( err
, exit
);
471 if (hPrinter
!= NULL
)
473 ClosePrinter(hPrinter
);
481 if (pOutputData
!= NULL
)
483 delete [] pOutputData
;
491 CPrinterSetupWizardSheet::InstallPrinterIPP(Printer
* printer
, Service
* service
)
493 DEBUG_UNUSED( service
);
495 Queue
* q
= service
->SelectedQueue();
496 HANDLE hPrinter
= NULL
;
497 PRINTER_INFO_2 pInfo
;
505 ZeroMemory(&pInfo
, sizeof(PRINTER_INFO_2
));
507 pInfo
.pPrinterName
= printer
->actualName
.GetBuffer();
508 pInfo
.pPortName
= printer
->portName
.GetBuffer();
509 pInfo
.pDriverName
= printer
->modelName
.GetBuffer();
510 pInfo
.pPrintProcessor
= L
"winprint";
511 pInfo
.pLocation
= q
->location
.GetBuffer();
512 pInfo
.pComment
= printer
->displayModelName
.GetBuffer();
513 pInfo
.Attributes
= PRINTER_ATTRIBUTE_NETWORK
| PRINTER_ATTRIBUTE_LOCAL
;
515 hPrinter
= AddPrinter(NULL
, 2, (LPBYTE
)&pInfo
);
516 err
= translate_errno( hPrinter
, errno_compat(), kUnknownErr
);
517 require_noerr( err
, exit
);
521 if ( hPrinter
!= NULL
)
523 ClosePrinter(hPrinter
);
530 BEGIN_MESSAGE_MAP(CPrinterSetupWizardSheet
, CPropertySheet
)
531 ON_MESSAGE( WM_SOCKET_EVENT
, OnSocketEvent
)
532 ON_MESSAGE( WM_PROCESS_EVENT
, OnProcessEvent
)
538 // ------------------------------------------------------
541 // Traps when the user hits Finish
543 BOOL
CPrinterSetupWizardSheet::OnCommand(WPARAM wParam
, LPARAM lParam
)
546 // Check if this is OK
548 if (wParam
== ID_WIZFINISH
) // If OK is hit...
553 return CPropertySheet::OnCommand(wParam
, lParam
);
557 // ------------------------------------------------------
560 // Initializes this Dialog object.
562 BOOL
CPrinterSetupWizardSheet::OnInitDialog()
566 CPropertySheet::OnInitDialog();
569 require_noerr( err
, exit
);
577 if ( err
== kDNSServiceErr_Firewall
)
579 CString text
, caption
;
581 text
.LoadString( IDS_FIREWALL
);
582 caption
.LoadString( IDS_FIREWALL_CAPTION
);
584 MessageBox(text
, caption
, MB_OK
|MB_ICONEXCLAMATION
);
588 CPrinterSetupWizardSheet::WizardException exc
;
590 exc
.text
.LoadString( IDS_NO_MDNSRESPONDER_SERVICE_TEXT
);
591 exc
.caption
.LoadString( IDS_ERROR_CAPTION
);
601 // ------------------------------------------------------
604 // This is called when Windows wants to know what cursor
605 // to display. So we tell it.
608 CPrinterSetupWizardSheet::OnSetCursor(CWnd
* pWnd
, UINT nHitTest
, UINT message
)
611 DEBUG_UNUSED(nHitTest
);
612 DEBUG_UNUSED(message
);
619 // ------------------------------------------------------
622 // This is not fully implemented yet.
626 CPrinterSetupWizardSheet::OnContextMenu(CWnd
* pWnd
, CPoint pos
)
637 // ------------------------------------------------------
640 // This is called when the user hits the "Finish" button
643 CPrinterSetupWizardSheet::OnOK()
645 check ( m_selectedPrinter
!= NULL
);
647 SetWizardButtons( PSWIZB_DISABLEDFINISH
);
649 if ( InstallPrinter( m_selectedPrinter
) != kNoErr
)
654 caption
.LoadString(IDS_INSTALL_ERROR_CAPTION
);
655 message
.LoadString(IDS_INSTALL_ERROR_MESSAGE
);
657 MessageBox(message
, caption
, MB_OK
|MB_ICONEXCLAMATION
);
664 // CPrinterSetupWizardSheet message handlers
666 void CPrinterSetupWizardSheet::Init(void)
669 AddPage(&m_pgSecond
);
671 AddPage(&m_pgFourth
);
673 m_psh
.dwFlags
&= (~PSH_HASHELP
);
675 m_psh
.dwFlags
|= PSH_WIZARD97
|PSH_WATERMARK
|PSH_HEADER
;
676 m_psh
.pszbmWatermark
= MAKEINTRESOURCE(IDB_WATERMARK
);
677 m_psh
.pszbmHeader
= MAKEINTRESOURCE(IDB_BANNER_ICON
);
679 m_psh
.hInstance
= GetNonLocalizedResources();
686 CPrinterSetupWizardSheet::OnSocketEvent(WPARAM inWParam
, LPARAM inLParam
)
688 if (WSAGETSELECTERROR(inLParam
) && !(HIWORD(inLParam
)))
690 dlog( kDebugLevelError
, "OnServiceEvent: window error\n" );
694 SOCKET sock
= (SOCKET
) inWParam
;
697 ServiceRefList::iterator begin
= m_serviceRefList
.begin();
698 ServiceRefList::iterator end
= m_serviceRefList
.end();
702 DNSServiceRef ref
= *begin
++;
706 if ((SOCKET
) DNSServiceRefSockFD(ref
) == sock
)
708 DNSServiceProcessResult(ref
);
719 CPrinterSetupWizardSheet::OnProcessEvent(WPARAM inWParam
, LPARAM inLParam
)
721 DEBUG_UNUSED(inLParam
);
723 m_driverThreadExitCode
= (DWORD
) inWParam
;
724 m_driverThreadFinished
= true;
731 CPrinterSetupWizardSheet::InstallDriverThread( LPVOID inParam
)
733 Printer
* printer
= (Printer
*) inParam
;
738 PROCESS_INFORMATION pi
;
745 // because we're calling endthreadex(), C++ objects won't be cleaned up
746 // correctly. we'll nest the CString 'command' inside a block so
747 // that it's destructor will be invoked.
752 ZeroMemory( &si
, sizeof(si
) );
754 ZeroMemory( &pi
, sizeof(pi
) );
756 command
.Format(L
"rundll32.exe printui.dll,PrintUIEntry /ia /m \"%s\" /f \"%s\"", (LPCTSTR
) printer
->modelName
, (LPCTSTR
) printer
->infFileName
);
758 ok
= CreateProcess(NULL
, command
.GetBuffer(), NULL
, NULL
, FALSE
, 0, NULL
, NULL
, &si
, &pi
);
759 err
= translate_errno( ok
, errno_compat(), kUnknownErr
);
760 require_noerr( err
, exit
);
762 dwResult
= WaitForSingleObject( pi
.hProcess
, INFINITE
);
763 translate_errno( dwResult
== WAIT_OBJECT_0
, errno_compat(), err
= kUnknownErr
);
764 require_noerr( err
, exit
);
766 ok
= GetExitCodeProcess( pi
.hProcess
, &exitCode
);
767 err
= translate_errno( ok
, errno_compat(), kUnknownErr
);
768 require_noerr( err
, exit
);
774 // Close process and thread handles.
778 CloseHandle( pi
.hProcess
);
783 CloseHandle( pi
.hThread
);
787 // alert the main thread
789 m_self
->PostMessage( WM_PROCESS_EVENT
, err
, exitCode
);
791 _endthreadex_compat( 0 );
798 CPrinterSetupWizardSheet::OnBrowse(
800 DNSServiceFlags inFlags
,
801 uint32_t inInterfaceIndex
,
802 DNSServiceErrorType inErrorCode
,
805 const char * inDomain
,
810 CPrinterSetupWizardSheet
* self
;
811 bool moreComing
= (bool) (inFlags
& kDNSServiceFlagsMoreComing
);
812 CPropertyPage
* active
;
813 Printer
* printer
= NULL
;
814 Service
* service
= NULL
;
815 OSStatus err
= kNoErr
;
817 require_noerr( inErrorCode
, exit
);
819 self
= reinterpret_cast <CPrinterSetupWizardSheet
*>( inContext
);
820 require_quiet( self
, exit
);
822 active
= self
->GetActivePage();
823 require_quiet( active
, exit
);
825 // Have we seen this printer before?
827 printer
= self
->Lookup( inName
);
831 service
= printer
->LookupService( inType
);
834 if ( inFlags
& kDNSServiceFlagsAdd
)
838 // If not, then create a new one
840 printer
= self
->OnAddPrinter( inInterfaceIndex
, inName
, inType
, inDomain
, moreComing
);
841 require_action( printer
, exit
, err
= kUnknownErr
);
846 err
= self
->OnAddService( printer
, inInterfaceIndex
, inName
, inType
, inDomain
);
847 require_noerr( err
, exit
);
858 err
= self
->OnRemoveService( service
);
859 require_noerr( err
, exit
);
861 if ( printer
->services
.size() == 0 )
863 err
= self
->OnRemovePrinter( printer
, moreComing
);
864 require_noerr( err
, exit
);
875 CPrinterSetupWizardSheet::OnResolve(
877 DNSServiceFlags inFlags
,
878 uint32_t inInterfaceIndex
,
879 DNSServiceErrorType inErrorCode
,
880 const char * inFullName
,
881 const char * inHostName
,
887 DEBUG_UNUSED(inFullName
);
888 DEBUG_UNUSED(inInterfaceIndex
);
889 DEBUG_UNUSED(inFlags
);
892 CPrinterSetupWizardSheet
* self
;
898 require_noerr( inErrorCode
, exit
);
900 service
= reinterpret_cast<Service
*>( inContext
);
901 require_quiet( service
, exit
);
903 check( service
->refs
!= 0 );
905 self
= service
->printer
->window
;
906 require_quiet( self
, exit
);
908 err
= self
->StopOperation( service
->serviceRef
);
909 require_noerr( err
, exit
);
912 // hold on to the hostname...
914 err
= UTF8StringToStringObject( inHostName
, service
->hostname
);
915 require_noerr( err
, exit
);
918 // <rdar://problem/3739200> remove the trailing dot on hostname
920 idx
= service
->hostname
.ReverseFind('.');
922 if ((idx
> 1) && ((service
->hostname
.GetLength() - 1) == idx
))
924 service
->hostname
.Delete(idx
, 1);
928 // hold on to the port
930 service
->portNumber
= ntohs(inPort
);
932 if ( service
->qtotal
== 1 )
935 // create a new queue
946 require_action( q
, exit
, err
= E_OUTOFMEMORY
);
949 // parse the text record.
952 err
= self
->ParseTextRecord( service
, q
, inTXTSize
, inTXT
);
953 require_noerr( err
, exit
);
955 service
->queues
.push_back( q
);
958 // we've completely resolved this service
961 self
->OnResolveService( service
);
966 // if qtotal is more than 1, then we need to get additional
967 // text records. if not, then this service is considered
971 err
= DNSServiceQueryRecord(&service
->serviceRef
, 0, inInterfaceIndex
, inFullName
, kDNSServiceType_TXT
, kDNSServiceClass_IN
, OnQuery
, (void*) service
);
972 require_noerr( err
, exit
);
974 err
= self
->StartOperation( service
->serviceRef
);
975 require_noerr( err
, exit
);
985 CPrinterSetupWizardSheet::OnQuery(
987 DNSServiceFlags inFlags
,
988 uint32_t inInterfaceIndex
,
989 DNSServiceErrorType inErrorCode
,
990 const char * inFullName
,
994 const void * inRData
,
998 DEBUG_UNUSED( inTTL
);
999 DEBUG_UNUSED( inRRClass
);
1000 DEBUG_UNUSED( inRRType
);
1001 DEBUG_UNUSED( inFullName
);
1002 DEBUG_UNUSED( inInterfaceIndex
);
1003 DEBUG_UNUSED( inRef
);
1005 Service
* service
= NULL
;
1007 CPrinterSetupWizardSheet
* self
;
1008 OSStatus err
= kNoErr
;
1010 require_noerr( inErrorCode
, exit
);
1012 service
= reinterpret_cast<Service
*>( inContext
);
1013 require_quiet( service
, exit
);
1015 self
= service
->printer
->window
;
1016 require_quiet( self
, exit
);
1018 if ( ( inFlags
& kDNSServiceFlagsAdd
) && ( inRDLen
> 0 ) && ( inRData
!= NULL
) )
1020 const char * inTXT
= ( const char * ) inRData
;
1023 // create a new queue
1034 require_action( q
, exit
, err
= E_OUTOFMEMORY
);
1036 err
= service
->printer
->window
->ParseTextRecord( service
, q
, inRDLen
, inTXT
);
1037 require_noerr( err
, exit
);
1043 service
->queues
.push_back( q
);
1045 if ( service
->queues
.size() == service
->qtotal
)
1048 // else if moreComing is not set, then we're going
1049 // to assume that we're done
1052 self
->StopOperation( service
->serviceRef
);
1058 service
->queues
.sort( OrderQueueFunc
);
1061 // we've completely resolved this service
1064 self
->OnResolveService( service
);
1070 if ( err
&& service
&& ( service
->serviceRef
!= NULL
) )
1072 service
->printer
->window
->StopOperation( service
->serviceRef
);
1080 CPrinterSetupWizardSheet::OnAddPrinter(
1081 uint32_t inInterfaceIndex
,
1082 const char * inName
,
1083 const char * inType
,
1084 const char * inDomain
,
1087 Printer
* printer
= NULL
;
1088 DWORD printerNameCount
;
1091 DEBUG_UNUSED( inInterfaceIndex
);
1092 DEBUG_UNUSED( inType
);
1093 DEBUG_UNUSED( inDomain
);
1097 printer
= new Printer
;
1104 require_action( printer
, exit
, err
= E_OUTOFMEMORY
);
1106 printer
->window
= this;
1107 printer
->name
= inName
;
1109 err
= UTF8StringToStringObject(inName
, printer
->displayName
);
1111 printer
->actualName
= printer
->displayName
;
1112 printer
->installed
= false;
1113 printer
->deflt
= false;
1114 printer
->resolving
= 0;
1116 // Compare this name against printers that are already installed
1117 // to avoid name clashes. Rename as necessary
1118 // to come up with a unique name.
1120 printerNameCount
= 2;
1124 CPrinterSetupWizardSheet::PrinterNameMap::iterator it
;
1126 it
= m_printerNames
.find(printer
->actualName
);
1128 if (it
!= m_printerNames
.end())
1130 printer
->actualName
.Format(L
"%s (%d)", printer
->displayName
, printerNameCount
);
1140 m_printers
.push_back( printer
);
1142 if ( GetActivePage() == &m_pgSecond
)
1144 m_pgSecond
.OnAddPrinter( printer
, moreComing
);
1154 CPrinterSetupWizardSheet::OnAddService(
1156 uint32_t inInterfaceIndex
,
1157 const char * inName
,
1158 const char * inType
,
1159 const char * inDomain
)
1161 Service
* service
= NULL
;
1162 OSStatus err
= kNoErr
;
1164 DEBUG_UNUSED( inName
);
1165 DEBUG_UNUSED( inDomain
);
1169 service
= new Service
;
1176 require_action( service
, exit
, err
= E_OUTOFMEMORY
);
1178 service
->printer
= printer
;
1179 service
->ifi
= inInterfaceIndex
;
1180 service
->type
= inType
;
1181 service
->domain
= inDomain
;
1182 service
->qtotal
= 1;
1184 service
->serviceRef
= NULL
;
1186 printer
->services
.push_back( service
);
1189 // if the printer is selected, then we'll want to start a
1190 // resolve on this guy
1193 if ( printer
== m_selectedPrinter
)
1195 StartResolve( service
);
1205 CPrinterSetupWizardSheet::OnRemovePrinter( Printer
* printer
, bool moreComing
)
1207 CPropertyPage
* active
= GetActivePage();
1208 OSStatus err
= kNoErr
;
1210 if ( active
== &m_pgSecond
)
1212 m_pgSecond
.OnRemovePrinter( printer
, moreComing
);
1215 m_printers
.remove( printer
);
1217 if ( m_selectedPrinter
== printer
)
1219 m_selectedPrinter
= NULL
;
1221 if ( ( active
== &m_pgThird
) || ( active
== &m_pgFourth
) )
1226 caption
.LoadString( IDS_ERROR_CAPTION
);
1227 message
.LoadString( IDS_PRINTER_UNAVAILABLE
);
1229 MessageBox(message
, caption
, MB_OK
|MB_ICONEXCLAMATION
);
1231 SetActivePage( &m_pgSecond
);
1242 CPrinterSetupWizardSheet::OnRemoveService( Service
* service
)
1244 OSStatus err
= kNoErr
;
1246 if ( service
&& ( --service
->refs
== 0 ) )
1248 if ( service
->serviceRef
!= NULL
)
1250 err
= StopResolve( service
);
1251 require_noerr( err
, exit
);
1254 service
->printer
->services
.remove( service
);
1266 CPrinterSetupWizardSheet::OnResolveService( Service
* service
)
1268 // Make sure that the active page is page 2
1270 check( GetActivePage() == &m_pgSecond
);
1272 if ( !--service
->printer
->resolving
)
1274 // sort the services now. we want the service that
1275 // has the highest priority queue to be first in
1278 service
->printer
->services
.sort( OrderServiceFunc
);
1280 // Now we can hit next
1282 SetWizardButtons( PSWIZB_BACK
|PSWIZB_NEXT
);
1288 // And tell page 2 about it
1290 m_pgSecond
.OnResolveService( service
);
1296 CPrinterSetupWizardSheet::StartBrowse()
1301 // setup the DNS-SD browsing
1303 err
= DNSServiceBrowse( &m_pdlBrowser
, 0, 0, kPDLServiceType
, NULL
, OnBrowse
, this );
1304 require_noerr( err
, exit
);
1306 err
= StartOperation( m_pdlBrowser
);
1307 require_noerr( err
, exit
);
1309 err
= DNSServiceBrowse( &m_lprBrowser
, 0, 0, kLPRServiceType
, NULL
, OnBrowse
, this );
1310 require_noerr( err
, exit
);
1312 err
= StartOperation( m_lprBrowser
);
1313 require_noerr( err
, exit
);
1315 err
= DNSServiceBrowse( &m_ippBrowser
, 0, 0, kIPPServiceType
, NULL
, OnBrowse
, this );
1316 require_noerr( err
, exit
);
1318 err
= StartOperation( m_ippBrowser
);
1319 require_noerr( err
, exit
);
1328 CPrinterSetupWizardSheet::StopBrowse()
1332 err
= StopOperation( m_pdlBrowser
);
1333 require_noerr( err
, exit
);
1335 err
= StopOperation( m_lprBrowser
);
1336 require_noerr( err
, exit
);
1338 err
= StopOperation( m_ippBrowser
);
1339 require_noerr( err
, exit
);
1341 while ( m_printers
.size() > 0 )
1343 Printer
* printer
= m_printers
.front();
1345 m_printers
.pop_front();
1347 if ( printer
->resolving
)
1349 StopResolve( printer
);
1362 CPrinterSetupWizardSheet::StartResolve( Printer
* printer
)
1364 OSStatus err
= kNoErr
;
1365 Services::iterator it
;
1369 for ( it
= printer
->services
.begin(); it
!= printer
->services
.end(); it
++ )
1371 if ( (*it
)->serviceRef
== NULL
)
1373 err
= StartResolve( *it
);
1374 require_noerr( err
, exit
);
1378 m_selectedPrinter
= printer
;
1387 CPrinterSetupWizardSheet::StartResolve( Service
* service
)
1389 OSStatus err
= kNoErr
;
1391 check( service
->serviceRef
== NULL
);
1394 // clean out any queues that were collected during a previous
1398 service
->EmptyQueues();
1401 // now start the new resolve
1404 err
= DNSServiceResolve( &service
->serviceRef
, 0, 0, service
->printer
->name
.c_str(), service
->type
.c_str(), service
->domain
.c_str(), (DNSServiceResolveReply
) OnResolve
, service
);
1405 require_noerr( err
, exit
);
1407 err
= StartOperation( service
->serviceRef
);
1408 require_noerr( err
, exit
);
1411 // If we're not currently resolving, then disable the next button
1412 // and set the cursor to hourglass
1415 if ( !service
->printer
->resolving
)
1417 SetWizardButtons( PSWIZB_BACK
);
1420 SetCursor(m_active
);
1423 service
->printer
->resolving
++;
1432 CPrinterSetupWizardSheet::StopResolve(Printer
* printer
)
1434 OSStatus err
= kNoErr
;
1438 Services::iterator it
;
1440 for ( it
= printer
->services
.begin(); it
!= printer
->services
.end(); it
++ )
1442 if ( (*it
)->serviceRef
)
1444 err
= StopResolve( *it
);
1445 require_noerr( err
, exit
);
1456 CPrinterSetupWizardSheet::StopResolve( Service
* service
)
1460 check( service
->serviceRef
);
1462 err
= StopOperation( service
->serviceRef
);
1463 require_noerr( err
, exit
);
1465 service
->printer
->resolving
--;
1474 CPrinterSetupWizardSheet::StartOperation( DNSServiceRef ref
)
1478 err
= WSAAsyncSelect((SOCKET
) DNSServiceRefSockFD(ref
), m_hWnd
, WM_SOCKET_EVENT
, FD_READ
|FD_CLOSE
);
1479 require_noerr( err
, exit
);
1481 m_serviceRefList
.push_back( ref
);
1490 CPrinterSetupWizardSheet::StopOperation( DNSServiceRef
& ref
)
1492 OSStatus err
= kNoErr
;
1496 m_serviceRefList
.remove( ref
);
1498 if ( IsWindow( m_hWnd
) )
1500 err
= WSAAsyncSelect((SOCKET
) DNSServiceRefSockFD( ref
), m_hWnd
, 0, 0 );
1501 require_noerr( err
, exit
);
1504 DNSServiceRefDeallocate( ref
);
1515 CPrinterSetupWizardSheet::ParseTextRecord( Service
* service
, Queue
* q
, uint16_t inTXTSize
, const char * inTXT
)
1520 // <rdar://problem/3946587> Use TXTRecord APIs declared in dns_sd.h
1522 bool qtotalDefined
= false;
1526 OSStatus err
= kNoErr
;
1528 // <rdar://problem/3987680> Default to queue "lp"
1532 // <rdar://problem/4003710> Default pdl key to be "application/postscript"
1534 q
->pdl
= L
"application/postscript";
1536 if ( ( val
= TXTRecordGetValuePtr( inTXTSize
, inTXT
, "rp", &len
) ) != NULL
)
1538 // Stringize val ( doesn't have trailing '\0' yet )
1540 memcpy( buf
, val
, len
);
1543 err
= UTF8StringToStringObject( buf
, q
->name
);
1544 require_noerr( err
, exit
);
1547 if ( ( val
= TXTRecordGetValuePtr( inTXTSize
, inTXT
, "pdl", &len
) ) != NULL
)
1549 // Stringize val ( doesn't have trailing '\0' yet )
1551 memcpy( buf
, val
, len
);
1554 err
= UTF8StringToStringObject( buf
, q
->pdl
);
1555 require_noerr( err
, exit
);
1558 if ( ( ( val
= TXTRecordGetValuePtr( inTXTSize
, inTXT
, "usb_mfg", &len
) ) != NULL
) ||
1559 ( ( val
= TXTRecordGetValuePtr( inTXTSize
, inTXT
, "usb_manufacturer", &len
) ) != NULL
) )
1561 // Stringize val ( doesn't have trailing '\0' yet )
1563 memcpy( buf
, val
, len
);
1566 err
= UTF8StringToStringObject( buf
, q
->usb_MFG
);
1567 require_noerr( err
, exit
);
1570 if ( ( ( val
= TXTRecordGetValuePtr( inTXTSize
, inTXT
, "usb_mdl", &len
) ) != NULL
) ||
1571 ( ( val
= TXTRecordGetValuePtr( inTXTSize
, inTXT
, "usb_model", &len
) ) != NULL
) )
1573 // Stringize val ( doesn't have trailing '\0' yet )
1575 memcpy( buf
, val
, len
);
1578 err
= UTF8StringToStringObject( buf
, q
->usb_MDL
);
1579 require_noerr( err
, exit
);
1582 if ( ( val
= TXTRecordGetValuePtr( inTXTSize
, inTXT
, "ty", &len
) ) != NULL
)
1584 // Stringize val ( doesn't have trailing '\0' yet )
1586 memcpy( buf
, val
, len
);
1589 err
= UTF8StringToStringObject( buf
, q
->description
);
1590 require_noerr( err
, exit
);
1593 if ( ( val
= TXTRecordGetValuePtr( inTXTSize
, inTXT
, "product", &len
) ) != NULL
)
1595 // Stringize val ( doesn't have trailing '\0' yet )
1597 memcpy( buf
, val
, len
);
1600 err
= UTF8StringToStringObject( buf
, q
->product
);
1601 require_noerr( err
, exit
);
1604 if ( ( val
= TXTRecordGetValuePtr( inTXTSize
, inTXT
, "note", &len
) ) != NULL
)
1606 // Stringize val ( doesn't have trailing '\0' yet )
1608 memcpy( buf
, val
, len
);
1611 err
= UTF8StringToStringObject( buf
, q
->location
);
1612 require_noerr( err
, exit
);
1615 if ( ( val
= TXTRecordGetValuePtr( inTXTSize
, inTXT
, "qtotal", &len
) ) != NULL
)
1617 // Stringize val ( doesn't have trailing '\0' yet )
1619 memcpy( buf
, val
, len
);
1622 service
->qtotal
= (unsigned short) atoi( buf
);
1623 qtotalDefined
= true;
1626 if ( ( val
= TXTRecordGetValuePtr( inTXTSize
, inTXT
, "priority", &len
) ) != NULL
)
1628 // Stringize val ( doesn't have trailing '\0' yet )
1630 memcpy( buf
, val
, len
);
1633 q
->priority
= atoi( buf
);
1638 // The following code is to fix a problem with older HP
1639 // printers that don't include "qtotal" in their text
1640 // record. We'll check to see if the q->name is "TEXT"
1641 // and if so, we're going to modify it to be "lp" so
1642 // that we don't use the wrong queue
1644 if ( !err
&& !qtotalDefined
&& ( q
->name
== L
"TEXT" ) )
1654 CPrinterSetupWizardSheet::Lookup(const char * inName
)
1658 Printer
* printer
= NULL
;
1659 Printers::iterator it
;
1661 for ( it
= m_printers
.begin(); it
!= m_printers
.end(); it
++ )
1663 if ( (*it
)->name
== inName
)
1675 CPrinterSetupWizardSheet::OrderServiceFunc( const Service
* a
, const Service
* b
)
1679 q1
= (a
->queues
.size() > 0) ? a
->queues
.front() : NULL
;
1681 q2
= (b
->queues
.size() > 0) ? b
->queues
.front() : NULL
;
1687 else if ( q1
&& !q2
)
1691 else if ( !q1
&& q2
)
1695 else if ( q1
->priority
< q2
->priority
)
1699 else if ( q1
->priority
> q2
->priority
)
1703 else if ( ( a
->type
== kPDLServiceType
) || ( ( a
->type
== kLPRServiceType
) && ( b
->type
== kIPPServiceType
) ) )
1715 CPrinterSetupWizardSheet::OrderQueueFunc( const Queue
* q1
, const Queue
* q2
)
1717 return ( q1
->priority
<= q2
->priority
) ? true : false;