2 * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
23 * @APPLE_LICENSE_HEADER_END@
25 Change History (most recent first):
27 $Log: ExplorerBarWindow.cpp,v $
28 Revision 1.5 2004/04/15 01:00:05 bradley
29 Removed support for automatically querying for A/AAAA records when resolving names. Platforms
30 without .local name resolving support will need to manually query for A/AAAA records as needed.
32 Revision 1.4 2004/04/09 21:03:15 bradley
33 Changed port numbers to use network byte order for consistency with other platforms.
35 Revision 1.3 2004/04/08 09:43:43 bradley
36 Changed callback calling conventions to __stdcall so they can be used with C# delegates.
38 Revision 1.2 2004/02/21 04:36:19 bradley
39 Enable dot local name lookups now that the NSP is being installed.
41 Revision 1.1 2004/01/30 03:01:56 bradley
42 Explorer Plugin to browse for Rendezvous-enabled Web and FTP servers from within Internet Explorer.
48 #include "CommonServices.h"
49 #include "DebugServices.h"
52 #include "ExplorerBar.h"
53 #include "LoginDialog.h"
56 #include "ExplorerBarWindow.h"
63 static char THIS_FILE
[] = __FILE__
;
67 #pragma mark == Constants ==
70 //===========================================================================================================================
72 //===========================================================================================================================
76 #define IDC_EXPLORER_TREE 1234
80 #define WM_PRIVATE_SERVICE_ADD ( WM_USER + 0x100 )
81 #define WM_PRIVATE_SERVICE_REMOVE ( WM_USER + 0x101 )
82 #define WM_PRIVATE_RESOLVE ( WM_USER + 0x102 )
86 #define kTXTRecordKeyPath "path="
87 #define kTXTRecordKeyPathSize sizeof_string( kTXTRecordKeyPath )
90 #pragma mark == Prototypes ==
93 //===========================================================================================================================
95 //===========================================================================================================================
97 DEBUG_LOCAL
int FindServiceArrayIndex( const ServiceInfoArray
&inArray
, const ServiceInfo
&inService
, int &outIndex
);
98 DEBUG_LOCAL OSStatus
UTF8StringToStringObject( const char *inUTF8
, CString
&inObject
);
101 #pragma mark == Message Map ==
104 //===========================================================================================================================
106 //===========================================================================================================================
108 BEGIN_MESSAGE_MAP( ExplorerBarWindow
, CWnd
)
112 ON_NOTIFY( NM_DBLCLK
, IDC_EXPLORER_TREE
, OnDoubleClick
)
113 ON_MESSAGE( WM_PRIVATE_SERVICE_ADD
, OnServiceAdd
)
114 ON_MESSAGE( WM_PRIVATE_SERVICE_REMOVE
, OnServiceRemove
)
115 ON_MESSAGE( WM_PRIVATE_RESOLVE
, OnResolve
)
122 //===========================================================================================================================
124 //===========================================================================================================================
126 ExplorerBarWindow::ExplorerBarWindow( void )
129 mResolveServiceRef
= NULL
;
132 //===========================================================================================================================
133 // ~ExplorerBarWindow
134 //===========================================================================================================================
136 ExplorerBarWindow::~ExplorerBarWindow( void )
145 //===========================================================================================================================
147 //===========================================================================================================================
149 int ExplorerBarWindow::OnCreate( LPCREATESTRUCT inCreateStruct
)
151 AFX_MANAGE_STATE( AfxGetStaticModuleState() );
157 err
= CWnd::OnCreate( inCreateStruct
);
158 require_noerr( err
, exit
);
160 GetClientRect( rect
);
161 mTree
.Create( WS_TABSTOP
| WS_VISIBLE
| WS_CHILD
| TVS_HASBUTTONS
| TVS_LINESATROOT
| TVS_HASLINES
, rect
, this,
164 err
= DNSServiceInitialize( kDNSServiceInitializeFlagsNone
, 0 );
167 // Cannot talk to the mDNSResponder service. Show the error message and exit (with kNoErr so they can see it).
169 s
.LoadString( IDS_RENDEZVOUS_NOT_AVAILABLE
);
170 mTree
.InsertItem( s
, 0, 0, TVI_ROOT
, TVI_LAST
);
175 ServiceHandlerEntry
* e
;
179 e
= new ServiceHandlerEntry
;
181 e
->type
= "_http._tcp";
182 e
->urlScheme
= "http://";
187 e
->needsLogin
= false;
188 mServiceHandlers
.Add( e
);
190 s
.LoadString( IDS_WEB_SITES
);
191 e
->treeItem
= mTree
.InsertItem( s
, 0, 0 );
192 mTree
.Expand( e
->treeItem
, TVE_EXPAND
);
194 err
= DNSServiceBrowse( &e
->ref
, 0, 0, e
->type
, NULL
, BrowseCallBack
, e
);
195 require_noerr( err
, exit
);
199 e
= new ServiceHandlerEntry
;
201 e
->type
= "_ftp._tcp";
202 e
->urlScheme
= "ftp://";
207 e
->needsLogin
= true;
208 mServiceHandlers
.Add( e
);
210 s
.LoadString( IDS_FTP_SITES
);
211 e
->treeItem
= mTree
.InsertItem( s
, 0, 0 );
212 mTree
.Expand( e
->treeItem
, TVE_EXPAND
);
214 err
= DNSServiceBrowse( &e
->ref
, 0, 0, e
->type
, NULL
, BrowseCallBack
, e
);
215 require_noerr( err
, exit
);
221 //===========================================================================================================================
223 //===========================================================================================================================
225 void ExplorerBarWindow::OnDestroy( void )
227 // Stop any resolves that may still be pending (shouldn't be any).
231 // Clean up the service handlers.
236 n
= (int) mServiceHandlers
.GetSize();
237 for( i
= 0; i
< n
; ++i
)
239 delete mServiceHandlers
[ i
];
242 DNSServiceFinalize();
246 //===========================================================================================================================
248 //===========================================================================================================================
250 void ExplorerBarWindow::OnSize( UINT inType
, int inX
, int inY
)
252 CWnd::OnSize( inType
, inX
, inY
);
253 mTree
.MoveWindow( 0, 0, inX
, inY
);
256 //===========================================================================================================================
258 //===========================================================================================================================
260 void ExplorerBarWindow::OnDoubleClick( NMHDR
*inNMHDR
, LRESULT
*outResult
)
263 ServiceInfo
* service
;
266 DEBUG_UNUSED( inNMHDR
);
268 item
= mTree
.GetSelectedItem();
269 require( item
, exit
);
271 service
= reinterpret_cast < ServiceInfo
* > ( mTree
.GetItemData( item
) );
272 require_quiet( service
, exit
);
274 err
= StartResolve( service
);
275 require_noerr( err
, exit
);
285 //===========================================================================================================================
287 //===========================================================================================================================
290 ExplorerBarWindow::BrowseCallBack(
292 DNSServiceFlags inFlags
,
293 uint32_t inInterfaceIndex
,
294 DNSServiceErrorType inErrorCode
,
297 const char * inDomain
,
300 ServiceHandlerEntry
* obj
;
301 ServiceInfo
* service
;
304 DEBUG_UNUSED( inRef
);
308 require_noerr( inErrorCode
, exit
);
309 obj
= reinterpret_cast < ServiceHandlerEntry
* > ( inContext
);
315 // Post a message to the main thread so it can handle it since MFC is not thread safe.
317 service
= new ServiceInfo
;
318 require_action( service
, exit
, err
= kNoMemoryErr
);
320 err
= UTF8StringToStringObject( inName
, service
->displayName
);
323 service
->name
= strdup( inName
);
324 require_action( service
->name
, exit
, err
= kNoMemoryErr
);
326 service
->type
= strdup( inType
);
327 require_action( service
->type
, exit
, err
= kNoMemoryErr
);
329 service
->domain
= strdup( inDomain
);
330 require_action( service
->domain
, exit
, err
= kNoMemoryErr
);
332 service
->ifi
= inInterfaceIndex
;
333 service
->handler
= obj
;
338 message
= ( inFlags
& kDNSServiceFlagsAdd
) ? WM_PRIVATE_SERVICE_ADD
: WM_PRIVATE_SERVICE_REMOVE
;
339 ok
= ::PostMessage( obj
->obj
->GetSafeHwnd(), message
, 0, (LPARAM
) service
);
348 dlog( kDebugLevelError
, "BrowseCallBack: exception thrown\n" );
358 //===========================================================================================================================
360 //===========================================================================================================================
362 LONG
ExplorerBarWindow::OnServiceAdd( WPARAM inWParam
, LPARAM inLParam
)
364 ServiceInfo
* service
;
365 ServiceHandlerEntry
* handler
;
369 DEBUG_UNUSED( inWParam
);
371 service
= reinterpret_cast < ServiceInfo
* > ( inLParam
);
373 handler
= service
->handler
;
376 cmp
= FindServiceArrayIndex( handler
->array
, *service
, index
);
379 // Found a match so update the item. The index is index + 1 so subtract 1.
382 check( index
< handler
->array
.GetSize() );
384 service
->item
= handler
->array
[ index
]->item
;
385 delete handler
->array
[ index
];
386 handler
->array
[ index
] = service
;
387 mTree
.SetItemText( service
->item
, service
->displayName
);
388 mTree
.SetItemData( service
->item
, (DWORD_PTR
) service
);
394 // Insert the new item in sorted order.
396 afterItem
= ( index
> 0 ) ? handler
->array
[ index
- 1 ]->item
: TVI_FIRST
;
397 handler
->array
.InsertAt( index
, service
);
398 service
->item
= mTree
.InsertItem( service
->displayName
, handler
->treeItem
, afterItem
);
399 mTree
.SetItemData( service
->item
, (DWORD_PTR
) service
);
401 // Make sure the item is visible if this is the first time a service was added.
403 if( handler
->treeFirst
)
405 handler
->treeFirst
= false;
406 mTree
.EnsureVisible( service
->item
);
412 //===========================================================================================================================
414 //===========================================================================================================================
416 LONG
ExplorerBarWindow::OnServiceRemove( WPARAM inWParam
, LPARAM inLParam
)
418 ServiceInfo
* service
;
419 ServiceHandlerEntry
* handler
;
423 DEBUG_UNUSED( inWParam
);
425 service
= reinterpret_cast < ServiceInfo
* > ( inLParam
);
427 handler
= service
->handler
;
430 // Search to see if we know about this service instance. If so, remove it from the list.
432 cmp
= FindServiceArrayIndex( handler
->array
, *service
, index
);
436 // Found a match remove the item. The index is index + 1 so subtract 1.
439 check( index
< handler
->array
.GetSize() );
441 mTree
.DeleteItem( handler
->array
[ index
]->item
);
442 delete handler
->array
[ index
];
443 handler
->array
.RemoveAt( index
);
453 //===========================================================================================================================
455 //===========================================================================================================================
457 OSStatus
ExplorerBarWindow::StartResolve( ServiceInfo
*inService
)
463 // Stop any current resolve that may be in progress.
467 // Resolve the service.
469 err
= DNSServiceResolve( &mResolveServiceRef
, kDNSServiceFlagsNone
, inService
->ifi
,
470 inService
->name
, inService
->type
, inService
->domain
, ResolveCallBack
, inService
->handler
);
471 require_noerr( err
, exit
);
477 //===========================================================================================================================
479 //===========================================================================================================================
481 void ExplorerBarWindow::StopResolve( void )
483 if( mResolveServiceRef
)
485 DNSServiceRefDeallocate( mResolveServiceRef
);
486 mResolveServiceRef
= NULL
;
490 //===========================================================================================================================
492 //===========================================================================================================================
495 ExplorerBarWindow::ResolveCallBack(
497 DNSServiceFlags inFlags
,
498 uint32_t inInterfaceIndex
,
499 DNSServiceErrorType inErrorCode
,
500 const char * inFullName
,
501 const char * inHostName
,
507 ExplorerBarWindow
* obj
;
508 ServiceHandlerEntry
* handler
;
511 DEBUG_UNUSED( inRef
);
512 DEBUG_UNUSED( inFlags
);
513 DEBUG_UNUSED( inErrorCode
);
514 DEBUG_UNUSED( inFullName
);
516 require_noerr( inErrorCode
, exit
);
517 handler
= (ServiceHandlerEntry
*) inContext
;
524 ResolveInfo
* resolve
;
527 dlog( kDebugLevelNotice
, "resolved %s on ifi %d to %s\n", inFullName
, inInterfaceIndex
, inHostName
);
529 // Stop resolving after the first good result.
533 // Post a message to the main thread so it can handle it since MFC is not thread safe.
535 resolve
= new ResolveInfo
;
536 require_action( resolve
, exit
, err
= kNoMemoryErr
);
538 UTF8StringToStringObject( inHostName
, resolve
->host
);
539 resolve
->port
= ntohs( inPort
);
540 resolve
->ifi
= inInterfaceIndex
;
541 resolve
->handler
= handler
;
543 err
= resolve
->txt
.SetData( inTXT
, inTXTSize
);
546 ok
= ::PostMessage( obj
->GetSafeHwnd(), WM_PRIVATE_RESOLVE
, 0, (LPARAM
) resolve
);
555 dlog( kDebugLevelError
, "ResolveCallBack: exception thrown\n" );
562 //===========================================================================================================================
564 //===========================================================================================================================
566 LONG
ExplorerBarWindow::OnResolve( WPARAM inWParam
, LPARAM inLParam
)
568 ResolveInfo
* resolve
;
576 DEBUG_UNUSED( inWParam
);
578 resolve
= reinterpret_cast < ResolveInfo
* > ( inLParam
);
581 // Get login info if needed.
583 if( resolve
->handler
->needsLogin
)
587 if( !dialog
.GetLogin( username
, password
) )
593 // If the HTTP TXT record is a "path=" entry, use it as the resource path. Otherwise, use "/".
596 if( strcmp( resolve
->handler
->type
, "_http._tcp" ) == 0 )
598 resolve
->txt
.GetData( &path
, &pathSize
);
603 if( ( pathSize
> kTXTRecordKeyPathSize
) && ( memicmp( path
, kTXTRecordKeyPath
, kTXTRecordKeyPathSize
) == 0 ) )
605 path
+= kTXTRecordKeyPathSize
;
606 pathSize
-= kTXTRecordKeyPathSize
;
608 else if( pathSize
== 0 )
610 path
= (uint8_t *) "/";
620 path
= (uint8_t *) "";
624 // Build the URL in the following format:
626 // <urlScheme>[<username>[:<password>]@]<name/ip>[<path>]
628 url
.AppendFormat( TEXT( "%S" ), resolve
->handler
->urlScheme
); // URL Scheme
629 if( username
.GetLength() > 0 )
631 url
.AppendFormat( TEXT( "%s" ), username
); // Username
632 if( password
.GetLength() > 0 )
634 url
.AppendFormat( TEXT( ":%s" ), password
); // Password
636 url
.AppendFormat( TEXT( "@" ) );
639 url
+= resolve
->host
; // Host
640 url
.AppendFormat( TEXT( ":%d" ), resolve
->port
); // :Port
641 url
.AppendFormat( TEXT( "%S" ), pathPrefix
); // Path Prefix ("/" or empty).
642 url
.AppendFormat( TEXT( "%.*S" ), (int) pathSize
, (char *) path
); // Path (possibly empty).
644 // Tell Internet Explorer to go to the URL.
647 mOwner
->GoToURL( url
);
658 //===========================================================================================================================
659 // FindServiceArrayIndex
660 //===========================================================================================================================
662 DEBUG_LOCAL
int FindServiceArrayIndex( const ServiceInfoArray
&inArray
, const ServiceInfo
&inService
, int &outIndex
)
672 hi
= (int)( inArray
.GetSize() - 1 );
675 mid
= ( lo
+ hi
) / 2;
676 result
= inService
.displayName
.CompareNoCase( inArray
[ mid
]->displayName
);
679 result
= ( (int) inService
.ifi
) - ( (int) inArray
[ mid
]->ifi
);
685 else if( result
< 0 )
696 mid
+= 1; // Bump index so new item is inserted after matching item.
698 else if( result
> 0 )
706 //===========================================================================================================================
707 // UTF8StringToStringObject
708 //===========================================================================================================================
710 DEBUG_LOCAL OSStatus
UTF8StringToStringObject( const char *inUTF8
, CString
&inObject
)
718 n
= MultiByteToWideChar( CP_UTF8
, 0, inUTF8
, -1, NULL
, 0 );
721 unicode
= (BSTR
) malloc( (size_t)( n
* sizeof( wchar_t ) ) );
724 err
= ERROR_INSUFFICIENT_BUFFER
;
728 n
= MultiByteToWideChar( CP_UTF8
, 0, inUTF8
, -1, unicode
, n
);
735 err
= ERROR_NO_UNICODE_TRANSLATION
;