connection.py :  » Network » Chestnut-Dialer » chestnut-dialer-0.3.3 » chestnut_dialer » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » Network » Chestnut Dialer 
Chestnut Dialer » chestnut dialer 0.3.3 » chestnut_dialer » connection.py
# Copyright (C) 2003, 2004 Konstantin Korikov

#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 2 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software
#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

import os
import pty
import Queue
import time
import random
import signal
import re
import select
import commands
import sys
import chestnut_dialer
from chestnut_dialer import _
from chestnut_dialer import debug_msg
import chestnut_dialer.config

def _uni2ascii(s):
  return s.encode("ascii", "replace")

class Connection:
  STOP_RESON_NORMAL_TERM = 0
  STOP_RESON_UNKNOWN = 1
  STOP_RESON_NO_DIALTONE = 2
  STOP_RESON_BUSY = 3
  STOP_RESON_NO_CARRIER = 4
  STOP_RESON_AUTH_FAILED = 5
  
  account = None
  text_queue = None
  active = None
  connected = None
  stop_reson = None
  pppd_exit_code = 0
  speed = None  
  start_time = None
  linkname = None

  script_noterm_tpl = """
ABORT "%(error_resp)s"
ABORT "%(busy_resp)s"
ABORT "%(nocarrier_resp)s"
ABORT "%(nodialtone_resp)s" 
ABORT "%(noanswer_resp)s"
TIMEOUT %(modem_timeout)s
'' "%(init_cmd)s"
"%(init_resp)s" "%(init2_cmd)s"
"%(init_resp)s" "%(vol_cmd)s"
"%(vol_resp)s" "%(dial_cmd)s%(dial_prefix)s%(phone_number)s"
TIMEOUT %(dial_timeout)s
"%(connect_resp)s" '\\c'
"%(modem_term)s" '\\c'
"""
  script_term_tpl = """
ABORT "%(error_resp)s"
ABORT "%(busy_resp)s"
ABORT "%(nocarrier_resp)s"
ABORT "%(nodialtone_resp)s" 
ABORT "%(noanswer_resp)s"
ABORT 'Invalid Login'
ABORT 'Login incorrect'
TIMEOUT %(modem_timeout)s
'' "%(init_cmd)s"
"%(init_resp)s" "%(init2_cmd)s"
"%(init_resp)s" "%(vol_cmd)s"
"%(vol_resp)s" "%(dial_cmd)s%(dial_prefix)s%(phone_number)s"
TIMEOUT %(dial_timeout)s
"%(connect_resp)s" ''
'%(chat_login_prompt)s--%(chat_login_prompt)s' '%(user)s'
'%(chat_passwd_prompt)s' '%(passwd)s'
TIMEOUT %(ppp_timeout)s
'~--' ''
"""
  script_auto_tpl = """
import sys
sys.path.append('%(path)s')
from chestnut_dialer.chat import Chat
chat = Chat()
r = chat.chat('-e',
  'ABORT', '%(error_resp)s',
  'ABORT', '%(busy_resp)s',
  'ABORT', '%(nocarrier_resp)s',
  'ABORT', '%(nodialtone_resp)s',
  'ABORT', '%(noanswer_resp)s',
  'TIMEOUT', '%(modem_timeout)s',
  '', '%(init_cmd)s',
  '%(init_resp)s', '%(init2_cmd)s',
  '%(init_resp)s', '%(vol_cmd)s',
  '%(vol_resp)s', '%(dial_cmd)s%(dial_prefix)s%(phone_number)s',
  'TIMEOUT', '%(dial_timeout)s',
  '%(connect_resp)s', '',
  '%(modem_term)s', '')
if r != 0: sys.exit(r)
r = chat.chat('-e',
  'ABORT', '%(error_resp)s',
  'TIMEOUT', '%(ppp_timeout)s',
  '~--', '')
if r != 0:
  r = chat.chat('-e',
    'ABORT', '%(error_resp)s',
    'ABORT', 'Invalid Login',
    'ABORT', 'Login incorrect',
    'TIMEOUT', '%(prompt_timeout)s'
    '%(chat_login_prompt)s--%(chat_login_prompt)s', '%(user)s',
    '%(chat_passwd_prompt)s', '@%(passwd_file)s',
    'TIMEOUT', '%(ppp_timeout)s',
    '~--', '')   
sys.exit(r)
"""
  script_callback_answer_tpl = """
ABORT "%(error_resp)s" 
TIMEOUT %(modem_timeout)s 
'' "%(callback_init_cmd)s"
"%(init_resp)s" "%(callback_init2_cmd)s"
"%(init_resp)s" "%(vol_cmd)s"
"%(vol_resp)s" ''
TIMEOUT %(dial_timeout)s
SAY '\\nWaiting for incoming call...'
"%(ring_resp)s" "%(answer_cmd)s"
"%(connect_resp)s" ''
TIMEOUT %(ppp_timeout)s
'~--' ''
"""
  script_dialin_tpl = """
ABORT "%(error_resp)s"
ABORT "%(nocarrier_resp)s"
ABORT "%(nodialtone_resp)s" 
TIMEOUT %(modem_timeout)s
'' "%(init_cmd)s"
"%(init_resp)s" "%(init2_cmd)s"
"%(init_resp)s" "%(vol_cmd)s"
"%(vol_resp)s" ''
TIMEOUT 3600
SAY '\\nWaiting for incoming call...'
"%(ring_resp)s" "%(answer_cmd)s"
TIMEOUT %(dial_timeout)s
"%(connect_resp)s" '\\c'
"%(modem_term)s" '\\c'
"""

  _linkname_re = re.compile(r'Using interface (.*)', re.I)
  _auth_failed_re = re.compile(
    r'(authentication failed|Invalid Login|Login incorrect)', re.I)
  
  _ifconfig_info_re = [
    re.compile(r'^\s*inet addr:(?P<local_ip>\d+\.\d+\.\d+\.\d+)\s+' +
      r'P-t-P:(?P<remote_ip>\d+\.\d+\.\d+\.\d+)\s+' +
      r'Mask:(?P<netmask>\d+\.\d+\.\d+\.\d+)', re.M | re.I),
    re.compile(r'^\s*RX (?P<rx>packets.*)$', re.M),
    re.compile(r'^\s*TX (?P<tx>packets.*)$', re.M),
    re.compile(r'^\s*RX bytes:(?P<rx_bytes>\d+ \(.*?\))\s+' +
      r'TX bytes:(?P<tx_bytes>\d+ \(.*?\))', re.M | re.I)]
    
  def __init__(self, account, text_queue,
      pppd_cbcp_with_auto_answer = 0,
      write_dns_to_resolv_conf = 1):
    self.account = account
    self.text_queue = text_queue
    self._pppd_cbcp_with_auto_answer = pppd_cbcp_with_auto_answer
    self._write_dns_to_resolv_conf = write_dns_to_resolv_conf
  def __del__(self):
    self.stop()  
  def start(self):
    self.stop()
    self.stop_reson = None
    account = self.account
    self._phone_num_index = -1
    self._attempt_count = account['redial_attempts']
    if not account['auth_type']:
      account['auth_type'] = "pap/chap"
    if len(account['phone_numbers']) == 0:
      account['phone_numbers'] = ['NONUMBER']
    self._callback_answer_mode = 0
    self._connection_init()
  def _make_temp_file(self, perm = 0600):
    file_name = os.tempnam()
    f = None
    try:
      f = open(file_name, "w")
      f.close()
    except IOError: 
      debug_msg(_("cannot create temp file '%s'") % file_name, 1)
      file_name = None
    else:
      try:
  os.chmod(file_name, perm)
  f = open(file_name, "w")  
      except OSError: 
  debug_msg(_("cannot chagne file permitions on '%s'") % file_name, 2)
    return (file_name, f)
  def _connection_init(self):
    account = self.account
    if not self._callback_answer_mode:
      self._phone_num_index = (self._phone_num_index + 1) % len(account['phone_numbers'])
      if self._attempt_count >= 0: self._attempt_count -= 1
    self.speed = None
    self.linkname = None
    self._chat_temp_file = None
    self._passwd_temp_file = None
    self._passwdfdr = None
    
    # init common ppp options
    po = []
    if account["device"]:
      po += [_uni2ascii(account["device"])]
    if account["speed"]:
      po += [_uni2ascii(unicode(account["speed"]))]
    if account['lock_device']: po += ["lock"]
    if account['modem']: po += ["modem"]
    else: po += ["local"]
    if account['flow_control'] != 'no-change':
      po += [_uni2ascii(account['flow_control'])]
    po += ["asyncmap", "00000000"]
    if account['default_route']: po += ["defaultroute"]      
    if not len(account['dns_servers']): po += ["usepeerdns"]
    po += ["mtu", str(account["mtu"])]
    po += ["mru", str(account["mru"])]
    if account["ip"] or account["remote"]: 
      po += [_uni2ascii(account["ip"] + ":" + account["remote"])]
    if account["mask"]: po += ["netmask", _uni2ascii(account['mask'])]
    po += ["ipparam", "ppp", "nodetach", "logfd", "1"]
    
    # routine to escape chat parameters
    def escape_quotes(s):
      "Escape unescaped quotes"
      ret, i = ("", 0)
      while i < len(s):
        if s[i] == '\\':
          ret += s[i:i+2]
          i += 1
        elif s[i] in '"\'':
          ret += '\\' + s[i]
        else:
          ret += s[i]
        i += 1
      return ret

    escape_param = escape_quotes
    # chat templete
    chat_tpl = ""

    # determine what chat templete to use and
    # check if we need to escape chat parameters
    if self._callback_answer_mode:
      chat_tpl = self.script_callback_answer_tpl
    elif account['use_script'] == 'predef-noterm':
      chat_tpl = self.script_noterm_tpl
    elif account['use_script'] == 'predef-auto':
      escape_param = lambda s: escape_quotes(s).replace("\\", "\\\\").replace("'", "\\'")
      chat_tpl = self.script_auto_tpl
    elif account['use_script'] == 'predef-term':
      chat_tpl = self.script_term_tpl
    elif account['use_script'] == 'custom':
      chat_tpl = account['chat_script']
    elif account['use_script'] == 'dialin':
      chat_tpl = self.script_dialin_tpl

    # init chat parameters
    chat_params = {}
    if chat_tpl != "":
      for p in account.keys():
        chat_params[p] = escape_param(_uni2ascii(unicode(account[p])))
      chat_params.update({"vol_cmd": escape_param(
        _uni2ascii(account[("mute_cmd", "low_vol_cmd",
          "max_vol_cmd")[account["volume_setting"]]]))})
      if not self._callback_answer_mode:
        chat_params.update({"path": chestnut_dialer.config.path})
        chat_params.update({"phone_number":
          escape_param(_uni2ascii(
            account['phone_numbers'][self._phone_num_index]))})

    # init authentication parameters
    if account['remotename'] != '':
      po += ["remotename", _uni2ascii(account['remotename'])]
    if account['auth_type'] == 'pap/chap':
      po += ["user", _uni2ascii(account["user"])]
      if account['use_passwordfd']:
        self._passwdfdr, passwdfdw  = os.pipe()
  os.write(passwdfdw, _uni2ascii(account["passwd"]))
  os.close(passwdfdw)
  po += ["call", "chestnut-dialer",
            "passwordfd", str(self._passwdfdr)]

    # chack if we need to write password to temporary file
    if (chat_tpl != "" and account['use_script'] == 'predef-auto' and
        not self._callback_answer_mode):
      self._passwd_temp_file, f = self._make_temp_file(0600)
      if f: 
        f.write(_uni2ascii(account["passwd"]))
  f.close()
        chat_params.update({"passwd_file": self._passwd_temp_file})

    # write chat to the temporary file
    if chat_tpl != "":
      self._chat_temp_file, f = self._make_temp_file(0600)
      if f: 
        f.write(chat_tpl % chat_params)
        f.close()

    # init connect command
    if self._chat_temp_file:
      if (account['use_script'] == 'predef-auto' and
          not self._callback_answer_mode):
        po += ["connect",
          chestnut_dialer.config.python + " " + self._chat_temp_file]
      else:
        po += ["connect", "%s -t %d -e -f %s" %
          (chestnut_dialer.config.chat,
              account["dial_timeout"], self._chat_temp_file)]

    if (account["callback"] and 
        not self._callback_answer_mode): 
      po += ["callback", _uni2ascii(account['callback_phone_number'])]
    
    # init user's ppp options
    s = _uni2ascii(account['ppp_options'])
    i = 0; a = ""
    try:
      while 1:
        while s[i].isspace(): i += 1
        if s[i] == '"':
          i += 1
          while 1:
            if s[i] == "\\":
              i += 1
              if s[i] in ('"', "\\"): a += s[i]
              else: a += "\\" + s[i]
            elif s[i] == '"': break
            else: a += s[i]
            i += 1
        elif s[i] == "'":
          i += 1
          while s[i] != "'":
            a += s[i]; i += 1
        else:
          while not s[i].isspace():
            a += s[i]; i += 1
        po += [a]; a = ""; i += 1
    except IndexError:
      if a != "": po += [a]
    debug_msg(_("PPP options: %s") % str(po), 3)
    
    self._pppd, self._pppd_pty = pty.fork()
    if self._pppd < 0:
      debug_msg(_("fork() failed"), 1)
    elif self._pppd == 0:
      os.execv(chestnut_dialer.config.pppd, ["pppd"] + po)
    debug_msg(_("pppd pid %d") % self._pppd, 3)
    self._no_dialtone = None
    self._busy = None
    self._no_carrier = None
    self._auth_failed = None
    self._speed_re = re.compile(
        re.escape(_uni2ascii(account['connect_resp'])) +
        r'\s+(\d+)(.*)')    
    self._interface_up = 0
    self._pppd_chars = ""
    self.active = 1
    self.connected = 0
    self._resolv_dns = []
  def _is_pppd_live(self):
    try: 
      pid, code = os.waitpid(self._pppd, os.WNOHANG)
    except OSError: return 0
    if pid != 0 and pid == self._pppd:
      self.pppd_exit_code = code / 256
      return 0
    return 1
  def iteration(self):
    account = self.account    
    if not self._interface_up:
      pppd_kiled = 0
      buf = ""
      t = time.time()      
      while time.time() - t < 0.1:
        if select.select([self._pppd_pty], [], [], 0.02)[0]:
          try: buf += os.read(self._pppd_pty, 1)
    except OSError: break
        else:   
    break
      if buf == "" and not self._is_pppd_live(): pppd_kiled = 1
      if len(buf):
        buf = buf.replace("\r", "") 
  self.text_queue.put(buf)
  buf = self._pppd_chars + buf
  lines = buf.split("\n")
  self._pppd_chars = lines[-1]
  lines[-1:] = []
  for s in lines:    
          if not self.speed:
      m = self._speed_re.match(s)
      if m: 
        if int(m.group(1)) < account['min_speed']:
    debug_msg(_("speed < minimum speed, reconnecting"), 3)
    os.kill(self._pppd, signal.SIGTERM)
    t = time.time()
    while self._is_pppd_live():
      if time.time() - t > 3:
        try: os.kill(self._pppd, signal.SIGKILL)
        except OSError: pass
        break
                  time.sleep(0.1)
        else:
    self.speed = m.group(1) + m.group(2)    
        continue
    if not self._no_dialtone and s == account['nodialtone_resp']:
      self._no_dialtone = 1
      continue
    if not self._busy and s == account['busy_resp']:
      self._busy = 1
      continue    
    if not self._no_carrier and s == account['nocarrier_resp']:
      self._no_carrier = 1
      continue
    if not self._auth_failed and self._auth_failed_re.search(s):
      self._auth_failed = 1
      continue    
    if not self.linkname:
      m = self._linkname_re.match(s)
            if m: 
        self.linkname = m.group(1)
        continue
      if self.linkname and re.search(r'^[ \t]+UP',
    commands.getoutput(
      "%s %s 2> /dev/null" % (chestnut_dialer.config.ifconfig, self.linkname)), re.M):
  self._interface_up = 1
  self.start_time = int(time.time())  
  debug_msg(_("connected"), 3)
        self._callback_answer_mode = 0
  if self._write_dns_to_resolv_conf:
    if account['dns_servers']:
            self._resolv_dns = map(lambda s: _uni2ascii(s),
                account['dns_servers'])
    else:
      try:
              f = open(chestnut_dialer.config.ppp_resolv_conf)
      except IOError: 
        debug_msg(_("cannot open '%s' for reading") %
                  chestnut_dialer.config.ppp_resolv_conf, 2)
      else:
        r = re.compile(r'nameserver\s+((\d+\.){3}\d+).*')
        for s in f.readlines():
    m = r.match(s)
    if m:
      self._resolv_dns.append(m.group(1))      
        f.close()
    if self._resolv_dns:
      debug_msg(_("writing to resolv.conf"), 3)
            try:
              f = open(chestnut_dialer.config.resolv_conf, "a")          
      except IOError:
        debug_msg(_("cannot open '%s' for appending") %
                  chestnut_dialer.config.resolv_conf, 2)
      else:
        f.write("\n")
              for dns in self._resolv_dns:
    f.write("nameserver %s # %s TEMP ENTRY\n" % 
      (dns, chestnut_dialer.program_name.upper()))
        f.close()
  self.connected = 1
  if account['connect_program']:
          os.system(account['connect_program'])
      if not pppd_kiled: return
    if self._chat_temp_file:
      debug_msg(_("removing chat temp file"), 3)
      os.unlink(self._chat_temp_file)
      self._chat_temp_file = None
    if self._passwd_temp_file:
      debug_msg(_("removing password temp file"), 3)
      os.unlink(self._passwd_temp_file)
      self._passwd_temp_file = None
    if self._is_pppd_live(): return
    os.close(self._pppd_pty)
    if self._passwdfdr:
      try: os.close(self._passwdfdr)
      except OSError: pass
    debug_msg(_("disconnected, pppd exit code %d") %
      self.pppd_exit_code, 3)
    
    if (self.pppd_exit_code == 14 and
        account['callback'] and 
        not self._pppd_cbcp_with_auto_answer and
        self.stop_reson == None and
        not self._callback_answer_mode):
      debug_msg(_("going into callback answer mode"), 3)
      self._callback_answer_mode = 1
      self._connection_init()
      return
    self._callback_answer_mode = 0
    
    if self._write_dns_to_resolv_conf and self._resolv_dns:
      debug_msg(_("restoring resolv.conf"), 3)
      try:
  f = open(chestnut_dialer.config.resolv_conf, "r+")
      except IOError:
  debug_msg(_("cannot open '%s' for reading and writing") %
            chestnut_dialer.config.resolv_conf, 2)
      else:
  txt = f.read()
  txt = re.sub(r'(?m)^.*# %s TEMP ENTRY$' % chestnut_dialer.program_name.upper(), "", txt)
  txt = re.sub(r'\n+', "\n", txt)
  f.seek(0)
  f.truncate(0)  
  f.write(txt)  
  f.close()
    if self.connected and account['disconnect_program']:
      os.system(account['disconnect_program'])      
    if self.stop_reson == None:
      self.stop_reson = (self._no_dialtone and self.STOP_RESON_NO_DIALTONE
            ) or (self._busy and self.STOP_RESON_BUSY
      ) or (self._no_carrier and self.STOP_RESON_NO_CARRIER
      ) or ((self._auth_failed or
              self.pppd_exit_code in (11, 19)) and self.STOP_RESON_AUTH_FAILED
      ) or self.STOP_RESON_UNKNOWN    
    if (self.stop_reson != self.STOP_RESON_NORMAL_TERM and
        self.pppd_exit_code != 2):
      if (self.stop_reson != self.STOP_RESON_NO_DIALTONE or
          'no-dialtone' in account['redial_if']) and (
          self.stop_reson != self.STOP_RESON_AUTH_FAILED or
          'auth-fail' in account['redial_if']):
        if ((not self.connected and self._attempt_count != 0) or
            (self.connected and account['redial_auto'])):
          self.connected = 0
          self.stop_reson = None
          self._connection_init()
          return
    self.connected = 0
    self.active = 0
  def stop(self):
    if not self.is_active(): return
    self.stop_reson = self.STOP_RESON_NORMAL_TERM
    os.kill(self._pppd, signal.SIGTERM)
    second_sig = 0
    loop_count = 0
    while self.active:
      time.sleep(0.1)
      if loop_count > 25:
        if not second_sig:
    try: os.kill(self._pppd, signal.SIGKILL)
    except OSError: pass
    second_sig = 1
  else:
    if loop_count > 80:
      debug_msg(_("unable to kill pppd"), 1)
      break
      self.iteration()
      loop_count += 1
  def is_active(self):
    return self.active
  def is_connected(self):
    return self.connected
  def get_stop_reson(self):
    return self.stop_reson
  def get_pppd_exit_code(self):
    return self.pppd_exit_code
  def get_info(self):
    t = int(time.time()) - self.start_time
    info = {
      'account_name': self.account['name'],
      'speed': self.speed,
      'time': "%02d:%02d:%02d" % (t / 3600, (t / 60) % 60, t % 60),
      'interface': self.linkname}
    output = commands.getoutput(chestnut_dialer.config.ifconfig + 
        " " + self.linkname)
    for r in self._ifconfig_info_re:
      m = r.search(output)
      if m:
  grps = m.groupdict("");
  for g in grps.keys():
    info.update({g:grps[g]})
    return info
    
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.