# Copyright (C) 2010 AG Projects. See LICENSE for details. # __all__ = ['Blink'] __version__ = '0.2.0' __date__ = 'October 21st, 2010' import os import sys from collections import defaultdict import cjson from PyQt4.QtCore import QThread from PyQt4.QtGui import QApplication from application import log from application.notification import IObserver, NotificationCenter from application.python.util import Null from application.system import unlink from gnutls.crypto import X509Certificate, X509PrivateKey from gnutls.errors import GNUTLSError from zope.interface import implements from sipsimple.account import Account, AccountManager, BonjourAccount from sipsimple.application import SIPApplication from sipsimple.configuration.backend.file import FileBackend from sipsimple.configuration.settings import SIPSimpleSettings from sipsimple.util import makedirs from blink.configuration.account import AccountExtension, BonjourAccountExtension from blink.configuration.datatypes import InvalidToken from blink.configuration.settings import SIPSimpleSettingsExtension from blink.logging import LogManager from blink.mainwindow import MainWindow from blink.resources import ApplicationData from blink.sessions import SessionManager from blink.update import UpdateManager from blink.util import QSingleton, run_in_gui_thread class AuxiliaryThread(QThread): def __init__(self, parent=None): super(AuxiliaryThread, self).__init__(parent) self.moveToThread(self) def run(self): self.exec_() def customEvent(self, event): handler = getattr(self, '_EH_%s' % event.name, Null) handler(event) def _EH_CallFunctionEvent(self, event): try: event.function(*event.args, **event.kw) except: log.error('Exception occured while calling function %s in the auxiliary thread' % event.function.__name__) log.err() class Blink(QApplication): __metaclass__ = QSingleton implements(IObserver) def __init__(self): super(Blink, self).__init__(sys.argv) self.application = SIPApplication() self.auxiliary_thread = AuxiliaryThread() self.first_run = False self.main_window = MainWindow() self.update_manager = UpdateManager() self.main_window.check_for_updates_action.triggered.connect(self.update_manager.check_for_updates) self.main_window.check_for_updates_action.setVisible(self.update_manager != Null) Account.register_extension(AccountExtension) BonjourAccount.register_extension(BonjourAccountExtension) SIPSimpleSettings.register_extension(SIPSimpleSettingsExtension) session_manager = SessionManager() session_manager.initialize(self.main_window, self.main_window.session_model) def run(self): from blink.util import call_in_gui_thread as call_later call_later(self._initialize_sipsimple) self.auxiliary_thread.start() self.exec_() self.update_manager.shutdown() self.application.stop() self.application.thread.join() log_manager = LogManager() log_manager.stop() def fetch_account(self): filename = os.path.expanduser('~/.blink_account') if not os.path.exists(filename): return try: data = open(filename).read() data = cjson.decode(data.replace(r'\/', '/')) except (OSError, IOError), e: print "Failed to read json data from ~/.blink_account: %s" % e return except cjson.DecodeError, e: print "Failed to decode json data from ~/.blink_account: %s" % e return finally: unlink(filename) data = defaultdict(lambda: None, data) account_id = data['sip_address'] if account_id is None: return account_manager = AccountManager() try: account = account_manager.get_account(account_id) except KeyError: account = Account(account_id) account.display_name = data['display_name'] default_account = account else: default_account = account_manager.default_account account.auth.username = data['auth_username'] account.auth.password = data['password'] or '' account.sip.outbound_proxy = data['outbound_proxy'] account.xcap.xcap_root = data['xcap_root'] account.nat_traversal.msrp_relay = data['msrp_relay'] account.server.settings_url = data['settings_url'] if data['passport'] is not None: try: passport = data['passport'] certificate_path = self.save_certificates(account_id, passport['crt'], passport['key'], passport['ca']) account.tls.certificate = certificate_path except (GNUTLSError, IOError, OSError): pass account.enabled = True account.save() account_manager.default_account = default_account def save_certificates(self, sip_address, crt, key, ca): crt = crt.strip() + os.linesep key = key.strip() + os.linesep ca = ca.strip() + os.linesep X509Certificate(crt) X509PrivateKey(key) X509Certificate(ca) makedirs(ApplicationData.get('tls')) certificate_path = ApplicationData.get(os.path.join('tls', sip_address+'.crt')) file = open(certificate_path, 'w') os.chmod(certificate_path, 0600) file.write(crt+key) file.close() ca_path = ApplicationData.get(os.path.join('tls', 'ca.crt')) try: existing_cas = open(ca_path).read().strip() + os.linesep except: file = open(ca_path, 'w') file.write(ca) file.close() else: if ca not in existing_cas: file = open(ca_path, 'w') file.write(existing_cas+ca) file.close() settings = SIPSimpleSettings() settings.tls.ca_list = ca_path settings.save() return certificate_path def customEvent(self, event): handler = getattr(self, '_EH_%s' % event.name, Null) handler(event) def _EH_CallFunctionEvent(self, event): try: event.function(*event.args, **event.kw) except: log.error('Exception occured while calling function %s in the GUI thread' % event.function.__name__) log.err() def handle_notification(self, notification): handler = getattr(self, '_NH_%s' % notification.name, Null) handler(notification) def _NH_SIPApplicationWillStart(self, notification): log_manager = LogManager() log_manager.start() @run_in_gui_thread def _NH_SIPApplicationDidStart(self, notification): self.fetch_account() self.main_window.show() settings = SIPSimpleSettings() accounts = AccountManager().get_accounts() if not accounts or (self.first_run and accounts==[BonjourAccount()]): self.main_window.add_account_dialog.open_for_create() if settings.google_contacts.authorization_token is InvalidToken: self.main_window.google_contacts_dialog.open_for_incorrect_password() self.update_manager.initialize() def _initialize_sipsimple(self): if not os.path.exists(ApplicationData.get('config')): self.first_run = True notification_center = NotificationCenter() notification_center.add_observer(self, sender=self.application) self.application.start(FileBackend(ApplicationData.get('config')))