1 /* -*- Mode: C; tab-width: 4 -*-
3 * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved.
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 Change History (most recent first):
19 $Log: ExplorerBarWindow.cpp,v $
20 Revision 1.22 2006/08/14 23:24:00 cheshire
21 Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
23 Revision 1.21 2005/04/06 01:13:07 shersche
24 <rdar://problem/4066195> Use the product icon instead of globe icon for 'About' link.
26 Revision 1.20 2005/03/18 02:43:02 shersche
27 <rdar://problem/4046443> Use standard IE website icon for 'About Bonjour', only using globe icon if standard icon cannot be loaded
29 Revision 1.19 2005/03/16 03:46:27 shersche
30 <rdar://problem/4045657> Use Bonjour icon for all discovered sites
32 Revision 1.18 2005/02/26 01:24:05 shersche
33 Remove display lines in tree control
35 Revision 1.17 2005/02/25 19:57:30 shersche
36 <rdar://problem/4023323> Remove FTP browsing from plugin
38 Revision 1.16 2005/02/08 23:31:06 shersche
39 Move "About ..." item underneath WebSites, change icons for discovered sites and "About ..." item
41 Revision 1.15 2005/01/27 22:38:27 shersche
42 add About item to tree list
44 Revision 1.14 2005/01/25 17:55:39 shersche
45 <rdar://problem/3911084> Get bitmaps from non-localizable resource module
48 Revision 1.13 2005/01/06 21:13:09 shersche
49 <rdar://problem/3796779> Handle kDNSServiceErr_Firewall return codes
52 Revision 1.12 2004/10/26 00:56:03 cheshire
53 Use "#if 0" instead of commenting out code
55 Revision 1.11 2004/10/18 23:49:17 shersche
56 <rdar://problem/3841564> Remove trailing dot from hostname, because some flavors of Windows have difficulty parsing hostnames with a trailing dot.
59 Revision 1.10 2004/09/02 02:18:58 cheshire
60 Minor textual cleanup to improve readability
62 Revision 1.9 2004/09/02 02:11:56 cheshire
63 <rdar://problem/3783611> Fix incorrect testing of MoreComing flag
65 Revision 1.8 2004/07/26 05:47:31 shersche
66 use TXTRecord APIs, fix bug in locating service to be removed
68 Revision 1.7 2004/07/22 16:08:20 shersche
69 clean up debug print statements, re-enable code inadvertently commented out
71 Revision 1.6 2004/07/22 05:27:20 shersche
72 <rdar://problem/3735827> Check to make sure error isn't WSAEWOULDBLOCK when canceling browse
75 Revision 1.5 2004/07/20 06:49:18 shersche
76 clean up socket handling code
78 Revision 1.4 2004/07/13 21:24:21 rpantos
79 Fix for <rdar://problem/3701120>.
81 Revision 1.3 2004/06/27 14:59:59 shersche
82 reference count service info to handle multi-homed hosts
84 Revision 1.2 2004/06/23 16:09:34 shersche
85 Add the resolve DNSServiceRef to list of extant refs. This fixes the "doesn't resolve when double clicking" problem
87 Submitted by: Scott Herscher
89 Revision 1.1 2004/06/18 04:34:59 rpantos
90 Move to Clients from mDNSWindows
92 Revision 1.5 2004/04/15 01:00:05 bradley
93 Removed support for automatically querying for A/AAAA records when resolving names. Platforms
94 without .local name resolving support will need to manually query for A/AAAA records as needed.
96 Revision 1.4 2004/04/09 21:03:15 bradley
97 Changed port numbers to use network byte order for consistency with other platforms.
99 Revision 1.3 2004/04/08 09:43:43 bradley
100 Changed callback calling conventions to __stdcall so they can be used with C# delegates.
102 Revision 1.2 2004/02/21 04:36:19 bradley
103 Enable dot local name lookups now that the NSP is being installed.
105 Revision 1.1 2004/01/30 03:01:56 bradley
106 Explorer Plugin to browse for DNS-SD advertised Web and FTP servers from within Internet Explorer.
112 #include "CommonServices.h"
113 #include "DebugServices.h"
114 #include "WinServices.h"
117 #include "ExplorerBar.h"
118 #include "LoginDialog.h"
119 #include "Resource.h"
121 #include "ExplorerBarWindow.h"
122 #include "ExplorerPlugin.h"
127 #define new DEBUG_NEW
129 static char THIS_FILE
[] = __FILE__
;
133 #pragma mark == Constants ==
136 //===========================================================================================================================
138 //===========================================================================================================================
142 #define IDC_EXPLORER_TREE 1234
146 #define WM_PRIVATE_SERVICE_EVENT ( WM_USER + 0x100 )
150 #define kTXTRecordKeyPath "path"
154 #define kIEIconResource 32529
158 #pragma mark == Prototypes ==
161 //===========================================================================================================================
163 //===========================================================================================================================
165 DEBUG_LOCAL
int FindServiceArrayIndex( const ServiceInfoArray
&inArray
, const ServiceInfo
&inService
, int &outIndex
);
168 #pragma mark == Message Map ==
171 //===========================================================================================================================
173 //===========================================================================================================================
175 BEGIN_MESSAGE_MAP( ExplorerBarWindow
, CWnd
)
179 ON_NOTIFY( NM_DBLCLK
, IDC_EXPLORER_TREE
, OnDoubleClick
)
180 ON_MESSAGE( WM_PRIVATE_SERVICE_EVENT
, OnServiceEvent
)
187 //===========================================================================================================================
189 //===========================================================================================================================
191 ExplorerBarWindow::ExplorerBarWindow( void )
194 mResolveServiceRef
= NULL
;
197 //===========================================================================================================================
198 // ~ExplorerBarWindow
199 //===========================================================================================================================
201 ExplorerBarWindow::~ExplorerBarWindow( void )
210 //===========================================================================================================================
212 //===========================================================================================================================
214 int ExplorerBarWindow::OnCreate( LPCREATESTRUCT inCreateStruct
)
216 AFX_MANAGE_STATE( AfxGetStaticModuleState() );
218 HINSTANCE
module = NULL
;
224 err
= CWnd::OnCreate( inCreateStruct
);
225 require_noerr( err
, exit
);
227 GetClientRect( rect
);
228 mTree
.Create( WS_TABSTOP
| WS_VISIBLE
| WS_CHILD
| TVS_HASBUTTONS
| TVS_LINESATROOT
| TVS_NOHSCROLL
, rect
, this,
231 ServiceHandlerEntry
* e
;
235 e
= new ServiceHandlerEntry
;
237 e
->type
= "_http._tcp";
238 e
->urlScheme
= "http://";
241 e
->needsLogin
= false;
242 mServiceHandlers
.Add( e
);
244 s
.LoadString( IDS_ABOUT
);
245 m_about
= mTree
.InsertItem( s
, 0, 0 );
247 err
= DNSServiceBrowse( &e
->ref
, 0, 0, e
->type
, NULL
, BrowseCallBack
, e
);
248 require_noerr( err
, exit
);
250 err
= WSAAsyncSelect((SOCKET
) DNSServiceRefSockFD(e
->ref
), m_hWnd
, WM_PRIVATE_SERVICE_EVENT
, FD_READ
|FD_CLOSE
);
251 require_noerr( err
, exit
);
253 m_serviceRefs
.push_back(e
->ref
);
255 m_imageList
.Create( 16, 16, ILC_MASK
| ILC_COLOR16
, 2, 0);
257 bitmap
.Attach( ::LoadBitmap( GetNonLocalizedResources(), MAKEINTRESOURCE( IDB_LOGO
) ) );
258 m_imageList
.Add( &bitmap
, (CBitmap
*) NULL
);
261 mTree
.SetImageList(&m_imageList
, TVSIL_NORMAL
);
267 FreeLibrary( module );
271 // Cannot talk to the mDNSResponder service. Show the error message and exit (with kNoErr so they can see it).
274 if ( err
== kDNSServiceErr_Firewall
)
276 s
.LoadString( IDS_FIREWALL
);
280 s
.LoadString( IDS_MDNSRESPONDER_NOT_AVAILABLE
);
283 mTree
.DeleteAllItems();
284 mTree
.InsertItem( s
, 0, 0, TVI_ROOT
, TVI_LAST
);
292 //===========================================================================================================================
294 //===========================================================================================================================
296 void ExplorerBarWindow::OnDestroy( void )
298 // Stop any resolves that may still be pending (shouldn't be any).
302 // Clean up the extant browses
303 while (m_serviceRefs
.size() > 0)
306 // take the head of the list
308 DNSServiceRef ref
= m_serviceRefs
.front();
311 // Stop will remove it from the list
316 // Clean up the service handlers.
321 n
= (int) mServiceHandlers
.GetSize();
322 for( i
= 0; i
< n
; ++i
)
324 delete mServiceHandlers
[ i
];
330 //===========================================================================================================================
332 //===========================================================================================================================
334 void ExplorerBarWindow::OnSize( UINT inType
, int inX
, int inY
)
336 CWnd::OnSize( inType
, inX
, inY
);
337 mTree
.MoveWindow( 0, 0, inX
, inY
);
340 //===========================================================================================================================
342 //===========================================================================================================================
344 void ExplorerBarWindow::OnDoubleClick( NMHDR
*inNMHDR
, LRESULT
*outResult
)
347 ServiceInfo
* service
;
350 DEBUG_UNUSED( inNMHDR
);
352 item
= mTree
.GetSelectedItem();
353 require( item
, exit
);
355 // Tell Internet Explorer to go to the URL if it's about item
357 if ( item
== m_about
)
363 url
.LoadString( IDS_ABOUT_URL
);
364 mOwner
->GoToURL( url
);
368 service
= reinterpret_cast < ServiceInfo
* > ( mTree
.GetItemData( item
) );
369 require_quiet( service
, exit
);
371 err
= StartResolve( service
);
372 require_noerr( err
, exit
);
380 //===========================================================================================================================
382 //===========================================================================================================================
385 ExplorerBarWindow::OnServiceEvent(WPARAM inWParam
, LPARAM inLParam
)
387 if (WSAGETSELECTERROR(inLParam
) && !(HIWORD(inLParam
)))
389 dlog( kDebugLevelError
, "OnServiceEvent: window error\n" );
393 SOCKET sock
= (SOCKET
) inWParam
;
396 ServiceRefList::iterator it
;
398 for (it
= m_serviceRefs
.begin(); it
!= m_serviceRefs
.end(); it
++)
400 DNSServiceRef ref
= *it
;
404 if ((SOCKET
) DNSServiceRefSockFD(ref
) == sock
)
406 DNSServiceErrorType err
;
408 err
= DNSServiceProcessResult(ref
);
414 s
.LoadString( IDS_MDNSRESPONDER_NOT_AVAILABLE
);
415 mTree
.DeleteAllItems();
416 mTree
.InsertItem( s
, 0, 0, TVI_ROOT
, TVI_LAST
);
433 //===========================================================================================================================
435 //===========================================================================================================================
438 ExplorerBarWindow::BrowseCallBack(
440 DNSServiceFlags inFlags
,
441 uint32_t inInterfaceIndex
,
442 DNSServiceErrorType inErrorCode
,
445 const char * inDomain
,
448 ServiceHandlerEntry
* obj
;
449 ServiceInfo
* service
;
452 DEBUG_UNUSED( inRef
);
457 require_noerr( inErrorCode
, exit
);
458 obj
= reinterpret_cast < ServiceHandlerEntry
* > ( inContext
);
463 // set the UI to hold off on updates
465 obj
->obj
->mTree
.SetRedraw(FALSE
);
469 service
= new ServiceInfo
;
470 require_action( service
, exit
, err
= kNoMemoryErr
);
472 err
= UTF8StringToStringObject( inName
, service
->displayName
);
475 service
->name
= strdup( inName
);
476 require_action( service
->name
, exit
, err
= kNoMemoryErr
);
478 service
->type
= strdup( inType
);
479 require_action( service
->type
, exit
, err
= kNoMemoryErr
);
481 service
->domain
= strdup( inDomain
);
482 require_action( service
->domain
, exit
, err
= kNoMemoryErr
);
484 service
->ifi
= inInterfaceIndex
;
485 service
->handler
= obj
;
489 if (inFlags
& kDNSServiceFlagsAdd
) obj
->obj
->OnServiceAdd (service
);
490 else obj
->obj
->OnServiceRemove(service
);
496 dlog( kDebugLevelError
, "BrowseCallBack: exception thrown\n" );
501 // If no more coming, then update UI
503 if (obj
&& obj
->obj
&& ((inFlags
& kDNSServiceFlagsMoreComing
) == 0))
505 obj
->obj
->mTree
.SetRedraw(TRUE
);
506 obj
->obj
->mTree
.Invalidate();
515 //===========================================================================================================================
517 //===========================================================================================================================
519 LONG
ExplorerBarWindow::OnServiceAdd( ServiceInfo
* service
)
521 ServiceHandlerEntry
* handler
;
527 handler
= service
->handler
;
530 cmp
= FindServiceArrayIndex( handler
->array
, *service
, index
);
533 // Found a match so update the item. The index is index + 1 so subtract 1.
536 check( index
< handler
->array
.GetSize() );
538 handler
->array
[ index
]->refs
++;
546 // Insert the new item in sorted order.
548 afterItem
= ( index
> 0 ) ? handler
->array
[ index
- 1 ]->item
: m_about
;
549 handler
->array
.InsertAt( index
, service
);
550 service
->item
= mTree
.InsertItem( service
->displayName
, 0, 0, NULL
, afterItem
);
551 mTree
.SetItemData( service
->item
, (DWORD_PTR
) service
);
556 //===========================================================================================================================
558 //===========================================================================================================================
560 LONG
ExplorerBarWindow::OnServiceRemove( ServiceInfo
* service
)
562 ServiceHandlerEntry
* handler
;
568 handler
= service
->handler
;
571 // Search to see if we know about this service instance. If so, remove it from the list.
573 cmp
= FindServiceArrayIndex( handler
->array
, *service
, index
);
578 // Possibly found a match remove the item. The index
579 // is index + 1 so subtract 1.
581 check( index
< handler
->array
.GetSize() );
583 if ( --handler
->array
[ index
]->refs
== 0 )
585 mTree
.DeleteItem( handler
->array
[ index
]->item
);
586 delete handler
->array
[ index
];
587 handler
->array
.RemoveAt( index
);
599 //===========================================================================================================================
601 //===========================================================================================================================
603 OSStatus
ExplorerBarWindow::StartResolve( ServiceInfo
*inService
)
609 // Stop any current resolve that may be in progress.
613 // Resolve the service.
614 err
= DNSServiceResolve( &mResolveServiceRef
, 0, 0,
615 inService
->name
, inService
->type
, inService
->domain
, (DNSServiceResolveReply
) ResolveCallBack
, inService
->handler
);
616 require_noerr( err
, exit
);
618 err
= WSAAsyncSelect((SOCKET
) DNSServiceRefSockFD(mResolveServiceRef
), m_hWnd
, WM_PRIVATE_SERVICE_EVENT
, FD_READ
|FD_CLOSE
);
619 require_noerr( err
, exit
);
621 m_serviceRefs
.push_back(mResolveServiceRef
);
627 //===========================================================================================================================
629 //===========================================================================================================================
631 void ExplorerBarWindow::StopResolve( void )
633 if( mResolveServiceRef
)
635 Stop( mResolveServiceRef
);
636 mResolveServiceRef
= NULL
;
640 //===========================================================================================================================
642 //===========================================================================================================================
645 ExplorerBarWindow::ResolveCallBack(
647 DNSServiceFlags inFlags
,
648 uint32_t inInterfaceIndex
,
649 DNSServiceErrorType inErrorCode
,
650 const char * inFullName
,
651 const char * inHostName
,
657 ExplorerBarWindow
* obj
;
658 ServiceHandlerEntry
* handler
;
661 DEBUG_UNUSED( inRef
);
662 DEBUG_UNUSED( inFlags
);
663 DEBUG_UNUSED( inErrorCode
);
664 DEBUG_UNUSED( inFullName
);
666 require_noerr( inErrorCode
, exit
);
667 handler
= (ServiceHandlerEntry
*) inContext
;
674 ResolveInfo
* resolve
;
677 dlog( kDebugLevelNotice
, "resolved %s on ifi %d to %s\n", inFullName
, inInterfaceIndex
, inHostName
);
679 // Stop resolving after the first good result.
683 // Post a message to the main thread so it can handle it since MFC is not thread safe.
685 resolve
= new ResolveInfo
;
686 require_action( resolve
, exit
, err
= kNoMemoryErr
);
688 UTF8StringToStringObject( inHostName
, resolve
->host
);
690 // rdar://problem/3841564
692 // strip trailing dot from hostname because some flavors of Windows
693 // have trouble parsing it.
695 idx
= resolve
->host
.ReverseFind('.');
697 if ((idx
> 1) && ((resolve
->host
.GetLength() - 1) == idx
))
699 resolve
->host
.Delete(idx
, 1);
702 resolve
->port
= ntohs( inPort
);
703 resolve
->ifi
= inInterfaceIndex
;
704 resolve
->handler
= handler
;
706 err
= resolve
->txt
.SetData( inTXT
, inTXTSize
);
709 obj
->OnResolve(resolve
);
713 dlog( kDebugLevelError
, "ResolveCallBack: exception thrown\n" );
720 //===========================================================================================================================
722 //===========================================================================================================================
724 LONG
ExplorerBarWindow::OnResolve( ResolveInfo
* resolve
)
736 // Get login info if needed.
738 if( resolve
->handler
->needsLogin
)
742 if( !dialog
.GetLogin( username
, password
) )
748 // If the HTTP TXT record is a "path=" entry, use it as the resource path. Otherwise, use "/".
751 if( strcmp( resolve
->handler
->type
, "_http._tcp" ) == 0 )
756 resolve
->txt
.GetData( &txtData
, &txtLen
);
758 path
= (uint8_t*) TXTRecordGetValuePtr(txtLen
, txtData
, kTXTRecordKeyPath
, &pathSize
);
762 path
= (uint8_t*) "";
768 path
= (uint8_t *) "";
772 // Build the URL in the following format:
774 // <urlScheme>[<username>[:<password>]@]<name/ip>[<path>]
776 url
.AppendFormat( TEXT( "%S" ), resolve
->handler
->urlScheme
); // URL Scheme
777 if( username
.GetLength() > 0 )
779 url
.AppendFormat( TEXT( "%s" ), username
); // Username
780 if( password
.GetLength() > 0 )
782 url
.AppendFormat( TEXT( ":%s" ), password
); // Password
784 url
.AppendFormat( TEXT( "@" ) );
787 url
+= resolve
->host
; // Host
788 url
.AppendFormat( TEXT( ":%d" ), resolve
->port
); // :Port
789 url
.AppendFormat( TEXT( "%S" ), pathPrefix
); // Path Prefix ("/" or empty).
790 url
.AppendFormat( TEXT( "%.*S" ), (int) pathSize
, (char *) path
); // Path (possibly empty).
792 // Tell Internet Explorer to go to the URL.
795 mOwner
->GoToURL( url
);
802 //===========================================================================================================================
804 //===========================================================================================================================
805 void ExplorerBarWindow::Stop( DNSServiceRef ref
)
807 m_serviceRefs
.remove( ref
);
809 WSAAsyncSelect(DNSServiceRefSockFD( ref
), m_hWnd
, WM_PRIVATE_SERVICE_EVENT
, 0);
811 DNSServiceRefDeallocate( ref
);
819 //===========================================================================================================================
820 // FindServiceArrayIndex
821 //===========================================================================================================================
823 DEBUG_LOCAL
int FindServiceArrayIndex( const ServiceInfoArray
&inArray
, const ServiceInfo
&inService
, int &outIndex
)
833 hi
= (int)( inArray
.GetSize() - 1 );
836 mid
= ( lo
+ hi
) / 2;
837 result
= inService
.displayName
.CompareNoCase( inArray
[ mid
]->displayName
);
841 result
= ( (int) inService
.ifi
) - ( (int) inArray
[ mid
]->ifi
);
848 else if( result
< 0 )
859 mid
+= 1; // Bump index so new item is inserted after matching item.
861 else if( result
> 0 )