예제 #1
0
    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)

        root_resource = Resource()
        root_resource.putChild('jitsi', JitsiHTTPService(self._tftpboot_dir))
        self.http_service = root_resource
예제 #2
0
    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)
예제 #3
0
class JitsiPlugin(StandardPlugin):
    IS_PLUGIN = True

    _ENCODING = 'UTF-8'

    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)

        root_resource = Resource()
        root_resource.putChild('jitsi', JitsiHTTPService(self._tftpboot_dir))
        self.http_service = root_resource

    http_dev_info_extractor = JitsiHTTPDeviceInfoExtractor()

    pg_associator = JitsiPgAssociator()

    def _device_config_filename(self, device):
        # Return the device specific filename (not pathname) of device
        return device[u'uuid']

    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'uuid' not in device:
            raise Exception('UUID needed for device configuration')
        if not is_normed_uuid(device[u'uuid']):
            # non normalized uuid can lead to security issue
            raise Exception('non normalized UUID: %s', device[u'uuid'])

    def configure(self, device, raw_config):
        self._check_config(raw_config)
        self._check_device(device)
        filename = self._device_config_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):
        path = os.path.join(self._tftpboot_dir,
                            self._device_config_filename(device))
        try:
            os.remove(path)
        except OSError, e:
            logger.warning('error while deconfiguring device: %s', e)
예제 #4
0
파일: entry.py 프로젝트: Eyepea/xivo-skaro
class JitsiPlugin(StandardPlugin):
    IS_PLUGIN = True
    
    _ENCODING = 'UTF-8'
    
    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)
        
        root_resource = Resource()
        root_resource.putChild('jitsi', JitsiHTTPService(self._tftpboot_dir))
        self.http_service = root_resource
    
    http_dev_info_extractor = JitsiHTTPDeviceInfoExtractor()
    
    pg_associator = JitsiPgAssociator()
    
    def _device_config_filename(self, device):
        # Return the device specific filename (not pathname) of device
        return device[u'uuid']
    
    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'uuid' not in device:
            raise Exception('UUID needed for device configuration')
        if not is_normed_uuid(device[u'uuid']):
            # non normalized uuid can lead to security issue
            raise Exception('non normalized UUID: %s', device[u'uuid'])
    
    def configure(self, device, raw_config):
        self._check_config(raw_config)
        self._check_device(device)
        filename = self._device_config_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):
        path = os.path.join(self._tftpboot_dir, self._device_config_filename(device))
        try:
            os.remove(path)
        except OSError, e:
            logger.warning('error while deconfiguring device: %s', e)
예제 #5
0
    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, 'Grandstream')

        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)
예제 #6
0
    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)
예제 #7
0
파일: entry.py 프로젝트: Eyepea/xivo-skaro
 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)
     
     root_resource = Resource()
     root_resource.putChild('jitsi', JitsiHTTPService(self._tftpboot_dir))
     self.http_service = root_resource
예제 #8
0
    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)

        cfg_service = ZenitelConfigureService(downloaders['auth'],
                                              spec_cfg.get('username'),
                                              spec_cfg.get('password'))
        persister = JsonConfigPersister(
            os.path.join(self._plugin_dir, 'var', 'config.json'))
        cfg_service = PersistentConfigureServiceDecorator(
            cfg_service, persister)

        self.services = {'configure': cfg_service, 'install': fetchfw_helper}
        self.tftp_service = TFTPFileService(self._tftpboot_dir)
예제 #9
0
    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)
예제 #10
0
파일: common.py 프로젝트: Eyepea/xivo-skaro
    def __init__(self, app, plugin_dir, gen_cfg, spec_cfg):
        StandardPlugin.__init__(self, app, plugin_dir, gen_cfg, spec_cfg)
        self._app = app
        self._cache_dir = os.path.join(plugin_dir, 'var', 'cache')

        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 = BaseCiscoHTTPHookService(HTTPNoListingFileService(self._tftpboot_dir), self)
        self.tftp_service = TFTPFileService(self._tftpboot_dir)
예제 #11
0
    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)
예제 #12
0
    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)
예제 #13
0
    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()

        # Maybe find a way to bind to a specific port without changing the general http_port setting of xivo-provd ?
        # At the moment, http_port 6970 must be set in /etc/xivo/provd/provd.conf
        self.http_service = HTTPNoListingFileService(self._tftpboot_dir)

        self.tftp_service = TFTPFileService(self._tftpboot_dir)
예제 #14
0
파일: common.py 프로젝트: Eyepea/xivo-skaro
 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)
     
     cfg_service = ZenitelConfigureService(downloaders['auth'],
                                           spec_cfg.get('username'),
                                           spec_cfg.get('password'))
     persister = JsonConfigPersister(os.path.join(self._plugin_dir, 'var',
                                                  'config.json'))
     cfg_service = PersistentConfigureServiceDecorator(cfg_service, persister)
     
     self.services = {'configure': cfg_service,
                      'install': fetchfw_helper}
     self.tftp_service = TFTPFileService(self._tftpboot_dir)
예제 #15
0
 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)
     
     handlers = FetchfwPluginHelper.new_handlers(gen_cfg.get('proxies'))
     downloaders = FetchfwPluginHelper.new_downloaders_from_handlers(handlers)
     cisco_dler = common['CiscoDownloader'](handlers)
     downloaders['x-cisco'] = cisco_dler
     fetchfw_helper = FetchfwPluginHelper(plugin_dir, downloaders)
     
     cfg_service = common['CiscoConfigureService'](cisco_dler, spec_cfg.get('username'),
                                                   spec_cfg.get('password'))
     persister = JsonConfigPersister(os.path.join(self._plugin_dir, 'var',
                                                  'config.json'))
     cfg_service = PersistentConfigureServiceDecorator(cfg_service, persister)
     
     self.services = {'configure': cfg_service,
                      'install': fetchfw_helper}
     self.tftp_service = TFTPFileService(self._tftpboot_dir)
