<rdar://problem/4262109> IE Plugin should browse for https
[people/sha0/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         e = new ServiceHandlerEntry;
164         check( e );
165         e->type                         = "_https._tcp";
166         e->urlScheme            = "https://";
167         e->ref                          = NULL;
168         e->obj                          = this;
169         e->needsLogin           = false;
170         mServiceHandlers.Add( e );
171
172         err = DNSServiceBrowse( &e->ref, 0, 0, e->type, NULL, BrowseCallBack, e );
173         require_noerr( err, exit );
174
175         err = WSAAsyncSelect((SOCKET) DNSServiceRefSockFD(e->ref), m_hWnd, WM_PRIVATE_SERVICE_EVENT, FD_READ|FD_CLOSE);
176         require_noerr( err, exit );
177
178         m_serviceRefs.push_back(e->ref);
179         
180         m_imageList.Create( 16, 16, ILC_MASK | ILC_COLOR16, 2, 0);
181
182         bitmap.Attach( ::LoadBitmap( GetNonLocalizedResources(), MAKEINTRESOURCE( IDB_LOGO ) ) );
183         m_imageList.Add( &bitmap, (CBitmap*) NULL );
184         bitmap.Detach();
185
186         mTree.SetImageList(&m_imageList, TVSIL_NORMAL);
187         
188 exit:
189
190         if ( module )
191         {
192                 FreeLibrary( module );
193                 module = NULL;
194         }
195
196         // Cannot talk to the mDNSResponder service. Show the error message and exit (with kNoErr so they can see it).
197         if ( err )
198         {
199                 if ( err == kDNSServiceErr_Firewall )
200                 {
201                         s.LoadString( IDS_FIREWALL );
202                 }
203                 else
204                 {
205                         s.LoadString( IDS_MDNSRESPONDER_NOT_AVAILABLE );
206                 }
207                 
208                 mTree.DeleteAllItems();
209                 mTree.InsertItem( s, 0, 0, TVI_ROOT, TVI_LAST );
210                 
211                 err = kNoErr;
212         }
213
214         return( err );
215 }
216
217 //===========================================================================================================================
218 //      OnDestroy
219 //===========================================================================================================================
220
221 void    ExplorerBarWindow::OnDestroy( void ) 
222 {
223         // Stop any resolves that may still be pending (shouldn't be any).
224         
225         StopResolve();
226         
227         // Clean up the extant browses
228         while (m_serviceRefs.size() > 0)
229         {
230                 //
231                 // take the head of the list
232                 //
233                 DNSServiceRef ref = m_serviceRefs.front();
234
235                 //
236                 // Stop will remove it from the list
237                 //
238                 Stop( ref );
239         }
240
241         // Clean up the service handlers.
242         
243         int             i;
244         int             n;
245         
246         n = (int) mServiceHandlers.GetSize();
247         for( i = 0; i < n; ++i )
248         {
249                 delete mServiceHandlers[ i ];
250         }
251         
252         CWnd::OnDestroy();
253 }
254
255 //===========================================================================================================================
256 //      OnSize
257 //===========================================================================================================================
258
259 void    ExplorerBarWindow::OnSize( UINT inType, int inX, int inY ) 
260 {
261         CWnd::OnSize( inType, inX, inY );
262         mTree.MoveWindow( 0, 0, inX, inY );
263 }
264
265 //===========================================================================================================================
266 //      OnDoubleClick
267 //===========================================================================================================================
268
269 void    ExplorerBarWindow::OnDoubleClick( NMHDR *inNMHDR, LRESULT *outResult )
270 {
271         HTREEITEM                       item;
272         ServiceInfo *           service;
273         OSStatus                        err;
274         
275         DEBUG_UNUSED( inNMHDR );
276         
277         item = mTree.GetSelectedItem();
278         require( item, exit );
279         
280         // Tell Internet Explorer to go to the URL if it's about item
281         
282         if ( item == m_about )
283         {
284                 CString url;
285
286                 check( mOwner );
287
288                 url.LoadString( IDS_ABOUT_URL );
289                 mOwner->GoToURL( url );
290         }
291         else
292         {
293                 service = reinterpret_cast < ServiceInfo * > ( mTree.GetItemData( item ) );
294                 require_quiet( service, exit );
295                 
296                 err = StartResolve( service );
297                 require_noerr( err, exit );
298         }
299
300 exit:
301         *outResult = 0;
302 }
303
304
305 //===========================================================================================================================
306 //      OnServiceEvent
307 //===========================================================================================================================
308
309 LRESULT
310 ExplorerBarWindow::OnServiceEvent(WPARAM inWParam, LPARAM inLParam)
311 {
312         if (WSAGETSELECTERROR(inLParam) && !(HIWORD(inLParam)))
313     {
314                 dlog( kDebugLevelError, "OnServiceEvent: window error\n" );
315     }
316     else
317     {
318                 SOCKET sock = (SOCKET) inWParam;
319
320                 // iterate thru list
321                 ServiceRefList::iterator it;
322
323                 for (it = m_serviceRefs.begin(); it != m_serviceRefs.end(); it++)
324                 {
325                         DNSServiceRef ref = *it;
326
327                         check(ref != NULL);
328
329                         if ((SOCKET) DNSServiceRefSockFD(ref) == sock)
330                         {
331                                 DNSServiceErrorType err;
332
333                                 err = DNSServiceProcessResult(ref);
334
335                                 if (err != 0)
336                                 {
337                                         CString s;
338
339                                         s.LoadString( IDS_MDNSRESPONDER_NOT_AVAILABLE );
340                                         mTree.DeleteAllItems();
341                                         mTree.InsertItem( s, 0, 0, TVI_ROOT, TVI_LAST );
342
343                                         Stop(ref);
344                                 }
345
346                                 break;
347                         }
348                 }
349         }
350
351         return ( 0 );
352 }
353
354 #if 0
355 #pragma mark -
356 #endif
357
358 //===========================================================================================================================
359 //      BrowseCallBack
360 //===========================================================================================================================
361
362 void DNSSD_API
363         ExplorerBarWindow::BrowseCallBack(
364                 DNSServiceRef                   inRef,
365                 DNSServiceFlags                 inFlags,
366                 uint32_t                                inInterfaceIndex,
367                 DNSServiceErrorType     inErrorCode,
368                 const char *                    inName, 
369                 const char *                    inType, 
370                 const char *                    inDomain,       
371                 void *                                  inContext )
372 {
373         ServiceHandlerEntry *           obj;
374         ServiceInfo *                           service;
375         OSStatus                                        err;
376         
377         DEBUG_UNUSED( inRef );
378         
379         obj             =       NULL;
380         service = NULL;
381         
382         require_noerr( inErrorCode, exit );
383         obj = reinterpret_cast < ServiceHandlerEntry * > ( inContext );
384         check( obj );
385         check( obj->obj );
386         
387         //
388         // set the UI to hold off on updates
389         //
390         obj->obj->mTree.SetRedraw(FALSE);
391
392         try
393         {
394                 service = new ServiceInfo;
395                 require_action( service, exit, err = kNoMemoryErr );
396                 
397                 err = UTF8StringToStringObject( inName, service->displayName );
398                 check_noerr( err );
399
400                 service->name = _strdup( inName );
401                 require_action( service->name, exit, err = kNoMemoryErr );
402                 
403                 service->type = _strdup( inType );
404                 require_action( service->type, exit, err = kNoMemoryErr );
405                 
406                 service->domain = _strdup( inDomain );
407                 require_action( service->domain, exit, err = kNoMemoryErr );
408                 
409                 service->ifi            = inInterfaceIndex;
410                 service->handler        = obj;
411
412                 service->refs           = 1;
413                 
414                 if (inFlags & kDNSServiceFlagsAdd) obj->obj->OnServiceAdd   (service);
415                 else                               obj->obj->OnServiceRemove(service);
416         
417                 service = NULL;
418         }
419         catch( ... )
420         {
421                 dlog( kDebugLevelError, "BrowseCallBack: exception thrown\n" );
422         }
423         
424 exit:
425         //
426         // If no more coming, then update UI
427         //
428         if (obj && obj->obj && ((inFlags & kDNSServiceFlagsMoreComing) == 0))
429         {
430                 obj->obj->mTree.SetRedraw(TRUE);
431                 obj->obj->mTree.Invalidate();
432         }
433
434         if( service )
435         {
436                 delete service;
437         }
438 }
439
440 //===========================================================================================================================
441 //      OnServiceAdd
442 //===========================================================================================================================
443
444 LONG    ExplorerBarWindow::OnServiceAdd( ServiceInfo * service )
445 {
446         ServiceHandlerEntry *           handler;
447         int                                                     cmp;
448         int                                                     index;
449         
450         
451         check( service );
452         handler = service->handler; 
453         check( handler );
454         
455         cmp = FindServiceArrayIndex( handler->array, *service, index );
456         if( cmp == 0 )
457         {
458                 // Found a match so update the item. The index is index + 1 so subtract 1.
459                 
460                 index -= 1;
461                 check( index < handler->array.GetSize() );
462
463                 handler->array[ index ]->refs++;
464
465                 delete service;
466         }
467         else
468         {
469                 HTREEITEM               afterItem;
470                 
471                 // Insert the new item in sorted order.
472                 
473                 afterItem = ( index > 0 ) ? handler->array[ index - 1 ]->item : m_about;
474                 handler->array.InsertAt( index, service );
475                 service->item = mTree.InsertItem( service->displayName, 0, 0, NULL, afterItem );
476                 mTree.SetItemData( service->item, (DWORD_PTR) service );
477         }
478         return( 0 );
479 }
480
481 //===========================================================================================================================
482 //      OnServiceRemove
483 //===========================================================================================================================
484
485 LONG    ExplorerBarWindow::OnServiceRemove( ServiceInfo * service )
486 {
487         ServiceHandlerEntry *           handler;
488         int                                                     cmp;
489         int                                                     index;
490         
491         
492         check( service );
493         handler = service->handler; 
494         check( handler );
495         
496         // Search to see if we know about this service instance. If so, remove it from the list.
497         
498         cmp = FindServiceArrayIndex( handler->array, *service, index );
499         check( cmp == 0 );
500
501         if( cmp == 0 )
502         {
503                 // Possibly found a match remove the item. The index
504                 // is index + 1 so subtract 1.
505                 index -= 1;
506                 check( index < handler->array.GetSize() );
507
508                 if ( --handler->array[ index ]->refs == 0 )
509                 {
510                         mTree.DeleteItem( handler->array[ index ]->item );
511                         delete handler->array[ index ];
512                         handler->array.RemoveAt( index );
513                 }
514         }
515
516         delete service;
517         return( 0 );
518 }
519
520 #if 0
521 #pragma mark -
522 #endif
523
524 //===========================================================================================================================
525 //      StartResolve
526 //===========================================================================================================================
527
528 OSStatus        ExplorerBarWindow::StartResolve( ServiceInfo *inService )
529 {
530         OSStatus                err;
531         
532         check( inService );
533         
534         // Stop any current resolve that may be in progress.
535         
536         StopResolve();
537         
538         // Resolve the service.
539         err = DNSServiceResolve( &mResolveServiceRef, 0, 0, 
540                 inService->name, inService->type, inService->domain, (DNSServiceResolveReply) ResolveCallBack, inService->handler );
541         require_noerr( err, exit );
542
543         err = WSAAsyncSelect((SOCKET) DNSServiceRefSockFD(mResolveServiceRef), m_hWnd, WM_PRIVATE_SERVICE_EVENT, FD_READ|FD_CLOSE);
544         require_noerr( err, exit );
545         
546         m_serviceRefs.push_back(mResolveServiceRef);
547
548 exit:
549         return( err );
550 }
551
552 //===========================================================================================================================
553 //      StopResolve
554 //===========================================================================================================================
555
556 void    ExplorerBarWindow::StopResolve( void )
557 {
558         if( mResolveServiceRef )
559         {
560                 Stop( mResolveServiceRef );
561                 mResolveServiceRef = NULL;
562         }
563 }
564
565 //===========================================================================================================================
566 //      ResolveCallBack
567 //===========================================================================================================================
568
569 void DNSSD_API
570         ExplorerBarWindow::ResolveCallBack(
571                 DNSServiceRef                   inRef,
572                 DNSServiceFlags                 inFlags,
573                 uint32_t                                inInterfaceIndex,
574                 DNSServiceErrorType             inErrorCode,
575                 const char *                    inFullName,     
576                 const char *                    inHostName, 
577                 uint16_t                                inPort,
578                 uint16_t                                inTXTSize,
579                 const char *                    inTXT,
580                 void *                                  inContext )
581 {
582         ExplorerBarWindow *                     obj;
583         ServiceHandlerEntry *           handler;
584         OSStatus                                        err;
585         
586         DEBUG_UNUSED( inRef );
587         DEBUG_UNUSED( inFlags );
588         DEBUG_UNUSED( inErrorCode );
589         DEBUG_UNUSED( inFullName );
590         
591         require_noerr( inErrorCode, exit );
592         handler = (ServiceHandlerEntry *) inContext;
593         check( handler );
594         obj = handler->obj;
595         check( obj );
596         
597         try
598         {
599                 ResolveInfo *           resolve;
600                 int                                     idx;
601                 
602                 dlog( kDebugLevelNotice, "resolved %s on ifi %d to %s\n", inFullName, inInterfaceIndex, inHostName );
603                 
604                 // Stop resolving after the first good result.
605                 
606                 obj->StopResolve();
607                 
608                 // Post a message to the main thread so it can handle it since MFC is not thread safe.
609                 
610                 resolve = new ResolveInfo;
611                 require_action( resolve, exit, err = kNoMemoryErr );
612                 
613                 UTF8StringToStringObject( inHostName, resolve->host );
614
615                 // rdar://problem/3841564
616                 // 
617                 // strip trailing dot from hostname because some flavors of Windows
618                 // have trouble parsing it.
619
620                 idx = resolve->host.ReverseFind('.');
621
622                 if ((idx > 1) && ((resolve->host.GetLength() - 1) == idx))
623                 {
624                         resolve->host.Delete(idx, 1);
625                 }
626
627                 resolve->port           = ntohs( inPort );
628                 resolve->ifi            = inInterfaceIndex;
629                 resolve->handler        = handler;
630                 
631                 err = resolve->txt.SetData( inTXT, inTXTSize );
632                 check_noerr( err );
633                 
634                 obj->OnResolve(resolve);
635         }
636         catch( ... )
637         {
638                 dlog( kDebugLevelError, "ResolveCallBack: exception thrown\n" );
639         }
640
641 exit:
642         return;
643 }
644
645 //===========================================================================================================================
646 //      OnResolve
647 //===========================================================================================================================
648
649 LONG    ExplorerBarWindow::OnResolve( ResolveInfo * resolve )
650 {
651         CString                         url;
652         uint8_t *                       path;
653         uint8_t                         pathSize;
654         char *                          pathPrefix;
655         CString                         username;
656         CString                         password;
657         
658         
659         check( resolve );
660                 
661         // Get login info if needed.
662         
663         if( resolve->handler->needsLogin )
664         {
665                 LoginDialog             dialog;
666                 
667                 if( !dialog.GetLogin( username, password ) )
668                 {
669                         goto exit;
670                 }
671         }
672         
673         // If the HTTP TXT record is a "path=" entry, use it as the resource path. Otherwise, use "/".
674         
675         pathPrefix = "";
676         if( strcmp( resolve->handler->type, "_http._tcp" ) == 0 )
677         {
678                 uint8_t *       txtData;
679                 uint16_t        txtLen; 
680
681                 resolve->txt.GetData( &txtData, &txtLen );
682
683                 path     = (uint8_t*) TXTRecordGetValuePtr(txtLen, txtData, kTXTRecordKeyPath, &pathSize);
684
685                 if (path == NULL)
686                 {
687                         path = (uint8_t*) "";
688                         pathSize = 1;
689                 }
690         }
691         else
692         {
693                 path            = (uint8_t *) "";
694                 pathSize        = 1;
695         }
696
697         // Build the URL in the following format:
698         //
699         // <urlScheme>[<username>[:<password>]@]<name/ip>[<path>]
700
701         url.AppendFormat( TEXT( "%S" ), resolve->handler->urlScheme );                                  // URL Scheme
702         if( username.GetLength() > 0 )
703         {
704                 url.AppendFormat( TEXT( "%s" ), username );                                                                     // Username
705                 if( password.GetLength() > 0 )
706                 {
707                         url.AppendFormat( TEXT( ":%s" ), password );                                                    // Password
708                 }
709                 url.AppendFormat( TEXT( "@" ) );
710         }
711         
712         url += resolve->host;                                                                                                                   // Host
713         url.AppendFormat( TEXT( ":%d" ), resolve->port );                                                               // :Port
714         url.AppendFormat( TEXT( "%S" ), pathPrefix );                                                                   // Path Prefix ("/" or empty).
715         url.AppendFormat( TEXT( "%.*S" ), (int) pathSize, (char *) path );                              // Path (possibly empty).
716         
717         // Tell Internet Explorer to go to the URL.
718         
719         check( mOwner );
720         mOwner->GoToURL( url );
721
722 exit:
723         delete resolve;
724         return( 0 );
725 }
726
727 //===========================================================================================================================
728 //      Stop
729 //===========================================================================================================================
730 void ExplorerBarWindow::Stop( DNSServiceRef ref )
731 {
732         m_serviceRefs.remove( ref );
733
734         WSAAsyncSelect(DNSServiceRefSockFD( ref ), m_hWnd, WM_PRIVATE_SERVICE_EVENT, 0);
735
736         DNSServiceRefDeallocate( ref );
737 }
738
739
740 #if 0
741 #pragma mark -
742 #endif
743
744 //===========================================================================================================================
745 //      FindServiceArrayIndex
746 //===========================================================================================================================
747
748 DEBUG_LOCAL int FindServiceArrayIndex( const ServiceInfoArray &inArray, const ServiceInfo &inService, int &outIndex )
749 {
750         int             result;
751         int             lo;
752         int             hi;
753         int             mid;
754         
755         result  = -1;
756         mid             = 0;
757         lo              = 0;
758         hi              = (int)( inArray.GetSize() - 1 );
759         while( lo <= hi )
760         {
761                 mid = ( lo + hi ) / 2;
762                 result = inService.displayName.CompareNoCase( inArray[ mid ]->displayName );
763 #if 0
764                 if( result == 0 )
765                 {
766                         result = ( (int) inService.ifi ) - ( (int) inArray[ mid ]->ifi );
767                 }
768 #endif
769                 if( result == 0 )
770                 {
771                         break;
772                 }
773                 else if( result < 0 )
774                 {
775                         hi = mid - 1;
776                 }
777                 else
778                 {
779                         lo = mid + 1;
780                 }
781         }
782         if( result == 0 )
783         {
784                 mid += 1;       // Bump index so new item is inserted after matching item.
785         }
786         else if( result > 0 )
787         {
788                 mid += 1;
789         }
790         outIndex = mid;
791         return( result );
792 }