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