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