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