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