]> git.saurik.com Git - apple/mdnsresponder.git/blob - Clients/PrinterSetupWizard/PrinterSetupWizardSheet.cpp
mDNSResponder-87.tar.gz
[apple/mdnsresponder.git] / Clients / PrinterSetupWizard / PrinterSetupWizardSheet.cpp
1 /*
2 * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22
23 Change History (most recent first):
24
25 $Log: PrinterSetupWizardSheet.cpp,v $
26 Revision 1.17 2004/10/12 18:02:53 shersche
27 <rdar://problem/3764873> Escape '/', '@', '"' characters in printui command.
28 Bug #: 3764873
29
30 Revision 1.16 2004/09/13 21:27:22 shersche
31 <rdar://problem/3796483> Pass the moreComing flag to OnAddPrinter and OnRemovePrinter callbacks
32 Bug #: 3796483
33
34 Revision 1.15 2004/09/11 05:59:06 shersche
35 <rdar://problem/3785766> Fix code that generates unique printer names based on currently installed printers
36 Bug #: 3785766
37
38 Revision 1.14 2004/09/02 01:57:58 cheshire
39 <rdar://problem/3783611> Fix incorrect testing of MoreComing flag
40
41 Revision 1.13 2004/07/26 21:06:29 shersche
42 <rdar://problem/3739200> Removing trailing '.' in hostname
43 Bug #: 3739200
44
45 Revision 1.12 2004/07/13 21:24:23 rpantos
46 Fix for <rdar://problem/3701120>.
47
48 Revision 1.11 2004/06/28 00:51:47 shersche
49 Move call to EnumPrinters out of browse callback into standalone function
50
51 Revision 1.10 2004/06/27 23:06:47 shersche
52 code cleanup, make sure EnumPrinters returns non-zero value
53
54 Revision 1.9 2004/06/27 15:49:31 shersche
55 clean up some cruft in the printer browsing code
56
57 Revision 1.8 2004/06/27 08:04:51 shersche
58 copy selected printer to prevent printer being deleted out from under
59
60 Revision 1.7 2004/06/26 23:27:12 shersche
61 support for installing multiple printers of the same name
62
63 Revision 1.6 2004/06/26 21:22:39 shersche
64 handle spaces in file names
65
66 Revision 1.5 2004/06/26 03:19:57 shersche
67 clean up warning messages
68
69 Submitted by: herscher
70
71 Revision 1.4 2004/06/25 02:26:52 shersche
72 Normalize key fields in text record entries
73 Submitted by: herscher
74
75 Revision 1.3 2004/06/24 20:12:07 shersche
76 Clean up source code
77 Submitted by: herscher
78
79 Revision 1.2 2004/06/23 17:58:21 shersche
80 <rdar://problem/3701837> eliminated memory leaks on exit
81 <rdar://problem/3701926> installation of a printer that is already installed results in a no-op
82 Bug #: 3701837, 3701926
83 Submitted by: herscher
84
85 Revision 1.1 2004/06/18 04:36:57 rpantos
86 First checked in
87
88
89 */
90
91 #include "stdafx.h"
92 #include "PrinterSetupWizardApp.h"
93 #include "PrinterSetupWizardSheet.h"
94 #include "CommonServices.h"
95 #include "DebugServices.h"
96 #include "WinServices.h"
97 #include "About.h"
98 #include <winspool.h>
99 #include <tcpxcv.h>
100 #include <string>
101
102 // unreachable code
103 #pragma warning(disable:4702)
104
105
106 #if( !TARGET_OS_WINDOWS_CE )
107 # include <mswsock.h>
108 # include <process.h>
109 #endif
110
111 // Private Messages
112
113 #define WM_SERVICE_EVENT ( WM_USER + 0x100 )
114 #define WM_PROCESS_EVENT ( WM_USER + 0x101 )
115
116 // Service Types
117
118 #define kPDLDataStreamServiceType "_pdl-datastream._tcp"
119 #define kLPRServiceType "_printer._tcp"
120 #define kIPPServiceType "_ipp._tcp"
121
122
123 // CPrinterSetupWizardSheet
124
125 IMPLEMENT_DYNAMIC(CPrinterSetupWizardSheet, CPropertySheet)
126 CPrinterSetupWizardSheet::CPrinterSetupWizardSheet(UINT nIDCaption, CWnd* pParentWnd, UINT iSelectPage)
127 :CPropertySheet(nIDCaption, pParentWnd, iSelectPage),
128 m_selectedPrinter(NULL)
129 {
130 m_arrow = LoadCursor(0, IDC_ARROW);
131 m_wait = LoadCursor(0, IDC_APPSTARTING);
132 m_active = m_arrow;
133
134 Init();
135 }
136
137
138 CPrinterSetupWizardSheet::~CPrinterSetupWizardSheet()
139 {
140 //
141 // rdar://problem/3701837 memory leaks
142 //
143 // Clean up the ServiceRef and printer list on exit
144 //
145 if (m_pdlBrowser != NULL)
146 {
147 DNSServiceRefDeallocate(m_pdlBrowser);
148 m_pdlBrowser = NULL;
149 }
150
151 while (m_printerList.size() > 0)
152 {
153 Printer * printer = m_printerList.front();
154
155 m_printerList.pop_front();
156
157 delete printer;
158 }
159
160 if (m_selectedPrinter != NULL)
161 {
162 delete m_selectedPrinter;
163 }
164 }
165
166
167 // ------------------------------------------------------
168 // InstallEventHandler
169 //
170 // Installs an event handler for DNSService events.
171 //
172 int
173 CPrinterSetupWizardSheet::InstallEventHandler(EventHandler * handler)
174 {
175 PrinterList::iterator iter;
176
177 m_eventHandlerList.push_back(handler);
178
179 iter = m_printerList.begin();
180
181 while (iter != m_printerList.end())
182 {
183 Printer * printer = *iter++;
184
185 handler->OnAddPrinter(printer, iter != m_printerList.end());
186 }
187
188 return kNoErr;
189 }
190
191
192 // ------------------------------------------------------
193 // RemoveEventHandler
194 //
195 // Removes an event handler for DNSService events.
196 //
197 int
198 CPrinterSetupWizardSheet::RemoveEventHandler(EventHandler * handler)
199 {
200 m_eventHandlerList.remove(handler);
201
202 return kNoErr;
203 }
204
205
206
207 // ------------------------------------------------------
208 // SetSelectedPrinter
209 //
210 // Manages setting a printer as the printer to install. Stops
211 // any pending resolves.
212 //
213 OSStatus
214 CPrinterSetupWizardSheet::SetSelectedPrinter(Printer * printer)
215 {
216 OSStatus err;
217
218 //
219 // we only want one resolve going on at a time, so we check
220 // state of the m_selectedPrinter
221 //
222 if (m_selectedPrinter != NULL)
223 {
224 //
225 // if we're currently resolving, then stop the resolve
226 //
227 if (m_selectedPrinter->serviceRef)
228 {
229 err = StopResolve(m_selectedPrinter);
230 require_noerr(err, exit);
231 }
232
233 delete m_selectedPrinter;
234 m_selectedPrinter = NULL;
235 }
236
237 check( m_selectedPrinter == NULL );
238
239 try
240 {
241 m_selectedPrinter = new Printer;
242 }
243 catch (...)
244 {
245 m_selectedPrinter = NULL;
246 }
247
248 require_action( m_selectedPrinter, exit, err = E_OUTOFMEMORY );
249
250 m_selectedPrinter->window = printer->window;
251 m_selectedPrinter->serviceRef = NULL;
252 m_selectedPrinter->item = NULL;
253 m_selectedPrinter->ifi = printer->ifi;
254 m_selectedPrinter->name = printer->name;
255 m_selectedPrinter->displayName = printer->displayName;
256 m_selectedPrinter->actualName = printer->actualName;
257 m_selectedPrinter->type = printer->type;
258 m_selectedPrinter->domain = printer->domain;
259 m_selectedPrinter->installed = printer->installed;
260 m_selectedPrinter->deflt = printer->deflt;
261 m_selectedPrinter->refs = 1;
262
263 err = StartResolve(m_selectedPrinter);
264 require_noerr(err, exit);
265
266 exit:
267
268 return err;
269 }
270
271
272 // ------------------------------------------------------
273 // InstallPrinter
274 //
275 // Installs a printer with Windows.
276 //
277 // NOTE: this works one of two ways, depending on whether
278 // there are drivers already installed for this printer.
279 // If there are, then we can just create a port with XcvData,
280 // and then call AddPrinter. If not, we use the printui.dll
281 // to install the printer. Actually installing drivers that
282 // are not currently installed is painful, and it's much
283 // easier and less error prone to just let printui.dll do
284 // the hard work for us.
285 //
286
287 OSStatus
288 CPrinterSetupWizardSheet::InstallPrinter(Printer * printer)
289 {
290 PRINTER_DEFAULTS printerDefaults = { NULL, NULL, SERVER_ACCESS_ADMINISTER };
291 DWORD dwStatus;
292 DWORD cbInputData = 100;
293 PBYTE pOutputData = NULL;
294 DWORD cbOutputNeeded = 0;
295 PORT_DATA_1 portData;
296 HANDLE hXcv = NULL;
297 HANDLE hPrinter = NULL;
298 BOOL ok;
299 OSStatus err;
300
301 check(printer != NULL);
302 check(printer->installed == false);
303
304 ok = OpenPrinter(L",XcvMonitor Standard TCP/IP Port", &hXcv, &printerDefaults);
305 err = translate_errno( ok, errno_compat(), kUnknownErr );
306 require_noerr( err, exit );
307
308 //
309 // BUGBUG: MSDN said this is not required, but my experience shows it is required
310 //
311 try
312 {
313 pOutputData = new BYTE[cbInputData];
314 }
315 catch (...)
316 {
317 pOutputData = NULL;
318 }
319
320 require_action( pOutputData, exit, err = kNoMemoryErr );
321
322 //
323 // setup the port
324 //
325 ZeroMemory(&portData, sizeof(PORT_DATA_1));
326 wcscpy(portData.sztPortName, printer->portName);
327
328 portData.dwPortNumber = printer->portNumber;
329 portData.dwVersion = 1;
330
331 portData.dwProtocol = PROTOCOL_RAWTCP_TYPE;
332 portData.cbSize = sizeof PORT_DATA_1;
333 portData.dwReserved = 0L;
334
335 wcscpy(portData.sztQueue, printer->hostname);
336 wcscpy(portData.sztIPAddress, printer->hostname);
337 wcscpy(portData.sztHostAddress, printer->hostname);
338
339 ok = XcvData(hXcv, L"AddPort", (PBYTE) &portData, sizeof(PORT_DATA_1), pOutputData, cbInputData, &cbOutputNeeded, &dwStatus);
340 err = translate_errno( ok, errno_compat(), kUnknownErr );
341 require_noerr( err, exit );
342
343 if (printer->driverInstalled)
344 {
345 PRINTER_INFO_2 pInfo;
346
347 ZeroMemory(&pInfo, sizeof(pInfo));
348
349 pInfo.pPrinterName = printer->actualName.GetBuffer();
350 pInfo.pServerName = NULL;
351 pInfo.pShareName = NULL;
352 pInfo.pPortName = printer->portName.GetBuffer();
353 pInfo.pDriverName = printer->model.GetBuffer();
354 pInfo.pComment = printer->model.GetBuffer();
355 pInfo.pLocation = L"";
356 pInfo.pDevMode = NULL;
357 pInfo.pDevMode = NULL;
358 pInfo.pSepFile = L"";
359 pInfo.pPrintProcessor = L"winprint";
360 pInfo.pDatatype = L"RAW";
361 pInfo.pParameters = L"";
362 pInfo.pSecurityDescriptor = NULL;
363 pInfo.Attributes = PRINTER_ATTRIBUTE_QUEUED;
364 pInfo.Priority = 0;
365 pInfo.DefaultPriority = 0;
366 pInfo.StartTime = 0;
367 pInfo.UntilTime = 0;
368
369 hPrinter = AddPrinter(NULL, 2, (LPBYTE) &pInfo);
370 err = translate_errno( hPrinter, errno_compat(), kUnknownErr );
371 require_noerr( err, exit );
372 }
373 else
374 {
375 DWORD dwResult;
376 HANDLE hThread;
377 unsigned threadID;
378
379
380 m_processFinished = false;
381
382 //
383 // create the thread
384 //
385 hThread = (HANDLE) _beginthreadex_compat( NULL, 0, InstallPrinterThread, printer, 0, &threadID );
386 err = translate_errno( hThread, (OSStatus) GetLastError(), kUnknownErr );
387 require_noerr( err, exit );
388
389 //
390 // go modal
391 //
392 while (!m_processFinished)
393 {
394 MSG msg;
395
396 GetMessage( &msg, m_hWnd, 0, 0 );
397 TranslateMessage(&msg);
398 DispatchMessage(&msg);
399 }
400
401 //
402 // Wait until child process exits.
403 //
404 dwResult = WaitForSingleObject( hThread, INFINITE );
405 err = translate_errno( dwResult == WAIT_OBJECT_0, errno_compat(), err = kUnknownErr );
406 require_noerr( err, exit );
407 }
408
409 printer->installed = true;
410
411 //
412 // if the user specified a default printer, set it
413 //
414 if (printer->deflt)
415 {
416 ok = SetDefaultPrinter(printer->actualName);
417 err = translate_errno( ok, errno_compat(), err = kUnknownErr );
418 require_noerr( err, exit );
419 }
420
421 exit:
422
423 if (hPrinter != NULL)
424 {
425 ClosePrinter(hPrinter);
426 }
427
428 if (hXcv != NULL)
429 {
430 ClosePrinter(hXcv);
431 }
432
433 if (pOutputData != NULL)
434 {
435 delete [] pOutputData;
436 }
437
438 return err;
439 }
440
441
442 BEGIN_MESSAGE_MAP(CPrinterSetupWizardSheet, CPropertySheet)
443 ON_MESSAGE( WM_SERVICE_EVENT, OnServiceEvent )
444 ON_MESSAGE( WM_PROCESS_EVENT, OnProcessEvent )
445 ON_WM_SETCURSOR()
446 END_MESSAGE_MAP()
447
448
449 // ------------------------------------------------------
450 // OnCommand
451 //
452 // Traps when the user hits Finish
453 //
454 BOOL CPrinterSetupWizardSheet::OnCommand(WPARAM wParam, LPARAM lParam)
455 {
456 //
457 // Check if this is OK
458 //
459 if (wParam == ID_WIZFINISH) // If OK is hit...
460 {
461 OnOK();
462 }
463
464 return CPropertySheet::OnCommand(wParam, lParam);
465 }
466
467
468 // ------------------------------------------------------
469 // OnInitDialog
470 //
471 // Initializes this Dialog object. We start the browse here,
472 // so that printers show up instantly when the user clicks
473 // the next button.
474 //
475 BOOL CPrinterSetupWizardSheet::OnInitDialog()
476 {
477 OSStatus err;
478
479 CPropertySheet::OnInitDialog();
480
481 //
482 // setup the DNS-SD browsing
483 //
484 err = DNSServiceBrowse( &m_pdlBrowser, 0, 0, kPDLDataStreamServiceType, NULL, OnBrowse, this );
485 require_noerr( err, exit );
486
487 m_serviceRefList.push_back(m_pdlBrowser);
488
489 err = WSAAsyncSelect((SOCKET) DNSServiceRefSockFD(m_pdlBrowser), m_hWnd, WM_SERVICE_EVENT, FD_READ|FD_CLOSE);
490 require_noerr( err, exit );
491
492 LoadPrinterNames();
493
494 exit:
495
496 if (err != kNoErr)
497 {
498 WizardException exc;
499
500 exc.text.LoadString(IDS_NO_MDNSRESPONDER_SERVICE_TEXT);
501 exc.caption.LoadString(IDS_NO_MDNSRESPONDER_SERVICE_CAPTION);
502
503 throw(exc);
504 }
505
506 return TRUE;
507 }
508
509
510 // ------------------------------------------------------
511 // OnSetCursor
512 //
513 // This is called when Windows wants to know what cursor
514 // to display. So we tell it.
515 //
516 BOOL
517 CPrinterSetupWizardSheet::OnSetCursor(CWnd * pWnd, UINT nHitTest, UINT message)
518 {
519 DEBUG_UNUSED(pWnd);
520 DEBUG_UNUSED(nHitTest);
521 DEBUG_UNUSED(message);
522
523 SetCursor(m_active);
524 return TRUE;
525 }
526
527
528 // ------------------------------------------------------
529 // OnContextMenu
530 //
531 // This is not fully implemented yet.
532 //
533
534 void
535 CPrinterSetupWizardSheet::OnContextMenu(CWnd * pWnd, CPoint pos)
536 {
537 DEBUG_UNUSED(pWnd);
538 DEBUG_UNUSED(pos);
539
540 CAbout dlg;
541
542 dlg.DoModal();
543 }
544
545
546 // ------------------------------------------------------
547 // OnOK
548 //
549 // This is called when the user hits the "Finish" button
550 //
551 void
552 CPrinterSetupWizardSheet::OnOK()
553 {
554 check ( m_selectedPrinter != NULL );
555
556 SetWizardButtons( PSWIZB_DISABLEDFINISH );
557
558 if ( InstallPrinter( m_selectedPrinter ) != kNoErr )
559 {
560 CString caption;
561 CString message;
562
563 caption.LoadString(IDS_INSTALL_ERROR_CAPTION);
564 message.LoadString(IDS_INSTALL_ERROR_MESSAGE);
565
566 MessageBox(message, caption, MB_OK|MB_ICONEXCLAMATION);
567 }
568 }
569
570
571 // CPrinterSetupWizardSheet message handlers
572
573 void CPrinterSetupWizardSheet::Init(void)
574 {
575 AddPage(&m_pgFirst);
576 AddPage(&m_pgSecond);
577 AddPage(&m_pgThird);
578 AddPage(&m_pgFourth);
579
580 m_psh.dwFlags &= (~PSH_HASHELP);
581
582 m_psh.dwFlags |= PSH_WIZARD97|PSH_WATERMARK|PSH_HEADER;
583 m_psh.pszbmWatermark = MAKEINTRESOURCE(IDB_WATERMARK);
584 m_psh.pszbmHeader = MAKEINTRESOURCE(IDB_BANNER_ICON);
585
586 m_psh.hInstance = AfxGetInstanceHandle();
587
588 SetWizardMode();
589 }
590
591
592 LONG
593 CPrinterSetupWizardSheet::OnServiceEvent(WPARAM inWParam, LPARAM inLParam)
594 {
595 if (WSAGETSELECTERROR(inLParam) && !(HIWORD(inLParam)))
596 {
597 dlog( kDebugLevelError, "OnServiceEvent: window error\n" );
598 }
599 else
600 {
601 SOCKET sock = (SOCKET) inWParam;
602
603 // iterate thru list
604 ServiceRefList::iterator begin = m_serviceRefList.begin();
605 ServiceRefList::iterator end = m_serviceRefList.end();
606
607 while (begin != end)
608 {
609 DNSServiceRef ref = *begin++;
610
611 check(ref != NULL);
612
613 if ((SOCKET) DNSServiceRefSockFD(ref) == sock)
614 {
615 DNSServiceProcessResult(ref);
616 break;
617 }
618 }
619 }
620
621 return ( 0 );
622 }
623
624
625 LONG
626 CPrinterSetupWizardSheet::OnProcessEvent(WPARAM inWParam, LPARAM inLParam)
627 {
628 DEBUG_UNUSED(inWParam);
629 DEBUG_UNUSED(inLParam);
630
631 m_processFinished = true;
632
633 return 0;
634 }
635
636
637 void DNSSD_API
638 CPrinterSetupWizardSheet::OnBrowse(
639 DNSServiceRef inRef,
640 DNSServiceFlags inFlags,
641 uint32_t inInterfaceIndex,
642 DNSServiceErrorType inErrorCode,
643 const char * inName,
644 const char * inType,
645 const char * inDomain,
646 void * inContext )
647 {
648 DEBUG_UNUSED(inRef);
649
650 CPrinterSetupWizardSheet * self;
651 Printer * printer;
652 EventHandlerList::iterator it;
653 DWORD printerNameCount;
654 bool moreComing = (bool) (inFlags & kDNSServiceFlagsMoreComing);
655
656 require_noerr( inErrorCode, exit );
657
658 self = reinterpret_cast <CPrinterSetupWizardSheet*>( inContext );
659 require_quiet( self, exit );
660
661 printer = self->LookUp(inName);
662
663 if (inFlags & kDNSServiceFlagsAdd)
664 {
665 OSStatus err;
666
667 if (printer != NULL)
668 {
669 printer->refs++;
670 }
671 else
672 {
673 try
674 {
675 printer = new Printer;
676 }
677 catch (...)
678 {
679 printer = NULL;
680 }
681
682 require_action( printer, exit, err = E_OUTOFMEMORY );
683
684 printer->window = self;
685 printer->ifi = inInterfaceIndex;
686 printer->name = inName;
687 err = UTF8StringToStringObject(inName, printer->displayName);
688 check_noerr( err );
689 printer->actualName = printer->displayName;
690
691 //
692 // Compare this name against printers that are already installed
693 // to avoid name clashes. Rename as necessary
694 // to come up with a unique name.
695 //
696 printerNameCount = 2;
697
698 for (;;)
699 {
700 PrinterNameMap::iterator it;
701
702 it = self->m_printerNames.find(printer->actualName);
703
704 if (it != self->m_printerNames.end())
705 {
706 printer->actualName.Format(L"%s (%d)", printer->displayName, printerNameCount);
707 }
708 else
709 {
710 break;
711 }
712
713 printerNameCount++;
714 }
715
716 printer->type = inType;
717 printer->domain = inDomain;
718 printer->installed = false;
719 printer->deflt = false;
720 printer->refs = 1;
721
722 self->m_printerList.push_back( printer );
723
724 //
725 // now invoke event handlers for AddPrinter event
726 //
727 for (it = self->m_eventHandlerList.begin(); it != self->m_eventHandlerList.end(); it++)
728 {
729 EventHandler * handler = *it;
730
731 handler->OnAddPrinter(printer, moreComing);
732 }
733 }
734 }
735 else
736 {
737 if ((printer != NULL) && (--printer->refs == 0))
738 {
739 //
740 // now invoke event handlers for RemovePrinter event
741 //
742 for (it = self->m_eventHandlerList.begin(); it != self->m_eventHandlerList.end(); it++)
743 {
744 EventHandler * handler = *it;
745
746 handler->OnRemovePrinter(printer, moreComing);
747 }
748
749 self->m_printerList.remove(printer);
750
751 //
752 // check to see if we've selected this printer
753 //
754 if (self->m_selectedPrinter == printer)
755 {
756 //
757 // this guy is being removed while we're resolving it...so let's
758 // stop the resolve
759 //
760 if (printer->serviceRef != NULL)
761 {
762 self->StopResolve(printer);
763 }
764
765 self->m_selectedPrinter = NULL;
766 }
767
768 delete printer;
769 }
770 }
771
772 exit:
773
774 return;
775 }
776
777
778 void DNSSD_API
779 CPrinterSetupWizardSheet::OnResolve(
780 DNSServiceRef inRef,
781 DNSServiceFlags inFlags,
782 uint32_t inInterfaceIndex,
783 DNSServiceErrorType inErrorCode,
784 const char * inFullName,
785 const char * inHostName,
786 uint16_t inPort,
787 uint16_t inTXTSize,
788 const char * inTXT,
789 void * inContext )
790 {
791 DEBUG_UNUSED(inFullName);
792 DEBUG_UNUSED(inInterfaceIndex);
793 DEBUG_UNUSED(inFlags);
794 DEBUG_UNUSED(inRef);
795
796 Printer * printer;
797 CPrinterSetupWizardSheet * self;
798 EventHandlerList::iterator it1;
799 EventHandlerList::iterator it2;
800 int idx;
801 OSStatus err;
802
803 require_noerr( inErrorCode, exit );
804
805 printer = reinterpret_cast<Printer*>( inContext );
806 require_quiet( printer, exit);
807
808 self = printer->window;
809 require_quiet( self, exit );
810
811 err = self->StopResolve(printer);
812 require_noerr(err, exit);
813
814 //
815 // hold on to the hostname...
816 //
817 err = UTF8StringToStringObject( inHostName, printer->hostname );
818 require_noerr( err, exit );
819
820 //
821 // <rdar://problem/3739200> remove the trailing dot on hostname
822 //
823 idx = printer->hostname.ReverseFind('.');
824
825 if ((idx > 1) && ((printer->hostname.GetLength() - 1) == idx))
826 {
827 printer->hostname.Delete(idx, 1);
828 }
829
830 //
831 // hold on to the port
832 //
833 printer->portNumber = ntohs(inPort);
834
835 //
836 // parse the text record. we create a stringlist of text record
837 // entries that can be interrogated later
838 //
839 while (inTXTSize)
840 {
841 char buf[256];
842
843 unsigned char num = *inTXT;
844 check( (int) num < inTXTSize );
845
846 memset(buf, 0, sizeof(buf));
847 memcpy(buf, inTXT + 1, num);
848
849 inTXTSize -= (num + 1);
850 inTXT += (num + 1);
851
852 CString elem;
853
854 err = UTF8StringToStringObject( buf, elem );
855 require_noerr( err, exit );
856
857 int curPos = 0;
858
859 CString key = elem.Tokenize(L"=", curPos);
860 CString val = elem.Tokenize(L"=", curPos);
861
862 key.MakeLower();
863
864 if ((key == L"usb_mfg") || (key == L"usb_manufacturer"))
865 {
866 printer->usb_MFG = val;
867 }
868 else if ((key == L"usb_mdl") || (key == L"usb_model"))
869 {
870 printer->usb_MDL = val;
871 }
872 else if (key == L"description")
873 {
874 printer->description = val;
875 }
876 else if (key == L"product")
877 {
878 printer->product = val;
879 }
880 }
881
882 //
883 // now invoke event handlers for Resolve event
884 //
885 it1 = self->m_eventHandlerList.begin();
886 it2 = self->m_eventHandlerList.end();
887
888 while (it1 != it2)
889 {
890 EventHandler * handler = *it1++;
891
892 handler->OnResolvePrinter(printer);
893 }
894
895 exit:
896
897 return;
898 }
899
900
901 OSStatus
902 CPrinterSetupWizardSheet::LoadPrinterNames()
903 {
904 PBYTE buffer = NULL;
905 OSStatus err = 0;
906
907 //
908 // rdar://problem/3701926 - Printer can't be installed twice
909 //
910 // First thing we want to do is make sure the printer isn't already installed.
911 // If the printer name is found, we'll try and rename it until we
912 // find a unique name
913 //
914 DWORD dwNeeded = 0, dwNumPrinters = 0;
915
916 BOOL ok = EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 4, NULL, 0, &dwNeeded, &dwNumPrinters);
917 err = translate_errno( ok, errno_compat(), kUnknownErr );
918
919 if ((err == ERROR_INSUFFICIENT_BUFFER) && (dwNeeded > 0))
920 {
921 try
922 {
923 buffer = new unsigned char[dwNeeded];
924 }
925 catch (...)
926 {
927 buffer = NULL;
928 }
929
930 require_action( buffer, exit, kNoMemoryErr );
931 ok = EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 4, buffer, dwNeeded, &dwNeeded, &dwNumPrinters);
932 err = translate_errno( ok, errno_compat(), kUnknownErr );
933 require_noerr( err, exit );
934
935 for (DWORD index = 0; index < dwNumPrinters; index++)
936 {
937 PRINTER_INFO_4 * lppi4 = (PRINTER_INFO_4*) (buffer + index * sizeof(PRINTER_INFO_4));
938
939 m_printerNames[lppi4->pPrinterName] = lppi4->pPrinterName;
940 }
941 }
942
943 exit:
944
945 if (buffer != NULL)
946 {
947 delete [] buffer;
948 }
949
950 return err;
951 }
952
953
954 OSStatus
955 CPrinterSetupWizardSheet::StartResolve(Printer * printer)
956 {
957 OSStatus err;
958
959 check( printer );
960
961 err = DNSServiceResolve( &printer->serviceRef, 0, 0, printer->name.c_str(), printer->type.c_str(), printer->domain.c_str(), (DNSServiceResolveReply) OnResolve, printer );
962 require_noerr( err, exit);
963
964 m_serviceRefList.push_back(printer->serviceRef);
965
966 err = WSAAsyncSelect((SOCKET) DNSServiceRefSockFD(printer->serviceRef), m_hWnd, WM_SERVICE_EVENT, FD_READ|FD_CLOSE);
967 require_noerr( err, exit );
968
969 //
970 // set the cursor to arrow+hourglass
971 //
972 m_active = m_wait;
973 SetCursor(m_active);
974
975 exit:
976
977 return err;
978 }
979
980
981 OSStatus
982 CPrinterSetupWizardSheet::StopResolve(Printer * printer)
983 {
984 OSStatus err;
985
986 check( printer );
987 check( printer->serviceRef );
988
989 m_serviceRefList.remove( printer->serviceRef );
990
991 err = WSAAsyncSelect((SOCKET) DNSServiceRefSockFD(printer->serviceRef), m_hWnd, 0, 0);
992 check(err == 0);
993
994 DNSServiceRefDeallocate( printer->serviceRef );
995
996 printer->serviceRef = NULL;
997
998 //
999 // set the cursor back to normal
1000 //
1001 m_active = m_arrow;
1002 SetCursor(m_active);
1003
1004 return kNoErr;
1005 }
1006
1007
1008 Printer*
1009 CPrinterSetupWizardSheet::LookUp(const char * inName)
1010 {
1011 PrinterList::iterator it1 = m_printerList.begin();
1012 PrinterList::iterator it2 = m_printerList.end();
1013
1014 while (it1 != it2)
1015 {
1016 Printer * printer = *it1++;
1017
1018 if (printer->name == inName)
1019 {
1020 return printer;
1021 }
1022 }
1023
1024 return NULL;
1025 }
1026
1027
1028 unsigned WINAPI
1029 CPrinterSetupWizardSheet::InstallPrinterThread( LPVOID inParam )
1030 {
1031 check( inParam );
1032
1033 Printer * printer = (Printer*) inParam;
1034 CString actualName;
1035 CString command;
1036 DWORD exitCode = 0;
1037 DWORD dwResult;
1038 OSStatus err;
1039 STARTUPINFO si;
1040 PROCESS_INFORMATION pi;
1041 BOOL ok;
1042
1043 ZeroMemory( &si, sizeof(si) );
1044 si.cb = sizeof(si);
1045 ZeroMemory( &pi, sizeof(pi) );
1046
1047 //
1048 // <rdar://problem/3764873> Escape '\', '@', '"' characters which seem to cause problems for printui
1049 //
1050
1051 actualName = printer->actualName;
1052
1053 actualName.Replace(L"\\", L"\\\\");
1054 actualName.Replace(L"@", L"\\@");
1055 actualName.Replace(L"\"", L"\\\"");
1056
1057 command.Format(L"rundll32.exe printui.dll,PrintUIEntry /if /b \"%s\" /f \"%s\" /r \"%s\" /m \"%s\"", (LPCTSTR) actualName, (LPCTSTR) printer->infFileName, (LPCTSTR) printer->portName, (LPCTSTR) printer->model);
1058
1059 ok = CreateProcess(NULL, command.GetBuffer(), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
1060 err = translate_errno( ok, errno_compat(), kUnknownErr );
1061 require_noerr( err, exit );
1062
1063 dwResult = WaitForSingleObject( pi.hProcess, INFINITE );
1064 translate_errno( dwResult == WAIT_OBJECT_0, errno_compat(), err = kUnknownErr );
1065 require_noerr( err, exit );
1066
1067 ok = GetExitCodeProcess( pi.hProcess, &exitCode );
1068 err = translate_errno( ok, errno_compat(), kUnknownErr );
1069 require_noerr( err, exit );
1070
1071 //
1072 // Close process and thread handles.
1073 //
1074 CloseHandle( pi.hProcess );
1075 CloseHandle( pi.hThread );
1076
1077 exit:
1078
1079 //
1080 // alert the main thread
1081 //
1082 printer->window->PostMessage( WM_PROCESS_EVENT, err, exitCode );
1083
1084 return 0;
1085 }