📄 wvdialer.cc
字号:
/* * Worldvisions Weaver Software: * Copyright (C) 1997-2003 Net Integration Technologies, Inc. * * Implementation of the WvDialer smart-dialer class. * */#include "wvdialer.h"#include "wvver.h"#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <signal.h>#include <termios.h>#include <time.h>#include <unistd.h>#include <ctype.h>#include <errno.h>#include <assert.h>#include <xplc/xplc.h>static char * init_responses[] = { "ok", "error", NULL};static char * dial_responses[] = { "connect", "no carrier", "no dialtone", "no dial tone", "busy", "error", "voice", "fclass", "no answer", NULL};static char * prompt_strings[] = { "}!}", "!}!", NULL};static int messagetail_pid = 0;//**************************************************// WvDialer Public Functions//**************************************************WvDialer::WvDialer( WvConf &_cfg, WvStringList *_sect_list, bool _chat_mode )/***************************************************************************/: WvStreamClone( 0 ), cfg(_cfg), log( "WvDial", WvLog::Debug ), err( log.split( WvLog::Error ) ), modemrx( "WvDial Modem", WvLog::Debug ){ ppp_pipe = NULL; pppd_log = NULL; been_online = false; stat = Idle; offset = 0; prompt_tries = 0; last_rx = last_execute = 0; prompt_response = ""; auto_reconnect_delay = 0; auto_reconnect_at = 0; connected_at = 0; phnum_count = 0; phnum_max = 0; // tell wvstreams we need our own subtask uses_continue_select = true; brain = NULL; modem = NULL; sect_list = _sect_list; chat_mode = _chat_mode; log("WvDial: Internet dialer version " WVDIAL_VER_STRING "\n"); // Ensure all sections in sect_list actually exist, warning if not. WvStringList::Iter iter(*sect_list); for(iter.rewind(); iter.next();) { if(cfg[*iter] == NULL) { err(WvLog::Warning, "Warning: section [%s] does not exist in wvdial.conf.\n", *iter); } } // Ensure all inherited sections exist, warning if not. WvConfigSectionList::Iter iter2 (cfg); for (iter2.rewind(); iter2.next();) { WvConfigSection & sect2 = *iter2; WvConfigEntry * entry = sect2["Inherits"]; if (entry) { WvString inherits = entry->value; if (cfg[inherits] == NULL) err( WvLog::Warning, "Warning: inherited section [%s] does not exist in wvdial.conf\n", inherits); } } // Activate the brain and read configuration. brain = new WvDialBrain(this); // init_modem() reads the config options. It MUST run here! if(!init_modem()) { // init_modem() printed an error stat = ModemError; return; } if (options.provider.len()) { log( WvLog::Notice, "Dialing %s %s.\n", options.provider, options.product); } if (options.homepage.len()) { log(WvLog::Notice, "Homepage of %s: %s\n", options.provider.len() ? options.provider.cstr() : "this provider", options.homepage); } if(options.auto_reconnect && options.idle_seconds > 0) { err(WvLog::Notice, "Idle Seconds = %s, disabling automatic reconnect.\n", options.idle_seconds); options.auto_reconnect = false; } pppd_mon.setdnstests(options.dnstest1, options.dnstest2); pppd_mon.setcheckdns(options.check_dns); pppd_mon.setcheckdfr(options.check_dfr);}WvDialer::~WvDialer()/*******************/{ terminate_continue_select(); WVRELEASE(ppp_pipe); WVRELEASE(pppd_log); delete brain;}bool WvDialer::dial()/*******************/// Returns false on error, or true to go asynchronous while dialing.{ if(stat == Online) return(true); if(stat != Idle) { // error message has already been printed elsewhere return(false); } if (!options.phnum) { err( "Configuration does not specify a valid phone number.\n" ); stat = OtherError; } if (!options.login) { err( "Configuration does not specify a valid login name.\n" ); stat = OtherError; } if (!options.password) { err( "Configuration does not specify a valid password.\n" ); stat = OtherError; } if( stat != Idle ) return( false ); phnum_max = 0; if(options.phnum1.len()) { phnum_max++; if(options.phnum2.len()) { phnum_max++; if(options.phnum3.len()) { phnum_max++; if(options.phnum4.len()) phnum_max++; } } } // we need to re-init the modem if we were online before. if(been_online && !init_modem()) stat = ModemError; else { stat = Dial; connect_attempts = 1; dial_stat = 0; brain->reset(); } return(true);}void WvDialer::hangup()/*********************/{ WVRELEASE(ppp_pipe); if( !chat_mode ) pppd_watch( 250 ); if( stat != Idle ) { time_t now; time( &now ); log( "Disconnecting at %s", ctime( &now ) ); del_modem(); stat = Idle; } if (messagetail_pid > 0) { kill(messagetail_pid, 15); messagetail_pid = 0; }}bool WvDialer::pre_select( SelectInfo& si )/*******************************************/{ if( isok() && stat != Online && stat != Idle && time( NULL ) - last_execute > 1 ) { // Pretend we have "data ready," so execute() gets called. // select() already returns true whenever the modem is readable, // but when we are doing a timeout (eg. PreDial1/2) for example, // we need to execute() even if no modem data is incoming. return( true ); } else { return WvStreamClone::pre_select( si ); }}bool WvDialer::isok() const/*************************/{ bool b = ( !modem || modem->isok() ) && stat != ModemError && stat != OtherError; /* if (!b) err("Returning not ok!!\n" ); */ return( b );}char *WvDialer::connect_status() const/*************************************/{ static char msg[ 160 ]; switch( stat ) { case PreDial2: case PreDial1: case Dial: case WaitDial: if( dial_stat == 1 ) strcpy( msg, "Last attempt timed out. Trying again." ); else if( dial_stat == 2 ) strcpy( msg, "Modem did not connect last attempt. Trying again." ); else if( dial_stat == 3 ) strcpy( msg, "No dial tone last attempt. Trying again." ); else if( dial_stat == 4 ) strcpy( msg, "Busy signal on last attempt. Trying again." ); else if( dial_stat == 5 ) strcpy( msg, "Voice answer on last attempt. Trying again." ); else if( dial_stat == 6 ) strcpy( msg, "Fax answer on last attempt. Trying again." ); else if( dial_stat == 7 ) strcpy( msg, "No answer on last attempt. Trying again." ); else return( NULL ); break; case WaitAnything: strcpy( msg, "Waiting for a response from Internet Provider." ); break; case WaitPrompt: strcpy( msg, "Waiting for a prompt from Internet Provider." ); break; case AutoReconnectDelay: sprintf( msg, "Next attempt in 00:%02ld:%02ld.", ( auto_reconnect_at - time( NULL ) ) / 60, ( auto_reconnect_at - time( NULL ) ) % 60 ); break; default: return( NULL ); } return( msg );}void WvDialer::pppd_watch( int ms )/*********************************/{ // see if pppd has a message, analyse it and output to log if( pppd_log != NULL && pppd_log->isok() ) { char *line; while ( (line = pppd_log->blocking_getline( ms )) ) { WvString buffer1(pppd_mon.analyse_line( line )); if (!!buffer1) { log("pppd: %s\n", buffer1); } } }}void WvDialer::execute()/**********************/{ WvStreamClone::execute(); // the modem object might not exist, if we just disconnected and are // redialing. if( !modem && !init_modem() ) return; last_execute = time( NULL ); if( !chat_mode ) pppd_watch( 100 ); switch( stat ) { case Dial: case WaitDial: case PreDial1: case PreDial2: async_dial(); break; case WaitAnything: // we allow some time after connection for silly servers/modems. if( modem->select( 500, true, false ) ) { // if any data comes in at all, switch to impatient mode. stat = WaitPrompt; last_rx = time( NULL ); } else if( time( NULL ) - last_rx >= 30 ) { // timed out - do what WaitPrompt would do on a timeout. stat = WaitPrompt; } else { // We prod the server with a CR character every once in a while. // FIXME: Does this cause problems with login prompts? modem->write( "\r", 1 ); } break; case WaitPrompt: async_waitprompt(); break; case Online: assert( !chat_mode ); // If already online, we only need to make sure pppd is still there. if( ppp_pipe && ppp_pipe->child_exited() ) { int pppd_exit_status = ppp_pipe->exit_status(); if( ppp_pipe->child_killed() ) { log( WvLog::Error, "PPP was killed! (signal = %s)\n", ppp_pipe->exit_status() ); } // we must delete the WvModem object so it can be recreated // later; starting pppd seems to screw up the file descriptor. hangup(); del_modem(); time_t call_duration = time( NULL ) - connected_at; if( pppd_mon.auth_failed() ) { log("Authentication error.\n" "We failed to authenticate ourselves to the peer.\n" "Maybe bad account or password?\n" ); } else { WvString msg = ""; switch (pppd_exit_status) { case 2: msg = "pppd options error"; break; case 3: msg = "No root priv error"; break; case 4: msg = "No ppp module error"; break; case 10: msg = "PPP negotiation failed"; break; case 11: msg = "Peer didn't authenticatie itself"; break; case 12: msg = "Link idle: Idle Seconds reached."; break; case 13: msg = "Connect time limit reached."; break; case 14: msg = "Callback negotiated, call should come back."; break; case 15: msg = "Lack of LCP echo responses"; break; case 17: msg = "Loopback detected"; break; case 19: msg = "Authentication error.\n" "We failed to authenticate ourselves to the peer.\n" "Maybe bad account or password?"; break; } if (msg.len()) { // Note: exit code = %s is parsed by kinternet: log("The PPP daemon has died: %s (exit code = %s)\n", msg, pppd_exit_status); log("man pppd explains pppd error codes in more detail.\n"); err(WvLog::Notice, "I guess that's it for now, exiting\n"); if (pppd_exit_status == 12 && options.auto_reconnect) err(WvLog::Notice, "Idle parameter is passed to pppd\n" "If you don't want an idle timeout per default,\n" "comment out the idle parameter in /etc/ppp/options\n"); if (pppd_exit_status == 15) { log("Provider is overloaded(often the case) or line problem.\n"); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -