2 * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
23 Change History (most recent first):
25 $Log: ChooserDialog.cpp,v $
26 Revision 1.3 2005/02/10 22:35:35 cheshire
27 <rdar://problem/3727944> Update name
29 Revision 1.2 2004/07/13 21:24:26 rpantos
30 Fix for <rdar://problem/3701120>.
32 Revision 1.1 2004/06/18 04:04:36 rpantos
35 Revision 1.10 2004/04/23 01:19:41 bradley
36 Changed TXT record new line delimiter from \n to \r\n so it works now that it is an edit text.
38 Revision 1.9 2004/03/07 05:51:04 bradley
39 Updated service type list table to include all service types from dns-sd.org as of 2004-03-06.
40 Added separate Service Type and Service Description columns so both are show in the window.
42 Revision 1.8 2004/01/30 02:56:32 bradley
43 Updated to support full Unicode display. Added support for all services on www.dns-sd.org.
45 Revision 1.7 2003/12/25 03:42:04 bradley
46 Added login dialog to get username/password when going to FTP sites. Added more services.
48 Revision 1.6 2003/10/31 12:18:30 bradley
49 Added display of the resolved host name. Show separate TXT record entries on separate lines.
51 Revision 1.5 2003/10/16 09:21:56 bradley
52 Ignore non-IPv4 resolves until mDNS on Windows supports IPv6.
54 Revision 1.4 2003/10/10 03:41:29 bradley
55 Changed HTTP double-click handling to work with or without the path= prefix in the TXT record.
57 Revision 1.3 2003/10/09 19:50:40 bradley
58 Sort service type list by description.
60 Revision 1.2 2003/10/09 19:41:29 bradley
61 Changed quit handling to go through normal close path so dialog is freed on quit. Integrated changes
62 from Andrew van der Stock for the addition of an _rfb._tcp service type for a VNC Remote Framebuffer
63 Server for KDE support. Widened service type list to handle larger service type descriptions.
65 Revision 1.1 2003/08/21 02:06:47 bradley
66 Moved DNSServiceBrowser for non-Windows CE into Windows sub-folder.
68 Revision 1.7 2003/08/20 06:45:56 bradley
69 Updated for IP address changes in DNSServices. Added support for browsing for Xserve RAID.
71 Revision 1.6 2003/08/12 19:56:28 cheshire
74 Revision 1.5 2003/07/13 01:03:55 cheshire
75 Diffs provided by Bob Bradley to provide provide proper display of Unicode names
77 Revision 1.4 2003/07/02 21:20:06 cheshire
78 <rdar://problem/3313413> Update copyright notices, etc., in source code comments
80 Revision 1.3 2002/09/21 20:44:55 zarzycki
83 Revision 1.2 2002/09/20 08:39:21 bradley
84 Make sure each resolved item matches the selected service type to handle resolved that may have
85 been queued up on the Windows Message Loop. Reduce column to fit when scrollbar is present.
87 Revision 1.1 2002/09/20 06:12:52 bradley
88 DNSServiceBrowser for Windows
103 #include "DNSServices.h"
105 #include "Application.h"
106 #include "AboutDialog.h"
107 #include "LoginDialog.h"
108 #include "Resource.h"
110 #include "ChooserDialog.h"
113 #define new DEBUG_NEW
115 static char THIS_FILE
[] = __FILE__
;
119 #pragma mark == Constants ==
122 //===========================================================================================================================
124 //===========================================================================================================================
130 kChooserMenuIndexFile
= 0,
131 kChooserMenuIndexHelp
= 1
136 #define kDomainListDefaultDomainColumnWidth 164
140 #define kServiceListDefaultServiceColumnTypeWidth 146
141 #define kServiceListDefaultServiceColumnDescWidth 230
145 #define kChooserListDefaultNameColumnWidth 190
146 #define kChooserListDefaultIPColumnWidth 120
148 // Windows User Messages
150 #define WM_USER_DOMAIN_ADD ( WM_USER + 0x100 )
151 #define WM_USER_DOMAIN_REMOVE ( WM_USER + 0x101 )
152 #define WM_USER_SERVICE_ADD ( WM_USER + 0x102 )
153 #define WM_USER_SERVICE_REMOVE ( WM_USER + 0x103 )
154 #define WM_USER_RESOLVE ( WM_USER + 0x104 )
157 #pragma mark == Constants - Service Table ==
160 //===========================================================================================================================
161 // Constants - Service Table
162 //===========================================================================================================================
164 struct KnownServiceEntry
166 const char * serviceType
;
167 const char * description
;
168 const char * urlScheme
;
172 static const KnownServiceEntry kKnownServiceTable
[] =
174 { "_accountedge._tcp.", "MYOB AccountEdge", "", false },
175 { "_aecoretech._tcp.", "Apple Application Engineering Services", "", false },
176 { "_afpovertcp._tcp.", "Apple File Sharing (AFP)", "afp://", false },
177 { "_airport._tcp.", "AirPort Base Station", "", false },
178 { "_apple-sasl._tcp.", "Apple Password Server", "", false },
179 { "_aquamon._tcp.", "AquaMon", "", false },
180 { "_async._tcp", "address-o-sync", "", false },
181 { "_auth._tcp.", "Authentication Service", "", false },
182 { "_bootps._tcp.", "Bootstrap Protocol Server", "", false },
183 { "_bousg._tcp.", "Bag Of Unusual Strategy Games", "", false },
184 { "_browse._udp.", "DNS Service Discovery", "", false },
185 { "_cheat._tcp.", "The Cheat", "", false },
186 { "_chess._tcp", "Project Gridlock", "", false },
187 { "_chfts._tcp", "Fluid Theme Server", "", false },
188 { "_clipboard._tcp", "Clipboard Sharing", "", false },
189 { "_contactserver._tcp.", "Now Up-to-Date & Contact", "", false },
190 { "_cvspserver._tcp", "CVS PServer", "", false },
191 { "_cytv._tcp.", "CyTV Network streaming for Elgato EyeTV", "", false },
192 { "_daap._tcp.", "Digital Audio Access Protocol (iTunes)", "daap://", false },
193 { "_distcc._tcp", "Distributed Compiler", "", false },
194 { "_dns-sd._udp", "DNS Service Discovery", "", false },
195 { "_dpap._tcp.", "Digital Picture Access Protocol (iPhoto)", "", false },
196 { "_earphoria._tcp.", "Earphoria", "", false },
197 { "_ecbyesfsgksc._tcp.", "Net Monitor Anti-Piracy Service", "", false },
198 { "_eheap._tcp.", "Interactive Room Software", "", false },
199 { "_embrace._tcp.", "DataEnvoy", "", false },
200 { "_eppc._tcp.", "Remote AppleEvents", "eppc://", false },
201 { "_exec._tcp.", "Remote Process Execution", "", false },
202 { "_facespan._tcp.", "FaceSpan", "", false },
203 { "_fjork._tcp.", "Fjork", "", false },
204 { "_ftp._tcp.", "File Transfer (FTP)", "ftp://", false },
205 { "_ftpcroco._tcp.", "Crocodile FTP Server", "", false },
206 { "_gbs-smp._tcp.", "SnapMail", "", false },
207 { "_gbs-stp._tcp.", "SnapTalk", "", false },
208 { "_grillezvous._tcp.", "Roxio ToastAnywhere(tm) Recorder Sharing", "", false },
209 { "_h323._tcp.", "H.323", "", false },
210 { "_hotwayd._tcp", "Hotwayd", "", false },
211 { "_http._tcp.", "Web Server (HTTP)", "http://", true },
212 { "_hydra._tcp", "SubEthaEdit", "", false },
213 { "_ica-networking._tcp.", "Image Capture Networking", "", false },
214 { "_ichalkboard._tcp.", "iChalk", "", false },
215 { "_ichat._tcp.", "iChat", "ichat://", false },
216 { "_iconquer._tcp.", "iConquer", "", false },
217 { "_imap._tcp.", "Internet Message Access Protocol", "", false },
218 { "_imidi._tcp.", "iMidi", "", false },
219 { "_ipp._tcp.", "Printer (IPP)", "ipp://", false },
220 { "_ishare._tcp.", "iShare", "", false },
221 { "_isparx._tcp.", "iSparx", "", false },
222 { "_istorm._tcp", "iStorm", "", false },
223 { "_iwork._tcp.", "iWork Server", "", false },
224 { "_liaison._tcp.", "Liaison", "", false },
225 { "_login._tcp.", "Remote Login a la Telnet", "", false },
226 { "_lontalk._tcp.", "LonTalk over IP (ANSI 852)", "", false },
227 { "_lonworks._tcp.", "Echelon LNS Remote Client", "", false },
228 { "_macfoh-remote._tcp.", "MacFOH Remote", "", false },
229 { "_moneyworks._tcp.", "MoneyWorks", "", false },
230 { "_mp3sushi._tcp", "MP3 Sushi", "", false },
231 { "_mttp._tcp.", "MenuTunes Sharing", "", false },
232 { "_ncbroadcast._tcp.", "Network Clipboard Broadcasts", "", false },
233 { "_ncdirect._tcp.", "Network Clipboard Direct Transfers", "", false },
234 { "_ncsyncserver._tcp.", "Network Clipboard Sync Server", "", false },
235 { "_newton-dock._tcp.", "Escale", "", false },
236 { "_nfs._tcp", "NFS", "", false },
237 { "_nssocketport._tcp.", "DO over NSSocketPort", "", false },
238 { "_omni-bookmark._tcp.", "OmniWeb", "", false },
239 { "_openbase._tcp.", "OpenBase SQL", "", false },
240 { "_p2pchat._tcp.", "Peer-to-Peer Chat", "", false },
241 { "_pdl-datastream._tcp.", "Printer (PDL)", "pdl://", false },
242 { "_poch._tcp.", "Parallel OperatiOn and Control Heuristic", "", false },
243 { "_pop_2_ambrosia._tcp.", "Pop-Pop", "", false },
244 { "_pop3._tcp", "POP3 Server", "", false },
245 { "_postgresql._tcp", "PostgreSQL Server", "", false },
246 { "_presence._tcp", "iChat AV", "", false },
247 { "_printer._tcp.", "Printer (LPR)", "lpr://", false },
248 { "_ptp._tcp.", "Picture Transfer (PTP)", "ptp://", false },
249 { "_register._tcp", "DNS Service Discovery", "", false },
250 { "_rfb._tcp.", "Remote Frame Buffer", "", false },
251 { "_riousbprint._tcp.", "Remote I/O USB Printer Protocol", "", false },
252 { "_rtsp._tcp.", "Real Time Stream Control Protocol", "", false },
253 { "_safarimenu._tcp", "Safari Menu", "", false },
254 { "_scone._tcp", "Scone", "", false },
255 { "_sdsharing._tcp.", "Speed Download", "", false },
256 { "_seeCard._tcp.", "seeCard", "", false },
257 { "_services._udp.", "DNS Service Discovery", "", false },
258 { "_shell._tcp.", "like exec, but automatic authentication", "", false },
259 { "_shout._tcp.", "Shout", "", false },
260 { "_shoutcast._tcp", "Nicecast", "", false },
261 { "_smb._tcp.", "Windows File Sharing (SMB)", "smb://", false },
262 { "_soap._tcp.", "Simple Object Access Protocol", "", false },
263 { "_spincrisis._tcp.", "Spin Crisis", "", false },
264 { "_spl-itunes._tcp.", "launchTunes", "", false },
265 { "_spr-itunes._tcp.", "netTunes", "", false },
266 { "_ssh._tcp.", "Secure Shell (SSH)", "ssh://", false },
267 { "_ssscreenshare._tcp", "Screen Sharing", "", false },
268 { "_sge-exec._tcp", "Sun Grid Engine (Execution Host)", "", false },
269 { "_sge-qmaster._tcp", "Sun Grid Engine (Master)", "", false },
270 { "_stickynotes._tcp", "Sticky Notes", "", false },
271 { "_strateges._tcp", "Strateges", "", false },
272 { "_sxqdea._tcp", "Synchronize! Pro X", "", false },
273 { "_sybase-tds._tcp", "Sybase Server", "", false },
274 { "_tce._tcp", "Power Card", "", false },
275 { "_teamlist._tcp", "ARTIS Team Task", "", false },
276 { "_teleport._tcp", "teleport", "", false },
277 { "_telnet._tcp.", "Telnet", "telnet://", false },
278 { "_tftp._tcp.", "Trivial File Transfer (TFTP)", "tftp://", false },
279 { "_tinavigator._tcp.", "TI Navigator", "", false },
280 { "_tivo_servemedia._tcp", "TiVo", "", false },
281 { "_upnp._tcp.", "Universal Plug and Play", "", false },
282 { "_utest._tcp.", "uTest", "", false },
283 { "_vue4rendercow._tcp", "VueProRenderCow", "", false },
284 { "_webdav._tcp.", "WebDAV", "webdav://", false },
285 { "_whamb._tcp.", "Whamb", "", false },
286 { "_workstation._tcp", "Macintosh Manager", "", false },
287 { "_ws._tcp", "Web Services", "", false },
288 { "_xserveraid._tcp.", "Xserve RAID", "xsr://", false },
289 { "_xsync._tcp.", "Xserve RAID Synchronization", "", false },
291 { "", "", "", false },
293 // Unofficial and invalid service types that will be phased out:
295 { "_clipboardsharing._tcp.", "ClipboardSharing", "", false },
296 { "_MacOSXDupSuppress._tcp.", "Mac OS X Duplicate Suppression", "", false },
297 { "_netmonitorserver._tcp.", "Net Monitor Server", "", false },
298 { "_networkclipboard._tcp.", "Network Clipboard", "", false },
299 { "_slimdevices_slimp3_cli._tcp.", "SliMP3 Server Command-Line Interface", "", false },
300 { "_slimdevices_slimp3_http._tcp.", "SliMP3 Server Web Interface", "", false },
301 { "_tieducationalhandhelddevice._tcp.", "TI Connect Manager", "", false },
302 { "_tivo_servemedia._tcp.", "TiVo", "", false },
304 { NULL
, NULL
, NULL
, false },
308 #pragma mark == Structures ==
311 //===========================================================================================================================
313 //===========================================================================================================================
315 struct DomainEventInfo
317 DNSBrowserEventType eventType
;
319 DNSNetworkAddress ifIP
;
322 struct ServiceEventInfo
324 DNSBrowserEventType eventType
;
328 DNSNetworkAddress ifIP
;
332 #pragma mark == Prototypes ==
335 //===========================================================================================================================
337 //===========================================================================================================================
343 DNSStatus inStatusCode
,
344 const DNSBrowserEvent
* inEvent
);
346 static char * DNSNetworkAddressToString( const DNSNetworkAddress
*inAddr
, char *outString
);
348 static DWORD
UTF8StringToStringObject( const char *inUTF8
, CString
&inObject
);
349 static DWORD
StringObjectToUTF8String( CString
&inObject
, std::string
&outUTF8
);
352 #pragma mark == Message Map ==
355 //===========================================================================================================================
357 //===========================================================================================================================
359 BEGIN_MESSAGE_MAP(ChooserDialog
, CDialog
)
360 //{{AFX_MSG_MAP(ChooserDialog)
362 ON_NOTIFY(LVN_ITEMCHANGED
, IDC_DOMAIN_LIST
, OnDomainListChanged
)
363 ON_NOTIFY(LVN_ITEMCHANGED
, IDC_SERVICE_LIST
, OnServiceListChanged
)
364 ON_NOTIFY(LVN_ITEMCHANGED
, IDC_CHOOSER_LIST
, OnChooserListChanged
)
365 ON_NOTIFY(NM_DBLCLK
, IDC_CHOOSER_LIST
, OnChooserListDoubleClick
)
366 ON_COMMAND(ID_HELP_ABOUT
, OnAbout
)
367 ON_WM_INITMENUPOPUP()
369 ON_COMMAND(ID_FILE_CLOSE
, OnFileClose
)
370 ON_COMMAND(ID_FILE_EXIT
, OnExit
)
374 ON_MESSAGE( WM_USER_DOMAIN_ADD
, OnDomainAdd
)
375 ON_MESSAGE( WM_USER_DOMAIN_REMOVE
, OnDomainRemove
)
376 ON_MESSAGE( WM_USER_SERVICE_ADD
, OnServiceAdd
)
377 ON_MESSAGE( WM_USER_SERVICE_REMOVE
, OnServiceRemove
)
378 ON_MESSAGE( WM_USER_RESOLVE
, OnResolve
)
382 #pragma mark == Routines ==
385 //===========================================================================================================================
387 //===========================================================================================================================
389 ChooserDialog::ChooserDialog( CWnd
*inParent
)
390 : CDialog( ChooserDialog::IDD
, inParent
)
392 //{{AFX_DATA_INIT(ChooserDialog)
393 // NOTE: the ClassWizard will add member initialization here
396 // Load menu accelerator table.
398 mMenuAcceleratorTable
= ::LoadAccelerators( AfxGetInstanceHandle(), MAKEINTRESOURCE( IDR_CHOOSER_DIALOG_MENU_ACCELERATORS
) );
399 assert( mMenuAcceleratorTable
);
402 mIsServiceBrowsing
= false;
405 //===========================================================================================================================
407 //===========================================================================================================================
409 ChooserDialog::~ChooserDialog( void )
415 err
= DNSBrowserRelease( mBrowser
, 0 );
416 assert( err
== kDNSNoErr
);
420 //===========================================================================================================================
422 //===========================================================================================================================
424 void ChooserDialog::DoDataExchange( CDataExchange
*pDX
)
426 CDialog::DoDataExchange(pDX
);
428 //{{AFX_DATA_MAP(ChooserDialog)
429 DDX_Control(pDX
, IDC_SERVICE_LIST
, mServiceList
);
430 DDX_Control(pDX
, IDC_DOMAIN_LIST
, mDomainList
);
431 DDX_Control(pDX
, IDC_CHOOSER_LIST
, mChooserList
);
435 //===========================================================================================================================
437 //===========================================================================================================================
439 BOOL
ChooserDialog::OnInitDialog( void )
446 // Initialize our parent.
448 CDialog::OnInitDialog();
450 // Set up the window icon.
452 icon
= AfxGetApp()->LoadIcon( IDR_MAIN_ICON
);
456 SetIcon( icon
, TRUE
); // Set big icon
457 SetIcon( icon
, FALSE
); // Set small icon
460 // Set up the Domain List.
462 result
= tempString
.LoadString( IDS_CHOOSER_DOMAIN_COLUMN_NAME
);
464 mDomainList
.InsertColumn( 0, tempString
, LVCFMT_LEFT
, kDomainListDefaultDomainColumnWidth
);
466 // Set up the Service List.
468 result
= tempString
.LoadString( IDS_CHOOSER_SERVICE_COLUMN_TYPE
);
470 mServiceList
.InsertColumn( 0, tempString
, LVCFMT_LEFT
, kServiceListDefaultServiceColumnTypeWidth
);
472 result
= tempString
.LoadString( IDS_CHOOSER_SERVICE_COLUMN_DESC
);
474 mServiceList
.InsertColumn( 1, tempString
, LVCFMT_LEFT
, kServiceListDefaultServiceColumnDescWidth
);
476 PopulateServicesList();
478 // Set up the Chooser List.
480 result
= tempString
.LoadString( IDS_CHOOSER_CHOOSER_NAME_COLUMN_NAME
);
482 mChooserList
.InsertColumn( 0, tempString
, LVCFMT_LEFT
, kChooserListDefaultNameColumnWidth
);
484 result
= tempString
.LoadString( IDS_CHOOSER_CHOOSER_IP_COLUMN_NAME
);
486 mChooserList
.InsertColumn( 1, tempString
, LVCFMT_LEFT
, kChooserListDefaultIPColumnWidth
);
488 // Set up the other controls.
492 // Start browsing for domains.
494 err
= DNSBrowserCreate( 0, BrowserCallBack
, this, &mBrowser
);
495 assert( err
== kDNSNoErr
);
497 err
= DNSBrowserStartDomainSearch( mBrowser
, 0 );
498 assert( err
== kDNSNoErr
);
503 //===========================================================================================================================
505 //===========================================================================================================================
507 void ChooserDialog::OnFileClose()
512 //===========================================================================================================================
514 //===========================================================================================================================
516 void ChooserDialog::OnActivate( UINT nState
, CWnd
* pWndOther
, BOOL bMinimized
)
518 // Always make the active window the "main" window so modal dialogs work better and the app quits after closing
521 gApp
.m_pMainWnd
= this;
523 CDialog::OnActivate(nState
, pWndOther
, bMinimized
);
526 //===========================================================================================================================
528 //===========================================================================================================================
530 void ChooserDialog::PostNcDestroy()
532 // Call the base class to do the normal cleanup.
537 //===========================================================================================================================
538 // PreTranslateMessage
539 //===========================================================================================================================
541 BOOL
ChooserDialog::PreTranslateMessage(MSG
* pMsg
)
546 assert( mMenuAcceleratorTable
);
547 if( mMenuAcceleratorTable
)
549 result
= ::TranslateAccelerator( m_hWnd
, mMenuAcceleratorTable
, pMsg
);
553 result
= CDialog::PreTranslateMessage( pMsg
);
558 //===========================================================================================================================
560 //===========================================================================================================================
562 void ChooserDialog::OnInitMenuPopup( CMenu
*pPopupMenu
, UINT nIndex
, BOOL bSysMenu
)
564 CDialog::OnInitMenuPopup( pPopupMenu
, nIndex
, bSysMenu
);
568 case kChooserMenuIndexFile
:
571 case kChooserMenuIndexHelp
:
579 //===========================================================================================================================
581 //===========================================================================================================================
583 void ChooserDialog::OnExit()
588 //===========================================================================================================================
590 //===========================================================================================================================
592 void ChooserDialog::OnAbout()
599 //===========================================================================================================================
601 //===========================================================================================================================
603 void ChooserDialog::OnSysCommand( UINT inID
, LPARAM inParam
)
605 CDialog::OnSysCommand( inID
, inParam
);
608 //===========================================================================================================================
610 //===========================================================================================================================
612 void ChooserDialog::OnClose()
616 gApp
.m_pMainWnd
= this;
620 //===========================================================================================================================
622 //===========================================================================================================================
624 void ChooserDialog::OnNcDestroy()
626 gApp
.m_pMainWnd
= this;
628 CDialog::OnNcDestroy();
631 //===========================================================================================================================
632 // OnDomainListChanged
633 //===========================================================================================================================
635 void ChooserDialog::OnDomainListChanged( NMHDR
*pNMHDR
, LRESULT
*pResult
)
637 UNUSED_ALWAYS( pNMHDR
);
639 // Domain list changes have similar effects to service list changes so reuse that code path by calling it here.
641 OnServiceListChanged( NULL
, NULL
);
646 //===========================================================================================================================
647 // OnServiceListChanged
648 //===========================================================================================================================
650 void ChooserDialog::OnServiceListChanged( NMHDR
*pNMHDR
, LRESULT
*pResult
)
655 UNUSED_ALWAYS( pNMHDR
);
657 // Stop any existing service search.
661 // If a domain and service type are selected, start searching for the service type on the domain.
663 selectedType
= mServiceList
.GetNextItem( -1, LVNI_SELECTED
);
664 selectedDomain
= mDomainList
.GetNextItem( -1, LVNI_SELECTED
);
666 if( ( selectedType
>= 0 ) && ( selectedDomain
>= 0 ) )
672 s
= mDomainList
.GetItemText( selectedDomain
, 0 );
673 StringObjectToUTF8String( s
, utf8
);
674 type
= mServiceTypes
[ selectedType
].serviceType
.c_str();
677 StartBrowsing( type
, utf8
.c_str() );
687 //===========================================================================================================================
688 // OnChooserListChanged
689 //===========================================================================================================================
691 void ChooserDialog::OnChooserListChanged( NMHDR
*pNMHDR
, LRESULT
*pResult
)
693 UNUSED_ALWAYS( pNMHDR
);
699 //===========================================================================================================================
700 // OnChooserListDoubleClick
701 //===========================================================================================================================
703 void ChooserDialog::OnChooserListDoubleClick( NMHDR
*pNMHDR
, LRESULT
*pResult
)
707 UNUSED_ALWAYS( pNMHDR
);
709 // Display the service instance if it is selected. Otherwise, clear all the info.
711 selectedItem
= mChooserList
.GetNextItem( -1, LVNI_SELECTED
);
712 if( selectedItem
>= 0 )
714 ServiceInstanceInfo
* p
;
716 const KnownServiceEntry
* service
;
718 assert( selectedItem
< (int) mServiceInstances
.size() );
719 p
= &mServiceInstances
[ selectedItem
];
721 // Search for a known service type entry that matches.
723 for( service
= kKnownServiceTable
; service
->serviceType
; ++service
)
725 if( p
->type
== service
->serviceType
)
730 if( service
->serviceType
)
734 // Create a URL representing the service instance.
736 if( strcmp( service
->serviceType
, "_smb._tcp." ) == 0 )
738 // Special case for SMB (no port number).
740 url
.Format( TEXT( "%s%s/" ), service
->urlScheme
, (const char *) p
->ip
.c_str() );
742 else if( strcmp( service
->serviceType
, "_ftp._tcp." ) == 0 )
744 // Special case for FTP to get login info.
750 if( !dialog
.GetLogin( username
, password
) )
755 // Build URL in the following format:
757 // ftp://[username[:password]@]<ip>
759 url
+= service
->urlScheme
;
760 if( username
.GetLength() > 0 )
763 if( password
.GetLength() > 0 )
770 url
+= p
->ip
.c_str();
772 else if( strcmp( service
->serviceType
, "_http._tcp." ) == 0 )
774 // Special case for HTTP to exclude "path=" if present.
776 text
= service
->useText
? p
->text
.c_str() : "";
777 if( strncmp( text
, "path=", 5 ) == 0 )
783 url
.Format( TEXT( "%s%s/%s" ), service
->urlScheme
, (const char *) p
->ip
.c_str(), text
);
787 url
.Format( TEXT( "%s%s%s" ), service
->urlScheme
, (const char *) p
->ip
.c_str(), text
);
792 text
= service
->useText
? p
->text
.c_str() : "";
793 url
.Format( TEXT( "%s%s/%s" ), service
->urlScheme
, (const char *) p
->ip
.c_str(), text
);
796 // Let the system open the URL in the correct app.
799 CWaitCursor waitCursor
;
801 ShellExecute( NULL
, TEXT( "open" ), url
, TEXT( "" ), TEXT( "c:\\" ), SW_SHOWNORMAL
);
810 //===========================================================================================================================
812 //===========================================================================================================================
814 void ChooserDialog::OnCancel()
819 //===========================================================================================================================
820 // PopulateServicesList
821 //===========================================================================================================================
823 void ChooserDialog::PopulateServicesList( void )
825 ServiceTypeVector::iterator i
;
830 // Add a fixed list of known services.
832 if( mServiceTypes
.empty() )
834 const KnownServiceEntry
* service
;
836 for( service
= kKnownServiceTable
; service
->serviceType
; ++service
)
838 ServiceTypeInfo info
;
840 info
.serviceType
= service
->serviceType
;
841 info
.description
= service
->description
;
842 info
.urlScheme
= service
->urlScheme
;
843 mServiceTypes
.push_back( info
);
847 // Add each service to the list.
849 for( i
= mServiceTypes
.begin(); i
!= mServiceTypes
.end(); ++i
)
854 p
= ( *i
).serviceType
.c_str();
855 if( *p
== '_' ) ++p
; // Skip leading '_'.
856 q
= strchr( p
, '.' ); // Find first '.'.
857 if( q
) tmp
.assign( p
, (size_t)( q
- p
) ); // Use only up to the first '.'.
858 else tmp
.assign( p
); // No '.' so use the entire string.
859 UTF8StringToStringObject( tmp
.c_str(), type
);
860 UTF8StringToStringObject( ( *i
).description
.c_str(), desc
);
864 n
= mServiceList
.GetItemCount();
865 mServiceList
.InsertItem( n
, type
);
866 mServiceList
.SetItemText( n
, 1, desc
);
869 // Select the first service type by default.
871 if( !mServiceTypes
.empty() )
873 mServiceList
.SetItemState( 0, LVIS_SELECTED
| LVIS_FOCUSED
, LVIS_SELECTED
| LVIS_FOCUSED
);
877 //===========================================================================================================================
879 //===========================================================================================================================
881 void ChooserDialog::UpdateInfoDisplay( void )
889 std::string textNewLines
;
890 std::string hostName
;
892 std::string::iterator i
;
894 // Display the service instance if it is selected. Otherwise, clear all the info.
896 selectedItem
= mChooserList
.GetNextItem( -1, LVNI_SELECTED
);
897 if( selectedItem
>= 0 )
899 ServiceInstanceInfo
* p
;
901 assert( selectedItem
< (int) mServiceInstances
.size() );
902 p
= &mServiceInstances
[ selectedItem
];
908 hostName
= p
->hostName
;
910 // Sync up the list items with the actual data (IP address may change).
912 UTF8StringToStringObject( ip
.c_str(), s
);
913 mChooserList
.SetItemText( selectedItem
, 1, s
);
918 item
= (CWnd
*) this->GetDlgItem( IDC_INFO_NAME_TEXT
);
920 UTF8StringToStringObject( name
.c_str(), s
);
921 item
->SetWindowText( s
);
925 item
= (CWnd
*) this->GetDlgItem( IDC_INFO_IP_TEXT
);
927 UTF8StringToStringObject( ip
.c_str(), s
);
928 item
->SetWindowText( s
);
932 item
= (CWnd
*) this->GetDlgItem( IDC_INFO_INTERFACE_TEXT
);
934 UTF8StringToStringObject( ifIP
.c_str(), s
);
935 item
->SetWindowText( s
);
938 item
= (CWnd
*) this->GetDlgItem( IDC_INFO_HOST_NAME_TEXT
);
940 UTF8StringToStringObject( hostName
.c_str(), s
);
941 item
->SetWindowText( s
);
945 item
= (CWnd
*) this->GetDlgItem( IDC_INFO_TEXT_TEXT
);
947 for( i
= text
.begin(); i
!= text
.end(); ++i
)
951 textNewLines
+= "\r\n";
958 UTF8StringToStringObject( textNewLines
.c_str(), s
);
959 item
->SetWindowText( s
);
966 //===========================================================================================================================
968 //===========================================================================================================================
970 LONG
ChooserDialog::OnDomainAdd( WPARAM inWParam
, LPARAM inLParam
)
973 std::auto_ptr
< DomainEventInfo
> pAutoPtr
;
980 UNUSED_ALWAYS( inWParam
);
983 p
= reinterpret_cast <DomainEventInfo
*> ( inLParam
);
986 // Search to see if we already know about this domain. If not, add it to the list.
990 n
= mDomainList
.GetItemCount();
991 for( i
= 0; i
< n
; ++i
)
993 s
= mDomainList
.GetItemText( i
, 0 );
1004 mDomainList
.InsertItem( n
, domain
);
1006 // If no domains are selected and the domain being added is a default domain, select it.
1008 selectedItem
= mDomainList
.GetNextItem( -1, LVNI_SELECTED
);
1009 if( ( selectedItem
< 0 ) && ( p
->eventType
== kDNSBrowserEventTypeAddDefaultDomain
) )
1011 mDomainList
.SetItemState( n
, LVIS_SELECTED
| LVIS_FOCUSED
, LVIS_SELECTED
| LVIS_FOCUSED
);
1017 //===========================================================================================================================
1019 //===========================================================================================================================
1021 LONG
ChooserDialog::OnDomainRemove( WPARAM inWParam
, LPARAM inLParam
)
1023 DomainEventInfo
* p
;
1024 std::auto_ptr
< DomainEventInfo
> pAutoPtr
;
1031 UNUSED_ALWAYS( inWParam
);
1034 p
= reinterpret_cast <DomainEventInfo
*> ( inLParam
);
1035 pAutoPtr
.reset( p
);
1037 // Search to see if we know about this domain. If so, remove it from the list.
1041 n
= mDomainList
.GetItemCount();
1042 for( i
= 0; i
< n
; ++i
)
1044 s
= mDomainList
.GetItemText( i
, 0 );
1053 mDomainList
.DeleteItem( i
);
1058 //===========================================================================================================================
1060 //===========================================================================================================================
1062 LONG
ChooserDialog::OnServiceAdd( WPARAM inWParam
, LPARAM inLParam
)
1064 ServiceEventInfo
* p
;
1065 std::auto_ptr
< ServiceEventInfo
> pAutoPtr
;
1067 UNUSED_ALWAYS( inWParam
);
1070 p
= reinterpret_cast <ServiceEventInfo
*> ( inLParam
);
1071 pAutoPtr
.reset( p
);
1076 //===========================================================================================================================
1078 //===========================================================================================================================
1080 LONG
ChooserDialog::OnServiceRemove( WPARAM inWParam
, LPARAM inLParam
)
1082 ServiceEventInfo
* p
;
1083 std::auto_ptr
< ServiceEventInfo
> pAutoPtr
;
1088 UNUSED_ALWAYS( inWParam
);
1091 p
= reinterpret_cast <ServiceEventInfo
*> ( inLParam
);
1092 pAutoPtr
.reset( p
);
1094 // Search to see if we know about this service instance. If so, remove it from the list.
1097 n
= (int) mServiceInstances
.size();
1098 for( i
= 0; i
< n
; ++i
)
1100 ServiceInstanceInfo
* q
;
1102 // If the name, type, domain, and interface match, treat it as the same service instance.
1104 q
= &mServiceInstances
[ i
];
1105 if( ( p
->name
== q
->name
) &&
1106 ( p
->type
== q
->type
) &&
1107 ( p
->domain
== q
->domain
) )
1115 mChooserList
.DeleteItem( i
);
1116 assert( i
< (int) mServiceInstances
.size() );
1117 mServiceInstances
.erase( mServiceInstances
.begin() + i
);
1122 //===========================================================================================================================
1124 //===========================================================================================================================
1126 LONG
ChooserDialog::OnResolve( WPARAM inWParam
, LPARAM inLParam
)
1128 ServiceInstanceInfo
* p
;
1129 std::auto_ptr
< ServiceInstanceInfo
> pAutoPtr
;
1135 UNUSED_ALWAYS( inWParam
);
1138 p
= reinterpret_cast <ServiceInstanceInfo
*> ( inLParam
);
1139 pAutoPtr
.reset( p
);
1141 // Make sure it is for an item of the correct type. This handles any resolves that may have been queued up.
1143 selectedType
= mServiceList
.GetNextItem( -1, LVNI_SELECTED
);
1144 assert( selectedType
>= 0 );
1145 if( selectedType
>= 0 )
1147 assert( selectedType
<= (int) mServiceTypes
.size() );
1148 if( p
->type
!= mServiceTypes
[ selectedType
].serviceType
)
1154 // Search to see if we know about this service instance. If so, update its info. Otherwise, add it to the list.
1157 n
= (int) mServiceInstances
.size();
1158 for( i
= 0; i
< n
; ++i
)
1160 ServiceInstanceInfo
* q
;
1162 // If the name, type, domain, and interface matches, treat it as the same service instance.
1164 q
= &mServiceInstances
[ i
];
1165 if( ( p
->name
== q
->name
) &&
1166 ( p
->type
== q
->type
) &&
1167 ( p
->domain
== q
->domain
) &&
1168 ( p
->ifIP
== q
->ifIP
) )
1176 mServiceInstances
[ i
] = *p
;
1182 mServiceInstances
.push_back( *p
);
1183 UTF8StringToStringObject( p
->name
.c_str(), s
);
1184 mChooserList
.InsertItem( n
, s
);
1186 UTF8StringToStringObject( p
->ip
.c_str(), s
);
1187 mChooserList
.SetItemText( n
, 1, s
);
1189 // If this is the only item, select it.
1193 mChooserList
.SetItemState( n
, LVIS_SELECTED
| LVIS_FOCUSED
, LVIS_SELECTED
| LVIS_FOCUSED
);
1196 UpdateInfoDisplay();
1202 //===========================================================================================================================
1204 //===========================================================================================================================
1206 void ChooserDialog::StartBrowsing( const char *inType
, const char *inDomain
)
1210 assert( mServiceInstances
.empty() );
1211 assert( mChooserList
.GetItemCount() == 0 );
1212 assert( !mIsServiceBrowsing
);
1214 mChooserList
.DeleteAllItems();
1215 mServiceInstances
.clear();
1217 mIsServiceBrowsing
= true;
1218 err
= DNSBrowserStartServiceSearch( mBrowser
, kDNSBrowserFlagAutoResolve
, inType
, inDomain
);
1219 assert( err
== kDNSNoErr
);
1222 //===========================================================================================================================
1224 //===========================================================================================================================
1226 void ChooserDialog::StopBrowsing( void )
1228 // If searching, stop.
1230 if( mIsServiceBrowsing
)
1234 mIsServiceBrowsing
= false;
1235 err
= DNSBrowserStopServiceSearch( mBrowser
, 0 );
1236 assert( err
== kDNSNoErr
);
1239 // Remove all service instances.
1241 mChooserList
.DeleteAllItems();
1242 assert( mChooserList
.GetItemCount() == 0 );
1243 mServiceInstances
.clear();
1244 assert( mServiceInstances
.empty() );
1245 UpdateInfoDisplay();
1252 //===========================================================================================================================
1254 //===========================================================================================================================
1259 DNSBrowserRef inRef
,
1260 DNSStatus inStatusCode
,
1261 const DNSBrowserEvent
* inEvent
)
1263 ChooserDialog
* dialog
;
1267 UNUSED_ALWAYS( inStatusCode
);
1268 UNUSED_ALWAYS( inRef
);
1270 // Check parameters.
1272 assert( inContext
);
1273 dialog
= reinterpret_cast <ChooserDialog
*> ( inContext
);
1277 switch( inEvent
->type
)
1279 case kDNSBrowserEventTypeRelease
:
1284 case kDNSBrowserEventTypeAddDomain
:
1285 case kDNSBrowserEventTypeAddDefaultDomain
:
1286 case kDNSBrowserEventTypeRemoveDomain
:
1288 DomainEventInfo
* domain
;
1289 std::auto_ptr
< DomainEventInfo
> domainAutoPtr
;
1291 domain
= new DomainEventInfo
;
1292 domainAutoPtr
.reset( domain
);
1294 domain
->eventType
= inEvent
->type
;
1295 domain
->domain
= inEvent
->data
.addDomain
.domain
;
1296 domain
->ifIP
= inEvent
->data
.addDomain
.interfaceIP
;
1298 message
= ( inEvent
->type
== kDNSBrowserEventTypeRemoveDomain
) ? WM_USER_DOMAIN_REMOVE
: WM_USER_DOMAIN_ADD
;
1299 posted
= ::PostMessage( dialog
->GetSafeHwnd(), message
, 0, (LPARAM
) domain
);
1303 domainAutoPtr
.release();
1310 case kDNSBrowserEventTypeAddService
:
1311 case kDNSBrowserEventTypeRemoveService
:
1313 ServiceEventInfo
* service
;
1314 std::auto_ptr
< ServiceEventInfo
> serviceAutoPtr
;
1316 service
= new ServiceEventInfo
;
1317 serviceAutoPtr
.reset( service
);
1319 service
->eventType
= inEvent
->type
;
1320 service
->name
= inEvent
->data
.addService
.name
;
1321 service
->type
= inEvent
->data
.addService
.type
;
1322 service
->domain
= inEvent
->data
.addService
.domain
;
1323 service
->ifIP
= inEvent
->data
.addService
.interfaceIP
;
1325 message
= ( inEvent
->type
== kDNSBrowserEventTypeAddService
) ? WM_USER_SERVICE_ADD
: WM_USER_SERVICE_REMOVE
;
1326 posted
= ::PostMessage( dialog
->GetSafeHwnd(), message
, 0, (LPARAM
) service
);
1330 serviceAutoPtr
.release();
1337 case kDNSBrowserEventTypeResolved
:
1338 if( inEvent
->data
.resolved
->address
.addressType
== kDNSNetworkAddressTypeIPv4
)
1340 ServiceInstanceInfo
* serviceInstance
;
1341 std::auto_ptr
< ServiceInstanceInfo
> serviceInstanceAutoPtr
;
1344 serviceInstance
= new ServiceInstanceInfo
;
1345 serviceInstanceAutoPtr
.reset( serviceInstance
);
1347 serviceInstance
->name
= inEvent
->data
.resolved
->name
;
1348 serviceInstance
->type
= inEvent
->data
.resolved
->type
;
1349 serviceInstance
->domain
= inEvent
->data
.resolved
->domain
;
1350 serviceInstance
->ip
= DNSNetworkAddressToString( &inEvent
->data
.resolved
->address
, s
);
1351 serviceInstance
->ifIP
= DNSNetworkAddressToString( &inEvent
->data
.resolved
->interfaceIP
, s
);
1352 serviceInstance
->text
= inEvent
->data
.resolved
->textRecord
;
1353 serviceInstance
->hostName
= inEvent
->data
.resolved
->hostName
;
1355 posted
= ::PostMessage( dialog
->GetSafeHwnd(), WM_USER_RESOLVE
, 0, (LPARAM
) serviceInstance
);
1359 serviceInstanceAutoPtr
.release();
1370 // Don't let exceptions escape.
1374 //===========================================================================================================================
1375 // DNSNetworkAddressToString
1377 // Note: Currently only supports IPv4 network addresses.
1378 //===========================================================================================================================
1380 static char * DNSNetworkAddressToString( const DNSNetworkAddress
*inAddr
, char *outString
)
1385 p
= inAddr
->u
.ipv4
.addr
.v8
;
1386 port
= ntohs( inAddr
->u
.ipv4
.port
.v16
);
1387 if( port
!= kDNSPortInvalid
)
1389 sprintf( outString
, "%u.%u.%u.%u:%u", p
[ 0 ], p
[ 1 ], p
[ 2 ], p
[ 3 ], port
);
1393 sprintf( outString
, "%u.%u.%u.%u", p
[ 0 ], p
[ 1 ], p
[ 2 ], p
[ 3 ] );
1395 return( outString
);
1398 //===========================================================================================================================
1399 // UTF8StringToStringObject
1400 //===========================================================================================================================
1402 static DWORD
UTF8StringToStringObject( const char *inUTF8
, CString
&inObject
)
1410 n
= MultiByteToWideChar( CP_UTF8
, 0, inUTF8
, -1, NULL
, 0 );
1413 unicode
= (BSTR
) malloc( (size_t)( n
* sizeof( wchar_t ) ) );
1416 err
= ERROR_INSUFFICIENT_BUFFER
;
1420 n
= MultiByteToWideChar( CP_UTF8
, 0, inUTF8
, -1, unicode
, n
);
1427 err
= ERROR_NO_UNICODE_TRANSLATION
;
1445 //===========================================================================================================================
1446 // StringObjectToUTF8String
1447 //===========================================================================================================================
1449 static DWORD
StringObjectToUTF8String( CString
&inObject
, std::string
&outUTF8
)
1460 nUnicode
= inObject
.GetLength();
1463 unicode
= inObject
.AllocSysString();
1464 n
= WideCharToMultiByte( CP_UTF8
, 0, unicode
, nUnicode
, NULL
, 0, NULL
, NULL
);
1467 utf8
= (char *) malloc( (size_t) n
);
1469 if( !utf8
) { err
= ERROR_INSUFFICIENT_BUFFER
; goto exit
; }
1471 n
= WideCharToMultiByte( CP_UTF8
, 0, unicode
, nUnicode
, utf8
, n
, NULL
, NULL
);
1476 outUTF8
.assign( utf8
, n
);
1480 err
= ERROR_NO_UNICODE_TRANSLATION
;
1493 SysFreeString( unicode
);