]> git.saurik.com Git - apple/mdnsresponder.git/blob - Clients/ExplorerPlugin/ExplorerBarWindow.cpp
mDNSResponder-87.tar.gz
[apple/mdnsresponder.git] / Clients / ExplorerPlugin / ExplorerBarWindow.cpp
1 /*
2 * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22
23 Change History (most recent first):
24
25 $Log: ExplorerBarWindow.cpp,v $
26 Revision 1.12 2004/10/26 00:56:03 cheshire
27 Use "#if 0" instead of commenting out code
28
29 Revision 1.11 2004/10/18 23:49:17 shersche
30 <rdar://problem/3841564> Remove trailing dot from hostname, because some flavors of Windows have difficulty parsing hostnames with a trailing dot.
31 Bug #: 3841564
32
33 Revision 1.10 2004/09/02 02:18:58 cheshire
34 Minor textual cleanup to improve readability
35
36 Revision 1.9 2004/09/02 02:11:56 cheshire
37 <rdar://problem/3783611> Fix incorrect testing of MoreComing flag
38
39 Revision 1.8 2004/07/26 05:47:31 shersche
40 use TXTRecord APIs, fix bug in locating service to be removed
41
42 Revision 1.7 2004/07/22 16:08:20 shersche
43 clean up debug print statements, re-enable code inadvertently commented out
44
45 Revision 1.6 2004/07/22 05:27:20 shersche
46 <rdar://problem/3735827> Check to make sure error isn't WSAEWOULDBLOCK when canceling browse
47 Bug #: 3735827
48
49 Revision 1.5 2004/07/20 06:49:18 shersche
50 clean up socket handling code
51
52 Revision 1.4 2004/07/13 21:24:21 rpantos
53 Fix for <rdar://problem/3701120>.
54
55 Revision 1.3 2004/06/27 14:59:59 shersche
56 reference count service info to handle multi-homed hosts
57
58 Revision 1.2 2004/06/23 16:09:34 shersche
59 Add the resolve DNSServiceRef to list of extant refs. This fixes the "doesn't resolve when double clicking" problem
60
61 Submitted by: Scott Herscher
62
63 Revision 1.1 2004/06/18 04:34:59 rpantos
64 Move to Clients from mDNSWindows
65
66 Revision 1.5 2004/04/15 01:00:05 bradley
67 Removed support for automatically querying for A/AAAA records when resolving names. Platforms
68 without .local name resolving support will need to manually query for A/AAAA records as needed.
69
70 Revision 1.4 2004/04/09 21:03:15 bradley
71 Changed port numbers to use network byte order for consistency with other platforms.
72
73 Revision 1.3 2004/04/08 09:43:43 bradley
74 Changed callback calling conventions to __stdcall so they can be used with C# delegates.
75
76 Revision 1.2 2004/02/21 04:36:19 bradley
77 Enable dot local name lookups now that the NSP is being installed.
78
79 Revision 1.1 2004/01/30 03:01:56 bradley
80 Explorer Plugin to browse for DNS-SD advertised Web and FTP servers from within Internet Explorer.
81
82 */
83
84 #include "StdAfx.h"
85
86 #include "CommonServices.h"
87 #include "DebugServices.h"
88 #include "WinServices.h"
89 #include "dns_sd.h"
90
91 #include "ExplorerBar.h"
92 #include "LoginDialog.h"
93 #include "Resource.h"
94
95 #include "ExplorerBarWindow.h"
96
97 // MFC Debugging
98
99 #ifdef _DEBUG
100 #define new DEBUG_NEW
101 #undef THIS_FILE
102 static char THIS_FILE[] = __FILE__;
103 #endif
104
105 #if 0
106 #pragma mark == Constants ==
107 #endif
108
109 //===========================================================================================================================
110 // Constants
111 //===========================================================================================================================
112
113 // Control IDs
114
115 #define IDC_EXPLORER_TREE 1234
116
117 // Private Messages
118
119 #define WM_PRIVATE_SERVICE_EVENT ( WM_USER + 0x100 )
120
121 // TXT records
122
123 #define kTXTRecordKeyPath "path"
124
125 #if 0
126 #pragma mark == Prototypes ==
127 #endif
128
129 //===========================================================================================================================
130 // Prototypes
131 //===========================================================================================================================
132
133 DEBUG_LOCAL int FindServiceArrayIndex( const ServiceInfoArray &inArray, const ServiceInfo &inService, int &outIndex );
134
135 #if 0
136 #pragma mark == Message Map ==
137 #endif
138
139 //===========================================================================================================================
140 // Message Map
141 //===========================================================================================================================
142
143 BEGIN_MESSAGE_MAP( ExplorerBarWindow, CWnd )
144 ON_WM_CREATE()
145 ON_WM_DESTROY()
146 ON_WM_SIZE()
147 ON_NOTIFY( NM_DBLCLK, IDC_EXPLORER_TREE, OnDoubleClick )
148 ON_MESSAGE( WM_PRIVATE_SERVICE_EVENT, OnServiceEvent )
149 END_MESSAGE_MAP()
150
151 #if 0
152 #pragma mark -
153 #endif
154
155 //===========================================================================================================================
156 // ExplorerBarWindow
157 //===========================================================================================================================
158
159 ExplorerBarWindow::ExplorerBarWindow( void )
160 {
161 mOwner = NULL;
162 mResolveServiceRef = NULL;
163 }
164
165 //===========================================================================================================================
166 // ~ExplorerBarWindow
167 //===========================================================================================================================
168
169 ExplorerBarWindow::~ExplorerBarWindow( void )
170 {
171 //
172 }
173
174 #if 0
175 #pragma mark -
176 #endif
177
178 //===========================================================================================================================
179 // OnCreate
180 //===========================================================================================================================
181
182 int ExplorerBarWindow::OnCreate( LPCREATESTRUCT inCreateStruct )
183 {
184 AFX_MANAGE_STATE( AfxGetStaticModuleState() );
185
186 OSStatus err;
187 CRect rect;
188 CBitmap bitmap;
189 CString s;
190
191 err = CWnd::OnCreate( inCreateStruct );
192 require_noerr( err, exit );
193
194 GetClientRect( rect );
195 mTree.Create( WS_TABSTOP | WS_VISIBLE | WS_CHILD | TVS_HASBUTTONS | TVS_LINESATROOT | TVS_HASLINES | TVS_NOHSCROLL , rect, this,
196 IDC_EXPLORER_TREE );
197
198
199 ServiceHandlerEntry * e;
200
201 // Web Site Handler
202
203 e = new ServiceHandlerEntry;
204 check( e );
205 e->type = "_http._tcp";
206 e->urlScheme = "http://";
207 e->ref = NULL;
208 e->treeItem = NULL;
209 e->treeFirst = true;
210 e->obj = this;
211 e->needsLogin = false;
212 mServiceHandlers.Add( e );
213
214 s.LoadString( IDS_WEB_SITES );
215 e->treeItem = mTree.InsertItem( s, 0, 0 );
216 mTree.Expand( e->treeItem, TVE_EXPAND );
217
218 err = DNSServiceBrowse( &e->ref, 0, 0, e->type, NULL, BrowseCallBack, e );
219 require_noerr( err, exit );
220
221 err = WSAAsyncSelect((SOCKET) DNSServiceRefSockFD(e->ref), m_hWnd, WM_PRIVATE_SERVICE_EVENT, FD_READ|FD_CLOSE);
222 require_noerr( err, exit );
223
224 m_serviceRefs.push_back(e->ref);
225
226 // FTP Site Handler
227
228 e = new ServiceHandlerEntry;
229 check( e );
230 e->type = "_ftp._tcp";
231 e->urlScheme = "ftp://";
232 e->ref = NULL;
233 e->treeItem = NULL;
234 e->treeFirst = true;
235 e->obj = this;
236 e->needsLogin = true;
237 mServiceHandlers.Add( e );
238
239 s.LoadString( IDS_FTP_SITES );
240 e->treeItem = mTree.InsertItem( s, 0, 0 );
241 mTree.Expand( e->treeItem, TVE_EXPAND );
242
243 err = DNSServiceBrowse( &e->ref, 0, 0, e->type, NULL, BrowseCallBack, e );
244 require_noerr( err, exit );
245
246 err = WSAAsyncSelect((SOCKET) DNSServiceRefSockFD(e->ref), m_hWnd, WM_PRIVATE_SERVICE_EVENT, FD_READ|FD_CLOSE);
247 require_noerr( err, exit );
248
249 m_serviceRefs.push_back(e->ref);
250
251 m_imageList.Create(16, 16, ILC_COLORDDB, 1, 0);
252 bitmap.LoadBitmap(IDB_LOGO);
253 m_imageList.Add(&bitmap, (CBitmap*) NULL);
254
255 mTree.SetImageList(&m_imageList, TVSIL_NORMAL);
256
257 exit:
258
259 // Cannot talk to the mDNSResponder service. Show the error message and exit (with kNoErr so they can see it).
260 if ( err != kNoErr )
261 {
262 s.LoadString( IDS_MDNSRESPONDER_NOT_AVAILABLE );
263 mTree.DeleteAllItems();
264 mTree.InsertItem( s, 0, 0, TVI_ROOT, TVI_LAST );
265 err = kNoErr;
266 }
267
268 return( err );
269 }
270
271 //===========================================================================================================================
272 // OnDestroy
273 //===========================================================================================================================
274
275 void ExplorerBarWindow::OnDestroy( void )
276 {
277 // Stop any resolves that may still be pending (shouldn't be any).
278
279 StopResolve();
280
281 // Clean up the extant browses
282 while (m_serviceRefs.size() > 0)
283 {
284 //
285 // take the head of the list
286 //
287 DNSServiceRef ref = m_serviceRefs.front();
288
289 //
290 // Stop will remove it from the list
291 //
292 Stop( ref );
293 }
294
295 // Clean up the service handlers.
296
297 int i;
298 int n;
299
300 n = (int) mServiceHandlers.GetSize();
301 for( i = 0; i < n; ++i )
302 {
303 delete mServiceHandlers[ i ];
304 }
305
306 CWnd::OnDestroy();
307 }
308
309 //===========================================================================================================================
310 // OnSize
311 //===========================================================================================================================
312
313 void ExplorerBarWindow::OnSize( UINT inType, int inX, int inY )
314 {
315 CWnd::OnSize( inType, inX, inY );
316 mTree.MoveWindow( 0, 0, inX, inY );
317 }
318
319 //===========================================================================================================================
320 // OnDoubleClick
321 //===========================================================================================================================
322
323 void ExplorerBarWindow::OnDoubleClick( NMHDR *inNMHDR, LRESULT *outResult )
324 {
325 HTREEITEM item;
326 ServiceInfo * service;
327 OSStatus err;
328
329 DEBUG_UNUSED( inNMHDR );
330
331 item = mTree.GetSelectedItem();
332 require( item, exit );
333
334 service = reinterpret_cast < ServiceInfo * > ( mTree.GetItemData( item ) );
335 require_quiet( service, exit );
336
337 err = StartResolve( service );
338 require_noerr( err, exit );
339
340 exit:
341 *outResult = 0;
342 }
343
344
345 //===========================================================================================================================
346 // OnServiceEvent
347 //===========================================================================================================================
348
349 LONG
350 ExplorerBarWindow::OnServiceEvent(WPARAM inWParam, LPARAM inLParam)
351 {
352 if (WSAGETSELECTERROR(inLParam) && !(HIWORD(inLParam)))
353 {
354 dlog( kDebugLevelError, "OnServiceEvent: window error\n" );
355 }
356 else
357 {
358 SOCKET sock = (SOCKET) inWParam;
359
360 // iterate thru list
361 ServiceRefList::iterator it;
362
363 for (it = m_serviceRefs.begin(); it != m_serviceRefs.end(); it++)
364 {
365 DNSServiceRef ref = *it;
366
367 check(ref != NULL);
368
369 if ((SOCKET) DNSServiceRefSockFD(ref) == sock)
370 {
371 DNSServiceErrorType err;
372
373 err = DNSServiceProcessResult(ref);
374
375 if (err != 0)
376 {
377 CString s;
378
379 s.LoadString( IDS_MDNSRESPONDER_NOT_AVAILABLE );
380 mTree.DeleteAllItems();
381 mTree.InsertItem( s, 0, 0, TVI_ROOT, TVI_LAST );
382
383 Stop(ref);
384 }
385
386 break;
387 }
388 }
389 }
390
391 return ( 0 );
392 }
393
394 #if 0
395 #pragma mark -
396 #endif
397
398 //===========================================================================================================================
399 // BrowseCallBack
400 //===========================================================================================================================
401
402 void DNSSD_API
403 ExplorerBarWindow::BrowseCallBack(
404 DNSServiceRef inRef,
405 DNSServiceFlags inFlags,
406 uint32_t inInterfaceIndex,
407 DNSServiceErrorType inErrorCode,
408 const char * inName,
409 const char * inType,
410 const char * inDomain,
411 void * inContext )
412 {
413 ServiceHandlerEntry * obj;
414 ServiceInfo * service;
415 OSStatus err;
416
417 DEBUG_UNUSED( inRef );
418
419 obj = NULL;
420 service = NULL;
421
422 require_noerr( inErrorCode, exit );
423 obj = reinterpret_cast < ServiceHandlerEntry * > ( inContext );
424 check( obj );
425 check( obj->obj );
426
427 //
428 // set the UI to hold off on updates
429 //
430 obj->obj->mTree.SetRedraw(FALSE);
431
432 try
433 {
434 service = new ServiceInfo;
435 require_action( service, exit, err = kNoMemoryErr );
436
437 err = UTF8StringToStringObject( inName, service->displayName );
438 check_noerr( err );
439
440 service->name = strdup( inName );
441 require_action( service->name, exit, err = kNoMemoryErr );
442
443 service->type = strdup( inType );
444 require_action( service->type, exit, err = kNoMemoryErr );
445
446 service->domain = strdup( inDomain );
447 require_action( service->domain, exit, err = kNoMemoryErr );
448
449 service->ifi = inInterfaceIndex;
450 service->handler = obj;
451
452 service->refs = 1;
453
454 if (inFlags & kDNSServiceFlagsAdd) obj->obj->OnServiceAdd (service);
455 else obj->obj->OnServiceRemove(service);
456
457 service = NULL;
458 }
459 catch( ... )
460 {
461 dlog( kDebugLevelError, "BrowseCallBack: exception thrown\n" );
462 }
463
464 exit:
465 //
466 // If no more coming, then update UI
467 //
468 if (obj && obj->obj && ((inFlags & kDNSServiceFlagsMoreComing) == 0))
469 {
470 obj->obj->mTree.SetRedraw(TRUE);
471 obj->obj->mTree.Invalidate();
472 }
473
474 if( service )
475 {
476 delete service;
477 }
478 }
479
480 //===========================================================================================================================
481 // OnServiceAdd
482 //===========================================================================================================================
483
484 LONG ExplorerBarWindow::OnServiceAdd( ServiceInfo * service )
485 {
486 ServiceHandlerEntry * handler;
487 int cmp;
488 int index;
489
490
491 check( service );
492 handler = service->handler;
493 check( handler );
494
495 cmp = FindServiceArrayIndex( handler->array, *service, index );
496 if( cmp == 0 )
497 {
498 // Found a match so update the item. The index is index + 1 so subtract 1.
499
500 index -= 1;
501 check( index < handler->array.GetSize() );
502
503 handler->array[ index ]->refs++;
504
505 delete service;
506 }
507 else
508 {
509 HTREEITEM afterItem;
510
511 // Insert the new item in sorted order.
512
513 afterItem = ( index > 0 ) ? handler->array[ index - 1 ]->item : TVI_FIRST;
514 handler->array.InsertAt( index, service );
515 service->item = mTree.InsertItem( service->displayName, handler->treeItem, afterItem );
516 mTree.SetItemData( service->item, (DWORD_PTR) service );
517
518 // Make sure the item is visible if this is the first time a service was added.
519
520 if( handler->treeFirst )
521 {
522 handler->treeFirst = false;
523 mTree.EnsureVisible( service->item );
524 }
525 }
526 return( 0 );
527 }
528
529 //===========================================================================================================================
530 // OnServiceRemove
531 //===========================================================================================================================
532
533 LONG ExplorerBarWindow::OnServiceRemove( ServiceInfo * service )
534 {
535 ServiceHandlerEntry * handler;
536 int cmp;
537 int index;
538
539
540 check( service );
541 handler = service->handler;
542 check( handler );
543
544 // Search to see if we know about this service instance. If so, remove it from the list.
545
546 cmp = FindServiceArrayIndex( handler->array, *service, index );
547 check( cmp == 0 );
548
549 if( cmp == 0 )
550 {
551 // Possibly found a match remove the item. The index
552 // is index + 1 so subtract 1.
553 index -= 1;
554 check( index < handler->array.GetSize() );
555
556 if ( --handler->array[ index ]->refs == 0 )
557 {
558 mTree.DeleteItem( handler->array[ index ]->item );
559 delete handler->array[ index ];
560 handler->array.RemoveAt( index );
561 }
562 }
563
564 delete service;
565 return( 0 );
566 }
567
568 #if 0
569 #pragma mark -
570 #endif
571
572 //===========================================================================================================================
573 // StartResolve
574 //===========================================================================================================================
575
576 OSStatus ExplorerBarWindow::StartResolve( ServiceInfo *inService )
577 {
578 OSStatus err;
579
580 check( inService );
581
582 // Stop any current resolve that may be in progress.
583
584 StopResolve();
585
586 // Resolve the service.
587 err = DNSServiceResolve( &mResolveServiceRef, 0, 0,
588 inService->name, inService->type, inService->domain, (DNSServiceResolveReply) ResolveCallBack, inService->handler );
589 require_noerr( err, exit );
590
591 err = WSAAsyncSelect((SOCKET) DNSServiceRefSockFD(mResolveServiceRef), m_hWnd, WM_PRIVATE_SERVICE_EVENT, FD_READ|FD_CLOSE);
592 require_noerr( err, exit );
593
594 m_serviceRefs.push_back(mResolveServiceRef);
595
596 exit:
597 return( err );
598 }
599
600 //===========================================================================================================================
601 // StopResolve
602 //===========================================================================================================================
603
604 void ExplorerBarWindow::StopResolve( void )
605 {
606 if( mResolveServiceRef )
607 {
608 Stop( mResolveServiceRef );
609 mResolveServiceRef = NULL;
610 }
611 }
612
613 //===========================================================================================================================
614 // ResolveCallBack
615 //===========================================================================================================================
616
617 void DNSSD_API
618 ExplorerBarWindow::ResolveCallBack(
619 DNSServiceRef inRef,
620 DNSServiceFlags inFlags,
621 uint32_t inInterfaceIndex,
622 DNSServiceErrorType inErrorCode,
623 const char * inFullName,
624 const char * inHostName,
625 uint16_t inPort,
626 uint16_t inTXTSize,
627 const char * inTXT,
628 void * inContext )
629 {
630 ExplorerBarWindow * obj;
631 ServiceHandlerEntry * handler;
632 OSStatus err;
633
634 DEBUG_UNUSED( inRef );
635 DEBUG_UNUSED( inFlags );
636 DEBUG_UNUSED( inErrorCode );
637 DEBUG_UNUSED( inFullName );
638
639 require_noerr( inErrorCode, exit );
640 handler = (ServiceHandlerEntry *) inContext;
641 check( handler );
642 obj = handler->obj;
643 check( obj );
644
645 try
646 {
647 ResolveInfo * resolve;
648 int idx;
649
650 dlog( kDebugLevelNotice, "resolved %s on ifi %d to %s\n", inFullName, inInterfaceIndex, inHostName );
651
652 // Stop resolving after the first good result.
653
654 obj->StopResolve();
655
656 // Post a message to the main thread so it can handle it since MFC is not thread safe.
657
658 resolve = new ResolveInfo;
659 require_action( resolve, exit, err = kNoMemoryErr );
660
661 UTF8StringToStringObject( inHostName, resolve->host );
662
663 // rdar://problem/3841564
664 //
665 // strip trailing dot from hostname because some flavors of Windows
666 // have trouble parsing it.
667
668 idx = resolve->host.ReverseFind('.');
669
670 if ((idx > 1) && ((resolve->host.GetLength() - 1) == idx))
671 {
672 resolve->host.Delete(idx, 1);
673 }
674
675 resolve->port = ntohs( inPort );
676 resolve->ifi = inInterfaceIndex;
677 resolve->handler = handler;
678
679 err = resolve->txt.SetData( inTXT, inTXTSize );
680 check_noerr( err );
681
682 obj->OnResolve(resolve);
683 }
684 catch( ... )
685 {
686 dlog( kDebugLevelError, "ResolveCallBack: exception thrown\n" );
687 }
688
689 exit:
690 return;
691 }
692
693 //===========================================================================================================================
694 // OnResolve
695 //===========================================================================================================================
696
697 LONG ExplorerBarWindow::OnResolve( ResolveInfo * resolve )
698 {
699 CString url;
700 uint8_t * path;
701 uint8_t pathSize;
702 char * pathPrefix;
703 CString username;
704 CString password;
705
706
707 check( resolve );
708
709 // Get login info if needed.
710
711 if( resolve->handler->needsLogin )
712 {
713 LoginDialog dialog;
714
715 if( !dialog.GetLogin( username, password ) )
716 {
717 goto exit;
718 }
719 }
720
721 // If the HTTP TXT record is a "path=" entry, use it as the resource path. Otherwise, use "/".
722
723 pathPrefix = "";
724 if( strcmp( resolve->handler->type, "_http._tcp" ) == 0 )
725 {
726 uint8_t * txtData;
727 uint16_t txtLen;
728
729 resolve->txt.GetData( &txtData, &txtLen );
730
731 path = (uint8_t*) TXTRecordGetValuePtr(txtLen, txtData, kTXTRecordKeyPath, &pathSize);
732
733 if (path == NULL)
734 {
735 path = (uint8_t*) "";
736 pathSize = 1;
737 }
738 }
739 else
740 {
741 path = (uint8_t *) "";
742 pathSize = 1;
743 }
744
745 // Build the URL in the following format:
746 //
747 // <urlScheme>[<username>[:<password>]@]<name/ip>[<path>]
748
749 url.AppendFormat( TEXT( "%S" ), resolve->handler->urlScheme ); // URL Scheme
750 if( username.GetLength() > 0 )
751 {
752 url.AppendFormat( TEXT( "%s" ), username ); // Username
753 if( password.GetLength() > 0 )
754 {
755 url.AppendFormat( TEXT( ":%s" ), password ); // Password
756 }
757 url.AppendFormat( TEXT( "@" ) );
758 }
759
760 url += resolve->host; // Host
761 url.AppendFormat( TEXT( ":%d" ), resolve->port ); // :Port
762 url.AppendFormat( TEXT( "%S" ), pathPrefix ); // Path Prefix ("/" or empty).
763 url.AppendFormat( TEXT( "%.*S" ), (int) pathSize, (char *) path ); // Path (possibly empty).
764
765 // Tell Internet Explorer to go to the URL.
766
767 check( mOwner );
768 mOwner->GoToURL( url );
769
770 exit:
771 delete resolve;
772 return( 0 );
773 }
774
775 //===========================================================================================================================
776 // Stop
777 //===========================================================================================================================
778 void ExplorerBarWindow::Stop( DNSServiceRef ref )
779 {
780 m_serviceRefs.remove( ref );
781
782 WSAAsyncSelect(DNSServiceRefSockFD( ref ), m_hWnd, WM_PRIVATE_SERVICE_EVENT, 0);
783
784 DNSServiceRefDeallocate( ref );
785 }
786
787
788 #if 0
789 #pragma mark -
790 #endif
791
792 //===========================================================================================================================
793 // FindServiceArrayIndex
794 //===========================================================================================================================
795
796 DEBUG_LOCAL int FindServiceArrayIndex( const ServiceInfoArray &inArray, const ServiceInfo &inService, int &outIndex )
797 {
798 int result;
799 int lo;
800 int hi;
801 int mid;
802
803 result = -1;
804 mid = 0;
805 lo = 0;
806 hi = (int)( inArray.GetSize() - 1 );
807 while( lo <= hi )
808 {
809 mid = ( lo + hi ) / 2;
810 result = inService.displayName.CompareNoCase( inArray[ mid ]->displayName );
811 #if 0
812 if( result == 0 )
813 {
814 result = ( (int) inService.ifi ) - ( (int) inArray[ mid ]->ifi );
815 }
816 #endif
817 if( result == 0 )
818 {
819 break;
820 }
821 else if( result < 0 )
822 {
823 hi = mid - 1;
824 }
825 else
826 {
827 lo = mid + 1;
828 }
829 }
830 if( result == 0 )
831 {
832 mid += 1; // Bump index so new item is inserted after matching item.
833 }
834 else if( result > 0 )
835 {
836 mid += 1;
837 }
838 outIndex = mid;
839 return( result );
840 }