]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/ChooserDialog.cpp
162acdac40fd09cbe6bcc861a458c19e6d51346d
[apple/mdnsresponder.git] / mDNSWindows / Applications / DNSServiceBrowser / Windows / Sources / ChooserDialog.cpp
1 /*
2 * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
7 *
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * file.
14 *
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
22 *
23 * @APPLE_LICENSE_HEADER_END@
24
25 Change History (most recent first):
26
27 $Log: ChooserDialog.cpp,v $
28 Revision 1.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.
30
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.
34
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.
37
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.
40
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.
43
44 Revision 1.5 2003/10/16 09:21:56 bradley
45 Ignore non-IPv4 resolves until mDNS on Windows supports IPv6.
46
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.
49
50 Revision 1.3 2003/10/09 19:50:40 bradley
51 Sort service type list by description.
52
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.
57
58 Revision 1.1 2003/08/21 02:06:47 bradley
59 Moved Rendezvous Browser for non-Windows CE into Windows sub-folder.
60
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.
63
64 Revision 1.6 2003/08/12 19:56:28 cheshire
65 Update to APSL 2.0
66
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
69
70 Revision 1.4 2003/07/02 21:20:06 cheshire
71 <rdar://problem/3313413> Update copyright notices, etc., in source code comments
72
73 Revision 1.3 2002/09/21 20:44:55 zarzycki
74 Added APSL info
75
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.
79
80 Revision 1.1 2002/09/20 06:12:52 bradley
81 Rendezvous Browser for Windows
82
83 */
84
85 #include <assert.h>
86 #include <stdio.h>
87 #include <stdlib.h>
88 #include <string.h>
89 #include <time.h>
90
91 #include <algorithm>
92 #include <memory>
93
94 #include "stdafx.h"
95
96 #include "DNSServices.h"
97
98 #include "Application.h"
99 #include "AboutDialog.h"
100 #include "LoginDialog.h"
101 #include "Resource.h"
102
103 #include "ChooserDialog.h"
104
105 #ifdef _DEBUG
106 #define new DEBUG_NEW
107 #undef THIS_FILE
108 static char THIS_FILE[] = __FILE__;
109 #endif
110
111 #if 0
112 #pragma mark == Constants ==
113 #endif
114
115 //===========================================================================================================================
116 // Constants
117 //===========================================================================================================================
118
119 // Menus
120
121 enum
122 {
123 kChooserMenuIndexFile = 0,
124 kChooserMenuIndexHelp = 1
125 };
126
127 // Domain List
128
129 #define kDomainListDefaultDomainColumnWidth 164
130
131 // Service List
132
133 #define kServiceListDefaultServiceColumnTypeWidth 146
134 #define kServiceListDefaultServiceColumnDescWidth 230
135
136 // Chooser List
137
138 #define kChooserListDefaultNameColumnWidth 190
139 #define kChooserListDefaultIPColumnWidth 120
140
141 // Windows User Messages
142
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 )
148
149 #if 0
150 #pragma mark == Constants - Service Table ==
151 #endif
152
153 //===========================================================================================================================
154 // Constants - Service Table
155 //===========================================================================================================================
156
157 struct KnownServiceEntry
158 {
159 const char * serviceType;
160 const char * description;
161 const char * urlScheme;
162 bool useText;
163 };
164
165 static const KnownServiceEntry kKnownServiceTable[] =
166 {
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 },
284
285 { "", "", "", false },
286
287 // Unofficial and invalid service types that will be phased out:
288
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 },
297
298 { NULL, NULL, NULL, false },
299 };
300
301 #if 0
302 #pragma mark == Structures ==
303 #endif
304
305 //===========================================================================================================================
306 // Structures
307 //===========================================================================================================================
308
309 struct DomainEventInfo
310 {
311 DNSBrowserEventType eventType;
312 CString domain;
313 DNSNetworkAddress ifIP;
314 };
315
316 struct ServiceEventInfo
317 {
318 DNSBrowserEventType eventType;
319 std::string name;
320 std::string type;
321 std::string domain;
322 DNSNetworkAddress ifIP;
323 };
324
325 #if 0
326 #pragma mark == Prototypes ==
327 #endif
328
329 //===========================================================================================================================
330 // Prototypes
331 //===========================================================================================================================
332
333 static void
334 BrowserCallBack(
335 void * inContext,
336 DNSBrowserRef inRef,
337 DNSStatus inStatusCode,
338 const DNSBrowserEvent * inEvent );
339
340 static char * DNSNetworkAddressToString( const DNSNetworkAddress *inAddr, char *outString );
341
342 static DWORD UTF8StringToStringObject( const char *inUTF8, CString &inObject );
343 static DWORD StringObjectToUTF8String( CString &inObject, std::string &outUTF8 );
344
345 #if 0
346 #pragma mark == Message Map ==
347 #endif
348
349 //===========================================================================================================================
350 // Message Map
351 //===========================================================================================================================
352
353 BEGIN_MESSAGE_MAP(ChooserDialog, CDialog)
354 //{{AFX_MSG_MAP(ChooserDialog)
355 ON_WM_SYSCOMMAND()
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()
362 ON_WM_ACTIVATE()
363 ON_COMMAND(ID_FILE_CLOSE, OnFileClose)
364 ON_COMMAND(ID_FILE_EXIT, OnExit)
365 ON_WM_CLOSE()
366 ON_WM_NCDESTROY()
367 //}}AFX_MSG_MAP
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 )
373 END_MESSAGE_MAP()
374
375 #if 0
376 #pragma mark == Routines ==
377 #endif
378
379 //===========================================================================================================================
380 // ChooserDialog
381 //===========================================================================================================================
382
383 ChooserDialog::ChooserDialog( CWnd *inParent )
384 : CDialog( ChooserDialog::IDD, inParent)
385 {
386 //{{AFX_DATA_INIT(ChooserDialog)
387 // NOTE: the ClassWizard will add member initialization here
388 //}}AFX_DATA_INIT
389
390 // Load menu accelerator table.
391
392 mMenuAcceleratorTable = ::LoadAccelerators( AfxGetInstanceHandle(), MAKEINTRESOURCE( IDR_CHOOSER_DIALOG_MENU_ACCELERATORS ) );
393 assert( mMenuAcceleratorTable );
394
395 mBrowser = NULL;
396 mIsServiceBrowsing = false;
397 }
398
399 //===========================================================================================================================
400 // ~ChooserDialog
401 //===========================================================================================================================
402
403 ChooserDialog::~ChooserDialog( void )
404 {
405 if( mBrowser )
406 {
407 DNSStatus err;
408
409 err = DNSBrowserRelease( mBrowser, 0 );
410 assert( err == kDNSNoErr );
411 }
412 }
413
414 //===========================================================================================================================
415 // DoDataExchange
416 //===========================================================================================================================
417
418 void ChooserDialog::DoDataExchange( CDataExchange *pDX )
419 {
420 CDialog::DoDataExchange(pDX);
421
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);
426 //}}AFX_DATA_MAP
427 }
428
429 //===========================================================================================================================
430 // OnInitDialog
431 //===========================================================================================================================
432
433 BOOL ChooserDialog::OnInitDialog( void )
434 {
435 HICON icon;
436 BOOL result;
437 CString tempString;
438 DNSStatus err;
439
440 // Initialize our parent.
441
442 CDialog::OnInitDialog();
443
444 // Set up the window icon.
445
446 icon = AfxGetApp()->LoadIcon( IDR_MAIN_ICON );
447 assert( icon );
448 if( icon )
449 {
450 SetIcon( icon, TRUE ); // Set big icon
451 SetIcon( icon, FALSE ); // Set small icon
452 }
453
454 // Set up the Domain List.
455
456 result = tempString.LoadString( IDS_CHOOSER_DOMAIN_COLUMN_NAME );
457 assert( result );
458 mDomainList.InsertColumn( 0, tempString, LVCFMT_LEFT, kDomainListDefaultDomainColumnWidth );
459
460 // Set up the Service List.
461
462 result = tempString.LoadString( IDS_CHOOSER_SERVICE_COLUMN_TYPE );
463 assert( result );
464 mServiceList.InsertColumn( 0, tempString, LVCFMT_LEFT, kServiceListDefaultServiceColumnTypeWidth );
465
466 result = tempString.LoadString( IDS_CHOOSER_SERVICE_COLUMN_DESC );
467 assert( result );
468 mServiceList.InsertColumn( 1, tempString, LVCFMT_LEFT, kServiceListDefaultServiceColumnDescWidth );
469
470 PopulateServicesList();
471
472 // Set up the Chooser List.
473
474 result = tempString.LoadString( IDS_CHOOSER_CHOOSER_NAME_COLUMN_NAME );
475 assert( result );
476 mChooserList.InsertColumn( 0, tempString, LVCFMT_LEFT, kChooserListDefaultNameColumnWidth );
477
478 result = tempString.LoadString( IDS_CHOOSER_CHOOSER_IP_COLUMN_NAME );
479 assert( result );
480 mChooserList.InsertColumn( 1, tempString, LVCFMT_LEFT, kChooserListDefaultIPColumnWidth );
481
482 // Set up the other controls.
483
484 UpdateInfoDisplay();
485
486 // Start browsing for domains.
487
488 err = DNSBrowserCreate( 0, BrowserCallBack, this, &mBrowser );
489 assert( err == kDNSNoErr );
490
491 err = DNSBrowserStartDomainSearch( mBrowser, 0 );
492 assert( err == kDNSNoErr );
493
494 return( true );
495 }
496
497 //===========================================================================================================================
498 // OnFileClose
499 //===========================================================================================================================
500
501 void ChooserDialog::OnFileClose()
502 {
503 OnClose();
504 }
505
506 //===========================================================================================================================
507 // OnActivate
508 //===========================================================================================================================
509
510 void ChooserDialog::OnActivate( UINT nState, CWnd* pWndOther, BOOL bMinimized )
511 {
512 // Always make the active window the "main" window so modal dialogs work better and the app quits after closing
513 // the last window.
514
515 gApp.m_pMainWnd = this;
516
517 CDialog::OnActivate(nState, pWndOther, bMinimized);
518 }
519
520 //===========================================================================================================================
521 // PostNcDestroy
522 //===========================================================================================================================
523
524 void ChooserDialog::PostNcDestroy()
525 {
526 // Call the base class to do the normal cleanup.
527
528 delete this;
529 }
530
531 //===========================================================================================================================
532 // PreTranslateMessage
533 //===========================================================================================================================
534
535 BOOL ChooserDialog::PreTranslateMessage(MSG* pMsg)
536 {
537 BOOL result;
538
539 result = false;
540 assert( mMenuAcceleratorTable );
541 if( mMenuAcceleratorTable )
542 {
543 result = ::TranslateAccelerator( m_hWnd, mMenuAcceleratorTable, pMsg );
544 }
545 if( !result )
546 {
547 result = CDialog::PreTranslateMessage( pMsg );
548 }
549 return( result );
550 }
551
552 //===========================================================================================================================
553 // OnInitMenuPopup
554 //===========================================================================================================================
555
556 void ChooserDialog::OnInitMenuPopup( CMenu *pPopupMenu, UINT nIndex, BOOL bSysMenu )
557 {
558 CDialog::OnInitMenuPopup( pPopupMenu, nIndex, bSysMenu );
559
560 switch( nIndex )
561 {
562 case kChooserMenuIndexFile:
563 break;
564
565 case kChooserMenuIndexHelp:
566 break;
567
568 default:
569 break;
570 }
571 }
572
573 //===========================================================================================================================
574 // OnExit
575 //===========================================================================================================================
576
577 void ChooserDialog::OnExit()
578 {
579 OnClose();
580 }
581
582 //===========================================================================================================================
583 // OnAbout
584 //===========================================================================================================================
585
586 void ChooserDialog::OnAbout()
587 {
588 AboutDialog dialog;
589
590 dialog.DoModal();
591 }
592
593 //===========================================================================================================================
594 // OnSysCommand
595 //===========================================================================================================================
596
597 void ChooserDialog::OnSysCommand( UINT inID, LPARAM inParam )
598 {
599 CDialog::OnSysCommand( inID, inParam );
600 }
601
602 //===========================================================================================================================
603 // OnClose
604 //===========================================================================================================================
605
606 void ChooserDialog::OnClose()
607 {
608 StopBrowsing();
609
610 gApp.m_pMainWnd = this;
611 DestroyWindow();
612 }
613
614 //===========================================================================================================================
615 // OnNcDestroy
616 //===========================================================================================================================
617
618 void ChooserDialog::OnNcDestroy()
619 {
620 gApp.m_pMainWnd = this;
621
622 CDialog::OnNcDestroy();
623 }
624
625 //===========================================================================================================================
626 // OnDomainListChanged
627 //===========================================================================================================================
628
629 void ChooserDialog::OnDomainListChanged( NMHDR *pNMHDR, LRESULT *pResult )
630 {
631 UNUSED_ALWAYS( pNMHDR );
632
633 // Domain list changes have similar effects to service list changes so reuse that code path by calling it here.
634
635 OnServiceListChanged( NULL, NULL );
636
637 *pResult = 0;
638 }
639
640 //===========================================================================================================================
641 // OnServiceListChanged
642 //===========================================================================================================================
643
644 void ChooserDialog::OnServiceListChanged( NMHDR *pNMHDR, LRESULT *pResult )
645 {
646 int selectedType;
647 int selectedDomain;
648
649 UNUSED_ALWAYS( pNMHDR );
650
651 // Stop any existing service search.
652
653 StopBrowsing();
654
655 // If a domain and service type are selected, start searching for the service type on the domain.
656
657 selectedType = mServiceList.GetNextItem( -1, LVNI_SELECTED );
658 selectedDomain = mDomainList.GetNextItem( -1, LVNI_SELECTED );
659
660 if( ( selectedType >= 0 ) && ( selectedDomain >= 0 ) )
661 {
662 CString s;
663 std::string utf8;
664 const char * type;
665
666 s = mDomainList.GetItemText( selectedDomain, 0 );
667 StringObjectToUTF8String( s, utf8 );
668 type = mServiceTypes[ selectedType ].serviceType.c_str();
669 if( *type != '\0' )
670 {
671 StartBrowsing( type, utf8.c_str() );
672 }
673 }
674
675 if( pResult )
676 {
677 *pResult = 0;
678 }
679 }
680
681 //===========================================================================================================================
682 // OnChooserListChanged
683 //===========================================================================================================================
684
685 void ChooserDialog::OnChooserListChanged( NMHDR *pNMHDR, LRESULT *pResult )
686 {
687 UNUSED_ALWAYS( pNMHDR );
688
689 UpdateInfoDisplay();
690 *pResult = 0;
691 }
692
693 //===========================================================================================================================
694 // OnChooserListDoubleClick
695 //===========================================================================================================================
696
697 void ChooserDialog::OnChooserListDoubleClick( NMHDR *pNMHDR, LRESULT *pResult )
698 {
699 int selectedItem;
700
701 UNUSED_ALWAYS( pNMHDR );
702
703 // Display the service instance if it is selected. Otherwise, clear all the info.
704
705 selectedItem = mChooserList.GetNextItem( -1, LVNI_SELECTED );
706 if( selectedItem >= 0 )
707 {
708 ServiceInstanceInfo * p;
709 CString url;
710 const KnownServiceEntry * service;
711
712 assert( selectedItem < (int) mServiceInstances.size() );
713 p = &mServiceInstances[ selectedItem ];
714
715 // Search for a known service type entry that matches.
716
717 for( service = kKnownServiceTable; service->serviceType; ++service )
718 {
719 if( p->type == service->serviceType )
720 {
721 break;
722 }
723 }
724 if( service->serviceType )
725 {
726 const char * text;
727
728 // Create a URL representing the service instance.
729
730 if( strcmp( service->serviceType, "_smb._tcp." ) == 0 )
731 {
732 // Special case for SMB (no port number).
733
734 url.Format( TEXT( "%s%s/" ), service->urlScheme, (const char *) p->ip.c_str() );
735 }
736 else if( strcmp( service->serviceType, "_ftp._tcp." ) == 0 )
737 {
738 // Special case for FTP to get login info.
739
740 LoginDialog dialog;
741 CString username;
742 CString password;
743
744 if( !dialog.GetLogin( username, password ) )
745 {
746 goto exit;
747 }
748
749 // Build URL in the following format:
750 //
751 // ftp://[username[:password]@]<ip>
752
753 url += service->urlScheme;
754 if( username.GetLength() > 0 )
755 {
756 url += username;
757 if( password.GetLength() > 0 )
758 {
759 url += ':';
760 url += password;
761 }
762 url += '@';
763 }
764 url += p->ip.c_str();
765 }
766 else if( strcmp( service->serviceType, "_http._tcp." ) == 0 )
767 {
768 // Special case for HTTP to exclude "path=" if present.
769
770 text = service->useText ? p->text.c_str() : "";
771 if( strncmp( text, "path=", 5 ) == 0 )
772 {
773 text += 5;
774 }
775 if( *text != '/' )
776 {
777 url.Format( TEXT( "%s%s/%s" ), service->urlScheme, (const char *) p->ip.c_str(), text );
778 }
779 else
780 {
781 url.Format( TEXT( "%s%s%s" ), service->urlScheme, (const char *) p->ip.c_str(), text );
782 }
783 }
784 else
785 {
786 text = service->useText ? p->text.c_str() : "";
787 url.Format( TEXT( "%s%s/%s" ), service->urlScheme, (const char *) p->ip.c_str(), text );
788 }
789
790 // Let the system open the URL in the correct app.
791
792 {
793 CWaitCursor waitCursor;
794
795 ShellExecute( NULL, TEXT( "open" ), url, TEXT( "" ), TEXT( "c:\\" ), SW_SHOWNORMAL );
796 }
797 }
798 }
799
800 exit:
801 *pResult = 0;
802 }
803
804 //===========================================================================================================================
805 // OnCancel
806 //===========================================================================================================================
807
808 void ChooserDialog::OnCancel()
809 {
810 // Do nothing.
811 }
812
813 //===========================================================================================================================
814 // PopulateServicesList
815 //===========================================================================================================================
816
817 void ChooserDialog::PopulateServicesList( void )
818 {
819 ServiceTypeVector::iterator i;
820 CString type;
821 CString desc;
822 std::string tmp;
823
824 // Add a fixed list of known services.
825
826 if( mServiceTypes.empty() )
827 {
828 const KnownServiceEntry * service;
829
830 for( service = kKnownServiceTable; service->serviceType; ++service )
831 {
832 ServiceTypeInfo info;
833
834 info.serviceType = service->serviceType;
835 info.description = service->description;
836 info.urlScheme = service->urlScheme;
837 mServiceTypes.push_back( info );
838 }
839 }
840
841 // Add each service to the list.
842
843 for( i = mServiceTypes.begin(); i != mServiceTypes.end(); ++i )
844 {
845 const char * p;
846 const char * q;
847
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 );
855
856 int n;
857
858 n = mServiceList.GetItemCount();
859 mServiceList.InsertItem( n, type );
860 mServiceList.SetItemText( n, 1, desc );
861 }
862
863 // Select the first service type by default.
864
865 if( !mServiceTypes.empty() )
866 {
867 mServiceList.SetItemState( 0, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED );
868 }
869 }
870
871 //===========================================================================================================================
872 // UpdateInfoDisplay
873 //===========================================================================================================================
874
875 void ChooserDialog::UpdateInfoDisplay( void )
876 {
877 int selectedItem;
878 std::string name;
879 CString s;
880 std::string ip;
881 std::string ifIP;
882 std::string text;
883 std::string textNewLines;
884 std::string hostName;
885 CWnd * item;
886 std::string::iterator i;
887
888 // Display the service instance if it is selected. Otherwise, clear all the info.
889
890 selectedItem = mChooserList.GetNextItem( -1, LVNI_SELECTED );
891 if( selectedItem >= 0 )
892 {
893 ServiceInstanceInfo * p;
894
895 assert( selectedItem < (int) mServiceInstances.size() );
896 p = &mServiceInstances[ selectedItem ];
897
898 name = p->name;
899 ip = p->ip;
900 ifIP = p->ifIP;
901 text = p->text;
902 hostName = p->hostName;
903
904 // Sync up the list items with the actual data (IP address may change).
905
906 UTF8StringToStringObject( ip.c_str(), s );
907 mChooserList.SetItemText( selectedItem, 1, s );
908 }
909
910 // Name
911
912 item = (CWnd *) this->GetDlgItem( IDC_INFO_NAME_TEXT );
913 assert( item );
914 UTF8StringToStringObject( name.c_str(), s );
915 item->SetWindowText( s );
916
917 // IP
918
919 item = (CWnd *) this->GetDlgItem( IDC_INFO_IP_TEXT );
920 assert( item );
921 UTF8StringToStringObject( ip.c_str(), s );
922 item->SetWindowText( s );
923
924 // Interface
925
926 item = (CWnd *) this->GetDlgItem( IDC_INFO_INTERFACE_TEXT );
927 assert( item );
928 UTF8StringToStringObject( ifIP.c_str(), s );
929 item->SetWindowText( s );
930
931
932 item = (CWnd *) this->GetDlgItem( IDC_INFO_HOST_NAME_TEXT );
933 assert( item );
934 UTF8StringToStringObject( hostName.c_str(), s );
935 item->SetWindowText( s );
936
937 // Text
938
939 item = (CWnd *) this->GetDlgItem( IDC_INFO_TEXT_TEXT );
940 assert( item );
941 for( i = text.begin(); i != text.end(); ++i )
942 {
943 if( *i == '\1' )
944 {
945 textNewLines += "\r\n";
946 }
947 else
948 {
949 textNewLines += *i;
950 }
951 }
952 UTF8StringToStringObject( textNewLines.c_str(), s );
953 item->SetWindowText( s );
954 }
955
956 #if 0
957 #pragma mark -
958 #endif
959
960 //===========================================================================================================================
961 // OnDomainAdd
962 //===========================================================================================================================
963
964 LONG ChooserDialog::OnDomainAdd( WPARAM inWParam, LPARAM inLParam )
965 {
966 DomainEventInfo * p;
967 std::auto_ptr < DomainEventInfo > pAutoPtr;
968 int n;
969 int i;
970 CString domain;
971 CString s;
972 bool found;
973
974 UNUSED_ALWAYS( inWParam );
975
976 assert( inLParam );
977 p = reinterpret_cast <DomainEventInfo *> ( inLParam );
978 pAutoPtr.reset( p );
979
980 // Search to see if we already know about this domain. If not, add it to the list.
981
982 found = false;
983 domain = p->domain;
984 n = mDomainList.GetItemCount();
985 for( i = 0; i < n; ++i )
986 {
987 s = mDomainList.GetItemText( i, 0 );
988 if( s == domain )
989 {
990 found = true;
991 break;
992 }
993 }
994 if( !found )
995 {
996 int selectedItem;
997
998 mDomainList.InsertItem( n, domain );
999
1000 // If no domains are selected and the domain being added is a default domain, select it.
1001
1002 selectedItem = mDomainList.GetNextItem( -1, LVNI_SELECTED );
1003 if( ( selectedItem < 0 ) && ( p->eventType == kDNSBrowserEventTypeAddDefaultDomain ) )
1004 {
1005 mDomainList.SetItemState( n, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED );
1006 }
1007 }
1008 return( 0 );
1009 }
1010
1011 //===========================================================================================================================
1012 // OnDomainRemove
1013 //===========================================================================================================================
1014
1015 LONG ChooserDialog::OnDomainRemove( WPARAM inWParam, LPARAM inLParam )
1016 {
1017 DomainEventInfo * p;
1018 std::auto_ptr < DomainEventInfo > pAutoPtr;
1019 int n;
1020 int i;
1021 CString domain;
1022 CString s;
1023 bool found;
1024
1025 UNUSED_ALWAYS( inWParam );
1026
1027 assert( inLParam );
1028 p = reinterpret_cast <DomainEventInfo *> ( inLParam );
1029 pAutoPtr.reset( p );
1030
1031 // Search to see if we know about this domain. If so, remove it from the list.
1032
1033 found = false;
1034 domain = p->domain;
1035 n = mDomainList.GetItemCount();
1036 for( i = 0; i < n; ++i )
1037 {
1038 s = mDomainList.GetItemText( i, 0 );
1039 if( s == domain )
1040 {
1041 found = true;
1042 break;
1043 }
1044 }
1045 if( found )
1046 {
1047 mDomainList.DeleteItem( i );
1048 }
1049 return( 0 );
1050 }
1051
1052 //===========================================================================================================================
1053 // OnServiceAdd
1054 //===========================================================================================================================
1055
1056 LONG ChooserDialog::OnServiceAdd( WPARAM inWParam, LPARAM inLParam )
1057 {
1058 ServiceEventInfo * p;
1059 std::auto_ptr < ServiceEventInfo > pAutoPtr;
1060
1061 UNUSED_ALWAYS( inWParam );
1062
1063 assert( inLParam );
1064 p = reinterpret_cast <ServiceEventInfo *> ( inLParam );
1065 pAutoPtr.reset( p );
1066
1067 return( 0 );
1068 }
1069
1070 //===========================================================================================================================
1071 // OnServiceRemove
1072 //===========================================================================================================================
1073
1074 LONG ChooserDialog::OnServiceRemove( WPARAM inWParam, LPARAM inLParam )
1075 {
1076 ServiceEventInfo * p;
1077 std::auto_ptr < ServiceEventInfo > pAutoPtr;
1078 bool found;
1079 int n;
1080 int i;
1081
1082 UNUSED_ALWAYS( inWParam );
1083
1084 assert( inLParam );
1085 p = reinterpret_cast <ServiceEventInfo *> ( inLParam );
1086 pAutoPtr.reset( p );
1087
1088 // Search to see if we know about this service instance. If so, remove it from the list.
1089
1090 found = false;
1091 n = (int) mServiceInstances.size();
1092 for( i = 0; i < n; ++i )
1093 {
1094 ServiceInstanceInfo * q;
1095
1096 // If the name, type, domain, and interface match, treat it as the same service instance.
1097
1098 q = &mServiceInstances[ i ];
1099 if( ( p->name == q->name ) &&
1100 ( p->type == q->type ) &&
1101 ( p->domain == q->domain ) )
1102 {
1103 found = true;
1104 break;
1105 }
1106 }
1107 if( found )
1108 {
1109 mChooserList.DeleteItem( i );
1110 assert( i < (int) mServiceInstances.size() );
1111 mServiceInstances.erase( mServiceInstances.begin() + i );
1112 }
1113 return( 0 );
1114 }
1115
1116 //===========================================================================================================================
1117 // OnResolve
1118 //===========================================================================================================================
1119
1120 LONG ChooserDialog::OnResolve( WPARAM inWParam, LPARAM inLParam )
1121 {
1122 ServiceInstanceInfo * p;
1123 std::auto_ptr < ServiceInstanceInfo > pAutoPtr;
1124 int selectedType;
1125 int n;
1126 int i;
1127 bool found;
1128
1129 UNUSED_ALWAYS( inWParam );
1130
1131 assert( inLParam );
1132 p = reinterpret_cast <ServiceInstanceInfo *> ( inLParam );
1133 pAutoPtr.reset( p );
1134
1135 // Make sure it is for an item of the correct type. This handles any resolves that may have been queued up.
1136
1137 selectedType = mServiceList.GetNextItem( -1, LVNI_SELECTED );
1138 assert( selectedType >= 0 );
1139 if( selectedType >= 0 )
1140 {
1141 assert( selectedType <= (int) mServiceTypes.size() );
1142 if( p->type != mServiceTypes[ selectedType ].serviceType )
1143 {
1144 goto exit;
1145 }
1146 }
1147
1148 // Search to see if we know about this service instance. If so, update its info. Otherwise, add it to the list.
1149
1150 found = false;
1151 n = (int) mServiceInstances.size();
1152 for( i = 0; i < n; ++i )
1153 {
1154 ServiceInstanceInfo * q;
1155
1156 // If the name, type, domain, and interface matches, treat it as the same service instance.
1157
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 ) )
1163 {
1164 found = true;
1165 break;
1166 }
1167 }
1168 if( found )
1169 {
1170 mServiceInstances[ i ] = *p;
1171 }
1172 else
1173 {
1174 CString s;
1175
1176 mServiceInstances.push_back( *p );
1177 UTF8StringToStringObject( p->name.c_str(), s );
1178 mChooserList.InsertItem( n, s );
1179
1180 UTF8StringToStringObject( p->ip.c_str(), s );
1181 mChooserList.SetItemText( n, 1, s );
1182
1183 // If this is the only item, select it.
1184
1185 if( n == 0 )
1186 {
1187 mChooserList.SetItemState( n, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED );
1188 }
1189 }
1190 UpdateInfoDisplay();
1191
1192 exit:
1193 return( 0 );
1194 }
1195
1196 //===========================================================================================================================
1197 // StartBrowsing
1198 //===========================================================================================================================
1199
1200 void ChooserDialog::StartBrowsing( const char *inType, const char *inDomain )
1201 {
1202 DNSStatus err;
1203
1204 assert( mServiceInstances.empty() );
1205 assert( mChooserList.GetItemCount() == 0 );
1206 assert( !mIsServiceBrowsing );
1207
1208 mChooserList.DeleteAllItems();
1209 mServiceInstances.clear();
1210
1211 mIsServiceBrowsing = true;
1212 err = DNSBrowserStartServiceSearch( mBrowser, kDNSBrowserFlagAutoResolve, inType, inDomain );
1213 assert( err == kDNSNoErr );
1214 }
1215
1216 //===========================================================================================================================
1217 // StopBrowsing
1218 //===========================================================================================================================
1219
1220 void ChooserDialog::StopBrowsing( void )
1221 {
1222 // If searching, stop.
1223
1224 if( mIsServiceBrowsing )
1225 {
1226 DNSStatus err;
1227
1228 mIsServiceBrowsing = false;
1229 err = DNSBrowserStopServiceSearch( mBrowser, 0 );
1230 assert( err == kDNSNoErr );
1231 }
1232
1233 // Remove all service instances.
1234
1235 mChooserList.DeleteAllItems();
1236 assert( mChooserList.GetItemCount() == 0 );
1237 mServiceInstances.clear();
1238 assert( mServiceInstances.empty() );
1239 UpdateInfoDisplay();
1240 }
1241
1242 #if 0
1243 #pragma mark -
1244 #endif
1245
1246 //===========================================================================================================================
1247 // BrowserCallBack
1248 //===========================================================================================================================
1249
1250 static void
1251 BrowserCallBack(
1252 void * inContext,
1253 DNSBrowserRef inRef,
1254 DNSStatus inStatusCode,
1255 const DNSBrowserEvent * inEvent )
1256 {
1257 ChooserDialog * dialog;
1258 UINT message;
1259 BOOL posted;
1260
1261 UNUSED_ALWAYS( inStatusCode );
1262 UNUSED_ALWAYS( inRef );
1263
1264 // Check parameters.
1265
1266 assert( inContext );
1267 dialog = reinterpret_cast <ChooserDialog *> ( inContext );
1268
1269 try
1270 {
1271 switch( inEvent->type )
1272 {
1273 case kDNSBrowserEventTypeRelease:
1274 break;
1275
1276 // Domains
1277
1278 case kDNSBrowserEventTypeAddDomain:
1279 case kDNSBrowserEventTypeAddDefaultDomain:
1280 case kDNSBrowserEventTypeRemoveDomain:
1281 {
1282 DomainEventInfo * domain;
1283 std::auto_ptr < DomainEventInfo > domainAutoPtr;
1284
1285 domain = new DomainEventInfo;
1286 domainAutoPtr.reset( domain );
1287
1288 domain->eventType = inEvent->type;
1289 domain->domain = inEvent->data.addDomain.domain;
1290 domain->ifIP = inEvent->data.addDomain.interfaceIP;
1291
1292 message = ( inEvent->type == kDNSBrowserEventTypeRemoveDomain ) ? WM_USER_DOMAIN_REMOVE : WM_USER_DOMAIN_ADD;
1293 posted = ::PostMessage( dialog->GetSafeHwnd(), message, 0, (LPARAM) domain );
1294 assert( posted );
1295 if( posted )
1296 {
1297 domainAutoPtr.release();
1298 }
1299 break;
1300 }
1301
1302 // Services
1303
1304 case kDNSBrowserEventTypeAddService:
1305 case kDNSBrowserEventTypeRemoveService:
1306 {
1307 ServiceEventInfo * service;
1308 std::auto_ptr < ServiceEventInfo > serviceAutoPtr;
1309
1310 service = new ServiceEventInfo;
1311 serviceAutoPtr.reset( service );
1312
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;
1318
1319 message = ( inEvent->type == kDNSBrowserEventTypeAddService ) ? WM_USER_SERVICE_ADD : WM_USER_SERVICE_REMOVE;
1320 posted = ::PostMessage( dialog->GetSafeHwnd(), message, 0, (LPARAM) service );
1321 assert( posted );
1322 if( posted )
1323 {
1324 serviceAutoPtr.release();
1325 }
1326 break;
1327 }
1328
1329 // Resolves
1330
1331 case kDNSBrowserEventTypeResolved:
1332 if( inEvent->data.resolved->address.addressType == kDNSNetworkAddressTypeIPv4 )
1333 {
1334 ServiceInstanceInfo * serviceInstance;
1335 std::auto_ptr < ServiceInstanceInfo > serviceInstanceAutoPtr;
1336 char s[ 32 ];
1337
1338 serviceInstance = new ServiceInstanceInfo;
1339 serviceInstanceAutoPtr.reset( serviceInstance );
1340
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;
1348
1349 posted = ::PostMessage( dialog->GetSafeHwnd(), WM_USER_RESOLVE, 0, (LPARAM) serviceInstance );
1350 assert( posted );
1351 if( posted )
1352 {
1353 serviceInstanceAutoPtr.release();
1354 }
1355 }
1356 break;
1357
1358 default:
1359 break;
1360 }
1361 }
1362 catch( ... )
1363 {
1364 // Don't let exceptions escape.
1365 }
1366 }
1367
1368 //===========================================================================================================================
1369 // DNSNetworkAddressToString
1370 //
1371 // Note: Currently only supports IPv4 network addresses.
1372 //===========================================================================================================================
1373
1374 static char * DNSNetworkAddressToString( const DNSNetworkAddress *inAddr, char *outString )
1375 {
1376 const DNSUInt8 * p;
1377 DNSUInt16 port;
1378
1379 p = inAddr->u.ipv4.addr.v8;
1380 port = ntohs( inAddr->u.ipv4.port.v16 );
1381 if( port != kDNSPortInvalid )
1382 {
1383 sprintf( outString, "%u.%u.%u.%u:%u", p[ 0 ], p[ 1 ], p[ 2 ], p[ 3 ], port );
1384 }
1385 else
1386 {
1387 sprintf( outString, "%u.%u.%u.%u", p[ 0 ], p[ 1 ], p[ 2 ], p[ 3 ] );
1388 }
1389 return( outString );
1390 }
1391
1392 //===========================================================================================================================
1393 // UTF8StringToStringObject
1394 //===========================================================================================================================
1395
1396 static DWORD UTF8StringToStringObject( const char *inUTF8, CString &inObject )
1397 {
1398 DWORD err;
1399 int n;
1400 BSTR unicode;
1401
1402 unicode = NULL;
1403
1404 n = MultiByteToWideChar( CP_UTF8, 0, inUTF8, -1, NULL, 0 );
1405 if( n > 0 )
1406 {
1407 unicode = (BSTR) malloc( (size_t)( n * sizeof( wchar_t ) ) );
1408 if( !unicode )
1409 {
1410 err = ERROR_INSUFFICIENT_BUFFER;
1411 goto exit;
1412 }
1413
1414 n = MultiByteToWideChar( CP_UTF8, 0, inUTF8, -1, unicode, n );
1415 try
1416 {
1417 inObject = unicode;
1418 }
1419 catch( ... )
1420 {
1421 err = ERROR_NO_UNICODE_TRANSLATION;
1422 goto exit;
1423 }
1424 }
1425 else
1426 {
1427 inObject = "";
1428 }
1429 err = 0;
1430
1431 exit:
1432 if( unicode )
1433 {
1434 free( unicode );
1435 }
1436 return( err );
1437 }
1438
1439 //===========================================================================================================================
1440 // StringObjectToUTF8String
1441 //===========================================================================================================================
1442
1443 static DWORD StringObjectToUTF8String( CString &inObject, std::string &outUTF8 )
1444 {
1445 DWORD err;
1446 BSTR unicode;
1447 int nUnicode;
1448 int n;
1449 char * utf8;
1450
1451 unicode = NULL;
1452 utf8 = NULL;
1453
1454 nUnicode = inObject.GetLength();
1455 if( nUnicode > 0 )
1456 {
1457 unicode = inObject.AllocSysString();
1458 n = WideCharToMultiByte( CP_UTF8, 0, unicode, nUnicode, NULL, 0, NULL, NULL );
1459 assert( n > 0 );
1460
1461 utf8 = (char *) malloc( (size_t) n );
1462 assert( utf8 );
1463 if( !utf8 ) { err = ERROR_INSUFFICIENT_BUFFER; goto exit; }
1464
1465 n = WideCharToMultiByte( CP_UTF8, 0, unicode, nUnicode, utf8, n, NULL, NULL );
1466 assert( n > 0 );
1467
1468 try
1469 {
1470 outUTF8.assign( utf8, n );
1471 }
1472 catch( ... )
1473 {
1474 err = ERROR_NO_UNICODE_TRANSLATION;
1475 goto exit;
1476 }
1477 }
1478 else
1479 {
1480 outUTF8.clear();
1481 }
1482 err = 0;
1483
1484 exit:
1485 if( unicode )
1486 {
1487 SysFreeString( unicode );
1488 }
1489 if( utf8 )
1490 {
1491 free( utf8 );
1492 }
1493 return( err );
1494 }