예제 #1
0
class BaseSnomPlugin(StandardPlugin):
    _ENCODING = 'UTF-8'
    _LOCALE = {
        u'de_DE': (u'Deutsch', u'GER'),
        u'en_US': (u'English', u'USA'),
        u'es_ES': (u'Espanol', u'ESP'),
        u'fr_FR': (u'Francais', u'FRA'),
        u'fr_CA': (u'Francais', u'USA'),
        u'it_IT': (u'Italiano', u'ITA'),
        u'nl_NL': (u'Dutch', u'NLD'),
    }
    _SIP_DTMF_MODE = {
        u'RTP-in-band': u'off',
        u'RTP-out-of-band': u'off',
        u'SIP-INFO': u'sip_info_only'
    }
    _XX_DICT_DEF = u'en'
    _XX_DICT = {
        u'en': {
            u'remote_directory': u'Directory',
        },
        u'fr': {
            u'remote_directory': u'Annuaire',
        },
    }

    def __init__(self, app, plugin_dir, gen_cfg, spec_cfg):
        StandardPlugin.__init__(self, app, plugin_dir, gen_cfg, spec_cfg)

        self._tpl_helper = TemplatePluginHelper(plugin_dir)

        downloaders = FetchfwPluginHelper.new_downloaders(gen_cfg.get('proxies'))
        fetchfw_helper = FetchfwPluginHelper(plugin_dir, downloaders)

        self.services = fetchfw_helper.services()
        self.http_service = HTTPNoListingFileService(self._tftpboot_dir)

    http_dev_info_extractor = BaseSnomHTTPDeviceInfoExtractor()

    def _common_templates(self):
        yield ('common/gui_lang.xml.tpl', 'gui_lang.xml')
        yield ('common/web_lang.xml.tpl', 'web_lang.xml')
        for tpl_format, file_format in [('common/snom%s.htm.tpl', 'snom%s.htm'),
                                        ('common/snom%s.xml.tpl', 'snom%s.xml'),
                                        ('common/snom%s-firmware.xml.tpl', 'snom%s-firmware.xml')]:
            for model in self._MODELS:
                yield tpl_format % model, file_format % model

    def configure_common(self, raw_config):
        for tpl_filename, filename in self._common_templates():
            tpl = self._tpl_helper.get_template(tpl_filename)
            dst = os.path.join(self._tftpboot_dir, filename)
            self._tpl_helper.dump(tpl, raw_config, dst, self._ENCODING)

    def _update_sip_lines(self, raw_config):
        proxy_ip = raw_config.get(u'sip_proxy_ip')
        backup_proxy_ip = raw_config.get(u'sip_backup_proxy_ip')
        voicemail = raw_config.get(u'exten_voicemail')
        for line in raw_config[u'sip_lines'].itervalues():
            if proxy_ip:
                line.setdefault(u'proxy_ip', proxy_ip)
            if backup_proxy_ip:
                line.setdefault(u'backup_proxy_ip', backup_proxy_ip)
            if voicemail:
                line.setdefault(u'voicemail', voicemail)

    def _get_fkey_domain(self, raw_config):
        # Return None if there's no usable domain
        if u'sip_proxy_ip' in raw_config:
            return raw_config[u'sip_proxy_ip']
        else:
            lines = raw_config[u'sip_lines']
            if lines:
                return lines[min(lines.iterkeys())][u'proxy_ip']
        return None

    def _add_fkeys(self, raw_config, model):
        domain = self._get_fkey_domain(raw_config)
        if domain is None:
            if raw_config[u'funckeys']:
                logger.warning('Could not set funckeys: no domain part')
        else:
            lines = []
            for funckey_no, funckey_dict in sorted(raw_config[u'funckeys'].iteritems(),
                                                   key=itemgetter(0)):
                funckey_type = funckey_dict[u'type']
                if funckey_type == u'speeddial':
                    type_ = u'speed'
                    suffix = ''
                elif funckey_type == u'park':
                    if model in ['710', '720', '715', '760']:
                        type_ = u'orbit'
                        suffix = ''
                    else:
                        type_ = u'speed'
                        suffix = ''
                elif funckey_type == u'blf':
                    if u'exten_pickup_call' in raw_config:
                        type_ = u'blf'
                        suffix = '|%s' % raw_config[u'exten_pickup_call']
                    else:
                        logger.warning('Could not set funckey %s: no exten_pickup_call',
                                       funckey_no)
                        continue
                else:
                    logger.info('Unsupported funckey type: %s', funckey_type)
                    continue
                value = funckey_dict[u'value']
                label = escape(funckey_dict.get(u'label', value))
                fkey_value = self._format_fkey_value(type_, value, domain, suffix)
                lines.append(u'<fkey idx="%d" label="%s" context="active" perm="R">%s</fkey>' %
                            (int(funckey_no) - 1, label, fkey_value))
            raw_config[u'XX_fkeys'] = u'\n'.join(lines)

    def _format_fkey_value(self, fkey_type, value, domain, suffix):
        return '%s &lt;sip:%s@%s&gt;%s' % (fkey_type, value, domain, suffix)

    def _add_lang(self, raw_config):
        if u'locale' in raw_config:
            locale = raw_config[u'locale']
            if locale in self._LOCALE:
                raw_config[u'XX_lang'] = self._LOCALE[locale]

    def _format_dst_change(self, dst_change):
        fmted_time = u'%02d:%02d:%02d' % tuple(dst_change['time'].as_hms)
        day = dst_change['day']
        if day.startswith('D'):
            return u'%02d.%02d %s' % (int(day[1:]), dst_change['month'], fmted_time)
        else:
            week, weekday = map(int, day[1:].split('.'))
            weekday = tzinform.week_start_on_monday(weekday)
            return u'%02d.%02d.%02d %s' % (dst_change['month'], week, weekday, fmted_time)

    def _format_tzinfo(self, tzinfo):
        lines = []
        lines.append(u'<timezone perm="R"></timezone>')
        lines.append(u'<utc_offset perm="R">%+d</utc_offset>' % tzinfo['utcoffset'].as_seconds)
        if tzinfo['dst'] is None:
            lines.append(u'<dst perm="R"></dst>')
        else:
            lines.append(u'<dst perm="R">%d %s %s</dst>' %
                         (tzinfo['dst']['save'].as_seconds,
                          self._format_dst_change(tzinfo['dst']['start']),
                          self._format_dst_change(tzinfo['dst']['end'])))
        return u'\n'.join(lines)

    def _add_timezone(self, raw_config):
        if u'timezone' in raw_config:
            try:
                tzinfo = tzinform.get_timezone_info(raw_config[u'timezone'])
            except tzinform.TimezoneNotFoundError, e:
                logger.warning('Unknown timezone %s: %s', raw_config[u'timezone'], e)
            else:
                raw_config[u'XX_timezone'] = self._format_tzinfo(tzinfo)
예제 #2
0
class BaseDigiumPlugin(StandardPlugin):

    _ENCODING = 'UTF-8'
    _CONTACT_TEMPLATE = 'contact.tpl'

    def __init__(self, app, plugin_dir, gen_cfg, spec_cfg):
        StandardPlugin.__init__(self, app, plugin_dir, gen_cfg, spec_cfg)

        self._tpl_helper = TemplatePluginHelper(plugin_dir)
        self._digium_dir = os.path.join(self._tftpboot_dir, 'Digium')

        downloaders = FetchfwPluginHelper.new_downloaders(gen_cfg.get('proxies'))
        fetchfw_helper = FetchfwPluginHelper(plugin_dir, downloaders)

        self.services = fetchfw_helper.services()
        self.http_service = HTTPNoListingFileService(self._tftpboot_dir)

    dhcp_dev_info_extractor = DigiumDHCPDeviceInfoExtractor()

    http_dev_info_extractor = DigiumHTTPDeviceInfoExtractor()

    def configure(self, device, raw_config):
        self._check_device(device)

        filename = self._dev_specific_filename(device)
        contact_filename = self._dev_contact_filename(device)

        tpl = self._tpl_helper.get_dev_template(filename, device)
        contact_tpl = self._tpl_helper.get_template(self._CONTACT_TEMPLATE)

        raw_config['XX_mac'] = self._format_mac(device)
        raw_config['XX_main_proxy_ip'] = self._get_main_proxy_ip(raw_config)
        raw_config['XX_funckeys'] = self._transform_funckeys(raw_config)
        raw_config['XX_lang'] = raw_config.get(u'locale')

        path = os.path.join(self._digium_dir, filename)
        contact_path = os.path.join(self._digium_dir, contact_filename)
        self._tpl_helper.dump(tpl, raw_config, path, self._ENCODING)
        self._tpl_helper.dump(contact_tpl, raw_config, contact_path, self._ENCODING)

    def deconfigure(self, device):
        filenames = [
            self._dev_specific_filename(device),
            self._dev_contact_filename(device)
        ]

        for filename in filenames:
            path = os.path.join(self._digium_dir, filename)
            try:
                os.remove(path)
            except OSError as e:
                logger.info('error while removing file %s: %s', (path, e))

    if hasattr(synchronize, 'standard_sip_synchronize'):
        def synchronize(self, device, raw_config):
            return synchronize.standard_sip_synchronize(device)

    else:
        # backward compatibility with older xivo-provd server
        def synchronize(self, device, raw_config):
            try:
                ip = device[u'ip'].encode('ascii')
            except KeyError:
                return defer.fail(Exception('IP address needed for device synchronization'))
            else:
                sync_service = synchronize.get_sync_service()
                if sync_service is None or sync_service.TYPE != 'AsteriskAMI':
                    return defer.fail(Exception('Incompatible sync service: %s' % sync_service))
                else:
                    return threads.deferToThread(sync_service.sip_notify, ip, 'check-sync')

    def get_remote_state_trigger_filename(self, device):
        if u'mac' not in device:
            return None

        return self._dev_specific_filename(device)

    def _check_device(self, device):
        if u'mac' not in device:
            raise Exception('MAC address needed to configure device')

    def _get_main_proxy_ip(self, raw_config):
        if raw_config[u'sip_lines']:
            line_no = min(int(x) for x in raw_config[u'sip_lines'].keys())
            line_no = str(line_no)
            return raw_config[u'sip_lines'][line_no][u'proxy_ip']
        else:
            return raw_config[u'ip']

    def _format_mac(self, device):
         return format_mac(device[u'mac'], separator='', uppercase=False)

    def _dev_specific_filename(self, device):
        filename = '%s.cfg' % self._format_mac(device)
        return filename

    def _dev_contact_filename(self, device):
        contact_filename = '%s-contacts.xml' % self._format_mac(device)
        return contact_filename

    def _transform_funckeys(self, raw_config):
        return dict(
            (int(k), v) for k, v in raw_config['funckeys'].iteritems()
        )
예제 #3
0
class BaseTechnicolorPlugin(StandardPlugin):
    _ENCODING = 'ISO-8859-1'
    _TZ_MAP = _gen_tz_map()
    _LOCALE = {
        # <locale id>, (<langage type>, <country code>)
        u'de_DE': (u'3', u'DE'),
        u'en_US': (u'0', u'US'),
        u'es_ES': (u'2', u'ES'),
        u'fr_FR': (u'1', u'FR'),
        u'fr_CA': (u'1', u'US'),
    }
    _LOCALE_DEF = (u'0', u'US')
    _SIP_DTMF_MODE = {
        u'RTP-in-band': u'0',
        u'RTP-out-of-band': u'1',
        u'SIP-INFO': u'4'
    }
    _DTMF_DEF = u'1'
    _SIP_TRANSPORT = {
        u'udp': u'0',
        u'tcp': u'1',
        u'tls': u'2'
    }
    _TRANSPORT_DEF = u'0'
    _NTP_ZONE_NUM_DEF = u'23'
    _XX_PHONEBOOK_NAME = {
        u'fr': u'Annuaire entreprise',
        u'en': u'Enterprise directory'
    }
    _XX_PHONEBOOK_NAME_DEF = u''
    _NB_FKEYS = 66
    _NB_LINES = 4

    def __init__(self, app, plugin_dir, gen_cfg, spec_cfg):
        StandardPlugin.__init__(self, app, plugin_dir, gen_cfg, spec_cfg)

        self._tpl_helper = TemplatePluginHelper(plugin_dir)

        downloaders = FetchfwPluginHelper.new_downloaders(gen_cfg.get('proxies'))
        fetchfw_helper = FetchfwPluginHelper(plugin_dir, downloaders)

        self.services = fetchfw_helper.services()
        self.http_service = HTTPNoListingFileService(self._tftpboot_dir)

    http_dev_info_extractor = BaseTechnicolorHTTPDeviceInfoExtractor()

    def configure_common(self, raw_config):
        for tpl_filename, filename in self._COMMON_TEMPLATES:
            tpl = self._tpl_helper.get_template(tpl_filename)
            dst = os.path.join(self._tftpboot_dir, filename)
            self._tpl_helper.dump(tpl, raw_config, dst, self._ENCODING)

    def _add_country_and_lang(self, raw_config):
        locale = raw_config.get(u'locale')
        raw_config[u'XX_language_type'], raw_config[u'XX_country_code'] = \
                                self._LOCALE.get(locale, self._LOCALE_DEF)

    def _add_config_sn(self, raw_config):
        # The only thing config_sn needs to be is 12 digit long and different
        # from one config file to another.
        raw_config[u'XX_config_sn'] = '%012.f' % time.time()

    def _add_dtmf_mode_flag(self, raw_config):
        dtmf_mode = raw_config.get(u'sip_dtmf_mode')
        raw_config[u'XX_dtmf_mode_flag'] = self._SIP_DTMF_MODE.get(dtmf_mode,
                                                                   self._DTMF_DEF)

    def _add_transport_flg(self, raw_config):
        sip_transport = raw_config.get(u'sip_transport')
        raw_config[u'XX_transport_flg'] = self._SIP_TRANSPORT.get(sip_transport,
                                                                  self._TRANSPORT_DEF)

    def _gen_xx_phonebook_name(self, raw_config):
        if u'locale' in raw_config:
            language = raw_config[u'locale'].split('_')[0]
            return self._XX_PHONEBOOK_NAME.get(language,
                                               self._XX_PHONEBOOK_NAME_DEF)
        else:
            return self._XX_PHONEBOOK_NAME_DEF

    def _tzinfo_to_zone_num(self, tzinfo):
        utcoffset_m = tzinfo['utcoffset'].as_minutes
        if utcoffset_m not in self._TZ_MAP:
            # No UTC offset matching. Let's try finding one relatively close...
            for supp_offset in [30, -30, 60, -60]:
                if utcoffset_m + supp_offset in self._TZ_MAP:
                    utcoffset_m += supp_offset
                    break
            else:
                return self._XX_NTP_ZONE_NUM_DEF

        dst_map = self._TZ_MAP[utcoffset_m]
        if tzinfo['dst']:
            dst_key = tzinfo['dst']['as_string']
        else:
            dst_key = None
        if dst_key not in dst_map:
            # No DST rules matching. Fallback on all-standard time or random
            # DST rule in last resort...
            if None in dst_map:
                dst_key = None
            else:
                dst_key = dst_map.keys[0]
        return dst_map[dst_key]

    def _add_ntp_zone_num(self, raw_config):
        raw_config[u'XX_ntp_zone_num'] = self._NTP_ZONE_NUM_DEF
        if u'timezone' in raw_config:
            try:
                tzinfo = tzinform.get_timezone_info(raw_config[u'timezone'])
            except tzinform.TimezoneNotFoundError, e:
                logger.info('Unknown timezone: %s', e)
            else:
                raw_config[u'XX_ntp_zone_num'] = self._tzinfo_to_zone_num(tzinfo)
