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.23 2009/03/30 18:47:40 herscher
21 <rdar://problem/5925472> Current Bonjour code does not compile on Windows
22 <rdar://problem/5187308> Move build train to Visual Studio 2005
24 Revision 1.22 2006/08/14 23:24:00 cheshire
25 Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
27 Revision 1.21 2005/04/06 01:13:07 shersche
28 <rdar://problem/4066195> Use the product icon instead of globe icon for 'About' link.
30 Revision 1.20 2005/03/18 02:43:02 shersche
31 <rdar://problem/4046443> Use standard IE website icon for 'About Bonjour', only using globe icon if standard icon cannot be loaded
33 Revision 1.19 2005/03/16 03:46:27 shersche
34 <rdar://problem/4045657> Use Bonjour icon for all discovered sites
36 Revision 1.18 2005/02/26 01:24:05 shersche
37 Remove display lines in tree control
39 Revision 1.17 2005/02/25 19:57:30 shersche
40 <rdar://problem/4023323> Remove FTP browsing from plugin
42 Revision 1.16 2005/02/08 23:31:06 shersche
43 Move "About ..." item underneath WebSites, change icons for discovered sites and "About ..." item
45 Revision 1.15 2005/01/27 22:38:27 shersche
46 add About item to tree list
48 Revision 1.14 2005/01/25 17:55:39 shersche
49 <rdar://problem/3911084> Get bitmaps from non-localizable resource module
52 Revision 1.13 2005/01/06 21:13:09 shersche
53 <rdar://problem/3796779> Handle kDNSServiceErr_Firewall return codes
56 Revision 1.12 2004/10/26 00:56:03 cheshire
57 Use "#if 0" instead of commenting out code
59 Revision 1.11 2004/10/18 23:49:17 shersche
60 <rdar://problem/3841564> Remove trailing dot from hostname, because some flavors of Windows have difficulty parsing hostnames with a trailing dot.
63 Revision 1.10 2004/09/02 02:18:58 cheshire
64 Minor textual cleanup to improve readability
66 Revision 1.9 2004/09/02 02:11:56 cheshire
67 <rdar://problem/3783611> Fix incorrect testing of MoreComing flag
69 Revision 1.8 2004/07/26 05:47:31 shersche
70 use TXTRecord APIs, fix bug in locating service to be removed
72 Revision 1.7 2004/07/22 16:08:20 shersche
73 clean up debug print statements, re-enable code inadvertently commented out
75 Revision 1.6 2004/07/22 05:27:20 shersche
76 <rdar://problem/3735827> Check to make sure error isn't WSAEWOULDBLOCK when canceling browse
79 Revision 1.5 2004/07/20 06:49:18 shersche
80 clean up socket handling code
82 Revision 1.4 2004/07/13 21:24:21 rpantos
83 Fix for <rdar://problem/3701120>.
85 Revision 1.3 2004/06/27 14:59:59 shersche
86 reference count service info to handle multi-homed hosts
88 Revision 1.2 2004/06/23 16:09:34 shersche
89 Add the resolve DNSServiceRef to list of extant refs. This fixes the "doesn't resolve when double clicking" problem
91 Submitted by: Scott Herscher
93 Revision 1.1 2004/06/18 04:34:59 rpantos
94 Move to Clients from mDNSWindows
96 Revision 1.5 2004/04/15 01:00:05 bradley
97 Removed support for automatically querying for A/AAAA records when resolving names. Platforms
98 without .local name resolving support will need to manually query for A/AAAA records as needed.
100 Revision 1.4 2004/04/09 21:03:15 bradley
101 Changed port numbers to use network byte order for consistency with other platforms.
103 Revision 1.3 2004/04/08 09:43:43 bradley
104 Changed callback calling conventions to __stdcall so they can be used with C# delegates.
106 Revision 1.2 2004/02/21 04:36:19 bradley
107 Enable dot local name lookups now that the NSP is being installed.
109 Revision 1.1 2004/01/30 03:01:56 bradley
110 Explorer Plugin to browse for DNS-SD advertised Web and FTP servers from within Internet Explorer.
116 #include "CommonServices.h"
117 #include "DebugServices.h"
118 #include "WinServices.h"
121 #include "ExplorerBar.h"
122 #include "LoginDialog.h"
123 #include "Resource.h"
125 #include "ExplorerBarWindow.h"
126 #include "ExplorerPlugin.h"
131 #define new DEBUG_NEW
133 static char THIS_FILE
[] = __FILE__
;
137 #pragma mark == Constants ==
140 //===========================================================================================================================
142 //===========================================================================================================================
146 #define IDC_EXPLORER_TREE 1234
150 #define WM_PRIVATE_SERVICE_EVENT ( WM_USER + 0x100 )
154 #define kTXTRecordKeyPath "path"
158 #define kIEIconResource 32529
162 #pragma mark == Prototypes ==
165 //===========================================================================================================================
167 //===========================================================================================================================
169 DEBUG_LOCAL
int FindServiceArrayIndex( const ServiceInfoArray
&inArray
, const ServiceInfo
&inService
, int &outIndex
);
172 #pragma mark == Message Map ==
175 //===========================================================================================================================
177 //===========================================================================================================================
179 BEGIN_MESSAGE_MAP( ExplorerBarWindow
, CWnd
)
183 ON_NOTIFY( NM_DBLCLK
, IDC_EXPLORER_TREE
, OnDoubleClick
)
184 ON_MESSAGE( WM_PRIVATE_SERVICE_EVENT
, OnServiceEvent
)
191 //===========================================================================================================================
193 //===========================================================================================================================
195 ExplorerBarWindow::ExplorerBarWindow( void )
198 mResolveServiceRef
= NULL
;
201 //===========================================================================================================================
202 // ~ExplorerBarWindow
203 //===========================================================================================================================
205 ExplorerBarWindow::~ExplorerBarWindow( void )
214 //===========================================================================================================================
216 //===========================================================================================================================
218 int ExplorerBarWindow::OnCreate( LPCREATESTRUCT inCreateStruct
)
220 AFX_MANAGE_STATE( AfxGetStaticModuleState() );
222 HINSTANCE
module = NULL
;
228 err
= CWnd::OnCreate( inCreateStruct
);
229 require_noerr( err
, exit
);
231 GetClientRect( rect
);
232 mTree
.Create( WS_TABSTOP
| WS_VISIBLE
| WS_CHILD
| TVS_HASBUTTONS
| TVS_LINESATROOT
| TVS_NOHSCROLL
, rect
, this,
235 ServiceHandlerEntry
* e
;
239 e
= new ServiceHandlerEntry
;
241 e
->type
= "_http._tcp";
242 e
->urlScheme
= "http://";
245 e
->needsLogin
= false;
246 mServiceHandlers
.Add( e
);
248 s
.LoadString( IDS_ABOUT
);
249 m_about
= mTree
.InsertItem( s
, 0, 0 );
251 err
= DNSServiceBrowse( &e
->ref
, 0, 0, e
->type
, NULL
, BrowseCallBack
, e
);
252 require_noerr( err
, exit
);
254 err
= WSAAsyncSelect((SOCKET
) DNSServiceRefSockFD(e
->ref
), m_hWnd
, WM_PRIVATE_SERVICE_EVENT
, FD_READ
|FD_CLOSE
);
255 require_noerr( err
, exit
);
257 m_serviceRefs
.push_back(e
->ref
);
259 m_imageList
.Create( 16, 16, ILC_MASK
| ILC_COLOR16
, 2, 0);
261 bitmap
.Attach( ::LoadBitmap( GetNonLocalizedResources(), MAKEINTRESOURCE( IDB_LOGO
) ) );
262 m_imageList
.Add( &bitmap
, (CBitmap
*) NULL
);
265 mTree
.SetImageList(&m_imageList
, TVSIL_NORMAL
);
271 FreeLibrary( module );
275 // Cannot talk to the mDNSResponder service. Show the error message and exit (with kNoErr so they can see it).
278 if ( err
== kDNSServiceErr_Firewall
)
280 s
.LoadString( IDS_FIREWALL
);
284 s
.LoadString( IDS_MDNSRESPONDER_NOT_AVAILABLE
);
287 mTree
.DeleteAllItems();
288 mTree
.InsertItem( s
, 0, 0, TVI_ROOT
, TVI_LAST
);
296 //===========================================================================================================================
298 //===========================================================================================================================
300 void ExplorerBarWindow::OnDestroy( void )
302 // Stop any resolves that may still be pending (shouldn't be any).
306 // Clean up the extant browses
307 while (m_serviceRefs
.size() > 0)
310 // take the head of the list
312 DNSServiceRef ref
= m_serviceRefs
.front();
315 // Stop will remove it from the list
320 // Clean up the service handlers.
325 n
= (int) mServiceHandlers
.GetSize();
326 for( i
= 0; i
< n
; ++i
)
328 delete mServiceHandlers
[ i
];
334 //===========================================================================================================================
336 //===========================================================================================================================
338 void ExplorerBarWindow::OnSize( UINT inType
, int inX
, int inY
)
340 CWnd::OnSize( inType
, inX
, inY
);
341 mTree
.MoveWindow( 0, 0, inX
, inY
);
344 //===========================================================================================================================
346 //===========================================================================================================================
348 void ExplorerBarWindow::OnDoubleClick( NMHDR
*inNMHDR
, LRESULT
*outResult
)
351 ServiceInfo
* service
;
354 DEBUG_UNUSED( inNMHDR
);
356 item
= mTree
.GetSelectedItem();
357 require( item
, exit
);
359 // Tell Internet Explorer to go to the URL if it's about item
361 if ( item
== m_about
)
367 url
.LoadString( IDS_ABOUT_URL
);
368 mOwner
->GoToURL( url
);
372 service
= reinterpret_cast < ServiceInfo
* > ( mTree
.GetItemData( item
) );
373 require_quiet( service
, exit
);
375 err
= StartResolve( service
);
376 require_noerr( err
, exit
);
384 //===========================================================================================================================
386 //===========================================================================================================================
389 ExplorerBarWindow::OnServiceEvent(WPARAM inWParam
, LPARAM inLParam
)
391 if (WSAGETSELECTERROR(inLParam
) && !(HIWORD(inLParam
)))
393 dlog( kDebugLevelError
, "OnServiceEvent: window error\n" );
397 SOCKET sock
= (SOCKET
) inWParam
;
400 ServiceRefList::iterator it
;
402 for (it
= m_serviceRefs
.begin(); it
!= m_serviceRefs
.end(); it
++)
404 DNSServiceRef ref
= *it
;
408 if ((SOCKET
) DNSServiceRefSockFD(ref
) == sock
)
410 DNSServiceErrorType err
;
412 err
= DNSServiceProcessResult(ref
);
418 s
.LoadString( IDS_MDNSRESPONDER_NOT_AVAILABLE
);
419 mTree
.DeleteAllItems();
420 mTree
.InsertItem( s
, 0, 0, TVI_ROOT
, TVI_LAST
);
437 //===========================================================================================================================
439 //===========================================================================================================================
442 ExplorerBarWindow::BrowseCallBack(
444 DNSServiceFlags inFlags
,
445 uint32_t inInterfaceIndex
,
446 DNSServiceErrorType inErrorCode
,
449 const char * inDomain
,
452 ServiceHandlerEntry
* obj
;
453 ServiceInfo
* service
;
456 DEBUG_UNUSED( inRef
);
461 require_noerr( inErrorCode
, exit
);
462 obj
= reinterpret_cast < ServiceHandlerEntry
* > ( inContext
);
467 // set the UI to hold off on updates
469 obj
->obj
->mTree
.SetRedraw(FALSE
);
473 service
= new ServiceInfo
;
474 require_action( service
, exit
, err
= kNoMemoryErr
);
476 err
= UTF8StringToStringObject( inName
, service
->displayName
);
479 service
->name
= _strdup( inName
);
480 require_action( service
->name
, exit
, err
= kNoMemoryErr
);
482 service
->type
= _strdup( inType
);
483 require_action( service
->type
, exit
, err
= kNoMemoryErr
);
485 service
->domain
= _strdup( inDomain
);
486 require_action( service
->domain
, exit
, err
= kNoMemoryErr
);
488 service
->ifi
= inInterfaceIndex
;
489 service
->handler
= obj
;
493 if (inFlags
& kDNSServiceFlagsAdd
) obj
->obj
->OnServiceAdd (service
);
494 else obj
->obj
->OnServiceRemove(service
);
500 dlog( kDebugLevelError
, "BrowseCallBack: exception thrown\n" );
505 // If no more coming, then update UI
507 if (obj
&& obj
->obj
&& ((inFlags
& kDNSServiceFlagsMoreComing
) == 0))
509 obj
->obj
->mTree
.SetRedraw(TRUE
);
510 obj
->obj
->mTree
.Invalidate();
519 //===========================================================================================================================
521 //===========================================================================================================================
523 LONG
ExplorerBarWindow::OnServiceAdd( ServiceInfo
* service
)
525 ServiceHandlerEntry
* handler
;
531 handler
= service
->handler
;
534 cmp
= FindServiceArrayIndex( handler
->array
, *service
, index
);
537 // Found a match so update the item. The index is index + 1 so subtract 1.
540 check( index
< handler
->array
.GetSize() );
542 handler
->array
[ index
]->refs
++;
550 // Insert the new item in sorted order.
552 afterItem
= ( index
> 0 ) ? handler
->array
[ index
- 1 ]->item
: m_about
;
553 handler
->array
.InsertAt( index
, service
);
554 service
->item
= mTree
.InsertItem( service
->displayName
, 0, 0, NULL
, afterItem
);
555 mTree
.SetItemData( service
->item
, (DWORD_PTR
) service
);
560 //===========================================================================================================================
562 //===========================================================================================================================
564 LONG
ExplorerBarWindow::OnServiceRemove( ServiceInfo
* service
)
566 ServiceHandlerEntry
* handler
;
572 handler
= service
->handler
;
575 // Search to see if we know about this service instance. If so, remove it from the list.
577 cmp
= FindServiceArrayIndex( handler
->array
, *service
, index
);
582 // Possibly found a match remove the item. The index
583 // is index + 1 so subtract 1.
585 check( index
< handler
->array
.GetSize() );
587 if ( --handler
->array
[ index
]->refs
== 0 )
589 mTree
.DeleteItem( handler
->array
[ index
]->item
);
590 delete handler
->array
[ index
];
591 handler
->array
.RemoveAt( index
);
603 //===========================================================================================================================
605 //===========================================================================================================================
607 OSStatus
ExplorerBarWindow::StartResolve( ServiceInfo
*inService
)
613 // Stop any current resolve that may be in progress.
617 // Resolve the service.
618 err
= DNSServiceResolve( &mResolveServiceRef
, 0, 0,
619 inService
->name
, inService
->type
, inService
->domain
, (DNSServiceResolveReply
) ResolveCallBack
, inService
->handler
);
620 require_noerr( err
, exit
);
622 err
= WSAAsyncSelect((SOCKET
) DNSServiceRefSockFD(mResolveServiceRef
), m_hWnd
, WM_PRIVATE_SERVICE_EVENT
, FD_READ
|FD_CLOSE
);
623 require_noerr( err
, exit
);
625 m_serviceRefs
.push_back(mResolveServiceRef
);
631 //===========================================================================================================================
633 //===========================================================================================================================
635 void ExplorerBarWindow::StopResolve( void )
637 if( mResolveServiceRef
)
639 Stop( mResolveServiceRef
);
640 mResolveServiceRef
= NULL
;
644 //===========================================================================================================================
646 //===========================================================================================================================
649 ExplorerBarWindow::ResolveCallBack(
651 DNSServiceFlags inFlags
,
652 uint32_t inInterfaceIndex
,
653 DNSServiceErrorType inErrorCode
,
654 const char * inFullName
,
655 const char * inHostName
,
661 ExplorerBarWindow
* obj
;
662 ServiceHandlerEntry
* handler
;
665 DEBUG_UNUSED( inRef
);
666 DEBUG_UNUSED( inFlags
);
667 DEBUG_UNUSED( inErrorCode
);
668 DEBUG_UNUSED( inFullName
);
670 require_noerr( inErrorCode
, exit
);
671 handler
= (ServiceHandlerEntry
*) inContext
;
678 ResolveInfo
* resolve
;
681 dlog( kDebugLevelNotice
, "resolved %s on ifi %d to %s\n", inFullName
, inInterfaceIndex
, inHostName
);
683 // Stop resolving after the first good result.
687 // Post a message to the main thread so it can handle it since MFC is not thread safe.
689 resolve
= new ResolveInfo
;
690 require_action( resolve
, exit
, err
= kNoMemoryErr
);
692 UTF8StringToStringObject( inHostName
, resolve
->host
);
694 // rdar://problem/3841564
696 // strip trailing dot from hostname because some flavors of Windows
697 // have trouble parsing it.
699 idx
= resolve
->host
.ReverseFind('.');
701 if ((idx
> 1) && ((resolve
->host
.GetLength() - 1) == idx
))
703 resolve
->host
.Delete(idx
, 1);
706 resolve
->port
= ntohs( inPort
);
707 resolve
->ifi
= inInterfaceIndex
;
708 resolve
->handler
= handler
;
710 err
= resolve
->txt
.SetData( inTXT
, inTXTSize
);
713 obj
->OnResolve(resolve
);
717 dlog( kDebugLevelError
, "ResolveCallBack: exception thrown\n" );
724 //===========================================================================================================================
726 //===========================================================================================================================
728 LONG
ExplorerBarWindow::OnResolve( ResolveInfo
* resolve
)
740 // Get login info if needed.
742 if( resolve
->handler
->needsLogin
)
746 if( !dialog
.GetLogin( username
, password
) )
752 // If the HTTP TXT record is a "path=" entry, use it as the resource path. Otherwise, use "/".
755 if( strcmp( resolve
->handler
->type
, "_http._tcp" ) == 0 )
760 resolve
->txt
.GetData( &txtData
, &txtLen
);
762 path
= (uint8_t*) TXTRecordGetValuePtr(txtLen
, txtData
, kTXTRecordKeyPath
, &pathSize
);
766 path
= (uint8_t*) "";
772 path
= (uint8_t *) "";
776 // Build the URL in the following format:
778 // <urlScheme>[<username>[:<password>]@]<name/ip>[<path>]
780 url
.AppendFormat( TEXT( "%S" ), resolve
->handler
->urlScheme
); // URL Scheme
781 if( username
.GetLength() > 0 )
783 url
.AppendFormat( TEXT( "%s" ), username
); // Username
784 if( password
.GetLength() > 0 )
786 url
.AppendFormat( TEXT( ":%s" ), password
); // Password
788 url
.AppendFormat( TEXT( "@" ) );
791 url
+= resolve
->host
; // Host
792 url
.AppendFormat( TEXT( ":%d" ), resolve
->port
); // :Port
793 url
.AppendFormat( TEXT( "%S" ), pathPrefix
); // Path Prefix ("/" or empty).
794 url
.AppendFormat( TEXT( "%.*S" ), (int) pathSize
, (char *) path
); // Path (possibly empty).
796 // Tell Internet Explorer to go to the URL.
799 mOwner
->GoToURL( url
);
806 //===========================================================================================================================
808 //===========================================================================================================================
809 void ExplorerBarWindow::Stop( DNSServiceRef ref
)
811 m_serviceRefs
.remove( ref
);
813 WSAAsyncSelect(DNSServiceRefSockFD( ref
), m_hWnd
, WM_PRIVATE_SERVICE_EVENT
, 0);
815 DNSServiceRefDeallocate( ref
);
823 //===========================================================================================================================
824 // FindServiceArrayIndex
825 //===========================================================================================================================
827 DEBUG_LOCAL
int FindServiceArrayIndex( const ServiceInfoArray
&inArray
, const ServiceInfo
&inService
, int &outIndex
)
837 hi
= (int)( inArray
.GetSize() - 1 );
840 mid
= ( lo
+ hi
) / 2;
841 result
= inService
.displayName
.CompareNoCase( inArray
[ mid
]->displayName
);
845 result
= ( (int) inService
.ifi
) - ( (int) inArray
[ mid
]->ifi
);
852 else if( result
< 0 )
863 mid
+= 1; // Bump index so new item is inserted after matching item.
865 else if( result
> 0 )