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 "stdafx.h" 19 20#include "Application.h" 21 22#include "DNSServices.h" 23 24#include "BrowserDialog.h" 25 26#ifdef _DEBUG 27#define new DEBUG_NEW 28#undef THIS_FILE 29static char THIS_FILE[] = __FILE__; 30#endif 31 32//=========================================================================================================================== 33// Constants 34//=========================================================================================================================== 35 36#define WM_USER_SERVICE_ADD ( WM_USER + 0x100 ) 37#define WM_USER_SERVICE_REMOVE ( WM_USER + 0x101 ) 38 39//=========================================================================================================================== 40// Message Map 41//=========================================================================================================================== 42 43BEGIN_MESSAGE_MAP(BrowserDialog, CDialog) 44 //{{AFX_MSG_MAP(BrowserDialog) 45 ON_NOTIFY(NM_CLICK, IDC_BROWSE_LIST, OnBrowserListDoubleClick) 46 ON_MESSAGE( WM_USER_SERVICE_ADD, OnServiceAdd ) 47 ON_MESSAGE( WM_USER_SERVICE_REMOVE, OnServiceRemove ) 48 //}}AFX_MSG_MAP 49END_MESSAGE_MAP() 50 51static DWORD UTF8StringToStringObject( const char *inUTF8, CString &inObject ); 52 53//=========================================================================================================================== 54// BrowserDialog 55//=========================================================================================================================== 56 57BrowserDialog::BrowserDialog( CWnd *inParent ) 58 : CDialog( BrowserDialog::IDD, inParent ) 59{ 60 //{{AFX_DATA_INIT(BrowserDialog) 61 // Note: the ClassWizard will add member initialization here 62 //}}AFX_DATA_INIT 63 64 // Note that LoadIcon does not require a subsequent DestroyIcon in Win32. 65 66 mIcon = AfxGetApp()->LoadIcon( IDR_MAINFRAME ); 67 ASSERT( mIcon ); 68} 69 70//=========================================================================================================================== 71// DoDataExchange 72//=========================================================================================================================== 73 74void BrowserDialog::DoDataExchange( CDataExchange *pDX ) 75{ 76 CDialog::DoDataExchange(pDX); 77 //{{AFX_DATA_MAP(BrowserDialog) 78 DDX_Control(pDX, IDC_BROWSE_LIST, mBrowserList); 79 //}}AFX_DATA_MAP 80} 81 82//=========================================================================================================================== 83// OnInitDialog 84//=========================================================================================================================== 85 86BOOL BrowserDialog::OnInitDialog() 87{ 88 CString s; 89 90 CDialog::OnInitDialog(); 91 92 // Set the icon for this dialog. The framework does this automatically when the application's main window is not a dialog. 93 94 SetIcon( mIcon, TRUE ); // Set big icon 95 SetIcon( mIcon, FALSE ); // Set small icon 96 97 CenterWindow( GetDesktopWindow() ); 98 99 // Set up the list. 100 101 CRect rect; 102 103 s.LoadString( IDS_BROWSER_LIST_COLUMN_NAME ); 104 mBrowserList.GetWindowRect( rect ); 105 mBrowserList.InsertColumn( 0, s, LVCFMT_LEFT, rect.Width() - 8 ); 106 107 // Start browsing for services. 108 109 DNSStatus err; 110 111 err = DNSBrowserCreate( 0, OnBrowserCallBack, this, &mBrowser ); 112 if( err ) 113 { 114 AfxMessageBox( IDP_SOCKETS_INIT_FAILED ); 115 goto exit; 116 } 117 118 err = DNSBrowserStartServiceSearch( mBrowser, kDNSBrowserFlagAutoResolve, "_http._tcp", NULL ); 119 if( err ) 120 { 121 AfxMessageBox( IDP_SOCKETS_INIT_FAILED ); 122 goto exit; 123 } 124 125exit: 126 return( TRUE ); 127} 128 129 130//=========================================================================================================================== 131// OnBrowserListDoubleClick 132//=========================================================================================================================== 133 134void BrowserDialog::OnBrowserListDoubleClick( NMHDR *pNMHDR, LRESULT *pResult ) 135{ 136 int selectedItem; 137 138 (void) pNMHDR; // Unused 139 140 selectedItem = mBrowserList.GetNextItem( -1, LVNI_SELECTED ); 141 if( selectedItem >= 0 ) 142 { 143 BrowserEntry * entry; 144 CString temp; 145 CString url; 146 147 // Build the URL from the IP and optional TXT record. 148 149 entry = &mBrowserEntries[ selectedItem ]; 150 url += "http://" + entry->ip; 151 temp = entry->text; 152 if( temp.Find( TEXT( "path=" ) ) == 0 ) 153 { 154 temp.Delete( 0, 5 ); 155 } 156 if( temp.Find( '/' ) != 0 ) 157 { 158 url += '/'; 159 } 160 url += temp; 161 162 // Let the system open the URL in the correct app. 163 164 SHELLEXECUTEINFO info; 165 166 info.cbSize = sizeof( info ); 167 info.fMask = 0; 168 info.hwnd = NULL; 169 info.lpVerb = NULL; 170 info.lpFile = url; 171 info.lpParameters = NULL; 172 info.lpDirectory = NULL; 173 info.nShow = SW_SHOWNORMAL; 174 info.hInstApp = NULL; 175 176 ShellExecuteEx( &info ); 177 } 178 *pResult = 0; 179} 180 181//=========================================================================================================================== 182// OnBrowserCallBack [static] 183//=========================================================================================================================== 184 185void 186 BrowserDialog::OnBrowserCallBack( 187 void * inContext, 188 DNSBrowserRef inRef, 189 DNSStatus inStatusCode, 190 const DNSBrowserEvent * inEvent ) 191{ 192 BrowserDialog * dialog; 193 BrowserEntry * entry; 194 BOOL posted; 195 196 DNS_UNUSED( inStatusCode ); 197 dialog = reinterpret_cast < BrowserDialog * > ( inContext ); 198 ASSERT( dialog ); 199 200 switch( inEvent->type ) 201 { 202 case kDNSBrowserEventTypeResolved: 203 if( inEvent->data.resolved->address.addressType == kDNSNetworkAddressTypeIPv4 ) 204 { 205 char ip[ 64 ]; 206 207 sprintf( ip, "%u.%u.%u.%u:%u", 208 inEvent->data.resolved->address.u.ipv4.addr.v8[ 0 ], 209 inEvent->data.resolved->address.u.ipv4.addr.v8[ 1 ], 210 inEvent->data.resolved->address.u.ipv4.addr.v8[ 2 ], 211 inEvent->data.resolved->address.u.ipv4.addr.v8[ 3 ], 212 ( inEvent->data.resolved->address.u.ipv4.port.v8[ 0 ] << 8 ) | 213 inEvent->data.resolved->address.u.ipv4.port.v8[ 1 ] ); 214 215 entry = new BrowserEntry; 216 ASSERT( entry ); 217 if( entry ) 218 { 219 UTF8StringToStringObject( inEvent->data.resolved->name, entry->name ); 220 UTF8StringToStringObject( ip, entry->ip ); 221 UTF8StringToStringObject( inEvent->data.resolved->textRecord, entry->text ); 222 223 posted = ::PostMessage( dialog->GetSafeHwnd(), WM_USER_SERVICE_ADD, 0, (LPARAM) entry ); 224 ASSERT( posted ); 225 if( !posted ) 226 { 227 delete entry; 228 } 229 } 230 } 231 break; 232 233 case kDNSBrowserEventTypeRemoveService: 234 entry = new BrowserEntry; 235 ASSERT( entry ); 236 if( entry ) 237 { 238 UTF8StringToStringObject( inEvent->data.removeService.name, entry->name ); 239 240 posted = ::PostMessage( dialog->GetSafeHwnd(), WM_USER_SERVICE_REMOVE, 0, (LPARAM) entry ); 241 ASSERT( posted ); 242 if( !posted ) 243 { 244 delete entry; 245 } 246 } 247 break; 248 249 default: 250 break; 251 } 252} 253 254//=========================================================================================================================== 255// BrowserAddService 256//=========================================================================================================================== 257 258LONG BrowserDialog::OnServiceAdd( WPARAM inWParam, LPARAM inLParam ) 259{ 260 BrowserEntry * entry; 261 INT_PTR lo; 262 INT_PTR hi; 263 INT_PTR mid; 264 int result; 265 266 (void) inWParam; // Unused 267 268 entry = reinterpret_cast < BrowserEntry * > ( inLParam ); 269 ASSERT( entry ); 270 271 result = -1; 272 mid = 0; 273 lo = 0; 274 hi = mBrowserEntries.GetSize() - 1; 275 while( lo <= hi ) 276 { 277 mid = ( lo + hi ) / 2; 278 result = entry->name.CompareNoCase( mBrowserEntries[ mid ].name ); 279 if( result == 0 ) 280 { 281 break; 282 } 283 else if( result < 0 ) 284 { 285 hi = mid - 1; 286 } 287 else 288 { 289 lo = mid + 1; 290 } 291 } 292 if( result == 0 ) 293 { 294 mBrowserEntries[ mid ].ip = entry->ip; 295 mBrowserEntries[ mid ].text = entry->text; 296 } 297 else 298 { 299 if( result > 0 ) 300 { 301 mid += 1; 302 } 303 mBrowserEntries.InsertAt( mid, *entry ); 304 mBrowserList.InsertItem( mid, entry->name ); 305 } 306 delete entry; 307 return( 0 ); 308} 309 310//=========================================================================================================================== 311// OnServiceRemove 312//=========================================================================================================================== 313 314LONG BrowserDialog::OnServiceRemove( WPARAM inWParam, LPARAM inLParam ) 315{ 316 BrowserEntry * entry; 317 INT_PTR hi; 318 INT_PTR lo; 319 INT_PTR mid; 320 int result; 321 322 (void) inWParam; // Unused 323 324 entry = reinterpret_cast < BrowserEntry * > ( inLParam ); 325 ASSERT( entry ); 326 327 result = -1; 328 mid = 0; 329 lo = 0; 330 hi = mBrowserEntries.GetSize() - 1; 331 while( lo <= hi ) 332 { 333 mid = ( lo + hi ) / 2; 334 result = entry->name.CompareNoCase( mBrowserEntries[ mid ].name ); 335 if( result == 0 ) 336 { 337 break; 338 } 339 else if( result < 0 ) 340 { 341 hi = mid - 1; 342 } 343 else 344 { 345 lo = mid + 1; 346 } 347 } 348 if( result == 0 ) 349 { 350 mBrowserList.DeleteItem( mid ); 351 mBrowserEntries.RemoveAt( mid ); 352 } 353 delete entry; 354 return( 0 ); 355} 356 357#if 0 358#pragma mark - 359#endif 360 361//=========================================================================================================================== 362// UTF8StringToStringObject 363//=========================================================================================================================== 364 365static DWORD UTF8StringToStringObject( const char *inUTF8, CString &inObject ) 366{ 367 DWORD err; 368 int n; 369 wchar_t * unicode; 370 371 unicode = NULL; 372 373 n = MultiByteToWideChar( CP_UTF8, 0, inUTF8, -1, NULL, 0 ); 374 if( n > 0 ) 375 { 376 unicode = (wchar_t *) malloc( (size_t)( n * sizeof( wchar_t ) ) ); 377 if( !unicode ) { err = ERROR_INSUFFICIENT_BUFFER; goto exit; }; 378 379 n = MultiByteToWideChar( CP_UTF8, 0, inUTF8, -1, unicode, n ); 380 inObject = unicode; 381 } 382 else 383 { 384 inObject = ""; 385 } 386 err = 0; 387 388exit: 389 if( unicode ) 390 { 391 free( unicode ); 392 } 393 return( err ); 394} 395