1/* -*- Mode: C; tab-width: 4 -*- 2 * 3 * Copyright (c) 2003-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 "CommonServices.h" 21#include "DebugServices.h" 22#include "WinServices.h" 23#include "dns_sd.h" 24 25#include "ExplorerBar.h" 26#include "LoginDialog.h" 27#include "Resource.h" 28 29#include "ExplorerBarWindow.h" 30#include "ExplorerPlugin.h" 31 32// MFC Debugging 33 34#ifdef _DEBUG 35#define new DEBUG_NEW 36#undef THIS_FILE 37static char THIS_FILE[] = __FILE__; 38#endif 39 40#if 0 41#pragma mark == Constants == 42#endif 43 44//=========================================================================================================================== 45// Constants 46//=========================================================================================================================== 47 48// Control IDs 49 50#define IDC_EXPLORER_TREE 1234 51 52// Private Messages 53 54#define WM_PRIVATE_SERVICE_EVENT ( WM_USER + 0x100 ) 55 56// TXT records 57 58#define kTXTRecordKeyPath "path" 59 60// IE Icon resource 61 62#define kIEIconResource 32529 63 64 65#if 0 66#pragma mark == Prototypes == 67#endif 68 69//=========================================================================================================================== 70// Prototypes 71//=========================================================================================================================== 72 73DEBUG_LOCAL int FindServiceArrayIndex( const ServiceInfoArray &inArray, const ServiceInfo &inService, int &outIndex ); 74 75#if 0 76#pragma mark == Message Map == 77#endif 78 79//=========================================================================================================================== 80// Message Map 81//=========================================================================================================================== 82 83BEGIN_MESSAGE_MAP( ExplorerBarWindow, CWnd ) 84 ON_WM_CREATE() 85 ON_WM_DESTROY() 86 ON_WM_SIZE() 87 ON_NOTIFY( NM_DBLCLK, IDC_EXPLORER_TREE, OnDoubleClick ) 88 ON_MESSAGE( WM_PRIVATE_SERVICE_EVENT, OnServiceEvent ) 89END_MESSAGE_MAP() 90 91#if 0 92#pragma mark - 93#endif 94 95//=========================================================================================================================== 96// ExplorerBarWindow 97//=========================================================================================================================== 98 99ExplorerBarWindow::ExplorerBarWindow( void ) 100{ 101 mOwner = NULL; 102 mResolveServiceRef = NULL; 103} 104 105//=========================================================================================================================== 106// ~ExplorerBarWindow 107//=========================================================================================================================== 108 109ExplorerBarWindow::~ExplorerBarWindow( void ) 110{ 111 // 112} 113 114#if 0 115#pragma mark - 116#endif 117 118//=========================================================================================================================== 119// OnCreate 120//=========================================================================================================================== 121 122int ExplorerBarWindow::OnCreate( LPCREATESTRUCT inCreateStruct ) 123{ 124 AFX_MANAGE_STATE( AfxGetStaticModuleState() ); 125 126 HINSTANCE module = NULL; 127 OSStatus err; 128 CRect rect; 129 CBitmap bitmap; 130 CString s; 131 132 err = CWnd::OnCreate( inCreateStruct ); 133 require_noerr( err, exit ); 134 135 GetClientRect( rect ); 136 mTree.Create( WS_TABSTOP | WS_VISIBLE | WS_CHILD | TVS_HASBUTTONS | TVS_LINESATROOT | TVS_NOHSCROLL , rect, this, 137 IDC_EXPLORER_TREE ); 138 139 ServiceHandlerEntry * e; 140 141 s.LoadString( IDS_ABOUT ); 142 m_about = mTree.InsertItem( s, 0, 0 ); 143 144 // Web Site Handler 145 146 e = new ServiceHandlerEntry; 147 check( e ); 148 e->type = "_http._tcp"; 149 e->urlScheme = "http://"; 150 e->ref = NULL; 151 e->obj = this; 152 e->needsLogin = false; 153 mServiceHandlers.Add( e ); 154 155 err = DNSServiceBrowse( &e->ref, 0, 0, e->type, NULL, BrowseCallBack, e ); 156 require_noerr( err, exit ); 157 158 err = WSAAsyncSelect((SOCKET) DNSServiceRefSockFD(e->ref), m_hWnd, WM_PRIVATE_SERVICE_EVENT, FD_READ|FD_CLOSE); 159 require_noerr( err, exit ); 160 161 m_serviceRefs.push_back(e->ref); 162 163#if defined( _BROWSE_FOR_HTTPS_ ) 164 e = new ServiceHandlerEntry; 165 check( e ); 166 e->type = "_https._tcp"; 167 e->urlScheme = "https://"; 168 e->ref = NULL; 169 e->obj = this; 170 e->needsLogin = false; 171 mServiceHandlers.Add( e ); 172 173 err = DNSServiceBrowse( &e->ref, 0, 0, e->type, NULL, BrowseCallBack, e ); 174 require_noerr( err, exit ); 175 176 err = WSAAsyncSelect((SOCKET) DNSServiceRefSockFD(e->ref), m_hWnd, WM_PRIVATE_SERVICE_EVENT, FD_READ|FD_CLOSE); 177 require_noerr( err, exit ); 178 179 m_serviceRefs.push_back(e->ref); 180#endif 181 182 m_imageList.Create( 16, 16, ILC_MASK | ILC_COLOR16, 2, 0); 183 184 bitmap.Attach( ::LoadBitmap( GetNonLocalizedResources(), MAKEINTRESOURCE( IDB_LOGO ) ) ); 185 m_imageList.Add( &bitmap, (CBitmap*) NULL ); 186 bitmap.Detach(); 187 188 mTree.SetImageList(&m_imageList, TVSIL_NORMAL); 189 190exit: 191 192 if ( module ) 193 { 194 FreeLibrary( module ); 195 module = NULL; 196 } 197 198 // Cannot talk to the mDNSResponder service. Show the error message and exit (with kNoErr so they can see it). 199 if ( err ) 200 { 201 if ( err == kDNSServiceErr_Firewall ) 202 { 203 s.LoadString( IDS_FIREWALL ); 204 } 205 else 206 { 207 s.LoadString( IDS_MDNSRESPONDER_NOT_AVAILABLE ); 208 } 209 210 mTree.DeleteAllItems(); 211 mTree.InsertItem( s, 0, 0, TVI_ROOT, TVI_LAST ); 212 213 err = kNoErr; 214 } 215 216 return( err ); 217} 218 219//=========================================================================================================================== 220// OnDestroy 221//=========================================================================================================================== 222 223void ExplorerBarWindow::OnDestroy( void ) 224{ 225 // Stop any resolves that may still be pending (shouldn't be any). 226 227 StopResolve(); 228 229 // Clean up the extant browses 230 while (m_serviceRefs.size() > 0) 231 { 232 // 233 // take the head of the list 234 // 235 DNSServiceRef ref = m_serviceRefs.front(); 236 237 // 238 // Stop will remove it from the list 239 // 240 Stop( ref ); 241 } 242 243 // Clean up the service handlers. 244 245 int i; 246 int n; 247 248 n = (int) mServiceHandlers.GetSize(); 249 for( i = 0; i < n; ++i ) 250 { 251 delete mServiceHandlers[ i ]; 252 } 253 254 CWnd::OnDestroy(); 255} 256 257//=========================================================================================================================== 258// OnSize 259//=========================================================================================================================== 260 261void ExplorerBarWindow::OnSize( UINT inType, int inX, int inY ) 262{ 263 CWnd::OnSize( inType, inX, inY ); 264 mTree.MoveWindow( 0, 0, inX, inY ); 265} 266 267//=========================================================================================================================== 268// OnDoubleClick 269//=========================================================================================================================== 270 271void ExplorerBarWindow::OnDoubleClick( NMHDR *inNMHDR, LRESULT *outResult ) 272{ 273 HTREEITEM item; 274 ServiceInfo * service; 275 OSStatus err; 276 277 DEBUG_UNUSED( inNMHDR ); 278 279 item = mTree.GetSelectedItem(); 280 require( item, exit ); 281 282 // Tell Internet Explorer to go to the URL if it's about item 283 284 if ( item == m_about ) 285 { 286 CString url; 287 288 check( mOwner ); 289 290 url.LoadString( IDS_ABOUT_URL ); 291 mOwner->GoToURL( url ); 292 } 293 else 294 { 295 service = reinterpret_cast < ServiceInfo * > ( mTree.GetItemData( item ) ); 296 require_quiet( service, exit ); 297 298 err = StartResolve( service ); 299 require_noerr( err, exit ); 300 } 301 302exit: 303 *outResult = 0; 304} 305 306 307//=========================================================================================================================== 308// OnServiceEvent 309//=========================================================================================================================== 310 311LRESULT 312ExplorerBarWindow::OnServiceEvent(WPARAM inWParam, LPARAM inLParam) 313{ 314 if (WSAGETSELECTERROR(inLParam) && !(HIWORD(inLParam))) 315 { 316 dlog( kDebugLevelError, "OnServiceEvent: window error\n" ); 317 } 318 else 319 { 320 SOCKET sock = (SOCKET) inWParam; 321 322 // iterate thru list 323 ServiceRefList::iterator it; 324 325 for (it = m_serviceRefs.begin(); it != m_serviceRefs.end(); it++) 326 { 327 DNSServiceRef ref = *it; 328 329 check(ref != NULL); 330 331 if ((SOCKET) DNSServiceRefSockFD(ref) == sock) 332 { 333 DNSServiceErrorType err; 334 335 err = DNSServiceProcessResult(ref); 336 337 if (err != 0) 338 { 339 CString s; 340 341 s.LoadString( IDS_MDNSRESPONDER_NOT_AVAILABLE ); 342 mTree.DeleteAllItems(); 343 mTree.InsertItem( s, 0, 0, TVI_ROOT, TVI_LAST ); 344 345 Stop(ref); 346 } 347 348 break; 349 } 350 } 351 } 352 353 return ( 0 ); 354} 355 356#if 0 357#pragma mark - 358#endif 359 360//=========================================================================================================================== 361// BrowseCallBack 362//=========================================================================================================================== 363 364void DNSSD_API 365 ExplorerBarWindow::BrowseCallBack( 366 DNSServiceRef inRef, 367 DNSServiceFlags inFlags, 368 uint32_t inInterfaceIndex, 369 DNSServiceErrorType inErrorCode, 370 const char * inName, 371 const char * inType, 372 const char * inDomain, 373 void * inContext ) 374{ 375 ServiceHandlerEntry * obj; 376 ServiceInfo * service; 377 OSStatus err; 378 379 DEBUG_UNUSED( inRef ); 380 381 obj = NULL; 382 service = NULL; 383 384 require_noerr( inErrorCode, exit ); 385 obj = reinterpret_cast < ServiceHandlerEntry * > ( inContext ); 386 check( obj ); 387 check( obj->obj ); 388 389 // 390 // set the UI to hold off on updates 391 // 392 obj->obj->mTree.SetRedraw(FALSE); 393 394 try 395 { 396 service = new ServiceInfo; 397 require_action( service, exit, err = kNoMemoryErr ); 398 399 err = UTF8StringToStringObject( inName, service->displayName ); 400 check_noerr( err ); 401 402 service->name = _strdup( inName ); 403 require_action( service->name, exit, err = kNoMemoryErr ); 404 405 service->type = _strdup( inType ); 406 require_action( service->type, exit, err = kNoMemoryErr ); 407 408 service->domain = _strdup( inDomain ); 409 require_action( service->domain, exit, err = kNoMemoryErr ); 410 411 service->ifi = inInterfaceIndex; 412 service->handler = obj; 413 414 service->refs = 1; 415 416 if (inFlags & kDNSServiceFlagsAdd) obj->obj->OnServiceAdd (service); 417 else obj->obj->OnServiceRemove(service); 418 419 service = NULL; 420 } 421 catch( ... ) 422 { 423 dlog( kDebugLevelError, "BrowseCallBack: exception thrown\n" ); 424 } 425 426exit: 427 // 428 // If no more coming, then update UI 429 // 430 if (obj && obj->obj && ((inFlags & kDNSServiceFlagsMoreComing) == 0)) 431 { 432 obj->obj->mTree.SetRedraw(TRUE); 433 obj->obj->mTree.Invalidate(); 434 } 435 436 if( service ) 437 { 438 delete service; 439 } 440} 441 442//=========================================================================================================================== 443// OnServiceAdd 444//=========================================================================================================================== 445 446LONG ExplorerBarWindow::OnServiceAdd( ServiceInfo * service ) 447{ 448 ServiceHandlerEntry * handler; 449 int cmp; 450 int index; 451 452 453 check( service ); 454 handler = service->handler; 455 check( handler ); 456 457 cmp = FindServiceArrayIndex( handler->array, *service, index ); 458 if( cmp == 0 ) 459 { 460 // Found a match so update the item. The index is index + 1 so subtract 1. 461 462 index -= 1; 463 check( index < handler->array.GetSize() ); 464 465 handler->array[ index ]->refs++; 466 467 delete service; 468 } 469 else 470 { 471 HTREEITEM afterItem; 472 473 // Insert the new item in sorted order. 474 475 afterItem = ( index > 0 ) ? handler->array[ index - 1 ]->item : m_about; 476 handler->array.InsertAt( index, service ); 477 service->item = mTree.InsertItem( service->displayName, 0, 0, NULL, afterItem ); 478 mTree.SetItemData( service->item, (DWORD_PTR) service ); 479 } 480 return( 0 ); 481} 482 483//=========================================================================================================================== 484// OnServiceRemove 485//=========================================================================================================================== 486 487LONG ExplorerBarWindow::OnServiceRemove( ServiceInfo * service ) 488{ 489 ServiceHandlerEntry * handler; 490 int cmp; 491 int index; 492 493 494 check( service ); 495 handler = service->handler; 496 check( handler ); 497 498 // Search to see if we know about this service instance. If so, remove it from the list. 499 500 cmp = FindServiceArrayIndex( handler->array, *service, index ); 501 check( cmp == 0 ); 502 503 if( cmp == 0 ) 504 { 505 // Possibly found a match remove the item. The index 506 // is index + 1 so subtract 1. 507 index -= 1; 508 check( index < handler->array.GetSize() ); 509 510 if ( --handler->array[ index ]->refs == 0 ) 511 { 512 mTree.DeleteItem( handler->array[ index ]->item ); 513 delete handler->array[ index ]; 514 handler->array.RemoveAt( index ); 515 } 516 } 517 518 delete service; 519 return( 0 ); 520} 521 522#if 0 523#pragma mark - 524#endif 525 526//=========================================================================================================================== 527// StartResolve 528//=========================================================================================================================== 529 530OSStatus ExplorerBarWindow::StartResolve( ServiceInfo *inService ) 531{ 532 OSStatus err; 533 534 check( inService ); 535 536 // Stop any current resolve that may be in progress. 537 538 StopResolve(); 539 540 // Resolve the service. 541 err = DNSServiceResolve( &mResolveServiceRef, 0, 0, 542 inService->name, inService->type, inService->domain, (DNSServiceResolveReply) ResolveCallBack, inService->handler ); 543 require_noerr( err, exit ); 544 545 err = WSAAsyncSelect((SOCKET) DNSServiceRefSockFD(mResolveServiceRef), m_hWnd, WM_PRIVATE_SERVICE_EVENT, FD_READ|FD_CLOSE); 546 require_noerr( err, exit ); 547 548 m_serviceRefs.push_back(mResolveServiceRef); 549 550exit: 551 return( err ); 552} 553 554//=========================================================================================================================== 555// StopResolve 556//=========================================================================================================================== 557 558void ExplorerBarWindow::StopResolve( void ) 559{ 560 if( mResolveServiceRef ) 561 { 562 Stop( mResolveServiceRef ); 563 mResolveServiceRef = NULL; 564 } 565} 566 567//=========================================================================================================================== 568// ResolveCallBack 569//=========================================================================================================================== 570 571void DNSSD_API 572 ExplorerBarWindow::ResolveCallBack( 573 DNSServiceRef inRef, 574 DNSServiceFlags inFlags, 575 uint32_t inInterfaceIndex, 576 DNSServiceErrorType inErrorCode, 577 const char * inFullName, 578 const char * inHostName, 579 uint16_t inPort, 580 uint16_t inTXTSize, 581 const char * inTXT, 582 void * inContext ) 583{ 584 ExplorerBarWindow * obj; 585 ServiceHandlerEntry * handler; 586 OSStatus err; 587 588 DEBUG_UNUSED( inRef ); 589 DEBUG_UNUSED( inFlags ); 590 DEBUG_UNUSED( inErrorCode ); 591 DEBUG_UNUSED( inFullName ); 592 593 require_noerr( inErrorCode, exit ); 594 handler = (ServiceHandlerEntry *) inContext; 595 check( handler ); 596 obj = handler->obj; 597 check( obj ); 598 599 try 600 { 601 ResolveInfo * resolve; 602 int idx; 603 604 dlog( kDebugLevelNotice, "resolved %s on ifi %d to %s\n", inFullName, inInterfaceIndex, inHostName ); 605 606 // Stop resolving after the first good result. 607 608 obj->StopResolve(); 609 610 // Post a message to the main thread so it can handle it since MFC is not thread safe. 611 612 resolve = new ResolveInfo; 613 require_action( resolve, exit, err = kNoMemoryErr ); 614 615 UTF8StringToStringObject( inHostName, resolve->host ); 616 617 // rdar://problem/3841564 618 // 619 // strip trailing dot from hostname because some flavors of Windows 620 // have trouble parsing it. 621 622 idx = resolve->host.ReverseFind('.'); 623 624 if ((idx > 1) && ((resolve->host.GetLength() - 1) == idx)) 625 { 626 resolve->host.Delete(idx, 1); 627 } 628 629 resolve->port = ntohs( inPort ); 630 resolve->ifi = inInterfaceIndex; 631 resolve->handler = handler; 632 633 err = resolve->txt.SetData( inTXT, inTXTSize ); 634 check_noerr( err ); 635 636 obj->OnResolve(resolve); 637 } 638 catch( ... ) 639 { 640 dlog( kDebugLevelError, "ResolveCallBack: exception thrown\n" ); 641 } 642 643exit: 644 return; 645} 646 647//=========================================================================================================================== 648// OnResolve 649//=========================================================================================================================== 650 651LONG ExplorerBarWindow::OnResolve( ResolveInfo * resolve ) 652{ 653 CString url; 654 uint8_t * path; 655 uint8_t pathSize; 656 char * pathPrefix; 657 CString username; 658 CString password; 659 660 661 check( resolve ); 662 663 // Get login info if needed. 664 665 if( resolve->handler->needsLogin ) 666 { 667 LoginDialog dialog; 668 669 if( !dialog.GetLogin( username, password ) ) 670 { 671 goto exit; 672 } 673 } 674 675 // If the HTTP TXT record is a "path=" entry, use it as the resource path. Otherwise, use "/". 676 677 pathPrefix = ""; 678 if( strcmp( resolve->handler->type, "_http._tcp" ) == 0 ) 679 { 680 uint8_t * txtData; 681 uint16_t txtLen; 682 683 resolve->txt.GetData( &txtData, &txtLen ); 684 685 path = (uint8_t*) TXTRecordGetValuePtr(txtLen, txtData, kTXTRecordKeyPath, &pathSize); 686 687 if (path == NULL) 688 { 689 path = (uint8_t*) ""; 690 pathSize = 1; 691 } 692 } 693 else 694 { 695 path = (uint8_t *) ""; 696 pathSize = 1; 697 } 698 699 // Build the URL in the following format: 700 // 701 // <urlScheme>[<username>[:<password>]@]<name/ip>[<path>] 702 703 url.AppendFormat( TEXT( "%S" ), resolve->handler->urlScheme ); // URL Scheme 704 if( username.GetLength() > 0 ) 705 { 706 url.AppendFormat( TEXT( "%s" ), username ); // Username 707 if( password.GetLength() > 0 ) 708 { 709 url.AppendFormat( TEXT( ":%s" ), password ); // Password 710 } 711 url.AppendFormat( TEXT( "@" ) ); 712 } 713 714 url += resolve->host; // Host 715 url.AppendFormat( TEXT( ":%d" ), resolve->port ); // :Port 716 url.AppendFormat( TEXT( "%S" ), pathPrefix ); // Path Prefix ("/" or empty). 717 url.AppendFormat( TEXT( "%.*S" ), (int) pathSize, (char *) path ); // Path (possibly empty). 718 719 // Tell Internet Explorer to go to the URL. 720 721 check( mOwner ); 722 mOwner->GoToURL( url ); 723 724exit: 725 delete resolve; 726 return( 0 ); 727} 728 729//=========================================================================================================================== 730// Stop 731//=========================================================================================================================== 732void ExplorerBarWindow::Stop( DNSServiceRef ref ) 733{ 734 m_serviceRefs.remove( ref ); 735 736 WSAAsyncSelect(DNSServiceRefSockFD( ref ), m_hWnd, WM_PRIVATE_SERVICE_EVENT, 0); 737 738 DNSServiceRefDeallocate( ref ); 739} 740 741 742#if 0 743#pragma mark - 744#endif 745 746//=========================================================================================================================== 747// FindServiceArrayIndex 748//=========================================================================================================================== 749 750DEBUG_LOCAL int FindServiceArrayIndex( const ServiceInfoArray &inArray, const ServiceInfo &inService, int &outIndex ) 751{ 752 int result; 753 int lo; 754 int hi; 755 int mid; 756 757 result = -1; 758 mid = 0; 759 lo = 0; 760 hi = (int)( inArray.GetSize() - 1 ); 761 while( lo <= hi ) 762 { 763 mid = ( lo + hi ) / 2; 764 result = inService.displayName.CompareNoCase( inArray[ mid ]->displayName ); 765#if 0 766 if( result == 0 ) 767 { 768 result = ( (int) inService.ifi ) - ( (int) inArray[ mid ]->ifi ); 769 } 770#endif 771 if( result == 0 ) 772 { 773 break; 774 } 775 else if( result < 0 ) 776 { 777 hi = mid - 1; 778 } 779 else 780 { 781 lo = mid + 1; 782 } 783 } 784 if( result == 0 ) 785 { 786 mid += 1; // Bump index so new item is inserted after matching item. 787 } 788 else if( result > 0 ) 789 { 790 mid += 1; 791 } 792 outIndex = mid; 793 return( result ); 794} 795