예제 #16
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)
예제 #17
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)
예제 #18
0
class BaseGigasetPlugin(StandardPlugin):
    _ENCODING = 'UTF-8'

    _TZ_GIGASET = {
        (-12, 0): 0x00,
        (-11, 0): 0x01,
        (-10, 0): 0x02,
        (-9, 0): 0x03,
        (-8, 0): 0x04,
        (-7, 0): 0x07,
        (-6, 0): 0x09,
        (-5, 0): 0x0d,
        (-4, 0): 0x10,
        (-3, 0): 0x12,
        (-2, 0): 0x16,
        (-1, 0): 0x18,
        (0, 0): 0x1a,
        (+1, 0): 0x1b,
        (+2, 0): 0x20,
        (+3, 0): 0x28,
        (+4, 0): 0x2c,
        (+4, 30): 0x2d,
        (+5, 0): 0x2f,
        (+5, 30): 0x30,
        (+5, 45): 0x31,
        (+6, 00): 0x33,
        (+6, 30): 0x35,
        (+7, 0): 0x36,
        (+8, 0): 0x38,
        (+9, 0): 0x3d,
        (+9, 30): 0x40,
        (+10, 0): 0x43,
        (+11, 0): 0x47,
        (+12, 0): 0x48,
        (+13, 0): 0x50,
    }

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

        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 = HTTPServiceWrapper(self._tftpboot_dir)

    dhcp_dev_info_extractor = GigasetDHCPDeviceInfoExtractor()
    http_dev_info_extractor = GigasetHTTPDeviceInfoExtractor()

    def _check_device(self, device):
        if u'ip' not in device:
            raise Exception('IP address needed for Gigaset configuration')

    def _check_config(self, raw_config):
        pass

    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 fmted_mac + '.xml'

    def _add_phonebook(self, raw_config):
        uuid_format = u'{scheme}://{hostname}:{port}/0.1/directories/lookup/default/gigaset/{user_uuid}?'
        plugins.add_xivo_phonebook_url_from_format(raw_config, uuid_format)

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

    def _add_xx_vars(self, device, raw_config):
        raw_config[u'XX_mac_addr'] = format_mac(device[u'mac'], separator='', uppercase=True)

        cur_datetime = datetime.datetime.now()
        raw_config[u'XX_version_date'] = cur_datetime.strftime('%d%m%y%H%M')

        if u'dns_enabled' in raw_config:
            ip = raw_config[u'dns_ip']
            ip_str = '0x' + ''.join(['%x' % int(p) for p in ip.split('.')])
            raw_config[u'XX_dns_ip_hex'] = ip_str

        self._add_timezone_code(raw_config)

    def _add_sip_info(self, raw_config):
        if u'1' in raw_config[u'sip_lines']:
            line = raw_config[u'sip_lines'][u'1']
            raw_config[u'sip_proxy_ip'] = line[u'proxy_ip']
            raw_config[u'sip_proxy_port'] = line.get(u'proxy_port', 5060)
            raw_config[u'sip_registrar_ip'] = line.get(u'registrar_ip')
            raw_config[u'sip_registrar_port'] = line.get(u'registrar_port', 5060)

    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_sip_info(raw_config)
        self._add_xx_vars(device, raw_config)
        self._add_phonebook(raw_config)

        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 as e:
            logger.info('error while removing configuration file: %s', e)

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

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

    def synchronize(self, device, raw_config):
        return synchronize.standard_sip_synchronize(device)
예제 #19
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)
예제 #20
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()
        )
