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 
BEGIN_MESSAGE_MAP(ExplorerBarWindow,CWnd)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 
~ExplorerBarWindow(void)109 ExplorerBarWindow::~ExplorerBarWindow( void )
110 {
111 	//
112 }
113 
114 #if 0
115 #pragma mark -
116 #endif
117 
118 //===========================================================================================================================
119 //	OnCreate
120 //===========================================================================================================================
121 
OnCreate(LPCREATESTRUCT inCreateStruct)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 
OnDestroy(void)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 
OnSize(UINT inType,int inX,int inY)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 
OnDoubleClick(NMHDR * inNMHDR,LRESULT * outResult)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
OnServiceEvent(WPARAM inWParam,LPARAM inLParam)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
BrowseCallBack(DNSServiceRef inRef,DNSServiceFlags inFlags,uint32_t inInterfaceIndex,DNSServiceErrorType inErrorCode,const char * inName,const char * inType,const char * inDomain,void * inContext)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 
OnServiceAdd(ServiceInfo * service)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 
OnServiceRemove(ServiceInfo * service)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 
StartResolve(ServiceInfo * inService)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 
StopResolve(void)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
ResolveCallBack(DNSServiceRef inRef,DNSServiceFlags inFlags,uint32_t inInterfaceIndex,DNSServiceErrorType inErrorCode,const char * inFullName,const char * inHostName,uint16_t inPort,uint16_t inTXTSize,const char * inTXT,void * inContext)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 
OnResolve(ResolveInfo * resolve)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 //===========================================================================================================================
Stop(DNSServiceRef ref)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 
FindServiceArrayIndex(const ServiceInfoArray & inArray,const ServiceInfo & inService,int & outIndex)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 }
795