예제 #4
0
class BaseAlcatelPlugin(StandardPlugin):
    _ENCODING = 'UTF-8'

    _SIP_DTMF_MODE = {
        u'off': 0,
        u'RTP-in-band': 1,
        u'RTP-out-of-band': 2,
        u'SIP-INFO': 4,
    }

    _NB_FUNCKEYS = {
        u'M3': 20,
        u'M5': 28,
        u'M7': 28,
    }
    _FUNCKEY_TYPE = {
        u'blf': 59,
        u'speeddial': 1,
    }
    _LANG = {
        u'en': 0,
        u'fr': 1,
        u'de': 2,
        u'it': 3,
        u'es': 4,
        u'nl': 5,
        u'pt': 6,
        u'hu': 7,
        u'cs': 8,
        u'sk': 9,
        u'sl': 10,
        u'et': 11,
        u'pl': 12,
        u'lt': 13,
        u'lv': 14,
        u'tr': 15,
        u'el': 16,
        u'sv': 17,
        u'no': 18,
        u'da': 19,
        u'fi': 20,
        u'is': 21,
        u'zh': 22,
    }

    _SENSITIVE_FILENAME_REGEX = re.compile(r'^config\.[0-9a-f]{12}\.xml')
    http_dev_info_extractor = BaseAlcatelMyriadHTTPDeviceInfoExtractor()

    def __init__(self, app, plugin_dir, gen_cfg, spec_cfg):
        StandardPlugin.__init__(self, app, plugin_dir, gen_cfg, spec_cfg)

        self._tpl_helper = TemplatePluginHelper(plugin_dir)

        downloaders = FetchfwPluginHelper.new_downloaders(
            gen_cfg.get('proxies'))
        fetchfw_helper = FetchfwPluginHelper(plugin_dir, downloaders)

        self.services = fetchfw_helper.services()
        self.http_service = HTTPNoListingFileService(self._tftpboot_dir)

    def _common_templates(self):
        for tpl_format, file_format in [('common/config.model.xml.tpl',
                                         'config.{}.xml')]:
            for model in self._MODELS_VERSIONS:
                yield tpl_format.format(model), file_format.format(model)

    def configure_common(self, raw_config):
        self._add_server_url(raw_config)
        for tpl_filename, filename in self._common_templates():
            tpl = self._tpl_helper.get_template(tpl_filename)
            dest_file = os.path.join(self._tftpboot_dir, filename)
            self._tpl_helper.dump(tpl, raw_config, dest_file, self._ENCODING)

    def _update_sip_lines(self, raw_config):
        proxy_ip = raw_config.get(u'sip_proxy_ip')
        proxy_port = raw_config.get(u'sip_proxy_port')
        backup_proxy_ip = raw_config.get(u'sip_backup_proxy_ip')
        backup_proxy_port = raw_config.get(u'sip_backup_proxy_port')
        outbound_proxy_ip = raw_config.get(u'sip_outbound_proxy_ip')
        outbound_proxy_port = raw_config.get(u'sip_outbound_proxy_port')
        voicemail = raw_config.get(u'exten_voicemail')

        for line in raw_config[u'sip_lines'].itervalues():
            if proxy_ip:
                line.setdefault(u'proxy_ip', proxy_ip)
            if proxy_port:
                line.setdefault(u'proxy_port', proxy_port)
            if backup_proxy_ip:
                line.setdefault(u'backup_proxy_ip', backup_proxy_ip)
            if backup_proxy_port:
                line.setdefault(u'backup_proxy_port', backup_proxy_port)
            if outbound_proxy_ip:
                line.setdefault(u'outbound_proxy_ip', outbound_proxy_ip)
            if outbound_proxy_port:
                line.setdefault(u'outbound_proxy_port', outbound_proxy_port)
            if voicemail:
                line.setdefault(u'voicemail', voicemail)

    def _add_fkeys(self, raw_config, model):
        nb_funckeys = self._NB_FUNCKEYS.get(model)
        if not nb_funckeys:
            logger.warning(
                'Unknown model: "%s". Skipping function key configuration.',
                model)
            return

        raw_config[u'XX_fkeys'] = []
        for funckey_no, funckey_dict in raw_config[u'funckeys'].iteritems():
            position = int(funckey_no) + 1
            fkey_type = self._FUNCKEY_TYPE.get(
                funckey_dict[u'type'], self._FUNCKEY_TYPE[u'speeddial'])
            fkey_label = funckey_dict[u'label']
            fkey_extension = funckey_dict[u'value']
            if position > nb_funckeys:
                logger.warning(
                    'Function key "%s" outside range supported by phone.',
                    position)
                continue
            fkey_data = {
                u'position': position,
                u'type': fkey_type,
                u'label': fkey_label,
                u'extension': fkey_extension,
                u'value': fkey_extension,
            }
            raw_config[u'XX_fkeys'].append(fkey_data)

    def _format_tzinfo(self, tzinfo):
        tz_hms = tzinfo['utcoffset'].as_hms
        offset_hour = tz_hms[0]
        offset_minutes = tz_hms[1]
        return '{:+02d}:{:02d}'.format(offset_hour, offset_minutes)

    def _add_timezone(self, raw_config):
        if u'timezone' in raw_config:
            try:
                tzinfo = tzinform.get_timezone_info(raw_config[u'timezone'])
            except tzinform.TimezoneNotFoundError as e:
                logger.warning('Unknown timezone "%s": "%s"',
                               raw_config[u'timezone'], e)
            else:
                raw_config[u'XX_timezone'] = self._format_tzinfo(tzinfo)

    def _add_language(self, raw_config):
        locale = raw_config[u'locale']
        if '_' in locale:
            lang, _ = locale.split('_')
        else:
            lang = locale

        lang_code = self._LANG.get(lang, self._LANG['en'])
        raw_config[u'XX_lang'] = lang_code

    def _add_user_dtmf_info(self, raw_config):
        dtmf_mode = raw_config.get(u'sip_dtmf_mode')
        for line in raw_config[u'sip_lines'].itervalues():
            cur_dtmf_mode = line.get(u'dtmf_mode', dtmf_mode)
            line[u'XX_user_dtmf_info'] = self._SIP_DTMF_MODE.get(
                cur_dtmf_mode, 'off')

    def _add_xivo_phonebook_url(self, raw_config):
        if hasattr(plugins, 'add_xivo_phonebook_url') and raw_config.get(
                u'config_version', 0) >= 1:
            plugins.add_xivo_phonebook_url(raw_config, u'snom')
        else:
            self._add_xivo_phonebook_url_compat(raw_config)

    def _add_xivo_phonebook_url_compat(self, raw_config):
        hostname = raw_config.get(u'X_xivo_phonebook_ip')
        if hostname:
            raw_config[
                u'XX_xivo_phonebook_url'] = u'http://{hostname}/service/ipbx/web_services.php/phonebook/search/'.format(
                    hostname=hostname)

    def _check_config(self, raw_config):
        if u'http_port' not in raw_config:
            raise RawConfigError('only support configuration via HTTP')

    def _check_device(self, device):
        if u'mac' not in device:
            raise Exception('MAC address needed for device configuration')
        if u'model' not in device:
            raise Exception('Model name needed for device configuration')

    def _dev_specific_filename(self, device):
        return u'config.{}.xml'.format(format_mac(device[u'mac'],
                                                  separator=''))

    def _add_server_url(self, raw_config):
        ip = raw_config[u'ip']
        http_port = raw_config[u'http_port']
        raw_config[u'XX_server_url'] = u'http://{}:{}'.format(ip, http_port)

    def configure(self, device, raw_config):
        self._check_config(raw_config)
        self._check_device(device)
        xml_filename = self._dev_specific_filename(device)

        # generate xml file
        tpl = self._tpl_helper.get_dev_template(xml_filename, device)

        model = device.get(u'model')
        self._update_sip_lines(raw_config)
        self._add_fkeys(raw_config, model)
        self._add_timezone(raw_config)
        self._add_user_dtmf_info(raw_config)
        self._add_xivo_phonebook_url(raw_config)
        self._add_server_url(raw_config)
        self._add_language(raw_config)
        raw_config[u'XX_options'] = device.get(u'options', {})

        path = os.path.join(self._tftpboot_dir, xml_filename)
        self._tpl_helper.dump(tpl, raw_config, path, self._ENCODING)

    def deconfigure(self, device):
        filename = self._dev_specific_filename(device)
        try:
            os.remove(os.path.join(self._tftpboot_dir, filename))
        except OSError as e:
            # ignore
            logger.warning('error while removing file: "%s"', e)

    if hasattr(synchronize, 'standard_sip_synchronize'):

        def synchronize(self, device, raw_config):
            return synchronize.standard_sip_synchronize(device,
                                                        event='check-sync')

    else:
        # backward compatibility with older wazo-provd server
        def synchronize(self, device, raw_config):
            try:
                ip = device[u'ip'].encode('ascii')
            except KeyError:
                return defer.fail(
                    Exception('IP address needed for device synchronization'))
            else:
                sync_service = synchronize.get_sync_service()
                if sync_service is None or sync_service.TYPE != 'AsteriskAMI':
                    return defer.fail(
                        Exception('Incompatible sync service: %s' %
                                  sync_service))
                else:
                    return threads.deferToThread(sync_service.sip_notify, ip,
                                                 'check-sync;reboot=true')

    def get_remote_state_trigger_filename(self, device):
        if u'mac' not in device:
            return None

        return self._dev_specific_filename(device)

    def is_sensitive_filename(self, filename):
        return bool(self._SENSITIVE_FILENAME_REGEX.match(filename))
예제 #5
0
class BaseYealinkPlugin(StandardPlugin):
    _ENCODING = 'UTF-8'
    _LOCALE = {
        u'de_DE': (u'German', u'Germany', u'2'),
        u'en_US': (u'English', u'United States', u'0'),
        u'es_ES': (u'Spanish', u'Spain', u'6'),
        u'fr_FR': (u'French', u'France', u'1'),
        u'fr_CA': (u'French', u'United States', u'1'),
    }
    _SIP_DTMF_MODE = {
        u'RTP-in-band': u'0',
        u'RTP-out-of-band': u'1',
        u'SIP-INFO': u'2',
    }

    def __init__(self, app, plugin_dir, gen_cfg, spec_cfg):
        StandardPlugin.__init__(self, app, plugin_dir, gen_cfg, spec_cfg)

        self._tpl_helper = TemplatePluginHelper(plugin_dir)

        downloaders = FetchfwPluginHelper.new_downloaders(gen_cfg.get('proxies'))
        fetchfw_helper = FetchfwPluginHelper(plugin_dir, downloaders)

        self.services = fetchfw_helper.services()
        self.http_service = HTTPNoListingFileService(self._tftpboot_dir)

    http_dev_info_extractor = BaseYealinkHTTPDeviceInfoExtractor()

    def configure_common(self, raw_config):
        for filename, fw_filename, tpl_filename in self._COMMON_FILES:
            tpl = self._tpl_helper.get_template('common/%s' % tpl_filename)
            dst = os.path.join(self._tftpboot_dir, filename)
            raw_config[u'XX_fw_filename'] = fw_filename
            self._tpl_helper.dump(tpl, raw_config, dst, self._ENCODING)

    def _update_sip_lines(self, raw_config):
        for line_no, line in raw_config['sip_lines'].iteritems():
            # set line number
            line[u'XX_line_no'] = int(line_no)
            # set dtmf inband transfer
            dtmf_mode = line.get(u'dtmf_mode') or raw_config.get(u'sip_dtmf_mode')
            if dtmf_mode in self._SIP_DTMF_MODE:
                line[u'XX_dtmf_type'] = self._SIP_DTMF_MODE[dtmf_mode]
            # set voicemail
            if u'voicemail' not in line and u'exten_voicemail' in raw_config:
                line[u'voicemail'] = raw_config[u'exten_voicemail']
            # set proxy_ip
            if u'proxy_ip' not in line:
                line[u'proxy_ip'] = raw_config[u'sip_proxy_ip']
            # set proxy_port
            if u'proxy_port' not in line and u'sip_proxy_port' in raw_config:
                line[u'proxy_port'] = raw_config[u'sip_proxy_port']

    def _format_funckey_speeddial(self, funckey_no, funckey_dict):
        lines = []
        lines.append(u'memorykey.%s.line = %s' % (funckey_no, funckey_dict.get(u'line', 1)))
        lines.append(u'memorykey.%s.value = %s' % (funckey_no, funckey_dict[u'value']))
        lines.append(u'memorykey.%s.type = 13' % funckey_no)
        lines.append(u'memorykey.%s.label = %s' % (funckey_no, funckey_dict.get(u'label', u'')))
        return lines

    def _format_funckey_call_park(self, funckey_no, funckey_dict):
        lines = []
        lines.append(u'memorykey.%s.line = %s' % (funckey_no, funckey_dict.get(u'line', 1)))
        lines.append(u'memorykey.%s.value = %s' % (funckey_no, funckey_dict[u'value']))
        lines.append(u'memorykey.%s.type = 10' % funckey_no)
        lines.append(u'memorykey.%s.label = %s' % (funckey_no, funckey_dict.get(u'label', u'')))
        return lines

    def _format_funckey_blf(self, funckey_no, funckey_dict, exten_pickup_call=None):
        # Be warned that blf works only for DSS keys.
        lines = []
        lines.append(u'memorykey.%s.line = %s' % (funckey_no, funckey_dict.get(u'line', 1) - 1))
        value = funckey_dict[u'value']
        lines.append(u'memorykey.%s.value = %s' % (funckey_no, value))
        lines.append(u'memorykey.%s.label = %s' % (funckey_no, funckey_dict.get(u'label', u'')))
        lines.append(u'memorykey.%s.sub_type = blf' % funckey_no)
        if exten_pickup_call:
            lines.append(u'memorykey.%s.pickup_value = %s' % (funckey_no, exten_pickup_call))
        lines.append(u'memorykey.%s.type = 16' % funckey_no)
        return lines

    def _add_fkeys(self, raw_config, device):
        # XXX maybe rework this, a bit ugly
        lines = []
        exten_pickup_call = raw_config.get('exten_pickup_call')
        for funckey_no, funckey_dict in sorted(raw_config[u'funckeys'].iteritems(),
                                               key=itemgetter(0)):
            keynum = int(funckey_no)
            funckey_type = funckey_dict[u'type']
            if funckey_type == u'speeddial':
                lines.extend(self._format_funckey_speeddial(funckey_no, funckey_dict))
            elif funckey_type == u'blf':
                if keynum <= 10:
                    lines.extend(self._format_funckey_blf(funckey_no, funckey_dict,
                                                          exten_pickup_call))
                else:
                    logger.info('For Yealink, blf is only available on DSS keys')
                    lines.extend(self._format_funckey_speeddial(funckey_no, funckey_dict))
            elif funckey_type == u'park':
                lines.extend(self._format_funckey_call_park(funckey_no, funckey_dict))
            else:
                logger.info('Unsupported funckey type: %s', funckey_type)
                continue
            lines.append(u'')
        raw_config[u'XX_fkeys'] = u'\n'.join(lines)

    def _add_country_and_lang(self, raw_config):
        locale = raw_config.get(u'locale')
        if locale in self._LOCALE:
            (raw_config[u'XX_lang'],
             raw_config[u'XX_country'],
             raw_config[u'XX_handset_lang']) = self._LOCALE[locale]

    def _format_dst_change(self, dst_change):
        if dst_change['day'].startswith('D'):
            return u'%02d/%02d/%02d' % (dst_change['month'], dst_change['day'][1:], dst_change['time'].as_hour)
        else:
            week, weekday = map(int, dst_change['day'][1:].split('.'))
            weekday = tzinform.week_start_on_monday(weekday)
            return u'%d/%d/%d/%d' % (dst_change['month'], week, weekday, dst_change['time'].as_hours)

    def _format_tz_info(self, tzinfo):
        lines = []
        lines.append(u'local_time.time_zone = %+d' % min(max(tzinfo['utcoffset'].as_hours, -11), 12))
        if tzinfo['dst'] is None:
            lines.append(u'local_time.summer_time = 0')
        else:
            lines.append(u'local_time.summer_time = 1')
            if tzinfo['dst']['start']['day'].startswith('D'):
                lines.append(u'local_time.dst_time_type = 0')
            else:
                lines.append(u'local_time.dst_time_type = 1')
            lines.append(u'local_time.start_time = %s' % self._format_dst_change(tzinfo['dst']['start']))
            lines.append(u'local_time.end_time = %s' % self._format_dst_change(tzinfo['dst']['end']))
            lines.append(u'local_time.offset_time = %s' % tzinfo['dst']['save'].as_minutes)
        return u'\n'.join(lines)

    def _add_timezone(self, raw_config):
        if u'timezone' in raw_config:
            try:
                tzinfo = tzinform.get_timezone_info(raw_config[u'timezone'])
            except tzinform.TimezoneNotFoundError, e:
                logger.warning('Unknown timezone: %s', e)
            else:
                raw_config[u'XX_timezone'] = self._format_tz_info(tzinfo)
