1 /* -*- Mode: C; tab-width: 4 -*-
3 * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
17 Change History (most recent first):
19 $Log: ChooserDialog.cpp,v $
20 Revision 1.4 2006/08/14 23:25:49 cheshire
21 Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
23 Revision 1.3 2005/02/10 22:35:35 cheshire
24 <rdar://problem/3727944> Update name
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 { "_rfb._tcp.", "Remote Frame Buffer", "", false },
248 { "_riousbprint._tcp.", "Remote I/O USB Printer Protocol", "", false },
249 { "_rtsp._tcp.", "Real Time Stream Control Protocol", "", false },
250 { "_safarimenu._tcp", "Safari Menu", "", false },
251 { "_scone._tcp", "Scone", "", false },
252 { "_sdsharing._tcp.", "Speed Download", "", false },
253 { "_seeCard._tcp.", "seeCard", "", false },
254 { "_services._udp.", "DNS Service Discovery", "", false },
255 { "_shell._tcp.", "like exec, but automatic authentication", "", false },
256 { "_shout._tcp.", "Shout", "", false },
257 { "_shoutcast._tcp", "Nicecast", "", false },
258 { "_smb._tcp.", "Windows File Sharing (SMB)", "smb://", false },
259 { "_soap._tcp.", "Simple Object Access Protocol", "", false },
260 { "_spincrisis._tcp.", "Spin Crisis", "", false },
261 { "_spl-itunes._tcp.", "launchTunes", "", false },
262 { "_spr-itunes._tcp.", "netTunes", "", false },
263 { "_ssh._tcp.", "Secure Shell (SSH)", "ssh://", false },
264 { "_ssscreenshare._tcp", "Screen Sharing", "", false },
265 { "_sge-exec._tcp", "Sun Grid Engine (Execution Host)", "", false },
266 { "_sge-qmaster._tcp", "Sun Grid Engine (Master)", "", false },
267 { "_stickynotes._tcp", "Sticky Notes", "", false },
268 { "_strateges._tcp", "Strateges", "", false },
269 { "_sxqdea._tcp", "Synchronize! Pro X", "", false },
270 { "_sybase-tds._tcp", "Sybase Server", "", false },
271 { "_tce._tcp", "Power Card", "", false },
272 { "_teamlist._tcp", "ARTIS Team Task", "", false },
273 { "_teleport._tcp", "teleport", "", false },
274 { "_telnet._tcp.", "Telnet", "telnet://", false },
275 { "_tftp._tcp.", "Trivial File Transfer (TFTP)", "tftp://", false },
276 { "_tinavigator._tcp.", "TI Navigator", "", false },
277 { "_tivo_servemedia._tcp", "TiVo", "", false },
278 { "_upnp._tcp.", "Universal Plug and Play", "", false },
279 { "_utest._tcp.", "uTest", "", false },
280 { "_vue4rendercow._tcp", "VueProRenderCow", "", false },
281 { "_webdav._tcp.", "WebDAV", "webdav://", false },
282 { "_whamb._tcp.", "Whamb", "", false },
283 { "_workstation._tcp", "Macintosh Manager", "", false },
284 { "_ws._tcp", "Web Services", "", false },
285 { "_xserveraid._tcp.", "Xserve RAID", "xsr://", false },
286 { "_xsync._tcp.", "Xserve RAID Synchronization", "", false },
288 { "", "", "", false },
290 // Unofficial and invalid service types that will be phased out:
292 { "_clipboardsharing._tcp.", "ClipboardSharing", "", false },
293 { "_MacOSXDupSuppress._tcp.", "Mac OS X Duplicate Suppression", "", false },
294 { "_netmonitorserver._tcp.", "Net Monitor Server", "", false },
295 { "_networkclipboard._tcp.", "Network Clipboard", "", false },
296 { "_slimdevices_slimp3_cli._tcp.", "SliMP3 Server Command-Line Interface", "", false },
297 { "_slimdevices_slimp3_http._tcp.", "SliMP3 Server Web Interface", "", false },
298 { "_tieducationalhandhelddevice._tcp.", "TI Connect Manager", "", false },
299 { "_tivo_servemedia._tcp.", "TiVo", "", false },
301 { NULL
, NULL
, NULL
, false },
305 #pragma mark == Structures ==
308 //===========================================================================================================================
310 //===========================================================================================================================
312 struct DomainEventInfo
314 DNSBrowserEventType eventType
;
316 DNSNetworkAddress ifIP
;
319 struct ServiceEventInfo
321 DNSBrowserEventType eventType
;
325 DNSNetworkAddress ifIP
;
329 #pragma mark == Prototypes ==
332 //===========================================================================================================================
334 //===========================================================================================================================
340 DNSStatus inStatusCode
,
341 const DNSBrowserEvent
* inEvent
);
343 static char * DNSNetworkAddressToString( const DNSNetworkAddress
*inAddr
, char *outString
);
345 static DWORD
UTF8StringToStringObject( const char *inUTF8
, CString
&inObject
);
346 static DWORD
StringObjectToUTF8String( CString
&inObject
, std::string
&outUTF8
);
349 #pragma mark == Message Map ==
352 //===========================================================================================================================
354 //===========================================================================================================================
356 BEGIN_MESSAGE_MAP(ChooserDialog
, CDialog
)
357 //{{AFX_MSG_MAP(ChooserDialog)
359 ON_NOTIFY(LVN_ITEMCHANGED
, IDC_DOMAIN_LIST
, OnDomainListChanged
)
360 ON_NOTIFY(LVN_ITEMCHANGED
, IDC_SERVICE_LIST
, OnServiceListChanged
)
361 ON_NOTIFY(LVN_ITEMCHANGED
, IDC_CHOOSER_LIST
, OnChooserListChanged
)
362 ON_NOTIFY(NM_DBLCLK
, IDC_CHOOSER_LIST
, OnChooserListDoubleClick
)
363 ON_COMMAND(ID_HELP_ABOUT
, OnAbout
)
364 ON_WM_INITMENUPOPUP()
366 ON_COMMAND(ID_FILE_CLOSE
, OnFileClose
)
367 ON_COMMAND(ID_FILE_EXIT
, OnExit
)
371 ON_MESSAGE( WM_USER_DOMAIN_ADD
, OnDomainAdd
)
372 ON_MESSAGE( WM_USER_DOMAIN_REMOVE
, OnDomainRemove
)
373 ON_MESSAGE( WM_USER_SERVICE_ADD
, OnServiceAdd
)
374 ON_MESSAGE( WM_USER_SERVICE_REMOVE
, OnServiceRemove
)
375 ON_MESSAGE( WM_USER_RESOLVE
, OnResolve
)
379 #pragma mark == Routines ==
382 //===========================================================================================================================
384 //===========================================================================================================================
386 ChooserDialog::ChooserDialog( CWnd
*inParent
)
387 : CDialog( ChooserDialog::IDD
, inParent
)
389 //{{AFX_DATA_INIT(ChooserDialog)
390 // NOTE: the ClassWizard will add member initialization here
393 // Load menu accelerator table.
395 mMenuAcceleratorTable
= ::LoadAccelerators( AfxGetInstanceHandle(), MAKEINTRESOURCE( IDR_CHOOSER_DIALOG_MENU_ACCELERATORS
) );
396 assert( mMenuAcceleratorTable
);
399 mIsServiceBrowsing
= false;
402 //===========================================================================================================================
404 //===========================================================================================================================
406 ChooserDialog::~ChooserDialog( void )
412 err
= DNSBrowserRelease( mBrowser
, 0 );
413 assert( err
== kDNSNoErr
);
417 //===========================================================================================================================
419 //===========================================================================================================================
421 void ChooserDialog::DoDataExchange( CDataExchange
*pDX
)
423 CDialog::DoDataExchange(pDX
);
425 //{{AFX_DATA_MAP(ChooserDialog)
426 DDX_Control(pDX
, IDC_SERVICE_LIST
, mServiceList
);
427 DDX_Control(pDX
, IDC_DOMAIN_LIST
, mDomainList
);
428 DDX_Control(pDX
, IDC_CHOOSER_LIST
, mChooserList
);
432 //===========================================================================================================================
434 //===========================================================================================================================
436 BOOL
ChooserDialog::OnInitDialog( void )
443 // Initialize our parent.
445 CDialog::OnInitDialog();
447 // Set up the window icon.
449 icon
= AfxGetApp()->LoadIcon( IDR_MAIN_ICON
);
453 SetIcon( icon
, TRUE
); // Set big icon
454 SetIcon( icon
, FALSE
); // Set small icon
457 // Set up the Domain List.
459 result
= tempString
.LoadString( IDS_CHOOSER_DOMAIN_COLUMN_NAME
);
461 mDomainList
.InsertColumn( 0, tempString
, LVCFMT_LEFT
, kDomainListDefaultDomainColumnWidth
);
463 // Set up the Service List.
465 result
= tempString
.LoadString( IDS_CHOOSER_SERVICE_COLUMN_TYPE
);
467 mServiceList
.InsertColumn( 0, tempString
, LVCFMT_LEFT
, kServiceListDefaultServiceColumnTypeWidth
);
469 result
= tempString
.LoadString( IDS_CHOOSER_SERVICE_COLUMN_DESC
);
471 mServiceList
.InsertColumn( 1, tempString
, LVCFMT_LEFT
, kServiceListDefaultServiceColumnDescWidth
);
473 PopulateServicesList();
475 // Set up the Chooser List.
477 result
= tempString
.LoadString( IDS_CHOOSER_CHOOSER_NAME_COLUMN_NAME
);
479 mChooserList
.InsertColumn( 0, tempString
, LVCFMT_LEFT
, kChooserListDefaultNameColumnWidth
);
481 result
= tempString
.LoadString( IDS_CHOOSER_CHOOSER_IP_COLUMN_NAME
);
483 mChooserList
.InsertColumn( 1, tempString
, LVCFMT_LEFT
, kChooserListDefaultIPColumnWidth
);
485 // Set up the other controls.
489 // Start browsing for domains.
491 err
= DNSBrowserCreate( 0, BrowserCallBack
, this, &mBrowser
);
492 assert( err
== kDNSNoErr
);
494 err
= DNSBrowserStartDomainSearch( mBrowser
, 0 );
495 assert( err
== kDNSNoErr
);
500 //===========================================================================================================================
502 //===========================================================================================================================
504 void ChooserDialog::OnFileClose()
509 //===========================================================================================================================
511 //===========================================================================================================================
513 void ChooserDialog::OnActivate( UINT nState
, CWnd
* pWndOther
, BOOL bMinimized
)
515 // Always make the active window the "main" window so modal dialogs work better and the app quits after closing
518 gApp
.m_pMainWnd
= this;
520 CDialog::OnActivate(nState
, pWndOther
, bMinimized
);
523 //===========================================================================================================================
525 //===========================================================================================================================
527 void ChooserDialog::PostNcDestroy()
529 // Call the base class to do the normal cleanup.
534 //===========================================================================================================================
535 // PreTranslateMessage
536 //===========================================================================================================================
538 BOOL
ChooserDialog::PreTranslateMessage(MSG
* pMsg
)
543 assert( mMenuAcceleratorTable
);
544 if( mMenuAcceleratorTable
)
546 result
= ::TranslateAccelerator( m_hWnd
, mMenuAcceleratorTable
, pMsg
);
550 result
= CDialog::PreTranslateMessage( pMsg
);
555 //===========================================================================================================================
557 //===========================================================================================================================
559 void ChooserDialog::OnInitMenuPopup( CMenu
*pPopupMenu
, UINT nIndex
, BOOL bSysMenu
)
561 CDialog::OnInitMenuPopup( pPopupMenu
, nIndex
, bSysMenu
);
565 case kChooserMenuIndexFile
:
568 case kChooserMenuIndexHelp
:
576 //===========================================================================================================================
578 //===========================================================================================================================
580 void ChooserDialog::OnExit()
585 //===========================================================================================================================
587 //===========================================================================================================================
589 void ChooserDialog::OnAbout()
596 //===========================================================================================================================
598 //===========================================================================================================================
600 void ChooserDialog::OnSysCommand( UINT inID
, LPARAM inParam
)
602 CDialog::OnSysCommand( inID
, inParam
);
605 //===========================================================================================================================
607 //===========================================================================================================================
609 void ChooserDialog::OnClose()
613 gApp
.m_pMainWnd
= this;
617 //===========================================================================================================================
619 //===========================================================================================================================
621 void ChooserDialog::OnNcDestroy()
623 gApp
.m_pMainWnd
= this;
625 CDialog::OnNcDestroy();
628 //===========================================================================================================================
629 // OnDomainListChanged
630 //===========================================================================================================================
632 void ChooserDialog::OnDomainListChanged( NMHDR
*pNMHDR
, LRESULT
*pResult
)
634 UNUSED_ALWAYS( pNMHDR
);
636 // Domain list changes have similar effects to service list changes so reuse that code path by calling it here.
638 OnServiceListChanged( NULL
, NULL
);
643 //===========================================================================================================================
644 // OnServiceListChanged
645 //===========================================================================================================================
647 void ChooserDialog::OnServiceListChanged( NMHDR
*pNMHDR
, LRESULT
*pResult
)
652 UNUSED_ALWAYS( pNMHDR
);
654 // Stop any existing service search.
658 // If a domain and service type are selected, start searching for the service type on the domain.
660 selectedType
= mServiceList
.GetNextItem( -1, LVNI_SELECTED
);
661 selectedDomain
= mDomainList
.GetNextItem( -1, LVNI_SELECTED
);
663 if( ( selectedType
>= 0 ) && ( selectedDomain
>= 0 ) )
669 s
= mDomainList
.GetItemText( selectedDomain
, 0 );
670 StringObjectToUTF8String( s
, utf8
);
671 type
= mServiceTypes
[ selectedType
].serviceType
.c_str();
674 StartBrowsing( type
, utf8
.c_str() );
684 //===========================================================================================================================
685 // OnChooserListChanged
686 //===========================================================================================================================
688 void ChooserDialog::OnChooserListChanged( NMHDR
*pNMHDR
, LRESULT
*pResult
)
690 UNUSED_ALWAYS( pNMHDR
);
696 //===========================================================================================================================
697 // OnChooserListDoubleClick
698 //===========================================================================================================================
700 void ChooserDialog::OnChooserListDoubleClick( NMHDR
*pNMHDR
, LRESULT
*pResult
)
704 UNUSED_ALWAYS( pNMHDR
);
706 // Display the service instance if it is selected. Otherwise, clear all the info.
708 selectedItem
= mChooserList
.GetNextItem( -1, LVNI_SELECTED
);
709 if( selectedItem
>= 0 )
711 ServiceInstanceInfo
* p
;
713 const KnownServiceEntry
* service
;
715 assert( selectedItem
< (int) mServiceInstances
.size() );
716 p
= &mServiceInstances
[ selectedItem
];
718 // Search for a known service type entry that matches.
720 for( service
= kKnownServiceTable
; service
->serviceType
; ++service
)
722 if( p
->type
== service
->serviceType
)
727 if( service
->serviceType
)
731 // Create a URL representing the service instance.
733 if( strcmp( service
->serviceType
, "_smb._tcp." ) == 0 )
735 // Special case for SMB (no port number).
737 url
.Format( TEXT( "%s%s/" ), service
->urlScheme
, (const char *) p
->ip
.c_str() );
739 else if( strcmp( service
->serviceType
, "_ftp._tcp." ) == 0 )
741 // Special case for FTP to get login info.
747 if( !dialog
.GetLogin( username
, password
) )
752 // Build URL in the following format:
754 // ftp://[username[:password]@]<ip>
756 url
+= service
->urlScheme
;
757 if( username
.GetLength() > 0 )
760 if( password
.GetLength() > 0 )
767 url
+= p
->ip
.c_str();
769 else if( strcmp( service
->serviceType
, "_http._tcp." ) == 0 )
771 // Special case for HTTP to exclude "path=" if present.
773 text
= service
->useText
? p
->text
.c_str() : "";
774 if( strncmp( text
, "path=", 5 ) == 0 )
780 url
.Format( TEXT( "%s%s/%s" ), service
->urlScheme
, (const char *) p
->ip
.c_str(), text
);
784 url
.Format( TEXT( "%s%s%s" ), service
->urlScheme
, (const char *) p
->ip
.c_str(), text
);
789 text
= service
->useText
? p
->text
.c_str() : "";
790 url
.Format( TEXT( "%s%s/%s" ), service
->urlScheme
, (const char *) p
->ip
.c_str(), text
);
793 // Let the system open the URL in the correct app.
796 CWaitCursor waitCursor
;
798 ShellExecute( NULL
, TEXT( "open" ), url
, TEXT( "" ), TEXT( "c:\\" ), SW_SHOWNORMAL
);
807 //===========================================================================================================================
809 //===========================================================================================================================
811 void ChooserDialog::OnCancel()
816 //===========================================================================================================================
817 // PopulateServicesList
818 //===========================================================================================================================
820 void ChooserDialog::PopulateServicesList( void )
822 ServiceTypeVector::iterator i
;
827 // Add a fixed list of known services.
829 if( mServiceTypes
.empty() )
831 const KnownServiceEntry
* service
;
833 for( service
= kKnownServiceTable
; service
->serviceType
; ++service
)
835 ServiceTypeInfo info
;
837 info
.serviceType
= service
->serviceType
;
838 info
.description
= service
->description
;
839 info
.urlScheme
= service
->urlScheme
;
840 mServiceTypes
.push_back( info
);
844 // Add each service to the list.
846 for( i
= mServiceTypes
.begin(); i
!= mServiceTypes
.end(); ++i
)
851 p
= ( *i
).serviceType
.c_str();
852 if( *p
== '_' ) ++p
; // Skip leading '_'.
853 q
= strchr( p
, '.' ); // Find first '.'.
854 if( q
) tmp
.assign( p
, (size_t)( q
- p
) ); // Use only up to the first '.'.
855 else tmp
.assign( p
); // No '.' so use the entire string.
856 UTF8StringToStringObject( tmp
.c_str(), type
);
857 UTF8StringToStringObject( ( *i
).description
.c_str(), desc
);
861 n
= mServiceList
.GetItemCount();
862 mServiceList
.InsertItem( n
, type
);
863 mServiceList
.SetItemText( n
, 1, desc
);
866 // Select the first service type by default.
868 if( !mServiceTypes
.empty() )
870 mServiceList
.SetItemState( 0, LVIS_SELECTED
| LVIS_FOCUSED
, LVIS_SELECTED
| LVIS_FOCUSED
);
874 //===========================================================================================================================
876 //===========================================================================================================================
878 void ChooserDialog::UpdateInfoDisplay( void )
886 std::string textNewLines
;
887 std::string hostName
;
889 std::string::iterator i
;
891 // Display the service instance if it is selected. Otherwise, clear all the info.
893 selectedItem
= mChooserList
.GetNextItem( -1, LVNI_SELECTED
);
894 if( selectedItem
>= 0 )
896 ServiceInstanceInfo
* p
;
898 assert( selectedItem
< (int) mServiceInstances
.size() );
899 p
= &mServiceInstances
[ selectedItem
];
905 hostName
= p
->hostName
;
907 // Sync up the list items with the actual data (IP address may change).
909 UTF8StringToStringObject( ip
.c_str(), s
);
910 mChooserList
.SetItemText( selectedItem
, 1, s
);
915 item
= (CWnd
*) this->GetDlgItem( IDC_INFO_NAME_TEXT
);
917 UTF8StringToStringObject( name
.c_str(), s
);
918 item
->SetWindowText( s
);
922 item
= (CWnd
*) this->GetDlgItem( IDC_INFO_IP_TEXT
);
924 UTF8StringToStringObject( ip
.c_str(), s
);
925 item
->SetWindowText( s
);
929 item
= (CWnd
*) this->GetDlgItem( IDC_INFO_INTERFACE_TEXT
);
931 UTF8StringToStringObject( ifIP
.c_str(), s
);
932 item
->SetWindowText( s
);
935 item
= (CWnd
*) this->GetDlgItem( IDC_INFO_HOST_NAME_TEXT
);
937 UTF8StringToStringObject( hostName
.c_str(), s
);
938 item
->SetWindowText( s
);
942 item
= (CWnd
*) this->GetDlgItem( IDC_INFO_TEXT_TEXT
);
944 for( i
= text
.begin(); i
!= text
.end(); ++i
)
948 textNewLines
+= "\r\n";
955 UTF8StringToStringObject( textNewLines
.c_str(), s
);
956 item
->SetWindowText( s
);
963 //===========================================================================================================================
965 //===========================================================================================================================
967 LONG
ChooserDialog::OnDomainAdd( WPARAM inWParam
, LPARAM inLParam
)
970 std::auto_ptr
< DomainEventInfo
> pAutoPtr
;
977 UNUSED_ALWAYS( inWParam
);
980 p
= reinterpret_cast <DomainEventInfo
*> ( inLParam
);
983 // Search to see if we already know about this domain. If not, add it to the list.
987 n
= mDomainList
.GetItemCount();
988 for( i
= 0; i
< n
; ++i
)
990 s
= mDomainList
.GetItemText( i
, 0 );
1001 mDomainList
.InsertItem( n
, domain
);
1003 // If no domains are selected and the domain being added is a default domain, select it.
1005 selectedItem
= mDomainList
.GetNextItem( -1, LVNI_SELECTED
);
1006 if( ( selectedItem
< 0 ) && ( p
->eventType
== kDNSBrowserEventTypeAddDefaultDomain
) )
1008 mDomainList
.SetItemState( n
, LVIS_SELECTED
| LVIS_FOCUSED
, LVIS_SELECTED
| LVIS_FOCUSED
);
1014 //===========================================================================================================================
1016 //===========================================================================================================================
1018 LONG
ChooserDialog::OnDomainRemove( WPARAM inWParam
, LPARAM inLParam
)
1020 DomainEventInfo
* p
;
1021 std::auto_ptr
< DomainEventInfo
> pAutoPtr
;
1028 UNUSED_ALWAYS( inWParam
);
1031 p
= reinterpret_cast <DomainEventInfo
*> ( inLParam
);
1032 pAutoPtr
.reset( p
);
1034 // Search to see if we know about this domain. If so, remove it from the list.
1038 n
= mDomainList
.GetItemCount();
1039 for( i
= 0; i
< n
; ++i
)
1041 s
= mDomainList
.GetItemText( i
, 0 );
1050 mDomainList
.DeleteItem( i
);
1055 //===========================================================================================================================
1057 //===========================================================================================================================
1059 LONG
ChooserDialog::OnServiceAdd( WPARAM inWParam
, LPARAM inLParam
)
1061 ServiceEventInfo
* p
;
1062 std::auto_ptr
< ServiceEventInfo
> pAutoPtr
;
1064 UNUSED_ALWAYS( inWParam
);
1067 p
= reinterpret_cast <ServiceEventInfo
*> ( inLParam
);
1068 pAutoPtr
.reset( p
);
1073 //===========================================================================================================================
1075 //===========================================================================================================================
1077 LONG
ChooserDialog::OnServiceRemove( WPARAM inWParam
, LPARAM inLParam
)
1079 ServiceEventInfo
* p
;
1080 std::auto_ptr
< ServiceEventInfo
> pAutoPtr
;
1085 UNUSED_ALWAYS( inWParam
);
1088 p
= reinterpret_cast <ServiceEventInfo
*> ( inLParam
);
1089 pAutoPtr
.reset( p
);
1091 // Search to see if we know about this service instance. If so, remove it from the list.
1094 n
= (int) mServiceInstances
.size();
1095 for( i
= 0; i
< n
; ++i
)
1097 ServiceInstanceInfo
* q
;
1099 // If the name, type, domain, and interface match, treat it as the same service instance.
1101 q
= &mServiceInstances
[ i
];
1102 if( ( p
->name
== q
->name
) &&
1103 ( p
->type
== q
->type
) &&
1104 ( p
->domain
== q
->domain
) )
1112 mChooserList
.DeleteItem( i
);
1113 assert( i
< (int) mServiceInstances
.size() );
1114 mServiceInstances
.erase( mServiceInstances
.begin() + i
);
1119 //===========================================================================================================================
1121 //===========================================================================================================================
1123 LONG
ChooserDialog::OnResolve( WPARAM inWParam
, LPARAM inLParam
)
1125 ServiceInstanceInfo
* p
;
1126 std::auto_ptr
< ServiceInstanceInfo
> pAutoPtr
;
1132 UNUSED_ALWAYS( inWParam
);
1135 p
= reinterpret_cast <ServiceInstanceInfo
*> ( inLParam
);
1136 pAutoPtr
.reset( p
);
1138 // Make sure it is for an item of the correct type. This handles any resolves that may have been queued up.
1140 selectedType
= mServiceList
.GetNextItem( -1, LVNI_SELECTED
);
1141 assert( selectedType
>= 0 );
1142 if( selectedType
>= 0 )
1144 assert( selectedType
<= (int) mServiceTypes
.size() );
1145 if( p
->type
!= mServiceTypes
[ selectedType
].serviceType
)
1151 // Search to see if we know about this service instance. If so, update its info. Otherwise, add it to the list.
1154 n
= (int) mServiceInstances
.size();
1155 for( i
= 0; i
< n
; ++i
)
1157 ServiceInstanceInfo
* q
;
1159 // If the name, type, domain, and interface matches, treat it as the same service instance.
1161 q
= &mServiceInstances
[ i
];
1162 if( ( p
->name
== q
->name
) &&
1163 ( p
->type
== q
->type
) &&
1164 ( p
->domain
== q
->domain
) &&
1165 ( p
->ifIP
== q
->ifIP
) )
1173 mServiceInstances
[ i
] = *p
;
1179 mServiceInstances
.push_back( *p
);
1180 UTF8StringToStringObject( p
->name
.c_str(), s
);
1181 mChooserList
.InsertItem( n
, s
);
1183 UTF8StringToStringObject( p
->ip
.c_str(), s
);
1184 mChooserList
.SetItemText( n
, 1, s
);
1186 // If this is the only item, select it.
1190 mChooserList
.SetItemState( n
, LVIS_SELECTED
| LVIS_FOCUSED
, LVIS_SELECTED
| LVIS_FOCUSED
);
1193 UpdateInfoDisplay();
1199 //===========================================================================================================================
1201 //===========================================================================================================================
1203 void ChooserDialog::StartBrowsing( const char *inType
, const char *inDomain
)
1207 assert( mServiceInstances
.empty() );
1208 assert( mChooserList
.GetItemCount() == 0 );
1209 assert( !mIsServiceBrowsing
);
1211 mChooserList
.DeleteAllItems();
1212 mServiceInstances
.clear();
1214 mIsServiceBrowsing
= true;
1215 err
= DNSBrowserStartServiceSearch( mBrowser
, kDNSBrowserFlagAutoResolve
, inType
, inDomain
);
1216 assert( err
== kDNSNoErr
);
1219 //===========================================================================================================================
1221 //===========================================================================================================================
1223 void ChooserDialog::StopBrowsing( void )
1225 // If searching, stop.
1227 if( mIsServiceBrowsing
)
1231 mIsServiceBrowsing
= false;
1232 err
= DNSBrowserStopServiceSearch( mBrowser
, 0 );
1233 assert( err
== kDNSNoErr
);
1236 // Remove all service instances.
1238 mChooserList
.DeleteAllItems();
1239 assert( mChooserList
.GetItemCount() == 0 );
1240 mServiceInstances
.clear();
1241 assert( mServiceInstances
.empty() );
1242 UpdateInfoDisplay();
1249 //===========================================================================================================================
1251 //===========================================================================================================================
1256 DNSBrowserRef inRef
,
1257 DNSStatus inStatusCode
,
1258 const DNSBrowserEvent
* inEvent
)
1260 ChooserDialog
* dialog
;
1264 UNUSED_ALWAYS( inStatusCode
);
1265 UNUSED_ALWAYS( inRef
);
1267 // Check parameters.
1269 assert( inContext
);
1270 dialog
= reinterpret_cast <ChooserDialog
*> ( inContext
);
1274 switch( inEvent
->type
)
1276 case kDNSBrowserEventTypeRelease
:
1281 case kDNSBrowserEventTypeAddDomain
:
1282 case kDNSBrowserEventTypeAddDefaultDomain
:
1283 case kDNSBrowserEventTypeRemoveDomain
:
1285 DomainEventInfo
* domain
;
1286 std::auto_ptr
< DomainEventInfo
> domainAutoPtr
;
1288 domain
= new DomainEventInfo
;
1289 domainAutoPtr
.reset( domain
);
1291 domain
->eventType
= inEvent
->type
;
1292 domain
->domain
= inEvent
->data
.addDomain
.domain
;
1293 domain
->ifIP
= inEvent
->data
.addDomain
.interfaceIP
;
1295 message
= ( inEvent
->type
== kDNSBrowserEventTypeRemoveDomain
) ? WM_USER_DOMAIN_REMOVE
: WM_USER_DOMAIN_ADD
;
1296 posted
= ::PostMessage( dialog
->GetSafeHwnd(), message
, 0, (LPARAM
) domain
);
1300 domainAutoPtr
.release();
1307 case kDNSBrowserEventTypeAddService
:
1308 case kDNSBrowserEventTypeRemoveService
:
1310 ServiceEventInfo
* service
;
1311 std::auto_ptr
< ServiceEventInfo
> serviceAutoPtr
;
1313 service
= new ServiceEventInfo
;
1314 serviceAutoPtr
.reset( service
);
1316 service
->eventType
= inEvent
->type
;
1317 service
->name
= inEvent
->data
.addService
.name
;
1318 service
->type
= inEvent
->data
.addService
.type
;
1319 service
->domain
= inEvent
->data
.addService
.domain
;
1320 service
->ifIP
= inEvent
->data
.addService
.interfaceIP
;
1322 message
= ( inEvent
->type
== kDNSBrowserEventTypeAddService
) ? WM_USER_SERVICE_ADD
: WM_USER_SERVICE_REMOVE
;
1323 posted
= ::PostMessage( dialog
->GetSafeHwnd(), message
, 0, (LPARAM
) service
);
1327 serviceAutoPtr
.release();
1334 case kDNSBrowserEventTypeResolved
:
1335 if( inEvent
->data
.resolved
->address
.addressType
== kDNSNetworkAddressTypeIPv4
)
1337 ServiceInstanceInfo
* serviceInstance
;
1338 std::auto_ptr
< ServiceInstanceInfo
> serviceInstanceAutoPtr
;
1341 serviceInstance
= new ServiceInstanceInfo
;
1342 serviceInstanceAutoPtr
.reset( serviceInstance
);
1344 serviceInstance
->name
= inEvent
->data
.resolved
->name
;
1345 serviceInstance
->type
= inEvent
->data
.resolved
->type
;
1346 serviceInstance
->domain
= inEvent
->data
.resolved
->domain
;
1347 serviceInstance
->ip
= DNSNetworkAddressToString( &inEvent
->data
.resolved
->address
, s
);
1348 serviceInstance
->ifIP
= DNSNetworkAddressToString( &inEvent
->data
.resolved
->interfaceIP
, s
);
1349 serviceInstance
->text
= inEvent
->data
.resolved
->textRecord
;
1350 serviceInstance
->hostName
= inEvent
->data
.resolved
->hostName
;
1352 posted
= ::PostMessage( dialog
->GetSafeHwnd(), WM_USER_RESOLVE
, 0, (LPARAM
) serviceInstance
);
1356 serviceInstanceAutoPtr
.release();
1367 // Don't let exceptions escape.
1371 //===========================================================================================================================
1372 // DNSNetworkAddressToString
1374 // Note: Currently only supports IPv4 network addresses.
1375 //===========================================================================================================================
1377 static char * DNSNetworkAddressToString( const DNSNetworkAddress
*inAddr
, char *outString
)
1382 p
= inAddr
->u
.ipv4
.addr
.v8
;
1383 port
= ntohs( inAddr
->u
.ipv4
.port
.v16
);
1384 if( port
!= kDNSPortInvalid
)
1386 sprintf( outString
, "%u.%u.%u.%u:%u", p
[ 0 ], p
[ 1 ], p
[ 2 ], p
[ 3 ], port
);
1390 sprintf( outString
, "%u.%u.%u.%u", p
[ 0 ], p
[ 1 ], p
[ 2 ], p
[ 3 ] );
1392 return( outString
);
1395 //===========================================================================================================================
1396 // UTF8StringToStringObject
1397 //===========================================================================================================================
1399 static DWORD
UTF8StringToStringObject( const char *inUTF8
, CString
&inObject
)
1407 n
= MultiByteToWideChar( CP_UTF8
, 0, inUTF8
, -1, NULL
, 0 );
1410 unicode
= (BSTR
) malloc( (size_t)( n
* sizeof( wchar_t ) ) );
1413 err
= ERROR_INSUFFICIENT_BUFFER
;
1417 n
= MultiByteToWideChar( CP_UTF8
, 0, inUTF8
, -1, unicode
, n
);
1424 err
= ERROR_NO_UNICODE_TRANSLATION
;
1442 //===========================================================================================================================
1443 // StringObjectToUTF8String
1444 //===========================================================================================================================
1446 static DWORD
StringObjectToUTF8String( CString
&inObject
, std::string
&outUTF8
)
1457 nUnicode
= inObject
.GetLength();
1460 unicode
= inObject
.AllocSysString();
1461 n
= WideCharToMultiByte( CP_UTF8
, 0, unicode
, nUnicode
, NULL
, 0, NULL
, NULL
);
1464 utf8
= (char *) malloc( (size_t) n
);
1466 if( !utf8
) { err
= ERROR_INSUFFICIENT_BUFFER
; goto exit
; }
1468 n
= WideCharToMultiByte( CP_UTF8
, 0, unicode
, nUnicode
, utf8
, n
, NULL
, NULL
);
1473 outUTF8
.assign( utf8
, n
);
1477 err
= ERROR_NO_UNICODE_TRANSLATION
;
1490 SysFreeString( unicode
);