]> git.saurik.com Git - apple/mdnsresponder.git/blob - Clients/ExplorerPlugin/ExplorerBarWindow.cpp
mDNSResponder-258.13.tar.gz
[apple/mdnsresponder.git] / Clients / ExplorerPlugin / ExplorerBarWindow.cpp
1 /* -*- Mode: C; tab-width: 4 -*-
2 *
3 * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved.
4 *
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
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
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.
16 */
17
18 #include "StdAfx.h"
19
20 #include "CommonServices.h"
21 #include "DebugServices.h"
22 #include "WinServices.h"
23 #include "dns_sd.h"
24
25 #include "ExplorerBar.h"
26 #include "LoginDialog.h"
27 #include "Resource.h"
28
29 #include "ExplorerBarWindow.h"
30 #include "ExplorerPlugin.h"
31
32 // MFC Debugging
33
34 #ifdef _DEBUG
35 #define new DEBUG_NEW
36 #undef THIS_FILE
37 static char THIS_FILE[] = __FILE__;
38 #endif
39
40 #if 0
41 #pragma mark == Constants ==
42 #endif
43
44 //===========================================================================================================================
45 // Constants
46 //===========================================================================================================================
47
48 // Control IDs
49
50 #define IDC_EXPLORER_TREE 1234
51
52 // Private Messages
53
54 #define WM_PRIVATE_SERVICE_EVENT ( WM_USER + 0x100 )
55
56 // TXT records
57
58 #define kTXTRecordKeyPath "path"
59
60 // IE Icon resource
61
62 #define kIEIconResource 32529
63
64
65 #if 0
66 #pragma mark == Prototypes ==
67 #endif
68
69 //===========================================================================================================================
70 // Prototypes
71 //===========================================================================================================================
72
73 DEBUG_LOCAL int FindServiceArrayIndex( const ServiceInfoArray &inArray, const ServiceInfo &inService, int &outIndex );
74
75 #if 0
76 #pragma mark == Message Map ==
77 #endif
78
79 //===========================================================================================================================
80 // Message Map
81 //===========================================================================================================================
82
83 BEGIN_MESSAGE_MAP( ExplorerBarWindow, CWnd )
84 ON_WM_CREATE()
85 ON_WM_DESTROY()
86 ON_WM_SIZE()
87 ON_NOTIFY( NM_DBLCLK, IDC_EXPLORER_TREE, OnDoubleClick )
88 ON_MESSAGE( WM_PRIVATE_SERVICE_EVENT, OnServiceEvent )
89 END_MESSAGE_MAP()
90
91 #if 0
92 #pragma mark -
93 #endif
94
95 //===========================================================================================================================
96 // ExplorerBarWindow
97 //===========================================================================================================================
98
99 ExplorerBarWindow::ExplorerBarWindow( void )
100 {
101 mOwner = NULL;
102 mResolveServiceRef = NULL;
103 }
104
105 //===========================================================================================================================
106 // ~ExplorerBarWindow
107 //===========================================================================================================================
108
109 ExplorerBarWindow::~ExplorerBarWindow( void )
110 {
111 //
112 }
113
114 #if 0
115 #pragma mark -
116 #endif
117
118 //===========================================================================================================================
119 // OnCreate
120 //===========================================================================================================================
121
122 int ExplorerBarWindow::OnCreate( LPCREATESTRUCT inCreateStruct )
123 {
124 AFX_MANAGE_STATE( AfxGetStaticModuleState() );
125
126 HINSTANCE module = NULL;
127 OSStatus err;
128 CRect rect;
129 CBitmap bitmap;
130 CString s;
131
132 err = CWnd::OnCreate( inCreateStruct );
133 require_noerr( err, exit );
134
135 GetClientRect( rect );
136 mTree.Create( WS_TABSTOP | WS_VISIBLE | WS_CHILD | TVS_HASBUTTONS | TVS_LINESATROOT | TVS_NOHSCROLL , rect, this,
137 IDC_EXPLORER_TREE );
138
139 ServiceHandlerEntry * e;
140
141 s.LoadString( IDS_ABOUT );
142 m_about = mTree.InsertItem( s, 0, 0 );
143
144 // Web Site Handler
145
146 e = new ServiceHandlerEntry;
147 check( e );
148 e->type = "_http._tcp";
149 e->urlScheme = "http://";
150 e->ref = NULL;
151 e->obj = this;
152 e->needsLogin = false;
153 mServiceHandlers.Add( e );
154
155 err = DNSServiceBrowse( &e->ref, 0, 0, e->type, NULL, BrowseCallBack, e );
156 require_noerr( err, exit );
157
158 err = WSAAsyncSelect((SOCKET) DNSServiceRefSockFD(e->ref), m_hWnd, WM_PRIVATE_SERVICE_EVENT, FD_READ|FD_CLOSE);
159 require_noerr( err, exit );
160
161 m_serviceRefs.push_back(e->ref);
162
163 #if defined( _BROWSE_FOR_HTTPS_ )
164 e = new ServiceHandlerEntry;
165 check( e );
166 e->type = "_https._tcp";
167 e->urlScheme = "https://";
168 e->ref = NULL;
169 e->obj = this;
170 e->needsLogin = false;
171 mServiceHandlers.Add( e );
172
173 err = DNSServiceBrowse( &e->ref, 0, 0, e->type, NULL, BrowseCallBack, e );
174 require_noerr( err, exit );
175
176 err = WSAAsyncSelect((SOCKET) DNSServiceRefSockFD(e->ref), m_hWnd, WM_PRIVATE_SERVICE_EVENT, FD_READ|FD_CLOSE);
177 require_noerr( err, exit );
178
179 m_serviceRefs.push_back(e->ref);
180 #endif
181
182 m_imageList.Create( 16, 16, ILC_MASK | ILC_COLOR16, 2, 0);
183
184 bitmap.Attach( ::LoadBitmap( GetNonLocalizedResources(), MAKEINTRESOURCE( IDB_LOGO ) ) );
185 m_imageList.Add( &bitmap, (CBitmap*) NULL );
186 bitmap.Detach();
187
188 mTree.SetImageList(&m_imageList, TVSIL_NORMAL);
189
190 exit:
191
192 if ( module )
193 {
194 FreeLibrary( module );
195 module = NULL;
196 }
197
198 // Cannot talk to the mDNSResponder service. Show the error message and exit (with kNoErr so they can see it).
199 if ( err )
200 {
201 if ( err == kDNSServiceErr_Firewall )
202 {
203 s.LoadString( IDS_FIREWALL );
204 }
205 else
206 {
207 s.LoadString( IDS_MDNSRESPONDER_NOT_AVAILABLE );
208 }
209
210 mTree.DeleteAllItems();
211 mTree.InsertItem( s, 0, 0, TVI_ROOT, TVI_LAST );
212
213 err = kNoErr;
214 }
215
216 return( err );
217 }
218
219 //===========================================================================================================================
220 // OnDestroy
221 //===========================================================================================================================
222
223 void ExplorerBarWindow::OnDestroy( void )
224 {
225 // Stop any resolves that may still be pending (shouldn't be any).
226
227 StopResolve();
228
229 // Clean up the extant browses
230 while (m_serviceRefs.size() > 0)
231 {
232 //
233 // take the head of the list
234 //
235 DNSServiceRef ref = m_serviceRefs.front();
236
237 //
238 // Stop will remove it from the list
239 //
240 Stop( ref );
241 }
242
243 // Clean up the service handlers.
244
245 int i;
246 int n;
247
248 n = (int) mServiceHandlers.GetSize();
249 for( i = 0; i < n; ++i )
250 {
251 delete mServiceHandlers[ i ];
252 }
253
254 CWnd::OnDestroy();
255 }
256
257 //===========================================================================================================================
258 // OnSize
259 //===========================================================================================================================
260
261 void ExplorerBarWindow::OnSize( UINT inType, int inX, int inY )
262 {
263 CWnd::OnSize( inType, inX, inY );
264 mTree.MoveWindow( 0, 0, inX, inY );
265 }
266
267 //===========================================================================================================================
268 // OnDoubleClick
269 //===========================================================================================================================
270
271 void ExplorerBarWindow::OnDoubleClick( NMHDR *inNMHDR, LRESULT *outResult )
272 {
273 HTREEITEM item;
274 ServiceInfo * service;
275 OSStatus err;
276
277 DEBUG_UNUSED( inNMHDR );
278
279 item = mTree.GetSelectedItem();
280 require( item, exit );
281
282 // Tell Internet Explorer to go to the URL if it's about item
283
284 if ( item == m_about )
285 {
286 CString url;
287
288 check( mOwner );
289
290 url.LoadString( IDS_ABOUT_URL );
291 mOwner->GoToURL( url );
292 }
293 else
294 {
295 service = reinterpret_cast < ServiceInfo * > ( mTree.GetItemData( item ) );
296 require_quiet( service, exit );
297
298 err = StartResolve( service );
299 require_noerr( err, exit );
300 }
301
302 exit:
303 *outResult = 0;
304 }
305
306
307 //===========================================================================================================================
308 // OnServiceEvent
309 //===========================================================================================================================
310
311 LRESULT
312 ExplorerBarWindow::OnServiceEvent(WPARAM inWParam, LPARAM inLParam)
313 {
314 if (WSAGETSELECTERROR(inLParam) && !(HIWORD(inLParam)))
315 {
316 dlog( kDebugLevelError, "OnServiceEvent: window error\n" );
317 }
318 else
319 {
320 SOCKET sock = (SOCKET) inWParam;
321
322 // iterate thru list
323 ServiceRefList::iterator it;
324
325 for (it = m_serviceRefs.begin(); it != m_serviceRefs.end(); it++)
326 {
327 DNSServiceRef ref = *it;
328
329 check(ref != NULL);
330
331 if ((SOCKET) DNSServiceRefSockFD(ref) == sock)
332 {
333 DNSServiceErrorType err;
334
335 err = DNSServiceProcessResult(ref);
336
337 if (err != 0)
338 {
339 CString s;
340
341 s.LoadString( IDS_MDNSRESPONDER_NOT_AVAILABLE );
342 mTree.DeleteAllItems();
343 mTree.InsertItem( s, 0, 0, TVI_ROOT, TVI_LAST );
344
345 Stop(ref);
346 }
347
348 break;
349 }
350 }
351 }
352
353 return ( 0 );
354 }
355
356 #if 0
357 #pragma mark -
358 #endif
359
360 //===========================================================================================================================
361 // BrowseCallBack
362 //===========================================================================================================================
363
364 void DNSSD_API
365 ExplorerBarWindow::BrowseCallBack(
366 DNSServiceRef inRef,
367 DNSServiceFlags inFlags,
368 uint32_t inInterfaceIndex,
369 DNSServiceErrorType inErrorCode,
370 const char * inName,
371 const char * inType,
372 const char * inDomain,
373 void * inContext )
374 {
375 ServiceHandlerEntry * obj;
376 ServiceInfo * service;
377 OSStatus err;
378
379 DEBUG_UNUSED( inRef );
380
381 obj = NULL;
382 service = NULL;
383
384 require_noerr( inErrorCode, exit );
385 obj = reinterpret_cast < ServiceHandlerEntry * > ( inContext );
386 check( obj );
387 check( obj->obj );
388
389 //
390 // set the UI to hold off on updates
391 //
392 obj->obj->mTree.SetRedraw(FALSE);
393
394 try
395 {
396 service = new ServiceInfo;
397 require_action( service, exit, err = kNoMemoryErr );
398
399 err = UTF8StringToStringObject( inName, service->displayName );
400 check_noerr( err );
401
402 service->name = _strdup( inName );
403 require_action( service->name, exit, err = kNoMemoryErr );
404
405 service->type = _strdup( inType );
406 require_action( service->type, exit, err = kNoMemoryErr );
407
408 service->domain = _strdup( inDomain );
409 require_action( service->domain, exit, err = kNoMemoryErr );
410
411 service->ifi = inInterfaceIndex;
412 service->handler = obj;
413
414 service->refs = 1;
415
416 if (inFlags & kDNSServiceFlagsAdd) obj->obj->OnServiceAdd (service);
417 else obj->obj->OnServiceRemove(service);
418
419 service = NULL;
420 }
421 catch( ... )
422 {
423 dlog( kDebugLevelError, "BrowseCallBack: exception thrown\n" );
424 }
425
426 exit:
427 //
428 // If no more coming, then update UI
429 //
430 if (obj && obj->obj && ((inFlags & kDNSServiceFlagsMoreComing) == 0))
431 {
432 obj->obj->mTree.SetRedraw(TRUE);
433 obj->obj->mTree.Invalidate();
434 }
435
436 if( service )
437 {
438 delete service;
439 }
440 }
441
442 //===========================================================================================================================
443 // OnServiceAdd
444 //===========================================================================================================================
445
446 LONG ExplorerBarWindow::OnServiceAdd( ServiceInfo * service )
447 {
448 ServiceHandlerEntry * handler;
449 int cmp;
450 int index;
451
452
453 check( service );
454 handler = service->handler;
455 check( handler );
456
457 cmp = FindServiceArrayIndex( handler->array, *service, index );
458 if( cmp == 0 )
459 {
460 // Found a match so update the item. The index is index + 1 so subtract 1.
461
462 index -= 1;
463 check( index < handler->array.GetSize() );
464
465 handler->array[ index ]->refs++;
466
467 delete service;
468 }
469 else
470 {
471 HTREEITEM afterItem;
472
473 // Insert the new item in sorted order.
474
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 );
479 }
480 return( 0 );
481 }
482
483 //===========================================================================================================================
484 // OnServiceRemove
485 //===========================================================================================================================
486
487 LONG ExplorerBarWindow::OnServiceRemove( ServiceInfo * service )
488 {
489 ServiceHandlerEntry * handler;
490 int cmp;
491 int index;
492
493
494 check( service );
495 handler = service->handler;
496 check( handler );
497
498 // Search to see if we know about this service instance. If so, remove it from the list.
499
500 cmp = FindServiceArrayIndex( handler->array, *service, index );
501 check( cmp == 0 );
502
503 if( cmp == 0 )
504 {
505 // Possibly found a match remove the item. The index
506 // is index + 1 so subtract 1.
507 index -= 1;
508 check( index < handler->array.GetSize() );
509
510 if ( --handler->array[ index ]->refs == 0 )
511 {
512 mTree.DeleteItem( handler->array[ index ]->item );
513 delete handler->array[ index ];
514 handler->array.RemoveAt( index );
515 }
516 }
517
518 delete service;
519 return( 0 );
520 }
521
522 #if 0
523 #pragma mark -
524 #endif
525
526 //===========================================================================================================================
527 // StartResolve
528 //===========================================================================================================================
529
530 OSStatus ExplorerBarWindow::StartResolve( ServiceInfo *inService )
531 {
532 OSStatus err;
533
534 check( inService );
535
536 // Stop any current resolve that may be in progress.
537
538 StopResolve();
539
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 );
544
545 err = WSAAsyncSelect((SOCKET) DNSServiceRefSockFD(mResolveServiceRef), m_hWnd, WM_PRIVATE_SERVICE_EVENT, FD_READ|FD_CLOSE);
546 require_noerr( err, exit );
547
548 m_serviceRefs.push_back(mResolveServiceRef);
549
550 exit:
551 return( err );
552 }
553
554 //===========================================================================================================================
555 // StopResolve
556 //===========================================================================================================================
557
558 void ExplorerBarWindow::StopResolve( void )
559 {
560 if( mResolveServiceRef )
561 {
562 Stop( mResolveServiceRef );
563 mResolveServiceRef = NULL;
564 }
565 }
566
567 //===========================================================================================================================
568 // ResolveCallBack
569 //===========================================================================================================================
570
571 void DNSSD_API
572 ExplorerBarWindow::ResolveCallBack(
573 DNSServiceRef inRef,
574 DNSServiceFlags inFlags,
575 uint32_t inInterfaceIndex,
576 DNSServiceErrorType inErrorCode,
577 const char * inFullName,
578 const char * inHostName,
579 uint16_t inPort,
580 uint16_t inTXTSize,
581 const char * inTXT,
582 void * inContext )
583 {
584 ExplorerBarWindow * obj;
585 ServiceHandlerEntry * handler;
586 OSStatus err;
587
588 DEBUG_UNUSED( inRef );
589 DEBUG_UNUSED( inFlags );
590 DEBUG_UNUSED( inErrorCode );
591 DEBUG_UNUSED( inFullName );
592
593 require_noerr( inErrorCode, exit );
594 handler = (ServiceHandlerEntry *) inContext;
595 check( handler );
596 obj = handler->obj;
597 check( obj );
598
599 try
600 {
601 ResolveInfo * resolve;
602 int idx;
603
604 dlog( kDebugLevelNotice, "resolved %s on ifi %d to %s\n", inFullName, inInterfaceIndex, inHostName );
605
606 // Stop resolving after the first good result.
607
608 obj->StopResolve();
609
610 // Post a message to the main thread so it can handle it since MFC is not thread safe.
611
612 resolve = new ResolveInfo;
613 require_action( resolve, exit, err = kNoMemoryErr );
614
615 UTF8StringToStringObject( inHostName, resolve->host );
616
617 // rdar://problem/3841564
618 //
619 // strip trailing dot from hostname because some flavors of Windows
620 // have trouble parsing it.
621
622 idx = resolve->host.ReverseFind('.');
623
624 if ((idx > 1) && ((resolve->host.GetLength() - 1) == idx))
625 {
626 resolve->host.Delete(idx, 1);
627 }
628
629 resolve->port = ntohs( inPort );
630 resolve->ifi = inInterfaceIndex;
631 resolve->handler = handler;
632
633 err = resolve->txt.SetData( inTXT, inTXTSize );
634 check_noerr( err );
635
636 obj->OnResolve(resolve);
637 }
638 catch( ... )
639 {
640 dlog( kDebugLevelError, "ResolveCallBack: exception thrown\n" );
641 }
642
643 exit:
644 return;
645 }
646
647 //===========================================================================================================================
648 // OnResolve
649 //===========================================================================================================================
650
651 LONG ExplorerBarWindow::OnResolve( ResolveInfo * resolve )
652 {
653 CString url;
654 uint8_t * path;
655 uint8_t pathSize;
656 char * pathPrefix;
657 CString username;
658 CString password;
659
660
661 check( resolve );
662
663 // Get login info if needed.
664
665 if( resolve->handler->needsLogin )
666 {
667 LoginDialog dialog;
668
669 if( !dialog.GetLogin( username, password ) )
670 {
671 goto exit;
672 }
673 }
674
675 // If the HTTP TXT record is a "path=" entry, use it as the resource path. Otherwise, use "/".
676
677 pathPrefix = "";
678 if( strcmp( resolve->handler->type, "_http._tcp" ) == 0 )
679 {
680 uint8_t * txtData;
681 uint16_t txtLen;
682
683 resolve->txt.GetData( &txtData, &txtLen );
684
685 path = (uint8_t*) TXTRecordGetValuePtr(txtLen, txtData, kTXTRecordKeyPath, &pathSize);
686
687 if (path == NULL)
688 {
689 path = (uint8_t*) "";
690 pathSize = 1;
691 }
692 }
693 else
694 {
695 path = (uint8_t *) "";
696 pathSize = 1;
697 }
698
699 // Build the URL in the following format:
700 //
701 // <urlScheme>[<username>[:<password>]@]<name/ip>[<path>]
702
703 url.AppendFormat( TEXT( "%S" ), resolve->handler->urlScheme ); // URL Scheme
704 if( username.GetLength() > 0 )
705 {
706 url.AppendFormat( TEXT( "%s" ), username ); // Username
707 if( password.GetLength() > 0 )
708 {
709 url.AppendFormat( TEXT( ":%s" ), password ); // Password
710 }
711 url.AppendFormat( TEXT( "@" ) );
712 }
713
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).
718
719 // Tell Internet Explorer to go to the URL.
720
721 check( mOwner );
722 mOwner->GoToURL( url );
723
724 exit:
725 delete resolve;
726 return( 0 );
727 }
728
729 //===========================================================================================================================
730 // Stop
731 //===========================================================================================================================
732 void ExplorerBarWindow::Stop( DNSServiceRef ref )
733 {
734 m_serviceRefs.remove( ref );
735
736 WSAAsyncSelect(DNSServiceRefSockFD( ref ), m_hWnd, WM_PRIVATE_SERVICE_EVENT, 0);
737
738 DNSServiceRefDeallocate( ref );
739 }
740
741
742 #if 0
743 #pragma mark -
744 #endif
745
746 //===========================================================================================================================
747 // FindServiceArrayIndex
748 //===========================================================================================================================
749
750 DEBUG_LOCAL int FindServiceArrayIndex( const ServiceInfoArray &inArray, const ServiceInfo &inService, int &outIndex )
751 {
752 int result;
753 int lo;
754 int hi;
755 int mid;
756
757 result = -1;
758 mid = 0;
759 lo = 0;
760 hi = (int)( inArray.GetSize() - 1 );
761 while( lo <= hi )
762 {
763 mid = ( lo + hi ) / 2;
764 result = inService.displayName.CompareNoCase( inArray[ mid ]->displayName );
765 #if 0
766 if( result == 0 )
767 {
768 result = ( (int) inService.ifi ) - ( (int) inArray[ mid ]->ifi );
769 }
770 #endif
771 if( result == 0 )
772 {
773 break;
774 }
775 else if( result < 0 )
776 {
777 hi = mid - 1;
778 }
779 else
780 {
781 lo = mid + 1;
782 }
783 }
784 if( result == 0 )
785 {
786 mid += 1; // Bump index so new item is inserted after matching item.
787 }
788 else if( result > 0 )
789 {
790 mid += 1;
791 }
792 outIndex = mid;
793 return( result );
794 }