2 * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
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
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.
23 * @APPLE_LICENSE_HEADER_END@
25 Change History (most recent first):
27 $Log: ChooserDialog.cpp,v $
28 Revision 1.10 2004/04/23 01:19:41 bradley
29 Changed TXT record new line delimiter from \n to \r\n so it works now that it is an edit text.
31 Revision 1.9 2004/03/07 05:51:04 bradley
32 Updated service type list table to include all service types from dns-sd.org as of 2004-03-06.
33 Added separate Service Type and Service Description columns so both are show in the window.
35 Revision 1.8 2004/01/30 02:56:32 bradley
36 Updated to support full Unicode display. Added support for all services on www.dns-sd.org.
38 Revision 1.7 2003/12/25 03:42:04 bradley
39 Added login dialog to get username/password when going to FTP sites. Added more services.
41 Revision 1.6 2003/10/31 12:18:30 bradley
42 Added display of the resolved host name. Show separate TXT record entries on separate lines.
44 Revision 1.5 2003/10/16 09:21:56 bradley
45 Ignore non-IPv4 resolves until mDNS on Windows supports IPv6.
47 Revision 1.4 2003/10/10 03:41:29 bradley
48 Changed HTTP double-click handling to work with or without the path= prefix in the TXT record.
50 Revision 1.3 2003/10/09 19:50:40 bradley
51 Sort service type list by description.
53 Revision 1.2 2003/10/09 19:41:29 bradley
54 Changed quit handling to go through normal close path so dialog is freed on quit. Integrated changes
55 from Andrew van der Stock for the addition of an _rfb._tcp service type for a VNC Remote Framebuffer
56 Server for KDE support. Widened service type list to handle larger service type descriptions.
58 Revision 1.1 2003/08/21 02:06:47 bradley
59 Moved Rendezvous Browser for non-Windows CE into Windows sub-folder.
61 Revision 1.7 2003/08/20 06:45:56 bradley
62 Updated for IP address changes in DNSServices. Added support for browsing for Xserve RAID.
64 Revision 1.6 2003/08/12 19:56:28 cheshire
67 Revision 1.5 2003/07/13 01:03:55 cheshire
68 Diffs provided by Bob Bradley to provide provide proper display of Unicode names
70 Revision 1.4 2003/07/02 21:20:06 cheshire
71 <rdar://problem/3313413> Update copyright notices, etc., in source code comments
73 Revision 1.3 2002/09/21 20:44:55 zarzycki
76 Revision 1.2 2002/09/20 08:39:21 bradley
77 Make sure each resolved item matches the selected service type to handle resolved that may have
78 been queued up on the Windows Message Loop. Reduce column to fit when scrollbar is present.
80 Revision 1.1 2002/09/20 06:12:52 bradley
81 Rendezvous Browser for Windows
96 #include "DNSServices.h"
98 #include "Application.h"
99 #include "AboutDialog.h"
100 #include "LoginDialog.h"
101 #include "Resource.h"
103 #include "ChooserDialog.h"
106 #define new DEBUG_NEW
108 static char THIS_FILE
[] = __FILE__
;
112 #pragma mark == Constants ==
115 //===========================================================================================================================
117 //===========================================================================================================================
123 kChooserMenuIndexFile
= 0,
124 kChooserMenuIndexHelp
= 1
129 #define kDomainListDefaultDomainColumnWidth 164
133 #define kServiceListDefaultServiceColumnTypeWidth 146
134 #define kServiceListDefaultServiceColumnDescWidth 230
138 #define kChooserListDefaultNameColumnWidth 190
139 #define kChooserListDefaultIPColumnWidth 120
141 // Windows User Messages
143 #define WM_USER_DOMAIN_ADD ( WM_USER + 0x100 )
144 #define WM_USER_DOMAIN_REMOVE ( WM_USER + 0x101 )
145 #define WM_USER_SERVICE_ADD ( WM_USER + 0x102 )
146 #define WM_USER_SERVICE_REMOVE ( WM_USER + 0x103 )
147 #define WM_USER_RESOLVE ( WM_USER + 0x104 )
150 #pragma mark == Constants - Service Table ==
153 //===========================================================================================================================
154 // Constants - Service Table
155 //===========================================================================================================================
157 struct KnownServiceEntry
159 const char * serviceType
;
160 const char * description
;
161 const char * urlScheme
;
165 static const KnownServiceEntry kKnownServiceTable
[] =
167 { "_accountedge._tcp.", "MYOB AccountEdge", "", false },
168 { "_aecoretech._tcp.", "Apple Application Engineering Services", "", false },
169 { "_afpovertcp._tcp.", "Apple File Sharing (AFP)", "afp://", false },
170 { "_airport._tcp.", "AirPort Base Station", "", false },
171 { "_apple-sasl._tcp.", "Apple Password Server", "", false },
172 { "_aquamon._tcp.", "AquaMon", "", false },
173 { "_async._tcp", "address-o-sync", "", false },
174 { "_auth._tcp.", "Authentication Service", "", false },
175 { "_bootps._tcp.", "Bootstrap Protocol Server", "", false },
176 { "_bousg._tcp.", "Bag Of Unusual Strategy Games", "", false },
177 { "_browse._udp.", "DNS Service Discovery", "", false },
178 { "_cheat._tcp.", "The Cheat", "", false },
179 { "_chess._tcp", "Project Gridlock", "", false },
180 { "_chfts._tcp", "Fluid Theme Server", "", false },
181 { "_clipboard._tcp", "Clipboard Sharing", "", false },
182 { "_contactserver._tcp.", "Now Up-to-Date & Contact", "", false },
183 { "_cvspserver._tcp", "CVS PServer", "", false },
184 { "_cytv._tcp.", "CyTV Network streaming for Elgato EyeTV", "", false },
185 { "_daap._tcp.", "Digital Audio Access Protocol (iTunes)", "daap://", false },
186 { "_distcc._tcp", "Distributed Compiler", "", false },
187 { "_dns-sd._udp", "DNS Service Discovery", "", false },
188 { "_dpap._tcp.", "Digital Picture Access Protocol (iPhoto)", "", false },
189 { "_earphoria._tcp.", "Earphoria", "", false },
190 { "_ecbyesfsgksc._tcp.", "Net Monitor Anti-Piracy Service", "", false },
191 { "_eheap._tcp.", "Interactive Room Software", "", false },
192 { "_embrace._tcp.", "DataEnvoy", "", false },
193 { "_eppc._tcp.", "Remote AppleEvents", "eppc://", false },
194 { "_exec._tcp.", "Remote Process Execution", "", false },
195 { "_facespan._tcp.", "FaceSpan", "", false },
196 { "_fjork._tcp.", "Fjork", "", false },
197 { "_ftp._tcp.", "File Transfer (FTP)", "ftp://", false },
198 { "_ftpcroco._tcp.", "Crocodile FTP Server", "", false },
199 { "_gbs-smp._tcp.", "SnapMail", "", false },
200 { "_gbs-stp._tcp.", "SnapTalk", "", false },
201 { "_grillezvous._tcp.", "Roxio ToastAnywhere(tm) Recorder Sharing", "", false },
202 { "_h323._tcp.", "H.323", "", false },
203 { "_hotwayd._tcp", "Hotwayd", "", false },
204 { "_http._tcp.", "Web Server (HTTP)", "http://", true },
205 { "_hydra._tcp", "SubEthaEdit", "", false },
206 { "_ica-networking._tcp.", "Image Capture Networking", "", false },
207 { "_ichalkboard._tcp.", "iChalk", "", false },
208 { "_ichat._tcp.", "iChat", "ichat://", false },
209 { "_iconquer._tcp.", "iConquer", "", false },
210 { "_imap._tcp.", "Internet Message Access Protocol", "", false },
211 { "_imidi._tcp.", "iMidi", "", false },
212 { "_ipp._tcp.", "Printer (IPP)", "ipp://", false },
213 { "_ishare._tcp.", "iShare", "", false },
214 { "_isparx._tcp.", "iSparx", "", false },
215 { "_istorm._tcp", "iStorm", "", false },
216 { "_iwork._tcp.", "iWork Server", "", false },
217 { "_liaison._tcp.", "Liaison", "", false },
218 { "_login._tcp.", "Remote Login a la Telnet", "", false },
219 { "_lontalk._tcp.", "LonTalk over IP (ANSI 852)", "", false },
220 { "_lonworks._tcp.", "Echelon LNS Remote Client", "", false },
221 { "_macfoh-remote._tcp.", "MacFOH Remote", "", false },
222 { "_moneyworks._tcp.", "MoneyWorks", "", false },
223 { "_mp3sushi._tcp", "MP3 Sushi", "", false },
224 { "_mttp._tcp.", "MenuTunes Sharing", "", false },
225 { "_ncbroadcast._tcp.", "Network Clipboard Broadcasts", "", false },
226 { "_ncdirect._tcp.", "Network Clipboard Direct Transfers", "", false },
227 { "_ncsyncserver._tcp.", "Network Clipboard Sync Server", "", false },
228 { "_newton-dock._tcp.", "Escale", "", false },
229 { "_nfs._tcp", "NFS", "", false },
230 { "_nssocketport._tcp.", "DO over NSSocketPort", "", false },
231 { "_omni-bookmark._tcp.", "OmniWeb", "", false },
232 { "_openbase._tcp.", "OpenBase SQL", "", false },
233 { "_p2pchat._tcp.", "Peer-to-Peer Chat", "", false },
234 { "_pdl-datastream._tcp.", "Printer (PDL)", "pdl://", false },
235 { "_poch._tcp.", "Parallel OperatiOn and Control Heuristic", "", false },
236 { "_pop_2_ambrosia._tcp.", "Pop-Pop", "", false },
237 { "_pop3._tcp", "POP3 Server", "", false },
238 { "_postgresql._tcp", "PostgreSQL Server", "", false },
239 { "_presence._tcp", "iChat AV", "", false },
240 { "_printer._tcp.", "Printer (LPR)", "lpr://", false },
241 { "_ptp._tcp.", "Picture Transfer (PTP)", "ptp://", false },
242 { "_register._tcp", "DNS Service Discovery", "", false },
243 { "_rendezvouspong._tcp", "RendezvousPong", "", false },
244 { "_rfb._tcp.", "Remote Frame Buffer", "", false },
245 { "_riousbprint._tcp.", "Remote I/O USB Printer Protocol", "", false },
246 { "_rtsp._tcp.", "Real Time Stream Control Protocol", "", false },
247 { "_safarimenu._tcp", "Safari Menu", "", false },
248 { "_scone._tcp", "Scone", "", false },
249 { "_sdsharing._tcp.", "Speed Download", "", false },
250 { "_seeCard._tcp.", "seeCard", "", false },
251 { "_services._udp.", "DNS Service Discovery", "", false },
252 { "_shell._tcp.", "like exec, but automatic authentication", "", false },
253 { "_shout._tcp.", "Shout", "", false },
254 { "_shoutcast._tcp", "Nicecast", "", false },
255 { "_smb._tcp.", "Windows File Sharing (SMB)", "smb://", false },
256 { "_soap._tcp.", "Simple Object Access Protocol", "", false },
257 { "_spincrisis._tcp.", "Spin Crisis", "", false },
258 { "_spl-itunes._tcp.", "launchTunes", "", false },
259 { "_spr-itunes._tcp.", "netTunes", "", false },
260 { "_ssh._tcp.", "Secure Shell (SSH)", "ssh://", false },
261 { "_ssscreenshare._tcp", "Screen Sharing", "", false },
262 { "_sge-exec._tcp", "Sun Grid Engine (Execution Host)", "", false },
263 { "_sge-qmaster._tcp", "Sun Grid Engine (Master)", "", false },
264 { "_stickynotes._tcp", "Sticky Notes", "", false },
265 { "_strateges._tcp", "Strateges", "", false },
266 { "_sxqdea._tcp", "Synchronize! Pro X", "", false },
267 { "_sybase-tds._tcp", "Sybase Server", "", false },
268 { "_tce._tcp", "Power Card", "", false },
269 { "_teamlist._tcp", "ARTIS Team Task", "", false },
270 { "_teleport._tcp", "teleport", "", false },
271 { "_telnet._tcp.", "Telnet", "telnet://", false },
272 { "_tftp._tcp.", "Trivial File Transfer (TFTP)", "tftp://", false },
273 { "_tinavigator._tcp.", "TI Navigator", "", false },
274 { "_tivo_servemedia._tcp", "TiVo", "", false },
275 { "_upnp._tcp.", "Universal Plug and Play", "", false },
276 { "_utest._tcp.", "uTest", "", false },
277 { "_vue4rendercow._tcp", "VueProRenderCow", "", false },
278 { "_webdav._tcp.", "WebDAV", "webdav://", false },
279 { "_whamb._tcp.", "Whamb", "", false },
280 { "_workstation._tcp", "Macintosh Manager", "", false },
281 { "_ws._tcp", "Web Services", "", false },
282 { "_xserveraid._tcp.", "Xserve RAID", "xsr://", false },
283 { "_xsync._tcp.", "Xserve RAID Synchronization", "", false },
285 { "", "", "", false },
287 // Unofficial and invalid service types that will be phased out:
289 { "_clipboardsharing._tcp.", "ClipboardSharing", "", false },
290 { "_MacOSXDupSuppress._tcp.", "Mac OS X Duplicate Suppression", "", false },
291 { "_netmonitorserver._tcp.", "Net Monitor Server", "", false },
292 { "_networkclipboard._tcp.", "Network Clipboard", "", false },
293 { "_slimdevices_slimp3_cli._tcp.", "SliMP3 Server Command-Line Interface", "", false },
294 { "_slimdevices_slimp3_http._tcp.", "SliMP3 Server Web Interface", "", false },
295 { "_tieducationalhandhelddevice._tcp.", "TI Connect Manager", "", false },
296 { "_tivo_servemedia._tcp.", "TiVo", "", false },
298 { NULL
, NULL
, NULL
, false },
302 #pragma mark == Structures ==
305 //===========================================================================================================================
307 //===========================================================================================================================
309 struct DomainEventInfo
311 DNSBrowserEventType eventType
;
313 DNSNetworkAddress ifIP
;
316 struct ServiceEventInfo
318 DNSBrowserEventType eventType
;
322 DNSNetworkAddress ifIP
;
326 #pragma mark == Prototypes ==
329 //===========================================================================================================================
331 //===========================================================================================================================
337 DNSStatus inStatusCode
,
338 const DNSBrowserEvent
* inEvent
);
340 static char * DNSNetworkAddressToString( const DNSNetworkAddress
*inAddr
, char *outString
);
342 static DWORD
UTF8StringToStringObject( const char *inUTF8
, CString
&inObject
);
343 static DWORD
StringObjectToUTF8String( CString
&inObject
, std::string
&outUTF8
);
346 #pragma mark == Message Map ==
349 //===========================================================================================================================
351 //===========================================================================================================================
353 BEGIN_MESSAGE_MAP(ChooserDialog
, CDialog
)
354 //{{AFX_MSG_MAP(ChooserDialog)
356 ON_NOTIFY(LVN_ITEMCHANGED
, IDC_DOMAIN_LIST
, OnDomainListChanged
)
357 ON_NOTIFY(LVN_ITEMCHANGED
, IDC_SERVICE_LIST
, OnServiceListChanged
)
358 ON_NOTIFY(LVN_ITEMCHANGED
, IDC_CHOOSER_LIST
, OnChooserListChanged
)
359 ON_NOTIFY(NM_DBLCLK
, IDC_CHOOSER_LIST
, OnChooserListDoubleClick
)
360 ON_COMMAND(ID_HELP_ABOUT
, OnAbout
)
361 ON_WM_INITMENUPOPUP()
363 ON_COMMAND(ID_FILE_CLOSE
, OnFileClose
)
364 ON_COMMAND(ID_FILE_EXIT
, OnExit
)
368 ON_MESSAGE( WM_USER_DOMAIN_ADD
, OnDomainAdd
)
369 ON_MESSAGE( WM_USER_DOMAIN_REMOVE
, OnDomainRemove
)
370 ON_MESSAGE( WM_USER_SERVICE_ADD
, OnServiceAdd
)
371 ON_MESSAGE( WM_USER_SERVICE_REMOVE
, OnServiceRemove
)
372 ON_MESSAGE( WM_USER_RESOLVE
, OnResolve
)
376 #pragma mark == Routines ==
379 //===========================================================================================================================
381 //===========================================================================================================================
383 ChooserDialog::ChooserDialog( CWnd
*inParent
)
384 : CDialog( ChooserDialog::IDD
, inParent
)
386 //{{AFX_DATA_INIT(ChooserDialog)
387 // NOTE: the ClassWizard will add member initialization here
390 // Load menu accelerator table.
392 mMenuAcceleratorTable
= ::LoadAccelerators( AfxGetInstanceHandle(), MAKEINTRESOURCE( IDR_CHOOSER_DIALOG_MENU_ACCELERATORS
) );
393 assert( mMenuAcceleratorTable
);
396 mIsServiceBrowsing
= false;
399 //===========================================================================================================================
401 //===========================================================================================================================
403 ChooserDialog::~ChooserDialog( void )
409 err
= DNSBrowserRelease( mBrowser
, 0 );
410 assert( err
== kDNSNoErr
);
414 //===========================================================================================================================
416 //===========================================================================================================================
418 void ChooserDialog::DoDataExchange( CDataExchange
*pDX
)
420 CDialog::DoDataExchange(pDX
);
422 //{{AFX_DATA_MAP(ChooserDialog)
423 DDX_Control(pDX
, IDC_SERVICE_LIST
, mServiceList
);
424 DDX_Control(pDX
, IDC_DOMAIN_LIST
, mDomainList
);
425 DDX_Control(pDX
, IDC_CHOOSER_LIST
, mChooserList
);
429 //===========================================================================================================================
431 //===========================================================================================================================
433 BOOL
ChooserDialog::OnInitDialog( void )
440 // Initialize our parent.
442 CDialog::OnInitDialog();
444 // Set up the window icon.
446 icon
= AfxGetApp()->LoadIcon( IDR_MAIN_ICON
);
450 SetIcon( icon
, TRUE
); // Set big icon
451 SetIcon( icon
, FALSE
); // Set small icon
454 // Set up the Domain List.
456 result
= tempString
.LoadString( IDS_CHOOSER_DOMAIN_COLUMN_NAME
);
458 mDomainList
.InsertColumn( 0, tempString
, LVCFMT_LEFT
, kDomainListDefaultDomainColumnWidth
);
460 // Set up the Service List.
462 result
= tempString
.LoadString( IDS_CHOOSER_SERVICE_COLUMN_TYPE
);
464 mServiceList
.InsertColumn( 0, tempString
, LVCFMT_LEFT
, kServiceListDefaultServiceColumnTypeWidth
);
466 result
= tempString
.LoadString( IDS_CHOOSER_SERVICE_COLUMN_DESC
);
468 mServiceList
.InsertColumn( 1, tempString
, LVCFMT_LEFT
, kServiceListDefaultServiceColumnDescWidth
);
470 PopulateServicesList();
472 // Set up the Chooser List.
474 result
= tempString
.LoadString( IDS_CHOOSER_CHOOSER_NAME_COLUMN_NAME
);
476 mChooserList
.InsertColumn( 0, tempString
, LVCFMT_LEFT
, kChooserListDefaultNameColumnWidth
);
478 result
= tempString
.LoadString( IDS_CHOOSER_CHOOSER_IP_COLUMN_NAME
);
480 mChooserList
.InsertColumn( 1, tempString
, LVCFMT_LEFT
, kChooserListDefaultIPColumnWidth
);
482 // Set up the other controls.
486 // Start browsing for domains.
488 err
= DNSBrowserCreate( 0, BrowserCallBack
, this, &mBrowser
);
489 assert( err
== kDNSNoErr
);
491 err
= DNSBrowserStartDomainSearch( mBrowser
, 0 );
492 assert( err
== kDNSNoErr
);
497 //===========================================================================================================================
499 //===========================================================================================================================
501 void ChooserDialog::OnFileClose()
506 //===========================================================================================================================
508 //===========================================================================================================================
510 void ChooserDialog::OnActivate( UINT nState
, CWnd
* pWndOther
, BOOL bMinimized
)
512 // Always make the active window the "main" window so modal dialogs work better and the app quits after closing
515 gApp
.m_pMainWnd
= this;
517 CDialog::OnActivate(nState
, pWndOther
, bMinimized
);
520 //===========================================================================================================================
522 //===========================================================================================================================
524 void ChooserDialog::PostNcDestroy()
526 // Call the base class to do the normal cleanup.
531 //===========================================================================================================================
532 // PreTranslateMessage
533 //===========================================================================================================================
535 BOOL
ChooserDialog::PreTranslateMessage(MSG
* pMsg
)
540 assert( mMenuAcceleratorTable
);
541 if( mMenuAcceleratorTable
)
543 result
= ::TranslateAccelerator( m_hWnd
, mMenuAcceleratorTable
, pMsg
);
547 result
= CDialog::PreTranslateMessage( pMsg
);
552 //===========================================================================================================================
554 //===========================================================================================================================
556 void ChooserDialog::OnInitMenuPopup( CMenu
*pPopupMenu
, UINT nIndex
, BOOL bSysMenu
)
558 CDialog::OnInitMenuPopup( pPopupMenu
, nIndex
, bSysMenu
);
562 case kChooserMenuIndexFile
:
565 case kChooserMenuIndexHelp
:
573 //===========================================================================================================================
575 //===========================================================================================================================
577 void ChooserDialog::OnExit()
582 //===========================================================================================================================
584 //===========================================================================================================================
586 void ChooserDialog::OnAbout()
593 //===========================================================================================================================
595 //===========================================================================================================================
597 void ChooserDialog::OnSysCommand( UINT inID
, LPARAM inParam
)
599 CDialog::OnSysCommand( inID
, inParam
);
602 //===========================================================================================================================
604 //===========================================================================================================================
606 void ChooserDialog::OnClose()
610 gApp
.m_pMainWnd
= this;
614 //===========================================================================================================================
616 //===========================================================================================================================
618 void ChooserDialog::OnNcDestroy()
620 gApp
.m_pMainWnd
= this;
622 CDialog::OnNcDestroy();
625 //===========================================================================================================================
626 // OnDomainListChanged
627 //===========================================================================================================================
629 void ChooserDialog::OnDomainListChanged( NMHDR
*pNMHDR
, LRESULT
*pResult
)
631 UNUSED_ALWAYS( pNMHDR
);
633 // Domain list changes have similar effects to service list changes so reuse that code path by calling it here.
635 OnServiceListChanged( NULL
, NULL
);
640 //===========================================================================================================================
641 // OnServiceListChanged
642 //===========================================================================================================================
644 void ChooserDialog::OnServiceListChanged( NMHDR
*pNMHDR
, LRESULT
*pResult
)
649 UNUSED_ALWAYS( pNMHDR
);
651 // Stop any existing service search.
655 // If a domain and service type are selected, start searching for the service type on the domain.
657 selectedType
= mServiceList
.GetNextItem( -1, LVNI_SELECTED
);
658 selectedDomain
= mDomainList
.GetNextItem( -1, LVNI_SELECTED
);
660 if( ( selectedType
>= 0 ) && ( selectedDomain
>= 0 ) )
666 s
= mDomainList
.GetItemText( selectedDomain
, 0 );
667 StringObjectToUTF8String( s
, utf8
);
668 type
= mServiceTypes
[ selectedType
].serviceType
.c_str();
671 StartBrowsing( type
, utf8
.c_str() );
681 //===========================================================================================================================
682 // OnChooserListChanged
683 //===========================================================================================================================
685 void ChooserDialog::OnChooserListChanged( NMHDR
*pNMHDR
, LRESULT
*pResult
)
687 UNUSED_ALWAYS( pNMHDR
);
693 //===========================================================================================================================
694 // OnChooserListDoubleClick
695 //===========================================================================================================================
697 void ChooserDialog::OnChooserListDoubleClick( NMHDR
*pNMHDR
, LRESULT
*pResult
)
701 UNUSED_ALWAYS( pNMHDR
);
703 // Display the service instance if it is selected. Otherwise, clear all the info.
705 selectedItem
= mChooserList
.GetNextItem( -1, LVNI_SELECTED
);
706 if( selectedItem
>= 0 )
708 ServiceInstanceInfo
* p
;
710 const KnownServiceEntry
* service
;
712 assert( selectedItem
< (int) mServiceInstances
.size() );
713 p
= &mServiceInstances
[ selectedItem
];
715 // Search for a known service type entry that matches.
717 for( service
= kKnownServiceTable
; service
->serviceType
; ++service
)
719 if( p
->type
== service
->serviceType
)
724 if( service
->serviceType
)
728 // Create a URL representing the service instance.
730 if( strcmp( service
->serviceType
, "_smb._tcp." ) == 0 )
732 // Special case for SMB (no port number).
734 url
.Format( TEXT( "%s%s/" ), service
->urlScheme
, (const char *) p
->ip
.c_str() );
736 else if( strcmp( service
->serviceType
, "_ftp._tcp." ) == 0 )
738 // Special case for FTP to get login info.
744 if( !dialog
.GetLogin( username
, password
) )
749 // Build URL in the following format:
751 // ftp://[username[:password]@]<ip>
753 url
+= service
->urlScheme
;
754 if( username
.GetLength() > 0 )
757 if( password
.GetLength() > 0 )
764 url
+= p
->ip
.c_str();
766 else if( strcmp( service
->serviceType
, "_http._tcp." ) == 0 )
768 // Special case for HTTP to exclude "path=" if present.
770 text
= service
->useText
? p
->text
.c_str() : "";
771 if( strncmp( text
, "path=", 5 ) == 0 )
777 url
.Format( TEXT( "%s%s/%s" ), service
->urlScheme
, (const char *) p
->ip
.c_str(), text
);
781 url
.Format( TEXT( "%s%s%s" ), service
->urlScheme
, (const char *) p
->ip
.c_str(), text
);
786 text
= service
->useText
? p
->text
.c_str() : "";
787 url
.Format( TEXT( "%s%s/%s" ), service
->urlScheme
, (const char *) p
->ip
.c_str(), text
);
790 // Let the system open the URL in the correct app.
793 CWaitCursor waitCursor
;
795 ShellExecute( NULL
, TEXT( "open" ), url
, TEXT( "" ), TEXT( "c:\\" ), SW_SHOWNORMAL
);
804 //===========================================================================================================================
806 //===========================================================================================================================
808 void ChooserDialog::OnCancel()
813 //===========================================================================================================================
814 // PopulateServicesList
815 //===========================================================================================================================
817 void ChooserDialog::PopulateServicesList( void )
819 ServiceTypeVector::iterator i
;
824 // Add a fixed list of known services.
826 if( mServiceTypes
.empty() )
828 const KnownServiceEntry
* service
;
830 for( service
= kKnownServiceTable
; service
->serviceType
; ++service
)
832 ServiceTypeInfo info
;
834 info
.serviceType
= service
->serviceType
;
835 info
.description
= service
->description
;
836 info
.urlScheme
= service
->urlScheme
;
837 mServiceTypes
.push_back( info
);
841 // Add each service to the list.
843 for( i
= mServiceTypes
.begin(); i
!= mServiceTypes
.end(); ++i
)
848 p
= ( *i
).serviceType
.c_str();
849 if( *p
== '_' ) ++p
; // Skip leading '_'.
850 q
= strchr( p
, '.' ); // Find first '.'.
851 if( q
) tmp
.assign( p
, (size_t)( q
- p
) ); // Use only up to the first '.'.
852 else tmp
.assign( p
); // No '.' so use the entire string.
853 UTF8StringToStringObject( tmp
.c_str(), type
);
854 UTF8StringToStringObject( ( *i
).description
.c_str(), desc
);
858 n
= mServiceList
.GetItemCount();
859 mServiceList
.InsertItem( n
, type
);
860 mServiceList
.SetItemText( n
, 1, desc
);
863 // Select the first service type by default.
865 if( !mServiceTypes
.empty() )
867 mServiceList
.SetItemState( 0, LVIS_SELECTED
| LVIS_FOCUSED
, LVIS_SELECTED
| LVIS_FOCUSED
);
871 //===========================================================================================================================
873 //===========================================================================================================================
875 void ChooserDialog::UpdateInfoDisplay( void )
883 std::string textNewLines
;
884 std::string hostName
;
886 std::string::iterator i
;
888 // Display the service instance if it is selected. Otherwise, clear all the info.
890 selectedItem
= mChooserList
.GetNextItem( -1, LVNI_SELECTED
);
891 if( selectedItem
>= 0 )
893 ServiceInstanceInfo
* p
;
895 assert( selectedItem
< (int) mServiceInstances
.size() );
896 p
= &mServiceInstances
[ selectedItem
];
902 hostName
= p
->hostName
;
904 // Sync up the list items with the actual data (IP address may change).
906 UTF8StringToStringObject( ip
.c_str(), s
);
907 mChooserList
.SetItemText( selectedItem
, 1, s
);
912 item
= (CWnd
*) this->GetDlgItem( IDC_INFO_NAME_TEXT
);
914 UTF8StringToStringObject( name
.c_str(), s
);
915 item
->SetWindowText( s
);
919 item
= (CWnd
*) this->GetDlgItem( IDC_INFO_IP_TEXT
);
921 UTF8StringToStringObject( ip
.c_str(), s
);
922 item
->SetWindowText( s
);
926 item
= (CWnd
*) this->GetDlgItem( IDC_INFO_INTERFACE_TEXT
);
928 UTF8StringToStringObject( ifIP
.c_str(), s
);
929 item
->SetWindowText( s
);
932 item
= (CWnd
*) this->GetDlgItem( IDC_INFO_HOST_NAME_TEXT
);
934 UTF8StringToStringObject( hostName
.c_str(), s
);
935 item
->SetWindowText( s
);
939 item
= (CWnd
*) this->GetDlgItem( IDC_INFO_TEXT_TEXT
);
941 for( i
= text
.begin(); i
!= text
.end(); ++i
)
945 textNewLines
+= "\r\n";
952 UTF8StringToStringObject( textNewLines
.c_str(), s
);
953 item
->SetWindowText( s
);
960 //===========================================================================================================================
962 //===========================================================================================================================
964 LONG
ChooserDialog::OnDomainAdd( WPARAM inWParam
, LPARAM inLParam
)
967 std::auto_ptr
< DomainEventInfo
> pAutoPtr
;
974 UNUSED_ALWAYS( inWParam
);
977 p
= reinterpret_cast <DomainEventInfo
*> ( inLParam
);
980 // Search to see if we already know about this domain. If not, add it to the list.
984 n
= mDomainList
.GetItemCount();
985 for( i
= 0; i
< n
; ++i
)
987 s
= mDomainList
.GetItemText( i
, 0 );
998 mDomainList
.InsertItem( n
, domain
);
1000 // If no domains are selected and the domain being added is a default domain, select it.
1002 selectedItem
= mDomainList
.GetNextItem( -1, LVNI_SELECTED
);
1003 if( ( selectedItem
< 0 ) && ( p
->eventType
== kDNSBrowserEventTypeAddDefaultDomain
) )
1005 mDomainList
.SetItemState( n
, LVIS_SELECTED
| LVIS_FOCUSED
, LVIS_SELECTED
| LVIS_FOCUSED
);
1011 //===========================================================================================================================
1013 //===========================================================================================================================
1015 LONG
ChooserDialog::OnDomainRemove( WPARAM inWParam
, LPARAM inLParam
)
1017 DomainEventInfo
* p
;
1018 std::auto_ptr
< DomainEventInfo
> pAutoPtr
;
1025 UNUSED_ALWAYS( inWParam
);
1028 p
= reinterpret_cast <DomainEventInfo
*> ( inLParam
);
1029 pAutoPtr
.reset( p
);
1031 // Search to see if we know about this domain. If so, remove it from the list.
1035 n
= mDomainList
.GetItemCount();
1036 for( i
= 0; i
< n
; ++i
)
1038 s
= mDomainList
.GetItemText( i
, 0 );
1047 mDomainList
.DeleteItem( i
);
1052 //===========================================================================================================================
1054 //===========================================================================================================================
1056 LONG
ChooserDialog::OnServiceAdd( WPARAM inWParam
, LPARAM inLParam
)
1058 ServiceEventInfo
* p
;
1059 std::auto_ptr
< ServiceEventInfo
> pAutoPtr
;
1061 UNUSED_ALWAYS( inWParam
);
1064 p
= reinterpret_cast <ServiceEventInfo
*> ( inLParam
);
1065 pAutoPtr
.reset( p
);
1070 //===========================================================================================================================
1072 //===========================================================================================================================
1074 LONG
ChooserDialog::OnServiceRemove( WPARAM inWParam
, LPARAM inLParam
)
1076 ServiceEventInfo
* p
;
1077 std::auto_ptr
< ServiceEventInfo
> pAutoPtr
;
1082 UNUSED_ALWAYS( inWParam
);
1085 p
= reinterpret_cast <ServiceEventInfo
*> ( inLParam
);
1086 pAutoPtr
.reset( p
);
1088 // Search to see if we know about this service instance. If so, remove it from the list.
1091 n
= (int) mServiceInstances
.size();
1092 for( i
= 0; i
< n
; ++i
)
1094 ServiceInstanceInfo
* q
;
1096 // If the name, type, domain, and interface match, treat it as the same service instance.
1098 q
= &mServiceInstances
[ i
];
1099 if( ( p
->name
== q
->name
) &&
1100 ( p
->type
== q
->type
) &&
1101 ( p
->domain
== q
->domain
) )
1109 mChooserList
.DeleteItem( i
);
1110 assert( i
< (int) mServiceInstances
.size() );
1111 mServiceInstances
.erase( mServiceInstances
.begin() + i
);
1116 //===========================================================================================================================
1118 //===========================================================================================================================
1120 LONG
ChooserDialog::OnResolve( WPARAM inWParam
, LPARAM inLParam
)
1122 ServiceInstanceInfo
* p
;
1123 std::auto_ptr
< ServiceInstanceInfo
> pAutoPtr
;
1129 UNUSED_ALWAYS( inWParam
);
1132 p
= reinterpret_cast <ServiceInstanceInfo
*> ( inLParam
);
1133 pAutoPtr
.reset( p
);
1135 // Make sure it is for an item of the correct type. This handles any resolves that may have been queued up.
1137 selectedType
= mServiceList
.GetNextItem( -1, LVNI_SELECTED
);
1138 assert( selectedType
>= 0 );
1139 if( selectedType
>= 0 )
1141 assert( selectedType
<= (int) mServiceTypes
.size() );
1142 if( p
->type
!= mServiceTypes
[ selectedType
].serviceType
)
1148 // Search to see if we know about this service instance. If so, update its info. Otherwise, add it to the list.
1151 n
= (int) mServiceInstances
.size();
1152 for( i
= 0; i
< n
; ++i
)
1154 ServiceInstanceInfo
* q
;
1156 // If the name, type, domain, and interface matches, treat it as the same service instance.
1158 q
= &mServiceInstances
[ i
];
1159 if( ( p
->name
== q
->name
) &&
1160 ( p
->type
== q
->type
) &&
1161 ( p
->domain
== q
->domain
) &&
1162 ( p
->ifIP
== q
->ifIP
) )
1170 mServiceInstances
[ i
] = *p
;
1176 mServiceInstances
.push_back( *p
);
1177 UTF8StringToStringObject( p
->name
.c_str(), s
);
1178 mChooserList
.InsertItem( n
, s
);
1180 UTF8StringToStringObject( p
->ip
.c_str(), s
);
1181 mChooserList
.SetItemText( n
, 1, s
);
1183 // If this is the only item, select it.
1187 mChooserList
.SetItemState( n
, LVIS_SELECTED
| LVIS_FOCUSED
, LVIS_SELECTED
| LVIS_FOCUSED
);
1190 UpdateInfoDisplay();
1196 //===========================================================================================================================
1198 //===========================================================================================================================
1200 void ChooserDialog::StartBrowsing( const char *inType
, const char *inDomain
)
1204 assert( mServiceInstances
.empty() );
1205 assert( mChooserList
.GetItemCount() == 0 );
1206 assert( !mIsServiceBrowsing
);
1208 mChooserList
.DeleteAllItems();
1209 mServiceInstances
.clear();
1211 mIsServiceBrowsing
= true;
1212 err
= DNSBrowserStartServiceSearch( mBrowser
, kDNSBrowserFlagAutoResolve
, inType
, inDomain
);
1213 assert( err
== kDNSNoErr
);
1216 //===========================================================================================================================
1218 //===========================================================================================================================
1220 void ChooserDialog::StopBrowsing( void )
1222 // If searching, stop.
1224 if( mIsServiceBrowsing
)
1228 mIsServiceBrowsing
= false;
1229 err
= DNSBrowserStopServiceSearch( mBrowser
, 0 );
1230 assert( err
== kDNSNoErr
);
1233 // Remove all service instances.
1235 mChooserList
.DeleteAllItems();
1236 assert( mChooserList
.GetItemCount() == 0 );
1237 mServiceInstances
.clear();
1238 assert( mServiceInstances
.empty() );
1239 UpdateInfoDisplay();
1246 //===========================================================================================================================
1248 //===========================================================================================================================
1253 DNSBrowserRef inRef
,
1254 DNSStatus inStatusCode
,
1255 const DNSBrowserEvent
* inEvent
)
1257 ChooserDialog
* dialog
;
1261 UNUSED_ALWAYS( inStatusCode
);
1262 UNUSED_ALWAYS( inRef
);
1264 // Check parameters.
1266 assert( inContext
);
1267 dialog
= reinterpret_cast <ChooserDialog
*> ( inContext
);
1271 switch( inEvent
->type
)
1273 case kDNSBrowserEventTypeRelease
:
1278 case kDNSBrowserEventTypeAddDomain
:
1279 case kDNSBrowserEventTypeAddDefaultDomain
:
1280 case kDNSBrowserEventTypeRemoveDomain
:
1282 DomainEventInfo
* domain
;
1283 std::auto_ptr
< DomainEventInfo
> domainAutoPtr
;
1285 domain
= new DomainEventInfo
;
1286 domainAutoPtr
.reset( domain
);
1288 domain
->eventType
= inEvent
->type
;
1289 domain
->domain
= inEvent
->data
.addDomain
.domain
;
1290 domain
->ifIP
= inEvent
->data
.addDomain
.interfaceIP
;
1292 message
= ( inEvent
->type
== kDNSBrowserEventTypeRemoveDomain
) ? WM_USER_DOMAIN_REMOVE
: WM_USER_DOMAIN_ADD
;
1293 posted
= ::PostMessage( dialog
->GetSafeHwnd(), message
, 0, (LPARAM
) domain
);
1297 domainAutoPtr
.release();
1304 case kDNSBrowserEventTypeAddService
:
1305 case kDNSBrowserEventTypeRemoveService
:
1307 ServiceEventInfo
* service
;
1308 std::auto_ptr
< ServiceEventInfo
> serviceAutoPtr
;
1310 service
= new ServiceEventInfo
;
1311 serviceAutoPtr
.reset( service
);
1313 service
->eventType
= inEvent
->type
;
1314 service
->name
= inEvent
->data
.addService
.name
;
1315 service
->type
= inEvent
->data
.addService
.type
;
1316 service
->domain
= inEvent
->data
.addService
.domain
;
1317 service
->ifIP
= inEvent
->data
.addService
.interfaceIP
;
1319 message
= ( inEvent
->type
== kDNSBrowserEventTypeAddService
) ? WM_USER_SERVICE_ADD
: WM_USER_SERVICE_REMOVE
;
1320 posted
= ::PostMessage( dialog
->GetSafeHwnd(), message
, 0, (LPARAM
) service
);
1324 serviceAutoPtr
.release();
1331 case kDNSBrowserEventTypeResolved
:
1332 if( inEvent
->data
.resolved
->address
.addressType
== kDNSNetworkAddressTypeIPv4
)
1334 ServiceInstanceInfo
* serviceInstance
;
1335 std::auto_ptr
< ServiceInstanceInfo
> serviceInstanceAutoPtr
;
1338 serviceInstance
= new ServiceInstanceInfo
;
1339 serviceInstanceAutoPtr
.reset( serviceInstance
);
1341 serviceInstance
->name
= inEvent
->data
.resolved
->name
;
1342 serviceInstance
->type
= inEvent
->data
.resolved
->type
;
1343 serviceInstance
->domain
= inEvent
->data
.resolved
->domain
;
1344 serviceInstance
->ip
= DNSNetworkAddressToString( &inEvent
->data
.resolved
->address
, s
);
1345 serviceInstance
->ifIP
= DNSNetworkAddressToString( &inEvent
->data
.resolved
->interfaceIP
, s
);
1346 serviceInstance
->text
= inEvent
->data
.resolved
->textRecord
;
1347 serviceInstance
->hostName
= inEvent
->data
.resolved
->hostName
;
1349 posted
= ::PostMessage( dialog
->GetSafeHwnd(), WM_USER_RESOLVE
, 0, (LPARAM
) serviceInstance
);
1353 serviceInstanceAutoPtr
.release();
1364 // Don't let exceptions escape.
1368 //===========================================================================================================================
1369 // DNSNetworkAddressToString
1371 // Note: Currently only supports IPv4 network addresses.
1372 //===========================================================================================================================
1374 static char * DNSNetworkAddressToString( const DNSNetworkAddress
*inAddr
, char *outString
)
1379 p
= inAddr
->u
.ipv4
.addr
.v8
;
1380 port
= ntohs( inAddr
->u
.ipv4
.port
.v16
);
1381 if( port
!= kDNSPortInvalid
)
1383 sprintf( outString
, "%u.%u.%u.%u:%u", p
[ 0 ], p
[ 1 ], p
[ 2 ], p
[ 3 ], port
);
1387 sprintf( outString
, "%u.%u.%u.%u", p
[ 0 ], p
[ 1 ], p
[ 2 ], p
[ 3 ] );
1389 return( outString
);
1392 //===========================================================================================================================
1393 // UTF8StringToStringObject
1394 //===========================================================================================================================
1396 static DWORD
UTF8StringToStringObject( const char *inUTF8
, CString
&inObject
)
1404 n
= MultiByteToWideChar( CP_UTF8
, 0, inUTF8
, -1, NULL
, 0 );
1407 unicode
= (BSTR
) malloc( (size_t)( n
* sizeof( wchar_t ) ) );
1410 err
= ERROR_INSUFFICIENT_BUFFER
;
1414 n
= MultiByteToWideChar( CP_UTF8
, 0, inUTF8
, -1, unicode
, n
);
1421 err
= ERROR_NO_UNICODE_TRANSLATION
;
1439 //===========================================================================================================================
1440 // StringObjectToUTF8String
1441 //===========================================================================================================================
1443 static DWORD
StringObjectToUTF8String( CString
&inObject
, std::string
&outUTF8
)
1454 nUnicode
= inObject
.GetLength();
1457 unicode
= inObject
.AllocSysString();
1458 n
= WideCharToMultiByte( CP_UTF8
, 0, unicode
, nUnicode
, NULL
, 0, NULL
, NULL
);
1461 utf8
= (char *) malloc( (size_t) n
);
1463 if( !utf8
) { err
= ERROR_INSUFFICIENT_BUFFER
; goto exit
; }
1465 n
= WideCharToMultiByte( CP_UTF8
, 0, unicode
, nUnicode
, utf8
, n
, NULL
, NULL
);
1470 outUTF8
.assign( utf8
, n
);
1474 err
= ERROR_NO_UNICODE_TRANSLATION
;
1487 SysFreeString( unicode
);