def updateBonjourServersPopupButton(self):
     settings = SIPSimpleSettings()
     account = AccountManager().default_account
     if isinstance(account, BonjourAccount):
         self.bonjour_server_combolist.removeAllItems()
         if SIPManager().bonjour_conference_services.servers:
             servers = set()
             servers_dict = {}
             for server in (server for server in SIPManager().bonjour_conference_services.servers if server.uri.transport in settings.sip.transport_list):
                 servers_dict.setdefault("%s@%s" % (server.uri.user, server.uri.host), []).append(server)
             for transport in (transport for transport in ('tls', 'tcp', 'udp') if transport in settings.sip.transport_list):
                 for k, v in servers_dict.items():
                     try:
                         server = next((server for server in v if server.uri.transport == transport))
                     except StopIteration:
                         pass
                     else:
                         servers.add(server)
                         break
             for server in servers:
                 self.bonjour_server_combolist.addItemWithTitle_('%s (%s)' % (server.host, server.uri.host))
                 item = self.bonjour_server_combolist.lastItem()
                 item.setRepresentedObject_(server)
                 self.ok_button.setEnabled_(True)
         else:
             self.bonjour_server_combolist.addItemWithTitle_(NSLocalizedString("No conference server in this neighbourhood", "Menu item"))
             self.bonjour_server_combolist.lastItem().setEnabled_(False)
             self.ok_button.setEnabled_(False)
     else:
         self.ok_button.setEnabled_(False)
    def init(self):
        if self:
            NSBundle.loadNibNamed_owner_("EnrollmentWindow", self)
            icloud_sync_enabled = NSUserDefaults.standardUserDefaults().stringForKey_("iCloudSyncEnabled")
            self.syncWithiCloudCheckbox.setHidden_(not icloud_sync_enabled)

            self.selectRadio_(self.radioMatrix)
            if not SIPManager().validateAddAccountAction():
                self.nextButton.setEnabled_(False)
                self.purchaseProLabel.setHidden_(False)

            if NSApp.delegate().contactsWindowController.first_run:
                NotificationCenter().add_observer(self, name='SIPAccountManagerDidAddAccount')

            if NSApp.delegate().allowed_domains:
                self.allowed_domains = NSApp.delegate().allowed_domains
                self.syncWithiCloudCheckbox.setHidden_(True)
                self.syncWithiCloudCheckbox.setState_(NSOffState)
                self.domainButton.setHidden_(True)
                self.addressText.cell().setPlaceholderString_('user@' + self.allowed_domains[0])

            if not NSApp.delegate().icloud_enabled:
                self.syncWithiCloudCheckbox.setHidden_(True)

        return self
