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