Small changes
[wxWidgets.git] / src / msw / dialup.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: msw/dialup.cpp
3 // Purpose: MSW implementation of network/dialup classes and functions
4 // Author: Vadim Zeitlin
5 // Modified by:
6 // Created: 07.07.99
7 // RCS-ID: $Id$
8 // Copyright: (c) Vadim Zeitlin
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 // for compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
22
23 #ifdef __BORLANDC__
24 #pragma hdrstop
25 #endif
26
27 // these functions require Win32
28 #if defined(__WIN16__) && wxUSE_DIALUP_MANAGER
29 #undef wxUSE_DIALUP_MANAGER
30 #define wxUSE_DIALUP_MANAGER 0
31 #endif // wxUSE_DIALUP_MANAGER && Win16
32
33 #if wxUSE_DIALUP_MANAGER
34
35 #ifndef WX_PRECOMP
36 #include "wx/log.h"
37 #endif
38
39 #include "wx/dynlib.h"
40
41 #include "wx/dialup.h"
42
43 // Doesn't yet compile under BC++: no wininet.h
44 #ifndef __BORLANDC__
45
46 #include <ras.h>
47 #include <raserror.h>
48
49 #include <wininet.h>
50
51 #include "wx/msw/private.h"
52
53 // ----------------------------------------------------------------------------
54 // constants
55 // ----------------------------------------------------------------------------
56
57 // this message is sent by the secondary thread when RAS status changes
58 #define wxWM_RAS_STATUS_CHANGED (WM_USER + 10010)
59 #define wxWM_RAS_DIALING_PROGRESS (WM_USER + 10011)
60
61 // ----------------------------------------------------------------------------
62 // types
63 // ----------------------------------------------------------------------------
64
65 // the signatures of RAS functions: all this is quite heavy, but we must do it
66 // to allow running wxWin programs on machine which don't have RAS installed
67 // (this does exist) - if we link with rasapi32.lib, the program will fail on
68 // startup because of the missing DLL...
69
70 #ifndef UNICODE
71 typedef DWORD (APIENTRY * RASDIAL)( LPRASDIALEXTENSIONS, LPCSTR, LPRASDIALPARAMSA, DWORD, LPVOID, LPHRASCONN );
72 typedef DWORD (APIENTRY * RASENUMCONNECTIONS)( LPRASCONNA, LPDWORD, LPDWORD );
73 typedef DWORD (APIENTRY * RASENUMENTRIES)( LPCSTR, LPCSTR, LPRASENTRYNAMEA, LPDWORD, LPDWORD );
74 typedef DWORD (APIENTRY * RASGETCONNECTSTATUS)( HRASCONN, LPRASCONNSTATUSA );
75 typedef DWORD (APIENTRY * RASGETERRORSTRING)( UINT, LPSTR, DWORD );
76 typedef DWORD (APIENTRY * RASHANGUP)( HRASCONN );
77 typedef DWORD (APIENTRY * RASGETPROJECTIONINFO)( HRASCONN, RASPROJECTION, LPVOID, LPDWORD );
78 typedef DWORD (APIENTRY * RASCREATEPHONEBOOKENTRY)( HWND, LPCSTR );
79 typedef DWORD (APIENTRY * RASEDITPHONEBOOKENTRY)( HWND, LPCSTR, LPCSTR );
80 typedef DWORD (APIENTRY * RASSETENTRYDIALPARAMS)( LPCSTR, LPRASDIALPARAMSA, BOOL );
81 typedef DWORD (APIENTRY * RASGETENTRYDIALPARAMS)( LPCSTR, LPRASDIALPARAMSA, LPBOOL );
82 typedef DWORD (APIENTRY * RASENUMDEVICES)( LPRASDEVINFOA, LPDWORD, LPDWORD );
83 typedef DWORD (APIENTRY * RASGETCOUNTRYINFO)( LPRASCTRYINFOA, LPDWORD );
84 typedef DWORD (APIENTRY * RASGETENTRYPROPERTIES)( LPCSTR, LPCSTR, LPRASENTRYA, LPDWORD, LPBYTE, LPDWORD );
85 typedef DWORD (APIENTRY * RASSETENTRYPROPERTIES)( LPCSTR, LPCSTR, LPRASENTRYA, DWORD, LPBYTE, DWORD );
86 typedef DWORD (APIENTRY * RASRENAMEENTRY)( LPCSTR, LPCSTR, LPCSTR );
87 typedef DWORD (APIENTRY * RASDELETEENTRY)( LPCSTR, LPCSTR );
88 typedef DWORD (APIENTRY * RASVALIDATEENTRYNAME)( LPCSTR, LPCSTR );
89 typedef DWORD (APIENTRY * RASCONNECTIONNOTIFICATION)( HRASCONN, HANDLE, DWORD );
90
91 static const char gs_funcSuffix = 'A';
92 #else // Unicode
93 typedef DWORD (APIENTRY * RASDIAL)( LPRASDIALEXTENSIONS, LPCWSTR, LPRASDIALPARAMSW, DWORD, LPVOID, LPHRASCONN );
94 typedef DWORD (APIENTRY * RASENUMCONNECTIONS)( LPRASCONNW, LPDWORD, LPDWORD );
95 typedef DWORD (APIENTRY * RASENUMENTRIES)( LPCWSTR, LPCWSTR, LPRASENTRYNAMEW, LPDWORD, LPDWORD );
96 typedef DWORD (APIENTRY * RASGETCONNECTSTATUS)( HRASCONN, LPRASCONNSTATUSW );
97 typedef DWORD (APIENTRY * RASGETERRORSTRING)( UINT, LPWSTR, DWORD );
98 typedef DWORD (APIENTRY * RASHANGUP)( HRASCONN );
99 typedef DWORD (APIENTRY * RASGETPROJECTIONINFO)( HRASCONN, RASPROJECTION, LPVOID, LPDWORD );
100 typedef DWORD (APIENTRY * RASCREATEPHONEBOOKENTRY)( HWND, LPCWSTR );
101 typedef DWORD (APIENTRY * RASEDITPHONEBOOKENTRY)( HWND, LPCWSTR, LPCWSTR );
102 typedef DWORD (APIENTRY * RASSETENTRYDIALPARAMS)( LPCWSTR, LPRASDIALPARAMSW, BOOL );
103 typedef DWORD (APIENTRY * RASGETENTRYDIALPARAMS)( LPCWSTR, LPRASDIALPARAMSW, LPBOOL );
104 typedef DWORD (APIENTRY * RASENUMDEVICES)( LPRASDEVINFOW, LPDWORD, LPDWORD );
105 typedef DWORD (APIENTRY * RASGETCOUNTRYINFO)( LPRASCTRYINFOW, LPDWORD );
106 typedef DWORD (APIENTRY * RASGETENTRYPROPERTIES)( LPCWSTR, LPCWSTR, LPRASENTRYW, LPDWORD, LPBYTE, LPDWORD );
107 typedef DWORD (APIENTRY * RASSETENTRYPROPERTIES)( LPCWSTR, LPCWSTR, LPRASENTRYW, DWORD, LPBYTE, DWORD );
108 typedef DWORD (APIENTRY * RASRENAMEENTRY)( LPCWSTR, LPCWSTR, LPCWSTR );
109 typedef DWORD (APIENTRY * RASDELETEENTRY)( LPCWSTR, LPCWSTR );
110 typedef DWORD (APIENTRY * RASVALIDATEENTRYNAME)( LPCWSTR, LPCWSTR );
111 typedef DWORD (APIENTRY * RASCONNECTIONNOTIFICATION)( HRASCONN, HANDLE, DWORD );
112
113 static const char gs_funcSuffix = 'W';
114 #endif // ASCII/Unicode
115
116 // structure passed to the secondary thread
117 struct wxRasThreadData
118 {
119 wxRasThreadData()
120 {
121 hWnd = 0;
122 hEventRas = hEventQuit = INVALID_HANDLE_VALUE;
123 dialUpManager = NULL;
124 }
125
126 HWND hWnd; // window to send notifications to
127 HANDLE hEventRas, // event which RAS signals when status changes
128 hEventQuit; // event which we signal when we terminate
129
130 class WXDLLEXPORT wxDialUpManagerMSW *dialUpManager; // the owner
131 };
132
133 // ----------------------------------------------------------------------------
134 // wxDialUpManager class for MSW
135 // ----------------------------------------------------------------------------
136
137 class WXDLLEXPORT wxDialUpManagerMSW : public wxDialUpManager
138 {
139 public:
140 // ctor & dtor
141 wxDialUpManagerMSW();
142 virtual ~wxDialUpManagerMSW();
143
144 // implement base class pure virtuals
145 virtual bool IsOk() const;
146 virtual size_t GetISPNames(wxArrayString& names) const;
147 virtual bool Dial(const wxString& nameOfISP,
148 const wxString& username,
149 const wxString& password,
150 bool async);
151 virtual bool IsDialing() const;
152 virtual bool CancelDialing();
153 virtual bool HangUp();
154 virtual bool IsAlwaysOnline() const;
155 virtual bool IsOnline() const;
156 virtual void SetOnlineStatus(bool isOnline = TRUE);
157 virtual bool EnableAutoCheckOnlineStatus(size_t nSeconds);
158 virtual void DisableAutoCheckOnlineStatus();
159 virtual void SetWellKnownHost(const wxString& hostname, int port);
160 virtual void SetConnectCommand(const wxString& commandDial,
161 const wxString& commandHangup);
162
163 // for RasTimer
164 void CheckRasStatus();
165
166 // for wxRasStatusWindowProc
167 void OnConnectStatusChange();
168 void OnDialProgress(RASCONNSTATE rasconnstate, DWORD dwError);
169
170 // for wxRasDialFunc
171 static HWND GetRasWindow() { return ms_hwndRas; }
172 static wxDialUpManagerMSW *GetDialer() { return ms_dialer; }
173
174 private:
175 // return the error string for the given RAS error code
176 static wxString GetErrorString(DWORD error);
177
178 // find the (first) handle of the active connection
179 static HRASCONN FindActiveConnection();
180
181 // notify the application about status change
182 void NotifyApp(bool connected, bool fromOurselves = FALSE) const;
183
184 // destroy the thread data and the thread itself
185 void CleanUpThreadData();
186
187 // timer used for polling RAS status
188 class RasTimer : public wxTimer
189 {
190 public:
191 RasTimer(wxDialUpManagerMSW *dialUpManager)
192 { m_dialUpManager = dialUpManager; }
193
194 virtual void Notify() { m_dialUpManager->CheckRasStatus(); }
195
196 private:
197 wxDialUpManagerMSW *m_dialUpManager;
198 } m_timerStatusPolling;
199
200 // thread handle for the thread sitting on connection change event
201 HANDLE m_hThread;
202
203 // data used by this thread and our hidden window to send messages between
204 // each other
205 wxRasThreadData m_data;
206
207 // the hidden window we use for passing messages between threads
208 static HWND ms_hwndRas;
209
210 // the handle of the connection we initiated or 0 if none
211 static HRASCONN ms_hRasConnection;
212
213 // the use count of rasapi32.dll
214 static int ms_nDllCount;
215
216 // the handle of rasapi32.dll when it's loaded
217 static wxDllType ms_dllRas;
218
219 // the pointers to RAS functions
220 static RASDIAL ms_pfnRasDial;
221 static RASENUMCONNECTIONS ms_pfnRasEnumConnections;
222 static RASENUMENTRIES ms_pfnRasEnumEntries;
223 static RASGETCONNECTSTATUS ms_pfnRasGetConnectStatus;
224 static RASGETERRORSTRING ms_pfnRasGetErrorString;
225 static RASHANGUP ms_pfnRasHangUp;
226 static RASGETPROJECTIONINFO ms_pfnRasGetProjectionInfo;
227 static RASCREATEPHONEBOOKENTRY ms_pfnRasCreatePhonebookEntry;
228 static RASEDITPHONEBOOKENTRY ms_pfnRasEditPhonebookEntry;
229 static RASSETENTRYDIALPARAMS ms_pfnRasSetEntryDialParams;
230 static RASGETENTRYDIALPARAMS ms_pfnRasGetEntryDialParams;
231 static RASENUMDEVICES ms_pfnRasEnumDevices;
232 static RASGETCOUNTRYINFO ms_pfnRasGetCountryInfo;
233 static RASGETENTRYPROPERTIES ms_pfnRasGetEntryProperties;
234 static RASSETENTRYPROPERTIES ms_pfnRasSetEntryProperties;
235 static RASRENAMEENTRY ms_pfnRasRenameEntry;
236 static RASDELETEENTRY ms_pfnRasDeleteEntry;
237 static RASVALIDATEENTRYNAME ms_pfnRasValidateEntryName;
238
239 // this function is not supported by Win95
240 static RASCONNECTIONNOTIFICATION ms_pfnRasConnectionNotification;
241
242 // if this flag is different from -1, it overrides IsOnline()
243 static int ms_userSpecifiedOnlineStatus;
244
245 // this flag tells us if we're online
246 static int ms_isConnected;
247
248 // this flag is the result of the call to IsAlwaysOnline() (-1 if not
249 // called yet)
250 static int ms_isAlwaysOnline;
251
252 // this flag tells us whether a call to RasDial() is in progress
253 static wxDialUpManagerMSW *ms_dialer;
254 };
255
256 // ----------------------------------------------------------------------------
257 // private functions
258 // ----------------------------------------------------------------------------
259
260 static LRESULT WINAPI wxRasStatusWindowProc(HWND hWnd, UINT message,
261 WPARAM wParam, LPARAM lParam);
262
263 static DWORD wxRasMonitorThread(wxRasThreadData *data);
264
265 static void WINAPI wxRasDialFunc(UINT unMsg,
266 RASCONNSTATE rasconnstate,
267 DWORD dwError);
268
269 // ============================================================================
270 // implementation
271 // ============================================================================
272
273 // ----------------------------------------------------------------------------
274 // init the static variables
275 // ----------------------------------------------------------------------------
276
277 HRASCONN wxDialUpManagerMSW::ms_hRasConnection = 0;
278
279 HWND wxDialUpManagerMSW::ms_hwndRas = 0;
280
281 int wxDialUpManagerMSW::ms_nDllCount = 0;
282 wxDllType wxDialUpManagerMSW::ms_dllRas = 0;
283
284 RASDIAL wxDialUpManagerMSW::ms_pfnRasDial = 0;
285 RASENUMCONNECTIONS wxDialUpManagerMSW::ms_pfnRasEnumConnections = 0;
286 RASENUMENTRIES wxDialUpManagerMSW::ms_pfnRasEnumEntries = 0;
287 RASGETCONNECTSTATUS wxDialUpManagerMSW::ms_pfnRasGetConnectStatus = 0;
288 RASGETERRORSTRING wxDialUpManagerMSW::ms_pfnRasGetErrorString = 0;
289 RASHANGUP wxDialUpManagerMSW::ms_pfnRasHangUp = 0;
290 RASGETPROJECTIONINFO wxDialUpManagerMSW::ms_pfnRasGetProjectionInfo = 0;
291 RASCREATEPHONEBOOKENTRY wxDialUpManagerMSW::ms_pfnRasCreatePhonebookEntry = 0;
292 RASEDITPHONEBOOKENTRY wxDialUpManagerMSW::ms_pfnRasEditPhonebookEntry = 0;
293 RASSETENTRYDIALPARAMS wxDialUpManagerMSW::ms_pfnRasSetEntryDialParams = 0;
294 RASGETENTRYDIALPARAMS wxDialUpManagerMSW::ms_pfnRasGetEntryDialParams = 0;
295 RASENUMDEVICES wxDialUpManagerMSW::ms_pfnRasEnumDevices = 0;
296 RASGETCOUNTRYINFO wxDialUpManagerMSW::ms_pfnRasGetCountryInfo = 0;
297 RASGETENTRYPROPERTIES wxDialUpManagerMSW::ms_pfnRasGetEntryProperties = 0;
298 RASSETENTRYPROPERTIES wxDialUpManagerMSW::ms_pfnRasSetEntryProperties = 0;
299 RASRENAMEENTRY wxDialUpManagerMSW::ms_pfnRasRenameEntry = 0;
300 RASDELETEENTRY wxDialUpManagerMSW::ms_pfnRasDeleteEntry = 0;
301 RASVALIDATEENTRYNAME wxDialUpManagerMSW::ms_pfnRasValidateEntryName = 0;
302 RASCONNECTIONNOTIFICATION wxDialUpManagerMSW::ms_pfnRasConnectionNotification = 0;
303
304 int wxDialUpManagerMSW::ms_userSpecifiedOnlineStatus = -1;
305 int wxDialUpManagerMSW::ms_isConnected = -1;
306 int wxDialUpManagerMSW::ms_isAlwaysOnline = -1;
307 wxDialUpManagerMSW *wxDialUpManagerMSW::ms_dialer = NULL;
308
309 // ----------------------------------------------------------------------------
310 // ctor and dtor: the dynamic linking happens here
311 // ----------------------------------------------------------------------------
312
313 // the static creator function is implemented here
314 wxDialUpManager *wxDialUpManager::Create()
315 {
316 return new wxDialUpManagerMSW;
317 }
318
319 #ifdef __VISUALC__
320 // warning about "'this' : used in base member initializer list" - so what?
321 #pragma warning(disable:4355)
322 #endif // VC++
323
324 wxDialUpManagerMSW::wxDialUpManagerMSW()
325 : m_timerStatusPolling(this)
326 {
327 // initialize our data
328 m_hThread = 0;
329
330 if ( !ms_nDllCount++ )
331 {
332 // load the RAS library
333 ms_dllRas = wxDllLoader::LoadLibrary("RASAPI32");
334 if ( !ms_dllRas )
335 {
336 wxLogError(_("Dial up functions are unavailable because the "
337 "remote access service (RAS) is not installed "
338 "on this machine. Please install it."));
339 }
340 else
341 {
342 // resolve the functions we need
343
344 // this will contain the name of the function we failed to resolve
345 // if any at the end
346 const char *funcName = NULL;
347
348 // get the function from rasapi32.dll and abort if it's not found
349 #define RESOLVE_RAS_FUNCTION(type, name) \
350 ms_pfn##name = (type)wxDllLoader::GetSymbol(ms_dllRas, \
351 wxString(_T(#name)) + gs_funcSuffix); \
352 if ( !ms_pfn##name ) \
353 { \
354 funcName = #name; \
355 goto exit; \
356 }
357
358 // a variant of above macro which doesn't abort if the function is
359 // not found in the DLL
360 #define RESOLVE_OPTIONAL_RAS_FUNCTION(type, name) \
361 ms_pfn##name = (type)wxDllLoader::GetSymbol(ms_dllRas, \
362 wxString(_T(#name)) + gs_funcSuffix);
363
364 RESOLVE_RAS_FUNCTION(RASDIAL, RasDial);
365 RESOLVE_RAS_FUNCTION(RASENUMCONNECTIONS, RasEnumConnections);
366 RESOLVE_RAS_FUNCTION(RASENUMENTRIES, RasEnumEntries);
367 RESOLVE_RAS_FUNCTION(RASGETCONNECTSTATUS, RasGetConnectStatus);
368 RESOLVE_RAS_FUNCTION(RASGETERRORSTRING, RasGetErrorString);
369 RESOLVE_RAS_FUNCTION(RASHANGUP, RasHangUp);
370 RESOLVE_RAS_FUNCTION(RASGETENTRYDIALPARAMS, RasGetEntryDialParams);
371
372 // suppress wxDllLoader messages about missing (non essential)
373 // functions
374 {
375 wxLogNull noLog;
376
377 RESOLVE_OPTIONAL_RAS_FUNCTION(RASGETPROJECTIONINFO, RasGetProjectionInfo);
378 RESOLVE_OPTIONAL_RAS_FUNCTION(RASCREATEPHONEBOOKENTRY, RasCreatePhonebookEntry);
379 RESOLVE_OPTIONAL_RAS_FUNCTION(RASEDITPHONEBOOKENTRY, RasEditPhonebookEntry);
380 RESOLVE_OPTIONAL_RAS_FUNCTION(RASSETENTRYDIALPARAMS, RasSetEntryDialParams);
381 RESOLVE_OPTIONAL_RAS_FUNCTION(RASGETENTRYPROPERTIES, RasGetEntryProperties);
382 RESOLVE_OPTIONAL_RAS_FUNCTION(RASSETENTRYPROPERTIES, RasSetEntryProperties);
383 RESOLVE_OPTIONAL_RAS_FUNCTION(RASRENAMEENTRY, RasRenameEntry);
384 RESOLVE_OPTIONAL_RAS_FUNCTION(RASDELETEENTRY, RasDeleteEntry);
385 RESOLVE_OPTIONAL_RAS_FUNCTION(RASVALIDATEENTRYNAME, RasValidateEntryName);
386 RESOLVE_OPTIONAL_RAS_FUNCTION(RASGETCOUNTRYINFO, RasGetCountryInfo);
387 RESOLVE_OPTIONAL_RAS_FUNCTION(RASENUMDEVICES, RasEnumDevices);
388 RESOLVE_OPTIONAL_RAS_FUNCTION(RASCONNECTIONNOTIFICATION, RasConnectionNotification);
389 }
390
391 // keep your preprocessor name space clean
392 #undef RESOLVE_RAS_FUNCTION
393 #undef RESOLVE_OPTIONAL_RAS_FUNCTION
394
395 exit:
396 if ( funcName )
397 {
398 wxLogError(_("The version of remote access service (RAS) "
399 "installed on this machine is too old, please "
400 "upgrade (the following required function is "
401 "missing: %s)."), funcName);
402
403 wxDllLoader::UnloadLibrary(ms_dllRas);
404 ms_dllRas = 0;
405 ms_nDllCount = 0;
406
407 return;
408 }
409 }
410 }
411
412 // enable auto check by default
413 EnableAutoCheckOnlineStatus(0);
414 }
415
416 wxDialUpManagerMSW::~wxDialUpManagerMSW()
417 {
418 CleanUpThreadData();
419
420 if ( !--ms_nDllCount )
421 {
422 // unload the RAS library
423 wxDllLoader::UnloadLibrary(ms_dllRas);
424 ms_dllRas = 0;
425 }
426 }
427
428 // ----------------------------------------------------------------------------
429 // helper functions
430 // ----------------------------------------------------------------------------
431
432 wxString wxDialUpManagerMSW::GetErrorString(DWORD error)
433 {
434 wxChar buffer[512]; // this should be more than enough according to MS docs
435 DWORD dwRet = ms_pfnRasGetErrorString(error, buffer, WXSIZEOF(buffer));
436 switch ( dwRet )
437 {
438 case ERROR_INVALID_PARAMETER:
439 // this was a standard Win32 error probably
440 return wxString(wxSysErrorMsg(error));
441
442 default:
443 {
444 wxLogSysError(dwRet, _("Failed to retrieve text of RAS "
445 "error message"));
446
447 wxString msg;
448 msg.Printf(_("unknown error (error code %08x)."), error);
449 return msg;
450 }
451
452 case 0:
453 // we want the error message to start from a lower case letter
454 buffer[0] = wxTolower(buffer[0]);
455
456 return wxString(buffer);
457 }
458 }
459
460 HRASCONN wxDialUpManagerMSW::FindActiveConnection()
461 {
462 // enumerate connections
463 DWORD cbBuf = sizeof(RASCONN);
464 LPRASCONN lpRasConn = (LPRASCONN)malloc(cbBuf);
465 if ( !lpRasConn )
466 {
467 // out of memory
468 return 0;
469 }
470
471 lpRasConn->dwSize = sizeof(RASCONN);
472
473 DWORD nConnections = 0;
474 DWORD dwRet = ERROR_BUFFER_TOO_SMALL;
475
476 while ( dwRet == ERROR_BUFFER_TOO_SMALL )
477 {
478 dwRet = ms_pfnRasEnumConnections(lpRasConn, &cbBuf, &nConnections);
479
480 if ( dwRet == ERROR_BUFFER_TOO_SMALL )
481 {
482 LPRASCONN lpRasConnOld = lpRasConn;
483 lpRasConn = (LPRASCONN)realloc(lpRasConn, cbBuf);
484 if ( !lpRasConn )
485 {
486 // out of memory
487 free(lpRasConnOld);
488
489 return 0;
490 }
491 }
492 else if ( dwRet == 0 )
493 {
494 // ok, success
495 break;
496 }
497 else
498 {
499 // an error occured
500 wxLogError(_("Cannot find active dialup connection: %s"),
501 GetErrorString(dwRet));
502 return 0;
503 }
504 }
505
506 HRASCONN hrasconn;
507
508 switch ( nConnections )
509 {
510 case 0:
511 // no connections
512 hrasconn = 0;
513 break;
514
515 default:
516 // more than 1 connection - we don't know what to do with this
517 // case, so give a warning but continue (taking the first
518 // connection) - the warning is really needed because this function
519 // is used, for example, to select the connection to hang up and so
520 // we may hang up the wrong connection here...
521 wxLogWarning(_("Several active dialup connections found, "
522 "choosing one randomly."));
523 // fall through
524
525 case 1:
526 // exactly 1 connection, great
527 hrasconn = lpRasConn->hrasconn;
528 }
529
530 free(lpRasConn);
531
532 return hrasconn;
533 }
534
535 void wxDialUpManagerMSW::CleanUpThreadData()
536 {
537 if ( m_hThread )
538 {
539 if ( !SetEvent(m_data.hEventQuit) )
540 {
541 wxLogLastError("SetEvent(RasThreadQuit)");
542 }
543
544 CloseHandle(m_hThread);
545
546 m_hThread = 0;
547 }
548
549 if ( m_data.hWnd )
550 {
551 DestroyWindow(m_data.hWnd);
552
553 m_data.hWnd = 0;
554 }
555
556 if ( m_data.hEventQuit )
557 {
558 CloseHandle(m_data.hEventQuit);
559
560 m_data.hEventQuit = 0;
561 }
562
563 if ( m_data.hEventRas )
564 {
565 CloseHandle(m_data.hEventRas);
566
567 m_data.hEventRas = 0;
568 }
569 }
570
571 // ----------------------------------------------------------------------------
572 // connection status
573 // ----------------------------------------------------------------------------
574
575 void wxDialUpManagerMSW::CheckRasStatus()
576 {
577 // use int, not bool to compare with -1
578 int isConnected = FindActiveConnection() != 0;
579 if ( isConnected != ms_isConnected )
580 {
581 if ( ms_isConnected != -1 )
582 {
583 // notify the program
584 NotifyApp(isConnected != 0);
585 }
586 // else: it's the first time we're called, just update the flag
587
588 ms_isConnected = isConnected;
589 }
590 }
591
592 void wxDialUpManagerMSW::NotifyApp(bool connected, bool fromOurselves) const
593 {
594 wxDialUpEvent event(connected, fromOurselves);
595 (void)wxTheApp->ProcessEvent(event);
596 }
597
598 // this function is called whenever the status of any RAS connection on this
599 // machine changes by RAS itself
600 void wxDialUpManagerMSW::OnConnectStatusChange()
601 {
602 // we know that status changed, but we don't know whether we're connected
603 // or not - so find it out
604 CheckRasStatus();
605 }
606
607 // this function is called by our callback which we give to RasDial() when
608 // calling it asynchronously
609 void wxDialUpManagerMSW::OnDialProgress(RASCONNSTATE rasconnstate,
610 DWORD dwError)
611 {
612 if ( !GetDialer() )
613 {
614 // this probably means that CancelDialing() was called and we get
615 // "disconnected" notification
616 return;
617 }
618
619 // we're only interested in 2 events: connected and disconnected
620 if ( dwError )
621 {
622 wxLogError(_("Failed to establish dialup connection: %s"),
623 GetErrorString(dwError));
624
625 // we should still call RasHangUp() if we got a non 0 connection
626 if ( ms_hRasConnection )
627 {
628 ms_pfnRasHangUp(ms_hRasConnection);
629 ms_hRasConnection = 0;
630 }
631
632 ms_dialer = NULL;
633
634 NotifyApp(FALSE /* !connected */, TRUE /* we dialed ourselves */);
635 }
636 else if ( rasconnstate == RASCS_Connected )
637 {
638 ms_isConnected = TRUE;
639 ms_dialer = NULL;
640
641 NotifyApp(TRUE /* connected */, TRUE /* we dialed ourselves */);
642 }
643 }
644
645 // ----------------------------------------------------------------------------
646 // implementation of wxDialUpManager functions
647 // ----------------------------------------------------------------------------
648
649 bool wxDialUpManagerMSW::IsOk() const
650 {
651 return ms_dllRas != 0;
652 }
653
654 size_t wxDialUpManagerMSW::GetISPNames(wxArrayString& names) const
655 {
656 // fetch the entries
657 DWORD size = sizeof(RASENTRYNAME);
658 RASENTRYNAME *rasEntries = (RASENTRYNAME *)malloc(size);
659 rasEntries->dwSize = sizeof(RASENTRYNAME);
660
661 DWORD nEntries;
662 DWORD dwRet;
663 do
664 {
665 dwRet = ms_pfnRasEnumEntries
666 (
667 NULL, // reserved
668 NULL, // default phone book (or all)
669 rasEntries, // [out] buffer for the entries
670 &size, // [in/out] size of the buffer
671 &nEntries // [out] number of entries fetched
672 );
673
674 if ( dwRet == ERROR_BUFFER_TOO_SMALL )
675 {
676 // reallocate the buffer
677 rasEntries = (RASENTRYNAME *)realloc(rasEntries, size);
678 }
679 else if ( dwRet != 0 )
680 {
681 // some other error - abort
682 wxLogError(_("Failed to get ISP names: %s"), GetErrorString(dwRet));
683
684 free(rasEntries);
685
686 return 0u;
687 }
688 }
689 while ( dwRet != 0 );
690
691 // process them
692 names.Empty();
693 for ( size_t n = 0; n < (size_t)nEntries; n++ )
694 {
695 names.Add(rasEntries[n].szEntryName);
696 }
697
698 free(rasEntries);
699
700 // return the number of entries
701 return names.GetCount();
702 }
703
704 bool wxDialUpManagerMSW::Dial(const wxString& nameOfISP,
705 const wxString& username,
706 const wxString& password,
707 bool async)
708 {
709 // check preconditions
710 wxCHECK_MSG( IsOk(), FALSE, wxT("using uninitialized wxDialUpManager") );
711
712 if ( ms_hRasConnection )
713 {
714 wxFAIL_MSG(wxT("there is already an active connection"));
715
716 return TRUE;
717 }
718
719 // get the default ISP if none given
720 wxString entryName(nameOfISP);
721 if ( !entryName )
722 {
723 wxArrayString names;
724 size_t count = GetISPNames(names);
725 switch ( count )
726 {
727 case 0:
728 // no known ISPs, abort
729 wxLogError(_("Failed to connect: no ISP to dial."));
730
731 return FALSE;
732
733 case 1:
734 // only one ISP, choose it
735 entryName = names[0u];
736 break;
737
738 default:
739 // several ISPs, let the user choose
740 {
741 wxString *strings = new wxString[count];
742 for ( size_t i = 0; i < count; i++ )
743 {
744 strings[i] = names[i];
745 }
746
747 entryName = wxGetSingleChoice
748 (
749 _("Choose ISP to dial"),
750 _("Please choose which ISP do you want to "
751 "connect to"),
752 count,
753 strings
754 );
755
756 delete [] strings;
757
758 if ( !entryName )
759 {
760 // cancelled by user
761 return FALSE;
762 }
763 }
764 }
765 }
766
767 RASDIALPARAMS rasDialParams;
768 rasDialParams.dwSize = sizeof(rasDialParams);
769 strncpy(rasDialParams.szEntryName, entryName, RAS_MaxEntryName);
770
771 // do we have the username and password?
772 if ( !username || !password )
773 {
774 BOOL gotPassword;
775 DWORD dwRet = ms_pfnRasGetEntryDialParams
776 (
777 NULL, // default phonebook
778 &rasDialParams, // [in/out] the params of this entry
779 &gotPassword // [out] did we get password?
780 );
781
782 if ( dwRet != 0 )
783 {
784 wxLogError(_("Failed to connect: missing username/password."));
785
786 return FALSE;
787 }
788 }
789 else
790 {
791 strncpy(rasDialParams.szUserName, username, UNLEN);
792 strncpy(rasDialParams.szPassword, password, PWLEN);
793 }
794
795 // default values for other fields
796 rasDialParams.szPhoneNumber[0] = '\0';
797 rasDialParams.szCallbackNumber[0] = '\0';
798 rasDialParams.szCallbackNumber[0] = '\0';
799
800 rasDialParams.szDomain[0] = '*';
801 rasDialParams.szDomain[1] = '\0';
802
803 // apparently, this is not really necessary - passing NULL instead of the
804 // phone book has the same effect
805 #if 0
806 wxString phoneBook;
807 if ( wxGetOsVersion() == wxWINDOWS_NT )
808 {
809 // first get the length
810 UINT nLen = ::GetSystemDirectory(NULL, 0);
811 nLen++;
812
813 if ( !::GetSystemDirectory(phoneBook.GetWriteBuf(nLen), nLen) )
814 {
815 wxLogSysError(_("Cannot find the location of address book file"));
816 }
817
818 phoneBook.UngetWriteBuf();
819
820 // this is the default phone book
821 phoneBook << "\\ras\\rasphone.pbk";
822 }
823 #endif // 0
824
825 // TODO may be we should disable auto check while async dialing is in
826 // progress?
827
828 ms_dialer = this;
829
830 DWORD dwRet = ms_pfnRasDial
831 (
832 (LPRASDIALEXTENSIONS)NULL, // no extended features
833 NULL, // default phone book file (NT only)
834 &rasDialParams,
835 0, // use callback for notifications
836 async ? wxRasDialFunc // the callback
837 : 0, // no notifications - sync operation
838 &ms_hRasConnection
839 );
840
841 if ( dwRet != 0 )
842 {
843 wxLogError(_("Failed to %s dialup connection: %s"),
844 async ? _("initiate") : _("establish"),
845 GetErrorString(dwRet));
846
847 // we should still call RasHangUp() if we got a non 0 connection
848 if ( ms_hRasConnection )
849 {
850 ms_pfnRasHangUp(ms_hRasConnection);
851 ms_hRasConnection = 0;
852 }
853
854 ms_dialer = NULL;
855
856 return FALSE;
857 }
858
859 // for async dialing, we're not yet connected
860 if ( !async )
861 {
862 ms_isConnected = TRUE;
863 }
864
865 return TRUE;
866 }
867
868 bool wxDialUpManagerMSW::IsDialing() const
869 {
870 return GetDialer() != NULL;
871 }
872
873 bool wxDialUpManagerMSW::CancelDialing()
874 {
875 if ( !GetDialer() )
876 {
877 // silently ignore
878 return FALSE;
879 }
880
881 wxASSERT_MSG( ms_hRasConnection, wxT("dialing but no connection?") );
882
883 ms_dialer = NULL;
884
885 return HangUp();
886 }
887
888 bool wxDialUpManagerMSW::HangUp()
889 {
890 wxCHECK_MSG( IsOk(), FALSE, wxT("using uninitialized wxDialUpManager") );
891
892 // we may terminate either the connection we initiated or another one which
893 // is active now
894 HRASCONN hRasConn;
895 if ( ms_hRasConnection )
896 {
897 hRasConn = ms_hRasConnection;
898
899 ms_hRasConnection = 0;
900 }
901 else
902 {
903 hRasConn = FindActiveConnection();
904 }
905
906 if ( !hRasConn )
907 {
908 wxLogError(_("Cannot hang up - no active dialup connection."));
909
910 return FALSE;
911 }
912
913 DWORD dwRet = ms_pfnRasHangUp(hRasConn);
914 if ( dwRet != 0 )
915 {
916 wxLogError(_("Failed to terminate the dialup connection: %s"),
917 GetErrorString(dwRet));
918 }
919
920 ms_isConnected = FALSE;
921
922 return TRUE;
923 }
924
925 bool wxDialUpManagerMSW::IsAlwaysOnline() const
926 {
927 // we cache the result (presumably this won't change while the program is
928 // running!)
929 if ( ms_isAlwaysOnline != -1 )
930 {
931 return ms_isAlwaysOnline != 0;
932 }
933
934 // try to use WinInet function first
935 bool ok;
936 wxDllType hDll = wxDllLoader::LoadLibrary(_T("WININET"), &ok);
937 if ( ok )
938 {
939 typedef BOOL (*INTERNETGETCONNECTEDSTATE)(LPDWORD, DWORD);
940 INTERNETGETCONNECTEDSTATE pfnInternetGetConnectedState;
941
942 #define RESOLVE_FUNCTION(type, name) \
943 pfn##name = (type)wxDllLoader::GetSymbol(hDll, _T(#name))
944
945 RESOLVE_FUNCTION(INTERNETGETCONNECTEDSTATE, InternetGetConnectedState);
946
947 if ( pfnInternetGetConnectedState )
948 {
949 DWORD flags = 0;
950 if ( pfnInternetGetConnectedState(&flags, 0 /* reserved */) )
951 {
952 // there is some connection to the net, see of which type
953 ms_isAlwaysOnline = (flags & INTERNET_CONNECTION_LAN != 0) ||
954 (flags & INTERNET_CONNECTION_PROXY != 0);
955
956 wxLogMessage("InternetGetConnectedState() returned TRUE, "
957 "flags = %08x", flags);
958 }
959 else
960 {
961 // no Internet connection at all
962 ms_isAlwaysOnline = FALSE;
963 }
964 }
965
966 wxDllLoader::UnloadLibrary(hDll);
967 }
968
969 // did we succeed with WinInet? if not, try something else
970 if ( ms_isAlwaysOnline == -1 )
971 {
972 if ( !IsOnline() )
973 {
974 // definitely no permanent connection because we are not connected
975 // now
976 ms_isAlwaysOnline = FALSE;
977 }
978 else
979 {
980 // of course, having a modem doesn't prevent us from having a
981 // permanent connection as well, but we have to guess somehow and
982 // it's probably more common that a system connected via a modem
983 // doesn't have any other net access, so:
984 ms_isAlwaysOnline = FALSE;
985 }
986 }
987
988 wxASSERT_MSG( ms_isAlwaysOnline != -1, wxT("logic error") );
989
990 return ms_isAlwaysOnline != 0;
991 }
992
993 bool wxDialUpManagerMSW::IsOnline() const
994 {
995 wxCHECK_MSG( IsOk(), FALSE, wxT("using uninitialized wxDialUpManager") );
996
997 if ( ms_userSpecifiedOnlineStatus != -1 )
998 {
999 // user specified flag overrides our logic
1000 return ms_userSpecifiedOnlineStatus != 0;
1001 }
1002 else
1003 {
1004 // return TRUE if there is at least one active connection
1005 return FindActiveConnection() != 0;
1006 }
1007 }
1008
1009 void wxDialUpManagerMSW::SetOnlineStatus(bool isOnline)
1010 {
1011 wxCHECK_RET( IsOk(), wxT("using uninitialized wxDialUpManager") );
1012
1013 ms_userSpecifiedOnlineStatus = isOnline;
1014 }
1015
1016 bool wxDialUpManagerMSW::EnableAutoCheckOnlineStatus(size_t nSeconds)
1017 {
1018 wxCHECK_MSG( IsOk(), FALSE, wxT("using uninitialized wxDialUpManager") );
1019
1020 bool ok = ms_pfnRasConnectionNotification != 0;
1021
1022 if ( ok )
1023 {
1024 // we're running under NT 4.0, Windows 98 or later and can use
1025 // RasConnectionNotification() to be notified by a secondary thread
1026
1027 // first, see if we don't have this thread already running
1028 if ( m_hThread != 0 )
1029 {
1030 DWORD dwSuspendCount = 2;
1031 while ( dwSuspendCount > 1 )
1032 {
1033 dwSuspendCount = ResumeThread(m_hThread);
1034 if ( dwSuspendCount == (DWORD)-1 )
1035 {
1036 wxLogLastError("ResumeThread(RasThread)");
1037
1038 ok = FALSE;
1039 }
1040 }
1041
1042 if ( ok )
1043 {
1044 return TRUE;
1045 }
1046 }
1047 }
1048
1049 // create all the stuff we need to be notified about RAS connection
1050 // status change
1051
1052 if ( ok )
1053 {
1054 // first create an event to wait on
1055 m_data.hEventRas = CreateEvent
1056 (
1057 NULL, // security attribute (default)
1058 FALSE, // manual reset (not)
1059 FALSE, // initial state (not signaled)
1060 NULL // name (no)
1061 );
1062 if ( !m_data.hEventRas )
1063 {
1064 wxLogLastError("CreateEvent(RasStatus)");
1065
1066 ok = FALSE;
1067 }
1068 }
1069
1070 if ( ok )
1071 {
1072 // create the event we use to quit the thread
1073 m_data.hEventQuit = CreateEvent(NULL, FALSE, FALSE, NULL);
1074 if ( !m_data.hEventQuit )
1075 {
1076 wxLogLastError("CreateEvent(RasThreadQuit)");
1077
1078 CleanUpThreadData();
1079
1080 ok = FALSE;
1081 }
1082 }
1083
1084 if ( ok && !ms_hwndRas )
1085 {
1086 // create a hidden window to receive notification about connections
1087 // status change
1088 extern wxChar wxPanelClassName[];
1089 ms_hwndRas = ::CreateWindow(wxPanelClassName, NULL,
1090 0, 0, 0, 0,
1091 0, NULL,
1092 (HMENU)NULL, wxGetInstance(), 0);
1093 if ( !ms_hwndRas )
1094 {
1095 wxLogLastError("CreateWindow(RasHiddenWindow)");
1096
1097 CleanUpThreadData();
1098
1099 ok = FALSE;
1100 }
1101
1102 // and subclass it
1103 FARPROC windowProc = MakeProcInstance
1104 (
1105 (FARPROC)wxRasStatusWindowProc,
1106 wxGetInstance()
1107 );
1108
1109 ::SetWindowLong(ms_hwndRas, GWL_WNDPROC, (LONG) windowProc);
1110 }
1111
1112 m_data.hWnd = ms_hwndRas;
1113
1114 if ( ok )
1115 {
1116 // start the secondary thread
1117 m_data.dialUpManager = this;
1118
1119 DWORD tid;
1120 m_hThread = CreateThread
1121 (
1122 NULL,
1123 0,
1124 (LPTHREAD_START_ROUTINE)wxRasMonitorThread,
1125 (void *)&m_data,
1126 0,
1127 &tid
1128 );
1129
1130 if ( !m_hThread )
1131 {
1132 wxLogLastError("CreateThread(RasStatusThread)");
1133
1134 CleanUpThreadData();
1135 }
1136 }
1137
1138 if ( ok )
1139 {
1140 // start receiving RAS notifications
1141 DWORD dwRet = ms_pfnRasConnectionNotification
1142 (
1143 (HRASCONN)INVALID_HANDLE_VALUE,
1144 m_data.hEventRas,
1145 3 /* RASCN_Connection | RASCN_Disconnection */
1146 );
1147
1148 if ( dwRet != 0 )
1149 {
1150 wxLogDebug(wxT("RasConnectionNotification() failed: %s"),
1151 GetErrorString(dwRet));
1152
1153 CleanUpThreadData();
1154 }
1155 else
1156 {
1157 return TRUE;
1158 }
1159 }
1160
1161 // we're running under Windows 95 and have to poll ourselves
1162 // (or, alternatively, the code above for NT/98 failed)
1163 m_timerStatusPolling.Stop();
1164 if ( nSeconds == 0 )
1165 {
1166 // default value
1167 nSeconds = 60;
1168 }
1169 m_timerStatusPolling.Start(nSeconds * 1000);
1170
1171 return TRUE;
1172 }
1173
1174 void wxDialUpManagerMSW::DisableAutoCheckOnlineStatus()
1175 {
1176 wxCHECK_RET( IsOk(), wxT("using uninitialized wxDialUpManager") );
1177
1178 if ( m_hThread )
1179 {
1180 // we have running secondary thread, it's just enough to suspend it
1181 if ( SuspendThread(m_hThread) == (DWORD)-1 )
1182 {
1183 wxLogLastError("SuspendThread(RasThread)");
1184 }
1185 }
1186 else
1187 {
1188 // even simpler - just stop the timer
1189 m_timerStatusPolling.Stop();
1190 }
1191 }
1192
1193 // ----------------------------------------------------------------------------
1194 // stubs which don't do anything in MSW version
1195 // ----------------------------------------------------------------------------
1196
1197 void wxDialUpManagerMSW::SetWellKnownHost(const wxString& WXUNUSED(hostname),
1198 int WXUNUSED(port))
1199 {
1200 wxCHECK_RET( IsOk(), wxT("using uninitialized wxDialUpManager") );
1201
1202 // nothing to do - we don't use this
1203 }
1204
1205 void wxDialUpManagerMSW::SetConnectCommand(const wxString& WXUNUSED(dial),
1206 const wxString& WXUNUSED(hangup))
1207 {
1208 wxCHECK_RET( IsOk(), wxT("using uninitialized wxDialUpManager") );
1209
1210 // nothing to do - we don't use this
1211 }
1212
1213 // ----------------------------------------------------------------------------
1214 // callbacks
1215 // ----------------------------------------------------------------------------
1216
1217 static DWORD wxRasMonitorThread(wxRasThreadData *data)
1218 {
1219 HANDLE handles[2];
1220 handles[0] = data->hEventRas;
1221 handles[1] = data->hEventQuit;
1222
1223 bool cont = TRUE;
1224 while ( cont )
1225 {
1226 DWORD dwRet = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
1227
1228 switch ( dwRet )
1229 {
1230 case WAIT_OBJECT_0:
1231 // RAS connection status changed
1232 SendMessage(data->hWnd, wxWM_RAS_STATUS_CHANGED,
1233 0, (LPARAM)data);
1234 break;
1235
1236 case WAIT_OBJECT_0 + 1:
1237 cont = FALSE;
1238 break;
1239
1240 case WAIT_FAILED:
1241 wxLogLastError("WaitForMultipleObjects(RasMonitor)");
1242 break;
1243 }
1244 }
1245
1246 return 0;
1247 }
1248
1249 static LRESULT APIENTRY wxRasStatusWindowProc(HWND hWnd, UINT message,
1250 WPARAM wParam, LPARAM lParam)
1251 {
1252 if ( message == wxWM_RAS_STATUS_CHANGED )
1253 {
1254 wxRasThreadData *data = (wxRasThreadData *)lParam;
1255 data->dialUpManager->OnConnectStatusChange();
1256 }
1257 else if ( message == wxWM_RAS_DIALING_PROGRESS )
1258 {
1259 wxDialUpManagerMSW *dialUpManager = wxDialUpManagerMSW::GetDialer();
1260
1261 dialUpManager->OnDialProgress((RASCONNSTATE)wParam, lParam);
1262 }
1263
1264 return 0;
1265 }
1266
1267 static void WINAPI wxRasDialFunc(UINT unMsg,
1268 RASCONNSTATE rasconnstate,
1269 DWORD dwError)
1270 {
1271 wxDialUpManagerMSW *dialUpManager = wxDialUpManagerMSW::GetDialer();
1272
1273 wxCHECK_RET( dialUpManager, wxT("who started to dial then?") );
1274
1275 SendMessage(dialUpManager->GetRasWindow(), wxWM_RAS_DIALING_PROGRESS,
1276 rasconnstate, dwError);
1277 }
1278
1279 #endif
1280 // __BORLANDC__
1281 #endif // wxUSE_DIALUP_MANAGER