]>
Commit | Line | Data |
---|---|---|
1 | // -*- c++ -*- /////////////////////////////////////////////////////////////// | |
2 | // Name: unix/net.cpp | |
3 | // Purpose: Network related wxWindows classes and functions | |
4 | // Author: Karsten Ballüder | |
5 | // Modified by: | |
6 | // Created: 03.10.99 | |
7 | // RCS-ID: $Id$ | |
8 | // Copyright: (c) Karsten Ballüder | |
9 | // Licence: wxWindows licence | |
10 | ///////////////////////////////////////////////////////////////////////////// | |
11 | ||
12 | #include "wx/setup.h" | |
13 | ||
14 | #if wxUSE_DIALUP_MANAGER | |
15 | ||
16 | #ifndef WX_PRECOMP | |
17 | # include "wx/defs.h" | |
18 | #endif // !PCH | |
19 | ||
20 | #include "wx/string.h" | |
21 | #include "wx/event.h" | |
22 | #include "wx/net.h" | |
23 | #include "wx/timer.h" | |
24 | #include "wx/filename.h" | |
25 | #include "wx/utils.h" | |
26 | #include "wx/log.h" | |
27 | #include "wx/file.h" | |
28 | ||
29 | #include <stdlib.h> | |
30 | #include <signal.h> | |
31 | #include <fcntl.h> | |
32 | #include <unistd.h> | |
33 | #define __STRICT_ANSI__ | |
34 | #include <sys/socket.h> | |
35 | #include <sys/types.h> | |
36 | #include <sys/stat.h> | |
37 | #include <netdb.h> | |
38 | #include <netinet/in.h> | |
39 | #include <arpa/inet.h> | |
40 | ||
41 | // ---------------------------------------------------------------------------- | |
42 | // A class which groups functions dealing with connecting to the network from a | |
43 | // workstation using dial-up access to the net. There is at most one instance | |
44 | // of this class in the program accessed via GetDialUpManager(). | |
45 | // ---------------------------------------------------------------------------- | |
46 | ||
47 | /* TODO | |
48 | * | |
49 | * 1. more configurability for Unix: i.e. how to initiate the connection, how | |
50 | * to check for online status, &c. | |
51 | * 2. add a "long Dial(long connectionId = -1)" function which asks the user | |
52 | * about which connection to dial (this may be done using native dialogs | |
53 | * under NT, need generic dialogs for all others) and returns the identifier | |
54 | * of the selected connection (it's opaque to the application) - it may be | |
55 | * reused later to dial the same connection later (or use strings instead of | |
56 | * longs may be?) | |
57 | * 3. add an async version of dialing functions which notify the caller about | |
58 | * the progress (or may be even start another thread to monitor it) | |
59 | * 4. the static creation/accessor functions are not MT-safe - but is this | |
60 | * really crucial? I think we may suppose they're always called from the | |
61 | * main thread? | |
62 | */ | |
63 | ||
64 | class WXDLLEXPORT wxDialUpManagerImpl : public wxDialUpManager | |
65 | { | |
66 | public: | |
67 | wxDialUpManagerImpl() | |
68 | { | |
69 | m_IsOnline = -1; // unknown | |
70 | m_timer = NULL; | |
71 | m_CanUseIfconfig = -1; // unknown | |
72 | m_BeaconHost = WXDIALUP_MANAGER_DEFAULT_BEACONHOST; | |
73 | m_BeaconPort = 80; | |
74 | } | |
75 | ||
76 | /** Could the dialup manager be initialized correctly? If this function | |
77 | returns FALSE, no other functions will work neither, so it's a good idea | |
78 | to call this function and check its result before calling any other | |
79 | wxDialUpManager methods. | |
80 | */ | |
81 | virtual bool IsOk() const | |
82 | { return TRUE; } | |
83 | ||
84 | /** The simplest way to initiate a dial up: this function dials the given | |
85 | ISP (exact meaning of the parameter depends on the platform), returns | |
86 | TRUE on success or FALSE on failure and logs the appropriate error | |
87 | message in the latter case. | |
88 | @param nameOfISP optional paramater for dial program | |
89 | @param username unused | |
90 | @param password unused | |
91 | */ | |
92 | virtual bool Dial(const wxString& nameOfISP, | |
93 | const wxString& WXUNUSED(username), | |
94 | const wxString& WXUNUSED(password)); | |
95 | ||
96 | /// Hang up the currently active dial up connection. | |
97 | virtual bool HangUp(); | |
98 | ||
99 | // returns TRUE if the computer is connected to the network: under Windows, | |
100 | // this just means that a RAS connection exists, under Unix we check that | |
101 | // the "well-known host" (as specified by SetWellKnownHost) is reachable | |
102 | virtual bool IsOnline() const | |
103 | { | |
104 | if( (! m_timer) // we are not polling, so test now: | |
105 | || m_IsOnline == -1 | |
106 | ) | |
107 | CheckStatus(); | |
108 | return m_IsOnline != 0; | |
109 | } | |
110 | ||
111 | // sometimes the built-in logic for determining the online status may fail, | |
112 | // so, in general, the user should be allowed to override it. This function | |
113 | // allows to forcefully set the online status - whatever our internal | |
114 | // algorithm may think about it. | |
115 | virtual void SetOnlineStatus(bool isOnline = TRUE) | |
116 | { m_IsOnline = isOnline; } | |
117 | ||
118 | // set misc wxDialUpManager options | |
119 | // -------------------------------- | |
120 | ||
121 | // enable automatical checks for the connection status and sending of | |
122 | // wxEVT_DIALUP_CONNECTED/wxEVT_DIALUP_DISCONNECTED events. The interval | |
123 | // parameter is only for Unix where we do the check manually: under | |
124 | // Windows, the notification about the change of connection status is | |
125 | // instantenous. | |
126 | // | |
127 | // Returns FALSE if couldn't set up automatic check for online status. | |
128 | virtual bool EnableAutoCheckOnlineStatus(size_t nSeconds); | |
129 | ||
130 | // disable automatic check for connection status change - notice that the | |
131 | // wxEVT_DIALUP_XXX events won't be sent any more neither. | |
132 | virtual void DisableAutoCheckOnlineStatus(); | |
133 | ||
134 | // under Unix, the value of well-known host is used to check whether we're | |
135 | // connected to the internet. It's unused under Windows, but this function | |
136 | // is always safe to call. The default value is www.yahoo.com. | |
137 | virtual void SetWellKnownHost(const wxString& hostname, | |
138 | int portno = 80); | |
139 | /** Sets the commands to start up the network and to hang up | |
140 | again. Used by the Unix implementations only. | |
141 | */ | |
142 | virtual void SetConnectCommand(const wxString &command, const wxString &hupcmd) | |
143 | { m_ConnectCommand = command; m_HangUpCommand = hupcmd; } | |
144 | ||
145 | private: | |
146 | /// -1: don't know, 0 = no, 1 = yes | |
147 | int m_IsOnline; | |
148 | ||
149 | /// Can we use ifconfig to list active devices? | |
150 | int m_CanUseIfconfig; | |
151 | /// The path to ifconfig | |
152 | wxString m_IfconfigPath; | |
153 | ||
154 | /// beacon host: | |
155 | wxString m_BeaconHost; | |
156 | /// beacon host portnumber for connect: | |
157 | int m_BeaconPort; | |
158 | ||
159 | /// command to connect to network | |
160 | wxString m_ConnectCommand; | |
161 | /// command to hang up | |
162 | wxString m_HangUpCommand; | |
163 | /// name of ISP | |
164 | wxString m_ISPname; | |
165 | /// a timer for regular testing | |
166 | class AutoCheckTimer *m_timer; | |
167 | ||
168 | friend class AutoCheckTimer; | |
169 | /// determine status | |
170 | void CheckStatus(void) const; | |
171 | ||
172 | /// real status check | |
173 | void CheckStatusInternal(void); | |
174 | }; | |
175 | ||
176 | ||
177 | class AutoCheckTimer : public wxTimer | |
178 | { | |
179 | public: | |
180 | AutoCheckTimer(wxDialUpManagerImpl *dupman) | |
181 | { | |
182 | m_dupman = dupman; | |
183 | m_started = FALSE; | |
184 | } | |
185 | ||
186 | virtual bool Start( int millisecs = -1 ) | |
187 | { m_started = TRUE; return wxTimer::Start(millisecs, FALSE); } | |
188 | ||
189 | virtual void Notify() | |
190 | { wxLogTrace("Checking dial up network status."); m_dupman->CheckStatus(); } | |
191 | ||
192 | virtual void Stop() | |
193 | { if ( m_started ) wxTimer::Stop(); } | |
194 | public: | |
195 | bool m_started; | |
196 | wxDialUpManagerImpl *m_dupman; | |
197 | }; | |
198 | ||
199 | bool | |
200 | wxDialUpManagerImpl::Dial(const wxString &isp, | |
201 | const wxString & WXUNUSED(username), | |
202 | const wxString & WXUNUSED(password)) | |
203 | { | |
204 | if(m_IsOnline == 1) | |
205 | return FALSE; | |
206 | m_IsOnline = -1; | |
207 | m_ISPname = isp; | |
208 | wxString cmd; | |
209 | if(m_ConnectCommand.Find("%s")) | |
210 | cmd.Printf(m_ConnectCommand,m_ISPname.c_str()); | |
211 | else | |
212 | cmd = m_ConnectCommand; | |
213 | return wxExecute(cmd, /* sync */ TRUE) == 0; | |
214 | } | |
215 | ||
216 | bool | |
217 | wxDialUpManagerImpl::HangUp(void) | |
218 | { | |
219 | if(m_IsOnline == 0) | |
220 | return FALSE; | |
221 | m_IsOnline = -1; | |
222 | wxString cmd; | |
223 | if(m_HangUpCommand.Find("%s")) | |
224 | cmd.Printf(m_HangUpCommand,m_ISPname.c_str()); | |
225 | else | |
226 | cmd = m_HangUpCommand; | |
227 | return wxExecute(cmd, /* sync */ TRUE) == 0; | |
228 | } | |
229 | ||
230 | ||
231 | bool | |
232 | wxDialUpManagerImpl::EnableAutoCheckOnlineStatus(size_t nSeconds) | |
233 | { | |
234 | wxASSERT(m_timer == NULL); | |
235 | m_timer = new AutoCheckTimer(this); | |
236 | bool rc = m_timer->Start(nSeconds*1000); | |
237 | if(! rc) | |
238 | { | |
239 | delete m_timer; | |
240 | m_timer = NULL; | |
241 | } | |
242 | return rc; | |
243 | } | |
244 | ||
245 | void | |
246 | wxDialUpManagerImpl::DisableAutoCheckOnlineStatus() | |
247 | { | |
248 | wxASSERT(m_timer != NULL); | |
249 | m_timer->Stop(); | |
250 | delete m_timer; | |
251 | m_timer = NULL; | |
252 | } | |
253 | ||
254 | ||
255 | void | |
256 | wxDialUpManagerImpl::SetWellKnownHost(const wxString& hostname, int portno) | |
257 | { | |
258 | /// does hostname contain a port number? | |
259 | wxString port = hostname.After(':'); | |
260 | if(port.Length()) | |
261 | { | |
262 | m_BeaconHost = hostname.Before(':'); | |
263 | m_BeaconPort = atoi(port); | |
264 | } | |
265 | else | |
266 | { | |
267 | m_BeaconHost = hostname; | |
268 | m_BeaconPort = portno; | |
269 | } | |
270 | } | |
271 | ||
272 | ||
273 | void | |
274 | wxDialUpManagerImpl::CheckStatus(void) const | |
275 | { | |
276 | // This function calls the CheckStatusInternal() helper function | |
277 | // which is OS - specific and then sends the events. | |
278 | ||
279 | int oldIsOnline = m_IsOnline; | |
280 | ( /* non-const */ (wxDialUpManagerImpl *)this)->CheckStatusInternal(); | |
281 | ||
282 | // now send the events as appropriate: | |
283 | if(m_IsOnline != oldIsOnline) | |
284 | { | |
285 | if(m_IsOnline) | |
286 | ; // send ev | |
287 | else | |
288 | ; // send ev | |
289 | } | |
290 | } | |
291 | ||
292 | /* | |
293 | We have three methods that we can use: | |
294 | ||
295 | 1. test via /sbin/ifconfig and grep for "sl", "ppp", "pl" | |
296 | --> should be fast enough for regular polling | |
297 | 2. test if we can reach the well known beacon host | |
298 | --> too slow for polling | |
299 | 3. check /proc/net/dev on linux?? | |
300 | This method should be preferred, if possible. Need to do more | |
301 | testing. | |
302 | ||
303 | */ | |
304 | ||
305 | void | |
306 | wxDialUpManagerImpl::CheckStatusInternal(void) | |
307 | { | |
308 | m_IsOnline = -1; | |
309 | ||
310 | // First time check for ifconfig location. We only use the variant | |
311 | // which does not take arguments, a la GNU. | |
312 | if(m_CanUseIfconfig == -1) // unknown | |
313 | { | |
314 | if(wxFileExists("/sbin/ifconfig")) | |
315 | m_IfconfigPath = "/sbin/ifconfig"; | |
316 | else if(wxFileExists("/usr/sbin/ifconfig")) | |
317 | m_IfconfigPath = "/usr/sbin/ifconfig"; | |
318 | } | |
319 | ||
320 | wxLogNull ln; // suppress all error messages | |
321 | // Let's try the ifconfig method first, should be fastest: | |
322 | if(m_CanUseIfconfig != 0) // unknown or yes | |
323 | { | |
324 | wxASSERT(m_IfconfigPath.length()); | |
325 | ||
326 | wxString tmpfile = wxFileName::CreateTempFileName("_wxdialuptest"); | |
327 | wxString cmd = "/bin/sh -c \'"; | |
328 | cmd << m_IfconfigPath << " >" << tmpfile << '\''; | |
329 | /* I tried to add an option to wxExecute() to not close stdout, | |
330 | so we could let ifconfig write directly to the tmpfile, but | |
331 | this does not work. That should be faster, as it doesn't call | |
332 | the shell first. I have no idea why. :-( (KB) */ | |
333 | #if 0 | |
334 | // temporarily redirect stdout/stderr: | |
335 | int | |
336 | new_stdout = dup(STDOUT_FILENO), | |
337 | new_stderr = dup(STDERR_FILENO); | |
338 | close(STDOUT_FILENO); | |
339 | close(STDERR_FILENO); | |
340 | ||
341 | int | |
342 | // new stdout: | |
343 | output_fd = open(tmpfile, O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR), | |
344 | // new stderr: | |
345 | null_fd = open("/dev/null", O_CREAT, S_IRUSR|S_IWUSR); | |
346 | // verify well behaved unix behaviour: | |
347 | wxASSERT(output_fd == STDOUT_FILENO); | |
348 | wxASSERT(null_fd == STDERR_FILENO); | |
349 | int rc = wxExecute(m_IfconfigPath,TRUE /* sync */,NULL ,wxEXECUTE_DONT_CLOSE_FDS); | |
350 | close(null_fd); close(output_fd); | |
351 | // restore old stdout, stderr: | |
352 | int test; | |
353 | test = dup(new_stdout); close(new_stdout); wxASSERT(test == STDOUT_FILENO); | |
354 | test = dup(new_stderr); close(new_stderr); wxASSERT(test == STDERR_FILENO); | |
355 | if(rc == 0) | |
356 | #endif | |
357 | if(wxExecute(cmd,TRUE /* sync */) == 0) | |
358 | { | |
359 | m_CanUseIfconfig = 1; | |
360 | wxFile file; | |
361 | if( file.Open(tmpfile) ) | |
362 | { | |
363 | char *output = new char [file.Length()+1]; | |
364 | output[file.Length()] = '\0'; | |
365 | if(file.Read(output,file.Length()) == file.Length()) | |
366 | { | |
367 | if(strstr(output,"ppp") // ppp | |
368 | || strstr(output,"sl") // slip | |
369 | || strstr(output,"pl") // plip | |
370 | ) | |
371 | m_IsOnline = 1; | |
372 | else | |
373 | m_IsOnline = 0; | |
374 | } | |
375 | file.Close(); | |
376 | delete [] output; | |
377 | } | |
378 | // else m_IsOnline remains -1 as we don't know for sure | |
379 | } | |
380 | else // could not run ifconfig correctly | |
381 | m_CanUseIfconfig = 0; // don't try again | |
382 | (void) wxRemoveFile(tmpfile); | |
383 | if(m_IsOnline != -1) // we are done | |
384 | return; | |
385 | } | |
386 | ||
387 | // second method: try to connect to well known host: | |
388 | // This can be used under Win 9x, too! | |
389 | struct hostent *hp; | |
390 | struct sockaddr_in serv_addr; | |
391 | int sockfd; | |
392 | ||
393 | m_IsOnline = 0; // assume false | |
394 | if((hp = gethostbyname(m_BeaconHost)) == NULL) | |
395 | return; // no DNS no net | |
396 | ||
397 | serv_addr.sin_family = hp->h_addrtype; | |
398 | memcpy(&serv_addr.sin_addr,hp->h_addr, hp->h_length); | |
399 | serv_addr.sin_port = htons(m_BeaconPort); | |
400 | if( ( sockfd = socket(hp->h_addrtype, SOCK_STREAM, 0)) < 0) | |
401 | { | |
402 | // sys_error("cannot create socket for gw"); | |
403 | return; | |
404 | } | |
405 | if( connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) | |
406 | { | |
407 | //sys_error("cannot connect to server"); | |
408 | return; | |
409 | } | |
410 | //connected! | |
411 | close(sockfd); | |
412 | } | |
413 | ||
414 | ||
415 | /* static */ | |
416 | wxDialUpManager * | |
417 | wxDialUpManager::wxDialUpManager::Create(void) | |
418 | { | |
419 | return new wxDialUpManagerImpl; | |
420 | } | |
421 | ||
422 | #endif // wxUSE_DIALUP_MANAGER |