예제 #21
0
class BasePattonPlugin(StandardPlugin):

    _ENCODING = 'ascii'
    _SIP_DTMF_MODE = {
        u'RTP-in-band': u'default',
        u'RTP-out-of-band': u'rtp',
        u'SIP-INFO': u'signaling'
    }

    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 = BasePattonHTTPDeviceInfoExtractor()

    def _add_syslog_level(self, raw_config):
        if u'syslog_level' in raw_config:
            if raw_config[u'syslog_level'] == u'info':
                raw_config[u'XX_syslog_level'] = u'informational'
            else:
                raw_config[u'XX_syslog_level'] = raw_config[u'syslog_level']

    def _add_timezone_and_dst(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.info('Unknown timezone: %s', e)
            else:
                converter = _TimezoneConverter(tzinfo)
                raw_config[u'XX_timezone_offset'] = converter.default_offset()
                if converter.has_dst():
                    raw_config[u'XX_dst_offset'] = converter.dst_offset()
                    raw_config[u'XX_dst_start'] = converter.dst_start()
                    raw_config[u'XX_dst_end'] = converter.dst_end()

    def _update_sip_transport(self, raw_config):
        if u'sip_transport' not in raw_config:
            raw_config[u'sip_transport'] = u'udp'
        elif raw_config.get(u'sip_transport') == u'tls':
            logger.warning("Patton doesn't support the SIP transport tls: fallback to udp")
            raw_config[u'sip_transport'] = u'udp'

    def _add_dtmf_relay(self, raw_config):
        if u'sip_dtmf_mode' in raw_config:
            raw_config[u'XX_dtmf_relay'] = self._SIP_DTMF_MODE[raw_config[u'sip_dtmf_mode']]

    def _add_lines_and_servers(self, raw_config):
        converter = _SIPLinesConverter()
        for sip_line_no, sip_line in raw_config[u'sip_lines'].iteritems():
            converter.add_sip_line(sip_line_no, sip_line)
        raw_config[u'XX_lines'] = converter.lines()
        raw_config[u'XX_servers'] = converter.servers()

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

    def _dev_specific_filename(self, device):
        fmted_mac = format_mac(device[u'mac'], separator='')
        return fmted_mac + '.cfg'

    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_device(device)
        filename = self._dev_specific_filename(device)
        tpl = self._tpl_helper.get_dev_template(filename, device)

        self._add_syslog_level(raw_config)
        self._add_timezone_and_dst(raw_config)
        self._update_sip_transport(raw_config)
        self._add_dtmf_relay(raw_config)
        self._add_lines_and_servers(raw_config)

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

    def deconfigure(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 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 is_sensitive_filename(self, filename):
        return bool(self._SENSITIVE_FILENAME_REGEX.match(filename))
예제 #22
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))
예제 #23
0
class BaseGrandstreamPlugin(StandardPlugin):
    _ENCODING = 'UTF-8'
    # VPKs are the virtual phone keys on the main display
    # MPKs are the physical programmable keys on some models
    MODEL_FKEYS = {
        u'GRP2612': {
            u'vpk': 16,
            u'mpk': 0,
        },
        u'GRP2613': {
            u'vpk': 24,
            u'mpk': 0,
        },
        u'GRP2614': {
            u'vpk': 16,
            u'mpk': 24,
        },
        u'GRP2615': {
            u'vpk': 40,
            u'mpk': 0,
        },
        u'GRP2616': {
            u'vpk': 16,
            u'mpk': 24,
        },
    }

    DTMF_MODES = {
        # mode: (in audio, in RTP, in SIP)
        u'RTP-in-band': ('Yes', 'Yes', 'No'),
        u'RTP-out-of-band': ('No', 'Yes', 'No'),
        u'SIP-INFO': ('No', 'No', 'Yes'),
    }

    SIP_TRANSPORTS = {
        u'udp': u'UDP',
        u'tcp': u'TCP',
        u'tls': u'TlsOrTcp',
    }

    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, 'Grandstream')

        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 = BaseGrandstreamHTTPDeviceInfoExtractor()

    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 '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)
        self._check_lines_password(raw_config)
        self._add_sip_transport(raw_config)
        self._add_timezone(raw_config)
        self._add_locale(raw_config)
        self._add_dtmf_mode(raw_config)
        self._add_fkeys(raw_config)
        self._add_mpk(raw_config)
        self._add_v2_fkeys(raw_config, device.get(u'model'))
        self._add_dns(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 _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' in device:
            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 _add_timezone(self, raw_config):
        if u'timezone' in raw_config and raw_config[u'timezone'] in TZ_NAME:
            raw_config[u'XX_timezone'] = TZ_NAME[raw_config[u'timezone']]
        else:
            raw_config['timezone'] = TZ_NAME['Europe/Paris']

    def _add_locale(self, raw_config):
        locale = raw_config.get(u'locale')
        if locale in LOCALE:
            raw_config[u'XX_locale'] = LOCALE[locale]

    def _add_fkeys(self, raw_config):
        lines = []
        for funckey_no, funckey_dict in raw_config[u'funckeys'].iteritems():
            i_funckey_no = int(funckey_no)
            funckey_type = funckey_dict[u'type']
            if funckey_type not in FUNCKEY_TYPES:
                logger.info('Unsupported funckey type: %s', funckey_type)
                continue
            type_code = u'P32%s' % (i_funckey_no + 2)
            lines.append((type_code, FUNCKEY_TYPES[funckey_type]))
            line_code = self._format_code(3 * i_funckey_no - 2)
            lines.append((line_code, int(funckey_dict[u'line']) - 1))
            if u'label' in funckey_dict:
                label_code = self._format_code(3 * i_funckey_no - 1)
                lines.append((label_code, funckey_dict[u'label']))
            value_code = self._format_code(3 * i_funckey_no)
            lines.append((value_code, funckey_dict[u'value']))
        raw_config[u'XX_fkeys'] = lines

    def _add_mpk(self, raw_config):
        lines = []
        start_code = 23000
        for funckey_no, funckey_dict in raw_config[u'funckeys'].iteritems():
            i_funckey_no = int(funckey_no)  # starts at 1
            funckey_type = funckey_dict[u'type']
            if funckey_type not in FUNCKEY_TYPES:
                logger.info('Unsupported funckey type: %s', funckey_type)
                continue
            start_p_code = start_code + (i_funckey_no - 1) * 5
            type_code = u'P{}'.format(start_p_code)
            lines.append((type_code, FUNCKEY_TYPES[funckey_type]))
            line_code = u'P{}'.format(start_p_code + 1)
            lines.append((line_code, int(funckey_dict[u'line']) - 1))
            if u'label' in funckey_dict:
                label_code = u'P{}'.format(start_p_code + 2)
                lines.append((label_code, funckey_dict[u'label']))
            value_code = u'P{}'.format(start_p_code + 3)
            lines.append((value_code, funckey_dict[u'value']))
        raw_config[u'XX_mpk'] = lines

    def _add_v2_fkeys(self, raw_config, model):
        lines = []
        model_fkeys = self.MODEL_FKEYS.get(model)
        if not model_fkeys:
            logger.info('Unknown model: "%s"', model)
            return
        for funckey_no in range(1, model_fkeys[u'vpk'] + 1):
            funckey = raw_config[u'funckeys'].get(str(funckey_no), {})
            funckey_type = funckey.get(u'type', 'disabled')
            if funckey_type not in FUNCKEY_TYPES:
                logger.info('Unsupported funckey type: %s', funckey_type)
                continue
            if str(funckey_no) in raw_config[u'sip_lines']:
                logger.info(
                    'Function key %s would conflict with an existing line',
                    funckey_no)
                continue
            lines.append((
                funckey_no,
                {
                    u'section': u'vpk',
                    u'type': FUNCKEY_TYPES[funckey_type],
                    u'label': funckey.get(u'label') or u'',
                    u'value': funckey.get(u'value') or u'',
                },
            ))
        for funckey_no in range(1, model_fkeys[u'mpk'] + 1):
            funckey = raw_config[u'funckeys'].get(
                str(funckey_no + model_fkeys[u'vpk']), {})
            funckey_type = funckey.get(u'type', 'disabled')
            if funckey_type not in FUNCKEY_TYPES:
                logger.info('Unsupported funckey type: %s', funckey_type)
            lines.append((
                funckey_no,
                {
                    u'section': u'mpk',
                    u'type': FUNCKEY_TYPES[funckey_type],
                    u'label': funckey.get(u'label') or u'',
                    u'value': funckey.get(u'value') or u'',
                },
            ))
        raw_config[u'XX_v2_fkeys'] = lines

    def _format_code(self, code):
        if code >= 10:
            str_code = str(code)
        else:
            str_code = u'0%s' % code
        return u'P3%s' % str_code

    def _add_dns(self, raw_config):
        if raw_config.get(u'dns_enabled'):
            dns_parts = raw_config[u'dns_ip'].split('.')
            for part_nb, part in enumerate(dns_parts, start=1):
                raw_config[u'XX_dns_%s' % part_nb] = part

    def _add_dtmf_mode(self, raw_config):
        if raw_config.get(u'sip_dtmf_mode'):
            dtmf_info = self.DTMF_MODES[raw_config[u'sip_dtmf_mode']]
            raw_config['XX_dtmf_in_audio'] = dtmf_info[0]
            raw_config['XX_dtmf_in_rtp'] = dtmf_info[1]
            raw_config['XX_dtmf_in_sip'] = dtmf_info[2]

    def _add_sip_transport(self, raw_config):
        sip_transport = raw_config.get(u'sip_transport')
        if sip_transport in self.SIP_TRANSPORTS:
            raw_config[u'XX_sip_transport'] = self.SIP_TRANSPORTS[
                sip_transport]
예제 #24
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)
예제 #25
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)
예제 #26
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)
예제 #27
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)
예제 #28
0
class BaseGigasetPlugin(StandardPlugin):
    _ENCODING = 'UTF-8'

    _SIP_DTMF_MODE = {
        u'RTP-in-band': u'1',
        u'RTP-out-of-band': u'2',
        u'SIP-INFO': u'4',
    }
    _SIP_SRTP_MODE = {
        u'disabled': u'0',
        u'preferred': u'1',
        u'required': u'1',
    }
    _SIP_TRANSPORT = {
        u'udp': u'1',
        u'tcp': u'2',
        u'tls': u'3',
    }

    _VALID_TZ_GIGASET = set((
        'Pacific/Honolulu',
        'America/Anchorage',
        'America/Los_Angeles',
        'America/Denver',
        'America/Chicago',
        'America/New_York',
        'America/Caracas',
        'America/Sao_Paulo',
        'Europe/Belfast',
        'Europe/Dublin',
        'Europe/Guernsey',
        'Europe/Isle_of_Man',
        'Europe/Jersey',
        'Europe/Lisbon',
        'Europe/London',
        'Greenwich',
        'Europe/Amsterdam',
        'Europe/Andorra',
        'Europe/Belgrade',
        'Europe/Berlin',
        'Europe/Bratislava',
        'Europe/Brussels',
        'Europe/Budapest',
        'Europe/Busingen',
        'Europe/Copenhagen',
        'Europe/Gibraltar',
        'Europe/Ljubljana',
        'Europe/Luxembourg',
        'Europe/Madrid',
        'Europe/Malta',
        'Europe/Monaco',
        'Europe/Oslo',
        'Europe/Paris',
        'Europe/Podgorica',
        'Europe/Prague',
        'Europe/Rome',
        'Europe/San_Marino',
        'Europe/Sarajevo',
        'Europe/Skopje',
        'Europe/Stockholm',
        'Europe/Tirane',
        'Europe/Vaduz',
        'Europe/Vatican',
        'Europe/Vienna',
        'Europe/Warsaw',
        'Europe/Zagreb',
        'Europe/Zurich',
        'Africa/Cairo',
        'Europe/Athens',
        'Europe/Bucharest',
        'Europe/Chisinau',
        'Europe/Helsinki',
        'Europe/Kaliningrad',
        'Europe/Kiev',
        'Europe/Mariehamn',
        'Europe/Nicosia',
        'Europe/Riga',
        'Europe/Sofia',
        'Europe/Tallinn',
        'Europe/Tiraspol',
        'Europe/Uzhgorod',
        'Europe/Vilnius',
        'Europe/Zaporozhye',
        'Europe/Istanbul',
        'Europe/Kirov',
        'Europe/Minsk',
        'Europe/Moscow',
        'Europe/Simferopol',
        'Europe/Volgograd',
        'Asia/Dubai',
        'Europe/Astrakhan',
        'Europe/Samara',
        'Europe/Ulyanovsk',
        'Asia/Karachi',
        'Asia/Dhaka',
        'Asia/Hong_Kong',
        'Asia/Tokyo',
        'Australia/Adelaide',
        'Australia/Darwin',
        'Australia/Brisbane',
        'Australia/Sydney',
        'Pacific/Noumea',
    ))

    _FALLBACK_TZ = {
        (-3, 0): 'America/Sao_Paulo',
        (-4, 0): 'America/New_York',
        (-5, 0): 'America/Chicago',
        (-6, 0): 'America/Denver',
        (-7, 0): 'America/Los_Angeles',
        (-8, 0): 'America/Anchorage',
        (-10, 0): 'Pacific/Honolulu',
        (0, 0): 'Greenwich',
        (1, 0): 'Europe/London',
        (2, 0): 'Europe/Paris',
        (3, 0): 'Europe/Moscow',
        (4, 0): 'Asia/Dubai',
        (5, 0): 'Asia/Karachi',
        (6, 0): 'Asia/Dhaka',
        (8, 0): 'Asia/Hong_Kong',
        (9, 0): 'Asia/Tokyo',
        (9, 3): 'Australia/Adelaide',
        (10, 0): 'Australia/Sydney',
        (11, 0): 'Pacific/Noumea',
    }

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

        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 = GigasetHTTPDeviceInfoExtractor()

    def _check_device(self, device):
        if u'ip' not in device:
            raise Exception('IP address needed for Gigaset configuration')

    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 + '.xml'

    def _add_phonebook(self, raw_config):
        uuid_format = u'{scheme}://{hostname}:{port}/0.1/directories/lookup/{profile}/gigaset/{user_uuid}?'
        plugins.add_xivo_phonebook_url_from_format(raw_config, uuid_format)

    def _fix_timezone(self, raw_config):
        timezone = raw_config.get(u'timezone', 'Greenwich')
        if timezone not in self._VALID_TZ_GIGASET:
            tz_db = tzinform.TextTimezoneInfoDB()
            tz_info = tz_db.get_timezone_info(timezone)['utcoffset'].as_hms
            offset_hour = tz_info[0]
            offset_minutes = tz_info[1]
            raw_config[u'timezone'] = self._FALLBACK_TZ[(offset_hour,
                                                         offset_minutes)]

    def _add_xx_vars(self, device, raw_config):
        raw_config[u'XX_epoch'] = int(time.time())
        self._fix_timezone(raw_config)

    def _add_voip_providers(self, raw_config):
        voip_providers = dict()
        provider_id = 0
        sip_lines = raw_config.get(u'sip_lines')
        dtmf_mode = raw_config.get(u'sip_dtmf_mode', '1')
        srtp_mode = raw_config.get(u'sip_srtp_mode', '0')
        sip_transport = self._SIP_TRANSPORT.get(
            raw_config.get(u'sip_transport', '1'))
        if sip_lines:
            for line in sip_lines.itervalues():
                proxy_ip = line.get(u'proxy_ip')
                proxy_port = line.get(u'proxy_port', 5060)
                line_dtmf_mode = self._SIP_DTMF_MODE.get(
                    line.get(u'dtmf_mode', dtmf_mode))
                line_srtp_mode = self._SIP_SRTP_MODE.get(
                    line.get(u'strp_mode', srtp_mode))
                if (proxy_ip, proxy_port) not in voip_providers:
                    provider = {
                        u'id': provider_id,
                        u'sip_proxy_ip': proxy_ip,
                        u'sip_proxy_port': proxy_port,
                        u'dtmf_mode': line_dtmf_mode,
                        u'srtp_mode': line_srtp_mode,
                        u'sip_transport': sip_transport,
                    }
                    line[u'provider_id'] = provider_id
                    voip_providers[(proxy_ip, proxy_port)] = provider
                    provider_id += 1
                else:
                    line[u'provider_id'] = voip_providers[(proxy_ip,
                                                           proxy_port)]['id']

        raw_config[u'XX_voip_providers'] = voip_providers.values()

    def _add_ac_code(self, raw_config):
        sip_lines = raw_config.get(u'sip_lines')
        if sip_lines:
            for line in sip_lines.itervalues():
                number = line.get(u'number')
                if number.startswith(u'auto'):
                    line[u'XX_hs_code'] = '0000'
                else:
                    line[u'XX_hs_code'] = number[-4:].zfill(4)

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

        self._add_voip_providers(raw_config)
        self._add_ac_code(raw_config)
        self._add_xx_vars(device, raw_config)
        self._add_phonebook(raw_config)

        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 as e:
            logger.info('error while removing configuration file: %s', e)

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

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

    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')
