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