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 <sip:%s@%s>%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)
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() )
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)
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))
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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())