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