예제 #29
0
class BaseCiscoSipPlugin(StandardPlugin):
    _ENCODING = 'UTF-8'
    _LOCALE = {
        # <locale>: (<name>, <lang code>, <network locale>)
        u'de_DE': (u'german_germany', u'de', u'germany'),
        u'en_US': (u'english_united_states', u'en', u'united_states'),
        u'es_ES': (u'spanish_spain', u'es', u'spain'),
        u'fr_FR': (u'french_france', u'fr', u'france'),
        u'fr_CA': (u'french_france', u'fr', u'canada')
    }
    
    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)
        
        handlers = FetchfwPluginHelper.new_handlers(gen_cfg.get('proxies'))
        downloaders = FetchfwPluginHelper.new_downloaders_from_handlers(handlers)
        cisco_dler = common['CiscoDownloader'](handlers)
        downloaders['x-cisco'] = cisco_dler
        fetchfw_helper = FetchfwPluginHelper(plugin_dir, downloaders)
        
        cfg_service = common['CiscoConfigureService'](cisco_dler, spec_cfg.get('username'),
                                                      spec_cfg.get('password'))
        persister = JsonConfigPersister(os.path.join(self._plugin_dir, 'var',
                                                     'config.json'))
        cfg_service = PersistentConfigureServiceDecorator(cfg_service, persister)
        
        self.services = {'configure': cfg_service,
                         'install': fetchfw_helper}
        self.tftp_service = TFTPFileService(self._tftpboot_dir)
    
    dhcp_dev_info_extractor = common['BaseCiscoDHCPDeviceInfoExtractor']()
    
    tftp_dev_info_extractor = common['BaseCiscoTFTPDeviceInfoExtractor']() 
    
    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 'SEP%s.cfg.xml' % fmted_mac
    
    def _check_config(self, raw_config):
        if u'tftp_port' not in raw_config:
            raise RawConfigError('only support configuration via TFTP')
    
    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)
        
        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)
