nitlog: oops, didn't deal properly with posts in months that haven't
[wvapps.git] / wvdial / wvdialer.cc
blob53b603235e84ac8a25145ce187d5d74f15a14475
1 /*
2 * Worldvisions Weaver Software:
3 * Copyright (C) 1997-2003 Net Integration Technologies, Inc.
5 * Implementation of the WvDialer smart-dialer class.
7 */
9 #include "wvdialer.h"
10 #include "wvver.h"
12 #include <sys/types.h>
13 #include <sys/stat.h>
14 #include <fcntl.h>
15 #include <signal.h>
16 #include <termios.h>
17 #include <time.h>
18 #include <unistd.h>
19 #include <ctype.h>
20 #include <errno.h>
21 #include <assert.h>
22 #include <xplc/xplc.h>
24 // N_() is defined here just for internationalization.
25 #define N_(String) String
27 static char * init_responses[] = {
28 "ok",
29 "error",
30 NULL
33 static char * dial_responses[] = {
34 "connect",
35 "no carrier",
36 "no dialtone",
37 "no dial tone",
38 "busy",
39 "error",
40 "voice",
41 "fclass",
42 "no answer",
43 NULL
46 static char * prompt_strings[] = {
47 "}!}",
48 "!}!",
49 NULL
52 static int messagetail_pid = 0;
54 //**************************************************
55 // WvDialer Public Functions
56 //**************************************************
58 WvDialer::WvDialer( WvConf &_cfg, WvStringList *_sect_list, bool _chat_mode )
59 /***************************************************************************/
60 : WvStreamClone( 0 ),
61 cfg(_cfg), log( "WvDial", WvLog::Debug ),
62 err( log.split( WvLog::Error ) ),
63 modemrx( "WvDial Modem", WvLog::Debug )
65 ppp_pipe = NULL;
66 pppd_log = NULL;
67 been_online = false;
68 stat = Idle;
69 offset = 0;
70 prompt_tries = 0;
71 last_rx = last_execute = 0;
72 prompt_response = "";
73 auto_reconnect_delay = 0;
74 auto_reconnect_at = 0;
75 connected_at = 0;
76 phnum_count = 0;
77 phnum_max = 0;
78 // tell wvstreams we need our own subtask
79 uses_continue_select = true;
81 brain = NULL;
82 modem = NULL;
83 sect_list = _sect_list;
84 chat_mode = _chat_mode;
86 log("WvDial: Internet dialer version " WVDIAL_VER_STRING "\n");
88 // Ensure all sections in sect_list actually exist, warning if not.
89 WvStringList::Iter iter(*sect_list);
90 for(iter.rewind(); iter.next();)
92 if(cfg[*iter] == NULL)
94 err(WvLog::Warning,
95 "Warning: section [%s] does not exist in wvdial.conf.\n",
96 *iter);
100 // Ensure all inherited sections exist, warning if not.
101 WvConfigSectionList::Iter iter2 (cfg);
102 for (iter2.rewind(); iter2.next();)
104 WvConfigSection & sect2 = *iter2;
105 WvConfigEntry * entry = sect2["Inherits"];
106 if (entry)
108 WvString inherits = entry->value;
109 if (cfg[inherits] == NULL)
110 err( WvLog::Warning,
111 "Warning: inherited section [%s] does not exist in wvdial.conf\n",
112 inherits);
116 // Activate the brain and read configuration.
117 brain = new WvDialBrain(this);
119 // init_modem() reads the config options. It MUST run here!
121 if(!init_modem())
123 // init_modem() printed an error
124 stat = ModemError;
125 return;
128 if (options.provider.len())
130 log( WvLog::Notice, "Dialing %s %s.\n",
131 options.provider,
132 options.product);
134 if (options.homepage.len())
136 log(WvLog::Notice, "Homepage of %s: %s\n",
137 options.provider.len() ? options.provider.cstr() : "this provider",
138 options.homepage);
141 if(options.auto_reconnect && options.idle_seconds > 0)
143 err(WvLog::Notice,
144 "Idle Seconds = %s, disabling automatic reconnect.\n",
145 options.idle_seconds);
146 options.auto_reconnect = false;
149 pppd_mon.setdnstests(options.dnstest1, options.dnstest2);
150 pppd_mon.setcheckdns(options.check_dns);
151 pppd_mon.setcheckdfr(options.check_dfr);
154 WvDialer::~WvDialer()
155 /*******************/
157 terminate_continue_select();
159 WVRELEASE(ppp_pipe);
160 WVRELEASE(pppd_log);
161 delete brain;
164 bool WvDialer::dial()
165 /*******************/
166 // Returns false on error, or true to go asynchronous while dialing.
168 if(stat == Online)
169 return(true);
171 if(stat != Idle)
173 // error message has already been printed elsewhere
174 return(false);
177 if (!options.phnum)
179 err( "Configuration does not specify a valid phone number.\n" );
180 stat = OtherError;
183 if (!options.login)
185 err( "Configuration does not specify a valid login name.\n" );
186 stat = OtherError;
189 if (!options.password)
191 err( "Configuration does not specify a valid password.\n" );
192 stat = OtherError;
195 if( stat != Idle )
196 return( false );
198 phnum_max = 0;
199 if(options.phnum1.len())
201 phnum_max++;
202 if(options.phnum2.len())
204 phnum_max++;
205 if(options.phnum3.len())
207 phnum_max++;
208 if(options.phnum4.len())
209 phnum_max++;
214 // we need to re-init the modem if we were online before.
215 if(been_online && !init_modem())
216 stat = ModemError;
217 else
219 stat = Dial;
220 connect_attempts = 1;
221 dial_stat = 0;
222 brain->reset();
225 return(true);
228 void WvDialer::hangup()
229 /*********************/
231 WVRELEASE(ppp_pipe);
233 if( !chat_mode )
234 pppd_watch( 250 );
236 if( stat != Idle )
238 time_t now;
239 time( &now );
240 log( "Disconnecting at %s", ctime( &now ) );
241 del_modem();
242 stat = Idle;
245 if (messagetail_pid > 0)
247 kill(messagetail_pid, 15);
248 messagetail_pid = 0;
252 bool WvDialer::pre_select( SelectInfo& si )
253 /*******************************************/
255 if( isok() && stat != Online && stat != Idle
256 && time( NULL ) - last_execute > 1 )
258 // Pretend we have "data ready," so execute() gets called.
259 // select() already returns true whenever the modem is readable,
260 // but when we are doing a timeout (eg. PreDial1/2) for example,
261 // we need to execute() even if no modem data is incoming.
262 return( true );
264 else
266 return WvStreamClone::pre_select( si );
270 bool WvDialer::isok() const
271 /*************************/
273 bool b = ( !modem || modem->isok() )
274 && stat != ModemError && stat != OtherError;
276 if (!b)
277 err("Returning not ok!!\n" );
279 return( b );
282 char *WvDialer::connect_status() const
283 /*************************************/
285 static char msg[ 160 ];
287 switch( stat )
289 case PreDial2:
290 case PreDial1:
291 case Dial:
292 case WaitDial:
293 if( dial_stat == 1 )
294 strcpy( msg, N_("Last attempt timed out. Trying again.") );
295 else if( dial_stat == 2 )
296 strcpy( msg, N_("Modem did not connect last attempt. Trying again.") );
297 else if( dial_stat == 3 )
298 strcpy( msg, N_("No dial tone last attempt. Trying again.") );
299 else if( dial_stat == 4 )
300 strcpy( msg, N_("Busy signal on last attempt. Trying again.") );
301 else if( dial_stat == 5 )
302 strcpy( msg, N_("Voice answer on last attempt. Trying again.") );
303 else if( dial_stat == 6 )
304 strcpy( msg, N_("Fax answer on last attempt. Trying again.") );
305 else if( dial_stat == 7 )
306 strcpy( msg, N_("No answer on last attempt. Trying again.") );
307 else
308 return( NULL );
309 break;
310 case WaitAnything:
311 strcpy( msg, N_("Waiting for a response from Internet Provider.") );
312 break;
313 case WaitPrompt:
314 strcpy( msg, N_("Waiting for a prompt from Internet Provider.") );
315 break;
316 case AutoReconnectDelay:
317 sprintf( msg, "Next attempt in 00:%02ld:%02ld.",
318 ( auto_reconnect_at - time( NULL ) ) / 60,
319 ( auto_reconnect_at - time( NULL ) ) % 60 );
320 break;
321 default:
322 return( NULL );
324 return( msg );
328 void WvDialer::pppd_watch( int ms )
329 /*********************************/
331 // see if pppd has a message, analyse it and output to log
333 if( pppd_log != NULL && pppd_log->isok() )
335 char *line;
337 while ( (line = pppd_log->blocking_getline( ms )) )
339 WvString buffer1(pppd_mon.analyse_line( line ));
340 if (!!buffer1)
342 log("pppd: %s\n", buffer1);
349 void WvDialer::execute()
350 /**********************/
352 WvStreamClone::execute();
354 // the modem object might not exist, if we just disconnected and are
355 // redialing.
356 if( !modem && !init_modem() )
357 return;
359 last_execute = time( NULL );
361 if( !chat_mode )
362 pppd_watch( 100 );
364 switch( stat )
366 case Dial:
367 case WaitDial:
368 case PreDial1:
369 case PreDial2:
370 async_dial();
371 break;
372 case WaitAnything:
373 // we allow some time after connection for silly servers/modems.
374 if( modem->select( 500, true, false ) )
376 // if any data comes in at all, switch to impatient mode.
377 stat = WaitPrompt;
378 last_rx = time( NULL );
380 else if( time( NULL ) - last_rx >= 30 )
382 // timed out - do what WaitPrompt would do on a timeout.
383 stat = WaitPrompt;
385 else
387 // We prod the server with a CR character every once in a while.
388 // FIXME: Does this cause problems with login prompts?
389 modem->write( "\r", 1 );
391 break;
392 case WaitPrompt:
393 async_waitprompt();
394 break;
395 case Online:
396 assert( !chat_mode );
397 // If already online, we only need to make sure pppd is still there.
398 if( ppp_pipe && ppp_pipe->child_exited() )
400 int pppd_exit_status = ppp_pipe->exit_status();
401 if( ppp_pipe->child_killed() )
403 log( WvLog::Error, "PPP was killed! (signal = %s)\n",
404 ppp_pipe->exit_status() );
407 // we must delete the WvModem object so it can be recreated
408 // later; starting pppd seems to screw up the file descriptor.
409 hangup();
410 del_modem();
412 time_t call_duration = time( NULL ) - connected_at;
414 if( pppd_mon.auth_failed() )
416 log("Authentication error.\n"
417 "We failed to authenticate ourselves to the peer.\n"
418 "Maybe bad account or password?\n" );
420 else
422 WvString msg = "";
423 switch (pppd_exit_status)
425 case 2:
426 msg = "pppd options error";
427 break;
428 case 3:
429 msg = "No root priv error";
430 break;
431 case 4:
432 msg = "No ppp module error";
433 break;
434 case 10:
435 msg = "PPP negotiation failed";
436 break;
437 case 11:
438 msg = "Peer didn't authenticatie itself";
439 break;
440 case 12:
441 msg = "Link idle: Idle Seconds reached.";
442 break;
443 case 13:
444 msg = "Connect time limit reached.";
445 break;
446 case 14:
447 msg = "Callback negotiated, call should come back.";
448 break;
449 case 15:
450 msg = "Lack of LCP echo responses";
451 break;
452 case 17:
453 msg = "Loopback detected";
454 break;
455 case 19:
456 msg = "Authentication error.\n"
457 "We failed to authenticate ourselves to the peer.\n"
458 "Maybe bad account or password?";
459 break;
461 if (msg.len())
463 // Note: exit code = %s is parsed by kinternet:
464 log("The PPP daemon has died: %s (exit code = %s)\n",
465 msg, pppd_exit_status);
466 log("man pppd explains pppd error codes in more detail.\n");
467 err(WvLog::Notice, "I guess that's it for now, exiting\n");
468 if (pppd_exit_status == 12 && options.auto_reconnect)
469 err(WvLog::Notice, "Idle parameter is passed to pppd\n"
470 "If you don't want an idle timeout per default,\n"
471 "comment out the idle parameter in /etc/ppp/options\n");
472 if (pppd_exit_status == 15)
474 log("Provider is overloaded(often the case) or line problem.\n");
476 options.auto_reconnect = false;
478 msg = "";
479 switch (pppd_exit_status)
481 case 1:
482 msg = "Fatal pppd error";
483 break;
484 case 5:
485 msg = "pppd received a signal";
486 break;
487 case 6:
488 msg = "Serial port lock failed";
489 break;
490 case 7:
491 msg = "Serial port open failed";
492 break;
493 case 8:
494 msg = "Connect script failed";
495 break;
496 case 9:
497 msg = "Pty program error";
498 break;
499 case 16:
500 msg = "A modem hung up the phone";
501 break;
502 case 18:
503 msg = "The init script failed";
504 break;
506 if (msg.len())
508 log("The PPP daemon has died: %s (exit code = %s)\n",
509 msg, pppd_exit_status);
510 log("man pppd explains pppd error codes in more detail.\n");
511 log(WvLog::Notice, "Try again and look into /var/log/messages "
512 "and the wvdial and pppd man pages for more information.\n");
513 } else
514 log(WvLog::Notice, "The PPP daemon has died. (exit code = %s)\n",
515 pppd_exit_status);
518 // check to see if we're supposed to redial automatically soon.
519 if( options.auto_reconnect && isok() )
521 if( call_duration >= 45 )
522 // Connection was more than 45 seconds, so reset the
523 // "exponential backup timer".
524 auto_reconnect_delay = 0;
526 // exponentially back up...
527 auto_reconnect_delay *= 2;
528 if( auto_reconnect_delay == 0 )
529 auto_reconnect_delay = 5; // start at 5 seconds
530 if( auto_reconnect_delay > 600 )
531 auto_reconnect_delay = 600; // no longer than 10 minutes
533 auto_reconnect_at = time( NULL ) + auto_reconnect_delay;
535 stat = AutoReconnectDelay;
536 log( WvLog::Notice, "Auto Reconnect will be attempted in %s "
537 "seconds\n",
538 auto_reconnect_at - time( NULL ) );
541 break;
542 case AutoReconnectDelay:
543 // If enough time has passed after ISP disconnected us that we should
544 // redial, do it...
545 // We can only get into this state if the Auto Reconnect option is
546 // enabled, so there's no point in checking the option here.
547 if( time( NULL ) >= auto_reconnect_at )
549 stat = Idle;
550 dial();
552 break;
553 case Idle:
554 case ModemError:
555 case OtherError:
556 default:
557 drain();
558 break;
563 //**************************************************
564 // WvDialer Private Functions
565 //**************************************************
567 void WvDialer::load_options()
568 /***************************/
570 OptInfo opts[] = {
571 // string options:
572 { "Modem", &options.modem, NULL, "/dev/modem", 0 },
573 { "Init1", &options.init1, NULL, "ATZ", 0 },
574 { "Init2", &options.init2, NULL, "", 0 },
575 { "Init3", &options.init3, NULL, "", 0 },
576 { "Init4", &options.init4, NULL, "", 0 },
577 { "Init5", &options.init5, NULL, "", 0 },
578 { "Init6", &options.init6, NULL, "", 0 },
579 { "Init7", &options.init7, NULL, "", 0 },
580 { "Init8", &options.init8, NULL, "", 0 },
581 { "Init9", &options.init9, NULL, "", 0 },
582 { "Phone", &options.phnum, NULL, "", 0 },
583 { "Phone1", &options.phnum1, NULL, "", 0 },
584 { "Phone2", &options.phnum2, NULL, "", 0 },
585 { "Phone3", &options.phnum3, NULL, "", 0 },
586 { "Phone4", &options.phnum4, NULL, "", 0 },
587 { "Dial Prefix", &options.dial_prefix, NULL, "", 0 },
588 { "Area Code", &options.areacode, NULL, "", 0 },
589 { "Dial Command", &options.dial_cmd, NULL, "ATDT", 0 },
590 { "Username", &options.login, NULL, "", 0 },
591 { "Login Prompt", &options.login_prompt, NULL, "", 0 },
592 { "Password", &options.password, NULL, "", 0 },
593 { "Password Prompt", &options.pass_prompt, NULL, "", 0 },
594 { "PPPD Path", &options.where_pppd, NULL, "/usr/sbin/pppd", 0 },
595 { "PPPD Option", &options.pppd_option, NULL, "", 0 },
596 { "Force Address", &options.force_addr, NULL, "", 0 },
597 { "Remote Name", &options.remote, NULL, "*", 0 },
598 { "Default Reply", &options.default_reply,NULL, "ppp", 0 },
599 { "Country", &options.country, NULL, "", 0 },
600 { "Provider", &options.provider, NULL, "", 0 },
601 { "Product", &options.product, NULL, "", 0 },
602 { "Homepage", &options.homepage, NULL, "", 0 },
603 { "DialMessage1", &options.dialmessage1, NULL, "", 0 },
604 { "DialMessage2", &options.dialmessage2, NULL, "", 0 },
605 { "DNS Test1", &options.dnstest1, NULL, "www.suse.de", 0 },
606 { "DNS Test2", &options.dnstest2, NULL, "www.suse.com", 0 },
608 // int/bool options
609 { "Baud", NULL, &options.baud, "", DEFAULT_BAUD },
610 { "Carrier Check", NULL, &options.carrier_check, "", true },
611 { "Stupid Mode", NULL, &options.stupid_mode, "", false },
612 { "New PPPD", NULL, &options.new_pppd, "", true },
613 { "Auto Reconnect", NULL, &options.auto_reconnect,"", true },
614 { "Dial Attempts", NULL, &options.dial_attempts, "", 0 },
615 { "Abort on Busy", NULL, &options.abort_on_busy, "", false },
616 { "Abort on No Dialtone", NULL, &options.abort_on_no_dialtone, "", true },
617 { "Compuserve", NULL, &options.compuserve, "", false },
618 { "Tonline", NULL, &options.tonline, "", false },
619 { "Auto DNS", NULL, &options.auto_dns, "", true },
620 { "Check DNS", NULL, &options.check_dns, "", true },
621 { "Check Def Route", NULL, &options.check_dfr, "", true },
622 { "Idle Seconds", NULL, &options.idle_seconds, "", 0 },
623 { "ISDN", NULL, &options.isdn, "", false },
624 { "Ask Password", NULL, &options.ask_password, "", false },
626 { NULL, NULL, NULL, "", 0 }
629 char * d = "Dialer Defaults";
631 for( int i=0; opts[i].name != NULL; i++ )
633 if( opts[i].str_member == NULL )
635 // it's an int/bool option.
636 *( opts[i].int_member ) =
637 cfg.fuzzy_getint( *sect_list, opts[i].name,
638 cfg.getint( d, opts[i].name, opts[i].int_default ) );
640 else
642 // it's a string option.
643 *( opts[i].str_member ) =
644 cfg.fuzzy_get( *sect_list, opts[i].name,
645 cfg.get( d, opts[i].name, opts[i].str_default ) );
649 // Support Init, as well as Init1, to make old WvDial people happy.
650 const char * newopt = cfg.fuzzy_get( *sect_list, "Init",
651 cfg.get( d, "Init", NULL ) );
652 if( newopt )
653 options.init1 = newopt;
656 bool WvDialer::init_modem()
657 /*************************/
659 int received, count;
661 load_options();
663 if (!options.modem)
665 err( "Configuration does not specify a valid modem device.\n" );
666 stat = ModemError;
667 return( false ); // if we get this error, we already have a problem.
670 for (count = 0; count < 3; count++)
672 // the buffer is empty.
673 offset = 0;
675 del_modem();
677 // Open the modem...
678 if( chat_mode )
680 int flags = fcntl( STDIN_FILENO, F_GETFL );
681 if( ( flags & O_ACCMODE ) == O_RDWR )
683 cloned = modem = new WvModemBase( STDIN_FILENO );
685 else
687 // The following is needed for diald.
688 // Stdin is not opened for read/write.
689 ::close( STDIN_FILENO );
690 if( getenv( "MODEM" ) == NULL )
692 err( "stdin not read/write and $MODEM not set\n" );
693 exit( 1 );
695 // Try to open device $MODEM.
696 flags &= !O_ACCMODE;
697 flags |= O_RDWR;
698 int tty = ::open( getenv( "MODEM" ), flags );
699 if( tty == -1 )
701 err( "can't open %s: %m\n", getenv( "MODEM" ) );
702 exit( 1 );
704 cloned = modem = new WvModemBase( tty );
707 else
709 cloned = modem = new WvModem( options.modem, options.baud );
711 if( !modem->isok() )
713 err( "Cannot open %s: %s\n", options.modem, modem->errstr() );
714 continue;
717 log( "Initializing modem.\n" );
719 // make modem happy
720 modem->print( "\r\r\r\r\r" );
721 while( modem->select( 100, true, false ) )
722 modem->drain();
724 // Send up to nine init strings, in order.
725 int init_count;
726 for( init_count=1; init_count<=9; init_count++ )
728 WvString *this_str;
729 switch( init_count )
731 case 1: this_str = &options.init1; break;
732 case 2: this_str = &options.init2; break;
733 case 3: this_str = &options.init3; break;
734 case 4: this_str = &options.init4; break;
735 case 5: this_str = &options.init5; break;
736 case 6: this_str = &options.init6; break;
737 case 7: this_str = &options.init7; break;
738 case 8: this_str = &options.init8; break;
739 case 9:
740 default:
741 this_str = &options.init9; break;
743 if( !! *this_str )
745 modem->print( "%s\r", *this_str );
746 log( "Sending: %s\n", *this_str );
748 received = wait_for_modem( init_responses, 5000, true );
749 switch( received )
751 case -1:
752 modem->print( "ATQ0\r" );
753 log( "Sending: ATQ0\n" );
754 received = wait_for_modem( init_responses, 500, true );
755 modem->print( "%s\r", *this_str );
756 log( "Re-Sending: %s\n", *this_str );
757 received = wait_for_modem( init_responses, 5000, true );
758 switch( received )
760 case -1:
761 err( "Modem not responding.\n" );
762 return( false );
763 case 1:
764 err( "Bad init string.\n" );
765 return( false );
767 goto end_outer;
768 case 1:
769 err( "Bad init string.\n" );
770 goto end_outer;
775 // Everything worked fine.
776 log( "Modem initialized.\n" );
777 return( true );
779 // allows us to exit the internal loop
780 end_outer:
781 continue;
784 // we tried 3 times and it didn't work.
785 return( false );
789 void WvDialer::del_modem()
791 assert(cloned == modem);
793 if (modem)
795 modem->hangup();
796 WVRELEASE(modem);
797 cloned = NULL;
802 WvModemBase *WvDialer::take_modem()
804 WvModemBase *_modem;
806 if (!modem)
807 init_modem();
809 _modem = modem;
810 cloned = modem = NULL;
812 return _modem;
816 void WvDialer::give_modem(WvModemBase *_modem)
818 del_modem();
819 cloned = modem = _modem;
823 void WvDialer::async_dial()
824 /*************************/
826 int received;
828 if( stat == PreDial2 )
830 // Wait for three seconds and then go to PreDial1.
831 continue_select(3000);
832 stat = PreDial1;
833 return;
836 if( stat == PreDial1 )
838 // Hit enter a few times.
839 for( int i=0; i<3; i++ )
841 modem->write( "\r", 1 );
842 continue_select(500);
843 if (!isok() || !modem)
844 break;
846 stat = Dial;
847 return;
850 if( stat == Dial )
852 // Construct the dial string. We use the dial command, prefix,
853 // area code, and phone number as specified in the config file.
854 WvString *this_str;
855 switch( phnum_count )
857 case 0:
858 this_str = &options.phnum;
859 break;
860 case 1:
861 this_str = &options.phnum1;
862 break;
863 case 2:
864 this_str = &options.phnum2;
865 break;
866 case 3:
867 this_str = &options.phnum3;
868 break;
869 case 4:
870 default:
871 this_str = &options.phnum4;
872 break;
875 WvString s( "%s%s%s%s%s\r", options.dial_cmd,
876 options.dial_prefix,
877 !options.dial_prefix ? "" : ",",
878 options.areacode,
879 *this_str );
880 modem->print( s );
881 log( "Sending: %s\n", s );
882 log( "Waiting for carrier.\n" );
884 stat = WaitDial;
887 received = async_wait_for_modem( dial_responses, true );
889 switch( received )
891 case -1: // nothing -- return control.
892 if( time( NULL ) - last_rx >= 60 )
894 log( WvLog::Warning, "Timed out while dialing. Trying again.\n" );
895 stat = PreDial1;
896 connect_attempts++;
897 dial_stat = 1;
899 //if Attempts in wvdial.conf is 0..dont do anything
900 if(options.dial_attempts != 0)
902 if(check_attempts_exceeded(connect_attempts))
904 hangup();
909 return;
910 case 0: // CONNECT
913 if( chat_mode )
915 if( options.ask_password )
917 err( "Error: dial on demand does not work with option Ask Password = 1\n" );
918 exit( 1 );
921 if( getuid() != 0 ) {
922 err( "Hint: if authentication does not work, start wvdial.dod once as user root\n" );
925 WvPapChap papchap;
926 papchap.put_secret( options.login, options.password, options.remote );
927 if( getuid() == 0 ) {
928 if( papchap.isok_pap() == false ) {
929 err("Warning: Could not modify %s: %s\n",PAP_SECRETS, strerror(errno));
931 if( papchap.isok_chap() == false ) {
932 err("Warning: Could not modify %s: %s\n",CHAP_SECRETS, strerror(errno));
938 if( options.stupid_mode == true || options.tonline == true )
940 if( chat_mode )
942 log( "Carrier detected. Chatmode finished.\n" );
943 exit( 0 );
945 else
947 log( "Carrier detected. Starting PPP immediately.\n" );
948 start_ppp();
951 else
953 log( "Carrier detected. Waiting for prompt.\n" );
954 stat = WaitAnything;
956 return;
957 case 1: // NO CARRIER
958 log( WvLog::Warning, "No Carrier! Trying again.\n" );
959 stat = PreDial1;
960 connect_attempts++;
961 dial_stat = 2;
962 continue_select(2000);
964 //if Attempts in wvdial.conf is 0..dont do anything
965 if(options.dial_attempts != 0)
967 if(check_attempts_exceeded(connect_attempts))
969 hangup();
973 return;
974 case 2: // NO DIALTONE
975 case 3: // NO DIAL TONE
976 if( options.abort_on_no_dialtone == true )
978 err( "No dial tone.\n" );
979 stat = ModemError;
981 else
983 log( "No dial tone. Trying again in 5 seconds.\n" );
984 stat = PreDial2;
985 connect_attempts++;
986 dial_stat = 3;
987 //if Attempts in wvdial.conf is 0..dont do anything
988 if(options.dial_attempts != 0)
990 if(check_attempts_exceeded(connect_attempts))
992 hangup();
996 return;
997 case 4: // BUSY
998 if( options.abort_on_busy == true )
1000 err( "The line is busy.\n" );
1001 stat = ModemError;
1003 else
1005 if( phnum_count++ == phnum_max )
1006 phnum_count = 0;
1007 if( phnum_count == 0 )
1008 log( WvLog::Warning, "The line is busy. Trying again.\n" );
1009 else
1010 log( WvLog::Warning, "The line is busy. Trying other number.\n");
1011 stat = PreDial1;
1012 connect_attempts++;
1013 dial_stat = 4;
1014 continue_select(2000);
1016 return;
1017 case 5: // ERROR
1018 err( "Invalid dial command.\n" );
1019 stat = ModemError;
1020 //if Attempts in wvdial.conf is 0..dont do anything
1021 if(options.dial_attempts != 0)
1023 if(check_attempts_exceeded(connect_attempts))
1025 hangup();
1028 return;
1029 case 6: // VOICE
1030 log( "Voice line detected. Trying again.\n" );
1031 connect_attempts++;
1032 dial_stat = 5;
1033 stat = PreDial2;
1035 //if Attempts in wvdial.conf is 0..dont do anything
1036 if(options.dial_attempts != 0)
1038 if(check_attempts_exceeded(connect_attempts))
1040 hangup();
1044 return;
1045 case 7: // FCLASS
1046 log( "Fax line detected. Trying again.\n" );
1047 connect_attempts++;
1048 dial_stat = 6;
1049 stat = PreDial2;
1050 if(options.dial_attempts != 0)
1052 if(check_attempts_exceeded(connect_attempts))
1054 hangup();
1057 return;
1059 case 8: // NO ANSWER
1060 log( WvLog::Warning, "No Answer. Trying again.\n" );
1061 stat = PreDial1;
1062 connect_attempts++;
1063 dial_stat = 7;
1064 if(options.dial_attempts != 0)
1066 if(check_attempts_exceeded(connect_attempts))
1068 hangup();
1071 continue_select(2000);
1072 return;
1073 default:
1074 err( "Unknown dial response string.\n" );
1075 stat = ModemError;
1076 return;
1081 bool WvDialer::check_attempts_exceeded(int no_of_attempts)
1082 /********************************************************/
1084 if(no_of_attempts > options.dial_attempts)
1086 log( WvLog::Warning, "Maximum Attempts Exceeded..Aborting!!\n" );
1087 return true;
1089 else
1091 return false;
1096 static int set_echo( int desc, int value )
1097 /****************************************/
1099 struct termios settings;
1101 if( isatty( desc ) != 1 )
1102 return 0;
1104 if( tcgetattr (desc, &settings) < 0 )
1106 perror ("error in tcgetattr");
1107 return 0;
1110 if( value )
1111 settings.c_lflag |= ECHO;
1112 else
1113 settings.c_lflag &= ~ECHO;
1115 if( tcsetattr (desc, TCSANOW, &settings) < 0 )
1117 perror ("error in tcgetattr");
1118 return 0;
1121 return 1;
1125 int WvDialer::ask_password()
1126 /**************************/
1128 char tmp[60];
1130 log("Please enter password (or empty password to stop):\n" );
1131 // fflush( stdout ); // kinternet needs this - WvLog should do it
1132 // automagically
1134 set_echo( STDOUT_FILENO, 0 );
1135 fgets( tmp, 50, stdin );
1136 set_echo( STDOUT_FILENO, 1 );
1138 if( tmp[ strlen(tmp)-1 ] == '\n' )
1139 tmp[ strlen(tmp)-1 ] = '\0';
1141 options.password = tmp;
1143 return 1;
1147 void WvDialer::start_ppp()
1148 /************************/
1150 if( chat_mode ) exit(0); // pppd is already started...
1152 WvString addr_colon( "%s:", options.force_addr );
1153 WvString speed( options.baud );
1154 WvString idle_seconds( options.idle_seconds );
1156 const char *dev_str = (const char *)options.modem;
1157 if (!(strncmp(options.modem, "/dev/", 5)))
1158 dev_str += 5;
1161 // open a pipe to access the messages of pppd
1162 if( pipe( pppd_msgfd ) == -1 )
1164 err("pipe failed: %s\n", strerror(errno) );
1165 exit( EXIT_FAILURE );
1167 pppd_log = new WvFDStream( pppd_msgfd[0] );
1168 WvString buffer1("%s", pppd_msgfd[1] );
1171 // open a pipe to pass password to pppd
1172 WvString buffer2;
1173 if (!options.password)
1175 if( pipe( pppd_passwdfd ) == -1 )
1177 err("pipe failed: %s\n", strerror(errno) );
1178 exit( EXIT_FAILURE );
1180 ::write( pppd_passwdfd[1], (const char *) options.password, options.password.len() );
1181 ::close( pppd_passwdfd[1] );
1182 buffer2.append("%s", pppd_passwdfd[0] );
1185 char const *argv_raw[] = {
1186 options.where_pppd,
1187 speed,
1188 "modem",
1189 "crtscts",
1190 "defaultroute",
1191 "usehostname",
1192 "-detach",
1193 "user", options.login,
1194 (!!options.force_addr) ? (const char *)addr_colon : "noipdefault",
1195 options.new_pppd ? "call" : NULL,
1196 options.new_pppd ? "wvdial" : NULL,
1197 options.new_pppd && options.auto_dns ? "usepeerdns" : NULL,
1198 options.new_pppd && options.isdn ? "default-asyncmap" : NULL,
1199 options.new_pppd && (!!options.pppd_option) ? (const char *) options.pppd_option : NULL,
1200 options.new_pppd && options.idle_seconds >= 0 ? "idle" : NULL,
1201 options.new_pppd && options.idle_seconds >= 0 ? (const char *)idle_seconds : NULL,
1202 "logfd", buffer1,
1203 // !!buffer2 ? "passwordfd" : NULL, !!buffer2 ? (const char *)buffer2 : NULL,
1204 NULL
1207 /* Filter out NULL holes in the raw argv list: */
1208 char * argv[sizeof(argv_raw)];
1209 int argv_index = 0;
1210 for (unsigned int i = 0; i < sizeof(argv_raw)/sizeof(char *); i++)
1212 if (argv_raw[i])
1213 argv[argv_index++] = (char *)argv_raw[i];
1215 argv[argv_index] = NULL;
1217 if( access( options.where_pppd, X_OK ) != 0 )
1219 err( "Unable to run %s.\n", options.where_pppd );
1220 err( "Check permissions, or specify a \"PPPD Path\" option "
1221 "in wvdial.conf.\n" );
1222 return;
1226 if (options.dialmessage1.len() || options.dialmessage2.len())
1228 log( WvLog::Notice, "\
1229 ==========================================================================\n");
1230 log( WvLog::Notice, "> %s\n", options.dialmessage1);
1231 if (options.dialmessage2.len())
1232 log( WvLog::Notice, "> %s\n", options.dialmessage2);
1233 log( WvLog::Notice, "\
1234 ==========================================================================\n");
1235 if (options.homepage.len())
1236 log( WvLog::Notice, "Homepage of %s: %s\n",
1237 options.provider.len() ? (const char *)options.provider : "this provider",
1238 options.homepage);
1241 time_t now;
1242 time( &now );
1243 log( WvLog::Notice, "Starting pppd at %s", ctime( &now ) );
1245 // PP - Put this back in, since we're not using passwordfd unless we're
1246 // SuSE... how did this work without this?
1247 WvPapChap papchap;
1248 papchap.put_secret( options.login, options.password, options.remote );
1249 if( papchap.isok_pap() == false )
1251 err( "Warning: Could not modify %s: %s\n"
1252 "--> PAP (Password Authentication Protocol) may be flaky.\n",
1253 PAP_SECRETS, strerror( errno ) );
1255 if( papchap.isok_chap() == false )
1257 err( "Warning: Could not modify %s: %s\n"
1258 "--> CHAP (Challenge Handshake) may be flaky.\n",
1259 CHAP_SECRETS, strerror( errno ) );
1262 ppp_pipe = new WvPipe( argv[0], argv, false, false, false,
1263 modem, modem, modem );
1265 log( WvLog::Notice, "Pid of pppd: %s\n", ppp_pipe->getpid() );
1267 stat = Online;
1268 been_online = true;
1269 connected_at = time( NULL );
1272 void WvDialer::async_waitprompt()
1273 /*******************************/
1275 int received;
1276 const char *prompt_response;
1278 if( options.carrier_check == true )
1280 if( !modem || !modem->carrier() )
1282 err( "Connected, but carrier signal lost! Retrying...\n" );
1283 stat = PreDial2;
1284 return;
1288 received = async_wait_for_modem( prompt_strings, false, true );
1289 if( received >= 0 )
1291 // We have a PPP sequence!
1292 log( "PPP negotiation detected.\n" );
1293 start_ppp();
1295 else if( received == -1 )
1297 // some milliseconds must have passed without receiving anything,
1298 // or async_wait_for_modem() would not have returned yet.
1300 // check to see if we are at a prompt.
1301 // Note: the buffer has been lowered by strlwr() already.
1303 prompt_response = brain->check_prompt( buffer );
1304 if( prompt_response != NULL )
1305 modem->print( "%s\r", prompt_response );
1310 static void strip_parity( char * buf, size_t size )
1311 /*************************************************/
1312 // clear the parity bit on incoming data (to allow 7e1 connections)
1314 while( size-- > 0 )
1316 *buf = *buf & 0x7f;
1317 buf++;
1322 int WvDialer::wait_for_modem( char * strs[],
1323 int timeout,
1324 bool neednewline,
1325 bool verbose )
1326 /***********************************************/
1328 off_t onset;
1329 char * soff;
1330 int result = -1;
1331 int len;
1332 const char *ppp_marker = NULL;
1334 while( modem->select( timeout, true, false ) )
1336 last_rx = time( NULL );
1337 onset = offset;
1338 offset += modem->read( buffer + offset, INBUF_SIZE - offset );
1340 // make sure we do not split lines TOO arbitrarily, or the
1341 // logs will look bad.
1342 while( offset < INBUF_SIZE && modem->select( 100, true, false ) )
1343 offset += modem->read( buffer + offset, INBUF_SIZE - offset );
1345 // Make sure there is a NULL on the end of the buffer.
1346 buffer[ offset ] = '\0';
1348 // Now turn all the NULLs in the middle of the buffer to spaces, for
1349 // easier parsing.
1350 replace_char( buffer + onset, '\0', ' ', offset - onset );
1351 strip_parity( buffer + onset, offset - onset );
1352 replace_char( buffer + onset, '\0', ' ', offset - onset );
1354 if( verbose )
1355 modemrx.write( buffer + onset, offset - onset );
1357 strlwr( buffer + onset );
1359 // Now we can search using strstr.
1360 for( result = 0; strs[ result ] != NULL; result++ )
1362 len = strlen( strs[ result ] );
1363 soff = strstr( buffer, strs[ result ] );
1365 if( soff && ( !neednewline
1366 || strchr( soff, '\n' ) || strchr( soff, '\r' ) ) )
1368 memmove( buffer, soff + len,
1369 offset - (int)( soff+len - buffer ) );
1370 offset -= (int)( soff+len - buffer );
1371 break;
1375 if( strs[ result ] == NULL )
1376 result = -1;
1378 // Search the buffer for a valid menu option...
1379 // If guess_menu returns an offset, we zap everything before it in
1380 // the buffer. This prevents finding the same menu option twice.
1381 ppp_marker = brain->guess_menu( buffer );
1382 if (strs != dial_responses)
1384 if( ppp_marker != NULL )
1385 memset( buffer, ' ', ppp_marker-buffer );
1388 // Looks like we didn't find anything. Is the buffer full yet?
1389 if( offset == INBUF_SIZE )
1391 // yes, move the second half to the first half for next time.
1392 memmove( buffer, buffer + INBUF_SIZE/2,
1393 INBUF_SIZE - INBUF_SIZE/2 );
1394 offset = INBUF_SIZE/2;
1397 if( result != -1 )
1398 break;
1401 buffer[ offset ] = 0;
1402 return( result ); // -1 == timeout
1405 int WvDialer::async_wait_for_modem( char * strs[], bool neednl, bool verbose )
1406 /****************************************************************************/
1408 return( wait_for_modem( strs, 10, neednl, verbose ) );
1411 void WvDialer::reset_offset()
1412 /***************************/
1414 offset = 0;
1415 buffer[0] = '\0';