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