예제 #6
0
class BaseCiscoPlugin(StandardPlugin):
    """Base classes MUST have a '_COMMON_FILENAMES' attribute which is a
    sequence of filenames that will be generated by the common template in
    the common_configure function.

    """

    _ENCODING = 'UTF-8'
    _NB_FKEY = {
        # <model>: (<nb keys>, <nb expansion modules>)
        u'SPA941': (4, 0),
        u'SPA942': (4, 0),
        u'SPA962': (6, 2),
        u'SPA303': (3, 2),
        u'SPA501G': (8, 2),
        u'SPA502G': (0, 2),
        u'SPA504G': (4, 2),
        u'SPA508G': (8, 2),
        u'SPA509G': (12, 2),
        u'SPA512G': (0, 2),
        u'SPA514G': (4, 2),
        u'SPA525G': (5, 2),
        u'SPA525G2': (5, 2)
    }
    _DEFAULT_LOCALE = u'en_US'
    _LANGUAGE = {
        u'de_DE': u'German',
        u'en_US': u'English',
        u'es_ES': u'Spanish',
        u'fr_FR': u'French',
        u'fr_CA': u'French',
    }
    _LOCALE = {
        u'de_DE': u'de-DE',
        u'en_US': u'en-US',
        u'es_ES': u'es-ES',
        u'fr_FR': u'fr-FR',
        u'fr_CA': u'fr-CA',
    }
    _DIRECTORY_NAME = {
        u'en_US': u'Wazo Directory',
        u'fr_FR': u'Répertoire Wazo',
    }

    def __init__(self, app, plugin_dir, gen_cfg, spec_cfg):
        StandardPlugin.__init__(self, app, plugin_dir, gen_cfg, spec_cfg)

        self._tpl_helper = TemplatePluginHelper(plugin_dir)

        downloaders = FetchfwPluginHelper.new_downloaders(gen_cfg.get('proxies'))
        if 'cisco' not in downloaders:
            logger.warning('cisco downloader not found (xivo is probably not up to date); not loading plugin packages')
        else:
            fetchfw_helper = FetchfwPluginHelper(plugin_dir, downloaders)
            self.services = fetchfw_helper.services()

        self.http_service = HTTPNoListingFileService(self._tftpboot_dir)
        self.tftp_service = TFTPFileService(self._tftpboot_dir)

    dhcp_dev_info_extractor = BaseCiscoDHCPDeviceInfoExtractor()

    http_dev_info_extractor = BaseCiscoHTTPDeviceInfoExtractor()

    tftp_dev_info_extractor = BaseCiscoTFTPDeviceInfoExtractor()

    def configure_common(self, raw_config):
        tpl = self._tpl_helper.get_template('common/model.cfg.tpl')
        common_filenames = self._COMMON_FILENAMES
        for filename in common_filenames:
            dst = os.path.join(self._tftpboot_dir, filename)
            self._tpl_helper.dump(tpl, raw_config, dst, self._ENCODING)

    def _add_fkeys(self, raw_config, model):
        if model not in self._NB_FKEY:
            logger.info(u'Unknown model or model with no funckeys: %s', model)
            return
        nb_keys, nb_expmods = self._NB_FKEY[model]
        lines = []
        for funckey_no, funckey_dict in sorted(raw_config[u'funckeys'].iteritems(),
                                               key=itemgetter(0)):
            funckey_type = funckey_dict[u'type']
            value = funckey_dict[u'value']
            label = escape(funckey_dict.get(u'label', value))
            if funckey_type == u'speeddial':
                function = u'fnc=sd;ext=%s@$PROXY;nme=%s' % (value, label)
            elif funckey_type == u'blf':
                function = u'fnc=sd+blf+cp;sub=%s@$PROXY;nme=%s' % (value, label)
            else:
                logger.info('Unsupported funckey type: %s', funckey_type)
                continue
            keynum = int(funckey_no)
            if keynum <= nb_keys:
                lines.append(u'<Extension_%s_>Disabled</Extension_%s_>' %
                             (funckey_no, funckey_no))
                lines.append(u'<Short_Name_%s_>%s</Short_Name_%s_>' %
                             (funckey_no, label, funckey_no))
                lines.append(u'<Extended_Function_%s_>%s</Extended_Function_%s_>' %
                             (funckey_no, function, funckey_no))
            else:
                expmod_keynum = keynum - nb_keys - 1
                expmod_no = expmod_keynum // 32 + 1
                if expmod_no > nb_expmods:
                    logger.info('Model %s has less than %s function keys', model, funckey_no)
                else:
                    expmod_key_no = expmod_keynum % 32 + 1
                    lines.append(u'<Unit_%s_Key_%s>%s</Unit_%s_Key_%s>' %
                                 (expmod_no, expmod_key_no, function, expmod_no, expmod_key_no))
        raw_config[u'XX_fkeys'] = u'\n'.join(lines)

    def _format_dst_change(self, dst_change):
        _day = dst_change['day']
        if _day.startswith('D'):
            day = _day[1:]
            weekday = '0'
        else:
            week, weekday = _day[1:].split('.')
            weekday = tzinform.week_start_on_monday(int(weekday))
            if week == '5':
                day = '-1'
            else:
                day = (int(week) - 1) * 7 + 1

        h, m, s = dst_change['time'].as_hms
        return u'%s/%s/%s/%s:%s:%s' % (dst_change['month'], day, weekday, h, m, s)

    def _format_tzinfo(self, tzinfo):
        lines = []
        hours, minutes = tzinfo['utcoffset'].as_hms[:2]
        lines.append(u'<Time_Zone>GMT%+03d:%02d</Time_Zone>' % (hours, minutes))
        if tzinfo['dst'] is None:
            lines.append(u'<Daylight_Saving_Time_Enable>no</Daylight_Saving_Time_Enable>')
        else:
            lines.append(u'<Daylight_Saving_Time_Enable>yes</Daylight_Saving_Time_Enable>')
            h, m, s = tzinfo['dst']['save'].as_hms
            lines.append(u'<Daylight_Saving_Time_Rule>start=%s;end=%s;save=%d:%d:%s</Daylight_Saving_Time_Rule>' %
                         (self._format_dst_change(tzinfo['dst']['start']),
                          self._format_dst_change(tzinfo['dst']['end']),
                          h, m, s,
                          ))
        return u'\n'.join(lines)

    def _add_timezone(self, raw_config):
        if u'timezone' in raw_config:
            try:
                tzinfo = tzinform.get_timezone_info(raw_config[u'timezone'])
            except tzinform.TimezoneNotFoundError, e:
                logger.info('Unknown timezone: %s', e)
            else:
                raw_config[u'XX_timezone'] = self._format_tzinfo(tzinfo)
