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.2 2004/07/13 21:24:26 rpantos
27 Fix for <rdar://problem/3701120>.
29 Revision 1.1 2004/06/18 04:04:36 rpantos
32 Revision 1.10 2004/04/23 01:19:41 bradley
33 Changed TXT record new line delimiter from \n to \r\n so it works now that it is an edit text.
35 Revision 1.9 2004/03/07 05:51:04 bradley
36 Updated service type list table to include all service types from dns-sd.org as of 2004-03-06.
37 Added separate Service Type and Service Description columns so both are show in the window.
39 Revision 1.8 2004/01/30 02:56:32 bradley
40 Updated to support full Unicode display. Added support for all services on www.dns-sd.org.
42 Revision 1.7 2003/12/25 03:42:04 bradley
43 Added login dialog to get username/password when going to FTP sites. Added more services.
45 Revision 1.6 2003/10/31 12:18:30 bradley
46 Added display of the resolved host name. Show separate TXT record entries on separate lines.
48 Revision 1.5 2003/10/16 09:21:56 bradley
49 Ignore non-IPv4 resolves until mDNS on Windows supports IPv6.
51 Revision 1.4 2003/10/10 03:41:29 bradley
52 Changed HTTP double-click handling to work with or without the path= prefix in the TXT record.
54 Revision 1.3 2003/10/09 19:50:40 bradley
55 Sort service type list by description.
57 Revision 1.2 2003/10/09 19:41:29 bradley
58 Changed quit handling to go through normal close path so dialog is freed on quit. Integrated changes
59 from Andrew van der Stock for the addition of an _rfb._tcp service type for a VNC Remote Framebuffer
60 Server for KDE support. Widened service type list to handle larger service type descriptions.
62 Revision 1.1 2003/08/21 02:06:47 bradley
63 Moved DNSServiceBrowser for non-Windows CE into Windows sub-folder.
65 Revision 1.7 2003/08/20 06:45:56 bradley
66 Updated for IP address changes in DNSServices. Added support for browsing for Xserve RAID.
68 Revision 1.6 2003/08/12 19:56:28 cheshire
71 Revision 1.5 2003/07/13 01:03:55 cheshire
72 Diffs provided by Bob Bradley to provide provide proper display of Unicode names
74 Revision 1.4 2003/07/02 21:20:06 cheshire
75 <rdar://problem/3313413> Update copyright notices, etc., in source code comments
77 Revision 1.3 2002/09/21 20:44:55 zarzycki
80 Revision 1.2 2002/09/20 08:39:21 bradley
81 Make sure each resolved item matches the selected service type to handle resolved that may have
82 been queued up on the Windows Message Loop. Reduce column to fit when scrollbar is present.
84 Revision 1.1 2002/09/20 06:12:52 bradley
85 DNSServiceBrowser for Windows
100 #include "DNSServices.h"
102 #include "Application.h"
103 #include "AboutDialog.h"
104 #include "LoginDialog.h"
105 #include "Resource.h"
107 #include "ChooserDialog.h"
110 #define new DEBUG_NEW
112 static char THIS_FILE
[] = __FILE__
;
116 #pragma mark == Constants ==
119 //===========================================================================================================================
121 //===========================================================================================================================
127 kChooserMenuIndexFile
= 0,
128 kChooserMenuIndexHelp
= 1
133 #define kDomainListDefaultDomainColumnWidth 164
137 #define kServiceListDefaultServiceColumnTypeWidth 146
138 #define kServiceListDefaultServiceColumnDescWidth 230
142 #define kChooserListDefaultNameColumnWidth 190
143 #define kChooserListDefaultIPColumnWidth 120
145 // Windows User Messages
147 #define WM_USER_DOMAIN_ADD ( WM_USER + 0x100 )
148 #define WM_USER_DOMAIN_REMOVE ( WM_USER + 0x101 )
149 #define WM_USER_SERVICE_ADD ( WM_USER + 0x102 )
150 #define WM_USER_SERVICE_REMOVE ( WM_USER + 0x103 )
151 #define WM_USER_RESOLVE ( WM_USER + 0x104 )
154 #pragma mark == Constants - Service Table ==
157 //===========================================================================================================================
158 // Constants - Service Table
159 //===========================================================================================================================
161 struct KnownServiceEntry
163 const char * serviceType
;
164 const char * description
;
165 const char * urlScheme
;
169 static const KnownServiceEntry kKnownServiceTable
[] =
171 { "_accountedge._tcp.", "MYOB AccountEdge", "", false },
172 { "_aecoretech._tcp.", "Apple Application Engineering Services", "", false },
173 { "_afpovertcp._tcp.", "Apple File Sharing (AFP)", "afp://", false },
174 { "_airport._tcp.", "AirPort Base Station", "", false },
175 { "_apple-sasl._tcp.", "Apple Password Server", "", false },
176 { "_aquamon._tcp.", "AquaMon", "", false },
177 { "_async._tcp", "address-o-sync", "", false },
178 { "_auth._tcp.", "Authentication Service", "", false },
179 { "_bootps._tcp.", "Bootstrap Protocol Server", "", false },
180 { "_bousg._tcp.", "Bag Of Unusual Strategy Games", "", false },
181 { "_browse._udp.", "DNS Service Discovery", "", false },
182 { "_cheat._tcp.", "The Cheat", "", false },
183 { "_chess._tcp", "Project Gridlock", "", false },
184 { "_chfts._tcp", "Fluid Theme Server", "", false },
185 { "_clipboard._tcp", "Clipboard Sharing", "", false },
186 { "_contactserver._tcp.", "Now Up-to-Date & Contact", "", false },
187 { "_cvspserver._tcp", "CVS PServer", "", false },
188 { "_cytv._tcp.", "CyTV Network streaming for Elgato EyeTV", "", false },
189 { "_daap._tcp.", "Digital Audio Access Protocol (iTunes)", "daap://", false },
190 { "_distcc._tcp", "Distributed Compiler", "", false },
191 { "_dns-sd._udp", "DNS Service Discovery", "", false },
192 { "_dpap._tcp.", "Digital Picture Access Protocol (iPhoto)", "", false },
193 { "_earphoria._tcp.", "Earphoria", "", false },
194 { "_ecbyesfsgksc._tcp.", "Net Monitor Anti-Piracy Service", "", false },
195 { "_eheap._tcp.", "Interactive Room Software", "", false },
196 { "_embrace._tcp.", "DataEnvoy", "", false },
197 { "_eppc._tcp.", "Remote AppleEvents", "eppc://", false },
198 { "_exec._tcp.", "Remote Process Execution", "", false },
199 { "_facespan._tcp.", "FaceSpan", "", false },
200 { "_fjork._tcp.", "Fjork", "", false },
201 { "_ftp._tcp.", "File Transfer (FTP)", "ftp://", false },
202 { "_ftpcroco._tcp.", "Crocodile FTP Server", "", false },
203 { "_gbs-smp._tcp.", "SnapMail", "", false },
204 { "_gbs-stp._tcp.", "SnapTalk", "", false },
205 { "_grillezvous._tcp.", "Roxio ToastAnywhere(tm) Recorder Sharing", "", false },
206 { "_h323._tcp.", "H.323", "", false },
207 { "_hotwayd._tcp", "Hotwayd", "", false },
208 { "_http._tcp.", "Web Server (HTTP)", "http://", true },
209 { "_hydra._tcp", "SubEthaEdit", "", false },
210 { "_ica-networking._tcp.", "Image Capture Networking", "", false },
211 { "_ichalkboard._tcp.", "iChalk", "", false },
212 { "_ichat._tcp.", "iChat", "ichat://", false },
213 { "_iconquer._tcp.", "iConquer", "", false },
214 { "_imap._tcp.", "Internet Message Access Protocol", "", false },
215 { "_imidi._tcp.", "iMidi", "", false },
216 { "_ipp._tcp.", "Printer (IPP)", "ipp://", false },
217 { "_ishare._tcp.", "iShare", "", false },
218 { "_isparx._tcp.", "iSparx", "", false },
219 { "_istorm._tcp", "iStorm", "", false },
220 { "_iwork._tcp.", "iWork Server", "", false },
221 { "_liaison._tcp.", "Liaison", "", false },
222 { "_login._tcp.", "Remote Login a la Telnet", "", false },
223 { "_lontalk._tcp.", "LonTalk over IP (ANSI 852)", "", false },
224 { "_lonworks._tcp.", "Echelon LNS Remote Client", "", false },
225 { "_macfoh-remote._tcp.", "MacFOH Remote", "", false },
226 { "_moneyworks._tcp.", "MoneyWorks", "", false },
227 { "_mp3sushi._tcp", "MP3 Sushi", "", false },
228 { "_mttp._tcp.", "MenuTunes Sharing", "", false },
229 { "_ncbroadcast._tcp.", "Network Clipboard Broadcasts", "", false },
230 { "_ncdirect._tcp.", "Network Clipboard Direct Transfers", "", false },
231 { "_ncsyncserver._tcp.", "Network Clipboard Sync Server", "", false },
232 { "_newton-dock._tcp.", "Escale", "", false },
233 { "_nfs._tcp", "NFS", "", false },
234 { "_nssocketport._tcp.", "DO over NSSocketPort", "", false },
235 { "_omni-bookmark._tcp.", "OmniWeb", "", false },
236 { "_openbase._tcp.", "OpenBase SQL", "", false },
237 { "_p2pchat._tcp.", "Peer-to-Peer Chat", "", false },
238 { "_pdl-datastream._tcp.", "Printer (PDL)", "pdl://", false },
239 { "_poch._tcp.", "Parallel OperatiOn and Control Heuristic", "", false },
240 { "_pop_2_ambrosia._tcp.", "Pop-Pop", "", false },
241 { "_pop3._tcp", "POP3 Server", "", false },
242 { "_postgresql._tcp", "PostgreSQL Server", "", false },
243 { "_presence._tcp", "iChat AV", "", false },
244 { "_printer._tcp.", "Printer (LPR)", "lpr://", false },
245 { "_ptp._tcp.", "Picture Transfer (PTP)", "ptp://", false },
246 { "_register._tcp", "DNS Service Discovery", "", false },
247 { "_rendezvouspong._tcp", "RendezvousPong", "", false },
248 { "_rfb._tcp.", "Remote Frame Buffer", "", false },
249 { "_riousbprint._tcp.", "Remote I/O USB Printer Protocol", "", false },
250 { "_rtsp._tcp.", "Real Time Stream Control Protocol", "", false },
251 { "_safarimenu._tcp", "Safari Menu", "", false },
252 { "_scone._tcp", "Scone", "", false },
253 { "_sdsharing._tcp.", "Speed Download", "", false },
254 { "_seeCard._tcp.", "seeCard", "", false },
255 { "_services._udp.", "DNS Service Discovery", "", false },
256 { "_shell._tcp.", "like exec, but automatic authentication", "", false },
257 { "_shout._tcp.", "Shout", "", false },
258 { "_shoutcast._tcp", "Nicecast", "", false },
259 { "_smb._tcp.", "Windows File Sharing (SMB)", "smb://", false },
260 { "_soap._tcp.", "Simple Object Access Protocol", "", false },
261 { "_spincrisis._tcp.", "Spin Crisis", "", false },
262 { "_spl-itunes._tcp.", "launchTunes", "", false },
263 { "_spr-itunes._tcp.", "netTunes", "", false },
264 { "_ssh._tcp.", "Secure Shell (SSH)", "ssh://", false },
265 { "_ssscreenshare._tcp", "Screen Sharing", "", false },
266 { "_sge-exec._tcp", "Sun Grid Engine (Execution Host)", "", false },
267 { "_sge-qmaster._tcp", "Sun Grid Engine (Master)", "", false },
268 { "_stickynotes._tcp", "Sticky Notes", "", false },
269 { "_strateges._tcp", "Strateges", "", false },
270 { "_sxqdea._tcp", "Synchronize! Pro X", "", false },
271 { "_sybase-tds._tcp", "Sybase Server", "", false },
272 { "_tce._tcp", "Power Card", "", false },
273 { "_teamlist._tcp", "ARTIS Team Task", "", false },
274 { "_teleport._tcp", "teleport", "", false },
275 { "_telnet._tcp.", "Telnet", "telnet://", false },
276 { "_tftp._tcp.", "Trivial File Transfer (TFTP)", "tftp://", false },
277 { "_tinavigator._tcp.", "TI Navigator", "", false },
278 { "_tivo_servemedia._tcp", "TiVo", "", false },
279 { "_upnp._tcp.", "Universal Plug and Play", "", false },
280 { "_utest._tcp.", "uTest", "", false },
281 { "_vue4rendercow._tcp", "VueProRenderCow", "", false },
282 { "_webdav._tcp.", "WebDAV", "webdav://", false },
283 { "_whamb._tcp.", "Whamb", "", false },
284 { "_workstation._tcp", "Macintosh Manager", "", false },
285 { "_ws._tcp", "Web Services", "", false },
286 { "_xserveraid._tcp.", "Xserve RAID", "xsr://", false },
287 { "_xsync._tcp.", "Xserve RAID Synchronization", "", false },
289 { "", "", "", false },
291 // Unofficial and invalid service types that will be phased out:
293 { "_clipboardsharing._tcp.", "ClipboardSharing", "", false },
294 { "_MacOSXDupSuppress._tcp.", "Mac OS X Duplicate Suppression", "", false },
295 { "_netmonitorserver._tcp.", "Net Monitor Server", "", false },
296 { "_networkclipboard._tcp.", "Network Clipboard", "", false },
297 { "_slimdevices_slimp3_cli._tcp.", "SliMP3 Server Command-Line Interface", "", false },
298 { "_slimdevices_slimp3_http._tcp.", "SliMP3 Server Web Interface", "", false },
299 { "_tieducationalhandhelddevice._tcp.", "TI Connect Manager", "", false },
300 { "_tivo_servemedia._tcp.", "TiVo", "", false },
302 { NULL
, NULL
, NULL
, false },
306 #pragma mark == Structures ==
309 //===========================================================================================================================
311 //===========================================================================================================================
313 struct DomainEventInfo
315 DNSBrowserEventType eventType
;
317 DNSNetworkAddress ifIP
;
320 struct ServiceEventInfo
322 DNSBrowserEventType eventType
;
326 DNSNetworkAddress ifIP
;
330 #pragma mark == Prototypes ==
333 //===========================================================================================================================
335 //===========================================================================================================================
341 DNSStatus inStatusCode
,
342 const DNSBrowserEvent
* inEvent
);
344 static char * DNSNetworkAddressToString( const DNSNetworkAddress
*inAddr
, char *outString
);
346 static DWORD
UTF8StringToStringObject( const char *inUTF8
, CString
&inObject
);
347 static DWORD
StringObjectToUTF8String( CString
&inObject
, std::string
&outUTF8
);
350 #pragma mark == Message Map ==
353 //===========================================================================================================================
355 //===========================================================================================================================
357 BEGIN_MESSAGE_MAP(ChooserDialog
, CDialog
)
358 //{{AFX_MSG_MAP(ChooserDialog)
360 ON_NOTIFY(LVN_ITEMCHANGED
, IDC_DOMAIN_LIST
, OnDomainListChanged
)
361 ON_NOTIFY(LVN_ITEMCHANGED
, IDC_SERVICE_LIST
, OnServiceListChanged
)
362 ON_NOTIFY(LVN_ITEMCHANGED
, IDC_CHOOSER_LIST
, OnChooserListChanged
)
363 ON_NOTIFY(NM_DBLCLK
, IDC_CHOOSER_LIST
, OnChooserListDoubleClick
)
364 ON_COMMAND(ID_HELP_ABOUT
, OnAbout
)
365 ON_WM_INITMENUPOPUP()
367 ON_COMMAND(ID_FILE_CLOSE
, OnFileClose
)
368 ON_COMMAND(ID_FILE_EXIT
, OnExit
)
372 ON_MESSAGE( WM_USER_DOMAIN_ADD
, OnDomainAdd
)
373 ON_MESSAGE( WM_USER_DOMAIN_REMOVE
, OnDomainRemove
)
374 ON_MESSAGE( WM_USER_SERVICE_ADD
, OnServiceAdd
)
375 ON_MESSAGE( WM_USER_SERVICE_REMOVE
, OnServiceRemove
)
376 ON_MESSAGE( WM_USER_RESOLVE
, OnResolve
)
380 #pragma mark == Routines ==
383 //===========================================================================================================================
385 //===========================================================================================================================
387 ChooserDialog::ChooserDialog( CWnd
*inParent
)
388 : CDialog( ChooserDialog::IDD
, inParent
)
390 //{{AFX_DATA_INIT(ChooserDialog)
391 // NOTE: the ClassWizard will add member initialization here
394 // Load menu accelerator table.
396 mMenuAcceleratorTable
= ::LoadAccelerators( AfxGetInstanceHandle(), MAKEINTRESOURCE( IDR_CHOOSER_DIALOG_MENU_ACCELERATORS
) );
397 assert( mMenuAcceleratorTable
);
400 mIsServiceBrowsing
= false;
403 //===========================================================================================================================
405 //===========================================================================================================================
407 ChooserDialog::~ChooserDialog( void )
413 err
= DNSBrowserRelease( mBrowser
, 0 );
414 assert( err
== kDNSNoErr
);
418 //===========================================================================================================================
420 //===========================================================================================================================
422 void ChooserDialog::DoDataExchange( CDataExchange
*pDX
)
424 CDialog::DoDataExchange(pDX
);
426 //{{AFX_DATA_MAP(ChooserDialog)
427 DDX_Control(pDX
, IDC_SERVICE_LIST
, mServiceList
);
428 DDX_Control(pDX
, IDC_DOMAIN_LIST
, mDomainList
);
429 DDX_Control(pDX
, IDC_CHOOSER_LIST
, mChooserList
);
433 //===========================================================================================================================
435 //===========================================================================================================================
437 BOOL
ChooserDialog::OnInitDialog( void )
444 // Initialize our parent.
446 CDialog::OnInitDialog();
448 // Set up the window icon.
450 icon
= AfxGetApp()->LoadIcon( IDR_MAIN_ICON
);
454 SetIcon( icon
, TRUE
); // Set big icon
455 SetIcon( icon
, FALSE
); // Set small icon
458 // Set up the Domain List.
460 result
= tempString
.LoadString( IDS_CHOOSER_DOMAIN_COLUMN_NAME
);
462 mDomainList
.InsertColumn( 0, tempString
, LVCFMT_LEFT
, kDomainListDefaultDomainColumnWidth
);
464 // Set up the Service List.
466 result
= tempString
.LoadString( IDS_CHOOSER_SERVICE_COLUMN_TYPE
);
468 mServiceList
.InsertColumn( 0, tempString
, LVCFMT_LEFT
, kServiceListDefaultServiceColumnTypeWidth
);
470 result
= tempString
.LoadString( IDS_CHOOSER_SERVICE_COLUMN_DESC
);
472 mServiceList
.InsertColumn( 1, tempString
, LVCFMT_LEFT
, kServiceListDefaultServiceColumnDescWidth
);
474 PopulateServicesList();
476 // Set up the Chooser List.
478 result
= tempString
.LoadString( IDS_CHOOSER_CHOOSER_NAME_COLUMN_NAME
);
480 mChooserList
.InsertColumn( 0, tempString
, LVCFMT_LEFT
, kChooserListDefaultNameColumnWidth
);
482 result
= tempString
.LoadString( IDS_CHOOSER_CHOOSER_IP_COLUMN_NAME
);
484 mChooserList
.InsertColumn( 1, tempString
, LVCFMT_LEFT
, kChooserListDefaultIPColumnWidth
);
486 // Set up the other controls.
490 // Start browsing for domains.
492 err
= DNSBrowserCreate( 0, BrowserCallBack
, this, &mBrowser
);
493 assert( err
== kDNSNoErr
);
495 err
= DNSBrowserStartDomainSearch( mBrowser
, 0 );
496 assert( err
== kDNSNoErr
);
501 //===========================================================================================================================
503 //===========================================================================================================================
505 void ChooserDialog::OnFileClose()
510 //===========================================================================================================================
512 //===========================================================================================================================
514 void ChooserDialog::OnActivate( UINT nState
, CWnd
* pWndOther
, BOOL bMinimized
)
516 // Always make the active window the "main" window so modal dialogs work better and the app quits after closing
519 gApp
.m_pMainWnd
= this;
521 CDialog::OnActivate(nState
, pWndOther
, bMinimized
);
524 //===========================================================================================================================
526 //===========================================================================================================================
528 void ChooserDialog::PostNcDestroy()
530 // Call the base class to do the normal cleanup.
535 //===========================================================================================================================
536 // PreTranslateMessage
537 //===========================================================================================================================
539 BOOL
ChooserDialog::PreTranslateMessage(MSG
* pMsg
)
544 assert( mMenuAcceleratorTable
);
545 if( mMenuAcceleratorTable
)
547 result
= ::TranslateAccelerator( m_hWnd
, mMenuAcceleratorTable
, pMsg
);
551 result
= CDialog::PreTranslateMessage( pMsg
);
556 //===========================================================================================================================
558 //===========================================================================================================================
560 void ChooserDialog::OnInitMenuPopup( CMenu
*pPopupMenu
, UINT nIndex
, BOOL bSysMenu
)
562 CDialog::OnInitMenuPopup( pPopupMenu
, nIndex
, bSysMenu
);
566 case kChooserMenuIndexFile
:
569 case kChooserMenuIndexHelp
:
577 //===========================================================================================================================
579 //===========================================================================================================================
581 void ChooserDialog::OnExit()
586 //===========================================================================================================================
588 //===========================================================================================================================
590 void ChooserDialog::OnAbout()
597 //===========================================================================================================================
599 //===========================================================================================================================
601 void ChooserDialog::OnSysCommand( UINT inID
, LPARAM inParam
)
603 CDialog::OnSysCommand( inID
, inParam
);
606 //===========================================================================================================================
608 //===========================================================================================================================
610 void ChooserDialog::OnClose()
614 gApp
.m_pMainWnd
= this;
618 //===========================================================================================================================
620 //===========================================================================================================================
622 void ChooserDialog::OnNcDestroy()
624 gApp
.m_pMainWnd
= this;
626 CDialog::OnNcDestroy();
629 //===========================================================================================================================
630 // OnDomainListChanged
631 //===========================================================================================================================
633 void ChooserDialog::OnDomainListChanged( NMHDR
*pNMHDR
, LRESULT
*pResult
)
635 UNUSED_ALWAYS( pNMHDR
);
637 // Domain list changes have similar effects to service list changes so reuse that code path by calling it here.
639 OnServiceListChanged( NULL
, NULL
);
644 //===========================================================================================================================
645 // OnServiceListChanged
646 //===========================================================================================================================
648 void ChooserDialog::OnServiceListChanged( NMHDR
*pNMHDR
, LRESULT
*pResult
)
653 UNUSED_ALWAYS( pNMHDR
);
655 // Stop any existing service search.
659 // If a domain and service type are selected, start searching for the service type on the domain.
661 selectedType
= mServiceList
.GetNextItem( -1, LVNI_SELECTED
);
662 selectedDomain
= mDomainList
.GetNextItem( -1, LVNI_SELECTED
);
664 if( ( selectedType
>= 0 ) && ( selectedDomain
>= 0 ) )
670 s
= mDomainList
.GetItemText( selectedDomain
, 0 );
671 StringObjectToUTF8String( s
, utf8
);
672 type
= mServiceTypes
[ selectedType
].serviceType
.c_str();
675 StartBrowsing( type
, utf8
.c_str() );
685 //===========================================================================================================================
686 // OnChooserListChanged
687 //===========================================================================================================================
689 void ChooserDialog::OnChooserListChanged( NMHDR
*pNMHDR
, LRESULT
*pResult
)
691 UNUSED_ALWAYS( pNMHDR
);
697 //===========================================================================================================================
698 // OnChooserListDoubleClick
699 //===========================================================================================================================
701 void ChooserDialog::OnChooserListDoubleClick( NMHDR
*pNMHDR
, LRESULT
*pResult
)
705 UNUSED_ALWAYS( pNMHDR
);
707 // Display the service instance if it is selected. Otherwise, clear all the info.
709 selectedItem
= mChooserList
.GetNextItem( -1, LVNI_SELECTED
);
710 if( selectedItem
>= 0 )
712 ServiceInstanceInfo
* p
;
714 const KnownServiceEntry
* service
;
716 assert( selectedItem
< (int) mServiceInstances
.size() );
717 p
= &mServiceInstances
[ selectedItem
];
719 // Search for a known service type entry that matches.
721 for( service
= kKnownServiceTable
; service
->serviceType
; ++service
)
723 if( p
->type
== service
->serviceType
)
728 if( service
->serviceType
)
732 // Create a URL representing the service instance.
734 if( strcmp( service
->serviceType
, "_smb._tcp." ) == 0 )
736 // Special case for SMB (no port number).
738 url
.Format( TEXT( "%s%s/" ), service
->urlScheme
, (const char *) p
->ip
.c_str() );
740 else if( strcmp( service
->serviceType
, "_ftp._tcp." ) == 0 )
742 // Special case for FTP to get login info.
748 if( !dialog
.GetLogin( username
, password
) )
753 // Build URL in the following format:
755 // ftp://[username[:password]@]<ip>
757 url
+= service
->urlScheme
;
758 if( username
.GetLength() > 0 )
761 if( password
.GetLength() > 0 )
768 url
+= p
->ip
.c_str();
770 else if( strcmp( service
->serviceType
, "_http._tcp." ) == 0 )
772 // Special case for HTTP to exclude "path=" if present.
774 text
= service
->useText
? p
->text
.c_str() : "";
775 if( strncmp( text
, "path=", 5 ) == 0 )
781 url
.Format( TEXT( "%s%s/%s" ), service
->urlScheme
, (const char *) p
->ip
.c_str(), text
);
785 url
.Format( TEXT( "%s%s%s" ), service
->urlScheme
, (const char *) p
->ip
.c_str(), text
);
790 text
= service
->useText
? p
->text
.c_str() : "";
791 url
.Format( TEXT( "%s%s/%s" ), service
->urlScheme
, (const char *) p
->ip
.c_str(), text
);
794 // Let the system open the URL in the correct app.
797 CWaitCursor waitCursor
;
799 ShellExecute( NULL
, TEXT( "open" ), url
, TEXT( "" ), TEXT( "c:\\" ), SW_SHOWNORMAL
);
808 //===========================================================================================================================
810 //===========================================================================================================================
812 void ChooserDialog::OnCancel()
817 //===========================================================================================================================
818 // PopulateServicesList
819 //===========================================================================================================================
821 void ChooserDialog::PopulateServicesList( void )
823 ServiceTypeVector::iterator i
;
828 // Add a fixed list of known services.
830 if( mServiceTypes
.empty() )
832 const KnownServiceEntry
* service
;
834 for( service
= kKnownServiceTable
; service
->serviceType
; ++service
)
836 ServiceTypeInfo info
;
838 info
.serviceType
= service
->serviceType
;
839 info
.description
= service
->description
;
840 info
.urlScheme
= service
->urlScheme
;
841 mServiceTypes
.push_back( info
);
845 // Add each service to the list.
847 for( i
= mServiceTypes
.begin(); i
!= mServiceTypes
.end(); ++i
)
852 p
= ( *i
).serviceType
.c_str();
853 if( *p
== '_' ) ++p
; // Skip leading '_'.
854 q
= strchr( p
, '.' ); // Find first '.'.
855 if( q
) tmp
.assign( p
, (size_t)( q
- p
) ); // Use only up to the first '.'.
856 else tmp
.assign( p
); // No '.' so use the entire string.
857 UTF8StringToStringObject( tmp
.c_str(), type
);
858 UTF8StringToStringObject( ( *i
).description
.c_str(), desc
);
862 n
= mServiceList
.GetItemCount();
863 mServiceList
.InsertItem( n
, type
);
864 mServiceList
.SetItemText( n
, 1, desc
);
867 // Select the first service type by default.
869 if( !mServiceTypes
.empty() )
871 mServiceList
.SetItemState( 0, LVIS_SELECTED
| LVIS_FOCUSED
, LVIS_SELECTED
| LVIS_FOCUSED
);
875 //===========================================================================================================================
877 //===========================================================================================================================
879 void ChooserDialog::UpdateInfoDisplay( void )
887 std::string textNewLines
;
888 std::string hostName
;
890 std::string::iterator i
;
892 // Display the service instance if it is selected. Otherwise, clear all the info.
894 selectedItem
= mChooserList
.GetNextItem( -1, LVNI_SELECTED
);
895 if( selectedItem
>= 0 )
897 ServiceInstanceInfo
* p
;
899 assert( selectedItem
< (int) mServiceInstances
.size() );
900 p
= &mServiceInstances
[ selectedItem
];
906 hostName
= p
->hostName
;
908 // Sync up the list items with the actual data (IP address may change).
910 UTF8StringToStringObject( ip
.c_str(), s
);
911 mChooserList
.SetItemText( selectedItem
, 1, s
);
916 item
= (CWnd
*) this->GetDlgItem( IDC_INFO_NAME_TEXT
);
918 UTF8StringToStringObject( name
.c_str(), s
);
919 item
->SetWindowText( s
);
923 item
= (CWnd
*) this->GetDlgItem( IDC_INFO_IP_TEXT
);
925 UTF8StringToStringObject( ip
.c_str(), s
);
926 item
->SetWindowText( s
);
930 item
= (CWnd
*) this->GetDlgItem( IDC_INFO_INTERFACE_TEXT
);
932 UTF8StringToStringObject( ifIP
.c_str(), s
);
933 item
->SetWindowText( s
);
936 item
= (CWnd
*) this->GetDlgItem( IDC_INFO_HOST_NAME_TEXT
);
938 UTF8StringToStringObject( hostName
.c_str(), s
);
939 item
->SetWindowText( s
);
943 item
= (CWnd
*) this->GetDlgItem( IDC_INFO_TEXT_TEXT
);
945 for( i
= text
.begin(); i
!= text
.end(); ++i
)
949 textNewLines
+= "\r\n";
956 UTF8StringToStringObject( textNewLines
.c_str(), s
);
957 item
->SetWindowText( s
);
964 //===========================================================================================================================
966 //===========================================================================================================================
968 LONG
ChooserDialog::OnDomainAdd( WPARAM inWParam
, LPARAM inLParam
)
971 std::auto_ptr
< DomainEventInfo
> pAutoPtr
;
978 UNUSED_ALWAYS( inWParam
);
981 p
= reinterpret_cast <DomainEventInfo
*> ( inLParam
);
984 // Search to see if we already know about this domain. If not, add it to the list.
988 n
= mDomainList
.GetItemCount();
989 for( i
= 0; i
< n
; ++i
)
991 s
= mDomainList
.GetItemText( i
, 0 );
1002 mDomainList
.InsertItem( n
, domain
);
1004 // If no domains are selected and the domain being added is a default domain, select it.
1006 selectedItem
= mDomainList
.GetNextItem( -1, LVNI_SELECTED
);
1007 if( ( selectedItem
< 0 ) && ( p
->eventType
== kDNSBrowserEventTypeAddDefaultDomain
) )
1009 mDomainList
.SetItemState( n
, LVIS_SELECTED
| LVIS_FOCUSED
, LVIS_SELECTED
| LVIS_FOCUSED
);
1015 //===========================================================================================================================
1017 //===========================================================================================================================
1019 LONG
ChooserDialog::OnDomainRemove( WPARAM inWParam
, LPARAM inLParam
)
1021 DomainEventInfo
* p
;
1022 std::auto_ptr
< DomainEventInfo
> pAutoPtr
;
1029 UNUSED_ALWAYS( inWParam
);
1032 p
= reinterpret_cast <DomainEventInfo
*> ( inLParam
);
1033 pAutoPtr
.reset( p
);
1035 // Search to see if we know about this domain. If so, remove it from the list.
1039 n
= mDomainList
.GetItemCount();
1040 for( i
= 0; i
< n
; ++i
)
1042 s
= mDomainList
.GetItemText( i
, 0 );
1051 mDomainList
.DeleteItem( i
);
1056 //===========================================================================================================================
1058 //===========================================================================================================================
1060 LONG
ChooserDialog::OnServiceAdd( WPARAM inWParam
, LPARAM inLParam
)
1062 ServiceEventInfo
* p
;
1063 std::auto_ptr
< ServiceEventInfo
> pAutoPtr
;
1065 UNUSED_ALWAYS( inWParam
);
1068 p
= reinterpret_cast <ServiceEventInfo
*> ( inLParam
);
1069 pAutoPtr
.reset( p
);
1074 //===========================================================================================================================
1076 //===========================================================================================================================
1078 LONG
ChooserDialog::OnServiceRemove( WPARAM inWParam
, LPARAM inLParam
)
1080 ServiceEventInfo
* p
;
1081 std::auto_ptr
< ServiceEventInfo
> pAutoPtr
;
1086 UNUSED_ALWAYS( inWParam
);
1089 p
= reinterpret_cast <ServiceEventInfo
*> ( inLParam
);
1090 pAutoPtr
.reset( p
);
1092 // Search to see if we know about this service instance. If so, remove it from the list.
1095 n
= (int) mServiceInstances
.size();
1096 for( i
= 0; i
< n
; ++i
)
1098 ServiceInstanceInfo
* q
;
1100 // If the name, type, domain, and interface match, treat it as the same service instance.
1102 q
= &mServiceInstances
[ i
];
1103 if( ( p
->name
== q
->name
) &&
1104 ( p
->type
== q
->type
) &&
1105 ( p
->domain
== q
->domain
) )
1113 mChooserList
.DeleteItem( i
);
1114 assert( i
< (int) mServiceInstances
.size() );
1115 mServiceInstances
.erase( mServiceInstances
.begin() + i
);
1120 //===========================================================================================================================
1122 //===========================================================================================================================
1124 LONG
ChooserDialog::OnResolve( WPARAM inWParam
, LPARAM inLParam
)
1126 ServiceInstanceInfo
* p
;
1127 std::auto_ptr
< ServiceInstanceInfo
> pAutoPtr
;
1133 UNUSED_ALWAYS( inWParam
);
1136 p
= reinterpret_cast <ServiceInstanceInfo
*> ( inLParam
);
1137 pAutoPtr
.reset( p
);
1139 // Make sure it is for an item of the correct type. This handles any resolves that may have been queued up.
1141 selectedType
= mServiceList
.GetNextItem( -1, LVNI_SELECTED
);
1142 assert( selectedType
>= 0 );
1143 if( selectedType
>= 0 )
1145 assert( selectedType
<= (int) mServiceTypes
.size() );
1146 if( p
->type
!= mServiceTypes
[ selectedType
].serviceType
)
1152 // Search to see if we know about this service instance. If so, update its info. Otherwise, add it to the list.
1155 n
= (int) mServiceInstances
.size();
1156 for( i
= 0; i
< n
; ++i
)
1158 ServiceInstanceInfo
* q
;
1160 // If the name, type, domain, and interface matches, treat it as the same service instance.
1162 q
= &mServiceInstances
[ i
];
1163 if( ( p
->name
== q
->name
) &&
1164 ( p
->type
== q
->type
) &&
1165 ( p
->domain
== q
->domain
) &&
1166 ( p
->ifIP
== q
->ifIP
) )
1174 mServiceInstances
[ i
] = *p
;
1180 mServiceInstances
.push_back( *p
);
1181 UTF8StringToStringObject( p
->name
.c_str(), s
);
1182 mChooserList
.InsertItem( n
, s
);
1184 UTF8StringToStringObject( p
->ip
.c_str(), s
);
1185 mChooserList
.SetItemText( n
, 1, s
);
1187 // If this is the only item, select it.
1191 mChooserList
.SetItemState( n
, LVIS_SELECTED
| LVIS_FOCUSED
, LVIS_SELECTED
| LVIS_FOCUSED
);
1194 UpdateInfoDisplay();
1200 //===========================================================================================================================
1202 //===========================================================================================================================
1204 void ChooserDialog::StartBrowsing( const char *inType
, const char *inDomain
)
1208 assert( mServiceInstances
.empty() );
1209 assert( mChooserList
.GetItemCount() == 0 );
1210 assert( !mIsServiceBrowsing
);
1212 mChooserList
.DeleteAllItems();
1213 mServiceInstances
.clear();
1215 mIsServiceBrowsing
= true;
1216 err
= DNSBrowserStartServiceSearch( mBrowser
, kDNSBrowserFlagAutoResolve
, inType
, inDomain
);
1217 assert( err
== kDNSNoErr
);
1220 //===========================================================================================================================
1222 //===========================================================================================================================
1224 void ChooserDialog::StopBrowsing( void )
1226 // If searching, stop.
1228 if( mIsServiceBrowsing
)
1232 mIsServiceBrowsing
= false;
1233 err
= DNSBrowserStopServiceSearch( mBrowser
, 0 );
1234 assert( err
== kDNSNoErr
);
1237 // Remove all service instances.
1239 mChooserList
.DeleteAllItems();
1240 assert( mChooserList
.GetItemCount() == 0 );
1241 mServiceInstances
.clear();
1242 assert( mServiceInstances
.empty() );
1243 UpdateInfoDisplay();
1250 //===========================================================================================================================
1252 //===========================================================================================================================
1257 DNSBrowserRef inRef
,
1258 DNSStatus inStatusCode
,
1259 const DNSBrowserEvent
* inEvent
)
1261 ChooserDialog
* dialog
;
1265 UNUSED_ALWAYS( inStatusCode
);
1266 UNUSED_ALWAYS( inRef
);
1268 // Check parameters.
1270 assert( inContext
);
1271 dialog
= reinterpret_cast <ChooserDialog
*> ( inContext
);
1275 switch( inEvent
->type
)
1277 case kDNSBrowserEventTypeRelease
:
1282 case kDNSBrowserEventTypeAddDomain
:
1283 case kDNSBrowserEventTypeAddDefaultDomain
:
1284 case kDNSBrowserEventTypeRemoveDomain
:
1286 DomainEventInfo
* domain
;
1287 std::auto_ptr
< DomainEventInfo
> domainAutoPtr
;
1289 domain
= new DomainEventInfo
;
1290 domainAutoPtr
.reset( domain
);
1292 domain
->eventType
= inEvent
->type
;
1293 domain
->domain
= inEvent
->data
.addDomain
.domain
;
1294 domain
->ifIP
= inEvent
->data
.addDomain
.interfaceIP
;
1296 message
= ( inEvent
->type
== kDNSBrowserEventTypeRemoveDomain
) ? WM_USER_DOMAIN_REMOVE
: WM_USER_DOMAIN_ADD
;
1297 posted
= ::PostMessage( dialog
->GetSafeHwnd(), message
, 0, (LPARAM
) domain
);
1301 domainAutoPtr
.release();
1308 case kDNSBrowserEventTypeAddService
:
1309 case kDNSBrowserEventTypeRemoveService
:
1311 ServiceEventInfo
* service
;
1312 std::auto_ptr
< ServiceEventInfo
> serviceAutoPtr
;
1314 service
= new ServiceEventInfo
;
1315 serviceAutoPtr
.reset( service
);
1317 service
->eventType
= inEvent
->type
;
1318 service
->name
= inEvent
->data
.addService
.name
;
1319 service
->type
= inEvent
->data
.addService
.type
;
1320 service
->domain
= inEvent
->data
.addService
.domain
;
1321 service
->ifIP
= inEvent
->data
.addService
.interfaceIP
;
1323 message
= ( inEvent
->type
== kDNSBrowserEventTypeAddService
) ? WM_USER_SERVICE_ADD
: WM_USER_SERVICE_REMOVE
;
1324 posted
= ::PostMessage( dialog
->GetSafeHwnd(), message
, 0, (LPARAM
) service
);
1328 serviceAutoPtr
.release();
1335 case kDNSBrowserEventTypeResolved
:
1336 if( inEvent
->data
.resolved
->address
.addressType
== kDNSNetworkAddressTypeIPv4
)
1338 ServiceInstanceInfo
* serviceInstance
;
1339 std::auto_ptr
< ServiceInstanceInfo
> serviceInstanceAutoPtr
;
1342 serviceInstance
= new ServiceInstanceInfo
;
1343 serviceInstanceAutoPtr
.reset( serviceInstance
);
1345 serviceInstance
->name
= inEvent
->data
.resolved
->name
;
1346 serviceInstance
->type
= inEvent
->data
.resolved
->type
;
1347 serviceInstance
->domain
= inEvent
->data
.resolved
->domain
;
1348 serviceInstance
->ip
= DNSNetworkAddressToString( &inEvent
->data
.resolved
->address
, s
);
1349 serviceInstance
->ifIP
= DNSNetworkAddressToString( &inEvent
->data
.resolved
->interfaceIP
, s
);
1350 serviceInstance
->text
= inEvent
->data
.resolved
->textRecord
;
1351 serviceInstance
->hostName
= inEvent
->data
.resolved
->hostName
;
1353 posted
= ::PostMessage( dialog
->GetSafeHwnd(), WM_USER_RESOLVE
, 0, (LPARAM
) serviceInstance
);
1357 serviceInstanceAutoPtr
.release();
1368 // Don't let exceptions escape.
1372 //===========================================================================================================================
1373 // DNSNetworkAddressToString
1375 // Note: Currently only supports IPv4 network addresses.
1376 //===========================================================================================================================
1378 static char * DNSNetworkAddressToString( const DNSNetworkAddress
*inAddr
, char *outString
)
1383 p
= inAddr
->u
.ipv4
.addr
.v8
;
1384 port
= ntohs( inAddr
->u
.ipv4
.port
.v16
);
1385 if( port
!= kDNSPortInvalid
)
1387 sprintf( outString
, "%u.%u.%u.%u:%u", p
[ 0 ], p
[ 1 ], p
[ 2 ], p
[ 3 ], port
);
1391 sprintf( outString
, "%u.%u.%u.%u", p
[ 0 ], p
[ 1 ], p
[ 2 ], p
[ 3 ] );
1393 return( outString
);
1396 //===========================================================================================================================
1397 // UTF8StringToStringObject
1398 //===========================================================================================================================
1400 static DWORD
UTF8StringToStringObject( const char *inUTF8
, CString
&inObject
)
1408 n
= MultiByteToWideChar( CP_UTF8
, 0, inUTF8
, -1, NULL
, 0 );
1411 unicode
= (BSTR
) malloc( (size_t)( n
* sizeof( wchar_t ) ) );
1414 err
= ERROR_INSUFFICIENT_BUFFER
;
1418 n
= MultiByteToWideChar( CP_UTF8
, 0, inUTF8
, -1, unicode
, n
);
1425 err
= ERROR_NO_UNICODE_TRANSLATION
;
1443 //===========================================================================================================================
1444 // StringObjectToUTF8String
1445 //===========================================================================================================================
1447 static DWORD
StringObjectToUTF8String( CString
&inObject
, std::string
&outUTF8
)
1458 nUnicode
= inObject
.GetLength();
1461 unicode
= inObject
.AllocSysString();
1462 n
= WideCharToMultiByte( CP_UTF8
, 0, unicode
, nUnicode
, NULL
, 0, NULL
, NULL
);
1465 utf8
= (char *) malloc( (size_t) n
);
1467 if( !utf8
) { err
= ERROR_INSUFFICIENT_BUFFER
; goto exit
; }
1469 n
= WideCharToMultiByte( CP_UTF8
, 0, unicode
, nUnicode
, utf8
, n
, NULL
, NULL
);
1474 outUTF8
.assign( utf8
, n
);
1478 err
= ERROR_NO_UNICODE_TRANSLATION
;
1491 SysFreeString( unicode
);