summaryrefslogtreecommitdiffstats
path: root/mDNSResponder/mDNSWindows/DNSServiceBrowser/Windows/Sources/ChooserDialog.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'mDNSResponder/mDNSWindows/DNSServiceBrowser/Windows/Sources/ChooserDialog.cpp')
-rw-r--r--mDNSResponder/mDNSWindows/DNSServiceBrowser/Windows/Sources/ChooserDialog.cpp1426
1 files changed, 1426 insertions, 0 deletions
diff --git a/mDNSResponder/mDNSWindows/DNSServiceBrowser/Windows/Sources/ChooserDialog.cpp b/mDNSResponder/mDNSWindows/DNSServiceBrowser/Windows/Sources/ChooserDialog.cpp
new file mode 100644
index 00000000..9e4db65b
--- /dev/null
+++ b/mDNSResponder/mDNSWindows/DNSServiceBrowser/Windows/Sources/ChooserDialog.cpp
@@ -0,0 +1,1426 @@
+/* -*- 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 <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <algorithm>
+#include <memory>
+
+#include "stdafx.h"
+
+#include "DNSServices.h"
+
+#include "Application.h"
+#include "AboutDialog.h"
+#include "LoginDialog.h"
+#include "Resource.h"
+
+#include "ChooserDialog.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+#if 0
+#pragma mark == Constants ==
+#endif
+
+//===========================================================================================================================
+// Constants
+//===========================================================================================================================
+
+// Menus
+
+enum
+{
+ kChooserMenuIndexFile = 0,
+ kChooserMenuIndexHelp = 1
+};
+
+// Domain List
+
+#define kDomainListDefaultDomainColumnWidth 164
+
+// Service List
+
+#define kServiceListDefaultServiceColumnTypeWidth 146
+#define kServiceListDefaultServiceColumnDescWidth 230
+
+// Chooser List
+
+#define kChooserListDefaultNameColumnWidth 190
+#define kChooserListDefaultIPColumnWidth 120
+
+// Windows User Messages
+
+#define WM_USER_DOMAIN_ADD ( WM_USER + 0x100 )
+#define WM_USER_DOMAIN_REMOVE ( WM_USER + 0x101 )
+#define WM_USER_SERVICE_ADD ( WM_USER + 0x102 )
+#define WM_USER_SERVICE_REMOVE ( WM_USER + 0x103 )
+#define WM_USER_RESOLVE ( WM_USER + 0x104 )
+
+#if 0
+#pragma mark == Constants - Service Table ==
+#endif
+
+//===========================================================================================================================
+// Constants - Service Table
+//===========================================================================================================================
+
+struct KnownServiceEntry
+{
+ const char * serviceType;
+ const char * description;
+ const char * urlScheme;
+ bool useText;
+};
+
+static const KnownServiceEntry kKnownServiceTable[] =
+{
+ { "_accountedge._tcp.", "MYOB AccountEdge", "", false },
+ { "_aecoretech._tcp.", "Apple Application Engineering Services", "", false },
+ { "_afpovertcp._tcp.", "Apple File Sharing (AFP)", "afp://", false },
+ { "_airport._tcp.", "AirPort Base Station", "", false },
+ { "_apple-sasl._tcp.", "Apple Password Server", "", false },
+ { "_aquamon._tcp.", "AquaMon", "", false },
+ { "_async._tcp", "address-o-sync", "", false },
+ { "_auth._tcp.", "Authentication Service", "", false },
+ { "_bootps._tcp.", "Bootstrap Protocol Server", "", false },
+ { "_bousg._tcp.", "Bag Of Unusual Strategy Games", "", false },
+ { "_browse._udp.", "DNS Service Discovery", "", false },
+ { "_cheat._tcp.", "The Cheat", "", false },
+ { "_chess._tcp", "Project Gridlock", "", false },
+ { "_chfts._tcp", "Fluid Theme Server", "", false },
+ { "_clipboard._tcp", "Clipboard Sharing", "", false },
+ { "_contactserver._tcp.", "Now Up-to-Date & Contact", "", false },
+ { "_cvspserver._tcp", "CVS PServer", "", false },
+ { "_cytv._tcp.", "CyTV Network streaming for Elgato EyeTV", "", false },
+ { "_daap._tcp.", "Digital Audio Access Protocol (iTunes)", "daap://", false },
+ { "_distcc._tcp", "Distributed Compiler", "", false },
+ { "_dns-sd._udp", "DNS Service Discovery", "", false },
+ { "_dpap._tcp.", "Digital Picture Access Protocol (iPhoto)", "", false },
+ { "_earphoria._tcp.", "Earphoria", "", false },
+ { "_ecbyesfsgksc._tcp.", "Net Monitor Anti-Piracy Service", "", false },
+ { "_eheap._tcp.", "Interactive Room Software", "", false },
+ { "_embrace._tcp.", "DataEnvoy", "", false },
+ { "_eppc._tcp.", "Remote AppleEvents", "eppc://", false },
+ { "_exec._tcp.", "Remote Process Execution", "", false },
+ { "_facespan._tcp.", "FaceSpan", "", false },
+ { "_fjork._tcp.", "Fjork", "", false },
+ { "_ftp._tcp.", "File Transfer (FTP)", "ftp://", false },
+ { "_ftpcroco._tcp.", "Crocodile FTP Server", "", false },
+ { "_gbs-smp._tcp.", "SnapMail", "", false },
+ { "_gbs-stp._tcp.", "SnapTalk", "", false },
+ { "_grillezvous._tcp.", "Roxio ToastAnywhere(tm) Recorder Sharing", "", false },
+ { "_h323._tcp.", "H.323", "", false },
+ { "_hotwayd._tcp", "Hotwayd", "", false },
+ { "_http._tcp.", "Web Server (HTTP)", "http://", true },
+ { "_hydra._tcp", "SubEthaEdit", "", false },
+ { "_ica-networking._tcp.", "Image Capture Networking", "", false },
+ { "_ichalkboard._tcp.", "iChalk", "", false },
+ { "_ichat._tcp.", "iChat", "ichat://", false },
+ { "_iconquer._tcp.", "iConquer", "", false },
+ { "_imap._tcp.", "Internet Message Access Protocol", "", false },
+ { "_imidi._tcp.", "iMidi", "", false },
+ { "_ipp._tcp.", "Printer (IPP)", "ipp://", false },
+ { "_ishare._tcp.", "iShare", "", false },
+ { "_isparx._tcp.", "iSparx", "", false },
+ { "_istorm._tcp", "iStorm", "", false },
+ { "_iwork._tcp.", "iWork Server", "", false },
+ { "_liaison._tcp.", "Liaison", "", false },
+ { "_login._tcp.", "Remote Login a la Telnet", "", false },
+ { "_lontalk._tcp.", "LonTalk over IP (ANSI 852)", "", false },
+ { "_lonworks._tcp.", "Echelon LNS Remote Client", "", false },
+ { "_macfoh-remote._tcp.", "MacFOH Remote", "", false },
+ { "_moneyworks._tcp.", "MoneyWorks", "", false },
+ { "_mp3sushi._tcp", "MP3 Sushi", "", false },
+ { "_mttp._tcp.", "MenuTunes Sharing", "", false },
+ { "_ncbroadcast._tcp.", "Network Clipboard Broadcasts", "", false },
+ { "_ncdirect._tcp.", "Network Clipboard Direct Transfers", "", false },
+ { "_ncsyncserver._tcp.", "Network Clipboard Sync Server", "", false },
+ { "_newton-dock._tcp.", "Escale", "", false },
+ { "_nfs._tcp", "NFS", "", false },
+ { "_nssocketport._tcp.", "DO over NSSocketPort", "", false },
+ { "_omni-bookmark._tcp.", "OmniWeb", "", false },
+ { "_openbase._tcp.", "OpenBase SQL", "", false },
+ { "_p2pchat._tcp.", "Peer-to-Peer Chat", "", false },
+ { "_pdl-datastream._tcp.", "Printer (PDL)", "pdl://", false },
+ { "_poch._tcp.", "Parallel OperatiOn and Control Heuristic", "", false },
+ { "_pop_2_ambrosia._tcp.", "Pop-Pop", "", false },
+ { "_pop3._tcp", "POP3 Server", "", false },
+ { "_postgresql._tcp", "PostgreSQL Server", "", false },
+ { "_presence._tcp", "iChat AV", "", false },
+ { "_printer._tcp.", "Printer (LPR)", "lpr://", false },
+ { "_ptp._tcp.", "Picture Transfer (PTP)", "ptp://", false },
+ { "_register._tcp", "DNS Service Discovery", "", false },
+ { "_rfb._tcp.", "Remote Frame Buffer", "", false },
+ { "_riousbprint._tcp.", "Remote I/O USB Printer Protocol", "", false },
+ { "_rtsp._tcp.", "Real Time Stream Control Protocol", "", false },
+ { "_safarimenu._tcp", "Safari Menu", "", false },
+ { "_scone._tcp", "Scone", "", false },
+ { "_sdsharing._tcp.", "Speed Download", "", false },
+ { "_seeCard._tcp.", "seeCard", "", false },
+ { "_services._udp.", "DNS Service Discovery", "", false },
+ { "_shell._tcp.", "like exec, but automatic authentication", "", false },
+ { "_shout._tcp.", "Shout", "", false },
+ { "_shoutcast._tcp", "Nicecast", "", false },
+ { "_smb._tcp.", "Windows File Sharing (SMB)", "smb://", false },
+ { "_soap._tcp.", "Simple Object Access Protocol", "", false },
+ { "_spincrisis._tcp.", "Spin Crisis", "", false },
+ { "_spl-itunes._tcp.", "launchTunes", "", false },
+ { "_spr-itunes._tcp.", "netTunes", "", false },
+ { "_ssh._tcp.", "Secure Shell (SSH)", "ssh://", false },
+ { "_ssscreenshare._tcp", "Screen Sharing", "", false },
+ { "_sge-exec._tcp", "Sun Grid Engine (Execution Host)", "", false },
+ { "_sge-qmaster._tcp", "Sun Grid Engine (Master)", "", false },
+ { "_stickynotes._tcp", "Sticky Notes", "", false },
+ { "_strateges._tcp", "Strateges", "", false },
+ { "_sxqdea._tcp", "Synchronize! Pro X", "", false },
+ { "_sybase-tds._tcp", "Sybase Server", "", false },
+ { "_tce._tcp", "Power Card", "", false },
+ { "_teamlist._tcp", "ARTIS Team Task", "", false },
+ { "_teleport._tcp", "teleport", "", false },
+ { "_telnet._tcp.", "Telnet", "telnet://", false },
+ { "_tftp._tcp.", "Trivial File Transfer (TFTP)", "tftp://", false },
+ { "_tinavigator._tcp.", "TI Navigator", "", false },
+ { "_tivo_servemedia._tcp", "TiVo", "", false },
+ { "_upnp._tcp.", "Universal Plug and Play", "", false },
+ { "_utest._tcp.", "uTest", "", false },
+ { "_vue4rendercow._tcp", "VueProRenderCow", "", false },
+ { "_webdav._tcp.", "WebDAV", "webdav://", false },
+ { "_whamb._tcp.", "Whamb", "", false },
+ { "_workstation._tcp", "Macintosh Manager", "", false },
+ { "_ws._tcp", "Web Services", "", false },
+ { "_xserveraid._tcp.", "Xserve RAID", "xsr://", false },
+ { "_xsync._tcp.", "Xserve RAID Synchronization", "", false },
+
+ { "", "", "", false },
+
+ // Unofficial and invalid service types that will be phased out:
+
+ { "_clipboardsharing._tcp.", "ClipboardSharing", "", false },
+ { "_MacOSXDupSuppress._tcp.", "Mac OS X Duplicate Suppression", "", false },
+ { "_netmonitorserver._tcp.", "Net Monitor Server", "", false },
+ { "_networkclipboard._tcp.", "Network Clipboard", "", false },
+ { "_slimdevices_slimp3_cli._tcp.", "SliMP3 Server Command-Line Interface", "", false },
+ { "_slimdevices_slimp3_http._tcp.", "SliMP3 Server Web Interface", "", false },
+ { "_tieducationalhandhelddevice._tcp.", "TI Connect Manager", "", false },
+ { "_tivo_servemedia._tcp.", "TiVo", "", false },
+
+ { NULL, NULL, NULL, false },
+};
+
+#if 0
+#pragma mark == Structures ==
+#endif
+
+//===========================================================================================================================
+// Structures
+//===========================================================================================================================
+
+struct DomainEventInfo
+{
+ DNSBrowserEventType eventType;
+ CString domain;
+ DNSNetworkAddress ifIP;
+};
+
+struct ServiceEventInfo
+{
+ DNSBrowserEventType eventType;
+ std::string name;
+ std::string type;
+ std::string domain;
+ DNSNetworkAddress ifIP;
+};
+
+#if 0
+#pragma mark == Prototypes ==
+#endif
+
+//===========================================================================================================================
+// Prototypes
+//===========================================================================================================================
+
+static void
+ BrowserCallBack(
+ void * inContext,
+ DNSBrowserRef inRef,
+ DNSStatus inStatusCode,
+ const DNSBrowserEvent * inEvent );
+
+static char * DNSNetworkAddressToString( const DNSNetworkAddress *inAddr, char *outString );
+
+static DWORD UTF8StringToStringObject( const char *inUTF8, CString &inObject );
+static DWORD StringObjectToUTF8String( CString &inObject, std::string &outUTF8 );
+
+#if 0
+#pragma mark == Message Map ==
+#endif
+
+//===========================================================================================================================
+// Message Map
+//===========================================================================================================================
+
+BEGIN_MESSAGE_MAP(ChooserDialog, CDialog)
+ //{{AFX_MSG_MAP(ChooserDialog)
+ ON_WM_SYSCOMMAND()
+ ON_NOTIFY(LVN_ITEMCHANGED, IDC_DOMAIN_LIST, OnDomainListChanged)
+ ON_NOTIFY(LVN_ITEMCHANGED, IDC_SERVICE_LIST, OnServiceListChanged)
+ ON_NOTIFY(LVN_ITEMCHANGED, IDC_CHOOSER_LIST, OnChooserListChanged)
+ ON_NOTIFY(NM_DBLCLK, IDC_CHOOSER_LIST, OnChooserListDoubleClick)
+ ON_COMMAND(ID_HELP_ABOUT, OnAbout)
+ ON_WM_INITMENUPOPUP()
+ ON_WM_ACTIVATE()
+ ON_COMMAND(ID_FILE_CLOSE, OnFileClose)
+ ON_COMMAND(ID_FILE_EXIT, OnExit)
+ ON_WM_CLOSE()
+ ON_WM_NCDESTROY()
+ //}}AFX_MSG_MAP
+ ON_MESSAGE( WM_USER_DOMAIN_ADD, OnDomainAdd )
+ ON_MESSAGE( WM_USER_DOMAIN_REMOVE, OnDomainRemove )
+ ON_MESSAGE( WM_USER_SERVICE_ADD, OnServiceAdd )
+ ON_MESSAGE( WM_USER_SERVICE_REMOVE, OnServiceRemove )
+ ON_MESSAGE( WM_USER_RESOLVE, OnResolve )
+END_MESSAGE_MAP()
+
+#if 0
+#pragma mark == Routines ==
+#endif
+
+//===========================================================================================================================
+// ChooserDialog
+//===========================================================================================================================
+
+ChooserDialog::ChooserDialog( CWnd *inParent )
+ : CDialog( ChooserDialog::IDD, inParent)
+{
+ //{{AFX_DATA_INIT(ChooserDialog)
+ // Note: the ClassWizard will add member initialization here
+ //}}AFX_DATA_INIT
+
+ // Load menu accelerator table.
+
+ mMenuAcceleratorTable = ::LoadAccelerators( AfxGetInstanceHandle(), MAKEINTRESOURCE( IDR_CHOOSER_DIALOG_MENU_ACCELERATORS ) );
+ assert( mMenuAcceleratorTable );
+
+ mBrowser = NULL;
+ mIsServiceBrowsing = false;
+}
+
+//===========================================================================================================================
+// ~ChooserDialog
+//===========================================================================================================================
+
+ChooserDialog::~ChooserDialog( void )
+{
+ if( mBrowser )
+ {
+ DNSStatus err;
+
+ err = DNSBrowserRelease( mBrowser, 0 );
+ assert( err == kDNSNoErr );
+ }
+}
+
+//===========================================================================================================================
+// DoDataExchange
+//===========================================================================================================================
+
+void ChooserDialog::DoDataExchange( CDataExchange *pDX )
+{
+ CDialog::DoDataExchange(pDX);
+
+ //{{AFX_DATA_MAP(ChooserDialog)
+ DDX_Control(pDX, IDC_SERVICE_LIST, mServiceList);
+ DDX_Control(pDX, IDC_DOMAIN_LIST, mDomainList);
+ DDX_Control(pDX, IDC_CHOOSER_LIST, mChooserList);
+ //}}AFX_DATA_MAP
+}
+
+//===========================================================================================================================
+// OnInitDialog
+//===========================================================================================================================
+
+BOOL ChooserDialog::OnInitDialog( void )
+{
+ HICON icon;
+ BOOL result;
+ CString tempString;
+ DNSStatus err;
+
+ // Initialize our parent.
+
+ CDialog::OnInitDialog();
+
+ // Set up the window icon.
+
+ icon = AfxGetApp()->LoadIcon( IDR_MAIN_ICON );
+ assert( icon );
+ if( icon )
+ {
+ SetIcon( icon, TRUE ); // Set big icon
+ SetIcon( icon, FALSE ); // Set small icon
+ }
+
+ // Set up the Domain List.
+
+ result = tempString.LoadString( IDS_CHOOSER_DOMAIN_COLUMN_NAME );
+ assert( result );
+ mDomainList.InsertColumn( 0, tempString, LVCFMT_LEFT, kDomainListDefaultDomainColumnWidth );
+
+ // Set up the Service List.
+
+ result = tempString.LoadString( IDS_CHOOSER_SERVICE_COLUMN_TYPE );
+ assert( result );
+ mServiceList.InsertColumn( 0, tempString, LVCFMT_LEFT, kServiceListDefaultServiceColumnTypeWidth );
+
+ result = tempString.LoadString( IDS_CHOOSER_SERVICE_COLUMN_DESC );
+ assert( result );
+ mServiceList.InsertColumn( 1, tempString, LVCFMT_LEFT, kServiceListDefaultServiceColumnDescWidth );
+
+ PopulateServicesList();
+
+ // Set up the Chooser List.
+
+ result = tempString.LoadString( IDS_CHOOSER_CHOOSER_NAME_COLUMN_NAME );
+ assert( result );
+ mChooserList.InsertColumn( 0, tempString, LVCFMT_LEFT, kChooserListDefaultNameColumnWidth );
+
+ result = tempString.LoadString( IDS_CHOOSER_CHOOSER_IP_COLUMN_NAME );
+ assert( result );
+ mChooserList.InsertColumn( 1, tempString, LVCFMT_LEFT, kChooserListDefaultIPColumnWidth );
+
+ // Set up the other controls.
+
+ UpdateInfoDisplay();
+
+ // Start browsing for domains.
+
+ err = DNSBrowserCreate( 0, BrowserCallBack, this, &mBrowser );
+ assert( err == kDNSNoErr );
+
+ err = DNSBrowserStartDomainSearch( mBrowser, 0 );
+ assert( err == kDNSNoErr );
+
+ return( true );
+}
+
+//===========================================================================================================================
+// OnFileClose
+//===========================================================================================================================
+
+void ChooserDialog::OnFileClose()
+{
+ OnClose();
+}
+
+//===========================================================================================================================
+// OnActivate
+//===========================================================================================================================
+
+void ChooserDialog::OnActivate( UINT nState, CWnd* pWndOther, BOOL bMinimized )
+{
+ // Always make the active window the "main" window so modal dialogs work better and the app quits after closing
+ // the last window.
+
+ gApp.m_pMainWnd = this;
+
+ CDialog::OnActivate(nState, pWndOther, bMinimized);
+}
+
+//===========================================================================================================================
+// PostNcDestroy
+//===========================================================================================================================
+
+void ChooserDialog::PostNcDestroy()
+{
+ // Call the base class to do the normal cleanup.
+
+ delete this;
+}
+
+//===========================================================================================================================
+// PreTranslateMessage
+//===========================================================================================================================
+
+BOOL ChooserDialog::PreTranslateMessage(MSG* pMsg)
+{
+ BOOL result;
+
+ result = false;
+ assert( mMenuAcceleratorTable );
+ if( mMenuAcceleratorTable )
+ {
+ result = ::TranslateAccelerator( m_hWnd, mMenuAcceleratorTable, pMsg );
+ }
+ if( !result )
+ {
+ result = CDialog::PreTranslateMessage( pMsg );
+ }
+ return( result );
+}
+
+//===========================================================================================================================
+// OnInitMenuPopup
+//===========================================================================================================================
+
+void ChooserDialog::OnInitMenuPopup( CMenu *pPopupMenu, UINT nIndex, BOOL bSysMenu )
+{
+ CDialog::OnInitMenuPopup( pPopupMenu, nIndex, bSysMenu );
+
+ switch( nIndex )
+ {
+ case kChooserMenuIndexFile:
+ break;
+
+ case kChooserMenuIndexHelp:
+ break;
+
+ default:
+ break;
+ }
+}
+
+//===========================================================================================================================
+// OnExit
+//===========================================================================================================================
+
+void ChooserDialog::OnExit()
+{
+ OnClose();
+}
+
+//===========================================================================================================================
+// OnAbout
+//===========================================================================================================================
+
+void ChooserDialog::OnAbout()
+{
+ AboutDialog dialog;
+
+ dialog.DoModal();
+}
+
+//===========================================================================================================================
+// OnSysCommand
+//===========================================================================================================================
+
+void ChooserDialog::OnSysCommand( UINT inID, LPARAM inParam )
+{
+ CDialog::OnSysCommand( inID, inParam );
+}
+
+//===========================================================================================================================
+// OnClose
+//===========================================================================================================================
+
+void ChooserDialog::OnClose()
+{
+ StopBrowsing();
+
+ gApp.m_pMainWnd = this;
+ DestroyWindow();
+}
+
+//===========================================================================================================================
+// OnNcDestroy
+//===========================================================================================================================
+
+void ChooserDialog::OnNcDestroy()
+{
+ gApp.m_pMainWnd = this;
+
+ CDialog::OnNcDestroy();
+}
+
+//===========================================================================================================================
+// OnDomainListChanged
+//===========================================================================================================================
+
+void ChooserDialog::OnDomainListChanged( NMHDR *pNMHDR, LRESULT *pResult )
+{
+ UNUSED_ALWAYS( pNMHDR );
+
+ // Domain list changes have similar effects to service list changes so reuse that code path by calling it here.
+
+ OnServiceListChanged( NULL, NULL );
+
+ *pResult = 0;
+}
+
+//===========================================================================================================================
+// OnServiceListChanged
+//===========================================================================================================================
+
+void ChooserDialog::OnServiceListChanged( NMHDR *pNMHDR, LRESULT *pResult )
+{
+ int selectedType;
+ int selectedDomain;
+
+ UNUSED_ALWAYS( pNMHDR );
+
+ // Stop any existing service search.
+
+ StopBrowsing();
+
+ // If a domain and service type are selected, start searching for the service type on the domain.
+
+ selectedType = mServiceList.GetNextItem( -1, LVNI_SELECTED );
+ selectedDomain = mDomainList.GetNextItem( -1, LVNI_SELECTED );
+
+ if( ( selectedType >= 0 ) && ( selectedDomain >= 0 ) )
+ {
+ CString s;
+ std::string utf8;
+ const char * type;
+
+ s = mDomainList.GetItemText( selectedDomain, 0 );
+ StringObjectToUTF8String( s, utf8 );
+ type = mServiceTypes[ selectedType ].serviceType.c_str();
+ if( *type != '\0' )
+ {
+ StartBrowsing( type, utf8.c_str() );
+ }
+ }
+
+ if( pResult )
+ {
+ *pResult = 0;
+ }
+}
+
+//===========================================================================================================================
+// OnChooserListChanged
+//===========================================================================================================================
+
+void ChooserDialog::OnChooserListChanged( NMHDR *pNMHDR, LRESULT *pResult )
+{
+ UNUSED_ALWAYS( pNMHDR );
+
+ UpdateInfoDisplay();
+ *pResult = 0;
+}
+
+//===========================================================================================================================
+// OnChooserListDoubleClick
+//===========================================================================================================================
+
+void ChooserDialog::OnChooserListDoubleClick( NMHDR *pNMHDR, LRESULT *pResult )
+{
+ int selectedItem;
+
+ UNUSED_ALWAYS( pNMHDR );
+
+ // Display the service instance if it is selected. Otherwise, clear all the info.
+
+ selectedItem = mChooserList.GetNextItem( -1, LVNI_SELECTED );
+ if( selectedItem >= 0 )
+ {
+ ServiceInstanceInfo * p;
+ CString url;
+ const KnownServiceEntry * service;
+
+ assert( selectedItem < (int) mServiceInstances.size() );
+ p = &mServiceInstances[ selectedItem ];
+
+ // Search for a known service type entry that matches.
+
+ for( service = kKnownServiceTable; service->serviceType; ++service )
+ {
+ if( p->type == service->serviceType )
+ {
+ break;
+ }
+ }
+ if( service->serviceType )
+ {
+ const char * text;
+
+ // Create a URL representing the service instance.
+
+ if( strcmp( service->serviceType, "_smb._tcp." ) == 0 )
+ {
+ // Special case for SMB (no port number).
+
+ url.Format( TEXT( "%s%s/" ), service->urlScheme, (const char *) p->ip.c_str() );
+ }
+ else if( strcmp( service->serviceType, "_ftp._tcp." ) == 0 )
+ {
+ // Special case for FTP to get login info.
+
+ LoginDialog dialog;
+ CString username;
+ CString password;
+
+ if( !dialog.GetLogin( username, password ) )
+ {
+ goto exit;
+ }
+
+ // Build URL in the following format:
+ //
+ // ftp://[username[:password]@]<ip>
+
+ url += service->urlScheme;
+ if( username.GetLength() > 0 )
+ {
+ url += username;
+ if( password.GetLength() > 0 )
+ {
+ url += ':';
+ url += password;
+ }
+ url += '@';
+ }
+ url += p->ip.c_str();
+ }
+ else if( strcmp( service->serviceType, "_http._tcp." ) == 0 )
+ {
+ // Special case for HTTP to exclude "path=" if present.
+
+ text = service->useText ? p->text.c_str() : "";
+ if( strncmp( text, "path=", 5 ) == 0 )
+ {
+ text += 5;
+ }
+ if( *text != '/' )
+ {
+ url.Format( TEXT( "%s%s/%s" ), service->urlScheme, (const char *) p->ip.c_str(), text );
+ }
+ else
+ {
+ url.Format( TEXT( "%s%s%s" ), service->urlScheme, (const char *) p->ip.c_str(), text );
+ }
+ }
+ else
+ {
+ text = service->useText ? p->text.c_str() : "";
+ url.Format( TEXT( "%s%s/%s" ), service->urlScheme, (const char *) p->ip.c_str(), text );
+ }
+
+ // Let the system open the URL in the correct app.
+
+ {
+ CWaitCursor waitCursor;
+
+ ShellExecute( NULL, TEXT( "open" ), url, TEXT( "" ), TEXT( "c:\\" ), SW_SHOWNORMAL );
+ }
+ }
+ }
+
+exit:
+ *pResult = 0;
+}
+
+//===========================================================================================================================
+// OnCancel
+//===========================================================================================================================
+
+void ChooserDialog::OnCancel()
+{
+ // Do nothing.
+}
+
+//===========================================================================================================================
+// PopulateServicesList
+//===========================================================================================================================
+
+void ChooserDialog::PopulateServicesList( void )
+{
+ ServiceTypeVector::iterator i;
+ CString type;
+ CString desc;
+ std::string tmp;
+
+ // Add a fixed list of known services.
+
+ if( mServiceTypes.empty() )
+ {
+ const KnownServiceEntry * service;
+
+ for( service = kKnownServiceTable; service->serviceType; ++service )
+ {
+ ServiceTypeInfo info;
+
+ info.serviceType = service->serviceType;
+ info.description = service->description;
+ info.urlScheme = service->urlScheme;
+ mServiceTypes.push_back( info );
+ }
+ }
+
+ // Add each service to the list.
+
+ for( i = mServiceTypes.begin(); i != mServiceTypes.end(); ++i )
+ {
+ const char * p;
+ const char * q;
+
+ p = ( *i ).serviceType.c_str();
+ if( *p == '_' ) ++p; // Skip leading '_'.
+ q = strchr( p, '.' ); // Find first '.'.
+ if( q ) tmp.assign( p, (size_t)( q - p ) ); // Use only up to the first '.'.
+ else tmp.assign( p ); // No '.' so use the entire string.
+ UTF8StringToStringObject( tmp.c_str(), type );
+ UTF8StringToStringObject( ( *i ).description.c_str(), desc );
+
+ int n;
+
+ n = mServiceList.GetItemCount();
+ mServiceList.InsertItem( n, type );
+ mServiceList.SetItemText( n, 1, desc );
+ }
+
+ // Select the first service type by default.
+
+ if( !mServiceTypes.empty() )
+ {
+ mServiceList.SetItemState( 0, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED );
+ }
+}
+
+//===========================================================================================================================
+// UpdateInfoDisplay
+//===========================================================================================================================
+
+void ChooserDialog::UpdateInfoDisplay( void )
+{
+ int selectedItem;
+ std::string name;
+ CString s;
+ std::string ip;
+ std::string ifIP;
+ std::string text;
+ std::string textNewLines;
+ std::string hostName;
+ CWnd * item;
+ std::string::iterator i;
+
+ // Display the service instance if it is selected. Otherwise, clear all the info.
+
+ selectedItem = mChooserList.GetNextItem( -1, LVNI_SELECTED );
+ if( selectedItem >= 0 )
+ {
+ ServiceInstanceInfo * p;
+
+ assert( selectedItem < (int) mServiceInstances.size() );
+ p = &mServiceInstances[ selectedItem ];
+
+ name = p->name;
+ ip = p->ip;
+ ifIP = p->ifIP;
+ text = p->text;
+ hostName = p->hostName;
+
+ // Sync up the list items with the actual data (IP address may change).
+
+ UTF8StringToStringObject( ip.c_str(), s );
+ mChooserList.SetItemText( selectedItem, 1, s );
+ }
+
+ // Name
+
+ item = (CWnd *) this->GetDlgItem( IDC_INFO_NAME_TEXT );
+ assert( item );
+ UTF8StringToStringObject( name.c_str(), s );
+ item->SetWindowText( s );
+
+ // IP
+
+ item = (CWnd *) this->GetDlgItem( IDC_INFO_IP_TEXT );
+ assert( item );
+ UTF8StringToStringObject( ip.c_str(), s );
+ item->SetWindowText( s );
+
+ // Interface
+
+ item = (CWnd *) this->GetDlgItem( IDC_INFO_INTERFACE_TEXT );
+ assert( item );
+ UTF8StringToStringObject( ifIP.c_str(), s );
+ item->SetWindowText( s );
+
+
+ item = (CWnd *) this->GetDlgItem( IDC_INFO_HOST_NAME_TEXT );
+ assert( item );
+ UTF8StringToStringObject( hostName.c_str(), s );
+ item->SetWindowText( s );
+
+ // Text
+
+ item = (CWnd *) this->GetDlgItem( IDC_INFO_TEXT_TEXT );
+ assert( item );
+ for( i = text.begin(); i != text.end(); ++i )
+ {
+ if( *i == '\1' )
+ {
+ textNewLines += "\r\n";
+ }
+ else
+ {
+ textNewLines += *i;
+ }
+ }
+ UTF8StringToStringObject( textNewLines.c_str(), s );
+ item->SetWindowText( s );
+}
+
+#if 0
+#pragma mark -
+#endif
+
+//===========================================================================================================================
+// OnDomainAdd
+//===========================================================================================================================
+
+LONG ChooserDialog::OnDomainAdd( WPARAM inWParam, LPARAM inLParam )
+{
+ DomainEventInfo * p;
+ std::auto_ptr < DomainEventInfo > pAutoPtr;
+ int n;
+ int i;
+ CString domain;
+ CString s;
+ bool found;
+
+ UNUSED_ALWAYS( inWParam );
+
+ assert( inLParam );
+ p = reinterpret_cast <DomainEventInfo *> ( inLParam );
+ pAutoPtr.reset( p );
+
+ // Search to see if we already know about this domain. If not, add it to the list.
+
+ found = false;
+ domain = p->domain;
+ n = mDomainList.GetItemCount();
+ for( i = 0; i < n; ++i )
+ {
+ s = mDomainList.GetItemText( i, 0 );
+ if( s == domain )
+ {
+ found = true;
+ break;
+ }
+ }
+ if( !found )
+ {
+ int selectedItem;
+
+ mDomainList.InsertItem( n, domain );
+
+ // If no domains are selected and the domain being added is a default domain, select it.
+
+ selectedItem = mDomainList.GetNextItem( -1, LVNI_SELECTED );
+ if( ( selectedItem < 0 ) && ( p->eventType == kDNSBrowserEventTypeAddDefaultDomain ) )
+ {
+ mDomainList.SetItemState( n, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED );
+ }
+ }
+ return( 0 );
+}
+
+//===========================================================================================================================
+// OnDomainRemove
+//===========================================================================================================================
+
+LONG ChooserDialog::OnDomainRemove( WPARAM inWParam, LPARAM inLParam )
+{
+ DomainEventInfo * p;
+ std::auto_ptr < DomainEventInfo > pAutoPtr;
+ int n;
+ int i;
+ CString domain;
+ CString s;
+ bool found;
+
+ UNUSED_ALWAYS( inWParam );
+
+ assert( inLParam );
+ p = reinterpret_cast <DomainEventInfo *> ( inLParam );
+ pAutoPtr.reset( p );
+
+ // Search to see if we know about this domain. If so, remove it from the list.
+
+ found = false;
+ domain = p->domain;
+ n = mDomainList.GetItemCount();
+ for( i = 0; i < n; ++i )
+ {
+ s = mDomainList.GetItemText( i, 0 );
+ if( s == domain )
+ {
+ found = true;
+ break;
+ }
+ }
+ if( found )
+ {
+ mDomainList.DeleteItem( i );
+ }
+ return( 0 );
+}
+
+//===========================================================================================================================
+// OnServiceAdd
+//===========================================================================================================================
+
+LONG ChooserDialog::OnServiceAdd( WPARAM inWParam, LPARAM inLParam )
+{
+ ServiceEventInfo * p;
+ std::auto_ptr < ServiceEventInfo > pAutoPtr;
+
+ UNUSED_ALWAYS( inWParam );
+
+ assert( inLParam );
+ p = reinterpret_cast <ServiceEventInfo *> ( inLParam );
+ pAutoPtr.reset( p );
+
+ return( 0 );
+}
+
+//===========================================================================================================================
+// OnServiceRemove
+//===========================================================================================================================
+
+LONG ChooserDialog::OnServiceRemove( WPARAM inWParam, LPARAM inLParam )
+{
+ ServiceEventInfo * p;
+ std::auto_ptr < ServiceEventInfo > pAutoPtr;
+ bool found;
+ int n;
+ int i;
+
+ UNUSED_ALWAYS( inWParam );
+
+ assert( inLParam );
+ p = reinterpret_cast <ServiceEventInfo *> ( inLParam );
+ pAutoPtr.reset( p );
+
+ // Search to see if we know about this service instance. If so, remove it from the list.
+
+ found = false;
+ n = (int) mServiceInstances.size();
+ for( i = 0; i < n; ++i )
+ {
+ ServiceInstanceInfo * q;
+
+ // If the name, type, domain, and interface match, treat it as the same service instance.
+
+ q = &mServiceInstances[ i ];
+ if( ( p->name == q->name ) &&
+ ( p->type == q->type ) &&
+ ( p->domain == q->domain ) )
+ {
+ found = true;
+ break;
+ }
+ }
+ if( found )
+ {
+ mChooserList.DeleteItem( i );
+ assert( i < (int) mServiceInstances.size() );
+ mServiceInstances.erase( mServiceInstances.begin() + i );
+ }
+ return( 0 );
+}
+
+//===========================================================================================================================
+// OnResolve
+//===========================================================================================================================
+
+LONG ChooserDialog::OnResolve( WPARAM inWParam, LPARAM inLParam )
+{
+ ServiceInstanceInfo * p;
+ std::auto_ptr < ServiceInstanceInfo > pAutoPtr;
+ int selectedType;
+ int n;
+ int i;
+ bool found;
+
+ UNUSED_ALWAYS( inWParam );
+
+ assert( inLParam );
+ p = reinterpret_cast <ServiceInstanceInfo *> ( inLParam );
+ pAutoPtr.reset( p );
+
+ // Make sure it is for an item of the correct type. This handles any resolves that may have been queued up.
+
+ selectedType = mServiceList.GetNextItem( -1, LVNI_SELECTED );
+ assert( selectedType >= 0 );
+ if( selectedType >= 0 )
+ {
+ assert( selectedType <= (int) mServiceTypes.size() );
+ if( p->type != mServiceTypes[ selectedType ].serviceType )
+ {
+ goto exit;
+ }
+ }
+
+ // Search to see if we know about this service instance. If so, update its info. Otherwise, add it to the list.
+
+ found = false;
+ n = (int) mServiceInstances.size();
+ for( i = 0; i < n; ++i )
+ {
+ ServiceInstanceInfo * q;
+
+ // If the name, type, domain, and interface matches, treat it as the same service instance.
+
+ q = &mServiceInstances[ i ];
+ if( ( p->name == q->name ) &&
+ ( p->type == q->type ) &&
+ ( p->domain == q->domain ) &&
+ ( p->ifIP == q->ifIP ) )
+ {
+ found = true;
+ break;
+ }
+ }
+ if( found )
+ {
+ mServiceInstances[ i ] = *p;
+ }
+ else
+ {
+ CString s;
+
+ mServiceInstances.push_back( *p );
+ UTF8StringToStringObject( p->name.c_str(), s );
+ mChooserList.InsertItem( n, s );
+
+ UTF8StringToStringObject( p->ip.c_str(), s );
+ mChooserList.SetItemText( n, 1, s );
+
+ // If this is the only item, select it.
+
+ if( n == 0 )
+ {
+ mChooserList.SetItemState( n, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED );
+ }
+ }
+ UpdateInfoDisplay();
+
+exit:
+ return( 0 );
+}
+
+//===========================================================================================================================
+// StartBrowsing
+//===========================================================================================================================
+
+void ChooserDialog::StartBrowsing( const char *inType, const char *inDomain )
+{
+ DNSStatus err;
+
+ assert( mServiceInstances.empty() );
+ assert( mChooserList.GetItemCount() == 0 );
+ assert( !mIsServiceBrowsing );
+
+ mChooserList.DeleteAllItems();
+ mServiceInstances.clear();
+
+ mIsServiceBrowsing = true;
+ err = DNSBrowserStartServiceSearch( mBrowser, kDNSBrowserFlagAutoResolve, inType, inDomain );
+ assert( err == kDNSNoErr );
+}
+
+//===========================================================================================================================
+// StopBrowsing
+//===========================================================================================================================
+
+void ChooserDialog::StopBrowsing( void )
+{
+ // If searching, stop.
+
+ if( mIsServiceBrowsing )
+ {
+ DNSStatus err;
+
+ mIsServiceBrowsing = false;
+ err = DNSBrowserStopServiceSearch( mBrowser, 0 );
+ assert( err == kDNSNoErr );
+ }
+
+ // Remove all service instances.
+
+ mChooserList.DeleteAllItems();
+ assert( mChooserList.GetItemCount() == 0 );
+ mServiceInstances.clear();
+ assert( mServiceInstances.empty() );
+ UpdateInfoDisplay();
+}
+
+#if 0
+#pragma mark -
+#endif
+
+//===========================================================================================================================
+// BrowserCallBack
+//===========================================================================================================================
+
+static void
+ BrowserCallBack(
+ void * inContext,
+ DNSBrowserRef inRef,
+ DNSStatus inStatusCode,
+ const DNSBrowserEvent * inEvent )
+{
+ ChooserDialog * dialog;
+ UINT message;
+ BOOL posted;
+
+ UNUSED_ALWAYS( inStatusCode );
+ UNUSED_ALWAYS( inRef );
+
+ // Check parameters.
+
+ assert( inContext );
+ dialog = reinterpret_cast <ChooserDialog *> ( inContext );
+
+ try
+ {
+ switch( inEvent->type )
+ {
+ case kDNSBrowserEventTypeRelease:
+ break;
+
+ // Domains
+
+ case kDNSBrowserEventTypeAddDomain:
+ case kDNSBrowserEventTypeAddDefaultDomain:
+ case kDNSBrowserEventTypeRemoveDomain:
+ {
+ DomainEventInfo * domain;
+ std::auto_ptr < DomainEventInfo > domainAutoPtr;
+
+ domain = new DomainEventInfo;
+ domainAutoPtr.reset( domain );
+
+ domain->eventType = inEvent->type;
+ domain->domain = inEvent->data.addDomain.domain;
+ domain->ifIP = inEvent->data.addDomain.interfaceIP;
+
+ message = ( inEvent->type == kDNSBrowserEventTypeRemoveDomain ) ? WM_USER_DOMAIN_REMOVE : WM_USER_DOMAIN_ADD;
+ posted = ::PostMessage( dialog->GetSafeHwnd(), message, 0, (LPARAM) domain );
+ assert( posted );
+ if( posted )
+ {
+ domainAutoPtr.release();
+ }
+ break;
+ }
+
+ // Services
+
+ case kDNSBrowserEventTypeAddService:
+ case kDNSBrowserEventTypeRemoveService:
+ {
+ ServiceEventInfo * service;
+ std::auto_ptr < ServiceEventInfo > serviceAutoPtr;
+
+ service = new ServiceEventInfo;
+ serviceAutoPtr.reset( service );
+
+ service->eventType = inEvent->type;
+ service->name = inEvent->data.addService.name;
+ service->type = inEvent->data.addService.type;
+ service->domain = inEvent->data.addService.domain;
+ service->ifIP = inEvent->data.addService.interfaceIP;
+
+ message = ( inEvent->type == kDNSBrowserEventTypeAddService ) ? WM_USER_SERVICE_ADD : WM_USER_SERVICE_REMOVE;
+ posted = ::PostMessage( dialog->GetSafeHwnd(), message, 0, (LPARAM) service );
+ assert( posted );
+ if( posted )
+ {
+ serviceAutoPtr.release();
+ }
+ break;
+ }
+
+ // Resolves
+
+ case kDNSBrowserEventTypeResolved:
+ if( inEvent->data.resolved->address.addressType == kDNSNetworkAddressTypeIPv4 )
+ {
+ ServiceInstanceInfo * serviceInstance;
+ std::auto_ptr < ServiceInstanceInfo > serviceInstanceAutoPtr;
+ char s[ 32 ];
+
+ serviceInstance = new ServiceInstanceInfo;
+ serviceInstanceAutoPtr.reset( serviceInstance );
+
+ serviceInstance->name = inEvent->data.resolved->name;
+ serviceInstance->type = inEvent->data.resolved->type;
+ serviceInstance->domain = inEvent->data.resolved->domain;
+ serviceInstance->ip = DNSNetworkAddressToString( &inEvent->data.resolved->address, s );
+ serviceInstance->ifIP = DNSNetworkAddressToString( &inEvent->data.resolved->interfaceIP, s );
+ serviceInstance->text = inEvent->data.resolved->textRecord;
+ serviceInstance->hostName = inEvent->data.resolved->hostName;
+
+ posted = ::PostMessage( dialog->GetSafeHwnd(), WM_USER_RESOLVE, 0, (LPARAM) serviceInstance );
+ assert( posted );
+ if( posted )
+ {
+ serviceInstanceAutoPtr.release();
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ catch( ... )
+ {
+ // Don't let exceptions escape.
+ }
+}
+
+//===========================================================================================================================
+// DNSNetworkAddressToString
+//
+// Note: Currently only supports IPv4 network addresses.
+//===========================================================================================================================
+
+static char * DNSNetworkAddressToString( const DNSNetworkAddress *inAddr, char *outString )
+{
+ const DNSUInt8 * p;
+ DNSUInt16 port;
+
+ p = inAddr->u.ipv4.addr.v8;
+ port = ntohs( inAddr->u.ipv4.port.v16 );
+ if( port != kDNSPortInvalid )
+ {
+ sprintf( outString, "%u.%u.%u.%u:%u", p[ 0 ], p[ 1 ], p[ 2 ], p[ 3 ], port );
+ }
+ else
+ {
+ sprintf( outString, "%u.%u.%u.%u", p[ 0 ], p[ 1 ], p[ 2 ], p[ 3 ] );
+ }
+ return( outString );
+}
+
+//===========================================================================================================================
+// UTF8StringToStringObject
+//===========================================================================================================================
+
+static DWORD UTF8StringToStringObject( const char *inUTF8, CString &inObject )
+{
+ DWORD err;
+ int n;
+ BSTR unicode;
+
+ unicode = NULL;
+
+ n = MultiByteToWideChar( CP_UTF8, 0, inUTF8, -1, NULL, 0 );
+ if( n > 0 )
+ {
+ unicode = (BSTR) malloc( (size_t)( n * sizeof( wchar_t ) ) );
+ if( !unicode )
+ {
+ err = ERROR_INSUFFICIENT_BUFFER;
+ goto exit;
+ }
+
+ n = MultiByteToWideChar( CP_UTF8, 0, inUTF8, -1, unicode, n );
+ try
+ {
+ inObject = unicode;
+ }
+ catch( ... )
+ {
+ err = ERROR_NO_UNICODE_TRANSLATION;
+ goto exit;
+ }
+ }
+ else
+ {
+ inObject = "";
+ }
+ err = 0;
+
+exit:
+ if( unicode )
+ {
+ free( unicode );
+ }
+ return( err );
+}
+
+//===========================================================================================================================
+// StringObjectToUTF8String
+//===========================================================================================================================
+
+static DWORD StringObjectToUTF8String( CString &inObject, std::string &outUTF8 )
+{
+ DWORD err;
+ BSTR unicode;
+ int nUnicode;
+ int n;
+ char * utf8;
+
+ unicode = NULL;
+ utf8 = NULL;
+
+ nUnicode = inObject.GetLength();
+ if( nUnicode > 0 )
+ {
+ unicode = inObject.AllocSysString();
+ n = WideCharToMultiByte( CP_UTF8, 0, unicode, nUnicode, NULL, 0, NULL, NULL );
+ assert( n > 0 );
+
+ utf8 = (char *) malloc( (size_t) n );
+ assert( utf8 );
+ if( !utf8 ) { err = ERROR_INSUFFICIENT_BUFFER; goto exit; }
+
+ n = WideCharToMultiByte( CP_UTF8, 0, unicode, nUnicode, utf8, n, NULL, NULL );
+ assert( n > 0 );
+
+ try
+ {
+ outUTF8.assign( utf8, n );
+ }
+ catch( ... )
+ {
+ err = ERROR_NO_UNICODE_TRANSLATION;
+ goto exit;
+ }
+ }
+ else
+ {
+ outUTF8.clear();
+ }
+ err = 0;
+
+exit:
+ if( unicode )
+ {
+ SysFreeString( unicode );
+ }
+ if( utf8 )
+ {
+ free( utf8 );
+ }
+ return( err );
+}