]> git.saurik.com Git - apple/mdnsresponder.git/blob - Clients/PrinterSetupWizard/SecondPage.cpp
mDNSResponder-98.tar.gz
[apple/mdnsresponder.git] / Clients / PrinterSetupWizard / SecondPage.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: SecondPage.cpp,v $
26 Revision 1.10 2005/01/20 19:54:38 shersche
27 Fix parse error when text record is NULL
28
29 Revision 1.9 2005/01/06 08:13:50 shersche
30 Don't use moreComing flag to determine number of text record, disregard queue name if qtotal isn't defined, don't disregard queue name if "rp" is the only key specified
31
32 Revision 1.8 2005/01/04 21:09:14 shersche
33 Fix problems in parsing text records. Fix problems in remove event handling. Ensure that the same service can't be resolved more than once.
34
35 Revision 1.7 2004/12/31 07:25:27 shersche
36 Tidy up printer management, and fix memory leaks when hitting 'Cancel'
37
38 Revision 1.6 2004/12/30 01:24:02 shersche
39 <rdar://problem/3906182> Remove references to description key
40 Bug #: 3906182
41
42 Revision 1.5 2004/12/30 01:02:47 shersche
43 <rdar://problem/3734478> Add Printer information box that displays description and location information when printer name is selected
44 Bug #: 3734478
45
46 Revision 1.4 2004/12/29 18:53:38 shersche
47 <rdar://problem/3725106>
48 <rdar://problem/3737413> Added support for LPR and IPP protocols as well as support for obtaining multiple text records. Reorganized and simplified codebase.
49 Bug #: 3725106, 3737413
50
51 Revision 1.3 2004/09/13 21:26:15 shersche
52 <rdar://problem/3796483> Use the moreComing flag to determine whether drawing should take place in OnAddPrinter and OnRemovePrinter callbacks
53 Bug #: 3796483
54
55 Revision 1.2 2004/06/26 03:19:57 shersche
56 clean up warning messages
57
58 Submitted by: herscher
59
60 Revision 1.1 2004/06/18 04:36:57 rpantos
61 First checked in
62
63
64 */
65
66 #include "stdafx.h"
67 #include "PrinterSetupWizardApp.h"
68 #include "PrinterSetupWizardSheet.h"
69 #include "SecondPage.h"
70 #include "DebugServices.h"
71 #include "WinServices.h"
72 #include <winspool.h>
73
74 // local variable is initialize but not referenced
75 #pragma warning(disable:4189)
76
77 #define WM_SERVICE_EVENT ( WM_USER + 0x101 )
78
79 // CSecondPage dialog
80
81 IMPLEMENT_DYNAMIC(CSecondPage, CPropertyPage)
82 CSecondPage::CSecondPage()
83 : CPropertyPage(CSecondPage::IDD),
84 m_pdlBrowser( NULL ),
85 m_lprBrowser( NULL ),
86 m_ippBrowser( NULL ),
87 m_selected( NULL )
88 {
89 m_psp.dwFlags &= ~(PSP_HASHELP);
90 m_psp.dwFlags |= PSP_DEFAULT|PSP_USEHEADERTITLE|PSP_USEHEADERSUBTITLE;
91
92 m_psp.pszHeaderTitle = MAKEINTRESOURCE(IDS_BROWSE_TITLE);
93 m_psp.pszHeaderSubTitle = MAKEINTRESOURCE(IDS_BROWSE_SUBTITLE);
94
95 m_resolver = NULL;
96 m_emptyListItem = NULL;
97 m_initialized = false;
98 m_waiting = false;
99
100 LoadPrinterNames();
101 }
102
103
104 CSecondPage::~CSecondPage()
105 {
106 StopBrowse();
107 }
108
109
110 OSStatus
111 CSecondPage::LoadPrinterNames()
112 {
113 PBYTE buffer = NULL;
114 OSStatus err = 0;
115
116 //
117 // rdar://problem/3701926 - Printer can't be installed twice
118 //
119 // First thing we want to do is make sure the printer isn't already installed.
120 // If the printer name is found, we'll try and rename it until we
121 // find a unique name
122 //
123 DWORD dwNeeded = 0, dwNumPrinters = 0;
124
125 BOOL ok = EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 4, NULL, 0, &dwNeeded, &dwNumPrinters);
126 err = translate_errno( ok, errno_compat(), kUnknownErr );
127
128 if ((err == ERROR_INSUFFICIENT_BUFFER) && (dwNeeded > 0))
129 {
130 try
131 {
132 buffer = new unsigned char[dwNeeded];
133 }
134 catch (...)
135 {
136 buffer = NULL;
137 }
138
139 require_action( buffer, exit, kNoMemoryErr );
140 ok = EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 4, buffer, dwNeeded, &dwNeeded, &dwNumPrinters);
141 err = translate_errno( ok, errno_compat(), kUnknownErr );
142 require_noerr( err, exit );
143
144 for (DWORD index = 0; index < dwNumPrinters; index++)
145 {
146 PRINTER_INFO_4 * lppi4 = (PRINTER_INFO_4*) (buffer + index * sizeof(PRINTER_INFO_4));
147
148 m_printerNames[lppi4->pPrinterName] = lppi4->pPrinterName;
149 }
150 }
151
152 exit:
153
154 if (buffer != NULL)
155 {
156 delete [] buffer;
157 }
158
159 return err;
160 }
161
162
163 void
164 CSecondPage::InitBrowseList()
165 {
166 CPrinterSetupWizardSheet * psheet;
167 CString text;
168
169 psheet = reinterpret_cast<CPrinterSetupWizardSheet*>(GetParent());
170 require_quiet( psheet, exit );
171
172 //
173 // load the no rendezvous printers message until something shows up in the browse list
174 //
175 text.LoadString(IDS_NO_RENDEZVOUS_PRINTERS);
176
177 LoadTextAndDisableWindow( text );
178
179 //
180 // disable the next button until there's a printer to select
181 //
182 psheet->SetWizardButtons(PSWIZB_BACK);
183
184 //
185 // disable the printer information box
186 //
187 SetPrinterInformationState( FALSE );
188
189 exit:
190
191 return;
192 }
193
194
195 OSStatus
196 CSecondPage::StartOperation( DNSServiceRef ref )
197 {
198 OSStatus err;
199
200 err = WSAAsyncSelect((SOCKET) DNSServiceRefSockFD(ref), m_hWnd, WM_SERVICE_EVENT, FD_READ|FD_CLOSE);
201 require_noerr( err, exit );
202
203 m_serviceRefList.push_back( ref );
204
205 exit:
206
207 return err;
208 }
209
210
211 OSStatus
212 CSecondPage::StopOperation( DNSServiceRef & ref )
213 {
214 OSStatus err = kNoErr;
215
216 if ( ref )
217 {
218 m_serviceRefList.remove( ref );
219
220 if ( IsWindow( m_hWnd ) )
221 {
222 err = WSAAsyncSelect((SOCKET) DNSServiceRefSockFD( ref ), m_hWnd, 0, 0 );
223 require_noerr( err, exit );
224 }
225
226 DNSServiceRefDeallocate( ref );
227 ref = NULL;
228 }
229
230 exit:
231
232 return err;
233 }
234
235
236 Printer*
237 CSecondPage::Lookup(const char * inName)
238 {
239 check( IsWindow( m_hWnd ) );
240 check( inName );
241
242 HTREEITEM item = m_browseList.GetChildItem( TVI_ROOT );
243 while ( item )
244 {
245 Printer * printer;
246 DWORD_PTR data;
247
248 data = m_browseList.GetItemData( item );
249 printer = reinterpret_cast<Printer*>(data);
250
251 if ( printer && ( printer->name == inName ) )
252 {
253 return printer;
254 }
255
256 item = m_browseList.GetNextItem( item, TVGN_NEXT );
257 }
258
259 return NULL;
260 }
261
262
263 OSStatus
264 CSecondPage::StartBrowse()
265 {
266 OSStatus err;
267
268 //
269 // setup the DNS-SD browsing
270 //
271 err = DNSServiceBrowse( &m_pdlBrowser, 0, 0, kPDLServiceType, NULL, OnBrowse, this );
272 require_noerr( err, exit );
273
274 err = StartOperation( m_pdlBrowser );
275 require_noerr( err, exit );
276
277 err = DNSServiceBrowse( &m_lprBrowser, 0, 0, kLPRServiceType, NULL, OnBrowse, this );
278 require_noerr( err, exit );
279
280 err = StartOperation( m_lprBrowser );
281 require_noerr( err, exit );
282
283 err = DNSServiceBrowse( &m_ippBrowser, 0, 0, kIPPServiceType, NULL, OnBrowse, this );
284 require_noerr( err, exit );
285
286 err = StartOperation( m_ippBrowser );
287 require_noerr( err, exit );
288
289 exit:
290
291 return err;
292 }
293
294
295 OSStatus
296 CSecondPage::StopBrowse()
297 {
298 OSStatus err;
299
300 err = StopOperation( m_pdlBrowser );
301 require_noerr( err, exit );
302
303 err = StopOperation( m_lprBrowser );
304 require_noerr( err, exit );
305
306 err = StopOperation( m_ippBrowser );
307 require_noerr( err, exit );
308
309 while ( m_printers.size() > 0 )
310 {
311 Printer * printer = m_printers.front();
312
313 m_printers.pop_front();
314
315 if ( printer->resolving )
316 {
317 StopResolve( printer );
318 }
319
320 delete printer;
321 }
322
323 exit:
324
325 return err;
326 }
327
328
329 OSStatus
330 CSecondPage::StartResolve( Printer * printer )
331 {
332 CPrinterSetupWizardSheet * psheet;
333 OSStatus err = kNoErr;
334 Services::iterator it;
335
336 psheet = reinterpret_cast<CPrinterSetupWizardSheet*>(GetParent());
337 require_quiet( psheet, exit );
338
339 check( printer );
340
341 for ( it = printer->services.begin(); it != printer->services.end(); it++ )
342 {
343 if ( (*it)->serviceRef == NULL )
344 {
345 err = StartResolve( *it );
346 require_noerr( err, exit );
347 }
348 }
349
350 exit:
351
352 return err;
353 }
354
355
356 OSStatus
357 CSecondPage::StartResolve( Service * service )
358 {
359 CPrinterSetupWizardSheet * psheet;
360 OSStatus err = kNoErr;
361
362 psheet = reinterpret_cast<CPrinterSetupWizardSheet*>(GetParent());
363 require_quiet( psheet, exit );
364
365 check( service->serviceRef == NULL );
366
367 //
368 // clean out any queues that were collected during a previous
369 // resolve
370 //
371
372 service->EmptyQueues();
373
374 //
375 // now start the new resolve
376 //
377
378 err = DNSServiceResolve( &service->serviceRef, 0, 0, service->printer->name.c_str(), service->type.c_str(), service->domain.c_str(), (DNSServiceResolveReply) OnResolve, service );
379 require_noerr( err, exit );
380
381 err = StartOperation( service->serviceRef );
382 require_noerr( err, exit );
383
384 //
385 // If we're not currently resolving, then disable the next button
386 // and set the cursor to hourglass
387 //
388
389 if ( !service->printer->resolving )
390 {
391 psheet->SetWizardButtons( PSWIZB_BACK );
392
393 psheet->m_active = psheet->m_wait;
394 SetCursor(psheet->m_active);
395 }
396
397 service->printer->resolving++;
398
399 exit:
400
401 return err;
402 }
403
404
405 OSStatus
406 CSecondPage::StopResolve(Printer * printer)
407 {
408 OSStatus err = kNoErr;
409
410 check( printer );
411
412 Services::iterator it;
413
414 for ( it = printer->services.begin(); it != printer->services.end(); it++ )
415 {
416 if ( (*it)->serviceRef )
417 {
418 err = StopResolve( *it );
419 require_noerr( err, exit );
420 }
421 }
422
423 exit:
424
425 return err;
426 }
427
428
429 OSStatus
430 CSecondPage::StopResolve( Service * service )
431 {
432 OSStatus err;
433
434 check( service->serviceRef );
435
436 err = StopOperation( service->serviceRef );
437 require_noerr( err, exit );
438
439 service->printer->resolving--;
440
441 exit:
442
443 return err;
444 }
445
446
447 void CSecondPage::DoDataExchange(CDataExchange* pDX)
448 {
449 CPropertyPage::DoDataExchange(pDX);
450 DDX_Control(pDX, IDC_BROWSE_LIST, m_browseList);
451 DDX_Control(pDX, IDC_PRINTER_INFORMATION, m_printerInformation);
452 DDX_Control(pDX, IDC_DESCRIPTION_LABEL, m_descriptionLabel);
453 DDX_Control(pDX, IDC_DESCRIPTION_FIELD, m_descriptionField);
454 DDX_Control(pDX, IDC_LOCATION_LABEL, m_locationLabel);
455 DDX_Control(pDX, IDC_LOCATION_FIELD, m_locationField);
456 }
457
458
459 afx_msg BOOL
460 CSecondPage::OnSetCursor(CWnd * pWnd, UINT nHitTest, UINT message)
461 {
462 DEBUG_UNUSED(pWnd);
463 DEBUG_UNUSED(nHitTest);
464 DEBUG_UNUSED(message);
465
466 CPrinterSetupWizardSheet * psheet;
467
468 psheet = reinterpret_cast<CPrinterSetupWizardSheet*>(GetParent());
469 require_quiet( psheet, exit );
470
471 SetCursor(psheet->GetCursor());
472
473 exit:
474
475 return TRUE;
476 }
477
478
479 BOOL
480 CSecondPage::OnSetActive()
481 {
482 CPrinterSetupWizardSheet * psheet;
483 Printer * printer;
484 OSStatus err = kNoErr;
485
486 psheet = reinterpret_cast<CPrinterSetupWizardSheet*>(GetParent());
487 require_action( psheet, exit, err = kUnknownErr );
488
489 if ( ( printer = psheet->GetSelectedPrinter() ) != NULL )
490 {
491 psheet->SetSelectedPrinter( NULL );
492 delete printer;
493 }
494
495 //
496 // initialize the browse list...this will remove everything currently
497 // in it, and add the no rendezvous printers item
498 //
499 InitBrowseList();
500
501 //
502 // start browing
503 //
504 err = StartBrowse();
505 require_noerr( err, exit );
506
507 exit:
508
509 if ( err != kNoErr )
510 {
511 if ( err == kDNSServiceErr_Firewall )
512 {
513 CString text, caption;
514
515 text.LoadString( IDS_FIREWALL );
516 caption.LoadString( IDS_FIREWALL_CAPTION );
517
518 MessageBox(text, caption, MB_OK|MB_ICONEXCLAMATION);
519 }
520 else
521 {
522 CPrinterSetupWizardSheet::WizardException exc;
523
524 exc.text.LoadString( IDS_NO_MDNSRESPONDER_SERVICE_TEXT );
525 exc.caption.LoadString( IDS_ERROR_CAPTION );
526
527 throw(exc);
528 }
529 }
530
531 return CPropertyPage::OnSetActive();
532 }
533
534
535 BOOL
536 CSecondPage::OnKillActive()
537 {
538 OSStatus err = kNoErr;
539
540 if ( m_selected )
541 {
542 CPrinterSetupWizardSheet * psheet;
543
544 psheet = reinterpret_cast<CPrinterSetupWizardSheet*>(GetParent());
545 require_quiet( psheet, exit );
546
547 psheet->SetSelectedPrinter( m_selected );
548 m_printers.remove( m_selected );
549 m_selected = NULL;
550 }
551
552 err = StopBrowse();
553 require_noerr( err, exit );
554
555 exit:
556
557 return CPropertyPage::OnKillActive();
558 }
559
560
561 void DNSSD_API
562 CSecondPage::OnBrowse(
563 DNSServiceRef inRef,
564 DNSServiceFlags inFlags,
565 uint32_t inInterfaceIndex,
566 DNSServiceErrorType inErrorCode,
567 const char * inName,
568 const char * inType,
569 const char * inDomain,
570 void * inContext )
571 {
572 DEBUG_UNUSED(inRef);
573
574 CSecondPage * self;
575 bool moreComing = (bool) (inFlags & kDNSServiceFlagsMoreComing);
576
577 require_noerr( inErrorCode, exit );
578
579 self = reinterpret_cast <CSecondPage*>( inContext );
580 require_quiet( self, exit );
581
582 if ( inFlags & kDNSServiceFlagsAdd )
583 {
584 self->OnAddPrinter( inInterfaceIndex, inName, inType, inDomain, moreComing );
585 }
586 else
587 {
588 self->OnRemovePrinter( inName, inType, inDomain, moreComing );
589 }
590
591 exit:
592
593 return;
594 }
595
596
597 void DNSSD_API
598 CSecondPage::OnResolve(
599 DNSServiceRef inRef,
600 DNSServiceFlags inFlags,
601 uint32_t inInterfaceIndex,
602 DNSServiceErrorType inErrorCode,
603 const char * inFullName,
604 const char * inHostName,
605 uint16_t inPort,
606 uint16_t inTXTSize,
607 const char * inTXT,
608 void * inContext )
609 {
610 DEBUG_UNUSED(inFullName);
611 DEBUG_UNUSED(inInterfaceIndex);
612 DEBUG_UNUSED(inFlags);
613 DEBUG_UNUSED(inRef);
614
615 CSecondPage * self;
616 Service * service;
617 Queue * q;
618 bool qtotalDefined = false;
619 uint32_t qpriority = kDefaultPriority;
620 CString qname;
621 int idx;
622 OSStatus err;
623
624 require_noerr( inErrorCode, exit );
625
626 service = reinterpret_cast<Service*>( inContext );
627 require_quiet( service, exit);
628
629 check( service->refs != 0 );
630
631 self = service->printer->window;
632 require_quiet( self, exit );
633
634 err = self->StopOperation( service->serviceRef );
635 require_noerr( err, exit );
636
637 //
638 // hold on to the hostname...
639 //
640 err = UTF8StringToStringObject( inHostName, service->hostname );
641 require_noerr( err, exit );
642
643 //
644 // <rdar://problem/3739200> remove the trailing dot on hostname
645 //
646 idx = service->hostname.ReverseFind('.');
647
648 if ((idx > 1) && ((service->hostname.GetLength() - 1) == idx))
649 {
650 service->hostname.Delete(idx, 1);
651 }
652
653 //
654 // hold on to the port
655 //
656 service->portNumber = ntohs(inPort);
657
658 //
659 // parse the text record.
660 //
661
662 err = self->ParseTextRecord( service, inTXTSize, inTXT, qtotalDefined, qname, qpriority );
663 require_noerr( err, exit );
664
665 if ( service->qtotal == 1 )
666 {
667 //
668 // create a new queue
669 //
670 try
671 {
672 q = new Queue;
673 }
674 catch (...)
675 {
676 q = NULL;
677 }
678
679 require_action( q, exit, err = E_OUTOFMEMORY );
680
681 if ( qtotalDefined )
682 {
683 q->name = qname;
684 }
685
686 q->priority = qpriority;
687
688 service->queues.push_back( q );
689
690 //
691 // we've completely resolved this service
692 //
693
694 self->OnResolveService( service );
695 }
696 else
697 {
698 //
699 // if qtotal is more than 1, then we need to get additional
700 // text records. if not, then this service is considered
701 // resolved
702 //
703
704 err = DNSServiceQueryRecord(&service->serviceRef, 0, inInterfaceIndex, inFullName, kDNSServiceType_TXT, kDNSServiceClass_IN, OnQuery, (void*) service );
705 require_noerr( err, exit );
706
707 err = self->StartOperation( service->serviceRef );
708 require_noerr( err, exit );
709 }
710
711 exit:
712
713 return;
714 }
715
716
717 void DNSSD_API
718 CSecondPage::OnQuery(
719 DNSServiceRef inRef,
720 DNSServiceFlags inFlags,
721 uint32_t inInterfaceIndex,
722 DNSServiceErrorType inErrorCode,
723 const char * inFullName,
724 uint16_t inRRType,
725 uint16_t inRRClass,
726 uint16_t inRDLen,
727 const void * inRData,
728 uint32_t inTTL,
729 void * inContext)
730 {
731 DEBUG_UNUSED( inTTL );
732 DEBUG_UNUSED( inRRClass );
733 DEBUG_UNUSED( inRRType );
734 DEBUG_UNUSED( inFullName );
735 DEBUG_UNUSED( inInterfaceIndex );
736 DEBUG_UNUSED( inRef );
737
738 Service * service = NULL;
739 Queue * q;
740 CSecondPage * self;
741 bool qtotalDefined = false;
742 bool moreComing = (bool) (inFlags & kDNSServiceFlagsMoreComing);
743 OSStatus err = kNoErr;
744
745 require_noerr( inErrorCode, exit );
746
747 service = reinterpret_cast<Service*>( inContext );
748 require_quiet( service, exit);
749
750 self = service->printer->window;
751 require_quiet( self, exit );
752
753 if ( ( inFlags & kDNSServiceFlagsAdd ) && ( inRDLen > 0 ) && ( inRData != NULL ) )
754 {
755 const char * inTXT = ( const char * ) inRData;
756
757 //
758 // create a new queue
759 //
760 try
761 {
762 q = new Queue;
763 }
764 catch (...)
765 {
766 q = NULL;
767 }
768
769 require_action( q, exit, err = E_OUTOFMEMORY );
770
771 err = service->printer->window->ParseTextRecord( service, inRDLen, inTXT, qtotalDefined, q->name, q->priority );
772 require_noerr( err, exit );
773
774 if ( !qtotalDefined )
775 {
776 q->name = L"";
777 }
778
779 //
780 // add this queue
781 //
782
783 service->queues.push_back( q );
784
785 if ( service->queues.size() == service->qtotal )
786 {
787 //
788 // else if moreComing is not set, then we're going
789 // to assume that we're done
790 //
791
792 self->StopOperation( service->serviceRef );
793
794 //
795 // sort the queues
796 //
797
798 service->queues.sort( OrderQueueFunc );
799
800 //
801 // we've completely resolved this service
802 //
803
804 self->OnResolveService( service );
805 }
806 }
807
808 exit:
809
810 if ( err && service && ( service->serviceRef != NULL ) )
811 {
812 service->printer->window->StopOperation( service->serviceRef );
813 }
814
815 return;
816 }
817
818
819 BEGIN_MESSAGE_MAP(CSecondPage, CPropertyPage)
820 ON_MESSAGE( WM_SERVICE_EVENT, OnServiceEvent )
821 ON_NOTIFY(TVN_SELCHANGED, IDC_BROWSE_LIST, OnTvnSelchangedBrowseList)
822 ON_WM_SETCURSOR()
823 END_MESSAGE_MAP()
824
825
826 // Printer::EventHandler implementation
827 OSStatus
828 CSecondPage::OnAddPrinter(
829 uint32_t inInterfaceIndex,
830 const char * inName,
831 const char * inType,
832 const char * inDomain,
833 bool moreComing)
834 {
835 Printer * printer;
836 Service * service;
837 CPrinterSetupWizardSheet * psheet;
838 DWORD printerNameCount;
839 bool newPrinter = false;
840 OSStatus err = kNoErr;
841
842 check( IsWindow( m_hWnd ) );
843
844 m_browseList.SetRedraw(FALSE);
845
846 psheet = reinterpret_cast<CPrinterSetupWizardSheet*>(GetParent());
847 require_quiet( psheet, exit );
848
849 printer = Lookup( inName );
850
851 if (printer == NULL)
852 {
853 try
854 {
855 printer = new Printer;
856 }
857 catch (...)
858 {
859 printer = NULL;
860 }
861
862 require_action( printer, exit, err = E_OUTOFMEMORY );
863
864 printer->window = this;
865 printer->name = inName;
866
867 err = UTF8StringToStringObject(inName, printer->displayName);
868 check_noerr( err );
869 printer->actualName = printer->displayName;
870 printer->installed = false;
871 printer->deflt = false;
872 printer->resolving = 0;
873
874 //
875 // Compare this name against printers that are already installed
876 // to avoid name clashes. Rename as necessary
877 // to come up with a unique name.
878 //
879 printerNameCount = 2;
880
881 for (;;)
882 {
883 PrinterNameMap::iterator it;
884
885 it = m_printerNames.find(printer->actualName);
886
887 if (it != m_printerNames.end())
888 {
889 printer->actualName.Format(L"%s (%d)", printer->displayName, printerNameCount);
890 }
891 else
892 {
893 break;
894 }
895
896 printerNameCount++;
897 }
898
899 newPrinter = true;
900 }
901
902 check( printer );
903
904 service = printer->LookupService( inType );
905
906 if ( service != NULL )
907 {
908 service->refs++;
909 }
910 else
911 {
912 try
913 {
914 service = new Service;
915 }
916 catch (...)
917 {
918 service = NULL;
919 }
920
921 require_action( service, exit, err = E_OUTOFMEMORY );
922
923 service->printer = printer;
924 service->ifi = inInterfaceIndex;
925 service->type = inType;
926 service->domain = inDomain;
927 service->qtotal = 1;
928 service->refs = 1;
929 service->serviceRef = NULL;
930
931 printer->services.push_back( service );
932
933 //
934 // if the printer is selected, then we'll want to start a
935 // resolve on this guy
936 //
937
938 if ( m_selected == printer )
939 {
940 StartResolve( service );
941 }
942 }
943
944 if ( newPrinter )
945 {
946 printer->item = m_browseList.InsertItem(printer->displayName);
947
948 m_browseList.SetItemData( printer->item, (DWORD_PTR) printer );
949
950 m_printers.push_back( printer );
951
952 m_browseList.SortChildren(TVI_ROOT);
953
954 if ( printer->name == m_selectedName )
955 {
956 m_browseList.SelectItem( printer->item );
957 }
958
959 //
960 // if the searching item is still in the list
961 // get rid of it
962 //
963 // note that order is important here. Insert the printer
964 // item before removing the placeholder so we always have
965 // an item in the list to avoid experiencing the bug
966 // in Microsoft's implementation of CTreeCtrl
967 //
968 if (m_emptyListItem != NULL)
969 {
970 m_browseList.DeleteItem(m_emptyListItem);
971 m_emptyListItem = NULL;
972 m_browseList.EnableWindow(TRUE);
973 }
974 }
975
976 exit:
977
978 if (!moreComing)
979 {
980 m_browseList.SetRedraw(TRUE);
981 m_browseList.Invalidate();
982 }
983
984 return err;
985 }
986
987
988 OSStatus
989 CSecondPage::OnRemovePrinter(
990 const char * inName,
991 const char * inType,
992 const char * inDomain,
993 bool moreComing)
994 {
995 DEBUG_UNUSED( inDomain );
996 DEBUG_UNUSED( inType );
997
998 Printer * printer;
999 OSStatus err = kNoErr;
1000
1001 check( IsWindow( m_hWnd ) );
1002
1003 m_browseList.SetRedraw(FALSE);
1004
1005 printer = Lookup( inName );
1006
1007 if ( printer )
1008 {
1009 Service * service;
1010
1011 service = printer->LookupService( inType );
1012
1013 if ( service && ( --service->refs == 0 ) )
1014 {
1015 if ( service->serviceRef != NULL )
1016 {
1017 err = StopResolve( service );
1018 require_noerr( err, exit );
1019 }
1020
1021 printer->services.remove( service );
1022
1023 delete service;
1024 }
1025
1026 if ( printer->services.size() == 0 )
1027 {
1028 //
1029 // check to make sure if we're the only item in the control...i.e.
1030 // the list size is 1.
1031 //
1032 if (m_browseList.GetCount() > 1)
1033 {
1034 //
1035 // if we're not the only thing in the list, then
1036 // simply remove it from the list
1037 //
1038 m_browseList.DeleteItem( printer->item );
1039 }
1040 else
1041 {
1042 //
1043 // if we're the only thing in the list, then redisplay
1044 // it with the no rendezvous printers message
1045 //
1046 InitBrowseList();
1047 }
1048
1049 m_printers.remove( printer );
1050
1051 if ( m_selected == printer )
1052 {
1053 m_selected = NULL;
1054 m_selectedName = "";
1055 }
1056
1057 delete printer;
1058 }
1059 }
1060
1061 exit:
1062
1063 if (!moreComing)
1064 {
1065 m_browseList.SetRedraw(TRUE);
1066 m_browseList.Invalidate();
1067 }
1068
1069 return err;
1070 }
1071
1072
1073 void
1074 CSecondPage::OnResolveService( Service * service )
1075 {
1076 CPrinterSetupWizardSheet * psheet = reinterpret_cast<CPrinterSetupWizardSheet*>(GetParent());
1077 require_quiet( psheet, exit );
1078
1079 check( service );
1080
1081 if ( !--service->printer->resolving )
1082 {
1083 //
1084 // sort the services now. we want the service that
1085 // has the highest priority queue to be first in
1086 // the list.
1087 //
1088
1089 service->printer->services.sort( OrderServiceFunc );
1090
1091 //
1092 // and set it to selected
1093 //
1094
1095 m_selected = service->printer;
1096 m_selectedName = service->printer->name;
1097
1098 //
1099 // and update the printer information box
1100 //
1101 SetPrinterInformationState( TRUE );
1102
1103 m_descriptionField.SetWindowText( service->description );
1104 m_locationField.SetWindowText( service->location );
1105
1106 psheet->SetWizardButtons( PSWIZB_BACK|PSWIZB_NEXT );
1107
1108 //
1109 // reset the cursor
1110 //
1111
1112 psheet->m_active = psheet->m_arrow;
1113 SetCursor(psheet->m_active);
1114 }
1115
1116 exit:
1117
1118 return;
1119 }
1120
1121
1122 LONG
1123 CSecondPage::OnServiceEvent(WPARAM inWParam, LPARAM inLParam)
1124 {
1125 if (WSAGETSELECTERROR(inLParam) && !(HIWORD(inLParam)))
1126 {
1127 dlog( kDebugLevelError, "OnServiceEvent: window error\n" );
1128 }
1129 else
1130 {
1131 SOCKET sock = (SOCKET) inWParam;
1132
1133 // iterate thru list
1134 ServiceRefList::iterator begin = m_serviceRefList.begin();
1135 ServiceRefList::iterator end = m_serviceRefList.end();
1136
1137 while (begin != end)
1138 {
1139 DNSServiceRef ref = *begin++;
1140
1141 check(ref != NULL);
1142
1143 if ((SOCKET) DNSServiceRefSockFD(ref) == sock)
1144 {
1145 DNSServiceProcessResult(ref);
1146 break;
1147 }
1148 }
1149 }
1150
1151 return ( 0 );
1152 }
1153
1154
1155 void CSecondPage::OnTvnSelchangedBrowseList(NMHDR *pNMHDR, LRESULT *pResult)
1156 {
1157 LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);
1158 CPrinterSetupWizardSheet * psheet;
1159 int err = 0;
1160
1161 HTREEITEM item = m_browseList.GetSelectedItem();
1162 require_quiet( item, exit );
1163
1164 psheet = reinterpret_cast<CPrinterSetupWizardSheet*>(GetParent());
1165 require_action( psheet, exit, err = kUnknownErr );
1166
1167 Printer * printer;
1168
1169 printer = reinterpret_cast<Printer*>(m_browseList.GetItemData( item ) );
1170 require_quiet( printer, exit );
1171
1172 //
1173 // this call will trigger a resolve. When the resolve is complete,
1174 // our OnResolve will be called.
1175 //
1176 err = StartResolve( printer );
1177 require_noerr( err, exit );
1178
1179 //
1180 // And clear out the printer information box
1181 //
1182 SetPrinterInformationState( FALSE );
1183 m_descriptionField.SetWindowText(L"");
1184 m_locationField.SetWindowText(L"");
1185
1186 exit:
1187
1188 if (err != 0)
1189 {
1190 CString text;
1191 CString caption;
1192
1193 text.LoadString(IDS_ERROR_SELECTING_PRINTER_TEXT);
1194 caption.LoadString(IDS_ERROR_SELECTING_PRINTER_CAPTION);
1195
1196 MessageBox(text, caption, MB_OK|MB_ICONEXCLAMATION);
1197 }
1198
1199 *pResult = 0;
1200 }
1201
1202
1203 bool
1204 CSecondPage::OrderServiceFunc( const Service * a, const Service * b )
1205 {
1206 Queue * q1, * q2;
1207
1208 q1 = (a->queues.size() > 0) ? a->queues.front() : NULL;
1209
1210 q2 = (b->queues.size() > 0) ? b->queues.front() : NULL;
1211
1212 if ( !q1 && !q2 )
1213 {
1214 return true;
1215 }
1216 else if ( q1 && !q2 )
1217 {
1218 return true;
1219 }
1220 else if ( !q1 && q2 )
1221 {
1222 return false;
1223 }
1224 else if ( q1->priority < q2->priority )
1225 {
1226 return true;
1227 }
1228 else if ( q1->priority > q2->priority )
1229 {
1230 return false;
1231 }
1232 else if ( ( a->type == kPDLServiceType ) || ( ( a->type == kLPRServiceType ) && ( b->type == kIPPServiceType ) ) )
1233 {
1234 return true;
1235 }
1236 else
1237 {
1238 return false;
1239 }
1240 }
1241
1242
1243 bool
1244 CSecondPage::OrderQueueFunc( const Queue * q1, const Queue * q2 )
1245 {
1246 return ( q1->priority <= q2->priority ) ? true : false;
1247 }
1248
1249
1250 void
1251 CSecondPage::LoadTextAndDisableWindow( CString & text )
1252 {
1253 m_emptyListItem = m_browseList.InsertItem( text, 0, 0, NULL, TVI_FIRST );
1254 m_browseList.SelectItem( NULL );
1255
1256 //
1257 // this will remove everything else in the list...we might be navigating
1258 // back to this window, and the browse list might have changed since
1259 // we last displayed it.
1260 //
1261 if ( m_emptyListItem )
1262 {
1263 HTREEITEM item = m_browseList.GetNextVisibleItem( m_emptyListItem );
1264
1265 while ( item )
1266 {
1267 m_browseList.DeleteItem( item );
1268 item = m_browseList.GetNextVisibleItem( m_emptyListItem );
1269 }
1270 }
1271
1272 m_browseList.EnableWindow( FALSE );
1273 }
1274
1275
1276 void
1277 CSecondPage::SetPrinterInformationState( BOOL state )
1278 {
1279 m_printerInformation.EnableWindow( state );
1280 m_descriptionLabel.EnableWindow( state );
1281 m_descriptionField.EnableWindow( state );
1282 m_locationLabel.EnableWindow( state );
1283 m_locationField.EnableWindow( state );
1284 }
1285
1286
1287 OSStatus
1288 CSecondPage::ParseTextRecord( Service * service, uint16_t inTXTSize, const char * inTXT, bool & qtotalDefined, CString & qname, uint32_t & qpriority )
1289 {
1290 bool rpOnly = true;
1291 OSStatus err = kNoErr;
1292
1293 while (inTXTSize)
1294 {
1295 char buf[256];
1296
1297 unsigned char num = *inTXT;
1298 check( (int) num < inTXTSize );
1299
1300 if ( num )
1301 {
1302 memset(buf, 0, sizeof(buf));
1303 memcpy(buf, inTXT + 1, num);
1304
1305 CString elem;
1306
1307 err = UTF8StringToStringObject( buf, elem );
1308 require_noerr( err, exit );
1309
1310 int curPos = 0;
1311
1312 CString key = elem.Tokenize(L"=", curPos);
1313 CString val = elem.Tokenize(L"=", curPos);
1314
1315 key.MakeLower();
1316
1317 if ( key == L"rp" )
1318 {
1319 qname = val;
1320 }
1321 else
1322 {
1323 rpOnly = false;
1324
1325 if ((key == L"usb_mfg") || (key == L"usb_manufacturer"))
1326 {
1327 service->usb_MFG = val;
1328 }
1329 else if ((key == L"usb_mdl") || (key == L"usb_model"))
1330 {
1331 service->usb_MDL = val;
1332 }
1333 else if (key == L"ty")
1334 {
1335 service->description = val;
1336 }
1337 else if (key == L"product")
1338 {
1339 service->product = val;
1340 }
1341 else if (key == L"note")
1342 {
1343 service->location = val;
1344 }
1345 else if (key == L"qtotal")
1346 {
1347 service->qtotal = (unsigned short) _ttoi((LPCTSTR) val);
1348 qtotalDefined = true;
1349 }
1350 else if (key == L"priority")
1351 {
1352 qpriority = _ttoi((LPCTSTR) val);
1353 }
1354 }
1355 }
1356
1357 inTXTSize -= (num + 1);
1358 inTXT += (num + 1);
1359 }
1360
1361 exit:
1362
1363 if ( rpOnly )
1364 {
1365 qtotalDefined = true;
1366 }
1367
1368 return err;
1369 }
1370
1371