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