예제 #7
0
class BaseHtekPlugin(StandardPlugin):
    _ENCODING = 'UTF-8'
    _LOCALE = {
        u'de_DE': (u'German', u'Germany'),
        u'en_US': (u'English', u'United States'),
        u'en_GB': (u'English', u'Great Britain'),
        u'es_ES': (u'Spanish', u'Spain'),
        u'fr_FR': (u'French', u'France'),
        u'fr_CA': (u'French', u'United States'),
    }

    # Used for tone select
    _COUNTRIES = {
        'Custom': 0,
        'Australia': 1,
        'Austria': 2,
        'Brazil': 3,
        'Belgium': 4,
        'China': 5,
        'Chile': 6,
        'Czech': 7,
        'Denmark': 8,
        'Finland': 9,
        'France': 10,
        'Germany': 11,
        'Great Britain': 12,
        'Greece': 13,
        'Hungary': 14,
        'Lithuania': 15,
        'India': 16,
        'Italy': 17,
        'Japan': 18,
        'Mexico': 19,
        'New Zealand': 20,
        'Netherlands': 21,
        'Norway': 22,
        'Portugal': 23,
        'Spain': 24,
        'Switzerland': 25,
        'Sweden': 26,
        'Russia': 27,
        'United States': 28,
    }

    _SIP_DTMF_MODE = {
        u'RTP-out-of-band': u'0',
        u'RTP-in-band': u'1',
        u'SIP-INFO': u'2',
    }
    _SIP_TRANSPORT = {
        u'udp': u'0',
        u'tcp': u'1',
        u'tls': u'2',
    }
    _SIP_TRANSPORT_DEF = u'0'
    _NB_LINEKEYS = {
        u'UC926': 36,
        u'UC926E': 36,
        u'UC924': 28,
        u'UC924E': 28,
        u'UC923': 20,
        u'UC912': 12,
        u'UC912E': 12,
        u'UC912G': 12,
        u'UC903': 20,
        u'UC862': 14,
        u'UC860': 14,
        u'UC860P': 14,
        u'UC842': 4,
        u'UC840': 4,
        u'UC840P': 4,
        u'UC806': 4,
        u'UC806T': 4,
        u'UC804': 4,
        u'UC804T': 4,
        u'UC803': 0,
        u'UC803T': 0,
        u'UC802': 0,
        u'UC802T': 0,
    }

    _TZ_INFO = {
        (-11, 00): 105,
        (-10, 00): 2,
        (-9, 00): 3,
        (-8, 00): 6,
        (-7, 00): 10,
        (-6, 00): 14,
        (-5, 00): 18,
        (-4, 30): 19,
        (-4, 00): 20,
        (-3, 30): 26,
        (-3, 00): 30,
        (-2, 00): 31,
        (-1, 00): 32,
        (00, 00): 33,
        (1, 00): 49,
        (2, 00): 57,
        (3, 00): 73,
        (3, 30): 74,
        (4, 00): 77,
        (5, 00): 83,
        (5, 30): 84,
        (6, 00): 85,
        (7, 00): 88,
        (8, 00): 89,
        (9, 00): 93,
        (9, 30): 94,
        (10, 00): 96,
        (10, 30): 100,
        (11, 00): 101,
        (12, 00): 102,
        (12, 45): 103,
        (13, 00): 104,
    }

    def __init__(self, app, plugin_dir, gen_cfg, spec_cfg):
        StandardPlugin.__init__(self, app, plugin_dir, gen_cfg, spec_cfg)

        self._tpl_helper = TemplatePluginHelper(plugin_dir)

        downloaders = FetchfwPluginHelper.new_downloaders(
            gen_cfg.get('proxies'))
        fetchfw_helper = FetchfwPluginHelper(plugin_dir, downloaders)

        self.services = fetchfw_helper.services()
        self.http_service = HTTPNoListingFileService(self._tftpboot_dir)

    http_dev_info_extractor = BaseHtekHTTPDeviceInfoExtractor()

    def configure_common(self, raw_config):
        for filename, tpl_filename in self._COMMON_FILES:
            tpl = self._tpl_helper.get_template('common/%s' % tpl_filename)
            dst = os.path.join(self._tftpboot_dir, filename)
            self._tpl_helper.dump(tpl, raw_config, dst, self._ENCODING)

    def _update_sip_lines(self, raw_config):
        for line_no, line in raw_config[u'sip_lines'].iteritems():
            # set line number
            line[u'XX_line_no'] = int(line_no)
            # set dtmf inband transfer
            dtmf_mode = line.get(u'dtmf_mode') or raw_config.get(
                u'sip_dtmf_mode')
            if dtmf_mode in self._SIP_DTMF_MODE:
                line[u'XX_dtmf_type'] = self._SIP_DTMF_MODE[dtmf_mode]
            # set voicemail
            if u'voicemail' not in line and u'exten_voicemail' in raw_config:
                line[u'voicemail'] = raw_config[u'exten_voicemail']
            # set proxy_ip
            if u'proxy_ip' not in line:
                line[u'proxy_ip'] = raw_config[u'sip_proxy_ip']
            # set proxy_port
            if u'proxy_port' not in line and u'sip_proxy_port' in raw_config:
                line[u'proxy_port'] = raw_config[u'sip_proxy_port']

    def _gen_param_num(self, line, offset=0):
        '''Method that generates line parameter numbers for the config file

        There are several steps in the numbering of parameters that need to
        be supported.'''
        param_nb = 0
        mode_nb = 0
        limit_step1 = 5
        limit_step2 = 37
        mode_base = 20600
        param_step1_base = 41200
        param_step2_base = 20200
        param_step3_base = 23000
        line = int(line)
        if line < limit_step1:
            param_nb = param_step1_base + line - 1 + 100 * offset
            mode_nb = mode_base + line - 1
        elif line >= limit_step1 and line < limit_step2:
            param_nb = param_step2_base + offset + 5 * (line - limit_step1)
            mode_nb = mode_base + line - 1
        else:
            param_nb = param_step3_base + offset + 5 * (line - limit_step2)
            mode_nb = param_nb
        return param_nb, mode_nb

    def _add_fkeys(self, device, raw_config):
        # Setting up the line/function keys
        complete_fkeys = {}
        fkeys = raw_config[u'funckeys']
        fkey_type_assoc = {u'': 0, u'speeddial': 2, u'blf': 3, u'park': 8}

        if u'model' in device and device[u'model'] is not None and device[
                u'model'] in self._NB_LINEKEYS:
            for key_nb in range(1, self._NB_LINEKEYS[device[u'model']] + 1):
                if str(key_nb) in raw_config[u'sip_lines']:
                    sip_line = raw_config[u'sip_lines'][str(key_nb)]
                    val = {
                        u'type': 1,
                        u'value': '',
                        u'label': sip_line['number']
                    }
                else:
                    val = fkeys.get(str(key_nb), {
                        u'type': '',
                        u'value': '',
                        u'label': ''
                    })
                    val[u'type'] = fkey_type_assoc[val[u'type']]
                complete_fkeys[key_nb] = {
                    'type': {
                        'p_nb': self._gen_param_num(key_nb)[0],
                        'val': val[u'type']
                    },
                    'mode': {
                        'p_nb': self._gen_param_num(key_nb)[1]
                    },
                    'value': {
                        'p_nb': self._gen_param_num(key_nb, offset=1)[0],
                        'val': val[u'value']
                    },
                    'label': {
                        'p_nb': self._gen_param_num(key_nb, offset=2)[0],
                        'val': val[u'label']
                    },
                    'account': {
                        'p_nb': self._gen_param_num(key_nb, offset=3)[0]
                    },
                    'extension': {
                        'p_nb': self._gen_param_num(key_nb, offset=4)[0],
                        'val': val[u'value']
                    },
                }
            raw_config[u'XX_fkeys'] = complete_fkeys

    def _add_country_and_lang(self, raw_config):
        locale = raw_config.get(u'locale')
        if locale in self._LOCALE:
            (lang, country) = self._LOCALE[locale]
            (raw_config[u'XX_lang'],
             raw_config[u'XX_country']) = (lang, self._COUNTRIES[country])

    def _add_timezone(self, raw_config):
        timezone = raw_config.get(u'timezone', 'Etc/UTC')
        tz_db = tzinform.TextTimezoneInfoDB()
        tz_timezone_info = tz_db.get_timezone_info(timezone)
        tz_info = tz_timezone_info['utcoffset'].as_hms
        offset_hour = tz_info[0]
        offset_minutes = tz_info[1]

        if (offset_hour, offset_minutes) in self._TZ_INFO:
            raw_config[u'XX_timezone_code'] = self._TZ_INFO[(offset_hour,
                                                             offset_minutes)]
        else:
            raw_config[u'XX_timezone_code'] = self._TZ_INFO[(-5, 0)]

    def _add_sip_transport(self, raw_config):
        raw_config[u'XX_sip_transport'] = self._SIP_TRANSPORT.get(
            raw_config.get(u'sip_transport'), self._SIP_TRANSPORT_DEF)

    def _add_xivo_phonebook_url(self, raw_config):
        if hasattr(plugins, 'add_xivo_phonebook_url') and raw_config.get(
                u'config_version', 0) >= 1:
            plugins.add_xivo_phonebook_url(raw_config,
                                           u'htek',
                                           entry_point=u'lookup')
        else:
            self._add_xivo_phonebook_url_compat(raw_config)

    def _add_xivo_phonebook_url_compat(self, raw_config):
        hostname = raw_config.get(u'X_xivo_phonebook_ip')
        if hostname:
            raw_config[
                u'XX_xivo_phonebook_url'] = u'http://{hostname}/service/ipbx/web_services.php/phonebook/search/?name=#SEARCH'.format(
                    hostname=hostname)

    _SENSITIVE_FILENAME_REGEX = re.compile(r'^cfg[0-9a-f]{12}\.xml')

    def _dev_specific_filename(self, device):
        # Return the device specific filename (not pathname) of device
        fmted_mac = format_mac(device[u'mac'], separator='')
        return 'cfg' + fmted_mac + '.xml'

    def _check_config(self, raw_config):
        if u'http_port' not in raw_config:
            raise RawConfigError('only support configuration via HTTP')

    def _check_device(self, device):
        if u'mac' not in device:
            raise Exception('MAC address needed for device configuration')

    def configure(self, device, raw_config):
        self._check_config(raw_config)
        self._check_device(device)
        filename = self._dev_specific_filename(device)
        tpl = self._tpl_helper.get_dev_template(filename, device)

        self._add_fkeys(device, raw_config)
        self._add_country_and_lang(raw_config)
        self._add_timezone(raw_config)
        self._add_sip_transport(raw_config)
        self._update_sip_lines(raw_config)
        self._add_xivo_phonebook_url(raw_config)
        raw_config[u'XX_options'] = device.get(u'options', {})

        path = os.path.join(self._tftpboot_dir, filename)
        self._tpl_helper.dump(tpl, raw_config, path, self._ENCODING)

    def deconfigure(self, device):
        path = os.path.join(self._tftpboot_dir,
                            self._dev_specific_filename(device))
        try:
            os.remove(path)
        except OSError, e:
            # ignore
            logger.info('error while removing file: %s', e)
예제 #8
0
class BaseCiscoSipPlugin(StandardPlugin):
    """Base classes MUST have a '_COMMON_FILENAMES' attribute which is a
    sequence of filenames that will be generated by the common template in
    the common_configure function.

    """

    _ENCODING = 'UTF-8'
    _NB_FKEY = {
        # <model>: (<nb keys>, <nb expansion modules>)
        u'ATA191': (0, 0),
        u'ATA192': (0, 0),
    }
    _DEFAULT_LOCALE = u'en_US'
    _LANGUAGE = {
        u'de_DE': u'German',
        u'en_US': u'English',
        u'es_ES': u'Spanish',
        u'fr_FR': u'French',
        u'fr_CA': u'French',
    }
    _LOCALE = {
        u'de_DE': u'de-DE',
        u'en_US': u'en-US',
        u'es_ES': u'es-ES',
        u'fr_FR': u'fr-FR',
        u'fr_CA': u'fr-CA',
    }
    _DIRECTORY_NAME = {
        u'en_US': u'Wazo Directory',
        u'fr_FR': u'Répertoire Wazo',
    }

    def __init__(self, app, plugin_dir, gen_cfg, spec_cfg):
        StandardPlugin.__init__(self, app, plugin_dir, gen_cfg, spec_cfg)

        downloaders = FetchfwPluginHelper.new_downloaders(
            gen_cfg.get('proxies'))
        fetchfw_helper = FetchfwPluginHelper(plugin_dir, downloaders)

        self.services = fetchfw_helper.services()

        self._tpl_helper = TemplatePluginHelper(plugin_dir)

        self.http_service = HTTPNoListingFileService(self._tftpboot_dir)
        self.tftp_service = TFTPFileService(self._tftpboot_dir)

    dhcp_dev_info_extractor = BaseCiscoDHCPDeviceInfoExtractor()

    http_dev_info_extractor = BaseCiscoHTTPDeviceInfoExtractor()

    tftp_dev_info_extractor = BaseCiscoTFTPDeviceInfoExtractor()

    def configure_common(self, raw_config):
        tpl = self._tpl_helper.get_template('common/model.cfg.tpl')
        common_filenames = self._COMMON_FILENAMES
        for filename in common_filenames:
            dst = os.path.join(self._tftpboot_dir, filename)
            self._tpl_helper.dump(tpl, raw_config, dst, self._ENCODING)

    def _add_fkeys(self, raw_config, model):
        if model not in self._NB_FKEY:
            logger.info(u'Unknown model or model with no funckeys: %s', model)
            return
        nb_keys, nb_expmods = self._NB_FKEY[model]
        lines = []
        for funckey_no, funckey_dict in sorted(
                raw_config[u'funckeys'].iteritems(), key=itemgetter(0)):
            funckey_type = funckey_dict[u'type']
            value = funckey_dict[u'value']
            label = escape(funckey_dict.get(u'label', value))
            if funckey_type == u'speeddial':
                function = u'fnc=sd;ext=%s@$PROXY;nme=%s' % (value, label)
            elif funckey_type == u'blf':
                function = u'fnc=sd+blf+cp;sub=%s@$PROXY;nme=%s' % (value,
                                                                    label)
            else:
                logger.info('Unsupported funckey type: %s', funckey_type)
                continue
            keynum = int(funckey_no)
            if keynum <= nb_keys:
                lines.append(u'<Extension_%s_>Disabled</Extension_%s_>' %
                             (funckey_no, funckey_no))
                lines.append(u'<Short_Name_%s_>%s</Short_Name_%s_>' %
                             (funckey_no, label, funckey_no))
                lines.append(
                    u'<Extended_Function_%s_>%s</Extended_Function_%s_>' %
                    (funckey_no, function, funckey_no))
            else:
                expmod_keynum = keynum - nb_keys - 1
                expmod_no = expmod_keynum // 32 + 1
                if expmod_no > nb_expmods:
                    logger.info('Model %s has less than %s function keys',
                                model, funckey_no)
                else:
                    expmod_key_no = expmod_keynum % 32 + 1
                    lines.append(u'<Unit_%s_Key_%s>%s</Unit_%s_Key_%s>' %
                                 (expmod_no, expmod_key_no, function,
                                  expmod_no, expmod_key_no))
        raw_config[u'XX_fkeys'] = u'\n'.join(lines)

    def _format_dst_change(self, dst_change):
        _day = dst_change['day']
        if _day.startswith('D'):
            day = _day[1:]
            weekday = '0'
        else:
            week, weekday = _day[1:].split('.')
            weekday = tzinform.week_start_on_monday(int(weekday))
            if week == '5':
                day = '-1'
            else:
                day = (int(week) - 1) * 7 + 1

        h, m, s = dst_change['time'].as_hms
        return u'%s/%s/%s/%s:%s:%s' % (dst_change['month'], day, weekday, h, m,
                                       s)

    def _format_tzinfo(self, tzinfo):
        lines = []
        hours, minutes = tzinfo['utcoffset'].as_hms[:2]
        lines.append(u'<Time_Zone>GMT%+03d:%02d</Time_Zone>' %
                     (hours, minutes))
        if tzinfo['dst'] is None:
            lines.append(
                u'<Daylight_Saving_Time_Enable>no</Daylight_Saving_Time_Enable>'
            )
        else:
            lines.append(
                u'<Daylight_Saving_Time_Enable>yes</Daylight_Saving_Time_Enable>'
            )
            h, m, s = tzinfo['dst']['save'].as_hms
            lines.append(
                u'<Daylight_Saving_Time_Rule>start=%s;end=%s;save=%d:%d:%s</Daylight_Saving_Time_Rule>'
                % (
                    self._format_dst_change(tzinfo['dst']['start']),
                    self._format_dst_change(tzinfo['dst']['end']),
                    h,
                    m,
                    s,
                ))
        return u'\n'.join(lines)

    def _add_timezone(self, raw_config):
        if u'timezone' in raw_config:
            try:
                tzinfo = tzinform.get_timezone_info(raw_config[u'timezone'])
            except tzinform.TimezoneNotFoundError, e:
                logger.info('Unknown timezone: %s', e)
            else:
                raw_config[u'XX_timezone'] = self._format_tzinfo(tzinfo)