Exemple #3
0
 def muteBeforeSpeechWillStart(self):
     hasAudio = any(sess.hasStreamOfType("audio") for sess in self.sessionControllersManager.sessionControllers)
     if hasAudio:
         if not SIPManager().is_muted():
             NSApp.delegate().contactsWindowController.muteClicked_(None)
             self.muted_by_synthesizer = True
     if self.speech_recognizer:
         self.speech_recognizer.stopListening()
    def init(self):
        if self:
            NSBundle.loadNibNamed_owner_("EnrollmentWindow", self)
            self.selectRadio_(self.radioMatrix)
            if not SIPManager().validateAddAccountAction():
                self.nextButton.setEnabled_(False)
                self.purchaseProLabel.setHidden_(False)

            if NSApp.delegate().contactsWindowController.first_run:
                NotificationCenter().add_observer(
                    self, name='SIPAccountManagerDidAddAccount')

        return self
    def applicationDidFinishLaunching_(self, sender):
        self.blinkMenu.setTitle_(self.applicationNamePrint)

        config_file = ApplicationData.get('config')
        self.icloud_manager = iCloudManager()
        self.backend = SIPManager()

        self.contactsWindowController.setup(self.backend)

        while True:
            try:
                first_run = not os.path.exists(config_file)
                self.contactsWindowController.first_run = first_run

                self.backend.init()
                self.backend.fetch_account()
                accounts = AccountManager().get_accounts()
                if not accounts or (first_run and accounts == [BonjourAccount()]):
                    self.wait_for_enrollment = True
                    self.enroll()
                break

            except FileParserError, exc:
                BlinkLogger().log_warning(u"Error parsing configuration file: %s" % exc)
                if NSRunAlertPanel(NSLocalizedString("Error", "Window title"),
                    NSLocalizedString("The configuration file is corrupted. You will need to replace it and re-enter your account information. \n\nYour current configuration file will be backed up to %s.corrupted. ", "Label") % config_file,
                    NSLocalizedString("Replace", "Button title"), NSLocalizedString("Quit", "Button title"), None) != NSAlertDefaultReturn:
                    NSApp.terminate_(None)
                    return
                os.rename(config_file, config_file+".corrupted")
                BlinkLogger().log_info(u"Renamed configuration file to %s" % config_file+".corrupted")
            except BaseException, exc:
                import traceback
                print traceback.print_exc()
                NSRunAlertPanel(NSLocalizedString("Error", "Window title"), NSLocalizedString("There was an error during startup of core functionality:\n%s", "Label") % exc,
                        NSLocalizedString("Quit", "Button title"), None, None)
                NSApp.terminate_(None)
                return
    def _addIncomingSession(self, session, streams, is_update_proposal):
        view = self.getItemView()
        self.sessions[session] = view
        settings = SIPSimpleSettings()
        stream_type_list = list(set(stream.type for stream in streams))

        if len(self.sessions) == 1:
            if "screen-sharing" in stream_type_list:
                base_text = NSLocalizedString("Screen Sharing from %s", "Label")
            elif "video" in stream_type_list:
                base_text = NSLocalizedString("Video call from %s", "Label")
            elif "audio" in stream_type_list:
                base_text = NSLocalizedString("Audio call from %s", "Label")
            elif stream_type_list == ["file-transfer"]:
                base_text = NSLocalizedString("File transfer from %s", "Label")
            elif stream_type_list == ["chat"]:
                base_text = NSLocalizedString("Chat from %s", "Label")
            else:
                base_text = NSLocalizedString("Call from %s", "Label")

            title = base_text % format_identity_to_string(session.remote_identity, check_contact=True, format='compact')
            self.panel.setTitle_(title)

            if settings.sounds.enable_speech_synthesizer:
                self.speak_text = title
                self.startSpeechSynthesizerTimer()
        else:
            self.panel.setTitle_(NSLocalizedString("Multiple Incoming Calls", "Label"))

        NotificationCenter().add_observer(self, sender=session)

        subjectLabel     = view.viewWithTag_(1)
        fromLabel        = view.viewWithTag_(2)
        accountLabel     = view.viewWithTag_(3)
        acceptButton     = view.viewWithTag_(5)
        rejectButton     = view.viewWithTag_(7)
        accepyOnlyButton = view.viewWithTag_(6)
        busyButton       = view.viewWithTag_(8)
        callerIcon       = view.viewWithTag_(99)
        chatIcon         = view.viewWithTag_(31)
        audioIcon        = view.viewWithTag_(32)
        fileIcon         = view.viewWithTag_(33)
        screenIcon       = view.viewWithTag_(34)
        videoIcon        = view.viewWithTag_(35)

        stream_types = [s.type for s in streams]

        session_manager = SessionManager()
        have_audio_call = any(s for s in session_manager.sessions if s is not session and s.streams and 'audio' in (stream.type for stream in s.streams))
        if not have_audio_call:
            self.startSpeechRecognition()

        typeCount = 0
        if 'audio' in stream_types:
            frame = audioIcon.frame()
            typeCount+= 1
            frame.origin.x = NSMaxX(view.frame()) - 10 - (NSWidth(frame) + 10) * typeCount
            audioIcon.setFrame_(frame)
            audioIcon.setHidden_(False)

            if not is_update_proposal:
                frame = view.frame()
                frame.size.height += 20 # give extra space for the counter label
                view.setFrame_(frame)
                if session.account.audio.auto_accept:
                    have_audio_call = any(s for s in session_manager.sessions if s is not session and s.streams and 'audio' in (stream.type for stream in s.streams))
                    if not have_audio_call:
                        self.enableAutoAnswer(view, session, session.account.audio.answer_delay)
                elif settings.answering_machine.enabled or (is_anonymous(session.remote_identity.uri) and session.account.pstn.anonymous_to_answering_machine):
                    self.enableAnsweringMachine(view, session)

        if 'chat' in stream_types:
            frame = chatIcon.frame()
            typeCount+= 1
            frame.origin.x = NSMaxX(view.frame()) - 10 - (NSWidth(frame) + 10) * typeCount
            chatIcon.setFrame_(frame)
            chatIcon.setHidden_(False)

        if 'screen-sharing' in stream_types:
            frame = screenIcon.frame()
            typeCount+= 1
            frame.origin.x = NSMaxX(view.frame()) - 10 - (NSWidth(frame) + 10) * typeCount
            screenIcon.setFrame_(frame)
            screenIcon.setHidden_(False)

        if 'video' in stream_types:
            #have_video_call = any(s for s in session_manager.sessions if s is not session and s.streams and 'video' in (stream.type for stream in s.streams))
            #if not have_video_call:
            #    NSApp.delegate().contactsWindowController.showLocalVideoWindow()

            frame = videoIcon.frame()
            typeCount+= 1
            frame.origin.x = NSMaxX(view.frame()) - 10 - (NSWidth(frame) + 10) * typeCount
            videoIcon.setFrame_(frame)
            videoIcon.setHidden_(False)

        is_file_transfer = False
        if 'file-transfer' in stream_types:
            is_file_transfer = True
            frame = fileIcon.frame()
            typeCount+= 1
            frame.origin.x = NSMaxX(view.frame()) - 10 - (NSWidth(frame) + 10) * typeCount
            fileIcon.setFrame_(frame)
            fileIcon.setHidden_(False)
            if settings.file_transfer.auto_accept and NSApp.delegate().contactsWindowController.my_device_is_active:
                BlinkLogger().log_info(u"Auto answer enabled for file transfers from known contacts")
                self.enableAutoAnswer(view, session, random.uniform(10, 20))

        self.sessionsListView.addSubview_(view)
        frame = self.sessionsListView.frame()
        frame.origin.y = self.extraHeight - 14
        frame.size.height = self.sessionsListView.minimumHeight()
        self.sessionsListView.setFrame_(frame)
        height = frame.size.height + self.extraHeight
        size = NSMakeSize(NSWidth(self.panel.frame()), height)

        screenSize = NSScreen.mainScreen().frame().size
        if size.height > (screenSize.height * 2) / 3:
            size.height = (screenSize.height * 2) / 3

        frame = self.panel.frame()
        frame.size.height = size.height
        frame.size.height = NSHeight(self.panel.frameRectForContentRect_(frame))
        self.panel.setFrame_display_animate_(frame, True, True)
        self.sessionsListView.relayout()

        acceptButton.cell().setRepresentedObject_(NSNumber.numberWithInt_(0))
        rejectButton.cell().setRepresentedObject_(NSNumber.numberWithInt_(2))
        busyButton.cell().setRepresentedObject_(NSNumber.numberWithInt_(3))

        # no Busy or partial accept option for Stream Update Proposals
        busyButton.setHidden_(is_update_proposal or is_file_transfer)
        accepyOnlyButton.setHidden_(is_update_proposal)
        if is_file_transfer:
            busyButton.setAttributedTitle_("")

        if is_update_proposal:
            subject, only_button_title, only_button_object = self.format_subject_for_incoming_reinvite(session, streams)
            only_button_title = ""
        else:
            subject, only_button_title, only_button_object = self.format_subject_for_incoming_invite(session, streams)
        subjectLabel.setStringValue_(subject)
        accepyOnlyButton.cell().setRepresentedObject_(NSNumber.numberWithInt_(only_button_object))
        frame = subjectLabel.frame()
        frame.size.width = NSWidth(self.sessionsListView.frame()) - 80 - 40 * typeCount
        subjectLabel.setFrame_(frame)

        has_audio_streams = any(s for s in reduce(lambda a,b:a+b, [session.proposed_streams for session in self.sessions.keys()], []) if s.type=="audio")
        caller_contact = NSApp.delegate().contactsWindowController.getFirstContactMatchingURI(session.remote_identity.uri)
        if caller_contact:
            if caller_contact.icon:
                callerIcon.setImage_(caller_contact.icon)

            if not is_update_proposal and caller_contact.auto_answer and NSApp.delegate().contactsWindowController.my_device_is_active:
                if has_audio_streams:
                    if not NSApp.delegate().contactsWindowController.has_audio:
                        BlinkLogger().log_info(u"Auto answer enabled for this contact")
                        video_requested = any(s for s in session.blink_supported_streams if s.type == "video")
                        if video_requested and not settings.video.enable_when_auto_answer:
                            blink_supported_streams = [s for s in session.blink_supported_streams if s.type != "video"]
                            session.blink_supported_streams = blink_supported_streams
                        self.enableAutoAnswer(view, session, session.account.audio.answer_delay)
                else:
                    video_requested = any(s for s in session.blink_supported_streams if s.type == "video")
                    if video_requested and not settings.video.enable_when_auto_answer:
                        blink_supported_streams = [s for s in session.blink_supported_streams if s.type != "video"]
                        session.blink_supported_streams = blink_supported_streams
                    BlinkLogger().log_info(u"Auto answer enabled for this contact")
                    self.enableAutoAnswer(view, session, session.account.audio.answer_delay)

        fromLabel.setStringValue_(u"%s" % format_identity_to_string(session.remote_identity, check_contact=True, format='full'))
        fromLabel.sizeToFit()

        if has_audio_streams:
            outdev = settings.audio.output_device
            indev = settings.audio.input_device

            if outdev == u"system_default":
                outdev = SIPManager()._app.engine.default_output_device
            if indev == u"system_default":
                indev = SIPManager()._app.engine.default_input_device

            outdev = outdev.strip() if outdev is not None else 'None'
            indev = indev.strip() if indev is not None else 'None'

            if outdev != indev:
                if indev.startswith('Built-in Mic') and outdev.startswith(u'Built-in Out'):
                    self.deviceLabel.setStringValue_(NSLocalizedString("Using Built-in Microphone and Output", "Label"))
                else:
                    self.deviceLabel.setStringValue_(NSLocalizedString("Using %s for output ", "Label") % outdev.strip() + NSLocalizedString(" and %s for input", "Label") % indev.strip())
            else:
                self.deviceLabel.setStringValue_(NSLocalizedString("Using audio device", "Label") + " " + outdev.strip())

            BlinkLogger().log_info(u"Using input/output audio devices: %s/%s" % (indev.strip(), outdev.strip()))

            self.deviceLabel.sizeToFit()
            self.deviceLabel.setHidden_(False)
        else:
            self.deviceLabel.setHidden_(True)

        acceptButton.setTitle_(NSLocalizedString("Accept", "Button title"))
        accepyOnlyButton.setTitle_(only_button_title or "")

        if False and sum(a.enabled for a in AccountManager().iter_accounts())==1:
            accountLabel.setHidden_(True)
        else:
            accountLabel.setHidden_(False)
            if isinstance(session.account, BonjourAccount):
                accountLabel.setStringValue_(NSLocalizedString("To Bonjour account", "Label"))
            else:
                to = format_identity_to_string(session.account)
                accountLabel.setStringValue_(NSLocalizedString("To %s", "Label") % to)
            accountLabel.sizeToFit()

        if len(self.sessions) == 1:
            self.acceptAllButton.setTitle_(NSLocalizedString("Accept", "Button title"))
            self.acceptAllButton.setHidden_(False)
            self.acceptButton.setTitle_(only_button_title or "")
            self.acceptButton.setHidden_(not only_button_title)
            self.rejectButton.setTitle_(NSLocalizedString("Reject", "Button title"))

            self.acceptAllButton.cell().setRepresentedObject_(NSNumber.numberWithInt_(0))
            self.rejectButton.cell().setRepresentedObject_(NSNumber.numberWithInt_(2))
            self.busyButton.cell().setRepresentedObject_(NSNumber.numberWithInt_(3))
            self.acceptButton.cell().setRepresentedObject_(NSNumber.numberWithInt_(only_button_object))
            self.answeringMachineButton.cell().setRepresentedObject_(NSNumber.numberWithInt_(4))
            self.conferenceButton.cell().setRepresentedObject_(NSNumber.numberWithInt_(5))

            self.busyButton.setHidden_(is_update_proposal or is_file_transfer)

            for i in (5, 6, 7, 8):
                view.viewWithTag_(i).setHidden_(True)

        else:
            self.acceptAllButton.setHidden_(False)
            self.acceptAllButton.setTitle_(NSLocalizedString("Accept All", "Button title"))
            self.acceptButton.setHidden_(True)
            self.busyButton.setHidden_(is_update_proposal or is_file_transfer)
            self.rejectButton.setTitle_(NSLocalizedString("Reject All", "Button title"))

            for v in self.sessions.values():
                for i in (5, 6, 7, 8):
                    btn = v.viewWithTag_(i)
                    btn.setHidden_(len(btn.attributedTitle()) == 0)

        if not has_audio_streams or is_update_proposal:
            self.answeringMachineButton.setHidden_(True)
        else:
            self.answeringMachineButton.setHidden_(not settings.answering_machine.show_in_alert_panel)

        if not self.isConferencing:
            self.conferenceButton.setHidden_(True)
        else:
            self.conferenceButton.setHidden_(False)
