Page MenuHomePhabricator

No OneTemporary

diff --git a/sip-publish-presence b/sip-publish-presence
index 30f2575..00d73b7 100755
--- a/sip-publish-presence
+++ b/sip-publish-presence
@@ -1,840 +1,840 @@
#!/usr/bin/env python3
import hashlib
import os
import random
import select
import sys
import termios
import traceback
import uuid
from collections import deque
from datetime import datetime
from optparse import OptionParser
from threading import RLock, Thread
from time import time
from application import log
from application.notification import IObserver, NotificationCenter, NotificationData
from application.python.queue import EventQueue
from eventlib.twistedutil import join_reactor
from twisted.internet import reactor
from twisted.internet.error import ReactorNotRunning
from zope.interface import implementer
from sipsimple.core import Engine, FromHeader, Publication, PublicationError, RouteHeader, SIPCoreError, SIPURI, PJSIPError
from sipsimple.account import Account, AccountManager, BonjourAccount
from sipsimple.application import SIPApplication
from sipsimple.configuration import ConfigurationError, ConfigurationManager
from sipsimple.configuration.settings import SIPSimpleSettings
from sipsimple.lookup import DNSLookup
from sipsimple.payloads import BuilderError
from sipsimple.payloads.pidf import Contact, Device, DeviceInfo, DMNote, DeviceTimestamp, Person, PIDFNote, PersonTimestamp, PIDF, Service, ServiceTimestamp, Status
from sipsimple.payloads.rpid import Activities, ActivityRegistry, Mood, MoodRegistry, RPIDNote, TimeOffset
from sipsimple.storage import FileStorage
from sipsimple.threading import run_in_twisted_thread
from sipclient.configuration import config_directory
from sipclient.configuration.account import AccountExtension
from sipclient.configuration.settings import SIPSimpleSettingsExtension
from sipclient.log import Logger
class KeyBinding(object):
def __init__(self, description, handler):
self.description = description
self.handler = handler
class Prompt(str):
"""Used to make a distinction between normal strings written to the console and prompts."""
class Menu(object):
key_bindings = {}
def __init__(self, interface):
self.interface = interface
def print_prompt(self):
buf = ['Commands:']
for key, binding in list(self.key_bindings.items()):
buf.append(' %s: %s' % (key, binding.description))
self.interface.application.output.put('\n'+'\n'.join(buf)+'\n')
def _exit(self):
self.interface.exit_menu()
def _exit_program(self):
self.interface.application.stop()
# Mood manipulation menu
class MoodMenu(Menu):
def _show_moods(self):
person = self.interface.application.person
buf = ['Moods:']
if person.mood is not None:
for m in list(person.mood):
buf.append(' %s' % str(m))
self.interface.application.output.put('\n'.join(buf))
def _add_mood(self):
person = self.interface.application.person
buf = ['Possible moods:']
values = list(MoodRegistry.names)
values.sort()
max_len = max(len(s) for s in values)+2
format = ' %%02d) %%-%ds' % max_len
num_line = 80/(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)
self.interface.application.output.put('\n'.join(buf)+'\n')
m = self.interface.getstring('Select mood to add (any non-number will return)')
try:
m = int(m)
if m not in range(len(values)):
raise ValueError
except ValueError:
self.interface.application.output.put('Invalid input')
else:
if person.mood is None:
person.mood = Mood()
person.mood.add(values[m-1])
person.timestamp = PersonTimestamp(datetime.now())
self.interface.application.publish()
self.interface.application.output.put('Mood added')
self.interface.show_top_level()
def _del_mood(self):
person = self.interface.application.person
if person.mood is None:
self.interface.application.output.put('There is no current mood set')
self.print_prompt()
return
buf = ['Current moods:']
values = list(person.mood)
values.sort()
max_len = max(len(s) for s in values)+2
format = " %%02d) %%-%ds" % max_len
num_line = 80/(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)
self.interface.application.output.put('\n'.join(buf)+'\n')
m = self.interface.getstring('Select mood to delete (any non-number will return)')
try:
m = int(m)
except ValueError:
self.interface.application.output.put('Invalid input')
else:
person.mood.remove(values[m-1])
person.timestamp = PersonTimestamp(datetime.now())
self.interface.application.publish()
self.interface.application.output.put('Mood deleted')
self.interface.show_top_level()
def _clear_moods(self):
person = self.interface.application.person
if person.mood is None:
self.interface.application.output.put('There is no current mood set')
self.print_prompt()
return
person.mood = None
person.timestamp = PersonTimestamp(datetime.now())
self.interface.application.publish()
self.interface.application.output.put('Mood information cleared')
self.interface.show_top_level()
def _set_note(self):
person = self.interface.application.person
if person.mood is not None and person.mood.notes:
a_note = next(iter(person.mood.notes))
self.interface.application.output.put('Current note: %s' % a_note)
note = self.interface.getstring('Set note (press return to delete)')
if note == '':
person.mood.notes.clear()
self.interface.application.output.put('Note removed')
else:
if person.mood is None:
person.mood = Mood()
person.mood.notes.add(RPIDNote(note, lang='en'))
self.interface.application.output.put('Note set')
person.timestamp = PersonTimestamp(datetime.now())
self.interface.application.publish()
self.interface.show_top_level()
def _set_random(self):
person = self.interface.application.person
values = list(value for value in MoodRegistry.names 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 = PersonTimestamp(datetime.now())
self.interface.application.publish()
self.interface.application.output.put('You are now ' + ', '.join(values))
self.interface.show_top_level()
key_bindings = {'s': KeyBinding(description='show current moods', handler=_show_moods),
'a': KeyBinding(description='add a mood', handler=_add_mood),
'd': KeyBinding(description='delete a mood', handler=_del_mood),
'c': KeyBinding(description='clear all mood data', handler=_clear_moods),
'n': KeyBinding(description='set mood note', handler=_set_note),
'r': KeyBinding(description='set random mood', handler=_set_random),
'x': KeyBinding(description='exit to upper level menu', handler=Menu._exit),
'q': KeyBinding(description='quit program', handler=Menu._exit_program)}
# Activities manipulation menu
class ActivitiesMenu(Menu):
def _show_activity(self):
person = self.interface.application.person
buf = ["Activity:"]
if person.activities is not None:
for a in list(person.activities):
buf.append(" %s" % str(a))
self.interface.application.output.put('\n'.join(buf))
def _set_activity(self):
person = self.interface.application.person
buf = ["Possible activities:"]
values = list(ActivityRegistry.names)
values.sort()
max_len = max(len(s) for s in values)+2
format = " %%02d) %%-%ds" % max_len
num_line = 80/(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 = ''
self.interface.application.output.put('\n'.join(buf)+'\n')
a = self.interface.getstring('Select activity to add (any non-number will return)')
try:
a = int(a)
if a-1 not in range(len(values)):
raise ValueError
except ValueError:
self.interface.application.output.put('Invalid input')
else:
if person.activities is None:
person.activities = Activities()
else:
person.activities.clear()
person.activities.add(values[a-1])
person.timestamp = PersonTimestamp(datetime.now())
self.interface.application.publish()
self.interface.application.output.put('Activity set')
self.interface.show_top_level()
def _del_activity(self):
person = self.interface.application.person
if person.activities is None or len(person.activities.values) == 0:
self.interface.application.output.put('There is no current activity set')
return
person.activities.clear()
person.activities.add('unknown')
person.timestamp = PersonTimestamp(datetime.now())
self.interface.application.publish()
self.interface.application.output.put('Activity deleted')
self.interface.show_top_level()
def _clear_activity(self):
person = self.interface.application.person
if person.activities is None:
self.interface.application.output.put('There is no current activity set')
return
person.activities = None
person.timestamp = PersonTimestamp(datetime.now())
self.interface.application.publish()
self.interface.application.output.put('Activities information cleared')
self.interface.show_top_level()
def _set_note(self):
person = self.interface.application.person
if person.activities is not None and person.activities.notes:
a_note = next(iter(person.activities.notes))
self.interface.application.output.put('Current note: %s' % a_note)
note = self.interface.getstring('Set note (press return to delete)')
if note == '':
person.activities.notes.clear()
self.interface.application.output.put('Note deleted')
else:
if person.activities is None:
person.activities = Activities()
person.activities.add('unknown')
person.activities.notes.add(RPIDNote(note, lang='en'))
self.interface.application.output.put('Note set')
person.timestamp = PersonTimestamp(datetime.now())
self.interface.application.publish()
self.interface.show_top_level()
def _set_random(self):
person = self.interface.application.person
values = list(value for value in ActivityRegistry.names 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 = PersonTimestamp(datetime.now())
self.interface.application.publish()
self.interface.application.output.put('You are now %s' % activity)
self.interface.show_top_level()
key_bindings = {'s': KeyBinding(description='show current activity', handler=_show_activity),
'a': KeyBinding(description='set activity', handler=_set_activity),
'd': KeyBinding(description='delete activity', handler=_del_activity),
'c': KeyBinding(description='clear all acitivity data', handler=_clear_activity),
'n': KeyBinding(description='set activity note', handler=_set_note),
'r': KeyBinding(description='set random activity', handler=_set_random),
'x': KeyBinding(description='exit to upper level menu', handler=Menu._exit),
'q': KeyBinding(description='quit program', handler=Menu._exit_program)}
# Extended status manipulation menu
class ExtendedStatusMenu(Menu):
def _show_extended_status(self):
service = self.interface.application.service
buf = ["Extended status:"]
if service.status.extended is not None:
buf.append(" %s" % str(service.status.extended))
self.interface.application.output.put('\n'.join(buf))
def _set_extended_status(self):
service = self.interface.application.service
buf = ["Possible statuses:"]
values = ['available', 'away', 'extended-away', 'busy', 'offline']
max_len = max(len(s) for s in values)+2
format = " %%02d) %%-%ds" % max_len
num_line = 80/(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)
self.interface.application.output.put('\n'.join(buf)+'\n')
a = self.interface.getstring('Select status (any non-number will return)')
try:
a = int(a)
if a-1 not in range(len(values)):
raise ValueError
except ValueError:
self.interface.application.output.put('Invalid input')
else:
status = values[a-1]
if status == 'offline':
service.status.basic = 'closed'
else:
service.status.basic = 'open'
service.status.extended = status
self.interface.application.publish()
- self.interface.application.output.put('Extended atatus set')
+ self.interface.application.output.put('Extended status set')
self.interface.show_top_level()
def _del_extended_status(self):
self.interface.application.service.status.extended = None
self.interface.application.publish()
self.interface.application.output.put('Activity deleted')
self.interface.show_top_level()
key_bindings = {'s': KeyBinding(description='show current extended status', handler=_show_extended_status),
'e': KeyBinding(description='set extended status', handler=_set_extended_status),
'd': KeyBinding(description='delete extended status', handler=_del_extended_status),
'x': KeyBinding(description='exit to upper level menu', handler=Menu._exit),
'q': KeyBinding(description='quit program', handler=Menu._exit_program)}
class TopLevelMenu(Menu):
def _show_pidf(self):
try:
pidf = self.interface.application.pidf.toxml(pretty_print=True)
except BuilderError as e:
print("PIDF as currently defined is invalid: %s" % str(e))
except:
traceback.print_exc()
else:
self.interface.application.output.put(pidf)
self.print_prompt()
def _set_mood_info(self):
mood_menu = MoodMenu(self.interface)
self.interface.add_menu(mood_menu)
def _set_activity_info(self):
activities_menu = ActivitiesMenu(self.interface)
self.interface.add_menu(activities_menu)
def _toggle_basic(self):
service = self.interface.application.service
if service.status.basic == 'open':
service.status.basic = 'closed'
else:
service.status.basic = 'open'
service.timestamp = ServiceTimestamp(datetime.now())
self.interface.application.output.put("Your basic status is now '%s'" % service.status.basic)
self.interface.application.publish()
self.print_prompt()
def _set_extended_status(self):
extended_status_menu = ExtendedStatusMenu(self.interface)
self.interface.add_menu(extended_status_menu)
def _set_note(self):
service = self.interface.application.service
if service.notes:
a_note = next(iter(service.notes))
self.interface.application.output.put('Current note: %s' % a_note)
note = self.interface.getstring('Set note (press return to delete)')
if note == '':
if service.notes:
service.notes.clear()
self.interface.application.output.put('Note removed')
else:
service.notes.add(PIDFNote(note, lang='en'))
self.interface.application.output.put('Note added')
self.interface.application.publish()
self.print_prompt()
key_bindings = {'s': KeyBinding(description='show PIDF', handler=_show_pidf),
'm': KeyBinding(description='set mood information', handler=_set_mood_info),
'a': KeyBinding(description='set activities information', handler=_set_activity_info),
'b': KeyBinding(description='toggle basic status', handler=_toggle_basic),
'e': KeyBinding(description='set extended status', handler=_set_extended_status),
'n': KeyBinding(description='set note', handler=_set_note),
'q': KeyBinding(description='quit program', handler=Menu._exit_program)}
class UserInterface(Thread):
def __init__(self, application):
Thread.__init__(self)
self.application = application
self.daemon = True
self.menu_stack = deque([TopLevelMenu(self)])
self._old_terminal_settings = None
def run(self):
self.menu_stack[-1].print_prompt()
notification_center = NotificationCenter()
while True:
for char in self._getchars():
menu = self.menu_stack[-1]
if char == '\x04':
self.application.stop()
return
elif char in menu.key_bindings:
menu.key_bindings[char].handler(menu)
else:
notification_center.post_notification('SAInputWasReceived', sender=self, data=NotificationData(input=char))
def stop(self):
self._termios_restore()
def add_menu(self, menu):
self.menu_stack.append(menu)
menu.print_prompt()
def show_top_level(self):
main = self.menu_stack[0]
self.menu_stack.clear()
self.menu_stack.append(main)
main.print_prompt()
def exit_menu(self):
if len(self.menu_stack) > 1:
self.menu_stack.pop()
self.menu_stack[-1].print_prompt()
def getstring(self, prompt='selection'):
self.application.output.put(Prompt(prompt))
return sys.stdin.readline().strip()
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] = b'\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)
@implementer(IObserver)
class PublicationApplication(object):
def __init__(self, account_name, trace_sip, trace_pjsip, trace_notifications):
self.account_name = account_name
self.interface = UserInterface(self)
self.output = EventQueue(self._output_handler)
self.logger = Logger(sip_to_stdout=trace_sip, pjsip_to_stdout=trace_pjsip, notifications_to_stdout=trace_notifications)
self.lookup = DNSLookup()
self.publication_lock = RLock()
self.success = False
self.account = None
self.publication = None
self.pidf = None
self.service = None
self.person = None
self.device = None
self.stopping = False
self.publishing = False
self._publication_routes = None
self._publication_timeout = 0.0
self._publication_wait = 0.5
account_manager = AccountManager()
engine = Engine()
notification_center = NotificationCenter()
notification_center.add_observer(self, sender=account_manager)
notification_center.add_observer(self, sender=engine)
notification_center.add_observer(self, sender=self.interface)
notification_center.add_observer(self, sender=self.lookup)
log.level.current = log.level.WARNING
def run(self):
account_manager = AccountManager()
configuration = ConfigurationManager()
engine = Engine()
notification_center = NotificationCenter()
# start output thread
self.output.start()
# startup configuration
Account.register_extension(AccountExtension)
BonjourAccount.register_extension(AccountExtension)
SIPSimpleSettings.register_extension(SIPSimpleSettingsExtension)
SIPApplication.storage = FileStorage(config_directory)
try:
configuration.start()
except ConfigurationError as e:
raise RuntimeError("Failed to load sipclient's configuration: %s\nIf an old configuration file is in place, delete it or move it and recreate the configuration using the sip_settings script." % str(e))
account_manager.load()
if self.account_name is None:
self.account = account_manager.default_account
else:
possible_accounts = [account for account in account_manager.iter_accounts() if self.account_name in account.id and account.enabled]
if len(possible_accounts) > 1:
raise RuntimeError("More than one account exists which matches %s: %s" % (self.account_name, ", ".join(sorted(account.id for account in possible_accounts))))
if len(possible_accounts) == 0:
raise RuntimeError("No enabled account that matches %s was found. Available and enabled accounts: %s" % (self.account_name, ", ".join(sorted(account.id for account in account_manager.get_accounts() if account.enabled))))
self.account = possible_accounts[0]
if self.account is None:
raise RuntimeError("Unknown account %s. Available enabled accounts: %s" % (self.account_name, ', '.join(sorted(account.id for account in account_manager.iter_accounts() if account.enabled))))
elif self.account == BonjourAccount():
raise RuntimeError("Cannot use bonjour account to publish presence information")
elif not self.account.presence.enabled:
raise RuntimeError("Presence is not enabled for account %s" % self.account.id)
for account in account_manager.iter_accounts():
if account == self.account:
account.sip.register = False
else:
account.enabled = False
self.output.put('Using account %s' % self.account.id)
settings = SIPSimpleSettings()
# start logging
self.logger.start()
# start the engine
engine.start(
udp_port=settings.sip.udp_port if "udp" in settings.sip.transport_list else None,
tcp_port=settings.sip.tcp_port if "tcp" in settings.sip.transport_list else None,
tls_port=settings.sip.tls_port if "tls" in settings.sip.transport_list else None,
tls_verify_server=self.account.tls.verify_server,
tls_ca_file=os.path.expanduser(settings.tls.ca_list) if settings.tls.ca_list else None,
tls_cert_file=os.path.expanduser(self.account.tls.certificate) if self.account.tls.certificate else None,
tls_privkey_file=os.path.expanduser(self.account.tls.certificate) if self.account.tls.certificate else None,
user_agent=settings.user_agent,
sample_rate=settings.audio.sample_rate,
rtp_port_range=(settings.rtp.port_range.start, settings.rtp.port_range.end),
trace_sip=settings.logs.trace_sip or self.logger.sip_to_stdout,
log_level=settings.logs.pjsip_level if (settings.logs.trace_pjsip or self.logger.pjsip_to_stdout) else 0
)
# initialize pidf
self.pidf = PIDF(entity=self.account.id) # entity will be determined when account is selected
# initialize top level elements
device_id = settings.instance_id
self.service = Service("ID-"+str(uuid.UUID(hex=settings.instance_id)))
self.service.status = Status(basic='open')
self.service.status.extended = 'available'
contact = "sip:%s;gr=%s" % (self.account.id, device_id)
self.service.contact = Contact(contact)
self.service.contact.priority = 0
self.service.device_id = device_id
self.service.device_info = DeviceInfo(device_id)
self.service.timestamp = ServiceTimestamp(datetime.now())
self.pidf.add(self.service)
self.person = Person("ID-"+hashlib.md5(self.account.id.encode()).hexdigest())
self.person.time_offset = TimeOffset()
self.person.timestamp = PersonTimestamp(datetime.now())
self.pidf.add(self.person)
self.device = Device(''.join(chr(random.randint(97, 122)) for i in range(8)))
self.device.device_id = device_id
self.device.notes.add(DMNote('Powered by %s' % settings.user_agent, lang='en'))
self.device.timestamp = DeviceTimestamp(datetime.now())
self.pidf.add(self.device)
# start the interface thread
self.interface.start()
# initialize publication object
self.publication = Publication(FromHeader(self.account.uri, self.account.display_name), "presence", "application/pidf+xml",
credentials=self.account.credentials, duration=self.account.sip.publish_interval)
notification_center.add_observer(self, sender=self.publication)
reactor.callLater(0, self.publish)
# start twisted
try:
reactor.run()
finally:
self.interface.stop()
# stop the output
self.output.stop()
self.output.join()
self.logger.stop()
return 0 if self.success else 1
def stop(self):
self.stopping = True
if self.publication is not None:
self.service.status.basic = 'closed'
self.service.status.extended = 'offline'
self.publish()
else:
engine = Engine()
engine.stop()
def print_help(self):
message = 'Available control keys:\n'
message += ' t: 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 publish(self):
with self.publication_lock:
if self.publishing:
return
settings = SIPSimpleSettings()
self._publication_timeout = time() + 30
if self.account.sip.outbound_proxy is not None:
uri = SIPURI(host=self.account.sip.outbound_proxy.host, port=self.account.sip.outbound_proxy.port, parameters={'transport': self.account.sip.outbound_proxy.transport})
else:
uri = SIPURI(host=self.account.id.domain)
self.lookup.lookup_sip_proxy(uri, settings.sip.transport_list)
self.publishing = True
def unpublish(self):
try:
self.publication.end(timeout=1)
return
except PublicationError:
pass
engine = Engine()
engine.stop()
def handle_notification(self, notification):
handler = getattr(self, '_NH_%s' % notification.name, None)
if handler is not None:
handler(notification)
def _NH_SIPPublicationDidSucceed(self, notification):
with self.publication_lock:
self._publication_routes = None
self._publication_wait = 0.5
self.success = True
self.publishing = False
if self.stopping:
self.unpublish()
return
def _NH_SIPPublicationDidFail(self, notification):
with self.publication_lock:
self.success = False
self.output.put('Publishing failed: %d %s' % (notification.data.code, notification.data.reason))
if notification.data.code in (401, 403, 407):
self.publishing = False
self.stop()
else:
if self.stopping:
self.unpublish()
return
if not self._publication_routes or time() > self._publication_timeout:
self._publication_wait = min(self._publication_wait*2, 30)
timeout = random.uniform(self._publication_wait, 2*self._publication_wait)
reactor.callFromThread(reactor.callLater, timeout, self.publish)
self.publishing = False
else:
route = self._publication_routes.popleft()
self._do_publish(route)
def _NH_SIPPublicationWillExpire(self, notification):
# For now, just re-publish the whole document instead of sending a refresh
self.publish()
def _NH_SIPPublicationDidNotEnd(self, notification):
self.success = False
engine = Engine()
engine.stop()
def _NH_SIPPublicationDidEnd(self, notification):
if notification.data.expired:
self.output.put('Publication expired')
else:
self.output.put('Unpublished')
if self.stopping:
self.success = True
engine = Engine()
engine.stop()
else:
self.publish()
def _NH_DNSLookupDidSucceed(self, notification):
with self.publication_lock:
self._publication_routes = deque(notification.data.result)
route = self._publication_routes.popleft()
self._do_publish(route)
def _NH_DNSLookupDidFail(self, notification):
with self.publication_lock:
self.output.put('DNS lookup failed: %s' % notification.data.error)
timeout = random.uniform(1.0, 2.0)
reactor.callLater(timeout, self.publish)
self.publishing = False
def _NH_SAInputWasReceived(self, notification):
engine = Engine()
settings = SIPSimpleSettings()
key = notification.data.input
if key == 't':
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.' % ('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.' % ('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.' % ('activated' if self.logger.notifications_to_stdout else 'deactivated'))
elif key == '?':
self.print_help()
@run_in_twisted_thread
def _NH_SIPEngineDidEnd(self, notification):
self._stop_reactor()
@run_in_twisted_thread
def _NH_SIPEngineDidFail(self, notification):
self.output.put('Engine failed.')
self._stop_reactor()
def _NH_SIPEngineGotException(self, notification):
self.output.put('An exception occured within the SIP core:\n'+notification.data.traceback)
def _stop_reactor(self):
try:
reactor.stop()
except ReactorNotRunning:
pass
def _do_publish(self, route):
try:
route_header = RouteHeader(route.uri)
self.publication.publish(self.pidf.toxml(), route_header, timeout=5)
except BuilderError as e:
self.output.put("PIDF as currently defined is invalid: %s" % str(e))
self.publishing = False
except PJSIPError as e:
self.output.put("Publish failed: %s" % str(e))
except:
traceback.print_exc()
self.publishing = False
def _output_handler(self, event):
event = event.decode() if isinstance(event, bytes) else event
if isinstance(event, Prompt):
sys.stdout.write(event+'> ')
sys.stdout.flush()
else:
sys.stdout.write(event+'\n')
if __name__ == "__main__":
description = "This script publishes the presence event package to a SIP Presence Agent for the given SIP account, 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 name of the account to use.")
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).")
options, args = parser.parse_args()
try:
application = PublicationApplication(options.account_name, options.trace_sip, options.trace_pjsip, options.trace_notifications)
return_code = application.run()
except RuntimeError as e:
print("Error: %s" % str(e))
sys.exit(1)
except SIPCoreError as e:
print("Error: %s" % str(e))
sys.exit(1)
else:
sys.exit(return_code)

File Metadata

Mime Type
text/x-diff
Expires
Sat, Nov 23, 2:29 AM (12 h, 48 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3408610
Default Alt Text
(35 KB)

Event Timeline