# 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 getopt
import os
import sys
import atexit
import signal
import re
import chestnut_dialer
from chestnut_dialer import _
from chestnut_dialer import debug_msg
import chestnut_dialer.config
from chestnut_dialer.account_set import Account
app = None
general_options = None
commands = None
options = None
def init():
chestnut_dialer.init_locale()
global _
_ = chestnut_dialer._
global general_options
general_options = [
("i", _("ui=uiname"), _("specifies the user interface")),
("", _("ui-param=name:value"), _("passes a parameter to the ui")),
("", _("debug=integer"), _("set debug level: 0 - only fatal errors will be output")),
("", "no-headers", _("print no header line at all")),
("", "daemon", _("detach from the console"))]
global commands
commands = [
("d", "dial", _("dial")),
("s", "disconnect", _("disconnect")),
("e", "erase", _("delete the account")),
("c", "create", _("create a new account")),
("m", "modify", _("modify the existing account")),
("p", "print", _("print the account")),
("", "print-full", _("print the account with all default values")),
("l", "list-accounts", _("print account list")),
("", "list-ui", _("print list of installed user interfaces")),
("", "list-ui-params", _("print list of params handled by user interface")),
("", "list-acc-attrs", _("print list of all account attributes")),
("q", "quit", _("disconnect and terminate existing process")),
("?", "help", _("print this screen")),
("", "version", _("print the program name and version"))]
global options
options = [
("a", _("account=integer"), _("selects the account by its ID")),
("n", _("account-name=name"), _("selects the account by its name"))]
def print_usage():
def format_opt_aliases(short, long):
o = []
if (short): o += ["-" + short]
if (long): o += ["--" + long]
return ", ".join(o)
try:
import textwrap # available in Python >= 2.3
except ImportError:
def format_option(s, l, d):
return u" %-25s %s" % (format_opt_aliases(s, l), d)
else:
wrapper = textwrap.TextWrapper(subsequent_indent="".center(28))
def format_option(s, l, d):
wrapper.initial_indent = u" %-25s " % format_opt_aliases(s, l)
return wrapper.fill(d)
print (_("""Usage: %s [basic options] [command [options]
[attr_name=value | unset attr_name]...]""") %
chestnut_dialer.exename)
print
print _("Basic options:")
for short, long, desc in general_options:
print format_option(short, long, desc)
print
print _("Commands:")
for short, long, desc in commands:
print format_option(short, long, desc)
print
print _("Options:")
for short, long, desc in options:
print format_option(short, long, desc)
print
print _("Report bugs to <%s>.") % chestnut_dialer.config.bugreport
def print_account(account):
attr_type = account.get_attr_type_dict()
account = account.copy()
keys = account.keys()
keys.sort()
for attr in keys:
value = account[attr]
if value != None:
atype = attr_type[attr]
if attr == "passwd":
value = _("<hidden>")
elif atype == 'boolean':
value = value and "on" or "off"
elif atype == 'list':
value = ",".join(value)
print "%-24s = %s" % (attr, value)
def signal_term(signum, stackframe):
debug_msg(_("interrupted by signal %d") % signum, 2)
app.exit()
def signal_disconnect(signum, stackframe):
app.disconnect()
class TempAccount(Account):
_data = None
def __init__(self):
Account.__init__(self)
self._data = {}
def _get_acc_attr(self, name):
try: return self._data[name]
except KeyError: return None
def _set_acc_attr(self, name, value):
self._data.update({name:value})
#### main ####
init()
short_opts = ""
long_opts = []
# construct arguments for getopt.getopt
for a in (general_options, commands, options):
for short, long, desc in a:
i = long.find("=")
if short: short_opts += short + (i != -1 and ":" or "")
long_opts += [i != -1 and long[0:i + 1] or long]
# try to get options and arguments
try: opts, args = getopt.getopt(sys.argv[1:], short_opts, long_opts)
except getopt.GetoptError, e:
debug_msg(str(e), 0)
sys.exit(chestnut_dialer.EX_USAGE)
sys.argv[1:] = []
command = None # command option
account_id = None # account identifier
account_name = None # account name
ui = None # user interface name
ui_params = {} # user interface parameters
acc_attrs = {} # account attributes
no_headers = 0 # print no header line at all
daemon = 0 # detach from console
# Parse options
for o, v in opts:
if o in ("-i", "--ui"): ui = v
elif o == "--ui-param":
m = re.match(r'^(\w+)(:(.*))?$', v)
if m: ui_params.update({m.group(1):m.group(3)})
else:
debug_msg(_("syntax error in --ui-param option"), 0)
sys.exit(chestnut_dialer.EX_USAGE)
elif o == "--debug":
chestnut_dialer.set_debug_level(int(v))
elif o in ("-d", "--dial"): command = "dial"
elif o in ("-s", "--disconnect"): command = "disconnect"
elif o in ("-e", "--erase"): command = "erase"
elif o in ("-c", "--create"): command = "create"
elif o in ("-m", "--modify"): command = "modify"
elif o in ("-p", "--print"): command = "print"
elif o == "--print-full": command = "print-full"
elif o in ("-l", "--list-accounts"): command = "list-accounts"
elif o == "--list-ui": command = "list-ui"
elif o == "--list-ui-params": command = "list-ui-params"
elif o == "--list-acc-attrs": command = "list-acc-attrs"
elif o in ("-q", "--quit"): command = "quit"
elif o in ("-?", "--help"): command = "help"
elif o == "--version": command = "version"
elif o in ("-a", "--account"):
try: account_id = int(v)
except ValueError:
debug_msg(_("account id must have an integer numeric value"), 0)
sys.exit(chestnut_dialer.EX_USAGE)
elif o in ("-n", "--account-name"):
account_name = unicode(v, chestnut_dialer.locale_encoding)
elif o == "--no-headers": no_headers = 1
elif o == "--daemon": daemon = 1
# Parse arguments
unset = 0
for token in args:
i = token.find("=")
if i != -1:
if unset:
debug_msg(_("syntax error in account attributes"), 0)
sys.exit(chestnut_dialer.EX_USAGE)
attr = token[0:i]
val = token[i+1:]
value = None
atype = Account.meta.get_attr_type(attr)
if atype in ('string', 'text'):
value = unicode(val, chestnut_dialer.locale_encoding)
elif atype == 'integer':
try: value = int(val)
except ValueError:
debug_msg(_("invalid value '%s' for an integer attribute") % val, 0)
sys.exit(chestnut_dialer.EX_ARGSERR)
elif atype == 'boolean':
if val.lower() in ("0", "no", "false", "disable", "off"): value = 0
elif val.lower() in ("1", "yes", "true", "enable", "on"): value = 1
else:
debug_msg(_("invalid value '%s' for an boolean attribute") % val, 0)
sys.exit(chestnut_dialer.EX_ARGSERR)
elif atype == 'list':
value = val.split(",")
else:
debug_msg(_("invalid account attribute '%s'") % attr, 0)
sys.exit(chestnut_dialer.EX_ARGSERR)
acc_attrs.update({attr: value})
elif token == "unset": unset = 1
elif unset == 1:
if not Account.meta.has_key(token):
debug_msg(_("invalid account attribute '%s'") % token, 0)
sys.exit(chestnut_dialer.EX_ARGSERR)
acc_attrs.update({token: None})
unset = 0
if acc_attrs.has_key('id'):
debug_msg(_("account identifier modification is not permitted"), 0)
sys.exit(chestnut_dialer.EX_ARGSERR)
if command == "list-ui":
format = u"%-16s %-8s %-24s %s"
if not no_headers:
print format % (_("NAME"), _("TYPE"), _("AUTHOR"), _("DESCRIPTION"))
for ui_info in chestnut_dialer.get_ui_info_list():
print (format %
(ui_info["name"], ui_info["type"],
ui_info.has_key("author") and ui_info["author"] or "",
ui_info.has_key("description") and ui_info["description"] or ""))
sys.exit(chestnut_dialer.EX_OK)
if command == "list-ui-params":
if ui:
ui_params = chestnut_dialer.get_ui_params_info(ui)
if ui_params != None:
ui_params.sort(lambda a,b: cmp(a["name"], b["name"]))
format = u"%-24s %-10s %s"
if not no_headers:
print format % (_("NAME"), _("TYPE"), _("DESCRIPTION"))
for ui_p in ui_params:
print (format %
(ui_p["name"],
ui_p.has_key("type") and ui_p["type"] or "",
ui_p.has_key("description") and ui_p["description"] or ""))
sys.exit(chestnut_dialer.EX_OK)
else:
debug_msg(_("user interface '%s' not found") % v, 0)
sys.exit(chestnut_dialer.EX_ARGSERR)
else:
debug_msg(_("you must specify user interface with -i or --ui option"), 0)
sys.exit(chestnut_dialer.EX_USAGE)
if command == "list-acc-attrs":
attrs = Account.meta.keys()
attrs.sort()
format = u"%-24s %-10s"
if not no_headers:
print format % (_("NAME"), _("TYPE"))
for a in attrs:
print format % (a, Account.meta.get_attr_type(a))
sys.exit(chestnut_dialer.EX_OK)
if command == "help":
print_usage()
sys.exit(chestnut_dialer.EX_OK)
if command == "version":
print (chestnut_dialer.program_name + " " +
chestnut_dialer.program_version)
print _("""Copyright (C) 2003, 2004, 2005 Konstantin Korikov
This is free software; see the source or file COPYING for copying
conditions. There is NO warranty; not even for MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE.
""")
sys.exit(chestnut_dialer.EX_OK)
# Create user config directory, if needed.
if not os.access(chestnut_dialer.user_config_dir, os.F_OK):
os.mkdir(chestnut_dialer.user_config_dir)
os.chmod(chestnut_dialer.user_config_dir, 0700)
ui_info_list = chestnut_dialer.get_ui_info_list()
ui_info = None
ui_type = os.getenv("DISPLAY") and "X11" or "console"
preferred = chestnut_dialer.user_config_dir + "/preferred-ui-" + ui_type
if command == "dial" or command == None:
# Try to determine preferred user interface
if ui == None:
if os.access(preferred, os.R_OK):
ui = open(preferred).readline().replace("\n", "") or None
else:
for uiinf in ui_info_list:
if (uiinf["type"] == ui_type):
ui_info = uiinf
ui = uiinf["name"]
break
else: ui = "none"
if ui_info == None:
for uiinf in ui_info_list:
if uiinf["name"] == ui:
ui_info = uiinf
break
if ui_info == None:
if ui != None:
debug_msg(_("user interface '%s' not found") % ui, 0)
else:
debug_msg(_("user interface for '%s' not found. You may need to pass --ui=none option") % ui_type, 0)
sys.exit(chestnut_dialer.EX_ARGSERR)
if (command == "dial" or command == None) and ui and not os.access(preferred, os.F_OK):
try: open(preferred, 'w').write(ui)
except IOError: pass
modname = ui_info["package"] + ".application"
__import__(modname)
modname = modname.split(".")
mod = globals()[modname[0]]
for m in modname[1:]:
mod = getattr(mod, m)
if daemon:
p = os.fork()
if p < 0:
debug_msg(_("fork() failed"), 0)
sys.exit(chestnut_dialer.EX_OSERR)
if p > 0: sys.exit(chestnut_dialer.EX_OK)
os.setsid()
try:
n = os.open("/dev/null", os.O_RDWR)
for i in (0, 1, 2): os.dup2(n, i)
except OSError: pass
signal.signal(signal.SIGINT, signal_term)
signal.signal(signal.SIGTERM, signal_term)
signal.signal(signal.SIGHUP, signal_term)
signal.signal(signal.SIGUSR1, signal_disconnect)
app = mod.Application(ui_params)
if command in ("quit", "disconnect"):
current_pid = os.getpid()
exists_pid = None
t = 0
for pid in app.share.ls_sessions():
if pid == current_pid: continue
s = app.share.get_session(pid)
if s.start_time > t:
t = s.start_time
exists_pid = s.pid
if exists_pid:
os.kill(exists_pid, {"quit": signal.SIGTERM,
"disconnect": signal.SIGUSR1}[command])
else:
debug_msg(_("no other %s is running") %
chestnut_dialer.program_name, 0)
app.destroy()
sys.exit(chestnut_dialer.EX_OK)
account = None
if account_id != None:
try:
account = app.accounts.get_account(account_id)
except chestnut_dialer.account_set.NoAccountException:
debug_msg(_("account with identifier %d not exists") % account_id, 0)
app.destroy()
sys.exit(chestnut_dialer.EX_ARGSERR)
elif account_name != None:
n = account_name.lower()
for aname, aid in app.accounts.ls_accounts():
if aname.lower().find(n) >= 0:
account = app.accounts.get_account(aid)
break
if account == None:
debug_msg(_("no account with name matching '%s' found") % account_name, 0)
app.destroy()
sys.exit(chestnut_dialer.EX_ARGSERR)
ex_status = chestnut_dialer.EX_OK
if command in ("dial", "print", "print-full"):
temp_account = None
if len(acc_attrs) > 0:
temp_account = TempAccount()
if account != None:
temp_account.update(account)
temp_account.name += " (CLM)"
else:
temp_account.name = "Temp Account"
account = temp_account
account.update(acc_attrs)
if command == "dial":
if account != None:
app.connect(account)
ex_status = app.run()
else:
debug_msg(_("no dial options"), 0)
app.destroy()
sys.exit(chestnut_dialer.EX_USAGE)
elif command == "print":
if account != None:
print_account(account)
else:
debug_msg(_("you must specify account id with -a or --account option"), 0)
app.destroy()
sys.exit(chestnut_dialer.EX_USAGE)
else: # print-full
if account != None:
account.set_default_account(app.config.default_account)
else:
account = app.config.default_account
print_account(account)
elif command == "erase":
if account != None:
app.accounts.remove_accounts([account.id])
account = None
else:
debug_msg(_("you must specify account id with -a or --account option"), 0)
app.destroy()
sys.exit(chestnut_dialer.EX_USAGE)
elif command == "create":
if account != None:
account = app.accounts.duplicate_account(account.id)
else: account = app.accounts.new_account()
account.update(acc_attrs)
print _("New account id: %d") % account.id
elif command == "modify":
if account != None:
account.update(acc_attrs)
else:
debug_msg(_("you must specify account id with -a or --account option"), 0)
app.destroy()
sys.exit(chestnut_dialer.EX_USAGE)
elif command == "list-accounts":
accs = app.accounts.ls_accounts()
accs.sort(lambda a,b: cmp(a[1],b[1]))
format = u"%3s %s"
if not no_headers:
print format % (_("ID"), _("NAME"))
for aname, aid in accs:
print format % (aid, aname)
else: ex_status = app.run()
app.destroy()
sys.exit(ex_status)
|