class BlinkAppDelegate(NSObject):
    """Responsible for starting and stopping the application
       Register URL types handled by Blink
       Updating the dock icon with missed calls
       Migrating data from one version to another
       Start enrollment if run first time
       Calling Initial SIP URL if necessary
       Handle wake up from sleep
       Show about panel"""

    contactsWindowController = objc.IBOutlet()
    chatWindowController = objc.IBOutlet()
    debugWindow = objc.IBOutlet()
    aboutPanel = objc.IBOutlet()
    migrationPanel = objc.IBOutlet()
    migrationText = objc.IBOutlet()
    migrationProgressWheel = objc.IBOutlet()
    aboutVersion = objc.IBOutlet()
    aboutSlogan = objc.IBOutlet()
    aboutCopyright = objc.IBOutlet()
    aboutIcon = objc.IBOutlet()
    aboutzRTPIcon = objc.IBOutlet()
    ui_notification_center = None
    application_will_end = False
    wake_up_timestamp = None
    ip_change_timestamp = None
    transport_lost_timestamp = None

    debug = False

    blinkMenu = objc.IBOutlet()
    ready = False
    missedCalls = 0
    missedChats = 0
    urisToOpen = []
    wait_for_enrollment = False
    updater = None

    # branding
    about_version = "1.0"
    about_slogan = "A state of the art, easy to use SIP client"
    help_url = "http://help-pro.icanblink.com"
    last_history_entries = 10
    allowed_domains = []
    icloud_enabled = False
    answering_machine_enabled = True
    history_enabled = True
    recording_enabled = True
    file_logging_enabled = True
    advanced_options_enabled = True
    hidden_account_preferences_sections = ()
    chat_replication_password_hidden = True
    external_alert_enabled = True
    migrate_passwords_to_keychain = True
    service_provider_help_url  = None
    service_provider_name = None
    maximum_accounts = None
    account_extension = None
    sp_update_url = None
    main_window_title = None
    call_transfer_enabled = True
    phone_numbers_enabled = True
    ldap_directory_enabled = True
    chat_print_enabled = True
    pause_music_enabled = True
    about_image = 'about'
    account_extension = None
    general_extension = None

    supported_languages = {
                           "system_default": NSLocalizedString("System Default", "Menu item"),
                           "en": NSLocalizedString("English", "Menu item"),
                           "nl": NSLocalizedString("Nederlands", "Menu item"),
                           "es": NSLocalizedString("Spanish", "Menu item"),
                           "ro": NSLocalizedString("Romanian", "Menu item"),
                           "pt": NSLocalizedString("Portuguese", "Menu item")
                           }

    statusbar_menu_icon = 'invisible'
    about_copyright = "Copyright 2009-2021 AG Projects"
    active_transports = set()
    terminating = False

    @objc.python_method
    @property
    def video_devices(self):
        devices = set()
        for item in Engine().video_devices:
            if 'colorbar' in item.lower():
                continue
            if 'null' in item.lower():
                continue

            devices.add(item)
        return list(devices)
    
    def init(self):
        self = objc.super(BlinkAppDelegate, self).init()
        if self:
            self.applicationName = str(NSBundle.mainBundle().infoDictionary().objectForKey_("CFBundleExecutable"))
            self.applicationNamePrint = str(NSBundle.mainBundle().infoDictionary().objectForKey_("CFBundleName"))
            build = str(NSBundle.mainBundle().infoDictionary().objectForKey_("CFBundleVersion"))
            date = str(NSBundle.mainBundle().infoDictionary().objectForKey_("BlinkVersionDate"))

            branding_file = NSBundle.mainBundle().infoDictionary().objectForKey_("BrandingFile")

            try:
                branding = __import__(branding_file)
            except ImportError:
                try:
                    import branding
                except ImportError:
                    branding = Null

            branding.init(self)

            BlinkLogger().log_info("Starting %s %s" % (self.applicationNamePrint, build))

            self.registerURLHandler()
            NSWorkspace.sharedWorkspace().notificationCenter().addObserver_selector_name_object_(self, "computerDidWake:", NSWorkspaceDidWakeNotification, None)
            NSWorkspace.sharedWorkspace().notificationCenter().addObserver_selector_name_object_(self, "computerWillSleep:", NSWorkspaceWillSleepNotification, None)
            NSDistributedNotificationCenter.defaultCenter().addObserver_selector_name_object_suspensionBehavior_(self, "callFromAddressBook:", "CallTelephoneNumberWithBlinkFromAddressBookNotification", "AddressBook", NSNotificationSuspensionBehaviorDeliverImmediately)
            NSDistributedNotificationCenter.defaultCenter().addObserver_selector_name_object_suspensionBehavior_(self, "callFromAddressBook:", "CallSipAddressWithBlinkFromAddressBookNotification", "AddressBook", NSNotificationSuspensionBehaviorDeliverImmediately)

            NotificationCenter().add_observer(self, name="CFGSettingsObjectDidChange")
            NotificationCenter().add_observer(self, name="SIPApplicationDidStart")
            NotificationCenter().add_observer(self, name="SIPApplicationWillEnd")
            NotificationCenter().add_observer(self, name="SIPApplicationDidEnd")
            NotificationCenter().add_observer(self, name="NetworkConditionsDidChange")
            NotificationCenter().add_observer(self, name="SIPEngineTransportDidDisconnect")
            NotificationCenter().add_observer(self, name="SIPEngineTransportDidConnect")
            NotificationCenter().add_observer(self, name="DNSNameserversDidChange")
            NotificationCenter().add_observer(self, name="SystemDidWakeUpFromSleep")

            # remove obsolete settings
            userdef = NSUserDefaults.standardUserDefaults()
            userdef.removeObjectForKey_('SIPTrace')
            userdef.removeObjectForKey_('MSRPTrace')
            userdef.removeObjectForKey_('XCAPTrace')
            userdef.removeObjectForKey_('EnablePJSIPTrace')
            userdef.removeObjectForKey_('EnableNotificationsTrace')

            try:
                from Updater import Updater
            except ImportError:
                pass
            else:
                self.updater = Updater()

            self.purge_temporary_files()

        return self

    @objc.python_method
    @run_in_thread('file-io')
    def purge_temporary_files(self):
        for dir in ('.tmp_screenshots', '.tmp_snapshots', '.tmp_file_transfers'):
            folder = ApplicationData.get(dir)
            if os.path.exists(folder):
                try:
                    shutil.rmtree(folder)
                except EnvironmentError:
                    pass

    @objc.python_method
    def gui_notify(self, title, body, subtitle=None):
        if self.application_will_end:
            return
        major, minor = platform.mac_ver()[0].split('.')[0:2]
        if (int(major) == 10 and int(minor) >= 8) or int(major) > 10:
            if self.ui_notification_center is None:
                self.ui_notification_center = Foundation.NSUserNotificationCenter.defaultUserNotificationCenter()
                self.ui_notification_center.setDelegate_(self)

            notification = Foundation.NSUserNotification.alloc().init()
            notification.setTitle_(title)
            if subtitle is not None:
                notification.setSubtitle_(subtitle)
            notification.setInformativeText_(body)
            self.ui_notification_center.scheduleNotification_(notification)

    def userNotificationCenter_didDeliverNotification_(self, center, notification):
        pass

    def userNotificationCenter_didActivateNotification_(self, center, notification):
        pass

    def userNotificationCenter_shouldPresentNotification_(self, center, notification):
        return True

    # Needed by run_in_gui_thread and call_in_gui_thread
    def callObject_(self, callable):
        try:
            callable()
        except:
            log.err()

    # Needed by call_later
    def callTimerObject_(self, timer):
        callable = timer.userInfo()
        try:
            callable()
        except:
            log.err()

    @objc.python_method
    def enroll(self):
        enroll = EnrollmentController.alloc().init()
        enroll.setCreateAccount()
        enroll.runModal()

    @objc.python_method
    def updateDockTile(self):
        if self.missedCalls > 0 or self.missedChats > 0:
            icon = NSImage.imageNamed_("Blink")
            image = NSImageView.alloc().initWithFrame_(NSMakeRect(0, 0, 32, 32))
            image.setImage_(icon)
            if self.missedCalls > 0 and self.missedChats > 0:
                NSApp.dockTile().setBadgeLabel_("%i / %i" % (self.missedCalls, self.missedChats))
            else:
                NSApp.dockTile().setBadgeLabel_("%i" % (self.missedCalls + self.missedChats))
            NSApp.dockTile().setContentView_(image)
        else:
            NSApp.dockTile().setBadgeLabel_("")
            NSApp.dockTile().setContentView_(None)
            icon = None
        NSApp.setApplicationIconImage_(icon)
        NSApp.dockTile().display()

    @objc.python_method
    def noteNewMessage(self, window):
        if not NSApp.isActive():
            self.missedChats += 1
            self.updateDockTile()

    @objc.python_method
    def noteMissedCall(self):
        self.missedCalls += 1
        self.updateDockTile()
        NSApp.requestUserAttention_(NSInformationalRequest)

    def applicationShouldHandleReopen_hasVisibleWindows_(self, sender, flag):
        if not flag:
            self.contactsWindowController.showWindow_(None)
        self.missedCalls = 0
        self.missedChats = 0
        self.updateDockTile()
        return False

    def applicationDidBecomeActive_(self, notif):
        self.missedCalls = 0
        self.missedChats = 0
        self.updateDockTile()

    def applicationDidFinishLaunching_(self, sender):
        BlinkLogger().log_debug("Application launched")

        branding_file = NSBundle.mainBundle().infoDictionary().objectForKey_("BrandingFile")
        try:
            branding = __import__(branding_file)
        except ImportError:
            try:
                import branding
            except ImportError:
                branding = Null

        branding.setup(self)

        if self.updater and self.sp_update_url is not None:
            self.updater.sp.setFeedURL_(NSURL.URLWithString_(self.sp_update_url))

        self.blinkMenu.setTitle_(self.applicationNamePrint)

        config_file = ApplicationData.get('config')
        self.icloud_manager = iCloudManager()
        self.backend = SIPManager()

        self.contactsWindowController.setup(self.backend)

        while True:
            try:
                first_run = not os.path.exists(config_file)
                self.contactsWindowController.first_run = first_run

                self.backend.init()
                self.backend.fetch_account()
                accounts = AccountManager().get_accounts()
                if not accounts or (first_run and accounts == [BonjourAccount()]):
                    self.wait_for_enrollment = True
                    self.enroll()
                break

            except FileParserError as exc:
                BlinkLogger().log_warning("Error parsing configuration file: %s" % exc)
                if NSRunAlertPanel(NSLocalizedString("Error", "Window title"),
                    NSLocalizedString("The configuration file is corrupted. You will need to replace it and re-enter your account information. \n\nYour current configuration file will be backed up to %s.corrupted. ", "Label") % config_file,
                    NSLocalizedString("Replace", "Button title"), NSLocalizedString("Quit", "Button title"), None) != NSAlertDefaultReturn:
                    NSApp.terminate_(None)
                    return
                os.rename(config_file, config_file+".corrupted")
                BlinkLogger().log_info("Renamed configuration file to %s" % config_file+".corrupted")
            except BaseException as exc:
                import traceback
                print(traceback.print_exc())
                NSRunAlertPanel(NSLocalizedString("Error", "Window title"), NSLocalizedString("There was an error during startup of core functionality:\n%s", "Label") % exc,
                        NSLocalizedString("Quit", "Button title"), None, None)
                NSApp.terminate_(None)
                return

        # window should be shown only after enrollment check
        if self.wait_for_enrollment:
            BlinkLogger().log_info('Starting User Interface')
            self.contactsWindowController.model.moveBonjourGroupFirst()
            self.contactsWindowController.showWindow_(None)
            self.wait_for_enrollment = False

        self.contactsWindowController.setupFinished()
        SMSWindowManager.SMSWindowManager().setOwner_(self.contactsWindowController)
        self.debugWindow = DebugWindow.alloc().init()
        self.chatWindowController = ChatWindowController.ChatWindowController.alloc().init()

    def killSelfAfterTimeout_(self, arg):
        time.sleep(15)
        BlinkLogger().log_info("Application forcefully terminated because core engine did not be stop in a timely manner")
        os._exit(0)

    def applicationShouldTerminate_(self, sender):
        if self.terminating:
            return True

        self.terminating = True
        BlinkLogger().log_info('Application will be terminated')
        NSThread.detachNewThreadSelector_toTarget_withObject_("killSelfAfterTimeout:", self, None)
        NotificationCenter().post_notification("BlinkShouldTerminate", None)
        NotificationCenter().add_observer(self, name="SIPApplicationDidEnd")
        app = SIPApplication()
        app.stop()

        import Profiler
        Profiler.stop(os.path.join(ApplicationData.directory, 'logs', 'profiler.stats'))
        return False

    @objc.python_method
    @run_in_gui_thread
    def handle_notification(self, notification):
        handler = getattr(self, '_NH_%s' % notification.name, Null)
        handler(notification)

    @objc.python_method
    def _NH_SIPEngineTransportDidDisconnect(self, notification):
        self.transport_lost_timestamp = int(time.time())

        transport = '%s:%s' % (notification.data.transport, notification.data.remote_address)
        try:
            self.active_transports.remove(transport)
        except KeyError:
            return

        for account_info in self.contactsWindowController.accounts:
            account = account_info.account

            if account is BonjourAccount():
                continue

            if not account.enabled:
                continue

            if account_info.registrar != transport:
                continue

            account_info.register_state = 'failed'

            if host is None or host.default_ip is None:
                account_info.register_failure_reason = NSLocalizedString("No Internet connection", "Label")
            else:
                account_info.register_failure_reason = NSLocalizedString("Connection failed", "Label")

            self.contactsWindowController.refreshAccountList()
            BlinkLogger().log_info('Re-register account %s' % account.id)

            account.reregister()
            account.resubscribe()
            presence_state = account.presence_state
            account.presence_state = None
            account.presence_state = presence_state

        if notification.data.reason != 'Success':
            BlinkLogger().log_info("%s connection %s <-> %s lost" % (notification.data.transport, notification.data.local_address, notification.data.remote_address))
            #nc_title = NSLocalizedString("Connection failed", "Label")
            #nc_body = NSLocalizedString("Remote Address", "Label") + " %s:%s" % (notification.data.transport, notification.data.remote_address)
            #self.gui_notify(nc_title, nc_body)

        else:
            NotificationCenter().post_notification("BlinkTransportFailed", data=NotificationData(transport=transport))

    @objc.python_method
    def _NH_SIPEngineTransportDidConnect(self, notification):
        transport = "%s:%s" %(notification.data.transport, notification.data.remote_address)
        if transport not in self.active_transports:
            BlinkLogger().log_info("%s connection %s <-> %s established" % (notification.data.transport, notification.data.local_address, notification.data.remote_address))
            self.active_transports.add(transport)

    @objc.python_method
    def _NH_DNSNameserversDidChange(self, notification):
        BlinkLogger().log_info("DNS servers changed to %s" % ", ".join(notification.data.nameservers))

    @objc.python_method
    def _NH_NetworkConditionsDidChange(self, notification):
        self.ip_change_timestamp = int(time.time())
        BlinkLogger().log_info("Network conditions changed")
        if host.default_ip is None:
            BlinkLogger().log_info("No IP address")
        else:
            BlinkLogger().log_info("IP address changed to %s" % host.default_ip)

    @objc.python_method
    def _NH_SIPApplicationWillEnd(self, notification):
        BlinkLogger().log_info("Core engine will be stopped")
        self.purge_temporary_files()

    @objc.python_method
    def _NH_CFGSettingsObjectDidChange(self, notification):
        if 'gui.extended_debug' in notification.data.modified:
            settings = SIPSimpleSettings()
            self.debug = settings.gui.extended_debug

    @objc.python_method
    def _NH_SIPApplicationDidStart(self, notification):
        settings = SIPSimpleSettings()
        self.debug = settings.gui.extended_debug
        self.purge_temporary_files()

    @objc.python_method
    def _NH_SIPApplicationDidEnd(self, notification):
        BlinkLogger().log_info("Core engine stopped")
        NSApp.terminate_(self)

    def applicationWillTerminate_(self, notification):
        NotificationCenter().post_notification("BlinkWillTerminate", None)
        BlinkLogger().log_info("Application ended")

    def computerDidWake_(self, notification):
        self.wake_up_timestamp = int(time.time())
        NotificationCenter().post_notification("SystemDidWakeUpFromSleep", None)

    def computerWillSleep_(self, notification):
        NotificationCenter().post_notification("SystemWillSleep", None)

    def callFromAddressBook_(self, notification):
        url = notification.userInfo()["URI"]
        name = notification.userInfo()["DisplayName"]
        url = self.normalizeExternalURL(url)

        BlinkLogger().log_info("Will start outgoing session to %s %s from Address Book" % (name, url))
        if not self.ready:
            self.urisToOpen.append((str(url), ('audio'), list()))
        else:
            self.contactsWindowController.joinConference(str(url), ('audio'))

    @objc.IBAction
    def orderFrontAboutPanel_(self, sender):
        if not self.aboutPanel:
            NSBundle.loadNibNamed_owner_("About", self)
            self.aboutVersion.setStringValue_(self.about_version)
            self.aboutSlogan.setStringValue_(self.about_slogan)
            self.aboutIcon.setImage_(NSImage.imageNamed_(self.about_image))
            self.aboutCopyright.setStringValue_(self.about_copyright)

        self.aboutPanel.makeKeyAndOrderFront_(None)

    @objc.python_method
    def normalizeExternalURL(self, url):
        return external_url_pattern.sub("", url)

    def getURL_withReplyEvent_(self, event, replyEvent):
        participants = set()
        media_type = set()
        url = event.descriptorForKeyword_(fourcharToInt('----')).stringValue()
        url = self.normalizeExternalURL(url)

        BlinkLogger().log_info("Will start outgoing session from external link: %s" % url)

        url = urllib.parse.unquote(url).replace(" ", "")
        _split = url.split(';')
        _url = []
        for item in _split[:]:
            if item.startswith("participant="):
                puri = item.split("=")[1]
                participants.add(puri)
            elif item.startswith("media_type="):
                m = item.split("=")[1]
                media_type.add(m)
            else:
                _url.append(item)
                _split.remove(item)

        url = ";".join(_url)

        if not self.ready:
            self.urisToOpen.append((str(url), list(media_type), list(participants)))
        else:
            self.contactsWindowController.joinConference(str(url), list(media_type), list(participants))

    @objc.python_method
    def registerURLHandler(self):
        event_class = event_id = fourcharToInt("GURL")
        event_manager = NSAppleEventManager.sharedAppleEventManager()
        event_manager.setEventHandler_andSelector_forEventClass_andEventID_(self, "getURL:withReplyEvent:", event_class, event_id)

        bundleID = NSBundle.mainBundle().bundleIdentifier()
        LaunchServices.LSSetDefaultHandlerForURLScheme("sip", bundleID)
        LaunchServices.LSSetDefaultHandlerForURLScheme("tel", bundleID)
    def applicationDidFinishLaunching_(self, sender):
        BlinkLogger().log_debug("Application launched")

        branding_file = NSBundle.mainBundle().infoDictionary().objectForKey_("BrandingFile")
        try:
            branding = __import__(branding_file)
        except ImportError:
            try:
                import branding
            except ImportError:
                branding = Null

        branding.setup(self)

        if self.updater and self.sp_update_url is not None:
            self.updater.sp.setFeedURL_(NSURL.URLWithString_(self.sp_update_url))

        self.blinkMenu.setTitle_(self.applicationNamePrint)

        config_file = ApplicationData.get('config')
        self.icloud_manager = iCloudManager()
        self.backend = SIPManager()

        self.contactsWindowController.setup(self.backend)

        while True:
            try:
                first_run = not os.path.exists(config_file)
                self.contactsWindowController.first_run = first_run

                self.backend.init()
                self.backend.fetch_account()
                accounts = AccountManager().get_accounts()
                if not accounts or (first_run and accounts == [BonjourAccount()]):
                    self.wait_for_enrollment = True
                    self.enroll()
                break

            except FileParserError as exc:
                BlinkLogger().log_warning("Error parsing configuration file: %s" % exc)
                if NSRunAlertPanel(NSLocalizedString("Error", "Window title"),
                    NSLocalizedString("The configuration file is corrupted. You will need to replace it and re-enter your account information. \n\nYour current configuration file will be backed up to %s.corrupted. ", "Label") % config_file,
                    NSLocalizedString("Replace", "Button title"), NSLocalizedString("Quit", "Button title"), None) != NSAlertDefaultReturn:
                    NSApp.terminate_(None)
                    return
                os.rename(config_file, config_file+".corrupted")
                BlinkLogger().log_info("Renamed configuration file to %s" % config_file+".corrupted")
            except BaseException as exc:
                import traceback
                print(traceback.print_exc())
                NSRunAlertPanel(NSLocalizedString("Error", "Window title"), NSLocalizedString("There was an error during startup of core functionality:\n%s", "Label") % exc,
                        NSLocalizedString("Quit", "Button title"), None, None)
                NSApp.terminate_(None)
                return

        # window should be shown only after enrollment check
        if self.wait_for_enrollment:
            BlinkLogger().log_info('Starting User Interface')
            self.contactsWindowController.model.moveBonjourGroupFirst()
            self.contactsWindowController.showWindow_(None)
            self.wait_for_enrollment = False

        self.contactsWindowController.setupFinished()
        SMSWindowManager.SMSWindowManager().setOwner_(self.contactsWindowController)
        self.debugWindow = DebugWindow.alloc().init()
        self.chatWindowController = ChatWindowController.ChatWindowController.alloc().init()
 def validateAddAccountButton(self):
     if self.addButton:
         self.addButton.setEnabled_(SIPManager().validateAddAccountAction())
    def createNewAccount(self):
        sip_address = None
        display_name = str(self.newDisplayNameText.stringValue().strip())
        username = str(self.newUsernameText.stringValue().strip())
        password = str(self.newPasswordText.stringValue().strip())
        email = str(self.newEmailText.stringValue())

        self.progressIndicator.setHidden_(False)
        self.domainButton.setHidden_(True)
        self.progressText.setHidden_(False)
        self.progressIndicator.setUsesThreadedAnimation_(True)
        self.progressIndicator.startAnimation_(None)
        self.window.display()

        url = SIPSimpleSettings().server.enrollment_url

        sip_address = None

        tzname = datetime.datetime.now(tzlocal()).tzname() or ""

        if not tzname:
            BlinkLogger().log_warning("Unable to determine timezone")

        values = {'password'     : password.encode("utf8"),
                  'username'     : username.encode("utf8"),
                  'email'        : email.encode("utf8"),
                  'display_name' : display_name.encode("utf8"),
                  'tzinfo'       : tzname }

        BlinkLogger().log_info("Requesting creation of a new SIP account at %s" % url)

        data = urllib.parse.urlencode(values)
        req = urllib.request.Request(url, data.encode("utf-8"))

        try:
            raw_response = urllib.request.urlopen(req)
        except urllib.error.URLError as e:
            error_message = NSLocalizedString("Cannot connect to enrollment server: %s", "Enrollment panel label") % e
        except urllib.error.HTTPError as e:
            error_message = NSLocalizedString("Error from enrollment server: %s", "Enrollment panel label") % e
        else:
            raw_data = raw_response.read().decode().replace('\\/', '/')

            try:
                json_data = json.loads(raw_data)
            except (TypeError, json.decoder.JSONDecodeError):
                error_message = NSLocalizedString("Cannot decode data from enrollment server", "Enrollment panel label")
            else:

                try:
                    success = json_data["success"]
                except (TypeError, KeyError):
                    success = False
                
                if not success:
                    BlinkLogger().log_info("Enrollment Server failed to create SIP account")
                    try:
                        error_message = json_data["error_message"]
                    except (TypeError, KeyError):
                        error_message == 'Cannot read server response'
                else:
                    BlinkLogger().log_info("Enrollment Server successfully created SIP account")

                    data = defaultdict(lambda: None, json_data)
                    tls_path = None if data['passport'] is None else SIPManager().save_certificates(data)

                    sip_address = data['sip_address']
                    try:
                        outbound_proxy = data['outbound_proxy']
                    except KeyError:
                        outbound_proxy = None

                    try:
                        xcap_root = data['xcap_root']
                    except KeyError:
                        xcap_root = None

                    try:
                        msrp_relay = data['msrp_relay']
                    except KeyError:
                        msrp_relay = None

                    try:
                        settings_url = data['settings_url']
                    except KeyError:
                        settings_url = None

                    try:
                        web_alert_url = data['web_alert_url']
                    except KeyError:
                        web_alert_url = None

                    try:
                        web_password = data['web_password']
                    except KeyError:
                        web_password = None

                    try:
                        conference_server = data['conference_server']
                    except KeyError:
                        conference_server = None

                    try:
                        ldap_hostname = data['ldap_hostname']
                    except KeyError:
                        ldap_hostname = None

                    try:
                        ldap_transport = data['ldap_transport']
                    except KeyError:
                        ldap_transport = None

                    try:
                        ldap_port = data['ldap_port']
                    except KeyError:
                        ldap_port = None

                    try:
                        ldap_username = data['ldap_username']
                    except KeyError:
                        ldap_username = None

                    try:
                        ldap_password = data['ldap_password']
                    except KeyError:
                        ldap_password = None

                    try:
                        ldap_dn = data['ldap_dn']
                    except KeyError:
                        ldap_dn = None


        self.progressIndicator.stopAnimation_(None)
        self.progressIndicator.setHidden_(True)
        self.progressText.setHidden_(True)
        self.domainButton.setHidden_(False)

        if sip_address is None:
            BlinkLogger().log_info(error_message)
            NSRunAlertPanel(NSLocalizedString("Sign Up to SIP Account", "Window title"),
                            NSLocalizedString("Error creating SIP account: %s", "Label") % error_message, NSLocalizedString("OK", "Button title"), None, None)
            return False

        try:
            account = Account(str(sip_address))
        except ValueError as e:
            NSRunAlertPanel(NSLocalizedString("Sign Up to SIP Account", "Window title"), NSLocalizedString("Cannot add SIP Account: %s", "Label") % e, NSLocalizedString("OK", "Button title"), None, None)
            return False
        else:
            NSApp.delegate().contactsWindowController.created_accounts.add(account.id)

        account.display_name = display_name
        account.auth.password = password
        account.nat_traversal.use_ice = True
        account.rtp.srtp_encryption = 'optional'

        if tls_path:
            account.tls.certificate = tls_path

        account.sip.outbound_proxy = outbound_proxy
        account.xcap.xcap_root = xcap_root
        account.nat_traversal.msrp_relay = msrp_relay

        if settings_url:
            account.server.settings_url = settings_url

        if web_alert_url:
            account.web_alert.alert_url = web_alert_url

        if web_password:
            account.server.web_password = web_password

        if conference_server:
            account.conference.server_address = conference_server

        if ldap_hostname:
            account.ldap.enabled = True
            account.ldap.hostname = ldap_hostname
            account.ldap.dn = ldap_dn
            account.ldap.username = ldap_username
            if ldap_password:
                account.ldap.password = ldap_password

            if ldap_transport:
                account.ldap.transport = ldap_transport

            if ldap_port:
                account.ldap.port = ldap_port

        sync_with_icloud = bool(self.syncWithiCloudCheckbox.state())
        account.gui.sync_with_icloud = sync_with_icloud

        account.save()

        NSRunAlertPanel(NSLocalizedString("SIP Account Created", "Window title"), NSLocalizedString("Your new SIP Address is:\n\n%s", "Label") % sip_address, NSLocalizedString("Continue", "Button title"), None, None)

        # enable account only after Continue pressed to give server time to update
        account.enabled = True
        account.save()
        AccountManager().default_account = account

        return True