예제 #30
0
class BaseGigasetPlugin(StandardPlugin):
    _ENCODING = 'UTF-8'
    # _BROKER_FACTORY attribute must be present in (derived) instance of this class
    
    def __init__(self, app, plugin_dir, gen_cfg, spec_cfg):
        StandardPlugin.__init__(self, app, plugin_dir, gen_cfg, spec_cfg)
        self._app = app
        
        self._tpl_helper = TemplatePluginHelper(plugin_dir)
        
    dhcp_dev_info_extractor = BaseGigasetDHCPDeviceInfoExtractor()
    
    def _check_device(self, device):
        if u'ip' not in device:
            raise Exception('IP address needed for Gigaset configuration')
    
    def configure(self, device, raw_config):
        self._check_device(device)
        # nothing else to do
    
    def deconfigure(self, device):
        # nothing to do
        pass
    
    def _do_synchronize(self, device, raw_config):
        # 1. generate template and read it as our own config for our broker
        tpl = self._tpl_helper.get_dev_template(raw_config.get(u'mac'), device)
        fobj = StringIO(tpl.render(raw_config))
        
        config = RawConfigParser()
        config.readfp(fobj)
        fobj.close()
        
        general = dict(config.items('general'))
        
        # 2. instantiate broker and do requests...
        host = device[u'ip']
        pin = general.get('pin')
        broker = self._BROKER_FACTORY(host, pin)
        
        broker.login()
        try:
            # update info
            if general.get('skip_update_infos') != '1':
                dev_info = broker.get_device_info()
                if dev_info:
                    device.update(dev_info)
                    from twisted.internet import reactor
                    reactor.callFromThread(self._app.dev_update, device)
            
            # configure lines
            if general.get('skip_lines_configuration') != '1':
                for line_no in xrange(1, 7):
                    line_no_str = str(line_no)
                    if line_no_str in raw_config[u'sip_lines']:
                        line = raw_config[u'sip_lines'][line_no_str]
                        kwargs = {
                            'password': line[u'password'],
                            'auth_username': line[u'auth_username'],
                            'username': line[u'username'],
                            'display_name': line[u'display_name'],
                            'proxy_ip': line.get(u'proxy_ip') or \
                                        raw_config[u'sip_proxy_ip'],
                            'proxy_port': line.get(u'proxy_port') or \
                                          raw_config.get(u'proxy_port', '5060'),
                            'registrar_ip': line.get(u'registrar_ip') or \
                                            raw_config[u'sip_registrar_ip'],
                            'registrar_port': line.get(u'registrar_port') or \
                                              raw_config.get(u'registrar_port', '5060')
                        }
                        broker.set_line(line_no, **kwargs)
                    else:
                        if general.get('skip_delete_line') != '1':
                            broker.delete_line(line_no)
            
            # disable gigaset line
            if general.get('skip_disable_gigasetnet_line') != '1':
                broker.disable_gigasetnet_line()
            
            # configure mailboxes
            if general.get('skip_mailboxes_configuration') != '1':
                mailboxes = {}
                for line_no_str, line in raw_config[u'sip_lines'].iteritems():
                    line_no = int(line_no_str)
                    voicemail = line.get(u'voicemail') or raw_config.get(u'exten_voicemail')
                    if voicemail:
                        mailboxes[line_no] = voicemail 
            
            # do generic requests stuff here
            config_dict = dict((s, dict(config.items(s))) for s in config.sections() if s != 'general')
            for path, raw_data in config_dict.iteritems():
                with broker.do_post_request(path, raw_data) as fobj:
                    fobj.read()
        finally:
            broker.logout()
    
    def synchronize(self, device, raw_config):
        assert u'ip' in device      # see self.configure() and plugin contract
        
        return threads.deferToThread(self._do_synchronize, device, raw_config)