예제 #9
0
class BaseSnomPlugin(StandardPlugin):
    _ENCODING = "UTF-8"
    _LOCALE = {
        u"de_DE": (u"Deutsch", u"GER"),
        u"en_US": (u"English", u"USA"),
        u"es_ES": (u"Espanol", u"ESP"),
        u"fr_FR": (u"Francais", u"FRA"),
        u"fr_CA": (u"Francais", u"USA"),
        u"it_IT": (u"Italiano", u"ITA"),
        u"nl_NL": (u"Dutch", u"NLD"),
    }
    _SIP_DTMF_MODE = {u"RTP-in-band": u"off", u"RTP-out-of-band": u"off", u"SIP-INFO": u"sip_info_only"}
    _XX_DICT_DEF = u"en"
    _XX_DICT = {u"en": {u"remote_directory": u"Directory"}, u"fr": {u"remote_directory": u"Annuaire"}}

    def __init__(self, app, plugin_dir, gen_cfg, spec_cfg):
        StandardPlugin.__init__(self, app, plugin_dir, gen_cfg, spec_cfg)

        self._tpl_helper = TemplatePluginHelper(plugin_dir)

        downloaders = FetchfwPluginHelper.new_downloaders(gen_cfg.get("proxies"))
        fetchfw_helper = FetchfwPluginHelper(plugin_dir, downloaders)

        self.services = fetchfw_helper.services()
        self.http_service = HTTPNoListingFileService(self._tftpboot_dir)

    http_dev_info_extractor = BaseSnomHTTPDeviceInfoExtractor()

    def _common_templates(self):
        yield ("common/gui_lang.xml.tpl", "gui_lang.xml")
        yield ("common/web_lang.xml.tpl", "web_lang.xml")
        for tpl_format, file_format in [
            ("common/snom%s.htm.tpl", "snom%s.htm"),
            ("common/snom%s.xml.tpl", "snom%s.xml"),
            ("common/snom%s-firmware.xml.tpl", "snom%s-firmware.xml"),
        ]:
            for model in self._MODELS:
                yield tpl_format % model, file_format % model

    def configure_common(self, raw_config):
        for tpl_filename, filename in self._common_templates():
            tpl = self._tpl_helper.get_template(tpl_filename)
            dst = os.path.join(self._tftpboot_dir, filename)
            self._tpl_helper.dump(tpl, raw_config, dst, self._ENCODING)

    def _update_sip_lines(self, raw_config):
        proxy_ip = raw_config.get(u"sip_proxy_ip")
        backup_proxy_ip = raw_config.get(u"sip_backup_proxy_ip")
        voicemail = raw_config.get(u"exten_voicemail")
        for line in raw_config[u"sip_lines"].itervalues():
            if proxy_ip:
                line.setdefault(u"proxy_ip", proxy_ip)
            if backup_proxy_ip:
                line.setdefault(u"backup_proxy_ip", backup_proxy_ip)
            if voicemail:
                line.setdefault(u"voicemail", voicemail)

    def _add_fkeys(self, raw_config, model):
        lines = []
        for funckey_no, funckey_dict in sorted(raw_config[u"funckeys"].iteritems(), key=itemgetter(0)):
            funckey_type = funckey_dict[u"type"]
            if funckey_type == u"speeddial":
                type_ = u"speed"
                suffix = ""
            elif funckey_type == u"park":
                if model in [u"710", u"715", u"720", u"725", u"760", u"D765"]:
                    type_ = u"orbit"
                    suffix = ""
                else:
                    type_ = u"speed"
                    suffix = ""
            elif funckey_type == u"blf":
                if u"exten_pickup_call" in raw_config:
                    type_ = u"blf"
                    suffix = "|%s" % raw_config[u"exten_pickup_call"]
                else:
                    logger.warning("Could not set funckey %s: no exten_pickup_call", funckey_no)
                    continue
            else:
                logger.info("Unsupported funckey type: %s", funckey_type)
                continue
            value = funckey_dict[u"value"]
            label = escape(funckey_dict.get(u"label", value))
            fkey_value = self._format_fkey_value(type_, value, suffix)
            lines.append(
                u'<fkey idx="%d" label="%s" context="active" perm="R">%s</fkey>'
                % (int(funckey_no) - 1, label, fkey_value)
            )
        raw_config[u"XX_fkeys"] = u"\n".join(lines)

    def _format_fkey_value(self, fkey_type, value, suffix):
        return "%s %s%s" % (fkey_type, value, suffix)

    def _add_lang(self, raw_config):
        if u"locale" in raw_config:
            locale = raw_config[u"locale"]
            if locale in self._LOCALE:
                raw_config[u"XX_lang"] = self._LOCALE[locale]

    def _format_dst_change(self, dst_change):
        fmted_time = u"%02d:%02d:%02d" % tuple(dst_change["time"].as_hms)
        day = dst_change["day"]
        if day.startswith("D"):
            return u"%02d.%02d %s" % (int(day[1:]), dst_change["month"], fmted_time)
        else:
            week, weekday = map(int, day[1:].split("."))
            weekday = tzinform.week_start_on_monday(weekday)
            return u"%02d.%02d.%02d %s" % (dst_change["month"], week, weekday, fmted_time)

    def _format_tzinfo(self, tzinfo):
        lines = []
        lines.append(u'<timezone perm="R"></timezone>')
        lines.append(u'<utc_offset perm="R">%+d</utc_offset>' % tzinfo["utcoffset"].as_seconds)
        if tzinfo["dst"] is None:
            lines.append(u'<dst perm="R"></dst>')
        else:
            lines.append(
                u'<dst perm="R">%d %s %s</dst>'
                % (
                    tzinfo["dst"]["save"].as_seconds,
                    self._format_dst_change(tzinfo["dst"]["start"]),
                    self._format_dst_change(tzinfo["dst"]["end"]),
                )
            )
        return u"\n".join(lines)

    def _add_timezone(self, raw_config):
        if u"timezone" in raw_config:
            try:
                tzinfo = tzinform.get_timezone_info(raw_config[u"timezone"])
            except tzinform.TimezoneNotFoundError, e:
                logger.warning("Unknown timezone %s: %s", raw_config[u"timezone"], e)
            else:
                raw_config[u"XX_timezone"] = self._format_tzinfo(tzinfo)
예제 #10
0
class BaseFanvilPlugin(StandardPlugin):
    _ENCODING = 'UTF-8'
    _LOCALE = {}
    _TZ_INFO = {}
    _SIP_DTMF_MODE = {
        u'RTP-in-band': u'0',
        u'RTP-out-of-band': u'1',
        u'SIP-INFO': u'2',
    }
    _SIP_TRANSPORT = {
        u'udp': u'0',
        u'tcp': u'1',
        u'tls': u'3',
    }
    _DIRECTORY_KEY = {
        u'en': u'Directory',
        u'fr': u'Annuaire',
    }

    def __init__(self, app, plugin_dir, gen_cfg, spec_cfg):
        StandardPlugin.__init__(self, app, plugin_dir, gen_cfg, spec_cfg)
        # update to use the non-standard tftpboot directory
        self._base_tftpboot_dir = self._tftpboot_dir
        self._tftpboot_dir = os.path.join(self._tftpboot_dir, 'Fanvil')

        self._tpl_helper = TemplatePluginHelper(plugin_dir)

        downloaders = FetchfwPluginHelper.new_downloaders(
            gen_cfg.get('proxies'))
        fetchfw_helper = FetchfwPluginHelper(plugin_dir, downloaders)
        # update to use the non-standard tftpboot directory
        fetchfw_helper.root_dir = self._tftpboot_dir

        self.services = fetchfw_helper.services()
        self.http_service = HTTPNoListingFileService(self._base_tftpboot_dir)

    def _dev_specific_filename(self, device):
        # Return the device specific filename (not pathname) of device
        fmted_mac = format_mac(device[u'mac'], separator='', uppercase=False)
        return fmted_mac + '.cfg'

    def _check_config(self, raw_config):
        if u'http_port' not in raw_config:
            raise RawConfigError('only support configuration via HTTP')

    def _check_device(self, device):
        if u'mac' not in device:
            raise Exception('MAC address needed for device configuration')

    def configure(self, device, raw_config):
        self._check_config(raw_config)
        self._check_device(device)
        self._check_lines_password(raw_config)
        self._add_timezone(device, raw_config)
        self._add_locale(device, raw_config)
        self._add_sip_transport(raw_config)
        self._update_lines(raw_config)
        self._add_fkeys(raw_config)
        self._add_phonebook_url(raw_config)
        filename = self._dev_specific_filename(device)
        tpl = self._tpl_helper.get_dev_template(filename, device)

        path = os.path.join(self._tftpboot_dir, filename)
        self._tpl_helper.dump(tpl, raw_config, path, self._ENCODING)

    def deconfigure(self, device):
        self._remove_configuration_file(device)

    def configure_common(self, raw_config):
        for filename, (_, fw_filename,
                       tpl_filename) in self._COMMON_FILES.iteritems():
            tpl = self._tpl_helper.get_template('common/%s' % tpl_filename)
            dst = os.path.join(self._tftpboot_dir, filename)
            raw_config[u'XX_fw_filename'] = fw_filename
            self._tpl_helper.dump(tpl, raw_config, dst, self._ENCODING)

    def _remove_configuration_file(self, device):
        path = os.path.join(self._tftpboot_dir,
                            self._dev_specific_filename(device))
        try:
            os.remove(path)
        except OSError as e:
            logger.info('error while removing configuration file: %s', e)

    if hasattr(synchronize, 'standard_sip_synchronize'):

        def synchronize(self, device, raw_config):
            return synchronize.standard_sip_synchronize(device)

    else:
        # backward compatibility with older wazo-provd server
        def synchronize(self, device, raw_config):
            try:
                ip = device[u'ip'].encode('ascii')
            except KeyError:
                return defer.fail(
                    Exception('IP address needed for device synchronization'))
            else:
                sync_service = synchronize.get_sync_service()
                if sync_service is None or sync_service.TYPE != 'AsteriskAMI':
                    return defer.fail(
                        Exception('Incompatible sync service: %s' %
                                  sync_service))
                else:
                    return threads.deferToThread(sync_service.sip_notify, ip,
                                                 'check-sync')

    def get_remote_state_trigger_filename(self, device):
        if u'mac' not in device:
            return None

        return self._dev_specific_filename(device)

    def _check_lines_password(self, raw_config):
        for line in raw_config[u'sip_lines'].itervalues():
            if line[u'password'] == u'autoprov':
                line[u'password'] = u''

    def _extract_dst_change(self, dst_change):
        lines = {}
        lines['month'] = dst_change['month']
        lines['hour'] = min(dst_change['time'].as_hours, 23)
        if dst_change['day'].startswith('D'):
            lines['dst_wday'] = dst_change['day'][1:]
        else:
            week, weekday = dst_change['day'][1:].split('.')
            if week == '5':
                lines['dst_week'] = -1
            else:
                lines['dst_week'] = week
            lines['dst_wday'] = weekday
        return lines

    def _extract_tzinfo(self, device, tzinfo):
        tz_all = {}
        utc = tzinfo['utcoffset'].as_hours
        utc_list = self._TZ_INFO[utc]
        for time_zone_name, time_zone in utc_list:
            tz_all['time_zone'] = time_zone
            tz_all['time_zone_name'] = time_zone_name

        if tzinfo['dst'] is None:
            tz_all['enable_dst'] = False
        else:
            tz_all['enable_dst'] = True
            tz_all['dst_min_offset'] = min(tzinfo['dst']['save'].as_minutes,
                                           60)
            tz_all['dst_start'] = self._extract_dst_change(
                tzinfo['dst']['start'])
            tz_all['dst_end'] = self._extract_dst_change(tzinfo['dst']['end'])
        return tz_all

    def _add_timezone(self, device, raw_config):
        if u'timezone' in raw_config:
            try:
                tzinfo = tzinform.get_timezone_info(raw_config[u'timezone'])
            except tzinform.TimezoneNotFoundError, e:
                logger.info('Unknown timezone: %s', e)
            else:
                raw_config[u'XX_timezone'] = self._extract_tzinfo(
                    device, tzinfo)
예제 #11
0
class BaseFanvilPlugin(StandardPlugin):
    _ENCODING = 'UTF-8'

    def __init__(self, app, plugin_dir, gen_cfg, spec_cfg):
        StandardPlugin.__init__(self, app, plugin_dir, gen_cfg, spec_cfg)
        # update to use the non-standard tftpboot directory
        self._base_tftpboot_dir = self._tftpboot_dir
        self._tftpboot_dir = os.path.join(self._tftpboot_dir, 'Fanvil')

        self._tpl_helper = TemplatePluginHelper(plugin_dir)

        downloaders = FetchfwPluginHelper.new_downloaders(gen_cfg.get('proxies'))
        fetchfw_helper = FetchfwPluginHelper(plugin_dir, downloaders)
        # update to use the non-standard tftpboot directory
        fetchfw_helper.root_dir = self._tftpboot_dir

        self.services = fetchfw_helper.services()
        self.http_service = HTTPNoListingFileService(self._base_tftpboot_dir)

    http_dev_info_extractor = BaseFanvilHTTPDeviceInfoExtractor()

    def _dev_specific_filename(self, device):
        # Return the device specific filename (not pathname) of device
        fmted_mac = format_mac(device[u'mac'], separator='', uppercase=False)
        return fmted_mac + '.cfg'

    def _check_config(self, raw_config):
        if u'http_port' not in raw_config:
            raise RawConfigError('only support configuration via HTTP')

    def _check_device(self, device):
        if u'mac' not in device:
            raise Exception('MAC address needed for device configuration')

    def configure(self, device, raw_config):
        self._check_config(raw_config)
        self._check_device(device)
        self._check_lines_password(raw_config)
        self._add_timezone(raw_config)
        self._add_locale(raw_config)
        self._add_fkeys(raw_config)
        filename = self._dev_specific_filename(device)
        tpl = self._tpl_helper.get_dev_template(filename, device)

        path = os.path.join(self._tftpboot_dir, filename)
        self._tpl_helper.dump(tpl, raw_config, path, self._ENCODING)

    def deconfigure(self, device):
        self._remove_configuration_file(device)

    def configure_common(self, raw_config):
        for filename, fw_filename, tpl_filename in self._COMMON_FILES:
            tpl = self._tpl_helper.get_template('common/%s' % tpl_filename)
            dst = os.path.join(self._tftpboot_dir, filename)
            raw_config[u'XX_fw_filename'] = fw_filename
            self._tpl_helper.dump(tpl, raw_config, dst, self._ENCODING)

    def _remove_configuration_file(self, device):
        path = os.path.join(self._tftpboot_dir, self._dev_specific_filename(device))
        try:
            os.remove(path)
        except OSError as e:
            logger.info('error while removing configuration file: %s', e)

    if hasattr(synchronize, 'standard_sip_synchronize'):
        def synchronize(self, device, raw_config):
            return synchronize.standard_sip_synchronize(device)

    else:
        # backward compatibility with older xivo-provd server
        def synchronize(self, device, raw_config):
            try:
                ip = device[u'ip'].encode('ascii')
            except KeyError:
                return defer.fail(Exception('IP address needed for device synchronization'))
            else:
                sync_service = synchronize.get_sync_service()
                if sync_service is None or sync_service.TYPE != 'AsteriskAMI':
                    return defer.fail(Exception('Incompatible sync service: %s' % sync_service))
                else:
                    return threads.deferToThread(sync_service.sip_notify, ip, 'check-sync')

    def get_remote_state_trigger_filename(self, device):
        if u'mac' not in device:
            return None

        return self._dev_specific_filename(device)

    def _check_lines_password(self, raw_config):
        for line in raw_config[u'sip_lines'].itervalues():
            if line[u'password'] == u'autoprov':
                line[u'password'] = u''

    def _format_dst_change(self, suffix, dst_change):
        lines = []
        lines.append(u'<DST_%s_Mon>%d</DST_%s_Mon>' % (suffix, dst_change['month'], suffix))
        lines.append(u'<DST_%s_Hour>%d</DST_%s_Hour>' % (suffix, min(dst_change['time'].as_hours, 23), suffix))
        if dst_change['day'].startswith('D'):
            lines.append(u'<DST_%s_Wday>%s</DST_%s_Wday>' % (suffix, dst_change['day'][1:], suffix))
        else:
            week, weekday = dst_change['day'][1:].split('.')
            if week == '5':
                lines.append(u'<DST_%s_Week>-1</DST_%s_Week>' % (suffix, suffix))
            else:
                lines.append(u'<DST_%s_Week>%s</DST_%s_Week>' % (suffix, week, suffix))
            lines.append(u'<DST_%s_Wday>%s</DST_%s_Wday>' % (suffix, weekday, suffix))
        lines.append(u'<DST_%s_Min>0</DST_%s_Min>' % (suffix, suffix))
        return lines

    def _format_tzinfo(self, tzinfo):
        lines = []
        utc = tzinfo['utcoffset'].as_hours
        utc_list = TZ_INFO[utc]
        for time_zone_name, time_zone in utc_list:
            lines.append(u'<Time_Zone>%s</Time_Zone>' % (time_zone))
            lines.append(u'<Time_Zone_Name>%s</Time_Zone_Name>' % (time_zone_name))    
        if tzinfo['dst'] is None:
            lines.append(u'<Enable_DST>0</Enable_DST>')
        else:
            lines.append(u'<Enable_DST>2</Enable_DST>')
            lines.append(u'<DST_Min_Offset>%d</DST_Min_Offset>' % (min(tzinfo['dst']['save'].as_minutes, 60)))
            lines.extend(self._format_dst_change('Start', tzinfo['dst']['start']))
            lines.extend(self._format_dst_change('End', tzinfo['dst']['end']))
        return u'\n'.join(lines)

    def _add_timezone(self, raw_config):
        if u'timezone' in raw_config:
            try:
                tzinfo = tzinform.get_timezone_info(raw_config[u'timezone'])
            except tzinform.TimezoneNotFoundError, e:
                logger.info('Unknown timezone: %s', e)
            else:
                raw_config[u'XX_timezone'] = self._format_tzinfo(tzinfo)
