blob: 92cdeb307c2338ea645847518951f5f8d5b902a5 [file] [log] [blame]
/* -*- Mode: C; tab-width: 4 -*-
*
* Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "stdafx.h"
#include "Application.h"
#include "DNSServices.h"
#include "BrowserDialog.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
//===========================================================================================================================
// Constants
//===========================================================================================================================
#define WM_USER_SERVICE_ADD ( WM_USER + 0x100 )
#define WM_USER_SERVICE_REMOVE ( WM_USER + 0x101 )
//===========================================================================================================================
// Message Map
//===========================================================================================================================
BEGIN_MESSAGE_MAP(BrowserDialog, CDialog)
//{{AFX_MSG_MAP(BrowserDialog)
ON_NOTIFY(NM_CLICK, IDC_BROWSE_LIST, OnBrowserListDoubleClick)
ON_MESSAGE( WM_USER_SERVICE_ADD, OnServiceAdd )
ON_MESSAGE( WM_USER_SERVICE_REMOVE, OnServiceRemove )
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
static DWORD UTF8StringToStringObject( const char *inUTF8, CString &inObject );
//===========================================================================================================================
// BrowserDialog
//===========================================================================================================================
BrowserDialog::BrowserDialog( CWnd *inParent )
: CDialog( BrowserDialog::IDD, inParent )
{
//{{AFX_DATA_INIT(BrowserDialog)
// Note: the ClassWizard will add member initialization here
//}}AFX_DATA_INIT
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32.
mIcon = AfxGetApp()->LoadIcon( IDR_MAINFRAME );
ASSERT( mIcon );
}
//===========================================================================================================================
// DoDataExchange
//===========================================================================================================================
void BrowserDialog::DoDataExchange( CDataExchange *pDX )
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(BrowserDialog)
DDX_Control(pDX, IDC_BROWSE_LIST, mBrowserList);
//}}AFX_DATA_MAP
}
//===========================================================================================================================
// OnInitDialog
//===========================================================================================================================
BOOL BrowserDialog::OnInitDialog()
{
CString s;
CDialog::OnInitDialog();
// Set the icon for this dialog. The framework does this automatically when the application's main window is not a dialog.
SetIcon( mIcon, TRUE ); // Set big icon
SetIcon( mIcon, FALSE ); // Set small icon
CenterWindow( GetDesktopWindow() );
// Set up the list.
CRect rect;
s.LoadString( IDS_BROWSER_LIST_COLUMN_NAME );
mBrowserList.GetWindowRect( rect );
mBrowserList.InsertColumn( 0, s, LVCFMT_LEFT, rect.Width() - 8 );
// Start browsing for services.
DNSStatus err;
err = DNSBrowserCreate( 0, OnBrowserCallBack, this, &mBrowser );
if( err )
{
AfxMessageBox( IDP_SOCKETS_INIT_FAILED );
goto exit;
}
err = DNSBrowserStartServiceSearch( mBrowser, kDNSBrowserFlagAutoResolve, "_http._tcp", NULL );
if( err )
{
AfxMessageBox( IDP_SOCKETS_INIT_FAILED );
goto exit;
}
exit:
return( TRUE );
}
//===========================================================================================================================
// OnBrowserListDoubleClick
//===========================================================================================================================
void BrowserDialog::OnBrowserListDoubleClick( NMHDR *pNMHDR, LRESULT *pResult )
{
int selectedItem;
(void) pNMHDR; // Unused
selectedItem = mBrowserList.GetNextItem( -1, LVNI_SELECTED );
if( selectedItem >= 0 )
{
BrowserEntry * entry;
CString temp;
CString url;
// Build the URL from the IP and optional TXT record.
entry = &mBrowserEntries[ selectedItem ];
url += "http://" + entry->ip;
temp = entry->text;
if( temp.Find( TEXT( "path=" ) ) == 0 )
{
temp.Delete( 0, 5 );
}
if( temp.Find( '/' ) != 0 )
{
url += '/';
}
url += temp;
// Let the system open the URL in the correct app.
SHELLEXECUTEINFO info;
info.cbSize = sizeof( info );
info.fMask = 0;
info.hwnd = NULL;
info.lpVerb = NULL;
info.lpFile = url;
info.lpParameters = NULL;
info.lpDirectory = NULL;
info.nShow = SW_SHOWNORMAL;
info.hInstApp = NULL;
ShellExecuteEx( &info );
}
*pResult = 0;
}
//===========================================================================================================================
// OnBrowserCallBack [static]
//===========================================================================================================================
void
BrowserDialog::OnBrowserCallBack(
void * inContext,
DNSBrowserRef inRef,
DNSStatus inStatusCode,
const DNSBrowserEvent * inEvent )
{
BrowserDialog * dialog;
BrowserEntry * entry;
BOOL posted;
DNS_UNUSED( inStatusCode );
dialog = reinterpret_cast < BrowserDialog * > ( inContext );
ASSERT( dialog );
switch( inEvent->type )
{
case kDNSBrowserEventTypeResolved:
if( inEvent->data.resolved->address.addressType == kDNSNetworkAddressTypeIPv4 )
{
char ip[ 64 ];
sprintf( ip, "%u.%u.%u.%u:%u",
inEvent->data.resolved->address.u.ipv4.addr.v8[ 0 ],
inEvent->data.resolved->address.u.ipv4.addr.v8[ 1 ],
inEvent->data.resolved->address.u.ipv4.addr.v8[ 2 ],
inEvent->data.resolved->address.u.ipv4.addr.v8[ 3 ],
( inEvent->data.resolved->address.u.ipv4.port.v8[ 0 ] << 8 ) |
inEvent->data.resolved->address.u.ipv4.port.v8[ 1 ] );
entry = new BrowserEntry;
ASSERT( entry );
if( entry )
{
UTF8StringToStringObject( inEvent->data.resolved->name, entry->name );
UTF8StringToStringObject( ip, entry->ip );
UTF8StringToStringObject( inEvent->data.resolved->textRecord, entry->text );
posted = ::PostMessage( dialog->GetSafeHwnd(), WM_USER_SERVICE_ADD, 0, (LPARAM) entry );
ASSERT( posted );
if( !posted )
{
delete entry;
}
}
}
break;
case kDNSBrowserEventTypeRemoveService:
entry = new BrowserEntry;
ASSERT( entry );
if( entry )
{
UTF8StringToStringObject( inEvent->data.removeService.name, entry->name );
posted = ::PostMessage( dialog->GetSafeHwnd(), WM_USER_SERVICE_REMOVE, 0, (LPARAM) entry );
ASSERT( posted );
if( !posted )
{
delete entry;
}
}
break;
default:
break;
}
}
//===========================================================================================================================
// BrowserAddService
//===========================================================================================================================
LONG BrowserDialog::OnServiceAdd( WPARAM inWParam, LPARAM inLParam )
{
BrowserEntry * entry;
INT_PTR lo;
INT_PTR hi;
INT_PTR mid;
int result;
(void) inWParam; // Unused
entry = reinterpret_cast < BrowserEntry * > ( inLParam );
ASSERT( entry );
result = -1;
mid = 0;
lo = 0;
hi = mBrowserEntries.GetSize() - 1;
while( lo <= hi )
{
mid = ( lo + hi ) / 2;
result = entry->name.CompareNoCase( mBrowserEntries[ mid ].name );
if( result == 0 )
{
break;
}
else if( result < 0 )
{
hi = mid - 1;
}
else
{
lo = mid + 1;
}
}
if( result == 0 )
{
mBrowserEntries[ mid ].ip = entry->ip;
mBrowserEntries[ mid ].text = entry->text;
}
else
{
if( result > 0 )
{
mid += 1;
}
mBrowserEntries.InsertAt( mid, *entry );
mBrowserList.InsertItem( mid, entry->name );
}
delete entry;
return( 0 );
}
//===========================================================================================================================
// OnServiceRemove
//===========================================================================================================================
LONG BrowserDialog::OnServiceRemove( WPARAM inWParam, LPARAM inLParam )
{
BrowserEntry * entry;
INT_PTR hi;
INT_PTR lo;
INT_PTR mid;
int result;
(void) inWParam; // Unused
entry = reinterpret_cast < BrowserEntry * > ( inLParam );
ASSERT( entry );
result = -1;
mid = 0;
lo = 0;
hi = mBrowserEntries.GetSize() - 1;
while( lo <= hi )
{
mid = ( lo + hi ) / 2;
result = entry->name.CompareNoCase( mBrowserEntries[ mid ].name );
if( result == 0 )
{
break;
}
else if( result < 0 )
{
hi = mid - 1;
}
else
{
lo = mid + 1;
}
}
if( result == 0 )
{
mBrowserList.DeleteItem( mid );
mBrowserEntries.RemoveAt( mid );
}
delete entry;
return( 0 );
}
#if 0
#pragma mark -
#endif
//===========================================================================================================================
// UTF8StringToStringObject
//===========================================================================================================================
static DWORD UTF8StringToStringObject( const char *inUTF8, CString &inObject )
{
DWORD err;
int n;
wchar_t * unicode;
unicode = NULL;
n = MultiByteToWideChar( CP_UTF8, 0, inUTF8, -1, NULL, 0 );
if( n > 0 )
{
unicode = (wchar_t *) malloc( (size_t)( n * sizeof( wchar_t ) ) );
if( !unicode ) { err = ERROR_INSUFFICIENT_BUFFER; goto exit; };
n = MultiByteToWideChar( CP_UTF8, 0, inUTF8, -1, unicode, n );
inObject = unicode;
}
else
{
inObject = "";
}
err = 0;
exit:
if( unicode )
{
free( unicode );
}
return( err );
}