예제 #31
0
class BaseGrandstreamPlugin(StandardPlugin):
    _ENCODING = 'UTF-8'

    DTMF_MODES = {
        # mode: (in audio, in RTP, in SIP)
        u'RTP-in-band': ('Yes', 'Yes', 'No'),
        u'RTP-out-of-band': ('No', 'Yes', 'No'),
        u'SIP-INFO': ('No', 'No', 'Yes'),
    }

    SIP_TRANSPORTS = {
        u'udp': u'UDP',
        u'tcp': u'TCP',
        u'tls': u'TlsOrTcp',
    }

    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._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._tftpboot_dir)

    http_dev_info_extractor = BaseGrandstreamHTTPDeviceInfoExtractor()

    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 '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)
        self._check_lines_password(raw_config)
        self._add_sip_transport(raw_config)
        self._add_timezone(raw_config)
        self._add_locale(raw_config)
        self._add_dtmf_mode(raw_config)
        self._add_dns(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 _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' in device:
            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 _add_timezone(self, raw_config):
        if u'timezone' in raw_config and raw_config[u'timezone'] in TZ_NAME:
            raw_config[u'XX_timezone'] = TZ_NAME[raw_config[u'timezone']]
        else:
            raw_config['timezone'] = TZ_NAME['Europe/Paris']

    def _add_locale(self, raw_config):
        locale = raw_config.get(u'locale')
        if locale in LOCALE:
            raw_config[u'XX_locale'] = LOCALE[locale]

    def _add_dns(self, raw_config):
        if raw_config.get(u'dns_enabled'):
            dns_parts = raw_config[u'dns_ip'].split('.')
            for part_nb, part in enumerate(dns_parts, start=1):
                raw_config[u'XX_dns_%s' % part_nb] = part

    def _add_dtmf_mode(self, raw_config):
        if raw_config.get(u'sip_dtmf_mode'):
            dtmf_info = self.DTMF_MODES[raw_config[u'sip_dtmf_mode']]
            raw_config['XX_dtmf_in_audio'] = dtmf_info[0]
            raw_config['XX_dtmf_in_rtp'] = dtmf_info[1]
            raw_config['XX_dtmf_in_sip'] = dtmf_info[2]

    def _add_sip_transport(self, raw_config):
        sip_transport = raw_config.get(u'sip_transport')
        if sip_transport in self.SIP_TRANSPORTS:
            raw_config[u'XX_sip_transport'] = self.SIP_TRANSPORTS[
                sip_transport]
예제 #32
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)
예제 #33
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)
예제 #34
0
class BaseZenitelPlugin(StandardPlugin):
    _ENCODING = 'UTF-8'
    _VALID_FUNCKEY_NO = [u'1', u'2', u'3']

    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)

        cfg_service = ZenitelConfigureService(downloaders['auth'],
                                              spec_cfg.get('username'),
                                              spec_cfg.get('password'))
        persister = JsonConfigPersister(
            os.path.join(self._plugin_dir, 'var', 'config.json'))
        cfg_service = PersistentConfigureServiceDecorator(
            cfg_service, persister)

        self.services = {'configure': cfg_service, 'install': fetchfw_helper}
        self.tftp_service = TFTPFileService(self._tftpboot_dir)

    tftp_dev_info_extractor = BaseZenitelTFTPDeviceInfoExtractor()

    pg_associator = BaseZenitelPgAssociator()

    def _add_sip_section_info(self, raw_config):
        if u'1' in raw_config[u'sip_lines']:
            line = raw_config[u'sip_lines'][u'1']
            raw_config[u'XX_sip'] = True
            raw_config[u'XX_nick_name'] = line[u'display_name']
            raw_config[u'XX_sip_id'] = line[u'username']
            raw_config[u'XX_domain'] = line.get(
                u'proxy_ip') or raw_config[u'sip_proxy_ip']
            raw_config[u'XX_domain2'] = line.get(u'backup_proxy_ip') or \
                                        raw_config.get(u'backup_proxy_ip', u'')
            raw_config[u'XX_auth_user'] = line[u'auth_username']
            raw_config[u'XX_auth_pwd'] = line[u'password']

    def _add_fkeys(self, raw_config):
        lines = []
        for funckey_no, funckey_dict in sorted(
                raw_config[u'funckeys'].iteritems(), key=itemgetter(0)):
            if funckey_no in self._VALID_FUNCKEY_NO:
                if funckey_dict[u'type'] == u'speeddial':
                    exten = funckey_dict[u'value']
                    lines.append(u'speeddial_%s_c1=%s' % (funckey_no, exten))
                else:
                    logger.info('Unsupported funckey type: %s',
                                funckey_dict[u'type'])
            else:
                logger.info('Out of range funckey no: %s', funckey_no)
        raw_config[u'XX_fkeys'] = u'\n'.join(' ' + s for s in lines)

    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 'ipst_config_%s.cfg' % fmted_mac

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

    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_sip_section_info(raw_config)
        self._add_fkeys(raw_config)

        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)
예제 #35
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)
예제 #36
0
 def __init__(self, app, plugin_dir, gen_cfg, spec_cfg):
     StandardPlugin.__init__(self, app, plugin_dir, gen_cfg, spec_cfg)
     self._app = app
     
     self._tpl_helper = TemplatePluginHelper(plugin_dir)
예제 #37
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)
예제 #38
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())
예제 #39
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)
예제 #40
0
class BaseGrandstreamPlugin(StandardPlugin):
    _ENCODING = 'UTF-8'

    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 = GrandstreamHTTPDeviceInfoExtractor()

    def configure(self, device, raw_config):
        self._check_config(raw_config)
        self._check_device(device)
        self._add_timezone(raw_config)
        self._add_locale(raw_config)
        raw_config['XX_mac'] = self._format_mac(device)
        raw_config['XX_main_proxy_ip'] = self._get_main_proxy_ip(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):
        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_device(self, device):
        if u'mac' not in device:
            raise Exception('MAC address needed to configure device')

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

    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 'cfg' + fmted_mac + '.xml'

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

    def _add_timezone(self, raw_config):
        if u'timezone' in raw_config and raw_config[u'timezone'] in TZ_NAME:
            raw_config[u'XX_timezone'] = TZ_NAME[raw_config[u'timezone']]
        else:
            raw_config['timezone'] = TZ_NAME['Europe/Paris']

    def _add_locale(self, raw_config):
       locale = raw_config.get(u'locale')
       if locale in LOCALE:
            raw_config[u'XX_locale'] = LOCALE[locale]

    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']