예제 #12
0
class BasePanasonicPlugin(StandardPlugin):
    _ENCODING = 'UTF-8'

    def __init__(self, app, plugin_dir, gen_cfg, spec_cfg):
        StandardPlugin.__init__(self, app, plugin_dir, gen_cfg, spec_cfg)
        # update to use the non-standard tftpboot directory
        self._base_tftpboot_dir = self._tftpboot_dir
        self._tftpboot_dir = os.path.join(self._tftpboot_dir, 'Panasonic')

        self._tpl_helper = TemplatePluginHelper(plugin_dir)

        downloaders = FetchfwPluginHelper.new_downloaders(
            gen_cfg.get('proxies'))
        fetchfw_helper = FetchfwPluginHelper(plugin_dir, downloaders)
        # update to use the non-standard tftpboot directory
        fetchfw_helper.root_dir = self._tftpboot_dir

        self.services = fetchfw_helper.services()
        self.http_service = HTTPNoListingFileService(self._base_tftpboot_dir)

    http_dev_info_extractor = BasePanasonicHTTPDeviceInfoExtractor()

    def _dev_specific_filename(self, device):
        # Return the device specific filename (not pathname) of device
        fmted_mac = format_mac(device[u'mac'], separator='', uppercase=True)
        return 'Config' + fmted_mac + '.cfg'

    def _check_config(self, raw_config):
        if u'http_port' not in raw_config:
            raise RawConfigError('only support configuration via HTTP')

    def _check_device(self, device):
        if u'mac' not in device:
            raise Exception('MAC address needed for device configuration')

    def _common_templates(self):
        for tpl_format, file_format in [
            ('common/%s.tpl', '%s.cfg'),
        ]:
            for model in self._MODELS:
                yield tpl_format % model, file_format % model

    def configure_common(self, raw_config):
        for tpl_filename, filename in self._common_templates():
            tpl = self._tpl_helper.get_template(tpl_filename)
            dst = os.path.join(self._base_tftpboot_dir, filename)
            self._tpl_helper.dump(tpl, raw_config, dst, self._ENCODING)

    def configure(self, device, raw_config):
        self._check_config(raw_config)
        self._check_device(device)
        filename = self._dev_specific_filename(device)
        tpl = self._tpl_helper.get_dev_template(filename, device)

        path = os.path.join(self._tftpboot_dir, filename)
        self._tpl_helper.dump(tpl, raw_config, path, self._ENCODING)

    def deconfigure(self, device):
        self._remove_configuration_file(device)

    def _remove_configuration_file(self, device):
        path = os.path.join(self._tftpboot_dir,
                            self._dev_specific_filename(device))
        try:
            os.remove(path)
        except OSError as e:
            logger.info('error while removing configuration file: %s', e)

    if hasattr(synchronize, 'standard_sip_synchronize'):

        def synchronize(self, device, raw_config):
            return synchronize.standard_sip_synchronize(device)

    else:
        # backward compatibility with older xivo-provd server
        def synchronize(self, device, raw_config):
            try:
                ip = device[u'ip'].encode('ascii')
            except KeyError:
                return defer.fail(
                    Exception('IP address needed for device synchronization'))
            else:
                sync_service = synchronize.get_sync_service()
                if sync_service is None or sync_service.TYPE != 'AsteriskAMI':
                    return defer.fail(
                        Exception('Incompatible sync service: %s' %
                                  sync_service))
                else:
                    return threads.deferToThread(sync_service.sip_notify, ip,
                                                 'check-sync')

    def get_remote_state_trigger_filename(self, device):
        if u'mac' not in device:
            return None

        return self._dev_specific_filename(device)
예제 #13
0
class BaseSnomPlugin(StandardPlugin):
    _ENCODING = 'UTF-8'
    _LOCALE = {
        u'de_DE': (u'Deutsch', u'GER'),
        u'en_US': (u'English', u'USA'),
        u'es_ES': (u'Espanol', u'ESP'),
        u'fr_FR': (u'Francais', u'FRA'),
        u'fr_CA': (u'Francais', u'USA'),
        u'it_IT': (u'Italiano', u'ITA'),
        u'nl_NL': (u'Dutch', u'NLD'),
    }
    _SIP_DTMF_MODE = {
        u'RTP-in-band': u'off',
        u'RTP-out-of-band': u'off',
        u'SIP-INFO': u'sip_info_only'
    }
    _XX_DICT_DEF = u'en'
    _XX_DICT = {
        u'en': {
            u'remote_directory': u'Directory',
        },
        u'fr': {
            u'remote_directory': u'Annuaire',
        },
    }

    def __init__(self, app, plugin_dir, gen_cfg, spec_cfg):
        StandardPlugin.__init__(self, app, plugin_dir, gen_cfg, spec_cfg)

        self._tpl_helper = TemplatePluginHelper(plugin_dir)

        downloaders = FetchfwPluginHelper.new_downloaders(
            gen_cfg.get('proxies'))
        fetchfw_helper = FetchfwPluginHelper(plugin_dir, downloaders)

        self.services = fetchfw_helper.services()
        self.http_service = HTTPNoListingFileService(self._tftpboot_dir)

    http_dev_info_extractor = BaseSnomDECTHTTPDeviceInfoExtractor()

    def _common_templates(self):
        yield ('common/snom-general.xml.tpl', 'snom-general.xml')
        for tpl_format, file_format in [
            ('common/snom%s.htm.tpl', 'snom%s.htm'),
            ('common/snom%s.xml.tpl', 'snom%s.xml'),
            ('common/snom%s-firmware.xml.tpl', 'snom%s-firmware.xml')
        ]:
            for model in self._MODELS:
                yield tpl_format % model, file_format % model

    def configure_common(self, raw_config):
        for tpl_filename, filename in self._common_templates():
            tpl = self._tpl_helper.get_template(tpl_filename)
            dst = os.path.join(self._tftpboot_dir, filename)
            self._tpl_helper.dump(tpl, raw_config, dst, self._ENCODING)

    def _update_sip_lines(self, raw_config):
        proxy_ip = raw_config.get(u'sip_proxy_ip')
        backup_proxy_ip = raw_config.get(u'sip_backup_proxy_ip')
        voicemail = raw_config.get(u'exten_voicemail')
        for line in raw_config[u'sip_lines'].itervalues():
            if proxy_ip:
                line.setdefault(u'proxy_ip', proxy_ip)
            if backup_proxy_ip:
                line.setdefault(u'backup_proxy_ip', backup_proxy_ip)
            if voicemail:
                line.setdefault(u'voicemail', voicemail)
            # set SIP server to use
            server_id = raw_config['XX_servers'].get(
                (line.get(u'proxy_ip'), line.get(u'proxy_port', 5060)),
                {}).get('id')
            line[u'XX_server_id'] = server_id or 1

    def _add_sip_servers(self, raw_config):
        servers = dict()
        server_number = 1
        for line_no, line in raw_config[u'sip_lines'].iteritems():
            proxy_ip = line.get(u'proxy_ip') or raw_config.get(u'sip_proxy_ip')
            proxy_port = line.get(u'proxy_port') or raw_config.get(
                u'sip_proxy_port')
            backup_proxy_ip = line.get(u'backup_proxy_ip') or raw_config.get(
                u'sip_backup_proxy_ip')
            backup_proxy_port = line.get(
                u'backup_proxy_port') or raw_config.get(
                    u'sip_backup_proxy_port')
            dtmf_mode = self._SIP_DTMF_MODE.get(
                line.get(u'dtmf_mode') or raw_config.get(u'sip_dtmf_mode'),
                'off',
            )
            if (proxy_ip, proxy_port) not in servers:
                servers[(proxy_ip, proxy_port)] = {
                    u'id': server_number,
                    u'proxy_ip': proxy_ip,
                    u'proxy_port': proxy_port,
                    u'backup_proxy_ip': backup_proxy_ip,
                    u'backup_proxy_port': backup_proxy_port,
                    u'dtmf_mode': dtmf_mode,
                }
            server_number += 1
            if server_number > 10:
                logger.warning('Maximum number of valid server reached')
        raw_config[u'XX_servers'] = servers

    def _format_fkey_value(self, fkey_type, value, suffix):
        return '%s %s%s' % (fkey_type, value, suffix)

    def _add_lang(self, raw_config):
        if u'locale' in raw_config:
            locale = raw_config[u'locale']
            if locale in self._LOCALE:
                raw_config[u'XX_lang'] = self._LOCALE[locale]

    def _add_user_dtmf_info(self, raw_config):
        dtmf_mode = raw_config.get(u'sip_dtmf_mode')
        for line in raw_config[u'sip_lines'].itervalues():
            cur_dtmf_mode = line.get(u'dtmf_mode', dtmf_mode)
            line[u'XX_user_dtmf_info'] = self._SIP_DTMF_MODE.get(
                cur_dtmf_mode, u'off')

    def _add_xivo_phonebook_url(self, raw_config):
        if hasattr(plugins, 'add_xivo_phonebook_url') and raw_config.get(
                u'config_version', 0) >= 1:
            plugins.add_xivo_phonebook_url(raw_config, u'snom')
        else:
            self._add_xivo_phonebook_url_compat(raw_config)

    def _add_xivo_phonebook_url_compat(self, raw_config):
        hostname = raw_config.get(u'X_xivo_phonebook_ip')
        if hostname:
            raw_config[
                u'XX_xivo_phonebook_url'] = u'http://{hostname}/service/ipbx/web_services.php/phonebook/search/'.format(
                    hostname=hostname)

    def _gen_xx_dict(self, raw_config):
        xx_dict = self._XX_DICT[self._XX_DICT_DEF]
        if u'locale' in raw_config:
            locale = raw_config[u'locale']
            lang = locale.split('_', 1)[0]
            if lang in self._XX_DICT:
                xx_dict = self._XX_DICT[lang]
        return xx_dict

    _SENSITIVE_FILENAME_REGEX = re.compile(r'^[0-9A-F]{12}\.xml')

    def _dev_specific_filenames(self, device):
        # Return a tuple (htm filename, xml filename)
        fmted_mac = format_mac(device[u'mac'], separator='', uppercase=True)
        return 'snom%s-%s.htm' % (device[u'model'],
                                  fmted_mac), fmted_mac + '.xml'

    def _check_config(self, raw_config):
        if u'http_port' not in raw_config:
            raise RawConfigError('only support configuration via HTTP')

    def _check_device(self, device):
        if u'mac' not in device:
            raise Exception('MAC address needed for device configuration')
        # model is needed since filename has model name in it.
        if u'model' not in device:
            raise Exception('model needed for device configuration')

    def configure(self, device, raw_config):
        self._check_config(raw_config)
        self._check_device(device)
        htm_filename, xml_filename = self._dev_specific_filenames(device)

        # generate xml file
        tpl = self._tpl_helper.get_dev_template(xml_filename, device)

        self._add_sip_servers(raw_config)
        self._update_sip_lines(raw_config)
        self._add_lang(raw_config)
        self._add_xivo_phonebook_url(raw_config)
        raw_config[u'XX_dict'] = self._gen_xx_dict(raw_config)
        raw_config[u'XX_options'] = device.get(u'options', {})

        path = os.path.join(self._tftpboot_dir, xml_filename)
        self._tpl_helper.dump(tpl, raw_config, path, self._ENCODING)

        # generate htm file
        tpl = self._tpl_helper.get_template('other/base.htm.tpl')

        raw_config[u'XX_xml_filename'] = xml_filename

        path = os.path.join(self._tftpboot_dir, htm_filename)
        self._tpl_helper.dump(tpl, raw_config, path, self._ENCODING)

    def deconfigure(self, device):
        for filename in self._dev_specific_filenames(device):
            try:
                os.remove(os.path.join(self._tftpboot_dir, filename))
            except OSError, e:
                # ignore
                logger.info('error while removing file: %s', e)
예제 #14
0
class BasePanasonicPlugin(StandardPlugin):
    _ENCODING = 'UTF-8'

    def __init__(self, app, plugin_dir, gen_cfg, spec_cfg):
        StandardPlugin.__init__(self, app, plugin_dir, gen_cfg, spec_cfg)
        # update to use the non-standard tftpboot directory
        self._base_tftpboot_dir = self._tftpboot_dir
        self._tftpboot_dir = os.path.join(self._tftpboot_dir, 'Panasonic')

        self._tpl_helper = TemplatePluginHelper(plugin_dir)

        downloaders = FetchfwPluginHelper.new_downloaders(gen_cfg.get('proxies'))
        fetchfw_helper = FetchfwPluginHelper(plugin_dir, downloaders)
        # update to use the non-standard tftpboot directory
        fetchfw_helper.root_dir = self._tftpboot_dir

        self.services = fetchfw_helper.services()
        self.http_service = HTTPNoListingFileService(self._base_tftpboot_dir)

    http_dev_info_extractor = BasePanasonicHTTPDeviceInfoExtractor()

    def _dev_specific_filename(self, device):
        # Return the device specific filename (not pathname) of device
        fmted_mac = format_mac(device[u'mac'], separator='', uppercase=True)
        return 'Config' + fmted_mac + '.cfg'

    def _check_config(self, raw_config):
        if u'http_port' not in raw_config:
            raise RawConfigError('only support configuration via HTTP')

    def _check_device(self, device):
        if u'mac' not in device:
            raise Exception('MAC address needed for device configuration')

    def _common_templates(self):
        for tpl_format, file_format in [('common/%s.tpl', '%s.cfg'),]:
            for model in self._MODELS:
                yield tpl_format % model, file_format % model

    def configure_common(self, raw_config):
        for tpl_filename, filename in self._common_templates():
            tpl = self._tpl_helper.get_template(tpl_filename)
            dst = os.path.join(self._base_tftpboot_dir, filename)
            self._tpl_helper.dump(tpl, raw_config, dst, self._ENCODING)


    def configure(self, device, raw_config):
        self._check_config(raw_config)
        self._check_device(device)
        filename = self._dev_specific_filename(device)
        tpl = self._tpl_helper.get_dev_template(filename, device)

        path = os.path.join(self._tftpboot_dir, filename)
        self._tpl_helper.dump(tpl, raw_config, path, self._ENCODING)

    def deconfigure(self, device):
        self._remove_configuration_file(device)

    def _remove_configuration_file(self, device):
        path = os.path.join(self._tftpboot_dir, self._dev_specific_filename(device))
        try:
            os.remove(path)
        except OSError as e:
            logger.info('error while removing configuration file: %s', e)

    if hasattr(synchronize, 'standard_sip_synchronize'):
        def synchronize(self, device, raw_config):
            return synchronize.standard_sip_synchronize(device)

    else:
        # backward compatibility with older xivo-provd server
        def synchronize(self, device, raw_config):
            try:
                ip = device[u'ip'].encode('ascii')
            except KeyError:
                return defer.fail(Exception('IP address needed for device synchronization'))
            else:
                sync_service = synchronize.get_sync_service()
                if sync_service is None or sync_service.TYPE != 'AsteriskAMI':
                    return defer.fail(Exception('Incompatible sync service: %s' % sync_service))
                else:
                    return threads.deferToThread(sync_service.sip_notify, ip, 'check-sync')

    def get_remote_state_trigger_filename(self, device):
        if u'mac' not in device:
            return None

        return self._dev_specific_filename(device)
