X-Git-Url: https://git.saurik.com/apple/mdnsresponder.git/blobdiff_plain/8e92c31c9a45a66732f5bc7afbc9f5596c17e91d..7f0064bd55e3fa98568d2c359429ff8a38b23a6c:/Clients/ExplorerPlugin/ExplorerBarWindow.cpp diff --git a/Clients/ExplorerPlugin/ExplorerBarWindow.cpp b/Clients/ExplorerPlugin/ExplorerBarWindow.cpp new file mode 100644 index 0000000..beb0b35 --- /dev/null +++ b/Clients/ExplorerPlugin/ExplorerBarWindow.cpp @@ -0,0 +1,840 @@ +/* + * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: ExplorerBarWindow.cpp,v $ +Revision 1.12 2004/10/26 00:56:03 cheshire +Use "#if 0" instead of commenting out code + +Revision 1.11 2004/10/18 23:49:17 shersche + Remove trailing dot from hostname, because some flavors of Windows have difficulty parsing hostnames with a trailing dot. +Bug #: 3841564 + +Revision 1.10 2004/09/02 02:18:58 cheshire +Minor textual cleanup to improve readability + +Revision 1.9 2004/09/02 02:11:56 cheshire + Fix incorrect testing of MoreComing flag + +Revision 1.8 2004/07/26 05:47:31 shersche +use TXTRecord APIs, fix bug in locating service to be removed + +Revision 1.7 2004/07/22 16:08:20 shersche +clean up debug print statements, re-enable code inadvertently commented out + +Revision 1.6 2004/07/22 05:27:20 shersche + Check to make sure error isn't WSAEWOULDBLOCK when canceling browse +Bug #: 3735827 + +Revision 1.5 2004/07/20 06:49:18 shersche +clean up socket handling code + +Revision 1.4 2004/07/13 21:24:21 rpantos +Fix for . + +Revision 1.3 2004/06/27 14:59:59 shersche +reference count service info to handle multi-homed hosts + +Revision 1.2 2004/06/23 16:09:34 shersche +Add the resolve DNSServiceRef to list of extant refs. This fixes the "doesn't resolve when double clicking" problem + +Submitted by: Scott Herscher + +Revision 1.1 2004/06/18 04:34:59 rpantos +Move to Clients from mDNSWindows + +Revision 1.5 2004/04/15 01:00:05 bradley +Removed support for automatically querying for A/AAAA records when resolving names. Platforms +without .local name resolving support will need to manually query for A/AAAA records as needed. + +Revision 1.4 2004/04/09 21:03:15 bradley +Changed port numbers to use network byte order for consistency with other platforms. + +Revision 1.3 2004/04/08 09:43:43 bradley +Changed callback calling conventions to __stdcall so they can be used with C# delegates. + +Revision 1.2 2004/02/21 04:36:19 bradley +Enable dot local name lookups now that the NSP is being installed. + +Revision 1.1 2004/01/30 03:01:56 bradley +Explorer Plugin to browse for DNS-SD advertised Web and FTP servers from within Internet Explorer. + +*/ + +#include "StdAfx.h" + +#include "CommonServices.h" +#include "DebugServices.h" +#include "WinServices.h" +#include "dns_sd.h" + +#include "ExplorerBar.h" +#include "LoginDialog.h" +#include "Resource.h" + +#include "ExplorerBarWindow.h" + +// MFC Debugging + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +#if 0 +#pragma mark == Constants == +#endif + +//=========================================================================================================================== +// Constants +//=========================================================================================================================== + +// Control IDs + +#define IDC_EXPLORER_TREE 1234 + +// Private Messages + +#define WM_PRIVATE_SERVICE_EVENT ( WM_USER + 0x100 ) + +// TXT records + +#define kTXTRecordKeyPath "path" + +#if 0 +#pragma mark == Prototypes == +#endif + +//=========================================================================================================================== +// Prototypes +//=========================================================================================================================== + +DEBUG_LOCAL int FindServiceArrayIndex( const ServiceInfoArray &inArray, const ServiceInfo &inService, int &outIndex ); + +#if 0 +#pragma mark == Message Map == +#endif + +//=========================================================================================================================== +// Message Map +//=========================================================================================================================== + +BEGIN_MESSAGE_MAP( ExplorerBarWindow, CWnd ) + ON_WM_CREATE() + ON_WM_DESTROY() + ON_WM_SIZE() + ON_NOTIFY( NM_DBLCLK, IDC_EXPLORER_TREE, OnDoubleClick ) + ON_MESSAGE( WM_PRIVATE_SERVICE_EVENT, OnServiceEvent ) +END_MESSAGE_MAP() + +#if 0 +#pragma mark - +#endif + +//=========================================================================================================================== +// ExplorerBarWindow +//=========================================================================================================================== + +ExplorerBarWindow::ExplorerBarWindow( void ) +{ + mOwner = NULL; + mResolveServiceRef = NULL; +} + +//=========================================================================================================================== +// ~ExplorerBarWindow +//=========================================================================================================================== + +ExplorerBarWindow::~ExplorerBarWindow( void ) +{ + // +} + +#if 0 +#pragma mark - +#endif + +//=========================================================================================================================== +// OnCreate +//=========================================================================================================================== + +int ExplorerBarWindow::OnCreate( LPCREATESTRUCT inCreateStruct ) +{ + AFX_MANAGE_STATE( AfxGetStaticModuleState() ); + + OSStatus err; + CRect rect; + CBitmap bitmap; + CString s; + + err = CWnd::OnCreate( inCreateStruct ); + require_noerr( err, exit ); + + GetClientRect( rect ); + mTree.Create( WS_TABSTOP | WS_VISIBLE | WS_CHILD | TVS_HASBUTTONS | TVS_LINESATROOT | TVS_HASLINES | TVS_NOHSCROLL , rect, this, + IDC_EXPLORER_TREE ); + + + ServiceHandlerEntry * e; + + // Web Site Handler + + e = new ServiceHandlerEntry; + check( e ); + e->type = "_http._tcp"; + e->urlScheme = "http://"; + e->ref = NULL; + e->treeItem = NULL; + e->treeFirst = true; + e->obj = this; + e->needsLogin = false; + mServiceHandlers.Add( e ); + + s.LoadString( IDS_WEB_SITES ); + e->treeItem = mTree.InsertItem( s, 0, 0 ); + mTree.Expand( e->treeItem, TVE_EXPAND ); + + err = DNSServiceBrowse( &e->ref, 0, 0, e->type, NULL, BrowseCallBack, e ); + require_noerr( err, exit ); + + err = WSAAsyncSelect((SOCKET) DNSServiceRefSockFD(e->ref), m_hWnd, WM_PRIVATE_SERVICE_EVENT, FD_READ|FD_CLOSE); + require_noerr( err, exit ); + + m_serviceRefs.push_back(e->ref); + + // FTP Site Handler + + e = new ServiceHandlerEntry; + check( e ); + e->type = "_ftp._tcp"; + e->urlScheme = "ftp://"; + e->ref = NULL; + e->treeItem = NULL; + e->treeFirst = true; + e->obj = this; + e->needsLogin = true; + mServiceHandlers.Add( e ); + + s.LoadString( IDS_FTP_SITES ); + e->treeItem = mTree.InsertItem( s, 0, 0 ); + mTree.Expand( e->treeItem, TVE_EXPAND ); + + err = DNSServiceBrowse( &e->ref, 0, 0, e->type, NULL, BrowseCallBack, e ); + require_noerr( err, exit ); + + err = WSAAsyncSelect((SOCKET) DNSServiceRefSockFD(e->ref), m_hWnd, WM_PRIVATE_SERVICE_EVENT, FD_READ|FD_CLOSE); + require_noerr( err, exit ); + + m_serviceRefs.push_back(e->ref); + + m_imageList.Create(16, 16, ILC_COLORDDB, 1, 0); + bitmap.LoadBitmap(IDB_LOGO); + m_imageList.Add(&bitmap, (CBitmap*) NULL); + + mTree.SetImageList(&m_imageList, TVSIL_NORMAL); + +exit: + + // Cannot talk to the mDNSResponder service. Show the error message and exit (with kNoErr so they can see it). + if ( err != kNoErr ) + { + s.LoadString( IDS_MDNSRESPONDER_NOT_AVAILABLE ); + mTree.DeleteAllItems(); + mTree.InsertItem( s, 0, 0, TVI_ROOT, TVI_LAST ); + err = kNoErr; + } + + return( err ); +} + +//=========================================================================================================================== +// OnDestroy +//=========================================================================================================================== + +void ExplorerBarWindow::OnDestroy( void ) +{ + // Stop any resolves that may still be pending (shouldn't be any). + + StopResolve(); + + // Clean up the extant browses + while (m_serviceRefs.size() > 0) + { + // + // take the head of the list + // + DNSServiceRef ref = m_serviceRefs.front(); + + // + // Stop will remove it from the list + // + Stop( ref ); + } + + // Clean up the service handlers. + + int i; + int n; + + n = (int) mServiceHandlers.GetSize(); + for( i = 0; i < n; ++i ) + { + delete mServiceHandlers[ i ]; + } + + CWnd::OnDestroy(); +} + +//=========================================================================================================================== +// OnSize +//=========================================================================================================================== + +void ExplorerBarWindow::OnSize( UINT inType, int inX, int inY ) +{ + CWnd::OnSize( inType, inX, inY ); + mTree.MoveWindow( 0, 0, inX, inY ); +} + +//=========================================================================================================================== +// OnDoubleClick +//=========================================================================================================================== + +void ExplorerBarWindow::OnDoubleClick( NMHDR *inNMHDR, LRESULT *outResult ) +{ + HTREEITEM item; + ServiceInfo * service; + OSStatus err; + + DEBUG_UNUSED( inNMHDR ); + + item = mTree.GetSelectedItem(); + require( item, exit ); + + service = reinterpret_cast < ServiceInfo * > ( mTree.GetItemData( item ) ); + require_quiet( service, exit ); + + err = StartResolve( service ); + require_noerr( err, exit ); + +exit: + *outResult = 0; +} + + +//=========================================================================================================================== +// OnServiceEvent +//=========================================================================================================================== + +LONG +ExplorerBarWindow::OnServiceEvent(WPARAM inWParam, LPARAM inLParam) +{ + if (WSAGETSELECTERROR(inLParam) && !(HIWORD(inLParam))) + { + dlog( kDebugLevelError, "OnServiceEvent: window error\n" ); + } + else + { + SOCKET sock = (SOCKET) inWParam; + + // iterate thru list + ServiceRefList::iterator it; + + for (it = m_serviceRefs.begin(); it != m_serviceRefs.end(); it++) + { + DNSServiceRef ref = *it; + + check(ref != NULL); + + if ((SOCKET) DNSServiceRefSockFD(ref) == sock) + { + DNSServiceErrorType err; + + err = DNSServiceProcessResult(ref); + + if (err != 0) + { + CString s; + + s.LoadString( IDS_MDNSRESPONDER_NOT_AVAILABLE ); + mTree.DeleteAllItems(); + mTree.InsertItem( s, 0, 0, TVI_ROOT, TVI_LAST ); + + Stop(ref); + } + + break; + } + } + } + + return ( 0 ); +} + +#if 0 +#pragma mark - +#endif + +//=========================================================================================================================== +// BrowseCallBack +//=========================================================================================================================== + +void DNSSD_API + ExplorerBarWindow::BrowseCallBack( + DNSServiceRef inRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + DNSServiceErrorType inErrorCode, + const char * inName, + const char * inType, + const char * inDomain, + void * inContext ) +{ + ServiceHandlerEntry * obj; + ServiceInfo * service; + OSStatus err; + + DEBUG_UNUSED( inRef ); + + obj = NULL; + service = NULL; + + require_noerr( inErrorCode, exit ); + obj = reinterpret_cast < ServiceHandlerEntry * > ( inContext ); + check( obj ); + check( obj->obj ); + + // + // set the UI to hold off on updates + // + obj->obj->mTree.SetRedraw(FALSE); + + try + { + service = new ServiceInfo; + require_action( service, exit, err = kNoMemoryErr ); + + err = UTF8StringToStringObject( inName, service->displayName ); + check_noerr( err ); + + service->name = strdup( inName ); + require_action( service->name, exit, err = kNoMemoryErr ); + + service->type = strdup( inType ); + require_action( service->type, exit, err = kNoMemoryErr ); + + service->domain = strdup( inDomain ); + require_action( service->domain, exit, err = kNoMemoryErr ); + + service->ifi = inInterfaceIndex; + service->handler = obj; + + service->refs = 1; + + if (inFlags & kDNSServiceFlagsAdd) obj->obj->OnServiceAdd (service); + else obj->obj->OnServiceRemove(service); + + service = NULL; + } + catch( ... ) + { + dlog( kDebugLevelError, "BrowseCallBack: exception thrown\n" ); + } + +exit: + // + // If no more coming, then update UI + // + if (obj && obj->obj && ((inFlags & kDNSServiceFlagsMoreComing) == 0)) + { + obj->obj->mTree.SetRedraw(TRUE); + obj->obj->mTree.Invalidate(); + } + + if( service ) + { + delete service; + } +} + +//=========================================================================================================================== +// OnServiceAdd +//=========================================================================================================================== + +LONG ExplorerBarWindow::OnServiceAdd( ServiceInfo * service ) +{ + ServiceHandlerEntry * handler; + int cmp; + int index; + + + check( service ); + handler = service->handler; + check( handler ); + + cmp = FindServiceArrayIndex( handler->array, *service, index ); + if( cmp == 0 ) + { + // Found a match so update the item. The index is index + 1 so subtract 1. + + index -= 1; + check( index < handler->array.GetSize() ); + + handler->array[ index ]->refs++; + + delete service; + } + else + { + HTREEITEM afterItem; + + // Insert the new item in sorted order. + + afterItem = ( index > 0 ) ? handler->array[ index - 1 ]->item : TVI_FIRST; + handler->array.InsertAt( index, service ); + service->item = mTree.InsertItem( service->displayName, handler->treeItem, afterItem ); + mTree.SetItemData( service->item, (DWORD_PTR) service ); + + // Make sure the item is visible if this is the first time a service was added. + + if( handler->treeFirst ) + { + handler->treeFirst = false; + mTree.EnsureVisible( service->item ); + } + } + return( 0 ); +} + +//=========================================================================================================================== +// OnServiceRemove +//=========================================================================================================================== + +LONG ExplorerBarWindow::OnServiceRemove( ServiceInfo * service ) +{ + ServiceHandlerEntry * handler; + int cmp; + int index; + + + check( service ); + handler = service->handler; + check( handler ); + + // Search to see if we know about this service instance. If so, remove it from the list. + + cmp = FindServiceArrayIndex( handler->array, *service, index ); + check( cmp == 0 ); + + if( cmp == 0 ) + { + // Possibly found a match remove the item. The index + // is index + 1 so subtract 1. + index -= 1; + check( index < handler->array.GetSize() ); + + if ( --handler->array[ index ]->refs == 0 ) + { + mTree.DeleteItem( handler->array[ index ]->item ); + delete handler->array[ index ]; + handler->array.RemoveAt( index ); + } + } + + delete service; + return( 0 ); +} + +#if 0 +#pragma mark - +#endif + +//=========================================================================================================================== +// StartResolve +//=========================================================================================================================== + +OSStatus ExplorerBarWindow::StartResolve( ServiceInfo *inService ) +{ + OSStatus err; + + check( inService ); + + // Stop any current resolve that may be in progress. + + StopResolve(); + + // Resolve the service. + err = DNSServiceResolve( &mResolveServiceRef, 0, 0, + inService->name, inService->type, inService->domain, (DNSServiceResolveReply) ResolveCallBack, inService->handler ); + require_noerr( err, exit ); + + err = WSAAsyncSelect((SOCKET) DNSServiceRefSockFD(mResolveServiceRef), m_hWnd, WM_PRIVATE_SERVICE_EVENT, FD_READ|FD_CLOSE); + require_noerr( err, exit ); + + m_serviceRefs.push_back(mResolveServiceRef); + +exit: + return( err ); +} + +//=========================================================================================================================== +// StopResolve +//=========================================================================================================================== + +void ExplorerBarWindow::StopResolve( void ) +{ + if( mResolveServiceRef ) + { + Stop( mResolveServiceRef ); + mResolveServiceRef = NULL; + } +} + +//=========================================================================================================================== +// ResolveCallBack +//=========================================================================================================================== + +void DNSSD_API + ExplorerBarWindow::ResolveCallBack( + DNSServiceRef inRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + DNSServiceErrorType inErrorCode, + const char * inFullName, + const char * inHostName, + uint16_t inPort, + uint16_t inTXTSize, + const char * inTXT, + void * inContext ) +{ + ExplorerBarWindow * obj; + ServiceHandlerEntry * handler; + OSStatus err; + + DEBUG_UNUSED( inRef ); + DEBUG_UNUSED( inFlags ); + DEBUG_UNUSED( inErrorCode ); + DEBUG_UNUSED( inFullName ); + + require_noerr( inErrorCode, exit ); + handler = (ServiceHandlerEntry *) inContext; + check( handler ); + obj = handler->obj; + check( obj ); + + try + { + ResolveInfo * resolve; + int idx; + + dlog( kDebugLevelNotice, "resolved %s on ifi %d to %s\n", inFullName, inInterfaceIndex, inHostName ); + + // Stop resolving after the first good result. + + obj->StopResolve(); + + // Post a message to the main thread so it can handle it since MFC is not thread safe. + + resolve = new ResolveInfo; + require_action( resolve, exit, err = kNoMemoryErr ); + + UTF8StringToStringObject( inHostName, resolve->host ); + + // rdar://problem/3841564 + // + // strip trailing dot from hostname because some flavors of Windows + // have trouble parsing it. + + idx = resolve->host.ReverseFind('.'); + + if ((idx > 1) && ((resolve->host.GetLength() - 1) == idx)) + { + resolve->host.Delete(idx, 1); + } + + resolve->port = ntohs( inPort ); + resolve->ifi = inInterfaceIndex; + resolve->handler = handler; + + err = resolve->txt.SetData( inTXT, inTXTSize ); + check_noerr( err ); + + obj->OnResolve(resolve); + } + catch( ... ) + { + dlog( kDebugLevelError, "ResolveCallBack: exception thrown\n" ); + } + +exit: + return; +} + +//=========================================================================================================================== +// OnResolve +//=========================================================================================================================== + +LONG ExplorerBarWindow::OnResolve( ResolveInfo * resolve ) +{ + CString url; + uint8_t * path; + uint8_t pathSize; + char * pathPrefix; + CString username; + CString password; + + + check( resolve ); + + // Get login info if needed. + + if( resolve->handler->needsLogin ) + { + LoginDialog dialog; + + if( !dialog.GetLogin( username, password ) ) + { + goto exit; + } + } + + // If the HTTP TXT record is a "path=" entry, use it as the resource path. Otherwise, use "/". + + pathPrefix = ""; + if( strcmp( resolve->handler->type, "_http._tcp" ) == 0 ) + { + uint8_t * txtData; + uint16_t txtLen; + + resolve->txt.GetData( &txtData, &txtLen ); + + path = (uint8_t*) TXTRecordGetValuePtr(txtLen, txtData, kTXTRecordKeyPath, &pathSize); + + if (path == NULL) + { + path = (uint8_t*) ""; + pathSize = 1; + } + } + else + { + path = (uint8_t *) ""; + pathSize = 1; + } + + // Build the URL in the following format: + // + // [[:]@][] + + url.AppendFormat( TEXT( "%S" ), resolve->handler->urlScheme ); // URL Scheme + if( username.GetLength() > 0 ) + { + url.AppendFormat( TEXT( "%s" ), username ); // Username + if( password.GetLength() > 0 ) + { + url.AppendFormat( TEXT( ":%s" ), password ); // Password + } + url.AppendFormat( TEXT( "@" ) ); + } + + url += resolve->host; // Host + url.AppendFormat( TEXT( ":%d" ), resolve->port ); // :Port + url.AppendFormat( TEXT( "%S" ), pathPrefix ); // Path Prefix ("/" or empty). + url.AppendFormat( TEXT( "%.*S" ), (int) pathSize, (char *) path ); // Path (possibly empty). + + // Tell Internet Explorer to go to the URL. + + check( mOwner ); + mOwner->GoToURL( url ); + +exit: + delete resolve; + return( 0 ); +} + +//=========================================================================================================================== +// Stop +//=========================================================================================================================== +void ExplorerBarWindow::Stop( DNSServiceRef ref ) +{ + m_serviceRefs.remove( ref ); + + WSAAsyncSelect(DNSServiceRefSockFD( ref ), m_hWnd, WM_PRIVATE_SERVICE_EVENT, 0); + + DNSServiceRefDeallocate( ref ); +} + + +#if 0 +#pragma mark - +#endif + +//=========================================================================================================================== +// FindServiceArrayIndex +//=========================================================================================================================== + +DEBUG_LOCAL int FindServiceArrayIndex( const ServiceInfoArray &inArray, const ServiceInfo &inService, int &outIndex ) +{ + int result; + int lo; + int hi; + int mid; + + result = -1; + mid = 0; + lo = 0; + hi = (int)( inArray.GetSize() - 1 ); + while( lo <= hi ) + { + mid = ( lo + hi ) / 2; + result = inService.displayName.CompareNoCase( inArray[ mid ]->displayName ); +#if 0 + if( result == 0 ) + { + result = ( (int) inService.ifi ) - ( (int) inArray[ mid ]->ifi ); + } +#endif + if( result == 0 ) + { + break; + } + else if( result < 0 ) + { + hi = mid - 1; + } + else + { + lo = mid + 1; + } + } + if( result == 0 ) + { + mid += 1; // Bump index so new item is inserted after matching item. + } + else if( result > 0 ) + { + mid += 1; + } + outIndex = mid; + return( result ); +}