예제 #41
0
파일: common.py 프로젝트: Eyepea/xivo-skaro
class BaseZenitelPlugin(StandardPlugin):
    _ENCODING = 'UTF-8'
    _VALID_FUNCKEY_NO = [u'1', u'2', u'3']
    
    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)
        
        cfg_service = ZenitelConfigureService(downloaders['auth'],
                                              spec_cfg.get('username'),
                                              spec_cfg.get('password'))
        persister = JsonConfigPersister(os.path.join(self._plugin_dir, 'var',
                                                     'config.json'))
        cfg_service = PersistentConfigureServiceDecorator(cfg_service, persister)
        
        self.services = {'configure': cfg_service,
                         'install': fetchfw_helper}
        self.tftp_service = TFTPFileService(self._tftpboot_dir)

    tftp_dev_info_extractor = BaseZenitelTFTPDeviceInfoExtractor()
    
    pg_associator = BaseZenitelPgAssociator()
    
    def _add_sip_section_info(self, raw_config):
        if u'1' in raw_config[u'sip_lines']:
            line = raw_config[u'sip_lines'][u'1']
            raw_config[u'XX_sip'] = True
            raw_config[u'XX_nick_name'] = line[u'display_name']
            raw_config[u'XX_sip_id'] = line[u'username']
            raw_config[u'XX_domain'] = line.get(u'proxy_ip') or raw_config[u'sip_proxy_ip']
            raw_config[u'XX_domain2'] = line.get(u'backup_proxy_ip') or \
                                        raw_config.get(u'backup_proxy_ip', u'')
            raw_config[u'XX_auth_user'] = line[u'auth_username']
            raw_config[u'XX_auth_pwd'] = line[u'password']
    
    def _add_fkeys(self, raw_config):
        lines = []
        for funckey_no, funckey_dict in sorted(raw_config[u'funckeys'].iteritems(),
                                               key=itemgetter(0)):
            if funckey_no in self._VALID_FUNCKEY_NO:
                if funckey_dict[u'type'] == u'speeddial':
                    exten = funckey_dict[u'value']
                    lines.append(u'speeddial_%s_c1=%s' % (funckey_no, exten))
                else:
                    logger.info('Unsupported funckey type: %s', funckey_dict[u'type'])
            else:
                logger.info('Out of range funckey no: %s', funckey_no)
        raw_config[u'XX_fkeys'] = u'\n'.join(' ' + s for s in lines)
    
    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 'ipst_config_%s.cfg' % fmted_mac
    
    def _check_config(self, raw_config):
        if u'tftp_port' not in raw_config:
            raise RawConfigError('only support configuration via TFTP')
    
    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_sip_section_info(raw_config)
        self._add_fkeys(raw_config)
        
        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)
예제 #42
0
class BaseGrandstreamPlugin(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, 'Grandstream')

        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 = BaseGrandstreamHTTPDeviceInfoExtractor()

    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 '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)
        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 _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 _add_timezone(self, raw_config):
        if u'timezone' in raw_config and raw_config[u'timezone'] in TZ_NAME:
            raw_config[u'XX_timezone'] = TZ_NAME[raw_config[u'timezone']]
        else:
            raw_config['timezone'] = TZ_NAME['Europe/Paris']

    def _add_locale(self, raw_config):
       locale = raw_config.get(u'locale')
       if locale in LOCALE:
            raw_config[u'XX_locale'] = LOCALE[locale]

    def _add_fkeys(self, raw_config):
        lines = []
        for funckey_no, funckey_dict in raw_config[u'funckeys'].iteritems():
            i_funckey_no = int(funckey_no)
            funckey_type = funckey_dict[u'type']
            if funckey_type not in FUNCKEY_TYPES:
                logger.info('Unsupported funckey type: %s', funckey_type)
                continue
            type_code = u'P32%s' % (i_funckey_no + 2)
            lines.append(self._format_line(type_code, FUNCKEY_TYPES[funckey_type]))
            line_code = self._format_code(3*i_funckey_no - 2)
            lines.append(self._format_line(line_code, int(funckey_dict[u'line']) - 1))
            if u'label' in funckey_dict : 
                label_code = self._format_code(3*i_funckey_no - 1)
                lines.append(self._format_line(label_code, funckey_dict[u'label']))
            value_code = self._format_code(3*i_funckey_no)
            lines.append(self._format_line(value_code, funckey_dict[u'value']))
        raw_config[u'XX_fkeys'] = u'\n'.join(lines)

    def _format_line(self, code, value):
        return u'    <%s>%s</%s>' % (code, value, code)

    def _format_code(self, code):
        if code >= 10:
            str_code = str(code)
        else:
            str_code = u'0%s' % code
        return u'P3%s' % str_code
예제 #43
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)
예제 #44
0
파일: common.py 프로젝트: Eyepea/xivo-skaro
class BaseGigasetPlugin(StandardPlugin):
    _ENCODING = 'UTF-8'
    # _BROKER_FACTORY attribute must be present in (derived) instance of this class
    
    def __init__(self, app, plugin_dir, gen_cfg, spec_cfg):
        StandardPlugin.__init__(self, app, plugin_dir, gen_cfg, spec_cfg)
        self._app = app
        
        self._tpl_helper = TemplatePluginHelper(plugin_dir)
        
    dhcp_dev_info_extractor = BaseGigasetDHCPDeviceInfoExtractor()
    
    def _check_device(self, device):
        if u'ip' not in device:
            raise Exception('IP address needed for Gigaset configuration')
    
    def configure(self, device, raw_config):
        self._check_device(device)
        # nothing else to do
    
    def deconfigure(self, device):
        # nothing to do
        pass
    
    def _do_synchronize(self, device, raw_config):
        # 1. generate template and read it as our own config for our broker
        tpl = self._tpl_helper.get_dev_template(raw_config.get(u'mac'), device)
        fobj = StringIO(tpl.render(raw_config))
        
        config = RawConfigParser()
        config.readfp(fobj)
        fobj.close()
        
        general = dict(config.items('general'))
        
        # 2. instantiate broker and do requests...
        host = device[u'ip']
        pin = general.get('pin')
        broker = self._BROKER_FACTORY(host, pin)
        
        broker.login()
        try:
            # update info
            if general.get('skip_update_infos') != '1':
                dev_info = broker.get_device_info()
                if dev_info:
                    device.update(dev_info)
                    from twisted.internet import reactor
                    reactor.callFromThread(self._app.dev_update, device)
            
            # configure lines
            if general.get('skip_lines_configuration') != '1':
                for line_no in xrange(1, 7):
                    line_no_str = str(line_no)
                    if line_no_str in raw_config[u'sip_lines']:
                        line = raw_config[u'sip_lines'][line_no_str]
                        kwargs = {
                            'password': line[u'password'],
                            'auth_username': line[u'auth_username'],
                            'username': line[u'username'],
                            'display_name': line[u'display_name'],
                            'proxy_ip': line.get(u'proxy_ip') or \
                                        raw_config[u'sip_proxy_ip'],
                            'proxy_port': line.get(u'proxy_port') or \
                                          raw_config.get(u'proxy_port', '5060'),
                            'registrar_ip': line.get(u'registrar_ip') or \
                                            raw_config[u'sip_registrar_ip'],
                            'registrar_port': line.get(u'registrar_port') or \
                                              raw_config.get(u'registrar_port', '5060')
                        }
                        broker.set_line(line_no, **kwargs)
                    else:
                        if general.get('skip_delete_line') != '1':
                            broker.delete_line(line_no)
            
            # disable gigaset line
            if general.get('skip_disable_gigasetnet_line') != '1':
                broker.disable_gigasetnet_line()
            
            # configure mailboxes
            if general.get('skip_mailboxes_configuration') != '1':
                mailboxes = {}
                for line_no_str, line in raw_config[u'sip_lines'].iteritems():
                    line_no = int(line_no_str)
                    voicemail = line.get(u'voicemail') or raw_config.get(u'exten_voicemail')
                    if voicemail:
                        mailboxes[line_no] = voicemail 
            
            # do generic requests stuff here
            config_dict = dict((s, dict(config.items(s))) for s in config.sections() if s != 'general')
            for path, raw_data in config_dict.iteritems():
                with broker.do_post_request(path, raw_data) as fobj:
                    fobj.read()
        finally:
            broker.logout()
    
    def synchronize(self, device, raw_config):
        assert u'ip' in device      # see self.configure() and plugin contract
        
        return threads.deferToThread(self._do_synchronize, device, raw_config)