예제 #15
0
class BaseFanvilPlugin(StandardPlugin):
    _ENCODING = 'UTF-8'

    def __init__(self, app, plugin_dir, gen_cfg, spec_cfg):
        StandardPlugin.__init__(self, app, plugin_dir, gen_cfg, spec_cfg)
        # update to use the non-standard tftpboot directory
        self._base_tftpboot_dir = self._tftpboot_dir
        self._tftpboot_dir = os.path.join(self._tftpboot_dir, 'Fanvil')

        self._tpl_helper = TemplatePluginHelper(plugin_dir)

        downloaders = FetchfwPluginHelper.new_downloaders(
            gen_cfg.get('proxies'))
        fetchfw_helper = FetchfwPluginHelper(plugin_dir, downloaders)
        # update to use the non-standard tftpboot directory
        fetchfw_helper.root_dir = self._tftpboot_dir

        self.services = fetchfw_helper.services()
        self.http_service = HTTPNoListingFileService(self._base_tftpboot_dir)

    http_dev_info_extractor = BaseFanvilHTTPDeviceInfoExtractor()

    def _dev_specific_filename(self, device):
        # Return the device specific filename (not pathname) of device
        fmted_mac = format_mac(device[u'mac'], separator='', uppercase=False)
        return fmted_mac + '.cfg'

    def _check_config(self, raw_config):
        if u'http_port' not in raw_config:
            raise RawConfigError('only support configuration via HTTP')

    def _check_device(self, device):
        if u'mac' not in device:
            raise Exception('MAC address needed for device configuration')

    def configure(self, device, raw_config):
        self._check_config(raw_config)
        self._check_device(device)
        self._check_lines_password(raw_config)
        self._add_timezone(raw_config)
        self._add_locale(raw_config)
        self._add_fkeys(raw_config)
        filename = self._dev_specific_filename(device)
        tpl = self._tpl_helper.get_dev_template(filename, device)

        path = os.path.join(self._tftpboot_dir, filename)
        self._tpl_helper.dump(tpl, raw_config, path, self._ENCODING)

    def deconfigure(self, device):
        self._remove_configuration_file(device)

    def configure_common(self, raw_config):
        for filename, fw_filename, tpl_filename in self._COMMON_FILES:
            tpl = self._tpl_helper.get_template('common/%s' % tpl_filename)
            dst = os.path.join(self._tftpboot_dir, filename)
            raw_config[u'XX_fw_filename'] = fw_filename
            self._tpl_helper.dump(tpl, raw_config, dst, self._ENCODING)

    def _remove_configuration_file(self, device):
        path = os.path.join(self._tftpboot_dir,
                            self._dev_specific_filename(device))
        try:
            os.remove(path)
        except OSError as e:
            logger.info('error while removing configuration file: %s', e)

    if hasattr(synchronize, 'standard_sip_synchronize'):

        def synchronize(self, device, raw_config):
            return synchronize.standard_sip_synchronize(device)

    else:
        # backward compatibility with older xivo-provd server
        def synchronize(self, device, raw_config):
            try:
                ip = device[u'ip'].encode('ascii')
            except KeyError:
                return defer.fail(
                    Exception('IP address needed for device synchronization'))
            else:
                sync_service = synchronize.get_sync_service()
                if sync_service is None or sync_service.TYPE != 'AsteriskAMI':
                    return defer.fail(
                        Exception('Incompatible sync service: %s' %
                                  sync_service))
                else:
                    return threads.deferToThread(sync_service.sip_notify, ip,
                                                 'check-sync')

    def get_remote_state_trigger_filename(self, device):
        if u'mac' not in device:
            return None

        return self._dev_specific_filename(device)

    def _check_lines_password(self, raw_config):
        for line in raw_config[u'sip_lines'].itervalues():
            if line[u'password'] == u'autoprov':
                line[u'password'] = u''

    def _format_dst_change(self, suffix, dst_change):
        lines = []
        lines.append(u'<DST_%s_Mon>%d</DST_%s_Mon>' %
                     (suffix, dst_change['month'], suffix))
        lines.append(u'<DST_%s_Hour>%d</DST_%s_Hour>' %
                     (suffix, min(dst_change['time'].as_hours, 23), suffix))
        if dst_change['day'].startswith('D'):
            lines.append(u'<DST_%s_Wday>%s</DST_%s_Wday>' %
                         (suffix, dst_change['day'][1:], suffix))
        else:
            week, weekday = dst_change['day'][1:].split('.')
            if week == '5':
                lines.append(u'<DST_%s_Week>-1</DST_%s_Week>' %
                             (suffix, suffix))
            else:
                lines.append(u'<DST_%s_Week>%s</DST_%s_Week>' %
                             (suffix, week, suffix))
            lines.append(u'<DST_%s_Wday>%s</DST_%s_Wday>' %
                         (suffix, weekday, suffix))
        lines.append(u'<DST_%s_Min>0</DST_%s_Min>' % (suffix, suffix))
        return lines

    def _format_tzinfo(self, tzinfo):
        lines = []
        utc = tzinfo['utcoffset'].as_hours
        utc_list = TZ_INFO[utc]
        for time_zone_name, time_zone in utc_list:
            lines.append(u'<Time_Zone>%s</Time_Zone>' % (time_zone))
            lines.append(u'<Time_Zone_Name>%s</Time_Zone_Name>' %
                         (time_zone_name))
        if tzinfo['dst'] is None:
            lines.append(u'<Enable_DST>0</Enable_DST>')
        else:
            lines.append(u'<Enable_DST>2</Enable_DST>')
            lines.append(u'<DST_Min_Offset>%d</DST_Min_Offset>' %
                         (min(tzinfo['dst']['save'].as_minutes, 60)))
            lines.extend(
                self._format_dst_change('Start', tzinfo['dst']['start']))
            lines.extend(self._format_dst_change('End', tzinfo['dst']['end']))
        return u'\n'.join(lines)

    def _add_timezone(self, raw_config):
        if u'timezone' in raw_config:
            try:
                tzinfo = tzinform.get_timezone_info(raw_config[u'timezone'])
            except tzinform.TimezoneNotFoundError, e:
                logger.info('Unknown timezone: %s', e)
            else:
                raw_config[u'XX_timezone'] = self._format_tzinfo(tzinfo)
예제 #16
0
class BaseSnomPlugin(StandardPlugin):
    _ENCODING = 'UTF-8'
    _LOCALE = {
        u'de_DE': (u'Deutsch', u'GER'),
        u'en_US': (u'English', u'USA'),
        u'es_ES': (u'Espanol', u'ESP'),
        u'fr_FR': (u'Francais', u'FRA'),
        u'fr_CA': (u'Francais', u'USA'),
        u'it_IT': (u'Italiano', u'ITA'),
        u'nl_NL': (u'Dutch', u'NLD'),
    }
    _SIP_DTMF_MODE = {
        u'RTP-in-band': u'off',
        u'RTP-out-of-band': u'off',
        u'SIP-INFO': u'sip_info_only'
    }
    _XX_DICT_DEF = u'en'
    _XX_DICT = {
        u'en': {
            u'remote_directory': u'Directory',
        },
        u'fr': {
            u'remote_directory': u'Annuaire',
        },
    }

    def __init__(self, app, plugin_dir, gen_cfg, spec_cfg):
        StandardPlugin.__init__(self, app, plugin_dir, gen_cfg, spec_cfg)

        self._tpl_helper = TemplatePluginHelper(plugin_dir)

        downloaders = FetchfwPluginHelper.new_downloaders(
            gen_cfg.get('proxies'))
        fetchfw_helper = FetchfwPluginHelper(plugin_dir, downloaders)

        self.services = fetchfw_helper.services()
        self.http_service = HTTPNoListingFileService(self._tftpboot_dir)

    http_dev_info_extractor = BaseSnomHTTPDeviceInfoExtractor()

    def _common_templates(self):
        yield ('common/gui_lang.xml.tpl', 'gui_lang.xml')
        yield ('common/web_lang.xml.tpl', 'web_lang.xml')
        for tpl_format, file_format in [
            ('common/snom%s.htm.tpl', 'snom%s.htm'),
            ('common/snom%s.xml.tpl', 'snom%s.xml'),
            ('common/snom%s-firmware.xml.tpl', 'snom%s-firmware.xml')
        ]:
            for model in self._MODELS:
                yield tpl_format % model, file_format % model

    def configure_common(self, raw_config):
        for tpl_filename, filename in self._common_templates():
            tpl = self._tpl_helper.get_template(tpl_filename)
            dst = os.path.join(self._tftpboot_dir, filename)
            self._tpl_helper.dump(tpl, raw_config, dst, self._ENCODING)

    def _update_sip_lines(self, raw_config):
        proxy_ip = raw_config.get(u'sip_proxy_ip')
        backup_proxy_ip = raw_config.get(u'sip_backup_proxy_ip')
        voicemail = raw_config.get(u'exten_voicemail')
        for line in raw_config[u'sip_lines'].itervalues():
            if proxy_ip:
                line.setdefault(u'proxy_ip', proxy_ip)
            if backup_proxy_ip:
                line.setdefault(u'backup_proxy_ip', backup_proxy_ip)
            if voicemail:
                line.setdefault(u'voicemail', voicemail)

    def _add_fkeys(self, raw_config, model):
        lines = []
        for funckey_no, funckey_dict in sorted(
                raw_config[u'funckeys'].iteritems(), key=itemgetter(0)):
            funckey_type = funckey_dict[u'type']
            if funckey_type == u'speeddial':
                type_ = u'speed'
                suffix = ''
            elif funckey_type == u'park':
                if model in [u'710', u'715', u'720', u'725', u'760', u'D765']:
                    type_ = u'orbit'
                    suffix = ''
                else:
                    type_ = u'speed'
                    suffix = ''
            elif funckey_type == u'blf':
                if u'exten_pickup_call' in raw_config:
                    type_ = u'blf'
                    suffix = '|%s' % raw_config[u'exten_pickup_call']
                else:
                    logger.warning(
                        'Could not set funckey %s: no exten_pickup_call',
                        funckey_no)
                    continue
            else:
                logger.info('Unsupported funckey type: %s', funckey_type)
                continue
            value = funckey_dict[u'value']
            label = escape(funckey_dict.get(u'label') or value)
            fkey_value = self._format_fkey_value(type_, value, suffix)
            lines.append(
                u'<fkey idx="%d" label="%s" context="active" perm="R">%s</fkey>'
                % (int(funckey_no) - 1, label, fkey_value))
        raw_config[u'XX_fkeys'] = u'\n'.join(lines)

    def _format_fkey_value(self, fkey_type, value, suffix):
        return '%s %s%s' % (fkey_type, value, suffix)

    def _add_lang(self, raw_config):
        if u'locale' in raw_config:
            locale = raw_config[u'locale']
            if locale in self._LOCALE:
                raw_config[u'XX_lang'] = self._LOCALE[locale]

    def _format_dst_change(self, dst_change):
        fmted_time = u'%02d:%02d:%02d' % tuple(dst_change['time'].as_hms)
        day = dst_change['day']
        if day.startswith('D'):
            return u'%02d.%02d %s' % (int(
                day[1:]), dst_change['month'], fmted_time)
        else:
            week, weekday = map(int, day[1:].split('.'))
            weekday = tzinform.week_start_on_monday(weekday)
            return u'%02d.%02d.%02d %s' % (dst_change['month'], week, weekday,
                                           fmted_time)

    def _format_tzinfo(self, tzinfo):
        lines = []
        lines.append(u'<timezone perm="R"></timezone>')
        lines.append(u'<utc_offset perm="R">%+d</utc_offset>' %
                     tzinfo['utcoffset'].as_seconds)
        if tzinfo['dst'] is None:
            lines.append(u'<dst perm="R"></dst>')
        else:
            lines.append(u'<dst perm="R">%d %s %s</dst>' %
                         (tzinfo['dst']['save'].as_seconds,
                          self._format_dst_change(tzinfo['dst']['start']),
                          self._format_dst_change(tzinfo['dst']['end'])))
        return u'\n'.join(lines)

    def _add_timezone(self, raw_config):
        if u'timezone' in raw_config:
            try:
                tzinfo = tzinform.get_timezone_info(raw_config[u'timezone'])
            except tzinform.TimezoneNotFoundError, e:
                logger.warning('Unknown timezone %s: %s',
                               raw_config[u'timezone'], e)
            else:
                raw_config[u'XX_timezone'] = self._format_tzinfo(tzinfo)
