Page MenuHomePhabricator

No OneTemporary

diff --git a/sip-register b/sip-register
index 7273a46..9191652 100755
--- a/sip-register
+++ b/sip-register
@@ -1,349 +1,365 @@
#!/usr/bin/python2
import atexit
import os
import select
import signal
import sys
import termios
from datetime import datetime
from optparse import OptionParser
from threading import Thread
from time import sleep
from application import log
from application.notification import NotificationCenter, NotificationData
from application.python.queue import EventQueue
from sipsimple.account import Account, AccountManager, BonjourAccount
from sipsimple.application import SIPApplication
from sipsimple.configuration import ConfigurationError
from sipsimple.configuration.settings import SIPSimpleSettings
from sipsimple.core import Engine
from sipsimple.storage import FileStorage
from sipclient.configuration import config_directory
from sipclient.configuration.account import AccountExtension
from sipclient.configuration.settings import SIPSimpleSettingsExtension
from sipclient.log import Logger
from sipclient.system import IPAddressMonitor
class BonjourNeighbour(object):
def __init__(self, neighbour, uri, display_name, host):
self.display_name = display_name
self.host = host
self.neighbour = neighbour
self.uri = uri
class InputThread(Thread):
def __init__(self):
Thread.__init__(self)
self.daemon = True
self._old_terminal_settings = None
def start(self):
atexit.register(self._termios_restore)
Thread.start(self)
def run(self):
notification_center = NotificationCenter()
while True:
for char in self._getchars():
notification_center.post_notification('SIPApplicationGotInput', sender=self, data=NotificationData(input=char))
def stop(self):
self._termios_restore()
def _termios_restore(self):
if self._old_terminal_settings is not None:
termios.tcsetattr(sys.stdin.fileno(), termios.TCSADRAIN, self._old_terminal_settings)
def _getchars(self):
fd = sys.stdin.fileno()
if os.isatty(fd):
self._old_terminal_settings = 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:
self._termios_restore()
else:
return os.read(fd, 4192)
class RegistrationApplication(SIPApplication):
def __init__(self):
self.account = None
self.options = None
self.input = None
self.output = None
self.ip_address_monitor = IPAddressMonitor()
self.logger = None
self.max_registers = None
self.neighbours = {}
self.success = False
def _write(self, message):
if isinstance(message, unicode):
message = message.encode(sys.getfilesystemencoding())
sys.stdout.write(message)
sys.stdout.flush()
def start(self, options):
notification_center = NotificationCenter()
self.options = options
self.max_registers = options.max_registers if options.max_registers > 0 else None
self.input = InputThread() if not options.batch_mode else None
self.output = EventQueue(self._write)
self.logger = Logger(sip_to_stdout=options.trace_sip, pjsip_to_stdout=options.trace_pjsip, notifications_to_stdout=options.trace_notifications)
notification_center.add_observer(self, sender=self)
notification_center.add_observer(self, sender=self.input)
notification_center.add_observer(self, name='SIPSessionNewIncoming')
notification_center.add_observer(self, name='DNSLookupDidSucceed')
if self.input:
self.input.start()
self.output.start()
log.level.current = log.level.WARNING # get rid of twisted messages
Account.register_extension(AccountExtension)
BonjourAccount.register_extension(AccountExtension)
SIPSimpleSettings.register_extension(SIPSimpleSettingsExtension)
try:
SIPApplication.start(self, FileStorage(options.config_directory or config_directory))
except ConfigurationError, e:
self.output.put("Failed to load sipclient's configuration: %s\n" % str(e))
self.output.put("If an old configuration file is in place, delete it or move it and recreate the configuration using the sip_settings script.\n")
self.output.stop()
def print_help(self):
message = 'Available control keys:\n'
message += ' s: toggle SIP trace on the console\n'
message += ' j: toggle PJSIP trace on the console\n'
message += ' n: toggle notifications trace on the console\n'
message += ' Ctrl-d: quit the program\n'
message += ' ?: display this help message\n'
self.output.put('\n'+message)
def _NH_SIPApplicationWillStart(self, notification):
account_manager = AccountManager()
notification_center = NotificationCenter()
settings = SIPSimpleSettings()
if self.options.account is None:
self.account = account_manager.default_account
else:
possible_accounts = [account for account in account_manager.iter_accounts() if self.options.account in account.id and account.enabled]
if len(possible_accounts) > 1:
self.output.put('More than one account exists which matches %s: %s\n' % (self.options.account, ', '.join(sorted(account.id for account in possible_accounts))))
self.output.stop()
self.stop()
return
elif len(possible_accounts) == 0:
self.output.put('No enabled account that matches %s was found. Available and enabled accounts: %s\n' % (self.options.account, ', '.join(sorted(account.id for account in account_manager.get_accounts() if account.enabled))))
self.output.stop()
self.stop()
return
else:
self.account = possible_accounts[0]
for account in account_manager.iter_accounts():
if account is self.account:
if isinstance(account, Account):
account.sip.register = True
account.message_summary.enabled = False
account.presence.enabled = False
account.xcap.enabled = False
account.enabled = True
else:
account.enabled = False
self.output.put('Using account %s\n' % self.account.id)
notification_center.add_observer(self, sender=self.account)
if self.account is BonjourAccount() and self.max_registers is not None:
if self.max_registers == 1:
self.max_registers = len([transport for transport in settings.sip.transport_list if (transport!='tls' or self.account.tls.certificate is not None)])
else:
self.output.put('--max-registers option only accepts 0 or 1 if using Bonjour account\n')
self.output.stop()
self.stop()
return
# start logging
self.logger.start()
if settings.logs.trace_sip and self.logger._siptrace_filename is not None:
self.output.put('Logging SIP trace to file "%s"\n' % self.logger._siptrace_filename)
if settings.logs.trace_pjsip and self.logger._pjsiptrace_filename is not None:
self.output.put('Logging PJSIP trace to file "%s"\n' % self.logger._pjsiptrace_filename)
if settings.logs.trace_notifications and self.logger._notifications_filename is not None:
self.output.put('Logging notifications trace to file "%s"\n' % self.logger._notifications_filename)
def _NH_SIPApplicationDidStart(self, notification):
engine = Engine()
settings = SIPSimpleSettings()
engine.trace_sip = self.logger.sip_to_stdout or settings.logs.trace_sip
engine.log_level = settings.logs.pjsip_level if (self.logger.pjsip_to_stdout or settings.logs.trace_pjsip) else 0
self.ip_address_monitor.start()
if self.account is not BonjourAccount() and self.max_registers != 1 and not self.options.batch_mode:
self.print_help()
elif self.account is BonjourAccount() and self.max_registers is None:
self.print_help()
def _NH_SIPApplicationWillEnd(self, notification):
self.ip_address_monitor.stop()
def _NH_SIPApplicationDidEnd(self, notification):
if self.input:
self.input.stop()
self.output.stop()
self.output.join()
def _NH_DNSLookupDidSucceed(self, notification):
notification_center = NotificationCenter()
- result_text = ', '.join(('%s:%s (%s)' % (result.address, result.port, result.transport.upper()) for result in notification.data.result))
- self.output.put(u"\n%s DNS lookup for %s succeeded: %s\n" % (datetime.now().replace(microsecond=0), self.account.id.domain, result_text))
+ targets = {}
+ for result in notification.data.result:
+ try:
+ ports = targets[result.address]
+ except KeyError:
+ targets[result.address] = set()
+
+ port = '%s-%s' % (result.port, result.transport.upper())
+ targets[result.address].add(port)
+ result_text = ''
+ i = 1
+ for t in targets.keys():
+ result_text = result_text + ' ' + str(i) + ') ' + t + ': '
+ result_text = result_text + ' '.join(sorted(list(targets[t])))
+ i = i + 1
+
+ self.output.put(u"\n%s DNS lookup for %s succeeded:%s\n" % (datetime.now().replace(microsecond=0), self.account.id.domain, result_text))
+
def _NH_SIPApplicationGotInput(self, notification):
engine = Engine()
settings = SIPSimpleSettings()
key = notification.data.input
if key == '\x04':
self.stop()
elif key == 's':
self.logger.sip_to_stdout = not self.logger.sip_to_stdout
engine.trace_sip = self.logger.sip_to_stdout or settings.logs.trace_sip
self.output.put('SIP tracing to console is now %s.\n' % ('activated' if self.logger.sip_to_stdout else 'deactivated'))
elif key == 'j':
self.logger.pjsip_to_stdout = not self.logger.pjsip_to_stdout
engine.log_level = settings.logs.pjsip_level if (self.logger.pjsip_to_stdout or settings.logs.trace_pjsip) else 0
self.output.put('PJSIP tracing to console is now %s.\n' % ('activated' if self.logger.pjsip_to_stdout else 'deactivated'))
elif key == 'n':
self.logger.notifications_to_stdout = not self.logger.notifications_to_stdout
self.output.put('Notification tracing to console is now %s.\n' % ('activated' if self.logger.notifications_to_stdout else 'deactivated'))
elif key == '?':
self.print_help()
def _NH_SIPAccountRegistrationDidSucceed(self, notification):
contact_header = notification.data.contact_header
contact_header_list = notification.data.contact_header_list
expires = notification.data.expires
registrar = notification.data.registrar
if not self.success:
- message = '%s Registered contact "%s" for sip:%s at %s:%d;transport=%s (expires in %d seconds).\n' % (datetime.now().replace(microsecond=0), contact_header.uri, self.account.id, registrar.address, registrar.port, registrar.transport, expires)
+ message = '%s Registered contact "%s" at %s:%d;transport=%s for %d seconds\n' % (datetime.now().replace(microsecond=0), contact_header.uri, registrar.address, registrar.port, registrar.transport, expires)
if len(contact_header_list) > 1:
message += 'Other registered contacts:\n%s\n' % '\n'.join([' %s (expires in %s seconds)' % (str(other_contact_header.uri), other_contact_header.expires) for other_contact_header in contact_header_list if other_contact_header.uri != notification.data.contact_header.uri])
self.output.put(message)
self.success = True
else:
- self.output.put('%s Refreshed registered contact "%s" for sip:%s at %s:%d;transport=%s (expires in %d seconds).\n' % (datetime.now().replace(microsecond=0), contact_header.uri, self.account.id, registrar.address, registrar.port, registrar.transport, expires))
+ self.output.put('%s Refreshed contact "%s" at %s:%d;transport=%s for %d seconds\n' % (datetime.now().replace(microsecond=0), contact_header.uri, registrar.address, registrar.port, registrar.transport, expires))
if self.max_registers is not None:
self.max_registers -= 1
if self.max_registers == 0:
self.stop()
def _NH_SIPAccountRegistrationGotAnswer(self, notification):
if notification.data.code >= 300:
registrar = notification.data.registrar
code = notification.data.code
reason = notification.data.reason
self.output.put('%s Registration failed at %s:%d;transport=%s: %d %s\n' % (datetime.now().replace(microsecond=0), registrar.address, registrar.port, registrar.transport, code, reason))
def _NH_SIPAccountRegistrationDidFail(self, notification):
self.output.put('%s Failed to register contact for sip:%s: %s (retrying in %.2f seconds)\n' % (datetime.now().replace(microsecond=0), self.account.id, notification.data.error, notification.data.retry_after))
self.success = False
if self.max_registers is not None:
self.max_registers -= 1
if self.max_registers == 0:
self.stop()
def _NH_SIPAccountRegistrationDidEnd(self, notification):
self.output.put('%s Registration ended.\n' % datetime.now().replace(microsecond=0))
def _NH_BonjourAccountRegistrationDidSucceed(self, notification):
self.output.put('%s Registered Bonjour contact "%s"\n' % (datetime.now().replace(microsecond=0), notification.data.name))
if self.max_registers is not None:
self.max_registers -= 1
if self.max_registers == 0:
self.stop()
def _NH_BonjourAccountRegistrationDidFail(self, notification):
self.output.put('%s Failed to register Bonjour contact: %s\n' % (datetime.now().replace(microsecond=0), notification.data.reason))
if self.max_registers is not None:
self.max_registers -= 1
if self.max_registers == 0:
self.stop()
def _NH_BonjourAccountRegistrationDidEnd(self, notification):
self.output.put('%s Registration ended.\n' % datetime.now().replace(microsecond=0))
def _NH_BonjourAccountDidAddNeighbour(self, notification):
neighbour, record = notification.data.neighbour, notification.data.record
now = datetime.now().replace(microsecond=0)
self.output.put('%s Discovered Bonjour neighbour: "%s (%s)" <%s>\n' % (now, record.name, record.host, record.uri))
self.neighbours[neighbour] = BonjourNeighbour(neighbour, record.uri, record.name, record.host)
def _NH_BonjourAccountDidUpdateNeighbour(self, notification):
neighbour, record = notification.data.neighbour, notification.data.record
now = datetime.now().replace(microsecond=0)
try:
bonjour_neighbour = self.neighbours[neighbour]
except KeyError:
self.output.put('%s Discovered Bonjour neighbour: "%s (%s)" <%s>\n' % (now, record.name, record.host, record.uri))
self.neighbours[neighbour] = BonjourNeighbour(neighbour, record.uri, record.name, record.host)
else:
self.output.put('%s Updated Bonjour neighbour: "%s (%s)" <%s>\n' % (now, record.name, record.host, record.uri))
bonjour_neighbour.display_name = record.name
bonjour_neighbour.host = record.host
bonjour_neighbour.uri = record.uri
def _NH_BonjourAccountDidRemoveNeighbour(self, notification):
neighbour = notification.data.neighbour
now = datetime.now().replace(microsecond=0)
try:
bonjour_neighbour = self.neighbours.pop(neighbour)
except KeyError:
pass
else:
self.output.put('%s Bonjour neighbour left: "%s (%s)" <%s>\n' % (now, bonjour_neighbour.display_name, bonjour_neighbour.host, bonjour_neighbour.uri))
def _NH_SIPEngineGotException(self, notification):
self.output.put('%s An exception occured within the SIP core:\n%s\n' % (datetime.now().replace(microsecond=0), notification.data.traceback))
if __name__ == "__main__":
description = 'This script registers the contact address of the given SIP account to the 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', type='string', dest='account', help='The name of the account to use. If not supplied, the default account will be used.', metavar='NAME')
parser.add_option('-c', '--config-directory', type='string', dest='config_directory', help='The configuration directory to use. This overrides the default location.')
parser.add_option('-s', '--trace-sip', action='store_true', dest='trace_sip', default=False, help='Dump the raw contents of incoming and outgoing SIP messages (disabled by default).')
parser.add_option('-j', '--trace-pjsip', action='store_true', dest='trace_pjsip', default=False, help='Print PJSIP logging output (disabled by default).')
parser.add_option('-n', '--trace-notifications', action='store_true', dest='trace_notifications', default=False, help='Print all notifications (disabled by default).')
parser.add_option('-r', '--max-registers', type='int', dest='max_registers', default=1, help='Max number of REGISTERs sent (default 1, set to 0 for infinite).')
parser.add_option('-b', '--batch', action='store_true', dest='batch_mode', default=False, help='Run the program in batch mode: reading input from the console is disabled. This is particularly useful when running this script in a non-interactive environment.')
options, args = parser.parse_args()
application = RegistrationApplication()
application.start(options)
signal.signal(signal.SIGINT, signal.SIG_DFL)
application.output.join()
sleep(0.1)
sys.exit(0 if application.success else 1)

File Metadata

Mime Type
text/x-diff
Expires
Sat, Nov 23, 4:03 AM (20 h, 52 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3408766
Default Alt Text
(18 KB)

Event Timeline