diff --git a/.gitignore b/.gitignore index 2f43ec2..3660390 100644 --- a/.gitignore +++ b/.gitignore @@ -50,3 +50,5 @@ ch2.wav src/auracast/available_samples.txt src/auracast/server/stream_settings2.json src/scripts/temperature_log* + +src/auracast/server/recordings/ \ No newline at end of file diff --git a/src/auracast/multicast.py b/src/auracast/multicast.py index 0c451ba..c4caefd 100644 --- a/src/auracast/multicast.py +++ b/src/auracast/multicast.py @@ -60,6 +60,85 @@ import sounddevice as sd from collections import deque +class AlsaArecordAudioInput(audio_io.AudioInput): + def __init__(self, device_name: str, pcm_format: audio_io.PcmFormat): + self._device_name = device_name + self._pcm_format = pcm_format + self._proc: asyncio.subprocess.Process | None = None + + async def open(self) -> audio_io.PcmFormat: + if self._proc is not None: + return self._pcm_format + + args = [ + 'arecord', + '-D', self._device_name, + '-q', + '-t', 'raw', + '-f', 'S16_LE', + '-r', str(int(self._pcm_format.sample_rate)), + '-c', str(int(self._pcm_format.channels)), + ] + + logging.info( + "Opening ALSA capture via arecord: device='%s' rate=%s ch=%s", + self._device_name, + self._pcm_format.sample_rate, + self._pcm_format.channels, + ) + + self._proc = await asyncio.create_subprocess_exec( + *args, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.DEVNULL, + ) + + if self._proc.stdout is None: + raise RuntimeError('arecord stdout pipe was not created') + + return self._pcm_format + + def frames(self, frame_size: int) -> AsyncGenerator[bytes]: + async def _gen() -> AsyncGenerator[bytes]: + if self._proc is None: + await self.open() + + if self._proc is None or self._proc.stdout is None: + return + + bytes_per_frame = frame_size * self._pcm_format.channels * self._pcm_format.bytes_per_sample + + while True: + try: + data = await self._proc.stdout.readexactly(bytes_per_frame) + except asyncio.IncompleteReadError: + return + except Exception: + return + yield data + + return _gen() + + async def aclose(self) -> None: + if self._proc is None: + return + try: + if self._proc.returncode is None: + self._proc.terminate() + except ProcessLookupError: + pass + except Exception: + pass + with contextlib.suppress(Exception): + await asyncio.wait_for(self._proc.wait(), timeout=1.0) + if self._proc.returncode is None: + with contextlib.suppress(Exception): + self._proc.kill() + with contextlib.suppress(Exception): + await asyncio.wait_for(self._proc.wait(), timeout=1.0) + self._proc = None + + class ModSoundDeviceAudioInput(audio_io.SoundDeviceAudioInput): """Patched SoundDeviceAudioInput with low-latency capture and adaptive resampling.""" @@ -671,7 +750,13 @@ class Streamer(): # anything else, e.g. realtime stream from device (bumble) else: - audio_input = await audio_io.create_audio_input(audio_source, input_format) + if isinstance(audio_source, str) and audio_source.startswith('alsa:'): + if input_format == 'auto': + raise ValueError('input format details required for alsa input') + pcm = audio_io.PcmFormat.from_str(input_format) + audio_input = AlsaArecordAudioInput(audio_source[5:], pcm) + else: + audio_input = await audio_io.create_audio_input(audio_source, input_format) # Store early so stop_streaming can close even if open() fails big['audio_input'] = audio_input # SoundDeviceAudioInput (used for `mic:` captures) has no `.rewind`. diff --git a/src/auracast/server/multicast_frontend.py b/src/auracast/server/multicast_frontend.py index fde2288..028ae14 100644 --- a/src/auracast/server/multicast_frontend.py +++ b/src/auracast/server/multicast_frontend.py @@ -111,20 +111,37 @@ is_streaming = bool(saved_settings.get("is_streaming", False)) secondary_status = saved_settings.get("secondary") or {} secondary_is_streaming = bool(saved_settings.get("secondary_is_streaming", secondary_status.get("is_streaming", False))) +def validate_unique_input_devices(radio1_streams, radio2_streams): + """Validate that input devices are unique across all streams within each radio.""" + # Check Radio 1 devices + r1_devices = [s['input_device'] for s in radio1_streams if s.get('input_device')] + if len(r1_devices) != len(set(r1_devices)): + return False, "Duplicate input devices detected in Radio 1 streams. Each stream must use a unique input device." + + # Check Radio 2 devices + r2_devices = [s['input_device'] for s in radio2_streams if s.get('input_device')] + if len(r2_devices) != len(set(r2_devices)): + return False, "Duplicate input devices detected in Radio 2 streams. Each stream must use a unique input device." + + return True, "" + st.title("Auracast Audio Mode Control") -def render_stream_controls(status_streaming: bool, start_label: str, stop_label: str, mode_label: str): - c_start, c_stop, c_spacer, c_status = st.columns([1, 1, 1, 2], gap="small", vertical_alignment="center") +def render_stream_controls(status_streaming: bool, start_label: str, stop_label: str, mode_label: str, secondary_streaming: bool = False): + c_start, c_stop, c_status = st.columns([1, 1, 3], gap="small", vertical_alignment="center") with c_start: start_clicked = st.button(start_label, disabled=status_streaming) with c_stop: stop_clicked = st.button(stop_label, disabled=not status_streaming) with c_status: - st.write( - ( - f"Mode: {mode_label} ยท " + ("๐ŸŸข Streaming" if status_streaming else "๐Ÿ”ด Stopped") - ) - ) + if status_streaming: + if secondary_streaming: + status_text = "๐ŸŸข Streaming on both Radios" + else: + status_text = "๐ŸŸข Streaming on Radio 1" + else: + status_text = "๐Ÿ”ด Stopped" + st.write(f"Mode: {mode_label} ยท {status_text}") return start_clicked, stop_clicked # Audio mode selection with persisted default @@ -132,8 +149,7 @@ def render_stream_controls(status_streaming: bool, start_label: str, stop_label: options = [ "Demo", "Analog", - "USB", - "Network", + "Network - Dante", ] saved_audio_mode = saved_settings.get("audio_mode", "Demo") if saved_audio_mode not in options: @@ -145,9 +161,11 @@ audio_mode = st.selectbox( "Audio Mode", options, index=options.index(saved_audio_mode) if saved_audio_mode in options else options.index("Demo"), + disabled=is_streaming, help=( "Select the audio input source. Choose 'USB' for a connected USB audio device (via PipeWire), " "'Network' (AES67) for network RTP/AES67 sources, " + "'Network - Dante' for Dante inputs via ALSA, " "or 'Demo' for a simulated stream." ) ) @@ -172,6 +190,15 @@ if audio_mode == "Analog": else: running_mode = backend_mode_mapped if (is_streaming and backend_mode_mapped) else audio_mode +# Start/Stop buttons and status (moved to top) +if audio_mode == "Demo": + start_stream, stop_stream = render_stream_controls(is_streaming, "Start Demo", "Stop Demo", running_mode, secondary_is_streaming) +else: + start_stream, stop_stream = render_stream_controls(is_streaming, "Start Auracast", "Stop Auracast", running_mode, secondary_is_streaming) + +# Placeholder for validation errors (will be filled in later) +validation_error_placeholder = st.empty() + is_started = False is_stopped = False @@ -210,6 +237,7 @@ if audio_mode == "Demo": "Demo Stream Type", demo_options, index=default_index, + disabled=is_streaming, help="Select the demo stream configuration." ) # Stream password and flags (same as USB/AES67) @@ -218,6 +246,7 @@ if audio_mode == "Demo": "Stream Passwort", value=saved_pwd, type=("password"), + disabled=is_streaming, help="Optional: Set a broadcast code to protect your stream. Leave empty for an open (uncoded) broadcast." ) col_flags1, col_flags2, col_pdelay, col_qos = st.columns([1, 1, 0.7, 0.6], gap="small", vertical_alignment="center") @@ -225,12 +254,14 @@ if audio_mode == "Demo": assisted_listening = st.checkbox( "Assistive listening", value=bool(saved_settings.get('assisted_listening_stream', False)), + disabled=is_streaming, help="tells the receiver that this is an assistive listening stream" ) with col_flags2: immediate_rendering = st.checkbox( "Immediate rendering", value=bool(saved_settings.get('immediate_rendering', False)), + disabled=is_streaming, help="tells the receiver to ignore presentation delay and render immediately if possible." ) # QoS/presentation controls inline with flags @@ -240,6 +271,7 @@ if audio_mode == "Demo": presentation_delay_ms = st.number_input( "Delay (ms)", min_value=10, max_value=200, step=5, value=default_pdelay_ms, + disabled=is_streaming, help="Delay between capture and presentation for receivers." ) with col_qos: @@ -248,103 +280,10 @@ if audio_mode == "Demo": default_qos_idx = qos_options.index(saved_qos) if saved_qos in qos_options else 0 qos_preset = st.selectbox( "QoS", options=qos_options, index=default_qos_idx, + disabled=is_streaming, help="Fast: 2 retransmissions, lower latency. Robust: 4 retransmissions, better reliability." ) #st.info(f"Demo mode selected: {demo_selected} (Streams: {demo_stream_map[demo_selected]['streams']}, Rate: {demo_stream_map[demo_selected]['rate']} Hz)") - # Start/Stop buttons for demo mode - if 'demo_stream_started' not in st.session_state: - st.session_state['demo_stream_started'] = False - start_demo, stop_demo = render_stream_controls(is_streaming, "Start Demo", "Stop Demo", running_mode) - if start_demo: - # Always stop any running stream for clean state - try: - requests.post(f"{BACKEND_URL}/stop_audio").json() - except Exception: - pass - time.sleep(1) - demo_cfg = demo_stream_map[demo_selected] - # Octets per frame logic matches quality_map - q = QUALITY_MAP[demo_cfg['quality']] - - # Language configs and test files - lang_cfgs = [ - (auracast_config.AuracastBigConfigDeu, 'de'), - (auracast_config.AuracastBigConfigEng, 'en'), - (auracast_config.AuracastBigConfigFra, 'fr'), - (auracast_config.AuracastBigConfigSpa, 'es'), - (auracast_config.AuracastBigConfigIta, 'it'), - (auracast_config.AuracastBigConfigPol, 'pl'), - ] - bigs1 = [] - for i in range(demo_cfg['streams']): - cfg_cls, lang = lang_cfgs[i % len(lang_cfgs)] - bigs1.append(cfg_cls( - code=(stream_passwort.strip() or None), - audio_source=f'file:../testdata/wave_particle_5min_{lang}_{int(q["rate"]/1000)}kHz_mono.wav', - iso_que_len=32, - sampling_frequency=q['rate'], - octets_per_frame=q['octets'], - )) - - # Split bigs into two configs if needed - max_per_mc = {48000: 1, 24000: 2, 16000: 3} - max_streams = max_per_mc.get(q['rate'], 3) - bigs2 = [] - if len(bigs1) > max_streams: - bigs2 = bigs1[max_streams:] - bigs1 = bigs1[:max_streams] - config1 = auracast_config.AuracastConfigGroup( - auracast_sampling_rate_hz=q['rate'], - octets_per_frame=q['octets'], - transport='', # is set in baccol_qoskend - assisted_listening_stream=assisted_listening, - immediate_rendering=immediate_rendering, - presentation_delay_us=int(presentation_delay_ms * 1000), - qos_config=QOS_PRESET_MAP[qos_preset], - bigs=bigs1 - ) - config2 = None - if bigs2: - config2 = auracast_config.AuracastConfigGroup( - auracast_sampling_rate_hz=q['rate'], - octets_per_frame=q['octets'], - transport='', # is set in backend - assisted_listening_stream=assisted_listening, - immediate_rendering=immediate_rendering, - presentation_delay_us=int(presentation_delay_ms * 1000), - qos_config=QOS_PRESET_MAP[qos_preset], - bigs=bigs2 - ) - # Call /init and /init2 - is_started = False - try: - r1 = requests.post(f"{BACKEND_URL}/init", json=config1.model_dump()) - if r1.status_code == 200: - st.session_state['demo_stream_started'] = True - is_started = True - else: - st.session_state['demo_stream_started'] = False - st.error(f"Failed to initialize multicaster 1: {r1.text}") - if config2: - r2 = requests.post(f"{BACKEND_URL}/init2", json=config2.model_dump()) - if r2.status_code == 200: - is_started = True - else: - st.error(f"Failed to initialize multicaster 2: {r2.text}") - except Exception as e: - st.session_state['demo_stream_started'] = False - st.error(f"Error: {e}") - if is_started: - pass - elif stop_demo: - try: - r = requests.post(f"{BACKEND_URL}/stop_audio").json() - st.session_state['demo_stream_started'] = False - if r.get('was_running'): - is_stopped = True - except Exception as e: - st.error(f"Error: {e}") - quality = None # Not used in demo mode else: # --- Mode-specific configuration --- @@ -364,225 +303,306 @@ else: if audio_mode == "Analog": # --- Radio 1 controls --- - st.subheader("Radio 1") - - quality_options = list(QUALITY_MAP.keys()) - default_quality = "Medium (24kHz)" if "Medium (24kHz)" in quality_options else quality_options[0] - quality1 = st.selectbox( - "Stream Quality (Radio 1)", - quality_options, - index=quality_options.index(default_quality), - help="Select the audio sampling rate for Radio 1." - ) - - stream_passwort1 = st.text_input( - "Stream Passwort (Radio 1)", - value="", - type="password", - help="Optional: Set a broadcast code for Radio 1." - ) - - col_r1_flags1, col_r1_flags2, col_r1_pdelay, col_r1_qos = st.columns([1, 1, 0.7, 0.6], gap="small") - with col_r1_flags1: - assisted_listening1 = st.checkbox( - "Assistive listening (R1)", - value=bool(saved_settings.get('assisted_listening_stream', False)), - help="tells the receiver that this is an assistive listening stream" - ) - with col_r1_flags2: - immediate_rendering1 = st.checkbox( - "Immediate rendering (R1)", - value=bool(saved_settings.get('immediate_rendering', False)), - help="tells the receiver to ignore presentation delay and render immediately if possible." - ) - default_pdelay = int(saved_settings.get('presentation_delay_us', 40000) or 40000) - with col_r1_pdelay: - default_pdelay_ms = max(10, min(200, default_pdelay // 1000)) - presentation_delay_ms1 = st.number_input( - "Delay (ms, R1)", - min_value=10, max_value=200, step=5, value=default_pdelay_ms, - help="Delay between capture and presentation for Radio 1." - ) - with col_r1_qos: - qos_options = list(QOS_PRESET_MAP.keys()) - saved_qos = saved_settings.get('qos_preset', 'Fast') - default_qos_idx = qos_options.index(saved_qos) if saved_qos in qos_options else 0 - qos_preset1 = st.selectbox( - "QoS (R1)", options=qos_options, index=default_qos_idx, - help="Fast: 2 retransmissions, lower latency. Robust: 4 retransmissions, better reliability." - ) - - col_r1_name, col_r1_lang = st.columns([2, 1]) - with col_r1_name: - stream_name1 = st.text_input( - "Channel Name (Radio 1)", - value=default_name, - help="Name for the first analog radio (Radio 1)." - ) - with col_r1_lang: - language1 = st.text_input( - "Language (ISO 639-3) (Radio 1)", - value=default_lang, - help="Language code for Radio 1." - ) - program_info1 = st.text_input( - "Program Info (Radio 1)", - value=default_program_info, - help="Program information for Radio 1." - ) - - # Analog mode exposes only ALSA ch1/ch2 inputs. - if not is_streaming: - try: - resp = requests.get(f"{BACKEND_URL}/audio_inputs_pw_usb") - device_list = resp.json().get('inputs', []) - except Exception as e: - st.error(f"Failed to fetch devices: {e}") - device_list = [] - - analog_devices = [d for d in device_list if d.get('name') in ('ch1', 'ch2')] - - if not analog_devices: - st.warning("No Analog (ch1/ch2) ALSA inputs found. Check asound configuration.") - if st.button("Refresh", disabled=is_streaming): - try: - r = requests.post(f"{BACKEND_URL}/refresh_audio_devices", timeout=8) - if not r.ok: - st.error(f"Failed to refresh: {r.text}") - except Exception as e: - st.error(f"Failed to refresh devices: {e}") - st.rerun() - analog_names = [d['name'] for d in analog_devices] - else: - analog_devices = [] - analog_names = [] - - if not is_streaming: - if analog_names: - default_r1_idx = 0 - input_device1 = st.selectbox( - "Input Device (Radio 1)", - analog_names, - index=default_r1_idx, - ) - else: - input_device1 = None - else: - input_device1 = saved_settings.get('input_device') - st.selectbox( - "Input Device (Radio 1)", - [input_device1 or "No device selected"], - index=0, + with st.container(border=True): + st.subheader("Radio 1") + + # Always-enabled checkbox for Radio 1 + st.checkbox( + "Radio 1 always enabled", + value=True, disabled=True, - help="Stop the stream to change the input device." + help="Radio 1 is always enabled, Radio 2 can be turned on or off." ) - # --- Radio 2 controls --- - st.subheader("Radio 2") - # If the backend reports that the secondary radio is currently streaming, - # initialize the checkbox to checked so the UI reflects the active state - # when the frontend is loaded. - radio2_enabled_default = secondary_is_streaming - radio2_enabled = st.checkbox( - "Enable Radio 2", - value=radio2_enabled_default, - help="Activate a second analog radio with its own quality and timing settings." - ) + # Stereo mode toggle for analog + stereo_enabled = st.checkbox( + "๐ŸŽง Stereo Mode", + value=bool(saved_settings.get('analog_stereo_mode', False)), + help="Enable stereo streaming for analog inputs. When enabled, ch1 becomes left channel and ch2 becomes right channel in a single stereo stream. Radio 2 will be disabled in stereo mode.", + disabled=is_streaming + ) - if radio2_enabled: - quality2 = st.selectbox( - "Stream Quality (Radio 2)", + # Use analog-specific defaults (not from saved settings which may have Dante values) + default_name = "Analog_Radio_1" + default_program_info = "Analog Radio Broadcast" + default_lang = "deu" + + quality_options = list(QUALITY_MAP.keys()) + default_quality = "Medium (24kHz)" if "Medium (24kHz)" in quality_options else quality_options[0] + quality1 = st.selectbox( + "Stream Quality (Radio 1)", quality_options, index=quality_options.index(default_quality), - help="Select the audio sampling rate for Radio 2." + disabled=is_streaming, + help="Select the audio sampling rate for Radio 1." ) - stream_passwort2 = st.text_input( - "Stream Passwort (Radio 2)", + stream_passwort1 = st.text_input( + "Stream Passwort (Radio 1)", value="", type="password", - help="Optional: Set a broadcast code for Radio 2." + disabled=is_streaming, + help="Optional: Set a broadcast code for Radio 1." ) - col_r2_flags1, col_r2_flags2, col_r2_pdelay, col_r2_qos = st.columns([1, 1, 0.7, 0.6], gap="small") - with col_r2_flags1: - assisted_listening2 = st.checkbox( - "Assistive listening (R2)", + col_r1_flags1, col_r1_flags2, col_r1_pdelay, col_r1_qos = st.columns([1, 1, 0.7, 0.6], gap="small") + with col_r1_flags1: + assisted_listening1 = st.checkbox( + "Assistive listening (R1)", value=bool(saved_settings.get('assisted_listening_stream', False)), + disabled=is_streaming, help="tells the receiver that this is an assistive listening stream" ) - with col_r2_flags2: - immediate_rendering2 = st.checkbox( - "Immediate rendering (R2)", + with col_r1_flags2: + immediate_rendering1 = st.checkbox( + "Immediate rendering (R1)", value=bool(saved_settings.get('immediate_rendering', False)), + disabled=is_streaming, help="tells the receiver to ignore presentation delay and render immediately if possible." ) - with col_r2_pdelay: - presentation_delay_ms2 = st.number_input( - "Delay (ms, R2)", + default_pdelay = int(saved_settings.get('presentation_delay_us', 40000) or 40000) + with col_r1_pdelay: + default_pdelay_ms = max(10, min(200, default_pdelay // 1000)) + presentation_delay_ms1 = st.number_input( + "Delay (ms, R1)", min_value=10, max_value=200, step=5, value=default_pdelay_ms, - help="Delay between capture and presentation for Radio 2." + disabled=is_streaming, + help="Delay between capture and presentation for Radio 1." ) - with col_r2_qos: - saved_qos2 = saved_settings.get('secondary', {}).get('qos_preset', 'Fast') - default_qos_idx2 = qos_options.index(saved_qos2) if saved_qos2 in qos_options else 0 - qos_preset2 = st.selectbox( - "QoS (R2)", options=qos_options, index=default_qos_idx2, + with col_r1_qos: + qos_options = list(QOS_PRESET_MAP.keys()) + saved_qos = saved_settings.get('qos_preset', 'Fast') + default_qos_idx = qos_options.index(saved_qos) if saved_qos in qos_options else 0 + qos_preset1 = st.selectbox( + "QoS (R1)", options=qos_options, index=default_qos_idx, + disabled=is_streaming, help="Fast: 2 retransmissions, lower latency. Robust: 4 retransmissions, better reliability." ) - col_r2_name, col_r2_lang = st.columns([2, 1]) - with col_r2_name: - stream_name2 = st.text_input( - "Channel Name (Radio 2)", - value=f"{default_name}_2", - help="Name for the second analog radio (Radio 2)." + col_r1_name, col_r1_lang = st.columns([2, 1]) + with col_r1_name: + stream_name1 = st.text_input( + "Channel Name (Radio 1)", + value=default_name, + disabled=is_streaming, + help="Name for the first analog radio (Radio 1)." ) - with col_r2_lang: - language2 = st.text_input( - "Language (ISO 639-3) (Radio 2)", + with col_r1_lang: + language1 = st.text_input( + "Language (ISO 639-3) (Radio 1)", value=default_lang, - help="Language code for Radio 2." + disabled=is_streaming, + help="Language code for Radio 1." ) - program_info2 = st.text_input( - "Program Info (Radio 2)", + program_info1 = st.text_input( + "Program Info (Radio 1)", value=default_program_info, - help="Program information for Radio 2." + disabled=is_streaming, + help="Program information for Radio 1." ) + # Analog mode exposes only ALSA ch1/ch2 inputs. + if not is_streaming: + try: + resp = requests.get(f"{BACKEND_URL}/audio_inputs_pw_usb") + device_list = resp.json().get('inputs', []) + except Exception as e: + st.error(f"Failed to fetch devices: {e}") + device_list = [] + + analog_devices = [d for d in device_list if d.get('name') in ('ch1', 'ch2')] + + if not analog_devices: + st.warning("No Analog (ch1/ch2) ALSA inputs found. Check asound configuration.") + if st.button("Refresh", key="refresh_analog", disabled=is_streaming): + try: + r = requests.post(f"{BACKEND_URL}/refresh_audio_devices", timeout=8) + if not r.ok: + st.error(f"Failed to refresh: {r.text}") + except Exception as e: + st.error(f"Failed to refresh devices: {e}") + st.rerun() + analog_names = [d['name'] for d in analog_devices] + else: + analog_devices = [] + analog_names = [] + if not is_streaming: if analog_names: - default_r2_idx = 1 if len(analog_names) > 1 else 0 - input_device2 = st.selectbox( - "Input Device (Radio 2)", - analog_names, - index=default_r2_idx, + if stereo_enabled: + # In stereo mode, only show ch1 and automatically select it + if 'ch1' in analog_names: + input_device1 = 'ch1' + st.selectbox( + "Input Device (Radio 1) - Stereo", + ['ch1 + ch2 (Stereo: Left+Right channels)'], + index=0, + disabled=is_streaming, + help="Stereo mode: Captures both ch1 (left) and ch2 (right) as a single stereo stream" + ) + st.info("๐ŸŽง Stereo mode enabled - both ch1 and ch2 will be captured as left/right channels") + else: + st.error("ch1 not available for stereo mode") + input_device1 = None + else: + # Mono mode: show all available channels + default_r1_idx = 0 + input_device1 = st.selectbox( + "Input Device (Radio 1)", + analog_names, + index=default_r1_idx, + disabled=is_streaming, + ) + else: + input_device1 = None + else: + input_device1 = saved_settings.get('input_device') + if stereo_enabled: + st.selectbox( + "Input Device (Radio 1) - Stereo", + [f"{input_device1 or 'ch1'} (Left+Right channels)" if input_device1 else "No device selected"], + index=0, + disabled=True, + help="Stop the stream to change the input device." ) else: - input_device2 = None + st.selectbox( + "Input Device (Radio 1)", + [input_device1 or "No device selected"], + index=0, + disabled=True, + help="Stop the stream to change the input device." + ) + + # --- Radio 2 controls --- + with st.container(border=True): + st.subheader("Radio 2") + + # Disable Radio 2 in stereo mode + if stereo_enabled: + st.info("๐ŸŽง Radio 2 is automatically disabled in stereo mode") + radio2_enabled = False else: - input_device2 = saved_settings.get('input_device') - st.selectbox( - "Input Device (Radio 2)", - [input_device2 or "No device selected"], - index=0, - disabled=True, - help="Stop the stream to change the input device." + # If the backend reports that the secondary radio is currently streaming, + # initialize the checkbox to checked so the UI reflects the active state + # when the frontend is loaded. + radio2_enabled_default = secondary_is_streaming + radio2_enabled = st.checkbox( + "Enable Radio 2", + value=radio2_enabled_default, + disabled=is_streaming, + help="Activate a second analog radio with its own quality and timing settings." ) - radio2_cfg = { - 'id': 1002, - 'name': stream_name2, - 'program_info': program_info2, - 'language': language2, - 'input_device': input_device2, - 'quality': quality2, - 'stream_passwort': stream_passwort2, - 'assisted_listening': assisted_listening2, - 'immediate_rendering': immediate_rendering2, - 'presentation_delay_ms': presentation_delay_ms2, - 'qos_preset': qos_preset2, - } + if radio2_enabled and not stereo_enabled: + # Use analog-specific defaults for Radio 2 + default_name_r2 = "Analog_Radio_2" + default_program_info_r2 = "Analog Radio Broadcast" + default_lang_r2 = "deu" + + quality2 = st.selectbox( + "Stream Quality (Radio 2)", + quality_options, + index=quality_options.index(default_quality), + disabled=is_streaming, + help="Select the audio sampling rate for Radio 2." + ) + + stream_passwort2 = st.text_input( + "Stream Passwort (Radio 2)", + value="", + type="password", + disabled=is_streaming, + help="Optional: Set a broadcast code for Radio 2." + ) + + col_r2_flags1, col_r2_flags2, col_r2_pdelay, col_r2_qos = st.columns([1, 1, 0.7, 0.6], gap="small") + with col_r2_flags1: + assisted_listening2 = st.checkbox( + "Assistive listening (R2)", + value=bool(saved_settings.get('assisted_listening_stream', False)), + disabled=is_streaming, + help="tells the receiver that this is an assistive listening stream" + ) + with col_r2_flags2: + immediate_rendering2 = st.checkbox( + "Immediate rendering (R2)", + value=bool(saved_settings.get('immediate_rendering', False)), + disabled=is_streaming, + help="tells the receiver to ignore presentation delay and render immediately if possible." + ) + with col_r2_pdelay: + presentation_delay_ms2 = st.number_input( + "Delay (ms, R2)", + min_value=10, max_value=200, step=5, value=default_pdelay_ms, + disabled=is_streaming, + help="Delay between capture and presentation for Radio 2." + ) + with col_r2_qos: + saved_qos2 = saved_settings.get('secondary', {}).get('qos_preset', 'Fast') + default_qos_idx2 = qos_options.index(saved_qos2) if saved_qos2 in qos_options else 0 + qos_preset2 = st.selectbox( + "QoS (R2)", options=qos_options, index=default_qos_idx2, + disabled=is_streaming, + help="Fast: 2 retransmissions, lower latency. Robust: 4 retransmissions, better reliability." + ) + + col_r2_name, col_r2_lang = st.columns([2, 1]) + with col_r2_name: + stream_name2 = st.text_input( + "Channel Name (Radio 2)", + value=default_name_r2, + disabled=is_streaming, + help="Name for the second analog radio (Radio 2)." + ) + with col_r2_lang: + language2 = st.text_input( + "Language (ISO 639-3) (Radio 2)", + value=default_lang_r2, + disabled=is_streaming, + help="Language code for Radio 2." + ) + program_info2 = st.text_input( + "Program Info (Radio 2)", + value=default_program_info_r2, + disabled=is_streaming, + help="Program information for Radio 2." + ) + + if not is_streaming: + if analog_names: + default_r2_idx = 1 if len(analog_names) > 1 else 0 + input_device2 = st.selectbox( + "Input Device (Radio 2)", + analog_names, + index=default_r2_idx, + disabled=is_streaming, + ) + else: + input_device2 = None + else: + input_device2 = saved_settings.get('input_device') + st.selectbox( + "Input Device (Radio 2)", + [input_device2 or "No device selected"], + index=0, + disabled=True, + help="Stop the stream to change the input device." + ) + + radio2_cfg = { + 'id': 1002, + 'name': stream_name2, + 'program_info': program_info2, + 'language': language2, + 'input_device': input_device2, + 'quality': quality2, + 'stream_passwort': stream_passwort2, + 'assisted_listening': assisted_listening2, + 'immediate_rendering': immediate_rendering2, + 'presentation_delay_ms': presentation_delay_ms2, + 'qos_preset': qos_preset2, + } radio1_cfg = { 'id': 1001, @@ -596,9 +616,628 @@ else: 'immediate_rendering': immediate_rendering1, 'presentation_delay_ms': presentation_delay_ms1, 'qos_preset': qos_preset1, + 'stereo_mode': stereo_enabled, # Add stereo mode setting } - else: + if audio_mode == "Network - Dante": + # --- Network - Dante mode with Radio 1 and Radio 2 categories --- + + # Define stream configuration options + dante_stream_options = { + "1 ร— 48kHz": {"streams": 1, "quality": "High (48kHz)"}, + "2 ร— 24kHz": {"streams": 2, "quality": "Medium (24kHz)"}, + "3 ร— 16kHz": {"streams": 3, "quality": "Fair (16kHz)"} + } + + # Get available Dante devices + if not is_streaming: + try: + resp = requests.get(f"{BACKEND_URL}/audio_inputs_dante") + device_list = resp.json().get('inputs', []) + except Exception as e: + st.error(f"Failed to fetch Dante devices: {e}") + device_list = [] + + input_options = [f"{d['name']} [{d['id']}]" for d in device_list] + option_name_map = {f"{d['name']} [{d['id']}]": d['name'] for d in device_list} + device_names = [d['name'] for d in device_list] + else: + input_options = [] + option_name_map = {} + device_names = [] + + # --- Radio 1 Section --- + with st.container(border=True): + st.subheader("Radio 1") + + # Always-enabled checkbox for Radio 1 + st.checkbox( + "Radio 1 always enabled", + value=True, + disabled=True, + help="Radio 1 is always enabled, Radio 2 can be turned on or off." + ) + + # Dante stereo mode toggle + saved_r1_config = saved_settings.get('dante_radio1', {}) + dante_stereo_enabled = st.checkbox( + "๐ŸŽง Stereo Mode", + value=bool(saved_r1_config.get('dante_stereo_mode', False)), + help="Enable stereo streaming for Dante inputs. Select left and right channels from ASRC channels 1-6. Radio 2 and multi-stream configurations will be disabled in stereo mode.", + disabled=is_streaming + ) + + # Dante stereo channel selectors + dante_left_channel = None + dante_right_channel = None + if dante_stereo_enabled: + dante_channel_options = ["dante_asrc_ch1", "dante_asrc_ch2", "dante_asrc_ch3", + "dante_asrc_ch4", "dante_asrc_ch5", "dante_asrc_ch6"] + dante_channel_labels = ["CH1", "CH2", "CH3", "CH4", "CH5", "CH6"] + + col_left, col_right = st.columns(2) + with col_left: + saved_left = saved_r1_config.get('dante_stereo_left', 'dante_asrc_ch1') + left_idx = dante_channel_options.index(saved_left) if saved_left in dante_channel_options else 0 + dante_left_channel = st.selectbox( + "Left Channel", + dante_channel_options, + index=left_idx, + format_func=lambda x: f"ASRC {dante_channel_labels[dante_channel_options.index(x)]}", + disabled=is_streaming, + help="Select the Dante ASRC channel for the left stereo channel" + ) + with col_right: + saved_right = saved_r1_config.get('dante_stereo_right', 'dante_asrc_ch2') + right_idx = dante_channel_options.index(saved_right) if saved_right in dante_channel_options else 1 + dante_right_channel = st.selectbox( + "Right Channel", + dante_channel_options, + index=right_idx, + format_func=lambda x: f"ASRC {dante_channel_labels[dante_channel_options.index(x)]}", + disabled=is_streaming, + help="Select the Dante ASRC channel for the right stereo channel" + ) + + if dante_left_channel == dante_right_channel: + st.warning("โš ๏ธ Left and right channels are the same. Select different channels for true stereo.") + else: + st.info(f"๐ŸŽง Stereo mode: {dante_channel_labels[dante_channel_options.index(dante_left_channel)]} (Left) + {dante_channel_labels[dante_channel_options.index(dante_right_channel)]} (Right)") + + # Stream count dropdown for Radio 1 (disabled in stereo mode - forced to 1 stream at 48kHz) + r1_stream_options = list(dante_stream_options.keys()) + saved_r1_streams = saved_r1_config.get('stream_config', '1x48') + default_r1_idx = r1_stream_options.index(saved_r1_streams) if saved_r1_streams in r1_stream_options else 0 + + if dante_stereo_enabled: + # Stereo mode: force 1 stream at 48kHz + r1_stream_config = "1 ร— 48kHz" + st.selectbox( + "Stream Configuration (Radio 1)", + ["1 ร— 48kHz (Stereo)"], + index=0, + disabled=True, + help="In stereo mode, only 1 stream at 48kHz is supported" + ) + else: + r1_stream_config = st.selectbox( + "Stream Configuration (Radio 1)", + r1_stream_options, + index=default_r1_idx, + disabled=is_streaming, + help="Select the number and quality of streams for Radio 1" + ) + + r1_num_streams = dante_stream_options[r1_stream_config]["streams"] + r1_quality = dante_stream_options[r1_stream_config]["quality"] + + # Stream quality (moved directly under stream configuration) + r1_max_quality = r1_quality + r1_available_qualities = [] + for quality in ["High (48kHz)", "Good (32kHz)", "Medium (24kHz)", "Fair (16kHz)"]: + # Check if this quality is equal to or lower than the max + if (r1_max_quality == "High (48kHz)" or + (r1_max_quality == "Medium (24kHz)" and quality in ["Medium (24kHz)", "Fair (16kHz)"]) or + (r1_max_quality == "Fair (16kHz)" and quality == "Fair (16kHz)")): + r1_available_qualities.append(quality) + + saved_r1_quality = saved_r1_config.get('radio_quality', r1_max_quality) + if saved_r1_quality not in r1_available_qualities: + saved_r1_quality = r1_max_quality + + r1_radio_quality = st.selectbox( + "Stream Quality (Radio 1)", + r1_available_qualities, + index=r1_available_qualities.index(saved_r1_quality), + disabled=is_streaming, + help=f"Select stream quality for Radio 1. Maximum quality based on configuration: {r1_max_quality}" + ) + + # Radio-level settings for Radio 1 + # First row: Assistive listening, immediate rendering, presentation delay, QoS + col_r1_flags1, col_r1_flags2, col_r1_pdelay, col_r1_qos = st.columns([1, 1, 0.7, 0.6], gap="small") + + with col_r1_flags1: + r1_assisted_listening = st.checkbox( + "Assistive (R1)", + value=bool(saved_r1_config.get('assisted_listening', False)), + disabled=is_streaming, + help="Assistive listening stream" + ) + + with col_r1_flags2: + r1_immediate_rendering = st.checkbox( + "Immediate (R1)", + value=bool(saved_r1_config.get('immediate_rendering', False)), + disabled=is_streaming, + help="Ignore presentation delay" + ) + + with col_r1_pdelay: + default_pdelay = int(saved_r1_config.get('presentation_delay_us', 40000) or 40000) + default_pdelay_ms = max(10, min(200, default_pdelay // 1000)) + r1_presentation_delay_ms = st.number_input( + "Delay (ms, R1)", + min_value=10, max_value=200, step=5, value=default_pdelay_ms, + disabled=is_streaming, + help="Presentation delay for Radio 1" + ) + + with col_r1_qos: + qos_options = list(QOS_PRESET_MAP.keys()) + saved_qos = saved_r1_config.get('qos_preset', 'Fast') + default_qos_idx = qos_options.index(saved_qos) if saved_qos in qos_options else 0 + r1_qos_preset = st.selectbox( + "QoS (R1)", options=qos_options, index=default_qos_idx, + disabled=is_streaming, + help="Quality of Service preset for Radio 1" + ) + + # Per-stream configuration for Radio 1 + if dante_stereo_enabled: + st.write("**Stereo Stream Configuration (Radio 1)**") + else: + st.write("**Stream Configuration (Radio 1)**") + r1_streams = [] + + if dante_stereo_enabled: + # Stereo mode: single stream with combined L+R channels + with st.expander("Stereo Stream - Radio 1", expanded=True): + saved_streams = saved_r1_config.get('streams', []) + saved_stream = saved_streams[0] if saved_streams else {} + + # First row: Channel name and password + col_name, col_pwd = st.columns([2, 1]) + + with col_name: + stream_name = st.text_input( + "Channel Name", + value=saved_stream.get('name', 'Dante_Stereo'), + disabled=is_streaming, + key="r1_stereo_name" + ) + + with col_pwd: + stream_password = st.text_input( + "Stream Password", + value=saved_stream.get('stream_password', ''), + type="password", + disabled=is_streaming, + key="r1_stereo_password", + help="Optional: Set a broadcast code for this stream" + ) + + # Second row: Program info and language + col_prog, col_lang_code = st.columns([2, 1]) + + with col_prog: + program_info = st.text_input( + "Program Info", + value=saved_stream.get('program_info', 'Dante Stereo Broadcast'), + disabled=is_streaming, + key="r1_stereo_program" + ) + + with col_lang_code: + language = st.text_input( + "Language", + value=saved_stream.get('language', 'eng'), + disabled=is_streaming, + key="r1_stereo_lang", + help="ISO 639-3 language code" + ) + + # Build stereo device name from selected channels + # Extract channel numbers from dante_asrc_chX + left_ch_num = dante_left_channel.replace('dante_asrc_ch', '') if dante_left_channel else '1' + right_ch_num = dante_right_channel.replace('dante_asrc_ch', '') if dante_right_channel else '2' + # Use the device name that matches the user's L/R selection + stereo_device_name = f"dante_stereo_{left_ch_num}_{right_ch_num}" + + # Show stereo device info + st.selectbox( + "Input Device (Stereo)", + [f"Stereo: CH{left_ch_num} (L) + CH{right_ch_num} (R)"], + index=0, + disabled=True, + help="Stereo input from the selected Dante ASRC channels" + ) + + r1_streams.append({ + 'name': stream_name, + 'program_info': program_info, + 'language': language, + 'input_device': stereo_device_name, + 'stream_password': stream_password, + 'dante_stereo_mode': True, + 'dante_stereo_left': dante_left_channel, + 'dante_stereo_right': dante_right_channel, + }) + else: + # Normal mono mode: multiple streams with individual channels + for i in range(r1_num_streams): + with st.expander(f"Stream {i+1} - Radio 1", expanded=True): + saved_streams = saved_r1_config.get('streams', []) + saved_stream = saved_streams[i] if i < len(saved_streams) else {} + + # First row: Channel name and language + col_name, col_lang = st.columns([2, 1]) + + with col_name: + stream_name = st.text_input( + f"Channel Name", + value=saved_stream.get('name', f'Dante_R1_S{i+1}'), + disabled=is_streaming, + key=f"r1_stream_{i}_name" + ) + + with col_lang: + stream_password = st.text_input( + f"Stream Password", + value=saved_stream.get('stream_password', ''), + type="password", + disabled=is_streaming, + key=f"r1_stream_{i}_password", + help="Optional: Set a broadcast code for this stream" + ) + + # Second row: Program info and language + col_prog, col_lang_code = st.columns([2, 1]) + + with col_prog: + program_info = st.text_input( + f"Program Info", + value=saved_stream.get('program_info', f'Dante Radio 1 Stream {i+1}'), + disabled=is_streaming, + key=f"r1_stream_{i}_program" + ) + + with col_lang_code: + language = st.text_input( + f"Language", + value=saved_stream.get('language', 'eng'), + disabled=is_streaming, + key=f"r1_stream_{i}_lang", + help="ISO 639-3 language code" + ) + + # Third row: Input device + col_device = st.columns([1])[0] + + with col_device: + # Session state key for persisting the selection + device_session_key = f"r1_stream_{i}_device_saved" + + if not is_streaming and input_options: + # Get default from session state first, then from saved settings + default_input_name = st.session_state.get(device_session_key, saved_stream.get('input_device')) + default_input_label = None + for label, name in option_name_map.items(): + if name == default_input_name: + default_input_label = label + break + if default_input_label not in input_options and input_options: + default_input_label = input_options[0] + + selected_option = st.selectbox( + f"Input Device", + input_options, + index=input_options.index(default_input_label) if default_input_label in input_options else 0, + disabled=is_streaming, + key=f"r1_stream_{i}_device" + ) + input_device = option_name_map.get(selected_option) + # Save to session state for persistence + st.session_state[device_session_key] = input_device + else: + # When streaming, get the device from session state + current_device = st.session_state.get(device_session_key, saved_stream.get('input_device', 'No device')) + + # Convert internal name to display label + display_label = current_device + for label, name in option_name_map.items(): + if name == current_device: + display_label = label + break + + st.selectbox( + f"Input Device", + [display_label if display_label else 'No device'], + index=0, + disabled=True, + key=f"r1_stream_{i}_device_disabled" + ) + input_device = current_device + + r1_streams.append({ + 'name': stream_name, + 'program_info': program_info, + 'language': language, + 'input_device': input_device, + 'stream_password': stream_password + }) + + # --- Radio 2 Section --- + with st.container(border=True): + st.subheader("Radio 2") + + # Disable Radio 2 in stereo mode + saved_r2_config = saved_settings.get('dante_radio2', {}) + if dante_stereo_enabled: + st.info("๐ŸŽง Radio 2 is automatically disabled in stereo mode") + radio2_enabled = False + else: + # Enable/disable checkbox for Radio 2 + radio2_enabled_default = secondary_is_streaming + radio2_enabled = st.checkbox( + "Enable Radio 2", + value=radio2_enabled_default, + disabled=is_streaming, + help="Activate a second Dante radio with its own quality and timing settings." + ) + + if radio2_enabled: + # Stream count dropdown for Radio 2 + r2_stream_options = r1_stream_options + saved_r2_streams = saved_r2_config.get('stream_config', '1x48') + default_r2_idx = r2_stream_options.index(saved_r2_streams) if saved_r2_streams in r2_stream_options else 0 + + r2_stream_config = st.selectbox( + "Stream Configuration (Radio 2)", + r2_stream_options, + index=default_r2_idx, + disabled=is_streaming, + help="Select the number and quality of streams for Radio 2" + ) + r2_num_streams = dante_stream_options[r2_stream_config]["streams"] + r2_quality = dante_stream_options[r2_stream_config]["quality"] + + # Stream quality (moved directly under stream configuration) + r2_max_quality = r2_quality + r2_available_qualities = [] + for quality in ["High (48kHz)", "Good (32kHz)", "Medium (24kHz)", "Fair (16kHz)"]: + # Check if this quality is equal to or lower than the max + if (r2_max_quality == "High (48kHz)" or + (r2_max_quality == "Medium (24kHz)" and quality in ["Medium (24kHz)", "Fair (16kHz)"]) or + (r2_max_quality == "Fair (16kHz)" and quality == "Fair (16kHz)")): + r2_available_qualities.append(quality) + + saved_r2_quality = saved_r2_config.get('radio_quality', r2_max_quality) + if saved_r2_quality not in r2_available_qualities: + saved_r2_quality = r2_max_quality + + r2_radio_quality = st.selectbox( + "Stream Quality (Radio 2)", + r2_available_qualities, + index=r2_available_qualities.index(saved_r2_quality), + disabled=is_streaming, + help=f"Select stream quality for Radio 2. Maximum quality based on configuration: {r2_max_quality}" + ) + + # Radio-level settings for Radio 2 + # First row: Assistive listening, immediate rendering, presentation delay, QoS + col_r2_flags1, col_r2_flags2, col_r2_pdelay, col_r2_qos = st.columns([1, 1, 0.7, 0.6], gap="small") + + with col_r2_flags1: + r2_assisted_listening = st.checkbox( + "Assistive (R2)", + value=bool(saved_r2_config.get('assisted_listening', False)), + disabled=is_streaming, + help="Assistive listening stream" + ) + + with col_r2_flags2: + r2_immediate_rendering = st.checkbox( + "Immediate (R2)", + value=bool(saved_r2_config.get('immediate_rendering', False)), + disabled=is_streaming, + help="Ignore presentation delay" + ) + + with col_r2_pdelay: + default_pdelay = int(saved_r2_config.get('presentation_delay_us', 40000) or 40000) + default_pdelay_ms = max(10, min(200, default_pdelay // 1000)) + r2_presentation_delay_ms = st.number_input( + "Delay (ms, R2)", + min_value=10, max_value=200, step=5, value=default_pdelay_ms, + disabled=is_streaming, + help="Presentation delay for Radio 2" + ) + + with col_r2_qos: + qos_options = list(QOS_PRESET_MAP.keys()) + saved_qos = saved_r2_config.get('qos_preset', 'Fast') + default_qos_idx = qos_options.index(saved_qos) if saved_qos in qos_options else 0 + r2_qos_preset = st.selectbox( + "QoS (R2)", options=qos_options, index=default_qos_idx, + disabled=is_streaming, + help="Quality of Service preset for Radio 2" + ) + + # Per-stream configuration for Radio 2 + st.write("**Stream Configuration (Radio 2)**") + r2_streams = [] + + for i in range(r2_num_streams): + with st.expander(f"Stream {i+1} - Radio 2", expanded=True): + saved_streams = saved_r2_config.get('streams', []) + saved_stream = saved_streams[i] if i < len(saved_streams) else {} + + # First row: Channel name and password + col_name, col_pwd = st.columns([2, 1]) + + with col_name: + stream_name = st.text_input( + f"Channel Name", + value=saved_stream.get('name', f'Dante_R2_S{i+1}'), + disabled=is_streaming, + key=f"r2_stream_{i}_name" + ) + + with col_pwd: + stream_password = st.text_input( + f"Stream Password", + value=saved_stream.get('stream_password', ''), + type="password", + disabled=is_streaming, + key=f"r2_stream_{i}_password", + help="Optional: Set a broadcast code for this stream" + ) + + # Second row: Program info and language + col_prog, col_lang = st.columns([2, 1]) + + with col_prog: + program_info = st.text_input( + f"Program Info", + value=saved_stream.get('program_info', f'Dante Radio 2 Stream {i+1}'), + disabled=is_streaming, + key=f"r2_stream_{i}_program" + ) + + with col_lang: + language = st.text_input( + f"Language", + value=saved_stream.get('language', 'eng'), + disabled=is_streaming, + key=f"r2_stream_{i}_lang", + help="ISO 639-3 language code" + ) + + # Third row: Input device + col_device = st.columns([1])[0] + + with col_device: + # Session state key for persisting the selection + device_session_key = f"r2_stream_{i}_device_saved" + + if not is_streaming and input_options: + # Get default from session state first, then from saved settings + default_input_name = st.session_state.get(device_session_key, saved_stream.get('input_device')) + default_input_label = None + for label, name in option_name_map.items(): + if name == default_input_name: + default_input_label = label + break + if default_input_label not in input_options and input_options: + default_input_label = input_options[0] + + selected_option = st.selectbox( + f"Input Device", + input_options, + index=input_options.index(default_input_label) if default_input_label in input_options else 0, + disabled=is_streaming, + key=f"r2_stream_{i}_device" + ) + input_device = option_name_map.get(selected_option) + # Save to session state for persistence + st.session_state[device_session_key] = input_device + else: + # When streaming, get the device from session state + current_device = st.session_state.get(device_session_key, saved_stream.get('input_device', 'No device')) + + # Convert internal name to display label + display_label = current_device + for label, name in option_name_map.items(): + if name == current_device: + display_label = label + break + + st.selectbox( + f"Input Device", + [display_label if display_label else 'No device'], + index=0, + disabled=True, + key=f"r2_stream_{i}_device_disabled" + ) + input_device = current_device + + r2_streams.append({ + 'name': stream_name, + 'program_info': program_info, + 'language': language, + 'input_device': input_device, + 'stream_password': stream_password + }) + else: + r2_streams = [] + # Set default values for r2_* variables when radio2 is disabled + r2_stream_config = '1 ร— 48kHz' + r2_quality = 'High (48kHz)' + r2_radio_quality = 'High (48kHz)' + r2_assisted_listening = False + r2_immediate_rendering = False + r2_presentation_delay_ms = 40 + r2_qos_preset = 'Fast' + + # Validate unique input devices for Network - Dante mode + if audio_mode == "Network - Dante": + is_valid, error_msg = validate_unique_input_devices(r1_streams, r2_streams) + if not is_valid: + # Display error in the placeholder at the top + validation_error_placeholder.error(error_msg) + + # Show device refresh button if no devices found + if not input_options and not is_streaming: + st.warning("No Dante inputs found. Check asound.conf configuration.") + if st.button("Refresh", key="refresh_dante_devices", disabled=is_streaming): + try: + r = requests.post(f"{BACKEND_URL}/refresh_audio_devices", timeout=8) + if not r.ok: + st.error(f"Failed to refresh: {r.text}") + except Exception as e: + st.error(f"Failed to refresh devices: {e}") + st.rerun() + + # Store radio configurations for backend + radio1_cfg = { + 'stream_config': r1_stream_config, + 'max_quality': r1_quality, # Original max quality from stream config + 'radio_quality': r1_radio_quality, # User-selected quality (may be lower) + 'quality': r1_radio_quality, # Use selected quality for backend + 'streams': r1_streams, + 'assisted_listening': r1_assisted_listening, + 'immediate_rendering': r1_immediate_rendering, + 'presentation_delay_ms': r1_presentation_delay_ms, + 'qos_preset': r1_qos_preset, + 'dante_stereo_mode': dante_stereo_enabled, + 'dante_stereo_left': dante_left_channel, + 'dante_stereo_right': dante_right_channel, + } + + radio2_cfg = { + 'stream_config': r2_stream_config, + 'max_quality': r2_quality if radio2_enabled else None, # Original max quality from stream config + 'radio_quality': r2_radio_quality if radio2_enabled else None, # User-selected quality + 'quality': r2_radio_quality if radio2_enabled else None, # Use selected quality for backend + 'streams': r2_streams, + 'assisted_listening': r2_assisted_listening if radio2_enabled else False, + 'immediate_rendering': r2_immediate_rendering if radio2_enabled else False, + 'presentation_delay_ms': r2_presentation_delay_ms if radio2_enabled else 40000, + 'qos_preset': r2_qos_preset if radio2_enabled else 'Fast', + } if radio2_enabled else None + + if audio_mode in ("USB", "Network"): # USB/Network: single set of controls shared with the single channel quality_options = list(QUALITY_MAP.keys()) default_quality = "Medium (24kHz)" if "Medium (24kHz)" in quality_options else quality_options[0] @@ -606,6 +1245,7 @@ else: "Stream Quality (Sampling Rate)", quality_options, index=quality_options.index(default_quality), + disabled=is_streaming, help="Select the audio sampling rate for the stream. Lower rates may improve compatibility." ) @@ -613,6 +1253,7 @@ else: "Stream Passwort", value="", type="password", + disabled=is_streaming, help="Optional: Set a broadcast code to protect your stream. Leave empty for an open (uncoded) broadcast." ) @@ -621,12 +1262,14 @@ else: assisted_listening = st.checkbox( "Assistive listening", value=bool(saved_settings.get('assisted_listening_stream', False)), + disabled=is_streaming, help="tells the receiver that this is an assistive listening stream" ) with col_flags2: immediate_rendering = st.checkbox( "Immediate rendering", value=bool(saved_settings.get('immediate_rendering', False)), + disabled=is_streaming, help="tells the receiver to ignore presentation delay and render immediately if possible." ) default_pdelay = int(saved_settings.get('presentation_delay_us', 40000) or 40000) @@ -635,6 +1278,7 @@ else: presentation_delay_ms = st.number_input( "Delay (ms)", min_value=10, max_value=200, step=5, value=default_pdelay_ms, + disabled=is_streaming, help="Delay between capture and presentation for receivers." ) with col_qos: @@ -643,29 +1287,37 @@ else: default_qos_idx = qos_options.index(saved_qos) if saved_qos in qos_options else 0 qos_preset = st.selectbox( "QoS", options=qos_options, index=default_qos_idx, + disabled=is_streaming, help="Fast: 2 retransmissions, lower latency. Robust: 4 retransmissions, better reliability." ) stream_name = st.text_input( "Channel Name", value=default_name, + disabled=is_streaming, help="The primary name for your broadcast. Like the SSID of a WLAN, it identifies your stream for receivers." ) program_info = st.text_input( "Program Info", value=default_program_info, + disabled=is_streaming, help="Additional details about the broadcast program, such as its content or purpose. Shown to receivers for more context." ) language = st.text_input( "Language (ISO 639-3)", value=default_lang, + disabled=is_streaming, help="Three-letter language code (e.g., 'eng' for English, 'deu' for German). Used by receivers to display the language of the stream. See: https://en.wikipedia.org/wiki/List_of_ISO_639-3_codes" ) if audio_mode in ("USB", "Network"): if not is_streaming: try: - endpoint = "/audio_inputs_pw_usb" if audio_mode == "USB" else "/audio_inputs_pw_network" + if audio_mode == "USB": + endpoint = "/audio_inputs_pw_usb" + elif audio_mode == "Network": + endpoint = "/audio_inputs_pw_network" + resp = requests.get(f"{BACKEND_URL}{endpoint}") device_list = resp.json().get('inputs', []) except Exception as e: @@ -680,6 +1332,7 @@ else: device_names = [d['name'] for d in device_list] default_input_name = saved_settings.get('input_device') + # If saved device isn't in current mode's device list, use first available if default_input_name not in device_names and device_names: default_input_name = device_names[0] default_input_label = None @@ -694,7 +1347,8 @@ else: "No AES67/Network inputs found." ) st.warning(warn_text) - if st.button("Refresh", disabled=is_streaming): + refresh_key = "refresh_usb" if audio_mode == "USB" else "refresh_aes67" + if st.button("Refresh", key=refresh_key, disabled=is_streaming): try: r = requests.post(f"{BACKEND_URL}/refresh_audio_devices", timeout=8) if not r.ok: @@ -712,7 +1366,8 @@ else: index=input_options.index(default_input_label) if default_input_label in input_options else 0 ) with col2: - if st.button("Refresh", disabled=is_streaming): + refresh_key = "refresh_inputs" if audio_mode == "USB" else "refresh_inputs_net" + if st.button("Refresh", key=refresh_key, disabled=is_streaming): try: r = requests.post(f"{BACKEND_URL}/refresh_audio_devices", timeout=8) if not r.ok: @@ -734,111 +1389,263 @@ else: else: input_device = None - start_stream, stop_stream = render_stream_controls(is_streaming, "Start Auracast", "Stop Auracast", running_mode) - - if stop_stream: - st.session_state['stream_started'] = False - try: - r = requests.post(f"{BACKEND_URL}/stop_audio").json() - if r['was_running']: - is_stopped = True - except Exception as e: - st.error(f"Error: {e}") +if stop_stream: + st.session_state['stream_started'] = False + try: + r = requests.post(f"{BACKEND_URL}/stop_audio").json() + if audio_mode == "Demo": + st.session_state['demo_stream_started'] = False + if r['was_running']: + is_stopped = True + except Exception as e: + st.error(f"Error: {e}") - if start_stream: - # Always send stop to ensure backend is in a clean state, regardless of current status - r = requests.post(f"{BACKEND_URL}/stop_audio").json() - # Small pause lets backend fully release audio devices before re-init - time.sleep(1) +if start_stream: + # Always send stop to ensure backend is in a clean state, regardless of current status + r = requests.post(f"{BACKEND_URL}/stop_audio").json() + # Small pause lets backend fully release audio devices before re-init + time.sleep(1) - if audio_mode == "Analog": - # Build separate configs per radio, each with its own quality and QoS parameters. - is_started = False + if audio_mode == "Demo": + demo_cfg = demo_stream_map[demo_selected] + q = QUALITY_MAP[demo_cfg['quality']] - def _build_group_from_radio(cfg: dict) -> auracast_config.AuracastConfigGroup | None: - if not cfg or not cfg.get('input_device'): - return None - q = QUALITY_MAP[cfg['quality']] - return auracast_config.AuracastConfigGroup( - auracast_sampling_rate_hz=q['rate'], - octets_per_frame=q['octets'], - transport='', # is set in backend - assisted_listening_stream=bool(cfg['assisted_listening']), - immediate_rendering=bool(cfg['immediate_rendering']), - presentation_delay_us=int(cfg['presentation_delay_ms'] * 1000), - qos_config=QOS_PRESET_MAP[cfg['qos_preset']], - bigs=[ - auracast_config.AuracastBigConfig( - id=cfg.get('id', 123456), - code=(cfg['stream_passwort'].strip() or None), - name=cfg['name'], - program_info=cfg['program_info'], - language=cfg['language'], - audio_source=f"device:{cfg['input_device']}", - input_format=f"int16le,{q['rate']},1", - iso_que_len=1, - sampling_frequency=q['rate'], - octets_per_frame=q['octets'], - ) - ], - ) + lang_cfgs = [ + (auracast_config.AuracastBigConfigDeu, 'de'), + (auracast_config.AuracastBigConfigEng, 'en'), + (auracast_config.AuracastBigConfigFra, 'fr'), + (auracast_config.AuracastBigConfigSpa, 'es'), + (auracast_config.AuracastBigConfigIta, 'it'), + (auracast_config.AuracastBigConfigPol, 'pl'), + ] + bigs1 = [] + for i in range(demo_cfg['streams']): + cfg_cls, lang = lang_cfgs[i % len(lang_cfgs)] + bigs1.append(cfg_cls( + code=(stream_passwort.strip() or None), + audio_source=f'file:../testdata/wave_particle_5min_{lang}_{int(q["rate"]/1000)}kHz_mono.wav', + iso_que_len=32, + sampling_frequency=q['rate'], + octets_per_frame=q['octets'], + )) - # Radio 1 (always active if a device is selected) - config1 = _build_group_from_radio(radio1_cfg) - # Radio 2 (optional) - config2 = _build_group_from_radio(radio2_cfg) if radio2_enabled else None - - try: - if config1 is not None: - r1 = requests.post(f"{BACKEND_URL}/init", json=config1.model_dump()) - if r1.status_code == 200: - is_started = True - else: - st.error(f"Failed to initialize Radio 1: {r1.text}") - else: - st.error("Radio 1 has no valid input device configured.") - - if config2 is not None: - r2 = requests.post(f"{BACKEND_URL}/init2", json=config2.model_dump()) - if r2.status_code != 200: - st.error(f"Failed to initialize Radio 2: {r2.text}") - except Exception as e: - st.error(f"Error while starting Analog radios: {e}") - else: - # USB/Network: single config as before, using shared controls - q = QUALITY_MAP[quality] - config = auracast_config.AuracastConfigGroup( + max_per_mc = {48000: 1, 24000: 2, 16000: 3} + max_streams = max_per_mc.get(q['rate'], 3) + bigs2 = [] + if len(bigs1) > max_streams: + bigs2 = bigs1[max_streams:] + bigs1 = bigs1[:max_streams] + config1 = auracast_config.AuracastConfigGroup( + auracast_sampling_rate_hz=q['rate'], + octets_per_frame=q['octets'], + transport='', + assisted_listening_stream=assisted_listening, + immediate_rendering=immediate_rendering, + presentation_delay_us=int(presentation_delay_ms * 1000), + qos_config=QOS_PRESET_MAP[qos_preset], + bigs=bigs1 + ) + config2 = None + if bigs2: + config2 = auracast_config.AuracastConfigGroup( auracast_sampling_rate_hz=q['rate'], octets_per_frame=q['octets'], - transport='', # is set in backend + transport='', assisted_listening_stream=assisted_listening, immediate_rendering=immediate_rendering, presentation_delay_us=int(presentation_delay_ms * 1000), qos_config=QOS_PRESET_MAP[qos_preset], + bigs=bigs2 + ) + + is_started = False + try: + r1 = requests.post(f"{BACKEND_URL}/init", json=config1.model_dump()) + if r1.status_code == 200: + st.session_state['demo_stream_started'] = True + is_started = True + else: + st.session_state['demo_stream_started'] = False + st.error(f"Failed to initialize multicaster 1: {r1.text}") + if config2: + r2 = requests.post(f"{BACKEND_URL}/init2", json=config2.model_dump()) + if r2.status_code == 200: + is_started = True + else: + st.error(f"Failed to initialize multicaster 2: {r2.text}") + except Exception as e: + st.session_state['demo_stream_started'] = False + st.error(f"Error: {e}") + + if audio_mode == "Analog": + # Build separate configs per radio, each with its own quality and QoS parameters. + is_started = False + + def _build_group_from_radio(cfg: dict) -> auracast_config.AuracastConfigGroup | None: + if not cfg or not cfg.get('input_device'): + return None + q = QUALITY_MAP[cfg['quality']] + + # Determine if this is stereo mode (only applicable for analog) + stereo_mode = cfg.get('stereo_mode', False) + channels = 2 if stereo_mode else 1 + + return auracast_config.AuracastConfigGroup( + auracast_sampling_rate_hz=q['rate'], + octets_per_frame=q['octets'], + transport='', # is set in backend + assisted_listening_stream=bool(cfg['assisted_listening']), + immediate_rendering=bool(cfg['immediate_rendering']), + presentation_delay_us=int(cfg['presentation_delay_ms'] * 1000), + qos_config=QOS_PRESET_MAP[cfg['qos_preset']], bigs=[ auracast_config.AuracastBigConfig( - code=(stream_passwort.strip() or None), - name=stream_name, - program_info=program_info, - language=language, - audio_source=(f"device:{input_device}"), - input_format=(f"int16le,{q['rate']},1"), + id=cfg.get('id', 123456), + code=(cfg['stream_passwort'].strip() or None), + name=cfg['name'], + program_info=cfg['program_info'], + language=cfg['language'], + audio_source=f"device:{cfg['input_device']}", + input_format=f"int16le,{q['rate']},{channels}", iso_que_len=1, sampling_frequency=q['rate'], octets_per_frame=q['octets'], - ), + num_bis=channels, # 1=mono, 2=stereo - this determines the behavior + ) ], ) - try: - r = requests.post(f"{BACKEND_URL}/init", json=config.model_dump()) - if r.status_code == 200: + # Radio 1 (always active if a device is selected) + config1 = _build_group_from_radio(radio1_cfg) + # Radio 2 (optional) + config2 = _build_group_from_radio(radio2_cfg) if radio2_enabled else None + + try: + if config1 is not None: + r1 = requests.post(f"{BACKEND_URL}/init", json=config1.model_dump()) + if r1.status_code == 200: is_started = True else: - st.error(f"Failed to initialize: {r.text}") - except Exception as e: - st.error(f"Error: {e}") + st.error(f"Failed to initialize Radio 1: {r1.text}") + else: + st.error("Radio 1 has no valid input device configured.") + + if config2 is not None: + r2 = requests.post(f"{BACKEND_URL}/init2", json=config2.model_dump()) + if r2.status_code != 200: + st.error(f"Failed to initialize Radio 2: {r2.text}") + except Exception as e: + st.error(f"Error while starting Analog radios: {e}") + elif audio_mode == "Network - Dante": + # Build multi-stream configs for Dante radios + is_started = False + + def _build_dante_radio_config(radio_cfg: dict, radio_id: int) -> auracast_config.AuracastConfigGroup | None: + if not radio_cfg or not radio_cfg.get('streams'): + return None + + q = QUALITY_MAP[radio_cfg['quality']] + bigs = [] + + # Check if stereo mode is enabled for this radio + is_stereo_mode = bool(radio_cfg.get('dante_stereo_mode', False)) + + for i, stream in enumerate(radio_cfg['streams']): + if not stream.get('input_device'): + continue + + stream_id = radio_id * 1000 + i + 1 # Unique ID per stream + + # Check if this specific stream uses stereo (dante_stereo_X_Y device) + input_device = stream['input_device'] + stream_is_stereo = is_stereo_mode or input_device.startswith('dante_stereo_') + num_bis = 2 if stream_is_stereo else 1 + num_channels = 2 if stream_is_stereo else 1 + + bigs.append(auracast_config.AuracastBigConfig( + id=stream_id, + code=(stream.get('stream_password', '').strip() or None), + name=stream['name'], + program_info=stream['program_info'], + language=stream['language'], + audio_source=f"device:{input_device}", + input_format=f"int16le,{q['rate']},{num_channels}", + iso_que_len=1, + sampling_frequency=q['rate'], + octets_per_frame=q['octets'], + num_bis=num_bis, + )) + + if not bigs: + return None + + return auracast_config.AuracastConfigGroup( + auracast_sampling_rate_hz=q['rate'], + octets_per_frame=q['octets'], + transport='', # is set in backend + assisted_listening_stream=bool(radio_cfg['assisted_listening']), + immediate_rendering=bool(radio_cfg['immediate_rendering']), + presentation_delay_us=int(radio_cfg['presentation_delay_ms'] * 1000), + qos_config=QOS_PRESET_MAP[radio_cfg['qos_preset']], + bigs=bigs + ) + + # Radio 1 config + config1 = _build_dante_radio_config(radio1_cfg, 1) + # Radio 2 config (optional) + config2 = _build_dante_radio_config(radio2_cfg, 2) if radio2_cfg else None + + try: + if config1 is not None: + r1 = requests.post(f"{BACKEND_URL}/init", json=config1.model_dump()) + if r1.status_code == 200: + is_started = True + else: + st.error(f"Failed to initialize Dante Radio 1: {r1.text}") + else: + st.error("Dante Radio 1 has no valid input devices configured.") + + if config2 is not None: + r2 = requests.post(f"{BACKEND_URL}/init2", json=config2.model_dump()) + if r2.status_code != 200: + st.error(f"Failed to initialize Dante Radio 2: {r2.text}") + except Exception as e: + st.error(f"Error while starting Dante radios: {e}") + if audio_mode not in ("Demo", "Analog", "Network - Dante"): + # USB/Network: single config as before, using shared controls + q = QUALITY_MAP[quality] + config = auracast_config.AuracastConfigGroup( + auracast_sampling_rate_hz=q['rate'], + octets_per_frame=q['octets'], + transport='', # is set in backend + assisted_listening_stream=assisted_listening, + immediate_rendering=immediate_rendering, + presentation_delay_us=int(presentation_delay_ms * 1000), + qos_config=QOS_PRESET_MAP[qos_preset], + bigs=[ + auracast_config.AuracastBigConfig( + code=(stream_passwort.strip() or None), + name=stream_name, + program_info=program_info, + language=language, + audio_source=(f"device:{input_device}"), + input_format=(f"int16le,{q['rate']},1"), + iso_que_len=1, + sampling_frequency=q['rate'], + octets_per_frame=q['octets'], + ), + ], + ) + + try: + r = requests.post(f"{BACKEND_URL}/init", json=config.model_dump()) + if r.status_code == 200: + is_started = True + else: + st.error(f"Failed to initialize: {r.text}") + except Exception as e: + st.error(f"Error: {e}") # Centralized rerun based on start/stop outcomes if is_started or is_stopped: @@ -864,7 +1671,7 @@ with st.expander("System control", expanded=False): st.subheader("System temperatures") temp_col1, temp_col2, temp_col3 = st.columns([1, 1, 1]) with temp_col1: - refresh_temps = st.button("Refresh") + refresh_temps = st.button("Refresh", key="refresh_temps_1") try: case_temp = read_case_temp() cpu_temp = read_cpu_temp() @@ -979,6 +1786,18 @@ with st.expander("System control", expanded=False): except Exception as e: st.error(f"Error calling update: {e}") + st.subheader("Restart DEP") + if st.button("Restart DEP"): + try: + r = requests.post(f"{BACKEND_URL}/restart_dep", timeout=30) + if r.ok: + result = r.json() + st.success(result.get('message', 'DEP restarted successfully.')) + else: + st.error(f"Failed to restart DEP: {r.status_code} {r.text}") + except Exception as e: + st.error(f"Error restarting DEP: {str(e)}") + st.subheader("Reboot") if st.button("Reboot now", type="primary"): try: @@ -990,6 +1809,77 @@ with st.expander("System control", expanded=False): except Exception as e: st.error(f"Error calling reboot: {e}") +############################ +# Record expander (collapsed) +############################ +with st.expander("Record", expanded=False): + + st.subheader("ALSA Device Recording") + + # Get ALSA devices from backend + try: + resp = requests.get(f"{BACKEND_URL}/alsa_devices", timeout=2) + if resp.status_code == 200: + alsa_devices = resp.json().get('devices', []) + else: + alsa_devices = [] + except Exception: + alsa_devices = [] + + # ALSA device selection dropdown + device_options = [f"{d['name']} [{d['id']}]" for d in alsa_devices] + device_name_map = {f"{d['name']} [{d['id']}]": d['name'] for d in alsa_devices} + + if device_options: + selected_device = st.selectbox( + "Select ALSA Device", + device_options, + help="Choose an ALSA device for recording" + ) + selected_device_name = device_name_map.get(selected_device) + else: + st.warning("No ALSA devices found.") + selected_device_name = None + + # Recording controls + col_record, col_download = st.columns([1, 1]) + + with col_record: + if st.button("Start Recording (5s)", disabled=not selected_device_name): + try: + r = requests.post(f"{BACKEND_URL}/start_recording", json={"device": selected_device_name}, timeout=15) + if r.ok: + result = r.json() + if result.get('success'): + st.success(f"Recording completed: {result.get('filename')}") + st.session_state['last_recording'] = result.get('filename') + else: + st.error(f"Recording failed: {result.get('error', 'Unknown error')}") + else: + st.error(f"Failed to start recording: {r.status_code} {r.text}") + except Exception as e: + st.error(f"Error starting recording: {e}") + + with col_download: + last_recording = st.session_state.get('last_recording') + if last_recording: + try: + # Get the recorded file for download + file_resp = requests.get(f"{BACKEND_URL}/download_recording/{last_recording}", timeout=5) + if file_resp.status_code == 200: + st.download_button( + label="Download Last Recording", + data=file_resp.content, + file_name=last_recording, + mime="audio/wav" + ) + else: + st.warning("Recording file not available") + except Exception as e: + st.warning(f"Could not fetch recording: {e}") + else: + st.button("Download Last Recording", disabled=True, help="No recording available yet") + log.basicConfig( level=os.environ.get('LOG_LEVEL', log.DEBUG), format='%(module)s.py:%(lineno)d %(levelname)s: %(message)s' diff --git a/src/auracast/server/multicast_server.py b/src/auracast/server/multicast_server.py index 0b97357..63a5050 100644 --- a/src/auracast/server/multicast_server.py +++ b/src/auracast/server/multicast_server.py @@ -8,6 +8,7 @@ import json from datetime import datetime import asyncio import random +import subprocess from dotenv import load_dotenv from fastapi import FastAPI, HTTPException @@ -21,6 +22,7 @@ from auracast.utils.sounddevice_utils import ( get_alsa_usb_inputs, resolve_input_device_index, refresh_pw_cache, + devices_by_backend, ) load_dotenv() @@ -326,30 +328,122 @@ async def init_radio(transport: str, conf: auracast_config.AuracastConfigGroup, first_source = conf.bigs[0].audio_source if conf.bigs else '' input_device_name = None audio_mode_persist = 'Demo' - if isinstance(first_source, str) and first_source.startswith('device:'): - input_device_name = first_source.split(':', 1)[1] if ':' in first_source else None + if any(isinstance(b.audio_source, str) and b.audio_source.startswith('device:') for b in conf.bigs): + if isinstance(first_source, str) and first_source.startswith('device:'): + input_device_name = first_source.split(':', 1)[1] if ':' in first_source else None + alsa_usb_names = {d.get('name') for _, d in get_alsa_usb_inputs()} net_names = {d.get('name') for _, d in get_network_pw_inputs()} + dante_channels = {"dante_asrc_ch1", "dante_asrc_ch2", "dante_asrc_ch3", "dante_asrc_ch4", "dante_asrc_ch5", "dante_asrc_ch6"} + if input_device_name in ('ch1', 'ch2'): - # Explicitly treat ch1/ch2 as Analog input mode audio_mode_persist = 'Analog' + elif input_device_name in dante_channels: + audio_mode_persist = 'Network - Dante' else: audio_mode_persist = 'Network' if (input_device_name in net_names) else 'USB' - if input_device_name and input_device_name.isdigit(): - device_index = int(input_device_name) + # Configure each BIG independently so Dante multi-stream can select different channels. + for big in conf.bigs: + if not (isinstance(big.audio_source, str) and big.audio_source.startswith('device:')): + continue + sel = big.audio_source.split(':', 1)[1] if ':' in big.audio_source else None + + # IMPORTANT: All hardware capture is at 48kHz; LC3 encoder may downsample. + hardware_capture_rate = 48000 + + if sel in dante_channels: + # Use ALSA directly (PortAudio doesn't enumerate route PCMs on some systems). + big.audio_source = f'alsa:{sel}' + big.input_format = f"int16le,{hardware_capture_rate},1" + continue + + # Dante stereo devices: dante_stereo_X_Y (e.g., dante_stereo_1_2) + if sel and sel.startswith('dante_stereo_'): + is_stereo = getattr(big, 'num_bis', 1) == 2 + if is_stereo: + # Stereo mode: use the stereo ALSA device with 2 channels + big.audio_source = f'alsa:{sel}' + big.input_format = f"int16le,{hardware_capture_rate},2" + log.info("Configured Dante stereo input: using ALSA %s with 2 channels", sel) + else: + # Fallback to mono if num_bis != 2 (shouldn't happen) + big.audio_source = f'alsa:{sel}' + big.input_format = f"int16le,{hardware_capture_rate},2" + log.warning("Dante stereo device %s used but num_bis=%d, capturing as stereo anyway", sel, getattr(big, 'num_bis', 1)) + continue + + if sel in ('ch1', 'ch2'): + # Analog channels: check if this should be stereo based on num_bis + is_stereo = getattr(big, 'num_bis', 1) == 2 + + if is_stereo and sel == 'ch1': + # Stereo mode: use ALSA directly to capture both channels from hardware + # ch1=left (channel 0), ch2=right (channel 1) + big.audio_source = 'alsa:hw:CARD=i2s,DEV=0' + big.input_format = f"int16le,{hardware_capture_rate},2" + log.info("Configured analog stereo input: using ALSA hw:CARD=i2s,DEV=0 with ch1=left, ch2=right") + elif is_stereo and sel == 'ch2': + # Skip ch2 in stereo mode as it's already captured as part of stereo pair + continue + else: + # Mono mode: individual channel capture + device_index = resolve_input_device_index(sel) + if device_index is None: + raise HTTPException(status_code=400, detail=f"Audio device '{sel}' not found.") + big.audio_source = f'device:{device_index}' + big.input_format = f"int16le,{hardware_capture_rate},1" + continue + + if sel and sel.isdigit(): + device_index = int(sel) + else: + device_index = resolve_input_device_index(sel or '') + if device_index is None: + raise HTTPException(status_code=400, detail=f"Audio device '{sel}' not found.") + + try: + resolved_devinfo = sd.query_devices(device_index) + log.info( + "Resolved input device '%s' -> idx=%s name='%s' hostapi=%s max_in=%s", + sel, + device_index, + resolved_devinfo.get('name'), + resolved_devinfo.get('hostapi'), + resolved_devinfo.get('max_input_channels'), + ) + except Exception: + log.info("Resolved input device '%s' -> idx=%s (devinfo unavailable)", sel, device_index) + + big.audio_source = f'device:{device_index}' + + devinfo = sd.query_devices(device_index) + max_in = int(devinfo.get('max_input_channels') or 1) + channels = max(1, min(2, max_in)) + big.input_format = f"int16le,{hardware_capture_rate},{channels}" + + # The config group keeps the target sampling rate for LC3 encoder + # The audio input will capture at 48kHz and LC3 encoder will downsample + target_sampling_rate = getattr(conf, 'auracast_sampling_rate_hz', None) + if target_sampling_rate is None and conf.bigs: + target_sampling_rate = getattr(conf.bigs[0], 'sampling_frequency', 48000) + if target_sampling_rate is None: + target_sampling_rate = 48000 + + # Keep the config group sampling rate as set by frontend + conf.auracast_sampling_rate_hz = target_sampling_rate + + # Ensure octets_per_frame matches the target sampling rate + if target_sampling_rate == 48000: + conf.octets_per_frame = 120 + elif target_sampling_rate == 32000: + conf.octets_per_frame = 80 + elif target_sampling_rate == 24000: + conf.octets_per_frame = 60 + elif target_sampling_rate == 16000: + conf.octets_per_frame = 40 else: - device_index = resolve_input_device_index(input_device_name or '') - if device_index is None: - raise HTTPException(status_code=400, detail=f"Audio device '{input_device_name}' not found.") - for big in conf.bigs: - if isinstance(big.audio_source, str) and big.audio_source.startswith('device:'): - big.audio_source = f'device:{device_index}' - devinfo = sd.query_devices(device_index) - max_in = int(devinfo.get('max_input_channels') or 1) - channels = max(1, min(2, max_in)) - for big in conf.bigs: - big.input_format = f"int16le,{48000},{channels}" + conf.octets_per_frame = 120 # default to 48000 setting conf.qos_config.max_transport_latency_ms = int(conf.qos_config.number_of_retransmissions) * 10 + 3 @@ -365,7 +459,7 @@ async def init_radio(transport: str, conf: auracast_config.AuracastConfigGroup, await mc.init_broadcast() auto_started = False - if any(isinstance(big.audio_source, str) and (big.audio_source.startswith("device:") or big.audio_source.startswith("file:")) for big in conf.bigs): + if any(isinstance(big.audio_source, str) and (big.audio_source.startswith("device:") or big.audio_source.startswith("alsa:") or big.audio_source.startswith("file:")) for big in conf.bigs): await mc.start_streaming() auto_started = True @@ -390,6 +484,7 @@ async def init_radio(transport: str, conf: auracast_config.AuracastConfigGroup, 'qos_preset': _resolve_qos_preset_name(conf.qos_config), 'immediate_rendering': getattr(conf, 'immediate_rendering', False), 'assisted_listening_stream': getattr(conf, 'assisted_listening_stream', False), + 'analog_stereo_mode': getattr(conf.bigs[0], 'analog_stereo_mode', False) if conf.bigs else False, 'stream_password': (conf.bigs[0].code if conf.bigs and getattr(conf.bigs[0], 'code', None) else None), 'big_ids': [getattr(big, 'id', DEFAULT_BIG_ID) for big in conf.bigs], 'big_random_addresses': [getattr(big, 'random_address', DEFAULT_RANDOM_ADDRESS) for big in conf.bigs], @@ -577,6 +672,9 @@ async def _autostart_from_settings(): presentation_delay_us=pres_delay if pres_delay is not None else 40000, bigs=bigs, ) + # Set num_bis for stereo mode if needed + if conf.bigs and settings.get('analog_stereo_mode', False): + conf.bigs[0].num_bis = 2 conf.qos_config = QOS_PRESET_MAP.get(saved_qos_preset, QOS_PRESET_MAP["Fast"]) log.info("[AUTOSTART][PRIMARY] Scheduling demo init_radio in 2s") await asyncio.sleep(2) @@ -641,6 +739,9 @@ async def _autostart_from_settings(): presentation_delay_us=pres_delay if pres_delay is not None else 40000, bigs=bigs, ) + # Set num_bis for stereo mode if needed + if conf.bigs and settings.get('analog_stereo_mode', False): + conf.bigs[0].num_bis = 2 conf.qos_config = QOS_PRESET_MAP.get(saved_qos_preset, QOS_PRESET_MAP["Fast"]) log.info("[AUTOSTART][PRIMARY] Scheduling device init_radio in 2s") await asyncio.sleep(2) @@ -810,6 +911,18 @@ async def _autostart_from_settings(): async def _startup_autostart_event(): # Spawn the autostart task without blocking startup log.info("[STARTUP] Auracast multicast server startup: initializing settings cache, I2C, and PipeWire cache") + + # Run install_asoundconf.sh script + script_path = os.path.join(os.path.dirname(__file__), '..', 'misc', 'install_asoundconf.sh') + try: + log.info("[STARTUP] Running install_asoundconf.sh script") + result = subprocess.run(['bash', script_path], capture_output=True, text=True, check=True) + log.info(f"[STARTUP] install_asoundconf.sh completed: {result.stdout.strip()}") + except subprocess.CalledProcessError as e: + log.error(f"[STARTUP] Failed to run install_asoundconf.sh: {e.stderr.strip()}") + except Exception as e: + log.error(f"[STARTUP] Error running install_asoundconf.sh: {str(e)}") + # Hydrate settings cache once to avoid disk I/O during /status _init_settings_cache_from_disk() await _init_i2c_on_startup() @@ -845,6 +958,28 @@ async def audio_inputs_pw_network(): log.error("Exception in /audio_inputs_pw_network: %s", traceback.format_exc()) raise HTTPException(status_code=500, detail=str(e)) +@app.get("/audio_inputs_dante") +async def audio_inputs_dante(): + """List Dante ALSA input devices from asound.conf.""" + try: + dante_channels = [ + "dante_asrc_ch1", + "dante_asrc_ch2", + "dante_asrc_ch3", + "dante_asrc_ch4", + "dante_asrc_ch5", + "dante_asrc_ch6", + ] + return { + "inputs": [ + {"id": name, "name": name, "max_input_channels": 1} + for name in dante_channels + ] + } + except Exception as e: + log.error("Exception in /audio_inputs_dante: %s", traceback.format_exc()) + raise HTTPException(status_code=500, detail=str(e)) + @app.post("/refresh_audio_devices") async def refresh_audio_devices(): """Triggers a re-scan of audio devices, but only if no stream is active.""" @@ -899,6 +1034,56 @@ async def system_reboot(): raise HTTPException(status_code=500, detail=str(e)) +@app.post("/restart_dep") +async def restart_dep(): + """Restart DEP by running dep.sh stop then dep.sh start in the dep directory. + + Requires the service user to have passwordless sudo permissions to run dep.sh. + """ + try: + # Get the dep directory path (dep.sh is in dante_package subdirectory) + dep_dir = os.path.join(os.path.dirname(__file__), '..', '..', 'dep', 'dante_package') + + # Run dep.sh stop first + log.info("Stopping DEP...") + stop_process = await asyncio.create_subprocess_exec( + "sudo", "bash", "dep.sh", "stop", + cwd=dep_dir, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE + ) + stop_stdout, stop_stderr = await stop_process.communicate() + + if stop_process.returncode != 0: + error_msg = stop_stderr.decode() if stop_stderr else "Unknown error" + log.error(f"Failed to stop DEP: {error_msg}") + raise HTTPException(status_code=500, detail=f"Failed to stop DEP: {error_msg}") + + # Run dep.sh start after stop succeeds + log.info("Starting DEP...") + start_process = await asyncio.create_subprocess_exec( + "sudo", "bash", "dep.sh", "start", + cwd=dep_dir, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE + ) + start_stdout, start_stderr = await start_process.communicate() + + if start_process.returncode == 0: + log.info("DEP restarted successfully") + return {"status": "success", "message": "DEP restarted successfully"} + else: + error_msg = start_stderr.decode() if start_stderr else "Unknown error" + log.error(f"Failed to start DEP: {error_msg}") + raise HTTPException(status_code=500, detail=f"Failed to start DEP: {error_msg}") + + except HTTPException: + raise + except Exception as e: + log.error("Exception in /restart_dep: %s", e, exc_info=True) + raise HTTPException(status_code=500, detail=str(e)) + + @app.get("/version") async def get_version(): """Get the current software version (git tag or commit).""" @@ -1069,6 +1254,169 @@ async def system_update(): raise HTTPException(status_code=500, detail=str(e)) +# Recording functionality +RECORDINGS_DIR = os.path.join(os.path.dirname(__file__), 'recordings') +os.makedirs(RECORDINGS_DIR, exist_ok=True) + + +def cleanup_old_recordings(keep_latest: str = None): + """Delete all recordings except the latest one (or specified file).""" + try: + recordings = [] + for filename in os.listdir(RECORDINGS_DIR): + if filename.endswith('.wav'): + filepath = os.path.join(RECORDINGS_DIR, filename) + if os.path.isfile(filepath): + recordings.append((filename, os.path.getmtime(filepath))) + + # Sort by modification time (newest first) + recordings.sort(key=lambda x: x[1], reverse=True) + + # Keep only the latest recording (or the specified one) + if keep_latest and os.path.exists(os.path.join(RECORDINGS_DIR, keep_latest)): + files_to_keep = {keep_latest} + else: + files_to_keep = {recordings[0][0]} if recordings else set() + + # Delete old recordings + for filename, _ in recordings: + if filename not in files_to_keep: + filepath = os.path.join(RECORDINGS_DIR, filename) + try: + os.remove(filepath) + log.info("Deleted old recording: %s", filename) + except Exception as e: + log.warning("Failed to delete recording %s: %s", filename, e) + + except Exception as e: + log.warning("Error during recording cleanup: %s", e) + + +@app.get("/alsa_devices") +async def get_alsa_devices(): + """Get list of available ALSA input devices.""" + try: + devices = [] + dev_list = sd.query_devices() + for idx, dev in enumerate(dev_list): + if dev.get('max_input_channels', 0) > 0: + devices.append({ + 'id': idx, + 'name': dev['name'], + 'max_input_channels': dev['max_input_channels'] + }) + + # Add individual Dante ASRC channels if shared device is found + dante_shared_device = None + for device in devices: + if device['name'] == 'dante_asrc_shared6': + dante_shared_device = device + break + + if dante_shared_device: + # Add individual Dante ASRC channels as virtual devices + for i in range(1, 7): # ch1 to ch6 + devices.append({ + 'id': f"dante_asrc_ch{i}", + 'name': f'dante_asrc_ch{i}', + 'max_input_channels': 1, + 'parent_device': dante_shared_device['name'], + 'parent_id': dante_shared_device['id'] + }) + + return {"devices": devices} + except Exception as e: + log.error("Exception in /alsa_devices: %s", e, exc_info=True) + raise HTTPException(status_code=500, detail=str(e)) + + +@app.post("/start_recording") +async def start_recording(request: dict): + """Start a 5-second recording from the specified ALSA device.""" + try: + device_name = request.get('device') + if not device_name: + raise HTTPException(status_code=400, detail="Device name is required") + + # Generate filename with timestamp + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + filename = f"recording_{timestamp}.wav" + filepath = os.path.join(RECORDINGS_DIR, filename) + + # Determine channel count based on device type + # For other devices, try to find actual channel count + channels = 1 # Default to mono + try: + devices = sd.query_devices() + for dev in devices: + if dev['name'] == device_name and dev.get('max_input_channels', 0) > 0: + channels = dev.get('max_input_channels', 1) + break + except Exception: + pass + + # Build arecord command + cmd = [ + "arecord", + "-D", device_name, # Use the device name directly + "-f", "cd", # CD quality (16-bit little-endian, 44100 Hz) + "-c", str(channels), # Channel count + "-d", "5", # Duration in seconds + "-t", "wav", # WAV format + filepath + ] + + log.info("Starting recording with command: %s", " ".join(cmd)) + proc = await asyncio.create_subprocess_exec( + *cmd, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE + ) + stdout, stderr = await proc.communicate() + + if proc.returncode != 0: + error_msg = stderr.decode(errors="ignore").strip() if stderr else "Unknown error" + log.error("Recording failed: %s", error_msg) + raise HTTPException(status_code=500, detail=f"Recording failed: {error_msg}") + + # Verify file was created and has content + if not os.path.exists(filepath) or os.path.getsize(filepath) == 0: + raise HTTPException(status_code=500, detail="Recording file was not created or is empty") + + # Clean up old recordings, keeping only this new one + cleanup_old_recordings(keep_latest=filename) + + log.info("Recording completed successfully: %s", filename) + return {"success": True, "filename": filename} + + except HTTPException: + raise + except Exception as e: + log.error("Exception in /start_recording: %s", e, exc_info=True) + raise HTTPException(status_code=500, detail=str(e)) + + +@app.get("/download_recording/{filename}") +async def download_recording(filename: str): + """Download a recorded WAV file.""" + try: + # Validate filename to prevent directory traversal + if not filename.endswith('.wav') or '/' in filename or '\\' in filename: + raise HTTPException(status_code=400, detail="Invalid filename") + + filepath = os.path.join(RECORDINGS_DIR, filename) + if not os.path.exists(filepath): + raise HTTPException(status_code=404, detail="Recording file not found") + + return FileResponse(filepath, filename=filename, media_type="audio/wav") + + except HTTPException: + raise + except Exception as e: + log.error("Exception in /download_recording: %s", e, exc_info=True) + raise HTTPException(status_code=500, detail=str(e)) + + if __name__ == '__main__': import os os.chdir(os.path.dirname(__file__)) diff --git a/src/auracast/utils/sounddevice_utils.py b/src/auracast/utils/sounddevice_utils.py index 2c30e89..2012df4 100644 --- a/src/auracast/utils/sounddevice_utils.py +++ b/src/auracast/utils/sounddevice_utils.py @@ -243,7 +243,8 @@ def get_alsa_usb_inputs(): 'usb' in name or re.search(r'hw:\d+(?:,\d+)?', name) or name.startswith('dsnoop') or - name in ('ch1', 'ch2') + name in ('ch1', 'ch2') or + name.startswith('dante_asrc_ch') ): usb_inputs.append((idx, dev)) diff --git a/src/dep/dante_package/bundle/config.json b/src/dep/dante_package/bundle/config.json new file mode 120000 index 0000000..5e39174 --- /dev/null +++ b/src/dep/dante_package/bundle/config.json @@ -0,0 +1 @@ +../dante_data/capability/config.json \ No newline at end of file diff --git a/src/dep/dante_package/bundle/rootfs/dante/DepApe b/src/dep/dante_package/bundle/rootfs/dante/DepApe new file mode 100755 index 0000000..24eaaf1 Binary files /dev/null and b/src/dep/dante_package/bundle/rootfs/dante/DepApe differ diff --git a/src/dep/dante_package/bundle/rootfs/dante/DepClockBridge b/src/dep/dante_package/bundle/rootfs/dante/DepClockBridge new file mode 100755 index 0000000..d0e4e86 Binary files /dev/null and b/src/dep/dante_package/bundle/rootfs/dante/DepClockBridge differ diff --git a/src/dep/dante_package/bundle/rootfs/dante/apec b/src/dep/dante_package/bundle/rootfs/dante/apec new file mode 100755 index 0000000..6aa1213 Binary files /dev/null and b/src/dep/dante_package/bundle/rootfs/dante/apec differ diff --git a/src/dep/dante_package/bundle/rootfs/dante/atest b/src/dep/dante_package/bundle/rootfs/dante/atest new file mode 100755 index 0000000..77cb821 Binary files /dev/null and b/src/dep/dante_package/bundle/rootfs/dante/atest differ diff --git a/src/dep/dante_package/bundle/rootfs/dante/conmon_server b/src/dep/dante_package/bundle/rootfs/dante/conmon_server new file mode 100755 index 0000000..4956350 Binary files /dev/null and b/src/dep/dante_package/bundle/rootfs/dante/conmon_server differ diff --git a/src/dep/dante_package/bundle/rootfs/dante/d2asrc b/src/dep/dante_package/bundle/rootfs/dante/d2asrc new file mode 100755 index 0000000..e2cc35d Binary files /dev/null and b/src/dep/dante_package/bundle/rootfs/dante/d2asrc differ diff --git a/src/dep/dante_package/bundle/rootfs/dante/dep_manager b/src/dep/dante_package/bundle/rootfs/dante/dep_manager new file mode 100755 index 0000000..afaa852 Binary files /dev/null and b/src/dep/dante_package/bundle/rootfs/dante/dep_manager differ diff --git a/src/dep/dante_package/bundle/rootfs/dante/hwclkcfg b/src/dep/dante_package/bundle/rootfs/dante/hwclkcfg new file mode 100755 index 0000000..51b4887 Binary files /dev/null and b/src/dep/dante_package/bundle/rootfs/dante/hwclkcfg differ diff --git a/src/dep/dante_package/bundle/rootfs/dante/mdnsd b/src/dep/dante_package/bundle/rootfs/dante/mdnsd new file mode 100755 index 0000000..2bd0be4 Binary files /dev/null and b/src/dep/dante_package/bundle/rootfs/dante/mdnsd differ diff --git a/src/dep/dante_package/bundle/rootfs/dante/ptp b/src/dep/dante_package/bundle/rootfs/dante/ptp new file mode 100755 index 0000000..5b1fcaf Binary files /dev/null and b/src/dep/dante_package/bundle/rootfs/dante/ptp differ diff --git a/src/dep/dante_package/bundle/rootfs/dante/upgrade b/src/dep/dante_package/bundle/rootfs/dante/upgrade new file mode 100755 index 0000000..dbba1a5 Binary files /dev/null and b/src/dep/dante_package/bundle/rootfs/dante/upgrade differ diff --git a/src/dep/dante_package/bundle/rootfs/etc/group b/src/dep/dante_package/bundle/rootfs/etc/group new file mode 100644 index 0000000..6822a27 --- /dev/null +++ b/src/dep/dante_package/bundle/rootfs/etc/group @@ -0,0 +1,26 @@ +root:x:0: +daemon:x:1: +bin:x:2: +sys:x:3: +adm:x:4: +tty:x:5: +disk:x:6: +lp:x:7: +mail:x:8: +kmem:x:9: +wheel:x:10:root +cdrom:x:11: +dialout:x:18: +floppy:x:19: +video:x:28: +audio:x:29: +tape:x:32: +www-data:x:33: +operator:x:37: +utmp:x:43: +plugdev:x:46: +staff:x:50: +lock:x:54: +netdev:x:82: +users:x:100: +nobody:x:65534: diff --git a/src/dep/dante_package/bundle/rootfs/etc/hostname b/src/dep/dante_package/bundle/rootfs/etc/hostname new file mode 100644 index 0000000..5b26b35 --- /dev/null +++ b/src/dep/dante_package/bundle/rootfs/etc/hostname @@ -0,0 +1 @@ +buildroot diff --git a/src/dep/dante_package/bundle/rootfs/etc/hosts b/src/dep/dante_package/bundle/rootfs/etc/hosts new file mode 100644 index 0000000..88e90b8 --- /dev/null +++ b/src/dep/dante_package/bundle/rootfs/etc/hosts @@ -0,0 +1,2 @@ +127.0.0.1 localhost +127.0.1.1 buildroot diff --git a/src/dep/dante_package/bundle/rootfs/etc/issue b/src/dep/dante_package/bundle/rootfs/etc/issue new file mode 100644 index 0000000..207b183 --- /dev/null +++ b/src/dep/dante_package/bundle/rootfs/etc/issue @@ -0,0 +1 @@ +Welcome to Buildroot diff --git a/src/dep/dante_package/bundle/rootfs/etc/localtime b/src/dep/dante_package/bundle/rootfs/etc/localtime new file mode 100644 index 0000000..8d1c8b6 --- /dev/null +++ b/src/dep/dante_package/bundle/rootfs/etc/localtime @@ -0,0 +1 @@ + diff --git a/src/dep/dante_package/bundle/rootfs/etc/machine-id b/src/dep/dante_package/bundle/rootfs/etc/machine-id new file mode 100644 index 0000000..8d1c8b6 --- /dev/null +++ b/src/dep/dante_package/bundle/rootfs/etc/machine-id @@ -0,0 +1 @@ + diff --git a/src/dep/dante_package/bundle/rootfs/etc/mtab b/src/dep/dante_package/bundle/rootfs/etc/mtab new file mode 120000 index 0000000..5c4677a --- /dev/null +++ b/src/dep/dante_package/bundle/rootfs/etc/mtab @@ -0,0 +1 @@ +../proc/self/mounts \ No newline at end of file diff --git a/src/dep/dante_package/bundle/rootfs/etc/network/if-pre-up.d/wait_iface b/src/dep/dante_package/bundle/rootfs/etc/network/if-pre-up.d/wait_iface new file mode 100755 index 0000000..ebccff2 --- /dev/null +++ b/src/dep/dante_package/bundle/rootfs/etc/network/if-pre-up.d/wait_iface @@ -0,0 +1,21 @@ +#!/bin/sh + +# In case we have a slow-to-appear interface (e.g. eth-over-USB), +# and we need to configure it, wait until it appears, but not too +# long either. IF_WAIT_DELAY is in seconds. + +if [ "${IF_WAIT_DELAY}" -a ! -e "/sys/class/net/${IFACE}" ]; then + printf "Waiting for interface %s to appear" "${IFACE}" + while [ ${IF_WAIT_DELAY} -gt 0 ]; do + if [ -e "/sys/class/net/${IFACE}" ]; then + printf "\n" + exit 0 + fi + sleep 1 + printf "." + : $((IF_WAIT_DELAY -= 1)) + done + printf " timeout!\n" + exit 1 +fi + diff --git a/src/dep/dante_package/bundle/rootfs/etc/network/nfs_check b/src/dep/dante_package/bundle/rootfs/etc/network/nfs_check new file mode 100755 index 0000000..dfa0cbf --- /dev/null +++ b/src/dep/dante_package/bundle/rootfs/etc/network/nfs_check @@ -0,0 +1,20 @@ +#!/bin/sh + +# This allows NFS booting to work while also being able to configure +# the network interface via DHCP when not NFS booting. Otherwise, a +# NFS booted system will likely hang during DHCP configuration. + +# Attempting to configure the network interface used for NFS will +# initially bring that network down. Since the root filesystem is +# accessed over this network, the system hangs. + +# This script is run by ifup and will attempt to detect if a NFS root +# mount uses the interface to be configured (IFACE), and if so does +# not configure it. This should allow the same build to be disk/flash +# booted or NFS booted. + +nfsip=`sed -n '/^[^ ]*:.* \/ nfs.*[ ,]addr=\([0-9.]\+\).*/s//\1/p' /proc/mounts` +if [ -n "$nfsip" ] && ip route get to "$nfsip" | grep -q "dev $IFACE"; then + echo Skipping $IFACE, used for NFS from $nfsip + exit 1 +fi diff --git a/src/dep/dante_package/bundle/rootfs/etc/nsswitch.conf b/src/dep/dante_package/bundle/rootfs/etc/nsswitch.conf new file mode 100644 index 0000000..5c38491 --- /dev/null +++ b/src/dep/dante_package/bundle/rootfs/etc/nsswitch.conf @@ -0,0 +1,13 @@ +# /etc/nsswitch.conf + +passwd: files +group: files +shadow: files + +hosts: files dns +networks: files dns + +protocols: files +services: files +ethers: files +rpc: files diff --git a/src/dep/dante_package/bundle/rootfs/etc/os-release b/src/dep/dante_package/bundle/rootfs/etc/os-release new file mode 120000 index 0000000..c4c75b4 --- /dev/null +++ b/src/dep/dante_package/bundle/rootfs/etc/os-release @@ -0,0 +1 @@ +../usr/lib/os-release \ No newline at end of file diff --git a/src/dep/dante_package/bundle/rootfs/etc/passwd b/src/dep/dante_package/bundle/rootfs/etc/passwd new file mode 100644 index 0000000..d8281d2 --- /dev/null +++ b/src/dep/dante_package/bundle/rootfs/etc/passwd @@ -0,0 +1,9 @@ +root:x:0:0:root:/root:/bin/sh +daemon:x:1:1:daemon:/usr/sbin:/bin/false +bin:x:2:2:bin:/bin:/bin/false +sys:x:3:3:sys:/dev:/bin/false +sync:x:4:100:sync:/bin:/bin/sync +mail:x:8:8:mail:/var/spool/mail:/bin/false +www-data:x:33:33:www-data:/var/www:/bin/false +operator:x:37:37:Operator:/var:/bin/false +nobody:x:65534:65534:nobody:/home:/bin/false diff --git a/src/dep/dante_package/bundle/rootfs/etc/profile b/src/dep/dante_package/bundle/rootfs/etc/profile new file mode 100644 index 0000000..eb8c75e --- /dev/null +++ b/src/dep/dante_package/bundle/rootfs/etc/profile @@ -0,0 +1,19 @@ +export PATH="/bin:/sbin:/usr/bin:/usr/sbin" + +if [ "$PS1" ]; then + if [ "`id -u`" -eq 0 ]; then + export PS1='# ' + else + export PS1='$ ' + fi +fi + +export EDITOR='/bin/vi' + +# Source configuration files from /etc/profile.d +for i in /etc/profile.d/*.sh ; do + if [ -r "$i" ]; then + . $i + fi +done +unset i diff --git a/src/dep/dante_package/bundle/rootfs/etc/profile.d/umask.sh b/src/dep/dante_package/bundle/rootfs/etc/profile.d/umask.sh new file mode 100644 index 0000000..8e71ad5 --- /dev/null +++ b/src/dep/dante_package/bundle/rootfs/etc/profile.d/umask.sh @@ -0,0 +1 @@ +umask 022 diff --git a/src/dep/dante_package/bundle/rootfs/etc/protocols b/src/dep/dante_package/bundle/rootfs/etc/protocols new file mode 100644 index 0000000..cfdd5ad --- /dev/null +++ b/src/dep/dante_package/bundle/rootfs/etc/protocols @@ -0,0 +1,61 @@ +# Internet (IP) protocols +# +# Updated from http://www.iana.org/assignments/protocol-numbers and other +# sources. + +ip 0 IP # internet protocol, pseudo protocol number +hopopt 0 HOPOPT # IPv6 Hop-by-Hop Option [RFC1883] +icmp 1 ICMP # internet control message protocol +igmp 2 IGMP # Internet Group Management +ggp 3 GGP # gateway-gateway protocol +ipencap 4 IP-ENCAP # IP encapsulated in IP (officially ``IP'') +st 5 ST # ST datagram mode +tcp 6 TCP # transmission control protocol +egp 8 EGP # exterior gateway protocol +igp 9 IGP # any private interior gateway (Cisco) +pup 12 PUP # PARC universal packet protocol +udp 17 UDP # user datagram protocol +hmp 20 HMP # host monitoring protocol +xns-idp 22 XNS-IDP # Xerox NS IDP +rdp 27 RDP # "reliable datagram" protocol +iso-tp4 29 ISO-TP4 # ISO Transport Protocol class 4 [RFC905] +dccp 33 DCCP # Datagram Congestion Control Prot. [RFC4340] +xtp 36 XTP # Xpress Transfer Protocol +ddp 37 DDP # Datagram Delivery Protocol +idpr-cmtp 38 IDPR-CMTP # IDPR Control Message Transport +ipv6 41 IPv6 # Internet Protocol, version 6 +ipv6-route 43 IPv6-Route # Routing Header for IPv6 +ipv6-frag 44 IPv6-Frag # Fragment Header for IPv6 +idrp 45 IDRP # Inter-Domain Routing Protocol +rsvp 46 RSVP # Reservation Protocol +gre 47 GRE # General Routing Encapsulation +esp 50 IPSEC-ESP # Encap Security Payload [RFC2406] +ah 51 IPSEC-AH # Authentication Header [RFC2402] +skip 57 SKIP # SKIP +ipv6-icmp 58 IPv6-ICMP # ICMP for IPv6 +ipv6-nonxt 59 IPv6-NoNxt # No Next Header for IPv6 +ipv6-opts 60 IPv6-Opts # Destination Options for IPv6 +rspf 73 RSPF CPHB # Radio Shortest Path First (officially CPHB) +vmtp 81 VMTP # Versatile Message Transport +eigrp 88 EIGRP # Enhanced Interior Routing Protocol (Cisco) +ospf 89 OSPFIGP # Open Shortest Path First IGP +ax.25 93 AX.25 # AX.25 frames +ipip 94 IPIP # IP-within-IP Encapsulation Protocol +etherip 97 ETHERIP # Ethernet-within-IP Encapsulation [RFC3378] +encap 98 ENCAP # Yet Another IP encapsulation [RFC1241] +# 99 # any private encryption scheme +pim 103 PIM # Protocol Independent Multicast +ipcomp 108 IPCOMP # IP Payload Compression Protocol +vrrp 112 VRRP # Virtual Router Redundancy Protocol [RFC5798] +l2tp 115 L2TP # Layer Two Tunneling Protocol [RFC2661] +isis 124 ISIS # IS-IS over IPv4 +sctp 132 SCTP # Stream Control Transmission Protocol +fc 133 FC # Fibre Channel +mobility-header 135 Mobility-Header # Mobility Support for IPv6 [RFC3775] +udplite 136 UDPLite # UDP-Lite [RFC3828] +mpls-in-ip 137 MPLS-in-IP # MPLS-in-IP [RFC4023] +manet 138 # MANET Protocols [RFC5498] +hip 139 HIP # Host Identity Protocol +shim6 140 Shim6 # Shim6 Protocol [RFC5533] +wesp 141 WESP # Wrapped Encapsulating Security Payload +rohc 142 ROHC # Robust Header Compression diff --git a/src/dep/dante_package/bundle/rootfs/etc/resolv.conf b/src/dep/dante_package/bundle/rootfs/etc/resolv.conf new file mode 100644 index 0000000..8d1c8b6 --- /dev/null +++ b/src/dep/dante_package/bundle/rootfs/etc/resolv.conf @@ -0,0 +1 @@ + diff --git a/src/dep/dante_package/bundle/rootfs/etc/services b/src/dep/dante_package/bundle/rootfs/etc/services new file mode 100644 index 0000000..b287b63 --- /dev/null +++ b/src/dep/dante_package/bundle/rootfs/etc/services @@ -0,0 +1,302 @@ +# /etc/services: +# $Id: services,v 1.1 2004/10/09 02:49:18 andersen Exp $ +# +# Network services, Internet style +# +# Note that it is presently the policy of IANA to assign a single well-known +# port number for both TCP and UDP; hence, most entries here have two entries +# even if the protocol doesn't support UDP operations. +# Updated from RFC 1700, ``Assigned Numbers'' (October 1994). Not all ports +# are included, only the more common ones. + +tcpmux 1/tcp # TCP port service multiplexer +echo 7/tcp +echo 7/udp +discard 9/tcp sink null +discard 9/udp sink null +systat 11/tcp users +daytime 13/tcp +daytime 13/udp +netstat 15/tcp +qotd 17/tcp quote +msp 18/tcp # message send protocol +msp 18/udp # message send protocol +chargen 19/tcp ttytst source +chargen 19/udp ttytst source +ftp-data 20/tcp +ftp 21/tcp +fsp 21/udp fspd +ssh 22/tcp # SSH Remote Login Protocol +ssh 22/udp # SSH Remote Login Protocol +telnet 23/tcp +# 24 - private +smtp 25/tcp mail +# 26 - unassigned +time 37/tcp timserver +time 37/udp timserver +rlp 39/udp resource # resource location +nameserver 42/tcp name # IEN 116 +whois 43/tcp nicname +re-mail-ck 50/tcp # Remote Mail Checking Protocol +re-mail-ck 50/udp # Remote Mail Checking Protocol +domain 53/tcp nameserver # name-domain server +domain 53/udp nameserver +mtp 57/tcp # deprecated +bootps 67/tcp # BOOTP server +bootps 67/udp +bootpc 68/tcp # BOOTP client +bootpc 68/udp +tftp 69/udp +gopher 70/tcp # Internet Gopher +gopher 70/udp +rje 77/tcp netrjs +finger 79/tcp +www 80/tcp http # WorldWideWeb HTTP +www 80/udp # HyperText Transfer Protocol +link 87/tcp ttylink +kerberos 88/tcp kerberos5 krb5 # Kerberos v5 +kerberos 88/udp kerberos5 krb5 # Kerberos v5 +supdup 95/tcp +# 100 - reserved +hostnames 101/tcp hostname # usually from sri-nic +iso-tsap 102/tcp tsap # part of ISODE. +csnet-ns 105/tcp cso-ns # also used by CSO name server +csnet-ns 105/udp cso-ns +# unfortunately the poppassd (Eudora) uses a port which has already +# been assigned to a different service. We list the poppassd as an +# alias here. This should work for programs asking for this service. +# (due to a bug in inetd the 3com-tsmux line is disabled) +#3com-tsmux 106/tcp poppassd +#3com-tsmux 106/udp poppassd +rtelnet 107/tcp # Remote Telnet +rtelnet 107/udp +pop-2 109/tcp postoffice # POP version 2 +pop-2 109/udp +pop-3 110/tcp # POP version 3 +pop-3 110/udp +sunrpc 111/tcp portmapper # RPC 4.0 portmapper TCP +sunrpc 111/udp portmapper # RPC 4.0 portmapper UDP +auth 113/tcp authentication tap ident +sftp 115/tcp +uucp-path 117/tcp +nntp 119/tcp readnews untp # USENET News Transfer Protocol +ntp 123/tcp +ntp 123/udp # Network Time Protocol +netbios-ns 137/tcp # NETBIOS Name Service +netbios-ns 137/udp +netbios-dgm 138/tcp # NETBIOS Datagram Service +netbios-dgm 138/udp +netbios-ssn 139/tcp # NETBIOS session service +netbios-ssn 139/udp +imap2 143/tcp # Interim Mail Access Proto v2 +imap2 143/udp +snmp 161/udp # Simple Net Mgmt Proto +snmp-trap 162/udp snmptrap # Traps for SNMP +cmip-man 163/tcp # ISO mgmt over IP (CMOT) +cmip-man 163/udp +cmip-agent 164/tcp +cmip-agent 164/udp +xdmcp 177/tcp # X Display Mgr. Control Proto +xdmcp 177/udp +nextstep 178/tcp NeXTStep NextStep # NeXTStep window +nextstep 178/udp NeXTStep NextStep # server +bgp 179/tcp # Border Gateway Proto. +bgp 179/udp +prospero 191/tcp # Cliff Neuman's Prospero +prospero 191/udp +irc 194/tcp # Internet Relay Chat +irc 194/udp +smux 199/tcp # SNMP Unix Multiplexer +smux 199/udp +at-rtmp 201/tcp # AppleTalk routing +at-rtmp 201/udp +at-nbp 202/tcp # AppleTalk name binding +at-nbp 202/udp +at-echo 204/tcp # AppleTalk echo +at-echo 204/udp +at-zis 206/tcp # AppleTalk zone information +at-zis 206/udp +qmtp 209/tcp # The Quick Mail Transfer Protocol +qmtp 209/udp # The Quick Mail Transfer Protocol +z3950 210/tcp wais # NISO Z39.50 database +z3950 210/udp wais +ipx 213/tcp # IPX +ipx 213/udp +imap3 220/tcp # Interactive Mail Access +imap3 220/udp # Protocol v3 +ulistserv 372/tcp # UNIX Listserv +ulistserv 372/udp +https 443/tcp # MCom +https 443/udp # MCom +snpp 444/tcp # Simple Network Paging Protocol +snpp 444/udp # Simple Network Paging Protocol +saft 487/tcp # Simple Asynchronous File Transfer +saft 487/udp # Simple Asynchronous File Transfer +npmp-local 610/tcp dqs313_qmaster # npmp-local / DQS +npmp-local 610/udp dqs313_qmaster # npmp-local / DQS +npmp-gui 611/tcp dqs313_execd # npmp-gui / DQS +npmp-gui 611/udp dqs313_execd # npmp-gui / DQS +hmmp-ind 612/tcp dqs313_intercell# HMMP Indication / DQS +hmmp-ind 612/udp dqs313_intercell# HMMP Indication / DQS +# +# UNIX specific services +# +exec 512/tcp +biff 512/udp comsat +login 513/tcp +who 513/udp whod +shell 514/tcp cmd # no passwords used +syslog 514/udp +printer 515/tcp spooler # line printer spooler +talk 517/udp +ntalk 518/udp +route 520/udp router routed # RIP +timed 525/udp timeserver +tempo 526/tcp newdate +courier 530/tcp rpc +conference 531/tcp chat +netnews 532/tcp readnews +netwall 533/udp # -for emergency broadcasts +uucp 540/tcp uucpd # uucp daemon +afpovertcp 548/tcp # AFP over TCP +afpovertcp 548/udp # AFP over TCP +remotefs 556/tcp rfs_server rfs # Brunhoff remote filesystem +klogin 543/tcp # Kerberized `rlogin' (v5) +kshell 544/tcp krcmd # Kerberized `rsh' (v5) +kerberos-adm 749/tcp # Kerberos `kadmin' (v5) +# +webster 765/tcp # Network dictionary +webster 765/udp +# +# From ``Assigned Numbers'': +# +#> The Registered Ports are not controlled by the IANA and on most systems +#> can be used by ordinary user processes or programs executed by ordinary +#> users. +# +#> Ports are used in the TCP [45,106] to name the ends of logical +#> connections which carry long term conversations. For the purpose of +#> providing services to unknown callers, a service contact port is +#> defined. This list specifies the port used by the server process as its +#> contact port. While the IANA can not control uses of these ports it +#> does register or list uses of these ports as a convienence to the +#> community. +# +nfsdstatus 1110/tcp +nfsd-keepalive 1110/udp + +ingreslock 1524/tcp +ingreslock 1524/udp +prospero-np 1525/tcp # Prospero non-privileged +prospero-np 1525/udp +datametrics 1645/tcp old-radius # datametrics / old radius entry +datametrics 1645/udp old-radius # datametrics / old radius entry +sa-msg-port 1646/tcp old-radacct # sa-msg-port / old radacct entry +sa-msg-port 1646/udp old-radacct # sa-msg-port / old radacct entry +radius 1812/tcp # Radius +radius 1812/udp # Radius +radacct 1813/tcp # Radius Accounting +radacct 1813/udp # Radius Accounting +nfsd 2049/tcp nfs +nfsd 2049/udp nfs +cvspserver 2401/tcp # CVS client/server operations +cvspserver 2401/udp # CVS client/server operations +mysql 3306/tcp # MySQL +mysql 3306/udp # MySQL +rfe 5002/tcp # Radio Free Ethernet +rfe 5002/udp # Actually uses UDP only +cfengine 5308/tcp # CFengine +cfengine 5308/udp # CFengine +bbs 7000/tcp # BBS service +# +# +# Kerberos (Project Athena/MIT) services +# Note that these are for Kerberos v4, and are unofficial. Sites running +# v4 should uncomment these and comment out the v5 entries above. +# +kerberos4 750/udp kerberos-iv kdc # Kerberos (server) udp +kerberos4 750/tcp kerberos-iv kdc # Kerberos (server) tcp +kerberos_master 751/udp # Kerberos authentication +kerberos_master 751/tcp # Kerberos authentication +passwd_server 752/udp # Kerberos passwd server +krb_prop 754/tcp # Kerberos slave propagation +krbupdate 760/tcp kreg # Kerberos registration +kpasswd 761/tcp kpwd # Kerberos "passwd" +kpop 1109/tcp # Pop with Kerberos +knetd 2053/tcp # Kerberos de-multiplexor +zephyr-srv 2102/udp # Zephyr server +zephyr-clt 2103/udp # Zephyr serv-hm connection +zephyr-hm 2104/udp # Zephyr hostmanager +eklogin 2105/tcp # Kerberos encrypted rlogin +# +# Unofficial but necessary (for NetBSD) services +# +supfilesrv 871/tcp # SUP server +supfiledbg 1127/tcp # SUP debugging +# +# Datagram Delivery Protocol services +# +rtmp 1/ddp # Routing Table Maintenance Protocol +nbp 2/ddp # Name Binding Protocol +echo 4/ddp # AppleTalk Echo Protocol +zip 6/ddp # Zone Information Protocol +# +# Services added for the Debian GNU/Linux distribution +poppassd 106/tcp # Eudora +poppassd 106/udp # Eudora +mailq 174/tcp # Mailer transport queue for Zmailer +mailq 174/tcp # Mailer transport queue for Zmailer +omirr 808/tcp omirrd # online mirror +omirr 808/udp omirrd # online mirror +rmtcfg 1236/tcp # Gracilis Packeten remote config server +xtel 1313/tcp # french minitel +coda_opcons 1355/udp # Coda opcons (Coda fs) +coda_venus 1363/udp # Coda venus (Coda fs) +coda_auth 1357/udp # Coda auth (Coda fs) +coda_udpsrv 1359/udp # Coda udpsrv (Coda fs) +coda_filesrv 1361/udp # Coda filesrv (Coda fs) +codacon 1423/tcp venus.cmu # Coda Console (Coda fs) +coda_aux1 1431/tcp # coda auxiliary service (Coda fs) +coda_aux1 1431/udp # coda auxiliary service (Coda fs) +coda_aux2 1433/tcp # coda auxiliary service (Coda fs) +coda_aux2 1433/udp # coda auxiliary service (Coda fs) +coda_aux3 1435/tcp # coda auxiliary service (Coda fs) +coda_aux3 1435/udp # coda auxiliary service (Coda fs) +cfinger 2003/tcp # GNU Finger +afbackup 2988/tcp # Afbackup system +afbackup 2988/udp # Afbackup system +icp 3130/tcp # Internet Cache Protocol (Squid) +icp 3130/udp # Internet Cache Protocol (Squid) +postgres 5432/tcp # POSTGRES +postgres 5432/udp # POSTGRES +fax 4557/tcp # FAX transmission service (old) +hylafax 4559/tcp # HylaFAX client-server protocol (new) +noclog 5354/tcp # noclogd with TCP (nocol) +noclog 5354/udp # noclogd with UDP (nocol) +hostmon 5355/tcp # hostmon uses TCP (nocol) +hostmon 5355/udp # hostmon uses TCP (nocol) +ircd 6667/tcp # Internet Relay Chat +ircd 6667/udp # Internet Relay Chat +webcache 8080/tcp # WWW caching service +webcache 8080/udp # WWW caching service +tproxy 8081/tcp # Transparent Proxy +tproxy 8081/udp # Transparent Proxy +mandelspawn 9359/udp mandelbrot # network mandelbrot +amanda 10080/udp # amanda backup services +amandaidx 10082/tcp # amanda backup services +amidxtape 10083/tcp # amanda backup services +isdnlog 20011/tcp # isdn logging system +isdnlog 20011/udp # isdn logging system +vboxd 20012/tcp # voice box system +vboxd 20012/udp # voice box system +binkp 24554/tcp # Binkley +binkp 24554/udp # Binkley +asp 27374/tcp # Address Search Protocol +asp 27374/udp # Address Search Protocol +tfido 60177/tcp # Ifmail +tfido 60177/udp # Ifmail +fido 60179/tcp # Ifmail +fido 60179/udp # Ifmail + +# Local services + diff --git a/src/dep/dante_package/bundle/rootfs/etc/shadow b/src/dep/dante_package/bundle/rootfs/etc/shadow new file mode 100644 index 0000000..37de977 --- /dev/null +++ b/src/dep/dante_package/bundle/rootfs/etc/shadow @@ -0,0 +1,9 @@ +root:::::::: +daemon:*::::::: +bin:*::::::: +sys:*::::::: +sync:*::::::: +mail:*::::::: +www-data:*::::::: +operator:*::::::: +nobody:*::::::: diff --git a/src/dep/dante_package/bundle/rootfs/etc/ssl/certs/DEP.crt b/src/dep/dante_package/bundle/rootfs/etc/ssl/certs/DEP.crt new file mode 100644 index 0000000..04f70bf --- /dev/null +++ b/src/dep/dante_package/bundle/rootfs/etc/ssl/certs/DEP.crt @@ -0,0 +1,33 @@ +-----BEGIN CERTIFICATE----- +MIIFrzCCA5egAwIBAgIUOTWl6IoOr4Lvj0qz0qoA2W8q6SEwDQYJKoZIhvcNAQEL +BQAwUjELMAkGA1UEBhMCQVUxDDAKBgNVBAgMA05TVzERMA8GA1UECgwIQXVkaW5h +dGUxFDASBgNVBAsMC0VuZ2luZWVyaW5nMQwwCgYDVQQDDANERVAwIBcNNzAwMTAx +MDAwMDAwWhgPMjE3MDAxMDExMjAwMDBaMFIxCzAJBgNVBAYTAkFVMQwwCgYDVQQI +DANOU1cxETAPBgNVBAoMCEF1ZGluYXRlMRQwEgYDVQQLDAtFbmdpbmVlcmluZzEM +MAoGA1UEAwwDREVQMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAr2Xf +QTbmntPQQxYSceK8a8OkKCCTkX2HLmpTDDBrySdBTtvu0jJDIISEtzSUQu8UQ+8H +2atTXgc+Cseam+fDOsU3gmDgn+lmcA5o1rDLrXlQZCJrO/JUpyQ5v7kkGU33AqKc +Ik307fPwkX6YvBBy4Zc/T23bU8KXW+8beQrteImie9Pw9tt9GBvREox0/MBv23qs +3IDdPXB5qdFdenwAwIUGvG43Aohhldnp063HLc8GNySmfwuFpCPgMYph+jX/yKv+ +EMBmH9KIhYDvzuq28NJajTaJtsMXr1OlW9a6s2zehK7JYnjrqo3J7ebhb6nxz4co +uQ39DHc+Hsbfi/Bg/UpabpW5Wdl3GHOaeUNWpLheifO2OP35S3yPkTCiz8Wfu7l3 +CO67360PaBTmq7tjen2H6pFLWsGLsjcjnUtp0sf50LAhpFHvsBVKS7MkkXwNdncg +X+kGTDCpYNeKdCU6s71Z7RKXjArO1RtRxVU1N4l5U3JOYQ7jChLGwoFDfpBRMVh+ +gNNi9/lLNU++gRFmF+1i//bI44Z+cviHkfGq+dIpWBO1KSkGtAE9hWmq0qNdWS1V +b60LU9h4IMg23XYmHpJEyQoKTz4EIJgVjor9ErHws9Ig5vG4FGyXnIjPC9N7PgN1 +iDIrf8FgaOSRQPcBd3VjguW4Y8qFts1opQCKFbkCAwEAAaN7MHkwCQYDVR0TBAIw +ADAsBglghkgBhvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUw +HQYDVR0OBBYEFNG9TtgJ0D6ukZf/M4ZyEAW+i+95MB8GA1UdIwQYMBaAFNG9TtgJ +0D6ukZf/M4ZyEAW+i+95MA0GCSqGSIb3DQEBCwUAA4ICAQBBmzlYv9E7W5E0Lv0X +wszsgvbfavB83vvhYkDiPFIUt+6s6b97TNtKhfZZWbJprL4Gt262Xt1t4pZY8csU +co8qpEop4uA943A2z+3Fwc5OuYH+TMlvYVhRLnCFtgeu89VaXEPAFd+d1652nfta +l1y9Gj8NsPdsM0xwDzChRqyAR9heXil1ZhLVzfJr3ri0jdK9ZEIfsS8GOgSmLm+5 +LO9UXu73Sqq4qWpp54T7AGEJz7DqIHQSY1c/beuFn7W8Ox9K/9MPoq3mQnn24z/1 +rS3chxriKTe3hcslKs0I5HE7HF9SSG9sW6GI5TUcWfCAjIn74WK5/q6XBhh+Io1p +QnsokMzgvNIk/Eit+9P38uNkU1TNsL+wMgV2/qNcRHX5gSfr3yhCN/tg8zCcBkef +Ek5dmdxfSY8wq273W6rKKJbGB3Eb9R9gpYnHWAgiH/qJ0epnT20ynakRUpj9ZSza +cuEZNbcXBUQPCUkyDUOpW7h4budXIMlIbvMtiL8pna8XQM309K9CNZfV16QGuK1P +KNjdiwvl7UVtEUiTCAwt+rq0N3tRuq5ceK9YrnWiCBCXTMIa9o3sv6IgByXvOCkv +uNa8PJr1O8uPtbQqaMsBC3nnG2wpSsEjaurYy5SOSUVvXxVnb3L93FuADMgn1i5Y +UtfGCCL4MCkNn9APcgzpRSqUFA== +-----END CERTIFICATE----- diff --git a/src/dep/dante_package/bundle/rootfs/etc/ssl/certs/ca-certificates.crt b/src/dep/dante_package/bundle/rootfs/etc/ssl/certs/ca-certificates.crt new file mode 100644 index 0000000..6317265 --- /dev/null +++ b/src/dep/dante_package/bundle/rootfs/etc/ssl/certs/ca-certificates.crt @@ -0,0 +1,3408 @@ +-----BEGIN CERTIFICATE----- +MIIH0zCCBbugAwIBAgIIXsO3pkN/pOAwDQYJKoZIhvcNAQEFBQAwQjESMBAGA1UE +AwwJQUNDVlJBSVoxMRAwDgYDVQQLDAdQS0lBQ0NWMQ0wCwYDVQQKDARBQ0NWMQsw +CQYDVQQGEwJFUzAeFw0xMTA1MDUwOTM3MzdaFw0zMDEyMzEwOTM3MzdaMEIxEjAQ +BgNVBAMMCUFDQ1ZSQUlaMTEQMA4GA1UECwwHUEtJQUNDVjENMAsGA1UECgwEQUND +VjELMAkGA1UEBhMCRVMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCb +qau/YUqXry+XZpp0X9DZlv3P4uRm7x8fRzPCRKPfmt4ftVTdFXxpNRFvu8gMjmoY +HtiP2Ra8EEg2XPBjs5BaXCQ316PWywlxufEBcoSwfdtNgM3802/J+Nq2DoLSRYWo +G2ioPej0RGy9ocLLA76MPhMAhN9KSMDjIgro6TenGEyxCQ0jVn8ETdkXhBilyNpA +lHPrzg5XPAOBOp0KoVdDaaxXbXmQeOW1tDvYvEyNKKGno6e6Ak4l0Squ7a4DIrhr +IA8wKFSVf+DuzgpmndFALW4ir50awQUZ0m/A8p/4e7MCQvtQqR0tkw8jq8bBD5L/ +0KIV9VMJcRz/RROE5iZe+OCIHAr8Fraocwa48GOEAqDGWuzndN9wrqODJerWx5eH +k6fGioozl2A3ED6XPm4pFdahD9GILBKfb6qkxkLrQaLjlUPTAYVtjrs78yM2x/47 +4KElB0iryYl0/wiPgL/AlmXz7uxLaL2diMMxs0Dx6M/2OLuc5NF/1OVYm3z61PMO +m3WR5LpSLhl+0fXNWhn8ugb2+1KoS5kE3fj5tItQo05iifCHJPqDQsGH+tUtKSpa +cXpkatcnYGMN285J9Y0fkIkyF/hzQ7jSWpOGYdbhdQrqeWZ2iE9x6wQl1gpaepPl +uUsXQA+xtrn13k/c4LOsOxFwYIRKQ26ZIMApcQrAZQIDAQABo4ICyzCCAscwfQYI +KwYBBQUHAQEEcTBvMEwGCCsGAQUFBzAChkBodHRwOi8vd3d3LmFjY3YuZXMvZmls +ZWFkbWluL0FyY2hpdm9zL2NlcnRpZmljYWRvcy9yYWl6YWNjdjEuY3J0MB8GCCsG +AQUFBzABhhNodHRwOi8vb2NzcC5hY2N2LmVzMB0GA1UdDgQWBBTSh7Tj3zcnk1X2 +VuqB5TbMjB4/vTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNKHtOPfNyeT +VfZW6oHlNsyMHj+9MIIBcwYDVR0gBIIBajCCAWYwggFiBgRVHSAAMIIBWDCCASIG +CCsGAQUFBwICMIIBFB6CARAAQQB1AHQAbwByAGkAZABhAGQAIABkAGUAIABDAGUA +cgB0AGkAZgBpAGMAYQBjAGkA8wBuACAAUgBhAO0AegAgAGQAZQAgAGwAYQAgAEEA +QwBDAFYAIAAoAEEAZwBlAG4AYwBpAGEAIABkAGUAIABUAGUAYwBuAG8AbABvAGcA +7QBhACAAeQAgAEMAZQByAHQAaQBmAGkAYwBhAGMAaQDzAG4AIABFAGwAZQBjAHQA +cgDzAG4AaQBjAGEALAAgAEMASQBGACAAUQA0ADYAMAAxADEANQA2AEUAKQAuACAA +QwBQAFMAIABlAG4AIABoAHQAdABwADoALwAvAHcAdwB3AC4AYQBjAGMAdgAuAGUA +czAwBggrBgEFBQcCARYkaHR0cDovL3d3dy5hY2N2LmVzL2xlZ2lzbGFjaW9uX2Mu +aHRtMFUGA1UdHwROMEwwSqBIoEaGRGh0dHA6Ly93d3cuYWNjdi5lcy9maWxlYWRt +aW4vQXJjaGl2b3MvY2VydGlmaWNhZG9zL3JhaXphY2N2MV9kZXIuY3JsMA4GA1Ud +DwEB/wQEAwIBBjAXBgNVHREEEDAOgQxhY2N2QGFjY3YuZXMwDQYJKoZIhvcNAQEF +BQADggIBAJcxAp/n/UNnSEQU5CmH7UwoZtCPNdpNYbdKl02125DgBS4OxnnQ8pdp +D70ER9m+27Up2pvZrqmZ1dM8MJP1jaGo/AaNRPTKFpV8M9xii6g3+CfYCS0b78gU +JyCpZET/LtZ1qmxNYEAZSUNUY9rizLpm5U9EelvZaoErQNV/+QEnWCzI7UiRfD+m +AM/EKXMRNt6GGT6d7hmKG9Ww7Y49nCrADdg9ZuM8Db3VlFzi4qc1GwQA9j9ajepD +vV+JHanBsMyZ4k0ACtrJJ1vnE5Bc5PUzolVt3OAJTS+xJlsndQAJxGJ3KQhfnlms +tn6tn1QwIgPBHnFk/vk4CpYY3QIUrCPLBhwepH2NDd4nQeit2hW3sCPdK6jT2iWH +7ehVRE2I9DZ+hJp4rPcOVkkO1jMl1oRQQmwgEh0q1b688nCBpHBgvgW1m54ERL5h +I6zppSSMEYCUWqKiuUnSwdzRp+0xESyeGabu4VXhwOrPDYTkF7eifKXeVSUG7szA +h1xA2syVP1XgNce4hL60Xc16gwFy7ofmXx2utYXGJt/mwZrpHgJHnyqobalbz+xF +d3+YJ5oyXSrjhO7FmGYvliAd3djDJ9ew+f7Zfc3Qn48LFFhRny+Lwzgt3uiP1o2H +pPVWQxaZLPSkVrQ0uGE3ycJYgBugl6H8WY3pEfbRD0tVNEYqi4Y7 +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFgzCCA2ugAwIBAgIPXZONMGc2yAYdGsdUhGkHMA0GCSqGSIb3DQEBCwUAMDsx +CzAJBgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJ +WiBGTk1ULVJDTTAeFw0wODEwMjkxNTU5NTZaFw0zMDAxMDEwMDAwMDBaMDsxCzAJ +BgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJWiBG +Tk1ULVJDTTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALpxgHpMhm5/ +yBNtwMZ9HACXjywMI7sQmkCpGreHiPibVmr75nuOi5KOpyVdWRHbNi63URcfqQgf +BBckWKo3Shjf5TnUV/3XwSyRAZHiItQDwFj8d0fsjz50Q7qsNI1NOHZnjrDIbzAz +WHFctPVrbtQBULgTfmxKo0nRIBnuvMApGGWn3v7v3QqQIecaZ5JCEJhfTzC8PhxF +tBDXaEAUwED653cXeuYLj2VbPNmaUtu1vZ5Gzz3rkQUCwJaydkxNEJY7kvqcfw+Z +374jNUUeAlz+taibmSXaXvMiwzn15Cou08YfxGyqxRxqAQVKL9LFwag0Jl1mpdIC +IfkYtwb1TplvqKtMUejPUBjFd8g5CSxJkjKZqLsXF3mwWsXmo8RZZUc1g16p6DUL +mbvkzSDGm0oGObVo/CK67lWMK07q87Hj/LaZmtVC+nFNCM+HHmpxffnTtOmlcYF7 +wk5HlqX2doWjKI/pgG6BU6VtX7hI+cL5NqYuSf+4lsKMB7ObiFj86xsc3i1w4peS +MKGJ47xVqCfWS+2QrYv6YyVZLag13cqXM7zlzced0ezvXg5KkAYmY6252TUtB7p2 +ZSysV4999AeU14ECll2jB0nVetBX+RvnU0Z1qrB5QstocQjpYL05ac70r8NWQMet +UqIJ5G+GR4of6ygnXYMgrwTJbFaai0b1AgMBAAGjgYMwgYAwDwYDVR0TAQH/BAUw +AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFPd9xf3E6Jobd2Sn9R2gzL+H +YJptMD4GA1UdIAQ3MDUwMwYEVR0gADArMCkGCCsGAQUFBwIBFh1odHRwOi8vd3d3 +LmNlcnQuZm5tdC5lcy9kcGNzLzANBgkqhkiG9w0BAQsFAAOCAgEAB5BK3/MjTvDD +nFFlm5wioooMhfNzKWtN/gHiqQxjAb8EZ6WdmF/9ARP67Jpi6Yb+tmLSbkyU+8B1 +RXxlDPiyN8+sD8+Nb/kZ94/sHvJwnvDKuO+3/3Y3dlv2bojzr2IyIpMNOmqOFGYM +LVN0V2Ue1bLdI4E7pWYjJ2cJj+F3qkPNZVEI7VFY/uY5+ctHhKQV8Xa7pO6kO8Rf +77IzlhEYt8llvhjho6Tc+hj507wTmzl6NLrTQfv6MooqtyuGC2mDOL7Nii4LcK2N +JpLuHvUBKwrZ1pebbuCoGRw6IYsMHkCtA+fdZn71uSANA+iW+YJF1DngoABd15jm +fZ5nc8OaKveri6E6FO80vFIOiZiaBECEHX5FaZNXzuvO+FB8TxxuBEOb+dY7Ixjp +6o7RTUaN8Tvkasq6+yO3m/qZASlaWFot4/nUbQ4mrcFuNLwy+AwF+mWj2zs3gyLp +1txyM/1d8iC9djwj2ij3+RvrWWTV3F9yfiD8zYm1kGdNYno/Tq0dwzn+evQoFt9B +9kiABdcPUXmsEKvU7ANm5mqwujGSQkBqvjrTcuFqN1W8rB2Vt2lh8kORdOag0wok +RqEIr9baRRmW1FMdW4R58MD3R++Lj8UGrp1MYp3/RgT408m2ECVAdf4WqslKYIYv +uu8wd+RU4riEmViAqhOLUTpPSPaLtrM= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFuzCCA6OgAwIBAgIIVwoRl0LE48wwDQYJKoZIhvcNAQELBQAwazELMAkGA1UE +BhMCSVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8w +MzM1ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290 +IENBMB4XDTExMDkyMjExMjIwMloXDTMwMDkyMjExMjIwMlowazELMAkGA1UEBhMC +SVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8wMzM1 +ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290IENB +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAp8bEpSmkLO/lGMWwUKNv +UTufClrJwkg4CsIcoBh/kbWHuUA/3R1oHwiD1S0eiKD4j1aPbZkCkpAW1V8IbInX +4ay8IMKx4INRimlNAJZaby/ARH6jDuSRzVju3PvHHkVH3Se5CAGfpiEd9UEtL0z9 +KK3giq0itFZljoZUj5NDKd45RnijMCO6zfB9E1fAXdKDa0hMxKufgFpbOr3JpyI/ +gCczWw63igxdBzcIy2zSekciRDXFzMwujt0q7bd9Zg1fYVEiVRvjRuPjPdA1Yprb +rxTIW6HMiRvhMCb8oJsfgadHHwTrozmSBp+Z07/T6k9QnBn+locePGX2oxgkg4YQ +51Q+qDp2JE+BIcXjDwL4k5RHILv+1A7TaLndxHqEguNTVHnd25zS8gebLra8Pu2F +be8lEfKXGkJh90qX6IuxEAf6ZYGyojnP9zz/GPvG8VqLWeICrHuS0E4UT1lF9gxe +KF+w6D9Fz8+vm2/7hNN3WpVvrJSEnu68wEqPSpP4RCHiMUVhUE4Q2OM1fEwZtN4F +v6MGn8i1zeQf1xcGDXqVdFUNaBr8EBtiZJ1t4JWgw5QHVw0U5r0F+7if5t+L4sbn +fpb2U8WANFAoWPASUHEXMLrmeGO89LKtmyuy/uE5jF66CyCU3nuDuP/jVo23Eek7 +jPKxwV2dpAtMK9myGPW1n0sCAwEAAaNjMGEwHQYDVR0OBBYEFFLYiDrIn3hm7Ynz +ezhwlMkCAjbQMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUUtiIOsifeGbt +ifN7OHCUyQICNtAwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQAL +e3KHwGCmSUyIWOYdiPcUZEim2FgKDk8TNd81HdTtBjHIgT5q1d07GjLukD0R0i70 +jsNjLiNmsGe+b7bAEzlgqqI0JZN1Ut6nna0Oh4lScWoWPBkdg/iaKWW+9D+a2fDz +WochcYBNy+A4mz+7+uAwTc+G02UQGRjRlwKxK3JCaKygvU5a2hi/a5iB0P2avl4V +SM0RFbnAKVy06Ij3Pjaut2L9HmLecHgQHEhb2rykOLpn7VU+Xlff1ANATIGk0k9j +pwlCCRT8AKnCgHNPLsBA2RF7SOp6AsDT6ygBJlh0wcBzIm2Tlf05fbsq4/aC4yyX +X04fkZT6/iyj2HYauE2yOE+b+h1IYHkm4vP9qdCa6HCPSXrW5b0KDtst842/6+Ok +fcvHlXHo2qN8xcL4dJIEG4aspCJTQLas/kx2z/uUMsA1n3Y/buWQbqCmJqK4LL7R +K4X9p2jIugErsWx0Hbhzlefut8cl8ABMALJ+tguLHPPAUJ4lueAI3jZm/zel0btU +ZCzJJ7VLkn5l/9Mt4blOvH+kQSGQQXemOR/qnuOf0GZvBeyqdn6/axag67XH/JJU +LysRJyU3eExRarDzzFhdFPFqSBX/wge2sY0PjlxQRrM9vwGYT7JZVEc+NHt4bVaT +LnPqZih4zR0Uv6CPLy64Lo7yFIrM6bV8+2ydDKXhlg== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEU +MBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFs +IFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290 +MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFowbzELMAkGA1UEBhMCU0Ux +FDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRUcnVzdCBFeHRlcm5h +bCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0EgUm9v +dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvt +H7xsD821+iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9 +uMq/NzgtHj6RQa1wVsfwTz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzX +mk6vBbOmcZSccbNQYArHE504B4YCqOmoaSYYkKtMsE8jqzpPhNjfzp/haW+710LX +a0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy2xSoRcRdKn23tNbE7qzN +E0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv77+ldU9U0 +WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYD +VR0PBAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0 +Jvf6xCZU7wO94CTLVBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRU +cnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsx +IjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENBIFJvb3SCAQEwDQYJKoZIhvcN +AQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZlj7DYd7usQWxH +YINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5 +6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvC +Nr4TDea9Y355e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEX +c4g/VhsxOBi0cQ+azcgOno4uG+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5a +mnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDTDCCAjSgAwIBAgIId3cGJyapsXwwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UE +BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVz +dCBDb21tZXJjaWFsMB4XDTEwMDEyOTE0MDYwNloXDTMwMTIzMTE0MDYwNlowRDEL +MAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZp +cm1UcnVzdCBDb21tZXJjaWFsMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEA9htPZwcroRX1BiLLHwGy43NFBkRJLLtJJRTWzsO3qyxPxkEylFf6EqdbDuKP +Hx6GGaeqtS25Xw2Kwq+FNXkyLbscYjfysVtKPcrNcV/pQr6U6Mje+SJIZMblq8Yr +ba0F8PrVC8+a5fBQpIs7R6UjW3p6+DM/uO+Zl+MgwdYoic+U+7lF7eNAFxHUdPAL +MeIrJmqbTFeurCA+ukV6BfO9m2kVrn1OIGPENXY6BwLJN/3HR+7o8XYdcxXyl6S1 +yHp52UKqK39c/s4mT6NmgTWvRLpUHhwwMmWd5jyTXlBOeuM61G7MGvv50jeuJCqr +VwMiKA1JdX+3KNp1v47j3A55MQIDAQABo0IwQDAdBgNVHQ4EFgQUnZPGU4teyq8/ +nx4P5ZmVvCT2lI8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJ +KoZIhvcNAQELBQADggEBAFis9AQOzcAN/wr91LoWXym9e2iZWEnStB03TX8nfUYG +XUPGhi4+c7ImfU+TqbbEKpqrIZcUsd6M06uJFdhrJNTxFq7YpFzUf1GO7RgBsZNj +vbz4YYCanrHOQnDiqX0GJX0nof5v7LMeJNrjS1UaADs1tDvZ110w/YETifLCBivt +Z8SOyUOyXGsViQK8YvxO8rUzqrJv0wqiUOP2O+guRMLbZjipM1ZI8W0bM40NjD9g +N53Tym1+NH4Nn3J2ixufcv1SNUFFApYvHLKac0khsUlHRUe072o0EclNmsxZt9YC +nlpOZbWUrhvfKbAW8b8Angc6F2S1BLUjIZkKlTuXfO8= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDTDCCAjSgAwIBAgIIfE8EORzUmS0wDQYJKoZIhvcNAQEFBQAwRDELMAkGA1UE +BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVz +dCBOZXR3b3JraW5nMB4XDTEwMDEyOTE0MDgyNFoXDTMwMTIzMTE0MDgyNFowRDEL +MAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZp +cm1UcnVzdCBOZXR3b3JraW5nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEAtITMMxcua5Rsa2FSoOujz3mUTOWUgJnLVWREZY9nZOIG41w3SfYvm4SEHi3y +YJ0wTsyEheIszx6e/jarM3c1RNg1lho9Nuh6DtjVR6FqaYvZ/Ls6rnla1fTWcbua +kCNrmreIdIcMHl+5ni36q1Mr3Lt2PpNMCAiMHqIjHNRqrSK6mQEubWXLviRmVSRL +QESxG9fhwoXA3hA/Pe24/PHxI1Pcv2WXb9n5QHGNfb2V1M6+oF4nI979ptAmDgAp +6zxG8D1gvz9Q0twmQVGeFDdCBKNwV6gbh+0t+nvujArjqWaJGctB+d1ENmHP4ndG +yH329JKBNv3bNPFyfvMMFr20FQIDAQABo0IwQDAdBgNVHQ4EFgQUBx/S55zawm6i +QLSwelAQUHTEyL0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJ +KoZIhvcNAQEFBQADggEBAIlXshZ6qML91tmbmzTCnLQyFE2npN/svqe++EPbkTfO +tDIuUFUaNU52Q3Eg75N3ThVwLofDwR1t3Mu1J9QsVtFSUzpE0nPIxBsFZVpikpzu +QY0x2+c06lkh1QF612S4ZDnNye2v7UsDSKegmQGA3GWjNq5lWUhPgkvIZfFXHeVZ +Lgo/bNjR9eUJtGxUAArgFU2HdW23WJZa3W3SAKD0m0i+wzekujbgfIeFlxoVot4u +olu9rxj5kFDNcFn4J2dHy8egBzp90SxdbBk6ZrV9/ZFvgrG+CJPbFEfxojfHRZ48 +x3evZKiT3/Zpg4Jg8klCNO1aAFSFHBY2kgxc+qatv9s= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFRjCCAy6gAwIBAgIIbYwURrGmCu4wDQYJKoZIhvcNAQEMBQAwQTELMAkGA1UE +BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVz +dCBQcmVtaXVtMB4XDTEwMDEyOTE0MTAzNloXDTQwMTIzMTE0MTAzNlowQTELMAkG +A1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1U +cnVzdCBQcmVtaXVtMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxBLf +qV/+Qd3d9Z+K4/as4Tx4mrzY8H96oDMq3I0gW64tb+eT2TZwamjPjlGjhVtnBKAQ +JG9dKILBl1fYSCkTtuG+kU3fhQxTGJoeJKJPj/CihQvL9Cl/0qRY7iZNyaqoe5rZ ++jjeRFcV5fiMyNlI4g0WJx0eyIOFJbe6qlVBzAMiSy2RjYvmia9mx+n/K+k8rNrS +s8PhaJyJ+HoAVt70VZVs+7pk3WKL3wt3MutizCaam7uqYoNMtAZ6MMgpv+0GTZe5 +HMQxK9VfvFMSF5yZVylmd2EhMQcuJUmdGPLu8ytxjLW6OQdJd/zvLpKQBY0tL3d7 +70O/Nbua2Plzpyzy0FfuKE4mX4+QaAkvuPjcBukumj5Rp9EixAqnOEhss/n/fauG +V+O61oV4d7pD6kh/9ti+I20ev9E2bFhc8e6kGVQa9QPSdubhjL08s9NIS+LI+H+S +qHZGnEJlPqQewQcDWkYtuJfzt9WyVSHvutxMAJf7FJUnM7/oQ0dG0giZFmA7mn7S +5u046uwBHjxIVkkJx0w3AJ6IDsBz4W9m6XJHMD4Q5QsDyZpCAGzFlH5hxIrff4Ia +C1nEWTJ3s7xgaVY5/bQGeyzWZDbZvUjthB9+pSKPKrhC9IK31FOQeE4tGv2Bb0TX +OwF0lkLgAOIua+rF7nKsu7/+6qqo+Nz2snmKtmcCAwEAAaNCMEAwHQYDVR0OBBYE +FJ3AZ6YMItkm9UWrpmVSESfYRaxjMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/ +BAQDAgEGMA0GCSqGSIb3DQEBDAUAA4ICAQCzV00QYk465KzquByvMiPIs0laUZx2 +KI15qldGF9X1Uva3ROgIRL8YhNILgM3FEv0AVQVhh0HctSSePMTYyPtwni94loMg +Nt58D2kTiKV1NpgIpsbfrM7jWNa3Pt668+s0QNiigfV4Py/VpfzZotReBA4Xrf5B +8OWycvpEgjNC6C1Y91aMYj+6QrCcDFx+LmUmXFNPALJ4fqENmS2NuB2OosSw/WDQ +MKSOyARiqcTtNd56l+0OOF6SL5Nwpamcb6d9Ex1+xghIsV5n61EIJenmJWtSKZGc +0jlzCFfemQa0W50QBuHCAKi4HEoCChTQwUHK+4w1IX2COPKpVJEZNZOUbWo6xbLQ +u4mGk+ibyQ86p3q4ofB4Rvr8Ny/lioTz3/4E2aFooC8k4gmVBtWVyuEklut89pMF +u+1z6S3RdTnX5yTb2E5fQ4+e0BQ5v1VwSJlXMbSc7kqYA5YwH2AG7hsj/oFgIxpH +YoWlzBk0gG+zrBrjn/B7SK3VAdlntqlyk+otZrWyuOQ9PLLvTIzq6we/qzWaVYa8 +GKa1qF60g2xraUDTn9zxw2lrueFtCfTxqlB2Cnp9ehehVZZCmTEJ3WARjQUwfuaO +RtGdFNrHF+QFlozEJLUbzxQHskD4o55BhrwE0GuWyCqANP2/7waj3VjFhT0+j/6e +KeC2uAloGRwYQw== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIB/jCCAYWgAwIBAgIIdJclisc/elQwCgYIKoZIzj0EAwMwRTELMAkGA1UEBhMC +VVMxFDASBgNVBAoMC0FmZmlybVRydXN0MSAwHgYDVQQDDBdBZmZpcm1UcnVzdCBQ +cmVtaXVtIEVDQzAeFw0xMDAxMjkxNDIwMjRaFw00MDEyMzExNDIwMjRaMEUxCzAJ +BgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1UcnVzdDEgMB4GA1UEAwwXQWZmaXJt +VHJ1c3QgUHJlbWl1bSBFQ0MwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQNMF4bFZ0D +0KF5Nbc6PJJ6yhUczWLznCZcBz3lVPqj1swS6vQUX+iOGasvLkjmrBhDeKzQN8O9 +ss0s5kfiGuZjuD0uL3jET9v0D6RoTFVya5UdThhClXjMNzyR4ptlKymjQjBAMB0G +A1UdDgQWBBSaryl6wBE1NSZRMADDav5A1a7WPDAPBgNVHRMBAf8EBTADAQH/MA4G +A1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNnADBkAjAXCfOHiFBar8jAQr9HX/Vs +aobgxCd05DhT1wV/GzTjxi+zygk8N53X57hG8f2h4nECMEJZh0PUUd+60wkyWs6I +flc9nF9Ca/UHLbXwgpP5WW+uZPpY5Yse42O+tYHNbwKMeQ== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF +ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6 +b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL +MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv +b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj +ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM +9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw +IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6 +VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L +93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm +jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA +A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI +U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs +N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv +o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU +5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy +rqXRfboQnoZsG4q5WTP468SQvvG5 +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFQTCCAymgAwIBAgITBmyf0pY1hp8KD+WGePhbJruKNzANBgkqhkiG9w0BAQwF +ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6 +b24gUm9vdCBDQSAyMB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTEL +MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv +b3QgQ0EgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK2Wny2cSkxK +gXlRmeyKy2tgURO8TW0G/LAIjd0ZEGrHJgw12MBvIITplLGbhQPDW9tK6Mj4kHbZ +W0/jTOgGNk3Mmqw9DJArktQGGWCsN0R5hYGCrVo34A3MnaZMUnbqQ523BNFQ9lXg +1dKmSYXpN+nKfq5clU1Imj+uIFptiJXZNLhSGkOQsL9sBbm2eLfq0OQ6PBJTYv9K +8nu+NQWpEjTj82R0Yiw9AElaKP4yRLuH3WUnAnE72kr3H9rN9yFVkE8P7K6C4Z9r +2UXTu/Bfh+08LDmG2j/e7HJV63mjrdvdfLC6HM783k81ds8P+HgfajZRRidhW+me +z/CiVX18JYpvL7TFz4QuK/0NURBs+18bvBt+xa47mAExkv8LV/SasrlX6avvDXbR +8O70zoan4G7ptGmh32n2M8ZpLpcTnqWHsFcQgTfJU7O7f/aS0ZzQGPSSbtqDT6Zj +mUyl+17vIWR6IF9sZIUVyzfpYgwLKhbcAS4y2j5L9Z469hdAlO+ekQiG+r5jqFoz +7Mt0Q5X5bGlSNscpb/xVA1wf+5+9R+vnSUeVC06JIglJ4PVhHvG/LopyboBZ/1c6 ++XUyo05f7O0oYtlNc/LMgRdg7c3r3NunysV+Ar3yVAhU/bQtCSwXVEqY0VThUWcI +0u1ufm8/0i2BWSlmy5A5lREedCf+3euvAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMB +Af8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSwDPBMMPQFWAJI/TPlUq9LhONm +UjANBgkqhkiG9w0BAQwFAAOCAgEAqqiAjw54o+Ci1M3m9Zh6O+oAA7CXDpO8Wqj2 +LIxyh6mx/H9z/WNxeKWHWc8w4Q0QshNabYL1auaAn6AFC2jkR2vHat+2/XcycuUY ++gn0oJMsXdKMdYV2ZZAMA3m3MSNjrXiDCYZohMr/+c8mmpJ5581LxedhpxfL86kS +k5Nrp+gvU5LEYFiwzAJRGFuFjWJZY7attN6a+yb3ACfAXVU3dJnJUH/jWS5E4ywl +7uxMMne0nxrpS10gxdr9HIcWxkPo1LsmmkVwXqkLN1PiRnsn/eBG8om3zEK2yygm +btmlyTrIQRNg91CMFa6ybRoVGld45pIq2WWQgj9sAq+uEjonljYE1x2igGOpm/Hl +urR8FLBOybEfdF849lHqm/osohHUqS0nGkWxr7JOcQ3AWEbWaQbLU8uz/mtBzUF+ +fUwPfHJ5elnNXkoOrJupmHN5fLT0zLm4BwyydFy4x2+IoZCn9Kr5v2c69BoVYh63 +n749sSmvZ6ES8lgQGVMDMBu4Gon2nL2XA46jCfMdiyHxtN/kHNGfZQIG6lzWE7OE +76KlXIx3KadowGuuQNKotOrN8I1LOJwZmhsoVLiJkO/KdYE+HvJkJMcYr07/R54H +9jVlpNMKVv/1F2Rs76giJUmTtt8AF9pYfl3uxRuw0dFfIRDH+fO6AgonB8Xx1sfT +4PsJYGw= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIBtjCCAVugAwIBAgITBmyf1XSXNmY/Owua2eiedgPySjAKBggqhkjOPQQDAjA5 +MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g +Um9vdCBDQSAzMB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG +A1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg +Q0EgMzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCmXp8ZBf8ANm+gBG1bG8lKl +ui2yEujSLtf6ycXYqm0fc4E7O5hrOXwzpcVOho6AF2hiRVd9RFgdszflZwjrZt6j +QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSr +ttvXBp43rDCGB5Fwx5zEGbF4wDAKBggqhkjOPQQDAgNJADBGAiEA4IWSoxe3jfkr +BqWTrBqYaGFy+uGh0PsceGCmQ5nFuMQCIQCcAu/xlJyzlvnrxir4tiz+OpAUFteM +YyRIHN8wfdVoOw== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIB8jCCAXigAwIBAgITBmyf18G7EEwpQ+Vxe3ssyBrBDjAKBggqhkjOPQQDAzA5 +MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g +Um9vdCBDQSA0MB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG +A1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg +Q0EgNDB2MBAGByqGSM49AgEGBSuBBAAiA2IABNKrijdPo1MN/sGKe0uoe0ZLY7Bi +9i0b2whxIdIA6GO9mif78DluXeo9pcmBqqNbIJhFXRbb/egQbeOc4OO9X4Ri83Bk +M6DLJC9wuoihKqB1+IGuYgbEgds5bimwHvouXKNCMEAwDwYDVR0TAQH/BAUwAwEB +/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFNPsxzplbszh2naaVvuc84ZtV+WB +MAoGCCqGSM49BAMDA2gAMGUCMDqLIfG9fhGt0O9Yli/W651+kI0rz2ZVwyzjKKlw +CkcO8DdZEv8tmZQoTipPNU0zWgIxAOp1AE47xDqUEpHJWEadIRNyp4iciuRMStuW +1KyLa2tJElMzrdfkviT8tQp21KW8EA== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIIXDPLYixfszIwDQYJKoZIhvcNAQELBQAwPDEeMBwGA1UE +AwwVQXRvcyBUcnVzdGVkUm9vdCAyMDExMQ0wCwYDVQQKDARBdG9zMQswCQYDVQQG +EwJERTAeFw0xMTA3MDcxNDU4MzBaFw0zMDEyMzEyMzU5NTlaMDwxHjAcBgNVBAMM +FUF0b3MgVHJ1c3RlZFJvb3QgMjAxMTENMAsGA1UECgwEQXRvczELMAkGA1UEBhMC +REUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCVhTuXbyo7LjvPpvMp +Nb7PGKw+qtn4TaA+Gke5vJrf8v7MPkfoepbCJI419KkM/IL9bcFyYie96mvr54rM +VD6QUM+A1JX76LWC1BTFtqlVJVfbsVD2sGBkWXppzwO3bw2+yj5vdHLqqjAqc2K+ +SZFhyBH+DgMq92og3AIVDV4VavzjgsG1xZ1kCWyjWZgHJ8cblithdHFsQ/H3NYkQ +4J7sVaE3IqKHBAUsR320HLliKWYoyrfhk/WklAOZuXCFteZI6o1Q/NnezG8HDt0L +cp2AMBYHlT8oDv3FdU9T1nSatCQujgKRz3bFmx5VdJx4IbHwLfELn8LVlhgf8FQi +eowHAgMBAAGjfTB7MB0GA1UdDgQWBBSnpQaxLKYJYO7Rl+lwrrw7GWzbITAPBgNV +HRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKelBrEspglg7tGX6XCuvDsZbNshMBgG +A1UdIAQRMA8wDQYLKwYBBAGwLQMEAQEwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3 +DQEBCwUAA4IBAQAmdzTblEiGKkGdLD4GkGDEjKwLVLgfuXvTBznk+j57sj1O7Z8j +vZfza1zv7v1Apt+hk6EKhqzvINB5Ab149xnYJDE0BAGmuhWawyfc2E8PzBhj/5kP +DpFrdRbhIfzYJsdHt6bPWHJxfrrhTZVHO8mvbaG0weyJ9rQPOLXiZNwlz6bb65pc +maHFCN795trV1lpFDMS3wrUU77QR/w4VtfX128a961qn8FYiqTxlVMYVqL2Gns2D +lmh6cYGJ4Qvh6hEbaAjMaZ7snkGeRDImeuKHCnE96+RapNLbxc3G3mB/ufNPRJLv +KrcYPqcZ2Qt9sTdBQrC6YB3y/gkRsPCHe6ed +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIGFDCCA/ygAwIBAgIIU+w77vuySF8wDQYJKoZIhvcNAQEFBQAwUTELMAkGA1UE +BhMCRVMxQjBABgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1h +cHJvZmVzaW9uYWwgQ0lGIEE2MjYzNDA2ODAeFw0wOTA1MjAwODM4MTVaFw0zMDEy +MzEwODM4MTVaMFExCzAJBgNVBAYTAkVTMUIwQAYDVQQDDDlBdXRvcmlkYWQgZGUg +Q2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBBNjI2MzQwNjgwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDDUtd9 +thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQM +cas9UX4PB99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefG +L9ItWY16Ck6WaVICqjaY7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15i +NA9wBj4gGFrO93IbJWyTdBSTo3OxDqqHECNZXyAFGUftaI6SEspd/NYrspI8IM/h +X68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyIplD9amML9ZMWGxmPsu2b +m8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctXMbScyJCy +Z/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirja +EbsXLZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/T +KI8xWVvTyQKmtFLKbpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF +6NkBiDkal4ZkQdU7hwxu+g/GvUgUvzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVh +OSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMBIGA1UdEwEB/wQIMAYBAf8CAQEwDgYD +VR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRlzeurNR4APn7VdMActHNHDhpkLzCBpgYD +VR0gBIGeMIGbMIGYBgRVHSAAMIGPMC8GCCsGAQUFBwIBFiNodHRwOi8vd3d3LmZp +cm1hcHJvZmVzaW9uYWwuY29tL2NwczBcBggrBgEFBQcCAjBQHk4AUABhAHMAZQBv +ACAAZABlACAAbABhACAAQgBvAG4AYQBuAG8AdgBhACAANAA3ACAAQgBhAHIAYwBl +AGwAbwBuAGEAIAAwADgAMAAxADcwDQYJKoZIhvcNAQEFBQADggIBABd9oPm03cXF +661LJLWhAqvdpYhKsg9VSytXjDvlMd3+xDLx51tkljYyGOylMnfX40S2wBEqgLk9 +am58m9Ot/MPWo+ZkKXzR4Tgegiv/J2Wv+xYVxC5xhOW1//qkR71kMrv2JYSiJ0L1 +ILDCExARzRAVukKQKtJE4ZYm6zFIEv0q2skGz3QeqUvVhyj5eTSSPi5E6PaPT481 +PyWzOdxjKpBrIF/EUhJOlywqrJ2X3kjyo2bbwtKDlaZmp54lD+kLM5FlClrD2VQS +3a/DTg4fJl4N3LON7NWBcN7STyQF82xO9UxJZo3R/9ILJUFI/lGExkKvgATP0H5k +SeTy36LssUzAKh3ntLFlosS88Zj0qnAHY7S42jtM+kAiMFsRpvAFDsYCA0irhpuF +3dvd6qJ2gHN99ZwExEWN57kci57q13XRcrHedUTnQn3iV2t93Jm8PYMo6oCTjcVM +ZcFwgbg4/EMxsvYDNEeyrPsiBsse3RdHHF9mudMaotoRsaS8I8nkvof/uZS2+F0g +StRf571oe2XyFR7SOqkt6dhrJKyXWERHrVkY8SFlcN7ONGCoQPHzPKTDKCOM/icz +Q0CgFzzr6juwcqajuUpLXhZI9LK8yIySxZ2frHI2vDSANGupi5LAuBft7HZT9SQB +jLMi6Et8Vcad+qMUu2WFbm5PEn4KPJ2V +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ +RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD +VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX +DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y +ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy +VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr +mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr +IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK +mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu +XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy +dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye +jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1 +BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3 +DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92 +9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx +jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0 +Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz +ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS +R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEd +MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3Mg +Q2xhc3MgMiBSb290IENBMB4XDTEwMTAyNjA4MzgwM1oXDTQwMTAyNjA4MzgwM1ow +TjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MSAw +HgYDVQQDDBdCdXlwYXNzIENsYXNzIDIgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEB +BQADggIPADCCAgoCggIBANfHXvfBB9R3+0Mh9PT1aeTuMgHbo4Yf5FkNuud1g1Lr +6hxhFUi7HQfKjK6w3Jad6sNgkoaCKHOcVgb/S2TwDCo3SbXlzwx87vFKu3MwZfPV +L4O2fuPn9Z6rYPnT8Z2SdIrkHJasW4DptfQxh6NR/Md+oW+OU3fUl8FVM5I+GC91 +1K2GScuVr1QGbNgGE41b/+EmGVnAJLqBcXmQRFBoJJRfuLMR8SlBYaNByyM21cHx +MlAQTn/0hpPshNOOvEu/XAFOBz3cFIqUCqTqc/sLUegTBxj6DvEr0VQVfTzh97QZ +QmdiXnfgolXsttlpF9U6r0TtSsWe5HonfOV116rLJeffawrbD02TTqigzXsu8lkB +arcNuAeBfos4GzjmCleZPe4h6KP1DBbdi+w0jpwqHAAVF41og9JwnxgIzRFo1clr +Us3ERo/ctfPYV3Me6ZQ5BL/T3jjetFPsaRyifsSP5BtwrfKi+fv3FmRmaZ9JUaLi +FRhnBkp/1Wy1TbMz4GHrXb7pmA8y1x1LPC5aAVKRCfLf6o3YBkBjqhHk/sM3nhRS +P/TizPJhk9H9Z2vXUq6/aKtAQ6BXNVN48FP4YUIHZMbXb5tMOA1jrGKvNouicwoN +9SG9dKpN6nIDSdvHXx1iY8f93ZHsM+71bbRuMGjeyNYmsHVee7QHIJihdjK4TWxP +AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMmAd+BikoL1Rpzz +uvdMw964o605MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAU18h +9bqwOlI5LJKwbADJ784g7wbylp7ppHR/ehb8t/W2+xUbP6umwHJdELFx7rxP462s +A20ucS6vxOOto70MEae0/0qyexAQH6dXQbLArvQsWdZHEIjzIVEpMMpghq9Gqx3t +OluwlN5E40EIosHsHdb9T7bWR9AUC8rmyrV7d35BH16Dx7aMOZawP5aBQW9gkOLo ++fsicdl9sz1Gv7SEr5AcD48Saq/v7h56rgJKihcrdv6sVIkkLE8/trKnToyokZf7 +KcZ7XC25y2a2t6hbElGFtQl+Ynhw/qlqYLYdDnkM/crqJIByw5c/8nerQyIKx+u2 +DISCLIBrQYoIwOula9+ZEsuK1V6ADJHgJgg2SMX6OBE1/yWDLfJ6v9r9jv6ly0Us +H8SIU653DtmadsWOLB2jutXsMq7Aqqz30XpN69QH4kj3Io6wpJ9qzo6ysmD0oyLQ +I+uUWnpp3Q+/QFesa1lQ2aOZ4W7+jQF5JyMV3pKdewlNWudLSDBaGOYKbeaP4NK7 +5t98biGCwWg5TbSYWGZizEqQXsP6JwSxeRV0mcy+rSDeJmAc61ZRpqPq5KM/p/9h +3PFaTWwyI0PurKju7koSCTxdccK+efrCh2gdC/1cacwG0Jp9VJkqyTkaGa9LKkPz +Y11aWOIv4x3kqdbQCtCev9eBCfHJxyYNrJgWVqA= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEd +MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3Mg +Q2xhc3MgMyBSb290IENBMB4XDTEwMTAyNjA4Mjg1OFoXDTQwMTAyNjA4Mjg1OFow +TjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MSAw +HgYDVQQDDBdCdXlwYXNzIENsYXNzIDMgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEB +BQADggIPADCCAgoCggIBAKXaCpUWUOOV8l6ddjEGMnqb8RB2uACatVI2zSRHsJ8Y +ZLya9vrVediQYkwiL944PdbgqOkcLNt4EemOaFEVcsfzM4fkoF0LXOBXByow9c3E +N3coTRiR5r/VUv1xLXA+58bEiuPwKAv0dpihi4dVsjoT/Lc+JzeOIuOoTyrvYLs9 +tznDDgFHmV0ST9tD+leh7fmdvhFHJlsTmKtdFoqwNxxXnUX/iJY2v7vKB3tvh2PX +0DJq1l1sDPGzbjniazEuOQAnFN44wOwZZoYS6J1yFhNkUsepNxz9gjDthBgd9K5c +/3ATAOux9TN6S9ZV+AWNS2mw9bMoNlwUxFFzTWsL8TQH2xc519woe2v1n/MuwU8X +KhDzzMro6/1rqy6any2CbgTUUgGTLT2G/H783+9CHaZr77kgxve9oKeV/afmiSTY +zIw0bOIjL9kSGiG5VZFvC5F5GQytQIgLcOJ60g7YaEi7ghM5EFjp2CoHxhLbWNvS +O1UQRwUVZ2J+GGOmRj8JDlQyXr8NYnon74Do29lLBlo3WiXQCBJ31G8JUJc9yB3D +34xFMFbG02SrZvPAXpacw8Tvw3xrizp5f7NJzz3iiZ+gMEuFuZyUJHmPfWupRWgP +K9Dx2hzLabjKSWJtyNBjYt1gD1iqj6G8BaVmos8bdrKEZLFMOVLAMLrwjEsCsLa3 +AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFEe4zf/lb+74suwv +Tg75JbCOPGvDMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAACAj +QTUEkMJAYmDv4jVM1z+s4jSQuKFvdvoWFqRINyzpkMLyPPgKn9iB5btb2iUspKdV +cSQy9sgL8rxq+JOssgfCX5/bzMiKqr5qb+FJEMwx14C7u8jYog5kV+qi9cKpMRXS +IGrs/CIBKM+GuIAeqcwRpTzyFrNHnfzSgCHEy9BHcEGhyoMZCCxt8l13nIoUE9Q2 +HJLw5QY33KbmkJs4j1xrG0aGQ0JfPgEHU1RdZX33inOhmlRaHylDFCfChQ+1iHsa +O5S3HWCntZznKWlXWpuTekMwGwPXYshApqr8ZORK15FTAaggiG6cX0S5y2CBNOxv +033aSF/rtJC8LakcC6wc1aJoIIAE1vyxjy+7SjENSoYc6+I2KSb12tjE8nVhz36u +dmNKekBlk4f4HoCMhuWG1o8O/FMsYOgWYRqiPkN7zTlgVGr18okmAWiDSKIz6MkE +kbIRNBE+6tBDGR8Dk5AM/1E9V/RBbuHLoL7ryWPNbczk+DaqaJ3tvV2XcEQNtg41 +3OEMXbugUZTLfhbrES+jkkXITHHZvMmZUldGL1DPvTVp9D0VzgalLA8+9oG6lLvD +u79leNKGef9JOxqDDPDeeOzI8k1MGt6CKfjBWtrt7uYnXuhF0J0cUahoq0Tj0Itq +4/g7u9xN12TyUb7mqqta6THuBrxzvxNiCp/HuZc= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFaTCCA1GgAwIBAgIJAJK4iNuwisFjMA0GCSqGSIb3DQEBCwUAMFIxCzAJBgNV +BAYTAlNLMRMwEQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMu +MRkwFwYDVQQDExBDQSBEaXNpZyBSb290IFIyMB4XDTEyMDcxOTA5MTUzMFoXDTQy +MDcxOTA5MTUzMFowUjELMAkGA1UEBhMCU0sxEzARBgNVBAcTCkJyYXRpc2xhdmEx +EzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERpc2lnIFJvb3QgUjIw +ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCio8QACdaFXS1tFPbCw3Oe +NcJxVX6B+6tGUODBfEl45qt5WDza/3wcn9iXAng+a0EE6UG9vgMsRfYvZNSrXaNH +PWSb6WiaxswbP7q+sos0Ai6YVRn8jG+qX9pMzk0DIaPY0jSTVpbLTAwAFjxfGs3I +x2ymrdMxp7zo5eFm1tL7A7RBZckQrg4FY8aAamkw/dLukO8NJ9+flXP04SXabBbe +QTg06ov80egEFGEtQX6sx3dOy1FU+16SGBsEWmjGycT6txOgmLcRK7fWV8x8nhfR +yyX+hk4kLlYMeE2eARKmK6cBZW58Yh2EhN/qwGu1pSqVg8NTEQxzHQuyRpDRQjrO +QG6Vrf/GlK1ul4SOfW+eioANSW1z4nuSHsPzwfPrLgVv2RvPN3YEyLRa5Beny912 +H9AZdugsBbPWnDTYltxhh5EF5EQIM8HauQhl1K6yNg3ruji6DOWbnuuNZt2Zz9aJ +QfYEkoopKW1rOhzndX0CcQ7zwOe9yxndnWCywmZgtrEE7snmhrmaZkCo5xHtgUUD +i/ZnWejBBhG93c+AAk9lQHhcR1DIm+YfgXvkRKhbhZri3lrVx/k6RGZL5DJUfORs +nLMOPReisjQS1n6yqEm70XooQL6iFh/f5DcfEXP7kAplQ6INfPgGAVUzfbANuPT1 +rqVCV3w2EYx7XsQDnYx5nQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1Ud +DwEB/wQEAwIBBjAdBgNVHQ4EFgQUtZn4r7CU9eMg1gqtzk5WpC5uQu0wDQYJKoZI +hvcNAQELBQADggIBACYGXnDnZTPIgm7ZnBc6G3pmsgH2eDtpXi/q/075KMOYKmFM +tCQSin1tERT3nLXK5ryeJ45MGcipvXrA1zYObYVybqjGom32+nNjf7xueQgcnYqf +GopTpti72TVVsRHFqQOzVju5hJMiXn7B9hJSi+osZ7z+Nkz1uM/Rs0mSO9MpDpkb +lvdhuDvEK7Z4bLQjb/D907JedR+Zlais9trhxTF7+9FGs9K8Z7RiVLoJ92Owk6Ka ++elSLotgEqv89WBW7xBci8QaQtyDW2QOy7W81k/BfDxujRNt+3vrMNDcTa/F1bal +TFtxyegxvug4BkihGuLq0t4SOVga/4AOgnXmt8kHbA7v/zjxmHHEt38OFdAlab0i +nSvtBfZGR6ztwPDUO+Ls7pZbkBNOHlY667DvlruWIxG68kOGdGSVyCh13x01utI3 +gzhTODY7z2zp+WsO0PsE6E9312UBeIYMej4hYvF/Y3EMyZ9E26gnonW+boE+18Dr +G5gPcFw0sorMwIUY6256s/daoQe/qUKS82Ail+QUoQebTnbAjn39pCXHR+3/H3Os +zMOl6W8KjptlwlCFtaOgUxLMVYdh84GuEEZhvUQhuMI9dM9+JDX6HAcOmz0iyu8x +L4ysEr3vQCj8KWefshNPZiTEUxnpHikV7+ZtsH8tZ/3zbBt1RqPlShfppNcL +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFjTCCA3WgAwIBAgIEGErM1jANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJD +TjEwMC4GA1UECgwnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9y +aXR5MRUwEwYDVQQDDAxDRkNBIEVWIFJPT1QwHhcNMTIwODA4MDMwNzAxWhcNMjkx +MjMxMDMwNzAxWjBWMQswCQYDVQQGEwJDTjEwMC4GA1UECgwnQ2hpbmEgRmluYW5j +aWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRUwEwYDVQQDDAxDRkNBIEVWIFJP +T1QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDXXWvNED8fBVnVBU03 +sQ7smCuOFR36k0sXgiFxEFLXUWRwFsJVaU2OFW2fvwwbwuCjZ9YMrM8irq93VCpL +TIpTUnrD7i7es3ElweldPe6hL6P3KjzJIx1qqx2hp/Hz7KDVRM8Vz3IvHWOX6Jn5 +/ZOkVIBMUtRSqy5J35DNuF++P96hyk0g1CXohClTt7GIH//62pCfCqktQT+x8Rgp +7hZZLDRJGqgG16iI0gNyejLi6mhNbiyWZXvKWfry4t3uMCz7zEasxGPrb382KzRz +EpR/38wmnvFyXVBlWY9ps4deMm/DGIq1lY+wejfeWkU7xzbh72fROdOXW3NiGUgt +hxwG+3SYIElz8AXSG7Ggo7cbcNOIabla1jj0Ytwli3i/+Oh+uFzJlU9fpy25IGvP +a931DfSCt/SyZi4QKPaXWnuWFo8BGS1sbn85WAZkgwGDg8NNkt0yxoekN+kWzqot +aK8KgWU6cMGbrU1tVMoqLUuFG7OA5nBFDWteNfB/O7ic5ARwiRIlk9oKmSJgamNg +TnYGmE69g60dWIolhdLHZR4tjsbftsbhf4oEIRUpdPA+nJCdDC7xij5aqgwJHsfV +PKPtl8MeNPo4+QgO48BdK4PRVmrJtqhUUy54Mmc9gn900PvhtgVguXDbjgv5E1hv +cWAQUhC5wUEJ73IfZzF4/5YFjQIDAQABo2MwYTAfBgNVHSMEGDAWgBTj/i39KNAL +tbq2osS/BqoFjJP7LzAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAd +BgNVHQ4EFgQU4/4t/SjQC7W6tqLEvwaqBYyT+y8wDQYJKoZIhvcNAQELBQADggIB +ACXGumvrh8vegjmWPfBEp2uEcwPenStPuiB/vHiyz5ewG5zz13ku9Ui20vsXiObT +ej/tUxPQ4i9qecsAIyjmHjdXNYmEwnZPNDatZ8POQQaIxffu2Bq41gt/UP+TqhdL +jOztUmCypAbqTuv0axn96/Ua4CUqmtzHQTb3yHQFhDmVOdYLO6Qn+gjYXB74BGBS +ESgoA//vU2YApUo0FmZ8/Qmkrp5nGm9BC2sGE5uPhnEFtC+NiWYzKXZUmhH4J/qy +P5Hgzg0b8zAarb8iXRvTvyUFTeGSGn+ZnzxEk8rUQElsgIfXBDrDMlI1Dlb4pd19 +xIsNER9Tyx6yF7Zod1rg1MvIB671Oi6ON7fQAUtDKXeMOZePglr4UeWJoBjnaH9d +Ci77o0cOPaYjesYBx4/IXr9tgFa+iiS6M+qf4TIRnvHST4D2G0CvOJ4RUHlzEhLN +5mydLIhyPDCBBpEi6lmt2hkuIsKNuYyH4Ga8cyNfIWRjgEj1oDwYPZTISEEdQLpe +/v5WOaHIz16eGWRGENoXkbcFgKyLmZJ956LYBws2J+dIeWCKw9cTXPhyQN9Ky8+Z +AAoACxGV2lZFA4gKn2fQ1XmxqI1AbQ3CekD6819kR5LLU7m7Wc5P/dAVUwHY3+vZ +5nbv0CO7O6l5s9UCKc2Jo5YPSjXnTkLAdc0Hz+Ys63su +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEHTCCAwWgAwIBAgIQToEtioJl4AsC7j41AkblPTANBgkqhkiG9w0BAQUFADCB +gTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G +A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNV +BAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEyMDEwMDAw +MDBaFw0yOTEyMzEyMzU5NTlaMIGBMQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3Jl +YXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01P +RE8gQ0EgTGltaXRlZDEnMCUGA1UEAxMeQ09NT0RPIENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ECLi3LjkRv3 +UcEbVASY06m/weaKXTuH+7uIzg3jLz8GlvCiKVCZrts7oVewdFFxze1CkU1B/qnI +2GqGd0S7WWaXUF601CxwRM/aN5VCaTwwxHGzUvAhTaHYujl8HJ6jJJ3ygxaYqhZ8 +Q5sVW7euNJH+1GImGEaaP+vB+fGQV+useg2L23IwambV4EajcNxo2f8ESIl33rXp ++2dtQem8Ob0y2WIC8bGoPW43nOIv4tOiJovGuFVDiOEjPqXSJDlqR6sA1KGzqSX+ +DT+nHbrTUcELpNqsOO9VUCQFZUaTNE8tja3G1CEZ0o7KBWFxB3NH5YoZEr0ETc5O +nKVIrLsm9wIDAQABo4GOMIGLMB0GA1UdDgQWBBQLWOWLxkwVN6RAqTCpIb5HNlpW +/zAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zBJBgNVHR8EQjBAMD6g +PKA6hjhodHRwOi8vY3JsLmNvbW9kb2NhLmNvbS9DT01PRE9DZXJ0aWZpY2F0aW9u +QXV0aG9yaXR5LmNybDANBgkqhkiG9w0BAQUFAAOCAQEAPpiem/Yb6dc5t3iuHXIY +SdOH5EOC6z/JqvWote9VfCFSZfnVDeFs9D6Mk3ORLgLETgdxb8CPOGEIqB6BCsAv +IC9Bi5HcSEW88cbeunZrM8gALTFGTO3nnc+IlP8zwFboJIYmuNg4ON8qa90SzMc/ +RxdMosIGlgnW2/4/PEZB31jiVg88O8EckzXZOFKs7sjsLjBOlDW0JB9LeGna8gI4 +zJVSk/BwJVmcIGfE7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5dd +BA6+C4OmF4O5MBKgxTMVBbkN+8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IB +ZQ== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTEL +MAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE +BxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMT +IkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwMzA2MDAw +MDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdy +ZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09N +T0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlv +biBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQDR3svdcmCFYX7deSR +FtSrYpn1PlILBs5BAH+X4QokPB0BBO490o0JlwzgdeT6+3eKKvUDYEs2ixYjFq0J +cfRK9ChQtP6IHG4/bC8vCVlbpVsLM5niwz2J+Wos77LTBumjQjBAMB0GA1UdDgQW +BBR1cacZSBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ +BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VGFAkK+qDm +fQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdv +GDeAU/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIF2DCCA8CgAwIBAgIQTKr5yttjb+Af907YWwOGnTANBgkqhkiG9w0BAQwFADCB +hTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G +A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNV +BAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMTE5 +MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgT +EkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR +Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCR +6FSS0gpWsawNJN3Fz0RndJkrN6N9I3AAcbxT38T6KhKPS38QVr2fcHK3YX/JSw8X +pz3jsARh7v8Rl8f0hj4K+j5c+ZPmNHrZFGvnnLOFoIJ6dq9xkNfs/Q36nGz637CC +9BR++b7Epi9Pf5l/tfxnQ3K9DADWietrLNPtj5gcFKt+5eNu/Nio5JIk2kNrYrhV +/erBvGy2i/MOjZrkm2xpmfh4SDBF1a3hDTxFYPwyllEnvGfDyi62a+pGx8cgoLEf +Zd5ICLqkTqnyg0Y3hOvozIFIQ2dOciqbXL1MGyiKXCJ7tKuY2e7gUYPDCUZObT6Z ++pUX2nwzV0E8jVHtC7ZcryxjGt9XyD+86V3Em69FmeKjWiS0uqlWPc9vqv9JWL7w +qP/0uK3pN/u6uPQLOvnoQ0IeidiEyxPx2bvhiWC4jChWrBQdnArncevPDt09qZah +SL0896+1DSJMwBGB7FY79tOi4lu3sgQiUpWAk2nojkxl8ZEDLXB0AuqLZxUpaVIC +u9ffUGpVRr+goyhhf3DQw6KqLCGqR84onAZFdr+CGCe01a60y1Dma/RMhnEw6abf +Fobg2P9A3fvQQoh/ozM6LlweQRGBY84YcWsr7KaKtzFcOmpH4MN5WdYgGq/yapiq +crxXStJLnbsQ/LBMQeXtHT1eKJ2czL+zUdqnR+WEUwIDAQABo0IwQDAdBgNVHQ4E +FgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB +/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAArx1UaEt65Ru2yyTUEUAJNMnMvl +wFTPoCWOAvn9sKIN9SCYPBMtrFaisNZ+EZLpLrqeLppysb0ZRGxhNaKatBYSaVqM +4dc+pBroLwP0rmEdEBsqpIt6xf4FpuHA1sj+nq6PK7o9mfjYcwlYRm6mnPTXJ9OV +2jeDchzTc+CiR5kDOF3VSXkAKRzH7JsgHAckaVd4sjn8OoSgtZx8jb8uk2Intzna +FxiuvTwJaP+EmzzV1gsD41eeFPfR60/IvYcjt7ZJQ3mFXLrrkguhxuhoqEwWsRqZ +CuhTLJK7oQkYdQxlqHvLI7cawiiFwxv/0Cti76R7CZGYZ4wUAc1oBmpjIXUDgIiK +boHGhfKppC3n9KUkEEeDys30jXlYsQab5xoq2Z0B15R97QNKyvDb6KkBPvVWmcke +jkk9u+UJueBPSZI9FoJAzMxZxuY67RIuaTxslbH9qh17f4a+Hg4yRvv7E491f0yL +S0Zj/gA0QHDBw7mh3aZw4gSzQbzpgJHqZJx64SIDqZxubw5lT2yHh17zbqD5daWb +QOhTsiedSrnAdyGN/4fy3ryM7xfft0kL0fJuMAsaDk527RH89elWsn2/x20Kk4yl +0MC2Hb46TpSi125sC8KKfPog88Tk5c0NqMuRkrF8hey1FGlmDoLnzc7ILaZRfyHB +NVOFBkpdn627G190 +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDqDCCApCgAwIBAgIJAP7c4wEPyUj/MA0GCSqGSIb3DQEBBQUAMDQxCzAJBgNV +BAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hMB4X +DTA3MDYyOTE1MTMwNVoXDTI3MDYyOTE1MTMwNVowNDELMAkGA1UEBhMCRlIxEjAQ +BgNVBAoMCURoaW15b3RpczERMA8GA1UEAwwIQ2VydGlnbmEwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQDIaPHJ1tazNHUmgh7stL7qXOEm7RFHYeGifBZ4 +QCHkYJ5ayGPhxLGWkv8YbWkj4Sti993iNi+RB7lIzw7sebYs5zRLcAglozyHGxny +gQcPOJAZ0xH+hrTy0V4eHpbNgGzOOzGTtvKg0KmVEn2lmsxryIRWijOp5yIVUxbw +zBfsV1/pogqYCd7jX5xv3EjjhQsVWqa6n6xI4wmy9/Qy3l40vhx4XUJbzg4ij02Q +130yGLMLLGq/jj8UEYkgDncUtT2UCIf3JR7VsmAA7G8qKCVuKj4YYxclPz5EIBb2 +JsglrgVKtOdjLPOMFlN+XPsRGgjBRmKfIrjxwo1p3Po6WAbfAgMBAAGjgbwwgbkw +DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUGu3+QTmQtCRZvgHyUtVF9lo53BEw +ZAYDVR0jBF0wW4AUGu3+QTmQtCRZvgHyUtVF9lo53BGhOKQ2MDQxCzAJBgNVBAYT +AkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hggkA/tzj +AQ/JSP8wDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzANBgkqhkiG +9w0BAQUFAAOCAQEAhQMeknH2Qq/ho2Ge6/PAD/Kl1NqV5ta+aDY9fm4fTIrv0Q8h +bV6lUmPOEvjvKtpv6zf+EwLHyzs+ImvaYS5/1HI93TDhHkxAGYwP15zRgzB7mFnc +fca5DClMoTOi62c6ZYTTluLtdkVwj7Ur3vkj1kluPBS1xp81HlDQwY9qcEQCYsuu +HWhBp6pX6FOqB9IG9tUUBguRA3UsbHK1YZWaDYu5Def131TN3ubY1gkIl2PlwS6w +t0QmwCbAr1UwnjvVNioZBPRcHv/PLLf/0P2HQBHVESO7SMAhqaQoLf0V+LBOK/Qw +WyH8EZE0vkHve52Xdf+XlcCWWC/qu0bXu+TZLg== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFkjCCA3qgAwIBAgIBATANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJGUjET +MBEGA1UEChMKQ2VydGlub21pczEXMBUGA1UECxMOMDAwMiA0MzM5OTg5MDMxHTAb +BgNVBAMTFENlcnRpbm9taXMgLSBSb290IENBMB4XDTEzMTAyMTA5MTcxOFoXDTMz +MTAyMTA5MTcxOFowWjELMAkGA1UEBhMCRlIxEzARBgNVBAoTCkNlcnRpbm9taXMx +FzAVBgNVBAsTDjAwMDIgNDMzOTk4OTAzMR0wGwYDVQQDExRDZXJ0aW5vbWlzIC0g +Um9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANTMCQosP5L2 +fxSeC5yaah1AMGT9qt8OHgZbn1CF6s2Nq0Nn3rD6foCWnoR4kkjW4znuzuRZWJfl +LieY6pOod5tK8O90gC3rMB+12ceAnGInkYjwSond3IjmFPnVAy//ldu9n+ws+hQV +WZUKxkd8aRi5pwP5ynapz8dvtF4F/u7BUrJ1Mofs7SlmO/NKFoL21prbcpjp3vDF +TKWrteoB4owuZH9kb/2jJZOLyKIOSY008B/sWEUuNKqEUL3nskoTuLAPrjhdsKkb +5nPJWqHZZkCqqU2mNAKthH6yI8H7KsZn9DS2sJVqM09xRLWtwHkziOC/7aOgFLSc +CbAK42C++PhmiM1b8XcF4LVzbsF9Ri6OSyemzTUK/eVNfaoqoynHWmgE6OXWk6Ri +wsXm9E/G+Z8ajYJJGYrKWUM66A0ywfRMEwNvbqY/kXPLynNvEiCL7sCCeN5LLsJJ +wx3tFvYk9CcbXFcx3FXuqB5vbKziRcxXV4p1VxngtViZSTYxPDMBbRZKzbgqg4SG +m/lg0h9tkQPTYKbVPZrdd5A9NaSfD171UkRpucC63M9933zZxKyGIjK8e2uR73r4 +F2iw4lNVYC2vPsKD2NkJK/DAZNuHi5HMkesE/Xa0lZrmFAYb1TQdvtj/dBxThZng +WVJKYe2InmtJiUZ+IFrZ50rlau7SZRFDAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIB +BjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTvkUz1pcMw6C8I6tNxIqSSaHh0 +2TAfBgNVHSMEGDAWgBTvkUz1pcMw6C8I6tNxIqSSaHh02TANBgkqhkiG9w0BAQsF +AAOCAgEAfj1U2iJdGlg+O1QnurrMyOMaauo++RLrVl89UM7g6kgmJs95Vn6RHJk/ +0KGRHCwPT5iVWVO90CLYiF2cN/z7ZMF4jIuaYAnq1fohX9B0ZedQxb8uuQsLrbWw +F6YSjNRieOpWauwK0kDDPAUwPk2Ut59KA9N9J0u2/kTO+hkzGm2kQtHdzMjI1xZS +g081lLMSVX3l4kLr5JyTCcBMWwerx20RoFAXlCOotQqSD7J6wWAsOMwaplv/8gzj +qh8c3LigkyfeY+N/IZ865Z764BNqdeuWXGKRlI5nU7aJ+BIJy29SWwNyhlCVCNSN +h4YVH5Uk2KRvms6knZtt0rJ2BobGVgjF6wnaNsIbW0G+YSrjcOa4pvi2WsS9Iff/ +ql+hbHY5ZtbqTFXhADObE5hjyW/QASAJN1LnDE8+zbz1X5YnpyACleAu6AdBBR8V +btaw5BngDwKTACdyxYvRVB9dSsNAl35VpnzBMwQUAR1JIGkLGZOdblgi90AMRgwj +Y/M50n92Uaf0yKHxDHYiI0ZSKS3io0EHVmmY0gUJvGnHWmHNj4FgFU2A3ZDifcRQ +8ow7bkrHxuaAKzyBvBGAFhAn1/DNP3nMcyrDflOR1m749fPH0FFNjkulW+YZFzvW +gQncItzujrnEj1PhZ7szuIgVRs/taTX/dQ1G885x4cVrhkIGuUE= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDkjCCAnqgAwIBAgIRAIW9S/PY2uNp9pTXX8OlRCMwDQYJKoZIhvcNAQEFBQAw +PTELMAkGA1UEBhMCRlIxETAPBgNVBAoTCENlcnRwbHVzMRswGQYDVQQDExJDbGFz +cyAyIFByaW1hcnkgQ0EwHhcNOTkwNzA3MTcwNTAwWhcNMTkwNzA2MjM1OTU5WjA9 +MQswCQYDVQQGEwJGUjERMA8GA1UEChMIQ2VydHBsdXMxGzAZBgNVBAMTEkNsYXNz +IDIgUHJpbWFyeSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANxQ +ltAS+DXSCHh6tlJw/W/uz7kRy1134ezpfgSN1sxvc0NXYKwzCkTsA18cgCSR5aiR +VhKC9+Ar9NuuYS6JEI1rbLqzAr3VNsVINyPi8Fo3UjMXEuLRYE2+L0ER4/YXJQyL +kcAbmXuZVg2v7tK8R1fjeUl7NIknJITesezpWE7+Tt9avkGtrAjFGA7v0lPubNCd +EgETjdyAYveVqUSISnFOYFWe2yMZeVYHDD9jC1yw4r5+FfyUM1hBOHTE4Y+L3yas +H7WLO7dDWWuwJKZtkIvEcupdM5i3y95ee++U8Rs+yskhwcWYAqqi9lt3m/V+llU0 +HGdpwPFC40es/CgcZlUCAwEAAaOBjDCBiTAPBgNVHRMECDAGAQH/AgEKMAsGA1Ud +DwQEAwIBBjAdBgNVHQ4EFgQU43Mt38sOKAze3bOkynm4jrvoMIkwEQYJYIZIAYb4 +QgEBBAQDAgEGMDcGA1UdHwQwMC4wLKAqoCiGJmh0dHA6Ly93d3cuY2VydHBsdXMu +Y29tL0NSTC9jbGFzczIuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQCnVM+IRBnL39R/ +AN9WM2K191EBkOvDP9GIROkkXe/nFL0gt5o8AP5tn9uQ3Nf0YtaLcF3n5QRIqWh8 +yfFC82x/xXp8HVGIutIKPidd3i1RTtMTZGnkLuPT55sJmabglZvOGtd/vjzOUrMR +FcEPF80Du5wlFbqidon8BvEY0JNLDnyCt6X09l/+7UCmnYR0ObncHoUW2ikbhiMA +ybuJfm6AiB4vFLQDJKgybwOaRywwvlbGp0ICcBvqQNi6BQNwB6SW//1IMwrh3KWB +kJtN3X3n57LNXMhqlfil9o3EXXgIvnsG1knPGTZQIy4I5p4FTUcY1Rbpsda2ENW7 +l7+ijrRU +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFazCCA1OgAwIBAgISESBVg+QtPlRWhS2DN7cs3EYRMA0GCSqGSIb3DQEBDQUA +MD4xCzAJBgNVBAYTAkZSMREwDwYDVQQKDAhDZXJ0cGx1czEcMBoGA1UEAwwTQ2Vy +dHBsdXMgUm9vdCBDQSBHMTAeFw0xNDA1MjYwMDAwMDBaFw0zODAxMTUwMDAwMDBa +MD4xCzAJBgNVBAYTAkZSMREwDwYDVQQKDAhDZXJ0cGx1czEcMBoGA1UEAwwTQ2Vy +dHBsdXMgUm9vdCBDQSBHMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB +ANpQh7bauKk+nWT6VjOaVj0W5QOVsjQcmm1iBdTYj+eJZJ+622SLZOZ5KmHNr49a +iZFluVj8tANfkT8tEBXgfs+8/H9DZ6itXjYj2JizTfNDnjl8KvzsiNWI7nC9hRYt +6kuJPKNxQv4c/dMcLRC4hlTqQ7jbxofaqK6AJc96Jh2qkbBIb6613p7Y1/oA/caP +0FG7Yn2ksYyy/yARujVjBYZHYEMzkPZHogNPlk2dT8Hq6pyi/jQu3rfKG3akt62f +6ajUeD94/vI4CTYd0hYCyOwqaK/1jpTvLRN6HkJKHRUxrgwEV/xhc/MxVoYxgKDE +EW4wduOU8F8ExKyHcomYxZ3MVwia9Az8fXoFOvpHgDm2z4QTd28n6v+WZxcIbekN +1iNQMLAVdBM+5S//Ds3EC0pd8NgAM0lm66EYfFkuPSi5YXHLtaW6uOrc4nBvCGrc +h2c0798wct3zyT8j/zXhviEpIDCB5BmlIOklynMxdCm+4kLV87ImZsdo/Rmz5yCT +mehd4F6H50boJZwKKSTUzViGUkAksnsPmBIgJPaQbEfIDbsYIC7Z/fyL8inqh3SV +4EJQeIQEQWGw9CEjjy3LKCHyamz0GqbFFLQ3ZU+V/YDI+HLlJWvEYLF7bY5KinPO +WftwenMGE9nTdDckQQoRb5fc5+R+ob0V8rqHDz1oihYHAgMBAAGjYzBhMA4GA1Ud +DwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSowcCbkahDFXxd +Bie0KlHYlwuBsTAfBgNVHSMEGDAWgBSowcCbkahDFXxdBie0KlHYlwuBsTANBgkq +hkiG9w0BAQ0FAAOCAgEAnFZvAX7RvUz1isbwJh/k4DgYzDLDKTudQSk0YcbX8ACh +66Ryj5QXvBMsdbRX7gp8CXrc1cqh0DQT+Hern+X+2B50ioUHj3/MeXrKls3N/U/7 +/SMNkPX0XtPGYX2eEeAC7gkE2Qfdpoq3DIMku4NQkv5gdRE+2J2winq14J2by5BS +S7CTKtQ+FjPlnsZlFT5kOwQ/2wyPX1wdaR+v8+khjPPvl/aatxm2hHSco1S1cE5j +2FddUyGbQJJD+tZ3VTNPZNX70Cxqjm0lpu+F6ALEUz65noe8zDUa3qHpimOHZR4R +Kttjd5cUvpoUmRGywO6wT/gUITJDT5+rosuoD6o7BlXGEilXCNQ314cnrUlZp5Gr +RHpejXDbl85IULFzk/bwg2D5zfHhMf1bfHEhYxQUqq/F3pN+aLHsIqKqkHWetUNy +6mSjhEv9DKgma3GX7lZjZuhCVPnHHd/Qj1vfyDBviP4NxDMcU6ij/UgQ8uQKTuEV +V/xuZDDCVRHc6qnNSlSsKWNEz0pAoNZoWRsz+e86i9sgktxChL8Bq4fA1SCC28a5 +g4VCXA9DO2pJNdWY9BW/+mGBDAkgGNLQFwzLSABQ6XaCjGTXOqAHVcweMcDvOrRl +++O/QmueD6i9a5jc2NvLi6Td11n0bt3+qsOR0C5CB8AMTVPNJLFMWx5R9N/pkvo= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICHDCCAaKgAwIBAgISESDZkc6uo+jF5//pAq/Pc7xVMAoGCCqGSM49BAMDMD4x +CzAJBgNVBAYTAkZSMREwDwYDVQQKDAhDZXJ0cGx1czEcMBoGA1UEAwwTQ2VydHBs +dXMgUm9vdCBDQSBHMjAeFw0xNDA1MjYwMDAwMDBaFw0zODAxMTUwMDAwMDBaMD4x +CzAJBgNVBAYTAkZSMREwDwYDVQQKDAhDZXJ0cGx1czEcMBoGA1UEAwwTQ2VydHBs +dXMgUm9vdCBDQSBHMjB2MBAGByqGSM49AgEGBSuBBAAiA2IABM0PW1aC3/BFGtat +93nwHcmsltaeTpwftEIRyoa/bfuFo8XlGVzX7qY/aWfYeOKmycTbLXku54uNAm8x +Ik0G42ByRZ0OQneezs/lf4WbGOT8zC5y0xaTTsqZY1yhBSpsBqNjMGEwDgYDVR0P +AQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNqDYwJ5jtpMxjwj +FNiPwyCrKGBZMB8GA1UdIwQYMBaAFNqDYwJ5jtpMxjwjFNiPwyCrKGBZMAoGCCqG +SM49BAMDA2gAMGUCMHD+sAvZ94OX7PNVHdTcswYO/jOYnYs5kGuUIe22113WTNch +p+e/IQ8rzfcq3IUHnQIxAIYUFuXcsGXCwI4Un78kFmjlvPl5adytRSv3tjFzzAal +U5ORGpOucGpnutee5WEaXw== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDuzCCAqOgAwIBAgIDBETAMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAlBM +MSIwIAYDVQQKExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5D +ZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBU +cnVzdGVkIE5ldHdvcmsgQ0EwHhcNMDgxMDIyMTIwNzM3WhcNMjkxMjMxMTIwNzM3 +WjB+MQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBUZWNobm9sb2dpZXMg +Uy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MSIw +IAYDVQQDExlDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBMIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEA4/t9o3K6wvDJFIf1awFO4W5AB7ptJ11/91sts1rH +UV+rpDKmYYe2bg+G0jACl/jXaVehGDldamR5xgFZrDwxSjh80gTSSyjoIF87B6LM +TXPb865Px1bVWqeWifrzq2jUI4ZZJ88JJ7ysbnKDHDBy3+Ci6dLhdHUZvSqeexVU +BBvXQzmtVSjF4hq79MDkrjhJM8x2hZ85RdKknvISjFH4fOQtf/WsX+sWn7Et0brM +kUJ3TCXJkDhv2/DM+44el1k+1WBO5gUo7Ul5E0u6SNsv+XLTOcr+H9g0cvW0QM8x +AcPs3hEtF10fuFDRXhmnad4HMyjKUJX5p1TLVIZQRan5SQIDAQABo0IwQDAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBQIds3LB/8k9sXN7buQvOKEN0Z19zAOBgNV +HQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEFBQADggEBAKaorSLOAT2mo/9i0Eidi15y +sHhE49wcrwn9I0j6vSrEuVUEtRCjjSfeC4Jj0O7eDDd5QVsisrCaQVymcODU0HfL +I9MA4GxWL+FpDQ3Zqr8hgVDZBqWo/5U30Kr+4rP1mS1FhIrlQgnXdAIv94nYmem8 +J9RHjboNRhx3zxSkHLmkMcScKHQDNP8zGSal6Q10tz6XxnboJ5ajZt3hrvJBW8qY +VoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI +03YnnZotBqbJ7DnSq9ufmgsnAjUpsUCV5/nonFWIGUbWtzT1fs45mtk48VH3Tyw= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIF0jCCA7qgAwIBAgIQIdbQSk8lD8kyN/yqXhKN6TANBgkqhkiG9w0BAQ0FADCB +gDELMAkGA1UEBhMCUEwxIjAgBgNVBAoTGVVuaXpldG8gVGVjaG5vbG9naWVzIFMu +QS4xJzAlBgNVBAsTHkNlcnR1bSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEkMCIG +A1UEAxMbQ2VydHVtIFRydXN0ZWQgTmV0d29yayBDQSAyMCIYDzIwMTExMDA2MDgz +OTU2WhgPMjA0NjEwMDYwODM5NTZaMIGAMQswCQYDVQQGEwJQTDEiMCAGA1UEChMZ +VW5pemV0byBUZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5MSQwIgYDVQQDExtDZXJ0dW0gVHJ1c3RlZCBOZXR3 +b3JrIENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC9+Xj45tWA +DGSdhhuWZGc/IjoedQF97/tcZ4zJzFxrqZHmuULlIEub2pt7uZld2ZuAS9eEQCsn +0+i6MLs+CRqnSZXvK0AkwpfHp+6bJe+oCgCXhVqqndwpyeI1B+twTUrWwbNWuKFB +OJvR+zF/j+Bf4bE/D44WSWDXBo0Y+aomEKsq09DRZ40bRr5HMNUuctHFY9rnY3lE +fktjJImGLjQ/KUxSiyqnwOKRKIm5wFv5HdnnJ63/mgKXwcZQkpsCLL2puTRZCr+E +Sv/f/rOf69me4Jgj7KZrdxYq28ytOxykh9xGc14ZYmhFV+SQgkK7QtbwYeDBoz1m +o130GO6IyY0XRSmZMnUCMe4pJshrAua1YkV/NxVaI2iJ1D7eTiew8EAMvE0Xy02i +sx7QBlrd9pPPV3WZ9fqGGmd4s7+W/jTcvedSVuWz5XV710GRBdxdaeOVDUO5/IOW +OZV7bIBaTxNyxtd9KXpEulKkKtVBRgkg/iKgtlswjbyJDNXXcPiHUv3a76xRLgez +Tv7QCdpw75j6VuZt27VXS9zlLCUVyJ4ueE742pyehizKV/Ma5ciSixqClnrDvFAS +adgOWkaLOusm+iPJtrCBvkIApPjW/jAux9JG9uWOdf3yzLnQh1vMBhBgu4M1t15n +3kfsmUjxpKEV/q2MYo45VU85FrmxY53/twIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MB0GA1UdDgQWBBS2oVQ5AsOgP46KvPrU+Bym0ToO/TAOBgNVHQ8BAf8EBAMC +AQYwDQYJKoZIhvcNAQENBQADggIBAHGlDs7k6b8/ONWJWsQCYftMxRQXLYtPU2sQ +F/xlhMcQSZDe28cmk4gmb3DWAl45oPePq5a1pRNcgRRtDoGCERuKTsZPpd1iHkTf +CVn0W3cLN+mLIMb4Ck4uWBzrM9DPhmDJ2vuAL55MYIR4PSFk1vtBHxgP58l1cb29 +XN40hz5BsA72udY/CROWFC/emh1auVbONTqwX3BNXuMp8SMoclm2q8KMZiYcdywm +djWLKKdpoPk79SPdhRB0yZADVpHnr7pH1BKXESLjokmUbOe3lEu6LaTaM4tMpkT/ +WjzGHWTYtTHkpjx6qFcL2+1hGsvxznN3Y6SHb0xRONbkX8eftoEq5IVIeVheO/jb +AoJnwTnbw3RLPTYe+SmTiGhbqEQZIfCn6IENLOiTNrQ3ssqwGyZ6miUfmpqAnksq +P/ujmv5zMnHCnsZy4YpoJ/HkD7TETKVhk/iXEAcqMCWpuchxuO9ozC1+9eB+D4Ko +b7a6bINDd82Kkhehnlt4Fj1F4jNy3eFmypnTycUm/Q1oBEauttmbjL4ZvrHG8hnj +XALKLNhvSgfZyTXaQHXyxKcZb55CEJh15pWLYLztxRLXis7VmFxWlgPF7ncGNf/P +5O4/E2Hu29othfDNrp2yGAlFw5Khchf8R7agCyzxxN5DaAhqXzvwdmP7zAYspsbi +DrW5viSP +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIHTzCCBTegAwIBAgIJAKPaQn6ksa7aMA0GCSqGSIb3DQEBBQUAMIGuMQswCQYD +VQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0 +IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3 +MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xKTAnBgNVBAMTIENoYW1iZXJz +IG9mIENvbW1lcmNlIFJvb3QgLSAyMDA4MB4XDTA4MDgwMTEyMjk1MFoXDTM4MDcz +MTEyMjk1MFowga4xCzAJBgNVBAYTAkVVMUMwQQYDVQQHEzpNYWRyaWQgKHNlZSBj +dXJyZW50IGFkZHJlc3MgYXQgd3d3LmNhbWVyZmlybWEuY29tL2FkZHJlc3MpMRIw +EAYDVQQFEwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENhbWVyZmlybWEgUy5BLjEp +MCcGA1UEAxMgQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdCAtIDIwMDgwggIiMA0G +CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCvAMtwNyuAWko6bHiUfaN/Gh/2NdW9 +28sNRHI+JrKQUrpjOyhYb6WzbZSm891kDFX29ufyIiKAXuFixrYp4YFs8r/lfTJq +VKAyGVn+H4vXPWCGhSRv4xGzdz4gljUha7MI2XAuZPeEklPWDrCQiorjh40G072Q +DuKZoRuGDtqaCrsLYVAGUvGef3bsyw/QHg3PmTA9HMRFEFis1tPo1+XqxQEHd9ZR +5gN/ikilTWh1uem8nk4ZcfUyS5xtYBkL+8ydddy/Js2Pk3g5eXNeJQ7KXOt3EgfL +ZEFHcpOrUMPrCXZkNNI5t3YRCQ12RcSprj1qr7V9ZS+UWBDsXHyvfuK2GNnQm05a +Sd+pZgvMPMZ4fKecHePOjlO+Bd5gD2vlGts/4+EhySnB8esHnFIbAURRPHsl18Tl +UlRdJQfKFiC4reRB7noI/plvg6aRArBsNlVq5331lubKgdaX8ZSD6e2wsWsSaR6s ++12pxZjptFtYer49okQ6Y1nUCyXeG0+95QGezdIp1Z8XGQpvvwyQ0wlf2eOKNcx5 +Wk0ZN5K3xMGtr/R5JJqyAQuxr1yW84Ay+1w9mPGgP0revq+ULtlVmhduYJ1jbLhj +ya6BXBg14JC7vjxPNyK5fuvPnnchpj04gftI2jE9K+OJ9dC1vX7gUMQSibMjmhAx +hduub+84Mxh2EQIDAQABo4IBbDCCAWgwEgYDVR0TAQH/BAgwBgEB/wIBDDAdBgNV +HQ4EFgQU+SSsD7K1+HnA+mCIG8TZTQKeFxkwgeMGA1UdIwSB2zCB2IAU+SSsD7K1 ++HnA+mCIG8TZTQKeFxmhgbSkgbEwga4xCzAJBgNVBAYTAkVVMUMwQQYDVQQHEzpN +YWRyaWQgKHNlZSBjdXJyZW50IGFkZHJlc3MgYXQgd3d3LmNhbWVyZmlybWEuY29t +L2FkZHJlc3MpMRIwEAYDVQQFEwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENhbWVy +ZmlybWEgUy5BLjEpMCcGA1UEAxMgQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdCAt +IDIwMDiCCQCj2kJ+pLGu2jAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRV +HSAAMCowKAYIKwYBBQUHAgEWHGh0dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20w +DQYJKoZIhvcNAQEFBQADggIBAJASryI1wqM58C7e6bXpeHxIvj99RZJe6dqxGfwW +PJ+0W2aeaufDuV2I6A+tzyMP3iU6XsxPpcG1Lawk0lgH3qLPaYRgM+gQDROpI9CF +5Y57pp49chNyM/WqfcZjHwj0/gF/JM8rLFQJ3uIrbZLGOU8W6jx+ekbURWpGqOt1 +glanq6B8aBMz9p0w8G8nOSQjKpD9kCk18pPfNKXG9/jvjA9iSnyu0/VU+I22mlaH +FoI6M6taIgj3grrqLuBHmrS1RaMFO9ncLkVAO+rcf+g769HsJtg1pDDFOqxXnrN2 +pSB7+R5KBWIBpih1YJeSDW4+TTdDDZIVnBgizVGZoCkaPF+KMjNbMMeJL0eYD6MD +xvbxrN8y8NmBGuScvfaAFPDRLLmF9dijscilIeUcE5fuDr3fKanvNFNb0+RqE4QG +tjICxFKuItLcsiFCGtpA8CnJ7AoMXOLQusxI0zcKzBIKinmwPQN/aUv0NCB9szTq +jktk9T79syNnFQ0EuPAtwQlRPLJsFfClI9eDdOTlLsn+mCdCxqvGnrDQWzilm1De +fhiYtUU79nm06PcaewaD+9CL2rvHvRirCG88gGtAPxkZumWK5r7VXNM21+9AUiRg +OGcEMeyP84LG3rlV8zsxkVrctQgVrXYlCg17LofiDKYGvCYQbTed7N14jHyAxfDZ +d0jQ +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEb +MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow +GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmlj +YXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4MTIzMTIzNTk1OVowezEL +MAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE +BwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNVBAMM +GEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQua +BtDFcCLNSS1UY8y2bmhGC1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe +3M/vg4aijJRPn2jymJBGhCfHdr/jzDUsi14HZGWCwEiwqJH5YZ92IFCokcdmtet4 +YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszWY19zjNoFmag4qMsXeDZR +rOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjHYpy+g8cm +ez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQU +oBEKIz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF +MAMBAf8wewYDVR0fBHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20v +QUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29t +b2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2VzLmNybDANBgkqhkiG9w0BAQUF +AAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm7l3sAg9g1o1Q +GE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz +Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2 +G9w84FoVxp7Z8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsi +l2D4kF501KKaU73yqWjgom7C12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3 +smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDoTCCAomgAwIBAgILBAAAAAABD4WqLUgwDQYJKoZIhvcNAQEFBQAwOzEYMBYG +A1UEChMPQ3liZXJ0cnVzdCwgSW5jMR8wHQYDVQQDExZDeWJlcnRydXN0IEdsb2Jh +bCBSb290MB4XDTA2MTIxNTA4MDAwMFoXDTIxMTIxNTA4MDAwMFowOzEYMBYGA1UE +ChMPQ3liZXJ0cnVzdCwgSW5jMR8wHQYDVQQDExZDeWJlcnRydXN0IEdsb2JhbCBS +b290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA+Mi8vRRQZhP/8NN5 +7CPytxrHjoXxEnOmGaoQ25yiZXRadz5RfVb23CO21O1fWLE3TdVJDm71aofW0ozS +J8bi/zafmGWgE07GKmSb1ZASzxQG9Dvj1Ci+6A74q05IlG2OlTEQXO2iLb3VOm2y +HLtgwEZLAfVJrn5GitB0jaEMAs7u/OePuGtm839EAL9mJRQr3RAwHQeWP032a7iP +t3sMpTjr3kfb1V05/Iin89cqdPHoWqI7n1C6poxFNcJQZZXcY4Lv3b93TZxiyWNz +FtApD0mpSPCzqrdsxacwOUBdrsTiXSZT8M4cIwhhqJQZugRiQOwfOHB3EgZxpzAY +XSUnpQIDAQABo4GlMIGiMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/ +MB0GA1UdDgQWBBS2CHsNesysIEyGVjJez6tuhS1wVzA/BgNVHR8EODA2MDSgMqAw +hi5odHRwOi8vd3d3Mi5wdWJsaWMtdHJ1c3QuY29tL2NybC9jdC9jdHJvb3QuY3Js +MB8GA1UdIwQYMBaAFLYIew16zKwgTIZWMl7Pq26FLXBXMA0GCSqGSIb3DQEBBQUA +A4IBAQBW7wojoFROlZfJ+InaRcHUowAl9B8Tq7ejhVhpwjCt2BWKLePJzYFa+HMj +Wqd8BfP9IjsO0QbE2zZMcwSO5bAi5MXzLqXZI+O4Tkogp24CJJ8iYGd7ix1yCcUx +XOl5n4BHPa2hCwcUPUf/A2kaDAtE52Mlp3+yybh2hO0j9n0Hq0V+09+zv+mKts2o +omcrUtW3ZfA5TGOgkXmTUg9U3YO7n9GPp1Nzw8v/MOx8BLjYRB+TX3EJIrduPuoc +A06dGiBh+4E37F78CkWr1+cXVdCg6mCbpvbjjFspwgZgFJ0tl0ypkxWdYcQBX0jW +WL1WMRJOEcgh4LMRkWXbtKaIOM5V +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEMzCCAxugAwIBAgIDCYPzMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNVBAYTAkRF +MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBD +bGFzcyAzIENBIDIgMjAwOTAeFw0wOTExMDUwODM1NThaFw0yOTExMDUwODM1NTha +ME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMM +HkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBANOySs96R+91myP6Oi/WUEWJNTrGa9v+2wBoqOADER03 +UAifTUpolDWzU9GUY6cgVq/eUXjsKj3zSEhQPgrfRlWLJ23DEE0NkVJD2IfgXU42 +tSHKXzlABF9bfsyjxiupQB7ZNoTWSPOSHjRGICTBpFGOShrvUD9pXRl/RcPHAY9R +ySPocq60vFYJfxLLHLGvKZAKyVXMD9O0Gu1HNVpK7ZxzBCHQqr0ME7UAyiZsxGsM +lFqVlNpQmvH/pStmMaTJOKDfHR+4CS7zp+hnUquVH+BGPtikw8paxTGA6Eian5Rp +/hnd2HN8gcqW3o7tszIFZYQ05ub9VxC1X3a/L7AQDcUCAwEAAaOCARowggEWMA8G +A1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFP3aFMSfMN4hvR5COfyrYyNJ4PGEMA4G +A1UdDwEB/wQEAwIBBjCB0wYDVR0fBIHLMIHIMIGAoH6gfIZ6bGRhcDovL2RpcmVj +dG9yeS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNUJTIwUm9vdCUyMENsYXNzJTIwMyUy +MENBJTIwMiUyMDIwMDksTz1ELVRydXN0JTIwR21iSCxDPURFP2NlcnRpZmljYXRl +cmV2b2NhdGlvbmxpc3QwQ6BBoD+GPWh0dHA6Ly93d3cuZC10cnVzdC5uZXQvY3Js +L2QtdHJ1c3Rfcm9vdF9jbGFzc18zX2NhXzJfMjAwOS5jcmwwDQYJKoZIhvcNAQEL +BQADggEBAH+X2zDI36ScfSF6gHDOFBJpiBSVYEQBrLLpME+bUMJm2H6NMLVwMeni +acfzcNsgFYbQDfC+rAF1hM5+n02/t2A7nPPKHeJeaNijnZflQGDSNiH+0LS4F9p0 +o3/U37CYAqxva2ssJSRyoWXuJVrl5jLn8t+rSfrzkGkj2wTZ51xY/GXUl77M/C4K +zCUqNQT4YJEVdT1B/yMfGchs64JTBKbkTCJNjYy6zltz7GRUUG3RnFX7acM2w4y8 +PIWmawomDeCTmGCufsYkl4phX5GOZpIJhzbNi5stPvZR1FDUWSi9g/LMKHtThm3Y +Johw1+qRzT65ysCQblrGXnRl11z+o+I= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEQzCCAyugAwIBAgIDCYP0MA0GCSqGSIb3DQEBCwUAMFAxCzAJBgNVBAYTAkRF +MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBD +bGFzcyAzIENBIDIgRVYgMjAwOTAeFw0wOTExMDUwODUwNDZaFw0yOTExMDUwODUw +NDZaMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxELVRydXN0IEdtYkgxKjAoBgNV +BAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAwOTCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAJnxhDRwui+3MKCOvXwEz75ivJn9gpfSegpn +ljgJ9hBOlSJzmY3aFS3nBfwZcyK3jpgAvDw9rKFs+9Z5JUut8Mxk2og+KbgPCdM0 +3TP1YtHhzRnp7hhPTFiu4h7WDFsVWtg6uMQYZB7jM7K1iXdODL/ZlGsTl28So/6Z +qQTMFexgaDbtCHu39b+T7WYxg4zGcTSHThfqr4uRjRxWQa4iN1438h3Z0S0NL2lR +p75mpoo6Kr3HGrHhFPC+Oh25z1uxav60sUYgovseO3Dvk5h9jHOW8sXvhXCtKSb8 +HgQ+HKDYD8tSg2J87otTlZCpV6LqYQXY+U3EJ/pure3511H3a6UCAwEAAaOCASQw +ggEgMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNOUikxiEyoZLsyvcop9Ntea +HNxnMA4GA1UdDwEB/wQEAwIBBjCB3QYDVR0fBIHVMIHSMIGHoIGEoIGBhn9sZGFw +Oi8vZGlyZWN0b3J5LmQtdHJ1c3QubmV0L0NOPUQtVFJVU1QlMjBSb290JTIwQ2xh +c3MlMjAzJTIwQ0ElMjAyJTIwRVYlMjAyMDA5LE89RC1UcnVzdCUyMEdtYkgsQz1E +RT9jZXJ0aWZpY2F0ZXJldm9jYXRpb25saXN0MEagRKBChkBodHRwOi8vd3d3LmQt +dHJ1c3QubmV0L2NybC9kLXRydXN0X3Jvb3RfY2xhc3NfM19jYV8yX2V2XzIwMDku +Y3JsMA0GCSqGSIb3DQEBCwUAA4IBAQA07XtaPKSUiO8aEXUHL7P+PPoeUSbrh/Yp +3uDx1MYkCenBz1UbtDDZzhr+BlGmFaQt77JLvyAoJUnRpjZ3NOhk31KxEcdzes05 +nsKtjHEh8lprr988TlWvsoRlFIm5d8sqMb7Po23Pb0iUMkZv53GMoKaEGTcH8gNF +CSuGdXzfX2lXANtu2KZyIktQ1HWYVt+3GP9DQ1CuekR78HlR10M9p9OB0/DJT7na +xpeG0ILD5EJt/rDiZE4OJudANCa1CInXCGNjOCd1HjPqbqjdn5lPdE2BiYBL3ZqX +KVwvvoFBuYz/6n1gBp7N1z3TLqMVvKjmJuVvw9y4AyHqnxbxLFS1 +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/ +MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT +DkRTVCBSb290IENBIFgzMB4XDTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVow +PzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD +Ew5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +AN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmTrE4O +rz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEq +OLl5CjH9UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9b +xiqKqy69cK3FCxolkHRyxXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw +7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40dutolucbY38EVAjqr2m7xPi71XAicPNaD +aeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV +HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQMA0GCSqG +SIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69 +ikugdB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXr +AvHRAosZy5Q6XkjEGB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZz +R8srzJmwN0jP41ZL9c8PDHIyh8bwRLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5 +JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubSfZGL+T0yjWW06XyxV3bqxbYo +Ob8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDnzCCAoegAwIBAgIBJjANBgkqhkiG9w0BAQUFADBxMQswCQYDVQQGEwJERTEc +MBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxlU2Vj +IFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290IENB +IDIwHhcNOTkwNzA5MTIxMTAwWhcNMTkwNzA5MjM1OTAwWjBxMQswCQYDVQQGEwJE +RTEcMBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxl +U2VjIFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290 +IENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrC6M14IspFLEU +ha88EOQ5bzVdSq7d6mGNlUn0b2SjGmBmpKlAIoTZ1KXleJMOaAGtuU1cOs7TuKhC +QN/Po7qCWWqSG6wcmtoIKyUn+WkjR/Hg6yx6m/UTAtB+NHzCnjwAWav12gz1Mjwr +rFDa1sPeg5TKqAyZMg4ISFZbavva4VhYAUlfckE8FQYBjl2tqriTtM2e66foai1S +NNs671x1Udrb8zH57nGYMsRUFUQM+ZtV7a3fGAigo4aKSe5TBY8ZTNXeWHmb0moc +QqvF1afPaA+W5OFhmHZhyJF81j4A4pFQh+GdCuatl9Idxjp9y7zaAzTVjlsB9WoH +txa2bkp/AgMBAAGjQjBAMB0GA1UdDgQWBBQxw3kbuvVT1xfgiXotF2wKsyudMzAP +BgNVHRMECDAGAQH/AgEFMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOC +AQEAlGRZrTlk5ynrE/5aw4sTV8gEJPB0d8Bg42f76Ymmg7+Wgnxu1MM9756Abrsp +tJh6sTtU6zkXR34ajgv8HzFZMQSyzhfzLMdiNlXiItiJVbSYSKpk+tYcNthEeFpa +IzpXl/V6ME+un2pMSyuOoAPjPuCp1NJ70rOo4nI8rZ7/gFnkm0W09juwzTkZmDLl +6iFhkOQxIY40sfcvNUqFENrnijchvllj4PKFiDFT1FQUhXB59C4Gdyd1Lx+4ivn+ +xbrYNuSD7Odlt79jWvNGr4GUN9RBjNYj1h7P9WgbRGOiWrqnNVmh5XAFmw4jV5mU +Cm26OWMohpLzGITY+9HPBVZkVw== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBl +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv +b3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzExMTEwMDAwMDAwWjBlMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl +cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7c +JpSIqvTO9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYP +mDI2dsze3Tyoou9q+yHyUmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+ +wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW/lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4 +VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpyoeb6pNnVFzF1roV9Iq4/ +AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whfGHdPAgMB +AAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW +BBRF66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYun +pyGd823IDzANBgkqhkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRC +dWKuh+vy1dneVrOfzM4UKLkNl2BcEkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTf +fwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38FnSbNd67IJKusm7Xi+fT8r87cm +NW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i8b5QZ7dsvfPx +H2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe ++o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDljCCAn6gAwIBAgIQC5McOtY5Z+pnI7/Dr5r0SzANBgkqhkiG9w0BAQsFADBl +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv +b3QgRzIwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl +cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzIwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDZ5ygvUj82ckmIkzTz+GoeMVSA +n61UQbVH35ao1K+ALbkKz3X9iaV9JPrjIgwrvJUXCzO/GU1BBpAAvQxNEP4Htecc +biJVMWWXvdMX0h5i89vqbFCMP4QMls+3ywPgym2hFEwbid3tALBSfK+RbLE4E9Hp +EgjAALAcKxHad3A2m67OeYfcgnDmCXRwVWmvo2ifv922ebPynXApVfSr/5Vh88lA +bx3RvpO704gqu52/clpWcTs/1PPRCv4o76Pu2ZmvA9OPYLfykqGxvYmJHzDNw6Yu +YjOuFgJ3RFrngQo8p0Quebg/BLxcoIfhG69Rjs3sLPr4/m3wOnyqi+RnlTGNAgMB +AAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQW +BBTOw0q5mVXyuNtgv6l+vVa1lzan1jANBgkqhkiG9w0BAQsFAAOCAQEAyqVVjOPI +QW5pJ6d1Ee88hjZv0p3GeDgdaZaikmkuOGybfQTUiaWxMTeKySHMq2zNixya1r9I +0jJmwYrA8y8678Dj1JGG0VDjA9tzd29KOVPt3ibHtX2vK0LRdWLjSisCx1BL4Gni +lmwORGYQRI+tBev4eaymG+g3NJ1TyWGqolKvSnAWhsI6yLETcDbYz+70CjTVW0z9 +B5yiutkBclzzTcHdDrEcDcRjvq30FPuJ7KJBDkzMyFdA0G4Dqs0MjomZmWzwPDCv +ON9vvKO+KSAnq3T/EyJ43pdSVR6DtVQgA+6uwE9W3jfMw3+qBCe703e4YtsXfJwo +IhNzbM8m9Yop5w== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICRjCCAc2gAwIBAgIQC6Fa+h3foLVJRK/NJKBs7DAKBggqhkjOPQQDAzBlMQsw +CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu +ZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3Qg +RzMwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQGEwJV +UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu +Y29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwdjAQBgcq +hkjOPQIBBgUrgQQAIgNiAAQZ57ysRGXtzbg/WPuNsVepRC0FFfLvC/8QdJ+1YlJf +Zn4f5dwbRXkLzMZTCp2NXQLZqVneAlr2lSoOjThKiknGvMYDOAdfVdp+CW7if17Q +RSAPWXYQ1qAk8C3eNvJsKTmjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/ +BAQDAgGGMB0GA1UdDgQWBBTL0L2p4ZgFUaFNN6KDec6NHSrkhDAKBggqhkjOPQQD +AwNnADBkAjAlpIFFAmsSS3V0T8gj43DydXLefInwz5FyYZ5eEJJZVrmDxxDnOOlY +JjZ91eQ0hjkCMHw2U/Aw5WJjOpnitqM7mzT6HtoQknFekROn3aRukswy1vUhZscv +6pZjamVFkpUBtA== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD +QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT +MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j +b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB +CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97 +nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt +43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P +T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4 +gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO +BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR +TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw +DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr +hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg +06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF +PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls +YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk +CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH +MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT +MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j +b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI +2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx +1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ +q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz +tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ +vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP +BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV +5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY +1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4 +NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG +Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91 +8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe +pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl +MrY= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICPzCCAcWgAwIBAgIQBVVWvPJepDU1w6QP1atFcjAKBggqhkjOPQQDAzBhMQsw +CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu +ZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMzAe +Fw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVTMRUw +EwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20x +IDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEczMHYwEAYHKoZIzj0CAQYF +K4EEACIDYgAE3afZu4q4C/sLfyHS8L6+c/MzXRq8NOrexpu80JX28MzQC7phW1FG +fp4tn+6OYwwX7Adw9c+ELkCDnOg/QW07rdOkFFk2eJ0DQ+4QE2xy3q6Ip6FrtUPO +Z9wj/wMco+I+o0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAd +BgNVHQ4EFgQUs9tIpPmhxdiuNkHMEWNpYim8S8YwCgYIKoZIzj0EAwMDaAAwZQIx +AK288mw/EkrRLTnDCgmXc/SINoyIJ7vmiI1Qhadj+Z4y3maTD/HMsQmP3Wyr+mt/ +oAIwOWZbwmSNuJ5Q3KjVSaLtx9zRSX8XAbjIho9OjIgrqJqpisXRAL34VOKa5Vt8 +sycX +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j +ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL +MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 +LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug +RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm ++9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW +PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM +xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB +Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3 +hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg +EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF +MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA +FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec +nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z +eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF +hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2 +Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe +vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep ++OkuE6N36B9K +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFkDCCA3igAwIBAgIQBZsbV56OITLiOQe9p3d1XDANBgkqhkiG9w0BAQwFADBi +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3Qg +RzQwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBiMQswCQYDVQQGEwJV +UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu +Y29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz7MKnJS7JIT3y +ithZwuEppz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS5F/WBTxSD1If +xp4VpX6+n6lXFllVcq9ok3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7bXHiLQwb7iDV +ySAdYyktzuxeTsiT+CFhmzTrBcZe7FsavOvJz82sNEBfsXpm7nfISKhmV1efVFiO +DCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGYQJB5w3jHtrHEtWoYOAMQ +jdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14Ztk6MUSaM0C/ +CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2h4mXaXpI8OCi +EhtmmnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt6zPZxd9LBADM +fRyVw4/3IbKyEbe7f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPRiQfhvbfmQ6QY +uKZ3AeEPlAwhHbJUKSWJbOUOUlFHdL4mrLZBdd56rF+NP8m800ERElvlEFDrMcXK +chYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8oR7FwI+isX4KJpn15GkvmB0t +9dmpsh3lGwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +hjAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wDQYJKoZIhvcNAQEMBQAD +ggIBALth2X2pbL4XxJEbw6GiAI3jZGgPVs93rnD5/ZpKmbnJeFwMDF/k5hQpVgs2 +SV1EY+CtnJYYZhsjDT156W1r1lT40jzBQ0CuHVD1UvyQO7uYmWlrx8GnqGikJ9yd ++SeuMIW59mdNOj6PWTkiU0TryF0Dyu1Qen1iIQqAyHNm0aAFYF/opbSnr6j3bTWc +fFqK1qI4mfN4i/RN0iAL3gTujJtHgXINwBQy7zBZLq7gcfJW5GqXb5JQbZaNaHqa +sjYUegbyJLkJEVDXCLG4iXqEI2FCKeWjzaIgQdfRnGTZ6iahixTXTBmyUEFxPT9N +cCOGDErcgdLMMpSEDQgJlxxPwO5rIHQw0uA5NBCFIRUBCOhVMt5xSdkoF1BN5r5N +0XWs0Mr7QbhDparTwwVETyw2m+L64kW4I1NsBm9nVX9GtUw/bihaeSbSpKhil9Ie +4u1Ki7wb/UdKDd9nZn6yW0HQO+T0O/QEY+nvwlQAUaCKKsnOeMzV6ocEGLPOr0mI +r/OSmbaz5mEP0oUA51Aa5BuVnRmhuZyxm7EAHu/QD09CbMkKvO5D+jpxpchNJqU1 +/YldvIViHTLSoCtU7ZpXwdv6EM8Zt4tKG48BtieVU+i2iW1bvGjUI+iLUaJW+fCm +gKDWHrO8Dw9TdSmq6hN35N6MgSGtBxBHEa2HPQfRdbzP82Z+ +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIGSzCCBDOgAwIBAgIIamg+nFGby1MwDQYJKoZIhvcNAQELBQAwgbIxCzAJBgNV +BAYTAlRSMQ8wDQYDVQQHDAZBbmthcmExQDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBC +aWxpxZ9pbSBUZWtub2xvamlsZXJpIHZlIEhpem1ldGxlcmkgQS7Fni4xJjAkBgNV +BAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBNZXJrZXppMSgwJgYDVQQDDB9FLVR1 +Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTEzMDMwNTEyMDk0OFoXDTIz +MDMwMzEyMDk0OFowgbIxCzAJBgNVBAYTAlRSMQ8wDQYDVQQHDAZBbmthcmExQDA+ +BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xvamlsZXJpIHZlIEhp +em1ldGxlcmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBN +ZXJrZXppMSgwJgYDVQQDDB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5 +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA4vU/kwVRHoViVF56C/UY +B4Oufq9899SKa6VjQzm5S/fDxmSJPZQuVIBSOTkHS0vdhQd2h8y/L5VMzH2nPbxH +D5hw+IyFHnSOkm0bQNGZDbt1bsipa5rAhDGvykPL6ys06I+XawGb1Q5KCKpbknSF +Q9OArqGIW66z6l7LFpp3RMih9lRozt6Plyu6W0ACDGQXwLWTzeHxE2bODHnv0ZEo +q1+gElIwcxmOj+GMB6LDu0rw6h8VqO4lzKRG+Bsi77MOQ7osJLjFLFzUHPhdZL3D +k14opz8n8Y4e0ypQBaNV2cvnOVPAmJ6MVGKLJrD3fY185MaeZkJVgkfnsliNZvcH +fC425lAcP9tDJMW/hkd5s3kc91r0E+xs+D/iWR+V7kI+ua2oMoVJl0b+SzGPWsut +dEcf6ZG33ygEIqDUD13ieU/qbIWGvaimzuT6w+Gzrt48Ue7LE3wBf4QOXVGUnhMM +ti6lTPk5cDZvlsouDERVxcr6XQKj39ZkjFqzAQqptQpHF//vkUAqjqFGOjGY5RH8 +zLtJVor8udBhmm9lbObDyz51Sf6Pp+KJxWfXnUYTTjF2OySznhFlhqt/7x3U+Lzn +rFpct1pHXFXOVbQicVtbC/DP3KBhZOqp12gKY6fgDT+gr9Oq0n7vUaDmUStVkhUX +U8u3Zg5mTPj5dUyQ5xJwx0UCAwEAAaNjMGEwHQYDVR0OBBYEFC7j27JJ0JxUeVz6 +Jyr+zE7S6E5UMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAULuPbsknQnFR5 +XPonKv7MTtLoTlQwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQAF +Nzr0TbdF4kV1JI+2d1LoHNgQk2Xz8lkGpD4eKexd0dCrfOAKkEh47U6YA5n+KGCR +HTAduGN8qOY1tfrTYXbm1gdLymmasoR6d5NFFxWfJNCYExL/u6Au/U5Mh/jOXKqY +GwXgAEZKgoClM4so3O0409/lPun++1ndYYRP0lSWE2ETPo+Aab6TR7U1Q9Jauz1c +77NCR807VRMGsAnb/WP2OogKmW9+4c4bU2pEZiNRCHu8W1Ki/QY3OEBhj0qWuJA3 ++GbHeJAAFS6LrVE1Uweoa2iu+U48BybNCAVwzDk/dr2l02cmAYamU9JgO3xDf1WK +vJUawSg5TB9D0pH0clmKuVb8P7Sd2nCcdlqMQ1DujjByTd//SffGqWfZbawCEeI6 +FiWnWAjLb1NBnEg4R2gz0dfHj9R0IdTDBZB6/86WiLEVKV0jq9BgoRJP3vQXzTLl +yb/IQ639Lo7xr+L0mPoSHyDYwKcMhcWQ9DstliaxLL5Mq+ux0orJ23gTDx4JnW2P +AJ8C2sH6H3p6CcRK5ogql5+Ji/03X186zjhZhkuvcQu02PJwT58yE+Owp1fl2tpD +y4Q08ijE6m30Ku/Ba3ba+367hTzSU8JNvnHhRdH9I2cNE3X7z2VnIp2usAnRCf8d +NL/+I5c30jn6PQ0GC7TbO6Orb1wdtn7os4I07QZcJA== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFVjCCBD6gAwIBAgIQ7is969Qh3hSoYqwE893EATANBgkqhkiG9w0BAQUFADCB +8zELMAkGA1UEBhMCRVMxOzA5BgNVBAoTMkFnZW5jaWEgQ2F0YWxhbmEgZGUgQ2Vy +dGlmaWNhY2lvIChOSUYgUS0wODAxMTc2LUkpMSgwJgYDVQQLEx9TZXJ2ZWlzIFB1 +YmxpY3MgZGUgQ2VydGlmaWNhY2lvMTUwMwYDVQQLEyxWZWdldSBodHRwczovL3d3 +dy5jYXRjZXJ0Lm5ldC92ZXJhcnJlbCAoYykwMzE1MDMGA1UECxMsSmVyYXJxdWlh +IEVudGl0YXRzIGRlIENlcnRpZmljYWNpbyBDYXRhbGFuZXMxDzANBgNVBAMTBkVD +LUFDQzAeFw0wMzAxMDcyMzAwMDBaFw0zMTAxMDcyMjU5NTlaMIHzMQswCQYDVQQG +EwJFUzE7MDkGA1UEChMyQWdlbmNpYSBDYXRhbGFuYSBkZSBDZXJ0aWZpY2FjaW8g +KE5JRiBRLTA4MDExNzYtSSkxKDAmBgNVBAsTH1NlcnZlaXMgUHVibGljcyBkZSBD +ZXJ0aWZpY2FjaW8xNTAzBgNVBAsTLFZlZ2V1IGh0dHBzOi8vd3d3LmNhdGNlcnQu +bmV0L3ZlcmFycmVsIChjKTAzMTUwMwYDVQQLEyxKZXJhcnF1aWEgRW50aXRhdHMg +ZGUgQ2VydGlmaWNhY2lvIENhdGFsYW5lczEPMA0GA1UEAxMGRUMtQUNDMIIBIjAN +BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsyLHT+KXQpWIR4NA9h0X84NzJB5R +85iKw5K4/0CQBXCHYMkAqbWUZRkiFRfCQ2xmRJoNBD45b6VLeqpjt4pEndljkYRm +4CgPukLjbo73FCeTae6RDqNfDrHrZqJyTxIThmV6PttPB/SnCWDaOkKZx7J/sxaV +HMf5NLWUhdWZXqBIoH7nF2W4onW4HvPlQn2v7fOKSGRdghST2MDk/7NQcvJ29rNd +QlB50JQ+awwAvthrDk4q7D7SzIKiGGUzE3eeml0aE9jD2z3Il3rucO2n5nzbcc8t +lGLfbdb1OL4/pYUKGbio2Al1QnDE6u/LDsg0qBIimAy4E5S2S+zw0JDnJwIDAQAB +o4HjMIHgMB0GA1UdEQQWMBSBEmVjX2FjY0BjYXRjZXJ0Lm5ldDAPBgNVHRMBAf8E +BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUoMOLRKo3pUW/l4Ba0fF4 +opvpXY0wfwYDVR0gBHgwdjB0BgsrBgEEAfV4AQMBCjBlMCwGCCsGAQUFBwIBFiBo +dHRwczovL3d3dy5jYXRjZXJ0Lm5ldC92ZXJhcnJlbDA1BggrBgEFBQcCAjApGidW +ZWdldSBodHRwczovL3d3dy5jYXRjZXJ0Lm5ldC92ZXJhcnJlbCAwDQYJKoZIhvcN +AQEFBQADggEBAKBIW4IB9k1IuDlVNZyAelOZ1Vr/sXE7zDkJlF7W2u++AVtd0x7Y +/X1PzaBB4DSTv8vihpw3kpBWHNzrKQXlxJ7HNd+KDM3FIUPpqojlNcAZQmNaAl6k +SBg6hW/cnbw/nZzBh7h6YQjpdwt/cKt63dmXLGQehb+8dJahw3oS7AwaboMMPOhy +Rp/7SNVel+axofjk70YllJyJ22k4vuxcDlbHZVHlUIiIv0LVKz3l+bqeLrPK9HOS +Agu+TGbrIP65y7WZf+a2E/rKS03Z7lNGBjvGTq2TWoF+bCpLagVFjPIhpDGQh2xl +nJ2lYJU6Un/10asIbvPuW/mIPX64b24D5EI= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEAzCCAuugAwIBAgIQVID5oHPtPwBMyonY43HmSjANBgkqhkiG9w0BAQUFADB1 +MQswCQYDVQQGEwJFRTEiMCAGA1UECgwZQVMgU2VydGlmaXRzZWVyaW1pc2tlc2t1 +czEoMCYGA1UEAwwfRUUgQ2VydGlmaWNhdGlvbiBDZW50cmUgUm9vdCBDQTEYMBYG +CSqGSIb3DQEJARYJcGtpQHNrLmVlMCIYDzIwMTAxMDMwMTAxMDMwWhgPMjAzMDEy +MTcyMzU5NTlaMHUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKDBlBUyBTZXJ0aWZpdHNl +ZXJpbWlza2Vza3VzMSgwJgYDVQQDDB9FRSBDZXJ0aWZpY2F0aW9uIENlbnRyZSBS +b290IENBMRgwFgYJKoZIhvcNAQkBFglwa2lAc2suZWUwggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQDIIMDs4MVLqwd4lfNE7vsLDP90jmG7sWLqI9iroWUy +euuOF0+W2Ap7kaJjbMeMTC55v6kF/GlclY1i+blw7cNRfdCT5mzrMEvhvH2/UpvO +bntl8jixwKIy72KyaOBhU8E2lf/slLo2rpwcpzIP5Xy0xm90/XsY6KxX7QYgSzIw +WFv9zajmofxwvI6Sc9uXp3whrj3B9UiHbCe9nyV0gVWw93X2PaRka9ZP585ArQ/d +MtO8ihJTmMmJ+xAdTX7Nfh9WDSFwhfYggx/2uh8Ej+p3iDXE/+pOoYtNP2MbRMNE +1CV2yreN1x5KZmTNXMWcg+HCCIia7E6j8T4cLNlsHaFLAgMBAAGjgYowgYcwDwYD +VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBLyWj7qVhy/ +zQas8fElyalL1BSZMEUGA1UdJQQ+MDwGCCsGAQUFBwMCBggrBgEFBQcDAQYIKwYB +BQUHAwMGCCsGAQUFBwMEBggrBgEFBQcDCAYIKwYBBQUHAwkwDQYJKoZIhvcNAQEF +BQADggEBAHv25MANqhlHt01Xo/6tu7Fq1Q+e2+RjxY6hUFaTlrg4wCQiZrxTFGGV +v9DHKpY5P30osxBAIWrEr7BSdxjhlthWXePdNl4dp1BUoMUq5KqMlIpPnTX/dqQG +E5Gion0ARD9V04I8GtVbvFZMIi5GQ4okQC3zErg7cBqklrkar4dBGmoYDQZPxz5u +uSlNDUmJEYcyW+ZLBMjkXOZ0c5RdFpgTlf7727FE5TpwrDdr5rMzcijJs1eg9gIW +iAYLtqZLICjU3j2LrTcFU3T+bsy8QxdxXvnFzBqpYe73dgzzcvRyrc9yAjYHR8/v +GVCJYMzpJJUPwssd8m92kMfMdcGWxZ0= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEKjCCAxKgAwIBAgIEOGPe+DANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChML +RW50cnVzdC5uZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBp +bmNvcnAuIGJ5IHJlZi4gKGxpbWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5 +IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNVBAMTKkVudHJ1c3QubmV0IENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEyMjQxNzUwNTFaFw0yOTA3 +MjQxNDE1MTJaMIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3d3d3 +LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxp +YWIuKTElMCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEG +A1UEAxMqRW50cnVzdC5uZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgp +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArU1LqRKGsuqjIAcVFmQq +K0vRvwtKTY7tgHalZ7d4QMBzQshowNtTK91euHaYNZOLGp18EzoOH1u3Hs/lJBQe +sYGpjX24zGtLA/ECDNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSrhRSGlVuX +MlBvPci6Zgzj/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVT +XTzWnLLPKQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/ +HoZdenoVve8AjhUiVBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH +4QIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV +HQ4EFgQUVeSB0RGAvtiJuQijMfmhJAkWuXAwDQYJKoZIhvcNAQEFBQADggEBADub +j1abMOdTmXx6eadNl9cZlZD7Bh/KM3xGY4+WZiT6QBshJ8rmcnPyT/4xmf3IDExo +U8aAghOY+rat2l098c5u9hURlIIM7j+VrxGrD9cv3h8Dj1csHsm7mhpElesYT6Yf +zX1XEC+bBAlahLVu2B064dae0Wx5XnkcFMXj0EyTO2U87d89vqbllRrDtRnDvV5b +u/8j72gZyxKTJ1wDLW8w0B62GqzeWvfRqqgnpv55gcR5mTNXuhKwqeBCbJPKVt7+ +bYQLCIt+jerXmCHG8+c8eS9enNFMFY3h7CI3zJpDC5fcgJCNs2ebb0gIFVbPv/Er +fF6adulZkMV8gzURZVE= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMC +VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0 +Lm5ldC9DUFMgaXMgaW5jb3Jwb3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMW +KGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsGA1UEAxMkRW50cnVzdCBSb290IENl +cnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2MTEyNzIwMjM0MloXDTI2MTEyNzIw +NTM0MlowgbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMTkw +NwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29ycG9yYXRlZCBieSBy +ZWZlcmVuY2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4xLTArBgNV +BAMTJEVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBALaVtkNC+sZtKm9I35RMOVcF7sN5EUFo +Nu3s/poBj6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYszA9u3g3s+IIRe7bJWKKf4 +4LlAcTfFy0cOlypowCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOwwCj0Yzfv9 +KlmaI5UXLEWeH25DeW0MXJj+SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGI +rb68j6xSlkuqUY3kEzEZ6E5Nn9uss2rVvDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi +94DkZfs0Nw4pgHBNrziGLp5/V6+eF67rHMsoIV+2HNjnogQi+dPa2MsCAwEAAaOB +sDCBrTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zArBgNVHRAEJDAi +gA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1MzQyWjAfBgNVHSMEGDAWgBRo +kORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DHhmak8fdLQ/uE +vW0wHQYJKoZIhvZ9B0EABBAwDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUA +A4IBAQCT1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9t +O1KzKtvn1ISMY/YPyyYBkVBs9F8U4pN0wBOeMDpQ47RgxRzwIkSNcUesyBrJ6Zua +AGAT/3B+XxFNSRuzFVJ7yVTav52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP +9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE2jTSW3iDVuycNsMm4hH2Z0kdkquM++v/ +eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0tHuu2guQOHXvgR1m +0vdXcDazv/wor3ElhVsT/h5/WrQ8 +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIC+TCCAoCgAwIBAgINAKaLeSkAAAAAUNCR+TAKBggqhkjOPQQDAzCBvzELMAkG +A1UEBhMCVVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3 +d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDEyIEVu +dHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEzMDEGA1UEAxMq +RW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRUMxMB4XDTEy +MTIxODE1MjUzNloXDTM3MTIxODE1NTUzNlowgb8xCzAJBgNVBAYTAlVTMRYwFAYD +VQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1c3QubmV0 +L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxMiBFbnRydXN0LCBJbmMuIC0g +Zm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxMzAxBgNVBAMTKkVudHJ1c3QgUm9vdCBD +ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEVDMTB2MBAGByqGSM49AgEGBSuBBAAi +A2IABIQTydC6bUF74mzQ61VfZgIaJPRbiWlH47jCffHyAsWfoPZb1YsGGYZPUxBt +ByQnoaD41UcZYUx9ypMn6nQM72+WCf5j7HBdNq1nd67JnXxVRDqiY1Ef9eNi1KlH +Bz7MIKNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O +BBYEFLdj5xrdjekIplWDpOBqUEFlEUJJMAoGCCqGSM49BAMDA2cAMGQCMGF52OVC +R98crlOZF7ZvHH3hvxGU0QOIdeSNiaSKd0bebWHvAvX7td/M/k7//qnmpwIwW5nX +hTcGtXsI/esni0qU+eH6p44mCOh8kmhtc9hvJqwhAriZtyZBWyVgrtBIGu4G +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEPjCCAyagAwIBAgIESlOMKDANBgkqhkiG9w0BAQsFADCBvjELMAkGA1UEBhMC +VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50 +cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3Qs +IEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVz +dCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIwHhcNMDkwNzA3MTcy +NTU0WhcNMzAxMjA3MTc1NTU0WjCBvjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUVu +dHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwt +dGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0 +aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmlj +YXRpb24gQXV0aG9yaXR5IC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQC6hLZy254Ma+KZ6TABp3bqMriVQRrJ2mFOWHLP/vaCeb9zYQYKpSfYs1/T +RU4cctZOMvJyig/3gxnQaoCAAEUesMfnmr8SVycco2gvCoe9amsOXmXzHHfV1IWN +cCG0szLni6LVhjkCsbjSR87kyUnEO6fe+1R9V77w6G7CebI6C1XiUJgWMhNcL3hW +wcKUs/Ja5CeanyTXxuzQmyWC48zCxEXFjJd6BmsqEZ+pCm5IO2/b1BEZQvePB7/1 +U1+cPvQXLOZprE4yTGJ36rfo5bs0vBmLrpxR57d+tVOxMyLlbc9wPBr64ptntoP0 +jaWvYkxN4FisZDQSA/i2jZRjJKRxAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAP +BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqciZ60B7vfec7aVHUbI2fkBJmqzAN +BgkqhkiG9w0BAQsFAAOCAQEAeZ8dlsa2eT8ijYfThwMEYGprmi5ZiXMRrEPR9RP/ +jTkrwPK9T3CMqS/qF8QLVJ7UG5aYMzyorWKiAHarWWluBh1+xLlEjZivEtRh2woZ +Rkfz6/djwUAFQKXSt/S1mja/qYh2iARVBCuch38aNzx+LaUa2NSJXsq9rD1s2G2v +1fN2D807iDginWyTmsQ9v4IbZT+mD12q/OWyFcq1rca8PdCE6OoGcrBNOTJ4vz4R +nAuknZoh8/CbCzB428Hch0P+vGOaysXCHMnHjf87ElgI5rY97HosTvuDls4MPGmH +VHOkc8KT/1EQrBVUAdj8BbGJoX90g5pJ19xOe4pIb4tF9g== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFiDCCA3CgAwIBAgIIfQmX/vBH6nowDQYJKoZIhvcNAQELBQAwYjELMAkGA1UE +BhMCQ04xMjAwBgNVBAoMKUdVQU5HIERPTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZ +IENPLixMVEQuMR8wHQYDVQQDDBZHRENBIFRydXN0QVVUSCBSNSBST09UMB4XDTE0 +MTEyNjA1MTMxNVoXDTQwMTIzMTE1NTk1OVowYjELMAkGA1UEBhMCQ04xMjAwBgNV +BAoMKUdVQU5HIERPTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZIENPLixMVEQuMR8w +HQYDVQQDDBZHRENBIFRydXN0QVVUSCBSNSBST09UMIICIjANBgkqhkiG9w0BAQEF +AAOCAg8AMIICCgKCAgEA2aMW8Mh0dHeb7zMNOwZ+Vfy1YI92hhJCfVZmPoiC7XJj +Dp6L3TQsAlFRwxn9WVSEyfFrs0yw6ehGXTjGoqcuEVe6ghWinI9tsJlKCvLriXBj +TnnEt1u9ol2x8kECK62pOqPseQrsXzrj/e+APK00mxqriCZ7VqKChh/rNYmDf1+u +KU49tm7srsHwJ5uu4/Ts765/94Y9cnrrpftZTqfrlYwiOXnhLQiPzLyRuEH3FMEj +qcOtmkVEs7LXLM3GKeJQEK5cy4KOFxg2fZfmiJqwTTQJ9Cy5WmYqsBebnh52nUpm +MUHfP/vFBu8btn4aRjb3ZGM74zkYI+dndRTVdVeSN72+ahsmUPI2JgaQxXABZG12 +ZuGR224HwGGALrIuL4xwp9E7PLOR5G62xDtw8mySlwnNR30YwPO7ng/Wi64HtloP +zgsMR6flPri9fcebNaBhlzpBdRfMK5Z3KpIhHtmVdiBnaM8Nvd/WHwlqmuLMc3Gk +L30SgLdTMEZeS1SZD2fJpcjyIMGC7J0R38IC+xo70e0gmu9lZJIQDSri3nDxGGeC +jGHeuLzRL5z7D9Ar7Rt2ueQ5Vfj4oR24qoAATILnsn8JuLwwoC8N9VKejveSswoA +HQBUlwbgsQfZxw9cZX08bVlX5O2ljelAU58VS6Bx9hoh49pwBiFYFIeFd3mqgnkC +AwEAAaNCMEAwHQYDVR0OBBYEFOLJQJ9NzuiaoXzPDj9lxSmIahlRMA8GA1UdEwEB +/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQDRSVfg +p8xoWLoBDysZzY2wYUWsEe1jUGn4H3++Fo/9nesLqjJHdtJnJO29fDMylyrHBYZm +DRd9FBUb1Ov9H5r2XpdptxolpAqzkT9fNqyL7FeoPueBihhXOYV0GkLH6VsTX4/5 +COmSdI31R9KrO9b7eGZONn356ZLpBN79SWP8bfsUcZNnL0dKt7n/HipzcEYwv1ry +L3ml4Y0M2fmyYzeMN2WFcGpcWwlyua1jPLHd+PwyvzeG5LuOmCd+uh8W4XAR8gPf +JWIyJyYYMoSf/wA6E7qaTfRPuBRwIrHKK5DOKcFw9C+df/KQHtZa37dG/OaG+svg +IHZ6uqbL9XzeYqWxi+7egmaKTjowHz+Ay60nugxe19CxVsp3cbK1daFQqUBDF8Io +2c9Si1vIY9RCPqAzekYu9wogRlR+ak8x8YF+QnQ4ZXMn7sZ8uI7XpTrXmKGcjBBV +09tL7ECQ8s1uV9JiDnxXk7Gnbc2dg7sq5+W2O3FYrf3RRbxake5TFW/TRQl1brqQ +XR4EzzffHqhmsYzmIGrv/EhOdJhCrylvLmrH+33RZjEizIYAfmaDDEL0vTSSwxrq +T8p+ck0LcIymSLumoRT2+1hEmRSuqguTaaApJUqlyyvdimYHFngVV3Eb7PVHhPOe +MTd61X8kreS8/f3MboPoDKi3QWwH3b08hpcv0g== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT +MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i +YWwgQ0EwHhcNMDIwNTIxMDQwMDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQG +EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMSR2VvVHJ1c3Qg +R2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2swYYzD9 +9BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjoBbdq +fnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDv +iS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU +1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+ +bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5aszPeE4uwc2hGKceeoW +MPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTA +ephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVkDBF9qn1l +uMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKIn +Z57QzxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfS +tQWVYrmm3ok9Nns4d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcF +PseKUgzbFbS9bZvlxrFUaKnjaZC2mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Un +hw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6pXE0zX5IJL4hmXXeXxx12E6nV +5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvmMw== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDfDCCAmSgAwIBAgIQGKy1av1pthU6Y2yv2vrEoTANBgkqhkiG9w0BAQUFADBY +MQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjExMC8GA1UEAxMo +R2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEx +MjcwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMFgxCzAJBgNVBAYTAlVTMRYwFAYDVQQK +Ew1HZW9UcnVzdCBJbmMuMTEwLwYDVQQDEyhHZW9UcnVzdCBQcmltYXJ5IENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEAvrgVe//UfH1nrYNke8hCUy3f9oQIIGHWAVlqnEQRr+92/ZV+zmEwu3qDXwK9 +AWbK7hWNb6EwnL2hhZ6UOvNWiAAxz9juapYC2e0DjPt1befquFUWBRaa9OBesYjA +ZIVcFU2Ix7e64HXprQU9nceJSOC7KMgD4TCTZF5SwFlwIjVXiIrxlQqD17wxcwE0 +7e9GceBrAqg1cmuXm2bgyxx5X9gaBGgeRwLmnWDiNpcB3841kt++Z8dtd1k7j53W +kBWUvEI0EME5+bEnPn7WinXFsq+W06Lem+SYvn3h6YGttm/81w7a4DSwDRp35+MI +mO9Y+pyEtzavwt+s0vQQBnBxNQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4G +A1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQULNVQQZcVi/CPNmFbSvtr2ZnJM5IwDQYJ +KoZIhvcNAQEFBQADggEBAFpwfyzdtzRP9YZRqSa+S7iq8XEN3GHHoOo0Hnp3DwQ1 +6CePbJC/kRYkRj5KTs4rFtULUh38H2eiAkUxT87z+gOneZ1TatnaYzr4gNfTmeGl +4b7UVXGYNTq+k+qurUKykG/g/CFNNWMziUnWm07Kx+dOCQD32sfvmWKZd7aVIl6K +oKv0uHiYyjgZmclynnjNS6yvGaBzEi38wkG6gZHaFloxt/m0cYASSJlyc1pZU8Fj +UjPtp8nSOQJw+uCxQmYpqptR7TBUIhRf2asdweSU8Pj1K/fqynhG1riR/aYNKxoU +AT6A8EKglQdebc3MS6RFjasS6LPeWuWgfOgPIh1a6Vk= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICrjCCAjWgAwIBAgIQPLL0SAoA4v7rJDteYD7DazAKBggqhkjOPQQDAzCBmDEL +MAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsTMChj +KSAyMDA3IEdlb1RydXN0IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE2 +MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0 +eSAtIEcyMB4XDTA3MTEwNTAwMDAwMFoXDTM4MDExODIzNTk1OVowgZgxCzAJBgNV +BAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAoYykgMjAw +NyBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0BgNV +BAMTLUdlb1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBH +MjB2MBAGByqGSM49AgEGBSuBBAAiA2IABBWx6P0DFUPlrOuHNxFi79KDNlJ9RVcL +So17VDs6bl8VAsBQps8lL33KSLjHUGMcKiEIfJo22Av+0SbFWDEwKCXzXV2juLal +tJLtbCyf691DiaI8S0iRHVDsJt/WYC69IaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO +BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBVfNVdRVfslsq0DafwBo/q+EVXVMAoG +CCqGSM49BAMDA2cAMGQCMGSWWaboCd6LuvpaiIjwH5HTRqjySkwCY/tsXzjbLkGT +qQ7mndwxHLKgpxgceeHHNgIwOlavmnRs9vuD4DPTCF+hnMJbn0bWtsuRBmOiBucz +rD6ogRLQy7rQkgu2npaqBA+K +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIID/jCCAuagAwIBAgIQFaxulBmyeUtB9iepwxgPHzANBgkqhkiG9w0BAQsFADCB +mDELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsT +MChjKSAyMDA4IEdlb1RydXN0IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25s +eTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhv +cml0eSAtIEczMB4XDTA4MDQwMjAwMDAwMFoXDTM3MTIwMTIzNTk1OVowgZgxCzAJ +BgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAoYykg +MjAwOCBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0 +BgNVBAMTLUdlb1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg +LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANziXmJYHTNXOTIz ++uvLh4yn1ErdBojqZI4xmKU4kB6Yzy5jK/BGvESyiaHAKAxJcCGVn2TAppMSAmUm +hsalifD614SgcK9PGpc/BkTVyetyEH3kMSj7HGHmKAdEc5IiaacDiGydY8hS2pgn +5whMcD60yRLBxWeDXTPzAxHsatBT4tG6NmCUgLthY2xbF37fQJQeqw3CIShwiP/W +JmxsYAQlTlV+fe+/lEjetx3dcI0FX4ilm/LC7urRQEFtYjgdVgbFA0dRIBn8exAL +DmKudlW/X3e+PkkBUz2YJQN2JFodtNuJ6nnltrM7P7pMKEF/BqxqjsHQ9gUdfeZC +huOl1UcCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYw +HQYDVR0OBBYEFMR5yo6hTgMdHNxr2zFblD4/MH8tMA0GCSqGSIb3DQEBCwUAA4IB +AQAtxRPPVoB7eni9n64smefv2t+UXglpp+duaIy9cr5HqQ6XErhK8WTTOd8lNNTB +zU6B8A8ExCSzNJbGpqow32hhc9f5joWJ7w5elShKKiePEI4ufIbEAp7aDHdlDkQN +kv39sxY2+hENHYwOB4lqKVb3cvTdFZx3NWZXqxNT2I7BQMXXExZacse3aQHEerGD +AWh9jUGhlBjBJVz88P6DAod8DQ3PLghcSkANPuyBYeYk28rgDi0Hsj5W3I31QYUH +SJsMC8tJP33st/3LjWeJGqvtux6jAAgIFyqCXDFdRootD4abdNlF+9RAsXqqaC2G +spki4cErx5z481+oghLrGREt +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFaDCCA1CgAwIBAgIBATANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJVUzEW +MBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEeMBwGA1UEAxMVR2VvVHJ1c3QgVW5pdmVy +c2FsIENBMB4XDTA0MDMwNDA1MDAwMFoXDTI5MDMwNDA1MDAwMFowRTELMAkGA1UE +BhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xHjAcBgNVBAMTFUdlb1RydXN0 +IFVuaXZlcnNhbCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKYV +VaCjxuAfjJ0hUNfBvitbtaSeodlyWL0AG0y/YckUHUWCq8YdgNY96xCcOq9tJPi8 +cQGeBvV8Xx7BDlXKg5pZMK4ZyzBIle0iN430SppyZj6tlcDgFgDgEB8rMQ7XlFTT +QjOgNB0eRXbdT8oYN+yFFXoZCPzVx5zw8qkuEKmS5j1YPakWaDwvdSEYfyh3peFh +F7em6fgemdtzbvQKoiFs7tqqhZJmr/Z6a4LauiIINQ/PQvE1+mrufislzDoR5G2v +c7J2Ha3QsnhnGqQ5HFELZ1aD/ThdDc7d8Lsrlh/eezJS/R27tQahsiFepdaVaH/w +mZ7cRQg+59IJDTWU3YBOU5fXtQlEIGQWFwMCTFMNaN7VqnJNk22CDtucvc+081xd +VHppCZbW2xHBjXWotM85yM48vCR85mLK4b19p71XZQvk/iXttmkQ3CgaRr0BHdCX +teGYO8A3ZNY9lO4L4fUorgtWv3GLIylBjobFS1J72HGrH4oVpjuDWtdYAVHGTEHZ +f9hBZ3KiKN9gg6meyHv8U3NyWfWTehd2Ds735VzZC1U0oqpbtWpU5xPKV+yXbfRe +Bi9Fi1jUIxaS5BZuKGNZMN9QAZxjiRqf2xeUgnA3wySemkfWWspOqGmJch+RbNt+ +nhutxx9z3SxPGWX9f5NAEC7S8O08ni4oPmkmM8V7AgMBAAGjYzBhMA8GA1UdEwEB +/wQFMAMBAf8wHQYDVR0OBBYEFNq7LqqwDLiIJlF0XG0D08DYj3rWMB8GA1UdIwQY +MBaAFNq7LqqwDLiIJlF0XG0D08DYj3rWMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG +9w0BAQUFAAOCAgEAMXjmx7XfuJRAyXHEqDXsRh3ChfMoWIawC/yOsjmPRFWrZIRc +aanQmjg8+uUfNeVE44B5lGiku8SfPeE0zTBGi1QrlaXv9z+ZhP015s8xxtxqv6fX +IwjhmF7DWgh2qaavdy+3YL1ERmrvl/9zlcGO6JP7/TG37FcREUWbMPEaiDnBTzyn +ANXH/KttgCJwpQzgXQQpAvvLoJHRfNbDflDVnVi+QTjruXU8FdmbyUqDWcDaU/0z +uzYYm4UPFd3uLax2k7nZAY1IEKj79TiG8dsKxr2EoyNB3tZ3b4XUhRxQ4K5RirqN +Pnbiucon8l+f725ZDQbYKxek0nxru18UGkiPGkzns0ccjkxFKyDuSN/n3QmOGKja +QI2SJhFTYXNd673nxE0pN2HrrDktZy4W1vUAg4WhzH92xH3kt0tm7wNFYGm2DFKW +koRepqO1pD4r2czYG0eq8kTaT/kD6PAUyz/zg97QwVTjt+gKN02LIFkDMBmhLMi9 +ER/frslKxfMnZmaGrGiR/9nmUxwPi1xpZQomyB40w11Re9epnAahNt3ViZS82eQt +DF4JbAiXfKM9fJP/P6EUp8+1Xevb2xzEdt+Iub1FBZUbrvxGakyvSOPOrg/Sfuvm +bJxPgWp6ZKy7PtXny3YuxadIwVyQD8vIP/rmMuGNG2+k5o7Y+SlIis5z/iw= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFbDCCA1SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBHMQswCQYDVQQGEwJVUzEW +MBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXR2VvVHJ1c3QgVW5pdmVy +c2FsIENBIDIwHhcNMDQwMzA0MDUwMDAwWhcNMjkwMzA0MDUwMDAwWjBHMQswCQYD +VQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXR2VvVHJ1 +c3QgVW5pdmVyc2FsIENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC +AQCzVFLByT7y2dyxUxpZKeexw0Uo5dfR7cXFS6GqdHtXr0om/Nj1XqduGdt0DE81 +WzILAePb63p3NeqqWuDW6KFXlPCQo3RWlEQwAx5cTiuFJnSCegx2oG9NzkEtoBUG +FF+3Qs17j1hhNNwqCPkuwwGmIkQcTAeC5lvO0Ep8BNMZcyfwqph/Lq9O64ceJHdq +XbboW0W63MOhBW9Wjo8QJqVJwy7XQYci4E+GymC16qFjwAGXEHm9ADwSbSsVsaxL +se4YuU6W3Nx2/zu+z18DwPw76L5GG//aQMJS9/7jOvdqdzXQ2o3rXhhqMcceujwb +KNZrVMaqW9eiLBsZzKIC9ptZvTdrhrVtgrrY6slWvKk2WP0+GfPtDCapkzj4T8Fd +IgbQl+rhrcZV4IErKIM6+vR7IVEAvlI4zs1meaj0gVbi0IMJR1FbUGrP20gaXT73 +y/Zl92zxlfgCOzJWgjl6W70viRu/obTo/3+NjN8D8WBOWBFM66M/ECuDmgFz2ZRt +hAAnZqzwcEAJQpKtT5MNYQlRJNiS1QuUYbKHsu3/mjX/hVTK7URDrBs8FmtISgoc +QIgfksILAAX/8sgCSqSqqcyZlpwvWOB94b67B9xfBHJcMTTD7F8t4D1kkCLm0ey4 +Lt1ZrtmhN79UNdxzMk+MBB4zsslG8dhcyFVQyWi9qLo2CQIDAQABo2MwYTAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR281Xh+qQ2+/CfXGJx7Tz0RzgQKzAfBgNV +HSMEGDAWgBR281Xh+qQ2+/CfXGJx7Tz0RzgQKzAOBgNVHQ8BAf8EBAMCAYYwDQYJ +KoZIhvcNAQEFBQADggIBAGbBxiPz2eAubl/oz66wsCVNK/g7WJtAJDday6sWSf+z +dXkzoS9tcBc0kf5nfo/sm+VegqlVHy/c1FEHEv6sFj4sNcZj/NwQ6w2jqtB8zNHQ +L1EuxBRa3ugZ4T7GzKQp5y6EqgYweHZUcyiYWTjgAA1i00J9IZ+uPTqM1fp3DRgr +Fg5fNuH8KrUwJM/gYwx7WBr+mbpCErGR9Hxo4sjoryzqyX6uuyo9DRXcNJW2GHSo +ag/HtPQTxORb7QrSpJdMKu0vbBKJPfEncKpqA1Ihn0CoZ1Dy81of398j9tx4TuaY +T1U6U+Pv8vSfx3zYWK8pIpe44L2RLrB27FcRz+8pRPPphXpgY+RdM4kX2TGq2tbz +GDVyz4crL2MjhF2EjD9XoIj8mZEoJmmZ1I+XRL6O1UixpCgp8RW04eWe3fiPpm8m +1wk8OhwRDqZsN/etRIcsKMfYdIKz0G9KV7s1KSegi+ghp4dkNl3M2Basx7InQJJV +OCiNUW7dFGdTbHFcJoRNdVq2fmBWqU2t+5sel/MN2dKXVHfaPRK34B7vCAas+YWH +6aLcr34YEoP9VhdBLtUpgn2Z9DH2canPLAEnpQW5qrJITirvn5NSUZU8UnOOVkwX +QMAJKOSLakhT2+zNVVXxxvjpoixMptEmX36vWkzaH6byHCx+rgIW0lbQL1dTR+iS +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIB4TCCAYegAwIBAgIRKjikHJYKBN5CsiilC+g0mAIwCgYIKoZIzj0EAwIwUDEk +MCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI0MRMwEQYDVQQKEwpH +bG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoX +DTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBD +QSAtIFI0MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEuMZ5049sJQ6fLjkZHAOkrprlOQcJ +FspjsbmG+IpXwVfOQvpzofdlQv8ewQCybnMO/8ch5RikqtlxP6jUuc6MHaNCMEAw +DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFFSwe61F +uOJAf/sKbvu+M8k8o4TVMAoGCCqGSM49BAMCA0gAMEUCIQDckqGgE6bPA7DmxCGX +kPoUVy0D7O48027KqGx2vKLeuwIgJ6iFJzWbVsaj8kfSt24bAgAXqmemFZHe+pTs +ewv4n4Q= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICHjCCAaSgAwIBAgIRYFlJ4CYuu1X5CneKcflK2GwwCgYIKoZIzj0EAwMwUDEk +MCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpH +bG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoX +DTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBD +QSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu +MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAER0UOlvt9Xb/pOdEh+J8LttV7HpI6SFkc +8GIxLcB6KP4ap1yztsyX50XUWPrRd21DosCHZTQKH3rd6zwzocWdTaRvQZU4f8ke +hOvRnkmSh5SHDDqFSmafnVmTTZdhBoZKo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUPeYpSJvqB8ohREom3m7e0oPQn1kwCgYI +KoZIzj0EAwMDaAAwZQIxAOVpEslu28YxuglB4Zf4+/2a4n0Sye18ZNPLBSWLVtmg +515dTguDnFt2KaAJJiFqYgIwcdK1j1zqO+F4CYWodZI7yFz9SO8NdCKoCOJuxUnO +xwy8p2Fp8fc74SrL+SvzZpA3 +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG +A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv +b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAw +MDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i +YWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxT +aWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZ +jc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavp +xy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp +1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdG +snUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJ +U26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N8 +9iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E +BTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0B +AQUFAAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOz +yj1hTdNGCbM+w6DjY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE +38NflNUVyRRBnMRddWQVDf9VMOyGj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymP +AbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr+WymXUad +DKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME +HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4G +A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNp +Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDYxMjE1MDgwMDAwWhcNMjExMjE1 +MDgwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMjETMBEG +A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAKbPJA6+Lm8omUVCxKs+IVSbC9N/hHD6ErPL +v4dfxn+G07IwXNb9rfF73OX4YJYJkhD10FPe+3t+c4isUoh7SqbKSaZeqKeMWhG8 +eoLrvozps6yWJQeXSpkqBy+0Hne/ig+1AnwblrjFuTosvNYSuetZfeLQBoZfXklq +tTleiDTsvHgMCJiEbKjNS7SgfQx5TfC4LcshytVsW33hoCmEofnTlEnLJGKRILzd +C9XZzPnqJworc5HGnRusyMvo4KD0L5CLTfuwNhv2GXqF4G3yYROIXJ/gkwpRl4pa +zq+r1feqCapgvdzZX99yqWATXgAByUr6P6TqBwMhAo6CygPCm48CAwEAAaOBnDCB +mTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUm+IH +V2ccHsBqBt5ZtJot39wZhi4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5n +bG9iYWxzaWduLm5ldC9yb290LXIyLmNybDAfBgNVHSMEGDAWgBSb4gdXZxwewGoG +3lm0mi3f3BmGLjANBgkqhkiG9w0BAQUFAAOCAQEAmYFThxxol4aR7OBKuEQLq4Gs +J0/WwbgcQ3izDJr86iw8bmEbTUsp9Z8FHSbBuOmDAGJFtqkIk7mpM0sYmsL4h4hO +291xNBrBVNpGP+DTKqttVCL1OmLNIG+6KYnX3ZHu01yiPqFbQfXf5WRDLenVOavS +ot+3i9DAgBkcRcAtjOj4LaR0VknFBbVPFd5uRHg5h6h+u/N5GJG79G+dwfCMNYxd +AfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7 +TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4G +A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNp +Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4 +MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEG +A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWtiHL8 +RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsT +gHeMCOFJ0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmm +KPZpO/bLyCiR5Z2KYVc3rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zd +QQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjlOCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZ +XriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2xmmFghcCAwEAAaNCMEAw +DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI/wS3+o +LkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZU +RUm7lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMp +jjM5RcOO5LlXbKr8EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK +6fBdRoyV3XpYKBovHd7NADdBj+1EbddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQX +mcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJrlAGomecs +Mx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH +WD9f +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIHSTCCBTGgAwIBAgIJAMnN0+nVfSPOMA0GCSqGSIb3DQEBBQUAMIGsMQswCQYD +VQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0 +IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3 +MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAlBgNVBAMTHkdsb2JhbCBD +aGFtYmVyc2lnbiBSb290IC0gMjAwODAeFw0wODA4MDExMjMxNDBaFw0zODA3MzEx +MjMxNDBaMIGsMQswCQYDVQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3Vy +cmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAG +A1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAl +BgNVBAMTHkdsb2JhbCBDaGFtYmVyc2lnbiBSb290IC0gMjAwODCCAiIwDQYJKoZI +hvcNAQEBBQADggIPADCCAgoCggIBAMDfVtPkOpt2RbQT2//BthmLN0EYlVJH6xed +KYiONWwGMi5HYvNJBL99RDaxccy9Wglz1dmFRP+RVyXfXjaOcNFccUMd2drvXNL7 +G706tcuto8xEpw2uIRU/uXpbknXYpBI4iRmKt4DS4jJvVpyR1ogQC7N0ZJJ0YPP2 +zxhPYLIj0Mc7zmFLmY/CDNBAspjcDahOo7kKrmCgrUVSY7pmvWjg+b4aqIG7HkF4 +ddPB/gBVsIdU6CeQNR1MM62X/JcumIS/LMmjv9GYERTtY/jKmIhYF5ntRQOXfjyG +HoiMvvKRhI9lNNgATH23MRdaKXoKGCQwoze1eqkBfSbW+Q6OWfH9GzO1KTsXO0G2 +Id3UwD2ln58fQ1DJu7xsepeY7s2MH/ucUa6LcL0nn3HAa6x9kGbo1106DbDVwo3V +yJ2dwW3Q0L9R5OP4wzg2rtandeavhENdk5IMagfeOx2YItaswTXbo6Al/3K1dh3e +beksZixShNBFks4c5eUzHdwHU1SjqoI7mjcv3N2gZOnm3b2u/GSFHTynyQbehP9r +6GsaPMWis0L7iwk+XwhSx2LE1AVxv8Rk5Pihg+g+EpuoHtQ2TS9x9o0o9oOpE9Jh +wZG7SMA0j0GMS0zbaRL/UJScIINZc+18ofLx/d33SdNDWKBWY8o9PeU1VlnpDsog +zCtLkykPAgMBAAGjggFqMIIBZjASBgNVHRMBAf8ECDAGAQH/AgEMMB0GA1UdDgQW +BBS5CcqcHtvTbDprru1U8VuTBjUuXjCB4QYDVR0jBIHZMIHWgBS5CcqcHtvTbDpr +ru1U8VuTBjUuXqGBsqSBrzCBrDELMAkGA1UEBhMCRVUxQzBBBgNVBAcTOk1hZHJp +ZCAoc2VlIGN1cnJlbnQgYWRkcmVzcyBhdCB3d3cuY2FtZXJmaXJtYS5jb20vYWRk +cmVzcykxEjAQBgNVBAUTCUE4Mjc0MzI4NzEbMBkGA1UEChMSQUMgQ2FtZXJmaXJt +YSBTLkEuMScwJQYDVQQDEx5HbG9iYWwgQ2hhbWJlcnNpZ24gUm9vdCAtIDIwMDiC +CQDJzdPp1X0jzjAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRVHSAAMCow +KAYIKwYBBQUHAgEWHGh0dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20wDQYJKoZI +hvcNAQEFBQADggIBAICIf3DekijZBZRG/5BXqfEv3xoNa/p8DhxJJHkn2EaqbylZ +UohwEurdPfWbU1Rv4WCiqAm57OtZfMY18dwY6fFn5a+6ReAJ3spED8IXDneRRXoz +X1+WLGiLwUePmJs9wOzL9dWCkoQ10b42OFZyMVtHLaoXpGNR6woBrX/sdZ7LoR/x +fxKxueRkf2fWIyr0uDldmOghp+G9PUIadJpwr2hsUF1Jz//7Dl3mLEfXgTpZALVz +a2Mg9jFFCDkO9HB+QHBaP9BrQql0PSgvAm11cpUJjUhjxsYjV5KTXjXBjfkK9yyd +Yhz2rXzdpjEetrHHfoUm+qRqtdpjMNHvkzeyZi99Bffnt0uYlDXA2TopwZ2yUDMd +SqlapskD7+3056huirRXhOukP9DuqqqHW2Pok+JrqNS4cnhrG+055F3Lm6qH1U9O +AP7Zap88MQ8oAgF9mOinsKJknnn4SPIVqczmyETrP3iZ8ntxPjzxmKfFGBI/5rso +M0LpRQp8bfKGeS/Fghl9CYl8slR2iK7ewfPM4W7bMdaTrpmg7yVqc5iJWzouE4ge +v8CSlDQb4ye3ix5vQv/n6TebUB0tovkC7stYWDpxvGjjqsGvHCgfotwjZT+B6q6Z +09gwzxMNTxXJhLynSC34MCN32EZLeW32jO06f2ARePTpm67VVMB0gNELQp/B +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEh +MB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBE +YWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3 +MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkGA1UEBhMCVVMxITAfBgNVBAoTGFRo +ZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28gRGFkZHkgQ2xhc3Mg +MiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQADggEN +ADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCA +PVYYYwhv2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6w +wdhFJ2+qN1j3hybX2C32qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXi +EqITLdiOr18SPaAIBQi2XKVlOARFmR6jYGB0xUGlcmIbYsUfb18aQr4CUWWoriMY +avx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmYvLEHZ6IVDd2gWMZEewo+ +YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0OBBYEFNLE +sNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h +/t2oatTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5 +IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmlj +YXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQAD +ggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wimPQoZ+YeAEW5p5JYXMP80kWNy +OO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKtI3lpjbi2Tc7P +TMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ +HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mER +dEr/VxqHD3VILs9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5Cuf +ReYNnyicsbkqWletNw+vHX/bvZ8= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoT +EUdvRGFkZHkuY29tLCBJbmMuMTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRp +ZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIz +NTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQH +EwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8GA1UE +AxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKD +E6bFIEMBO4Tx5oVJnyfq9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH +/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD+qK+ihVqf94Lw7YZFAXK6sOoBJQ7Rnwy +DfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutdfMh8+7ArU6SSYmlRJQVh +GkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMlNAJWJwGR +tDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEA +AaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE +FDqahQcQZyi27/a9BUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmX +WWcDYfF+OwYxdS2hII5PZYe096acvNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu +9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r5N9ss4UXnT3ZJE95kTXWXwTr +gIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYVN8Gb5DKj7Tjo +2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO +LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI +4uJEvlz36hz1 +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICwzCCAkqgAwIBAgIBADAKBggqhkjOPQQDAjCBqjELMAkGA1UEBhMCR1IxDzAN +BgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJl +c2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxRDBCBgNVBAMTO0hl +bGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgRUNDIFJv +b3RDQSAyMDE1MB4XDTE1MDcwNzEwMzcxMloXDTQwMDYzMDEwMzcxMlowgaoxCzAJ +BgNVBAYTAkdSMQ8wDQYDVQQHEwZBdGhlbnMxRDBCBgNVBAoTO0hlbGxlbmljIEFj +YWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ2VydC4gQXV0aG9yaXR5 +MUQwQgYDVQQDEztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0 +dXRpb25zIEVDQyBSb290Q0EgMjAxNTB2MBAGByqGSM49AgEGBSuBBAAiA2IABJKg +QehLgoRc4vgxEZmGZE4JJS+dQS8KrjVPdJWyUWRrjWvmP3CV8AVER6ZyOFB2lQJa +jq4onvktTpnvLEhvTCUp6NFxW98dwXU3tNf6e3pCnGoKVlp8aQuqgAkkbH7BRqNC +MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFLQi +C4KZJAEOnLvkDv2/+5cgk5kqMAoGCCqGSM49BAMCA2cAMGQCMGfOFmI4oqxiRaep +lSTAGiecMjvAwNW6qef4BENThe5SId6d9SWDPp5YSy/XZxMOIQIwBeF1Ad5o7Sof +TUwJCA3sS61kFyjndc5FZXIhF8siQQ6ME5g4mlRtm8rifOoCWCKR +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEMTCCAxmgAwIBAgIBADANBgkqhkiG9w0BAQUFADCBlTELMAkGA1UEBhMCR1Ix +RDBCBgNVBAoTO0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1 +dGlvbnMgQ2VydC4gQXV0aG9yaXR5MUAwPgYDVQQDEzdIZWxsZW5pYyBBY2FkZW1p +YyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25zIFJvb3RDQSAyMDExMB4XDTExMTIw +NjEzNDk1MloXDTMxMTIwMTEzNDk1MlowgZUxCzAJBgNVBAYTAkdSMUQwQgYDVQQK +EztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25zIENl +cnQuIEF1dGhvcml0eTFAMD4GA1UEAxM3SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJl +c2VhcmNoIEluc3RpdHV0aW9ucyBSb290Q0EgMjAxMTCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBAKlTAOMupvaO+mDYLZU++CwqVE7NuYRhlFhPjz2L5EPz +dYmNUeTDN9KKiE15HrcS3UN4SoqS5tdI1Q+kOilENbgH9mgdVc04UfCMJDGFr4PJ +fel3r+0ae50X+bOdOFAPplp5kYCvN66m0zH7tSYJnTxa71HFK9+WXesyHgLacEns +bgzImjeN9/E2YEsmLIKe0HjzDQ9jpFEw4fkrJxIH2Oq9GGKYsFk3fb7u8yBRQlqD +75O6aRXxYp2fmTmCobd0LovUxQt7L/DICto9eQqakxylKHJzkUOap9FNhYS5qXSP +FEDH3N6sQWRstBmbAmNtJGSPRLIl6s5ddAxjMlyNh+UCAwEAAaOBiTCBhjAPBgNV +HRMBAf8EBTADAQH/MAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQUppFC/RNhSiOeCKQp +5dgTBCPuQSUwRwYDVR0eBEAwPqA8MAWCAy5ncjAFggMuZXUwBoIELmVkdTAGggQu +b3JnMAWBAy5ncjAFgQMuZXUwBoEELmVkdTAGgQQub3JnMA0GCSqGSIb3DQEBBQUA +A4IBAQAf73lB4XtuP7KMhjdCSk4cNx6NZrokgclPEg8hwAOXhiVtXdMiKahsog2p +6z0GW5k6x8zDmjR/qw7IThzh+uTczQ2+vyT+bOdrwg3IBp5OjWEopmr95fZi6hg8 +TqBTnbI6nOulnJEWtk2C4AwFSKls9cz4y51JtPACpf1wA+2KIaWuE4ZJwzNzvoc7 +dIsXRSZMFpGD/md9zU1jZ/rzAxKWeAaNsWftjj++n08C9bMJL/NMh98qy5V8Acys +Nnq/onN694/BtZqhFLKPM58N7yLcZnuEvUUXBj08yrl3NI/K6s8/MT7jiOOASSXI +l7WdmplNsDz4SgCbZN2fOUvRJ9e4 +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIGCzCCA/OgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBpjELMAkGA1UEBhMCR1Ix +DzANBgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5k +IFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMT +N0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9v +dENBIDIwMTUwHhcNMTUwNzA3MTAxMTIxWhcNNDAwNjMwMTAxMTIxWjCBpjELMAkG +A1UEBhMCR1IxDzANBgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNh +ZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkx +QDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1 +dGlvbnMgUm9vdENBIDIwMTUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC +AQDC+Kk/G4n8PDwEXT2QNrCROnk8ZlrvbTkBSRq0t89/TSNTt5AA4xMqKKYx8ZEA +4yjsriFBzh/a/X0SWwGDD7mwX5nh8hKDgE0GPt+sr+ehiGsxr/CL0BgzuNtFajT0 +AoAkKAoCFZVedioNmToUW/bLy1O8E00BiDeUJRtCvCLYjqOWXjrZMts+6PAQZe10 +4S+nfK8nNLspfZu2zwnI5dMK/IhlZXQK3HMcXM1AsRzUtoSMTFDPaI6oWa7CJ06C +ojXdFPQf/7J31Ycvqm59JCfnxssm5uX+Zwdj2EUN3TpZZTlYepKZcj2chF6IIbjV +9Cz82XBST3i4vTwri5WY9bPRaM8gFH5MXF/ni+X1NYEZN9cRCLdmvtNKzoNXADrD +gfgXy5I2XdGj2HUb4Ysn6npIQf1FGQatJ5lOwXBH3bWfgVMS5bGMSF0xQxfjjMZ6 +Y5ZLKTBOhE5iGV48zpeQpX8B653g+IuJ3SWYPZK2fu/Z8VFRfS0myGlZYeCsargq +NhEEelC9MoS+L9xy1dcdFkfkR2YgP/SWxa+OAXqlD3pk9Q0Yh9muiNX6hME6wGko +LfINaFGq46V3xqSQDqE3izEjR8EJCOtu93ib14L8hCCZSRm2Ekax+0VVFqmjZayc +Bw/qa9wfLgZy7IaIEuQt218FL+TwA9MmM+eAws1CoRc0CwIDAQABo0IwQDAPBgNV +HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUcRVnyMjJvXVd +ctA4GGqd83EkVAswDQYJKoZIhvcNAQELBQADggIBAHW7bVRLqhBYRjTyYtcWNl0I +XtVsyIe9tC5G8jH4fOpCtZMWVdyhDBKg2mF+D1hYc2Ryx+hFjtyp8iY/xnmMsVMI +M4GwVhO+5lFc2JsKT0ucVlMC6U/2DWDqTUJV6HwbISHTGzrMd/K4kPFox/la/vot +9L/J9UUbzjgQKjeKeaO04wlshYaT/4mWJ3iBj2fjRnRUjtkNaeJK9E10A/+yd+2V +Z5fkscWrv2oj6NSU4kQoYsRL4vDY4ilrGnB+JGGTe08DMiUNRSQrlrRGar9KC/ea +j8GsGsVn82800vpzY4zvFrCopEYq+OsS7HK07/grfoxSwIuEVPkvPuNVqNxmsdnh +X9izjFk0WaSrT2y7HxjbdavYy5LNlDhhDgcGH0tGEPEVvo2FXDtKK4F5D7Rpn0lQ +l033DlZdwJVqwjbDG2jJ9SrcR5q+ss7FJej6A7na+RZukYT1HCjI/CbM1xyQVqdf +bzoEvM14iQuODy+jqk+iGxI9FghAD/FGTNeqewjBCvVtJ94Cj8rDtSvK6evIIVM4 +pcw72Hc3MKJP2W/R8kCtQXoXxdZKNYm3QdV8hn9VTYNKpXMgwDqvkPGaJI7ZjnHK +e7iG2rKPmT4dEw0SEe7Uq/DpFXYC5ODfqiAeW2GFZECpkJcNrVPSWh2HagCXZWK0 +vm9qp/UsQu0yrbYhnr68 +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDMDCCAhigAwIBAgICA+gwDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCSEsx +FjAUBgNVBAoTDUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3Qg +Um9vdCBDQSAxMB4XDTAzMDUxNTA1MTMxNFoXDTIzMDUxNTA0NTIyOVowRzELMAkG +A1UEBhMCSEsxFjAUBgNVBAoTDUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdr +b25nIFBvc3QgUm9vdCBDQSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEArP84tulmAknjorThkPlAj3n54r15/gK97iSSHSL22oVyaf7XPwnU3ZG1ApzQ +jVrhVcNQhrkpJsLj2aDxaQMoIIBFIi1WpztUlVYiWR8o3x8gPW2iNr4joLFutbEn +PzlTCeqrauh0ssJlXI6/fMN4hM2eFvz1Lk8gKgifd/PFHsSaUmYeSF7jEAaPIpjh +ZY4bXSNmO7ilMlHIhqqhqZ5/dpTCpmy3QfDVyAY45tQM4vM7TG1QjMSDJ8EThFk9 +nnV0ttgCXjqQesBCNnLsak3c78QA3xMYV18meMjWCnl3v/evt3a5pQuEF10Q6m/h +q5URX208o1xNg1vysxmKgIsLhwIDAQABoyYwJDASBgNVHRMBAf8ECDAGAQH/AgED +MA4GA1UdDwEB/wQEAwIBxjANBgkqhkiG9w0BAQUFAAOCAQEADkbVPK7ih9legYsC +mEEIjEy82tvuJxuC52pF7BaLT4Wg87JwvVqWuspube5Gi27nKi6Wsxkz67SfqLI3 +7piol7Yutmcn1KZJ/RyTZXaeQi/cImyaT/JaFTmxcdcrUehtHJjA2Sr0oYJ71clB +oiMBdDhViw+5LmeiIAQ32pwL0xch4I+XeTRvhEgCIDMb5jREn5Fw9IBehEPCKdJs +EhTkYY2sEJCehFC78JZvRZ+K88psT/oROhUVRsPNH4NbLUES7VBnQRM9IauUiqpO +fMGx+6fWtScvl6tu4B3i0RwsH0Ti/L6RoZz71ilTc4afU9hDDl3WY4JxHYB0yvbi +AmvZWg== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw +TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh +cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4 +WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu +ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY +MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc +h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+ +0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U +A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW +T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH +B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC +B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv +KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn +OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn +jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw +qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI +rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq +hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL +ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ +3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK +NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5 +ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur +TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC +jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc +oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq +4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA +mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d +emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIQCgFCgAAAAUUjyES1AAAAAjANBgkqhkiG9w0BAQsFADBK +MQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MScwJQYDVQQDEx5JZGVu +VHJ1c3QgQ29tbWVyY2lhbCBSb290IENBIDEwHhcNMTQwMTE2MTgxMjIzWhcNMzQw +MTE2MTgxMjIzWjBKMQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MScw +JQYDVQQDEx5JZGVuVHJ1c3QgQ29tbWVyY2lhbCBSb290IENBIDEwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCnUBneP5k91DNG8W9RYYKyqU+PZ4ldhNlT +3Qwo2dfw/66VQ3KZ+bVdfIrBQuExUHTRgQ18zZshq0PirK1ehm7zCYofWjK9ouuU ++ehcCuz/mNKvcbO0U59Oh++SvL3sTzIwiEsXXlfEU8L2ApeN2WIrvyQfYo3fw7gp +S0l4PJNgiCL8mdo2yMKi1CxUAGc1bnO/AljwpN3lsKImesrgNqUZFvX9t++uP0D1 +bVoE/c40yiTcdCMbXTMTEl3EASX2MN0CXZ/g1Ue9tOsbobtJSdifWwLziuQkkORi +T0/Br4sOdBeo0XKIanoBScy0RnnGF7HamB4HWfp1IYVl3ZBWzvurpWCdxJ35UrCL +vYf5jysjCiN2O/cz4ckA82n5S6LgTrx+kzmEB/dEcH7+B1rlsazRGMzyNeVJSQjK +Vsk9+w8YfYs7wRPCTY/JTw436R+hDmrfYi7LNQZReSzIJTj0+kuniVyc0uMNOYZK +dHzVWYfCP04MXFL0PfdSgvHqo6z9STQaKPNBiDoT7uje/5kdX7rL6B7yuVBgwDHT +c+XvvqDtMwt0viAgxGds8AgDelWAf0ZOlqf0Hj7h9tgJ4TNkK2PXMl6f+cB7D3hv +l7yTmvmcEpB4eoCHFddydJxVdHixuuFucAS6T6C6aMN7/zHwcz09lCqxC0EOoP5N +iGVreTO01wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB +/zAdBgNVHQ4EFgQU7UQZwNPwBovupHu+QucmVMiONnYwDQYJKoZIhvcNAQELBQAD +ggIBAA2ukDL2pkt8RHYZYR4nKM1eVO8lvOMIkPkp165oCOGUAFjvLi5+U1KMtlwH +6oi6mYtQlNeCgN9hCQCTrQ0U5s7B8jeUeLBfnLOic7iPBZM4zY0+sLj7wM+x8uwt +LRvM7Kqas6pgghstO8OEPVeKlh6cdbjTMM1gCIOQ045U8U1mwF10A0Cj7oV+wh93 +nAbowacYXVKV7cndJZ5t+qntozo00Fl72u1Q8zW/7esUTTHHYPTa8Yec4kjixsU3 ++wYQ+nVZZjFHKdp2mhzpgq7vmrlR94gjmmmVYjzlVYA211QC//G5Xc7UI2/YRYRK +W2XviQzdFKcgyxilJbQN+QHwotL0AMh0jqEqSI5l2xPE4iUXfeu+h1sXIFRRk0pT +AwvsXcoz7WL9RccvW9xYoIA55vrX/hMUpu09lEpCdNTDd1lzzY9GvlU47/rokTLq +l1gEIt44w8y8bckzOmoKaT+gyOpyj4xjhiO9bTyWnpXgSUyqorkqG5w2gXjtw+hG +4iZZRHUe2XWJUc0QhJ1hYMtd+ZciTY6Y5uN/9lu7rs3KSoFrXgvzUeF0K+l+J6fZ +mUlO+KWA2yUPHGNiiskzZ2s8EIPGrd6ozRaOjfAHN3Gf8qv8QfXBi+wAN10J5U6A +7/qxXDgGpRtK4dw4LTzcqx+QGtVKnO7RcGzM7vRX+Bi6hG6H +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFZjCCA06gAwIBAgIQCgFCgAAAAUUjz0Z8AAAAAjANBgkqhkiG9w0BAQsFADBN +MQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MSowKAYDVQQDEyFJZGVu +VHJ1c3QgUHVibGljIFNlY3RvciBSb290IENBIDEwHhcNMTQwMTE2MTc1MzMyWhcN +MzQwMTE2MTc1MzMyWjBNMQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0 +MSowKAYDVQQDEyFJZGVuVHJ1c3QgUHVibGljIFNlY3RvciBSb290IENBIDEwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2IpT8pEiv6EdrCvsnduTyP4o7 +ekosMSqMjbCpwzFrqHd2hCa2rIFCDQjrVVi7evi8ZX3yoG2LqEfpYnYeEe4IFNGy +RBb06tD6Hi9e28tzQa68ALBKK0CyrOE7S8ItneShm+waOh7wCLPQ5CQ1B5+ctMlS +bdsHyo+1W/CD80/HLaXIrcuVIKQxKFdYWuSNG5qrng0M8gozOSI5Cpcu81N3uURF +/YTLNiCBWS2ab21ISGHKTN9T0a9SvESfqy9rg3LvdYDaBjMbXcjaY8ZNzaxmMc3R +3j6HEDbhuaR672BQssvKplbgN6+rNBM5Jeg5ZuSYeqoSmJxZZoY+rfGwyj4GD3vw +EUs3oERte8uojHH01bWRNszwFcYr3lEXsZdMUD2xlVl8BX0tIdUAvwFnol57plzy +9yLxkA2T26pEUWbMfXYD62qoKjgZl3YNa4ph+bz27nb9cCvdKTz4Ch5bQhyLVi9V +GxyhLrXHFub4qjySjmm2AcG1hp2JDws4lFTo6tyePSW8Uybt1as5qsVATFSrsrTZ +2fjXctscvG29ZV/viDUqZi/u9rNl8DONfJhBaUYPQxxp+pu10GFqzcpL2UyQRqsV +WaFHVCkugyhfHMKiq3IXAAaOReyL4jM9f9oZRORicsPfIsbyVtTdX5Vy7W1f90gD +W/3FKqD2cyOEEBsB5wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ +BAUwAwEB/zAdBgNVHQ4EFgQU43HgntinQtnbcZFrlJPrw6PRFKMwDQYJKoZIhvcN +AQELBQADggIBAEf63QqwEZE4rU1d9+UOl1QZgkiHVIyqZJnYWv6IAcVYpZmxI1Qj +t2odIFflAWJBF9MJ23XLblSQdf4an4EKwt3X9wnQW3IV5B4Jaj0z8yGa5hV+rVHV +DRDtfULAj+7AmgjVQdZcDiFpboBhDhXAuM/FSRJSzL46zNQuOAXeNf0fb7iAaJg9 +TaDKQGXSc3z1i9kKlT/YPyNtGtEqJBnZhbMX73huqVjRI9PHE+1yJX9dsXNw0H8G +lwmEKYBhHfpe/3OsoOOJuBxxFcbeMX8S3OFtm6/n6J91eEyrRjuazr8FGF1NFTwW +mhlQBJqymm9li1JfPFgEKCXAZmExfrngdbkaqIHWchezxQMxNRF4eKLg6TCMf4Df +WN88uieW4oA0beOY02QnrEh+KHdcxiVhJfiFDGX6xDIvpZgF5PgLZxYWxoK4Mhn5 ++bl53B/N66+rDt0b20XkeucC4pVd/GnwU2lhlXV5C15V5jgclKlZM57IcXR5f1GJ +tshquDDIajjDbp7hNxbqBWJMWxJH7ae0s1hWx0nzfxJoCTFx8G34Tkf71oXuxVhA +GaQdp/lLQzfcaFpPz+vCZHTetBXZ9FRUGi8c15dxVJCO2SCdUyt/q4/i6jC8UDfv +8Ue1fXwsBOxonbRJRBD0ckscZOf85muQ3Wl9af0AVqW3rLatt8o+Ae+c +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIF8TCCA9mgAwIBAgIQALC3WhZIX7/hy/WL1xnmfTANBgkqhkiG9w0BAQsFADA4 +MQswCQYDVQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6 +ZW5wZS5jb20wHhcNMDcxMjEzMTMwODI4WhcNMzcxMjEzMDgyNzI1WjA4MQswCQYD +VQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6ZW5wZS5j +b20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDJ03rKDx6sp4boFmVq +scIbRTJxldn+EFvMr+eleQGPicPK8lVx93e+d5TzcqQsRNiekpsUOqHnJJAKClaO +xdgmlOHZSOEtPtoKct2jmRXagaKH9HtuJneJWK3W6wyyQXpzbm3benhB6QiIEn6H +LmYRY2xU+zydcsC8Lv/Ct90NduM61/e0aL6i9eOBbsFGb12N4E3GVFWJGjMxCrFX +uaOKmMPsOzTFlUFpfnXCPCDFYbpRR6AgkJOhkEvzTnyFRVSa0QUmQbC1TR0zvsQD +yCV8wXDbO/QJLVQnSKwv4cSsPsjLkkxTOTcj7NMB+eAJRE1NZMDhDVqHIrytG6P+ +JrUV86f8hBnp7KGItERphIPzidF0BqnMC9bC3ieFUCbKF7jJeodWLBoBHmy+E60Q +rLUk9TiRodZL2vG70t5HtfG8gfZZa88ZU+mNFctKy6lvROUbQc/hhqfK0GqfvEyN +BjNaooXlkDWgYlwWTvDjovoDGrQscbNYLN57C9saD+veIR8GdwYDsMnvmfzAuU8L +hij+0rnq49qlw0dpEuDb8PYZi+17cNcC1u2HGCgsBCRMd+RIihrGO5rUD8r6ddIB +QFqNeb+Lz0vPqhbBleStTIo+F5HUsWLlguWABKQDfo2/2n+iD5dPDNMN+9fR5XJ+ +HMh3/1uaD7euBUbl8agW7EekFwIDAQABo4H2MIHzMIGwBgNVHREEgagwgaWBD2lu +Zm9AaXplbnBlLmNvbaSBkTCBjjFHMEUGA1UECgw+SVpFTlBFIFMuQS4gLSBDSUYg +QTAxMzM3MjYwLVJNZXJjLlZpdG9yaWEtR2FzdGVpeiBUMTA1NSBGNjIgUzgxQzBB +BgNVBAkMOkF2ZGEgZGVsIE1lZGl0ZXJyYW5lbyBFdG9yYmlkZWEgMTQgLSAwMTAx +MCBWaXRvcmlhLUdhc3RlaXowDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwHQYDVR0OBBYEFB0cZQ6o8iV7tJHP5LGx5r1VdGwFMA0GCSqGSIb3DQEBCwUA +A4ICAQB4pgwWSp9MiDrAyw6lFn2fuUhfGI8NYjb2zRlrrKvV9pF9rnHzP7MOeIWb +laQnIUdCSnxIOvVFfLMMjlF4rJUT3sb9fbgakEyrkgPH7UIBzg/YsfqikuFgba56 +awmqxinuaElnMIAkejEWOVt+8Rwu3WwJrfIxwYJOubv5vr8qhT/AQKM6WfxZSzwo +JNu0FXWuDYi6LnPAvViH5ULy617uHjAimcs30cQhbIHsvm0m5hzkQiCeR7Csg1lw +LDXWrzY0tM07+DKo7+N4ifuNRSzanLh+QBxh5z6ikixL8s36mLYp//Pye6kfLqCT +VyvehQP5aTfLnnhqBbTFMXiJ7HqnheG5ezzevh55hM6fcA5ZwjUukCox2eRFekGk +LhObNA5me0mrZJfQRsN5nXJQY6aYWwa9SG3YOYNw6DXwBdGqvOPbyALqfP2C2sJb +UjWumDqtujWTI6cfSN01RpiyEGjkpTHCClguGYEQyVB1/OpaFs4R1+7vUIgtYf8/ +QnMFlEPVjjxOAToZpR9GTnfQXeWBIiGH/pR9hNiTrdZoQ0iy2+tzJOeRf1SktoA+ +naM8THLCV8Sg1Mw4J87VBp6iSNnpn86CcDaTmjvfliHjWbcM2pE38P1ZWrOZyGls +QyYBNWNgVYkDOnXYukrZVP/u3oDYLdE41V4tC5h9Pmzb/CaIxw== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFwzCCA6ugAwIBAgIUCn6m30tEntpqJIWe5rgV0xZ/u7EwDQYJKoZIhvcNAQEL +BQAwRjELMAkGA1UEBhMCTFUxFjAUBgNVBAoMDUx1eFRydXN0IFMuQS4xHzAdBgNV +BAMMFkx1eFRydXN0IEdsb2JhbCBSb290IDIwHhcNMTUwMzA1MTMyMTU3WhcNMzUw +MzA1MTMyMTU3WjBGMQswCQYDVQQGEwJMVTEWMBQGA1UECgwNTHV4VHJ1c3QgUy5B +LjEfMB0GA1UEAwwWTHV4VHJ1c3QgR2xvYmFsIFJvb3QgMjCCAiIwDQYJKoZIhvcN +AQEBBQADggIPADCCAgoCggIBANeFl78RmOnwYoNMPIf5U2o3C/IPPIfOb9wmKb3F +ibrJgz337spbxm1Jc7TJRqMbNBM/wYlFV/TZsfs2ZUv7COJIcRHIbjuend+JZTem +hfY7RBi2xjcwYkSSl2l9QjAk5A0MiWtj3sXh306pFGxT4GHO9hcvHTy95iJMHZP1 +EMShduxq3sVs35a0VkBCwGKSMKEtFZSg0iAGCW5qbeXrt77U8PEVfIvmTroTzEsn +Xpk8F12PgX8zPU/TPxvsXD/wPEx1bvKm1Z3aLQdjAsZy6ZS8TEmVT4hSyNvoaYL4 +zDRbIvCGp4m9SAptZoFtyMhk+wHh9OHe2Z7d21vUKpkmFRseTJIpgp7VkoGSQXAZ +96Tlk0u8d2cx3Rz9MXANF5kM+Qw5GSoXtTBxVdUPrljhPS80m8+f9niFwpN6cj5m +j5wWEWCPnolvZ77gR1o7DJpni89Gxq44o/KnvObWhWszJHAiS8sIm7vI+AIpHb4g +DEa/a4ebsypmQjVGbKq6rfmYe+lQVRQxv7HaLe2ArWgk+2mr2HETMOZns4dA/Yl+ +8kPREd8vZS9kzl8UubG/Mb2HeFpZZYiq/FkySIbWTLkpS5XTdvN3JW1CHDiDTf2j +X5t/Lax5Gw5CMZdjpPuKadUiDTSQMC6otOBttpSsvItO13D8xTiOZCXhTTmQzsmH +hFhxAgMBAAGjgagwgaUwDwYDVR0TAQH/BAUwAwEB/zBCBgNVHSAEOzA5MDcGByuB +KwEBAQowLDAqBggrBgEFBQcCARYeaHR0cHM6Ly9yZXBvc2l0b3J5Lmx1eHRydXN0 +Lmx1MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBT/GCh2+UgFLKGu8SsbK7JT ++Et8szAdBgNVHQ4EFgQU/xgodvlIBSyhrvErGyuyU/hLfLMwDQYJKoZIhvcNAQEL +BQADggIBAGoZFO1uecEsh9QNcH7X9njJCwROxLHOk3D+sFTAMs2ZMGQXvw/l4jP9 +BzZAcg4atmpZ1gDlaCDdLnINH2pkMSCEfUmmWjfrRcmF9dTHF5kH5ptV5AzoqbTO +jFu1EVzPig4N1qx3gf4ynCSecs5U89BvolbW7MM3LGVYvlcAGvI1+ut7MV3CwRI9 +loGIlonBWVx65n9wNOeD4rHh4bhY79SV5GCc8JaXcozrhAIuZY+kt9J/Z93I055c +qqmkoCUUBpvsT34tC38ddfEz2O3OuHVtPlu5mB0xDVbYQw8wkbIEa91WvpWAVWe+ +2M2D2RjuLg+GLZKecBPs3lHJQ3gCpU3I+V/EkVhGFndadKpAvAefMLmx9xIX3eP/ +JEAdemrRTxgKqpAd60Ae36EeRJIQmvKN4dFLRp7oRUKX6kWZ8+xm1QL68qZKJKre +zrnK+T+Tb/mjuuqlPpmt/f97mfVl7vBZKGfXkJWkE4SphMHozs51k2MavDzq1WQf +LSoSOcbDWjLtR5EWDrw4wVDej8oqkDQc7kGUnF4ZLvhFSZl0kbAEb+MEWrGrKqv+ +x9CWttrhSmQGbmBNvUJO/3jaJMobtNeWOWyu8Q6qp31IiyBMz2TWuJdGsE7RKlY6 +oJO9r4Ak4Ap+58rVyuiFVdw2KuGUaJPHZnJED4AhMmwlxyOAgwrr +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIECjCCAvKgAwIBAgIJAMJ+QwRORz8ZMA0GCSqGSIb3DQEBCwUAMIGCMQswCQYD +VQQGEwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0 +ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUtU3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0G +CSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5odTAeFw0wOTA2MTYxMTMwMThaFw0y +OTEyMzAxMTMwMThaMIGCMQswCQYDVQQGEwJIVTERMA8GA1UEBwwIQnVkYXBlc3Qx +FjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUtU3pp +Z25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5o +dTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOn4j/NjrdqG2KfgQvvP +kd6mJviZpWNwrZuuyjNAfW2WbqEORO7hE52UQlKavXWFdCyoDh2Tthi3jCyoz/tc +cbna7P7ofo/kLx2yqHWH2Leh5TvPmUpG0IMZfcChEhyVbUr02MelTTMuhTlAdX4U +fIASmFDHQWe4oIBhVKZsTh/gnQ4H6cm6M+f+wFUoLAKApxn1ntxVUwOXewdI/5n7 +N4okxFnMUBBjjqqpGrCEGob5X7uxUG6k0QrM1XF+H6cbfPVTbiJfyyvm1HxdrtbC +xkzlBQHZ7Vf8wSN5/PrIJIOV87VqUQHQd9bpEqH5GoP7ghu5sJf0dgYzQ0mg/wu1 ++rUCAwEAAaOBgDB+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G +A1UdDgQWBBTLD8bfQkPMPcu1SCOhGnqmKrs0aDAfBgNVHSMEGDAWgBTLD8bfQkPM +Pcu1SCOhGnqmKrs0aDAbBgNVHREEFDASgRBpbmZvQGUtc3ppZ25vLmh1MA0GCSqG +SIb3DQEBCwUAA4IBAQDJ0Q5eLtXMs3w+y/w9/w0olZMEyL/azXm4Q5DwpL7v8u8h +mLzU1F0G9u5C7DBsoKqpyvGvivo/C3NqPuouQH4frlRheesuCDfXI/OMn74dseGk +ddug4lQUsbocKaQY9hK6ohQU4zE1yED/t+AFdlfBHFny+L/k7SViXITwfn4fs775 +tyERzAMBVnCnEJIeGzSBHq2cGsMEPO0CYdYeBvNfOofyK/FFh+U9rNHHV4S9a67c +2Pm2G2JwCz02yULyMtd6YebS2z3PyKnJm9zbWETXbzivf3jTo60adbocwTZ8jx5t +HMN1Rq41Bab2XD0h7lbwyYIiLXpUq3DDfSJlgnCW +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEFTCCAv2gAwIBAgIGSUEs5AAQMA0GCSqGSIb3DQEBCwUAMIGnMQswCQYDVQQG +EwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5ldExvY2sgS2Z0LjE3 +MDUGA1UECwwuVGFuw7pzw610dsOhbnlraWFkw7NrIChDZXJ0aWZpY2F0aW9uIFNl +cnZpY2VzKTE1MDMGA1UEAwwsTmV0TG9jayBBcmFueSAoQ2xhc3MgR29sZCkgRsWR +dGFuw7pzw610dsOhbnkwHhcNMDgxMjExMTUwODIxWhcNMjgxMjA2MTUwODIxWjCB +pzELMAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFwZXN0MRUwEwYDVQQKDAxOZXRM +b2NrIEtmdC4xNzA1BgNVBAsMLlRhbsO6c8OtdHbDoW55a2lhZMOzayAoQ2VydGlm +aWNhdGlvbiBTZXJ2aWNlcykxNTAzBgNVBAMMLE5ldExvY2sgQXJhbnkgKENsYXNz +IEdvbGQpIEbFkXRhbsO6c8OtdHbDoW55MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAxCRec75LbRTDofTjl5Bu0jBFHjzuZ9lk4BqKf8owyoPjIMHj9DrT +lF8afFttvzBPhCf2nx9JvMaZCpDyD/V/Q4Q3Y1GLeqVw/HpYzY6b7cNGbIRwXdrz +AZAj/E4wqX7hJ2Pn7WQ8oLjJM2P+FpD/sLj916jAwJRDC7bVWaaeVtAkH3B5r9s5 +VA1lddkVQZQBr17s9o3x/61k/iCa11zr/qYfCGSji3ZVrR47KGAuhyXoqq8fxmRG +ILdwfzzeSNuWU7c5d+Qa4scWhHaXWy+7GRWF+GmF9ZmnqfI0p6m2pgP8b4Y9VHx2 +BJtr+UBdADTHLpl1neWIA6pN+APSQnbAGwIDAKiLo0UwQzASBgNVHRMBAf8ECDAG +AQH/AgEEMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUzPpnk/C2uNClwB7zU/2M +U9+D15YwDQYJKoZIhvcNAQELBQADggEBAKt/7hwWqZw8UQCgwBEIBaeZ5m8BiFRh +bvG5GK1Krf6BQCOUL/t1fC8oS2IkgYIL9WHxHG64YTjrgfpioTtaYtOUZcTh5m2C ++C8lcLIhJsFyUR+MLMOEkMNaj7rP9KdlpeuY0fsFskZ1FSNqb4VjMIDw1Z4fKRzC +bLBQWV2QWzuoDTDPv31/zvGdg73JRm4gpvlhUbohL3u+pRVjodSVh/GeufOJ8z2F +uLjbvrW5KfnaNwUASZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2 +XjG4Kvte9nHfRCaexOYNkbQudZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIID5jCCAs6gAwIBAgIQV8szb8JcFuZHFhfjkDFo4DANBgkqhkiG9w0BAQUFADBi +MQswCQYDVQQGEwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMu +MTAwLgYDVQQDEydOZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3Jp +dHkwHhcNMDYxMjAxMDAwMDAwWhcNMjkxMjMxMjM1OTU5WjBiMQswCQYDVQQGEwJV +UzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMuMTAwLgYDVQQDEydO +ZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDkvH6SMG3G2I4rC7xGzuAnlt7e+foS0zwz +c7MEL7xxjOWftiJgPl9dzgn/ggwbmlFQGiaJ3dVhXRncEg8tCqJDXRfQNJIg6nPP +OCwGJgl6cvf6UDL4wpPTaaIjzkGxzOTVHzbRijr4jGPiFFlp7Q3Tf2vouAPlT2rl +mGNpSAW+Lv8ztumXWWn4Zxmuk2GWRBXTcrA/vGp97Eh/jcOrqnErU2lBUzS1sLnF +BgrEsEX1QV1uiUV7PTsmjHTC5dLRfbIR1PtYMiKagMnc/Qzpf14Dl847ABSHJ3A4 +qY5usyd2mFHgBeMhqxrVhSI8KbWaFsWAqPS7azCPL0YCorEMIuDTAgMBAAGjgZcw +gZQwHQYDVR0OBBYEFCEwyfsA106Y2oeqKtCnLrFAMadMMA4GA1UdDwEB/wQEAwIB +BjAPBgNVHRMBAf8EBTADAQH/MFIGA1UdHwRLMEkwR6BFoEOGQWh0dHA6Ly9jcmwu +bmV0c29sc3NsLmNvbS9OZXR3b3JrU29sdXRpb25zQ2VydGlmaWNhdGVBdXRob3Jp +dHkuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQC7rkvnt1frf6ott3NHhWrB5KUd5Oc8 +6fRZZXe1eltajSU24HqXLjjAV2CDmAaDn7l2em5Q4LqILPxFzBiwmZVRDuwduIj/ +h1AcgsLj4DKAv6ALR8jDMe+ZZzKATxcheQxpXN5eNK4CtSbqUN9/GGUsyfJj4akH +/nxxH2szJGoeBfcFaMBqEssuXmHLrijTfsK0ZpEmXzwuJF/LWA/rKOyvEZbz3Htv +wKeI8lN3s2Berq4o2jUsbzRF0ybh3uxbTydrFny9RAQYgrOJeRcQcT16ohZO9QHN +pGxlaKFJdlxDydi8NmdspZS11My5vWo1ViHe2MPr+8ukYEywVaCge1ey +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIID8TCCAtmgAwIBAgIQQT1yx/RrH4FDffHSKFTfmjANBgkqhkiG9w0BAQUFADCB +ijELMAkGA1UEBhMCQ0gxEDAOBgNVBAoTB1dJU2VLZXkxGzAZBgNVBAsTEkNvcHly +aWdodCAoYykgMjAwNTEiMCAGA1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRvcnNl +ZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwgUm9vdCBHQSBDQTAeFw0w +NTEyMTExNjAzNDRaFw0zNzEyMTExNjA5NTFaMIGKMQswCQYDVQQGEwJDSDEQMA4G +A1UEChMHV0lTZUtleTEbMBkGA1UECxMSQ29weXJpZ2h0IChjKSAyMDA1MSIwIAYD +VQQLExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBX +SVNlS2V5IEdsb2JhbCBSb290IEdBIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAy0+zAJs9Nt350UlqaxBJH+zYK7LG+DKBKUOVTJoZIyEVRd7jyBxR +VVuuk+g3/ytr6dTqvirdqFEr12bDYVxgAsj1znJ7O7jyTmUIms2kahnBAbtzptf2 +w93NvKSLtZlhuAGio9RN1AU9ka34tAhxZK9w8RxrfvbDd50kc3vkDIzh2TbhmYsF +mQvtRTEJysIA2/dyoJaqlYfQjse2YXMNdmaM3Bu0Y6Kff5MTMPGhJ9vZ/yxViJGg +4E8HsChWjBgbl0SOid3gF27nKu+POQoxhILYQBRJLnpB5Kf+42TMwVlxSywhp1t9 +4B3RLoGbw9ho972WG6xwsRYUC9tguSYBBQIDAQABo1EwTzALBgNVHQ8EBAMCAYYw +DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUswN+rja8sHnR3JQmthG+IbJphpQw +EAYJKwYBBAGCNxUBBAMCAQAwDQYJKoZIhvcNAQEFBQADggEBAEuh/wuHbrP5wUOx +SPMowB0uyQlB+pQAHKSkq0lPjz0e701vvbyk9vImMMkQyh2I+3QZH4VFvbBsUfk2 +ftv1TDI6QU9bR8/oCy22xBmddMVHxjtqD6wU2zz0c5ypBd8A3HR4+vg1YFkCExh8 +vPtNsCBtQ7tgMHpnM1zFmdH4LTlSc/uMqpclXHLZCB6rTjzjgTGfA6b7wP4piFXa +hNVQA7bihKOmNqoROgHhGEvWRGizPflTdISzRpFGlgC3gCy24eMQ4tui5yiPAZZi +Fj4A4xylNoEYokxSdsARo27mHbrjWr42U8U+dY+GaSlYU7Wcu2+fXMUY7N0v4ZjJ +/L7fCg0= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDtTCCAp2gAwIBAgIQdrEgUnTwhYdGs/gjGvbCwDANBgkqhkiG9w0BAQsFADBt +MQswCQYDVQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUg +Rm91bmRhdGlvbiBFbmRvcnNlZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9i +YWwgUm9vdCBHQiBDQTAeFw0xNDEyMDExNTAwMzJaFw0zOTEyMDExNTEwMzFaMG0x +CzAJBgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYDVQQLExlPSVNURSBG +b3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEdsb2Jh +bCBSb290IEdCIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2Be3 +HEokKtaXscriHvt9OO+Y9bI5mE4nuBFde9IllIiCFSZqGzG7qFshISvYD06fWvGx +WuR51jIjK+FTzJlFXHtPrby/h0oLS5daqPZI7H17Dc0hBt+eFf1Biki3IPShehtX +1F1Q/7pn2COZH8g/497/b1t3sWtuuMlk9+HKQUYOKXHQuSP8yYFfTvdv37+ErXNk +u7dCjmn21HYdfp2nuFeKUWdy19SouJVUQHMD9ur06/4oQnc/nSMbsrY9gBQHTC5P +99UKFg29ZkM3fiNDecNAhvVMKdqOmq0NpQSHiB6F4+lT1ZvIiwNjeOvgGUpuuy9r +M2RYk61pv48b74JIxwIDAQABo1EwTzALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUw +AwEB/zAdBgNVHQ4EFgQUNQ/INmNe4qPs+TtmFc5RUuORmj0wEAYJKwYBBAGCNxUB +BAMCAQAwDQYJKoZIhvcNAQELBQADggEBAEBM+4eymYGQfp3FsLAmzYh7KzKNbrgh +cViXfa43FK8+5/ea4n32cZiZBKpDdHij40lhPnOMTZTg+XHEthYOU3gf1qKHLwI5 +gSk8rxWYITD+KJAAjNHhy/peyP34EEY7onhCkRd0VQreUGdNZtGn//3ZwLWoo4rO +ZvUPQ82nK1d7Y0Zqqi5S2PTt4W2tKZB4SLrhI6qjiey1q5bAtEuiHZeeevJuQHHf +aPFlTc58Bd9TZaml8LGXBHAVRgOY1NK/VLSgWH1Sb9pWJmLU2NuJMW8c8CLC02Ic +Nc1MaRVUGpCY3useX8p3x8uOPUNpnJpY0CQ73xtAln41rYHHTnG6iBM= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFbzCCA1egAwIBAgISESCzkFU5fX82bWTCp59rY45nMA0GCSqGSIb3DQEBCwUA +MEAxCzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlPcGVuVHJ1c3QxHTAbBgNVBAMMFE9w +ZW5UcnVzdCBSb290IENBIEcxMB4XDTE0MDUyNjA4NDU1MFoXDTM4MDExNTAwMDAw +MFowQDELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCU9wZW5UcnVzdDEdMBsGA1UEAwwU +T3BlblRydXN0IFJvb3QgQ0EgRzEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK +AoICAQD4eUbalsUwXopxAy1wpLuwxQjczeY1wICkES3d5oeuXT2R0odsN7faYp6b +wiTXj/HbpqbfRm9RpnHLPhsxZ2L3EVs0J9V5ToybWL0iEA1cJwzdMOWo010hOHQX +/uMftk87ay3bfWAfjH1MBcLrARYVmBSO0ZB3Ij/swjm4eTrwSSTilZHcYTSSjFR0 +77F9jAHiOH3BX2pfJLKOYheteSCtqx234LSWSE9mQxAGFiQD4eCcjsZGT44ameGP +uY4zbGneWK2gDqdkVBFpRGZPTBKnjix9xNRbxQA0MMHZmf4yzgeEtE7NCv82TWLx +p2NX5Ntqp66/K7nJ5rInieV+mhxNaMbBGN4zK1FGSxyO9z0M+Yo0FMT7MzUj8czx +Kselu7Cizv5Ta01BG2Yospb6p64KTrk5M0ScdMGTHPjgniQlQ/GbI4Kq3ywgsNw2 +TgOzfALU5nsaqocTvz6hdLubDuHAk5/XpGbKuxs74zD0M1mKB3IDVedzagMxbm+W +G+Oin6+Sx+31QrclTDsTBM8clq8cIqPQqwWyTBIjUtz9GVsnnB47ev1CI9sjgBPw +vFEVVJSmdz7QdFG9URQIOTfLHzSpMJ1ShC5VkLG631UAC9hWLbFJSXKAqWLXwPYY +EQRVzXR7z2FwefR7LFxckvzluFqrTJOVoSfupb7PcSNCupt2LQIDAQABo2MwYTAO +BgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUl0YhVyE1 +2jZVx/PxN3DlCPaTKbYwHwYDVR0jBBgwFoAUl0YhVyE12jZVx/PxN3DlCPaTKbYw +DQYJKoZIhvcNAQELBQADggIBAB3dAmB84DWn5ph76kTOZ0BP8pNuZtQ5iSas000E +PLuHIT839HEl2ku6q5aCgZG27dmxpGWX4m9kWaSW7mDKHyP7Rbr/jyTwyqkxf3kf +gLMtMrpkZ2CvuVnN35pJ06iCsfmYlIrM4LvgBBuZYLFGZdwIorJGnkSI6pN+VxbS +FXJfLkur1J1juONI5f6ELlgKn0Md/rcYkoZDSw6cMoYsYPXpSOqV7XAp8dUv/TW0 +V8/bhUiZucJvbI/NeJWsZCj9VrDDb8O+WVLhX4SPgPL0DTatdrOjteFkdjpY3H1P +XlZs5VVZV6Xf8YpmMIzUUmI4d7S+KNfKNsSbBfD4Fdvb8e80nR14SohWZ25g/4/I +i+GOvUKpMwpZQhISKvqxnUOOBZuZ2mKtVzazHbYNeS2WuOvyDEsMpZTGMKcmGS3t +TAZQMPH9WD25SxdfGbRqhFS0OE85og2WaMMolP3tLR9Ka0OWLpABEPs4poEL0L91 +09S5zvE/bw4cHjdx5RiHdRk/ULlepEU0rbDK5uUTdg8xFKmOLZTW1YVNcxVPS/Ky +Pu1svf0OnWZzsD2097+o4BGkxK51CUpjAEggpsadCwmKtODmzj7HPiY46SvepghJ +AwSQiumPv+i2tCqjI40cHLI5kqiPAlxAOXXUc0ECd97N4EOH1uS6SsNsEn/+KuYj +1oxx +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFbzCCA1egAwIBAgISESChaRu/vbm9UpaPI+hIvyYRMA0GCSqGSIb3DQEBDQUA +MEAxCzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlPcGVuVHJ1c3QxHTAbBgNVBAMMFE9w +ZW5UcnVzdCBSb290IENBIEcyMB4XDTE0MDUyNjAwMDAwMFoXDTM4MDExNTAwMDAw +MFowQDELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCU9wZW5UcnVzdDEdMBsGA1UEAwwU +T3BlblRydXN0IFJvb3QgQ0EgRzIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK +AoICAQDMtlelM5QQgTJT32F+D3Y5z1zCU3UdSXqWON2ic2rxb95eolq5cSG+Ntmh +/LzubKh8NBpxGuga2F8ORAbtp+Dz0mEL4DKiltE48MLaARf85KxP6O6JHnSrT78e +CbY2albz4e6WiWYkBuTNQjpK3eCasMSCRbP+yatcfD7J6xcvDH1urqWPyKwlCm/6 +1UWY0jUJ9gNDlP7ZvyCVeYCYitmJNbtRG6Q3ffyZO6v/v6wNj0OxmXsWEH4db0fE +FY8ElggGQgT4hNYdvJGmQr5J1WqIP7wtUdGejeBSzFfdNTVY27SPJIjki9/ca1TS +gSuyzpJLHB9G+h3Ykst2Z7UJmQnlrBcUVXDGPKBWCgOz3GIZ38i1MH/1PCZ1Eb3X +G7OHngevZXHloM8apwkQHZOJZlvoPGIytbU6bumFAYueQ4xncyhZW+vj3CzMpSZy +YhK05pyDRPZRpOLAeiRXyg6lPzq1O4vldu5w5pLeFlwoW5cZJ5L+epJUzpM5ChaH +vGOz9bGTXOBut9Dq+WIyiET7vycotjCVXRIouZW+j1MY5aIYFuJWpLIsEPUdN6b4 +t/bQWVyJ98LVtZR00dX+G7bw5tYee9I8y6jj9RjzIR9u701oBnstXW5DiabA+aC/ +gh7PU3+06yzbXfZqfUAkBXKJOAGTy3HCOV0GEfZvePg3DTmEJwIDAQABo2MwYTAO +BgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUajn6QiL3 +5okATV59M4PLuG53hq8wHwYDVR0jBBgwFoAUajn6QiL35okATV59M4PLuG53hq8w +DQYJKoZIhvcNAQENBQADggIBAJjLq0A85TMCl38th6aP1F5Kr7ge57tx+4BkJamz +Gj5oXScmp7oq4fBXgwpkTx4idBvpkF/wrM//T2h6OKQQbA2xx6R3gBi2oihEdqc0 +nXGEL8pZ0keImUEiyTCYYW49qKgFbdEfwFFEVn8nNQLdXpgKQuswv42hm1GqO+qT +RmTFAHneIWv2V6CG1wZy7HBGS4tz3aAhdT7cHcCP009zHIXZ/n9iyJVvttN7jLpT +wm+bREx50B1ws9efAvSyB7DH5fitIw6mVskpEndI2S9G/Tvw/HRwkqWOOAgfZDC2 +t0v7NqwQjqBSM2OdAzVWxWm9xiNaJ5T2pBL4LTM8oValX9YZ6e18CL13zSdkzJTa +TkZQh+D5wVOAHrut+0dSixv9ovneDiK3PTNZbNTe9ZUGMg1RGUFcPk8G97krgCf2 +o6p6fAbhQ8MTOWIaNr3gKC6UAuQpLmBVrkA9sHSSXvAgZJY/X0VdiLWK2gKgW0VU +3jg9CcCoSmVGFvyqv1ROTVu+OEO3KMqLM6oaJbolXCkvW0pujOotnCr2BXbgd5eA +iN1nE28daCSLT7d0geX0YJ96Vdc+N9oWaz53rK4YcJUIeSkDiv7BO7M/Gg+kO14f +WKGVyasvc0rQLW6aWQ9VGHgtPFGml4vmu7JwqkwR3v98KzfUetF3NI/n+UL3PIEM +S1IK +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICITCCAaagAwIBAgISESDm+Ez8JLC+BUCs2oMbNGA/MAoGCCqGSM49BAMDMEAx +CzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlPcGVuVHJ1c3QxHTAbBgNVBAMMFE9wZW5U +cnVzdCBSb290IENBIEczMB4XDTE0MDUyNjAwMDAwMFoXDTM4MDExNTAwMDAwMFow +QDELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCU9wZW5UcnVzdDEdMBsGA1UEAwwUT3Bl +blRydXN0IFJvb3QgQ0EgRzMwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAARK7liuTcpm +3gY6oxH84Bjwbhy6LTAMidnW7ptzg6kjFYwvWYpa3RTqnVkrQ7cG7DK2uu5Bta1d +oYXM6h0UZqNnfkbilPPntlahFVmhTzeXuSIevRHr9LIfXsMUmuXZl5mjYzBhMA4G +A1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRHd8MUi2I5 +DMlv4VBN0BBY3JWIbTAfBgNVHSMEGDAWgBRHd8MUi2I5DMlv4VBN0BBY3JWIbTAK +BggqhkjOPQQDAwNpADBmAjEAj6jcnboMBBf6Fek9LykBl7+BFjNAk2z8+e2AcG+q +j9uEwov1NcoG3GRvaBbhj5G5AjEA2Euly8LQCGzpGPta3U1fJAuwACEl74+nBCZx +4nxp5V2a+EEfOzmTk51V6s2N8fvB +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIF0DCCBLigAwIBAgIEOrZQizANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJC +TTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDElMCMGA1UECxMcUm9vdCBDZXJ0 +aWZpY2F0aW9uIEF1dGhvcml0eTEuMCwGA1UEAxMlUXVvVmFkaXMgUm9vdCBDZXJ0 +aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wMTAzMTkxODMzMzNaFw0yMTAzMTcxODMz +MzNaMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMSUw +IwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQDEyVR +dW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv2G1lVO6V/z68mcLOhrfEYBklbTRvM16z/Yp +li4kVEAkOPcahdxYTMukJ0KX0J+DisPkBgNbAKVRHnAEdOLB1Dqr1607BxgFjv2D +rOpm2RgbaIr1VxqYuvXtdj182d6UajtLF8HVj71lODqV0D1VNk7feVcxKh7YWWVJ +WCCYfqtffp/p1k3sg3Spx2zY7ilKhSoGFPlU5tPaZQeLYzcS19Dsw3sgQUSj7cug +F+FxZc4dZjH3dgEZyH0DWLaVSR2mEiboxgx24ONmy+pdpibu5cxfvWenAScOospU +xbF6lR1xHkopigPcakXBpBlebzbNw6Kwt/5cOOJSvPhEQ+aQuwIDAQABo4ICUjCC +Ak4wPQYIKwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwczovL29jc3AucXVv +dmFkaXNvZmZzaG9yZS5jb20wDwYDVR0TAQH/BAUwAwEB/zCCARoGA1UdIASCAREw +ggENMIIBCQYJKwYBBAG+WAABMIH7MIHUBggrBgEFBQcCAjCBxxqBxFJlbGlhbmNl +IG9uIHRoZSBRdW9WYWRpcyBSb290IENlcnRpZmljYXRlIGJ5IGFueSBwYXJ0eSBh +c3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJsZSBzdGFuZGFy +ZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRpb24gcHJh +Y3RpY2VzLCBhbmQgdGhlIFF1b1ZhZGlzIENlcnRpZmljYXRlIFBvbGljeS4wIgYI +KwYBBQUHAgEWFmh0dHA6Ly93d3cucXVvdmFkaXMuYm0wHQYDVR0OBBYEFItLbe3T +KbkGGew5Oanwl4Rqy+/fMIGuBgNVHSMEgaYwgaOAFItLbe3TKbkGGew5Oanwl4Rq +y+/foYGEpIGBMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1p +dGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYD +VQQDEyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggQ6tlCL +MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAitQUtf70mpKnGdSk +fnIYj9lofFIk3WdvOXrEql494liwTXCYhGHoG+NpGA7O+0dQoE7/8CQfvbLO9Sf8 +7C9TqnN7Az10buYWnuulLsS/VidQK2K6vkscPFVcQR0kvoIgR13VRH56FmjffU1R +cHhXHTMe/QKZnAzNCgVPx7uOpHX6Sm2xgI4JVrmcGmD+XcHXetwReNDWXcG31a0y +mQM6isxUJTkxgXsTIlG6Rmyhu576BGxJJnSP0nPrzDCi5upZIof4l/UO/erMkqQW +xFIY6iHOsfHmhIHluqmGKPJDWl0Snawe2ajlCmqnf6CHKc/yiU3U7MXi5nrQNiOK +SnQ2+Q== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIUeFhfLq0sGUvjNwc1NBMotZbUZZMwDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMSBHMzAeFw0xMjAxMTIxNzI3NDRaFw00 +MjAxMTIxNzI3NDRaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDEgRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCgvlAQjunybEC0BJyFuTHK3C3kEakEPBtV +wedYMB0ktMPvhd6MLOHBPd+C5k+tR4ds7FtJwUrVu4/sh6x/gpqG7D0DmVIB0jWe +rNrwU8lmPNSsAgHaJNM7qAJGr6Qc4/hzWHa39g6QDbXwz8z6+cZM5cOGMAqNF341 +68Xfuw6cwI2H44g4hWf6Pser4BOcBRiYz5P1sZK0/CPTz9XEJ0ngnjybCKOLXSoh +4Pw5qlPafX7PGglTvF0FBM+hSo+LdoINofjSxxR3W5A2B4GbPgb6Ul5jxaYA/qXp +UhtStZI5cgMJYr2wYBZupt0lwgNm3fME0UDiTouG9G/lg6AnhF4EwfWQvTA9xO+o +abw4m6SkltFi2mnAAZauy8RRNOoMqv8hjlmPSlzkYZqn0ukqeI1RPToV7qJZjqlc +3sX5kCLliEVx3ZGZbHqfPT2YfF72vhZooF6uCyP8Wg+qInYtyaEQHeTTRCOQiJ/G +KubX9ZqzWB4vMIkIG1SitZgj7Ah3HJVdYdHLiZxfokqRmu8hqkkWCKi9YSgxyXSt +hfbZxbGL0eUQMk1fiyA6PEkfM4VZDdvLCXVDaXP7a3F98N/ETH3Goy7IlXnLc6KO +Tk0k+17kBL5yG6YnLUlamXrXXAkgt3+UuU/xDRxeiEIbEbfnkduebPRq34wGmAOt +zCjvpUfzUwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQUo5fW816iEOGrRZ88F2Q87gFwnMwwDQYJKoZIhvcNAQELBQAD +ggIBABj6W3X8PnrHX3fHyt/PX8MSxEBd1DKquGrX1RUVRpgjpeaQWxiZTOOtQqOC +MTaIzen7xASWSIsBx40Bz1szBpZGZnQdT+3Btrm0DWHMY37XLneMlhwqI2hrhVd2 +cDMT/uFPpiN3GPoajOi9ZcnPP/TJF9zrx7zABC4tRi9pZsMbj/7sPtPKlL92CiUN +qXsCHKnQO18LwIE6PWThv6ctTr1NxNgpxiIY0MWscgKCP6o6ojoilzHdCGPDdRS5 +YCgtW2jgFqlmgiNR9etT2DGbe+m3nUvriBbP+V04ikkwj+3x6xn0dxoxGE1nVGwv +b2X52z3sIexe9PSLymBlVNFxZPT5pqOBMzYzcfCkeF9OrYMh3jRJjehZrJ3ydlo2 +8hP0r+AJx2EqbPfgna67hkooby7utHnNkDPDs3b69fBsnQGQ+p6Q9pxyz0fawx/k +NSBT8lTR32GDpgLiJTjehTItXnOQUl1CxM49S+H5GYQd1aJQzEH7QRTDvdbJWqNj +ZgKAvQU6O0ec7AAmTPWIUb+oI38YB7AL7YsmoWTTYUrrXJ/es69nA7Mf3W1daWhp +q1467HxpvMc7hU6eFbm0FU/DlXpY18ls6Wy58yljXrQs8C097Vpl4KlbQMJImYFt +nh8GKjwStIsPm6Ik8KaN1nrgS7ZklmOVhMJKzRwuJIczYOXD +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFtzCCA5+gAwIBAgICBQkwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x +GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv +b3QgQ0EgMjAeFw0wNjExMjQxODI3MDBaFw0zMTExMjQxODIzMzNaMEUxCzAJBgNV +BAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9W +YWRpcyBSb290IENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCa +GMpLlA0ALa8DKYrwD4HIrkwZhR0In6spRIXzL4GtMh6QRr+jhiYaHv5+HBg6XJxg +Fyo6dIMzMH1hVBHL7avg5tKifvVrbxi3Cgst/ek+7wrGsxDp3MJGF/hd/aTa/55J +WpzmM+Yklvc/ulsrHHo1wtZn/qtmUIttKGAr79dgw8eTvI02kfN/+NsRE8Scd3bB +rrcCaoF6qUWD4gXmuVbBlDePSHFjIuwXZQeVikvfj8ZaCuWw419eaxGrDPmF60Tp ++ARz8un+XJiM9XOva7R+zdRcAitMOeGylZUtQofX1bOQQ7dsE/He3fbE+Ik/0XX1 +ksOR1YqI0JDs3G3eicJlcZaLDQP9nL9bFqyS2+r+eXyt66/3FsvbzSUr5R/7mp/i +Ucw6UwxI5g69ybR2BlLmEROFcmMDBOAENisgGQLodKcftslWZvB1JdxnwQ5hYIiz +PtGo/KPaHbDRsSNU30R2be1B2MGyIrZTHN81Hdyhdyox5C315eXbyOD/5YDXC2Og +/zOhD7osFRXql7PSorW+8oyWHhqPHWykYTe5hnMz15eWniN9gqRMgeKh0bpnX5UH +oycR7hYQe7xFSkyyBNKr79X9DFHOUGoIMfmR2gyPZFwDwzqLID9ujWc9Otb+fVuI +yV77zGHcizN300QyNQliBJIWENieJ0f7OyHj+OsdWwIDAQABo4GwMIGtMA8GA1Ud +EwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1UdDgQWBBQahGK8SEwzJQTU7tD2 +A8QZRtGUazBuBgNVHSMEZzBlgBQahGK8SEwzJQTU7tD2A8QZRtGUa6FJpEcwRTEL +MAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMT +ElF1b1ZhZGlzIFJvb3QgQ0EgMoICBQkwDQYJKoZIhvcNAQEFBQADggIBAD4KFk2f +BluornFdLwUvZ+YTRYPENvbzwCYMDbVHZF34tHLJRqUDGCdViXh9duqWNIAXINzn +g/iN/Ae42l9NLmeyhP3ZRPx3UIHmfLTJDQtyU/h2BwdBR5YM++CCJpNVjP4iH2Bl +fF/nJrP3MpCYUNQ3cVX2kiF495V5+vgtJodmVjB3pjd4M1IQWK4/YY7yarHvGH5K +WWPKjaJW1acvvFYfzznB4vsKqBUsfU16Y8Zsl0Q80m/DShcK+JDSV6IZUaUtl0Ha +B0+pUNqQjZRG4T7wlP0QADj1O+hA4bRuVhogzG9Yje0uRY/W6ZM/57Es3zrWIozc +hLsib9D45MY56QSIPMO661V6bYCZJPVsAfv4l7CUW+v90m/xd2gNNWQjrLhVoQPR +TUIZ3Ph1WVaj+ahJefivDrkRoHy3au000LYmYjgahwz46P0u05B/B5EqHdZ+XIWD +mbA4CD/pXvk1B+TJYm5Xf6dQlfe6yJvmjqIBxdZmv3lh8zwc4bmCXF2gw+nYSL0Z +ohEUGW6yhhtoPkg3Goi3XZZenMfvJ2II4pEZXNLxId26F0KCl3GBUzGpn/Z9Yr9y +4aOTHcyKJloJONDO1w2AFrR4pTqHTI2KpdVGl/IsELm8VCLAAVBpQ570su9t+Oza +8eOx79+Rj1QqCyXBJhnEUhAFZdWCEOrCMc0u +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIURFc0JFuBiZs18s64KztbpybwdSgwDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjAxMTIxODU5MzJaFw00 +MjAxMTIxODU5MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDIgRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQChriWyARjcV4g/Ruv5r+LrI3HimtFhZiFf +qq8nUeVuGxbULX1QsFN3vXg6YOJkApt8hpvWGo6t/x8Vf9WVHhLL5hSEBMHfNrMW +n4rjyduYNM7YMxcoRvynyfDStNVNCXJJ+fKH46nafaF9a7I6JaltUkSs+L5u+9ym +c5GQYaYDFCDy54ejiK2toIz/pgslUiXnFgHVy7g1gQyjO/Dh4fxaXc6AcW34Sas+ +O7q414AB+6XrW7PFXmAqMaCvN+ggOp+oMiwMzAkd056OXbxMmO7FGmh77FOm6RQ1 +o9/NgJ8MSPsc9PG/Srj61YxxSscfrf5BmrODXfKEVu+lV0POKa2Mq1W/xPtbAd0j +IaFYAI7D0GoT7RPjEiuA3GfmlbLNHiJuKvhB1PLKFAeNilUSxmn1uIZoL1NesNKq +IcGY5jDjZ1XHm26sGahVpkUG0CM62+tlXSoREfA7T8pt9DTEceT/AFr2XK4jYIVz +8eQQsSWu1ZK7E8EM4DnatDlXtas1qnIhO4M15zHfeiFuuDIIfR0ykRVKYnLP43eh +vNURG3YBZwjgQQvD6xVu+KQZ2aKrr+InUlYrAoosFCT5v0ICvybIxo/gbjh9Uy3l +7ZizlWNof/k19N+IxWA1ksB8aRxhlRbQ694Lrz4EEEVlWFA4r0jyWbYW8jwNkALG +cC4BrTwV1wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQU7edvdlq/YOxJW8ald7tyFnGbxD0wDQYJKoZIhvcNAQELBQAD +ggIBAJHfgD9DCX5xwvfrs4iP4VGyvD11+ShdyLyZm3tdquXK4Qr36LLTn91nMX66 +AarHakE7kNQIXLJgapDwyM4DYvmL7ftuKtwGTTwpD4kWilhMSA/ohGHqPHKmd+RC +roijQ1h5fq7KpVMNqT1wvSAZYaRsOPxDMuHBR//47PERIjKWnML2W2mWeyAMQ0Ga +W/ZZGYjeVYg3UQt4XAoeo0L9x52ID8DyeAIkVJOviYeIyUqAHerQbj5hLja7NQ4n +lv1mNDthcnPxFlxHBlRJAHpYErAK74X9sbgzdWqTHBLmYF5vHX/JHyPLhGGfHoJE ++V+tYlUkmlKY7VHnoX6XOuYvHxHaU4AshZ6rNRDbIl9qxV6XU/IyAgkwo1jwDQHV +csaxfGl7w/U2Rcxhbl5MlMVerugOXou/983g7aEOGzPuVBj+D77vfoRrQ+NwmNtd +dbINWQeFFSM51vHfqSYP1kjHs6Yi9TM3WpVHn3u6GBVv/9YUZINJ0gpnIdsPNWNg +KCLjsZWDzYWm3S8P52dSbrsvhXz1SnPnxT7AvSESBT/8twNJAlvIJebiVDj1eYeM +HVOyToV7BjjHLPj4sHKNJeV3UvQDHEimUF+IIDBu8oJDqz2XhOdT+yHBTw8imoa4 +WSr2Rz0ZiC3oheGe7IUIarFsNMkd7EgrO3jtZsSOeWmD3n+M +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIGnTCCBIWgAwIBAgICBcYwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x +GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv +b3QgQ0EgMzAeFw0wNjExMjQxOTExMjNaFw0zMTExMjQxOTA2NDRaMEUxCzAJBgNV +BAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9W +YWRpcyBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDM +V0IWVJzmmNPTTe7+7cefQzlKZbPoFog02w1ZkXTPkrgEQK0CSzGrvI2RaNggDhoB +4hp7Thdd4oq3P5kazethq8Jlph+3t723j/z9cI8LoGe+AaJZz3HmDyl2/7FWeUUr +H556VOijKTVopAFPD6QuN+8bv+OPEKhyq1hX51SGyMnzW9os2l2ObjyjPtr7guXd +8lyyBTNvijbO0BNO/79KDDRMpsMhvVAEVeuxu537RR5kFd5VAYwCdrXLoT9Cabwv +vWhDFlaJKjdhkf2mrk7AyxRllDdLkgbvBNDInIjbC3uBr7E9KsRlOni27tyAsdLT +mZw67mtaa7ONt9XOnMK+pUsvFrGeaDsGb659n/je7Mwpp5ijJUMv7/FfJuGITfhe +btfZFG4ZM2mnO4SJk8RTVROhUXhA+LjJou57ulJCg54U7QVSWllWp5f8nT8KKdjc +T5EOE7zelaTfi5m+rJsziO+1ga8bxiJTyPbH7pcUsMV8eFLI8M5ud2CEpukqdiDt +WAEXMJPpGovgc2PZapKUSU60rUqFxKMiMPwJ7Wgic6aIDFUhWMXhOp8q3crhkODZ +c6tsgLjoC2SToJyMGf+z0gzskSaHirOi4XCPLArlzW1oUevaPwV/izLmE1xr/l9A +4iLItLRkT9a6fUg+qGkM17uGcclzuD87nSVL2v9A6wIDAQABo4IBlTCCAZEwDwYD +VR0TAQH/BAUwAwEB/zCB4QYDVR0gBIHZMIHWMIHTBgkrBgEEAb5YAAMwgcUwgZMG +CCsGAQUFBwICMIGGGoGDQW55IHVzZSBvZiB0aGlzIENlcnRpZmljYXRlIGNvbnN0 +aXR1dGVzIGFjY2VwdGFuY2Ugb2YgdGhlIFF1b1ZhZGlzIFJvb3QgQ0EgMyBDZXJ0 +aWZpY2F0ZSBQb2xpY3kgLyBDZXJ0aWZpY2F0aW9uIFByYWN0aWNlIFN0YXRlbWVu +dC4wLQYIKwYBBQUHAgEWIWh0dHA6Ly93d3cucXVvdmFkaXNnbG9iYWwuY29tL2Nw +czALBgNVHQ8EBAMCAQYwHQYDVR0OBBYEFPLAE+CCQz777i9nMpY1XNu4ywLQMG4G +A1UdIwRnMGWAFPLAE+CCQz777i9nMpY1XNu4ywLQoUmkRzBFMQswCQYDVQQGEwJC +TTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDEbMBkGA1UEAxMSUXVvVmFkaXMg +Um9vdCBDQSAzggIFxjANBgkqhkiG9w0BAQUFAAOCAgEAT62gLEz6wPJv92ZVqyM0 +7ucp2sNbtrCD2dDQ4iH782CnO11gUyeim/YIIirnv6By5ZwkajGxkHon24QRiSem +d1o417+shvzuXYO8BsbRd2sPbSQvS3pspweWyuOEn62Iix2rFo1bZhfZFvSLgNLd ++LJ2w/w4E6oM3kJpK27zPOuAJ9v1pkQNn1pVWQvVDVJIxa6f8i+AxeoyUDUSly7B +4f/xI4hROJ/yZlZ25w9Rl6VSDE1JUZU2Pb+iSwwQHYaZTKrzchGT5Or2m9qoXadN +t54CrnMAyNojA+j56hl0YgCUyyIgvpSnWbWCar6ZeXqp8kokUvd0/bpO5qgdAm6x +DYBEwa7TIzdfu4V8K5Iu6H6li92Z4b8nby1dqnuH/grdS/yO9SbkbnBCbjPsMZ57 +k8HkyWkaPcBrTiJt7qtYTcbQQcEr6k8Sh17rRdhs9ZgC06DYVYoGmRmioHfRMJ6s +zHXug/WwYjnPbFfiTNKRCw51KBuav/0aQ/HKd/s7j2G4aSgWQgRecCocIdiP4b0j +Wy10QJLZYxkNc91pvGJHvOB0K7Lrfb5BG7XARsWhIstfTsEokt4YutUqKLsRixeT +mJlglFwjz1onl14LBQaTNx47aTbrqZ5hHY8y2o4M1nQ+ewkk2gF3R8Q7zTSMmfXK +4SVhM7JZG+Ju1zdXtg2pEto= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIULvWbAiin23r/1aOp7r0DoM8Sah0wDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMyBHMzAeFw0xMjAxMTIyMDI2MzJaFw00 +MjAxMTIyMDI2MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDMgRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCzyw4QZ47qFJenMioKVjZ/aEzHs286IxSR +/xl/pcqs7rN2nXrpixurazHb+gtTTK/FpRp5PIpM/6zfJd5O2YIyC0TeytuMrKNu +FoM7pmRLMon7FhY4futD4tN0SsJiCnMK3UmzV9KwCoWdcTzeo8vAMvMBOSBDGzXR +U7Ox7sWTaYI+FrUoRqHe6okJ7UO4BUaKhvVZR74bbwEhELn9qdIoyhA5CcoTNs+c +ra1AdHkrAj80//ogaX3T7mH1urPnMNA3I4ZyYUUpSFlob3emLoG+B01vr87ERROR +FHAGjx+f+IdpsQ7vw4kZ6+ocYfx6bIrc1gMLnia6Et3UVDmrJqMz6nWB2i3ND0/k +A9HvFZcba5DFApCTZgIhsUfei5pKgLlVj7WiL8DWM2fafsSntARE60f75li59wzw +eyuxwHApw0BiLTtIadwjPEjrewl5qW3aqDCYz4ByA4imW0aucnl8CAMhZa634Ryl +sSqiMd5mBPfAdOhx3v89WcyWJhKLhZVXGqtrdQtEPREoPHtht+KPZ0/l7DxMYIBp +VzgeAVuNVejH38DMdyM0SXV89pgR6y3e7UEuFAUCf+D+IOs15xGsIs5XPd7JMG0Q +A4XN8f+MFrXBsj6IbGB/kE+V9/YtrQE5BwT6dYB9v0lQ7e/JxHwc64B+27bQ3RP+ +ydOc17KXqQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQUxhfQvKjqAkPyGwaZXSuQILnXnOQwDQYJKoZIhvcNAQELBQAD +ggIBADRh2Va1EodVTd2jNTFGu6QHcrxfYWLopfsLN7E8trP6KZ1/AvWkyaiTt3px +KGmPc+FSkNrVvjrlt3ZqVoAh313m6Tqe5T72omnHKgqwGEfcIHB9UqM+WXzBusnI +FUBhynLWcKzSt/Ac5IYp8M7vaGPQtSCKFWGafoaYtMnCdvvMujAWzKNhxnQT5Wvv +oxXqA/4Ti2Tk08HS6IT7SdEQTXlm66r99I0xHnAUrdzeZxNMgRVhvLfZkXdxGYFg +u/BYpbWcC/ePIlUnwEsBbTuZDdQdm2NnL9DuDcpmvJRPpq3t/O5jrFc/ZSXPsoaP +0Aj/uHYUbt7lJ+yreLVTubY/6CD50qi+YUbKh4yE8/nxoGibIh6BJpsQBJFxwAYf +3KDTuVan45gtf4Od34wrnDKOMpTwATwiKp9Dwi7DmDkHOHv8XgBCH/MyJnmDhPbl +8MFREsALHgQjDFSlTC9JxUrRtm5gDWv8a4uFJGS3iQ6rJUdbPM9+Sb3H6QrG2vd+ +DhcI00iX0HGS8A85PjRqHH3Y8iKuu2n0M7SmSFXRDw4m6Oy2Cy2nhTXN/VnIn9HN +PlopNLk9hM6xZdRZkZFWdSHBd575euFgndOtBBj0fOtek49TSiIp+EgrPk2GrFt/ +ywaZWWDYWGWVjUTR939+J399roD1B0y2PpxxVJkES/1Y+Zj0 +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIClDCCAhqgAwIBAgIILCmcWxbtBZUwCgYIKoZIzj0EAwIwfzELMAkGA1UEBhMC +VVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9T +U0wgQ29ycG9yYXRpb24xNDAyBgNVBAMMK1NTTC5jb20gRVYgUm9vdCBDZXJ0aWZp +Y2F0aW9uIEF1dGhvcml0eSBFQ0MwHhcNMTYwMjEyMTgxNTIzWhcNNDEwMjEyMTgx +NTIzWjB/MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hv +dXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjE0MDIGA1UEAwwrU1NMLmNv +bSBFViBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAGByqGSM49 +AgEGBSuBBAAiA2IABKoSR5CYG/vvw0AHgyBO8TCCogbR8pKGYfL2IWjKAMTH6kMA +VIbc/R/fALhBYlzccBYy3h+Z1MzFB8gIH2EWB1E9fVwHU+M1OIzfzZ/ZLg1Kthku +WnBaBu2+8KGwytAJKaNjMGEwHQYDVR0OBBYEFFvKXuXe0oGqzagtZFG22XKbl+ZP +MA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUW8pe5d7SgarNqC1kUbbZcpuX +5k8wDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2gAMGUCMQCK5kCJN+vp1RPZ +ytRrJPOwPYdGWBrssd9v+1a6cGvHOMzosYxPD/fxZ3YOg9AeUY8CMD32IygmTMZg +h5Mmm7I1HrrW9zzRHM76JTymGoEVW/MSD2zuZYrJh6j5B+BimoxcSg== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIF6zCCA9OgAwIBAgIIVrYpzTS8ePYwDQYJKoZIhvcNAQELBQAwgYIxCzAJBgNV +BAYTAlVTMQ4wDAYDVQQIDAVUZXhhczEQMA4GA1UEBwwHSG91c3RvbjEYMBYGA1UE +CgwPU1NMIENvcnBvcmF0aW9uMTcwNQYDVQQDDC5TU0wuY29tIEVWIFJvb3QgQ2Vy +dGlmaWNhdGlvbiBBdXRob3JpdHkgUlNBIFIyMB4XDTE3MDUzMTE4MTQzN1oXDTQy +MDUzMDE4MTQzN1owgYIxCzAJBgNVBAYTAlVTMQ4wDAYDVQQIDAVUZXhhczEQMA4G +A1UEBwwHSG91c3RvbjEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMTcwNQYDVQQD +DC5TU0wuY29tIEVWIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgUlNBIFIy +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAjzZlQOHWTcDXtOlG2mvq +M0fNTPl9fb69LT3w23jhhqXZuglXaO1XPqDQCEGD5yhBJB/jchXQARr7XnAjssuf +OePPxU7Gkm0mxnu7s9onnQqG6YE3Bf7wcXHswxzpY6IXFJ3vG2fThVUCAtZJycxa +4bH3bzKfydQ7iEGonL3Lq9ttewkfokxykNorCPzPPFTOZw+oz12WGQvE43LrrdF9 +HSfvkusQv1vrO6/PgN3B0pYEW3p+pKk8OHakYo6gOV7qd89dAFmPZiw+B6KjBSYR +aZfqhbcPlgtLyEDhULouisv3D5oi53+aNxPN8k0TayHRwMwi8qFG9kRpnMphNQcA +b9ZhCBHqurj26bNg5U257J8UZslXWNvNh2n4ioYSA0e/ZhN2rHd9NCSFg83XqpyQ +Gp8hLH94t2S42Oim9HizVcuE0jLEeK6jj2HdzghTreyI/BXkmg3mnxp3zkyPuBQV +PWKchjgGAGYS5Fl2WlPAApiiECtoRHuOec4zSnaqW4EWG7WK2NAAe15itAnWhmMO +pgWVSbooi4iTsjQc2KRVbrcc0N6ZVTsj9CLg+SlmJuwgUHfbSguPvuUCYHBBXtSu +UDkiFCbLsjtzdFVHB3mBOagwE0TlBIqulhMlQg+5U8Sb/M3kHN48+qvWBkofZ6aY +MBzdLNvcGJVXZsb/XItW9XcCAwEAAaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAfBgNV +HSMEGDAWgBT5YLvU49U09rj1BoAlp3PbRmmonjAdBgNVHQ4EFgQU+WC71OPVNPa4 +9QaAJadz20ZpqJ4wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQBW +s47LCp1Jjr+kxJG7ZhcFUZh1++VQLHqe8RT6q9OKPv+RKY9ji9i0qVQBDb6Thi/5 +Sm3HXvVX+cpVHBK+Rw82xd9qt9t1wkclf7nxY/hoLVUE0fKNsKTPvDxeH3jnpaAg +cLAExbf3cqfeIg29MyVGjGSSJuM+LmOW2puMPfgYCdcDzH2GguDKBAdRUNf/ktUM +79qGn5nX67evaOI5JpS6aLe/g9Pqemc9YmeuJeVy6OLk7K4S9ksrPJ/psEDzOFSz +/bdoyNrGj1E8svuR3Bznm53htw1yj+KkxKl4+esUrMZDBcJlOSgYAsOCsp0FvmXt +ll9ldDz7CTUue5wT/RsPXcdtgTpWD8w74a8CLyKsRspGPKAcTNZEtF4uXBVmCeEm +Kf7GUmG6sXP/wwyc5WxqlD8UykAWlYTzWamsX0xhk23RO8yilQwipmdnRC652dKK +QbNmC1r7fSOl8hqw/96bg5Qu0T/fkreRrwU7ZcegbLHNYhLDkBvjJc40vG93drEQ +w/cFGsDWr3RiSBd3kmmQYRzelYB0VI8YHMPzA9C/pEN1hlMYegouCRw2n5H9gooi +S9EOUCXdywMMF8mDAAhONU2Ki+3wApRmLER/y5UnlhetCTCstnEXbosX9hwJ1C07 +mKVx01QT2WDz9UtmT/rx7iASjbSsV7FFY6GsdqnC+w== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICjTCCAhSgAwIBAgIIdebfy8FoW6gwCgYIKoZIzj0EAwIwfDELMAkGA1UEBhMC +VVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9T +U0wgQ29ycG9yYXRpb24xMTAvBgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZpY2F0 +aW9uIEF1dGhvcml0eSBFQ0MwHhcNMTYwMjEyMTgxNDAzWhcNNDEwMjEyMTgxNDAz +WjB8MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hvdXN0 +b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8GA1UEAwwoU1NMLmNvbSBS +b290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAGByqGSM49AgEGBSuB +BAAiA2IABEVuqVDEpiM2nl8ojRfLliJkP9x6jh3MCLOicSS6jkm5BBtHllirLZXI +7Z4INcgn64mMU1jrYor+8FsPazFSY0E7ic3s7LaNGdM0B9y7xgZ/wkWV7Mt/qCPg +CemB+vNH06NjMGEwHQYDVR0OBBYEFILRhXMw5zUE044CkvvlpNHEIejNMA8GA1Ud +EwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUgtGFczDnNQTTjgKS++Wk0cQh6M0wDgYD +VR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2cAMGQCMG/n61kRpGDPYbCWe+0F+S8T +kdzt5fxQaxFGRrMcIQBiu77D5+jNB5n5DQtdcj7EqgIwH7y6C+IwJPt8bYBVCpk+ +gA0z5Wajs6O7pdWLjwkspl1+4vAHCGht0nxpbl/f5Wpl +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIF3TCCA8WgAwIBAgIIeyyb0xaAMpkwDQYJKoZIhvcNAQELBQAwfDELMAkGA1UE +BhMCVVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQK +DA9TU0wgQ29ycG9yYXRpb24xMTAvBgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZp +Y2F0aW9uIEF1dGhvcml0eSBSU0EwHhcNMTYwMjEyMTczOTM5WhcNNDEwMjEyMTcz +OTM5WjB8MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hv +dXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8GA1UEAwwoU1NMLmNv +bSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFJTQTCCAiIwDQYJKoZIhvcN +AQEBBQADggIPADCCAgoCggIBAPkP3aMrfcvQKv7sZ4Wm5y4bunfh4/WvpOz6Sl2R +xFdHaxh3a3by/ZPkPQ/CFp4LZsNWlJ4Xg4XOVu/yFv0AYvUiCVToZRdOQbngT0aX +qhvIuG5iXmmxX9sqAn78bMrzQdjt0Oj8P2FI7bADFB0QDksZ4LtO7IZl/zbzXmcC +C52GVWH9ejjt/uIZALdvoVBidXQ8oPrIJZK0bnoix/geoeOy3ZExqysdBP+lSgQ3 +6YWkMyv94tZVNHwZpEpox7Ko07fKoZOI68GXvIz5HdkihCR0xwQ9aqkpk8zruFvh +/l8lqjRYyMEjVJ0bmBHDOJx+PYZspQ9AhnwC9FwCTyjLrnGfDzrIM/4RJTXq/LrF +YD3ZfBjVsqnTdXgDciLKOsMf7yzlLqn6niy2UUb9rwPW6mBo6oUWNmuF6R7As93E +JNyAKoFBbZQ+yODJgUEAnl6/f8UImKIYLEJAs/lvOCdLToD0PYFH4Ih86hzOtXVc +US4cK38acijnALXRdMbX5J+tB5O2UzU1/Dfkw/ZdFr4hc96SCvigY2q8lpJqPvi8 +ZVWb3vUNiSYE/CUapiVpy8JtynziWV+XrOvvLsi81xtZPCvM8hnIk2snYxnP/Okm ++Mpxm3+T/jRnhE6Z6/yzeAkzcLpmpnbtG3PrGqUNxCITIJRWCk4sbE6x/c+cCbqi +M+2HAgMBAAGjYzBhMB0GA1UdDgQWBBTdBAkHovV6fVJTEpKV7jiAJQ2mWTAPBgNV +HRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFN0ECQei9Xp9UlMSkpXuOIAlDaZZMA4G +A1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAIBgRlCn7Jp0cHh5wYfGV +cpNxJK1ok1iOMq8bs3AD/CUrdIWQPXhq9LmLpZc7tRiRux6n+UBbkflVma8eEdBc +Hadm47GUBwwyOabqG7B52B2ccETjit3E+ZUfijhDPwGFpUenPUayvOUiaPd7nNgs +PgohyC0zrL/FgZkxdMF1ccW+sfAjRfSda/wZY52jvATGGAslu1OJD7OAUN5F7kR/ +q5R4ZJjT9ijdh9hwZXT7DrkT66cPYakylszeu+1jTBi7qUD3oFRuIIhxdRjqerQ0 +cuAjJ3dctpDqhiVAq+8zD8ufgr6iIPv2tS0a5sKFsXQP+8hlAqRSAUfdSSLBv9jr +a6x+3uxjMxW3IwiPxg+NQVrdjsW5j+VFP3jbutIbQLH+cU0/4IGiul607BXgk90I +H37hVZkLId6Tngr75qNJvTYw/ud3sqB1l7UtgYgXZSD32pAAn8lSzDLKNXz1PQ/Y +K9f1JmzJBjSWFupwWRoyeXkLtoh/D1JIPb9s2KJELtFOt3JY04kTlf5Eq/jXixtu +nLwsoFvVagCvXzfh1foQC5ichucmj87w7G6KVwuA406ywKBjYZC6VWg3dGq2ktuf +oYYitmUnDuy2n0Jg5GfCtdpBC8TTi2EbvPofkSvXRAdeuims2cXp71NIWuuA8ShY +Ic2wBlX7Jz9TkHCpBB5XJ7k= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDcjCCAlqgAwIBAgIUPopdB+xV0jLVt+O2XwHrLdzk1uQwDQYJKoZIhvcNAQEL +BQAwUTELMAkGA1UEBhMCUEwxKDAmBgNVBAoMH0tyYWpvd2EgSXpiYSBSb3psaWN6 +ZW5pb3dhIFMuQS4xGDAWBgNVBAMMD1NaQUZJUiBST09UIENBMjAeFw0xNTEwMTkw +NzQzMzBaFw0zNTEwMTkwNzQzMzBaMFExCzAJBgNVBAYTAlBMMSgwJgYDVQQKDB9L +cmFqb3dhIEl6YmEgUm96bGljemVuaW93YSBTLkEuMRgwFgYDVQQDDA9TWkFGSVIg +Uk9PVCBDQTIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC3vD5QqEvN +QLXOYeeWyrSh2gwisPq1e3YAd4wLz32ohswmUeQgPYUM1ljj5/QqGJ3a0a4m7utT +3PSQ1hNKDJA8w/Ta0o4NkjrcsbH/ON7Dui1fgLkCvUqdGw+0w8LBZwPd3BucPbOw +3gAeqDRHu5rr/gsUvTaE2g0gv/pby6kWIK05YO4vdbbnl5z5Pv1+TW9NL++IDWr6 +3fE9biCloBK0TXC5ztdyO4mTp4CEHCdJckm1/zuVnsHMyAHs6A6KCpbns6aH5db5 +BSsNl0BwPLqsdVqc1U2dAgrSS5tmS0YHF2Wtn2yIANwiieDhZNRnvDF5YTy7ykHN +XGoAyDw4jlivAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD +AgEGMB0GA1UdDgQWBBQuFqlKGLXLzPVvUPMjX/hd56zwyDANBgkqhkiG9w0BAQsF +AAOCAQEAtXP4A9xZWx126aMqe5Aosk3AM0+qmrHUuOQn/6mWmc5G4G18TKI4pAZw +8PRBEew/R40/cof5O/2kbytTAOD/OblqBw7rHRz2onKQy4I9EYKL0rufKq8h5mOG +nXkZ7/e7DDWQw4rtTw/1zBLZpD67oPwglV9PJi8RI4NOdQcPv5vRtB3pEAT+ymCP +oky4rc/hkA/NrgrHXXu3UNLUYfrVFdvXn4dRVOul4+vJhaAlIDf7js4MNIThPIGy +d05DpYhfhmehPea0XGG2Ptv+tyjFogeutcrKjSoS75ftwjCkySp6+/NNIxuZMzSg +LvWpCz/UXeHPhJ/iGcJfitYgHuNztw== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDbTCCAlWgAwIBAgIBATANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQGEwJKUDEr +MCkGA1UEChMiSmFwYW4gQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcywgSW5jLjEcMBoG +A1UEAxMTU2VjdXJlU2lnbiBSb290Q0ExMTAeFw0wOTA0MDgwNDU2NDdaFw0yOTA0 +MDgwNDU2NDdaMFgxCzAJBgNVBAYTAkpQMSswKQYDVQQKEyJKYXBhbiBDZXJ0aWZp +Y2F0aW9uIFNlcnZpY2VzLCBJbmMuMRwwGgYDVQQDExNTZWN1cmVTaWduIFJvb3RD +QTExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA/XeqpRyQBTvLTJsz +i1oURaTnkBbR31fSIRCkF/3frNYfp+TbfPfs37gD2pRY/V1yfIw/XwFndBWW4wI8 +h9uuywGOwvNmxoVF9ALGOrVisq/6nL+k5tSAMJjzDbaTj6nU2DbysPyKyiyhFTOV +MdrAG/LuYpmGYz+/3ZMqg6h2uRMft85OQoWPIucuGvKVCbIFtUROd6EgvanyTgp9 +UK31BQ1FT0Zx/Sg+U/sE2C3XZR1KG/rPO7AxmjVuyIsG0wCR8pQIZUyxNAYAeoni +8McDWc/V1uinMrPmmECGxc0nEovMe863ETxiYAcjPitAbpSACW22s293bzUIUPsC +h8U+iQIDAQABo0IwQDAdBgNVHQ4EFgQUW/hNT7KlhtQ60vFjmqC+CfZXt94wDgYD +VR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEB +AKChOBZmLqdWHyGcBvod7bkixTgm2E5P7KN/ed5GIaGHd48HCJqypMWvDzKYC3xm +KbabfSVSSUOrTC4rbnpwrxYO4wJs+0LmGJ1F2FXI6Dvd5+H0LgscNFxsWEr7jIhQ +X5Ucv+2rIrVls4W6ng+4reV6G4pQOh29Dbx7VFALuUKvVaAYga1lme++5Jy/xIWr +QbJUb9wlze144o4MjQlJ3WN7WmmWAiGovVJZ6X01y8hSyn+B/tlr0/cR7SXf+Of5 +pPpyl4RTDaXQMhhRdlkUbA/r7F+AjHVDg8OFmP9Mni0N5HeDk061lgeLKBObjBmN +QSdJQO7e5iNEOdyhIta6A/I= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDuDCCAqCgAwIBAgIQDPCOXAgWpa1Cf/DrJxhZ0DANBgkqhkiG9w0BAQUFADBI +MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24x +FzAVBgNVBAMTDlNlY3VyZVRydXN0IENBMB4XDTA2MTEwNzE5MzExOFoXDTI5MTIz +MTE5NDA1NVowSDELMAkGA1UEBhMCVVMxIDAeBgNVBAoTF1NlY3VyZVRydXN0IENv +cnBvcmF0aW9uMRcwFQYDVQQDEw5TZWN1cmVUcnVzdCBDQTCCASIwDQYJKoZIhvcN +AQEBBQADggEPADCCAQoCggEBAKukgeWVzfX2FI7CT8rU4niVWJxB4Q2ZQCQXOZEz +Zum+4YOvYlyJ0fwkW2Gz4BERQRwdbvC4u/jep4G6pkjGnx29vo6pQT64lO0pGtSO +0gMdA+9tDWccV9cGrcrI9f4Or2YlSASWC12juhbDCE/RRvgUXPLIXgGZbf2IzIao +wW8xQmxSPmjL8xk037uHGFaAJsTQ3MBv396gwpEWoGQRS0S8Hvbn+mPeZqx2pHGj +7DaUaHp3pLHnDi+BeuK1cobvomuL8A/b01k/unK8RCSc43Oz969XL0Imnal0ugBS +8kvNU3xHCzaFDmapCJcWNFfBZveA4+1wVMeT4C4oFVmHursCAwEAAaOBnTCBmjAT +BgkrBgEEAYI3FAIEBh4EAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB +/zAdBgNVHQ4EFgQUQjK2FvoE/f5dS3rD/fdMQB1aQ68wNAYDVR0fBC0wKzApoCeg +JYYjaHR0cDovL2NybC5zZWN1cmV0cnVzdC5jb20vU1RDQS5jcmwwEAYJKwYBBAGC +NxUBBAMCAQAwDQYJKoZIhvcNAQEFBQADggEBADDtT0rhWDpSclu1pqNlGKa7UTt3 +6Z3q059c4EVlew3KW+JwULKUBRSuSceNQQcSc5R+DCMh/bwQf2AQWnL1mA6s7Ll/ +3XpvXdMc9P+IBWlCqQVxyLesJugutIxq/3HcuLHfmbx8IVQr5Fiiu1cprp6poxkm +D5kuCLDv/WnPmRoJjeOnnyvJNjR7JLN4TJUXpAYmHrZkUjZfYGfZnMUFdAvnZyPS +CPyI6a6Lf+Ew9Dd+/cYy2i2eRDAwbO4H3tI0/NL/QPZL9GZGBlSm8jIKYyYwa5vR +3ItHuuG51WLQoqD0ZwV4KWMabwTW+MZMo5qxN7SN5ShLHZ4swrhovO0C7jE= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDvDCCAqSgAwIBAgIQB1YipOjUiolN9BPI8PjqpTANBgkqhkiG9w0BAQUFADBK +MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24x +GTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwgQ0EwHhcNMDYxMTA3MTk0MjI4WhcNMjkx +MjMxMTk1MjA2WjBKMQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3Qg +Q29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwgQ0EwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvNS7YrGxVaQZx5RNoJLNP2MwhR/jxYDiJ +iQPpvepeRlMJ3Fz1Wuj3RSoC6zFh1ykzTM7HfAo3fg+6MpjhHZevj8fcyTiW89sa +/FHtaMbQbqR8JNGuQsiWUGMu4P51/pinX0kuleM5M2SOHqRfkNJnPLLZ/kG5VacJ +jnIFHovdRIWCQtBJwB1g8NEXLJXr9qXBkqPFwqcIYA1gBBCWeZ4WNOaptvolRTnI +HmX5k/Wq8VLcmZg9pYYaDDUz+kulBAYVHDGA76oYa8J719rO+TMg1fW9ajMtgQT7 +sFzUnKPiXB3jqUJ1XnvUd+85VLrJChgbEplJL4hL/VBi0XPnj3pDAgMBAAGjgZ0w +gZowEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQF +MAMBAf8wHQYDVR0OBBYEFK9EBMJBfkiD2045AuzshHrmzsmkMDQGA1UdHwQtMCsw +KaAnoCWGI2h0dHA6Ly9jcmwuc2VjdXJldHJ1c3QuY29tL1NHQ0EuY3JsMBAGCSsG +AQQBgjcVAQQDAgEAMA0GCSqGSIb3DQEBBQUAA4IBAQBjGghAfaReUw132HquHw0L +URYD7xh8yOOvaliTFGCRsoTciE6+OYo68+aCiV0BN7OrJKQVDpI1WkpEXk5X+nXO +H0jOZvQ8QCaSmGwb7iRGDBezUqXbpZGRzzfTb+cnCDpOGR86p1hcF895P4vkp9Mm +I50mD1hp/Ed+stCNi5O/KU9DaXR2Z0vPB4zmAve14bRDtUstFJ/53CYNv6ZHdAbY +iNE6KTCEztI5gGIbqMdXSbxqVVFnFUq+NQfk1XWYN3kwFNspnWzFacxHVaIw98xc +f8LDmBxrThaA63p4ZUWiABqvDA1VZDRIuJK58bRQKfJPIx/abKwfROHdI3hRW8cW +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIBADANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJKUDEl +MCMGA1UEChMcU0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UECxMe +U2VjdXJpdHkgQ29tbXVuaWNhdGlvbiBSb290Q0EyMB4XDTA5MDUyOTA1MDAzOVoX +DTI5MDUyOTA1MDAzOVowXTELMAkGA1UEBhMCSlAxJTAjBgNVBAoTHFNFQ09NIFRy +dXN0IFN5c3RlbXMgQ08uLExURC4xJzAlBgNVBAsTHlNlY3VyaXR5IENvbW11bmlj +YXRpb24gUm9vdENBMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANAV +OVKxUrO6xVmCxF1SrjpDZYBLx/KWvNs2l9amZIyoXvDjChz335c9S672XewhtUGr +zbl+dp+++T42NKA7wfYxEUV0kz1XgMX5iZnK5atq1LXaQZAQwdbWQonCv/Q4EpVM +VAX3NuRFg3sUZdbcDE3R3n4MqzvEFb46VqZab3ZpUql6ucjrappdUtAtCms1FgkQ +hNBqyjoGADdH5H5XTz+L62e4iKrFvlNVspHEfbmwhRkGeC7bYRr6hfVKkaHnFtWO +ojnflLhwHyg/i/xAXmODPIMqGplrz95Zajv8bxbXH/1KEOtOghY6rCcMU/Gt1SSw +awNQwS08Ft1ENCcadfsCAwEAAaNCMEAwHQYDVR0OBBYEFAqFqXdlBZh8QIH4D5cs +OPEK7DzPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3 +DQEBCwUAA4IBAQBMOqNErLlFsceTfsgLCkLfZOoc7llsCLqJX2rKSpWeeo8HxdpF +coJxDjrSzG+ntKEju/Ykn8sX/oymzsLS28yN/HH8AynBbF0zX2S2ZTuJbxh2ePXc +okgfGT+Ok+vx+hfuzU7jBBJV1uXk3fs+BXziHV7Gp7yXT2g69ekuCkO2r1dcYmh8 +t/2jioSgrGK+KwmHNPBqAbubKVY8/gA3zyNs8U6qtnRGEmyR7jTV7JqR50S+kDFy +1UkC9gLl9B/rfNmWVan/7Ir5mUf/NVoCqgTLiluHcSmRvaS0eg29mvVXIwAHIRc/ +SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03 +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDWjCCAkKgAwIBAgIBADANBgkqhkiG9w0BAQUFADBQMQswCQYDVQQGEwJKUDEY +MBYGA1UEChMPU0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21t +dW5pY2F0aW9uIFJvb3RDQTEwHhcNMDMwOTMwMDQyMDQ5WhcNMjMwOTMwMDQyMDQ5 +WjBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMPU0VDT00gVHJ1c3QubmV0MScwJQYD +VQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQCzs/5/022x7xZ8V6UMbXaKL0u/ZPtM7orw8yl8 +9f/uKuDp6bpbZCKamm8sOiZpUQWZJtzVHGpxxpp9Hp3dfGzGjGdnSj74cbAZJ6kJ +DKaVv0uMDPpVmDvY6CKhS3E4eayXkmmziX7qIWgGmBSWh9JhNrxtJ1aeV+7AwFb9 +Ms+k2Y7CI9eNqPPYJayX5HA49LY6tJ07lyZDo6G8SVlyTCMwhwFY9k6+HGhWZq/N +QV3Is00qVUarH9oe4kA92819uZKAnDfdDJZkndwi92SL32HeFZRSFaB9UslLqCHJ +xrHty8OVYNEP8Ktw+N/LTX7s1vqr2b1/VPKl6Xn62dZ2JChzAgMBAAGjPzA9MB0G +A1UdDgQWBBSgc0mZaNyFW2XjmygvV5+9M7wHSDALBgNVHQ8EBAMCAQYwDwYDVR0T +AQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAaECpqLvkT115swW1F7NgE+vG +kl3g0dNq/vu+m22/xwVtWSDEHPC32oRYAmP6SBbvT6UL90qY8j+eG61Ha2POCEfr +Uj94nK9NrvjVT8+amCoQQTlSxN3Zmw7vkwGusi7KaEIkQmywszo+zenaSMQVy+n5 +Bw+SUEmK3TGXX8npN6o7WWWXlDLJs58+OmJYxUmtYg5xpTKqL8aJdkNAExNnPaJU +JRDL8Try2frbSVa7pv6nQTXD4IhhyYjH3zYQIphZ6rBK+1YWc26sTfcioU+tHXot +RSflMMFe8toTyyVCUZVHA4xsIcx0Qu1T/zOLjw9XARYvz6buyXAiFL39vmwLAw== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDIDCCAgigAwIBAgIBHTANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJGSTEP +MA0GA1UEChMGU29uZXJhMRkwFwYDVQQDExBTb25lcmEgQ2xhc3MyIENBMB4XDTAx +MDQwNjA3Mjk0MFoXDTIxMDQwNjA3Mjk0MFowOTELMAkGA1UEBhMCRkkxDzANBgNV +BAoTBlNvbmVyYTEZMBcGA1UEAxMQU29uZXJhIENsYXNzMiBDQTCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAJAXSjWdyvANlsdE+hY3/Ei9vX+ALTU74W+o +Z6m/AxxNjG8yR9VBaKQTBME1DJqEQ/xcHf+Js+gXGM2RX/uJ4+q/Tl18GybTdXnt +5oTjV+WtKcT0OijnpXuENmmz/V52vaMtmdOQTiMofRhj8VQ7Jp12W5dCsv+u8E7s +3TmVToMGf+dJQMjFAbJUWmYdPfz56TwKnoG4cPABi+QjVHzIrviQHgCWctRUz2Ej +vOr7nQKV0ba5cTppCD8PtOFCx4j1P5iop7oc4HFx71hXgVB6XGt0Rg6DA5jDjqhu +8nYybieDwnPz3BjotJPqdURrBGAgcVeHnfO+oJAjPYok4doh28MCAwEAAaMzMDEw +DwYDVR0TAQH/BAUwAwEB/zARBgNVHQ4ECgQISqCqWITTXjwwCwYDVR0PBAQDAgEG +MA0GCSqGSIb3DQEBBQUAA4IBAQBazof5FnIVV0sd2ZvnoiYw7JNn39Yt0jSv9zil +zqsWuasvfDXLrNAPtEwr/IDva4yRXzZ299uzGxnq9LIR/WFxRL8oszodv7ND6J+/ +3DEIcbCdjdY0RzKQxmUk96BKfARzjzlvF4xytb1LyHr4e4PDKE6cCepnP7JnBBvD +FNr450kkkdAdavphOe9r5yF1BgfYErQhIHBCcYHaPJo2vqZbDWpsmh+Re/n570K6 +Tk6ezAyNlNzZRZxe7EJQY670XcSxEtzKO6gunRRaBXW37Ndj4ro1tgQIkejanZz2 +ZrUYrAqmVCY0M9IbwdR/GjqOC6oybtv8TyWf2TLHllpwrN9M +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFcDCCA1igAwIBAgIEAJiWjTANBgkqhkiG9w0BAQsFADBYMQswCQYDVQQGEwJO +TDEeMBwGA1UECgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSkwJwYDVQQDDCBTdGFh +dCBkZXIgTmVkZXJsYW5kZW4gRVYgUm9vdCBDQTAeFw0xMDEyMDgxMTE5MjlaFw0y +MjEyMDgxMTEwMjhaMFgxCzAJBgNVBAYTAk5MMR4wHAYDVQQKDBVTdGFhdCBkZXIg +TmVkZXJsYW5kZW4xKTAnBgNVBAMMIFN0YWF0IGRlciBOZWRlcmxhbmRlbiBFViBS +b290IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA48d+ifkkSzrS +M4M1LGns3Amk41GoJSt5uAg94JG6hIXGhaTK5skuU6TJJB79VWZxXSzFYGgEt9nC +UiY4iKTWO0Cmws0/zZiTs1QUWJZV1VD+hq2kY39ch/aO5ieSZxeSAgMs3NZmdO3d +Z//BYY1jTw+bbRcwJu+r0h8QoPnFfxZpgQNH7R5ojXKhTbImxrpsX23Wr9GxE46p +rfNeaXUmGD5BKyF/7otdBwadQ8QpCiv8Kj6GyzyDOvnJDdrFmeK8eEEzduG/L13l +pJhQDBXd4Pqcfzho0LKmeqfRMb1+ilgnQ7O6M5HTp5gVXJrm0w912fxBmJc+qiXb +j5IusHsMX/FjqTf5m3VpTCgmJdrV8hJwRVXj33NeN/UhbJCONVrJ0yPr08C+eKxC +KFhmpUZtcALXEPlLVPxdhkqHz3/KRawRWrUgUY0viEeXOcDPusBCAUCZSCELa6fS +/ZbV0b5GnUngC6agIk440ME8MLxwjyx1zNDFjFE7PZQIZCZhfbnDZY8UnCHQqv0X +cgOPvZuM5l5Tnrmd74K74bzickFbIZTTRTeU0d8JOV3nI6qaHcptqAqGhYqCvkIH +1vI4gnPah1vlPNOePqc7nvQDs/nxfRN0Av+7oeX6AHkcpmZBiFxgV6YuCcS6/ZrP +px9Aw7vMWgpVSzs4dlG4Y4uElBbmVvMCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB +/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFP6rAJCYniT8qcwaivsnuL8wbqg7 +MA0GCSqGSIb3DQEBCwUAA4ICAQDPdyxuVr5Os7aEAJSrR8kN0nbHhp8dB9O2tLsI +eK9p0gtJ3jPFrK3CiAJ9Brc1AsFgyb/E6JTe1NOpEyVa/m6irn0F3H3zbPB+po3u +2dfOWBfoqSmuc0iH55vKbimhZF8ZE/euBhD/UcabTVUlT5OZEAFTdfETzsemQUHS +v4ilf0X8rLiltTMMgsT7B/Zq5SWEXwbKwYY5EdtYzXc7LMJMD16a4/CrPmEbUCTC +wPTxGfARKbalGAKb12NMcIxHowNDXLldRqANb/9Zjr7dn3LDWyvfjFvO5QxGbJKy +CqNMVEIYFRIYvdr8unRu/8G2oGTYqV9Vrp9canaW2HNnh/tNf1zuacpzEPuKqf2e +vTY4SUmH9A4U8OmHuD+nT3pajnnUk+S7aFKErGzp85hwVXIy+TSrK0m1zSBi5Dp6 +Z2Orltxtrpfs/J92VoguZs9btsmksNcFuuEnL5O7Jiqik7Ab846+HUCjuTaPPoIa +Gl6I6lD4WeKDRikL40Rc4ZW2aZCaFG+XroHPaO+Zmr615+F/+PoTRxZMzG0IQOeL +eG9QgkRQP2YGiqtDhFZKDyAthg710tvSeopLzaXoTvFeJiUBWSOgftL2fiFX1ye8 +FVdMpEbB4IMeDExNH08GGeL5qPQ6gqGyeUN51q1veieQA6TqJIc/2b3Z6fJfUEkc +7uzXLg== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFyjCCA7KgAwIBAgIEAJiWjDANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJO +TDEeMBwGA1UECgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSswKQYDVQQDDCJTdGFh +dCBkZXIgTmVkZXJsYW5kZW4gUm9vdCBDQSAtIEcyMB4XDTA4MDMyNjExMTgxN1oX +DTIwMDMyNTExMDMxMFowWjELMAkGA1UEBhMCTkwxHjAcBgNVBAoMFVN0YWF0IGRl +ciBOZWRlcmxhbmRlbjErMCkGA1UEAwwiU3RhYXQgZGVyIE5lZGVybGFuZGVuIFJv +b3QgQ0EgLSBHMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMVZ5291 +qj5LnLW4rJ4L5PnZyqtdj7U5EILXr1HgO+EASGrP2uEGQxGZqhQlEq0i6ABtQ8Sp +uOUfiUtnvWFI7/3S4GCI5bkYYCjDdyutsDeqN95kWSpGV+RLufg3fNU254DBtvPU +Z5uW6M7XxgpT0GtJlvOjCwV3SPcl5XCsMBQgJeN/dVrlSPhOewMHBPqCYYdu8DvE +pMfQ9XQ+pV0aCPKbJdL2rAQmPlU6Yiile7Iwr/g3wtG61jj99O9JMDeZJiFIhQGp +5Rbn3JBV3w/oOM2ZNyFPXfUib2rFEhZgF1XyZWampzCROME4HYYEhLoaJXhena/M +UGDWE4dS7WMfbWV9whUYdMrhfmQpjHLYFhN9C0lK8SgbIHRrxT3dsKpICT0ugpTN +GmXZK4iambwYfp/ufWZ8Pr2UuIHOzZgweMFvZ9C+X+Bo7d7iscksWXiSqt8rYGPy +5V6548r6f1CGPqI0GAwJaCgRHOThuVw+R7oyPxjMW4T182t0xHJ04eOLoEq9jWYv +6q012iDTiIJh8BIitrzQ1aTsr1SIJSQ8p22xcik/Plemf1WvbibG/ufMQFxRRIEK +eN5KzlW/HdXZt1bv8Hb/C3m1r737qWmRRpdogBQ2HbN/uymYNqUg+oJgYjOk7Na6 +B6duxc8UpufWkjTYgfX8HV2qXB72o007uPc5AgMBAAGjgZcwgZQwDwYDVR0TAQH/ +BAUwAwEB/zBSBgNVHSAESzBJMEcGBFUdIAAwPzA9BggrBgEFBQcCARYxaHR0cDov +L3d3dy5wa2lvdmVyaGVpZC5ubC9wb2xpY2llcy9yb290LXBvbGljeS1HMjAOBgNV +HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJFoMocVHYnitfGsNig0jQt8YojrMA0GCSqG +SIb3DQEBCwUAA4ICAQCoQUpnKpKBglBu4dfYszk78wIVCVBR7y29JHuIhjv5tLyS +CZa59sCrI2AGeYwRTlHSeYAz+51IvuxBQ4EffkdAHOV6CMqqi3WtFMTC6GY8ggen +5ieCWxjmD27ZUD6KQhgpxrRW/FYQoAUXvQwjf/ST7ZwaUb7dRUG/kSS0H4zpX897 +IZmflZ85OkYcbPnNe5yQzSipx6lVu6xiNGI1E0sUOlWDuYaNkqbG9AclVMwWVxJK +gnjIFNkXgiYtXSAfea7+1HAWFpWD2DU5/1JddRwWxRNVz0fMdWVSSt7wsKfkCpYL ++63C4iWEst3kvX5ZbJvw8NjnyvLplzh+ib7M+zkXYT9y2zqR2GUBGR2tUKRXCnxL +vJxxcypFURmFzI79R6d0lR2o0a9OF7FpJsKqeFdbxU2n5Z4FF5TKsl+gSRiNNOkm +bEgeqmiSBeGCc1qb3AdbCG19ndeNIdn8FCCqwkXfP+cAslHkwvgFuXkajDTznlvk +N1trSt8sV4pAWja63XVECDdCcAz+3F4hoKOKwJCcaNpQ5kUQR3i2TtJlycM33+FC +Y7BXN0Ute4qcvwXqZVUz9zkQxSgqIXobisQk+T8VyJoVIPVVYpbtbZNQvOSqeK3Z +ywplh6ZmwcSBo3c6WB4L7oOLnR7SUqTMHW+wmG2UMbX4cQrcufx9MmDm66+KAQ== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFdDCCA1ygAwIBAgIEAJiiOTANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJO +TDEeMBwGA1UECgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSswKQYDVQQDDCJTdGFh +dCBkZXIgTmVkZXJsYW5kZW4gUm9vdCBDQSAtIEczMB4XDTEzMTExNDExMjg0MloX +DTI4MTExMzIzMDAwMFowWjELMAkGA1UEBhMCTkwxHjAcBgNVBAoMFVN0YWF0IGRl +ciBOZWRlcmxhbmRlbjErMCkGA1UEAwwiU3RhYXQgZGVyIE5lZGVybGFuZGVuIFJv +b3QgQ0EgLSBHMzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL4yolQP +cPssXFnrbMSkUeiFKrPMSjTysF/zDsccPVMeiAho2G89rcKezIJnByeHaHE6n3WW +IkYFsO2tx1ueKt6c/DrGlaf1F2cY5y9JCAxcz+bMNO14+1Cx3Gsy8KL+tjzk7FqX +xz8ecAgwoNzFs21v0IJyEavSgWhZghe3eJJg+szeP4TrjTgzkApyI/o1zCZxMdFy +KJLZWyNtZrVtB0LrpjPOktvA9mxjeM3KTj215VKb8b475lRgsGYeCasH/lSJEULR +9yS6YHgamPfJEf0WwTUaVHXvQ9Plrk7O53vDxk5hUUurmkVLoR9BvUhTFXFkC4az +5S6+zqQbwSmEorXLCCN2QyIkHxcE1G6cxvx/K2Ya7Irl1s9N9WMJtxU51nus6+N8 +6U78dULI7ViVDAZCopz35HCz33JvWjdAidiFpNfxC95DGdRKWCyMijmev4SH8RY7 +Ngzp07TKbBlBUgmhHbBqv4LvcFEhMtwFdozL92TkA1CvjJFnq8Xy7ljY3r735zHP +bMk7ccHViLVlvMDoFxcHErVc0qsgk7TmgoNwNsXNo42ti+yjwUOH5kPiNL6VizXt +BznaqB16nzaeErAMZRKQFWDZJkBE41ZgpRDUajz9QdwOWke275dhdU/Z/seyHdTt +XUmzqWrLZoQT1Vyg3N9udwbRcXXIV2+vD3dbAgMBAAGjQjBAMA8GA1UdEwEB/wQF +MAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRUrfrHkleuyjWcLhL75Lpd +INyUVzANBgkqhkiG9w0BAQsFAAOCAgEAMJmdBTLIXg47mAE6iqTnB/d6+Oea31BD +U5cqPco8R5gu4RV78ZLzYdqQJRZlwJ9UXQ4DO1t3ApyEtg2YXzTdO2PCwyiBwpwp +LiniyMMB8jPqKqrMCQj3ZWfGzd/TtiunvczRDnBfuCPRy5FOCvTIeuXZYzbB1N/8 +Ipf3YF3qKS9Ysr1YvY2WTxB1v0h7PVGHoTx0IsL8B3+A3MSs/mrBcDCw6Y5p4ixp +gZQJut3+TcCDjJRYwEYgr5wfAvg1VUkvRtTA8KCWAg8zxXHzniN9lLf9OtMJgwYh +/WA9rjLA0u6NpvDntIJ8CsxwyXmA+P5M9zWEGYox+wrZ13+b8KKaa8MFSu1BYBQw +0aoRQm7TIwIEC8Zl3d1Sd9qBa7Ko+gE4uZbqKmxnl4mUnrzhVNXkanjvSr0rmj1A +fsbAddJu+2gw7OyLnflJNZoaLNmzlTnVHpL3prllL+U9bTpITAjc5CgSKL59NVzq +4BZ+Extq1z7XnvwtdbLBFNUjA9tbbws+eC8N3jONFrdI54OagQ97wUNNVQQXOEpR +1VmiiXTTn74eS9fGbbeIJG9gkaSChVtWQbzQRKtqE77RLFi3EjNYsjdj3BP1lB0/ +QFH1T/U67cjF68IeHRaVesd+QnGTbksVtzDfqu1XhUisHWrdOWnk4Xl4vs4Fv6EM +94B7IWcnMFk= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzEl +MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMp +U3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQw +NjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBoMQswCQYDVQQGEwJVUzElMCMGA1UE +ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZp +ZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqGSIb3 +DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf +8MOh2tTYbitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN ++lq2cwQlZut3f+dZxkqZJRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0 +X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVmepsZGD3/cVE8MC5fvj13c7JdBmzDI1aa +K4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSNF4Azbl5KXZnJHoe0nRrA +1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HFMIHCMB0G +A1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fR +zt0fhvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0 +YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBD +bGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8w +DQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGsafPzWdqbAYcaT1epoXkJKtv3 +L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLMPUxA2IGvd56D +eruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl +xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynp +VSJYACPq4xJDKVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEY +WQPJIrSPnNVeKtelttQKbfi3QBFGmh95DmK/D5fs4C8fF5Q= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT +HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVs +ZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAw +MFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6 +b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQgVGVj +aG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZp +Y2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAL3twQP89o/8ArFvW59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMg +nLRJdzIpVv257IzdIvpy3Cdhl+72WoTsbhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1 +HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNkN3mSwOxGXn/hbVNMYq/N +Hwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7NfZTD4p7dN +dloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0 +HZbUJtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO +BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0G +CSqGSIb3DQEBCwUAA4IBAQARWfolTwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjU +sHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx4mcujJUDJi5DnUox9g61DLu3 +4jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUwF5okxBDgBPfg +8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K +pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1 +mMpYjn0q7pBZc2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0 +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT +HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVs +ZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5 +MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgZgxCzAJBgNVBAYTAlVTMRAwDgYD +VQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFy +ZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJTdGFyZmllbGQgU2Vy +dmljZXMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20p +OsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm2 +8xpWriu2dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4PahHQUw2eeBGg6345AWh1K +Ts9DkTvnVtYAcMtS7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLPLJGmpufe +hRhJfGZOozptqbXuNC66DQO4M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk +6mFBrMnUVN+HL8cisibMn1lUaJ/8viovxFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAw +DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJxfAN+q +AdcwKziIorhtSpzyEZGDMA0GCSqGSIb3DQEBCwUAA4IBAQBLNqaEd2ndOxmfZyMI +bw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPPE95Dz+I0swSdHynVv/heyNXB +ve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTyxQGjhdByPq1z +qwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd +iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn +0q23KXB56jzaYyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCN +sSi6 +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFujCCA6KgAwIBAgIJALtAHEP1Xk+wMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV +BAYTAkNIMRUwEwYDVQQKEwxTd2lzc1NpZ24gQUcxHzAdBgNVBAMTFlN3aXNzU2ln +biBHb2xkIENBIC0gRzIwHhcNMDYxMDI1MDgzMDM1WhcNMzYxMDI1MDgzMDM1WjBF +MQswCQYDVQQGEwJDSDEVMBMGA1UEChMMU3dpc3NTaWduIEFHMR8wHQYDVQQDExZT +d2lzc1NpZ24gR29sZCBDQSAtIEcyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC +CgKCAgEAr+TufoskDhJuqVAtFkQ7kpJcyrhdhJJCEyq8ZVeCQD5XJM1QiyUqt2/8 +76LQwB8CJEoTlo8jE+YoWACjR8cGp4QjK7u9lit/VcyLwVcfDmJlD909Vopz2q5+ +bbqBHH5CjCA12UNNhPqE21Is8w4ndwtrvxEvcnifLtg+5hg3Wipy+dpikJKVyh+c +6bM8K8vzARO/Ws/BtQpgvd21mWRTuKCWs2/iJneRjOBiEAKfNA+k1ZIzUd6+jbqE +emA8atufK+ze3gE/bk3lUIbLtK/tREDFylqM2tIrfKjuvqblCqoOpd8FUrdVxyJd +MmqXl2MT28nbeTZ7hTpKxVKJ+STnnXepgv9VHKVxaSvRAiTysybUa9oEVeXBCsdt +MDeQKuSeFDNeFhdVxVu1yzSJkvGdJo+hB9TGsnhQ2wwMC3wLjEHXuendjIj3o02y +MszYF9rNt85mndT9Xv+9lz4pded+p2JYryU0pUHHPbwNUMoDAw8IWh+Vc3hiv69y +FGkOpeUDDniOJihC8AcLYiAQZzlG+qkDzAQ4embvIIO1jEpWjpEA/I5cgt6IoMPi +aG59je883WX0XaxR7ySArqpWl2/5rX3aYT+YdzylkbYcjCbaZaIJbcHiVOO5ykxM +gI93e2CaHt+28kgeDrpOVG2Y4OGiGqJ3UM/EY5LsRxmd6+ZrzsECAwEAAaOBrDCB +qTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUWyV7 +lqRlUX64OfPAeGZe6Drn8O4wHwYDVR0jBBgwFoAUWyV7lqRlUX64OfPAeGZe6Drn +8O4wRgYDVR0gBD8wPTA7BglghXQBWQECAQEwLjAsBggrBgEFBQcCARYgaHR0cDov +L3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBACe6 +45R88a7A3hfm5djV9VSwg/S7zV4Fe0+fdWavPOhWfvxyeDgD2StiGwC5+OlgzczO +UYrHUDFu4Up+GC9pWbY9ZIEr44OE5iKHjn3g7gKZYbge9LgriBIWhMIxkziWMaa5 +O1M/wySTVltpkuzFwbs4AOPsF6m43Md8AYOfMke6UiI0HTJ6CVanfCU2qT1L2sCC +bwq7EsiHSycR+R4tx5M/nttfJmtS2S6K8RTGRI0Vqbe/vd6mGu6uLftIdxf+u+yv +GPUqUfA5hJeVbG4bwyvEdGB5JbAKJ9/fXtI5z0V9QkvfsywexcZdylU6oJxpmo/a +77KwPJ+HbBIrZXAVUjEaJM9vMSNQH4xPjyPDdEFjHFWoFN0+4FFQz/EbMFYOkrCC +hdiDyyJkvC24JdVUorgG6q2SpCSgwYa1ShNqR88uC1aVVMvOmttqtKay20EIhid3 +92qgQmwLOM7XdVAyksLfKzAiSNDVQTglXaTpXZ/GlHXQRf0wl0OPkKsKx4ZzYEpp +Ld6leNcG2mqeSz53OiATIgHQv2ieY2BrNU0LbbqhPcCT4H8js1WtciVORvnSFu+w +ZMEBnunKoGqYDs/YYPIvSbjkQuE4NRb0yG5P94FW6LqjviOvrv1vA+ACOzB2+htt +Qc8Bsem4yWb02ybzOqR08kkkW8mw0FfB+j564ZfJ +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFvTCCA6WgAwIBAgIITxvUL1S7L0swDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UE +BhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMYU3dpc3NTaWdu +IFNpbHZlciBDQSAtIEcyMB4XDTA2MTAyNTA4MzI0NloXDTM2MTAyNTA4MzI0Nlow +RzELMAkGA1UEBhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMY +U3dpc3NTaWduIFNpbHZlciBDQSAtIEcyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A +MIICCgKCAgEAxPGHf9N4Mfc4yfjDmUO8x/e8N+dOcbpLj6VzHVxumK4DV644N0Mv +Fz0fyM5oEMF4rhkDKxD6LHmD9ui5aLlV8gREpzn5/ASLHvGiTSf5YXu6t+WiE7br +YT7QbNHm+/pe7R20nqA1W6GSy/BJkv6FCgU+5tkL4k+73JU3/JHpMjUi0R86TieF +nbAVlDLaYQ1HTWBCrpJH6INaUFjpiou5XaHc3ZlKHzZnu0jkg7Y360g6rw9njxcH +6ATK72oxh9TAtvmUcXtnZLi2kUpCe2UuMGoM9ZDulebyzYLs2aFK7PayS+VFheZt +eJMELpyCbTapxDFkH4aDCyr0NQp4yVXPQbBH6TCfmb5hqAaEuSh6XzjZG6k4sIN/ +c8HDO0gqgg8hm7jMqDXDhBuDsz6+pJVpATqJAHgE2cn0mRmrVn5bi4Y5FZGkECwJ +MoBgs5PAKrYYC51+jUnyEEp/+dVGLxmSo5mnJqy7jDzmDrxHB9xzUfFwZC8I+bRH +HTBsROopN4WSaGa8gzj+ezku01DwH/teYLappvonQfGbGHLy9YR0SslnxFSuSGTf +jNFusB3hB48IHpmccelM2KX3RxIfdNFRnobzwqIjQAtz20um53MGjMGg6cFZrEb6 +5i/4z3GcRm25xBWNOHkDRUjvxF3XCO6HOSKGsg0PWEP3calILv3q1h8CAwEAAaOB +rDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU +F6DNweRBtjpbO8tFnb0cwpj6hlgwHwYDVR0jBBgwFoAUF6DNweRBtjpbO8tFnb0c +wpj6hlgwRgYDVR0gBD8wPTA7BglghXQBWQEDAQEwLjAsBggrBgEFBQcCARYgaHR0 +cDovL3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIB +AHPGgeAn0i0P4JUw4ppBf1AsX19iYamGamkYDHRJ1l2E6kFSGG9YrVBWIGrGvShp +WJHckRE1qTodvBqlYJ7YH39FkWnZfrt4csEGDyrOj4VwYaygzQu4OSlWhDJOhrs9 +xCrZ1x9y7v5RoSJBsXECYxqCsGKrXlcSH9/L3XWgwF15kIwb4FDm3jH+mHtwX6WQ +2K34ArZv02DdQEsixT2tOnqfGhpHkXkzuoLcMmkDlm4fS/Bx/uNncqCxv1yL5PqZ +IseEuRuNI5c/7SXgz2W79WEE790eslpBIlqhn10s6FvJbakMDHiqYMZWjwFaDGi8 +aRl5xB9+lwW/xekkUV7U1UtT7dkjWjYDZaPBA61BMPNGG4WQr2W11bHkFlt4dR2X +em1ZqSqPe97Dh4kQmUlzeMg9vVE1dCrV8X5pGyq7O70luJpaPXJhkGaH7gzWTdQR +dAtq/gsD/KNVV4n+SsuuWxcFyPKNIzFTONItaj+CuY0IavdeQXRuwxF+B6wpYJE/ +OMpXEA29MC/HpeZBoNquBYeaoKRlbEwJDIm6uNO5wJOKMPqN5ZprFQFOZ6raYlY+ +hAhm0sQ2fac+EPyI4NSA5QC9qvNOBqN6avlicuMJT+ubDgEj8Z+7fNzcbBGXJbLy +tGMU0gYqZ4yD9c7qB9iaah7s5Aq7KkzrCWA5zspi2C5u +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUx +KzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAd +BgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNl +YyBHbG9iYWxSb290IENsYXNzIDIwHhcNMDgxMDAxMTA0MDE0WhcNMzMxMDAxMjM1 +OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnBy +aXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50 +ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCqX9obX+hzkeXaXPSi5kfl82hVYAUd +AqSzm1nzHoqvNK38DcLZSBnuaY/JIPwhqgcZ7bBcrGXHX+0CfHt8LRvWurmAwhiC +FoT6ZrAIxlQjgeTNuUk/9k9uN0goOA/FvudocP05l03Sx5iRUKrERLMjfTlH6VJi +1hKTXrcxlkIF+3anHqP1wvzpesVsqXFP6st4vGCvx9702cu+fjOlbpSD8DT6Iavq +jnKgP6TeMFvvhk1qlVtDRKgQFRzlAVfFmPHmBiiRqiDFt1MmUUOyCxGVWOHAD3bZ +wI18gfNycJ5v/hqO2V81xrJvNHy+SE/iWjnX2J14np+GPgNeGYtEotXHAgMBAAGj +QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS/ +WSA2AHmgoCJrjNXyYdK4LMuCSjANBgkqhkiG9w0BAQsFAAOCAQEAMQOiYQsfdOhy +NsZt+U2e+iKo4YFWz827n+qrkRk4r6p8FU3ztqONpfSO9kSpp+ghla0+AGIWiPAC +uvxhI+YzmzB6azZie60EI4RYZeLbK4rnJVM3YlNfvNoBYimipidx5joifsFvHZVw +IEoHNN/q/xWA5brXethbdXwFeilHfkCoMRN3zUA7tFFHei4R40cR3p1m0IvVVGb6 +g1XqfMIpiRvpb7PO4gWEyS8+eIVibslfwXhjdFjASBgMmTnrpMwatXlajRWc2BQN +9noHV8cigwUtPJslJj0Ys6lDfMjIq2SPDqO/nBudMNva0Bkuqjzx+zOAduTNrRlP +BSeOE6Fuwg== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUx +KzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAd +BgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNl +YyBHbG9iYWxSb290IENsYXNzIDMwHhcNMDgxMDAxMTAyOTU2WhcNMzMxMDAxMjM1 +OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnBy +aXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50 +ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC9dZPwYiJvJK7genasfb3ZJNW4t/zN +8ELg63iIVl6bmlQdTQyK9tPPcPRStdiTBONGhnFBSivwKixVA9ZIw+A5OO3yXDw/ +RLyTPWGrTs0NvvAgJ1gORH8EGoel15YUNpDQSXuhdfsaa3Ox+M6pCSzyU9XDFES4 +hqX2iys52qMzVNn6chr3IhUciJFrf2blw2qAsCTz34ZFiP0Zf3WHHx+xGwpzJFu5 +ZeAsVMhg02YXP+HMVDNzkQI6pn97djmiH5a2OK61yJN0HZ65tOVgnS9W0eDrXltM +EnAMbEQgqxHY9Bn20pxSN+f6tsIxO0rUFJmtxxr1XV/6B7h8DR/Wgx6zAgMBAAGj +QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS1 +A/d2O2GCahKqGFPrAyGUv/7OyjANBgkqhkiG9w0BAQsFAAOCAQEAVj3vlNW92nOy +WL6ukK2YJ5f+AbGwUgC4TeQbIXQbfsDuXmkqJa9c1h3a0nnJ85cp4IaH3gRZD/FZ +1GSFS5mvJQQeyUapl96Cshtwn5z2r3Ex3XsFpSzTucpH9sry9uetuUg/vBa3wW30 +6gmv7PO15wWeph6KU1HWk4HMdJP2udqmJQV0eVp+QD6CSyYRMG7hP0HHRwA11fXT +91Q+gT3aSWqas+8QPebrb9HIIkfLzM8BMZLZGOMivgkeGj5asuRrDFR6fUNOuIml +e9eiPZaGzPImNC1qkp2aGtAw4l1OBLBfiyB+d8E9lYLRRpo7PHi4b6HQDWSieB4p +TpPDpFQUWw== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEYzCCA0ugAwIBAgIBATANBgkqhkiG9w0BAQsFADCB0jELMAkGA1UEBhMCVFIx +GDAWBgNVBAcTD0dlYnplIC0gS29jYWVsaTFCMEAGA1UEChM5VHVya2l5ZSBCaWxp +bXNlbCB2ZSBUZWtub2xvamlrIEFyYXN0aXJtYSBLdXJ1bXUgLSBUVUJJVEFLMS0w +KwYDVQQLEyRLYW11IFNlcnRpZmlrYXN5b24gTWVya2V6aSAtIEthbXUgU00xNjA0 +BgNVBAMTLVRVQklUQUsgS2FtdSBTTSBTU0wgS29rIFNlcnRpZmlrYXNpIC0gU3Vy +dW0gMTAeFw0xMzExMjUwODI1NTVaFw00MzEwMjUwODI1NTVaMIHSMQswCQYDVQQG +EwJUUjEYMBYGA1UEBxMPR2ViemUgLSBLb2NhZWxpMUIwQAYDVQQKEzlUdXJraXll +IEJpbGltc2VsIHZlIFRla25vbG9qaWsgQXJhc3Rpcm1hIEt1cnVtdSAtIFRVQklU +QUsxLTArBgNVBAsTJEthbXUgU2VydGlmaWthc3lvbiBNZXJrZXppIC0gS2FtdSBT +TTE2MDQGA1UEAxMtVFVCSVRBSyBLYW11IFNNIFNTTCBLb2sgU2VydGlmaWthc2kg +LSBTdXJ1bSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr3UwM6q7 +a9OZLBI3hNmNe5eA027n/5tQlT6QlVZC1xl8JoSNkvoBHToP4mQ4t4y86Ij5iySr +LqP1N+RAjhgleYN1Hzv/bKjFxlb4tO2KRKOrbEz8HdDc72i9z+SqzvBV96I01INr +N3wcwv61A+xXzry0tcXtAA9TNypN9E8Mg/uGz8v+jE69h/mniyFXnHrfA2eJLJ2X +YacQuFWQfw4tJzh03+f92k4S400VIgLI4OD8D62K18lUUMw7D8oWgITQUVbDjlZ/ +iSIzL+aFCr2lqBs23tPcLG07xxO9WSMs5uWk99gL7eqQQESolbuT1dCANLZGeA4f +AJNG4e7p+exPFwIDAQABo0IwQDAdBgNVHQ4EFgQUZT/HiobGPN08VFw1+DrtUgxH +V8gwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL +BQADggEBACo/4fEyjq7hmFxLXs9rHmoJ0iKpEsdeV31zVmSAhHqT5Am5EM2fKifh +AHe+SMg1qIGf5LgsyX8OsNJLN13qudULXjS99HMpw+0mFZx+CFOKWI3QSyjfwbPf +IPP54+M638yclNhOT8NrF7f3cuitZjO1JVOr4PhMqZ398g26rrnZqsZr+ZO7rqu4 +lzwDGrpDxpa5RXI4s6ehlj2Re37AIVNMh+3yC1SVUZPVIqUNivGTDj5UDrDYyU7c +8jEyVupk+eq1nRZmQnLzf9OxMUP8pI4X8W0jq5Rm+K37DwhuJi1/FwcJsoz7UMCf +lo3Ptv0AnVoUmr8CRPXBwp8iXqIPoeM= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFQTCCAymgAwIBAgICDL4wDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCVFcx +EjAQBgNVBAoTCVRBSVdBTi1DQTEQMA4GA1UECxMHUm9vdCBDQTEcMBoGA1UEAxMT +VFdDQSBHbG9iYWwgUm9vdCBDQTAeFw0xMjA2MjcwNjI4MzNaFw0zMDEyMzExNTU5 +NTlaMFExCzAJBgNVBAYTAlRXMRIwEAYDVQQKEwlUQUlXQU4tQ0ExEDAOBgNVBAsT +B1Jvb3QgQ0ExHDAaBgNVBAMTE1RXQ0EgR2xvYmFsIFJvb3QgQ0EwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCwBdvI64zEbooh745NnHEKH1Jw7W2CnJfF +10xORUnLQEK1EjRsGcJ0pDFfhQKX7EMzClPSnIyOt7h52yvVavKOZsTuKwEHktSz +0ALfUPZVr2YOy+BHYC8rMjk1Ujoog/h7FsYYuGLWRyWRzvAZEk2tY/XTP3VfKfCh +MBwqoJimFb3u/Rk28OKRQ4/6ytYQJ0lM793B8YVwm8rqqFpD/G2Gb3PpN0Wp8DbH +zIh1HrtsBv+baz4X7GGqcXzGHaL3SekVtTzWoWH1EfcFbx39Eb7QMAfCKbAJTibc +46KokWofwpFFiFzlmLhxpRUZyXx1EcxwdE8tmx2RRP1WKKD+u4ZqyPpcC1jcxkt2 +yKsi2XMPpfRaAok/T54igu6idFMqPVMnaR1sjjIsZAAmY2E2TqNGtz99sy2sbZCi +laLOz9qC5wc0GZbpuCGqKX6mOL6OKUohZnkfs8O1CWfe1tQHRvMq2uYiN2DLgbYP +oA/pyJV/v1WRBXrPPRXAb94JlAGD1zQbzECl8LibZ9WYkTunhHiVJqRaCPgrdLQA +BDzfuBSO6N+pjWxnkjMdwLfS7JLIvgm/LCkFbwJrnu+8vyq8W8BQj0FwcYeyTbcE +qYSjMq+u7msXi7Kx/mzhkIyIqJdIzshNy/MGz19qCkKxHh53L46g5pIOBvwFItIm +4TFRfTLcDwIDAQABoyMwITAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB +/zANBgkqhkiG9w0BAQsFAAOCAgEAXzSBdu+WHdXltdkCY4QWwa6gcFGn90xHNcgL +1yg9iXHZqjNB6hQbbCEAwGxCGX6faVsgQt+i0trEfJdLjbDorMjupWkEmQqSpqsn +LhpNgb+E1HAerUf+/UqdM+DyucRFCCEK2mlpc3INvjT+lIutwx4116KD7+U4x6WF +H6vPNOw/KP4M8VeGTslV9xzU2KV9Bnpv1d8Q34FOIWWxtuEXeZVFBs5fzNxGiWNo +RI2T9GRwoD2dKAXDOXC4Ynsg/eTb6QihuJ49CcdP+yz4k3ZB3lLg4VfSnQO8d57+ +nile98FRYB/e2guyLXW3Q0iT5/Z5xoRdgFlglPx4mI88k1HtQJAH32RjJMtOcQWh +15QaiDLxInQirqWm2BJpTGCjAu4r7NRjkgtevi92a6O2JryPA9gK8kxkRr05YuWW +6zRjESjMlfGt7+/cgFhI6Uu46mWs6fyAtbXIRfmswZ/ZuepiiI7E8UuDEq3mi4TW +nsLrgxifarsbJGAzcMzs9zLzXNl5fe+epP7JI8Mk7hWSsT2RTyaGvWZzJBPqpK5j +wa19hAM8EHiGG3njxPPyBJUgriOCxLM6AGK/5jYk4Ve6xx6QddVfP5VhK8E7zeWz +aGHQRiapIVJpLesux+t3zqY6tQMzT3bR51xUAV3LePTJDL/PEo4XLSNolOer/qmy +KwbQBM0= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDezCCAmOgAwIBAgIBATANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJUVzES +MBAGA1UECgwJVEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFU +V0NBIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwODI4MDcyNDMz +WhcNMzAxMjMxMTU1OTU5WjBfMQswCQYDVQQGEwJUVzESMBAGA1UECgwJVEFJV0FO +LUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NBIFJvb3QgQ2VydGlm +aWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB +AQCwfnK4pAOU5qfeCTiRShFAh6d8WWQUe7UREN3+v9XAu1bihSX0NXIP+FPQQeFE +AcK0HMMxQhZHhTMidrIKbw/lJVBPhYa+v5guEGcevhEFhgWQxFnQfHgQsIBct+HH +K3XLfJ+utdGdIzdjp9xCoi2SBBtQwXu4PhvJVgSLL1KbralW6cH/ralYhzC2gfeX +RfwZVzsrb+RH9JlF/h3x+JejiB03HFyP4HYlmlD4oFT/RJB2I9IyxsOrBr/8+7/z +rX2SYgJbKdM1o5OaQ2RgXbL6Mv87BK9NQGr5x+PvI/1ry+UPizgN7gr8/g+YnzAx +3WxSZfmLgb4i4RxYA7qRG4kHAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqOFsmjd6LWvJPelSDGRjjCDWmujANBgkq +hkiG9w0BAQUFAAOCAQEAPNV3PdrfibqHDAhUaiBQkr6wQT25JmSDCi/oQMCXKCeC +MErJk/9q56YAf4lCmtYR5VPOL8zy2gXE/uJQxDqGfczafhAJO5I1KlOy/usrBdls +XebQ79NqZp4VKIV66IIArB6nCWlWQtNoURi+VJq/REG6Sb4gumlc7rh3zc5sH62D +lhh9DrUUOYTxKOkto557HnpyWoOzeW/vtPzQCqVYT0bf+215WfKEIlKuD8z7fDvn +aspHYcN6+NOSBB+4IIThNlQWx0DeO4pz3N/GCUzf7Nr/1FNCocnyYh0igzyXxfkZ +YiesZSLX0zzG5Y6yU8xJzrww/nsOM5D77dIUkR8Hrw== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFcjCCA1qgAwIBAgIQH51ZWtcvwgZEpYAIaeNe9jANBgkqhkiG9w0BAQUFADA/ +MQswCQYDVQQGEwJUVzEwMC4GA1UECgwnR292ZXJubWVudCBSb290IENlcnRpZmlj +YXRpb24gQXV0aG9yaXR5MB4XDTAyMTIwNTEzMjMzM1oXDTMyMTIwNTEzMjMzM1ow +PzELMAkGA1UEBhMCVFcxMDAuBgNVBAoMJ0dvdmVybm1lbnQgUm9vdCBDZXJ0aWZp +Y2F0aW9uIEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB +AJoluOzMonWoe/fOW1mKydGGEghU7Jzy50b2iPN86aXfTEc2pBsBHH8eV4qNw8XR +IePaJD9IK/ufLqGU5ywck9G/GwGHU5nOp/UKIXZ3/6m3xnOUT0b3EEk3+qhZSV1q +gQdW8or5BtD3cCJNtLdBuTK4sfCxw5w/cP1T3YGq2GN49thTbqGsaoQkclSGxtKy +yhwOeYHWtXBiCAEuTk8O1RGvqa/lmr/czIdtJuTJV6L7lvnM4T9TjGxMfptTCAts +F/tnyMKtsc2AtJfcdgEWFelq16TheEfOhtX7MfP6Mb40qij7cEwdScevLJ1tZqa2 +jWR+tSBqnTuBto9AAGdLiYa4zGX+FVPpBMHWXx1E1wovJ5pGfaENda1UhhXcSTvx +ls4Pm6Dso3pdvtUqdULle96ltqqvKKyskKw4t9VoNSZ63Pc78/1Fm9G7Q3hub/FC +VGqY8A2tl+lSXunVanLeavcbYBT0peS2cWeqH+riTcFCQP5nRhc4L0c/cZyu5SHK +YS1tB6iEfC3uUSXxY5Ce/eFXiGvviiNtsea9P63RPZYLhY3Naye7twWb7LuRqQoH +EgKXTiCQ8P8NHuJBO9NAOueNXdpm5AKwB1KYXA6OM5zCppX7VRluTI6uSw+9wThN +Xo+EHWbNxWCWtFJaBYmOlXqYwZE8lSOyDvR5tMl8wUohAgMBAAGjajBoMB0GA1Ud +DgQWBBTMzO/MKWCkO7GStjz6MmKPrCUVOzAMBgNVHRMEBTADAQH/MDkGBGcqBwAE +MTAvMC0CAQAwCQYFKw4DAhoFADAHBgVnKgMAAAQUA5vwIhP/lSg209yewDL7MTqK +UWUwDQYJKoZIhvcNAQEFBQADggIBAECASvomyc5eMN1PhnR2WPWus4MzeKR6dBcZ +TulStbngCnRiqmjKeKBMmo4sIy7VahIkv9Ro04rQ2JyftB8M3jh+Vzj8jeJPXgyf +qzvS/3WXy6TjZwj/5cAWtUgBfen5Cv8b5Wppv3ghqMKnI6mGq3ZW6A4M9hPdKmaK +ZEk9GhiHkASfQlK3T8v+R0F2Ne//AHY2RTKbxkaFXeIksB7jSJaYV0eUVXoPQbFE +JPPB/hprv4j9wabak2BegUqZIJxIZhm1AHlUD7gsL0u8qV1bYH+Mh6XgUmMqvtg7 +hUAV/h62ZT/FS9p+tXo1KaMuephgIqP0fSdOLeq0dDzpD6QzDxARvBMB1uUO07+1 +EqLhRSPAzAhuYbeJq4PjJB7mXQfnHyA+z2fI56wwbSdLaG5LKlwCCDTb+HbkZ6Mm +nD+iMsJKxYEYMRBWqoTvLQr/uB930r+lWKBi5NdLkXWNiYCYfm3LU05er/ayl4WX +udpVBrkk7tfGOB5jGxI7leFYrPLfhNVfmS8NVVvmONsuP3LpSIXLuykTjx44Vbnz +ssQwmSNOXfJIoRIM3BKQCZBUkQM8R+XVyWXgt0t97EfTsws+rZ7QdAAO671RrcDe +LMDDav7v3Aun+kbfYNucpllQdSNpc5Oy+fwC00fmcc4QAu4njIT/rEUNE1yDMuAl +pYYsfPQS +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFODCCAyCgAwIBAgIRAJW+FqD3LkbxezmCcvqLzZYwDQYJKoZIhvcNAQEFBQAw +NzEUMBIGA1UECgwLVGVsaWFTb25lcmExHzAdBgNVBAMMFlRlbGlhU29uZXJhIFJv +b3QgQ0EgdjEwHhcNMDcxMDE4MTIwMDUwWhcNMzIxMDE4MTIwMDUwWjA3MRQwEgYD +VQQKDAtUZWxpYVNvbmVyYTEfMB0GA1UEAwwWVGVsaWFTb25lcmEgUm9vdCBDQSB2 +MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMK+6yfwIaPzaSZVfp3F +VRaRXP3vIb9TgHot0pGMYzHw7CTww6XScnwQbfQ3t+XmfHnqjLWCi65ItqwA3GV1 +7CpNX8GH9SBlK4GoRz6JI5UwFpB/6FcHSOcZrr9FZ7E3GwYq/t75rH2D+1665I+X +Z75Ljo1kB1c4VWk0Nj0TSO9P4tNmHqTPGrdeNjPUtAa9GAH9d4RQAEX1jF3oI7x+ +/jXh7VB7qTCNGdMJjmhnXb88lxhTuylixcpecsHHltTbLaC0H2kD7OriUPEMPPCs +81Mt8Bz17Ww5OXOAFshSsCPN4D7c3TxHoLs1iuKYaIu+5b9y7tL6pe0S7fyYGKkm +dtwoSxAgHNN/Fnct7W+A90m7UwW7XWjH1Mh1Fj+JWov3F0fUTPHSiXk+TT2YqGHe +Oh7S+F4D4MHJHIzTjU3TlTazN19jY5szFPAtJmtTfImMMsJu7D0hADnJoWjiUIMu +sDor8zagrC/kb2HCUQk5PotTubtn2txTuXZZNp1D5SDgPTJghSJRt8czu90VL6R4 +pgd7gUY2BIbdeTXHlSw7sKMXNeVzH7RcWe/a6hBle3rQf5+ztCo3O3CLm1u5K7fs +slESl1MpWtTwEhDcTwK7EpIvYtQ/aUN8Ddb8WHUBiJ1YFkveupD/RwGJBmr2X7KQ +arMCpgKIv7NHfirZ1fpoeDVNAgMBAAGjPzA9MA8GA1UdEwEB/wQFMAMBAf8wCwYD +VR0PBAQDAgEGMB0GA1UdDgQWBBTwj1k4ALP1j5qWDNXr+nuqF+gTEjANBgkqhkiG +9w0BAQUFAAOCAgEAvuRcYk4k9AwI//DTDGjkk0kiP0Qnb7tt3oNmzqjMDfz1mgbl +dxSR651Be5kqhOX//CHBXfDkH1e3damhXwIm/9fH907eT/j3HEbAek9ALCI18Bmx +0GtnLLCo4MBANzX2hFxc469CeP6nyQ1Q6g2EdvZR74NTxnr/DlZJLo961gzmJ1Tj +TQpgcmLNkQfWpb/ImWvtxBnmq0wROMVvMeJuScg/doAmAyYp4Db29iBT4xdwNBed +Y2gea+zDTYa4EzAvXUYNR0PVG6pZDrlcjQZIrXSHX8f8MVRBE+LHIQ6e4B4N4cB7 +Q4WQxYpYxmUKeFfyxiMPAdkgS94P+5KFdSpcc41teyWRyu5FrgZLAMzTsVlQ2jqI +OylDRl6XK1TOU2+NSueW+r9xDkKLfP0ooNBIytrEgUy7onOTJsjrDNYmiLbAJM+7 +vVvrdX3pCI6GMyx5dwlppYn8s3CQh3aP0yK7Qs69cwsgJirQmz1wHiRszYd2qReW +t88NkvuOGKmYSdGe/mBEciG5Ge3C9THxOUiIkCR1VBatzvT4aRRkOfujuLpwQMcn +HL/EVlP6Y2XQ8xwOFvVrhlhNGNTkDY6lnVuR3HYkUD/GKvvZt5y11ubQ2egZixVx +SK236thZiNSQvxaz2emsWWFUyBy6ysHK4bkgTI86k4mloMy/0/Z1pHWWbVY= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEIDCCAwigAwIBAgIJAISCLF8cYtBAMA0GCSqGSIb3DQEBCwUAMIGcMQswCQYD +VQQGEwJQQTEPMA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5hbWEgQ2l0eTEk +MCIGA1UECgwbVHJ1c3RDb3IgU3lzdGVtcyBTLiBkZSBSLkwuMScwJQYDVQQLDB5U +cnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxFzAVBgNVBAMMDlRydXN0Q29y +IEVDQS0xMB4XDTE2MDIwNDEyMzIzM1oXDTI5MTIzMTE3MjgwN1owgZwxCzAJBgNV +BAYTAlBBMQ8wDQYDVQQIDAZQYW5hbWExFDASBgNVBAcMC1BhbmFtYSBDaXR5MSQw +IgYDVQQKDBtUcnVzdENvciBTeXN0ZW1zIFMuIGRlIFIuTC4xJzAlBgNVBAsMHlRy +dXN0Q29yIENlcnRpZmljYXRlIEF1dGhvcml0eTEXMBUGA1UEAwwOVHJ1c3RDb3Ig +RUNBLTEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDPj+ARtZ+odnbb +3w9U73NjKYKtR8aja+3+XzP4Q1HpGjORMRegdMTUpwHmspI+ap3tDvl0mEDTPwOA +BoJA6LHip1GnHYMma6ve+heRK9jGrB6xnhkB1Zem6g23xFUfJ3zSCNV2HykVh0A5 +3ThFEXXQmqc04L/NyFIduUd+Dbi7xgz2c1cWWn5DkR9VOsZtRASqnKmcp0yJF4Ou +owReUoCLHhIlERnXDH19MURB6tuvsBzvgdAsxZohmz3tQjtQJvLsznFhBmIhVE5/ +wZ0+fyCMgMsq2JdiyIMzkX2woloPV+g7zPIlstR8L+xNxqE6FXrntl019fZISjZF +ZtS6mFjBAgMBAAGjYzBhMB0GA1UdDgQWBBREnkj1zG1I1KBLf/5ZJC+Dl5mahjAf +BgNVHSMEGDAWgBREnkj1zG1I1KBLf/5ZJC+Dl5mahjAPBgNVHRMBAf8EBTADAQH/ +MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOCAQEABT41XBVwm8nHc2Fv +civUwo/yQ10CzsSUuZQRg2dd4mdsdXa/uwyqNsatR5Nj3B5+1t4u/ukZMjgDfxT2 +AHMsWbEhBuH7rBiVDKP/mZb3Kyeb1STMHd3BOuCYRLDE5D53sXOpZCz2HAF8P11F +hcCF5yWPldwX8zyfGm6wyuMdKulMY/okYWLW2n62HGz1Ah3UKt1VkOsqEUc8Ll50 +soIipX1TH0XsJ5F95yIW6MBoNtjG8U+ARDL54dHRHareqKucBK+tIA5kmE2la8BI +WJZpTdwHjFGTot+fDz2LYLSCjaoITmJF4PkL0uDgPFveXHEnJcLmA4GLEFPjx1Wi +tJ/X5g== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEMDCCAxigAwIBAgIJANqb7HHzA7AZMA0GCSqGSIb3DQEBCwUAMIGkMQswCQYD +VQQGEwJQQTEPMA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5hbWEgQ2l0eTEk +MCIGA1UECgwbVHJ1c3RDb3IgU3lzdGVtcyBTLiBkZSBSLkwuMScwJQYDVQQLDB5U +cnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxHzAdBgNVBAMMFlRydXN0Q29y +IFJvb3RDZXJ0IENBLTEwHhcNMTYwMjA0MTIzMjE2WhcNMjkxMjMxMTcyMzE2WjCB +pDELMAkGA1UEBhMCUEExDzANBgNVBAgMBlBhbmFtYTEUMBIGA1UEBwwLUGFuYW1h +IENpdHkxJDAiBgNVBAoMG1RydXN0Q29yIFN5c3RlbXMgUy4gZGUgUi5MLjEnMCUG +A1UECwweVHJ1c3RDb3IgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MR8wHQYDVQQDDBZU +cnVzdENvciBSb290Q2VydCBDQS0xMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEAv463leLCJhJrMxnHQFgKq1mqjQCj/IDHUHuO1CAmujIS2CNUSSUQIpid +RtLByZ5OGy4sDjjzGiVoHKZaBeYei0i/mJZ0PmnK6bV4pQa81QBeCQryJ3pS/C3V +seq0iWEk8xoT26nPUu0MJLq5nux+AHT6k61sKZKuUbS701e/s/OojZz0JEsq1pme +9J7+wH5COucLlVPat2gOkEz7cD+PSiyU8ybdY2mplNgQTsVHCJCZGxdNuWxu72CV +EY4hgLW9oHPY0LJ3xEXqWib7ZnZ2+AYfYW0PVcWDtxBWcgYHpfOxGgMFZA6dWorW +hnAbJN7+KIor0Gqw/Hqi3LJ5DotlDwIDAQABo2MwYTAdBgNVHQ4EFgQU7mtJPHo/ +DeOxCbeKyKsZn3MzUOcwHwYDVR0jBBgwFoAU7mtJPHo/DeOxCbeKyKsZn3MzUOcw +DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQAD +ggEBACUY1JGPE+6PHh0RU9otRCkZoB5rMZ5NDp6tPVxBb5UrJKF5mDo4Nvu7Zp5I +/5CQ7z3UuJu0h3U/IJvOcs+hVcFNZKIZBqEHMwwLKeXx6quj7LUKdJDHfXLy11yf +ke+Ri7fc7Waiz45mO7yfOgLgJ90WmMCV1Aqk5IGadZQ1nJBfiDcGrVmVCrDRZ9MZ +yonnMlo2HD6CqFqTvsbQZJG2z9m2GM/bftJlo6bEjhcxwft+dtvTheNYsnd6djts +L1Ac59v2Z3kf9YKVmgenFK+P3CghZwnS1k1aHBkcjndcw5QkPTJrS37UeJSDvjdN +zl/HHk484IkzlQsPpTLWPFp5LBk= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIGLzCCBBegAwIBAgIIJaHfyjPLWQIwDQYJKoZIhvcNAQELBQAwgaQxCzAJBgNV +BAYTAlBBMQ8wDQYDVQQIDAZQYW5hbWExFDASBgNVBAcMC1BhbmFtYSBDaXR5MSQw +IgYDVQQKDBtUcnVzdENvciBTeXN0ZW1zIFMuIGRlIFIuTC4xJzAlBgNVBAsMHlRy +dXN0Q29yIENlcnRpZmljYXRlIEF1dGhvcml0eTEfMB0GA1UEAwwWVHJ1c3RDb3Ig +Um9vdENlcnQgQ0EtMjAeFw0xNjAyMDQxMjMyMjNaFw0zNDEyMzExNzI2MzlaMIGk +MQswCQYDVQQGEwJQQTEPMA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5hbWEg +Q2l0eTEkMCIGA1UECgwbVHJ1c3RDb3IgU3lzdGVtcyBTLiBkZSBSLkwuMScwJQYD +VQQLDB5UcnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxHzAdBgNVBAMMFlRy +dXN0Q29yIFJvb3RDZXJ0IENBLTIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK +AoICAQCnIG7CKqJiJJWQdsg4foDSq8GbZQWU9MEKENUCrO2fk8eHyLAnK0IMPQo+ +QVqedd2NyuCb7GgypGmSaIwLgQ5WoD4a3SwlFIIvl9NkRvRUqdw6VC0xK5mC8tkq +1+9xALgxpL56JAfDQiDyitSSBBtlVkxs1Pu2YVpHI7TYabS3OtB0PAx1oYxOdqHp +2yqlO/rOsP9+aij9JxzIsekp8VduZLTQwRVtDr4uDkbIXvRR/u8OYzo7cbrPb1nK +DOObXUm4TOJXsZiKQlecdu/vvdFoqNL0Cbt3Nb4lggjEFixEIFapRBF37120Hape +az6LMvYHL1cEksr1/p3C6eizjkxLAjHZ5DxIgif3GIJ2SDpxsROhOdUuxTTCHWKF +3wP+TfSvPd9cW436cOGlfifHhi5qjxLGhF5DUVCcGZt45vz27Ud+ez1m7xMTiF88 +oWP7+ayHNZ/zgp6kPwqcMWmLmaSISo5uZk3vFsQPeSghYA2FFn3XVDjxklb9tTNM +g9zXEJ9L/cb4Qr26fHMC4P99zVvh1Kxhe1fVSntb1IVYJ12/+CtgrKAmrhQhJ8Z3 +mjOAPF5GP/fDsaOGM8boXg25NSyqRsGFAnWAoOsk+xWq5Gd/bnc/9ASKL3x74xdh +8N0JqSDIvgmk0H5Ew7IwSjiqqewYmgeCK9u4nBit2uBGF6zPXQIDAQABo2MwYTAd +BgNVHQ4EFgQU2f4hQG6UnrybPZx9mCAZ5YwwYrIwHwYDVR0jBBgwFoAU2f4hQG6U +nrybPZx9mCAZ5YwwYrIwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYw +DQYJKoZIhvcNAQELBQADggIBAJ5Fngw7tu/hOsh80QA9z+LqBrWyOrsGS2h60COX +dKcs8AjYeVrXWoSK2BKaG9l9XE1wxaX5q+WjiYndAfrs3fnpkpfbsEZC89NiqpX+ +MWcUaViQCqoL7jcjx1BRtPV+nuN79+TMQjItSQzL/0kMmx40/W5ulop5A7Zv2wnL +/V9lFDfhOPXzYRZY5LVtDQsEGz9QLX+zx3oaFoBg+Iof6Rsqxvm6ARppv9JYx1RX +CI/hOWB3S6xZhBqI8d3LT3jX5+EzLfzuQfogsL7L9ziUwOHQhQ+77Sxzq+3+knYa +ZH9bDTMJBzN7Bj8RpFxwPIXAz+OQqIN3+tvmxYxoZxBnpVIt8MSZj3+/0WvitUfW +2dCFmU2Umw9Lje4AWkcdEQOsQRivh7dvDDqPys/cA8GiCcjl/YBeyGBCARsaU1q7 +N6a3vLqE6R5sGtRk2tRD/pOLS/IseRYQ1JMLiI+h2IYURpFHmygk71dSTlxCnKr3 +Sewn6EAes6aJInKc9Q0ztFijMDvd1GpUk74aTfOTlPf8hAs/hCBcNANExdqtvArB +As8e5ZTZ845b2EzwnexhF7sUMlQMAimTHpKG9n/v55IFDlndmQguLvqcAFLTxWYp +5KeXRKQOKIETNcX2b2TmQcTVL8w0RSXPQQCWPUouwpaYT05KnJe32x+SMsj/D1Fu +1uwJ +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDZzCCAk+gAwIBAgIQGx+ttiD5JNM2a/fH8YygWTANBgkqhkiG9w0BAQUFADBF +MQswCQYDVQQGEwJHQjEYMBYGA1UEChMPVHJ1c3RpcyBMaW1pdGVkMRwwGgYDVQQL +ExNUcnVzdGlzIEZQUyBSb290IENBMB4XDTAzMTIyMzEyMTQwNloXDTI0MDEyMTEx +MzY1NFowRTELMAkGA1UEBhMCR0IxGDAWBgNVBAoTD1RydXN0aXMgTGltaXRlZDEc +MBoGA1UECxMTVHJ1c3RpcyBGUFMgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBAMVQe547NdDfxIzNjpvto8A2mfRC6qc+gIMPpqdZh8mQRUN+ +AOqGeSoDvT03mYlmt+WKVoaTnGhLaASMk5MCPjDSNzoiYYkchU59j9WvezX2fihH +iTHcDnlkH5nSW7r+f2C/revnPDgpai/lkQtV/+xvWNUtyd5MZnGPDNcE2gfmHhjj +vSkCqPoc4Vu5g6hBSLwacY3nYuUtsuvffM/bq1rKMfFMIvMFE/eC+XN5DL7XSxzA +0RU8k0Fk0ea+IxciAIleH2ulrG6nS4zto3Lmr2NNL4XSFDWaLk6M6jKYKIahkQlB +OrTh4/L68MkKokHdqeMDx4gVOxzUGpTXn2RZEm0CAwEAAaNTMFEwDwYDVR0TAQH/ +BAUwAwEB/zAfBgNVHSMEGDAWgBS6+nEleYtXQSUhhgtx67JkDoshZzAdBgNVHQ4E +FgQUuvpxJXmLV0ElIYYLceuyZA6LIWcwDQYJKoZIhvcNAQEFBQADggEBAH5Y//01 +GX2cGE+esCu8jowU/yyg2kdbw++BLa8F6nRIW/M+TgfHbcWzk88iNVy2P3UnXwmW +zaD+vkAMXBJV+JOCyinpXj9WV4s4NvdFGkwozZ5BuO1WTISkQMi4sKUraXAEasP4 +1BIy+Q7DsdwyhEQsb8tGD+pmQQ9P8Vilpg0ND2HepZ5dfWWhPBfnqFVO76DH7cZE +f1T1o+CP8HxVIo8ptoGj4W1OLBuAZ+ytIJ8MYmHVl/9D7S3B2l0pKoU/rGXuhg8F +jZBf3+6f9L/uHfuY5H+QK4R4EA5sSVPvFVtlRkpdr7r7OnIdzfYliB6XzCGcKQEN +ZetX2fNXlrtIzYE= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEJzCCAw+gAwIBAgIHAI4X/iQggTANBgkqhkiG9w0BAQsFADCBsTELMAkGA1UE +BhMCVFIxDzANBgNVBAcMBkFua2FyYTFNMEsGA1UECgxEVMOcUktUUlVTVCBCaWxn +aSDEsGxldGnFn2ltIHZlIEJpbGnFn2ltIEfDvHZlbmxpxJ9pIEhpem1ldGxlcmkg +QS7Fni4xQjBABgNVBAMMOVTDnFJLVFJVU1QgRWxla3Ryb25payBTZXJ0aWZpa2Eg +SGl6bWV0IFNhxJ9sYXnEsWPEsXPEsSBINTAeFw0xMzA0MzAwODA3MDFaFw0yMzA0 +MjgwODA3MDFaMIGxMQswCQYDVQQGEwJUUjEPMA0GA1UEBwwGQW5rYXJhMU0wSwYD +VQQKDERUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUgQmlsacWfaW0gR8O8 +dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLjFCMEAGA1UEAww5VMOcUktUUlVTVCBF +bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxIEg1MIIB +IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApCUZ4WWe60ghUEoI5RHwWrom +/4NZzkQqL/7hzmAD/I0Dpe3/a6i6zDQGn1k19uwsu537jVJp45wnEFPzpALFp/kR +Gml1bsMdi9GYjZOHp3GXDSHHmflS0yxjXVW86B8BSLlg/kJK9siArs1mep5Fimh3 +4khon6La8eHBEJ/rPCmBp+EyCNSgBbGM+42WAA4+Jd9ThiI7/PS98wl+d+yG6w8z +5UNP9FR1bSmZLmZaQ9/LXMrI5Tjxfjs1nQ/0xVqhzPMggCTTV+wVunUlm+hkS7M0 +hO8EuPbJbKoCPrZV4jI3X/xml1/N1p7HIL9Nxqw/dV8c7TKcfGkAaZHjIxhT6QID +AQABo0IwQDAdBgNVHQ4EFgQUVpkHHtOsDGlktAxQR95DLL4gwPswDgYDVR0PAQH/ +BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAJ5FdnsX +SDLyOIspve6WSk6BGLFRRyDN0GSxDsnZAdkJzsiZ3GglE9Rc8qPoBP5yCccLqh0l +VX6Wmle3usURehnmp349hQ71+S4pL+f5bFgWV1Al9j4uPqrtd3GqqpmWRgqujuwq +URawXs3qZwQcWDD1YIq9pr1N5Za0/EKJAWv2cMhQOQwt1WbZyNKzMrcbGW3LM/nf +peYVhDfwwvJllpKQd/Ct9JDpEXjXk4nAPQu6KfTomZ1yju2dL+6SfaHx/126M2CF +Yv4HAqGEVka+lgqaE9chTLd8B59OTj+RdPsnnRHM3eaxynFNExc5JsUpISuTKWqW ++qtB4Uu2NQvAmxU= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICjzCCAhWgAwIBAgIQXIuZxVqUxdJxVt7NiYDMJjAKBggqhkjOPQQDAzCBiDEL +MAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNl +eSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMT +JVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMjAx +MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgT +Ck5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVUaGUg +VVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlm +aWNhdGlvbiBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQarFRaqflo +I+d61SRvU8Za2EurxtW20eZzca7dnNYMYf3boIkDuAUU7FfO7l0/4iGzzvfUinng +o4N+LZfQYcTxmdwlkWOrfzCjtHDix6EznPO/LlxTsV+zfTJ/ijTjeXmjQjBAMB0G +A1UdDgQWBBQ64QmG1M8ZwpZ2dEl23OA1xmNjmjAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjA2Z6EWCNzklwBBHU6+4WMB +zzuqQhFkoJ2UOQIReVx7Hfpkue4WQrO/isIJxOzksU0CMQDpKmFHjFJKS04YcPbW +RNZu9YO6bVi9JNlWSOrvxKJGgYhqOkbRqZtNyWHa0V1Xahg= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCB +iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl +cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV +BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAw +MjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNV +BAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU +aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2Vy +dGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK +AoICAQCAEmUXNg7D2wiz0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B +3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2jY0K2dvKpOyuR+OJv0OwWIJAJPuLodMkY +tJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFnRghRy4YUVD+8M/5+bJz/ +Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O+T23LLb2 +VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT +79uq/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6 +c0Plfg6lZrEpfDKEY1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmT +Yo61Zs8liM2EuLE/pDkP2QKe6xJMlXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97l +c6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8yexDJtC/QV9AqURE9JnnV4ee +UB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+eLf8ZxXhyVeE +Hg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd +BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8G +A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPF +Up/L+M+ZBn8b2kMVn54CVVeWFPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KO +VWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ7l8wXEskEVX/JJpuXior7gtNn3/3 +ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQEg9zKC7F4iRO/Fjs +8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM8WcR +iQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYze +Sf7dNXGiFSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZ +XHlKYC6SQK5MNyosycdiyA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/ +qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9cJ2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRB +VXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGwsAvgnEzDHNb842m1R0aB +L6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gxQ+6IHdfG +jjxDah2nGN59PRbxYvnKkKj9 +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDhDCCAwqgAwIBAgIQL4D+I4wOIg9IZxIokYesszAKBggqhkjOPQQDAzCByjEL +MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW +ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2ln +biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJp +U2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y +aXR5IC0gRzQwHhcNMDcxMTA1MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCByjELMAkG +A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJp +U2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2lnbiwg +SW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2ln +biBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5 +IC0gRzQwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASnVnp8Utpkmw4tXNherJI9/gHm +GUo9FANL+mAnINmDiWn6VMaaGF5VKmTeBvaNSjutEDxlPZCIBIngMGGzrl0Bp3ve +fLK+ymVhAIau2o970ImtTR1ZmkGxvEeA3J5iw/mjgbIwga8wDwYDVR0TAQH/BAUw +AwEB/zAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJ +aW1hZ2UvZ2lmMCEwHzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYj +aHR0cDovL2xvZ28udmVyaXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFLMW +kf3upm7ktS5Jj4d4gYDs5bG1MAoGCCqGSM49BAMDA2gAMGUCMGYhDBgmYFo4e1ZC +4Kf8NoRRkSAsdk1DPcQdhCPQrNZ8NQbOzWm9kA3bbEhCHQ6qQgIxAJw9SDkjOVga +FRJZap7v1VmyHVIsmXHNxynfGyphe3HR3vPA5Q06Sqotp9iGKt0uEA== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIE0zCCA7ugAwIBAgIQGNrRniZ96LtKIVjNzGs7SjANBgkqhkiG9w0BAQUFADCB +yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL +ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp +U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW +ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCByjEL +MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW +ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2ln +biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJp +U2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y +aXR5IC0gRzUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvJAgIKXo1 +nmAMqudLO07cfLw8RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKzj/i5Vbex +t0uz/o9+B1fs70PbZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIz +SdhDY2pSS9KP6HBRTdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQG +BO+QueQA5N06tRn/Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+ +rCpSx4/VBEnkjWNHiDxpg8v+R70rfk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/ +NIeWiu5T6CUVAgMBAAGjgbIwga8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8E +BAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEwHzAH +BgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVy +aXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFH/TZafC3ey78DAJ80M5+gKv +MzEzMA0GCSqGSIb3DQEBBQUAA4IBAQCTJEowX2LP2BqYLz3q3JktvXf2pXkiOOzE +p6B4Eq1iDkVwZMXnl2YtmAl+X6/WzChl8gGqCBpH3vn5fJJaCGkgDdk+bW48DW7Y +5gaRQBi5+MHt39tBquCWIMnNZBU4gcmU7qKEKQsTb47bDN0lAtukixlE0kF6BWlK +WE9gyn6CagsCqiUXObXbf+eEZSqVir2G3l6BFoMtEMze/aiCKm0oHw0LxOXnGiYZ +4fQRbxC1lfznQgUy286dUV4otp6F01vvpX1FQHKOtw5rDgb7MzVIcbidJ4vEZV8N +hnacRHr2lVz2XTIIM6RUthg/aFzyQkqFOFSDX9HoLPKsEdao7WNq +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEuTCCA6GgAwIBAgIQQBrEZCGzEyEDDrvkEhrFHTANBgkqhkiG9w0BAQsFADCB +vTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL +ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwOCBWZXJp +U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MTgwNgYDVQQDEy9W +ZXJpU2lnbiBVbml2ZXJzYWwgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAe +Fw0wODA0MDIwMDAwMDBaFw0zNzEyMDEyMzU5NTlaMIG9MQswCQYDVQQGEwJVUzEX +MBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0 +IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9y +IGF1dGhvcml6ZWQgdXNlIG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNh +bCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAx2E3XrEBNNti1xWb/1hajCMj1mCOkdeQmIN65lgZOIzF +9uVkhbSicfvtvbnazU0AtMgtc6XHaXGVHzk8skQHnOgO+k1KxCHfKWGPMiJhgsWH +H26MfF8WIFFE0XBPV+rjHOPMee5Y2A7Cs0WTwCznmhcrewA3ekEzeOEz4vMQGn+H +LL729fdC4uW/h2KJXwBL38Xd5HVEMkE6HnFuacsLdUYI0crSK5XQz/u5QGtkjFdN +/BMReYTtXlT2NJ8IAfMQJQYXStrxHXpma5hgZqTZ79IugvHw7wnqRMkVauIDbjPT +rJ9VAMf2CGqUuV/c4DPxhGD5WycRtPwW8rtWaoAljQIDAQABo4GyMIGvMA8GA1Ud +EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMG0GCCsGAQUFBwEMBGEwX6FdoFsw +WTBXMFUWCWltYWdlL2dpZjAhMB8wBwYFKw4DAhoEFI/l0xqGrI2Oa8PPgGrUSBgs +exkuMCUWI2h0dHA6Ly9sb2dvLnZlcmlzaWduLmNvbS92c2xvZ28uZ2lmMB0GA1Ud +DgQWBBS2d/ppSEefUxLVwuoHMnYH0ZcHGTANBgkqhkiG9w0BAQsFAAOCAQEASvj4 +sAPmLGd75JR3Y8xuTPl9Dg3cyLk1uXBPY/ok+myDjEedO2Pzmvl2MpWRsXe8rJq+ +seQxIcaBlVZaDrHC1LGmWazxY8u4TB1ZkErvkBYoH1quEPuBUDgMbMzxPcP1Y+Oz +4yHJJDnp/RVmRvQbEdBNc6N9Rvk97ahfYtTxP/jgdFcrGJ2BtMQo2pSXpXDrrB2+ +BxHw1dvd5Yzw1TKwg+ZX4o+/vqGqvz0dtdQ46tewXDpPaj+PwGZsY6rp2aQW9IHR +lRQOfc2VNNnSj3BzgXucfr2YYdhFh5iQxeuGMMY1v/D/w1WIg0vvBZIGcfK4mJO3 +7M2CYfE45k+XmCpajQ== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEGjCCAwICEQCbfgZJoz5iudXukEhxKe9XMA0GCSqGSIb3DQEBBQUAMIHKMQsw +CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZl +cmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWdu +LCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlT +aWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3Jp +dHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQswCQYD +VQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlT +aWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJ +bmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWdu +IENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg +LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMu6nFL8eB8aHm8b +N3O9+MlrlBIwT/A2R/XQkQr1F8ilYcEWQE37imGQ5XYgwREGfassbqb1EUGO+i2t +KmFZpGcmTNDovFJbcCAEWNF6yaRpvIMXZK0Fi7zQWM6NjPXr8EJJC52XJ2cybuGu +kxUccLwgTS8Y3pKI6GyFVxEa6X7jJhFUokWWVYPKMIno3Nij7SqAP395ZVc+FSBm +CC+Vk7+qRy+oRpfwEuL+wgorUeZ25rdGt+INpsyow0xZVYnm6FNcHOqd8GIWC6fJ +Xwzw3sJ2zq/3avL6QaaiMxTJ5Xpj055iN9WFZZ4O5lMkdBteHRJTW8cs54NJOxWu +imi5V5cCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAERSWwauSCPc/L8my/uRan2Te +2yFPhpk0djZX3dAVL8WtfxUfN2JzPtTnX84XA9s1+ivbrmAJXx5fj267Cz3qWhMe +DGBvtcC1IyIuBwvLqXTLR7sdwdela8wv0kL9Sd2nic9TutoAWii/gt/4uhMdUIaC +/Y4wjylGsB49Ndo4YhYYSq3mtlFs3q9i6wHQHiT+eo8SGhJouPtmmRQURVyu565p +F4ErWjfJXir0xuKhXFSbplQAz/DxwceYMBo7Nhbbo27q/a2ywtrvAkcTisDxszGt +TxzhT5yvDwyd93gN2PQ1VoDat20Xj50egWTh/sVFuq1ruQp6Tk9LhO5L8X3dEQ== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDojCCAoqgAwIBAgIQE4Y1TR0/BvLB+WUF1ZAcYjANBgkqhkiG9w0BAQUFADBr +MQswCQYDVQQGEwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMmVmlzYSBJbnRl +cm5hdGlvbmFsIFNlcnZpY2UgQXNzb2NpYXRpb24xHDAaBgNVBAMTE1Zpc2EgZUNv +bW1lcmNlIFJvb3QwHhcNMDIwNjI2MDIxODM2WhcNMjIwNjI0MDAxNjEyWjBrMQsw +CQYDVQQGEwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMmVmlzYSBJbnRlcm5h +dGlvbmFsIFNlcnZpY2UgQXNzb2NpYXRpb24xHDAaBgNVBAMTE1Zpc2EgZUNvbW1l +cmNlIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvV95WHm6h +2mCxlCfLF9sHP4CFT8icttD0b0/Pmdjh28JIXDqsOTPHH2qLJj0rNfVIsZHBAk4E +lpF7sDPwsRROEW+1QK8bRaVK7362rPKgH1g/EkZgPI2h4H3PVz4zHvtH8aoVlwdV +ZqW1LS7YgFmypw23RuwhY/81q6UCzyr0TP579ZRdhE2o8mCP2w4lPJ9zcc+U30rq +299yOIzzlr3xF7zSujtFWsan9sYXiwGd/BmoKoMWuDpI/k4+oKsGGelT84ATB+0t +vz8KPFUgOSwsAGl0lUq8ILKpeeUYiZGo3BxN77t+Nwtd/jmliFKMAGzsGHxBvfaL +dXe6YJ2E5/4tAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD +AgEGMB0GA1UdDgQWBBQVOIMPPyw/cDMezUb+B4wg4NfDtzANBgkqhkiG9w0BAQUF +AAOCAQEAX/FBfXxcCLkr4NWSR/pnXKUTwwMhmytMiUbPWU3J/qVAtmPN3XEolWcR +zCSs00Rsca4BIGsDoo8Ytyk6feUWYFN4PMCvFYP3j1IzJL1kk5fui/fbGKhtcbP3 +LBfQdCVp9/5rPJS+TUtBjE7ic9DjkCJzQ83z7+pzzkWKsKZJ/0x9nXGIxHYdkFsd +7v3M9+79YKWxehZx0RbQfBI8bGmX265fOZpwLwU8GUYEmSA20GBuYQa7FkKMcPcw +++DbZqMAAb3mLNqRX6BGi01qnD093QVG/na/oAo85ADmJ7f/hC3euiInlhBx6yLt +398znM/jra6O1I7mT1GvFpLgXPYHDw== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEMDCCAxigAwIBAgIQUJRs7Bjq1ZxN1ZfvdY+grTANBgkqhkiG9w0BAQUFADCB +gjELMAkGA1UEBhMCVVMxHjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEk +MCIGA1UEChMbWFJhbXAgU2VjdXJpdHkgU2VydmljZXMgSW5jMS0wKwYDVQQDEyRY +UmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQxMTAxMTcx +NDA0WhcNMzUwMTAxMDUzNzE5WjCBgjELMAkGA1UEBhMCVVMxHjAcBgNVBAsTFXd3 +dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2VjdXJpdHkgU2Vy +dmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCYJB69FbS6 +38eMpSe2OAtp87ZOqCwuIR1cRN8hXX4jdP5efrRKt6atH67gBhbim1vZZ3RrXYCP +KZ2GG9mcDZhtdhAoWORlsH9KmHmf4MMxfoArtYzAQDsRhtDLooY2YKTVMIJt2W7Q +DxIEM5dfT2Fa8OT5kavnHTu86M/0ay00fOJIYRyO82FEzG+gSqmUsE3a56k0enI4 +qEHMPJQRfevIpoy3hsvKMzvZPTeL+3o+hiznc9cKV6xkmxnr9A8ECIqsAxcZZPRa +JSKNNCyy9mgdEm3Tih4U2sSPpuIjhdV6Db1q4Ons7Be7QhtnqiXtRYMh/MHJfNVi +PvryxS3T/dRlAgMBAAGjgZ8wgZwwEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0P +BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMZPoj0GY4QJnM5i5ASs +jVy16bYbMDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9jcmwueHJhbXBzZWN1cml0 +eS5jb20vWEdDQS5jcmwwEAYJKwYBBAGCNxUBBAMCAQEwDQYJKoZIhvcNAQEFBQAD +ggEBAJEVOQMBG2f7Shz5CmBbodpNl2L5JFMn14JkTpAuw0kbK5rc/Kh4ZzXxHfAR +vbdI4xD2Dd8/0sm2qlWkSLoC295ZLhVbO50WfUfXN+pfTXYSNrsf16GBBEYgoyxt +qZ4Bfj8pzgCT3/3JknOJiWSe5yvkHJEs0rnOfc5vMZnT5r7SHpDwCRR5XCOrTdLa +IR9NmXmd4c8nnxCbHIgNsIpkQTG4DmyQJKSbXHGPurt+HBvbaoAPIbzp26a3QPSy +i6mx5O+aGtA9aZnuqCij4Tyz8LIRnM98QObd50N9otg6tamN8jSZxNQQ4Qb9CYQQ +O+7ETPTsJ3xCwnR8gooJybQDJbw= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDODCCAiCgAwIBAgIGIAYFFnACMA0GCSqGSIb3DQEBBQUAMDsxCzAJBgNVBAYT +AlJPMREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBD +QTAeFw0wNjA3MDQxNzIwMDRaFw0zMTA3MDQxNzIwMDRaMDsxCzAJBgNVBAYTAlJP +MREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBDQTCC +ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALczuX7IJUqOtdu0KBuqV5Do +0SLTZLrTk+jUrIZhQGpgV2hUhE28alQCBf/fm5oqrl0Hj0rDKH/v+yv6efHHrfAQ +UySQi2bJqIirr1qjAOm+ukbuW3N7LBeCgV5iLKECZbO9xSsAfsT8AzNXDe3i+s5d +RdY4zTW2ssHQnIFKquSyAVwdj1+ZxLGt24gh65AIgoDzMKND5pCCrlUoSe1b16kQ +OA7+j0xbm0bqQfWwCHTD0IgztnzXdN/chNFDDnU5oSVAKOp4yw4sLjmdjItuFhwv +JoIQ4uNllAoEwF73XVv4EOLQunpL+943AAAaWyjj0pxzPjKHmKHJUS/X3qwzs08C +AwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAcYwHQYDVR0O +BBYEFOCMm9slSbPxfIbWskKHC9BroNnkMA0GCSqGSIb3DQEBBQUAA4IBAQA+0hyJ +LjX8+HXd5n9liPRyTMks1zJO890ZeUe9jjtbkw9QSSQTaxQGcu8J06Gh40CEyecY +MnQ8SG4Pn0vU9x7Tk4ZkVJdjclDVVc/6IJMCopvDI5NOFlV2oHB5bc0hH88vLbwZ +44gx+FkagQnIl6Z0x2DEW8xXjrJ1/RsCCdtZb3KTafcxQdaIOL+Hsr0Wefmq5L6I +Jd1hJyMctTEHBDa0GpC9oHRxUIltvBTjD4au8as+x6AJzKNI0eDbZOeStc+vckNw +i/nDhDwTqn6Sm1dTk/pwwpEOMfmbZ13pljheX7NzTogVZ96edhBiIL5VaZVDADlN +9u6wWk5JRFRYX0KD +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFsDCCA5igAwIBAgIQFci9ZUdcr7iXAF7kBtK8nTANBgkqhkiG9w0BAQUFADBe +MQswCQYDVQQGEwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0 +ZC4xKjAoBgNVBAsMIWVQS0kgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAe +Fw0wNDEyMjAwMjMxMjdaFw0zNDEyMjAwMjMxMjdaMF4xCzAJBgNVBAYTAlRXMSMw +IQYDVQQKDBpDaHVuZ2h3YSBUZWxlY29tIENvLiwgTHRkLjEqMCgGA1UECwwhZVBL +SSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0BAQEF +AAOCAg8AMIICCgKCAgEA4SUP7o3biDN1Z82tH306Tm2d0y8U82N0ywEhajfqhFAH +SyZbCUNsIZ5qyNUD9WBpj8zwIuQf5/dqIjG3LBXy4P4AakP/h2XGtRrBp0xtInAh +ijHyl3SJCRImHJ7K2RKilTza6We/CKBk49ZCt0Xvl/T29de1ShUCWH2YWEtgvM3X +DZoTM1PRYfl61dd4s5oz9wCGzh1NlDivqOx4UXCKXBCDUSH3ET00hl7lSM2XgYI1 +TBnsZfZrxQWh7kcT1rMhJ5QQCtkkO7q+RBNGMD+XPNjX12ruOzjjK9SXDrkb5wdJ +fzcq+Xd4z1TtW0ado4AOkUPB1ltfFLqfpo0kR0BZv3I4sjZsN/+Z0V0OWQqraffA +sgRFelQArr5T9rXn4fg8ozHSqf4hUmTFpmfwdQcGlBSBVcYn5AGPF8Fqcde+S/uU +WH1+ETOxQvdibBjWzwloPn9s9h6PYq2lY9sJpx8iQkEeb5mKPtf5P0B6ebClAZLS +nT0IFaUQAS2zMnaolQ2zepr7BxB4EW/hj8e6DyUadCrlHJhBmd8hh+iVBmoKs2pH +dmX2Os+PYhcZewoozRrSgx4hxyy/vv9haLdnG7t4TY3OZ+XkwY63I2binZB1NJip +NiuKmpS5nezMirH4JYlcWrYvjB9teSSnUmjDhDXiZo1jDiVN1Rmy5nk3pyKdVDEC +AwEAAaNqMGgwHQYDVR0OBBYEFB4M97Zn8uGSJglFwFU5Lnc/QkqiMAwGA1UdEwQF +MAMBAf8wOQYEZyoHAAQxMC8wLQIBADAJBgUrDgMCGgUAMAcGBWcqAwAABBRFsMLH +ClZ87lt4DJX5GFPBphzYEDANBgkqhkiG9w0BAQUFAAOCAgEACbODU1kBPpVJufGB +uvl2ICO1J2B01GqZNF5sAFPZn/KmsSQHRGoqxqWOeBLoR9lYGxMqXnmbnwoqZ6Yl +PwZpVnPDimZI+ymBV3QGypzqKOg4ZyYr8dW1P2WT+DZdjo2NQCCHGervJ8A9tDkP +JXtoUHRVnAxZfVo9QZQlUgjgRywVMRnVvwdVxrsStZf0X4OFunHB2WyBEXYKCrC/ +gpf36j36+uwtqSiUO1bd0lEursC9CBWMd1I0ltabrNMdjmEPNXubrjlpC2JgQCA2 +j6/7Nu4tCEoduL+bXPjqpRugc6bY+G7gMwRfaKonh+3ZwZCc7b3jajWvY9+rGNm6 +5ulK6lCKD2GTHuItGeIwlDWSXQ62B68ZgI9HkFFLLk3dheLSClIKF5r8GrBQAuUB +o2M3IUxExJtRmREOc5wGj1QupyheRDmHVi03vYVElOEMSyycw5KFNGHLD7ibSkNS +/jQ6fbjpKdx2qcgw+BRxgMYeNkh0IkFch4LoGHGLQYlE535YW6i4jRPpp2zDR+2z +Gp1iro2C6pSe3VkQw63d4k3jMdXH7OjysP6SHhYKGvzZ8/gntsm+HbRsZJB/9OTE +W9c3rkIO3aQab3yIVMUWbuF6aC74Or8NpDyJO3inTmODBCEIZ43ygknQW/2xzQ+D +hNQ+IIX3Sj0rnP0qCglN6oH4EZw= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEIDCCAwigAwIBAgIQNE7VVyDV7exJ9C/ON9srbTANBgkqhkiG9w0BAQUFADCB +qTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf +Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw +MDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNV +BAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMDYxMTE3MDAwMDAwWhcNMzYw +NzE2MjM1OTU5WjCBqTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5j +LjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYG +A1UECxMvKGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl +IG9ubHkxHzAdBgNVBAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCsoPD7gFnUnMekz52hWXMJEEUMDSxuaPFs +W0hoSVk3/AszGcJ3f8wQLZU0HObrTQmnHNK4yZc2AreJ1CRfBsDMRJSUjQJib+ta +3RGNKJpchJAQeg29dGYvajig4tVUROsdB58Hum/u6f1OCyn1PoSgAfGcq/gcfomk +6KHYcWUNo1F77rzSImANuVud37r8UVsLr5iy6S7pBOhih94ryNdOwUxkHt3Ph1i6 +Sk/KaAcdHJ1KxtUvkcx8cXIcxcBn6zL9yZJclNqFwJu/U30rCfSMnZEfl2pSy94J +NqR32HuHUETVPm4pafs5SSYeCaWAe0At6+gnhcn+Yf1+5nyXHdWdAgMBAAGjQjBA +MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBR7W0XP +r87Lev0xkhpqtvNG61dIUDANBgkqhkiG9w0BAQUFAAOCAQEAeRHAS7ORtvzw6WfU +DW5FvlXok9LOAz/t2iWwHVfLHjp2oEzsUHboZHIMpKnxuIvW1oeEuzLlQRHAd9mz +YJ3rG9XRbkREqaYB7FViHXe4XI5ISXycO1cRrK1zN44veFyQaEfZYGDm/Ac9IiAX +xPcW6cTYcvnIc3zfFi8VqT79aie2oetaupgf1eNNZAqdE8hhuvU5HIe6uL17In/2 +/qxAeeWsEG89jxt5dovEN7MhGITlNgDrYyCZuen+MwS7QcjBAvlEYyCegc5C09Y/ +LHbTY5xZ3Y+m4Q6gLkH3LpVHz7z9M/P2C2F+fpErgUfCJzDupxBdN49cOSvkBPB7 +jVaMaA== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICiDCCAg2gAwIBAgIQNfwmXNmET8k9Jj1Xm67XVjAKBggqhkjOPQQDAzCBhDEL +MAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjE4MDYGA1UECxMvKGMp +IDIwMDcgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAi +BgNVBAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMjAeFw0wNzExMDUwMDAw +MDBaFw0zODAxMTgyMzU5NTlaMIGEMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhh +d3RlLCBJbmMuMTgwNgYDVQQLEy8oYykgMjAwNyB0aGF3dGUsIEluYy4gLSBGb3Ig +YXV0aG9yaXplZCB1c2Ugb25seTEkMCIGA1UEAxMbdGhhd3RlIFByaW1hcnkgUm9v +dCBDQSAtIEcyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEotWcgnuVnfFSeIf+iha/ +BebfowJPDQfGAFG6DAJSLSKkQjnE/o/qycG+1E3/n3qe4rF8mq2nhglzh9HnmuN6 +papu+7qzcMBniKI11KOasf2twu8x+qi58/sIxpHR+ymVo0IwQDAPBgNVHRMBAf8E +BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUmtgAMADna3+FGO6Lts6K +DPgR4bswCgYIKoZIzj0EAwMDaQAwZgIxAN344FdHW6fmCsO99YCKlzUNG4k8VIZ3 +KMqh9HneteY4sPBlcIx/AlTCv//YoT7ZzwIxAMSNlPzcU9LcnXgWHxUzI1NS41ox +XZ3Krr0TKUQNJ1uo52icEvdYPy5yAlejj6EULg== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEKjCCAxKgAwIBAgIQYAGXt0an6rS0mtZLL/eQ+zANBgkqhkiG9w0BAQsFADCB +rjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf +Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw +MDggdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAiBgNV +BAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMzAeFw0wODA0MDIwMDAwMDBa +Fw0zNzEyMDEyMzU5NTlaMIGuMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhhd3Rl +LCBJbmMuMSgwJgYDVQQLEx9DZXJ0aWZpY2F0aW9uIFNlcnZpY2VzIERpdmlzaW9u +MTgwNgYDVQQLEy8oYykgMjAwOCB0aGF3dGUsIEluYy4gLSBGb3IgYXV0aG9yaXpl +ZCB1c2Ugb25seTEkMCIGA1UEAxMbdGhhd3RlIFByaW1hcnkgUm9vdCBDQSAtIEcz +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsr8nLPvb2FvdeHsbnndm +gcs+vHyu86YnmjSjaDFxODNi5PNxZnmxqWWjpYvVj2AtP0LMqmsywCPLLEHd5N/8 +YZzic7IilRFDGF/Eth9XbAoFWCLINkw6fKXRz4aviKdEAhN0cXMKQlkC+BsUa0Lf +b1+6a4KinVvnSr0eAXLbS3ToO39/fR8EtCab4LRarEc9VbjXsCZSKAExQGbY2SS9 +9irY7CFJXJv2eul/VTV+lmuNk5Mny5K76qxAwJ/C+IDPXfRa3M50hqY+bAtTyr2S +zhkGcuYMXDhpxwTWvGzOW/b3aJzcJRVIiKHpqfiYnODz1TEoYRFsZ5aNOZnLwkUk +OQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNV +HQ4EFgQUrWyqlGCc7eT/+j4KdCtjA/e2Wb8wDQYJKoZIhvcNAQELBQADggEBABpA +2JVlrAmSicY59BDlqQ5mU1143vokkbvnRFHfxhY0Cu9qRFHqKweKA3rD6z8KLFIW +oCtDuSWQP3CpMyVtRRooOyfPqsMpQhvfO0zAMzRbQYi/aytlryjvsvXDqmbOe1bu +t8jLZ8HJnBoYuMTDSQPxYA5QzUbF83d597YV4Djbxy8ooAw/dyZ02SUS2jHaGh7c +KUGRIjxpp7sC8rZcJwOJ9Abqm+RyguOhCcHpABnTPtRwa7pxpqpYrvS76Wy274fM +m7v/OeZWYdMKp8RcTGB7BXcmer/YB1IsYvdwY9k5vG8cwnncdimvzsUsZAReiDZu +MdRAGmI0Nj81Aa6sY6A= +-----END CERTIFICATE----- diff --git a/src/dep/dante_package/bundle/rootfs/lib/ld-linux-aarch64.so.1 b/src/dep/dante_package/bundle/rootfs/lib/ld-linux-aarch64.so.1 new file mode 100755 index 0000000..7967b95 Binary files /dev/null and b/src/dep/dante_package/bundle/rootfs/lib/ld-linux-aarch64.so.1 differ diff --git a/src/dep/dante_package/bundle/rootfs/lib/libc.so.6 b/src/dep/dante_package/bundle/rootfs/lib/libc.so.6 new file mode 100755 index 0000000..1b7e9cc Binary files /dev/null and b/src/dep/dante_package/bundle/rootfs/lib/libc.so.6 differ diff --git a/src/dep/dante_package/bundle/rootfs/lib/libgcc_s.so b/src/dep/dante_package/bundle/rootfs/lib/libgcc_s.so new file mode 100644 index 0000000..c8e9224 --- /dev/null +++ b/src/dep/dante_package/bundle/rootfs/lib/libgcc_s.so @@ -0,0 +1,4 @@ +/* GNU ld script + Use the shared library, but some functions are only in + the static library. */ +GROUP ( libgcc_s.so.1 -lgcc ) diff --git a/src/dep/dante_package/bundle/rootfs/lib/libgcc_s.so.1 b/src/dep/dante_package/bundle/rootfs/lib/libgcc_s.so.1 new file mode 100644 index 0000000..de0ec59 Binary files /dev/null and b/src/dep/dante_package/bundle/rootfs/lib/libgcc_s.so.1 differ diff --git a/src/dep/dante_package/bundle/rootfs/lib/libm.so.6 b/src/dep/dante_package/bundle/rootfs/lib/libm.so.6 new file mode 100755 index 0000000..b36bd73 Binary files /dev/null and b/src/dep/dante_package/bundle/rootfs/lib/libm.so.6 differ diff --git a/src/dep/dante_package/bundle/rootfs/lib64 b/src/dep/dante_package/bundle/rootfs/lib64 new file mode 120000 index 0000000..7951405 --- /dev/null +++ b/src/dep/dante_package/bundle/rootfs/lib64 @@ -0,0 +1 @@ +lib \ No newline at end of file diff --git a/src/dep/dante_package/bundle/rootfs/usr/bin/mksquashfs b/src/dep/dante_package/bundle/rootfs/usr/bin/mksquashfs new file mode 100755 index 0000000..4bfe95e Binary files /dev/null and b/src/dep/dante_package/bundle/rootfs/usr/bin/mksquashfs differ diff --git a/src/dep/dante_package/bundle/rootfs/usr/bin/unsquashfs b/src/dep/dante_package/bundle/rootfs/usr/bin/unsquashfs new file mode 100755 index 0000000..4b4414c Binary files /dev/null and b/src/dep/dante_package/bundle/rootfs/usr/bin/unsquashfs differ diff --git a/src/dep/dante_package/bundle/rootfs/usr/lib/libasound.so b/src/dep/dante_package/bundle/rootfs/usr/lib/libasound.so new file mode 120000 index 0000000..42dfc99 --- /dev/null +++ b/src/dep/dante_package/bundle/rootfs/usr/lib/libasound.so @@ -0,0 +1 @@ +libasound.so.2.0.0 \ No newline at end of file diff --git a/src/dep/dante_package/bundle/rootfs/usr/lib/libasound.so.2 b/src/dep/dante_package/bundle/rootfs/usr/lib/libasound.so.2 new file mode 120000 index 0000000..42dfc99 --- /dev/null +++ b/src/dep/dante_package/bundle/rootfs/usr/lib/libasound.so.2 @@ -0,0 +1 @@ +libasound.so.2.0.0 \ No newline at end of file diff --git a/src/dep/dante_package/bundle/rootfs/usr/lib/libasound.so.2.0.0 b/src/dep/dante_package/bundle/rootfs/usr/lib/libasound.so.2.0.0 new file mode 100755 index 0000000..042ff19 Binary files /dev/null and b/src/dep/dante_package/bundle/rootfs/usr/lib/libasound.so.2.0.0 differ diff --git a/src/dep/dante_package/bundle/rootfs/usr/lib/libstdc++.so b/src/dep/dante_package/bundle/rootfs/usr/lib/libstdc++.so new file mode 120000 index 0000000..7f77806 --- /dev/null +++ b/src/dep/dante_package/bundle/rootfs/usr/lib/libstdc++.so @@ -0,0 +1 @@ +libstdc++.so.6.0.28 \ No newline at end of file diff --git a/src/dep/dante_package/bundle/rootfs/usr/lib/libstdc++.so.6 b/src/dep/dante_package/bundle/rootfs/usr/lib/libstdc++.so.6 new file mode 120000 index 0000000..7f77806 --- /dev/null +++ b/src/dep/dante_package/bundle/rootfs/usr/lib/libstdc++.so.6 @@ -0,0 +1 @@ +libstdc++.so.6.0.28 \ No newline at end of file diff --git a/src/dep/dante_package/bundle/rootfs/usr/lib/libstdc++.so.6.0.28 b/src/dep/dante_package/bundle/rootfs/usr/lib/libstdc++.so.6.0.28 new file mode 100755 index 0000000..8f58d12 Binary files /dev/null and b/src/dep/dante_package/bundle/rootfs/usr/lib/libstdc++.so.6.0.28 differ diff --git a/src/dep/dante_package/bundle/rootfs/usr/lib/libz.so b/src/dep/dante_package/bundle/rootfs/usr/lib/libz.so new file mode 120000 index 0000000..95e0ebd --- /dev/null +++ b/src/dep/dante_package/bundle/rootfs/usr/lib/libz.so @@ -0,0 +1 @@ +libz.so.1.2.11 \ No newline at end of file diff --git a/src/dep/dante_package/bundle/rootfs/usr/lib/libz.so.1 b/src/dep/dante_package/bundle/rootfs/usr/lib/libz.so.1 new file mode 120000 index 0000000..95e0ebd --- /dev/null +++ b/src/dep/dante_package/bundle/rootfs/usr/lib/libz.so.1 @@ -0,0 +1 @@ +libz.so.1.2.11 \ No newline at end of file diff --git a/src/dep/dante_package/bundle/rootfs/usr/lib/libz.so.1.2.11 b/src/dep/dante_package/bundle/rootfs/usr/lib/libz.so.1.2.11 new file mode 100755 index 0000000..f3ce9f6 Binary files /dev/null and b/src/dep/dante_package/bundle/rootfs/usr/lib/libz.so.1.2.11 differ diff --git a/src/dep/dante_package/bundle/rootfs/usr/lib/os-release b/src/dep/dante_package/bundle/rootfs/usr/lib/os-release new file mode 100644 index 0000000..e02db84 --- /dev/null +++ b/src/dep/dante_package/bundle/rootfs/usr/lib/os-release @@ -0,0 +1,5 @@ +NAME=Buildroot +VERSION=2021.11 +ID=buildroot +VERSION_ID=2021.11 +PRETTY_NAME="Buildroot 2021.11" diff --git a/src/dep/dante_package/bundle/rootfs/usr/lib64 b/src/dep/dante_package/bundle/rootfs/usr/lib64 new file mode 120000 index 0000000..7951405 --- /dev/null +++ b/src/dep/dante_package/bundle/rootfs/usr/lib64 @@ -0,0 +1 @@ +lib \ No newline at end of file diff --git a/src/dep/dante_package/bundle/rootfs/usr/share/alsa/alsa.conf b/src/dep/dante_package/bundle/rootfs/usr/share/alsa/alsa.conf new file mode 100644 index 0000000..8d1c8b6 --- /dev/null +++ b/src/dep/dante_package/bundle/rootfs/usr/share/alsa/alsa.conf @@ -0,0 +1 @@ + diff --git a/src/dep/dante_package/bundle/rootfs/var/db/dbus/machine-id b/src/dep/dante_package/bundle/rootfs/var/db/dbus/machine-id new file mode 100644 index 0000000..8d1c8b6 --- /dev/null +++ b/src/dep/dante_package/bundle/rootfs/var/db/dbus/machine-id @@ -0,0 +1 @@ + diff --git a/src/dep/dante_package/bundle/rootfs/var/lib/dbus/machine-id b/src/dep/dante_package/bundle/rootfs/var/lib/dbus/machine-id new file mode 100644 index 0000000..8d1c8b6 --- /dev/null +++ b/src/dep/dante_package/bundle/rootfs/var/lib/dbus/machine-id @@ -0,0 +1 @@ + diff --git a/src/dep/dante_package/bundle/rootfs/var/log b/src/dep/dante_package/bundle/rootfs/var/log new file mode 120000 index 0000000..cad2309 --- /dev/null +++ b/src/dep/dante_package/bundle/rootfs/var/log @@ -0,0 +1 @@ +/tmp \ No newline at end of file diff --git a/src/dep/dante_package/crun b/src/dep/dante_package/crun new file mode 100755 index 0000000..4aea42d Binary files /dev/null and b/src/dep/dante_package/crun differ diff --git a/src/dep/dante_package/dante_data/capability/config.json b/src/dep/dante_package/dante_data/capability/config.json new file mode 100644 index 0000000..61666be --- /dev/null +++ b/src/dep/dante_package/dante_data/capability/config.json @@ -0,0 +1,320 @@ +{ + "ociVersion": "1.0.1", + "process": { + "terminal": false, + "user": { + "uid": 0, + "gid": 0 + }, + "args": [ + "./dep_manager", + "/dante_data/capability/dante.json" + ], + "env": [ + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/dante", + "TERM=xterm" + ], + "cwd": "/dante", + "capabilities": { + "bounding": [ + "CAP_CHOWN", + "CAP_DAC_OVERRIDE", + "CAP_FSETID", + "CAP_FOWNER", + "CAP_MKNOD", + "CAP_NET_RAW", + "CAP_SETGID", + "CAP_SETUID", + "CAP_SETFCAP", + "CAP_SETPCAP", + "CAP_NET_BIND_SERVICE", + "CAP_SYS_CHROOT", + "CAP_KILL", + "CAP_SYS_NICE", + "CAP_AUDIT_WRITE", + "CAP_NET_ADMIN" + ], + "effective": [ + "CAP_CHOWN", + "CAP_DAC_OVERRIDE", + "CAP_FSETID", + "CAP_FOWNER", + "CAP_MKNOD", + "CAP_NET_RAW", + "CAP_SETGID", + "CAP_SETUID", + "CAP_SETFCAP", + "CAP_SETPCAP", + "CAP_NET_BIND_SERVICE", + "CAP_SYS_CHROOT", + "CAP_KILL", + "CAP_SYS_NICE", + "CAP_AUDIT_WRITE", + "CAP_NET_ADMIN" + ], + "inheritable": [ + "CAP_CHOWN", + "CAP_DAC_OVERRIDE", + "CAP_FSETID", + "CAP_FOWNER", + "CAP_MKNOD", + "CAP_NET_RAW", + "CAP_SETGID", + "CAP_SETUID", + "CAP_SETFCAP", + "CAP_SETPCAP", + "CAP_NET_BIND_SERVICE", + "CAP_SYS_CHROOT", + "CAP_KILL", + "CAP_SYS_NICE", + "CAP_AUDIT_WRITE", + "CAP_NET_ADMIN" + ], + "permitted": [ + "CAP_CHOWN", + "CAP_DAC_OVERRIDE", + "CAP_FSETID", + "CAP_FOWNER", + "CAP_MKNOD", + "CAP_NET_RAW", + "CAP_SETGID", + "CAP_SETUID", + "CAP_SETFCAP", + "CAP_SETPCAP", + "CAP_NET_BIND_SERVICE", + "CAP_SYS_CHROOT", + "CAP_KILL", + "CAP_SYS_NICE", + "CAP_AUDIT_WRITE", + "CAP_NET_ADMIN" + ], + "ambient": [ + "CAP_CHOWN", + "CAP_DAC_OVERRIDE", + "CAP_FSETID", + "CAP_FOWNER", + "CAP_MKNOD", + "CAP_NET_RAW", + "CAP_SETGID", + "CAP_SETUID", + "CAP_SETFCAP", + "CAP_SETPCAP", + "CAP_NET_BIND_SERVICE", + "CAP_SYS_CHROOT", + "CAP_KILL", + "CAP_SYS_NICE", + "CAP_AUDIT_WRITE", + "CAP_NET_ADMIN" + ] + }, + "rlimits": [ + { + "type": "RLIMIT_NOFILE", + "hard": 1024, + "soft": 1024 + } + ], + "noNewPrivileges": true + }, + "root": { + "path": "rootfs", + "readonly": false + }, + "hostname": "", + "mounts": [ + { + "destination": "/proc", + "type": "proc", + "source": "proc" + }, + { + "destination": "/var/run", + "type": "tmpfs", + "source": "tmpfs", + "options": ["nosuid", "strictatime", "mode=755", "size=65536k"] + }, + { + "destination": "/var/run/dante", + "type": "bind", + "source": "/var/run/dante", + "options": ["bind", "rw"] + }, + { + "destination": "/var/lib/dbus/machine-id", + "type": "bind", + "source": "/var/lib/dbus/machine-id", + "options": ["ro", "rbind", "rprivate", "nosuid", "noexec", "nodev"] + }, + { + "destination": "/dev", + "type": "tmpfs", + "source": "tmpfs", + "options": ["nosuid", "strictatime", "mode=755", "size=65536k"] + }, + { + "destination": "/dev/pts", + "type": "devpts", + "source": "devpts", + "options": ["nosuid", "noexec", "newinstance", "ptmxmode=0666", "mode=0620", "gid=5"] + }, + { + "destination": "/dev/shm", + "type": "bind", + "source": "/dev/shm", + "options": ["bind", "rw"] + }, + { + "destination": "/dev/snd", + "type": "bind", + "source": "/dev/snd", + "options": ["bind", "rw"] + }, + { + "destination": "/tmp", + "type": "tmpfs", + "source": "tmpfs", + "options": ["nosuid", "strictatime", "mode=755", "size=65536k"] + }, + { + "destination": "/var/log", + "type": "bind", + "source": "/var/log", + "options": ["bind", "rw"] + }, + { + "destination": "/etc/machine-id", + "type": "bind", + "source": "/etc/machine-id", + "options": ["ro", "rbind", "rprivate", "nosuid", "noexec", "nodev"] + }, + { + "destination": "/etc/resolv.conf", + "type": "bind", + "source": "/etc/resolv.conf", + "options": ["ro", "rbind", "rprivate", "nosuid", "noexec", "nodev"] + }, + { + "destination": "/dante_data", + "type": "bind", + "source": "/home/caster/bumble-auracast/src/dep/dante_package/dante_data", + "options": ["bind", "rw"] + }, + { + "destination": "/dante_data/capability", + "type": "bind", + "source": "/home/caster/bumble-auracast/src/dep/dante_package/dante_data/capability", + "options": ["bind", "ro"] + }, + { + "destination": "/sys", + "type": "sysfs", + "source": "sysfs", + "options": ["nosuid", "noexec", "nodev", "ro"] + }, + { + "destination": "/sys/fs/cgroup", + "type": "cgroup2", + "source": "cgroup2", + "options": ["nosuid", "noexec", "nodev", "relatime", "ro"] + }, + { + "destination": "/usr/share/alsa/alsa.conf", + "type": "bind", + "source": "/usr/share/alsa/alsa.conf", + "options": ["bind", "ro"] + } + ], + "linux": { + "cgroupsPath": "dante", + "namespaces": [ + { "type": "pid" }, + { "type": "ipc" }, + { "type": "mount" }, + { "type": "uts" }, + { "type": "cgroup" } + ], + "devices": [ + { + "path": "/dev/ptp0", + "type": "c", + "major": 249, + "minor": 0, + "fileMode": 384, + "uid": 0, + "gid": 0 + }, + { + "path": "/dev/snd/pcmC3D0p", + "type": "c", + "major": 116, + "minor": 8, + "fileMode": 432, + "uid": 0, + "gid": 29 + }, + { + "path": "/dev/snd/pcmC3D0c", + "type": "c", + "major": 116, + "minor": 9, + "fileMode": 432, + "uid": 0, + "gid": 29 + }, + { + "path": "/dev/snd/pcmC3D1p", + "type": "c", + "major": 116, + "minor": 10, + "fileMode": 432, + "uid": 0, + "gid": 29 + }, + { + "path": "/dev/snd/pcmC3D1c", + "type": "c", + "major": 116, + "minor": 11, + "fileMode": 432, + "uid": 0, + "gid": 29 + }, + { + "path": "/dev/snd/controlC3", + "type": "c", + "major": 116, + "minor": 12, + "fileMode": 432, + "uid": 0, + "gid": 29 + } + ], + "maskedPaths": [ + "/proc/kcore", + "/proc/latency_stats", + "/proc/timer_list", + "/proc/timer_stats", + "/proc/sched_debug", + "/proc/scsi" + ], + "readonlyPaths": [ + "/proc/asound", + "/proc/bus", + "/proc/fs", + "/proc/irq", + "/proc/sys", + "/proc/sysrq-trigger" + ], + "resources":{ + "devices":[ + { + "allow": true, + "type": "c", + "major": 116, + "access": "rw" + } + ] + } + } +} diff --git a/src/dep/dante_package/dante_data/capability/dante.json b/src/dep/dante_package/dante_data/capability/dante.json new file mode 100644 index 0000000..9840222 --- /dev/null +++ b/src/dep/dante_package/dante_data/capability/dante.json @@ -0,0 +1,80 @@ +{ + "trialMode": true, + "$schema": "./dante.json_schema.json", + "platform": + { + "cgroupVersion": 2, + "logDirectory" : "/var/log" + }, + "audio" : + { + "txChannels" : 0, + "rxChannels" : 6, + "sampleRate" : 48000, + "availableSampleRates" : + [ + 48000 + ], + "samplesPerPeriod" : 16, + "periodsPerBuffer" : 300, + "networkLatencyMinMs" : 2, + "networkLatencyDefaultMs" : 5, + "supportedEncodings" : + [ + "PCM16" + ], + "defaultEncoding" : "PCM16", + "numDepCores" : 1 + }, + "network" : + { + "interfaceMode" : "Direct", + "interfaces" : + [ + "eth0" + ], + "preferredLinkSpeed" : "LINK_SPEED_100M" + }, + "clock" : + { + "enableHwTimestamping" : false + }, + "hardwareClock" : + { + "useHwClock" : false + }, + "hostcpu" : + { + "enableDdp" : false + }, + "alsaAsrc": + { + "enableAlsaAsrc": true, + "deviceConfigurations": [ + { + "deviceIdentifier": "hw:0,0", + "direction": "playback", + "bitDepth": 16, + "numOpenChannels": 6, + "alsaChannelRange": "0-5", + "danteChannelRange": "0-5", + "bufferSize": 4800, + "samplesPerPeriod": 16 + } + ] + }, + "product" : + { + "manfId" : "Audinate", + "manfName" : "Audinate Pty Ltd", + "modelId" : "OEMDEP", + "modelName" : "Linux Dante Embedded Platform", + "modelVersion" : + { + "major" : 9, + "minor" : 9, + "bugfix" : 99 + }, + "devicePrefix" : "DEP" + } +} diff --git a/src/dep/dante_package/dante_data/capability/dante.json_schema.json b/src/dep/dante_package/dante_data/capability/dante.json_schema.json new file mode 100644 index 0000000..6fc2330 --- /dev/null +++ b/src/dep/dante_package/dante_data/capability/dante.json_schema.json @@ -0,0 +1,734 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Schema for Dante Embedded Platform dante.json configuration file.", + "type": "object", + "properties": { + "$schema": { + "type": "string", + "description": "Removes superfluous warning on the schema field introduced by additionalProperties = false." + }, + "platform": { + "type": "object", + "properties": { + "cgroupVersion": { + "type": "integer", + "oneOf": [ + { + "enum": [1, 2] + } + ], + "description": "Tells DEP which version of cgroups the system is configured for. Setting this value allows DEP to optimise the system more appropriately. If this value is not set at all then DEP will still function but possibly in a less optimal way." + }, + "logDirectory": { + "type": "string", + "description": "Directory to write DEP log files. The directory must be available inside the container, DEP must have read/write access to the directory and the directory must exist before the DEP container is started." + }, + "maxNumLogs": { + "type": "integer", + "default": 6, + "description": "Maximum number of versions of each DEP log file to keep." + }, + "maxLogSize": { + "type": "integer", + "default": 102400, + "description": "Maximum number of bytes of each DEP log file." + }, + "logLevel": { + "type": "string", + "oneOf": [ + { + "enum": [ + "Error", + "Warning", + "Notice", + "Info", + "Debug" + ] + } + ], + "default": "Warning", + "description": "The minimum log level to write to the log files." + } + }, + "required": [ + "logDirectory" + ] + }, + "audio": { + "type": "object", + "properties": { + "rxChannels": { + "type": "integer", + "minimum": 0, + "maximum": 512, + "description": "The number of receive channels. Actual number of available channels will be the minimum of this value and the licensed channels." + }, + "txChannels": { + "type": "integer", + "minimum": 0, + "maximum": 512, + "description": "The number of transmit channels. Actual number of available channels will be the minimum of this value and the licensed channels." + }, + "maxRxFlows": { + "type": "integer", + "description": "Maximum receive flows that DEP will allow. Default is 'MAX(2, (rxChannels + 1) / 2)'." + }, + "maxTxFlows": { + "type": "integer", + "description": "Maximum transmit flows that DEP will allow. Default is 'MAX(2, (txChannels + 1) / 2)'." + }, + "sampleRate": { + "type": "integer", + "oneOf": [ + { + "enum": [ + 44100, + 48000, + 88200, + 96000 + ] + } + ], + "default": 48000, + "description": "Default sample rate of DEP." + }, + "availableSampleRates": { + "type": "array", + "minItems": 1, + "items": { + "type": "integer", + "oneOf": [ + { + "enum": [ + 44100, + 48000, + 88200, + 96000 + ] + } + ] + }, + "default": [ + 44100, + 48000, + 88200, + 96000 + ], + "contains": { + "const": 48000 + }, + "uniqueItems": true, + "description": "A list of sample rates that can be selected for the DEP device." + }, + "samplesPerPeriod": { + "type": "integer", + "default": 16, + "description": "The number of samples between audio period events (ticks)." + }, + "periodsPerBuffer": { + "type": "integer", + "default": 3000, + "description": "The number of periods in the buffer." + }, + "networkLatencyMinMs": { + "type": "integer", + "oneOf": [ + { + "enum": [ + 1, + 2, + 3, + 4, + 5, + 10 + ] + } + ], + "default": 2, + "description": "The minimum latency in milliseconds (ms) that this device can support." + }, + "networkLatencyDefaultMs": { + "type": "integer", + "minimum": 1, + "maximum": 40, + "default": 4, + "description": "Default network latency in milliseconds (ms)." + }, + "numDepCores": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "integer", + "minimum": 0 + }, + "minItems": 1, + "uniqueItems": true, + "description": "List of CPU core IDs DEP will run on." + }, + { + "type": "integer", + "minimum": 0, + "description": "The number of CPU cores DEP will run on. DEP will run on the given number of consecutive cores starting from core 0" + } + ], + "description": "The CPU cores DEP will run on. The host system is responsible for ensuring the configured cores are isolated exclusively for DEP use." + }, + "percentCpuShare": { + "type": "integer", + "minimum": 1, + "maximum": 100, + "default": 100, + "description": "The share of CPU time DEP will be allocated when CPU time is under contention. NOTE: this setting has no effect when cgroups v2 is in use and will be deprecated soon." + }, + "defaultEncoding": { + "type": "string", + "oneOf": [ + { + "enum": [ + "PCM16", + "PCM24", + "PCM32" + ] + } + ], + "description": "(DEPRECATED) The default native device encoding value. This field should no longer be used. The default should instead be specified as the first entry in supportedEncodings" + }, + "supportedEncodings": { + "type": "array", + "items": { + "type": "string", + "oneOf": [ + { + "enum": [ + "PCM16", + "PCM24", + "PCM32" + ] + } + ] + }, + "default": [ + "PCM24" + ], + "uniqueItems": true, + "description": "A list of supported native device encoding values." + }, + "aes67Supported": { + "type": "boolean", + "default": false, + "description": "Whether this device supports the AES67 protocol." + }, + "channelGroupsFile": { + "type": "string", + "description": "The full path to a separate JSON file that specifies logical channel groupings." + }, + "defaultChannelNamesFile": { + "type": "string", + "description": "The full path to a separate JSON file that specifies custom default channel names." + }, + "perChannelEncodingsFile": { + "type": "string", + "description": "The full path to a separate JSON file that specifies per-channel encodings." + }, + "enableSelfSubscription": { + "type": "boolean", + "default": true, + "description": "Whether the device self-subscription capability should be enabled." + }, + "silenceHeadDelayMs" : { + "type": "integer", + "default": 20, + "description": "DEP erases audio in the rx and tx audio buffer shortly after the network time for those frames passes. This controls the delay on this erasure measured in milliseconds." + } + }, + "required": [ + "txChannels", + "rxChannels", + "availableSampleRates", + "numDepCores" + ] + }, + "network": { + "type": "object", + "properties": { + "interfaceMode": { + "type": "string", + "oneOf": [ + { + "enum": [ + "Switched", + "Direct" + ] + } + ], + "default": "Direct", + "description": "DEP network interface mode. Direct means connected to the network via a PHY; Switched means connected to the network via a switch." + }, + "interfaces": { + "type": "array", + "items": { + "anyOf": [ + { "type": "string" }, + { "type": "integer" } + ] + }, + "minItems": 1, + "maxItems": 2, + "uniqueItems": true, + "description": "List of network interface names or indexes DEP will use to connect to the Dante network." + }, + "preferredLinkSpeed": { + "type": "string", + "oneOf": [ + { + "enum": [ + "LINK_SPEED_10G", + "LINK_SPEED_1G", + "LINK_SPEED_100M" + ] + } + ], + "default": "LINK_SPEED_1G", + "description": "The preferred link speed of the network interface/s used by DEP." + }, + "webSocketPort": { + "type": "integer", + "minimum": 1024, + "maximum": 65535, + "description": "The websocket port used by DEP. If not set an ephemeral port is used." + } + }, + "required": [ + "interfaces" + ] + }, + "mdns": { + "type": "object", + "properties": { + "restrictInterfaces": { + "type": "boolean", + "default": true, + "description": "Whether to restrict mDNS advertisements to only the specified network interfaces." + } + } + }, + "clock": { + "type": "object", + "properties": { + "enableHwTimestamping": { + "anyOf": [ + { + "type": "boolean" + }, + { + "const": "v1" + } + ], + "default": false, + "description": "Whether to use hardware packet timestamping at the Network Interface Card (NIC) level." + }, + "dsaTaggedPackets": { + "type": "boolean", + "default": false, + "description": "Whether packets read from the network interface have a DSA tag attached." + }, + "hardwareInterfaces": { + "type": "array", + "items": { + "anyOf": [ + { "type": "string" }, + { "type": "integer" } + ] + }, + "minItems": 1, + "maxItems": 2, + "description": "List of network interface names or indexes that support hardware packet timestamping." + }, + "followerOnly": { + "type": "boolean", + "default": false, + "description": "Whether the device should be in follower only mode. When true, DEP cannot become clock leader." + } + }, + "if": { + "properties": { + "enableHwTimestamping": { + "anyOf": [ + { "const": true }, + { "const": "v1" } + ] + }, + "dsaTaggedPackets": { "const": true } + }, + "required": [ + "enableHwTimestamping", "dsaTaggedPackets" + ] + }, + "then": { + "required": [ + "hardwareInterfaces" + ] + } + }, + "hardwareClock": { + "type": "object", + "properties": { + "useHwClock": { + "type": "boolean", + "default": false, + "description": "Enable use of clocking hardware." + }, + "circuitName": { + "type": "string", + "description": "Name of the clock generator and adjustment circuitry. This field must be one of the supported strings in a DEP release." + }, + "circuitRevision": { + "type": "integer", + "description": "An integer representing the circuit revision to use. This field must correspond to a supported revision and circuit in a DEP release." + }, + "i2cBus": { + "type": "string", + "default": "/dev/i2c-0", + "description": "The I2C bus device to use to communicate with the clock circuitry. If not present, the first I2C bus device '/dev/i2c-0' is used.", + "pattern": "^\\/dev" + }, + "i2cAddr": { + "type": "string", + "description": "The I2C address configurable for a circuit. If not present, the default addresses for the circuit are used." + }, + "extClockInputDev": { + "type": "string", + "default": "/dev/extclkin", + "description": "The device path to the external clock input driver used in the clock feedback algorithm. If not present, this field defaults to '/dev/extclkin'.", + "pattern": "^\\/dev" + }, + "bitClocks": { + "type": "array", + "description": "An array of mappings between the sample rate and bit clock configurations.", + "items": { + "type": "object", + "properties": { + "sampleRate": { + "type": "integer", + "description": "Sample rate of the mapping." + }, + "tdmChannels": { + "type": "integer", + "description": "Number of TDM channels." + }, + "bitDepth": { + "type": "integer", + "description": "Bit depth of mapping." + } + }, + "required": [ + "tdmChannels", + "bitDepth" + ] + } + }, + "loadCapacitance": { + "type": "integer", + "default": -1, + "description": "Value for the internal load capacitance in pf to set for the clock circuit. If not set or set to a negative number the circuitโ€™s default will be used. The default and set of valid values are clock circuit specific. For DEP supported si5351b based clock circuits the default load capacitance is 10pF and the set of valid values for this field are 6, 8 and 10." + } + }, + "if": { + "properties": { + "useHwClock": { "const": true } + }, + "required": ["useHwClock"] + }, + "then": { + "required": ["circuitName"] + } + }, + "hostcpu": { + "type": "object", + "properties": { + "enableDdp": { + "type": "boolean", + "default": false, + "description": "Enable the 'Dante Device Protocol'." + } + }, + "required": ["enableDdp"] + }, + "alsaAsrc": { + "type": "object", + "properties": { + "enableAlsaAsrc": { + "type": "boolean", + "description": "Set to true to enable ALSA ASRC and false to disable." + }, + "txLatencySamples": { + "type": "integer", + "default": 48, + "description": "Offset used by ASRC when writing audio to the DEP TX buffer measured in samples." + }, + "pollMode": { + "type": "boolean", + "default": false, + "description": "If true, ALSA ASRC will not wait on the DEP shared memory semaphore and will instead poll the memory to determine when more data is available." + }, + "schedulingPriority": { + "type": "integer", + "default": 70, + "minimum": 0, + "maximum": 100, + "description": "The real-time scheduling priority to run the ALSA ASRC application at." + }, + "cpuAffinity": { + "type": "integer", + "minimum": 0, + "description": "The CPU core ID which should be exclusively assigned to ASRC. NOTE: for optimal performance, ensure that the selected CPU core ID is not already listed in the numDepCores value" + }, + "deviceConfigurations": { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "description": "List of devices to open. This key is required if Asrc is enabled.", + "items": { + "allOf": [ + { + "type": "object", + "description": "Configuration options for each ALSA device to be opened", + "properties": { + "deviceIdentifier": { + "type": "string", + "description": "The ALSA device identifier for this device, e.g. \"hw:1,0\" or \"hw:CARD=sofhdadsp,DEV=0\"." + }, + "direction": { + "enum": [ + "playback", + "capture" + ], + "description": "The direction to open the ALSA device in. Must be \"capture\" or \"playback\"." + }, + "bitDepth": { + "enum": [ + 16, + 24, + 32 + ], + "default": 24, + "description": "The PCM bit depth to open the ALSA device with. The device will be opened with the first format it claims to support which is that depth. Typically this maps 8 to S8, 16 to S16_LE, 24 to S24_LE and 32 to S32_LE." + }, + "bitWidthOverride": { + "enum": [ + 16, + 24, + 32 + ], + "description": "The number of bits each sample is packed into. For example, \"bitDepth\": 24, \"bitWidthOverride\": 24 is equivalent to S24_3LE, so the application writes samples aligned to 3 bytes. This overrides the alignment of the selected format. So if, for example, a device only claims to support S24_LE (24 bits aligned to 32 bit words) but actually writes 24 bit samples aligned to 24 bits, this setting can account for this." + }, + "alsaFormat": { + "enum": [ + "S16_LE", + "S24_LE", + "S32_LE", + "FLOAT_LE", + "S24_3LE", + "S16", + "S24", + "S32", + "FLOAT" + ], + "description": "The specific ALSA format name to open the device with. Incompatible with bitDepth." + }, + "numOpenChannels": { + "type": "integer", + "minimum": 1, + "default": 2, + "description": "The number of channels to open on the ALSA device." + }, + "alsaChannelRange": { + "type": "string", + "pattern": "^[0-9]+-[0-9]+$", + "description": "The block of ALSA channels to use. Can only be provided if numOpenChannels is specified. String of the form \"X-Y\" where X and Y are zero indexed channel numbers, specifying the block [X,Y] inclusive. Defaults to 0-(numOpenChannels - 1)" + }, + "danteChannelRange": { + "type": "string", + "pattern": "^[0-9]+-[0-9]+$", + "description": "The block of DEP channels this device will read from or write to. Can only be provided if numOpenChannels is specified. String of the form \"X-Y\" where X and Y are zero indexed channel numbers, specifying the block [X,Y] inclusive. Defaults to 0-(numOpenChannels-1)" + }, + "gain": { + "type": "integer", + "default": 0, + "description": "Positive or negative gain in dB to apply to the audio for this device." + }, + "bufferSize": { + "type": "integer", + "default": 64, + "description": "Size of the ALSA buffer to request the device to open with. Should never be less than 2 DEP periods or 2 ALSA periods. Note that the exact numbers for bufferSize and samplesPerPeriod are merely a request, and individual ALSA drivers are entitled to find other nearby valid values, if necessary." + }, + "samplesPerPeriod": { + "type": "integer", + "default": 8, + "description": "Samples per period to request the ALSA device to open with. Note that the exact numbers for bufferSize and samplesPerPeriod are merely a request, and individual ALSA drivers are entitled to find other nearby valid values, if necessary." + }, + "latency": { + "type": "integer", + "description": "By default, ASRC maintains the ALSA buffer at its halfway point - which corresponds to the insertion latency of ASRC. This key overrides this behaviour, specifying the target buffer point in samples." + }, + "readWriteiBuffer": { + "type": "integer", + "description": "For drivers that don't support MMAP (memory-mapped) buffer operations, the application can emulate the memory mapping internally by inserting an additional buffer and services that through ALSA R/W calls. If this value is >0, it specifies the size of this additional buffer." + }, + "forceArtificialAudioTime": { + "type": "boolean", + "description": "This setting provides an override for drivers which don't provide correct audio timestamps. If this is set to true, ASRC overrides the audio time with an artificial one calculated from sample counts." + } + }, + "required": [ + "deviceIdentifier", + "direction" + ] + }, + { + "description": "The alsaFormat option is incompatible with the bitDepth and bitWidthOverride option", + "if": { + "anyOf": [ + {"required": ["bitDepth"]}, + {"required": ["bitWidthOverride"]} + ] + }, + "then": { + "not": {"required": ["alsaFormat"]} + } + }, + { + "description": "alsaChannelRange and danteChannelRange can each only be defined if numOpenChannels is specified", + "if": { + "anyOf": [ + {"required": ["alsaChannelRange"]}, + {"required": ["danteChannelRange"]} + ] + }, + "then": { + "required": ["numOpenChannels"] + } + } + ] + } + } + }, + "required": [ + "enableAlsaAsrc" + ] + }, + "product": { + "type": "object", + "properties": { + "manfId": { + "type": "string", + "minLength": 1, + "maxLength": 8, + "description": "The ID of the device manufacturer. This value is assigned to the manufacturer by Audinate when signing up as a DEP licensee." + }, + "manfName": { + "type": "string", + "minLength": 1, + "maxLength": 31, + "description": "Human-readable manufacturer name that users will see in Dante Controller." + }, + "modelId": { + "type": "string", + "minLength": 1, + "maxLength": 8, + "description": "The device model ID, up to 8 characters long and unique for each product type produced by a manufacturer." + }, + "modelName": { + "type": "string", + "minLength": 1, + "maxLength": 31, + "description": "Human-readable model name that users will see in Dante Controller." + }, + "modelVersion": { + "type": "object", + "description": "3-part version number of the DEP device, this will be shown in Dante Controller.", + "properties": { + "major": { + "type": "integer", + "description": "Product version major number." + }, + "minor": { + "type": "integer", + "description": "Product version minor number." + }, + "bugfix": { + "type": "integer", + "description": "Product version bugfix number." + } + }, + "required": [ + "major", + "minor", + "bugfix" + ] + }, + "modelVersionString": { + "type": "string", + "description": "An arbitrary string that overrides the 'modelVersion'. If not set the 'modelVersion' fields will be used to construct a model version string.", + "minLength": 1, + "maxLength": 31 + }, + "devicePrefix": { + "type": "string", + "default": "DEP", + "minLength": 1, + "maxLength": 24, + "pattern": "^[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,23})?$", + "description": "Dante device name prefix. Up to 24 characters, legal characters are A-Z, a-z, 0-9, and '-' ('-' cannot be the first character)." + } + }, + "required": [ + "manfId", + "manfName", + "modelId", + "modelName", + "modelVersion" + ] + }, + "trialMode": { + "type": "boolean", + "default": false, + "description": "Set to true to start the container in 'Trial Mode'. If excluded or false, DEP will require activation." + }, + "misc": { + "type": "object", + "properties": { + "enableIdentify": { + "type": "boolean", + "default": false, + "description": "Set to true to enable the device 'Identify' function and false to disable." + } + } + }, + "ddhi": { + "type": "object", + "properties": { + "enable": { + "type": "boolean", + "default": true, + "description": "Set to true to enable Dante Device Host Interface (DDHI) and false to disable. All other properties in the ddhi object are only used if this value is true." + }, + "clientRpcs": { + "type": "array", + "items": { + "type": "string" + }, + "description": "List of DDHI RPCs supported by the platform DDHI client(s)" + } + } + } + }, + "required": [ + "platform", + "audio", + "network", + "product" + ], + "additionalProperties" : false +} diff --git a/src/dep/dante_package/dante_data/config/apec.conf b/src/dep/dante_package/dante_data/config/apec.conf new file mode 100644 index 0000000..0f02a67 Binary files /dev/null and b/src/dep/dante_package/dante_data/config/apec.conf differ diff --git a/src/dep/dante_package/dante_data/config/manager.conf b/src/dep/dante_package/dante_data/config/manager.conf new file mode 100644 index 0000000..76b271b --- /dev/null +++ b/src/dep/dante_package/dante_data/config/manager.conf @@ -0,0 +1,3 @@ +{ + "sampleRate" : 48000 +} \ No newline at end of file diff --git a/src/dep/dante_package/dante_data/config/ptp.conf b/src/dep/dante_package/dante_data/config/ptp.conf new file mode 100644 index 0000000..d163a16 --- /dev/null +++ b/src/dep/dante_package/dante_data/config/ptp.conf @@ -0,0 +1,7 @@ +!preferred +subdomain_name _DFLT +mport +socket_loglevel 3 +port_v1_m_p +!port_v2_u_p +!enrolled diff --git a/src/dep/dante_package/dante_data/config/uuid b/src/dep/dante_package/dante_data/config/uuid new file mode 100644 index 0000000..c72a541 --- /dev/null +++ b/src/dep/dante_package/dante_data/config/uuid @@ -0,0 +1 @@ +e—|ฦ%J‘NŸ˜6w7 \ No newline at end of file diff --git a/src/dep/dante_package/dante_data/images/0/rootfs_squash b/src/dep/dante_package/dante_data/images/0/rootfs_squash new file mode 100644 index 0000000..75e7f80 Binary files /dev/null and b/src/dep/dante_package/dante_data/images/0/rootfs_squash differ diff --git a/src/dep/dante_package/dante_data/images/active b/src/dep/dante_package/dante_data/images/active new file mode 100644 index 0000000..573541a --- /dev/null +++ b/src/dep/dante_package/dante_data/images/active @@ -0,0 +1 @@ +0 diff --git a/src/dep/dante_package/dep.sh b/src/dep/dante_package/dep.sh new file mode 100755 index 0000000..8dfae0b --- /dev/null +++ b/src/dep/dante_package/dep.sh @@ -0,0 +1,280 @@ +#!/bin/sh + +# To use a different OCI-compliant container runtime, +# update both CONTAINER_RUNTIME and CONTAINER_RUNTIME_PATH: +# +# - CONTAINER_RUNTIME should be set to the name of the runtime binary (e.g., 'crun', 'runc'). +# - CONTAINER_RUNTIME_PATH should point to the directory where the binary is installed +# (e.g., '/usr/bin' for a system-installed runtime). + +CONTAINER_RUNTIME_PATH=$PWD +CONTAINER_RUNTIME=crun +CONTAINER_STATUS_PATH=/run/$CONTAINER_RUNTIME +CONTAINER_CMD="$CONTAINER_RUNTIME_PATH/$CONTAINER_RUNTIME --root=$CONTAINER_STATUS_PATH" +CONTAINER_CMD_ADDITIONAL_OPTIONS="" +CONTAINER_LOGS="/var/log/dante_container.log" + +IMAGES_PATH=$PWD/dante_data/images +ROOTFS_MOUNTPOINT=$PWD/bundle/rootfs +ACTIVE_IMAGE_ID_PATH=$IMAGES_PATH/active +DANTE_JSON=$PWD/dante_data/capability/dante.json + +# Check we can actually start/stop containers +# NOTE: on some systems 'id' might not be available, hence we check manually +if [ "$(grep -E '^Uid:' /proc/self/status | awk '{print $2}')" -ne 0 ]; then + echo "This script must be executed with root privileges." + echo "" + exit 1 +fi + +# This function assumes that: +# - the JSON file is well-formed +# - key and value appear on the same line +# - strings are double-quoted and donโ€™t contain escaped quotes +# - assumes the key exists exactly once per line +get_json_field() +{ + json_file=$1 + field_name=$2 + default="__NOT_FOUND__" + + if [ ! -f "$json_file" ]; then + echo "error: file '$json_file' not found" >&2 + exit 1 + fi + + # explaining each sed: + # - 's/^[^:]*://' removes everything up to and including the first colon + # - 's/ //' removes the first space character after the colon, if present + # - 's/^"//' and 's/"$//' removes leading and trailing double quotes from the value + # - 's/,[[:space:]]*$//' removes a trailing comma and any following whitespace (e.g. to handle lists) + # - 's/[[:space:]]*$//' trims any remaining trailing whitespace from the value + value=$(grep "\"$field_name\"" "$json_file" | \ + sed -e 's/^[^:]*://' -e 's/ //' | \ + sed -e 's/^"//' -e 's/"$//' | \ + sed -e 's/,[[:space:]]*$//' | \ + sed -e 's/[[:space:]]*$//' | head -n 1) + + if [ -z "$value" ]; then + echo "$default" + else + echo "$value" + fi +} + +check_cgroup_mounts() +{ + cgroup_version=$(get_json_field "$DANTE_JSON" "cgroupVersion") + + if [ "$cgroup_version" = "__NOT_FOUND__" ]; then + return + fi + + check_mount() { + path=$1 + expectedType=$2 + + while IFS= read -r line; do + # get the second field (mount point) and the third field (type) + mountPoint=$(echo "$line" | awk '{print $2}') + mountType=$(echo "$line" | awk '{print $3}') + + # if the mountPoint doesn't start with /sys/fs/cgroup, skip it + case "$mountPoint" in + /sys/fs/cgroup*) ;; + *) continue ;; + esac + + # if mount point and type exactly match the expected values, we're good + if [ "$mountPoint" = "$path" ] && [ "$mountType" = "$expectedType" ]; then + echo "mount OK: $path ($expectedType)" + return + fi + + # There is a chance multiple controllers are mounted on the same path, + # for instance we might be looking for /sys/fs/cgroup/cpu and have + # + # /sys/fs/cgroup/cpu,cpuacct cgroup etc.. + # + # mounted instead. + + # because we skip entries that do not start with /sys/fs/cgroup at + # the beginning of the loop, we know getting the substring after + # /sys/fs/cgroup at this point will yield an empty string at worst + cgroupSubstring=${mountPoint#/sys/fs/cgroup/} + + # do the same with $path + cgroupPathSubstring=${path#/sys/fs/cgroup/} + + # check if cgroupPathSubstring is part of cgroupSubstring + # eg this would successfully match 'cpu' against both 'cpuacct,cpu' and 'cpu,cpuacct' + if echo "$cgroupSubstring" | grep -qw "$cgroupPathSubstring"; then + if [ "$mountType" = "$expectedType" ]; then + echo "mount OK: $path ($expectedType)" + return + fi + fi + done < /proc/mounts + + echo "warning: missing or incorrect mountpoint: $path (expected type: $expectedType)" + } + + if [ "$cgroup_version" = "1" ]; then + echo "cgroup version set to v1 in $DANTE_JSON" + echo "checking mounts..." + check_mount "/sys/fs/cgroup" "tmpfs" + check_mount "/sys/fs/cgroup/cpuset" "cgroup" + check_mount "/sys/fs/cgroup/cpu" "cgroup" + check_mount "/sys/fs/cgroup/memory" "cgroup" + check_mount "/sys/fs/cgroup/devices" "cgroup" + elif [ "$cgroup_version" = "2" ]; then + echo "cgroup version set to v2 in $DANTE_JSON" + echo "checking mounts..." + check_mount "/sys/fs/cgroup" "cgroup2" + else + echo "error: unsupported cgroupVersion value ($cgroup_version) in $DANTE_JSON" + exit 1 + fi +} + +start() +{ + # A poorly-timed stop() could leave the container mounted while + # the processes inside the container were successfully shut down. + # Instead of relying on whether the container is there or not when + # deciding to start DEP, check whether dep_manager is actually running. + + # shellcheck disable=SC2009 + # (SC2009 recommends using pgrep, but it is not always available) + if ps -e >/dev/null 2>&1; then + PS_CMD="ps -e" + else + PS_CMD="ps" # assume heavily stripped-down BusyBox + fi + + if $PS_CMD | grep -q "[d]ep_manager"; then + echo "DEP is already running" + exit 0 + fi + + # Some basic checks before proceeding + if [ ! -f "$ACTIVE_IMAGE_ID_PATH" ]; then + echo "error: $ACTIVE_IMAGE_ID_PATH not found, can't select active rootfs" + exit 1 + fi + + active_image_id=$(cat "$ACTIVE_IMAGE_ID_PATH") + rootfs="$IMAGES_PATH/$active_image_id/rootfs_squash" + + if [ ! -f "$rootfs" ]; then + echo "error: $rootfs not found" + exit 1 + fi + + check_cgroup_mounts + + mkdir -p /var/run/dante + mkdir -p ${CONTAINER_STATUS_PATH} + + # Make sure /etc/resolv.conf is there when later on we + # try to bind mount it from the container. + if [ ! -f "/etc/resolv.conf" ]; then + touch /etc/resolv.conf + fi + + if ! grep -q " $ROOTFS_MOUNTPOINT " /proc/mounts; then + if ! mount "$rootfs" "$ROOTFS_MOUNTPOINT" >/dev/null 2>&1; then + echo "error: could not mount $rootfs" + exit 1 + fi + fi + + # At this point, it's safe to always forcefully delete the container. + # + # This may be necessary in scenarios where the DEP processes did not actually + # start after running ./dep.sh start โ€” for example, due to invalid configuration + # in dante.json. In such cases, a user would typically inspect the logs, + # fix the underlying issue, and then retry with ./dep.sh start. + # + # However, if the dante container remains mounted, the container runtime's 'run' + # command will fail, forcing the user to manually delete the container - either + # by using ./dep.sh stop (which is not intuitive) or manually. + # + # To avoid these issues and make the recovery easier to execute, unconditionally + # remove the dante container before attempting to run it again. + # + # NOTE: while we could check whether the container exists before removing it, + # not all systems provide the necessary cgroup status layers to reliably list + # configured containers. + ${CONTAINER_CMD} delete --force dante + + # rootfs (only mount with no parent mount) cannot be pivot_root()ed. The check hereafter + # relies on the fact that rootfs will be either a ramfs or tmpfs. This is a bit more restrictive + # than necessary, as the container could in practice be started from a ramfs or tmpfs (as long as + # it is not the rootfs). + # WARNING: crun falls back to chroot when --no-pivot is enabled, and a process running in the container + # can in practice access the tree outside of the chroot. + ROOT_FSTYPE=$(mount|grep 'on / type'|awk '{print $5}') + if [ "$ROOT_FSTYPE" = "rootfs" ] || [ "$ROOT_FSTYPE" = "ramfs" ] || [ "$ROOT_FSTYPE" = "tmpfs" ]; then + CONTAINER_CMD_ADDITIONAL_OPTIONS="$CONTAINER_CMD_ADDITIONAL_OPTIONS --no-pivot" + fi + + if ! ${CONTAINER_CMD} run ${CONTAINER_CMD_ADDITIONAL_OPTIONS} --detach --bundle ./bundle dante > "$CONTAINER_LOGS" 2>&1; then + echo "error: failed to start dante container, more details available in $CONTAINER_LOGS" + exit 1 + else + echo "DEP started" + fi +} + +stop() +{ + # in some cases we might have the mountpoint but no container running: + # check if that's the case before proceeding + if ${CONTAINER_CMD} list | grep dante >/dev/null 2>&1; then + # stop the init process (dep_manager) by sending a SIGTERM signal + echo "stopping DEP..." + ${CONTAINER_CMD} kill dante TERM + + for _ in $(seq 1 10); do + sleep 1 + DEP_PROCS=$(${CONTAINER_CMD} ps dante | grep -v PID -c) + if [ "$DEP_PROCS" -eq 0 ]; then + break + fi + done + + DEP_PROCS=$(${CONTAINER_CMD} ps dante | grep -v PID -c) + if [ "$DEP_PROCS" -ne 0 ]; then + echo "DEP still running, sending SIGKILL" + ${CONTAINER_CMD} kill -a dante KILL + sleep 1 + fi + + echo "removing container..." + ${CONTAINER_CMD} delete --force dante + fi + + if grep -q " $ROOTFS_MOUNTPOINT " /proc/mounts; then + echo "umount rootfs..." + umount "$PWD"/bundle/rootfs + fi + + echo "done" +} + +USAGE_MESSAGE="Usage: dep.sh " + +if [ "$#" -eq 0 ]; then + echo "$USAGE_MESSAGE" + exit 1 +fi + +case $1 in + "start" ) start "$2" ;; + "stop" ) stop ;; + * ) + echo "$USAGE_MESSAGE" + exit 1 + ;; +esac diff --git a/src/dep/dante_package/dep_check.sh b/src/dep/dante_package/dep_check.sh new file mode 120000 index 0000000..5322983 --- /dev/null +++ b/src/dep/dante_package/dep_check.sh @@ -0,0 +1 @@ +development/dep_check.sh \ No newline at end of file diff --git a/src/dep/dante_package/dep_support_collection.sh b/src/dep/dante_package/dep_support_collection.sh new file mode 100755 index 0000000..28eb902 --- /dev/null +++ b/src/dep/dante_package/dep_support_collection.sh @@ -0,0 +1,556 @@ +#!/bin/sh + +# This script collects all the necessary information/files for support, then bundle them into a single .tgz file. + +# Copyright ยฉ 2022-2025 Audinate Pty Ltd ACN 120 828 006 (Audinate). All rights reserved. +# +# +# 1. Subject to the terms and conditions of this Licence, Audinate hereby grants you a worldwide, non-exclusive, +# no-charge, royalty free licence to copy, modify, merge, publish, redistribute, sublicense, and/or sell the +# Software, provided always that the following conditions are met: +# 1.1. the Software must accompany, or be incorporated in a licensed Audinate product, solution or offering +# or be used in a product, solution or offering which requires the use of another licensed Audinate +# product, solution or offering. The Software is not for use as a standalone product without any +# reference to Audinate's products; +# 1.2. the Software is provided as part of example code and as guidance material only without any warranty +# or expectation of performance, compatibility, support, updates or security; and +# 1.3. the above copyright notice and this License must be included in all copies or substantial portions +# of the Software, and all derivative works of the Software, unless the copies or derivative works are +# solely in the form of machine-executable object code generated by the source language processor. +# +# 2. TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +# PURPOSE AND NONINFRINGEMENT. +# +# 3. TO THE FULLEST EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT SHALL AUDINATE BE LIABLE ON ANY LEGAL THEORY +# (INCLUDING, WITHOUT LIMITATION, IN AN ACTION FOR BREACH OF CONTRACT, NEGLIGENCE OR OTHERWISE) FOR ANY CLAIM, +# LOSS, DAMAGES OR OTHER LIABILITY HOWSOEVER INCURRED. WITHOUT LIMITING THE SCOPE OF THE PREVIOUS SENTENCE THE +# EXCLUSION OF LIABILITY SHALL INCLUDE: LOSS OF PRODUCTION OR OPERATION TIME, LOSS, DAMAGE OR CORRUPTION OF +# DATA OR RECORDS; OR LOSS OF ANTICIPATED SAVINGS, OPPORTUNITY, REVENUE, PROFIT OR GOODWILL, OR OTHER ECONOMIC +# LOSS; OR ANY SPECIAL, INCIDENTAL, INDIRECT, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES, ARISING OUT OF OR +# IN CONNECTION WITH THIS AGREEMENT, ACCESS OF THE SOFTWARE OR ANY OTHER DEALINGS WITH THE SOFTWARE, EVEN IF +# AUDINATE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH CLAIM, LOSS, DAMAGES OR OTHER LIABILITY. +# +# 4. APPLICABLE LEGISLATION SUCH AS THE AUSTRALIAN CONSUMER LAW MAY APPLY REPRESENTATIONS, WARRANTIES, OR CONDITIONS, +# OR IMPOSES OBLIGATIONS OR LIABILITY ON AUDINATE THAT CANNOT BE EXCLUDED, RESTRICTED OR MODIFIED TO THE FULL +# EXTENT SET OUT IN THE EXPRESS TERMS OF THIS CLAUSE ABOVE "CONSUMER GUARANTEES". TO THE EXTENT THAT SUCH CONSUMER +# GUARANTEES CONTINUE TO APPLY, THEN TO THE FULL EXTENT PERMITTED BY THE APPLICABLE LEGISLATION, THE LIABILITY OF +# AUDINATE UNDER THE RELEVANT CONSUMER GUARANTEE IS LIMITED (WHERE PERMITTED AT AUDINATE'S OPTION) TO ONE OF +# FOLLOWING REMEDIES OR SUBSTANTIALLY EQUIVALENT REMEDIES: +# 4.1. THE REPLACEMENT OF THE SOFTWARE, THE SUPPLY OF EQUIVALENT SOFTWARE, OR SUPPLYING RELEVANT SERVICES AGAIN; +# 4.2. THE REPAIR OF THE SOFTWARE; +# 4.3. THE PAYMENT OF THE COST OF REPLACING THE SOFTWARE, OF ACQUIRING EQUIVALENT SOFTWARE, HAVING THE RELEVANT +# SERVICES SUPPLIED AGAIN, OR HAVING THE SOFTWARE REPAIRED. +# +# 5. This License does not grant any permissions or rights to use the trade marks (whether registered or unregistered), +# the trade names, or product names of Audinate. +# +# 6. If you choose to redistribute or sell the Software you may elect to offer support, maintenance, warranties, +# indemnities or other liability obligations or rights consistent with this License. However, you may only act on +# your own behalf and must not bind Audinate. You agree to indemnify and hold harmless Audinate, and its affiliates +# from any liability claimed or incurred by reason of your offering or accepting any additional warranty or additional +# liability. +# + +# NOTE: this script is intended to be run on production systems where the dante_package/development +# directory might not be available (thus no `jq` to rely on for JSON parsing) and basic tools such as `id` +# could be missing (e.g. BusyBox). +# Any changes to the script should take this into account. + +RED_COLOR="\e[01;31m" +GREEN_COLOR="\e[01;32m" +YELLOW_COLOR="\e[01;33m" +BLUE_COLOR="\e[01;34m" +END_COLOR="\e[0m" + +red() { printf '%b %s %b' "$RED_COLOR" "$*" "$END_COLOR"; } +green() { printf '%b %s %b' "$GREEN_COLOR" "$*" "$END_COLOR"; } +blue() { printf '%b %s %b' "$BLUE_COLOR" "$*" "$END_COLOR"; } +yellow() { printf '%b %s %b' "$YELLOW_COLOR" "$*" "$END_COLOR"; } + +logerr() { echo "[ $(red ERROR)] $1"; } +logwarn() { echo "[$(yellow WARNING)] $1"; } +loginfo() { echo "[ $(blue INFO)] $1"; } +logok() { echo "[ $(green OK)] $1"; } + +fail() { exit 1; } + +cmd_exists() { command -v -- "$1" >/dev/null 2>&1; } + +# This function assumes that: +# - the JSON file is well-formed +# - key and value appear on the same line +# - strings are double-quoted and donโ€™t contain escaped quotes +# - assumes the key exists exactly once per line +get_json_field() +{ + json_file=$1 + field_name=$2 + default="__NOT_FOUND__" + + if [ ! -f "$json_file" ]; then + echo "error: file '$json_file' not found" >&2 + exit 1 + fi + + # explaining each sed: + # - 's/^[^:]*://' removes everything up to and including the first colon + # - 's/ //' removes the first space character after the colon, if present + # - 's/^"//' and 's/"$//' removes leading and trailing double quotes from the value + # - 's/,[[:space:]]*$//' removes a trailing comma and any following whitespace (e.g. to handle lists) + # - 's/[[:space:]]*$//' trims any remaining trailing whitespace from the value + value=$(grep "\"$field_name\"" "$json_file" | \ + sed -e 's/^[^:]*://' -e 's/ //' | \ + sed -e 's/^"//' -e 's/"$//' | \ + sed -e 's/,[[:space:]]*$//' | \ + sed -e 's/[[:space:]]*$//' | head -n 1) + + if [ -z "$value" ]; then + echo "$default" + else + echo "$value" + fi +} + +# where DEP is installed, default value +DEFAULT_DEP_PATH="/opt/dep" + +# where DEP logs are stored, default value +DEFAULT_LOGS_PATH="/var/log" + +# where temporary files created by this script will be stored, default value +DEFAULT_TEMP_PATH="/tmp" + +# where the archive created by this script will be stored, default value +DEFAULT_OUTPUT_PATH=$(pwd) + +# DEP container logs can only be stored in /var/log at the moment. +CONT_LOGS="/var/log/dante_container.log" + +usage() { + loginfo "Usage: $0 [OPTIONS]" + loginfo "" + loginfo "This tool collects diagnostic data to help debug issues with the DEP software." + loginfo "" + loginfo "Options:" + loginfo " -c Specify the directory where DEP is installed." + loginfo " Default is '${DEFAULT_DEP_PATH}'." + loginfo " -l Specify the directory where DEP stores its log files." + loginfo " Default is '${DEFAULT_LOGS_PATH}'." + loginfo " -o Specify the output directory for the final archive and any temporary" + loginfo " files or directories created in the process. This directory must be" + loginfo " writable by the user executing the script." + loginfo " Default is the current directory, '${DEFAULT_OUTPUT_PATH}'" + loginfo "" + loginfo "Examples:" + loginfo "" + loginfo " $0 -c /apps/dep -l /tmp/logs" + loginfo "" + loginfo " Collects diagnostic data from a DEP installation in /apps/dep, DEP log files in" + loginfo " /tmp/logs, and stores the output in the current directory." + loginfo "" + loginfo " $0 -c /apps/dep -l /tmp/logs -o /tmp/dep_diagnostics" + loginfo "" + loginfo " Collects diagnostic data from a DEP installation in /apps/dep, DEP log files in" + loginfo " /tmp/logs, and stores the output in /tmp/dep_diagnostics." + loginfo "" + loginfo " $0 -o /home/user/dep_diagnostics" + loginfo "" + loginfo " Uses the default DEP installation and log file paths, and stores the output in" + loginfo " /home/user/dep_diagnostics." +} + +# Copy a file or directory from a source to a destination. +# +# Arguments: +# src (str): the source file or directory to be copied. +# dst (str): the destination where the source will be copied. +# msg (str): an error message to be logged if the copy operation fails. +# +# Behaviour: +# If the source is a directory, the function performs a recursive +# copy. If the copy operation fails for any reason, it logs a warning +# message using the provided `msg` argument along with the captured +# error message from the failed copy operation. +# +# NOTE: the function uses `eval` to allow for correct parameter expansion +# (e.g. "cp /var/log/dante_*" wouldn't work otherwise). +copy() { + src="$1" + dst="$2" + msg="$3" + cmd="cp ${src} ${dst}" + + if [ -d "${src}" ]; then + cmd="cp -r ${src} ${dst}" + fi + + err=$(eval "${cmd}" 2>&1) + res=$? + if [ "${res}" -ne 0 ]; then + logwarn "$msg: $err" + fi +} + +# Checks if a specified directory exists and if it's writable. +# +# Arguments: +# path (str): Directory to check. +# check_write (str): '1' to check write permission, '0' otherwise. +# err_msg (str): Optional. Additional error message to display. +# +# Behaviour: +# Logs an error and exits if `path` is not a valid directory. +# If `check_write` is '1', also checks for write permission. +# Logs an error and exits if the directory is not writable. +check_path() { + path="$1" + check_write="$2" + err_msg="$3" + _ret_val=0 + + if [ ! -d "${path}" ]; then + logerr "${path} is not a valid path" + _ret_val=1 + elif [ ! -w "${path}" ] && [ "${check_write}" = "1" ]; then + logerr "you don't have writing permission for the directory: $path" + _ret_val=1 + fi + + if [ "${err_msg}" ] && [ ${_ret_val} -eq 1 ]; then + logerr "${err_msg}" + fi + + if [ ${_ret_val} -eq 1 ]; then + exit ${_ret_val} + fi +} + +collect_kernel_config() { + dest_path="$1" + config_file="" + is_gzipped=0 + + if [ -f "/proc/config.gz" ]; then + config_file="/proc/config.gz" + is_gzipped=1 + elif [ -f "/boot/config-$(uname -r)" ]; then + config_file="/boot/config-$(uname -r)" + elif [ -f "/boot/config" ]; then + config_file="/boot/config" + elif [ -f "/lib/modules/$(uname -r)/build/.config" ]; then + config_file="/lib/modules/$(uname -r)/build/.config" + fi + + if [ -z "$config_file" ]; then + logerr "no kernel config found in standard locations" + return + fi + + loginfo "found kernel config at: $config_file" + + # for gzipped config, try to decompress and copy + if [ "$is_gzipped" -eq 1 ]; then + if cmd_exists gunzip; then + if gunzip -c "$config_file" > "$dest_path"/kernel_config.txt 2>/dev/null; then + # if gunzip suceeeds, early return to avoid copy + return + fi + fi + fi + + copy "$config_file" "$dest_path" "Failed to copy config from $config_file to $dest_path" +} + +while getopts ":o:c:l:h" option; do + case $option in + o) # output directory + OUTPUT_PATH=$OPTARG + TEMP_PATH=$OPTARG + ;; + l) # log directory + LOGS_PATH=$OPTARG + ;; + c) # DEP install path + DEP_PATH=$OPTARG + ;; + h) # display Help + usage + exit 0 + ;; + \?) # invalid option + errmsg="invalid option: -$OPTARG" + ;; + :) # missing argument + errmsg="option -$OPTARG requires an argument." + ;; + esac +done + +# if we have an error from getopts, log it and exit +if [ -n "$errmsg" ]; then + logerr "$errmsg" + fail +fi + +# if we can't create archives, we can't proceed +if ! cmd_exists tar; then + logerr "'tar' not found, unable to create archives" + fail +fi + +# check whether we need to use defaults +: "${DEP_PATH:=$DEFAULT_DEP_PATH}" +: "${LOGS_PATH:=$DEFAULT_LOGS_PATH}" +: "${TEMP_PATH:=$DEFAULT_TEMP_PATH}" +: "${OUTPUT_PATH:=$DEFAULT_OUTPUT_PATH}" + +# if OUTPUT_PATH can't be written to, we can't proceed +# NOTE: by checking OUTPUT_PATH we also check TEMP_PATH: +# the latter is set to /tmp by default, so it is only necessary +# to make sure we can write to it when the user has specified +# a different directory, in which case OUTPUT_PATH would have +# the same value so it makes sense to only check OUTPUT_PATH +check_path "$OUTPUT_PATH" 1 "please chose a different directory using the -o option. Try $0 -h for more information" + +# check that provided paths are valid +check_path "$DEP_PATH" 0 "please chose a different directory using the -c option. Try $0 -h for more information" +check_path "$LOGS_PATH" 0 "please chose a different directory using the -l option. Try $0 -h for more information" + +# this script's own log file +LOGFILE="/tmp/collector.txt" + +# start logging our own output: +# - create a named pipe +# - start tee reading from it in the background +# - redirect stdout and stderr to the named pipe +# trap command ensures that the named pipe gets deleted when the script exits. +mkfifo /tmp/tmpfifo +trap 'rm /tmp/tmpfifo && rm ${LOGFILE}' EXIT +tee -a "${LOGFILE}" < /tmp/tmpfifo & +exec > /tmp/tmpfifo 2>&1 + +# in a world where all shells support process substitution +# this is an alternative way +# exec > >(tee -a ${LOGFILE} ) +# exec 2> >(tee -a ${LOGFILE} >&2) + +# output what we're running with +loginfo "DEP install path: ${DEP_PATH}" +loginfo "DEP logs path: ${LOGS_PATH}" +loginfo "Temporary files will be saved in: ${TEMP_PATH}" +loginfo "Script output archive will be saved in: ${OUTPUT_PATH}" + +# we'll use a subdir to store our data +SUPPORT_DIR=${TEMP_PATH}/dep_support + +# where to store the ethtool output +ETHTOOL_FILE="${SUPPORT_DIR}/ethtoolinfo.txt" + +# where to store the HW clock info +HW_CLKING_FILE="${SUPPORT_DIR}/hwclk.txt" + +# in case the script was interrupted midway during a previous run +rm -rf "${SUPPORT_DIR}" + +# if we can't create ${SUPPORT_DIR}, we can't proceed +if ! mkdir -p "${SUPPORT_DIR}" 2>/dev/null; then + logerr "cannot create directory ${SUPPORT_DIR}: permission denied" + fail +fi + +DANTE_JSON="$DEP_PATH"/dante_package/dante_data/capability/dante.json +CONFIG_JSON="$DEP_PATH"/dante_package/dante_data/capability/config.json +CONFIG_DEP="$DEP_PATH"/dante_package/dante_data/config +ACTIVATION_DIR="${DEP_PATH}/dante_package/dante_data/activation" + +loginfo "Collecting config files..." + +# if found, get dante.json +if [ -f "${DANTE_JSON}" ]; then + copy "${DANTE_JSON}" "${SUPPORT_DIR}" "collection of ${DANTE_JSON} failed" +else + logerr "dante.json not found in $(dirname "${DANTE_JSON}")" +fi + +# if found, get config.json +if [ -f "${CONFIG_JSON}" ]; then + copy "${CONFIG_JSON}" "${SUPPORT_DIR}" "collection of ${CONFIG_JSON} failed" +else + logerr "config.json not found in $(dirname "${CONFIG_JSON}")" +fi + +# if found, get all content from dante_data/config +if [ -d "${CONFIG_DEP}" ]; then + copy "${CONFIG_DEP}" "${SUPPORT_DIR}" "collection of DEP ${CONFIG_DEP} directory failed" +else + logerr "DEP config directory not found in $(dirname "${CONFIG_DEP}")" +fi + +# check and collect activation files +if [ -d "${ACTIVATION_DIR}" ]; then + # copy whatever we have in the activation directory + copy "${ACTIVATION_DIR}" "${SUPPORT_DIR}" "collection of DEP activation files failed" + # log errors related to single act + + for actFile in device.lic manufacturer.cert; do + if [ ! -f "${ACTIVATION_DIR}/${actFile}" ]; then + logwarn "activation file '${actFile}' not found in ${ACTIVATION_DIR}" + fi + done +else + logerr "DEP activation directory not found in $(dirname "${ACTIVATION_DIR}")" +fi + +loginfo "Collecting DEP logs..." + +# get all DEP logs +mkdir -p "${SUPPORT_DIR}/logs" +copy "${LOGS_PATH}/dante_*" "${SUPPORT_DIR}/logs" "collection of DEP logs failed" + +# get the container logs +mkdir -p "${SUPPORT_DIR}/logs" +copy "${CONT_LOGS}" "${SUPPORT_DIR}/logs" "collection of DEP container logs failed" + +loginfo "Collecting system info..." + +# get kernel config +collect_kernel_config "${SUPPORT_DIR}" + +# get /proc/cpuinfo +copy "/proc/cpuinfo" "${SUPPORT_DIR}/cpuinfo.txt" "collection of /proc/cpuinfo failed" + +# get /proc/interrupts +copy "/proc/interrupts" "${SUPPORT_DIR}/interrupts.txt" "collection of /proc/interrupts failed" + +# get mount points +mount > "${SUPPORT_DIR}/mountinfo.txt" || logwarn "collection of mount points failed" + +# get info about running processes: try including thread info first, +# in case of failure (e.g. "ps" is actually BusyBox) fall back to processes only +if ! ps -efL > "${SUPPORT_DIR}/processinfo.txt" 2> /dev/null; then + ps > "${SUPPORT_DIR}/processinfo.txt" || logwarn "unable to write process info into ${SUPPORT_DIR}/processinfo.txt" +fi + +# get the list of active sockets +if cmd_exists netstat; then + netstat -anp 2>/dev/null > "${SUPPORT_DIR}/netstat.txt" || logwarn "unable to collect active socket info" +else + logwarn "netstat command not available" +fi + +# get info about network interfaces +if cmd_exists ip; then + ip address > "${SUPPORT_DIR}/ipinfo.txt" || logwarn "unable to write ip info to ${SUPPORT_DIR}/ipinfo.txt" +else + logwarn "ip command not available" +fi + +# get ALSA version (userspace libs) +if cmd_exists aplay; then + aplay --version > "${SUPPORT_DIR}/alsa.txt" || logwarn "unable to write ALSA version to ${SUPPORT_DIR}/alsa.txt" +fi + +# get kernel messages +if cmd_exists dmesg; then + dmesg > "${SUPPORT_DIR}/dmesg.txt" || logwarn "unable to collect kernel messages - dmesg failed" +fi + +# get device nodes +ls -l /dev > "${SUPPORT_DIR}/device_nodes.txt" || logwarn "unable to collect info about device nodes" + +# get timestamp and coalesce info about each network interface +if cmd_exists ethtool; then + for NETWORK_INTERFACE in /sys/class/net/*; do + INTERFACE_NAME=$(basename "$NETWORK_INTERFACE") + + { + echo "ethtool -c \"$INTERFACE_NAME\"" + ethtool -c "$INTERFACE_NAME" 2>&1 + echo "------------------------" + } >> "$ETHTOOL_FILE" + + { + echo "ethtool -T \"$INTERFACE_NAME\"" + ethtool -T "$INTERFACE_NAME" 2>&1 + echo "------------------------" + } >> "$ETHTOOL_FILE" + done +else + logwarn "ethtool command not available" +fi + + +# get info for HW clocking, if enabled in dante.json +if [ -f "${DANTE_JSON}" ]; then + MNT_DIR="${SUPPORT_DIR}/mnt" + ROOTFS_FILE="$DEP_PATH/dante_package/dante_data/images/0/rootfs_squash" + useHwClock=$(get_json_field "${DANTE_JSON}" useHwClock) + + if [ "$useHwClock" = "true" ]; then + circuitName=$(get_json_field "${DANTE_JSON}" circuitName) + i2cBus=$(get_json_field "${DANTE_JSON}" i2cBus) + i2cAddr=$(get_json_field "${DANTE_JSON}" i2cAddr) + + { + echo "circuitName=$circuitName" + echo "i2cBus=$i2cBus" + echo "i2cAddr=$i2cAddr" + } >> "$HW_CLKING_FILE" + + # hwclkcfg binary is in the DEP rootfs so mount rootfs first and then run it + mkdir -p "${MNT_DIR}" + if ! mount "$ROOTFS_FILE" "${MNT_DIR}"; then + logerr "unable to collect HW clocking info: rootfs mount failed" + else + "$MNT_DIR"/dante/hwclkcfg -c --i2cbus "$i2cBus" --i2caddr "$i2cAddr" "$circuitName" >> "$HW_CLKING_FILE" 2>&1 + umount "${MNT_DIR}" 2> /dev/null + fi + rm -rf "${MNT_DIR}" + fi +fi + +# if we are UID 0, run dep_check.sh and save its output +if [ "$(grep -E '^Uid:' /proc/self/status | awk '{print $2}')" -eq "0" ]; then + if [ ! -f "./development/dep_check.sh" ]; then + logwarn "dep_check.sh not found, skipping" + else + loginfo "Run dep_check and collect its output..." + { ./development/dep_check.sh "${DEP_PATH}" > "${SUPPORT_DIR}/depcheck.txt"; } 2>&1 + # remove escape characters from dep_check.sh output + sed -i 's/[^[:print:]]\[[0-9;]*[a-zA-Z]//g' "${SUPPORT_DIR}/depcheck.txt" + fi +else + logwarn "could not run dep_check.sh because user was not root" +fi + +# add this script own logs to the bundle +if [ -f "$LOGFILE" ]; then + # remove escape characters from this script output + sed -i 's/[^[:print:]]\[[0-9;]*[a-zA-Z]//g' "$LOGFILE" +fi + +loginfo "Create final archive..." + +# copy our own logs to the support directory, fail silently +cp "$LOGFILE" "${SUPPORT_DIR}/collector.txt" || true + +# bundle everything together +timestamp=$(date "+%Y.%m.%d-%H.%M.%S") +tgz_name="dep_support-${timestamp}.tgz" + +if ! tar czf "${OUTPUT_PATH}"/"${tgz_name}" -C "$(dirname "${SUPPORT_DIR}")" "$(basename "${SUPPORT_DIR}")" > /dev/null 2>&1; then + logerr "unable to bundle support files in ${OUTPUT_PATH}/${tgz_name}" + _exit_val=1 +else + logok "DEP log files and system info bundled in ${OUTPUT_PATH}/${tgz_name}" + _exit_val=0 +fi + +# remove temporary data +rm -rf "${SUPPORT_DIR}" +exit ${_exit_val} + +# +# Copyright ยฉ 2022-2025 Audinate Pty Ltd ACN 120 828 006 (Audinate). All rights reserved. +# diff --git a/src/dep/dante_package/depconfig b/src/dep/dante_package/depconfig new file mode 100755 index 0000000..5a79f83 Binary files /dev/null and b/src/dep/dante_package/depconfig differ diff --git a/src/dep/dante_package/development/dep.service b/src/dep/dante_package/development/dep.service new file mode 100644 index 0000000..c1591ee --- /dev/null +++ b/src/dep/dante_package/development/dep.service @@ -0,0 +1,27 @@ +[Unit] +Description=Dante Embedded Platform +After=network.target + +[Service] +Type=simple + +# Optional: restrict CPU affinity for relevant slices before the service starts. +# Uncomment one or more of the following lines to pin system slices to specific CPUs. +# +# Notes: +# - These affect *other* processes in the corresponding slices (init, user, system), not just this service. +# - This may impact unrelated services or user sessions: if possible, CPU isolation should be obtained +# at a system level by tuning the kernel command line +# +#ExecStartPre=/usr/bin/systemctl set-property init.scope AllowedCPUs=0,1 +#ExecStartPre=/usr/bin/systemctl set-property user.slice AllowedCPUs=0,1 +#ExecStartPre=/usr/bin/systemctl set-property system.slice AllowedCPUs=0,1 + +ExecStart=/opt/dep/dante_package/dep.sh start +ExecStopPost=/opt/dep/dante_package/dep.sh stop +WorkingDirectory=/opt/dep/dante_package +PIDFile=/run/dante.pid +RemainAfterExit=yes + +[Install] +WantedBy=multi-user.target diff --git a/src/dep/dante_package/development/dep_check.sh b/src/dep/dante_package/development/dep_check.sh new file mode 100755 index 0000000..cb41a09 --- /dev/null +++ b/src/dep/dante_package/development/dep_check.sh @@ -0,0 +1,1802 @@ +#!/bin/sh + +# Copyright ยฉ 2022-2025 Audinate Pty Ltd ACN 120 828 006 (Audinate). All rights reserved. +# +# +# 1. Subject to the terms and conditions of this Licence, Audinate hereby grants you a worldwide, non-exclusive, +# no-charge, royalty free licence to copy, modify, merge, publish, redistribute, sublicense, and/or sell the +# Software, provided always that the following conditions are met: +# 1.1. the Software must accompany, or be incorporated in a licensed Audinate product, solution or offering +# or be used in a product, solution or offering which requires the use of another licensed Audinate +# product, solution or offering. The Software is not for use as a standalone product without any +# reference to Audinate's products; +# 1.2. the Software is provided as part of example code and as guidance material only without any warranty +# or expectation of performance, compatibility, support, updates or security; and +# 1.3. the above copyright notice and this License must be included in all copies or substantial portions +# of the Software, and all derivative works of the Software, unless the copies or derivative works are +# solely in the form of machine-executable object code generated by the source language processor. +# +# 2. TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +# PURPOSE AND NONINFRINGEMENT. +# +# 3. TO THE FULLEST EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT SHALL AUDINATE BE LIABLE ON ANY LEGAL THEORY +# (INCLUDING, WITHOUT LIMITATION, IN AN ACTION FOR BREACH OF CONTRACT, NEGLIGENCE OR OTHERWISE) FOR ANY CLAIM, +# LOSS, DAMAGES OR OTHER LIABILITY HOWSOEVER INCURRED. WITHOUT LIMITING THE SCOPE OF THE PREVIOUS SENTENCE THE +# EXCLUSION OF LIABILITY SHALL INCLUDE: LOSS OF PRODUCTION OR OPERATION TIME, LOSS, DAMAGE OR CORRUPTION OF +# DATA OR RECORDS; OR LOSS OF ANTICIPATED SAVINGS, OPPORTUNITY, REVENUE, PROFIT OR GOODWILL, OR OTHER ECONOMIC +# LOSS; OR ANY SPECIAL, INCIDENTAL, INDIRECT, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES, ARISING OUT OF OR +# IN CONNECTION WITH THIS AGREEMENT, ACCESS OF THE SOFTWARE OR ANY OTHER DEALINGS WITH THE SOFTWARE, EVEN IF +# AUDINATE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH CLAIM, LOSS, DAMAGES OR OTHER LIABILITY. +# +# 4. APPLICABLE LEGISLATION SUCH AS THE AUSTRALIAN CONSUMER LAW MAY APPLY REPRESENTATIONS, WARRANTIES, OR CONDITIONS, +# OR IMPOSES OBLIGATIONS OR LIABILITY ON AUDINATE THAT CANNOT BE EXCLUDED, RESTRICTED OR MODIFIED TO THE FULL +# EXTENT SET OUT IN THE EXPRESS TERMS OF THIS CLAUSE ABOVE "CONSUMER GUARANTEES". TO THE EXTENT THAT SUCH CONSUMER +# GUARANTEES CONTINUE TO APPLY, THEN TO THE FULL EXTENT PERMITTED BY THE APPLICABLE LEGISLATION, THE LIABILITY OF +# AUDINATE UNDER THE RELEVANT CONSUMER GUARANTEE IS LIMITED (WHERE PERMITTED AT AUDINATE'S OPTION) TO ONE OF +# FOLLOWING REMEDIES OR SUBSTANTIALLY EQUIVALENT REMEDIES: +# 4.1. THE REPLACEMENT OF THE SOFTWARE, THE SUPPLY OF EQUIVALENT SOFTWARE, OR SUPPLYING RELEVANT SERVICES AGAIN; +# 4.2. THE REPAIR OF THE SOFTWARE; +# 4.3. THE PAYMENT OF THE COST OF REPLACING THE SOFTWARE, OF ACQUIRING EQUIVALENT SOFTWARE, HAVING THE RELEVANT +# SERVICES SUPPLIED AGAIN, OR HAVING THE SOFTWARE REPAIRED. +# +# 5. This License does not grant any permissions or rights to use the trade marks (whether registered or unregistered), +# the trade names, or product names of Audinate. +# +# 6. If you choose to redistribute or sell the Software you may elect to offer support, maintenance, warranties, +# indemnities or other liability obligations or rights consistent with this License. However, you may only act on +# your own behalf and must not bind Audinate. You agree to indemnify and hold harmless Audinate, and its affiliates +# from any liability claimed or incurred by reason of your offering or accepting any additional warranty or additional +# liability. +# + +# shellcheck disable=SC1091 +if [ -f "$(pwd)"/dep_check_platform.sh ]; then + . "$(pwd)"/dep_check_platform.sh +elif [ -f ./development/dep_check_platform.sh ]; then + . ./development/dep_check_platform.sh +fi + +RED_COLOR="\e[01;31m" +GREEN_COLOR="\e[01;32m" +YELLOW_COLOR="\e[01;33m" +BLUE_COLOR="\e[01;34m" +END_COLOR="\e[0m" + +red() { printf '%b %s %b' "$RED_COLOR" "$*" "$END_COLOR"; } +green() { printf '%b %s %b' "$GREEN_COLOR" "$*" "$END_COLOR"; } +blue() { printf '%b %s %b' "$BLUE_COLOR" "$*" "$END_COLOR"; } +yellow() { printf '%b %s %b' "$YELLOW_COLOR" "$*" "$END_COLOR"; } + +logerr() { echo "[ $(red ERROR)] $1"; } +logwarn() { echo "[$(yellow WARNING)] $1"; } +loginfo() { echo "[ $(blue INFO)] $1"; } +logok() { echo "[ $(green OK)] $1"; } + +USER=$(whoami) +DEP_PATH="$1" +PATH_TO_BUNDLE_DIR="${DEP_PATH}"/bundle +DANTE_JSON="${DEP_PATH}"/dante_data/capability/dante.json +CONFIG_JSON="${DEP_PATH}"/dante_data/capability/config.json +DEFER_COA_CHECK=0 +TAB=" " + +# To use host-provided jq, set JQ to the absolute path of the jq binary. +JQ="${DEP_PATH}"/development/tools/jq + +# If a different container runtime is used, its absolute path must be set here. +# When the container runtime is changed to something other than crun, please disable +# check_container_runtime_glibc() from running as it targets the specific binary +# provided by Audinate. +CONTAINER_RUNTIME=$DEP_PATH/crun + +PTP_TEST="${DEP_PATH}"/development/tools/ptp_timestamping_test +PTP_TEST_README="${DEP_PATH}"/development/tools/README_ptp.txt + +# 2.28 was obtained using: +# +# $ objdump -T /usr/bin/crun | grep GLIBC_ | sed 's/.*GLIBC_\([.0-9]*\).*/\1/g' | sort -Vu | tail -n1 +# +# the general rule is that the system's glibc version must be equal to or greater than the +# version required by crun, otherwise crun will not be able to start +REQUIRED_GLIBC_CRUN="2.28" +REQUIRED_GLIBC_PTP_TEST="2.34" + +### functions + +fail() { exit 1; } + +cmd_exists() { command -v -- "$1" >/dev/null 2>&1; } + +# Argument validation for JSON utility functions. +validate_json_args() { + jsonData="$1" + field="$2" + + if [ -z "$jsonData" ] || [ -z "$field" ]; then + logerr "invalid use of validate_json_args (or calling function): missing arguments" + fail + fi +} + +# Extracts a field from JSON data using jq. +# +# Arguments: +# $1 - JSON data as a string +# $2 - absolute path of the field to extract (e.g., ".field.subfield") +# +# If the field does not exist or is not valid JSON, it returns "null". +# While such return value represents an obvious limitation (e.g. it cannot +# distinguish between a missing field and a field with the value "null"), it +# is the best we can do given the constraints of the JSON format. +get_json_field() { + validate_json_args "$1" "$2" + echo "$1" | "$JQ" -erc "$2" 2>/dev/null +} + +get_dante_json_field() { + field="$1" + + if [ -z "$DANTE_JSON_DATA" ]; then + logerr "get_dante_json_field: DANTE_JSON_DATA is empty, this function must be called after load_config_files()" + fail + fi + + get_json_field "$DANTE_JSON_DATA" "$field" +} + +get_config_json_field() { + field="$1" + + if [ -z "$CONFIG_JSON_DATA" ]; then + logerr "get_config_json_field: CONFIG_JSON_DATA is empty, this function must be called after load_config_files()" + fail + fi + + get_json_field "$CONFIG_JSON_DATA" "$field" +} + +# NOTE: checking whether the field exists and is an array is left to the caller +get_json_array_length() { + validate_json_args "$1" "$2" + + # output the length of the array + echo "$1" | "$JQ" "$2 | length" +} + +# Extract array elements from JSON data as space-separated values. +get_json_array_elements() { + validate_json_args "$1" "$2" + + # check if the field exists and is an array + if is_json_field_array "$1" "$2"; then + # output each array element as a compact JSON string, space-separated + echo "$1" | "$JQ" -cr "$2[]? // empty" | tr '\n' ' ' | sed 's/ $//' + else + # field doesn't exist, isn't an array, or is null + echo "" + fi +} + +get_dante_json_array_elements() { + field="$1" + + if [ -z "$DANTE_JSON_DATA" ]; then + logerr "get_dante_json_array_elements: DANTE_JSON_DATA is empty, this function must be called after load_config_files()" + fail + fi + + get_json_array_elements "$DANTE_JSON_DATA" "$field" +} + +get_config_json_array_elements() { + field="$1" + + if [ -z "$CONFIG_JSON_DATA" ]; then + logerr "get_config_json_array_elements: CONFIG_JSON_DATA is empty, this function must be called after load_config_files()" + fail + fi + + get_json_array_elements "$CONFIG_JSON_DATA" "$field" +} + +is_json_field_array() { + validate_json_args "$1" "$2" + echo "$1" | "$JQ" -e "$2 | type == \"array\"" >/dev/null 2>&1 +} + +is_json_field_object() { + validate_json_args "$1" "$2" + echo "$1" | "$JQ" -e "$2 | type == \"object\"" >/dev/null 2>&1 +} + +is_json_field_string() { + validate_json_args "$1" "$2" + echo "$1" | "$JQ" -e "$2 | type == \"string\"" >/dev/null 2>&1 +} + +is_json_field_number() { + validate_json_args "$1" "$2" + echo "$1" | "$JQ" -e "$2 | type == \"number\"" >/dev/null 2>&1 +} + +is_json_field_bool() { + validate_json_args "$1" "$2" + echo "$1" | "$JQ" -e "$2 | type == \"boolean\"" >/dev/null 2>&1 +} + +is_json_field_null() { + validate_json_args "$1" "$2" + echo "$1" | "$JQ" -e "$2 | type == \"null\"" >/dev/null 2>&1 +} + +json_field_exists() { + validate_json_args "$1" "$2" + echo "$1" | "$JQ" -e "$2" >/dev/null 2>&1 +} + +get_json_field_type() { + validate_json_args "$1" "$2" + echo "$1" | "$JQ" -r "$2 | type" 2>/dev/null +} + +kernel_info() { + configFile="" + isGzipped=0 + + # find kernel config file in order of most common to least common locations + if [ -f "/proc/config.gz" ]; then + configFile="/proc/config.gz" + isGzipped=1 + elif [ -f "/boot/config-$(uname -r)" ]; then + configFile="/boot/config-$(uname -r)" + elif [ -f "/boot/config" ]; then + configFile="/boot/config" + elif [ -f "/lib/modules/$(uname -r)/build/.config" ]; then + configFile="/lib/modules/$(uname -r)/build/.config" + fi + + # no config file found + if [ -z "$configFile" ]; then + echo "" + return + fi + + if [ "$isGzipped" -eq 1 ]; then + if cmd_exists gunzip; then + gunzip -c "$configFile" 2>/dev/null + else + echo "" + fi + else + cat "$configFile" + fi +} + +check_iface_coalesce() { + iface="$1" + coalesceErr=0 + + if ! cmd_exists ethtool; then + logwarn "'ethtool' not found, skipping coalescing check for $iface" + return + fi + + if ! ethtool -c "$iface" >/dev/null 2>&1; then + logwarn "network coalescing cannot be set/read on $iface" + return + fi + + for dir in rx tx; do + val=$(ethtool -c "$iface" 2>/dev/null | awk "/${dir}-usecs:/ {print \$2}") + if [ "$val" != 1 ] && [ "$val" != "n/a" ]; then + logwarn "$iface has ${dir}-usecs=$val, set to 1 for best performance." + echo "${TAB}This can be set with 'ethtool -C $iface $dir-usecs 1' before starting DEP." + echo "${TAB}Some drivers might not support this setting or only allow values significantly higher than 1 usecs." + echo "${TAB}Please contact your network driver vendor for more information." + coalesceErr=1 + fi + done + + [ "$coalesceErr" -eq 0 ] && logok "$iface coalescing settings ok" +} + +# NOTE: on some systems 'id' might not be available, hence we check manually +check_uid() { + if [ "$(grep -E '^Uid:' /proc/self/status | awk '{print $2}')" -ne "0" ]; then + logerr "this script must be executed with root privileges" + fail + fi +} + +check_arg() { + if [ -z "$1" ]; then + logerr "missing argument: DEP installation path" + loginfo "usage:" + loginfo " sudo ./dep_check.sh " + fail + fi + + if [ "${1#/}" = "$1" ]; then + logerr "$1 is not an absolute path" + fail + fi + + if [ ! -d "$1" ]; then + logerr "$1: directory not found" + fail + fi +} + +check_runtime_deps() { + if [ ! -f "$JQ" ]; then + logerr "$JQ not found. This script requires jq to parse JSON files." + echo "${TAB}Please install jq or set JQ to the absolute path of the jq binary." + fail + fi + + # determine installed glibc version + if cmd_exists ldd; then + installedGlibc=$(ldd --version 2>/dev/null | awk 'NR==1{print $NF}') + elif [ -x /lib/libc.so.6 ]; then + installedGlibc=$(/lib/libc.so.6 2>/dev/null | awk 'NR==1{print $NF}') + elif [ -x /lib64/libc.so.6 ]; then + installedGlibc=$(/lib64/libc.so.6 2>/dev/null | awk 'NR==1{print $NF}') + else + logwarn "could not determine installed glibc version:" + return + fi + + # remove trailing dot if present + installedGlibc="${installedGlibc%.}" + + is_glibc_version_host_compatible() { + requiredVersion="$1" + + requiredMajor=$(echo "$requiredVersion" | cut -d'.' -f1 | tr -dc '0-9') + requiredMinor=$(echo "$requiredVersion" | cut -d'.' -f2 | tr -dc '0-9') + + installedMajor=$(echo "$installedGlibc" | cut -d'.' -f1 | tr -dc '0-9') + installedMinor=$(echo "$installedGlibc" | cut -d'.' -f2 | tr -dc '0-9') + + if [ "$installedMajor" -lt "$requiredMajor" ] || { [ "$installedMajor" -eq "$requiredMajor" ] && [ "$installedMinor" -lt "$requiredMinor" ]; }; then + return 0 + fi + return 1 + } + + if ! is_glibc_version_host_compatible $REQUIRED_GLIBC_CRUN; then + logok "host-provided glibc ($installedGlibc) is compatible with container crun (requires >= $REQUIRED_GLIBC_CRUN)" + else + logwarn "host-provided glibc ($installedGlibc) is older than required ($REQUIRED_GLIBC_CRUN) for container crun" + echo "${TAB}if unable to start DEP due to crun issues, consider compiling crun" + echo "${TAB}(or an alternative OCI-compliant container runtime) using a compatible" + echo "${TAB}toolchain for this system" + fi + + PTP_TEST_COMPATIBLE=0 + is_glibc_version_host_compatible "$REQUIRED_GLIBC_PTP_TEST" || PTP_TEST_COMPATIBLE=1 +} + +check_architecture() { + if ! cmd_exists hexdump; then + logwarn "host architecture check skipped: 'hexdump' not found" + return + fi + + if [ ! -f "$CONTAINER_RUNTIME" ]; then + logerr "container runtime binary not found: $CONTAINER_RUNTIME" + echo "${TAB}If you have changed the default container runtime shipped with DEP, please" + echo "${TAB}set CONTAINER_RUNTIME to the absolute path of the new binary or just ignore this message." + return + fi + + HOST_BINARY=$(which uname) + ELF_ISA_OFFSET=0x12 + ELF_ISA_SIZE=2 + ELF_ISA_ARM_32=28 + ELF_ISA_X86_64=3e + ELF_ISA_ARM_64=b7 + # get the hexdecimal value of the architecture in the ELF header section + HOST_ARCH=$(hexdump -e '"%x"' -s $ELF_ISA_OFFSET -n $ELF_ISA_SIZE "$HOST_BINARY") + DEP_ARCH=$(hexdump -e '"%x"' -s $ELF_ISA_OFFSET -n $ELF_ISA_SIZE "$CONTAINER_RUNTIME") + + DEP_ARCH_NAME= + case $DEP_ARCH in + "$ELF_ISA_ARM_32" ) + DEP_ARCH_NAME="arm32";; + "$ELF_ISA_X86_64" ) + DEP_ARCH_NAME="x86_64";; + "$ELF_ISA_ARM_64" ) + DEP_ARCH_NAME="arm64";; + * ) + DEP_ARCH_NAME="ELF_ISA: $DEP_ARCH";; + esac + + HOST_ARCH_NAME= + case $HOST_ARCH in + "$ELF_ISA_ARM_32" ) + HOST_ARCH_NAME="arm32";; + "$ELF_ISA_X86_64" ) + HOST_ARCH_NAME="x86_64";; + "$ELF_ISA_ARM_64" ) + HOST_ARCH_NAME="arm64";; + * ) + HOST_ARCH_NAME="ELF_ISA: $HOST_ARCH";; + esac + + if [ "$HOST_ARCH" = "$DEP_ARCH" ]; then + logok "DEP and host architecture matched" + return + else + if [ "$HOST_ARCH" = "$ELF_ISA_ARM_64" ] && [ "$DEP_ARCH" = "$ELF_ISA_ARM_32" ]; then + # execute the container runtime without arguments to check whether the host is + # available for multiple architecture supports suppressing both output and error + # messages of the container runtime + # shellcheck disable=SC2086 + ./${CONTAINER_RUNTIME} > /dev/null 2>&1 + CHECK_MULTI_ARCH_SUPPORT=$? + if [ "$CHECK_MULTI_ARCH_SUPPORT" -eq 0 ]; then + logerr "it appears you are running DEP built for arm32 on an arm64 host. Please obtain DEP built for arm64 instead" + else + logerr "system incompatibility detected: host CPU is $HOST_ARCH_NAME while DEP was built for $DEP_ARCH_NAME" + fi + else + logerr "system incompatibility detected: host CPU is $HOST_ARCH_NAME while DEP was built for $DEP_ARCH_NAME" + fi + fi +} + +check_kernel_config() { + kernelConfig=$(kernel_info) + if [ -z "$kernelConfig" ]; then + logwarn "kernel config not found, skipping kernel config checks" + return + fi + + # Check if a kernel config option is enabled, built as module, or disabled + # Usage: check_config_option CONFIG_NAME [required|disabled] + check_config_option() { + configName="$1" + requirement="${2:-optional}" + + enabledCount=$(echo "$kernelConfig" | grep -cw "${configName}=y") + moduleCount=$(echo "$kernelConfig" | grep -cw "${configName}=m") + + # handle options that should be disabled + if [ "$requirement" = "disabled" ]; then + if [ "$enabledCount" -ne 0 ] || [ "$moduleCount" -ne 0 ]; then + logwarn "$configName needs to be disabled in kernel config" + fi + return 1 + fi + + # handle enabled/module options + if [ "$enabledCount" -eq 1 ]; then + return 0 + fi + + if [ "$moduleCount" -eq 1 ]; then + return 0 + fi + + # option not found + if [ "$requirement" = "required" ]; then + logerr "$configName not found in kernel config" + else + logwarn "$configName not found in kernel config" + fi + return 1 + } + + # Define required kernel configuration options + requiredConfigs="CGROUPS CGROUP_DEVICE CPUSETS CGROUP_SCHED MEMCG NAMESPACES UTS_NS IPC_NS USER_NS PID_NS NET_NS SQUASHFS SQUASHFS_ZLIB BLK_DEV_LOOP IP_MULTICAST" + + # check required kernel configs + for option in $requiredConfigs; do + check_config_option "CONFIG_$option" "required" + done + + # check recommended kernel configs + check_config_option "CONFIG_PREEMPT" "optional" + check_config_option "CONFIG_CGROUP_FREEZER" "optional" + + # DEP doesn't support fine-grained real-time scheduling per cgroup yet. + # Hence, CONFIG_RT_GROUP_SCHED needs to be disabled to start DEP + # TODO: is this actually true? + check_config_option "CONFIG_RT_GROUP_SCHED" "disabled" +} + +check_cgroups() { + cgroupWarningMsg="one or more cgroup mounts were not found and DEP is configured to run with + numDepCores = 0 in dante.json: if this is intended, please ignore this warning." + cgroupMountErr=0 + + display_notfound() { logwarn "cgroup mount '$1' not found"; } + display_found() { logok "cgroup mount '$1' found"; } + + check_mount() { + path=$1 + expectedMountType=$2 + + while IFS= read -r line; do + # get the second field (mount point) and the third field (type) + mountPoint=$(echo "$line" | awk '{print $2}') + mountType=$(echo "$line" | awk '{print $3}') + + # if the mountPoint doesn't start with /sys/fs/cgroup, skip it + case "$mountPoint" in + /sys/fs/cgroup*) ;; + *) continue ;; + esac + + # if mount point and type exactly match the expected values, we're good + if [ "$mountPoint" = "$path" ] && [ "$mountType" = "$expectedMountType" ]; then + display_found "$path ($expectedMountType)" + return + fi + + # There is a chance multiple controllers are mounted on the same path, + # for instance we might be looking for /sys/fs/cgroup/cpu and have + # + # /sys/fs/cgroup/cpu,cpuacct cgroup etc.. + # + # mounted instead. + + # because we skip entries that do not start with /sys/fs/cgroup at + # the beginning of the loop, we know getting the substring after + # /sys/fs/cgroup at this point will yield an empty string at worst + cgroupSubstring=${mountPoint#/sys/fs/cgroup/} + + # do the same with $path + cgroupPathSubstring=${path#/sys/fs/cgroup/} + + # check if cgroupPathSubstring is part of cgroupSubstring + # eg this would successfully match 'cpu' against both 'cpuacct,cpu' and 'cpu,cpuacct' + if echo "$cgroupSubstring" | grep -qw "$cgroupPathSubstring"; then + if echo "$mountType" | grep -qw "$expectedMountType"; then + display_found "$path ($expectedMountType)" + return + fi + fi + done < /proc/mounts + + display_notfound "$path (expected type: $expectedMountType)" + cgroupMountErr=1 + } + + # hybrid cgroup mode detection + v1Found=$(awk '$3 == "cgroup" { found=1 } END { print found+0 }' /proc/mounts) + v2Found=$(awk '$3 == "cgroup2" { found=1 } END { print found+0 }' /proc/mounts) + + if [ "$v1Found" -eq 1 ] && [ "$v2Found" -eq 1 ]; then + logwarn "Hybrid cgroup mode detected: both cgroup v1 and v2 are mounted." + echo "${TAB}The default crun runtime shipped with DEP does not support hybrid cgroup mode." + echo "${TAB}Please ensure only one cgroup version (v1 or v2) is in use on your system." + echo "${TAB}If DEP fails to start, refer to the DEP manual for more information on supported cgroup configurations." + return + fi + + if ! json_field_exists "$DANTE_JSON_DATA" ".platform.cgroupVersion"; then + logwarn "'cgroupVersion' not set in dante.json, assuming cgroup v1 (legacy)" + cgroupVersion="1" + else + cgroupVersion=$(get_dante_json_field ".platform.cgroupVersion") + fi + + if [ "$cgroupVersion" = "1" ]; then + selectedVersion="v1" + check_mount "/sys/fs/cgroup" "tmpfs" + check_mount "/sys/fs/cgroup/cpu" "cgroup" + check_mount "/sys/fs/cgroup/cpuset" "cgroup" + check_mount "/sys/fs/cgroup/memory" "cgroup" + check_mount "/sys/fs/cgroup/devices" "cgroup" + elif [ "$cgroupVersion" = "2" ]; then + selectedVersion="v2" + check_mount "/sys/fs/cgroup" "cgroup2" + else + logerr "unsupported cgroupVersion value ($cgroupVersion) in $DANTE_JSON" + return + fi + + if [ "$cgroupMountErr" -eq 1 ]; then + if is_json_field_number "$DANTE_JSON_DATA" ".audio.numDepCores"; then + depCores=$(get_dante_json_field ".audio.numDepCores") + else + # If numDepCores is not a number, assume it's a non-zero configuration + # (e.g., an array of specific cores). We only need to distinguish + # between 0 (disabled) and non-zero (enabled) for cgroup error handling. + depCores=1 + fi + + if [ "$depCores" -ne 0 ]; then + logerr "one or more required cgroup mounts were not found" + else + logwarn "$cgroupWarningMsg" + fi + else + logok "all required cgroup ${selectedVersion} mounts found" + fi +} + +check_cpu_config() { + configuredCores="" + checkFailed=0 + depProcesses="dep_manager apec DepApe conmon_server mdnsd ptp hwclkcfg DepClockBridge" + + loginfo "validating CPU configuration and runtime affinity settings" + + # utility to check that given a string that string is found in depProcesses + is_dep_process() { + for depProc in $depProcesses; do + if [ "$1" = "$depProc" ]; then + return 0 + fi + done + return 1 + } + + if json_field_exists "$DANTE_JSON_DATA" ".audio.percentCpuShare"; then + logwarn "the percentCpuShare option in $DANTE_JSON was deprecated in DEP 1.5 and can be removed" + fi + + if is_json_field_number "$DANTE_JSON_DATA" ".audio.numDepCores"; then + numCores=$(get_dante_json_field ".audio.numDepCores") + + if [ "$numCores" -eq 0 ]; then + loginfo "numDepCores is set to 0, DEP won't perform CPU affinity allocation - skipping check" + return + fi + + # generate space-separated core IDs from 0 to numCores-1 + i=0 + while [ "$i" -lt "$numCores" ]; do + if [ -z "$configuredCores" ]; then + configuredCores="$i" + else + configuredCores="$configuredCores $i" + fi + i=$((i + 1)) + done + + elif is_json_field_array "$DANTE_JSON_DATA" ".audio.numDepCores"; then + configuredCores=$(get_dante_json_array_elements ".audio.numDepCores") + if [ -z "$configuredCores" ]; then + logerr "numDepCores array is empty in $DANTE_JSON" + return + fi + + else + logerr "numDepCores is not a number or an array in $DANTE_JSON" + return + fi + + # check that each CPU ID specified in configuredCores actually exists on the system + for core in $configuredCores; do + # check CPU ID validity + if ! printf '%s\n' "$core" | grep -Eq '^[0-9]+$'; then + logerr "invalid core ID '$core' in numDepCores - must be a non-negative integer" + checkFailed=1 + continue + fi + + # check if the core is online (exists in /sys/devices/system/cpu/cpu$core) + if [ ! -d "/sys/devices/system/cpu/cpu$core" ]; then + logerr "core ID '$core' in numDepCores does not exist on the system" + checkFailed=1 + fi + done + + # before we start checking processes that might interfere with DEP, we need + # to do some self-examination and collect all the PIDs from 1 (init) to our own PID + currentPid=$$ + currentPidTree="" + while [ "$currentPid" -ne 1 ] && [ -n "$currentPid" ]; do + currentPidTree="$currentPid $currentPidTree" + # try to get the parent PID, but if the process no longer exists, break the loop + if ! currentPid=$(awk '/^PPid:/ { print $2 }' /proc/"$currentPid"/status 2>/dev/null); then + break + fi + done + + # list all user-space processes (excluding kernel tasks) and warn the user for each + # non-dep process running on any of the cores in configuredCores + + matchedPids="" + + # shellcheck disable=SC2010 + for pid in /proc/[0-9]*; do + pid=${pid##*/} + + # skip if not all digits + case "$pid" in + ''|*[!0-9]*) continue ;; + esac + + # check if /proc/$pid still there + [ -d "/proc/$pid" ] || continue + + # skip kernel tasks, read all files in one shot + # + # NOTE: on Linux, /proc/$pid/cmdline is a null-separated string of + # command-line arguments for the given process. + # POSIX shells do not support null bytes inside variables, so to avoid triggering errors like: + # + # warning: command substitution: ignored null byte in input + # + # we read the cmdline file and replace null bytes with spaces. + { + cmdline=$( { tr '\0' ' ' < "/proc/$pid/cmdline"; } 2>/dev/null ) || continue + [ -n "$cmdline" ] || continue + + procName=$(cat "/proc/$pid/comm") || continue + procName=$(printf "%s" "$procName" | sed 's/ //g' | tr -d '\r\n') # remove whitespaces and trim newline + + [ -r "/proc/$pid/stat" ] || continue + cpuCore=$(awk '{print $39}' "/proc/$pid/stat" 2>/dev/null) || continue + } || continue + + # skip processes that are not running on any of the configured cores + if ! echo "$configuredCores" | grep -qw "$cpuCore"; then + continue + fi + + # skip DEP processes + if is_dep_process "$procName"; then + # this is a DEP process, skip it + continue + fi + + if [ -z "$matchedPids" ]; then + matchedPids="name:$procName,PID:$pid,core:$cpuCore" + else + matchedPids="$matchedPids name:$procName,PID:$pid,core:$cpuCore" + fi + done + + if [ -n "$matchedPids" ]; then + logwarn "the following processes/threads are running on one or multiple CPU cores which are also configured for DEP:" + for p in $matchedPids; do + # extract name, PID and core id from the formatted string (name:procName,PID:pid,core:cpuCore) + procName=$(echo "$p" | cut -d',' -f1 | cut -d':' -f2) + procPid=$(echo "$p" | cut -d',' -f2 | cut -d':' -f2) + procCore=$(echo "$p" | cut -d',' -f3 | cut -d':' -f2) + echo "${TAB}name: $procName, PID: $procPid, core: $procCore" + done + logwarn "Please ensure that these processes are not running on the same cores as DEP." + echo "${TAB}DEP to work at its best requires good CPU isolation, which prevents its processing" + echo "${TAB}threads from being interrupted from other unrelated userspace or kernelspace tasks." + echo "${TAB}Consult the DEP manual for more information on how to achieve optimal CPU isolation." + checkFailed=1 + fi + + if [ "$checkFailed" -eq 0 ]; then + logok "DEP CPU configuration is valid - configured cores: $configuredCores" + fi + + # check the scaling governor for each configured core + for core in $configuredCores; do + path="/sys/devices/system/cpu/cpu$core/cpufreq/scaling_governor" + + if [ -r "$path" ]; then + governor=$(cat "$path" 2>/dev/null) + if [ "$governor" = "performance" ]; then + logok "CPU $core scaling governor set to performance" + else + logwarn "CPU $core scaling governor set to '$governor' - recommended to set to 'performance' if kernel supports it" + fi + else + loginfo "CPU $core scaling governor not found or unreadable, skipping check" + fi + done +} + +check_rng_speed() { + if ! cmd_exists /usr/bin/time; then + logwarn "'/usr/bin/time' not found, skipping /dev/random speed test" + return + fi + + if ! cmd_exists dd; then + logwarn "'dd' not found, skipping /dev/random speed test" + return + fi + + # try to use more basic system utilities such as ps and sleep rather than timeout + dd if=/dev/random of=/dev/null bs=1024 count=1 iflag=fullblock >/tmp/dep_check_out 2>&1 & + PID=$! + sleep 1 + kill $PID > /dev/null 2> /dev/null + wait $PID > /dev/null 2> /dev/null + RETVAL=$? + OUTPUT=$(cat /tmp/dep_check_out) + if [ $RETVAL -ne 0 ]; then # dd did not complete successfully + if [ -n "$OUTPUT" ]; then # some output was produced, dd probably had an error + logerr "$OUTPUT" + else # dd did not produce output + logerr "dd was killed before producing any output. /dev/random is probably too slow" + fi + return + fi # dd did not time out + + SPEED_TEST_OUTPUT=$({ /usr/bin/time -p dd if=/dev/random of=/dev/null bs=1024 count=1 iflag=fullblock; } 2>&1 | grep real) + # At this stage, SPEED_TEST_OUTPUT will have a form like "real 0.00". Note that we use the GNU time here which only + # contains two decimal point precision. This does not mean "0.00" took 0 seconds to finish but rather was less than 10 ms. + # The following code converts this output to a millisecond value. + + # remove the "real" part of the output + TIME=${SPEED_TEST_OUTPUT#real } + SECS=${TIME%%.*} # grab the digits before the decimal point + SUB_SECOND=${TIME#*.} + SUB_SECOND=${SUB_SECOND%[0-9]*} # grab the first digit after the decimal point + + if [ "$SECS" -gt "0" ] || [ "$SUB_SECOND" -gt "0" ]; then + logwarn "/dev/random is too slow (took $TIME seconds or more)" + else + logok "/dev/random is sufficiently fast (took less than 100 milliseconds)" + fi + + # cleanup + rm -rf /tmp/dep_check_out +} + +load_config_files() { + jsonSyntaxError=0 + if [ ! -e "$DANTE_JSON" ]; then + logerr "$DANTE_JSON not found" + fail + fi + + if [ -L "$CONFIG_JSON" ]; then + # check where the symlink points to + target="$PATH_TO_BUNDLE_DIR/$(readlink "$PATH_TO_BUNDLE_DIR/config.json")" + if [ -e "$target" ]; then + target=$(realpath "$target") + else + logerr "$target does not exist at the symlink target pointed by '$PATH_TO_BUNDLE_DIR/config.json'" + fail + fi + fi + + if [ ! -e "$CONFIG_JSON" ]; then + logerr "$CONFIG_JSON not found" + fail + fi + + if ! "$JQ" empty "$DANTE_JSON" 2>jq_err; then + logerr "syntax error detected in '$DANTE_JSON': $(cat jq_err)" + jsonSyntaxError=1 + fi + + if ! "$JQ" empty "$CONFIG_JSON" 2>jq_err; then + logerr "syntax error detected in '$CONFIG_JSON': $(cat jq_err)" + jsonSyntaxError=1 + fi + + rm -f jq_err + + if [ "$jsonSyntaxError" -eq 1 ]; then + fail + fi + + # load once instead of parsing multiple times when needed + logok "JSON configuration files loaded" + DANTE_JSON_DATA=$(cat "$DANTE_JSON") + CONFIG_JSON_DATA=$(cat "$CONFIG_JSON") +} + +check_network_interfaces() { + # first, check that all network interfaces specified in $DANTE_JSON actually exist + interfaces=$(get_dante_json_array_elements ".network.interfaces") + for iface in $interfaces; do + if ! ip a show dev "$iface" >/dev/null 2>&1; then + logerr "network interface $iface specified in $DANTE_JSON not found on the system" + else + logok "network interface $iface found" + fi + done + + # check that interfaceMode is set to a valid value, and if it is set to "Direct", + # check network coalescing configuration + if interfaceMode=$(get_dante_json_field ".network.interfaceMode"); then + case $interfaceMode in + Direct | Switched) + loginfo "interfaceMode set to '$interfaceMode'" + ;; + *) + logerr "invalid interfaceMode in dante.json: '$interfaceMode'. Possible values are: Direct, Switched" + ;; + esac + else + # default: Direct + interfaceMode="Direct" + fi + + # if interfaceMode is set to "Switched", checking network coalescing is deferred + # to the check_clock_hardware_interfaces() function, which will check the interfaces + # specified in the "hardwareInterfaces" + if [ "$interfaceMode" = "Direct" ]; then + for iface in $interfaces; do + check_iface_coalesce "$iface" + done + else + DEFER_COA_CHECK=1 + fi + + if ! json_field_exists "$DANTE_JSON_DATA" ".network.preferredLinkSpeed"; then + return + fi + + # check that preferredLinkSpeed is set to a valid value + configuredSpeedValue=$(get_dante_json_field ".network.preferredLinkSpeed") + + case $configuredSpeedValue in + LINK_SPEED_100M) ;; + LINK_SPEED_1G) ;; + LINK_SPEED_10G) ;; + *) + logwarn "preferredLinkSpeed '$configuredSpeedValue' in $DANTE_JSON is not supported." + echo "${TAB}Possible values are: LINK_SPEED_100M, LINK_SPEED_1G, LINK_SPEED_10G" + ;; + esac +} + +check_clock_hardware_interfaces() { + hwTimestampingEnabled=0 + hwTsField=$(get_dante_json_field ".clock.enableHwTimestamping") + hwTsDescription="" + case "$hwTsField" in + true) + hwTimestampingEnabled=1 + hwTsDescription="hardware" + ;; + "v1") + hwTimestampingEnabled=1 + hwTsDescription="PTPv1-only hardware" + ;; + false|"") hwTimestampingEnabled=0 ;; + *) logerr "invalid enableHwTimestamping in dante.json: '$hwTsField'. Possible values are: true, false, \"v1\"" ;; + esac + + dsaTagField="" + if json_field_exists "$DANTE_JSON_DATA" ".clock.dsaTaggedPackets"; then + if ! is_json_field_bool "$DANTE_JSON_DATA" ".clock.dsaTaggedPackets"; then + logerr "'clock.dsaTaggedPackets' in dante.json is not a boolean value. Please set to either true or false" + return + fi + + dsaTagField=$(get_dante_json_field ".clock.dsaTaggedPackets") + if [ "$dsaTagField" = true ]; then + loginfo "DSA tagging in use" + fi + fi + + if [ "$hwTimestampingEnabled" -eq 0 ]; then + loginfo "hardware timestamping is disabled in $DANTE_JSON" + return + fi + + logok "$hwTsDescription timestamping is enabled" + + # check that, if defined, hardwareInterfaces is an array and not empty + hwIfacesCount=0 + hardwareInterfaces="" + networkInterfacesCount=0 + + if is_json_field_array "$DANTE_JSON_DATA" ".clock.hardwareInterfaces"; then + hwIfacesCount=$(get_json_array_length "$DANTE_JSON_DATA" ".clock.hardwareInterfaces") + if [ "$hwIfacesCount" -eq 0 ]; then + logerr "'clock.hardwareInterfaces' in $DANTE_JSON is an empty array" + else + hardwareInterfaces=$(get_dante_json_array_elements ".clock.hardwareInterfaces") + + # make sure number of interfaces specified in network.interfaces and clock.hardwareInterfaces matches + networkInterfacesCount=$(get_json_array_length "$DANTE_JSON_DATA" ".network.interfaces") + fi + elif json_field_exists "$DANTE_JSON_DATA" ".clock.hardwareInterfaces"; then + logerr "'clock.hardwareInterfaces' in $DANTE_JSON is not an array" + fi + + if [ "$dsaTagField" = true ] && [ "$networkInterfacesCount" -ne "$hwIfacesCount" ]; then + logerr "number of interfaces in 'clock.hardwareInterfaces' ($hwIfacesCount) does not match number of interfaces in 'network.interfaces' ($networkInterfacesCount)" + echo "${TAB}When using DSA, the virtual interfaces cannot directly make use of the IEEE1588 packet timestamping available" + echo "${TAB}through the FEC. DEP supports using an alternative 'hardware' interface for timestamping when virtual interfaces" + echo "${TAB}are used. Similar to the 'interfaces' field of dante.json, the 'hardwareInterfaces' field takes an array" + echo "${TAB}of interface identifiers (names or indices). Each value in hardwareInterfaces must correspond to the same" + echo "${TAB}positioned value in the 'interfaces' array." + fi + + # from here, all checks are done only if hardwareInterfaces is populated + if [ "$hwIfacesCount" -eq 0 ]; then + return + fi + + # check all interfaces specified in hardwareInterfaces actually exist + previousInterface="" + for iface in $hardwareInterfaces; do + if [ -n "$previousInterface" ] && [ "$iface" = "$previousInterface" ]; then + # already checked, skip it + continue + fi + previousInterface="$iface" + + if ! ip a show dev "$iface" >/dev/null 2>&1; then + logerr "network interface $iface specified in hardwareInterfaces in $DANTE_JSON not found on the system" + else + logok "network interface $iface found" + # this is a result of the check_network_interfaces() function letting us know that + # the interfaceMode is set to "Switched" and the 'real' network interfaces on which network + # coalescing should be checked are those in hardwareInterfaces, not in interfaces + if [ "$DEFER_COA_CHECK" -eq 1 ]; then + check_iface_coalesce "$iface" + fi + fi + done +} + +# This function checks whether a device node is configured correctly +# in an OCI-compliant containter runtime configuration file (e.g., config.json). +# +# It relies on cgroupVersion being set in dante.json for its functionality: this is +# because different cgroup versions require different ways to configure device nodes +# in the configuration file. +is_device_node_configured() { + deviceNode="$1" + + fileMode=384 # 0600 in octal + gid=0 + uid=0 + allow="true" + access="rw" + + # shellcheck disable=SC2012 + deviceType=$(ls -l "$deviceNode" | cut -c1) + if [ "$deviceType" = "c" ] || [ "$deviceType" = "b" ]; then + : + else + logerr "parsing device node '$deviceNode' type failed, it's not a character or block device" + fi + + # shellcheck disable=SC2012 + major=$(ls -l "$deviceNode" | awk '{print $5}' | sed 's/,//') + # shellcheck disable=SC2012 + minor=$(ls -l "$deviceNode" | awk '{print $6}') + + if ! json_field_exists "$DANTE_JSON_DATA" ".platform.cgroupVersion"; then + # assume cgroup v1 + logwarn "assuming cgroup v1 - please set 'cgroupVersion' in dante.json to allow this check to work correctly" + cgroupVersion="1" + else + cgroupVersion=$(get_dante_json_field ".platform.cgroupVersion") + fi + + # for cgroup v1, device node needs to be also specified in linux.resources.devices[] + foundInLinuxResources=0 + if [ "$cgroupVersion" = "1" ]; then + # entries in linux.resources.devices[] are of the form: + # { + # "allow": true, + # "type": "c", + # "major": 189, + # "minor": 1, + # "access": "rw" + # } + # so it's not easy to identify whether a device node is configured or not. + # What we do is check whether an entry with the same allow, type, major, minor and access fields exists, + # and if it doesn't, print out what it should look like. + + linuxResourcesDevices=$(get_config_json_array_elements ".linux.resources.devices") + for entry in $linuxResourcesDevices; do + if [ "$(get_json_field "$entry" ".allow")" = "$allow" ] && + [ "$(get_json_field "$entry" ".type")" = "$deviceType" ] && + [ "$(get_json_field "$entry" ".major")" = "$major" ] && + [ "$(get_json_field "$entry" ".minor")" = "$minor" ] && + [ "$(get_json_field "$entry" ".access")" = "$access" ]; then + foundInLinuxResources=1 + fi + done + + if [ "$foundInLinuxResources" -eq 0 ]; then + # shellcheck disable=SC2089 + expectedEntry="{\"allow\": $allow,\"type\": \"$deviceType\",\"major\": $major,\"minor\": $minor,\"access\": \"$access\"}" + logerr "device node '$deviceNode' not found in linux.resources.devices[], check that an" + echo "${TAB}identical entry like the following one exists in $CONFIG_JSON:" + # shellcheck disable=SC2090 + # shellcheck disable=SC2086 + echo $expectedEntry | $JQ | sed 's/^/ /' + fi + else + foundInLinuxResources=1 + fi + + foundInLinuxDevices=0 + misconfiguredEntryFound=0 + linuxDevices=$(get_config_json_array_elements ".linux.devices") + linuxDeviceEntryFields=".type:$deviceType .major:$major .minor:$minor .fileMode:$fileMode .gid:$gid .uid:$uid" + + for entry in $linuxDevices; do + if [ "$(get_json_field "$entry" ".path")" = "$deviceNode" ]; then + foundInLinuxDevices=1 + + for f in $linuxDeviceEntryFields; do + field=$(echo "$f" | cut -d':' -f1) + expectedValue=$(echo "$f" | cut -d':' -f2) + + # check if the entry has the correct field and value + configuredValue=$(get_json_field "$entry" "$field") + + if [ "$configuredValue" != "$expectedValue" ]; then + logerr "device node '$deviceNode' has incorrect $field in $CONFIG_JSON: expected '$expectedValue', got '$configuredValue' in linux.devices[]" + misconfiguredEntryFound=1 + fi + done + fi + done + + if [ $foundInLinuxDevices -eq 0 ]; then + logerr "device node '$deviceNode' not found in $CONFIG_JSON: expected entry with path '$deviceNode' in linux.devices[]" + echo "${TAB}Please add the device node to the 'linux.devices[]' array in $CONFIG_JSON." + return 0 + fi + + if [ $foundInLinuxDevices -eq 1 ] && [ $foundInLinuxResources -eq 1 ] && [ $misconfiguredEntryFound -eq 0 ]; then + logok "'$deviceNode' configured correctly" + return 1 + fi +} + +# For both the primary and secondary (if being used) network interfaces, perform a +# preliminary check of all timestamping modes using the PTP test tool. For each mode, +# the tool will attempt to create a network socket confgured with that mode. This is +# therefore a much more definitive check than relying solely on capability reports +# provided by ethtool (which may not be 100% accurate). +# +# If DSA tagging is set and software timestamping has been configured in dante.json, +# the preliminary checks for hardware timestamping are simply skipped as the hardware +# interface(s) are deliberately ignored (even if defined). +# +# The return value is an array of strings, where each string is itself a collection +# of the following substrings separated by a '@' character: +# +# - Timestamping interface +# - The timestamping mode description (hardware/software/PTPv1-only hardware) +# - A 1 or 0 indicating if this mode is the one in dante.json +# - The return value from the PTP test tool (0 = success, 1 = failure) +# - The complete command line needed to run either a full timestamping test (if the +# check succeeded) or to run the preliminary check again (if it failed) +# +# NOTE: to allow the caller to iterate through the array using for(), the substrings +# cannot have any spaces at all. For this reason, a string will have all its spaces +# substituted with '+' characters. The caller, when looking at an array entry, is +# expected to replace the '+'s with spaces again and to simply use cut -d'@' to get +# at each substring. +timestamping_preliminary_checks() { + interfaceName=$1 + danteJsonEnableHwTs=$2 + dsaTaggedPackets=$3 + hwInterfaceName=$4 + + # Create an array of modes to test, where the mode configured in dante.json is the + # first entry. This is done so that the caller, when reporting the check results, + # will show that mode first. + if [ "$danteJsonEnableHwTs" = true ]; then + configuredTsMode="-hw" + tsModesToTest="-hw -hwv1 -sw" + elif [ "$danteJsonEnableHwTs" = "v1" ]; then + configuredTsMode="-hwv1" + tsModesToTest="-hwv1 -hw -sw" + else + configuredTsMode="-sw" + if [ "$dsaTaggedPackets" = true ]; then + tsModesToTest="-sw" + else + tsModesToTest="-sw -hw -hwv1" + fi + fi + + for tsMode in $tsModesToTest; do + if [ "$tsMode" = "-hw" ]; then + tsModeDescription="hardware" + elif [ "$tsMode" = "-hwv1" ]; then + tsModeDescription="PTPv1-only hardware" + else + tsModeDescription="software" + fi + + timestampingInterface=$interfaceName + dsaArgs="" + + # Any DSA arguments will be appended to the end of the standard PTP test tool command line + # arguments. We don't want any trailing spaces for the command lines we show the user, and + # so the DSA arguments will have a leading space. + if [ "$dsaTaggedPackets" = true ]; then + if [ "$tsMode" = "-hw" ] || [ "$tsMode" = "-hwv1" ]; then + dsaArgs=" -dsa -hwi $hwInterfaceName" + timestampingInterface=$hwInterfaceName + else + dsaArgs=" -dsa" + fi + fi + + ptpTestToolCmdLine="${PTP_TEST} -i $interfaceName $tsMode$dsaArgs" # No space between $tsMode and $dsaArgs + + # shellcheck disable=SC2086 + ${ptpTestToolCmdLine} -c > /dev/null 2>&1 + retval=$? + if [ "$tsMode" = "$configuredTsMode" ]; then + isConfiguredTsMode=1 + else + isConfiguredTsMode=0 + fi + + # If the check failed, the command line we pass back will be to run a preliminary + # rather than a full check. + if [ $retval -eq 1 ]; then + ptpTestToolCmdLine="$ptpTestToolCmdLine -c" + fi + + # Substitute all spaces in the output string with the '+' character + echo "$timestampingInterface@$tsModeDescription@$isConfiguredTsMode@$retval@$ptpTestToolCmdLine" | sed 's/ /+/g' + done +} + +check_timestamping_config() { + hwTsField=$(get_dante_json_field ".clock.enableHwTimestamping") + networkInterfaces=$(get_dante_json_array_elements ".network.interfaces") + + if json_field_exists "$DANTE_JSON_DATA" ".clock.dsaTaggedPackets"; then + if ! is_json_field_bool "$DANTE_JSON_DATA" ".clock.dsaTaggedPackets"; then + logwarn "'clock.dsaTaggedPackets' is invalid - skipping timestamping configuration check" + return + fi + dsaTaggedPackets=$(get_dante_json_field ".clock.dsaTaggedPackets") + else + dsaTaggedPackets=false + fi + + clockHwInterfaces="" + if [ "$hwTsField" = true ] || [ "$hwTsField" = "v1" ]; then + # Check if AES67 is being supported (if so, PTPv1 hardware timestamping cannot be used) + aes67Supported=$(get_dante_json_field ".audio.aes67Supported") + + if [ "$aes67Supported" = true ] && [ "$hwTsField" = "v1" ]; then + logerr "AES67 cannot be supported with PTPv1 timestamping." + echo "${TAB}To enable AES67, enableHwTimestamping must be set to either true or false." + return + fi + + # Hardware timestamping is in use, so check PHCs. + # + # If dsaTaggedPackets is false, the NICs whose PHCs need to be checked are those in + # .network.interfaces. + if [ "$dsaTaggedPackets" = true ]; then + clockHwInterfaces=$(get_dante_json_array_elements ".clock.hardwareInterfaces") + + # $clockHwInterfaces will be empty if it is either undefined or invalid. + # Error and return in this case. + if [ -z "$clockHwInterfaces" ]; then + logerr "'clock.hardwareInterfaces' is undefined or invalid - unable to check PHC(s) or run preliminary hardware timestamping checks" + return + fi + + ifacesToCheck=$clockHwInterfaces + else + ifacesToCheck=$networkInterfaces + fi + + previousPHC="" + for iface in $ifacesToCheck; do + # $iface needs to be associated with a PHC device for hardware timestamping to work + phcName="" + + ptpPath="/sys/class/net/$iface/device/ptp" + if [ -d "$ptpPath" ]; then + for d in "$ptpPath"/*; do + if [ -d "$d" ] && [ -f "$d/clock_name" ]; then + phcName=$(basename "$d") + deviceNode="/dev/$phcName" + fi + done + elif cmd_exists ethtool; then + phc=$(ethtool -T "$iface" 2>/dev/null | awk "/PTP Hardware Clock:/ {print \$4}") + if [ -n "$phc" ] && [ "$phc" != "none" ]; then + phcName="ptp$phc" + deviceNode="/dev/$phcName" + fi + fi + + if [ -z "$phcName" ]; then + logerr "no PTP device found for $iface." + echo "${TAB}hardware timestamping cannot be enabled when using this interface." + return + fi + + if [ -n "$previousPHC" ] && [ "$previousPHC" = "$phcName" ]; then + # already checked this node, skip it + continue + fi + previousPHC="$phcName" + + loginfo "$iface: PHC device found ($phcName)" + + if [ ! -e "$deviceNode" ]; then + logerr "corresponding PTP device node '$deviceNode' not found for $iface interface" + continue + fi + + # we have a PHC node, has the user configured it in config.json? + is_device_node_configured "$deviceNode" + done + fi + + # Perform preliminary timestamping checks using the PTP timestamping test tool + if [ "$PTP_TEST_COMPATIBLE" -ne 1 ]; then + # Do a test run of the tool to see if the host's glibc really is incompatible. + # If it is, the checks are skipped. + ${PTP_TEST} -h > /dev/null 2>&1 + if [ $? -ne 0 ]; then + logwarn "host-provided glibc ($installedGlibc) is older than required ($REQUIRED_GLIBC_PTP_TEST) for ${PTP_TEST}" + echo "${TAB}Skipping preliminary timestamping checks" + return + fi + fi + + # Initially, we assume that all timestamping modes will pass their preliminary checks. + # If EITHER the primary or secondary interface has a failed or skipped check for a + # particular timestamping mode, that mode has its check marked as having failed. + hwTsPrelimCheckOk=1 + hwV1TsPrelimCheckOk=1 + swTsPrelimCheckOk=1 + + if [ "$dsaTaggedPackets" = true ] && [ "$hwTsField" = false ]; then + loginfo "DSA tagging in use along with software timestamping - skipping preliminary hardware timestamping checks." + echo "${TAB}To perform these checks, please rerun this script after turning on hardware timestamping and ensuring" + echo "${TAB}that 'clock.hardwareInterfaces' contains valid entries." + hwTsPrelimCheckOk=0 + hwV1TsPrelimCheckOk=0 + fi + + primaryCheckCmdsAndResults="" + secondaryCheckCmdsAndResults="" + + ifaceIndex=0 + hwIface="" + for iface in $networkInterfaces; do + if [ -n "$clockHwInterfaces" ]; then + hwIface=$(echo "$DANTE_JSON_DATA" | "$JQ" -cr ".clock.hardwareInterfaces[$ifaceIndex]") + if [ "$hwIface" = null ]; then + # We'll hit this if clock.hardwareInterfaces has fewer entries than + # network.interfaces. This error will already have been reported in + # an earlier check, but it will also interfere with proper timestamping + # checking and so log another error here and exit this function. + logerr "Unable to perform preliminary timestamping check for interface $iface, as associated 'clock.hardwareInterfaces' entry is missing" + return + fi + fi + + # NOTE: if we're now checking the secondary interface, it's possible that + # $hwIface is common to both the primary and secondary. The preliminary checks + # below will end up checking hardware timestamping on $hwIface a second time - + # even though this is arguably redundant, we still want to do this in order to + # be able to show the user the full timestamping test command lines (which will + # feature the primary and secondary interfaces respectively). + checkCmdsAndResults=$(timestamping_preliminary_checks "$iface" "$hwTsField" "$dsaTaggedPackets" "$hwIface") + + if [ "$ifaceIndex" -eq 0 ]; then + primaryCheckCmdsAndResults="$checkCmdsAndResults" + else + secondaryCheckCmdsAndResults="$checkCmdsAndResults" + fi + + ifaceIndex=$((ifaceIndex+1)) + done + + # Keep track of the PTP test tool commands that can be used to do full tests for all + # interfaces and each timestamping mode. Depending on which modes is later deemed as + # recommended, the appropriate set of commands will be shown to the user. + hwTsFullCheckCmds="" + hwV1TsFullCheckCmds="" + swTsFullCheckCmds="" + + # We need to report on the success/failure of the check of the timestamping mode in + # dante.json + danteJsonHwTsDescription="" + danteJsonHwTsFullCheckCmds="" + danteJsonFailedPrelimCheckCmds="" + + # Display all preliminary check results first + for check in $primaryCheckCmdsAndResults $secondaryCheckCmdsAndResults; do + iface=$(echo "$check" | cut -d'@' -f1) + hwTsDescription=$(echo "$check" | cut -d'@' -f2 | sed 's/+/ /g') # Restore any spaces + isDanteJsonSetting=$(echo "$check" | cut -d'@' -f3) + checkRetval=$(echo "$check" | cut -d'@' -f4) + + # Don't restore spaces in the command line for now, as it will be added to another + # array. Spaces will only be restored if the command line ends up being displayed. + ptpTestToolCmdLine=$(echo "$check" | cut -d'@' -f5) + + logMsgPrefix="Preliminary check of $hwTsDescription timestamping on interface $iface" + + if [ "$checkRetval" -eq 0 ]; then + if [ "$isDanteJsonSetting" -eq 1 ]; then + logok "$logMsgPrefix passed" + + danteJsonHwTsDescription="$hwTsDescription" + danteJsonHwTsFullCheckCmds="$danteJsonHwTsFullCheckCmds $ptpTestToolCmdLine" + else + loginfo "$logMsgPrefix passed" + fi + + # Add the command line to the ones for the current timestamping mode + case "$hwTsDescription" in + "hardware") + hwTsFullCheckCmds="$hwTsFullCheckCmds $ptpTestToolCmdLine" + ;; + "software") + swTsFullCheckCmds="$swTsFullCheckCmds $ptpTestToolCmdLine" + ;; + *) + # PTPv1 hardware + hwV1TsFullCheckCmds="$hwV1TsFullCheckCmds $ptpTestToolCmdLine" + ;; + esac + else + if [ "$isDanteJsonSetting" -eq 1 ]; then + logerr "$logMsgPrefix failed" + + danteJsonHwTsDescription="$hwTsDescription" + danteJsonFailedPrelimCheckCmds="$danteJsonFailedPrelimCheckCmds $ptpTestToolCmdLine" + else + logwarn "$logMsgPrefix failed" + fi + + case "$hwTsDescription" in + "hardware") + hwTsPrelimCheckOk=0 + ;; + "software") + swTsPrelimCheckOk=0 + ;; + *) + # PTPv1 hardware + hwV1TsPrelimCheckOk=0 + ;; + esac + fi + done + + # If no timestamping modes passed their preliminary check, log an error and return + if [ "$hwTsPrelimCheckOk" -eq 0 ] && [ "$hwV1TsPrelimCheckOk" -eq 0 ] && [ "$swTsPrelimCheckOk" -eq 0 ]; then + if [ -z "$secondaryCheckCmdsAndResults" ]; then # No secondary + logerr "Primary interface does not support any of the required timestamping modes and cannot be used." + echo "${TAB}Please specify an alternative interface in dante.json." + else + logerr "Primary and secondary interfaces do not support any of the required timestamping modes in common." + echo "${TAB}Please remove the secondary interface from dante.json and/or change the interfaces." + fi + return + fi + + # Internal function for displaying an array of PTP test tool commands. The array will + # have two entries if using a primary and secondary interface, one otherwise. These + # commands, obtained from timestamping_preliminary_checks(), need to have their + # placeholder '+' characters changed back to spaces. + display_ptp_test_tool_cmds() { + for cmd in $1; do + restoredCmd=$(echo "$cmd" | sed 's/+/ /g') + echo "${TAB}$restoredCmd" + done + } + + display_full_timestamping_test_advice() { + hwTsDescription=$1 + hwTsFullCheckCmds=$2 + referToReadme=$3 + + echo "${TAB}Please be sure to run the following command(s) to perform a full $hwTsDescription timestamping test:" + display_ptp_test_tool_cmds "$hwTsFullCheckCmds" + if [ "$referToReadme" -eq 1 ]; then + echo "${TAB}Please see $PTP_TEST_README for advice on running a full test." + fi + } + + # Determine the recommended timestamping setting + recommendedHwTsDescription="" + recommendedHwTsFullCheckCmds="" + + if [ "$hwTsPrelimCheckOk" -eq 1 ]; then + recommendedHwTsDescription="hardware" + recommendedHwTsFullCheckCmds="$hwTsFullCheckCmds" + elif [ "$swTsPrelimCheckOk" -eq 1 ]; then + if [ "$hwV1TsPrelimCheckOk" -eq 1 ]; then + recommendedHwTsDescription="either software or PTPv1-only hardware" + recommendedHwTsFullCheckCmds="$swTsFullCheckCmds $hwV1TsFullCheckCmds" + else + recommendedHwTsDescription="software" + recommendedHwTsFullCheckCmds="$swTsFullCheckCmds" + fi + else + recommendedHwTsDescription="PTPv1-only hardware" + recommendedHwTsFullCheckCmds="$hwV1TsFullCheckCmds" + fi + + referToReadme=1 + + # Check whether the timestamping setting in dante.json passed or not + if [ -z "$danteJsonFailedPrelimCheckCmds" ]; then + logok "All preliminary checks passed for $danteJsonHwTsDescription timestamping." + if [ "$recommendedHwTsDescription" = "$danteJsonHwTsDescription" ]; then + echo "${TAB}Based on the checks above, $danteJsonHwTsDescription timestamping is the recommended setting." + fi + display_full_timestamping_test_advice "$danteJsonHwTsDescription" "$danteJsonHwTsFullCheckCmds" "$referToReadme" + + # If the dante.json setting is the recommended one, there's nothing more to do + if [ "$recommendedHwTsDescription" = "$danteJsonHwTsDescription" ]; then + return + fi + + # Prepare to offer advice on an alternative, recommended setting + however=" however," + referToReadme=0 # We've displayed it already, so don't do it again + else + logwarn "Preliminary check(s) failed for $danteJsonHwTsDescription timestamping." + echo "${TAB}Therefore, $danteJsonHwTsDescription timestamping cannot be used with the interface(s) configured in dante.json." + echo "${TAB}For more details on the failure(s), the following command(s) can be run:" + display_ptp_test_tool_cmds "$danteJsonFailedPrelimCheckCmds" + + # Suggest the recommended setting(s) + however="" + fi + + loginfo "Based on the checks above,$however the recommended setting is $recommendedHwTsDescription timestamping." + if [ "$recommendedHwTsDescription" = "hardware" ]; then + if [ "$danteJsonHwTsDescription" = "PTPv1-only hardware" ]; then + echo "${TAB}This will enable use of PTPv2 features such as AES67 and site/domain unicast clocking." + else + echo "${TAB}This will provide better clocking stability." + fi + elif [ "$recommendedHwTsDescription" = "either software or PTPv1-only hardware" ]; then + # Explain the tradeoffs for each mode, however only display the full test advice + # if it has not already been displayed. + echo "${TAB}With software, clocking is not as accurate as hardware but PTPv2 features such as" + echo "${TAB}AES67 and site/domain unicast clocking can be used." + if [ "$danteJsonHwTsDescription" != "software" ]; then + display_full_timestamping_test_advice "software" "$swTsFullCheckCmds" "$referToReadme" + referToReadme=0 + fi + echo "${TAB}With PTPv1-only hardware, clocking will be more stable but PTPv2 features such as" + echo "${TAB}AES67 and site/domain unicast clocking cannot be used." + if [ "$danteJsonHwTsDescription" != "PTPv1-only hardware" ]; then + display_full_timestamping_test_advice "PTPv1-only hardware" "$hwV1TsFullCheckCmds" "$referToReadme" + fi + + # Advice displayed, so nothing more to do + return + elif [ "$recommendedHwTsDescription" = "software" ]; then + # If v1 was configured but is not supported, explain that PTPv2 can now be used + if [ -n "$danteJsonFailedPrelimCheckCmds" ] && [ "$danteJsonHwTsDescription" = "PTPv1-only hardware" ]; then + echo "${TAB}Clocking is not as accurate as hardware, but PTPv2 features such as AES67 and" + echo "${TAB}site/domain unicast clocking can be used." + fi + else # $recommendedHwTs = "v1" + # If software was configured but is not supported, explain that PTPv2 can't be used + if [ -n "$danteJsonFailedPrelimCheckCmds" ] && [ "$danteJsonHwTsDescription" = "software" ]; then + echo "${TAB}This will provide better clocking stability, but PTPv2 features such as AES67 and" + echo "${TAB}site/domain unicast clocking cannot be used." + fi + fi + + display_full_timestamping_test_advice "$recommendedHwTsDescription" "$recommendedHwTsFullCheckCmds" "$referToReadme" +} + +check_hw_clock_config() { + if ! json_field_exists "$DANTE_JSON_DATA" ".hardwareClock.useHwClock"; then + return + fi + + if ! is_json_field_bool "$DANTE_JSON_DATA" ".hardwareClock.useHwClock"; then + logerr "'hardwareClock.useHwClock' in dante.json is not a boolean value. Please set to either true or false" + return + fi + + useHwClockField=$(get_dante_json_field ".hardwareClock.useHwClock") + + if [ "$useHwClockField" = false ]; then + return + fi + + loginfo "hardware clock enabled in $DANTE_JSON" + + circuitNameField=$(get_dante_json_field ".hardwareClock.circuitName") + if [ -z "$circuitNameField" ]; then + logerr "'hardwareClock.circuitName' is not set in $DANTE_JSON." + echo "${TAB}Please specify the circuit name for the hardware clock." + fi + + i2cBusField=$(get_dante_json_field ".hardwareClock.i2cBus") + if [ "$i2cBusField" = null ]; then + i2cBusField="/dev/i2c-0" # default value + fi + + if [ ! -e "$i2cBusField" ]; then + logerr "i2c bus device node '$i2cBusField' not found on the system." + echo "${TAB}Please check the 'hardwareClock.i2cBus' field in $DANTE_JSON." + else + is_device_node_configured "$i2cBusField" + fi + + extClockInputDevField=$(get_dante_json_field ".hardwareClock.extClockInputDev") + if [ "$extClockInputDevField" = null ]; then + extClockInputDevField="/dev/extclkin" # default value + fi + + if [ ! -e "$extClockInputDevField" ]; then + logerr "external clock input device node '$extClockInputDevField' not found on the system." + echo "${TAB}Please check the 'hardwareClock.extClockInputDev' field in $DANTE_JSON." + else + is_device_node_configured "$extClockInputDevField" + fi +} + +# Check if an encoding value is valid. +# Returns: +# - 1 if the encoding is a standard PCM encoding (PCM16, PCM24, PCM32) +# - 2 if the encoding is a custom value (between 0x1300 and 0x13FF inclusive) +# - 0 otherwise (invalid encoding) +is_encoding_valid() { + encoding="$1" + + encodingNumeric=$(printf "%d" "$encoding" 2>/dev/null) + # shellcheck disable=SC2181 + if [ $? -eq 0 ]; then + # check if encoding is in 0x1300โ€“0x13FF range + case "$encoding" in + 0x13[0-9A-Fa-f][0-9A-Fa-f]) + if [ "$encodingNumeric" -ge 4864 ] && [ "$encodingNumeric" -le 5119 ]; then + echo 2 + return + fi + ;; + esac + fi + + # standard PCM encodings + case "$encoding" in + PCM16|PCM24|PCM32) + echo 1 + ;; + *) + echo 0 + ;; + esac +} + +check_encodings() +{ + defaultEncoding="" + + if json_field_exists "$DANTE_JSON_DATA" "audio.defaultEncoding"; then + defaultEncoding=$(get_dante_json_field ".audio.defaultEncoding") + encodingType=$(is_encoding_valid "$defaultEncoding") + if [ "$encodingType" -eq 0 ]; then + logerr "defaultEncoding $defaultEncoding is invalid in $DANTE_JSON" + elif [ "$encodingType" -eq 2 ]; then + defaultEncoding=$(echo "$defaultEncoding" | tr '[a-f]' '[A-F]') + fi + logwarn "the 'defaultEncoding' field should no longer be used and will be removed in future versions of DEP." + echo "${TAB}The default should instead be specified as the first entry in 'supportedEncodings'" + fi + + if [ -z "$defaultEncoding" ] && ! json_field_exists "$DANTE_JSON_DATA" ".audio.supportedEncodings"; then + loginfo "neither 'supportedEncodings' nor 'defaultEncoding' specified - PCM24 will be used by default" + return + fi + + if ! is_json_field_array "$DANTE_JSON_DATA" ".audio.supportedEncodings"; then + logerr "'supportedEncodings' in $DANTE_JSON is not an array" + return + fi + + supportedEncodingsCount=$(get_json_array_length "$DANTE_JSON_DATA" ".audio.supportedEncodings") + if [ "$supportedEncodingsCount" -eq 0 ]; then + logerr "'supportedEncodings' cannot be specified as empty in $DANTE_JSON" + return + fi + + supportedEncodings=$(get_dante_json_array_elements ".audio.supportedEncodings") + + # check for duplicates in supportedEncodings + if echo "$DANTE_JSON_DATA" | "$JQ" -e '.audio.supportedEncodings | sort | group_by(.) | any(length > 1)' > /dev/null; then + logerr "'supportedEncodings' contains duplicate entries in $DANTE_JSON" + fi + + customEncodingCount=0 + loopIndex=0 + + for encoding in $supportedEncodings; do + loopIndex=$((loopIndex+1)) + encodingType=$(is_encoding_valid "$encoding") + + if [ "$encodingType" -eq 0 ]; then + logerr "'supportedEncodings' contains the invalid encoding $encoding in $DANTE_JSON" + elif [ "$encodingType" -eq 2 ]; then + customEncodingCount=$((customEncodingCount+1)) + if [ $customEncodingCount -gt 4 ]; then + logerr "'supportedEncodings' cannot contain more than 4 custom encodings in $DANTE_JSON" + fi + # convert encoding string to uppercase + encoding=$(echo "$encoding" | tr '[a-f]' '[A-F]') + fi + + # if defaultEncoding is specified and we are looping over supportedEncodings, + # check that it matches the first entry in supportedEncodings + if [ -n "$defaultEncoding" ] && [ "$loopIndex" -eq 1 ]; then + if [ "$encoding" != "$defaultEncoding" ]; then + logerr "'supportedEncodings' does not match the specified 'defaultEncoding' in $DANTE_JSON" + fi + fi + done +} + +check_old_files() { + mainPath=${DEP_PATH}"/" + + oldRootfsFound=0 + if [ -f "$mainPath/dante_data/images/1/rootfs_squash" ]; then + oldRootfsFound=1 + fi + + # any old file around? + if ls -1 "$mainPath" | grep -qE '^(runc|fixer|dep_util.sh|dep.service|select_image.sh)$' || [ $oldRootfsFound -eq 1 ]; then + loginfo "These files can be removed to reduce disk usage:" + else + return + fi + + if [ -f "$mainPath/runc" ]; then + echo "${TAB}runc (old container runtime)" + fi + if [ -f "$mainPath/fixer" ]; then + echo "${TAB}fixer (deprecated and no longer used)" + fi + if [ -f "$mainPath/dep_util.sh" ]; then + echo "${TAB}dep_util.sh (deprecated and no longer used)" + fi + if [ -f "$mainPath/dep.service" ]; then + echo "${TAB}dep.service (old systemd service file, a new updated version is available in the development subdirectory)" + fi + if [ -f "$mainPath/select_image.sh" ]; then + echo "${TAB}select_image.sh (not used anymore)" + fi + if [ "$oldRootfsFound" -eq 1 ]; then + echo "${TAB}dante_data/images/1/rootfs_squash (second rootfs image deprecated and no longer used)" + fi +} + +### main logic + +# check we are running as root +check_uid + +# make sure the first arg is a valid path +check_arg "$1" + +check_runtime_deps + +load_config_files + +check_architecture +check_kernel_config +check_cgroups +check_cpu_config +check_rng_speed +check_network_interfaces +check_clock_hardware_interfaces +check_timestamping_config +check_hw_clock_config +check_encodings +check_old_files + +# +# Copyright ยฉ 2022-2025 Audinate Pty Ltd ACN 120 828 006 (Audinate). All rights reserved. +# diff --git a/src/dep/dante_package/development/tools/README_ptp.txt b/src/dep/dante_package/development/tools/README_ptp.txt new file mode 100644 index 0000000..169b6bf --- /dev/null +++ b/src/dep/dante_package/development/tools/README_ptp.txt @@ -0,0 +1,429 @@ +PTP TIMESTAMPING TEST TOOL OVERVIEW +=================================== + +ptp_timestamping_test is a standalone tool that can be used independently of DEP +itself to do a definitive check of a network driver's timestamping capabilities. + +After installing DEP, this tool should ALWAYS be run before starting DEP for the +first time. This is to ensure that the driver(s) for the selected network interface(s) +are able to properly timestamp all PTP event packets using the required timestamping +mode (i.e. hardware, PTPv1-only hardware or software). + +If problems are found with the driver(s) when timestamping in a given mode, DEP +should NOT be started in that mode, as otherwise PTP will either fail to start +or will experience synchronisation issues. + +In such a case, in order to run DEP: + +- To continue using the given mode, the driver(s) must be updated and the tool + rerun until problems are no longer reported +- Or, another timestamping mode that shows no problems must be used instead + + +CONTENTS +======== + +1. Test types +2. Test requirements + a. Preliminary check + b. Full test +3. Running tests + a. Via dep_check.sh + b. Running manually +4. Understanding test results + a. Preliminary check + b. Full test + c. Full test output examples +5. Using event + error logging +6. Test behaviour and duration + a. Test packet receive and send behaviour + b. Test duration + + +1. TEST TYPES +============= + +There are two types of tests that can be done with the tool: + +1) A preliminary check of whether a driver actually supports configuring a + particular timestamping mode. This test tries to create a network socket + configured to use the selected mode. The success or failure of this test + is a much more reliable indicator of configuration support than the output + of 'ethtool -T', as a driver's reported claims are not always 100% accurate. + Nevertheless, as a guide to the user the tool gathers and displays the + equivalent information shown via 'ethtool -T'. + +2) A full test. This uses a configured network socket to send and receive + all PTP event packet types, and checks that they all get timestamped. + +A full test is the only real way to determine if timestamping is fully operational. +A simple preliminary check is NOT a substitute for this. + +However, if a timestamping mode fails a preliminary check it means that mode +CANNOT be used (unless the driver can be updated/rectified). + + +2. TEST REQUIREMENTS +==================== + +The tool must always be run as root. + +a. Preliminary check +-------------------- + +Running a preliminary check has no extra requirements. This check can be done +at any time. + +b. Full test +------------ + +The device being tested must be connected to a Dante network. And, in addition +to this device: + +- At least two Dante devices (one leader, and at least one follower) must be + present on the network +- If AES67 and/or site/domain unicast clocking are going to be used, at least + two PTPv2-capable devices (again, one leader and at least one follower) must + be present: + * At least one of these must be a third-party (i.e. non-Dante) device + * Two Dante devices running in AES67 mode are NOT a suitable substitute, as + without a third-party device there will be no PTPv2 follower devices + +Points of note: + +- ALL devices (both Dante and third-party) MUST be in the unmanaged domain (i.e. + in the default subdomain for PTPv1, and in domain number 0 for PTPv2) +- The tool assumes that all PTP traffic is multicast. Therefore, any follower + devices configured to use unicast delay requests will not be visible and thus + cannot be used +- If only one device of each PTPv1/PTPv2 type is present (as a leader), the + tool can still be run but it will report that the test is incomplete +- Running the test will NOT cause clock synchronisation to be disrupted on the + network + + +3. RUNNING TESTS +================ + +a. Via dep_check.sh +------------------- + +dep_check.sh will use the tool to do preliminary checks of all three timestamping +modes for the interface(s) configured in dante.json. Upon completion, it will +display a complete command line(s) (with parameters based on the settings in +dante.json) that the user can run to do a full test(s) for: + +- The mode in dante.json, and/or +- The recommended mode (if different to the above), based on the check results + +If the timestamping mode set in dante.json fails a preliminary check, dep_check.sh +will show a complete command line the user can run to see the full output of the +check if desired. + + +b. Running manually +------------------- + +Running the tool with the -h option will show how the tool can be used. + +As stated above dep_check.sh provides complete command lines for convenience, +however manual usage is fairly straightforward: + +- The value for -i is the interface under "network" in dante.json +- The timestamping mode can be set as required +- If "dsaTaggedPackets" in dante.json is set to true, -dsa must be specified +- If -dsa is used along with either -hw or -hwv1, a value must be supplied + for -hwi. This is the entry under "clock.hardwareInterfaces" in dante.json + that corresponds to the value supplied for -i +- By default, the tool will run a full test. Specifying -c will result in the + tool running only a preliminary check + + +4. UNDERSTANDING TEST RESULTS +============================= + +a. Preliminary check +-------------------- + +The result will be a simple pass or fail, depending on whether a network socket +could be configured with the chosen settings. + +The tool will also display the timestamping capabilities reported by the driver +(the same ones shown via 'ethtool -T') and indicate whether these match what the +user wants. HOWEVER, even if the reported capabilities do not match the check +still proceeds to do a network socket setup by explaining that it MAY succeed- +in spite of the mismatch. + +b. Full test +------------ + +For timestamping to be deemed operational, the driver MUST be able to timestamp: + +- Both PTP event packet types (SYNC and DELAY_REQ) +- In both directions + +Furthermore, in order to be able to run DEP: + +- To use Dante audio, PTPv1 timestamping must fully work +- To use AES67 with a third-party device and to enable site/domain unicast + clocking, PTPv2 timestamping must also work + +The test output provides a terse summary of whether timestamping for a particular +version of PTP was: + +- All OK +- Found to have errors for a particular packet type + timestamping direction +- Not (completely) tested due to lack of devices present + +Refer to the next section for some example full test outputs. + +The tool will display detailed results for the packet version + type(s) that +encountered any timestamping errors. These results provide specific information +on the number of packets sent/received, the number successfully timestamped and +how many attempts resulted in errors. This can be useful when debugging a network +driver. If however, yet more specific details are required about the types of +errors and/or the relative times at which packet events + timestamping operations +occur, the logging option can be used. This is discussed further below. + +c. Full test output examples +---------------------------- + +On a device with a working driver, assuming both PTPv1 and PTPv2 leaders and +followers are present the output will look like the following: + + # ./ptp_timestamping_test -i eno2 -hw + Using interface eno2, with hardware timestamping + Checking PHC resolution... + Checking PHC read speed... 4283ns (approx.) + + Testing PTP timestamping... + + Testing v1 SYNC packets... OK + Testing v1 DELAY_REQ packets... OK + Testing v2 SYNC packets... OK + Testing v2 DELAY_REQ packets... OK + + + TEST SUMMARY + ============ + + PTPv1 all OK + PTPv2 all OK + +Points of note: + +- When using hardware timestamping, the first thing the tool does is perform some + measurements of the PHC (PTP Hardware Clock) +- The approximate read speed of the PHC should be noted. NICs that perform + timestamping at the PHY instead of the MAC layer will exhibit very large values + here (greater than about 100,000ns), and these are typically unsuitable for use + with DEP as the slow read times will adversely affect its PTP accuracy + +The outputs below were produced on a board which has problems with hardware +timestamping (but not software timestamping). Also: + +- The Dante network used had two devices +- One of the devices had AES67 turned on (and so there was a PTPv2 leader + present, but no PTPv2 followers) + +Software timestamping result: + + # ./ptp_timestamping_test -i eth1 + Using interface eth1, with software timestamping + + Testing PTP timestamping... + + Testing v1 SYNC packets... OK + Testing v1 DELAY_REQ packets... OK + Testing v2 SYNC packets... OK + Testing v2 DELAY_REQ packets... none detected + + + TEST SUMMARY + ============ + + PTPv1 all OK + PTPv2 only partially tested - no follower devices detected + +Hardware timestamping result: + + # ./ptp_timestamping_test -i eth1 -hw + Using interface eth1, with hardware timestamping + Checking PHC resolution... + Checking PHC read speed... 3928ns (approx.) + + Testing PTP timestamping... + + Testing v1 SYNC packets... OK + Testing v1 DELAY_REQ packets... Tx problems + Testing v2 SYNC packets... OK + Testing v2 DELAY_REQ packets... none detected + + + TEST SUMMARY + ============ + + PTPv1 SYNCs OK, errors found with DELAY_REQs - details below + PTPv2 only partially tested - no follower devices detected + + + DETAILED RESULTS + ================ + + v1 DELAY_REQ + ------------ + Rx packet receive limit: 5 + Rx packets received: 5 + Rx packets timestamped: 5 + Rx packet timestamping errors: 0 + Rx packets from additional followers received: 0 + Rx packets from additional followers timestamped: 0 + Rx packet timestamping errors for additional followers: 0 + + Tx packets sent: 5 + Tx packets timestamped: 0 + Tx packets with slow timestamps: 0 + Tx packet timestamping errors: 0 + +The detailed results show that: + +- All Rx packets (up to the default receive limit of 5) from the follower the + test chose were successfully timestamped +- No other followers were present (and so DELAY_REQs from those were not received) +- No Tx packets were timestamped, HOWEVER there were no errors. This is an + indication that the driver did not attempt to timestamp any outgoing v1 DELAY_REQs + +For some drivers, timestamping operations do indeed take place BUT result in +errors. The following detailed results are from a board whose driver produces +these: + + DETAILED RESULTS + ================ + + v1 SYNC + ------- + Rx packet receive limit: 20 + Rx packets received: 20 + Rx packets timestamped: 0 + Rx packet timestamping errors: 20 + Rx FOLLOW-UPs received: 20 + Multiple leaders detected: no + + Tx packets sent: 20 + Tx packets timestamped: 20 + Tx packets with slow timestamps: 0 + Tx packet timestamping errors: 0 + +These details show that: + +- All 20 (the default receive limit) SYNC packets were indeed received, however + none were timestamped +- Timestamping was attempted each time, but always resulted in an error +- THere were no issues timestamping outgoing packets + +The details in a SYNC report also include network information that may be of +interest to the user: + +- Most PTP leaders issue FOLLOW-UP packets after each SYNC. These do not need + to be timestamped, however the tool listens for these and checks that they + indeed come from the leader sending the SYNCs. If no FOLLOW-UPs are received, + this may indicate either a problematic leader OR the presence of a leader + using the one-step rather than the more common two-step synchronisation method +- If the tool detects more than one leader on the network, it will be indicated + here + + +5. USING EVENT + ERROR LOGGING +============================== + +While the full test outputs above point to which timestamping operations fail to +work, in some cases (e.g. if trying to debug a network driver) a detailed timeline +of packet and timestamping events, along with the specific errors that occurred, +can be useful. + +By using the -l option along with a file to log to, the tool will produce its +normal output but also place all events into that file. The logs will contain: + +- The start and end of each test +- Every packet receive and send +- Every successful timestamp read (Rx/Tx), along with the timestamp value +- If an error reading a timestamp occurs, a description of the error (ancillary + data truncated, no timestamp information, insufficient timestamp data) +- If timestamping is not taking place at all (as in the example above, for Tx), + the log will be missing these events + +Each line also starts with a timestamp (seconds and nanoseconds), which is the +value of CLOCK_REALTIME at the moment an event or error was logged. + +NOTE: When running a hardware timestamping test, you will notice that the first +test in the log is a PTPv1 "scratch" test. This is a throwaway test done at the +start that, on some devices, will exhibit odd Tx timestamping and/or errors. +A scratch test is always done because in some cases a driver will only start +reporting correct timestamps after a few initial socket and timestamp operation +failures. This way, the actual tests of interest are not affected. The scratch +test logs can be safely ignored (although initial socket behaviour after setup +may be of interest to some). + + +6. TEST BEHAVIOUR AND DURATION +============================== + +a. Test packet receive and send behaviour +----------------------------------------- + +The test uses packet receive limits for each PTP event packet type (for both +protocol versions). By default, these are: + +- 20 SYNCs from the first (and ideally only) leader seen +- 5 DELAY_REQs from the first follower detected + +When transmitting packets: + +- For a SYNC test, each received SYNC is copied and sent out +- For a DELAY_REQ, packet sends are throttled if required so that not less than + 0.25 seconds can elapse before a DELAY_REQ is sent. The sent packet is a copy + of the last received DELAY_REQ. DELAY_REQ tests count packets from the first + follower detected but otherwise receive and timestamp DELAY_REQs from any and + all followers on the network + +Each test only ends when: + +- The receive limit has been reached, and + - For SYNCs, that same number has been sent out + - For DELAY_REQs, a minimum of that same number has been sent +- Or, the test times out waiting for a packet to arrive + - For SYNCs, the test will wait 2 seconds before timing out + - for DELAY_REQs, this figure is 8 seconds (because it can be up to 7.5 seconds + between packet arrivals) +- Or, a socket error occurs (these should NOT occur unless there is a system + issue or network connectivity is suddenly lost) + +NOTE: If the test sees no SYNC packets for a particular PTP version, it will +automatically skip the DELAY_REQ test for that version as, in the absence of a +leader, there will not be followers sending DELAY_REQs. If no SYNCs are detected +despite the device being on a populated Dante network (or one with PTPv2 devices), +it may be the case that multicast PTP traffic is not being sent to this device. +If this happens, the network and/or switch(es) should be checked to ensure that +the device receives multicast PTP. + +b. Test duration +---------------- + +At the default receive limits, a test will last about 25 seconds typically: + +- SYNCs from Dante leaders are normally sent every 0.25 seconds +- DELAY_REQs from Dante followers are sent at varying intervals, but on average + are around 4-5 seconds + +Note: third-party AES67 devices may have their own packet send intervals + +To run a shorter or longer test, the -nsy and -ndr tool options can be used: + +- If a driver is known to have working timestamping for a particular packet + type but not another, the limit for the working type can be reduced +- Setting the limit to 0 will skip that packet type entirely (and the full test + output will say so, and also warn of an incomplete test) +- On the other hand, it may be the case that a driver only begins to exhibit + problems after running for a while. In this case, the limits can be increased + using the send intervals above as a rough guide for determining the approximate + test duration diff --git a/src/dep/dante_package/development/tools/jq b/src/dep/dante_package/development/tools/jq new file mode 100755 index 0000000..550ad66 Binary files /dev/null and b/src/dep/dante_package/development/tools/jq differ diff --git a/src/dep/dante_package/development/tools/ptp_timestamping_test b/src/dep/dante_package/development/tools/ptp_timestamping_test new file mode 100755 index 0000000..9b386b2 Binary files /dev/null and b/src/dep/dante_package/development/tools/ptp_timestamping_test differ diff --git a/src/dep/dante_package/version b/src/dep/dante_package/version new file mode 100644 index 0000000..eaf5f2d --- /dev/null +++ b/src/dep/dante_package/version @@ -0,0 +1,3 @@ +DEP_VERSION=1.5.0.2 +DEP_GIT_HASH= +DEP_BUILD_TIMESTAMP=2025-08-20_05-14-33_UTC diff --git a/src/misc/asound.conf b/src/misc/asound.conf index 30e6594..b9351ad 100644 --- a/src/misc/asound.conf +++ b/src/misc/asound.conf @@ -25,4 +25,322 @@ pcm.ch2 { buffer_size 240 } bindings.0 1 -} \ No newline at end of file +} + + +# ============================================================ +# DEP Dante RX -> ALSA Loopback is now done by DEP ALSA ASRC. +# So: NO alsaloop needed anymore. +# +# Apps read from hw:Loopback,1,0 via dsnoop fanout, +# then we split into 6 mono virtual devices. +# ============================================================ + +# ---- shared 6ch capture from Loopback with dsnoop fanout ---- +pcm.dante_asrc_shared6 { + type dsnoop + ipc_key 1048577 + ipc_key_add_uid true + ipc_perm 0666 + + slave { + pcm "hw:Loopback,1,0" # capture side of ALSA loopback + channels 6 + rate 48000 + format S16_LE + + period_size 240 + buffer_size 960 + } + + hint { show on ; description "DEP RX (via ASRC) shared 6ch (loopback+dsnoop)" } +} + +# ---- 6 mono devices (each maps one of the 6 channels) ---- +# (Using route explicitly makes the intent very clear.) +pcm.dante_asrc_ch1 { + type route + slave { pcm "dante_asrc_shared6"; channels 6; } + ttable.0.0 1 + hint { show on ; description "DEP RX CH1" } +} + +pcm.dante_asrc_ch2 { + type route + slave { pcm "dante_asrc_shared6"; channels 6; } + ttable.0.1 1 + hint { show on ; description "DEP RX CH2" } +} + +pcm.dante_asrc_ch3 { + type route + slave { pcm "dante_asrc_shared6"; channels 6; } + ttable.0.2 1 + hint { show on ; description "DEP RX CH3" } +} + +pcm.dante_asrc_ch4 { + type route + slave { pcm "dante_asrc_shared6"; channels 6; } + ttable.0.3 1 + hint { show on ; description "DEP RX CH4" } +} + +pcm.dante_asrc_ch5 { + type route + slave { pcm "dante_asrc_shared6"; channels 6; } + ttable.0.4 1 + hint { show on ; description "DEP RX CH5" } +} + +pcm.dante_asrc_ch6 { + type route + slave { pcm "dante_asrc_shared6"; channels 6; } + ttable.0.5 1 + hint { show on ; description "DEP RX CH6" } +} + +# ---- Stereo devices for Dante (combine any two channels as L+R) ---- +# These devices route selected source channels to stereo output +# Format: dante_stereo__ + +pcm.dante_stereo_1_2 { + type route + slave { pcm "dante_asrc_shared6"; channels 6; } + ttable.0.0 1 # Left channel from ch1 + ttable.1.1 1 # Right channel from ch2 + hint { show on ; description "DEP RX Stereo CH1+CH2" } +} + +pcm.dante_stereo_1_3 { + type route + slave { pcm "dante_asrc_shared6"; channels 6; } + ttable.0.0 1 + ttable.1.2 1 + hint { show on ; description "DEP RX Stereo CH1+CH3" } +} + +pcm.dante_stereo_1_4 { + type route + slave { pcm "dante_asrc_shared6"; channels 6; } + ttable.0.0 1 + ttable.1.3 1 + hint { show on ; description "DEP RX Stereo CH1+CH4" } +} + +pcm.dante_stereo_1_5 { + type route + slave { pcm "dante_asrc_shared6"; channels 6; } + ttable.0.0 1 + ttable.1.4 1 + hint { show on ; description "DEP RX Stereo CH1+CH5" } +} + +pcm.dante_stereo_1_6 { + type route + slave { pcm "dante_asrc_shared6"; channels 6; } + ttable.0.0 1 + ttable.1.5 1 + hint { show on ; description "DEP RX Stereo CH1+CH6" } +} + +pcm.dante_stereo_2_3 { + type route + slave { pcm "dante_asrc_shared6"; channels 6; } + ttable.0.1 1 + ttable.1.2 1 + hint { show on ; description "DEP RX Stereo CH2+CH3" } +} + +pcm.dante_stereo_2_4 { + type route + slave { pcm "dante_asrc_shared6"; channels 6; } + ttable.0.1 1 + ttable.1.3 1 + hint { show on ; description "DEP RX Stereo CH2+CH4" } +} + +pcm.dante_stereo_2_5 { + type route + slave { pcm "dante_asrc_shared6"; channels 6; } + ttable.0.1 1 + ttable.1.4 1 + hint { show on ; description "DEP RX Stereo CH2+CH5" } +} + +pcm.dante_stereo_2_6 { + type route + slave { pcm "dante_asrc_shared6"; channels 6; } + ttable.0.1 1 + ttable.1.5 1 + hint { show on ; description "DEP RX Stereo CH2+CH6" } +} + +pcm.dante_stereo_3_4 { + type route + slave { pcm "dante_asrc_shared6"; channels 6; } + ttable.0.2 1 + ttable.1.3 1 + hint { show on ; description "DEP RX Stereo CH3+CH4" } +} + +pcm.dante_stereo_3_5 { + type route + slave { pcm "dante_asrc_shared6"; channels 6; } + ttable.0.2 1 + ttable.1.4 1 + hint { show on ; description "DEP RX Stereo CH3+CH5" } +} + +pcm.dante_stereo_3_6 { + type route + slave { pcm "dante_asrc_shared6"; channels 6; } + ttable.0.2 1 + ttable.1.5 1 + hint { show on ; description "DEP RX Stereo CH3+CH6" } +} + +pcm.dante_stereo_4_5 { + type route + slave { pcm "dante_asrc_shared6"; channels 6; } + ttable.0.3 1 + ttable.1.4 1 + hint { show on ; description "DEP RX Stereo CH4+CH5" } +} + +pcm.dante_stereo_4_6 { + type route + slave { pcm "dante_asrc_shared6"; channels 6; } + ttable.0.3 1 + ttable.1.5 1 + hint { show on ; description "DEP RX Stereo CH4+CH6" } +} + +pcm.dante_stereo_5_6 { + type route + slave { pcm "dante_asrc_shared6"; channels 6; } + ttable.0.4 1 + ttable.1.5 1 + hint { show on ; description "DEP RX Stereo CH5+CH6" } +} + +# ---- Reverse stereo devices (for when left channel > right channel) ---- +pcm.dante_stereo_2_1 { + type route + slave { pcm "dante_asrc_shared6"; channels 6; } + ttable.0.1 1 # Left from ch2 + ttable.1.0 1 # Right from ch1 + hint { show on ; description "DEP RX Stereo CH2+CH1" } +} + +pcm.dante_stereo_3_1 { + type route + slave { pcm "dante_asrc_shared6"; channels 6; } + ttable.0.2 1 + ttable.1.0 1 + hint { show on ; description "DEP RX Stereo CH3+CH1" } +} + +pcm.dante_stereo_3_2 { + type route + slave { pcm "dante_asrc_shared6"; channels 6; } + ttable.0.2 1 + ttable.1.1 1 + hint { show on ; description "DEP RX Stereo CH3+CH2" } +} + +pcm.dante_stereo_4_1 { + type route + slave { pcm "dante_asrc_shared6"; channels 6; } + ttable.0.3 1 + ttable.1.0 1 + hint { show on ; description "DEP RX Stereo CH4+CH1" } +} + +pcm.dante_stereo_4_2 { + type route + slave { pcm "dante_asrc_shared6"; channels 6; } + ttable.0.3 1 + ttable.1.1 1 + hint { show on ; description "DEP RX Stereo CH4+CH2" } +} + +pcm.dante_stereo_4_3 { + type route + slave { pcm "dante_asrc_shared6"; channels 6; } + ttable.0.3 1 + ttable.1.2 1 + hint { show on ; description "DEP RX Stereo CH4+CH3" } +} + +pcm.dante_stereo_5_1 { + type route + slave { pcm "dante_asrc_shared6"; channels 6; } + ttable.0.4 1 + ttable.1.0 1 + hint { show on ; description "DEP RX Stereo CH5+CH1" } +} + +pcm.dante_stereo_5_2 { + type route + slave { pcm "dante_asrc_shared6"; channels 6; } + ttable.0.4 1 + ttable.1.1 1 + hint { show on ; description "DEP RX Stereo CH5+CH2" } +} + +pcm.dante_stereo_5_3 { + type route + slave { pcm "dante_asrc_shared6"; channels 6; } + ttable.0.4 1 + ttable.1.2 1 + hint { show on ; description "DEP RX Stereo CH5+CH3" } +} + +pcm.dante_stereo_5_4 { + type route + slave { pcm "dante_asrc_shared6"; channels 6; } + ttable.0.4 1 + ttable.1.3 1 + hint { show on ; description "DEP RX Stereo CH5+CH4" } +} + +pcm.dante_stereo_6_1 { + type route + slave { pcm "dante_asrc_shared6"; channels 6; } + ttable.0.5 1 + ttable.1.0 1 + hint { show on ; description "DEP RX Stereo CH6+CH1" } +} + +pcm.dante_stereo_6_2 { + type route + slave { pcm "dante_asrc_shared6"; channels 6; } + ttable.0.5 1 + ttable.1.1 1 + hint { show on ; description "DEP RX Stereo CH6+CH2" } +} + +pcm.dante_stereo_6_3 { + type route + slave { pcm "dante_asrc_shared6"; channels 6; } + ttable.0.5 1 + ttable.1.2 1 + hint { show on ; description "DEP RX Stereo CH6+CH3" } +} + +pcm.dante_stereo_6_4 { + type route + slave { pcm "dante_asrc_shared6"; channels 6; } + ttable.0.5 1 + ttable.1.3 1 + hint { show on ; description "DEP RX Stereo CH6+CH4" } +} + +pcm.dante_stereo_6_5 { + type route + slave { pcm "dante_asrc_shared6"; channels 6; } + ttable.0.5 1 + ttable.1.4 1 + hint { show on ; description "DEP RX Stereo CH6+CH5" } +} diff --git a/src/misc/install_asoundconf.sh b/src/misc/install_asoundconf.sh old mode 100644 new mode 100755