예제 #17
0
class BaseYealinkPlugin(StandardPlugin):
    _ENCODING = 'UTF-8'
    _LOCALE = {
        u'de_DE': (u'German', u'Germany', u'2'),
        u'en_US': (u'English', u'United States', u'0'),
        u'es_ES': (u'Spanish', u'Spain', u'6'),
        u'fr_FR': (u'French', u'France', u'1'),
        u'fr_CA': (u'French', u'United States', u'1'),
    }
    _SIP_DTMF_MODE = {
        u'RTP-in-band': u'0',
        u'RTP-out-of-band': u'1',
        u'SIP-INFO': u'2',
    }
    _SIP_TRANSPORT = {
        u'udp': u'0',
        u'tcp': u'1',
        u'tls': u'2',
    }
    _SIP_TRANSPORT_DEF = u'0'
    _NB_SIP_ACCOUNTS = {
        u'CP860': 1,
        u'T19P': 1,
        u'T19P_E2': 1,
        u'T20P': 2,
        u'T21P': 2,
        u'T21P_E2': 2,
        u'T23P': 3,
        u'T23G': 3,
        u'T27P': 6,
        u'T27G': 6,
        u'T29G': 16,
        u'T32G': 3,
        u'T38G': 6,
        u'T40P': 3,
        u'T41P': 6,
        u'T41S': 6,
        u'T42G': 12,
        u'T42S': 12,
        u'T46G': 16,
        u'T46S': 16,
        u'T48G': 16,
        u'T48S': 16,
        u'T49G': 16,
        u'VP530P': 4,
        u'W52P': 5,
    }

    def __init__(self, app, plugin_dir, gen_cfg, spec_cfg):
        StandardPlugin.__init__(self, app, plugin_dir, gen_cfg, spec_cfg)

        self._tpl_helper = TemplatePluginHelper(plugin_dir)

        downloaders = FetchfwPluginHelper.new_downloaders(gen_cfg.get('proxies'))
        fetchfw_helper = FetchfwPluginHelper(plugin_dir, downloaders)

        self.services = fetchfw_helper.services()
        self.http_service = HTTPNoListingFileService(self._tftpboot_dir)

    http_dev_info_extractor = BaseYealinkHTTPDeviceInfoExtractor()

    def configure_common(self, raw_config):
        for filename, fw_filename, tpl_filename in self._COMMON_FILES:
            tpl = self._tpl_helper.get_template('common/%s' % tpl_filename)
            dst = os.path.join(self._tftpboot_dir, filename)
            raw_config[u'XX_fw_filename'] = fw_filename
            self._tpl_helper.dump(tpl, raw_config, dst, self._ENCODING)

    def _update_sip_lines(self, raw_config):
        for line_no, line in raw_config[u'sip_lines'].iteritems():
            # set line number
            line[u'XX_line_no'] = int(line_no)
            # set dtmf inband transfer
            dtmf_mode = line.get(u'dtmf_mode') or raw_config.get(u'sip_dtmf_mode')
            if dtmf_mode in self._SIP_DTMF_MODE:
                line[u'XX_dtmf_type'] = self._SIP_DTMF_MODE[dtmf_mode]
            # set voicemail
            if u'voicemail' not in line and u'exten_voicemail' in raw_config:
                line[u'voicemail'] = raw_config[u'exten_voicemail']
            # set proxy_ip
            if u'proxy_ip' not in line:
                line[u'proxy_ip'] = raw_config[u'sip_proxy_ip']
            # set proxy_port
            if u'proxy_port' not in line and u'sip_proxy_port' in raw_config:
                line[u'proxy_port'] = raw_config[u'sip_proxy_port']

    def _add_fkeys(self, device, raw_config):
        funckey_generator = BaseYealinkFunckeyGenerator(device, raw_config)
        raw_config[u'XX_fkeys'] = funckey_generator.generate()

    def _add_country_and_lang(self, raw_config):
        locale = raw_config.get(u'locale')
        if locale in self._LOCALE:
            (raw_config[u'XX_lang'],
             raw_config[u'XX_country'],
             raw_config[u'XX_handset_lang']) = self._LOCALE[locale]

    def _format_dst_change(self, dst_change):
        if dst_change['day'].startswith('D'):
            return u'%02d/%02d/%02d' % (dst_change['month'], int(dst_change['day'][1:]), dst_change['time'].as_hours)
        else:
            week, weekday = map(int, dst_change['day'][1:].split('.'))
            weekday = tzinform.week_start_on_monday(weekday)
            return u'%d/%d/%d/%d' % (dst_change['month'], week, weekday, dst_change['time'].as_hours)

    def _format_tz_info(self, tzinfo):
        lines = []
        lines.append(u'local_time.time_zone = %+d' % min(max(tzinfo['utcoffset'].as_hours, -11), 12))
        if tzinfo['dst'] is None:
            lines.append(u'local_time.summer_time = 0')
        else:
            lines.append(u'local_time.summer_time = 1')
            if tzinfo['dst']['start']['day'].startswith('D'):
                lines.append(u'local_time.dst_time_type = 0')
            else:
                lines.append(u'local_time.dst_time_type = 1')
            lines.append(u'local_time.start_time = %s' % self._format_dst_change(tzinfo['dst']['start']))
            lines.append(u'local_time.end_time = %s' % self._format_dst_change(tzinfo['dst']['end']))
            lines.append(u'local_time.offset_time = %s' % tzinfo['dst']['save'].as_minutes)
        return u'\n'.join(lines)

    def _add_timezone(self, raw_config):
        if u'timezone' in raw_config:
            try:
                tzinfo = tzinform.get_timezone_info(raw_config[u'timezone'])
            except tzinform.TimezoneNotFoundError, e:
                logger.warning('Unknown timezone: %s', e)
            else:
                raw_config[u'XX_timezone'] = self._format_tz_info(tzinfo)
예제 #18
0
class BaseYealinkPlugin(StandardPlugin):
    _ENCODING = 'UTF-8'
    _LOCALE = {
        u'de_DE': (u'German', u'Germany', u'2'),
        u'en_US': (u'English', u'United States', u'0'),
        u'es_ES': (u'Spanish', u'Spain', u'6'),
        u'fr_FR': (u'French', u'France', u'1'),
        u'fr_CA': (u'French', u'United States', u'1'),
    }
    _SIP_DTMF_MODE = {
        u'RTP-in-band': u'0',
        u'RTP-out-of-band': u'1',
        u'SIP-INFO': u'2',
    }
    _SIP_TRANSPORT = {
        u'udp': u'0',
        u'tcp': u'1',
        u'tls': u'2',
    }
    _SIP_TRANSPORT_DEF = u'0'
    _NB_SIP_ACCOUNTS = {
        u'CP860': 1,
        u'T19P': 1,
        u'T19P_E2': 1,
        u'T20P': 2,
        u'T21P': 2,
        u'T21P_E2': 2,
        u'T22P': 3,
        u'T23P': 3,
        u'T23G': 3,
        u'T26P': 3,
        u'T27P': 6,
        u'T27G': 6,
        u'T28P': 6,
        u'T29G': 16,
        u'T32G': 3,
        u'T38G': 6,
        u'T40P': 3,
        u'T41P': 6,
        u'T41S': 6,
        u'T42G': 12,
        u'T42S': 12,
        u'T46G': 16,
        u'T46S': 16,
        u'T48G': 16,
        u'T48S': 16,
        u'T49G': 16,
        u'VP530P': 4,
        u'W52P': 5,
    }

    def __init__(self, app, plugin_dir, gen_cfg, spec_cfg):
        StandardPlugin.__init__(self, app, plugin_dir, gen_cfg, spec_cfg)

        self._tpl_helper = TemplatePluginHelper(plugin_dir)

        downloaders = FetchfwPluginHelper.new_downloaders(
            gen_cfg.get('proxies'))
        fetchfw_helper = FetchfwPluginHelper(plugin_dir, downloaders)

        self.services = fetchfw_helper.services()
        self.http_service = HTTPNoListingFileService(self._tftpboot_dir)

    http_dev_info_extractor = BaseYealinkHTTPDeviceInfoExtractor()

    def configure_common(self, raw_config):
        for filename, fw_filename, tpl_filename in self._COMMON_FILES:
            tpl = self._tpl_helper.get_template('common/%s' % tpl_filename)
            dst = os.path.join(self._tftpboot_dir, filename)
            raw_config[u'XX_fw_filename'] = fw_filename
            self._tpl_helper.dump(tpl, raw_config, dst, self._ENCODING)

    def _update_sip_lines(self, raw_config):
        for line_no, line in raw_config[u'sip_lines'].iteritems():
            # set line number
            line[u'XX_line_no'] = int(line_no)
            # set dtmf inband transfer
            dtmf_mode = line.get(u'dtmf_mode') or raw_config.get(
                u'sip_dtmf_mode')
            if dtmf_mode in self._SIP_DTMF_MODE:
                line[u'XX_dtmf_type'] = self._SIP_DTMF_MODE[dtmf_mode]
            # set voicemail
            if u'voicemail' not in line and u'exten_voicemail' in raw_config:
                line[u'voicemail'] = raw_config[u'exten_voicemail']
            # set proxy_ip
            if u'proxy_ip' not in line:
                line[u'proxy_ip'] = raw_config[u'sip_proxy_ip']
            # set proxy_port
            if u'proxy_port' not in line and u'sip_proxy_port' in raw_config:
                line[u'proxy_port'] = raw_config[u'sip_proxy_port']

    def _add_fkeys(self, device, raw_config):
        funckey_generator = BaseYealinkFunckeyGenerator(device, raw_config)
        raw_config[u'XX_fkeys'] = funckey_generator.generate()

    def _add_country_and_lang(self, raw_config):
        locale = raw_config.get(u'locale')
        if locale in self._LOCALE:
            (raw_config[u'XX_lang'], raw_config[u'XX_country'],
             raw_config[u'XX_handset_lang']) = self._LOCALE[locale]

    def _format_dst_change(self, dst_change):
        if dst_change['day'].startswith('D'):
            return u'%02d/%02d/%02d' % (dst_change['month'],
                                        int(dst_change['day'][1:]),
                                        dst_change['time'].as_hours)
        else:
            week, weekday = map(int, dst_change['day'][1:].split('.'))
            weekday = tzinform.week_start_on_monday(weekday)
            return u'%d/%d/%d/%d' % (dst_change['month'], week, weekday,
                                     dst_change['time'].as_hours)

    def _format_tz_info(self, tzinfo):
        lines = []
        lines.append(u'local_time.time_zone = %+d' %
                     min(max(tzinfo['utcoffset'].as_hours, -11), 12))
        if tzinfo['dst'] is None:
            lines.append(u'local_time.summer_time = 0')
        else:
            lines.append(u'local_time.summer_time = 1')
            if tzinfo['dst']['start']['day'].startswith('D'):
                lines.append(u'local_time.dst_time_type = 0')
            else:
                lines.append(u'local_time.dst_time_type = 1')
            lines.append(u'local_time.start_time = %s' %
                         self._format_dst_change(tzinfo['dst']['start']))
            lines.append(u'local_time.end_time = %s' %
                         self._format_dst_change(tzinfo['dst']['end']))
            lines.append(u'local_time.offset_time = %s' %
                         tzinfo['dst']['save'].as_minutes)
        return u'\n'.join(lines)

    def _add_timezone(self, raw_config):
        if u'timezone' in raw_config:
            try:
                tzinfo = tzinform.get_timezone_info(raw_config[u'timezone'])
            except tzinform.TimezoneNotFoundError, e:
                logger.warning('Unknown timezone: %s', e)
            else:
                raw_config[u'XX_timezone'] = self._format_tz_info(tzinfo)
예제 #19
0
class BaseDigiumPlugin(StandardPlugin):

    _ENCODING = 'UTF-8'
    _CONTACT_TEMPLATE = 'contact.tpl'

    def __init__(self, app, plugin_dir, gen_cfg, spec_cfg):
        StandardPlugin.__init__(self, app, plugin_dir, gen_cfg, spec_cfg)

        self._tpl_helper = TemplatePluginHelper(plugin_dir)
        self._digium_dir = os.path.join(self._tftpboot_dir, 'Digium')

        downloaders = FetchfwPluginHelper.new_downloaders(
            gen_cfg.get('proxies'))
        fetchfw_helper = FetchfwPluginHelper(plugin_dir, downloaders)

        self.services = fetchfw_helper.services()
        self.http_service = HTTPNoListingFileService(self._tftpboot_dir)

    dhcp_dev_info_extractor = DigiumDHCPDeviceInfoExtractor()

    http_dev_info_extractor = DigiumHTTPDeviceInfoExtractor()

    def configure(self, device, raw_config):
        self._check_device(device)

        filename = self._dev_specific_filename(device)
        contact_filename = self._dev_contact_filename(device)

        tpl = self._tpl_helper.get_dev_template(filename, device)
        contact_tpl = self._tpl_helper.get_template(self._CONTACT_TEMPLATE)

        raw_config['XX_mac'] = self._format_mac(device)
        raw_config['XX_main_proxy_ip'] = self._get_main_proxy_ip(raw_config)
        raw_config['XX_funckeys'] = self._transform_funckeys(raw_config)
        raw_config['XX_lang'] = raw_config.get(u'locale')

        path = os.path.join(self._digium_dir, filename)
        contact_path = os.path.join(self._digium_dir, contact_filename)
        self._tpl_helper.dump(tpl, raw_config, path, self._ENCODING)
        self._tpl_helper.dump(contact_tpl, raw_config, contact_path,
                              self._ENCODING)

    def deconfigure(self, device):
        filenames = [
            self._dev_specific_filename(device),
            self._dev_contact_filename(device)
        ]

        for filename in filenames:
            path = os.path.join(self._digium_dir, filename)
            try:
                os.remove(path)
            except OSError as e:
                logger.info('error while removing file %s: %s', path, e)

    if hasattr(synchronize, 'standard_sip_synchronize'):

        def synchronize(self, device, raw_config):
            return synchronize.standard_sip_synchronize(device)

    else:
        # backward compatibility with older wazo-provd server
        def synchronize(self, device, raw_config):
            try:
                ip = device[u'ip'].encode('ascii')
            except KeyError:
                return defer.fail(
                    Exception('IP address needed for device synchronization'))
            else:
                sync_service = synchronize.get_sync_service()
                if sync_service is None or sync_service.TYPE != 'AsteriskAMI':
                    return defer.fail(
                        Exception('Incompatible sync service: %s' %
                                  sync_service))
                else:
                    return threads.deferToThread(sync_service.sip_notify, ip,
                                                 'check-sync')

    def get_remote_state_trigger_filename(self, device):
        if u'mac' not in device:
            return None

        return self._dev_specific_filename(device)

    def is_sensitive_filename(self, filename):
        return bool(self._SENSITIVE_FILENAME_REGEX.match(filename))

    def _check_device(self, device):
        if u'mac' not in device:
            raise Exception('MAC address needed to configure device')

    def _get_main_proxy_ip(self, raw_config):
        if raw_config[u'sip_lines']:
            line_no = min(int(x) for x in raw_config[u'sip_lines'].keys())
            line_no = str(line_no)
            return raw_config[u'sip_lines'][line_no][u'proxy_ip']
        else:
            return raw_config[u'ip']

    def _format_mac(self, device):
        return format_mac(device[u'mac'], separator='', uppercase=False)

    _SENSITIVE_FILENAME_REGEX = re.compile(r'^[0-9a-f]{12}\.cfg$')

    def _dev_specific_filename(self, device):
        filename = '%s.cfg' % self._format_mac(device)
        return filename

    def _dev_contact_filename(self, device):
        contact_filename = '%s-contacts.xml' % self._format_mac(device)
        return contact_filename

    def _transform_funckeys(self, raw_config):
        return dict((int(k), v) for k, v in raw_config['funckeys'].iteritems())