]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSWindows/Applications/ExplorerPlugin/ExplorerBarWindow.cpp
7ca9f2aafb85549ed39bbb2d79f3aadd8debfab4
[apple/mdnsresponder.git] / mDNSWindows / Applications / ExplorerPlugin / ExplorerBarWindow.cpp
1 /*
2 * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
7 *
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
13 * file.
14 *
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.
22 *
23 * @APPLE_LICENSE_HEADER_END@
24
25 Change History (most recent first):
26
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.
31
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.
34
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.
37
38 Revision 1.2 2004/02/21 04:36:19 bradley
39 Enable dot local name lookups now that the NSP is being installed.
40
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.
43
44 */
45
46 #include "StdAfx.h"
47
48 #include "CommonServices.h"
49 #include "DebugServices.h"
50 #include "DNSSD.h"
51
52 #include "ExplorerBar.h"
53 #include "LoginDialog.h"
54 #include "Resource.h"
55
56 #include "ExplorerBarWindow.h"
57
58 // MFC Debugging
59
60 #ifdef _DEBUG
61 #define new DEBUG_NEW
62 #undef THIS_FILE
63 static char THIS_FILE[] = __FILE__;
64 #endif
65
66 #if 0
67 #pragma mark == Constants ==
68 #endif
69
70 //===========================================================================================================================
71 // Constants
72 //===========================================================================================================================
73
74 // Control IDs
75
76 #define IDC_EXPLORER_TREE 1234
77
78 // Private Messages
79
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 )
83
84 // TXT records
85
86 #define kTXTRecordKeyPath "path="
87 #define kTXTRecordKeyPathSize sizeof_string( kTXTRecordKeyPath )
88
89 #if 0
90 #pragma mark == Prototypes ==
91 #endif
92
93 //===========================================================================================================================
94 // Prototypes
95 //===========================================================================================================================
96
97 DEBUG_LOCAL int FindServiceArrayIndex( const ServiceInfoArray &inArray, const ServiceInfo &inService, int &outIndex );
98 DEBUG_LOCAL OSStatus UTF8StringToStringObject( const char *inUTF8, CString &inObject );
99
100 #if 0
101 #pragma mark == Message Map ==
102 #endif
103
104 //===========================================================================================================================
105 // Message Map
106 //===========================================================================================================================
107
108 BEGIN_MESSAGE_MAP( ExplorerBarWindow, CWnd )
109 ON_WM_CREATE()
110 ON_WM_DESTROY()
111 ON_WM_SIZE()
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 )
116 END_MESSAGE_MAP()
117
118 #if 0
119 #pragma mark -
120 #endif
121
122 //===========================================================================================================================
123 // ExplorerBarWindow
124 //===========================================================================================================================
125
126 ExplorerBarWindow::ExplorerBarWindow( void )
127 {
128 mOwner = NULL;
129 mResolveServiceRef = NULL;
130 }
131
132 //===========================================================================================================================
133 // ~ExplorerBarWindow
134 //===========================================================================================================================
135
136 ExplorerBarWindow::~ExplorerBarWindow( void )
137 {
138 //
139 }
140
141 #if 0
142 #pragma mark -
143 #endif
144
145 //===========================================================================================================================
146 // OnCreate
147 //===========================================================================================================================
148
149 int ExplorerBarWindow::OnCreate( LPCREATESTRUCT inCreateStruct )
150 {
151 AFX_MANAGE_STATE( AfxGetStaticModuleState() );
152
153 OSStatus err;
154 CRect rect;
155 CString s;
156
157 err = CWnd::OnCreate( inCreateStruct );
158 require_noerr( err, exit );
159
160 GetClientRect( rect );
161 mTree.Create( WS_TABSTOP | WS_VISIBLE | WS_CHILD | TVS_HASBUTTONS | TVS_LINESATROOT | TVS_HASLINES, rect, this,
162 IDC_EXPLORER_TREE );
163
164 err = DNSServiceInitialize( kDNSServiceInitializeFlagsNone, 0 );
165 if( err != kNoErr )
166 {
167 // Cannot talk to the mDNSResponder service. Show the error message and exit (with kNoErr so they can see it).
168
169 s.LoadString( IDS_RENDEZVOUS_NOT_AVAILABLE );
170 mTree.InsertItem( s, 0, 0, TVI_ROOT, TVI_LAST );
171 err = kNoErr;
172 goto exit;
173 }
174
175 ServiceHandlerEntry * e;
176
177 // Web Site Handler
178
179 e = new ServiceHandlerEntry;
180 check( e );
181 e->type = "_http._tcp";
182 e->urlScheme = "http://";
183 e->ref = NULL;
184 e->treeItem = NULL;
185 e->treeFirst = true;
186 e->obj = this;
187 e->needsLogin = false;
188 mServiceHandlers.Add( e );
189
190 s.LoadString( IDS_WEB_SITES );
191 e->treeItem = mTree.InsertItem( s, 0, 0 );
192 mTree.Expand( e->treeItem, TVE_EXPAND );
193
194 err = DNSServiceBrowse( &e->ref, 0, 0, e->type, NULL, BrowseCallBack, e );
195 require_noerr( err, exit );
196
197 // FTP Site Handler
198
199 e = new ServiceHandlerEntry;
200 check( e );
201 e->type = "_ftp._tcp";
202 e->urlScheme = "ftp://";
203 e->ref = NULL;
204 e->treeItem = NULL;
205 e->treeFirst = true;
206 e->obj = this;
207 e->needsLogin = true;
208 mServiceHandlers.Add( e );
209
210 s.LoadString( IDS_FTP_SITES );
211 e->treeItem = mTree.InsertItem( s, 0, 0 );
212 mTree.Expand( e->treeItem, TVE_EXPAND );
213
214 err = DNSServiceBrowse( &e->ref, 0, 0, e->type, NULL, BrowseCallBack, e );
215 require_noerr( err, exit );
216
217 exit:
218 return( err );
219 }
220
221 //===========================================================================================================================
222 // OnDestroy
223 //===========================================================================================================================
224
225 void ExplorerBarWindow::OnDestroy( void )
226 {
227 // Stop any resolves that may still be pending (shouldn't be any).
228
229 StopResolve();
230
231 // Clean up the service handlers.
232
233 int i;
234 int n;
235
236 n = (int) mServiceHandlers.GetSize();
237 for( i = 0; i < n; ++i )
238 {
239 delete mServiceHandlers[ i ];
240 }
241
242 DNSServiceFinalize();
243 CWnd::OnDestroy();
244 }
245
246 //===========================================================================================================================
247 // OnSize
248 //===========================================================================================================================
249
250 void ExplorerBarWindow::OnSize( UINT inType, int inX, int inY )
251 {
252 CWnd::OnSize( inType, inX, inY );
253 mTree.MoveWindow( 0, 0, inX, inY );
254 }
255
256 //===========================================================================================================================
257 // OnDoubleClick
258 //===========================================================================================================================
259
260 void ExplorerBarWindow::OnDoubleClick( NMHDR *inNMHDR, LRESULT *outResult )
261 {
262 HTREEITEM item;
263 ServiceInfo * service;
264 OSStatus err;
265
266 DEBUG_UNUSED( inNMHDR );
267
268 item = mTree.GetSelectedItem();
269 require( item, exit );
270
271 service = reinterpret_cast < ServiceInfo * > ( mTree.GetItemData( item ) );
272 require_quiet( service, exit );
273
274 err = StartResolve( service );
275 require_noerr( err, exit );
276
277 exit:
278 *outResult = 0;
279 }
280
281 #if 0
282 #pragma mark -
283 #endif
284
285 //===========================================================================================================================
286 // BrowseCallBack
287 //===========================================================================================================================
288
289 void CALLBACK_COMPAT
290 ExplorerBarWindow::BrowseCallBack(
291 DNSServiceRef inRef,
292 DNSServiceFlags inFlags,
293 uint32_t inInterfaceIndex,
294 DNSServiceErrorType inErrorCode,
295 const char * inName,
296 const char * inType,
297 const char * inDomain,
298 void * inContext )
299 {
300 ServiceHandlerEntry * obj;
301 ServiceInfo * service;
302 OSStatus err;
303
304 DEBUG_UNUSED( inRef );
305
306 service = NULL;
307
308 require_noerr( inErrorCode, exit );
309 obj = reinterpret_cast < ServiceHandlerEntry * > ( inContext );
310 check( obj );
311 check( obj->obj );
312
313 try
314 {
315 // Post a message to the main thread so it can handle it since MFC is not thread safe.
316
317 service = new ServiceInfo;
318 require_action( service, exit, err = kNoMemoryErr );
319
320 err = UTF8StringToStringObject( inName, service->displayName );
321 check_noerr( err );
322
323 service->name = strdup( inName );
324 require_action( service->name, exit, err = kNoMemoryErr );
325
326 service->type = strdup( inType );
327 require_action( service->type, exit, err = kNoMemoryErr );
328
329 service->domain = strdup( inDomain );
330 require_action( service->domain, exit, err = kNoMemoryErr );
331
332 service->ifi = inInterfaceIndex;
333 service->handler = obj;
334
335 DWORD message;
336 BOOL ok;
337
338 message = ( inFlags & kDNSServiceFlagsAdd ) ? WM_PRIVATE_SERVICE_ADD : WM_PRIVATE_SERVICE_REMOVE;
339 ok = ::PostMessage( obj->obj->GetSafeHwnd(), message, 0, (LPARAM) service );
340 check( ok );
341 if( ok )
342 {
343 service = NULL;
344 }
345 }
346 catch( ... )
347 {
348 dlog( kDebugLevelError, "BrowseCallBack: exception thrown\n" );
349 }
350
351 exit:
352 if( service )
353 {
354 delete service;
355 }
356 }
357
358 //===========================================================================================================================
359 // OnServiceAdd
360 //===========================================================================================================================
361
362 LONG ExplorerBarWindow::OnServiceAdd( WPARAM inWParam, LPARAM inLParam )
363 {
364 ServiceInfo * service;
365 ServiceHandlerEntry * handler;
366 int cmp;
367 int index;
368
369 DEBUG_UNUSED( inWParam );
370
371 service = reinterpret_cast < ServiceInfo * > ( inLParam );
372 check( service );
373 handler = service->handler;
374 check( handler );
375
376 cmp = FindServiceArrayIndex( handler->array, *service, index );
377 if( cmp == 0 )
378 {
379 // Found a match so update the item. The index is index + 1 so subtract 1.
380
381 index -= 1;
382 check( index < handler->array.GetSize() );
383
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 );
389 }
390 else
391 {
392 HTREEITEM afterItem;
393
394 // Insert the new item in sorted order.
395
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 );
400
401 // Make sure the item is visible if this is the first time a service was added.
402
403 if( handler->treeFirst )
404 {
405 handler->treeFirst = false;
406 mTree.EnsureVisible( service->item );
407 }
408 }
409 return( 0 );
410 }
411
412 //===========================================================================================================================
413 // OnServiceRemove
414 //===========================================================================================================================
415
416 LONG ExplorerBarWindow::OnServiceRemove( WPARAM inWParam, LPARAM inLParam )
417 {
418 ServiceInfo * service;
419 ServiceHandlerEntry * handler;
420 int cmp;
421 int index;
422
423 DEBUG_UNUSED( inWParam );
424
425 service = reinterpret_cast < ServiceInfo * > ( inLParam );
426 check( service );
427 handler = service->handler;
428 check( handler );
429
430 // Search to see if we know about this service instance. If so, remove it from the list.
431
432 cmp = FindServiceArrayIndex( handler->array, *service, index );
433 check( cmp == 0 );
434 if( cmp == 0 )
435 {
436 // Found a match remove the item. The index is index + 1 so subtract 1.
437
438 index -= 1;
439 check( index < handler->array.GetSize() );
440
441 mTree.DeleteItem( handler->array[ index ]->item );
442 delete handler->array[ index ];
443 handler->array.RemoveAt( index );
444 }
445 delete service;
446 return( 0 );
447 }
448
449 #if 0
450 #pragma mark -
451 #endif
452
453 //===========================================================================================================================
454 // StartResolve
455 //===========================================================================================================================
456
457 OSStatus ExplorerBarWindow::StartResolve( ServiceInfo *inService )
458 {
459 OSStatus err;
460
461 check( inService );
462
463 // Stop any current resolve that may be in progress.
464
465 StopResolve();
466
467 // Resolve the service.
468
469 err = DNSServiceResolve( &mResolveServiceRef, kDNSServiceFlagsNone, inService->ifi,
470 inService->name, inService->type, inService->domain, ResolveCallBack, inService->handler );
471 require_noerr( err, exit );
472
473 exit:
474 return( err );
475 }
476
477 //===========================================================================================================================
478 // StopResolve
479 //===========================================================================================================================
480
481 void ExplorerBarWindow::StopResolve( void )
482 {
483 if( mResolveServiceRef )
484 {
485 DNSServiceRefDeallocate( mResolveServiceRef );
486 mResolveServiceRef = NULL;
487 }
488 }
489
490 //===========================================================================================================================
491 // ResolveCallBack
492 //===========================================================================================================================
493
494 void CALLBACK_COMPAT
495 ExplorerBarWindow::ResolveCallBack(
496 DNSServiceRef inRef,
497 DNSServiceFlags inFlags,
498 uint32_t inInterfaceIndex,
499 DNSServiceErrorType inErrorCode,
500 const char * inFullName,
501 const char * inHostName,
502 uint16_t inPort,
503 uint16_t inTXTSize,
504 const char * inTXT,
505 void * inContext )
506 {
507 ExplorerBarWindow * obj;
508 ServiceHandlerEntry * handler;
509 OSStatus err;
510
511 DEBUG_UNUSED( inRef );
512 DEBUG_UNUSED( inFlags );
513 DEBUG_UNUSED( inErrorCode );
514 DEBUG_UNUSED( inFullName );
515
516 require_noerr( inErrorCode, exit );
517 handler = (ServiceHandlerEntry *) inContext;
518 check( handler );
519 obj = handler->obj;
520 check( obj );
521
522 try
523 {
524 ResolveInfo * resolve;
525 BOOL ok;
526
527 dlog( kDebugLevelNotice, "resolved %s on ifi %d to %s\n", inFullName, inInterfaceIndex, inHostName );
528
529 // Stop resolving after the first good result.
530
531 obj->StopResolve();
532
533 // Post a message to the main thread so it can handle it since MFC is not thread safe.
534
535 resolve = new ResolveInfo;
536 require_action( resolve, exit, err = kNoMemoryErr );
537
538 UTF8StringToStringObject( inHostName, resolve->host );
539 resolve->port = ntohs( inPort );
540 resolve->ifi = inInterfaceIndex;
541 resolve->handler = handler;
542
543 err = resolve->txt.SetData( inTXT, inTXTSize );
544 check_noerr( err );
545
546 ok = ::PostMessage( obj->GetSafeHwnd(), WM_PRIVATE_RESOLVE, 0, (LPARAM) resolve );
547 check( ok );
548 if( !ok )
549 {
550 delete resolve;
551 }
552 }
553 catch( ... )
554 {
555 dlog( kDebugLevelError, "ResolveCallBack: exception thrown\n" );
556 }
557
558 exit:
559 return;
560 }
561
562 //===========================================================================================================================
563 // OnResolve
564 //===========================================================================================================================
565
566 LONG ExplorerBarWindow::OnResolve( WPARAM inWParam, LPARAM inLParam )
567 {
568 ResolveInfo * resolve;
569 CString url;
570 uint8_t * path;
571 size_t pathSize;
572 char * pathPrefix;
573 CString username;
574 CString password;
575
576 DEBUG_UNUSED( inWParam );
577
578 resolve = reinterpret_cast < ResolveInfo * > ( inLParam );
579 check( resolve );
580
581 // Get login info if needed.
582
583 if( resolve->handler->needsLogin )
584 {
585 LoginDialog dialog;
586
587 if( !dialog.GetLogin( username, password ) )
588 {
589 goto exit;
590 }
591 }
592
593 // If the HTTP TXT record is a "path=" entry, use it as the resource path. Otherwise, use "/".
594
595 pathPrefix = "";
596 if( strcmp( resolve->handler->type, "_http._tcp" ) == 0 )
597 {
598 resolve->txt.GetData( &path, &pathSize );
599 if( pathSize > 0 )
600 {
601 pathSize = *path++;
602 }
603 if( ( pathSize > kTXTRecordKeyPathSize ) && ( memicmp( path, kTXTRecordKeyPath, kTXTRecordKeyPathSize ) == 0 ) )
604 {
605 path += kTXTRecordKeyPathSize;
606 pathSize -= kTXTRecordKeyPathSize;
607 }
608 else if( pathSize == 0 )
609 {
610 path = (uint8_t *) "/";
611 pathSize = 1;
612 }
613 if( *path != '/' )
614 {
615 pathPrefix = "/";
616 }
617 }
618 else
619 {
620 path = (uint8_t *) "";
621 pathSize = 1;
622 }
623
624 // Build the URL in the following format:
625 //
626 // <urlScheme>[<username>[:<password>]@]<name/ip>[<path>]
627
628 url.AppendFormat( TEXT( "%S" ), resolve->handler->urlScheme ); // URL Scheme
629 if( username.GetLength() > 0 )
630 {
631 url.AppendFormat( TEXT( "%s" ), username ); // Username
632 if( password.GetLength() > 0 )
633 {
634 url.AppendFormat( TEXT( ":%s" ), password ); // Password
635 }
636 url.AppendFormat( TEXT( "@" ) );
637 }
638
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).
643
644 // Tell Internet Explorer to go to the URL.
645
646 check( mOwner );
647 mOwner->GoToURL( url );
648
649 exit:
650 delete resolve;
651 return( 0 );
652 }
653
654 #if 0
655 #pragma mark -
656 #endif
657
658 //===========================================================================================================================
659 // FindServiceArrayIndex
660 //===========================================================================================================================
661
662 DEBUG_LOCAL int FindServiceArrayIndex( const ServiceInfoArray &inArray, const ServiceInfo &inService, int &outIndex )
663 {
664 int result;
665 int lo;
666 int hi;
667 int mid;
668
669 result = -1;
670 mid = 0;
671 lo = 0;
672 hi = (int)( inArray.GetSize() - 1 );
673 while( lo <= hi )
674 {
675 mid = ( lo + hi ) / 2;
676 result = inService.displayName.CompareNoCase( inArray[ mid ]->displayName );
677 if( result == 0 )
678 {
679 result = ( (int) inService.ifi ) - ( (int) inArray[ mid ]->ifi );
680 }
681 if( result == 0 )
682 {
683 break;
684 }
685 else if( result < 0 )
686 {
687 hi = mid - 1;
688 }
689 else
690 {
691 lo = mid + 1;
692 }
693 }
694 if( result == 0 )
695 {
696 mid += 1; // Bump index so new item is inserted after matching item.
697 }
698 else if( result > 0 )
699 {
700 mid += 1;
701 }
702 outIndex = mid;
703 return( result );
704 }
705
706 //===========================================================================================================================
707 // UTF8StringToStringObject
708 //===========================================================================================================================
709
710 DEBUG_LOCAL OSStatus UTF8StringToStringObject( const char *inUTF8, CString &inObject )
711 {
712 OSStatus err;
713 int n;
714 BSTR unicode;
715
716 unicode = NULL;
717
718 n = MultiByteToWideChar( CP_UTF8, 0, inUTF8, -1, NULL, 0 );
719 if( n > 0 )
720 {
721 unicode = (BSTR) malloc( (size_t)( n * sizeof( wchar_t ) ) );
722 if( !unicode )
723 {
724 err = ERROR_INSUFFICIENT_BUFFER;
725 goto exit;
726 }
727
728 n = MultiByteToWideChar( CP_UTF8, 0, inUTF8, -1, unicode, n );
729 try
730 {
731 inObject = unicode;
732 }
733 catch( ... )
734 {
735 err = ERROR_NO_UNICODE_TRANSLATION;
736 goto exit;
737 }
738 }
739 else
740 {
741 inObject = "";
742 }
743 err = ERROR_SUCCESS;
744
745 exit:
746 if( unicode )
747 {
748 free( unicode );
749 }
750 return( err );
751 }