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.
20 #include "CommonServices.h"
21 #include "DebugServices.h"
22 #include "WinServices.h"
25 #include "ExplorerBar.h"
26 #include "LoginDialog.h"
29 #include "ExplorerBarWindow.h"
30 #include "ExplorerPlugin.h"
37 static char THIS_FILE
[] = __FILE__
;
41 #pragma mark == Constants ==
44 //===========================================================================================================================
46 //===========================================================================================================================
50 #define IDC_EXPLORER_TREE 1234
54 #define WM_PRIVATE_SERVICE_EVENT ( WM_USER + 0x100 )
58 #define kTXTRecordKeyPath "path"
62 #define kIEIconResource 32529
66 #pragma mark == Prototypes ==
69 //===========================================================================================================================
71 //===========================================================================================================================
73 DEBUG_LOCAL
int FindServiceArrayIndex( const ServiceInfoArray
&inArray
, const ServiceInfo
&inService
, int &outIndex
);
76 #pragma mark == Message Map ==
79 //===========================================================================================================================
81 //===========================================================================================================================
83 BEGIN_MESSAGE_MAP( ExplorerBarWindow
, CWnd
)
87 ON_NOTIFY( NM_DBLCLK
, IDC_EXPLORER_TREE
, OnDoubleClick
)
88 ON_MESSAGE( WM_PRIVATE_SERVICE_EVENT
, OnServiceEvent
)
95 //===========================================================================================================================
97 //===========================================================================================================================
99 ExplorerBarWindow::ExplorerBarWindow( void )
102 mResolveServiceRef
= NULL
;
105 //===========================================================================================================================
106 // ~ExplorerBarWindow
107 //===========================================================================================================================
109 ExplorerBarWindow::~ExplorerBarWindow( void )
118 //===========================================================================================================================
120 //===========================================================================================================================
122 int ExplorerBarWindow::OnCreate( LPCREATESTRUCT inCreateStruct
)
124 AFX_MANAGE_STATE( AfxGetStaticModuleState() );
126 HINSTANCE
module = NULL
;
132 err
= CWnd::OnCreate( inCreateStruct
);
133 require_noerr( err
, exit
);
135 GetClientRect( rect
);
136 mTree
.Create( WS_TABSTOP
| WS_VISIBLE
| WS_CHILD
| TVS_HASBUTTONS
| TVS_LINESATROOT
| TVS_NOHSCROLL
, rect
, this,
139 ServiceHandlerEntry
* e
;
141 s
.LoadString( IDS_ABOUT
);
142 m_about
= mTree
.InsertItem( s
, 0, 0 );
146 e
= new ServiceHandlerEntry
;
148 e
->type
= "_http._tcp";
149 e
->urlScheme
= "http://";
152 e
->needsLogin
= false;
153 mServiceHandlers
.Add( e
);
155 err
= DNSServiceBrowse( &e
->ref
, 0, 0, e
->type
, NULL
, BrowseCallBack
, e
);
156 require_noerr( err
, exit
);
158 err
= WSAAsyncSelect((SOCKET
) DNSServiceRefSockFD(e
->ref
), m_hWnd
, WM_PRIVATE_SERVICE_EVENT
, FD_READ
|FD_CLOSE
);
159 require_noerr( err
, exit
);
161 m_serviceRefs
.push_back(e
->ref
);
163 #if defined( _BROWSE_FOR_HTTPS_ )
164 e
= new ServiceHandlerEntry
;
166 e
->type
= "_https._tcp";
167 e
->urlScheme
= "https://";
170 e
->needsLogin
= false;
171 mServiceHandlers
.Add( e
);
173 err
= DNSServiceBrowse( &e
->ref
, 0, 0, e
->type
, NULL
, BrowseCallBack
, e
);
174 require_noerr( err
, exit
);
176 err
= WSAAsyncSelect((SOCKET
) DNSServiceRefSockFD(e
->ref
), m_hWnd
, WM_PRIVATE_SERVICE_EVENT
, FD_READ
|FD_CLOSE
);
177 require_noerr( err
, exit
);
179 m_serviceRefs
.push_back(e
->ref
);
182 m_imageList
.Create( 16, 16, ILC_MASK
| ILC_COLOR16
, 2, 0);
184 bitmap
.Attach( ::LoadBitmap( GetNonLocalizedResources(), MAKEINTRESOURCE( IDB_LOGO
) ) );
185 m_imageList
.Add( &bitmap
, (CBitmap
*) NULL
);
188 mTree
.SetImageList(&m_imageList
, TVSIL_NORMAL
);
194 FreeLibrary( module );
198 // Cannot talk to the mDNSResponder service. Show the error message and exit (with kNoErr so they can see it).
201 if ( err
== kDNSServiceErr_Firewall
)
203 s
.LoadString( IDS_FIREWALL
);
207 s
.LoadString( IDS_MDNSRESPONDER_NOT_AVAILABLE
);
210 mTree
.DeleteAllItems();
211 mTree
.InsertItem( s
, 0, 0, TVI_ROOT
, TVI_LAST
);
219 //===========================================================================================================================
221 //===========================================================================================================================
223 void ExplorerBarWindow::OnDestroy( void )
225 // Stop any resolves that may still be pending (shouldn't be any).
229 // Clean up the extant browses
230 while (m_serviceRefs
.size() > 0)
233 // take the head of the list
235 DNSServiceRef ref
= m_serviceRefs
.front();
238 // Stop will remove it from the list
243 // Clean up the service handlers.
248 n
= (int) mServiceHandlers
.GetSize();
249 for( i
= 0; i
< n
; ++i
)
251 delete mServiceHandlers
[ i
];
257 //===========================================================================================================================
259 //===========================================================================================================================
261 void ExplorerBarWindow::OnSize( UINT inType
, int inX
, int inY
)
263 CWnd::OnSize( inType
, inX
, inY
);
264 mTree
.MoveWindow( 0, 0, inX
, inY
);
267 //===========================================================================================================================
269 //===========================================================================================================================
271 void ExplorerBarWindow::OnDoubleClick( NMHDR
*inNMHDR
, LRESULT
*outResult
)
274 ServiceInfo
* service
;
277 DEBUG_UNUSED( inNMHDR
);
279 item
= mTree
.GetSelectedItem();
280 require( item
, exit
);
282 // Tell Internet Explorer to go to the URL if it's about item
284 if ( item
== m_about
)
290 url
.LoadString( IDS_ABOUT_URL
);
291 mOwner
->GoToURL( url
);
295 service
= reinterpret_cast < ServiceInfo
* > ( mTree
.GetItemData( item
) );
296 require_quiet( service
, exit
);
298 err
= StartResolve( service
);
299 require_noerr( err
, exit
);
307 //===========================================================================================================================
309 //===========================================================================================================================
312 ExplorerBarWindow::OnServiceEvent(WPARAM inWParam
, LPARAM inLParam
)
314 if (WSAGETSELECTERROR(inLParam
) && !(HIWORD(inLParam
)))
316 dlog( kDebugLevelError
, "OnServiceEvent: window error\n" );
320 SOCKET sock
= (SOCKET
) inWParam
;
323 ServiceRefList::iterator it
;
325 for (it
= m_serviceRefs
.begin(); it
!= m_serviceRefs
.end(); it
++)
327 DNSServiceRef ref
= *it
;
331 if ((SOCKET
) DNSServiceRefSockFD(ref
) == sock
)
333 DNSServiceErrorType err
;
335 err
= DNSServiceProcessResult(ref
);
341 s
.LoadString( IDS_MDNSRESPONDER_NOT_AVAILABLE
);
342 mTree
.DeleteAllItems();
343 mTree
.InsertItem( s
, 0, 0, TVI_ROOT
, TVI_LAST
);
360 //===========================================================================================================================
362 //===========================================================================================================================
365 ExplorerBarWindow::BrowseCallBack(
367 DNSServiceFlags inFlags
,
368 uint32_t inInterfaceIndex
,
369 DNSServiceErrorType inErrorCode
,
372 const char * inDomain
,
375 ServiceHandlerEntry
* obj
;
376 ServiceInfo
* service
;
379 DEBUG_UNUSED( inRef
);
384 require_noerr( inErrorCode
, exit
);
385 obj
= reinterpret_cast < ServiceHandlerEntry
* > ( inContext
);
390 // set the UI to hold off on updates
392 obj
->obj
->mTree
.SetRedraw(FALSE
);
396 service
= new ServiceInfo
;
397 require_action( service
, exit
, err
= kNoMemoryErr
);
399 err
= UTF8StringToStringObject( inName
, service
->displayName
);
402 service
->name
= _strdup( inName
);
403 require_action( service
->name
, exit
, err
= kNoMemoryErr
);
405 service
->type
= _strdup( inType
);
406 require_action( service
->type
, exit
, err
= kNoMemoryErr
);
408 service
->domain
= _strdup( inDomain
);
409 require_action( service
->domain
, exit
, err
= kNoMemoryErr
);
411 service
->ifi
= inInterfaceIndex
;
412 service
->handler
= obj
;
416 if (inFlags
& kDNSServiceFlagsAdd
) obj
->obj
->OnServiceAdd (service
);
417 else obj
->obj
->OnServiceRemove(service
);
423 dlog( kDebugLevelError
, "BrowseCallBack: exception thrown\n" );
428 // If no more coming, then update UI
430 if (obj
&& obj
->obj
&& ((inFlags
& kDNSServiceFlagsMoreComing
) == 0))
432 obj
->obj
->mTree
.SetRedraw(TRUE
);
433 obj
->obj
->mTree
.Invalidate();
442 //===========================================================================================================================
444 //===========================================================================================================================
446 LONG
ExplorerBarWindow::OnServiceAdd( ServiceInfo
* service
)
448 ServiceHandlerEntry
* handler
;
454 handler
= service
->handler
;
457 cmp
= FindServiceArrayIndex( handler
->array
, *service
, index
);
460 // Found a match so update the item. The index is index + 1 so subtract 1.
463 check( index
< handler
->array
.GetSize() );
465 handler
->array
[ index
]->refs
++;
473 // Insert the new item in sorted order.
475 afterItem
= ( index
> 0 ) ? handler
->array
[ index
- 1 ]->item
: m_about
;
476 handler
->array
.InsertAt( index
, service
);
477 service
->item
= mTree
.InsertItem( service
->displayName
, 0, 0, NULL
, afterItem
);
478 mTree
.SetItemData( service
->item
, (DWORD_PTR
) service
);
483 //===========================================================================================================================
485 //===========================================================================================================================
487 LONG
ExplorerBarWindow::OnServiceRemove( ServiceInfo
* service
)
489 ServiceHandlerEntry
* handler
;
495 handler
= service
->handler
;
498 // Search to see if we know about this service instance. If so, remove it from the list.
500 cmp
= FindServiceArrayIndex( handler
->array
, *service
, index
);
505 // Possibly found a match remove the item. The index
506 // is index + 1 so subtract 1.
508 check( index
< handler
->array
.GetSize() );
510 if ( --handler
->array
[ index
]->refs
== 0 )
512 mTree
.DeleteItem( handler
->array
[ index
]->item
);
513 delete handler
->array
[ index
];
514 handler
->array
.RemoveAt( index
);
526 //===========================================================================================================================
528 //===========================================================================================================================
530 OSStatus
ExplorerBarWindow::StartResolve( ServiceInfo
*inService
)
536 // Stop any current resolve that may be in progress.
540 // Resolve the service.
541 err
= DNSServiceResolve( &mResolveServiceRef
, 0, 0,
542 inService
->name
, inService
->type
, inService
->domain
, (DNSServiceResolveReply
) ResolveCallBack
, inService
->handler
);
543 require_noerr( err
, exit
);
545 err
= WSAAsyncSelect((SOCKET
) DNSServiceRefSockFD(mResolveServiceRef
), m_hWnd
, WM_PRIVATE_SERVICE_EVENT
, FD_READ
|FD_CLOSE
);
546 require_noerr( err
, exit
);
548 m_serviceRefs
.push_back(mResolveServiceRef
);
554 //===========================================================================================================================
556 //===========================================================================================================================
558 void ExplorerBarWindow::StopResolve( void )
560 if( mResolveServiceRef
)
562 Stop( mResolveServiceRef
);
563 mResolveServiceRef
= NULL
;
567 //===========================================================================================================================
569 //===========================================================================================================================
572 ExplorerBarWindow::ResolveCallBack(
574 DNSServiceFlags inFlags
,
575 uint32_t inInterfaceIndex
,
576 DNSServiceErrorType inErrorCode
,
577 const char * inFullName
,
578 const char * inHostName
,
584 ExplorerBarWindow
* obj
;
585 ServiceHandlerEntry
* handler
;
588 DEBUG_UNUSED( inRef
);
589 DEBUG_UNUSED( inFlags
);
590 DEBUG_UNUSED( inErrorCode
);
591 DEBUG_UNUSED( inFullName
);
593 require_noerr( inErrorCode
, exit
);
594 handler
= (ServiceHandlerEntry
*) inContext
;
601 ResolveInfo
* resolve
;
604 dlog( kDebugLevelNotice
, "resolved %s on ifi %d to %s\n", inFullName
, inInterfaceIndex
, inHostName
);
606 // Stop resolving after the first good result.
610 // Post a message to the main thread so it can handle it since MFC is not thread safe.
612 resolve
= new ResolveInfo
;
613 require_action( resolve
, exit
, err
= kNoMemoryErr
);
615 UTF8StringToStringObject( inHostName
, resolve
->host
);
617 // rdar://problem/3841564
619 // strip trailing dot from hostname because some flavors of Windows
620 // have trouble parsing it.
622 idx
= resolve
->host
.ReverseFind('.');
624 if ((idx
> 1) && ((resolve
->host
.GetLength() - 1) == idx
))
626 resolve
->host
.Delete(idx
, 1);
629 resolve
->port
= ntohs( inPort
);
630 resolve
->ifi
= inInterfaceIndex
;
631 resolve
->handler
= handler
;
633 err
= resolve
->txt
.SetData( inTXT
, inTXTSize
);
636 obj
->OnResolve(resolve
);
640 dlog( kDebugLevelError
, "ResolveCallBack: exception thrown\n" );
647 //===========================================================================================================================
649 //===========================================================================================================================
651 LONG
ExplorerBarWindow::OnResolve( ResolveInfo
* resolve
)
663 // Get login info if needed.
665 if( resolve
->handler
->needsLogin
)
669 if( !dialog
.GetLogin( username
, password
) )
675 // If the HTTP TXT record is a "path=" entry, use it as the resource path. Otherwise, use "/".
678 if( strcmp( resolve
->handler
->type
, "_http._tcp" ) == 0 )
683 resolve
->txt
.GetData( &txtData
, &txtLen
);
685 path
= (uint8_t*) TXTRecordGetValuePtr(txtLen
, txtData
, kTXTRecordKeyPath
, &pathSize
);
689 path
= (uint8_t*) "";
695 path
= (uint8_t *) "";
699 // Build the URL in the following format:
701 // <urlScheme>[<username>[:<password>]@]<name/ip>[<path>]
703 url
.AppendFormat( TEXT( "%S" ), resolve
->handler
->urlScheme
); // URL Scheme
704 if( username
.GetLength() > 0 )
706 url
.AppendFormat( TEXT( "%s" ), username
); // Username
707 if( password
.GetLength() > 0 )
709 url
.AppendFormat( TEXT( ":%s" ), password
); // Password
711 url
.AppendFormat( TEXT( "@" ) );
714 url
+= resolve
->host
; // Host
715 url
.AppendFormat( TEXT( ":%d" ), resolve
->port
); // :Port
716 url
.AppendFormat( TEXT( "%S" ), pathPrefix
); // Path Prefix ("/" or empty).
717 url
.AppendFormat( TEXT( "%.*S" ), (int) pathSize
, (char *) path
); // Path (possibly empty).
719 // Tell Internet Explorer to go to the URL.
722 mOwner
->GoToURL( url
);
729 //===========================================================================================================================
731 //===========================================================================================================================
732 void ExplorerBarWindow::Stop( DNSServiceRef ref
)
734 m_serviceRefs
.remove( ref
);
736 WSAAsyncSelect(DNSServiceRefSockFD( ref
), m_hWnd
, WM_PRIVATE_SERVICE_EVENT
, 0);
738 DNSServiceRefDeallocate( ref
);
746 //===========================================================================================================================
747 // FindServiceArrayIndex
748 //===========================================================================================================================
750 DEBUG_LOCAL
int FindServiceArrayIndex( const ServiceInfoArray
&inArray
, const ServiceInfo
&inService
, int &outIndex
)
760 hi
= (int)( inArray
.GetSize() - 1 );
763 mid
= ( lo
+ hi
) / 2;
764 result
= inService
.displayName
.CompareNoCase( inArray
[ mid
]->displayName
);
768 result
= ( (int) inService
.ifi
) - ( (int) inArray
[ mid
]->ifi
);
775 else if( result
< 0 )
786 mid
+= 1; // Bump index so new item is inserted after matching item.
788 else if( result
> 0 )