class BlinkAppDelegate(NSObject):
    '''Responsable for starting and stoping the application
       Register URL types handled by Blink
       Updating the dock icon with missed calls
       Migrating data from one version to another
       Start enrollment if run first time
       Calling Initial SIP URL if necessary
       Handle wakeup from sleep
       Show about panel'''

    implements(IObserver)

    contactsWindowController = objc.IBOutlet()
    aboutPanel = objc.IBOutlet()
    migrationPanel = objc.IBOutlet()
    migrationText = objc.IBOutlet()
    migrationProgressWheel = objc.IBOutlet()
    aboutVersion = objc.IBOutlet()
    aboutBundle = objc.IBOutlet()
    aboutSlogan = objc.IBOutlet()
    aboutCopyright = objc.IBOutlet()
    aboutzRTPIcon = objc.IBOutlet()
    ui_notification_center = None
    application_will_end = False
    wake_up_timestamp = None

    debug = False

    blinkMenu = objc.IBOutlet()
    ready = False
    missedCalls = 0
    missedChats = 0
    urisToOpen = []
    wait_for_enrollment = False
    updater = None

    def init(self):
        self = super(BlinkAppDelegate, self).init()
        if self:
            self.applicationName = str(NSBundle.mainBundle().infoDictionary().objectForKey_("CFBundleExecutable"))
            self.applicationNamePrint = str(NSBundle.mainBundle().infoDictionary().objectForKey_("CFBundleName"))
            build = str(NSBundle.mainBundle().infoDictionary().objectForKey_("CFBundleVersion"))
            date = str(NSBundle.mainBundle().infoDictionary().objectForKey_("BlinkVersionDate"))

            BlinkLogger().log_info(u"Starting %s build %s from %s" % (self.applicationNamePrint, build, date))

            self.registerURLHandler()
            NSWorkspace.sharedWorkspace().notificationCenter().addObserver_selector_name_object_(self, "computerDidWake:", NSWorkspaceDidWakeNotification, None)
            NSWorkspace.sharedWorkspace().notificationCenter().addObserver_selector_name_object_(self, "computerWillSleep:", NSWorkspaceWillSleepNotification, None)
            NSDistributedNotificationCenter.defaultCenter().addObserver_selector_name_object_suspensionBehavior_(self, "callFromAddressBook:", "CallTelephoneNumberWithBlinkFromAddressBookNotification", "AddressBook", NSNotificationSuspensionBehaviorDeliverImmediately)
            NSDistributedNotificationCenter.defaultCenter().addObserver_selector_name_object_suspensionBehavior_(self, "callFromAddressBook:", "CallSipAddressWithBlinkFromAddressBookNotification", "AddressBook", NSNotificationSuspensionBehaviorDeliverImmediately)

            NotificationCenter().add_observer(self, name="CFGSettingsObjectDidChange")
            NotificationCenter().add_observer(self, name="SIPApplicationDidStart")
            NotificationCenter().add_observer(self, name="SIPApplicationWillEnd")
            NotificationCenter().add_observer(self, name="SIPApplicationDidEnd")
            NotificationCenter().add_observer(self, name="SystemIPAddressDidChange")
            NotificationCenter().add_observer(self, name="NetworkConditionsDidChange")
            NotificationCenter().add_observer(self, name="SIPEngineTransportDidDisconnect")
            NotificationCenter().add_observer(self, name="SIPEngineTransportDidConnect")
            NotificationCenter().add_observer(self, name="DNSNameserversDidChange")

            # remove obsolete settings
            userdef = NSUserDefaults.standardUserDefaults()
            userdef.removeObjectForKey_('SIPTrace')
            userdef.removeObjectForKey_('MSRPTrace')
            userdef.removeObjectForKey_('XCAPTrace')
            userdef.removeObjectForKey_('EnablePJSIPTrace')
            userdef.removeObjectForKey_('EnableNotificationsTrace')

            try:
                from Updater import Updater
            except ImportError:
                pass
            else:
                self.updater = Updater()

            call_in_thread('file-io', self.purge_temporary_files)

        return self

    def purge_temporary_files(self):
        for dir in ('.tmp_screenshots', '.tmp_snapshots', '.tmp_file_transfers'):
            folder = ApplicationData.get(dir)
            if os.path.exists(folder):
                try:
                    shutil.rmtree(folder)
                except EnvironmentError:
                    pass

    def gui_notify(self, title, body, subtitle=None):
        if self.application_will_end:
            return
        major, minor = platform.mac_ver()[0].split('.')[0:2]
        if (int(major) == 10 and int(minor) >= 8) or int(major) > 10:
            if self.ui_notification_center is None:
                self.ui_notification_center = Foundation.NSUserNotificationCenter.defaultUserNotificationCenter()
                self.ui_notification_center.setDelegate_(self)

            notification = Foundation.NSUserNotification.alloc().init()
            notification.setTitle_(title)
            if subtitle is not None:
                notification.setSubtitle_(subtitle)
            notification.setInformativeText_(body)
            self.ui_notification_center.scheduleNotification_(notification)

    def userNotificationCenter_didDeliverNotification_(self, center, notification):
        pass

    def userNotificationCenter_didActivateNotification_(self, center, notification):
        pass

    def userNotificationCenter_shouldPresentNotification_(self, center, notification):
        return True

    # Needed by run_in_gui_thread and call_in_gui_thread
    def callObject_(self, callable):
        try:
            callable()
        except:
            log.err()

    def enroll(self):
        enroll = EnrollmentController.alloc().init()
        enroll.setCreateAccount()
        enroll.runModal()

    def updateDockTile(self):
        if self.missedCalls > 0 or self.missedChats > 0:
            icon = NSImage.imageNamed_("Blink")
            image = NSImageView.alloc().initWithFrame_(NSMakeRect(0, 0, 32, 32))
            image.setImage_(icon)
            if self.missedCalls > 0 and self.missedChats > 0:
                NSApp.dockTile().setBadgeLabel_("%i / %i" % (self.missedCalls, self.missedChats))
            else:
                NSApp.dockTile().setBadgeLabel_("%i" % (self.missedCalls + self.missedChats))
            NSApp.dockTile().setContentView_(image)
        else:
            NSApp.dockTile().setBadgeLabel_("")
            NSApp.dockTile().setContentView_(None)
            icon = None
        NSApp.setApplicationIconImage_(icon)
        NSApp.dockTile().display()

    def noteNewMessage(self, window):
        if not NSApp.isActive():
            self.missedChats += 1
            self.updateDockTile()

    def noteMissedCall(self):
        self.missedCalls += 1
        self.updateDockTile()
        NSApp.requestUserAttention_(NSInformationalRequest)

    def applicationShouldHandleReopen_hasVisibleWindows_(self, sender, flag):
        if not flag:
            self.contactsWindowController.showWindow_(None)
        self.missedCalls = 0
        self.missedChats = 0
        self.updateDockTile()
        return False

    def applicationDidBecomeActive_(self, notif):
        self.missedCalls = 0
        self.missedChats = 0
        self.updateDockTile()

    def applicationDidFinishLaunching_(self, sender):
        self.blinkMenu.setTitle_(self.applicationNamePrint)

        config_file = ApplicationData.get('config')
        self.icloud_manager = iCloudManager()
        self.backend = SIPManager()

        self.contactsWindowController.setup(self.backend)

        while True:
            try:
                first_run = not os.path.exists(config_file)
                self.contactsWindowController.first_run = first_run

                self.backend.init()
                self.backend.fetch_account()
                accounts = AccountManager().get_accounts()
                if not accounts or (first_run and accounts == [BonjourAccount()]):
                    self.wait_for_enrollment = True
                    self.enroll()
                break

            except FileParserError, exc:
                BlinkLogger().log_warning(u"Error parsing configuration file: %s" % exc)
                if NSRunAlertPanel(NSLocalizedString("Error", "Window title"),
                    NSLocalizedString("The configuration file is corrupted. You will need to replace it and re-enter your account information. \n\nYour current configuration file will be backed up to %s.corrupted. ", "Label") % config_file,
                    NSLocalizedString("Replace", "Button title"), NSLocalizedString("Quit", "Button title"), None) != NSAlertDefaultReturn:
                    NSApp.terminate_(None)
                    return
                os.rename(config_file, config_file+".corrupted")
                BlinkLogger().log_info(u"Renamed configuration file to %s" % config_file+".corrupted")
            except BaseException, exc:
                import traceback
                print traceback.print_exc()
                NSRunAlertPanel(NSLocalizedString("Error", "Window title"), NSLocalizedString("There was an error during startup of core functionality:\n%s", "Label") % exc,
                        NSLocalizedString("Quit", "Button title"), None, None)
                NSApp.terminate_(None)
                return
    def createNewAccount(self):
        display_name = unicode(self.newDisplayNameText.stringValue())
        username = unicode(self.newUsernameText.stringValue())
        password = unicode(self.newPasswordText.stringValue())
        email = unicode(self.newEmailText.stringValue())

        self.progressIndicator.setHidden_(False)
        self.progressText.setHidden_(False)
        self.progressIndicator.setUsesThreadedAnimation_(True)
        self.progressIndicator.startAnimation_(None)
        self.window.display()

        url = SIPSimpleSettings().server.enrollment_url

        tzname = datetime.datetime.now(tzlocal()).tzname() or ""
        if not tzname:
            BlinkLogger().log_warning(u"Unable to determine timezone")

        values = {
            'password': password.encode("utf8"),
            'username': username.encode("utf8"),
            'email': email.encode("utf8"),
            'display_name': display_name.encode("utf8"),
            'tzinfo': tzname
        }

        BlinkLogger().log_info(
            u"Requesting creation of a new SIP account at %s" % url)

        data = urllib.urlencode(values)
        req = urllib2.Request(url, data)
        raw_response = urllib2.urlopen(req)
        json_data = raw_response.read()

        try:
            response = cjson.decode(json_data.replace('\\/', '/'))
        except TypeError:
            error_message = 'Cannot decode json data from enrollment server'

        if response:
            if not response["success"]:
                BlinkLogger().log_info(
                    u"Enrollment Server failed to create SIP account: %(error_message)s"
                    % response)
                error_message = response["error_message"]
            else:
                BlinkLogger().log_info(
                    u"Enrollment Server successfully created SIP account %(sip_address)s"
                    % response)
                data = defaultdict(lambda: None, response)
                tls_path = None if data['passport'] is None else SIPManager(
                ).save_certificates(data)

                try:
                    sip_address = data['sip_address']
                    try:
                        outbound_proxy = data['outbound_proxy']
                    except KeyError:
                        outbound_proxy = None

                    try:
                        xcap_root = data['xcap_root']
                    except KeyError:
                        xcap_root = None

                    try:
                        msrp_relay = data['msrp_relay']
                    except KeyError:
                        msrp_relay = None

                    try:
                        settings_url = data['settings_url']
                    except KeyError:
                        settings_url = None

                    try:
                        web_alert_url = data['web_alert_url']
                    except KeyError:
                        web_alert_url = None

                    try:
                        web_password = data['web_password']
                    except KeyError:
                        web_password = None

                    try:
                        conference_server = data['conference_server']
                    except KeyError:
                        conference_server = None

                    try:
                        ldap_hostname = data['ldap_hostname']
                    except KeyError:
                        ldap_hostname = None

                    try:
                        ldap_transport = data['ldap_transport']
                    except KeyError:
                        ldap_transport = None

                    try:
                        ldap_port = data['ldap_port']
                    except KeyError:
                        ldap_port = None

                    try:
                        ldap_username = data['ldap_username']
                    except KeyError:
                        ldap_username = None

                    try:
                        ldap_password = data['ldap_password']
                    except KeyError:
                        ldap_password = None

                    try:
                        ldap_dn = data['ldap_dn']
                    except KeyError:
                        ldap_dn = None

                except KeyError:
                    sip_address = None
        else:
            sip_address = None
            error_message = "No response received from %s" % url

        self.progressIndicator.stopAnimation_(None)
        self.progressIndicator.setHidden_(True)
        self.progressText.setHidden_(True)

        if not sip_address:
            NSRunAlertPanel("Sign Up to SIP Account",
                            "Error creating account: %s" % error_message, "OK",
                            None, None)
            return False

        try:
            account = Account(str(sip_address))
        except ValueError, e:
            NSRunAlertPanel("Sign Up to SIP Account",
                            "Cannot add SIP Account: %s" % str(e), "OK", None,
                            None)
            return False
Exemple #13
0
 def unMuteAfterSpeechDidEnd(self):
     if self.muted_by_synthesizer and SIPManager().is_muted():
         NSApp.delegate().contactsWindowController.muteClicked_(None)
         self.muted_by_synthesizer = False
     if self.speech_recognizer:
         self.speech_recognizer.startListening()