| /* -*- Mode: C; tab-width: 4 -*- |
| * |
| * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #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" |
| #include "ExplorerPlugin.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" |
| |
| // IE Icon resource |
| |
| #define kIEIconResource 32529 |
| |
| |
| #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() ); |
| |
| HINSTANCE module = NULL; |
| 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_NOHSCROLL , rect, this, |
| IDC_EXPLORER_TREE ); |
| |
| ServiceHandlerEntry * e; |
| |
| s.LoadString( IDS_ABOUT ); |
| m_about = mTree.InsertItem( s, 0, 0 ); |
| |
| // Web Site Handler |
| |
| e = new ServiceHandlerEntry; |
| check( e ); |
| e->type = "_http._tcp"; |
| e->urlScheme = "http://"; |
| e->ref = NULL; |
| e->obj = this; |
| e->needsLogin = false; |
| mServiceHandlers.Add( e ); |
| |
| 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); |
| |
| #if defined( _BROWSE_FOR_HTTPS_ ) |
| e = new ServiceHandlerEntry; |
| check( e ); |
| e->type = "_https._tcp"; |
| e->urlScheme = "https://"; |
| e->ref = NULL; |
| e->obj = this; |
| e->needsLogin = false; |
| mServiceHandlers.Add( e ); |
| |
| 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); |
| #endif |
| |
| m_imageList.Create( 16, 16, ILC_MASK | ILC_COLOR16, 2, 0); |
| |
| bitmap.Attach( ::LoadBitmap( GetNonLocalizedResources(), MAKEINTRESOURCE( IDB_LOGO ) ) ); |
| m_imageList.Add( &bitmap, (CBitmap*) NULL ); |
| bitmap.Detach(); |
| |
| mTree.SetImageList(&m_imageList, TVSIL_NORMAL); |
| |
| exit: |
| |
| if ( module ) |
| { |
| FreeLibrary( module ); |
| module = NULL; |
| } |
| |
| // Cannot talk to the mDNSResponder service. Show the error message and exit (with kNoErr so they can see it). |
| if ( err ) |
| { |
| if ( err == kDNSServiceErr_Firewall ) |
| { |
| s.LoadString( IDS_FIREWALL ); |
| } |
| else |
| { |
| 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 ); |
| |
| // Tell Internet Explorer to go to the URL if it's about item |
| |
| if ( item == m_about ) |
| { |
| CString url; |
| |
| check( mOwner ); |
| |
| url.LoadString( IDS_ABOUT_URL ); |
| mOwner->GoToURL( url ); |
| } |
| else |
| { |
| service = reinterpret_cast < ServiceInfo * > ( mTree.GetItemData( item ) ); |
| require_quiet( service, exit ); |
| |
| err = StartResolve( service ); |
| require_noerr( err, exit ); |
| } |
| |
| exit: |
| *outResult = 0; |
| } |
| |
| |
| //=========================================================================================================================== |
| // OnServiceEvent |
| //=========================================================================================================================== |
| |
| LRESULT |
| 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 : m_about; |
| handler->array.InsertAt( index, service ); |
| service->item = mTree.InsertItem( service->displayName, 0, 0, NULL, afterItem ); |
| mTree.SetItemData( service->item, (DWORD_PTR) service ); |
| } |
| 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: |
| // |
| // <urlScheme>[<username>[:<password>]@]<name/ip>[<path>] |
| |
| 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 ); |
| } |