예제 #45
0
파일: common.py 프로젝트: Eyepea/xivo-skaro
 def __init__(self, app, plugin_dir, gen_cfg, spec_cfg):
     StandardPlugin.__init__(self, app, plugin_dir, gen_cfg, spec_cfg)
     self._app = app
     
     self._tpl_helper = TemplatePluginHelper(plugin_dir)
예제 #46
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)
예제 #47
0
class BaseGrandstreamPlugin(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, 'Grandstream')

        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 = BaseGrandstreamHTTPDeviceInfoExtractor()

    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 '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)
        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 _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 _add_timezone(self, raw_config):
        if u'timezone' in raw_config and raw_config[u'timezone'] in TZ_NAME:
            raw_config[u'XX_timezone'] = TZ_NAME[raw_config[u'timezone']]
        else:
            raw_config['timezone'] = TZ_NAME['Europe/Paris']

    def _add_locale(self, raw_config):
        locale = raw_config.get(u'locale')
        if locale in LOCALE:
            raw_config[u'XX_locale'] = LOCALE[locale]

    def _add_fkeys(self, raw_config):
        lines = []
        for funckey_no, funckey_dict in raw_config[u'funckeys'].iteritems():
            i_funckey_no = int(funckey_no)
            funckey_type = funckey_dict[u'type']
            if funckey_type not in FUNCKEY_TYPES:
                logger.info('Unsupported funckey type: %s', funckey_type)
                continue
            type_code = u'P32%s' % (i_funckey_no + 2)
            lines.append(
                self._format_line(type_code, FUNCKEY_TYPES[funckey_type]))
            line_code = self._format_code(3 * i_funckey_no - 2)
            lines.append(
                self._format_line(line_code,
                                  int(funckey_dict[u'line']) - 1))
            if u'label' in funckey_dict:
                label_code = self._format_code(3 * i_funckey_no - 1)
                lines.append(
                    self._format_line(label_code, funckey_dict[u'label']))
            value_code = self._format_code(3 * i_funckey_no)
            lines.append(self._format_line(value_code, funckey_dict[u'value']))
        raw_config[u'XX_fkeys'] = u'\n'.join(lines)

    def _format_line(self, code, value):
        return u'    <%s>%s</%s>' % (code, value, code)

    def _format_code(self, code):
        if code >= 10:
            str_code = str(code)
        else:
            str_code = u'0%s' % code
        return u'P3%s' % str_code
예제 #48
0
class BasePattonPlugin(StandardPlugin):

    _ENCODING = 'ascii'
    _SIP_DTMF_MODE = {
        u'RTP-in-band': u'default',
        u'RTP-out-of-band': u'rtp',
        u'SIP-INFO': u'signaling'
    }

    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 = BasePattonHTTPDeviceInfoExtractor()

    def _add_syslog_level(self, raw_config):
        if u'syslog_level' in raw_config:
            if raw_config[u'syslog_level'] == u'info':
                raw_config[u'XX_syslog_level'] = u'informational'
            else:
                raw_config[u'XX_syslog_level'] = raw_config[u'syslog_level']

    def _add_timezone_and_dst(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.info('Unknown timezone: %s', e)
            else:
                converter = _TimezoneConverter(tzinfo)
                raw_config[u'XX_timezone_offset'] = converter.default_offset()
                if converter.has_dst():
                    raw_config[u'XX_dst_offset'] = converter.dst_offset()
                    raw_config[u'XX_dst_start'] = converter.dst_start()
                    raw_config[u'XX_dst_end'] = converter.dst_end()

    def _update_sip_transport(self, raw_config):
        if u'sip_transport' not in raw_config:
            raw_config[u'sip_transport'] = u'udp'
        elif raw_config.get(u'sip_transport') == u'tls':
            logger.warning(
                "Patton doesn't support the SIP transport tls: fallback to udp"
            )
            raw_config[u'sip_transport'] = u'udp'

    def _add_dtmf_relay(self, raw_config):
        if u'sip_dtmf_mode' in raw_config:
            raw_config[u'XX_dtmf_relay'] = self._SIP_DTMF_MODE[
                raw_config[u'sip_dtmf_mode']]

    def _add_lines_and_servers(self, raw_config):
        converter = _SIPLinesConverter()
        for sip_line_no, sip_line in raw_config[u'sip_lines'].iteritems():
            converter.add_sip_line(sip_line_no, sip_line)
        raw_config[u'XX_lines'] = converter.lines()
        raw_config[u'XX_servers'] = converter.servers()

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

    def _dev_specific_filename(self, device):
        fmted_mac = format_mac(device[u'mac'], separator='')
        return fmted_mac + '.cfg'

    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_device(device)
        filename = self._dev_specific_filename(device)
        tpl = self._tpl_helper.get_dev_template(filename, device)

        self._add_syslog_level(raw_config)
        self._add_timezone_and_dst(raw_config)
        self._update_sip_transport(raw_config)
        self._add_dtmf_relay(raw_config)
        self._add_lines_and_servers(raw_config)

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

    def deconfigure(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 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 is_sensitive_filename(self, filename):
        return bool(self._SENSITIVE_FILENAME_REGEX.match(filename))
예제 #49
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)
예제 #50
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)