Home | History | Annotate | Download | only in Sources
      1 /* -*- Mode: C; tab-width: 4 -*-
      2  *
      3  * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
      4  *
      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
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     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.
     16  */
     17 
     18 #include	<assert.h>
     19 #include	<stdio.h>
     20 #include	<stdlib.h>
     21 #include	<string.h>
     22 #include	<time.h>
     23 
     24 #include	<algorithm>
     25 #include	<memory>
     26 
     27 #include	"stdafx.h"
     28 
     29 #include	"DNSServices.h"
     30 
     31 #include	"Application.h"
     32 #include	"AboutDialog.h"
     33 #include	"LoginDialog.h"
     34 #include	"Resource.h"
     35 
     36 #include	"ChooserDialog.h"
     37 
     38 #ifdef _DEBUG
     39 #define new DEBUG_NEW
     40 #undef THIS_FILE
     41 static char THIS_FILE[] = __FILE__;
     42 #endif
     43 
     44 #if 0
     45 #pragma mark == Constants ==
     46 #endif
     47 
     48 //===========================================================================================================================
     49 //	Constants
     50 //===========================================================================================================================
     51 
     52 // Menus
     53 
     54 enum
     55 {
     56 	kChooserMenuIndexFile	= 0,
     57 	kChooserMenuIndexHelp	= 1
     58 };
     59 
     60 // Domain List
     61 
     62 #define kDomainListDefaultDomainColumnWidth		 		164
     63 
     64 // Service List
     65 
     66 #define kServiceListDefaultServiceColumnTypeWidth		146
     67 #define kServiceListDefaultServiceColumnDescWidth		230
     68 
     69 // Chooser List
     70 
     71 #define kChooserListDefaultNameColumnWidth				190
     72 #define kChooserListDefaultIPColumnWidth				120
     73 
     74 // Windows User Messages
     75 
     76 #define	WM_USER_DOMAIN_ADD								( WM_USER + 0x100 )
     77 #define	WM_USER_DOMAIN_REMOVE							( WM_USER + 0x101 )
     78 #define	WM_USER_SERVICE_ADD								( WM_USER + 0x102 )
     79 #define	WM_USER_SERVICE_REMOVE							( WM_USER + 0x103 )
     80 #define	WM_USER_RESOLVE									( WM_USER + 0x104 )
     81 
     82 #if 0
     83 #pragma mark == Constants - Service Table ==
     84 #endif
     85 
     86 //===========================================================================================================================
     87 //	Constants - Service Table
     88 //===========================================================================================================================
     89 
     90 struct	KnownServiceEntry
     91 {
     92 	const char *		serviceType;
     93 	const char *		description;
     94 	const char *		urlScheme;
     95 	bool				useText;
     96 };
     97 
     98 static const KnownServiceEntry		kKnownServiceTable[] =
     99 {
    100 	{ "_accountedge._tcp.",	 		"MYOB AccountEdge", 										"", 			false },
    101 	{ "_aecoretech._tcp.", 			"Apple Application Engineering Services",					"", 			false },
    102 	{ "_afpovertcp._tcp.", 			"Apple File Sharing (AFP)", 								"afp://", 		false },
    103 	{ "_airport._tcp.", 			"AirPort Base Station",										"", 			false },
    104 	{ "_apple-sasl._tcp.", 			"Apple Password Server", 									"", 			false },
    105 	{ "_aquamon._tcp.", 			"AquaMon", 													"", 			false },
    106 	{ "_async._tcp", 				"address-o-sync", 											"", 			false },
    107 	{ "_auth._tcp.", 				"Authentication Service",									"", 			false },
    108 	{ "_bootps._tcp.", 				"Bootstrap Protocol Server",								"", 			false },
    109 	{ "_bousg._tcp.", 				"Bag Of Unusual Strategy Games",							"", 			false },
    110 	{ "_browse._udp.", 				"DNS Service Discovery",									"", 			false },
    111 	{ "_cheat._tcp.", 				"The Cheat",												"", 			false },
    112 	{ "_chess._tcp", 				"Project Gridlock", 										"", 			false },
    113 	{ "_chfts._tcp", 				"Fluid Theme Server", 										"", 			false },
    114 	{ "_clipboard._tcp", 			"Clipboard Sharing", 										"", 			false },
    115 	{ "_contactserver._tcp.", 		"Now Up-to-Date & Contact",									"", 			false },
    116 	{ "_cvspserver._tcp", 			"CVS PServer", 												"", 			false },
    117 	{ "_cytv._tcp.", 				"CyTV Network streaming for Elgato EyeTV",					"", 			false },
    118 	{ "_daap._tcp.", 				"Digital Audio Access Protocol (iTunes)",					"daap://",		false },
    119 	{ "_distcc._tcp", 				"Distributed Compiler", 									"", 			false },
    120 	{ "_dns-sd._udp", 				"DNS Service Discovery", 									"", 			false },
    121 	{ "_dpap._tcp.", 				"Digital Picture Access Protocol (iPhoto)",					"", 			false },
    122 	{ "_earphoria._tcp.", 			"Earphoria",												"", 			false },
    123 	{ "_ecbyesfsgksc._tcp.", 		"Net Monitor Anti-Piracy Service",							"",				false },
    124 	{ "_eheap._tcp.", 				"Interactive Room Software",								"",				false },
    125 	{ "_embrace._tcp.", 			"DataEnvoy",												"",				false },
    126 	{ "_eppc._tcp.", 				"Remote AppleEvents", 										"eppc://", 		false },
    127 	{ "_exec._tcp.", 				"Remote Process Execution",									"",				false },
    128 	{ "_facespan._tcp.", 			"FaceSpan",													"",				false },
    129 	{ "_fjork._tcp.", 				"Fjork",													"",				false },
    130 	{ "_ftp._tcp.", 				"File Transfer (FTP)", 										"ftp://", 		false },
    131 	{ "_ftpcroco._tcp.", 			"Crocodile FTP Server",										"",				false },
    132 	{ "_gbs-smp._tcp.", 			"SnapMail",													"",				false },
    133 	{ "_gbs-stp._tcp.", 			"SnapTalk",													"",				false },
    134 	{ "_grillezvous._tcp.", 		"Roxio ToastAnywhere(tm) Recorder Sharing",					"",				false },
    135 	{ "_h323._tcp.", 				"H.323",													"",				false },
    136 	{ "_hotwayd._tcp", 				"Hotwayd", 													"", 			false },
    137 	{ "_http._tcp.", 				"Web Server (HTTP)", 										"http://", 		true  },
    138 	{ "_hydra._tcp", 				"SubEthaEdit", 												"", 			false },
    139 	{ "_ica-networking._tcp.", 		"Image Capture Networking",									"", 			false },
    140 	{ "_ichalkboard._tcp.", 		"iChalk",													"", 			false },
    141 	{ "_ichat._tcp.", 				"iChat",				 									"ichat://",		false },
    142 	{ "_iconquer._tcp.",	 		"iConquer",													"", 			false },
    143 	{ "_imap._tcp.", 				"Internet Message Access Protocol",							"",				false },
    144 	{ "_imidi._tcp.", 				"iMidi",													"",				false },
    145 	{ "_ipp._tcp.", 				"Printer (IPP)", 											"ipp://", 		false },
    146 	{ "_ishare._tcp.", 				"iShare",													"",				false },
    147 	{ "_isparx._tcp.", 				"iSparx",													"",				false },
    148 	{ "_istorm._tcp", 				"iStorm", 													"", 			false },
    149 	{ "_iwork._tcp.", 				"iWork Server",												"",				false },
    150 	{ "_liaison._tcp.", 			"Liaison",													"",				false },
    151 	{ "_login._tcp.", 				"Remote Login a la Telnet",									"",				false },
    152 	{ "_lontalk._tcp.", 			"LonTalk over IP (ANSI 852)",								"",				false },
    153 	{ "_lonworks._tcp.", 			"Echelon LNS Remote Client",								"",				false },
    154 	{ "_macfoh-remote._tcp.", 		"MacFOH Remote",											"",				false },
    155 	{ "_moneyworks._tcp.", 			"MoneyWorks",												"",				false },
    156 	{ "_mp3sushi._tcp", 			"MP3 Sushi", 												"", 			false },
    157 	{ "_mttp._tcp.", 				"MenuTunes Sharing",										"",				false },
    158 	{ "_ncbroadcast._tcp.", 		"Network Clipboard Broadcasts",								"",				false },
    159 	{ "_ncdirect._tcp.", 			"Network Clipboard Direct Transfers",						"",				false },
    160 	{ "_ncsyncserver._tcp.", 		"Network Clipboard Sync Server",							"",				false },
    161 	{ "_newton-dock._tcp.", 		"Escale",													"",				false },
    162 	{ "_nfs._tcp", 					"NFS", 														"", 			false },
    163 	{ "_nssocketport._tcp.", 		"DO over NSSocketPort",										"",				false },
    164 	{ "_omni-bookmark._tcp.", 		"OmniWeb",													"",				false },
    165 	{ "_openbase._tcp.", 			"OpenBase SQL",												"",				false },
    166 	{ "_p2pchat._tcp.", 			"Peer-to-Peer Chat",										"",				false },
    167 	{ "_pdl-datastream._tcp.", 		"Printer (PDL)", 											"pdl://", 		false },
    168 	{ "_poch._tcp.", 				"Parallel OperatiOn and Control Heuristic",					"",				false },
    169 	{ "_pop_2_ambrosia._tcp.",		"Pop-Pop",													"",				false },
    170 	{ "_pop3._tcp", 				"POP3 Server", 												"", 			false },
    171 	{ "_postgresql._tcp", 			"PostgreSQL Server", 										"", 			false },
    172 	{ "_presence._tcp", 			"iChat AV", 												"", 			false },
    173 	{ "_printer._tcp.", 			"Printer (LPR)", 											"lpr://", 		false },
    174 	{ "_ptp._tcp.", 				"Picture Transfer (PTP)", 									"ptp://", 		false },
    175 	{ "_register._tcp", 			"DNS Service Discovery", 									"", 			false },
    176 	{ "_rfb._tcp.", 				"Remote Frame Buffer",										"",				false },
    177 	{ "_riousbprint._tcp.", 		"Remote I/O USB Printer Protocol",							"",				false },
    178 	{ "_rtsp._tcp.", 				"Real Time Stream Control Protocol",						"",				false },
    179 	{ "_safarimenu._tcp", 			"Safari Menu", 												"", 			false },
    180 	{ "_scone._tcp", 				"Scone", 													"", 			false },
    181 	{ "_sdsharing._tcp.", 			"Speed Download",											"",				false },
    182 	{ "_seeCard._tcp.", 			"seeCard",													"",				false },
    183 	{ "_services._udp.", 			"DNS Service Discovery",									"",				false },
    184 	{ "_shell._tcp.", 				"like exec, but automatic authentication",					"",				false },
    185 	{ "_shout._tcp.", 				"Shout",													"",				false },
    186 	{ "_shoutcast._tcp", 			"Nicecast", 												"", 			false },
    187 	{ "_smb._tcp.", 				"Windows File Sharing (SMB)", 								"smb://", 		false },
    188 	{ "_soap._tcp.", 				"Simple Object Access Protocol", 							"", 			false },
    189 	{ "_spincrisis._tcp.", 			"Spin Crisis",												"",				false },
    190 	{ "_spl-itunes._tcp.", 			"launchTunes",												"",				false },
    191 	{ "_spr-itunes._tcp.", 			"netTunes",													"",				false },
    192 	{ "_ssh._tcp.", 				"Secure Shell (SSH)", 										"ssh://", 		false },
    193 	{ "_ssscreenshare._tcp", 		"Screen Sharing", 											"", 			false },
    194 	{ "_sge-exec._tcp", 			"Sun Grid Engine (Execution Host)", 						"", 			false },
    195 	{ "_sge-qmaster._tcp", 			"Sun Grid Engine (Master)", 								"", 			false },
    196 	{ "_stickynotes._tcp", 			"Sticky Notes", 											"", 			false },
    197 	{ "_strateges._tcp", 			"Strateges", 												"", 			false },
    198 	{ "_sxqdea._tcp", 				"Synchronize! Pro X", 										"", 			false },
    199 	{ "_sybase-tds._tcp", 			"Sybase Server", 											"", 			false },
    200 	{ "_tce._tcp", 					"Power Card", 												"", 			false },
    201 	{ "_teamlist._tcp", 			"ARTIS Team Task",											"", 			false },
    202 	{ "_teleport._tcp", 			"teleport",													"", 			false },
    203 	{ "_telnet._tcp.", 				"Telnet", 													"telnet://", 	false },
    204 	{ "_tftp._tcp.", 				"Trivial File Transfer (TFTP)", 							"tftp://", 		false },
    205 	{ "_tinavigator._tcp.", 		"TI Navigator", 											"", 			false },
    206 	{ "_tivo_servemedia._tcp", 		"TiVo",														"", 			false },
    207 	{ "_upnp._tcp.", 				"Universal Plug and Play", 									"", 			false },
    208 	{ "_utest._tcp.", 				"uTest", 													"", 			false },
    209 	{ "_vue4rendercow._tcp",		"VueProRenderCow",											"", 			false },
    210 	{ "_webdav._tcp.", 				"WebDAV", 													"webdav://",	false },
    211 	{ "_whamb._tcp.", 				"Whamb", 													"",				false },
    212 	{ "_workstation._tcp", 			"Macintosh Manager",										"", 			false },
    213 	{ "_ws._tcp", 					"Web Services",												"", 			false },
    214 	{ "_xserveraid._tcp.", 			"Xserve RAID",												"xsr://", 		false },
    215 	{ "_xsync._tcp.",	 			"Xserve RAID Synchronization",								"",		 		false },
    216 
    217 	{ "",	 						"",															"",		 		false },
    218 
    219 	// Unofficial and invalid service types that will be phased out:
    220 
    221 	{ "_clipboardsharing._tcp.",			"ClipboardSharing",									"",		 		false },
    222 	{ "_MacOSXDupSuppress._tcp.",			"Mac OS X Duplicate Suppression",					"",		 		false },
    223 	{ "_netmonitorserver._tcp.",			"Net Monitor Server",								"",		 		false },
    224 	{ "_networkclipboard._tcp.",			"Network Clipboard",								"",		 		false },
    225 	{ "_slimdevices_slimp3_cli._tcp.",		"SliMP3 Server Command-Line Interface",				"",		 		false },
    226 	{ "_slimdevices_slimp3_http._tcp.",		"SliMP3 Server Web Interface",						"",		 		false },
    227 	{ "_tieducationalhandhelddevice._tcp.",	"TI Connect Manager",								"",		 		false },
    228 	{ "_tivo_servemedia._tcp.",				"TiVo",												"",		 		false },
    229 
    230 	{ NULL,							NULL,														NULL,			false },
    231 };
    232 
    233 #if 0
    234 #pragma mark == Structures ==
    235 #endif
    236 
    237 //===========================================================================================================================
    238 //	Structures
    239 //===========================================================================================================================
    240 
    241 struct	DomainEventInfo
    242 {
    243 	DNSBrowserEventType		eventType;
    244 	CString					domain;
    245 	DNSNetworkAddress		ifIP;
    246 };
    247 
    248 struct	ServiceEventInfo
    249 {
    250 	DNSBrowserEventType		eventType;
    251 	std::string				name;
    252 	std::string				type;
    253 	std::string				domain;
    254 	DNSNetworkAddress		ifIP;
    255 };
    256 
    257 #if 0
    258 #pragma mark == Prototypes ==
    259 #endif
    260 
    261 //===========================================================================================================================
    262 //	Prototypes
    263 //===========================================================================================================================
    264 
    265 static void
    266 	BrowserCallBack(
    267 		void *					inContext,
    268 		DNSBrowserRef			inRef,
    269 		DNSStatus				inStatusCode,
    270 		const DNSBrowserEvent *	inEvent );
    271 
    272 static char *	DNSNetworkAddressToString( const DNSNetworkAddress *inAddr, char *outString );
    273 
    274 static DWORD	UTF8StringToStringObject( const char *inUTF8, CString &inObject );
    275 static DWORD	StringObjectToUTF8String( CString &inObject, std::string &outUTF8 );
    276 
    277 #if 0
    278 #pragma mark == Message Map ==
    279 #endif
    280 
    281 //===========================================================================================================================
    282 //	Message Map
    283 //===========================================================================================================================
    284 
    285 BEGIN_MESSAGE_MAP(ChooserDialog, CDialog)
    286 	//{{AFX_MSG_MAP(ChooserDialog)
    287 	ON_WM_SYSCOMMAND()
    288 	ON_NOTIFY(LVN_ITEMCHANGED, IDC_DOMAIN_LIST, OnDomainListChanged)
    289 	ON_NOTIFY(LVN_ITEMCHANGED, IDC_SERVICE_LIST, OnServiceListChanged)
    290 	ON_NOTIFY(LVN_ITEMCHANGED, IDC_CHOOSER_LIST, OnChooserListChanged)
    291 	ON_NOTIFY(NM_DBLCLK, IDC_CHOOSER_LIST, OnChooserListDoubleClick)
    292 	ON_COMMAND(ID_HELP_ABOUT, OnAbout)
    293 	ON_WM_INITMENUPOPUP()
    294 	ON_WM_ACTIVATE()
    295 	ON_COMMAND(ID_FILE_CLOSE, OnFileClose)
    296 	ON_COMMAND(ID_FILE_EXIT, OnExit)
    297 	ON_WM_CLOSE()
    298 	ON_WM_NCDESTROY()
    299 	//}}AFX_MSG_MAP
    300 	ON_MESSAGE( WM_USER_DOMAIN_ADD, OnDomainAdd )
    301 	ON_MESSAGE( WM_USER_DOMAIN_REMOVE, OnDomainRemove )
    302 	ON_MESSAGE( WM_USER_SERVICE_ADD, OnServiceAdd )
    303 	ON_MESSAGE( WM_USER_SERVICE_REMOVE, OnServiceRemove )
    304 	ON_MESSAGE( WM_USER_RESOLVE, OnResolve )
    305 END_MESSAGE_MAP()
    306 
    307 #if 0
    308 #pragma mark == Routines ==
    309 #endif
    310 
    311 //===========================================================================================================================
    312 //	ChooserDialog
    313 //===========================================================================================================================
    314 
    315 ChooserDialog::ChooserDialog( CWnd *inParent )
    316 	: CDialog( ChooserDialog::IDD, inParent)
    317 {
    318 	//{{AFX_DATA_INIT(ChooserDialog)
    319 		// Note: the ClassWizard will add member initialization here
    320 	//}}AFX_DATA_INIT
    321 
    322 	// Load menu accelerator table.
    323 
    324 	mMenuAcceleratorTable = ::LoadAccelerators( AfxGetInstanceHandle(), MAKEINTRESOURCE( IDR_CHOOSER_DIALOG_MENU_ACCELERATORS ) );
    325 	assert( mMenuAcceleratorTable );
    326 
    327 	mBrowser 			= NULL;
    328 	mIsServiceBrowsing	= false;
    329 }
    330 
    331 //===========================================================================================================================
    332 //	~ChooserDialog
    333 //===========================================================================================================================
    334 
    335 ChooserDialog::~ChooserDialog( void )
    336 {
    337 	if( mBrowser )
    338 	{
    339 		DNSStatus		err;
    340 
    341 		err = DNSBrowserRelease( mBrowser, 0 );
    342 		assert( err == kDNSNoErr );
    343 	}
    344 }
    345 
    346 //===========================================================================================================================
    347 //	DoDataExchange
    348 //===========================================================================================================================
    349 
    350 void ChooserDialog::DoDataExchange( CDataExchange *pDX )
    351 {
    352 	CDialog::DoDataExchange(pDX);
    353 
    354 	//{{AFX_DATA_MAP(ChooserDialog)
    355 	DDX_Control(pDX, IDC_SERVICE_LIST, mServiceList);
    356 	DDX_Control(pDX, IDC_DOMAIN_LIST, mDomainList);
    357 	DDX_Control(pDX, IDC_CHOOSER_LIST, mChooserList);
    358 	//}}AFX_DATA_MAP
    359 }
    360 
    361 //===========================================================================================================================
    362 //	OnInitDialog
    363 //===========================================================================================================================
    364 
    365 BOOL	ChooserDialog::OnInitDialog( void )
    366 {
    367 	HICON			icon;
    368 	BOOL			result;
    369 	CString			tempString;
    370 	DNSStatus		err;
    371 
    372 	// Initialize our parent.
    373 
    374 	CDialog::OnInitDialog();
    375 
    376 	// Set up the window icon.
    377 
    378 	icon = AfxGetApp()->LoadIcon( IDR_MAIN_ICON );
    379 	assert( icon );
    380 	if( icon )
    381 	{
    382 		SetIcon( icon, TRUE );		// Set big icon
    383 		SetIcon( icon, FALSE );		// Set small icon
    384 	}
    385 
    386 	// Set up the Domain List.
    387 
    388 	result = tempString.LoadString( IDS_CHOOSER_DOMAIN_COLUMN_NAME );
    389 	assert( result );
    390 	mDomainList.InsertColumn( 0, tempString, LVCFMT_LEFT, kDomainListDefaultDomainColumnWidth );
    391 
    392 	// Set up the Service List.
    393 
    394 	result = tempString.LoadString( IDS_CHOOSER_SERVICE_COLUMN_TYPE );
    395 	assert( result );
    396 	mServiceList.InsertColumn( 0, tempString, LVCFMT_LEFT, kServiceListDefaultServiceColumnTypeWidth );
    397 
    398 	result = tempString.LoadString( IDS_CHOOSER_SERVICE_COLUMN_DESC );
    399 	assert( result );
    400 	mServiceList.InsertColumn( 1, tempString, LVCFMT_LEFT, kServiceListDefaultServiceColumnDescWidth );
    401 
    402 	PopulateServicesList();
    403 
    404 	// Set up the Chooser List.
    405 
    406 	result = tempString.LoadString( IDS_CHOOSER_CHOOSER_NAME_COLUMN_NAME );
    407 	assert( result );
    408 	mChooserList.InsertColumn( 0, tempString, LVCFMT_LEFT, kChooserListDefaultNameColumnWidth );
    409 
    410 	result = tempString.LoadString( IDS_CHOOSER_CHOOSER_IP_COLUMN_NAME );
    411 	assert( result );
    412 	mChooserList.InsertColumn( 1, tempString, LVCFMT_LEFT, kChooserListDefaultIPColumnWidth );
    413 
    414 	// Set up the other controls.
    415 
    416 	UpdateInfoDisplay();
    417 
    418 	// Start browsing for domains.
    419 
    420 	err = DNSBrowserCreate( 0, BrowserCallBack, this, &mBrowser );
    421 	assert( err == kDNSNoErr );
    422 
    423 	err = DNSBrowserStartDomainSearch( mBrowser, 0 );
    424 	assert( err == kDNSNoErr );
    425 
    426 	return( true );
    427 }
    428 
    429 //===========================================================================================================================
    430 //	OnFileClose
    431 //===========================================================================================================================
    432 
    433 void ChooserDialog::OnFileClose()
    434 {
    435 	OnClose();
    436 }
    437 
    438 //===========================================================================================================================
    439 //	OnActivate
    440 //===========================================================================================================================
    441 
    442 void ChooserDialog::OnActivate( UINT nState, CWnd* pWndOther, BOOL bMinimized )
    443 {
    444 	// Always make the active window the "main" window so modal dialogs work better and the app quits after closing
    445 	// the last window.
    446 
    447 	gApp.m_pMainWnd = this;
    448 
    449 	CDialog::OnActivate(nState, pWndOther, bMinimized);
    450 }
    451 
    452 //===========================================================================================================================
    453 //	PostNcDestroy
    454 //===========================================================================================================================
    455 
    456 void	ChooserDialog::PostNcDestroy()
    457 {
    458 	// Call the base class to do the normal cleanup.
    459 
    460 	delete this;
    461 }
    462 
    463 //===========================================================================================================================
    464 //	PreTranslateMessage
    465 //===========================================================================================================================
    466 
    467 BOOL	ChooserDialog::PreTranslateMessage(MSG* pMsg)
    468 {
    469 	BOOL		result;
    470 
    471 	result = false;
    472 	assert( mMenuAcceleratorTable );
    473 	if( mMenuAcceleratorTable )
    474 	{
    475 		result = ::TranslateAccelerator( m_hWnd, mMenuAcceleratorTable, pMsg );
    476 	}
    477 	if( !result )
    478 	{
    479 		result = CDialog::PreTranslateMessage( pMsg );
    480 	}
    481 	return( result );
    482 }
    483 
    484 //===========================================================================================================================
    485 //	OnInitMenuPopup
    486 //===========================================================================================================================
    487 
    488 void	ChooserDialog::OnInitMenuPopup( CMenu *pPopupMenu, UINT nIndex, BOOL bSysMenu )
    489 {
    490 	CDialog::OnInitMenuPopup( pPopupMenu, nIndex, bSysMenu );
    491 
    492 	switch( nIndex )
    493 	{
    494 		case kChooserMenuIndexFile:
    495 			break;
    496 
    497 		case kChooserMenuIndexHelp:
    498 			break;
    499 
    500 		default:
    501 			break;
    502 	}
    503 }
    504 
    505 //===========================================================================================================================
    506 //	OnExit
    507 //===========================================================================================================================
    508 
    509 void ChooserDialog::OnExit()
    510 {
    511 	OnClose();
    512 }
    513 
    514 //===========================================================================================================================
    515 //	OnAbout
    516 //===========================================================================================================================
    517 
    518 void	ChooserDialog::OnAbout()
    519 {
    520 	AboutDialog		dialog;
    521 
    522 	dialog.DoModal();
    523 }
    524 
    525 //===========================================================================================================================
    526 //	OnSysCommand
    527 //===========================================================================================================================
    528 
    529 void	ChooserDialog::OnSysCommand( UINT inID, LPARAM inParam )
    530 {
    531 	CDialog::OnSysCommand( inID, inParam );
    532 }
    533 
    534 //===========================================================================================================================
    535 //	OnClose
    536 //===========================================================================================================================
    537 
    538 void ChooserDialog::OnClose()
    539 {
    540 	StopBrowsing();
    541 
    542 	gApp.m_pMainWnd = this;
    543 	DestroyWindow();
    544 }
    545 
    546 //===========================================================================================================================
    547 //	OnNcDestroy
    548 //===========================================================================================================================
    549 
    550 void ChooserDialog::OnNcDestroy()
    551 {
    552 	gApp.m_pMainWnd = this;
    553 
    554 	CDialog::OnNcDestroy();
    555 }
    556 
    557 //===========================================================================================================================
    558 //	OnDomainListChanged
    559 //===========================================================================================================================
    560 
    561 void	ChooserDialog::OnDomainListChanged( NMHDR *pNMHDR, LRESULT *pResult )
    562 {
    563 	UNUSED_ALWAYS( pNMHDR );
    564 
    565 	// Domain list changes have similar effects to service list changes so reuse that code path by calling it here.
    566 
    567 	OnServiceListChanged( NULL, NULL );
    568 
    569 	*pResult = 0;
    570 }
    571 
    572 //===========================================================================================================================
    573 //	OnServiceListChanged
    574 //===========================================================================================================================
    575 
    576 void	ChooserDialog::OnServiceListChanged( NMHDR *pNMHDR, LRESULT *pResult )
    577 {
    578 	int				selectedType;
    579 	int				selectedDomain;
    580 
    581 	UNUSED_ALWAYS( pNMHDR );
    582 
    583 	// Stop any existing service search.
    584 
    585 	StopBrowsing();
    586 
    587 	// If a domain and service type are selected, start searching for the service type on the domain.
    588 
    589 	selectedType 	= mServiceList.GetNextItem( -1, LVNI_SELECTED );
    590 	selectedDomain 	= mDomainList.GetNextItem( -1, LVNI_SELECTED );
    591 
    592 	if( ( selectedType >= 0 ) && ( selectedDomain >= 0 ) )
    593 	{
    594 		CString				s;
    595 		std::string			utf8;
    596 		const char *		type;
    597 
    598 		s = mDomainList.GetItemText( selectedDomain, 0 );
    599 		StringObjectToUTF8String( s, utf8 );
    600 		type = mServiceTypes[ selectedType ].serviceType.c_str();
    601 		if( *type != '\0' )
    602 		{
    603 			StartBrowsing( type, utf8.c_str() );
    604 		}
    605 	}
    606 
    607 	if( pResult )
    608 	{
    609 		*pResult = 0;
    610 	}
    611 }
    612 
    613 //===========================================================================================================================
    614 //	OnChooserListChanged
    615 //===========================================================================================================================
    616 
    617 void	ChooserDialog::OnChooserListChanged( NMHDR *pNMHDR, LRESULT *pResult )
    618 {
    619 	UNUSED_ALWAYS( pNMHDR );
    620 
    621 	UpdateInfoDisplay();
    622 	*pResult = 0;
    623 }
    624 
    625 //===========================================================================================================================
    626 //	OnChooserListDoubleClick
    627 //===========================================================================================================================
    628 
    629 void	ChooserDialog::OnChooserListDoubleClick( NMHDR *pNMHDR, LRESULT *pResult )
    630 {
    631 	int		selectedItem;
    632 
    633 	UNUSED_ALWAYS( pNMHDR );
    634 
    635 	// Display the service instance if it is selected. Otherwise, clear all the info.
    636 
    637 	selectedItem = mChooserList.GetNextItem( -1, LVNI_SELECTED );
    638 	if( selectedItem >= 0 )
    639 	{
    640 		ServiceInstanceInfo *			p;
    641 		CString							url;
    642 		const KnownServiceEntry *		service;
    643 
    644 		assert( selectedItem < (int) mServiceInstances.size() );
    645 		p = &mServiceInstances[ selectedItem ];
    646 
    647 		// Search for a known service type entry that matches.
    648 
    649 		for( service = kKnownServiceTable; service->serviceType; ++service )
    650 		{
    651 			if( p->type == service->serviceType )
    652 			{
    653 				break;
    654 			}
    655 		}
    656 		if( service->serviceType )
    657 		{
    658 			const char *		text;
    659 
    660 			// Create a URL representing the service instance.
    661 
    662 			if( strcmp( service->serviceType, "_smb._tcp." ) == 0 )
    663 			{
    664 				// Special case for SMB (no port number).
    665 
    666 				url.Format( TEXT( "%s%s/" ), service->urlScheme, (const char *) p->ip.c_str() );
    667 			}
    668 			else if( strcmp( service->serviceType, "_ftp._tcp." ) == 0 )
    669 			{
    670 				// Special case for FTP to get login info.
    671 
    672 				LoginDialog		dialog;
    673 				CString			username;
    674 				CString			password;
    675 
    676 				if( !dialog.GetLogin( username, password ) )
    677 				{
    678 					goto exit;
    679 				}
    680 
    681 				// Build URL in the following format:
    682 				//
    683 				// ftp://[username[:password]@]<ip>
    684 
    685 				url += service->urlScheme;
    686 				if( username.GetLength() > 0 )
    687 				{
    688 					url += username;
    689 					if( password.GetLength() > 0 )
    690 					{
    691 						url += ':';
    692 						url += password;
    693 					}
    694 					url += '@';
    695 				}
    696 				url += p->ip.c_str();
    697 			}
    698 			else if( strcmp( service->serviceType, "_http._tcp." ) == 0 )
    699 			{
    700 				// Special case for HTTP to exclude "path=" if present.
    701 
    702 				text = service->useText ? p->text.c_str() : "";
    703 				if( strncmp( text, "path=", 5 ) == 0 )
    704 				{
    705 					text += 5;
    706 				}
    707 				if( *text != '/' )
    708 				{
    709 					url.Format( TEXT( "%s%s/%s" ), service->urlScheme, (const char *) p->ip.c_str(), text );
    710 				}
    711 				else
    712 				{
    713 					url.Format( TEXT( "%s%s%s" ), service->urlScheme, (const char *) p->ip.c_str(), text );
    714 				}
    715 			}
    716 			else
    717 			{
    718 				text = service->useText ? p->text.c_str() : "";
    719 				url.Format( TEXT( "%s%s/%s" ), service->urlScheme, (const char *) p->ip.c_str(), text );
    720 			}
    721 
    722 			// Let the system open the URL in the correct app.
    723 
    724 			{
    725 				CWaitCursor		waitCursor;
    726 
    727 				ShellExecute( NULL, TEXT( "open" ), url, TEXT( "" ), TEXT( "c:\\" ), SW_SHOWNORMAL );
    728 			}
    729 		}
    730 	}
    731 
    732 exit:
    733 	*pResult = 0;
    734 }
    735 
    736 //===========================================================================================================================
    737 //	OnCancel
    738 //===========================================================================================================================
    739 
    740 void ChooserDialog::OnCancel()
    741 {
    742 	// Do nothing.
    743 }
    744 
    745 //===========================================================================================================================
    746 //	PopulateServicesList
    747 //===========================================================================================================================
    748 
    749 void	ChooserDialog::PopulateServicesList( void )
    750 {
    751 	ServiceTypeVector::iterator		i;
    752 	CString							type;
    753 	CString							desc;
    754 	std::string						tmp;
    755 
    756 	// Add a fixed list of known services.
    757 
    758 	if( mServiceTypes.empty() )
    759 	{
    760 		const KnownServiceEntry *		service;
    761 
    762 		for( service = kKnownServiceTable; service->serviceType; ++service )
    763 		{
    764 			ServiceTypeInfo		info;
    765 
    766 			info.serviceType 	= service->serviceType;
    767 			info.description 	= service->description;
    768 			info.urlScheme 		= service->urlScheme;
    769 			mServiceTypes.push_back( info );
    770 		}
    771 	}
    772 
    773 	// Add each service to the list.
    774 
    775 	for( i = mServiceTypes.begin(); i != mServiceTypes.end(); ++i )
    776 	{
    777 		const char *		p;
    778 		const char *		q;
    779 
    780 		p  = ( *i ).serviceType.c_str();
    781 		if( *p == '_' ) ++p;							// Skip leading '_'.
    782 		q  = strchr( p, '.' );							// Find first '.'.
    783 		if( q )	tmp.assign( p, (size_t)( q - p ) );		// Use only up to the first '.'.
    784 		else	tmp.assign( p );						// No '.' so use the entire string.
    785 		UTF8StringToStringObject( tmp.c_str(), type );
    786 		UTF8StringToStringObject( ( *i ).description.c_str(), desc );
    787 
    788 		int		n;
    789 
    790 		n = mServiceList.GetItemCount();
    791 		mServiceList.InsertItem( n, type );
    792 		mServiceList.SetItemText( n, 1, desc );
    793 	}
    794 
    795 	// Select the first service type by default.
    796 
    797 	if( !mServiceTypes.empty() )
    798 	{
    799 		mServiceList.SetItemState( 0, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED );
    800 	}
    801 }
    802 
    803 //===========================================================================================================================
    804 //	UpdateInfoDisplay
    805 //===========================================================================================================================
    806 
    807 void	ChooserDialog::UpdateInfoDisplay( void )
    808 {
    809 	int							selectedItem;
    810 	std::string					name;
    811 	CString						s;
    812 	std::string					ip;
    813 	std::string					ifIP;
    814 	std::string					text;
    815 	std::string					textNewLines;
    816 	std::string					hostName;
    817 	CWnd *						item;
    818 	std::string::iterator		i;
    819 
    820 	// Display the service instance if it is selected. Otherwise, clear all the info.
    821 
    822 	selectedItem = mChooserList.GetNextItem( -1, LVNI_SELECTED );
    823 	if( selectedItem >= 0 )
    824 	{
    825 		ServiceInstanceInfo *		p;
    826 
    827 		assert( selectedItem < (int) mServiceInstances.size() );
    828 		p = &mServiceInstances[ selectedItem ];
    829 
    830 		name 		= p->name;
    831 		ip 			= p->ip;
    832 		ifIP 		= p->ifIP;
    833 		text 		= p->text;
    834 		hostName	= p->hostName;
    835 
    836 		// Sync up the list items with the actual data (IP address may change).
    837 
    838 		UTF8StringToStringObject( ip.c_str(), s );
    839 		mChooserList.SetItemText( selectedItem, 1, s );
    840 	}
    841 
    842 	// Name
    843 
    844 	item = (CWnd *) this->GetDlgItem( IDC_INFO_NAME_TEXT );
    845 	assert( item );
    846 	UTF8StringToStringObject( name.c_str(), s );
    847 	item->SetWindowText( s );
    848 
    849 	// IP
    850 
    851 	item = (CWnd *) this->GetDlgItem( IDC_INFO_IP_TEXT );
    852 	assert( item );
    853 	UTF8StringToStringObject( ip.c_str(), s );
    854 	item->SetWindowText( s );
    855 
    856 	// Interface
    857 
    858 	item = (CWnd *) this->GetDlgItem( IDC_INFO_INTERFACE_TEXT );
    859 	assert( item );
    860 	UTF8StringToStringObject( ifIP.c_str(), s );
    861 	item->SetWindowText( s );
    862 
    863 
    864 	item = (CWnd *) this->GetDlgItem( IDC_INFO_HOST_NAME_TEXT );
    865 	assert( item );
    866 	UTF8StringToStringObject( hostName.c_str(), s );
    867 	item->SetWindowText( s );
    868 
    869 	// Text
    870 
    871 	item = (CWnd *) this->GetDlgItem( IDC_INFO_TEXT_TEXT );
    872 	assert( item );
    873 	for( i = text.begin(); i != text.end(); ++i )
    874 	{
    875 		if( *i == '\1' )
    876 		{
    877 			textNewLines += "\r\n";
    878 		}
    879 		else
    880 		{
    881 			textNewLines += *i;
    882 		}
    883 	}
    884 	UTF8StringToStringObject( textNewLines.c_str(), s );
    885 	item->SetWindowText( s );
    886 }
    887 
    888 #if 0
    889 #pragma mark -
    890 #endif
    891 
    892 //===========================================================================================================================
    893 //	OnDomainAdd
    894 //===========================================================================================================================
    895 
    896 LONG	ChooserDialog::OnDomainAdd( WPARAM inWParam, LPARAM inLParam )
    897 {
    898 	DomainEventInfo *						p;
    899 	std::auto_ptr < DomainEventInfo >		pAutoPtr;
    900 	int										n;
    901 	int										i;
    902 	CString									domain;
    903 	CString									s;
    904 	bool									found;
    905 
    906 	UNUSED_ALWAYS( inWParam );
    907 
    908 	assert( inLParam );
    909 	p = reinterpret_cast <DomainEventInfo *> ( inLParam );
    910 	pAutoPtr.reset( p );
    911 
    912 	// Search to see if we already know about this domain. If not, add it to the list.
    913 
    914 	found = false;
    915 	domain = p->domain;
    916 	n = mDomainList.GetItemCount();
    917 	for( i = 0; i < n; ++i )
    918 	{
    919 		s = mDomainList.GetItemText( i, 0 );
    920 		if( s == domain )
    921 		{
    922 			found = true;
    923 			break;
    924 		}
    925 	}
    926 	if( !found )
    927 	{
    928 		int		selectedItem;
    929 
    930 		mDomainList.InsertItem( n, domain );
    931 
    932 		// If no domains are selected and the domain being added is a default domain, select it.
    933 
    934 		selectedItem = mDomainList.GetNextItem( -1, LVNI_SELECTED );
    935 		if( ( selectedItem < 0 ) && ( p->eventType == kDNSBrowserEventTypeAddDefaultDomain ) )
    936 		{
    937 			mDomainList.SetItemState( n, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED );
    938 		}
    939 	}
    940 	return( 0 );
    941 }
    942 
    943 //===========================================================================================================================
    944 //	OnDomainRemove
    945 //===========================================================================================================================
    946 
    947 LONG	ChooserDialog::OnDomainRemove( WPARAM inWParam, LPARAM inLParam )
    948 {
    949 	DomainEventInfo *						p;
    950 	std::auto_ptr < DomainEventInfo >		pAutoPtr;
    951 	int										n;
    952 	int										i;
    953 	CString									domain;
    954 	CString									s;
    955 	bool									found;
    956 
    957 	UNUSED_ALWAYS( inWParam );
    958 
    959 	assert( inLParam );
    960 	p = reinterpret_cast <DomainEventInfo *> ( inLParam );
    961 	pAutoPtr.reset( p );
    962 
    963 	// Search to see if we know about this domain. If so, remove it from the list.
    964 
    965 	found = false;
    966 	domain = p->domain;
    967 	n = mDomainList.GetItemCount();
    968 	for( i = 0; i < n; ++i )
    969 	{
    970 		s = mDomainList.GetItemText( i, 0 );
    971 		if( s == domain )
    972 		{
    973 			found = true;
    974 			break;
    975 		}
    976 	}
    977 	if( found )
    978 	{
    979 		mDomainList.DeleteItem( i );
    980 	}
    981 	return( 0 );
    982 }
    983 
    984 //===========================================================================================================================
    985 //	OnServiceAdd
    986 //===========================================================================================================================
    987 
    988 LONG	ChooserDialog::OnServiceAdd( WPARAM inWParam, LPARAM inLParam )
    989 {
    990 	ServiceEventInfo *						p;
    991 	std::auto_ptr < ServiceEventInfo >		pAutoPtr;
    992 
    993 	UNUSED_ALWAYS( inWParam );
    994 
    995 	assert( inLParam );
    996 	p = reinterpret_cast <ServiceEventInfo *> ( inLParam );
    997 	pAutoPtr.reset( p );
    998 
    999 	return( 0 );
   1000 }
   1001 
   1002 //===========================================================================================================================
   1003 //	OnServiceRemove
   1004 //===========================================================================================================================
   1005 
   1006 LONG	ChooserDialog::OnServiceRemove( WPARAM inWParam, LPARAM inLParam )
   1007 {
   1008 	ServiceEventInfo *						p;
   1009 	std::auto_ptr < ServiceEventInfo >		pAutoPtr;
   1010 	bool									found;
   1011 	int										n;
   1012 	int										i;
   1013 
   1014 	UNUSED_ALWAYS( inWParam );
   1015 
   1016 	assert( inLParam );
   1017 	p = reinterpret_cast <ServiceEventInfo *> ( inLParam );
   1018 	pAutoPtr.reset( p );
   1019 
   1020 	// Search to see if we know about this service instance. If so, remove it from the list.
   1021 
   1022 	found = false;
   1023 	n = (int) mServiceInstances.size();
   1024 	for( i = 0; i < n; ++i )
   1025 	{
   1026 		ServiceInstanceInfo *		q;
   1027 
   1028 		// If the name, type, domain, and interface match, treat it as the same service instance.
   1029 
   1030 		q = &mServiceInstances[ i ];
   1031 		if( ( p->name 	== q->name ) 	&&
   1032 			( p->type 	== q->type ) 	&&
   1033 			( p->domain	== q->domain ) )
   1034 		{
   1035 			found = true;
   1036 			break;
   1037 		}
   1038 	}
   1039 	if( found )
   1040 	{
   1041 		mChooserList.DeleteItem( i );
   1042 		assert( i < (int) mServiceInstances.size() );
   1043 		mServiceInstances.erase( mServiceInstances.begin() + i );
   1044 	}
   1045 	return( 0 );
   1046 }
   1047 
   1048 //===========================================================================================================================
   1049 //	OnResolve
   1050 //===========================================================================================================================
   1051 
   1052 LONG	ChooserDialog::OnResolve( WPARAM inWParam, LPARAM inLParam )
   1053 {
   1054 	ServiceInstanceInfo *						p;
   1055 	std::auto_ptr < ServiceInstanceInfo >		pAutoPtr;
   1056 	int											selectedType;
   1057 	int											n;
   1058 	int											i;
   1059 	bool										found;
   1060 
   1061 	UNUSED_ALWAYS( inWParam );
   1062 
   1063 	assert( inLParam );
   1064 	p = reinterpret_cast <ServiceInstanceInfo *> ( inLParam );
   1065 	pAutoPtr.reset( p );
   1066 
   1067 	// Make sure it is for an item of the correct type. This handles any resolves that may have been queued up.
   1068 
   1069 	selectedType = mServiceList.GetNextItem( -1, LVNI_SELECTED );
   1070 	assert( selectedType >= 0 );
   1071 	if( selectedType >= 0 )
   1072 	{
   1073 		assert( selectedType <= (int) mServiceTypes.size() );
   1074 		if( p->type != mServiceTypes[ selectedType ].serviceType )
   1075 		{
   1076 			goto exit;
   1077 		}
   1078 	}
   1079 
   1080 	// Search to see if we know about this service instance. If so, update its info. Otherwise, add it to the list.
   1081 
   1082 	found = false;
   1083 	n = (int) mServiceInstances.size();
   1084 	for( i = 0; i < n; ++i )
   1085 	{
   1086 		ServiceInstanceInfo *		q;
   1087 
   1088 		// If the name, type, domain, and interface matches, treat it as the same service instance.
   1089 
   1090 		q = &mServiceInstances[ i ];
   1091 		if( ( p->name 	== q->name ) 	&&
   1092 			( p->type 	== q->type ) 	&&
   1093 			( p->domain	== q->domain ) 	&&
   1094 			( p->ifIP 	== q->ifIP ) )
   1095 		{
   1096 			found = true;
   1097 			break;
   1098 		}
   1099 	}
   1100 	if( found )
   1101 	{
   1102 		mServiceInstances[ i ] = *p;
   1103 	}
   1104 	else
   1105 	{
   1106 		CString		s;
   1107 
   1108 		mServiceInstances.push_back( *p );
   1109 		UTF8StringToStringObject( p->name.c_str(), s );
   1110 		mChooserList.InsertItem( n, s );
   1111 
   1112 		UTF8StringToStringObject( p->ip.c_str(), s );
   1113 		mChooserList.SetItemText( n, 1, s );
   1114 
   1115 		// If this is the only item, select it.
   1116 
   1117 		if( n == 0 )
   1118 		{
   1119 			mChooserList.SetItemState( n, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED );
   1120 		}
   1121 	}
   1122 	UpdateInfoDisplay();
   1123 
   1124 exit:
   1125 	return( 0 );
   1126 }
   1127 
   1128 //===========================================================================================================================
   1129 //	StartBrowsing
   1130 //===========================================================================================================================
   1131 
   1132 void	ChooserDialog::StartBrowsing( const char *inType, const char *inDomain )
   1133 {
   1134 	DNSStatus		err;
   1135 
   1136 	assert( mServiceInstances.empty() );
   1137 	assert( mChooserList.GetItemCount() == 0 );
   1138 	assert( !mIsServiceBrowsing );
   1139 
   1140 	mChooserList.DeleteAllItems();
   1141 	mServiceInstances.clear();
   1142 
   1143 	mIsServiceBrowsing = true;
   1144 	err = DNSBrowserStartServiceSearch( mBrowser, kDNSBrowserFlagAutoResolve, inType, inDomain );
   1145 	assert( err == kDNSNoErr );
   1146 }
   1147 
   1148 //===========================================================================================================================
   1149 //	StopBrowsing
   1150 //===========================================================================================================================
   1151 
   1152 void	ChooserDialog::StopBrowsing( void )
   1153 {
   1154 	// If searching, stop.
   1155 
   1156 	if( mIsServiceBrowsing )
   1157 	{
   1158 		DNSStatus		err;
   1159 
   1160 		mIsServiceBrowsing = false;
   1161 		err = DNSBrowserStopServiceSearch( mBrowser, 0 );
   1162 		assert( err == kDNSNoErr );
   1163 	}
   1164 
   1165 	// Remove all service instances.
   1166 
   1167 	mChooserList.DeleteAllItems();
   1168 	assert( mChooserList.GetItemCount() == 0 );
   1169 	mServiceInstances.clear();
   1170 	assert( mServiceInstances.empty() );
   1171 	UpdateInfoDisplay();
   1172 }
   1173 
   1174 #if 0
   1175 #pragma mark -
   1176 #endif
   1177 
   1178 //===========================================================================================================================
   1179 //	BrowserCallBack
   1180 //===========================================================================================================================
   1181 
   1182 static void
   1183 	BrowserCallBack(
   1184 		void *					inContext,
   1185 		DNSBrowserRef			inRef,
   1186 		DNSStatus				inStatusCode,
   1187 		const DNSBrowserEvent *	inEvent )
   1188 {
   1189 	ChooserDialog *		dialog;
   1190 	UINT 				message;
   1191 	BOOL				posted;
   1192 
   1193 	UNUSED_ALWAYS( inStatusCode );
   1194 	UNUSED_ALWAYS( inRef );
   1195 
   1196 	// Check parameters.
   1197 
   1198 	assert( inContext );
   1199 	dialog = reinterpret_cast <ChooserDialog *> ( inContext );
   1200 
   1201 	try
   1202 	{
   1203 		switch( inEvent->type )
   1204 		{
   1205 			case kDNSBrowserEventTypeRelease:
   1206 				break;
   1207 
   1208 			// Domains
   1209 
   1210 			case kDNSBrowserEventTypeAddDomain:
   1211 			case kDNSBrowserEventTypeAddDefaultDomain:
   1212 			case kDNSBrowserEventTypeRemoveDomain:
   1213 			{
   1214 				DomainEventInfo *						domain;
   1215 				std::auto_ptr < DomainEventInfo >		domainAutoPtr;
   1216 
   1217 				domain = new DomainEventInfo;
   1218 				domainAutoPtr.reset( domain );
   1219 
   1220 				domain->eventType 	= inEvent->type;
   1221 				domain->domain 		= inEvent->data.addDomain.domain;
   1222 				domain->ifIP		= inEvent->data.addDomain.interfaceIP;
   1223 
   1224 				message = ( inEvent->type == kDNSBrowserEventTypeRemoveDomain ) ? WM_USER_DOMAIN_REMOVE : WM_USER_DOMAIN_ADD;
   1225 				posted = ::PostMessage( dialog->GetSafeHwnd(), message, 0, (LPARAM) domain );
   1226 				assert( posted );
   1227 				if( posted )
   1228 				{
   1229 					domainAutoPtr.release();
   1230 				}
   1231 				break;
   1232 			}
   1233 
   1234 			// Services
   1235 
   1236 			case kDNSBrowserEventTypeAddService:
   1237 			case kDNSBrowserEventTypeRemoveService:
   1238 			{
   1239 				ServiceEventInfo *						service;
   1240 				std::auto_ptr < ServiceEventInfo >		serviceAutoPtr;
   1241 
   1242 				service = new ServiceEventInfo;
   1243 				serviceAutoPtr.reset( service );
   1244 
   1245 				service->eventType 	= inEvent->type;
   1246 				service->name 		= inEvent->data.addService.name;
   1247 				service->type 		= inEvent->data.addService.type;
   1248 				service->domain		= inEvent->data.addService.domain;
   1249 				service->ifIP		= inEvent->data.addService.interfaceIP;
   1250 
   1251 				message = ( inEvent->type == kDNSBrowserEventTypeAddService ) ? WM_USER_SERVICE_ADD : WM_USER_SERVICE_REMOVE;
   1252 				posted = ::PostMessage( dialog->GetSafeHwnd(), message, 0, (LPARAM) service );
   1253 				assert( posted );
   1254 				if( posted )
   1255 				{
   1256 					serviceAutoPtr.release();
   1257 				}
   1258 				break;
   1259 			}
   1260 
   1261 			// Resolves
   1262 
   1263 			case kDNSBrowserEventTypeResolved:
   1264 				if( inEvent->data.resolved->address.addressType == kDNSNetworkAddressTypeIPv4  )
   1265 				{
   1266 					ServiceInstanceInfo *						serviceInstance;
   1267 					std::auto_ptr < ServiceInstanceInfo >		serviceInstanceAutoPtr;
   1268 					char										s[ 32 ];
   1269 
   1270 					serviceInstance = new ServiceInstanceInfo;
   1271 					serviceInstanceAutoPtr.reset( serviceInstance );
   1272 
   1273 					serviceInstance->name 		= inEvent->data.resolved->name;
   1274 					serviceInstance->type 		= inEvent->data.resolved->type;
   1275 					serviceInstance->domain		= inEvent->data.resolved->domain;
   1276 					serviceInstance->ip			= DNSNetworkAddressToString( &inEvent->data.resolved->address, s );
   1277 					serviceInstance->ifIP		= DNSNetworkAddressToString( &inEvent->data.resolved->interfaceIP, s );
   1278 					serviceInstance->text 		= inEvent->data.resolved->textRecord;
   1279 					serviceInstance->hostName	= inEvent->data.resolved->hostName;
   1280 
   1281 					posted = ::PostMessage( dialog->GetSafeHwnd(), WM_USER_RESOLVE, 0, (LPARAM) serviceInstance );
   1282 					assert( posted );
   1283 					if( posted )
   1284 					{
   1285 						serviceInstanceAutoPtr.release();
   1286 					}
   1287 				}
   1288 				break;
   1289 
   1290 			default:
   1291 				break;
   1292 		}
   1293 	}
   1294 	catch( ... )
   1295 	{
   1296 		// Don't let exceptions escape.
   1297 	}
   1298 }
   1299 
   1300 //===========================================================================================================================
   1301 //	DNSNetworkAddressToString
   1302 //
   1303 //	Note: Currently only supports IPv4 network addresses.
   1304 //===========================================================================================================================
   1305 
   1306 static char *	DNSNetworkAddressToString( const DNSNetworkAddress *inAddr, char *outString )
   1307 {
   1308 	const DNSUInt8 *		p;
   1309 	DNSUInt16				port;
   1310 
   1311 	p = inAddr->u.ipv4.addr.v8;
   1312 	port = ntohs( inAddr->u.ipv4.port.v16 );
   1313 	if( port != kDNSPortInvalid )
   1314 	{
   1315 		sprintf( outString, "%u.%u.%u.%u:%u", p[ 0 ], p[ 1 ], p[ 2 ], p[ 3 ], port );
   1316 	}
   1317 	else
   1318 	{
   1319 		sprintf( outString, "%u.%u.%u.%u", p[ 0 ], p[ 1 ], p[ 2 ], p[ 3 ] );
   1320 	}
   1321 	return( outString );
   1322 }
   1323 
   1324 //===========================================================================================================================
   1325 //	UTF8StringToStringObject
   1326 //===========================================================================================================================
   1327 
   1328 static DWORD	UTF8StringToStringObject( const char *inUTF8, CString &inObject )
   1329 {
   1330 	DWORD		err;
   1331 	int			n;
   1332 	BSTR		unicode;
   1333 
   1334 	unicode = NULL;
   1335 
   1336 	n = MultiByteToWideChar( CP_UTF8, 0, inUTF8, -1, NULL, 0 );
   1337 	if( n > 0 )
   1338 	{
   1339 		unicode = (BSTR) malloc( (size_t)( n * sizeof( wchar_t ) ) );
   1340 		if( !unicode )
   1341 		{
   1342 			err = ERROR_INSUFFICIENT_BUFFER;
   1343 			goto exit;
   1344 		}
   1345 
   1346 		n = MultiByteToWideChar( CP_UTF8, 0, inUTF8, -1, unicode, n );
   1347 		try
   1348 		{
   1349 			inObject = unicode;
   1350 		}
   1351 		catch( ... )
   1352 		{
   1353 			err = ERROR_NO_UNICODE_TRANSLATION;
   1354 			goto exit;
   1355 		}
   1356 	}
   1357 	else
   1358 	{
   1359 		inObject = "";
   1360 	}
   1361 	err = 0;
   1362 
   1363 exit:
   1364 	if( unicode )
   1365 	{
   1366 		free( unicode );
   1367 	}
   1368 	return( err );
   1369 }
   1370 
   1371 //===========================================================================================================================
   1372 //	StringObjectToUTF8String
   1373 //===========================================================================================================================
   1374 
   1375 static DWORD	StringObjectToUTF8String( CString &inObject, std::string &outUTF8 )
   1376 {
   1377 	DWORD		err;
   1378 	BSTR		unicode;
   1379 	int			nUnicode;
   1380 	int			n;
   1381 	char *		utf8;
   1382 
   1383 	unicode = NULL;
   1384 	utf8	= NULL;
   1385 
   1386 	nUnicode = inObject.GetLength();
   1387 	if( nUnicode > 0 )
   1388 	{
   1389 		unicode = inObject.AllocSysString();
   1390 		n = WideCharToMultiByte( CP_UTF8, 0, unicode, nUnicode, NULL, 0, NULL, NULL );
   1391 		assert( n > 0 );
   1392 
   1393 		utf8 = (char *) malloc( (size_t) n );
   1394 		assert( utf8 );
   1395 		if( !utf8 ) { err = ERROR_INSUFFICIENT_BUFFER; goto exit; }
   1396 
   1397 		n = WideCharToMultiByte( CP_UTF8, 0, unicode, nUnicode, utf8, n, NULL, NULL );
   1398 		assert( n > 0 );
   1399 
   1400 		try
   1401 		{
   1402 			outUTF8.assign( utf8, n );
   1403 		}
   1404 		catch( ... )
   1405 		{
   1406 			err = ERROR_NO_UNICODE_TRANSLATION;
   1407 			goto exit;
   1408 		}
   1409 	}
   1410 	else
   1411 	{
   1412 		outUTF8.clear();
   1413 	}
   1414 	err = 0;
   1415 
   1416 exit:
   1417 	if( unicode )
   1418 	{
   1419 		SysFreeString( unicode );
   1420 	}
   1421 	if( utf8 )
   1422 	{
   1423 		free( utf8 );
   1424 	}
   1425 	return( err );
   1426 }
   1427