]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/ChooserDialog.cpp
mDNSResponder-58.8.1.tar.gz
[apple/mdnsresponder.git] / mDNSWindows / Applications / DNSServiceBrowser / Windows / Sources / ChooserDialog.cpp
1 /*
2 * Copyright (c) 2002-2003 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: ChooserDialog.cpp,v $
26 Revision 1.1 2003/08/21 02:06:47 bradley
27 Moved Rendezvous Browser for non-Windows CE into Windows sub-folder.
28
29 Revision 1.7 2003/08/20 06:45:56 bradley
30 Updated for IP address changes in DNSServices. Added support for browsing for Xserve RAID.
31
32 Revision 1.6 2003/08/12 19:56:28 cheshire
33 Update to APSL 2.0
34
35 Revision 1.5 2003/07/13 01:03:55 cheshire
36 Diffs provided by Bob Bradley to provide provide proper display of Unicode names
37
38 Revision 1.4 2003/07/02 21:20:06 cheshire
39 <rdar://problem/3313413> Update copyright notices, etc., in source code comments
40
41 Revision 1.3 2002/09/21 20:44:55 zarzycki
42 Added APSL info
43
44 Revision 1.2 2002/09/20 08:39:21 bradley
45 Make sure each resolved item matches the selected service type to handle resolved that may have
46 been queued up on the Windows Message Loop. Reduce column to fit when scrollbar is present.
47
48 Revision 1.1 2002/09/20 06:12:52 bradley
49 Rendezvous Browser for Windows
50
51 */
52
53 #include <assert.h>
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <string.h>
57 #include <time.h>
58
59 #include <algorithm>
60 #include <memory>
61
62 #include "stdafx.h"
63
64 #include "DNSServices.h"
65
66 #include "Application.h"
67 #include "AboutDialog.h"
68 #include "Resource.h"
69
70 #include "ChooserDialog.h"
71
72 #ifdef _DEBUG
73 #define new DEBUG_NEW
74 #undef THIS_FILE
75 static char THIS_FILE[] = __FILE__;
76 #endif
77
78 #if 0
79 #pragma mark == Constants ==
80 #endif
81
82 //===========================================================================================================================
83 // Constants
84 //===========================================================================================================================
85
86 // Menus
87
88 enum
89 {
90 kChooserMenuIndexFile = 0,
91 kChooserMenuIndexHelp = 1
92 };
93
94 // Domain List
95
96 #define kDomainListDefaultDomainColumnIndex 0
97 #define kDomainListDefaultDomainColumnWidth 140
98
99 // Service List
100
101 #define kServiceListDefaultServiceColumnIndex 0
102 #define kServiceListDefaultServiceColumnWidth 140
103
104 // Chooser List
105
106 #define kChooserListDefaultNameColumnIndex 0
107 #define kChooserListDefaultNameColumnWidth 162
108
109 #define kChooserListDefaultIPColumnIndex 1
110 #define kChooserListDefaultIPColumnWidth 126
111
112 // Windows User Messages
113
114 #define WM_USER_DOMAIN_ADD ( WM_USER + 0x100 )
115 #define WM_USER_DOMAIN_REMOVE ( WM_USER + 0x101 )
116 #define WM_USER_SERVICE_ADD ( WM_USER + 0x102 )
117 #define WM_USER_SERVICE_REMOVE ( WM_USER + 0x103 )
118 #define WM_USER_RESOLVE ( WM_USER + 0x104 )
119
120 #if 0
121 #pragma mark == Constants - Service Table ==
122 #endif
123
124 //===========================================================================================================================
125 // Constants - Service Table
126 //===========================================================================================================================
127
128 struct KnownServiceEntry
129 {
130 const char * serviceType;
131 const char * description;
132 const char * urlScheme;
133 bool useText;
134 };
135
136 static const KnownServiceEntry kKnownServiceTable[] =
137 {
138 { "_airport._tcp.", "AirPort Base Station", "acp://", false },
139 { "_afpovertcp._tcp.", "AppleShare Server", "afp://", false },
140 { "_ftp._tcp.", "File Transfer (FTP)", "ftp://", false },
141 { "_ichat._tcp.", "iChat", "ichat://", false },
142 { "_printer._tcp.", "Printer (LPD)", "ldp://", false },
143 { "_eppc._tcp.", "Remote AppleEvents", "eppc://", false },
144 { "_ssh._tcp.", "Secure Shell (SSH)", "ssh://", false },
145 { "_tftp._tcp.", "Trivial File Transfer (TFTP)", "tftp://", false },
146 { "_http._tcp.", "Web Server (HTTP)", "http://", true },
147 { "_smb._tcp.", "Windows File Sharing", "smb://", false },
148 { "_xserveraid._tcp.", "Xserve RAID", "xsr://", false },
149 { NULL, NULL, NULL, false },
150 };
151
152 #if 0
153 #pragma mark == Structures ==
154 #endif
155
156 //===========================================================================================================================
157 // Structures
158 //===========================================================================================================================
159
160 struct DomainEventInfo
161 {
162 DNSBrowserEventType eventType;
163 CString domain;
164 DNSNetworkAddress ifIP;
165 };
166
167 struct ServiceEventInfo
168 {
169 DNSBrowserEventType eventType;
170 std::string name;
171 std::string type;
172 std::string domain;
173 DNSNetworkAddress ifIP;
174 };
175
176 #if 0
177 #pragma mark == Prototypes ==
178 #endif
179
180 //===========================================================================================================================
181 // Prototypes
182 //===========================================================================================================================
183
184 static void
185 BrowserCallBack(
186 void * inContext,
187 DNSBrowserRef inRef,
188 DNSStatus inStatusCode,
189 const DNSBrowserEvent * inEvent );
190
191 static char * DNSNetworkAddressToString( const DNSNetworkAddress *inAddr, char *outString );
192
193 static DWORD UTF8StringToStringObject( const char *inUTF8, CString &inObject );
194
195 #if 0
196 #pragma mark == Message Map ==
197 #endif
198
199 //===========================================================================================================================
200 // Message Map
201 //===========================================================================================================================
202
203 BEGIN_MESSAGE_MAP(ChooserDialog, CDialog)
204 //{{AFX_MSG_MAP(ChooserDialog)
205 ON_WM_SYSCOMMAND()
206 ON_NOTIFY(LVN_ITEMCHANGED, IDC_DOMAIN_LIST, OnDomainListChanged)
207 ON_NOTIFY(LVN_ITEMCHANGED, IDC_SERVICE_LIST, OnServiceListChanged)
208 ON_NOTIFY(LVN_ITEMCHANGED, IDC_CHOOSER_LIST, OnChooserListChanged)
209 ON_NOTIFY(NM_DBLCLK, IDC_CHOOSER_LIST, OnChooserListDoubleClick)
210 ON_COMMAND(ID_HELP_ABOUT, OnAbout)
211 ON_WM_INITMENUPOPUP()
212 ON_WM_ACTIVATE()
213 ON_COMMAND(ID_FILE_CLOSE, OnFileClose)
214 ON_COMMAND(ID_FILE_EXIT, OnExit)
215 ON_WM_CLOSE()
216 ON_WM_NCDESTROY()
217 //}}AFX_MSG_MAP
218 ON_MESSAGE( WM_USER_DOMAIN_ADD, OnDomainAdd )
219 ON_MESSAGE( WM_USER_DOMAIN_REMOVE, OnDomainRemove )
220 ON_MESSAGE( WM_USER_SERVICE_ADD, OnServiceAdd )
221 ON_MESSAGE( WM_USER_SERVICE_REMOVE, OnServiceRemove )
222 ON_MESSAGE( WM_USER_RESOLVE, OnResolve )
223 END_MESSAGE_MAP()
224
225 #if 0
226 #pragma mark == Routines ==
227 #endif
228
229 //===========================================================================================================================
230 // ChooserDialog
231 //===========================================================================================================================
232
233 ChooserDialog::ChooserDialog( CWnd *inParent )
234 : CDialog( ChooserDialog::IDD, inParent)
235 {
236 //{{AFX_DATA_INIT(ChooserDialog)
237 // NOTE: the ClassWizard will add member initialization here
238 //}}AFX_DATA_INIT
239
240 // Load menu accelerator table.
241
242 mMenuAcceleratorTable = ::LoadAccelerators( AfxGetInstanceHandle(), MAKEINTRESOURCE( IDR_CHOOSER_DIALOG_MENU_ACCELERATORS ) );
243 assert( mMenuAcceleratorTable );
244
245 mBrowser = NULL;
246 mIsServiceBrowsing = false;
247 }
248
249 //===========================================================================================================================
250 // ~ChooserDialog
251 //===========================================================================================================================
252
253 ChooserDialog::~ChooserDialog( void )
254 {
255 if( mBrowser )
256 {
257 DNSStatus err;
258
259 err = DNSBrowserRelease( mBrowser, 0 );
260 assert( err == kDNSNoErr );
261 }
262 }
263
264 //===========================================================================================================================
265 // DoDataExchange
266 //===========================================================================================================================
267
268 void ChooserDialog::DoDataExchange( CDataExchange *pDX )
269 {
270 CDialog::DoDataExchange(pDX);
271
272 //{{AFX_DATA_MAP(ChooserDialog)
273 DDX_Control(pDX, IDC_SERVICE_LIST, mServiceList);
274 DDX_Control(pDX, IDC_DOMAIN_LIST, mDomainList);
275 DDX_Control(pDX, IDC_CHOOSER_LIST, mChooserList);
276 //}}AFX_DATA_MAP
277 }
278
279 //===========================================================================================================================
280 // OnInitDialog
281 //===========================================================================================================================
282
283 BOOL ChooserDialog::OnInitDialog( void )
284 {
285 BOOL result;
286 CString tempString;
287 DNSStatus err;
288
289 // Initialize our parent.
290
291 CDialog::OnInitDialog();
292
293 // Set up the Domain List.
294
295 result = tempString.LoadString( IDS_CHOOSER_DOMAIN_COLUMN_NAME );
296 assert( result );
297 mDomainList.InsertColumn( 0, tempString, LVCFMT_LEFT, kDomainListDefaultDomainColumnWidth );
298
299 // Set up the Service List.
300
301 result = tempString.LoadString( IDS_CHOOSER_SERVICE_COLUMN_NAME );
302 assert( result );
303 mServiceList.InsertColumn( 0, tempString, LVCFMT_LEFT, kServiceListDefaultServiceColumnWidth );
304
305 PopulateServicesList();
306
307 // Set up the Chooser List.
308
309 result = tempString.LoadString( IDS_CHOOSER_CHOOSER_NAME_COLUMN_NAME );
310 assert( result );
311 mChooserList.InsertColumn( 0, tempString, LVCFMT_LEFT, kChooserListDefaultNameColumnWidth );
312
313 result = tempString.LoadString( IDS_CHOOSER_CHOOSER_IP_COLUMN_NAME );
314 assert( result );
315 mChooserList.InsertColumn( 1, tempString, LVCFMT_LEFT, kChooserListDefaultIPColumnWidth );
316
317 // Set up the other controls.
318
319 UpdateInfoDisplay();
320
321 // Start browsing for domains.
322
323 err = DNSBrowserCreate( 0, BrowserCallBack, this, &mBrowser );
324 assert( err == kDNSNoErr );
325
326 err = DNSBrowserStartDomainSearch( mBrowser, 0 );
327 assert( err == kDNSNoErr );
328
329 return( true );
330 }
331
332 //===========================================================================================================================
333 // OnFileClose
334 //===========================================================================================================================
335
336 void ChooserDialog::OnFileClose()
337 {
338 OnClose();
339 }
340
341 //===========================================================================================================================
342 // OnActivate
343 //===========================================================================================================================
344
345 void ChooserDialog::OnActivate( UINT nState, CWnd* pWndOther, BOOL bMinimized )
346 {
347 // Always make the active window the "main" window so modal dialogs work better and the app quits after closing
348 // the last window.
349
350 gApp.m_pMainWnd = this;
351
352 CDialog::OnActivate(nState, pWndOther, bMinimized);
353 }
354
355 //===========================================================================================================================
356 // PostNcDestroy
357 //===========================================================================================================================
358
359 void ChooserDialog::PostNcDestroy()
360 {
361 // Call the base class to do the normal cleanup.
362
363 delete this;
364 }
365
366 //===========================================================================================================================
367 // PreTranslateMessage
368 //===========================================================================================================================
369
370 BOOL ChooserDialog::PreTranslateMessage(MSG* pMsg)
371 {
372 BOOL result;
373
374 result = false;
375 assert( mMenuAcceleratorTable );
376 if( mMenuAcceleratorTable )
377 {
378 result = ::TranslateAccelerator( m_hWnd, mMenuAcceleratorTable, pMsg );
379 }
380 if( !result )
381 {
382 result = CDialog::PreTranslateMessage( pMsg );
383 }
384 return( result );
385 }
386
387 //===========================================================================================================================
388 // OnInitMenuPopup
389 //===========================================================================================================================
390
391 void ChooserDialog::OnInitMenuPopup( CMenu *pPopupMenu, UINT nIndex, BOOL bSysMenu )
392 {
393 CDialog::OnInitMenuPopup( pPopupMenu, nIndex, bSysMenu );
394
395 switch( nIndex )
396 {
397 case kChooserMenuIndexFile:
398 break;
399
400 case kChooserMenuIndexHelp:
401 break;
402
403 default:
404 break;
405 }
406 }
407
408 //===========================================================================================================================
409 // OnExit
410 //===========================================================================================================================
411
412 void ChooserDialog::OnExit()
413 {
414 AfxPostQuitMessage( 0 );
415 }
416
417 //===========================================================================================================================
418 // OnAbout
419 //===========================================================================================================================
420
421 void ChooserDialog::OnAbout()
422 {
423 AboutDialog dialog;
424
425 dialog.DoModal();
426 }
427
428 //===========================================================================================================================
429 // OnSysCommand
430 //===========================================================================================================================
431
432 void ChooserDialog::OnSysCommand( UINT inID, LPARAM inParam )
433 {
434 CDialog::OnSysCommand( inID, inParam );
435 }
436
437 //===========================================================================================================================
438 // OnClose
439 //===========================================================================================================================
440
441 void ChooserDialog::OnClose()
442 {
443 StopBrowsing();
444
445 gApp.m_pMainWnd = this;
446 DestroyWindow();
447 }
448
449 //===========================================================================================================================
450 // OnNcDestroy
451 //===========================================================================================================================
452
453 void ChooserDialog::OnNcDestroy()
454 {
455 gApp.m_pMainWnd = this;
456
457 CDialog::OnNcDestroy();
458 }
459
460 //===========================================================================================================================
461 // OnDomainListChanged
462 //===========================================================================================================================
463
464 void ChooserDialog::OnDomainListChanged( NMHDR *pNMHDR, LRESULT *pResult )
465 {
466 UNUSED_ALWAYS( pNMHDR );
467
468 // Domain list changes have similar effects to service list changes so reuse that code path by calling it here.
469
470 OnServiceListChanged( NULL, NULL );
471
472 *pResult = 0;
473 }
474
475 //===========================================================================================================================
476 // OnServiceListChanged
477 //===========================================================================================================================
478
479 void ChooserDialog::OnServiceListChanged( NMHDR *pNMHDR, LRESULT *pResult )
480 {
481 int selectedType;
482 int selectedDomain;
483
484 UNUSED_ALWAYS( pNMHDR );
485
486 // Stop any existing service search.
487
488 StopBrowsing();
489
490 // If a domain and service type are selected, start searching for the service type on the domain.
491
492 selectedType = mServiceList.GetNextItem( -1, LVNI_SELECTED );
493 selectedDomain = mDomainList.GetNextItem( -1, LVNI_SELECTED );
494
495 if( ( selectedType >= 0 ) && ( selectedDomain >= 0 ) )
496 {
497 CString type;
498 CString domain;
499
500 type = mServiceTypes[ selectedType ].serviceType.c_str();
501 domain = mDomainList.GetItemText( selectedDomain, 0 );
502
503 StartBrowsing( type, domain );
504 }
505
506 if( pResult )
507 {
508 *pResult = 0;
509 }
510 }
511
512 //===========================================================================================================================
513 // OnChooserListChanged
514 //===========================================================================================================================
515
516 void ChooserDialog::OnChooserListChanged( NMHDR *pNMHDR, LRESULT *pResult )
517 {
518 UNUSED_ALWAYS( pNMHDR );
519
520 UpdateInfoDisplay();
521 *pResult = 0;
522 }
523
524 //===========================================================================================================================
525 // OnChooserListDoubleClick
526 //===========================================================================================================================
527
528 void ChooserDialog::OnChooserListDoubleClick( NMHDR *pNMHDR, LRESULT *pResult )
529 {
530 int selectedItem;
531
532 UNUSED_ALWAYS( pNMHDR );
533
534 // Display the service instance if it is selected. Otherwise, clear all the info.
535
536 selectedItem = mChooserList.GetNextItem( -1, LVNI_SELECTED );
537 if( selectedItem >= 0 )
538 {
539 ServiceInstanceInfo * p;
540 CString url;
541 const KnownServiceEntry * service;
542
543 assert( selectedItem < (int) mServiceInstances.size() );
544 p = &mServiceInstances[ selectedItem ];
545
546 // Search for a known service type entry that matches.
547
548 for( service = kKnownServiceTable; service->serviceType; ++service )
549 {
550 if( p->type == service->serviceType )
551 {
552 break;
553 }
554 }
555 if( service->serviceType )
556 {
557 // Create a URL representing the service instance. Special case for SMB (no port number).
558
559 if( strcmp( service->serviceType, "_smb._tcp" ) == 0 )
560 {
561 url.Format( "%s%s/", service->urlScheme, (const char *) p->ip.c_str() );
562 }
563 else
564 {
565 const char * text;
566
567 text = service->useText ? p->text.c_str() : "";
568 url.Format( "%s%s/%s", service->urlScheme, (const char *) p->ip.c_str(), text );
569 }
570
571 // Let the system open the URL in the correct app.
572
573 ShellExecute( NULL, "open", url, "", "c:\\", SW_SHOWNORMAL );
574 }
575 }
576 *pResult = 0;
577 }
578
579 //===========================================================================================================================
580 // OnCancel
581 //===========================================================================================================================
582
583 void ChooserDialog::OnCancel()
584 {
585 // Do nothing.
586 }
587
588 //===========================================================================================================================
589 // PopulateServicesList
590 //===========================================================================================================================
591
592 void ChooserDialog::PopulateServicesList( void )
593 {
594 ServiceTypeVector::iterator i;
595 CString name;
596
597 // Add a fixed list of known services.
598
599 if( mServiceTypes.empty() )
600 {
601 const KnownServiceEntry * service;
602
603 for( service = kKnownServiceTable; service->serviceType; ++service )
604 {
605 ServiceTypeInfo info;
606
607 info.serviceType = service->serviceType;
608 info.description = service->description;
609 info.urlScheme = service->urlScheme;
610 mServiceTypes.push_back( info );
611 }
612 }
613
614 // Add each service to the list.
615
616 for( i = mServiceTypes.begin(); i != mServiceTypes.end(); ++i )
617 {
618 UTF8StringToStringObject( ( *i ).description.c_str(), name );
619 mServiceList.InsertItem( mServiceList.GetItemCount(), name );
620 }
621
622 // Select the first service type by default.
623
624 if( !mServiceTypes.empty() )
625 {
626 mServiceList.SetItemState( 0, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED );
627 }
628 }
629
630 //===========================================================================================================================
631 // UpdateInfoDisplay
632 //===========================================================================================================================
633
634 void ChooserDialog::UpdateInfoDisplay( void )
635 {
636 int selectedItem;
637 std::string name;
638 CString s;
639 std::string ip;
640 std::string ifIP;
641 std::string text;
642 CWnd * item;
643
644 // Display the service instance if it is selected. Otherwise, clear all the info.
645
646 selectedItem = mChooserList.GetNextItem( -1, LVNI_SELECTED );
647 if( selectedItem >= 0 )
648 {
649 ServiceInstanceInfo * p;
650
651 assert( selectedItem < (int) mServiceInstances.size() );
652 p = &mServiceInstances[ selectedItem ];
653
654 name = p->name;
655 ip = p->ip;
656 ifIP = p->ifIP;
657 text = p->text;
658
659 // Sync up the list items with the actual data (IP address may change).
660
661 mChooserList.SetItemText( selectedItem, 1, ip.c_str() );
662 }
663
664 // Name
665
666 item = (CWnd *) this->GetDlgItem( IDC_INFO_NAME_TEXT );
667 assert( item );
668 UTF8StringToStringObject( name.c_str(), s );
669 item->SetWindowText( s );
670
671 // IP
672
673 item = (CWnd *) this->GetDlgItem( IDC_INFO_IP_TEXT );
674 assert( item );
675 item->SetWindowText( ip.c_str() );
676
677 // Interface
678
679 item = (CWnd *) this->GetDlgItem( IDC_INFO_INTERFACE_TEXT );
680 assert( item );
681 item->SetWindowText( ifIP.c_str() );
682
683 // Text
684
685 if( text.size() > 255 )
686 {
687 text.resize( 255 );
688 }
689 item = (CWnd *) this->GetDlgItem( IDC_INFO_TEXT_TEXT );
690 assert( item );
691 item->SetWindowText( text.c_str() );
692 }
693
694 #if 0
695 #pragma mark -
696 #endif
697
698 //===========================================================================================================================
699 // OnDomainAdd
700 //===========================================================================================================================
701
702 LONG ChooserDialog::OnDomainAdd( WPARAM inWParam, LPARAM inLParam )
703 {
704 DomainEventInfo * p;
705 std::auto_ptr < DomainEventInfo > pAutoPtr;
706 int n;
707 int i;
708 CString domain;
709 CString s;
710 bool found;
711
712 UNUSED_ALWAYS( inWParam );
713
714 assert( inLParam );
715 p = reinterpret_cast <DomainEventInfo *> ( inLParam );
716 pAutoPtr.reset( p );
717
718 // Search to see if we already know about this domain. If not, add it to the list.
719
720 found = false;
721 domain = p->domain;
722 n = mDomainList.GetItemCount();
723 for( i = 0; i < n; ++i )
724 {
725 s = mDomainList.GetItemText( i, 0 );
726 if( s == domain )
727 {
728 found = true;
729 break;
730 }
731 }
732 if( !found )
733 {
734 int selectedItem;
735
736 mDomainList.InsertItem( n, domain );
737
738 // If no domains are selected and the domain being added is a default domain, select it.
739
740 selectedItem = mDomainList.GetNextItem( -1, LVNI_SELECTED );
741 if( ( selectedItem < 0 ) && ( p->eventType == kDNSBrowserEventTypeAddDefaultDomain ) )
742 {
743 mDomainList.SetItemState( n, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED );
744 }
745 }
746 return( 0 );
747 }
748
749 //===========================================================================================================================
750 // OnDomainRemove
751 //===========================================================================================================================
752
753 LONG ChooserDialog::OnDomainRemove( WPARAM inWParam, LPARAM inLParam )
754 {
755 DomainEventInfo * p;
756 std::auto_ptr < DomainEventInfo > pAutoPtr;
757 int n;
758 int i;
759 CString domain;
760 CString s;
761 bool found;
762
763 UNUSED_ALWAYS( inWParam );
764
765 assert( inLParam );
766 p = reinterpret_cast <DomainEventInfo *> ( inLParam );
767 pAutoPtr.reset( p );
768
769 // Search to see if we know about this domain. If so, remove it from the list.
770
771 found = false;
772 domain = p->domain;
773 n = mDomainList.GetItemCount();
774 for( i = 0; i < n; ++i )
775 {
776 s = mDomainList.GetItemText( i, 0 );
777 if( s == domain )
778 {
779 found = true;
780 break;
781 }
782 }
783 if( found )
784 {
785 mDomainList.DeleteItem( i );
786 }
787 return( 0 );
788 }
789
790 //===========================================================================================================================
791 // OnServiceAdd
792 //===========================================================================================================================
793
794 LONG ChooserDialog::OnServiceAdd( WPARAM inWParam, LPARAM inLParam )
795 {
796 ServiceEventInfo * p;
797 std::auto_ptr < ServiceEventInfo > pAutoPtr;
798
799 UNUSED_ALWAYS( inWParam );
800
801 assert( inLParam );
802 p = reinterpret_cast <ServiceEventInfo *> ( inLParam );
803 pAutoPtr.reset( p );
804
805 return( 0 );
806 }
807
808 //===========================================================================================================================
809 // OnServiceRemove
810 //===========================================================================================================================
811
812 LONG ChooserDialog::OnServiceRemove( WPARAM inWParam, LPARAM inLParam )
813 {
814 ServiceEventInfo * p;
815 std::auto_ptr < ServiceEventInfo > pAutoPtr;
816 bool found;
817 int n;
818 int i;
819
820 UNUSED_ALWAYS( inWParam );
821
822 assert( inLParam );
823 p = reinterpret_cast <ServiceEventInfo *> ( inLParam );
824 pAutoPtr.reset( p );
825
826 // Search to see if we know about this service instance. If so, remove it from the list.
827
828 found = false;
829 n = (int) mServiceInstances.size();
830 for( i = 0; i < n; ++i )
831 {
832 ServiceInstanceInfo * q;
833
834 // If the name, type, domain, and interface match, treat it as the same service instance.
835
836 q = &mServiceInstances[ i ];
837 if( ( p->name == q->name ) &&
838 ( p->type == q->type ) &&
839 ( p->domain == q->domain ) )
840 {
841 found = true;
842 break;
843 }
844 }
845 if( found )
846 {
847 mChooserList.DeleteItem( i );
848 assert( i < (int) mServiceInstances.size() );
849 mServiceInstances.erase( mServiceInstances.begin() + i );
850 }
851 return( 0 );
852 }
853
854 //===========================================================================================================================
855 // OnResolve
856 //===========================================================================================================================
857
858 LONG ChooserDialog::OnResolve( WPARAM inWParam, LPARAM inLParam )
859 {
860 ServiceInstanceInfo * p;
861 std::auto_ptr < ServiceInstanceInfo > pAutoPtr;
862 int selectedType;
863 int n;
864 int i;
865 bool found;
866
867 UNUSED_ALWAYS( inWParam );
868
869 assert( inLParam );
870 p = reinterpret_cast <ServiceInstanceInfo *> ( inLParam );
871 pAutoPtr.reset( p );
872
873 // Make sure it is for an item of the correct type. This handles any resolves that may have been queued up.
874
875 selectedType = mServiceList.GetNextItem( -1, LVNI_SELECTED );
876 assert( selectedType >= 0 );
877 if( selectedType >= 0 )
878 {
879 assert( selectedType <= (int) mServiceTypes.size() );
880 if( p->type != mServiceTypes[ selectedType ].serviceType )
881 {
882 goto exit;
883 }
884 }
885
886 // Search to see if we know about this service instance. If so, update its info. Otherwise, add it to the list.
887
888 found = false;
889 n = (int) mServiceInstances.size();
890 for( i = 0; i < n; ++i )
891 {
892 ServiceInstanceInfo * q;
893
894 // If the name, type, domain, and interface matches, treat it as the same service instance.
895
896 q = &mServiceInstances[ i ];
897 if( ( p->name == q->name ) &&
898 ( p->type == q->type ) &&
899 ( p->domain == q->domain ) &&
900 ( p->ifIP == q->ifIP ) )
901 {
902 found = true;
903 break;
904 }
905 }
906 if( found )
907 {
908 mServiceInstances[ i ] = *p;
909 }
910 else
911 {
912 CString s;
913
914 mServiceInstances.push_back( *p );
915 UTF8StringToStringObject( p->name.c_str(), s );
916 mChooserList.InsertItem( n, s );
917 mChooserList.SetItemText( n, 1, p->ip.c_str() );
918
919 // If this is the only item, select it.
920
921 if( n == 0 )
922 {
923 mChooserList.SetItemState( n, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED );
924 }
925 }
926 UpdateInfoDisplay();
927
928 exit:
929 return( 0 );
930 }
931
932 //===========================================================================================================================
933 // StartBrowsing
934 //===========================================================================================================================
935
936 void ChooserDialog::StartBrowsing( const char *inType, const char *inDomain )
937 {
938 DNSStatus err;
939
940 assert( mServiceInstances.empty() );
941 assert( mChooserList.GetItemCount() == 0 );
942 assert( !mIsServiceBrowsing );
943
944 mChooserList.DeleteAllItems();
945 mServiceInstances.clear();
946
947 mIsServiceBrowsing = true;
948 err = DNSBrowserStartServiceSearch( mBrowser, kDNSBrowserFlagAutoResolve, inType, inDomain );
949 assert( err == kDNSNoErr );
950 }
951
952 //===========================================================================================================================
953 // StopBrowsing
954 //===========================================================================================================================
955
956 void ChooserDialog::StopBrowsing( void )
957 {
958 // If searching, stop.
959
960 if( mIsServiceBrowsing )
961 {
962 DNSStatus err;
963
964 mIsServiceBrowsing = false;
965 err = DNSBrowserStopServiceSearch( mBrowser, 0 );
966 assert( err == kDNSNoErr );
967 }
968
969 // Remove all service instances.
970
971 mChooserList.DeleteAllItems();
972 assert( mChooserList.GetItemCount() == 0 );
973 mServiceInstances.clear();
974 assert( mServiceInstances.empty() );
975 UpdateInfoDisplay();
976 }
977
978 #if 0
979 #pragma mark -
980 #endif
981
982 //===========================================================================================================================
983 // BrowserCallBack
984 //===========================================================================================================================
985
986 static void
987 BrowserCallBack(
988 void * inContext,
989 DNSBrowserRef inRef,
990 DNSStatus inStatusCode,
991 const DNSBrowserEvent * inEvent )
992 {
993 ChooserDialog * dialog;
994 UINT message;
995 BOOL posted;
996
997 UNUSED_ALWAYS( inStatusCode );
998 UNUSED_ALWAYS( inRef );
999
1000 // Check parameters.
1001
1002 assert( inContext );
1003 dialog = reinterpret_cast <ChooserDialog *> ( inContext );
1004
1005 try
1006 {
1007 switch( inEvent->type )
1008 {
1009 case kDNSBrowserEventTypeRelease:
1010 break;
1011
1012 // Domains
1013
1014 case kDNSBrowserEventTypeAddDomain:
1015 case kDNSBrowserEventTypeAddDefaultDomain:
1016 case kDNSBrowserEventTypeRemoveDomain:
1017 {
1018 DomainEventInfo * domain;
1019 std::auto_ptr < DomainEventInfo > domainAutoPtr;
1020
1021 domain = new DomainEventInfo;
1022 domainAutoPtr.reset( domain );
1023
1024 domain->eventType = inEvent->type;
1025 domain->domain = inEvent->data.addDomain.domain;
1026 domain->ifIP = inEvent->data.addDomain.interfaceIP;
1027
1028 message = ( inEvent->type == kDNSBrowserEventTypeRemoveDomain ) ? WM_USER_DOMAIN_REMOVE : WM_USER_DOMAIN_ADD;
1029 posted = ::PostMessage( dialog->GetSafeHwnd(), message, 0, (LPARAM) domain );
1030 assert( posted );
1031 if( posted )
1032 {
1033 domainAutoPtr.release();
1034 }
1035 break;
1036 }
1037
1038 // Services
1039
1040 case kDNSBrowserEventTypeAddService:
1041 case kDNSBrowserEventTypeRemoveService:
1042 {
1043 ServiceEventInfo * service;
1044 std::auto_ptr < ServiceEventInfo > serviceAutoPtr;
1045
1046 service = new ServiceEventInfo;
1047 serviceAutoPtr.reset( service );
1048
1049 service->eventType = inEvent->type;
1050 service->name = inEvent->data.addService.name;
1051 service->type = inEvent->data.addService.type;
1052 service->domain = inEvent->data.addService.domain;
1053 service->ifIP = inEvent->data.addService.interfaceIP;
1054
1055 message = ( inEvent->type == kDNSBrowserEventTypeAddService ) ? WM_USER_SERVICE_ADD : WM_USER_SERVICE_REMOVE;
1056 posted = ::PostMessage( dialog->GetSafeHwnd(), message, 0, (LPARAM) service );
1057 assert( posted );
1058 if( posted )
1059 {
1060 serviceAutoPtr.release();
1061 }
1062 break;
1063 }
1064
1065 // Resolves
1066
1067 case kDNSBrowserEventTypeResolved:
1068 {
1069 ServiceInstanceInfo * serviceInstance;
1070 std::auto_ptr < ServiceInstanceInfo > serviceInstanceAutoPtr;
1071 char s[ 32 ];
1072
1073 serviceInstance = new ServiceInstanceInfo;
1074 serviceInstanceAutoPtr.reset( serviceInstance );
1075
1076 serviceInstance->name = inEvent->data.resolved->name;
1077 serviceInstance->type = inEvent->data.resolved->type;
1078 serviceInstance->domain = inEvent->data.resolved->domain;
1079 serviceInstance->ip = DNSNetworkAddressToString( &inEvent->data.resolved->address, s );
1080 serviceInstance->ifIP = DNSNetworkAddressToString( &inEvent->data.resolved->interfaceIP, s );
1081 serviceInstance->text = inEvent->data.resolved->textRecord;
1082
1083 posted = ::PostMessage( dialog->GetSafeHwnd(), WM_USER_RESOLVE, 0, (LPARAM) serviceInstance );
1084 assert( posted );
1085 if( posted )
1086 {
1087 serviceInstanceAutoPtr.release();
1088 }
1089 break;
1090 }
1091
1092 default:
1093 break;
1094 }
1095 }
1096 catch( ... )
1097 {
1098 // Don't let exceptions escape.
1099 }
1100 }
1101
1102 //===========================================================================================================================
1103 // DNSNetworkAddressToString
1104 //
1105 // Note: Currently only supports IPv4 network addresses.
1106 //===========================================================================================================================
1107
1108 static char * DNSNetworkAddressToString( const DNSNetworkAddress *inAddr, char *outString )
1109 {
1110 const DNSUInt8 * p;
1111 DNSUInt16 port;
1112
1113 p = inAddr->u.ipv4.addr.v8;
1114 port = ntohs( inAddr->u.ipv4.port.v16 );
1115 if( port != kDNSPortInvalid )
1116 {
1117 sprintf( outString, "%u.%u.%u.%u:%u", p[ 0 ], p[ 1 ], p[ 2 ], p[ 3 ], port );
1118 }
1119 else
1120 {
1121 sprintf( outString, "%u.%u.%u.%u", p[ 0 ], p[ 1 ], p[ 2 ], p[ 3 ] );
1122 }
1123 return( outString );
1124 }
1125
1126 //===========================================================================================================================
1127 // UTF8StringToStringObject
1128 //===========================================================================================================================
1129
1130 static DWORD UTF8StringToStringObject( const char *inUTF8, CString &inObject )
1131 {
1132 DWORD err;
1133 int n;
1134 BSTR unicode;
1135
1136 unicode = NULL;
1137
1138 n = MultiByteToWideChar( CP_UTF8, 0, inUTF8, -1, NULL, 0 );
1139 if( n > 0 )
1140 {
1141 unicode = (BSTR) malloc( (size_t)( n * sizeof( wchar_t ) ) );
1142 if( !unicode )
1143 {
1144 err = ERROR_INSUFFICIENT_BUFFER;
1145 goto exit;
1146 }
1147
1148 n = MultiByteToWideChar( CP_UTF8, 0, inUTF8, -1, unicode, n );
1149 try
1150 {
1151 inObject = unicode;
1152 }
1153 catch( ... )
1154 {
1155 err = ERROR_NO_UNICODE_TRANSLATION;
1156 goto exit;
1157 }
1158 }
1159 else
1160 {
1161 inObject = "";
1162 }
1163 err = 0;
1164
1165 exit:
1166 if( unicode )
1167 {
1168 free( unicode );
1169 }
1170 return( err );
1171 }