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.29 2005/02/14 20:48:37 shersche
27 <rdar://problem/4003710> Default pdl key to "application/postscript"
29 Revision 1.28 2005/02/14 20:37:53 shersche
30 <rdar://problem/4003944> Populate comment field with the model name that users see in the wizard UI.
32 Revision 1.27 2005/02/09 05:04:03 shersche
33 <rdar://problem/3946587> Use TXTRecordGetValuePtr() API in ParseTextRecord
35 Revision 1.26 2005/02/08 21:45:06 shersche
36 <rdar://problem/3947490> Default to Generic PostScript or PCL if unable to match driver
38 Revision 1.25 2005/02/08 18:54:17 shersche
39 <rdar://problem/3987680> Default queue name is "lp" when rp key is not specified.
41 Revision 1.24 2005/02/01 02:15:55 shersche
42 <rdar://problem/3946587> Use TXTRecord parsing APIs in ParseTextRecord
44 Revision 1.23 2005/01/31 23:54:30 shersche
45 <rdar://problem/3947508> Start browsing when printer wizard starts. Move browsing logic from CSecondPage object to CPrinterSetupWizardSheet object.
47 Revision 1.22 2005/01/25 18:49:43 shersche
48 Get icon resources from resource DLL
50 Revision 1.21 2005/01/10 01:09:32 shersche
51 Use the "note" key to populate pLocation field when setting up printer
53 Revision 1.20 2005/01/03 19:05:01 shersche
54 Store pointer to instance of wizard sheet so that print driver install thread sends a window message to the correct window
56 Revision 1.19 2004/12/31 07:23:53 shersche
57 Don't modify the button setting in SetSelectedPrinter()
59 Revision 1.18 2004/12/29 18:53:38 shersche
60 <rdar://problem/3725106>
61 <rdar://problem/3737413> Added support for LPR and IPP protocols as well as support for obtaining multiple text records. Reorganized and simplified codebase.
62 Bug #: 3725106, 3737413
64 Revision 1.17 2004/10/12 18:02:53 shersche
65 <rdar://problem/3764873> Escape '/', '@', '"' characters in printui command.
68 Revision 1.16 2004/09/13 21:27:22 shersche
69 <rdar://problem/3796483> Pass the moreComing flag to OnAddPrinter and OnRemovePrinter callbacks
72 Revision 1.15 2004/09/11 05:59:06 shersche
73 <rdar://problem/3785766> Fix code that generates unique printer names based on currently installed printers
76 Revision 1.14 2004/09/02 01:57:58 cheshire
77 <rdar://problem/3783611> Fix incorrect testing of MoreComing flag
79 Revision 1.13 2004/07/26 21:06:29 shersche
80 <rdar://problem/3739200> Removing trailing '.' in hostname
83 Revision 1.12 2004/07/13 21:24:23 rpantos
84 Fix for <rdar://problem/3701120>.
86 Revision 1.11 2004/06/28 00:51:47 shersche
87 Move call to EnumPrinters out of browse callback into standalone function
89 Revision 1.10 2004/06/27 23:06:47 shersche
90 code cleanup, make sure EnumPrinters returns non-zero value
92 Revision 1.9 2004/06/27 15:49:31 shersche
93 clean up some cruft in the printer browsing code
95 Revision 1.8 2004/06/27 08:04:51 shersche
96 copy selected printer to prevent printer being deleted out from under
98 Revision 1.7 2004/06/26 23:27:12 shersche
99 support for installing multiple printers of the same name
101 Revision 1.6 2004/06/26 21:22:39 shersche
102 handle spaces in file names
104 Revision 1.5 2004/06/26 03:19:57 shersche
105 clean up warning messages
107 Submitted by: herscher
109 Revision 1.4 2004/06/25 02:26:52 shersche
110 Normalize key fields in text record entries
111 Submitted by: herscher
113 Revision 1.3 2004/06/24 20:12:07 shersche
115 Submitted by: herscher
117 Revision 1.2 2004/06/23 17:58:21 shersche
118 <rdar://problem/3701837> eliminated memory leaks on exit
119 <rdar://problem/3701926> installation of a printer that is already installed results in a no-op
120 Bug #: 3701837, 3701926
121 Submitted by: herscher
123 Revision 1.1 2004/06/18 04:36:57 rpantos
130 #include "PrinterSetupWizardApp.h"
131 #include "PrinterSetupWizardSheet.h"
132 #include "CommonServices.h"
133 #include "DebugServices.h"
134 #include "WinServices.h"
136 #include <winspool.h>
141 #pragma warning(disable:4702)
144 #if( !TARGET_OS_WINDOWS_CE )
145 # include <mswsock.h>
146 # include <process.h>
151 #define WM_SOCKET_EVENT ( WM_USER + 0x100 )
152 #define WM_PROCESS_EVENT ( WM_USER + 0x101 )
155 // CPrinterSetupWizardSheet
156 CPrinterSetupWizardSheet
* CPrinterSetupWizardSheet::m_self
;
158 IMPLEMENT_DYNAMIC(CPrinterSetupWizardSheet
, CPropertySheet
)
159 CPrinterSetupWizardSheet::CPrinterSetupWizardSheet(UINT nIDCaption
, CWnd
* pParentWnd
, UINT iSelectPage
)
160 :CPropertySheet(nIDCaption
, pParentWnd
, iSelectPage
),
161 m_selectedPrinter(NULL
),
162 m_driverThreadExitCode( 0 ),
163 m_driverThreadFinished( false ),
164 m_pdlBrowser( NULL
),
165 m_ippBrowser( NULL
),
168 m_arrow
= LoadCursor(0, IDC_ARROW
);
169 m_wait
= LoadCursor(0, IDC_APPSTARTING
);
179 CPrinterSetupWizardSheet::~CPrinterSetupWizardSheet()
183 while ( m_printers
.size() > 0 )
185 printer
= m_printers
.front();
186 m_printers
.pop_front();
195 // ------------------------------------------------------
196 // SetSelectedPrinter
198 // Manages setting a printer as the printer to install. Stops
199 // any pending resolves.
202 CPrinterSetupWizardSheet::SetSelectedPrinter(Printer
* printer
)
204 check( !printer
|| ( printer
!= m_selectedPrinter
) );
206 m_selectedPrinter
= printer
;
211 CPrinterSetupWizardSheet::LoadPrinterNames()
217 // rdar://problem/3701926 - Printer can't be installed twice
219 // First thing we want to do is make sure the printer isn't already installed.
220 // If the printer name is found, we'll try and rename it until we
221 // find a unique name
223 DWORD dwNeeded
= 0, dwNumPrinters
= 0;
225 BOOL ok
= EnumPrinters(PRINTER_ENUM_LOCAL
, NULL
, 4, NULL
, 0, &dwNeeded
, &dwNumPrinters
);
226 err
= translate_errno( ok
, errno_compat(), kUnknownErr
);
228 if ((err
== ERROR_INSUFFICIENT_BUFFER
) && (dwNeeded
> 0))
232 buffer
= new unsigned char[dwNeeded
];
239 require_action( buffer
, exit
, kNoMemoryErr
);
240 ok
= EnumPrinters(PRINTER_ENUM_LOCAL
, NULL
, 4, buffer
, dwNeeded
, &dwNeeded
, &dwNumPrinters
);
241 err
= translate_errno( ok
, errno_compat(), kUnknownErr
);
242 require_noerr( err
, exit
);
244 for (DWORD index
= 0; index
< dwNumPrinters
; index
++)
246 PRINTER_INFO_4
* lppi4
= (PRINTER_INFO_4
*) (buffer
+ index
* sizeof(PRINTER_INFO_4
));
248 m_printerNames
[lppi4
->pPrinterName
] = lppi4
->pPrinterName
;
264 // ------------------------------------------------------
267 // Installs a printer with Windows.
269 // NOTE: this works one of two ways, depending on whether
270 // there are drivers already installed for this printer.
271 // If there are, then we can just create a port with XcvData,
272 // and then call AddPrinter. If not, we use the printui.dll
273 // to install the printer. Actually installing drivers that
274 // are not currently installed is painful, and it's much
275 // easier and less error prone to just let printui.dll do
276 // the hard work for us.
280 CPrinterSetupWizardSheet::InstallPrinter(Printer
* printer
)
286 service
= printer
->services
.front();
290 // if the driver isn't installed, then install it
292 if ( !printer
->driverInstalled
)
298 m_driverThreadFinished
= false;
303 hThread
= (HANDLE
) _beginthreadex_compat( NULL
, 0, InstallDriverThread
, printer
, 0, &threadID
);
304 err
= translate_errno( hThread
, (OSStatus
) GetLastError(), kUnknownErr
);
305 require_noerr( err
, exit
);
310 while (!m_driverThreadFinished
)
314 GetMessage( &msg
, m_hWnd
, 0, 0 );
315 TranslateMessage(&msg
);
316 DispatchMessage(&msg
);
320 // Wait until child process exits.
322 dwResult
= WaitForSingleObject( hThread
, INFINITE
);
323 err
= translate_errno( dwResult
== WAIT_OBJECT_0
, errno_compat(), err
= kUnknownErr
);
324 require_noerr( err
, exit
);
327 // check the return value of thread
329 require_noerr( m_driverThreadExitCode
, exit
);
332 // now we know that the driver was successfully installed
334 printer
->driverInstalled
= true;
337 if ( service
->type
== kPDLServiceType
)
339 err
= InstallPrinterPDLAndLPR( printer
, service
, PROTOCOL_RAWTCP_TYPE
);
340 require_noerr( err
, exit
);
342 else if ( service
->type
== kLPRServiceType
)
344 err
= InstallPrinterPDLAndLPR( printer
, service
, PROTOCOL_LPR_TYPE
);
345 require_noerr( err
, exit
);
347 else if ( service
->type
== kIPPServiceType
)
349 err
= InstallPrinterIPP( printer
, service
);
350 require_noerr( err
, exit
);
355 require_noerr( err
, exit
);
358 printer
->installed
= true;
361 // if the user specified a default printer, set it
365 ok
= SetDefaultPrinter( printer
->actualName
);
366 err
= translate_errno( ok
, errno_compat(), err
= kUnknownErr
);
367 require_noerr( err
, exit
);
377 CPrinterSetupWizardSheet::InstallPrinterPDLAndLPR(Printer
* printer
, Service
* service
, DWORD protocol
)
379 PRINTER_DEFAULTS printerDefaults
= { NULL
, NULL
, SERVER_ACCESS_ADMINISTER
};
381 DWORD cbInputData
= 100;
382 PBYTE pOutputData
= NULL
;
383 DWORD cbOutputNeeded
= 0;
384 PORT_DATA_1 portData
;
385 PRINTER_INFO_2 pInfo
;
387 HANDLE hPrinter
= NULL
;
392 check(printer
!= NULL
);
393 check(printer
->installed
== false);
395 q
= service
->queues
.front();
398 ok
= OpenPrinter(L
",XcvMonitor Standard TCP/IP Port", &hXcv
, &printerDefaults
);
399 err
= translate_errno( ok
, errno_compat(), kUnknownErr
);
400 require_noerr( err
, exit
);
403 // BUGBUG: MSDN said this is not required, but my experience shows it is required
407 pOutputData
= new BYTE
[cbInputData
];
414 require_action( pOutputData
, exit
, err
= kNoMemoryErr
);
419 ZeroMemory(&portData
, sizeof(PORT_DATA_1
));
420 wcscpy(portData
.sztPortName
, printer
->portName
);
422 portData
.dwPortNumber
= service
->portNumber
;
423 portData
.dwVersion
= 1;
425 portData
.dwProtocol
= protocol
;
426 portData
.cbSize
= sizeof PORT_DATA_1
;
427 portData
.dwReserved
= 0L;
429 wcscpy(portData
.sztQueue
, q
->name
);
430 wcscpy(portData
.sztIPAddress
, service
->hostname
);
431 wcscpy(portData
.sztHostAddress
, service
->hostname
);
433 ok
= XcvData(hXcv
, L
"AddPort", (PBYTE
) &portData
, sizeof(PORT_DATA_1
), pOutputData
, cbInputData
, &cbOutputNeeded
, &dwStatus
);
434 err
= translate_errno( ok
, errno_compat(), kUnknownErr
);
435 require_noerr( err
, exit
);
440 ZeroMemory(&pInfo
, sizeof(pInfo
));
442 pInfo
.pPrinterName
= printer
->actualName
.GetBuffer();
443 pInfo
.pServerName
= NULL
;
444 pInfo
.pShareName
= NULL
;
445 pInfo
.pPortName
= printer
->portName
.GetBuffer();
446 pInfo
.pDriverName
= printer
->modelName
.GetBuffer();
447 pInfo
.pComment
= printer
->displayModelName
.GetBuffer();
448 pInfo
.pLocation
= service
->location
.GetBuffer();
449 pInfo
.pDevMode
= NULL
;
450 pInfo
.pDevMode
= NULL
;
451 pInfo
.pSepFile
= L
"";
452 pInfo
.pPrintProcessor
= L
"winprint";
453 pInfo
.pDatatype
= L
"RAW";
454 pInfo
.pParameters
= L
"";
455 pInfo
.pSecurityDescriptor
= NULL
;
456 pInfo
.Attributes
= PRINTER_ATTRIBUTE_QUEUED
;
458 pInfo
.DefaultPriority
= 0;
462 hPrinter
= AddPrinter(NULL
, 2, (LPBYTE
) &pInfo
);
463 err
= translate_errno( hPrinter
, errno_compat(), kUnknownErr
);
464 require_noerr( err
, exit
);
468 if (hPrinter
!= NULL
)
470 ClosePrinter(hPrinter
);
478 if (pOutputData
!= NULL
)
480 delete [] pOutputData
;
488 CPrinterSetupWizardSheet::InstallPrinterIPP(Printer
* printer
, Service
* service
)
490 DEBUG_UNUSED( service
);
492 HANDLE hPrinter
= NULL
;
493 PRINTER_INFO_2 pInfo
;
499 ZeroMemory(&pInfo
, sizeof(PRINTER_INFO_2
));
501 pInfo
.pPrinterName
= printer
->actualName
.GetBuffer();
502 pInfo
.pPortName
= printer
->portName
.GetBuffer();
503 pInfo
.pDriverName
= printer
->modelName
.GetBuffer();
504 pInfo
.pPrintProcessor
= L
"winprint";
505 pInfo
.pLocation
= service
->location
.GetBuffer();
506 pInfo
.pComment
= printer
->displayModelName
.GetBuffer();
507 pInfo
.Attributes
= PRINTER_ATTRIBUTE_NETWORK
| PRINTER_ATTRIBUTE_LOCAL
;
509 hPrinter
= AddPrinter(NULL
, 2, (LPBYTE
)&pInfo
);
510 err
= translate_errno( hPrinter
, errno_compat(), kUnknownErr
);
511 require_noerr( err
, exit
);
515 if ( hPrinter
!= NULL
)
517 ClosePrinter(hPrinter
);
524 BEGIN_MESSAGE_MAP(CPrinterSetupWizardSheet
, CPropertySheet
)
525 ON_MESSAGE( WM_SOCKET_EVENT
, OnSocketEvent
)
526 ON_MESSAGE( WM_PROCESS_EVENT
, OnProcessEvent
)
532 // ------------------------------------------------------
535 // Traps when the user hits Finish
537 BOOL
CPrinterSetupWizardSheet::OnCommand(WPARAM wParam
, LPARAM lParam
)
540 // Check if this is OK
542 if (wParam
== ID_WIZFINISH
) // If OK is hit...
547 return CPropertySheet::OnCommand(wParam
, lParam
);
551 // ------------------------------------------------------
554 // Initializes this Dialog object.
556 BOOL
CPrinterSetupWizardSheet::OnInitDialog()
560 CPropertySheet::OnInitDialog();
563 require_noerr( err
, exit
);
571 if ( err
== kDNSServiceErr_Firewall
)
573 CString text
, caption
;
575 text
.LoadString( IDS_FIREWALL
);
576 caption
.LoadString( IDS_FIREWALL_CAPTION
);
578 MessageBox(text
, caption
, MB_OK
|MB_ICONEXCLAMATION
);
582 CPrinterSetupWizardSheet::WizardException exc
;
584 exc
.text
.LoadString( IDS_NO_MDNSRESPONDER_SERVICE_TEXT
);
585 exc
.caption
.LoadString( IDS_ERROR_CAPTION
);
595 // ------------------------------------------------------
598 // This is called when Windows wants to know what cursor
599 // to display. So we tell it.
602 CPrinterSetupWizardSheet::OnSetCursor(CWnd
* pWnd
, UINT nHitTest
, UINT message
)
605 DEBUG_UNUSED(nHitTest
);
606 DEBUG_UNUSED(message
);
613 // ------------------------------------------------------
616 // This is not fully implemented yet.
620 CPrinterSetupWizardSheet::OnContextMenu(CWnd
* pWnd
, CPoint pos
)
631 // ------------------------------------------------------
634 // This is called when the user hits the "Finish" button
637 CPrinterSetupWizardSheet::OnOK()
639 check ( m_selectedPrinter
!= NULL
);
641 SetWizardButtons( PSWIZB_DISABLEDFINISH
);
643 if ( InstallPrinter( m_selectedPrinter
) != kNoErr
)
648 caption
.LoadString(IDS_INSTALL_ERROR_CAPTION
);
649 message
.LoadString(IDS_INSTALL_ERROR_MESSAGE
);
651 MessageBox(message
, caption
, MB_OK
|MB_ICONEXCLAMATION
);
658 // CPrinterSetupWizardSheet message handlers
660 void CPrinterSetupWizardSheet::Init(void)
663 AddPage(&m_pgSecond
);
665 AddPage(&m_pgFourth
);
667 m_psh
.dwFlags
&= (~PSH_HASHELP
);
669 m_psh
.dwFlags
|= PSH_WIZARD97
|PSH_WATERMARK
|PSH_HEADER
;
670 m_psh
.pszbmWatermark
= MAKEINTRESOURCE(IDB_WATERMARK
);
671 m_psh
.pszbmHeader
= MAKEINTRESOURCE(IDB_BANNER_ICON
);
673 m_psh
.hInstance
= GetNonLocalizedResources();
680 CPrinterSetupWizardSheet::OnSocketEvent(WPARAM inWParam
, LPARAM inLParam
)
682 if (WSAGETSELECTERROR(inLParam
) && !(HIWORD(inLParam
)))
684 dlog( kDebugLevelError
, "OnServiceEvent: window error\n" );
688 SOCKET sock
= (SOCKET
) inWParam
;
691 ServiceRefList::iterator begin
= m_serviceRefList
.begin();
692 ServiceRefList::iterator end
= m_serviceRefList
.end();
696 DNSServiceRef ref
= *begin
++;
700 if ((SOCKET
) DNSServiceRefSockFD(ref
) == sock
)
702 DNSServiceProcessResult(ref
);
713 CPrinterSetupWizardSheet::OnProcessEvent(WPARAM inWParam
, LPARAM inLParam
)
715 DEBUG_UNUSED(inLParam
);
717 m_driverThreadExitCode
= (DWORD
) inWParam
;
718 m_driverThreadFinished
= true;
725 CPrinterSetupWizardSheet::InstallDriverThread( LPVOID inParam
)
727 Printer
* printer
= (Printer
*) inParam
;
732 PROCESS_INFORMATION pi
;
739 // because we're calling endthreadex(), C++ objects won't be cleaned up
740 // correctly. we'll nest the CString 'command' inside a block so
741 // that it's destructor will be invoked.
746 ZeroMemory( &si
, sizeof(si
) );
748 ZeroMemory( &pi
, sizeof(pi
) );
750 command
.Format(L
"rundll32.exe printui.dll,PrintUIEntry /ia /m \"%s\" /f \"%s\"", (LPCTSTR
) printer
->modelName
, (LPCTSTR
) printer
->infFileName
);
752 ok
= CreateProcess(NULL
, command
.GetBuffer(), NULL
, NULL
, FALSE
, 0, NULL
, NULL
, &si
, &pi
);
753 err
= translate_errno( ok
, errno_compat(), kUnknownErr
);
754 require_noerr( err
, exit
);
756 dwResult
= WaitForSingleObject( pi
.hProcess
, INFINITE
);
757 translate_errno( dwResult
== WAIT_OBJECT_0
, errno_compat(), err
= kUnknownErr
);
758 require_noerr( err
, exit
);
760 ok
= GetExitCodeProcess( pi
.hProcess
, &exitCode
);
761 err
= translate_errno( ok
, errno_compat(), kUnknownErr
);
762 require_noerr( err
, exit
);
768 // Close process and thread handles.
772 CloseHandle( pi
.hProcess
);
777 CloseHandle( pi
.hThread
);
781 // alert the main thread
783 m_self
->PostMessage( WM_PROCESS_EVENT
, err
, exitCode
);
785 _endthreadex_compat( 0 );
792 CPrinterSetupWizardSheet::OnBrowse(
794 DNSServiceFlags inFlags
,
795 uint32_t inInterfaceIndex
,
796 DNSServiceErrorType inErrorCode
,
799 const char * inDomain
,
804 CPrinterSetupWizardSheet
* self
;
805 bool moreComing
= (bool) (inFlags
& kDNSServiceFlagsMoreComing
);
806 CPropertyPage
* active
;
807 Printer
* printer
= NULL
;
808 Service
* service
= NULL
;
809 OSStatus err
= kNoErr
;
811 require_noerr( inErrorCode
, exit
);
813 self
= reinterpret_cast <CPrinterSetupWizardSheet
*>( inContext
);
814 require_quiet( self
, exit
);
816 active
= self
->GetActivePage();
817 require_quiet( active
, exit
);
819 // Have we seen this printer before?
821 printer
= self
->Lookup( inName
);
825 service
= printer
->LookupService( inType
);
828 if ( inFlags
& kDNSServiceFlagsAdd
)
832 // If not, then create a new one
834 printer
= self
->OnAddPrinter( inInterfaceIndex
, inName
, inType
, inDomain
, moreComing
);
835 require_action( printer
, exit
, err
= kUnknownErr
);
840 err
= self
->OnAddService( printer
, inInterfaceIndex
, inName
, inType
, inDomain
);
841 require_noerr( err
, exit
);
852 err
= self
->OnRemoveService( service
);
853 require_noerr( err
, exit
);
855 if ( printer
->services
.size() == 0 )
857 err
= self
->OnRemovePrinter( printer
, moreComing
);
858 require_noerr( err
, exit
);
869 CPrinterSetupWizardSheet::OnResolve(
871 DNSServiceFlags inFlags
,
872 uint32_t inInterfaceIndex
,
873 DNSServiceErrorType inErrorCode
,
874 const char * inFullName
,
875 const char * inHostName
,
881 DEBUG_UNUSED(inFullName
);
882 DEBUG_UNUSED(inInterfaceIndex
);
883 DEBUG_UNUSED(inFlags
);
886 CPrinterSetupWizardSheet
* self
;
889 uint32_t qpriority
= kDefaultPriority
;
894 require_noerr( inErrorCode
, exit
);
896 service
= reinterpret_cast<Service
*>( inContext
);
897 require_quiet( service
, exit
);
899 check( service
->refs
!= 0 );
901 self
= service
->printer
->window
;
902 require_quiet( self
, exit
);
904 err
= self
->StopOperation( service
->serviceRef
);
905 require_noerr( err
, exit
);
908 // hold on to the hostname...
910 err
= UTF8StringToStringObject( inHostName
, service
->hostname
);
911 require_noerr( err
, exit
);
914 // <rdar://problem/3739200> remove the trailing dot on hostname
916 idx
= service
->hostname
.ReverseFind('.');
918 if ((idx
> 1) && ((service
->hostname
.GetLength() - 1) == idx
))
920 service
->hostname
.Delete(idx
, 1);
924 // hold on to the port
926 service
->portNumber
= ntohs(inPort
);
929 // parse the text record.
932 err
= self
->ParseTextRecord( service
, inTXTSize
, inTXT
, qname
, qpriority
);
933 require_noerr( err
, exit
);
935 if ( service
->qtotal
== 1 )
938 // create a new queue
949 require_action( q
, exit
, err
= E_OUTOFMEMORY
);
953 q
->priority
= qpriority
;
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
, inRDLen
, inTXT
, q
->name
, q
->priority
);
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
, uint16_t inTXTSize
, const char * inTXT
, CString
& qname
, uint32_t & qpriority
)
1517 // <rdar://problem/3946587> Use TXTRecord APIs declared in dns_sd.h
1519 bool qtotalDefined
= false;
1523 OSStatus err
= kNoErr
;
1525 // <rdar://problem/3987680> Default to queue "lp"
1529 // <rdar://problem/4003710> Default pdl key to be "application/postscript"
1531 service
->pdl
= L
"application/postscript";
1533 if ( ( val
= TXTRecordGetValuePtr( inTXTSize
, inTXT
, "rp", &len
) ) != NULL
)
1535 // Stringize val ( doesn't have trailing '\0' yet )
1537 memcpy( buf
, val
, len
);
1540 err
= UTF8StringToStringObject( buf
, qname
);
1541 require_noerr( err
, exit
);
1544 if ( ( val
= TXTRecordGetValuePtr( inTXTSize
, inTXT
, "pdl", &len
) ) != NULL
)
1546 // Stringize val ( doesn't have trailing '\0' yet )
1548 memcpy( buf
, val
, len
);
1551 err
= UTF8StringToStringObject( buf
, service
->pdl
);
1552 require_noerr( err
, exit
);
1555 if ( ( ( val
= TXTRecordGetValuePtr( inTXTSize
, inTXT
, "usb_mfg", &len
) ) != NULL
) ||
1556 ( ( val
= TXTRecordGetValuePtr( inTXTSize
, inTXT
, "usb_manufacturer", &len
) ) != NULL
) )
1558 // Stringize val ( doesn't have trailing '\0' yet )
1560 memcpy( buf
, val
, len
);
1563 err
= UTF8StringToStringObject( buf
, service
->usb_MFG
);
1564 require_noerr( err
, exit
);
1567 if ( ( ( val
= TXTRecordGetValuePtr( inTXTSize
, inTXT
, "usb_mdl", &len
) ) != NULL
) ||
1568 ( ( val
= TXTRecordGetValuePtr( inTXTSize
, inTXT
, "usb_model", &len
) ) != NULL
) )
1570 // Stringize val ( doesn't have trailing '\0' yet )
1572 memcpy( buf
, val
, len
);
1575 err
= UTF8StringToStringObject( buf
, service
->usb_MDL
);
1576 require_noerr( err
, exit
);
1579 if ( ( val
= TXTRecordGetValuePtr( inTXTSize
, inTXT
, "ty", &len
) ) != NULL
)
1581 // Stringize val ( doesn't have trailing '\0' yet )
1583 memcpy( buf
, val
, len
);
1586 err
= UTF8StringToStringObject( buf
, service
->description
);
1587 require_noerr( err
, exit
);
1590 if ( ( val
= TXTRecordGetValuePtr( inTXTSize
, inTXT
, "product", &len
) ) != NULL
)
1592 // Stringize val ( doesn't have trailing '\0' yet )
1594 memcpy( buf
, val
, len
);
1597 err
= UTF8StringToStringObject( buf
, service
->product
);
1598 require_noerr( err
, exit
);
1601 if ( ( val
= TXTRecordGetValuePtr( inTXTSize
, inTXT
, "note", &len
) ) != NULL
)
1603 // Stringize val ( doesn't have trailing '\0' yet )
1605 memcpy( buf
, val
, len
);
1608 err
= UTF8StringToStringObject( buf
, service
->location
);
1609 require_noerr( err
, exit
);
1612 if ( ( val
= TXTRecordGetValuePtr( inTXTSize
, inTXT
, "qtotal", &len
) ) != NULL
)
1614 // Stringize val ( doesn't have trailing '\0' yet )
1616 memcpy( buf
, val
, len
);
1619 service
->qtotal
= (unsigned short) atoi( buf
);
1620 qtotalDefined
= true;
1623 if ( ( val
= TXTRecordGetValuePtr( inTXTSize
, inTXT
, "priority", &len
) ) != NULL
)
1625 // Stringize val ( doesn't have trailing '\0' yet )
1627 memcpy( buf
, val
, len
);
1630 qpriority
= atoi( buf
);
1635 // The following code is to fix a problem with older HP
1636 // printers that don't include "qtotal" in their text
1637 // record. We'll check to see if the qname is "TEXT"
1638 // and if so, we're going to modify it to be "lp" so
1639 // that we don't use the wrong queue
1641 if ( !err
&& !qtotalDefined
&& ( qname
== L
"TEXT" ) )
1651 CPrinterSetupWizardSheet::Lookup(const char * inName
)
1655 Printer
* printer
= NULL
;
1656 Printers::iterator it
;
1658 for ( it
= m_printers
.begin(); it
!= m_printers
.end(); it
++ )
1660 if ( (*it
)->name
== inName
)
1672 CPrinterSetupWizardSheet::OrderServiceFunc( const Service
* a
, const Service
* b
)
1676 q1
= (a
->queues
.size() > 0) ? a
->queues
.front() : NULL
;
1678 q2
= (b
->queues
.size() > 0) ? b
->queues
.front() : NULL
;
1684 else if ( q1
&& !q2
)
1688 else if ( !q1
&& q2
)
1692 else if ( q1
->priority
< q2
->priority
)
1696 else if ( q1
->priority
> q2
->priority
)
1700 else if ( ( a
->type
== kPDLServiceType
) || ( ( a
->type
== kLPRServiceType
) && ( b
->type
== kIPPServiceType
) ) )
1712 CPrinterSetupWizardSheet::OrderQueueFunc( const Queue
* q1
, const Queue
* q2
)
1714 return ( q1
->priority
<= q2
->priority
) ? true : false;