Page MenuHomePhabricator

No OneTemporary

diff --git a/pypjua/clients/__init__.py b/pypjua/clients/__init__.py
index f7690689..79a0b01d 100644
--- a/pypjua/clients/__init__.py
+++ b/pypjua/clients/__init__.py
@@ -1,18 +1,67 @@
import re
+from pypjua import SIPURI
+
_pstn_num_sub_char = "[-() ]"
_re_pstn_num_sub = re.compile(_pstn_num_sub_char)
_re_pstn_num = re.compile("^\+?([0-9]|%s)+$" % _pstn_num_sub_char)
def format_cmdline_uri(uri, default_domain):
if "@" not in uri:
if _re_pstn_num.match(uri):
username = _re_pstn_num_sub.sub("", uri)
else:
username = uri
uri = "%s@%s" % (username, default_domain)
if not uri.startswith("sip:") and not uri.startswith("sips:"):
uri = "sip:%s" % uri
return uri
-__all__ = ["format_cmdline_uri"]
\ No newline at end of file
+_re_host_port = re.compile("^((?P<ip>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})|(?P<host>[a-zA-Z0-9\-\.]+))(:(?P<port>\d+))?$")
+class IPAddressOrHostname(tuple):
+ def __new__(typ, value):
+ match = _re_host_port.search(value)
+ if match is None:
+ raise ValueError("invalid hostname/port: %r" % value)
+ if match.group("ip") is None:
+ host = match.group("host")
+ is_ip = False
+ else:
+ host = match.group("ip")
+ is_ip = True
+ if match.group("port") is None:
+ port = None
+ else:
+ port = int(match.group("port"))
+ if port > 65535:
+ raise ValueError("port is out of range: %d" % port)
+ return host, port, is_ip
+
+
+class OutboundProxy(SIPURI):
+ def __new__(type, value):
+ if value.lower() == "none":
+ return None
+ parameters = {}
+ host = None
+ port = None
+ splitval = value.split(":")
+ if len(splitval) > 3:
+ raise ValueError("Could not parse outbound proxy")
+ elif len(splitval) == 3:
+ parameters["transport"], host, port = splitval
+ elif len(splitval) == 2:
+ if splitval[1].isdigit():
+ host, port = splitval
+ else:
+ parameters["transport"], host = splitval
+ else:
+ host = splitval[0]
+ if port is not None:
+ port = int(port)
+ if port < 0 or port > 65535:
+ raise ValueError("port is out of range: %d" % port)
+ return SIPURI(host=host, port=port, parameters=parameters)
+
+
+__all__ = ["format_cmdline_uri", "IPAddressOrHostname", "OutboundProxy"]
\ No newline at end of file
diff --git a/pypjua/clients/config.py b/pypjua/clients/config.py
index 32b484b7..17d1c217 100644
--- a/pypjua/clients/config.py
+++ b/pypjua/clients/config.py
@@ -1,225 +1,225 @@
import sys
import os
import glob
from optparse import OptionValueError, OptionParser
from ConfigParser import NoSectionError
from application.configuration import ConfigSection, ConfigFile, datatypes
from application.process import process
from msrplib.connect import MSRPRelaySettings
from pypjua import SIPURI, Route
-from pypjua.clients.lookup import lookup_srv
-from pypjua.clients.lookup import IPAddressOrHostname
+from pypjua.clients.dns_lookup import lookup_srv
+from pypjua.clients import IPAddressOrHostname
from pypjua.clients.cpim import SIPAddress
process._system_config_directory = os.path.expanduser("~/.sipclient")
config_ini = os.path.join(process._system_config_directory, 'config.ini')
# disable twisted debug messages (enabled by python-application)
from twisted.python import log
if log.defaultObserver is not None:
log.defaultObserver.stop()
log.defaultObserver = log.DefaultObserver()
log.defaultObserver.start()
class GeneralConfig(ConfigSection):
_datatypes = {"listen_udp": datatypes.NetworkAddress,
"trace_pjsip": datatypes.Boolean,
"trace_sip": datatypes.Boolean,
"auto_accept_file_transfers": datatypes.Boolean}
listen_udp = datatypes.NetworkAddress("any")
trace_pjsip = False
trace_sip = False
file_transfers_directory = os.path.join(process._system_config_directory, 'file_transfers')
auto_accept_file_transfers = False
class AccountConfig(ConfigSection):
_datatypes = {"sip_address": str,
"password": str,
"display_name": str,
"outbound_proxy": IPAddressOrHostname,
"msrp_relay": str}
sip_address = None
password = None
display_name = None
outbound_proxy = None
msrp_relay = "auto"
class AudioConfig(ConfigSection):
_datatypes = {"disable_sound": datatypes.Boolean}
disable_sound = False
def get_download_path(name):
path = os.path.join(GeneralConfig.file_transfers_directory, name)
if os.path.exists(path):
all = [int(x[len(path)+1:]) for x in glob.glob(path + '.*')]
if not all:
return path + '.1'
else:
return path + '.' + str(max(all)+1)
return path
def parse_outbound_proxy(option, opt_str, value, parser):
try:
parser.values.outbound_proxy = IPAddressOrHostname(value)
except ValueError, e:
raise OptionValueError(e.message)
def _parse_msrp_relay(value):
if value in ['auto', 'srv', 'none']:
return value
try:
return IPAddressOrHostname(value)
except ValueError, e:
raise OptionValueError(e.message)
def parse_msrp_relay(option, opt_str, value, parser):
parser.values.msrp_relay = _parse_msrp_relay(value)
def parse_options(usage, description):
configuration = ConfigFile(config_ini)
configuration.read_settings("Audio", AudioConfig)
configuration.read_settings("General", GeneralConfig)
parser = OptionParser(usage=usage, description=description)
parser.print_usage = parser.print_help
parser.add_option("-a", "--account-name", type="string",
help=('The account name from which to read account settings. '
'Corresponds to section Account_NAME in the configuration file.'))
parser.add_option('--show-config', action='store_true',
help = ('Show settings from the configuration and exit; '
'use together with --account-name option'))
parser.add_option("--sip-address", type="string", help="SIP login account")
parser.add_option("-p", "--password", type="string",
help="Password to use to authenticate the local account.")
parser.add_option("-n", "--display-name", type="string",
help="Display name to use for the local account.")
help = ('Use the outbound SIP proxy; '
'if "auto", discover the SIP proxy through SRV and A '
'records lookup on the domain part of user SIP URI.')
parser.add_option("-o", "--outbound-proxy", type="string",
action="callback", callback=parse_outbound_proxy, help=help, metavar="IP[:PORT]")
parser.add_option("-m", "--trace-msrp", action="store_true", default=False,
help="Dump the raw contents of incoming and outgoing MSRP messages.")
parser.add_option("-s", "--trace-sip", action="store_true", default=GeneralConfig.trace_sip,
help="Dump the raw contents of incoming and outgoing SIP messages.")
parser.add_option("-j", "--trace-pjsip", action="store_true", default=GeneralConfig.trace_pjsip,
help="Print PJSIP logging output.")
help=('Use the MSRP relay; '
'if "srv", do SRV lookup on domain part of the target SIP URI, '
'use user\'s domain if SRV lookup was not successful; '
'if "none", the direct connection is performed; '
'if "auto", use "srv" for incoming connections and "none" for outgoing; '
'default is "auto".')
parser.add_option("-r", "--msrp-relay", type='string',
action="callback", callback=parse_msrp_relay, help=help, metavar='IP[:PORT]')
parser.add_option("-S", "--disable-sound", action="store_true", default=AudioConfig.disable_sound,
help="Do not initialize the soundcard (by default the soundcard is enabled).")
#parser.add_option("-y", '--auto-accept-all', action='store_true', default=False, help=SUPPRESS_HELP)
parser.add_option('--auto-accept-files', action='store_true',
help='Accept all incoming file transfers without bothering user.')
parser.add_option('--no-register', action='store_false', dest='register', default=True,
help='Bypass registration')
parser.add_option('--msrp-tcp', action='store_false', dest='msrp_tls', default=True)
options, args = parser.parse_args()
if options.account_name is None:
account_section = 'Account'
else:
account_section = 'Account_%s' % options.account_name
options.use_bonjour = options.account_name == 'bonjour'
if options.account_name not in [None, 'bonjour'] and account_section not in configuration.parser.sections():
msg = "Section [%s] was not found in the configuration file %s" % (account_section, config_ini)
raise RuntimeError(msg)
configuration.read_settings(account_section, AccountConfig)
default_options = dict(outbound_proxy=AccountConfig.outbound_proxy,
msrp_relay=_parse_msrp_relay(AccountConfig.msrp_relay),
sip_address=AccountConfig.sip_address,
password=AccountConfig.password,
display_name=AccountConfig.display_name,
local_ip=GeneralConfig.listen_udp[0],
local_port=GeneralConfig.listen_udp[1],
auto_accept_files=GeneralConfig.auto_accept_file_transfers)
if options.show_config:
print 'Configuration file: %s' % config_ini
for config_section in [account_section, 'General', 'Audio']:
try:
options = configuration.parser.options(config_section)
except NoSectionError:
pass
else:
print '[%s]' % config_section
for option in options:
if option in default_options.keys():
print '%s=%s' % (option, default_options[option])
accounts = []
for section in configuration.parser.sections():
if section.startswith('Account') and account_section != section:
if section == 'Account':
accounts.append('(default)')
else:
accounts.append(section[8:])
accounts.sort()
print "Other accounts: %s" % ', '.join(accounts)
sys.exit()
options._update_loose(dict((name, value) for name, value in default_options.items() if getattr(options, name, None) is None))
accounts = [(acc == 'Account') and 'default' or "'%s'" % acc[8:] for acc in configuration.parser.sections() if acc.startswith('Account')]
accounts.sort()
print "Accounts available: %s" % ', '.join(accounts)
if options.account_name is None:
print "Using default account: %s" % options.sip_address
else:
print "Using account '%s': %s" % (options.account_name, options.sip_address)
if not options.use_bonjour:
if not all([options.sip_address, options.password]):
raise RuntimeError("No complete set of SIP credentials specified in config file and on commandline.")
options.sip_address = SIPAddress.parse(options.sip_address)
options.uri = SIPURI(user=options.sip_address.username, host=options.sip_address.domain, display=options.display_name)
if args:
options.target_address = SIPAddress.parse(args[0], default_domain = options.sip_address.domain)
options.target_uri = SIPURI(user=options.target_address.username, host=options.target_address.domain)
del args[0]
else:
options.target_address = None
options.target_uri = None
options.args = args
if options.msrp_relay == 'auto':
if options.target_uri is None:
options.msrp_relay = 'srv'
else:
options.msrp_relay = 'none'
if options.msrp_relay == 'srv':
options.relay = MSRPRelaySettings(domain=options.sip_address.domain,
username=options.sip_address.username, password=options.password)
elif options.msrp_relay == 'none':
options.relay = None
else:
host, port, is_ip = options.msrp_relay
if is_ip or port is not None:
options.relay = MSRPRelaySettings(domain=options.sip_address.domain, host=host, port=port,
username=options.sip_address.username, password=options.password)
else:
options.relay = MSRPRelaySettings(domain=options.sip_address.domain,
username=options.sip_address.username, password=options.password)
if options.use_bonjour:
options.route = None
else:
if options.outbound_proxy is None:
proxy_host, proxy_port, proxy_is_ip = options.sip_address.domain, None, False
else:
proxy_host, proxy_port, proxy_is_ip = options.outbound_proxy
options.route = Route(*lookup_srv(proxy_host, proxy_port, proxy_is_ip, 5060))
return options
diff --git a/pypjua/clients/lookup.py b/pypjua/clients/dns_lookup.py
similarity index 82%
rename from pypjua/clients/lookup.py
rename to pypjua/clients/dns_lookup.py
index e0bd6882..92b8c37b 100644
--- a/pypjua/clients/lookup.py
+++ b/pypjua/clients/dns_lookup.py
@@ -1,217 +1,170 @@
import re
import random
import dns.resolver
-from pypjua import Route, SIPURI
-
-_re_host_port = re.compile("^((?P<ip>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})|(?P<host>[a-zA-Z0-9\-\.]+))(:(?P<port>\d+))?$")
-class IPAddressOrHostname(tuple):
- def __new__(typ, value):
- match = _re_host_port.search(value)
- if match is None:
- raise ValueError("invalid hostname/port: %r" % value)
- if match.group("ip") is None:
- host = match.group("host")
- is_ip = False
- else:
- host = match.group("ip")
- is_ip = True
- if match.group("port") is None:
- port = None
- else:
- port = int(match.group("port"))
- if port > 65535:
- raise ValueError("port is out of range: %d" % port)
- return host, port, is_ip
-
-
-class OutboundProxy(SIPURI):
- def __new__(type, value):
- if value.lower() == "none":
- return None
- parameters = {}
- host = None
- port = None
- splitval = value.split(":")
- if len(splitval) > 3:
- raise ValueError("Could not parse outbound proxy")
- elif len(splitval) == 3:
- parameters["transport"], host, port = splitval
- elif len(splitval) == 2:
- if splitval[1].isdigit():
- host, port = splitval
- else:
- parameters["transport"], host = splitval
- else:
- host = splitval[0]
- if port is not None:
- port = int(port)
- if port < 0 or port > 65535:
- raise ValueError("port is out of range: %d" % port)
- return SIPURI(host=host, port=port, parameters=parameters)
-
+from pypjua import Route
def lookup_srv(host, port, is_ip, default_port, service='_sip._udp'):
if is_ip:
return host, port or default_port
else:
if port is None:
try:
srv_answers = dns.resolver.query("%s.%s" % (service, host), "SRV")
a_host = str(srv_answers[0].target).rstrip(".")
port = srv_answers[0].port
print 'Resolved DNS SRV record "%s.%s" --> %s:%d' % (service, host, a_host, port)
except:
print "Domain %s has no DNS SRV record, attempting DNS A record lookup" % host
a_host = host
port = default_port
else:
a_host = host
try:
a_answers = dns.resolver.query(a_host, "A")
print 'Resolved DNS A record "%s" --> %s' % (a_host, ", ".join(a.address for a in a_answers))
except:
raise RuntimeError('Could not resolve "%s"' % a_host)
return random.choice(a_answers).address, port
_service_srv_record_map = {"stun": ("_stun._udp", 3478, False),
"msrprelay": ("_msrps._tcp", 2855, True)}
def lookup_service_for_sip_uri(uri, service):
try:
service_prefix, service_port, service_fallback = _service_srv_record_map[service]
except KeyError:
raise RuntimeError("Unknown service: %s" % service)
a_candidates = []
servers = []
try:
srv_answers = dns.resolver.query("%s.%s" % (service_prefix, uri.host), "SRV")
except:
if service_fallback:
a_candidates.append((uri.host, service_port))
else:
srv_answers = sorted(srv_answers, key=lambda x: x.priority)
srv_answers.sort(key=lambda x: x.weight, reverse=True)
a_candidates = [(srv_answer.target, srv_answer.port) for srv_answer in srv_answers]
for a_host, a_port in a_candidates:
try:
a_answers = dns.resolver.query(a_host, "A")
except:
pass
else:
for a_answer in a_answers:
servers.append((a_answer.address, a_port))
return servers
_naptr_service_transport_map = {"sips+d2t": "tls",
"sip+d2t": "tcp",
"sip+d2u": "udp"}
_transport_srv_service_map = {"udp": "_sip._udp",
"tcp": "_sip._tcp",
"tls": "_sips._tls"}
def lookup_routes_for_sip_uri(uri, supported_transports):
"""This function preforms RFC 3263 compliant lookup of transport/ip/port
combinations for a particular SIP URI. As arguments it takes a SIPURI object
and a list of supported transports, in order of preference of the application.
It returns a list of Route objects that can be used in order of preference."""
if len(supported_transports) == 0:
raise RuntimeError("No transports are supported")
for supported_transport in supported_transports:
if supported_transport not in _transport_srv_service_map:
raise RuntimeError("Unsupported transport: %s" % supported_transport)
supported_transports = [transport.lower() for transport in supported_transports]
# If the URI is a SIPS URI, only a TLS transport can be returned.
if uri.secure:
if "tls" not in supported_transports:
raise RuntimeError("Requested lookup for SIPS URI, but TLS transport is not supported")
supported_transports = ["tls"]
transport = None
port = None
ip = None
srv_candidates = []
a_candidates = []
routes = []
# Check if the transport was already set as a parameter on the SIP URI.
if uri.parameters and "transport" in uri.parameters:
transport = uri.parameters["transport"]
# Check if the port was already set, we can skip NAPTR/SRV lookup later if it is.
if uri.port:
port = uri.port
# Check if the host part of the URI is a IP address, we can skip NAPTR/SRV lookup later if it is.
if re.match("^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$", uri.host):
ip = uri.host
# Otherwise we can try NAPTR/SRV lookup.
if port is None and ip is None:
# Only do the NAPTR part if the transport was not specified as a URI parameter.
if transport is None:
try:
naptr_answers = dns.resolver.query(uri.host, "NAPTR")
except:
# If NAPTR lookup fails for some reason, try SRV lookup for all supported transports.
# Only the transports of those lookups that succeed are supported by the server.
srv_candidates = [(transport, "%s.%s" % (_transport_srv_service_map[transport], uri.host)) for transport in supported_transports]
else:
# If NAPTR lookup succeeds, order those entries that are applicable for SIP based on server prefernce.
naptr_answers = [answer for answer in naptr_answers if answer.flags.lower() == "s" and answer.service.lower() in _naptr_service_transport_map and _naptr_service_transport_map[answer.service.lower()] in supported_transports]
naptr_answers.sort(key=lambda x: x.preference)
naptr_answers.sort(key=lambda x: x.order)
if len(naptr_answers) == 0:
raise RuntimeError("Could find a suitable transport in NAPTR record of domain")
srv_candidates = [(_naptr_service_transport_map[answer.service.lower()], answer.replacement) for answer in naptr_answers]
else:
# Directly try the SRV record of the requested transport.
srv_candidates = [(transport, "%s.%s" % (_transport_srv_service_map[transport], uri.host))]
for srv_transport, srv_qname in srv_candidates:
try:
srv_answers = dns.resolver.query(srv_qname, "SRV")
except:
# If SRV lookup fails, try A record directly for a transport that was requested,
# otherwise UDP for a SIP URI, TLS for a SIPS URI.
if transport is None:
if (uri.secure and srv_transport == "tls") or (not uri.secure and srv_transport == "udp"):
a_candidates.append((srv_transport, uri.host, 5061 if srv_transport == "tls" else 5060))
else:
if transport == srv_transport:
a_candidates.append((transport, uri.host, 5061 if transport == "tls" else 5060))
else:
# If SRV lookup succeeds, sort the resulting hosts based on server preference.
srv_answers = sorted(srv_answers, key=lambda x: x.priority)
srv_answers.sort(key=lambda x: x.weight, reverse=True)
for answer in srv_answers:
a_candidates.append((srv_transport, answer.target, answer.port))
else:
# If NAPT/SRV was skipped, fill in defaults for the other variables.
if transport is None:
if uri.secure:
transport = "tls"
else:
if "udp" not in supported_transports:
raise RuntimeError("UDP transport is not suported")
transport = "udp"
if port is None:
port = 5061 if uri.secure else 5060
# For an IP address, return this immedeately, otherwise do a lookup for the requested hostname.
if ip is None:
a_candidates.append((transport, uri.host, port))
else:
return [Route(ip, port=port, transport=transport)]
# Keep results in a dictionary so we don't do double A record lookups
a_cache = {}
for a_transport, a_qname, a_port in a_candidates:
try:
if a_qname in a_cache:
a_answers = a_cache[a_qname]
else:
a_answers = dns.resolver.query(a_qname, "A")
a_cache[a_qname] = a_answers
except:
# If lookup fails then don't return this value
pass
else:
for answer in a_answers:
routes.append(Route(answer.address, port=a_port, transport=a_transport))
return routes
-__all__ = ["IPAddressOrHostname", "OutboundProxy", "lookup_srv", "lookup_service_for_sip_uri", "lookup_routes_for_sip_uri"]
+__all__ = ["lookup_srv", "lookup_service_for_sip_uri", "lookup_routes_for_sip_uri"]
diff --git a/scripts/sip_audio_session.py b/scripts/sip_audio_session.py
index 875c616e..5ff00f0d 100644
--- a/scripts/sip_audio_session.py
+++ b/scripts/sip_audio_session.py
@@ -1,590 +1,590 @@
#!/usr/bin/env python
import sys
import traceback
import string
import socket
import os
import atexit
import select
import termios
import signal
import datetime
import random
from thread import start_new_thread, allocate_lock
from threading import Thread, Timer
from Queue import Queue
from optparse import OptionParser, OptionValueError
from time import sleep, time
from application.process import process
from application.configuration import *
from pypjua import *
from pypjua.clients import enrollment
from pypjua.clients.log import Logger
-from pypjua.clients.lookup import *
+from pypjua.clients.dns_lookup import *
from pypjua.clients.clientconfig import get_path
-from pypjua.clients import format_cmdline_uri
+from pypjua.clients import *
class GeneralConfig(ConfigSection):
_datatypes = {"local_ip": datatypes.IPAddress, "sip_transports": datatypes.StringList, "trace_pjsip": datatypes.Boolean, "trace_sip": datatypes.Boolean}
local_ip = None
sip_local_udp_port = 0
sip_local_tcp_port = 0
sip_local_tls_port = 0
sip_transports = ["tls", "tcp", "udp"]
trace_pjsip = False
trace_sip = False
history_directory = '~/.sipclient/history'
log_directory = '~/.sipclient/log'
class AccountConfig(ConfigSection):
_datatypes = {"sip_address": str, "password": str, "display_name": str, "outbound_proxy": OutboundProxy, "use_ice": datatypes.Boolean, "use_stun_for_ice": datatypes.Boolean, "stun_servers": datatypes.StringList}
sip_address = None
password = None
display_name = None
outbound_proxy = None
use_ice = False
use_stun_for_ice = False
stun_servers = []
class SRTPOptions(dict):
def __new__(typ, value):
value_lower = value.lower()
if value_lower == "disabled":
return dict(use_srtp=False, srtp_forced=False)
elif value_lower == "optional":
return dict(use_srtp=True, srtp_forced=False)
elif value_lower == "mandatory":
return dict(use_srtp=True, srtp_forced=True)
else:
raise ValueError('Unknown SRTP option: "%s"' % value)
class AudioConfig(ConfigSection):
_datatypes = {"sample_rate": int, "echo_cancellation_tail_length": int,"codec_list": datatypes.StringList, "disable_sound": datatypes.Boolean, "encryption": SRTPOptions}
sample_rate = 32
echo_cancellation_tail_length = 50
codec_list = ["speex", "g711", "ilbc", "gsm", "g722"]
disable_sound = False
encryption = dict(use_srtp=True, srtp_forced=False)
process._system_config_directory = os.path.expanduser("~/.sipclient")
enrollment.verify_account_config()
configuration = ConfigFile("config.ini")
configuration.read_settings("Audio", AudioConfig)
configuration.read_settings("General", GeneralConfig)
queue = Queue()
packet_count = 0
start_time = None
old = None
user_quit = True
lock = allocate_lock()
logger = None
return_code = 1
def termios_restore():
global old
if old is not None:
termios.tcsetattr(sys.stdin.fileno(), termios.TCSADRAIN, old)
def getchar():
global old
fd = sys.stdin.fileno()
if os.isatty(fd):
old = termios.tcgetattr(fd)
new = termios.tcgetattr(fd)
new[3] = new[3] & ~termios.ICANON & ~termios.ECHO
new[6][termios.VMIN] = '\000'
try:
termios.tcsetattr(fd, termios.TCSADRAIN, new)
if select.select([fd], [], [], None)[0]:
return sys.stdin.read(10)
finally:
termios_restore()
else:
return os.read(fd, 10)
def event_handler(event_name, **kwargs):
global start_time, packet_count, queue, do_trace_pjsip, logger
if event_name == "siptrace":
logger.log(event_name, **kwargs)
elif event_name != "log":
queue.put(("pypjua_event", (event_name, kwargs)))
elif do_trace_pjsip:
queue.put(("print", "%(timestamp)s (%(level)d) %(sender)14s: %(message)s" % kwargs))
class RingingThread(Thread):
def __init__(self, inbound):
self.inbound = inbound
self.stopping = False
Thread.__init__(self)
self.setDaemon(True)
self.start()
def stop(self):
self.stopping = True
def run(self):
global queue
while True:
if self.stopping:
return
if self.inbound:
queue.put(("play_wav", "ring_inbound.wav"))
else:
queue.put(("play_wav", "ring_outbound.wav"))
sleep(5)
def print_control_keys():
print "Available control keys:"
print " h: hang-up the active session"
print " r: toggle audio recording"
print " <> : adjust echo cancellation"
print " SPACE: hold/on-hold"
print " Ctrl-d: quit the program"
def read_queue(e, username, domain, password, display_name, route, target_uri, trace_sip, ec_tail_length, sample_rate, codecs, do_trace_pjsip, use_bonjour, stun_servers, transport, auto_hangup):
global user_quit, lock, queue, return_code
lock.acquire()
inv = None
audio = None
ringer = None
printed = False
rec_file = None
want_quit = target_uri is not None
other_user_agent = None
on_hold = False
session_start_time = None
try:
if not use_bonjour:
sip_uri = SIPURI(user=username, host=domain, display=display_name)
credentials = Credentials(sip_uri, password)
if len(stun_servers) > 0:
e.detect_nat_type(*stun_servers[0])
if target_uri is None:
if use_bonjour:
print "Using bonjour"
print "Listening on local interface %s:%d" % (e.local_ip, e.local_udp_port)
print_control_keys()
print 'Waiting for incoming SIP session requests...'
else:
reg = Registration(credentials, route=route)
print 'Registering "%s" at %s:%d' % (credentials.uri, route.host, route.port)
reg.register()
else:
inv = Invitation(credentials, target_uri, route=route)
print "Call from %s to %s through proxy %s:%s:%d" % (inv.caller_uri, inv.callee_uri, route.transport, route.host, route.port)
audio = AudioTransport(transport)
inv.set_offered_local_sdp(SDPSession(audio.transport.local_rtp_address, connection=SDPConnection(audio.transport.local_rtp_address), media=[audio.get_local_media(True)]))
inv.send_invite()
print_control_keys()
while True:
command, data = queue.get()
if command == "print":
print data
if command == "pypjua_event":
event_name, args = data
if event_name == "Registration_state":
if args["state"] == "registered":
if not printed:
print "REGISTER was successful"
print "Contact: %s (expires in %d seconds)" % (args["contact_uri"], args["expires"])
print_control_keys()
print "Waiting for incoming session..."
printed = True
elif args["state"] == "unregistered":
if "code" in args and args["code"] / 100 != 2:
print "Unregistered: %(code)d %(reason)s" % args
elif inv is None:
return_code = 0
user_quit = False
command = "quit"
elif event_name == "Invitation_sdp":
if args["obj"] is inv:
if args["succeeded"]:
if not audio.is_started:
if ringer is not None:
ringer.stop()
ringer = None
session_start_time = time()
audio.start(args["local_sdp"], args["remote_sdp"], 0)
e.connect_audio_transport(audio)
print 'Media negotiation done, using "%s" codec at %dHz' % (audio.codec, audio.sample_rate)
print "Audio RTP endpoints %s:%d <-> %s:%d" % (audio.transport.local_rtp_address, audio.transport.local_rtp_port, audio.transport.remote_rtp_address_sdp, audio.transport.remote_rtp_port_sdp)
return_code = 0
if auto_hangup is not None:
Timer(auto_hangup, lambda: queue.put(("eof", None))).start()
if audio.transport.srtp_active:
print "RTP audio stream is encrypted"
else:
audio.update_direction(inv.get_active_local_sdp().media[0].get_direction())
else:
print "SDP negotation failed: %s" % args["error"]
elif event_name == "Invitation_state":
if args["state"] == args["prev_state"] and args["state"] != "EARLY":
print "SAME STATE"
print args
data, args = None, None
continue
if args["state"] == "EARLY":
if "code" in args and args["code"] == 180:
if ringer is None:
print "Ringing..."
ringer = RingingThread(target_uri is None)
elif args["state"] == "CONNECTING":
if "headers" in args and "User-Agent" in args["headers"]:
other_user_agent = args["headers"].get("User-Agent")
elif args["state"] == "INCOMING":
print "Incoming session..."
if inv is None:
remote_sdp = args["obj"].get_offered_remote_sdp()
if remote_sdp is not None and len(remote_sdp.media) == 1 and remote_sdp.media[0].media == "audio":
inv = args["obj"]
other_user_agent = args["headers"].get("User-Agent")
if ringer is None:
ringer = RingingThread(True)
inv.respond_to_invite_provisionally()
print 'Incoming audio session from "%s", do you want to accept? (y/n)' % str(inv.caller_uri)
else:
print "Not an audio call, rejecting."
args["obj"].disconnect()
else:
print "Rejecting."
args["obj"].disconnect()
elif args["prev_state"] == "CONNECTING" and args["state"] == "CONFIRMED":
if other_user_agent is not None:
print 'Remote SIP User Agent is "%s"' % other_user_agent
elif args["state"] == "REINVITED":
# Just assume the call got placed on hold for now...
prev_remote_direction = inv.get_active_remote_sdp().media[0].get_direction()
remote_direction = inv.get_offered_remote_sdp().media[0].get_direction()
if "recv" in prev_remote_direction and "recv" not in remote_direction:
print "Remote party is placing us on hold"
elif "recv" not in prev_remote_direction and "recv" in remote_direction:
print "Remote party is taking us out of hold"
local_sdp = inv.get_active_local_sdp()
local_sdp.version += 1
local_sdp.media[0] = audio.get_local_media(False)
inv.set_offered_local_sdp(local_sdp)
inv.respond_to_reinvite()
elif args["state"] == "DISCONNECTED":
if args["obj"] is inv:
if rec_file is not None:
rec_file.stop()
print 'Stopped recording audio to "%s"' % rec_file.file_name
rec_file = None
if ringer is not None:
ringer.stop()
ringer = None
if args["prev_state"] == "DISCONNECTING":
disc_msg = "Session ended by local user"
elif args["prev_state"] in ["CALLING", "EARLY"]:
if "headers" in args:
if "Server" in args["headers"]:
print 'Remote SIP server is "%s"' % args["headers"]["Server"]
elif "User-Agent" in args["headers"]:
print 'Remote SIP User Agent is "%s"' % args["headers"]["User-Agent"]
disc_msg = "Session could not be established"
else:
disc_msg = 'Session ended by "%s"' % inv.remote_uri
if "code" in args and args["code"] / 100 != 2:
print "%s: %d %s" % (disc_msg, args["code"], args["reason"])
if args["code"] == 408 and args["prev_state"] == "CONNECTING":
print "Session failed because ACK was never received"
if args["code"] in [301, 302]:
print 'Received redirect request to "%s"' % args["headers"]["Contact"]
return_code = 0
else:
print disc_msg
if session_start_time is not None:
duration = time() - session_start_time
print "Session duration was %d minutes, %d seconds" % (duration / 60, duration % 60)
session_start_time = None
if want_quit:
command = "unregister"
else:
if audio is not None:
audio.stop()
audio = None
inv = None
elif event_name == "detect_nat_type":
if args["succeeded"]:
print "Detected NAT type: %s" % args["nat_type"]
if command == "user_input":
if inv is not None:
data = data[0]
if data.lower() == "h":
command = "end"
want_quit = target_uri is not None
elif data in "0123456789*#ABCD" and audio is not None and audio.is_active:
audio.send_dtmf(data)
elif data.lower() == "r":
if rec_file is None:
src = '%s@%s' % (inv.caller_uri.user, inv.caller_uri.host)
dst = '%s@%s' % (inv.callee_uri.user, inv.callee_uri.host)
dir = os.path.join(os.path.expanduser(GeneralConfig.history_directory), '%s@%s' % (username, domain))
try:
file_name = os.path.join(dir, '%s-%s-%s.wav' % (datetime.datetime.now().strftime("%Y%m%d-%H%M%S"), src, dst))
rec_file = e.rec_wav_file(file_name)
print 'Recording audio to "%s"' % rec_file.file_name
except OSError, e:
print "Error while trying to record file: %s"
else:
rec_file.stop()
print 'Stopped recording audio to "%s"' % rec_file.file_name
rec_file = None
elif data == " ":
if inv.state == "CONFIRMED":
if not on_hold:
on_hold = True
print "Placing call on hold"
if "send" in audio.direction:
new_direction = "sendonly"
else:
new_direction = "inactive"
e.disconnect_audio_transport(audio)
else:
on_hold = False
print "Taking call out of hold"
if "send" in audio.direction:
new_direction = "sendrecv"
else:
new_direction = "recvonly"
e.connect_audio_transport(audio)
local_sdp = inv.get_active_local_sdp()
local_sdp.version += 1
local_sdp.media[0] = audio.get_local_media(True, new_direction)
inv.set_offered_local_sdp(local_sdp)
inv.send_reinvite()
elif inv.state in ["INCOMING", "EARLY"] and target_uri is None:
if data.lower() == "n":
command = "end"
want_quit = False
elif data.lower() == "y":
remote_sdp = inv.get_offered_remote_sdp()
audio = AudioTransport(transport, remote_sdp, 0)
inv.set_offered_local_sdp(SDPSession(audio.transport.local_rtp_address, connection=SDPConnection(audio.transport.local_rtp_address), media=[audio.get_local_media(False)], start_time=remote_sdp.start_time, stop_time=remote_sdp.stop_time))
inv.accept_invite()
if data in ",<":
if ec_tail_length > 0:
ec_tail_length = max(0, ec_tail_length - 10)
e.auto_set_sound_devices(ec_tail_length)
print "Set echo cancellation tail length to %d ms" % ec_tail_length
elif data in ".>":
if ec_tail_length < 500:
ec_tail_length = min(500, ec_tail_length + 10)
e.auto_set_sound_devices(ec_tail_length)
print "Set echo cancellation tail length to %d ms" % ec_tail_length
if command == "play_wav":
e.play_wav_file(get_path(data))
if command == "eof":
command = "end"
want_quit = True
if command == "end":
try:
inv.disconnect()
except:
command = "unregister"
if command == "unregister":
if target_uri is None and not use_bonjour:
reg.unregister()
else:
user_quit = False
command = "quit"
if command == "quit":
break
data, args = None, None
except:
user_quit = False
traceback.print_exc()
finally:
e.stop()
logger.stop()
if not user_quit:
os.kill(os.getpid(), signal.SIGINT)
lock.release()
def do_invite(**kwargs):
global user_quit, lock, queue, do_trace_pjsip, logger
ctrl_d_pressed = False
do_trace_pjsip = kwargs["do_trace_pjsip"]
outbound_proxy = kwargs.pop("outbound_proxy")
kwargs["stun_servers"] = lookup_service_for_sip_uri(SIPURI(host=kwargs["domain"]), "stun")
if kwargs["use_bonjour"]:
kwargs["route"] = None
else:
if outbound_proxy is None:
routes = lookup_routes_for_sip_uri(SIPURI(host=kwargs["domain"]), kwargs.pop("sip_transports"))
else:
routes = lookup_routes_for_sip_uri(outbound_proxy, kwargs.pop("sip_transports"))
# Only try the first Route for now
try:
kwargs["route"] = routes[0]
except IndexError:
raise RuntimeError("No route found to SIP proxy")
logger = Logger(AccountConfig, GeneralConfig.log_directory, trace_sip=kwargs['trace_sip'])
if kwargs['trace_sip']:
print "Logging SIP trace to file '%s'" % logger._siptrace_filename
e = Engine(event_handler, trace_sip=True, codecs=kwargs["codecs"], ec_tail_length=kwargs["ec_tail_length"], sample_rate=kwargs["sample_rate"], local_ip=kwargs["local_ip"], local_udp_port=kwargs.pop("local_udp_port"), local_tcp_port=kwargs.pop("local_tcp_port"), local_tls_port=kwargs.pop("local_tls_port"))
e.start(not kwargs.pop("disable_sound"))
if kwargs["target_uri"] is not None:
kwargs["target_uri"] = e.parse_sip_uri(kwargs["target_uri"])
transport_kwargs = AudioConfig.encryption.copy()
transport_kwargs["use_ice"] = AccountConfig.use_ice
wait_for_stun = False
if AccountConfig.use_stun_for_ice:
if len(AccountConfig.stun_servers) > 0:
wait_for_stun = True
try:
random_stun = random.choice(AccountConfig.stun_servers)
transport_kwargs["ice_stun_address"], ice_stun_port = random_stun.split(":")
except:
transport_kwargs["ice_stun_address"] = random_stun
transport_kwargs["ice_stun_port"] = 3478
else:
transport_kwargs["ice_stun_port"] = int(ice_stun_port)
else:
if len(kwargs["stun_servers"]) > 0:
wait_for_stun = True
transport_kwargs["ice_stun_address"], transport_kwargs["ice_stun_port"] = random.choice(kwargs["stun_servers"])
kwargs["transport"] = RTPTransport(kwargs.pop("local_ip"), **transport_kwargs)
if wait_for_stun:
print "Waiting for STUN response for ICE from %s:%d" % (transport_kwargs["ice_stun_address"], transport_kwargs["ice_stun_port"])
while True:
command, data = queue.get()
if command == "print":
print data
elif command == "pypjua_event":
event_name, args = data
if event_name == "RTPTransport_init":
if args["succeeded"]:
break
else:
raise RuntimeError("STUN request failed")
start_new_thread(read_queue, (e,), kwargs)
atexit.register(termios_restore)
try:
while True:
char = getchar()
if char == "\x04":
if not ctrl_d_pressed:
queue.put(("eof", None))
ctrl_d_pressed = True
else:
queue.put(("user_input", char))
except KeyboardInterrupt:
if user_quit:
print "Ctrl+C pressed, exiting instantly!"
queue.put(("quit", True))
lock.acquire()
return
def parse_outbound_proxy(option, opt_str, value, parser):
try:
parser.values.outbound_proxy = OutboundProxy(value)
except ValueError, e:
raise OptionValueError(e.message)
def parse_auto_hangup(option, opt_str, value, parser):
try:
value = parser.rargs[0]
except IndexError:
value = 0
else:
if value == "" or value[0] == '-':
value = 0
else:
try:
value = int(value)
except ValueError:
value = 0
else:
del parser.rargs[0]
parser.values.auto_hangup = value
def split_codec_list(option, opt_str, value, parser):
parser.values.codecs = value.split(",")
def parse_options():
retval = {}
description = "This script can sit idle waiting for an incoming audio call, or perform an outgoing audio call to the target SIP account. The program will close the session and quit when Ctrl+D is pressed."
usage = "%prog [options] [target-user@target-domain.com]"
parser = OptionParser(usage=usage, description=description)
parser.print_usage = parser.print_help
parser.add_option("-a", "--account-name", type="string", dest="account_name", help="The account name from which to read account settings. Corresponds to section Account_NAME in the configuration file. If not supplied, the section Account will be read.", metavar="NAME")
parser.add_option("--sip-address", type="string", dest="sip_address", help="SIP address of the user in the form user@domain")
parser.add_option("-p", "--password", type="string", dest="password", help="Password to use to authenticate the local account. This overrides the setting from the config file.")
parser.add_option("-n", "--display-name", type="string", dest="display_name", help="Display name to use for the local account. This overrides the setting from the config file.")
parser.add_option("-o", "--outbound-proxy", type="string", action="callback", callback=parse_outbound_proxy, help="Outbound SIP proxy to use. By default a lookup of the domain is performed based on SRV and A records. This overrides the setting from the config file.", metavar="IP[:PORT]")
parser.add_option("-s", "--trace-sip", action="store_true", dest="trace_sip", help="Dump the raw contents of incoming and outgoing SIP messages (disabled by default).")
parser.add_option("-t", "--ec-tail-length", type="int", dest="ec_tail_length", help='Echo cancellation tail length in ms, setting this to 0 will disable echo cancellation. Default is 50 ms.')
parser.add_option("-r", "--sample-rate", type="int", dest="sample_rate", help='Sample rate in kHz, should be one of 8, 16 or 32kHz. Default is 32kHz.')
parser.add_option("-c", "--codecs", type="string", action="callback", callback=split_codec_list, help='Comma separated list of codecs to be used. Default is "speex,g711,ilbc,gsm,g722".')
parser.add_option("-S", "--disable-sound", action="store_true", dest="disable_sound", help="Do not initialize the soundcard (by default the soundcard is enabled).")
parser.add_option("-j", "--trace-pjsip", action="store_true", dest="do_trace_pjsip", help="Print PJSIP logging output (disabled by default).")
parser.add_option("--auto-hangup", action="callback", callback=parse_auto_hangup, help="Interval after which to hangup an on-going call (applies only to outgoing calls, disabled by default). If the option is specified but the interval is not, it defaults to 0 (hangup the call as soon as it connects).", metavar="[INTERVAL]")
options, args = parser.parse_args()
retval["use_bonjour"] = options.account_name == "bonjour"
if not retval["use_bonjour"]:
if options.account_name is None:
account_section = "Account"
else:
account_section = "Account_%s" % options.account_name
if account_section not in configuration.parser.sections():
raise RuntimeError("There is no account section named '%s' in the configuration file" % account_section)
configuration.read_settings(account_section, AccountConfig)
default_options = dict(outbound_proxy=AccountConfig.outbound_proxy, sip_address=AccountConfig.sip_address, password=AccountConfig.password, display_name=AccountConfig.display_name, trace_sip=GeneralConfig.trace_sip, ec_tail_length=AudioConfig.echo_cancellation_tail_length, sample_rate=AudioConfig.sample_rate, codecs=AudioConfig.codec_list, disable_sound=AudioConfig.disable_sound, do_trace_pjsip=GeneralConfig.trace_pjsip, local_ip=GeneralConfig.local_ip, local_udp_port=GeneralConfig.sip_local_udp_port, local_tcp_port=GeneralConfig.sip_local_tcp_port, local_tls_port=GeneralConfig.sip_local_tls_port, sip_transports=GeneralConfig.sip_transports, auto_hangup=None)
options._update_loose(dict((name, value) for name, value in default_options.items() if getattr(options, name, None) is None))
for transport in set(["tls", "tcp", "udp"]) - set(options.sip_transports):
setattr(options, "local_%s_port" % transport, None)
if not retval["use_bonjour"]:
if not all([options.sip_address, options.password]):
raise RuntimeError("No complete set of SIP credentials specified in config file and on commandline.")
for attr in default_options:
retval[attr] = getattr(options, attr)
try:
if retval["use_bonjour"]:
retval["username"], retval["domain"] = None, None
else:
retval["username"], retval["domain"] = options.sip_address.split("@")
except ValueError:
raise RuntimeError("Invalid value for sip_address: %s" % options.sip_address)
else:
del retval["sip_address"]
if args:
retval["target_uri"] = format_cmdline_uri(args[0], retval["domain"])
else:
retval["target_uri"] = None
accounts = [(acc == 'Account') and 'default' or "'%s'" % acc[8:] for acc in configuration.parser.sections() if acc.startswith('Account')]
accounts.sort()
print "Accounts available: %s" % ', '.join(accounts)
if options.account_name is None:
print "Using default account: %s" % options.sip_address
else:
if not retval["use_bonjour"]:
print "Using account '%s': %s" % (options.account_name, options.sip_address)
return retval
def main():
do_invite(**parse_options())
if __name__ == "__main__":
try:
main()
except RuntimeError, e:
print "Error: %s" % str(e)
sys.exit(1)
except PyPJUAError, e:
print "Error: %s" % str(e)
sys.exit(1)
sys.exit(return_code)
diff --git a/scripts/sip_auto_publish_presence.py b/scripts/sip_auto_publish_presence.py
index e8b64cab..8dcaec27 100644
--- a/scripts/sip_auto_publish_presence.py
+++ b/scripts/sip_auto_publish_presence.py
@@ -1,446 +1,447 @@
#!/usr/bin/env python
import sys
import traceback
import string
import random
import socket
import os
import atexit
import select
import termios
import signal
import re
import subprocess
import datetime
from thread import start_new_thread, allocate_lock
from threading import Thread, Event
from Queue import Queue
from optparse import OptionParser, OptionValueError
from time import sleep
from collections import deque
from application.process import process
from application.configuration import *
from pypjua import *
from pypjua.clients import enrollment
from pypjua.clients.log import Logger
from pypjua.applications import BuilderError
from pypjua.applications.pidf import *
from pypjua.applications.presdm import *
from pypjua.applications.rpid import *
from pypjua.clients.clientconfig import get_path
-from pypjua.clients.lookup import *
+from pypjua.clients.dns_lookup import *
+from pypjua.clients import *
class GeneralConfig(ConfigSection):
_datatypes = {"local_ip": datatypes.IPAddress, "sip_transports": datatypes.StringList, "trace_pjsip": datatypes.Boolean, "trace_sip": datatypes.Boolean}
local_ip = None
sip_local_udp_port = 0
sip_local_tcp_port = 0
sip_local_tls_port = 0
sip_transports = ["tls", "tcp", "udp"]
trace_pjsip = False
trace_sip = False
log_directory = '~/.sipclient/log'
class AccountConfig(ConfigSection):
_datatypes = {"sip_address": str, "password": str, "display_name": str, "outbound_proxy": OutboundProxy, "use_presence_agent": datatypes.Boolean}
sip_address = None
password = None
display_name = None
outbound_proxy = None
use_presence_agent = True
process._system_config_directory = os.path.expanduser("~/.sipclient")
enrollment.verify_account_config()
configuration = ConfigFile("config.ini")
configuration.read_settings("General", GeneralConfig)
queue = Queue()
getstr_event = Event()
packet_count = 0
start_time = None
old = None
user_quit = True
want_quit = False
lock = allocate_lock()
pub = None
sip_uri = None
string = None
logger = None
return_code = 1
pidf = None
user_agent = None
def publish_pidf():
try:
pub.publish("application", "pidf+xml", pidf.toxml())
except BuilderError, e:
print "PIDF as currently defined is invalid: %s" % str(e)
except:
traceback.print_exc()
def random_note():
try:
fortune = subprocess.Popen('fortune', stdout=subprocess.PIPE)
fortune.wait()
return ' '.join(s for s in re.split(r'\n|\t', fortune.stdout.read()) if s != '')
except:
return 'Fortune is not installed'
def auto_publish(interval):
# initialize top level elements
tuple = Tuple(''.join(chr(random.randint(97, 122)) for i in xrange(8)), status=Status(basic=Basic('open')))
tuple.contact = Contact("sip:%s@%s" % (sip_uri.user, sip_uri.host))
tuple.contact.priority = "0"
tuple.relationship = Relationship('self')
tuple.timestamp = Timestamp()
pidf.append(tuple)
# add email service
email_tuple = Tuple(''.join(chr(random.randint(97, 122)) for i in xrange(8)), status=Status(basic=Basic('open')))
email_tuple.contact = Contact("mailto:%s@%s" % (sip_uri.user, sip_uri.host))
email_tuple.contact.priority = "0.5"
email_tuple.relationship = Relationship('self')
email_tuple.timestamp = Timestamp()
pidf.append(email_tuple)
person = Person(''.join(chr(random.randint(97, 122)) for i in xrange(8)))
person.privacy = Privacy()
person.time_offset = TimeOffset()
person.timestamp = DMTimestamp()
pidf.append(person)
device = Device(''.join(chr(random.randint(97, 122)) for i in xrange(8)))
device.notes.append(DMNote('Powered by %s' % user_agent, lang='en'))
device.timestamp = DMTimestamp()
device.user_input = UserInput()
pidf.append(device)
while True:
# 50% chance that basic status will change
if random.randint(0, 1) == 1:
if tuple.status.basic == 'open':
tuple.status.basic = Basic('closed')
else:
tuple.status.basic = Basic('open')
tuple.timestamp = Timestamp()
# set sphere (9-18 at work (except on weekends), else at home)
now = datetime.datetime.now()
if (now.hour >= 9 and now.hour < 18) and now.isoweekday() not in (6, 7):
person.sphere = Sphere(Work())
person.sphere.since = datetime.datetime(now.year, now.month, now.day, 9, 0)
person.sphere.until = datetime.datetime(now.year, now.month, now.day, 18, 0)
else:
person.sphere = Sphere(Home())
# set privacy
person.privacy.audio = random.choice((True, False))
person.privacy.text = random.choice((True, False))
person.privacy.video = random.choice((True, False))
# set status icon
person.status_icon = StatusIcon("http://sipsimpleclient.com/chrome/site/StatusIcons/%s.png" % random.choice(('available', 'busy')))
# change person note
if len(person.notes) > 0:
del person.notes['en']
person.notes.append(DMNote(random_note(), lang='en'))
# change person activity
if person.activities is None:
person.activities = Activities()
else:
person.activities.clear()
values = list(Activities._xml_value_maps.get(value, value) for value in Activities._xml_values if value != 'unknown')
for i in xrange(random.randrange(3)):
person.activities.add(random.choice(values))
# change person mood
if person.mood is None:
person.mood = Mood()
else:
person.mood.clear()
values = list(Mood._xml_value_maps.get(value, value) for value in Mood._xml_values if value != 'unknown')
for i in xrange(random.randrange(3)):
person.mood.add(random.choice(values))
# change place is
if person.place_is is None:
person.place_is = PlaceIs()
# 50% chance that place is will change
if random.randint(0, 1) == 1:
person.place_is.audio = Audio(random.choice(list(Audio._xml_values)))
if random.randint(0, 1) == 1:
person.place_is.video = Video(random.choice(list(Video._xml_values)))
if random.randint(0, 1) == 1:
person.place_is.text = Text(random.choice(list(Text._xml_values)))
person.timestamp = DMTimestamp()
# set user-input
if device.user_input.value == 'idle':
# 50 % chance to change to active:
if random.randint(0, 1) == 1:
device.user_input.value = 'active'
device.user_input.last_input = None
else:
# 50 % chance to change to idle:
if random.randint(0, 1) == 1:
device.user_input.value = 'idle'
device.user_input.last_input = now - datetime.timedelta(seconds=30)
# publish new pidf
publish_pidf()
sleep(interval)
def termios_restore():
global old
if old is not None:
termios.tcsetattr(sys.stdin.fileno(), termios.TCSADRAIN, old)
atexit.register(termios_restore)
def getstr(prompt='selection'):
global string, getstr_event
string = ''
sys.stdout.write("%s> " % prompt)
sys.stdout.flush()
getstr_event.wait()
getstr_event.clear()
sys.stdout.write("\n")
ret = string
string = None
return ret
def getchar():
global old
fd = sys.stdin.fileno()
if os.isatty(fd):
old = termios.tcgetattr(fd)
new = termios.tcgetattr(fd)
new[3] = new[3] & ~termios.ICANON & ~termios.ECHO
new[6][termios.VMIN] = '\000'
try:
termios.tcsetattr(fd, termios.TCSADRAIN, new)
if select.select([fd], [], [], None)[0]:
return sys.stdin.read(4192)
except select.error, e:
if e[0] != 4:
raise
return ''
finally:
termios_restore()
else:
return os.read(fd, 4192)
def event_handler(event_name, **kwargs):
global packet_count, start_time, queue, do_trace_pjsip, logger, want_quit, pub, return_code
if event_name == "Publication_state":
if kwargs["state"] == "unpublished":
queue.put(("print", "Unpublished: %(code)d %(reason)s" % kwargs))
if want_quit or kwargs['code'] in (401, 403, 407):
if kwargs['code'] / 100 == 2:
return_code = 0
queue.put(("quit", None))
else:
pub = Publication(pub.credentials, pub.event, route=pub.route, expires=pub.expires)
publish_pidf()
elif kwargs["state"] == "published":
queue.put(("print", "PUBLISH was successful"))
pass
elif event_name == "siptrace":
logger.log(event_name, **kwargs)
elif event_name != "log":
queue.put(("pypjua_event", (event_name, kwargs)))
elif do_trace_pjsip:
queue.put(("print", "%(timestamp)s (%(level)d) %(sender)14s: %(message)s" % kwargs))
def read_queue(e, username, domain, password, display_name, route, expires, do_trace_pjsip, interval):
global user_quit, lock, queue, pub, sip_uri, pidf, user_agent
lock.acquire()
try:
sip_uri = SIPURI(user=username, host=domain, display=display_name)
pub = Publication(Credentials(sip_uri, password), "presence", route=route, expires=expires)
# initialize PIDF
pidf = PIDF(entity='%s@%s' % (username, domain))
user_agent = e.user_agent
#initialize auto publisher
start_new_thread(auto_publish, (interval,))
while True:
command, data = queue.get()
if command == "print":
print data
if command == "pypjua_event":
event_name, args = data
if command == "user_input":
key = data
if command == "eof":
command = "end"
user_quit = True
if command == "end":
try:
pub.unpublish()
except:
pass
if command == "quit":
user_quit = False
break
except:
user_quit = False
traceback.print_exc()
finally:
e.stop()
logger.stop()
if not user_quit:
os.kill(os.getpid(), signal.SIGINT)
lock.release()
def sig_handler(signum, frame):
global queue, want_quit
want_quit = True
queue.put(("end", None))
def do_publish(**kwargs):
global user_quit, want_quit, lock, queue, do_trace_pjsip, string, getstr_event, old, logger
ctrl_d_pressed = False
do_trace_pjsip = kwargs["do_trace_pjsip"]
outbound_proxy = kwargs.pop("outbound_proxy")
if outbound_proxy is None:
routes = lookup_routes_for_sip_uri(SIPURI(host=kwargs["domain"]), kwargs.pop("sip_transports"))
else:
routes = lookup_routes_for_sip_uri(outbound_proxy, kwargs.pop("sip_transports"))
# Only try the first Route for now
try:
kwargs["route"] = routes[0]
except IndexError:
raise RuntimeError("No route found to SIP proxy")
logger = Logger(AccountConfig, GeneralConfig.log_directory, trace_sip=kwargs['trace_sip'])
if kwargs.pop('trace_sip'):
print "Logging SIP trace to file '%s'" % logger._siptrace_filename
e = Engine(event_handler, trace_sip=True, local_ip=kwargs.pop("local_ip"), local_udp_port=kwargs.pop("local_udp_port"), local_tcp_port=kwargs.pop("local_tcp_port"), local_tls_port=kwargs.pop("local_tls_port"))
e.start(False)
start_new_thread(read_queue, (e,), kwargs)
atexit.register(termios_restore)
# unsubscribe on
signal.signal(signal.SIGUSR1, sig_handler)
try:
while True:
for char in getchar():
if char == "\x04":
if not ctrl_d_pressed:
queue.put(("eof", None))
ctrl_d_pressed = True
want_quit = True
break
else:
if string is not None:
if char == "\x7f":
if len(string) > 0:
char = "\x08"
sys.stdout.write("\x08 \x08")
sys.stdout.flush()
string = string[:-1]
else:
if old is not None:
sys.stdout.write(char)
sys.stdout.flush()
if char == "\x0A":
getstr_event.set()
break
else:
string += char
else:
queue.put(("user_input", char))
except KeyboardInterrupt:
if user_quit:
print "Ctrl+C pressed, exiting instantly!"
want_quit = True
queue.put(("quit", True))
return
def parse_outbound_proxy(option, opt_str, value, parser):
try:
parser.values.outbound_proxy = OutboundProxy(value)
except ValueError, e:
raise OptionValueError(e.message)
def parse_options():
retval = {}
description = "This script will publish rich presence state of the specified SIP account to a SIP Presence Agent, the presence information can be changed using a menu-driven interface."
usage = "%prog [options]"
parser = OptionParser(usage=usage, description=description)
parser.print_usage = parser.print_help
parser.add_option("-a", "--account-name", type="string", dest="account_name", help="The account name from which to read account settings. Corresponds to section Account_NAME in the configuration file. If not supplied, the section Account will be read.", metavar="NAME")
parser.add_option("--sip-address", type="string", dest="sip_address", help="SIP address of the user in the form user@domain")
parser.add_option("-e", "--expires", type="int", dest="expires", help='"Expires" value to set in PUBLISH. Default is 300 seconds.')
parser.add_option("-o", "--outbound-proxy", type="string", action="callback", callback=parse_outbound_proxy, help="Outbound SIP proxy to use. By default a lookup of the domain is performed based on SRV and A records. This overrides the setting from the config file.", metavar="IP[:PORT]")
parser.add_option("-i", "--interval", type="int", dest="interval", help='Time between state changes. Default is 60 seconds.')
parser.add_option("-s", "--trace-sip", action="store_true", dest="trace_sip", help="Dump the raw contents of incoming and outgoing SIP messages (disabled by default).")
parser.add_option("-j", "--trace-pjsip", action="store_true", dest="do_trace_pjsip", help="Print PJSIP logging output (disabled by default).")
options, args = parser.parse_args()
if options.account_name is None:
account_section = "Account"
else:
account_section = "Account_%s" % options.account_name
if account_section not in configuration.parser.sections():
raise RuntimeError("There is no account section named '%s' in the configuration file" % account_section)
configuration.read_settings(account_section, AccountConfig)
if not AccountConfig.use_presence_agent:
raise RuntimeError("Presence is not enabled for this account. Please set use_presence_agent=True in the config file")
default_options = dict(expires=300, outbound_proxy=AccountConfig.outbound_proxy, sip_address=AccountConfig.sip_address, password=AccountConfig.password, display_name=AccountConfig.display_name, trace_sip=GeneralConfig.trace_sip, do_trace_pjsip=GeneralConfig.trace_pjsip, local_ip=GeneralConfig.local_ip, local_udp_port=GeneralConfig.sip_local_udp_port, local_tcp_port=GeneralConfig.sip_local_tcp_port, local_tls_port=GeneralConfig.sip_local_tls_port, sip_transports=GeneralConfig.sip_transports, interval=60)
options._update_loose(dict((name, value) for name, value in default_options.items() if getattr(options, name, None) is None))
for transport in set(["tls", "tcp", "udp"]) - set(options.sip_transports):
setattr(options, "local_%s_port" % transport, None)
if not all([options.sip_address, options.password]):
raise RuntimeError("No complete set of SIP credentials specified in config file and on commandline.")
for attr in default_options:
retval[attr] = getattr(options, attr)
try:
retval["username"], retval["domain"] = options.sip_address.split("@")
except ValueError:
raise RuntimeError("Invalid value for sip_address: %s" % options.sip_address)
else:
del retval["sip_address"]
accounts = [(acc == 'Account') and 'default' or "'%s'" % acc[8:] for acc in configuration.parser.sections() if acc.startswith('Account')]
accounts.sort()
print "Accounts available: %s" % ', '.join(accounts)
if options.account_name is None:
print "Using default account: %s" % options.sip_address
else:
print "Using account '%s': %s" % (options.account_name, options.sip_address)
return retval
def main():
do_publish(**parse_options())
if __name__ == "__main__":
try:
main()
except RuntimeError, e:
print "Error: %s" % str(e)
sys.exit(1)
except PyPJUAError, e:
print "Error: %s" % str(e)
sys.exit(1)
sys.exit(return_code)
diff --git a/scripts/sip_message.py b/scripts/sip_message.py
index 376055f5..7329ac1d 100644
--- a/scripts/sip_message.py
+++ b/scripts/sip_message.py
@@ -1,240 +1,240 @@
#!/usr/bin/env python
import sys
import traceback
import os
import signal
import random
from thread import start_new_thread, allocate_lock
from Queue import Queue
from optparse import OptionParser, OptionValueError
from application.configuration import *
from application.process import process
from pypjua import *
from pypjua.clients import enrollment
-from pypjua.clients.lookup import *
-from pypjua.clients import format_cmdline_uri
+from pypjua.clients.dns_lookup import *
+from pypjua.clients import *
from pypjua.clients.log import Logger
class GeneralConfig(ConfigSection):
_datatypes = {"local_ip": datatypes.IPAddress, "sip_transports": datatypes.StringList, "trace_pjsip": datatypes.Boolean, "trace_sip": datatypes.Boolean}
local_ip = None
sip_local_udp_port = 0
sip_local_tcp_port = 0
sip_local_tls_port = 0
sip_transports = ["tls", "tcp", "udp"]
trace_pjsip = False
trace_sip = False
log_directory = '~/.sipclient/log'
class AccountConfig(ConfigSection):
_datatypes = {"sip_address": str, "password": str, "display_name": str, "outbound_proxy": OutboundProxy}
sip_address = None
password = None
display_name = None
outbound_proxy = None
process._system_config_directory = os.path.expanduser("~/.sipclient")
enrollment.verify_account_config()
configuration = ConfigFile("config.ini")
configuration.read_settings("General", GeneralConfig)
queue = Queue()
packet_count = 0
start_time = None
user_quit = True
lock = allocate_lock()
logger = None
def event_handler(event_name, **kwargs):
global start_time, packet_count, queue, do_trace_pjsip, logger
if event_name == "siptrace":
logger.log(event_name, **kwargs)
elif event_name != "log":
queue.put(("pypjua_event", (event_name, kwargs)))
elif do_trace_pjsip:
queue.put(("print", "%(timestamp)s (%(level)d) %(sender)14s: %(message)s" % kwargs))
def read_queue(e, username, domain, password, display_name, route, target_uri, message):
global user_quit, lock, queue
lock.acquire()
printed = False
sent = False
msg_buf = []
try:
credentials = Credentials(SIPURI(user=username, host=domain, display=display_name), password)
if target_uri is None:
reg = Registration(credentials, route=route)
print 'Registering "%s" at %s:%s:%d' % (credentials.uri, route.transport, route.host, route.port)
reg.register()
else:
if message is None:
print "Press Ctrl+D on an empty line to end input and send the MESSAGE request."
else:
msg_buf.append(message)
queue.put(("eof", None))
while True:
command, data = queue.get()
if command == "print":
print data
if command == "pypjua_event":
event_name, args = data
if event_name == "Registration_state":
if args["state"] == "registered":
if not printed:
print "REGISTER was successful"
print "Contact: %s (expires in %d seconds)" % (args["contact_uri"], args["expires"])
if len(args["contact_uri_list"]) > 1:
print "Other registered contacts:\n%s" % "\n".join(["%s (expires in %d seconds)" % contact_tup for contact_tup in args["contact_uri_list"] if contact_tup[0] != args["contact_uri"]])
print "Press Ctrl+D to stop the program."
printed = True
elif args["state"] == "unregistered":
if "code" in args and args["code"] / 100 != 2:
print "Unregistered: %(code)d %(reason)s" % args
user_quit = False
command = "quit"
elif event_name == "Invitation_state":
if args["state"] == "INCOMING":
args["obj"].end()
elif event_name == "message":
print 'Received MESSAGE from "%(from_uri)s", Content-Type: %(content_type)s/%(content_subtype)s' % args
print args["body"]
elif event_name == "message_response":
if args["code"] / 100 != 2:
print "Could not deliver MESSAGE: %(code)d %(reason)s" % args
else:
print "MESSAGE was accepted by remote party."
user_quit = False
command = "quit"
if command == "user_input":
if not sent:
msg_buf.append(data)
if command == "eof":
if target_uri is None:
reg.unregister()
elif not sent:
sent = True
print 'Sending MESSAGE from "%s" to "%s" using proxy %s:%s:%d' % (credentials.uri, target_uri, route.transport, route.host, route.port)
send_message(credentials, target_uri, "text", "plain", "\n".join(msg_buf), route)
if command == "quit":
break
except:
user_quit = False
traceback.print_exc()
finally:
e.stop()
logger.stop()
if not user_quit:
os.kill(os.getpid(), signal.SIGINT)
lock.release()
def do_message(**kwargs):
global user_quit, lock, queue, do_trace_pjsip, logger
do_trace_pjsip = kwargs.pop("do_trace_pjsip")
outbound_proxy = kwargs.pop("outbound_proxy")
ctrl_d_pressed = False
if outbound_proxy is None:
routes = lookup_routes_for_sip_uri(SIPURI(host=kwargs["domain"]), kwargs.pop("sip_transports"))
else:
routes = lookup_routes_for_sip_uri(outbound_proxy, kwargs.pop("sip_transports"))
# Only try the first Route for now
try:
kwargs["route"] = routes[0]
except IndexError:
raise RuntimeError("No route found to SIP proxy")
logger = Logger(AccountConfig, GeneralConfig.log_directory, trace_sip=kwargs['trace_sip'])
if kwargs['trace_sip']:
print "Logging SIP trace to file '%s'" % logger._siptrace_filename
e = Engine(event_handler, trace_sip=kwargs.pop("trace_sip"), local_ip=kwargs.pop("local_ip"), local_udp_port=kwargs.pop("local_udp_port"), local_tcp_port=kwargs.pop("local_tcp_port"), local_tls_port=kwargs.pop("local_tls_port"))
e.start(False)
if kwargs["target_uri"] is not None:
kwargs["target_uri"] = e.parse_sip_uri(kwargs["target_uri"])
start_new_thread(read_queue, (e,), kwargs)
try:
while True:
try:
msg = raw_input()
queue.put(("user_input", msg))
except EOFError:
if not ctrl_d_pressed:
queue.put(("eof", None))
ctrl_d_pressed = True
except KeyboardInterrupt:
if user_quit:
print "Ctrl+C pressed, exiting instantly!"
queue.put(("quit", True))
lock.acquire()
return
def parse_outbound_proxy(option, opt_str, value, parser):
try:
parser.values.outbound_proxy = OutboundProxy(value)
except ValueError, e:
raise OptionValueError(e.message)
def parse_options():
retval = {}
description = "This will either sit idle waiting for an incoming MESSAGE request, or send a MESSAGE request to the specified SIP target. In outgoing mode the program will read the contents of the messages to be sent from standard input, Ctrl+D signalling EOF as usual. In listen mode the program will quit when Ctrl+D is pressed."
usage = "%prog [options] [target-user@target-domain.com]"
parser = OptionParser(usage=usage, description=description)
parser.print_usage = parser.print_help
parser.add_option("-a", "--account-name", type="string", dest="account_name", help="The account name from which to read account settings. Corresponds to section Account_NAME in the configuration file.")
parser.add_option("--sip-address", type="string", dest="sip_address", help="SIP login account")
parser.add_option("-p", "--password", type="string", dest="password", help="Password to use to authenticate the local account. This overrides the setting from the config file.")
parser.add_option("-n", "--display-name", type="string", dest="display_name", help="Display name to use for the local account. This overrides the setting from the config file.")
parser.add_option("-o", "--outbound-proxy", type="string", action="callback", callback=parse_outbound_proxy, help="Outbound SIP proxy to use. By default a lookup of the domain is performed based on SRV and A records. This overrides the setting from the config file.", metavar="IP[:PORT]")
parser.add_option("-s", "--trace-sip", action="store_true", dest="trace_sip", help="Dump the raw contents of incoming and outgoing SIP messages (disabled by default).")
parser.add_option("-m", "--message", type="string", dest="message", help="Contents of the message to send. This disables reading the message from standard input.")
parser.add_option("-j", "--trace-pjsip", action="store_true", dest="do_trace_pjsip", help="Print PJSIP logging output (disabled by default).")
options, args = parser.parse_args()
if options.account_name is None:
account_section = "Account"
else:
account_section = "Account_%s" % options.account_name
if account_section not in configuration.parser.sections():
raise RuntimeError("There is no account section named '%s' in the configuration file" % account_section)
configuration.read_settings(account_section, AccountConfig)
default_options = dict(outbound_proxy=AccountConfig.outbound_proxy, sip_address=AccountConfig.sip_address, password=AccountConfig.password, display_name=AccountConfig.display_name, trace_sip=GeneralConfig.trace_sip, message=None, do_trace_pjsip=GeneralConfig.trace_pjsip, local_ip=GeneralConfig.local_ip, local_udp_port=GeneralConfig.sip_local_udp_port, local_tcp_port=GeneralConfig.sip_local_tcp_port, local_tls_port=GeneralConfig.sip_local_tls_port, sip_transports=GeneralConfig.sip_transports)
options._update_loose(dict((name, value) for name, value in default_options.items() if getattr(options, name, None) is None))
for transport in set(["tls", "tcp", "udp"]) - set(options.sip_transports):
setattr(options, "local_%s_port" % transport, None)
if not all([options.sip_address, options.password]):
raise RuntimeError("No complete set of SIP credentials specified in config file and on commandline.")
for attr in default_options:
retval[attr] = getattr(options, attr)
try:
retval["username"], retval["domain"] = options.sip_address.split("@")
except ValueError:
raise RuntimeError("Invalid value for sip_address: %s" % options.sip_address)
else:
del retval["sip_address"]
if args:
retval["target_uri"] = format_cmdline_uri(args[0], retval["domain"])
else:
retval["target_uri"] = None
accounts = [(acc == 'Account') and 'default' or "'%s'" % acc[8:] for acc in configuration.parser.sections() if acc.startswith('Account')]
accounts.sort()
print "Accounts available: %s" % ', '.join(accounts)
if options.account_name is None:
print "Using default account: %s" % options.sip_address
else:
print "Using account '%s': %s" % (options.account_name, options.sip_address)
return retval
def main():
do_message(**parse_options())
if __name__ == "__main__":
try:
main()
except RuntimeError, e:
print "Error: %s" % str(e)
sys.exit(1)
except PyPJUAError, e:
print "Error: %s" % str(e)
sys.exit(1)
diff --git a/scripts/sip_publish_presence.py b/scripts/sip_publish_presence.py
index 94f72cf0..705a5662 100644
--- a/scripts/sip_publish_presence.py
+++ b/scripts/sip_publish_presence.py
@@ -1,703 +1,704 @@
#!/usr/bin/env python
import sys
import traceback
import string
import random
import socket
import os
import atexit
import select
import termios
import signal
from thread import start_new_thread, allocate_lock
from threading import Thread, Event
from Queue import Queue
from optparse import OptionParser, OptionValueError
from time import sleep
from collections import deque
from application.process import process
from application.configuration import *
from pypjua import *
from pypjua.clients import enrollment
from pypjua.clients.log import Logger
from pypjua.applications import BuilderError
from pypjua.applications.pidf import *
from pypjua.applications.presdm import *
from pypjua.applications.rpid import *
from pypjua.clients.clientconfig import get_path
-from pypjua.clients.lookup import *
+from pypjua.clients.dns_lookup import *
+from pypjua.clients import *
class GeneralConfig(ConfigSection):
_datatypes = {"local_ip": datatypes.IPAddress, "sip_transports": datatypes.StringList, "trace_pjsip": datatypes.Boolean, "trace_sip": datatypes.Boolean}
local_ip = None
sip_local_udp_port = 0
sip_local_tcp_port = 0
sip_local_tls_port = 0
sip_transports = ["tls", "tcp", "udp"]
trace_pjsip = False
trace_sip = False
log_directory = '~/.sipclient/log'
class AccountConfig(ConfigSection):
_datatypes = {"sip_address": str, "password": str, "display_name": str, "outbound_proxy": OutboundProxy, "use_presence_agent": datatypes.Boolean}
sip_address = None
password = None
display_name = None
outbound_proxy = None
use_presence_agent = True
process._system_config_directory = os.path.expanduser("~/.sipclient")
enrollment.verify_account_config()
configuration = ConfigFile("config.ini")
configuration.read_settings("General", GeneralConfig)
queue = Queue()
getstr_event = Event()
packet_count = 0
start_time = None
old = None
user_quit = True
lock = allocate_lock()
pub = None
sip_uri = None
string = None
logger = None
return_code = 1
pidf = None
person = None
tuple = None
menu_stack = deque()
def publish_pidf():
try:
pub.publish("application", "pidf+xml", pidf.toxml())
except BuilderError, e:
print "PIDF as currently defined is invalid: %s" % str(e)
except:
traceback.print_exc()
def exit_program():
print 'Exiting...'
queue.put(("eof", None))
class Menu(object):
def __init__(self, interface):
interface['x'] = {"description": "exit to upper level menu", "handler": Menu.exitMenu}
interface['q'] = {"description": "quit program", "handler": exit_program}
self.interface = interface
def print_prompt(self):
print
buf = ["Commands:"]
for key, desc in self.interface.items():
buf.append(" %s: %s" % (key, desc['description']))
print "\n".join(buf)
print
def process_input(self, key):
desc = self.interface.get(key)
if desc is not None:
desc["handler"]()
else:
queue.put(("print", "Illegal key"))
def add_action(self, key, description):
self.interface[key] = description
def del_action(self, key):
try:
del self.interface[key]
except KeyError:
pass
@staticmethod
def gotoMenu(menu):
func = (lambda: menu_stack.append(menu))
func.menu = menu
return func
@staticmethod
def exitMenu():
menu_stack.pop()
@staticmethod
def exitTopLevel():
main = menu_stack.popleft()
menu_stack.clear()
menu_stack.append(main)
class NotesMenu(Menu):
def __init__(self, note_type, obj=None, timestamp_type=None):
Menu.__init__(self, {'s': {"description": "show current notes", "handler": self._show_notes},
'a': {"description": "add a note", "handler": self._add_note},
'd': {"description": "delete a note", "handler": self._del_note},
'c': {"description": "clear all note data", "handler": self._clear_notes}})
self.list = NoteList()
self.note_type = note_type
self.obj = obj
self.timestamp_type = timestamp_type
def _show_notes(self):
buf = ["Notes:"]
for note in self.list:
buf.append(" %s'%s'" % ((note.lang is None) and ' ' or (' (%s) ' % note.lang), note.value))
print '\n'.join(buf)
def _add_note(self):
lang = getstr("Language")
if lang == '':
lang = None
value = getstr("Note")
self.list.append(self.note_type(value, lang))
if self.obj:
self.obj.timestamp = self.timestamp_type()
print "Note added"
self.exitTopLevel()
def _del_note(self):
buf = ["Current notes:"]
for note in self.list:
buf.append(" %s'%s'" % ((note.lang is None) and ' ' or (' (%s) ' % note.lang), note.value))
print '\n'.join(buf)
print
lang = getstr("Language of note to delete")
if lang == '':
lang = None
try:
del self.list[lang]
except KeyError:
print "No note in language `%s'" % lang
else:
if self.obj:
self.obj.timestamp = self.timestamp_type()
print "Note deleted"
self.exitTopLevel()
def _clear_notes(self):
notes = list(self.list)
for note in notes:
del self.list[note.lang]
if self.obj:
self.obj.timestamp = self.timestamp_type()
print "Notes deleted"
self.exitTopLevel()
# Mood manipulation pidf
class MoodMenu(Menu):
def __init__(self):
Menu.__init__(self, {'s': {"description": "show current moods", "handler": self._show_moods},
'a': {"description": "add a mood", "handler": self._add_mood},
'd': {"description": "delete a mood", "handler": self._del_mood},
'c': {"description": "clear all mood data", "handler": self._clear_moods},
'n': {"description": "set mood note", "handler": self._set_note},
'r': {"description": "set random mood", "handler": self._set_random}})
self.auto_random = False
def _show_moods(self):
buf = ["Moods:"]
if person.mood is not None:
for m in person.mood.values:
buf.append(" %s" % str(m))
print '\n'.join(buf)
def _add_mood(self):
buf = ["Possible moods:"]
values = list(Mood._xml_value_maps.get(value, value) for value in Mood._xml_values)
values.sort()
max_len = max(len(s) for s in values)+2
format = " %%02d) %%-%ds" % max_len
num_line = 72/(max_len+5)
i = 0
text = ''
for val in values:
text += format % (i+1, val)
i += 1
if i % num_line == 0:
buf.append(text)
text = ''
print '\n'.join(buf)
print
m = getstr("Select mood to add (any non-number will string will return")
try:
m = int(m)
if m not in xrange(len(values)):
raise ValueError
except ValueError:
print "Invalid input"
else:
if person.mood is None:
person.mood = Mood()
person.mood.add(values[m-1])
person.timestamp = DMTimestamp()
publish_pidf()
print "Mood added"
self.exitTopLevel()
def _del_mood(self):
if person.mood is None:
print "There is no current mood set"
return
buf = ["Current moods:"]
values = person.mood.values
values.sort()
max_len = max(len(s) for s in values)+2
format = " %%02d) %%-%ds" % max_len
num_line = 72/(max_len+5)
i = 0
text = ''
for val in values:
text += format % (i+1, val)
i += 1
if i % num_line == 0:
buf.append(text)
text = ''
buf.append(text)
print '\n'.join(buf)
print
m = getstr("Select mood to delete")
try:
m = int(m)
except ValueError:
print "Invalid input"
else:
person.mood.remove(values[m-1])
person.timestamp = DMTimestamp()
publish_pidf()
print "Mood deleted"
self.exitTopLevel()
def _clear_moods(self):
if person.mood is None:
print "There is no current mood set"
return
person.mood = None
person.timestamp = DMTimestamp()
publish_pidf()
print "Mood information cleared"
self.exitTopLevel()
def _set_note(self):
if person.mood is not None and len(person.mood.notes) > 0:
print 'Current note: %s' % person.mood.notes['en']
note = getstr("Set note")
if note == '':
if person.mood is not None and len(person.mood.notes) > 0:
del person.mood.notes['en']
else:
if person.mood is None:
person.mood = Mood()
person.mood.notes.append(RPIDNote(note, lang='en'))
person.timestamp = DMTimestamp()
publish_pidf()
print 'Note set'
self.exitTopLevel()
def _set_random(self):
values = list(Mood._xml_value_maps.get(value, value) for value in Mood._xml_values if value != 'unknown')
random.shuffle(values)
if person.mood is None:
person.mood = Mood()
else:
person.mood.clear()
values = values[:3]
for mood in values:
person.mood.add(mood)
person.timestamp = DMTimestamp()
publish_pidf()
print "You are now " + ", ".join(values)
self.exitTopLevel()
def _set_auto_random(self):
if self.auto_random:
pass
# Activities manipulation pidf
class ActivitiesMenu(Menu):
def __init__(self):
Menu.__init__(self, {'s': {"description": "show current activity", "handler": self._show_activity},
'a': {"description": "set activity", "handler": self._set_activity},
'd': {"description": "delete activity", "handler": self._del_activity},
'c': {"description": "clear all activity data", "handler": self._clear_activity},
'n': {"description": "set activity note", "handler": self._set_note},
'r': {"description": "set random activity", "handler": self._set_random}})
def _show_activity(self):
buf = ["Activity:"]
if person.activities is not None:
for a in person.activities.values:
buf.append(" %s" % str(a))
print '\n'.join(buf)
def _set_activity(self):
buf = ["Possible activities:"]
values = list(Activities._xml_value_maps.get(value, value) for value in Activities._xml_values)
values.sort()
max_len = max(len(s) for s in values)+2
format = " %%02d) %%-%ds" % max_len
num_line = 72/(max_len+5)
i = 0
text = ''
for val in values:
text += format % (i+1, val)
i += 1
if i % num_line == 0:
buf.append(text)
text = ''
print '\n'.join(buf)
print
a = getstr("Select activity to add")
try:
a = int(a)
if a not in xrange(len(values)):
raise ValueError
except ValueError:
print "Invalid input"
else:
if person.activities is None:
person.activities = Activities()
else:
person.activities.clear()
person.activities.add(values[a-1])
person.timestamp = DMTimestamp()
publish_pidf()
print "Activity set"
self.exitTopLevel()
def _del_activity(self):
if person.activities is None or len(person.activities.values) == 0:
print "There is no current activity set"
return
person.activities.clear()
person.timestamp = DMTimestamp()
publish_pidf()
print "Activity deleted"
self.exitTopLevel()
def _clear_activity(self):
if person.activities is None:
print "There is no current activity set"
return
person.activities = None
person.timestamp = DMTimestamp()
publish_pidf()
print "Activities information cleared"
self.exitTopLevel()
def _set_note(self):
if person.activities is not None and len(person.activities.notes) > 0:
print 'Current note: %s' % person.activities.notes['en']
note = getstr("Set note")
if note == '':
if person.activities is not None and len(person.activities.notes) > 0:
del person.activities.notes['en']
else:
if person.activities is None:
person.activities = Activities()
person.activities.notes.append(RPIDNote(note, lang='en'))
person.timestamp = DMTimestamp()
publish_pidf()
print 'Note set'
self.exitTopLevel()
def _set_random(self):
values = list(Activities._xml_value_maps.get(value, value) for value in Activities._xml_values if value != 'unknown')
activity = random.choice(values)
if person.activities is None:
person.activities = Activities()
else:
person.activities.clear()
person.activities.add(activity)
person.timestamp = DMTimestamp()
publish_pidf()
print "You are now %s" % activity
self.exitTopLevel()
def set_person_note():
if len(person.notes) > 0:
print 'Current note: %s' % person.notes['en']
note = getstr("Set note")
if note == '':
if len(person.notes) > 0:
del person.notes['en']
else:
person.notes.append(DMNote(note, lang='en'))
person.timestamp = DMTimestamp()
publish_pidf()
print 'Note added'
def toggle_basic():
if tuple.status.basic == 'open':
tuple.status.basic = Basic('closed')
tuple.timestamp = Timestamp()
publish_pidf()
print "Your basic status is now 'closed'"
else:
tuple.status.basic = Basic('open')
tuple.timestamp = Timestamp()
publish_pidf()
print "Your basic status is now 'open'"
def termios_restore():
global old
if old is not None:
termios.tcsetattr(sys.stdin.fileno(), termios.TCSADRAIN, old)
atexit.register(termios_restore)
def getstr(prompt='selection'):
global string, getstr_event
string = ''
sys.stdout.write("%s> " % prompt)
sys.stdout.flush()
getstr_event.wait()
getstr_event.clear()
sys.stdout.write("\n")
ret = string
string = None
return ret
def getchar():
global old
fd = sys.stdin.fileno()
if os.isatty(fd):
old = termios.tcgetattr(fd)
new = termios.tcgetattr(fd)
new[3] = new[3] & ~termios.ICANON & ~termios.ECHO
new[6][termios.VMIN] = '\000'
try:
termios.tcsetattr(fd, termios.TCSADRAIN, new)
if select.select([fd], [], [], None)[0]:
return sys.stdin.read(4192)
finally:
termios_restore()
else:
return os.read(fd, 4192)
def event_handler(event_name, **kwargs):
global packet_count, start_time, queue, do_trace_pjsip, logger, return_code
if event_name == "Publication_state":
if kwargs["state"] == "unpublished":
queue.put(("print", "Unpublished: %(code)d %(reason)s" % kwargs))
if kwargs["code"] / 100 == 2:
return_code = 0
queue.put(("quit", None))
elif kwargs["state"] == "published":
#queue.put(("print", "PUBLISH was successful"))
pass
elif event_name == "siptrace":
logger.log(event_name, **kwargs)
elif event_name != "log":
queue.put(("pypjua_event", (event_name, kwargs)))
elif do_trace_pjsip:
queue.put(("print", "%(timestamp)s (%(level)d) %(sender)14s: %(message)s" % kwargs))
def read_queue(e, username, domain, password, display_name, route, expires, do_trace_pjsip):
global user_quit, lock, queue, pub, sip_uri, pidf, person, tuple
lock.acquire()
try:
sip_uri = SIPURI(user=username, host=domain, display=display_name)
pub = Publication(Credentials(sip_uri, password), "presence", route=route, expires=expires)
# initialize PIDF
pidf = PIDF(entity='%s@%s' % (username, domain))
tuple = Tuple(''.join(chr(random.randint(97, 122)) for i in xrange(8)), status=Status(basic=Basic('open')))
tuple.timestamp = Timestamp()
pidf.append(tuple)
person = Person(''.join(chr(random.randint(97, 122)) for i in xrange(8)))
person.time_offset = TimeOffset()
person.timestamp = DMTimestamp()
pidf.append(person)
# initialize menus
top_level = Menu({'s': {"description": "show PIDF", "handler": lambda: sys.stdout.write(pidf.toxml(pretty_print=True))}})
top_level.del_action('x')
menu_stack.append(top_level)
top_level.add_action('m', {"description": "set mood information", "handler": Menu.gotoMenu(MoodMenu())})
top_level.add_action('a', {"description": "set activities information", "handler": Menu.gotoMenu(ActivitiesMenu())})
top_level.add_action('b', {"description": "toggle basic status", "handler": toggle_basic})
person_notes_menu = NotesMenu(DMNote, person, DMTimestamp)
top_level.add_action('n', {"description": "set note", "handler": set_person_note})
# publish initial pidf
publish_pidf()
# stuff that depends on menus
person.notes = person_notes_menu.list
menu_stack[-1].print_prompt()
while True:
command, data = queue.get()
if command == "print":
print data
menu_stack[-1].print_prompt()
if command == "pypjua_event":
event_name, args = data
if command == "user_input":
key = data
if command == "eof":
command = "end"
want_quit = True
if command == "end":
try:
pub.unpublish()
except:
pass
if command == "quit":
user_quit = False
break
if command == "user_input":
menu_stack[-1].process_input(data)
menu_stack[-1].print_prompt()
except:
user_quit = False
traceback.print_exc()
finally:
e.stop()
logger.stop()
if not user_quit:
os.kill(os.getpid(), signal.SIGINT)
lock.release()
def do_publish(**kwargs):
global user_quit, lock, queue, do_trace_pjsip, string, getstr_event, old, logger
ctrl_d_pressed = False
do_trace_pjsip = kwargs["do_trace_pjsip"]
outbound_proxy = kwargs.pop("outbound_proxy")
if outbound_proxy is None:
routes = lookup_routes_for_sip_uri(SIPURI(host=kwargs["domain"]), kwargs.pop("sip_transports"))
else:
routes = lookup_routes_for_sip_uri(outbound_proxy, kwargs.pop("sip_transports"))
# Only try the first Route for now
try:
kwargs["route"] = routes[0]
except IndexError:
raise RuntimeError("No route found to SIP proxy")
logger = Logger(AccountConfig, GeneralConfig.log_directory, trace_sip=kwargs['trace_sip'])
if kwargs.pop('trace_sip'):
print "Logging SIP trace to file '%s'" % logger._siptrace_filename
e = Engine(event_handler, trace_sip=True, local_ip=kwargs.pop("local_ip"), local_udp_port=kwargs.pop("local_udp_port"), local_tcp_port=kwargs.pop("local_tcp_port"), local_tls_port=kwargs.pop("local_tls_port"))
e.start(False)
start_new_thread(read_queue, (e,), kwargs)
atexit.register(termios_restore)
try:
while True:
for char in getchar():
if char == "\x04":
if not ctrl_d_pressed:
queue.put(("eof", None))
ctrl_d_pressed = True
break
else:
if string is not None:
if char == "\x7f":
if len(string) > 0:
char = "\x08"
sys.stdout.write("\x08 \x08")
sys.stdout.flush()
string = string[:-1]
else:
if old is not None:
sys.stdout.write(char)
sys.stdout.flush()
if char == "\x0A":
getstr_event.set()
break
else:
string += char
else:
queue.put(("user_input", char))
except KeyboardInterrupt:
if user_quit:
print "Ctrl+C pressed, exiting instantly!"
queue.put(("quit", True))
return
def parse_outbound_proxy(option, opt_str, value, parser):
try:
parser.values.outbound_proxy = OutboundProxy(value)
except ValueError, e:
raise OptionValueError(e.message)
def parse_options():
retval = {}
description = "This script will publish rich presence state of the specified SIP account to a SIP Presence Agent, the presence information can be changed using a menu-driven interface."
usage = "%prog [options]"
parser = OptionParser(usage=usage, description=description)
parser.print_usage = parser.print_help
parser.add_option("-a", "--account-name", type="string", dest="account_name", help="The account name from which to read account settings. Corresponds to section Account_NAME in the configuration file. If not supplied, the section Account will be read.", metavar="NAME")
parser.add_option("--sip-address", type="string", dest="sip_address", help="SIP address of the user in the form user@domain")
parser.add_option("-e", "--expires", type="int", dest="expires", help='"Expires" value to set in PUBLISH. Default is 300 seconds.')
parser.add_option("-o", "--outbound-proxy", type="string", action="callback", callback=parse_outbound_proxy, help="Outbound SIP proxy to use. By default a lookup of the domain is performed based on SRV and A records. This overrides the setting from the config file.", metavar="IP[:PORT]")
parser.add_option("-s", "--trace-sip", action="store_true", dest="trace_sip", help="Dump the raw contents of incoming and outgoing SIP messages (disabled by default).")
parser.add_option("-j", "--trace-pjsip", action="store_true", dest="do_trace_pjsip", help="Print PJSIP logging output (disabled by default).")
options, args = parser.parse_args()
if options.account_name is None:
account_section = "Account"
else:
account_section = "Account_%s" % options.account_name
if account_section not in configuration.parser.sections():
raise RuntimeError("There is no account section named '%s' in the configuration file" % account_section)
configuration.read_settings(account_section, AccountConfig)
if not AccountConfig.use_presence_agent:
raise RuntimeError("Presence is not enabled for this account. Please set use_presence_agent=True in the config file")
default_options = dict(expires=300, outbound_proxy=AccountConfig.outbound_proxy, sip_address=AccountConfig.sip_address, password=AccountConfig.password, display_name=AccountConfig.display_name, trace_sip=GeneralConfig.trace_sip, do_trace_pjsip=GeneralConfig.trace_pjsip, local_ip=GeneralConfig.local_ip, local_udp_port=GeneralConfig.sip_local_udp_port, local_tcp_port=GeneralConfig.sip_local_tcp_port, local_tls_port=GeneralConfig.sip_local_tls_port, sip_transports=GeneralConfig.sip_transports)
options._update_loose(dict((name, value) for name, value in default_options.items() if getattr(options, name, None) is None))
for transport in set(["tls", "tcp", "udp"]) - set(options.sip_transports):
setattr(options, "local_%s_port" % transport, None)
if not all([options.sip_address, options.password]):
raise RuntimeError("No complete set of SIP credentials specified in config file and on commandline.")
for attr in default_options:
retval[attr] = getattr(options, attr)
try:
retval["username"], retval["domain"] = options.sip_address.split("@")
except ValueError:
raise RuntimeError("Invalid value for sip_address: %s" % options.sip_address)
else:
del retval["sip_address"]
accounts = [(acc == 'Account') and 'default' or "'%s'" % acc[8:] for acc in configuration.parser.sections() if acc.startswith('Account')]
accounts.sort()
print "Accounts available: %s" % ', '.join(accounts)
if options.account_name is None:
print "Using default account: %s" % options.sip_address
else:
print "Using account '%s': %s" % (options.account_name, options.sip_address)
return retval
def main():
do_publish(**parse_options())
if __name__ == "__main__":
try:
main()
except RuntimeError, e:
print "Error: %s" % str(e)
sys.exit(1)
except PyPJUAError, e:
print "Error: %s" % str(e)
sys.exit(1)
sys.exit(return_code)
diff --git a/scripts/sip_register.py b/scripts/sip_register.py
index 8bde9603..f6464f79 100644
--- a/scripts/sip_register.py
+++ b/scripts/sip_register.py
@@ -1,250 +1,251 @@
#!/usr/bin/env python
import sys
import traceback
import os
import signal
import termios
import select
from thread import start_new_thread, allocate_lock
from Queue import Queue
from optparse import OptionParser, OptionValueError
from application.configuration import *
from application.process import process
from pypjua import *
from pypjua.clients import enrollment
from pypjua.clients.log import Logger
-from pypjua.clients.lookup import *
+from pypjua.clients.dns_lookup import *
+from pypjua.clients import *
class GeneralConfig(ConfigSection):
_datatypes = {"local_ip": datatypes.IPAddress, "sip_transports": datatypes.StringList, "trace_pjsip": datatypes.Boolean, "trace_sip": datatypes.Boolean}
local_ip = None
sip_local_udp_port = 0
sip_local_tcp_port = 0
sip_local_tls_port = 0
sip_transports = ["tls", "tcp", "udp"]
trace_pjsip = False
trace_sip = False
log_directory = '~/.sipclient/log'
class AccountConfig(ConfigSection):
_datatypes = {"sip_address": str, "password": str, "display_name": str, "outbound_proxy": OutboundProxy}
sip_address = None
password = None
display_name = None
outbound_proxy = None
process._system_config_directory = os.path.expanduser("~/.sipclient")
enrollment.verify_account_config()
configuration = ConfigFile("config.ini")
configuration.read_settings("General", GeneralConfig)
queue = Queue()
packet_count = 0
start_time = None
old = None
user_quit = True
lock = allocate_lock()
logger = None
return_code = 1
def termios_restore():
global old
if old is not None:
termios.tcsetattr(sys.stdin.fileno(), termios.TCSADRAIN, old)
def getchar():
global old
fd = sys.stdin.fileno()
if os.isatty(fd):
old = termios.tcgetattr(fd)
new = termios.tcgetattr(fd)
new[3] = new[3] & ~termios.ICANON & ~termios.ECHO
new[6][termios.VMIN] = '\000'
try:
termios.tcsetattr(fd, termios.TCSADRAIN, new)
if select.select([fd], [], [], None)[0]:
return sys.stdin.read(10)
finally:
termios_restore()
else:
return os.read(fd, 10)
def event_handler(event_name, **kwargs):
global start_time, packet_count, queue, do_trace_pjsip
if event_name == "siptrace":
logger.log(event_name, **kwargs)
elif event_name != "log":
queue.put(("pypjua_event", (event_name, kwargs)))
elif do_trace_pjsip:
queue.put(("print", "%(timestamp)s (%(level)d) %(sender)14s: %(message)s" % kwargs))
def read_queue(e, username, domain, password, display_name, route, expires, max_registers):
global user_quit, lock, queue, do_trace_pjsip, logger, return_code
lock.acquire()
printed = False
max_registers = max_registers or None
try:
credentials = Credentials(SIPURI(user=username, host=domain, display=display_name), password)
reg = Registration(credentials, route=route, expires=expires)
print 'Registering "%s" at %s:%s:%d' % (credentials.uri, route.transport, route.host, route.port)
reg.register()
while True:
command, data = queue.get()
if command == "print":
print data
if command == "pypjua_event":
event_name, args = data
if event_name == "Registration_state":
if args["state"] == "registered":
return_code = 0
if not printed:
print "REGISTER was successful"
print "Contact: %s (expires in %d seconds)" % (args["contact_uri"], args["expires"])
if len(args["contact_uri_list"]) > 1:
print "Other registered contacts:\n%s" % "\n".join(["%s (expires in %d seconds)" % contact_tup for contact_tup in args["contact_uri_list"] if contact_tup[0] != args["contact_uri"]])
print "Press Ctrl+D to stop the program."
printed = True
if max_registers is not None:
max_registers -= 1
if max_registers <= 0:
command = "eof"
elif args["state"] == "unregistered":
if "code" in args and args["code"] / 100 != 2:
print "Unregistered: %(code)d %(reason)s" % args
user_quit = False
command = "quit"
elif event_name == "Invitation_state":
if args["state"] == "INCOMING":
args["obj"].end()
if command == "user_input":
key = data
if key == 's':
logger.trace_sip = not logger.trace_sip
print "SIP tracing is now %s" % ("activated" if logger.trace_sip else "deactivated")
if key == 'l':
do_trace_pjsip = not do_trace_pjsip
print "PJSIP logging is now %s" % ("activated" if do_trace_pjsip else "deactivated")
if command == "eof":
reg.unregister()
if command == "quit":
break
except:
user_quit = False
traceback.print_exc()
finally:
e.stop()
logger.stop()
if not user_quit:
os.kill(os.getpid(), signal.SIGINT)
lock.release()
def do_register(**kwargs):
global user_quit, lock, queue, do_trace_pjsip, logger
do_trace_pjsip = kwargs.pop("do_trace_pjsip")
ctrl_d_pressed = False
outbound_proxy = kwargs.pop("outbound_proxy")
if outbound_proxy is None:
routes = lookup_routes_for_sip_uri(SIPURI(host=kwargs["domain"]), kwargs.pop("sip_transports"))
else:
routes = lookup_routes_for_sip_uri(outbound_proxy, kwargs.pop("sip_transports"))
# Only try the first Route for now
try:
kwargs["route"] = routes[0]
except IndexError:
raise RuntimeError("No route found to SIP proxy")
logger = Logger(AccountConfig, GeneralConfig.log_directory, trace_sip=kwargs.pop('trace_sip'))
if logger.trace_sip:
print "Logging SIP trace to file '%s'" % logger._siptrace_filename
e = Engine(event_handler, trace_sip=True, local_ip=kwargs.pop("local_ip"), local_udp_port=kwargs.pop("local_udp_port"), local_tcp_port=kwargs.pop("local_tcp_port"), local_tls_port=kwargs.pop("local_tls_port"))
e.start(False)
start_new_thread(read_queue, (e,), kwargs)
try:
while True:
char = getchar()
if char == "\x04":
if not ctrl_d_pressed:
queue.put(("eof", None))
ctrl_d_pressed = True
else:
queue.put(("user_input", char))
except KeyboardInterrupt:
if user_quit:
print "Ctrl+C pressed, exiting instantly!"
queue.put(("quit", True))
lock.acquire()
return
def parse_outbound_proxy(option, opt_str, value, parser):
try:
parser.values.outbound_proxy = OutboundProxy(value)
except ValueError, e:
raise OptionValueError(e.message)
def parse_options():
retval = {}
description = "This script will register a SIP account to a SIP registrar and refresh it while the program is running. When Ctrl+D is pressed it will unregister."
usage = "%prog [options]"
parser = OptionParser(usage=usage, description=description)
parser.print_usage = parser.print_help
parser.add_option("-a", "--account-name", type="string", dest="account_name", help="The account name from which to read account settings. Corresponds to section Account_NAME in the configuration file.")
parser.add_option("--sip-address", type="string", dest="sip_address", help="SIP login account")
parser.add_option("-p", "--password", type="string", dest="password", help="Password to use to authenticate the local account. This overrides the setting from the config file.")
parser.add_option("-n", "--display-name", type="string", dest="display_name", help="Display name to use for the local account. This overrides the setting from the config file.")
parser.add_option("-e", "--expires", type="int", dest="expires", help='"Expires" value to set in REGISTER. Default is 300 seconds.')
parser.add_option("-o", "--outbound-proxy", type="string", action="callback", callback=parse_outbound_proxy, help="Outbound SIP proxy to use. By default a lookup of the domain is performed based on SRV and A records. This overrides the setting from the config file.", metavar="IP[:PORT]")
parser.add_option("-s", "--trace-sip", action="store_true", dest="trace_sip", help="Dump the raw contents of incoming and outgoing SIP messages (disabled by default).")
parser.add_option("-j", "--trace-pjsip", action="store_true", dest="do_trace_pjsip", help="Print PJSIP logging output (disabled by default).")
parser.add_option("-r", "--max-registers", type="int", dest="max_registers", help="Max number of REGISTERs sent (default 1, set to 0 for infinite).")
options, args = parser.parse_args()
if options.account_name is None:
account_section = "Account"
else:
account_section = "Account_%s" % options.account_name
if account_section not in configuration.parser.sections():
raise RuntimeError("There is no account section named '%s' in the configuration file" % account_section)
configuration.read_settings(account_section, AccountConfig)
default_options = dict(expires=300, outbound_proxy=AccountConfig.outbound_proxy, sip_address=AccountConfig.sip_address, password=AccountConfig.password, display_name=AccountConfig.display_name, trace_sip=GeneralConfig.trace_sip, do_trace_pjsip=GeneralConfig.trace_pjsip, local_ip=GeneralConfig.local_ip, local_udp_port=GeneralConfig.sip_local_udp_port, local_tcp_port=GeneralConfig.sip_local_tcp_port, local_tls_port=GeneralConfig.sip_local_tls_port, sip_transports=GeneralConfig.sip_transports, max_registers=1)
options._update_loose(dict((name, value) for name, value in default_options.items() if getattr(options, name, None) is None))
for transport in set(["tls", "tcp", "udp"]) - set(options.sip_transports):
setattr(options, "local_%s_port" % transport, None)
if not all([options.sip_address, options.password]):
raise RuntimeError("No complete set of SIP credentials specified in config file and on commandline.")
for attr in default_options:
retval[attr] = getattr(options, attr)
try:
retval["username"], retval["domain"] = options.sip_address.split("@")
except ValueError:
raise RuntimeError("Invalid value for sip_address: %s" % options.sip_address)
else:
del retval["sip_address"]
accounts = [(acc == 'Account') and 'default' or "'%s'" % acc[8:] for acc in configuration.parser.sections() if acc.startswith('Account')]
accounts.sort()
print "Accounts available: %s" % ', '.join(accounts)
if options.account_name is None:
print "Using default account: %s" % options.sip_address
else:
print "Using account '%s': %s" % (options.account_name, options.sip_address)
return retval
def main():
do_register(**parse_options())
if __name__ == "__main__":
try:
main()
except RuntimeError, e:
print "Error: %s" % str(e)
sys.exit(1)
except PyPJUAError, e:
print "Error: %s" % str(e)
sys.exit(1)
sys.exit(return_code)
diff --git a/scripts/sip_subscribe_presence.py b/scripts/sip_subscribe_presence.py
index 79b385c7..8e6d0f68 100644
--- a/scripts/sip_subscribe_presence.py
+++ b/scripts/sip_subscribe_presence.py
@@ -1,502 +1,502 @@
#!/usr/bin/env python
import sys
import traceback
import string
import socket
import os
import atexit
import select
import termios
import signal
import datetime
from thread import start_new_thread, allocate_lock
from threading import Thread
from Queue import Queue
from optparse import OptionParser, OptionValueError
from time import sleep
from application.process import process
from application.configuration import *
from pypjua import *
from pypjua.clients import enrollment
from pypjua.clients.log import Logger
from pypjua.applications import ParserError
from pypjua.applications.pidf import *
from pypjua.applications.presdm import *
from pypjua.applications.rpid import *
from pypjua.clients.clientconfig import get_path
-from pypjua.clients.lookup import *
-from pypjua.clients import format_cmdline_uri
+from pypjua.clients.dns_lookup import *
+from pypjua.clients import *
class GeneralConfig(ConfigSection):
_datatypes = {"local_ip": datatypes.IPAddress, "sip_transports": datatypes.StringList, "trace_pjsip": datatypes.Boolean, "trace_sip": datatypes.Boolean}
local_ip = None
sip_local_udp_port = 0
sip_local_tcp_port = 0
sip_local_tls_port = 0
sip_transports = ["tls", "tcp", "udp"]
trace_pjsip = False
trace_sip = False
log_directory = '~/.sipclient/log'
class AccountConfig(ConfigSection):
_datatypes = {"sip_address": str, "password": str, "display_name": str, "outbound_proxy": OutboundProxy}
sip_address = None
password = None
display_name = None
outbound_proxy = None
process._system_config_directory = os.path.expanduser("~/.sipclient")
enrollment.verify_account_config()
configuration = ConfigFile("config.ini")
configuration.read_settings("General", GeneralConfig)
queue = Queue()
packet_count = 0
start_time = None
old = None
user_quit = True
lock = allocate_lock()
logger = None
return_code = 1
def format_note(note):
text = "Note"
if note.lang is not None:
text += "(%s)" % note.lang
if note.since is not None or note.until is not None:
text += " valid"
if note.since is not None:
text += " from %s" % note.since
if note.until is not None:
text += " until %s" % note.until
text += ": %s" % note
return text
def display_person(person, pidf, buf):
# display class
if person.rpid_class is not None:
buf.append(" Class: %s" % person.rpid_class)
# display timestamp
if person.timestamp is not None:
buf.append(" Timestamp: %s" % person.timestamp)
# display notes
if len(person.notes) > 0:
for note in person.notes:
buf.append(" %s" % format_note(note))
elif len(pidf.notes) > 0:
for note in pidf.notes:
buf.append(" %s" % format_note(note))
# display activities
if person.activities is not None:
activities = person.activities.values
if len(activities) > 0:
text = " Activities"
if person.activities.since is not None or person.activities.until is not None:
text += " valid"
if person.activities.since is not None:
text += " from %s" % person.activities.since
if person.activities.until is not None:
text += " until %s" % person.activities.until
text += ": %s" % ', '.join(str(activity) for activity in activities)
buf.append(text)
if len(person.activities.notes) > 0:
for note in person.activities.notes:
buf.append(" %s" % format_note(note))
elif len(person.activities.notes) > 0:
buf.append(" Activities")
for note in person.activities.notes:
buf.append(" %s" % format_note(note))
# display mood
if person.mood is not None:
moods = person.mood.values
if len(moods) > 0:
text = " Mood"
if person.mood.since is not None or person.mood.until is not None:
text += " valid"
if person.mood.since is not None:
text += " from %s" % person.mood.since
if person.mood.until is not None:
text += " until %s" % person.mood.until
text += ": %s" % ', '.join(str(mood) for mood in moods)
buf.append(text)
if len(person.mood.notes) > 0:
for note in person.mood.notes:
buf.append(" %s" % format_note(note))
# display place is
if person.place_is is not None:
place_keys = (key for key in ('audio', 'video', 'text') if getattr(person.place_is, key) is not None)
place_info = ', '.join('%s %s' % (key.capitalize(), getattr(person.place_is, key).value) for key in place_keys)
if place_info != '':
buf.append(" Place information: " + place_info)
# display privacy
if person.privacy is not None:
text = " Private conversation possible with: "
private = []
if person.privacy.audio:
private.append("Audio")
if person.privacy.video:
private.append("Video")
if person.privacy.text:
private.append("Text")
if len(private) > 0:
text += ", ".join(private)
else:
text += "None"
buf.append(text)
# display sphere
if person.sphere is not None:
timeinfo = []
if person.sphere.since is not None:
timeinfo.append('from %s' % str(person.sphere.since))
if person.sphere.until is not None:
timeinfo.append('until %s' % str(person.sphere.until))
if len(timeinfo) != 0:
timeinfo = ' (' + ', '.join(timeinfo) + ')'
else:
timeinfo = ''
buf.append(" Current sphere%s: %s" % (timeinfo, person.sphere.value))
# display status icon
if person.status_icon is not None:
buf.append(" Status icon: %s" % person.status_icon)
# display time and time offset
if person.time_offset is not None:
ctime = datetime.datetime.utcnow() + datetime.timedelta(minutes=int(person.time_offset))
time_offset = int(person.time_offset)/60.0
if time_offset == int(time_offset):
offset_info = '(UTC+%d%s)' % (time_offset, (person.time_offset.description is not None and (' (%s)' % person.time_offset.description) or ''))
else:
offset_info = '(UTC+%.1f%s)' % (time_offset, (person.time_offset.description is not None and (' (%s)' % person.time_offset.description) or ''))
buf.append(" Current user time: %s %s" % (ctime.strftime("%H:%M"), offset_info))
# display user input
if person.user_input is not None:
buf.append(" User is %s" % person.user_input)
if person.user_input.last_input:
buf.append(" Last input at: %s" % person.user_input.last_input)
if person.user_input.idle_threshold:
buf.append(" Idle threshold: %s seconds" % person.user_input.idle_threshold)
def display_service(service, pidf, buf):
# display class
if service.rpid_class is not None:
buf.append(" Class: %s" % person.rpid_class)
# display timestamp
if service.timestamp is not None:
buf.append(" Timestamp: %s" % service.timestamp)
# display notes
for note in service.notes:
buf.append(" %s" % format_note(note))
# display status
if service.status is not None and service.status.basic is not None:
buf.append(" Status: %s" % service.status.basic)
# display contact
if service.contact is not None:
buf.append(" Contact%s: %s" % ((service.contact.priority is not None) and (' priority %s' % service.contact.priority) or '', service.contact))
# display device ID
if service.device_id is not None:
buf.append(" Service offered by device id: %s" % service.device_id)
# display relationship
if service.relationship is not None:
buf.append(" Relationship: %s" % service.relationship.value)
# display service-class
if service.service_class is not None:
buf.append(" Service class: %s" % service.service_class.value)
# display status icon
if service.status_icon is not None:
buf.append(" Status icon: %s" % service.status_icon)
# display user input
if service.user_input is not None:
buf.append(" Service is %s" % service.user_input)
if service.user_input.last_input:
buf.append(" Last input at: %s" % service.user_input.last_input)
if service.user_input.idle_threshold:
buf.append(" Idle threshold: %s seconds" % service.user_input.idle_threshold)
def display_device(device, pidf, buf):
# display device ID
if device.device_id is not None:
buf.append(" Device id: %s" % device.device_id)
# display class
if device.rpid_class is not None:
buf.append(" Class: %s" % person.rpid_class)
# display timestamp
if device.timestamp is not None:
buf.append(" Timestamp: %s" % device.timestamp)
# display notes
for note in device.notes:
buf.append(" %s" % format_note(note))
# display user input
if device.user_input is not None:
buf.append(" Device is %s" % device.user_input)
if device.user_input.last_input:
buf.append(" Last input at: %s" % device.user_input.last_input)
if device.user_input.idle_threshold:
buf.append(" Idle threshold: %s seconds" % device.user_input.idle_threshold)
def handle_pidf(pidf):
buf = ["-"*16]
buf.append("Presence for %s:" % pidf.entity)
persons = {}
devices = {}
services = {}
printed_sep = True
for child in pidf:
if isinstance(child, Person):
persons[child.id] = child
elif isinstance(child, Device):
devices[child.id] = child
elif isinstance(child, Tuple):
services[child.id] = child
# handle person information
if len(persons) == 0:
if len(pidf.notes) > 0:
buf.append(" Person information:")
for note in pidf.notes:
buf.append(" %s" % format_note(note))
printed_sep = False
else:
for person in persons.values():
buf.append(" Person id: %s" % person.id)
display_person(person, pidf, buf)
printed_sep = False
# handle services informaation
if len(services) > 0:
if not printed_sep:
buf.append(" " + "-"*3)
for service in services.values():
buf.append(" Service id: %s" % service.id)
display_service(service, pidf, buf)
# handle devices informaation
if len(devices) > 0:
if not printed_sep:
buf.append(" " + "-"*3)
for device in devices.values():
buf.append(" Device id: %s" % device.id)
display_device(device, pidf, buf)
buf.append("-"*16)
# push the data
text = '\n'.join(buf)
queue.put(("print", text))
def termios_restore():
global old
if old is not None:
termios.tcsetattr(sys.stdin.fileno(), termios.TCSADRAIN, old)
def getchar():
global old
fd = sys.stdin.fileno()
if os.isatty(fd):
old = termios.tcgetattr(fd)
new = termios.tcgetattr(fd)
new[3] = new[3] & ~termios.ICANON & ~termios.ECHO
new[6][termios.VMIN] = '\000'
try:
termios.tcsetattr(fd, termios.TCSADRAIN, new)
if select.select([fd], [], [], None)[0]:
return sys.stdin.read(10)
finally:
termios_restore()
else:
return os.read(fd, 10)
def event_handler(event_name, **kwargs):
global start_time, packet_count, queue, do_trace_pjsip, logger, return_code
if event_name == "Subscription_state":
if kwargs["state"] == "ACTIVE":
#queue.put(("print", "SUBSCRIBE was successful"))
return_code = 0
elif kwargs["state"] == "TERMINATED":
if kwargs.has_key("code"):
if kwargs['code'] / 100 == 2:
return_code = 0
queue.put(("print", "Unsubscribed: %(code)d %(reason)s" % kwargs))
else:
queue.put(("print", "Unsubscribed"))
queue.put(("quit", None))
elif kwargs["state"] == "PENDING":
queue.put(("print", "Subscription is pending"))
elif event_name == "Subscription_notify":
return_code = 0
if ('%s/%s' % (kwargs['content_type'], kwargs['content_subtype'])) in PIDF.accept_types:
queue.put(("print", "Received NOTIFY:"))
try:
pidf = PIDF.parse(kwargs['body'])
except ParserError, e:
queue.put(("print", "Got illegal pidf document: %s\n%s" % (str(e), kwargs['body'])))
else:
handle_pidf(pidf)
elif event_name == "siptrace":
logger.log(event_name, **kwargs)
elif event_name != "log":
queue.put(("pypjua_event", (event_name, kwargs)))
elif do_trace_pjsip:
queue.put(("print", "%(timestamp)s (%(level)d) %(sender)14s: %(message)s" % kwargs))
def read_queue(e, username, domain, password, display_name, presentity_uri, route, expires, content_type, do_trace_pjsip):
global user_quit, lock, queue, logger
lock.acquire()
try:
credentials = Credentials(SIPURI(user=username, host=domain, display=display_name), password)
sub = Subscription(credentials, presentity_uri, 'presence', route=route, expires=expires)
print 'Subscribing to "%s" for the presence event, at %s:%s:%d' % (presentity_uri, route.transport, route.host, route.port)
sub.subscribe()
while True:
command, data = queue.get()
if command == "print":
print data
if command == "pypjua_event":
event_name, args = data
if command == "user_input":
key = data
if command == "eof":
command = "end"
want_quit = True
if command == "end":
try:
sub.unsubscribe()
except:
pass
if command == "quit":
user_quit = False
break
except:
user_quit = False
traceback.print_exc()
finally:
e.stop()
logger.stop()
if not user_quit:
os.kill(os.getpid(), signal.SIGINT)
lock.release()
def do_subscribe(**kwargs):
global user_quit, lock, queue, do_trace_pjsip, logger
ctrl_d_pressed = False
do_trace_pjsip = kwargs["do_trace_pjsip"]
outbound_proxy = kwargs.pop("outbound_proxy")
if outbound_proxy is None:
routes = lookup_routes_for_sip_uri(SIPURI(host=kwargs["domain"]), kwargs.pop("sip_transports"))
else:
routes = lookup_routes_for_sip_uri(outbound_proxy, kwargs.pop("sip_transports"))
# Only try the first Route for now
try:
kwargs["route"] = routes[0]
except IndexError:
raise RuntimeError("No route found to SIP proxy")
events = Engine.init_options_defaults["events"]
if kwargs['content_type'] is not None:
events['presence'] = [kwargs['content_type']]
logger = Logger(AccountConfig, GeneralConfig.log_directory, trace_sip=kwargs['trace_sip'])
if kwargs['trace_sip']:
print "Logging SIP trace to file '%s'" % logger._siptrace_filename
e = Engine(event_handler, trace_sip=kwargs.pop('trace_sip'), events=events, local_ip=kwargs.pop("local_ip"), local_udp_port=kwargs.pop("local_udp_port"), local_tcp_port=kwargs.pop("local_tcp_port"), local_tls_port=kwargs.pop("local_tls_port"))
e.start(False)
kwargs["presentity_uri"] = e.parse_sip_uri(kwargs["presentity_uri"])
start_new_thread(read_queue, (e,), kwargs)
atexit.register(termios_restore)
try:
while True:
char = getchar()
if char == "\x04":
if not ctrl_d_pressed:
queue.put(("eof", None))
ctrl_d_pressed = True
else:
queue.put(("user_input", char))
except KeyboardInterrupt:
if user_quit:
print "Ctrl+C pressed, exiting instantly!"
queue.put(("quit", True))
lock.acquire()
return
def parse_outbound_proxy(option, opt_str, value, parser):
try:
parser.values.outbound_proxy = OutboundProxy(value)
except ValueError, e:
raise OptionValueError(e.message)
def parse_options():
retval = {}
description = "This script will SUBSCRIBE to the presence event published by the specified SIP target. If a SIP target is not specified, it will subscribe to its own address. It will then interprete PIDF bodies contained in NOTIFYs and display their meaning. The program will un-SUBSCRIBE and quit when CTRL+D is pressed."
usage = "%prog [options] [target-user@target-domain.com]"
parser = OptionParser(usage=usage, description=description)
parser.print_usage = parser.print_help
parser.add_option("-a", "--account-name", type="string", dest="account_name", help="The account name from which to read account settings. Corresponds to section Account_NAME in the configuration file. If not supplied, the section Account will be read.", metavar="NAME")
parser.add_option("--sip-address", type="string", dest="sip_address", help="SIP address of the user in the form user@domain")
parser.add_option("-p", "--password", type="string", dest="password", help="Password to use to authenticate the local account. This overrides the setting from the config file.")
parser.add_option("-n", "--display-name", type="string", dest="display_name", help="Display name to use for the local account. This overrides the setting from the config file.")
parser.add_option("-e", "--expires", type="int", dest="expires", help='"Expires" value to set in SUBSCRIBE. Default is 300 seconds.')
parser.add_option("-o", "--outbound-proxy", type="string", action="callback", callback=parse_outbound_proxy, help="Outbound SIP proxy to use. By default a lookup of the domain is performed based on SRV and A records. This overrides the setting from the config file.", metavar="IP[:PORT]")
parser.add_option("-c", "--content-type", type="string", dest="content_type", help = '"Content-Type" the UA expects to receving in a NOTIFY for this subscription. For the known events this does not need to be specified, but may be overridden".')
parser.add_option("-s", "--trace-sip", action="store_true", dest="trace_sip", help="Dump the raw contents of incoming and outgoing SIP messages (disabled by default).")
parser.add_option("-j", "--trace-pjsip", action="store_true", dest="do_trace_pjsip", help="Print PJSIP logging output (disabled by default).")
options, args = parser.parse_args()
if options.account_name is None:
account_section = "Account"
else:
account_section = "Account_%s" % options.account_name
if account_section not in configuration.parser.sections():
raise RuntimeError("There is no account section named '%s' in the configuration file" % account_section)
configuration.read_settings(account_section, AccountConfig)
default_options = dict(expires=300, outbound_proxy=AccountConfig.outbound_proxy, sip_address=AccountConfig.sip_address, password=AccountConfig.password, display_name=AccountConfig.display_name, content_type=None, trace_sip=GeneralConfig.trace_sip, do_trace_pjsip=GeneralConfig.trace_pjsip, local_ip=GeneralConfig.local_ip, local_udp_port=GeneralConfig.sip_local_udp_port, local_tcp_port=GeneralConfig.sip_local_tcp_port, local_tls_port=GeneralConfig.sip_local_tls_port, sip_transports=GeneralConfig.sip_transports)
options._update_loose(dict((name, value) for name, value in default_options.items() if getattr(options, name, None) is None))
for transport in set(["tls", "tcp", "udp"]) - set(options.sip_transports):
setattr(options, "local_%s_port" % transport, None)
if not all([options.sip_address, options.password]):
raise RuntimeError("No complete set of SIP credentials specified in config file and on commandline.")
for attr in default_options:
retval[attr] = getattr(options, attr)
try:
retval["username"], retval["domain"] = options.sip_address.split("@")
except ValueError:
raise RuntimeError("Invalid value for sip_address: %s" % options.sip_address)
else:
del retval["sip_address"]
if args:
retval["presentity_uri"] = format_cmdline_uri(args[0], retval["domain"])
else:
retval["presentity_uri"] = format_cmdline_uri(retval["username"], retval["domain"])
accounts = [(acc == 'Account') and 'default' or "'%s'" % acc[8:] for acc in configuration.parser.sections() if acc.startswith('Account')]
accounts.sort()
print "Accounts available: %s" % ', '.join(accounts)
if options.account_name is None:
print "Using default account: %s" % options.sip_address
else:
print "Using account '%s': %s" % (options.account_name, options.sip_address)
return retval
def main():
do_subscribe(**parse_options())
if __name__ == "__main__":
try:
main()
except RuntimeError, e:
print "Error: %s" % str(e)
sys.exit(1)
except PyPJUAError, e:
print "Error: %s" % str(e)
sys.exit(1)
sys.exit(return_code)
diff --git a/scripts/sip_subscribe_rls.py b/scripts/sip_subscribe_rls.py
index 3f8bf04e..d8a82b59 100644
--- a/scripts/sip_subscribe_rls.py
+++ b/scripts/sip_subscribe_rls.py
@@ -1,485 +1,485 @@
#!/usr/bin/env python
import sys
import traceback
import string
import socket
import os
import atexit
import select
import termios
import signal
from thread import start_new_thread, allocate_lock
from threading import Thread
from Queue import Queue
from optparse import OptionParser, OptionValueError
from time import sleep
from application.process import process
from application.configuration import *
from pypjua import *
from pypjua.clients import enrollment
from pypjua.clients.log import Logger
from pypjua.applications import ParserError
from pypjua.applications.pidf import *
from pypjua.applications.presdm import *
from pypjua.applications.rpid import *
from pypjua.clients.clientconfig import get_path
-from pypjua.clients.lookup import *
-from pypjua.clients import format_cmdline_uri
+from pypjua.clients.dns_lookup import *
+from pypjua.clients import *
class GeneralConfig(ConfigSection):
_datatypes = {"local_ip": datatypes.IPAddress, "sip_transports": datatypes.StringList, "trace_pjsip": datatypes.Boolean, "trace_sip": datatypes.Boolean}
local_ip = None
sip_local_udp_port = 0
sip_local_tcp_port = 0
sip_local_tls_port = 0
sip_transports = ["tls", "tcp", "udp"]
trace_pjsip = False
trace_sip = False
log_directory = '~/.sipclient/log'
class AccountConfig(ConfigSection):
_datatypes = {"sip_address": str, "password": str, "display_name": str, "outbound_proxy": OutboundProxy, "use_presence_agent": datatypes.Boolean}
sip_address = None
password = None
display_name = None
outbound_proxy = None
use_presence_agent = True
process._system_config_directory = os.path.expanduser("~/.sipclient")
enrollment.verify_account_config()
configuration = ConfigFile("config.ini")
configuration.read_settings("General", GeneralConfig)
queue = Queue()
packet_count = 0
start_time = None
old = None
user_quit = True
lock = allocate_lock()
logger = None
return_code = 1
def format_note(note):
text = "Note"
if note.lang is not None:
text += "(%s)" % note.lang
if note.since is not None or note.until is not None:
text += " valid"
if note.since is not None:
text += " from %s" % note.since
if note.until is not None:
text += " until %s" % note.until
text += ": %s" % note
return text
def display_person(person, pidf, buf):
# display class
if person.rpid_class is not None:
buf.append(" Class: %s" % person.rpid_class)
# display timestamp
if person.timestamp is not None:
buf.append(" Timestamp: %s" % person.timestamp)
# display notes
if len(person.notes) > 0:
for note in person.notes:
buf.append(" %s" % format_note(note))
elif len(pidf.notes) > 0:
for note in pidf.notes:
buf.append(" %s" % format_note(note))
# display activities
if person.activities is not None:
activities = person.activities.values
if len(activities) > 0:
text = " Activities"
if person.activities.since is not None or person.activities.until is not None:
text += " valid"
if person.activities.since is not None:
text += " from %s" % person.activities.since
if person.activities.until is not None:
text += " until %s" % person.activities.until
text += ": %s" % ', '.join(str(activity) for activity in activities)
buf.append(text)
if len(person.activities.notes) > 0:
for note in person.activities.notes:
buf.append(" %s" % format_note(note))
elif len(person.activities.notes) > 0:
buf.append(" Activities")
for note in person.activities.notes:
buf.append(" %s" % format_note(note))
# display mood
if person.mood is not None:
moods = person.mood.values
if len(moods) > 0:
text = " Mood"
if person.mood.since is not None or person.mood.until is not None:
text += " valid"
if person.mood.since is not None:
text += " from %s" % person.mood.since
if person.mood.until is not None:
text += " until %s" % person.mood.until
text += ": %s" % ', '.join(str(mood) for mood in moods)
buf.append(text)
if len(person.mood.notes) > 0:
for note in person.mood.notes:
buf.append(" %s" % format_note(note))
# display place is
if person.place_is is not None:
buf.append(" Place information:")
if person.place_is.audio is not None:
buf.append(" Audio: %s" % person.place_is.audio.values[0])
if person.place_is.video is not None:
buf.append(" Video: %s" % person.place_is.video.values[0])
if person.place_is.text is not None:
buf.append(" Text: %s" % person.place_is.text.values[0])
# display privacy
if person.privacy is not None:
text = " Communication that is private: "
private = []
if person.privacy.audio:
private.append("audio")
if person.privacy.video:
private.append("video")
if person.privacy.text:
private.append("text")
text += ", ".join(private)
buf.append(text)
# display sphere
if person.sphere is not None:
buf.append(" Current sphere: %s" % person.sphere.values[0])
# display status icon
if person.status_icon is not None:
buf.append(" Status icon: %s" % person.status_icon)
# display time offset
if person.time_offset is not None:
buf.append(" Time offset from UTC: %s minutes %s" % (person.time_offset, (person.time_offset.description is not None and ('(%s)' % person.time_offset.description) or '')))
# display user input
if person.user_input is not None:
buf.append(" User is %s" % person.user_input)
if person.user_input.last_input:
buf.append(" Last input at: %s" % person.user_input.last_input)
if person.user_input.idle_threshold:
buf.append(" Idle threshold: %s seconds" % person.user_input.idle_threshold)
def display_service(service, pidf, buf):
# display class
if service.rpid_class is not None:
buf.append(" Class: %s" % person.rpid_class)
# display timestamp
if service.timestamp is not None:
buf.append(" Timestamp: %s" % service.timestamp)
# display notes
for note in service.notes:
buf.append(" %s" % format_note(note))
# display status
if service.status is not None and service.status.basic is not None:
buf.append(" Status: %s" % service.status.basic)
# display contact
if service.contact is not None:
buf.append(" Contact%s: %s" % ((service.contact.priority is not None) and (' priority %s' % service.contact.priority) or '', service.contact))
# display device ID
if service.device_id is not None:
buf.append(" Service offered by device id: %s" % service.device_id)
# display relationship
if service.relationship is not None:
buf.append(" Relationship: %s" % service.relationship.values[0])
# display service-class
if service.service_class is not None:
buf.append(" Service class: %s" % service.service_class.values[0])
# display status icon
if service.status_icon is not None:
buf.append(" Status icon: %s" % service.status_icon)
# display user input
if service.user_input is not None:
buf.append(" Service is %s" % service.user_input)
if service.user_input.last_input:
buf.append(" Last input at: %s" % service.user_input.last_input)
if service.user_input.idle_threshold:
buf.append(" Idle threshold: %s seconds" % service.user_input.idle_threshold)
def display_device(device, pidf, buf):
# display device ID
if device.device_id is not None:
buf.append(" Device id: %s" % device.device_id)
# display class
if device.rpid_class is not None:
buf.append(" Class: %s" % person.rpid_class)
# display timestamp
if device.timestamp is not None:
buf.append(" Timestamp: %s" % device.timestamp)
# display notes
for note in device.notes:
buf.append(" %s" % format_note(note))
# display user input
if device.user_input is not None:
buf.append(" Service is %s" % device.user_input)
if device.user_input.last_input:
buf.append(" Last input at: %s" % device.user_input.last_input)
if device.user_input.idle_threshold:
buf.append(" Idle threshold: %s seconds" % device.user_input.idle_threshold)
def handle_pidf(pidf):
buf = ["-"*16]
buf.append("Presence for %s:" % pidf.entity)
persons = {}
devices = {}
services = {}
printed_sep = True
for child in pidf:
if isinstance(child, Person):
persons[child.id] = child
elif isinstance(child, Device):
devices[child.id] = child
elif isinstance(child, Tuple):
services[child.id] = child
# handle person information
if len(persons) == 0:
if len(pidf.notes) > 0:
buf.append(" Person information:")
for note in pidf.notes:
buf.append(" %s" % format_note(note))
printed_sep = False
else:
for person in persons.values():
buf.append(" Person id %s" % person.id)
display_person(person, pidf, buf)
printed_sep = False
# handle services informaation
if len(services) > 0:
if not printed_sep:
buf.append(" " + "-"*3)
for service in services.values():
buf.append(" Service id %s" % service.id)
display_service(service, pidf, buf)
# handle devices informaation
if len(devices) > 0:
if not printed_sep:
buf.append(" " + "-"*3)
for device in devices.values():
buf.append(" Device id %s" % device.id)
display_device(device, pidf, buf)
buf.append("-"*16)
# push the data
text = '\n'.join(buf)
queue.put(("print", text))
def termios_restore():
global old
if old is not None:
termios.tcsetattr(sys.stdin.fileno(), termios.TCSADRAIN, old)
def getchar():
global old
fd = sys.stdin.fileno()
if os.isatty(fd):
old = termios.tcgetattr(fd)
new = termios.tcgetattr(fd)
new[3] = new[3] & ~termios.ICANON & ~termios.ECHO
new[6][termios.VMIN] = '\000'
try:
termios.tcsetattr(fd, termios.TCSADRAIN, new)
if select.select([fd], [], [], None)[0]:
return sys.stdin.read(10)
finally:
termios_restore()
else:
return os.read(fd, 10)
def event_handler(event_name, **kwargs):
global start_time, packet_count, queue, do_trace_pjsip, logger, return_code
if event_name == "Subscription_state":
if kwargs["state"] == "ACTIVE":
#queue.put(("print", "SUBSCRIBE was successful"))
return_code = 0
elif kwargs["state"] == "TERMINATED":
if kwargs.has_key("code"):
if kwargs['code'] / 100 == 2:
return_code = 0
queue.put(("print", "Unsubscribed: %(code)d %(reason)s" % kwargs))
else:
queue.put(("print", "Unsubscribed"))
queue.put(("quit", None))
elif kwargs["state"] == "PENDING":
queue.put(("print", "Subscription is pending"))
elif event_name == "Subscription_notify":
return_code = 0
if ('%s/%s' % (kwargs['content_type'], kwargs['content_subtype'])) in PIDF.accept_types:
queue.put(("print", "Received NOTIFY: %s" % kwargs))
elif event_name == "siptrace":
logger.log(event_name, **kwargs)
elif event_name != "log":
queue.put(("pypjua_event", (event_name, kwargs)))
elif do_trace_pjsip:
queue.put(("print", "%(timestamp)s (%(level)d) %(sender)14s: %(message)s" % kwargs))
def read_queue(e, username, domain, password, display_name, presentity_uri, route, expires, content_type, do_trace_pjsip):
global user_quit, lock, queue, logger
lock.acquire()
try:
credentials = Credentials(SIPURI(user=username, host=domain, display=display_name), password)
sub = Subscription(credentials, presentity_uri, 'presence', route=route, expires=expires, extra_headers={'Supported': 'eventlist'})
print 'Subscribing to "%s" for the presence event, at %s:%s:%d' % (presentity_uri, route.transport, route.host, route.port)
sub.subscribe()
while True:
command, data = queue.get()
if command == "print":
print data
if command == "pypjua_event":
event_name, args = data
if command == "user_input":
key = data
if command == "eof":
command = "end"
want_quit = True
if command == "end":
try:
sub.unsubscribe()
except:
pass
if command == "quit":
user_quit = False
break
except:
user_quit = False
traceback.print_exc()
finally:
e.stop()
logger.stop()
if not user_quit:
os.kill(os.getpid(), signal.SIGINT)
lock.release()
def do_subscribe(**kwargs):
global user_quit, lock, queue, do_trace_pjsip, logger
ctrl_d_pressed = False
do_trace_pjsip = kwargs["do_trace_pjsip"]
outbound_proxy = kwargs.pop("outbound_proxy")
if outbound_proxy is None:
routes = lookup_routes_for_sip_uri(SIPURI(host=kwargs["domain"]), kwargs.pop("sip_transports"))
else:
routes = lookup_routes_for_sip_uri(outbound_proxy, kwargs.pop("sip_transports"))
# Only try the first Route for now
try:
kwargs["route"] = routes[0]
except IndexError:
raise RuntimeError("No route found to SIP proxy")
events = Engine.init_options_defaults["events"]
if kwargs['content_type'] is not None:
events['presence'] = [kwargs['content_type']]
else:
events['presence'] = ['multipart/related', 'application/rlmi+xml', 'application/pidf+xml']
logger = Logger(AccountConfig, GeneralConfig.log_directory, trace_sip=kwargs['trace_sip'])
if kwargs['trace_sip']:
print "Logging SIP trace to file '%s'" % logger._siptrace_filename
e = Engine(event_handler, trace_sip=kwargs.pop('trace_sip'), local_ip=kwargs.pop("local_ip"), local_udp_port=kwargs.pop("local_udp_port"), local_tcp_port=kwargs.pop("local_tcp_port"), local_tls_port=kwargs.pop("local_tls_port"), events=events)
e.start(False)
kwargs["presentity_uri"] = e.parse_sip_uri(kwargs["presentity_uri"])
start_new_thread(read_queue, (e,), kwargs)
atexit.register(termios_restore)
try:
while True:
char = getchar()
if char == "\x04":
if not ctrl_d_pressed:
queue.put(("eof", None))
ctrl_d_pressed = True
else:
queue.put(("user_input", char))
except KeyboardInterrupt:
if user_quit:
print "Ctrl+C pressed, exiting instantly!"
queue.put(("quit", True))
lock.acquire()
return
def parse_outbound_proxy(option, opt_str, value, parser):
try:
parser.values.outbound_proxy = OutboundProxy(value)
except ValueError, e:
raise OptionValueError(e.message)
def parse_options():
retval = {}
description = "This script will SUBSCRIBE to the presence event published by the specified SIP target assuming it is a resource list handled by a RLS server. The RLS server will then SUBSCRIBE in behalf of the account, collect NOTIFYs with the presence information of the recipients and provide periodically aggregated NOTIFYs back to the subscriber. If a target address is not specified, it will subscribe to the account's own address. It will then interprete PIDF bodies contained in NOTIFYs and display their meaning. The program will un-SUBSCRIBE and quit when CTRL+D is pressed."
usage = "%prog [options] [target-user@target-domain.com]"
parser = OptionParser(usage=usage, description=description)
parser.print_usage = parser.print_help
parser.add_option("-a", "--account-name", type="string", dest="account_name", help="The account name from which to read account settings. Corresponds to section Account_NAME in the configuration file. If not supplied, the section Account will be read.", metavar="NAME")
parser.add_option("--sip-address", type="string", dest="sip_address", help="SIP address of the user in the form user@domain")
parser.add_option("-p", "--password", type="string", dest="password", help="Password to use to authenticate the local account. This overrides the setting from the config file.")
parser.add_option("-n", "--display-name", type="string", dest="display_name", help="Display name to use for the local account. This overrides the setting from the config file.")
parser.add_option("-e", "--expires", type="int", dest="expires", help='"Expires" value to set in SUBSCRIBE. Default is 300 seconds.')
parser.add_option("-o", "--outbound-proxy", type="string", action="callback", callback=parse_outbound_proxy, help="Outbound SIP proxy to use. By default a lookup of the domain is performed based on SRV and A records. This overrides the setting from the config file.", metavar="IP[:PORT]")
parser.add_option("-c", "--content-type", type="string", dest="content_type", help = '"Content-Type" the UA expects to receving in a NOTIFY for this subscription. For the known events this does not need to be specified, but may be overridden".')
parser.add_option("-s", "--trace-sip", action="store_true", dest="trace_sip", help="Dump the raw contents of incoming and outgoing SIP messages (disabled by default).")
parser.add_option("-j", "--trace-pjsip", action="store_true", dest="do_trace_pjsip", help="Print PJSIP logging output (disabled by default).")
options, args = parser.parse_args()
if options.account_name is None:
account_section = "Account"
else:
account_section = "Account_%s" % options.account_name
if account_section not in configuration.parser.sections():
raise RuntimeError("There is no account section named '%s' in the configuration file" % account_section)
configuration.read_settings(account_section, AccountConfig)
if not AccountConfig.use_presence_agent:
raise RuntimeError("Presence is not enabled for this account. Please set use_presence_agent=True in the config file")
default_options = dict(expires=300, outbound_proxy=AccountConfig.outbound_proxy, sip_address=AccountConfig.sip_address, password=AccountConfig.password, display_name=AccountConfig.display_name, content_type=None, trace_sip=GeneralConfig.trace_sip, do_trace_pjsip=GeneralConfig.trace_pjsip, local_ip=GeneralConfig.local_ip, local_udp_port=GeneralConfig.sip_local_udp_port, local_tcp_port=GeneralConfig.sip_local_tcp_port, local_tls_port=GeneralConfig.sip_local_tls_port, sip_transports=GeneralConfig.sip_transports)
options._update_loose(dict((name, value) for name, value in default_options.items() if getattr(options, name, None) is None))
for transport in set(["tls", "tcp", "udp"]) - set(options.sip_transports):
setattr(options, "local_%s_port" % transport, None)
if not all([options.sip_address, options.password]):
raise RuntimeError("No complete set of SIP credentials specified in config file and on commandline.")
for attr in default_options:
retval[attr] = getattr(options, attr)
try:
retval["username"], retval["domain"] = options.sip_address.split("@")
except ValueError:
raise RuntimeError("Invalid value for sip_address: %s" % options.sip_address)
else:
del retval["sip_address"]
if args:
retval["presentity_uri"] = format_cmdline_uri(args[0], retval["domain"])
else:
retval["presentity_uri"] = format_cmdline_uri('%s-buddies' % retval["username"], retval["domain"])
accounts = [(acc == 'Account') and 'default' or "'%s'" % acc[8:] for acc in configuration.parser.sections() if acc.startswith('Account')]
accounts.sort()
print "Accounts available: %s" % ', '.join(accounts)
if options.account_name is None:
print "Using default account: %s" % options.sip_address
else:
print "Using account '%s': %s" % (options.account_name, options.sip_address)
return retval
def main():
do_subscribe(**parse_options())
if __name__ == "__main__":
try:
main()
except RuntimeError, e:
print "Error: %s" % str(e)
sys.exit(1)
except PyPJUAError, e:
print "Error: %s" % str(e)
sys.exit(1)
sys.exit(return_code)
diff --git a/scripts/sip_subscribe_winfo.py b/scripts/sip_subscribe_winfo.py
index fae61e9b..34714f67 100644
--- a/scripts/sip_subscribe_winfo.py
+++ b/scripts/sip_subscribe_winfo.py
@@ -1,478 +1,479 @@
#!/usr/bin/env python
import sys
import traceback
import string
import socket
import os
import atexit
import select
import termios
import signal
from collections import deque
from thread import start_new_thread, allocate_lock
from threading import Thread
from Queue import Queue
from optparse import OptionParser, OptionValueError
from time import sleep
from application.process import process
from application.configuration import *
from urllib2 import URLError
from pypjua import *
from pypjua.clients import enrollment
from pypjua.clients.log import Logger
from pypjua.applications import ParserError
from pypjua.applications.watcherinfo import *
from pypjua.applications.policy import *
from pypjua.applications.presrules import *
from pypjua.clients.clientconfig import get_path
-from pypjua.clients.lookup import *
+from pypjua.clients.dns_lookup import *
+from pypjua.clients import *
from xcaplib.client import XCAPClient
from xcaplib.error import HTTPError
class GeneralConfig(ConfigSection):
_datatypes = {"local_ip": datatypes.IPAddress, "sip_transports": datatypes.StringList, "trace_pjsip": datatypes.Boolean, "trace_sip": datatypes.Boolean}
local_ip = None
sip_local_udp_port = 0
sip_local_tcp_port = 0
sip_local_tls_port = 0
sip_transports = ["tls", "tcp", "udp"]
trace_pjsip = False
trace_sip = False
log_directory = '~/.sipclient/log'
class AccountConfig(ConfigSection):
_datatypes = {"sip_address": str, "password": str, "display_name": str, "outbound_proxy": OutboundProxy, "xcap_root": str, "use_presence_agent": datatypes.Boolean}
sip_address = None
password = None
display_name = None
outbound_proxy = None
xcap_root = None
use_presence_agent = True
process._system_config_directory = os.path.expanduser("~/.sipclient")
enrollment.verify_account_config()
configuration = ConfigFile("config.ini")
configuration.read_settings("General", GeneralConfig)
queue = Queue()
packet_count = 0
start_time = None
old = None
user_quit = True
lock = allocate_lock()
sip_uri = None
logger = None
return_code = 1
pending = deque()
winfo = None
xcap_client = None
prules = None
prules_etag = None
allow_rule = None
allow_rule_identities = None
block_rule = None
block_rule_identities = None
polite_block_rule = None
polite_block_rule_identities = None
def get_prules():
global prules, prules_etag, allow_rule, block_rule, allow_rule_identities, block_rule_identities
prules = None
prules_etag = None
allow_rule = None
allow_rule_identities = None
block_rule = None
block_rule_identities = None
try:
doc = xcap_client.get('pres-rules')
except URLError, e:
print "Cannot obtain 'pres-rules' document: %s" % str(e)
except HTTPError, e:
if e.response.status != 404:
print "Cannot obtain 'pres-rules' document: %s %s" % (e.response.status, e.response.reason)
else:
prules = PresRules()
else:
try:
prules = PresRules.parse(doc)
except ParserError, e:
print "Invalid 'pres-rules' document: %s" % str(e)
else:
prules_etag = doc.etag
# find each rule type
for rule in prules:
if rule.actions is not None:
for action in rule.actions:
if isinstance(action, SubHandling):
if action == 'allow':
if rule.conditions is not None:
for condition in rule.conditions:
if isinstance(condition, Identity):
allow_rule = rule
allow_rule_identities = condition
break
elif action == 'block':
if rule.conditions is not None:
for condition in rule.conditions:
if isinstance(condition, Identity):
block_rule = rule
block_rule_identities = condition
break
elif action == 'polite-block':
if rule.conditions is not None:
for condition in rule.conditions:
if isinstance(condition, Identity):
polite_block_rule = rule
polite_block_rule_identities = condition
break
break
def allow_watcher(watcher):
global prules, prules_etag, allow_rule, allow_rule_identities
for i in xrange(3):
if prules is None:
get_prules()
if prules is not None:
if allow_rule is None:
allow_rule_identities = Identity()
allow_rule = Rule('pres_whitelist', conditions=Conditions([allow_rule_identities]), actions=Actions([SubHandling('allow')]),
transformations=Transformations([ProvideServices([AllServices()]), ProvidePersons([AllPersons()]),
ProvideDevices([AllDevices()]), ProvideAllAttributes()]))
prules.append(allow_rule)
if str(watcher) not in allow_rule_identities:
allow_rule_identities.append(IdentityOne(str(watcher)))
try:
res = xcap_client.put('pres-rules', prules.toxml(pretty_print=True), etag=prules_etag)
except HTTPError, e:
print "Cannot PUT 'pres-rules' document: %s" % str(e)
prules = None
else:
prules_etag = res.etag
print "Watcher %s is now allowed" % watcher
break
sleep(0.1)
else:
print "Could not allow watcher %s" % watcher
def block_watcher(watcher):
global prules, prules_etag, block_rule, block_rule_identities
for i in xrange(3):
if prules is None:
get_prules()
if prules is not None:
if block_rule is None:
block_rule_identities = Identity()
block_rule = Rule('pres_blacklist', conditions=Conditions([block_rule_identities]), actions=Actions([SubHandling('block')]),
transformations=Transformations())
prules.append(block_rule)
if str(watcher) not in block_rule_identities:
block_rule_identities.append(IdentityOne(str(watcher)))
try:
res = xcap_client.put('pres-rules', prules.toxml(pretty_print=True), etag=prules_etag)
except HTTPError, e:
print "Cannot PUT 'pres-rules' document: %s" % str(e)
prules = None
else:
prules_etag = res.etag
print "Watcher %s is now denied" % watcher
break
sleep(0.1)
else:
print "Could not deny watcher %s" % watcher
def polite_block_watcher(watcher):
global prules, prules_etag, polite_block_rule, polite_block_rule_identities
for i in xrange(3):
if prules is None:
get_prules()
if prules is not None:
if polite_block_rule is None:
polite_block_rule_identities = Identity()
polite_block_rule = Rule('pres_polite_blacklist', conditions=Conditions([polite_block_rule_identities]), actions=Actions([SubHandling('polite-block')]),
transformations=Transformations())
prules.append(polite_block_rule)
if str(watcher) not in polite_block_rule_identities:
polite_block_rule_identities.append(IdentityOne(str(watcher)))
try:
res = xcap_client.put('pres-rules', prules.toxml(pretty_print=True), etag=prules_etag)
except HTTPError, e:
print "Cannot PUT 'pres-rules' document: %s" % str(e)
prules = None
else:
prules_etag = res.etag
print "Watcher %s is now politely blocked" % watcher
break
sleep(0.1)
else:
print "Could not politely block authorization of watcher %s" % watcher
def handle_winfo(result):
buf = ["Received NOTIFY:", "----"]
self = 'sip:%s@%s' % (sip_uri.user, sip_uri.host)
wlist = winfo[self]
buf.append("Active watchers:")
for watcher in wlist.active:
buf.append(" %s" % watcher)
buf.append("Terminated watchers:")
for watcher in wlist.terminated:
buf.append(" %s" % watcher)
buf.append("Pending watchers:")
for watcher in wlist.pending:
buf.append(" %s" % watcher)
buf.append("Waiting watchers:")
for watcher in wlist.waiting:
buf.append(" %s" % watcher)
buf.append("----")
queue.put(("print", '\n'.join(buf)))
if result.has_key(self):
for watcher in result[self]:
if (watcher.status == 'pending' or watcher.status == 'waiting') and watcher not in pending and xcap_client is not None:
pending.append(watcher)
def termios_restore():
global old
if old is not None:
termios.tcsetattr(sys.stdin.fileno(), termios.TCSADRAIN, old)
def getchar():
global old
fd = sys.stdin.fileno()
if os.isatty(fd):
old = termios.tcgetattr(fd)
new = termios.tcgetattr(fd)
new[3] = new[3] & ~termios.ICANON & ~termios.ECHO
new[6][termios.VMIN] = '\000'
try:
termios.tcsetattr(fd, termios.TCSADRAIN, new)
if select.select([fd], [], [], None)[0]:
return sys.stdin.read(10)
finally:
termios_restore()
else:
return os.read(fd, 10)
def event_handler(event_name, **kwargs):
global start_time, packet_count, queue, do_trace_pjsip, winfo, logger, return_code
if event_name == "Subscription_state":
if kwargs["state"] == "ACTIVE":
#queue.put(("print", "SUBSCRIBE was successful"))
return_code = 0
elif kwargs["state"] == "TERMINATED":
if kwargs.has_key("code"):
if kwargs['code'] / 100 == 2:
return_code = 0
queue.put(("print", "Unsubscribed: %(code)d %(reason)s" % kwargs))
else:
queue.put(("print", "Unsubscribed"))
queue.put(("quit", None))
elif kwargs["state"] == "PENDING":
queue.put(("print", "Subscription is pending"))
elif event_name == "Subscription_notify":
return_code = 0
if ('%s/%s' % (kwargs['content_type'], kwargs['content_subtype'])) in WatcherInfo.accept_types:
try:
result = winfo.update(kwargs['body'])
except ParserError, e:
queue.put(("print", "Got illegal winfo document: %s\n%s" % (str(e), kwargs['body'])))
else:
handle_winfo(result)
elif event_name == "siptrace":
logger.log(event_name, **kwargs)
elif event_name != "log":
queue.put(("pypjua_event", (event_name, kwargs)))
elif do_trace_pjsip:
queue.put(("print", "%(timestamp)s (%(level)d) %(sender)14s: %(message)s" % kwargs))
def read_queue(e, username, domain, password, display_name, route, xcap_root, expires, do_trace_pjsip):
global user_quit, lock, queue, sip_uri, winfo, xcap_client, logger
lock.acquire()
try:
sip_uri = SIPURI(user=username, host=domain, display=display_name)
sub = Subscription(Credentials(sip_uri, password), sip_uri, 'presence.winfo', route=route, expires=expires)
winfo = WatcherInfo()
if xcap_root is not None:
xcap_client = XCAPClient(xcap_root, '%s@%s' % (sip_uri.user, sip_uri.host), password=password, auth=None)
print 'Retrieving current presence rules from %s' % xcap_root
get_prules()
print 'Allowed list:'
if allow_rule_identities is not None:
for identity in allow_rule_identities:
print '\t%s' % identity
print 'Blocked list:'
if block_rule_identities is not None:
for identity in block_rule_identities:
print '\t%s' % identity
print 'Polite-blocked list:'
if polite_block_rule_identities is not None:
for identity in polite_block_rule_identities:
print '\t%s' % identity
print 'Subscribing to "%s@%s" for the presence.winfo event, at %s:%d' % (sip_uri.user, sip_uri.host, route.host, route.port)
sub.subscribe()
while True:
command, data = queue.get()
if command == "print":
print data
if len(pending) > 0:
print "%s watcher %s wants to subscribe to your presence information. Press (a) for allow, (d) for deny or (p) for polite blocking:" % (pending[0].status.capitalize(), pending[0])
if command == "pypjua_event":
event_name, args = data
if command == "user_input":
key = data
if len(pending) > 0:
if key == 'a':
watcher = pending.popleft()
allow_watcher(watcher)
elif key == 'd':
watcher = pending.popleft()
block_watcher(watcher)
elif key == 'p':
watcher = pending.popleft()
polite_block_watcher(watcher)
else:
print "Please select a valid choice. Press (a) to allow, (d) to deny, (p) to polite block"
if len(pending) > 0:
print "%s watcher %s wants to subscribe to your presence information. Press (a) for allow, (d) for deny or (p) for polite blocking:" % (pending[0].status.capitalize(), pending[0])
if command == "eof":
command = "end"
want_quit = True
if command == "end":
try:
sub.unsubscribe()
except:
pass
if command == "quit":
user_quit = False
break
except:
user_quit = False
traceback.print_exc()
finally:
e.stop()
logger.stop()
if not user_quit:
os.kill(os.getpid(), signal.SIGINT)
lock.release()
def do_subscribe(**kwargs):
global user_quit, lock, queue, do_trace_pjsip, logger
ctrl_d_pressed = False
do_trace_pjsip = kwargs["do_trace_pjsip"]
outbound_proxy = kwargs.pop("outbound_proxy")
if outbound_proxy is None:
routes = lookup_routes_for_sip_uri(SIPURI(host=kwargs["domain"]), kwargs.pop("sip_transports"))
else:
routes = lookup_routes_for_sip_uri(outbound_proxy, kwargs.pop("sip_transports"))
# Only try the first Route for now
try:
kwargs["route"] = routes[0]
except IndexError:
raise RuntimeError("No route found to SIP proxy")
logger = Logger(AccountConfig, GeneralConfig.log_directory, trace_sip=kwargs['trace_sip'])
if kwargs['trace_sip']:
print "Logging SIP trace to file '%s'" % logger._siptrace_filename
e = Engine(event_handler, trace_sip=kwargs.pop('trace_sip'), local_ip=kwargs.pop("local_ip"), local_udp_port=kwargs.pop("local_udp_port"), local_tcp_port=kwargs.pop("local_tcp_port"), local_tls_port=kwargs.pop("local_tls_port"))
e.start(False)
start_new_thread(read_queue, (e,), kwargs)
atexit.register(termios_restore)
try:
while True:
char = getchar()
if char == "\x04":
if not ctrl_d_pressed:
queue.put(("eof", None))
ctrl_d_pressed = True
else:
queue.put(("user_input", char))
except KeyboardInterrupt:
if user_quit:
print "Ctrl+C pressed, exiting instantly!"
queue.put(("quit", True))
lock.acquire()
return
def parse_outbound_proxy(option, opt_str, value, parser):
try:
parser.values.outbound_proxy = OutboundProxy(value)
except ValueError, e:
raise OptionValueError(e.message)
def parse_options():
retval = {}
description = "This script displays the current presence rules, SUBSCRIBEs to the presence.winfo event of itself and prompts the user to update the presence rules document when a new watcher is in 'pending'/'waiting' state. The program will un-SUBSCRIBE and quit when CTRL+D is pressed."
usage = "%prog [options]"
parser = OptionParser(usage=usage, description=description)
parser.print_usage = parser.print_help
parser.add_option("-a", "--account-name", type="string", dest="account_name", help="The account name from which to read account settings. Corresponds to section Account_NAME in the configuration file. If not supplied, the section Account will be read.", metavar="NAME")
parser.add_option("--sip-address", type="string", dest="sip_address", help="SIP address of the user in the form user@domain")
parser.add_option("-p", "--password", type="string", dest="password", help="Password to use to authenticate the local account. This overrides the setting from the config file.")
parser.add_option("-n", "--display-name", type="string", dest="display_name", help="Display name to use for the local account. This overrides the setting from the config file.")
parser.add_option("-e", "--expires", type="int", dest="expires", help='"Expires" value to set in SUBSCRIBE. Default is 300 seconds.')
parser.add_option("-o", "--outbound-proxy", type="string", action="callback", callback=parse_outbound_proxy, help="Outbound SIP proxy to use. By default a lookup of the domain is performed based on SRV and A records. This overrides the setting from the config file.", metavar="IP[:PORT]")
parser.add_option("-x", "--xcap-root", type="string", dest="xcap_root", help = 'The XCAP root to use to access the pres-rules document for authorizing subscriptions to presence.')
parser.add_option("-s", "--trace-sip", action="store_true", dest="trace_sip", help="Dump the raw contents of incoming and outgoing SIP messages (disabled by default).")
parser.add_option("-j", "--trace-pjsip", action="store_true", dest="do_trace_pjsip", help="Print PJSIP logging output (disabled by default).")
options, args = parser.parse_args()
if options.account_name is None:
account_section = "Account"
else:
account_section = "Account_%s" % options.account_name
if account_section not in configuration.parser.sections():
raise RuntimeError("There is no account section named '%s' in the configuration file" % account_section)
configuration.read_settings(account_section, AccountConfig)
if not AccountConfig.use_presence_agent:
raise RuntimeError("Presence is not enabled for this account. Please set use_presence_agent=True in the config file")
default_options = dict(expires=300, outbound_proxy=AccountConfig.outbound_proxy, sip_address=AccountConfig.sip_address, password=AccountConfig.password, display_name=AccountConfig.display_name, trace_sip=GeneralConfig.trace_sip, do_trace_pjsip=GeneralConfig.trace_pjsip, xcap_root=AccountConfig.xcap_root, local_ip=GeneralConfig.local_ip, local_udp_port=GeneralConfig.sip_local_udp_port, local_tcp_port=GeneralConfig.sip_local_tcp_port, local_tls_port=GeneralConfig.sip_local_tls_port, sip_transports=GeneralConfig.sip_transports)
options._update_loose(dict((name, value) for name, value in default_options.items() if getattr(options, name, None) is None))
for transport in set(["tls", "tcp", "udp"]) - set(options.sip_transports):
setattr(options, "local_%s_port" % transport, None)
if not all([options.sip_address, options.password]):
raise RuntimeError("No complete set of SIP credentials specified in config file and on commandline.")
for attr in default_options:
retval[attr] = getattr(options, attr)
try:
retval["username"], retval["domain"] = options.sip_address.split("@")
except ValueError:
raise RuntimeError("Invalid value for sip_address: %s" % options.sip_address)
else:
del retval["sip_address"]
accounts = [(acc == 'Account') and 'default' or "'%s'" % acc[8:] for acc in configuration.parser.sections() if acc.startswith('Account')]
accounts.sort()
print "Accounts available: %s" % ', '.join(accounts)
if options.account_name is None:
print "Using default account: %s" % options.sip_address
else:
print "Using account '%s': %s" % (options.account_name, options.sip_address)
return retval
def main():
do_subscribe(**parse_options())
if __name__ == "__main__":
try:
main()
except RuntimeError, e:
print "Error: %s" % str(e)
sys.exit(1)
except PyPJUAError, e:
print "Error: %s" % str(e)
sys.exit(1)
sys.exit(return_code)
diff --git a/scripts/xcap_pres_rules.py b/scripts/xcap_pres_rules.py
index 8f06f690..89819de6 100644
--- a/scripts/xcap_pres_rules.py
+++ b/scripts/xcap_pres_rules.py
@@ -1,462 +1,463 @@
#!/usr/bin/env python
import sys
import traceback
import string
import socket
import os
import atexit
import select
import termios
import signal
from collections import deque
from thread import start_new_thread, allocate_lock
from threading import Thread, Event
from Queue import Queue
from optparse import OptionParser, OptionValueError
from time import sleep
from application.process import process
from application.configuration import *
from urllib2 import URLError
from pypjua import *
from pypjua.clients import enrollment
from pypjua.applications import ParserError
from pypjua.applications.watcherinfo import *
from pypjua.applications.policy import *
from pypjua.applications.presrules import *
from pypjua.clients.clientconfig import get_path
-from pypjua.clients.lookup import *
+from pypjua.clients.dns_lookup import *
+from pypjua.clients import *
from xcaplib.client import XCAPClient
from xcaplib.error import HTTPError
class Boolean(int):
def __new__(typ, value):
if value.lower() == 'true':
return True
else:
return False
class AccountConfig(ConfigSection):
_datatypes = {"sip_address": str, "password": str, "display_name": str, "xcap_root": str, "use_presence_agent": Boolean}
sip_address = None
password = None
display_name = None
xcap_root = None
use_presence_agent = True
process._system_config_directory = os.path.expanduser("~/.sipclient")
enrollment.verify_account_config()
configuration = ConfigFile("config.ini")
queue = Queue()
packet_count = 0
start_time = None
old = None
user_quit = True
lock = allocate_lock()
string = None
getstr_event = Event()
show_xml = False
sip_uri = None
xcap_client = None
prules = None
prules_etag = None
allow_rule = None
allow_rule_identities = None
block_rule = None
block_rule_identities = None
polite_block_rule = None
polite_block_rule_identities = None
def get_prules():
global prules, prules_etag, allow_rule, block_rule, allow_rule_identities, block_rule_identities
prules = None
prules_etag = None
allow_rule = None
allow_rule_identities = None
block_rule = None
block_rule_identities = None
try:
doc = xcap_client.get('pres-rules')
except URLError, e:
print "Cannot obtain 'pres-rules' document: %s" % str(e)
except HTTPError, e:
if e.response.status != 404:
print "Cannot obtain 'pres-rules' document: %s %s" % (e.response.status, e.response.reason)
else:
prules = PresRules()
else:
try:
prules = PresRules.parse(doc)
except ParserError, e:
print "Invalid 'pres-rules' document: %s" % str(e)
else:
prules_etag = doc.etag
# find each rule type
for rule in prules:
if rule.actions is not None:
for action in rule.actions:
if isinstance(action, SubHandling):
if action == 'allow':
if rule.conditions is not None:
for condition in rule.conditions:
if isinstance(condition, Identity):
allow_rule = rule
allow_rule_identities = condition
break
elif action == 'block':
if rule.conditions is not None:
for condition in rule.conditions:
if isinstance(condition, Identity):
block_rule = rule
block_rule_identities = condition
break
elif action == 'polite-block':
if rule.conditions is not None:
for condition in rule.conditions:
if isinstance(condition, Identity):
polite_block_rule = rule
polite_block_rule_identities = condition
break
break
def allow_watcher(watcher):
global prules, prules_etag, allow_rule, allow_rule_identities, show_xml
for i in xrange(3):
if prules is None:
get_prules()
if prules is not None:
if allow_rule is None:
allow_rule_identities = Identity()
allow_rule = Rule('pres_whitelist', conditions=Conditions([allow_rule_identities]), actions=Actions([SubHandling('allow')]),
transformations=Transformations([ProvideServices([AllServices()]), ProvidePersons([AllPersons()]),
ProvideDevices([AllDevices()]), ProvideAllAttributes()]))
prules.append(allow_rule)
if str(watcher) not in allow_rule_identities:
allow_rule_identities.append(IdentityOne(str(watcher)))
try:
res = xcap_client.put('pres-rules', prules.toxml(pretty_print=True), etag=prules_etag)
except HTTPError, e:
print "Cannot PUT 'pres-rules' document: %s" % str(e)
prules = None
else:
prules_etag = res.etag
if show_xml:
print "Presence rules document:"
print prules.toxml(pretty_print=True)
print "Watcher %s is now authorized" % watcher
break
sleep(0.1)
else:
print "Could not authorized watcher %s" % watcher
def block_watcher(watcher):
global prules, prules_etag, block_rule, block_rule_identities
for i in xrange(3):
if prules is None:
get_prules()
if prules is not None:
if block_rule is None:
block_rule_identities = Identity()
block_rule = Rule('pres_blacklist', conditions=Conditions([block_rule_identities]), actions=Actions([SubHandling('block')]),
transformations=Transformations())
prules.append(block_rule)
if str(watcher) not in block_rule_identities:
block_rule_identities.append(IdentityOne(str(watcher)))
try:
res = xcap_client.put('pres-rules', prules.toxml(pretty_print=True), etag=prules_etag)
except HTTPError, e:
print "Cannot PUT 'pres-rules' document: %s" % str(e)
prules = None
else:
prules_etag = res.etag
if show_xml:
print "Presence rules document:"
print prules.toxml(pretty_print=True)
print "Watcher %s is now denied authorization" % watcher
break
sleep(0.1)
else:
print "Could not deny authorization of watcher %s" % watcher
def polite_block_watcher(watcher):
global prules, prules_etag, polite_block_rule, polite_block_rule_identities
for i in xrange(3):
if prules is None:
get_prules()
if prules is not None:
if polite_block_rule is None:
polite_block_rule_identities = Identity()
polite_block_rule = Rule('pres_polite_blacklist', conditions=Conditions([polite_block_rule_identities]), actions=Actions([SubHandling('polite-block')]),
transformations=Transformations())
prules.append(polite_block_rule)
if str(watcher) not in polite_block_rule_identities:
polite_block_rule_identities.append(IdentityOne(str(watcher)))
try:
res = xcap_client.put('pres-rules', prules.toxml(pretty_print=True), etag=prules_etag)
except HTTPError, e:
print "Cannot PUT 'pres-rules' document: %s" % str(e)
prules = None
else:
prules_etag = res.etag
if show_xml:
print "Presence rules document:"
print prules.toxml(pretty_print=True)
print "Watcher %s is now politely blocked" % watcher
break
sleep(0.1)
else:
print "Could not politely block authorization of watcher %s" % watcher
def remove_watcher(watcher):
global prules, prules_etag, allow_rule_identities, block_rule_identities, polite_block_rule_identities
for i in xrange(3):
if prules is None:
get_prules()
if prules is not None:
if allow_rule_identities is not None and str(watcher) in allow_rule_identities:
allow_rule_identities.remove(str(watcher))
if len(allow_rule_identities) == 0:
prules.remove(allow_rule)
if block_rule_identities is not None and str(watcher) in block_rule_identities:
block_rule_identities.remove(str(watcher))
if len(block_rule_identities) == 0:
prules.remove(block_rule)
if polite_block_rule_identities is not None and str(watcher) in polite_block_rule_identities:
polite_block_rule_identities.remove(str(watcher))
if len(polite_block_rule_identities) == 0:
prules.remove(polite_block_rule)
try:
res = xcap_client.put('pres-rules', prules.toxml(pretty_print=True), etag=prules_etag)
except HTTPError, e:
print "Cannot PUT 'pres-rules' document: %s" % str(e)
prules = None
else:
prules_etag = res.etag
if show_xml:
print "Presence rules document:"
print prules.toxml(pretty_print=True)
print "Watcher %s has been removed from the rules" % watcher
break
sleep(0.1)
else:
print "Could not politely block authorization of watcher %s" % watcher
def print_prules():
global allow_rule_identities, block_rule_identities, polite_block_rule_identities
print 'Allowed watchers:'
if allow_rule_identities is not None:
for identity in allow_rule_identities:
print '\t%s' % str(identity).replace('sip:', '')
print 'Blocked watchers:'
if block_rule_identities is not None:
for identity in block_rule_identities:
print '\t%s' % str(identity).replace('sip:', '')
print 'Polite-blocked watchers:'
if polite_block_rule_identities is not None:
for identity in polite_block_rule_identities:
print '\t%s' % str(identity).replace('sip:', '')
print "Press (a) to allow, (d) to deny, (p) to politely block a new watcher or (r) to remove a watcher from the rules. (s) will show the presence rules xml."
def termios_restore():
global old
if old is not None:
termios.tcsetattr(sys.stdin.fileno(), termios.TCSADRAIN, old)
def getchar():
global old
fd = sys.stdin.fileno()
if os.isatty(fd):
old = termios.tcgetattr(fd)
new = termios.tcgetattr(fd)
new[3] = new[3] & ~termios.ICANON & ~termios.ECHO
new[6][termios.VMIN] = '\000'
try:
termios.tcsetattr(fd, termios.TCSADRAIN, new)
if select.select([fd], [], [], None)[0]:
return sys.stdin.read(10)
finally:
termios_restore()
else:
return os.read(fd, 10)
def getstr(prompt='selection'):
global string, getstr_event
string = ''
sys.stdout.write("%s> " % prompt)
sys.stdout.flush()
getstr_event.wait()
getstr_event.clear()
sys.stdout.write("\n")
ret = string
string = None
return ret
def read_queue(username, domain, password, display_name, xcap_root):
global user_quit, lock, queue, sip_uri, xcap_client
lock.acquire()
try:
sip_uri = SIPURI(user=username, host=domain, display=display_name)
if xcap_root is not None:
xcap_client = XCAPClient(xcap_root, '%s@%s' % (sip_uri.user, sip_uri.host), password=password, auth=None)
print 'Retrieving current presence rules from %s' % xcap_root
get_prules()
if show_xml and prules is not None:
print "Presence rules document:"
print prules.toxml(pretty_print=True)
print_prules()
while True:
command, data = queue.get()
if command == "print":
print data
if command == "pypjua_event":
event_name, args = data
if command == "user_input":
key = data
if key == 'a':
watcher = getstr('watcher')
if watcher != '':
watcher = 'sip:' + watcher
allow_watcher(watcher)
elif key == 'd':
watcher = getstr('watcher')
if watcher != '':
watcher = 'sip:' + watcher
block_watcher(watcher)
elif key == 'p':
watcher = getstr('watcher')
if watcher != '':
watcher = 'sip:' + watcher
polite_block_watcher(watcher)
elif key == 'r':
watcher = getstr('watcher')
if watcher != '':
watcher = 'sip:' + watcher
remove_watcher(watcher)
elif key == 's':
if prules is not None:
print "Presence rules document:"
print prules.toxml(pretty_print=True)
print_prules()
if command == "eof":
command = "end"
want_quit = True
if command == "quit" or command == "end":
user_quit = False
break
except:
user_quit = False
traceback.print_exc()
finally:
if not user_quit:
os.kill(os.getpid(), signal.SIGINT)
lock.release()
def do_xcap_pres_rules(**kwargs):
global user_quit, lock, queue, string, getstr_event, old, show_xml
ctrl_d_pressed = False
show_xml = kwargs.pop('show_xml')
start_new_thread(read_queue,(), kwargs)
atexit.register(termios_restore)
try:
while True:
char = getchar()
if char == "\x04":
if not ctrl_d_pressed:
queue.put(("eof", None))
ctrl_d_pressed = True
else:
if string is not None:
if char == "\x7f":
if len(string) > 0:
char = "\x08"
sys.stdout.write("\x08 \x08")
sys.stdout.flush()
string = string[:-1]
else:
if old is not None:
sys.stdout.write(char)
sys.stdout.flush()
if char == "\x0A":
getstr_event.set()
else:
string += char
else:
queue.put(("user_input", char))
except KeyboardInterrupt:
if user_quit:
print "Ctrl+C pressed, exiting instantly!"
queue.put(("quit", True))
lock.acquire()
return
def parse_options():
retval = {}
description = "This example script will use the specified SIP account to manage presence rules via XCAP. The program will quit when CTRL+D is pressed."
usage = "%prog [options]"
parser = OptionParser(usage=usage, description=description)
parser.print_usage = parser.print_help
parser.add_option("-a", "--account-name", type="string", dest="account_name", help="The account name from which to read account settings. Corresponds to section Account_NAME in the configuration file. If not supplied, the section Account will be read.", metavar="NAME")
parser.add_option("--sip-address", type="string", dest="sip_address", help="SIP address of the user in the form user@domain")
parser.add_option("-p", "--password", type="string", dest="password", help="Password to use to authenticate the local account. This overrides the setting from the config file.")
parser.add_option("-x", "--xcap-root", type="string", dest="xcap_root", help = 'The XCAP root to use to access the pres-rules document for authorizing subscriptions to presence.')
parser.add_option("-s", "--show-xml", action="store_true", dest="show_xml", help = 'Show the presence rules XML whenever it is changed and at start-up.')
options, args = parser.parse_args()
if options.account_name is None:
account_section = "Account"
else:
account_section = "Account_%s" % options.account_name
if account_section not in configuration.parser.sections():
raise RuntimeError("There is no account section named '%s' in the configuration file" % account_section)
configuration.read_settings(account_section, AccountConfig)
if not AccountConfig.use_presence_agent:
raise RuntimeError("Presence is not enabled for this account. Please set use_presence_agent=True in the config file")
default_options = dict(sip_address=AccountConfig.sip_address, password=AccountConfig.password, display_name=AccountConfig.display_name, xcap_root=AccountConfig.xcap_root, show_xml=False)
options._update_loose(dict((name, value) for name, value in default_options.items() if getattr(options, name, None) is None))
if not all([options.sip_address, options.password]):
raise RuntimeError("No complete set of SIP credentials specified in config file and on commandline.")
for attr in default_options:
retval[attr] = getattr(options, attr)
try:
retval["username"], retval["domain"] = options.sip_address.split("@")
except ValueError:
raise RuntimeError("Invalid value for sip_address: %s" % options.sip_address)
else:
del retval["sip_address"]
accounts = [(acc == 'Account') and 'default' or "'%s'" % acc[8:] for acc in configuration.parser.sections() if acc.startswith('Account')]
accounts.sort()
print "Accounts available: %s" % ', '.join(accounts)
if options.account_name is None:
print "Using default account: %s" % options.sip_address
else:
print "Using account '%s': %s" % (options.account_name, options.sip_address)
return retval
def main():
do_xcap_pres_rules(**parse_options())
if __name__ == "__main__":
try:
main()
except RuntimeError, e:
print "Error: %s" % str(e)
sys.exit(1)
diff --git a/scripts/xcap_rls_services.py b/scripts/xcap_rls_services.py
index 04d43b79..e163f9cd 100644
--- a/scripts/xcap_rls_services.py
+++ b/scripts/xcap_rls_services.py
@@ -1,398 +1,399 @@
#!/usr/bin/env python
import sys
import traceback
import string
import socket
import os
import atexit
import select
import termios
import signal
from collections import deque
from thread import start_new_thread, allocate_lock
from threading import Thread, Event
from Queue import Queue
from optparse import OptionParser, OptionValueError
from time import sleep
from application.process import process
from application.configuration import *
from urllib2 import URLError
from pypjua import *
from pypjua.clients import enrollment
from pypjua.applications import ParserError
from pypjua.applications.resourcelists import *
from pypjua.applications.rlsservices import *
from pypjua.clients.clientconfig import get_path
-from pypjua.clients.lookup import *
+from pypjua.clients.dns_lookup import *
+from pypjua.clients import *
from xcaplib.client import XCAPClient
from xcaplib.error import HTTPError
class Boolean(int):
def __new__(typ, value):
if value.lower() == 'true':
return True
else:
return False
class AccountConfig(ConfigSection):
_datatypes = {"sip_address": str, "password": str, "display_name": str, "xcap_root": str, "use_presence_agent": Boolean}
sip_address = None
password = None
display_name = None
xcap_root = None
use_presence_agent = True
process._system_config_directory = os.path.expanduser("~/.sipclient")
enrollment.verify_account_config()
configuration = ConfigFile("config.ini")
queue = Queue()
packet_count = 0
start_time = None
old = None
user_quit = True
lock = allocate_lock()
string = None
getstr_event = Event()
show_xml = False
sip_uri = None
xcap_client = None
service_uri = None
rls_services = None
buddy_service = None
def get_rls_services():
global rls_services, rls_services_etag, buddy_service
rls_services = None
rls_services_etag = None
buddy_service = None
try:
doc = xcap_client.get('rls-services')
except URLError, e:
print "Cannot obtain 'rls-services' document: %s" % str(e)
except HTTPError, e:
if e.response.status != 404:
print "Cannot obtain 'rls-services' document: %s %s" % (e.response.status, e.response.reason)
else:
buddy_service = Service(service_uri, list=RLSList(), packages=['presence'])
rls_services = RLSServices([buddy_service])
else:
try:
rls_services = RLSServices.parse(doc)
except ParserError, e:
print "Invalid 'rls-services' document: %s" % str(e)
else:
rls_services_etag = doc.etag
if service_uri in rls_services:
buddy_service = rls_services[service_uri]
if not isinstance(buddy_service.list, RLSList):
raise RuntimeError("service element `%s' must contain a `list' element, not a `resource-list' element in order to be managed" % service_uri)
else:
buddy_service = Service(service_uri, list=RLSList(), packages=['presence'])
rls_services.append(buddy_service)
def add_buddy(buddy):
global rls_services, rls_services_etag, buddy_service, show_xml
for i in xrange(3):
if rls_services is None:
get_rls_services()
if rls_services is not None:
if buddy not in buddy_service.list:
buddy_service.list.append(Entry(buddy))
try:
res = xcap_client.put('rls-services', rls_services.toxml(pretty_print=True), etag=rls_services_etag)
except HTTPError, e:
print "Cannot PUT 'rls-services' document: %s" % str(e)
rls_services = None
else:
rls_services_etag = res.etag
if show_xml:
print "RLS Services document:"
print rls_services.toxml(pretty_print=True)
print "Buddy %s has been added" % buddy
break
else:
print "Buddy %s already in list" % buddy
break
sleep(0.1)
else:
print "Could not add buddy %s" % buddy
def remove_buddy(buddy):
global rls_services, rls_services_etag, buddy_service, show_xml
for i in xrange(3):
if rls_services is None:
get_rls_services()
if rls_services is not None:
if buddy in buddy_service.list:
buddy_service.list.remove(buddy)
try:
res = xcap_client.put('rls-services', rls_services.toxml(pretty_print=True), etag=rls_services_etag)
except HTTPError, e:
print "Cannot PUT 'rls-services' document: %s" % str(e)
rls_services = None
else:
rls_services_etag = res.etag
if show_xml:
print "RLS Services document:"
print rls_services.toxml(pretty_print=True)
print "Buddy %s has been removed" % buddy
break
else:
print "No such buddy: %s" % buddy
break
sleep(0.1)
else:
print "Could not remove buddy %s" % buddy
def delete_service():
global rls_services, rls_services_etag, buddy_service, show_xml
for i in xrange(3):
if rls_services is None:
get_rls_services()
if rls_services is not None:
if buddy_service.uri in rls_services:
rls_services.remove(buddy_service.uri)
try:
res = xcap_client.put('rls-services', rls_services.toxml(pretty_print=True), etag=rls_services_etag)
except HTTPError, e:
print "Cannot PUT 'rls-services' document: %s" % str(e)
rls_services = None
else:
rls_services_etag = res.etag
if show_xml:
print "RLS Services document:"
print rls_services.toxml(pretty_print=True)
print "Service %s has been removed" % buddy_service.uri
queue.put(("quit", None))
break
else:
print "No such service: %s" % buddy_service.uri
queue.put(("quit", None))
break
sleep(0.1)
else:
print "Could not delete service %s" % buddy_service.uri
def print_rls_services():
global rls_services, rls_services_etag, buddy_service, show_xml
print '\nBuddies:'
for buddy in buddy_service.list:
print '\t%s' % str(buddy).replace('sip:', '')
print "Press (a) to add or (r) to remove a buddy. (s) will show the RLS services xml. (d) will delete the currently selected service."
def termios_restore():
global old
if old is not None:
termios.tcsetattr(sys.stdin.fileno(), termios.TCSADRAIN, old)
def getchar():
global old
fd = sys.stdin.fileno()
if os.isatty(fd):
old = termios.tcgetattr(fd)
new = termios.tcgetattr(fd)
new[3] = new[3] & ~termios.ICANON & ~termios.ECHO
new[6][termios.VMIN] = '\000'
try:
termios.tcsetattr(fd, termios.TCSADRAIN, new)
if select.select([fd], [], [], None)[0]:
return sys.stdin.read(4192)
finally:
termios_restore()
else:
return os.read(fd, 4192)
def getstr(prompt='selection'):
global string, getstr_event
string = ''
sys.stdout.write("%s> " % prompt)
sys.stdout.flush()
getstr_event.wait()
getstr_event.clear()
sys.stdout.write("\n")
ret = string
string = None
return ret
def read_queue(username, domain, password, display_name, xcap_root):
global user_quit, lock, queue, sip_uri, xcap_client, service_uri
lock.acquire()
try:
sip_uri = SIPURI(user=username, host=domain, display=display_name)
if xcap_root is not None:
xcap_client = XCAPClient(xcap_root, '%s@%s' % (sip_uri.user, sip_uri.host), password=password, auth=None)
print 'Retrieving current RLS services from %s' % xcap_root
get_rls_services()
if show_xml and rls_services is not None:
print "RLS services document:"
print rls_services.toxml(pretty_print=True)
print 'Managing service URI %s' % service_uri
print_rls_services()
while True:
command, data = queue.get()
if command == "print":
print data
if command == "pypjua_event":
event_name, args = data
if command == "user_input":
key = data
if key == 'a':
buddy = getstr('new buddy')
if buddy != '':
if '@' not in buddy:
buddy = 'sip:%s@%s' % (buddy, domain)
else:
buddy = 'sip:%s' % buddy
add_buddy(buddy)
elif key == 'r':
buddy = getstr('buddy to delete')
if buddy != '':
if '@' not in buddy:
buddy = 'sip:%s@%s' % (buddy, domain)
else:
buddy = 'sip:%s' % buddy
remove_buddy(buddy)
elif key == 's':
if rls_services is not None:
print "RLS services document:"
print rls_services.toxml(pretty_print=True)
elif key == 'd':
delete_service()
if key != 'd':
print_rls_services()
if command == "eof":
command = "end"
want_quit = True
if command == "quit" or command == "end":
user_quit = False
break
except:
user_quit = False
traceback.print_exc()
finally:
if not user_quit:
os.kill(os.getpid(), signal.SIGINT)
lock.release()
def do_xcap_rls_services(**kwargs):
global user_quit, lock, queue, string, getstr_event, old, show_xml
ctrl_d_pressed = False
show_xml = kwargs.pop('show_xml')
start_new_thread(read_queue,(), kwargs)
atexit.register(termios_restore)
try:
while True:
for char in getchar():
if char == "\x04":
if not ctrl_d_pressed:
queue.put(("eof", None))
ctrl_d_pressed = True
break
else:
if string is not None:
if char == "\x7f":
if len(string) > 0:
char = "\x08"
sys.stdout.write("\x08 \x08")
sys.stdout.flush()
string = string[:-1]
else:
if old is not None:
sys.stdout.write(char)
sys.stdout.flush()
if char == "\x0A":
getstr_event.set()
break
else:
string += char
else:
queue.put(("user_input", char))
except KeyboardInterrupt:
if user_quit:
print "Ctrl+C pressed, exiting instantly!"
queue.put(("quit", True))
lock.acquire()
return
def parse_options():
global service_uri
retval = {}
description = "This example script will use the specified SIP account to manage rls services via XCAP. The program will quit when CTRL+D is pressed. You can specify the service URI as an argument (if domain name is not specified, the user's domain name will be used). If it is not specified, it defaults to username-buddies@domain."
usage = "%prog [options] [service URI]"
parser = OptionParser(usage=usage, description=description)
parser.print_usage = parser.print_help
parser.add_option("-a", "--account-name", type="string", dest="account_name", help="The account name from which to read account settings. Corresponds to section Account_NAME in the configuration file. If not supplied, the section Account will be read.", metavar="NAME")
parser.add_option("--sip-address", type="string", dest="sip_address", help="SIP address of the user in the form user@domain")
parser.add_option("-p", "--password", type="string", dest="password", help="Password to use to authenticate the local account. This overrides the setting from the config file.")
parser.add_option("-x", "--xcap-root", type="string", dest="xcap_root", help = 'The XCAP root to use to access the rls-services document to manage.')
parser.add_option("-s", "--show-xml", action="store_true", dest="show_xml", help = 'Show the RLS services XML whenever it is changed and at start-up.')
options, args = parser.parse_args()
if options.account_name is None:
account_section = "Account"
else:
account_section = "Account_%s" % options.account_name
if account_section not in configuration.parser.sections():
raise RuntimeError("There is no account section named '%s' in the configuration file" % account_section)
configuration.read_settings(account_section, AccountConfig)
if not AccountConfig.use_presence_agent:
raise RuntimeError("Presence is not enabled for this account. Please set use_presence_agent=True in the config file")
default_options = dict(sip_address=AccountConfig.sip_address, password=AccountConfig.password, display_name=AccountConfig.display_name, xcap_root=AccountConfig.xcap_root, show_xml=False)
options._update_loose(dict((name, value) for name, value in default_options.items() if getattr(options, name, None) is None))
if not all([options.sip_address, options.password]):
raise RuntimeError("No complete set of SIP credentials specified in config file and on commandline.")
for attr in default_options:
retval[attr] = getattr(options, attr)
try:
retval["username"], retval["domain"] = options.sip_address.split("@")
except ValueError:
raise RuntimeError("Invalid value for sip_address: %s" % options.sip_address)
else:
del retval["sip_address"]
accounts = [(acc == 'Account') and 'default' or "'%s'" % acc[8:] for acc in configuration.parser.sections() if acc.startswith('Account')]
accounts.sort()
print "Accounts available: %s" % ', '.join(accounts)
if options.account_name is None:
print "Using default account: %s" % options.sip_address
else:
print "Using account '%s': %s" % (options.account_name, options.sip_address)
if len(args) > 0:
if '@' not in args[0]:
service_uri = 'sip:%s@%s' % (args[0], retval["domain"])
else:
service_uri = 'sip:%s' % args[0]
else:
service_uri = 'sip:%s-buddies@%s' % (retval["username"], retval["domain"])
return retval
def main():
do_xcap_rls_services(**parse_options())
if __name__ == "__main__":
try:
main()
except RuntimeError, e:
print "Error: %s" % str(e)
sys.exit(1)

File Metadata

Mime Type
text/x-diff
Expires
Sat, Feb 1, 5:13 PM (1 d, 16 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3489421
Default Alt Text
(221 KB)

Event Timeline