1 /* -*- Mode: C; tab-width: 4 -*-
2 *
3 * Copyright (c) 2002-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 "Application.h"
21
22 #include "DNSServices.h"
23
24 #include "BrowserDialog.h"
25
26 #ifdef _DEBUG
27 #define new DEBUG_NEW
28 #undef THIS_FILE
29 static char THIS_FILE[] = __FILE__;
30 #endif
31
32 //===========================================================================================================================
33 // Constants
34 //===========================================================================================================================
35
36 #define WM_USER_SERVICE_ADD ( WM_USER + 0x100 )
37 #define WM_USER_SERVICE_REMOVE ( WM_USER + 0x101 )
38
39 //===========================================================================================================================
40 // Message Map
41 //===========================================================================================================================
42
43 BEGIN_MESSAGE_MAP(BrowserDialog, CDialog)
44 //{{AFX_MSG_MAP(BrowserDialog)
45 ON_NOTIFY(NM_CLICK, IDC_BROWSE_LIST, OnBrowserListDoubleClick)
46 ON_MESSAGE( WM_USER_SERVICE_ADD, OnServiceAdd )
47 ON_MESSAGE( WM_USER_SERVICE_REMOVE, OnServiceRemove )
48 //}}AFX_MSG_MAP
49 END_MESSAGE_MAP()
50
51 static DWORD UTF8StringToStringObject( const char *inUTF8, CString &inObject );
52
53 //===========================================================================================================================
54 // BrowserDialog
55 //===========================================================================================================================
56
BrowserDialog(CWnd * inParent)57 BrowserDialog::BrowserDialog( CWnd *inParent )
58 : CDialog( BrowserDialog::IDD, inParent )
59 {
60 //{{AFX_DATA_INIT(BrowserDialog)
61 // Note: the ClassWizard will add member initialization here
62 //}}AFX_DATA_INIT
63
64 // Note that LoadIcon does not require a subsequent DestroyIcon in Win32.
65
66 mIcon = AfxGetApp()->LoadIcon( IDR_MAINFRAME );
67 ASSERT( mIcon );
68 }
69
70 //===========================================================================================================================
71 // DoDataExchange
72 //===========================================================================================================================
73
DoDataExchange(CDataExchange * pDX)74 void BrowserDialog::DoDataExchange( CDataExchange *pDX )
75 {
76 CDialog::DoDataExchange(pDX);
77 //{{AFX_DATA_MAP(BrowserDialog)
78 DDX_Control(pDX, IDC_BROWSE_LIST, mBrowserList);
79 //}}AFX_DATA_MAP
80 }
81
82 //===========================================================================================================================
83 // OnInitDialog
84 //===========================================================================================================================
85
OnInitDialog()86 BOOL BrowserDialog::OnInitDialog()
87 {
88 CString s;
89
90 CDialog::OnInitDialog();
91
92 // Set the icon for this dialog. The framework does this automatically when the application's main window is not a dialog.
93
94 SetIcon( mIcon, TRUE ); // Set big icon
95 SetIcon( mIcon, FALSE ); // Set small icon
96
97 CenterWindow( GetDesktopWindow() );
98
99 // Set up the list.
100
101 CRect rect;
102
103 s.LoadString( IDS_BROWSER_LIST_COLUMN_NAME );
104 mBrowserList.GetWindowRect( rect );
105 mBrowserList.InsertColumn( 0, s, LVCFMT_LEFT, rect.Width() - 8 );
106
107 // Start browsing for services.
108
109 DNSStatus err;
110
111 err = DNSBrowserCreate( 0, OnBrowserCallBack, this, &mBrowser );
112 if( err )
113 {
114 AfxMessageBox( IDP_SOCKETS_INIT_FAILED );
115 goto exit;
116 }
117
118 err = DNSBrowserStartServiceSearch( mBrowser, kDNSBrowserFlagAutoResolve, "_http._tcp", NULL );
119 if( err )
120 {
121 AfxMessageBox( IDP_SOCKETS_INIT_FAILED );
122 goto exit;
123 }
124
125 exit:
126 return( TRUE );
127 }
128
129
130 //===========================================================================================================================
131 // OnBrowserListDoubleClick
132 //===========================================================================================================================
133
OnBrowserListDoubleClick(NMHDR * pNMHDR,LRESULT * pResult)134 void BrowserDialog::OnBrowserListDoubleClick( NMHDR *pNMHDR, LRESULT *pResult )
135 {
136 int selectedItem;
137
138 (void) pNMHDR; // Unused
139
140 selectedItem = mBrowserList.GetNextItem( -1, LVNI_SELECTED );
141 if( selectedItem >= 0 )
142 {
143 BrowserEntry * entry;
144 CString temp;
145 CString url;
146
147 // Build the URL from the IP and optional TXT record.
148
149 entry = &mBrowserEntries[ selectedItem ];
150 url += "http://" + entry->ip;
151 temp = entry->text;
152 if( temp.Find( TEXT( "path=" ) ) == 0 )
153 {
154 temp.Delete( 0, 5 );
155 }
156 if( temp.Find( '/' ) != 0 )
157 {
158 url += '/';
159 }
160 url += temp;
161
162 // Let the system open the URL in the correct app.
163
164 SHELLEXECUTEINFO info;
165
166 info.cbSize = sizeof( info );
167 info.fMask = 0;
168 info.hwnd = NULL;
169 info.lpVerb = NULL;
170 info.lpFile = url;
171 info.lpParameters = NULL;
172 info.lpDirectory = NULL;
173 info.nShow = SW_SHOWNORMAL;
174 info.hInstApp = NULL;
175
176 ShellExecuteEx( &info );
177 }
178 *pResult = 0;
179 }
180
181 //===========================================================================================================================
182 // OnBrowserCallBack [static]
183 //===========================================================================================================================
184
185 void
OnBrowserCallBack(void * inContext,DNSBrowserRef inRef,DNSStatus inStatusCode,const DNSBrowserEvent * inEvent)186 BrowserDialog::OnBrowserCallBack(
187 void * inContext,
188 DNSBrowserRef inRef,
189 DNSStatus inStatusCode,
190 const DNSBrowserEvent * inEvent )
191 {
192 BrowserDialog * dialog;
193 BrowserEntry * entry;
194 BOOL posted;
195
196 DNS_UNUSED( inStatusCode );
197 dialog = reinterpret_cast < BrowserDialog * > ( inContext );
198 ASSERT( dialog );
199
200 switch( inEvent->type )
201 {
202 case kDNSBrowserEventTypeResolved:
203 if( inEvent->data.resolved->address.addressType == kDNSNetworkAddressTypeIPv4 )
204 {
205 char ip[ 64 ];
206
207 sprintf( ip, "%u.%u.%u.%u:%u",
208 inEvent->data.resolved->address.u.ipv4.addr.v8[ 0 ],
209 inEvent->data.resolved->address.u.ipv4.addr.v8[ 1 ],
210 inEvent->data.resolved->address.u.ipv4.addr.v8[ 2 ],
211 inEvent->data.resolved->address.u.ipv4.addr.v8[ 3 ],
212 ( inEvent->data.resolved->address.u.ipv4.port.v8[ 0 ] << 8 ) |
213 inEvent->data.resolved->address.u.ipv4.port.v8[ 1 ] );
214
215 entry = new BrowserEntry;
216 ASSERT( entry );
217 if( entry )
218 {
219 UTF8StringToStringObject( inEvent->data.resolved->name, entry->name );
220 UTF8StringToStringObject( ip, entry->ip );
221 UTF8StringToStringObject( inEvent->data.resolved->textRecord, entry->text );
222
223 posted = ::PostMessage( dialog->GetSafeHwnd(), WM_USER_SERVICE_ADD, 0, (LPARAM) entry );
224 ASSERT( posted );
225 if( !posted )
226 {
227 delete entry;
228 }
229 }
230 }
231 break;
232
233 case kDNSBrowserEventTypeRemoveService:
234 entry = new BrowserEntry;
235 ASSERT( entry );
236 if( entry )
237 {
238 UTF8StringToStringObject( inEvent->data.removeService.name, entry->name );
239
240 posted = ::PostMessage( dialog->GetSafeHwnd(), WM_USER_SERVICE_REMOVE, 0, (LPARAM) entry );
241 ASSERT( posted );
242 if( !posted )
243 {
244 delete entry;
245 }
246 }
247 break;
248
249 default:
250 break;
251 }
252 }
253
254 //===========================================================================================================================
255 // BrowserAddService
256 //===========================================================================================================================
257
OnServiceAdd(WPARAM inWParam,LPARAM inLParam)258 LONG BrowserDialog::OnServiceAdd( WPARAM inWParam, LPARAM inLParam )
259 {
260 BrowserEntry * entry;
261 INT_PTR lo;
262 INT_PTR hi;
263 INT_PTR mid;
264 int result;
265
266 (void) inWParam; // Unused
267
268 entry = reinterpret_cast < BrowserEntry * > ( inLParam );
269 ASSERT( entry );
270
271 result = -1;
272 mid = 0;
273 lo = 0;
274 hi = mBrowserEntries.GetSize() - 1;
275 while( lo <= hi )
276 {
277 mid = ( lo + hi ) / 2;
278 result = entry->name.CompareNoCase( mBrowserEntries[ mid ].name );
279 if( result == 0 )
280 {
281 break;
282 }
283 else if( result < 0 )
284 {
285 hi = mid - 1;
286 }
287 else
288 {
289 lo = mid + 1;
290 }
291 }
292 if( result == 0 )
293 {
294 mBrowserEntries[ mid ].ip = entry->ip;
295 mBrowserEntries[ mid ].text = entry->text;
296 }
297 else
298 {
299 if( result > 0 )
300 {
301 mid += 1;
302 }
303 mBrowserEntries.InsertAt( mid, *entry );
304 mBrowserList.InsertItem( mid, entry->name );
305 }
306 delete entry;
307 return( 0 );
308 }
309
310 //===========================================================================================================================
311 // OnServiceRemove
312 //===========================================================================================================================
313
OnServiceRemove(WPARAM inWParam,LPARAM inLParam)314 LONG BrowserDialog::OnServiceRemove( WPARAM inWParam, LPARAM inLParam )
315 {
316 BrowserEntry * entry;
317 INT_PTR hi;
318 INT_PTR lo;
319 INT_PTR mid;
320 int result;
321
322 (void) inWParam; // Unused
323
324 entry = reinterpret_cast < BrowserEntry * > ( inLParam );
325 ASSERT( entry );
326
327 result = -1;
328 mid = 0;
329 lo = 0;
330 hi = mBrowserEntries.GetSize() - 1;
331 while( lo <= hi )
332 {
333 mid = ( lo + hi ) / 2;
334 result = entry->name.CompareNoCase( mBrowserEntries[ mid ].name );
335 if( result == 0 )
336 {
337 break;
338 }
339 else if( result < 0 )
340 {
341 hi = mid - 1;
342 }
343 else
344 {
345 lo = mid + 1;
346 }
347 }
348 if( result == 0 )
349 {
350 mBrowserList.DeleteItem( mid );
351 mBrowserEntries.RemoveAt( mid );
352 }
353 delete entry;
354 return( 0 );
355 }
356
357 #if 0
358 #pragma mark -
359 #endif
360
361 //===========================================================================================================================
362 // UTF8StringToStringObject
363 //===========================================================================================================================
364
UTF8StringToStringObject(const char * inUTF8,CString & inObject)365 static DWORD UTF8StringToStringObject( const char *inUTF8, CString &inObject )
366 {
367 DWORD err;
368 int n;
369 wchar_t * unicode;
370
371 unicode = NULL;
372
373 n = MultiByteToWideChar( CP_UTF8, 0, inUTF8, -1, NULL, 0 );
374 if( n > 0 )
375 {
376 unicode = (wchar_t *) malloc( (size_t)( n * sizeof( wchar_t ) ) );
377 if( !unicode ) { err = ERROR_INSUFFICIENT_BUFFER; goto exit; };
378
379 n = MultiByteToWideChar( CP_UTF8, 0, inUTF8, -1, unicode, n );
380 inObject = unicode;
381 }
382 else
383 {
384 inObject = "";
385 }
386 err = 0;
387
388 exit:
389 if( unicode )
390 {
391 free( unicode );
392 }
393 return( err );
394 }
395