2 * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
23 Change History (most recent first):
25 $Log: ExplorerBarWindow.cpp,v $
26 Revision 1.15 2005/01/27 22:38:27 shersche
27 add About item to tree list
29 Revision 1.14 2005/01/25 17:55:39 shersche
30 <rdar://problem/3911084> Get bitmaps from non-localizable resource module
33 Revision 1.13 2005/01/06 21:13:09 shersche
34 <rdar://problem/3796779> Handle kDNSServiceErr_Firewall return codes
37 Revision 1.12 2004/10/26 00:56:03 cheshire
38 Use "#if 0" instead of commenting out code
40 Revision 1.11 2004/10/18 23:49:17 shersche
41 <rdar://problem/3841564> Remove trailing dot from hostname, because some flavors of Windows have difficulty parsing hostnames with a trailing dot.
44 Revision 1.10 2004/09/02 02:18:58 cheshire
45 Minor textual cleanup to improve readability
47 Revision 1.9 2004/09/02 02:11:56 cheshire
48 <rdar://problem/3783611> Fix incorrect testing of MoreComing flag
50 Revision 1.8 2004/07/26 05:47:31 shersche
51 use TXTRecord APIs, fix bug in locating service to be removed
53 Revision 1.7 2004/07/22 16:08:20 shersche
54 clean up debug print statements, re-enable code inadvertently commented out
56 Revision 1.6 2004/07/22 05:27:20 shersche
57 <rdar://problem/3735827> Check to make sure error isn't WSAEWOULDBLOCK when canceling browse
60 Revision 1.5 2004/07/20 06:49:18 shersche
61 clean up socket handling code
63 Revision 1.4 2004/07/13 21:24:21 rpantos
64 Fix for <rdar://problem/3701120>.
66 Revision 1.3 2004/06/27 14:59:59 shersche
67 reference count service info to handle multi-homed hosts
69 Revision 1.2 2004/06/23 16:09:34 shersche
70 Add the resolve DNSServiceRef to list of extant refs. This fixes the "doesn't resolve when double clicking" problem
72 Submitted by: Scott Herscher
74 Revision 1.1 2004/06/18 04:34:59 rpantos
75 Move to Clients from mDNSWindows
77 Revision 1.5 2004/04/15 01:00:05 bradley
78 Removed support for automatically querying for A/AAAA records when resolving names. Platforms
79 without .local name resolving support will need to manually query for A/AAAA records as needed.
81 Revision 1.4 2004/04/09 21:03:15 bradley
82 Changed port numbers to use network byte order for consistency with other platforms.
84 Revision 1.3 2004/04/08 09:43:43 bradley
85 Changed callback calling conventions to __stdcall so they can be used with C# delegates.
87 Revision 1.2 2004/02/21 04:36:19 bradley
88 Enable dot local name lookups now that the NSP is being installed.
90 Revision 1.1 2004/01/30 03:01:56 bradley
91 Explorer Plugin to browse for DNS-SD advertised Web and FTP servers from within Internet Explorer.
97 #include "CommonServices.h"
98 #include "DebugServices.h"
99 #include "WinServices.h"
102 #include "ExplorerBar.h"
103 #include "LoginDialog.h"
104 #include "Resource.h"
106 #include "ExplorerBarWindow.h"
107 #include "ExplorerPlugin.h"
112 #define new DEBUG_NEW
114 static char THIS_FILE
[] = __FILE__
;
118 #pragma mark == Constants ==
121 //===========================================================================================================================
123 //===========================================================================================================================
127 #define IDC_EXPLORER_TREE 1234
131 #define WM_PRIVATE_SERVICE_EVENT ( WM_USER + 0x100 )
135 #define kTXTRecordKeyPath "path"
139 #pragma mark == Prototypes ==
142 //===========================================================================================================================
144 //===========================================================================================================================
146 DEBUG_LOCAL
int FindServiceArrayIndex( const ServiceInfoArray
&inArray
, const ServiceInfo
&inService
, int &outIndex
);
149 #pragma mark == Message Map ==
152 //===========================================================================================================================
154 //===========================================================================================================================
156 BEGIN_MESSAGE_MAP( ExplorerBarWindow
, CWnd
)
160 ON_NOTIFY( NM_DBLCLK
, IDC_EXPLORER_TREE
, OnDoubleClick
)
161 ON_MESSAGE( WM_PRIVATE_SERVICE_EVENT
, OnServiceEvent
)
168 //===========================================================================================================================
170 //===========================================================================================================================
172 ExplorerBarWindow::ExplorerBarWindow( void )
175 mResolveServiceRef
= NULL
;
178 //===========================================================================================================================
179 // ~ExplorerBarWindow
180 //===========================================================================================================================
182 ExplorerBarWindow::~ExplorerBarWindow( void )
191 //===========================================================================================================================
193 //===========================================================================================================================
195 int ExplorerBarWindow::OnCreate( LPCREATESTRUCT inCreateStruct
)
197 AFX_MANAGE_STATE( AfxGetStaticModuleState() );
204 err
= CWnd::OnCreate( inCreateStruct
);
205 require_noerr( err
, exit
);
207 GetClientRect( rect
);
208 mTree
.Create( WS_TABSTOP
| WS_VISIBLE
| WS_CHILD
| TVS_HASBUTTONS
| TVS_LINESATROOT
| TVS_HASLINES
| TVS_NOHSCROLL
, rect
, this,
211 s
.LoadString( IDS_ABOUT
);
212 m_about
= mTree
.InsertItem( s
, 0, 0 );
214 ServiceHandlerEntry
* e
;
218 e
= new ServiceHandlerEntry
;
220 e
->type
= "_http._tcp";
221 e
->urlScheme
= "http://";
226 e
->needsLogin
= false;
227 mServiceHandlers
.Add( e
);
229 s
.LoadString( IDS_WEB_SITES
);
230 e
->treeItem
= mTree
.InsertItem( s
, 1, 1 );
231 mTree
.Expand( e
->treeItem
, TVE_EXPAND
);
233 err
= DNSServiceBrowse( &e
->ref
, 0, 0, e
->type
, NULL
, BrowseCallBack
, e
);
234 require_noerr( err
, exit
);
236 err
= WSAAsyncSelect((SOCKET
) DNSServiceRefSockFD(e
->ref
), m_hWnd
, WM_PRIVATE_SERVICE_EVENT
, FD_READ
|FD_CLOSE
);
237 require_noerr( err
, exit
);
239 m_serviceRefs
.push_back(e
->ref
);
243 e
= new ServiceHandlerEntry
;
245 e
->type
= "_ftp._tcp";
246 e
->urlScheme
= "ftp://";
251 e
->needsLogin
= true;
252 mServiceHandlers
.Add( e
);
254 s
.LoadString( IDS_FTP_SITES
);
255 e
->treeItem
= mTree
.InsertItem( s
, 1, 1 );
256 mTree
.Expand( e
->treeItem
, TVE_EXPAND
);
258 err
= DNSServiceBrowse( &e
->ref
, 0, 0, e
->type
, NULL
, BrowseCallBack
, e
);
259 require_noerr( err
, exit
);
261 err
= WSAAsyncSelect((SOCKET
) DNSServiceRefSockFD(e
->ref
), m_hWnd
, WM_PRIVATE_SERVICE_EVENT
, FD_READ
|FD_CLOSE
);
262 require_noerr( err
, exit
);
264 m_serviceRefs
.push_back(e
->ref
);
266 m_imageList
.Create( 16, 16, ILC_COLORDDB
, 2, 0);
267 bitmap
.Attach( ::LoadBitmap( GetNonLocalizedResources(), MAKEINTRESOURCE( IDB_GLOBE
) ) );
268 m_imageList
.Add( &bitmap
, (CBitmap
*) NULL
);
270 bitmap
.Attach( ::LoadBitmap( GetNonLocalizedResources(), MAKEINTRESOURCE( IDB_LOGO
) ) );
271 m_imageList
.Add( &bitmap
, (CBitmap
*) NULL
);
273 mTree
.SetImageList(&m_imageList
, TVSIL_NORMAL
);
277 // Cannot talk to the mDNSResponder service. Show the error message and exit (with kNoErr so they can see it).
280 if ( err
== kDNSServiceErr_Firewall
)
282 s
.LoadString( IDS_FIREWALL
);
286 s
.LoadString( IDS_MDNSRESPONDER_NOT_AVAILABLE
);
289 mTree
.DeleteAllItems();
290 mTree
.InsertItem( s
, 0, 0, TVI_ROOT
, TVI_LAST
);
298 //===========================================================================================================================
300 //===========================================================================================================================
302 void ExplorerBarWindow::OnDestroy( void )
304 // Stop any resolves that may still be pending (shouldn't be any).
308 // Clean up the extant browses
309 while (m_serviceRefs
.size() > 0)
312 // take the head of the list
314 DNSServiceRef ref
= m_serviceRefs
.front();
317 // Stop will remove it from the list
322 // Clean up the service handlers.
327 n
= (int) mServiceHandlers
.GetSize();
328 for( i
= 0; i
< n
; ++i
)
330 delete mServiceHandlers
[ i
];
336 //===========================================================================================================================
338 //===========================================================================================================================
340 void ExplorerBarWindow::OnSize( UINT inType
, int inX
, int inY
)
342 CWnd::OnSize( inType
, inX
, inY
);
343 mTree
.MoveWindow( 0, 0, inX
, inY
);
346 //===========================================================================================================================
348 //===========================================================================================================================
350 void ExplorerBarWindow::OnDoubleClick( NMHDR
*inNMHDR
, LRESULT
*outResult
)
353 ServiceInfo
* service
;
356 DEBUG_UNUSED( inNMHDR
);
358 item
= mTree
.GetSelectedItem();
359 require( item
, exit
);
361 // Tell Internet Explorer to go to the URL if it's about item
363 if ( item
== m_about
)
369 url
.LoadString( IDS_ABOUT_URL
);
370 mOwner
->GoToURL( url
);
374 service
= reinterpret_cast < ServiceInfo
* > ( mTree
.GetItemData( item
) );
375 require_quiet( service
, exit
);
377 err
= StartResolve( service
);
378 require_noerr( err
, exit
);
386 //===========================================================================================================================
388 //===========================================================================================================================
391 ExplorerBarWindow::OnServiceEvent(WPARAM inWParam
, LPARAM inLParam
)
393 if (WSAGETSELECTERROR(inLParam
) && !(HIWORD(inLParam
)))
395 dlog( kDebugLevelError
, "OnServiceEvent: window error\n" );
399 SOCKET sock
= (SOCKET
) inWParam
;
402 ServiceRefList::iterator it
;
404 for (it
= m_serviceRefs
.begin(); it
!= m_serviceRefs
.end(); it
++)
406 DNSServiceRef ref
= *it
;
410 if ((SOCKET
) DNSServiceRefSockFD(ref
) == sock
)
412 DNSServiceErrorType err
;
414 err
= DNSServiceProcessResult(ref
);
420 s
.LoadString( IDS_MDNSRESPONDER_NOT_AVAILABLE
);
421 mTree
.DeleteAllItems();
422 mTree
.InsertItem( s
, 0, 0, TVI_ROOT
, TVI_LAST
);
439 //===========================================================================================================================
441 //===========================================================================================================================
444 ExplorerBarWindow::BrowseCallBack(
446 DNSServiceFlags inFlags
,
447 uint32_t inInterfaceIndex
,
448 DNSServiceErrorType inErrorCode
,
451 const char * inDomain
,
454 ServiceHandlerEntry
* obj
;
455 ServiceInfo
* service
;
458 DEBUG_UNUSED( inRef
);
463 require_noerr( inErrorCode
, exit
);
464 obj
= reinterpret_cast < ServiceHandlerEntry
* > ( inContext
);
469 // set the UI to hold off on updates
471 obj
->obj
->mTree
.SetRedraw(FALSE
);
475 service
= new ServiceInfo
;
476 require_action( service
, exit
, err
= kNoMemoryErr
);
478 err
= UTF8StringToStringObject( inName
, service
->displayName
);
481 service
->name
= strdup( inName
);
482 require_action( service
->name
, exit
, err
= kNoMemoryErr
);
484 service
->type
= strdup( inType
);
485 require_action( service
->type
, exit
, err
= kNoMemoryErr
);
487 service
->domain
= strdup( inDomain
);
488 require_action( service
->domain
, exit
, err
= kNoMemoryErr
);
490 service
->ifi
= inInterfaceIndex
;
491 service
->handler
= obj
;
495 if (inFlags
& kDNSServiceFlagsAdd
) obj
->obj
->OnServiceAdd (service
);
496 else obj
->obj
->OnServiceRemove(service
);
502 dlog( kDebugLevelError
, "BrowseCallBack: exception thrown\n" );
507 // If no more coming, then update UI
509 if (obj
&& obj
->obj
&& ((inFlags
& kDNSServiceFlagsMoreComing
) == 0))
511 obj
->obj
->mTree
.SetRedraw(TRUE
);
512 obj
->obj
->mTree
.Invalidate();
521 //===========================================================================================================================
523 //===========================================================================================================================
525 LONG
ExplorerBarWindow::OnServiceAdd( ServiceInfo
* service
)
527 ServiceHandlerEntry
* handler
;
533 handler
= service
->handler
;
536 cmp
= FindServiceArrayIndex( handler
->array
, *service
, index
);
539 // Found a match so update the item. The index is index + 1 so subtract 1.
542 check( index
< handler
->array
.GetSize() );
544 handler
->array
[ index
]->refs
++;
552 // Insert the new item in sorted order.
554 afterItem
= ( index
> 0 ) ? handler
->array
[ index
- 1 ]->item
: TVI_FIRST
;
555 handler
->array
.InsertAt( index
, service
);
556 service
->item
= mTree
.InsertItem( service
->displayName
, 1, 1, handler
->treeItem
, afterItem
);
557 mTree
.SetItemData( service
->item
, (DWORD_PTR
) service
);
559 // Make sure the item is visible if this is the first time a service was added.
561 if( handler
->treeFirst
)
563 handler
->treeFirst
= false;
564 mTree
.EnsureVisible( service
->item
);
570 //===========================================================================================================================
572 //===========================================================================================================================
574 LONG
ExplorerBarWindow::OnServiceRemove( ServiceInfo
* service
)
576 ServiceHandlerEntry
* handler
;
582 handler
= service
->handler
;
585 // Search to see if we know about this service instance. If so, remove it from the list.
587 cmp
= FindServiceArrayIndex( handler
->array
, *service
, index
);
592 // Possibly found a match remove the item. The index
593 // is index + 1 so subtract 1.
595 check( index
< handler
->array
.GetSize() );
597 if ( --handler
->array
[ index
]->refs
== 0 )
599 mTree
.DeleteItem( handler
->array
[ index
]->item
);
600 delete handler
->array
[ index
];
601 handler
->array
.RemoveAt( index
);
613 //===========================================================================================================================
615 //===========================================================================================================================
617 OSStatus
ExplorerBarWindow::StartResolve( ServiceInfo
*inService
)
623 // Stop any current resolve that may be in progress.
627 // Resolve the service.
628 err
= DNSServiceResolve( &mResolveServiceRef
, 0, 0,
629 inService
->name
, inService
->type
, inService
->domain
, (DNSServiceResolveReply
) ResolveCallBack
, inService
->handler
);
630 require_noerr( err
, exit
);
632 err
= WSAAsyncSelect((SOCKET
) DNSServiceRefSockFD(mResolveServiceRef
), m_hWnd
, WM_PRIVATE_SERVICE_EVENT
, FD_READ
|FD_CLOSE
);
633 require_noerr( err
, exit
);
635 m_serviceRefs
.push_back(mResolveServiceRef
);
641 //===========================================================================================================================
643 //===========================================================================================================================
645 void ExplorerBarWindow::StopResolve( void )
647 if( mResolveServiceRef
)
649 Stop( mResolveServiceRef
);
650 mResolveServiceRef
= NULL
;
654 //===========================================================================================================================
656 //===========================================================================================================================
659 ExplorerBarWindow::ResolveCallBack(
661 DNSServiceFlags inFlags
,
662 uint32_t inInterfaceIndex
,
663 DNSServiceErrorType inErrorCode
,
664 const char * inFullName
,
665 const char * inHostName
,
671 ExplorerBarWindow
* obj
;
672 ServiceHandlerEntry
* handler
;
675 DEBUG_UNUSED( inRef
);
676 DEBUG_UNUSED( inFlags
);
677 DEBUG_UNUSED( inErrorCode
);
678 DEBUG_UNUSED( inFullName
);
680 require_noerr( inErrorCode
, exit
);
681 handler
= (ServiceHandlerEntry
*) inContext
;
688 ResolveInfo
* resolve
;
691 dlog( kDebugLevelNotice
, "resolved %s on ifi %d to %s\n", inFullName
, inInterfaceIndex
, inHostName
);
693 // Stop resolving after the first good result.
697 // Post a message to the main thread so it can handle it since MFC is not thread safe.
699 resolve
= new ResolveInfo
;
700 require_action( resolve
, exit
, err
= kNoMemoryErr
);
702 UTF8StringToStringObject( inHostName
, resolve
->host
);
704 // rdar://problem/3841564
706 // strip trailing dot from hostname because some flavors of Windows
707 // have trouble parsing it.
709 idx
= resolve
->host
.ReverseFind('.');
711 if ((idx
> 1) && ((resolve
->host
.GetLength() - 1) == idx
))
713 resolve
->host
.Delete(idx
, 1);
716 resolve
->port
= ntohs( inPort
);
717 resolve
->ifi
= inInterfaceIndex
;
718 resolve
->handler
= handler
;
720 err
= resolve
->txt
.SetData( inTXT
, inTXTSize
);
723 obj
->OnResolve(resolve
);
727 dlog( kDebugLevelError
, "ResolveCallBack: exception thrown\n" );
734 //===========================================================================================================================
736 //===========================================================================================================================
738 LONG
ExplorerBarWindow::OnResolve( ResolveInfo
* resolve
)
750 // Get login info if needed.
752 if( resolve
->handler
->needsLogin
)
756 if( !dialog
.GetLogin( username
, password
) )
762 // If the HTTP TXT record is a "path=" entry, use it as the resource path. Otherwise, use "/".
765 if( strcmp( resolve
->handler
->type
, "_http._tcp" ) == 0 )
770 resolve
->txt
.GetData( &txtData
, &txtLen
);
772 path
= (uint8_t*) TXTRecordGetValuePtr(txtLen
, txtData
, kTXTRecordKeyPath
, &pathSize
);
776 path
= (uint8_t*) "";
782 path
= (uint8_t *) "";
786 // Build the URL in the following format:
788 // <urlScheme>[<username>[:<password>]@]<name/ip>[<path>]
790 url
.AppendFormat( TEXT( "%S" ), resolve
->handler
->urlScheme
); // URL Scheme
791 if( username
.GetLength() > 0 )
793 url
.AppendFormat( TEXT( "%s" ), username
); // Username
794 if( password
.GetLength() > 0 )
796 url
.AppendFormat( TEXT( ":%s" ), password
); // Password
798 url
.AppendFormat( TEXT( "@" ) );
801 url
+= resolve
->host
; // Host
802 url
.AppendFormat( TEXT( ":%d" ), resolve
->port
); // :Port
803 url
.AppendFormat( TEXT( "%S" ), pathPrefix
); // Path Prefix ("/" or empty).
804 url
.AppendFormat( TEXT( "%.*S" ), (int) pathSize
, (char *) path
); // Path (possibly empty).
806 // Tell Internet Explorer to go to the URL.
809 mOwner
->GoToURL( url
);
816 //===========================================================================================================================
818 //===========================================================================================================================
819 void ExplorerBarWindow::Stop( DNSServiceRef ref
)
821 m_serviceRefs
.remove( ref
);
823 WSAAsyncSelect(DNSServiceRefSockFD( ref
), m_hWnd
, WM_PRIVATE_SERVICE_EVENT
, 0);
825 DNSServiceRefDeallocate( ref
);
833 //===========================================================================================================================
834 // FindServiceArrayIndex
835 //===========================================================================================================================
837 DEBUG_LOCAL
int FindServiceArrayIndex( const ServiceInfoArray
&inArray
, const ServiceInfo
&inService
, int &outIndex
)
847 hi
= (int)( inArray
.GetSize() - 1 );
850 mid
= ( lo
+ hi
) / 2;
851 result
= inService
.displayName
.CompareNoCase( inArray
[ mid
]->displayName
);
855 result
= ( (int) inService
.ifi
) - ( (int) inArray
[ mid
]->ifi
);
862 else if( result
< 0 )
873 mid
+= 1; // Bump index so new item is inserted after matching item.
875 else if( result
> 0 )