added missing implementation (untested, but compiles)
[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 #ifdef __GNUG__
15 # pragma implementation "dialup.h"
16 #endif
17
18 #if wxUSE_DIALUP_MANAGER
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 /// do we have a constant net connection? -- GUESS!
113 bool IsAlwaysOnline() const
114 {
115 ((wxDialUpManagerImpl *) this)->HangUp(); // brutal but necessary
116 return IsOnline();
117 }
118 /// returns TRUE if (async) dialing is in progress
119 inline virtual bool IsDialing() const
120 { return m_DialProcess != NULL; }
121
122 // cancel dialing the number initiated with Dial(async = TRUE)
123 // NB: this won't result in DISCONNECTED event being sent
124 virtual bool CancelDialing();
125
126 unsigned int GetISPNames(class wxArrayString &) const
127 { return 0; }
128
129 // sometimes the built-in logic for determining the online status may fail,
130 // so, in general, the user should be allowed to override it. This function
131 // allows to forcefully set the online status - whatever our internal
132 // algorithm may think about it.
133 virtual void SetOnlineStatus(bool isOnline = TRUE)
134 { m_IsOnline = isOnline; }
135
136 // set misc wxDialUpManager options
137 // --------------------------------
138
139 // enable automatical checks for the connection status and sending of
140 // wxEVT_DIALUP_CONNECTED/wxEVT_DIALUP_DISCONNECTED events. The interval
141 // parameter is only for Unix where we do the check manually: under
142 // Windows, the notification about the change of connection status is
143 // instantenous.
144 //
145 // Returns FALSE if couldn't set up automatic check for online status.
146 virtual bool EnableAutoCheckOnlineStatus(size_t nSeconds);
147
148 // disable automatic check for connection status change - notice that the
149 // wxEVT_DIALUP_XXX events won't be sent any more neither.
150 virtual void DisableAutoCheckOnlineStatus();
151
152 // under Unix, the value of well-known host is used to check whether we're
153 // connected to the internet. It's unused under Windows, but this function
154 // is always safe to call. The default value is www.yahoo.com.
155 virtual void SetWellKnownHost(const wxString& hostname,
156 int portno = 80);
157 /** Sets the commands to start up the network and to hang up
158 again. Used by the Unix implementations only.
159 */
160 virtual void SetConnectCommand(const wxString &command, const wxString &hupcmd)
161 { m_ConnectCommand = command; m_HangUpCommand = hupcmd; }
162
163 private:
164 /// -1: don´t know, 0 = no, 1 = yes
165 int m_IsOnline;
166
167 /// Can we use ifconfig to list active devices?
168 int m_CanUseIfconfig;
169 /// The path to ifconfig
170 wxString m_IfconfigPath;
171
172 /// beacon host:
173 wxString m_BeaconHost;
174 /// beacon host portnumber for connect:
175 int m_BeaconPort;
176
177 /// command to connect to network
178 wxString m_ConnectCommand;
179 /// command to hang up
180 wxString m_HangUpCommand;
181 /// name of ISP
182 wxString m_ISPname;
183 /// a timer for regular testing
184 class AutoCheckTimer *m_timer;
185 friend class AutoCheckTimer;
186
187 /// a wxProcess for dialling in background
188 class wxDialProcess *m_DialProcess;
189 /// pid of dial process
190 int m_DialPId;
191 friend class wxDialProcess;
192
193 /// determine status
194 void CheckStatus(bool fromAsync = FALSE) const;
195
196 /// real status check
197 void CheckStatusInternal(void);
198 };
199
200
201 class AutoCheckTimer : public wxTimer
202 {
203 public:
204 AutoCheckTimer(wxDialUpManagerImpl *dupman)
205 {
206 m_dupman = dupman;
207 m_started = FALSE;
208 }
209
210 virtual bool Start( int millisecs = -1 )
211 { m_started = TRUE; return wxTimer::Start(millisecs, FALSE); }
212
213 virtual void Notify()
214 { wxLogTrace("Checking dial up network status."); m_dupman->CheckStatus(); }
215
216 virtual void Stop()
217 { if ( m_started ) wxTimer::Stop(); }
218 public:
219 bool m_started;
220 wxDialUpManagerImpl *m_dupman;
221 };
222
223 class wxDialProcess : public wxProcess
224 {
225 public:
226 wxDialProcess(wxDialUpManagerImpl *dupman)
227 {
228 m_DupMan = dupman;
229 }
230 void OnTerminate(int WXUNUSED(pid), int WXUNUSED(status)) const
231 {
232 m_DupMan->m_DialProcess = NULL;
233 m_DupMan->CheckStatus(TRUE);
234 }
235 private:
236 wxDialUpManagerImpl *m_DupMan;
237 };
238
239
240 wxDialUpManagerImpl::wxDialUpManagerImpl()
241 {
242 m_IsOnline = -1; // unknown
243 m_DialProcess = NULL;
244 m_timer = NULL;
245 m_CanUseIfconfig = -1; // unknown
246 m_BeaconHost = WXDIALUP_MANAGER_DEFAULT_BEACONHOST;
247 m_BeaconPort = 80;
248 }
249
250 wxDialUpManagerImpl::~wxDialUpManagerImpl()
251 {
252 if(m_timer) delete m_timer;
253 if(m_DialProcess) m_DialProcess->Detach();
254 }
255
256 bool
257 wxDialUpManagerImpl::Dial(const wxString &isp,
258 const wxString & WXUNUSED(username),
259 const wxString & WXUNUSED(password),
260 bool async)
261 {
262 if(m_IsOnline == 1)
263 return FALSE;
264 m_IsOnline = -1;
265 m_ISPname = isp;
266 wxString cmd;
267 if(m_ConnectCommand.Find("%s"))
268 cmd.Printf(m_ConnectCommand,m_ISPname.c_str());
269 else
270 cmd = m_ConnectCommand;
271
272 if ( async )
273 {
274 m_DialProcess = new wxDialProcess(this);
275 m_DialPId = wxExecute(cmd, FALSE, m_DialProcess);
276 if(m_DialPId == 0)
277 {
278 delete m_DialProcess;
279 m_DialProcess = NULL;
280 return FALSE;
281 }
282 else
283 return TRUE;
284 }
285 else
286 return wxExecute(cmd, /* sync */ TRUE) == 0;
287 }
288
289 bool
290 wxDialUpManagerImpl::HangUp(void)
291 {
292 if(m_IsOnline == 0)
293 return FALSE;
294 if(IsDialing())
295 {
296 wxLogError(_("Already dialling ISP."));
297 return FALSE;
298 }
299 m_IsOnline = -1;
300 wxString cmd;
301 if(m_HangUpCommand.Find("%s"))
302 cmd.Printf(m_HangUpCommand,m_ISPname.c_str(), m_DialProcess);
303 else
304 cmd = m_HangUpCommand;
305 return wxExecute(cmd, /* sync */ TRUE) == 0;
306 }
307
308
309 bool
310 wxDialUpManagerImpl::CancelDialing()
311 {
312 if(! IsDialing())
313 return FALSE;
314 return kill(m_DialPId, SIGTERM) > 0;
315 }
316
317 bool
318 wxDialUpManagerImpl::EnableAutoCheckOnlineStatus(size_t nSeconds)
319 {
320 wxASSERT(m_timer == NULL);
321 m_timer = new AutoCheckTimer(this);
322 bool rc = m_timer->Start(nSeconds*1000);
323 if(! rc)
324 {
325 delete m_timer;
326 m_timer = NULL;
327 }
328 return rc;
329 }
330
331 void
332 wxDialUpManagerImpl::DisableAutoCheckOnlineStatus()
333 {
334 wxASSERT(m_timer != NULL);
335 m_timer->Stop();
336 delete m_timer;
337 m_timer = NULL;
338 }
339
340
341 void
342 wxDialUpManagerImpl::SetWellKnownHost(const wxString& hostname, int portno)
343 {
344 /// does hostname contain a port number?
345 wxString port = hostname.After(':');
346 if(port.Length())
347 {
348 m_BeaconHost = hostname.Before(':');
349 m_BeaconPort = atoi(port);
350 }
351 else
352 {
353 m_BeaconHost = hostname;
354 m_BeaconPort = portno;
355 }
356 }
357
358
359 void
360 wxDialUpManagerImpl::CheckStatus(bool fromAsync) const
361 {
362 // This function calls the CheckStatusInternal() helper function
363 // which is OS - specific and then sends the events.
364
365 int oldIsOnline = m_IsOnline;
366 ( /* non-const */ (wxDialUpManagerImpl *)this)->CheckStatusInternal();
367
368 // now send the events as appropriate:
369 if(m_IsOnline != oldIsOnline && oldIsOnline != -1)
370 {
371 wxDialUpEvent event(m_IsOnline, ! fromAsync);
372 (void)wxTheApp->ProcessEvent(event);
373 }
374 }
375
376 /*
377 We have three methods that we can use:
378
379 1. test via /sbin/ifconfig and grep for "sl", "ppp", "pl"
380 --> should be fast enough for regular polling
381 2. test if we can reach the well known beacon host
382 --> too slow for polling
383 3. check /proc/net/dev on linux??
384 This method should be preferred, if possible. Need to do more
385 testing.
386
387 */
388
389 void
390 wxDialUpManagerImpl::CheckStatusInternal(void)
391 {
392 m_IsOnline = -1;
393
394 // First time check for ifconfig location. We only use the variant
395 // which does not take arguments, a la GNU.
396 if(m_CanUseIfconfig == -1) // unknown
397 {
398 if(wxFileExists("/sbin/ifconfig"))
399 m_IfconfigPath = "/sbin/ifconfig";
400 else if(wxFileExists("/usr/sbin/ifconfig"))
401 m_IfconfigPath = "/usr/sbin/ifconfig";
402 }
403
404 wxLogNull ln; // suppress all error messages
405 // Let´s try the ifconfig method first, should be fastest:
406 if(m_CanUseIfconfig != 0) // unknown or yes
407 {
408 wxASSERT(m_IfconfigPath.length());
409
410 wxString tmpfile = wxGetTempFileName("_wxdialuptest");
411 wxString cmd = "/bin/sh -c \'";
412 cmd << m_IfconfigPath << " >" << tmpfile << '\'';
413 /* I tried to add an option to wxExecute() to not close stdout,
414 so we could let ifconfig write directly to the tmpfile, but
415 this does not work. That should be faster, as it doesn´t call
416 the shell first. I have no idea why. :-( (KB) */
417 #if 0
418 // temporarily redirect stdout/stderr:
419 int
420 new_stdout = dup(STDOUT_FILENO),
421 new_stderr = dup(STDERR_FILENO);
422 close(STDOUT_FILENO);
423 close(STDERR_FILENO);
424
425 int
426 // new stdout:
427 output_fd = open(tmpfile, O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR),
428 // new stderr:
429 null_fd = open("/dev/null", O_CREAT, S_IRUSR|S_IWUSR);
430 // verify well behaved unix behaviour:
431 wxASSERT(output_fd == STDOUT_FILENO);
432 wxASSERT(null_fd == STDERR_FILENO);
433 int rc = wxExecute(m_IfconfigPath,TRUE /* sync */,NULL ,wxEXECUTE_DONT_CLOSE_FDS);
434 close(null_fd); close(output_fd);
435 // restore old stdout, stderr:
436 int test;
437 test = dup(new_stdout); close(new_stdout); wxASSERT(test == STDOUT_FILENO);
438 test = dup(new_stderr); close(new_stderr); wxASSERT(test == STDERR_FILENO);
439 if(rc == 0)
440 #endif
441 if(wxExecute(cmd,TRUE /* sync */) == 0)
442 {
443 m_CanUseIfconfig = 1;
444 wxFile file;
445 if( file.Open(tmpfile) )
446 {
447 char *output = new char [file.Length()+1];
448 output[file.Length()] = '\0';
449 if(file.Read(output,file.Length()) == file.Length())
450 {
451 if(strstr(output,"ppp") // ppp
452 || strstr(output,"sl") // slip
453 || strstr(output,"pl") // plip
454 )
455 m_IsOnline = 1;
456 else
457 m_IsOnline = 0;
458 }
459 file.Close();
460 delete [] output;
461 }
462 // else m_IsOnline remains -1 as we don't know for sure
463 }
464 else // could not run ifconfig correctly
465 m_CanUseIfconfig = 0; // don´t try again
466 (void) wxRemoveFile(tmpfile);
467 if(m_IsOnline != -1) // we are done
468 return;
469 }
470
471 // second method: try to connect to well known host:
472 // This can be used under Win 9x, too!
473 struct hostent *hp;
474 struct sockaddr_in serv_addr;
475 int sockfd;
476
477 m_IsOnline = 0; // assume false
478 if((hp = gethostbyname(m_BeaconHost)) == NULL)
479 return; // no DNS no net
480
481 serv_addr.sin_family = hp->h_addrtype;
482 memcpy(&serv_addr.sin_addr,hp->h_addr, hp->h_length);
483 serv_addr.sin_port = htons(m_BeaconPort);
484 if( ( sockfd = socket(hp->h_addrtype, SOCK_STREAM, 0)) < 0)
485 {
486 // sys_error("cannot create socket for gw");
487 return;
488 }
489 // PING method:
490
491 if(sendto(sockfd, "hello", strlen("hello"), /* flags */ 0,
492 (struct sockaddr *)&serv_addr,
493 sizeof(serv_addr)) == -1)
494 return;
495 #if 0
496
497 if( connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
498 {
499 //sys_error("cannot connect to server");
500 return;
501 }
502 //connected!
503 close(sockfd);
504 #endif
505 m_IsOnline = TRUE;
506 }
507
508
509 /* static */
510 wxDialUpManager *
511 wxDialUpManager::Create(void)
512 {
513 return new wxDialUpManagerImpl;
514 }
515
516 #endif // wxUSE_DIALUP_MANAGER