From 097381fb8be698a8550701548f8730d90fadfe75 Mon Sep 17 00:00:00 2001 From: Jonathan Martin Date: Fri, 5 Jul 2024 17:02:04 -0700 Subject: [PATCH] v2.11.4 --- .gitlab-ci.yml | 276 +- README.md | 3 +- backend/linux/helper/CMakeLists.txt | 1 + backend/linux/helper/execute_cmd.cpp | 25 +- backend/linux/helper/execute_cmd.h | 6 +- backend/linux/helper/firewallcontroller.cpp | 15 + backend/linux/helper/firewallcontroller.h | 4 +- backend/linux/helper/firewallonboot.cpp | 113 + backend/linux/helper/firewallonboot.h | 27 + backend/linux/helper/main.cpp | 11 - backend/linux/helper/process_command.cpp | 32 +- backend/linux/helper/process_command.h | 40 +- backend/linux/helper/server.cpp | 12 +- .../hostnames_manager/hostnames_manager.cpp | 8 +- backend/mac/helper/CMakeLists.txt | 10 +- backend/mac/helper/execute_cmd.cpp | 25 +- backend/mac/helper/execute_cmd.h | 6 +- backend/mac/helper/firewallcontroller.cpp | 15 +- backend/mac/helper/firewallcontroller.h | 4 +- backend/mac/helper/firewallonboot.cpp | 109 +- backend/mac/helper/firewallonboot.h | 4 +- backend/mac/helper/helper-info.plist | 2 +- backend/mac/helper/installer/files.cpp | 141 +- backend/mac/helper/installer/files.h | 12 - .../helper/ip_hostnames/ares_library_init.cpp | 30 + .../helper/ip_hostnames/ares_library_init.h | 14 + .../mac/helper/ip_hostnames/dns_resolver.cpp | 194 + .../mac/helper/ip_hostnames/dns_resolver.h | 65 + .../ip_hostnames/ip_hostnames_manager.cpp | 76 +- .../ip_hostnames/ip_hostnames_manager.h | 22 +- backend/mac/helper/process_command.cpp | 62 +- backend/mac/helper/process_command.h | 4 +- backend/mac/helper/server.cpp | 9 +- backend/mac/helper/utils.cpp | 10 + backend/mac/helper/utils.h | 2 + backend/posix_common/helper_commands.h | 6 +- .../posix_common/helper_commands_serialize.h | 9 +- .../install_service_command.cpp | 2 +- .../windows/windscribe_service/CMakeLists.txt | 4 +- .../windows/windscribe_service/all_headers.h | 1 - .../windscribe_service/process_command.cpp | 13 +- .../hostnames_manager/hostnames_manager.cpp | 25 +- backend/windows/windscribe_service/utils.cpp | 24 +- client/CMakeLists.txt | 74 +- client/base/CMakeLists.txt | 51 + client/{gui => base}/backend/CMakeLists.txt | 2 +- client/{gui => base}/backend/backend.cpp | 30 +- client/{gui => base}/backend/backend.h | 16 +- .../backend/connectstatehelper.cpp | 0 .../backend/connectstatehelper.h | 0 .../backend/firewallstatehelper.cpp | 0 .../backend/firewallstatehelper.h | 0 .../backend/notificationscontroller.cpp | 0 .../backend/notificationscontroller.h | 0 .../{gui => base}/backend/persistentstate.cpp | 31 + .../{gui => base}/backend/persistentstate.h | 7 + .../backend/preferences/accountinfo.cpp | 0 .../backend/preferences/accountinfo.h | 0 .../backend/preferences/detectlanrange.cpp | 0 .../backend/preferences/detectlanrange.h | 0 .../backend/preferences/preferences.cpp | 98 +- .../backend/preferences/preferences.h | 17 +- .../backend/preferences/preferenceshelper.cpp | 0 .../backend/preferences/preferenceshelper.h | 0 .../backend/types/upgrademodetype.cpp | 0 .../backend/types/upgrademodetype.h | 0 client/{gui => base}/blockconnect.cpp | 0 client/{gui => base}/blockconnect.h | 0 client/{gui => base}/languagecontroller.cpp | 2 +- client/{gui => base}/languagecontroller.h | 0 .../launchonstartup/CMakeLists.txt | 8 +- .../launchonstartup/launchonstartup.cpp | 0 .../launchonstartup/launchonstartup.h | 0 .../launchonstartup/launchonstartup_linux.cpp | 18 +- .../launchonstartup/launchonstartup_linux.h | 0 .../launchonstartup/launchonstartup_mac.h | 0 .../launchonstartup/launchonstartup_mac.mm | 0 .../launchonstartup/launchonstartup_win.cpp | 0 .../launchonstartup/launchonstartup_win.h | 0 .../localipcserver/CMakeLists.txt | 2 +- client/base/localipcserver/localipcserver.cpp | 235 + client/base/localipcserver/localipcserver.h | 63 + client/base/locations/CMakeLists.txt | 7 + .../locations/locationsmodel_manager.cpp | 0 .../locations/locationsmodel_manager.h | 0 .../locations/locationsmodel_roles.h | 0 .../locations/model/CMakeLists.txt | 13 +- .../model/favoritelocationsstorage.cpp | 0 .../model/favoritelocationsstorage.h | 0 .../locations/model/locationitem.cpp | 0 .../locations/model/locationitem.h | 0 .../locations/model/locationsmodel.cpp | 0 .../locations/model/locationsmodel.h | 0 .../locations/model/locationsmodel.test.cpp | 0 .../locations/model/locationsmodel.test.h | 0 .../locations/model/locationsmodel.test.qrc | 0 .../locations/model/locationsmodel_utils.cpp | 0 .../locations/model/locationsmodel_utils.h | 0 .../model/proxymodels/cities_proxymodel.cpp | 0 .../model/proxymodels/cities_proxymodel.h | 0 .../proxymodels/customconfigs_proxymodel.cpp | 0 .../proxymodels/customconfigs_proxymodel.h | 0 .../proxymodels/favoritecities_proxymodel.cpp | 0 .../proxymodels/favoritecities_proxymodel.h | 0 .../proxymodels/sortedcities_proxymodel.cpp | 0 .../proxymodels/sortedcities_proxymodel.h | 0 .../sortedlocations_proxymodel.cpp | 0 .../proxymodels/sortedlocations_proxymodel.h | 0 .../proxymodels/staticips_proxymodel.cpp | 0 .../model/proxymodels/staticips_proxymodel.h | 0 .../locations/model/selectedlocation.cpp | 0 .../locations/model/selectedlocation.h | 0 .../model/selectedlocationwatcher.test.cpp | 0 .../multipleaccountdetection/CMakeLists.txt | 6 +- .../imultipleaccountdetection.h | 0 .../multipleaccountdetection_posix.cpp | 0 .../multipleaccountdetection_posix.h | 0 .../multipleaccountdetection_win.cpp | 0 .../multipleaccountdetection_win.h | 0 .../multipleaccountdetectionfactory.cpp | 0 .../multipleaccountdetectionfactory.h | 0 .../secretvalue_win.cpp | 0 .../secretvalue_win.h | 0 client/base/utils/CMakeLists.txt | 23 + .../{gui => base}/utils/authchecker_linux.cpp | 0 .../{gui => base}/utils/authchecker_linux.h | 0 client/{gui => base}/utils/authchecker_mac.h | 0 client/{gui => base}/utils/authchecker_mac.mm | 0 .../{gui => base}/utils/authchecker_win.cpp | 0 client/{gui => base}/utils/authchecker_win.h | 0 .../{gui => base}/utils/authcheckerfactory.h | 0 client/{gui => base}/utils/iauthchecker.h | 0 .../utils/writeaccessrightschecker.cpp | 0 .../utils/writeaccessrightschecker.h | 0 client/cli/CMakeLists.txt | 30 + client/cli/application/CMakeLists.txt | 4 + client/cli/application/singleappinstance.cpp | 56 + client/cli/application/singleappinstance.h | 25 + .../cli/application/windscribeapplication.cpp | 41 + .../cli/application/windscribeapplication.h | 30 + client/cli/mainservice.cpp | 265 + client/cli/mainservice.h | 52 + client/common/CMakeLists.txt | 6 +- client/common/archive/archive.cpp | 1023 ---- client/common/archive/archive.h | 186 - client/common/changelog.txt | 88 +- client/common/config/openssl.cnf | 22 + client/common/ipc/CMakeLists.txt | 1 - client/common/ipc/clicommands.cpp | 5 - client/common/ipc/clicommands.h | 264 +- client/common/ipc/commandfactory.cpp | 71 +- client/common/ipc/connection.cpp | 10 +- client/common/ipc/server.cpp | 8 +- client/common/types/apiresolutionsettings.cpp | 31 +- client/common/types/apiresolutionsettings.h | 24 +- client/common/types/backgroundsettings.h | 32 +- client/common/types/connecteddnsinfo.cpp | 66 + client/common/types/connecteddnsinfo.h | 76 +- client/common/types/connectionsettings.cpp | 67 +- client/common/types/connectionsettings.h | 21 +- client/common/types/enginesettings.cpp | 301 +- client/common/types/enginesettings.h | 67 +- client/common/types/enums.cpp | 246 +- client/common/types/enums.h | 35 +- client/common/types/firewallsettings.h | 45 +- client/common/types/guisettings.h | 179 +- client/common/types/macaddrspoofing.h | 48 +- client/common/types/networkinterface.h | 144 +- client/common/types/packetsize.h | 53 +- client/common/types/proxysettings.cpp | 51 +- client/common/types/proxysettings.h | 31 +- client/common/types/shareproxygateway.h | 66 +- client/common/types/sharesecurehotspot.h | 34 +- client/common/types/splittunneling.h | 217 +- client/common/utils/extraconfig.cpp | 6 + client/common/utils/extraconfig.h | 1 + client/common/utils/languagesutil.cpp | 6 + client/common/utils/languagesutil.h | 2 + client/common/utils/linuxutils.cpp | 2 +- client/common/utils/linuxutils.h | 8 +- client/common/utils/macutils.h | 2 +- client/common/utils/macutils.mm | 4 +- client/common/utils/mergelog.cpp | 8 + .../utils/network_utils/network_utils.cpp | 16 +- .../utils/network_utils/network_utils.h | 2 + .../network_utils/network_utils_linux.cpp | 13 + .../utils/network_utils/network_utils_linux.h | 2 + .../utils/network_utils/network_utils_mac.cpp | 175 +- .../utils/network_utils/network_utils_mac.h | 6 +- .../utils/network_utils/network_utils_win.cpp | 106 +- .../utils/network_utils/network_utils_win.h | 1 + client/common/utils/utils.cpp | 8 +- client/common/utils/utils.h | 2 +- client/common/utils/winutils.cpp | 8 +- client/common/utils/winutils.h | 2 +- client/common/version/windscribe_version.h | 6 +- client/engine/CMakeLists.txt | 2 +- client/engine/engine/CMakeLists.txt | 2 +- .../engine/autoupdater/downloadhelper.cpp | 12 +- .../connectionmanager/availableport.cpp | 21 +- .../ctrldmanager/ctrldmanager_posix.cpp | 30 +- .../connectionmanager/sleepevents_mac.mm | 10 +- client/engine/engine/engine.cpp | 241 +- client/engine/engine/engine.h | 16 +- .../engine/firewall/firewallcontroller.h | 2 +- .../firewall/firewallcontroller_linux.cpp | 7 +- .../firewall/firewallcontroller_linux.h | 2 +- .../firewall/firewallcontroller_mac.cpp | 4 +- .../engine/firewall/firewallcontroller_mac.h | 2 +- .../firewall/firewallcontroller_win.cpp | 2 +- .../engine/firewall/firewallcontroller_win.h | 2 +- client/engine/engine/helper/helper_mac.cpp | 19 + client/engine/engine/helper/helper_mac.h | 2 + client/engine/engine/helper/helper_posix.cpp | 16 +- client/engine/engine/helper/helper_posix.h | 4 +- client/engine/engine/helper/helper_win.cpp | 4 +- client/engine/engine/helper/helper_win.h | 2 +- client/engine/engine/helper/ihelper.h | 2 +- .../engine/engine/helper/installhelper_mac.mm | 1 - .../{signout_helper.h => logout_helper.h} | 6 +- .../macaddresscontroller_mac.cpp | 13 +- .../networkdetectionmanager_mac.cpp | 5 +- .../engine/vpnshare/vpnsharecontroller.cpp | 49 +- .../engine/vpnshare/vpnsharecontroller.h | 2 +- .../wireguardconfig/getwireguardconfig.cpp | 2 +- client/engine/mac/resources/cert.pem | 4649 ----------------- client/engine/utils/CMakeLists.txt | 6 +- client/engine/utils/interfaceutils_mac.cpp | 184 + client/engine/utils/interfaceutils_mac.h | 27 + client/gui/CMakeLists.txt | 11 +- client/gui/application/singleappinstance.cpp | 5 +- .../gui/application/windscribeapplication.h | 2 - client/gui/connectwindow/locationsbutton.h | 2 +- .../emergencyconnectwindowitem.h | 2 +- .../externalconfig/externalconfigwindowitem.h | 2 +- client/gui/localipcserver/localipcserver.cpp | 273 - client/gui/localipcserver/localipcserver.h | 45 - client/gui/locations/CMakeLists.txt | 10 - .../gui/locations/view/cityitemdelegate.cpp | 2 +- .../locations/view/countryitemdelegate.cpp | 2 +- .../locations/view/expandableitemswidget.cpp | 2 +- .../gui/loginwindow/credentialswindowitem.h | 2 +- client/gui/loginwindow/welcomewindowitem.h | 2 +- client/gui/mainwindow.cpp | 189 +- client/gui/mainwindow.h | 29 +- .../gui/newsfeedwindow/newsfeedwindowitem.h | 2 +- .../connectionwindow/connectionwindowitem.cpp | 14 - .../connectionwindow/proxygatewaygroup.cpp | 71 + .../connectionwindow/proxygatewaygroup.h | 7 +- client/gui/preferenceswindow/editboxitem.cpp | 2 + client/gui/preferenceswindow/editboxitem.h | 5 +- .../preferencestabcontrolitem.cpp | 20 +- .../preferencestabcontrolitem.h | 4 +- .../preferenceswindowitem.cpp | 2 +- .../preferenceswindow/preferenceswindowitem.h | 2 +- .../gui/protocolwindow/protocolpromptitem.h | 6 +- .../gui/protocolwindow/protocolwindowitem.h | 2 +- client/gui/svg.qrc | 2 +- .../{SIGN_OUT_ICON.svg => LOGOUT_ICON.svg} | 0 client/gui/translations/ws_desktop_ar.ts | 16 + client/gui/translations/ws_desktop_cs.ts | 16 + client/gui/translations/ws_desktop_de.ts | 16 + client/gui/translations/ws_desktop_en.ts | 16 + client/gui/translations/ws_desktop_es.ts | 16 + client/gui/translations/ws_desktop_fa.ts | 16 + client/gui/translations/ws_desktop_fr.ts | 16 + client/gui/translations/ws_desktop_hi.ts | 16 + client/gui/translations/ws_desktop_id.ts | 16 + client/gui/translations/ws_desktop_it.ts | 16 + client/gui/translations/ws_desktop_ja.ts | 16 + client/gui/translations/ws_desktop_ko.ts | 16 + client/gui/translations/ws_desktop_pl.ts | 16 + client/gui/translations/ws_desktop_pt.ts | 16 + client/gui/translations/ws_desktop_ru.ts | 16 + client/gui/translations/ws_desktop_tr.ts | 16 + client/gui/translations/ws_desktop_uk.ts | 20 +- client/gui/translations/ws_desktop_vi.ts | 16 + client/gui/translations/ws_desktop_zh-CN.ts | 16 + client/gui/translations/ws_desktop_zh-TW.ts | 16 + .../twofactorauth/twofactorauthwindowitem.h | 2 +- client/gui/utils/CMakeLists.txt | 10 - client/main_cli.cpp | 129 + client/{main.cpp => main_gui.cpp} | 62 +- gui/cli/CMakeLists.txt | 47 +- gui/cli/backendcommander.cpp | 254 +- gui/cli/backendcommander.h | 11 +- gui/cli/cliarguments.cpp | 124 +- gui/cli/cliarguments.h | 9 +- gui/cli/languagecontroller.cpp | 58 + gui/cli/languagecontroller.h | 33 + gui/cli/main.cpp | 117 +- gui/cli/strings.cpp | 135 + gui/cli/strings.h | 14 + gui/cli/translations/windscribe_cli_ar.ts | 206 + gui/cli/translations/windscribe_cli_cs.ts | 206 + gui/cli/translations/windscribe_cli_de.ts | 206 + gui/cli/translations/windscribe_cli_en.ts | 206 + gui/cli/translations/windscribe_cli_es.ts | 206 + gui/cli/translations/windscribe_cli_fa.ts | 206 + gui/cli/translations/windscribe_cli_fr.ts | 206 + gui/cli/translations/windscribe_cli_hi.ts | 206 + gui/cli/translations/windscribe_cli_id.ts | 206 + gui/cli/translations/windscribe_cli_it.ts | 206 + gui/cli/translations/windscribe_cli_ja.ts | 206 + gui/cli/translations/windscribe_cli_ko.ts | 206 + gui/cli/translations/windscribe_cli_pl.ts | 206 + gui/cli/translations/windscribe_cli_pt.ts | 206 + gui/cli/translations/windscribe_cli_ru.ts | 206 + gui/cli/translations/windscribe_cli_tr.ts | 206 + gui/cli/translations/windscribe_cli_uk.ts | 206 + gui/cli/translations/windscribe_cli_vi.ts | 206 + gui/cli/translations/windscribe_cli_zh-CN.ts | 206 + gui/cli/translations/windscribe_cli_zh-TW.ts | 206 + installer/common/alertwindow.cpp | 1 + installer/common/alertwindowcontents.cpp | 1 - installer/common/alertwindowcontents.h | 2 - installer/common/installer_shim.h | 2 +- installer/common/mainwindow.cpp | 87 +- .../translations/windscribe_installer_ru.ts | 30 +- installer/linux/cli/arch_package/PKGBUILD | 28 + .../cli/arch_package/windscribe-cli.install | 63 + .../linux/cli/debian_package/DEBIAN/control | 9 + .../linux/cli/debian_package/DEBIAN/postinst | 15 + .../{ => cli}/debian_package/DEBIAN/preinst | 0 .../{ => cli}/debian_package/DEBIAN/prerm | 0 .../etc/systemd/user/windscribe.service | 8 + .../SPECS/windscribe_rpm.spec | 77 + .../SPECS/windscribe_rpm.spec | 79 + .../common/etc/windscribe/install-update | 4 +- .../linux/{ => gui}/arch_package/PKGBUILD | 0 .../{ => gui}/arch_package/windscribe.install | 0 .../{ => gui}/debian_package/DEBIAN/control | 1 + .../{ => gui}/debian_package/DEBIAN/postinst | 0 .../linux/gui/debian_package/DEBIAN/preinst | 30 + .../linux/gui/debian_package/DEBIAN/prerm | 15 + .../usr/share/applications/windscribe.desktop | 2 +- .../hicolor/128x128/apps/Windscribe.png} | Bin .../icons/hicolor/16x16/apps/Windscribe.png} | Bin .../icons/hicolor/24x24/apps/Windscribe.png} | Bin .../hicolor/256x256/apps/Windscribe.png} | Bin .../icons/hicolor/32x32/apps/Windscribe.png} | Bin .../icons/hicolor/48x48/apps/Windscribe.png} | Bin .../icons/hicolor/64x64/apps/Windscribe.png} | Bin .../png_icons/128x128/windscribe.png | Bin .../{ => gui}/png_icons/16x16/windscribe.png | Bin .../{ => gui}/png_icons/24x24/windscribe.png | Bin .../png_icons/256x256/windscribe.png | Bin .../{ => gui}/png_icons/32x32/windscribe.png | Bin .../{ => gui}/png_icons/48x48/windscribe.png | Bin .../{ => gui}/png_icons/64x64/windscribe.png | Bin .../SPECS/windscribe_rpm.spec | 7 +- .../SPECS/windscribe_rpm.spec | 89 + installer/mac/dmgbuild/dmgbuild_settings.py | 2 +- installer/mac/installer/CMakeLists.txt | 2 +- installer/mac/installer/helper/helper_mac.h | 2 +- installer/mac/installer/helper/helper_mac.mm | 4 +- .../mac/installer/installer/base_installer.h | 2 +- .../mac/installer/installer/installer.mm | 83 +- installer/windows/bootstrap/CMakeLists.txt | 7 +- installer/windows/bootstrap/installer.rc | 5 +- installer/windows/bootstrap/main.cpp | 55 +- installer/windows/bootstrap/resources/7zr.exe | Bin 0 -> 920176 bytes installer/windows/installer/CMakeLists.txt | 4 +- installer/windows/installer/installer.rc | 2 +- .../installer/installer/blocks/files.cpp | 116 +- .../installer/installer/blocks/files.h | 9 - .../installer/installer/blocks/icons.cpp | 18 +- .../installer/blocks/install_authhelper.cpp | 15 +- .../installer/blocks/install_openvpn_dco.cpp | 8 +- .../installer/blocks/install_splittunnel.cpp | 10 +- .../installer/installer/blocks/service.cpp | 2 +- .../installer/blocks/uninstallprev.cpp | 62 +- .../installer/blocks/uninstallprev.h | 3 - .../installer/installer/installer_base.h | 1 + .../windows/uninstaller/copy_and_run.cpp | 8 +- installer/windows/uninstaller/uninstall.cpp | 35 +- installer/windows/utils/applicationinfo.cpp | 3 - installer/windows/utils/archive.cpp | 232 + installer/windows/utils/archive.h | 39 + installer/windows/utils/logger.cpp | 18 - installer/windows/utils/logger.h | 1 - installer/windows/utils/path.cpp | 2 +- installer/windows/utils/utils.cpp | 33 +- installer/windows/utils/utils.h | 9 +- libs/wsnet/include/wsnet/WSNetServerAPI.h | 9 +- .../httpnetworkmanager/curlnetworkmanager.cpp | 1 + libs/wsnet/src/serverapi/requestsfactory.cpp | 13 +- libs/wsnet/src/serverapi/requestsfactory.h | 4 +- libs/wsnet/src/serverapi/serverapi.cpp | 12 +- libs/wsnet/src/serverapi/serverapi.h | 5 +- libs/wsnet/tools/build_android.sh | 8 +- tools/base/arghelper.py | 21 +- tools/build_all.py | 80 +- tools/build_all.yml | 33 +- tools/vars/ctrld.yml | 0 tools/vcpkg/ports/curl/portfile.cmake | 5 + .../ports/liboqs/0000-macos-universal.patch | 22 + tools/vcpkg/ports/liboqs/portfile.cmake | 129 + tools/vcpkg/ports/liboqs/vcpkg.json | 19 + tools/vcpkg/ports/oqsprovider/portfile.cmake | 23 + tools/vcpkg/ports/oqsprovider/vcpkg.json | 20 + tools/vcpkg/ports/reproc/latest_changes.patch | 268 - tools/vcpkg/ports/reproc/portfile.cmake | 33 - tools/vcpkg/ports/reproc/readme.txt | 1 - tools/vcpkg/ports/reproc/vcpkg.json | 18 - tools/vcpkg/ports/scapix-bin/CMakeLists.txt | 18 + tools/vcpkg/ports/scapix-bin/portfile.cmake | 55 + .../scapix-bin/scapix-bin-config.cmake.in | 15 + tools/vcpkg/ports/scapix-bin/vcpkg.json | 17 + tools/vcpkg/ports/scapix/CMakeLists.txt | 14 + tools/vcpkg/ports/scapix/fix-project.patch | 28 + tools/vcpkg/ports/scapix/portfile.cmake | 34 + .../vcpkg/ports/scapix/scapix-config.cmake.in | 4 + tools/vcpkg/ports/scapix/vcpkg.json | 18 + tools/vcpkg/vcpkg.json | 3 +- 415 files changed, 11115 insertions(+), 9256 deletions(-) create mode 100644 backend/linux/helper/firewallonboot.cpp create mode 100644 backend/linux/helper/firewallonboot.h create mode 100644 backend/mac/helper/ip_hostnames/ares_library_init.cpp create mode 100644 backend/mac/helper/ip_hostnames/ares_library_init.h create mode 100644 backend/mac/helper/ip_hostnames/dns_resolver.cpp create mode 100644 backend/mac/helper/ip_hostnames/dns_resolver.h create mode 100644 client/base/CMakeLists.txt rename client/{gui => base}/backend/CMakeLists.txt (95%) rename client/{gui => base}/backend/backend.cpp (97%) rename client/{gui => base}/backend/backend.h (96%) rename client/{gui => base}/backend/connectstatehelper.cpp (100%) rename client/{gui => base}/backend/connectstatehelper.h (100%) rename client/{gui => base}/backend/firewallstatehelper.cpp (100%) rename client/{gui => base}/backend/firewallstatehelper.h (100%) rename client/{gui => base}/backend/notificationscontroller.cpp (100%) rename client/{gui => base}/backend/notificationscontroller.h (100%) rename client/{gui => base}/backend/persistentstate.cpp (81%) rename client/{gui => base}/backend/persistentstate.h (86%) rename client/{gui => base}/backend/preferences/accountinfo.cpp (100%) rename client/{gui => base}/backend/preferences/accountinfo.h (100%) rename client/{gui => base}/backend/preferences/detectlanrange.cpp (100%) rename client/{gui => base}/backend/preferences/detectlanrange.h (100%) rename client/{gui => base}/backend/preferences/preferences.cpp (90%) rename client/{gui => base}/backend/preferences/preferences.h (96%) rename client/{gui => base}/backend/preferences/preferenceshelper.cpp (100%) rename client/{gui => base}/backend/preferences/preferenceshelper.h (100%) rename client/{gui => base}/backend/types/upgrademodetype.cpp (100%) rename client/{gui => base}/backend/types/upgrademodetype.h (100%) rename client/{gui => base}/blockconnect.cpp (100%) rename client/{gui => base}/blockconnect.h (100%) rename client/{gui => base}/languagecontroller.cpp (98%) rename client/{gui => base}/languagecontroller.h (100%) rename client/{gui => base}/launchonstartup/CMakeLists.txt (70%) rename client/{gui => base}/launchonstartup/launchonstartup.cpp (100%) rename client/{gui => base}/launchonstartup/launchonstartup.h (100%) rename client/{gui => base}/launchonstartup/launchonstartup_linux.cpp (77%) rename client/{gui => base}/launchonstartup/launchonstartup_linux.h (100%) rename client/{gui => base}/launchonstartup/launchonstartup_mac.h (100%) rename client/{gui => base}/launchonstartup/launchonstartup_mac.mm (100%) rename client/{gui => base}/launchonstartup/launchonstartup_win.cpp (100%) rename client/{gui => base}/launchonstartup/launchonstartup_win.h (100%) rename client/{gui => base}/localipcserver/CMakeLists.txt (62%) create mode 100644 client/base/localipcserver/localipcserver.cpp create mode 100644 client/base/localipcserver/localipcserver.h create mode 100644 client/base/locations/CMakeLists.txt rename client/{gui => base}/locations/locationsmodel_manager.cpp (100%) rename client/{gui => base}/locations/locationsmodel_manager.h (100%) rename client/{gui => base}/locations/locationsmodel_roles.h (100%) rename client/{gui => base}/locations/model/CMakeLists.txt (82%) rename client/{gui => base}/locations/model/favoritelocationsstorage.cpp (100%) rename client/{gui => base}/locations/model/favoritelocationsstorage.h (100%) rename client/{gui => base}/locations/model/locationitem.cpp (100%) rename client/{gui => base}/locations/model/locationitem.h (100%) rename client/{gui => base}/locations/model/locationsmodel.cpp (100%) rename client/{gui => base}/locations/model/locationsmodel.h (100%) rename client/{gui => base}/locations/model/locationsmodel.test.cpp (100%) rename client/{gui => base}/locations/model/locationsmodel.test.h (100%) rename client/{gui => base}/locations/model/locationsmodel.test.qrc (100%) rename client/{gui => base}/locations/model/locationsmodel_utils.cpp (100%) rename client/{gui => base}/locations/model/locationsmodel_utils.h (100%) rename client/{gui => base}/locations/model/proxymodels/cities_proxymodel.cpp (100%) rename client/{gui => base}/locations/model/proxymodels/cities_proxymodel.h (100%) rename client/{gui => base}/locations/model/proxymodels/customconfigs_proxymodel.cpp (100%) rename client/{gui => base}/locations/model/proxymodels/customconfigs_proxymodel.h (100%) rename client/{gui => base}/locations/model/proxymodels/favoritecities_proxymodel.cpp (100%) rename client/{gui => base}/locations/model/proxymodels/favoritecities_proxymodel.h (100%) rename client/{gui => base}/locations/model/proxymodels/sortedcities_proxymodel.cpp (100%) rename client/{gui => base}/locations/model/proxymodels/sortedcities_proxymodel.h (100%) rename client/{gui => base}/locations/model/proxymodels/sortedlocations_proxymodel.cpp (100%) rename client/{gui => base}/locations/model/proxymodels/sortedlocations_proxymodel.h (100%) rename client/{gui => base}/locations/model/proxymodels/staticips_proxymodel.cpp (100%) rename client/{gui => base}/locations/model/proxymodels/staticips_proxymodel.h (100%) rename client/{gui => base}/locations/model/selectedlocation.cpp (100%) rename client/{gui => base}/locations/model/selectedlocation.h (100%) rename client/{gui => base}/locations/model/selectedlocationwatcher.test.cpp (100%) rename client/{gui => base}/multipleaccountdetection/CMakeLists.txt (81%) rename client/{gui => base}/multipleaccountdetection/imultipleaccountdetection.h (100%) rename client/{gui => base}/multipleaccountdetection/multipleaccountdetection_posix.cpp (100%) rename client/{gui => base}/multipleaccountdetection/multipleaccountdetection_posix.h (100%) rename client/{gui => base}/multipleaccountdetection/multipleaccountdetection_win.cpp (100%) rename client/{gui => base}/multipleaccountdetection/multipleaccountdetection_win.h (100%) rename client/{gui => base}/multipleaccountdetection/multipleaccountdetectionfactory.cpp (100%) rename client/{gui => base}/multipleaccountdetection/multipleaccountdetectionfactory.h (100%) rename client/{gui => base}/multipleaccountdetection/secretvalue_win.cpp (100%) rename client/{gui => base}/multipleaccountdetection/secretvalue_win.h (100%) create mode 100644 client/base/utils/CMakeLists.txt rename client/{gui => base}/utils/authchecker_linux.cpp (100%) rename client/{gui => base}/utils/authchecker_linux.h (100%) rename client/{gui => base}/utils/authchecker_mac.h (100%) rename client/{gui => base}/utils/authchecker_mac.mm (100%) rename client/{gui => base}/utils/authchecker_win.cpp (100%) rename client/{gui => base}/utils/authchecker_win.h (100%) rename client/{gui => base}/utils/authcheckerfactory.h (100%) rename client/{gui => base}/utils/iauthchecker.h (100%) rename client/{gui => base}/utils/writeaccessrightschecker.cpp (100%) rename client/{gui => base}/utils/writeaccessrightschecker.h (100%) create mode 100644 client/cli/CMakeLists.txt create mode 100644 client/cli/application/CMakeLists.txt create mode 100644 client/cli/application/singleappinstance.cpp create mode 100644 client/cli/application/singleappinstance.h create mode 100644 client/cli/application/windscribeapplication.cpp create mode 100644 client/cli/application/windscribeapplication.h create mode 100644 client/cli/mainservice.cpp create mode 100644 client/cli/mainservice.h delete mode 100644 client/common/archive/archive.cpp delete mode 100644 client/common/archive/archive.h create mode 100644 client/common/config/openssl.cnf delete mode 100644 client/common/ipc/clicommands.cpp rename client/engine/engine/{signout_helper.h => logout_helper.h} (89%) delete mode 100644 client/engine/mac/resources/cert.pem create mode 100644 client/engine/utils/interfaceutils_mac.cpp create mode 100644 client/engine/utils/interfaceutils_mac.h delete mode 100644 client/gui/localipcserver/localipcserver.cpp delete mode 100644 client/gui/localipcserver/localipcserver.h rename client/gui/svg/preferences/{SIGN_OUT_ICON.svg => LOGOUT_ICON.svg} (100%) create mode 100644 client/main_cli.cpp rename client/{main.cpp => main_gui.cpp} (90%) create mode 100644 gui/cli/languagecontroller.cpp create mode 100644 gui/cli/languagecontroller.h create mode 100644 gui/cli/strings.cpp create mode 100644 gui/cli/strings.h create mode 100644 gui/cli/translations/windscribe_cli_ar.ts create mode 100644 gui/cli/translations/windscribe_cli_cs.ts create mode 100644 gui/cli/translations/windscribe_cli_de.ts create mode 100644 gui/cli/translations/windscribe_cli_en.ts create mode 100644 gui/cli/translations/windscribe_cli_es.ts create mode 100644 gui/cli/translations/windscribe_cli_fa.ts create mode 100644 gui/cli/translations/windscribe_cli_fr.ts create mode 100644 gui/cli/translations/windscribe_cli_hi.ts create mode 100644 gui/cli/translations/windscribe_cli_id.ts create mode 100644 gui/cli/translations/windscribe_cli_it.ts create mode 100644 gui/cli/translations/windscribe_cli_ja.ts create mode 100644 gui/cli/translations/windscribe_cli_ko.ts create mode 100644 gui/cli/translations/windscribe_cli_pl.ts create mode 100644 gui/cli/translations/windscribe_cli_pt.ts create mode 100644 gui/cli/translations/windscribe_cli_ru.ts create mode 100644 gui/cli/translations/windscribe_cli_tr.ts create mode 100644 gui/cli/translations/windscribe_cli_uk.ts create mode 100644 gui/cli/translations/windscribe_cli_vi.ts create mode 100644 gui/cli/translations/windscribe_cli_zh-CN.ts create mode 100644 gui/cli/translations/windscribe_cli_zh-TW.ts create mode 100644 installer/linux/cli/arch_package/PKGBUILD create mode 100644 installer/linux/cli/arch_package/windscribe-cli.install create mode 100644 installer/linux/cli/debian_package/DEBIAN/control create mode 100755 installer/linux/cli/debian_package/DEBIAN/postinst rename installer/linux/{ => cli}/debian_package/DEBIAN/preinst (100%) rename installer/linux/{ => cli}/debian_package/DEBIAN/prerm (100%) create mode 100644 installer/linux/cli/overlay/etc/systemd/user/windscribe.service create mode 100644 installer/linux/cli/rpm_fedora_package/SPECS/windscribe_rpm.spec create mode 100644 installer/linux/cli/rpm_opensuse_package/SPECS/windscribe_rpm.spec rename installer/linux/{ => gui}/arch_package/PKGBUILD (100%) rename installer/linux/{ => gui}/arch_package/windscribe.install (100%) rename installer/linux/{ => gui}/debian_package/DEBIAN/control (95%) rename installer/linux/{ => gui}/debian_package/DEBIAN/postinst (100%) create mode 100755 installer/linux/gui/debian_package/DEBIAN/preinst create mode 100755 installer/linux/gui/debian_package/DEBIAN/prerm rename installer/linux/{common => gui/overlay}/usr/share/applications/windscribe.desktop (91%) rename installer/linux/{common/usr/share/icons/hicolor/128x128/apps/windscribe.png => gui/overlay/usr/share/icons/hicolor/128x128/apps/Windscribe.png} (100%) rename installer/linux/{common/usr/share/icons/hicolor/16x16/apps/windscribe.png => gui/overlay/usr/share/icons/hicolor/16x16/apps/Windscribe.png} (100%) rename installer/linux/{common/usr/share/icons/hicolor/24x24/apps/windscribe.png => gui/overlay/usr/share/icons/hicolor/24x24/apps/Windscribe.png} (100%) rename installer/linux/{common/usr/share/icons/hicolor/256x256/apps/windscribe.png => gui/overlay/usr/share/icons/hicolor/256x256/apps/Windscribe.png} (100%) rename installer/linux/{common/usr/share/icons/hicolor/32x32/apps/windscribe.png => gui/overlay/usr/share/icons/hicolor/32x32/apps/Windscribe.png} (100%) rename installer/linux/{common/usr/share/icons/hicolor/48x48/apps/windscribe.png => gui/overlay/usr/share/icons/hicolor/48x48/apps/Windscribe.png} (100%) rename installer/linux/{common/usr/share/icons/hicolor/64x64/apps/windscribe.png => gui/overlay/usr/share/icons/hicolor/64x64/apps/Windscribe.png} (100%) rename installer/linux/{ => gui}/png_icons/128x128/windscribe.png (100%) rename installer/linux/{ => gui}/png_icons/16x16/windscribe.png (100%) rename installer/linux/{ => gui}/png_icons/24x24/windscribe.png (100%) rename installer/linux/{ => gui}/png_icons/256x256/windscribe.png (100%) rename installer/linux/{ => gui}/png_icons/32x32/windscribe.png (100%) rename installer/linux/{ => gui}/png_icons/48x48/windscribe.png (100%) rename installer/linux/{ => gui}/png_icons/64x64/windscribe.png (100%) rename installer/linux/{rpm_package => gui/rpm_fedora_package}/SPECS/windscribe_rpm.spec (93%) create mode 100644 installer/linux/gui/rpm_opensuse_package/SPECS/windscribe_rpm.spec create mode 100644 installer/windows/bootstrap/resources/7zr.exe create mode 100644 installer/windows/utils/archive.cpp create mode 100644 installer/windows/utils/archive.h delete mode 100644 tools/vars/ctrld.yml create mode 100644 tools/vcpkg/ports/liboqs/0000-macos-universal.patch create mode 100644 tools/vcpkg/ports/liboqs/portfile.cmake create mode 100644 tools/vcpkg/ports/liboqs/vcpkg.json create mode 100644 tools/vcpkg/ports/oqsprovider/portfile.cmake create mode 100644 tools/vcpkg/ports/oqsprovider/vcpkg.json delete mode 100644 tools/vcpkg/ports/reproc/latest_changes.patch delete mode 100644 tools/vcpkg/ports/reproc/portfile.cmake delete mode 100644 tools/vcpkg/ports/reproc/readme.txt delete mode 100644 tools/vcpkg/ports/reproc/vcpkg.json create mode 100644 tools/vcpkg/ports/scapix-bin/CMakeLists.txt create mode 100644 tools/vcpkg/ports/scapix-bin/portfile.cmake create mode 100644 tools/vcpkg/ports/scapix-bin/scapix-bin-config.cmake.in create mode 100644 tools/vcpkg/ports/scapix-bin/vcpkg.json create mode 100644 tools/vcpkg/ports/scapix/CMakeLists.txt create mode 100644 tools/vcpkg/ports/scapix/fix-project.patch create mode 100644 tools/vcpkg/ports/scapix/portfile.cmake create mode 100644 tools/vcpkg/ports/scapix/scapix-config.cmake.in create mode 100644 tools/vcpkg/ports/scapix/vcpkg.json diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index eb55b7db4..d790076f1 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -12,6 +12,7 @@ variables: GIT_DEPTH: 5 # Only grab the last 5 commits when cloning NEXUS_PATH_ROOT: 'https://nexus.int.windscribe.com/repository/client-desktop/client-desktop' NEXUS_PATH_DEPS: '$NEXUS_PATH_ROOT/dependencies/current' + NEXUS_PATH_VCPKG_CACHE: '$NEXUS_PATH_ROOT/vcpkg_cache/current' NEXUS_PATH_BRANCH_UPLOAD: '${NEXUS_PATH_ROOT}/branches/${CI_COMMIT_BRANCH}' NEXUS_PATH_TAGGED_UPLOAD: '${NEXUS_PATH_ROOT}/tagged-builds' LINUX_SIGNING_PATH: 'client/common/keys/linux' @@ -44,7 +45,6 @@ variables: interruptible: true .template_mac_build: &template_mac_build - tags: [macos11qt6] before_script: - brew install pkg-config - export VCPKG_ROOT="${HOME}/vcpkg" @@ -55,36 +55,17 @@ variables: interruptible: true .template_aarch64_ubuntu_build: &template_aarch64_ubuntu_build - image: arm64v8/ubuntu:focal + tags: [linux-arm64-qt6] + image: registry.gitlab.int.windscribe.com:5005/ws/client/desktop/client-desktop/ubuntu-aarch64 before_script: - # run this blindly until baked into image - - DEBIAN_FRONTEND=noninteractive apt-get -y update - - DEBIAN_FRONTEND=noninteractive apt-get -y install build-essential git curl libpam0g-dev software-properties-common libgl1-mesa-dev fakeroot python3-pip zip unzip - - DEBIAN_FRONTEND=noninteractive apt-get -y update - - DEBIAN_FRONTEND=noninteractive apt-get -y install wget autoconf libtool - # install cmake 3.23.x (default for Ubuntu 20.04 is 3.16.3) - - wget -qO /etc/apt/trusted.gpg.d/kitware-key.asc https://apt.kitware.com/keys/kitware-archive-latest.asc - - echo "deb https://apt.kitware.com/ubuntu/ focal main" | tee /etc/apt/sources.list.d/kitware.list - - DEBIAN_FRONTEND=noninteractive apt-get -y update - - DEBIAN_FRONTEND=noninteractive apt-get -y install cmake - - DEBIAN_FRONTEND=noninteractive apt-get -y install ninja-build - # this packages are required for building openvpn - - DEBIAN_FRONTEND=noninteractive apt-get -y install automake libnl-genl-3-dev libcap-ng-dev libpam-dev - # Qt 6 dependencies - - DEBIAN_FRONTEND=noninteractive apt-get -y install libfontconfig1-dev libfreetype6-dev libx11-dev libx11-xcb-dev libxext-dev libxfixes-dev libxi-dev libxrender-dev libxcb1-dev libxcb-glx0-dev libxcb-keysyms1-dev libxcb-image0-dev libxcb-shm0-dev libxcb-icccm4-dev libxcb-sync0-dev libxcb-xfixes0-dev libxcb-shape0-dev libxcb-randr0-dev libxcb-render-util0-dev libxcb-util-dev libxcb-xinerama0-dev libxcb-xkb-dev libxkbcommon-dev libxkbcommon-x11-dev libxcb-cursor-dev libwayland-dev - # Only necessary to install golang if we're building the WireGuard package. - #- wget -q https://go.dev/dl/go1.18.2.linux-amd64.tar.gz - #- rm -rf /usr/local/go && tar -C /usr/local -xzf go1.18.2.linux-amd64.tar.gz && export PATH=$PATH:/usr/local/go/bin - # Can't get >= 0.12 on 20.04, no snap in docker image. Use the github release for now. - - wget -q https://github.com/NixOS/patchelf/releases/download/0.18.0/patchelf-0.18.0-aarch64.tar.gz && mkdir -p tools/patchelf && tar -C tools/patchelf -xzf patchelf-0.18.0-aarch64.tar.gz - - export PATH=`pwd`/tools/patchelf/bin:$PATH - python3 -m pip install --user -r tools/requirements.txt # hack to fix 777 file permissions, which breaks the dpkg-deb command in the build_all script. - chmod -R o-w installer/linux # vcpkg settings - export VCPKG_ROOT="${HOME}/vcpkg" - export VCPKG_FORCE_SYSTEM_BINARIES=1 - - ./tools/vcpkg/install_ci/vcpkg_install.sh "${VCPKG_ROOT}" --configure-git + - wget -q https://github.com/NixOS/patchelf/releases/download/0.18.0/patchelf-0.18.0-aarch64.tar.gz && mkdir -p tools/patchelf && tar -C tools/patchelf -xzf patchelf-0.18.0-aarch64.tar.gz + - export PATH=${RHEL_CMAKE_BUILD_PATH}/bin:`pwd`/tools/patchelf/bin:$PATH - cmake --version - ninja --version interruptible: true @@ -92,28 +73,13 @@ variables: .template_rhel_build: &template_rhel_build # RHEL 8.4 is the minimum build target for Qt 6.5 # Could not use the RHEL 8.4 ubi Docker images, as they require a subscription license in order to install many of the packages we require below. - image: fedora:32 + image: registry.gitlab.int.windscribe.com:5005/ws/client/desktop/client-desktop/fedora32 before_script: - # run this blindly until baked into image - - dnf -y update - - dnf group install -y "Development Tools" - - dnf install -y gcc-c++ patchelf libxkbcommon-devel mesa-libGL-devel mesa-vulkan-devel cmake python3-pip wget rpm-build fakeroot dpkg kernel-headers perl - # this packages are required for building openvpn - - dnf install -y autoconf automake libtool libnl3-devel libcap-ng-devel pam-devel - # Qt 6 dependencies - - dnf install -y fontconfig-devel freetype-devel libX11-devel libxcb-devel xcb-util-devel xcb-util-image-devel xcb-util-cursor-devel xcb-util-keysyms-devel xcb-util-renderutil-devel libxkbcommon-x11-devel wayland-devel - - python3 -m pip install --user -r tools/requirements.txt - - python3 -m pip install --user gcovr - # install cmake 3.28.x (default for Fedora 29 is 3.14.5) - - wget -q -N https://cmake.org/files/v3.28/cmake-3.28.3-linux-x86_64.sh -P ${RHEL_CMAKE_BUILD_PATH} - - chmod u+x ${RHEL_CMAKE_BUILD_PATH}/cmake-3.28.3-linux-x86_64.sh - - ${RHEL_CMAKE_BUILD_PATH}/cmake-3.28.3-linux-x86_64.sh --skip-license --exclude-subdir --prefix=${RHEL_CMAKE_BUILD_PATH} - - export PATH=${RHEL_CMAKE_BUILD_PATH}/bin:$PATH # hack to fix 777 file permissions, which breaks the dpkg-deb command in the build_all script. - chmod -R o-w installer/linux # vcpkg settings - export VCPKG_ROOT="${HOME}/vcpkg" - - ./tools/vcpkg/install_ci/vcpkg_install.sh "${VCPKG_ROOT}" --configure-git + - export PATH=${RHEL_CMAKE_BUILD_PATH}/bin:$PATH interruptible: true .download_dependency_openvpn_dco: @@ -141,6 +107,11 @@ variables: - curl --silent --show-error --fail -u "${NEXUS_USERNAME}:${NEXUS_PASSWORD}" --cacert tools/cacert.pem --create-dirs -o ${BUILD_LIBS_FOLDER}/wstunnel.zip "${NEXUS_PATH_DEPS}/${OS_IDENTIFIER}/wstunnel.zip" +.download_vcpkg_cache: + script: + - curl --silent --show-error --fail -u "${NEXUS_USERNAME}:${NEXUS_PASSWORD}" --cacert tools/cacert.pem + --create-dirs -o vcpkg_cache.zip "${NEXUS_PATH_VCPKG_CACHE}/${OS_IDENTIFIER}/vcpkg_cache.zip" || ./tools/vcpkg/install_ci/vcpkg_install.sh "${VCPKG_ROOT}" --configure-git + .download_dependencies_win: script: - !reference [.download_dependency_openvpn_dco, script] @@ -154,6 +125,27 @@ variables: - !reference [.download_dependency_qt, script] - !reference [.download_dependency_wireguard, script] - !reference [.download_dependency_wstunnel, script] + - !reference [.download_vcpkg_cache, script] + +.package_vcpkg_cache: + script: + - rm -f vcpkg_cache.zip + - (cd ${HOME} && zip -qr vcpkg_cache.zip vcpkg) + - curl --silent --show-error --fail -u "${NEXUS_USERNAME}:${NEXUS_PASSWORD}" --cacert tools/cacert.pem + --upload-file ${HOME}/vcpkg_cache.zip "${NEXUS_PATH_VCPKG_CACHE}/${OS_IDENTIFIER}/vcpkg_cache.zip" + +.run_tests_posix: + script: + - set +o pipefail + - build/client/wsnet_test | tee output.txt; + - for i in {1..3}; do + failed_tests=$(cat output.txt | grep "\[ *FAILED" | grep -v \( | grep -v ':' | cut -c 14- | paste -sd "," -); + if [ -z "$failed_tests" ]; then + break; + else + build/client/wsnet_test --gtest_filter=$failed_tests | tee output.txt; + fi; + done; build:win:x64:app: <<: *template_win10_build @@ -479,6 +471,7 @@ build:win:arm64:bootstrap: build:mac:installer: <<: *template_mac_build + tags: [macos11qt6] stage: Build variables: GIT_STRATEGY: clone @@ -493,6 +486,7 @@ build:mac:installer: # We don't upload artifacts for merge requests build:mac:installer:mr: <<: *template_mac_build + tags: [macos-arm64-qt6] stage: Build variables: GIT_STRATEGY: clone @@ -504,6 +498,7 @@ build:mac:installer:mr: build:mac:installer:tagged: <<: *template_mac_build + tags: [macos11qt6] stage: Build variables: GIT_STRATEGY: clone @@ -522,6 +517,7 @@ build:mac:installer:tagged: script: - !reference [.download_dependencies_posix, script] - if [ -d ./${BUILD_LIBS_FOLDER}/ ]; then for z in ./${BUILD_LIBS_FOLDER}/*.zip; do unzip -qod ./${BUILD_LIBS_FOLDER} $z; done; fi + - if [ -f vcpkg_cache.zip ]; then rm -rf ${HOME}/vcpkg; unzip -qod ${HOME} vcpkg_cache.zip; fi # write public key to file - mkdir -p ${LINUX_SIGNING_PATH} # create the public key from the private key @@ -540,11 +536,24 @@ build:aarch64_ubuntu:installer: - tools/build_all --ci-mode --sign - curl --silent --show-error --fail -u "${NEXUS_USERNAME}:${NEXUS_PASSWORD}" --cacert tools/cacert.pem --upload-file build-exe/windscribe_${VERSION}_arm64.deb "${NEXUS_PATH_BRANCH_UPLOAD}/${OS_IDENTIFIER}/windscribe_${VERSION}_arm64.deb" - timeout: 4 hours + - !reference [.package_vcpkg_cache, script] + rules: + - if: $BUILD_LINUX_ARM64 == "y" && $CI_COMMIT_TAG == null && $CI_COMMIT_BRANCH != null && $NIGHTLY_TEST_BUILD != "y" + +build:aarch64_ubuntu_cli:installer: + <<: *template_aarch64_ubuntu_build + stage: Build + variables: + GIT_STRATEGY: clone + script: + - OS_IDENTIFIER="linux-arm64" + - !reference [.build_linux_installer_common, script] + - tools/build_all --ci-mode --sign --build-cli-only + - curl --silent --show-error --fail -u "${NEXUS_USERNAME}:${NEXUS_PASSWORD}" --cacert tools/cacert.pem + --upload-file build-exe/windscribe-cli_${VERSION}_arm64.deb "${NEXUS_PATH_BRANCH_UPLOAD}/${OS_IDENTIFIER}/windscribe-cli_${VERSION}_arm64.deb" + - !reference [.package_vcpkg_cache, script] rules: - if: $BUILD_LINUX_ARM64 == "y" && $CI_COMMIT_TAG == null && $CI_COMMIT_BRANCH != null && $NIGHTLY_TEST_BUILD != "y" - when: manual - allow_failure: true # We don't upload artifacts for merge requests build:aarch64_ubuntu:installer:mr: @@ -556,11 +565,20 @@ build:aarch64_ubuntu:installer:mr: - OS_IDENTIFIER="linux-arm64" - !reference [.build_linux_installer_common, script] - tools/build_all --ci-mode --sign - timeout: 4 hours rules: - if: $BUILD_LINUX_ARM64 == "y" && $CI_COMMIT_TAG == null && $CI_COMMIT_BRANCH == null && $NIGHTLY_TEST_BUILD != "y" - when: manual - allow_failure: true + +build:aarch64_ubuntu_cli:installer:mr: + <<: *template_aarch64_ubuntu_build + stage: Build + variables: + GIT_STRATEGY: clone + script: + - OS_IDENTIFIER="linux-arm64" + - !reference [.build_linux_installer_common, script] + - tools/build_all --ci-mode --sign --build-cli-only + rules: + - if: $BUILD_LINUX_ARM64 == "y" && $CI_COMMIT_TAG == null && $CI_COMMIT_BRANCH == null && $NIGHTLY_TEST_BUILD != "y" build:aarch64_ubuntu:installer:tagged: <<: *template_aarch64_ubuntu_build @@ -575,11 +593,24 @@ build:aarch64_ubuntu:installer:tagged: - if [[ $CI_COMMIT_TAG =~ ^v[0-9] ]]; then TAG=${CI_COMMIT_TAG:1}; else TAG=${CI_COMMIT_TAG}; fi - curl --silent --show-error --fail -u "${NEXUS_USERNAME}:${NEXUS_PASSWORD}" --cacert tools/cacert.pem --upload-file build-exe/windscribe_${VERSION}_arm64.deb "${NEXUS_PATH_TAGGED_UPLOAD}/${TAG}/windscribe_${VERSION}_arm64.deb" - timeout: 4 hours rules: - if: $BUILD_LINUX_ARM64 == "y" && $CI_COMMIT_TAG != null && $NIGHTLY_TEST_BUILD != "y" - when: manual - allow_failure: true + +build:aarch64_ubuntu_cli:installer:tagged: + <<: *template_aarch64_ubuntu_build + stage: Build + variables: + GIT_STRATEGY: clone + script: + - OS_IDENTIFIER="linux-arm64" + - !reference [.build_linux_installer_common, script] + - tools/build_all --ci-mode --sign --build-cli-only + - VERSION_NO_SUFFIX=$(python3 tools/base/extract.py --no-suffix) + - if [[ $CI_COMMIT_TAG =~ ^v[0-9] ]]; then TAG=${CI_COMMIT_TAG:1}; else TAG=${CI_COMMIT_TAG}; fi + - curl --silent --show-error --fail -u "${NEXUS_USERNAME}:${NEXUS_PASSWORD}" --cacert tools/cacert.pem + --upload-file build-exe/windscribe-cli_${VERSION}_arm64.deb "${NEXUS_PATH_TAGGED_UPLOAD}/${TAG}/windscribe-cli_${VERSION}_arm64.deb" + rules: + - if: $BUILD_LINUX_ARM64 == "y" && $CI_COMMIT_TAG != null && $NIGHTLY_TEST_BUILD != "y" build:rhel:installer: <<: *template_rhel_build @@ -589,12 +620,33 @@ build:rhel:installer: script: - OS_IDENTIFIER="linux" - !reference [.build_linux_installer_common, script] - - tools/build_all --ci-mode --sign --build-deb --build-rpm + - tools/build_all --ci-mode --sign --build-deb --build-rpm --build-rpm-opensuse + - curl --silent --show-error --fail -u "${NEXUS_USERNAME}:${NEXUS_PASSWORD}" --cacert tools/cacert.pem + --upload-file build-exe/windscribe_${VERSION}_x86_64_fedora.rpm "${NEXUS_PATH_BRANCH_UPLOAD}/${OS_IDENTIFIER}/windscribe_${VERSION}_x86_64_fedora.rpm" - curl --silent --show-error --fail -u "${NEXUS_USERNAME}:${NEXUS_PASSWORD}" --cacert tools/cacert.pem - --upload-file build-exe/windscribe_${VERSION}_x86_64.rpm "${NEXUS_PATH_BRANCH_UPLOAD}/${OS_IDENTIFIER}/windscribe_${VERSION}_x86_64.rpm" + --upload-file build-exe/windscribe_${VERSION}_x86_64_opensuse.rpm "${NEXUS_PATH_BRANCH_UPLOAD}/${OS_IDENTIFIER}/windscribe_${VERSION}_x86_64_opensuse.rpm" - curl --silent --show-error --fail -u "${NEXUS_USERNAME}:${NEXUS_PASSWORD}" --cacert tools/cacert.pem --upload-file build-exe/windscribe_${VERSION}_amd64.deb "${NEXUS_PATH_BRANCH_UPLOAD}/${OS_IDENTIFIER}/windscribe_${VERSION}_amd64.deb" + - !reference [.package_vcpkg_cache, script] + rules: + - if: $BUILD_LINUX == "y" && $CI_COMMIT_TAG == null && $CI_COMMIT_BRANCH != null && $NIGHTLY_TEST_BUILD != "y" +build:rhel_cli:installer: + <<: *template_rhel_build + stage: Build + variables: + GIT_STRATEGY: clone + script: + - OS_IDENTIFIER="linux" + - !reference [.build_linux_installer_common, script] + - tools/build_all --ci-mode --sign --build-deb --build-rpm --build-rpm-opensuse --build-cli-only + - curl --silent --show-error --fail -u "${NEXUS_USERNAME}:${NEXUS_PASSWORD}" --cacert tools/cacert.pem + --upload-file build-exe/windscribe-cli_${VERSION}_x86_64_fedora.rpm "${NEXUS_PATH_BRANCH_UPLOAD}/${OS_IDENTIFIER}/windscribe-cli_${VERSION}_x86_64_fedora.rpm" + - curl --silent --show-error --fail -u "${NEXUS_USERNAME}:${NEXUS_PASSWORD}" --cacert tools/cacert.pem + --upload-file build-exe/windscribe-cli_${VERSION}_x86_64_opensuse.rpm "${NEXUS_PATH_BRANCH_UPLOAD}/${OS_IDENTIFIER}/windscribe-cli_${VERSION}_x86_64_opensuse.rpm" + - curl --silent --show-error --fail -u "${NEXUS_USERNAME}:${NEXUS_PASSWORD}" --cacert tools/cacert.pem + --upload-file build-exe/windscribe-cli_${VERSION}_amd64.deb "${NEXUS_PATH_BRANCH_UPLOAD}/${OS_IDENTIFIER}/windscribe-cli_${VERSION}_amd64.deb" + - !reference [.package_vcpkg_cache, script] rules: - if: $BUILD_LINUX == "y" && $CI_COMMIT_TAG == null && $CI_COMMIT_BRANCH != null && $NIGHTLY_TEST_BUILD != "y" @@ -607,7 +659,19 @@ build:rhel:installer:mr: script: - OS_IDENTIFIER="linux" - !reference [.build_linux_installer_common, script] - - tools/build_all --ci-mode --sign --build-deb --build-rpm + - tools/build_all --ci-mode --sign --build-deb --build-rpm --build-rpm-opensuse + rules: + - if: $BUILD_LINUX == "y" && $CI_COMMIT_TAG == null && $CI_COMMIT_BRANCH == null && $NIGHTLY_TEST_BUILD != "y" + +build:rhel_cli:installer:mr: + <<: *template_rhel_build + stage: Build + variables: + GIT_STRATEGY: clone + script: + - OS_IDENTIFIER="linux" + - !reference [.build_linux_installer_common, script] + - tools/build_all --ci-mode --sign --build-deb --build-rpm --build-rpm-opensuse --build-cli-only rules: - if: $BUILD_LINUX == "y" && $CI_COMMIT_TAG == null && $CI_COMMIT_BRANCH == null && $NIGHTLY_TEST_BUILD != "y" @@ -619,22 +683,40 @@ build:rhel:installer:tagged: script: - OS_IDENTIFIER="linux" - !reference [.build_linux_installer_common, script] - - tools/build_all --ci-mode --sign --build-deb --build-rpm + - tools/build_all --ci-mode --sign --build-deb --build-rpm --build-rpm-opensuse - VERSION_NO_SUFFIX=$(python3 tools/base/extract.py --no-suffix) - if [[ $CI_COMMIT_TAG =~ ^v[0-9] ]]; then TAG=${CI_COMMIT_TAG:1}; else TAG=${CI_COMMIT_TAG}; fi - curl --silent --show-error --fail -u "${NEXUS_USERNAME}:${NEXUS_PASSWORD}" --cacert tools/cacert.pem - --upload-file build-exe/windscribe_${VERSION}_x86_64.rpm "${NEXUS_PATH_TAGGED_UPLOAD}/${TAG}/windscribe_${VERSION}_x86_64.rpm" + --upload-file build-exe/windscribe_${VERSION}_x86_64_fedora.rpm "${NEXUS_PATH_TAGGED_UPLOAD}/${TAG}/windscribe_${VERSION}_x86_64_fedora.rpm" + - curl --silent --show-error --fail -u "${NEXUS_USERNAME}:${NEXUS_PASSWORD}" --cacert tools/cacert.pem + --upload-file build-exe/windscribe_${VERSION}_x86_64_opensuse.rpm "${NEXUS_PATH_TAGGED_UPLOAD}/${TAG}/windscribe_${VERSION}_x86_64_opensuse.rpm" - curl --silent --show-error --fail -u "${NEXUS_USERNAME}:${NEXUS_PASSWORD}" --cacert tools/cacert.pem --upload-file build-exe/windscribe_${VERSION}_amd64.deb "${NEXUS_PATH_TAGGED_UPLOAD}/${TAG}/windscribe_${VERSION}_amd64.deb" + rules: + - if: $BUILD_LINUX == "y" && $CI_COMMIT_TAG != null && $NIGHTLY_TEST_BUILD != "y" +build:rhel_cli:installer:tagged: + <<: *template_rhel_build + stage: Build + variables: + GIT_STRATEGY: clone + script: + - OS_IDENTIFIER="linux" + - !reference [.build_linux_installer_common, script] + - tools/build_all --ci-mode --sign --build-deb --build-rpm --build-rpm-opensuse --build-cli-only + - VERSION_NO_SUFFIX=$(python3 tools/base/extract.py --no-suffix) + - if [[ $CI_COMMIT_TAG =~ ^v[0-9] ]]; then TAG=${CI_COMMIT_TAG:1}; else TAG=${CI_COMMIT_TAG}; fi + - curl --silent --show-error --fail -u "${NEXUS_USERNAME}:${NEXUS_PASSWORD}" --cacert tools/cacert.pem + --upload-file build-exe/windscribe-cli_${VERSION}_x86_64_fedora.rpm "${NEXUS_PATH_TAGGED_UPLOAD}/${TAG}/windscribe-cli_${VERSION}_x86_64_fedora.rpm" + - curl --silent --show-error --fail -u "${NEXUS_USERNAME}:${NEXUS_PASSWORD}" --cacert tools/cacert.pem + --upload-file build-exe/windscribe-cli_${VERSION}_x86_64_opensuse.rpm "${NEXUS_PATH_TAGGED_UPLOAD}/${TAG}/windscribe-cli_${VERSION}_x86_64_opensuse.rpm" + - curl --silent --show-error --fail -u "${NEXUS_USERNAME}:${NEXUS_PASSWORD}" --cacert tools/cacert.pem + --upload-file build-exe/windscribe-cli_${VERSION}_amd64.deb "${NEXUS_PATH_TAGGED_UPLOAD}/${TAG}/windscribe-cli_${VERSION}_amd64.deb" rules: - if: $BUILD_LINUX == "y" && $CI_COMMIT_TAG != null && $NIGHTLY_TEST_BUILD != "y" .template_archlinux_build: &template_archlinux_build - image: archlinux:base-devel - before_script: - - pacman -Syu --noconfirm - - pacman -Sy --noconfirm git curl python nftables c-ares freetype2 hicolor-icon-theme libglvnd fontconfig libx11 libxkbcommon libxcb net-tools xcb-util-wm xcb-util-image xcb-util-keysyms xcb-util-renderutil xcb-util-cursor networkmanager + image: registry.gitlab.int.windscribe.com:5005/ws/client/desktop/client-desktop/arch .build_archlinux_installer_prep_build_dir: script: @@ -644,7 +726,6 @@ build:rhel:installer:tagged: - chmod g+ws ${ARCH_LINUX_BUILD_PATH} - setfacl -m u::rwx,g::rwx ${ARCH_LINUX_BUILD_PATH} - setfacl -d --set u::rwx,g::rwx,o::- ${ARCH_LINUX_BUILD_PATH} - - cp installer/linux/arch_package/* ${ARCH_LINUX_BUILD_PATH} - export VERSION=$(python3 tools/base/extract.py) - export VERSION_NO_SUFFIX=$(python3 tools/base/extract.py --no-suffix) @@ -663,6 +744,7 @@ build:archlinux:installer: script: - OS_IDENTIFIER="linux" - !reference [.build_archlinux_installer_prep_build_dir, script] + - cp installer/linux/gui/arch_package/* ${ARCH_LINUX_BUILD_PATH} - export APP_DOWNLOAD_URL="${NEXUS_PATH_BRANCH_UPLOAD}/${OS_IDENTIFIER}/windscribe_${VERSION}_amd64.deb" - !reference [.build_archlinux_installer_makepkg, script] - curl --silent --show-error --fail -u "${NEXUS_USERNAME}:${NEXUS_PASSWORD}" --cacert tools/cacert.pem @@ -674,6 +756,26 @@ build:archlinux:installer: rules: - if: $BUILD_LINUX == "y" && $CI_COMMIT_TAG == null && $CI_COMMIT_BRANCH != null && $NIGHTLY_TEST_BUILD != "y" +build:archlinux_cli:installer: + <<: *template_archlinux_build + stage: Build + variables: + GIT_STRATEGY: clone + script: + - OS_IDENTIFIER="linux" + - !reference [.build_archlinux_installer_prep_build_dir, script] + - cp installer/linux/cli/arch_package/* ${ARCH_LINUX_BUILD_PATH} + - export APP_DOWNLOAD_URL="${NEXUS_PATH_BRANCH_UPLOAD}/${OS_IDENTIFIER}/windscribe-cli_${VERSION}_amd64.deb" + - !reference [.build_archlinux_installer_makepkg, script] + - curl --silent --show-error --fail -u "${NEXUS_USERNAME}:${NEXUS_PASSWORD}" --cacert tools/cacert.pem + --upload-file ${ARCH_LINUX_BUILD_PATH}/windscribe-cli-$VERSION_NO_SUFFIX-1-x86_64.pkg.tar.zst + "${NEXUS_PATH_BRANCH_UPLOAD}/${OS_IDENTIFIER}/windscribe-cli_${VERSION}_x86_64.pkg.tar.zst" + needs: + - job: "build:rhel_cli:installer" + optional: true + rules: + - if: $BUILD_LINUX == "y" && $CI_COMMIT_TAG == null && $CI_COMMIT_BRANCH != null && $NIGHTLY_TEST_BUILD != "y" + build:archlinux:installer:tagged: <<: *template_archlinux_build stage: Build @@ -681,6 +783,7 @@ build:archlinux:installer:tagged: GIT_STRATEGY: clone script: - !reference [.build_archlinux_installer_prep_build_dir, script] + - cp installer/linux/gui/arch_package/* ${ARCH_LINUX_BUILD_PATH} - export APP_DOWNLOAD_URL=${NEXUS_PATH_TAGGED_UPLOAD}/$VERSION_NO_SUFFIX/windscribe_${VERSION}_amd64.deb - !reference [.build_archlinux_installer_makepkg, script] - if [[ $CI_COMMIT_TAG =~ ^v[0-9] ]]; then TAG=${CI_COMMIT_TAG:1}; else TAG=${CI_COMMIT_TAG}; fi @@ -693,6 +796,26 @@ build:archlinux:installer:tagged: rules: - if: $BUILD_LINUX == "y" && $CI_COMMIT_TAG != null && $NIGHTLY_TEST_BUILD != "y" +build:archlinux_cli:installer:tagged: + <<: *template_archlinux_build + stage: Build + variables: + GIT_STRATEGY: clone + script: + - !reference [.build_archlinux_installer_prep_build_dir, script] + - cp installer/linux/cli/arch_package/* ${ARCH_LINUX_BUILD_PATH} + - export APP_DOWNLOAD_URL=${NEXUS_PATH_TAGGED_UPLOAD}/$VERSION_NO_SUFFIX/windscribe-cli_${VERSION}_amd64.deb + - !reference [.build_archlinux_installer_makepkg, script] + - if [[ $CI_COMMIT_TAG =~ ^v[0-9] ]]; then TAG=${CI_COMMIT_TAG:1}; else TAG=${CI_COMMIT_TAG}; fi + - curl --silent --show-error --fail -u "${NEXUS_USERNAME}:${NEXUS_PASSWORD}" --cacert tools/cacert.pem + --upload-file ${ARCH_LINUX_BUILD_PATH}/windscribe-cli-$VERSION_NO_SUFFIX-1-x86_64.pkg.tar.zst + "${NEXUS_PATH_TAGGED_UPLOAD}/${TAG}/windscribe-cli_${VERSION}_x86_64.pkg.tar.zst" + needs: + - job: "build:rhel_cli:installer:tagged" + optional: true + rules: + - if: $BUILD_LINUX == "y" && $CI_COMMIT_TAG != null && $NIGHTLY_TEST_BUILD != "y" + build:artifact:links: tags: [win10qty6] stage: Artifact Links @@ -702,8 +825,13 @@ build:artifact:links: - echo ${NEXUS_PATH_BRANCH_UPLOAD}/windows-arm64/Windscribe_${VERSION}_arm64.exe - echo ${NEXUS_PATH_BRANCH_UPLOAD}/macos/Windscribe_$VERSION.dmg - echo ${NEXUS_PATH_BRANCH_UPLOAD}/linux/windscribe_${VERSION}_amd64.deb - - echo ${NEXUS_PATH_BRANCH_UPLOAD}/linux/windscribe_${VERSION}_x86_64.rpm + - echo ${NEXUS_PATH_BRANCH_UPLOAD}/linux/windscribe-cli_${VERSION}_amd64.deb + - echo ${NEXUS_PATH_BRANCH_UPLOAD}/linux/windscribe_${VERSION}_x86_64_fedora.rpm + - echo ${NEXUS_PATH_BRANCH_UPLOAD}/linux/windscribe-cli_${VERSION}_x86_64_fedora.rpm + - echo ${NEXUS_PATH_BRANCH_UPLOAD}/linux/windscribe_${VERSION}_x86_64_opensuse.rpm + - echo ${NEXUS_PATH_BRANCH_UPLOAD}/linux/windscribe-cli_${VERSION}_x86_64_opensuse.rpm - echo ${NEXUS_PATH_BRANCH_UPLOAD}/linux/windscribe_${VERSION}_x86_64.pkg.tar.zst + - echo ${NEXUS_PATH_BRANCH_UPLOAD}/linux/windscribe-cli_${VERSION}_x86_64.pkg.tar.zst dependencies: [] rules: - if: $CI_COMMIT_TAG == null && $CI_COMMIT_BRANCH != null && $NIGHTLY_TEST_BUILD != "y" @@ -718,8 +846,13 @@ build:artifact:links:tagged: - echo ${NEXUS_PATH_TAGGED_UPLOAD}/${TAG}/Windscribe_${VERSION}_arm64.exe - echo ${NEXUS_PATH_TAGGED_UPLOAD}/${TAG}/Windscribe_$VERSION.dmg - echo ${NEXUS_PATH_TAGGED_UPLOAD}/${TAG}/windscribe_${VERSION}_amd64.deb - - echo ${NEXUS_PATH_TAGGED_UPLOAD}/${TAG}/windscribe_${VERSION}_x86_64.rpm + - echo ${NEXUS_PATH_TAGGED_UPLOAD}/${TAG}/windscribe-cli_${VERSION}_amd64.deb + - echo ${NEXUS_PATH_TAGGED_UPLOAD}/${TAG}/windscribe_${VERSION}_x86_64_fedora.rpm + - echo ${NEXUS_PATH_TAGGED_UPLOAD}/${TAG}/windscribe-cli_${VERSION}_x86_64_fedora.rpm + - echo ${NEXUS_PATH_TAGGED_UPLOAD}/${TAG}/windscribe_${VERSION}_x86_64_opensuse.rpm + - echo ${NEXUS_PATH_TAGGED_UPLOAD}/${TAG}/windscribe-cli_${VERSION}_x86_64_opensuse.rpm - echo ${NEXUS_PATH_TAGGED_UPLOAD}/${TAG}/windscribe_${VERSION}_x86_64.pkg.tar.zst + - echo ${NEXUS_PATH_TAGGED_UPLOAD}/${TAG}/windscribe-cli_${VERSION}_x86_64.pkg.tar.zst dependencies: [] rules: - if: $CI_COMMIT_TAG != null && $NIGHTLY_TEST_BUILD != "y" @@ -763,19 +896,30 @@ test:wsnet:win: - 'IF(Test-Path .\$BUILD_LIBS_FOLDER\) {Get-ChildItem .\$BUILD_LIBS_FOLDER\*.zip | Foreach {.\tools\bin\7z.exe x $_.FullName -o"$BUILD_LIBS_FOLDER\"}}' - tools/build_all --build-app --build-tests - cd build/test - - ./wsnet_test.exe + - ./wsnet_test.exe | Tee-Object -Variable TestOutput; + For ($i=1; $i -le 3; $i++) { + $FailedTests = ($TestOutput -match "^\[ FAILED \] .*[^:)]$"); + if (!$FailedTests) { + break; + } else { + $TestString = $FailedTests.SubString(13) -join ':'; + ./wsnet_test.exe --gtest_filter=$TestString | Tee-Object -Variable TestOutput; + } + } rules: - if: $BUILD_WIN == "y" && $NIGHTLY_TEST_BUILD == "y" test:wsnet:mac: <<: *template_mac_build + tags: [macos11qt6] stage: Test variables: GIT_STRATEGY: clone script: - !reference [.build_mac_installer_common, script] - tools/build_all --build-app --build-tests - - build/client/wsnet_test + - !reference [.run_tests_posix, script] + after_script: - python3 -m gcovr --xml-pretty --exclude-unreachable-branches --print-summary -o coverage.xml --root ${CI_PROJECT_DIR} coverage: /^\s*lines:\s*\d+.\d+\%/ @@ -798,7 +942,8 @@ test:wsnet:rhel: - OS_IDENTIFIER="linux" - !reference [.build_linux_installer_common, script] - tools/build_all --build-app --build-tests - - build/client/wsnet_test + - !reference [.run_tests_posix, script] + after_script: - python3 -m gcovr --xml-pretty --exclude-unreachable-branches --print-summary -o coverage.xml --root ${CI_PROJECT_DIR} coverage: /^\s*lines:\s*\d+.\d+\%/ @@ -811,4 +956,3 @@ test:wsnet:rhel: path: coverage.xml rules: - if: $BUILD_LINUX == "y" && $NIGHTLY_TEST_BUILD == "y" - diff --git a/README.md b/README.md index 26849927c..25e1ed2a7 100644 --- a/README.md +++ b/README.md @@ -92,11 +92,10 @@ See `build_all --help` for other build options. ```bash brew install git ``` -- Install Auto-Tools and 7-Zip: +- Install Auto-Tools: ```bash brew install libtool brew install automake - brew install p7zip ``` - Install Python 3: - Minimum tested version is Python 3.6.8. 3.12.0 seems to have some issues with the python deps, so 3.11.6 is the latest recommended version. You may do this however you like, however `pyenv` is recommended: diff --git a/backend/linux/helper/CMakeLists.txt b/backend/linux/helper/CMakeLists.txt index 4be5fa913..0beb1595d 100644 --- a/backend/linux/helper/CMakeLists.txt +++ b/backend/linux/helper/CMakeLists.txt @@ -25,6 +25,7 @@ set(SOURCES ../../../client/common/utils/executable_signature/executablesignature_linux.cpp execute_cmd.cpp firewallcontroller.cpp + firewallonboot.cpp ipc/helper_security.cpp logger.cpp main.cpp diff --git a/backend/linux/helper/execute_cmd.cpp b/backend/linux/helper/execute_cmd.cpp index efb7a3fac..6c1481b73 100644 --- a/backend/linux/helper/execute_cmd.cpp +++ b/backend/linux/helper/execute_cmd.cpp @@ -2,7 +2,7 @@ #include #include -unsigned long ExecuteCmd::execute(const std::string &cmd, const std::string &cwd) +unsigned long ExecuteCmd::execute(const std::string &cmd, const std::string &cwd, bool deleteOnFinish) { mutex_.lock(); curCmdId_++; @@ -14,9 +14,9 @@ unsigned long ExecuteCmd::execute(const std::string &cmd, const std::string &cwd mutex_.unlock(); if (!cwd.empty()) { - boost::thread(runCmd, curCmdId_, "cd \"" + cwd + "\" && " + cmd); + boost::thread(runCmd, curCmdId_, "cd \"" + cwd + "\" && " + cmd, deleteOnFinish); } else { - boost::thread(runCmd, curCmdId_, cmd); + boost::thread(runCmd, curCmdId_, cmd, deleteOnFinish); } return curCmdId_; @@ -55,7 +55,7 @@ ExecuteCmd::ExecuteCmd() : curCmdId_(0) { } -void ExecuteCmd::runCmd(unsigned long cmdId, std::string cmd) +void ExecuteCmd::runCmd(unsigned long cmdId, std::string cmd, bool deleteOnFinish) { std::string strReply; @@ -69,20 +69,25 @@ void ExecuteCmd::runCmd(unsigned long cmdId, std::string cmd) } } pclose(file); - instance().cmdFinished(cmdId, true, strReply); + instance().cmdFinished(cmdId, true, strReply, deleteOnFinish); } else { - instance().cmdFinished(cmdId, false, std::string()); + instance().cmdFinished(cmdId, false, std::string(), deleteOnFinish); } } -void ExecuteCmd::cmdFinished(unsigned long cmdId, bool bSuccess, std::string log) +void ExecuteCmd::cmdFinished(unsigned long cmdId, bool bSuccess, std::string log, bool del) { mutex_.lock(); for (auto it = executingCmds_.begin(); it != executingCmds_.end(); ++it) { if ((*it)->cmdId == cmdId) { - (*it)->bFinished = true; - (*it)->bSuccess = bSuccess; - (*it)->log = log; + if (del) { + delete(*it); + executingCmds_.erase(it); + } else { + (*it)->bFinished = true; + (*it)->bSuccess = bSuccess; + (*it)->log = log; + } break; } } diff --git a/backend/linux/helper/execute_cmd.h b/backend/linux/helper/execute_cmd.h index 018d58c10..73665ce7a 100644 --- a/backend/linux/helper/execute_cmd.h +++ b/backend/linux/helper/execute_cmd.h @@ -14,7 +14,7 @@ class ExecuteCmd return i; } - unsigned long execute(const std::string &cmd, const std::string &cwd = ""); + unsigned long execute(const std::string &cmd, const std::string &cwd = "", bool deleteOnFinish = false); void getStatus(unsigned long cmdId, bool &bFinished, std::string &log); void clearCmds(); @@ -23,8 +23,8 @@ class ExecuteCmd unsigned long curCmdId_; - static void runCmd(unsigned long cmdId, std::string cmd); - void cmdFinished(unsigned long cmdId, bool bSuccess, std::string log); + static void runCmd(unsigned long cmdId, std::string cmd, bool deleteOnFinish); + void cmdFinished(unsigned long cmdId, bool bSuccess, std::string log, bool del); bool isCmdExist(unsigned long cmdId); struct CmdDescr diff --git a/backend/linux/helper/firewallcontroller.cpp b/backend/linux/helper/firewallcontroller.cpp index f1038a496..c53a190e9 100644 --- a/backend/linux/helper/firewallcontroller.cpp +++ b/backend/linux/helper/firewallcontroller.cpp @@ -9,6 +9,21 @@ #include "logger.h" #include "utils.h" +FirewallController::FirewallController() : connected_(false), splitTunnelEnabled_(false), splitTunnelExclude_(true) +{ + // If firewall on boot is enabled, restore boot rules + if (Utils::isFileExists("/etc/windscribe/boot_rules.v4")) { + Utils::executeCommand("iptables-restore", {"-n", "/etc/windscribe/boot_rules.v4"}); + } + if (Utils::isFileExists("/etc/windscribe/boot_rules.v6")) { + Utils::executeCommand("ip6tables-restore", {"-n", "/etc/windscribe/boot_rules.v6"}); + } +} + +FirewallController::~FirewallController() +{ +} + bool FirewallController::enable(bool ipv6, const std::string &rules) { int fd; diff --git a/backend/linux/helper/firewallcontroller.h b/backend/linux/helper/firewallcontroller.h index 96e40167a..23a8eb7bd 100644 --- a/backend/linux/helper/firewallcontroller.h +++ b/backend/linux/helper/firewallcontroller.h @@ -27,8 +27,8 @@ class FirewallController void setSplitTunnelIpExceptions(const std::vector &ips); private: - FirewallController() : connected_(false), splitTunnelEnabled_(false), splitTunnelExclude_(true) {}; - ~FirewallController() { disable(); }; + FirewallController(); + ~FirewallController(); bool connected_; bool splitTunnelEnabled_; diff --git a/backend/linux/helper/firewallonboot.cpp b/backend/linux/helper/firewallonboot.cpp new file mode 100644 index 000000000..2413dbb80 --- /dev/null +++ b/backend/linux/helper/firewallonboot.cpp @@ -0,0 +1,113 @@ +#include "firewallonboot.h" +#include +#include +#include +#include +#include "logger.h" +#include "utils.h" + +FirewallOnBootManager::FirewallOnBootManager(): comment_("Windscribe client rule") +{ +} + +FirewallOnBootManager::~FirewallOnBootManager() +{ +} + +bool FirewallOnBootManager::setEnabled(bool enabled, bool allowLanTraffic) +{ + if (enabled) { + return enable(allowLanTraffic); + } + return disable(); +} + +bool FirewallOnBootManager::enable(bool allowLanTraffic) { + std::stringstream rules; + int bytes; + + rules << "*filter\n"; + rules << ":windscribe_input - [0:0]\n"; + rules << ":windscribe_output - [0:0]\n"; + rules << "-I INPUT -j windscribe_input -m comment --comment \"" + comment_ + "\"\n"; + rules << "-I OUTPUT -j windscribe_output -m comment --comment \"" + comment_ + "\"\n"; + rules << "-A windscribe_input -i lo -j ACCEPT -m comment --comment \"" + comment_ + "\"\n"; + rules << "-A windscribe_output -o lo -j ACCEPT -m comment --comment \"" + comment_ + "\"\n"; + rules << "-A windscribe_input -p udp --sport 67 --dport 68 -j ACCEPT -m comment --comment \"" + comment_ + "\"\n"; + rules << "-A windscribe_output -p udp --sport 68 --dport 67 -j ACCEPT -m comment --comment \"" + comment_ + "\"\n"; + + std::istringstream ips(ipTable_); + std::string ip; + + while (std::getline(ips, ip, ' ')) { + rules << "-A windscribe_input -s " + ip + " -j ACCEPT -m comment --comment \"" + comment_ + "\"\n"; + rules << "-A windscribe_output -d " + ip + " -j ACCEPT -m comment --comment \"" + comment_ + "\"\n"; + } + + if (allowLanTraffic) { + // Local Network + rules << "-A windscribe_input -s 192.168.0.0/16 -j ACCEPT -m comment --comment \"" + comment_ + "\"\n"; + rules << "-A windscribe_output -d 192.168.0.0/16 -j ACCEPT -m comment --comment \"" + comment_ + "\"\n"; + + rules << "-A windscribe_input -s 172.16.0.0/12 -j ACCEPT -m comment --comment \"" + comment_ + "\"\n"; + rules << "-A windscribe_output -d 172.16.0.0/12 -j ACCEPT -m comment --comment \"" + comment_ + "\"\n"; + + rules << "-A windscribe_input -s 169.254.0.0/16 -j ACCEPT -m comment --comment \"" + comment_ + "\"\n"; + rules << "-A windscribe_output -d 169.254.0.0/16 -j ACCEPT -m comment --comment \"" + comment_ + "\"\n"; + + rules << "-A windscribe_input -s 10.255.255.0/24 -j DROP -m comment --comment \"" + comment_ + "\"\n"; + rules << "-A windscribe_output -d 10.255.255.0/24 -j DROP -m comment --comment \"" + comment_ + "\"\n"; + rules << "-A windscribe_input -s 10.0.0.0/8 -j ACCEPT -m comment --comment \"" + comment_ + "\"\n"; + rules << "-A windscribe_output -d 10.0.0.0/8 -j ACCEPT -m comment --comment \"" + comment_ + "\"\n"; + + // Multicast addresses + rules << "-A windscribe_input -s 224.0.0.0/4 -j ACCEPT -m comment --comment \"" + comment_ + "\"\n"; + rules << "-A windscribe_output -d 224.0.0.0/4 -j ACCEPT -m comment --comment \"" + comment_ + "\"\n"; + } + rules << "-A windscribe_input -j DROP -m comment --comment \"" + comment_ + "\"\n"; + rules << "-A windscribe_output -j DROP -m comment --comment \"" + comment_ + "\"\n"; + rules << "COMMIT\n"; + + // write rules + int fd = open("/etc/windscribe/boot_rules.v4", O_CREAT | O_WRONLY | O_TRUNC, S_IRWXU | S_IRGRP | S_IROTH); + if (fd < 0) { + Logger::instance().out("Could not open boot firewall rules for writing"); + return false; + } + + bytes = write(fd, rules.str().c_str(), rules.str().length()); + close(fd); + + rules.str(""); + rules.clear(); + + rules << "*filter\n"; + rules << ":windscribe_input - [0:0]\n"; + rules << ":windscribe_output - [0:0]\n"; + rules << "-A INPUT -j windscribe_input -m comment --comment \"" + comment_ + "\"\n"; + rules << "-A OUTPUT -j windscribe_output -m comment --comment \"" + comment_ + "\"\n"; + rules << "-A windscribe_input -s ::1/128 -j ACCEPT -m comment --comment \"" + comment_ + "\"\n"; + rules << "-A windscribe_output -d ::1/128 -j ACCEPT -m comment --comment \"" + comment_ + "\"\n"; + rules << "-A windscribe_input -j DROP -m comment --comment \"" + comment_ + "\"\n"; + rules << "-A windscribe_output -j DROP -m comment --comment \"" + comment_ + "\"\n"; + rules << "COMMIT\n"; + + // write rules + fd = open("/etc/windscribe/boot_rules.v6", O_CREAT | O_WRONLY | O_TRUNC, S_IRWXU | S_IRGRP | S_IROTH); + if (fd < 0) { + Logger::instance().out("Could not open v6 boot firewall rules for writing"); + return false; + } + + bytes = write(fd, rules.str().c_str(), rules.str().length()); + close(fd); + + return true; +} + +bool FirewallOnBootManager::disable() +{ + Utils::executeCommand("rm", {"-f", "/etc/windscribe/boot_rules.v4"}); + Utils::executeCommand("rm", {"-f", "/etc/windscribe/boot_rules.v6"}); + return true; +} diff --git a/backend/linux/helper/firewallonboot.h b/backend/linux/helper/firewallonboot.h new file mode 100644 index 000000000..dd3031d7a --- /dev/null +++ b/backend/linux/helper/firewallonboot.h @@ -0,0 +1,27 @@ +#pragma once + +#include + +class FirewallOnBootManager +{ +public: + static FirewallOnBootManager& instance() + { + static FirewallOnBootManager fobm; + return fobm; + } + + bool setEnabled(bool enabled, bool allowLanTraffic); + void setIpTable(const std::string& ipTable) { ipTable_ = ipTable; } + +private: + FirewallOnBootManager(); + ~FirewallOnBootManager(); + + std::string comment_; + + bool enable(bool allowLanTraffic); + bool disable(); + + std::string ipTable_; +}; diff --git a/backend/linux/helper/main.cpp b/backend/linux/helper/main.cpp index b32b8116a..be296bce9 100644 --- a/backend/linux/helper/main.cpp +++ b/backend/linux/helper/main.cpp @@ -27,17 +27,6 @@ int main(int argc, const char *argv[]) Logger::instance().checkLogSize(); - // restore firewall setting on OS reboot, if there are saved rules on /etc/windscribe dir - - if (Utils::isFileExists("/etc/windscribe/rules.v4")) - { - Utils::executeCommand("iptables-restore -n < /etc/windscribe/rules.v4"); - } - if (Utils::isFileExists("/etc/windscribe/rules.v6")) - { - Utils::executeCommand("ip6tables-restore -n < /etc/windscribe/rules.v6"); - } - server.run(); Logger::instance().out("Windscribe helper finished"); diff --git a/backend/linux/helper/process_command.cpp b/backend/linux/helper/process_command.cpp index 2c661f858..5f6cf003a 100644 --- a/backend/linux/helper/process_command.cpp +++ b/backend/linux/helper/process_command.cpp @@ -7,6 +7,7 @@ #include #include "execute_cmd.h" #include "firewallcontroller.h" +#include "firewallonboot.h" #include "logger.h" #include "ovpn.h" #include "routes_manager/routes_manager.h" @@ -295,6 +296,19 @@ CMD_ANSWER getFirewallRules(boost::archive::text_iarchive &ia) return answer; } +CMD_ANSWER setFirewallOnBoot(boost::archive::text_iarchive &ia) +{ + CMD_ANSWER answer; + CMD_SET_FIREWALL_ON_BOOT cmd; + ia >> cmd; + Logger::instance().out("Set firewall on boot: %s", cmd.enabled ? "true" : "false"); + + FirewallOnBootManager::instance().setIpTable(cmd.ipTable); + answer.executed = FirewallOnBootManager::instance().setEnabled(cmd.enabled, cmd.allowLanTraffic); + + return answer; +} + CMD_ANSWER taskKill(boost::archive::text_iarchive &ia) { CMD_ANSWER answer; @@ -343,14 +357,10 @@ CMD_ANSWER startCtrld(boost::archive::text_iarchive &ia) CMD_START_CTRLD cmd; ia >> cmd; - if (!Utils::isValidIpAddress(cmd.ip)) { - Logger::instance().out("Invalid IP address: %s", cmd.ip.c_str()); - answer.executed = 0; - return answer; - } + Logger::instance().out("Starting ctrld"); // Validate URLs - if ((!Utils::isValidUrl(cmd.upstream1)) || (!cmd.upstream2.empty() && !Utils::isValidUrl(cmd.upstream2))) { + if (!Utils::isValidUrl(cmd.upstream1) || (!cmd.upstream2.empty() && !Utils::isValidUrl(cmd.upstream2))) { Logger::instance().out("Invalid upstream URL(s)"); answer.executed = 0; return answer; @@ -365,7 +375,8 @@ CMD_ANSWER startCtrld(boost::archive::text_iarchive &ia) std::stringstream arguments; arguments << "run"; - arguments << " --listen=" + cmd.ip + ":53"; + arguments << " --daemon"; + arguments << " --listen=127.0.0.1:53"; arguments << " --primary_upstream=" + cmd.upstream1; if (!cmd.upstream2.empty()) { arguments << " --secondary_upstream=" + cmd.upstream2; @@ -397,8 +408,7 @@ CMD_ANSWER startCtrld(boost::archive::text_iarchive &ia) Logger::instance().out("ctrld executable signature incorrect: %s", sigCheck.lastError().c_str()); answer.executed = 0; } else { - answer.cmdId = ExecuteCmd::instance().execute(fullCmd, std::string()); - answer.executed = 1; + answer.executed = Utils::executeCommand(fullCmd) ? 0 : 1; } return answer; } @@ -437,7 +447,7 @@ CMD_ANSWER startStunnel(boost::archive::text_iarchive &ia) Logger::instance().out("stunnel executable signature incorrect: %s", sigCheck.lastError().c_str()); answer.executed = 0; } else { - answer.cmdId = ExecuteCmd::instance().execute(fullCmd, std::string()); + answer.cmdId = ExecuteCmd::instance().execute(fullCmd, std::string(), true); answer.executed = 1; } return answer; @@ -473,7 +483,7 @@ CMD_ANSWER startWstunnel(boost::archive::text_iarchive &ia) Logger::instance().out("wstunnel executable signature incorrect: %s", sigCheck.lastError().c_str()); answer.executed = 0; } else { - answer.cmdId = ExecuteCmd::instance().execute(fullCmd, std::string()); + answer.cmdId = ExecuteCmd::instance().execute(fullCmd, std::string(), true); answer.executed = 1; } return answer; diff --git a/backend/linux/helper/process_command.h b/backend/linux/helper/process_command.h index 20a9391f7..c82ca4c72 100644 --- a/backend/linux/helper/process_command.h +++ b/backend/linux/helper/process_command.h @@ -24,31 +24,33 @@ CMD_ANSWER clearFirewallRules(boost::archive::text_iarchive &ia); CMD_ANSWER checkFirewallState(boost::archive::text_iarchive &ia); CMD_ANSWER setFirewallRules(boost::archive::text_iarchive &ia); CMD_ANSWER getFirewallRules(boost::archive::text_iarchive &ia); +CMD_ANSWER setFirewallOnBoot(boost::archive::text_iarchive &ia); CMD_ANSWER taskKill(boost::archive::text_iarchive &ia); CMD_ANSWER startCtrld(boost::archive::text_iarchive &ia); CMD_ANSWER startStunnel(boost::archive::text_iarchive &ia); CMD_ANSWER startWstunnel(boost::archive::text_iarchive &ia); static const std::map> kCommands = { - { HELPER_CMD_START_OPENVPN, startOpenvpn }, - { HELPER_CMD_GET_CMD_STATUS, getCmdStatus }, - { HELPER_CMD_CLEAR_CMDS, clearCmds }, - { HELPER_CMD_SPLIT_TUNNELING_SETTINGS, splitTunnelingSettings }, - { HELPER_CMD_SEND_CONNECT_STATUS, sendConnectStatus }, - { HELPER_CMD_START_WIREGUARD, startWireGuard }, - { HELPER_CMD_STOP_WIREGUARD, stopWireGuard }, - { HELPER_CMD_CONFIGURE_WIREGUARD, configureWireGuard }, - { HELPER_CMD_GET_WIREGUARD_STATUS, getWireGuardStatus }, - { HELPER_CMD_CHANGE_MTU, changeMtu }, - { HELPER_CMD_SET_DNS_LEAK_PROTECT_ENABLED, setDnsLeakProtectEnabled }, - { HELPER_CMD_CLEAR_FIREWALL_RULES, clearFirewallRules }, - { HELPER_CMD_CHECK_FIREWALL_STATE, checkFirewallState }, - { HELPER_CMD_SET_FIREWALL_RULES, setFirewallRules }, - { HELPER_CMD_GET_FIREWALL_RULES, getFirewallRules }, - { HELPER_CMD_TASK_KILL, taskKill }, - { HELPER_CMD_START_CTRLD, startCtrld }, - { HELPER_CMD_START_STUNNEL, startStunnel }, - { HELPER_CMD_START_WSTUNNEL, startWstunnel }, + { HELPER_CMD_START_OPENVPN, startOpenvpn }, + { HELPER_CMD_GET_CMD_STATUS, getCmdStatus }, + { HELPER_CMD_CLEAR_CMDS, clearCmds }, + { HELPER_CMD_SPLIT_TUNNELING_SETTINGS, splitTunnelingSettings }, + { HELPER_CMD_SEND_CONNECT_STATUS, sendConnectStatus }, + { HELPER_CMD_START_WIREGUARD, startWireGuard }, + { HELPER_CMD_STOP_WIREGUARD, stopWireGuard }, + { HELPER_CMD_CONFIGURE_WIREGUARD, configureWireGuard }, + { HELPER_CMD_GET_WIREGUARD_STATUS, getWireGuardStatus }, + { HELPER_CMD_CHANGE_MTU, changeMtu }, + { HELPER_CMD_SET_DNS_LEAK_PROTECT_ENABLED, setDnsLeakProtectEnabled }, + { HELPER_CMD_CLEAR_FIREWALL_RULES, clearFirewallRules }, + { HELPER_CMD_CHECK_FIREWALL_STATE, checkFirewallState }, + { HELPER_CMD_SET_FIREWALL_RULES, setFirewallRules }, + { HELPER_CMD_GET_FIREWALL_RULES, getFirewallRules }, + { HELPER_CMD_SET_FIREWALL_ON_BOOT, setFirewallOnBoot }, + { HELPER_CMD_TASK_KILL, taskKill }, + { HELPER_CMD_START_CTRLD, startCtrld }, + { HELPER_CMD_START_STUNNEL, startStunnel }, + { HELPER_CMD_START_WSTUNNEL, startWstunnel }, }; CMD_ANSWER processCommand(int cmdId, const std::string packet); diff --git a/backend/linux/helper/server.cpp b/backend/linux/helper/server.cpp index 3af2f3977..162a2ee81 100644 --- a/backend/linux/helper/server.cpp +++ b/backend/linux/helper/server.cpp @@ -15,6 +15,7 @@ #include #include "execute_cmd.h" +#include "firewallcontroller.h" #include "ipc/helper_security.h" #include "logger.h" #include "ovpn.h" @@ -22,7 +23,7 @@ #include "utils.h" #include "utils/executable_signature/executable_signature.h" -#define SOCK_PATH "/var/run/windscribe_helper_socket2" +#define SOCK_PATH "/var/run/windscribe/helper.sock" Server::Server() { @@ -155,10 +156,11 @@ bool Server::sendAnswerCmd(socket_ptr sock, const CMD_ANSWER &cmdAnswer) void Server::run() { - auto res = system("mkdir -p /var/run"); // res is necessary to avoid no-discard warning. - UNUSED(res); Utils::createWindscribeUserAndGroup(); + auto res = system("mkdir -p /var/run/windscribe && chown :windscribe /var/run/windscribe && chmod 775 /var/run/windscribe"); // res is necessary to avoid no-discard warning. + UNUSED(res); + ::unlink(SOCK_PATH); boost::asio::local::stream_protocol::endpoint ep(SOCK_PATH); @@ -170,6 +172,10 @@ void Server::run() ::unlink(SOCK_PATH); return; } + + // Cause the FirewallController to be constructed here, so that on-boot rules are processed, even if the Windscribe app/service does not start. + FirewallController::instance(); + startAccept(); service_.run(); diff --git a/backend/linux/helper/split_tunneling/hostnames_manager/hostnames_manager.cpp b/backend/linux/helper/split_tunneling/hostnames_manager/hostnames_manager.cpp index cb5ec0358..c494d2675 100644 --- a/backend/linux/helper/split_tunneling/hostnames_manager/hostnames_manager.cpp +++ b/backend/linux/helper/split_tunneling/hostnames_manager/hostnames_manager.cpp @@ -65,7 +65,13 @@ void HostnamesManager::dnsResolverCallback(std::mapsecond.error) { for (const auto addr : it->second.addresses) { - Logger::instance().out("HostnamesManager::dnsResolverCallback(), Resolved : %s, IP: %s", it->first.c_str(), addr.c_str()); + if (addr == "0.0.0.0") { + // ROBERT sometimes will give us an address of 0.0.0.0 for a 'blocked' resource. This is not a valid address. + Logger::instance().out("IpHostnamesManager::dnsResolverCallback(), Resolved : %s, IP: 0.0.0.0 (Ignored)", it->first.c_str()); + } else { + Logger::instance().out("IpHostnamesManager::dnsResolverCallback(), Resolved : %s, IP: %s", it->first.c_str(), addr.c_str()); + hostsIps.insert(hostsIps.end(), addr); + } } hostsIps.insert(hostsIps.end(), it->second.addresses.begin(), it->second.addresses.end()); } else { diff --git a/backend/mac/helper/CMakeLists.txt b/backend/mac/helper/CMakeLists.txt index 44b1c72ff..d6ae34541 100644 --- a/backend/mac/helper/CMakeLists.txt +++ b/backend/mac/helper/CMakeLists.txt @@ -6,7 +6,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) project(com.windscribe.helper.macos) find_package(Boost REQUIRED COMPONENTS serialization thread) -find_package(7zip CONFIG REQUIRED) +find_package(c-ares REQUIRED) find_package(skyr-url CONFIG REQUIRED) # build_all.py sets this option when invoked with the '--sign' flag. Disabled by default @@ -30,11 +30,13 @@ set(SOURCES firewallonboot.cpp firewallonboot.h helper.cpp - ../../../client/common/archive/archive.cpp - ../../../client/common/archive/archive.h installer/files.cpp installer/files.h installer/iinstall_block.h + ip_hostnames/ares_library_init.cpp + ip_hostnames/ares_library_init.h + ip_hostnames/dns_resolver.cpp + ip_hostnames/dns_resolver.h ip_hostnames/ip_hostnames_manager.cpp ip_hostnames/ip_hostnames_manager.h ip_hostnames/ip_routes.cpp @@ -85,7 +87,7 @@ target_link_libraries(com.windscribe.helper.macos PRIVATE Boost::serialization Boost::thread - 7zip::7zip + c-ares::cares skyr::skyr-url "-framework Foundation" "-framework Security" diff --git a/backend/mac/helper/execute_cmd.cpp b/backend/mac/helper/execute_cmd.cpp index c12259893..20451767c 100644 --- a/backend/mac/helper/execute_cmd.cpp +++ b/backend/mac/helper/execute_cmd.cpp @@ -2,7 +2,7 @@ #include #include -unsigned long ExecuteCmd::execute(const std::string &cmd, const std::string &cwd) +unsigned long ExecuteCmd::execute(const std::string &cmd, const std::string &cwd, bool deleteOnFinish) { mutex_.lock(); @@ -15,9 +15,9 @@ unsigned long ExecuteCmd::execute(const std::string &cmd, const std::string &cwd mutex_.unlock(); if (!cwd.empty()) { - boost::thread(runCmd, curCmdId_, "cd \"" + cwd + "\" && " + cmd); + boost::thread(runCmd, curCmdId_, "cd \"" + cwd + "\" && " + cmd, deleteOnFinish); } else { - boost::thread(runCmd, curCmdId_, cmd); + boost::thread(runCmd, curCmdId_, cmd, deleteOnFinish); } return curCmdId_; @@ -56,7 +56,7 @@ ExecuteCmd::ExecuteCmd() : curCmdId_(0) { } -void ExecuteCmd::runCmd(unsigned long cmdId, std::string cmd) +void ExecuteCmd::runCmd(unsigned long cmdId, std::string cmd, bool deleteOnFinish) { std::string strReply; @@ -69,20 +69,25 @@ void ExecuteCmd::runCmd(unsigned long cmdId, std::string cmd) } } pclose(file); - instance().cmdFinished(cmdId, true, strReply); + instance().cmdFinished(cmdId, true, strReply, deleteOnFinish); } else { - instance().cmdFinished(cmdId, false, std::string()); + instance().cmdFinished(cmdId, false, std::string(), deleteOnFinish); } } -void ExecuteCmd::cmdFinished(unsigned long cmdId, bool bSuccess, std::string log) +void ExecuteCmd::cmdFinished(unsigned long cmdId, bool bSuccess, std::string log, bool del) { mutex_.lock(); for (auto it = executingCmds_.begin(); it != executingCmds_.end(); ++it) { if ((*it)->cmdId == cmdId) { - (*it)->bFinished = true; - (*it)->bSuccess = bSuccess; - (*it)->log = log; + if (del) { + delete(*it); + executingCmds_.erase(it); + } else { + (*it)->bFinished = true; + (*it)->bSuccess = bSuccess; + (*it)->log = log; + } break; } } diff --git a/backend/mac/helper/execute_cmd.h b/backend/mac/helper/execute_cmd.h index 018d58c10..73665ce7a 100644 --- a/backend/mac/helper/execute_cmd.h +++ b/backend/mac/helper/execute_cmd.h @@ -14,7 +14,7 @@ class ExecuteCmd return i; } - unsigned long execute(const std::string &cmd, const std::string &cwd = ""); + unsigned long execute(const std::string &cmd, const std::string &cwd = "", bool deleteOnFinish = false); void getStatus(unsigned long cmdId, bool &bFinished, std::string &log); void clearCmds(); @@ -23,8 +23,8 @@ class ExecuteCmd unsigned long curCmdId_; - static void runCmd(unsigned long cmdId, std::string cmd); - void cmdFinished(unsigned long cmdId, bool bSuccess, std::string log); + static void runCmd(unsigned long cmdId, std::string cmd, bool deleteOnFinish); + void cmdFinished(unsigned long cmdId, bool bSuccess, std::string log, bool del); bool isCmdExist(unsigned long cmdId); struct CmdDescr diff --git a/backend/mac/helper/firewallcontroller.cpp b/backend/mac/helper/firewallcontroller.cpp index c6460acec..9ae1b98ff 100644 --- a/backend/mac/helper/firewallcontroller.cpp +++ b/backend/mac/helper/firewallcontroller.cpp @@ -6,6 +6,19 @@ #include "logger.h" #include "utils.h" +FirewallController::FirewallController() : enabled_(false), connected_(false), splitTunnelEnabled_(false), splitTunnelExclude_(false) +{ + // If firewall on boot is enabled, restore boot rules + if (Utils::isFileExists("/etc/windscribe/boot_pf.conf")) { + LOG("Applying boot pfctl rules"); + Utils::executeCommand("pfctl", {"-e", "-f", "/etc/windscribe/boot_pf.conf"}); + } +} + +FirewallController::~FirewallController() +{ +} + bool FirewallController::enable(const std::string &rules, const std::string &table, const std::string &group) { int fd = open("/etc/windscribe/pf.conf", O_CREAT | O_WRONLY | O_TRUNC); @@ -104,4 +117,4 @@ void FirewallController::setSplitTunnelExceptions(const std::vector Utils::executeCommand("pfctl", {"-T", "load", "-f", "/etc/windscribe/pf_st.conf"}); Utils::executeCommand("rm", {"/etc/windscribe/pf_st.conf"}); -} \ No newline at end of file +} diff --git a/backend/mac/helper/firewallcontroller.h b/backend/mac/helper/firewallcontroller.h index aa61a5c77..f20f8066e 100644 --- a/backend/mac/helper/firewallcontroller.h +++ b/backend/mac/helper/firewallcontroller.h @@ -20,8 +20,8 @@ class FirewallController void getRules(const std::string &table, const std::string &group, std::string *outRules); private: - FirewallController() : enabled_(false), connected_(false), splitTunnelEnabled_(false), splitTunnelExclude_(true) {}; - ~FirewallController() { disable(); }; + FirewallController(); + ~FirewallController(); bool enabled_; bool connected_; diff --git a/backend/mac/helper/firewallonboot.cpp b/backend/mac/helper/firewallonboot.cpp index 67c68a37b..b8dfdc574 100644 --- a/backend/mac/helper/firewallonboot.cpp +++ b/backend/mac/helper/firewallonboot.cpp @@ -14,15 +14,15 @@ FirewallOnBootManager::~FirewallOnBootManager() { } -bool FirewallOnBootManager::setEnabled(bool bEnabled) +bool FirewallOnBootManager::setEnabled(bool enabled, bool allowLanTraffic) { - if (bEnabled) { - return enable(); + if (enabled) { + return enable(allowLanTraffic); } return disable(); } -bool FirewallOnBootManager::enable() { +bool FirewallOnBootManager::enable(bool allowLanTraffic) { std::stringstream rules; rules << "set block-policy return\n"; @@ -36,7 +36,42 @@ bool FirewallOnBootManager::enable() { rules << "anchor windscribe_vpn_traffic all\n"; rules << "pass out quick inet proto udp from any to any port = 67\n"; rules << "pass in quick inet proto udp from any to any port = 68\n"; - rules << "anchor windscribe_lan_traffic all\n"; + rules << "anchor windscribe_lan_traffic all {\n"; + if (allowLanTraffic) { + // Always allow localhost + rules << "pass out quick inet from any to 127.0.0.0/8\n"; + rules << "pass in quick inet from 127.0.0.0/8 to any\n"; + + // Local Network + rules << "pass out quick inet from any to 192.168.0.0/16\n"; + rules << "pass in quick inet from 192.168.0.0/16 to any\n"; + rules << "pass out quick inet from any to 172.16.0.0/12\n"; + rules << "pass in quick inet from 172.16.0.0/12 to any\n"; + rules << "pass out quick inet from any to 169.254.0.0/16\n"; + rules << "pass in quick inet from 169.254.0.0/16 to any\n"; + rules << "block out quick inet from any to 10.255.255.0/24\n"; + rules << "block in quick inet from 10.255.255.0/24 to any\n"; + rules << "pass out quick inet from any to 10.0.0.0/8\n"; + rules << "pass in quick inet from 10.0.0.0/8 to any\n"; + + // Multicast addresses + rules << "pass out quick inet from any to 224.0.0.0/4\n"; + rules << "pass in quick inet from 224.0.0.0/4 to any\n"; + + // UPnP + rules << "pass out quick inet proto udp from any to any port = 1900\n"; + rules << "pass in quick proto udp from any to any port = 1900\n"; + rules << "pass out quick inet proto udp from any to any port = 1901\n"; + rules << "pass in quick proto udp from any to any port = 1901\n"; + + rules << "pass out quick inet proto udp from any to any port = 5350\n"; + rules << "pass in quick proto udp from any to any port = 5350\n"; + rules << "pass out quick inet proto udp from any to any port = 5351\n"; + rules << "pass in quick proto udp from any to any port = 5351\n"; + rules << "pass out quick inet proto udp from any to any port = 5353\n"; + rules << "pass in quick proto udp from any to any port = 5353\n"; + } + rules << "}\n"; rules << "anchor windscribe_static_ports_traffic all\n"; // write rules @@ -49,74 +84,14 @@ bool FirewallOnBootManager::enable() { write(fd, rules.str().c_str(), rules.str().length()); close(fd); - // write script - std::stringstream script; - - script << "#!/bin/bash\n"; - script << "FILE=\"/Applications/Windscribe.app/Contents/MacOS/Windscribe\"\n"; - script << "if [ -d \"/Applications/Windscribe.app\" ]; then\n"; - script << " while grep -q \"No such key\" <<< \"`echo 'show State:/Network/Global/IPv4' | scutil`\"; do sleep 1; done;\n"; - script << " /sbin/pfctl -e -f \"/etc/windscribe/boot_pf.conf\"\n"; - script << "else\n"; - script << " launchctl stop com.aaa.windscribe.firewall_on\n"; - script << " launchctl unload /Library/LaunchDaemons/com.aaa.windscribe.firewall_on.plist\n"; - script << " launchctl remove com.aaa.windscribe.firewall_on\n"; - script << "fi\n"; - - fd = open("/etc/windscribe/boot_pf.sh", O_CREAT | O_WRONLY | O_TRUNC); - if (fd < 0) { - LOG("Could not open boot script for writing"); - return false; - } - - write(fd, script.str().c_str(), script.str().length()); - close(fd); - Utils::executeCommand("chmod", {"+x", "/etc/windscribe/boot_pf.sh"}); - - // write plist - std::stringstream plist; - - plist << "\n"; - plist << "\n"; - plist << "\n"; - plist << "\n"; - plist << "Label\n"; - plist << "com.aaa.windscribe.firewall_on\n"; - - plist << "ProgramArguments\n"; - plist << "\n"; - plist << "/bin/bash\n"; - plist << "/etc/windscribe/boot_pf.sh\n"; - plist << "\n"; - - plist << "StandardErrorPath\n"; - plist << "/var/log/windscribe_pf.log\n"; - plist << "StandardOutPath\n"; - plist << "/var/log/windscribe_pf.log\n"; - - plist << "RunAtLoad\n"; - plist << "\n"; - - plist << "\n"; - plist << "\n"; - - fd = open("/Library/LaunchDaemons/com.aaa.windscribe.firewall_on.plist", O_CREAT | O_WRONLY | O_TRUNC); - if (fd < 0) { - LOG("Could not open boot plist for writing"); - return false; - } - write(fd, plist.str().c_str(), plist.str().length()); - close(fd); - Utils::executeCommand("launchctl", {"load", "-w", "/Library/LaunchDaemons/com.aaa.windscribe.firewall_on.plist"}); + Utils::executeCommand("launchctl", {"disable", "system/com.apple.pfctl"}); return true; } bool FirewallOnBootManager::disable() { - Utils::executeCommand("launchctl", {"unload", "/Library/LaunchDaemons/com.aaa.windscribe.firewall_on.plist"}); - Utils::executeCommand("rm", {"-f", "/Library/LaunchDaemons/com.aaa.windscribe.firewall_on.plist"}); + Utils::executeCommand("launchctl", {"enable", "system/com.apple.pfctl"}); Utils::executeCommand("rm", {"-f", "/etc/windscribe/boot_pf.conf"}); - Utils::executeCommand("rm", {"-f", "/etc/windscribe/boot_pf.sh"}); return true; } diff --git a/backend/mac/helper/firewallonboot.h b/backend/mac/helper/firewallonboot.h index 2dd2dce65..33b9aa91c 100644 --- a/backend/mac/helper/firewallonboot.h +++ b/backend/mac/helper/firewallonboot.h @@ -11,14 +11,14 @@ class FirewallOnBootManager return fobm; } - bool setEnabled(bool bEnabled); + bool setEnabled(bool enabled, bool allowLanTraffic = false); void setIpTable(const std::string& ipTable) { ipTable_ = ipTable; } private: FirewallOnBootManager(); ~FirewallOnBootManager(); - bool enable(); + bool enable(bool allowLanTraffic); bool disable(); std::string ipTable_; diff --git a/backend/mac/helper/helper-info.plist b/backend/mac/helper/helper-info.plist index 343d9a5e1..7c5e545ff 100644 --- a/backend/mac/helper/helper-info.plist +++ b/backend/mac/helper/helper-info.plist @@ -9,7 +9,7 @@ CFBundleName WindscribeHelper CFBundleVersion - 63 + 69 NSHumanReadableCopyright Copyright © 2024 Windscribe Limited. All rights reserved. LSMinimumSystemVersion diff --git a/backend/mac/helper/installer/files.cpp b/backend/mac/helper/installer/files.cpp index 3113ea862..1ec0d3e12 100644 --- a/backend/mac/helper/installer/files.cpp +++ b/backend/mac/helper/installer/files.cpp @@ -1,130 +1,51 @@ #include "files.h" + +#include +#include +#include + #include "../logger.h" +#include "../utils.h" -Files::Files(const std::wstring &archivePath, const std::wstring &installPath) : archive_(NULL), - archivePath_(archivePath), installPath_(installPath), state_(0) +Files::Files(const std::wstring &archivePath, const std::wstring &installPath) : archivePath_(archivePath), installPath_(installPath) { } Files::~Files() { - if (archive_) - { - delete archive_; - } } int Files::executeStep() { - if (state_ == 0) - { - if (archive_) - { - delete archive_; - } - - archive_ = new Archive(archivePath_); - archive_->setLogFunction([](const char* str) { - LOG((std::string("(archive) ") + str).c_str()); - }); - if (!archive_->isCorrect()) - { - LOG("Can't open file archive"); - lastError_ = "Can't open file archive."; - return -1; - } - - SRes res = archive_->fileList(fileList_); - if (res != SZ_OK) - { - LOG("Can't get files list from archive"); - lastError_ = "Can't get files list from archive."; - return -1; - } - - fillPathList(); - - archive_->calcTotal(fileList_, pathList_); - curFileInd_ = 0; - state_++; - return 0; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + std::wstring_convert> converter; + std::string archivePath = converter.to_bytes(archivePath_); + std::string installPath = converter.to_bytes(installPath_); +#pragma clang diagnostic pop + + // The installer should have removed any existing Windscribe app instance by this point, + // but we'll doublecheck just to be sure. + if (std::filesystem::exists(installPath)) { + LOG("Files: install path already exists"); + std::filesystem::remove_all(installPath); } - else - { - SRes res = archive_->extractionFile(curFileInd_); - if (res != SZ_OK) - { - archive_->finish(); - LOG("Can't extract files"); - lastError_ = "Can't extract file."; - return -1; - } - else - { - int progress = ((double)curFileInd_ / (double)archive_->getNumFiles()) * 100.0; - if (curFileInd_ >= (archive_->getNumFiles() - 1)) - { - archive_->finish(); - return 100; - } - else - { - curFileInd_++; - } - return progress; - } + auto status = Utils::executeCommand("mkdir", {installPath.c_str()}, &lastError_); + if (status != 0) { + LOG("Files: failed to create the app bundle folder"); + LOG(lastError_.c_str()); + return -1; } - return 100; -} - -void Files::eraseSubStr(std::wstring &mainStr, const std::wstring &toErase) -{ - size_t pos = mainStr.find(toErase); - if (pos != std::string::npos) - { - mainStr.erase(pos, toErase.length()); + status = Utils::executeCommand("tar", {"-xovf", archivePath.c_str(), "-C", installPath.c_str()}, &lastError_); + if (status != 0) { + LOG("Files: failed to untar the app archive"); + LOG(lastError_.c_str()); + return -1; } -} - -void Files::fillPathList() -{ - pathList_.clear(); - for (auto it = fileList_.cbegin(); it != fileList_.cend(); it++) - { - std::wstring srcPath = *it; - eraseSubStr(srcPath, L"Windscribe.app/"); - std::wstring destination = installPath_ + L"/" + srcPath; - std::wstring directory; - const size_t last_slash_idx = destination.rfind(L'/'); - if (std::wstring::npos != last_slash_idx) - { - directory = destination.substr(0, last_slash_idx); - } - - pathList_.push_back(directory); - } -} - - -std::wstring Files::getFileName(const std::wstring &s) -{ - std::wstring str; - - size_t i = s.rfind('\\', s.length()); - - if (i == std::wstring::npos) - { - i = s.rfind('/', s.length()); - } - - - if (i != std::wstring::npos) - { - str = s.substr(i + 1, s.length() - i); - } + Utils::executeCommand("xattr", {"-r", "-d", "com.apple.quarantine", installPath.c_str()}); - return str; + return 1; } diff --git a/backend/mac/helper/installer/files.h b/backend/mac/helper/installer/files.h index dc55a03b3..4f1c06057 100644 --- a/backend/mac/helper/installer/files.h +++ b/backend/mac/helper/installer/files.h @@ -1,10 +1,7 @@ #pragma once -#include #include -#include "../../../../client/common/archive/archive.h" - class Files { public: @@ -17,14 +14,5 @@ class Files private: std::wstring archivePath_; std::wstring installPath_; - Archive *archive_; - int state_; - unsigned int curFileInd_; - std::list fileList_; - std::list pathList_; std::string lastError_; - - void eraseSubStr(std::wstring &mainStr, const std::wstring &toErase); - void fillPathList(); - std::wstring getFileName(const std::wstring &s); }; diff --git a/backend/mac/helper/ip_hostnames/ares_library_init.cpp b/backend/mac/helper/ip_hostnames/ares_library_init.cpp new file mode 100644 index 000000000..9c479d646 --- /dev/null +++ b/backend/mac/helper/ip_hostnames/ares_library_init.cpp @@ -0,0 +1,30 @@ +#include "ares_library_init.h" + +#include +#include "ares.h" +#include "../logger.h" + + +AresLibraryInit::AresLibraryInit() : init_(false), failedInit_(false) +{ +} + +AresLibraryInit::~AresLibraryInit() +{ + if (init_) { + ares_library_cleanup(); + } +} + +void AresLibraryInit::init() +{ + if (!init_ && !failedInit_) { + int status = ares_library_init(ARES_LIB_INIT_ALL); + if (status != ARES_SUCCESS) { + Logger::instance().out("ares_library_init failed: %s", ares_strerror(status)); + failedInit_ = true; + } else { + init_ = true; + } + } +} diff --git a/backend/mac/helper/ip_hostnames/ares_library_init.h b/backend/mac/helper/ip_hostnames/ares_library_init.h new file mode 100644 index 000000000..673baef70 --- /dev/null +++ b/backend/mac/helper/ip_hostnames/ares_library_init.h @@ -0,0 +1,14 @@ +#pragma once + +class AresLibraryInit +{ +public: + AresLibraryInit(); + ~AresLibraryInit(); + + void init(); + +private: + bool init_; + bool failedInit_; +}; diff --git a/backend/mac/helper/ip_hostnames/dns_resolver.cpp b/backend/mac/helper/ip_hostnames/dns_resolver.cpp new file mode 100644 index 000000000..a3966d5e1 --- /dev/null +++ b/backend/mac/helper/ip_hostnames/dns_resolver.cpp @@ -0,0 +1,194 @@ +#include "dns_resolver.h" + +#include +#include +#include +#include + +#include "../logger.h" + +DnsResolver *DnsResolver::this_ = NULL; + +DnsResolver::DnsResolver() + : bStopCalled_(false) + , bNeedFinish_(false) + , channel_(NULL) +{ + assert(this_ == NULL); + this_ = this; + aresLibraryInit_.init(); + + thread_ = std::thread(threadFunc, this); +} + +DnsResolver::~DnsResolver() +{ + assert(bStopCalled_); + this_ = NULL; +} + +void DnsResolver::setResolveDomainsCallbackHandler(std::function)> resolveDomainsCallback) +{ + std::lock_guard lock(mutex_); + resolveDomainsCallback_ = resolveDomainsCallback; +} + +void DnsResolver::stop() +{ + cancelAll(); + + // kick thread + this_->mutex_.lock(); + bNeedFinish_ = true; + waitCondition_.notify_all(); + this_->mutex_.unlock(); + + // wait for thread to finish + thread_.join(); + bStopCalled_ = true; +} + +void DnsResolver::resolveDomains(const std::vector &hostnames) +{ + std::lock_guard lock(mutex_); + + { + // cancel any lookups currently in channel + if (!hostnamesInProgress_.empty()) + { + ares_cancel(channel_); + ares_destroy(channel_); + this_->channel_ = NULL; + } + else + { + assert(channel_ == NULL); + } + hostnamesInProgress_.clear(); + hostinfoResults_.clear(); + + struct ares_options options; + int optmask = 0; + memset(&options, 0, sizeof(options)); + optmask |= ARES_OPT_TRIES; + options.tries = 3; + + int status = ares_init_options(&channel_, &options, optmask); + if (status != ARES_SUCCESS) + { + Logger::instance().out("ares_init_options failed: %s", ares_strerror(status)); + return; + } + + for (auto &hostname : hostnames) + { + hostnamesInProgress_.insert(hostname); + } + + // filter for unique hostnames and execute request + for (auto &hostname : hostnames) + { + USER_ARG *userArg = new USER_ARG(); + userArg->hostname = hostname; + ares_gethostbyname(channel_, hostname.c_str(), AF_INET, aresLookupFinishedCallback, userArg); + } + } + + // kick thread + waitCondition_.notify_all(); +} + +void DnsResolver::cancelAll() +{ + std::lock_guard lock(mutex_); + if (channel_) + { + ares_cancel(channel_); + hostnamesInProgress_.clear(); + ares_destroy(channel_); + } + channel_ = NULL; +} + + +void DnsResolver::aresLookupFinishedCallback(void * arg, int status, int /*timeouts*/, struct hostent * host) +{ + USER_ARG *userArg = static_cast(arg); + + // cancel and fail cases + if (status == ARES_ECANCELLED) + { + delete userArg; + return; + } + + HostInfo hostInfo; + hostInfo.hostname = userArg->hostname; + + if (status != ARES_SUCCESS) + { + hostInfo.error = true; + } + else + { + // add ips + for (char **p = host->h_addr_list; *p; p++) + { + char addr_buf[46] = "??"; + ares_inet_ntop(host->h_addrtype, *p, addr_buf, sizeof(addr_buf)); + hostInfo.addresses.push_back(std::string(addr_buf)); + } + } + + this_->hostinfoResults_[hostInfo.hostname] = hostInfo; + this_->hostnamesInProgress_.erase(userArg->hostname); + + if (this_->hostnamesInProgress_.empty()) + { + this_->resolveDomainsCallback_(this_->hostinfoResults_); + } + + delete userArg; +} + +void DnsResolver::threadFunc(void *arg) +{ + //BIND_CRASH_HANDLER_FOR_THREAD(); + DnsResolver *resolver = static_cast(arg); + + while (true) + { + { + std::unique_lock lockWait(resolver->mutex_); + if (resolver->bNeedFinish_) + { + break; + } + + if (resolver->channel_ != NULL && !resolver->processChannel(resolver->channel_)) + { + resolver->waitCondition_.wait(lockWait); + } + } + sleep(1); + } +} + +bool DnsResolver::processChannel(ares_channel channel) +{ + fd_set read_fds, write_fds; + int res; + int nfds; + + FD_ZERO(&read_fds); + FD_ZERO(&write_fds); + nfds = ares_fds(channel, &read_fds, &write_fds); + if (nfds == 0) { + return false; + } + timeval tv; + timeval *tvp = ares_timeout(channel, NULL, &tv); + res = select(nfds, &read_fds, &write_fds, NULL, tvp); + ares_process(channel, &read_fds, &write_fds); + return true; +} diff --git a/backend/mac/helper/ip_hostnames/dns_resolver.h b/backend/mac/helper/ip_hostnames/dns_resolver.h new file mode 100644 index 000000000..14f243df1 --- /dev/null +++ b/backend/mac/helper/ip_hostnames/dns_resolver.h @@ -0,0 +1,65 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ares_library_init.h" +#include "ares.h" + +class DnsResolver +{ +public: + struct HostInfo + { + std::string hostname; + std::vector addresses; + bool error; + + HostInfo() : error(false) {} + }; + + explicit DnsResolver(); + ~DnsResolver(); + DnsResolver(const DnsResolver &) = delete; + DnsResolver &operator=(const DnsResolver &) = delete; + + void stop(); + + void resolveDomains(const std::vector &hostnames); + void cancelAll(); + void setResolveDomainsCallbackHandler(std::function)> resolveDomainsCallback); + +private: + std::function)> resolveDomainsCallback_; + + struct USER_ARG + { + std::string hostname; + }; + + bool bStopCalled_; + std::mutex mutex_; + std::condition_variable waitCondition_; + bool bNeedFinish_; + + AresLibraryInit aresLibraryInit_; + ares_channel channel_; + + static DnsResolver *this_; + + std::map hostinfoResults_; + std::set hostnamesInProgress_; + + // thread specific + std::thread thread_; + static void threadFunc(void *arg); + static void aresLookupFinishedCallback(void *arg, int status, int timeouts, struct hostent *host); + + bool processChannel(ares_channel channel); +}; diff --git a/backend/mac/helper/ip_hostnames/ip_hostnames_manager.cpp b/backend/mac/helper/ip_hostnames/ip_hostnames_manager.cpp index 61c8c14e9..58fea166e 100644 --- a/backend/mac/helper/ip_hostnames/ip_hostnames_manager.cpp +++ b/backend/mac/helper/ip_hostnames/ip_hostnames_manager.cpp @@ -1,44 +1,88 @@ #include "ip_hostnames_manager.h" + #include "../firewallcontroller.h" #include "../logger.h" IpHostnamesManager::IpHostnamesManager(): isEnabled_(false) { + dnsResolver_.setResolveDomainsCallbackHandler(std::bind(&IpHostnamesManager::dnsResolverCallback, this, std::placeholders::_1)); } IpHostnamesManager::~IpHostnamesManager() { - disable(); + dnsResolver_.stop(); } -void IpHostnamesManager::enable(const std::string &ipAddress) +void IpHostnamesManager::enable(const std::string &gatewayIp) { - std::lock_guard guard(mutex_); + Logger::instance().out("IpHostnamesManager::enable(), begin"); + { + std::lock_guard guard(mutex_); - defaultRouteIp_ = ipAddress; - ipRoutes_.clear(); - ipRoutes_.setIps(defaultRouteIp_, ipsLatest_); - FirewallController::instance().setSplitTunnelExceptions(ipsLatest_); - isEnabled_ = true; + gatewayIp_ = gatewayIp; + ipRoutes_.clear(); + ipRoutes_.setIps(gatewayIp_, ipsLatest_); + FirewallController::instance().setSplitTunnelExceptions(ipsLatest_); + isEnabled_ = true; + } + + dnsResolver_.cancelAll(); + dnsResolver_.resolveDomains(hostsLatest_); + Logger::instance().out("IpHostnamesManager::enable(), end"); } void IpHostnamesManager::disable() { - std::lock_guard guard(mutex_); + Logger::instance().out("IpHostnamesManager::disable(), begin"); - if (!isEnabled_) { - return; - } + { + std::lock_guard guard(mutex_); + if (!isEnabled_) { + return; + } + ipRoutes_.clear(); + isEnabled_ = false; + } + dnsResolver_.cancelAll(); FirewallController::instance().setSplitTunnelExceptions(std::vector()); - ipRoutes_.clear(); - isEnabled_ = false; + Logger::instance().out("IpHostnamesManager::disable(), end"); } void IpHostnamesManager::setSettings(const std::vector &ips, const std::vector &hosts) { std::lock_guard guard(mutex_); - ipsLatest_ = ips; - ipsLatest_.insert( ipsLatest_.end(), hosts.begin(), hosts.end() ); + hostsLatest_ = hosts; + Logger::instance().out("IpHostnamesManager::setSettings(), end"); +} + +void IpHostnamesManager::dnsResolverCallback(std::map hostInfos) +{ + std::lock_guard guard(mutex_); + + std::vector hostsIps; + + for (auto it = hostInfos.begin(); it != hostInfos.end(); ++it) { + if (!it->second.error) { + for (const auto addr : it->second.addresses) { + if (addr == "0.0.0.0") { + // ROBERT sometimes will give us an address of 0.0.0.0 for a 'blocked' resource. This is not a valid address. + Logger::instance().out("IpHostnamesManager::dnsResolverCallback(), Resolved : %s, IP: 0.0.0.0 (Ignored)", it->first.c_str()); + } else { + Logger::instance().out("IpHostnamesManager::dnsResolverCallback(), Resolved : %s, IP: %s", it->first.c_str(), addr.c_str()); + hostsIps.insert(hostsIps.end(), addr); + } + } + } else { + Logger::instance().out("IpHostnamesManager::dnsResolverCallback(), Failed resolve : %s", it->first.c_str()); + } + } + + hostsIps.insert(hostsIps.end(), ipsLatest_.begin(), ipsLatest_.end()); + + if (isEnabled_) { + ipRoutes_.setIps(gatewayIp_, hostsIps); + FirewallController::instance().setSplitTunnelExceptions(hostsIps); + } } diff --git a/backend/mac/helper/ip_hostnames/ip_hostnames_manager.h b/backend/mac/helper/ip_hostnames/ip_hostnames_manager.h index 0a58e08d1..e26f69f5d 100644 --- a/backend/mac/helper/ip_hostnames/ip_hostnames_manager.h +++ b/backend/mac/helper/ip_hostnames/ip_hostnames_manager.h @@ -1,27 +1,33 @@ #pragma once +#include "dns_resolver.h" +#include "ip_routes.h" + #include -#include #include -#include "ip_routes.h" class IpHostnamesManager { public: - IpHostnamesManager(); + explicit IpHostnamesManager(); ~IpHostnamesManager(); - void enable(const std::string &ipAddress); + void enable(const std::string &gatewayIp); void disable(); void setSettings(const std::vector &ips, const std::vector &hosts); private: + DnsResolver dnsResolver_; IpRoutes ipRoutes_; + bool isEnabled_; std::recursive_mutex mutex_; - std::string defaultRouteIp_; - - //latest ips and hosts list + // latest ips and hosts list std::vector ipsLatest_; -}; + std::vector hostsLatest_; + + std::string gatewayIp_; + + void dnsResolverCallback(std::map hostInfos); +}; \ No newline at end of file diff --git a/backend/mac/helper/process_command.cpp b/backend/mac/helper/process_command.cpp index d7f767a69..683d09e85 100644 --- a/backend/mac/helper/process_command.cpp +++ b/backend/mac/helper/process_command.cpp @@ -2,10 +2,11 @@ #include #include +#include #include #include #include -#include + #include "execute_cmd.h" #include "files_manager.h" #include "firewallcontroller.h" @@ -239,9 +240,8 @@ CMD_ANSWER installerExecuteCopyFile(boost::archive::text_iarchive &ia) if (answer.executed == -1) { answer.body = files->getLastError(); } - if (answer.executed == -1 || answer.executed == 100) { - FilesManager::instance().setFiles(nullptr); - } + + FilesManager::instance().setFiles(nullptr); } return answer; @@ -414,9 +414,10 @@ CMD_ANSWER setFirewallOnBoot(boost::archive::text_iarchive &ia) CMD_ANSWER answer; CMD_SET_FIREWALL_ON_BOOT cmd; ia >> cmd; - LOG("Set firewall on boot: %s ip table: %s", cmd.enabled ? "true" : "false", cmd.ipTable.c_str()); + LOG("Set firewall on boot: %s", cmd.enabled ? "true" : "false"); + FirewallOnBootManager::instance().setIpTable(cmd.ipTable); - answer.executed = FirewallOnBootManager::instance().setEnabled(cmd.enabled); + answer.executed = FirewallOnBootManager::instance().setEnabled(cmd.enabled, cmd.allowLanTraffic); return answer; } @@ -493,12 +494,6 @@ CMD_ANSWER startCtrld(boost::archive::text_iarchive &ia) CMD_START_CTRLD cmd; ia >> cmd; - if (!Utils::isValidIpAddress(cmd.ip)) { - LOG("Invalid IP address: %s", cmd.ip.c_str()); - answer.executed = 0; - return answer; - } - // Validate URLs if (!Utils::isValidUrl(cmd.upstream1) || (!cmd.upstream2.empty() && !Utils::isValidUrl(cmd.upstream2))) { LOG("Invalid upstream URL(s)"); @@ -515,7 +510,8 @@ CMD_ANSWER startCtrld(boost::archive::text_iarchive &ia) std::stringstream arguments; arguments << "run"; - arguments << " --listen=" + cmd.ip + ":53"; + arguments << " --daemon"; + arguments << " --listen=127.0.0.1:53"; arguments << " --primary_upstream=" + cmd.upstream1; if (!cmd.upstream2.empty()) { arguments << " --secondary_upstream=" + cmd.upstream2; @@ -547,8 +543,7 @@ CMD_ANSWER startCtrld(boost::archive::text_iarchive &ia) LOG("ctrld executable signature incorrect: %s", sigCheck.lastError().c_str()); answer.executed = 0; } else { - answer.cmdId = ExecuteCmd::instance().execute(fullCmd, std::string()); - answer.executed = 1; + answer.executed = Utils::executeCommand(fullCmd) ? 0 : 1; } return answer; @@ -588,7 +583,7 @@ CMD_ANSWER startStunnel(boost::archive::text_iarchive &ia) LOG("stunnel executable signature incorrect: %s", sigCheck.lastError().c_str()); answer.executed = 0; } else { - answer.cmdId = ExecuteCmd::instance().execute(fullCmd, std::string()); + answer.cmdId = ExecuteCmd::instance().execute(fullCmd, std::string(), true); answer.executed = 1; } @@ -625,7 +620,7 @@ CMD_ANSWER startWstunnel(boost::archive::text_iarchive &ia) LOG("wstunnel executable signature incorrect: %s", sigCheck.lastError().c_str()); answer.executed = 0; } else { - answer.cmdId = ExecuteCmd::instance().execute(fullCmd, std::string()); + answer.cmdId = ExecuteCmd::instance().execute(fullCmd, std::string(), true); answer.executed = 1; } @@ -682,3 +677,36 @@ CMD_ANSWER getHelperVersion(boost::archive::text_iarchive &ia) answer.executed = 1; return answer; } + +CMD_ANSWER getInterfaceSsid(boost::archive::text_iarchive &ia) +{ + CMD_ANSWER answer; + CMD_GET_INTERFACE_SSID cmd; + ia >> cmd; + LOG("Get interface SSID for %s", cmd.interface.c_str()); + + std::string output; + + answer.executed = Utils::executeCommand("/usr/bin/wdutil", {"info"}, &output); + + std::istringstream stream(output); + std::string line; + + // Skip all the lines until we see our interface + while (getline(stream, line)) { + if (line.find("Interface Name") != std::string::npos && line.find(cmd.interface) != std::string::npos) { + continue; + } + break; + } + // Now look for the SSID + while (getline(stream, line)) { + if (line.find("SSID") != std::string::npos) { + answer.body = line.substr(line.find(":") + 2); + LOG("Found SSID for %s: %s", cmd.interface.c_str(), answer.body.c_str()); + return answer; + } + } + LOG("No SSID for %s", cmd.interface.c_str()); + return answer; +} diff --git a/backend/mac/helper/process_command.h b/backend/mac/helper/process_command.h index 5f9ce6ea6..22f7fef34 100644 --- a/backend/mac/helper/process_command.h +++ b/backend/mac/helper/process_command.h @@ -40,6 +40,7 @@ CMD_ANSWER startStunnel(boost::archive::text_iarchive &ia); CMD_ANSWER startWstunnel(boost::archive::text_iarchive &ia); CMD_ANSWER installerCreateCliSymlinkDir(boost::archive::text_iarchive &ia); CMD_ANSWER getHelperVersion(boost::archive::text_iarchive &ia); +CMD_ANSWER getInterfaceSsid(boost::archive::text_iarchive &ia); static const std::map> kCommands = { { HELPER_CMD_START_OPENVPN, startOpenvpn }, @@ -72,7 +73,8 @@ static const std::map #include #include +#include "firewallcontroller.h" #include "ipc/helper_security.h" #include "logger.h" #include "process_command.h" @@ -98,10 +99,11 @@ void Server::run() return; } - system("mkdir -p /var/run"); - system("mkdir -p /etc/windscribe"); Utils::createWindscribeUserAndGroup(); + system("mkdir -p /var/run/windscribe && chown :windscribe /var/run/windscribe && chmod 777 /var/run/windscribe"); + system("mkdir -p /etc/windscribe"); + xpc_connection_t listener = xpc_connection_create_mach_service("com.windscribe.helper.macos", NULL, XPC_CONNECTION_MACH_SERVICE_LISTENER); if (!listener) { LOG("xpc_connection_create_mach_service failed"); @@ -113,6 +115,9 @@ void Server::run() xpc_event_handler((xpc_connection_t)event); }); + // Cause the FirewallController to be constructed here, so that on-boot rules are processed, even if the Windscribe app/service does not start. + FirewallController::instance(); + xpc_connection_resume(listener); // this method does not return diff --git a/backend/mac/helper/utils.cpp b/backend/mac/helper/utils.cpp index cbda05c84..1cc5bca8e 100644 --- a/backend/mac/helper/utils.cpp +++ b/backend/mac/helper/utils.cpp @@ -5,8 +5,10 @@ #include #include #include +#include #include "3rdparty/pstream.h" +#include "firewallonboot.h" #include "logger.h" namespace Utils @@ -73,6 +75,12 @@ size_t findCaseInsensitive(std::string data, std::string toSearch, size_t pos) return data.find(toSearch, pos); } +bool isFileExists(const std::string &name) +{ + struct stat buffer; + return (stat (name.c_str(), &buffer) == 0); +} + std::string getFullCommand(const std::string &exePath, const std::string &executable, const std::string &arguments) { char *canonicalPath = realpath(exePath.c_str(), NULL); @@ -164,6 +172,8 @@ bool isAppUninstalled() void deleteSelf() { + FirewallOnBootManager::instance().setEnabled(false); + Utils::executeCommand("launchctl", {"remove", "/Library/LaunchDaemons/com.windscribe.helper.macos.plist"}); Utils::executeCommand("rm", {"/Library/LaunchDaemons/com.windscribe.helper.macos.plist"}); Utils::executeCommand("rm", {"/Library/PrivilegedHelperTools/com.windscribe.helper.macos"}); diff --git a/backend/mac/helper/utils.h b/backend/mac/helper/utils.h index b312edf6b..b235f78c6 100644 --- a/backend/mac/helper/utils.h +++ b/backend/mac/helper/utils.h @@ -14,6 +14,8 @@ namespace Utils // find case insensitive sub string in a given substring size_t findCaseInsensitive(std::string data, std::string toSearch, size_t pos = 0); + bool isFileExists(const std::string &name); + // combine exe path, exe, and arguments std::string getFullCommand(const std::string &exePath, const std::string &executable, const std::string &arguments); std::string getFullCommandAsUser(const std::string &user, const std::string &exePath, const std::string &executable, const std::string &arguments); diff --git a/backend/posix_common/helper_commands.h b/backend/posix_common/helper_commands.h index b2e4ae158..94eab0cd0 100644 --- a/backend/posix_common/helper_commands.h +++ b/backend/posix_common/helper_commands.h @@ -41,6 +41,7 @@ #define HELPER_CMD_START_WSTUNNEL 34 #define HELPER_CMD_INSTALLER_CREATE_CLI_SYMLINK_DIR 35 #define HELPER_CMD_HELPER_VERSION 36 +#define HELPER_CMD_GET_INTERFACE_SSID 37 // enums @@ -160,7 +161,6 @@ struct CMD_CONFIGURE_WIREGUARD { }; struct CMD_START_CTRLD { - std::string ip; std::string upstream1; std::string upstream2; std::vector domains; @@ -232,6 +232,7 @@ struct CMD_INSTALLER_REMOVE_OLD_INSTALL { struct CMD_SET_FIREWALL_ON_BOOT { bool enabled; + bool allowLanTraffic; std::string ipTable; }; @@ -267,3 +268,6 @@ struct CMD_INSTALLER_CREATE_CLI_SYMLINK_DIR { uid_t uid; }; +struct CMD_GET_INTERFACE_SSID { + std::string interface; +}; diff --git a/backend/posix_common/helper_commands_serialize.h b/backend/posix_common/helper_commands_serialize.h index 20f4b8245..7e88cab51 100644 --- a/backend/posix_common/helper_commands_serialize.h +++ b/backend/posix_common/helper_commands_serialize.h @@ -104,7 +104,6 @@ template void serialize(Archive &ar, CMD_START_CTRLD &a, const unsigned int version) { UNUSED(version); - ar & a.ip; ar & a.upstream1; ar & a.upstream2; ar & a.domains; @@ -218,6 +217,7 @@ void serialize(Archive &ar, CMD_SET_FIREWALL_ON_BOOT &a, const unsigned int vers { UNUSED(version); ar & a.enabled; + ar & a.allowLanTraffic; ar & a.ipTable; } @@ -271,5 +271,12 @@ void serialize(Archive &ar, CMD_INSTALLER_CREATE_CLI_SYMLINK_DIR &a, const unsig ar & a.uid; } +template +void serialize(Archive &ar, CMD_GET_INTERFACE_SSID &a, const unsigned int version) +{ + UNUSED(version); + ar & a.interface; +} + } } diff --git a/backend/windows/windscribe_install_helper/install_service_command.cpp b/backend/windows/windscribe_install_helper/install_service_command.cpp index 937a76073..3140d50d3 100644 --- a/backend/windows/windscribe_install_helper/install_service_command.cpp +++ b/backend/windows/windscribe_install_helper/install_service_command.cpp @@ -9,7 +9,7 @@ using namespace std; InstallServiceCommand::InstallServiceCommand(Logger *logger, const wstring &installDir) : BasicCommand(logger), - installDir_(installDir) + installDir_(installDir) { } diff --git a/backend/windows/windscribe_service/CMakeLists.txt b/backend/windows/windscribe_service/CMakeLists.txt index 7a34263d7..f615d71fd 100644 --- a/backend/windows/windscribe_service/CMakeLists.txt +++ b/backend/windows/windscribe_service/CMakeLists.txt @@ -18,7 +18,7 @@ if(DEFINE_USE_SIGNATURE_CHECK_MACRO) add_definitions(-DUSE_SIGNATURE_CHECK) endif(DEFINE_USE_SIGNATURE_CHECK_MACRO) -add_definitions(-DUNICODE -D_UNICODE -DWINDSCRIBE_SERVICE) +add_definitions(-DUNICODE -D_UNICODE -DWINDSCRIBE_SERVICE -D_WIN32_WINNT=0x0601) project(Service) @@ -95,7 +95,7 @@ target_link_libraries(WindscribeService target_include_directories(WindscribeService PRIVATE ${WINDSCRIBE_BUILD_LIBS_PATH}/wintun/include - ../../../client/common/ + ../../../client/common ${WINREG_INCLUDE_DIRS} ) diff --git a/backend/windows/windscribe_service/all_headers.h b/backend/windows/windscribe_service/all_headers.h index ed873c427..dec0501b5 100644 --- a/backend/windows/windscribe_service/all_headers.h +++ b/backend/windows/windscribe_service/all_headers.h @@ -4,7 +4,6 @@ // If you wish to build your application for a previous Windows platform, include WinSDKVer.h and // set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. #define WINVER 0x0601 -#define _WIN32_WINNT 0x0601 #include diff --git a/backend/windows/windscribe_service/process_command.cpp b/backend/windows/windscribe_service/process_command.cpp index 23301ec0b..127938fb3 100644 --- a/backend/windows/windscribe_service/process_command.cpp +++ b/backend/windows/windscribe_service/process_command.cpp @@ -763,12 +763,15 @@ MessagePacketResult connectedDns(boost::archive::text_iarchive &ia) MessagePacketResult makeHostsFileWritable(boost::archive::text_iarchive &ia) { MessagePacketResult mpr; + mpr.success = true; - const auto hostsPath = L"C:\\Windows\\System32\\drivers\\etc\\hosts"; - if (SetFileAttributes(hostsPath, GetFileAttributes(hostsPath) & ~FILE_ATTRIBUTE_READONLY)) { - mpr.success = true; - } else { - Logger::instance().out(L"Can't change permissions of \"hosts\" file."); + const auto hostsFile = Utils::getSystemDir() + L"\\drivers\\etc\\hosts"; + const auto attributes = ::GetFileAttributes(hostsFile.c_str()); + if (attributes == INVALID_FILE_ATTRIBUTES) { + Logger::instance().out(L"GetFileAttributes of hosts file failed (%lu)", ::GetLastError()); + mpr.success = false; + } else if (::SetFileAttributes(hostsFile.c_str(), attributes & ~FILE_ATTRIBUTE_READONLY) == FALSE) { + Logger::instance().out(L"SetFileAttributes of hosts file failed (%lu)", ::GetLastError()); mpr.success = false; } diff --git a/backend/windows/windscribe_service/split_tunneling/hostnames_manager/hostnames_manager.cpp b/backend/windows/windscribe_service/split_tunneling/hostnames_manager/hostnames_manager.cpp index 2e437b8f9..8e3abe625 100644 --- a/backend/windows/windscribe_service/split_tunneling/hostnames_manager/hostnames_manager.cpp +++ b/backend/windows/windscribe_service/split_tunneling/hostnames_manager/hostnames_manager.cpp @@ -63,27 +63,26 @@ void HostnamesManager::dnsResolverCallback(std::map guard(mutex_); std::vector hostsIps; - for (auto it = hostInfos.begin(); it != hostInfos.end(); ++it) - { - if (!it->second.error) - { + for (auto it = hostInfos.begin(); it != hostInfos.end(); ++it) { + if (!it->second.error) { std::vector addresses = it->second.addresses; - for (auto addr = addresses.begin(); addr != addresses.end(); ++addr) - { - hostsIps.push_back(Ip4AddressAndMask(addr->c_str())); - Logger::instance().out("RoutesManager::dnsResolverCallback(), Resolved : %s, IP: %s", it->first.c_str(), addr->c_str()); + for (const auto addr : it->second.addresses) { + if (addr == "0.0.0.0") { + // ROBERT sometimes will give us an address of 0.0.0.0 for a 'blocked' resource. This is not a valid address. + Logger::instance().out("IpHostnamesManager::dnsResolverCallback(), Resolved : %s, IP: 0.0.0.0 (Ignored)", it->first.c_str()); + } else { + hostsIps.push_back(Ip4AddressAndMask(addr.c_str())); + Logger::instance().out("RoutesManager::dnsResolverCallback(), Resolved : %s, IP: %s", it->first.c_str(), addr.c_str()); + } } - } - else - { + } else { Logger::instance().out("RoutesManager::dnsResolverCallback(), Failed resolve : %s", it->first.c_str()); } } hostsIps.insert(hostsIps.end(), ipsLatest_.begin(), ipsLatest_.end()); - if (isEnabled_) - { + if (isEnabled_) { ipRoutes_.setIps(gatewayIp_, ifIndex_, hostsIps); FirewallFilter::instance().setSplitTunnelingWhitelistIps(hostsIps); } diff --git a/backend/windows/windscribe_service/utils.cpp b/backend/windows/windscribe_service/utils.cpp index ca3baa8d3..14572d8eb 100644 --- a/backend/windows/windscribe_service/utils.cpp +++ b/backend/windows/windscribe_service/utils.cpp @@ -96,9 +96,9 @@ std::wstring guidToStr(const GUID &guid) { wchar_t szBuf[256]; swprintf(szBuf, 256, L"%08lX-%04hX-%04hX-%02hhX%02hhX-%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX", - guid.Data1, guid.Data2, guid.Data3, - guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3], - guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]); + guid.Data1, guid.Data2, guid.Data3, + guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3], + guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]); return szBuf; } @@ -226,7 +226,7 @@ void callNetworkAdapterMethod(const std::wstring &methodName, const std::wstring 0, // Authority (e.g. Kerberos) 0, // Context object &pSvc // pointer to IWbemServices proxy - ); + ); if (FAILED(hres)) { @@ -246,7 +246,7 @@ void callNetworkAdapterMethod(const std::wstring &methodName, const std::wstring RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx NULL, // client identity EOAC_NONE // proxy capabilities - ); + ); if (FAILED(hres)) { @@ -287,7 +287,7 @@ void callNetworkAdapterMethod(const std::wstring &methodName, const std::wstring std::wstring networkAdapterDeviceID = L"Win32_NetworkAdapter.DeviceID=\"" + adapterRegistryName + L"\""; hres = pSvc->ExecMethod((BSTR)networkAdapterDeviceID.c_str(), MethodName, 0, - NULL, pClassInstance, &pOutParams, NULL); + NULL, pClassInstance, &pOutParams, NULL); if (FAILED(hres)) { @@ -322,7 +322,7 @@ GUID guidFromString(const std::wstring &str) unsigned int p1, p2, p3, p4, p5, p6, p7, p8, p9, p10; swscanf_s(str.c_str(), L"{%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}", - &p0, &p1, &p2, &p3, &p4, &p5, &p6, &p7, &p8, &p9, &p10); + &p0, &p1, &p2, &p3, &p4, &p5, &p6, &p7, &p8, &p9, &p10); reqGUID.Data1 = p0; reqGUID.Data2 = static_cast(p1); reqGUID.Data3 = static_cast(p2); @@ -339,9 +339,9 @@ GUID guidFromString(const std::wstring &str) } bool addFilterV4(HANDLE engineHandle, std::vector *filterId, FWP_ACTION_TYPE type, UINT8 weight, - GUID subLayerKey, wchar_t *subLayerName, PNET_LUID pluid, - const std::vector *ranges, - uint16_t localPort, uint16_t remotePort, AppsIds *appsIds, bool persistent) + GUID subLayerKey, wchar_t *subLayerName, PNET_LUID pluid, + const std::vector *ranges, + uint16_t localPort, uint16_t remotePort, AppsIds *appsIds, bool persistent) { UINT64 id = 0; bool success = true; @@ -437,8 +437,8 @@ bool addFilterV4(HANDLE engineHandle, std::vector *filterId, FWP_ACTION_ } bool addFilterV6(HANDLE engineHandle, std::vector *filterId, FWP_ACTION_TYPE type, UINT8 weight, - GUID subLayerKey, wchar_t *subLayerName, PNET_LUID pluid, - const std::vector *ranges, bool persistent) + GUID subLayerKey, wchar_t *subLayerName, PNET_LUID pluid, + const std::vector *ranges, bool persistent) { UINT64 id = 0; bool success = true; diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index d909b1e11..197b9ad30 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -16,15 +16,8 @@ set(CMAKE_AUTORCC ON) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) -# This is a workaround for https://bugreports.qt.io/browse/QTBUG-89754 / #499 -# Without this flag, on Linux libOpenGL.so.0 will be linked instead of libGL, -# which causes Windscribe to not start on some distributions where libopengl0 is not -# installed by default. -set(OpenGL_GL_PREFERENCE LEGACY) - find_package(OpenSSL REQUIRED) find_package(Boost REQUIRED COMPONENTS serialization) -find_package(Qt6 REQUIRED COMPONENTS Widgets Network Test LinguistTools) # build_all.py sets this option when invoked with the '--sign' flag. Disabled by default option(DEFINE_USE_SIGNATURE_CHECK_MACRO "Add define USE_SIGNATURE_CHECK to project" OFF) @@ -32,19 +25,40 @@ if(DEFINE_USE_SIGNATURE_CHECK_MACRO) add_definitions(-DUSE_SIGNATURE_CHECK) endif(DEFINE_USE_SIGNATURE_CHECK_MACRO) -#set(IS_BUILD_TESTS "1") - # if a build identifier is provided, add it to the project. option(DEFINE_USE_BUILD_ID_MACRO "Add define BUILD_ID to project" "") if(DEFINE_USE_BUILD_ID_MACRO) add_definitions(-DUSE_BUILD_ID="${DEFINE_USE_BUILD_ID_MACRO}") endif(DEFINE_USE_BUILD_ID_MACRO) +if(UNIX AND (NOT APPLE)) + option(DEFINE_CLI_ONLY_MACRO "Build GUI-less client" OFF) + # if build requested a headless client, set the necessary definitions + if(DEFINE_CLI_ONLY_MACRO) + add_definitions(-DCLI_ONLY) + find_package(Qt6 REQUIRED COMPONENTS Network Test LinguistTools) + endif(DEFINE_CLI_ONLY_MACRO) +endif() + +if(NOT DEFINE_CLI_ONLY_MACRO) + # This is a workaround for https://bugreports.qt.io/browse/QTBUG-89754 / #499 + # Without this flag, on Linux libOpenGL.so.0 will be linked instead of libGL, + # which causes Windscribe to not start on some distributions where libopengl0 is not + # installed by default. + + set(OpenGL_GL_PREFERENCE LEGACY) + find_package(Qt6 REQUIRED COMPONENTS Widgets Network Test LinguistTools) +endif() + set(PROJECT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) -set(PROJECT_SOURCES - main.cpp -) +if(DEFINE_CLI_ONLY_MACRO) + set(PROJECT_SOURCES main_cli.cpp) + set(PROJECT_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/cli) +else() + set(PROJECT_SOURCES main_gui.cpp) + set(PROJECT_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/gui) +endif() if (WIN32) set (OS_SPECIFIC_LIBRARIES psapi.lib iphlpapi.lib dnsapi.lib rasapi32.lib pdh.lib Crypt32.lib Version.lib) @@ -87,6 +101,10 @@ if (WIN32) add_custom_command(TARGET Windscribe POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/tools/openvpn/openvpn.exe $/windscribeopenvpn.exe) + # post-quantum libs + add_custom_command(TARGET Windscribe POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different + ${VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/bin/oqsprovider.dll $/oqsprovider.dll) + add_custom_command(TARGET Windscribe POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/tools/ctrld/ctrld.exe $/windscribectrld.exe) elseif (APPLE) @@ -98,6 +116,10 @@ elseif (APPLE) add_custom_command(TARGET Windscribe POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/../build/backend/mac/helper/$/ $/../Library/LaunchServices) + # post-quantum libs + add_custom_command(TARGET Windscribe POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different + ${VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/bin/oqsprovider.dylib $/../Frameworks/oqsprovider.dylib) + add_custom_command(TARGET Windscribe POST_BUILD COMMAND ${CMAKE_COMMAND} -E make_directory $/../Helpers) add_custom_command(TARGET Windscribe POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different @@ -126,6 +148,8 @@ elseif (APPLE) add_custom_command(TARGET Windscribe POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/../build/gui/cli/windscribe-cli $/windscribe-cli) + add_custom_command(TARGET Windscribe POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different + ${CMAKE_CURRENT_SOURCE_DIR}/common/config/openssl.cnf $/../Resources/openssl.cnf) add_custom_command(TARGET Windscribe POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/common/licenses/open_source_licenses.txt $/../Resources/open_source_licenses.txt) endif() @@ -136,12 +160,19 @@ elseif (APPLE) set_property(TARGET Windscribe APPEND_STRING PROPERTY COMPILE_FLAGS "-fobjc-arc") endif() -target_link_libraries(Windscribe PRIVATE Qt6::Widgets Qt6::Network gui engine common ${OS_SPECIFIC_LIBRARIES}) +if(UNIX AND (NOT APPLE) AND DEFINE_CLI_ONLY_MACRO) + set(CLIENT_LIBS cli base) +else() + set(CLIENT_LIBS gui base Qt6::Widgets) +endif() + +target_link_libraries(Windscribe PRIVATE ${CLIENT_LIBS} engine common Qt6::Network ${OS_SPECIFIC_LIBRARIES}) target_include_directories(Windscribe PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/engine - ${CMAKE_CURRENT_SOURCE_DIR}/gui ${CMAKE_CURRENT_SOURCE_DIR}/common + ${CMAKE_CURRENT_SOURCE_DIR}/base + ${PROJECT_INCLUDE_DIRS} ) set_target_properties(Windscribe PROPERTIES @@ -154,17 +185,16 @@ set_target_properties(Windscribe PROPERTIES add_subdirectory(../libs/wsnet wsnet) add_subdirectory(engine) -add_subdirectory(gui) add_subdirectory(common) +add_subdirectory(base) +if(UNIX AND (NOT APPLE) AND DEFINE_CLI_ONLY_MACRO) + add_subdirectory(cli) +else() + add_subdirectory(gui) +endif() qt_finalize_executable(Windscribe) -if(DEFINED IS_BUILD_TESTS) - # Disabled for now, revisit later - # enable_testing () - # add_test (NAME locationsmodel.test COMMAND locationsmodel.test) -endif (DEFINED IS_BUILD_TESTS) - # ----- Install section ----- install(TARGETS Windscribe wsnet RUNTIME DESTINATION . @@ -175,6 +205,7 @@ install(TARGETS Windscribe wsnet if (WIN32) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/windscribeopenvpn.exe DESTINATION .) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/windscribectrld.exe DESTINATION .) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/oqsprovider.dll DESTINATION .) endif() if(UNIX AND (NOT APPLE)) @@ -183,6 +214,7 @@ if(UNIX AND (NOT APPLE)) # so for now we just copy the openssl libs install(FILES ${_OPENSSL_LIBDIR}/libcrypto.so.3 DESTINATION lib) install(FILES ${_OPENSSL_LIBDIR}/libssl.so.3 DESTINATION lib) + install(FILES ${VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/bin/oqsprovider.so DESTINATION lib) # copy openvpn install(FILES ${VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/tools/openvpn/sbin/openvpn diff --git a/client/base/CMakeLists.txt b/client/base/CMakeLists.txt new file mode 100644 index 000000000..9b14d7f16 --- /dev/null +++ b/client/base/CMakeLists.txt @@ -0,0 +1,51 @@ +cmake_minimum_required(VERSION 3.23) + +project(base) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +if (DEFINE_CLI_ONLY_MACRO) + find_package(Qt6 REQUIRED COMPONENTS Core5Compat Network) +else() + find_package(Qt6 REQUIRED COMPONENTS Core5Compat Network Widgets) +endif() + +set(PROJECT_SOURCES + blockconnect.cpp + languagecontroller.cpp +) + +add_library(base STATIC ${PROJECT_SOURCES}) + +target_link_libraries(base PRIVATE Qt6::Core Qt6::Network Qt6::Core5Compat wsnet::wsnet Boost::serialization) +if (NOT DEFINE_CLI_ONLY_MACRO) + target_link_libraries(base PRIVATE Qt6::Widgets) +endif() + +if (WIN32) + target_compile_definitions(base PRIVATE CMAKE_LIBRARY_LIBRARY + WINVER=0x0601 + _WIN32_WINNT=0x0601 + WIN32_LEAN_AND_MEAN + PIO_APC_ROUTINE_DEFINED) +endif (WIN32) + +target_include_directories(base PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/../base + ${CMAKE_CURRENT_SOURCE_DIR}/../engine + ${CMAKE_CURRENT_SOURCE_DIR}/../common +) + +add_subdirectory(backend) +add_subdirectory(launchonstartup) +add_subdirectory(localipcserver) +add_subdirectory(locations) +add_subdirectory(multipleaccountdetection) +add_subdirectory(utils) diff --git a/client/gui/backend/CMakeLists.txt b/client/base/backend/CMakeLists.txt similarity index 95% rename from client/gui/backend/CMakeLists.txt rename to client/base/backend/CMakeLists.txt index 7e8080d76..4551fdfde 100644 --- a/client/gui/backend/CMakeLists.txt +++ b/client/base/backend/CMakeLists.txt @@ -1,4 +1,4 @@ -target_sources(gui PRIVATE +target_sources(base PRIVATE backend.cpp backend.h connectstatehelper.cpp diff --git a/client/gui/backend/backend.cpp b/client/base/backend/backend.cpp similarity index 97% rename from client/gui/backend/backend.cpp rename to client/base/backend/backend.cpp index 6da7e0b0a..fbed60313 100644 --- a/client/gui/backend/backend.cpp +++ b/client/base/backend/backend.cpp @@ -20,8 +20,10 @@ Backend::Backend(QObject *parent) : QObject(parent), cmdId_(0), isCanLoginWithAuthHash_(false), isFirewallEnabled_(false), - isExternalConfigMode_(false) + isExternalConfigMode_(false), + loginState_(LOGIN_STATE_LOGGED_OUT) { + preferences_.loadGuiSettings(); #ifdef Q_OS_LINUX @@ -78,7 +80,7 @@ void Backend::init() connect(engine_, &Engine::proxySharingStateChanged, this, &Backend::onEngineProxySharingStateChanged); connect(engine_, &Engine::wifiSharingStateChanged, this, &Backend::onEngineWifiSharingStateChanged); connect(engine_, &Engine::wifiSharingFailed, this, &Backend::wifiSharingFailed); - connect(engine_, &Engine::signOutFinished, this, &Backend::onEngineSignOutFinished); + connect(engine_, &Engine::logoutFinished, this, &Backend::onEngineLogoutFinished); connect(engine_, &Engine::gotoCustomOvpnConfigModeFinished, this, &Backend::onEngineGotoCustomOvpnConfigModeFinished); connect(engine_, &Engine::detectionCpuUsageAfterConnected, this, &Backend::onEngineDetectionCpuUsageAfterConnected); connect(engine_, &Engine::requestUsername, this, &Backend::onEngineRequestUsername); @@ -119,6 +121,7 @@ void Backend::enableBFE_win() void Backend::login(const QString &username, const QString &password, const QString &code2fa) { + loginState_ = LOGIN_STATE_LOGGING_IN; bLastLoginWithAuthHash_ = false; lastUsername_ = username; lastPassword_ = password; @@ -138,12 +141,14 @@ bool Backend::isSavedApiSettingsExists() const void Backend::loginWithAuthHash() { + loginState_ = LOGIN_STATE_LOGGING_IN; bLastLoginWithAuthHash_ = true; engine_->loginWithAuthHash(); } void Backend::loginWithLastLoginSettings() { + loginState_ = LOGIN_STATE_LOGGING_IN; if (bLastLoginWithAuthHash_) loginWithAuthHash(); else @@ -155,9 +160,9 @@ bool Backend::isLastLoginWithAuthHash() const return bLastLoginWithAuthHash_; } -void Backend::signOut(bool keepFirewallOn) +void Backend::logout(bool keepFirewallOn) { - engine_->signOut(keepFirewallOn); + engine_->logout(keepFirewallOn); } void Backend::sendConnect(const LocationID &lid, const types::ConnectionSettings &connectionSettings) @@ -177,6 +182,11 @@ bool Backend::isDisconnected() const return connectStateHelper_.isDisconnected(); } +LOGIN_STATE Backend::currentLoginState() const +{ + return loginState_; +} + CONNECT_STATE Backend::currentConnectState() const { return connectStateHelper_.currentConnectState().connectState; @@ -236,9 +246,9 @@ void Backend::stopWifiSharing() engine_->stopWifiSharing(); } -void Backend::startProxySharing(PROXY_SHARING_TYPE proxySharingMode) +void Backend::startProxySharing(PROXY_SHARING_TYPE proxySharingMode, uint port) { - engine_->startProxySharing(proxySharingMode); + engine_->startProxySharing(proxySharingMode, port); } void Backend::stopProxySharing() @@ -430,12 +440,15 @@ void Backend::onEngineFirewallStateChanged(bool isEnabled) void Backend::onEngineLoginFinished(bool isLoginFromSavedSettings, const QString &authHash, const api_responses::PortMap &portMap) { + loginState_ = LOGIN_STATE_LOGGED_IN; preferencesHelper_.setPortMap(portMap); emit loginFinished(isLoginFromSavedSettings); } void Backend::onEngineLoginError(LOGIN_RET retCode, const QString &errorMessage) { + loginState_ = LOGIN_STATE_LOGIN_ERROR; + lastLoginError_ = retCode; emit loginError(retCode, errorMessage); } @@ -573,9 +586,10 @@ void Backend::onEngineWifiSharingStateChanged(bool bEnabled, const QString &ssid emit wifiSharingInfoChanged(wifiSharingInfo); } -void Backend::onEngineSignOutFinished() +void Backend::onEngineLogoutFinished() { - emit signOutFinished(); + loginState_ = LOGIN_STATE_LOGGED_OUT; + emit logoutFinished(); } void Backend::onEngineGotoCustomOvpnConfigModeFinished() diff --git a/client/gui/backend/backend.h b/client/base/backend/backend.h similarity index 96% rename from client/gui/backend/backend.h rename to client/base/backend/backend.h index 12ab6c6d7..34a9d4003 100644 --- a/client/gui/backend/backend.h +++ b/client/base/backend/backend.h @@ -1,9 +1,7 @@ #pragma once -#include #include #include -#include #include "connectstatehelper.h" #include "firewallstatehelper.h" @@ -39,11 +37,14 @@ class Backend : public QObject bool isSavedApiSettingsExists() const; void loginWithAuthHash(); void loginWithLastLoginSettings(); + void setLoginError(LOGIN_RET err, const QString &msg); bool isLastLoginWithAuthHash() const; - void signOut(bool keepFirewallOn); + void logout(bool keepFirewallOn); void sendConnect(const LocationID &lid, const types::ConnectionSettings &connectionSettings = types::ConnectionSettings(types::Protocol(), 0, true)); void sendDisconnect(); bool isDisconnected() const; + LOGIN_STATE currentLoginState() const; + LOGIN_RET lastLoginError() const; CONNECT_STATE currentConnectState() const; LocationID currentLocation() const; @@ -58,7 +59,7 @@ class Backend : public QObject void startWifiSharing(const QString &ssid, const QString &password); void stopWifiSharing(); - void startProxySharing(PROXY_SHARING_TYPE proxySharingMode); + void startProxySharing(PROXY_SHARING_TYPE proxySharingMode, uint port); void stopProxySharing(); void setIPv6StateInOS(bool bEnabled); @@ -148,7 +149,7 @@ private slots: void onEngineLostConnectionToHelper(); void onEngineProxySharingStateChanged(bool bEnabled, PROXY_SHARING_TYPE proxySharingType, const QString &address, int usersCount); void onEngineWifiSharingStateChanged(bool bEnabled, const QString &ssid, int usersCount); - void onEngineSignOutFinished(); + void onEngineLogoutFinished(); void onEngineGotoCustomOvpnConfigModeFinished(); @@ -189,7 +190,7 @@ private slots: void tryingBackupEndpoint(int num, int cnt); void loginError(LOGIN_RET loginError, const QString &errorMessage); - void signOutFinished(); + void logoutFinished(); void myIpChanged(QString ip, bool isFromDisconnectedState); void connectStateChanged(const types::ConnectState &connectState); @@ -273,4 +274,7 @@ private slots: QString generateNewFriendlyName(); void updateAccountInfo(); + + LOGIN_STATE loginState_; + LOGIN_RET lastLoginError_; }; diff --git a/client/gui/backend/connectstatehelper.cpp b/client/base/backend/connectstatehelper.cpp similarity index 100% rename from client/gui/backend/connectstatehelper.cpp rename to client/base/backend/connectstatehelper.cpp diff --git a/client/gui/backend/connectstatehelper.h b/client/base/backend/connectstatehelper.h similarity index 100% rename from client/gui/backend/connectstatehelper.h rename to client/base/backend/connectstatehelper.h diff --git a/client/gui/backend/firewallstatehelper.cpp b/client/base/backend/firewallstatehelper.cpp similarity index 100% rename from client/gui/backend/firewallstatehelper.cpp rename to client/base/backend/firewallstatehelper.cpp diff --git a/client/gui/backend/firewallstatehelper.h b/client/base/backend/firewallstatehelper.h similarity index 100% rename from client/gui/backend/firewallstatehelper.h rename to client/base/backend/firewallstatehelper.h diff --git a/client/gui/backend/notificationscontroller.cpp b/client/base/backend/notificationscontroller.cpp similarity index 100% rename from client/gui/backend/notificationscontroller.cpp rename to client/base/backend/notificationscontroller.cpp diff --git a/client/gui/backend/notificationscontroller.h b/client/base/backend/notificationscontroller.h similarity index 100% rename from client/gui/backend/notificationscontroller.h rename to client/base/backend/notificationscontroller.h diff --git a/client/gui/backend/persistentstate.cpp b/client/base/backend/persistentstate.cpp similarity index 81% rename from client/gui/backend/persistentstate.cpp rename to client/base/backend/persistentstate.cpp index 535a2c3ec..82b326e9e 100644 --- a/client/gui/backend/persistentstate.cpp +++ b/client/base/backend/persistentstate.cpp @@ -195,3 +195,34 @@ void PersistentState::setLastLocationTab(LOCATION_TAB tab) state_.lastLocationTab = tab; save(); } + +void PersistentState::fromIni(QSettings &settings) +{ + QVector networks = state_.networkWhiteList; + QVector out; + + settings.beginGroup(kIniNetworksProp); + for (auto network : networks) { + settings.beginGroup(network.networkOrSsid); + network.trustType = NETWORK_TRUST_TYPE_fromString(settings.value(kIniNetworkTrustTypeProp, NETWORK_TRUST_TYPE_toString(network.trustType)).toString()); + settings.endGroup(); + out << network; + } + settings.endGroup(); + + state_.networkWhiteList = out; + save(); +} + +void PersistentState::toIni(QSettings &settings) +{ + settings.beginGroup(kIniNetworksProp); + for (auto network : state_.networkWhiteList) { + settings.beginGroup(network.networkOrSsid); + settings.setValue(kIniNetworkTrustTypeProp, NETWORK_TRUST_TYPE_toString(network.trustType)); + settings.endGroup(); + } + settings.endGroup(); + save(); +} + diff --git a/client/gui/backend/persistentstate.h b/client/base/backend/persistentstate.h similarity index 86% rename from client/gui/backend/persistentstate.h rename to client/base/backend/persistentstate.h index b8d6add46..5fb1940d6 100644 --- a/client/gui/backend/persistentstate.h +++ b/client/base/backend/persistentstate.h @@ -1,5 +1,6 @@ #pragma once +#include #include "types/locationid.h" #include "types/networkinterface.h" #include "types/guipersistentstate.h" @@ -46,6 +47,9 @@ class PersistentState LOCATION_TAB lastLocationTab() const; void setLastLocationTab(LOCATION_TAB tab); + void fromIni(QSettings &settings); + void toIni(QSettings &settings); + void save(); private: @@ -54,6 +58,9 @@ class PersistentState types::GuiPersistentState state_; + static const inline QString kIniNetworksProp = "Networks"; + static const inline QString kIniNetworkTrustTypeProp = "NetworkTrustType"; + // for serialization static constexpr quint32 magic_ = 0x8845C2AE; static constexpr int versionForSerialization_ = 2; // should increment the version if the data format is changed diff --git a/client/gui/backend/preferences/accountinfo.cpp b/client/base/backend/preferences/accountinfo.cpp similarity index 100% rename from client/gui/backend/preferences/accountinfo.cpp rename to client/base/backend/preferences/accountinfo.cpp diff --git a/client/gui/backend/preferences/accountinfo.h b/client/base/backend/preferences/accountinfo.h similarity index 100% rename from client/gui/backend/preferences/accountinfo.h rename to client/base/backend/preferences/accountinfo.h diff --git a/client/gui/backend/preferences/detectlanrange.cpp b/client/base/backend/preferences/detectlanrange.cpp similarity index 100% rename from client/gui/backend/preferences/detectlanrange.cpp rename to client/base/backend/preferences/detectlanrange.cpp diff --git a/client/gui/backend/preferences/detectlanrange.h b/client/base/backend/preferences/detectlanrange.h similarity index 100% rename from client/gui/backend/preferences/detectlanrange.h rename to client/base/backend/preferences/detectlanrange.h diff --git a/client/gui/backend/preferences/preferences.cpp b/client/base/backend/preferences/preferences.cpp similarity index 90% rename from client/gui/backend/preferences/preferences.cpp rename to client/base/backend/preferences/preferences.cpp index 9542542f3..f2b08c756 100644 --- a/client/gui/backend/preferences/preferences.cpp +++ b/client/base/backend/preferences/preferences.cpp @@ -1,7 +1,9 @@ #include "preferences.h" #include +#ifndef CLI_ONLY #include +#endif #include "../persistentstate.h" #include "detectlanrange.h" @@ -76,7 +78,11 @@ void Preferences::setAllowLanTraffic(bool b) bool Preferences::isMinimizeAndCloseToTray() const { +#ifndef CLI_ONLY return QSystemTrayIcon::isSystemTrayAvailable() && guiSettings_.isMinimizeAndCloseToTray; +#else + return false; +#endif } void Preferences::setMinimizeAndCloseToTray(bool b) @@ -687,34 +693,29 @@ void Preferences::clearLastKnownGoodProtocols(const QString &network) QJsonObject Preferences::toJson() const { QJsonObject json; - json[jsonInfo_.kGuiSettingsProp] = guiSettings_.toJson(); - json[jsonInfo_.kEngineSettingsProp] = engineSettings_.toJson(); + json[kJsonGuiSettingsProp] = guiSettings_.toJson(); + json[kJsonEngineSettingsProp] = engineSettings_.toJson(); return json; } void Preferences::updateFromJson(const QJsonObject& json) { - if (json.contains(jsonInfo_.kEngineSettingsProp) && json[jsonInfo_.kEngineSettingsProp].isObject()) - updateEngineSettingsFromJson(json[jsonInfo_.kEngineSettingsProp].toObject()); - - if (json.contains(jsonInfo_.kGuiSettingsProp) && json[jsonInfo_.kGuiSettingsProp].isObject()) - updateGuiSettingsFromJson(json[jsonInfo_.kGuiSettingsProp].toObject()); -} - -void Preferences::updateEngineSettingsFromJson(const QJsonObject &json) -{ - setEngineSettings(types::EngineSettings(json)); -} + if (json.contains(kJsonEngineSettingsProp) && json[kJsonEngineSettingsProp].isObject()) + setEngineSettings(json[kJsonEngineSettingsProp].toObject()); + emitEngineSettingsChanged(); -void Preferences::updateGuiSettingsFromJson(const QJsonObject &json) -{ - setGuiSettings(types::GuiSettings(json)); + if (json.contains(kJsonGuiSettingsProp) && json[kJsonGuiSettingsProp].isObject()) + setGuiSettings(json[kJsonGuiSettingsProp].toObject()); } void Preferences::emitEngineSettingsChanged() { if (!isSettingEngineSettings_) emit engineSettingsChanged(); + +#ifdef CLI_ONLY + saveIni(); +#endif } void Preferences::setEngineSettings(const types::EngineSettings &es) @@ -805,6 +806,10 @@ void Preferences::saveGuiSettings() const SimpleCrypt simpleCrypt(SIMPLE_CRYPT_KEY); settings.setValue("guiSettings2", simpleCrypt.encryptToString(arr)); settings.sync(); + +#ifdef CLI_ONLY + saveIni(); +#endif } void Preferences::loadGuiSettings() @@ -853,10 +858,60 @@ void Preferences::loadGuiSettings() guiSettings_ = types::GuiSettings(); // reset to defaults } +#ifdef CLI_ONLY + QSettings ini("Windscribe", "windscribe_cli"); + guiSettings_.fromIni(ini); + PersistentState::instance().fromIni(ini); +#endif qCDebug(LOG_BASIC) << "Gui settings" << guiSettings_; } +void Preferences::loadIni() +{ + QSettings settings("Windscribe", "windscribe_cli"); + + // PersistentState can be updated directly as there are no signals to worry about + PersistentState::instance().fromIni(settings); + + // Copy settings and apply the ini to the copy, then call the setXYZ functions on the actual settings. + // This is a bit roundabount but ensures that all the correct signals are emitted. + types::GuiSettings gs = guiSettings_; + gs.fromIni(settings); + + setAutoConnect(gs.isAutoConnect); + setAutoSecureNetworks(gs.isAutoSecureNetworks); + setLaunchOnStartup(gs.isLaunchOnStartup); + setShareProxyGateway(gs.shareProxyGateway); + setSplitTunnelingApps(gs.splitTunneling.apps); + setSplitTunnelingNetworkRoutes(gs.splitTunneling.networkRoutes); + setSplitTunnelingSettings(gs.splitTunneling.settings); + + // Same for engine settings + types::EngineSettings es = engineSettings_; + es.fromIni(settings); + + isSettingEngineSettings_ = true; + setLanguage(es.language()); + setUpdateChannel(es.updateChannel()); + setIgnoreSslErrors(es.isIgnoreSslErrors()); + setAntiCensorship(es.isAntiCensorship()); + setAllowLanTraffic(es.isAllowLanTraffic()); + setFirewallSettings(es.firewallSettings()); + setConnectionSettings(es.connectionSettings()); + setApiResolution(es.apiResolutionSettings()); + setProxySettings(es.proxySettings()); + setPacketSize(es.packetSize()); + setDnsPolicy(es.dnsPolicy()); + setConnectedDnsInfo(es.connectedDnsInfo()); +#ifdef Q_OS_LINUX + setDnsManager(es.dnsManager()); +#endif + isSettingEngineSettings_ = false; + + emitEngineSettingsChanged(); +} + void Preferences::validateAndUpdateIfNeeded() { bool is_update_needed = false; @@ -911,3 +966,14 @@ void Preferences::setShowLocationLoad(bool b) emit showLocationLoadChanged(guiSettings_.isShowLocationHealth); } } + +void Preferences::saveIni() const +{ + QSettings settings("Windscribe", "windscribe_cli"); + + guiSettings_.toIni(settings); + engineSettings_.toIni(settings); + PersistentState::instance().toIni(settings); + + settings.sync(); +} diff --git a/client/gui/backend/preferences/preferences.h b/client/base/backend/preferences/preferences.h similarity index 96% rename from client/gui/backend/preferences/preferences.h rename to client/base/backend/preferences/preferences.h index 4d4469b25..be453acee 100644 --- a/client/gui/backend/preferences/preferences.h +++ b/client/base/backend/preferences/preferences.h @@ -13,13 +13,6 @@ class Preferences : public QObject { Q_OBJECT public: - - struct JsonInfo - { - const QString kEngineSettingsProp = "engineSettings"; - const QString kGuiSettingsProp = "guiSettings"; - }; - explicit Preferences(QObject *parent = nullptr); ~Preferences(); @@ -168,6 +161,9 @@ class Preferences : public QObject QJsonObject toJson() const; void updateFromJson(const QJsonObject& ob); + void loadIni(); + void saveIni() const; + signals: void isLaunchOnStartupChanged(bool b); void isAutoConnectChanged(bool b); @@ -220,18 +216,17 @@ class Preferences : public QObject void reportErrorToUser(QString title, QString desc); private: - void updateEngineSettingsFromJson(const QJsonObject& json); - void updateGuiSettingsFromJson(const QJsonObject& json); - types::EngineSettings engineSettings_; types::GuiSettings guiSettings_; bool isSettingEngineSettings_; QMap timers_; - JsonInfo jsonInfo_; void emitEngineSettingsChanged(); + static const inline QString kJsonEngineSettingsProp = "engineSettings"; + static const inline QString kJsonGuiSettingsProp = "guiSettings"; + // for serialization static constexpr quint32 magic_ = 0x7715C211; static constexpr int versionForSerialization_ = 1; // should increment the version if the data format is changed diff --git a/client/gui/backend/preferences/preferenceshelper.cpp b/client/base/backend/preferences/preferenceshelper.cpp similarity index 100% rename from client/gui/backend/preferences/preferenceshelper.cpp rename to client/base/backend/preferences/preferenceshelper.cpp diff --git a/client/gui/backend/preferences/preferenceshelper.h b/client/base/backend/preferences/preferenceshelper.h similarity index 100% rename from client/gui/backend/preferences/preferenceshelper.h rename to client/base/backend/preferences/preferenceshelper.h diff --git a/client/gui/backend/types/upgrademodetype.cpp b/client/base/backend/types/upgrademodetype.cpp similarity index 100% rename from client/gui/backend/types/upgrademodetype.cpp rename to client/base/backend/types/upgrademodetype.cpp diff --git a/client/gui/backend/types/upgrademodetype.h b/client/base/backend/types/upgrademodetype.h similarity index 100% rename from client/gui/backend/types/upgrademodetype.h rename to client/base/backend/types/upgrademodetype.h diff --git a/client/gui/blockconnect.cpp b/client/base/blockconnect.cpp similarity index 100% rename from client/gui/blockconnect.cpp rename to client/base/blockconnect.cpp diff --git a/client/gui/blockconnect.h b/client/base/blockconnect.h similarity index 100% rename from client/gui/blockconnect.h rename to client/base/blockconnect.h diff --git a/client/gui/languagecontroller.cpp b/client/base/languagecontroller.cpp similarity index 98% rename from client/gui/languagecontroller.cpp rename to client/base/languagecontroller.cpp index f49003ca5..e1fe968d4 100644 --- a/client/gui/languagecontroller.cpp +++ b/client/base/languagecontroller.cpp @@ -1,6 +1,6 @@ #include "languagecontroller.h" -#include +#include #include "utils/languagesutil.h" #include "utils/logger.h" diff --git a/client/gui/languagecontroller.h b/client/base/languagecontroller.h similarity index 100% rename from client/gui/languagecontroller.h rename to client/base/languagecontroller.h diff --git a/client/gui/launchonstartup/CMakeLists.txt b/client/base/launchonstartup/CMakeLists.txt similarity index 70% rename from client/gui/launchonstartup/CMakeLists.txt rename to client/base/launchonstartup/CMakeLists.txt index 434859134..6f581cde5 100644 --- a/client/gui/launchonstartup/CMakeLists.txt +++ b/client/base/launchonstartup/CMakeLists.txt @@ -1,20 +1,20 @@ -target_sources(gui PRIVATE +target_sources(base PRIVATE launchonstartup.cpp launchonstartup.h ) if (WIN32) - target_sources(gui PRIVATE + target_sources(base PRIVATE launchonstartup_win.cpp launchonstartup_win.h ) elseif(APPLE) - target_sources(gui PRIVATE + target_sources(base PRIVATE launchonstartup_mac.mm launchonstartup_win.h ) elseif(UNIX) - target_sources(gui PRIVATE + target_sources(base PRIVATE launchonstartup_linux.cpp launchonstartup_linux.h ) diff --git a/client/gui/launchonstartup/launchonstartup.cpp b/client/base/launchonstartup/launchonstartup.cpp similarity index 100% rename from client/gui/launchonstartup/launchonstartup.cpp rename to client/base/launchonstartup/launchonstartup.cpp diff --git a/client/gui/launchonstartup/launchonstartup.h b/client/base/launchonstartup/launchonstartup.h similarity index 100% rename from client/gui/launchonstartup/launchonstartup.h rename to client/base/launchonstartup/launchonstartup.h diff --git a/client/gui/launchonstartup/launchonstartup_linux.cpp b/client/base/launchonstartup/launchonstartup_linux.cpp similarity index 77% rename from client/gui/launchonstartup/launchonstartup_linux.cpp rename to client/base/launchonstartup/launchonstartup_linux.cpp index d9f139758..a3ebd1548 100644 --- a/client/gui/launchonstartup/launchonstartup_linux.cpp +++ b/client/base/launchonstartup/launchonstartup_linux.cpp @@ -6,8 +6,13 @@ void LaunchOnStartup_linux::setLaunchOnStartup(bool enable) { - if (enable) - { +#ifdef CLI_ONLY + int err = system("systemctl --user enable windscribe"); + if (err != 0) { + qCDebug(LOG_BASIC) << "Could not enable user service for launch on startup"; + } +#else + if (enable) { if (QFile::exists("/usr/share/applications/windscribe.desktop")) { QString destDir = QDir::homePath() + "/.config/autostart"; @@ -16,16 +21,13 @@ void LaunchOnStartup_linux::setLaunchOnStartup(bool enable) QString destFile = destDir + "/windscribe.desktop"; QFile::remove(destFile); QFile::link("/usr/share/applications/windscribe.desktop", destFile); - } - else - { + } else { qCDebug(LOG_BASIC) << "LaunchOnStartup_linux: File /usr/share/applications/windscribe.desktop doesn't exists"; } - } - else - { + } else { QString destFile = QDir::homePath() + "/.config/autostart/windscribe.desktop"; QFile::remove(destFile); } +#endif } diff --git a/client/gui/launchonstartup/launchonstartup_linux.h b/client/base/launchonstartup/launchonstartup_linux.h similarity index 100% rename from client/gui/launchonstartup/launchonstartup_linux.h rename to client/base/launchonstartup/launchonstartup_linux.h diff --git a/client/gui/launchonstartup/launchonstartup_mac.h b/client/base/launchonstartup/launchonstartup_mac.h similarity index 100% rename from client/gui/launchonstartup/launchonstartup_mac.h rename to client/base/launchonstartup/launchonstartup_mac.h diff --git a/client/gui/launchonstartup/launchonstartup_mac.mm b/client/base/launchonstartup/launchonstartup_mac.mm similarity index 100% rename from client/gui/launchonstartup/launchonstartup_mac.mm rename to client/base/launchonstartup/launchonstartup_mac.mm diff --git a/client/gui/launchonstartup/launchonstartup_win.cpp b/client/base/launchonstartup/launchonstartup_win.cpp similarity index 100% rename from client/gui/launchonstartup/launchonstartup_win.cpp rename to client/base/launchonstartup/launchonstartup_win.cpp diff --git a/client/gui/launchonstartup/launchonstartup_win.h b/client/base/launchonstartup/launchonstartup_win.h similarity index 100% rename from client/gui/launchonstartup/launchonstartup_win.h rename to client/base/launchonstartup/launchonstartup_win.h diff --git a/client/gui/localipcserver/CMakeLists.txt b/client/base/localipcserver/CMakeLists.txt similarity index 62% rename from client/gui/localipcserver/CMakeLists.txt rename to client/base/localipcserver/CMakeLists.txt index 907ff6295..a7ae8f8cd 100644 --- a/client/gui/localipcserver/CMakeLists.txt +++ b/client/base/localipcserver/CMakeLists.txt @@ -1,4 +1,4 @@ -target_sources(gui PRIVATE +target_sources(base PRIVATE localipcserver.cpp localipcserver.h ) diff --git a/client/base/localipcserver/localipcserver.cpp b/client/base/localipcserver/localipcserver.cpp new file mode 100644 index 000000000..ef9c123e4 --- /dev/null +++ b/client/base/localipcserver/localipcserver.cpp @@ -0,0 +1,235 @@ +#include "localipcserver.h" + +#include "backend/persistentstate.h" +#include "ipc/clicommands.h" +#include "ipc/server.h" +#include "utils/logger.h" +#include "utils/utils.h" +#include "utils/ws_assert.h" + +LocalIPCServer::LocalIPCServer(Backend *backend, QObject *parent) : QObject(parent) + , backend_(backend), updateError_(UPDATE_VERSION_ERROR_NO_ERROR), updateProgress_(0) +{ + connect(backend_, &Backend::checkUpdateChanged, this, &LocalIPCServer::onBackendCheckUpdateChanged); + connect(backend_, &Backend::connectStateChanged, this, &LocalIPCServer::onBackendConnectStateChanged); + connect(backend_, &Backend::internetConnectivityChanged, this, &LocalIPCServer::onBackendInternetConnectivityChanged); + connect(backend_, &Backend::loginError, this, &LocalIPCServer::onBackendLoginError); + connect(backend_, &Backend::loginFinished, this, &LocalIPCServer::onBackendLoginFinished); + connect(backend_, &Backend::logoutFinished, this, &LocalIPCServer::onBackendLogoutFinished); + connect(backend_, &Backend::protocolPortChanged, this, &LocalIPCServer::onBackendProtocolPortChanged); + connect(backend_, &Backend::testTunnelResult, this, &LocalIPCServer::onBackendTestTunnelResult); + connect(backend_, &Backend::updateVersionChanged, this, &LocalIPCServer::onBackendUpdateVersionChanged); +} + +LocalIPCServer::~LocalIPCServer() +{ + for (IPC::Connection * connection : connections_) { + connection->close(); + delete connection; + } + connections_.clear(); + SAFE_DELETE(server_); + qCDebug(LOG_CLI_IPC) << "IPC server for CLI stopped"; +} + +void LocalIPCServer::start() +{ + WS_ASSERT(server_ == nullptr); + server_ = new IPC::Server(); + connect(server_, &IPC::Server::newConnection, this, &LocalIPCServer::onServerCallbackAcceptFunction); + + if (!server_->start()) { + qCDebug(LOG_CLI_IPC) << "Can't start IPC server for CLI"; + } else { + qCDebug(LOG_CLI_IPC) << "IPC server for CLI started"; + } +} + +void LocalIPCServer::sendLocations(const QStringList &list) +{ + IPC::CliCommands::LocationsList cmd; + cmd.locations_ = list; + sendCommand(cmd); +} + +void LocalIPCServer::onServerCallbackAcceptFunction(IPC::Connection *connection) +{ + qCDebug(LOG_IPC) << "Client connected:" << connection; + + connections_.append(connection); + + connect(connection, &IPC::Connection::newCommand, this, &LocalIPCServer::onConnectionCommandCallback); + connect(connection, &IPC::Connection::stateChanged, this, &LocalIPCServer::onConnectionStateCallback); +} + +void LocalIPCServer::onConnectionCommandCallback(IPC::Command *command, IPC::Connection * /*connection*/) +{ + if (command->getStringId() ==IPC::CliCommands::ShowLocations::getCommandStringId()) { + emit showLocations(); +#ifdef CLI_ONLY + // For a headless client, do not return Acknowledge here; we need the MainService to provide us + // the list of locations to return via sendLocations(). + return; +#endif + } else if (command->getStringId() == IPC::CliCommands::GetState::getCommandStringId()) { + sendState(); + return; + } else if (command->getStringId() == IPC::CliCommands::Connect::getCommandStringId()) { + IPC::CliCommands::Connect *cmd = static_cast(command); + QString locationStr = cmd->location_; + types::Protocol protocol = types::Protocol::fromString(cmd->protocol_); + LocationID lid; + if (locationStr.isEmpty()) { + lid = PersistentState::instance().lastLocation(); + if (!lid.isValid()) { + lid = backend_->locationsModelManager()->getBestLocationId(); + } + } else if (locationStr == "best") { + lid = backend_->locationsModelManager()->getBestLocationId(); + } else { + lid = backend_->locationsModelManager()->findLocationByFilter(locationStr); + } + + if (lid.isValid()) { + emit connectToLocation(lid, protocol); + } + } else if (command->getStringId() == IPC::CliCommands::Disconnect::getCommandStringId()) { + if (!backend_->isDisconnected()) { + backend_->sendDisconnect(); + } + } else if (command->getStringId() == IPC::CliCommands::Firewall::getCommandStringId()) { + IPC::CliCommands::Firewall *cmd = static_cast(command); + if (cmd->isEnable_ && !backend_->isFirewallEnabled()) { + backend_->firewallOn(); + } else if (!cmd->isEnable_ && backend_->isFirewallEnabled() && !backend_->isFirewallAlwaysOn()) { + backend_->firewallOff(); + } + } else if (command->getStringId() == IPC::CliCommands::Login::getCommandStringId()) { + if (backend_->currentLoginState() == LOGIN_STATE_LOGGED_OUT || backend_->currentLoginState() == LOGIN_STATE_LOGIN_ERROR) { + IPC::CliCommands::Login *cmd = static_cast(command); + emit attemptLogin(cmd->username_, cmd->password_, cmd->code2fa_); + } + } else if (command->getStringId() == IPC::CliCommands::Logout::getCommandStringId()) { + if (backend_->currentLoginState() == LOGIN_STATE_LOGGED_IN || backend_->currentLoginState() == LOGIN_STATE_LOGGING_IN) { + IPC::CliCommands::Logout *cmd = static_cast(command); + backend_->logout(cmd->isKeepFirewallOn_); + } + } else if (command->getStringId() == IPC::CliCommands::SendLogs::getCommandStringId()) { + backend_->sendDebugLog(); + } else if (command->getStringId() == IPC::CliCommands::Update::getCommandStringId()) { + if (!updateAvailable_.isEmpty()) { +#ifdef CLI_ONLY + backend_->sendUpdateVersion(0); +#else + emit update(); +#endif + } + } else if (command->getStringId() == IPC::CliCommands::ReloadConfig::getCommandStringId()) { + backend_->getPreferences()->loadIni(); + } + + IPC::CliCommands::Acknowledge cmd; + sendCommand(cmd); +} + +void LocalIPCServer::onConnectionStateCallback(int state, IPC::Connection *connection) +{ + if (state == IPC::CONNECTION_DISCONNECTED) { + connections_.removeOne(connection); + connection->close(); + delete connection; + } else if (state == IPC::CONNECTION_ERROR) { + qCDebug(LOG_BASIC) << "CLI disconnected from server with error"; + connections_.removeOne(connection); + connection->close(); + delete connection; + } +} + +void LocalIPCServer::onBackendLoginFinished(bool /*isLoginFromSavedSettings*/) +{ + loginState_ = LOGIN_STATE_LOGGED_IN; +} + +void LocalIPCServer::onBackendLoginError(LOGIN_RET code, const QString &msg) +{ + lastLoginError_ = code; + lastLoginErrorMessage_ = msg; +} + +void LocalIPCServer::onBackendLogoutFinished() +{ + loginState_ = LOGIN_STATE_LOGGED_OUT; +} + +void LocalIPCServer::sendCommand(const IPC::Command &command) +{ + for (IPC::Connection * connection : connections_) { + connection->sendCommand(command); + } +} + +void LocalIPCServer::sendState() +{ + IPC::CliCommands::State cmd; + cmd.language_ = backend_->getPreferences()->language(); + cmd.connectivity_ = connectivity_; + cmd.loginState_ = backend_->currentLoginState(); + cmd.loginError_ = lastLoginError_; + cmd.loginErrorMessage_ = lastLoginErrorMessage_; + cmd.connectState_ = connectState_; + cmd.tunnelTestState_ = tunnelTestState_; + cmd.protocol_ = protocol_; + cmd.port_ = port_; + cmd.location_ = backend_->currentLocation(); + cmd.isFirewallOn_ = backend_->isFirewallEnabled(); + cmd.isFirewallAlwaysOn_ = backend_->isFirewallAlwaysOn(); + cmd.updateError_ = updateError_; + cmd.updateProgress_ = updateProgress_; + cmd.updateAvailable_ = updateAvailable_; + cmd.trafficUsed_ = backend_->getAccountInfo()->trafficUsed(); + cmd.trafficMax_ = backend_->getAccountInfo()->plan(); + sendCommand(cmd); +} + +void LocalIPCServer::onBackendCheckUpdateChanged(const api_responses::CheckUpdate &info) +{ + if (info.isAvailable()) { + updateAvailable_ = info.version() + "." + QString::number(info.latestBuild()); + } else { + updateAvailable_ = ""; + } +} + +void LocalIPCServer::onBackendConnectStateChanged(const types::ConnectState &state) +{ + connectState_ = state; + tunnelTestState_ = TUNNEL_TEST_STATE_UNKNOWN; +} + +void LocalIPCServer::onBackendProtocolPortChanged(const types::Protocol &protocol, uint port) +{ + protocol_ = protocol; + port_ = port; +} + +void LocalIPCServer::onBackendInternetConnectivityChanged(bool connectivity) +{ + connectivity_ = connectivity; +} + +void LocalIPCServer::onBackendTestTunnelResult(bool success) +{ + tunnelTestState_ = success ? TUNNEL_TEST_STATE_SUCCESS : TUNNEL_TEST_STATE_FAILURE; +} + +void LocalIPCServer::onBackendUpdateVersionChanged(uint progressPercent, UPDATE_VERSION_STATE state, UPDATE_VERSION_ERROR error) +{ + if (state == UPDATE_VERSION_STATE_DONE) { + updateError_ = error; + } else if (state == UPDATE_VERSION_STATE_DOWNLOADING) { + updateProgress_ = progressPercent; + } else if (state == UPDATE_VERSION_STATE_RUNNING) { + updateProgress_ = 100; + } +} diff --git a/client/base/localipcserver/localipcserver.h b/client/base/localipcserver/localipcserver.h new file mode 100644 index 000000000..4b9fbe978 --- /dev/null +++ b/client/base/localipcserver/localipcserver.h @@ -0,0 +1,63 @@ +#pragma once + +#include +#include "api_responses/checkupdate.h" +#include "backend/backend.h" +#include "ipc/server.h" +#include "ipc/connection.h" +#include "types/connectstate.h" +#include "types/enums.h" +#include "types/protocol.h" + +// Local server for receive and execute commands from local processes (currently only from the CLI). +class LocalIPCServer : public QObject +{ + Q_OBJECT +public: + explicit LocalIPCServer(Backend *backend, QObject *parent = nullptr); + ~LocalIPCServer(); + + void start(); + void sendLocations(const QStringList &locations); + +signals: + void showLocations(); + void connectToLocation(const LocationID &id, const types::Protocol &protocol); + void attemptLogin(const QString &username, const QString &password, const QString &code2fa); + void update(); + +private slots: + void onServerCallbackAcceptFunction(IPC::Connection *connection); + void onConnectionCommandCallback(IPC::Command *command, IPC::Connection *connection); + void onConnectionStateCallback(int state, IPC::Connection *connection); + + void onBackendCheckUpdateChanged(const api_responses::CheckUpdate &checkUpdate); + void onBackendConnectStateChanged(const types::ConnectState &state); + void onBackendInternetConnectivityChanged(bool connectivity); + void onBackendLoginFinished(bool isLoginFromSavedSettings); + void onBackendLoginError(LOGIN_RET code, const QString &msg); + void onBackendLogoutFinished(); + void onBackendProtocolPortChanged(const types::Protocol &protocol, uint port); + void onBackendTestTunnelResult(bool success); + void onBackendUpdateVersionChanged(uint progressPercent, UPDATE_VERSION_STATE state, UPDATE_VERSION_ERROR error); + +private: + Backend *backend_; + IPC::Server *server_ = nullptr; + QVector connections_; + + bool connectivity_; + LOGIN_STATE loginState_; + UPDATE_VERSION_ERROR updateError_; + uint updateProgress_; + QString updateAvailable_; + LOGIN_RET lastLoginError_; + QString lastLoginErrorMessage_; + types::ConnectState connectState_; + types::Protocol protocol_; + uint port_; + TUNNEL_TEST_STATE tunnelTestState_; + + void sendState(); + void sendCommand(const IPC::Command &command); +}; diff --git a/client/base/locations/CMakeLists.txt b/client/base/locations/CMakeLists.txt new file mode 100644 index 000000000..a17ff824f --- /dev/null +++ b/client/base/locations/CMakeLists.txt @@ -0,0 +1,7 @@ +target_sources(base PRIVATE + locationsmodel_manager.cpp + locationsmodel_manager.h + locationsmodel_roles.h +) + +add_subdirectory(model) diff --git a/client/gui/locations/locationsmodel_manager.cpp b/client/base/locations/locationsmodel_manager.cpp similarity index 100% rename from client/gui/locations/locationsmodel_manager.cpp rename to client/base/locations/locationsmodel_manager.cpp diff --git a/client/gui/locations/locationsmodel_manager.h b/client/base/locations/locationsmodel_manager.h similarity index 100% rename from client/gui/locations/locationsmodel_manager.h rename to client/base/locations/locationsmodel_manager.h diff --git a/client/gui/locations/locationsmodel_roles.h b/client/base/locations/locationsmodel_roles.h similarity index 100% rename from client/gui/locations/locationsmodel_roles.h rename to client/base/locations/locationsmodel_roles.h diff --git a/client/gui/locations/model/CMakeLists.txt b/client/base/locations/model/CMakeLists.txt similarity index 82% rename from client/gui/locations/model/CMakeLists.txt rename to client/base/locations/model/CMakeLists.txt index 0c235aeee..363a963dd 100644 --- a/client/gui/locations/model/CMakeLists.txt +++ b/client/base/locations/model/CMakeLists.txt @@ -1,4 +1,4 @@ -target_sources(gui PRIVATE +target_sources(base PRIVATE proxymodels/cities_proxymodel.cpp proxymodels/cities_proxymodel.h proxymodels/customconfigs_proxymodel.cpp @@ -25,8 +25,6 @@ target_sources(gui PRIVATE # unit tests if(DEFINED IS_BUILD_TESTS) - - # ---------------------------- set(TEST_SOURCES locationsmodel.test.cpp locationsmodel.test.h @@ -34,14 +32,11 @@ if(DEFINED IS_BUILD_TESTS) ) add_executable (locationsmodel.test ${TEST_SOURCES}) - target_link_libraries(locationsmodel.test PRIVATE Qt6::Test gui engine common ${OS_SPECIFIC_LIBRARIES}) + target_link_libraries(locationsmodel.test PRIVATE Qt6::Test base engine common ${OS_SPECIFIC_LIBRARIES}) target_include_directories(locationsmodel.test PRIVATE - ${PROJECT_DIRECTORY}/gui + ${PROJECT_DIRECTORY}/base ${PROJECT_DIRECTORY}/common ) - set_target_properties( locationsmodel.test PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}" ) + set_target_properties(locationsmodel.test PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}") endif(DEFINED IS_BUILD_TESTS) - - - diff --git a/client/gui/locations/model/favoritelocationsstorage.cpp b/client/base/locations/model/favoritelocationsstorage.cpp similarity index 100% rename from client/gui/locations/model/favoritelocationsstorage.cpp rename to client/base/locations/model/favoritelocationsstorage.cpp diff --git a/client/gui/locations/model/favoritelocationsstorage.h b/client/base/locations/model/favoritelocationsstorage.h similarity index 100% rename from client/gui/locations/model/favoritelocationsstorage.h rename to client/base/locations/model/favoritelocationsstorage.h diff --git a/client/gui/locations/model/locationitem.cpp b/client/base/locations/model/locationitem.cpp similarity index 100% rename from client/gui/locations/model/locationitem.cpp rename to client/base/locations/model/locationitem.cpp diff --git a/client/gui/locations/model/locationitem.h b/client/base/locations/model/locationitem.h similarity index 100% rename from client/gui/locations/model/locationitem.h rename to client/base/locations/model/locationitem.h diff --git a/client/gui/locations/model/locationsmodel.cpp b/client/base/locations/model/locationsmodel.cpp similarity index 100% rename from client/gui/locations/model/locationsmodel.cpp rename to client/base/locations/model/locationsmodel.cpp diff --git a/client/gui/locations/model/locationsmodel.h b/client/base/locations/model/locationsmodel.h similarity index 100% rename from client/gui/locations/model/locationsmodel.h rename to client/base/locations/model/locationsmodel.h diff --git a/client/gui/locations/model/locationsmodel.test.cpp b/client/base/locations/model/locationsmodel.test.cpp similarity index 100% rename from client/gui/locations/model/locationsmodel.test.cpp rename to client/base/locations/model/locationsmodel.test.cpp diff --git a/client/gui/locations/model/locationsmodel.test.h b/client/base/locations/model/locationsmodel.test.h similarity index 100% rename from client/gui/locations/model/locationsmodel.test.h rename to client/base/locations/model/locationsmodel.test.h diff --git a/client/gui/locations/model/locationsmodel.test.qrc b/client/base/locations/model/locationsmodel.test.qrc similarity index 100% rename from client/gui/locations/model/locationsmodel.test.qrc rename to client/base/locations/model/locationsmodel.test.qrc diff --git a/client/gui/locations/model/locationsmodel_utils.cpp b/client/base/locations/model/locationsmodel_utils.cpp similarity index 100% rename from client/gui/locations/model/locationsmodel_utils.cpp rename to client/base/locations/model/locationsmodel_utils.cpp diff --git a/client/gui/locations/model/locationsmodel_utils.h b/client/base/locations/model/locationsmodel_utils.h similarity index 100% rename from client/gui/locations/model/locationsmodel_utils.h rename to client/base/locations/model/locationsmodel_utils.h diff --git a/client/gui/locations/model/proxymodels/cities_proxymodel.cpp b/client/base/locations/model/proxymodels/cities_proxymodel.cpp similarity index 100% rename from client/gui/locations/model/proxymodels/cities_proxymodel.cpp rename to client/base/locations/model/proxymodels/cities_proxymodel.cpp diff --git a/client/gui/locations/model/proxymodels/cities_proxymodel.h b/client/base/locations/model/proxymodels/cities_proxymodel.h similarity index 100% rename from client/gui/locations/model/proxymodels/cities_proxymodel.h rename to client/base/locations/model/proxymodels/cities_proxymodel.h diff --git a/client/gui/locations/model/proxymodels/customconfigs_proxymodel.cpp b/client/base/locations/model/proxymodels/customconfigs_proxymodel.cpp similarity index 100% rename from client/gui/locations/model/proxymodels/customconfigs_proxymodel.cpp rename to client/base/locations/model/proxymodels/customconfigs_proxymodel.cpp diff --git a/client/gui/locations/model/proxymodels/customconfigs_proxymodel.h b/client/base/locations/model/proxymodels/customconfigs_proxymodel.h similarity index 100% rename from client/gui/locations/model/proxymodels/customconfigs_proxymodel.h rename to client/base/locations/model/proxymodels/customconfigs_proxymodel.h diff --git a/client/gui/locations/model/proxymodels/favoritecities_proxymodel.cpp b/client/base/locations/model/proxymodels/favoritecities_proxymodel.cpp similarity index 100% rename from client/gui/locations/model/proxymodels/favoritecities_proxymodel.cpp rename to client/base/locations/model/proxymodels/favoritecities_proxymodel.cpp diff --git a/client/gui/locations/model/proxymodels/favoritecities_proxymodel.h b/client/base/locations/model/proxymodels/favoritecities_proxymodel.h similarity index 100% rename from client/gui/locations/model/proxymodels/favoritecities_proxymodel.h rename to client/base/locations/model/proxymodels/favoritecities_proxymodel.h diff --git a/client/gui/locations/model/proxymodels/sortedcities_proxymodel.cpp b/client/base/locations/model/proxymodels/sortedcities_proxymodel.cpp similarity index 100% rename from client/gui/locations/model/proxymodels/sortedcities_proxymodel.cpp rename to client/base/locations/model/proxymodels/sortedcities_proxymodel.cpp diff --git a/client/gui/locations/model/proxymodels/sortedcities_proxymodel.h b/client/base/locations/model/proxymodels/sortedcities_proxymodel.h similarity index 100% rename from client/gui/locations/model/proxymodels/sortedcities_proxymodel.h rename to client/base/locations/model/proxymodels/sortedcities_proxymodel.h diff --git a/client/gui/locations/model/proxymodels/sortedlocations_proxymodel.cpp b/client/base/locations/model/proxymodels/sortedlocations_proxymodel.cpp similarity index 100% rename from client/gui/locations/model/proxymodels/sortedlocations_proxymodel.cpp rename to client/base/locations/model/proxymodels/sortedlocations_proxymodel.cpp diff --git a/client/gui/locations/model/proxymodels/sortedlocations_proxymodel.h b/client/base/locations/model/proxymodels/sortedlocations_proxymodel.h similarity index 100% rename from client/gui/locations/model/proxymodels/sortedlocations_proxymodel.h rename to client/base/locations/model/proxymodels/sortedlocations_proxymodel.h diff --git a/client/gui/locations/model/proxymodels/staticips_proxymodel.cpp b/client/base/locations/model/proxymodels/staticips_proxymodel.cpp similarity index 100% rename from client/gui/locations/model/proxymodels/staticips_proxymodel.cpp rename to client/base/locations/model/proxymodels/staticips_proxymodel.cpp diff --git a/client/gui/locations/model/proxymodels/staticips_proxymodel.h b/client/base/locations/model/proxymodels/staticips_proxymodel.h similarity index 100% rename from client/gui/locations/model/proxymodels/staticips_proxymodel.h rename to client/base/locations/model/proxymodels/staticips_proxymodel.h diff --git a/client/gui/locations/model/selectedlocation.cpp b/client/base/locations/model/selectedlocation.cpp similarity index 100% rename from client/gui/locations/model/selectedlocation.cpp rename to client/base/locations/model/selectedlocation.cpp diff --git a/client/gui/locations/model/selectedlocation.h b/client/base/locations/model/selectedlocation.h similarity index 100% rename from client/gui/locations/model/selectedlocation.h rename to client/base/locations/model/selectedlocation.h diff --git a/client/gui/locations/model/selectedlocationwatcher.test.cpp b/client/base/locations/model/selectedlocationwatcher.test.cpp similarity index 100% rename from client/gui/locations/model/selectedlocationwatcher.test.cpp rename to client/base/locations/model/selectedlocationwatcher.test.cpp diff --git a/client/gui/multipleaccountdetection/CMakeLists.txt b/client/base/multipleaccountdetection/CMakeLists.txt similarity index 81% rename from client/gui/multipleaccountdetection/CMakeLists.txt rename to client/base/multipleaccountdetection/CMakeLists.txt index fbe7e8227..630169822 100644 --- a/client/gui/multipleaccountdetection/CMakeLists.txt +++ b/client/base/multipleaccountdetection/CMakeLists.txt @@ -1,18 +1,18 @@ -target_sources(gui PRIVATE +target_sources(base PRIVATE imultipleaccountdetection.h multipleaccountdetectionfactory.cpp multipleaccountdetectionfactory.h ) if (WIN32) - target_sources(gui PRIVATE + target_sources(base PRIVATE multipleaccountdetection_win.cpp multipleaccountdetection_win.h secretvalue_win.cpp secretvalue_win.h ) elseif(UNIX) #both Mac and Linux - target_sources(gui PRIVATE + target_sources(base PRIVATE multipleaccountdetection_posix.cpp multipleaccountdetection_posix.h ) diff --git a/client/gui/multipleaccountdetection/imultipleaccountdetection.h b/client/base/multipleaccountdetection/imultipleaccountdetection.h similarity index 100% rename from client/gui/multipleaccountdetection/imultipleaccountdetection.h rename to client/base/multipleaccountdetection/imultipleaccountdetection.h diff --git a/client/gui/multipleaccountdetection/multipleaccountdetection_posix.cpp b/client/base/multipleaccountdetection/multipleaccountdetection_posix.cpp similarity index 100% rename from client/gui/multipleaccountdetection/multipleaccountdetection_posix.cpp rename to client/base/multipleaccountdetection/multipleaccountdetection_posix.cpp diff --git a/client/gui/multipleaccountdetection/multipleaccountdetection_posix.h b/client/base/multipleaccountdetection/multipleaccountdetection_posix.h similarity index 100% rename from client/gui/multipleaccountdetection/multipleaccountdetection_posix.h rename to client/base/multipleaccountdetection/multipleaccountdetection_posix.h diff --git a/client/gui/multipleaccountdetection/multipleaccountdetection_win.cpp b/client/base/multipleaccountdetection/multipleaccountdetection_win.cpp similarity index 100% rename from client/gui/multipleaccountdetection/multipleaccountdetection_win.cpp rename to client/base/multipleaccountdetection/multipleaccountdetection_win.cpp diff --git a/client/gui/multipleaccountdetection/multipleaccountdetection_win.h b/client/base/multipleaccountdetection/multipleaccountdetection_win.h similarity index 100% rename from client/gui/multipleaccountdetection/multipleaccountdetection_win.h rename to client/base/multipleaccountdetection/multipleaccountdetection_win.h diff --git a/client/gui/multipleaccountdetection/multipleaccountdetectionfactory.cpp b/client/base/multipleaccountdetection/multipleaccountdetectionfactory.cpp similarity index 100% rename from client/gui/multipleaccountdetection/multipleaccountdetectionfactory.cpp rename to client/base/multipleaccountdetection/multipleaccountdetectionfactory.cpp diff --git a/client/gui/multipleaccountdetection/multipleaccountdetectionfactory.h b/client/base/multipleaccountdetection/multipleaccountdetectionfactory.h similarity index 100% rename from client/gui/multipleaccountdetection/multipleaccountdetectionfactory.h rename to client/base/multipleaccountdetection/multipleaccountdetectionfactory.h diff --git a/client/gui/multipleaccountdetection/secretvalue_win.cpp b/client/base/multipleaccountdetection/secretvalue_win.cpp similarity index 100% rename from client/gui/multipleaccountdetection/secretvalue_win.cpp rename to client/base/multipleaccountdetection/secretvalue_win.cpp diff --git a/client/gui/multipleaccountdetection/secretvalue_win.h b/client/base/multipleaccountdetection/secretvalue_win.h similarity index 100% rename from client/gui/multipleaccountdetection/secretvalue_win.h rename to client/base/multipleaccountdetection/secretvalue_win.h diff --git a/client/base/utils/CMakeLists.txt b/client/base/utils/CMakeLists.txt new file mode 100644 index 000000000..f05afd75b --- /dev/null +++ b/client/base/utils/CMakeLists.txt @@ -0,0 +1,23 @@ +target_sources(base PRIVATE + authcheckerfactory.h + iauthchecker.h + writeaccessrightschecker.cpp + writeaccessrightschecker.h +) + +if (WIN32) + target_sources(base PRIVATE + authchecker_win.cpp + authchecker_win.h + ) +elseif(APPLE) + target_sources(base PRIVATE + authchecker_mac.h + authchecker_mac.mm + ) +elseif(UNIX) + target_sources(base PRIVATE + authchecker_linux.cpp + authchecker_linux.h + ) +endif() diff --git a/client/gui/utils/authchecker_linux.cpp b/client/base/utils/authchecker_linux.cpp similarity index 100% rename from client/gui/utils/authchecker_linux.cpp rename to client/base/utils/authchecker_linux.cpp diff --git a/client/gui/utils/authchecker_linux.h b/client/base/utils/authchecker_linux.h similarity index 100% rename from client/gui/utils/authchecker_linux.h rename to client/base/utils/authchecker_linux.h diff --git a/client/gui/utils/authchecker_mac.h b/client/base/utils/authchecker_mac.h similarity index 100% rename from client/gui/utils/authchecker_mac.h rename to client/base/utils/authchecker_mac.h diff --git a/client/gui/utils/authchecker_mac.mm b/client/base/utils/authchecker_mac.mm similarity index 100% rename from client/gui/utils/authchecker_mac.mm rename to client/base/utils/authchecker_mac.mm diff --git a/client/gui/utils/authchecker_win.cpp b/client/base/utils/authchecker_win.cpp similarity index 100% rename from client/gui/utils/authchecker_win.cpp rename to client/base/utils/authchecker_win.cpp diff --git a/client/gui/utils/authchecker_win.h b/client/base/utils/authchecker_win.h similarity index 100% rename from client/gui/utils/authchecker_win.h rename to client/base/utils/authchecker_win.h diff --git a/client/gui/utils/authcheckerfactory.h b/client/base/utils/authcheckerfactory.h similarity index 100% rename from client/gui/utils/authcheckerfactory.h rename to client/base/utils/authcheckerfactory.h diff --git a/client/gui/utils/iauthchecker.h b/client/base/utils/iauthchecker.h similarity index 100% rename from client/gui/utils/iauthchecker.h rename to client/base/utils/iauthchecker.h diff --git a/client/gui/utils/writeaccessrightschecker.cpp b/client/base/utils/writeaccessrightschecker.cpp similarity index 100% rename from client/gui/utils/writeaccessrightschecker.cpp rename to client/base/utils/writeaccessrightschecker.cpp diff --git a/client/gui/utils/writeaccessrightschecker.h b/client/base/utils/writeaccessrightschecker.h similarity index 100% rename from client/gui/utils/writeaccessrightschecker.h rename to client/base/utils/writeaccessrightschecker.h diff --git a/client/cli/CMakeLists.txt b/client/cli/CMakeLists.txt new file mode 100644 index 000000000..11fab0b83 --- /dev/null +++ b/client/cli/CMakeLists.txt @@ -0,0 +1,30 @@ +cmake_minimum_required(VERSION 3.23) + +project(cli) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +find_package(Qt6 REQUIRED COMPONENTS Core5Compat Network) + +set(PROJECT_SOURCES + mainservice.cpp +) + +add_library(cli STATIC ${PROJECT_SOURCES}) + +target_link_libraries(cli PRIVATE Qt6::Core Qt6::Core5Compat Qt6::Network wsnet::wsnet Boost::serialization) + +target_include_directories(cli PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/../engine + ${CMAKE_CURRENT_SOURCE_DIR}/../common + ${CMAKE_CURRENT_SOURCE_DIR}/../base +) + +add_subdirectory(application) diff --git a/client/cli/application/CMakeLists.txt b/client/cli/application/CMakeLists.txt new file mode 100644 index 000000000..95aa74824 --- /dev/null +++ b/client/cli/application/CMakeLists.txt @@ -0,0 +1,4 @@ +target_sources(cli PRIVATE + singleappinstance.cpp + windscribeapplication.cpp +) diff --git a/client/cli/application/singleappinstance.cpp b/client/cli/application/singleappinstance.cpp new file mode 100644 index 000000000..02612d5a7 --- /dev/null +++ b/client/cli/application/singleappinstance.cpp @@ -0,0 +1,56 @@ +#include "singleappinstance.h" + +#if defined(Q_OS_LINUX) +#include +#endif + +#include +#include +#include +#include "utils/logger.h" + +namespace windscribe { + +bool SingleAppInstance::isRunning() +{ +#if defined(Q_OS_LINUX) + if (lockFile_.isNull()) { + lockFile_.reset(new QLockFile("/var/run/windscribe/windscribe.lock")); + lockFile_->setStaleLockTime(0); + lockFile_->tryLock(); + + localServer_.setSocketOptions(QLocalServer::UserAccessOption); + + if (lockFile_->error() == QLockFile::LockFailedError) { + return true; + } + + if (lockFile_->error() != QLockFile::NoError) { + qCDebug(LOG_BASIC) << "SingleAppInstance could not create the lock file. A new instance will be launched."; + } + } +#endif + return false; +} + +void SingleAppInstance::release() +{ +#if defined(Q_OS_LINUX) + localServer_.close(); + if (!lockFile_.isNull()) { + lockFile_->unlock(); + } +#endif +} + +SingleAppInstance::SingleAppInstance() +{ +} + +SingleAppInstance::~SingleAppInstance() +{ + release(); +} + +} + diff --git a/client/cli/application/singleappinstance.h b/client/cli/application/singleappinstance.h new file mode 100644 index 000000000..d66280f79 --- /dev/null +++ b/client/cli/application/singleappinstance.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include +#include + +namespace windscribe { + +class SingleAppInstance +{ +public: + explicit SingleAppInstance(); + ~SingleAppInstance(); + + bool isRunning(); + void release(); + +private: +#if defined(Q_OS_LINUX) + QScopedPointer lockFile_; + QLocalServer localServer_; +#endif +}; + +} diff --git a/client/cli/application/windscribeapplication.cpp b/client/cli/application/windscribeapplication.cpp new file mode 100644 index 000000000..195157b49 --- /dev/null +++ b/client/cli/application/windscribeapplication.cpp @@ -0,0 +1,41 @@ +#include "windscribeapplication.h" + +#include +#include + +#include "utils/logger.h" + +WindscribeApplication::WindscribeApplication(int &argc, char **argv) : QCoreApplication(argc, argv) +{ +} + +WindscribeApplication::~WindscribeApplication() +{ +} + +void WindscribeApplication::setService(MainService *service) +{ + service_ = service; +} + +void WindscribeApplication::setSigTermHandler(int fd) +{ + fd_ = fd; + + socketNotifier_ = new QSocketNotifier(fd_, QSocketNotifier::Read, this); + connect(socketNotifier_, &QSocketNotifier::activated, this, &WindscribeApplication::onSigTerm); +} + +void WindscribeApplication::onSigTerm() +{ + socketNotifier_->setEnabled(false); + char tmp; + if (::read(fd_, &tmp, sizeof(tmp)) < 0) { + qCDebug(LOG_BASIC) << "Could not read from signal socket"; + return; + } + + service_->stop(); + exit(0); +} + diff --git a/client/cli/application/windscribeapplication.h b/client/cli/application/windscribeapplication.h new file mode 100644 index 000000000..97820f225 --- /dev/null +++ b/client/cli/application/windscribeapplication.h @@ -0,0 +1,30 @@ +#pragma once + +#include +#include + +#include "../mainservice.h" + +class WindscribeApplication : public QCoreApplication +{ + Q_OBJECT +public: + explicit WindscribeApplication(int &argc, char **argv); + ~WindscribeApplication(); + + static WindscribeApplication *instance() { + return static_cast(qApp->instance()); + } + + void setService(MainService *service); + void setSigTermHandler(int fd); + +private slots: + void onSigTerm(); + +private: + MainService *service_; + + QSocketNotifier *socketNotifier_; + int fd_; +}; diff --git a/client/cli/mainservice.cpp b/client/cli/mainservice.cpp new file mode 100644 index 000000000..c8c3c46be --- /dev/null +++ b/client/cli/mainservice.cpp @@ -0,0 +1,265 @@ +#include "mainservice.h" + +#include +#include +#include + +#include "backend/persistentstate.h" +#include "launchonstartup/launchonstartup.h" +#include "locations/locationsmodel_roles.h" +#include "multipleaccountdetection/multipleaccountdetectionfactory.h" +#include "utils/logger.h" + +MainService::MainService() : QObject(), isExitingAfterUpdate_(false) +{ + backend_ = new Backend(this); + connect(backend_, &Backend::checkUpdateChanged, this, &MainService::onBackendCheckUpdateChanged); + connect(backend_, &Backend::initFinished, this, &MainService::onBackendInitFinished); + connect(backend_, &Backend::sessionStatusChanged, this, &MainService::onBackendSessionStatusChanged); + connect(backend_, &Backend::updateVersionChanged, this, &MainService::onBackendUpdateVersionChanged); + connect(backend_, &Backend::wireGuardAtKeyLimit, this, &MainService::onBackendWireGuardAtKeyLimit); + backend_->init(); + + // signals from here to Backend + connect(this, &MainService::wireGuardKeyLimitUserResponse, backend_, &Backend::wireGuardKeyLimitUserResponse); + + // signals from Preferences + connect(backend_->getPreferences(), &Preferences::firewallSettingsChanged, this, &MainService::onPreferencesFirewallSettingsChanged); + connect(backend_->getPreferences(), &Preferences::shareProxyGatewayChanged, this, &MainService::onPreferencesShareProxyGatewayChanged); + connect(backend_->getPreferences(), &Preferences::splitTunnelingChanged, this, &MainService::onPreferencesSplitTunnelingChanged); + connect(backend_->getPreferences(), &Preferences::isAllowLanTrafficChanged, this, &MainService::onPreferencesAllowLanTrafficChanged); + connect(backend_->getPreferences(), &Preferences::isLaunchOnStartupChanged, this, &MainService::onPreferencesLaunchOnStartupChanged); + + multipleAccountDetection_ = QSharedPointer(MultipleAccountDetectionFactory::create()); + + localIpcServer_ = new LocalIPCServer(backend_, this); + connect(localIpcServer_, &LocalIPCServer::showLocations, this, &MainService::onShowLocations); + connect(localIpcServer_, &LocalIPCServer::connectToLocation, this, &MainService::onConnectToLocation); + connect(localIpcServer_, &LocalIPCServer::attemptLogin, this, &MainService::onLogin); +} + +MainService::~MainService() +{ +} + +void MainService::onBackendInitFinished(INIT_STATE initState) +{ + if (initState != INIT_STATE_SUCCESS) { + qCDebug(LOG_BASIC) << "Could not initialize helper"; + qApp->exit(); + } + + // Set initial state + { + setInitialFirewallState(); + Preferences *p = backend_->getPreferences(); + p->validateAndUpdateIfNeeded(); + + backend_->sendSplitTunneling(p->splitTunneling()); + } + + localIpcServer_->start(); + + QString autoLoginUsername; + QString autoLoginPassword; + + if (backend_->haveAutoLoginCredentials(autoLoginUsername, autoLoginPassword)) { + onLogin(autoLoginUsername, autoLoginPassword, QString()); + } else if (backend_->isCanLoginWithAuthHash()) { + backend_->loginWithAuthHash(); + } +} + +void MainService::setInitialFirewallState() +{ + bool bFirewallStateOn = PersistentState::instance().isFirewallOn(); + qCDebug(LOG_BASIC) << "Firewall state from last app start:" << bFirewallStateOn; + + if (bFirewallStateOn) { + backend_->firewallOn(); + } else { + if (backend_->getPreferences()->firewallSettings().mode == FIREWALL_MODE_ALWAYS_ON) { + backend_->firewallOn(); + } else { + backend_->firewallOff(); + } + } +} + +void MainService::onConnectToLocation(const LocationID &lid, const types::Protocol &protocol) +{ + if (!lid.isValid()) { + qCDebug(LOG_USER) << "Invalid location"; + return; + } + + qCDebug(LOG_USER) << "Location selected:" << lid.getHashString(); + PersistentState::instance().setLastLocation(lid); + + uint port = 0; + if (protocol.isValid()) { + // determine default port for protocol + QVector ports = backend_->getPreferencesHelper()->getAvailablePortsForProtocol(protocol); + if (ports.size() == 0) { + qCDebug(LOG_BASIC) << "Could not determine port for protocol" << protocol.toLongString(); + } else { + port = ports[0]; + } + } + + if (port != 0) { + backend_->sendConnect(lid, types::ConnectionSettings(protocol, port, false)); + } else { + backend_->sendConnect(lid); + } +} + +void MainService::onLogin(const QString &username, const QString &password, const QString &code2fa) +{ + backend_->login(username, password, code2fa); +} + +void MainService::onShowLocations() +{ + QStringList locations; + + for (int i = 0; i < backend_->locationsModelManager()->sortedLocationsProxyModel()->rowCount(); i++) { + QModelIndex miCountry = backend_->locationsModelManager()->sortedLocationsProxyModel()->index(i, 0); + LocationID lid = qvariant_cast(miCountry.data(gui_locations::kLocationId)); + QString countryName = miCountry.data(gui_locations::kName).toString(); + + if (lid.isBestLocation()) { + locations << countryName + " - " + lid.city(); + continue; + } + + for (int j = 0; j < miCountry.model()->rowCount(miCountry); j++) { + QModelIndex miCity = miCountry.model()->index(j, 0, miCountry); + locations << countryName + " - " + miCity.data(gui_locations::kName).toString() + " - " + miCity.data(gui_locations::kNick).toString(); + } + } + + localIpcServer_->sendLocations(locations); +} + +void MainService::onBackendCheckUpdateChanged(const api_responses::CheckUpdate &info) +{ + if (info.isAvailable() && !info.isSupported()) { + blockConnect_.setNeedUpgrade(); + } +} + +void MainService::onBackendSessionStatusChanged(const api_responses::SessionStatus &sessionStatus) +{ + blockConnect_.setNotBlocking(); + qint32 status = sessionStatus.getStatus(); + + // multiple account abuse detection + QString entryUsername; + bool bEntryIsPresent = multipleAccountDetection_->entryIsPresent(entryUsername); + if (bEntryIsPresent && (!sessionStatus.isPremium()) && sessionStatus.getAlc().size() == 0 && sessionStatus.getStatus() == 1 && entryUsername != sessionStatus.getUsername()) { + status = 2; + blockConnect_.setBlockedMultiAccount(entryUsername); + } else if (bEntryIsPresent && entryUsername == sessionStatus.getUsername() && sessionStatus.getStatus() == 1) { + multipleAccountDetection_->removeEntry(); + } + + // free account + if (!sessionStatus.isPremium()) { + if (status == 2) { + // write entry into registry expired_user = username + multipleAccountDetection_->userBecomeExpired(sessionStatus.getUsername()); + + if (backend_->currentConnectState() == CONNECT_STATE_CONNECTED || backend_->currentConnectState() == CONNECT_STATE_CONNECTING) { + backend_->sendDisconnect(); + } + if (!blockConnect_.isBlocked()) { + blockConnect_.setBlockedExceedTraffic(); + } + } + } + + if (status == 3) { + blockConnect_.setBlockedBannedUser(); + if (backend_->currentConnectState() == CONNECT_STATE_CONNECTED || backend_->currentConnectState() == CONNECT_STATE_CONNECTING) { + backend_->sendDisconnect(); + } + } + backend_->setBlockConnect(blockConnect_.isBlocked()); +} + +void MainService::onPreferencesLaunchOnStartupChanged(bool enabled) +{ + LaunchOnStartup::instance().setLaunchOnStartup(enabled); +} + +void MainService::stop() +{ + backend_->cleanup(true, PersistentState::instance().isFirewallOn(), + backend_->getPreferences()->firewallSettings().mode == FIREWALL_MODE_ALWAYS_ON || isExitingAfterUpdate_, + backend_->getPreferences()->isLaunchOnStartup()); + + // Backend handles setting firewall state after app closes + // This block handles initializing the firewall state on next run + if (PersistentState::instance().isFirewallOn() && + backend_->getPreferences()->firewallSettings().mode == FIREWALL_MODE_AUTOMATIC) + { + if (!backend_->getPreferences()->isLaunchOnStartup() || !backend_->getPreferences()->isAutoConnect()) { + qCDebug(LOG_BASIC) << "Setting firewall persistence to false"; + PersistentState::instance().setFirewallState(false); + } + } else if (backend_->getPreferences()->firewallSettings().mode == FIREWALL_MODE_ALWAYS_ON) { + PersistentState::instance().setFirewallState(true); + } + + PersistentState::instance().save(); + backend_->locationsModelManager()->saveFavoriteLocations(); + + // Wait for backend to complete its operations before returning + while (!backend_->isAppCanClose()) { + QThread::msleep(1); + qApp->processEvents(QEventLoop::ExcludeUserInputEvents); + } +} + +void MainService::onBackendUpdateVersionChanged(uint progressPercent, UPDATE_VERSION_STATE state, UPDATE_VERSION_ERROR error) +{ + if (state == UPDATE_VERSION_STATE_DONE && error == UPDATE_VERSION_ERROR_NO_ERROR) { + isExitingAfterUpdate_ = true; + } +} + +void MainService::onBackendWireGuardAtKeyLimit() +{ + emit wireGuardKeyLimitUserResponse(true); +} + +void MainService::onPreferencesSplitTunnelingChanged(const types::SplitTunneling &st) +{ + backend_->sendSplitTunneling(st); +} + +void MainService::onPreferencesFirewallSettingsChanged(const types::FirewallSettings &fm) +{ + if (fm.mode == FIREWALL_MODE_ALWAYS_ON) { + if (!PersistentState::instance().isFirewallOn()) { + backend_->firewallOn(); + } + } +} + +void MainService::onPreferencesShareProxyGatewayChanged(const types::ShareProxyGateway &sp) +{ + if (sp.isEnabled) { + backend_->startProxySharing((PROXY_SHARING_TYPE)sp.proxySharingMode, sp.port); + } else { + backend_->stopProxySharing(); + } +} + +void MainService::onPreferencesAllowLanTrafficChanged(bool /*allowLanTraffic*/) +{ + // Changing Allow Lan Traffic may affect split tunnel behavior + onPreferencesSplitTunnelingChanged(backend_->getPreferences()->splitTunneling()); +} + diff --git a/client/cli/mainservice.h b/client/cli/mainservice.h new file mode 100644 index 000000000..9ac6f5455 --- /dev/null +++ b/client/cli/mainservice.h @@ -0,0 +1,52 @@ +#pragma once + +#include +#include "backend/backend.h" +#include "blockconnect.h" +#include "localipcserver/localipcserver.h" +#include "multipleaccountdetection/imultipleaccountdetection.h" +#include "types/enums.h" +#include "types/protocol.h" + +class MainService : public QObject +{ + Q_OBJECT + +public: + MainService(); + ~MainService(); + + void stop(); + +signals: + void wireGuardKeyLimitUserResponse(bool deleteOldestKey); + +private slots: + void onBackendCheckUpdateChanged(const api_responses::CheckUpdate &info); + void onBackendInitFinished(INIT_STATE initState); + void onBackendSessionStatusChanged(const api_responses::SessionStatus &sessionStatus); + void onBackendUpdateVersionChanged(uint progressPercent, UPDATE_VERSION_STATE state, UPDATE_VERSION_ERROR error); + void onBackendWireGuardAtKeyLimit(); + + void onPreferencesAllowLanTrafficChanged(bool allowLanTraffic); + void onPreferencesFirewallSettingsChanged(const types::FirewallSettings &fm); + void onPreferencesLaunchOnStartupChanged(bool enabled); + void onPreferencesShareProxyGatewayChanged(const types::ShareProxyGateway &sp); + void onPreferencesSplitTunnelingChanged(const types::SplitTunneling &st); + + // From CLI commands + void onConnectToLocation(const LocationID &id, const types::Protocol &protocol); + void onLogin(const QString &username, const QString &password, const QString &code2fa); + void onShowLocations(); + +private: + Backend *backend_; + LocalIPCServer *localIpcServer_; + + BlockConnect blockConnect_; + QSharedPointer multipleAccountDetection_; + + bool isExitingAfterUpdate_; + + void setInitialFirewallState(); +}; diff --git a/client/common/CMakeLists.txt b/client/common/CMakeLists.txt index d6cb4e13c..0c691558f 100644 --- a/client/common/CMakeLists.txt +++ b/client/common/CMakeLists.txt @@ -11,7 +11,7 @@ set(CMAKE_AUTORCC ON) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) -find_package(Qt6 REQUIRED COMPONENTS Widgets Network Core5Compat) +find_package(Qt6 REQUIRED COMPONENTS Network Core5Compat) set(PROJECT_SOURCES names.h @@ -22,16 +22,16 @@ set(PROJECT_SOURCES add_library(common STATIC ${PROJECT_SOURCES}) -target_link_libraries(common PRIVATE Qt6::Core Qt6::Network Qt6::Widgets Qt6::Core5Compat OpenSSL::Crypto) +target_link_libraries(common PRIVATE Qt6::Core Qt6::Network Qt6::Core5Compat OpenSSL::Crypto) target_compile_definitions(common PRIVATE CMAKE_LIBRARY_LIBRARY WINVER=0x0601 _WIN32_WINNT=0x0601 WIN32_LEAN_AND_MEAN PIO_APC_ROUTINE_DEFINED) +add_subdirectory(api_responses) add_subdirectory(ipc) add_subdirectory(legacy_protobuf_support) -add_subdirectory(api_responses) add_subdirectory(types) add_subdirectory(utils) diff --git a/client/common/archive/archive.cpp b/client/common/archive/archive.cpp deleted file mode 100644 index 63bc2e086..000000000 --- a/client/common/archive/archive.cpp +++ /dev/null @@ -1,1023 +0,0 @@ -#include "archive.h" - -#ifndef _WIN32 -#include -#include -#endif - -#include <7zAlloc.h> -#include <7zCrc.h> -#include <7zTypes.h> - -static const ISzAlloc g_Alloc = { SzAlloc, SzFree }; - -#ifdef _WIN32 -#ifndef USE_WINDOWS_FILE -static UINT g_FileCodePage = CP_ACP; -#endif -#endif - -using namespace std; - -#ifdef _WIN32 -Archive::Archive(const wstring& name) -#else -Archive::Archive(const wstring &name) : file_size(0), pData(NULL) -#endif -{ - importantTotalUnpacked=0; - Total=0; - Completed=0; - res=SZ_OK; - -#ifdef _WIN32 - LPCWSTR name1 = name.c_str(); - HRSRC hResource = FindResource(nullptr,name1, RT_RCDATA); - hGlobal = LoadResource(nullptr, hResource); - pData = static_cast(LockResource(hGlobal)); - file_size = SizeofResource(nullptr, hResource); -#else -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - using convert_type = std::codecvt_utf8; - std::wstring_convert converter; - std::string filename = converter.to_bytes( name ); -#pragma clang diagnostic pop - - FILE *file = fopen(filename.c_str(), "rb"); - if (file) - { - // obtain file size: - fseek (file , 0 , SEEK_END); - file_size = ftell (file); - rewind (file); - - pData = (unsigned char*)malloc(sizeof(char)*file_size); - - size_t readedCount = fread(pData, 1, file_size, file); - if (readedCount != file_size) - { - free(pData); - pData = NULL; - file_size = 0; - } - fclose(file); - } - if (!isCorrect()) - { - return; - } - #endif - - SzArEx_Init(&db); - -#if defined(_WIN32) && !defined(USE_WINDOWS_FILE) && !defined(UNDER_CE) - g_FileCodePage = AreFileApisANSI() ? CP_ACP : CP_OEMCP; -#endif - - allocImp = g_Alloc; - allocTempImp = g_Alloc; - - //We work with the file as with an array of bytes - archiveStream.file.pData = pData; - archiveStream.file.Position = 0; - archiveStream.file.Length = file_size; - - FileInStream_CreateVTable1(&archiveStream); - LookToRead2_CreateVTable(&lookStream, False); - lookStream.buf = nullptr; - lookStream.buf = static_cast(ISzAlloc_Alloc(&allocImp, kInputBufSize)); - - if (!lookStream.buf) - { - res = SZ_ERROR_MEM; - } - else - { - lookStream.bufSize = kInputBufSize; - lookStream.realStream = &archiveStream.vt; - LookToRead2_INIT(&lookStream); - } - - CrcGenerateTable(); - if (res == SZ_OK) - { - res = SzArEx_Open(&db, &lookStream.vt, &allocImp, &allocTempImp); - } - - temp = nullptr; - tempSize = 0; - blockIndex = 0xFFFFFFFF; - outBuffer = nullptr; - outBufferSize = 0; - val = 0; - max_percent = 90; -} - -Archive::~Archive() -{ - if (lookStream.buf != nullptr) { - ISzAlloc_Free(&allocImp, lookStream.buf); - } - - if (temp != nullptr) { - SzFree(nullptr, temp); - } - -#ifndef _WIN32 - if (pData) { - free(pData); - } -#endif - - SzArEx_Free(&db, &allocImp); - -#ifdef _WIN32 - FreeResource(hGlobal); // done with data -#endif -} - -void Archive::setLogFunction(const std::function &logFunc) -{ - if (logFunc) - logFunction = logFunc; -} - -#ifndef _WIN32 -bool Archive::isCorrect() const -{ - return pData != NULL && file_size != 0; -} -#endif - -void Archive::Print(const char *s) -{ -#ifndef GUI -#ifdef _WIN32 - fputs(s, stdout); -#endif // _WIN32 -#else - if (logFunction) - logFunction(s); -#endif -} - - -int Archive::Buf_EnsureSize(CBuf *dest, size_t size) -{ - if (dest->size >= size) - return 1; - Buf_Free(dest, &g_Alloc); - return Buf_Create(dest, size, &g_Alloc); -} - -#ifndef _WIN32 -#define _USE_UTF8 -#endif - -/* #define _USE_UTF8 */ - -#ifdef _USE_UTF8 - -#define _UTF8_START(n) (0x100 - (1 << (7 - (n)))) - -#define _UTF8_RANGE(n) (((UInt32)1) << ((n) * 5 + 6)) - -#define _UTF8_HEAD(n, val) ((Byte)(_UTF8_START(n) + (val >> (6 * (n))))) -#define _UTF8_CHAR(n, val) ((Byte)(0x80 + (((val) >> (6 * (n))) & 0x3F))) - -size_t Archive::Utf16_To_Utf8_Calc(const UInt16 *src, const UInt16 *srcLim) -{ - size_t size = 0; - for (;;) - { - UInt32 val; - if (src == srcLim) - return size; - - size++; - val = *src++; - - if (val < 0x80) - continue; - - if (val < _UTF8_RANGE(1)) - { - size++; - continue; - } - - if (val >= 0xD800 && val < 0xDC00 && src != srcLim) - { - UInt32 c2 = *src; - if (c2 >= 0xDC00 && c2 < 0xE000) - { - src++; - size += 3; - continue; - } - } - - size += 2; - } -} - -Byte *Archive::Utf16_To_Utf8(Byte *dest, const UInt16 *src, const UInt16 *srcLim) -{ - for (;;) - { - UInt32 val; - if (src == srcLim) - return dest; - - val = *src++; - - if (val < 0x80) - { - *dest++ = (char)val; - continue; - } - - if (val < _UTF8_RANGE(1)) - { - dest[0] = _UTF8_HEAD(1, val); - dest[1] = _UTF8_CHAR(0, val); - dest += 2; - continue; - } - - if (val >= 0xD800 && val < 0xDC00 && src != srcLim) - { - UInt32 c2 = *src; - if (c2 >= 0xDC00 && c2 < 0xE000) - { - src++; - val = (((val - 0xD800) << 10) | (c2 - 0xDC00)) + 0x10000; - dest[0] = _UTF8_HEAD(3, val); - dest[1] = _UTF8_CHAR(2, val); - dest[2] = _UTF8_CHAR(1, val); - dest[3] = _UTF8_CHAR(0, val); - dest += 4; - continue; - } - } - - dest[0] = _UTF8_HEAD(2, val); - dest[1] = _UTF8_CHAR(1, val); - dest[2] = _UTF8_CHAR(0, val); - dest += 3; - } -} - -SRes Archive::Utf16_To_Utf8Buf(CBuf *dest, const UInt16 *src, size_t srcLen) -{ - size_t destLen = Utf16_To_Utf8_Calc(src, src + srcLen); - destLen += 1; - if (!Buf_EnsureSize(dest, destLen)) - return SZ_ERROR_MEM; - *Utf16_To_Utf8(dest->data, src, src + srcLen) = 0; - return SZ_OK; -} - -#endif - -SRes Archive::Utf16_To_Char(CBuf *buf, const UInt16 *s -#ifndef _USE_UTF8 - , UINT codePage -#endif -) -{ - unsigned len = 0; - for (len = 0; s[len] != 0; len++) - { - - } - -#ifndef _USE_UTF8 - { - unsigned size = len * 3 + 100; - if (!Buf_EnsureSize(buf, size)) - return SZ_ERROR_MEM; - { - buf->data[0] = 0; - if (len != 0) - { - char defaultChar = '_'; - BOOL defUsed; - unsigned numChars = 0; - numChars = static_cast(WideCharToMultiByte(codePage, 0, reinterpret_cast(s), static_cast(len), reinterpret_cast(buf->data), static_cast(size), &defaultChar, &defUsed)); - if (numChars == 0 || numChars >= size) - return SZ_ERROR_FAIL; - buf->data[numChars] = 0; - } - return SZ_OK; - } - } -#else - return Utf16_To_Utf8Buf(buf, s, len); -#endif -} - - - -WRes Archive::MyCreateDir(const UInt16 *name) -{ -#ifdef USE_WINDOWS_FILE - - return CreateDirectoryW((LPCWSTR)name, nullptr) ? 0 : GetLastError(); - -#else - CBuf buf; - WRes res; - Buf_Init(&buf); - - SRes __result__ = Utf16_To_Char(&buf, name); - if (__result__ != 0) - { - return static_cast(__result__); - } - - res = static_cast( -#ifdef _WIN32 - _mkdir(reinterpret_cast(buf.data)) -#else - mkdir(reinterpret_cast(buf.data), 0777) -#endif - == 0 ? 0 : errno); - - Buf_Free(&buf, &g_Alloc); - - return res; - -#endif -} - -WRes Archive::OutFile_OpenUtf16(CSzFile *p, const UInt16 *name) -{ -#ifdef USE_WINDOWS_FILE - return OutFile_OpenW(p, (LPCWSTR)name); -#else - CBuf buf; - WRes res; - Buf_Init(&buf); - - SRes __result__ = Utf16_To_Char(&buf, name MY_FILE_CODE_PAGE_PARAM); - if (__result__ != 0) - { - return static_cast(__result__); - } - - res = OutFile_Open(p, reinterpret_cast(buf.data)); - Buf_Free(&buf, &g_Alloc); - return res; -#endif -} - - -SRes Archive::PrintString(const UInt16 *s) -{ - CBuf buf; - SRes res; - Buf_Init(&buf); - res = Utf16_To_Char(&buf, s -#ifndef _USE_UTF8 - , CP_OEMCP -#endif - ); - if (res == SZ_OK) - { - Print(reinterpret_cast(buf.data)); - } - Buf_Free(&buf, &g_Alloc); - return res; -} - -void Archive::UInt64ToStr(UInt64 value, char *s, int numDigits) -{ - char temp[32]; - int pos = 0; - do - { - temp[pos++] = static_cast('0' + static_cast(value % 10)); - value /= 10; - } while (value != 0); - - for (numDigits -= pos; numDigits > 0; numDigits--) - *s++ = ' '; - - do - *s++ = temp[--pos]; - while (pos); - *s = '\0'; -} - -char *Archive::UIntToStr(char *s, unsigned value, int numDigits) -{ - char temp[16]; - int pos = 0; - do - temp[pos++] = static_cast('0' + (value % 10)); - while (value /= 10); - - for (numDigits -= pos; numDigits > 0; numDigits--) - *s++ = '0'; - - do - *s++ = temp[--pos]; - while (pos); - *s = '\0'; - return s; -} - -void Archive::UIntToStr_2(char *s, unsigned value) -{ - s[0] = static_cast('0' + (value / 10)); - s[1] = static_cast('0' + (value % 10)); -} - -#define PERIOD_4 (4 * 365 + 1) -#define PERIOD_100 (PERIOD_4 * 25 - 1) -#define PERIOD_400 (PERIOD_100 * 4 + 1) - -void Archive::ConvertFileTimeToString(const CNtfsFileTime *nt, char *s) -{ - unsigned year, mon, hour, min, sec; - Byte ms[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; - unsigned t; - UInt32 v; - UInt64 v64 = nt->Low | (static_cast(nt->High) << 32); - v64 /= 10000000; - sec = static_cast(v64 % 60); v64 /= 60; - min = static_cast(v64 % 60); v64 /= 60; - hour = static_cast(v64 % 24); v64 /= 24; - - v = static_cast(v64); - - year = static_cast(1601 + v / PERIOD_400 * 400); - v %= PERIOD_400; - - t = v / PERIOD_100; if (t == 4) t = 3; year += t * 100; v -= t * PERIOD_100; - t = v / PERIOD_4; if (t == 25) t = 24; year += t * 4; v -= t * PERIOD_4; - t = v / 365; if (t == 4) t = 3; year += t; v -= t * 365; - - if (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)) - ms[1] = 29; - for (mon = 0;; mon++) - { - unsigned d = ms[mon]; - if (v < d) - break; - v -= d; - } - s = UIntToStr(s, year, 4); *s++ = '-'; - UIntToStr_2(s, mon + 1); s[2] = '-'; s += 3; - UIntToStr_2(s, static_cast(v) + 1); s[2] = ' '; s += 3; - UIntToStr_2(s, hour); s[2] = ':'; s += 3; - UIntToStr_2(s, min); s[2] = ':'; s += 3; - UIntToStr_2(s, sec); s[2] = 0; -} - -void Archive::PrintLF() -{ - Print("\n"); -} - -void Archive::PrintError(const char *s) -{ - Print("\nERROR: "); - Print(s); - PrintLF(); - - string s1 = s; - last_error = wstring(s1.begin(), s1.end()); -} - -wstring Archive::getLastError() -{ - return last_error; -} - -void Archive::GetAttribString(UInt32 wa, bool isDir, char *s) -{ -#ifdef USE_WINDOWS_FILE - s[0] = (char)(((wa & FILE_ATTRIBUTE_DIRECTORY) != 0 || isDir) ? 'D' : '.'); - s[1] = (char)(((wa & FILE_ATTRIBUTE_READONLY) != 0) ? 'R' : '.'); - s[2] = (char)(((wa & FILE_ATTRIBUTE_HIDDEN) != 0) ? 'H' : '.'); - s[3] = (char)(((wa & FILE_ATTRIBUTE_SYSTEM) != 0) ? 'S' : '.'); - s[4] = (char)(((wa & FILE_ATTRIBUTE_ARCHIVE) != 0) ? 'A' : '.'); - s[5] = 0; -#else - s[0] = static_cast(((wa & (1 << 4)) != 0 || isDir) ? 'D' : '.'); - s[1] = 0; -#endif -} - - -WRes Archive::File_Read1(CSzFile1 *p, void *data, size_t *size) -{ - size_t originalSize = *size; - if (originalSize == 0) - return 0; - - memcpy(data,p->pData+p->Position,originalSize); - p->Position = p->Position + originalSize; - *size = originalSize; - //*size = fread(data, 1, originalSize, p->file); - - if (*size == originalSize) - return 0; - - //return ferror(p->file); - return 1; -} - -SRes Archive::FileInStream_Read1(ISeekInStreamPtr pp, void *buf, size_t *size) -{ - const char *p0 = reinterpret_cast((pp) - MY_offsetof(CFileInStream1, vt)); - CFileInStream1 *p = reinterpret_cast(const_cast(p0)); - return (File_Read1(&p->file, buf, size) == 0) ? SZ_OK : SZ_ERROR_READ; -} - - -WRes Archive::File_Seek1(CSzFile1 *p, Int64 *pos, ESzSeek origin) -{ - int moveMethod; - WRes res=0; - - switch (origin) - { - case SZ_SEEK_SET: - moveMethod = SEEK_SET; - p->Position = *pos; - break; - case SZ_SEEK_CUR: - moveMethod = SEEK_CUR; - p->Position = p->Position + *pos; - *pos = p->Position; - break; - case SZ_SEEK_END: - moveMethod = SEEK_END; - p->Position = p->Length; - *pos = p->Position; - - break; - default: res = 1; - } - - //res = fseek(p->file, (long)*pos, moveMethod); - //*pos = ftell(p->file); - - return res; -} - -SRes Archive::FileInStream_Seek1(ISeekInStreamPtr pp, Int64 *pos, ESzSeek origin) -{ - const char *p0 = reinterpret_cast((pp) - MY_offsetof(CFileInStream1, vt)); - CFileInStream1 *p = reinterpret_cast(const_cast(p0)); - return static_cast(File_Seek1(&p->file, pos, origin)); -} - -void Archive::FileInStream_CreateVTable1(CFileInStream1 *p) -{ - p->vt.Read = FileInStream_Read1; - p->vt.Seek = FileInStream_Seek1; -} - - -#define CONVERT_INT_TO_STR(charType, tempSize) \ - unsigned char temp[tempSize]; unsigned i = 0; \ - while (val >= 10) { temp[i++] = static_cast('0' + static_cast(val % 10)); val /= 10; } \ - *s++ = static_cast('0' + static_cast(val)); \ - while (i != 0) { i--; *s++ = static_cast(temp[i]); } \ - *s = 0; - -void Archive::ConvertUInt32ToString(UInt32 val, char *s) -{ - CONVERT_INT_TO_STR(char, 16); -} - -void Archive::ConvertUInt64ToString(UInt64 val, char *s) -{ - if (val <= static_cast(0xFFFFFFFF)) - { - ConvertUInt32ToString(static_cast(val), s); - return; - } - CONVERT_INT_TO_STR(char, 24); -} - -u16string Archive::getFileName(const UInt32 &i, UInt16 *&temp, size_t &tempSize) -{ - u16string file_name; - size_t len = SzArEx_GetFileNameUtf16(&db, i, nullptr); - - if (len > tempSize) - { - SzFree(nullptr, temp); - tempSize = len; - temp = reinterpret_cast(SzAlloc(nullptr, tempSize * sizeof(temp[0]))); - if (temp==nullptr) - { - res = SZ_ERROR_MEM; - } - } - - if (res != SZ_ERROR_MEM) - { - unsigned long len1 = SzArEx_GetFileNameUtf16(&db, i, temp); - file_name = u16string(reinterpret_cast(temp),len1); - } - - return file_name; -} - -void Archive::printPercent(const size_t &processedSize) -{ - //Print percent - char s[32]; - unsigned size; - char c = '%'; - - if (Total != 0) - { - Completed += processedSize; - val = Completed * max_percent / Total; - } - ConvertUInt64ToString(val, s); - size = static_cast(strlen(s)); - s[size++] = c; - s[size] = 0; - - Print(" ("); - Print(s); - Print(")"); -} - -UInt64 Archive::getPercent() -{ - return val; -} - -UInt64 Archive::getMaxPercent() -{ - return max_percent; -} - -SRes Archive::fileList(std::list &file_list) -{ - Print("\n7z Decoder " MY_VERSION_CPU " : " MY_COPYRIGHT_DATE "\n\n"); - - for (UInt32 i = 0; i < db.NumFiles; i++) - { - unsigned isDir = SzArEx_IsDir(&db, i); - - #ifndef _WIN32 - // skip dirs - if (isDir) - continue; - #endif - - u16string file_name = getFileName(i, temp, tempSize); - - if (file_name.empty()==true) - break; - - char attr[8], s[32], t[32]; - UInt64 fileSize; - - GetAttribString(SzBitWithVals_Check(&db.Attribs, i) ? db.Attribs.Vals[i] : 0, static_cast(isDir), attr); - - fileSize = SzArEx_GetFileSize(&db, i); - UInt64ToStr(fileSize, s, 10); - - if (SzBitWithVals_Check(&db.MTime, i)) - { - ConvertFileTimeToString(&db.MTime.Vals[i], t); - } - else - { - size_t j; - for (j = 0; j < 19; j++) - t[j] = ' '; - t[j] = '\0'; - } - - Print(t); - Print(" "); - Print(attr); - Print(" "); - Print(s); - Print(" "); - res = PrintString(temp); - - if (res != SZ_OK) - break; - - file_list.push_back(wstring(file_name.begin(),file_name.end())); - - if (isDir) - Print("/"); - PrintLF(); - continue; - - }//end for (i = 0; i < db.NumFiles; i++) - - return finish(); -} - -void Archive::calcTotal(const std::list &files, const std::list &paths) -{ - Print("\n7z Decoder " MY_VERSION_CPU " : " MY_COPYRIGHT_DATE "\n\n"); - - this->file_list = files; - this->path_list = paths; - - //Calculation of the size of the unpacked files - for (UInt32 i = 0; i < db.NumFiles; i++) - { - UInt64 fileSize; - fileSize = SzArEx_GetFileSize(&db, i); - u16string file_name = getFileName(i, temp, tempSize); - - if (file_name.empty()==true) - break; - - //We look for in the list - wstring file_name1 = wstring(file_name.begin(), file_name.end()); - if (std::find(file_list.begin(), file_list.end(), file_name1) != file_list.end()) - { - importantTotalUnpacked += fileSize; - } - } - - Total = importantTotalUnpacked; // the size of the unpacked archive - - /* - if you need cache, use these 3 variables. - if you use external function, you can make these variable as static. - */ - blockIndex = 0xFFFFFFFF; /* it can have any value before first call (if outBuffer = 0) */ - outBuffer = nullptr; /* it must be 0 before first call for each new archive. */ - outBufferSize = 0; /* it can have any value before first call (if outBuffer = 0) */ - - return; -} - - - -SRes Archive::extractionFile(const UInt32 &i) -{ - size_t offset = 0; - size_t outSizeProcessed = 0; - unsigned isDir = SzArEx_IsDir(&db, i); - u16string file_name = getFileName(i, temp, tempSize); - - if (file_name.empty()==true) - { - return res; - } - - //The file which needs to be derived is found - bool extract=false; - unsigned int n=0; - u16string source1; - - //We look for in the list - wstring file_name1 = wstring(file_name.begin(), file_name.end()); - - for (std::list::iterator it=file_list.begin(); it != file_list.end(); ++it) - { - if (*it==file_name1) - { - extract=true; - break; - } - n++; - } - - if (extract) - { - Print("Extracting "); - res = PrintString(temp); - if (res != SZ_OK) - { - return res; - } - - if (isDir) - { - Print("/"); - } - else - { - res = SzArEx_Extract(&db, &lookStream.vt, i, - &blockIndex, &outBuffer, &outBufferSize, - &offset, &outSizeProcessed, - &allocImp, &allocTempImp); - - if (res != SZ_OK) - { - return res; - } - } - -#ifdef _WIN32 - if (outSizeProcessed==0) return res; -#endif - - CSzFile outFile; - size_t processedSize; - size_t j; - const UInt16 *destPath; - std::u16string destination1; - - - //To take file name from a path - - std::u16string file; - file = file_name.substr(file_name.rfind('/')+1); - std::u16string str; - - std::list::iterator it1 = path_list.begin(); - std::advance(it1,n); - str = std::u16string((*it1).begin(), (*it1).end()); - - str.resize(str.length()+1); - str.back() = '/'; - - destination1 = str + file; - - - //To create folders - for (j = 0; j < destination1.length(); j++) - { - if ((destination1.at(j) == '/')|| - (destination1.at(j) == '\\')) - { - destination1.at(j) = 0; - MyCreateDir(reinterpret_cast(destination1.c_str())); - destination1.at(j) = CHAR_PATH_SEPARATOR; - } - } - - destPath = reinterpret_cast(destination1.c_str()); - - if ((isDir)&&(path_list.empty()==true)) - { - MyCreateDir(destPath); - PrintLF(); - return res; - } - else - { -#ifdef _WIN32 - if (OutFile_OpenUtf16(&outFile, destPath)) - { - PrintString(destPath); - PrintError("can not open output file"); - res = SZ_ERROR_FAIL; - return res; - } -#else - bool isSymLink = (db.Attribs.Vals[i] >> 16) & 0x2000; - if (isSymLink) - { - processedSize = outSizeProcessed; - printPercent(processedSize); - - CBuf buf; - Buf_Init(&buf); - Utf16_To_Char(&buf, destPath MY_FILE_CODE_PAGE_PARAM); - char *symlinkStr = new char[processedSize]; - memcpy(symlinkStr, outBuffer + offset, processedSize); - symlinkStr[processedSize] = '\0'; - symlink(symlinkStr, reinterpret_cast(buf.data)); - PrintLF(); - Buf_Free(&buf, &g_Alloc); - delete[] symlinkStr; - return res; - } - else - { - if(OutFile_OpenUtf16(&outFile, destPath)) - { - PrintString(destPath); - PrintError("can not open output file"); - res = SZ_ERROR_FAIL; - return res; - } - } -#endif - } - - processedSize = outSizeProcessed; - - printPercent(processedSize); - - //Saving of the unpacked file - if ((File_Write(&outFile, outBuffer + offset, &processedSize) != 0) || (processedSize != outSizeProcessed)) - { - PrintError("can not write output file"); - res = SZ_ERROR_FAIL; - return res; - } - - if (File_Close(&outFile)) - { - PrintError("can not close output file"); - res = SZ_ERROR_FAIL; - return res; - } - -//Set file permission(for OS X only) -#ifndef _WIN32 - { - UInt32 attrVal = SzBitWithVals_Check(&db.Attribs, i) ? db.Attribs.Vals[i] : 0; - mode_t perm = attrVal >> 16; - CBuf buf; - Buf_Init(&buf); - - SRes __result__ = Utf16_To_Char(&buf, destPath); - if (__result__ != 0) - { - return __result__; - } - - if (chmod(reinterpret_cast(buf.data), perm) != 0) - { - return SZ_ERROR_FAIL; - } - - Buf_Free(&buf, &g_Alloc); - } -#endif - - PrintLF(); - } - return res; -} - -bool Archive::is_finish() -{ - bool ret = false; - if ((Total>0)&&(Completed == Total)) - { - ret = true; - } - - return ret; -} - -SRes Archive::finish() -{ - ISzAlloc_Free(&allocImp, outBuffer); - - if (res == SZ_OK) - { - Print("\nEverything is Ok\n"); - return res; - } - - if (res == SZ_ERROR_UNSUPPORTED) - PrintError("decoder doesn't support this archive"); - else if (res == SZ_ERROR_MEM) - PrintError("can not allocate memory"); - else if (res == SZ_ERROR_CRC) - PrintError("CRC error"); - else - { - char s[32]; - UInt64ToStr(static_cast(res), s, 0); - PrintError(s); - } - - val = max_percent; - return res; -} - -UInt32 Archive::getNumFiles() -{ - return db.NumFiles; -} - -string Archive::ConvertToString(const wstring &str) -{ - const UInt16 *s = reinterpret_cast(str.c_str()); - - string str1; - CBuf buf; - SRes res; - Buf_Init(&buf); - res = Utf16_To_Char(&buf, s -#ifndef _USE_UTF8 - , CP_OEMCP -#endif - ); - if (res == SZ_OK) - { - str1 = reinterpret_cast(buf.data); - } - Buf_Free(&buf, &g_Alloc); - return str1; -} - diff --git a/client/common/archive/archive.h b/client/common/archive/archive.h deleted file mode 100644 index dec9bcd2f..000000000 --- a/client/common/archive/archive.h +++ /dev/null @@ -1,186 +0,0 @@ -#pragma once - -//LZMA SDK it is taken from here https://www.7-zip.org/a/lzma1805.7z - -#ifdef _WIN32 -#include -#endif - -#include -#include -#include - -#include -#include <7z.h> -#include <7zBuf.h> -#include <7zFile.h> -#include <7zVersion.h> - -typedef struct -{ -#ifdef _WIN32 - BYTE* pData; //the pointer on the first byte of the file.7z -#else - unsigned char* pData; -#endif - - Int64 Position; //number of byte in an array from which will be will be executed reading - unsigned long Length; //it is long the file.7z -} CSzFile1; - - -#ifndef USE_WINDOWS_FILE -/* for mkdir */ -#ifdef _WIN32 -#include -#else -#include -#include -#endif -#endif - -#define kInputBufSize (static_cast(1) << 18) - -#include -#include -#include - -typedef struct -{ - ISeekInStream vt; - CSzFile1 file; -} CFileInStream1; - -#ifdef _WIN32 -#define MY_FILE_CODE_PAGE_PARAM ,g_FileCodePage -#else -#define MY_FILE_CODE_PAGE_PARAM -#endif - -#define PERIOD_4 (4 * 365 + 1) -#define PERIOD_100 (PERIOD_4 * 25 - 1) -#define PERIOD_400 (PERIOD_100 * 4 + 1) - -#if defined __APPLE__ -#define E_ABORT 0x80004004L -#define S_OK 0L - -#include -#endif - -class Archive -{ -private: -#ifdef _WIN32 - HGLOBAL hGlobal; -#endif - - std::function logFunction; - unsigned long file_size; - unsigned char* pData; - -#ifndef _WIN32 - uid_t userId_; - gid_t groupId_; -#endif - - UInt64 importantTotalUnpacked; - UInt64 Total; - UInt64 Completed; - SRes res; - - std::list file_list; - std::list path_list; - - CSzArEx db; - ISzAlloc allocImp; - ISzAlloc allocTempImp; - - CFileInStream1 archiveStream; - CLookToRead2 lookStream; - - UInt16 *temp; - size_t tempSize; - - UInt32 blockIndex; /* it can have any value before first call (if outBuffer = 0) */ - Byte *outBuffer; /* it must be 0 before first call for each new archive. */ - size_t outBufferSize; /* it can have any value before first call (if outBuffer = 0) */ - - UInt64 val; - UInt64 max_percent; - std::string path_to_the_log; - std::wstring last_error; - - void Print(const char *s); - int Buf_EnsureSize(CBuf *dest, size_t size); - -#ifndef _WIN32 - #define _USE_UTF8 -#endif - - /* #define _USE_UTF8 */ -#ifdef _USE_UTF8 - #define _UTF8_START(n) (0x100 - (1 << (7 - (n)))) - #define _UTF8_RANGE(n) (((UInt32)1) << ((n) * 5 + 6)) - #define _UTF8_HEAD(n, val) ((Byte)(_UTF8_START(n) + (val >> (6 * (n))))) - #define _UTF8_CHAR(n, val) ((Byte)(0x80 + (((val) >> (6 * (n))) & 0x3F))) - - size_t Utf16_To_Utf8_Calc(const UInt16 *src, const UInt16 *srcLim); - Byte *Utf16_To_Utf8(Byte *dest, const UInt16 *src, const UInt16 *srcLim); - SRes Utf16_To_Utf8Buf(CBuf *dest, const UInt16 *src, size_t srcLen); -#endif - - SRes Utf16_To_Char(CBuf *buf, const UInt16 *s -#ifndef _USE_UTF8 - , UINT codePage -#endif - ); - - WRes MyCreateDir(const UInt16 *name); - WRes OutFile_OpenUtf16(CSzFile *p, const UInt16 *name); - SRes PrintString(const UInt16 *s); - void UInt64ToStr(UInt64 value, char *s, int numDigits); - char *UIntToStr(char *s, unsigned value, int numDigits); - void UIntToStr_2(char *s, unsigned value); - void ConvertFileTimeToString(const CNtfsFileTime *nt, char *s); - void PrintLF(); - void PrintError(const char *s); - void GetAttribString(UInt32 wa, bool isDir, char *s); - - static WRes File_Read1(CSzFile1 *p, void *data, size_t *size); - static SRes FileInStream_Read1(ISeekInStreamPtr pp, void *buf, size_t *size); - static WRes File_Seek1(CSzFile1 *p, Int64 *pos, ESzSeek origin); - static SRes FileInStream_Seek1(ISeekInStreamPtr pp, Int64 *pos, ESzSeek origin); - - void FileInStream_CreateVTable1(CFileInStream1 *p); - - std::string ConvertToString(const std::wstring &str); - void ConvertUInt32ToString(UInt32 val, char *s); - void ConvertUInt64ToString(UInt64 val, char *s); - - std::u16string getFileName(const UInt32 &i, UInt16 *&temp, size_t &tempSize); - void printPercent(const size_t &processedSize); - - public: -#ifdef _WIN32 - Archive(const std::wstring &name); -#else - Archive(const std::wstring &name); - bool isCorrect() const; -#endif - ~Archive(); - void setLogFunction(const std::function& logFunc); - - //list of files - SRes fileList(std::list &file_list); - - //extract files[i] to path paths[i] - void calcTotal(const std::list &files, const std::list &paths); - UInt32 getNumFiles(); - SRes extractionFile(const UInt32 &i); - UInt64 getPercent(); - UInt64 getMaxPercent(); - bool is_finish(); - SRes finish(); - std::wstring getLastError(); -}; diff --git a/client/common/changelog.txt b/client/common/changelog.txt index 81a2f52b3..ae01605d5 100644 --- a/client/common/changelog.txt +++ b/client/common/changelog.txt @@ -1,9 +1,86 @@ -2.10.15 (28/06/2024) +2.11.4 (05/07/2024) All: - * No changes. Promoted 2.10.14 to the stable channel. + * Added CLI mechanisms to connect with a specific protocol, and improve help. #962 + * Fixed handling of 0.0.0.0 response from DNS server for split tunnel hostnames. #1049 +Windows: + * Fixed packet size not applying to WireGuard connections. #1021 + * Fixed app fails to start on Windows Server 2022. #1053 +MacOS: + * Improved CI to make use of the arm64 runner. #1009 + * Fixed installer may not exit after attempting to launch app. #1035 + * Fixed SSID detection not working in MacOS 15. #1034 + * Fixed window move is not smooth. #1037 + * Fixed firewall-on-boot may not function until the app starts. #1032 + * Fixed app not showing an error when ctrld fails to execute. #1017 + * Fixed app is very sluggish with many split tunnel hostnames. #1049 + * Fixed split tunneling may not function correctly on MacOS 15. #1049 + * Fixed app crash when cancelling 'install helper' dialog. #1057 +Linux: + * Added "Packet Size" preference to match other platforms. #1018 + * Added CLI-only support for preferences. #962 + * Improved CI to make use of the arm64 runner. #1009 + * Fixed CI builds of the CLI-only app being linked to GUI libs. #962 + * Fixed firewall blocking DHCP traffic on shared connections. #1031 + * Fixed firewall-on-boot may not function until the app starts. #1032 + * Fixed app not showing an error when ctrld fails to execute. #1017 + * Fixed app update not completing if apt-listbugs is installed. #1052 + * Fixed helper not working after update until restart. #1050 + + +2.11.3 (11/06/2024) +All: + * Add wg_ttl parameter to WgConnect call [wsnet]. #1002 + * Improved CI test stability. #1027 +Windows: + * Added debugging information for wsnet. #1001 + * Fixed Program Files\Windscribe folder not cleaned up on uninstall if installed to custom folder. #1007 +MacOS: + * Improved IPC sockets/lockfiles by consolidation. #962 + * Fixed firewall-on-boot may not function until the app starts. #1032 +Linux: + * Added partial implementation of GUI-less client. #962 + * Improved CI build image and duration. #1009 + * Improved IPC sockets/lockfiles by consolidation. #962 + * Fixed Linux setgid change not working in OpenSUSE. #1000 + * Fixed firewall blocking DHCP traffic. #1031 + * Fixed firewall-on-boot may not function until the app starts. #1032 + + +2.11.2 (01/05/2024) +All: + * Added new API endpoints for iOS [wsnet]. #972 + * Fixed proxy gateway port not displaying 'Auto' when edit is cancelled. #50 + * Fixed a wrong translation in Ukrainian. #993 +Windows: + * Fixed main window can be dragged beyond the top of the screen. #356 +MacOS: + * Updated wsnet library, included some required files from scapix to wsnet.framework for iOS build. #970 + * Fixed app may be quarantined by MacOS. #963 + * Fixed Mac pid checking vulnerability. #977 +Linux: + * Improved code organization and build scripts to prepare for GUI-less client. #962 + * Fixed missing dependency for xcb-util-cursor-devel in the OpenSUSE RPM package. #955 + * Fixed app icons not showing in XFCE. #985 + + +2.11.1 (09/04/2024) +All: + * Added support for quantum-safe TLS and OpenVPN. #947 #965 + * Added port selection to proxy gateway. #50 + * Improved debug log usefulness by adding routing table. #873 + * Updated wsnet library, refactoring without changing functionality. #959, #961. +Windows: + * Improved installer 7z extraction to use the 7zr utility and log the extraction process. #926 + * Improved installer logging to consistently use wide strings. #968 + * Fixed ICMP pings crash [wsnet]. #969 +MacOS: + * Improved installer archive generation and extraction to use built-in macOS utilities. #963 + * Fixed emit keyword missing from SleepEvents_mac logic. #954 +Linux: + * Added support for OpenSUSE. #954 -2.10.14 (20/06/2024) +2.10.13 (??/??/2024) All: * Improved best location selection. #1040 Windows: @@ -24,7 +101,6 @@ All: * Updated ctrld dependency to 1.3.7. #1022 MacOS: * Fixed MacOS 11 bug, unable to open app after installing. #1011 - * Fixed Wireguard takes too long to connect and tunnel tests are randomly slow. #1013 * Fixed missing "verb 3" argument for OpenVPN after refactor. #997 Linux: * Fixed tray icon sometimes can't be clicked. #792 @@ -38,7 +114,6 @@ All: * Fixed wsnet bugs, in particular the icmp ping bug. Also other library refactoring related improvements taken from 2.11. #999 Windows: * Added security warning in the installer when installing to a custom folder. #988 - * Added debugging information for wsnet. #1001 * Fixed openvpn adapter IP assertion. #997 * Fixed privilege escalation vulnerability allowing an attacker to inject a DLL into the client app and task kill any process (Reported by Zeze Lin working with Trend Micro Zero Day Initiative). #984 * Fixed privilege escalation vulnerability allowing an attacker to inject a DLL into the client app and start an arbitrary process as admin (Reported by Zeze Lin working with Trend Micro Zero Day Initiative). #984 @@ -64,10 +139,9 @@ Windows: * Fixed possible delay in executable signature logic when it checks CRLs over the network. #983 MacOS: * Improved helper upgrade flow. #982 - * Fixed Mac pid checking vulnerability. #977 Linux: * Fixed misdetection of platform on Raspberry Pi. #979 - * Fixed pid race in helper. #977 + * Fixed pid race in helper #977 * Fixed typo in command to get local IP. #995 diff --git a/client/common/config/openssl.cnf b/client/common/config/openssl.cnf new file mode 100644 index 000000000..617aeb2a4 --- /dev/null +++ b/client/common/config/openssl.cnf @@ -0,0 +1,22 @@ +HOME = . +openssl_conf = openssl_init + +[openssl_init] +providers = provider_sect +ssl_conf = ssl_sect + +[provider_sect] +default = default_sect +oqsprovider = oqsprovider_sect + +[default_sect] +activate = 1 + +[oqsprovider_sect] +activate = 1 + +[ssl_sect] +system_default = system_default_sect + +[system_default_sect] +Groups = p521_kyber1024:kyber1024:kyber768:p384_kyber768:X448:X25519:secp521r1:secp384r1:secp256k1:ffdhe8192:ffdhe6144:ffdhe4096:ffdhe3072:ffdhe2048 \ No newline at end of file diff --git a/client/common/ipc/CMakeLists.txt b/client/common/ipc/CMakeLists.txt index c9c801511..568b7ddd7 100644 --- a/client/common/ipc/CMakeLists.txt +++ b/client/common/ipc/CMakeLists.txt @@ -1,5 +1,4 @@ target_sources(common PRIVATE - clicommands.cpp clicommands.h command.h commandfactory.cpp diff --git a/client/common/ipc/clicommands.cpp b/client/common/ipc/clicommands.cpp deleted file mode 100644 index 35bf57c15..000000000 --- a/client/common/ipc/clicommands.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#include - -namespace IPC -{ -} // namespace IPC diff --git a/client/common/ipc/clicommands.h b/client/common/ipc/clicommands.h index 738434f3d..04951a1df 100644 --- a/client/common/ipc/clicommands.h +++ b/client/common/ipc/clicommands.h @@ -10,6 +10,36 @@ namespace IPC namespace CliCommands { +class Acknowledge : public Command +{ +public: + Acknowledge() {} + explicit Acknowledge(char *buf, int size) + { + QByteArray arr(buf, size); + QDataStream ds(&arr, QIODevice::ReadOnly); + ds >> code_ >> message_; + } + + std::vector getData() const override + { + QByteArray arr; + QDataStream ds(&arr, QIODevice::WriteOnly); + ds << code_ << message_; + return std::vector(arr.begin(), arr.end()); + } + + std::string getStringId() const override { return getCommandStringId(); } + std::string getDebugString() const override + { + return "CliCommands::Acknowledge debug string"; + } + static std::string getCommandStringId() { return "CliCommands::Acknowledge"; } + + int code_; + QString message_; +}; + class Connect : public Command { public: @@ -18,14 +48,14 @@ class Connect : public Command { QByteArray arr(buf, size); QDataStream ds(&arr, QIODevice::ReadOnly); - ds >> location_; + ds >> location_ >> protocol_; } std::vector getData() const override { QByteArray arr; QDataStream ds(&arr, QIODevice::WriteOnly); - ds << location_; + ds << location_ << protocol_; return std::vector(arr.begin(), arr.end()); } @@ -37,6 +67,7 @@ class Connect : public Command static std::string getCommandStringId() { return "CliCommands::Connect"; } QString location_; + QString protocol_; }; class Disconnect : public Command @@ -85,6 +116,35 @@ class ShowLocations : public Command static std::string getCommandStringId() { return "CliCommands::ShowLocations"; } }; +class LocationsList : public Command +{ +public: + LocationsList() {} + explicit LocationsList(char *buf, int size) + { + QByteArray arr(buf, size); + QDataStream ds(&arr, QIODevice::ReadOnly); + ds >> locations_; + } + + std::vector getData() const override + { + QByteArray arr; + QDataStream ds(&arr, QIODevice::WriteOnly); + ds << locations_; + return std::vector(arr.begin(), arr.end()); + } + + std::string getStringId() const override { return getCommandStringId(); } + std::string getDebugString() const override + { + return "CliCommands::LocationsList debug string"; + } + static std::string getCommandStringId() { return "CliCommands::LocationsList"; } + + QStringList locations_; +}; + class Firewall : public Command { public: @@ -168,11 +228,11 @@ class Login : public Command QString code2fa_; }; -class SignOut : public Command +class Logout : public Command { public: - SignOut() {} - explicit SignOut(char *buf, int size) + Logout() {} + explicit Logout(char *buf, int size) { QByteArray arr(buf, size); QDataStream ds(&arr, QIODevice::ReadOnly); @@ -190,133 +250,21 @@ class SignOut : public Command std::string getStringId() const override { return getCommandStringId(); } std::string getDebugString() const override { - return "CliCommands::SignOut debug string"; + return "CliCommands::Logout debug string"; } - static std::string getCommandStringId() { return "CliCommands::SignOut"; } + static std::string getCommandStringId() { return "CliCommands::Logout"; } bool isKeepFirewallOn_; }; -class ConnectToLocationAnswer : public Command -{ -public: - ConnectToLocationAnswer() {} - explicit ConnectToLocationAnswer(char *buf, int size) - { - QByteArray arr(buf, size); - QDataStream ds(&arr, QIODevice::ReadOnly); - ds >> isSuccess_ >> location_; - } - - std::vector getData() const override - { - QByteArray arr; - QDataStream ds(&arr, QIODevice::WriteOnly); - ds << isSuccess_ << location_; - return std::vector(arr.begin(), arr.end()); - } - - std::string getStringId() const override { return getCommandStringId(); } - std::string getDebugString() const override - { - return "CliCommands::ConnectToLocationAnswer debug string"; - } - static std::string getCommandStringId() { return "CliCommands::ConnectToLocationAnswer"; } - - bool isSuccess_; - QString location_; -}; - -class ConnectStateChanged : public Command +class SendLogs: public Command { public: - ConnectStateChanged() {} - explicit ConnectStateChanged(char *buf, int size) + SendLogs() {} + explicit SendLogs(char *buf, int size) { - QByteArray arr(buf, size); - QDataStream ds(&arr, QIODevice::ReadOnly); - ds >> connectState; - } - - std::vector getData() const override - { - QByteArray arr; - QDataStream ds(&arr, QIODevice::WriteOnly); - ds << connectState; - return std::vector(arr.begin(), arr.end()); - } - - std::string getStringId() const override { return getCommandStringId(); } - std::string getDebugString() const override - { - return "CliCommands::ConnectStateChanged debug string"; - } - static std::string getCommandStringId() { return "CliCommands::ConnectStateChanged"; } - - types::ConnectState connectState; -}; - -class FirewallStateChanged : public Command -{ -public: - FirewallStateChanged() {} - explicit FirewallStateChanged(char *buf, int size) - { - QByteArray arr(buf, size); - QDataStream ds(&arr, QIODevice::ReadOnly); - ds >> isFirewallEnabled_ >> isFirewallAlwaysOn_; - } - - std::vector getData() const override - { - QByteArray arr; - QDataStream ds(&arr, QIODevice::WriteOnly); - ds << isFirewallEnabled_ << isFirewallAlwaysOn_; - return std::vector(arr.begin(), arr.end()); - } - - std::string getStringId() const override { return getCommandStringId(); } - std::string getDebugString() const override - { - return "CliCommands::FirewallStateChanged debug string"; - } - static std::string getCommandStringId() { return "CliCommands::FirewallStateChanged"; } - - bool isFirewallEnabled_; - bool isFirewallAlwaysOn_; -}; - -class LocationsShown : public Command -{ -public: - LocationsShown() {} - explicit LocationsShown(char *buf, int size) - { - Q_UNUSED(buf) - Q_UNUSED(size) - } - - std::vector getData() const override - { - return std::vector(); - } - - std::string getStringId() const override { return getCommandStringId(); } - std::string getDebugString() const override - { - return "CliCommands::LocationsShown debug string"; - } - static std::string getCommandStringId() { return "CliCommands::LocationsShown"; } -}; - -class AlreadyDisconnected : public Command -{ -public: - AlreadyDisconnected() {} - explicit AlreadyDisconnected(char *buf, int size) - { - Q_UNUSED(buf) - Q_UNUSED(size) + Q_UNUSED(buf); + Q_UNUSED(size); } std::vector getData() const override @@ -327,9 +275,9 @@ class AlreadyDisconnected : public Command std::string getStringId() const override { return getCommandStringId(); } std::string getDebugString() const override { - return "CliCommands::AlreadyDisconnected debug string"; + return "CliCommands::SendLogs debug string"; } - static std::string getCommandStringId() { return "CliCommands::AlreadyDisconnected"; } + static std::string getCommandStringId() { return "CliCommands::SendLogs"; } }; class State : public Command @@ -340,14 +288,18 @@ class State : public Command { QByteArray arr(buf, size); QDataStream ds(&arr, QIODevice::ReadOnly); - ds >> isLoggedIn_ >> waitingForLoginInfo_ >> connectState_ >> location_; + ds >> language_ >> connectivity_ >> loginState_ >> loginError_ >> loginErrorMessage_ >> connectState_ + >> protocol_ >> port_ >> tunnelTestState_ >> location_ >> isFirewallOn_ >> isFirewallAlwaysOn_ + >> updateError_ >> updateProgress_ >> updateAvailable_ >> trafficUsed_ >> trafficMax_; } std::vector getData() const override { QByteArray arr; QDataStream ds(&arr, QIODevice::WriteOnly); - ds << isLoggedIn_ << waitingForLoginInfo_ << connectState_ << location_; + ds << language_ << connectivity_ << loginState_ << loginError_ << loginErrorMessage_ << connectState_ + << protocol_ << port_ << tunnelTestState_ << location_ << isFirewallOn_ << isFirewallAlwaysOn_ + << updateError_ << updateProgress_ << updateAvailable_ << trafficUsed_ << trafficMax_; return std::vector(arr.begin(), arr.end()); } @@ -358,50 +310,56 @@ class State : public Command } static std::string getCommandStringId() { return "CliCommands::State"; } - bool isLoggedIn_ = false; - bool waitingForLoginInfo_ = false; - CONNECT_STATE connectState_ = CONNECT_STATE_DISCONNECTED; + QString language_; + bool connectivity_; + LOGIN_STATE loginState_ = LOGIN_STATE_LOGGED_OUT; + LOGIN_RET loginError_; + QString loginErrorMessage_; + types::ConnectState connectState_; + types::Protocol protocol_; + uint port_; + TUNNEL_TEST_STATE tunnelTestState_; LocationID location_; + bool isFirewallOn_; + bool isFirewallAlwaysOn_; + UPDATE_VERSION_ERROR updateError_; + uint updateProgress_; + QString updateAvailable_; + qint64 trafficUsed_; + qint64 trafficMax_; }; -class LoginResult : public Command +class Update : public Command { public: - LoginResult() {} - explicit LoginResult(char *buf, int size) + Update() {} + explicit Update(char *buf, int size) { - QByteArray arr(buf, size); - QDataStream ds(&arr, QIODevice::ReadOnly); - ds >> isLoggedIn_ >> loginError_; + Q_UNUSED(buf); + Q_UNUSED(size); } std::vector getData() const override { - QByteArray arr; - QDataStream ds(&arr, QIODevice::WriteOnly); - ds << isLoggedIn_ << loginError_; - return std::vector(arr.begin(), arr.end()); + return std::vector(); } std::string getStringId() const override { return getCommandStringId(); } std::string getDebugString() const override { - return "CliCommands::LoginResult debug string"; + return "CliCommands::Update debug string"; } - static std::string getCommandStringId() { return "CliCommands::LoginResult"; } - - bool isLoggedIn_; - QString loginError_; + static std::string getCommandStringId() { return "CliCommands::Update"; } }; -class SignedOut : public Command +class ReloadConfig : public Command { public: - SignedOut() {} - explicit SignedOut(char *buf, int size) + ReloadConfig() {} + explicit ReloadConfig(char *buf, int size) { - Q_UNUSED(buf) - Q_UNUSED(size) + Q_UNUSED(buf); + Q_UNUSED(size); } std::vector getData() const override @@ -412,9 +370,9 @@ class SignedOut : public Command std::string getStringId() const override { return getCommandStringId(); } std::string getDebugString() const override { - return "CliCommands::SignedOut debug string"; + return "CliCommands::ReloadConfig debug string"; } - static std::string getCommandStringId() { return "CliCommands::SignedOut"; } + static std::string getCommandStringId() { return "CliCommands::ReloadConfig"; } }; } // namespace CliCommands diff --git a/client/common/ipc/commandfactory.cpp b/client/common/ipc/commandfactory.cpp index 9bf36e995..dd5840bce 100644 --- a/client/common/ipc/commandfactory.cpp +++ b/client/common/ipc/commandfactory.cpp @@ -13,65 +13,32 @@ Command *CommandFactory::makeCommand(const std::string strId, char *buf, int siz qDebug() << QString::fromStdString(strId); // CLI commands - if (strId == IPC::CliCommands::Connect::getCommandStringId()) - { + if (strId == IPC::CliCommands::Acknowledge::getCommandStringId()) { + return new IPC::CliCommands::Acknowledge(buf, size); + } else if (strId == IPC::CliCommands::Connect::getCommandStringId()) { return new IPC::CliCommands::Connect(buf, size); - } - else if (strId == IPC::CliCommands::ConnectToLocationAnswer::getCommandStringId()) - { - return new IPC::CliCommands::ConnectToLocationAnswer(buf, size); - } - else if (strId == IPC::CliCommands::ConnectStateChanged::getCommandStringId()) - { - return new IPC::CliCommands::ConnectStateChanged(buf, size); - } - else if (strId == IPC::CliCommands::Disconnect::getCommandStringId()) - { + } else if (strId == IPC::CliCommands::Disconnect::getCommandStringId()) { return new IPC::CliCommands::Disconnect(buf, size); - } - else if (strId == IPC::CliCommands::AlreadyDisconnected::getCommandStringId()) - { - return new IPC::CliCommands::AlreadyDisconnected(buf, size); - } - else if (strId == IPC::CliCommands::ShowLocations::getCommandStringId()) - { + } else if (strId == IPC::CliCommands::ShowLocations::getCommandStringId()) { return new IPC::CliCommands::ShowLocations(buf, size); - } - else if (strId == IPC::CliCommands::LocationsShown::getCommandStringId()) - { - return new IPC::CliCommands::LocationsShown(buf, size); - } - else if (strId == IPC::CliCommands::GetState::getCommandStringId()) - { + } else if (strId == IPC::CliCommands::LocationsList::getCommandStringId()) { + return new IPC::CliCommands::LocationsList(buf, size); + } else if (strId == IPC::CliCommands::GetState::getCommandStringId()) { return new IPC::CliCommands::GetState(buf, size); - } - else if (strId == IPC::CliCommands::State::getCommandStringId()) - { + } else if (strId == IPC::CliCommands::State::getCommandStringId()) { return new IPC::CliCommands::State(buf, size); - } - else if (strId == IPC::CliCommands::Firewall::getCommandStringId()) - { + } else if (strId == IPC::CliCommands::Firewall::getCommandStringId()) { return new IPC::CliCommands::Firewall(buf, size); - } - else if (strId == IPC::CliCommands::FirewallStateChanged::getCommandStringId()) - { - return new IPC::CliCommands::FirewallStateChanged(buf, size); - } - else if (strId == IPC::CliCommands::Login::getCommandStringId()) - { + } else if (strId == IPC::CliCommands::Login::getCommandStringId()) { return new IPC::CliCommands::Login(buf, size); - } - else if (strId == IPC::CliCommands::LoginResult::getCommandStringId()) - { - return new IPC::CliCommands::LoginResult(buf, size); - } - else if (strId == IPC::CliCommands::SignOut::getCommandStringId()) - { - return new IPC::CliCommands::SignOut(buf, size); - } - else if (strId == IPC::CliCommands::SignedOut::getCommandStringId()) - { - return new IPC::CliCommands::SignedOut(buf, size); + } else if (strId == IPC::CliCommands::Logout::getCommandStringId()) { + return new IPC::CliCommands::Logout(buf, size); + } else if (strId == IPC::CliCommands::Update::getCommandStringId()) { + return new IPC::CliCommands::Update(buf, size); + } else if (strId == IPC::CliCommands::SendLogs::getCommandStringId()) { + return new IPC::CliCommands::SendLogs(buf, size); + } else if (strId == IPC::CliCommands::ReloadConfig::getCommandStringId()) { + return new IPC::CliCommands::ReloadConfig(buf, size); } WS_ASSERT(false); diff --git a/client/common/ipc/connection.cpp b/client/common/ipc/connection.cpp index 7188ba0fc..c0a5e96b7 100644 --- a/client/common/ipc/connection.cpp +++ b/client/common/ipc/connection.cpp @@ -32,7 +32,11 @@ void Connection::connect() QObject::connect(localSocket_, &QLocalSocket::bytesWritten, this, &Connection::onSocketBytesWritten); QObject::connect(localSocket_, &QLocalSocket::readyRead, this, &Connection::onReadyRead); QObject::connect(localSocket_, &QLocalSocket::errorOccurred, this, &Connection::onSocketError); +#if defined(Q_OS_MAC) || defined(Q_OS_LINUX) + localSocket_->connectToServer("/var/run/windscribe/localipc.sock"); +#else localSocket_->connectToServer("Windscribe8rM7bza5OR"); +#endif } void Connection::close() @@ -146,7 +150,11 @@ void Connection::onSocketError(QLocalSocket::LocalSocketError socketError) { Q_UNUSED(socketError) // qDebug() << "Socket error: " << socketError; - emit stateChanged(CONNECTION_ERROR, this); + if (socketError == QLocalSocket::LocalSocketError::PeerClosedError) { + emit stateChanged(CONNECTION_DISCONNECTED, this); + } else { + emit stateChanged(CONNECTION_ERROR, this); + } } bool Connection::canReadCommand() diff --git a/client/common/ipc/server.cpp b/client/common/ipc/server.cpp index 4bb8c2a3d..10338ff9c 100644 --- a/client/common/ipc/server.cpp +++ b/client/common/ipc/server.cpp @@ -21,11 +21,13 @@ bool Server::start() #if defined(Q_OS_MAC) || defined(Q_OS_LINUX) // remove socket file, if already exists (for Mac/Linux) QString connectingPathName = QDir::tempPath(); - connectingPathName += QLatin1Char('/') + "Windscribe8rM7bza5OR"; - QFile::remove(connectingPathName); -#endif + QFile::remove("/var/run/windscribe/localipc.sock"); + bool b = server_.listen("/var/run/windscribe/localipc.sock"); +#else bool b = server_.listen("Windscribe8rM7bza5OR"); +#endif + if (!b) qCDebug(LOG_IPC) << "IPC server listen error:" << server_.errorString(); return b; diff --git a/client/common/types/apiresolutionsettings.cpp b/client/common/types/apiresolutionsettings.cpp index 76cd198a7..64078edb8 100644 --- a/client/common/types/apiresolutionsettings.cpp +++ b/client/common/types/apiresolutionsettings.cpp @@ -1,4 +1,5 @@ #include "apiresolutionsettings.h" +#include "types/enums.h" #include "utils/logger.h" namespace types { @@ -9,11 +10,11 @@ ApiResolutionSettings::ApiResolutionSettings() : bAutomatic_(true) ApiResolutionSettings::ApiResolutionSettings(const QJsonObject &json) { - if (json.contains(jsonInfo_.kIsAutomaticProp) && json[jsonInfo_.kIsAutomaticProp].isBool()) - bAutomatic_ = json[jsonInfo_.kIsAutomaticProp].toBool(); + if (json.contains(kJsonIsAutomaticProp) && json[kJsonIsAutomaticProp].isBool()) + bAutomatic_ = json[kJsonIsAutomaticProp].toBool(); - if (json.contains(jsonInfo_.kManualAddressProp) && json[jsonInfo_.kManualAddressProp].isString()) - manualAddress_ = json[jsonInfo_.kManualAddressProp].toString(); + if (json.contains(kJsonManualAddressProp) && json[kJsonManualAddressProp].isString()) + manualAddress_ = json[kJsonManualAddressProp].toString(); } void ApiResolutionSettings::set(bool bAutomatic, const QString &manualAddress) @@ -42,6 +43,28 @@ void ApiResolutionSettings::setManualAddress(const QString &manualAddress) manualAddress_ = manualAddress; } +QJsonObject ApiResolutionSettings::toJson() const +{ + QJsonObject json; + json[kJsonIsAutomaticProp] = bAutomatic_; + json[kJsonManualAddressProp] = manualAddress_; + return json; +} + +void ApiResolutionSettings::fromIni(const QSettings &settings) +{ + QString prevMode = TOGGLE_MODE_toString(bAutomatic_ ? TOGGLE_MODE_AUTO : TOGGLE_MODE_MANUAL); + TOGGLE_MODE mode = TOGGLE_MODE_fromString(settings.value(kIniIsAutomaticProp, prevMode).toString()); + bAutomatic_ = (mode == TOGGLE_MODE_AUTO); + manualAddress_ = settings.value(kIniManualAddressProp, manualAddress_).toString(); +} + +void ApiResolutionSettings::toIni(QSettings &settings) const +{ + settings.setValue(kIniIsAutomaticProp, TOGGLE_MODE_toString(bAutomatic_ ? TOGGLE_MODE_AUTO : TOGGLE_MODE_MANUAL)); + settings.setValue(kIniManualAddressProp, manualAddress_); +} + QDataStream& operator <<(QDataStream &stream, const ApiResolutionSettings &o) { stream << o.versionForSerialization_; diff --git a/client/common/types/apiresolutionsettings.h b/client/common/types/apiresolutionsettings.h index af56e25f9..db2f4f630 100644 --- a/client/common/types/apiresolutionsettings.h +++ b/client/common/types/apiresolutionsettings.h @@ -2,6 +2,7 @@ #include #include +#include #include namespace types { @@ -9,14 +10,6 @@ namespace types { class ApiResolutionSettings { public: - struct JsonInfo - { - JsonInfo& operator=(const JsonInfo&) { return *this; } - - const QString kIsAutomaticProp = "isAutomatic"; - const QString kManualAddressProp = "manualAddress"; - }; - explicit ApiResolutionSettings(); ApiResolutionSettings(const QJsonObject& json); @@ -37,13 +30,9 @@ class ApiResolutionSettings return !(*this == other); } - QJsonObject toJson() const - { - QJsonObject json; - json[jsonInfo_.kIsAutomaticProp] = bAutomatic_; - json[jsonInfo_.kManualAddressProp] = manualAddress_; - return json; - } + QJsonObject toJson() const; + void fromIni(const QSettings &settings); + void toIni(QSettings &settings) const; friend QDataStream& operator <<(QDataStream &stream, const ApiResolutionSettings &o); friend QDataStream& operator >>(QDataStream &stream, ApiResolutionSettings &o); @@ -53,8 +42,11 @@ class ApiResolutionSettings private: bool bAutomatic_; QString manualAddress_; - JsonInfo jsonInfo_; + static const inline QString kIniIsAutomaticProp = "APIResolutionMode"; + static const inline QString kIniManualAddressProp = "APIResolutionAddress"; + static const inline QString kJsonIsAutomaticProp = "isAutomatic"; + static const inline QString kJsonManualAddressProp = "manualAddress"; static constexpr quint32 versionForSerialization_ = 1; // should increment the version if the data format is changed }; diff --git a/client/common/types/backgroundsettings.h b/client/common/types/backgroundsettings.h index 9aa55050c..3bfe1deca 100644 --- a/client/common/types/backgroundsettings.h +++ b/client/common/types/backgroundsettings.h @@ -9,32 +9,22 @@ namespace types { struct BackgroundSettings { - struct JsonInfo - { - JsonInfo& operator=(const JsonInfo&) { return *this; } - - const QString kBackgroundTypeProp = "backgroundType"; - const QString kBackgroundImageDisconnectedProp = "backgroundImageDisconnected"; - const QString kBackgroundImageConnectedProp = "backgroundImageConnected"; - }; - BACKGROUND_TYPE backgroundType = BACKGROUND_TYPE_COUNTRY_FLAGS; QString backgroundImageDisconnected; QString backgroundImageConnected; - JsonInfo jsonInfo; BackgroundSettings() = default; BackgroundSettings(const QJsonObject &json) { - if (json.contains(jsonInfo.kBackgroundTypeProp) && json[jsonInfo.kBackgroundTypeProp].isDouble()) - backgroundType = static_cast(json[jsonInfo.kBackgroundTypeProp].toInt()); + if (json.contains(kJsonBackgroundTypeProp) && json[kJsonBackgroundTypeProp].isDouble()) + backgroundType = static_cast(json[kJsonBackgroundTypeProp].toInt()); - if (json.contains(jsonInfo.kBackgroundImageDisconnectedProp) && json[jsonInfo.kBackgroundImageDisconnectedProp].isString()) - backgroundImageDisconnected = json[jsonInfo.kBackgroundImageDisconnectedProp].toString(); + if (json.contains(kJsonBackgroundImageDisconnectedProp) && json[kJsonBackgroundImageDisconnectedProp].isString()) + backgroundImageDisconnected = json[kJsonBackgroundImageDisconnectedProp].toString(); - if (json.contains(jsonInfo.kBackgroundImageConnectedProp) && json[jsonInfo.kBackgroundImageConnectedProp].isString()) - backgroundImageConnected = json[jsonInfo.kBackgroundImageConnectedProp].toString(); + if (json.contains(kJsonBackgroundImageConnectedProp) && json[kJsonBackgroundImageConnectedProp].isString()) + backgroundImageConnected = json[kJsonBackgroundImageConnectedProp].toString(); } bool operator==(const BackgroundSettings &other) const @@ -52,9 +42,9 @@ struct BackgroundSettings QJsonObject toJson() const { QJsonObject json; - json[jsonInfo.kBackgroundTypeProp] = static_cast(backgroundType); - json[jsonInfo.kBackgroundImageDisconnectedProp] = backgroundImageDisconnected; - json[jsonInfo.kBackgroundImageConnectedProp] = backgroundImageConnected; + json[kJsonBackgroundTypeProp] = static_cast(backgroundType); + json[kJsonBackgroundImageDisconnectedProp] = backgroundImageDisconnected; + json[kJsonBackgroundImageConnectedProp] = backgroundImageConnected; return json; } @@ -89,6 +79,10 @@ struct BackgroundSettings } private: + static const inline QString kJsonBackgroundTypeProp = "backgroundType"; + static const inline QString kJsonBackgroundImageDisconnectedProp = "backgroundImageDisconnected"; + static const inline QString kJsonBackgroundImageConnectedProp = "backgroundImageConnected"; + static constexpr quint32 versionForSerialization_ = 1; // should increment the version if the data format is changed }; diff --git a/client/common/types/connecteddnsinfo.cpp b/client/common/types/connecteddnsinfo.cpp index 9233375a4..bd05eb286 100644 --- a/client/common/types/connecteddnsinfo.cpp +++ b/client/common/types/connecteddnsinfo.cpp @@ -5,6 +5,32 @@ namespace types { +ConnectedDnsInfo::ConnectedDnsInfo(const QJsonObject &json) +{ + if (json.contains(kJsonTypeProp) && json[kJsonTypeProp].isDouble()) + type = static_cast(json[kJsonTypeProp].toInt()); + + if (json.contains(kJsonUpStream1Prop) && json[kJsonUpStream1Prop].isString()) + upStream1 = json[kJsonUpStream1Prop].toString(); + + if (json.contains(kJsonIsSplitDnsProp) && json[kJsonIsSplitDnsProp].isBool()) + isSplitDns = json[kJsonIsSplitDnsProp].toBool(); + + if (json.contains(kJsonUpStream2Prop) && json[kJsonUpStream2Prop].isString()) + upStream2 = json[kJsonUpStream2Prop].toString(); + + if (json.contains(kJsonHostnamesProp) && json[kJsonHostnamesProp].isArray()) + { + QJsonArray hostnamesArray = json[kJsonHostnamesProp].toArray(); + hostnames.clear(); + for (const QJsonValue &hostnameValue : hostnamesArray) + { + if (hostnameValue.isString()) + hostnames.append(hostnameValue.toString()); + } + } +} + QList ConnectedDnsInfo::allAvailableTypes() { QList t; @@ -38,6 +64,46 @@ bool ConnectedDnsInfo::isCustomIPv4Address() const return type == CONNECTED_DNS_TYPE_CUSTOM && IpValidation::isIp(upStream1) && isSplitDns == false; } + +QJsonObject ConnectedDnsInfo::toJson() const +{ + QJsonObject json; + json[kJsonTypeProp] = static_cast(type); + json[kJsonUpStream1Prop] = upStream1; + json[kJsonIsSplitDnsProp] = isSplitDns; + json[kJsonUpStream2Prop] = upStream2; + + QJsonArray hostnamesArray; + for (const QString& hostname : hostnames) { + hostnamesArray.append(hostname); + } + json[kJsonHostnamesProp] = hostnamesArray; + + return json; +} + +void ConnectedDnsInfo::fromIni(const QSettings &settings) +{ + type = CONNECTED_DNS_TYPE_fromString(settings.value(kIniTypeProp, "Auto").toString()); + upStream1 = settings.value(kIniUpStream1Prop).toString(); + isSplitDns = settings.value(kIniIsSplitDnsProp, false).toBool(); + upStream2 = settings.value(kIniUpStream2Prop).toString(); + hostnames = settings.value(kIniHostnamesProp).toStringList(); +} + +void ConnectedDnsInfo::toIni(QSettings &settings) const +{ + settings.setValue(kIniTypeProp, CONNECTED_DNS_TYPE_toString(type)); + settings.setValue(kIniUpStream1Prop, upStream1); + settings.setValue(kIniIsSplitDnsProp, isSplitDns); + settings.setValue(kIniUpStream2Prop, upStream2); + if (hostnames.isEmpty()) { + settings.remove(kIniHostnamesProp); + } else { + settings.setValue(kIniHostnamesProp, hostnames); + } +} + bool ConnectedDnsInfo::operator==(const ConnectedDnsInfo &other) const { return other.type == type && diff --git a/client/common/types/connecteddnsinfo.h b/client/common/types/connecteddnsinfo.h index 9fea7991c..ef20f2195 100644 --- a/client/common/types/connecteddnsinfo.h +++ b/client/common/types/connecteddnsinfo.h @@ -1,82 +1,32 @@ #pragma once -#include -#include #include #include #include +#include +#include +#include #include "enums.h" namespace types { struct ConnectedDnsInfo { - struct JsonInfo - { - JsonInfo& operator=(const JsonInfo&) { return *this; } - - const QString kTypeProp = "type"; - const QString kUpStream1Prop = "upStream1"; - const QString kIsSplitDnsProp = "isSplitDns"; - const QString kUpStream2Prop = "upStream2"; - const QString kHostnamesProp = "hostnames"; - }; - CONNECTED_DNS_TYPE type = CONNECTED_DNS_TYPE_AUTO; QString upStream1; bool isSplitDns = false; QString upStream2; QStringList hostnames; - JsonInfo jsonInfo; ConnectedDnsInfo() = default; - - ConnectedDnsInfo(const QJsonObject &json) - { - if (json.contains(jsonInfo.kTypeProp) && json[jsonInfo.kTypeProp].isDouble()) - type = static_cast(json[jsonInfo.kTypeProp].toInt()); - - if (json.contains(jsonInfo.kUpStream1Prop) && json[jsonInfo.kUpStream1Prop].isString()) - upStream1 = json[jsonInfo.kUpStream1Prop].toString(); - - if (json.contains(jsonInfo.kIsSplitDnsProp) && json[jsonInfo.kIsSplitDnsProp].isBool()) - isSplitDns = json[jsonInfo.kIsSplitDnsProp].toBool(); - - if (json.contains(jsonInfo.kUpStream2Prop) && json[jsonInfo.kUpStream2Prop].isString()) - upStream2 = json[jsonInfo.kUpStream2Prop].toString(); - - if (json.contains(jsonInfo.kHostnamesProp) && json[jsonInfo.kHostnamesProp].isArray()) - { - QJsonArray hostnamesArray = json[jsonInfo.kHostnamesProp].toArray(); - hostnames.clear(); - for (const QJsonValue &hostnameValue : hostnamesArray) - { - if (hostnameValue.isString()) - hostnames.append(hostnameValue.toString()); - } - } - } - + ConnectedDnsInfo(const QJsonObject &json); static QList allAvailableTypes(); static QString typeToString(const CONNECTED_DNS_TYPE &type); - QJsonObject toJson() const - { - QJsonObject json; - json[jsonInfo.kTypeProp] = static_cast(type); - json[jsonInfo.kUpStream1Prop] = upStream1; - json[jsonInfo.kIsSplitDnsProp] = isSplitDns; - json[jsonInfo.kUpStream2Prop] = upStream2; - - QJsonArray hostnamesArray; - for (const QString& hostname : hostnames) { - hostnamesArray.append(hostname); - } - json[jsonInfo.kHostnamesProp] = hostnamesArray; - - return json; - } + QJsonObject toJson() const; + void fromIni(const QSettings &settings); + void toIni(QSettings &settings) const; bool isCustomIPv4Address() const; @@ -88,6 +38,18 @@ struct ConnectedDnsInfo bool operator==(const ConnectedDnsInfo &other) const; bool operator!=(const ConnectedDnsInfo &other) const; + static const inline QString kIniTypeProp = "ConnectedDNSMode"; + static const inline QString kIniUpStream1Prop = "ConnectedDNSUpstream1"; + static const inline QString kIniIsSplitDnsProp = "SplitDNS"; + static const inline QString kIniUpStream2Prop = "ConnectedDNSUpstream2"; + static const inline QString kIniHostnamesProp = "SplitDNSHostnames"; + + static const inline QString kJsonTypeProp = "type"; + static const inline QString kJsonUpStream1Prop = "upStream1"; + static const inline QString kJsonIsSplitDnsProp = "isSplitDns"; + static const inline QString kJsonUpStream2Prop = "upStream2"; + static const inline QString kJsonHostnamesProp = "hostnames"; + static constexpr quint32 versionForSerialization_ = 2; // should increment the version if the data format is changed }; diff --git a/client/common/types/connectionsettings.cpp b/client/common/types/connectionsettings.cpp index ffd42b69f..d1fb65e14 100644 --- a/client/common/types/connectionsettings.cpp +++ b/client/common/types/connectionsettings.cpp @@ -1,4 +1,5 @@ #include "connectionsettings.h" +#include "types/enums.h" #include "utils/logger.h" #include "utils/ws_assert.h" @@ -25,16 +26,26 @@ ConnectionSettings::ConnectionSettings(Protocol protocol, uint port, bool isAuto ConnectionSettings::ConnectionSettings(const QJsonObject &json) { - if (json.contains(jsonInfo_.kProtocolProp) && json[jsonInfo_.kProtocolProp].isDouble()) - protocol_ = static_cast(json[jsonInfo_.kProtocolProp].toInt()); + if (json.contains(kJsonProtocolProp) && json[kJsonProtocolProp].isDouble()) + protocol_ = static_cast(json[kJsonProtocolProp].toInt()); + + if (json.contains(kJsonPortProp) && json[kJsonPortProp].isDouble()) { + uint port = static_cast(json[kJsonPortProp].toInt()); + if (port >= 0 && port < 65536) { + port_ = port; + } + } - if (json.contains(jsonInfo_.kPortProp) && json[jsonInfo_.kPortProp].isDouble()) - port_ = static_cast(json[jsonInfo_.kPortProp].toInt()); + if (json.contains(kJsonIsAutomaticProp) && json[kJsonIsAutomaticProp].isBool()) + isAutomatic_ = json[kJsonIsAutomaticProp].toBool(); - if (json.contains(jsonInfo_.kIsAutomaticProp) && json[jsonInfo_.kIsAutomaticProp].isBool()) - isAutomatic_ = json[jsonInfo_.kIsAutomaticProp].toBool(); + checkForUnavailableProtocolAndFix(); } +ConnectionSettings::ConnectionSettings(QSettings &settings, const QString &key) +{ + fromIni(settings, key); +} void ConnectionSettings::setProtocolAndPort(Protocol protocol, uint port) { @@ -56,12 +67,50 @@ void ConnectionSettings::setIsAutomatic(bool isAutomatic) QJsonObject ConnectionSettings::toJson() const { QJsonObject json; - json[jsonInfo_.kProtocolProp] = protocol_.toInt(); - json[jsonInfo_.kPortProp] = static_cast(port_); - json[jsonInfo_.kIsAutomaticProp] = isAutomatic_; + json[kJsonProtocolProp] = protocol_.toInt(); + json[kJsonPortProp] = static_cast(port_); + json[kJsonIsAutomaticProp] = isAutomatic_; return json; } +void ConnectionSettings::fromIni(QSettings &settings, const QString &key) +{ + QString prevMode = TOGGLE_MODE_toString(isAutomatic_ ? TOGGLE_MODE_AUTO : TOGGLE_MODE_MANUAL); + + if (!key.isEmpty()) { + settings.beginGroup(key); + } + + TOGGLE_MODE mode = TOGGLE_MODE_fromString(settings.value(kIniIsAutomaticProp, prevMode).toString()); + isAutomatic_ = (mode == TOGGLE_MODE_AUTO); + protocol_ = Protocol::fromString(settings.value(kIniProtocolProp, protocol_.toLongString()).toString()); + uint port = settings.value(kIniPortProp, port_).toUInt(); + if (port < 65536) { + port_ = port; + } + + if (!key.isEmpty()) { + settings.endGroup(); + } + + checkForUnavailableProtocolAndFix(); +} + +void ConnectionSettings::toIni(QSettings &settings, const QString &key) const +{ + if (!key.isEmpty()) { + settings.beginGroup(key); + } + + settings.setValue(kIniIsAutomaticProp, TOGGLE_MODE_toString(isAutomatic_ ? TOGGLE_MODE_AUTO : TOGGLE_MODE_MANUAL)); + settings.setValue(kIniProtocolProp, protocol_.toLongString()); + settings.setValue(kIniPortProp, port_); + + if (!key.isEmpty()) { + settings.endGroup(); + } +} + void ConnectionSettings::checkForUnavailableProtocolAndFix() { QList protocols = Protocol::supportedProtocols(); diff --git a/client/common/types/connectionsettings.h b/client/common/types/connectionsettings.h index a492cfa5e..06fff7ca6 100644 --- a/client/common/types/connectionsettings.h +++ b/client/common/types/connectionsettings.h @@ -9,18 +9,10 @@ namespace types { struct ConnectionSettings { - struct JsonInfo - { - JsonInfo& operator=(const JsonInfo&) { return *this; } - - const QString kProtocolProp = "protocol"; - const QString kPortProp = "port"; - const QString kIsAutomaticProp = "isAutomatic"; - }; - ConnectionSettings(); explicit ConnectionSettings(Protocol protocol, uint port, bool isAutomatic); ConnectionSettings(const QJsonObject& jsonObject); + ConnectionSettings(QSettings &settings, const QString &key = ""); Protocol protocol() const { return protocol_; } uint port() const { return port_; } @@ -43,6 +35,8 @@ struct ConnectionSettings } QJsonObject toJson() const; + void fromIni(QSettings &settings, const QString &key = ""); + void toIni(QSettings &settings, const QString &key = "") const; friend QDataStream& operator <<(QDataStream &stream, const ConnectionSettings &o); friend QDataStream& operator >>(QDataStream &stream, ConnectionSettings &o); @@ -56,7 +50,14 @@ struct ConnectionSettings Protocol protocol_; uint port_; bool isAutomatic_; - JsonInfo jsonInfo_; + + static const inline QString kIniProtocolProp = "ManualConnectionProtocol"; + static const inline QString kIniPortProp = "ManualConnectionPort"; + static const inline QString kIniIsAutomaticProp = "ConnectionMode"; + + static const inline QString kJsonProtocolProp = "protocol"; + static const inline QString kJsonPortProp = "port"; + static const inline QString kJsonIsAutomaticProp = "isAutomatic"; static constexpr quint32 versionForSerialization_ = 1; // should increment the version if the data format is changed }; diff --git a/client/common/types/enginesettings.cpp b/client/common/types/enginesettings.cpp index 7a0252a13..d7d8e3e99 100644 --- a/client/common/types/enginesettings.cpp +++ b/client/common/types/enginesettings.cpp @@ -18,95 +18,7 @@ EngineSettings::EngineSettings() : d(new EngineSettingsData) EngineSettings::EngineSettings(const QJsonObject &json) : d(new EngineSettingsData) { - const auto& jsonInfo = d->jsonInfo; - - if (json.contains(jsonInfo.kApiResolutionSettingsProp) && json[jsonInfo.kApiResolutionSettingsProp].isObject()) - d->apiResolutionSettings = types::ApiResolutionSettings(json[jsonInfo.kApiResolutionSettingsProp].toObject()); - - if (json.contains(jsonInfo.kConnectedDnsInfoProp) && json[jsonInfo.kConnectedDnsInfoProp].isObject()) - d->connectedDnsInfo = types::ConnectedDnsInfo(json[jsonInfo.kConnectedDnsInfoProp].toObject()); - - if (json.contains(jsonInfo.kConnectionSettingsProp) && json[jsonInfo.kConnectionSettingsProp].isObject()) - d->connectionSettings = types::ConnectionSettings(json[jsonInfo.kConnectionSettingsProp].toObject()); - - if (json.contains(jsonInfo.kCustomOvpnConfigsPathProp) && json[jsonInfo.kCustomOvpnConfigsPathProp].isString()) - d->customOvpnConfigsPath = Utils::fromBase64(json[jsonInfo.kCustomOvpnConfigsPathProp].toString()); - -#if defined(Q_OS_LINUX) - if (json.contains(jsonInfo.kDnsManagerProp) && json[jsonInfo.kDnsManagerProp].isDouble()) - d->dnsManager = static_cast(json[jsonInfo.kDnsManagerProp].toInt()); -#endif - - if (json.contains(jsonInfo.kDnsPolicyProp) && json[jsonInfo.kDnsPolicyProp].isDouble()) - d->dnsPolicy = static_cast(json[jsonInfo.kDnsPolicyProp].toInt()); - - if (json.contains(jsonInfo.kFirewallSettingsProp) && json[jsonInfo.kFirewallSettingsProp].isObject()) - d->firewallSettings = types::FirewallSettings(json[jsonInfo.kFirewallSettingsProp].toObject()); - - if (json.contains(jsonInfo.kIsAllowLanTrafficProp) && json[jsonInfo.kIsAllowLanTrafficProp].isBool()) - d->isAllowLanTraffic = json[jsonInfo.kIsAllowLanTrafficProp].toBool(); - - if (json.contains(jsonInfo.kIsAntiCensorshipProp) && json[jsonInfo.kIsAntiCensorshipProp].isBool()) - d->isAntiCensorship = json[jsonInfo.kIsAntiCensorshipProp].toBool(); - - if (json.contains(jsonInfo.kIsKeepAliveEnabledProp) && json[jsonInfo.kIsKeepAliveEnabledProp].isBool()) - d->isKeepAliveEnabled = json[jsonInfo.kIsKeepAliveEnabledProp].toBool(); - -#if defined(Q_OS_WIN) - if (json.contains(jsonInfo.kIsTerminateSocketsProp) && json[jsonInfo.kIsTerminateSocketsProp].isBool()) - d->isTerminateSockets = json[jsonInfo.kIsTerminateSocketsProp].toBool(); -#endif - - if (json.contains(jsonInfo.kLanguageProp) && json[jsonInfo.kLanguageProp].isString()) - d->language = json[jsonInfo.kLanguageProp].toString(); - - if (json.contains(jsonInfo.kMacAddrSpoofingProp) && json[jsonInfo.kMacAddrSpoofingProp].isObject()) - d->macAddrSpoofing = types::MacAddrSpoofing(json[jsonInfo.kMacAddrSpoofingProp].toObject()); - - if (json.contains(jsonInfo.kPacketSizeProp) && json[jsonInfo.kPacketSizeProp].isObject()) - d->packetSize = types::PacketSize(json[jsonInfo.kPacketSizeProp].toObject()); - - if (json.contains(jsonInfo.kProxySettingsProp) && json[jsonInfo.kProxySettingsProp].isObject()) - d->proxySettings = types::ProxySettings(json[jsonInfo.kProxySettingsProp].toObject()); - - if (json.contains(jsonInfo.kUpdateChannelProp) && json[jsonInfo.kUpdateChannelProp].isDouble()) - d->updateChannel = static_cast(json[jsonInfo.kUpdateChannelProp].toInt()); - - if (json.contains(jsonInfo.kNetworkPreferredProtocolsProp) && json[jsonInfo.kNetworkPreferredProtocolsProp].isObject()) - { - QMap networkPreferredProtocols; - const QJsonObject protocolsObj = json[jsonInfo.kNetworkPreferredProtocolsProp].toObject(); - for (const QString& networkBase64 : protocolsObj.keys()) - { - if (protocolsObj[networkBase64].isObject()) - networkPreferredProtocols.insert(Utils::fromBase64(networkBase64), types::ConnectionSettings(protocolsObj[networkBase64].toObject())); - } - d->networkPreferredProtocols = networkPreferredProtocols; - } - - if (json.contains(jsonInfo.kNetworkLastKnownGoodProtocolsProp) && json[jsonInfo.kNetworkLastKnownGoodProtocolsProp].isObject()) - { - const QJsonObject protocolsObj = json[jsonInfo.kNetworkLastKnownGoodProtocolsProp].toObject(); - for (const QString& networkBase64 : protocolsObj.keys()) - { - const QString network = Utils::fromBase64(networkBase64); - if (protocolsObj[network].isObject()) - { - types::Protocol protocol; - uint port; - const QJsonObject protocolObj = protocolsObj[network].toObject(); - if (protocolObj.contains(jsonInfo.kProtocolProp) - && protocolObj.contains(jsonInfo.kValueProp) - && protocolObj[jsonInfo.kProtocolProp].isDouble() - && protocolObj[jsonInfo.kValueProp].isDouble()) - { - protocol = static_cast(protocolObj[jsonInfo.kProtocolProp].toInt()); - port = static_cast(protocolObj[jsonInfo.kValueProp].toInt()); - setNetworkLastKnownGoodProtocolPort(network, protocol, port); - } - } - } - } + d->fromJson(json); } void EngineSettings::saveToSettings() @@ -195,6 +107,11 @@ bool EngineSettings::loadFromSettings() d->language = LanguagesUtil::systemLanguage(); } +#ifdef CLI_ONLY + QSettings ini("Windscribe", "windscribe_cli"); + fromIni(ini); +#endif + return bLoaded; } @@ -446,10 +363,20 @@ bool EngineSettings::operator!=(const EngineSettings &other) const QJsonObject EngineSettings::toJson() const { auto json = d->toJson(); - json[d->jsonInfo.kVersionProp] = static_cast(versionForSerialization_); + json[kJsonVersionProp] = static_cast(versionForSerialization_); return json; } +void EngineSettings::fromIni(QSettings &settings) +{ + d->fromIni(settings); +} + +void EngineSettings::toIni(QSettings &settings) const +{ + d->toIni(settings); +} + QDebug operator<<(QDebug dbg, const EngineSettings &es) { QDebugStateSaver saver(dbg); @@ -466,7 +393,7 @@ QDebug operator<<(QDebug dbg, const EngineSettings &es) dbg << "proxySettings: " << es.d->proxySettings << "; "; dbg << "packetSize: " << es.d->packetSize << "; "; dbg << "macAddrSpoofing: " << es.d->macAddrSpoofing << "; "; - dbg << "dnsPolicy: " << DNS_POLICY_TYPE_ToString(es.d->dnsPolicy) << "; "; + dbg << "dnsPolicy: " << DNS_POLICY_TYPE_toString(es.d->dnsPolicy) << "; "; #ifdef Q_OS_WIN dbg << "tapAdapter: " << TAP_ADAPTER_TYPE_toString(es.d->tapAdapter) << "; "; #endif @@ -485,45 +412,195 @@ QDebug operator<<(QDebug dbg, const EngineSettings &es) return dbg; } +void EngineSettingsData::fromJson(const QJsonObject &json) +{ + if (json.contains(kJsonApiResolutionSettingsProp) && json[kJsonApiResolutionSettingsProp].isObject()) + apiResolutionSettings = types::ApiResolutionSettings(json[kJsonApiResolutionSettingsProp].toObject()); + + if (json.contains(kJsonConnectedDnsInfoProp) && json[kJsonConnectedDnsInfoProp].isObject()) + connectedDnsInfo = types::ConnectedDnsInfo(json[kJsonConnectedDnsInfoProp].toObject()); + + if (json.contains(kJsonConnectionSettingsProp) && json[kJsonConnectionSettingsProp].isObject()) + connectionSettings = types::ConnectionSettings(json[kJsonConnectionSettingsProp].toObject()); + + if (json.contains(kJsonCustomOvpnConfigsPathProp) && json[kJsonCustomOvpnConfigsPathProp].isString()) + customOvpnConfigsPath = Utils::fromBase64(json[kJsonCustomOvpnConfigsPathProp].toString()); + +#if defined(Q_OS_LINUX) + if (json.contains(kJsonDnsManagerProp) && json[kJsonDnsManagerProp].isDouble()) + dnsManager = static_cast(json[kJsonDnsManagerProp].toInt()); +#endif + + if (json.contains(kJsonDnsPolicyProp) && json[kJsonDnsPolicyProp].isDouble()) + dnsPolicy = static_cast(json[kJsonDnsPolicyProp].toInt()); + + if (json.contains(kJsonFirewallSettingsProp) && json[kJsonFirewallSettingsProp].isObject()) + firewallSettings = types::FirewallSettings(json[kJsonFirewallSettingsProp].toObject()); + + if (json.contains(kJsonIsAllowLanTrafficProp) && json[kJsonIsAllowLanTrafficProp].isBool()) + isAllowLanTraffic = json[kJsonIsAllowLanTrafficProp].toBool(); + + if (json.contains(kJsonIsAntiCensorshipProp) && json[kJsonIsAntiCensorshipProp].isBool()) + isAntiCensorship = json[kJsonIsAntiCensorshipProp].toBool(); + + if (json.contains(kJsonIsKeepAliveEnabledProp) && json[kJsonIsKeepAliveEnabledProp].isBool()) + isKeepAliveEnabled = json[kJsonIsKeepAliveEnabledProp].toBool(); + +#if defined(Q_OS_WIN) + if (json.contains(kJsonIsTerminateSocketsProp) && json[kJsonIsTerminateSocketsProp].isBool()) + isTerminateSockets = json[kJsonIsTerminateSocketsProp].toBool(); +#endif + + if (json.contains(kJsonLanguageProp) && json[kJsonLanguageProp].isString() + && LanguagesUtil::isSupportedLanguage(json[kJsonLanguageProp].toString())) { + language = json[kJsonLanguageProp].toString(); + } + + if (json.contains(kJsonMacAddrSpoofingProp) && json[kJsonMacAddrSpoofingProp].isObject()) + macAddrSpoofing = types::MacAddrSpoofing(json[kJsonMacAddrSpoofingProp].toObject()); + + if (json.contains(kJsonPacketSizeProp) && json[kJsonPacketSizeProp].isObject()) + packetSize = types::PacketSize(json[kJsonPacketSizeProp].toObject()); + + if (json.contains(kJsonProxySettingsProp) && json[kJsonProxySettingsProp].isObject()) + proxySettings = types::ProxySettings(json[kJsonProxySettingsProp].toObject()); + + if (json.contains(kJsonUpdateChannelProp) && json[kJsonUpdateChannelProp].isDouble()) + updateChannel = static_cast(json[kJsonUpdateChannelProp].toInt()); + + if (json.contains(kJsonNetworkPreferredProtocolsProp) && json[kJsonNetworkPreferredProtocolsProp].isObject()) { + QMap networkPreferredProtocols; + const QJsonObject protocolsObj = json[kJsonNetworkPreferredProtocolsProp].toObject(); + for (const QString& networkBase64 : protocolsObj.keys()) { + if (protocolsObj[networkBase64].isObject()) + networkPreferredProtocols.insert(Utils::fromBase64(networkBase64), types::ConnectionSettings(protocolsObj[networkBase64].toObject())); + } + networkPreferredProtocols = networkPreferredProtocols; + } + + if (json.contains(kJsonNetworkLastKnownGoodProtocolsProp) && json[kJsonNetworkLastKnownGoodProtocolsProp].isObject()) { + const QJsonObject protocolsObj = json[kJsonNetworkLastKnownGoodProtocolsProp].toObject(); + for (const QString& networkBase64 : protocolsObj.keys()) { + const QString network = Utils::fromBase64(networkBase64); + if (protocolsObj[network].isObject()) { + types::Protocol protocol; + uint port; + const QJsonObject protocolObj = protocolsObj[network].toObject(); + if (protocolObj.contains(kJsonProtocolProp) + && protocolObj.contains(kJsonValueProp) + && protocolObj[kJsonProtocolProp].isDouble() + && protocolObj[kJsonValueProp].isDouble()) + { + protocol = static_cast(protocolObj[kJsonProtocolProp].toInt()); + port = static_cast(protocolObj[kJsonValueProp].toInt()); + networkLastKnownGoodProtocols[network] = std::make_pair(protocol, port); + } + } + } + } +} + QJsonObject EngineSettingsData::toJson() const { QJsonObject json; - json[jsonInfo.kApiResolutionSettingsProp] = apiResolutionSettings.toJson(); - json[jsonInfo.kConnectedDnsInfoProp] = connectedDnsInfo.toJson(); - json[jsonInfo.kConnectionSettingsProp] = connectionSettings.toJson(); - json[jsonInfo.kCustomOvpnConfigsPathProp] = Utils::toBase64(customOvpnConfigsPath); - json[jsonInfo.kDnsPolicyProp] = static_cast(dnsPolicy); - json[jsonInfo.kFirewallSettingsProp] = firewallSettings.toJson(); - json[jsonInfo.kIsAllowLanTrafficProp] = isAllowLanTraffic; - json[jsonInfo.kIsAntiCensorshipProp] = isAntiCensorship; - json[jsonInfo.kIsIgnoreSslErrorsProp] = isIgnoreSslErrors; - json[jsonInfo.kIsKeepAliveEnabledProp] = isKeepAliveEnabled; - json[jsonInfo.kIsTerminateSocketsProp] = isTerminateSockets; - json[jsonInfo.kLanguageProp] = language; - json[jsonInfo.kMacAddrSpoofingProp] = macAddrSpoofing.toJson(); + json[kJsonApiResolutionSettingsProp] = apiResolutionSettings.toJson(); + json[kJsonConnectedDnsInfoProp] = connectedDnsInfo.toJson(); + json[kJsonConnectionSettingsProp] = connectionSettings.toJson(); + json[kJsonCustomOvpnConfigsPathProp] = Utils::toBase64(customOvpnConfigsPath); + json[kJsonDnsPolicyProp] = static_cast(dnsPolicy); + json[kJsonFirewallSettingsProp] = firewallSettings.toJson(); + json[kJsonIsAllowLanTrafficProp] = isAllowLanTraffic; + json[kJsonIsAntiCensorshipProp] = isAntiCensorship; + json[kJsonIsIgnoreSslErrorsProp] = isIgnoreSslErrors; + json[kJsonIsKeepAliveEnabledProp] = isKeepAliveEnabled; + json[kJsonIsTerminateSocketsProp] = isTerminateSockets; + json[kJsonLanguageProp] = language; + json[kJsonMacAddrSpoofingProp] = macAddrSpoofing.toJson(); QJsonObject networkLastKnownGoodProtocolsObj; for (const auto& key : networkLastKnownGoodProtocols.keys()) { auto value = networkLastKnownGoodProtocols[key]; QJsonObject innerObj; - innerObj[jsonInfo.kProtocolProp] = value.first.toInt(); - innerObj[jsonInfo.kValueProp] = static_cast(value.second); + innerObj[kJsonProtocolProp] = value.first.toInt(); + innerObj[kJsonValueProp] = static_cast(value.second); networkLastKnownGoodProtocolsObj[Utils::toBase64(key)] = innerObj; } - json[jsonInfo.kNetworkLastKnownGoodProtocolsProp] = networkLastKnownGoodProtocolsObj; + json[kJsonNetworkLastKnownGoodProtocolsProp] = networkLastKnownGoodProtocolsObj; QJsonObject networkPreferredProtocolsObj; for (const auto& key : networkPreferredProtocols.keys()) { networkPreferredProtocolsObj[Utils::toBase64(key)] = networkPreferredProtocols[key].toJson(); } - json[jsonInfo.kNetworkPreferredProtocolsProp] = networkPreferredProtocolsObj; + json[kJsonNetworkPreferredProtocolsProp] = networkPreferredProtocolsObj; - json[jsonInfo.kPacketSizeProp] = packetSize.toJson(); - json[jsonInfo.kProxySettingsProp] = proxySettings.toJson(); - json[jsonInfo.kTapAdapterProp] = static_cast(tapAdapter); - json[jsonInfo.kUpdateChannelProp] = static_cast(updateChannel); + json[kJsonPacketSizeProp] = packetSize.toJson(); + json[kJsonProxySettingsProp] = proxySettings.toJson(); + json[kJsonTapAdapterProp] = static_cast(tapAdapter); + json[kJsonUpdateChannelProp] = static_cast(updateChannel); return json; } +void EngineSettingsData::fromIni(QSettings &settings) +{ + QString lang = settings.value(kIniLanguageProp, language).toString(); + if (LanguagesUtil::isSupportedLanguage(lang)) { + language = lang; + } + + updateChannel = UPDATE_CHANNEL_fromString(settings.value(kIniUpdateChannelProp, UPDATE_CHANNEL_toString(updateChannel)).toString()); + + settings.beginGroup(QString("Networks")); + QMap networkPreferredProtocols; + for (const auto& key : settings.childKeys()) { + ConnectionSettings connSettings(settings, key); + networkPreferredProtocols.insert(key, connSettings); + } + settings.endGroup(); + + settings.beginGroup(QString("Connection")); + proxySettings.fromIni(settings); + firewallSettings.fromIni(settings); + connectionSettings.fromIni(settings); + packetSize.fromIni(settings); + connectedDnsInfo.fromIni(settings); + isAllowLanTraffic = settings.value(kIniIsAllowLanTrafficProp, isAllowLanTraffic).toBool(); + isAntiCensorship = settings.value(kIniIsAntiCensorshipProp, isAntiCensorship).toBool(); + settings.endGroup(); + + settings.beginGroup(QString("Advanced")); + apiResolutionSettings.fromIni(settings); + isIgnoreSslErrors = settings.value(kIniIsIgnoreSslErrorsProp, isIgnoreSslErrors).toBool(); + dnsPolicy = DNS_POLICY_TYPE_fromString(settings.value(kIniDnsPolicyProp, DNS_POLICY_TYPE_toString(dnsPolicy)).toString()); + settings.endGroup(); +} + +void EngineSettingsData::toIni(QSettings &settings) const +{ + settings.setValue(kIniLanguageProp, language); + settings.setValue(kIniUpdateChannelProp, UPDATE_CHANNEL_toString(updateChannel)); + + settings.beginGroup(QString("Networks")); + for (const auto& key : networkPreferredProtocols.keys()) { + networkPreferredProtocols[key].toIni(settings, key); + } + settings.endGroup(); + + settings.beginGroup(QString("Connection")); + proxySettings.toIni(settings); + firewallSettings.toIni(settings); + connectionSettings.toIni(settings); + packetSize.toIni(settings); + connectedDnsInfo.toIni(settings); + settings.setValue(kIniIsAllowLanTrafficProp, isAllowLanTraffic); + settings.setValue(kIniIsAntiCensorshipProp, isAntiCensorship); + settings.endGroup(); + + settings.beginGroup(QString("Advanced")); + apiResolutionSettings.toIni(settings); + settings.setValue(kIniIsIgnoreSslErrorsProp, isIgnoreSslErrors); + settings.setValue(kIniDnsPolicyProp, DNS_POLICY_TYPE_toString(dnsPolicy)); + settings.endGroup(); +} + } // types namespace diff --git a/client/common/types/enginesettings.h b/client/common/types/enginesettings.h index 4e9f71ae6..e60ea560e 100644 --- a/client/common/types/enginesettings.h +++ b/client/common/types/enginesettings.h @@ -21,32 +21,6 @@ namespace types { struct EngineSettingsData : public QSharedData { - struct JsonInfo { - const QString kApiResolutionSettingsProp = "apiResolutionSettings"; - const QString kConnectedDnsInfoProp = "connectedDnsInfo"; - const QString kConnectionSettingsProp = "connectionSettings"; - const QString kCustomOvpnConfigsPathProp = "customOvpnConfigsPath"; - const QString kDnsManagerProp = "dnsManager"; - const QString kDnsPolicyProp = "dnsPolicy"; - const QString kFirewallSettingsProp = "firewallSettings"; - const QString kIsAllowLanTrafficProp = "isAllowLanTraffic"; - const QString kIsAntiCensorshipProp = "isAntiCensorship"; - const QString kIsIgnoreSslErrorsProp = "isIgnoreSslErrors"; - const QString kIsKeepAliveEnabledProp = "isKeepAliveEnabled"; - const QString kIsTerminateSocketsProp = "isTerminateSockets"; - const QString kLanguageProp = "language"; - const QString kMacAddrSpoofingProp = "macAddrSpoofing"; - const QString kNetworkLastKnownGoodProtocolsProp = "networkLastKnownGoodProtocols"; - const QString kNetworkPreferredProtocolsProp = "networkPreferredProtocols"; - const QString kPacketSizeProp = "packetSize"; - const QString kProtocolProp = "protocol"; - const QString kProxySettingsProp = "proxySettings"; - const QString kTapAdapterProp = "tapAdapter"; - const QString kUpdateChannelProp = "updateChannel"; - const QString kValueProp = "value"; - const QString kVersionProp = "version"; - }; - EngineSettingsData() : updateChannel(UPDATE_CHANNEL_RELEASE), isIgnoreSslErrors(false), @@ -80,9 +54,42 @@ struct EngineSettingsData : public QSharedData QMap> networkLastKnownGoodProtocols; QString language; - JsonInfo jsonInfo; - + void fromJson(const QJsonObject &json); QJsonObject toJson() const; + void fromIni(QSettings &settings); + void toIni(QSettings &settings) const; + +private: + static const inline QString kIniDnsPolicyProp = "DNSPolicy"; + static const inline QString kIniIsAllowLanTrafficProp = "AllowLANTraffic"; + static const inline QString kIniIsAntiCensorshipProp = "CircumventCensorship"; + static const inline QString kIniIsIgnoreSslErrorsProp = "IgnoreSSLErrors"; + static const inline QString kIniIsKeepAliveEnabledProp = "ClientsideKeepalive"; + static const inline QString kIniLanguageProp = "Language"; + static const inline QString kIniUpdateChannelProp = "UpdateChannel"; + + static const inline QString kJsonApiResolutionSettingsProp = "apiResolutionSettings"; + static const inline QString kJsonConnectedDnsInfoProp = "connectedDnsInfo"; + static const inline QString kJsonConnectionSettingsProp = "connectionSettings"; + static const inline QString kJsonCustomOvpnConfigsPathProp = "customOvpnConfigsPath"; + static const inline QString kJsonDnsManagerProp = "dnsManager"; + static const inline QString kJsonDnsPolicyProp = "dnsPolicy"; + static const inline QString kJsonFirewallSettingsProp = "firewallSettings"; + static const inline QString kJsonIsAllowLanTrafficProp = "isAllowLanTraffic"; + static const inline QString kJsonIsAntiCensorshipProp = "isAntiCensorship"; + static const inline QString kJsonIsIgnoreSslErrorsProp = "isIgnoreSslErrors"; + static const inline QString kJsonIsKeepAliveEnabledProp = "isKeepAliveEnabled"; + static const inline QString kJsonIsTerminateSocketsProp = "isTerminateSockets"; + static const inline QString kJsonLanguageProp = "language"; + static const inline QString kJsonMacAddrSpoofingProp = "macAddrSpoofing"; + static const inline QString kJsonNetworkLastKnownGoodProtocolsProp = "networkLastKnownGoodProtocols"; + static const inline QString kJsonNetworkPreferredProtocolsProp = "networkPreferredProtocols"; + static const inline QString kJsonPacketSizeProp = "packetSize"; + static const inline QString kJsonProtocolProp = "protocol"; + static const inline QString kJsonProxySettingsProp = "proxySettings"; + static const inline QString kJsonTapAdapterProp = "tapAdapter"; + static const inline QString kJsonUpdateChannelProp = "updateChannel"; + static const inline QString kJsonValueProp = "value"; }; @@ -147,6 +154,8 @@ class EngineSettings bool operator==(const EngineSettings &other) const; bool operator!=(const EngineSettings &other) const; QJsonObject toJson() const; + void fromIni(QSettings &settings); + void toIni(QSettings & settings) const; friend QDebug operator<<(QDebug dbg, const EngineSettings &es); @@ -155,6 +164,8 @@ class EngineSettings private: QSharedDataPointer d; + static const inline QString kJsonVersionProp = "version"; + // for serialization static constexpr quint32 magic_ = 0x7745C2AE; static constexpr int versionForSerialization_ = 5; // should increment the version if the data format is changed diff --git a/client/common/types/enums.cpp b/client/common/types/enums.cpp index 3f5cac2c3..ed8a23600 100644 --- a/client/common/types/enums.cpp +++ b/client/common/types/enums.cpp @@ -17,86 +17,80 @@ const int typeIdWebSessionPurpose = qRegisterMetaType("WEB_ QString LOGIN_RET_toString(LOGIN_RET ret) { - if (ret == LOGIN_RET_SUCCESS) - { + if (ret == LOGIN_RET_SUCCESS) { return "SUCCESS"; - } - else if (ret == LOGIN_RET_NO_API_CONNECTIVITY) - { + } else if (ret == LOGIN_RET_NO_API_CONNECTIVITY) { return "NO_API_CONNECTIVITY"; - } - else if (ret == LOGIN_RET_NO_CONNECTIVITY) - { + } else if (ret == LOGIN_RET_NO_CONNECTIVITY) { return "NO_CONNECTIVITY"; - } - else if (ret == LOGIN_RET_INCORRECT_JSON) - { + } else if (ret == LOGIN_RET_INCORRECT_JSON) { return "INCORRECT_JSON"; - } - else if (ret == LOGIN_RET_BAD_USERNAME) - { + } else if (ret == LOGIN_RET_BAD_USERNAME) { return "BAD_USERNAME"; - } - else if (ret == LOGIN_RET_SSL_ERROR) - { + } else if (ret == LOGIN_RET_SSL_ERROR) { return "SSL_ERROR"; - } - else if (ret == LOGIN_RET_BAD_CODE2FA) - { + } else if (ret == LOGIN_RET_BAD_CODE2FA) { return "BAD_CODE2FA"; - } - else if (ret == LOGIN_RET_MISSING_CODE2FA) - { + } else if (ret == LOGIN_RET_MISSING_CODE2FA) { return "MISSING_CODE2FA"; - } - else if (ret == LOGIN_RET_ACCOUNT_DISABLED) - { + } else if (ret == LOGIN_RET_ACCOUNT_DISABLED) { return "ACCOUNT_DISABLED"; - } - else if (ret == LOGIN_RET_SESSION_INVALID) - { + } else if (ret == LOGIN_RET_SESSION_INVALID) { return "SESSION_INVALID"; - } - else if (ret == LOGIN_RET_RATE_LIMITED) - { + } else if (ret == LOGIN_RET_RATE_LIMITED) { return "RATE_LIMITED"; - } - else - { + } else { WS_ASSERT(false); return "UNKNOWN"; } } -QString DNS_POLICY_TYPE_ToString(DNS_POLICY_TYPE d) +DNS_POLICY_TYPE DNS_POLICY_TYPE_fromString(const QString &s) { - if (d == DNS_TYPE_OS_DEFAULT) - { - return QObject::tr("OS Default"); + if (s == "OS Default") { + return DNS_TYPE_OS_DEFAULT; + } else if (s == "OpenDNS") { + return DNS_TYPE_OPEN_DNS; + } else if (s == "Cloudflare") { + return DNS_TYPE_CLOUDFLARE; + } else if (s == "Google") { + return DNS_TYPE_GOOGLE; + } else if (s == "Control D") { + return DNS_TYPE_CONTROLD; + } else { + WS_ASSERT(false); + return DNS_TYPE_OS_DEFAULT; } - else if (d == DNS_TYPE_OPEN_DNS) - { +} + +QString DNS_POLICY_TYPE_toString(DNS_POLICY_TYPE d) +{ + if (d == DNS_TYPE_OS_DEFAULT) { + return QObject::tr("OS Default"); + } else if (d == DNS_TYPE_OPEN_DNS) { return "OpenDNS"; - } - else if (d == DNS_TYPE_CLOUDFLARE) - { + } else if (d == DNS_TYPE_CLOUDFLARE) { return "Cloudflare"; - } - else if (d == DNS_TYPE_GOOGLE) - { + } else if (d == DNS_TYPE_GOOGLE) { return "Google"; - } - else if (d == DNS_TYPE_CONTROLD) - { + } else if (d == DNS_TYPE_CONTROLD) { return "Control D"; - } - else - { + } else { WS_ASSERT(false); return QObject::tr("UNKNOWN"); } } +PROXY_SHARING_TYPE PROXY_SHARING_TYPE_fromString(const QString &s) +{ + if (s == "HTTP") return PROXY_SHARING_HTTP; + else if (s == "SOCKS") return PROXY_SHARING_SOCKS; + else { + WS_ASSERT(false); + return PROXY_SHARING_HTTP; + } +} + QString PROXY_SHARING_TYPE_toString(PROXY_SHARING_TYPE t) { if (t == PROXY_SHARING_HTTP) return "HTTP"; @@ -138,6 +132,17 @@ QString TAP_ADAPTER_TYPE_toString(TAP_ADAPTER_TYPE t) } } +FIREWALL_MODE FIREWALL_MODE_fromString(const QString &s) +{ + if (s == "Manual") return FIREWALL_MODE_MANUAL; + else if (s == "Auto") return FIREWALL_MODE_AUTOMATIC; + else if (s == "Always On") return FIREWALL_MODE_ALWAYS_ON; + else { + WS_ASSERT(false); + return FIREWALL_MODE_AUTOMATIC; + } +} + QString FIREWALL_MODE_toString(FIREWALL_MODE t) { if (t == FIREWALL_MODE_MANUAL) return QObject::tr("Manual"); @@ -158,6 +163,16 @@ QList> FIREWALL_MODE_toList() return l; } +FIREWALL_WHEN FIREWALL_WHEN_fromString(const QString &s) +{ + if (s == "Before Connection") return FIREWALL_WHEN_BEFORE_CONNECTION; + else if (s == "After Connection") return FIREWALL_WHEN_AFTER_CONNECTION; + else { + WS_ASSERT(false); + return FIREWALL_WHEN_BEFORE_CONNECTION; + } +} + QString FIREWALL_WHEN_toString(FIREWALL_WHEN t) { if (t == FIREWALL_WHEN_BEFORE_CONNECTION) return QObject::tr("Before Connection"); @@ -168,6 +183,28 @@ QString FIREWALL_WHEN_toString(FIREWALL_WHEN t) } } +NETWORK_TRUST_TYPE NETWORK_TRUST_TYPE_fromString(const QString &s) +{ + if (s == "Secured") return NETWORK_TRUST_SECURED; + else if (s == "Unsecured") return NETWORK_TRUST_UNSECURED; + else { + // NETWORK_TRUST_FORGET is a transitory state and should never be here + WS_ASSERT(false); + return NETWORK_TRUST_SECURED; + } +} + +QString NETWORK_TRUST_TYPE_toString(NETWORK_TRUST_TYPE t) +{ + if (t == NETWORK_TRUST_SECURED) return "Secured"; + else if (t == NETWORK_TRUST_UNSECURED) return "Unsecured"; + else { + // NETWORK_TRUST_FORGET is a transitory state and should never be here + WS_ASSERT(false); + return "Secured"; + } +} + QList> FIREWALL_WHEN_toList() { QList> l; @@ -184,6 +221,19 @@ QList> PROXY_SHARING_TYPE_toList() return l; } +PROXY_OPTION PROXY_OPTION_fromString(const QString &s) +{ + if (s == "None") return PROXY_OPTION_NONE; + else if (s == "Auto-detect") return PROXY_OPTION_AUTODETECT; + else if (s == "HTTP") return PROXY_OPTION_HTTP; + else if (s == "SOCKS") return PROXY_OPTION_SOCKS; + else { + WS_ASSERT(false); + return PROXY_OPTION_NONE; + } + +} + QString PROXY_OPTION_toString(PROXY_OPTION t) { if (t == PROXY_OPTION_NONE) return QObject::tr("None"); @@ -209,11 +259,11 @@ QList> PROXY_OPTION_toList() QList> DNS_POLICY_TYPE_toList() { QList> l; - l << qMakePair(DNS_POLICY_TYPE_ToString(DNS_TYPE_OS_DEFAULT), DNS_TYPE_OS_DEFAULT); - l << qMakePair(DNS_POLICY_TYPE_ToString(DNS_TYPE_OPEN_DNS), DNS_TYPE_OPEN_DNS); - l << qMakePair(DNS_POLICY_TYPE_ToString(DNS_TYPE_CLOUDFLARE), DNS_TYPE_CLOUDFLARE); - l << qMakePair(DNS_POLICY_TYPE_ToString(DNS_TYPE_GOOGLE), DNS_TYPE_GOOGLE); - l << qMakePair(DNS_POLICY_TYPE_ToString(DNS_TYPE_CONTROLD), DNS_TYPE_CONTROLD); + l << qMakePair(DNS_POLICY_TYPE_toString(DNS_TYPE_OS_DEFAULT), DNS_TYPE_OS_DEFAULT); + l << qMakePair(DNS_POLICY_TYPE_toString(DNS_TYPE_OPEN_DNS), DNS_TYPE_OPEN_DNS); + l << qMakePair(DNS_POLICY_TYPE_toString(DNS_TYPE_CLOUDFLARE), DNS_TYPE_CLOUDFLARE); + l << qMakePair(DNS_POLICY_TYPE_toString(DNS_TYPE_GOOGLE), DNS_TYPE_GOOGLE); + l << qMakePair(DNS_POLICY_TYPE_toString(DNS_TYPE_CONTROLD), DNS_TYPE_CONTROLD); return l; } @@ -247,6 +297,18 @@ QString UPDATE_CHANNEL_toString(UPDATE_CHANNEL t) } } +UPDATE_CHANNEL UPDATE_CHANNEL_fromString(const QString &s) +{ + if (s == "Release") return UPDATE_CHANNEL_RELEASE; + else if (s == "Beta") return UPDATE_CHANNEL_BETA; + else if (s == "Guinea Pig") return UPDATE_CHANNEL_GUINEA_PIG; + else if (s == "Internal") return UPDATE_CHANNEL_INTERNAL; + else { + WS_ASSERT(false); + return UPDATE_CHANNEL_RELEASE; + } +} + QList> UPDATE_CHANNEL_toList() { QList> l; @@ -269,6 +331,18 @@ QString DNS_MANAGER_TYPE_toString(DNS_MANAGER_TYPE t) } } +DNS_MANAGER_TYPE DNS_MANAGER_TYPE_fromString(const QString &s) +{ + if (s == "Auto") return DNS_MANAGER_AUTOMATIC; + else if (s == "resolvconf") return DNS_MANAGER_RESOLV_CONF; + else if (s == "systemd-resolved") return DNS_MANAGER_SYSTEMD_RESOLVED; + else if (s == "NetworkManager") return DNS_MANAGER_NETWORK_MANAGER; + else { + WS_ASSERT(false); + return DNS_MANAGER_AUTOMATIC; + } +} + QList> DNS_MANAGER_TYPE_toList() { QList> l; @@ -297,6 +371,37 @@ QString CONNECTED_DNS_TYPE_toString(CONNECTED_DNS_TYPE t) } } +CONNECTED_DNS_TYPE CONNECTED_DNS_TYPE_fromString(const QString &s) +{ + if (s == "Auto") { + return CONNECTED_DNS_TYPE_AUTO; + } + else if (s == "Custom") { + return CONNECTED_DNS_TYPE_CUSTOM; + } + else if (s == "Forced") { + return CONNECTED_DNS_TYPE_FORCED; + } + else { + WS_ASSERT(false); + return CONNECTED_DNS_TYPE_AUTO; + } +} + +SPLIT_TUNNELING_MODE SPLIT_TUNNELING_MODE_fromString(const QString &s) +{ + if (s == "Exclude") { + return SPLIT_TUNNELING_MODE_EXCLUDE; + } + else if (s == "Include") { + return SPLIT_TUNNELING_MODE_INCLUDE; + } + else { + WS_ASSERT(false); + return SPLIT_TUNNELING_MODE_EXCLUDE; + } +} + QString SPLIT_TUNNELING_MODE_toString(SPLIT_TUNNELING_MODE t) { if (t == SPLIT_TUNNELING_MODE_EXCLUDE) { @@ -354,3 +459,28 @@ QList> TRAY_ICON_COLOR_toList() l << qMakePair(TRAY_ICON_COLOR_toString(TRAY_ICON_COLOR_BLACK), TRAY_ICON_COLOR_BLACK); return l; } + +TOGGLE_MODE TOGGLE_MODE_fromString(const QString &s) +{ + if (s == "Auto") { + return TOGGLE_MODE_AUTO; + } else if (s == "Manual") { + return TOGGLE_MODE_MANUAL; + } else { + WS_ASSERT(false); + return TOGGLE_MODE_AUTO; + } +} + +QString TOGGLE_MODE_toString(TOGGLE_MODE t) +{ + if (t == TOGGLE_MODE_AUTO) { + return "Auto"; + } else if (t == TOGGLE_MODE_MANUAL) { + return "Manual"; + } else { + WS_ASSERT(false); + return "Auto"; + } +} + diff --git a/client/common/types/enums.h b/client/common/types/enums.h index f8848d49f..c08e78ad0 100644 --- a/client/common/types/enums.h +++ b/client/common/types/enums.h @@ -268,14 +268,36 @@ enum LOCATION_TAB { LOCATION_TAB_LAST = LOCATION_TAB_SEARCH_LOCATIONS }; +enum LOGIN_STATE { + LOGIN_STATE_LOGGED_OUT = 0, + LOGIN_STATE_LOGIN_ERROR, + LOGIN_STATE_LOGGING_IN, + LOGIN_STATE_LOGGED_IN, +}; + +enum TUNNEL_TEST_STATE { + TUNNEL_TEST_STATE_UNKNOWN = 0, + TUNNEL_TEST_STATE_SUCCESS, + TUNNEL_TEST_STATE_FAILURE +}; + +enum TOGGLE_MODE { + TOGGLE_MODE_AUTO = 0, + TOGGLE_MODE_MANUAL +}; + // utils for enums QString LOGIN_RET_toString(LOGIN_RET ret); -QString DNS_POLICY_TYPE_ToString(DNS_POLICY_TYPE d); +DNS_POLICY_TYPE DNS_POLICY_TYPE_fromString(const QString &s); +QString DNS_POLICY_TYPE_toString(DNS_POLICY_TYPE d); QList> DNS_POLICY_TYPE_toList(); +CONNECTED_DNS_TYPE CONNECTED_DNS_TYPE_fromString(const QString &s); QString CONNECTED_DNS_TYPE_toString(CONNECTED_DNS_TYPE t); +SPLIT_TUNNELING_MODE SPLIT_TUNNELING_MODE_fromString(const QString &s); QString SPLIT_TUNNELING_MODE_toString(SPLIT_TUNNELING_MODE t); +PROXY_SHARING_TYPE PROXY_SHARING_TYPE_fromString(const QString &s); QString PROXY_SHARING_TYPE_toString(PROXY_SHARING_TYPE t); QList> PROXY_SHARING_TYPE_toList(); @@ -287,18 +309,26 @@ QList> LATENCY_DISPLAY_TYPE_toList(); QString TAP_ADAPTER_TYPE_toString(TAP_ADAPTER_TYPE t); +FIREWALL_MODE FIREWALL_MODE_fromString(const QString &s); QString FIREWALL_MODE_toString(FIREWALL_MODE t); QList> FIREWALL_MODE_toList(); +FIREWALL_WHEN FIREWALL_WHEN_fromString(const QString &s); QString FIREWALL_WHEN_toString(FIREWALL_WHEN t); QList> FIREWALL_WHEN_toList(); +NETWORK_TRUST_TYPE NETWORK_TRUST_TYPE_fromString(const QString &s); +QString NETWORK_TRUST_TYPE_toString(NETWORK_TRUST_TYPE t); + +PROXY_OPTION PROXY_OPTION_fromString(const QString &s); QString PROXY_OPTION_toString(PROXY_OPTION t); QList> PROXY_OPTION_toList(); +UPDATE_CHANNEL UPDATE_CHANNEL_fromString(const QString &s); QString UPDATE_CHANNEL_toString(UPDATE_CHANNEL t); QList> UPDATE_CHANNEL_toList(); +DNS_MANAGER_TYPE DNS_MANAGER_TYPE_fromString(const QString &s); QString DNS_MANAGER_TYPE_toString(DNS_MANAGER_TYPE t); QList> DNS_MANAGER_TYPE_toList(); @@ -307,3 +337,6 @@ QList> APP_SKIN_toList(); QString TRAY_ICON_COLOR_toString(TRAY_ICON_COLOR c); QList> TRAY_ICON_COLOR_toList(); + +TOGGLE_MODE TOGGLE_MODE_fromString(const QString &s); +QString TOGGLE_MODE_toString(TOGGLE_MODE t); diff --git a/client/common/types/firewallsettings.h b/client/common/types/firewallsettings.h index ed96c935a..bb3218484 100644 --- a/client/common/types/firewallsettings.h +++ b/client/common/types/firewallsettings.h @@ -8,14 +8,6 @@ namespace types { struct FirewallSettings { - struct JsonInfo - { - JsonInfo& operator=(const JsonInfo&) { return *this; } - - const QString kModeProp = "mode"; - const QString kWhenProp = "when"; - }; - FirewallSettings() : mode(FIREWALL_MODE_AUTOMATIC), when(FIREWALL_WHEN_BEFORE_CONNECTION) @@ -23,16 +15,20 @@ struct FirewallSettings FirewallSettings(const QJsonObject &json) { - if (json.contains(jsonInfo.kModeProp) && json[jsonInfo.kModeProp].isDouble()) - mode = static_cast(json[jsonInfo.kModeProp].toInt()); + if (json.contains(kJsonModeProp) && json[kJsonModeProp].isDouble()) + mode = static_cast(json[kJsonModeProp].toInt()); + + if (json.contains(kJsonWhenProp) && json[kJsonWhenProp].isDouble()) + when = static_cast(json[kJsonWhenProp].toInt()); + } - if (json.contains(jsonInfo.kWhenProp) && json[jsonInfo.kWhenProp].isDouble()) - when = static_cast(json[jsonInfo.kWhenProp].toInt()); + FirewallSettings(const QSettings &settings) + { + fromIni(settings); } FIREWALL_MODE mode; FIREWALL_WHEN when; - JsonInfo jsonInfo; bool operator==(const FirewallSettings &other) const { @@ -48,11 +44,23 @@ struct FirewallSettings QJsonObject toJson() const { QJsonObject json; - json[jsonInfo.kModeProp] = static_cast(mode); - json[jsonInfo.kWhenProp] = static_cast(when); + json[kJsonModeProp] = static_cast(mode); + json[kJsonWhenProp] = static_cast(when); return json; } + void fromIni(const QSettings &settings) + { + mode = FIREWALL_MODE_fromString(settings.value(kIniModeProp, FIREWALL_MODE_toString(mode)).toString()); + when = FIREWALL_WHEN_fromString(settings.value(kIniWhenProp, FIREWALL_WHEN_toString(when)).toString()); + } + + void toIni(QSettings &settings) const + { + settings.setValue(kIniModeProp, FIREWALL_MODE_toString(mode)); + settings.setValue(kIniWhenProp, FIREWALL_WHEN_toString(when)); + } + friend QDataStream& operator <<(QDataStream &stream, const FirewallSettings &o) { stream << versionForSerialization_; @@ -84,8 +92,13 @@ struct FirewallSettings } private: - static constexpr quint32 versionForSerialization_ = 1; // should increment the version if the data format is changed + static const inline QString kIniModeProp = "FirewallMode"; + static const inline QString kIniWhenProp = "FirewallWhen"; + + static const inline QString kJsonModeProp = "mode"; + static const inline QString kJsonWhenProp = "when"; + static constexpr quint32 versionForSerialization_ = 1; // should increment the version if the data format is changed }; } // types namespace diff --git a/client/common/types/guisettings.h b/client/common/types/guisettings.h index 890f280b3..ae891efbc 100644 --- a/client/common/types/guisettings.h +++ b/client/common/types/guisettings.h @@ -1,8 +1,9 @@ #pragma once #include -#include #include +#include +#include #include "enums.h" #include "sharesecurehotspot.h" #include "shareproxygateway.h" @@ -13,31 +14,6 @@ namespace types { struct GuiSettings { - struct JsonInfo - { - JsonInfo& operator=(const JsonInfo&) { return *this; } - - const QString kAppSkinProp = "appSkin"; - const QString kBackgroundSettingsProp = "backgroundSettings"; - const QString kIsAutoConnectProp = "isAutoConnect"; - const QString kIsAutoSecureNetworksProp = "isAutoSecureNetworks"; - const QString kIsDockedToTrayProp = "isDockedToTray"; - const QString kIsHideFromDockProp = "isHideFromDock"; - const QString kIsLaunchOnStartupProp = "isLaunchOnStartup"; - const QString kIsMinimizeAndCloseToTrayProp = "isMinimizeAndCloseToTray"; - const QString kIsShowLocationHealthProp = "isShowLocationHealth"; - const QString kIsShowNotificationsProp = "isShowNotifications"; - const QString kIsStartMinimizedProp = "isStartMinimized"; - const QString kLatencyDisplayProp = "latencyDisplay"; - const QString kOrderLocationProp = "orderLocation"; - const QString kShareProxyGatewayProp = "shareProxyGateway"; - const QString kShareSecureHotspotProp = "shareSecureHotspot"; - const QString kSplitTunnelingProp = "splitTunneling"; - const QString kTrayIconColorProp = "trayIconColor"; - const QString kVersionProp = "version"; - }; - - JsonInfo jsonInfo; GuiSettings() = default; GuiSettings(const QJsonObject& json); @@ -86,27 +62,53 @@ struct GuiSettings return !(*this == other); } + void fromIni(QSettings &settings) + { + isLaunchOnStartup = settings.value(kIniIsLaunchOnStartupProp, isLaunchOnStartup).toBool(); + + settings.beginGroup(QString("Connection")); + isAutoConnect = settings.value(kIniIsAutoConnectProp, isAutoConnect).toBool(); + isAutoSecureNetworks = settings.value(kIniIsAutoSecureNetworksProp, isAutoSecureNetworks).toBool(); + + shareProxyGateway.fromIni(settings); + splitTunneling.fromIni(settings); + settings.endGroup(); + } + + void toIni(QSettings &settings) const + { + settings.setValue(kIniIsLaunchOnStartupProp, isLaunchOnStartup); + + settings.beginGroup(QString("Connection")); + settings.setValue(kIniIsAutoConnectProp, isAutoConnect); + settings.setValue(kIniIsAutoSecureNetworksProp, isAutoSecureNetworks); + + shareProxyGateway.toIni(settings); + splitTunneling.toIni(settings); + settings.endGroup(); + } + QJsonObject toJson() const { QJsonObject json; - json[jsonInfo.kAppSkinProp] = static_cast(appSkin); - json[jsonInfo.kBackgroundSettingsProp] = backgroundSettings.toJson(); - json[jsonInfo.kIsAutoConnectProp] = isAutoConnect; - json[jsonInfo.kIsAutoSecureNetworksProp] = isAutoSecureNetworks; - json[jsonInfo.kIsDockedToTrayProp] = isDockedToTray; - json[jsonInfo.kIsHideFromDockProp] = isHideFromDock; - json[jsonInfo.kIsLaunchOnStartupProp] = isLaunchOnStartup; - json[jsonInfo.kIsMinimizeAndCloseToTrayProp] = isMinimizeAndCloseToTray; - json[jsonInfo.kIsShowLocationHealthProp] = isShowLocationHealth; - json[jsonInfo.kIsShowNotificationsProp] = isShowNotifications; - json[jsonInfo.kIsStartMinimizedProp] = isStartMinimized; - json[jsonInfo.kLatencyDisplayProp] = static_cast(latencyDisplay); - json[jsonInfo.kOrderLocationProp] = static_cast(orderLocation); - json[jsonInfo.kShareProxyGatewayProp] = shareProxyGateway.toJson(); - json[jsonInfo.kShareSecureHotspotProp] = shareSecureHotspot.toJson(); - json[jsonInfo.kSplitTunnelingProp] = splitTunneling.toJson(); - json[jsonInfo.kTrayIconColorProp] = static_cast(trayIconColor); - json[jsonInfo.kVersionProp] = static_cast(versionForSerialization_); + json[kJsonAppSkinProp] = static_cast(appSkin); + json[kJsonBackgroundSettingsProp] = backgroundSettings.toJson(); + json[kJsonIsAutoConnectProp] = isAutoConnect; + json[kJsonIsAutoSecureNetworksProp] = isAutoSecureNetworks; + json[kJsonIsDockedToTrayProp] = isDockedToTray; + json[kJsonIsHideFromDockProp] = isHideFromDock; + json[kJsonIsLaunchOnStartupProp] = isLaunchOnStartup; + json[kJsonIsMinimizeAndCloseToTrayProp] = isMinimizeAndCloseToTray; + json[kJsonIsShowLocationHealthProp] = isShowLocationHealth; + json[kJsonIsShowNotificationsProp] = isShowNotifications; + json[kJsonIsStartMinimizedProp] = isStartMinimized; + json[kJsonLatencyDisplayProp] = static_cast(latencyDisplay); + json[kJsonOrderLocationProp] = static_cast(orderLocation); + json[kJsonShareProxyGatewayProp] = shareProxyGateway.toJson(); + json[kJsonShareSecureHotspotProp] = shareSecureHotspot.toJson(); + json[kJsonSplitTunnelingProp] = splitTunneling.toJson(); + json[kJsonTrayIconColorProp] = static_cast(trayIconColor); + json[kJsonVersionProp] = static_cast(versionForSerialization_); return json; } @@ -173,65 +175,88 @@ struct GuiSettings } private: - static constexpr quint32 versionForSerialization_ = 3; // should increment the version if the data format is changed + static const inline QString kIniIsAutoConnectProp = "Autoconnect"; + static const inline QString kIniIsAutoSecureNetworksProp = "AutosecureNetworks"; + static const inline QString kIniIsLaunchOnStartupProp = "LaunchOnStartup"; + static const inline QString kIniSplitTunnelingProp = "SplitTunneling"; + + static const inline QString kJsonAppSkinProp = "appSkin"; + static const inline QString kJsonBackgroundSettingsProp = "backgroundSettings"; + static const inline QString kJsonIsAutoConnectProp = "isAutoConnect"; + static const inline QString kJsonIsAutoSecureNetworksProp = "isAutoSecureNetworks"; + static const inline QString kJsonIsDockedToTrayProp = "isDockedToTray"; + static const inline QString kJsonIsHideFromDockProp = "isHideFromDock"; + static const inline QString kJsonIsLaunchOnStartupProp = "isLaunchOnStartup"; + static const inline QString kJsonIsMinimizeAndCloseToTrayProp = "isMinimizeAndCloseToTray"; + static const inline QString kJsonIsShowLocationHealthProp = "isShowLocationHealth"; + static const inline QString kJsonIsShowNotificationsProp = "isShowNotifications"; + static const inline QString kJsonIsStartMinimizedProp = "isStartMinimized"; + static const inline QString kJsonLatencyDisplayProp = "latencyDisplay"; + static const inline QString kJsonOrderLocationProp = "orderLocation"; + static const inline QString kJsonShareProxyGatewayProp = "shareProxyGateway"; + static const inline QString kJsonShareSecureHotspotProp = "shareSecureHotspot"; + static const inline QString kJsonSplitTunnelingProp = "splitTunneling"; + static const inline QString kJsonTrayIconColorProp = "trayIconColor"; + static const inline QString kJsonVersionProp = "version"; + static constexpr quint32 versionForSerialization_ = 3; // should increment the version if the data format is changed }; inline GuiSettings::GuiSettings(const QJsonObject &json) { - if (json.contains(jsonInfo.kAppSkinProp) && json[jsonInfo.kAppSkinProp].isDouble()) - appSkin = static_cast(json[jsonInfo.kAppSkinProp].toInt()); + if (json.contains(kJsonAppSkinProp) && json[kJsonAppSkinProp].isDouble()) + appSkin = static_cast(json[kJsonAppSkinProp].toInt()); - if (json.contains(jsonInfo.kBackgroundSettingsProp) && json[jsonInfo.kBackgroundSettingsProp].isObject()) - backgroundSettings = types::BackgroundSettings(json[jsonInfo.kBackgroundSettingsProp].toObject()); + if (json.contains(kJsonBackgroundSettingsProp) && json[kJsonBackgroundSettingsProp].isObject()) + backgroundSettings = types::BackgroundSettings(json[kJsonBackgroundSettingsProp].toObject()); - if (json.contains(jsonInfo.kIsAutoConnectProp) && json[jsonInfo.kIsAutoConnectProp].isBool()) - isAutoConnect = json[jsonInfo.kIsAutoConnectProp].toBool(); + if (json.contains(kJsonIsAutoConnectProp) && json[kJsonIsAutoConnectProp].isBool()) + isAutoConnect = json[kJsonIsAutoConnectProp].toBool(); #if !defined(Q_OS_LINUX) - if (json.contains(jsonInfo.kIsDockedToTrayProp) && json[jsonInfo.kIsDockedToTrayProp].isBool()) - isDockedToTray = json[jsonInfo.kIsDockedToTrayProp].toBool(); + if (json.contains(kJsonIsDockedToTrayProp) && json[kJsonIsDockedToTrayProp].isBool()) + isDockedToTray = json[kJsonIsDockedToTrayProp].toBool(); #endif #if defined(Q_OS_MAC) - if (json.contains(jsonInfo.kIsHideFromDockProp) && json[jsonInfo.kIsHideFromDockProp].isBool()) - isHideFromDock = json[jsonInfo.kIsHideFromDockProp].toBool(); + if (json.contains(kJsonIsHideFromDockProp) && json[kJsonIsHideFromDockProp].isBool()) + isHideFromDock = json[kJsonIsHideFromDockProp].toBool(); #endif - if (json.contains(jsonInfo.kIsLaunchOnStartupProp) && json[jsonInfo.kIsLaunchOnStartupProp].isBool()) - isLaunchOnStartup = json[jsonInfo.kIsLaunchOnStartupProp].toBool(); + if (json.contains(kJsonIsLaunchOnStartupProp) && json[kJsonIsLaunchOnStartupProp].isBool()) + isLaunchOnStartup = json[kJsonIsLaunchOnStartupProp].toBool(); - if (json.contains(jsonInfo.kIsMinimizeAndCloseToTrayProp) && json[jsonInfo.kIsMinimizeAndCloseToTrayProp].isBool()) - isMinimizeAndCloseToTray = json[jsonInfo.kIsMinimizeAndCloseToTrayProp].toBool(); + if (json.contains(kJsonIsMinimizeAndCloseToTrayProp) && json[kJsonIsMinimizeAndCloseToTrayProp].isBool()) + isMinimizeAndCloseToTray = json[kJsonIsMinimizeAndCloseToTrayProp].toBool(); - if (json.contains(jsonInfo.kIsShowLocationHealthProp) && json[jsonInfo.kIsShowLocationHealthProp].isBool()) - isShowLocationHealth = json[jsonInfo.kIsShowLocationHealthProp].toBool(); + if (json.contains(kJsonIsShowLocationHealthProp) && json[kJsonIsShowLocationHealthProp].isBool()) + isShowLocationHealth = json[kJsonIsShowLocationHealthProp].toBool(); - if (json.contains(jsonInfo.kIsShowNotificationsProp) && json[jsonInfo.kIsShowNotificationsProp].isBool()) - isShowNotifications = json[jsonInfo.kIsShowNotificationsProp].toBool(); + if (json.contains(kJsonIsShowNotificationsProp) && json[kJsonIsShowNotificationsProp].isBool()) + isShowNotifications = json[kJsonIsShowNotificationsProp].toBool(); - if (json.contains(jsonInfo.kIsStartMinimizedProp) && json[jsonInfo.kIsStartMinimizedProp].isBool()) - isStartMinimized = json[jsonInfo.kIsStartMinimizedProp].toBool(); + if (json.contains(kJsonIsStartMinimizedProp) && json[kJsonIsStartMinimizedProp].isBool()) + isStartMinimized = json[kJsonIsStartMinimizedProp].toBool(); - if (json.contains(jsonInfo.kLatencyDisplayProp) && json[jsonInfo.kLatencyDisplayProp].isDouble()) - latencyDisplay = static_cast(json[jsonInfo.kLatencyDisplayProp].toInt()); + if (json.contains(kJsonLatencyDisplayProp) && json[kJsonLatencyDisplayProp].isDouble()) + latencyDisplay = static_cast(json[kJsonLatencyDisplayProp].toInt()); - if (json.contains(jsonInfo.kOrderLocationProp) && json[jsonInfo.kOrderLocationProp].isDouble()) - orderLocation = static_cast(json[jsonInfo.kOrderLocationProp].toInt()); + if (json.contains(kJsonOrderLocationProp) && json[kJsonOrderLocationProp].isDouble()) + orderLocation = static_cast(json[kJsonOrderLocationProp].toInt()); #if defined(Q_OS_LINUX) - if (json.contains(jsonInfo.kTrayIconColorProp) && json[jsonInfo.kTrayIconColorProp].isDouble()) - trayIconColor = static_cast(json[jsonInfo.kTrayIconColorProp].toInt()); + if (json.contains(kJsonTrayIconColorProp) && json[kJsonTrayIconColorProp].isDouble()) + trayIconColor = static_cast(json[kJsonTrayIconColorProp].toInt()); #endif - if (json.contains(jsonInfo.kShareSecureHotspotProp) && json[jsonInfo.kShareSecureHotspotProp].isObject()) - shareSecureHotspot = types::ShareSecureHotspot(json[jsonInfo.kShareSecureHotspotProp].toObject()); + if (json.contains(kJsonShareSecureHotspotProp) && json[kJsonShareSecureHotspotProp].isObject()) + shareSecureHotspot = types::ShareSecureHotspot(json[kJsonShareSecureHotspotProp].toObject()); - if (json.contains(jsonInfo.kShareProxyGatewayProp) && json[jsonInfo.kShareProxyGatewayProp].isObject()) - shareProxyGateway = types::ShareProxyGateway(json[jsonInfo.kShareProxyGatewayProp].toObject()); + if (json.contains(kJsonShareProxyGatewayProp) && json[kJsonShareProxyGatewayProp].isObject()) + shareProxyGateway = types::ShareProxyGateway(json[kJsonShareProxyGatewayProp].toObject()); - if (json.contains(jsonInfo.kSplitTunnelingProp) && json[jsonInfo.kSplitTunnelingProp].isObject()) - splitTunneling = types::SplitTunneling(json[jsonInfo.kSplitTunnelingProp].toObject()); + if (json.contains(kJsonSplitTunnelingProp) && json[kJsonSplitTunnelingProp].isObject()) + splitTunneling = types::SplitTunneling(json[kJsonSplitTunnelingProp].toObject()); } } // types namespace diff --git a/client/common/types/macaddrspoofing.h b/client/common/types/macaddrspoofing.h index 056f856bb..503729b9f 100644 --- a/client/common/types/macaddrspoofing.h +++ b/client/common/types/macaddrspoofing.h @@ -11,17 +11,6 @@ namespace types { struct MacAddrSpoofing { - struct JsonInfo - { - JsonInfo& operator=(const JsonInfo&) { return *this; } - - const QString kIsEnabledProp = "isEnabled"; - const QString kMacAddressProp = "macAddress"; - const QString kIsAutoRotateProp = "isAutoRotate"; - const QString kSelectedNetworkInterfaceProp = "selectedNetworkInterface"; - const QString kNetworkInterfacesProp = "networkInterfaces"; - }; - MacAddrSpoofing() : isEnabled(false), isAutoRotate(false) @@ -29,8 +18,8 @@ struct MacAddrSpoofing MacAddrSpoofing(const QJsonObject &json) { - if (json.contains(jsonInfo.kIsEnabledProp) && json[jsonInfo.kIsEnabledProp].isBool()) { - isEnabled = json[jsonInfo.kIsEnabledProp].toBool(); + if (json.contains(kJsonIsEnabledProp) && json[kJsonIsEnabledProp].isBool()) { + isEnabled = json[kJsonIsEnabledProp].toBool(); #ifdef Q_OS_MAC // MacOS 14.4 does not support this feature if (MacUtils::isOsVersionAtLeast(14, 4)) { @@ -39,18 +28,18 @@ struct MacAddrSpoofing #endif } - if (json.contains(jsonInfo.kMacAddressProp) && json[jsonInfo.kMacAddressProp].isString()) - macAddress = json[jsonInfo.kMacAddressProp].toString(); + if (json.contains(kJsonMacAddressProp) && json[kJsonMacAddressProp].isString()) + macAddress = json[kJsonMacAddressProp].toString(); - if (json.contains(jsonInfo.kIsAutoRotateProp) && json[jsonInfo.kIsAutoRotateProp].isBool()) - isAutoRotate = json[jsonInfo.kIsAutoRotateProp].toBool(); + if (json.contains(kJsonIsAutoRotateProp) && json[kJsonIsAutoRotateProp].isBool()) + isAutoRotate = json[kJsonIsAutoRotateProp].toBool(); - if (json.contains(jsonInfo.kSelectedNetworkInterfaceProp) && json[jsonInfo.kSelectedNetworkInterfaceProp].isObject()) - selectedNetworkInterface = NetworkInterface(json[jsonInfo.kSelectedNetworkInterfaceProp].toObject()); + if (json.contains(kJsonSelectedNetworkInterfaceProp) && json[kJsonSelectedNetworkInterfaceProp].isObject()) + selectedNetworkInterface = NetworkInterface(json[kJsonSelectedNetworkInterfaceProp].toObject()); - if (json.contains(jsonInfo.kNetworkInterfacesProp) && json[jsonInfo.kNetworkInterfacesProp].isArray()) + if (json.contains(kJsonNetworkInterfacesProp) && json[kJsonNetworkInterfacesProp].isArray()) { - QJsonArray networkInterfacesArray = json[jsonInfo.kNetworkInterfacesProp].toArray(); + QJsonArray networkInterfacesArray = json[kJsonNetworkInterfacesProp].toArray(); networkInterfaces.clear(); for (const QJsonValue &ifaceValue : networkInterfacesArray) { @@ -68,7 +57,6 @@ struct MacAddrSpoofing bool isAutoRotate; NetworkInterface selectedNetworkInterface; QVector networkInterfaces; - JsonInfo jsonInfo; bool operator==(const MacAddrSpoofing &other) const { @@ -88,17 +76,17 @@ struct MacAddrSpoofing { QJsonObject json; - json[jsonInfo.kIsEnabledProp] = isEnabled; - json[jsonInfo.kMacAddressProp] = macAddress; - json[jsonInfo.kIsAutoRotateProp] = isAutoRotate; - json[jsonInfo.kSelectedNetworkInterfaceProp] = selectedNetworkInterface.toJson(); + json[kJsonIsEnabledProp] = isEnabled; + json[kJsonMacAddressProp] = macAddress; + json[kJsonIsAutoRotateProp] = isAutoRotate; + json[kJsonSelectedNetworkInterfaceProp] = selectedNetworkInterface.toJson(); if (!networkInterfaces.isEmpty()) { QJsonArray networkInterfacesArray; for (const NetworkInterface& iface : networkInterfaces) { networkInterfacesArray.append(iface.toJson()); } - json[jsonInfo.kNetworkInterfacesProp] = networkInterfacesArray; + json[kJsonNetworkInterfacesProp] = networkInterfacesArray; } return json; @@ -143,6 +131,12 @@ struct MacAddrSpoofing } private: + const static inline QString kJsonIsEnabledProp = "isEnabled"; + const static inline QString kJsonMacAddressProp = "macAddress"; + const static inline QString kJsonIsAutoRotateProp = "isAutoRotate"; + const static inline QString kJsonSelectedNetworkInterfaceProp = "selectedNetworkInterface"; + const static inline QString kJsonNetworkInterfacesProp = "networkInterfaces"; + static constexpr quint32 versionForSerialization_ = 1; // should increment the version if the data format is changed }; diff --git a/client/common/types/networkinterface.h b/client/common/types/networkinterface.h index 3a88cf5c3..4c5eaf7f0 100644 --- a/client/common/types/networkinterface.h +++ b/client/common/types/networkinterface.h @@ -10,29 +10,6 @@ namespace types { struct NetworkInterface { - struct JsonInfo - { - JsonInfo& operator=(const JsonInfo&) { return *this; } - - const QString kInterfaceIndexProp = "interfaceIndex"; - const QString kInterfaceNameProp = "interfaceName"; - const QString kInterfaceGuidProp = "interfaceGuid"; - const QString kNetworkOrSsidProp = "networkOrSsid"; - const QString kInterfaceTypeProp = "interfaceType"; - const QString kTrustTypeProp = "trustType"; - const QString kActiveProp = "active"; - const QString kFriendlyNameProp = "friendlyName"; - const QString kRequestedProp = "requested"; - const QString kMetricProp = "metric"; - const QString kPhysicalAddressProp = "physicalAddress"; - const QString kMtuProp = "mtu"; - const QString kStateProp = "state"; - const QString kDwTypeProp = "dwType"; - const QString kDeviceNameProp = "deviceName"; - const QString kConnectorPresentProp = "connectorPresent"; - const QString kEndPointInterfaceProp = "endPointInterface"; - }; - // defaults NetworkInterface() : interfaceIndex(-1), @@ -50,56 +27,56 @@ struct NetworkInterface NetworkInterface(const QJsonObject &json) { - if (json.contains(jsonInfo.kInterfaceIndexProp) && json[jsonInfo.kInterfaceIndexProp].isDouble()) - interfaceIndex = static_cast(json[jsonInfo.kInterfaceIndexProp].toDouble(-1)); + if (json.contains(kJsonInterfaceIndexProp) && json[kJsonInterfaceIndexProp].isDouble()) + interfaceIndex = static_cast(json[kJsonInterfaceIndexProp].toDouble(-1)); - if (json.contains(jsonInfo.kInterfaceNameProp) && json[jsonInfo.kInterfaceNameProp].isString()) - interfaceName = fromBase64(json[jsonInfo.kInterfaceNameProp].toString()); + if (json.contains(kJsonInterfaceNameProp) && json[kJsonInterfaceNameProp].isString()) + interfaceName = fromBase64(json[kJsonInterfaceNameProp].toString()); - if (json.contains(jsonInfo.kInterfaceGuidProp) && json[jsonInfo.kInterfaceGuidProp].isString()) - interfaceGuid = fromBase64(json[jsonInfo.kInterfaceGuidProp].toString()); + if (json.contains(kJsonInterfaceGuidProp) && json[kJsonInterfaceGuidProp].isString()) + interfaceGuid = fromBase64(json[kJsonInterfaceGuidProp].toString()); - if (json.contains(jsonInfo.kNetworkOrSsidProp) && json[jsonInfo.kNetworkOrSsidProp].isString()) - networkOrSsid = fromBase64(json[jsonInfo.kNetworkOrSsidProp].toString()); + if (json.contains(kJsonNetworkOrSsidProp) && json[kJsonNetworkOrSsidProp].isString()) + networkOrSsid = fromBase64(json[kJsonNetworkOrSsidProp].toString()); - if (json.contains(jsonInfo.kInterfaceTypeProp) && json[jsonInfo.kInterfaceTypeProp].isDouble()) - interfaceType = static_cast(json[jsonInfo.kInterfaceTypeProp].toInt(NETWORK_INTERFACE_NONE)); + if (json.contains(kJsonInterfaceTypeProp) && json[kJsonInterfaceTypeProp].isDouble()) + interfaceType = static_cast(json[kJsonInterfaceTypeProp].toInt(NETWORK_INTERFACE_NONE)); - if (json.contains(jsonInfo.kTrustTypeProp) && json[jsonInfo.kTrustTypeProp].isDouble()) - trustType = static_cast(json[jsonInfo.kTrustTypeProp].toInt(NETWORK_TRUST_SECURED)); + if (json.contains(kJsonTrustTypeProp) && json[kJsonTrustTypeProp].isDouble()) + trustType = static_cast(json[kJsonTrustTypeProp].toInt(NETWORK_TRUST_SECURED)); - if (json.contains(jsonInfo.kActiveProp) && json[jsonInfo.kActiveProp].isBool()) - active = json[jsonInfo.kActiveProp].toBool(); + if (json.contains(kJsonActiveProp) && json[kJsonActiveProp].isBool()) + active = json[kJsonActiveProp].toBool(); - if (json.contains(jsonInfo.kFriendlyNameProp) && json[jsonInfo.kFriendlyNameProp].isString()) - friendlyName = fromBase64(json[jsonInfo.kFriendlyNameProp].toString()); + if (json.contains(kJsonFriendlyNameProp) && json[kJsonFriendlyNameProp].isString()) + friendlyName = fromBase64(json[kJsonFriendlyNameProp].toString()); - if (json.contains(jsonInfo.kRequestedProp) && json[jsonInfo.kRequestedProp].isBool()) - requested = json[jsonInfo.kRequestedProp].toBool(); + if (json.contains(kJsonRequestedProp) && json[kJsonRequestedProp].isBool()) + requested = json[kJsonRequestedProp].toBool(); - if (json.contains(jsonInfo.kMetricProp) && json[jsonInfo.kMetricProp].isDouble()) - metric = static_cast(json[jsonInfo.kMetricProp].toDouble(100)); + if (json.contains(kJsonMetricProp) && json[kJsonMetricProp].isDouble()) + metric = static_cast(json[kJsonMetricProp].toDouble(100)); - if (json.contains(jsonInfo.kPhysicalAddressProp) && json[jsonInfo.kPhysicalAddressProp].isString()) - physicalAddress = fromBase64(json[jsonInfo.kPhysicalAddressProp].toString()); + if (json.contains(kJsonPhysicalAddressProp) && json[kJsonPhysicalAddressProp].isString()) + physicalAddress = fromBase64(json[kJsonPhysicalAddressProp].toString()); - if (json.contains(jsonInfo.kMtuProp) && json[jsonInfo.kMtuProp].isDouble()) - mtu = static_cast(json[jsonInfo.kMtuProp].toDouble(1470)); + if (json.contains(kJsonMtuProp) && json[kJsonMtuProp].isDouble()) + mtu = static_cast(json[kJsonMtuProp].toDouble(1470)); - if (json.contains(jsonInfo.kStateProp) && json[jsonInfo.kStateProp].isDouble()) - state = static_cast(json[jsonInfo.kStateProp].toDouble(0)); + if (json.contains(kJsonStateProp) && json[kJsonStateProp].isDouble()) + state = static_cast(json[kJsonStateProp].toDouble(0)); - if (json.contains(jsonInfo.kDwTypeProp) && json[jsonInfo.kDwTypeProp].isDouble()) - dwType = static_cast(json[jsonInfo.kDwTypeProp].toDouble(0)); + if (json.contains(kJsonDwTypeProp) && json[kJsonDwTypeProp].isDouble()) + dwType = static_cast(json[kJsonDwTypeProp].toDouble(0)); - if (json.contains(jsonInfo.kDeviceNameProp) && json[jsonInfo.kDeviceNameProp].isString()) - deviceName = fromBase64(json[jsonInfo.kDeviceNameProp].toString()); + if (json.contains(kJsonDeviceNameProp) && json[kJsonDeviceNameProp].isString()) + deviceName = fromBase64(json[kJsonDeviceNameProp].toString()); - if (json.contains(jsonInfo.kConnectorPresentProp) && json[jsonInfo.kConnectorPresentProp].isBool()) - connectorPresent = json[jsonInfo.kConnectorPresentProp].toBool(); + if (json.contains(kJsonConnectorPresentProp) && json[kJsonConnectorPresentProp].isBool()) + connectorPresent = json[kJsonConnectorPresentProp].toBool(); - if (json.contains(jsonInfo.kEndPointInterfaceProp) && json[jsonInfo.kEndPointInterfaceProp].isBool()) - endPointInterface = json[jsonInfo.kEndPointInterfaceProp].toBool(); + if (json.contains(kJsonEndPointInterfaceProp) && json[kJsonEndPointInterfaceProp].isBool()) + endPointInterface = json[kJsonEndPointInterfaceProp].toBool(); } int interfaceIndex; @@ -119,7 +96,6 @@ struct NetworkInterface QString deviceName; bool connectorPresent; bool endPointInterface; - JsonInfo jsonInfo; bool operator==(const NetworkInterface &other) const { @@ -179,23 +155,23 @@ struct NetworkInterface QJsonObject toJson() const { QJsonObject json; - json[jsonInfo.kInterfaceIndexProp] = interfaceIndex; - json[jsonInfo.kInterfaceNameProp] = toBase64(interfaceName); - json[jsonInfo.kInterfaceGuidProp] = toBase64(interfaceGuid); - json[jsonInfo.kNetworkOrSsidProp] = toBase64(networkOrSsid); - json[jsonInfo.kInterfaceTypeProp] = static_cast(interfaceType); - json[jsonInfo.kTrustTypeProp] = static_cast(trustType); - json[jsonInfo.kActiveProp] = active; - json[jsonInfo.kFriendlyNameProp] = toBase64(friendlyName); - json[jsonInfo.kRequestedProp] = requested; - json[jsonInfo.kMetricProp] = metric; - json[jsonInfo.kPhysicalAddressProp] = toBase64(physicalAddress); - json[jsonInfo.kMtuProp] = mtu; - json[jsonInfo.kStateProp] = state; - json[jsonInfo.kDwTypeProp] = dwType; - json[jsonInfo.kDeviceNameProp] = toBase64(deviceName); - json[jsonInfo.kConnectorPresentProp] = connectorPresent; - json[jsonInfo.kEndPointInterfaceProp] = endPointInterface; + json[kJsonInterfaceIndexProp] = interfaceIndex; + json[kJsonInterfaceNameProp] = toBase64(interfaceName); + json[kJsonInterfaceGuidProp] = toBase64(interfaceGuid); + json[kJsonNetworkOrSsidProp] = toBase64(networkOrSsid); + json[kJsonInterfaceTypeProp] = static_cast(interfaceType); + json[kJsonTrustTypeProp] = static_cast(trustType); + json[kJsonActiveProp] = active; + json[kJsonFriendlyNameProp] = toBase64(friendlyName); + json[kJsonRequestedProp] = requested; + json[kJsonMetricProp] = metric; + json[kJsonPhysicalAddressProp] = toBase64(physicalAddress); + json[kJsonMtuProp] = mtu; + json[kJsonStateProp] = state; + json[kJsonDwTypeProp] = dwType; + json[kJsonDeviceNameProp] = toBase64(deviceName); + json[kJsonConnectorPresentProp] = connectorPresent; + json[kJsonEndPointInterfaceProp] = endPointInterface; return json; } @@ -249,6 +225,24 @@ struct NetworkInterface } private: + static const inline QString kJsonInterfaceIndexProp = "interfaceIndex"; + static const inline QString kJsonInterfaceNameProp = "interfaceName"; + static const inline QString kJsonInterfaceGuidProp = "interfaceGuid"; + static const inline QString kJsonNetworkOrSsidProp = "networkOrSsid"; + static const inline QString kJsonInterfaceTypeProp = "interfaceType"; + static const inline QString kJsonTrustTypeProp = "trustType"; + static const inline QString kJsonActiveProp = "active"; + static const inline QString kJsonFriendlyNameProp = "friendlyName"; + static const inline QString kJsonRequestedProp = "requested"; + static const inline QString kJsonMetricProp = "metric"; + static const inline QString kJsonPhysicalAddressProp = "physicalAddress"; + static const inline QString kJsonMtuProp = "mtu"; + static const inline QString kJsonStateProp = "state"; + static const inline QString kJsonDwTypeProp = "dwType"; + static const inline QString kJsonDeviceNameProp = "deviceName"; + static const inline QString kJsonConnectorPresentProp = "connectorPresent"; + static const inline QString kJsonEndPointInterfaceProp = "endPointInterface"; + static constexpr quint32 versionForSerialization_ = 1; // should increment the version if the data format is changed }; diff --git a/client/common/types/packetsize.h b/client/common/types/packetsize.h index a3df509de..222af2598 100644 --- a/client/common/types/packetsize.h +++ b/client/common/types/packetsize.h @@ -3,19 +3,12 @@ #include "types/enums.h" #include +#include namespace types { struct PacketSize { - struct JsonInfo - { - JsonInfo& operator=(const JsonInfo&) { return *this; } - - const QString kIsAutomaticProp = "isAutomatic"; - const QString kMTUProp = "mtu"; - }; - PacketSize() : isAutomatic(true), mtu(-1) // -1 not set @@ -23,16 +16,20 @@ struct PacketSize PacketSize(const QJsonObject &json) { - if (json.contains(jsonInfo.kIsAutomaticProp) && json[jsonInfo.kIsAutomaticProp].isBool()) - isAutomatic = json[jsonInfo.kIsAutomaticProp].toBool(); - - if (json.contains(jsonInfo.kMTUProp) && json[jsonInfo.kMTUProp].isDouble()) - mtu = json[jsonInfo.kMTUProp].toInt(); + if (json.contains(kJsonIsAutomaticProp) && json[kJsonIsAutomaticProp].isBool()) + isAutomatic = json[kJsonIsAutomaticProp].toBool(); + + if (json.contains(kJsonMTUProp) && json[kJsonMTUProp].isDouble()) { + int mtuValue = json[kJsonMTUProp].toInt(); + // MTU is unset, or must be a minimum of 68 per RFC 791 and can't exceed maximum IP packet size of 65535 + if (mtuValue < 0 || (mtuValue >= 68 && mtuValue < 65536)) { + mtu = mtuValue; + } + } } bool isAutomatic; int mtu; - JsonInfo jsonInfo; bool operator==(const PacketSize &other) const { @@ -44,11 +41,29 @@ struct PacketSize return !(*this == other); } + void fromIni(const QSettings &settings) + { + QString prevMode = TOGGLE_MODE_toString(isAutomatic ? TOGGLE_MODE_AUTO : TOGGLE_MODE_MANUAL); + TOGGLE_MODE mode = TOGGLE_MODE_fromString(settings.value(kJsonIsAutomaticProp, prevMode).toString()); + isAutomatic = (mode == TOGGLE_MODE_AUTO); + int mtuValue = settings.value(kJsonMTUProp, mtu).toInt(); + // MTU is unset, or must be a minimum of 68 per RFC 791 and can't exceed maximum IP packet size of 65535 + if (mtuValue < 0 || (mtuValue >= 68 && mtuValue < 65536)) { + mtu = mtuValue; + } + } + + void toIni(QSettings &settings) const + { + settings.setValue(kIniIsAutomaticProp, TOGGLE_MODE_toString(isAutomatic ? TOGGLE_MODE_AUTO : TOGGLE_MODE_MANUAL)); + settings.setValue(kIniMTUProp, mtu); + } + QJsonObject toJson() const { QJsonObject json; - json[jsonInfo.kIsAutomaticProp] = isAutomatic; - json[jsonInfo.kMTUProp] = mtu; + json[kJsonIsAutomaticProp] = isAutomatic; + json[kJsonMTUProp] = mtu; return json; } @@ -83,6 +98,12 @@ struct PacketSize } private: + static const inline QString kIniIsAutomaticProp = "PacketSizeMode"; + static const inline QString kIniMTUProp = "PacketSizeMTU"; + + static const inline QString kJsonIsAutomaticProp = "isAutomatic"; + static const inline QString kJsonMTUProp = "mtu"; + static constexpr quint32 versionForSerialization_ = 1; // should increment the version if the data format is changed }; diff --git a/client/common/types/proxysettings.cpp b/client/common/types/proxysettings.cpp index e65cbd8b8..e836a989a 100644 --- a/client/common/types/proxysettings.cpp +++ b/client/common/types/proxysettings.cpp @@ -22,30 +22,51 @@ ProxySettings::ProxySettings(PROXY_OPTION option, const QString &address, uint p ProxySettings::ProxySettings(const QJsonObject &json) { - if (json.contains(jsonInfo_.kOptionProp) && json[jsonInfo_.kOptionProp].isDouble()) - option_ = static_cast(json[jsonInfo_.kOptionProp].toInt()); + if (json.contains(kJsonOptionProp) && json[kJsonOptionProp].isDouble()) + option_ = static_cast(json[kJsonOptionProp].toInt()); - if (json.contains(jsonInfo_.kAddressProp) && json[jsonInfo_.kAddressProp].isString()) - address_ = json[jsonInfo_.kAddressProp].toString(); + if (json.contains(kJsonAddressProp) && json[kJsonAddressProp].isString()) + address_ = json[kJsonAddressProp].toString(); - if (json.contains(jsonInfo_.kPortProp) && json[jsonInfo_.kPortProp].isDouble()) - port_ = static_cast(json[jsonInfo_.kPortProp].toInt()); + if (json.contains(kJsonPortProp) && json[kJsonPortProp].isDouble()) + port_ = static_cast(json[kJsonPortProp].toInt()); - if (json.contains(jsonInfo_.kUsernameProp) && json[jsonInfo_.kUsernameProp].isString()) - username_ = json[jsonInfo_.kUsernameProp].toString(); + if (json.contains(kJsonUsernameProp) && json[kJsonUsernameProp].isString()) + username_ = json[kJsonUsernameProp].toString(); - if (json.contains(jsonInfo_.kPasswordProp) && json[jsonInfo_.kPasswordProp].isString()) - password_ = Utils::fromBase64(json[jsonInfo_.kPasswordProp].toString()); + if (json.contains(kJsonPasswordProp) && json[kJsonPasswordProp].isString()) + password_ = Utils::fromBase64(json[kJsonPasswordProp].toString()); +} + +void ProxySettings::fromIni(const QSettings &settings) +{ + option_ = PROXY_OPTION_fromString(settings.value(kIniOptionProp, PROXY_OPTION_toString(option_)).toString()); + address_ = settings.value(kIniAddressProp).toString(); + uint port = settings.value(kIniPortProp, 0).toUInt(); + if (port < 65536) { + port_ = port; + } + username_ = settings.value(kIniUsernameProp).toString(); + password_ = settings.value(kIniPasswordProp).toString(); +} + +void ProxySettings::toIni(QSettings &settings) const +{ + settings.setValue(kIniOptionProp, PROXY_OPTION_toString(option_)); + settings.setValue(kIniAddressProp, address_); + settings.setValue(kIniPortProp, static_cast(port_)); + settings.setValue(kIniUsernameProp, username_); + settings.setValue(kIniPasswordProp, password_); } QJsonObject ProxySettings::toJson() const { QJsonObject json; - json[jsonInfo_.kOptionProp] = static_cast(option_); - json[jsonInfo_.kAddressProp] = address_; - json[jsonInfo_.kPortProp] = static_cast(port_); - json[jsonInfo_.kUsernameProp] = username_; - json[jsonInfo_.kPasswordProp] = Utils::toBase64(password_); + json[kJsonOptionProp] = static_cast(option_); + json[kJsonAddressProp] = address_; + json[kJsonPortProp] = static_cast(port_); + json[kJsonUsernameProp] = username_; + json[kJsonPasswordProp] = Utils::toBase64(password_); return json; } diff --git a/client/common/types/proxysettings.h b/client/common/types/proxysettings.h index 43937c892..a6f70cdde 100644 --- a/client/common/types/proxysettings.h +++ b/client/common/types/proxysettings.h @@ -1,9 +1,9 @@ #pragma once +#include +#include #include #include -#include -#include #include "enums.h" namespace types { @@ -11,18 +11,6 @@ namespace types { class ProxySettings { public: - - struct JsonInfo - { - JsonInfo& operator=(const JsonInfo&) { return *this; } - - const QString kOptionProp = "option"; - const QString kAddressProp = "address"; - const QString kPortProp = "port"; - const QString kUsernameProp = "username"; - const QString kPasswordProp = "password"; - }; - ProxySettings(); ProxySettings(PROXY_OPTION option, const QString &address, uint port, const QString &password, const QString &username); ProxySettings(const QJsonObject &json); @@ -57,6 +45,8 @@ class ProxySettings return !(*this == other); } + void fromIni(const QSettings &settings); + void toIni(QSettings &settings) const; QJsonObject toJson() const; friend QDataStream& operator <<(QDataStream &stream, const ProxySettings &o); @@ -70,7 +60,18 @@ class ProxySettings uint port_; QString username_; QString password_; - JsonInfo jsonInfo_; + + static const inline QString kIniOptionProp = "ProxyMode"; + static const inline QString kIniAddressProp = "ProxyAddress"; + static const inline QString kIniPortProp = "ProxyPort"; + static const inline QString kIniUsernameProp = "ProxyUsername"; + static const inline QString kIniPasswordProp = "ProxyPassword"; + + static const inline QString kJsonOptionProp = "option"; + static const inline QString kJsonAddressProp = "address"; + static const inline QString kJsonPortProp = "port"; + static const inline QString kJsonUsernameProp = "username"; + static const inline QString kJsonPasswordProp = "password"; static constexpr quint32 versionForSerialization_ = 1; // should increment the version if the data format is changed }; diff --git a/client/common/types/shareproxygateway.h b/client/common/types/shareproxygateway.h index b769caf8a..efc661e2d 100644 --- a/client/common/types/shareproxygateway.h +++ b/client/common/types/shareproxygateway.h @@ -4,38 +4,35 @@ #include #include +#include namespace types { struct ShareProxyGateway { - struct JsonInfo - { - JsonInfo& operator=(const JsonInfo&) { return *this; } - - const QString kIsEnabledProp = "isEnabled"; - const QString kProxySharingModeProp = "proxySharingMode"; - }; - ShareProxyGateway() = default; ShareProxyGateway(const QJsonObject &json) { - if (json.contains(jsonInfo.kIsEnabledProp) && json[jsonInfo.kIsEnabledProp].isBool()) - isEnabled = json[jsonInfo.kIsEnabledProp].toBool(); + if (json.contains(kJsonIsEnabledProp) && json[kJsonIsEnabledProp].isBool()) + isEnabled = json[kJsonIsEnabledProp].toBool(); + + if (json.contains(kJsonProxySharingModeProp) && json[kJsonProxySharingModeProp].isDouble()) + proxySharingMode = static_cast(json[kJsonProxySharingModeProp].toInt()); - if (json.contains(jsonInfo.kProxySharingModeProp) && json[jsonInfo.kProxySharingModeProp].isDouble()) - proxySharingMode = static_cast(json[jsonInfo.kProxySharingModeProp].toInt()); + if (json.contains(kJsonPortProp) && json[kJsonPortProp].isDouble()) + port = json[kJsonPortProp].toInt(); } bool isEnabled = false; PROXY_SHARING_TYPE proxySharingMode = PROXY_SHARING_HTTP; - JsonInfo jsonInfo; + uint port = 0; bool operator==(const ShareProxyGateway &other) const { return other.isEnabled == isEnabled && - other.proxySharingMode == proxySharingMode; + other.proxySharingMode == proxySharingMode && + other.port == port; } bool operator!=(const ShareProxyGateway &other) const @@ -43,18 +40,36 @@ struct ShareProxyGateway return !(*this == other); } + void fromIni(const QSettings &settings) + { + isEnabled = settings.value(kIniIsEnabledProp, isEnabled).toBool(); + proxySharingMode = PROXY_SHARING_TYPE_fromString(settings.value(kIniProxySharingModeProp, PROXY_SHARING_TYPE_toString(proxySharingMode)).toString()); + uint p = settings.value(kIniPortProp, port).toUInt(); + if (p < 65536) { + port = p; + } + } + + void toIni(QSettings &settings) const + { + settings.setValue(kIniIsEnabledProp, isEnabled); + settings.setValue(kIniProxySharingModeProp, PROXY_SHARING_TYPE_toString(proxySharingMode)); + settings.setValue(kIniPortProp, port); + } + QJsonObject toJson() const { QJsonObject json; - json[jsonInfo.kIsEnabledProp] = isEnabled; - json[jsonInfo.kProxySharingModeProp] = static_cast(proxySharingMode); + json[kJsonIsEnabledProp] = isEnabled; + json[kJsonProxySharingModeProp] = static_cast(proxySharingMode); + json[kJsonPortProp] = static_cast(port); return json; } friend QDataStream& operator <<(QDataStream &stream, const ShareProxyGateway &o) { stream << versionForSerialization_; - stream << o.isEnabled << o.proxySharingMode; + stream << o.isEnabled << o.proxySharingMode << o.port; return stream; } @@ -68,6 +83,9 @@ struct ShareProxyGateway return stream; } stream >> o.isEnabled >> o.proxySharingMode; + if (version >= 2) { + stream >> o.port; + } return stream; } @@ -76,13 +94,21 @@ struct ShareProxyGateway QDebugStateSaver saver(dbg); dbg.nospace(); dbg << "{isEnabled:" << s.isEnabled << "; "; - dbg << "proxySharingMode:" << PROXY_SHARING_TYPE_toString(s.proxySharingMode) << "}"; + dbg << "proxySharingMode:" << PROXY_SHARING_TYPE_toString(s.proxySharingMode) << "; "; + dbg << "port:" << s.port << "}"; return dbg; } - private: - static constexpr quint32 versionForSerialization_ = 1; // should increment the version if the data format is changed + static const inline QString kIniIsEnabledProp = "ShareProxyGatewayEnabled"; + static const inline QString kIniProxySharingModeProp = "ShareProxyGatewayMode"; + static const inline QString kIniPortProp = "ShareProxyGatewayPort"; + + static const inline QString kJsonIsEnabledProp = "isEnabled"; + static const inline QString kJsonProxySharingModeProp = "proxySharingMode"; + static const inline QString kJsonPortProp = "port"; + + static constexpr quint32 versionForSerialization_ = 2; // should increment the version if the data format is changed }; } // types namespace diff --git a/client/common/types/sharesecurehotspot.h b/client/common/types/sharesecurehotspot.h index efe044682..c7ff60f8a 100644 --- a/client/common/types/sharesecurehotspot.h +++ b/client/common/types/sharesecurehotspot.h @@ -12,33 +12,23 @@ namespace types { struct ShareSecureHotspot { - struct JsonInfo - { - JsonInfo& operator=(const JsonInfo&) { return *this; } - - const QString kIsEnabledProp = "isEnabled"; - const QString kSSIDProp = "ssid"; - const QString kPasswordProp = "password"; - }; - ShareSecureHotspot() = default; ShareSecureHotspot(const QJsonObject &json) { - if (json.contains(jsonInfo.kIsEnabledProp) && json[jsonInfo.kIsEnabledProp].isBool()) - isEnabled = json[jsonInfo.kIsEnabledProp].toBool(); + if (json.contains(kJsonIsEnabledProp) && json[kJsonIsEnabledProp].isBool()) + isEnabled = json[kJsonIsEnabledProp].toBool(); - if (json.contains(jsonInfo.kSSIDProp) && json[jsonInfo.kSSIDProp].isString()) - ssid = Utils::fromBase64(json[jsonInfo.kSSIDProp].toString()); + if (json.contains(kJsonSSIDProp) && json[kJsonSSIDProp].isString()) + ssid = Utils::fromBase64(json[kJsonSSIDProp].toString()); - if (json.contains(jsonInfo.kPasswordProp) && json[jsonInfo.kPasswordProp].isString()) - password = Utils::fromBase64(json[jsonInfo.kPasswordProp].toString()); + if (json.contains(kJsonPasswordProp) && json[kJsonPasswordProp].isString()) + password = Utils::fromBase64(json[kJsonPasswordProp].toString()); } bool isEnabled = false; QString ssid; QString password; - JsonInfo jsonInfo; // Include the JsonInfo structure within ShareSecureHotspot bool operator==(const ShareSecureHotspot &other) const { @@ -55,9 +45,9 @@ struct ShareSecureHotspot QJsonObject toJson() const { QJsonObject json; - json[jsonInfo.kIsEnabledProp] = isEnabled; - json[jsonInfo.kSSIDProp] = Utils::toBase64(ssid); - json[jsonInfo.kPasswordProp] = Utils::toBase64(password); + json[kJsonIsEnabledProp] = isEnabled; + json[kJsonSSIDProp] = Utils::toBase64(ssid); + json[kJsonPasswordProp] = Utils::toBase64(password); return json; } @@ -95,10 +85,12 @@ struct ShareSecureHotspot return dbg; } - private: - static constexpr quint32 versionForSerialization_ = 2; // should increment the version if the data format is changed + static const inline QString kJsonIsEnabledProp = "isEnabled"; + static const inline QString kJsonSSIDProp = "ssid"; + static const inline QString kJsonPasswordProp = "password"; + static constexpr quint32 versionForSerialization_ = 2; // should increment the version if the data format is changed }; } // types namespace diff --git a/client/common/types/splittunneling.h b/client/common/types/splittunneling.h index 99bebb24b..69f147371 100644 --- a/client/common/types/splittunneling.h +++ b/client/common/types/splittunneling.h @@ -1,38 +1,33 @@ #pragma once +#include #include #include +#include #include #include "types/enums.h" +#include "utils/ipvalidation.h" +#include "utils/logger.h" namespace types { struct SplitTunnelingSettings { - struct JsonInfo - { - JsonInfo& operator=(const JsonInfo&) { return *this; } - - const QString kActiveProp = "active"; - const QString kModeProp = "mode"; - }; - SplitTunnelingSettings() = default; SplitTunnelingSettings(const QJsonObject &json) { - if (json.contains(jsonInfo.kActiveProp) && json[jsonInfo.kActiveProp].isBool()) - active = json[jsonInfo.kActiveProp].toBool(); + if (json.contains(kJsonActiveProp) && json[kJsonActiveProp].isBool()) + active = json[kJsonActiveProp].toBool(); - if (json.contains(jsonInfo.kModeProp) && json[jsonInfo.kModeProp].isDouble()) - mode = static_cast(json[jsonInfo.kModeProp].toInt()); + if (json.contains(kJsonModeProp) && json[kJsonModeProp].isDouble()) + mode = static_cast(json[kJsonModeProp].toInt()); } bool active = false; SPLIT_TUNNELING_MODE mode = SPLIT_TUNNELING_MODE_EXCLUDE; - JsonInfo jsonInfo; bool operator==(const SplitTunnelingSettings &other) const { @@ -48,8 +43,8 @@ struct SplitTunnelingSettings QJsonObject toJson() const { QJsonObject json; - json[jsonInfo.kActiveProp] = active; - json[jsonInfo.kModeProp] = static_cast(mode); + json[kJsonActiveProp] = active; + json[kJsonModeProp] = static_cast(mode); return json; } @@ -81,34 +76,27 @@ struct SplitTunnelingSettings return dbg; } - private: + static const inline QString kJsonActiveProp = "active"; + static const inline QString kJsonModeProp = "mode"; + static constexpr quint32 versionForSerialization_ = 1; // should increment the version if the data format is changed }; struct SplitTunnelingNetworkRoute { - struct JsonInfo - { - JsonInfo& operator=(const JsonInfo&) { return *this; } - - const QString kTypeProp = "type"; - const QString kNameProp = "name"; - }; - SPLIT_TUNNELING_NETWORK_ROUTE_TYPE type = SPLIT_TUNNELING_NETWORK_ROUTE_TYPE_IP; QString name; - JsonInfo jsonInfo; SplitTunnelingNetworkRoute() : type(SPLIT_TUNNELING_NETWORK_ROUTE_TYPE_IP) {} SplitTunnelingNetworkRoute(const QJsonObject &json) { - if (json.contains(jsonInfo.kTypeProp) && json[jsonInfo.kTypeProp].isDouble()) - type = static_cast(json[jsonInfo.kTypeProp].toInt()); + if (json.contains(kJsonTypeProp) && json[kJsonTypeProp].isDouble()) + type = static_cast(json[kJsonTypeProp].toInt()); - if (json.contains(jsonInfo.kNameProp) && json[jsonInfo.kNameProp].isString()) - name = json[jsonInfo.kNameProp].toString(); + if (json.contains(kJsonNameProp) && json[kJsonNameProp].isString()) + name = json[kJsonNameProp].toString(); } bool operator==(const SplitTunnelingNetworkRoute &other) const @@ -125,8 +113,8 @@ struct SplitTunnelingNetworkRoute QJsonObject toJson() const { QJsonObject json; - json[jsonInfo.kTypeProp] = static_cast(type); - json[jsonInfo.kNameProp] = name; + json[kJsonTypeProp] = static_cast(type); + json[kJsonNameProp] = name; return json; } @@ -159,41 +147,33 @@ struct SplitTunnelingNetworkRoute } private: + static const inline QString kJsonTypeProp = "type"; + static const inline QString kJsonNameProp = "name"; + static constexpr quint32 versionForSerialization_ = 1; // should increment the version if the data format is changed }; struct SplitTunnelingApp { - struct JsonInfo - { - JsonInfo& operator=(const JsonInfo&) { return *this; } - - const QString kActiveProp = "active"; - const QString kFullNameProp = "fullName"; - const QString kNameProp = "name"; - const QString kIconProp = "icon"; - const QString kTypeProp = "type"; - }; - SplitTunnelingApp() = default; SplitTunnelingApp& operator=(const SplitTunnelingApp&) = default; SplitTunnelingApp(const QJsonObject &json) { - if (json.contains(jsonInfo.kTypeProp) && json[jsonInfo.kTypeProp].isDouble()) - type = static_cast(json[jsonInfo.kTypeProp].toInt()); + if (json.contains(kJsonTypeProp) && json[kJsonTypeProp].isDouble()) + type = static_cast(json[kJsonTypeProp].toInt()); - if (json.contains(jsonInfo.kNameProp) && json[jsonInfo.kNameProp].isString()) - name = json[jsonInfo.kNameProp].toString(); + if (json.contains(kJsonNameProp) && json[kJsonNameProp].isString()) + name = json[kJsonNameProp].toString(); - if (json.contains(jsonInfo.kFullNameProp) && json[jsonInfo.kFullNameProp].isString()) - fullName = json[jsonInfo.kFullNameProp].toString(); + if (json.contains(kJsonFullNameProp) && json[kJsonFullNameProp].isString()) + fullName = json[kJsonFullNameProp].toString(); - if (json.contains(jsonInfo.kActiveProp) && json[jsonInfo.kActiveProp].isBool()) - active = json[jsonInfo.kActiveProp].toBool(); + if (json.contains(kJsonActiveProp) && json[kJsonActiveProp].isBool()) + active = json[kJsonActiveProp].toBool(); - if (json.contains(jsonInfo.kIconProp) && json[jsonInfo.kIconProp].isString()) - icon = json[jsonInfo.kIconProp].toString(); + if (json.contains(kJsonIconProp) && json[kJsonIconProp].isString()) + icon = json[kJsonIconProp].toString(); } bool active = false; @@ -201,7 +181,6 @@ struct SplitTunnelingApp QString name; QString icon; SPLIT_TUNNELING_APP_TYPE type = SPLIT_TUNNELING_APP_TYPE_USER; - JsonInfo jsonInfo; bool operator==(const SplitTunnelingApp &other) const { @@ -220,11 +199,11 @@ struct SplitTunnelingApp QJsonObject toJson() const { QJsonObject json; - json[jsonInfo.kActiveProp] = active; - json[jsonInfo.kFullNameProp] = fullName; - json[jsonInfo.kNameProp] = name; - json[jsonInfo.kTypeProp] = static_cast(type); - json[jsonInfo.kIconProp] = icon; + json[kJsonActiveProp] = active; + json[kJsonFullNameProp] = fullName; + json[kJsonNameProp] = name; + json[kJsonTypeProp] = static_cast(type); + json[kJsonIconProp] = icon; return json; } @@ -264,32 +243,28 @@ struct SplitTunnelingApp } private: + static const inline QString kJsonActiveProp = "active"; + static const inline QString kJsonFullNameProp = "fullName"; + static const inline QString kJsonNameProp = "name"; + static const inline QString kJsonIconProp = "icon"; + static const inline QString kJsonTypeProp = "type"; + static constexpr quint32 versionForSerialization_ = 2; // should increment the version if the data format is changed }; struct SplitTunneling { - struct JsonInfo - { - JsonInfo& operator=(const JsonInfo&) { return *this; } - - const QString kAppsProp = "apps"; - const QString kNetworkRoutesProp = "networkRoutes"; - const QString kSettingsProp = "settings"; - const QString kVersionProp = "version"; - }; - SplitTunneling() = default; SplitTunneling(const QJsonObject &json) { - if (json.contains(jsonInfo.kSettingsProp) && json[jsonInfo.kSettingsProp].isObject()) - settings = SplitTunnelingSettings(json[jsonInfo.kSettingsProp].toObject()); + if (json.contains(kJsonSettingsProp) && json[kJsonSettingsProp].isObject()) + settings = SplitTunnelingSettings(json[kJsonSettingsProp].toObject()); - if (json.contains(jsonInfo.kAppsProp) && json[jsonInfo.kAppsProp].isArray()) + if (json.contains(kJsonAppsProp) && json[kJsonAppsProp].isArray()) { - QJsonArray appsArray = json[jsonInfo.kAppsProp].toArray(); + QJsonArray appsArray = json[kJsonAppsProp].toArray(); apps.clear(); apps.reserve(appsArray.size()); for (const QJsonValue &appValue : appsArray) @@ -301,9 +276,9 @@ struct SplitTunneling } } - if (json.contains(jsonInfo.kNetworkRoutesProp) && json[jsonInfo.kNetworkRoutesProp].isArray()) + if (json.contains(kJsonNetworkRoutesProp) && json[kJsonNetworkRoutesProp].isArray()) { - QJsonArray networkRoutesArray = json[jsonInfo.kNetworkRoutesProp].toArray(); + QJsonArray networkRoutesArray = json[kJsonNetworkRoutesProp].toArray(); networkRoutes.clear(); networkRoutes.reserve(networkRoutesArray.size()); for (const QJsonValue &routeValue : networkRoutesArray) @@ -319,7 +294,6 @@ struct SplitTunneling SplitTunnelingSettings settings; QVector apps; QVector networkRoutes; - JsonInfo jsonInfo; bool operator==(const SplitTunneling &other) const { @@ -333,26 +307,100 @@ struct SplitTunneling return !(*this == other); } + void fromIni(const QSettings &s) + { + settings.active = s.value(kIniSplitTunnelingEnabledProp, settings.active).toBool(); + settings.mode = SPLIT_TUNNELING_MODE_fromString(s.value(kIniSplitTunnelingModeProp, SPLIT_TUNNELING_MODE_toString(settings.mode)).toString()); + + if (s.contains(kIniSplitTunnelingAppsProp)) { + apps.clear(); + QStringList appsList; + appsList = s.value(kIniSplitTunnelingAppsProp).toStringList(); + for (auto appPath : appsList) { + std::error_code ec; + std::filesystem::path path(appPath.toStdString()); + bool exists = std::filesystem::exists(path, ec); + if (ec || !exists) { + qCDebug(LOG_BASIC) << "Skipping non-existent split tunneling app '" << appPath << "'"; + continue; + } + + SplitTunnelingApp a; + a.active = true; + a.fullName = appPath; + a.name = appPath; + a.icon = ""; + a.type = SPLIT_TUNNELING_APP_TYPE_USER; + apps << a; + } + } + + if (s.contains(kIniSplitTunnelingRoutesProp)) { + networkRoutes.clear(); + QStringList networkRoutesList; + networkRoutesList = s.value(kIniSplitTunnelingRoutesProp).toStringList(); + for (auto route : networkRoutesList) { + SplitTunnelingNetworkRoute r; + r.name = route; + if (IpValidation::isIp(route)) { + r.type = SPLIT_TUNNELING_NETWORK_ROUTE_TYPE_IP; + } else if (IpValidation::isDomain(route)) { + r.type = SPLIT_TUNNELING_NETWORK_ROUTE_TYPE_HOSTNAME; + } else { + qCDebug(LOG_BASIC) << "Skipping unrecognized split tunneling route type"; + continue; + } + networkRoutes << r; + } + } + } + + void toIni(QSettings &s) const + { + s.setValue(kIniSplitTunnelingEnabledProp, settings.active); + s.setValue(kIniSplitTunnelingModeProp, SPLIT_TUNNELING_MODE_toString(settings.mode)); + + QStringList appsList; + for (auto app : apps) { + appsList << app.fullName; + } + if (appsList.isEmpty()) { + s.remove(kIniSplitTunnelingAppsProp); + } else { + s.setValue(kIniSplitTunnelingAppsProp, appsList); + } + + QStringList routesList; + for (auto route : networkRoutes) { + routesList << route.name; + } + if (routesList.isEmpty()) { + s.remove(kIniSplitTunnelingRoutesProp); + } else { + s.setValue(kIniSplitTunnelingRoutesProp, routesList); + } + } + QJsonObject toJson() const { QJsonObject json; - json[jsonInfo.kSettingsProp] = settings.toJson(); + json[kJsonSettingsProp] = settings.toJson(); QJsonArray appsArray; for (const SplitTunnelingApp& app : apps) { appsArray.append(app.toJson()); } - json[jsonInfo.kAppsProp] = appsArray; + json[kJsonAppsProp] = appsArray; QJsonArray networkRoutesArray; for (const SplitTunnelingNetworkRoute& route : networkRoutes) { networkRoutesArray.append(route.toJson()); } - json[jsonInfo.kNetworkRoutesProp] = networkRoutesArray; + json[kJsonNetworkRoutesProp] = networkRoutesArray; - json[jsonInfo.kVersionProp] = static_cast(versionForSerialization_); + json[kJsonVersionProp] = static_cast(versionForSerialization_); return json; } @@ -387,8 +435,17 @@ struct SplitTunneling } private: - static constexpr quint32 versionForSerialization_ = 1; // should increment the version if the data format is changed + static const inline QString kIniSplitTunnelingAppsProp = "SplitTunnelingApps"; + static const inline QString kIniSplitTunnelingRoutesProp = "SplitTunnelingRoutes"; + static const inline QString kIniSplitTunnelingEnabledProp = "SplitTunnelingEnabled"; + static const inline QString kIniSplitTunnelingModeProp = "SplitTunnelingMode"; + static const inline QString kJsonAppsProp = "apps"; + static const inline QString kJsonNetworkRoutesProp = "networkRoutes"; + static const inline QString kJsonSettingsProp = "settings"; + static const inline QString kJsonVersionProp = "version"; + + static constexpr quint32 versionForSerialization_ = 1; // should increment the version if the data format is changed }; diff --git a/client/common/utils/extraconfig.cpp b/client/common/utils/extraconfig.cpp index 7ec0951a1..f0a2a3f6a 100644 --- a/client/common/utils/extraconfig.cpp +++ b/client/common/utils/extraconfig.cpp @@ -25,6 +25,7 @@ const QString WS_LOG_API_RESPONSE = WS_PREFIX + "log-api-response"; const QString WS_WG_VERBOSE_LOGGING = WS_PREFIX + "wireguard-verbose-logging"; const QString WS_SCREEN_TRANSITION_HOTKEYS = WS_PREFIX + "screen-transition-hotkeys"; const QString WS_USE_ICMP_PINGS = WS_PREFIX + "use-icmp-pings"; +const QString WS_USE_PQ_ALGORITHMS = WS_PREFIX + "use-post-quantum-algorithms"; const QString WS_STEALTH_EXTRA_TLS_PADDING = WS_PREFIX + "stealth-extra-tls-padding"; const QString WS_API_EXTRA_TLS_PADDING = WS_PREFIX + "api-extra-tls-padding"; @@ -255,6 +256,11 @@ bool ExtraConfig::getUseICMPPings() return getFlagFromExtraConfigLines(WS_USE_ICMP_PINGS); } +bool ExtraConfig::getUsePQAlgorithms() +{ + return getFlagFromExtraConfigLines(WS_USE_PQ_ALGORITHMS); +} + bool ExtraConfig::getStealthExtraTLSPadding() { return getFlagFromExtraConfigLines(WS_STEALTH_EXTRA_TLS_PADDING); diff --git a/client/common/utils/extraconfig.h b/client/common/utils/extraconfig.h index ef6a9d4d7..9606dfb62 100644 --- a/client/common/utils/extraconfig.h +++ b/client/common/utils/extraconfig.h @@ -51,6 +51,7 @@ class ExtraConfig bool getLogCtrld(); bool getUsingScreenTransitionHotkeys(); bool getUseICMPPings(); + bool getUsePQAlgorithms(); bool getStealthExtraTLSPadding(); bool getAPIExtraTLSPadding(); diff --git a/client/common/utils/languagesutil.cpp b/client/common/utils/languagesutil.cpp index f5e20652c..57fd0daea 100644 --- a/client/common/utils/languagesutil.cpp +++ b/client/common/utils/languagesutil.cpp @@ -55,6 +55,12 @@ QString LanguagesUtil::convertCodeToNative(const QString &code) return "Unknown"; } +bool LanguagesUtil::isSupportedLanguage(const QString &lang) +{ + static QStringList sl = { "en", "ar", "be", "cs", "de", "es", "fa", "fr", "hi", "id", "it", "ja", "ko", "pl", "pt", "ru", "tr", "uk", "vi", "zh-CN", "zh-TW" }; + return sl.contains(lang); +} + QString LanguagesUtil::systemLanguage() { QStringList languages = QLocale::system().uiLanguages(); diff --git a/client/common/utils/languagesutil.h b/client/common/utils/languagesutil.h index baa61a72c..9fefb2b71 100644 --- a/client/common/utils/languagesutil.h +++ b/client/common/utils/languagesutil.h @@ -7,6 +7,8 @@ class LanguagesUtil public: static QString convertCodeToNative(const QString &code); + static bool isSupportedLanguage(const QString &lang); + // Returns the two-letter ISO639 language code currently being used by the OS, or "en" otherwise. static QString systemLanguage(); diff --git a/client/common/utils/linuxutils.cpp b/client/common/utils/linuxutils.cpp index b6fa5dc88..a0355a3f5 100644 --- a/client/common/utils/linuxutils.cpp +++ b/client/common/utils/linuxutils.cpp @@ -67,7 +67,7 @@ const QString getLastInstallPlatform() return linuxPlatformName; } -bool isGuiAlreadyRunning() +bool isAppAlreadyRunning() { // Look for process containing "Windscribe" -- exclude grep and Engine QString cmd = "ps axco command | grep Windscribe | grep -v grep | grep -v WindscribeEngine | grep -v windscribe-cli"; diff --git a/client/common/utils/linuxutils.h b/client/common/utils/linuxutils.h index 928d9611f..efcdf5db7 100644 --- a/client/common/utils/linuxutils.h +++ b/client/common/utils/linuxutils.h @@ -7,9 +7,15 @@ namespace LinuxUtils { const QString LAST_INSTALL_PLATFORM_FILE = "/etc/windscribe/platform"; const QString DEB_PLATFORM_NAME_X64 = QString("linux_deb_x64"); + const QString DEB_PLATFORM_NAME_X64_CLI = QString("linux_deb_x64_cli"); const QString DEB_PLATFORM_NAME_ARM64 = QString("linux_deb_arm64"); + const QString DEB_PLATFORM_NAME_ARM64_CLI = QString("linux_deb_arm64_cli"); const QString RPM_PLATFORM_NAME = QString("linux_rpm_x64"); + const QString RPM_PLATFORM_NAME_CLI = QString("linux_rpm_x64_cli"); + const QString RPM_OPENSUSE_PLATFORM_NAME = QString("linux_rpm_opensuse_x64"); + const QString RPM_OPENSUSE_PLATFORM_NAME_CLI = QString("linux_rpm_opensuse_x64_cli"); const QString ZST_PLATFORM_NAME = QString("linux_zst_x64"); + const QString ZST_PLATFORM_NAME_CLI = QString("linux_zst_x64_cli"); QString getOsVersionString(); QString getLinuxKernelVersion(); @@ -18,7 +24,7 @@ namespace LinuxUtils gid_t getWindscribeGid(); // CLI - bool isGuiAlreadyRunning(); + bool isAppAlreadyRunning(); // Split Tunneling QMap enumerateInstalledPrograms(); diff --git a/client/common/utils/macutils.h b/client/common/utils/macutils.h index 966148f0a..ae9026f7e 100644 --- a/client/common/utils/macutils.h +++ b/client/common/utils/macutils.h @@ -21,7 +21,7 @@ namespace MacUtils bool isLockdownMode(); // CLI - bool isGuiAlreadyRunning(); + bool isAppAlreadyRunning(); bool showGui(); // Split Routing diff --git a/client/common/utils/macutils.mm b/client/common/utils/macutils.mm index 374613974..d7eb593a4 100644 --- a/client/common/utils/macutils.mm +++ b/client/common/utils/macutils.mm @@ -93,7 +93,7 @@ [[NSCursor arrowCursor] set]; } -bool MacUtils::isGuiAlreadyRunning() +bool MacUtils::isAppAlreadyRunning() { // Look for process containing "Windscribe" -- exclude grep and Engine QString cmd = "ps axco command | grep Windscribe | grep -v grep | grep -v WindscribeEngine | grep -v windscribe-cli"; @@ -252,4 +252,4 @@ { QString response = Utils::execCmd("defaults read .GlobalPreferences.plist LDMGlobalEnabled"); return response.trimmed() == "1"; -} \ No newline at end of file +} diff --git a/client/common/utils/mergelog.cpp b/client/common/utils/mergelog.cpp index 1efe75367..3e7381838 100644 --- a/client/common/utils/mergelog.cpp +++ b/client/common/utils/mergelog.cpp @@ -138,7 +138,11 @@ int MergeLog::mergeTask(QMutex *mutex, QMultiMap /dev/null").arg(mtu).arg(url); + QString result = Utils::execCmd(cmd).trimmed(); + + return result.contains("icmp_seq="); +} + QString getLocalIP() { // Yegor and Clayton found this command to work on many distros, including old ones. @@ -144,4 +152,9 @@ QString getLocalIP() return sLocalIP; } +QString getRoutingTable() +{ + return Utils::execCmd("cat /proc/net/route"); +} + } // namespace NetworkUtils_linux diff --git a/client/common/utils/network_utils/network_utils_linux.h b/client/common/utils/network_utils/network_utils_linux.h index 728835912..01760da7a 100644 --- a/client/common/utils/network_utils/network_utils_linux.h +++ b/client/common/utils/network_utils/network_utils_linux.h @@ -19,6 +19,8 @@ class RoutingTableEntry }; void getDefaultRoute(QString &outGatewayIp, QString &outInterfaceName, QString &outAdapterIp, bool ignoreTun = false); +bool pingWithMtu(const QString &url, int mtu); QString getLocalIP(); +QString getRoutingTable(); } // namespace NetworkUtils_linux diff --git a/client/common/utils/network_utils/network_utils_mac.cpp b/client/common/utils/network_utils/network_utils_mac.cpp index 14163d235..bda7dac0d 100644 --- a/client/common/utils/network_utils/network_utils_mac.cpp +++ b/client/common/utils/network_utils/network_utils_mac.cpp @@ -18,99 +18,13 @@ #include "../macutils.h" #include "../utils.h" -namespace -{ -QString ssidOfInterface(const QString &networkInterface) -{ - // In MacOS 14.4, Apple removed the SSID from scutil output, use an alternative method - if (MacUtils::isOsVersionAtLeast(14, 4)) { - QString command = "networksetup -getairportnetwork " + networkInterface + " | head -n 1 | cut -s -d ':' -f 2"; - QString strReply = Utils::execCmd(command).trimmed(); - if (strReply.length() > 32) { - // SSIDs are at most 32 octets, so if the string is longer than that, it's probably an error message. - strReply = ""; - } - return strReply; - } - // Older versions of macOS should use scutil - QString command = "echo 'show State:/Network/Interface/" + networkInterface + "/AirPort' | scutil | grep SSID_STR | sed -e 's/.*SSID_STR : //'"; - return Utils::execCmd(command).trimmed(); -} - -QString macAddressFromInterfaceName(const QString &interfaceName) +QString NetworkUtils_mac::macAddressFromInterfaceName(const QString &interfaceName) { QString command = "ifconfig " + interfaceName + " | grep 'ether' | awk '{print $2}'"; QString strIP = Utils::execCmd(command).trimmed(); return strIP; } -QList currentNetworkHwInterfaces() -{ - QList result; - QString cmd = "networksetup -listnetworkserviceorder | grep Device | sed -e 's/.*Device: //' -e 's/)//'"; - QString response = Utils::execCmd(cmd); - - const QList lines = response.split("\n"); - - for (const QString &line : lines) - { - const QString iName = line.trimmed(); - if (iName != "") - { - if (NetworkUtils_mac::isAdapterUp(iName)) - { - result.append(iName); - } - } - } - - return result; -} - -bool isAdapterActive(const QString &networkInterface) -{ - QString cmdInterfaceStatus = "ifconfig " + networkInterface + " | grep status | awk '{print $2}'"; - QString statusResult = Utils::execCmd(cmdInterfaceStatus).trimmed(); - return statusResult == "active"; -} - -QMap currentHardwareInterfaceIndexes() -{ - QMap result; - QString cmd = "networksetup -listallhardwareports | grep Device | awk '{print $2}'"; - QString response = Utils::execCmd(cmd); - - const QList lines = response.split("\n"); - - int index = 1; - for (const QString &line : lines) - { - const QString iName = line.trimmed(); - if (iName != "") - { - result.insert(iName, index); - } - index++; - } - - return result; -} - -QVector currentlyUpNetInterfaces() -{ - auto isUp = [](const types::NetworkInterface &ni) - { - return NetworkUtils_mac::isAdapterUp(ni.interfaceName); - }; - - QVector interfaces = NetworkUtils_mac::currentNetworkInterfaces(false); - QVector upInterfaces; - std::copy_if(interfaces.begin(), interfaces.end(), std::back_inserter(upInterfaces), isUp); - return upInterfaces; -} - -} // namespace - QString NetworkUtils_mac::lastConnectedNetworkInterfaceName() { QString ifname(""); @@ -143,83 +57,12 @@ QString NetworkUtils_mac::lastConnectedNetworkInterfaceName() return ifname; } -QVector NetworkUtils_mac::currentNetworkInterfaces(bool includeNoInterface) -{ - QVector networkInterfaces; - - if (includeNoInterface) { - networkInterfaces << types::NetworkInterface::noNetworkInterface(); - } - - const QList hwInterfaces = currentNetworkHwInterfaces(); - const QMap interfaceIndexes = currentHardwareInterfaceIndexes(); - - for (const QString &interfaceName : hwInterfaces) { - types::NetworkInterface networkInterface; - - int index = 0; - if (interfaceIndexes.contains(interfaceName)) index = interfaceIndexes[interfaceName]; - networkInterface.interfaceIndex = index; - networkInterface.interfaceName = interfaceName; - - bool wifi = isWifiAdapter(interfaceName); - QString macAddress = macAddressFromInterfaceName(interfaceName); - networkInterface.physicalAddress = macAddress; - - // TODO: **JDRM** see if we can get a useful adapter friendlyName (e.g. Belkin USB-C Ethernet) - // from a macOS API. - if (wifi) { - networkInterface.interfaceType = NETWORK_INTERFACE_WIFI; - QString ssid = ssidOfInterface(interfaceName); - networkInterface.networkOrSsid = ssid; - networkInterface.friendlyName = "Wi-Fi"; - } - else { - networkInterface.interfaceType = NETWORK_INTERFACE_ETH; - networkInterface.networkOrSsid = macAddress; - networkInterface.friendlyName = "Ethernet"; - } - - networkInterface.active = isAdapterActive(interfaceName); - networkInterfaces << networkInterface; - - // TODO: The following fields should be removeable from types::NetworkInterface: - // interface_guid - // state - // metric - // dw_type - // device_name - // connector_present - // end_point_interface - // active ? - } - - return networkInterfaces; -} - QString NetworkUtils_mac::trueMacAddress(const QString &interfaceName) { QString cmdGetTrueMAC = "networksetup -getmacaddress " + interfaceName + " | awk '{print $3}'"; return Utils::execCmd(cmdGetTrueMAC).trimmed(); } -QVector NetworkUtils_mac::currentSpoofedInterfaces() -{ - QVector spoofed; - QVector currentInterfaces = currentlyUpNetInterfaces(); - for (const types::NetworkInterface &ii : currentInterfaces) - { - const QString &interfaceName = ii.interfaceName; - - if (isInterfaceSpoofed(interfaceName)) - { - spoofed << ii; - } - } - - return spoofed; -} - bool NetworkUtils_mac::isInterfaceSpoofed(const QString &interfaceName) { return trueMacAddress(interfaceName) != macAddressFromInterfaceName(interfaceName); @@ -248,17 +91,6 @@ bool NetworkUtils_mac::isAdapterUp(const QString &networkInterfaceName) } } -const types::NetworkInterface NetworkUtils_mac::currentNetworkInterface() -{ - QVector ni = currentNetworkInterfaces(false); - if (ni.size() > 0) - { - return ni[0]; - } - - return types::NetworkInterface::noNetworkInterface(); -} - bool NetworkUtils_mac::pingWithMtu(const QString &url, int mtu) { const QString cmd = QString("ping -c 1 -W 1000 -D -s %1 %2 2> /dev/null").arg(mtu).arg(url); @@ -418,3 +250,8 @@ QStringList NetworkUtils_mac::getP2P_AWDL_NetworkInterfaces() ret << s; return ret; } + +QString NetworkUtils_mac::getRoutingTable() +{ + return Utils::execCmd("netstat -rn -finet").trimmed(); +} \ No newline at end of file diff --git a/client/common/utils/network_utils/network_utils_mac.h b/client/common/utils/network_utils/network_utils_mac.h index bf55b9630..c611cad9f 100644 --- a/client/common/utils/network_utils/network_utils_mac.h +++ b/client/common/utils/network_utils/network_utils_mac.h @@ -8,6 +8,7 @@ namespace NetworkUtils_mac { // Networking QString ipAddressByInterfaceName(const QString &interfaceName); + QString macAddressFromInterfaceName(const QString &interfaceName); void getDefaultRoute(QString &outGatewayIp, QString &outInterfaceName); QString lastConnectedNetworkInterfaceName(); @@ -16,15 +17,12 @@ namespace NetworkUtils_mac bool isWifiAdapter(const QString &networkInterface); bool isAdapterUp(const QString &networkInterfaceName); - const types::NetworkInterface currentNetworkInterface(); - QVector currentNetworkInterfaces(bool includeNoInterface); - QVector currentSpoofedInterfaces(); - bool isInterfaceSpoofed(const QString &interfaceName); bool checkMacAddr(const QString& interfaceName, const QString& macAddr); bool pingWithMtu(const QString &url, int mtu); QString getLocalIP(); + QString getRoutingTable(); // read DNS-servers for device name (now used for ipsec adapters for ikev2) // implemented with "scutil --dns" command diff --git a/client/common/utils/network_utils/network_utils_win.cpp b/client/common/utils/network_utils/network_utils_win.cpp index 79cd786eb..76a279e8b 100644 --- a/client/common/utils/network_utils/network_utils_win.cpp +++ b/client/common/utils/network_utils/network_utils_win.cpp @@ -14,8 +14,6 @@ #include "../networktypes.h" #include "../winutils.h" -#pragma comment(lib, "wlanapi.lib") - static GUID guidFromQString(QString str) { GUID reqGUID; @@ -351,55 +349,97 @@ static QList getAdapterAddressesTable() return adapters; } +static QString getSystemDir() +{ + wchar_t path[MAX_PATH]; + UINT result = ::GetSystemDirectory(path, MAX_PATH); + if (result == 0 || result >= MAX_PATH) { + qCDebug(LOG_BASIC) << "GetSystemDirectory failed" << ::GetLastError(); + return QString("C:\\Windows\\System32"); + } + + return QString::fromWCharArray(path); +} + static QString ssidFromInterfaceGUID(QString interfaceGUID) { QString ssid = ""; + // This DLL is not be available on default installs of Windows Server. Dynamically load it so + // the app doesn't fail to launch with a "DLL not found" error. + const QString dll = getSystemDir() + QString("\\wlanapi.dll"); + auto wlanDll = ::LoadLibraryEx(qUtf16Printable(dll), NULL, LOAD_LIBRARY_SEARCH_SYSTEM32); + if (wlanDll == NULL) { + qCDebug(LOG_BASIC) << "ssidFromInterfaceGUID wlanapi.dll does not exist on this computer or could not be loaded:" << ::GetLastError(); + return ssid; + } + + auto freeDLL = qScopeGuard([&] { + ::FreeLibrary(wlanDll); + }); + + typedef DWORD (WINAPI * WlanOpenHandleFunc)(DWORD dwClientVersion, PVOID pReserved, PDWORD pdwNegotiatedVersion, PHANDLE phClientHandle); + typedef DWORD (WINAPI * WlanCloseHandleFunc)(HANDLE hClientHandle, PVOID pReserved); + typedef VOID (WINAPI * WlanFreeMemoryFunc)(PVOID pMemory); + typedef DWORD (WINAPI * WlanEnumInterfacesFunc)(HANDLE hClientHandle, PVOID pReserved, PWLAN_INTERFACE_INFO_LIST *ppInterfaceList); + typedef DWORD (WINAPI * WlanQueryInterfaceFunc)(HANDLE hClientHandle, CONST GUID *pInterfaceGuid, WLAN_INTF_OPCODE OpCode, PVOID pReserved, + PDWORD pdwDataSize, PVOID *ppData, PWLAN_OPCODE_VALUE_TYPE pWlanOpcodeValueType); + + WlanOpenHandleFunc pfnWlanOpenHandle = (WlanOpenHandleFunc)::GetProcAddress(wlanDll, "WlanOpenHandle"); + if (pfnWlanOpenHandle == NULL) { + qCDebug(LOG_BASIC) << "ssidFromInterfaceGUID failed to load WlanOpenHandle:" << ::GetLastError(); + return ssid; + } + + WlanCloseHandleFunc pfnWlanCloseHandle = (WlanCloseHandleFunc)::GetProcAddress(wlanDll, "WlanCloseHandle"); + if (pfnWlanCloseHandle == NULL) { + qCDebug(LOG_BASIC) << "ssidFromInterfaceGUID failed to load WlanCloseHandle:" << ::GetLastError(); + return ssid; + } + + WlanFreeMemoryFunc pfnWlanFreeMemory = (WlanFreeMemoryFunc)::GetProcAddress(wlanDll, "WlanFreeMemory"); + if (pfnWlanFreeMemory == NULL) { + qCDebug(LOG_BASIC) << "ssidFromInterfaceGUID failed to load WlanFreeMemory:" << ::GetLastError(); + return ssid; + } + + WlanEnumInterfacesFunc pfnWlanEnumInterfaces = (WlanEnumInterfacesFunc)::GetProcAddress(wlanDll, "WlanEnumInterfaces"); + if (pfnWlanEnumInterfaces == NULL) { + qCDebug(LOG_BASIC) << "ssidFromInterfaceGUID failed to load WlanEnumInterfaces:" << ::GetLastError(); + return ssid; + } + + WlanQueryInterfaceFunc pfnWlanQueryInterface = (WlanQueryInterfaceFunc)::GetProcAddress(wlanDll, "WlanQueryInterface"); + if (pfnWlanQueryInterface == NULL) { + qCDebug(LOG_BASIC) << "ssidFromInterfaceGUID failed to load WlanQueryInterface:" << ::GetLastError(); + return ssid; + } + DWORD dwCurVersion = 0; HANDLE hClient = NULL; - auto result = WlanOpenHandle(2, NULL, &dwCurVersion, &hClient); + auto result = pfnWlanOpenHandle(2, NULL, &dwCurVersion, &hClient); if (result != ERROR_SUCCESS) { - qCDebug(LOG_BASIC) << "WlanOpenHandle failed with error: " << result; + qCDebug(LOG_BASIC) << "WlanOpenHandle failed with error:" << result; return ssid; } - PWLAN_INTERFACE_INFO_LIST pIfList = NULL; PWLAN_CONNECTION_ATTRIBUTES pConnectInfo = NULL; - auto exitGuard = qScopeGuard([&] { + auto freeWlanResources = qScopeGuard([&] { if (pConnectInfo != NULL) { - WlanFreeMemory(pConnectInfo); - } - - if (pIfList != NULL) { - WlanFreeMemory(pIfList); + pfnWlanFreeMemory(pConnectInfo); } - if (hClient != NULL) { - WlanCloseHandle(hClient, NULL); - } + pfnWlanCloseHandle(hClient, NULL); }); - // TODO: **JDRM** investigate if this call is required. We never reference pIfList after this call. - result = WlanEnumInterfaces(hClient, NULL, &pIfList); - if (result != ERROR_SUCCESS) { - qCDebug(LOG_BASIC) << "WlanEnumInterfaces failed with error:" << result; - return ssid; - } - GUID actualGUID = guidFromQString(interfaceGUID); DWORD connectInfoSize = sizeof(WLAN_CONNECTION_ATTRIBUTES); WLAN_OPCODE_VALUE_TYPE opCode = wlan_opcode_value_type_invalid; - result = WlanQueryInterface(hClient, - &actualGUID, - wlan_intf_opcode_current_connection, - NULL, - &connectInfoSize, - (PVOID *) &pConnectInfo, - &opCode); - + result = pfnWlanQueryInterface(hClient, &actualGUID, wlan_intf_opcode_current_connection, NULL, + &connectInfoSize, (PVOID *) &pConnectInfo, &opCode); if (result != ERROR_SUCCESS) { // Will receive these errors when an adapter is being reset. if (result != ERROR_NOT_FOUND && result != ERROR_INVALID_STATE) { @@ -480,7 +520,7 @@ bool NetworkUtils_win::isInterfaceSpoofed(int interfaceIndex) bool NetworkUtils_win::pingWithMtu(const QString &url, int mtu) { - const QString cmd = QString("C:\\Windows\\system32\\ping.exe"); + const QString cmd = getSystemDir() + QString("\\ping.exe"); const QString params = QString(" -n 1 -l %1 -f %2").arg(mtu).arg(url); QString result = WinUtils::executeBlockingCmd(cmd + params, params, 1000).trimmed(); if (result.contains("bytes=")) { @@ -688,3 +728,9 @@ std::optional NetworkUtils_win::haveInternetConnectivity() return false; } + +QString NetworkUtils_win::getRoutingTable() +{ + const QString cmd = getSystemDir() + QString("\\route.exe print"); + return WinUtils::executeBlockingCmd(cmd, "", 50).trimmed(); +} diff --git a/client/common/utils/network_utils/network_utils_win.h b/client/common/utils/network_utils/network_utils_win.h index 7e493cc72..695a9ff17 100644 --- a/client/common/utils/network_utils/network_utils_win.h +++ b/client/common/utils/network_utils/network_utils_win.h @@ -11,6 +11,7 @@ namespace NetworkUtils_win bool isInterfaceSpoofed(int interfaceIndex); bool pingWithMtu(const QString &url, int mtu); QString getLocalIP(); + QString getRoutingTable(); types::NetworkInterface currentNetworkInterface(); QVector currentNetworkInterfaces(bool includeNoInterface); diff --git a/client/common/utils/utils.cpp b/client/common/utils/utils.cpp index 5f73f60e0..548778070 100644 --- a/client/common/utils/utils.cpp +++ b/client/common/utils/utils.cpp @@ -193,14 +193,14 @@ QList Utils::insertionSort(QListappMainWindow; } -bool WinUtils::isGuiAlreadyRunning() +bool WinUtils::isAppAlreadyRunning() { auto handle = appMainWindowHandle(); return handle != nullptr; diff --git a/client/common/utils/winutils.h b/client/common/utils/winutils.h index f2ac0f51f..58e9055ee 100644 --- a/client/common/utils/winutils.h +++ b/client/common/utils/winutils.h @@ -22,7 +22,7 @@ namespace WinUtils QString executeBlockingCmd(QString cmd, const QString ¶ms, int timeoutMs = -1); - bool isGuiAlreadyRunning(); + bool isAppAlreadyRunning(); bool isServiceRunning(const QString &serviceName); diff --git a/client/common/version/windscribe_version.h b/client/common/version/windscribe_version.h index 9bc42e532..37c8f4ffc 100644 --- a/client/common/version/windscribe_version.h +++ b/client/common/version/windscribe_version.h @@ -1,12 +1,12 @@ #pragma once #define WINDSCRIBE_MAJOR_VERSION 2 -#define WINDSCRIBE_MINOR_VERSION 10 -#define WINDSCRIBE_BUILD_VERSION 15 +#define WINDSCRIBE_MINOR_VERSION 11 +#define WINDSCRIBE_BUILD_VERSION 4 // only one of these should be enabled; neither -> stable //#define WINDSCRIBE_IS_BETA -//#define WINDSCRIBE_IS_GUINEA_PIG +#define WINDSCRIBE_IS_GUINEA_PIG #define STR_HELPER(x) #x #define STR(x) STR_HELPER(x) diff --git a/client/engine/CMakeLists.txt b/client/engine/CMakeLists.txt index 450122cba..423ba3df7 100644 --- a/client/engine/CMakeLists.txt +++ b/client/engine/CMakeLists.txt @@ -11,7 +11,7 @@ set(CMAKE_AUTORCC ON) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) -find_package(Qt6 REQUIRED COMPONENTS Widgets Network Core5Compat) +find_package(Qt6 REQUIRED COMPONENTS Network Core5Compat) add_library(engine STATIC) diff --git a/client/engine/engine/CMakeLists.txt b/client/engine/engine/CMakeLists.txt index e1af8fbc0..a538af31a 100644 --- a/client/engine/engine/CMakeLists.txt +++ b/client/engine/engine/CMakeLists.txt @@ -5,11 +5,11 @@ target_sources(engine PRIVATE engine.h getdeviceid.cpp getdeviceid.h + logout_helper.h openvpnversioncontroller.cpp openvpnversioncontroller.h packetsizecontroller.cpp packetsizecontroller.h - signout_helper.h ) if (WIN32) diff --git a/client/engine/engine/autoupdater/downloadhelper.cpp b/client/engine/engine/autoupdater/downloadhelper.cpp index c6adf0994..9019bb3b4 100644 --- a/client/engine/engine/autoupdater/downloadhelper.cpp +++ b/client/engine/engine/autoupdater/downloadhelper.cpp @@ -42,13 +42,19 @@ const QString DownloadHelper::downloadInstallerPath() QString path; // if getPlatformName() fails, we should never get this far anyway if (platform_ == LinuxUtils::DEB_PLATFORM_NAME_X64 || - platform_ == LinuxUtils::DEB_PLATFORM_NAME_ARM64) { + platform_ == LinuxUtils::DEB_PLATFORM_NAME_X64_CLI || + platform_ == LinuxUtils::DEB_PLATFORM_NAME_ARM64 || + platform_ == LinuxUtils::DEB_PLATFORM_NAME_ARM64_CLI) { path = downloadInstallerPathWithoutExtension() + ".deb"; } - else if (platform_ == LinuxUtils::RPM_PLATFORM_NAME) { + else if (platform_ == LinuxUtils::RPM_PLATFORM_NAME || + platform_ == LinuxUtils::RPM_PLATFORM_NAME_CLI || + platform_ == LinuxUtils::RPM_OPENSUSE_PLATFORM_NAME || + platform_ == LinuxUtils::RPM_OPENSUSE_PLATFORM_NAME_CLI) { path = downloadInstallerPathWithoutExtension() + ".rpm"; } - else if (platform_ == LinuxUtils::ZST_PLATFORM_NAME) { + else if (platform_ == LinuxUtils::ZST_PLATFORM_NAME || + platform_ == LinuxUtils::ZST_PLATFORM_NAME_CLI) { path = downloadInstallerPathWithoutExtension() + ".pkg.tar.zst"; } #endif diff --git a/client/engine/engine/connectionmanager/availableport.cpp b/client/engine/engine/connectionmanager/availableport.cpp index 90de8cce0..f9fbebd2c 100644 --- a/client/engine/engine/connectionmanager/availableport.cpp +++ b/client/engine/engine/connectionmanager/availableport.cpp @@ -116,23 +116,8 @@ bool AvailablePort::isPortBusy(const QString &ip, unsigned int port) return false; #elif defined(Q_OS_MAC) || defined(Q_OS_LINUX) - int sock = socket(AF_INET, SOCK_STREAM, 0); - if (sock < 0) { - return true; - } - - struct sockaddr_in serv_addr; - bzero((char *) &serv_addr, sizeof(serv_addr)); - serv_addr.sin_family = AF_INET; - serv_addr.sin_addr.s_addr = INADDR_ANY; - serv_addr.sin_port = port; - if (bind(sock, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) - { - close(sock); - return true; - } - - close (sock); - return false; + // In most UNIX-like environments, this may fail on all addresses if port is <1024 since this process is not root + // so this is a meaningless test. + return true; #endif } diff --git a/client/engine/engine/connectionmanager/ctrldmanager/ctrldmanager_posix.cpp b/client/engine/engine/connectionmanager/ctrldmanager/ctrldmanager_posix.cpp index fbc693757..8d9402056 100644 --- a/client/engine/engine/connectionmanager/ctrldmanager/ctrldmanager_posix.cpp +++ b/client/engine/engine/connectionmanager/ctrldmanager/ctrldmanager_posix.cpp @@ -20,15 +20,8 @@ bool CtrldManager_posix::runProcess(const QString &upstream1, const QString &ups { WS_ASSERT(!bProcessStarted_); - QString ip = getAvailableIp(); - if (ip.isEmpty()) { - qCDebug(LOG_CTRLD) << "ctrld cannot be started, all IP-addresses are occupied"; - return false; - } - - IHelper::ExecuteError err = helper_->startCtrld(ip, addWsSuffix(upstream1), addWsSuffix(upstream2), domains, isCreateLog_); - bProcessStarted_ = (err == IHelper::ExecuteError::EXECUTE_SUCCESS); - if (bProcessStarted_) { + if (helper_->startCtrld(addWsSuffix(upstream1), addWsSuffix(upstream2), domains, isCreateLog_)) { + bProcessStarted_ = true; qCDebug(LOG_CTRLD) << "ctrld started"; } return bProcessStarted_; @@ -47,22 +40,3 @@ QString CtrldManager_posix::listenIp() const { return listenIp_; } - - -QString CtrldManager_posix::getAvailableIp() -{ - if (!AvailablePort::isPortBusy(listenIp_, 53)) - return listenIp_; - - for (int i = 1; i <= 255; ++i) { - QString ip = QString("127.0.0.%1").arg(i); - if (ip != listenIp_ && !AvailablePort::isPortBusy(ip, 53)) { - listenIp_ = ip; - return listenIp_; - } - } - // All IP-addresses are occupied - listenIp_.clear(); - return ""; -} - diff --git a/client/engine/engine/connectionmanager/sleepevents_mac.mm b/client/engine/engine/connectionmanager/sleepevents_mac.mm index 2261ced8c..6fbf1085b 100644 --- a/client/engine/engine/connectionmanager/sleepevents_mac.mm +++ b/client/engine/engine/connectionmanager/sleepevents_mac.mm @@ -11,17 +11,15 @@ @implementation MacSleepEvents : NSObject - (void) receiveSleepNote:(NSNotification*)notification { Q_UNUSED(notification); - if (g_SleepEvents) - { - g_SleepEvents->gotoSleep(); + if (g_SleepEvents) { + g_SleepEvents->emitGotoSleep(); } } - (void) receiveWakeNote:(NSNotification*)notification { Q_UNUSED(notification); - if (g_SleepEvents) - { - g_SleepEvents->gotoWake(); + if (g_SleepEvents) { + g_SleepEvents->emitGotoWake(); } } @end diff --git a/client/engine/engine/engine.cpp b/client/engine/engine/engine.cpp index 62ba9aa9e..4a076d4f1 100644 --- a/client/engine/engine/engine.cpp +++ b/client/engine/engine/engine.cpp @@ -37,6 +37,7 @@ #include "ipv6controller_mac.h" #include "networkdetectionmanager/reachabilityevents.h" #include "utils/network_utils/network_utils_mac.h" + #include "utils/interfaceutils_mac.h" #elif defined Q_OS_LINUX #include "helper/helper_linux.h" #include "utils/executable_signature/executablesignature_linux.h" @@ -89,6 +90,26 @@ Engine::Engine() : QObject(nullptr), qCDebug(LOG_WSNET) << logStr; }, false); + if (ExtraConfig::instance().getUsePQAlgorithms()) { + // Enable PQ algorithms. We need to set some environment variables so OpenSSL can find the right provider. +#ifdef Q_OS_WIN + wchar_t p[MAX_PATH]; + p[0] = '\0'; + GetModuleFileNameW(NULL, p, MAX_PATH); + + std::string pathStr = std::filesystem::path(p).parent_path().string(); + _putenv_s("OPENSSL_MODULES", pathStr.c_str()); + pathStr = std::filesystem::path(p).parent_path().append("openssl.cnf").string(); + _putenv_s("OPENSSL_CONF", pathStr.c_str()); +#elif defined(Q_OS_MAC) + setenv("OPENSSL_CONF", "/Applications/Windscribe.app/Contents/Resources/openssl.cnf", 1); + setenv("OPENSSL_MODULES", "/Applications/Windscribe.app/Contents/Frameworks", 1); +#elif defined(Q_OS_LINUX) + setenv("OPENSSL_CONF", "/opt/windscribe/openssl.cnf", 1); + setenv("OPENSSL_MODULES", "/opt/windscribe/lib", 1); +#endif + } + QSettings settings; std::string wsnetSettings = settings.value("wsnetSettings").toString().toStdString(); bool bWsnetSuccess = WSNet::initialize(Utils::getPlatformNameSafe().toStdString(), AppVersion::instance().semanticVersionString().toStdString(), AppVersion::instance().isStaging(), wsnetSettings); @@ -176,9 +197,9 @@ bool Engine::isApiSavedSettingsExists() return api_resources::ApiResourcesManager::isCanBeLoadFromSettings(); } -void Engine::signOut(bool keepFirewallOn) +void Engine::logout(bool keepFirewallOn) { - QMetaObject::invokeMethod(this, "signOutImpl", Q_ARG(bool, keepFirewallOn)); + QMetaObject::invokeMethod(this, "logoutImpl", Q_ARG(bool, keepFirewallOn)); } void Engine::gotoCustomOvpnConfigMode() @@ -401,13 +422,12 @@ void Engine::stopWifiSharing() } } -void Engine::startProxySharing(PROXY_SHARING_TYPE proxySharingType) +void Engine::startProxySharing(PROXY_SHARING_TYPE proxySharingType, uint port) { QMutexLocker locker(&mutex_); WS_ASSERT(bInitialized_); - if (bInitialized_) - { - QMetaObject::invokeMethod(this, "startProxySharingImpl", Q_ARG(PROXY_SHARING_TYPE, proxySharingType)); + if (bInitialized_) { + QMetaObject::invokeMethod(this, "startProxySharingImpl", Q_ARG(PROXY_SHARING_TYPE, proxySharingType), Q_ARG(uint, port)); } } @@ -544,6 +564,7 @@ void Engine::initPart2() { #ifdef Q_OS_MAC Ipv6Controller_mac::instance().setHelper(helper_); + InterfaceUtils_mac::instance().setHelper(static_cast(helper_)); ReachAbilityEvents::instance().init(); #endif @@ -557,7 +578,7 @@ void Engine::initPart2() types::MacAddrSpoofing macAddrSpoofing = engineSettings_.macAddrSpoofing(); //todo refactor #ifdef Q_OS_MAC - macAddrSpoofing.networkInterfaces = NetworkUtils_mac::currentNetworkInterfaces(true); + macAddrSpoofing.networkInterfaces = InterfaceUtils_mac::instance().currentNetworkInterfaces(true); #elif defined Q_OS_WIN macAddrSpoofing.networkInterfaces = NetworkUtils_win::currentNetworkInterfaces(true); #elif define Q_OS_LINUX @@ -590,6 +611,15 @@ void Engine::initPart2() packetSizeControllerThread_->start(QThread::LowPriority); firewallController_ = CrossPlatformObjectFactory::createFirewallController(this, helper_); +#ifdef Q_OS_LINUX + // On Linux, the system may reboot without ever sending us SIGTERM, which means we never get to clean up and + // set the boot firewall rules. Set the boot rules here if applicable. + if (engineSettings_.firewallSettings().mode == FIREWALL_MODE_ALWAYS_ON) { + firewallController_->setFirewallOnBoot(true, firewallExceptions_.getIPAddressesForFirewall(), engineSettings_.isAllowLanTraffic()); + } else { + firewallController_->setFirewallOnBoot(false); + } +#endif // do not return from this function until Engine::onHostIPsChanged() is finished // callback comes from another thread, so synchronization is needed @@ -758,34 +788,30 @@ void Engine::cleanupImpl(bool isExitWithRestart, bool isFirewallChecked, bool is SAFE_DELETE(checkUpdateManager_); #ifdef Q_OS_MAC - macSpoofTimer_->stop(); + if (macSpoofTimer_) { + macSpoofTimer_->stop(); + } #endif // to skip blocking calls - if (helper_) - { + if (helper_) { helper_->setNeedFinish(); } - if (emergencyController_) - { + if (emergencyController_) { emergencyController_->blockingDisconnect(); } - if (connectionManager_) - { + if (connectionManager_) { bool bWasIsConnected = !connectionManager_->isDisconnected(); connectionManager_->blockingDisconnect(); - if (bWasIsConnected) - { - #ifdef Q_OS_WIN + if (bWasIsConnected) { +#ifdef Q_OS_WIN enableDohSettings(); DnsInfo_win::outputDebugDnsInfo(); - #endif +#endif qCDebug(LOG_BASIC) << "Cleanup, connection manager disconnected"; - } - else - { + } else { qCDebug(LOG_BASIC) << "Cleanup, connection manager no need disconnect"; } @@ -793,8 +819,7 @@ void Engine::cleanupImpl(bool isExitWithRestart, bool isFirewallChecked, bool is } // turn off split tunneling - if (helper_) - { + if (helper_) { helper_->sendConnectStatus(false, engineSettings_.isTerminateSockets(), engineSettings_.isAllowLanTraffic(), AdapterGatewayInfo::detectAndCreateDefaultAdapterInfo(), AdapterGatewayInfo(), QString(), types::Protocol()); helper_->setSplitTunnelingSettings(false, false, false, QStringList(), QStringList(), QStringList()); } @@ -806,72 +831,48 @@ void Engine::cleanupImpl(bool isExitWithRestart, bool isFirewallChecked, bool is } #endif - if (!isExitWithRestart) - { - if (vpnShareController_) - { + if (!isExitWithRestart) { + if (vpnShareController_) { vpnShareController_->stopWifiSharing(); vpnShareController_->stopProxySharing(); } } - if (helper_ && firewallController_) - { - if (isFirewallChecked) - { - if (isExitWithRestart) - { - if (isLaunchOnStart) - { -#if defined(Q_OS_MAC) - firewallController_->enableFirewallOnBoot(true, firewallExceptions_.getIPAddressesForFirewall()); -#elif defined(Q_OS_LINUX) - firewallController_->enableFirewallOnBoot(true); + if (helper_ && firewallController_) { + if (isFirewallChecked) { + if (isExitWithRestart) { + if (isLaunchOnStart) { +#if defined(Q_OS_MAC) || defined(Q_OS_LINUX) + firewallController_->setFirewallOnBoot(true, firewallExceptions_.getIPAddressesForFirewall(), engineSettings_.isAllowLanTraffic()); #endif - } - else - { - if (isFirewallAlwaysOn) - { -#if defined(Q_OS_MAC) - firewallController_->enableFirewallOnBoot(true, firewallExceptions_.getIPAddressesForFirewall()); -#elif defined(Q_OS_LINUX) - firewallController_->enableFirewallOnBoot(true); + } else { + if (isFirewallAlwaysOn) { +#if defined(Q_OS_MAC) || defined(Q_OS_LINUX) + firewallController_->setFirewallOnBoot(true, firewallExceptions_.getIPAddressesForFirewall(), engineSettings_.isAllowLanTraffic()); #endif - } - else - { + } else { #if defined(Q_OS_MAC) || defined(Q_OS_LINUX) - firewallController_->enableFirewallOnBoot(false); + firewallController_->setFirewallOnBoot(false); #endif firewallController_->firewallOff(); } } - } - else // if exit without restart - { - if (isFirewallAlwaysOn) - { -#if defined(Q_OS_MAC) - firewallController_->enableFirewallOnBoot(true, firewallExceptions_.getIPAddressesForFirewall()); -#elif defined(Q_OS_LINUX) - firewallController_->enableFirewallOnBoot(true); + } else { // if exit without restart + if (isFirewallAlwaysOn) { +#if defined(Q_OS_MAC) || defined(Q_OS_LINUX) + firewallController_->setFirewallOnBoot(true, firewallExceptions_.getIPAddressesForFirewall(), engineSettings_.isAllowLanTraffic()); #endif - } - else - { + } else { #if defined(Q_OS_MAC) || defined(Q_OS_LINUX) - firewallController_->enableFirewallOnBoot(false); + firewallController_->setFirewallOnBoot(false); #endif firewallController_->firewallOff(); } } - } - else // if (!isFirewallChecked) - { + } else { // if (!isFirewallChecked) firewallController_->firewallOff(); #if defined(Q_OS_MAC) || defined(Q_OS_LINUX) - firewallController_->enableFirewallOnBoot(false); + firewallController_->setFirewallOnBoot(false); #endif } #ifdef Q_OS_WIN @@ -1038,32 +1039,32 @@ void Engine::getWebSessionTokenImpl(WEB_SESSION_PURPOSE purpose) }); } -// function consists of two parts (first - disconnect if need, second - do other signout stuff) -void Engine::signOutImpl(bool keepFirewallOn) +// function consists of two parts (first - disconnect if need, second - do other logout stuff) +void Engine::logoutImpl(bool keepFirewallOn) { if (!connectionManager_->isDisconnected()) { - connectionManager_->setProperty("senderSource", (keepFirewallOn ? "signOutImplKeepFirewallOn" : "signOutImpl")); + connectionManager_->setProperty("senderSource", (keepFirewallOn ? "logoutImplKeepFirewallOn" : "logoutImpl")); connectionManager_->clickDisconnect(); } else { - signOutImplAfterDisconnect(keepFirewallOn); + logoutImplAfterDisconnect(keepFirewallOn); } } -void Engine::signOutImplAfterDisconnect(bool keepFirewallOn) +void Engine::logoutImplAfterDisconnect(bool keepFirewallOn) { locationsModel_->clear(); #if defined(Q_OS_MAC) || defined(Q_OS_LINUX) - firewallController_->enableFirewallOnBoot(false); + firewallController_->setFirewallOnBoot(false); #endif if (apiResourcesManager_) { - signOutHelper_.reset(new SignOutHelper()); - signOutHelper_->signOut(apiResourcesManager_->authHash()); + logoutHelper_.reset(new LogoutHelper()); + logoutHelper_->logout(apiResourcesManager_->authHash()); apiResourcesManager_.reset(); api_resources::ApiResourcesManager::removeFromSettings(); } @@ -1075,7 +1076,7 @@ void Engine::signOutImplAfterDisconnect(bool keepFirewallOn) emit firewallStateChanged(false); } - emit signOutFinished(); + emit logoutFinished(); } void Engine::continueWithUsernameAndPasswordImpl(const QString &username, const QString &password, bool bSave) @@ -1202,6 +1203,16 @@ void Engine::setSettingsImpl(const types::EngineSettings &engineSettings) bool isMACSpoofingChanged = engineSettings_.macAddrSpoofing() != engineSettings.macAddrSpoofing(); bool isPacketSizeChanged = engineSettings_.packetSize() != engineSettings.packetSize(); bool isDnsWhileConnectedChanged = engineSettings_.connectedDnsInfo() != engineSettings.connectedDnsInfo(); + +#ifdef Q_OS_LINUX + // On Linux, the system may reboot without ever sending us SIGTERM, which means we never get to clean up and + // set the boot firewall rules. Set the boot rules here if applicable. + if (engineSettings.firewallSettings().mode == FIREWALL_MODE_ALWAYS_ON) { + firewallController_->setFirewallOnBoot(true, firewallExceptions_.getIPAddressesForFirewall(), engineSettings_.isAllowLanTraffic()); + } else { + firewallController_->setFirewallOnBoot(false); + } +#endif engineSettings_ = engineSettings; engineSettings_.saveToSettings(); @@ -1364,42 +1375,40 @@ void Engine::onConnectionManagerConnected() } - if (connectionManager_->currentProtocol().isIkev2Protocol() || connectionManager_->currentProtocol().isWireGuardProtocol()) - { - if (!packetSize_.isAutomatic) - { - int mtuForProtocol = 0; - if (connectionManager_->currentProtocol().isWireGuardProtocol()) - { - bool advParamWireguardMtuOffset = false; - int wgoffset = ExtraConfig::instance().getMtuOffsetWireguard(advParamWireguardMtuOffset); - if (!advParamWireguardMtuOffset) wgoffset = MTU_OFFSET_WG; - - mtuForProtocol = packetSize_.mtu - wgoffset; - } - else - { - bool advParamIkevMtuOffset = false; - int ikev2offset = ExtraConfig::instance().getMtuOffsetIkev2(advParamIkevMtuOffset); - if (!advParamIkevMtuOffset) ikev2offset = MTU_OFFSET_IKEV2; + if (packetSize_.isAutomatic) { + qCDebug(LOG_PACKET_SIZE) << "Packet size mode auto - using default MTU (Engine)"; + } else { + int mtu = -1; - mtuForProtocol = packetSize_.mtu - ikev2offset; - } + if (connectionManager_->currentProtocol().isIkev2Protocol()) { + bool advParamIkevMtuOffset = false; + int ikev2offset = ExtraConfig::instance().getMtuOffsetIkev2(advParamIkevMtuOffset); + if (!advParamIkevMtuOffset) ikev2offset = MTU_OFFSET_IKEV2; - if (mtuForProtocol > 0) - { - qCDebug(LOG_PACKET_SIZE) << "Applying MTU on " << adapterName << ": " << mtuForProtocol; - helper_->changeMtu(adapterName, mtuForProtocol); + mtu = packetSize_.mtu - ikev2offset; + if (mtu > 0) { + qCDebug(LOG_PACKET_SIZE) << "Applying MTU on " << adapterName << ": " << mtu; + helper_->changeMtu(adapterName, mtu); + } else { + qCDebug(LOG_PACKET_SIZE) << "Using default MTU, mtu minus overhead is too low: " << mtu; } - else - { - qCDebug(LOG_PACKET_SIZE) << "Using default MTU, mtu minus overhead is too low: " << mtuForProtocol; + } else if (connectionManager_->currentProtocol().isWireGuardProtocol()) { + bool advParamWireguardMtuOffset = false; + int wgoffset = ExtraConfig::instance().getMtuOffsetWireguard(advParamWireguardMtuOffset); + if (!advParamWireguardMtuOffset) wgoffset = MTU_OFFSET_WG; + mtu = packetSize_.mtu - wgoffset; + if (mtu > 0) { + qCDebug(LOG_PACKET_SIZE) << "Applying MTU on WindscribeWireguard: " << mtu; + // For WireGuard, this function needs the subinterface name, e.g. always "WindscribeWireguard" +#ifdef Q_OS_WIN + helper_->changeMtu("WindscribeWireguard", mtu); +#else + helper_->changeMtu(adapterName, mtu); +#endif + } else { + qCDebug(LOG_PACKET_SIZE) << "Using default MTU, mtu minus overhead is too low: " << mtu; } } - else - { - qCDebug(LOG_PACKET_SIZE) << "Packet size mode auto - using default MTU (Engine)"; - } } if (connectionManager_->isStaticIpsLocation()) @@ -1462,13 +1471,13 @@ void Engine::onConnectionManagerDisconnected(DISCONNECT_REASON reason) DnsInfo_win::outputDebugDnsInfo(); #endif - if (senderSource == "signOutImpl") + if (senderSource == "logoutImpl") { - signOutImplAfterDisconnect(false); + logoutImplAfterDisconnect(false); } - else if (senderSource == "signOutImplKeepFirewallOn") + else if (senderSource == "logoutImplKeepFirewallOn") { - signOutImplAfterDisconnect(true); + logoutImplAfterDisconnect(true); } else if (senderSource == "reconnect") { @@ -2142,9 +2151,9 @@ void Engine::checkForceDisconnectNode(const QStringList & /*forceDisconnectNodes } } -void Engine::startProxySharingImpl(PROXY_SHARING_TYPE proxySharingType) +void Engine::startProxySharingImpl(PROXY_SHARING_TYPE proxySharingType, uint port) { - vpnShareController_->startProxySharing(proxySharingType); + vpnShareController_->startProxySharing(proxySharingType, port); emit proxySharingStateChanged(true, proxySharingType, getProxySharingAddress(), 0); } @@ -2512,7 +2521,7 @@ void Engine::doCheckUpdate() void Engine::loginImpl(bool isUseAuthHash, const QString &username, const QString &password, const QString &code2fa) { - signOutHelper_.reset(); + logoutHelper_.reset(); apiResourcesManager_.reset(new api_resources::ApiResourcesManager(this, connectStateController_, networkDetectionManager_)); connect(apiResourcesManager_.get(), &api_resources::ApiResourcesManager::loginFailed, this, &Engine::onApiResourcesManagerLoginFailed); connect(apiResourcesManager_.get(), &api_resources::ApiResourcesManager::sessionDeleted, this, &Engine::onApiResourcesManagerSessionDeleted); diff --git a/client/engine/engine/engine.h b/client/engine/engine/engine.h index ee7c41ff0..48a8b75f2 100644 --- a/client/engine/engine/engine.h +++ b/client/engine/engine/engine.h @@ -23,7 +23,7 @@ #include "engine/macaddresscontroller/imacaddresscontroller.h" #include "engine/ping/keepalivemanager.h" #include "packetsizecontroller.h" -#include "signout_helper.h" +#include "logout_helper.h" #include "autoupdater/downloadhelper.h" #include "autoupdater/autoupdaterhelper_mac.h" #include "apiresources/apiresourcesmanager.h" @@ -61,7 +61,7 @@ class Engine : public QObject void loginWithUsernameAndPassword(const QString &username, const QString &password, const QString &code2fa); bool isApiSavedSettingsExists(); - void signOut(bool keepFirewallOn); + void logout(bool keepFirewallOn); void gotoCustomOvpnConfigMode(); @@ -106,7 +106,7 @@ class Engine : public QObject bool isWifiSharingSupported(); void startWifiSharing(const QString &ssid, const QString &password); void stopWifiSharing(); - void startProxySharing(PROXY_SHARING_TYPE proxySharingType); + void startProxySharing(PROXY_SHARING_TYPE proxySharingType, uint port); void stopProxySharing(); QString getProxySharingAddress(); QString getSharingCaption(); @@ -167,7 +167,7 @@ public slots: void wifiSharingStateChanged(bool bEnabled, const QString &ssid, int usersCount); void wifiSharingFailed(); - void signOutFinished(); + void logoutFinished(); void gotoCustomOvpnConfigModeFinished(); @@ -204,8 +204,8 @@ private slots: void disconnectClickImpl(); void sendDebugLogImpl(); void getWebSessionTokenImpl(WEB_SESSION_PURPOSE purpose); - void signOutImpl(bool keepFirewallOn); - void signOutImplAfterDisconnect(bool keepFirewallOn); + void logoutImpl(bool keepFirewallOn); + void logoutImplAfterDisconnect(bool keepFirewallOn); void continueWithUsernameAndPasswordImpl(const QString &username, const QString &password, bool bSave); void continueWithPasswordImpl(const QString &password, bool bSave); void continueWithPrivKeyPasswordImpl(const QString &password, bool bSave); @@ -221,7 +221,7 @@ private slots: void setSettingsImpl(const types::EngineSettings &engineSettings); void checkForceDisconnectNode(const QStringList &forceDisconnectNodes); - void startProxySharingImpl(PROXY_SHARING_TYPE proxySharingType); + void startProxySharingImpl(PROXY_SHARING_TYPE proxySharingType, uint port); void stopProxySharingImpl(); void startWifiSharingImpl(const QString &ssid, const QString &password); @@ -334,7 +334,7 @@ private slots: QScopedPointer apiResourcesManager_; // can be null for the custom config mode or when we in the logout state api_resources::CheckUpdateManager *checkUpdateManager_; api_resources::MyIpManager *myIpManager_; - std::unique_ptr signOutHelper_; + std::unique_ptr logoutHelper_; #ifdef Q_OS_WIN MeasurementCpuUsage *measurementCpuUsage_; diff --git a/client/engine/engine/firewall/firewallcontroller.h b/client/engine/engine/firewall/firewallcontroller.h index a9a949057..f85e7458a 100644 --- a/client/engine/engine/firewall/firewallcontroller.h +++ b/client/engine/engine/firewall/firewallcontroller.h @@ -25,7 +25,7 @@ class FirewallController : public QObject // Mac/Linux specific functions virtual void setInterfaceToSkip_posix(const QString &interfaceToSkip) = 0; - virtual void enableFirewallOnBoot(bool bEnable, const QSet& ipTable = QSet()) = 0; + virtual void setFirewallOnBoot(bool bEnable, const QSet& ipTable = QSet(), bool isAllowLanTraffic = false) = 0; protected: bool isStateChanged(); diff --git a/client/engine/engine/firewall/firewallcontroller_linux.cpp b/client/engine/engine/firewall/firewallcontroller_linux.cpp index dcc519d1c..a5b6a38a5 100644 --- a/client/engine/engine/firewall/firewallcontroller_linux.cpp +++ b/client/engine/engine/firewall/firewallcontroller_linux.cpp @@ -83,10 +83,10 @@ void FirewallController_linux::setInterfaceToSkip_posix(const QString &interface } } -void FirewallController_linux::enableFirewallOnBoot(bool bEnable, const QSet& ipTable) +void FirewallController_linux::setFirewallOnBoot(bool bEnable, const QSet& ipTable, bool isAllowLanTraffic) { Q_UNUSED(bEnable); - //nothing todo for Linux + helper_->setFirewallOnBoot(bEnable, ipTable, isAllowLanTraffic); } bool FirewallController_linux::firewallOnImpl(const QString &connectingIp, const QSet &ips, bool bAllowLanTraffic, bool bIsCustomConfig, const api_responses::StaticIpPortsVector &ports) @@ -114,6 +114,9 @@ bool FirewallController_linux::firewallOnImpl(const QString &connectingIp, const rules << "-A windscribe_input -i lo -j ACCEPT -m comment --comment \"" + comment_ + "\"\n"; rules << "-A windscribe_output -o lo -j ACCEPT -m comment --comment \"" + comment_ + "\"\n"; + rules << "-A windscribe_input -p udp --sport 67:68 --dport 67:68 -j ACCEPT -m comment --comment \"" + comment_ + "\"\n"; + rules << "-A windscribe_output -p udp --sport 67:68 --dport 67:68 -j ACCEPT -m comment --comment \"" + comment_ + "\"\n"; + if (!interfaceToSkip_.isEmpty()) { if (!bIsCustomConfig) { // Allow local addresses diff --git a/client/engine/engine/firewall/firewallcontroller_linux.h b/client/engine/engine/firewall/firewallcontroller_linux.h index 6bde52099..8cc747661 100644 --- a/client/engine/engine/firewall/firewallcontroller_linux.h +++ b/client/engine/engine/firewall/firewallcontroller_linux.h @@ -19,7 +19,7 @@ class FirewallController_linux : public FirewallController bool deleteWhitelistPorts() override; void setInterfaceToSkip_posix(const QString &interfaceToSkip) override; - void enableFirewallOnBoot(bool bEnable, const QSet& ipTable = QSet()) override; + void setFirewallOnBoot(bool bEnable, const QSet& ipTable = QSet(), bool isAllowLanTraffic = false) override; private: Helper_linux *helper_; diff --git a/client/engine/engine/firewall/firewallcontroller_mac.cpp b/client/engine/engine/firewall/firewallcontroller_mac.cpp index 24d9e3c33..121ee409d 100644 --- a/client/engine/engine/firewall/firewallcontroller_mac.cpp +++ b/client/engine/engine/firewall/firewallcontroller_mac.cpp @@ -381,9 +381,9 @@ void FirewallController_mac::setInterfaceToSkip_posix(const QString &interfaceTo } } -void FirewallController_mac::enableFirewallOnBoot(bool bEnable, const QSet &ipTable) +void FirewallController_mac::setFirewallOnBoot(bool bEnable, const QSet &ipTable, bool isAllowLanTraffic) { - helper_->setFirewallOnBoot(bEnable, ipTable); + helper_->setFirewallOnBoot(bEnable, ipTable, isAllowLanTraffic); } QStringList FirewallController_mac::vpnTrafficRules(const QString &connectingIp, const QString &interfaceToSkip, bool bIsCustomConfig) const diff --git a/client/engine/engine/firewall/firewallcontroller_mac.h b/client/engine/engine/firewall/firewallcontroller_mac.h index 4750bf482..122f4355c 100644 --- a/client/engine/engine/firewall/firewallcontroller_mac.h +++ b/client/engine/engine/firewall/firewallcontroller_mac.h @@ -23,7 +23,7 @@ class FirewallController_mac : public FirewallController bool deleteWhitelistPorts() override; void setInterfaceToSkip_posix(const QString &interfaceToSkip) override; - void enableFirewallOnBoot(bool bEnable, const QSet& ipTable) override; + void setFirewallOnBoot(bool bEnable, const QSet& ipTable = QSet(), bool isAllowLanTraffic = false) override; private: Helper_mac *helper_; diff --git a/client/engine/engine/firewall/firewallcontroller_win.cpp b/client/engine/engine/firewall/firewallcontroller_win.cpp index c78bfa0a4..72a495b3a 100644 --- a/client/engine/engine/firewall/firewallcontroller_win.cpp +++ b/client/engine/engine/firewall/firewallcontroller_win.cpp @@ -80,7 +80,7 @@ void FirewallController_win::setInterfaceToSkip_posix(const QString &interfaceTo //nothing todo for Windows } -void FirewallController_win::enableFirewallOnBoot(bool bEnable, const QSet &ipTable) +void FirewallController_win::setFirewallOnBoot(bool bEnable, const QSet &ipTable, bool isAllowLanTraffic) { Q_UNUSED(bEnable); //nothing todo for Windows diff --git a/client/engine/engine/firewall/firewallcontroller_win.h b/client/engine/engine/firewall/firewallcontroller_win.h index fb0081caf..0e8c0623d 100644 --- a/client/engine/engine/firewall/firewallcontroller_win.h +++ b/client/engine/engine/firewall/firewallcontroller_win.h @@ -19,7 +19,7 @@ class FirewallController_win : public FirewallController bool deleteWhitelistPorts() override; void setInterfaceToSkip_posix(const QString &interfaceToSkip) override; - void enableFirewallOnBoot(bool bEnable, const QSet& ipTable = QSet()) override; + void setFirewallOnBoot(bool bEnable, const QSet& ipTable = QSet(), bool isAllowLanTraffic = false) override; private: Helper_win *helper_win_; diff --git a/client/engine/engine/helper/helper_mac.cpp b/client/engine/engine/helper/helper_mac.cpp index 7907e5cac..4a37383cc 100644 --- a/client/engine/engine/helper/helper_mac.cpp +++ b/client/engine/engine/helper/helper_mac.cpp @@ -84,6 +84,25 @@ QString Helper_mac::getHelperVersion() return ""; } +QString Helper_mac::getInterfaceSsid(const QString &interfaceName) +{ + QMutexLocker locker(&mutex_); + + CMD_GET_INTERFACE_SSID cmd; + CMD_ANSWER answer; + cmd.interface = interfaceName.toStdString(); + + std::stringstream stream; + boost::archive::text_oarchive oa(stream, boost::archive::no_header); + oa << cmd; + + if (runCommand(HELPER_CMD_GET_INTERFACE_SSID, stream.str(), answer)) { + return QString::fromStdString(answer.body); + } else { + return ""; + } +} + bool Helper_mac::setMacAddress(const QString &interface, const QString &macAddress) { QMutexLocker locker(&mutex_); diff --git a/client/engine/engine/helper/helper_mac.h b/client/engine/engine/helper/helper_mac.h index 1f6139e1c..4e4474732 100644 --- a/client/engine/engine/helper/helper_mac.h +++ b/client/engine/engine/helper/helper_mac.h @@ -21,6 +21,8 @@ class Helper_mac : public Helper_posix bool setDnsOfDynamicStoreEntry(const QString &ipAddress, const QString &dynEnties); bool setIpv6Enabled(bool bEnabled); + QString getInterfaceSsid(const QString &interfaceName); + protected: void doDisconnectAndReconnect() override; bool runCommand(int cmdId, const std::string &data, CMD_ANSWER &answer) override; diff --git a/client/engine/engine/helper/helper_posix.cpp b/client/engine/engine/helper/helper_posix.cpp index 7330126dc..b4064b06e 100644 --- a/client/engine/engine/helper/helper_posix.cpp +++ b/client/engine/engine/helper/helper_posix.cpp @@ -23,7 +23,7 @@ #include "utils/dnsscripts_linux.h" #endif -#define SOCK_PATH "/var/run/windscribe_helper_socket2" +#define SOCK_PATH "/var/run/windscribe/helper.sock" using namespace boost::asio; @@ -377,18 +377,17 @@ bool Helper_posix::getWireGuardStatus(types::WireGuardStatus *status) return true; } -IHelper::ExecuteError Helper_posix::startCtrld(const QString &ip, const QString &upstream1, const QString &upstream2, const QStringList &domains, bool isCreateLog) +bool Helper_posix::startCtrld(const QString &upstream1, const QString &upstream2, const QStringList &domains, bool isCreateLog) { QMutexLocker locker(&mutex_); if (curState_ != STATE_CONNECTED) { - return IHelper::EXECUTE_ERROR; + return false; } std::vector domainsList; CMD_START_CTRLD cmd; - cmd.ip = ip.toStdString(); cmd.upstream1 = upstream1.toStdString(); cmd.upstream2 = upstream2.toStdString(); for (auto domain : domains) { @@ -405,10 +404,10 @@ IHelper::ExecuteError Helper_posix::startCtrld(const QString &ip, const QString if (!runCommand(HELPER_CMD_START_CTRLD, stream.str(), answer) || answer.executed == 0) { qCDebug(LOG_BASIC) << "helper returned error starting ctrld"; doDisconnectAndReconnect(); - return IHelper::EXECUTE_ERROR; + return false; } - return IHelper::EXECUTE_SUCCESS; + return true; } bool Helper_posix::stopCtrld() @@ -566,13 +565,14 @@ bool Helper_posix::getFirewallRules(CmdIpVersion version, const QString &table, return true; } -bool Helper_posix::setFirewallOnBoot(bool bEnabled, const QSet &ipTable) +bool Helper_posix::setFirewallOnBoot(bool enabled, const QSet &ipTable, bool allowLanTraffic) { QMutexLocker locker(&mutex_); CMD_SET_FIREWALL_ON_BOOT cmd; CMD_ANSWER answer; - cmd.enabled = bEnabled; + cmd.enabled = enabled; + cmd.allowLanTraffic = allowLanTraffic; std::string ipTableStr = ""; for (const auto& ip : ipTable) { diff --git a/client/engine/engine/helper/helper_posix.h b/client/engine/engine/helper/helper_posix.h index b073c59f6..564f029d1 100644 --- a/client/engine/engine/helper/helper_posix.h +++ b/client/engine/engine/helper/helper_posix.h @@ -42,7 +42,7 @@ class Helper_posix : public IHelper bool getWireGuardStatus(types::WireGuardStatus *status) override; // ctrld functions - ExecuteError startCtrld(const QString &ip, const QString &upstream1, const QString &upstream2, const QStringList &domains, bool isCreateLog) override; + bool startCtrld(const QString &upstream1, const QString &upstream2, const QStringList &domains, bool isCreateLog) override; bool stopCtrld() override; // Posix specific functions @@ -52,7 +52,7 @@ class Helper_posix : public IHelper bool clearFirewallRules(bool isKeepPfEnabled); bool setFirewallRules(CmdIpVersion version, const QString &table, const QString &group, const QString &rules); bool getFirewallRules(CmdIpVersion version, const QString &table, const QString &group, QString &rules); - bool setFirewallOnBoot(bool bEnabled, const QSet& ipTable); + bool setFirewallOnBoot(bool enabled, const QSet& ipTable, bool allowLanTraffic); bool startStunnel(const QString &hostname, unsigned int port, unsigned int localPort, bool extraPadding); bool startWstunnel(const QString &hostname, unsigned int port, unsigned int localPort); diff --git a/client/engine/engine/helper/helper_win.cpp b/client/engine/engine/helper/helper_win.cpp index 07b5bdf19..afe7f6f73 100644 --- a/client/engine/engine/helper/helper_win.cpp +++ b/client/engine/engine/helper/helper_win.cpp @@ -292,10 +292,10 @@ bool Helper_win::getWireGuardStatus(types::WireGuardStatus *status) return mpr.success; } -IHelper::ExecuteError Helper_win::startCtrld(const QString &ip, const QString &upstream1, const QString &upstream2, const QStringList &domains, bool isCreateLog) +bool Helper_win::startCtrld(const QString &upstream1, const QString &upstream2, const QStringList &domains, bool isCreateLog) { // Nothing to do. - return IHelper::EXECUTE_SUCCESS; + return true; } bool Helper_win::stopCtrld() diff --git a/client/engine/engine/helper/helper_win.h b/client/engine/engine/helper/helper_win.h index 7650ba173..7fe904f5b 100644 --- a/client/engine/engine/helper/helper_win.h +++ b/client/engine/engine/helper/helper_win.h @@ -47,7 +47,7 @@ class Helper_win : public IHelper bool getWireGuardStatus(types::WireGuardStatus *status) override; // ctrld functions - ExecuteError startCtrld(const QString &ip, const QString &upstream1, const QString &upstream2, const QStringList &domains, bool isCreateLog) override; + bool startCtrld(const QString &upstream1, const QString &upstream2, const QStringList &domains, bool isCreateLog) override; bool stopCtrld() override; // Windows specific functions diff --git a/client/engine/engine/helper/ihelper.h b/client/engine/engine/helper/ihelper.h index cbf29b9f2..9f17f1bad 100644 --- a/client/engine/engine/helper/ihelper.h +++ b/client/engine/engine/helper/ihelper.h @@ -53,7 +53,7 @@ class IHelper : public QThread virtual bool getWireGuardStatus(types::WireGuardStatus *status) = 0; // ctrld functions - virtual ExecuteError startCtrld(const QString &ip, const QString &upstream1, const QString &upstream2, const QStringList &domains, bool isCreateLog) = 0; + virtual bool startCtrld(const QString &upstream1, const QString &upstream2, const QStringList &domains, bool isCreateLog) = 0; virtual bool stopCtrld() = 0; signals: diff --git a/client/engine/engine/helper/installhelper_mac.mm b/client/engine/engine/helper/installhelper_mac.mm index 71d010828..91781e50a 100644 --- a/client/engine/engine/helper/installhelper_mac.mm +++ b/client/engine/engine/helper/installhelper_mac.mm @@ -106,7 +106,6 @@ CFBundleRef bundle = CFBundleCreate(NULL, url); CFRelease(url); - if (bundle == NULL) { return false; } diff --git a/client/engine/engine/signout_helper.h b/client/engine/engine/logout_helper.h similarity index 89% rename from client/engine/engine/signout_helper.h rename to client/engine/engine/logout_helper.h index c27bea06e..de367f232 100644 --- a/client/engine/engine/signout_helper.h +++ b/client/engine/engine/logout_helper.h @@ -3,10 +3,10 @@ #include #include "utils/logger.h" -class SignOutHelper final +class LogoutHelper final { public: - ~SignOutHelper() + ~LogoutHelper() { if (request_) { qCDebug(LOG_BASIC) << "deleteSession request canceled"; @@ -14,7 +14,7 @@ class SignOutHelper final } } - void signOut(const QString &authHash) + void logout(const QString &authHash) { auto callback = [this](wsnet::ServerApiRetCode serverApiRetCode, const std::string &jsonData) { diff --git a/client/engine/engine/macaddresscontroller/macaddresscontroller_mac.cpp b/client/engine/engine/macaddresscontroller/macaddresscontroller_mac.cpp index b9117033e..c7a44c95e 100644 --- a/client/engine/engine/macaddresscontroller/macaddresscontroller_mac.cpp +++ b/client/engine/engine/macaddresscontroller/macaddresscontroller_mac.cpp @@ -1,5 +1,6 @@ #include "macaddresscontroller_mac.h" +#include "utils/interfaceutils_mac.h" #include "utils/logger.h" #include "utils/network_utils/network_utils.h" #include "utils/network_utils/network_utils_mac.h" @@ -45,7 +46,7 @@ void MacAddressController_mac::setMacAddrSpoofing(const types::MacAddrSpoofing & qCDebug(LOG_BASIC) << "MacAddressController_mac::setMacAddrSpoofing MacAddrSpoofing has changed. actuallyAutoRotate_=" << actuallyAutoRotate_; qCDebug(LOG_BASIC) << macAddrSpoofing; - types::NetworkInterface currentAdapter = NetworkUtils_mac::currentNetworkInterface(); + types::NetworkInterface currentAdapter = InterfaceUtils_mac::instance().currentNetworkInterface(); types::NetworkInterface selectedInterface = macAddrSpoofing.selectedNetworkInterface; if (macAddrSpoofing.isEnabled) { @@ -68,7 +69,7 @@ void MacAddressController_mac::setMacAddrSpoofing(const types::MacAddrSpoofing & } // remove all but last - const QVector spoofedInterfacesExceptLast = NetworkUtils::interfacesExceptOne(NetworkUtils_mac::currentSpoofedInterfaces(), lastInterface); + const QVector spoofedInterfacesExceptLast = NetworkUtils::interfacesExceptOne(InterfaceUtils_mac::instance().currentSpoofedInterfaces(), lastInterface); for (const types::NetworkInterface &networkInterface : spoofedInterfacesExceptLast) { spoofsToRemove.insert(networkInterface.interfaceName, ""); } @@ -101,7 +102,7 @@ void MacAddressController_mac::setMacAddrSpoofing(const types::MacAddrSpoofing & } // remove all spoofs except current - const QVector spoofedInterfacesExceptLast = NetworkUtils::interfacesExceptOne(NetworkUtils_mac::currentSpoofedInterfaces(), currentAdapter); + const QVector spoofedInterfacesExceptLast = NetworkUtils::interfacesExceptOne(InterfaceUtils_mac::instance().currentSpoofedInterfaces(), currentAdapter); for (const types::NetworkInterface &networkInterface : spoofedInterfacesExceptLast) { spoofsToRemove.insert(networkInterface.interfaceName, ""); } @@ -122,7 +123,7 @@ void MacAddressController_mac::setMacAddrSpoofing(const types::MacAddrSpoofing & qCDebug(LOG_BASIC) << "MacAddressController_mac::setMacAddrSpoofing MAC spoofing is disabled"; QMap spoofsToRemove; - const QVector spoofs = NetworkUtils_mac::currentSpoofedInterfaces(); + const QVector spoofs = InterfaceUtils_mac::instance().currentSpoofedInterfaces(); for (const types::NetworkInterface &networkInterface : spoofs) { // only unspoof adapters that we spoofed, not from other sources if (NetworkUtils_mac::checkMacAddr(networkInterface.interfaceName, macAddrSpoofing_.macAddress)) { @@ -198,7 +199,7 @@ bool MacAddressController_mac::doneFilteringNetworkEvents() } qCDebug(LOG_BASIC) << "Filtering MAC spoofing network events"; - QVector networkInterfaces = NetworkUtils_mac::currentNetworkInterfaces(true); + QVector networkInterfaces = InterfaceUtils_mac::instance().currentNetworkInterfaces(true); for (const types::NetworkInterface &networkInterface : networkInterfaces) { if (networksBeingUpdated_.contains(networkInterface.interfaceName)) { if (NetworkUtils_mac::isAdapterUp(networkInterface.interfaceName)) { @@ -242,7 +243,7 @@ void MacAddressController_mac::autoRotateUpdateMacSpoof() types::MacAddrSpoofing MacAddressController_mac::macAddrSpoofingWithUpdatedNetworkList() { types::MacAddrSpoofing updatedMacAddrSpoofing = macAddrSpoofing_; - QVector networkInterfaces = NetworkUtils_mac::currentNetworkInterfaces(true); + QVector networkInterfaces = InterfaceUtils_mac::instance().currentNetworkInterfaces(true); // update adapter list updatedMacAddrSpoofing.networkInterfaces = networkInterfaces; diff --git a/client/engine/engine/networkdetectionmanager/networkdetectionmanager_mac.cpp b/client/engine/engine/networkdetectionmanager/networkdetectionmanager_mac.cpp index 15e100e62..a55372737 100644 --- a/client/engine/engine/networkdetectionmanager/networkdetectionmanager_mac.cpp +++ b/client/engine/engine/networkdetectionmanager/networkdetectionmanager_mac.cpp @@ -6,6 +6,7 @@ #include "utils/logger.h" #include "utils/network_utils/network_utils.h" #include "utils/network_utils/network_utils_mac.h" +#include "utils/interfaceutils_mac.h" #include "utils/utils.h" const int typeIdNetworkInterface = qRegisterMetaType("types::NetworkInterface"); @@ -15,7 +16,7 @@ NetworkDetectionManager_mac::NetworkDetectionManager_mac(QObject *parent, IHelpe , lastWifiAdapterUp_(false) { connect(&ReachAbilityEvents::instance(), &ReachAbilityEvents::networkStateChanged, this, &NetworkDetectionManager_mac::onNetworkStateChanged); - lastNetworkList_ = NetworkUtils_mac::currentNetworkInterfaces(true); + lastNetworkList_ = InterfaceUtils_mac::instance().currentNetworkInterfaces(true); lastNetworkInterface_ = currentNetworkInterfaceFromNetworkList(lastNetworkList_); lastWifiAdapterUp_ = isWifiAdapterUp(lastNetworkList_); lastIsOnlineState_ = isOnlineImpl(); @@ -40,7 +41,7 @@ void NetworkDetectionManager_mac::onNetworkStateChanged() emit onlineStateChanged(curIsOnlineState); } - const QVector &networkList = NetworkUtils_mac::currentNetworkInterfaces(true); + const QVector &networkList = InterfaceUtils_mac::instance().currentNetworkInterfaces(true); const types::NetworkInterface &networkInterface = currentNetworkInterfaceFromNetworkList(networkList); bool wifiAdapterUp = isWifiAdapterUp(networkList); diff --git a/client/engine/engine/vpnshare/vpnsharecontroller.cpp b/client/engine/engine/vpnshare/vpnsharecontroller.cpp index 46ca64d12..130d2f910 100644 --- a/client/engine/engine/vpnshare/vpnsharecontroller.cpp +++ b/client/engine/engine/vpnshare/vpnsharecontroller.cpp @@ -32,7 +32,7 @@ VpnShareController::~VpnShareController() #endif } -void VpnShareController::startProxySharing(PROXY_SHARING_TYPE proxyType) +void VpnShareController::startProxySharing(PROXY_SHARING_TYPE proxyType, uint port) { QMutexLocker locker(&mutex_); @@ -43,39 +43,38 @@ void VpnShareController::startProxySharing(PROXY_SHARING_TYPE proxyType) httpProxyServer_ = new HttpProxyServer::HttpProxyServer(this); connect(httpProxyServer_, &HttpProxyServer::HttpProxyServer::usersCountChanged, this, &VpnShareController::onProxyUsersCountChanged); - uint port; bool isStarted = false; - if (getLastSavedPort(port)) - { + if (port != 0) { + // Port provided by user, use it. isStarted = httpProxyServer_->startServer(port); + } else { + if (getLastSavedPort(port)) { + isStarted = httpProxyServer_->startServer(port); + } + if (!isStarted) { + uint randomPort = AvailablePort::getAvailablePort(18888); + httpProxyServer_->startServer(randomPort); + saveLastPort(randomPort); + } } - if (!isStarted) - { - uint randomPort = AvailablePort::getAvailablePort(18888); - httpProxyServer_->startServer(randomPort); - saveLastPort(randomPort); - } - } - else if (proxyType == PROXY_SHARING_SOCKS) - { + } else if (proxyType == PROXY_SHARING_SOCKS) { socksProxyServer_ = new SocksProxyServer::SocksProxyServer(this); connect(socksProxyServer_, &SocksProxyServer::SocksProxyServer::usersCountChanged, this, &VpnShareController::onProxyUsersCountChanged); - uint port; bool isStarted = false; - if (getLastSavedPort(port)) - { + if (port != 0) { isStarted = socksProxyServer_->startServer(port); + } else { + if (getLastSavedPort(port)) { + isStarted = socksProxyServer_->startServer(port); + } + if (!isStarted) { + uint randomPort = AvailablePort::getAvailablePort(18888); + socksProxyServer_->startServer(randomPort); + saveLastPort(randomPort); + } } - if (!isStarted) - { - uint randomPort = AvailablePort::getAvailablePort(18888); - socksProxyServer_->startServer(randomPort); - saveLastPort(randomPort); - } - } - else - { + } else { WS_ASSERT(false); } } diff --git a/client/engine/engine/vpnshare/vpnsharecontroller.h b/client/engine/engine/vpnshare/vpnsharecontroller.h index f4ab4d2e5..3d66ffbac 100644 --- a/client/engine/engine/vpnshare/vpnsharecontroller.h +++ b/client/engine/engine/vpnshare/vpnsharecontroller.h @@ -22,7 +22,7 @@ class VpnShareController : public QObject void onConnectedToVPNEvent(const QString &vpnAdapterName); void onDisconnectedFromVPNEvent(); - void startProxySharing(PROXY_SHARING_TYPE proxyType); + void startProxySharing(PROXY_SHARING_TYPE proxyType, uint port); void stopProxySharing(); bool isWifiSharingSupported(); diff --git a/client/engine/engine/wireguardconfig/getwireguardconfig.cpp b/client/engine/engine/wireguardconfig/getwireguardconfig.cpp index 80800097a..4e8420442 100644 --- a/client/engine/engine/wireguardconfig/getwireguardconfig.cpp +++ b/client/engine/engine/wireguardconfig/getwireguardconfig.cpp @@ -144,7 +144,7 @@ void GetWireGuardConfig::submitWireguardConnectRequest() { WS_ASSERT(request_ == nullptr); request_ = WSNet::instance()->serverAPI()->wgConfigsConnect(apiinfo::ApiInfo::getAuthHash().toStdString(), wireGuardConfig_.clientPublicKey().toStdString(), - serverName_.toStdString(), deviceId_.toStdString(), + serverName_.toStdString(), deviceId_.toStdString(), std::string(), [this](ServerApiRetCode serverApiRetCode, const std::string &jsonData) { QMetaObject::invokeMethod(this, [this, serverApiRetCode, jsonData]() { diff --git a/client/engine/mac/resources/cert.pem b/client/engine/mac/resources/cert.pem deleted file mode 100644 index e62c7eeb9..000000000 --- a/client/engine/mac/resources/cert.pem +++ /dev/null @@ -1,4649 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDGzCCAgOgAwIBAgIEDMOcgDANBgkqhkiG9w0BAQsFADA8MSAwHgYDVQQDDBdj -b20uYXBwbGUuc3lzdGVtZGVmYXVsdDEYMBYGA1UECgwPU3lzdGVtIElkZW50aXR5 -MB4XDTE2MDQyNjE1MzIwM1oXDTM2MDQyMTE1MzIwM1owPDEgMB4GA1UEAwwXY29t -LmFwcGxlLnN5c3RlbWRlZmF1bHQxGDAWBgNVBAoMD1N5c3RlbSBJZGVudGl0eTCC -ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJIxCb44tyWb5zBNM8ZUkD8P -UE3xpJju8vu0mFKb5UwkQ8NOEXkXcp96X3L5K0+R5rNAu6pWqNo9dFuGPG4M3LM9 -pBF8Th4xkk2qX9s3B/dHwjNckwGjvIGKrh5Ub5lnVEGGqdhddxHTGn3Lp6LlRZgU -hz2MjmzbXdHtj1wcJHHdHNo0Zd4QXAAqehXca8jWM1wuimqfxhHNw7/t3kv+fm6z -SgKFPXibLJK9bKTmo06clWa+V41iv4xr8jrRY/7fIPFwxggceHoxyfWLH0ryzfTE -0SZgwLlwOGdxYRg3D88Ikjh/SSa8lHOZP1uHqmk6JWt0PmWEr2reMD8oqfgYV8MC -AwEAAaMlMCMwCwYDVR0PBAQDAgSwMBQGA1UdJQQNMAsGCSqGSIb3Y2QEBDANBgkq -hkiG9w0BAQsFAAOCAQEAOt+yaI4SJ9YvcWQ/kZyl4w838+yyiiIFiBUBJBjPE5C/ -P7DIPGRnN/F58NTAkQXfK5G7PaspFamfOwOl1cdWV6hbFsqI/2R/746rIawy5YNy -wNwkNURlH7Of8VbTGPEiR9ivZIm+7q9J5P0/ZGfBqFWI8qgdrAkbfBKOlXizzrfA -MJqxAyTk5dCz2LTImmVx3IMH9yfnpW5aJLmEZ9yEwtLk11JoOv7yIiE9gOoYobcN -Gsu3cqQEUjk+A3DV/A2m2ucGBdAJUIlBehq9PribicaIWnfNaSICR7HFAW+yNrT1 -VimwffR/mPzmyDnfMpgK2LxRBLQLhCschlZt4OK82w== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDLDCCAhSgAwIBAgIELcGEgTANBgkqhkiG9w0BAQsFADA7MR8wHQYDVQQDDBZj -b20uYXBwbGUua2VyYmVyb3Mua2RjMRgwFgYDVQQKDA9TeXN0ZW0gSWRlbnRpdHkw -HhcNMTYwNDI2MTUzMjA0WhcNMzYwNDIxMTUzMjA0WjA7MR8wHQYDVQQDDBZjb20u -YXBwbGUua2VyYmVyb3Mua2RjMRgwFgYDVQQKDA9TeXN0ZW0gSWRlbnRpdHkwggEi -MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC7ju5uL/kkNWJN1oSjOp7xGBWN -cCB0WKTXyfogbM+65lCgSewcH3CxlhMO5YAHRWyb6iHNB5VP1HXu8ahB7S+VITht -Dzpa8ur4EUzOR+C/DymtiL7zsuJdkAmGUi9kmzV8Su0rR7YU37n4x7xwl4m7v9kA -W6YGHTEFq0N44Fbhp/wtPtCLS+p3gjDgUKlBD8Xi/riJAD8Vhwlv0Cxc5FPrNLd9 -/m10OKNURpxjJtM4pj4gQDtXK5Ot0d0H1f1N3QF4jTuURMafRnXeFuejQwt+3Qwv -hd4QdexHCMMG9XTv8sxTTnDxWXe3klf+6J9GDd4ydp3MTND6kMdQeBDc/lLVAgMB -AAGjODA2MAsGA1UdDwQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATASBgNVHSUE -CzAJBgcrBgEFAgMFMA0GCSqGSIb3DQEBCwUAA4IBAQCt8zyHuAMqVTjodVwITaxu -piyhP0ROuB2s4UKFe3Q5N5OYtPXrUUI/BGBXifQXDAHGmBCMzoBLWtZc/gYO3q+m -uIkRxcS5CkVIOxKY29oEIdOOLAFinYGD+Jn8810BHra1zXnqFA6DUpFyIIy2yzxU -+hSN+QSO/MUmpLomrGfOPdyQlAAS+OUdqVO4kbwskZe0G/tMa28OSbSvyXP/G+Q3 -dGleiZ1FUHoebpzhNduORqbAhOnPedGswKvzDvL76mVc8/QDepTFVekOAmVU0ZZC -HxY/oYF19216q/d99ARocFsBg9HULOesGS6HMkqlK551f2jszy2qCoNl3ARm1ZqX ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEIjCCAwqgAwIBAgIIAd68xDltoBAwDQYJKoZIhvcNAQEFBQAwYjELMAkGA1UE -BhMCVVMxEzARBgNVBAoTCkFwcGxlIEluYy4xJjAkBgNVBAsTHUFwcGxlIENlcnRp -ZmljYXRpb24gQXV0aG9yaXR5MRYwFAYDVQQDEw1BcHBsZSBSb290IENBMB4XDTEz -MDIwNzIxNDg0N1oXDTIzMDIwNzIxNDg0N1owgZYxCzAJBgNVBAYTAlVTMRMwEQYD -VQQKDApBcHBsZSBJbmMuMSwwKgYDVQQLDCNBcHBsZSBXb3JsZHdpZGUgRGV2ZWxv -cGVyIFJlbGF0aW9uczFEMEIGA1UEAww7QXBwbGUgV29ybGR3aWRlIERldmVsb3Bl -ciBSZWxhdGlvbnMgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3 -DQEBAQUAA4IBDwAwggEKAoIBAQDKOFSmy1aqyCQ5SOmM7uxfuH8mkbw0U3rOfGOA -YXdkXqUHI7Y5/lAtFVZYcC1+xG7BSoU+L/DehBqhV8mvexj/avoVEkkVCBmsqtsq -Mu2WY2hSFT2Miuy/axiV4AOsAX2XBWfODoWVN2rtCbauZ81RZJ/GXNG8V25nNYB2 -NqSHgW44j9grFU57Jdhav06DwY3Sk9UacbVgnJ0zTlX5ElgMhrgWDcHld0WNUEi6 -Ky3klIXh6MSdxmilsKP8Z35wugJZS3dCkTm59c3hTO/AO0iMpuUhXf1qarunFjVg -0uat80YpyejDi+l5wGphZxWy8P3laLxiX27Pmd3vG2P+kmWrAgMBAAGjgaYwgaMw -HQYDVR0OBBYEFIgnFwmpthhgi+zruvZHWcVSVKO3MA8GA1UdEwEB/wQFMAMBAf8w -HwYDVR0jBBgwFoAUK9BpR5R2Cf70a40uQKb3R01/CF4wLgYDVR0fBCcwJTAjoCGg -H4YdaHR0cDovL2NybC5hcHBsZS5jb20vcm9vdC5jcmwwDgYDVR0PAQH/BAQDAgGG -MBAGCiqGSIb3Y2QGAgEEAgUAMA0GCSqGSIb3DQEBBQUAA4IBAQBPz+9Zviz1smwv -j+4ThzLoBTWobot9yWkMudkXvHcs1Gfi/ZptOllc34MBvbKuKmFysa/Nw0Uwj6OD -Dc4dR7Txk4qjdJukw5hyhzs+r0ULklS5MruQGFNrCk4QttkdUGwhgAqJTleMa1s8 -Pab93vcNIx0LSiaHP7qRkkykGRIZbVf1eliHe2iK5IaMSuviSRSqpd1VAKmuu0sw -ruGgsbwpgOYJd+W+NKIByn/c4grmO7i77LpilfMFY0GCzQ87HUyVpNur+cmV6U/k -TecmmYHpvPm0KdIBembhLoz2IYrF+Hjhga6/05Cdqa3zr/04GpZnMBxRpVzscYqC -tGwPDBUf ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEXDCCA0SgAwIBAgIEOGO5ZjANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChML -RW50cnVzdC5uZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBp -bmNvcnAuIGJ5IHJlZi4gKGxpbWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5 -IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNVBAMTKkVudHJ1c3QubmV0IENlcnRp -ZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEyMjQxNzUwNTFaFw0xOTEy -MjQxODIwNTFaMIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3d3d3 -LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxp -YWIuKTElMCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEG -A1UEAxMqRW50cnVzdC5uZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgp -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArU1LqRKGsuqjIAcVFmQq -K0vRvwtKTY7tgHalZ7d4QMBzQshowNtTK91euHaYNZOLGp18EzoOH1u3Hs/lJBQe -sYGpjX24zGtLA/ECDNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSrhRSGlVuX -MlBvPci6Zgzj/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVT -XTzWnLLPKQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/ -HoZdenoVve8AjhUiVBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH -4QIDAQABo3QwcjARBglghkgBhvhCAQEEBAMCAAcwHwYDVR0jBBgwFoAUVeSB0RGA -vtiJuQijMfmhJAkWuXAwHQYDVR0OBBYEFFXkgdERgL7YibkIozH5oSQJFrlwMB0G -CSqGSIb2fQdBAAQQMA4bCFY1LjA6NC4wAwIEkDANBgkqhkiG9w0BAQUFAAOCAQEA -WUesIYSKF8mciVMeuoCFGsY8Tj6xnLZ8xpJdGGQC49MGCBFhfGPjK50xA3B20qMo -oPS7mmNz7W3lKtvtFKkrxjYR0CvrB4ul2p5cGZ1WEvVUKcgF7bISKo30Axv/55IQ -h7A6tcOdBTcSo8f0FbnVpDkWm1M6I5HxqIKiaohowXkCIryqptau37AUX7iH0N18 -f3v/rxzP5tsHrV7bhZ3QKw0z2wTR5klAEyt2+z7pnIkPFc4YsIV4IU9rTw76NmfN -B/L/CNDi3tm/Kq+4h4YhPATKt5Rof8886ZjXOP/swNlQ8C5LWK5Gb9Auw2DaclVy -vUxFnmG6v4SBkgPR0ml8xQ== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIIGDCCBgCgAwIBAgIGAT8vMXfmMA0GCSqGSIb3DQEBCwUAMIIBCjELMAkGA1UE -BhMCRVMxEjAQBgNVBAgMCUJhcmNlbG9uYTFYMFYGA1UEBwxPQmFyY2Vsb25hIChz -ZWUgY3VycmVudCBhZGRyZXNzIGF0IGh0dHA6Ly93d3cuYW5mLmVzL2VzL2FkZHJl -c3MtZGlyZWNjaW9uLmh0bWwgKTEnMCUGA1UECgweQU5GIEF1dG9yaWRhZCBkZSBD -ZXJ0aWZpY2FjaW9uMRcwFQYDVQQLDA5BTkYgQ2xhc2UgMSBDQTEaMBgGCSqGSIb3 -DQEJARYLaW5mb0BhbmYuZXMxEjAQBgNVBAUTCUc2MzI4NzUxMDEbMBkGA1UEAwwS -QU5GIEdsb2JhbCBSb290IENBMB4XDTEzMDYxMDE3NDUzOFoXDTMzMDYwNTE3NDUz -OFowggEKMQswCQYDVQQGEwJFUzESMBAGA1UECAwJQmFyY2Vsb25hMVgwVgYDVQQH -DE9CYXJjZWxvbmEgKHNlZSBjdXJyZW50IGFkZHJlc3MgYXQgaHR0cDovL3d3dy5h -bmYuZXMvZXMvYWRkcmVzcy1kaXJlY2Npb24uaHRtbCApMScwJQYDVQQKDB5BTkYg -QXV0b3JpZGFkIGRlIENlcnRpZmljYWNpb24xFzAVBgNVBAsMDkFORiBDbGFzZSAx -IENBMRowGAYJKoZIhvcNAQkBFgtpbmZvQGFuZi5lczESMBAGA1UEBRMJRzYzMjg3 -NTEwMRswGQYDVQQDDBJBTkYgR2xvYmFsIFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEB -AQUAA4ICDwAwggIKAoICAQDHPi9xy4wynbcUbWjorVUgQKeUAVh937J7P37XmsfH -ZLOBZKIIlhhCtRwnDlg7x+BUvtJOTkIbEGMujDygUQ2s3HDYr5I41hTyM2Pl0cq2 -EuSGEbPIHb3dEX8NAguFexM0jqNjrreN3hM2/+TOkAxSdDJP2aMurlySC5zwl47K -ZLHtcVrkZnkDa0o5iN24hJT4vBDT4t2q9khQ+qb1D8KgCOb02r1PxWXu3vfd6Ha2 -mkdB97iGuEh5gO2n4yOmFS5goFlVA2UdPbbhJsb8oKVKDd+YdCKGQDCkQyG4AjmC -YiNm3UPG/qtftTH5cWri67DlLtm6fyUFOMmO6NSh0RtR745pL8GyWJUanyq/Q4bF -HQB21E+WtTsCaqjGaoFcrBunMypmCd+jUZXl27TYENRFbrwNdAh7m2UztcIyb+Sg -VJFyfvVsBQNvnp7GPimVxXZNc4VpxEXObRuPWQN1oZN/90PcZVqTia/SHzEyTryL -ckhiLG3jZiaFZ7pTZ5I9wti9Pn+4kOHvE3Y/4nEnUo4mTxPX9pOlinF+VCiybtV2 -u1KSlc+YaIM7VmuyndDZCJRXm3v0/qTE7t5A5fArZl9lvibigMbWB8fpD+c1GpGH -Eo8NRY0lkaM+DkIqQoaziIsz3IKJrfdKaq9bQMSlIfameKBZ8fNYTBZrH9KZAIhz -YwIDAQABo4IBfjCCAXowHQYDVR0OBBYEFIf6nt9SdnXsSUogb1twlo+d77sXMB8G -A1UdIwQYMBaAFIf6nt9SdnXsSUogb1twlo+d77sXMA8GA1UdEwEB/wQFMAMBAf8w -DgYDVR0PAQH/BAQDAgEGMIIBFQYDVR0RBIIBDDCCAQiCEWh0dHA6Ly93d3cuYW5m -LmVzgQtpbmZvQGFuZi5lc6SB5TCB4jE0MDIGA1UECQwrR3JhbiBWaWEgZGUgbGVz -IENvcnRzIENhdGFsYW5lcy4gOTk2LiAwODAxODESMBAGA1UEBwwJQmFyY2Vsb25h -MScwJQYDVQQKDB5BTkYgQXV0b3JpZGFkIGRlIENlcnRpZmljYWNpb24xEjAQBgNV -BAUTCUc2MzI4NzUxMDFZMFcGA1UECwxQSW5zY3JpdGEgZW4gZWwgTWluaXN0ZXJp -byBkZWwgSW50ZXJpb3IgZGUgRXNwYcOxYSBjb24gZWwgbnVtZXJvIG5hY2lvbmFs -IDE3MS40NDMwDQYJKoZIhvcNAQELBQADggIBAIgR9tFTZ9BCYg+HViMxOfF0MHN2 -Pe/eC128ARdS+GH8A4thtbqiH/SOYbWofO/0zssHhNKa5iQEj45lCAb8BANpWJMD -nWkPr6jq2+50a6d0MMgSS2l1rvjSF+3nIrEuicshHXSTi3q/vBLKr7uGKMVFaM68 -XAropIwk6ndlA0JseARSPsbetv7ALESMIZAxlHV1TcctYHd0bB3c/Jz+PLszJQqs -Cg/kBPo2D111OXZkIY8W/fJuG9veR783khAK2gUnC0zLLCNsYzEbdGt8zUmBsAsM -cGxqGm6B6vDXd65OxWqw13xdq/24+5R8Ng1PF9tvfjZkUFBF30CxjWur7P90WiKI -G7IGfr6BE1NgXlhEQQu4F+HizB1ypEPzGWltecXQ4yOzO+H0WfFTjLTYX6VSveyW -DQV18ixF8M4tHP/SwNE+yyv2b2JJ3/3RpxjtFlLk+opJ574x0gD/dMJuWTH0JqVY -3PbRfE1jIxFpk164Qz/Xp7H7w7f6xh+tQCkBs3PUYmnGIZcPwq44Q6JHlCNsKx4K -hxfggTvRCk4w79cUID45c2qDsRCqTPoOo/cbOpcfVhbH9LdMORpmuLwNogRZEUSE -fWpqR9q+0kcQf4zGSWIURIyDrogdpDgoHDxktqgMgc+qA4ZE2WQl1D8hmev53A46 -lUSrWUiWfDXtK3ux ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDoDCCAoigAwIBAgIBMTANBgkqhkiG9w0BAQUFADBDMQswCQYDVQQGEwJKUDEc -MBoGA1UEChMTSmFwYW5lc2UgR292ZXJubWVudDEWMBQGA1UECxMNQXBwbGljYXRp -b25DQTAeFw0wNzEyMTIxNTAwMDBaFw0xNzEyMTIxNTAwMDBaMEMxCzAJBgNVBAYT -AkpQMRwwGgYDVQQKExNKYXBhbmVzZSBHb3Zlcm5tZW50MRYwFAYDVQQLEw1BcHBs -aWNhdGlvbkNBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp23gdE6H -j6UG3mii24aZS2QNcfAKBZuOquHMLtJqO8F6tJdhjYq+xpqcBrSGUeQ3DnR4fl+K -f5Sk10cI/VBaVuRorChzoHvpfxiSQE8tnfWuREhzNgaeZCw7NCPbXCbkcXmP1G55 -IrmTwcrNwVbtiGrXoDkhBFcsovW8R0FPXjQilbUfKW1eSvNNcr5BViCH/OlQR9cw -FO5cjFW6WY2H/CPek9AEjP3vbb3QesmlOmpyM8ZKDQUXKi17safY1vC+9D/qDiht -QWEjdnjDuGWk81quzMKq2edY3rZ+nYVunyoKb58DKTCXKB28t89UKU5RMfkntigm -/qJj5kEW8DOYRwIDAQABo4GeMIGbMB0GA1UdDgQWBBRUWssmP3HMlEYNllPqa0jQ -k/5CdTAOBgNVHQ8BAf8EBAMCAQYwWQYDVR0RBFIwUKROMEwxCzAJBgNVBAYTAkpQ -MRgwFgYDVQQKDA/ml6XmnKzlm73mlL/lupwxIzAhBgNVBAsMGuOCouODl+ODquOC -seODvOOCt+ODp+ODs0NBMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQAD -ggEBADlqRHZ3ODrso2dGD/mLBqj7apAxzn7s2tGJfHrrLgy9mTLnsCTWw//1sogJ -hyzjVOGjprIIC8CFqMjSnHH2HZ9g/DgzE+Ge3Atf2hZQKXsvcJEPmbo0NI2VdMV+ -eKlmXb3KIXdCEKxmJj3ekav9FfBv7WxfEPjzFvYDio+nEhEMy/0/ecGc/WLuo89U -DNErXxc+4z6/wCs+CZv+iKZ+tJIX/COUgb1up8WMwusRRdv4QcmWdupwX3kSa+Sj -B1oF7ydJzyGfikwJcGapJsErEU4z0g781mzSDjJkaP+tBXhfAx2o45CsJOAPQKdL -rosot4LKGAfmt1t06SAZf7IbiVQ= ------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----- -MIIEGDCCAwCgAwIBAgIBATANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQGEwJTRTEU -MBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3 -b3JrMSEwHwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3QwHhcNMDAwNTMw -MTAzODMxWhcNMjAwNTMwMTAzODMxWjBlMQswCQYDVQQGEwJTRTEUMBIGA1UEChML -QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSEwHwYD -VQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUA -A4IBDwAwggEKAoIBAQCWltQhSWDia+hBBwzexODcEyPNwTXH+9ZOEQpnXvUGW2ul -CDtbKRY654eyNAbFvAWlA3yCyykQruGIgb3WntP+LVbBFc7jJp0VLhD7Bo8wBN6n -tGO0/7Gcrjyvd7ZWxbWroulpOj0OM3kyP3CCkplhbY0wCI9xP6ZIVxn4JdxLZlyl -dI+Yrsj5wAYi56xz36Uu+1LcsRVlIPo1Zmne3yzxbrww2ywkEtvrNTVokMsAsJch -PXQhI2U0K7t4WaPW4XY5mqRJjox0r26kmqPZm9I4XJuiGMx1I4S+6+JNM3GOGvDC -+Mcdoq0Dlyz4zyXG9rgkMbFjXZJ/Y/AlyVMuH79NAgMBAAGjgdIwgc8wHQYDVR0O -BBYEFJWxtPCUtr3H2tERCSG+wa9J/RB7MAsGA1UdDwQEAwIBBjAPBgNVHRMBAf8E -BTADAQH/MIGPBgNVHSMEgYcwgYSAFJWxtPCUtr3H2tERCSG+wa9J/RB7oWmkZzBl -MQswCQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFk -ZFRydXN0IFRUUCBOZXR3b3JrMSEwHwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENB -IFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBACxtZBsfzQ3duQH6lmM0MkhHma6X -7f1yFqZzR1r0693p9db7RcwpiURdv0Y5PejuvE1Uhh4dbOMXJ0PhiVYrqW9yTkkz -43J8KiOavD7/KCrto/8cI7pDVwlnTUtiBi34/2ydYB7YHEt9tTEv2dB8Xfjea4MY -eDdXL+gzB2ffHsdrKpV2ro9Xo/D0UrSpUwjP4E/TelOL/bscVjby/rK25Xa71SJl -pz/+0WatC7xrmYbvP33zGDLKe8bjq2RGlfgmadlVg3sslgf/WSxEo8bl6ancoWOA -WiFeIc9TVPC6b4nbqKqVz4vjccweGyBECMB6tkD9xOQ14R0WHNC8K47Wcdk= ------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----- -MIIEFTCCAv2gAwIBAgIBATANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQGEwJTRTEU -MBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3 -b3JrMSAwHgYDVQQDExdBZGRUcnVzdCBQdWJsaWMgQ0EgUm9vdDAeFw0wMDA1MzAx -MDQxNTBaFw0yMDA1MzAxMDQxNTBaMGQxCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtB -ZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQIE5ldHdvcmsxIDAeBgNV -BAMTF0FkZFRydXN0IFB1YmxpYyBDQSBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOC -AQ8AMIIBCgKCAQEA6Rowj4OIFMEg2Dybjxt+A3S72mnTRqX4jsIMEZBRpS9mVEBV -6tsfSlbunyNu9DnLoblv8n75XYcmYZ4c+OLspoH4IcUkzBEMP9smcnrHAZcHF/nX -GCwwfQ56HmIexkvA/X1id9NEHif2P0tEs7c42TkfYNVRknMDtABp4/MUTu7R3AnP -dzRGULD4EfL+OHn3Bzn+UZKXC1sIXzSGAa2Il+tmzV7R/9x98oTaunet3IAIx6eH -1lWfl2royBFkuucZKT8Rs3iQhCBSWxHveNCD9tVIkNAwHM+A+WD+eeSI8t0A65RF -62WUaUC6wNW0uLp9BBGo6zEFlpROWCGOn9Bg/QIDAQABo4HRMIHOMB0GA1UdDgQW -BBSBPjfYkrAfd59ctKtzquf2NGAv+jALBgNVHQ8EBAMCAQYwDwYDVR0TAQH/BAUw -AwEB/zCBjgYDVR0jBIGGMIGDgBSBPjfYkrAfd59ctKtzquf2NGAv+qFopGYwZDEL -MAkGA1UEBhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQLExRBZGRU -cnVzdCBUVFAgTmV0d29yazEgMB4GA1UEAxMXQWRkVHJ1c3QgUHVibGljIENBIFJv -b3SCAQEwDQYJKoZIhvcNAQEFBQADggEBAAP3FUr4JNojVhaTdt02KLmuG7jD8WS6 -IBh4lSknVwW8fCr0uVFV2ocC3g8WFzH4qnkuCRO7r7IgGRLlk/lL+YPoRNWyQSW/ -iHVv/xD8SlTQX/D67zZzfRs2RcYhbbQVuE7PnFylPVoAjgbjPGsye/Kf8Lb93/Ao -GEjwxrzQvzSAlsJKsW2Ox5BF3i9nrEUEo3rcVZLJR2bYGozH7ZxOmuASu7VqTITh -4SINhwBk/ox9Yjllpu9CtoAlEmEBqCQTcAARJl/6NVDFSMwGR+gn2HCNX2TmoUQm -XiLsks3/QppEIW1cxeMiHV9HEufOX1362KqxMy3ZdvJOOjMMK7MtkAY= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEHjCCAwagAwIBAgIBATANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJTRTEU -MBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3 -b3JrMSMwIQYDVQQDExpBZGRUcnVzdCBRdWFsaWZpZWQgQ0EgUm9vdDAeFw0wMDA1 -MzAxMDQ0NTBaFw0yMDA1MzAxMDQ0NTBaMGcxCzAJBgNVBAYTAlNFMRQwEgYDVQQK -EwtBZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQIE5ldHdvcmsxIzAh -BgNVBAMTGkFkZFRydXN0IFF1YWxpZmllZCBDQSBSb290MIIBIjANBgkqhkiG9w0B -AQEFAAOCAQ8AMIIBCgKCAQEA5B6a/twJWoekn0e+EV+vhDTbYjx5eLfpMLXsDBwq -xBb/4Oxx64r1EW7tTw2R0hIYLUkVAcKkIhPHEWT/IhKauY5cLwjPcWqzZwFZ8V1G -87B4pfYOQnrjfxvM0PC3KP0q6p6zsLkEqv32x7SxuCqg+1jxGaBvcCV+PmlKfw8i -2O+tCBGaKZnhqkRFmhJePp1tUvznoD1oL/BLcHwTOK28FSXx1s6rosAx1i+f4P8U -WfyEk9mHfExUE+uf0S0R+Bg6Ot4l2ffTQO2kBhLEO+GRwVY18BTcZTYJbqukB8c1 -0cIDMzZbdSZtQvESa0NvS3GU+jQd7RNuyoB/mC9suWXY6QIDAQABo4HUMIHRMB0G -A1UdDgQWBBQ5lYtii1zJ1IC6WA+XPxUIQ8yYpzALBgNVHQ8EBAMCAQYwDwYDVR0T -AQH/BAUwAwEB/zCBkQYDVR0jBIGJMIGGgBQ5lYtii1zJ1IC6WA+XPxUIQ8yYp6Fr -pGkwZzELMAkGA1UEBhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQL -ExRBZGRUcnVzdCBUVFAgTmV0d29yazEjMCEGA1UEAxMaQWRkVHJ1c3QgUXVhbGlm -aWVkIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBABmrder4i2VhlRO6aQTv -hsoToMeqT2QbPxj2qC0sVY8FtzDqQmodwCVRLae/DLPt7wh/bDxGGuoYQ992zPlm -hpwsaPXpF/gxsxjE1kh9I0xowX67ARRvxdlu3rsEQmr49lx95dr6h+sNNVJn0J6X -dgWTP5XHAeZpVTh/EGGZyeNfpso+gmNIquIISD6q8rKFYqa0p9m9N5xotS1WfbC3 -P6CxB9bpT9zeRXEwMn8bLgn5v1Kh7sKAPgZcLlVAwRv1cEWw3F369nJad9Jjzc9Y -iQBCYz95OdBEsIJuQRno3eDBiFrRHnGTHyQwdOUeqN48Jzd/g66ed8/wMLH/S5no -xqE= ------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----- -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----- -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----- -MIIFkjCCA3qgAwIBAgIIAeDltYNno+AwDQYJKoZIhvcNAQEMBQAwZzEbMBkGA1UE -AwwSQXBwbGUgUm9vdCBDQSAtIEcyMSYwJAYDVQQLDB1BcHBsZSBDZXJ0aWZpY2F0 -aW9uIEF1dGhvcml0eTETMBEGA1UECgwKQXBwbGUgSW5jLjELMAkGA1UEBhMCVVMw -HhcNMTQwNDMwMTgxMDA5WhcNMzkwNDMwMTgxMDA5WjBnMRswGQYDVQQDDBJBcHBs -ZSBSb290IENBIC0gRzIxJjAkBgNVBAsMHUFwcGxlIENlcnRpZmljYXRpb24gQXV0 -aG9yaXR5MRMwEQYDVQQKDApBcHBsZSBJbmMuMQswCQYDVQQGEwJVUzCCAiIwDQYJ -KoZIhvcNAQEBBQADggIPADCCAgoCggIBANgREkhI2imKScUcx+xuM23+TfvgHN6s -XuI2pyT5f1BrTM65MFQn5bPW7SXmMLYFN14UIhHF6Kob0vuy0gmVOKTvKkmMXT5x -ZgM4+xb1hYjkWpIMBDLyyED7Ul+f9sDx47pFoFDVEovy3d6RhiPw9bZyLgHaC/Yu -OQhfGaFjQQscp5TBhsRTL3b2CtcM0YM/GlMZ81fVJ3/8E7j4ko380yhDPLVoACVd -J2LT3VXdRCCQgzWTxb+4Gftr49wIQuavbfqeQMpOhYV4SbHXw8EwOTKrfl+q04tv -ny0aIWhwZ7Oj8ZhBbZF8+NfbqOdfIRqMM78xdLe40fTgIvS/cjTf94FNcX1RoeKz -8NMoFnNvzcytN31O661A4T+B/fc9Cj6i8b0xlilZ3MIZgIxbdMYs0xBTJh0UT8TU -gWY8h2czJxQI6bR3hDRSj4n4aJgXv8O7qhOTH11UL6jHfPsNFL4VPSQ08prcdUFm -IrQB1guvkJ4M6mL4m1k8COKWNORj3rw31OsMiANDC1CvoDTdUE0V+1ok2Az6DGOe -HwOx4e7hqkP0ZmUoNwIx7wHHHtHMn23KVDpA287PT0aLSmWaasZobNfMmRtHsHLD -d4/E92GcdB/O/WuhwpyUgquUoue9G7q5cDmVF8Up8zlYNPXEpMZ7YLlmQ1A/bmH8 -DvmGqmAMQ0uVAgMBAAGjQjBAMB0GA1UdDgQWBBTEmRNsGAPCe8CjoA1/coB6HHcm -jTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQwF -AAOCAgEAUabz4vS4PZO/Lc4Pu1vhVRROTtHlznldgX/+tvCHM/jvlOV+3Gp5pxy+ -8JS3ptEwnMgNCnWefZKVfhidfsJxaXwU6s+DDuQUQp50DhDNqxq6EWGBeNjxtUVA -eKuowM77fWM3aPbn+6/Gw0vsHzYmE1SGlHKy6gLti23kDKaQwFd1z4xCfVzmMX3z -ybKSaUYOiPjjLUKyOKimGY3xn83uamW8GrAlvacp/fQ+onVJv57byfenHmOZ4VxG -/5IFjPoeIPmGlFYl5bRXOJ3riGQUIUkhOb9iZqmxospvPyFgxYnURTbImHy99v6Z -SYA7LNKmp4gDBDEZt7Y6YUX6yfIjyGNzv1aJMbDZfGKnexWoiIqrOEDCzBL/FePw -N983csvMmOa/orz6JopxVtfnJBtIRD6e/J/JzBrsQzwBvDR4yGn1xuZW7AYJNpDr -FEobXsmII9oDMJELuDY++ee1KG++P+w8j2Ud5cAeh6Squpj9kuNsJnfdBrRkBof0 -Tta6SqoWqPQFZ2aWuuJVecMsXUmPgEkrihLHdoBR37q9ZV0+N0djMenl9MU/S60E -inpxLK8JQzcPqOMyT/RFtm2XNuyE9QoB6he7hY1Ck3DDUOUUi78/w0EP3SIEIwiK -um1xRKtzCTrJ+VKACd+66eYWyi4uTLLT3OUEVLLUNIAytbwPF+E= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIICQzCCAcmgAwIBAgIILcX8iNLFS5UwCgYIKoZIzj0EAwMwZzEbMBkGA1UEAwwS -QXBwbGUgUm9vdCBDQSAtIEczMSYwJAYDVQQLDB1BcHBsZSBDZXJ0aWZpY2F0aW9u -IEF1dGhvcml0eTETMBEGA1UECgwKQXBwbGUgSW5jLjELMAkGA1UEBhMCVVMwHhcN -MTQwNDMwMTgxOTA2WhcNMzkwNDMwMTgxOTA2WjBnMRswGQYDVQQDDBJBcHBsZSBS -b290IENBIC0gRzMxJjAkBgNVBAsMHUFwcGxlIENlcnRpZmljYXRpb24gQXV0aG9y -aXR5MRMwEQYDVQQKDApBcHBsZSBJbmMuMQswCQYDVQQGEwJVUzB2MBAGByqGSM49 -AgEGBSuBBAAiA2IABJjpLz1AcqTtkyJygRMc3RCV8cWjTnHcFBbZDuWmBSp3ZHtf -TjjTuxxEtX/1H7YyYl3J6YRbTzBPEVoA/VhYDKX1DyxNB0cTddqXl5dvMVztK517 -IDvYuVTZXpmkOlEKMaNCMEAwHQYDVR0OBBYEFLuw3qFYM4iapIqZ3r6966/ayySr -MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49BAMDA2gA -MGUCMQCD6cHEFl4aXTQY2e3v9GwOAEZLuN+yRhHFD/3meoyhpmvOwgPUnPWTxnS4 -at+qIxUCMG1mihDK1A3UT82NQz60imOlM27jbdoXt2QfyFMm+YhidDkLF1vLUagM -6BgD56KyKA== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEBDCCAuygAwIBAgIIGHqpqMKWIQwwDQYJKoZIhvcNAQELBQAwYjELMAkGA1UE -BhMCVVMxEzARBgNVBAoTCkFwcGxlIEluYy4xJjAkBgNVBAsTHUFwcGxlIENlcnRp -ZmljYXRpb24gQXV0aG9yaXR5MRYwFAYDVQQDEw1BcHBsZSBSb290IENBMB4XDTEy -MDIwMTIyMTIxNVoXDTI3MDIwMTIyMTIxNVoweTEtMCsGA1UEAwwkRGV2ZWxvcGVy -IElEIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MSYwJAYDVQQLDB1BcHBsZSBDZXJ0 -aWZpY2F0aW9uIEF1dGhvcml0eTETMBEGA1UECgwKQXBwbGUgSW5jLjELMAkGA1UE -BhMCVVMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCJdk8GW5pB7qUj -KwKjX9dzP8A1sIuECj8GJH+nlT/rTw6Tr7QO0Mg+5W0Ysx/oiUe/1wkI5P9WmCkV -55SduTWjCs20wOHiYPTK7Cl4RWlpYGtfipL8niPmOsIiszFPHLrytjRZQu6wqQID -GJEEtrN4LjMfgEUNRW+7Dlpbfzrn2AjXCw4ybfuGNuRsq8QRinCEJqqfRNHxuMZ7 -lBebSPcLWBa6I8WfFTl+yl3DMl8P4FJ/QOq+rAhklVvJGpzlgMofakQcbD7EsCYf -Hex7r16gaj1HqVgSMT8gdihtHRywwk4RaSaLy9bQEYLJTg/xVnTQ2QhLZniiq6yn -4tJMh1nJAgMBAAGjgaYwgaMwHQYDVR0OBBYEFFcX7aLP3HyYoRDg/L6HLSzy4xdU -MA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUK9BpR5R2Cf70a40uQKb3R01/ -CF4wLgYDVR0fBCcwJTAjoCGgH4YdaHR0cDovL2NybC5hcHBsZS5jb20vcm9vdC5j -cmwwDgYDVR0PAQH/BAQDAgGGMBAGCiqGSIb3Y2QGAgYEAgUAMA0GCSqGSIb3DQEB -CwUAA4IBAQBCOXRrodzGpI83KoyzHQpEvJUsf7xZuKxh+weQkjK51L87wVA5akR0 -ouxbH3Dlqt1LbBwjcS1f0cWTvu6binBlgp0W4xoQF4ktqM39DHhYSQwofzPuAHob -tHastrW7T9+oG53IGZdKC1ZnL8I+trPEgzrwd210xC4jUe6apQNvYPSlSKcGwrta -4h8fRkV+5Jf1JxC3ICJyb3LaxlB1xT0lj12jAOmfNoxIOY+zO+qQgC6VmmD0eM70 -DgpTPqL6T9geroSVjTK8Vk2J6XgY4KyaQrp6RhuEoonOFOiI0ViL9q5WxCwFKkWv -C9lLqQIPNKyIx2FViUTJJ3MH7oLlTvVw ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEuzCCA6OgAwIBAgIBAjANBgkqhkiG9w0BAQUFADBiMQswCQYDVQQGEwJVUzET -MBEGA1UEChMKQXBwbGUgSW5jLjEmMCQGA1UECxMdQXBwbGUgQ2VydGlmaWNhdGlv -biBBdXRob3JpdHkxFjAUBgNVBAMTDUFwcGxlIFJvb3QgQ0EwHhcNMDYwNDI1MjE0 -MDM2WhcNMzUwMjA5MjE0MDM2WjBiMQswCQYDVQQGEwJVUzETMBEGA1UEChMKQXBw -bGUgSW5jLjEmMCQGA1UECxMdQXBwbGUgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkx -FjAUBgNVBAMTDUFwcGxlIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw -ggEKAoIBAQDkkakJH5HbHkdQ6wXtXnmELes2oldMVeyLGYne+Uts9QerIjAC6Bg+ -+FAJ039BqJj50cpmnCRrEdCju+QbKsMflZ56DKRHi1vUFjczy8QPTc4UadHJGXL1 -XQ7Vf1+b8iUDulWPTV0N8WQ1IxVLFVkds5T39pyez1C6wVhQZ48ItCD3y6wsIG9w -tj8BMIy3Q88PnT3zK0koGsj+zrW5DtleHNbLPbU6rfQPDgCSC7EhFi501TwN22IW -q6NxkkdTVcGvL0Gz+PvjcM3mo0xFfh9Ma1CWQYnEdGILEINBhzOKgbEwWOxaBDKM -aLOPHd5lc/9nXmW8Sdh2nzMUZaF3lMktAgMBAAGjggF6MIIBdjAOBgNVHQ8BAf8E -BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUK9BpR5R2Cf70a40uQKb3 -R01/CF4wHwYDVR0jBBgwFoAUK9BpR5R2Cf70a40uQKb3R01/CF4wggERBgNVHSAE -ggEIMIIBBDCCAQAGCSqGSIb3Y2QFATCB8jAqBggrBgEFBQcCARYeaHR0cHM6Ly93 -d3cuYXBwbGUuY29tL2FwcGxlY2EvMIHDBggrBgEFBQcCAjCBthqBs1JlbGlhbmNl -IG9uIHRoaXMgY2VydGlmaWNhdGUgYnkgYW55IHBhcnR5IGFzc3VtZXMgYWNjZXB0 -YW5jZSBvZiB0aGUgdGhlbiBhcHBsaWNhYmxlIHN0YW5kYXJkIHRlcm1zIGFuZCBj -b25kaXRpb25zIG9mIHVzZSwgY2VydGlmaWNhdGUgcG9saWN5IGFuZCBjZXJ0aWZp -Y2F0aW9uIHByYWN0aWNlIHN0YXRlbWVudHMuMA0GCSqGSIb3DQEBBQUAA4IBAQBc -NplMLXi37Yyb3PN3m/J20ncwT8EfhYOFG5k9RzfyqZtAjizUsZAS2L70c5vu0mQP -y3lPNNiiPvl4/2vIB+x9OYOLUyDTOMSxv5pPCmv/K/xZpwUJfBdAVhEedNO3iyM7 -R6PVbyTi69G3cN8PReEnyvFteO3ntRcXqNx+IjXKJdXZD9Zr1KIkIxH3oayPc4Fg -xhtbCS+SsvhESPBgOJ4V9T0mZyCKM2r3DYLP3uujL/lTaltkwGMzd/c6ByxW69oP -IQ7aunMZT7XZNn/Bh1XZp5m5MkL72NVxnn6hUrcbvZNCJBIqxw8dtk2cXmPIS4AX -UKqK1drk/NAJBzewdXUh ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIFujCCBKKgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBhjELMAkGA1UEBhMCVVMx -HTAbBgNVBAoTFEFwcGxlIENvbXB1dGVyLCBJbmMuMS0wKwYDVQQLEyRBcHBsZSBD -b21wdXRlciBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxKTAnBgNVBAMTIEFwcGxlIFJv -b3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4XDTA1MDIxMDAwMTgxNFoXDTI1MDIx -MDAwMTgxNFowgYYxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRBcHBsZSBDb21wdXRl -ciwgSW5jLjEtMCsGA1UECxMkQXBwbGUgQ29tcHV0ZXIgQ2VydGlmaWNhdGUgQXV0 -aG9yaXR5MSkwJwYDVQQDEyBBcHBsZSBSb290IENlcnRpZmljYXRlIEF1dGhvcml0 -eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOSRqQkfkdseR1DrBe1e -eYQt6zaiV0xV7IsZid75S2z1B6siMALoGD74UAnTf0GomPnRymacJGsR0KO75Bsq -wx+VnnoMpEeLW9QWNzPLxA9NzhRp0ckZcvVdDtV/X5vyJQO6VY9NXQ3xZDUjFUsV -WR2zlPf2nJ7PULrBWFBnjwi0IPfLrCwgb3C2PwEwjLdDzw+dPfMrSSgayP7OtbkO -2V4c1ss9tTqt9A8OAJILsSEWLnTVPA3bYharo3GSR1NVwa8vQbP4++NwzeajTEV+ -H0xrUJZBicR0YgsQg0GHM4qBsTBY7FoEMoxos48d3mVz/2deZbxJ2HafMxRloXeU -yS0CAwEAAaOCAi8wggIrMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/ -MB0GA1UdDgQWBBQr0GlHlHYJ/vRrjS5ApvdHTX8IXjAfBgNVHSMEGDAWgBQr0GlH -lHYJ/vRrjS5ApvdHTX8IXjCCASkGA1UdIASCASAwggEcMIIBGAYJKoZIhvdjZAUB -MIIBCTBBBggrBgEFBQcCARY1aHR0cHM6Ly93d3cuYXBwbGUuY29tL2NlcnRpZmlj -YXRlYXV0aG9yaXR5L3Rlcm1zLmh0bWwwgcMGCCsGAQUFBwICMIG2GoGzUmVsaWFu -Y2Ugb24gdGhpcyBjZXJ0aWZpY2F0ZSBieSBhbnkgcGFydHkgYXNzdW1lcyBhY2Nl -cHRhbmNlIG9mIHRoZSB0aGVuIGFwcGxpY2FibGUgc3RhbmRhcmQgdGVybXMgYW5k -IGNvbmRpdGlvbnMgb2YgdXNlLCBjZXJ0aWZpY2F0ZSBwb2xpY3kgYW5kIGNlcnRp -ZmljYXRpb24gcHJhY3RpY2Ugc3RhdGVtZW50cy4wRAYDVR0fBD0wOzA5oDegNYYz -aHR0cHM6Ly93d3cuYXBwbGUuY29tL2NlcnRpZmljYXRlYXV0aG9yaXR5L3Jvb3Qu -Y3JsMFUGCCsGAQUFBwEBBEkwRzBFBggrBgEFBQcwAoY5aHR0cHM6Ly93d3cuYXBw -bGUuY29tL2NlcnRpZmljYXRlYXV0aG9yaXR5L2Nhc2lnbmVycy5odG1sMA0GCSqG -SIb3DQEBBQUAA4IBAQCd2i0oWC99dgS5BNM+zrdmY06PL9T+S61yvaM5xlJNBZhS -9YlRASR5vhoy9+VEi0tEBzmC1lrKtCBe2a4VXR2MHTK/ODFiSF3H4ZCx+CRA+F9Y -m1FdV53B5f88zHIhbsTp6aF31ywXJsM/65roCwO66bNKcuszCVut5mIxauivL9Wv -Hld2j383LS4CXN1jyfJxuCZA3xWNdUQ/eb3mHZnhQyw+rW++uaT+DjUZUWOxw961 -kj5ReAFziqQjyqSI8R5cH0EWLX6VCqrpiUGYGxrdyyC/R14MJsVVNU3GMIuZZxTH -CR+6R8faAQmHJEKVvRNgGQrv6n8Obs3BREM6StXj ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIJmzCCB4OgAwIBAgIBATANBgkqhkiG9w0BAQwFADCCAR4xPjA8BgNVBAMTNUF1 -dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIFJhaXogZGVsIEVzdGFkbyBWZW5lem9s -YW5vMQswCQYDVQQGEwJWRTEQMA4GA1UEBxMHQ2FyYWNhczEZMBcGA1UECBMQRGlz -dHJpdG8gQ2FwaXRhbDE2MDQGA1UEChMtU2lzdGVtYSBOYWNpb25hbCBkZSBDZXJ0 -aWZpY2FjaW9uIEVsZWN0cm9uaWNhMUMwQQYDVQQLEzpTdXBlcmludGVuZGVuY2lh -IGRlIFNlcnZpY2lvcyBkZSBDZXJ0aWZpY2FjaW9uIEVsZWN0cm9uaWNhMSUwIwYJ -KoZIhvcNAQkBFhZhY3JhaXpAc3VzY2VydGUuZ29iLnZlMB4XDTEwMTIyMjE4MDgy -MVoXDTMwMTIxNzIzNTk1OVowggEeMT4wPAYDVQQDEzVBdXRvcmlkYWQgZGUgQ2Vy -dGlmaWNhY2lvbiBSYWl6IGRlbCBFc3RhZG8gVmVuZXpvbGFubzELMAkGA1UEBhMC -VkUxEDAOBgNVBAcTB0NhcmFjYXMxGTAXBgNVBAgTEERpc3RyaXRvIENhcGl0YWwx -NjA0BgNVBAoTLVNpc3RlbWEgTmFjaW9uYWwgZGUgQ2VydGlmaWNhY2lvbiBFbGVj -dHJvbmljYTFDMEEGA1UECxM6U3VwZXJpbnRlbmRlbmNpYSBkZSBTZXJ2aWNpb3Mg -ZGUgQ2VydGlmaWNhY2lvbiBFbGVjdHJvbmljYTElMCMGCSqGSIb3DQEJARYWYWNy -YWl6QHN1c2NlcnRlLmdvYi52ZTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC -ggIBAME77xNS8ZlW47RsBeEaaRZhJoZ4rw785UAFCuPZOAVMqNS1wMYqzy95q6Gk -UO81ER/ugiQX/KMcq/4HBn83fwdYWxPZfwBfK7BP2p/JsFgzYeFP0BXOLmvoJIzl -Jb6FW+1MPwGBjuaZGFImWZsSmGUclb51mRYMZETh9/J5CLThR1exStxHQptwSzra -zNFpkQY/zmj7+YZNA9yDoroVFv6sybYOZ7OxNDo7zkSLo45I7gMwtxqWZ8VkJZkC -8+p0dX6mkhUT0QAV64Zc9HsZiH/oLhEkXjhrgZ28cF73MXIqLx1fyM4kPH1yOJi/ -R72nMwL7D+Sd6mZgI035TxuHXc2/uOwXfKrrTjaJDz8Jp6DdessOkxIgkKXRjP+F -K3ze3n4NUIRGhGRtyvEjK95/2g02t6PeYiYVGur6ruS49n0RAaSS0/LJb6XzaAAe -0mmO2evnEqxIKwy2mZRNPfAVW1l3wCnWiUwryBU6OsbFcFFrQm+00wOicXvOTHBM -aiCVAVZTb9RSLyi+LJ1llzJZO3pq3IRiiBj38Nooo+2ZNbMEciSgmig7YXaUcmud -SVQvLSL+Yw+SqawyezwZuASbp7d/0rutQ59d81zlbMt3J7yB567rT2IqIydQ8qBW -k+fmXzghX+/FidYsh/aK+zZ7Wy68kKHuzEw1Vqkat5DGs+VzAgMBAAGjggLeMIIC -2jASBgNVHRMBAf8ECDAGAQH/AgECMDcGA1UdEgQwMC6CD3N1c2NlcnRlLmdvYi52 -ZaAbBgVghl4CAqASDBBSSUYtRy0yMDAwNDAzNi0wMB0GA1UdDgQWBBStuyIdxuDS -Aaj9dlBSk+2YwU2u0zCCAVAGA1UdIwSCAUcwggFDgBStuyIdxuDSAaj9dlBSk+2Y -wU2u06GCASakggEiMIIBHjE+MDwGA1UEAxM1QXV0b3JpZGFkIGRlIENlcnRpZmlj -YWNpb24gUmFpeiBkZWwgRXN0YWRvIFZlbmV6b2xhbm8xCzAJBgNVBAYTAlZFMRAw -DgYDVQQHEwdDYXJhY2FzMRkwFwYDVQQIExBEaXN0cml0byBDYXBpdGFsMTYwNAYD -VQQKEy1TaXN0ZW1hIE5hY2lvbmFsIGRlIENlcnRpZmljYWNpb24gRWxlY3Ryb25p -Y2ExQzBBBgNVBAsTOlN1cGVyaW50ZW5kZW5jaWEgZGUgU2VydmljaW9zIGRlIENl -cnRpZmljYWNpb24gRWxlY3Ryb25pY2ExJTAjBgkqhkiG9w0BCQEWFmFjcmFpekBz -dXNjZXJ0ZS5nb2IudmWCAQEwDgYDVR0PAQH/BAQDAgEGMDcGA1UdEQQwMC6CD3N1 -c2NlcnRlLmdvYi52ZaAbBgVghl4CAqASDBBSSUYtRy0yMDAwNDAzNi0wMFQGA1Ud -HwRNMEswJKAioCCGHmhodHA6Ly93d3cuc3VzY2VydGUuZ29iLnZlL2xjcjAjoCGg -H4YdbGRhcDovL2FjcmFpei5zdXNjZXJ0ZS5nb2IudmUwNwYIKwYBBQUHAQEEKzAp -MCcGCCsGAQUFBzABhhtoaHRwOi8vb2NzcC5zdXNjZXJ0ZS5nb2IudmUwQAYDVR0g -BDkwNzA1BgVghl4BAjAsMCoGCCsGAQUFBwIBFh5odHRwOi8vd3d3LnN1c2NlcnRl -LmdvYi52ZS9kcGMwDQYJKoZIhvcNAQEMBQADggIBAK4qy/zmZ9zBwfW3yOYtLcBT -Oy4szJyPz7/RhNH3bPVH7HbDTGpi6JZ4YXdXMBeJE5qBF4a590Kgj8Rlnltt+Rbo -OFQOU1UDqKuTdBsA//Zry5899fmn8jBUkg4nh09jhHHbLlaUScdz704Zz2+UVg7i -s/r3Legxap60KzmdrmTAE9VKte1TQRgavQwVX5/2mO/J+SCas//UngI+h8SyOucq -mjudYEgBrZaodUsagUfn/+AzFNrGLy+al+5nZeHb8JnCfLHWS0M9ZyhgoeO/czyn -99+5G93VWNv4zfc4KiavHZKrkn8F9pg0ycIZh+OwPT/RE2zq4gTazBMlP3ACIe/p -olkNaOEa8KvgzW96sjBZpMW49zFmyINYkcj+uaNCJrVGsXgdBmkuRGJNWFZ9r0cG -woIaxViFBypsz045r1ESfYPlfDOavBhZ/giR/Xocm9CHkPRY2BApMMR0DUCyGETg -Ql+L3kfdTKzuDjUp2DM9FqysQmaM81YDZufWkMhlZPfHwC7KbNougoLroa5Umeos -bqAXWmk46SwIdWRPLLqbUpDTKooynZKpSYIkkotdgJoVZUUCY+RCO8jsVPEU6ece -SxztNUm5UOta1OJPMwSAKRHOo3ilVb9c6lAixDdvV8MeNbqe6asM1mpCHWbJ/0rg -5Ls9Cxx8hracyp0ev7b0 ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIFVTCCBD2gAwIBAgIEO/OB0DANBgkqhkiG9w0BAQUFADBsMQswCQYDVQQGEwJj -aDEOMAwGA1UEChMFYWRtaW4xETAPBgNVBAsTCFNlcnZpY2VzMSIwIAYDVQQLExlD -ZXJ0aWZpY2F0aW9uIEF1dGhvcml0aWVzMRYwFAYDVQQDEw1BZG1pbi1Sb290LUNB -MB4XDTAxMTExNTA4NTEwN1oXDTIxMTExMDA3NTEwN1owbDELMAkGA1UEBhMCY2gx -DjAMBgNVBAoTBWFkbWluMREwDwYDVQQLEwhTZXJ2aWNlczEiMCAGA1UECxMZQ2Vy -dGlmaWNhdGlvbiBBdXRob3JpdGllczEWMBQGA1UEAxMNQWRtaW4tUm9vdC1DQTCC -ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMvgr0QUIv5qF0nyXZ3PXAJi -C4C5Wr+oVTN7oxIkXkxvO0GJToM9n7OVJjSmzBL0zJ2HXj0MDRcvhSY+KiZZc6Go -vDvr5Ua481l7ILFeQAFtumeza+vvxeL5Nd0Maga2miiacLNAKXbAcUYRa0Ov5VZB -++YcOYNNt/aisWbJqA2y8He+NsEgJzK5zNdayvYXQTZN+7tVgWOck16Da3+4FXdy -fH1NCWtZlebtMKtERtkVAaVbiWW24CjZKAiVfggjsiLo3yVMPGj3budLx5D9hEEm -vlyDOtcjebca+AcZglppWMX/iHIrx7740y0zd6cWEqiLIcZCrnpkr/KzwO135GkC -AwEAAaOCAf0wggH5MA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIASBkTCBjjCBiwYI -YIV0AREDAQAwfzArBggrBgEFBQcCAjAfGh1UaGlzIGlzIHRoZSBBZG1pbi1Sb290 -LUNBIENQUzBQBggrBgEFBQcCARZEaHR0cDovL3d3dy5pbmZvcm1hdGlrLmFkbWlu -LmNoL1BLSS9saW5rcy9DUFNfMl8xNl83NTZfMV8xN18zXzFfMC5wZGYwfwYDVR0f -BHgwdjB0oHKgcKRuMGwxFjAUBgNVBAMTDUFkbWluLVJvb3QtQ0ExIjAgBgNVBAsT -GUNlcnRpZmljYXRpb24gQXV0aG9yaXRpZXMxETAPBgNVBAsTCFNlcnZpY2VzMQ4w -DAYDVQQKEwVhZG1pbjELMAkGA1UEBhMCY2gwHQYDVR0OBBYEFIKf+iNzIPGXi7JM -Tb5CxX9mzWToMIGZBgNVHSMEgZEwgY6AFIKf+iNzIPGXi7JMTb5CxX9mzWTooXCk -bjBsMQswCQYDVQQGEwJjaDEOMAwGA1UEChMFYWRtaW4xETAPBgNVBAsTCFNlcnZp -Y2VzMSIwIAYDVQQLExlDZXJ0aWZpY2F0aW9uIEF1dGhvcml0aWVzMRYwFAYDVQQD -Ew1BZG1pbi1Sb290LUNBggQ784HQMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0B -AQUFAAOCAQEAeE96XCYRpy6umkPKXDWCRn7INo96ZrWpMggcDORuofHIwdTkgOeM -vWOxDN/yuT7CC3FAaUajbPRbDw0hRMcqKz0aC8CgwcyIyhw/rFK29mfNTG3EviP9 -QSsEbnelFnjpm1wjz4EaBiFjatwpUbI6+Zv3XbEt9QQXBn+c6DeFLe4xvC4B+MTr -a440xTk59pSYux8OHhEvqIwHCkiijGqZhTS3KmGFeBopaR+dJVBRBMoXwzk4B3Hn -0Zib1dEYFZa84vPJZyvxCbLOnPRDJgH6V2uQqbG+6DXVaf/wORVOvF/wzzv0viM/ -RWbEtJZdvo8N3sdtCULzifnxP/V0T9+4ZQ== ------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----- -MIIEGjCCAwICEQCLW3VWhFSFCwDPrzhIzrGkMA0GCSqGSIb3DQEBBQUAMIHKMQsw -CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZl -cmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWdu -LCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlT -aWduIENsYXNzIDEgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3Jp -dHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQswCQYD -VQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlT -aWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJ -bmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWdu -IENsYXNzIDEgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg -LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAN2E1Lm0+afY8wR4 -nN493GwTFtl63SRRZsDHJlkNrAYIwpTRMx/wgzUfbhvI3qpuFU5UJ+/EbRrsC+MO -8ESlV8dAWB6jRx9x7GD2bZTIGDnt/kIYVt/kTEkQeE4BdjVjEjbdZrwBBDajVWjV -ojYJrKshJlQGrT/KFOCsyq0GHZXi+J3x4GD/wn91K0zM2v6HmSHquv4+VNfSWXjb -PG7PoBMAGrgnoeS+Z5bKoMWznN3JdZ7rMJpfo83ZrngZPyPpXNspva1VyBtUjGP2 -6KbqxzcSXKMpHgLZ2x87tNcPVkeBFQRKr4Mn0cVYiMHd9qqnoxjaaKptEVHhv2Vr -n5Z20T0CAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAq2aN17O6x5q25lXQBfGfMY1a -qtmqRiYPce2lrVNWYgFHKkTp/j90CxObufRNG7LRX7K20ohcs5/Ny9Sn2WCVhDr4 -wTcdYcrnsMXlkdpUpqwxga6X3s0IrLjAl4B/bnKk52kTlWUfxJM8/XmPBNQ+T+r3 -ns7NZ3xPZQL/kYVUc8f/NveGLezQXk//EZ9yBta4GvFMDSZl4kSAHsef493oCtrs -pSCAaWihT37ha88HQfqDjrw43bAuEbFrskLMmrz5SCJ5ShkPshw+IHTZasO+8ih4 -E1Z5T21Q6huwtVexN2ZYI/PcD98Kh8TvhgXVOBRgmaNL3gaWcSzy27YfpO8/7g== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEGTCCAwECEGFwy0mMX5hFKeewptlQW3owDQYJKoZIhvcNAQEFBQAwgcoxCzAJ -BgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjEfMB0GA1UECxMWVmVy -aVNpZ24gVHJ1c3QgTmV0d29yazE6MDgGA1UECxMxKGMpIDE5OTkgVmVyaVNpZ24s -IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTFFMEMGA1UEAxM8VmVyaVNp -Z24gQ2xhc3MgMiBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0 -eSAtIEczMB4XDTk5MTAwMTAwMDAwMFoXDTM2MDcxNjIzNTk1OVowgcoxCzAJBgNV -BAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjEfMB0GA1UECxMWVmVyaVNp -Z24gVHJ1c3QgTmV0d29yazE6MDgGA1UECxMxKGMpIDE5OTkgVmVyaVNpZ24sIElu -Yy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTFFMEMGA1UEAxM8VmVyaVNpZ24g -Q2xhc3MgMiBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAt -IEczMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArwoNwtUs22e5LeWU -J92lvuCwTY+zYVY81nzD9M0+hsuiiOLh2KRpxbXiv8GmR1BeRjmL1Za6tW8UvxDO -JxOeBUebMXoT2B/Z0wI3i60sR/COgQanDTAM6/c8DyAd3HJG7qUCyFvDyVZpTMUY -wZF7C9UTAJu878NIPkZgIIUq1ZC2zYugzDLdt/1AVbJQHFauzI13TccgTacxdu9o -koqQHgiBVrKtaaNS0MscxCM9H5n+TOgWY47GCI72MfbS+uV23bUckqNJzc0BzWjN -qWm6o+sdDZykIKbBoMXRRkwXbdKsZj+WjOCE1Db/IlnF+RFgqF8EffIa9iVCYQ/E -Srg+iQIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQA0JhU8wI1NQ0kdvekhktdmnLfe -xbjQ5F1fdiLAJvmEOjr5jLX77GDx6M4EsMjdpwOPMPOY36TmpDHf0xwLRtxyID+u -7gU8pDM/CzmscHhzS5kr3zDCVLCoO1Wh/hYozUK9dG6A2ydEp85EXdQbkJgNHkKU -sQAsBNB0owIFImNjzYO1+8FtYmtpdf1dcEG59b98377BMnMiIYtYgXsVkXq642RI -sH/7NiXaldDxJBQX3RiAa0YjOVT1jmIJBB2UkKab5iXiQkWquJCtvgiPqQtCGJTP -cjnhsUPgKM+351psE2tJs//jGHyJizNdrDPXp/naOlXJWBD5qu9ats9LS98q ------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----- -MIIEGjCCAwICEQDsoKeLbnVqAc/EfMwvlF7XMA0GCSqGSIb3DQEBBQUAMIHKMQsw -CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZl -cmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWdu -LCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlT -aWduIENsYXNzIDQgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3Jp -dHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQswCQYD -VQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlT -aWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJ -bmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWdu -IENsYXNzIDQgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg -LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK3LpRFpxlmr8Y+1 -GQ9Wzsy1HyDkniYlS+BzZYlZ3tCD5PUPtbut8XzoIfzk6AzufEUiGXaStBO3IFsJ -+mGuqPKljYXCKtbeZjbSmwL0qJJgfJxptI8kHtCGUvYynEFYHiK9zUVilQhu0Gbd -U6LM8BDcVHOLBKFGMzNcF0C5nk3T875Vg+ixiY5afJqWIpA7iCXy0lOIAgwLePLm -NxdLMEYH5IBtptiWLugs+BGzOA1mppvqySNb247i8xOOGlktqgLw7KSHZtzBP/XY -ufTsgsbSPZUd5cBPhMnZo0QoBmrXRazwa2rvTl/4EYIeOGM0ZlDUPpNz+jDDZq3/ -ky2X7wMCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAj/ola09b5KROJ1WrIhVZPMq1 -CtRK26vdoV9TxaBXOcLORyu+OshWv8LZJxA6sQU8wHcxuzrTBXttmhwwjIDLk5Mq -g6sFUYICABFna/OIYUdfA5PVWw3g8dShMjWFsjrbsIKr0csKvE+MW8VLADsfKoKm -fjaF3H48ZwC15DtS4KjrXRX5xm3wrR0OhbepmnMUWluPQSjA1egtTaRezarZ7c7c -2NU8Qh0XwRJdRTjDOPP8hS6DRkiy1yBfkjaP53kPmF6Z6PDQpLv1U70qzlmwr25/ -bLvSHgCwIe34QWKCudiyxLtGUPMxxY8BqHTr9Xgn2uf3ZkPznoM+IKrDNWCRzg== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIFaTCCA1GgAwIBAgIJAMMDmu5QkG4oMA0GCSqGSIb3DQEBBQUAMFIxCzAJBgNV -BAYTAlNLMRMwEQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMu -MRkwFwYDVQQDExBDQSBEaXNpZyBSb290IFIxMB4XDTEyMDcxOTA5MDY1NloXDTQy -MDcxOTA5MDY1NlowUjELMAkGA1UEBhMCU0sxEzARBgNVBAcTCkJyYXRpc2xhdmEx -EzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERpc2lnIFJvb3QgUjEw -ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCqw3j33Jijp1pedxiy3QRk -D2P9m5YJgNXoqqXinCaUOuiZc4yd39ffg/N4T0Dhf9Kn0uXKE5Pn7cZ3Xza1lK/o -OI7bm+V8u8yN63Vz4STN5qctGS7Y1oprFOsIYgrY3LMATcMjfF9DCCMyEtztDK3A -fQ+lekLZWnDZv6fXARz2m6uOt0qGeKAeVjGu74IKgEH3G8muqzIm1Cxr7X1r5OJe -IgpFy4QxTaz+29FHuvlglzmxZcfe+5nkCiKxLU3lSCZpq+Kq8/v8kiky6bM+TR8n -oc2OuRf7JT7JbvN32g0S9l3HuzYQ1VTW8+DiR0jm3hTaYVKvJrT1cU/J19IG32PK -/yHoWQbgCNWEFVP3Q+V8xaCJmGtzxmjOZd69fwX3se72V6FglcXM6pM6vpmumwKj -rckWtc7dXpl4fho5frLABaTAgqWjR56M6ly2vGfb5ipN0gTco65F97yLnByn1tUD -3AjLLhbKXEAz6GfDLuemROoRRRw1ZS0eRWEkG4IupZ0zXWX4Qfkuy5Q/H6MMMSRE -7cderVC6xkGbrPAXZcD4XW9boAo0PO7X6oifmPmvTiT6l7Jkdtqr9O3jw2Dv1fkC -yC2fg69naQanMVXVz0tv/wQFx1isXxYb5dKj6zHbHzMVTdDypVP1y+E9Tmgt2BLd -qvLmTZtJ5cUoobqwWsagtQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1Ud -DwEB/wQEAwIBBjAdBgNVHQ4EFgQUiQq0OJMa5qvum5EY+fU8PjXQ04IwDQYJKoZI -hvcNAQEFBQADggIBADKL9p1Kyb4U5YysOMo6CdQbzoaz3evUuii+Eq5FLAR0rBNR -xVgYZk2C2tXck8An4b58n1KeElb21Zyp9HWc+jcSjxyT7Ff+Bw+r1RL3D65hXlaA -SfX8MPWbTx9BLxyE04nH4toCdu0Jz2zBuByDHBb6lM19oMgY0sidbvW9adRtPTXo -HqJPYNcHKfyyo6SdbhWSVhlMCrDpfNIZTUJG7L399ldb3Zh+pE3McgODWF3vkzpB -emOqfDqo9ayk0d2iLbYq/J8BjuIQscTK5GfbVSUZP/3oNn6z4eGBrxEWi1CXYBmC -AMBrTXO40RMHPuq2MU/wQppt4hF05ZSsjYSVPCGvxdpHyN85YmLLW1AL14FABZyb -7bq2ix4Eb5YgOe2kfSnbSM6C3NQCjR0EMVrHS/BsYVLXtFHCgWzN4funodKSds+x -DzdYpPJScWc/DIh4gInByLUfkmO+p3qKViwaqKactV2zY9ATIKHrkWzQjX2v3wvk -F7mGnjixlAxYjOBVqjtjbZqJYLhkKpLGN/R+Q0O3c+gB53+XD9fyexn9GtePyfqF -a3qdnom2piiZk4hA9z7NUaPK6u95RyG1/jLix8NRb76AdPCkwzryT+lf3xkK8jsT -Q6wxpLPn6/wY1gGp8yqPNg7rtLG8t0zJa7+h89n07eLw4+1knj0vllJPgFOL ------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----- -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----- -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----- -MIIFnDCCA4SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJGUjET -MBEGA1UEChMKQ2VydGlub21pczEXMBUGA1UECxMOMDAwMiA0MzM5OTg5MDMxJjAk -BgNVBAMMHUNlcnRpbm9taXMgLSBBdXRvcml0w6kgUmFjaW5lMB4XDTA4MDkxNzA4 -Mjg1OVoXDTI4MDkxNzA4Mjg1OVowYzELMAkGA1UEBhMCRlIxEzARBgNVBAoTCkNl -cnRpbm9taXMxFzAVBgNVBAsTDjAwMDIgNDMzOTk4OTAzMSYwJAYDVQQDDB1DZXJ0 -aW5vbWlzIC0gQXV0b3JpdMOpIFJhY2luZTCCAiIwDQYJKoZIhvcNAQEBBQADggIP -ADCCAgoCggIBAJ2Fn4bT46/HsmtuM+Cet0I0VZ35gb5j2CN2DpdUzZlMGvE5x4jY -F1AMnmHawE5V3udauHpOd4cN5bjr+p5eex7Ezyh0x5P1FMYiKAT5kcOrJ3NqDi5N -8y4oH3DfVS9O7cdxbwlyLu3VMpfQ8Vh30WC8Tl7bmoT2R2FFK/ZQpn9qcSdIhDWe -rP5pqZ56XjUl+rSnSTV3lqc2W+HN3yNw2F1MpQiD8aYkOBOo7C+ooWfHpi2GR+6K -/OybDnT0K0kCe5B1jPyZOQE51kqJ5Z52qz6WKDgmi92NjMD2AR5vpTESOH2VwnHu -7XSu5DaiQ3XV8QCb4uTXzEIDS3h65X27uK4uIJPT5GHfceF2Z5c/tt9qc1pkIuVC -28+BA5PY9OMQ4HL2AHCs8MF6DwV/zzRpRbWT5BnbUhYjBYkOjUjkJW+zeL9i9Qf6 -lSTClrLooyPCXQP8w9PlfMl1I9f09bze5N/NgL+RiH2nE7Q5uiy6vdFrzPOlKO1E -nn1So2+WLhl+HPNbxxaOu2B9d2ZHVIIAEWBsMsGoOBvrbpgT1u449fCfDu/+MYHB -0iSVL1N6aaLwD4ZFjliCK0wi1F6g530mJ0jfJUaNSih8hp75mxpZuWW/Bd22Ql09 -5gBIgl4g9xGC3srYn+Y3RyYe63j3YcNBZFgCQfna4NH4+ej9Uji29YnfAgMBAAGj -WzBZMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBQN -jLZh2kS40RR9w759XkjwzspqsDAXBgNVHSAEEDAOMAwGCiqBegFWAgIAAQEwDQYJ -KoZIhvcNAQEFBQADggIBACQ+YAZ+He86PtvqrxyaLAEL9MW12Ukx9F1BjYkMTv9s -ov3/4gbIOZ/xWqndIlgVqIrTseYyCYIDbNc/CMf4uboAbbnW/FIyXaR/pDGUu7ZM -OH8oMDX/nyNTt7buFHAAQCvaR6s0fl6nVjBhK4tDrP22iCj1a7Y+YEq6QpA0Z43q -619FVDsXrIvkxmUP7tCMXWY5zjKn2BCXwH40nJ+U8/aGH88bc62UeYdocMMzpXDn -2NU4lG9jeeu/Cg4I58UvD0KgKxRA/yHgBcUn4YQRE7rWhh1BCxMjidPJC+iKunqj -o3M3NYB9Ergzd0A4wPpeMNLytqOx1qKVl4GbUu1pTP+A5FPbVFsDbVRfsbjvJL1v -nxHDx2TCDyhihWZeGnuyt++uNckZM6i4J9szVb9o4XVIRFb7zdNIu0eJOqxp9YDG -5ERQL1TEqkPFMTFYvZbF6nVsmnWxTfj3l/+WFvKXTej28xH5On2KOG4Ey+HTRRWq -pdEdnV1j6CTmNhTih60bWfVEm/vXd3wfAXBioSAaosUaKPQhA+4u2cGA6rnZgtZb -dsLLO7XSAPCjDuGtbkD326C00EauFddEwk01+dIL8hf2rGbVJLJP0RyZwG71fet0 -BLj5TXcJ17TPBzAJ8bgAVtkXFhYKK4bfjwEZGuW7gmP/vgt2Fl43N+bYdJeimUV5 ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIF0zCCA7ugAwIBAgIVALhZFHE/V9+PMcAzPdLWGXojF7TrMA0GCSqGSIb3DQEB -DQUAMIGAMQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBUZWNobm9sb2dp -ZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5 -MSQwIgYDVQQDExtDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBIDIwHhcNMTExMDA2 -MDgzOTU2WhcNNDYxMDA2MDgzOTU2WjCBgDELMAkGA1UEBhMCUEwxIjAgBgNVBAoT -GVVuaXpldG8gVGVjaG5vbG9naWVzIFMuQS4xJzAlBgNVBAsTHkNlcnR1bSBDZXJ0 -aWZpY2F0aW9uIEF1dGhvcml0eTEkMCIGA1UEAxMbQ2VydHVtIFRydXN0ZWQgTmV0 -d29yayBDQSAyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAvfl4+ObV -gAxknYYblmRnPyI6HnUBfe/7XGeMycxca6mR5rlC5SBLm9qbe7mZXdmbgEvXhEAr -J9PoujC7Pgkap0mV7ytAJMKXx6fumyXvqAoAl4Vaqp3cKcniNQfrcE1K1sGzVrih -QTib0fsxf4/gX+GxPw+OFklg1waNGPmqJhCrKtPQ0WeNG0a+RzDVLnLRxWPa52N5 -RH5LYySJhi40PylMUosqp8DikSiJucBb+R3Z5yet/5oCl8HGUJKbAiy9qbk0WQq/ -hEr/3/6zn+vZnuCYI+yma3cWKtvMrTscpIfcRnNeGWJoRVfkkIJCu0LW8GHgwaM9 -ZqNd9BjuiMmNF0UpmTJ1AjHuKSbIawLmtWJFfzcVWiNoidQ+3k4nsPBADLxNF8tN -orMe0AZa3faTz1d1mfX6hhpneLO/lv403L3nUlbls+V1e9dBkQXcXWnjlQ1DufyD -ljmVe2yAWk8TcsbXfSl6RLpSpCrVQUYJIP4ioLZbMI28iQzV13D4h1L92u+sUS4H -s07+0AnacO+Y+lbmbdu1V0vc5SwlFcieLnhO+NqcnoYsylfzGuXIkosagpZ6w7xQ -EmnYDlpGizrrJvojybawgb5CAKT41v4wLsfSRvbljnX98sy50IdbzAYQYLuDNbde -Z95H7JlI8aShFf6tjGKOOVVPORa5sWOd/7cCAwEAAaNCMEAwDwYDVR0TAQH/BAUw -AwEB/zAdBgNVHQ4EFgQUtqFUOQLDoD+Oirz61PgcptE6Dv0wDgYDVR0PAQH/BAQD -AgEGMA0GCSqGSIb3DQEBDQUAA4ICAQCdU8KBJdw1LK4K3VqbRjBWu9S0bEuG5gql -0pKKmo3cj7TudvQDy+ubAXirKmu1uiNOMXy1LN0taWczbmNdORgS+KAoU0SHq2rE -kpYfKqIcup3dJ/tSTbCPWujtjcNo45KgJgyHkLAD6mplKAjERnjgW7oO8DPcJ7Z+ -iD29kqSWfkGogAh71jYSvBAVmyS8q619EYkvMe340s9Tjuu0U6fnBMovpiLEEdzr -mMkiXUFq3ApSBFu8LqB9x7aSuySg8zfRK0OozPFoeBp+b2OQe590yGvZC1X2eQM9 -g8dBQJL7dgs3JRc8rz76PFwbhvlKDD+KxF4OmPGt7s/g/SE1xzNhzKI3GEN8M+mu -doKCB0VIO8lnbq2jheiWVs+8u/qry7dXJ40aL5nzIzM0jspTY9NXNFBPz0nBBbrF -qId744aP+0OiEumsUewEdkzw+o+5MRPpCLckCfmgtwc2WFfPxLt+SWaVNQS2dzW4 -qVMpX5KF+FLEWk79BmE5+33QdkeSzOwrvYRu5ptFwX1isVMtnnWg58koUNflvKiq -B3hquXS0YPOEjQPcrpHadEQNe0Kpd9YrfKHGbBNTIqkSmqX5TyhFNbCXT0ZlhcX0 -/WKiomr8NDAGft8M4HOBlslEKt4fguxscletKWSk8cYpjjVgU85r2QK+OTB14Pdc -Y2rwQMEsjQ== ------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----- -MIIDkzCCAnugAwIBAgIQFBOWgxRVjOp7Y+X8NId3RDANBgkqhkiG9w0BAQUFADA0 -MRMwEQYDVQQDEwpDb21TaWduIENBMRAwDgYDVQQKEwdDb21TaWduMQswCQYDVQQG -EwJJTDAeFw0wNDAzMjQxMTMyMThaFw0yOTAzMTkxNTAyMThaMDQxEzARBgNVBAMT -CkNvbVNpZ24gQ0ExEDAOBgNVBAoTB0NvbVNpZ24xCzAJBgNVBAYTAklMMIIBIjAN -BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA8ORUaSvTx49qROR+WCf4C9DklBKK -8Rs4OC8fMZwG1Cyn3gsqrhqg455qv588x26i+YtkbDqthVVRVKU4VbirgwTyP2Q2 -98CNQ0NqZtH3FyrV7zb6MBBC11PN+fozc0yz6YQgitZBJzXkOPqUm7h65HkfM/sb -2CEJKHxNGGleZIp6GZPKfuzzcuc3B1hZKKxC+cX/zT/npfo4sdAMx9lSGlPWgcxC -ejVb7Us6eva1jsz/D3zkYDaHL63woSV9/9JLEYhwVKZBqGdTUkJe5DSe5L6j7Kpi -Xd3DTKaCQeQzC6zJMw9kglcq/QytNuEMrkvF7zuZ2SOzW120V+x0cAwqTwIDAQAB -o4GgMIGdMAwGA1UdEwQFMAMBAf8wPQYDVR0fBDYwNDAyoDCgLoYsaHR0cDovL2Zl -ZGlyLmNvbXNpZ24uY28uaWwvY3JsL0NvbVNpZ25DQS5jcmwwDgYDVR0PAQH/BAQD -AgGGMB8GA1UdIwQYMBaAFEsBmz5WGmU2dst7l6qSBe4y5ygxMB0GA1UdDgQWBBRL -AZs+VhplNnbLe5eqkgXuMucoMTANBgkqhkiG9w0BAQUFAAOCAQEA0Nmlfv4pYEWd -foPPbrxHbvUanlR2QnG0PFg/LUAlQvaBnPGJEMgOqnhPOAlXsDzACPw1jvFIUY0M -cXS6hMTXcpuEfDhOZAYnKuGntewImbQKDdSFc8gS4TXt8QUxHXOZDOuWyt3T5oWq -8Ir7dcHyCTxlZWTzTNity4hp8+SDtwy9F1qWF8pb/627HOkthIDYIb6FUtnUdLlp -hbpN7Sgy6/lhSuTENh4Z3G+EER+V9YMoGKgzkkMn3V0TBEVPh9VGzT2ouvDzuFYk -Res3x+F2T3I5GN9+dHLHcy056mDmrRGiVod7w2ia/viMcKjfZTL0pECMocJEAw6U -AGegcQCCSA== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -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 -DscLP1I= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDqzCCApOgAwIBAgIRAMcoRwmzuGxFjB36JPU2TukwDQYJKoZIhvcNAQEFBQAw -PDEbMBkGA1UEAxMSQ29tU2lnbiBTZWN1cmVkIENBMRAwDgYDVQQKEwdDb21TaWdu -MQswCQYDVQQGEwJJTDAeFw0wNDAzMjQxMTM3MjBaFw0yOTAzMTYxNTA0NTZaMDwx -GzAZBgNVBAMTEkNvbVNpZ24gU2VjdXJlZCBDQTEQMA4GA1UEChMHQ29tU2lnbjEL -MAkGA1UEBhMCSUwwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGtWhf -HZQVw6QIVS3joFd67+l0Kru5fFdJGhFeTymHDEjWaueP1H5XJLkGieQcPOqs49oh -gHMhCu95mGwfCP+hUH3ymBvJVG8+pSjsIQQPRbsHPaHA+iqYHU4Gk/v1iDurX8sW -v+bznkqH7Rnqwp9D5PGBpX8QTz7RSmKtUxvLg/8HZaWSLWapW7ha9B20IZFKF3ue -Mv5WJDmyVIRD9YTC2LxBkMyd1mja6YJQqTtoz7VdApRgFrFD2UNd3V2Hbuq7s8lr -9gOUCXDeFhF6K+h2j0kQmHe5Y1yLM5d19guMsqtb3nQgJT/j8xH5h2iGNXHDHYwt -6+UarA9z1YJZQIDTAgMBAAGjgacwgaQwDAYDVR0TBAUwAwEB/zBEBgNVHR8EPTA7 -MDmgN6A1hjNodHRwOi8vZmVkaXIuY29tc2lnbi5jby5pbC9jcmwvQ29tU2lnblNl -Y3VyZWRDQS5jcmwwDgYDVR0PAQH/BAQDAgGGMB8GA1UdIwQYMBaAFMFL7XC29z58 -ADsAj8c+DkWfHl3sMB0GA1UdDgQWBBTBS+1wtvc+fAA7AI/HPg5Fnx5d7DANBgkq -hkiG9w0BAQUFAAOCAQEAFs/ukhNQq3sUnjO2QiBq1BW9Cav8cujvR3qQrFHBZE7p -iL1DRYHjZiM/EoZNGeQFsOY3wo3aBijJD4mkU6l1P7CW+6tMM1X5eCZGbxs2mPtC -dsGCuY7e+0X5YxtiOzkGynd6qDwJz2w2PQ8KRUtpFhpFfTMDZflScZAmlaxMDPWL -kz/MdXSFmLr/YnpNH4n+rr2UAJm/EaXc4HnFFgt9AmEd6oX5AhVP51qJThRv4zdL -hfXBPGHg/QVBspJ/wx2g0K5SZGBrGMYmnNj1ZOQ2GmKfig8+/21OGVZOIJFsnzQz -OjRXUDpvgV4GxvU+fE6OK85lBi5d0ipTdF7Tbieejw== ------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----- -MIIEPzCCAyegAwIBAgIBATANBgkqhkiG9w0BAQUFADB+MQswCQYDVQQGEwJHQjEb -MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow -GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEkMCIGA1UEAwwbU2VjdXJlIENlcnRp -ZmljYXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4MTIzMTIzNTk1OVow -fjELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G -A1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxJDAiBgNV -BAMMG1NlY3VyZSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEB -BQADggEPADCCAQoCggEBAMBxM4KK0HDrc4eCQNUd5MvJDkKQ+d40uaG6EfQlhfPM -cm3ye5drswfxdySRXyWP9nQ95IDC+DwN879A6vfIUtFyb+/Iq0G4bi4XKpVpDM3S -HpR7LZQdqnXXs5jLrLxkU0C8j6ysNstcrbvd4JQX7NFc0L/vpZXJkMWwrPsbQ996 -CF23uPJAGysnnlDOXmWCiIxe004MeuoIkbY2qitC++rCoznl2yY4rYsK7hljxxwk -3wN42ubqwUcaCwtGCd0C/N7Lh1/XMGNooa7cMqG6vv5Eq2i2pRcV/b3Vp6ea5EQz -6YiO/O1R65NxTq0B50SOqy3LqP4BSUjwwN3HaNiS/j0CAwEAAaOBxzCBxDAdBgNV -HQ4EFgQUPNiTiMLAggnMAZkGkyDpnnAJY08wDgYDVR0PAQH/BAQDAgEGMA8GA1Ud -EwEB/wQFMAMBAf8wgYEGA1UdHwR6MHgwO6A5oDeGNWh0dHA6Ly9jcmwuY29tb2Rv -Y2EuY29tL1NlY3VyZUNlcnRpZmljYXRlU2VydmljZXMuY3JsMDmgN6A1hjNodHRw -Oi8vY3JsLmNvbW9kby5uZXQvU2VjdXJlQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmww -DQYJKoZIhvcNAQEFBQADggEBAIcBbSMdflsXfcFhMs+P5/OKlFlm4J4oqF7Tt/Q0 -5qo5spcWxYJvMqTpjOev/e/C6LlLqqP05tqNZSH7uoDrJiiFGv45jN5bBAS0VPmj -Z55B+glSzAVIqMk/IQQezkhr/IXownuvf7fM+F86/TXGDe+X3EyrEeFryzHRbPtI -gKvcnDe4IRRLDXE97IMzbtFuMhbsmMcWi1mmNKsFVy2T96oTy9IT4rcuO81rUBcJ -aD61JlfutuC23bkpgHl9j6PwpCikFcSF9CfUa7/lXORlAnZUtOM3ZiTTGWHIUhDl -izeauan5Hb/qmZJhlv8BzaFfDbxxvA6sCx1HRR3B7Hzs/Sk= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEQzCCAyugAwIBAgIBATANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJHQjEb -MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow -GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDElMCMGA1UEAwwcVHJ1c3RlZCBDZXJ0 -aWZpY2F0ZSBTZXJ2aWNlczAeFw0wNDAxMDEwMDAwMDBaFw0yODEyMzEyMzU5NTla -MH8xCzAJBgNVBAYTAkdCMRswGQYDVQQIDBJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAO -BgNVBAcMB1NhbGZvcmQxGjAYBgNVBAoMEUNvbW9kbyBDQSBMaW1pdGVkMSUwIwYD -VQQDDBxUcnVzdGVkIENlcnRpZmljYXRlIFNlcnZpY2VzMIIBIjANBgkqhkiG9w0B -AQEFAAOCAQ8AMIIBCgKCAQEA33FvNlhTWvI2VFeAxHQIIO0Yfyod5jWaHiWsnOWW -fnJSoBVC21ndZHoa0Lh73TkVvFVIxO06AOoxEbrycXQaZ7jPM8yoMa+j49d/vzMt -TGo87IvDktJTdyR0nAducPy9C1t2ul/y/9c3S0pgePfw+spwtOpZqqPOSC+pw7IL -fhdyFgymBwwbOM/JYrc/oJOlh0Hyt3BAd9i+FHzjqMB6juljatEPmsbS9Is6FARW -1O24zG71++IsWL1/T2sr92AkWCTOJu80kTrV44HQsvAEAtdbtz6SrGsSivnkBbA7 -kUlcsutT6vifR4buv5XAwAaf0lteERv0xwQ1KdJVXOTt6wIDAQABo4HJMIHGMB0G -A1UdDgQWBBTFe1i97doladL3WRaoszLAeydb9DAOBgNVHQ8BAf8EBAMCAQYwDwYD -VR0TAQH/BAUwAwEB/zCBgwYDVR0fBHwwejA8oDqgOIY2aHR0cDovL2NybC5jb21v -ZG9jYS5jb20vVHJ1c3RlZENlcnRpZmljYXRlU2VydmljZXMuY3JsMDqgOKA2hjRo -dHRwOi8vY3JsLmNvbW9kby5uZXQvVHJ1c3RlZENlcnRpZmljYXRlU2VydmljZXMu -Y3JsMA0GCSqGSIb3DQEBBQUAA4IBAQDIk4E7ibSvuIQSTI3S8NtwuleGFTQQuS9/ -HrCoiWChisJ3DFBKmwCL2Iv0QeLQg4pKHBQGsKNoBXAxMKdTmw7pSqBYaWcOrp32 -pSxBvzwGa+RZzG0Q8ZZvH9/0BAKkn0U+yNj6NkZEUD+Cl5EfKNsYEYwq5GWDVxIS -jBc/lDb+XbDABHcTuPQV1T84zJQ6VdCsmPW6AF/ghhmBeC8owH7TzEIK9a5QoNE+ -xqFx7D+gIIxmOom0jtTYsU0lR+4viMi14QVFwL4Ucd56/Y57fU0IlqUSc/Atyjcn -dBInTMu2l+nZrghtWjlA3QVHdWpaIbOjGM9O9y5Xt5hwXsjEeLBi ------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----- -MIIDOzCCAiOgAwIBAgIRANAeRlAAACmMAAAAAgAAAAIwDQYJKoZIhvcNAQEFBQAw -PzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD -Ew5EU1QgUm9vdCBDQSBYNDAeFw0wMDA5MTMwNjIyNTBaFw0yMDA5MTMwNjIyNTBa -MD8xJDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0dXJlIFRydXN0IENvLjEXMBUGA1UE -AxMORFNUIFJvb3QgQ0EgWDQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB -AQCthX3OFEYY8gSeIYur0O4ypOT68HnDrjLfIutL5PZHRwQGjzCPb9PFo/ihboJ8 -RvfGhBAqpQCo47zwYEhpWm1jB+L/OE/dBBiyn98krfU2NiBKSom2J58RBeAwHGEy -cO+lewyjVvbDDLUy4CheY059vfMjPAftCRXjqSZIolQb9FdPcAoa90mFwB7rKniE -J7vppdrUScSS0+eBrHSUPLdvwyn4RGp+lSwbWYcbg5EpSpE0GRJdchic0YDjvIoC -YHpe7Rkj93PYRTQyU4bhC88ck8tMqbvRYqMRqR+vobbkrj5LLCOQCHV5WEoxWh+0 -E2SpIFe7RkV++MmpIAc0h1tZAgMBAAGjMjAwMA8GA1UdEwEB/wQFMAMBAf8wHQYD -VR0OBBYEFPCD6nPIP1ubWzdf9UyPWvf0hki9MA0GCSqGSIb3DQEBBQUAA4IBAQCE -G85wl5eEWd7adH6XW/ikGN5salvpq/Fix6yVTzE6CrhlP5LBdkf6kx1bSPL18M45 -g0rw2zA/MWOhJ3+S6U+BE0zPGCuu8YQaZibR7snm3HiHUaZNMu5c8D0x0bcMxDjY -AVVcHCoNiL53Q4PLW27nbY6wwG0ffFKmgV3blxrYWfuUDgGpyPwHwkfVFvz9qjaV -mf12VJffL6W8omBPtgteb6UaT/k1oJ7YI0ldGf+ngpVbRhD+LC3cUtT6GO/BEPZu -8YTV/hbiDH5v3khVqMIeKT6o8IuXGG7F6a6vKwP1F1FwTXf4UC/ivhme7vdUH7B/ -Vv4AEbT8dNfEeFxrkDbh ------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----- -MIIDcDCCAligAwIBAgIBBTANBgkqhkiG9w0BAQUFADBbMQswCQYDVQQGEwJVUzEY -MBYGA1UEChMPVS5TLiBHb3Zlcm5tZW50MQwwCgYDVQQLEwNEb0QxDDAKBgNVBAsT -A1BLSTEWMBQGA1UEAxMNRG9EIFJvb3QgQ0EgMjAeFw0wNDEyMTMxNTAwMTBaFw0y -OTEyMDUxNTAwMTBaMFsxCzAJBgNVBAYTAlVTMRgwFgYDVQQKEw9VLlMuIEdvdmVy -bm1lbnQxDDAKBgNVBAsTA0RvRDEMMAoGA1UECxMDUEtJMRYwFAYDVQQDEw1Eb0Qg -Um9vdCBDQSAyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwCzB9o07 -rP8/PNZxvrh0IgfscEEV/KtA4weqwcPYn/7aTDq/P8jYKHtLNgHArEUlw9IOCo+F -GGQQPRoTcCpvjtfcjZOzQQ84Ic2tq8I9KgXTVxE3Dc2MUfmT48xGSSGOFLTNyxQ+ -OM1yMe6rEvJl6jQuVl3/7mN1y226kTT8nvP0LRy+UMRC31mI/2qz+qhsPctWcXEF -lrufgOWARVlnQbDrw61gpIB1BhecDvRD4JkOG/t/9bPMsoGCsf0ywbi+QaRktWA6 -WlEwjM7eQSwZR1xJEGS5dKmHQa99brrBuKG/ZTE6BGf5tbuOkooAY7ix5ow4X4P/ -UNU7ol1rshDMYwIDAQABoz8wPTAdBgNVHQ4EFgQUSXS7DF66ev4CVO97oMaVxgmA -cJYwCwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQAD -ggEBAJiRjT+JyLv1wGlzKTs1rLqzCHY9cAmS6YREIQF9FHYb7lFsHY0VNy17MWn0 -mkS4r0bMNPojywMnGdKDIXUr5+AbmSbchECV6KjSzPZYXGbvP0qXEIIdugqi3VsG -K52nZE7rLgE1pLQ/E61V5NVzqGmbEfGY8jEeb0DU+HifjpGgb3AEkGaqBivO4XqS -tX3h4NGW56E6LcyxnR8FRO2HmdNNGnA5wQQM5X7Z8a/XIA7xInolpHOZzD+kByeW -qKKV7YK5FtOeC4fCwfKI9WLfaN/HvGlR7bFc3FRUKQ8JOZqsA8HbDE2ubwp6Fknx -v5HSOJTT9pUst2zJQraNypCNhdk= ------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----- -MIIE5zCCA8+gAwIBAgIBADANBgkqhkiG9w0BAQUFADCBjTELMAkGA1UEBhMCQ0Ex -EDAOBgNVBAgTB09udGFyaW8xEDAOBgNVBAcTB1Rvcm9udG8xHTAbBgNVBAoTFEVj -aG93b3J4IENvcnBvcmF0aW9uMR8wHQYDVQQLExZDZXJ0aWZpY2F0aW9uIFNlcnZp -Y2VzMRowGAYDVQQDExFFY2hvd29yeCBSb290IENBMjAeFw0wNTEwMDYxMDQ5MTNa -Fw0zMDEwMDcxMDQ5MTNaMIGNMQswCQYDVQQGEwJDQTEQMA4GA1UECBMHT250YXJp -bzEQMA4GA1UEBxMHVG9yb250bzEdMBsGA1UEChMURWNob3dvcnggQ29ycG9yYXRp -b24xHzAdBgNVBAsTFkNlcnRpZmljYXRpb24gU2VydmljZXMxGjAYBgNVBAMTEUVj -aG93b3J4IFJvb3QgQ0EyMIIBIDANBgkqhkiG9w0BAQEFAAOCAQ0AMIIBCAKCAQEA -utU/5BkV15UBf+s+JQruKQxr77s3rjp/RpOtmhHILIiO5gsEWP8MMrfrVEiidjI6 -Qh6ans0KAWc2Dw0/j4qKAQzOSyAZgjcdypNTBZ7muv212DA2Pu41rXqwMrlBrVi/ -KTghfdLlNRu6JrC5y8HarrnRFSKF1Thbzz921kLDRoCi+FVs5eVuK5LvIfkhNAqA -byrTgO3T9zfZgk8upmEkANPDL1+8y7dGPB/d6lk0I5mv8PESKX02TlvwgRSIiTHR -k8++iOPLBWlGp7ZfqTEXkPUZhgrQQvxcrwCUo6mk8TqgxCDP5FgPoHFiPLef5szP -ZLBJDWp7GLyE1PmkQI6WiwIBA6OCAVAwggFMMA8GA1UdEwEB/wQFMAMBAf8wCwYD -VR0PBAQDAgEGMB0GA1UdDgQWBBQ74YEboKs/OyGC1eISrq5QqxSlEzCBugYDVR0j -BIGyMIGvgBQ74YEboKs/OyGC1eISrq5QqxSlE6GBk6SBkDCBjTELMAkGA1UEBhMC -Q0ExEDAOBgNVBAgTB09udGFyaW8xEDAOBgNVBAcTB1Rvcm9udG8xHTAbBgNVBAoT -FEVjaG93b3J4IENvcnBvcmF0aW9uMR8wHQYDVQQLExZDZXJ0aWZpY2F0aW9uIFNl -cnZpY2VzMRowGAYDVQQDExFFY2hvd29yeCBSb290IENBMoIBADBQBgNVHSAESTBH -MEUGCysGAQQB+REKAQMBMDYwNAYIKwYBBQUHAgEWKGh0dHA6Ly93d3cuZWNob3dv -cnguY29tL2NhL3Jvb3QyL2Nwcy5wZGYwDQYJKoZIhvcNAQEFBQADggEBAG+nrPi/ -0RpfEzrj02C6JGPUar4nbjIhcY6N7DWNeqBoUulBSIH/PYGNHYx7/lnJefiixPGE -7TQ5xPgElxb9bK8zoAApO7U33OubqZ7M7DlHnFeCoOoIAZnG1kuwKwD5CXKB2a74 -HzcqNnFW0IsBFCYqrVh/rQgJOzDA8POGbH0DeD0xjwBBooAolkKT+7ZItJF1Pb56 -QpDL9G+16F7GkmnKlAIYT3QTS3yFGYChnJcd+6txUPhKi9sSOOmAIaKHnkH9Scz+ -A2cSi4A3wUYXVatuVNHpRb2lygfH3SuCX9MU8Ure3zBlSU1LALtMqI4JmcQmQpIq -zIzvO2jHyu9PQqo= ------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----- -MIIDoTCCAomgAwIBAgIQKTZHquOKrIZKI1byyrdhrzANBgkqhkiG9w0BAQUFADBO -MQswCQYDVQQGEwJ1czEYMBYGA1UEChMPVS5TLiBHb3Zlcm5tZW50MQ0wCwYDVQQL -EwRGQkNBMRYwFAYDVQQDEw1Db21tb24gUG9saWN5MB4XDTA3MTAxNTE1NTgwMFoX -DTI3MTAxNTE2MDgwMFowTjELMAkGA1UEBhMCdXMxGDAWBgNVBAoTD1UuUy4gR292 -ZXJubWVudDENMAsGA1UECxMERkJDQTEWMBQGA1UEAxMNQ29tbW9uIFBvbGljeTCC -ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJeNvTMn5K1b+3i9L0dHbsd4 -6ZOcpN7JHP0vGzk4rEcXwH53KQA7Ax9oD81Npe53uCxiazH2+nIJfTApBnznfKM9 -hBiKHa4skqgf6F5PjY7rPxr4nApnnbBnTfAu0DDew5SwoM8uCjR/VAnTNr2kSVdS -c+md/uRIeUYbW40y5KVIZPMiDZKdCBW/YDyD90ciJSKtKXG3d+8XyaK2lF7IMJCk -FEhcVlcLQUwF1CpMP64Sm1kRdXAHImktLNMxzJJ+zM2kfpRHqpwJCPZLr1LoakCR -xVW9QLHIbVeGlRfmH3O+Ry4+i0wXubklHKVSFzYIWcBCvgortFZRPBtVyYyQd+sC -AwEAAaN7MHkwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O -BBYEFC9Yl9ipBZilVh/72at17wI8NjTHMBIGCSsGAQQBgjcVAQQFAgMBAAEwIwYJ -KwYBBAGCNxUCBBYEFHa3YJbdFFYprHWF03BjwbxHhhyLMA0GCSqGSIb3DQEBBQUA -A4IBAQBgrvNIFkBypgiIybxHLCRLXaCRc+1leJDwZ5B6pb8KrbYq+Zln34PFdx80 -CTj5fp5B4Ehg/uKqXYeI6oj9XEWyyWrafaStsU+/HA2fHprA1RRzOCuKeEBuMPdi -4c2Z/FFpZ2wR3bgQo2jeJqVW/TZsN5hs++58PGxrcD/3SDcJjwtCga1GRrgLgwb0 -Gzigf0/NC++DiYeXHIowZ9z9VKEDfgHLhUyxCynDvux84T8PCVI8L6eaSP436REG -WOE2QYrEtr+O3c5Ks7wawM36GpnScZv6z7zyxFSjiDV2zBssRm8MtNHDYXaSdBHq -S4CNHIkRi+xb/xfJSPzn4AYR4oRe ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEYDCCA0igAwIBAgICATAwDQYJKoZIhvcNAQELBQAwWTELMAkGA1UEBhMCVVMx -GDAWBgNVBAoTD1UuUy4gR292ZXJubWVudDENMAsGA1UECxMERlBLSTEhMB8GA1UE -AxMYRmVkZXJhbCBDb21tb24gUG9saWN5IENBMB4XDTEwMTIwMTE2NDUyN1oXDTMw -MTIwMTE2NDUyN1owWTELMAkGA1UEBhMCVVMxGDAWBgNVBAoTD1UuUy4gR292ZXJu -bWVudDENMAsGA1UECxMERlBLSTEhMB8GA1UEAxMYRmVkZXJhbCBDb21tb24gUG9s -aWN5IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2HX7NRY0WkG/ -Wq9cMAQUHK14RLXqJup1YcfNNnn4fNi9KVFmWSHjeavUeL6wLbCh1bI1FiPQzB6+ -Duir3MPJ1hLXp3JoGDG4FyKyPn66CG3G/dFYLGmgA/Aqo/Y/ISU937cyxY4nsyOl -4FKzXZbpsLjFxZ+7xaBugkC7xScFNknWJidpDDSPzyd6KgqjQV+NHQOGgxXgVcHF -mCye7Bpy3EjBPvmE0oSCwRvDdDa3ucc2Mnr4MrbQNq4iGDGMUHMhnv6DOzCIJOPp -wX7e7ZjHH5IQip9bYi+dpLzVhW86/clTpyBLqtsgqyFOHQ1O5piF5asRR12dP8Qj -wOMUBm7+nQIDAQABo4IBMDCCASwwDwYDVR0TAQH/BAUwAwEB/zCB6QYIKwYBBQUH -AQsEgdwwgdkwPwYIKwYBBQUHMAWGM2h0dHA6Ly9odHRwLmZwa2kuZ292L2ZjcGNh -L2NhQ2VydHNJc3N1ZWRCeWZjcGNhLnA3YzCBlQYIKwYBBQUHMAWGgYhsZGFwOi8v -bGRhcC5mcGtpLmdvdi9jbj1GZWRlcmFsJTIwQ29tbW9uJTIwUG9saWN5JTIwQ0Es -b3U9RlBLSSxvPVUuUy4lMjBHb3Zlcm5tZW50LGM9VVM/Y0FDZXJ0aWZpY2F0ZTti -aW5hcnksY3Jvc3NDZXJ0aWZpY2F0ZVBhaXI7YmluYXJ5MA4GA1UdDwEB/wQEAwIB -BjAdBgNVHQ4EFgQUrQx6dVzl85jEeZgOrCj9l/TnAvwwDQYJKoZIhvcNAQELBQAD -ggEBAI9z2uF/gLGH9uwsz9GEYx728Yi3mvIRte9UrYpuGDco71wb5O9Qt2wmGCMi -TR0mRyDpCZzicGJxqxHPkYnos/UqoEfAFMtOQsHdDA4b8Idb7OV316rgVNdF9IU+ -7LQd3nyKf1tNnJaK0KIyn9psMQz4pO9+c+iR3Ah6cFqgr2KBWfgAdKLI3VTKQVZH -venAT+0g3eOlCd+uKML80cgX2BLHb94u6b2akfI8WpQukSKAiaGMWMyDeiYZdQKl -Dn0KJnNR6obLB6jI/WNaNZvSr79PMUjBhHDbNXuaGQ/lj/RqDG8z2esccKIN47lQ -A2EC/0rskqTcLe4qNJMHtyznGI8= ------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----- -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----- -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----- -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----- -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----- -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----- -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----- -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----- -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----- -MIIFejCCA2KgAwIBAgIJAN7E8kTzHab8MA0GCSqGSIb3DQEBCwUAMEoxCzAJBgNV -BAYTAkNIMRUwEwYDVQQKEwxTd2lzc1NpZ24gQUcxJDAiBgNVBAMTG1N3aXNzU2ln -biBHb2xkIFJvb3QgQ0EgLSBHMzAeFw0wOTA4MDQxMzMxNDdaFw0zNzA4MDQxMzMx -NDdaMEoxCzAJBgNVBAYTAkNIMRUwEwYDVQQKEwxTd2lzc1NpZ24gQUcxJDAiBgNV -BAMTG1N3aXNzU2lnbiBHb2xkIFJvb3QgQ0EgLSBHMzCCAiIwDQYJKoZIhvcNAQEB -BQADggIPADCCAgoCggIBAMPon8hlWp1nG8FFl7S0h0NbYWCAnvJ/XvlnRN1E+qu1 -q3f/KhlMzm/Ej0Gf4OLNcuDR1FJhQQkKvwpw++CDaWEpytsimlul5t0XlbBvhI46 -PmRaQfsbWPz9Kz6ypOasyYK8zvaV+Jd37Sb2WK6eJ+IPg+zFNljIe8/Vh6GphxoT -Z2EBbaZpnOKQ8StoZfPosHz8gj3erdgKAAlEeROc8P5udXvCvLNZAQt8xdUt8L// -bVfSSYHrtLNQrFv5CxUVjGn/ozkB7fzc3CeXjnuL1Wqm1uAdX80Bkeb1Ipi6LgkY -OG8TqIHS+yE35y20YueBkLDGeVm3Z3X+vo87+jbsr63ST3Q2AeVXqyMEzEpel89+ -xu+MzJUjaY3LOMcZ9taKABQeND1v2gwLw7qX/BFLUmE+vzNnUxC/eBsJwke6Hq9Y -9XWBf71W8etW19lpDAfpNzGwEhwy71bZvnorfL3TPbxqM006PFAQhyfHegpnU9t/ -gJvoniP6+Qg6i6GONFpIM19k05eGBxl9iJTOKnzFat+vvKmfzTqmurtU+X+P388O -WsStmryzOndzg0yTPJBotXxQlRHIgl6UcdBBGPvJxmXszom2ziKzEVs/4J0+Gxho -DaoDoWdZv2udvPjyZS+aQTpF2F7QNmxvOx5jtI6YTBPbIQ6fe+3qoKpxw+ujoNIl -AgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud -DgQWBBRclwZGNKvfMMV8xQ1VcWYwtWCPnjAfBgNVHSMEGDAWgBRclwZGNKvfMMV8 -xQ1VcWYwtWCPnjANBgkqhkiG9w0BAQsFAAOCAgEAd0tN3uqFSqssJ9ZFx/FfIMFb -YO0Hy6Iz3DbPx5TxBsfV2s/NrYQ+/xJIf0HopWZXMMQd5KcaLy1Cwe9Gc7LV9Vr9 -Dnpr0sgxow1IlldlY1UYwPzkisyYhlurDIonN/ojaFlcJtehwcK5Tiz/KV7mlAu+ -zXJPleiP9ve4Pl7Oz54RyawDKUiKqbamNLmsQP/EtnM3scd/qVHbSypHX0AkB4gG -tySz+3/3sIsz+r8jdaNc/qplGsK+8X2BdwOBsY3XlQ16PEKYt4+pfVDh31IGmqBS -VHiDB2FSCTdeipynxlHRXGPRhNzC29L6Wxg2fWa81CiXL3WWHIQHrIuOUxG+JCGq -Z/LBrYic07B4Z3j101gDIApdIPG152XMDiDj1d/mLxkrhWjBBCbPj+0FU6HdBw7r -QSbHtKksW+NpPWbAYhvAqobAN8MxBIZwOb5rXyFAQaB/5dkPOEtwX0n4hbgrLqof -k0FD+PuydDwfS1dbt9RRoZJKzr4Qou7YFCJ7uUG9jemIqdGPAxpg/z+HiaCZJyJm -sD5onnKIUTidEz5FbQXlRrVz7UOGsRQKHrzaDb8eJFxmjw6+of3G62m8Q3nXA3b5 -3IeZuJjEzX9tEPkQvixC/pwpTYNrCr21jsRIiv0hB6aAfR+b6au9gmFECnEnX22b -kJ6u/zYks2gD1pWMa3M= ------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----- -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----- -MIIFHjCCBAagAwIBAgIEAKA3oDANBgkqhkiG9w0BAQsFADCBtzELMAkGA1UEBhMC -Q1oxOjA4BgNVBAMMMUkuQ0EgLSBRdWFsaWZpZWQgQ2VydGlmaWNhdGlvbiBBdXRo -b3JpdHksIDA5LzIwMDkxLTArBgNVBAoMJFBydm7DrSBjZXJ0aWZpa2HEjW7DrSBh -dXRvcml0YSwgYS5zLjE9MDsGA1UECww0SS5DQSAtIEFjY3JlZGl0ZWQgUHJvdmlk -ZXIgb2YgQ2VydGlmaWNhdGlvbiBTZXJ2aWNlczAeFw0wOTA5MDEwMDAwMDBaFw0x -OTA5MDEwMDAwMDBaMIG3MQswCQYDVQQGEwJDWjE6MDgGA1UEAwwxSS5DQSAtIFF1 -YWxpZmllZCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSwgMDkvMjAwOTEtMCsGA1UE -CgwkUHJ2bsOtIGNlcnRpZmlrYcSNbsOtIGF1dG9yaXRhLCBhLnMuMT0wOwYDVQQL -DDRJLkNBIC0gQWNjcmVkaXRlZCBQcm92aWRlciBvZiBDZXJ0aWZpY2F0aW9uIFNl -cnZpY2VzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtTaEy0KC8M9l -4lSaWHMs4+sVV1LwzyJYiIQNeCrv1HHm/YpGIdY/Z640ceankjQvIX7m23BK4OSC -6KO8kZYA3zopOz6GFCOKV2PvLukbc+c2imF6kLHEv6qNA8WxhPbR3xKwlHDwB2yh -Wzo7V3QVgDRG83sugqQntKYC3LnlTGbJpNP+Az72gpO9AHUn/IBhFk4ksc8lYS2L -9GCy9CsmdKSBP78p9w8Lx7vDLqkDgt1/zBrcUWmSSb7AE/BPEeMryQV1IdI6nlGn -BhWkXOYf6GSdayJw86btuxC7viDKNrbp44HjQRaSxnp6O3eto1x4DfiYdw/YbJFe -7EjkxSQBywIDAQABo4IBLjCCASowDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8E -BAMCAQYwgecGA1UdIASB3zCB3DCB2QYEVR0gADCB0DCBzQYIKwYBBQUHAgIwgcAa -gb1UZW50byBjZXJ0aWZpa2F0IGplIHZ5ZGFuIGpha28ga3ZhbGlmaWtvdmFueSBz -eXN0ZW1vdnkgY2VydGlmaWthdCBwb2RsZSB6YWtvbmEgYy4gMjI3LzIwMDAgU2Iu -IHYgcGxhdG5lbSB6bmVuaS9UaGlzIGlzIHF1YWxpZmllZCBzeXN0ZW0gY2VydGlm -aWNhdGUgYWNjb3JkaW5nIHRvIEN6ZWNoIEFjdCBOby4gMjI3LzIwMDAgQ29sbC4w -HQYDVR0OBBYEFHnL0CPpOmdwkXRP01Hi4CD94Sj7MA0GCSqGSIb3DQEBCwUAA4IB -AQB9laU214hYaBHPZftbDS/2dIGLWdmdSbj1OZbJ8LIPBMxYjPoEMqzAR74tw96T -i6aWRa5WdOWaS6I/qibEKFZhJAVXX5mkx2ewGFLJ+0Go+eTxnjLOnhVF2V2s+57b -m8c8j6/bS6Ij6DspcHEYpfjjh64hE2r0aSpZDjGzKFM6YpqsCJN8qYe2X1qmGMLQ -wvNdjG+nPzCJOOuUEypIWt555ZDLXqS5F7ZjBjlfyDZjEfS2Es9Idok8alf563Mi -9/o+Ba46wMYOkk3P1IlU0RqCajdbliioACKDztAqubONU1guZVzV8tuMASVzbJeL -/GAB7ECTwe1RuKrLYtglMKI9 ------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----- -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----- -MIIECTCCAvGgAwIBAgIQDV6ZCtadt3js2AdWO4YV2TANBgkqhkiG9w0BAQUFADBb -MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3Qx -ETAPBgNVBAsTCERTVCBBQ0VTMRcwFQYDVQQDEw5EU1QgQUNFUyBDQSBYNjAeFw0w -MzExMjAyMTE5NThaFw0xNzExMjAyMTE5NThaMFsxCzAJBgNVBAYTAlVTMSAwHgYD -VQQKExdEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdDERMA8GA1UECxMIRFNUIEFDRVMx -FzAVBgNVBAMTDkRTVCBBQ0VTIENBIFg2MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A -MIIBCgKCAQEAuT31LMmU3HWKlV1j6IR3dma5WZFcRt2SPp/5DgO0PWGSvSMmtWPu -ktKe1jzIDZBfZIGxqAgNTNj50wUoUrQBJcWVHAx+PhCEdc/BGZFjz+iokYi5Q1K7 -gLFViYsx+tC3dr5BPTCapCIlF3PoHuLTrCq9Wzgh1SpL11V94zpVvddtawJXa+ZH -fAjIgrrep4c9oW24MFbCswKBXy314powGCi4ZtPLAZZv6opFVdbgnf9nKxcCpk4a -ahELfrd755jWjHZvwTvbUJN+5dCOHze4vbrGn2zpfDPyMjwmR/onJALJfh1biEIT -ajV8fTXpLmaRcpPVMibEdPVTo7NdmvYJywIDAQABo4HIMIHFMA8GA1UdEwEB/wQF -MAMBAf8wDgYDVR0PAQH/BAQDAgHGMB8GA1UdEQQYMBaBFHBraS1vcHNAdHJ1c3Rk -c3QuY29tMGIGA1UdIARbMFkwVwYKYIZIAWUDAgEBATBJMEcGCCsGAQUFBwIBFjto -dHRwOi8vd3d3LnRydXN0ZHN0LmNvbS9jZXJ0aWZpY2F0ZXMvcG9saWN5L0FDRVMt -aW5kZXguaHRtbDAdBgNVHQ4EFgQUCXIGThhDD+XWzMNqizF7eI+og7gwDQYJKoZI -hvcNAQEFBQADggEBAKPYjtay284F5zLNAdMEA+V25FYrnJmQ6AgwbN99Pe7lv7Uk -QIRJ4dEorsTCOlMwiPH1d25Ryvr/ma8kXxug/fKshMrfqfBfBC6tFr8hlxCBPeP/ -h40y3JTlR4peahPJlJU90u7INJXQgNStMgiAVDzgvVJT11J8smk/f3rPanTK+gQq -nExaBqXpIK1FZg9p8d2/6eMyi/rgwYZNcjwu2JN4Cir42NInPRmJX1p7ijvMDNpR -rscL9yuwNwXsvFcj4jjSm2jzVhKIT0J8uDHEtdvkyCE06UgRNe76x5JXxZ805Mf2 -9w4LTJxoeHtxMcfrHuBnQfO3oKfN5XozNmr6mis= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIF8DCCA9igAwIBAgIPBuhGJy8fCo/RhFzjafbVMA0GCSqGSIb3DQEBBQUAMDgx -CzAJBgNVBAYTAkVTMRQwEgYDVQQKDAtJWkVOUEUgUy5BLjETMBEGA1UEAwwKSXpl -bnBlLmNvbTAeFw0wNzEyMTMxMzA4MjdaFw0zNzEyMTMwODI3MjVaMDgxCzAJBgNV -BAYTAkVTMRQwEgYDVQQKDAtJWkVOUEUgUy5BLjETMBEGA1UEAwwKSXplbnBlLmNv -bTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMnTesoPHqynhugWZWqx -whtFMnGV2f4QW8yv56V5AY+Jw8ryVXH3d753lPNypCxE2J6SmxQ6oeckkAoKVo7F -2CaU4dlI4S0+2gpy3aOZFdqBoof0e24md4lYrdbrDLJBenNubdt6eEHpCIgSfocu -ZhFjbFT7PJ1ywLwu/8K33Q124zrX97RovqL144FuwUZvXY3gTcZUVYkaMzEKsVe5 -o4qYw+w7NMWVQWl+dcI8IMVhulFHoCCQk6GQS/NOfIVFVJrRBSZBsLVNHTO+xAPI -JXzBcNs79AktVCdIrC/hxKw+yMuSTFM5NyPs0wH54AlETU1kwOENWocivK0bo/4m -tRXzp/yEGensoYi0RGmEg/OJ0XQGqcwL1sLeJ4VQJsoXuMl6h1YsGgEebL4TrRCs -tST1OJGh1kva8bvS3ke18byB9llrzxlT6Y0Vy0rLqW9E5RtBz+GGp8rQap+8TI0G -M1qiheWQNaBiXBZO8OOi+gMatCxxs1gs3nsL2xoP694hHwZ3BgOwye+Z/MC5TwuG -KP7Suerj2qXDR2kS4Nvw9hmL7Xtw1wLW7YcYKCwEJEx35EiKGsY7mtQPyvp10gFA -Wo15v4vPS8+qFsGV5K1Mij4XkdSxYuWC5YAEpAN+jb/af6IPl08M0w3719Hlcn4c -yHf/W5oPt64FRuXxqBbsR6QXAgMBAAGjgfYwgfMwgbAGA1UdEQSBqDCBpYEPaW5m -b0BpemVucGUuY29tpIGRMIGOMUcwRQYDVQQKDD5JWkVOUEUgUy5BLiAtIENJRiBB -MDEzMzcyNjAtUk1lcmMuVml0b3JpYS1HYXN0ZWl6IFQxMDU1IEY2MiBTODFDMEEG -A1UECQw6QXZkYSBkZWwgTWVkaXRlcnJhbmVvIEV0b3JiaWRlYSAxNCAtIDAxMDEw -IFZpdG9yaWEtR2FzdGVpejAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB -BjAdBgNVHQ4EFgQUHRxlDqjyJXu0kc/ksbHmvVV0bAUwDQYJKoZIhvcNAQEFBQAD -ggIBAMeBRm8hGE+gBe/n1bqXUKJg7aWSFBpSm/nxiEqg3Hh10dUflU7F57dp5iL0 -+CmoKom+z892j+Mxc50m0xwbRxYpB2iEitL7sRskPtKYGCwkjq/2e+pEFhsqxPqg -l+nqbFik73WrAGLRne0TNtsiC7bw0fRue0aHwp28vb5CO7dz0JoqPLRbEhYArxk5 -ja2DUBzIgU+9Ag89njWW7u/kwgN8KRwCfr00J16vU9adF79XbOnQgxCvv11N75B7 -XSus7Op9ACYXzAJcY9cZGKfsK8eKPlgOiofmg59OsjQerFQJTx0CCzl+gQgVuaBp -E8gyK+OtbBPWg50jLbJtooiGfqgNASYJQNntKE6MkyQP2/EeTXp6WuKlWPHcj1+Z -ggwuz7LdmMySlD/5CbOlliVbN/UShUHiGUzGigjB3Bh6Dx4/glmimj4/+eAJn/3B -kUtdyXvWton83x18hqrNA/ILUpLxYm9/h+qrdslsUMIZgq+qHfUgKGgu1fxkN0/P -pUTEvnK0jHS0bKf68r10OEMr3q/53NjgnZ/cPcqlY0S/kqJPTIAcuxrDmkoEVU3K -7iYLHL8CxWTTnn7S05EcS6L1HOUXHA0MUqORH5zwIe0ClG+poEnK6EOMxPQ02nwi -o8ZmPrgbBYhdurz3vOXcFD2nhqi2WVIhA16L4wTtSyoeo09Q ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEXzCCA0egAwIBAgIBATANBgkqhkiG9w0BAQUFADCB0DELMAkGA1UEBhMCRVMx -SDBGBgNVBAoTP0laRU5QRSBTLkEuIC0gQ0lGIEEtMDEzMzcyNjAtUk1lcmMuVml0 -b3JpYS1HYXN0ZWl6IFQxMDU1IEY2MiBTODFCMEAGA1UEBxM5QXZkYSBkZWwgTWVk -aXRlcnJhbmVvIEV0b3JiaWRlYSAzIC0gMDEwMTAgVml0b3JpYS1HYXN0ZWl6MRMw -EQYDVQQDEwpJemVucGUuY29tMR4wHAYJKoZIhvcNAQkBFg9JbmZvQGl6ZW5wZS5j -b20wHhcNMDMwMTMwMjMwMDAwWhcNMTgwMTMwMjMwMDAwWjCB0DELMAkGA1UEBhMC -RVMxSDBGBgNVBAoTP0laRU5QRSBTLkEuIC0gQ0lGIEEtMDEzMzcyNjAtUk1lcmMu -Vml0b3JpYS1HYXN0ZWl6IFQxMDU1IEY2MiBTODFCMEAGA1UEBxM5QXZkYSBkZWwg -TWVkaXRlcnJhbmVvIEV0b3JiaWRlYSAzIC0gMDEwMTAgVml0b3JpYS1HYXN0ZWl6 -MRMwEQYDVQQDEwpJemVucGUuY29tMR4wHAYJKoZIhvcNAQkBFg9JbmZvQGl6ZW5w -ZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC1btoCXXhp3xIW -D+Bxl8nUCxkyiazWfpt0e68t+Qt9+lZjKZSdEw2Omj4qvr+ovRmDXO3iWpWVOWDl -3JHJjAzFCe8ZEBNDH+QNYwZHmPBaMYFOYFdbAFVHWvys152C308hcFJ6xWWGmjvl -2eMiEl9P2nR2LWue368DCu+ak7j3gjAXaCOdP1a7Bfr+RW3X2SC5R4Xyp8iHlL5J -PHJD/WBkLrezwzQPdACw8m9EG7q9kUwlNpL32mROujS3ZkT6mQTzJieLiE3X04s0 -uIUqVkk5MhjcHFf7al0N5CzjtTcnXYJKN2Z9EDVskk4olAdGi46eSoZXbjUOP5gk -Ej6wVZAXAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEG -MB0GA1UdDgQWBBTqVk/sPIOhFIh4gbIrBSLAB0FbQjANBgkqhkiG9w0BAQUFAAOC -AQEAYp7mEzzhw6o5Hf5+T5kcI+t4BJyiIWy7vHlLs/G8dLYXO81aN/Mzg928eMTR -TxxYZL8dd9uwsJ50TVfX6L0R4Dyw6wikh3fHRrat9ufXi63j5K91Ysr7aXqnF38d -iAgHYkrwC3kuxHBb9C0KBz6h8Q45/KCyN7d37wWAq38yyhPDlaOvyoE6bdUuK5hT -m5EYA5JmPyrhQ1moDOyueWBAjxzMEMj+OAY1H90cLv6wszsqerxRrdTOHBdv7MjB -EIpvEEQkXUxVXAzFuuT6m2t91Lfnwfl/IvljHaVC7DlyyhRYHD6D4Rx+4QKp4tWL -vpw6LkI+gKNJ/YdMCsRZQzEEFA== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -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 ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIID9zCCAt+gAwIBAgILMTI1MzcyODI4MjgwDQYJKoZIhvcNAQELBQAwWDELMAkG -A1UEBhMCSlAxHDAaBgNVBAoTE0phcGFuZXNlIEdvdmVybm1lbnQxDTALBgNVBAsT -BEdQS0kxHDAaBgNVBAMTE0FwcGxpY2F0aW9uQ0EyIFJvb3QwHhcNMTMwMzEyMTUw -MDAwWhcNMzMwMzEyMTUwMDAwWjBYMQswCQYDVQQGEwJKUDEcMBoGA1UEChMTSmFw -YW5lc2UgR292ZXJubWVudDENMAsGA1UECxMER1BLSTEcMBoGA1UEAxMTQXBwbGlj -YXRpb25DQTIgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKaq -rSVl1gAR1uh6dqr05rRL88zDUrSNrKZPtZJxb0a11a2LEiIXJc5F6BR6hZrkIxCo -+rFnUOVtR+BqiRPjrq418fRCxQX3TZd+PCj8sCaRHoweOBqW3FhEl2LjMsjRFUFN -dZh4vqtoqV7tR76kuo6hApfek3SZbWe0BSXulMjtqqS6MmxCEeu+yxcGkOGThchk -KM4fR8fAXWDudjbcMztR63vPctgPeKgZggiQPhqYjY60zxU2pm7dt+JNQCBT2XYq -0HisifBPizJtROouurCp64ndt295D6uBbrjmiykLWa+2SQ1RLKn9nShjZrhwlXOa -2Po7M7xCQhsyrLEy+z0CAwEAAaOBwTCBvjAdBgNVHQ4EFgQUVqesqgIdsqw9kA6g -by5Bxnbne9owDgYDVR0PAQH/BAQDAgEGMHwGA1UdEQR1MHOkcTBvMQswCQYDVQQG -EwJKUDEYMBYGA1UECgwP5pel5pys5Zu95pS/5bqcMRswGQYDVQQLDBLmlL/lupzo -qo3oqLzln7rnm6QxKTAnBgNVBAMMIOOCouODl+ODquOCseODvOOCt+ODp+ODs0NB -MiBSb290MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAH+aCXWs -B9FydC53VzDCBJzUgKaD56WgG5/+q/OAvdVKo6GPtkxgEefK4WCB10jBIFmlYTKL -nZ6X02aD2mUuWD7b5S+lzYxzplG+WCigeVxpL0PfY7KJR8q73rk0EWOgDiUX5Yf0 -HbCwpc9BqHTG6FPVQvSCLVMJEWgmcZR1E02qdog8dLHW40xPYsNJTE5t8XB+w3+m -Bcx4m+mB26jIx1ye/JKSLaaX8ji1bnOVDMA/zqaUMLX6BbfeniCq/BNkyYq6ZO/i -Y+TYmK5rtT6mVbgzPixy+ywRAPtbFi+E0hOe+gXFwctyTiLdhMpLvNIthhoEdlkf -SUJiOxMfFui61/0= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDcTCCAlmgAwIBAgIVAOYJ/nrqAGiM4CS07SAbH+9StETRMA0GCSqGSIb3DQEB -BQUAMFAxCzAJBgNVBAYTAlBMMSgwJgYDVQQKDB9LcmFqb3dhIEl6YmEgUm96bGlj -emVuaW93YSBTLkEuMRcwFQYDVQQDDA5TWkFGSVIgUk9PVCBDQTAeFw0xMTEyMDYx -MTEwNTdaFw0zMTEyMDYxMTEwNTdaMFAxCzAJBgNVBAYTAlBMMSgwJgYDVQQKDB9L -cmFqb3dhIEl6YmEgUm96bGljemVuaW93YSBTLkEuMRcwFQYDVQQDDA5TWkFGSVIg -Uk9PVCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKxHL49ZMTml -6g3wpYwrvQKkvc0Kc6oJ5sxfgmp1qZfluwbv88BdocHSiXlY8NzrVYzuWBp7J/9K -ULMAoWoTIzOQ6C9TNm4YbA9A1jdX1wYNL5Akylf8W5L/I4BXhT9KnlI6x+a7BVAm -nr/Ttl+utT/Asms2fRfEsF2vZPMxH4UFqOAhFjxTkmJWf2Cu4nvRQJHcttB+cEAo -ag/hERt/+tzo4URz6x6r19toYmxx4FjjBkUhWQw1X21re//Hof2+0YgiwYT84zLb -eqDqCOMOXxvH480yGDkh/QoazWX3U75HQExT/iJlwnu7I1V6HXztKIwCBjsxffbH -3jOshCJtywcCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC -AQYwHQYDVR0OBBYEFFOSo33/gnbwM9TrkmdHYTMbaDsqMA0GCSqGSIb3DQEBBQUA -A4IBAQA5UFWd5EL/pBviIMm1zD2JLUCpp0mJG7JkwznIOzawhGmFFaxGoxAhQBEg -haP+E0KR66oAwVC6xe32QUVSHfWqWndzbODzLB8yj7WAR0cDM45ZngSBPBuFE3Wu -GLJX9g100ETfIX+4YBR/4NR/uvTnpnd9ete7Whl0ZfY94yuu4xQqB5QFv+P7IXXV -lTOjkjuGXEcyQAjQzbFaT9vIABSbeCXWBbjvOXukJy6WgAiclzGNSYprre8Ryydd -fmjW9HIGwsIO03EldivvqEYL1Hv1w/Pur+6FUEOaL68PEIUovfgwIB2BAw+vZDuw -cH0mX548PojGyg434cDjkSXa3mHF ------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----- -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----- -MIIFgTCCA2mgAwIBAgIIIj+pFyDegZQwDQYJKoZIhvcNAQELBQAwTjELMAkGA1UE -BhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzEoMCYGA1UEAxMfU3dpc3NTaWdu -IFBsYXRpbnVtIFJvb3QgQ0EgLSBHMzAeFw0wOTA4MDQxMzM0MDRaFw0zNzA4MDQx -MzM0MDRaME4xCzAJBgNVBAYTAkNIMRUwEwYDVQQKEwxTd2lzc1NpZ24gQUcxKDAm -BgNVBAMTH1N3aXNzU2lnbiBQbGF0aW51bSBSb290IENBIC0gRzMwggIiMA0GCSqG -SIb3DQEBAQUAA4ICDwAwggIKAoICAQCUoO8TG59EIBvNxaoiu9nyUj56Wlh35o2h -K8ncpPPksxOUAGKbHPJDUEOBfq8wNkmsGIkMGEW4PsdUbePYmllriholqba1Dbd9 -I/BffagHqfc+hi7IAU3c5jbtHeU3B2kSS+OD0QQcJPAfcHHnGe1zSG6VKxW2VuYC -31bpm/rqpu7gwsO64MzGyHvXbzqVmzqPvlss0qmgOD7WiOGxYhOO3KswZ82oaqZj -K4Kwy8c9Tu1y9n2rMk5lAusPmXT4HBoojA5FAJMsFJ9txxue9orce3jjtJRHHU0F -bYR6kFSynot1woDfhzk/n/tIVAeNoCn1+WBfWnLou5ugQuAIADSjFTwT49YaawKy -lCGjnUG8KmtOMzumlDj8PccrM7MuKwZ0rJsQb8VORfddoVYDLA1fer0e3h13kGva -pS2KTOnfQfTnS+x9lUKfTKkJD0OIPz2T5yv0ekjaaMTdEoAxGl0kVCamJCGzTK3a -Fwg2AlfGnIZwyXXJnnxh2HjmuegUafkcECgSXUt1ULo80GdwVVVWS/s9HNjbeU2X -37ie2xcs1TUHuFCp9473Vv96Z0NPINnKZtY4YEvulDHWDaJIm/80aZTGNfWWiO+q -ZsyBputMU/8ydKe2nZhXtLomqfEzM2J+OrADEVf/3G8RI60+xgrQzFS3LcKTHeXC -pozH2O9T9wIDAQABo2MwYTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB -/zAdBgNVHQ4EFgQUVio/kFj0F1oUstcIG4VbVGpUGigwHwYDVR0jBBgwFoAUVio/ -kFj0F1oUstcIG4VbVGpUGigwDQYJKoZIhvcNAQELBQADggIBAGztiudDqHknm7jP -hz5kOBiMEUKShjfgWMMb7gQu94TsgxBoDH94LZzCl442ThbYDuprSK1Pnl0NzA2p -PhiFfsxomTk11tifhsEy+01lsyIUS8iFZtoX/3GRrJxWV95xLFZCv/jNDvCi0//S -IhX70HgKfuGwWs6ON9upnueVz2PyLA3S+m/zyNX7ALf3NWcQ03tS7BAy+L/dXsmm -gqTxsL8dLt0l5L1N8DWpkQFH+BAClFvrPusNutUdYyylLqvn4x6j7kuqX7FmAbSC -WvlGS8fx+N8svv113ZY4mjc6bqXmMhVus5DAOYp0pZWgvg0uiXnNKVaOw15XUcQF -bwRVj4HpTL1ZRssqvE3JHfLGTwXkyAQN925P2sM6nNLC9enGJHoUPhxCMKgCRTGp -/FCp3NyGOA9bkz9/CE5qDSc6EHlWwxW4PgaG9tlwZ691eoviWMzGdU8yVcVsFAko -O/KV5GreLCgHraB9Byjd1Fqj6aZ8E4yZC1J429nR3z5aQ3Z/RmBTws3ndkd8Vc20 -OWQQW5VLNV1EgyTV4C4kDMGAbmkAgAZ3CmaCEAxRbzeJV9vzTOW4ue4jZpdgt1Ld -2Zb7uoo7oE3OXvBETJDMIU8bOphrjjGD+YMIUssZwTVr7qEVW4g/bazyNJJTpjAq -E9fmhqhd2ULSx52peovL3+6iMcLl ------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----- -MIIEvTCCA6WgAwIBAgIBADANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJFVTEn -MCUGA1UEChMeQUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQL -ExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEiMCAGA1UEAxMZQ2hhbWJlcnMg -b2YgQ29tbWVyY2UgUm9vdDAeFw0wMzA5MzAxNjEzNDNaFw0zNzA5MzAxNjEzNDRa -MH8xCzAJBgNVBAYTAkVVMScwJQYDVQQKEx5BQyBDYW1lcmZpcm1hIFNBIENJRiBB -ODI3NDMyODcxIzAhBgNVBAsTGmh0dHA6Ly93d3cuY2hhbWJlcnNpZ24ub3JnMSIw -IAYDVQQDExlDaGFtYmVycyBvZiBDb21tZXJjZSBSb290MIIBIDANBgkqhkiG9w0B -AQEFAAOCAQ0AMIIBCAKCAQEAtzZV5aVdGDDg2olUkfzIx1L4L1DZ77F1c2VHfRtb -unXF/KGIJPov7coISjlUxFF6tdpg6jg8gbLL8bvZkSM/SAFwdakFKq0fcfPJVD0d -BmpAPrMMhe5cG3nCYsS4No41XQEMIwRHNaqbYE6gZj3LJgqcQKH0XZi/caulAGgq -7YN6D6IUtdQis4CwPAxaUWktWBiP7Zme8a7ileb2R6jWDA+wWFjbw2Y3npuRVDM3 -0pQcakjJyfKl2qUMI/cjDpwyVV5xnIQFUZot/eZOKjRa3spAN2cMVCFVd9oKDMyX -roDclDZK9D7ONhMeU+SsTjoF7Nuucpw4i9A5O4kKPnf+dQIBA6OCAUQwggFAMBIG -A1UdEwEB/wQIMAYBAf8CAQwwPAYDVR0fBDUwMzAxoC+gLYYraHR0cDovL2NybC5j -aGFtYmVyc2lnbi5vcmcvY2hhbWJlcnNyb290LmNybDAdBgNVHQ4EFgQU45T1sU3p -26EpW1eLTXYGduHRooowDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIA -BzAnBgNVHREEIDAegRxjaGFtYmVyc3Jvb3RAY2hhbWJlcnNpZ24ub3JnMCcGA1Ud -EgQgMB6BHGNoYW1iZXJzcm9vdEBjaGFtYmVyc2lnbi5vcmcwWAYDVR0gBFEwTzBN -BgsrBgEEAYGHLgoDATA+MDwGCCsGAQUFBwIBFjBodHRwOi8vY3BzLmNoYW1iZXJz -aWduLm9yZy9jcHMvY2hhbWJlcnNyb290Lmh0bWwwDQYJKoZIhvcNAQEFBQADggEB -AAxBl8IahsAifJ/7kPMa0QOx7xP5IV8EnNrJpY0nbJaHkb5BkAFyk+cefV/2icZd -p0AJPaxJRUXcLo0waLIJuvvDL8y6C98/d3tGfToSJI6WjzwFCm/SlCgdbQzALogi -1djPHRPH8EjX1wWnz8dHnjs8NMiAT9QUu/wNUPf6s+xCX6ndbcj0dc97wXImsQEc -XCz9ek60AcUFV7nnPKoF2YjpB0ZBzu9Bga5Y34OirsrXdx/nADydb47kMgkdTXg0 -eDQ8lJsm7U9xxhl6vSAiSFr+S30Dt+dYvsYyTnQeaN2oaFuzPu5ifdmA6Ap1erfu -tGWaIZDgqtCYvDi1czyL+Nw= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIExTCCA62gAwIBAgIBADANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJFVTEn -MCUGA1UEChMeQUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQL -ExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEgMB4GA1UEAxMXR2xvYmFsIENo -YW1iZXJzaWduIFJvb3QwHhcNMDMwOTMwMTYxNDE4WhcNMzcwOTMwMTYxNDE4WjB9 -MQswCQYDVQQGEwJFVTEnMCUGA1UEChMeQUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgy -NzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEgMB4G -A1UEAxMXR2xvYmFsIENoYW1iZXJzaWduIFJvb3QwggEgMA0GCSqGSIb3DQEBAQUA -A4IBDQAwggEIAoIBAQCicKLQn0KuWxfH2H3PFIP8T8mhtxOviteePgQKkotgVvq0 -Mi+ITaFgCPS3CU6gSS9J1tPfnZdan5QEcOw/Wdm3zGaLmFIoCQLfxS+EjXqXd7/s -QJ0lcqu1PzKY+7e3/HKE5TWH+VX6ox8Oby4o3Wmg2UIQxvi1RMLQQ3/bvOSiPGpV -eAp3qdjqGTK3L/5cPxvusZjsyq16aUXjlg9V9ubtdepl6DJWk0aJqCWKZQbua795 -B9Dxt6/tLE2Su8CoX6dnfQTyFQhwrJLWfQTSM/tMtgsL+xrJxI0DqX5c8lCrEqWh -z0hQpe/SyBoT+rB/sYIcd2oPX9wLlY/vQ37mRQklAgEDo4IBUDCCAUwwEgYDVR0T -AQH/BAgwBgEB/wIBDDA/BgNVHR8EODA2MDSgMqAwhi5odHRwOi8vY3JsLmNoYW1i -ZXJzaWduLm9yZy9jaGFtYmVyc2lnbnJvb3QuY3JsMB0GA1UdDgQWBBRDnDafsJ4w -TcbOX60Qq+UDpfqpFDAOBgNVHQ8BAf8EBAMCAQYwEQYJYIZIAYb4QgEBBAQDAgAH -MCoGA1UdEQQjMCGBH2NoYW1iZXJzaWducm9vdEBjaGFtYmVyc2lnbi5vcmcwKgYD -VR0SBCMwIYEfY2hhbWJlcnNpZ25yb290QGNoYW1iZXJzaWduLm9yZzBbBgNVHSAE -VDBSMFAGCysGAQQBgYcuCgEBMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly9jcHMuY2hh -bWJlcnNpZ24ub3JnL2Nwcy9jaGFtYmVyc2lnbnJvb3QuaHRtbDANBgkqhkiG9w0B -AQUFAAOCAQEAPDtwkfkEVCeR4e3t/mh/YV3lQWVPMvEYBZRqHN4fcNs+ezICNLUM -bKGKfKX0j//U2K0X1S0E0T9YgOKBWYi+wONGkyT+kL0mojAt6JcmVzWJdJYY9hXi -ryQZVgICsroPFOrGimbBhkVVi76SvpykBMdJPJ7oKXqJ1/6v/2j1pReQvayZzKWG -VwlnRtvWFsJG8eSpUPWP0ZIV018+xgBJOm5YstHRJw0lyDL4IBHNfTIzSJRUTN3c -ecQwn+uOuFW114hcxWokPbLTBQNRxgfvzBRydD1ucs4YKIxKoHflCStFREest2d/ -AYoFWpO+ocH/+OcOZ6RHSXZddZAa9SaP8A== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDYTCCAkmgAwIBAgIQCgEBAQAAAnwAAAAKAAAAAjANBgkqhkiG9w0BAQUFADA6 -MRkwFwYDVQQKExBSU0EgU2VjdXJpdHkgSW5jMR0wGwYDVQQLExRSU0EgU2VjdXJp -dHkgMjA0OCBWMzAeFw0wMTAyMjIyMDM5MjNaFw0yNjAyMjIyMDM5MjNaMDoxGTAX -BgNVBAoTEFJTQSBTZWN1cml0eSBJbmMxHTAbBgNVBAsTFFJTQSBTZWN1cml0eSAy -MDQ4IFYzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAt49VcdKA3Xtp -eafwGFAyPGJn9gqVB93mG/Oe2dJBVGutn3y+Gc37RqtBaB4Y6lXIL5F4iSj7Jylg -/9+PjDvJSZu1pJTOAeo+tWN7fyb9Gd3AIb2E0S1PRsNO3Ng3OTsor8udGuorryGl -wSMiuLgbWhOHV4PR8CDn6E8jQrAApX2J6elhc5SYcSa8LWrg903w8bYqODGBDSnh -AMFRD0xS+ARaqn1y07iHKrtjEAMqs6FPDVpeRrc9DvV07Jmf+T0kgYim3WBU6JU2 -PcYJk5qjEoAAVZkZR73QpXzDuvsf9/UP+Ky5tfQ3mBMY3oVbtwyCO4dvlTlYMNpu -AWgXIszACwIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB -BjAfBgNVHSMEGDAWgBQHw1EwpKrpRa41JPr/JCwz0LGdjDAdBgNVHQ4EFgQUB8NR -MKSq6UWuNST6/yQsM9CxnYwwDQYJKoZIhvcNAQEFBQADggEBAF8+hnZuuDU8TjYc -HnmYv/3VEhF5Ug7uMYm83X/50cYVIeiKAVQNOvtUudZj1LGqlk2iQk3UUx+LEN5/ -Zb5gEydxiKRz44Rj0aRV4VCT5hsOedBnvEbIvz8XDZXmxpBp3ue0L96VfdASPz0+ -f00/FGj1EVDVwfSQpQgdMWD/YIwjVAqv/qFuxdF6Kmh4zx6CCiC0H63lhbJqaHVO -rSU3lIW+vaHU6rcMSzyd6BIA8F+sDeGscGNz9395nzIlQnQFgCi/vcEkllgVsRch -6YlL2weIZ/QVrXA+L02FO8K32/6YaCOJ4XQP3vTFhGMpG8zLB8kApKnXwiJPZ9d3 -7CAFYd4= ------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----- -MIIDfTCCAmWgAwIBAgIBADANBgkqhkiG9w0BAQUFADBgMQswCQYDVQQGEwJKUDEl -MCMGA1UEChMcU0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEqMCgGA1UECxMh -U2VjdXJpdHkgQ29tbXVuaWNhdGlvbiBFViBSb290Q0ExMB4XDTA3MDYwNjAyMTIz -MloXDTM3MDYwNjAyMTIzMlowYDELMAkGA1UEBhMCSlAxJTAjBgNVBAoTHFNFQ09N -IFRydXN0IFN5c3RlbXMgQ08uLExURC4xKjAoBgNVBAsTIVNlY3VyaXR5IENvbW11 -bmljYXRpb24gRVYgUm9vdENBMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC -ggEBALx/7FebJOD+nLpCeamIivqA4PUHKUPqjgo0No0c+qe1OXj/l3X3L+SqawSE -RMqm4miO/VVQYg+kcQ7OBzgtQoVQrTyWb4vVog7P3kmJPdZkLjjlHmy1V4qe70gO -zXppFodEtZDkBp2uoQSXWHnvIEqCa4wiv+wfD+mEce3xDuS4GBPMVjZd0ZoeUWs5 -bmB2iDQL87PRsJ3KYeJkHcFGB7hj3R4zZbOOCVVSPbW9/wfrrWFVGCypaZhKqkDF -MxRldAD5kd6vA0jFQFTcD4SQaCDFkpbcLuUCRarAX1T4bepJz11sS6/vmsJWXMY1 -VkJqMF/Cq/biPT+zyRGPMUzXn0kCAwEAAaNCMEAwHQYDVR0OBBYEFDVK9U2vP9eC -OKyrcWUXdYydVZPmMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0G -CSqGSIb3DQEBBQUAA4IBAQCoh+ns+EBnXcPBZsdAS5f8hxOQWsTvoMpfi7ent/HW -tWS3irO4G8za+6xmiEHO6Pzk2x6Ipu0nUBsCMCRGef4Eh3CXQHPRwMFXGZpppSeZ -q51ihPZRwSzJIxXYKLerJRO1RuGGAv8mjMSIkh1W/hln8lXkgKNrnKt34VFxDSDb -EJrbvXZ5B3eZKK2aXtqxT0QsNY6llsf9g/BYxnnWmHyojf6GPgcWkuF75x3sM3Z+ -Qi5KhfmRiWiEA4Glm5q+4zfFVKtWOxgtQaQM+ELbmaDgcm+7XeEWT1MKZPlO9L9O -VL14bIjqv5wTJMJwaaJ/D8g8rQjJsJhAoyrniIPtd490 ------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----- -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----- -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----- -MIIFfjCCA2agAwIBAgIJAKqIsFoLsXabMA0GCSqGSIb3DQEBCwUAMEwxCzAJBgNV -BAYTAkNIMRUwEwYDVQQKEwxTd2lzc1NpZ24gQUcxJjAkBgNVBAMTHVN3aXNzU2ln -biBTaWx2ZXIgUm9vdCBDQSAtIEczMB4XDTA5MDgwNDEzMTkxNFoXDTM3MDgwNDEz -MTkxNFowTDELMAkGA1UEBhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzEmMCQG -A1UEAxMdU3dpc3NTaWduIFNpbHZlciBSb290IENBIC0gRzMwggIiMA0GCSqGSIb3 -DQEBAQUAA4ICDwAwggIKAoICAQC+h5sF5nF8Um9t7Dep6bPczF9/01DqIZsE8D2/ -vo7JpRQWMhDPmfzscK1INmckDBcy1inlSjmxN+umeAxsbxnKTvdR2hro+iE4bJWc -L9aLzDsCm78mmxFFtrg0Wh2mVEhSyJ14cc5ISsyneIPcaKtmHncH0zYYCNfUbWD4 -8HnTMzYJkmO3BJr1p5baRa90GvyC46hbDjo/UleYfrycjMHAslrfxH7+DKZUdoN+ -ut3nKvRKNk+HZS6lujmNWWEp89OOJHCMU5sRpUcHsnUFXA2E2UTZzckmRFduAn2V -AdSrJIbuPXD7V/qwKRTQnfLFl8sJyvHyPefYS5bpiC+eR1GKVGWYSNIS5FR3DAfm -vluc8d0Dfo2E/L7JYtX8yTroibVfwgVSYfCcPuwuTYxykY7IQ8GiKF71gCTc4i+H -O1MA5cvwsnyNeRmgiM14+MWKWnflBqzdSt7mcG6+r771sasOCLDboD+Uxb4Subx7 -J3m1MildrsUgI5IDe1Q5sIkiVG0S48N46jpA/aSTrOktiDzbpkdmTN/YF+0W3hrW -10Fmvx2A8aTgZBEpXgwnBWLr5cQEYtHEnwxqVdZYOJxmD537q1SAmZzsSdaCn9pF -1j9TBgO3/R/shn104KS06DK2qgcj+O8kQZ5jMHj0VN2O8Fo4jhJ/eMdvAlYhM864 -uK1pVQIDAQABo2MwYTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAd -BgNVHQ4EFgQUoYxFkwoSYwunV18ySn3hIee3PmYwHwYDVR0jBBgwFoAUoYxFkwoS -YwunV18ySn3hIee3PmYwDQYJKoZIhvcNAQELBQADggIBAIeuYW1IOCrGHNxKLoR4 -ScAjKkW4NU3RBfq5BTPEZL3brVQWKrA+DVoo2qYagHMMxEFvr7g0tnfUW44dC4tG -kES1s+5JGInBSzSzhzV0op5FZ+1FcWa2uaElc9fCrIj70h2na9rAWubYWWQ0l2Ug -MTMDT86tCZ6u6cI+GHW0MyUSuwXsULpxQOK93ohGBSGEi6MrHuswMIm/EfVcRPiR -i0tZRQswDcoMT29jvgT+we3gh/7IzVa/5dyOetTWKU6A26ubP45lByL3RM2WHy3H -9Qm2mHD/ONxQFRGEO3+p8NgkVMgXjCsTSdaZf0XRD46/aXI3Uwf05q79Wz55uQbN -uIF4tE2g0DW65K7/00m8Ne1jxrP846thWgW2C+T/qSq+31ROwktcaNqjMqLJTVcY -UzRZPGaZ1zwCeKdMcdC/2/HEPOcB5gTyRPZIJjAzybEBGesC8cwh+joCMBedyF+A -P90lrAKb4xfevcqSFNJSgVPm6vwwZzKpYvaTFxUHMV4PG2n19Km3fC2z7YREMkco -BzuGaUWpxzaWkHJ02BKmcyPRTrm2ejrEKaFQBhG52fQmbmIIEiAW8AFXF9QFNmeX -61H5/zMkDAUPVr/vPRxSjoreaQ9aH/DVAzFEs5LG6nWorrvHYAOImP/HBIRSkIbh -tJOpUC/o69I2rDBgp9ADE7UK ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDIDCCAgigAwIBAgIBJDANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJGSTEP -MA0GA1UEChMGU29uZXJhMRkwFwYDVQQDExBTb25lcmEgQ2xhc3MxIENBMB4XDTAx -MDQwNjEwNDkxM1oXDTIxMDQwNjEwNDkxM1owOTELMAkGA1UEBhMCRkkxDzANBgNV -BAoTBlNvbmVyYTEZMBcGA1UEAxMQU29uZXJhIENsYXNzMSBDQTCCASIwDQYJKoZI -hvcNAQEBBQADggEPADCCAQoCggEBALWJHytPZwp5/8Ue+H887dF+2rDNbS82rDTG -29lkFwhjMDMiikzujrsPDUJVyZ0upe/3p4zDq7mXy47vPxVnqIJyY1MPQYx9EJUk -oVqlBvqSV536pQHydekfvFYmUk54GWVYVQNYwBSujHxVX3BbdyMGNpfzJLWaRpXk -3w0LBUXl0fIdgrvGE+D+qnr9aTCU89JFhfzyMlsy3uhsXR/LpCJ0sICOXZT3BgBL -qdReLjVQCfOAl/QMF6452F/NM8EcyonCIvdFEu1eEpOdY6uCLrnrQkFEy0oaAIIN -nvmLVz5MxxftLItyM19yejhW1ebZrgUaHXVFsculJRwSVzb9IjcCAwEAAaMzMDEw -DwYDVR0TAQH/BAUwAwEB/zARBgNVHQ4ECgQIR+IMi/ZTiFIwCwYDVR0PBAQDAgEG -MA0GCSqGSIb3DQEBBQUAA4IBAQCLGrLJXWG04bkruVPRsoWdd44W7hE928Jj2VuX -ZfsSZ9gqXLar5V7DtxYvyOirHYr9qxp81V9jz9yw3Xe5qObSIjiHBxTZ/75Wtf0H -DjxVyhbMp6Z3N/vbXB9OWQaHowND9Rart4S9Tu+fMTfwRvFAttEMpWT4Y14h21VO -TzF2nBBhjrZTOqMRvq9tfB69ri3iDGnHhVNoomG6xT60eVR4ngrHAr5i0RGCS2Uv -kVrCqIexVmiUefkl98HVrhq4uz2PqYo4Ffdz0Fpg0YCw8NzVUM1O7pJIae2yIx4w -zMiUyLb1O4Z/P6Yun/Y+LLWSlj7fLJOK/4GMDw9ZIRlXvVWa ------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----- -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----- -MIIFYzCCA0ugAwIBAgIBOzANBgkqhkiG9w0BAQsFADBTMQswCQYDVQQGEwJJTDEW -MBQGA1UEChMNU3RhcnRDb20gTHRkLjEsMCoGA1UEAxMjU3RhcnRDb20gQ2VydGlm -aWNhdGlvbiBBdXRob3JpdHkgRzIwHhcNMTAwMTAxMDEwMDAxWhcNMzkxMjMxMjM1 -OTAxWjBTMQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjEsMCoG -A1UEAxMjU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgRzIwggIiMA0G -CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2iTZbB7cgNr2Cu+EWIAOVeq8Oo1XJ -JZlKxdBWQYeQTSFgpBSHO839sj60ZwNq7eEPS8CRhXBF4EKe3ikj1AENoBB5uNsD -vfOpL9HG4A/LnooUCri99lZi8cVytjIl2bLzvWXFDSxu1ZJvGIsAQRSCb0AgJnoo -D/Uefyf3lLE3PbfHkffiAez9lInhzG7TNtYKGXmu1zSCZf98Qru23QumNK9LYP5/ -Q0kGi4xDuFby2X8hQxfqp0iVAXV16iulQ5XqFYSdCI0mblWbq9zSOdIxHWDirMxW -RST1HFSr7obdljKF+ExP6JV2tgXdNiNnvP8V4so75qbsO+wmETRIjfaAKxojAuuK -HDp2KntWFhxyKrOq42ClAJ8Em+JvHhRYW6Vsi1g8w7pOOlz34ZYrPu8HvKTlXcxN -nw3h3Kq74W4a7I/htkxNeXJdFzULHdfBR9qWJODQcqhaX2YtENwvKhOuJv4KHBnM -0D4LnMgJLvlblnpHnOl68wVQdJVznjAJ85eCXuaPOQgeWeU1FEIT/wCc976qUM/i -UUjXuG+v+E5+M5iSFGI6dWPPe/regjupuznixL0sAA7IF6wT700ljtizkC+p2il9 -Ha90OrInwMEePnWjFqmveiJdnxMaz6eg6+OGCtP95paV1yPIN93EfKo2rJgaErHg -TuixO/XWb/Ew1wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQE -AwIBBjAdBgNVHQ4EFgQUS8W0QGutHLOlHGVuRjaJhwUMDrYwDQYJKoZIhvcNAQEL -BQADggIBAHNXPyzVlTJ+N9uWkusZXn5T50HsEbZH77Xe7XRcxfGOSeD8bpkTzZ+K -2s06Ctg6Wgk/XzTQLwPSZh0avZyQN8gMjgdalEVGKua+etqhqaRpEpKwfTbURIfX -UfEpY9Z1zRbkJ4kd+MIySP3bmdCPX1R0zKxnNBFi2QwKN4fRoxdIjtIXHfbX/dtl -6/2o1PXWT6RbdejF0mCy2wl+JYt7ulKSnj7oxXehPOBKc2thz4bcQ///If4jXSRK -9dNtD2IEBVeC2m6kMyV5Sy5UGYvMLD0w6dEG/+gyRr61M3Z3qAFdlsHB1b6uJcDJ -HgoJIIihDsnzb02CVAAgp9KP5DlUFy6NHrgbuxu9mk47EDTcnIhT76IxW1hPkWLI -wpqazRVdOKnWvvgTtZ8SafJQYqz7Fzf07rh1Z2AQ+4NQ+US1dZxAF7L+/XldblhY -XzD8AK6vM8EOTmy6p6ahfzLbOOCxchcKK5HsamMm7YnUeMx0HgX4a/6ManY5Ka5l -IxKVCCIcl85bBu4M4ru8H0ST9tg4RQUh7eStqxK2A6RCLi3ECToDZ2mEmuFZkIoo -hdVddLHRDiBYmxOlsGOm7XtH/UVVMKTumtTm4ofvmMkyghEpIrwACjFeLQ/Ajulr -so8uBtjRkcfGEvRM/TAXw8HaOFvjqermobp573PYtlNXLfbQ4ddI ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -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 ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIFwTCCA6mgAwIBAgIITrIAZwwDXU8wDQYJKoZIhvcNAQEFBQAwSTELMAkGA1UE -BhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzEjMCEGA1UEAxMaU3dpc3NTaWdu -IFBsYXRpbnVtIENBIC0gRzIwHhcNMDYxMDI1MDgzNjAwWhcNMzYxMDI1MDgzNjAw -WjBJMQswCQYDVQQGEwJDSDEVMBMGA1UEChMMU3dpc3NTaWduIEFHMSMwIQYDVQQD -ExpTd2lzc1NpZ24gUGxhdGludW0gQ0EgLSBHMjCCAiIwDQYJKoZIhvcNAQEBBQAD -ggIPADCCAgoCggIBAMrfogLi2vj8Bxax3mCq3pZcZB/HL37PZ/pEQtZ2Y5Wu669y -IIpFR4ZieIbWIDkm9K6j/SPnpZy1IiEZtzeTIsBQnIJ71NUERFzLtMKfkr4k2Htn -IuJpX+UFeNSH2XFwMyVTtIc7KZAoNppVRDBopIOXfw0enHb/FZ1glwCNioUD7IC+ -6ixuEFGSzH7VozPY1kneWCqv9hbrS3uQMpe5up1Y8fhXSQQeol0GcN1x2/ndi5ob -jM89o03Oy3z2u5yg+gnOI2Ky6Q0f4nIoj5+saCB9bzuohTEJfwvH6GXp43gOCWcw -izSC+13gzJ2BbWLuCB4ELE6b7P6pT1/9aXjvCR+htL/68++QHkwFix7qepF6w9fl -+zC8bBsQWJj3Gl/QKTIDE0ZNYWqFTFJ0LwYfexHihJfGmfNtf9dng34TaNhxKFrY -zt3oEBSa/m0jh26OWnA81Y0JAKeqvLAxN23IhBQeW71FYyBrS3SMvds6DsHPWhaP -pZjydomyExI7C3d3rLvlPClKknLKYRorXkzig3R3+jVIeoVNjZpTxN94ypeRSCtF -KwH3HBqi7Ri6Cr2D+m+8jVeTO9TUps4e8aCxzqv9KyiaTxvXw3LbpMS/XUz13XuW -ae5ogObnmLo2t/5u7Su9IPhlGdpVCX4l3P5hYnL5fhgC72O00Puv5TtjjGePAgMB -AAGjgawwgakwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O -BBYEFFCvzAeHFUdvOMW0ZdHelarp35zMMB8GA1UdIwQYMBaAFFCvzAeHFUdvOMW0 -ZdHelarp35zMMEYGA1UdIAQ/MD0wOwYJYIV0AVkBAQEBMC4wLAYIKwYBBQUHAgEW -IGh0dHA6Ly9yZXBvc2l0b3J5LnN3aXNzc2lnbi5jb20vMA0GCSqGSIb3DQEBBQUA -A4ICAQAIhab1Fgz8RBrBY+D5VUYI/HAcQiiWjrfFwUF1TglxeeVtlspLpYhg0DB0 -uMoI3LQwnkAHFmtllXcBrqS3NQuB2nEVqXQXOHtYyvkv+8Bldo1bAbl93oI9ZLi+ -FHSjClTTLJUYFzX1UWs/j6KWYTl4a0vlpqD4U99REJNi54Av4tHgvI42Rncz7Lj7 -jposiU0xEQ8mngS7twSNC/K5/FqdOxa3L8iYq/6KUFkuozv8KV2LwUvJ4ooTHbG/ -u0IdUt1O2BReEMYxB+9xJ/cbOQncguqLs5WGXv312l0xpuAxtpTmREl0xRbl9x8D -YSjFyMsSoEJL+WuICI20MhjzdZ/EfwBPBZWcoxcCw7NTm6ogOSkrZvqdr16zktK1 -puEa+S1BaYEUtLS17Yk9zvupnTVCRLEcFHOBzyoBNZox1S2PbYTfgE1X4z/FhHXa -icYwu+uPyyIIoK6q8QNsOktNCaUOcsZWayFCTiMlFGiudgp8DAdwZPmaL/YFOSbG -DI8Zf0NebvRbFS/bYV3mZy8/CJT5YLSYMdp08YSTcU1f+2BY0fvEwW2JorsgH51x -kcsymxM9Pn2SUjWskpSi0xjCfMfqr3YFFt1nJ8J+HAciIfNAChs0B0QTwoRqjt8Z -Wr9/6x3iGjjRXK9HkmuAtTClyY3YqzGBH9/CZjfTk6mFhnll0g== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -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 ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIF2TCCA8GgAwIBAgIQHp4o6Ejy5e/DfEoeWhhntjANBgkqhkiG9w0BAQsFADBk -MQswCQYDVQQGEwJjaDERMA8GA1UEChMIU3dpc3Njb20xJTAjBgNVBAsTHERpZ2l0 -YWwgQ2VydGlmaWNhdGUgU2VydmljZXMxGzAZBgNVBAMTElN3aXNzY29tIFJvb3Qg -Q0EgMjAeFw0xMTA2MjQwODM4MTRaFw0zMTA2MjUwNzM4MTRaMGQxCzAJBgNVBAYT -AmNoMREwDwYDVQQKEwhTd2lzc2NvbTElMCMGA1UECxMcRGlnaXRhbCBDZXJ0aWZp -Y2F0ZSBTZXJ2aWNlczEbMBkGA1UEAxMSU3dpc3Njb20gUm9vdCBDQSAyMIICIjAN -BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAlUJOhJ1R5tMJ6HJaI2nbeHCOFvEr -jw0DzpPMLgAIe6szjPTpQOYXTKueuEcUMncy3SgM3hhLX3af+Dk7/E6J2HzFZ++r -0rk0X2s682Q2zsKwzxNoysjL67XiPS4h3+os1OD5cJZM/2pYmLcX5BtS5X4HAB1f -2uY+lQS3aYg5oUFgJWFLlTloYhyxCwWJwDaCFCE/rtuh/bxvHGCGtlOUSbkrRsVP -ACu/obvLP+DHVxxX6NZp+MEkUp2IVd3Chy50I9AU/SpHWrumnf2U5NGKpV+GY3aF -y6//SSj8gO1MedK75MDvAe5QQQg1I3ArqRa0jG6F6bYRzzHdUyYb3y1aSgJA/MTA -tukxGggo5WDDH8SQjhBiYEQN7Aq+VRhxLKX0srwVYv8c474d2h5Xszx+zYIdkeNL -6yxSNLCK/RJOlrDrcH+eOfdmQrGrrFLadkBXeyq96G4DsguAhYidDMfCd7Camlf0 -uPoTXGiTOmekl9AbmbeGMktg2M7v0Ax/lZ9vh0+Hio5fCHyqW/xavqGRn1V9TrAL -acywlKinh/LTSlDcX3KwFnUey7QYYpqwpzmqm59m2I2mbJYV4+by+PGDYmy7Velh -k6M99bFXi08jsJvllGov34zflVEpYKELKeRcVVi3qPyZ7iVNTA6z00yPhOgpD/0Q -VAKFyPnlw4vP5w8CAwEAAaOBhjCBgzAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0hBBYw -FDASBgdghXQBUwIBBgdghXQBUwIBMBIGA1UdEwEB/wQIMAYBAf8CAQcwHQYDVR0O -BBYEFE0mICKJS9PVpAqhb97iEoHF8TwuMB8GA1UdIwQYMBaAFE0mICKJS9PVpAqh -b97iEoHF8TwuMA0GCSqGSIb3DQEBCwUAA4ICAQAyCrKkG8t9voJXiblqf/P0wS4R -fbgZPnm3qKhyN2abGu2sEzsOv2LwnN+ee6FTSA5BesogpxcbtnjsQJHzQq0Qw1zv -/2BZf82Fo4s9SBwlAjxnffUy6S8w5X2lejjQ82YqZh6NM4OKb3xuqFp1mrjX2lhI -REeoTPpMSQpKwhI3qEAMw8jh0FcNlzKVxzqfl9NX+Ave5XLzo9v/tdhZsnPdTSpx -srpJ9csc1fV5yJmz/MFMdOO0vSk3FQQoHt5FRnDsr7p4DooqzgB53MBfGWcsa0vv -aGgLQ+OswWIJ76bdZWGgr4RVSJFSHMYlkSrQwSIjYVmvRRGFHQEkNI/Ps/8XciAT -woCqISxxOQ7Qj1zB09GOInJGTB2Wrk9xseEFKZZZ9LuedT3PDTcNYtsmjGOpI99n -Bjx8Oto0QuFmtEYE3saWmA9LSHokMnWRn6z3aOkquVVlzl1h0ydw2Df+n7mvoC5W -t6NlUe07qxS/TFED6F+KBZvuim6c779o+sjaC+NCydAXFJy3SuCvkychVSa1ZC+N -8f+mQAWFBVzKBxlcCxMoTFh/wqXvRdpg065lYZ1Tg3TCrvJcwhbtkj6EPnNgiLx2 -9CzP0H1907he0ZESEOnN3col49XtmS++dYFLJPlFRpTJKSFTnCZFqhMX5OfNeOI5 -wSsSnqaeG8XmDtkx2Q== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIF4DCCA8igAwIBAgIRAPL6ZOJ0Y9ON/RAdBB92ylgwDQYJKoZIhvcNAQELBQAw -ZzELMAkGA1UEBhMCY2gxETAPBgNVBAoTCFN3aXNzY29tMSUwIwYDVQQLExxEaWdp -dGFsIENlcnRpZmljYXRlIFNlcnZpY2VzMR4wHAYDVQQDExVTd2lzc2NvbSBSb290 -IEVWIENBIDIwHhcNMTEwNjI0MDk0NTA4WhcNMzEwNjI1MDg0NTA4WjBnMQswCQYD -VQQGEwJjaDERMA8GA1UEChMIU3dpc3Njb20xJTAjBgNVBAsTHERpZ2l0YWwgQ2Vy -dGlmaWNhdGUgU2VydmljZXMxHjAcBgNVBAMTFVN3aXNzY29tIFJvb3QgRVYgQ0Eg -MjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMT3HS9X6lds93BdY7Bx -UglgRCgzo3pOCvrY6myLURYaVa5UJsTMRQdBTxB5f3HSek4/OE6zAMaVylvNwSqD -1ycfMQ4jFrclyxy0uYAyXhqdk/HoPGAsp15XGVhRXrwsVgu42O+LgrQ8uMIkqBPH -oCE2G3pXKSinLr9xJZDzRINpUKTk4RtiGZQJo/PDvO/0vezbE53PnUgJUmfANykR -HvvSEaeFGHR55E+FFOtSN+KxRdjMDUN/rhPSays/p8LiqG12W0OfvrSdsyaGOx9/ -5fLoZigWJdBLlzin5M8J0TbDC77aO0RYjb7xnglrPvMyxyuHxuxenPaHZa0zKcQv -idm5y8kDnftslFGXEBuGCxobP/YCfnvUxVFkKJ3106yDgYjTdLRZncHrYTNaRdHL -OdAGalNgHa/2+2m8atwBz735j9m9W8E6X47aD0upm50qKGsaCnw8qyIL5XctcfaC -NYGu+HuB5ur+rPQam3Rc6I8k9l2dRsQs0h4rIWqDJ2dVSqTjyDKXZpBy2uPUZC5f -46Fq9mDU5zXNysRojddxyNMkM3OxbPlq4SjbX8Y96L5V5jcb7STZDxmPX2MYWFCB -UWVv8p9+agTnNCRxunZLWB4ZvRVgRaoMEkABnRDixzgHcgplwLa7JSnaFp6LNYth -7eVxV4O1PHGf40+/fh6Bn0GXAgMBAAGjgYYwgYMwDgYDVR0PAQH/BAQDAgGGMB0G -A1UdIQQWMBQwEgYHYIV0AVMCAgYHYIV0AVMCAjASBgNVHRMBAf8ECDAGAQH/AgED -MB0GA1UdDgQWBBRF2aWBbj2ITY1x0kbBbkUe88SAnTAfBgNVHSMEGDAWgBRF2aWB -bj2ITY1x0kbBbkUe88SAnTANBgkqhkiG9w0BAQsFAAOCAgEAlDpzBp9SSzBc1P6x -XCX5145v9Ydkn+0UjrgEjihLj6p7jjm02Vj2e6E1CqGdivdj5eu9OYLU43otb98T -PLr+flaYC/NUn81ETm484T4VvwYmneTwkLbUwp4wLh/vx3rEUMfqe9pQy3omywC0 -Wqu1kx+AiYQElY2NfwmTv9SoqORjbdlk5LgpWgi/UOGED1V7XwgiG/W9mR4U9s70 -WBCCswo9GcG/W6uqmdjyMb3lOGbcWAXH7WMaLgqXfIeTK7KK4/HsGOV1timH59yL -Gn602MnTihdsfSlEvoqq9X46Lmgxk7lq2prg2+kupYTNHAq4Sgj5nPFhJpiTt3tm -7JFe3VE/23MPrQRYCd0EApUKPtN236YQHoA96M2kZNEzx5LH4k5E4wnJTsJdhw4S -nr8PyQUQ3nqjsTzyP6WqJ3mtMX0f/fwZacXduT98zca0wjAefm6S139hdlqP65VN -vBFuIXxZN5nQBrz5Bm0yFqXZaajh3DyAHmBR3NdUIR7KYndP+tiPsys6DXhyyWhB -WkdKwqPrGtcKqzwyVcgKEZzfdNbwQBUdyLmPtTbFr/giuMod89a2GQ+fYWVq6nTI -fI/DT11lgh/ZDYnadXL77/FHZxOzyNEZiCcmmpl5fx7kLD977vHeTYuWl8PVP3wb -I+2ksx0WckNLIOFZfsLorSa/ovc= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIICqDCCAi2gAwIBAgIQIW4zpcvTiKRvKQe0JzzE2DAKBggqhkjOPQQDAzCBlDEL -MAkGA1UEBhMCVVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMR8wHQYD -VQQLExZTeW1hbnRlYyBUcnVzdCBOZXR3b3JrMUUwQwYDVQQDEzxTeW1hbnRlYyBD -bGFzcyAxIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0g -RzQwHhcNMTExMDA1MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBlDELMAkGA1UEBhMC -VVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMR8wHQYDVQQLExZTeW1h -bnRlYyBUcnVzdCBOZXR3b3JrMUUwQwYDVQQDEzxTeW1hbnRlYyBDbGFzcyAxIFB1 -YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzQwdjAQBgcq -hkjOPQIBBgUrgQQAIgNiAATXZrUb266zYO5G6ohjdTsqlG3zXxL24w+etgoUU0hS -yNw6s8tIICYSTvqJhNTfkeQpfSgB2dsYQ2mhH7XThhbcx39nI9/fMTGDAzVwsUu3 -yBe7UcvclBfb6gk7dhLeqrWjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E -BTADAQH/MB0GA1UdDgQWBBRlwI0l9Qy6l3eQP54u4Fr1ztXh5DAKBggqhkjOPQQD -AwNpADBmAjEApa7jRlP4mDbjIvouKEkN7jB+M/PsP3FezFWJeJmssv3cHFwzjim5 -axfIEWi13IMHAjEAnMhE2mnCNsNUGRCFAtqdR+9B52wmnQk9922Q0QVEL7C8g5No -8gxFSTm/mQQc0xCg ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIID9jCCAt6gAwIBAgIQJDJ18h0v0gkz97RqytDzmDANBgkqhkiG9w0BAQsFADCB -lDELMAkGA1UEBhMCVVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMR8w -HQYDVQQLExZTeW1hbnRlYyBUcnVzdCBOZXR3b3JrMUUwQwYDVQQDEzxTeW1hbnRl -YyBDbGFzcyAxIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5 -IC0gRzYwHhcNMTExMDE4MDAwMDAwWhcNMzcxMjAxMjM1OTU5WjCBlDELMAkGA1UE -BhMCVVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMR8wHQYDVQQLExZT -eW1hbnRlYyBUcnVzdCBOZXR3b3JrMUUwQwYDVQQDEzxTeW1hbnRlYyBDbGFzcyAx -IFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzYwggEi -MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDHOddJZKmZgiJM6kXZBxbje/SD -6Jlz+muxNuCad6BAwoGNAcfMjL2Pffd543pMA03Z+/2HOCgs3ZqLVAjbZ/sbjP4o -ki++t7JIp4Gh2F6Iw8w5QEFa0dzl2hCfL9oBTf0uRnz5LicKaTfukaMbasxEvxvH -w9QRslBglwm9LiL1QYRmn81ApqkAgMEflZKf3vNI79sdd2H8f9/ulqRy0LY+/3gn -r8uSFWkI22MQ4uaXrG7crPaizh5HmbmJtxLmodTNWRFnw2+F2EJOKL5ZVVkElauP -N4C/DfD8HzpkMViBeNfiNfYgPym4jxZuPkjctUwH4fIa6n4KedaovetdhitNAgMB -AAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW -BBQzQejIORIVk0jyljIuWvXalF9TYDANBgkqhkiG9w0BAQsFAAOCAQEAFeNzV7EX -tl9JaUSm9l56Z6zS3nVJq/4lVcc6yUQVEG6/MWvL2QeTfxyFYwDjMhLgzMv7OWyP -4lPiPEAz2aSMR+atWPuJr+PehilWNCxFuBL6RIluLRQlKCQBZdbqUqwFblYSCT3Q -dPTXvQbKqDqNVkL6jXI+dPEDct+HG14OelWWLDi3mIXNTTNEyZSPWjEwN0ujOhKz -5zbRIWhLLTjmU64cJVYIVgNnhJ3Gw84kYsdMNs+wBkS39V8C3dlU6S+QTnrIToNA -DJqXPDe/v+z28LSFdyjBC8hnghAXOKK3Buqbvzr46SMHv3TgmDgVVXjucgBcGaP0 -0jPg/73RVDkpDw== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIICqDCCAi2gAwIBAgIQNBdlEkA7t1aALYDLeVWmHjAKBggqhkjOPQQDAzCBlDEL -MAkGA1UEBhMCVVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMR8wHQYD -VQQLExZTeW1hbnRlYyBUcnVzdCBOZXR3b3JrMUUwQwYDVQQDEzxTeW1hbnRlYyBD -bGFzcyAyIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0g -RzQwHhcNMTExMDA1MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBlDELMAkGA1UEBhMC -VVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMR8wHQYDVQQLExZTeW1h -bnRlYyBUcnVzdCBOZXR3b3JrMUUwQwYDVQQDEzxTeW1hbnRlYyBDbGFzcyAyIFB1 -YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzQwdjAQBgcq -hkjOPQIBBgUrgQQAIgNiAATR2UqOTA2ESlG6fO/TzPo6mrWnYxM9AeBJPvrBR8mS -szrX/m+c95o6D/UOCgrDP8jnEhSO1dVtmCyzcTIK6yq99tdqIAtnRZzSsr9TImYJ -XdsR8/EFM1ij4rjPfM2Cm72jQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E -BTADAQH/MB0GA1UdDgQWBBQ9MvM6qQyQhPmijGkGYVQvh3L+BTAKBggqhkjOPQQD -AwNpADBmAjEAyKapr0F/tckRQhZoaUxcuCcYtpjxwH+QbYfTjEYX8D5P/OqwCMR6 -S7wIL8fip29lAjEA1lnehs5fDspU1cbQFQ78i5Ry1I4AWFPPfrFLDeVQhuuea9// -KabYR9mglhjb8kWz ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIID9jCCAt6gAwIBAgIQZIKe/DcedF38l/+XyLH/QTANBgkqhkiG9w0BAQsFADCB -lDELMAkGA1UEBhMCVVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMR8w -HQYDVQQLExZTeW1hbnRlYyBUcnVzdCBOZXR3b3JrMUUwQwYDVQQDEzxTeW1hbnRl -YyBDbGFzcyAyIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5 -IC0gRzYwHhcNMTExMDE4MDAwMDAwWhcNMzcxMjAxMjM1OTU5WjCBlDELMAkGA1UE -BhMCVVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMR8wHQYDVQQLExZT -eW1hbnRlYyBUcnVzdCBOZXR3b3JrMUUwQwYDVQQDEzxTeW1hbnRlYyBDbGFzcyAy -IFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzYwggEi -MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDNzOkFyGOFyz9AYxe9GPo15gRn -V2WYKaRPyVyPDzTS+NqoE2KquB5QZ3iwFkygOakVeq7t0qLA8JA3KRgmXOgNPLZs -ST/B4NzZS7YUGQum05bh1gnjGSYc+R9lS/kaQxwAg9bQqkmi1NvmYji6UBRDbfkx -+FYW2TgCkc/rbN27OU6Z4TBnRfHU8I3D3/7yOAchfQBeVkSz5GC9kSucq1sEcg+y -KNlyqwUgQiWpWwNqIBDMMfAr2jUs0Pual07wgksr2F82owstr2MNHSV/oW5cYqGN -KD6h/Bwg+AEvulWaEbAZ0shQeWsOagXXqgQ2sqPy4V93p3ec5R7c6d9qwWVdAgMB -AAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW -BBSHjCCVyJhK0daABkqQNETfHE2/sDANBgkqhkiG9w0BAQsFAAOCAQEAgY6ypWaW -tyGltu9vI1pf24HFQqV4wWn99DzX+VxrcHIa/FqXTQCAiIiCisNxDY7FiZss7Y0L -0nJU9X3UXENX6fOupQIR9nYrgVfdfdp0MP1UR/bgFm6mtApI5ud1Bw8pGTnOefS2 -bMVfmdUfS/rfbSw8DVSAcPCIC4DPxmiiuB1w2XaM/O6lyc+tHc+ZJVdaYkXLFmu9 -Sc2lo4xpeSWuuExsi0BmSxY/zwIa3eFsawdhanYVKZl/G92IgMG/tY9zxaaWI4Sm -KIYkM2oBLldzJbZev4/mHWGoQClnHYebHX+bn5nNMdZUvmK7OaxoEkiRIKXLsd3+ -b/xa5IJVWa8xqQ== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIICpzCCAi2gAwIBAgIQTHm1miicdjFk9YlE0JEC3jAKBggqhkjOPQQDAzCBlDEL -MAkGA1UEBhMCVVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMR8wHQYD -VQQLExZTeW1hbnRlYyBUcnVzdCBOZXR3b3JrMUUwQwYDVQQDEzxTeW1hbnRlYyBD -bGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0g -RzQwHhcNMTIxMDE4MDAwMDAwWhcNMzcxMjAxMjM1OTU5WjCBlDELMAkGA1UEBhMC -VVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMR8wHQYDVQQLExZTeW1h -bnRlYyBUcnVzdCBOZXR3b3JrMUUwQwYDVQQDEzxTeW1hbnRlYyBDbGFzcyAzIFB1 -YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzQwdjAQBgcq -hkjOPQIBBgUrgQQAIgNiAARXz+qzOU0/oSHgbi84csaHl/OFC0fnD1HI0fSZm8pZ -Zf9M+eoLtyXV0vbsMS0yYhLXdoan+jjJZdT+c+KEOfhMSWIT3brViKBfPchPsD+P -oVAR5JNGrcNfy/GkapVW6MCjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E -BTADAQH/MB0GA1UdDgQWBBQknbzScfcdwiW+IvGJpSwVOzQeXjAKBggqhkjOPQQD -AwNoADBlAjEAuWZoZdsF0Dh9DvPIdWG40CjEsUozUVj78jwQyK5HeHbKZiQXhj5Q -Vm6lLZmIuL0kAjAD6qfnqDzqnWLGX1TamPR3vU+PGJyRXEdrQE0QHbPhicoLIsga -xcX+i93B3294n5E= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIF9jCCA96gAwIBAgIQZWNxhdNvRcaPfzH5CYeSgjANBgkqhkiG9w0BAQwFADCB -lDELMAkGA1UEBhMCVVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMR8w -HQYDVQQLExZTeW1hbnRlYyBUcnVzdCBOZXR3b3JrMUUwQwYDVQQDEzxTeW1hbnRl -YyBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5 -IC0gRzYwHhcNMTIxMDE4MDAwMDAwWhcNMzcxMjAxMjM1OTU5WjCBlDELMAkGA1UE -BhMCVVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMR8wHQYDVQQLExZT -eW1hbnRlYyBUcnVzdCBOZXR3b3JrMUUwQwYDVQQDEzxTeW1hbnRlYyBDbGFzcyAz -IFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzYwggIi -MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC3DrL6TbyachX7d1vb/UMPywv3 -YC6zK34Mu1PyzE5l8xm7/zUd99Opu0Attd141Kb5N+qFBXttt+YTSwZ8+3ZjjyAd -LTgrBIXy6LDRX01KIclq2JTqHgJQpqqQB6BHIepm+QSg5oPwxPVeluInTWHDs8GM -IrZmoQDRVin77cF/JMo9+lqUsITDx7pDHP1kDvEo+0dZ8ibhMblE+avd+76+LDfj -rAsY0/wBovGkCjWCR0yrvYpe3xOF/CDMSFmvr0FvyyPNypOn3dVfyGQ7/wEDoApP -LW49hL6vyDKyUymQFfewBZoKPPa5BpDJpeFdoDuw/qi2v/WJKFckOiGGceTciotB -VeweMCRZ0cBZuHivqlp03iWAMJjtMERvIXAc2xJTDtamKGaTLB/MTzwbgcW59nhv -0DI6CHLbaw5GF4WU87zvvPekXo7p6bVk5bdLRRIsTDe3YEMKTXEGAJQmNXQfu3o5 -XE475rgD4seTi4QsJUlF3X8jlGAfy+nN9quX92Hn+39igcjcCjBcGHzmzu/Hbh6H -fLPpysh7avRo/IOlDFa0urKNSgrHl5fFiDAVPRAIVBVycmczM/R8t84AJ1NlziTx -WmTnNi/yLgLCl99y6AIeoPc9tftoYAP6M6nmEm0G4amoXU48/tnnAGWsthlNe4N/ -NEfq4RhtsYsceavnnQIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ -BAUwAwEB/zAdBgNVHQ4EFgQUOXEIAD7eyIbnkP/k/SEPziQZFvYwDQYJKoZIhvcN -AQEMBQADggIBAFBriE1gSM5a4yLOZ3yEp80c/ekMA4w2rwqHDmquV64B0Da78v25 -c8FftaiuTKL6ScsHRhY2vePIVzh+OOS/JTNgxtw3nGO7XpgeGrKC8K6mdxGAREeh -KcXwszrOmPC47NMOgAZ3IzBM/3lkYyJbd5NDS3Wz2ztuO0rd8ciutTeKlYg6EGhw -OLlbcH7VQ8n8X0/l5ns27vAg7UdXEyYQXhQGDXt2B8LGLRb0rqdsD7yID08sAraj -1yLmmUc12I2lT4ESOhF9s8wLdfMecKMbA+r6mujmLjY5zJnOOj8Mt674Q5mwk25v -qtkPajGRu5zTtCj7g0x6c4JQZ9IOrO1gxbJdNZjPh34eWR0kvFa62qRa2MzmvB4Q -jxuMjvPB27e+1LBbZY8WaPNWxSoZFk0PuGWHbSSDuGLc4EdhGoh7zk5//dzGDVqa -pPO1TPbdMaboHREhMzAEYX0c4D5PjT+1ixIAWn2poQDUg+twuxj4pNIcgS23CBHI -Jnu21OUPA0Zy1CVAHr5JXW2T8VyyO3VUaTqg7kwiuqya4gitRWMFSlI1dsQ09V4H -Mq3cfCbRW4+t5OaqG3Wf61206MCpFXxOSgdy30bJ1JGSdVaw4e43NmUoxRXIK3bM -bW8Zg/T92hXiQeczeUaDV/nxpbZt07zXU+fucW14qZen7iCcGRVyFT0E ------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----- -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 ------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----- -MIIFSzCCAzOgAwIBAgIRALZLiAfiI+7IXBKtpg4GofIwDQYJKoZIhvcNAQELBQAw -PzELMAkGA1UEBhMCVFcxMDAuBgNVBAoMJ0dvdmVybm1lbnQgUm9vdCBDZXJ0aWZp -Y2F0aW9uIEF1dGhvcml0eTAeFw0xMjA5MjgwODU4NTFaFw0zNzEyMzExNTU5NTla -MD8xCzAJBgNVBAYTAlRXMTAwLgYDVQQKDCdHb3Zlcm5tZW50IFJvb3QgQ2VydGlm -aWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC -AQC2/5c8gb4BWCQnr44BK9ZykjAyG1+bfNTUf+ihYHMwVxAA+lCWJP5Q5ow6ldFX -eYTVZ1MMKoI+GFy4MCYa1l7GLbIEUQ7v3wxjR+vEEghRK5lxXtVpe+FdyXcdIOxW -juVhYC386RyA3/pqg7sFtR4jEpyCygrzFB0g5AaPQySZn7YKk1pzGxY5vgW28Yyl -ZJKPBeRcdvc5w88tvQ7Yy6gOMZvJRg9nU0MEj8iyyIOAX7ryD6uBNaIgIZfOD4k0 -eA/PH07p+4woPN405+2f0mb1xcoxeNLOUNFggmOd4Ez3B66DNJ1JSUPUfr0t4urH -cWWACOQ2nnlwCjyHKenkkpTqBpIpJ3jmrdc96QoLXvTg1oadLXLLi2RW5vSueKWg -OTNYPNyoj420ai39iHPplVBzBN8RiD5C1gJ0+yzEb7xs1uCAb9GGpTJXA9ZN9E4K -mSJ2fkpAgvjJ5E7LUy3Hsbbi08J1J265DnGyNPy/HE7CPfg26QrMWJqhGIZO4uGq -s3NZbl6dtMIIr69c/aQCb/+4DbvVq9dunxpPkUDwH0ZVbaCSw4nNt7H/HLPLo5wK -4/7NqrwB7N1UypHdTxOHpPaY7/1J1lcqPKZc9mA3v9g+fk5oKiMyOr5u5CI9ByTP -isubXVGzMNJxbc5Gim18SjNE2hIvNkvy6fFRCW3bapcOFwIDAQABo0IwQDAPBgNV -HRMBAf8EBTADAQH/MB0GA1UdDgQWBBTVZx3gnHosnMvFmOcdByYqhux0zTAOBgNV -HQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQELBQADggIBAJA75cJTQijq9TFOjj2Rnk0J -89ixUuZPrAwxIbvx6pnMg/y2KOTshAcOD06Xu29oRo8OURWV+Do7H1+CDgxxDryR -T64zLiNB9CZrTxOH+nj2LsIPkQWXqmrBap+8hJ4IKifd2ocXhuGzyl3tOKkpboTe -Rmv8JxlQpRJ6jH1i/NrnzLyfSa8GuCcn8on3Fj0Y5r3e9YwSkZ/jBI3+BxQaWqw5 -ghvxOBnhY+OvbLamURfr+kvriyL2l/4QOl+UoEtTcT9a4RD4co+WgN2NApgAYT2N -vC2xR8zaXeEgp4wxXPHj2rkKhkfIoT0Hozymc26Uke1uJDr5yTDRB6iBfSZ9fYTf -hsmL5a4NHr6JSFEVg5iWL0rrczTXdM3Jb9DCuiv2mv6Z3WAUjhv5nDk8f0OJU+jl -wqu+Iq0nOJt3KLejY2OngeepaUXrjnhWzAWEx/uttjB8YwWfLYwkf0uLkvw4Hp+g -pVezbp3YZLhwmmBScMip0P/GnO0QYV7Ngw5u6E0CQUridgR51lQ/ipgyFKDdLZzn -uoJxo4ZVKZnSKdt1OvfbQ/+2W/u3fjWAjg1srnm3Ni2XUqGwB5wH5Ss2zQOXlL0t -DjQG/MAWifw3VOTWzz0TBPKR2ck2Lj7FWtClTILD/y58Jnb38/1FoqVuVa4uzM8s -iTTa9g3nkagQ6hed8vbs ------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----- -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----- -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----- -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----- -MIIFFzCCA/+gAwIBAgIBETANBgkqhkiG9w0BAQUFADCCASsxCzAJBgNVBAYTAlRS -MRgwFgYDVQQHDA9HZWJ6ZSAtIEtvY2FlbGkxRzBFBgNVBAoMPlTDvHJraXllIEJp -bGltc2VsIHZlIFRla25vbG9qaWsgQXJhxZ90xLFybWEgS3VydW11IC0gVMOcQsSw -VEFLMUgwRgYDVQQLDD9VbHVzYWwgRWxla3Ryb25payB2ZSBLcmlwdG9sb2ppIEFy -YcWfdMSxcm1hIEVuc3RpdMO8c8O8IC0gVUVLQUUxIzAhBgNVBAsMGkthbXUgU2Vy -dGlmaWthc3lvbiBNZXJrZXppMUowSAYDVQQDDEFUw5xCxLBUQUsgVUVLQUUgS8O2 -ayBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsSAtIFPDvHLDvG0gMzAe -Fw0wNzA4MjQxMTM3MDdaFw0xNzA4MjExMTM3MDdaMIIBKzELMAkGA1UEBhMCVFIx -GDAWBgNVBAcMD0dlYnplIC0gS29jYWVsaTFHMEUGA1UECgw+VMO8cmtpeWUgQmls -aW1zZWwgdmUgVGVrbm9sb2ppayBBcmHFn3TEsXJtYSBLdXJ1bXUgLSBUw5xCxLBU -QUsxSDBGBgNVBAsMP1VsdXNhbCBFbGVrdHJvbmlrIHZlIEtyaXB0b2xvamkgQXJh -xZ90xLFybWEgRW5zdGl0w7xzw7wgLSBVRUtBRTEjMCEGA1UECwwaS2FtdSBTZXJ0 -aWZpa2FzeW9uIE1lcmtlemkxSjBIBgNVBAMMQVTDnELEsFRBSyBVRUtBRSBLw7Zr -IFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxIC0gU8O8csO8bSAzMIIB -IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAim1L/xCIOsP2fpTo6iBkcK4h -gb46ezzb8R1Sf1n68yJMlaCQvEhOEav7t7WNeoMojCZG2E6VQIdhn8WebYGHV2yK -O7Rm6sxA/OOqbLLLAdsyv9Lrhc+hDVXDWzhXcLh1xnnRFDDtG1hba+818qEhTsXO -fJlfbLm4IpNQp81McGq+agV/E5wrHur+R84EpW+sky58K5+eeROR6Oqeyjh1jmKw -lZMq5d/pXpduIF9fhHpEORlAHLpVK/swsoHvhOPc7Jg4OQOFCKlUAwUp8MmPi+oL -hmUZEdPpCSPeaJMDyTYcIW7OjGbxmTDY17PDHfiBLqi9ggtm/oLL4eAagsNAgQID -AQABo0IwQDAdBgNVHQ4EFgQUvYiHyY/2pAoLquvF/pEjnatKijIwDgYDVR0PAQH/ -BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAB18+kmP -NOm3JpIWmgV050vQbTlswyb2zrgxvMTfvCr4N5EY3ATIZJkrGG2AA1nJrvhY0D7t -wyOfaTyGOBye79oneNGEN3GKPEs5z35FBtYt2IpNeBLWrcLTy9LQQfMmNkqblWwM -7uXRQydmwYj3erMgbOqwaSvHIOgMA8RBBZniP+Rr+KCGgceExh/VS4ESshYhLBOh -gLJeDEoTniDYYkCrkOpkSi+sDQESeUWoL4cZaMjihccwsnX5OD+ywJO0a+IDRM5n -oN+J1q2MdqMTw5RhK2vZbMEHCiIHhWyFJEapvj+LeISCfiQMnf2BN+MlqO02TpUs -yZyQ2uypQjyttgI= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIFkjCCA3qgAwIBAgIBCDANBgkqhkiG9w0BAQUFADA6MQswCQYDVQQGEwJDTjER -MA8GA1UEChMIVW5pVHJ1c3QxGDAWBgNVBAMTD1VDQSBHbG9iYWwgUm9vdDAeFw0w -ODAxMDEwMDAwMDBaFw0zNzEyMzEwMDAwMDBaMDoxCzAJBgNVBAYTAkNOMREwDwYD -VQQKEwhVbmlUcnVzdDEYMBYGA1UEAxMPVUNBIEdsb2JhbCBSb290MIICIjANBgkq -hkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA2rPlBlA/9nP3xDK/RqUlYjOHsGj+p9+I -A2N9Apb964fJ7uIIu527u+RBj8cwiQ9tJMAEbBSUgU2gDXRm8/CFr/hkGd656YGT -0CiFmUdCSiw8OCdKzP/5bBnXtfPvm65bNAbXj6ITBpyKhELVs6OQaG2BkO5NhOxM -cE4t3iQ5zhkAQ5N4+QiGHUPR9HK8BcBn+sBR0smFBySuOR56zUHSNqth6iur8CBV -mTxtLRwuLnWW2HKX4AzKaXPudSsVCeCObbvaE/9GqOgADKwHLx25urnRoPeZnnRc -GQVmMc8+KlL+b5/zub35wYH1N9ouTIElXfbZlJrTNYsgKDdfUet9Ysepk9H50DTL -qScmLCiQkjtVY7cXDlRzq6987DqrcDOsIfsiJrOGrCOp139tywgg8q9A9f9ER3Hd -J90TKKHqdjn5EKCgTUCkJ7JZFStsLSS3JGN490MYeg9NEePorIdCjedYcaSrbqLA -l3y74xNLytu7awj5abQEctXDRrl36v+6++nwOgw19o8PrgaEFt2UVdTvyie3AzzF -HCYq9TyopZWbhvGKiWf4xwxmse1Bv4KmAGg6IjTuHuvlb4l0T2qqaqhXZ1LUIGHB -zlPL/SR/XybfoQhplqCe/klD4tPq2sTxiDEhbhzhzfN1DiBEFsx9c3Q1RSw7gdQg -7LYJjD5IskkCAwEAAaOBojCBnzALBgNVHQ8EBAMCAQYwDAYDVR0TBAUwAwEB/zBj -BgNVHSUEXDBaBggrBgEFBQcDAQYIKwYBBQUHAwIGCCsGAQUFBwMDBggrBgEFBQcD -BAYIKwYBBQUHAwUGCCsGAQUFBwMGBggrBgEFBQcDBwYIKwYBBQUHAwgGCCsGAQUF -BwMJMB0GA1UdDgQWBBTZw9P4gJJnzF3SOqLXcaK0xDiALTANBgkqhkiG9w0BAQUF -AAOCAgEA0Ih5ygiq9ws0oE4Jwul+NUiJcIQjL1HDKy9e21NrW3UIKlS6Mg7VxnGF -sZdJgPaE0PC6t3GUyHlrpsVE6EKirSUtVy/m1jEp+hmJVCl+t35HNmktbjK81HXa -QnO4TuWDQHOyXd/URHOmYgvbqm4FjMh/Rk85hZCdvBtUKayl1/7lWFZXbSyZoUkh -1WHGjGHhdSTBAd0tGzbDLxLMC9Z4i3WA6UG5iLHKPKkWxk4V43I29tSgQYWvimVw -TbVEEFDs7d9t5tnGwBLxSzovc+k8qe4bqi81pZufTcU0hF8mFGmzI7GJchT46U1R -IgP/SobEHOh7eQrbRyWBfvw0hKxZuFhD5D1DCVR0wtD92e9uWfdyYJl2b/Unp7uD -pEqB7CmB9HdL4UISVdSGKhK28FWbAS7d9qjjGcPORy/AeGEYWsdl/J1GW1fcfA67 -loMQfFUYCQSu0feLKj6g5lDWMDbX54s4U+xJRODPpN/xU3uLWrb2EZBL1nXz/gLz -Ka/wI3J9FO2pXd96gZ6bkiL8HvgBRUGXx2sBYb4zaPKgZYRmvOAqpGjTcezHCN6j -w8k2SjTxF+KAryAhk5Qe5hXTVGLxtTgv48y5ZwSpuuXu+RBuyy5+E6+SFP7zJ3N7 -OPxzbbm5iPZujAv1/P8JDrMtXnt145Ik4ubhWD5LKAN1axibRww= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDhDCCAmygAwIBAgIBCTANBgkqhkiG9w0BAQUFADAzMQswCQYDVQQGEwJDTjER -MA8GA1UEChMIVW5pVHJ1c3QxETAPBgNVBAMTCFVDQSBSb290MB4XDTA0MDEwMTAw -MDAwMFoXDTI5MTIzMTAwMDAwMFowMzELMAkGA1UEBhMCQ04xETAPBgNVBAoTCFVu -aVRydXN0MREwDwYDVQQDEwhVQ0EgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEP -ADCCAQoCggEBALNdB8qGJn1r4vs4CQ7MgsJqGgCiFV/W6dQBt1YDAVmP9ThpJHbC -XivF9iu/r/tB/Q9a/KvXg3BNMJjRnrJ2u5LWu+kQKGkoNkTo8SzXWHwk1n8COvCB -a2FgP/Qz3m3l6ihST/ypHWN8C7rqrsRoRuTej8GnsrZYWm0dLNmMOreIy4XU9+gD -Xv2yTVDo1h//rgI/i0+WITyb1yXJHT/7mLFZ5PCpO6+zzYUs4mBGzG+OoOvwNMXx -QhhgrhLtRnUc5dipllq+3lrWeGeWW5N3UPJuG96WUUqm1ktDdSFmjXfsAoR2XEQQ -th1hbOSjIH23jboPkXXHjd+8AmCoKai9PUMCAwEAAaOBojCBnzALBgNVHQ8EBAMC -AQYwDAYDVR0TBAUwAwEB/zBjBgNVHSUEXDBaBggrBgEFBQcDAQYIKwYBBQUHAwIG -CCsGAQUFBwMDBggrBgEFBQcDBAYIKwYBBQUHAwUGCCsGAQUFBwMGBggrBgEFBQcD -BwYIKwYBBQUHAwgGCCsGAQUFBwMJMB0GA1UdDgQWBBTbHzXza0z/QjFkm827Wh4d -SBC37jANBgkqhkiG9w0BAQUFAAOCAQEAOGy3iPGt+lg3dNHocN6cJ1nL5BXXoMNg -14iABMUwTD3UGusGXllH5rxmy+AI/Og17GJ9ysDawXiv5UZv+4mCI4/211NmVaDe -JRI7cTYWVRJ2+z34VFsxugAG+H1V5ad2g6pcSpemKijfvcZsCyOVjjN/Hl5AHxNU -LJzltQ7dFyiuawHTUin1Ih+QOfTcYmjwPIZH7LgFRbu3DJaUxmfLI3HQjnQi1kHr -A6i26r7EARK1s11AdgYg1GS4KUYGis4fk5oQ7vuqWrTcL9Ury/bXBYSYBZELhPc9 -+tb5evosFeo2gkO3t7jj83EB7UNDogVFwygFBzXjAaU4HoDU18PZ3g== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEojCCA4qgAwIBAgIQRL4Mi1AAJLQR0zYlJWfJiTANBgkqhkiG9w0BAQUFADCB -rjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug -Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho -dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xNjA0BgNVBAMTLVVUTi1VU0VSRmlyc3Qt -Q2xpZW50IEF1dGhlbnRpY2F0aW9uIGFuZCBFbWFpbDAeFw05OTA3MDkxNzI4NTBa -Fw0xOTA3MDkxNzM2NThaMIGuMQswCQYDVQQGEwJVUzELMAkGA1UECBMCVVQxFzAV -BgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5l -dHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93d3cudXNlcnRydXN0LmNvbTE2MDQGA1UE -AxMtVVROLVVTRVJGaXJzdC1DbGllbnQgQXV0aGVudGljYXRpb24gYW5kIEVtYWls -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsjmFpPJ9q0E7YkY3rs3B -YHW8OWX5ShpHornMSMxqmNVNNRm5pELlzkniii8efNIxB8dOtINknS4p1aJkxIW9 -hVE1eaROaJB7HHqkkqgX8pgV8pPMyaQylbsMTzC9mKALi+VuG6JG+ni8om+rWV6l -L8/K2m2qL+usobNqqrcuZzWLeeEeaYji5kbNoKXqvgvOdjp6Dpvq/NonWz1zHyLm -SGHGTPNpsaguG7bUMSAsvIKKjqQOpdeJQ/wWWq8dcdcRWdq6hw2v+vPhwvCkxWeM -1tZUOt4KpLoDd7NlyP0e03RiqhjKaJMeoYV+9Udly/hNVyh00jT/MLbu9mIwFIws -6wIDAQABo4G5MIG2MAsGA1UdDwQEAwIBxjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud -DgQWBBSJgmd9xJ0mcABLtFBIfN49rgRufTBYBgNVHR8EUTBPME2gS6BJhkdodHRw -Oi8vY3JsLnVzZXJ0cnVzdC5jb20vVVROLVVTRVJGaXJzdC1DbGllbnRBdXRoZW50 -aWNhdGlvbmFuZEVtYWlsLmNybDAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUH -AwQwDQYJKoZIhvcNAQEFBQADggEBALFtYV2mGn98q0rkMPxTbyUkxsrt4jFcKw7u -7mFVbwQ+zznexRtJlOTrIEy05p5QLnLZjfWqo7NK2lYcYJeA3IKirUq9iiv/Cwm0 -xtcgBEXkzYABurorbs6q15L+5K/r9CYdFip/bDCVNy8zEqx/3cfREYxRmLLQo5HQ -rfafnoOTHh1CuEava2bwm3/q4wMC5QJRwarVNZ1yQAOJujEdxRBoUp7fooXFXAim -eOZTT7Hot9MUnpOmw2TjrH5xzbyf6QMbzPvprDHBr3wVdAKZw7JHpsIyYdfHb0gk -USeh1YdV8nuPmD0Wnu51tvjQjvLzxq4oW6fw8zYX/MMF08oDSlQ= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEdDCCA1ygAwIBAgIQRL4Mi1AAJLQR0zYq/mUK/TANBgkqhkiG9w0BAQUFADCB -lzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug -Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho -dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3Qt -SGFyZHdhcmUwHhcNOTkwNzA5MTgxMDQyWhcNMTkwNzA5MTgxOTIyWjCBlzELMAkG -A1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEe -MBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8v -d3d3LnVzZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3QtSGFyZHdh -cmUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCx98M4P7Sof885glFn -0G2f0v9Y8+efK+wNiVSZuTiZFvfgIXlIwrthdBKWHTxqctU8EGc6Oe0rE81m65UJ -M6Rsl7HoxuzBdXmcRl6Nq9Bq/bkqVRcQVLMZ8Jr28bFdtqdt++BxF2uiiPsA3/4a -MXcMmgF6sTLjKwEHOG7DpV4jvEWbe1DByTCP2+UretNb+zNAHqDVmBe8i4fDidNd -oI6yqqr2jmmIBsX6iSHzCJ1pLgkzmykNRg+MzEk0sGlRvfkGzWitZky8PqxhvQqI -DsjfPe58BEydCl5rkdbux+0ojatNh4lz0G6k0B4WixThdkQDf2Os5M1JnMWS9Ksy -oUhbAgMBAAGjgbkwgbYwCwYDVR0PBAQDAgHGMA8GA1UdEwEB/wQFMAMBAf8wHQYD -VR0OBBYEFKFyXyYbKJhDlV0HN9WFlp1L0sNFMEQGA1UdHwQ9MDswOaA3oDWGM2h0 -dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9VVE4tVVNFUkZpcnN0LUhhcmR3YXJlLmNy -bDAxBgNVHSUEKjAoBggrBgEFBQcDAQYIKwYBBQUHAwUGCCsGAQUFBwMGBggrBgEF -BQcDBzANBgkqhkiG9w0BAQUFAAOCAQEARxkP3nTGmZev/K0oXnWO6y1n7k57K9cM -//bey1WiCuFMVGWTYGufEpytXoMs61quwOQt9ABjHbjAbPLPSbtNk28Gpgoiskli -CE7/yMgUsogWXecB5BKV5UU0s4tpvc+0hY91UZ59Ojg6FEgSxvunOxqNDYJAB+gE -CJChicsZUN/KHAG8HQQZexB2lzvukJDKxA4fFm517zP4029bHpbj4HR3dHuKom4t -3XbWOTCC8KucUvIqx69JXn7HaOWCgchqJ/kniCrVWFCVH/A7HFe7fRQ5YiuayZSS -KqMiDP+JJn1fIytH1xUdqWqeUQ0qUZ6B+dQ7XnASfxAynB67nfhmqA== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEZDCCA0ygAwIBAgIQRL4Mi1AAJLQR0zYwS8AzdzANBgkqhkiG9w0BAQUFADCB -ozELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug -Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho -dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xKzApBgNVBAMTIlVUTi1VU0VSRmlyc3Qt -TmV0d29yayBBcHBsaWNhdGlvbnMwHhcNOTkwNzA5MTg0ODM5WhcNMTkwNzA5MTg1 -NzQ5WjCBozELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0 -IExha2UgQ2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYD -VQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xKzApBgNVBAMTIlVUTi1VU0VS -Rmlyc3QtTmV0d29yayBBcHBsaWNhdGlvbnMwggEiMA0GCSqGSIb3DQEBAQUAA4IB -DwAwggEKAoIBAQCz+5Gh5DZVhawGNFugmliy+LUPBXeDrjKxdpJo7CNKyXY/45y2 -N3kDuatpjQclthln5LAbGHNhSuh+zdMvZOOmfAz6F4CjDUeJT1FxL+78P/m4FoCH -iZMlIJpDgmkkdihZNaEdwH+DBmQWICzTSaSFtMBhf1EI+GgVkYDLpdXuOzr0hARe -YFmnjDRy7rh4xdE7EkpvfmUnuaRVxblvQ6TFHSyZwFKkeEwVs0CYCGtDxgGwenv1 -axwiP8vv/6jQOkt2FZ7S0cYu49tXGzKiuG/ohqY/cKvlcJKrRB5AUPuco2LkbG6g -yN7igEL66S/ozjIEj3yNtxyjNTwV3Z7DrpelAgMBAAGjgZEwgY4wCwYDVR0PBAQD -AgHGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFPqGydvguul49Uuo1hXf8NPh -ahQ8ME8GA1UdHwRIMEYwRKBCoECGPmh0dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9V -VE4tVVNFUkZpcnN0LU5ldHdvcmtBcHBsaWNhdGlvbnMuY3JsMA0GCSqGSIb3DQEB -BQUAA4IBAQCk8yXM0dSRgyLQzDKrm5ZONJFUICU0YV8qAhXhi6r/fWRRzwr/vH3Y -IWp4yy9Rb/hCHTO967V7lMPDqaAt39EpHx3+jz+7qEUqf9FuVSTiuwL7MT++6Lzs -QCv4AdRWOOTKRIK1YSAhZ2X28AvnNPilwpyjXEAfhZOVBt5P1CeptqX8Fs1zMT+4 -ZSfP1FMa8Kxun08FDAOBp4QpxFq9ZFdyrTvPNximmMatBrTcCKME1SmklpoSZ0qM -YEWd8SOasACcaLWYUNPvji6SZbFIPiG+FTAqDbUMo2s/rn9X9R+WfN9v3YIwLGUb -QErNaLly7HF27FSOH4UMAWr6pjisH8SE ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEZjCCA06gAwIBAgIQRL4Mi1AAJLQR0zYt4LNfGzANBgkqhkiG9w0BAQUFADCB -lTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug -Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho -dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHTAbBgNVBAMTFFVUTi1VU0VSRmlyc3Qt -T2JqZWN0MB4XDTk5MDcwOTE4MzEyMFoXDTE5MDcwOTE4NDAzNlowgZUxCzAJBgNV -BAYTAlVTMQswCQYDVQQIEwJVVDEXMBUGA1UEBxMOU2FsdCBMYWtlIENpdHkxHjAc -BgNVBAoTFVRoZSBVU0VSVFJVU1QgTmV0d29yazEhMB8GA1UECxMYaHR0cDovL3d3 -dy51c2VydHJ1c3QuY29tMR0wGwYDVQQDExRVVE4tVVNFUkZpcnN0LU9iamVjdDCC -ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM6qgT+jo2F4qjEAVZURnicP -HxzfOpuCaDDASmEd8S8O+r5596Uj71VRloTN2+O5bj4x2AogZ8f02b+U60cEPgLO -KqJdhwQJ9jCdGIqXsqoc/EHSoTbL+z2RuufZcDX65OeQw5ujm9M89RKZd7G3CeBo -5hy485RjiGpq/gt2yb70IuRnuasaXnfBhQfdDWy/7gbHd2pBnqcP1/vulBe3/IW+ -pKvEHDHd17bR5PDv3xaPslKT16HUiaEHLr/hARJCHhrh2JU022R5KP+6LhHC5ehb -kkj7RwvCbNqtMoNB86XlQXD9ZZBt+vpRxPm9lisZBCzTbafc8H9vg2XiaquHhnUC -AwEAAaOBrzCBrDALBgNVHQ8EBAMCAcYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E -FgQU2u1kdBScFDyr3ZmpvVsoTYs8ydgwQgYDVR0fBDswOTA3oDWgM4YxaHR0cDov -L2NybC51c2VydHJ1c3QuY29tL1VUTi1VU0VSRmlyc3QtT2JqZWN0LmNybDApBgNV -HSUEIjAgBggrBgEFBQcDAwYIKwYBBQUHAwgGCisGAQQBgjcKAwQwDQYJKoZIhvcN -AQEFBQADggEBAAgfUrE3RHjb/c652pWWmKpVZIC1WkDdIaXFwfNfLEzIR1pp6ujw -NTX00CXzyKakh0q9G7FzCL3Uw8q2NbtZhncxzaeAFK4T7/yxSPlrJSUtUbYsbUXB -mMiKVl0+7kNOPmsnjtA6S4ULX9Ptaqd1y9Fahy85dRNacrACgZ++8A+EVCBibGnU -4U3GDZlDAQ0Slox4nb9QorFEqmrPF3rPbw/U+CRVX/A0FklmPlBGyWNxODFiuGK5 -81OtbLUrohKqGU8J2l7nk8aOFAj+8DCAGKCGhU3IfdeLA/5u1fedFqySLKAj5ZyR -Uh+U3xeUc8OzwcFxBSAAeL0TUh2oPs0AH8g= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEXjCCA0agAwIBAgIQRL4Mi1AAIbQR0ypoBqmtaTANBgkqhkiG9w0BAQUFADCB -kzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug -Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho -dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xGzAZBgNVBAMTElVUTiAtIERBVEFDb3Jw -IFNHQzAeFw05OTA2MjQxODU3MjFaFw0xOTA2MjQxOTA2MzBaMIGTMQswCQYDVQQG -EwJVUzELMAkGA1UECBMCVVQxFzAVBgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4wHAYD -VQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93d3cu -dXNlcnRydXN0LmNvbTEbMBkGA1UEAxMSVVROIC0gREFUQUNvcnAgU0dDMIIBIjAN -BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3+5YEKIrblXEjr8uRgnn4AgPLit6 -E5Qbvfa2gI5lBZMAHryv4g+OGQ0SR+ysraP6LnD43m77VkIVni5c7yPeIbkFdicZ -D0/Ww5y0vpQZY/KmEQrrU0icvvIpOxboGqBMpsn0GFlowHDyUwDAXlCCpVZvNvlK -4ESGoE1O1kduSUrLZ9emxAW5jh70/P/N5zbgnAVssjMiFdC04MwXwLLA9P4yPykq -lXvY8qdOD1R8oQ2AswkDwf9c3V6aPryuvEeKaq5xyh+xKrhfQgUL7EYw0XILyulW -bfXv33i+Ybqypa4ETLyorGkVl73v67SMvzX41MPRKA5cOp9wGDMgd8SirwIDAQAB -o4GrMIGoMAsGA1UdDwQEAwIBxjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRT -MtGzz3/64PGgXYVOktKeRR20TzA9BgNVHR8ENjA0MDKgMKAuhixodHRwOi8vY3Js -LnVzZXJ0cnVzdC5jb20vVVROLURBVEFDb3JwU0dDLmNybDAqBgNVHSUEIzAhBggr -BgEFBQcDAQYKKwYBBAGCNwoDAwYJYIZIAYb4QgQBMA0GCSqGSIb3DQEBBQUAA4IB -AQAnNZcAiosovcYzMB4p/OL31ZjUQLtgyr+rFywJNn9Q+kHcrpY6CiM+iVnJowft -Gzet/Hy+UUla3joKVAgWRcKZsYfNjGjgaQPpxE6YsjuMFrMOoAyYUJuTqXAJyCyj -j98C5OBxOvG0I3KgqgHf35g+FFCgMSa9KOlaMCZ1+XtgHI3zzVAmbQQnmt/VDUVH -KWss5nbZqSl9Mt3JNjy9rjXxEZ4du5A/EkdOjtd+D2JzHVImOBwYSf0wdJrE5SIv -2MCN7ZF6TACPcn9d2t0bi0Vr591pl6jFVkwPDPafepE39peC4N1xaf92P2BNPM/3 -mfnGV/TJVTl4uix5yaaIK/QI ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDDDCCAfSgAwIBAgIDAQAgMA0GCSqGSIb3DQEBBQUAMD4xCzAJBgNVBAYTAlBM -MRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBD -QTAeFw0wMjA2MTExMDQ2MzlaFw0yNzA2MTExMDQ2MzlaMD4xCzAJBgNVBAYTAlBM -MRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBD -QTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM6xwS7TT3zNJc4YPk/E -jG+AanPIW1H4m9LcuwBcsaD8dQPugfCI7iNS6eYVM42sLQnFdvkrOYCJ5JdLkKWo -ePhzQ3ukYbDYWMzhbGZ+nPMJXlVjhNWo7/OxLjBos8Q82KxujZlakE403Daaj4GI -ULdtlkIJ89eVgw1BS7Bqa/j8D35in2fE7SZfECYPCE/wpFcozo+47UX2bu4lXapu -Ob7kky/ZR6By6/qmW6/KUz/iDsaWVhFu9+lmqSbYf5VT7QqFiLpPKaVCjF62/IUg -AKpoC6EahQGcxEZjgoi2IrHu/qpGWX7PNSzVttpd90gzFFS269lvzs2I1qsb2pY7 -HVkCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEA -uI3O7+cUus/usESSbLQ5PqKEbq24IXfS1HeCh+YgQYHu4vgRt2PRFze+GXYkHAQa -TOs9qmdvLdTN/mUxcMUbpgIKumB7bVjCmkn+YzILa+M6wKyrO7Do0wlRjBCDxjTg -xSvgGrZgFCdsMneMvLJymM/NzD+5yCRCFNZX/OYmQ6kd5YCQzgNUKD73P9P4Te1q -CjqTE5s7FCMTY5w/0YcneeVMUeMBrYVdGjux1XMQpNPyvG5k9VpWkKjHDkx0Dy5x -O/fIR/RpbxXyEV6DHpx8Uq79AtoSqFlnGNu8cN2bsWntgM6JQEhqDjXKKWYVIZQs -6GAqm4VKQPNriiTsBhYscw== ------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----- -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----- -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----- -MIID+TCCAuGgAwIBAgIQW1fXqEywr9nTb0ugMbTW4jANBgkqhkiG9w0BAQUFADB5 -MQswCQYDVQQGEwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMmVmlzYSBJbnRl -cm5hdGlvbmFsIFNlcnZpY2UgQXNzb2NpYXRpb24xKjAoBgNVBAMTIVZpc2EgSW5m -b3JtYXRpb24gRGVsaXZlcnkgUm9vdCBDQTAeFw0wNTA2MjcxNzQyNDJaFw0yNTA2 -MjkxNzQyNDJaMHkxCzAJBgNVBAYTAlVTMQ0wCwYDVQQKEwRWSVNBMS8wLQYDVQQL -EyZWaXNhIEludGVybmF0aW9uYWwgU2VydmljZSBBc3NvY2lhdGlvbjEqMCgGA1UE -AxMhVmlzYSBJbmZvcm1hdGlvbiBEZWxpdmVyeSBSb290IENBMIIBIjANBgkqhkiG -9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyREA4R/QkkfpLx0cYjga/EhIPZpchH0MZsRZ -FfP6C2ITtf/Wc+MtgD4yTK0yoiXvni3d+aCtEgK3GDvkdgYrgF76ROJFZwUQjQ9l -x42gRT05DbXvWFoy7dTglCZ9z/Tt2Cnktv9oxKgmkeHY/CyfpCBg1S8xth2JlGMR -0ug/GMO5zANuegZOv438p5Lt5So+du2Gl+RMFQqEPwqN5uJSqAe0VtmB4gWdQ8on -Bj2ZAM2R73QW7UW0Igt2vA4JaSiNtaAG/Y/58VXWHGgbq7rDtNK1R30X0kJV0rGA -ib3RSwB3LpG7bOjbIucV5mQgJoVjoA1e05w6g1x/KmNTmOGRVwIDAQABo30wezAP -BgNVHRMBAf8EBTADAQH/MDkGA1UdIAQyMDAwLgYFZ4EDAgEwJTAVBggrBgEFBQcC -ARYJMS4yLjMuNC41MAwGCCsGAQUFBwICMAAwDgYDVR0PAQH/BAQDAgEGMB0GA1Ud -DgQWBBRPitp2/2d3I5qmgH1924h1hfeBejANBgkqhkiG9w0BAQUFAAOCAQEACUW1 -QdUHdDJydgDPmYt+telnG/Su+DPaf1cregzlN43bJaJosMP7NwjoJY/H2He4XLWb -5rXEkl+xH1UyUwF7mtaUoxbGxEvt8hPZSTB4da2mzXgwKvXuHyzF5Qjy1hOB0/pS -WaF9ARpVKJJ7TOJQdGKBsF2Ty4fSCLqZLgfxbqwMsd9sysXI3rDXjIhekqvbgeLz -PqZr+pfgFhwCCLSMQWl5Ll3u7Qk9wR094DZ6jj6+JCVCRUS3HyabH4OlM0Vc2K+j -INsF/64Or7GNtRf9HYEJvrPxHINxl3JVwhYj4ASeaO4KwhVbwtw94Tc/XrGcexDo -c5lC3rAi4/UZqweYCw== ------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----- -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----- -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----- -MIIEvTCCA6WgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBhTELMAkGA1UEBhMCVVMx -IDAeBgNVBAoMF1dlbGxzIEZhcmdvIFdlbGxzU2VjdXJlMRwwGgYDVQQLDBNXZWxs -cyBGYXJnbyBCYW5rIE5BMTYwNAYDVQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMgUm9v -dCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDcxMjEzMTcwNzU0WhcNMjIxMjE0 -MDAwNzU0WjCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoMF1dlbGxzIEZhcmdvIFdl -bGxzU2VjdXJlMRwwGgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYwNAYDVQQD -DC1XZWxsc1NlY3VyZSBQdWJsaWMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkw -ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDub7S9eeKPCCGeOARBJe+r -WxxTkqxtnt3CxC5FlAM1iGd0V+PfjLindo8796jE2yljDpFoNoqXjopxaAkH5OjU -Dk/41itMpBb570OYj7OeUt9tkTmPOL13i0Nj67eT/DBMHAGTthP796EfvyXhdDcs -HqRePGj4S78NuR4uNuip5Kf4D8uCdXw1LSLWwr8L87T8bJVhHlfXBIEyg1J55oNj -z7fLY4sR4r1e6/aN7ZVyKLSsEmLpSjPmgzKuBXWVvYSV2ypcm44uDLiBK0HmOFaf -SZtsdvqKXfcBeYF8wYNABf5x/Qw/zE5gCQ5lRxAvAcAFP4/4s0HvWkJ+We/Slwxl -AgMBAAGjggE0MIIBMDAPBgNVHRMBAf8EBTADAQH/MDkGA1UdHwQyMDAwLqAsoCqG -KGh0dHA6Ly9jcmwucGtpLndlbGxzZmFyZ28uY29tL3dzcHJjYS5jcmwwDgYDVR0P -AQH/BAQDAgHGMB0GA1UdDgQWBBQmlRkQ2eihl5H/3BnZtQQ+0nMKajCBsgYDVR0j -BIGqMIGngBQmlRkQ2eihl5H/3BnZtQQ+0nMKaqGBi6SBiDCBhTELMAkGA1UEBhMC -VVMxIDAeBgNVBAoMF1dlbGxzIEZhcmdvIFdlbGxzU2VjdXJlMRwwGgYDVQQLDBNX -ZWxscyBGYXJnbyBCYW5rIE5BMTYwNAYDVQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMg -Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHmCAQEwDQYJKoZIhvcNAQEFBQADggEB -ALkVsUSRzCPIK0134/iaeycNzXK7mQDKfGYZUMbVmO2rvwNa5U3lHshPcZeG1eMd -/ZDJPHV3V3p9+N701NX3leZ0bh08rnyd2wIDBSxxSyU+B+NemvVmFymIGjifz6pB -A4SXa5M4esowRBskRDPQ5NHcKDj0E0M1NSljqHyita04pO2t/caaH/+Xc/77szWn -k4bGdpEA5qxRFsQnMlzbc9qlk1eOPm01JghZ1edE13YgY+esE2fDbbFwRnzVlhE9 -iW9dqKHrjQrawx0zbKPqZxmamX9LPYNRKh3KL4YMon4QLSvUFpULB6ouFJJJtylv -2G0xffX8oRAHh84vWdw+WNs= ------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----- -MIIDjjCCAnagAwIBAgIIKv++n6Lw6YcwDQYJKoZIhvcNAQEFBQAwKDELMAkGA1UE -BhMCQkUxGTAXBgNVBAMTEEJlbGdpdW0gUm9vdCBDQTIwHhcNMDcxMDA0MTAwMDAw -WhcNMjExMjE1MDgwMDAwWjAoMQswCQYDVQQGEwJCRTEZMBcGA1UEAxMQQmVsZ2l1 -bSBSb290IENBMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMZzQh6S -/3UPi790hqc/7bIYLS2X+an7mEoj39WN4IzGMhwWLQdC1i22bi+n9fzGhYJdld61 -IgDMqFNAn68KNaJ6x+HK92AQZw6nUHMXU5WfIp8MXW+2QbyM69odRr2nlL/zGsvU -+40OHjPIltfsjFPekx40HopQcSZYtF3CiInaYNKJIT/e1wEYNm7hLHADBGXvmAYr -XR5i3FVr/mZkIV/4L+HXmymvb82fqgxG0YjFnaKVn6w/Fa7yYd/vw2uaItgscf1Y -HewApDgglVrH1Tdjuk+bqv5WRi5j2Qsj1Yr6tSPwiRuhFA0m2kHwOI8w7QUmecFL -TqG4flVSOmlGhHUCAwEAAaOBuzCBuDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ -BAUwAwEB/zBCBgNVHSAEOzA5MDcGBWA4CQEBMC4wLAYIKwYBBQUHAgEWIGh0dHA6 -Ly9yZXBvc2l0b3J5LmVpZC5iZWxnaXVtLmJlMB0GA1UdDgQWBBSFiuv0xbu+DlkD -lN7WgAEV4xCcOTARBglghkgBhvhCAQEEBAMCAAcwHwYDVR0jBBgwFoAUhYrr9MW7 -vg5ZA5Te1oABFeMQnDkwDQYJKoZIhvcNAQEFBQADggEBAFHYhd27V2/MoGy1oyCc -UwnzSgEMdL8rs5qauhjyC4isHLMzr87lEwEnkoRYmhC598wUkmt0FoqW6FHvv/pK -JaeJtmMrXZRY0c8RcrYeuTlBFk0pvDVTC9rejg7NqZV3JcqUWumyaa7YwBO+mPyW -nIR/VRPmPIfjvCCkpDZoa01gZhz5v6yAlGYuuUGK02XThIAC71AdXkbc98m6tTR8 -KvPG2F9fVJ3bTc0R5/0UAoNmXsimABKgX77OFP67H6dh96tK8QYUn8pJQsKpvO2F -sauBQeYNxUJpU4c5nUwfAA4+Bw11V0SoU7Q2dmSZ3G7rPUZuFF1eR1ONeE3gJ7uO -hXY= ------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----- -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----- -MIIDQzCCAiugAwIBAgIQX/h7KCtU3I1CoxW1aMmt/zANBgkqhkiG9w0BAQUFADA1 -MRYwFAYDVQQKEw1DaXNjbyBTeXN0ZW1zMRswGQYDVQQDExJDaXNjbyBSb290IENB -IDIwNDgwHhcNMDQwNTE0MjAxNzEyWhcNMjkwNTE0MjAyNTQyWjA1MRYwFAYDVQQK -Ew1DaXNjbyBTeXN0ZW1zMRswGQYDVQQDExJDaXNjbyBSb290IENBIDIwNDgwggEg -MA0GCSqGSIb3DQEBAQUAA4IBDQAwggEIAoIBAQCwmrmrp68Kd6ficba0ZmKUeIhH -xmJVhEAyv8CrLqUccda8bnuoqrpu0hWISEWdovyD0My5jOAmaHBKeN8hF570YQXJ -FcjPFto1YYmUQ6iEqDGYeJu5Tm8sUxJszR2tKyS7McQr/4NEb7Y9JHcJ6r8qqB9q -VvYgDxFUl4F1pyXOWWqCZe+36ufijXWLbvLdT6ZeYpzPEApk0E5tzivMW/VgpSdH -jWn0f84bcN5wGyDWbs2mAag8EtKpP6BrXruOIIt6keO1aO6g58QBdKhTCytKmg9l -Eg6CTY5j/e/rmxrbU6YTYK/CfdfHbBcl1HP7R2RQgYCUTOG/rksc35LtLgXfAgED -o1EwTzALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUJ/PI -FR5umgIJFq0roIlgX9p7L6owEAYJKwYBBAGCNxUBBAMCAQAwDQYJKoZIhvcNAQEF -BQADggEBAJ2dhISjQal8dwy3U8pORFBi71R803UXHOjgxkhLtv5MOhmBVrBW7hmW -Yqpao2TB9k5UM8Z3/sUcuuVdJcr18JOagxEu5sv4dEX+5wW4q+ffy0vhN4TauYuX -cB7w4ovXsNgOnbFp1iqRe6lJT37mjpXYgyc81WhJDtSd9i7rp77rMKSsH0T8lasz -Bvt9YAretIpjsJyp8qS5UwGH0GikJ3+r/+n6yUA4iGe0OcaEb1fJU9u6ju7AQ7L4 -CYNu/2bPPu8Xs1gYJQk0XuPL1hS27PKSb3TkL4Eq1ZKR4OCXPDJoBYVL0fdX4lId -kxpUnwVwwEpxYB5DC2Ae/qPOgRnhCzU= ------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----- -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----- -MIIDczCCAlugAwIBAgIBBDANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQGEwJLUjEN -MAsGA1UECgwES0lTQTEuMCwGA1UECwwlS29yZWEgQ2VydGlmaWNhdGlvbiBBdXRo -b3JpdHkgQ2VudHJhbDEWMBQGA1UEAwwNS0lTQSBSb290Q0EgMTAeFw0wNTA4MjQw -ODA1NDZaFw0yNTA4MjQwODA1NDZaMGQxCzAJBgNVBAYTAktSMQ0wCwYDVQQKDARL -SVNBMS4wLAYDVQQLDCVLb3JlYSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSBDZW50 -cmFsMRYwFAYDVQQDDA1LSVNBIFJvb3RDQSAxMIIBIDANBgkqhkiG9w0BAQEFAAOC -AQ0AMIIBCAKCAQEAvATk+hM58DSWIGtsaLv623f/J/es7C/n/fB/bW+MKs0lCVsk -9KFo/CjsySXirO3eyDOE9bClCTqnsUdIxcxPjHmc+QZXfd3uOPbPFLKc6tPAXXdi -8EcNuRpAU1xkcK8IWsD3z3X5bI1kKB4g/rcbGdNaZoNy4rCbvdMlFQ0yb2Q3lIVG -yHK+d9VuHygvx2nt54OJM1jT3qC/QOhDUO7cTWu8peqmyGGO9cNkrwYV3CmLP3WM -vHFE2/yttRcdbYmDz8Yzvb9Fov4Kn6MRXw+5H5wawkbMnChmn3AmPC7fqoD+jMUE -CSVPzZNHPDfqAmeS/vwiJFys0izgXAEzisEZ2wIBA6MyMDAwHQYDVR0OBBYEFL+2 -J9gDWnZlTGEBQVYx5Yt7OtnMMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEF -BQADggEBABOvUQveimpb5poKyLGQSk6hAp3MiNKrZr097LuxQpVqslxa/6FjZJap -aBV/JV6K+KRzwYCKhQoOUugy50X4TmWAkZl0Q+VFnUkq8JSV3enhMNITbslOsXfl -BM+tWh6UCVrXPAgcrnrpFDLBRa3SJkhyrKhB2vAhhzle3/xk/2F0KpzZm4tfwjeT -2KM3LzuTa7IbB6d/CVDv0zq+IWuKkDsnSlFOa56ch534eJAx7REnxqhZvvwYC/uO -fi5C4e3nCSG9uRPFVmf0JqZCQ5BEVLRxm3bkGhKsGigA35vB1fjbXKP4krG9tNT5 -UNkAAk/bg9ART6RCVmE6fhMy04Qfybo= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIGfTCCBWWgAwIBAgICAQMwDQYJKoZIhvcNAQEEBQAwga8xCzAJBgNVBAYTAkhV -MRAwDgYDVQQIEwdIdW5nYXJ5MREwDwYDVQQHEwhCdWRhcGVzdDEnMCUGA1UEChMe -TmV0TG9jayBIYWxvemF0Yml6dG9uc2FnaSBLZnQuMRowGAYDVQQLExFUYW51c2l0 -dmFueWtpYWRvazE2MDQGA1UEAxMtTmV0TG9jayBLb3pqZWd5em9pIChDbGFzcyBB -KSBUYW51c2l0dmFueWtpYWRvMB4XDTk5MDIyNDIzMTQ0N1oXDTE5MDIxOTIzMTQ0 -N1owga8xCzAJBgNVBAYTAkhVMRAwDgYDVQQIEwdIdW5nYXJ5MREwDwYDVQQHEwhC -dWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6dG9uc2FnaSBLZnQu -MRowGAYDVQQLExFUYW51c2l0dmFueWtpYWRvazE2MDQGA1UEAxMtTmV0TG9jayBL -b3pqZWd5em9pIChDbGFzcyBBKSBUYW51c2l0dmFueWtpYWRvMIIBIjANBgkqhkiG -9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvHSMD7tM9DceqQWC2ObhbHDqeLVu0ThEDaiD -zl3S1tWBxdRL51uUcCbbO51qTGL3cfNk1mE7PetzozfZz+qMkjvN9wfcZnSX9EUi -3fRc4L9t875lM+QVOr/bmJBVOMTtplVjC7B4BPTjbsE/jvxReB+SnoPC/tmwqcm8 -WgD/qaiYdPv2LD4VOQ22BFWoDpggQrOxJa1+mm9dU7GrDPzr4PN6s6iz/0b2Y6LY -Oph7tqyF/7AlT3Rj5xMHpQqPBffAZG9+pyeAlt7ULoZgx2srXnN7F+eRP2QM2Esi -NCubMvJIH5+hCoR64sKtlz2O1cH5VqNQ6ca0+pii7pXmKgOM3wIDAQABo4ICnzCC -ApswDgYDVR0PAQH/BAQDAgAGMBIGA1UdEwEB/wQIMAYBAf8CAQQwEQYJYIZIAYb4 -QgEBBAQDAgAHMIICYAYJYIZIAYb4QgENBIICURaCAk1GSUdZRUxFTSEgRXplbiB0 -YW51c2l0dmFueSBhIE5ldExvY2sgS2Z0LiBBbHRhbGFub3MgU3pvbGdhbHRhdGFz -aSBGZWx0ZXRlbGVpYmVuIGxlaXJ0IGVsamFyYXNvayBhbGFwamFuIGtlc3p1bHQu -IEEgaGl0ZWxlc2l0ZXMgZm9seWFtYXRhdCBhIE5ldExvY2sgS2Z0LiB0ZXJtZWtm -ZWxlbG9zc2VnLWJpenRvc2l0YXNhIHZlZGkuIEEgZGlnaXRhbGlzIGFsYWlyYXMg -ZWxmb2dhZGFzYW5hayBmZWx0ZXRlbGUgYXogZWxvaXJ0IGVsbGVub3J6ZXNpIGVs -amFyYXMgbWVndGV0ZWxlLiBBeiBlbGphcmFzIGxlaXJhc2EgbWVndGFsYWxoYXRv -IGEgTmV0TG9jayBLZnQuIEludGVybmV0IGhvbmxhcGphbiBhIGh0dHBzOi8vd3d3 -Lm5ldGxvY2submV0L2RvY3MgY2ltZW4gdmFneSBrZXJoZXRvIGF6IGVsbGVub3J6 -ZXNAbmV0bG9jay5uZXQgZS1tYWlsIGNpbWVuLiBJTVBPUlRBTlQhIFRoZSBpc3N1 -YW5jZSBhbmQgdGhlIHVzZSBvZiB0aGlzIGNlcnRpZmljYXRlIGlzIHN1YmplY3Qg -dG8gdGhlIE5ldExvY2sgQ1BTIGF2YWlsYWJsZSBhdCBodHRwczovL3d3dy5uZXRs -b2NrLm5ldC9kb2NzIG9yIGJ5IGUtbWFpbCBhdCBjcHNAbmV0bG9jay5uZXQuMA0G -CSqGSIb3DQEBBAUAA4IBAQBIJEb3ulZv+sgoA0BO5TE5ayZrU3/b39/zcT0mwBQO -xmd7I6gMc90Bu8bKbjc5VdXHjFYgDigKDtIqpLBJUsY4B/6+CgmM0ZjPytoUMaFP -0jn8DxEsQ8Pdq5PHVT5HfBgaANzze9jyf1JsIPQLX2lS9O74silg6+NJMSEN1rUQ -QeJBCWziGppWS3cC9qCbmieH6FUpccKQn0V4GuEVZD3QDtigdp+uxdAu6tYPVuxk -f1qbFFgBJ34TUMdrKuZoPL9coAob4Q566eKAw+np9v1sEZ7Q5SgnK1QyQhSCdeZK -8CtmdWOMovsEPoMOmzbwGOQmIMOM8CgHrTwXZoi1/baI ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEGjCCAwKgAwIBAgIDAYagMA0GCSqGSIb3DQEBBQUAMIGjMQswCQYDVQQGEwJG -STEQMA4GA1UECBMHRmlubGFuZDEhMB8GA1UEChMYVmFlc3RvcmVraXN0ZXJpa2Vz -a3VzIENBMSkwJwYDVQQLEyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSBTZXJ2aWNl -czEZMBcGA1UECxMQVmFybWVubmVwYWx2ZWx1dDEZMBcGA1UEAxMQVlJLIEdvdi4g -Um9vdCBDQTAeFw0wMjEyMTgxMzUzMDBaFw0yMzEyMTgxMzUxMDhaMIGjMQswCQYD -VQQGEwJGSTEQMA4GA1UECBMHRmlubGFuZDEhMB8GA1UEChMYVmFlc3RvcmVraXN0 -ZXJpa2Vza3VzIENBMSkwJwYDVQQLEyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSBT -ZXJ2aWNlczEZMBcGA1UECxMQVmFybWVubmVwYWx2ZWx1dDEZMBcGA1UEAxMQVlJL -IEdvdi4gUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALCF -FdrIAzfQo0Y3bBseljDCWoUSZyPyu5/nioFgJ/gTqTy894aqqvTzJSm0/nWuHoGG -igWyHWWyOOi0zCia+xc28ZPVec7Bg4shT8MNrUHfeJ1I4x9CRPw8bSEga60ihCRC -jxdNwlAfZM0tOSJWiP2yY51U2kJpwMhP1xjiPshphJQ9LIDGfM6911Mf64i5psu7 -hVfvV3ZdDIvTXhJBnyHAOfQmbQj6OLOhd7HuFtjQaNq0mKWgZUZKa41+qk1guPjI -DfxxPu45h4G02fhukO4/DmHXHSto5i7hQkQmeCxY8n0Wf2HASSQqiYe2XS8pGfim -545SnkFLWg6quMJmQlMCAwEAAaNVMFMwDwYDVR0TAQH/BAUwAwEB/zARBglghkgB -hvhCAQEEBAMCAAcwDgYDVR0PAQH/BAQDAgHGMB0GA1UdDgQWBBTb6eGb0tEkC/yr -46Bn6q6cS3f0sDANBgkqhkiG9w0BAQUFAAOCAQEArX1ID1QRnljurw2bEi8hpM2b -uoRH5sklVSPj3xhYKizbXvfNVPVRJHtiZ+GxH0mvNNDrsczZog1Sf0JLiGCXzyVy -t08pLWKfT6HAVVdWDsRol5EfnGTCKTIB6dTI2riBmCguGMcs/OubUpbf9MiQGS0j -8/G7cdqehSO9Gu8u5Hp5t8OdhkktY7ktdM9lDzJmid87Ie4pbzlj2RXBbvbfgD5Q -eBmK3QOjFKU3p7UsfLYRh+cF8ry23tT/l4EohP7+bEaFEEGfTXWMB9SZZ291im/k -UJL2mdUQuMSpe/cXjUu/15WfCdxEDx4yw8DP03kN5Mc7h/CQNIghYkmSBAQfvA== ------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----- -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----- -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----- -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----- -MIIHyTCCBbGgAwIBAgIBATANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJJTDEW -MBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwg -Q2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNh -dGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0NjM2WhcNMzYwOTE3MTk0NjM2WjB9 -MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMi -U2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3Rh -cnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUA -A4ICDwAwggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZk -pMyONvg45iPwbm2xPN1yo4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rf -OQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/C -Ji/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/deMotHweXMAEtcnn6RtYT -Kqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt2PZE4XNi -HzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMM -Av+Z6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w -+2OqqGwaVLRcJXrJosmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+ -Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3 -Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVcUjyJthkqcwEKDwOzEmDyei+B -26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT37uMdBNSSwID -AQABo4ICUjCCAk4wDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAa4wHQYDVR0OBBYE -FE4L7xqkQFulF2mHMMo0aEPQQa7yMGQGA1UdHwRdMFswLKAqoCiGJmh0dHA6Ly9j -ZXJ0LnN0YXJ0Y29tLm9yZy9zZnNjYS1jcmwuY3JsMCugKaAnhiVodHRwOi8vY3Js -LnN0YXJ0Y29tLm9yZy9zZnNjYS1jcmwuY3JsMIIBXQYDVR0gBIIBVDCCAVAwggFM -BgsrBgEEAYG1NwEBATCCATswLwYIKwYBBQUHAgEWI2h0dHA6Ly9jZXJ0LnN0YXJ0 -Y29tLm9yZy9wb2xpY3kucGRmMDUGCCsGAQUFBwIBFilodHRwOi8vY2VydC5zdGFy -dGNvbS5vcmcvaW50ZXJtZWRpYXRlLnBkZjCB0AYIKwYBBQUHAgIwgcMwJxYgU3Rh -cnQgQ29tbWVyY2lhbCAoU3RhcnRDb20pIEx0ZC4wAwIBARqBl0xpbWl0ZWQgTGlh -YmlsaXR5LCByZWFkIHRoZSBzZWN0aW9uICpMZWdhbCBMaW1pdGF0aW9ucyogb2Yg -dGhlIFN0YXJ0Q29tIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFBvbGljeSBhdmFp -bGFibGUgYXQgaHR0cDovL2NlcnQuc3RhcnRjb20ub3JnL3BvbGljeS5wZGYwEQYJ -YIZIAYb4QgEBBAQDAgAHMDgGCWCGSAGG+EIBDQQrFilTdGFydENvbSBGcmVlIFNT -TCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTANBgkqhkiG9w0BAQUFAAOCAgEAFmyZ -9GYMNPXQhV59CuzaEE44HF7fpiUFS5Eyweg78T3dRAlbB0mKKctmArexmvclmAk8 -jhvh3TaHK0u7aNM5Zj2gJsfyOZEdUauCe37Vzlrk4gNXcGmXCPleWKYK34wGmkUW -FjgKXlf2Ysd6AgXmvB618p70qSmD+LIU424oh0TDkBreOKk8rENNZEXO3SipXPJz -ewT4F+irsfMuXGRuczE6Eri8sxHkfY+BUZo7jYn0TZNmezwD7dOaHZrzZVD1oNB1 -ny+v8OqCQ5j4aZyJecRDjkZy42Q2Eq/3JR44iZB3fsNrarnDy0RLrHiQi+fHLB5L -EUTINFInzQpdn4XBidUaePKVEFMy3YCEZnXZtWgo+2EuvoSoOMCZEoalHmdkrQYu -L6lwhceWD3yJZfWOQ1QOq92lgDmUYMA0yZZwLKMS9R9Ie70cfmu3nZD0Ijuu+Pwq -yvqCUqDvr0tVk+vBtfAii6w0TiYiBKGHLHVKt+V9E9e4DGTANtLJL4YSjCMJwRuC -O3NJo2pXh5Tl1njFmUNj403gdy3hZZlyaQQaRwnmDwFWJPsfvw55qVguucQJAX6V -um0ABj6y6koQOdjQK/W/7HW/lwLFCRsI3FU34oH7N4RDYiDK51ZLZer+bMEkkySh -NOsF/5oirpt9P/FlUQqmMGqz9IgcgA38corog14= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIHhzCCBW+gAwIBAgIBLTANBgkqhkiG9w0BAQsFADB9MQswCQYDVQQGEwJJTDEW -MBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwg -Q2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNh -dGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0NjM3WhcNMzYwOTE3MTk0NjM2WjB9 -MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMi -U2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3Rh -cnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUA -A4ICDwAwggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZk -pMyONvg45iPwbm2xPN1yo4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rf -OQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/C -Ji/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/deMotHweXMAEtcnn6RtYT -Kqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt2PZE4XNi -HzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMM -Av+Z6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w -+2OqqGwaVLRcJXrJosmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+ -Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3 -Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVcUjyJthkqcwEKDwOzEmDyei+B -26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT37uMdBNSSwID -AQABo4ICEDCCAgwwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD -VR0OBBYEFE4L7xqkQFulF2mHMMo0aEPQQa7yMB8GA1UdIwQYMBaAFE4L7xqkQFul -F2mHMMo0aEPQQa7yMIIBWgYDVR0gBIIBUTCCAU0wggFJBgsrBgEEAYG1NwEBATCC -ATgwLgYIKwYBBQUHAgEWImh0dHA6Ly93d3cuc3RhcnRzc2wuY29tL3BvbGljeS5w -ZGYwNAYIKwYBBQUHAgEWKGh0dHA6Ly93d3cuc3RhcnRzc2wuY29tL2ludGVybWVk -aWF0ZS5wZGYwgc8GCCsGAQUFBwICMIHCMCcWIFN0YXJ0IENvbW1lcmNpYWwgKFN0 -YXJ0Q29tKSBMdGQuMAMCAQEagZZMaW1pdGVkIExpYWJpbGl0eSwgcmVhZCB0aGUg -c2VjdGlvbiAqTGVnYWwgTGltaXRhdGlvbnMqIG9mIHRoZSBTdGFydENvbSBDZXJ0 -aWZpY2F0aW9uIEF1dGhvcml0eSBQb2xpY3kgYXZhaWxhYmxlIGF0IGh0dHA6Ly93 -d3cuc3RhcnRzc2wuY29tL3BvbGljeS5wZGYwEQYJYIZIAYb4QgEBBAQDAgAHMDgG -CWCGSAGG+EIBDQQrFilTdGFydENvbSBGcmVlIFNTTCBDZXJ0aWZpY2F0aW9uIEF1 -dGhvcml0eTANBgkqhkiG9w0BAQsFAAOCAgEAjo/n3JR5fPGFf59Jb2vKXfuM/gTF -wWLRfUKKvFO3lANmMD+x5wqnUCBVJX92ehQN6wQOQOY+2IirByeDqXWmN3PH/UvS -Ta0XQMhGvjt/UfzDtgUx3M2FIk5xt/JxXrAaxrqTi3iSSoX4eA+D/i+tLPfkpLst -0OcNOrg+zvZ49q5HJMqjNTbOx8aHmNrs++myziebiMMEofYLWWivydsQD032ZGNc -pRJvkrKTlMeIFw6Ttn5ii5B/q06f/ON1FE8qMt9bDeD1e5MNq6HPh+GlBEXoPBKl -CcWw0bdT82AUuoVpaiF8H3VhFyAXe2w7QSlc4axa0c2Mm+tgHRns9+Ww2vl5GKVF -P0lDV9LdJNUso/2RjSe15esUBppMeyG7Oq0wBhjA2MFrLH9ZXF2RsXAiV+uKa0hK -1Q8p7MZAwC+ITGgBF3f0JBlPvfrhsiAhS90a2Cl9qrjeVOwhVYBsHvUwyKMQ5bLm -KhQxw4UtjJixhlpPiVktucf3HMiKf8CdBUrmQk9io20ppB+Fq9vlgcitKj1MXVuE -JnHEhV5xJMqlG2zYYdMa4FTbzrqpMrUi9nNBCV24F10OD5mQ1kfabwo6YigUZ4LZ -8dCAWZvLMdibD4x3TrVoivJs9iQOLWxwxXPR3hTQcY+203sC9uO41Alua551hDnm -fyWl8kgAwKQB2j8= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIF2TCCA8GgAwIBAgIQXAuFXAvnWUHfV8w/f52oNjANBgkqhkiG9w0BAQUFADBk -MQswCQYDVQQGEwJjaDERMA8GA1UEChMIU3dpc3Njb20xJTAjBgNVBAsTHERpZ2l0 -YWwgQ2VydGlmaWNhdGUgU2VydmljZXMxGzAZBgNVBAMTElN3aXNzY29tIFJvb3Qg -Q0EgMTAeFw0wNTA4MTgxMjA2MjBaFw0yNTA4MTgyMjA2MjBaMGQxCzAJBgNVBAYT -AmNoMREwDwYDVQQKEwhTd2lzc2NvbTElMCMGA1UECxMcRGlnaXRhbCBDZXJ0aWZp -Y2F0ZSBTZXJ2aWNlczEbMBkGA1UEAxMSU3dpc3Njb20gUm9vdCBDQSAxMIICIjAN -BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0LmwqAzZuz8h+BvVM5OAFmUgdbI9 -m2BtRsiMMW8Xw/qabFbtPMWRV8PNq5ZJkCoZSx6jbVfd8StiKHVFXqrWW/oLJdih -FvkcxC7mlSpnzNApbjyFNDhhSbEAn9Y6cV9Nbc5fuankiX9qUvrKm/LcqfmdmUc/ -TilftKaNXXsLmREDA/7n29uj/x2lzZAeAR81sH8A25Bvxn570e56eqeqDFdvpG3F -EzuwpdntMhy0XmeLVNxzh+XTF3xmUHJd1BpYwdnP2IkCb6dJtDZd0KTeByy2dbco -kdaXvij1mB7qWybJvbCXc9qukSbraMH5ORXWZ0sKbU/Lz7DkQnGMU3nn7uHbHaBu -HYwadzVcFh4rUx80i9Fs/PJnB3r1re3WmquhsUvhzDdf/X/NTa64H5xD+SpYVUNF -vJbNcA78yeNmuk6NO4HLFWR7uZToXTNShXEuT46iBhFRyePLoW4xCGQMwtI89Tbo -19AOeCMgkckkKmUpWyL3Ic6DXqTz3kvTaI9GdVyDCW4pa8RwjPWd1yAv/0bSKzjC -L3UcPX7ape8eYIVpQtPM+GP+HkM5haa2Y0EQs3MevNP6yn0WR+Kn1dCjigoIlmJW -bjTb2QK5MHXjBNLnj8KwEUAKrNVxAmKLMb7dxiNYMUJDLXT5xp6mig/p/r+D5kNX -JLrvRjSq1xIBOO0CAwEAAaOBhjCBgzAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0hBBYw -FDASBgdghXQBUwABBgdghXQBUwABMBIGA1UdEwEB/wQIMAYBAf8CAQcwHwYDVR0j -BBgwFoAUAyUv3m+CATpcLNwroWm1Z9SM0/0wHQYDVR0OBBYEFAMlL95vggE6XCzc -K6FptWfUjNP9MA0GCSqGSIb3DQEBBQUAA4ICAQA1EMvspgQNDQ/NwNurqPKIlwzf -ky9NfEBWMXrrpA9gzXrzvsMnjgM+pN0S734edAY8PzHyHHuRMSG08NBsl9Tpl7Ik -Vh5WwzW9iAUPWxAaZOHHgjD5Mq2eUCzneAXQMbFamIp1TpBcahQq4FJHgmDmHtqB -sfsUC1rxn9KVuj7QG9YVHaO+htXbD8BJZLsuUBlL0iT43R4HVtA4oJVwIHaM190e -3p9xxCPvgxNcoyQVTSlAPGrEqdi3pkSlDfTgnXceQHAm/NrZNuR55LU/vJtlvrsR -ls/bxig5OgjOR1tTWsWZ/l2p3e9M1MalrQLmjAcSHm8D0W+go/MpvRLHUKKwf4ip -mXeascClOS5cfGniLLDqN2qk4Vrh9VDlg++luyqI54zb/W1elxmofmZ1a3Hqv7HH -b6D0jqTsNFFbjCYDcKF31QESVwA12yPeDooomf2xEG9L/zgtYE4snOtnta1J7ksf -rK/7DZBaZmBwXarNeNQk7shBoJMBkpxqnvy5JMWzFYJ+vq6VK+uxwNrjAWALXmms -hFZhvnEX/h0TD/7Gh0Xp/jKgGg0TpJRVcaUWi7rKibCyx/yP2FS1k2Kdzs9Z+z0Y -zirLNRWCXf9UIltxUvu3yf5gmwBBZPCqKuy2QkPOiWaByIufOVQDJdMWNY6E0F/6 -MBr1mmz0DlP5OlvRHA== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIID3TCCAsWgAwIBAgIOHaIAAQAC7LdggHiNtgYwDQYJKoZIhvcNAQEFBQAweTEL -MAkGA1UEBhMCREUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxJDAiBgNV -BAsTG1RDIFRydXN0Q2VudGVyIFVuaXZlcnNhbCBDQTEmMCQGA1UEAxMdVEMgVHJ1 -c3RDZW50ZXIgVW5pdmVyc2FsIENBIEkwHhcNMDYwMzIyMTU1NDI4WhcNMjUxMjMx -MjI1OTU5WjB5MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMgVHJ1c3RDZW50ZXIg -R21iSDEkMCIGA1UECxMbVEMgVHJ1c3RDZW50ZXIgVW5pdmVyc2FsIENBMSYwJAYD -VQQDEx1UQyBUcnVzdENlbnRlciBVbml2ZXJzYWwgQ0EgSTCCASIwDQYJKoZIhvcN -AQEBBQADggEPADCCAQoCggEBAKR3I5ZEr5D0MacQ9CaHnPM42Q9e3s9B6DGtxnSR -JJZ4Hgmgm5qVSkr1YnwCqMqs+1oEdjneX/H5s7/zA1hV0qq34wQi0fiU2iIIAI3T -fCZdzHd55yx4Oagmcw6iXSVphU9VDprvxrlE4Vc93x9UIuVvZaozhDrzznq+VZeu -jRIPFDPiUHDDSYcTvFHe15gSWu86gzOSBnWLknwSaHtwag+1m7Z3W0hZneTvWq3z -wZ7U10VOylY0Ibw+F1tvdwxIAUMpsN0/lm7mlaoMwCC2/T42J5zjXM9OgdwZu5GQ -fezmlwQek8wiSdeXhrYTCjxDI3d+8NzmzSQfO4ObNDqDNOMCAwEAAaNjMGEwHwYD -VR0jBBgwFoAUkqR1LKSevoFE63n8isWVpesQdXMwDwYDVR0TAQH/BAUwAwEB/zAO -BgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFJKkdSyknr6BROt5/IrFlaXrEHVzMA0G -CSqGSIb3DQEBBQUAA4IBAQAo0uCG1eb4e/CX3CJrO5UUVg8RMKWaTzqwOuAGy2X1 -7caXJ/4l8lfmXpWMPmRgFVp/Lw0BxbFg/UU1z/CyvwbZ71q+s2IhtNerNXxTPqYn -8aEt2hojnczd7Dwtnic0XQ/CNnm8yUpiLe1r2X1BQ3y2qsrtYbE3ghUJGooWMNjs -ydZHcnhLEEYUjl8Or+zHL6sQ17bxbuyGssLoDZJz3KL0Dzq/YSMQiZxIQG5wALPT -ujdEWBF6AmqI8Dc08BnprNRlc/ZpjGSUOnmFKbAWKwyCPwacx/0QK54PLLae4xW/ -2TYcuiUaUj0a7CIMHOCkoj3w6DnPgcB77V0fb8XQC9eY ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -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 -NvAsGnE8EXbfXRo+VGN4a2V+Hw== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEqjCCA5KgAwIBAgIOLmoAAQACH9dSISwRXDswDQYJKoZIhvcNAQEFBQAwdjEL -MAkGA1UEBhMCREUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxIjAgBgNV -BAsTGVRDIFRydXN0Q2VudGVyIENsYXNzIDIgQ0ExJTAjBgNVBAMTHFRDIFRydXN0 -Q2VudGVyIENsYXNzIDIgQ0EgSUkwHhcNMDYwMTEyMTQzODQzWhcNMjUxMjMxMjI1 -OTU5WjB2MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMgVHJ1c3RDZW50ZXIgR21i -SDEiMCAGA1UECxMZVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMiBDQTElMCMGA1UEAxMc -VEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMiBDQSBJSTCCASIwDQYJKoZIhvcNAQEBBQAD -ggEPADCCAQoCggEBAKuAh5uO8MN8h9foJIIRszzdQ2Lu+MNF2ujhoF/RKrLqk2jf -tMjWQ+nEdVl//OEd+DFwIxuInie5e/060smp6RQvkL4DUsFJzfb95AhmC1eKokKg -uNV/aVyQMrKXDcpK3EY+AlWJU+MaWss2xgdW94zPEfRMuzBwBJWl9jmM/XOBCH2J -XjIeIqkiRUuwZi4wzJ9l/fzLganx4Duvo4bRierERXlQXa7pIXSSTYtZgo+U4+lK -8edJsBTj9WLL1XK9H7nSn6DNqPoByNkN39r8R52zyFTfSUrxIan+GE7uSNQZu+99 -5OKdy1u2bv/jzVrndIIFuoAlOMvkaZ6vQaoahPUCAwEAAaOCATQwggEwMA8GA1Ud -EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTjq1RMgKHbVkO3 -kUrL84J6E1wIqzCB7QYDVR0fBIHlMIHiMIHfoIHcoIHZhjVodHRwOi8vd3d3LnRy -dXN0Y2VudGVyLmRlL2NybC92Mi90Y19jbGFzc18yX2NhX0lJLmNybIaBn2xkYXA6 -Ly93d3cudHJ1c3RjZW50ZXIuZGUvQ049VEMlMjBUcnVzdENlbnRlciUyMENsYXNz -JTIwMiUyMENBJTIwSUksTz1UQyUyMFRydXN0Q2VudGVyJTIwR21iSCxPVT1yb290 -Y2VydHMsREM9dHJ1c3RjZW50ZXIsREM9ZGU/Y2VydGlmaWNhdGVSZXZvY2F0aW9u -TGlzdD9iYXNlPzANBgkqhkiG9w0BAQUFAAOCAQEAjNfffu4bgBCzg/XbEeprS6iS -GNn3Bzn1LL4GdXpoUxUc6krtXvwjshOg0wn/9vYua0Fxec3ibf2uWWuFHbhOIprt -ZjluS5TmVfwLG4t3wVMTZonZKNaL80VKY7f9ewthXbhtvsPcW3nS7Yblok2+XnR8 -au0WOB9/WIFaGusyiC2y8zl3gK9etmF1KdsjTYjKUCjLhdLTEKJZbtOTVAB6okaV -hgWcqRmY5TFyDADiZ9lA4CQze28suVyrZZ0srHbqNZn1l7kPJOzHdiEoZa5X6AeI -dUpWoNIFOqTmjZKILPPy4cHGYdtBxceb9w4aUUXCYWvcZCcXjFq32nQozZfkvQ== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEqjCCA5KgAwIBAgIOSkcAAQAC5aBd1j8AUb8wDQYJKoZIhvcNAQEFBQAwdjEL -MAkGA1UEBhMCREUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxIjAgBgNV -BAsTGVRDIFRydXN0Q2VudGVyIENsYXNzIDMgQ0ExJTAjBgNVBAMTHFRDIFRydXN0 -Q2VudGVyIENsYXNzIDMgQ0EgSUkwHhcNMDYwMTEyMTQ0MTU3WhcNMjUxMjMxMjI1 -OTU5WjB2MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMgVHJ1c3RDZW50ZXIgR21i -SDEiMCAGA1UECxMZVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMyBDQTElMCMGA1UEAxMc -VEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMyBDQSBJSTCCASIwDQYJKoZIhvcNAQEBBQAD -ggEPADCCAQoCggEBALTgu1G7OVyLBMVMeRwjhjEQY0NVJz/GRcekPewJDRoeIMJW -Ht4bNwcwIi9v8Qbxq63WyKthoy9DxLCyLfzDlml7forkzMA5EpBCYMnMNWju2l+Q -Vl/NHE1bWEnrDgFPZPosPIlY2C8u4rBo6SI7dYnWRBpl8huXJh0obazovVkdKyT2 -1oQDZogkAHhg8fir/gKya/si+zXmFtGt9i4S5Po1auUZuV3bOx4a+9P/FRQI2Alq -ukWdFHlgfa9Aigdzs5OW03Q0jTo3Kd5c7PXuLjHCINy+8U9/I1LZW+Jk2ZyqBwi1 -Rb3R0DHBq1SfqdLDYmAD8bs5SpJKPQq5ncWg/jcCAwEAAaOCATQwggEwMA8GA1Ud -EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTUovyfs8PYA9NX -XAek0CSnwPIA1DCB7QYDVR0fBIHlMIHiMIHfoIHcoIHZhjVodHRwOi8vd3d3LnRy -dXN0Y2VudGVyLmRlL2NybC92Mi90Y19jbGFzc18zX2NhX0lJLmNybIaBn2xkYXA6 -Ly93d3cudHJ1c3RjZW50ZXIuZGUvQ049VEMlMjBUcnVzdENlbnRlciUyMENsYXNz -JTIwMyUyMENBJTIwSUksTz1UQyUyMFRydXN0Q2VudGVyJTIwR21iSCxPVT1yb290 -Y2VydHMsREM9dHJ1c3RjZW50ZXIsREM9ZGU/Y2VydGlmaWNhdGVSZXZvY2F0aW9u -TGlzdD9iYXNlPzANBgkqhkiG9w0BAQUFAAOCAQEANmDkcPcGIEPZIxpC8vijsrlN -irTzwppVMXzEO2eatN9NDoqTSheLG43KieHPOh6sHfGcMrSOWXaiQYUlN6AT0PV8 -TtXqluJucsG7Kv5sbviRmEb8yRtXW+rIGjs/sFGYPAfaLFkB2otE6OF0/ado3VS6 -g0bsyEa1+K+XwDsJHI/OcpY9M1ZwvJbL2NV9IJqDnxrcOfHFcqMRA/07QlIp2+gB -95tejNaNhk4Z+rwcvsUhpYeeeC422wlxo3I0+GzjBgnyXlal092Y+tTmBvTwtiBj -S+opvaqCZh77gaqnN60TGOaSw4HBM7uIHqHn4rS9MWwOUT1v+5ZWgOI2F9Hc5A== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDtjCCAp6gAwIBAgIOBcAAAQACQdAGCk3OdRAwDQYJKoZIhvcNAQEFBQAwdjEL -MAkGA1UEBhMCREUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxIjAgBgNV -BAsTGVRDIFRydXN0Q2VudGVyIENsYXNzIDQgQ0ExJTAjBgNVBAMTHFRDIFRydXN0 -Q2VudGVyIENsYXNzIDQgQ0EgSUkwHhcNMDYwMzIzMTQxMDIzWhcNMjUxMjMxMjI1 -OTU5WjB2MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMgVHJ1c3RDZW50ZXIgR21i -SDEiMCAGA1UECxMZVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgNCBDQTElMCMGA1UEAxMc -VEMgVHJ1c3RDZW50ZXIgQ2xhc3MgNCBDQSBJSTCCASIwDQYJKoZIhvcNAQEBBQAD -ggEPADCCAQoCggEBALXNTJytrlG7fEjFDSmGehSt2VA9CXIgDRS2Y8b+WJ7gIV7z -jyIZ3E6RIM1viCmis8GsKnK6i1S4QF/yqvhDhsIwXMynXX/GCEnkDjkvjhjWkd0j -FnmA22xIHbzB3ygQY9GB493fL3l1oht48pQB5hBiecugfQLANIJ7x8CtHUzXapZ2 -W78mhEj9h/aECqqSB5lIPGG8ToVYx5ct/YFKocabEvVCUNFkPologiJw3fX64yhC -L04y87OjNopq1mJcrPoBbbTgci6VaLTxkwzGioLSHVPqfOA/QrcSWrjN2qUGZ8uh -d32llvCSHmcOHUJG5vnt+0dTf1cERh9GX8eu4I8CAwEAAaNCMEAwDwYDVR0TAQH/ -BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFB/quz4lGwa9pd1iBX7G -TFq/6A9DMA0GCSqGSIb3DQEBBQUAA4IBAQBYpCubTPfkpJKknGWYGWIi/HIy6QRd -xMRwLVpG3kxHiiW5ot3u6hKvSI3vK2fbO8w0mCr3CEf/Iq978fTr4jgCMxh1KBue -dmWsiANy8jhHHYz1nwqIUxAUu4DlDLNdjRfuHhkcho0UZ3iMksseIUn3f9MYv5x5 -+F0IebWqak2SNmy8eesOPXmK2PajVnBd3ttPedJ60pVchidlvqDTB4FAVd0Qy+BL -iILAkH0457+W4Ze6mqtCD9Of2J4VMxHL94J59bXAQVaS4d9VA61Iz9PyLrHHLVZM -ZHQqMc7cdalUR6SnQnIJ5+ECpkeyBM1CE+FhDOB4OiIgohxgQoaH96Xm ------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----- ------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----- -MIID4TCCAsmgAwIBAgIOYyUAAQACFI0zFQLkbPQwDQYJKoZIhvcNAQEFBQAwezEL -MAkGA1UEBhMCREUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxJDAiBgNV -BAsTG1RDIFRydXN0Q2VudGVyIFVuaXZlcnNhbCBDQTEoMCYGA1UEAxMfVEMgVHJ1 -c3RDZW50ZXIgVW5pdmVyc2FsIENBIElJSTAeFw0wOTA5MDkwODE1MjdaFw0yOTEy -MzEyMzU5NTlaMHsxCzAJBgNVBAYTAkRFMRwwGgYDVQQKExNUQyBUcnVzdENlbnRl -ciBHbWJIMSQwIgYDVQQLExtUQyBUcnVzdENlbnRlciBVbml2ZXJzYWwgQ0ExKDAm -BgNVBAMTH1RDIFRydXN0Q2VudGVyIFVuaXZlcnNhbCBDQSBJSUkwggEiMA0GCSqG -SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDC2pxisLlxErALyBpXsq6DFJmzNEubkKLF -5+cvAqBNLaT6hdqbJYUtQCggbergvbFIgyIpRJ9Og+41URNzdNW88jBmlFPAQDYv -DIRlzg9uwliT6CwLOunBjvvya8o84pxOjuT5fdMnnxvVZ3iHLX8LR7PH6MlIfK8v -zArZQe+f/prhsq75U7Xl6UafYOPfjdN/+5Z+s7Vy+EutCHnNaYlAJ/Uqwa1D7KRT -yGG299J5KmcYdkhtWyUB0SbFt1dpIxVbYYqt8Bst2a9c8SaQaanVDED1M4BDj5yj -dipFtK+/fz6HP3bFzSreIMUWWMv5G/UPyw0RUmS40nZid4PxWJ//AgMBAAGjYzBh -MB8GA1UdIwQYMBaAFFbn4VslQ4Dg9ozhcbyO5YAvxEjiMA8GA1UdEwEB/wQFMAMB -Af8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRW5+FbJUOA4PaM4XG8juWAL8RI -4jANBgkqhkiG9w0BAQUFAAOCAQEAg8ev6n9NCjw5sWi+e22JLumzCecYV42Fmhfz -dkJQEw/HkG8zrcVJYCtsSVgZ1OK+t7+rSbyUyKu+KGwWaODIl0YgoGhnYIg5IFHY -aAERzqf2EQf27OysGh+yZm5WZ2B6dF7AbZc2rrUNXWZzwCUyRdhKBgePxLcHsU0G -DeGl6/R1yrqc0L2z0zIkTO5+4nYES0lT2PLpVDP85XEfPRRclkvxOvIAu2y0+pZV -CIgJwcyRGSmwIC3/yzikQOEXvnlhgP8HA4ZMTnsGnxGGjYnuJ8Tb4rwZjgvDwxPH -LQNjO9Po5KIqwoIIlBZU8O8fJ5AluA0OKBtHd0e9HKgl8ZS0Zg== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEPTCCAyWgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBvzE/MD0GA1UEAww2VMOc -UktUUlVTVCBFbGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sx -c8SxMQswCQYDVQQGEwJUUjEPMA0GA1UEBwwGQW5rYXJhMV4wXAYDVQQKDFVUw5xS -S1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUgQmlsacWfaW0gR8O8dmVubGnEn2kg -SGl6bWV0bGVyaSBBLsWeLiAoYykgQXJhbMSxayAyMDA3MB4XDTA3MTIyNTE4Mzcx -OVoXDTE3MTIyMjE4MzcxOVowgb8xPzA9BgNVBAMMNlTDnFJLVFJVU1QgRWxla3Ry -b25payBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsTELMAkGA1UEBhMC -VFIxDzANBgNVBAcMBkFua2FyYTFeMFwGA1UECgxVVMOcUktUUlVTVCBCaWxnaSDE -sGxldGnFn2ltIHZlIEJpbGnFn2ltIEfDvHZlbmxpxJ9pIEhpem1ldGxlcmkgQS7F -ni4gKGMpIEFyYWzEsWsgMjAwNzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC -ggEBAKu3PgqMyKVYFeaK7yc9SrToJdPNM8Ig3BnuiD9NYvDdE3ePYakqtdTyuTFY -KTsvP2qcb3N2Je40IIDu6rfwxArNK4aUyeNgsURSsloptJGXg9i3phQvKUmi8wUG -+7RP2qFsmmaf8EMJyupyj+sA1zU511YXRxcw9L6/P8JorzZAwan0qafoEGsIiveG -HtyaKhUG9qPw9ODHFNRRf8+0222vR5YXm3dx2KdxnSQM9pQ/hTEST7ruToK4uT6P -IzdezKKqdfcYbwnTrqdUKDT74eA7YH2gvnmJhsifLfkKS8RQouf9eRbHegsYz85M -733WB2+Y8a+xwXrXgTW4qhe04MsCAwEAAaNCMEAwHQYDVR0OBBYEFCnFkKslrxHk -Yb+j/4hhkeYO/pyBMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0G -CSqGSIb3DQEBBQUAA4IBAQAQDdr4Ouwo0RSVgrESLFF6QSU2TJ/sPx+EnWVUXKgW -AkD6bho3hO9ynYYKVZ1WKKxmLNA6VpM0ByWtCLCPyA8JWcqdmBzlVPi5RX9ql2+I -aE1KBiY3iAIOtsbWcpnOa3faYjGkVh+uX4132l32iPwa2Z61gfAyuOOI0JzzaqC5 -mxRZNTZPz/OOXl0XrRWV2N2y1RVuAE6zS89mlOTgzbUF2mNXi+WzqtvALhyQRNsa -XRik7r4EW5nVcV9VZWRi1aKbBFmGyGJ353yCRWo9F7/snXUMrqNvWtMvmDb08PUZ -qxFdyKbjKlhqQgnDvZImZjINXQhVdP+MmNAKpoRq0Tl9 ------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----- \ No newline at end of file diff --git a/client/engine/utils/CMakeLists.txt b/client/engine/utils/CMakeLists.txt index 772ff07e1..a840a96ba 100644 --- a/client/engine/utils/CMakeLists.txt +++ b/client/engine/utils/CMakeLists.txt @@ -11,8 +11,10 @@ if (WIN32) ras_service_win.h ) elseif(APPLE) - # no files - + target_sources(engine PRIVATE + interfaceutils_mac.cpp + interfaceutils_mac.h + ) elseif(UNIX) target_sources(engine PRIVATE dnsscripts_linux.cpp diff --git a/client/engine/utils/interfaceutils_mac.cpp b/client/engine/utils/interfaceutils_mac.cpp new file mode 100644 index 000000000..4184b7b8e --- /dev/null +++ b/client/engine/utils/interfaceutils_mac.cpp @@ -0,0 +1,184 @@ + +#include "interfaceutils_mac.h" + +#include "utils/macutils.h" +#include "utils/network_utils/network_utils_mac.h" +#include "utils/logger.h" +#include "utils/utils.h" + +InterfaceUtils_mac::InterfaceUtils_mac() + : helper_(nullptr) +{ +} + +InterfaceUtils_mac::~InterfaceUtils_mac() +{ +} + +void InterfaceUtils_mac::setHelper(Helper_mac *helper) +{ + helper_ = helper; +} + +QString ssidOfInterface(const QString &networkInterface, Helper_mac *helper = nullptr) +{ + // In MacOS 15.0, Apple removed the SSID from networksetup -getairportnetwork as well. Use wdutil to get the SSID. + if (MacUtils::isOsVersionAtLeast(15, 0)) { + if (helper) { + return helper->getInterfaceSsid(networkInterface); + } + // In MacOS 14.4, Apple removed the SSID from scutil output, use an alternative method + } else if (MacUtils::isOsVersionAtLeast(14, 4)) { + QString command = "networksetup -getairportnetwork " + networkInterface + " | head -n 1 | cut -s -d ':' -f 2"; + QString strReply = Utils::execCmd(command).trimmed(); + if (strReply.length() > 32) { + // SSIDs are at most 32 octets, so if the string is longer than that, it's probably an error message. + strReply = ""; + } + return strReply; + } + // Older versions of macOS should use scutil + QString command = "echo 'show State:/Network/Interface/" + networkInterface + "/AirPort' | scutil | grep SSID_STR | sed -e 's/.*SSID_STR : //'"; + return Utils::execCmd(command).trimmed(); +} + +bool isAdapterActive(const QString &networkInterface) +{ + QString cmdInterfaceStatus = "ifconfig " + networkInterface + " | grep status | awk '{print $2}'"; + QString statusResult = Utils::execCmd(cmdInterfaceStatus).trimmed(); + return statusResult == "active"; +} + +QList currentNetworkHwInterfaces() +{ + QList result; + QString cmd = "networksetup -listnetworkserviceorder | grep Device | sed -e 's/.*Device: //' -e 's/)//'"; + QString response = Utils::execCmd(cmd); + + const QList lines = response.split("\n"); + + for (const QString &line : lines) { + const QString iName = line.trimmed(); + if (iName != "") { + if (NetworkUtils_mac::isAdapterUp(iName)) { + result.append(iName); + } + } + } + + return result; +} + +QMap currentHardwareInterfaceIndexes() +{ + QMap result; + QString cmd = "networksetup -listallhardwareports | grep Device | awk '{print $2}'"; + QString response = Utils::execCmd(cmd); + + const QList lines = response.split("\n"); + + int index = 1; + for (const QString &line : lines) + { + const QString iName = line.trimmed(); + if (iName != "") + { + result.insert(iName, index); + } + index++; + } + + return result; +} + +QVector currentlyUpNetInterfaces() +{ + auto isUp = [](const types::NetworkInterface &ni) { + return NetworkUtils_mac::isAdapterUp(ni.interfaceName); + }; + + QVector interfaces = InterfaceUtils_mac::instance().currentNetworkInterfaces(false); + QVector upInterfaces; + std::copy_if(interfaces.begin(), interfaces.end(), std::back_inserter(upInterfaces), isUp); + return upInterfaces; +} + +const types::NetworkInterface InterfaceUtils_mac::currentNetworkInterface() +{ + QVector ni = currentNetworkInterfaces(false); + if (ni.size() > 0) { + return ni[0]; + } + + return types::NetworkInterface::noNetworkInterface(); +} + + +QVector InterfaceUtils_mac::currentSpoofedInterfaces() +{ + QVector spoofed; + QVector currentInterfaces = currentlyUpNetInterfaces(); + for (const types::NetworkInterface &ii : currentInterfaces) { + const QString &interfaceName = ii.interfaceName; + + if (NetworkUtils_mac::isInterfaceSpoofed(interfaceName)) { + spoofed << ii; + } + } + + return spoofed; +} + +QVector InterfaceUtils_mac::currentNetworkInterfaces(bool includeNoInterface) +{ + QVector networkInterfaces; + + if (includeNoInterface) { + networkInterfaces << types::NetworkInterface::noNetworkInterface(); + } + + const QList hwInterfaces = currentNetworkHwInterfaces(); + const QMap interfaceIndexes = currentHardwareInterfaceIndexes(); + + for (const QString &interfaceName : hwInterfaces) { + types::NetworkInterface networkInterface; + + int index = 0; + if (interfaceIndexes.contains(interfaceName)) index = interfaceIndexes[interfaceName]; + networkInterface.interfaceIndex = index; + networkInterface.interfaceName = interfaceName; + + bool wifi = NetworkUtils_mac::isWifiAdapter(interfaceName); + QString macAddress = NetworkUtils_mac::macAddressFromInterfaceName(interfaceName); + networkInterface.physicalAddress = macAddress; + + // TODO: **JDRM** see if we can get a useful adapter friendlyName (e.g. Belkin USB-C Ethernet) + // from a macOS API. + if (wifi) { + networkInterface.interfaceType = NETWORK_INTERFACE_WIFI; + QString ssid = ssidOfInterface(interfaceName, helper_); + networkInterface.networkOrSsid = ssid; + networkInterface.friendlyName = "Wi-Fi"; + } + else { + networkInterface.interfaceType = NETWORK_INTERFACE_ETH; + networkInterface.networkOrSsid = macAddress; + networkInterface.friendlyName = "Ethernet"; + } + + networkInterface.active = isAdapterActive(interfaceName); + networkInterfaces << networkInterface; + + // TODO: The following fields should be removeable from types::NetworkInterface: + // interface_guid + // state + // metric + // dw_type + // device_name + // connector_present + // end_point_interface + // active ? + } + + return networkInterfaces; +} diff --git a/client/engine/utils/interfaceutils_mac.h b/client/engine/utils/interfaceutils_mac.h new file mode 100644 index 000000000..45738c663 --- /dev/null +++ b/client/engine/utils/interfaceutils_mac.h @@ -0,0 +1,27 @@ +#include +#include + +#include "engine/helper/helper_mac.h" +#include "types/networkinterface.h" + +class InterfaceUtils_mac +{ +public: + static InterfaceUtils_mac &instance() + { + static InterfaceUtils_mac iu; + return iu; + } + + void setHelper(Helper_mac *helper); + + const types::NetworkInterface currentNetworkInterface(); + QVector currentNetworkInterfaces(bool includeNoInterface); + QVector currentSpoofedInterfaces(); + +private: + InterfaceUtils_mac(); + ~InterfaceUtils_mac(); + + Helper_mac *helper_; +}; \ No newline at end of file diff --git a/client/gui/CMakeLists.txt b/client/gui/CMakeLists.txt index f71fef131..775698ccb 100644 --- a/client/gui/CMakeLists.txt +++ b/client/gui/CMakeLists.txt @@ -41,8 +41,6 @@ qt6_create_translation(WS_QM_FILES ${CMAKE_CURRENT_SOURCE_DIR}/.. ${WS_TS_FILES} set(PROJECT_SOURCES # --- sources and headers --- - blockconnect.cpp - blockconnect.h dpiscaleawarewidget.cpp dpiscaleawarewidget.h dpiscalemanager.cpp @@ -52,8 +50,6 @@ set(PROJECT_SOURCES generalmessage.h generalmessagecontroller.cpp generalmessagecontroller.h - languagecontroller.cpp - languagecontroller.h loginattemptscontroller.cpp loginattemptscontroller.h mainwindow.cpp @@ -107,10 +103,10 @@ endif (UNIX) target_include_directories(gui PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../engine ${CMAKE_CURRENT_SOURCE_DIR}/../common + ${CMAKE_CURRENT_SOURCE_DIR}/../base ) add_subdirectory(application) -add_subdirectory(backend) add_subdirectory(bottominfowidget) add_subdirectory(commongraphics) add_subdirectory(commonwidgets) @@ -120,13 +116,10 @@ add_subdirectory(emergencyconnectwindow) add_subdirectory(externalconfig) add_subdirectory(generalmessage) add_subdirectory(graphicresources) -add_subdirectory(launchonstartup) -add_subdirectory(localipcserver) add_subdirectory(locations) add_subdirectory(locationswindow) add_subdirectory(log) add_subdirectory(loginwindow) -add_subdirectory(multipleaccountdetection) add_subdirectory(newsfeedwindow) add_subdirectory(overlaysconnectwindow) add_subdirectory(preferenceswindow) @@ -134,6 +127,6 @@ add_subdirectory(protocolwindow) add_subdirectory(systemtray) add_subdirectory(tooltips) add_subdirectory(twofactorauth) -add_subdirectory(utils) add_subdirectory(widgetutils) add_subdirectory(tests) +add_subdirectory(utils) diff --git a/client/gui/application/singleappinstance.cpp b/client/gui/application/singleappinstance.cpp index a6546dcca..71e515db5 100644 --- a/client/gui/application/singleappinstance.cpp +++ b/client/gui/application/singleappinstance.cpp @@ -101,10 +101,9 @@ bool SingleAppInstancePrivate::isRunning() if (lockFile_.isNull()) { - socketName_ = QLatin1String("windscribe-singleappistance.socket"); + socketName_ = "/var/run/windscribe/windscribe-singleappistance.socket"; - lockFile_.reset(new QLockFile(QStandardPaths::writableLocation(QStandardPaths::TempLocation) + - QLatin1String("/windscribe-singleappistance.lock"))); + lockFile_.reset(new QLockFile("/var/run/windscribe/windscribe.lock")); lockFile_->setStaleLockTime(0); lockFile_->tryLock(); diff --git a/client/gui/application/windscribeapplication.h b/client/gui/application/windscribeapplication.h index 6585f2de6..5b3d45840 100644 --- a/client/gui/application/windscribeapplication.h +++ b/client/gui/application/windscribeapplication.h @@ -1,7 +1,6 @@ #pragma once #include -#include #ifdef Q_OS_WIN #include "windowsnativeeventfilter.h" @@ -58,5 +57,4 @@ class WindscribeApplication : public QApplication #ifdef Q_OS_MAC ExitHandler_mac exitHandlerMac_; #endif - QTranslator translator; }; diff --git a/client/gui/connectwindow/locationsbutton.h b/client/gui/connectwindow/locationsbutton.h index 675dbbb88..655d6ca49 100644 --- a/client/gui/connectwindow/locationsbutton.h +++ b/client/gui/connectwindow/locationsbutton.h @@ -3,7 +3,7 @@ #include #include #include -#include "../backend/backend.h" +#include "backend/backend.h" #include "commongraphics/clickablegraphicsobject.h" #include "commongraphics/imageitem.h" diff --git a/client/gui/emergencyconnectwindow/emergencyconnectwindowitem.h b/client/gui/emergencyconnectwindow/emergencyconnectwindowitem.h index 54b4ceb82..149a248cd 100644 --- a/client/gui/emergencyconnectwindow/emergencyconnectwindowitem.h +++ b/client/gui/emergencyconnectwindow/emergencyconnectwindowitem.h @@ -2,7 +2,7 @@ #include #include -#include "../backend/backend.h" +#include "backend/backend.h" #include "commongraphics/bubblebutton.h" #include "commongraphics/escapebutton.h" #include "commongraphics/iconbutton.h" diff --git a/client/gui/externalconfig/externalconfigwindowitem.h b/client/gui/externalconfig/externalconfigwindowitem.h index a2b208910..984e06548 100644 --- a/client/gui/externalconfig/externalconfigwindowitem.h +++ b/client/gui/externalconfig/externalconfigwindowitem.h @@ -2,7 +2,7 @@ #include #include -#include "../backend/backend.h" +#include "backend/backend.h" #include "commongraphics/bubblebutton.h" #include "commongraphics/escapebutton.h" #include "commongraphics/iconbutton.h" diff --git a/client/gui/localipcserver/localipcserver.cpp b/client/gui/localipcserver/localipcserver.cpp deleted file mode 100644 index 93f3738c2..000000000 --- a/client/gui/localipcserver/localipcserver.cpp +++ /dev/null @@ -1,273 +0,0 @@ -#include "localipcserver.h" -#include "utils/ws_assert.h" -#include "utils/utils.h" -#include "utils/logger.h" -#include "ipc/server.h" -#include "ipc/clicommands.h" -#include "backend/persistentstate.h" - -LocalIPCServer::LocalIPCServer(Backend *backend, QObject *parent) : QObject(parent) - , backend_(backend) -{ - connect(backend_, &Backend::connectStateChanged, this, &LocalIPCServer::onBackendConnectStateChanged); - connect(backend_, &Backend::firewallStateChanged, this, &LocalIPCServer::onBackendFirewallStateChanged); - connect(backend_, &Backend::loginFinished, this, &LocalIPCServer::onBackendLoginFinished); - connect(backend_, &Backend::signOutFinished, this, &LocalIPCServer::onBackendSignOutFinished); -} - -LocalIPCServer::~LocalIPCServer() -{ - for (IPC::Connection * connection : connections_) - { - connection->close(); - delete connection; - } - connections_.clear(); - SAFE_DELETE(server_); - qCDebug(LOG_CLI_IPC) << "IPC server for CLI stopped"; -} - -void LocalIPCServer::start() -{ - WS_ASSERT(server_ == nullptr); - server_ = new IPC::Server(); - connect(server_, &IPC::Server::newConnection, this, &LocalIPCServer::onServerCallbackAcceptFunction); - - if (!server_->start()) - { - qCDebug(LOG_CLI_IPC) << "Can't start IPC server for CLI"; - } - else - { - qCDebug(LOG_CLI_IPC) << "IPC server for CLI started"; - } -} - -void LocalIPCServer::sendLocationsShown() -{ - for (IPC::Connection * connection : connections_) - { - IPC::CliCommands::LocationsShown cmd; - connection->sendCommand(cmd); - } -} - -void LocalIPCServer::onServerCallbackAcceptFunction(IPC::Connection *connection) -{ - qCDebug(LOG_IPC) << "Client connected:" << connection; - - connections_.append(connection); - - connect(connection, &IPC::Connection::newCommand, this, &LocalIPCServer::onConnectionCommandCallback); - connect(connection, &IPC::Connection::stateChanged, this, &LocalIPCServer::onConnectionStateCallback); -} - -void LocalIPCServer::onConnectionCommandCallback(IPC::Command *command, IPC::Connection * /*connection*/) -{ - if (command->getStringId() ==IPC::CliCommands::ShowLocations::getCommandStringId()) - { - emit showLocations(); - } - else if (command->getStringId() == IPC::CliCommands::Connect::getCommandStringId()) - { - IPC::CliCommands::Connect *cmd = static_cast(command); - QString locationStr = cmd->location_; - LocationID lid; - if (locationStr.isEmpty()) - { - lid = PersistentState::instance().lastLocation(); - } - else if (locationStr == "best") - { - lid = backend_->locationsModelManager()->getBestLocationId(); - } - else - { - lid = backend_->locationsModelManager()->findLocationByFilter(locationStr); - } - - IPC::CliCommands::ConnectToLocationAnswer cmd_send; - if (lid.isValid()) - { - cmd_send.isSuccess_ = true; - cmd_send.location_ = lid.city(); - } - else - { - cmd_send.isSuccess_ = false; - } - sendCommand(cmd_send); - - if (lid.isValid()) - { - emit connectToLocation(lid); - } - } - else if (command->getStringId() == IPC::CliCommands::Disconnect::getCommandStringId()) - { - if (backend_->isDisconnected()) - { - IPC::CliCommands::AlreadyDisconnected cmd; - sendCommand(cmd); - } - else - { - backend_->sendDisconnect(); - } - } - else if (command->getStringId() == IPC::CliCommands::GetState::getCommandStringId()) - { - IPC::CliCommands::State cmd; - cmd.isLoggedIn_ = isLoggedIn_; - cmd.waitingForLoginInfo_ = !backend_->isCanLoginWithAuthHash(); - cmd.connectState_ = backend_->currentConnectState(); - cmd.location_ = backend_->currentLocation(); - sendCommand(cmd); - } - else if (command->getStringId() == IPC::CliCommands::Firewall::getCommandStringId()) - { - IPC::CliCommands::Firewall *cmd = static_cast(command); - if (cmd->isEnable_) - { - if (!backend_->isFirewallEnabled()) - { - backend_->firewallOn(); - } - else - { - IPC::CliCommands::FirewallStateChanged cmd; - cmd.isFirewallEnabled_ = true; - cmd.isFirewallAlwaysOn_ = backend_->isFirewallAlwaysOn(); - sendCommand(cmd); - } - } - else - { - if (!backend_->isFirewallEnabled()) - { - IPC::CliCommands::FirewallStateChanged cmd; - cmd.isFirewallEnabled_ = false; - cmd.isFirewallAlwaysOn_ = backend_->isFirewallAlwaysOn(); - sendCommand(cmd); - } - else if (!backend_->isFirewallAlwaysOn()) - { - backend_->firewallOff(); - } - else - { - IPC::CliCommands::FirewallStateChanged cmd; - cmd.isFirewallEnabled_ = true; - cmd.isFirewallAlwaysOn_ = backend_->isFirewallAlwaysOn(); - sendCommand(cmd); - } - } - } - else if (command->getStringId() == IPC::CliCommands::Login::getCommandStringId()) - { - if (isLoggedIn_) { - notifyCliLoginFinished(); - } - else - { - connect(backend_, &Backend::loginFinished, this, &LocalIPCServer::notifyCliLoginFinished); - connect(backend_, &Backend::loginError, this, &LocalIPCServer::notifyCliLoginFailed); - IPC::CliCommands::Login *cmd = static_cast(command); - emit attemptLogin(cmd->username_, cmd->password_, cmd->code2fa_); - } - } - else if (command->getStringId() == IPC::CliCommands::SignOut::getCommandStringId()) - { - if (isLoggedIn_) - { - connect(backend_, &Backend::signOutFinished, this, &LocalIPCServer::notifyCliSignOutFinished); - IPC::CliCommands::SignOut *cmd = static_cast(command); - backend_->signOut(cmd->isKeepFirewallOn_); - } - else { - notifyCliSignOutFinished(); - } - } -} - -void LocalIPCServer::onConnectionStateCallback(int state, IPC::Connection *connection) -{ - if (state == IPC::CONNECTION_DISCONNECTED) - { - qCDebug(LOG_BASIC) << "CLI disconnected from GUI server"; - connections_.removeOne(connection); - connection->close(); - delete connection; - } - else if (state == IPC::CONNECTION_ERROR) - { - qCDebug(LOG_BASIC) << "CLI disconnected from GUI server with error"; - connections_.removeOne(connection); - connection->close(); - delete connection; - } - -} - -void LocalIPCServer::onBackendConnectStateChanged(const types::ConnectState &connectState) -{ - IPC::CliCommands::ConnectStateChanged cmd; - cmd.connectState = connectState; - sendCommand(cmd); -} - -void LocalIPCServer::onBackendFirewallStateChanged(bool isEnabled) -{ - IPC::CliCommands::FirewallStateChanged cmd; - cmd.isFirewallEnabled_ = isEnabled; - cmd.isFirewallAlwaysOn_ = backend_->isFirewallAlwaysOn(); - sendCommand(cmd); -} - -void LocalIPCServer::onBackendLoginFinished(bool /*isLoginFromSavedSettings*/) -{ - isLoggedIn_ = true; -} - -void LocalIPCServer::onBackendSignOutFinished() -{ - isLoggedIn_ = false; -} - -void LocalIPCServer::sendCommand(const IPC::Command &command) -{ - for (IPC::Connection * connection : connections_) - { - connection->sendCommand(command); - } -} - -void LocalIPCServer::notifyCliLoginFinished() -{ - sendLoginResult(true, QString()); -} - -void LocalIPCServer::notifyCliLoginFailed(LOGIN_RET loginError, const QString &errorMessage) -{ - Q_UNUSED(loginError) - sendLoginResult(false, errorMessage); -} - -void LocalIPCServer::notifyCliSignOutFinished() -{ - disconnect(backend_, &Backend::signOutFinished, this, &LocalIPCServer::notifyCliSignOutFinished); - IPC::CliCommands::SignedOut cmd; - sendCommand(cmd); -} - -void LocalIPCServer::sendLoginResult(bool isLoggedIn, const QString &errorMessage) -{ - disconnect(backend_, &Backend::loginFinished, this, &LocalIPCServer::notifyCliLoginFinished); - disconnect(backend_, &Backend::loginError, this, &LocalIPCServer::notifyCliLoginFailed); - IPC::CliCommands::LoginResult cmd; - cmd.isLoggedIn_ = isLoggedIn; - if (!errorMessage.isEmpty()) { - cmd.loginError_ = errorMessage; - } - sendCommand(cmd); -} diff --git a/client/gui/localipcserver/localipcserver.h b/client/gui/localipcserver/localipcserver.h deleted file mode 100644 index 2faaa812b..000000000 --- a/client/gui/localipcserver/localipcserver.h +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once - -#include -#include "ipc/server.h" -#include "ipc/connection.h" -#include "backend/backend.h" - -// Local server for receive and execute commands from local processes (currently only from the CLI). -class LocalIPCServer : public QObject -{ - Q_OBJECT -public: - explicit LocalIPCServer(Backend *backend, QObject *parent = nullptr); - ~LocalIPCServer(); - - void start(); - void sendLocationsShown(); - -signals: - void showLocations(); - void connectToLocation(const LocationID &id); - void attemptLogin(const QString &username, const QString &password, const QString &code2fa); - -private slots: - void onServerCallbackAcceptFunction(IPC::Connection *connection); - void onConnectionCommandCallback(IPC::Command *command, IPC::Connection *connection); - void onConnectionStateCallback(int state, IPC::Connection *connection); - - void onBackendConnectStateChanged(const types::ConnectState &connectState); - void onBackendFirewallStateChanged(bool isEnabled); - void onBackendLoginFinished(bool isLoginFromSavedSettings); - void onBackendSignOutFinished(); - void notifyCliSignOutFinished(); - void notifyCliLoginFinished(); - void notifyCliLoginFailed(LOGIN_RET loginError, const QString &errorMessage); - -private: - Backend *backend_; - IPC::Server *server_ = nullptr; - QVector connections_; - bool isLoggedIn_ = false; - - void sendCommand(const IPC::Command &command); - void sendLoginResult(bool isLoggedIn, const QString &errorMessage); -}; diff --git a/client/gui/locations/CMakeLists.txt b/client/gui/locations/CMakeLists.txt index 439f295c4..8303c2446 100644 --- a/client/gui/locations/CMakeLists.txt +++ b/client/gui/locations/CMakeLists.txt @@ -1,11 +1 @@ -target_sources(gui PRIVATE - locationsmodel_manager.cpp - locationsmodel_manager.h - locationsmodel_roles.h -) - -add_subdirectory(model) add_subdirectory(view) - - - diff --git a/client/gui/locations/view/cityitemdelegate.cpp b/client/gui/locations/view/cityitemdelegate.cpp index 12c74867f..19874366c 100644 --- a/client/gui/locations/view/cityitemdelegate.cpp +++ b/client/gui/locations/view/cityitemdelegate.cpp @@ -3,7 +3,7 @@ #include #include -#include "../locationsmodel_roles.h" +#include "locations/locationsmodel_roles.h" #include "clickableandtooltiprects.h" #include "commongraphics/commongraphics.h" #include "dpiscalemanager.h" diff --git a/client/gui/locations/view/countryitemdelegate.cpp b/client/gui/locations/view/countryitemdelegate.cpp index 78d7f8088..a068d341d 100644 --- a/client/gui/locations/view/countryitemdelegate.cpp +++ b/client/gui/locations/view/countryitemdelegate.cpp @@ -4,7 +4,7 @@ #include #include -#include "../locationsmodel_roles.h" +#include "locations/locationsmodel_roles.h" #include "commongraphics/commongraphics.h" #include "dpiscalemanager.h" #include "graphicresources/fontmanager.h" diff --git a/client/gui/locations/view/expandableitemswidget.cpp b/client/gui/locations/view/expandableitemswidget.cpp index 3d509eb04..f90e5ade4 100644 --- a/client/gui/locations/view/expandableitemswidget.cpp +++ b/client/gui/locations/view/expandableitemswidget.cpp @@ -7,7 +7,7 @@ #include #include "dpiscalemanager.h" -#include "../locationsmodel_roles.h" +#include "locations/locationsmodel_roles.h" #include "clickableandtooltiprects.h" #include "commongraphics/commongraphics.h" diff --git a/client/gui/loginwindow/credentialswindowitem.h b/client/gui/loginwindow/credentialswindowitem.h index f9067851a..3a5b2c325 100644 --- a/client/gui/loginwindow/credentialswindowitem.h +++ b/client/gui/loginwindow/credentialswindowitem.h @@ -4,7 +4,7 @@ #include #include #include -#include "../backend/backend.h" +#include "backend/backend.h" #include "loginwindowtypes.h" #include "loginyesnobutton.h" #include "usernamepasswordentry.h" diff --git a/client/gui/loginwindow/welcomewindowitem.h b/client/gui/loginwindow/welcomewindowitem.h index f1d51d9d4..c6b4db448 100644 --- a/client/gui/loginwindow/welcomewindowitem.h +++ b/client/gui/loginwindow/welcomewindowitem.h @@ -4,7 +4,7 @@ #include #include #include -#include "../backend/backend.h" +#include "backend/backend.h" #include "loginyesnobutton.h" #include "usernamepasswordentry.h" #include "commongraphics/iconbutton.h" diff --git a/client/gui/mainwindow.cpp b/client/gui/mainwindow.cpp index 2da6217b0..6b46c5f8f 100644 --- a/client/gui/mainwindow.cpp +++ b/client/gui/mainwindow.cpp @@ -20,37 +20,38 @@ #endif #include "application/windscribeapplication.h" -#include "commongraphics/commongraphics.h" #include "backend/persistentstate.h" - -#include "utils/ws_assert.h" -#include "utils/extraconfig.h" -#include "utils/hardcodedsettings.h" -#include "utils/utils.h" -#include "utils/logger.h" -#include "utils/writeaccessrightschecker.h" -#include "utils/mergelog.h" -#include "languagecontroller.h" -#include "multipleaccountdetection/multipleaccountdetectionfactory.h" - +#include "commongraphics/commongraphics.h" +#include "dpiscalemanager.h" #include "graphicresources/imageresourcessvg.h" #include "graphicresources/imageresourcesjpg.h" #include "graphicresources/fontmanager.h" -#include "dpiscalemanager.h" +#include "languagecontroller.h" #include "launchonstartup/launchonstartup.h" -#include "showingdialogstate.h" #include "mainwindowstate.h" -#include "utils/interfaceutils.h" -#include "utils/iauthchecker.h" +#include "multipleaccountdetection/multipleaccountdetectionfactory.h" +#include "showingdialogstate.h" #include "utils/authcheckerfactory.h" +#include "utils/extraconfig.h" +#include "utils/hardcodedsettings.h" +#include "utils/iauthchecker.h" +#include "utils/interfaceutils.h" +#include "utils/logger.h" +#include "utils/mergelog.h" +#include "utils/network_utils/network_utils.h" +#include "utils/utils.h" +#include "utils/writeaccessrightschecker.h" +#include "utils/ws_assert.h" #if defined(Q_OS_WIN) #include "utils/winutils.h" #include "widgetutils/widgetutils_win.h" #include #elif defined(Q_OS_LINUX) + #include #include "utils/authchecker_linux.h" #else + #include #include "utils/macutils.h" #include "widgetutils/widgetutils_mac.h" #include "utils/authchecker_mac.h" @@ -70,7 +71,7 @@ MainWindow::MainWindow() : bytesTransferred_(0), bMousePressed_(false), bMoveEnabled_(true), - signOutReason_(SIGN_OUT_UNDEFINED), + logoutReason_(LOGOUT_UNDEFINED), isInitializationAborted_(false), isLoginOkAndConnectWindowVisible_(false), revealingConnectWindow_(false), @@ -144,7 +145,7 @@ MainWindow::MainWindow() : connect(backend_, &Backend::loginFinished, this, &MainWindow::onBackendLoginFinished); connect(backend_, &Backend::tryingBackupEndpoint, this, &MainWindow::onBackendTryingBackupEndpoint); connect(backend_, &Backend::loginError, this, &MainWindow::onBackendLoginError); - connect(backend_, &Backend::signOutFinished, this, &MainWindow::onBackendSignOutFinished); + connect(backend_, &Backend::logoutFinished, this, &MainWindow::onBackendLogoutFinished); connect(backend_, &Backend::sessionStatusChanged, this, &MainWindow::onBackendSessionStatusChanged); connect(backend_, &Backend::checkUpdateChanged, this, &MainWindow::onBackendCheckUpdateChanged); connect(backend_, &Backend::myIpChanged, this, &MainWindow::onBackendMyIpChanged); @@ -198,9 +199,10 @@ MainWindow::MainWindow() : connect(backend_->getPreferences(), &Preferences::isAutoConnectChanged, this, &MainWindow::onAutoConnectUpdated); localIpcServer_ = new LocalIPCServer(backend_, this); - connect(localIpcServer_, &LocalIPCServer::showLocations, this, &MainWindow::onReceivedOpenLocationsMessage); - connect(localIpcServer_, &LocalIPCServer::connectToLocation, this, &MainWindow::onConnectToLocation); + connect(localIpcServer_, &LocalIPCServer::showLocations, this, &MainWindow::onIpcOpenLocations); + connect(localIpcServer_, &LocalIPCServer::connectToLocation, this, &MainWindow::onIpcConnect); connect(localIpcServer_, &LocalIPCServer::attemptLogin, this, &MainWindow::onLoginClick); + connect(localIpcServer_, &LocalIPCServer::update, this, &MainWindow::onIpcUpdate); mainWindowController_ = new MainWindowController(this, locationsWindow_, backend_->getPreferencesHelper(), backend_->getPreferences(), backend_->getAccountInfo()); @@ -255,7 +257,7 @@ MainWindow::MainWindow() : // preferences window signals connect(mainWindowController_->getPreferencesWindow(), &PreferencesWindow::PreferencesWindowItem::quitAppClick, this, &MainWindow::onPreferencesQuitAppClick); connect(mainWindowController_->getPreferencesWindow(), &PreferencesWindow::PreferencesWindowItem::escape, this, &MainWindow::onPreferencesEscapeClick); - connect(mainWindowController_->getPreferencesWindow(), &PreferencesWindow::PreferencesWindowItem::signOutClick, this, &MainWindow::onPreferencesSignOutClick); + connect(mainWindowController_->getPreferencesWindow(), &PreferencesWindow::PreferencesWindowItem::logoutClick, this, &MainWindow::onPreferencesLogoutClick); connect(mainWindowController_->getPreferencesWindow(), &PreferencesWindow::PreferencesWindowItem::loginClick, this, &MainWindow::onPreferencesLoginClick); connect(mainWindowController_->getPreferencesWindow(), &PreferencesWindow::PreferencesWindowItem::viewLogClick, this, &MainWindow::onPreferencesViewLogClick); connect(mainWindowController_->getPreferencesWindow(), &PreferencesWindow::PreferencesWindowItem::exportSettingsClick, this, &MainWindow::onPreferencesExportSettingsClick); @@ -567,6 +569,8 @@ bool MainWindow::doClose(QCloseEvent *event, bool isFromSigTerm_mac) PersistentState::instance().save(); backend_->locationsModelManager()->saveFavoriteLocations(); + ImageResourcesSvg::instance().finishGracefully(); + if (WindscribeApplication::instance()->isExitWithRestart() || isFromSigTerm_mac || isSpontaneousCloseEvent_) { // Since we may process events below, disable UI updates and prevent the slot for this signal // from attempting to close this widget. We have encountered instances of that occurring during @@ -687,15 +691,26 @@ void MainWindow::closeEvent(QCloseEvent *event) void MainWindow::mouseMoveEvent(QMouseEvent *event) { - if (bMoveEnabled_) - { - if (event->buttons() & Qt::LeftButton && bMousePressed_) +#ifdef Q_OS_WIN + if (!bMoveEnabled_) { + return; + } + + if (event->buttons() & Qt::LeftButton && bMousePressed_) { + // On Windows, do not allow dragging the window beyond the top of the screen. + // You can still drag past the screen edge on the left/right/bottom. This is the same as MacOS. + if (event->globalPosition().y() - dragPosition_.y() < -mainWindowController_->getShadowMargin() && + event->globalPosition().y() - this->frameGeometry().top() < dragPosition_.y()) { - this->move(event->globalPosition().toPoint() - dragPosition_); - mainWindowController_->hideAllToolTips(); - event->accept(); + event->ignore(); + return; } + + this->move(event->globalPosition().toPoint() - dragPosition_); + mainWindowController_->hideAllToolTips(); + event->accept(); } +#endif } void MainWindow::mousePressEvent(QMouseEvent *event) @@ -716,9 +731,9 @@ void MainWindow::mousePressEvent(QMouseEvent *event) //event->accept(); bMousePressed_ = true; - if (QGuiApplication::platformName() == "wayland") { - this->window()->windowHandle()->startSystemMove(); - } +#if defined(Q_OS_MAC) || defined(Q_OS_LINUX) + this->window()->windowHandle()->startSystemMove(); +#endif } } } @@ -1078,7 +1093,7 @@ void MainWindow::onPreferencesEscapeClick() collapsePreferences(); } -void MainWindow::onPreferencesSignOutClick() +void MainWindow::onPreferencesLogoutClick() { gotoLogoutWindow(); } @@ -1087,8 +1102,8 @@ void MainWindow::onPreferencesLoginClick() { if (backend_->getPreferencesHelper()->isExternalConfigMode()) { QApplication::setOverrideCursor(Qt::WaitCursor); - signOutReason_ = SIGN_OUT_GO_TO_LOGIN; - backend_->signOut(false); + logoutReason_ = LOGOUT_GO_TO_LOGIN; + backend_->logout(false); } else { collapsePreferences(); mainWindowController_->getLoginWindow()->transitionToUsernameScreen(); @@ -1217,6 +1232,8 @@ void MainWindow::onPreferencesSendConfirmEmailClick() void MainWindow::onSendDebugLogClick() { + // capture the routing table in the debug log since it is useful to catch configuration issues + qCDebugMultiline(LOG_NETWORK) << NetworkUtils::getRoutingTable(); backend_->sendDebugLog(); } @@ -1422,6 +1439,12 @@ void MainWindow::onUpdateWindowAccept() backend_->sendUpdateVersion(static_cast(this->winId())); } +void MainWindow::onIpcUpdate() +{ + onUpdateAppItemClick(); + onUpdateWindowAccept(); +} + void MainWindow::onUpdateWindowCancel() { backend_->cancelUpdateVersion(); @@ -1456,10 +1479,10 @@ void MainWindow::onLogoutWindowAccept() { QApplication::setOverrideCursor(Qt::WaitCursor); setEnabled(false); - signOutReason_ = SIGN_OUT_FROM_MENU; + logoutReason_ = LOGOUT_FROM_MENU; isExitingFromPreferences_ = false; selectedLocation_->clear(); - backend_->signOut(false); + backend_->logout(false); } void MainWindow::onExitWindowAccept() @@ -1478,21 +1501,47 @@ void MainWindow::onExitWindowReject() } void MainWindow::onLocationSelected(const LocationID &lid) +{ + selectLocation(lid, types::Protocol()); +} + +void MainWindow::selectLocation(const LocationID &lid, const types::Protocol &protocol) { qCDebug(LOG_USER) << "Location selected:" << lid.getHashString(); selectedLocation_->set(lid); PersistentState::instance().setLastLocation(selectedLocation_->locationdId()); - if (selectedLocation_->isValid()) - { + if (selectedLocation_->isValid()) { mainWindowController_->getConnectWindow()->updateLocationInfo(selectedLocation_->firstName(), selectedLocation_->secondName(), selectedLocation_->countryCode(), selectedLocation_->pingTime(), selectedLocation_->locationdId().isCustomConfigsLocation()); mainWindowController_->collapseLocations(); - backend_->sendConnect(lid); - } - else - { + + uint port = 0; + if (protocol.isValid()) { + // determine default port for protocol + QVector ports = backend_->getPreferencesHelper()->getAvailablePortsForProtocol(protocol); + if (ports.size() == 0) { + qCDebug(LOG_BASIC) << "Could not determine port for protocol" << protocol.toLongString(); + } else { + port = ports[0]; + } + } + + if (port != 0) { + // If chosen protocol is different from preferred, turn off the badge + types::ProtocolStatus ps = mainWindowController_->getConnectWindow()->getProtocolStatus(); + if (protocol != ps.protocol || port != ps.port) { + mainWindowController_->getConnectWindow()->setIsPreferredProtocol(false); + } + + mainWindowController_->getConnectWindow()->setProtocolPort(protocol, port); + backend_->sendConnect(lid, types::ConnectionSettings(protocol, port, false)); + userProtocolOverride_ = true; + } else { + backend_->sendConnect(lid); + } + } else { WS_ASSERT(false); } } @@ -2086,7 +2135,7 @@ void MainWindow::onSplitTunnelingStateChanged(bool isActive) mainWindowController_->getConnectWindow()->setSplitTunnelingState(isActive); } -void MainWindow::onBackendSignOutFinished() +void MainWindow::onBackendLogoutFinished() { selectedLocation_->clear(); mainWindowController_->getConnectWindow()->updateLocationInfo(selectedLocation_->firstName(), selectedLocation_->secondName(), @@ -2099,16 +2148,16 @@ void MainWindow::onBackendSignOutFinished() backend_->getPreferencesHelper()->setIsExternalConfigMode(false); mainWindowController_->getBottomInfoWindow()->setDataRemaining(-1, -1); - if (signOutReason_ == SIGN_OUT_FROM_MENU) { + if (logoutReason_ == LOGOUT_FROM_MENU) { mainWindowController_->getLoginWindow()->resetState(); mainWindowController_->getLoginWindow()->setErrorMessage(LoginWindow::ERR_MSG_EMPTY, QString()); - } else if (signOutReason_ == SIGN_OUT_SESSION_EXPIRED) { + } else if (logoutReason_ == LOGOUT_SESSION_EXPIRED) { mainWindowController_->getLoginWindow()->transitionToUsernameScreen(); mainWindowController_->getLoginWindow()->setErrorMessage(LoginWindow::ERR_MSG_SESSION_EXPIRED, QString()); - } else if (signOutReason_ == SIGN_OUT_WITH_MESSAGE) { + } else if (logoutReason_ == LOGOUT_WITH_MESSAGE) { mainWindowController_->getLoginWindow()->transitionToUsernameScreen(); - mainWindowController_->getLoginWindow()->setErrorMessage(signOutMessageType_, signOutErrorMessage_); - } else if (signOutReason_ == SIGN_OUT_GO_TO_LOGIN) { + mainWindowController_->getLoginWindow()->setErrorMessage(logoutMessageType_, logoutErrorMessage_); + } else if (logoutReason_ == LOGOUT_GO_TO_LOGIN) { mainWindowController_->getLoginWindow()->transitionToUsernameScreen(); mainWindowController_->getLoginWindow()->setErrorMessage(LoginWindow::ERR_MSG_EMPTY, QString()); } else { @@ -2266,9 +2315,9 @@ void MainWindow::onBackendSessionDeleted() QApplication::setOverrideCursor(Qt::WaitCursor); setEnabled(false); - signOutReason_ = SIGN_OUT_SESSION_EXPIRED; + logoutReason_ = LOGOUT_SESSION_EXPIRED; selectedLocation_->clear(); - backend_->signOut(true); + backend_->logout(true); } void MainWindow::onBackendTestTunnelResult(bool success) @@ -2618,7 +2667,7 @@ void MainWindow::onPreferencesShareProxyGatewayChanged(const types::ShareProxyGa { if (sp.isEnabled) { - backend_->startProxySharing((PROXY_SHARING_TYPE)sp.proxySharingMode); + backend_->startProxySharing((PROXY_SHARING_TYPE)sp.proxySharingMode, sp.port); } else { @@ -2935,7 +2984,7 @@ void MainWindow::onAppShouldTerminate_mac() close(); } -void MainWindow::onReceivedOpenLocationsMessage() +void MainWindow::onIpcOpenLocations() { activateAndShow(); @@ -2961,13 +3010,12 @@ void MainWindow::onReceivedOpenLocationsMessage() // from a CLI-spawned-GUI (Mac): could fail WS_ASSERT(curWindow_ == WINDOW_ID_CONNECT) in expandLocations QTimer::singleShot(500, [this](){ mainWindowController_->expandLocations(); - localIpcServer_->sendLocationsShown(); }); } -void MainWindow::onConnectToLocation(const LocationID &id) +void MainWindow::onIpcConnect(const LocationID &id, const types::Protocol &protocol) { - onLocationSelected(id); + selectLocation(id, protocol); } void MainWindow::onAppCloseRequest() @@ -3413,11 +3461,11 @@ void MainWindow::backToLoginWithErrorMessage(LoginWindow::ERROR_MESSAGE_TYPE err { QApplication::setOverrideCursor(Qt::WaitCursor); setEnabled(false); - signOutMessageType_ = errorMessageType; - signOutReason_ = SIGN_OUT_WITH_MESSAGE; - signOutErrorMessage_ = errorMessage; + logoutMessageType_ = errorMessageType; + logoutReason_ = LOGOUT_WITH_MESSAGE; + logoutErrorMessage_ = errorMessage; selectedLocation_->clear(); - backend_->signOut(false); + backend_->logout(false); } void MainWindow::setupTrayIcon() @@ -3575,7 +3623,7 @@ void MainWindow::handleDisconnectWithError(const types::ConnectState &connectSta void MainWindow::setVariablesToInitState() { - signOutReason_ = SIGN_OUT_UNDEFINED; + logoutReason_ = LOGOUT_UNDEFINED; isLoginOkAndConnectWindowVisible_ = false; bNotificationConnectedShowed_ = false; bytesTransferred_ = 0; @@ -3854,3 +3902,26 @@ void MainWindow::onAppStateChanged(Qt::ApplicationState state) } #endif } + +#if defined(Q_OS_MAC) || defined(Q_OS_LINUX) +void MainWindow::setSigTermHandler(int fd) +{ + fd_ = fd; + + socketNotifier_ = new QSocketNotifier(fd_, QSocketNotifier::Read, this); + connect(socketNotifier_, &QSocketNotifier::activated, this, &MainWindow::onSigTerm); +} + +void MainWindow::onSigTerm() +{ + socketNotifier_->setEnabled(false); + char tmp; + if (::read(fd_, &tmp, sizeof(tmp)) < 0) { + qCDebug(LOG_BASIC) << "Could not read from signal socket"; + return; + } + + doClose(nullptr, true); + qApp->quit(); +} +#endif diff --git a/client/gui/mainwindow.h b/client/gui/mainwindow.h index 6845bf006..245b41398 100644 --- a/client/gui/mainwindow.h +++ b/client/gui/mainwindow.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include #include "generalmessagecontroller.h" @@ -44,6 +45,9 @@ class MainWindow : public QWidget void showAfterLaunch(); bool handleKeyPressEvent(QKeyEvent *event); +#if defined(Q_OS_MAC) || defined(Q_OS_LINUX) + void setSigTermHandler(int fd); +#endif protected: bool event(QEvent *event); @@ -97,7 +101,7 @@ private slots: // preferences window signals void onPreferencesEscapeClick(); - void onPreferencesSignOutClick(); + void onPreferencesLogoutClick(); void onPreferencesLoginClick(); void onPreferencesViewLogClick(); void onPreferencesExportSettingsClick(); @@ -173,7 +177,7 @@ private slots: void onBackendFirewallStateChanged(bool isEnabled); void onNetworkChanged(types::NetworkInterface network); void onSplitTunnelingStateChanged(bool isActive); - void onBackendSignOutFinished(); + void onBackendLogoutFinished(); void onBackendCleanupFinished(); void onBackendGotoCustomOvpnConfigModeFinished(); void onBackendConfirmEmailResult(bool bSuccess); @@ -241,8 +245,9 @@ private slots: #endif // LocalIPCServer signals - void onReceivedOpenLocationsMessage(); - void onConnectToLocation(const LocationID &id); + void onIpcOpenLocations(); + void onIpcConnect(const LocationID &id, const types::Protocol &protocol); + void onIpcUpdate(); void showShutdownWindow(); @@ -282,11 +287,16 @@ private slots: void onSelectedLocationChanged(); void onSelectedLocationRemoved(); +#if defined(Q_OS_MAC) || defined(Q_OS_LINUX) + void onSigTerm(); +#endif + private: void gotoLoginWindow(); void gotoLogoutWindow(); void gotoExitWindow(); void collapsePreferences(); + void selectLocation(const LocationID &lid, const types::Protocol &protocol); Backend *backend_; LocalIPCServer *localIpcServer_; @@ -326,10 +336,10 @@ private slots: bool bMousePressed_; bool bMoveEnabled_; - enum SIGN_OUT_REASON { SIGN_OUT_UNDEFINED, SIGN_OUT_FROM_MENU, SIGN_OUT_SESSION_EXPIRED, SIGN_OUT_WITH_MESSAGE, SIGN_OUT_GO_TO_LOGIN }; - SIGN_OUT_REASON signOutReason_; - LoginWindow::ERROR_MESSAGE_TYPE signOutMessageType_; - QString signOutErrorMessage_; + enum LOGOUT_REASON { LOGOUT_UNDEFINED, LOGOUT_FROM_MENU, LOGOUT_SESSION_EXPIRED, LOGOUT_WITH_MESSAGE, LOGOUT_GO_TO_LOGIN }; + LOGOUT_REASON logoutReason_; + LoginWindow::ERROR_MESSAGE_TYPE logoutMessageType_; + QString logoutErrorMessage_; LoginAttemptsController loginAttemptsController_; QSharedPointer multipleAccountDetection_; @@ -415,4 +425,7 @@ private slots: bool userProtocolOverride_; bool sendDebugLogOnDisconnect_; + + QSocketNotifier *socketNotifier_; + int fd_; }; diff --git a/client/gui/newsfeedwindow/newsfeedwindowitem.h b/client/gui/newsfeedwindow/newsfeedwindowitem.h index 73cf4bc04..5f4be1ed5 100644 --- a/client/gui/newsfeedwindow/newsfeedwindowitem.h +++ b/client/gui/newsfeedwindow/newsfeedwindowitem.h @@ -1,7 +1,7 @@ #pragma once #include -#include "../backend/backend.h" +#include "backend/backend.h" #include "commongraphics/scalablegraphicsobject.h" #include "commongraphics/resizablewindow.h" #include "newsfeedwindow/newscontentitem.h" diff --git a/client/gui/preferenceswindow/connectionwindow/connectionwindowitem.cpp b/client/gui/preferenceswindow/connectionwindow/connectionwindowitem.cpp index 5472c6745..f0bacf201 100644 --- a/client/gui/preferenceswindow/connectionwindow/connectionwindowitem.cpp +++ b/client/gui/preferenceswindow/connectionwindow/connectionwindowitem.cpp @@ -86,7 +86,6 @@ ConnectionWindowItem::ConnectionWindowItem(ScalableGraphicsObject *parent, Prefe connect(connectionModeGroup_, &ProtocolGroup::connectionModePreferencesChanged, this, &ConnectionWindowItem::onConnectionModePreferencesChangedByUser); addItem(connectionModeGroup_); -#ifndef Q_OS_LINUX packetSizeGroup_ = new PacketSizeGroup(this, "", QString("https://%1/features/packet-size").arg(HardcodedSettings::instance().windscribeServerUrl())); @@ -94,7 +93,6 @@ ConnectionWindowItem::ConnectionWindowItem(ScalableGraphicsObject *parent, Prefe connect(packetSizeGroup_, &PacketSizeGroup::packetSizeChanged, this, &ConnectionWindowItem::onPacketSizePreferencesChangedByUser); connect(packetSizeGroup_, &PacketSizeGroup::detectPacketSize, this, &ConnectionWindowItem::detectPacketSize); addItem(packetSizeGroup_); -#endif connectedDnsGroup_ = new ConnectedDnsGroup(this, "", @@ -199,17 +197,13 @@ void ConnectionWindowItem::setCurrentNetwork(const types::NetworkInterface &netw void ConnectionWindowItem::setPacketSizeDetectionState(bool on) { -#ifndef Q_OS_LINUX packetSizeGroup_->setPacketSizeDetectionState(on); -#endif } void ConnectionWindowItem::showPacketSizeDetectionError(const QString &title, const QString &message) { -#ifndef Q_OS_LINUX packetSizeGroup_->showPacketSizeDetectionError(title, message); -#endif } void ConnectionWindowItem::onFirewallPreferencesChangedByUser(const types::FirewallSettings &fm) @@ -224,9 +218,7 @@ void ConnectionWindowItem::onConnectionModePreferencesChangedByUser(const types: void ConnectionWindowItem::onPacketSizePreferencesChangedByUser(const types::PacketSize &ps) { -#ifndef Q_OS_LINUX preferences_->setPacketSize(ps); -#endif } void ConnectionWindowItem::onMacAddrSpoofingPreferencesChangedByUser(const types::MacAddrSpoofing &mas) @@ -283,9 +275,7 @@ void ConnectionWindowItem::onConnectionModePreferencesChanged(const types::Conne void ConnectionWindowItem::onPacketSizePreferencesChanged(const types::PacketSize &ps) { -#ifndef Q_OS_LINUX packetSizeGroup_->setPacketSizeSettings(ps); -#endif } void ConnectionWindowItem::onMacAddrSpoofingPreferencesChanged(const types::MacAddrSpoofing &mas) @@ -346,11 +336,7 @@ void ConnectionWindowItem::onLanguageChanged() firewallGroup_->setDescription(tr("Control the mode of behavior of the Windscribe firewall.")); connectionModeGroup_->setTitle(tr("Connection Mode")); connectionModeGroup_->setDescription(tr("Automatically choose the VPN protocol, or select one manually. NOTE: \"Preferred Protocol\" will override this setting.")); - -#ifndef Q_OS_LINUX packetSizeGroup_->setDescription(tr("Automatically determine the MTU for your connection, or manually override.")); -#endif - connectedDnsGroup_->setDescription(tr("Select the DNS server while connected to Windscribe.")); allowLanTrafficGroup_->setDescription(tr("Allow access to local services and printers while connected to Windscribe.")); checkBoxAllowLanTraffic_->setCaption(tr("Allow LAN Traffic")); diff --git a/client/gui/preferenceswindow/connectionwindow/proxygatewaygroup.cpp b/client/gui/preferenceswindow/connectionwindow/proxygatewaygroup.cpp index b6c02f85e..9173083e8 100644 --- a/client/gui/preferenceswindow/connectionwindow/proxygatewaygroup.cpp +++ b/client/gui/preferenceswindow/connectionwindow/proxygatewaygroup.cpp @@ -1,6 +1,8 @@ #include "proxygatewaygroup.h" +#include #include +#include "generalmessagecontroller.h" #include "graphicresources/imageresourcessvg.h" #include "languagecontroller.h" @@ -21,6 +23,18 @@ ProxyGatewayGroup::ProxyGatewayGroup(ScalableGraphicsObject *parent, const QStri connect(comboBoxProxyType_, &ComboBoxItem::currentItemChanged, this, &ProxyGatewayGroup::onProxyTypeItemChanged); addItem(comboBoxProxyType_); + editBoxPort_ = new EditBoxItem(this); + editBoxPort_->setText(tr("Auto")); + + QRegularExpression ipRegex ("^\\d+$"); + QRegularExpressionValidator *integerValidator = new QRegularExpressionValidator(ipRegex, this); + editBoxPort_->setValidator(integerValidator); + + connect(editBoxPort_, &EditBoxItem::textChanged, this, &ProxyGatewayGroup::onPortChanged); + connect(editBoxPort_, &EditBoxItem::cancelled, this, &ProxyGatewayGroup::onPortCancelClicked); + connect(editBoxPort_, &EditBoxItem::editClicked, this, &ProxyGatewayGroup::onPortEditClicked); + addItem(editBoxPort_); + proxyIpAddressItem_ = new ProxyIpAddressItem(this); addItem(proxyIpAddressItem_); @@ -38,6 +52,11 @@ void ProxyGatewayGroup::setProxyGatewaySettings(const types::ShareProxyGateway & settings_ = sp; checkBoxEnable_->setState(sp.isEnabled); comboBoxProxyType_->setCurrentItem(sp.proxySharingMode); + if (sp.port == 0) { + editBoxPort_->setText(tr("Auto")); + } else { + editBoxPort_->setText(QString::number(sp.port)); + } updateMode(); } } @@ -55,8 +74,52 @@ void ProxyGatewayGroup::onProxyTypeItemChanged(QVariant v) emit proxyGatewayPreferencesChanged(settings_); } +void ProxyGatewayGroup::onPortCancelClicked() +{ + if (settings_.port == 0) { + editBoxPort_->setText(tr("Auto")); + } +} + +void ProxyGatewayGroup::onPortChanged(QVariant v) +{ + if (v.toInt() == settings_.port) { + if (v.toInt() == 0) { + editBoxPort_->setText(tr("Auto")); + } + return; + } + + if (v.toInt() > 0 && v.toInt() <= 65535) { + settings_.port = v.toInt(); + } else { + editBoxPort_->setText(tr("Auto")); + settings_.port = 0; + } + emit proxyGatewayPreferencesChanged(settings_); +} + void ProxyGatewayGroup::setProxyGatewayAddress(const QString &address) { + if (settings_.isEnabled && (address.isEmpty() || address.endsWith(":0"))) { + // Invalid address or port 0 indicates that the proxy is not running. Reset to 0. + editBoxPort_->setText(tr("Auto")); + settings_.port = 0; + emit proxyGatewayPreferencesChanged(settings_); + + GeneralMessageController::instance().showMessage( + "WARNING_YELLOW", + tr("Unable to start proxy server"), + tr("The proxy server couldn't be started on the requested port. Please try again with a different port."), + GeneralMessageController::tr(GeneralMessageController::kOk), + "", + "", + std::function(nullptr), + std::function(nullptr), + std::function(nullptr), + GeneralMessage::kFromPreferences); + return; + } proxyIpAddressItem_->setIP(address); } @@ -80,6 +143,14 @@ void ProxyGatewayGroup::onLanguageChanged() checkBoxEnable_->setCaption(tr("Proxy Gateway")); comboBoxProxyType_->setLabelCaption(tr("Proxy Type")); comboBoxProxyType_->setItems(PROXY_SHARING_TYPE_toList(), settings_.proxySharingMode); + editBoxPort_->setCaption(tr("Port")); +} + +void ProxyGatewayGroup::onPortEditClicked() +{ + if (settings_.port == 0) { + editBoxPort_->setText(QString::number(0)); + } } } // namespace PreferencesWindow diff --git a/client/gui/preferenceswindow/connectionwindow/proxygatewaygroup.h b/client/gui/preferenceswindow/connectionwindow/proxygatewaygroup.h index 39bcb62a8..477de769d 100644 --- a/client/gui/preferenceswindow/connectionwindow/proxygatewaygroup.h +++ b/client/gui/preferenceswindow/connectionwindow/proxygatewaygroup.h @@ -1,8 +1,9 @@ #pragma once -#include "preferenceswindow/toggleitem.h" #include "preferenceswindow/comboboxitem.h" +#include "preferenceswindow/editboxitem.h" #include "preferenceswindow/preferencegroup.h" +#include "preferenceswindow/toggleitem.h" #include "proxyipaddressitem.h" #include "types/shareproxygateway.h" @@ -25,6 +26,9 @@ class ProxyGatewayGroup : public PreferenceGroup private slots: void onCheckBoxStateChanged(bool isChecked); void onProxyTypeItemChanged(QVariant v); + void onPortChanged(QVariant v); + void onPortEditClicked(); + void onPortCancelClicked(); void onLanguageChanged(); @@ -33,6 +37,7 @@ private slots: private: ToggleItem *checkBoxEnable_; + EditBoxItem *editBoxPort_; ComboBoxItem *comboBoxProxyType_; ProxyIpAddressItem *proxyIpAddressItem_; diff --git a/client/gui/preferenceswindow/editboxitem.cpp b/client/gui/preferenceswindow/editboxitem.cpp index 035e6b4d8..bc74a32ff 100644 --- a/client/gui/preferenceswindow/editboxitem.cpp +++ b/client/gui/preferenceswindow/editboxitem.cpp @@ -151,6 +151,7 @@ void EditBoxItem::onEditClick() lineEdit_->setFocus(); isEditMode_ = true; update(); + emit editClicked(); } void EditBoxItem::onConfirmClick() @@ -173,6 +174,7 @@ void EditBoxItem::onUndoClick() proxyWidget_->hide(); isEditMode_ = false; update(); + emit cancelled(); } void EditBoxItem::updateScaling() diff --git a/client/gui/preferenceswindow/editboxitem.h b/client/gui/preferenceswindow/editboxitem.h index 0326ddffe..c02b92da9 100644 --- a/client/gui/preferenceswindow/editboxitem.h +++ b/client/gui/preferenceswindow/editboxitem.h @@ -31,9 +31,8 @@ class EditBoxItem : public CommonGraphics::BaseItem signals: void textChanged(const QString &text); - void additionalButtonClicked(); - void additionalButtonHoverEnter(); - void additionalButtonHoverLeave(); + void editClicked(); + void cancelled(); protected: void keyPressEvent(QKeyEvent *event) override; diff --git a/client/gui/preferenceswindow/preferencestab/preferencestabcontrolitem.cpp b/client/gui/preferenceswindow/preferencestab/preferencestabcontrolitem.cpp index 5e6972a8c..dc76cafbf 100644 --- a/client/gui/preferenceswindow/preferencestab/preferencestabcontrolitem.cpp +++ b/client/gui/preferenceswindow/preferencestab/preferencestabcontrolitem.cpp @@ -55,8 +55,8 @@ PreferencesTabControlItem::PreferencesTabControlItem(ScalableGraphicsObject * pa dividerLine_ = new CommonGraphics::DividerLine(this, 32, 0); dividerLine_->setOpacity(0.1); - signOutButton_ = new TabButton(this, TAB_UNDEFINED, "preferences/SIGN_OUT_ICON", QColor(0xff, 0xef, 0x02)); - connect(signOutButton_, &TabButton::tabClicked, this, &PreferencesTabControlItem::onTabClicked); + logoutButton_ = new TabButton(this, TAB_UNDEFINED, "preferences/LOGOUT_ICON", QColor(0xff, 0xef, 0x02)); + connect(logoutButton_, &TabButton::tabClicked, this, &PreferencesTabControlItem::onTabClicked); quitButton_ = new TabButton(this, TAB_UNDEFINED, "preferences/QUIT_ICON", QColor(0xff, 0x3b, 0x3b)); connect(quitButton_, &TabButton::tabClicked, this, &PreferencesTabControlItem::onTabClicked); @@ -84,9 +84,9 @@ void PreferencesTabControlItem::onLanguageChanged() helpButton_->setText(tr("Help")); aboutButton_->setText(tr("About")); if (!loggedIn_ || isExternalConfigMode_) { - signOutButton_->setText(tr("Login")); + logoutButton_->setText(tr("Login")); } else { - signOutButton_->setText(tr("Log Out")); + logoutButton_->setText(tr("Log Out")); } quitButton_->setText(tr("Quit")); } @@ -134,9 +134,9 @@ void PreferencesTabControlItem::onTabClicked(PREFERENCES_TAB_TYPE tab, TabButton { switch(tab) { case TAB_UNDEFINED: - if (button == signOutButton_) { + if (button == logoutButton_) { if (loggedIn_ && !isExternalConfigMode_) { - emit signOutClick(); + emit logoutClick(); } else { emit loginClick(); } @@ -179,7 +179,7 @@ void PreferencesTabControlItem::updateScaling() void PreferencesTabControlItem::onIsExternalConfigModeChanged(bool bIsExternalConfigMode) { isExternalConfigMode_ = bIsExternalConfigMode; - // May change "Sign Out" vs "Login" + // May change "Log Out" vs "Login" onLanguageChanged(); accountButton_->setVisible(!bIsExternalConfigMode); @@ -203,11 +203,11 @@ void PreferencesTabControlItem::fadeOtherButtons(TabButton *button) void PreferencesTabControlItem::updateBottomAnchoredButtonPos() { int quitButtonPosY = height_ - (BUTTON_MARGIN + TabButton::BUTTON_HEIGHT)*G_SCALE; - int signOutButtonPosY = quitButtonPosY - (BUTTON_MARGIN + TabButton::BUTTON_HEIGHT)*G_SCALE; - int dividerPosY = signOutButtonPosY - (BUTTON_MARGIN + CommonGraphics::DividerLine::DIVIDER_HEIGHT)*G_SCALE; + int logoutButtonPosY = quitButtonPosY - (BUTTON_MARGIN + TabButton::BUTTON_HEIGHT)*G_SCALE; + int dividerPosY = logoutButtonPosY - (BUTTON_MARGIN + CommonGraphics::DividerLine::DIVIDER_HEIGHT)*G_SCALE; dividerLine_->setPos(0, dividerPosY); - signOutButton_->setPos(buttonMarginX(), signOutButtonPosY); + logoutButton_->setPos(buttonMarginX(), logoutButtonPosY); quitButton_->setPos(buttonMarginX(), quitButtonPosY); } diff --git a/client/gui/preferenceswindow/preferencestab/preferencestabcontrolitem.h b/client/gui/preferenceswindow/preferencestab/preferencestabcontrolitem.h index 01fc6315e..90164c677 100644 --- a/client/gui/preferenceswindow/preferencestab/preferencestabcontrolitem.h +++ b/client/gui/preferenceswindow/preferencestab/preferencestabcontrolitem.h @@ -34,7 +34,7 @@ public slots: signals: void currentTabChanged(PREFERENCES_TAB_TYPE tab); - void signOutClick(); + void logoutClick(); void loginClick(); void quitClick(); @@ -51,7 +51,7 @@ private slots: TabButton *helpButton_; TabButton *aboutButton_; CommonGraphics::DividerLine *dividerLine_; - TabButton *signOutButton_; + TabButton *logoutButton_; TabButton *quitButton_; QList buttonList_; diff --git a/client/gui/preferenceswindow/preferenceswindowitem.cpp b/client/gui/preferenceswindow/preferenceswindowitem.cpp index 507b6cef3..640e493cf 100644 --- a/client/gui/preferenceswindow/preferenceswindowitem.cpp +++ b/client/gui/preferenceswindow/preferenceswindowitem.cpp @@ -29,7 +29,7 @@ PreferencesWindowItem::PreferencesWindowItem(QGraphicsObject *parent, Preference tabControlItem_ = new PreferencesTabControlItem(this, preferencesHelper); connect(tabControlItem_, &PreferencesTabControlItem::currentTabChanged, this, &PreferencesWindowItem::onCurrentTabChanged); - connect(tabControlItem_, &PreferencesTabControlItem::signOutClick, this, &PreferencesWindowItem::signOutClick); + connect(tabControlItem_, &PreferencesTabControlItem::logoutClick, this, &PreferencesWindowItem::logoutClick); connect(tabControlItem_, &PreferencesTabControlItem::loginClick, this, &PreferencesWindowItem::loginClick); connect(tabControlItem_, &PreferencesTabControlItem::quitClick, this, &PreferencesWindowItem::quitAppClick); diff --git a/client/gui/preferenceswindow/preferenceswindowitem.h b/client/gui/preferenceswindow/preferenceswindowitem.h index 5d5ed3ab3..6986332b6 100644 --- a/client/gui/preferenceswindow/preferenceswindowitem.h +++ b/client/gui/preferenceswindow/preferenceswindowitem.h @@ -60,7 +60,7 @@ class PreferencesWindowItem : public ResizableWindow void onCollapse(); signals: - void signOutClick(); + void logoutClick(); void loginClick(); void quitAppClick(); diff --git a/client/gui/protocolwindow/protocolpromptitem.h b/client/gui/protocolwindow/protocolpromptitem.h index 34a410308..73bcf5b28 100644 --- a/client/gui/protocolwindow/protocolpromptitem.h +++ b/client/gui/protocolwindow/protocolpromptitem.h @@ -3,9 +3,9 @@ #include #include #include "../../common/types/protocolstatus.h" -#include "../backend/backend.h" -#include "../backend/preferences/preferences.h" -#include "../backend/preferences/preferenceshelper.h" +#include "backend/backend.h" +#include "backend/preferences/preferences.h" +#include "backend/preferences/preferenceshelper.h" #include "commongraphics/imageitem.h" #include "commongraphics/basepage.h" #include "commongraphics/listbutton.h" diff --git a/client/gui/protocolwindow/protocolwindowitem.h b/client/gui/protocolwindow/protocolwindowitem.h index 99e391e80..be1b96a78 100644 --- a/client/gui/protocolwindow/protocolwindowitem.h +++ b/client/gui/protocolwindow/protocolwindowitem.h @@ -1,6 +1,6 @@ #pragma once -#include "../backend/backend.h" +#include "backend/backend.h" #include "commongraphics/scalablegraphicsobject.h" #include "commongraphics/resizablewindow.h" #include "commongraphics/basepage.h" diff --git a/client/gui/svg.qrc b/client/gui/svg.qrc index 223536dc5..248a79f05 100644 --- a/client/gui/svg.qrc +++ b/client/gui/svg.qrc @@ -280,6 +280,7 @@ svg/preferences/LAUNCH_AT_STARTUP.svg svg/preferences/LOCATION_LOAD.svg svg/preferences/LOCATION_ORDER.svg + svg/preferences/LOGOUT_ICON.svg svg/preferences/MAC_SPOOFING.svg svg/preferences/MAGNIFYING_GLASS.svg svg/preferences/MALWARE.svg @@ -297,7 +298,6 @@ svg/preferences/SEND_LOG.svg svg/preferences/SEND_TICKET.svg svg/preferences/SHOW_NOTIFICATIONS.svg - svg/preferences/SIGN_OUT_ICON.svg svg/preferences/SOCIAL_NETWORKS.svg svg/preferences/SPLIT_TUNNELING.svg svg/preferences/START_MINIMIZED.svg diff --git a/client/gui/svg/preferences/SIGN_OUT_ICON.svg b/client/gui/svg/preferences/LOGOUT_ICON.svg similarity index 100% rename from client/gui/svg/preferences/SIGN_OUT_ICON.svg rename to client/gui/svg/preferences/LOGOUT_ICON.svg diff --git a/client/gui/translations/ws_desktop_ar.ts b/client/gui/translations/ws_desktop_ar.ts index 58ab9b11b..d36e9ae05 100644 --- a/client/gui/translations/ws_desktop_ar.ts +++ b/client/gui/translations/ws_desktop_ar.ts @@ -1698,6 +1698,22 @@ Connect to a network first Proxy Type نوع الوكيل + + Port + ميناء + + + Unable to start proxy server + غير قادر على بدء تشغيل الخادم الوكيل + + + The proxy server couldn't be started on the requested port. Please try again with a different port. + تعذر بدء تشغيل الخادم الوكيل على المنفذ المطلوب. يرجى المحاولة مرة أخرى باستخدام منفذ مختلف. + + + Auto + تلقائي + PreferencesWindow::ProxyIpAddressItem diff --git a/client/gui/translations/ws_desktop_cs.ts b/client/gui/translations/ws_desktop_cs.ts index d72bc2852..e4a6ea3ea 100644 --- a/client/gui/translations/ws_desktop_cs.ts +++ b/client/gui/translations/ws_desktop_cs.ts @@ -1698,6 +1698,22 @@ Nejprve se připojte k síti Proxy Type Typ proxy serveru + + Port + Přístav + + + Unable to start proxy server + Nelze spustit proxy server + + + The proxy server couldn't be started on the requested port. Please try again with a different port. + Proxy server nelze spustit na požadovaném portu. Zkuste to prosím znovu s jiným portem. + + + Auto + Auto + PreferencesWindow::ProxyIpAddressItem diff --git a/client/gui/translations/ws_desktop_de.ts b/client/gui/translations/ws_desktop_de.ts index 6a49a75ea..ff2fda980 100644 --- a/client/gui/translations/ws_desktop_de.ts +++ b/client/gui/translations/ws_desktop_de.ts @@ -1698,6 +1698,22 @@ Zuerst eine Verbindung mit einem Netzwerk herstellen Proxy Type Proxy-Typ + + Port + Hafen + + + Unable to start proxy server + Proxyserver kann nicht gestartet werden + + + The proxy server couldn't be started on the requested port. Please try again with a different port. + Der Proxyserver konnte auf dem angeforderten Port nicht gestartet werden. Bitte versuchen Sie es erneut mit einem anderen Port. + + + Auto + Auto + PreferencesWindow::ProxyIpAddressItem diff --git a/client/gui/translations/ws_desktop_en.ts b/client/gui/translations/ws_desktop_en.ts index 564914236..4989eb85f 100644 --- a/client/gui/translations/ws_desktop_en.ts +++ b/client/gui/translations/ws_desktop_en.ts @@ -1698,6 +1698,22 @@ Connect to a network first Proxy Type Proxy Type + + Port + Port + + + Unable to start proxy server + Unable to start proxy server + + + The proxy server couldn't be started on the requested port. Please try again with a different port. + The proxy server couldn't be started on the requested port. Please try again with a different port. + + + Auto + Auto + PreferencesWindow::ProxyIpAddressItem diff --git a/client/gui/translations/ws_desktop_es.ts b/client/gui/translations/ws_desktop_es.ts index ce32fe068..bbc6f1625 100644 --- a/client/gui/translations/ws_desktop_es.ts +++ b/client/gui/translations/ws_desktop_es.ts @@ -1698,6 +1698,22 @@ Conéctate primero a una red Proxy Type Tipo de proxy + + Port + Puerto + + + Unable to start proxy server + No se puede iniciar el servidor proxy + + + The proxy server couldn't be started on the requested port. Please try again with a different port. + No se pudo iniciar el servidor proxy en el puerto solicitado. Inténtelo de nuevo con un puerto diferente. + + + Auto + Automático + PreferencesWindow::ProxyIpAddressItem diff --git a/client/gui/translations/ws_desktop_fa.ts b/client/gui/translations/ws_desktop_fa.ts index 3c8022928..f14984e72 100644 --- a/client/gui/translations/ws_desktop_fa.ts +++ b/client/gui/translations/ws_desktop_fa.ts @@ -1698,6 +1698,22 @@ Connect to a network first Proxy Type نوع پیشکار + + Port + پورت + + + Unable to start proxy server + قادر به اغاز کارگزار پیشکار نیست + + + The proxy server couldn't be started on the requested port. Please try again with a different port. + کارگزار پروکسی نمی تواند بر روی درگاه درخواست شده اغاز شود. لطفا با درگاه دیگری دوباره تلاش کنید. + + + Auto + خودکار + PreferencesWindow::ProxyIpAddressItem diff --git a/client/gui/translations/ws_desktop_fr.ts b/client/gui/translations/ws_desktop_fr.ts index 127e06966..5a2c3db61 100644 --- a/client/gui/translations/ws_desktop_fr.ts +++ b/client/gui/translations/ws_desktop_fr.ts @@ -1698,6 +1698,22 @@ Se connecter d’abord à un réseau Proxy Type Proxy Type + + Port + Port + + + Unable to start proxy server + Impossible de démarrer le serveur proxy + + + The proxy server couldn't be started on the requested port. Please try again with a different port. + Le serveur proxy n’a pas pu être démarré sur le port demandé. Veuillez réessayer avec un autre port. + + + Auto + Auto + PreferencesWindow::ProxyIpAddressItem diff --git a/client/gui/translations/ws_desktop_hi.ts b/client/gui/translations/ws_desktop_hi.ts index 4e0f67e24..202482c4a 100644 --- a/client/gui/translations/ws_desktop_hi.ts +++ b/client/gui/translations/ws_desktop_hi.ts @@ -1698,6 +1698,22 @@ Connect to a network first Proxy Type प्रॉक्सी प्रकार + + Port + बंदरगाह + + + Unable to start proxy server + प्रॉक्सी सर्वर प्रारंभ करने में असमर्थ + + + The proxy server couldn't be started on the requested port. Please try again with a different port. + प्रॉक्सी सर्वर अनुरोधित पोर्ट पर प्रारंभ नहीं किया जा सका। कृपया किसी भिन्न पोर्ट के साथ पुन: प्रयास करें. + + + Auto + ऑटो + PreferencesWindow::ProxyIpAddressItem diff --git a/client/gui/translations/ws_desktop_id.ts b/client/gui/translations/ws_desktop_id.ts index e55e6ffa6..4c60e21a4 100644 --- a/client/gui/translations/ws_desktop_id.ts +++ b/client/gui/translations/ws_desktop_id.ts @@ -1698,6 +1698,22 @@ Menyambungkan ke jaringan terlebih dahulu Proxy Type Jenis Proksi + + Port + Pelabuhan + + + Unable to start proxy server + Tidak dapat memulai server proxy + + + The proxy server couldn't be started on the requested port. Please try again with a different port. + Server proxy tidak dapat dimulai pada port yang diminta. Silakan coba lagi dengan port lain. + + + Auto + Auto + PreferencesWindow::ProxyIpAddressItem diff --git a/client/gui/translations/ws_desktop_it.ts b/client/gui/translations/ws_desktop_it.ts index 8e6e8935b..b3afb4523 100644 --- a/client/gui/translations/ws_desktop_it.ts +++ b/client/gui/translations/ws_desktop_it.ts @@ -1698,6 +1698,22 @@ Connettersi prima a una rete Proxy Type Tipo di proxy + + Port + Porto + + + Unable to start proxy server + Impossibile avviare il server proxy + + + The proxy server couldn't be started on the requested port. Please try again with a different port. + Non è stato possibile avviare il server proxy sulla porta richiesta. Riprovare con una porta diversa. + + + Auto + Automatico + PreferencesWindow::ProxyIpAddressItem diff --git a/client/gui/translations/ws_desktop_ja.ts b/client/gui/translations/ws_desktop_ja.ts index 6fa869e5d..fef70eb04 100644 --- a/client/gui/translations/ws_desktop_ja.ts +++ b/client/gui/translations/ws_desktop_ja.ts @@ -1698,6 +1698,22 @@ Connect to a network first Proxy Type プロキシの種類 + + Port + + + + Unable to start proxy server + プロキシ サーバーを起動できません + + + The proxy server couldn't be started on the requested port. Please try again with a different port. + 要求されたポートでプロキシ サーバーを開始できませんでした。別のポートで再試行してください。 + + + Auto + 自動 + PreferencesWindow::ProxyIpAddressItem diff --git a/client/gui/translations/ws_desktop_ko.ts b/client/gui/translations/ws_desktop_ko.ts index 1004de77a..1aea6bd41 100644 --- a/client/gui/translations/ws_desktop_ko.ts +++ b/client/gui/translations/ws_desktop_ko.ts @@ -1698,6 +1698,22 @@ Connect to a network first Proxy Type 프록시 유형 + + Port + 항구 + + + Unable to start proxy server + 프록시 서버를 시작할 수 없습니다. + + + The proxy server couldn't be started on the requested port. Please try again with a different port. + 요청된 포트에서 프록시 서버를 시작할 수 없습니다. 다른 포트로 다시 시도하십시오. + + + Auto + 자동 + PreferencesWindow::ProxyIpAddressItem diff --git a/client/gui/translations/ws_desktop_pl.ts b/client/gui/translations/ws_desktop_pl.ts index e900fcd06..154246da9 100644 --- a/client/gui/translations/ws_desktop_pl.ts +++ b/client/gui/translations/ws_desktop_pl.ts @@ -1698,6 +1698,22 @@ Najpierw połącz się z siecią Proxy Type Typ serwera proxy + + Port + Port + + + Unable to start proxy server + Nie można uruchomić serwera proxy + + + The proxy server couldn't be started on the requested port. Please try again with a different port. + Nie można uruchomić serwera proxy na żądanym porcie. Spróbuj ponownie z innym portem. + + + Auto + Automatycznie + PreferencesWindow::ProxyIpAddressItem diff --git a/client/gui/translations/ws_desktop_pt.ts b/client/gui/translations/ws_desktop_pt.ts index 5af84a815..7791dd062 100644 --- a/client/gui/translations/ws_desktop_pt.ts +++ b/client/gui/translations/ws_desktop_pt.ts @@ -1698,6 +1698,22 @@ Conecte-se a uma rede primeiro Proxy Type Tipo de proxy + + Port + Porto + + + Unable to start proxy server + Não é possível iniciar o servidor proxy + + + The proxy server couldn't be started on the requested port. Please try again with a different port. + Não foi possível iniciar o servidor proxy na porta solicitada. Tente novamente com uma porta diferente. + + + Auto + Automático + PreferencesWindow::ProxyIpAddressItem diff --git a/client/gui/translations/ws_desktop_ru.ts b/client/gui/translations/ws_desktop_ru.ts index d74d3e433..baf4bdc0d 100644 --- a/client/gui/translations/ws_desktop_ru.ts +++ b/client/gui/translations/ws_desktop_ru.ts @@ -1698,6 +1698,22 @@ Connect to a network first Proxy Type Тип прокси + + Port + Порт + + + Unable to start proxy server + Не удается запустить прокси-сервер + + + The proxy server couldn't be started on the requested port. Please try again with a different port. + Не удалось запустить прокси-сервер на запрошенном порту. Пожалуйста, повторите попытку с другим портом. + + + Auto + Авто + PreferencesWindow::ProxyIpAddressItem diff --git a/client/gui/translations/ws_desktop_tr.ts b/client/gui/translations/ws_desktop_tr.ts index ccf9d9786..abb689f2e 100644 --- a/client/gui/translations/ws_desktop_tr.ts +++ b/client/gui/translations/ws_desktop_tr.ts @@ -1698,6 +1698,22 @@ Connect to a network first Proxy Type Proxy Türü + + Port + Liman + + + Unable to start proxy server + Proxy sunucusu başlatılamıyor + + + The proxy server couldn't be started on the requested port. Please try again with a different port. + Proxy sunucusu istenen bağlantı noktasında başlatılamadı. Lütfen farklı bir bağlantı noktasıyla tekrar deneyin. + + + Auto + Otomatik + PreferencesWindow::ProxyIpAddressItem diff --git a/client/gui/translations/ws_desktop_uk.ts b/client/gui/translations/ws_desktop_uk.ts index fc2e23f1d..75041be34 100644 --- a/client/gui/translations/ws_desktop_uk.ts +++ b/client/gui/translations/ws_desktop_uk.ts @@ -803,7 +803,7 @@ If the problem persists after a restart, please send a debug log and open a supp Quit - Кинути + Вимкнути Log Out @@ -1666,7 +1666,7 @@ Connect to a network first Quit - Кинути + Вимкнути @@ -1698,6 +1698,22 @@ Connect to a network first Proxy Type Тип проксі-сервера + + Port + Порт + + + Unable to start proxy server + Не вдається запустити проксі-сервер + + + The proxy server couldn't be started on the requested port. Please try again with a different port. + Не вдалося запустити проксі-сервер на запитаному порту. Повторіть спробу з іншим портом. + + + Auto + Автоматичного + PreferencesWindow::ProxyIpAddressItem diff --git a/client/gui/translations/ws_desktop_vi.ts b/client/gui/translations/ws_desktop_vi.ts index 987d8a532..62e74e85c 100644 --- a/client/gui/translations/ws_desktop_vi.ts +++ b/client/gui/translations/ws_desktop_vi.ts @@ -1698,6 +1698,22 @@ Kết nối với mạng trước Proxy Type Loại proxy + + Port + Cảng + + + Unable to start proxy server + Không thể khởi động máy chủ proxy + + + The proxy server couldn't be started on the requested port. Please try again with a different port. + Không thể khởi động máy chủ proxy trên cổng được yêu cầu. Vui lòng thử lại với một cổng khác. + + + Auto + Xe ô tô + PreferencesWindow::ProxyIpAddressItem diff --git a/client/gui/translations/ws_desktop_zh-CN.ts b/client/gui/translations/ws_desktop_zh-CN.ts index 4f9cdec6e..285b537e5 100644 --- a/client/gui/translations/ws_desktop_zh-CN.ts +++ b/client/gui/translations/ws_desktop_zh-CN.ts @@ -1698,6 +1698,22 @@ Connect to a network first Proxy Type 代理类型 + + Port + 港口 + + + Unable to start proxy server + 无法启动代理服务器 + + + The proxy server couldn't be started on the requested port. Please try again with a different port. + 无法在请求的端口上启动代理服务器。请使用其他端口重试。 + + + Auto + 自动 + PreferencesWindow::ProxyIpAddressItem diff --git a/client/gui/translations/ws_desktop_zh-TW.ts b/client/gui/translations/ws_desktop_zh-TW.ts index 2b0bea5bb..df0d4d297 100644 --- a/client/gui/translations/ws_desktop_zh-TW.ts +++ b/client/gui/translations/ws_desktop_zh-TW.ts @@ -1698,6 +1698,22 @@ Connect to a network first Proxy Type 代理類型 + + Port + 港口 + + + Unable to start proxy server + 無法啟動代理伺服器 + + + The proxy server couldn't be started on the requested port. Please try again with a different port. + 無法在請求的埠上啟動代理伺服器。請使用其他埠重試。 + + + Auto + 自動 + PreferencesWindow::ProxyIpAddressItem diff --git a/client/gui/twofactorauth/twofactorauthwindowitem.h b/client/gui/twofactorauth/twofactorauthwindowitem.h index 248a3bfa3..5ea28ee17 100644 --- a/client/gui/twofactorauth/twofactorauthwindowitem.h +++ b/client/gui/twofactorauth/twofactorauthwindowitem.h @@ -2,7 +2,7 @@ #include #include -#include "../backend/backend.h" +#include "backend/backend.h" #include "twofactorauthokbutton.h" #include "commongraphics/escapebutton.h" #include "commongraphics/iconbutton.h" diff --git a/client/gui/utils/CMakeLists.txt b/client/gui/utils/CMakeLists.txt index 1fac4864a..6e3d66854 100644 --- a/client/gui/utils/CMakeLists.txt +++ b/client/gui/utils/CMakeLists.txt @@ -1,6 +1,4 @@ target_sources(gui PRIVATE - authcheckerfactory.h - iauthchecker.h imagewithshadow.cpp imagewithshadow.h interfaceutils.h @@ -10,28 +8,20 @@ target_sources(gui PRIVATE shadowmanager.h textshadow.cpp textshadow.h - writeaccessrightschecker.cpp - writeaccessrightschecker.h ) if (WIN32) target_sources(gui PRIVATE - authchecker_win.cpp - authchecker_win.h interfaceutils_win.cpp scaleutils_win.cpp scaleutils_win.h ) elseif(APPLE) target_sources(gui PRIVATE - authchecker_mac.h - authchecker_mac.mm interfaceutils_mac.mm ) elseif(UNIX) target_sources(gui PRIVATE - authchecker_linux.cpp - authchecker_linux.h interfaceutils_linux.cpp ) endif() diff --git a/client/main_cli.cpp b/client/main_cli.cpp new file mode 100644 index 000000000..88e41bad8 --- /dev/null +++ b/client/main_cli.cpp @@ -0,0 +1,129 @@ +#include +#include + +#include "application/singleappinstance.h" +#include "application/windscribeapplication.h" +#include "cli/mainservice.h" +#include "utils/logger.h" +#include "utils/utils.h" +#include "utils/extraconfig.h" +#include "version/appversion.h" +#include "engine/openvpnversioncontroller.h" + +#if defined (Q_OS_LINUX) + #include // dirname + #include // readlink + #include // PATH_MAX + #include + #include + #include // gid_t + #include "utils/linuxutils.h" +#endif + +static int fds[2]; + +#if defined (Q_OS_LINUX) +void handler_sigterm(int signum) +{ + if (signum == SIGTERM) { + qCDebug(LOG_BASIC) << "SIGTERM signal received"; + + // Can't call Qt functions safely from here, see https://doc.qt.io/qt-6/unix-signals.html + // Instead, write a byte to a local socket + char a = 1; + if (::write(fds[0], &a, sizeof(a)) < 0) { + qCDebug(LOG_BASIC) << "Could not write to signal socket"; + } + } +} +#endif + +int main(int argc, char *argv[]) +{ +#if defined (Q_OS_LINUX) + gid_t gid = LinuxUtils::getWindscribeGid(); + qCDebug(LOG_BASIC) << "Setting gid to:" << gid; + if (setgid(gid) < 0) { + qCDebug(LOG_BASIC) << "Could not set windscribe group"; + return 0; + } +#endif + +#if defined (Q_OS_LINUX) + signal(SIGTERM, handler_sigterm); + + if (::socketpair(AF_UNIX, SOCK_STREAM, 0, fds)) { + qCDebug(LOG_BASIC) << "Could not create socket pair"; + return -1; + } + + // set Qt plugin library paths for release build + //todo move to LinuxUtils + char result[PATH_MAX] = {}; + ssize_t count = readlink("/proc/self/exe", result, PATH_MAX); + const char *path; + if (count != -1) { + path = dirname(result); + } + QStringList pluginsPath; + pluginsPath << QString::fromStdString(path) + "/plugins"; + QCoreApplication::setLibraryPaths(pluginsPath); +#endif + + qSetMessagePattern("[{gmt_time} %{time process}] [%{category}]\t %{message}"); + + qputenv("QT_EVENT_DISPATCHER_CORE_FOUNDATION", "1"); + + // Unlike the GUI application, the guard does not participate in the event loop anyway, + // so it can be started early. + windscribe::SingleAppInstance appSingleInstGuard; + if (appSingleInstGuard.isRunning()) { + qCDebug(LOG_BASIC) << "Windscribe already running"; + return 0; + } + + WindscribeApplication a(argc, argv); + a.setSigTermHandler(fds[1]); + + // These values are used for QSettings by default + a.setOrganizationName("Windscribe"); + a.setApplicationName("Windscribe2"); + + // Switch to staging if necessary. It should be done at the beginning of main function. + // Further, in all parts of the program, the staging check is performed by the function AppVersion::instance().isStaging() + // Works only for guinea pig builds +#ifdef WINDSCRIBE_IS_GUINEA_PIG + if (ExtraConfig::instance().getIsStaging()) { + AppVersion::instance().switchToStaging(); + } +#endif + + Logger::instance().install("cli", true, false); + + qCDebug(LOG_BASIC) << "App start time:" << QDateTime::currentDateTime().toString(); + qCDebug(LOG_BASIC) << "App version:" << AppVersion::instance().fullVersionString(); + qCDebug(LOG_BASIC) << "OS Version:" << Utils::getOSVersion(); + qCDebug(LOG_BASIC) << "Platform: CLI"; +#if defined(Q_OS_LINUX) + qCDebug(LOG_BASIC) << "Distribution:" << LinuxUtils::getDistroName(); +#endif + qCDebug(LOG_BASIC) << "CPU architecture:" << QSysInfo::currentCpuArchitecture(); + // To aid us in diagnosing possible region-specific issues. + qCDebug(LOG_BASIC) << "UI languages:" << QLocale::system().uiLanguages(); + + ExtraConfig::instance().logExtraConfig(); + + // We do this here so that the service constructor happeens after log initialization & changing gid + MainService *service = new MainService(); + a.setService(service); + + if (!QFileInfo::exists(OpenVpnVersionController::instance().getOpenVpnFilePath())) { + qCDebug(LOG_BASIC) << "OpenVPN executable not found"; + return 0; + } + + int ret = a.exec(); + qCDebug(LOG_BASIC) << "Releasing lock"; + appSingleInstGuard.release(); + return ret; +} diff --git a/client/main.cpp b/client/main_gui.cpp similarity index 90% rename from client/main.cpp rename to client/main_gui.cpp index 25ca99c55..1fa7043c7 100644 --- a/client/main.cpp +++ b/client/main_gui.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include "gui/dpiscalemanager.h" @@ -22,12 +23,15 @@ #include "utils/installedantiviruses_win.h" #include "utils/winutils.h" #elif defined (Q_OS_MACOS) + #include + #include #include "utils/macutils.h" #elif defined (Q_OS_LINUX) #include // dirname #include // readlink #include // PATH_MAX #include + #include #include // gid_t #include "utils/linuxutils.h" #endif @@ -36,29 +40,42 @@ void applyScalingFactor(qreal ldpi, MainWindow &mw); #if defined (Q_OS_MAC) || defined (Q_OS_LINUX) MainWindow *g_MainWindow = NULL; - void handler_sigterm(int signum) - { - if (signum == SIGTERM) - { - qCDebug(LOG_BASIC) << "SIGTERM signal received"; - - // on linux we consider the SIGTERM as a reboot of the OS, because there is no other option. - #ifdef Q_OS_LINUX - WindscribeApplication::instance()->setWasRestartOSFlag(); - #endif - - if (g_MainWindow) - { - g_MainWindow->doClose(NULL, true); - } - exit(0); +static int fds[2]; + +void handler_sigterm(int signum) +{ + if (signum == SIGTERM) { + qCDebug(LOG_BASIC) << "SIGTERM signal received"; + +#ifdef Q_OS_LINUX + WindscribeApplication::instance()->setWasRestartOSFlag(); +#endif + + // Can't call Qt functions safely from here, see https://doc.qt.io/qt-6/unix-signals.html + // Instead, write a byte to a local socket + char a = 1; + if (::write(fds[0], &a, sizeof(a)) < 0) { + qCDebug(LOG_BASIC) << "Could not write to signal socket"; } } +} #endif int main(int argc, char *argv[]) { +#if defined (Q_OS_LINUX) + gid_t gid = LinuxUtils::getWindscribeGid(); + qCDebug(LOG_BASIC) << "Setting gid to:" << gid; + if (setgid(gid) < 0) { + qCDebug(LOG_BASIC) << "Could not set windscribe group"; + return 0; + } +#endif + #if defined (Q_OS_MAC) || defined (Q_OS_LINUX) + if (::socketpair(AF_UNIX, SOCK_STREAM, 0, fds)) { + qCDebug(LOG_BASIC) << "Could not open signal socket"; + } signal(SIGTERM, handler_sigterm); #endif @@ -175,15 +192,6 @@ int main(int argc, char *argv[]) } #endif -#if defined (Q_OS_LINUX) - gid_t gid = LinuxUtils::getWindscribeGid(); - qCDebug(LOG_BASIC) << "Setting gid to:" << gid; - if (setgid(gid) < 0) { - qCDebug(LOG_BASIC) << "Could not set windscribe group"; - return 0; - } -#endif - if (!QFileInfo::exists(OpenVpnVersionController::instance().getOpenVpnFilePath())) { qCDebug(LOG_BASIC) << "OpenVPN executable not found"; return 0; @@ -204,8 +212,10 @@ int main(int argc, char *argv[]) DpiScaleManager::instance(); // init dpi scale manager MainWindow w; + #if defined (Q_OS_MAC) || defined (Q_OS_LINUX) g_MainWindow = &w; + w.setSigTermHandler(fds[1]); #endif w.showAfterLaunch(); @@ -213,8 +223,6 @@ int main(int argc, char *argv[]) #if defined (Q_OS_MAC) || defined (Q_OS_LINUX) g_MainWindow = nullptr; #endif - ImageResourcesSvg::instance().finishGracefully(); - appSingleInstGuard.release(); return ret; diff --git a/gui/cli/CMakeLists.txt b/gui/cli/CMakeLists.txt index 1d62399ca..1f047f5fa 100644 --- a/gui/cli/CMakeLists.txt +++ b/gui/cli/CMakeLists.txt @@ -9,7 +9,7 @@ set(CMAKE_AUTOUIC ON) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) -find_package(Qt6 COMPONENTS Core Network Core5Compat) +find_package(Qt6 COMPONENTS Core Network Core5Compat LinguistTools) find_package(OpenSSL REQUIRED) # build_all.py sets this option when invoked with the '--sign' flag. Disabled by default @@ -18,18 +18,28 @@ if(DEFINE_USE_SIGNATURE_CHECK_MACRO) add_definitions(-DUSE_SIGNATURE_CHECK) endif(DEFINE_USE_SIGNATURE_CHECK_MACRO) +if(UNIX AND (NOT APPLE)) + # if build requested a headless client, set the necessary definitions + option(DEFINE_CLI_ONLY_MACRO "Build GUI-less client" OFF) + if(DEFINE_CLI_ONLY_MACRO) + add_definitions(-DCLI_ONLY) + endif(DEFINE_CLI_ONLY_MACRO) +endif() + set(SOURCES backendcommander.cpp backendcommander.h cliarguments.cpp cliarguments.h + languagecontroller.cpp main.cpp + strings.cpp ../../client/common/utils/ipvalidation.cpp ../../client/common/ipc/commandfactory.cpp ../../client/common/ipc/connection.cpp - ../../client/common/ipc/clicommands.cpp ../../client/common/ipc/server.cpp ../../client/common/types/locationid.cpp + ../../client/common/types/protocol.cpp ../../client/common/utils/extraconfig.cpp ../../client/common/utils/languagesutil.cpp ../../client/common/utils/logger.cpp @@ -81,6 +91,39 @@ endif() add_executable(windscribe-cli ${SOURCES}) +set(TS_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/translations/windscribe_cli_ar.ts + ${CMAKE_CURRENT_SOURCE_DIR}/translations/windscribe_cli_cs.ts + ${CMAKE_CURRENT_SOURCE_DIR}/translations/windscribe_cli_de.ts + ${CMAKE_CURRENT_SOURCE_DIR}/translations/windscribe_cli_en.ts + ${CMAKE_CURRENT_SOURCE_DIR}/translations/windscribe_cli_es.ts + ${CMAKE_CURRENT_SOURCE_DIR}/translations/windscribe_cli_fa.ts + ${CMAKE_CURRENT_SOURCE_DIR}/translations/windscribe_cli_fr.ts + ${CMAKE_CURRENT_SOURCE_DIR}/translations/windscribe_cli_hi.ts + ${CMAKE_CURRENT_SOURCE_DIR}/translations/windscribe_cli_id.ts + ${CMAKE_CURRENT_SOURCE_DIR}/translations/windscribe_cli_it.ts + ${CMAKE_CURRENT_SOURCE_DIR}/translations/windscribe_cli_ja.ts + ${CMAKE_CURRENT_SOURCE_DIR}/translations/windscribe_cli_ko.ts + ${CMAKE_CURRENT_SOURCE_DIR}/translations/windscribe_cli_pl.ts + ${CMAKE_CURRENT_SOURCE_DIR}/translations/windscribe_cli_pt.ts + ${CMAKE_CURRENT_SOURCE_DIR}/translations/windscribe_cli_ru.ts + ${CMAKE_CURRENT_SOURCE_DIR}/translations/windscribe_cli_tr.ts + ${CMAKE_CURRENT_SOURCE_DIR}/translations/windscribe_cli_uk.ts + ${CMAKE_CURRENT_SOURCE_DIR}/translations/windscribe_cli_vi.ts + ${CMAKE_CURRENT_SOURCE_DIR}/translations/windscribe_cli_zh-CN.ts + ${CMAKE_CURRENT_SOURCE_DIR}/translations/windscribe_cli_zh-TW.ts +) +qt6_create_translation(QM_FILES ${CMAKE_CURRENT_SOURCE_DIR} ${TS_FILES} OPTIONS -extensions cpp,h -locations none) + +qt_add_resources(windscribe-cli qm_files + PREFIX + "/translations" + BASE + ${CMAKE_CURRENT_BINARY_DIR} + FILES + ${QM_FILES} +) + if(APPLE) set_property(TARGET windscribe-cli APPEND_STRING PROPERTY COMPILE_FLAGS "-fobjc-arc") endif() diff --git a/gui/cli/backendcommander.cpp b/gui/cli/backendcommander.cpp index a548943e7..274160ba3 100644 --- a/gui/cli/backendcommander.cpp +++ b/gui/cli/backendcommander.cpp @@ -4,12 +4,13 @@ #include "ipc/clicommands.h" #include "ipc/connection.h" -#include "types/locationid.h" +#include "languagecontroller.h" +#include "strings.h" #include "utils/logger.h" #include "utils/utils.h" -BackendCommander::BackendCommander(const CliArguments &cliArgs) : QObject() - , cliArgs_(cliArgs) + +BackendCommander::BackendCommander(const CliArguments &cliArgs) : QObject(), cliArgs_(cliArgs) { unsigned long cliPid = Utils::getCurrentPid(); qCDebug(LOG_BASIC) << "CLI pid: " << cliPid; @@ -23,9 +24,8 @@ BackendCommander::~BackendCommander() } } -void BackendCommander::initAndSend(bool isGuiAlreadyRunning) +void BackendCommander::initAndSend() { - isGuiAlreadyRunning_ = isGuiAlreadyRunning; connection_ = new IPC::Connection(); connect(connection_, &IPC::Connection::newCommand, this, &BackendCommander::onConnectionNewCommand, Qt::QueuedConnection); connect(connection_, &IPC::Connection::stateChanged, this, &BackendCommander::onConnectionStateChanged, Qt::QueuedConnection); @@ -36,103 +36,41 @@ void BackendCommander::initAndSend(bool isGuiAlreadyRunning) void BackendCommander::onConnectionNewCommand(IPC::Command *command, IPC::Connection * /*connection*/) { - if (bCommandSent_ && command->getStringId() == IPC::CliCommands::LocationsShown::getCommandStringId()) { - emit finished(0, tr("Viewing Locations...")); - } - else if (bCommandSent_ && command->getStringId() == IPC::CliCommands::ConnectToLocationAnswer::getCommandStringId()) { - IPC::CliCommands::ConnectToLocationAnswer *cmd = static_cast(command); - - if (cmd->isSuccess_) { - qCDebug(LOG_BASIC) << "Connecting to" << cmd->location_; - emit report("Connecting to " + cmd->location_); - } - else { - emit finished(1, tr("Error: Could not find server matching: \"") + cliArgs_.location() + "\" or the location is disabled"); - } - } - else if (bCommandSent_ && command->getStringId() == IPC::CliCommands::ConnectStateChanged::getCommandStringId()) { - IPC::CliCommands::ConnectStateChanged *cmd = static_cast(command); - - if (cliArgs_.cliCommand() >= CLI_COMMAND_CONNECT && cliArgs_.cliCommand() <= CLI_COMMAND_DISCONNECT) { - if (cmd->connectState.connectState == CONNECT_STATE_CONNECTED) { - if (cmd->connectState.location.isValid()) { - LocationID currentLocation = cmd->connectState.location; - - QString locationConnectedTo; - if (currentLocation.isBestLocation()) { - locationConnectedTo = tr("Best Location"); - } - else { - locationConnectedTo = cmd->connectState.location.city(); - } - emit finished(0, tr("Connected to ") + locationConnectedTo); - } - } - else if (cmd->connectState.connectState == CONNECT_STATE_DISCONNECTED) { - emit finished(0, tr("Disconnected")); - } - } - } - else if (bCommandSent_ && command->getStringId() == IPC::CliCommands::AlreadyDisconnected::getCommandStringId()) { - emit finished(0, tr("Already Disconnected")); - } - else if (command->getStringId() == IPC::CliCommands::State::getCommandStringId()) { + if (command->getStringId() == IPC::CliCommands::Acknowledge::getCommandStringId()) { + // There are currently no commands that return a real value we need to parse here + emit finished(0, QString()); + } else if (command->getStringId() == IPC::CliCommands::State::getCommandStringId()) { if (cliArgs_.cliCommand() == CLI_COMMAND_STATUS) { - onStatusResponse(command); - } - else { - onLoginStateResponse(command); - } - } - else if (command->getStringId() == IPC::CliCommands::FirewallStateChanged::getCommandStringId()) { - IPC::CliCommands::FirewallStateChanged *cmd = static_cast(command); - if (cmd->isFirewallEnabled_) { - if (cmd->isFirewallAlwaysOn_) { - emit finished(0, tr("Firewall is in Always On mode and cannot be changed")); - } - else { - emit finished(0, tr("Firewall is ON")); - } - } - else { - emit finished(0, tr("Firewall is OFF")); - } - } - else if (bCommandSent_ && command->getStringId() == IPC::CliCommands::SignedOut::getCommandStringId()) { - emit finished(0, tr("Signed out")); - } - else if (bCommandSent_ && command->getStringId() == IPC::CliCommands::LoginResult::getCommandStringId()) { - IPC::CliCommands::LoginResult *cmd = static_cast(command); - if (cmd->isLoggedIn_) { - emit finished(0, tr("login successful")); - } - else { - QString errorMessage(tr("login failed")); - if (!cmd->loginError_.isEmpty()) { - errorMessage += tr(". %1").arg(cmd->loginError_); - } - emit finished(1, errorMessage); + // If we explicitly requested status, print it. + onStateResponse(command); + } else { + // If it's a response for the initial state request, send the command now. + IPC::CliCommands::State *state = static_cast(command); + sendCommand(state); } + } else if (command->getStringId() == IPC::CliCommands::LocationsList::getCommandStringId()) { + IPC::CliCommands::LocationsList *cmd = static_cast(command); + emit finished(0, cmd->locations_.join("\n")); } } void BackendCommander::onConnectionStateChanged(int state, IPC::Connection * /*connection*/) { if (state == IPC::CONNECTION_CONNECTED) { - qCDebug(LOG_BASIC) << "Connected to GUI server"; + qCDebug(LOG_BASIC) << "Connected to app"; ipcState_ = IPC_CONNECTED; loggedInTimer_.start(); sendStateCommand(); } else if (state == IPC::CONNECTION_DISCONNECTED) { - qCDebug(LOG_BASIC) << "Disconnected from GUI server"; + qCDebug(LOG_BASIC) << "Disconnected from app"; emit finished(0, ""); } else if (state == IPC::CONNECTION_ERROR) { if (ipcState_ == IPC_CONNECTING) { if (connectingTimer_.isValid() && connectingTimer_.elapsed() > MAX_WAIT_TIME_MS) { connectingTimer_.invalidate(); - emit finished(1, "Aborting: Gui did not start in time"); + emit finished(1, QObject::tr("Aborting: app did not start in time")); } else { // Try connect again. Delay is necessary so that Engine process will actually start @@ -141,50 +79,104 @@ void BackendCommander::onConnectionStateChanged(int state, IPC::Connection * /*c } } else { - emit finished(1, "Aborting: IPC communication error"); + emit finished(1, QObject::tr("Aborting: IPC communication error")); } } } -void BackendCommander::sendCommand() +void BackendCommander::sendCommand(IPC::CliCommands::State *state) { - if (cliArgs_.cliCommand() == CLI_COMMAND_CONNECT || cliArgs_.cliCommand() == CLI_COMMAND_CONNECT_BEST || cliArgs_.cliCommand() == CLI_COMMAND_CONNECT_LOCATION) { - qCDebug(LOG_BASIC) << "Connecting to last"; + LanguageController::instance().setLanguage(state->language_); + if (cliArgs_.cliCommand() == CLI_COMMAND_CONNECT || cliArgs_.cliCommand() == CLI_COMMAND_CONNECT_BEST || cliArgs_.cliCommand() == CLI_COMMAND_CONNECT_LOCATION) { + if (state->loginState_ != LOGIN_STATE_LOGGED_IN) { + emit finished(1, QObject::tr("Not logged in")); + return; + } IPC::CliCommands::Connect cmd; cmd.location_ = cliArgs_.location(); + cmd.protocol_ = cliArgs_.protocol(); connection_->sendCommand(cmd); } else if (cliArgs_.cliCommand() == CLI_COMMAND_DISCONNECT) { + if (state->loginState_ != LOGIN_STATE_LOGGED_IN) { + emit finished(1, QObject::tr("Not logged in")); + return; + } IPC::CliCommands::Disconnect cmd; connection_->sendCommand(cmd); } else if (cliArgs_.cliCommand() == CLI_COMMAND_FIREWALL_ON) { + if (state->isFirewallOn_) { + emit finished(1, QObject::tr("Firewall already on")); + return; + } IPC::CliCommands::Firewall cmd; cmd.isEnable_ = true; connection_->sendCommand(cmd); } else if (cliArgs_.cliCommand() == CLI_COMMAND_FIREWALL_OFF) { + if (!state->isFirewallOn_) { + emit finished(1, QObject::tr("Firewall already off")); + return; + } + if (state->isFirewallAlwaysOn_) { + emit finished(1, QObject::tr("Firewall set to always on and can't be turned off")); + return; + } + IPC::CliCommands::Firewall cmd; cmd.isEnable_ = false; connection_->sendCommand(cmd); } else if (cliArgs_.cliCommand() == CLI_COMMAND_LOCATIONS) { + if (state->loginState_ != LOGIN_STATE_LOGGED_IN) { + emit finished(1, QObject::tr("Not logged in")); + return; + } IPC::CliCommands::ShowLocations cmd; connection_->sendCommand(cmd); } else if (cliArgs_.cliCommand() == CLI_COMMAND_LOGIN) { + if (state->loginState_ == LOGIN_STATE_LOGGED_IN) { + emit finished(1, QObject::tr("Already logged in")); + return; + } IPC::CliCommands::Login cmd; cmd.username_ = cliArgs_.username(); cmd.password_ = cliArgs_.password(); cmd.code2fa_ = cliArgs_.code2fa(); connection_->sendCommand(cmd); } - else if (cliArgs_.cliCommand() == CLI_COMMAND_SIGN_OUT) { - IPC::CliCommands::SignOut cmd; + else if (cliArgs_.cliCommand() == CLI_COMMAND_LOGOUT) { + if (state->loginState_ == LOGIN_STATE_LOGGED_OUT) { + emit finished(1, QObject::tr("Already logged out")); + return; + } + IPC::CliCommands::Logout cmd; cmd.isKeepFirewallOn_ = cliArgs_.keepFirewallOn(); connection_->sendCommand(cmd); } + else if (cliArgs_.cliCommand() == CLI_COMMAND_SEND_LOGS) { + IPC::CliCommands::SendLogs cmd; + connection_->sendCommand(cmd); + } + else if (cliArgs_.cliCommand() == CLI_COMMAND_UPDATE) { + if (state->loginState_ != LOGIN_STATE_LOGGED_IN) { + emit finished(1, QObject::tr("Not logged in")); + return; + } + if (state->updateAvailable_.isEmpty()) { + emit finished(1, QObject::tr("No update available")); + return; + } + IPC::CliCommands::Update cmd; + connection_->sendCommand(cmd); + } + else if (cliArgs_.cliCommand() == CLI_COMMAND_RELOAD_CONFIG) { + IPC::CliCommands::ReloadConfig cmd; + connection_->sendCommand(cmd); + } else if (cliArgs_.cliCommand() == CLI_COMMAND_STATUS) { sendStateCommand(); } @@ -198,81 +190,31 @@ void BackendCommander::sendStateCommand() connection_->sendCommand(cmd); } -void BackendCommander::onLoginStateResponse(IPC::Command *command) +void BackendCommander::onStateResponse(IPC::Command *command) { IPC::CliCommands::State *cmd = static_cast(command); - if (cmd->isLoggedIn_) { - if (cliArgs_.cliCommand() == CLI_COMMAND_LOGIN) { - emit finished(0, tr("The application is already logged in")); - } - else { - sendCommand(); - } - } - else { - if (cliArgs_.cliCommand() == CLI_COMMAND_LOGIN && cmd->waitingForLoginInfo_) { - // The app has let us know that it is not logged in and does not have cached login info. - loggedInTimer_.invalidate(); + LanguageController::instance().setLanguage(cmd->language_); - if (isGuiAlreadyRunning_) { - sendCommand(); - } - else { - // Encountered an issue where the app UI gets stuck on the 'logging in' screen if we - // send the login command as soon as the app reports that its backend init has finished. - // The app does log in, but the UI doesn't update to reflect this state. The app does - // not currently have a mechanism to let us know when it has finished transitioning to - // the login screen and is ready for us to submit the login request. - QTimer::singleShot(2500, this, &BackendCommander::sendCommand); - } - } - else if (cliArgs_.cliCommand() == CLI_COMMAND_SIGN_OUT && cmd->waitingForLoginInfo_) { - loggedInTimer_.invalidate(); - emit finished(0, tr("The application is already signed out")); - } - else { - if (loggedInTimer_.isValid() && loggedInTimer_.elapsed() > MAX_LOGIN_TIME_MS) { - loggedInTimer_.invalidate(); - emit finished(1, "Aborting: GUI did not login in time"); - } - else { - if (!bLogginInMessageShown_) { - bLogginInMessageShown_ = true; - emit report("GUI is not logged in. Waiting for the login..."); - } - QTimer::singleShot(100, this, &BackendCommander::sendStateCommand); - } - } - } -} + QString msg = QString("%1\n%2\n%3") + .arg(connectivityString(cmd->connectivity_)) + .arg(loginStateString(cmd->loginState_, cmd->loginError_, cmd->loginErrorMessage_)) + .arg(firewallStateString(cmd->isFirewallOn_, cmd->isFirewallAlwaysOn_)); -void BackendCommander::onStatusResponse(IPC::Command *command) -{ - IPC::CliCommands::State *cmd = static_cast(command); + if (cmd->loginState_ != LOGIN_STATE_LOGGED_OUT) { + msg += "\n" + connectStateString(cmd->connectState_, cmd->location_, cmd->tunnelTestState_); + } - QString msg; - if (!cmd->isLoggedIn_) { - msg = tr("Signed out"); + if (cmd->connectState_.connectState != CONNECT_STATE_DISCONNECTED && cmd->protocol_.isValid()) { + msg += "\n" + protocolString(cmd->protocol_, cmd->port_); } - else { - switch (cmd->connectState_) { - case CONNECT_STATE_DISCONNECTED: - msg = tr("Disconnected"); - break; - case CONNECT_STATE_CONNECTED: - msg = tr("Connected"); - break; - case CONNECT_STATE_CONNECTING: - msg = tr("Connecting"); - break; - case CONNECT_STATE_DISCONNECTING: - msg = tr("Disconnecting"); - break; - } - if (cmd->location_.isValid()) { - msg += QString(": %1").arg(cmd->location_.city()); + if (cmd->loginState_ == LOGIN_STATE_LOGGED_IN) { + msg += "\n" + tr("Data usage: %1 / %2") + .arg(dataString(cmd->language_, cmd->trafficUsed_)) + .arg((cmd->trafficMax_ == -1) ? tr("Unlimited") : dataString(cmd->language_, cmd->trafficMax_)); + if (!cmd->updateAvailable_.isEmpty()) { + msg += "\n" + updateString(cmd->updateError_, cmd->updateProgress_, cmd->updateAvailable_); } } diff --git a/gui/cli/backendcommander.h b/gui/cli/backendcommander.h index b2fc566c5..9dd7f98a4 100644 --- a/gui/cli/backendcommander.h +++ b/gui/cli/backendcommander.h @@ -4,6 +4,7 @@ #include #include "cliarguments.h" +#include "ipc/clicommands.h" #include "ipc/command.h" #include "ipc/connection.h" @@ -14,7 +15,7 @@ class BackendCommander : public QObject BackendCommander(const CliArguments &cliArgs); ~BackendCommander(); - void initAndSend(bool isGuiAlreadyRunning); + void initAndSend(); signals: void finished(int returnCode, const QString &errorMsg); @@ -24,7 +25,7 @@ private slots: void onConnectionNewCommand(IPC::Command *command, IPC::Connection *connection); void onConnectionStateChanged(int state, IPC::Connection *connection); - void sendCommand(); + void sendCommand(IPC::CliCommands::State *state); void sendStateCommand(); private: @@ -38,9 +39,7 @@ private slots: QElapsedTimer connectingTimer_; QElapsedTimer loggedInTimer_; bool bCommandSent_ = false; - bool bLogginInMessageShown_ = false; - bool isGuiAlreadyRunning_ = false; + bool bLoggingInMessageShown_ = false; - void onLoginStateResponse(IPC::Command *command); - void onStatusResponse(IPC::Command *command); + void onStateResponse(IPC::Command *command); }; diff --git a/gui/cli/cliarguments.cpp b/gui/cli/cliarguments.cpp index d82d84e0e..46c9315de 100644 --- a/gui/cli/cliarguments.cpp +++ b/gui/cli/cliarguments.cpp @@ -7,65 +7,60 @@ CliArguments::CliArguments() { } +static bool isProtocol(const QString &str) +{ + if (str == "wireguard" || str == "ikev2" || str == "udp" || str == "tcp" || str == "stealth" || str == "wstunnel") { + return true; + } + return false; +} + + void CliArguments::processArguments() { QStringList args = qApp->arguments(); - if (args.length() > 1) - { + if (args.length() > 1) { QString arg1 = args[1].toLower(); - if (arg1 == "connect") - { - if (args.length() > 2) - { + if (arg1 == "connect") { + if (args.length() > 2) { QString arg2 = args[2].toLower(); - if (arg2 == "best") - { - locationStr_ = "best"; - cliCommand_ = CLI_COMMAND_CONNECT_BEST; - } - else - { - locationStr_ = arg2; - cliCommand_ = CLI_COMMAND_CONNECT_LOCATION; + if (isProtocol(arg2)) { + protocol_ = arg2; + cliCommand_ = CLI_COMMAND_CONNECT; + } else { + if (arg2 == "best") { + locationStr_ = "best"; + cliCommand_ = CLI_COMMAND_CONNECT_BEST; + } else { + locationStr_ = arg2; + cliCommand_ = CLI_COMMAND_CONNECT_LOCATION; + } + + if (args.length() > 3 && isProtocol(args[3].toLower())) { + protocol_ = args[3].toLower(); + } } - } - else - { + } else { cliCommand_ = CLI_COMMAND_CONNECT; } - } - else if (arg1 == "disconnect") - { + } else if (arg1 == "disconnect") { cliCommand_ = CLI_COMMAND_DISCONNECT; - } - else if (arg1 == "firewall") - { - if (args.length() > 2) - { + } else if (arg1 == "firewall") { + if (args.length() > 2) { QString arg2 = args[2].toLower(); - if (arg2 == "on") - { + if (arg2 == "on") { cliCommand_ = CLI_COMMAND_FIREWALL_ON; - } - else if (arg2 == "off") - { + } else if (arg2 == "off") { cliCommand_ = CLI_COMMAND_FIREWALL_OFF; } } - } - else if (arg1 == "?" || arg1 == "help") - { + } else if (arg1 == "?" || arg1 == "help") { cliCommand_ = CLI_COMMAND_HELP; - } - else if (arg1 == "locations") - { + } else if (arg1 == "locations") { cliCommand_ = CLI_COMMAND_LOCATIONS; - } - else if (arg1 == "login") - { - if (args.length() >= 4) - { + } else if (arg1 == "login") { + if (args.length() >= 4) { username_ = args.at(2); password_ = args.at(3); if (args.length() > 4) { @@ -78,21 +73,42 @@ void CliArguments::processArguments() cliCommand_ = CLI_COMMAND_LOGIN; } - } - else if (arg1 == "signout") - { - cliCommand_ = CLI_COMMAND_SIGN_OUT; + } else if (arg1 == "logout") { + cliCommand_ = CLI_COMMAND_LOGOUT; if (args.length() > 2) { keepFirewallOn_ = (args[2].toLower() == "on"); - } - else { + } else { keepFirewallOn_ = false; } - } - else if (arg1 == "status") - { + } else if (arg1 == "status") { cliCommand_ = CLI_COMMAND_STATUS; +#ifdef CLI_ONLY + } else if (arg1 == "preferences") { + if (args.length() > 2) { + QString arg2 = args[2].toLower(); + if (arg2 == "reload") { + cliCommand_ = CLI_COMMAND_RELOAD_CONFIG; + } else { + cliCommand_ = CLI_COMMAND_HELP; + } + } else { + cliCommand_ = CLI_COMMAND_HELP; + } +#endif + } else if (arg1 == "update") { + cliCommand_ = CLI_COMMAND_UPDATE; + } else if (arg1 == "logs") { + if (args.length() > 2) { + QString arg2 = args[2].toLower(); + if (arg2 == "send") { + cliCommand_ = CLI_COMMAND_SEND_LOGS; + } else { + cliCommand_ = CLI_COMMAND_HELP; + } + } else { + cliCommand_ = CLI_COMMAND_HELP; + } } } } @@ -122,7 +138,13 @@ const QString &CliArguments::code2fa() const return code2fa_; } +const QString &CliArguments::protocol() const +{ + return protocol_; +} + bool CliArguments::keepFirewallOn() const { return keepFirewallOn_; } + diff --git a/gui/cli/cliarguments.h b/gui/cli/cliarguments.h index 730b90dcc..0e592ee60 100644 --- a/gui/cli/cliarguments.h +++ b/gui/cli/cliarguments.h @@ -13,8 +13,11 @@ enum CliCommand { CLI_COMMAND_FIREWALL_OFF, CLI_COMMAND_LOCATIONS, CLI_COMMAND_LOGIN, - CLI_COMMAND_SIGN_OUT, - CLI_COMMAND_STATUS + CLI_COMMAND_LOGOUT, + CLI_COMMAND_RELOAD_CONFIG, + CLI_COMMAND_SEND_LOGS, + CLI_COMMAND_STATUS, + CLI_COMMAND_UPDATE, }; class CliArguments @@ -28,6 +31,7 @@ class CliArguments const QString &username() const; const QString &password() const; const QString &code2fa() const; + const QString &protocol() const; bool keepFirewallOn() const; private: @@ -36,5 +40,6 @@ class CliArguments QString username_; QString password_; QString code2fa_; + QString protocol_ = ""; bool keepFirewallOn_ = false; }; diff --git a/gui/cli/languagecontroller.cpp b/gui/cli/languagecontroller.cpp new file mode 100644 index 000000000..5945e0c97 --- /dev/null +++ b/gui/cli/languagecontroller.cpp @@ -0,0 +1,58 @@ +#include "languagecontroller.h" + +#include + +#include "utils/languagesutil.h" +#include "utils/logger.h" + +LanguageController::LanguageController() +{ +} + +LanguageController::~LanguageController() +{ +} + +QString LanguageController::getLanguage() const +{ + return language_; +} + +void LanguageController::setLanguage(const QString &language) +{ + if (language != language_) { + bool languageLoaded = false; + + // We don't have a language file for English, since all the hard-coded strings are + // already in English. + if (language != "en") { + // Try to load the specified language. If that fails, try to load the system language. + languageLoaded = loadLanguage(language); + if (!languageLoaded) { + languageLoaded = loadLanguage(LanguagesUtil::systemLanguage()); + } + } + + if (!languageLoaded) { + qApp->removeTranslator(&translator_); + language_ = "en"; + } + + emit languageChanged(); + } +} + +bool LanguageController::loadLanguage(const QString &language) +{ + const QString filename = ":/translations/windscribe_cli_" + language + ".qm"; + + if (translator_.load(filename)) { + qCDebug(LOG_BASIC) << "LanguageController::setLanguage - language changed:" << language; + qApp->installTranslator(&translator_); + language_ = language; + return true; + } + + qCDebug(LOG_BASIC) << "LanguageController::setLanguage - failed to load language file:" << filename; + return false; +} diff --git a/gui/cli/languagecontroller.h b/gui/cli/languagecontroller.h new file mode 100644 index 000000000..df3bd16d2 --- /dev/null +++ b/gui/cli/languagecontroller.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include + + +class LanguageController : public QObject +{ + Q_OBJECT +public: + + static LanguageController &instance() + { + static LanguageController lc; + return lc; + } + + void setLanguage(const QString &language); + QString getLanguage() const; + +signals: + void languageChanged(); + +private: + LanguageController(); + ~LanguageController(); + + QString language_; + + QTranslator translator_; + + bool loadLanguage(const QString &language); +}; diff --git a/gui/cli/main.cpp b/gui/cli/main.cpp index 0e80d37f0..ea80926ff 100644 --- a/gui/cli/main.cpp +++ b/gui/cli/main.cpp @@ -7,6 +7,8 @@ #include "backendcommander.h" #include "cliarguments.h" +#include "languagecontroller.h" +#include "utils/languagesutil.h" #include "utils/logger.h" #include "utils/utils.h" @@ -45,29 +47,59 @@ int main(int argc, char *argv[]) CliArguments cliArgs; cliArgs.processArguments(); - if (cliArgs.cliCommand() == CLI_COMMAND_NONE) - { - qCDebug(LOG_BASIC) << "CLI args fail: Couldn't determine appropriate command from arguments"; - QString output = QObject::tr("There appears to be an issue with the provided arguments. Try 'windscribe-cli help' to see available options"); - std::cout << output.toStdString() << std::endl; - return 1; - } - else if (cliArgs.cliCommand() == CLI_COMMAND_HELP) - { + LanguageController::instance().setLanguage(LanguagesUtil::systemLanguage()); + + if (cliArgs.cliCommand() == CLI_COMMAND_NONE || cliArgs.cliCommand() == CLI_COMMAND_HELP) { qCDebug(LOG_BASIC) << "Printing help menu"; - std::cout << "windscribe-cli supports the following commands:" << std::endl; - std::cout << "connect - Connects to last connected location. If no last location, connect to best location." << std::endl; - std::cout << "connect best - Connects to best location" << std::endl; - std::cout << "connect \"RegionName\" - Connects to a random datacenter in the region" << std::endl; - std::cout << "connect \"CityName\" - Connects to a random datacenter in the city" << std::endl; - std::cout << "connect \"Nickname\" - Connects to datacenter with same nickname" << std::endl; - std::cout << "connect \"ISO CountryCode\" - Connects to a random data center in the country" << std::endl; - std::cout << "disconnect - Disconnects from current datacenter" << std::endl; - std::cout << "firewall on|off - Turn firewall ON/OFF" << std::endl; - std::cout << "locations - View a list of available locations" << std::endl; - std::cout << "login \"username\" \"password\" [2FA code] - login with given username and password, and optional two-factor authentication code" << std::endl; - std::cout << "signout [on|off] - Sign out of the application, and optionally leave the firewall ON/OFF" << std::endl; - std::cout << "status - View the connected/disconnected state of the application" << std::endl; + std::cout << "windscribe-cli :" << std::endl; + std::cout << std::endl; + std::cout << "Authenticating with Windscribe" << std::endl; + std::cout << " login \"username\" \"password\" [2FA code]" << std::endl; + std::cout << " " << "Login with given username and password, and optional two-factor authentication code" << std::endl; + std::cout << " logout [on|off]" << std::endl; + std::cout << " " << "Sign out of the application, and optionally leave the firewall ON/OFF" << std::endl; + std::cout << std::endl; + std::cout << "Getting application state" << std::endl; + std::cout << " status" << std::endl; + std::cout << " " << "View basic login, connection, and account information" << std::endl; + std::cout << " locations" << std::endl; + std::cout << " " << "View a list of available locations" << std::endl; + std::cout << std::endl; +#ifdef CLI_ONLY + std::cout << "Managing preferences" << std::endl; + std::cout << " preferences reload" << std::endl; + std::cout << " " << "Reload the preference conf file (located at ~/.config/Windscribe/windscribe-cli.conf)" << std::endl; + std::cout << std::endl; +#endif + std::cout << "Managing VPN and firewall" << std::endl; + std::cout << " connect [protocol]" << std::endl; + std::cout << " " << "Connects to last connected location. If no last location, connect to best location." << std::endl; + std::cout << " connect best [protocol]" << std::endl; + std::cout << " " << "Connects to best location" << std::endl; + std::cout << " connect \"RegionName\" [protocol]" << std::endl; + std::cout << " " << "Connects to a random datacenter in the region" << std::endl; + std::cout << " connect \"CityName\" [protocol]" << std::endl; + std::cout << " " << "Connects to a random datacenter in the city" << std::endl; + std::cout << " connect \"Nickname\" [protocol]" << std::endl; + std::cout << " " << "Connects to datacenter with same nickname" << std::endl; + std::cout << " connect \"ISO CountryCode\" [protocol]" << std::endl; + std::cout << " " << "Connects to a random data center in the country" << std::endl; + std::cout << " disconnect" << std::endl; + std::cout << " " << "Disconnects from current datacenter" << std::endl; + std::cout << " firewall on|off" << std::endl; + std::cout << " " << "Turn firewall ON/OFF" << std::endl; + std::cout << std::endl; +#ifdef Q_OS_LINUX + std::cout << "Protocols: " << "wireguard, udp, tcp, stealth, wstunnel" << std::endl; +#else + std::cout << "Protocols: " << "wireguard, ikev2, udp, tcp, stealth, wstunnel" << std::endl; +#endif + std::cout << std::endl; + std::cout << "Application administration" << std::endl; + std::cout << " logs send" << std:: endl; + std::cout << " " << "Send debug log to Windscribe" << std::endl; + std::cout << " update" << std::endl; + std::cout << " " << "Updates to the latest available version" << std::endl; return 0; } @@ -84,43 +116,39 @@ int main(int argc, char *argv[]) logAndCout(msg); }); - if (Utils::isGuiAlreadyRunning()) - { - qCDebug(LOG_BASIC) << "GUI detected -- attempting Engine connect"; - backendCommander->initAndSend(true); - } - else - { - if (cliArgs.cliCommand() == CLI_COMMAND_SIGN_OUT) - { - logAndCout(QCoreApplication::tr("The application is not running, signout not required.")); - return 0; + if (Utils::isAppAlreadyRunning()) { + backendCommander->initAndSend(); + } else { +#ifdef CLI_ONLY + int err = system("systemctl --user daemon-reload && systemctl --user start windscribe"); + if (err != 0) { + logAndCout("Could not start application"); + return err; } - - logAndCout(QCoreApplication::tr("No GUI instance detected, starting one now...")); - - // GUI pathing + backendCommander->initAndSend(); +#else #ifdef Q_OS_WIN - QString guiPath = QCoreApplication::applicationDirPath() + "/Windscribe.exe"; + QString appPath = QCoreApplication::applicationDirPath() + "/Windscribe.exe"; #else - QString guiPath = QCoreApplication::applicationDirPath() + "/Windscribe"; + QString appPath = QCoreApplication::applicationDirPath() + "/Windscribe"; #endif QString workingDir = QCoreApplication::applicationDirPath(); #ifdef Q_OS_WIN - QProcess::startDetached(guiPath, QStringList(), workingDir); - backendCommander->initAndSend(false); + QProcess::startDetached(appPath, QStringList(), workingDir); + backendCommander->initAndSend(); #else // use non-static start detached to prevent GUI output from polluting cli QProcess process; - process.setProgram(guiPath); + process.setProgram(appPath); process.setWorkingDirectory(workingDir); process.setStandardOutputFile(QProcess::nullDevice()); process.setStandardErrorFile(QProcess::nullDevice()); qint64 pid; process.startDetached(&pid); - backendCommander->initAndSend(false); + backendCommander->initAndSend(); +#endif #endif } @@ -129,6 +157,9 @@ int main(int argc, char *argv[]) void logAndCout(const QString &str) { + if (str.isEmpty()) { + return; + } qCDebug(LOG_BASIC) << str; std::cout << str.toStdString() << std::endl; } diff --git a/gui/cli/strings.cpp b/gui/cli/strings.cpp new file mode 100644 index 000000000..dc719c190 --- /dev/null +++ b/gui/cli/strings.cpp @@ -0,0 +1,135 @@ +#include "strings.h" + +QString connectivityString(bool connectivity) +{ + return QObject::tr("Internet connectivity: %1").arg(connectivity ? QObject::tr("available") : QObject::tr("unavailable")); +} + +QString loginStateString(LOGIN_STATE state, LOGIN_RET loginError, const QString &loginErrorMessage) +{ + QString msg = QObject::tr("Login state: %1"); + + switch (state) { + case LOGIN_STATE_LOGGED_OUT: + return msg.arg(QObject::tr("Logged out")); + case LOGIN_STATE_LOGGING_IN: + return msg.arg(QObject::tr("Logging in")); + case LOGIN_STATE_LOGGED_IN: + return msg.arg(QObject::tr("Logged in")); + case LOGIN_STATE_LOGIN_ERROR: + default: + QString error = QObject::tr("Error: %1"); + + switch(loginError) { + case LOGIN_RET_NO_API_CONNECTIVITY: + return msg.arg(error.arg(QObject::tr("No internet connectivity"))); + case LOGIN_RET_NO_CONNECTIVITY: + return msg.arg(error.arg(QObject::tr("No API connectivity"))); + case LOGIN_RET_BAD_USERNAME: + case LOGIN_RET_MISSING_CODE2FA: + return msg.arg(error.arg(QObject::tr("Incorrect username, password, or 2FA code"))); + case LOGIN_RET_ACCOUNT_DISABLED: + return msg.arg(error.arg(loginErrorMessage)); + case LOGIN_RET_SSL_ERROR: + return msg.arg(error.arg(QObject::tr("SSL error"))); + case LOGIN_RET_SESSION_INVALID: + return msg.arg(error.arg(QObject::tr("Session expired"))); + case LOGIN_RET_RATE_LIMITED: + return msg.arg(error.arg(QObject::tr("Rate limited"))); + case LOGIN_RET_INCORRECT_JSON: + case LOGIN_RET_SUCCESS: + default: + return msg.arg(QObject::tr("Unknown error")); + } + } +} + +QString connectStateString(types::ConnectState state, LocationID location, TUNNEL_TEST_STATE tunnelTest) +{ + QString msg = QObject::tr("Connect state: %1"); + QString connectState; + + switch (state.connectState) { + case CONNECT_STATE_CONNECTED: + if (location.isValid()) { + connectState = msg.arg(QObject::tr("Connected: %1").arg(location.city())); + } else { + connectState = msg.arg(QObject::tr("Connected")); + } + if (tunnelTest == TUNNEL_TEST_STATE_UNKNOWN) { + connectState = "*" + connectState; + } else if (tunnelTest == TUNNEL_TEST_STATE_FAILURE) { + connectState += " " + QObject::tr("[Network interference]"); + } + return connectState; + case CONNECT_STATE_CONNECTING: + if (location.isValid()) { + return msg.arg(QObject::tr("Connecting: %1").arg(location.city())); + } else { + return msg.arg(QObject::tr("Connecting")); + } + case CONNECT_STATE_DISCONNECTING: + return msg.arg(QObject::tr("Disconnecting")); + case CONNECT_STATE_DISCONNECTED: + switch(state.connectError) { + case NO_CONNECT_ERROR: + return msg.arg(QObject::tr("Disconnected")); + case LOCATION_NOT_EXIST: + case LOCATION_NO_ACTIVE_NODES: + return msg.arg(QObject::tr("Error: Location does not exist or is disabled")); + case CONNECTION_BLOCKED: + return msg.arg(QObject::tr("Error: You are out of data, or your account has been disabled. Upgrade to Pro to continue using Windscribe")); + case CTRLD_START_FAILED: + return msg.arg(QObject::tr("Error: Unable to start custom DNS service")); + case WIREGUARD_ADAPTER_SETUP_FAILED: + return msg.arg(QObject::tr("Error: WireGuard adapter setup failed")); + case WIREGUARD_COULD_NOT_RETRIEVE_CONFIG: + return msg.arg(QObject::tr("Error: Could not retrieve WireGuard configuration")); + default: + return msg.arg(QObject::tr("Error: %1").arg(state.connectError)); + } + default: + return msg.arg(QObject::tr("Unknown state")); + } +} + +QString protocolString(types::Protocol protocol, uint port) +{ + return QObject::tr("Protocol: %1:%2").arg(protocol.toLongString()).arg(port); +} + +QString firewallStateString(bool isFirewallOn, bool isFirewallAlwaysOn) +{ + QString msg = QObject::tr("Firewall state: %1"); + + if (isFirewallAlwaysOn) { + return msg.arg(QObject::tr("Always On")); + } + else if (isFirewallOn) { + return msg.arg(QObject::tr("On")); + } + else { + return msg.arg(QObject::tr("Off")); + } +} + +QString dataString(const QString &language, qint64 data) +{ + QLocale locale(language); + return locale.formattedDataSize(data, 2, QLocale::DataSizeTraditionalFormat); +} + +QString updateString(UPDATE_VERSION_ERROR error, uint progress, const QString &updateAvailable) +{ + if (error == UPDATE_VERSION_ERROR_DL_FAIL) { + return QObject::tr("Update error: download failed. Please try again or try a different network."); + } else if (error != UPDATE_VERSION_ERROR_NO_ERROR) { + return QObject::tr("Update error: %1").arg(error); + } else if (progress != 0) { + return QObject::tr("Update downloading: %1%%").arg(progress); + } else if (!updateAvailable.isEmpty()) { + return QObject::tr("Update available: %1").arg(updateAvailable); + } + return QObject::tr("No update available"); +} + diff --git a/gui/cli/strings.h b/gui/cli/strings.h new file mode 100644 index 000000000..c84ac17ec --- /dev/null +++ b/gui/cli/strings.h @@ -0,0 +1,14 @@ +#include + +#include "ipc/clicommands.h" +#include "ipc/connection.h" +#include "types/locationid.h" + +QString connectivityString(bool connectivity); +QString loginStateString(LOGIN_STATE state, LOGIN_RET loginError, const QString &loginErrorMessage); +QString connectStateString(types::ConnectState state, LocationID location, TUNNEL_TEST_STATE tunnelTest); +QString protocolString(types::Protocol protocol, uint port); +QString firewallStateString(bool isFirewallOn, bool isFirewallAlwaysOn); +QString dataString(const QString &language, qint64 data); +QString updateString(UPDATE_VERSION_ERROR error, uint progress, const QString &updateAvailable); + diff --git a/gui/cli/translations/windscribe_cli_ar.ts b/gui/cli/translations/windscribe_cli_ar.ts new file mode 100644 index 000000000..a4226048d --- /dev/null +++ b/gui/cli/translations/windscribe_cli_ar.ts @@ -0,0 +1,206 @@ + + + + + BackendCommander + + Data usage: %1 / %2 + استخدام البيانات: ٪1 / ٪2 + + + Unlimited + مطلق + + + + QObject + + Aborting: app did not start in time + إحباط: التطبيق لم يبدأ في الوقت المناسب + + + Aborting: IPC communication error + إحباط: خطأ في اتصال IPC + + + Not logged in + لم يتم تسجيل الدخول + + + Firewall already on + جدار الحماية قيد التشغيل بالفعل + + + Firewall already off + جدار الحماية متوقف بالفعل + + + Firewall set to always on and can't be turned off + تم تعيين جدار الحماية على تشغيل دائم ولا يمكن إيقاف تشغيله + + + Already logged in + تسجيل الدخول بالفعل + + + Already logged out + تم تسجيل الخروج بالفعل + + + No update available + لا يوجد تحديث متاح + + + Internet connectivity: %1 + الاتصال بالإنترنت: ٪1 + + + Login state: %1 + حالة تسجيل الدخول: ٪1 + + + Logged out + تسجيل الخروج + + + Logging in + تسجيل الدخول + + + Logged in + تسجيل الدخول + + + Error: %1 + خطأ: ٪1 + + + No internet connectivity + لا يوجد اتصال بالإنترنت + + + No API connectivity + لا يوجد اتصال API + + + Incorrect username, password, or 2FA code + اسم مستخدم أو كلمة مرور أو رمز 2FA غير صحيح + + + SSL error + خطأ SSL + + + Session expired + انتهت صلاحية الجلسة + + + Rate limited + معدل محدود + + + Unknown error + خطأ غير معروف + + + Connect state: %1 + حالة الاتصال: ٪1 + + + Connected: %1 + متصل: ٪1 + + + Connected + متصل + + + [Network interference] + [تداخل الشبكة] + + + Connecting: %1 + الاتصال: ٪1 + + + Connecting + الاتصال + + + Disconnecting + قطع الاتصال + + + Disconnected + قطع اتصال + + + Error: Location does not exist or is disabled + خطأ: الموقع غير موجود أو معطل + + + Error: You are out of data, or your account has been disabled. Upgrade to Pro to continue using Windscribe + خطأ: نفدت بياناتك، أو تم تعطيل حسابك. قم بالترقية إلى Pro لمتابعة استخدام Windscribe + + + Error: Unable to start custom DNS service + خطأ: غير قادر على بدء تشغيل خدمة DNS المخصصة + + + Error: WireGuard adapter setup failed + خطأ: فشل إعداد محول WireGuard + + + Error: Could not retrieve WireGuard configuration + خطأ: تعذر استرداد تكوين WireGuard + + + Unknown state + حالة غير معروفة + + + Protocol: %1:%2 + البروتوكول: ٪1:٪2 + + + Firewall state: %1 + حالة جدار الحماية: ٪1 + + + Always On + دائما في وضع التشغيل + + + On + على + + + Off + قباله + + + Update error: download failed. Please try again or try a different network. + خطأ في التحديث: فشل التنزيل. يرجى المحاولة مرة أخرى أو تجربة شبكة مختلفة. + + + Update error: %1 + خطأ في التحديث: ٪1 + + + Update downloading: %1%% + تنزيل التحديث: ٪1٪٪ + + + Update available: %1 + التحديث المتوفر: ٪1 + + + available + متوفر + + + unavailable + غير متوفر + + + diff --git a/gui/cli/translations/windscribe_cli_cs.ts b/gui/cli/translations/windscribe_cli_cs.ts new file mode 100644 index 000000000..728afa41f --- /dev/null +++ b/gui/cli/translations/windscribe_cli_cs.ts @@ -0,0 +1,206 @@ + + + + + BackendCommander + + Data usage: %1 / %2 + Využití dat: %1 / %2 + + + Unlimited + Neomezený + + + + QObject + + Aborting: app did not start in time + Přehrávání: aplikace se nespustila včas + + + Aborting: IPC communication error + Přerušení: Chyba komunikace IPC + + + Not logged in + Nepřihlášen + + + Firewall already on + Brána firewall je již zapnuta + + + Firewall already off + Brána firewall je již vypnutá + + + Firewall set to always on and can't be turned off + Brána firewall je nastavena tak, aby byla vždy zapnutá a nedá se vypnout + + + Already logged in + Již přihlášen + + + Already logged out + Již odhlášen + + + No update available + Není k dispozici žádná aktualizace + + + Internet connectivity: %1 + Připojení k internetu: %1 + + + Login state: %1 + Stav přihlášení: %1 + + + Logged out + Odhlášen + + + Logging in + Přihlašování + + + Logged in + Přihlášen + + + Error: %1 + Chyba: %1 + + + No internet connectivity + Žádné připojení k internetu + + + No API connectivity + Žádné připojení k rozhraní API + + + Incorrect username, password, or 2FA code + Nesprávné uživatelské jméno, heslo nebo kód 2FA + + + SSL error + Chyba SSL + + + Session expired + Platnost relace vypršela + + + Rate limited + Sazba omezena + + + Unknown error + Neznámá chyba + + + Connect state: %1 + Stav připojení: %1 + + + Connected: %1 + Připojeno: %1 + + + Connected + Připojený + + + [Network interference] + [Síťové rušení] + + + Connecting: %1 + Připojení: %1 + + + Connecting + Připojování + + + Disconnecting + Odpojující + + + Disconnected + Nesouvislý + + + Error: Location does not exist or is disabled + Chyba: Umístění neexistuje nebo je zakázáno + + + Error: You are out of data, or your account has been disabled. Upgrade to Pro to continue using Windscribe + Chyba: Došla vám data nebo byl váš účet deaktivován. Upgradujte na verzi Pro, abyste mohli Windscribe dál používat + + + Error: Unable to start custom DNS service + Chyba: Nelze spustit vlastní službu DNS + + + Error: WireGuard adapter setup failed + Chyba: Nastavení adaptéru WireGuard se nezdařilo. + + + Error: Could not retrieve WireGuard configuration + Chyba: Nelze načíst konfiguraci WireGuard. + + + Unknown state + Neznámý stav + + + Protocol: %1:%2 + Protokol: %1:%2 + + + Firewall state: %1 + Stav brány firewall: %1 + + + Always On + Vždy zapnuto + + + On + Na + + + Off + Pryč + + + Update error: download failed. Please try again or try a different network. + Chyba aktualizace: stahování se nezdařilo. Zkuste to prosím znovu nebo zkuste jinou síť. + + + Update error: %1 + Chyba aktualizace: %1 + + + Update downloading: %1%% + Stahování aktualizací: %1%% + + + Update available: %1 + Dostupná aktualizace: %1 + + + available + k dispozici + + + unavailable + nedostupný + + + diff --git a/gui/cli/translations/windscribe_cli_de.ts b/gui/cli/translations/windscribe_cli_de.ts new file mode 100644 index 000000000..f7e659263 --- /dev/null +++ b/gui/cli/translations/windscribe_cli_de.ts @@ -0,0 +1,206 @@ + + + + + BackendCommander + + Data usage: %1 / %2 + Datennutzung: %1 / %2 + + + Unlimited + Unbegrenzt + + + + QObject + + Aborting: app did not start in time + Abbruch: App startete nicht rechtzeitig + + + Aborting: IPC communication error + Abbruch: IPC-Kommunikationsfehler + + + Not logged in + Nicht eingeloggt + + + Firewall already on + Firewall bereits aktiviert + + + Firewall already off + Firewall bereits deaktiviert + + + Firewall set to always on and can't be turned off + Firewall immer aktiviert und kann nicht deaktiviert werden + + + Already logged in + Bereits eingeloggt + + + Already logged out + Bereits ausgeloggt + + + No update available + Kein Update verfügbar + + + Internet connectivity: %1 + Internetverbindung: %1 + + + Login state: %1 + Anmeldestatus: %1 + + + Logged out + Abgemeldet + + + Logging in + Einloggen + + + Logged in + Eingeloggt + + + Error: %1 + Fehler: %1 + + + No internet connectivity + Keine Internetverbindung + + + No API connectivity + Keine API-Konnektivität + + + Incorrect username, password, or 2FA code + Falscher Benutzername, falsches Passwort oder falscher 2FA-Code + + + SSL error + SSL-Fehler + + + Session expired + Sitzung abgelaufen + + + Rate limited + Preis begrenzt + + + Unknown error + Unbekannter Fehler + + + Connect state: %1 + Verbindungsstatus: %1 + + + Connected: %1 + Verbunden: %1 + + + Connected + Verbunden + + + [Network interference] + [Netzwerk-Interferenz] + + + Connecting: %1 + Verbinden: %1 + + + Connecting + Verbindend + + + Disconnecting + Trennend + + + Disconnected + Entfernt + + + Error: Location does not exist or is disabled + Fehler: Standort ist nicht vorhanden oder deaktiviert + + + Error: You are out of data, or your account has been disabled. Upgrade to Pro to continue using Windscribe + Fehler: Sie haben keine Daten mehr oder Ihr Konto wurde deaktiviert. Upgrade auf Pro, um Windscribe weiterhin zu verwenden + + + Error: Unable to start custom DNS service + Fehler: Benutzerdefinierter DNS-Dienst kann nicht gestartet werden + + + Error: WireGuard adapter setup failed + Fehler: Fehler bei der Einrichtung des WireGuard-Adapters + + + Error: Could not retrieve WireGuard configuration + Fehler: WireGuard-Konfiguration konnte nicht abgerufen werden + + + Unknown state + Unbekannter Zustand + + + Protocol: %1:%2 + Protokoll: %1:%2 + + + Firewall state: %1 + Firewall-Status: %1 + + + Always On + Immer eingeschaltet + + + On + Auf + + + Off + Aus + + + Update error: download failed. Please try again or try a different network. + Aktualisierungsfehler: Download fehlgeschlagen. Bitte versuchen Sie es erneut oder versuchen Sie es mit einem anderen Netzwerk. + + + Update error: %1 + Aktualisierungsfehler: %1 + + + Update downloading: %1%% + Update-Downloads: %1%% + + + Update available: %1 + Update verfügbar: %1 + + + available + verfügbar + + + unavailable + nicht verfügbar + + + diff --git a/gui/cli/translations/windscribe_cli_en.ts b/gui/cli/translations/windscribe_cli_en.ts new file mode 100644 index 000000000..ffcb0da1c --- /dev/null +++ b/gui/cli/translations/windscribe_cli_en.ts @@ -0,0 +1,206 @@ + + + + + BackendCommander + + Data usage: %1 / %2 + Data usage: %1 / %2 + + + Unlimited + Unlimited + + + + QObject + + Aborting: app did not start in time + Aborting: app did not start in time + + + Aborting: IPC communication error + Aborting: IPC communication error + + + Not logged in + Not logged in + + + Firewall already on + Firewall already on + + + Firewall already off + Firewall already off + + + Firewall set to always on and can't be turned off + Firewall set to always on and can't be turned off + + + Already logged in + Already logged in + + + Already logged out + Already logged out + + + No update available + No update available + + + Internet connectivity: %1 + Internet connectivity: %1 + + + Login state: %1 + Login state: %1 + + + Logged out + Logged out + + + Logging in + Logging in + + + Logged in + Logged in + + + Error: %1 + Error: %1 + + + No internet connectivity + No internet connectivity + + + No API connectivity + No API connectivity + + + Incorrect username, password, or 2FA code + Incorrect username, password, or 2FA code + + + SSL error + SSL error + + + Session expired + Session expired + + + Rate limited + Rate limited + + + Unknown error + Unknown error + + + Connect state: %1 + Connect state: %1 + + + Connected: %1 + Connected: %1 + + + Connected + Connected + + + [Network interference] + [Network interference] + + + Connecting: %1 + Connecting: %1 + + + Connecting + Connecting + + + Disconnecting + Disconnecting + + + Disconnected + Disconnected + + + Error: Location does not exist or is disabled + Error: Location does not exist or is disabled + + + Error: You are out of data, or your account has been disabled. Upgrade to Pro to continue using Windscribe + Error: You are out of data, or your account has been disabled. Upgrade to Pro to continue using Windscribe + + + Error: Unable to start custom DNS service + Error: Unable to start custom DNS service + + + Error: WireGuard adapter setup failed + Error: WireGuard adapter setup failed + + + Error: Could not retrieve WireGuard configuration + Error: Could not retrieve WireGuard configuration + + + Unknown state + Unknown state + + + Protocol: %1:%2 + Protocol: %1:%2 + + + Firewall state: %1 + Firewall state: %1 + + + Always On + Always On + + + On + On + + + Off + Off + + + Update error: download failed. Please try again or try a different network. + Update error: download failed. Please try again or try a different network. + + + Update error: %1 + Update error: %1 + + + Update downloading: %1%% + Update downloading: %1%% + + + Update available: %1 + Update available: %1 + + + available + available + + + unavailable + unavailable + + + diff --git a/gui/cli/translations/windscribe_cli_es.ts b/gui/cli/translations/windscribe_cli_es.ts new file mode 100644 index 000000000..2440dd995 --- /dev/null +++ b/gui/cli/translations/windscribe_cli_es.ts @@ -0,0 +1,206 @@ + + + + + BackendCommander + + Data usage: %1 / %2 + Uso de datos: %1 / %2 + + + Unlimited + Ilimitado + + + + QObject + + Aborting: app did not start in time + Abortar: la aplicación no se inició a tiempo + + + Aborting: IPC communication error + Anulación: Error de comunicación IPC + + + Not logged in + No ha iniciado sesión + + + Firewall already on + El cortafuegos ya está activado + + + Firewall already off + El cortafuegos ya está desactivado + + + Firewall set to always on and can't be turned off + El cortafuegos está siempre activado y no se puede desactivar + + + Already logged in + Ya has iniciado sesión + + + Already logged out + Ya se ha cerrado la sesión + + + No update available + No hay actualización disponible + + + Internet connectivity: %1 + Conectividad a Internet: %1 + + + Login state: %1 + Estado de inicio de sesión: %1 + + + Logged out + Cierre de sesión + + + Logging in + Iniciar sesión + + + Logged in + Conectado + + + Error: %1 + Error: %1 + + + No internet connectivity + Sin conexión a Internet + + + No API connectivity + Sin conectividad API + + + Incorrect username, password, or 2FA code + Nombre de usuario, contraseña o código 2FA incorrectos + + + SSL error + Error SSL + + + Session expired + Sesión caducada + + + Rate limited + Tarifa limitada + + + Unknown error + Error desconocido + + + Connect state: %1 + Estado de conexión: %1 + + + Connected: %1 + Conectado: %1 + + + Connected + Conexo + + + [Network interference] + [Interferencia de red] + + + Connecting: %1 + Conectando: %1 + + + Connecting + Conectivo + + + Disconnecting + Desconectar + + + Disconnected + Desconectado + + + Error: Location does not exist or is disabled + Error: La ubicación no existe o está deshabilitada + + + Error: You are out of data, or your account has been disabled. Upgrade to Pro to continue using Windscribe + Error: Te has quedado sin datos o tu cuenta se ha desactivado. Actualiza a Pro para seguir usando Windscribe + + + Error: Unable to start custom DNS service + Error: No se puede iniciar el servicio DNS personalizado + + + Error: WireGuard adapter setup failed + Error: Error en la configuración del adaptador WireGuard + + + Error: Could not retrieve WireGuard configuration + Error: No se pudo recuperar la configuración de WireGuard + + + Unknown state + Estado desconocido + + + Protocol: %1:%2 + Protocolo: %1:%2 + + + Firewall state: %1 + Estado del cortafuegos: %1 + + + Always On + Siempre activo + + + On + En + + + Off + Apagado + + + Update error: download failed. Please try again or try a different network. + Error de actualización: error de descarga. Inténtelo de nuevo o pruebe con una red diferente. + + + Update error: %1 + Error de actualización: %1 + + + Update downloading: %1%% + Descarga de actualizaciones: %1%% + + + Update available: %1 + Actualización disponible: %1 + + + available + disponible + + + unavailable + indisponible + + + diff --git a/gui/cli/translations/windscribe_cli_fa.ts b/gui/cli/translations/windscribe_cli_fa.ts new file mode 100644 index 000000000..d7b7d1124 --- /dev/null +++ b/gui/cli/translations/windscribe_cli_fa.ts @@ -0,0 +1,206 @@ + + + + + BackendCommander + + Data usage: %1 / %2 + کاربرد داده:٪ 1 /٪ 2 + + + Unlimited + نامحدود + + + + QObject + + Aborting: app did not start in time + ساقط کردن: برنامه به موقع اغاز نشد + + + Aborting: IPC communication error + لغو: خطای ارتباطی IPC + + + Not logged in + وارد نشده + + + Firewall already on + فایروال در حال حاضر روشن است + + + Firewall already off + فایروال در حال حاضر خاموش است + + + Firewall set to always on and can't be turned off + دیوار اتش همیشه روشن است و نمی توان ان را خاموش کرد + + + Already logged in + قبلا وارد سیستم شده اید + + + Already logged out + قبلا وارد سیستم شده اید + + + No update available + بروزرسانی موجود نیست + + + Internet connectivity: %1 + اتصال اینترنت:٪ 1 + + + Login state: %1 + وضعیت ورود:٪ 1 + + + Logged out + خارج شده + + + Logging in + ورود به سیستم + + + Logged in + ورود به سیستم + + + Error: %1 + خطا:٪ 1 + + + No internet connectivity + بدون اتصال به اینترنت + + + No API connectivity + بدون اتصال API + + + Incorrect username, password, or 2FA code + نام کاربری نادرست، رمز عبور یا کد 2FA + + + SSL error + خطای SSL + + + Session expired + نشست منقضی شد + + + Rate limited + نرخ محدود + + + Unknown error + خطای ناشناخته + + + Connect state: %1 + وضعیت اتصال:٪ 1 + + + Connected: %1 + متصل:٪ 1 + + + Connected + متصل + + + [Network interference] + [تداخل شبکه] + + + Connecting: %1 + اتصال:٪ 1 + + + Connecting + اتصال + + + Disconnecting + قطع ارتباط + + + Disconnected + قطع + + + Error: Location does not exist or is disabled + خطا: مکان وجود ندارد یا غیر فعال است + + + Error: You are out of data, or your account has been disabled. Upgrade to Pro to continue using Windscribe + خطا: شما از داده ها خارج شده اید یا حساب شما غیرفعال شده است. ارتقاء به نرم افزار برای ادامه استفاده از Windscribe + + + Error: Unable to start custom DNS service + خطا: قادر به اغاز خدمت DNS سفارشی نیست + + + Error: WireGuard adapter setup failed + خطا: راه اندازی اداپتور WireGuard شکست خورد + + + Error: Could not retrieve WireGuard configuration + خطا: نتوانست پیکربندی WireGuard را بازیابی کند + + + Unknown state + وضعیت ناشناخته + + + Protocol: %1:%2 + قرارداد:٪ 1:٪ 2 + + + Firewall state: %1 + وضعیت دیواراتش:٪ 1 + + + Always On + همیشه روشن + + + On + در + + + Off + خاموش + + + Update error: download failed. Please try again or try a different network. + خطای بروزرسانی: بارگیری شکست خورد. لطفا دوباره تلاش کنید یا یک شبکه دیگر را امتحان کنید. + + + Update error: %1 + خطای بهروزرسانی:٪ 1 + + + Update downloading: %1%% + بهروزرسانی بارگیری:٪ 1٪ + + + Update available: %1 + بهروزرسانی موجود است:٪ 1 + + + available + دسترس + + + unavailable + دسترس نیست + + + diff --git a/gui/cli/translations/windscribe_cli_fr.ts b/gui/cli/translations/windscribe_cli_fr.ts new file mode 100644 index 000000000..11d13c603 --- /dev/null +++ b/gui/cli/translations/windscribe_cli_fr.ts @@ -0,0 +1,206 @@ + + + + + BackendCommander + + Data usage: %1 / %2 + Utilisation des données : %1 / %2 + + + Unlimited + Illimité + + + + QObject + + Aborting: app did not start in time + Abandon : l’application n’a pas démarré à temps + + + Aborting: IPC communication error + Abandon : erreur de communication IPC + + + Not logged in + Non connecté + + + Firewall already on + Pare-feu déjà activé + + + Firewall already off + Pare-feu déjà désactivé + + + Firewall set to always on and can't be turned off + Pare-feu toujours activé et ne peut pas être désactivé + + + Already logged in + Déjà connecté + + + Already logged out + Déjà déconnecté + + + No update available + Aucune mise à jour disponible + + + Internet connectivity: %1 + Connectivité Internet : %1 + + + Login state: %1 + État de connexion : %1 + + + Logged out + Déconnecté + + + Logging in + Connexion + + + Logged in + Connecté + + + Error: %1 + Erreur : %1 + + + No internet connectivity + Pas de connexion Internet + + + No API connectivity + Pas de connectivité API + + + Incorrect username, password, or 2FA code + Nom d’utilisateur, mot de passe ou code 2FA incorrects + + + SSL error + Erreur SSL + + + Session expired + Session expirée + + + Rate limited + Tarif limité + + + Unknown error + Erreur inconnue + + + Connect state: %1 + État de connexion : %1 + + + Connected: %1 + Connecté : %1 + + + Connected + Relié + + + [Network interference] + [Interférence réseau] + + + Connecting: %1 + Connexion : %1 + + + Connecting + Connectant + + + Disconnecting + Déconnexion + + + Disconnected + Coupé + + + Error: Location does not exist or is disabled + Erreur : L’emplacement n’existe pas ou est désactivé + + + Error: You are out of data, or your account has been disabled. Upgrade to Pro to continue using Windscribe + Erreur : Vous n’avez plus de données ou votre compte a été désactivé. Passez à Pro pour continuer à utiliser Windscribe + + + Error: Unable to start custom DNS service + Erreur : Impossible de démarrer le service DNS personnalisé + + + Error: WireGuard adapter setup failed + Erreur : échec de la configuration de la carte WireGuard + + + Error: Could not retrieve WireGuard configuration + Erreur : Impossible de récupérer la configuration de WireGuard + + + Unknown state + État inconnu + + + Protocol: %1:%2 + Protocole : %1 :%2 + + + Firewall state: %1 + État du pare-feu : %1 + + + Always On + Toujours actif + + + On + Sur + + + Off + De + + + Update error: download failed. Please try again or try a different network. + Erreur de mise à jour : échec du téléchargement. Veuillez réessayer ou essayer un autre réseau. + + + Update error: %1 + Erreur de mise à jour : %1 + + + Update downloading: %1%% + Téléchargement de la mise à jour : %1%% + + + Update available: %1 + Mise à jour disponible : %1 + + + available + disponible + + + unavailable + indisponible + + + diff --git a/gui/cli/translations/windscribe_cli_hi.ts b/gui/cli/translations/windscribe_cli_hi.ts new file mode 100644 index 000000000..107e37427 --- /dev/null +++ b/gui/cli/translations/windscribe_cli_hi.ts @@ -0,0 +1,206 @@ + + + + + BackendCommander + + Data usage: %1 / %2 + डेटा उपयोग: %1 / %2 + + + Unlimited + असीम + + + + QObject + + Aborting: app did not start in time + निरस्तीकरण: ऐप समय पर शुरू नहीं हुआ + + + Aborting: IPC communication error + निरस्तीकरण: आईपीसी संचार त्रुटि + + + Not logged in + लॉग इन नहीं किया है + + + Firewall already on + फ़ायरवॉल पहले से ही चालू है + + + Firewall already off + फ़ायरवॉल पहले से ही बंद है + + + Firewall set to always on and can't be turned off + फ़ायरवॉल हमेशा चालू पर सेट है और बंद नहीं किया जा सकता + + + Already logged in + पहले से लॉग इन है + + + Already logged out + पहले से लॉग आउट + + + No update available + कोई अपडेट उपलब्ध नहीं है + + + Internet connectivity: %1 + इंटरनेट कनेक्टिविटी: %1 + + + Login state: %1 + लॉगिन स्थिति: %1 + + + Logged out + लॉग आउट किया + + + Logging in + लॉग इन करना + + + Logged in + लॉग इन किया + + + Error: %1 + त्रुटि: %1 + + + No internet connectivity + कोई इंटरनेट कनेक्टिविटी नहीं + + + No API connectivity + कोई एपीआई कनेक्टिविटी नहीं + + + Incorrect username, password, or 2FA code + गलत उपयोगकर्ता नाम, पासवर्ड या 2FA कोड + + + SSL error + एसएसएल त्रुटि + + + Session expired + सत्र समाप्त हो गया + + + Rate limited + दर सीमित + + + Unknown error + अज्ञात त्रुटि + + + Connect state: %1 + कनेक्ट स्थिति: %1 + + + Connected: %1 + कनेक्ट किया गया: %1 + + + Connected + जुड़ा + + + [Network interference] + [नेटवर्क हस्तक्षेप] + + + Connecting: %1 + कनेक्ट कर रहा है: %1 + + + Connecting + जोड़ने + + + Disconnecting + रखती + + + Disconnected + डिस्कनेक्ट + + + Error: Location does not exist or is disabled + त्रुटि: स्थान मौजूद नहीं है या अक्षम है + + + Error: You are out of data, or your account has been disabled. Upgrade to Pro to continue using Windscribe + त्रुटि: आपका डेटा समाप्त हो गया है या आपका खाता अक्षम कर दिया गया है. Windscribe का उपयोग जारी रखने के लिए प्रो में अपग्रेड करें + + + Error: Unable to start custom DNS service + त्रुटि: कस्टम DNS सेवा प्रारंभ करने में असमर्थ + + + Error: WireGuard adapter setup failed + त्रुटि: WireGuard एडाप्टर सेटअप विफल हुआ + + + Error: Could not retrieve WireGuard configuration + त्रुटि: WireGuard कॉन्फ़िगरेशन पुनर्प्राप्त नहीं कर सका + + + Unknown state + अज्ञात अवस्था + + + Protocol: %1:%2 + प्रोटोकॉल: %1:%2 + + + Firewall state: %1 + फ़ायरवॉल स्थिति: %1 + + + Always On + हमेशा चालू + + + On + पर + + + Off + बंद + + + Update error: download failed. Please try again or try a different network. + अद्यतन त्रुटि: डाउनलोड विफल रहा। कृपया पुन: प्रयास करें या किसी भिन्न नेटवर्क का प्रयास करें. + + + Update error: %1 + अद्यतन त्रुटि: %1 + + + Update downloading: %1%% + अद्यतन डाउनलोडिंग: %1%% + + + Update available: %1 + अद्यतन उपलब्ध है: %1 + + + available + सुलभ + + + unavailable + अनुपलब्ध + + + diff --git a/gui/cli/translations/windscribe_cli_id.ts b/gui/cli/translations/windscribe_cli_id.ts new file mode 100644 index 000000000..d22bc8532 --- /dev/null +++ b/gui/cli/translations/windscribe_cli_id.ts @@ -0,0 +1,206 @@ + + + + + BackendCommander + + Data usage: %1 / %2 + Penggunaan data: %1 / %2 + + + Unlimited + Unlimited + + + + QObject + + Aborting: app did not start in time + Dibatalkan: aplikasi tidak dimulai tepat waktu + + + Aborting: IPC communication error + Membatalkan: Kesalahan komunikasi IPC + + + Not logged in + Tidak masuk log + + + Firewall already on + Firewall sudah aktif + + + Firewall already off + Firewall sudah mati + + + Firewall set to always on and can't be turned off + Firewall diatur ke selalu aktif dan tidak dapat dimatikan + + + Already logged in + Sudah masuk + + + Already logged out + Sudah logout + + + No update available + Pembaruan tidak tersedia + + + Internet connectivity: %1 + Konektivitas internet: %1 + + + Login state: %1 + Status login: %1 + + + Logged out + Keluar log + + + Logging in + Masuk log + + + Logged in + Masuk log + + + Error: %1 + Kesalahan: %1 + + + No internet connectivity + Tidak ada konektivitas internet + + + No API connectivity + Tidak ada konektivitas API + + + Incorrect username, password, or 2FA code + Nama pengguna, kata sandi, atau kode 2FA salah + + + SSL error + Kesalahan SSL + + + Session expired + Sesi berakhir + + + Rate limited + Harga terbatas + + + Unknown error + Kesalahan tidak diketahui + + + Connect state: %1 + Status sambungkan: %1 + + + Connected: %1 + Terhubung: %1 + + + Connected + Terhubung + + + [Network interference] + [Gangguan jaringan] + + + Connecting: %1 + Menghubungkan: %1 + + + Connecting + Menghubungkan + + + Disconnecting + Melepaskan + + + Disconnected + Terputus + + + Error: Location does not exist or is disabled + Kesalahan: Lokasi tidak ada atau dinonaktifkan + + + Error: You are out of data, or your account has been disabled. Upgrade to Pro to continue using Windscribe + Kesalahan: Anda kehabisan data, atau akun Anda telah dinonaktifkan. Tingkatkan ke Pro untuk terus menggunakan Windscribe + + + Error: Unable to start custom DNS service + Kesalahan: Tidak dapat memulai layanan DNS kustom + + + Error: WireGuard adapter setup failed + Galat: Pengaturan adaptor WireGuard gagal + + + Error: Could not retrieve WireGuard configuration + Kesalahan: Tidak dapat mengambil konfigurasi WireGuard + + + Unknown state + Status tidak diketahui + + + Protocol: %1:%2 + Protokol: %1:%2 + + + Firewall state: %1 + Status firewall: %1 + + + Always On + Selalu Aktif + + + On + Di atas + + + Off + Off + + + Update error: download failed. Please try again or try a different network. + Kesalahan pembaruan: unduhan gagal. Coba lagi atau coba jaringan lain. + + + Update error: %1 + Kesalahan pembaruan: %1 + + + Update downloading: %1%% + Perbarui pengunduhan: %1%% + + + Update available: %1 + Pembaruan tersedia: %1 + + + available + tersedia + + + unavailable + tidak tersedia + + + diff --git a/gui/cli/translations/windscribe_cli_it.ts b/gui/cli/translations/windscribe_cli_it.ts new file mode 100644 index 000000000..7e06e47b9 --- /dev/null +++ b/gui/cli/translations/windscribe_cli_it.ts @@ -0,0 +1,206 @@ + + + + + BackendCommander + + Data usage: %1 / %2 + Utilizzo dati: %1 / %2 + + + Unlimited + Illimitato + + + + QObject + + Aborting: app did not start in time + Interruzione dell'app: l'app non si è avviata in tempo + + + Aborting: IPC communication error + Interruzione dell'errore: errore di comunicazione IPC + + + Not logged in + Non effettuato l'accesso + + + Firewall already on + Firewall già attivo + + + Firewall already off + Firewall già disattivato + + + Firewall set to always on and can't be turned off + Il firewall è impostato su sempre attivo e non può essere disattivato + + + Already logged in + Già loggato + + + Already logged out + Già disconnesso + + + No update available + Nessun aggiornamento disponibile + + + Internet connectivity: %1 + Connettività Internet: %1 + + + Login state: %1 + Stato di accesso: %1 + + + Logged out + Disconnesso + + + Logging in + Effettuare l'accesso + + + Logged in + Effettuato l'accesso + + + Error: %1 + Errore: %1 + + + No internet connectivity + Nessuna connessione internet + + + No API connectivity + Nessuna connettività API + + + Incorrect username, password, or 2FA code + Nome utente, password o codice 2FA errati + + + SSL error + Errore SSL + + + Session expired + Sessione scaduta + + + Rate limited + Tariffa limitata + + + Unknown error + Errore sconosciuto + + + Connect state: %1 + Stato di connessione: %1 + + + Connected: %1 + Collegato: %1 + + + Connected + Connesso + + + [Network interference] + [Interferenza di rete] + + + Connecting: %1 + Connessione: %1 + + + Connecting + Connettivo + + + Disconnecting + Scollegare + + + Disconnected + Sconnesso + + + Error: Location does not exist or is disabled + Errore: la posizione non esiste o è disabilitata + + + Error: You are out of data, or your account has been disabled. Upgrade to Pro to continue using Windscribe + Errore: hai esaurito i dati o il tuo account è stato disattivato. Esegui l'upgrade a Pro per continuare a utilizzare Windscribe + + + Error: Unable to start custom DNS service + Errore: Impossibile avviare il servizio DNS personalizzato + + + Error: WireGuard adapter setup failed + Errore: configurazione dell'adattatore WireGuard non riuscita + + + Error: Could not retrieve WireGuard configuration + Errore: Impossibile recuperare la configurazione di WireGuard + + + Unknown state + Stato sconosciuto + + + Protocol: %1:%2 + Protocollo: %1:%2 + + + Firewall state: %1 + Stato del firewall: %1 + + + Always On + Sempre attivo + + + On + Su + + + Off + Spento + + + Update error: download failed. Please try again or try a different network. + Errore di aggiornamento: download non riuscito. Riprova o prova un'altra rete. + + + Update error: %1 + Errore di aggiornamento: %1 + + + Update downloading: %1%% + Download dell'aggiornamento: %1%% + + + Update available: %1 + Aggiornamento disponibile: %1 + + + available + disponibile + + + unavailable + indisponibile + + + diff --git a/gui/cli/translations/windscribe_cli_ja.ts b/gui/cli/translations/windscribe_cli_ja.ts new file mode 100644 index 000000000..c9d662b71 --- /dev/null +++ b/gui/cli/translations/windscribe_cli_ja.ts @@ -0,0 +1,206 @@ + + + + + BackendCommander + + Data usage: %1 / %2 + データ使用量: %1 / %2 + + + Unlimited + 無制限の + + + + QObject + + Aborting: app did not start in time + 中止:アプリが時間内に起動しませんでした + + + Aborting: IPC communication error + 中止中: IPC 通信エラー + + + Not logged in + ログインしていません + + + Firewall already on + ファイアウォールは既にオンになっています + + + Firewall already off + ファイアウォールは既にオフになっています + + + Firewall set to always on and can't be turned off + ファイアウォールが常時オンに設定されており、オフにできない + + + Already logged in + すでにログインしています + + + Already logged out + すでにログアウトしています + + + No update available + 利用可能なアップデートはありません + + + Internet connectivity: %1 + インターネット接続: %1 + + + Login state: %1 + ログイン状態: %1 + + + Logged out + ログアウト済み + + + Logging in + ログイン + + + Logged in + ログイン済み + + + Error: %1 + エラー: %1 + + + No internet connectivity + インターネット接続なし + + + No API connectivity + API 接続なし + + + Incorrect username, password, or 2FA code + ユーザー名、パスワード、または2FAコードが正しくない + + + SSL error + SSL エラー + + + Session expired + セッションの有効期限が切れました + + + Rate limited + 料金制限あり + + + Unknown error + 不明なエラー + + + Connect state: %1 + 接続状態: %1 + + + Connected: %1 + 接続済み: %1 + + + Connected + 接続 + + + [Network interference] + [ネットワーク干渉] + + + Connecting: %1 + 接続中: %1 + + + Connecting + 接続 + + + Disconnecting + 切断 + + + Disconnected + 途切れ途切れ + + + Error: Location does not exist or is disabled + エラー: 場所が存在しないか、無効になっています + + + Error: You are out of data, or your account has been disabled. Upgrade to Pro to continue using Windscribe + エラー: データが不足しているか、アカウントが無効になっています。ProにアップグレードしてWindscribeを使い続ける + + + Error: Unable to start custom DNS service + エラー:カスタムDNSサービスを開始できません + + + Error: WireGuard adapter setup failed + エラー:WireGuardアダプターのセットアップに失敗しました + + + Error: Could not retrieve WireGuard configuration + エラー:WireGuard構成を取得できませんでした + + + Unknown state + 不明な状態 + + + Protocol: %1:%2 + プロトコル: %1:%2 + + + Firewall state: %1 + ファイアウォールの状態: %1 + + + Always On + 常時接続 + + + On + オン + + + Off + オフ + + + Update error: download failed. Please try again or try a different network. + 更新エラー: ダウンロードに失敗しました。もう一度やり直すか、別のネットワークを試してください。 + + + Update error: %1 + 更新エラー: %1 + + + Update downloading: %1%% + 更新プログラムのダウンロード: %1%% + + + Update available: %1 + 利用可能な更新プログラム: %1 + + + available + 利用できる + + + unavailable + 使用禁止 + + + diff --git a/gui/cli/translations/windscribe_cli_ko.ts b/gui/cli/translations/windscribe_cli_ko.ts new file mode 100644 index 000000000..965eccde4 --- /dev/null +++ b/gui/cli/translations/windscribe_cli_ko.ts @@ -0,0 +1,206 @@ + + + + + BackendCommander + + Data usage: %1 / %2 + 데이터 사용량: %1 / %2 + + + Unlimited + 무제한 + + + + QObject + + Aborting: app did not start in time + 중단 중 : 앱이 제 시간에 시작되지 않았습니다. + + + Aborting: IPC communication error + 중단 중: IPC 통신 오류 + + + Not logged in + 로그인하지 않음 + + + Firewall already on + 방화벽이 이미 켜져 있습니다. + + + Firewall already off + 방화벽이 이미 꺼져 있습니다. + + + Firewall set to always on and can't be turned off + 방화벽이 항상 켜짐으로 설정되어 있으며 끌 수 없습니다. + + + Already logged in + 이미 로그인했습니다. + + + Already logged out + 이미 로그아웃했습니다. + + + No update available + 사용 가능한 업데이트가 없습니다. + + + Internet connectivity: %1 + 인터넷 연결: %1 + + + Login state: %1 + 로그인 상태: %1 + + + Logged out + 로그아웃됨 + + + Logging in + 로그인 + + + Logged in + 로그인됨 + + + Error: %1 + 오류: %1 + + + No internet connectivity + 인터넷에 연결되어 있지 않습니다. + + + No API connectivity + API 연결 없음 + + + Incorrect username, password, or 2FA code + 잘못된 사용자 이름, 비밀번호 또는 2FA 코드 + + + SSL error + SSL 오류 + + + Session expired + 세션이 만료되었습니다. + + + Rate limited + 속도 제한 + + + Unknown error + 알 수 없는 오류 + + + Connect state: %1 + 연결 상태: %1 + + + Connected: %1 + 연결됨: %1 + + + Connected + 연결 + + + [Network interference] + [네트워크 간섭] + + + Connecting: %1 + 연결 중: %1 + + + Connecting + 연결 + + + Disconnecting + 분리 + + + Disconnected + 연결이 끊어진 + + + Error: Location does not exist or is disabled + 오류: 위치가 없거나 사용할 수 없습니다. + + + Error: You are out of data, or your account has been disabled. Upgrade to Pro to continue using Windscribe + 오류: 데이터가 부족하거나 계정이 비활성화되었습니다. Pro로 업그레이드하여 Windscribe 계속 사용 + + + Error: Unable to start custom DNS service + 오류: 사용자 지정 DNS 서비스를 시작할 수 없습니다. + + + Error: WireGuard adapter setup failed + 오류: WireGuard 어댑터 설정 실패 + + + Error: Could not retrieve WireGuard configuration + 오류: WireGuard 구성을 검색할 수 없습니다. + + + Unknown state + 알 수 없는 상태 + + + Protocol: %1:%2 + 프로토콜: %1:%2 + + + Firewall state: %1 + 방화벽 상태: %1 + + + Always On + 항상 켜기 + + + On + + + + Off + 끄기 + + + Update error: download failed. Please try again or try a different network. + 업데이트 오류: 다운로드에 실패했습니다. 다시 시도하거나 다른 네트워크를 사용해 보십시오. + + + Update error: %1 + 업데이트 오류: %1 + + + Update downloading: %1%% + 업데이트 다운로드 중: %1%% + + + Update available: %1 + 사용 가능한 업데이트: %1 + + + available + 이용할 수 있는 + + + unavailable + 사용할 + + + diff --git a/gui/cli/translations/windscribe_cli_pl.ts b/gui/cli/translations/windscribe_cli_pl.ts new file mode 100644 index 000000000..28ea01e69 --- /dev/null +++ b/gui/cli/translations/windscribe_cli_pl.ts @@ -0,0 +1,206 @@ + + + + + BackendCommander + + Data usage: %1 / %2 + Wykorzystanie danych: %1 / %2 + + + Unlimited + Nieograniczony + + + + QObject + + Aborting: app did not start in time + Przerwanie: aplikacja nie uruchomiła się na czas + + + Aborting: IPC communication error + Przerwanie: błąd komunikacji IPC + + + Not logged in + Nie zalogowany + + + Firewall already on + Zapora sieciowa jest już włączona + + + Firewall already off + Zapora sieciowa jest już wyłączona + + + Firewall set to always on and can't be turned off + Zapora sieciowa jest ustawiona na zawsze włączoną i nie można jej wyłączyć + + + Already logged in + Już zalogowany + + + Already logged out + Już wylogowany + + + No update available + Brak dostępnej aktualizacji + + + Internet connectivity: %1 + Łączność z Internetem: %1 + + + Login state: %1 + Stan logowania: %1 + + + Logged out + Wylogowany + + + Logging in + Logowanie + + + Logged in + Zalogowany + + + Error: %1 + Błąd: %1 + + + No internet connectivity + Brak połączenia z Internetem + + + No API connectivity + Brak łączności z interfejsem API + + + Incorrect username, password, or 2FA code + Nieprawidłowa nazwa użytkownika, hasło lub kod 2FA + + + SSL error + Błąd SSL + + + Session expired + Sesja wygasła + + + Rate limited + Stawka ograniczona + + + Unknown error + Nieznany błąd + + + Connect state: %1 + Stan połączenia: %1 + + + Connected: %1 + Połączono: %1 + + + Connected + Spójny + + + [Network interference] + [Zakłócenia sieci] + + + Connecting: %1 + Łączenie: %1 + + + Connecting + Podłączanie + + + Disconnecting + Odłączanie + + + Disconnected + Bezładny + + + Error: Location does not exist or is disabled + Błąd: Lokalizacja nie istnieje lub jest wyłączona + + + Error: You are out of data, or your account has been disabled. Upgrade to Pro to continue using Windscribe + Błąd: Brak danych lub Twoje konto zostało wyłączone. Uaktualnij do wersji Pro, aby nadal korzystać z Windscribe + + + Error: Unable to start custom DNS service + Błąd: Nie można uruchomić niestandardowej usługi DNS + + + Error: WireGuard adapter setup failed + Błąd: Konfiguracja adaptera WireGuard nie powiodła się + + + Error: Could not retrieve WireGuard configuration + Błąd: Nie można pobrać konfiguracji WireGuard + + + Unknown state + Nieznany stan + + + Protocol: %1:%2 + Protokół: %1:%2 + + + Firewall state: %1 + Stan zapory: %1 + + + Always On + Zawsze włączony + + + On + Na + + + Off + Od + + + Update error: download failed. Please try again or try a different network. + Błąd aktualizacji: pobieranie nie powiodło się. Spróbuj ponownie lub spróbuj użyć innej sieci. + + + Update error: %1 + Błąd aktualizacji: %1 + + + Update downloading: %1%% + Pobieranie aktualizacji: %1%% + + + Update available: %1 + Dostępna aktualizacja: %1 + + + available + dostępny + + + unavailable + niedostępny + + + diff --git a/gui/cli/translations/windscribe_cli_pt.ts b/gui/cli/translations/windscribe_cli_pt.ts new file mode 100644 index 000000000..2c64706fd --- /dev/null +++ b/gui/cli/translations/windscribe_cli_pt.ts @@ -0,0 +1,206 @@ + + + + + BackendCommander + + Data usage: %1 / %2 + Utilização de dados: %1 / %2 + + + Unlimited + Ilimitado + + + + QObject + + Aborting: app did not start in time + Anulação: a aplicação não foi iniciada a tempo + + + Aborting: IPC communication error + Anulação: erro de comunicação IPC + + + Not logged in + Não conectado + + + Firewall already on + Firewall já ativado + + + Firewall already off + Firewall já desligado + + + Firewall set to always on and can't be turned off + Firewall definido para estar sempre ligado e não pode ser desligado + + + Already logged in + Já iniciou sessão + + + Already logged out + Já desconectado + + + No update available + Nenhuma atualização disponível + + + Internet connectivity: %1 + Conectividade com a Internet: %1 + + + Login state: %1 + Estado de login: %1 + + + Logged out + Desconectado + + + Logging in + Iniciar sessão + + + Logged in + Sessão iniciada + + + Error: %1 + Erro: %1 + + + No internet connectivity + Sem ligação à Internet + + + No API connectivity + Sem conectividade de API + + + Incorrect username, password, or 2FA code + Nome de usuário, senha ou código 2FA incorretos + + + SSL error + Erro SSL + + + Session expired + Sessão expirada + + + Rate limited + Tarifa limitada + + + Unknown error + Erro desconhecido + + + Connect state: %1 + Estado de conexão: %1 + + + Connected: %1 + Ligado: %1 + + + Connected + Ligado + + + [Network interference] + [Interferência na rede] + + + Connecting: %1 + Conexão: %1 + + + Connecting + Ligação + + + Disconnecting + Desconexão + + + Disconnected + Desconectado + + + Error: Location does not exist or is disabled + Erro: A localização não existe ou está desativada + + + Error: You are out of data, or your account has been disabled. Upgrade to Pro to continue using Windscribe + Erro: Você está sem dados ou sua conta foi desativada. Atualize para o Pro para continuar a usar o Windscribe + + + Error: Unable to start custom DNS service + Erro: Não é possível iniciar o serviço DNS personalizado + + + Error: WireGuard adapter setup failed + Erro: Falha na configuração do adaptador WireGuard + + + Error: Could not retrieve WireGuard configuration + Erro: Não foi possível recuperar a configuração do WireGuard + + + Unknown state + Estado desconhecido + + + Protocol: %1:%2 + Protocolo: %1:%2 + + + Firewall state: %1 + Estado do firewall: %1 + + + Always On + Sempre ligado + + + On + Em + + + Off + Desligado + + + Update error: download failed. Please try again or try a different network. + Erro de atualização: falha no download. Tente novamente ou tente uma rede diferente. + + + Update error: %1 + Erro de atualização: %1 + + + Update downloading: %1%% + Download da atualização: %1%% + + + Update available: %1 + Atualização disponível: %1 + + + available + disponível + + + unavailable + indisponível + + + diff --git a/gui/cli/translations/windscribe_cli_ru.ts b/gui/cli/translations/windscribe_cli_ru.ts new file mode 100644 index 000000000..c492f6b39 --- /dev/null +++ b/gui/cli/translations/windscribe_cli_ru.ts @@ -0,0 +1,206 @@ + + + + + BackendCommander + + Data usage: %1 / %2 + Использование данных: %1 / %2 + + + Unlimited + Неограниченный + + + + QObject + + Aborting: app did not start in time + Прерывание: приложение не запустилось вовремя + + + Aborting: IPC communication error + Прерывание: ошибка связи IPC + + + Not logged in + Не вошел в систему + + + Firewall already on + Брандмауэр уже включен + + + Firewall already off + Брандмауэр уже выключен + + + Firewall set to always on and can't be turned off + Брандмауэр всегда включен и не может быть отключен + + + Already logged in + Уже вошли в систему + + + Already logged out + Вы уже вышли из системы + + + No update available + Обновление недоступно + + + Internet connectivity: %1 + Подключение к Интернету: %1 + + + Login state: %1 + Состояние входа: %1 + + + Logged out + Вышел из системы + + + Logging in + Вход в систему + + + Logged in + Авторизовался + + + Error: %1 + Ошибка: %1 + + + No internet connectivity + Нет подключения к Интернету + + + No API connectivity + Нет подключения к API + + + Incorrect username, password, or 2FA code + Неверное имя пользователя, пароль или код 2FA + + + SSL error + Ошибка SSL + + + Session expired + Срок действия сеанса истек + + + Rate limited + Тариф ограничен + + + Unknown error + Неизвестная ошибка + + + Connect state: %1 + Состояние подключения: %1 + + + Connected: %1 + Подключено: %1 + + + Connected + Связанный + + + [Network interference] + [Сетевые помехи] + + + Connecting: %1 + Подключение: %1 + + + Connecting + Соединительный + + + Disconnecting + Отключение + + + Disconnected + Бессвязный + + + Error: Location does not exist or is disabled + Ошибка: Местоположение не существует или отключено + + + Error: You are out of data, or your account has been disabled. Upgrade to Pro to continue using Windscribe + Ошибка: У вас закончились данные или ваша учетная запись была отключена. Перейдите на Pro, чтобы продолжить пользоваться Windscribe + + + Error: Unable to start custom DNS service + Ошибка: Не удается запустить пользовательскую службу DNS + + + Error: WireGuard adapter setup failed + Ошибка: Не удалось настроить адаптер WireGuard + + + Error: Could not retrieve WireGuard configuration + Ошибка: не удалось получить конфигурацию WireGuard + + + Unknown state + Неизвестное состояние + + + Protocol: %1:%2 + Протокол: %1:%2 + + + Firewall state: %1 + Состояние брандмауэра: %1 + + + Always On + Всегда на связи + + + On + На + + + Off + От + + + Update error: download failed. Please try again or try a different network. + Ошибка обновления: загрузка не удалась. Повторите попытку или попробуйте использовать другую сеть. + + + Update error: %1 + Ошибка обновления: %1 + + + Update downloading: %1%% + Загрузка обновлений: %1%% + + + Update available: %1 + Доступно обновление: %1 + + + available + доступный + + + unavailable + недоступный + + + diff --git a/gui/cli/translations/windscribe_cli_tr.ts b/gui/cli/translations/windscribe_cli_tr.ts new file mode 100644 index 000000000..7892822c8 --- /dev/null +++ b/gui/cli/translations/windscribe_cli_tr.ts @@ -0,0 +1,206 @@ + + + + + BackendCommander + + Data usage: %1 / %2 + Veri kullanımı: %1 / %2 + + + Unlimited + Sınırsız + + + + QObject + + Aborting: app did not start in time + İptal ediliyor: uygulama zamanında başlamadı + + + Aborting: IPC communication error + İptal: IPC iletişim hatası + + + Not logged in + Giriş yapılmadı + + + Firewall already on + Güvenlik duvarı zaten açık + + + Firewall already off + Güvenlik duvarı zaten kapalı + + + Firewall set to always on and can't be turned off + Güvenlik duvarı her zaman açık olarak ayarlandı ve kapatılamıyor + + + Already logged in + Zaten giriş yapıldı + + + Already logged out + Zaten oturumu kapattım + + + No update available + Güncelleme yok + + + Internet connectivity: %1 + İnternet bağlantısı: %1 + + + Login state: %1 + Oturum açma durumu: %1 + + + Logged out + Oturumu kapattı + + + Logging in + Giriş Yapma + + + Logged in + Giriş yapıldı + + + Error: %1 + Hata: %1 + + + No internet connectivity + İnternet bağlantısı yok + + + No API connectivity + API bağlantısı yok + + + Incorrect username, password, or 2FA code + Yanlış kullanıcı adı, şifre veya 2FA kodu + + + SSL error + SSL hatası + + + Session expired + Oturum süresi doldu + + + Rate limited + Oran sınırlı + + + Unknown error + Bilinmeyen hata + + + Connect state: %1 + Bağlantı durumu: %1 + + + Connected: %1 + Bağlı: %1 + + + Connected + Bağlandı + + + [Network interference] + [Ağ paraziti] + + + Connecting: %1 + Bağlanıyor: %1 + + + Connecting + Bağlanma + + + Disconnecting + Bağlantıyı kesme + + + Disconnected + Bağlantısı kesilmiş + + + Error: Location does not exist or is disabled + Hata: Konum yok veya devre dışı bırakıldı + + + Error: You are out of data, or your account has been disabled. Upgrade to Pro to continue using Windscribe + Hata: Verileriniz kalmadı veya hesabınız devre dışı bırakıldı. Windscribe'ı kullanmaya devam etmek için Pro'ya yükseltin + + + Error: Unable to start custom DNS service + Hata: Özel DNS hizmeti başlatılamıyor + + + Error: WireGuard adapter setup failed + Hata: WireGuard bağdaştırıcısı kurulumu başarısız oldu + + + Error: Could not retrieve WireGuard configuration + Hata: WireGuard yapılandırması alınamadı + + + Unknown state + Bilinmeyen durum + + + Protocol: %1:%2 + Protokol: %1:%2 + + + Firewall state: %1 + Güvenlik duvarı durumu: %1 + + + Always On + Her Zaman Açık + + + On + Üzerinde + + + Off + Kapalı + + + Update error: download failed. Please try again or try a different network. + Güncelleme hatası: indirme başarısız oldu. Lütfen tekrar deneyin veya farklı bir ağ deneyin. + + + Update error: %1 + Güncelleştirme hatası: %1 + + + Update downloading: %1%% + Güncelleştirme indirme: %1%% + + + Update available: %1 + Kullanılabilir güncelleştirme: %1 + + + available + mevcut + + + unavailable + kullanılamaz + + + diff --git a/gui/cli/translations/windscribe_cli_uk.ts b/gui/cli/translations/windscribe_cli_uk.ts new file mode 100644 index 000000000..1e54b42b7 --- /dev/null +++ b/gui/cli/translations/windscribe_cli_uk.ts @@ -0,0 +1,206 @@ + + + + + BackendCommander + + Data usage: %1 / %2 + Використання даних: %1 / %2 + + + Unlimited + Необмежений + + + + QObject + + Aborting: app did not start in time + Переривання: додаток не запустився вчасно + + + Aborting: IPC communication error + Переривання: помилка зв'язку IPC + + + Not logged in + Не ввійшли в систему + + + Firewall already on + Брандмауер уже ввімкнено + + + Firewall already off + Брандмауер уже вимкнено + + + Firewall set to always on and can't be turned off + Брандмауер завжди ввімкнено, і його не можна вимкнути + + + Already logged in + Ви вже ввійшли в систему + + + Already logged out + Ви вже вийшли з системи + + + No update available + Оновлення недоступне + + + Internet connectivity: %1 + Підключення до Інтернету: %1 + + + Login state: %1 + Стан входу: %1 + + + Logged out + Вийшли з облікового запису + + + Logging in + Вхід в систему + + + Logged in + Увійшли в систему + + + Error: %1 + Помилка: %1 + + + No internet connectivity + Відсутнє підключення до Інтернету + + + No API connectivity + Немає підключення до API + + + Incorrect username, password, or 2FA code + Неправильне ім'я користувача, пароль або код 2FA + + + SSL error + Помилка SSL + + + Session expired + Термін дії сеансу минув + + + Rate limited + Ставка обмежена + + + Unknown error + Невідома помилка + + + Connect state: %1 + Стан підключення: %1 + + + Connected: %1 + пов'язаний: %1 + + + Connected + Підключений + + + [Network interference] + [Мережеві перешкоди] + + + Connecting: %1 + Підключення: %1 + + + Connecting + Підключення + + + Disconnecting + Відключення + + + Disconnected + Відключено + + + Error: Location does not exist or is disabled + Помилка: Місцезнаходження не існує або вимкнено + + + Error: You are out of data, or your account has been disabled. Upgrade to Pro to continue using Windscribe + Помилка: у вас закінчилися дані або ваш обліковий запис вимкнено. Перейдіть на Pro, щоб продовжити використання Windscribe + + + Error: Unable to start custom DNS service + Помилка: не вдається запустити спеціальну службу DNS + + + Error: WireGuard adapter setup failed + Помилка: не вдалося налаштувати адаптер WireGuard + + + Error: Could not retrieve WireGuard configuration + Помилка: не вдалося відновити конфігурацію WireGuard + + + Unknown state + Невідомий стан + + + Protocol: %1:%2 + Протокол: %1:%2 + + + Firewall state: %1 + Стан брандмауера: %1 + + + Always On + Завжди ввімкнено + + + On + На + + + Off + Вимкнено + + + Update error: download failed. Please try again or try a different network. + Помилка оновлення: помилка завантаження. Будь ласка, спробуйте ще раз або спробуйте іншу мережу. + + + Update error: %1 + Помилка оновлення: %1 + + + Update downloading: %1%% + Завантаження оновлення: %1%% + + + Update available: %1 + Доступне оновлення: %1 + + + available + Доступні + + + unavailable + недоступний + + + diff --git a/gui/cli/translations/windscribe_cli_vi.ts b/gui/cli/translations/windscribe_cli_vi.ts new file mode 100644 index 000000000..e22ac0732 --- /dev/null +++ b/gui/cli/translations/windscribe_cli_vi.ts @@ -0,0 +1,206 @@ + + + + + BackendCommander + + Data usage: %1 / %2 + Sử dụng dữ liệu: %1 / %2 + + + Unlimited + Vô hạn + + + + QObject + + Aborting: app did not start in time + Hủy bỏ: ứng dụng không khởi động kịp thời + + + Aborting: IPC communication error + Hủy bỏ: Lỗi giao tiếp IPC + + + Not logged in + Chưa đăng nhập + + + Firewall already on + Tường lửa đã bật + + + Firewall already off + Tường lửa đã tắt + + + Firewall set to always on and can't be turned off + Tường lửa được đặt thành luôn bật và không thể tắt + + + Already logged in + Đã đăng nhập + + + Already logged out + Đã đăng xuất + + + No update available + Không có bản cập nhật nào + + + Internet connectivity: %1 + Kết nối Internet: %1 + + + Login state: %1 + Trạng thái đăng nhập: %1 + + + Logged out + Đăng xuất + + + Logging in + Đăng nhập + + + Logged in + Đã đăng nhập + + + Error: %1 + Lỗi: %1 + + + No internet connectivity + Không có kết nối internet + + + No API connectivity + Không có kết nối API + + + Incorrect username, password, or 2FA code + Tên người dùng, mật khẩu hoặc mã 2FA không chính xác + + + SSL error + Lỗi SSL + + + Session expired + Phiên đã hết hạn + + + Rate limited + Giới hạn tỷ lệ + + + Unknown error + Lỗi không xác định + + + Connect state: %1 + Trạng thái kết nối: %1 + + + Connected: %1 + Đã kết nối: %1 + + + Connected + Kết nối + + + [Network interference] + [Nhiễu mạng] + + + Connecting: %1 + Kết nối: %1 + + + Connecting + Kết nối + + + Disconnecting + Ngắt kết nối + + + Disconnected + Rời rạc + + + Error: Location does not exist or is disabled + Lỗi: Vị trí không tồn tại hoặc bị vô hiệu hóa + + + Error: You are out of data, or your account has been disabled. Upgrade to Pro to continue using Windscribe + Lỗi: Bạn hết dữ liệu hoặc tài khoản của bạn đã bị vô hiệu hóa. Nâng cấp lên Pro để tiếp tục sử dụng Windscribe + + + Error: Unable to start custom DNS service + Lỗi: Không thể khởi động dịch vụ DNS tùy chỉnh + + + Error: WireGuard adapter setup failed + Lỗi: Thiết lập bộ điều hợp WireGuard không thành công + + + Error: Could not retrieve WireGuard configuration + Lỗi: Không thể truy xuất cấu hình WireGuard + + + Unknown state + Trạng thái không xác định + + + Protocol: %1:%2 + Giao thức: %1:%2 + + + Firewall state: %1 + Trạng thái tường lửa: %1 + + + Always On + Luôn bật + + + On + Trên + + + Off + Tắt + + + Update error: download failed. Please try again or try a different network. + Lỗi cập nhật: tải xuống không thành công. Vui lòng thử lại hoặc thử một mạng khác. + + + Update error: %1 + Lỗi cập nhật: %1 + + + Update downloading: %1%% + Cập nhật tải xuống: %1%% + + + Update available: %1 + Cập nhật có sẵn: %1 + + + available + Có sẵ + + + unavailable + Không khả dụng + + + diff --git a/gui/cli/translations/windscribe_cli_zh-CN.ts b/gui/cli/translations/windscribe_cli_zh-CN.ts new file mode 100644 index 000000000..2dd14d65a --- /dev/null +++ b/gui/cli/translations/windscribe_cli_zh-CN.ts @@ -0,0 +1,206 @@ + + + + + BackendCommander + + Data usage: %1 / %2 + 数据使用量: %1 / %2 + + + Unlimited + 无限 + + + + QObject + + Aborting: app did not start in time + 中止:应用未及时启动 + + + Aborting: IPC communication error + 中止:IPC通信错误 + + + Not logged in + 未登录 + + + Firewall already on + 防火墙已开启 + + + Firewall already off + 防火墙已关闭 + + + Firewall set to always on and can't be turned off + 防火墙设置为始终开启且无法关闭 + + + Already logged in + 已登录 + + + Already logged out + 已注销 + + + No update available + 没有可用的更新 + + + Internet connectivity: %1 + 因特网连接: %1 + + + Login state: %1 + 登录状态: %1 + + + Logged out + 已注销 + + + Logging in + 登录 + + + Logged in + 已登录 + + + Error: %1 + 错误: %1 + + + No internet connectivity + 没有互联网连接 + + + No API connectivity + 无 API 连接 + + + Incorrect username, password, or 2FA code + 用户名、密码或 2FA 代码不正确 + + + SSL error + SSL 错误 + + + Session expired + 会话已过期 + + + Rate limited + 价格限制 + + + Unknown error + 未知错误 + + + Connect state: %1 + 连接状态: %1 + + + Connected: %1 + 已连接: %1 + + + Connected + 连接 + + + [Network interference] + [网络干扰] + + + Connecting: %1 + 连接: %1 + + + Connecting + 连接 + + + Disconnecting + 断开 + + + Disconnected + 断开 + + + Error: Location does not exist or is disabled + 错误:位置不存在或已禁用 + + + Error: You are out of data, or your account has been disabled. Upgrade to Pro to continue using Windscribe + 错误:您的数据不足,或者您的帐户已被禁用。升级到 Pro 以继续使用 Windscribe + + + Error: Unable to start custom DNS service + 错误:无法启动自定义 DNS 服务 + + + Error: WireGuard adapter setup failed + 错误:WireGuard 适配器设置失败 + + + Error: Could not retrieve WireGuard configuration + 错误:无法检索 WireGuard 配置 + + + Unknown state + 未知状态 + + + Protocol: %1:%2 + 协议: %1:%2 + + + Firewall state: %1 + 防火墙状态: %1 + + + Always On + 始终开启 + + + On + + + + Off + 关闭 + + + Update error: download failed. Please try again or try a different network. + 更新错误:下载失败。请重试或尝试其他网络。 + + + Update error: %1 + 更新错误: %1 + + + Update downloading: %1%% + 更新下载: %1%% + + + Update available: %1 + 可用更新: %1 + + + available + 可用 + + + unavailable + 不能利用的 + + + diff --git a/gui/cli/translations/windscribe_cli_zh-TW.ts b/gui/cli/translations/windscribe_cli_zh-TW.ts new file mode 100644 index 000000000..36ae35cb7 --- /dev/null +++ b/gui/cli/translations/windscribe_cli_zh-TW.ts @@ -0,0 +1,206 @@ + + + + + BackendCommander + + Data usage: %1 / %2 + 數據使用量: %1 / %2 + + + Unlimited + 無限 + + + + QObject + + Aborting: app did not start in time + 中止:應用未及時啟動 + + + Aborting: IPC communication error + 中止:IPC通信錯誤 + + + Not logged in + 未登錄 + + + Firewall already on + 防火牆已開啟 + + + Firewall already off + 防火牆已關閉 + + + Firewall set to always on and can't be turned off + 防火牆設置為始終開啟且無法關閉 + + + Already logged in + 已登錄 + + + Already logged out + 已註銷 + + + No update available + 沒有可用的更新 + + + Internet connectivity: %1 + 因特網連接: %1 + + + Login state: %1 + 登錄狀態: %1 + + + Logged out + 已註銷 + + + Logging in + 登錄 + + + Logged in + 已登錄 + + + Error: %1 + 錯誤: %1 + + + No internet connectivity + 沒有互聯網連接 + + + No API connectivity + 無 API 連接 + + + Incorrect username, password, or 2FA code + 使用者名、密碼或 2FA 代碼不正確 + + + SSL error + SSL 錯誤 + + + Session expired + 會話已過期 + + + Rate limited + 價格限制 + + + Unknown error + 未知錯誤 + + + Connect state: %1 + 連接狀態: %1 + + + Connected: %1 + 已連接: %1 + + + Connected + 連接 + + + [Network interference] + [網络干擾] + + + Connecting: %1 + 連接: %1 + + + Connecting + 連接 + + + Disconnecting + 斷開 + + + Disconnected + 斷開 + + + Error: Location does not exist or is disabled + 錯誤:位置不存在或已禁用 + + + Error: You are out of data, or your account has been disabled. Upgrade to Pro to continue using Windscribe + 錯誤:您的數據不足,或者您的帳戶已被禁用。升級到 Pro 以繼續使用Windscribe + + + Error: Unable to start custom DNS service + 錯誤:無法啟動自定義 DNS 服務 + + + Error: WireGuard adapter setup failed + 錯誤:WireGuard 適配器設置失敗 + + + Error: Could not retrieve WireGuard configuration + 錯誤:無法檢索WireGuard配置 + + + Unknown state + 未知狀態 + + + Protocol: %1:%2 + 協定: %1:%2 + + + Firewall state: %1 + 防火牆狀態: %1 + + + Always On + 始終開啟 + + + On + + + + Off + 關閉 + + + Update error: download failed. Please try again or try a different network. + 更新錯誤:下載失敗。請重試或嘗試其他網路。 + + + Update error: %1 + 更新錯誤: %1 + + + Update downloading: %1%% + 更新下載: %1% + + + Update available: %1 + 可用更新: %1 + + + available + 可用 + + + unavailable + 不能利用的 + + + diff --git a/installer/common/alertwindow.cpp b/installer/common/alertwindow.cpp index ea85456c9..d538480d9 100644 --- a/installer/common/alertwindow.cpp +++ b/installer/common/alertwindow.cpp @@ -7,6 +7,7 @@ #include #include +#include "hoverbutton.h" #include "languagecontroller.h" #include "themecontroller.h" diff --git a/installer/common/alertwindowcontents.cpp b/installer/common/alertwindowcontents.cpp index 22b3288da..24dfa093e 100644 --- a/installer/common/alertwindowcontents.cpp +++ b/installer/common/alertwindowcontents.cpp @@ -7,7 +7,6 @@ #include #include -#include "languagecontroller.h" #include "svgresources.h" #include "themecontroller.h" diff --git a/installer/common/alertwindowcontents.h b/installer/common/alertwindowcontents.h index 0d97e0581..72874c798 100644 --- a/installer/common/alertwindowcontents.h +++ b/installer/common/alertwindowcontents.h @@ -4,8 +4,6 @@ #include #include -#include "hoverbutton.h" - class AlertWindowContents : public QWidget { Q_OBJECT diff --git a/installer/common/installer_shim.h b/installer/common/installer_shim.h index 84ea58795..6704f8a30 100644 --- a/installer/common/installer_shim.h +++ b/installer/common/installer_shim.h @@ -6,7 +6,7 @@ class InstallerShim { public: - enum INSTALLER_STATE { STATE_INIT, STATE_EXTRACTING, STATE_CANCELED, STATE_FINISHED, STATE_ERROR, STATE_LAUNCHED }; + enum INSTALLER_STATE { STATE_INIT, STATE_EXTRACTING, STATE_CANCELED, STATE_FINISHED, STATE_ERROR, STATE_LAUNCHED, STATE_EXTRACTED }; enum INSTALLER_ERROR { ERROR_OTHER = 1, ERROR_PERMISSION, ERROR_KILL, ERROR_CONNECT_HELPER, ERROR_DELETE, ERROR_UNINSTALL, ERROR_MOVE_CUSTOM_DIR }; static InstallerShim &instance() diff --git a/installer/common/mainwindow.cpp b/installer/common/mainwindow.cpp index f470e1e38..94039ac98 100644 --- a/installer/common/mainwindow.cpp +++ b/installer/common/mainwindow.cpp @@ -173,50 +173,51 @@ void MainWindow::onSettingsWindowAnimFinished(bool dimmed) void MainWindow::onInstallerCallback() { switch (installerShim_->state()) { - case InstallerShim::STATE_INIT: - break; - case InstallerShim::STATE_EXTRACTING: - // No UI created when running in silent mode. - if (initialWindow_) { - QMetaObject::invokeMethod(initialWindow_, "setProgress", Qt::QueuedConnection, Q_ARG(int, installerShim_->progress())); - } - break; - case InstallerShim::STATE_CANCELED: - case InstallerShim::STATE_LAUNCHED: + case InstallerShim::STATE_INIT: + break; + case InstallerShim::STATE_EXTRACTING: + case InstallerShim::STATE_EXTRACTED: + // No UI created when running in silent mode. + if (initialWindow_) { + QMetaObject::invokeMethod(initialWindow_, "setProgress", Qt::QueuedConnection, Q_ARG(int, installerShim_->progress())); + } + break; + case InstallerShim::STATE_CANCELED: + case InstallerShim::STATE_LAUNCHED: + qApp->exit(); + break; + case InstallerShim::STATE_FINISHED: + installerShim_->finish(); + break; + case InstallerShim::STATE_ERROR: + QString errorMsg; + InstallerShim::INSTALLER_ERROR error = installerShim_->lastError(); + if (error == InstallerShim::ERROR_PERMISSION) { + errorMsg = tr("The installation was cancelled. Administrator privileges are required to install the application."); + } else if (error == InstallerShim::ERROR_KILL) { + errorMsg = tr("Windscribe is running and could not be closed. Please close the application manually and try again."); + } else if (error == InstallerShim::ERROR_CONNECT_HELPER) { + errorMsg = tr("The installer could not connect to the privileged helper tool. Please try again."); + } else if (error == InstallerShim::ERROR_DELETE) { + errorMsg = tr("An existing installation of Windscribe could not be removed. Please uninstall the application manually and try again."); + } else if (error == InstallerShim::ERROR_UNINSTALL) { + errorMsg = tr("The uninstaller for the existing installation of Windscribe could not be found. Please uninstall the application manually and try again."); + } else if (error == InstallerShim::ERROR_MOVE_CUSTOM_DIR) { + errorMsg = tr("The installation folder contains data which could not be uninstalled. Please uninstall the application manually and try again."); + } else { + errorMsg = tr("The installation could not be completed successfully. Please contact our Technical Support."); + } + // No UI created when running in silent mode. + if (options_.silent) { + // On Windows this will go to the system debugger (e.g. Debug View app). + qDebug() << errorMsg; qApp->exit(); - break; - case InstallerShim::STATE_FINISHED: - installerShim_->finish(); - break; - case InstallerShim::STATE_ERROR: - QString errorMsg; - InstallerShim::INSTALLER_ERROR error = installerShim_->lastError(); - if (error == InstallerShim::ERROR_PERMISSION) { - errorMsg = tr("The installation was cancelled. Administrator privileges are required to install the application."); - } else if (error == InstallerShim::ERROR_KILL) { - errorMsg = tr("Windscribe is running and could not be closed. Please close the application manually and try again."); - } else if (error == InstallerShim::ERROR_CONNECT_HELPER) { - errorMsg = tr("The installer could not connect to the privileged helper tool. Please try again."); - } else if (error == InstallerShim::ERROR_DELETE) { - errorMsg = tr("An existing installation of Windscribe could not be removed. Please uninstall the application manually and try again."); - } else if (error == InstallerShim::ERROR_UNINSTALL) { - errorMsg = tr("The uninstaller for the existing installation of Windscribe could not be found. Please uninstall the application manually and try again."); - } else if (error == InstallerShim::ERROR_MOVE_CUSTOM_DIR) { - errorMsg = tr("The installation folder contains data which could not be uninstalled. Please uninstall the application manually and try again."); - } else { - errorMsg = tr("The installation could not be completed successfully. Please contact our Technical Support."); - } - // No UI created when running in silent mode. - if (options_.silent) { - // On Windows this will go to the system debugger (e.g. Debug View app). - qDebug() << errorMsg; - qApp->exit(); - } - else { - QMetaObject::invokeMethod(this, "showError", Qt::QueuedConnection, Q_ARG(QString, tr("Installation failed")), - Q_ARG(QString, errorMsg), Q_ARG(bool, true)); - } - break; + } + else { + QMetaObject::invokeMethod(this, "showError", Qt::QueuedConnection, Q_ARG(QString, tr("Installation failed")), + Q_ARG(QString, errorMsg), Q_ARG(bool, true)); + } + break; } } diff --git a/installer/common/translations/windscribe_installer_ru.ts b/installer/common/translations/windscribe_installer_ru.ts index bbefaae24..669d8da3e 100644 --- a/installer/common/translations/windscribe_installer_ru.ts +++ b/installer/common/translations/windscribe_installer_ru.ts @@ -5,21 +5,21 @@ AlertWindow ESC - ЗАКР + Регулятор InitialWindow Read EULA - Ознакомиться с лицензионным соглашением + Ознакомиться с конечным пользователем InstallButton Install - Установить + Устанавливать %1% @@ -46,11 +46,11 @@ The specified installation path is not on the system drive. To ensure the security of the application, and your system, it must be installed on the same drive as Windows. The installation folder has been reset to the default. - Указанный путь установки отсутствует на системном диске. Чтобы обеспечить безопасность приложения и вашей системы, оно должно быть установлено на том же диске, что и Windows. Папка установки была сброшена до стандартной. + Указанный путь установки отсутствует на системном диске. Чтобы обеспечить безопасность приложения и вашей системы, оно должно быть установлено на том же диске, что и Windows. Папка установки была сброшена до значений по умолчанию. OK - ОК + ХОРОШО The installation was cancelled. Administrator privileges are required to install the application. @@ -66,7 +66,7 @@ The installer could not connect to the privileged helper tool. Please try again. - Установщику не удалось подключиться к привилегированной вспомогательной службе. Повторите попытку. + Установщику не удалось подключиться к привилегированному вспомогательному средству. Повторите попытку. An existing installation of Windscribe could not be removed. Please uninstall the application manually and try again. @@ -74,7 +74,7 @@ Quit - Выйти + Покидать Cancel @@ -98,7 +98,7 @@ Installation to a custom folder may allow an attacker to tamper with the Windscribe application. To ensure the security of the application, and your system, we strongly recommend you install to the default location in the 'Program Files' folder. Click OK to continue with the custom folder or Cancel to use the default location. - Установка в пользовательскую папку может позволить злоумышленнику модифицировать приложение Windscribe. Чтобы обеспечить безопасность приложения и вашей системы, мы настоятельно рекомендуем вам установить его в папку «Program Files» по умолчанию. Нажмите кнопку ОК, чтобы продолжить работу с пользовательской папкой, или кнопку Отмена, чтобы использовать расположение по умолчанию. + Установка в пользовательскую папку может позволить злоумышленнику взломать приложение Windscribe. Чтобы обеспечить безопасность приложения и вашей системы, мы настоятельно рекомендуем вам установить его в папку «Program Files» по умолчанию. Нажмите кнопку ОК, чтобы продолжить работу с пользовательской папкой, или кнопку Отмена, чтобы использовать расположение по умолчанию. @@ -117,15 +117,15 @@ The Windscribe installer accepts the following optional commmand-line parameters: - Инсталлятор Windscribe принимает следующие необязательные параметры командной строки: + Инсталлятор Windscribe принимает следующие необязательные параметры commmand-line: Show this information. - Показать эту информацию. + Покажите эту информацию. Do not launch the application after installation. - Не запускать приложение после установки. + Не запускайте приложение после установки. Instructs the installer to skip installing drivers. @@ -133,7 +133,7 @@ Delete existing preferences, logs, and other data, if they exist. - Удалить существующие настройки, журналы и другие данные, если они существуют. + Удалите существующие настройки, журналы и другие данные, если они существуют. Overrides the default installation directory. Installation directory must be on the system drive. @@ -204,15 +204,15 @@ SettingsWindow Install Settings - Настройки установщика + Настройки установки OK - ОК + ХОРОШО Factory Reset - Сброс к стандартным настройкам + Сброс к заводским настройкам Create shortcut diff --git a/installer/linux/cli/arch_package/PKGBUILD b/installer/linux/cli/arch_package/PKGBUILD new file mode 100644 index 000000000..abd277451 --- /dev/null +++ b/installer/linux/cli/arch_package/PKGBUILD @@ -0,0 +1,28 @@ +# Contributor: Nik Rozman +# Contributor: Topik + +pkgname=windscribe-cli +pkgver=$VERSION_NO_SUFFIX +pkgrel=1 +pkgdesc="Windscribe CLI Client" +arch=('x86_64') +url="https://windscribe.com/download" +license=('GPL2') +depends=('nftables' 'c-ares' 'systemd' 'glibc>=2.28' 'glib2' 'zlib' 'gcc-libs' 'dbus' 'sudo' 'shadow' 'networkmanager' 'procps-ng' 'polkit' 'iproute2' 'iputils') +conflicts=('windscribe') +provides=('windscribe-cli') +options=('!strip' '!emptydirs' '!debug') +install=${pkgname}.install +source=($APP_DOWNLOAD_URL) +sha512sums=('SKIP') + +package(){ + # Extract package data + tar xf data.tar.xz -C "${pkgdir}" + + # Correct permissions + chmod -R 755 "${pkgdir}" + + # Install license + install -D -m644 "${pkgdir}/opt/windscribe/open_source_licenses.txt" "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE" +} diff --git a/installer/linux/cli/arch_package/windscribe-cli.install b/installer/linux/cli/arch_package/windscribe-cli.install new file mode 100644 index 000000000..5d1ced549 --- /dev/null +++ b/installer/linux/cli/arch_package/windscribe-cli.install @@ -0,0 +1,63 @@ +pre_install() { + end=$((SECONDS+10)) + + while : + do + if [ -z "$(ps -eo args | grep WindscribeEngine | grep -v grep)" ]; then + break + fi + + if [ $SECONDS -gt $end ]; then + echo 'Error during Windscribe installation. WindscribeEngine is running. Please re-launch Windscribe. And try to update again.' + exit 1 + fi + done + + systemctl stop windscribe-helper > /dev/null 2>&1 || true + systemctl disable windscribe-helper > /dev/null 2>&1 || true + + # Stop and disable firewalld service if it is active, otherwise it will conflict with iptables on OS reboot + # Relevant for Fedora distributions + systemctl is-active --quiet firewalld + if [ $? -eq 0 ] + then + echo "Firewalld service is running. It will be stopped and disabled to avoid conflicts with the Windscribe iptables firewall." + systemctl stop firewalld + systemctl disable firewalld + fi + + echo "Finish pre-install script" +} + +post_install() { + systemctl enable NetworkManager.service + systemctl start NetworkManager.service + systemctl enable systemd-resolved + systemctl start systemd-resolved + systemctl enable windscribe-helper + systemctl start windscribe-helper + ln -s /opt/windscribe/Windscribe /usr/bin/windscribe + update-desktop-database + echo linux_zst_x64_cli > ../etc/windscribe/platform +} + +pre_upgrade() { + set -e + systemctl stop windscribe-helper || true + systemctl disable windscribe-helper || true +} + +post_upgrade() { + post_install +} + +pre_remove() { + killall -q Windscribe || true + systemctl stop windscribe-helper || true + systemctl disable windscribe-helper || true + userdel -f windscribe || true + groupdel -f windscribe || true + rm -rf /etc/windscribe + rm -f /opt/windscribe/helper_log.txt + rm -f /usr/bin/windscribe +} diff --git a/installer/linux/cli/debian_package/DEBIAN/control b/installer/linux/cli/debian_package/DEBIAN/control new file mode 100644 index 000000000..fee2aeb9b --- /dev/null +++ b/installer/linux/cli/debian_package/DEBIAN/control @@ -0,0 +1,9 @@ +Package: windscribe-cli +Version: 2.8-0 +Section: misc +Architecture: amd64 +Depends: bash, iptables, libc6 (>= 2.28), libstdc++6, libglib2.0-0, libdbus-1-3, libsystemd0, zlib1g, policykit-1, sudo, passwd, net-tools, procps, policykit-1, pkexec | policykit-1 (<< 0.105-33), iproute2, iputils-ping +Conflicts: windscribe +Maintainer: Windscribe Limited +Description: Windscribe + Windscribe CLI Client. diff --git a/installer/linux/cli/debian_package/DEBIAN/postinst b/installer/linux/cli/debian_package/DEBIAN/postinst new file mode 100755 index 000000000..0671b498e --- /dev/null +++ b/installer/linux/cli/debian_package/DEBIAN/postinst @@ -0,0 +1,15 @@ +#!/bin/bash +# This file will be overwritten by rpm specific script which is passed to fpm in build script +# So if multi-platform change is needed remember to change that file as well +set -e +systemctl enable windscribe-helper +systemctl start windscribe-helper +ln -sf /opt/windscribe/windscribe-cli /usr/bin/windscribe-cli +setcap cap_setgid+ep /opt/windscribe/Windscribe + +if [ "`uname -p`" == "aarch64" ]; then + echo linux_deb_arm64_cli > ../etc/windscribe/platform +else + echo linux_deb_x64_cli > ../etc/windscribe/platform +fi + diff --git a/installer/linux/debian_package/DEBIAN/preinst b/installer/linux/cli/debian_package/DEBIAN/preinst similarity index 100% rename from installer/linux/debian_package/DEBIAN/preinst rename to installer/linux/cli/debian_package/DEBIAN/preinst diff --git a/installer/linux/debian_package/DEBIAN/prerm b/installer/linux/cli/debian_package/DEBIAN/prerm similarity index 100% rename from installer/linux/debian_package/DEBIAN/prerm rename to installer/linux/cli/debian_package/DEBIAN/prerm diff --git a/installer/linux/cli/overlay/etc/systemd/user/windscribe.service b/installer/linux/cli/overlay/etc/systemd/user/windscribe.service new file mode 100644 index 000000000..8955b0937 --- /dev/null +++ b/installer/linux/cli/overlay/etc/systemd/user/windscribe.service @@ -0,0 +1,8 @@ +[Unit] +Description=Windscribe service + +[Service] +Type=simple +ExecStart=/opt/windscribe/Windscribe + +[Install] diff --git a/installer/linux/cli/rpm_fedora_package/SPECS/windscribe_rpm.spec b/installer/linux/cli/rpm_fedora_package/SPECS/windscribe_rpm.spec new file mode 100644 index 000000000..53d7dc458 --- /dev/null +++ b/installer/linux/cli/rpm_fedora_package/SPECS/windscribe_rpm.spec @@ -0,0 +1,77 @@ +# make sure rpmbuild does not attempt to strip our binaries, which sometimes causes corruption +%define debug_package %{nil} +%define __strip /bin/true + +%global __provides_exclude_from .* +%global __requires_exclude_from .* + +Name: windscribe-cli +Version: 2.6.0 +Release: 0 +Summary: Windscribe CLI Client + +Group: Applications/Internet +License: GPLv2 +URL: https://www.windscribe.com +Vendor: Windscribe Limited +BuildArch: x86_64 +Source0: windscribe.tar +Conflicts: windscribe + +Requires: bash +Requires: iptables +Requires: glibc >= 2.28 +Requires: libstdc++ +Requires: glib2 +Requires: zlib +Requires: dbus-libs +Requires: systemd-libs +Requires: sudo +Requires: shadow-utils +Requires: procps-ng +Requires: polkit +Requires: iproute +Requires: iputils + +%description +Windscribe CLI client. + +%prep + +%build + +%install +rm -rf %{buildroot} +mkdir -p %{buildroot} +mv -f %{_sourcedir}/* %{buildroot} + +%posttrans +systemctl enable windscribe-helper +systemctl restart windscribe-helper + +%post +ln -sf /opt/windscribe/windscribe-cli /usr/bin/windscribe-cli +update-desktop-database +echo linux_rpm_x64_cli > ../etc/windscribe/platform + +%postun +if [ $1 -eq 0 ]; then + killall -q Windscribe || true + systemctl stop windscribe-helper || true + systemctl disable windscribe-helper || true + userdel -f windscribe || true + groupdel -f windscribe || true + rm -f /usr/bin/windscribe-cli + rm -rf /etc/windscribe/rules.* + rm -rf /etc/windscribe/*.ovpn + rm -rf /etc/windscribe/stunnel.conf + rm -f /opt/windscribe/helper_log.txt +fi + +%files +%config /etc/systemd/system-preset/50-windscribe-helper.preset +%config /etc/systemd/system/windscribe-helper.service +%config /etc/systemd/user/windscribe.service +%config /etc/windscribe/* +/opt/windscribe/* +/usr/polkit-1/actions/com.windscribe.authhelper.policy diff --git a/installer/linux/cli/rpm_opensuse_package/SPECS/windscribe_rpm.spec b/installer/linux/cli/rpm_opensuse_package/SPECS/windscribe_rpm.spec new file mode 100644 index 000000000..ea8f1e73c --- /dev/null +++ b/installer/linux/cli/rpm_opensuse_package/SPECS/windscribe_rpm.spec @@ -0,0 +1,79 @@ +# make sure rpmbuild does not attempt to strip our binaries, which sometimes causes corruption +%define debug_package %{nil} +%define __strip /bin/true + +%global __provides_exclude_from .* +%global __requires_exclude_from .* + + +Name: windscribe-cli +Version: 2.6.0 +Release: 0 +Summary: Windscribe CLI Client + +Group: Applications/Internet +License: GPLv2 +URL: https://www.windscribe.com +Vendor: Windscribe Limited +BuildArch: x86_64 +Source0: windscribe.tar +Conflicts: windscribe + +Requires: bash +Requires: iptables +Requires: glibc >= 2.28 +Requires: libstdc++6 +Requires: glib2 +Requires: zlib +Requires: libsystemd0 +Requires: sudo +Requires: shadow +Requires: procps +Requires: polkit +Requires: iproute2 + +%description +Windscribe CLI client. + +%prep + +%build + +%install +rm -rf %{buildroot} +mkdir -p %{buildroot} +mv -f %{_sourcedir}/* %{buildroot} + +%posttrans +systemctl disable firewalld +systemctl stop firewalld +systemctl enable windscribe-helper +systemctl restart windscribe-helper + +%post +ln -sf /opt/windscribe/windscribe-cli /usr/bin/windscribe-cli +update-desktop-database +echo linux_rpm_opensuse_x64_headless > ../etc/windscribe/platform + +%postun +if [ $1 -eq 0 ]; then + killall -q Windscribe || true + systemctl stop windscribe-helper || true + systemctl disable windscribe-helper || true + systemctl enable firewalld + systemctl start firewalld + userdel -f windscribe || true + groupdel -f windscribe || true + rm -f /usr/bin/windscribe-cli + rm -f /opt/windscribe/helper_log.txt + rm -rf /etc/windscribe + rm -rf /opt/windscribe +fi + +%files +%config /etc/systemd/system-preset/50-windscribe-helper.preset +%config /etc/systemd/system/windscribe-helper.service +%config /etc/systemd/user/windscribe.service +%config /etc/windscribe/* +/opt/windscribe/* +/usr/polkit-1/actions/com.windscribe.authhelper.policy diff --git a/installer/linux/common/etc/windscribe/install-update b/installer/linux/common/etc/windscribe/install-update index 3fe8ae74c..08704cc3b 100755 --- a/installer/linux/common/etc/windscribe/install-update +++ b/installer/linux/common/etc/windscribe/install-update @@ -11,9 +11,11 @@ if [ -z $platform ]; then fi if [ $platform == "linux_deb_x64" -o $platform == "linux_deb_arm64" ]; then - pkexec sh -c "killall -q -w Windscribe && apt install -y --reinstall \"$1\"" + pkexec sh -c "killall -q -w Windscribe && APT_LISTBUGS_FRONTEND=none apt install -y --reinstall \"$1\"" elif [ $platform == "linux_rpm_x64" ]; then pkexec sh -c "killall -q -w Windscribe && dnf upgrade -y \"$1\"" +elif [ $platform = "linux_rpm_opensuse_x64" ]; then + pkexec sh -c "killall -q -w Windscribe && zypper --non-interactive install --force --allow-unsigned-rpm \"$1\"" elif [ $platform == "linux_zst_x64" ]; then pkexec sh -c "killall -q -w Windscribe && pacman -U --noconfirm \"$1\"" fi diff --git a/installer/linux/arch_package/PKGBUILD b/installer/linux/gui/arch_package/PKGBUILD similarity index 100% rename from installer/linux/arch_package/PKGBUILD rename to installer/linux/gui/arch_package/PKGBUILD diff --git a/installer/linux/arch_package/windscribe.install b/installer/linux/gui/arch_package/windscribe.install similarity index 100% rename from installer/linux/arch_package/windscribe.install rename to installer/linux/gui/arch_package/windscribe.install diff --git a/installer/linux/debian_package/DEBIAN/control b/installer/linux/gui/debian_package/DEBIAN/control similarity index 95% rename from installer/linux/debian_package/DEBIAN/control rename to installer/linux/gui/debian_package/DEBIAN/control index ee9119588..8494372e5 100644 --- a/installer/linux/debian_package/DEBIAN/control +++ b/installer/linux/gui/debian_package/DEBIAN/control @@ -3,6 +3,7 @@ Version: 2.8-0 Section: misc Architecture: amd64 Depends: bash, iptables, libc6 (>= 2.28), libstdc++6, libglib2.0-0, libdbus-1-3, libsystemd0, zlib1g, policykit-1, libx11-6, libegl1, libgl1, libfreetype6, libglvnd0, libxkbcommon0, libfontconfig1, libxcb1, libx11-xcb1, libx11-6, libxkbcommon-x11-0, libxcb-icccm4, libxcb-image0, libxcb-keysyms1, libxcb-render-util0, sudo, passwd, net-tools, libopengl0, libxcb-cursor0, procps, policykit-1, pkexec | policykit-1 (<< 0.105-33), iproute2, iputils-ping +Conflicts: windscribe-cli Maintainer: Windscribe Limited Description: Windscribe Windscribe Client. diff --git a/installer/linux/debian_package/DEBIAN/postinst b/installer/linux/gui/debian_package/DEBIAN/postinst similarity index 100% rename from installer/linux/debian_package/DEBIAN/postinst rename to installer/linux/gui/debian_package/DEBIAN/postinst diff --git a/installer/linux/gui/debian_package/DEBIAN/preinst b/installer/linux/gui/debian_package/DEBIAN/preinst new file mode 100755 index 000000000..ed50ead7a --- /dev/null +++ b/installer/linux/gui/debian_package/DEBIAN/preinst @@ -0,0 +1,30 @@ +#!/bin/bash + +end=$((SECONDS+10)) + +while : +do + if [ -z "$(ps -eo args | grep WindscribeEngine | grep -v grep)" ]; then + break + fi + + if [ $SECONDS -gt $end ]; then + echo 'Error during Windscribe installation. WindscribeEngine is running. Please re-launch Windscribe. And try to update again.' + exit 1 + fi +done + +systemctl stop windscribe-helper > /dev/null 2>&1 || true +systemctl disable windscribe-helper > /dev/null 2>&1 || true + +# Stop and disable firewalld service if it is active, otherwise it will conflict with iptables on OS reboot +# Relevant for Fedora distributions +systemctl is-active --quiet firewalld +if [ $? -eq 0 ] +then + echo "Firewalld service is running. It will be stopped and disabled to avoid conflicts with the Windscribe iptables firewall." + sudo systemctl stop firewalld + sudo systemctl disable firewalld +fi + +echo "Finish pre-install script" diff --git a/installer/linux/gui/debian_package/DEBIAN/prerm b/installer/linux/gui/debian_package/DEBIAN/prerm new file mode 100755 index 000000000..086f3d7a0 --- /dev/null +++ b/installer/linux/gui/debian_package/DEBIAN/prerm @@ -0,0 +1,15 @@ +#!/bin/bash +set -e + +systemctl stop windscribe-helper || true +systemctl disable windscribe-helper || true + +if [ $1 != "upgrade" ]; then + killall -q Windscribe || true + deluser windscribe || true + delgroup windscribe || true + + rm -f /opt/windscribe/helper_log.txt + rm -rf /etc/windscribe + rm -f /usr/bin/windscribe-cli +fi diff --git a/installer/linux/common/usr/share/applications/windscribe.desktop b/installer/linux/gui/overlay/usr/share/applications/windscribe.desktop similarity index 91% rename from installer/linux/common/usr/share/applications/windscribe.desktop rename to installer/linux/gui/overlay/usr/share/applications/windscribe.desktop index 365c51149..ce32aa7be 100644 --- a/installer/linux/common/usr/share/applications/windscribe.desktop +++ b/installer/linux/gui/overlay/usr/share/applications/windscribe.desktop @@ -3,7 +3,7 @@ Type=Application Terminal=false Exec=/opt/windscribe/Windscribe %F Name=Windscribe -Icon=windscribe +Icon=Windscribe Categories=Network StartupWMClass=Windscribe SingleMainWindow=true diff --git a/installer/linux/common/usr/share/icons/hicolor/128x128/apps/windscribe.png b/installer/linux/gui/overlay/usr/share/icons/hicolor/128x128/apps/Windscribe.png similarity index 100% rename from installer/linux/common/usr/share/icons/hicolor/128x128/apps/windscribe.png rename to installer/linux/gui/overlay/usr/share/icons/hicolor/128x128/apps/Windscribe.png diff --git a/installer/linux/common/usr/share/icons/hicolor/16x16/apps/windscribe.png b/installer/linux/gui/overlay/usr/share/icons/hicolor/16x16/apps/Windscribe.png similarity index 100% rename from installer/linux/common/usr/share/icons/hicolor/16x16/apps/windscribe.png rename to installer/linux/gui/overlay/usr/share/icons/hicolor/16x16/apps/Windscribe.png diff --git a/installer/linux/common/usr/share/icons/hicolor/24x24/apps/windscribe.png b/installer/linux/gui/overlay/usr/share/icons/hicolor/24x24/apps/Windscribe.png similarity index 100% rename from installer/linux/common/usr/share/icons/hicolor/24x24/apps/windscribe.png rename to installer/linux/gui/overlay/usr/share/icons/hicolor/24x24/apps/Windscribe.png diff --git a/installer/linux/common/usr/share/icons/hicolor/256x256/apps/windscribe.png b/installer/linux/gui/overlay/usr/share/icons/hicolor/256x256/apps/Windscribe.png similarity index 100% rename from installer/linux/common/usr/share/icons/hicolor/256x256/apps/windscribe.png rename to installer/linux/gui/overlay/usr/share/icons/hicolor/256x256/apps/Windscribe.png diff --git a/installer/linux/common/usr/share/icons/hicolor/32x32/apps/windscribe.png b/installer/linux/gui/overlay/usr/share/icons/hicolor/32x32/apps/Windscribe.png similarity index 100% rename from installer/linux/common/usr/share/icons/hicolor/32x32/apps/windscribe.png rename to installer/linux/gui/overlay/usr/share/icons/hicolor/32x32/apps/Windscribe.png diff --git a/installer/linux/common/usr/share/icons/hicolor/48x48/apps/windscribe.png b/installer/linux/gui/overlay/usr/share/icons/hicolor/48x48/apps/Windscribe.png similarity index 100% rename from installer/linux/common/usr/share/icons/hicolor/48x48/apps/windscribe.png rename to installer/linux/gui/overlay/usr/share/icons/hicolor/48x48/apps/Windscribe.png diff --git a/installer/linux/common/usr/share/icons/hicolor/64x64/apps/windscribe.png b/installer/linux/gui/overlay/usr/share/icons/hicolor/64x64/apps/Windscribe.png similarity index 100% rename from installer/linux/common/usr/share/icons/hicolor/64x64/apps/windscribe.png rename to installer/linux/gui/overlay/usr/share/icons/hicolor/64x64/apps/Windscribe.png diff --git a/installer/linux/png_icons/128x128/windscribe.png b/installer/linux/gui/png_icons/128x128/windscribe.png similarity index 100% rename from installer/linux/png_icons/128x128/windscribe.png rename to installer/linux/gui/png_icons/128x128/windscribe.png diff --git a/installer/linux/png_icons/16x16/windscribe.png b/installer/linux/gui/png_icons/16x16/windscribe.png similarity index 100% rename from installer/linux/png_icons/16x16/windscribe.png rename to installer/linux/gui/png_icons/16x16/windscribe.png diff --git a/installer/linux/png_icons/24x24/windscribe.png b/installer/linux/gui/png_icons/24x24/windscribe.png similarity index 100% rename from installer/linux/png_icons/24x24/windscribe.png rename to installer/linux/gui/png_icons/24x24/windscribe.png diff --git a/installer/linux/png_icons/256x256/windscribe.png b/installer/linux/gui/png_icons/256x256/windscribe.png similarity index 100% rename from installer/linux/png_icons/256x256/windscribe.png rename to installer/linux/gui/png_icons/256x256/windscribe.png diff --git a/installer/linux/png_icons/32x32/windscribe.png b/installer/linux/gui/png_icons/32x32/windscribe.png similarity index 100% rename from installer/linux/png_icons/32x32/windscribe.png rename to installer/linux/gui/png_icons/32x32/windscribe.png diff --git a/installer/linux/png_icons/48x48/windscribe.png b/installer/linux/gui/png_icons/48x48/windscribe.png similarity index 100% rename from installer/linux/png_icons/48x48/windscribe.png rename to installer/linux/gui/png_icons/48x48/windscribe.png diff --git a/installer/linux/png_icons/64x64/windscribe.png b/installer/linux/gui/png_icons/64x64/windscribe.png similarity index 100% rename from installer/linux/png_icons/64x64/windscribe.png rename to installer/linux/gui/png_icons/64x64/windscribe.png diff --git a/installer/linux/rpm_package/SPECS/windscribe_rpm.spec b/installer/linux/gui/rpm_fedora_package/SPECS/windscribe_rpm.spec similarity index 93% rename from installer/linux/rpm_package/SPECS/windscribe_rpm.spec rename to installer/linux/gui/rpm_fedora_package/SPECS/windscribe_rpm.spec index 4c51468e2..a0bf078c3 100644 --- a/installer/linux/rpm_package/SPECS/windscribe_rpm.spec +++ b/installer/linux/gui/rpm_fedora_package/SPECS/windscribe_rpm.spec @@ -16,6 +16,7 @@ URL: https://www.windscribe.com Vendor: Windscribe Limited BuildArch: x86_64 Source0: windscribe.tar +Conflicts: windscribe-cli Requires: bash Requires: iptables @@ -53,11 +54,13 @@ Windscribe client. %build %install +rm -rf %{buildroot} +mkdir -p %{buildroot} mv -f %{_sourcedir}/* %{buildroot} %posttrans systemctl enable windscribe-helper -systemctl start windscribe-helper +systemctl restart windscribe-helper %post ln -sf /opt/windscribe/windscribe-cli /usr/bin/windscribe-cli @@ -86,4 +89,4 @@ fi /opt/windscribe/* /usr/polkit-1/actions/com.windscribe.authhelper.policy /usr/share/applications/windscribe.desktop -/usr/share/icons/hicolor/*/apps/windscribe.png +/usr/share/icons/hicolor/*/apps/Windscribe.png diff --git a/installer/linux/gui/rpm_opensuse_package/SPECS/windscribe_rpm.spec b/installer/linux/gui/rpm_opensuse_package/SPECS/windscribe_rpm.spec new file mode 100644 index 000000000..dff7f3fa4 --- /dev/null +++ b/installer/linux/gui/rpm_opensuse_package/SPECS/windscribe_rpm.spec @@ -0,0 +1,89 @@ +# make sure rpmbuild does not attempt to strip our binaries, which sometimes causes corruption +%define debug_package %{nil} +%define __strip /bin/true + +%global __provides_exclude_from .* +%global __requires_exclude_from .* + + +Name: windscribe +Version: 2.6.0 +Release: 0 +Summary: Windscribe Client + +Group: Applications/Internet +License: GPLv2 +URL: https://www.windscribe.com +Vendor: Windscribe Limited +BuildArch: x86_64 +Source0: windscribe.tar +Conflicts: windscribe-cli + +Requires: bash +Requires: iptables +Requires: glibc >= 2.28 +Requires: libstdc++6 +Requires: glib2 +Requires: zlib +Requires: libglvnd +Requires: libX11-6 +Requires: fontconfig +Requires: freetype2 +Requires: libsystemd0 +Requires: libxcb1 +Requires: libxcb-util1 +Requires: xcb-util-cursor-devel +Requires: sudo +Requires: shadow +Requires: procps +Requires: polkit +Requires: iproute2 +Requires: libcap-progs + +%description +Windscribe client. + +%prep + +%build + +%install +rm -rf %{buildroot} +mkdir -p %{buildroot} +mv -f %{_sourcedir}/* %{buildroot} + +%posttrans +systemctl disable firewalld +systemctl stop firewalld +systemctl enable windscribe-helper +systemctl restart windscribe-helper + +%post +ln -sf /opt/windscribe/windscribe-cli /usr/bin/windscribe-cli +update-desktop-database +setcap cap_setgid+ep /opt/windscribe/Windscribe +echo linux_rpm_opensuse_x64 > ../etc/windscribe/platform + +%postun +if [ $1 -eq 0 ]; then + killall -q Windscribe || true + systemctl stop windscribe-helper || true + systemctl disable windscribe-helper || true + systemctl enable firewalld + systemctl start firewalld + userdel -f windscribe || true + groupdel -f windscribe || true + rm -f /usr/bin/windscribe-cli + rm -f /opt/windscribe/helper_log.txt + rm -rf /etc/windscribe + rm -rf /opt/windscribe +fi + +%files +%config /etc/systemd/system-preset/50-windscribe-helper.preset +%config /etc/systemd/system/windscribe-helper.service +%config /etc/windscribe/* +/opt/windscribe/* +/usr/polkit-1/actions/com.windscribe.authhelper.policy +/usr/share/applications/windscribe.desktop +/usr/share/icons/hicolor/*/apps/Windscribe.png diff --git a/installer/mac/dmgbuild/dmgbuild_settings.py b/installer/mac/dmgbuild/dmgbuild_settings.py index 7e7f9f3a9..6ab728c54 100644 --- a/installer/mac/dmgbuild/dmgbuild_settings.py +++ b/installer/mac/dmgbuild/dmgbuild_settings.py @@ -39,7 +39,7 @@ def icon_from_app(app_path): # volume_name = 'Test' # Volume format (see hdiutil create -help) -format = defines.get('format', 'UDBZ') +format = defines.get('format', 'ULMO') # Compression level (if relevant) # compression_level = 9 diff --git a/installer/mac/installer/CMakeLists.txt b/installer/mac/installer/CMakeLists.txt index 8d8014545..3edfff1f6 100644 --- a/installer/mac/installer/CMakeLists.txt +++ b/installer/mac/installer/CMakeLists.txt @@ -40,7 +40,7 @@ set(SOURCES main.mm ) -set_source_files_properties(main.mm OBJECT_DEPENDS ${CMAKE_SOURCE_DIR}/resources/windscribe.7z) +set_source_files_properties(main.mm OBJECT_DEPENDS ${CMAKE_SOURCE_DIR}/resources/windscribe.tar.lzma) qt_add_resources(rc ../../common/installer_mac.qrc) qt_add_executable(installer MACOSX_BUNDLE ${SOURCES} ${rc}) diff --git a/installer/mac/installer/helper/helper_mac.h b/installer/mac/installer/helper/helper_mac.h index f83fc29e2..71b26d5d4 100644 --- a/installer/mac/installer/helper/helper_mac.h +++ b/installer/mac/installer/helper/helper_mac.h @@ -15,7 +15,7 @@ class Helper_mac void stop(); bool setPaths(const std::wstring &archivePath, const std::wstring &installPath); - int executeFilesStep(); + bool executeFilesStep(); bool killWindscribeProcess(); diff --git a/installer/mac/installer/helper/helper_mac.mm b/installer/mac/installer/helper/helper_mac.mm index 4733e9a29..60c31e362 100644 --- a/installer/mac/installer/helper/helper_mac.mm +++ b/installer/mac/installer/helper/helper_mac.mm @@ -86,11 +86,11 @@ return answerCmd.executed == 1; } -int Helper_mac::executeFilesStep() +bool Helper_mac::executeFilesStep() { std::lock_guard locker(mutex_); CMD_ANSWER answerCmd = sendCmdToHelper(HELPER_CMD_INSTALLER_EXECUTE_COPY_FILE, std::string()); - return answerCmd.executed; + return answerCmd.executed == 1; } bool Helper_mac::deleteOldHelper() diff --git a/installer/mac/installer/installer/base_installer.h b/installer/mac/installer/installer/base_installer.h index 158f105fc..71e5d54af 100644 --- a/installer/mac/installer/installer/base_installer.h +++ b/installer/mac/installer/installer/base_installer.h @@ -9,7 +9,7 @@ NS_ASSUME_NONNULL_BEGIN -enum INSTALLER_CURRENT_STATE { STATE_INIT, STATE_EXTRACTING, STATE_CANCELED, STATE_FINISHED, STATE_ERROR, STATE_LAUNCHED }; +enum INSTALLER_CURRENT_STATE { STATE_INIT, STATE_EXTRACTING, STATE_CANCELED, STATE_FINISHED, STATE_ERROR, STATE_LAUNCHED, STATE_EXTRACTED }; enum INSTALLER_ERROR { ERROR_OTHER = 1, ERROR_PERMISSION, ERROR_KILL, ERROR_CONNECT_HELPER, ERROR_DELETE, ERROR_UNINSTALL, ERROR_MOVE_CUSTOM_DIR }; @interface BaseInstaller : NSObject diff --git a/installer/mac/installer/installer/installer.mm b/installer/mac/installer/installer/installer.mm index 3bbcb03af..43b280f81 100644 --- a/installer/mac/installer/installer/installer.mm +++ b/installer/mac/installer/installer/installer.mm @@ -70,19 +70,16 @@ -(void)cancel [[NSFileManager defaultManager] removeItemAtPath:[self getOldInstallPath] error:nil]; } -- (void)appDidLaunch:(NSNotification*)note +-(void)runLauncher { + if([[NSWorkspace sharedWorkspace] openURL: [NSURL fileURLWithPath: [self getInstallPath]]] == NO) { + [[Logger sharedLogger] logAndStdOut:@"App failed to launch"]; + } self.progress = 100; self.currentState = STATE_LAUNCHED; callback_(); } --(void)runLauncher -{ - [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector:@selector(appDidLaunch:) name:NSWorkspaceDidLaunchApplicationNotification object:nil]; - [[NSWorkspace sharedWorkspace] openURL: [NSURL fileURLWithPath: [self getInstallPath]]]; -} - -(NSString *)runProcess:(NSString*)exePath args:(NSArray *)args { NSPipe *pipe = [[NSPipe alloc] init]; @@ -108,7 +105,6 @@ -(NSString *)runProcess:(NSString*)exePath args:(NSArray *)args -(void) execution { - int prevOverallProgress = 0; bool terminated = false; self.progress = 0; [[Logger sharedLogger] logAndStdOut:@"Starting execution"]; @@ -245,16 +241,18 @@ -(void) execution [self runProcess:@"/usr/bin/killall" args:@[@"cfprefsd"]]; } -[[Logger sharedLogger] logAndStdOut:@"Writing blocks"]; + [[Logger sharedLogger] logAndStdOut:@"Extracting and installing Windscribe app"]; - NSString* archivePathFromApp = [[NSBundle mainBundle] pathForResource:@"windscribe.7z" ofType:nil]; + NSString* archivePathFromApp = [[NSBundle mainBundle] pathForResource:@"windscribe.tar.lzma" ofType:nil]; std::wstring strArchivePath = NSStringToStringW(archivePathFromApp); std::wstring strPath = NSStringToStringW([self getInstallPath]); - if (!helper_.setPaths(strArchivePath, strPath)) - { - NSString *errStr = @"setPaths in helper failed"; - [[Logger sharedLogger] logAndStdOut:errStr]; + self.progress = 50; + self.currentState = STATE_EXTRACTING; + callback_(); + + if (!helper_.setPaths(strArchivePath, strPath)) { + [[Logger sharedLogger] logAndStdOut:@"setPaths in helper failed"]; self.lastError = ERROR_OTHER; self.currentState = STATE_ERROR; callback_(); @@ -262,50 +260,20 @@ -(void) execution return; } - while (true) - { - //[NSThread sleepForTimeInterval:0.2]; - std::lock_guard lock(mutex_); - if (isCanceled_) - { - [[Logger sharedLogger] logAndStdOut:@"Block install cancelled"]; - - self.progress = 0; - self.currentState = STATE_CANCELED; - helper_.stop(); - return; - } - - int progressOfBlock = helper_.executeFilesStep(); - - // block is finished? - if (progressOfBlock >= 100) - { - [[Logger sharedLogger] logAndStdOut:@"Block install 100+"]; - - self.progress = prevOverallProgress + (int)(100.0); - callback_(); - break; - } - // error from block? - else if (progressOfBlock < 0) - { - [[Logger sharedLogger] logAndStdOut:[NSString stringWithFormat:@"Block < 0: %i", progressOfBlock]]; - self.lastError = ERROR_OTHER; - self.currentState = STATE_ERROR; - callback_(); - helper_.stop(); - return; - } - else - { - // [[Logger sharedLogger] writeToLog:@"Block processing"]; - self.progress = prevOverallProgress + (int)(progressOfBlock); - callback_(); - } + if (!helper_.executeFilesStep()) { + [[Logger sharedLogger] logAndStdOut:@"executeFilesStep in helper failed"]; + self.lastError = ERROR_OTHER; + self.currentState = STATE_ERROR; + callback_(); + helper_.stop(); + return; } - [[Logger sharedLogger] logAndStdOut:@"Done writing blocks"]; + [[Logger sharedLogger] logAndStdOut:@"Windscribe app installed"]; + + self.progress = 75; + self.currentState = STATE_EXTRACTED; + callback_(); // create symlink for cli helper_.createCliSymlinkDir(); @@ -315,12 +283,11 @@ -(void) execution [[NSFileManager defaultManager] removeItemAtPath:sympath error:nil]; [[NSFileManager defaultManager] createSymbolicLinkAtPath:sympath withDestinationPath:filepath error:nil]; - self.progress = 100; + self.progress = 90; self.currentState = STATE_FINISHED; callback_(); helper_.stop(); - } -(void)waitForCompletion diff --git a/installer/windows/bootstrap/CMakeLists.txt b/installer/windows/bootstrap/CMakeLists.txt index c320df51e..4c95aae6b 100644 --- a/installer/windows/bootstrap/CMakeLists.txt +++ b/installer/windows/bootstrap/CMakeLists.txt @@ -5,15 +5,14 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") project(bootstrap) -find_package(7zip CONFIG REQUIRED) - add_definitions(-DUNICODE -D_UNICODE /wd4090 -D_CRT_SECURE_NO_WARNINGS -D_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING) set(SOURCES - ../../../client/common/archive/archive.cpp + ../utils/archive.cpp installer.rc main.cpp ossupport.manifest + resources/7zr.exe resources/windscribeinstaller.7z version.rc ) @@ -25,6 +24,7 @@ if(WINDSCRIBE_INSTALLER_NAME) add_definitions(-DWINDSCRIBE_INSTALLER_NAME=L\"${WINDSCRIBE_INSTALLER_NAME}\") endif(WINDSCRIBE_INSTALLER_NAME) +set_source_files_properties(installer.rc OBJECT_DEPENDS ${CMAKE_SOURCE_DIR}/resources/7zr.exe) set_source_files_properties(installer.rc OBJECT_DEPENDS ${CMAKE_SOURCE_DIR}/resources/windscribeinstaller.7z) set_property(TARGET windscribe_installer PROPERTY COMPILE_WARNING_AS_ERROR ON) set_property(TARGET windscribe_installer PROPERTY VS_DPI_AWARE "PerMonitor") @@ -34,7 +34,6 @@ set_property(TARGET windscribe_installer PROPERTY LINK_FLAGS "/DELAYLOAD:dwmapi. target_link_libraries(windscribe_installer PRIVATE delayimp - 7zip::7zip ) target_include_directories(windscribe_installer diff --git a/installer/windows/bootstrap/installer.rc b/installer/windows/bootstrap/installer.rc index 116d5b0a9..cde82ba80 100644 --- a/installer/windows/bootstrap/installer.rc +++ b/installer/windows/bootstrap/installer.rc @@ -1,4 +1,5 @@ #include "resource.h" -Installer RCDATA "resources\windscribeinstaller.7z" -IDI_ICON1 ICON DISCARDABLE "resources\windscribe.ico" +7zExtractor BINARY "resources\\7zr.exe" +Installer BINARY "resources\\windscribeinstaller.7z" +IDI_ICON1 ICON DISCARDABLE "resources\\windscribe.ico" diff --git a/installer/windows/bootstrap/main.cpp b/installer/windows/bootstrap/main.cpp index 8dab5e83d..ac021540c 100644 --- a/installer/windows/bootstrap/main.cpp +++ b/installer/windows/bootstrap/main.cpp @@ -5,12 +5,11 @@ #include #include #include -#include #include #include #include -#include "../../../client/common/archive/archive.h" +#include "../utils/archive.h" #include "global_consts.h" #include "wsscopeguard.h" #include "win32handle.h" @@ -159,7 +158,7 @@ int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpszC return -1; } - std::wstringstream path; + std::filesystem::path installPath; srand(time(NULL)); // Doesn't have to be cryptographically secure, just random if (!isAdmin.value()) { @@ -200,8 +199,7 @@ int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpszC _T("Couldn't locate Windows temporary directory (%lu)"), GetLastError()); return -1; } - path << tempPath; - path << L"\\WindscribeInstaller" + std::to_wstring(rand()); + installPath = tempPath; } else { // Find the Windows dir wchar_t *windowsPath = NULL; @@ -212,12 +210,12 @@ int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpszC CoTaskMemFree(windowsPath); return -1; } - path << windowsPath; + installPath = windowsPath; CoTaskMemFree(windowsPath); - path << L"\\Temp\\WindscribeInstaller" + std::to_wstring(rand()); + installPath.append(L"Temp"); } - std::wstring installPath = path.str(); + installPath.append(L"WindscribeInstaller" + std::to_wstring(rand())); auto exitGuard = wsl::wsScopeGuard([&] { @@ -225,40 +223,21 @@ int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpszC DeleteFolder(installPath.c_str()); }); - // Extract archive - std::wstring archive = L"Installer"; - std::unique_ptr a(new Archive(archive)); + debugMessage(L"Windscribe bootstrapper installing to %s", installPath.c_str()); - std::list fileList; - std::list pathList; + // Extract archive + wsl::Archive archive; + archive.setLogFunction([](const std::wstring &str) { + debugMessage(str.c_str()); + }); - SRes res = a->fileList(fileList); - if (res != SZ_OK) { + if (!archive.extract(L"Installer", L"windscribeinstaller.7z", installPath, installPath)) { showMessageBox(NULL, _T("Windscribe Installer"), MB_OK | MB_ICONSTOP, - _T("Couldn't get archive file list")); + _T("Failed to extract the Windscribe installer.")); return -1; } - pathList.clear(); - for (auto it = fileList.cbegin(); it != fileList.cend(); it++) { - std::filesystem::path p(installPath + L"\\" + *it); - pathList.push_back(p.parent_path()); - } - - a->calcTotal(fileList, pathList); - for (int fileIndex = 0; fileIndex < a->getNumFiles(); fileIndex++) { - SRes res = a->extractionFile(fileIndex); - if (res != SZ_OK) { - a->finish(); - showMessageBox(NULL, _T("Windscribe Installer"), MB_OK | MB_ICONSTOP, - _T("Couldn't extract files")); - return -1; - } - - } - a->finish(); - - std::wstring app = installPath + L"\\"; + std::filesystem::path app(installPath); app.append(WINDSCRIBE_INSTALLER_NAME); DWORD exitCode = 0; @@ -272,8 +251,8 @@ int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpszC if (exitCode != 0) { debugMessage(_T("Windscribe installer exited with an error: %lu"), exitCode); showMessageBox(NULL, _T("Windscribe Installer"), MB_OK | MB_ICONSTOP, - _T("The installer reported an unexpected exit code, indicating it may have crashed during exit.") - _T(" Please report this failure to Windscribe support.")); + _T("The installer reported an unexpected exit code, indicating it may have crashed during exit.") + _T(" Please report this failure to Windscribe support.")); } return exitCode; diff --git a/installer/windows/bootstrap/resources/7zr.exe b/installer/windows/bootstrap/resources/7zr.exe new file mode 100644 index 0000000000000000000000000000000000000000..b3e826e03d6c01de002d7aa41b665f0151628b9d GIT binary patch literal 920176 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

278%>@v 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%0Aw$F3%9wpcXPW+82#B<^+F{D^`8<8GNh} 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

*$6|gDf@5`>b3v(~9|YG20XZilQbkGI-SJxYcjC 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


o^5RK=$&+wQp(TTOlSOJ!MV4rX$l2HA}dJXL)i z=G7U-{-ZQRBFwO-wSI;U{qQtQ=)7$Fj0 zs@{-z((Oflh1N_>v{j7jnQ-qabaHtAWsB8U*q>U(L!SIS9s>KPSYGL8=H)nS$~@|8 z%tkcY70Y{`jZqGWvis3Jp2{Q{C*PgG(IE^#S75U9d2(h=b98Ec`E&!`{V2N_bEVJF zC)&8_0CKNa%dAZYFxVv>z)V&bX!w&B@|cJ)-USH91^#5N+`en2pFg(VIjS?1(+T6J zm4-Q1DH0z`K;V`I%qE~#H)KwjJcd=8iprszwdZsKv5YDcU5gy4MK-S)M<<-fDokY+ zez8o_rbb=6PLe7B*rYc#{s`QHz!?5{ar_h83-_FVZQ(8FPk!It>>xA|a?vMT-CCeJ zR6c3|?Dw$d&B0}En8}tQs5sm{V9d{~kxyI(RFP@J+6azAJDuQAig3eKZ)1@NRyR-9 zPwFa62IBKD?r|$_6n&R6&NCO6tO0QDMo<48WoP;#gY6h}%dpQt7%WF!3eJMtjKWyS zL4L$THw)mN#;4p>YG6%vRw!iU-(DE}JA zX|6yn4Clt{@P^!_?*#XS$@GPUp4)#~= zl`B1q=GGM(U2q+PnQruz`qJ;O(4C{M%=YMm2fcXG09O$C4*Hx+fAY>;FZU!xea_*q zGQ0y9@?;^HIz*Zyk7?P>41ml4Qv&c0qf0QM`HV}}xhndI~!c9c#z>jgHzVAqR8}^jI z$`3n6e{u2w9;x=ns;~~JTkH}?!tMde>a|zpx_SFdt^?8G%Fzqot(VGRaC0oxeUF&} zT#&ty{kbA@s&=Mn=U(<|fGdZqc5sHn7Ua9%q9VvKmXU`2%{|&XfnMn6OX2b#?;wFu zy=^YR3Lr2IoVAMs`~fp4p{KJKcyMwE?Fq^=3BXT~x>jWRO+QmHzSQ*Rnf_6xAEJuQ z1KZPy3OY{AR?k%7CvVWTvuS|PUUYkDJT?kQ}>{&uq& zo77n?Y>OuKj3jGPkHHnIR*vh)e5pZM%>Aw7m%mEl~&Ut*jt|&4! z$!kPg+Up@!@SC$@2=43h7GTU2#m3AiprKBevignu2`E)!Utl9}=GZt(!Z|jY?;x6w zq*%SJ2a>9ZGIU(sm`%=beuDP)ga^M4P?CkJb><8fklXBVQ)A+FEXfhVDD~ zbk3LVXU<&YoEfzatGd?2qHmVY-UZPu7wX}>fiy0-%sL;Hwt#>yMZcDTDzWGOKuV_F z&zLLz6YKHPI(EddgMCbPZ}wMqaOr*Od)-;2-x_0VyhOn4=O4e!3y<}%wubQ=*ttSy zIf$B~8Jx3cOOUij%AgbSwnD;&u3QIf{(!yjyM<1SIG(R}$>M>%I7V7uho$LvV8 zIV(>m5y5ZV2&Cx@)+y7EVgb3Gz|HO+>^SgRTWYzp;%y}2P_2w0S5aP3_h1hS%YF2; zf8AA9MoEJ^M{$cVVx5(Il9d=zG)1<|U_OhaPv^X-bqISO9fZ< z#m!f?Dl!?yoJ`IHRCiR!Ey;6j(B4?zo+NL7RNzz649Mj;D|!1VGZCGjhw#}bnifYZ z1U`q=HGs`|gpnM@RY$Qi;Hy< z3&cJc2ZGkbs$!6d)y2*VzRJsvG?76d9Zy0?7dQP?bP-=|x;(6;ySSO)3DbQNu5@v_ zz>5IA%g}xn?LVcab~~%s4OL{4a%PQq9J+Bj#}m9D33Q=4a%(&?7n7t#`wsqSMdo&t zflCt6B^Lr9{!3rx5c8wcOaxs>a+}}jt0!TJn3}N(`%t>)Nqcl)%apTa&S%Ro4GIR1 zHj?))ET42&l^%CJFpzi1OP#aM!Wz?JP;pkphU3uW%(_qzNjaG%2vZ-*uPTI=74fTv zUxkzn$mxTQK=Mk>Nf=Z}qBSpF~<;kmboc$bD3BVmarz zxWwW!xKPro51^FiO|An=%_~mEyA4>96b!+s`d&YCK7$g<;jT0#A4p^53oNJFUkiBMNE5Q1dYiJ(4{2z78P zs8tr!=_XXH_@q6Bs^d+leNF_mI1%d3s}d>D$Aa43gvtp{I;C9telXaFm%gJ{(l|?c zC6?^%OA59JyU2SPySH*}`;qz&Tr%^5gJm#PW!L-!A@Q#H)fI_dbD55_S3pWXj|6eF0$hKhTG1Lt4_}!`*)30-xHcdSo(0_5+~@y>?jwW^bPc$z(Va$!F?2(viKM+z z$Ju+F2we|BTIhavMIyRAEOfgP0MW&(rN7@3T2uG?&osJUz-^80hX{$I`^ktzbYItT z_IvL<0d*fDNDJM*t=!LC=J?7_^X&b2V@LIA{cIwmrUu>Tv= zFF)0oehN2^X&roVOg}D3#Pk&%XP@%t6VUXp1ZQD-aVt!xJZaMOb^;)#A9IG|gbLpE zzcI}vXwWnpZXDBY1Qw&|1+6em(sB0nx1Rvh?>|Yvbk6WZny!Ds#Iy!!1k(%tPnd=Y z8ko+68^`n>0*hffBalebYjvFcyc1#CpWrN-ep!-;>3J5Wrx5^YI)l=YNmFq52#KTHI4lv}7j>LHc-sjGYY?P`Ztqs;1|K&G`zHb*y1bhw588L| zE@uT*9#GXgE35J0%OB#Ha#C@@L7W(e7kBmkwNYyj#*-d~>jCjvvgX#k++A!`#X>Bt z&nFfle&>8ik*87hLL@D5BCf;iya;*uLkfHh@%%}LEI>$!u?8XkHq*>TNbW{CXv;Tk zPW2V+fH@I%kDuKrwb;c~8}3x055~e)p^LwPV7eAN?gzm-676od;~xK)u+I_^WN)Hi z8j6|Q9~%!9kXz-x2NLKsCy38cr~YodN9|kLW`1=bwnZgKCc>YHVW5`A^D;qVd^KvS z-ozC^ZQw5|`YCPa7mhXp7tptnUqoVVEq-=6dc^VdXT16hHdAfnPbYqVdbmyaqE-BC zMT|Db)=$KZrN1AK_DNTDSm2!iKjxSAyC)7Kk$wVyoWIo-c?t2j`{ge&z}(Mt9A_`E zH8<+1%J)=FOTw=me#!Wy;Aam{bA+dL3QtQ10vYP|Z_rsiRnu|eeR>c4vhlkRzl-p@ z7{5OF<>J>Dzf17Dw6?G(Ku+&fTi83PwlD|3elU6(mZ#i=bOH;5INsJ7ll$Mb2%C7$ zIZuslLU`<)bAUecEl952XjhQeF4!5bZWl*a;eEpLd|TNsSVrG-hA+EDObdWIT=n@uVjRVITir14)pNO*2oRm%idEh5u79|U)ZHu$6Uo~qokU%+=#WjWg*x9*N3V9RO^W5GI;>^BB(|BT#3UHVA zW;pMV|9mH|yl7l@-RVm|zHM9i-xk=i8@x+$4t%p}N1E{=Hz;i?8TpD=;EVr~TH56l~OTW_}^t+c>g1uok&=<@aQAZGSeB(@(;jVt(UY zC|UQnqd)T3Xw(ThG>rHkMi{yG{Y!e2VHI zw|BU%)tRfU%!`GJX;a}xN!Za-MM#K}CZMFAI&qLH6g<#{kI0Kl!A1oa*Fn5bW@OWz1T# zTILSkS>@e1ckr$%?=EBTp0Ib1G5Eh>?|+TK2gBZjYBUsOIDwLfM`QmCE29zkc;K!q zRmfmenS28G$(?xqLLa!!%@dHLpmK5D1fqqLd98(PHLF`{X^d1CqMX2gwV{LBIi#J# zbo>#yN?*;PhNNyz4(41PY+nz0;oLqTKPg{B9r&E6KhyD9q(5QWYwsYRC|VwFP(M<( zv))kXO~CMs0%UbAJhgMAKh(|P)z>VpZcdF=s#KV!cX57Vl#BvN9qN?9KvoMet>h>@ zU%FN2mFNI%#Lrxn$I`Rk1Wk34MEE@O=x2P#g!uT}^s|^+ZY{18!n~Ib2ZR5o#q5%6 zDHZ=bgN&-oj+cvDu1C~Ix5T18lc?Yi2(68WuF@H>eRNei&)~aW#_3%X*z-CLcko`7 zK{+A*guF8vV(ZA`@`^GMSWz85EazZv_^Xy%<>I!#8ikr4i1?AzVU36yE9cZ6{0Kg?z~K39t% zH~0TBSEln~{e`_tZ{mL8I<%YUd8H0H^xT`1OvXz@8aePd~+9>f{>Yu4=pfz>t zbE&1!G)z_91VYIK)kS}ls*d`@quS|@9Cc_dQHAulM}MTNAN2?9N#tX_+6)(`Sszb4 z&H4et5@ni1oO+!YC$0Smn?Pr3pb#kHM`Z6LP}6plk3$P{8n>L}Dlxvn-NltykI@R) zZ~TPg;j4Z?%|jW8^@nyjsMj?5;%ZD&$?led8dU}hik5v^7a|<~tCOC^uR!=sPEmpI zE8E5S2PbNd8E^b~j|905<%VVGtXTu1ja%UXKz-~rM^`z-RzBwFtFHFL%zbs*5yXDLAIL~)FgHEav7b4-S*MoYlJ#MT~0S~#(f`}68q7n&j6fQ5C`b?>)4 zIyENUYjnxh0@eb39~L${n$*HNrmj~Xp=?u{s$7i|^{b0RYjDlS+LVGZj!DmIcr{RT z;{7MUqg))aUchUvMqUgx`4{J9l)Z+$4(P(h9D>Hi>r$3z#qJjYzRi99+7-^ zAs>w{af6(6zb5r5>be4{Y()vIArE#RNu{1ih}fmU1{sE(G_)lhZQxu+*OEZ zo?|ARLEOBOl(l^ys#=_Xl$1@XhlDq&bK$bK((h2)t=NXtwVWd9!$=RK2S6Gm^uhd? zc7?>;g_zLMq{&c$oWA@Xlo@r6G=YEgfk_Bsb+ZgJz9{%E(SmfFrGDWoDo-zBBH$rs z239~&*IzF~gNgn)#yze8YOLHt5vZ2}n2ua`QrC(aej_!6Kn<&C^$9#?A6|ug%i0nB z(3(-d`@V#noqGBe6cd}T*^v|bah|$A7M~!Kg{Pvu)wP1*Fs#ja)z_Vn^E8h{KgS&f z)7;>Pnq6F|nGkNA<799wuSh2tbO#4qa3E|t;#k0u_ZcYHQfZE`0|Dgg!g|SQ!_~xL zlcX~QpE~_Lf#ECo9lKfVV~{@)xBs{M6Vg?gK!j@i4*6KGZh?F3{RyRvOPB+5Vs|rK zqU(*LTdgNS9Ydm}gEr z9Hz%iJZIpne!6;rIk3(9w79Tgf(wd&1fI`4YPN(M>HNM|f7|%|7yV5ER^6<>xAXfN z{k?(Tm+S8ueqW})U*vZm{k?$S=j-n(exE7d!=|c}^~VI|;74TGDD?_&*&Fxg(n^e{ zU#yE83jIRFwWt6N%7HziN;np|i8@2P9o!&plUfb8au*M&3_}K!hOZ+KedolwCoQDE z|1Rx7p@v$afE}D>ICY_&_zMcy!t(GUgmjs${)(9cvb27@>eEJG5MDu+6kb6xhF4_b zFM`-I;T3GF@Cq87ASh2f9{KCjL4NU+%HNSXXcQf}Su64QOMF~H)>d;bVI#*BuC2x` zW$~G-1l9Nd3G8$-U4M+9#Cx`Jhb4r1z7vBA+5aV~aV#JV8AONI>w3!QdK`jTb#15$ zxFyww^!@7*qZ(dyKch6l)}`CW;Ny{EE@#8Wa89g`2lz^n&IEj8Z)_MPj2A3^dLdq) z_?VkED#mNfF;`f646o5+ZV^?O-0ON$KGv&8;9>}1%nd75vn3Gf!5R7k{L1V>cQC*z z&zR=GxeH^5x8M*j2?C!xX-Fi+>HSy@F6c5dSTe&7Yp|A-B3y6cD`+Z%gwm*Tq5BOO zr}>N_4qtX%WJHqjb#bK26UC8EbL9=}g1afIoNd&3!@Zu24slo|ul5=>EkC!_cWaZ9 z*k_N|nW0_;tq5iC;Q?C*yYtey8Hs1wR*lr-i3wgr{}I zm%Gl(^H3}(2R~?9r$N&?4VuEm->JAk9hu=tn9`xo;04%`hCmw2rkYX zH!VX?!{+j*5EFK*X*iv;@5Wjh4E*iK02%RQb40R3cV#} z*$?#+Opf<7??71Vb{ea`eJ(}-3NHAU$Wtb$4`T5YNkX{(3eTvQb&?ghS_hsNTY$&L z_Wwa{83l|;u~K4d$t{Hv7+=V2GuP`f_lOQw-my=l)mHzwL|Wg8Qz)e>%0^Lr`3$E}#EVIL2b8)L0404hzWGvZKX z_v}uFM+{D7>TWV|Q((AS(re9CF~7&jdev|I2F)bIksp)2$-8*E6m{5w!HmYo3DfEadkk#}AYED!1AO5wN&KWoC=BJtbh@Ao>M;-~r)*84<22Au66J>eA`iD4Pd;T8Ac8>N+NXQp;$ zX(yze3hh+F39q09Cg2HM5FHGVmk2T*AdkN|#}*DP!AD)_E&RoQTR6mpWO&6&{B1pJ zCbl={L$|u-?``!r#^&Q=&~czc;S?yMY3v~b z^k&c;9L4liEx}|j6A<)AgjVq7w-;Vc zwau-_><(X*{Uvx%gD2p4If!hbm6uzFe`=2` zf)3J7(}`yc`-um69pzJURKy!P5)Hb1;cn3vLEvyV$4laT3199uUu1d` zj^lqpnx})bY%QEoAccd;MYtvGy;33QzF?2#I8udy`1f_BWj%}Jpu)Le1mG(2lW-vT zRS8<2g>bd^RaRf|VHc>C9{aQzIce;Xr6f0+hgTs z^Y!Wl+*!oXa%5E}eagPOC;!HbvcmqW%g#a%V|aV))>=t^{^Urs|7skxjo3S0PJCnV z-+C|s{?!5>V|)+l3g@79eucx4!%$La3Ft^{SN`)Tpq3Hpqs6VDj!A?Xy@^-@TskNL zCqp}3;o$Wjv{OCcIcF~fbcpgmjV%CYfp%n?<~}!45evpbJqz-#S09{qlvg$SP-Ui0 zD(laK7)*g{f>C*vs7})NBAE>GPp^PM@}{cQ4qw3{DSJ{??zR@zq6wPq1llb4YXWWZ zQHyF|KLPCmwDC2OErjAo?9f36&IZwH+pz!fEo3WL?ROR(B!lfEU4}ex54uX!^|Ev| zye+}-NZM)ys?L9;;@}|5=Asb63mJR>?Gb=^1cK{HjHjdn1 z2$TJj6i|!;d~?Tmy!*G|X2SDQ+_*dv@vy@{L=mL!o`J}P0VH+)F}TG}n? zZ6!)ZBJ7?-*Ln-Sp7bU1Eq9~nsH+976vEY=!Qr{viXi=G1IUr<2xLsk@lf7FY7@#= zlfE#nNm;eyEE3~RO$-y}V@MS*Aq<}}#tr>E_$tmazXpZ*5yw4Umx0cr%Wu@TmRj@q_D9u0u{VDj{&g3irqY;aOLLA(?Q=5}A?ocn72l9Z3T{(h$;`9>ejV z&@uEVKoBY53tj-ap+^mtnYkB&jcah%74{xpCK1x{gksc{hG3tuFMkZqKK{T0f!0*e zoh=Q<%>0LC(%Feefj~JN+8L0H8_-Jf@4}r8M6m=5BiWa6!xMF_M7m-lb@1Fyc*5iB z0b{0gcD{qRZO{v!LrhD+`Oa2ca_HsV1p128OU)}zLvqEbmADLvL*F?gSLH+s%@m;{ zyyw(PFp}j&*OuT0UgxZ-pd9ZdB<5beC9@i@7HY4rU@}g;U5Es{fPjOH`*lrf{)?Ou z&Gu23LvhJhp~!sYeIor?jJ-r(!JQerI;0-ebIz*3GLAM!ng=wg5`fWUYLIzu?OKwl zN%ccGI$x2o+n3*O!fQB9I$IC$e%HT}ievQmqr0E2Gxm>iw{xCkJl%`?yIfQ8ZCvFJ z9Zte!JBALs1z{~%b>bcEmsn(>16B>Z#S|BkHd#a&ghT=3tl|S?0JzJv&Klh3Bf>ZL z`b^Cl&QFNxgcz#~8^6t&&`%9gHr9w<8HVxTCiO9nzgbzsdQ-d~|2)>ONM`DT}#LbllNc}(*$ ziaCywVxx7@La`zIHZkW06xSo(Wtcc(p)4~a0r4(o4mK;Wcc?*6C)QO5H>n?RhSRF+ zH><6>rsMq!@ieIb@Qv4X78=(%>nhZhy@^vs^Zb)JOQe>K#|$y(b+kQh3@oVSBoQ!D zG0eAfQU&uk$SM8yiE?N&bP)F%4@X8~z2kXO%GD8hiwib$RMPmc??_VZ8erjl4>|GH z*?*dQAcwF2K8Wacmf7A}ftxb~xX24Xe8khV`@Q{ZofVg18u4Kx_zJkevwheE&I2B0 zUBLZ5yh?Mw90aJ7m!t5A{b;_PAon0Fb_K^lCh$EASuy~7!R;uDmsl&)Z8qHEC6*75CcYIz_540C_wAm=~z|c*#aNPBr^m8IghBNm?qc`FT%sfC8qlp zT;630(`nQIaCw2LjqG6vLJc=7s9)!LQi; z6_+MNA4?N&!B@eHxmFY%G*yxF^zm7vuaixH(a>4V!|H}>lM1-VPzFm0ok~rT7?VP` z$|49+Lh@@FW#RjmFLY-)?b3KZClKjY7n&)hzi&Xznrkoh8MVK*{aw2*Sz`zJa+PLT(wY@Gw%DF1Jc821+23cq+<8D~7>) zi;Z=<71KiSVevwQaa0?-0ReWC3bUB`QZ2USYLHaga-|gFH@@~39A#U6r_YLVGSlba zp;u0hwPg*a9Jb{$PCW%jbz5!+cIpL*F;ROQp7``_#C#;z3^SoL^F-4#fia@^p`pwku-1+J@yo=+yb_dW7``lAEr|3>@`>I$TTR);bJ zfP;Ll10g+18Ki7inaiJQM4Hkb9IbuC;zx&)&DzAIbdvQNe&p>KwGi}sF9*IU{U&8kGMd%>jr`+ z1}X*M2{`@t0C#K+m^jlYn}GGVZhio+Z=Nip!QRze7`B=*9GnAAc@iUJ?tJM%b3fRR z3j-h@cFX)HNOxX?Ka81>9oev-$q;f~b5h`3VKUq=7zDw-YsE(DV%Ri0MFlcsC7Y^2 zy@T37&I4iHLg+o>X4 zQWN+Y4s8b&0^ui>5aUgDj6itl1VHdrEuE;HyWt>^#sF~2wNs^?YV9o0&O+_HsGTKn zthK)Lv6`wmXCT0un8oth7k;07n0%yz^pZK$Ua$$qB3OAFz>P5b5+2=`O!sBeeFZN0 zA`2p4TxPnOw}Y#3MznzWaa@i}o1>LXn?S)5d1AAwLO{dUL;F~OIaMS(=T~I1AU5hj zL=zwjRH2Xc6?Qgy24f$tXfuVnUIm_NBjpW!5HY zk^c4o8woT(8>_p9ix#OuW(dCN5P>y1j*i9p7;bACOApcT^XjGmF2Td=9QVQWA5*r6 zM)D##kfd62z;F=029C|0^q;zNIjX7={^-Q)T^s?dJfj$AYIRaj31Z~-b+{f-66#ORSkz3 zm@Z<%Nke*X|F41_F_Ch)g#OH?cUH)N%wNjdIxBC*U!eP*rl$e5|xq089{+;svv3V1E~8hp2D z-yh*)+fo81e-qw@i;)9VUhJL47qa%iVTsSEF|Kjfkd*jN=N<3C^dr=b)<0W zjxSMm-fotL1$(fFy^Ix^45rwmHj}W1>nBmSr%k}?Gf~&&Z|XiO)kT_~Y+-bdmyye(r6pyOYc8|>mN=SZ$yMYJXzfaerEjiw&EiZS>H@Y zKR+S-NW>=fpZ@#s&uSY!xsU3&tWEk0yN}`~yWcCn?%p=ak3>Y$J@9Co?Z^6o@jKK7 zxR1{)Be8!<{Vxc$T|U-NoH*3=@is`zPYs${+}LJ*=^t;l9WVL!wH+_{>!!6?f9W4r z#q%eg6HNR1G4Lu}-)~osKL`j$x6Kl;4I99+NIq*2+55S#aiTbtYdx1sz8h2}(}!R$ zjbpMnYr)I!XYMQ~Tf+_TV`;Pk-Dx$nQ!rhaNuaP()xJN&R~*4+L5I;`O-SuVE}RwX zo`U%wIv`^UCO&ub6A1kOIJ*+~D64Cql?<3T0}>4?I%?F>ppJy<41;w(GDE(R2?SAz zbs;V9RoCGuZ9L2h}eN|ppyIEVUfV7%`CJ~giDrm*6+8f3Nv_in@y#N2+ z@0%qc?R)$*bHBSU=bn4cx#yg_8nBUpFFa?ENVIk=?6b9j}> zV);QtC@1B!OHtX{-{nk?m+p&QDtZCy|gX%t5AXcn0s}$mA(&I1hKaaN`NgohaUm-=qbZrrztY z{PoO#l9_+Qzt8Vx{?DlrXq5f^K?MSMi7x2JEuUD(y$UkmrwMtE6nB0@eY4qN(0e;| z;|li@pLCc^^7BWKd5MQmIXgd-g>PKqD*|&BW$MqF+GeKmorGka9y{+!bOI0GBmEVo zHz1wx2b0ma{w@usaQc@`MkcXc!s&o0mc28O<9S2%^D`8Z^W=hcu9_$7Z`OW9fgdi& z*(}r}=6q(2r>y@0H7Uhn$=z~aJSQX4`{G_7_r=Ixxaa3| zM%JHR-`IIKqqaQI%6JQXGKPCS?g+!i74;$Rv9j4n6rbIr>O0P???kIU`NHk6{>IKb zPF6Sp6^>+u6{m$H-&DCZ-&yl9S5}Xw#>y^W^_R-(FK)nOOhbzJ zz?|ICX4SK;s;%dKhxOMOZ)eXS?+c=eFTbzWvQ+%3Tdw72jF9MU0Ij9Ur=sV^#en4X z;$gJFW&9;_av8ruPm&`Muvtw?;L2T3yu-y_Lp@)X`HW>U`;~8Yc;YnvkIo2tIN3tZ)6=y`9uNRIGeVaoDaOaX;ELe_kvJ`Pq=myCsmN1G1 zbbj!TWzWgnUc|%LkXh~3SnW+_dq<%gtY5WP5rOQE4cpkB!%T~o$W}G7LN?I}_QWWT zWj8{U55EZ9__0+SOCF>$$P1~=Q}{47?5#7VpU4(f_)!JC)nQKN6$W z2UK(eqM=&-4u2B&OZwt5DDT18|D?Vc%V5f+5<1>X`#FFP-V!(odpDg-9Aq6z5U2K= zFJQ+<>+xB@AX-Iyma~fiSA3ScDejQA_?GS^2W4E^5aV&U z3JqAX^UbhvU8B}lR9O~EL@M(m4)Y=14Px6}X+f2xk=l`ia}oPy#nE;^irT zc8KErU5iVdt3dqVimk|*=j&}9Q#Alfjs#wkTE;t&)W+y%{E~Hu+sG2cUQnKA$jaJi zo0(%<2DdQuxdXPuScz)IDUuN}jK}#g4j)K(gyAxkFuhq6nxRIq4-`B~LIeI&GvpT^ zm?4*Vlk~lEzvRXn?)b7$yZp`Ici4`J?<~qWT#0YexQ4c6F}wx-P_rD4i9m*@*NX** zOm7sI95TIGoO6hDQ@{BsD501%KyLd>dgb(eesj8uX`6DX9~}Eg4_5yjKpOB~0N%h0tpI#RXdvXNg@Zj9M zK;c|H;CqZ0gbRw%iD(D@a1FRPwT%FsK>WISScW9@vXC&RkpmwD!2xSUZ*2qome4iX z%zE)7k6iA^w}D$A)!BjrPPgzH^o-!ztK@$GcAj7IAjlxK88NQK%z7?alRUcN&Ia?J zkWi+_C3HxjiA$i7z!aCjB+g-eIZqPg#2+C+k|10Zh~GQ~+eIHPbFj`DW0gQJh$*4C zh{dKRngcVKD!+O;DHHVnPS!mgYfyd-5=0YdD|KK=Uq7b*6!Tca_M&`vssrf4trt$U z>4iKk1)`kztO&t|Kdx;PqbUwc%(RIHPzVSN>3E}D@8(MjBsw$;n{wa~f;;S`SV z8yUNC{0*Svd65#Fp*Qa%PI&GeM%X(Z#b4dHXB&ScllIRgSfs7Q<1X>%mnFi)NM&{o za5s`ctBjgRxG7s*Qk>`S_!x>q1DFlme>}!%fGB69ctC>BfapPiWxB!Y7PZqD30m{_ zyo2V%(Gt`jWmwWhtQqvtz4BLOxsG9+2x%`)3MFT78U13Md85n(JZst9Wo+T_w1;xx zq$kd3lDy%3`5qYLiT@3$nSAW5&+PFtI9Ti}b}U&hA6(jFpRq%S46JXZdk6yDWcl z=kJs2GxIk`l3jE*cwZXSw?slpz3V17L46?`1^i%&sbejey>d5hSsFe5h2^%E{Qz{0JRT~hhQ%`#Dff&b@#{S@!-Kf9Ud>> z&;(p@@z2;?qU=xj@ptSYG^WZoVSR^pQwKO@cXNu>&7U%}p>ByWkEQ4-=d;M_u~R0xUct9zKGDV#I&R2tGt8KQED=On87Y@I?88>7qU+5iK$G=;@q4hUOz*`yHi{ z9W0-d&RvGw#vGf{0Z;QtU!@Ua=Q8?^-RMEN!{mvTf8DWdLm_2s^R0Aenm zwK+EakX5V~=R@vm_I3y-x$Nz*O_94Fb~I)Bt?ZCpu~lRdP#x&@-S(w>7>>!q01K=( z!4io=2@3Ey2*O2vM@vAkehs_s_UD>eK@``IkIMWY{+;aI|6l?Yqj>I_9QZi1*Kx)t z%EurQejjZpERM#Qt?-2pu-HSYX_+jy+nYb&1l?1C?7H!)*d{srd(@_Uh_T`&*`ACF zCzi5aJ)u&br+nOyDdI5XYJZLlr95+0nub$I(~ zIj;~dHdt%xylkyeJP}Gy8LlVk zR78)%Uni(+?+q?ie}g+(ioc%d2>kU&urFoxsPgG5z`}K`D;*U_6p6W6nOM<@iOc~E za~-oeLY^2<#qM4L?jJq?>Op71Ug`lNsMu-+p@+j37nC6mk_zlZEE00;zaIt(V0B+` zE6jc%T~RMCs2z5mAEezeX1fit*59JmWju7or5lGYUAQv>!xGOIKLGF=#Sgbj(0p^r z`;@52a`RD6k{D)hvf-scteKYShiUIG{l#UvQ)lcBc7tq`{^Am6+=do(Xvs87`sFBg z3UBvt0rPljE4z7MsbcME=>G;M{(UfsBy20f1F1l}?(2SP|Sq~P$?3Wx6|DjQ$nx=k+QM#JA-Sl>|9tV#SSeOxg zc*z+;8Ht-ryZs~D$gM~(m%~yC(@gG==iFG)kTqvG=Wpf9CzE-c2=Y|(L;5z);|X#`~fyzM)B|QBpOgqdYwT^cHC#JbC%P+EW#{%-J8X_KziL{A9>nW4Y4y} zV{@bko+2Y8e?~OLCg(*ls`B@s6l&m7WECeIOM5H?ZWXH_Tf zgZJWbL-aj^s}p5K7;>uo7t4SyQ#DTD%g=9PL;?|VvjH_LUIkQ?o~EKg#%|3Jc+?dl zu`Tuv>GEwj-hWmaVn4r&Q(*6S0Dn!?p_rEQB@IPVCkTQCn#fRVFkS`po){*Qd5qV!ayi& z_`h3;tPXttID1W9;?65kNe=GEI%?g!Lv)Y5Pdd*t*4R6Kg7#Ab_Ky3^-*)*MX-y48 zn(+Tc_h7*@(+y>1*5%`1+&Xz8Lf6`#r1q>$g7$4@XmQLn2+aL0&-ci;u{b*CvZsnqpfI zIZ1hSGfci-jNAWgk-ACiXF6-r`byl~F1wnC6EC-mm!XT0A)jt`G2N%ud)cQP8FE&i z)ct1eeOvL4;r+?rfapX;kCrSrEf)*??L=<$va?UdS&AJ5U~uM(e(?JySaS24XQ&g? zZiGs`({0h~pG8x5_|~=1VeQ$ofgS-mevnH34=^0@N+mQjt2P_Gy`mu1<6C2|U6YuU zZ*0(e-z^Z|g$y!ws|{;36geV512En%4M+!64iwal!^EAGQ^tMq)>1+~r(eFEZOOz< zc_XMTFvz_QkV4*H5j%&B?QnyNSC*yJVwHe6&wCTJdOol(9S@;j*!Us@H5J&Kj_`Y) z_yGzSk5Y|2QW#tBgfV!jB6#c_yfZMjwC;N)a-ul%dyx0|KA-^S611vz@(0+y6k?A? zBJ9LrA1dQn0!MiH{(Y2vt`+YApQwr>nr{4F;uY8$;yk+;jXe4!+e(zZjNGa68TWAf zuZZV3eh`-k-eI=#8$^-jOSdsvJR)>Yo`UsTAQqJ~+EO0tKk_E3u&=IKZ@hiR4IFbmCLehJo5k%w}aGE3Gld)Ns4 zhYoR=*Co_2U#Bct4f7GRl@|~thiSEOwD<#}{GQ*FwUE+0*{gO$dY||i;ueEJN{_)A zHu^&F$+-`*j|0ktQ3j^dG*k`x{tNb&pYVW!Zf+N`=a2&9;5Rey*a@)&d-C|{KFq6|To7F&sZ4}G0lAGi?biKMpauQ4)@q4(OCC*AW>&}qfP1YYKB)_vFo)>n8J_0b z;j2t+nU98Y&ZrC)lEoO(lj^{4VD(9)Q6{_!VmNryn7I@eq7(m)^Q<0SzzPpx zdk?{H7RY2wseI=b^1|hv^z|M&(vT4wWAA;LDt|!6Y4!(kuToa>UjQ@XHBp5Qg9q?; zi%6N78G7)@No4>m@OtqIa`5+8_=R*lTC$R_@g~p4{I9uIt`!f{;>YtNCDI}wc~tKp z*CSFEyWIwQekB=0^T^Kl#k1t4zRl*q>y2A%zD1=AuzVxM`0Lr`#{Qa8Imq633;Szq zGT2Y@E>FBLZ+|L&Ybo9l=)|ur0%(AA<1jw`0Pi~DnqxU}6my%h&cR2q$db;kt2On6+gWlnyB9FL96?2JeRlr6<9_j*c zV($P&eHcGSs87^r`T)(~2j0O>US=uuSu_nWitH!6Q0xPiL>)k+M423IJ}Kh*RDDGN z$P#hJfSy0S0{oi|6kAbWO=&(k_kwtdX7pyJ;pG6zS7iCU4^^O*&X+))aIv#0MU@)s zoC(-`Gg279gAMYIM$%lIL!bcfrH;rHjNd`46*t)wH|oH_I|grcr%)daxiE6TG$sOn z)i<6W!CZ_^UFc9NB=x=0cRrb8L*Djap+=$(<)O&*@FkAKv&(xwLd;>cI`FY*-H3leBpj5KhDs1m~K{bno9$bYW{PfTqSSJ~2@AxHyna)keahC4j_}mUgs`q$X z{R?iY21IgfQ=l$=zL~w*c#Yi_SX$T*Sp7Rz^{=#~b&TJ0}JMGxHMi&mYKunr#!#h*40!7HhD%ony_F~BD7&Rtx?>bPVd+J7;;{}Cb3h7KL%xb z@b@WZ)`7@}dNj_{3y;aXPjNn0@NW1ZFq6)eN%DP)XEL!Tr~*fGrzd99TLTRpPg;ou501id8So59Cfk@+vPwo13dCU)?iTGB|&GCbWOo;zfE zg?Kcbu5e^7bt7dVxYv4Noa$~^b$4ck7^6A~(#~dvF#dZB^Y*Nj9#uDr@@t5o`I3g| z=sCM@^&nDg;>2{@!~4#3hbTB?dWCpTULdP`PPq<=;_2)tP%nJL8lh&f>}q*RG>W@a zpkCar0)7!y0o+NcK!vzQ1vu?kzl-se#iQctx)&&5zc}q}I+>c|z>PtL*aiQE_;B)S zLD-Q3odh=`a6vZ1ABbP+5&L(@>^dUq#mD&QpEgf!v!xyU!9Tfs&9lzB-%k|ZS;2wq z!vdH?dGRU^WoD!JvkKIUr&Pc%eysv7@joh1As$fyo46N&^pe~uW39(ctUGEK(bo2U zt&N@Wu3C=2{Y%KZ@ZW-o)q5Z_CC4Y-slvZBcMnK@A|AazD08JyC*1T%r;#n2s#9L+ zE7C0c_X02|Z20t!JU zOa-ZzBLo>ZLYk?L6n~mac-W{bs>&p{#C3?xCa%g9WrfSX{&Lb)aFA@QH|pKu7ho39 z&l9U(g5+fZsFEYvr;N@spdFd&^-Z_6?0(YJ>{vUoC;0_-7j-SeMsL6yat9N$U>DQ7 z0OwN3=Gb0M$=Yt1~*a7H+(f<`8+lpxV!gKTZCQE7Od+0Bc(N6i78Xe-SYh`IKsr0`qjiWVB#9?P56NkCq{xXzjm!Z?f z7TqY?l##b(h%}&=K)2lK+A4xvu%jnh;+^G>o}N6Ry2q<|794>Ec4OTRH^)=!X@P{i zx3j0M1jG_ApWC08m?C--3e>tdHc*||PbhSTEM)W5x@4i`c$Ecp&UB{hOebU4jgs8S z;A8Cc0AvJCbSN^yhzt}Z3ynzw?A}OY0}>jHNnH%wrO?vY0n%xTr!q!iM@F#|FTmG#NkF8YlrPZC(%1eRGY?_>r1Yl?iDyJfct{Py?~DUB7ssZ1nP9oJc>vB$Zc`o;n)^rfk$B&!Okr~duUA{zJzBmkNY(3Y%E!hDDIik zO@I-y=3p576%Zp4pZJ|7zR@m{zRnv6d45hxm>L<|yXBSx*p|q31l)elgRf#^ktbRF zJmoq7xL9wqsJRD0D2*sT%-_NnmiSCO_#7Z$%kvg)voXP=jp=Qan$@%cCp7Evu>&DY zS19#TbQ|#6xw6DYl;Bxtl`sfoEEHn^iS7K!D!P%rJqQJ1W45af_jea{8~o&T9GYni z8x_f-uyL7=h!J5tH-ZaG8Ep%jl^L^}Lq-8TAh^QdAf0v4-#5mDjCI&m@ffegk{5Ny zWS!W9c0IrfH4ajt=Ha35xLoD zow4o$W9+8bEgtM133$e(jZ@pF9F`YZg!9cm%{aD8QYUUno!}rDmqnepGt|j+lRCjh z;`tcxR^(DAawDawlhb7$9ws59eL1>w0YDpli7oQvN7{q&W!4_Ri-?Kx)n2u8Jb-JD z+&Oa&!_Em9@h4Ff&=&L!EP!jiIN=Q&#Jh}Z4V`O+?1R4Emci8wna;pyBcq88c+W;4Un0@qr@`|q=GQC1P^Udkvmc^N~n*6Uzvc2MU+~cHqr)d{)ZE|=go&lDP z0?z?c6LY&%I=>3+-&K&q{R^fnn(yUk$;6()KVcRUTFO@gdnA1R0-u`k5mFai9Wwq2 z-rpns0q^($<1)XNr~%H$Qr38;#-}_IHmcyDd78KK%mt7XPuIosDqp?ISC4%9)cAx) zG_aSxDQC94i9Sp%oN}ggGg;)j5s&@Bpae3p!U5mD2#M$k;rO+ct^oeH`Dc>py@vAi zxlm5(mQ;^R%$yDP2o%QVs-kD+pK|eYzHPxq7OjCp9M|fkF}p%zDWNO~CqfmWcz!q@ zawfq#Y84niL~NRqzs98wz?lnoS+2X_^J}i#mo#|LG8(4_i zE~kWyBFHFvU?0T8XQnF@FTxRB>$)HTiqNR_gyLiKlAtweT_N~!0A1{Z{ahijUsWxzpzTv=4lCketg zG-7EQCn4;?FW{*$fHWQdgDwjhe>3jkw&FRzlmXpX%KCM<88d=oKAWU&p;G958ld2U51Sr4h|+iV1zM&9h1vfA(OBwM*riHsXj(*9si zSB;)k#qbi%2Q3lGDuLBVOEa@^4ds~|+N`~LAg*Hi6X|pl7Kgn_gEvh9lkm|?YNI*O zeIH4rV4^M+ocwC!uz*+dN9VxjL-J(&33#vfNG8`!rY?&K0+ZXT`CeRnhHf0YCWPzj zaj-%I%9m3N8E**px8@{=;>T8kSK0vDPw`6m(Ni?~$G_P{I=B|xQcn@NK{ttt9-D8j zi>&eXBp7Fv3tVL_L=|wwk()#Fy>*+(GEw!^_?-DTiV*>$Xjig0U<7s*@v5PjEY6YR zkVT-=N_uW}QA*EW96SpZcpu{tzYzSAvPQ8j0-SP>iZPIP!gF2-h0DTm5Pb6h2J;-r z!aCzE<6f2`5K0>=P)o$+^mV7|TkxbAg}3c?g}pc9@y*{cgZC_Z=g$yW<-{NHD7UKBfl^$&ElF;DGjbE9sMAPY)YQSCR!YSy11s0r29TTbKV+t1})1Z@S z!0iy21`1X{k2ur7cqxDp)~^y6F9E0#7%6x;2r$-o2rX+Vahafiar-b}bY>Gx@#6uD z-^V>%z$2Li!s70Lce;c{D9WW_5w{P3#a0Q6pA!~UF2G`q35z*7G~aFu7AK*0@GuyV zpP4Zd@bL2`4?kgm;~Htaf`kTRLKgza<~I2q0LTEnsd+%og?y5Up9$O}nRp0T%RL{G ziRW5yNYPPp6$W2~22Qu+bHG}Z8gm?R^=NxiJ%wd{Aq|qS8u;gifQbSe|HKp=NZUJ4 z1mN@Rb^!{3$M85QFRfIgIBCmc!fF(c9OEi$3$r1=oB5??TYxtp4@hMX5`BkbU5%mY zj{(W@zQ;{n4xnFdv}J4n*HSWq@>3b%WQ2(=jQ+uYvPUu>*iT&Ry5+6c|GT=MqdNZz z)WnHKLzo6Bxn&D5_Le^dP>sp-!9V${=xI!FVp#1MCN6+KhW^RXS$Wjlhs>@zy-!Kh zaWQCQNy)tuu~5hB7f95h6Lsiu(7M>$5_N=uIt&YS#27G{Scy7heMB8xY{lH_#$rO$ zlp$Rr5$Oe#Zq_@gR<(-jCvHGvI8WKwacBfcAaJY9yM7KY8A1QL0Rk8oi8 zS@3d6_({Wtff&j{-SoowtUUg;D zR!*_wdlnu!*m>s?&mA(|Egns$OB@rHm&j@5ZTSGtN=`mJ%q_>S5@qZU8}A0Gb=@J| zYX)FOe$5ZKvbyoP=6fUJAEOWX*5LLrUJ=3sLhc7~+4|Ilcx`hFkGK8k!aOKZ!E3Ax@FF}uWq^5cPAMRD zATO^JDs}sv-^1yVJc0YePy7x9qwonjP%n1yiLJqtipgGl7BK!Sha2?mw=d;2cNp%- zs19$9<{Wc0ud311;ZjYG=4L5D6Zz(7%Knd|X|P6v0Hko=FdEIb*M4Wqp$0Qr4(1XN z@O8${u!+wD`0c`2_x|DhoMysB8~-duo4V#(v)~fUtxi6KGisD#qQ>b71vlc9 zX8Pz>ih3a&qN%lfcY^hDj@t1XieLUB1cd!IJ+&daIz5$;Wcv0m2%&1t*sF$TRSil( zY`B^AUS8Q|Rf=F(k|yzE<+VO*8_23nGxi6)56Ep`UmC^QglIwWTN&Z|%HHulyJGGS zobn@r9)+1B*K$G#%#yNSM-tssR8TOE6Kwap>I74s1tlD=i$MQC$-nW9S+aLNj23i5 z<>BUyD1ah#UM~UVfCjK-P4SFdUHl;k}037Nc_|?6N6jBkZEM zx<=G|>e(9mo%w`|?`3z(g7XlhKTiiBdkH}x{u>4qu>SpRLjGx>3DVVP7g=HzY0DJ( zgDkQ~&Y_xdt-GVL9rK}0UIX`cm=vO=x`gITmZrZ9&BJ6olQPSbED9xNIYY)sj^=a< zGEs(Ff5f;h>39UffjYxhXCVK@ctYbs+&m=RA&3Sr_UznX^ep*j7)C^pf5MF-o??-A z70)~J@|?c6wdc{DJk1_}%kZ}&#j!8*qbJF&3sbgb|B!bBM<7MsWQ@Y^2+h}zn*q)E zH0b@aM7;|dLdM<<-Yy04;SKq4OQulw#>h!(Ny70v9JEqhgl^6R-r7hn$XqKVRHzp$ zG~aJ!l`T(Ny)*lDB9~}%jk!cN2GkO1JGewkIRxWv_K3)T!KEPsxbnC1xXD2JtK6~T zM{fcn?ZPOcW5@yZL!P2uhiQ2=c>x@-_}D?@uG!zyM# z7nr?K{03jRHCgfk55wVFvFhZ_K9?KCEhvzYM-8X{>&15tneG=OZaCDr?iU|ke~9!% zJ9;UepMdiPf{QC8`;jG+OVl-spX4Uy@+ZoVT`aeW?D6?UQ#z3}I&>nkbY5|ry!S)Q z9vX)tf3E!k{A%^LQzl$Y9w|}0je8gts|oW#Z9%cP`(7e&9_}`c{;BiuTD4I*f)OKvpi%hD z3ZwPJk7X6twBl0Z6V3NoWJ-t{Lw%`G>|+2t4(0j9ke}=%MN@}B7De-lI2W=`^by6P z;AYTtAU+%F-|sCrPlF^6e84))-zpp05q~{jlEBDK3ZOU~myGipK-BVHhfR^@<(bfj zPCd@P$xeO9PMs6#{k!VaN2*g3ioU5&KSe`kpEjuO>}PjyK&=TOM@7HK`=X+)PD+jC(y*4~RBr+$@Qjl7NFuHQ>k{q_i%?UzOEtEbCa6 zZw>iwcG>S-hy^lc`qSer8VGnE=4#o=~Y+T6LSy%N1Aj1t2mWz-w)fn4ONC{LGEz9(JlhtdG z7v}O;_Dna9J@bGj`Sx&bzbTMQo@0)7hEdsb(BU4u9F;O(EDFV5wD}goyAYM^<>jB} z;3r0oOejIX6ZUI!(0UfmRXF|AYtrf2@=KRRkW%?6);py21vy)T;x|x{qrMeEazS_MGR$qrox!NF6qLlBT!bz)sZ4sqhV!o1FuJuoEa)aH`~S~lZO z`xN_%{xa$LBxRq`KllY&v9I8pG9~T7&NnstjJ1RBvgO3|p=XP5$cVY`Q)$cIx!#JU ziJb_Uv428rz__3N0lr>M8VdLSba!L+Jl~M#6)qA+Yj`uH6wgaVO3vf)6PgezCtIU3Jzp2MaHF=Ky13z)RdYeUZ6Dag7hE=ttqQJ)-J zWu`~m;U!Nx6TUrEz?Q>3;vO~ohION;M=t1@{FX|ymTK%2#O2aEyjz`EnL~`^&>IH) zP#$@e1=wjjUGa;naYnAhbciQCut?gXgA?g!$G0A@N5|{kUG-D&Qy;Hyh}U0^P-DD) zUc7!jLO6b)ei(;lI^Lc66ju;&#x%$4+v4?$_4qQ@5?|IO|B^xMT1G^L1n$E4vgPvc z&r}paBfg9$SbW*j_)GE78e_ICaY25Z+|*%uyl68in9J7k>M2pY;Y(;Y!zW=N4!x7O zb}Yg;|25p$Xt-9>-1r3{uT4Q%$8SCUzX;(5{6dzMsvrBQMGsheFJf zUN$IB#vLqFL42VCy7)*1+~R!|aEdon z02hMPe|{NXgg5J724_kiuJW!sqd+cpZyiRMXXS@Kyd}L75C5JHtQ1k2#t^k#6LxVM4o16#rV+r|gm z-T1xfaiNT+1liEL<@J2|U0#DgmN`DRO+O%b}iV*|5V((iBr@DP+{iG1?t zFMtKgfoLW|Kv7brv>01nD_FcWNe?;54E8YA4!*!1p{x@Gjp@c@D;e2csB|(-Q@~9s zDVQY)&z58zq{_ZQX=nWm)Uu?3lPMOHZ791A*(+0)4hKy3&T90b^J9BQ)qgNMuW!np zohYuA4;Yn_dnYk%(8)u!FJ(_4B!g$kNu*>DGk7abASp=$wUIRoaHDB}w3G7?y@*3e zi=lA__`Yg+LJg>yKi0e}>7K0S-%QflLUc69CFZ;%z5i$7kDU;EPU^9#tV zQ*H4{C9JLykpOm@mT=e20M<)e3(dsy?&keC>n)et;S#3>0@xn8);&td;gij^Je-6w zu$i9OZka9;FpRz=T;*a+(15^YGts7{%%GO&fOTjSOD{dtJU5D_bb4kUoj`?R5Z={8 z*CD!c7H_ z+Z|#V(v3Q)+!1U;imBX@SdKQu@w^L|F*dG)-1;`}qCk0JdjqOALfDTJP;O`wL%|)8 zOE+!@4JI>Td6kj|lp?dXV*FNjcV{~)?~?kISY2fyVcLo)U8UNgEM27^p2agZqpKu! znjlmm;f*PfHiNt#si#^Fz!Y}|07h`zmf;{!L9XFohGzTYYp5F~-0{MAP0>nptDTyf z(AmWGF_li@H5Gj@_!Ms;V+T}0Mn5y z;b5I=gL2ZKbdqPZ*pefgyhqzR32gQihx@yeut{z@Z(QEOqNZ4{vnjT(Ia=cX6Ko{< z3QW`84dNasO94jJg@A9Dy(5m&{>e%E3R|S430Bt?$y@LfMX!%fE^P8P6hv=8O(#bk zAij8FXcdrA#7mNw@~4&Xr%%4y1a(R!TI98;E^c zKh90eP@?>jt3k~{l-g9yWsUfa+M;+#Uo9PO90@iD%Dp(Ogag7QIK7z(ky<|;Y|6;m zabKq5L1bi(uOXL&dBSW|x$!DhMW>3OhBh-nb051qxCY6 zCMN6gT3o!=I{Ioy=BEv8{KN2G?%8tBQGi_l3H@^_J-<$v7Cb46Y!_40Mf%OS_2QZAA}{xAF3fDIUz?zQ+(jtb zr?urvy9}ido3YZz3P{zt{is~)%cogx?FXDIyZGfqIsLf5*~-`MauKSNY*#ZZ=M_4dMlvKib+? zQ=Ff?2^BvjGj0@f&zAkT2z;fvF82BacI zMt^dwxujqga5FxFvJFLb9B?1*66?x`uVH2#o*mE8wH*~OdUT`EcpaL~I1HgYdeh7B zs(}^FEYDF`GIK@K=YW0OyHr0T6CUg{fL@?^Xy|BKZ!b<82=Ix>?`7I zIfm#q%fcPzRsA&;oD8f3Zoi!UH7Auw6!Wx2s~`j5*&_VV)D9=6xwQVgk(d!3;u0DE+pz$kZG-QU znopG9@*bd8#&1H!{>lB38eAJBrUE0gl4?{!hKMy#UD27Y=D%b$zWzw@;6tbi2cI)O zZG!RkV3(>m7`G~3XjXidtQd+UtoRKG|Is@)ddGd7uJ&-%yBbu&LlxxGi#!|L+)3Yu zppHEpiF;w^pi{P^6d_cdKRJRUQzvC&?4^wKi(ZIYmigYcchdb92Hc6;RyC97n*C~l z6T~)x;?XG28!{&Q#UrPpAjZS4XoMu2h3c5_2 z#4V>KEHjF&lM@4G2m|ZnRK)238YwZt?7B3l0y2 zUJ*~# zz-RrubNGZ4h-Ht-)#AX#N=iy_v%HwR+(!nEc=`Y%I1c8nH5l z&AGM?u1)ZkM5Hhj9}S}p^pjL$h0>3fdbKbfdI?_hr18_fNQp@eptdV2sPkQiO;%Syi5$fXkSsXSY=Bg3GZJl@(ZfyHtXF{)dKG>o z1NPcp_MfTPMPQsN0JP2Uli|?w>p=4EfubTJ;uLjeNnT-h*r(ZG57%ALrQs^%B}Bjz zOK-Sv9U9jX_4zg3MYgG3f&7uAT4O`Si$7^!H1ge$p@H7YZ>?`%WGiUBfbL!*_7Ha@ z|Kah|p4M-H_Z$_#Wzd<`FUhx>w!Rba^+cR2p+lLc!ea~9#r7Sy;9U*HVE_k0sGxdF zH_U5Zj_ogO86iz3VQvHD@Z{I~Qub#$96dWmtXD@l9$S zWBUto%0|qxFZ>5(_1ONhmebn3@3!6q@mKM|#s#TH54`5Z_S;)W$Z@T0UsRSCJx}Hx zu}3?+J$kk=D<2w|kqPOM`!+?6HwTX3*ihoJm)Du_Z zy7NJl+NR`7mz0NL2f$ccT!`(7f0C|(L$N}3WutneTCY;N_q)9`g z47Yut510%NlKx*V_-0;O`Dt*7Bhz^@=cTj#7F#|P(j~End&_+bOfjMqE6HB18L6!- z1~8C}Q7}t#?^lRvztxuSw?FVAj2d~F!E>Lcg=!QP%oSrfnQ$)7)vOAl^ARm~PRs`( zC#1r!h3&z$;*)a<=tSSx6**Owi}iX`oBZnBV-M^~H{k5t6CD9b-X0KeeMyj`M_@Um zEB2XiyF0554ux>UQFRm)*f84)xp?2;Q{+oq+Na3Zui=KMCyG6EeDZw2w`+CUr^x#< zN%|C-nu&cy6*&6;>r;gGe|Wee_EDoATja*nkpr@KT#kXXFLL7it?z+m4F2}{d!~jH z#W%l6D#70quIhmmtJYoQ0KZ!h{ajC!g^|=(5Z0>zhikfvM$3eM;`IyI+sldB`i_Q; zel&p`rr+A$9(erEd+O@w7!42EJatoYocJD?6+Tyg0uzA`pvTiIMm=>ST>FTNnU-kC z@2b%H3iIsG%sgSG_n!AXd*3zQ*qlrhy;(J|@)hioz3&_Qdp|8m6g^+HEw-;Pa(XB+ z8|;4ZuP?$=7k!13`wHfR+s^5`*ngyK7X1GNGk4oJ=cb;6${$M=L{2jc%uE+(9$r28 zs~?~MsUjz-BE3hR-i`FdC}j+OVAPnv!%W+ofU2GRb0XZaEoNX|KLqf#h4@E^^`WMp)o?prg(lQd< zuF8iRhzlSF>8pjAWLiLrO@ovYE&M28Z8syfa|*Kvxfj z9!yAw)Y(Az7a3zp5^<~C@0MkUi4}AF9>u@So~Gx*0hzUR2vTs979rzdmkOW~^I!J4 zhdW;m3)tOMKtrC~^f8A>r>#~&jdgCSy&s}2Tu*HOkxiu2obvC9R2g$A7lX0=6Cy3K z{k0H<_Lb`&{2nfO*=dfg3RkOwn6Jv8SJ^Jev4|%E2&9n3FV@PhapXI$-bR{DAd8bN0fCd%rl1~ zPnz!a)3B}$M?-PX7NT|N9NsDo>)xg>h;|G(1Z`p&wY!keKJA$T)}X!C#+B|%^-XxY5~SO(|a z^z#o|;ag{anF@LqS89=4LukZ}#-r6axp{M}daUk{t!ZLEq)W@eQ4rCHi#)v35uD9) z%<}3==1}Ef0Xq0HC@-xmQmJuJ4g52&4*SQWbxSv>;mQWl0Azr=F-kY-pGdhxJKr$hks!8ySVZMJwDT=$L9d%H;OVhr0N}V zcVXKERjXs!DzB5$(}@9QpGf8`W$hElUU|R`z6VJhh%XPZ?SnnBo-!_*yc?o86?m!( z3(BQ#7SpN<^30~883_9UtN8~%`+qlo4Voun@}bp;lQsz*zej_C?yor~C$M4Q06aZ3 z_)A0wyV1>brxNP+Ph1EG0#GE4jyd_Q!;MiU$$X80r?sdQubNpHaSiM?|U<}g{B=_cGft@_Op$xsj~#l^&vFez!W`%)xZ^G zq;eYn)bfuWMD6})$#W$bMeH)BAq~uD(G&3aH92{J)jw7N#Y3I7m&4 ze25TLHSYgkHKynNB`@@#Lm7Qd-soihW#||BHMn}RMW0wXveFans7IWinj@j3ILJyc6KoZlo{=M-YbARaK2W8EbOZ-C9}-Z-rGp2fuGk7xiUALo5!HWW%9q~x_wuFw0z%orWlc7-o)`Xu zdQ2W8b2+JKsPHu!T;KHQglqK7w4?Ju%jl}CZ+ z>B(0S_noL$#%VI{=wvVAywQ_ooLk0?O8yS_q(S_zH9dPa#V!tUvkO$9ttqvpsRr^> zJVA-gYQFd7IYA1lH<6q9=Sil}+9k$0c*%*S^JhQmgE`G!U)+W6-)F9GhW8!9!FT}g zdn{;<5eq8u*R~MOcV_9_2~N@X2XyRkJu%Cpz8`(kq(sT-{^0lKhq(EPY_e3r2>9&O zI?&t0x$KFtou@=xv7N<{^4R{9=#blL>{2#%JUMI#Sl~Fx7?b?anB@ecA@6LQ!iEg8 zA8h@YbnCl+m(_Yk#@?^#fDjxgn#XiJt1ii|S>!bD;69B=S3tN%17q^Bx`dkq^@)S} zaQ)@sQpUcezsAXGt9z_xbYf|zi}X_F#L*UqOiIcSrA#o3{-fXr9ZG-l4(kmh)fr#Vp@ctd{H(GE9S9jkPz;_0+pU*D^TF)` zbgz+(dEjd83(fZxNIsA`MU`-IMVik;nh$v;&1X7k@R%MBOq#ESUGRNHnh&+eUd=@m zDT&{u@ z2<~iU)*2_AD7f)Ih?5)rB<9{C_ghbY3RyIyA_BLTqCuQr4bp)GiNNb!X(I3|Dkvl6 zs|?9vTLvyrKF_&PRO40%^A*6np}B?pAE38U*b$HO8aw-(e^2gM2+*RmPQzKP`4&2( zm-D=qsSpB7^oaE*;VLK9ToIms|CXZ4Zo=prq^#Z85Hi3V;chDo^E&i-;~7?;tIZ12 zH0XgOnpL573byf###FfoqE2>E`r|}*K_=3amYRgY7_O@qM;;>Gz*+a^)E9Y)Q3Ouiwtg0 zo7?LLvY2>TMn34XTLPf74r*~DwL~y~uz>6})GP7;to+9}W@wDWZ%ER1{k*C}UqVOZ4M^mN(hF5~Iqmn|RzW z&Urk24O=@>2F)@V0s?Bjm!dsfGgkU-czs5e`}&vZa`ta1C#5li$FZv*>g9Rn78gLq zwoFyA8dWqsd#0ou-QtVm_opE4hDr{l3iFu7EjnPCvW&zrdOH@926A1?j+Qk@$pcTw zX3hXNxl{a{Xb+j%L~5MObhqSa#BP4A26n{T414fA1+1j1KvAO%5Dm5(%IN>XgvVbj z$`d^}W*~5xq&UlfH_SnHl@RB1 z@V6sm&Pb~jY!LIT;5>1K6?BUWRq%jCSBrNQdjc^M_^9O=o?3uU=VDR9WEjnL@{6$* zvc*km@;As8LRb9a^AR%PZdqJ>fG--&HyUfhRogiA4RGWoB}iSUUYvn~fv%?|&n7Q< zc@DSYp;kQN3ERB6n4>$f+DYDO%Sgc0Jh1|$*ux6$8sOl$r%|;5Abw?fZswz5H8)qQ z4iQ@BSiuGnwu1A7W(D2i92GoZZm=PAuwZ=uN4A-NC4hT|OFVnl*H8>~)8um+5Y{yC z@v!-X0EEnU4$8J+3&6E_90imC>HWp*;5JH+kI>`5;3Yc;p<8s*?VdX-EOjxP4f(>Gd6`Uu|wt{YPiV7ZV zK0H7zTztRigg6MJ1KX;AVLV>$>*%p}_&Eo_b?g)Wacv}?BXUyFL=%45SuDG~V?1(F zpy^>reHHgd#7LaE|-GzC<3HisuG7#|k!xDl0foIIW;t zl&j#u0OC|RTy}R?0bXsp-I*V43U=(t!dKf6ubSL|kO_lptzd&#X$9wr-&sMo_=yT0 z(4@jwe=tEsVv~i)sv5Y-D(@6mTfqh~#|q99VJqksnhGA!0QiI1xqvuq&TUpf+4%l^ ztMOe3nWOu_3O0y0tl&Jc(F(f73o3X(oG13?nFDu=k5ur0s^zM|BSQ}c zm;Fjk)1Bhz zL#DTh{W}lUzc%spKdraN4$}X`a?Fibky}Of_@V!nn}|xX(i6lG;?`5YasK?`JO94C zFH`;?daZRvuh<7XFHafVH>z{XX{;Uo?j*;T>q{b0{Dwu-n|F+dxO47>dpL+;%sc1a zr_*=Njz_ZZoFA77hrV-$3@vlrtXy^4p&-RG7;G#Y`9qs5;)&}Ha2?NraK>#4Ld#@` z9>%6uz^EZUy%Hf;e7ZY6eG*jvJmM}pU_J>+>vpA^d(gL}c6{~C@St>wD#VG9@@HT+ zvz{v5C4M2SIx_Rc3v=-;tkfzeVdi``)7gU|Mer~JjRm1MVBRm`aI3{AXt@bd7Y!`P z$HJ~d(i+oj2|Yhf11CIZeQYoLp@duq$R1(1g!i?|*pdOK#zhR^F=KHJPA&SR`21h{ z(t<~>efC|PDT^`9Fy9FqgFzSz`ChkVF5>{Wb#adpFtnf|DbeVUIL%A0LbWWoo`R&} zR#@iV3ai{(Cs9%%FT7S_5{6xP;U6;gD$GCpspZWm3<^&y3E8nFX zXNP=yd4F!*K7xA&D;{)8zY}0hLE^yJ2eghyz!fC#g0iy1(2q?e@2@lZSvBk~H?r}{ zfN#3H6;2sng4oNY8)FB;@u`k*;Z)fCg53zmZ>fOa+5kA)%H&^R`%=MMJPfQORo9%OIcH+*kWcDxs2dbyTL4sC-R8j!L*dLlK)#Ptx} z0~VlHljX;Iz1Rsks3n0098kAUz8r=(Fn#r~9D@vxIxE!A!p<}_H>{Uov>cy``xZ?n z`;18<$I#5Z#fgG!M42>$=2wr;PCcd|ulNDS5?nn|ATr=T|%jgt3$Kv?iECW*z4OvbfRKFm9`{OCLIwtV*5t5e3G=on5wWR zWL(q(y^<|aC~o7qRoqgEMZ38@5#rWd3~D}h2cAJ&`Zy-QsIhr#N~2iF_2LOoG6o*( zG7jr28Z|k!27mM5`bLkPe?s&I6v>ZX8?Zl9TM7r8P`Ze{mk)8qW&thP(&RC*bp_B~ z^RKvZ^I)oX>w93}MlTrAyK|I(1vE+53-UQq|7Oy#HYcasgdgQmgQZ-r*!hLgak9N5 z?9a?8g`)=4Fbg%vXGxGeDr%R>BbgjqGbXviJKG$?PS7E04DbVt0bG_I2Hf)6>`SR_ zA5L6Xs@b2JREfbGpY(oQl%9d#qI3Y>d%{LxB6zO-nN6{;MnMzZ{!FiZ>ZaI-_L{D- z`Pig2eMR~3T{OKk04D-!?pm@+PLn0S9+jA3mef)&hEgx&6h^^-4;{L*6b7ArKuKIt99UH2}v-U$5 zM9z#2&9HZ_Lz?b;&ECFQd;y~bYbS#1dzslrbrUDf% zirT0H#1CJu43AU^h4N#ROhEth*&*4PBiWhHP$QDo)w;!DB#8IK9znF921n%&{Rz8TCJlguP$}&)Wqvy4 z38Z*&1Vw-dX>uYvIQUSS%3cb2WHN-gjRY{~HkID_Fa#!(7}cl#^+=Rx?CGOks`W2>?p)$gdX~Z4!44(S! z0_jU6U0>QrG=Y3L=xyR6FZ0p>&8$PVpOJ%;ypyl2!zUtMl-eb>dJ17;uX~S=9t$6( z`FWxMjp#l+)KLPD7tPr20fw0k0*@N!t#sifD{6(Tzhl(^~kFkF5 z4rnqgm&wW4hlODy2^-tOJ$!5fN1#tcb>G%#0EAFJDo7gZb>A+t0Q6m2OMIUPj6UE- z<8Eo>1ipoKsj5EQ$y6Yo9{>{E`PmJa+ubcY6RLVeuY$E&pfW%R^6(a-9!(kR?GN-q z_)d(Khd7Xiki?VUfqW45viPbG7ruhaRFraxtr#DC2u;=t=Y#mfA872MH*$z~1u(gg zChO4>{?tcKM9$;P4pB@tcnp?5HdGjyg1fe#{~mA%ZQ{?PXTl2j^e|o|??D%$PWuXI z5Z-uFVV*JbSU86Y7j6$1&UM!Lc0dzRa%b!*R;*he_Z0+ zJs|1cMDN^|rQZ{*jWTms`nB3S>D)$7G#4~^kBiQ=uP8y^JhaU|Ha2t^^#9~L#kV(k z=i2z5K!+67Cr_hbk3tOEBZ!_i`S^6SEa`kQ-^x+at+MXf##epfoeda6^4xF&mj}Es z>ml00RsD9nji}?pU4w?x1anfMHwzpw`-sEEVTI$skJFd z=ceB)r@qLUZ(2AjO_xcZGD|*MiRLNbdH9PRj>LMs7L8DW%2Y29EYwj2bhNy8V zb+{--tw3GriXCWA+?1(UgI*tN#Y<_j)(B6~tK;&7r5YPJ9aBF~H;gtE&QSzhtzw2{ zYd;_xV**|BpoYv0FN}jqa70@*T$$d6kAU~jvX{}TkjaC*Aj!MrkCInwD<8oe2>(wC zp&0hxQnylzyKV(7eRz5Z zpn|KW%PZ$eElCYVrh#p=lam>WS<~R(055X93lX56sc!F_k092DC#lN14q5ehjD18r zyUb1QMij@1s6Q|&-9N;0$cQJM6g2)3#wK&u`8FWbZbpZa1Db4YSM4W_l$)$+v_-L&GlX}1d<1O$mK`> zmh_W?iJu@c0}{@C`%*X*^w`(-+n0mLVc+;7ov}{ueYcp7C-6Kv-I8^-5}?MP%O#98 zB8(NZcU*~pX1rbpMNJMPL}nhhv6v%_$lk#R5+VKy^}bV#F@$2mNq_QwI2Lt7rRBxa zI^!R8-o zw%!h(da^5c))akC;cgf$hXCQ4eMONwoUu=5)6j{upXh zi~U^E%T0|pV6e}XfJBTyxH{$US}obeuGJ)?~^E1g;k?pE3zD#xNR@Abtmv9^_>hKN3FTs|mhRdX%=o%F*en=iK{w z=)%$xq$WJNf%Jsk@&V?`oP)5`n+#Qi@sJ(WzK%g$sA@p>?Y7tMhRq}6O4vLeF=d`I zy>O-r0sEcwUrP1a%b~DsJZ!UZ>vyw@^aAbCTkBNHRCvm|3c@cqg@m~xL|qt6 z?QUa-UN{F{SfQe}QEd7l);@o(qptJsks>WV2|lJwP)$NXP@>9^aVN(T_62Metz}_E zg0giF7E45eH>@EB9UQ_UV9(TNc9`NX$QzK3!Z@hM^HU4PEj=E}h1ElAOFrVE!Ue78 zg2FX0mXZj{G|#BO2?{<9p3L!AD(OwO1jb(hl?6Pja#_G~#0=vB>AMYw2yQ8!mh^=b z@X~V=Y${;8VMSAD>t`AWHT)t`(GCxx8K${|#-~BwUf7AlqXPU+zrthK4eEZlT?cI% zvg|r8A;$IXRdyZF<^Z_WAPRM8z@*Bs!-F{xw|@Xy6N{~kv7DdGlu5Xv>^ z(4diulMlFNM+y=w&-2`(k?VEiUIG_QD^ANPp-cs={&AZs$b5>hR9KQWsDRU5X*iN? z+mKy~whffn^xee<@%-E3%0{dpw?-a*=ku4OO*@AJpO}mBf&+{l;KlQD>gr?jgpIoh zz?|zwDsg-^D3$dC9^{JF$X>w~CSVT;lI)FgMn(Q)VEID~$WjLUJDLXN(FS2;)Rt)5 zlc@bN+4MYgTk-%@qn-x=otLQnDmgpg`Ez9qAFyo3g5)KqJ`Z4Hp5jC;geb8g$d3|j zUnOeulc$oX9VxTH(q$Y$G5vMe-fhREWYS4P7^+9}rSK-L?^AnFesTbE^0I7curFZM zBNyMJyyuuhJPu6azZ)Xl>!dZ`xp~y)%a+XKo?YKPEay@nhsQDMbn)%?Egt6}XCu3l%|0t4$X2%R+6c5I$(I?gQ&L0MoYhJTgfICTkgI zuU(5PGuqZ|6l+o6vw3+AR6YPw5pwo*D6kGud2mpHaP2y}IkQh4&=Yvi-JYkPoR_zY z=ue(f>GO5J*~9JPtG{wsi{^5JCNPPsoTy7Y{4SWk&tC*JK0h!${z6Uz7kCcj zfoK*k@F0)|OF}gu-rGr!@*zAzqxIDkKv%V1UR(B^rh5Sffov zYbt0b4(PmOLf(;y#FeyesZ@+bC59QqJ#iA4H!q{O)U~zL+LqRhsud8Ige3_edsUFC zxZgOUxIu86|L1e>eP;=a?eF9DMwVQWB`4WHYRv_0=OjhNUDuF(m9u&swDN}{YU(fbAl?KKYEBRT;9g}zoeXnaNez)xQyxl zYvsh?us8?Afu&p=6ai1-T2b~uaq94q5#(o%FaD!?Wu~ycW(#jLR;&?{T`Cj@5pU1c3?A=g1GU0K=|@*flxntrsN*%x&< z?nu}sccr?kZ#=0bQI(&XE*bp=+a$d}q+ak|;Pk{P`6tU$ic5}z?L}-MIFujRbC-%e zYep)zK*omOL7u7U(4BY4JRmjCaV5@1Tr!f%@p?M05JnB+MR%q1y_k+GP8^Q7%hPcW zt2l(xFw?RWc1+8itSFV_eLp`aGCg#bC5=+spdKCG@ejd5z;P7qDeOrCo623>^F_+- z>G@E*xrSuQbu@pxp3Md;v>jh0!rctiN4+EI`0adbvXo0U09{b?<#`|eo1Ddtu|h)@JS42g(Rrf;9UfdPwl+xfRKCYnGA?6 zX?>C8AQ+~PqO4RFkP#$%VNeuz5R$hsM@FM7<|sZirV_YpbL2^ZEY`VbltbjpM?V)J zLjWvy4+c#*42=e0=gZ$S2yqQ4J2L4^$%9mqB>D{x?)}*Kr)N_ASQ4O$Z##`KXIulJp9SPp|ztxwsu(v2ru_zLt_Ecv6pn7Ugo` zkmnXWBKo~#d-i!p1Y^#NNr+PiR-nVGzzO$hmJj(PoEdHm*u9H$z-5&>zIO8jaU*ST zdoM3`w+bM>G9E6;HGG}!Terc7@Wb3Kufj#k#CQVr8yXe-P06{0wpPU7k*gAQ2E5t> zXQ2G@n()aGfigq3hp&<(W+(0VCK<$e1dAdwT?wj~UO9N{MGu7j009pyV7*Q(6muR; zCY5SXnft)iUqhH_`HhF#-Q<>9|7d*_anh;Qm9qaq9BO0pKyJYMW3F zjNfnp zfW1S~2?409oyD%Jv_A;oZha<3vCMHc2wkWlGGwYs-?kZ-PKXa1X+UX|`^gRXboAtn ziwBO%MVa1U^LHpAfBHZpk7f+DA457{PjlEn-i&kHa;FIe1>fn!p{5HB$8cQWZAsyGTJ)9 z=G1u;OS6@)sCj!LYTH}*p$LcGx8l9UeS<@&J)00((g-fcz4eHCB;mWfX8KeP+%xSl z+aT!@upX1f5-TB7GQBcr4}_?i2*t;_Y)ApE7G)9>RL;K#COUiWB4fYhJNYtYXev;TRV~f#z@Zg|9xInso-MG-aJ7|+32?2X@ojhY{0x;isUmv; zVkqOWb$l2@`^`G(_(~ENj*2Cu|DCF6u zv^KcrI^3FdqU;l<-P_|oTm4FQJRgDg?I<&9OINy*;fX=0ir+k zrsgMolkTvakeB;Q3OMsF0WRVlHo*@wQd?g60#gJ}A zK@J0N-*xah3al{JA&&hLtK>eRVPjR-ehoc9&P82nJBx2;ckOpOt8DAkoVas8=IXjk zK6X`gU%xUp$L`JA|5OiV;Ut5p!`AlUsDdEVe`(s4x>E1l{awZxgh?{vO)-L?#KC}8v{HgYzxUc+tYj99@WLEoZg`c<9?X!G|zjY9> z&1hm@-r0u`zm;-7nY?qU-w4NaDYPLasw?fc@$NJv6&!Xob7SP6+11Rmk90;mibY~R z4%b`Bg*ANtnSQJp&%dBL4yCO@d)S+xYJ~XY4P|(X4K(Z z>7(o~ph+9*;rs8HgGC^3hiRXWQ{BV{aUbMm@s@J1J;0JXb;2dfC`oi99|&&www_9g zvE@Q2!#AmRI&>dY)u{b;CKOh=x!-~ihJ8XLNW1}{YQ$wwni0LFHh0EASwi9>dLm~s zr)d{QZ$Lgg8Z3Gz4rr)LyLt<5i6K2uw4Nalff7jw;9|cWj<9D&>9B+f(`61yU4Wo3 zOh-P3Jg`1IlWgmk&e-F(1psnJFD5S)9Mc9&CVO%gcZSG%koiKl!RqH!ODJ&^H%ogXPfdzpS#w ztIR6r@?Oeq-b=ZElI6xtk37t_n@96$0xQWb_vUoDT31RbWYkO13lXdb$o#Aq~(ty>dl>y(%fW2kLk3;rBLAy9O@^!=RH?BPYV0SBp&S2I%2)hK8P%8al zrEL}?C(eZ=;dMU9Z9wus%4c4Z9~=pr-pqYyBy8B@^2_rgSE2+ny3B=Apf+hs<6B0# zSr@Q3%s47!pHW+F*Va~#guH$3g!23$k<*PNbU=3?VNG4*N2m{P#Lp;=;CJN=EMI4P zs%>2RkKAbYGs^Q1iWExO{CvZ=vT^%@TreV`d1iMO{(`k)6?um?IPo3I;T4ON!y}$l zjzaNQ>CnA!KRoCIEpLf#!}x|8ZhLo-qIwfNFrf;S-N~ZX_t_`Ve{D}HHhpImhks0j zt4n4eU0yNUJ0cTR8a@Itim{LuV%XLJe^Rup4yv+n&%B)C70g1V+I3K>jpNGpRN0U{ zfpi%ntZKZ#APC8#98`|$W{l-bnRUgN zVZa5k178i{!$r240bh-o3v)44;m&+i@CEp&{JX|-%gIGaztkNKOygy6^)P+UU%*|V zTR$~BAL1mMrw1*jIK20BIA<&)#Um$c9M{#Wtm|EP#baHMCVnJhmnM_eO?A{v8m}(2 zH;Iw3OJ`kFTz)gOd90fOW8z&<%n2CJE5wrm!_Id>$vd=S1zfN~+73^k@Y{gz%;Lr^ zHuR6$Yf!>X&?s+Ry;P#BGY_--z3{E#by*0?yTJqAn7h6^%DmNAUO38oV*125%_QW#FOjL~rUEd#$H z{MT4Ut!2RSqfuuW^_Fp^X*HAlj-7?8><+H&dJRwAVI7T431jP-cQ~-oxfxekSl637 zGuQRLc@M+T^7P3uF*672bH=B%xw%`h-q4;;mr3S>}86l#``zI^aN}|;!)TCzqTcxijj!;pKVL3{RfJL`_7S*9?zU3C*2_D z2rOwHnUq$nvZu+*$>zD}R$Km(F5r?CmA<43j1;0{UDE!liB*PXtbjZ$%h;Pg1MoD(3|4u<>VYreuz-0yxy$5v+k1-u*r&}J_g4`mw0&x2gf?G zTz|JMlY;|oxs8d%diX&Nj)#5AH_hQ&&7(JCh+KX>y9)1dCNzjAkpPZ5@mJ-j5r0q) zzqngDN` z*TbJk(Q#>-Am|Z0mrB43#dhhCD)vFa{<1d|KVawZZ<5b>(Efm(C+@$NQ+c&m!E~fC z`L@kv~T9^DJo_f?(#FlCb zh`kMwB8oqfcN~a$tL?LJNokn&-6ib^!Om&t&ke@&A*0w*(hD#t(gHp*UWn2KGp~?v zp{t&*1}T6Wv{#Bh<5nBHR1Kb$L3SIE%{1cEJmlfe7*DoWy?DEWNFhV{HSK~B&pKFA zP;BHf}~Y&bouh7%CQOA6r2b&_8)9~sUGMnAiYU!JTOK2Q5O z^cnt`4qv4G?fSc1hj(lLdi|wp8njrqm4b|OHFWp2_QLLxe~XC6I^wTEpIk7-X6J%5B}u0rPBGMvb4)2=LJ-T|v* z8kwg=6f!SGI)%)e4o9PGn02ww@u$0NTwcLHa9iDt7o}y`fFR0^;UEQp|BAZJatQKf zwdNpMuWn6$?UAkNeMz-*9$`*sk8I7G+SA0C4e;pJbjonH=6UVPYRxiOj?=BF53APP zjdbeP>dC_tbaw;pO7EDG@NnK$fIiTD@mJg$$iCQ(OP0#mn35p6fa8aQZz$K>n!;*y zF@va%H#dBxGTXfSMO=G8lwIpXKb2~+wFFua?0}gRzXFFBBNp6A%%OCt;K|9WHQe{e zif{{p)LoE-16f1nT~|=w6D1l|U2)XK374aA?$r6)S=n_q{7-cv_1@|vq)linfOLj1 z;J14z4c~I7jL-x2L_*~Z*dJ?;?BXKrnTO=Ur9HBXzj;CBX=2PL@aQgnSBA5Tw`f;Z z7jJ{{G~LCmjjD_PiFDwbuR9c{9D7#)BKkG%S^vPvk+0mLzu7eHd>8Vz&0k4!B->niz8xJh}remEr7wTJ6f} zfJ=%}9gu&G>VPt&Q!B=VLljWNC#MYv1vWr7l#T8iCV~Hq%uKX|YpQrpdnE9$Y0o^y zNS-H>N#I}5F-?qF0FMU#FEX6KKd4<2cxO+27`DkUdgQh$DjO8|*B~XNGjA^Lp&upd(a;~OJrerEv`0c;pgj`$EgFQ`qB-oc~j|BTX?U7)c+9Q$fgGYm1Cc_E# zvD%dd_6eC_ufJM>{q150>_Y*zoLSBy6O2R5Npzj^9WIkYpjq^*9bwf#P68pN43W_N z8EFF0F}5HFOEQ*PFQfg8mMv+~Mbt1vj;=->Qpb?Z$R$RjPy>rS)nYB;2(?p-9@UWU zWw9Fv$Wz1hWbKjlm1>WKZMgPG%nj9^CRUjXkH*~2MJ$V$`&hfO=GqqrrsmpxS1G(L zM>;Ha??9B1bcz=sV{7)0v-9U;TNndxW!tBL7~V_4Y1p4K(GJwAm&M`WqH)3FUFnOY zm`uEWg0R@{)sB*Gy#0#2D=e#qxPN}gZg5q$YV2%F5bIC)9Nuw3;fK8G z^nxkSa}Fg=^c|O%cU;GGLdpfTfmF!F>?%e!d4nhgLm+$z@S%NF%b#$u=v!px9z`CT zQKX!)S!I5nS*8{srf}%?z6BrP6pWVyM;6+6QMBIO`b%v7DTqn|bAYG1bC1MJ16+HK zB|F|7_#O&q-L0FPq=`X>?-h4zJtRx^(~dWt>YU=;EmMFOQc%74zJPzc3n>l3zXR@S zXv=N+JBpOoh(Q}WT9-p6tgr}QHBwl!kdvmB|LNqcZj zF>R=T_}Q9~$)dl&P>y1oG9f0q*gcg=;Yt?GM=<~vDiy2GETpgVUl4^l1F6{AR3}0P@XT< zzOTm62-&Mc_FJKmTkXf>tgnnW=8jr9s+|u8YtgB7=+i=@{P=cQG|XEY%z8q-J&r|D#887GvT7weR5DQz0f*Mdz5lgK{X)%`88O&Sj zTguY#Vwk0^bjOaD7`R)W{LuBQT;|C9RTHfIA!o!3UPEz}d9RXZX5@W>VZv3a5>w>X z3CQk9s5nOliUSm(QCy{bkIA(>QUK+d36BnBLIrq@7f!Bmc`IQ48uK!>R`acd2PopQ zRfffNwRrGpIegZMJK<1Zum-C3WiJJzT^?s4^x^OsXv2$egu8G32ro-1B(VU#o&+YK z97r64nbeHjJbr{WJM;BA3$P}9dQs+0?4xc0=fO48JOIrJrc-G+9QGN)E)`sV(pPYJ_e5l z@=r3HK;EleSwKECI0KW02J$MT#Pe%=$o?>C%HlL8ecB^|{F(MhAdk`>3FI*Ckw6ZF zM+5oo0;jt_*RCuecMr+{^7of1Om-tBF!=#rJz(f>&cO(JM&{Nq{Y`r$Ob=*}gek5) z5~e2YkuXh%N5fPv!-=?ywJQsz%Q9hdX_!VMB@lPEgegVqOVAFJg=o+Uv`2!Ld{Xtc z1nqO}k)XY=JrcCd@MzH1$Z&$TOuMo`Tjk0?*Hyn!=(-;%0op=H%W`%&+sMUh`fejn z$+pOC9H~9Bfk$YMY~X>~BO92nJ+gtjz!uaE{Fod< zIsMP+9TWk&Eu)I?|@Ua{R8dFYWwy98J#=#Qq}h7 zkrIct*E2WbJv!)KS)2y4O?xDeH))RqvO#+!kdw7X0(m|>x`QUjaE?GjyRv{ZGl5L} zS^;?|Qlf)SmO!o+E>+q(k$g-RtEcd1@TGQ<_q0bs`I`1fC|7BZgz^RLkx(vxM??7+ z8BQo4)Gm{JdS@4TxPJyJD>anYASLDrb!u~WT#tQkR5Ao@7l&kQuzqm7%G`H@2J%o= z&xJ(z6six!(3Dw)?T@l8xTNd~k5GoTk`PEVtpTQWb0IW3dxZN97$gKQY!{VoK_km9 z-k^gtgIpm@9<*Iql8af;U~t3FF?`+bS!3ZMX*(a^kbQ6nnu$$N2EB-7Z@ zd$o}~+e67jcZ<}v023J~8Mk|DrI=-Wjs#kCjh9beH0~o3UOhtUwMXLq0_~BwKT~@o z?hWmcxE~FV#{ExaIB|cZc4guI*!&FKue?N!kgxtBN65kQD4{uoyz2!0s9wZ!NrhR( z!oaDYMvim(!@i>htrzEp@cP&N>~>gGSNN)QR|;Y;F(Qq_M4^K`2WwE^2)@;<_jpu5 zA1c!7c}ee>c{dU1ksp920Xr%-Qj?WgCq0zyaIS|EQ)s7C=34-+PAhQd_$R~8$LBev{;Oactj&p!=x5&Y1TE}O3hes_MCQ4tH_*JKay?N^QusLQKgU z#xogCwen1Msw`BV$#m3le28+ryxZV92X-(Rfxuz@skqH|1xLOWdCyrWt6TB1;Clef zqntWwa++;sHdIpIXU3jyQdN26EGmFq`d2kaFb3pWhGBA4mmhmMvc~(*zrq>D=uh+^ z_3lXJ>V3LjSLz&P5NoP5LtgeDNSbvun{k&LSMsP&Z_P@Q&TlK- z+0*`lu1MeRrsL!BV$?4_{B@Q{@E-A}ugR82P2K(K{73#keDdY}=YMOT@zq|jG&_|L_mQ-}(dbm+m*-FAmv~h1~4=75=&)0lIhm0=|CxD_`}`*4y^gKgj=Lc4VLW zIf2Ed)Am#Ut*f(;-zPtp1aV1rk_`ODWxOvNgv@xddBlD>h_Ulm_ioeK5+{N8s#z2nB(f(*kGjPogl5 zw5R>$m`vT~FUyGi>o!+YH-h9F4U+_4$u*{_Ph|K|0K9MJZwhV`#)2o_&$ z!u5*vFU|tPe*8WJA&J1p$a_474}>^2$|R5PVBF-3Wdfdz`Qt_&KR6gojgL-7kvAx>V-1G=F?K zgzzS>neiizO(c?)gYp`ai^k?ByT<$S2RD8Q>7{uXf~8qo%|7sR)>R5W<$uh=kJ8Ic zVP+4-XgIVoI+F4)xQ8m5f2w+)1u0ANQCt2Fm=!7e)I*p`N3#~_&N z2rXdrB4GmR%TLe2!&gXojU}*MIM)IcSw&Zsn)PW?6lb!i(ie^;HEU0*5kouov*toS z$|ozFcqKc^4b3lK))BSx#fI-JS2%)8o%jiBc486S7(UHjMQwFpI*ZHy0O-I#Zy@bm zVwIWkdMH%8lCP8x_yRIoSUTkuFfTSfJv)zBgp@Az|KlD5QbyJGn$$h(k%;GPm>|nM z(Yls`V}9czXI`cDT8vI{EE7pWVvn&ynM|z79z6HzyU3vQ?Z3|&ODTC-`|q43ATI5% zeZR#@^I7;6pDG1!Yr0(O2WoK36`gshHlL*r-9VPs+)Ue`Zpf1AMjMhaEq^zP?< z4-{X%w48U<7rUV0+=x%W;w>G4gfQxz%gg`kk*TST${1LKq?%1 zzi=CSMKFA^k`KD-0m>q+2)myG;omKuz?!UOk?^Yg5*J_8VYAb#1dvwDI|j^t#b&q! zJynH7D-fh5kXGR6SeUp}mF`CAyt|qQ3}n>u@vk#$Ir6`#WZ<54$ej|$mH4CvoOI;f z;7@UrTM4t5<d$RI%OZg~{pupm|*C<(b+=fi_2oT*?^0Xz!X z@GR?|0q@l%;R`}IH!tA0fYKoxu%t6yv~@HomeqXmIY)*O+YmL#wa@p+TTt0dY7=!5 z@bEZaR+8$Ly%+6I7k#%b`js%=en8RMTJdNct=!?1$x2UCU7R=tmze>3JRWAOz>Wyx zCAi@B*5|`I0>&|}>oCDTPUJ7hP2?GEE1(>BXY&WV@-}>5xmyPVP69J8-A?I@7v-It z(N2b;N_Wd&;IOOe1M%b0i|>!xNrqo37Fu*#8LvFik1vbo@5PV;tXQo}gZ9T%Mo>n; zSqeB%f%1v^6WHjAp2U{>k|O;|H@t(M8#RnU04WCHq4b1m_((RKw|vYG-yUAxOtY76<76 zWAnguS1az`5Tc&6>j{-XR{S~}`~Hx^{?6Y?E|2Q&|3`T6d|Q*#wOQ0#Ce2~T*`H!I zs#DuDNs=s@1n1DRvkv)t#Q*9AhbEDk_lQ6B2jUO^f%vbV-%t6`<|A zpYb>+Kad@n4ZnJKdu?`-jPjBHSJ{z$@=N^wB0EWD{!+2DJL?dhogd|Yy<|V_NBrxl z_`SbBkqVQ@IEBI_kpGUpB&d+7c8P0FBF(-=wBF^Ygi60VxdvhBcVfo*pa?vs^Rgv; z8K|vb$9ov;1B20zSu(btd~(OC%*j`~u#TM=vcH1PVx$-lWZf&EWA*p}6pA@8$hyD! z`Pay^Mhs%z+o_IfsQB(qE_-XmSNP)W@vuWalSH{V{SpjgG%cIs+o=^9~%FQ)O z+@su0bhk-2o?JI82aZe<>Z&VKQ2)IOP*>F>HK3kYCnl+sc;8D$e4&FyyxTOFO_FjwIQg}QPmszFy2V}he*^mcVu-z6P-veCn`y=N_z_mc z2)rZ1Yv#7`C9#SUp7KDL(d5`fuqfQxG@_?*N6vLp+1X%)+L=QnK+W; z0sAW0knCwF4Kg$0PKf1?+Ufgb1_(ef##fv6u#?0I=pAT^hoZVQ5dpfaP`(sTKq+9L z?Gg_H{DIg7p0=u^5blpv9A#IPSQRC0Ri&|t(zdFf#wvb_UAxSLmcO+-GR#hIW zC>I<5Ohl03BNeidH{s}FGvp(EgRo}Oxu4-B3Ut(Rw+mAj>=n4F#A{_Kn@t-#0q=Fc zj2tBe=p6DSUbO3)0Xb$|?JVNH3cRBYlwao!_f57wGubsv#y2Z4m8SLVjKN8J z@mJ!B3q*}V>|$^3z|RJd0tMyd5pONR@&yV4(y6%R{0!KIUc&~Slny%rn4ajOR!Ufu zvjrC19FW6v9s9R8FNogC;; z8!urZ8fKLPH=+17sBb*Ts)B*`FN>o$dm(-P0aDuIOOxFJ-?nfSb=GjFFhDEkf{=d( zZotxnk2nGiuy2dODPf_*OBpO?#3jgvgb&=tUpP(hp$Rx#vN-`;wt#FsGk!+C zFazX?J;bO=Yh-%=#K$;m*nNS# z?Ezn+adjv@qamOvE9yW}G*%!l!R8S)kU$lGMv0X?Z=pED8jp7u1IAe+P{#X;IGX|! z*K0AGF}CdS-e6ZJJ6S$$a08b>uEgsjvNSaC%(8%et{X(%`7_yBJ!0H&mRPI6N3S0KgvY=-=6(1(GJMX zXjehNH^u9|bt-^OXzdWc!czyB?SuN<8Iz#S%D#tPM+k~^=arX-56D|V`W{kzpTo=q zw17p9aZrE#@O_Oz48gqbAnMxqcMj#swk~Mk3Y6dCnsGXs2uJY@tQA#wlh~eLUKk#l zw?-a?Cl)utn9W+4(zt0z<*8kfgDj}fZd;M}if@vuv6CSh?uX^MXPx-hFXY1;C)h@C zxg0C*7Rnpjx6Q*Zxp$a5f$}l1hV@4TgzVec)s=SsAbrMpGG9Fe0sVBb7bGB(4LD@X0Di0A*rnSN?~ z8+O3}OC1u?F@#aXpCQNKA=#U9PSDk7KAvIaZ;?Z~6NB&F#=q%t%mveWk;@5fTFm-0 z9HIEtV1NCUlTpvTJ_{Wco=MGoa)FcUnzP7}dPzR5*Ef0&l5d>WiPH|_w7o=5GPSsk zvyQ)y=eiQaR6@2~&p)Z*G{0n=HTu51udtwfXJHc<`G&joS`1{W6Q4&ACr>G@w!f_O zeHgAJL1jQ08YwEhsA0dw^Zod3-AF1L01l9|9&WW@(1qS*omcmkF^2{9h3gp6Wnhk6La6^3d%ZMO}%q+pWSmG zX8f6{c=--9P0wMUYu2jVA>azQu^NF;+zgF0czr28z(YYLa#8NWl7<(O#e8|RiP(Jy zn=pkjj${l@TRgtvxdMX<5JdS3EQK+N5-F(7ZV%aTURlzB|C#uQYH?Uj!GAseuQcRd zQVBIy95wD)+wi-%0YBpK824+O&|6BH;HM142qn|;n-F=^&;tp^%5Q2xU|Uz$QyAha z+H_HAGh_u^b4^_BHi&2ci3N{ZgzaTg)eUYD@IEwXjqYN6owx__%If&M*{m5i)Y9a8 z8EJ0t)zl?2@4~lJNZGX4gPSiM(8mA?iuGVIWQvhMXTXX|2o}qB-C8Qgn|g0&*a{P& zQ9D7bAK@U9rZUNUz*PeI2xZAm-&c+AfR#WNhH4l!rm;LsWttg)=EN{xyoO6I9CD>zQvC7gOqq1jn^?}#$&j6=DfiEi zS|0H;DN)Q7h$360{UG-CeCdaRG60U{ zL$UodlSD(og*ZiI9dHcKSYVE1f`sxEH=_`p6&e{RDN90C__qEirx1JcTCwZ{Y(KI| zccU=KpO;BRMrFQp!GF7THRgm=s@ZeDga(`zmhP}#u#!C8N*00~IUjo_)wwi400 z>{w1>Kb{Y?_MicjS6J1;Q7vVoJZc_&YpOdQYbr_XV)606@E+cp>gQbAi=dR6)_0ef z=oj8hobYZkx!8l!#WY#86jM4>75%m>T8b$_RH>LHerMvj1(?9VfRr!-Imy+5tST={ z#8iNMk0Wv-*|Hgj9mqR@kz9<ht>4s)Yt(NXI0*BT!7~lx9TLTWh?z zycd-yFuSAV>f;H>&LH&H76q&ZN{F2gP=i(_3>rruQdMn#QtkUBJQ&7=fn5r}1nsZL zeQZK8LErb_$bK4Rf;6Z>yBb<**E;92pmi1A>q229#zu|sKhFIEI#rI_=EiqIVS(%d8b1w92s0L=XJ_-6>w*D!VWdE(-38-;X0`M z`B^Nu%y=#lV$we2arb%3RIF?Jq=V|jnK)7ujOFPH^G}r7JUq&zdUi_DX^74yQ zz~`BZ?V`b&I@6j8s_xMyq_G}lk6Mq;!7qZ?eb%E~T&zc_^8!KhrDuWsl?PJm(M8hJ zE`OKf7pc0XXTALWw+h-UJ=^5(dn!ms&kp(fr3&hm9twU~kHQ8t+VCg|YM7Rez!vee z^2p&>T*xm=yNCejW`36M5rY$%W%P}25SI?ycYJdP@hA>X zSCO4x3DJlzE>D5bH=Y<4oAw<~bc?4m;?w-uL8O?-o-If*I@?bod)9?K*!Q)%wBO!H z#_oRPuP*xyZ}}YgtIJRX_9$rZDSD(RQ~oL>BlahMl{8$?pGR9Myn}~PtE@Q^Xj=uQ zseRUHDA~jI`#BY72J;rgB&^U;h)VNqh-}zPjTgZx$nmX@3?8-B`^#cjY{j*u{iZ(3 zw5$vt1=jkH4BuStdD9Dmb`y6h?}&a8SJTZ0GA&6dHnl(ANPQ+$99@?Kd_u zN(tl4q6u0lBD13BaR~{fOVrt9sA*$tb|I%XTu^bjxAj zPBh&-G$%ZSP(=D0_8J^!R>md{55%T{Low8}CVBuln;JP992T!VakMX{8euw($W1lk zP0(nBoiA&lwSEtX$!hg-_17XRT>9l-dW^-feqaBvGk;P#-=y|A$T4C3ppmjQjvSIb zp|rE&a8QdY!*07`ba`&1z>FLD;P#eAE6PCNPVgryMpGXeL>*}E2f7QrsFioR-r#WF zf*am|4OM6MTz0K(jHu3Q-?e^Rd0?q4JV0qhHtY>O(||MN)ABUem0<{O0rwGVY3y^5 zIVQ(N-bOPzW z`<%j2+p8g$ba>G27qB}h#@7H9$3d!;pnAXtTQDczX?NHYMj%QYd=#Yi5Cir<54-(x zhuR$@cZc$}nLn-YMDWCVwRj!}A~XlXWVH~=J(O8+finj5%g9K4}ovbeL;^WSGyoW3ns8-B&05QOsqZ10V zysO29DBl?^0nnz!N*E`@sABWS?O08yIm?4Da14G|4|MUv>|cm-oX{$R<%utBau5&R zU(ok0m5S(d7f%CxtzT{bJ7hf~AJxBzGLiw`PWRn3_RQR{dk}V4+6%c)2EcQm(uGRj zmysftl{&*g4_wdVcz_wO-$WYj)rS3VYNUS|PAH|MlN&Xxg}9^h7>#24SK8f)fri~) zX%{pbHYULAnJC>J5%awnzQpitjr0%rR!u(;;SCHQb{`nmED)bYR`jgA9#?FWDXdhk-z~sg^qE=$Ql@07*d`Vj4J~ zN-HJH!&qRk6t7d}Nje7MBB#*x|VCxTCw?d7@=spmnu}v_#AoK5f?szqXj!np_x&O)l=NI1I-ACgUt0wEsg9qC%jDUlKz>-+u@d z4&~Gg1#N1xNn2qJ#lGhp&#{mOeJ`g*s&#|E()TgVfmak)mS0{gU5BxU7f4{&imz{j zxf5zGS{^<>s{)Fy%u)9vC@?kuoK~NKsXRC6lX!9tntGUN)YFDxrHkWCU1eZAHJdDjG9wYf(NR@&MO#BlY{0e*n-bdt=;K1FSfc6SCea71(nu9Rj$jM?a zv-6uV8c4b-B0w7cVBc2T_imG)*_^B)zP*C5U{i&iAH(5(V{l{v^w=pw)|K&dAa*SW zJqnMd8XK?A<^57SiV52Hvcdqkiky+t%hSMdGfx3G&Mx>nl=PMt+!{H=fIfx;1DFh8 zM>^#sUZ@-?40}@m;>&AKbMy#xDDJNU>}$X?r;M{#abkzf?SJw zdSp@X)X~H88B+_>SCSnxy9PrGBW*py zok{w0EKTUfXfJNTHee)=r4Ao=ODCLINLf7Ac6Te8`RG}9%frl!sd68_Tt;wt)l%{Z zlSw%ZD2Dmhu~ZE6NXfj6l;YwBo+*)!&@-R2TKq44w|mz<`!2PhboP9S+so(BQh>Qy z%t98mcKtV?e|^keK>zc!{{zrZ`!7IW(F4PyKY-lYy+N4U7eY;i^o#=vxQCHZN~Se3~yQRoY5oK26KV)%(AlIIC zvmP_^%lJR;D}Ovr_O_V#g>zKsQ+_3V^TlXw)r{u~;|TZu~a9?3kn)!NH+@&3o)7?Kz~Y9p?!{&EEMcQh|& z3C4vNQD3n0r{DZFf6VKi85T9NA|Yv0*v(9ZUuG zOlf=^qEsnTC3(ooUvHmPW?S=+09OgSHp=%{&GZKCX2N0(i&c7!>ER;{+gbp>VV_ki zFCCicLn2}vi4fh+=(e*?R?+k!dJ#UcVYw)yAq)#_h93n4ka#`9@k-1CQ)((b1CYL( z;nuKjWVl9VFp(jI4BPr~k$Yii5a^qlLx~ z1dQhCx12aarXN1mXUtXTSpZ*pYMph%g*d%HQ`Y2CnDS-IKv0gx|H%mQ25_*!e*pg` z{zLf3Rrus{@n7p((gLV>q`f6z{kaM583Et-((uQHXJ~FOIl%9O+!wvWCD(VtOz|FB znD#o9J`>qkgt#9;mMje?qnT!0KFk3VqW|wPYvMKs6@2!C5Q$jA9RT^-@tIMrtJ@yz zTb-qsEnft&muM+{hFAC3IlByT!YZf2sL(Y zmBG_ga3g{_Fr4686+9im^D}~9d4Oy21U$oenI}QA*wFN1Gn(;K34JKVH`5Q`$RCv> z!e?CQj=qKNCm*v8c(Z%sPA6TqAjw*WL`4W`7Wmk?G_T*3lLN zxFFzEg0%_mr3``*3~;T$?^7eAPz77pQsy;scI#Fsfk6j}t>56@9_6`PC$N5*H_cJ8 zt1c{`O%U9-;&D*2$G5jM%X*I0pw}ZM%8*ZFK~EPwoXpEncGBumt1@?H+K07O0e_Qf zcW$m8zBP38>upu&l1YAgH-?Vh*j5FkPpYAJW$5TtZB^)+NhZDRp`(|=?d8MV(hwCP zB+n@Z7tPgKH9EJm>SVl?!rcQ=;n2B#+k=PGHxb7gP>HA%wEzA-6Iwp7(f_}x24^ako`K3``gw!jv0R<8zu~Rq(m7e1Bj|@jI}~? zM5VbA+8*h{5&Z)?%XnVdeWwMHmEkk-qSEkvNgiiirMqgmiC0EA%3y#|Vg*>3X2XXs z-XW{9o*?qEkE z-;5^u#U_9jU2Q}^>SsjXM$+9#8vI3N+(6O|PCAI@L|c0=x$HrC%cv=lkS|SFO8gHbckV3UzHexavO^mtwCS!db{JJ zy!g1XsT+41vD{#?BX3nxM_G_tU~G`lY2@Z4jzWH%R3ZbAd+^g!gElOdc8f!QwL2Lr zHk0k0n0Z)kQgBe7bwjkbbVh%iDif#72WNkSh?@oC{a?aH*s&YL(^He}z(HgGr>}~( z@3gxYq41nd+fe2*q=*%F`g5HUx&M%lT+g{9pGC_@{jZxCD5auQQm|5H8Vkxl4*xnC zh17LU^|PeOM{bFY)cb)ZcGxYt0!Nx_kuI3nHwb)Fc?5U_GrizC%}gk?VRYwRD< z<@Js=^US$WD((!)SeU&|b+XZwdm7;9UI`~$kohyZa=6v0{ZmQK>9V){WB`;ApRvdT)7gp1@O#rRbMHPs@${b4=WLisV__6I;e8e0T zL$|eLE?Q|VnJ0f2$lrGPyI%folfNWxtR*DjttBKottB4$OM=;2f-YtsOOPCx0EE58 zA6v`;j1R@e@S%ASe5i>BpXb$wHdNswn?fANW>`z=;l`&yeP*gp6FvZ!9^1YTAF=!r zqHGD>2%5*Bf2tt55i?(X7Vwk88Oa|2hwhsxZe?;_I$Wn5wW2{eYQ*KrVTy~D!!OQL z4zCC)N13Ql4v#ohISR$km4g=(c++w;d>Bi1GaF>}<0``an@<}6MT6W#Fre{9NMIWu zwRH;jB>o&HIYH5n4sf?rfVPi*G|1iZE0huaXsEm8Bsk&~{YI{Lw;UwH2Dn?kKo2EX zsY7w}qr<~DCRc?|i+)|?ZXJXC-uv&wZ)Ak`{=4`c=Dq)Jeha+!--EBag^HN5>dJ|D zWDcx~4@3aKD6u!Gr0&Y~*eOfA_x}zNVVC>4k6iToHo05=0}xcs!h)+xi zHh-I&o0k(g33*Q{cJdY@&b~L4%DMwvU`E#6kQhi5R@=)N<|p0tD9X-zlkoOHbvZ^d z!)3d2zn2UF0{YN}$X0G|Lb4jwtb2)`=^!p{ccCtOY5C~pYlpxT^SRLL@Q%Oj3xC_I zs+5CK3ayyQB7gNQpp%U+6+in1?q$yDJ-WO>JyYYFltFKliUCN+sgOQ$X2$!)(5gM+ z0YzcDjQ*gBvvkk|eB)fxPXR%SD=VBQE4}%-;h&iHxI&n2?w-;GHrSLdqu-%9)>$sI z<0B8OyBqn|Sq1zKP-}PycQ4Q&5#`OuQg;j2JPfWfV1lRHGoc9q_B65YNh_eictLLU z$c|uF1=m2xBu>~Cj8E8BZB+>IrwUq5wl{}Cd4@_nqQ_uKaIqio%%MFLA2vA9wwZ(i z4Bg-qatMIN#t#i9Z$mh8sX3%Om|PjzPWFA?db4>q9})3DUKT#a*tyIThFMM)>_tV` z^g_Ad4&%*XWbR4rK&tLFh{EkWPGz<3p?tr3t!8n%=tn%Z?w@%%(cb>d8auX!58CFgErkm~Q8VMe8R(fcx1)h(xcr9S!c`q6GBhV_7@{DIYw7 zDS;ZEyNStZKnGX8a#4>`v2b346rDMzqGh05+Xly6Eh{6R6Nwmm{{k>h`{R(7bH7^ zxN&S6HlEB5f9}z zfHW8Hf(tovbK(RE=e3n1x0sWc;UoyW1IiGNQV_3RX|(0U`l01C1c2$bgY&Uge+U&SrM&T3Ut*XvFX#G{ zE$^DS2f-GW-`RuYnHPHlhJtx1$+LZ?K>wtO>hRvicS zHwEt5g>WbuESku3w@@}xI+nqaT&={0lB>h#;=cs{!w}Z`6pM=AGZ&wpcBF%wkoHjg z()UC!ON`#eH1@0=_;^G8QN>LvAO|anT`<4rJLa=z$@^z}*n9(f8W;9i4Tv9Ce*9c_ zD~SoLtseQgZ5z@X;M3~sZiq}5Y`}GQ$CR#tMWB}_x_U;)3b2RDfGStd5CpXO59s08 z!+R@ow_u$C&xBtx0PLqS^qvJs6aZQDz z=pQ~M`b|G~E8~4nY({o>^iM3yp0yqx)!4ZzU>gF!>l8ibOA<%67LCWdf$%ke^WYr7 zow!`1L0a)#3tWgvAf6Iu`<~#2H~k<)lS$5k!~NwWmYrJ?$9m17;S+4XOBGY)N_ccJ zkVjw{>u@`=bMutp?MN^rX9`}mNp3>wtuo~*`^q$}C)61HknT}Ja#4ox&s5Aou?4fG zGkPDu$?1&V&ks*8_%hoO0XzB_+^IX+qXt1dGdiEi@Ry~u59C(XA2*#$+6l_kvS_D_ zcluT>uqvWwW9X|>qqtVNy$qkK+$595CCXh)_c_W!acQ1evKIIm4pQzj%DB@`4 z?xy=t& zH3w9XsXjI8Q_GLC7b|7+cqRoY$D&AU`ANz_-C#9!iG7ZvsmZbI>6+cGhar!r<{oP1 z93l6&+iWRXg$ioW*c^Oo`Hd;X$DOlzRD^U6IEM240iKAT z>&iATs=+IB4ucTy-%U1qy*)Z&mJxX#V2wNQrqS7oQ)SoALl zC7&^wpMyJ9kQ-5mpsIy9jcdrAit&(ER%c%Y#~wN%caPd>;n_#Dqg0%Y3P5r7e_|MT z0nj8K?|-FqRZ)u2P|iX)hbkuoP}S&BqDkUi@))dzbr{pu!g~2D6^U63NeLi^6oMJQ zol`&@bTJTUEnFazEt0>_Gum3%EyItuG{J|H#sIfmn(N2-51cU$J#1?|EncyyUIG?N?Bn}1hWOYv&UE^N;(gT zmzJiGF1A1kMry!0GTxpf&<$2^#4R4@Xu_<$10|~)A@Zu&59?C6FYH;Cau@e3)NaXR zH@Pe7JB!^@;4bBp=|IVG7=HMaVHje5;1Uqm4)Wmzwyb`MGu#W2a>`gg9wT6#hf(ym zJ#vQ`(v49&(eb95DqM1}>p`y~MDTE>$O4*d4!u6M}A zwRs+r0?*0V9goONa&e81OLjK@6@Fx-_+(6qn8fa&*ca%XCiRKogJoCWQ%Z(9hy<7{ zC&#P}7h2r5J)#}AJfpUjgRgFXqU2x=bb3<=luMb*+!q=)o*QvDTlt^K< z?~Q8rxHm%9L#%|u0r!Hvmxg~9?Y%tw<7n^H@FCIOtHOh$z2QiC+0Q)07e185hO1Fxu-19~SK$64qK+W})aQ(d%6~kR(rxCkFsic_MV^LOU4v2H2t}VX5IrEU(op z0-Tjsi)%5vh&INYC}BvP2Hs>o?B>8)G3L`B_ct9on#{pQmWzza|QYlZ2 zq(POGtW#`7H>8q|RY^yINj4Lk!nstZu~rP1snN^*d&VLMx?(1TRaWlWAklF2mY$!e za!yQ@^A%79=t=<{r!Wh7^+ZA^3*q^sb2evq?x?LjA29)_XneTnfpW%+bs$Cfi{!XTH{6cO95@-YqEj}*52>s z`ap;R8luR+&e>coaub(w&rW42Q&|p17A_&oa-htT07I7r zaBG2kLhNFeu?=p4ufTT)z8K2mdhzWQ?O=cf>6Jiqm3M zr?pj_9;-UNt->Fx^0!qOu_~jjVqC0hTw6s&tg1p$Y|g!`qxa3xW{6E%ZP9#R(pOKS zjmdV|I4+`V#6|e3Ejq0yF^DjEmkADt2V1NiEGVDgid_91mZV$ox`D(}bIAaV5nMrG zhxETc5c|)McJ|A8dE^GWqeJvdw)>X4tCq$g5ECz$K?O|Wc>{Cs1{XT5H++aa!3E{y zFbxm{KUZ1@xZo?O!mx*kHArS!K5Kdbw>o{^(fb{*mXT<;y-B<%_pg~Fxp57|mCkx> zbTeHCM-De^f8pYJvvPC5(HqyXc;1o&=&I^S;2q*Ru@KpO%i^J&q`P7Z7|@Fcp+q>Z z*vc$&_a#Q1x3Sn@r6~lF_$^e`c#u|n1%@Ja5t!rjZ4M7JVioyQ46DM$pTR+^0tzh< zE1UNkL^BY>v$8d!8y8D1qq8C(H!h=e%X-S+k$C19f@$4evU6)g@iU4;@o|M5QSpg| zxVVLQjYquRi*9PibHVHMmJN|Wxskt6`;FSOy%KJgm6pL!!?J1d5JMxDtYKmgQj6z*#i&J}h!FawwJ~+z8|i%FlU@xMOIkcp0nd zb8dJ8@o_s<&|iS*=UfOf79A+pBBq;n^Kl4(dd;&y>(b|e&m*p7N1?(0jq=DWxTrt!SF8|| zEq#7Z!_D{^|JZlC5ii^QCAS0wD;|rahOu|G7QI$)nYw*3hMBnoN{gCLv9_jNAjIDLB&im{QMV zno5#ye?d&#!Q)a8L!eM3aT5(mzB)Dh$}y1UN{a)Jev20a@#t-UQL&?bNp*~HhFkFwdb&M)?*Fo5ACf3By=@0I=qi}Z-C@Bo& zhH_nj+=5&K7n0GA0t0%KiW}bx#j6?=*;6Y1IqBP^0*J*O#p3)Y?Zi7)?(FQK%r_NR$T&X$R#CKr3d4y(C>bBCzcb#UHKW_ z-H}W+qv9xc%b)2wVOtI^a-}9C^8`3^@DTlkfw++Y0hR2A?TE3vk<8tC8b=V?bph@p z?9G+&GjkKyVzGrdUBi-`CJ???NP&JPDF6WijGgFUYy>cN128tkzMxQbQeq54itxTZ z9rROX%|+I@d9WT^_LfcWCh%H!jW_|}SSeJv8NI3SD0eHl!oy^_2zO4Q zNq1GZ>06DU)n@#DN*kIT?+!4V-$nelEc2#Gyq?}+GDA*2{!QOzWZK+WS(1akbhlET zrV=(HOWmz#r*wMVEgvE!vmZh-s~a!r<{mI&YVS-|eqz+*3-^**BP9WeKT^zZ!Q z>*Sg7d}rP~hp1fp)u8*y^>$~6$cu-{a)Zs^BQ$?{o{_h*+V{Eq$}oS9blV+B-U%6t zAZ9?>3>VDA7{1T0UEI18dLKSBu*wA7Pb$5Gr2W^3r-q`lRF`Y0WVMO&fr^2u;b@rC za6?6w>l}$JP!m7SDq&cbZ-C0$^peCSpK`w*bdP)8wEnmN#Rp=yGaL^l@52Wx1|EIU zW!ifFMLS&1T;$C4CET~xE0GJizAR&-~scq%|VL;UMK0IS3AlB}N9pL18JJ zjB~-o>$&StWPu)FFaG`b541J&?h@8E!-x63iywm<7S|>9$p^eG@FxZb?6>gZpYA~H z!jkGX>rt6y9kaan50ypII@AN(1V6L9hAeakVo-Y5cF*78^q=}#_`pD$)NvY!%_*6S z7AA%m|HOi&JOQlTX9U`2{fqek(olcfi_b7)?8cI^gv-A$SGxE!f#RWWg|97_f}i1O zXhG!Aaw-35Kxv8tyad>P>N>`?qa?&Fb)b}x0Vc+6Q*j;Hkf6*%%B75`-~GF;th%w6 z){EP*h0TTOQ8aZukj&Sq(cgf+gQ3~TB^(>|LY#~w7}S1XlRd&vFUXf9fIk5PI2Al* z3kAKg?PLFW8kxAZKPtb-Fe;f?C;kARdKtW?h)WRI>vKv7IOz~i2%ZI~fOOwX0}Bf^ z4d8*?HmG;KNWpSp1}y*HJ1iBq`kTm)sFLs5jHL<+!CRZpA2cp5@Y8!}N_7XTn0eRs8|^Aqc- z{N3pEmTocMt)5UJem=%L6!jE1S(N z=K+;dFa&D&b2e8rC_#i$3*2xTi+EadW&l)Na|X;HVOk?j+aTf9(1!@Jg5IE0Pt*5M zHbeVBuYxn7FOX2L6Z!J%K)>_nYHE}Mb~4Uurue!d2RQSghs?q$V-L;=&r~2ix;_)6 z#mKBddV)?3Qt#eDQo)%ZU589W=r!`|fHWCCh0wbW#Qeh98)LW|fMBSu#|NwnV#fne zh;6R)?iP+aGpyY+Z$5@yLfHKZ)Y0sZsGIxSP5WJ#^|ue7W~VsNl>- zJcCSZ#AEX7G~yxnxa6!aRAZtEK%}AM*iJz?4<2LD5dO-AP<#)CtbL(8lc1^Iub@*y z*}ZotRd6PhLnUL zK-|1{5LIv{h%3ls1c)!luLI(f@Daqfho+~ILgbXOuGH+gDhrYuSiORzfliIAd3%RM z1!qFylMt*EqvY3t^TpxkfmD7`BE>!WZ+5HAuf6E%}KQN<=x4BqIAGMQThn7 z0d$Shr%p&UJ>_VM5l&i(cU3Z!I_YV|Bte_fseAd7XLTd4J;VgSVN7PYSgH)L5)J&ghkzl zY{;{)fhee0QE0W4R$J3#H=s~S+yL1;ZUw8=-g>Y0^?F-xMcP`lRudvg03nD~QGB6V zb>jMhRS|q4|L>W3HrXJ0|35xtpLu^dGjrz5nRCueG%eYqznPZo#AiU7OIsGP`(yOS z?644d0@HeR%T~FfTLg^^k`D`#2X74{c?gdYNFH`sgUNU)8HYjnU8-_BFkhT*2liw- z0NGz|=r9vx8b6@vuyWn#es!|`()jcf>S){o#0U#l0}&PEMQE(Y$BCg8+3N>ZXCR7Z zr}U(6OLl{vE+1;pR&lxw>dZ9A!hn+7A`>?CS+V}2bc=pY4jI_B`YSEkp;qEH!2Am+ zZ&A4FDg#Yvi)lbD4G@D95`tWH$79k|LHzO+szSbkLFFqrO3coz5CbbW5&F4H zg}r{n_zrL4*KN)Q=W5uH?)WYjrwh)wMd*URP?3aXGt3qoZYUob$j~N>H3>#*Kznv zyJkwe^yE(W(gR$T>i!GiblEgb{Y_YtG&scOM}x8Q&IR=vVnn2U{i|9lKyD9SqGut( zw6S{}$d1beg6+7I=ns^`%X4xBkn>g8!K4pG4*V1`N|{c3DU#2A^Z7(7!0RC?AHVtJ z^AX~!!2Jq~p3`+9AO7p8A{hhy({>t+o}Y<)KZU+ z&ph*)FKO^DG~UJLa}6IUslomJh%Zfu@!`LYT8e`vTqi+Nc?%B7((Znfe5LX=d{vRI zoL6xK#?s7=>Bq1TrJ>IBf(JeDvh+m|vC_?s=|#j!Cp)yIYdq3bK2jaepePw0fu+pE z`*6R!d)m?6h0y1@rTcR{t{jVuecI4{`&&+kDb2Bk5 zjQ_a^)r1K`J?J7-TNm4}Li$T;5b9*yviKLVT?=TW-2$3+W-g%l{|T20w-+Pe^6V*D zaCuxj8MwTD%l`?N7k3{9F00)VHC(QDOVn_=&6Fr``8MGhxcufYa4})oaH-J6_Ny}e zHE=1$EpVBYCp&~OyRJ86R=O0Lns8>P7sdpp1g(yqaQmN2^_s~ZYxWKqYD^_M@}?TL zy2fO6ip?gILkeWR1=?Q0A@5Nj44{n@fbNVLMr{wt3`SIHccP_BWrmgyPMAH7y9~Di z9j<=;oZPvRVsOHRM50!q%N(2##;2#FTVa-5?iWq^Ig}@8X~f`ua&p$-z9XK@;ErAU ze-3VrTjGdG|9iJYJ-GkrmZ%4J!x~+p4DL$8GlP4t3yJJWZ^E(%caAQ$U(M8CGq~sD zmcjiIWPiBo&zc1zh8WF|SW|jp#}0v*S391NzUhVe!>>!Phk?H{bSUsoeRc@=PZ2o` z{9b(Sn1?%l_iGk&xc#cqh}Cg0+>SL}Dvh5Vu6&>soF9vU9%XVs9saCc_LWTi7gN7m zh_o_FhTl+?0lx+0Ei(0ZjqX zIXoQ3q7x?)QGx$#D?JjYGkvOT1i0E6F+S*qtf2teJc^80AN6G~!P~Lwm(l~a6Ycqx z@tm$be>I-3YL7gcK(cZ?!X}V@2#Jm@5@cnfEc!bL5tdpd5B%`NlO_vag_XyZ3$g!i z>+O}N53}BGD#==JE5yUt39?rIk@`Q^+gi885$kP=TcTcXXSgNm^=6wAWxY)xJhR^Z zOl>mxv)7vm%U*A4h9X|Zs zwb(@I7)vFe7oU<5SBb_ENwUkYS0_C(B67std5XL7-2Wr~<-dP%{*C`f{I9+s-bsZ3 zkNU;&BdW-1zpA)azsf*+2%1G3{;>;o8Clos~<-4jbRoc7gp(%QyEL7`HJb++Q;HG(c>C7!% zxF|8@+kus{@Gi2dic3XOVvAZgREkY0=9+(@$^|D2N5Mh>_y!rFak{GEcd@8<0eT&Q)u;_E{i*vy6y{^Q_iG zlPW}bOlv(z=ds=9N&40z{^d)C*ZTPS`Em^^jaW)m>B+VOTxB5ZoX6D9pE?9Ub4eor zdRzLcb4?>;um>uubRb{Eg~Z50S@4p1O~*UjZq(KzcGhQA%`1wJ=hq+i>*uV>(k}#` zY`)gnO5&2BZgSJIN z7n@EH;{mDsOwQBiw);=VV4m324wsgschQ-8b!8AMf%8iz2JaHEG$G-cf0qpoork^o&jmg$FY#6?x9id=!$a-6-rsNGF3R#T&8(MEA#=zkJu#_+` zID~UskdcWxU9k&n2bC$S!FK8kY(~e%?vby@T8ey0K}|>fn8Y+cGj@hQc5+SIPAg@x zmCY(K?|kS$J47&Vy3 zbc#Y)APB5hZ9^=P%oj#d;mrk+q}8C6WKlDWNNbW5Ph)yR1&q5iyG-QHwVLn~Sdl?y zVurubl>5hn+2x)ou0vfU#^Xfge28o9+T@4`Govng(Y zTUloHmjNV?VM%0P%V}wZNd^Gu%qSWofvU&%Q<>Pvu>AiFz!%* zfT`Oe`eS6FC;}RUrxiIgP)c2ScU>wUj>8ki?izI6G5W?j>fM4-fwtXCkBK_n7|A#) z;&et#2TNRATe_EIs3NWN2B{zM13v5F7Ji*=){}3BSH2Rfl~i99Lkyvg?Z`vfPwp{A zW0%4EM&e+oak~~G6usV+JL9+M(X&4_P&Q!c+e&v35UXK{52h~4iJsgYp_C(f(_#}# zV4_Z7!rABYv!eM}1feUL0av+YR07&c)h1==!7<}{K>-4mzG%?$yAXWplk9=LNGIo& z4PS0F8-{vL=)C@{ygwh2_ZT;?Ju>s6H8vLZzujTwv7wBb`i)r8)azqYr#>7zcj|qy z^QWE`v!)iuF7Uva#r*IfGxo+?q@xSo; z#$Y!#Ez(btAveuitOMRO+FJv@beHXHWcs?gaq{NM@H@-HdPW!|iekqj^`KC=)*~hS z%9~{RZB=z!SDHYwAxBKP6=#Vsqm_r>d5R^%1OXSA&FofZ$btjM`OeqPJ@ z--uV)sRiKb+4-r9k5aWf))Mr_j!#V=9jYvkf5nSz&WjM~jqmar(5Uz*en;!qoqrA5 zj|Sh0jf+(M0O?exXJ9QM50$jYZZK})0c`C}_$I%uFeHy!Z;BUl3?QBa69zw&QM*Zd z)!mzOJ!?!agq=1HZ`64;g1YI4yK0W>tU0;IYVF*!!c@pZ9d({a`Qw6JgI!^ygTp+i z*(T?rq!Gf=as+cqGM9T=Y@kRU-Mo!7Vt`mcr!&c^Ba)h1#0hL>jyg(&6_I8OZfrU) z?0gU|e~(A$^B*cCkva?8&cPag?6ORLoelZJ<8#z*h!m5rvCoVMZHsy_M^ngd#n^0B z|Jstv!)61g%WUW9kQ#UXegTlhFBJ1Pc?LnHU$ZupP?c?j;QW2LFpGO4UUY5^nCzU^ z79GD%Oz^18$VbJpX|OW&J2VtYg#4&0ryaZ? zUq2<%YY9^DG|y7lUK>n5eU{g{OcKI|miZ!-Vg4KR%FP_m;IFxK9>7 zD$g1dL*EUkMMQ$!v=H@nI~n(=b%cOE8hUE{IqfxLbVd4MmCXA}Zf|58L(>=eYhwXG zH@2sGsowHL?`#;?^2Do1C~XF=Tt@n`q+3 zNco#~E7FFZf~GT4KOPJ&%WocQ#JxvBCvsGkoJh+<_?+RHkt$rMihFWBSgMm@a53~y zzaeKa*c0JNfag@=e%ZbsmHIrf>u^ayp4hcfXSIa!_;AFTxGUlmck*Vh-TJX71VwQ{ zY7TX7SHA;9XkUg=4TrukO_;^9FI*fu6P+Wp9dZ|)dlu#g7j2mDNe8SA^Eo6gI(I%4 zY#`jZ2%W}6SFSy{HNMkTGSixq?woNh_0dqCjk$w%sNN%`n_KPP)#8>RWR^7#~p8L`pS;kh&a&NvTaBFuB zy`0jvTdX|j99FL{M`mz35}fTb533L(Eej_L*FXwZB&MzbgNdo3jp^NOQlQjiCG&IQ z%+J~iA=!eyHA3%^L`)@PCneEc5aHeFaPW5Bpc~^4vq71OAVW9SHf_%zUO`nat zyX?*2DTGdyR^Q-K)(_NZ*u0(r8N0Nc5aq z2om&v$~huF9G|51m*5=rtD`Z-Ldc0?sR7EGn&oX7ATYuZ!3lb;xr#LI*z1KBX-FSu zI|~}5@aHu&wxVF6Zp#3sPaKrL+XZ8$&2DB)M_^Ee*?LB%klTj~sm~OW5C?D2KTRRJ z4SHUF=e_KC`D&LtFEza)G^+D`y73WDV?$CDAQb;|i=Mv4tG2eY1mg9C*on6DDdv{E z@@gzkVyRLFk>{x)ixNFPd$2$Lvef4yLOtj8_?KA0dLD<{FUnf)TCV;`bM_4U!#ju0 z{90*U#PO@i;|@0iU(3(Tz;kw<)2U~n?VKMw2?07L@Wl*7hT-}yhlK{+JhhBm6v1Tf6*l2^HAT?RqTqu3-deM#X1Dr-w_SL$ry5mfwfqd(5zOCf)M zS1+8<@L)2YnSUeckg+TXfvV6UBWd3x$RAk!2YhvC z6+l_C(Qk>A_eF!71FLT(IQWcFk?5~W6=Ee&tn!Yq5@-)@!Aju1-h!DaUk>jgb8^cs zkCAShDyF@Bli~6rRe(F~@5M=(SR3yxiD6iNo7&H{FY@)#;PUcZbH@1p|KwyPBQ;6Nt#C{sX9!OIg*6;CCgt3UQtG882xwXj8>{p@B5`Qiq4d% z65W{}mL#tzOMILY|5iBoq0`@dPSp)XPH!`|YA!CQ8XGHsDot$k2d5V`zN-~ug1yVx z;(0=_@#!=MW2ToR{dnTmaJzq_7xc)kJ%SyN$Ad35AM2z67livZ_32(#mG1Jp``8QWL)ahCxO1Ko|2Cdne_nQBn&nvxTpcS(jk> zd3FY)M8&oYq+g^#mD3}Zr%^3rvaW_GmnEeh7>%*gUU`#DDCKBrj-FVHB{W&%uStH# zGil$Xw`x}Y3q`5jtT$9ACbJ{5ZF}AUD|q+X!M%;+1~)Nr0rs1X&!>;aNQ@DtrrA%v zDb41Ni{jPXztLi;bw+aXKXn-ozfBn^6Sc|l4=w+(@kP@5GHDIp3imr%z7kRC287&i zSiZugEaSaC*}F%hpClYl5L3U(rITw|cCae?N6F+8ytfHg8VO{xK?kJ)f}^&o_umpH z9Xri!)XJ;DzyNsOkXe8XdK|$kFH^tzcv$~18$Y(b*-rDM-3`JA=cJ&Cl7WXrR#f=} zt2#UzdNcJgHu8kj4kD0+sBzv73wu=VB^S%j_wVk;ZE7xEZ z;p8I7{8_eo4L)Kt*=$O1gu$4cZj-8Bb(&Q5qB6GJMAQ>8PyM%v zm?vACs1GhMk@$Hhxx++;q`2uOB7`IsH*oS-&);0R=SJ%~RvMC@@TAQs3&36b4ytop zAMk*NpvcmBRX6Fb238IDv~Y@E8v-lf!Rn?ucx>hL%l78XNcujine>)4^*aoaelel# zC*;$6%t{>cJkuU>%R~~{dr5jr8lZOUzXwIN-?I&M^kGasaexiwQG5{?)&)EoD*AqY zv6&j|46KH;L4>aqW2eZMaS~;xIGn*#;Q>Z~c|q$VEmSIh=v1d>Ermi=mmGy=pl0sK z#uGb5nsVQpGQPYx#+G=at^_?j!n8$CF)3Z+ecMWWO02Y0Z$Oxse_Som`8tIXgE5IV zWfVD^)&D~pTMUDKh5Yez(YOO*c7g49Itp_tFK%f1YWu|v=~HZDGc{g}GIpHlK9tIt zo0C&@ajeOkyf{{uLpOM4a}RwO>_w7D9KnwRH$KFCjXDgJVBhiKU^+eqo!^fK{O5`HOWTz6ze$dS>vR%Os8#yFH*q4~{VQzd$GdF_WjsJFT)lWU8k8pOR zN4p*x)V7BL*iJ%W=bF;e>B)&3!rrtzF^A^+bd6M|CG$5pqsqI2`yqNr?CqCnPi+0P z_QFagTZz8HD2WXA>ggZ6)lB~zb2NL3^%HeiHv=NL*kMESK%skIHz-uR>c|H` zp`S^zEj!8Ee<6v+!EOPwvtxHi>}LwNDLcvk9yZDMvXgWiHp%qtB;P%>8GWy1^&@iF z0`js8IQ6heUd}GXd)Oq8W+&P6=fm~)&g>+AKWvigvXlIWB*}*9$FF8}X8B#xdA_Irs;PG1N)xLDUECs}Ic1i{tItc9xfPcU`G*y{#TQX_JB z+M&0X-ynDPkgx$nrE`It5}a9;>L;c`y#dPtbScpDO;wd*X0{lEOR|n%N3lTi*hwD` zqj&2=$7?!fo5XOmjq0ub#As@I0n1EJQ|g)tVXD>ean}mT4BEQ9K{|AsE^&uAXK5$3 zw3@Qwe@2=?w%HN%S!ScoKF2In(?AjI=ZDqDPu1D#fdSz5x$%|iXG8H(tpmpL>DK?Y9XHDFkLzThRk#y28DqO>taJkSdm$Hh;#oP9bET=^Gu?0FhSvnzoA zAbO(RzTgsH1ZUu~||l@CyWK9k&3dN&bj%xOp&aKsT$*qP_I zVCN@ozwJN;7C#&Hz84L?*LXQky7DM*xND?s z-Rm;TTD;)e((KsZO04eF1l^~->FaBp*CNnnGD1Hz%))zemP}7f<%ySb4rzLsH~;s# z>9jWQkk-0QwFV!vdCE5UU0>t+k!16o|IBLTSEL;^Obj6jEoZx~{u@zeb_F*W5$EY? ziK%0A;(_4oqNcGls-UKFvM27F?kyavZDwaUn7hJIV5Jcr$8NifA+r+)#|Bo)nOA5w z2uPQ3n=0fq^AIiaezgJ8)N(FjwC;6n7i?37tRmo0=e%9#9ON)3Il29$jKm5b2m5t^ zQgoGTE1beniI0Fxa6wVi8BBEACfk1OM9UFeD38z)#!g&U;P^&OOZxr?h(rcAr7!3D z;ZkiG^I=zz`%Ybbbw}Yh*h6)8FS#dmw(S%a9V_DcS7h4FEEq0DPHwCrqteJ>ih4{Z z^mToSaI}I8m?IZz(tNwR_oMyLJf=b>QTpi2>NxskDO#_N9sd+Ep6a|hY%i-Q%=TWI znVNx>zttC2X1Y7VH8d@+n&UUN!v%-!MCiH3d6Biw0P4~FQJq=tHBMm;7w$RnNx@5s zn$C1Y9HJmxIXNeGk|hB2hKFD_6JxEu9J<@ettAG+(+MFT4Lc_=uu$hUo*7lX zXWguc5m~(^D-|_KMS>6yj*cG-b5D3>ly{A6DbkQRCSGPJjMc;n%!`~V#j6MQ?;m{7 zFRR~W0IF*U0Tm;)doGtEK2{}F)Yik$nhUI)+K}Zf zWb+W@SiD31<-|`pQCto#t(M2I;pX@_tjm%Dr|t@a`Wn}Zc>*I3G5dvVaQFW~z@31h zarrRoFFLf9jo8Jw+^|cB z?a{ygGBHYf_UhmFP0)Vx@lYaJ(P)!Tf%z1fPl@@InNJ0uQ2T~TKFJNj93_27du;t1 zH9>21C3ooGJ53#TYtKFU_g)jUPJ1@!--jhA)gr0nERCGc@YB@#rg{gX!PUngT$zsB zI%Q~5H)-_>6*~(KL)dvI>U@Zb`9FvXtXc)U1a}scprBGfkUDmsOawS3y&$V3bkS0O z8Xn4r786jO8Dp}^y?AnRpO96|uo6HWctEftqPDoFc?;Kx+mD;Kbaq68i~JZ&amEWn z(y#vgfy~(R?BorwBwvpOXFs=f$Oi?Ipr%w_{9J?plB1k1ZK0d6IhN8s zPy1X^m2WF;mw5v`7mKIG^(++6Qr9z2JPob~8MQWDwB)fx_$eemKnrUO;xey#i{yZe z2gXc?hvD*b3|}C=ywa3`094J=oM`a7C4rSw{plX~B{A8w%c^H(A-l}9(f6>9vtt)$ z#}52A6uy+ci!x-Ee2IP=xaITKUrO3D)XH0yWw-J-?`5^}c09waOln`Im9PI4rhg~o zw_u2osQ zxsLkxvpRvx;DYU``CtQ3PJGW7(B$^-|Gw`p}H@i4uwflHRWO6+PxOG8{z zw&?P2m%d3g^I>bkBDBCDw%DezZCDW@3#%Td+o|O*c!q4#d?SKco&qp~df{Ce#_j4U zz9d8n3PQ9`Jji0+@0W5{>*90V%IL%yrV~*(LG3g+%y*6ZMUoyu(GXLX^NvK_?MD5^ zjgolTF>mXgjyB!I_$ole}3Rh&4!*-RpZo3^rs^ zUM(qg0xkb)Qm%GW_K5;@4R+JcvV!k$w02AX#uw%hYX!pOSuCU9AU`qj@)oaap@wf8 zY3kTD2%je@7Rt|6x+lMXM!KmqQ~Uw1&;@Vl@LP2FE|{YYoFO5L5Q`LcAW}c5vD{nQ zNs;pHlIpP)7WL-JJvs7-efnVlSI4pTd5YH(Ft8g(Q-0#pqXMgiWhRccHvo~dDWP~T z?@pY9eSx+W?)TgJ%S$YMftA}y&#=kLt$}cb-yV{wb}BUZKnHQs5Lx!}6@_0pQjp?l z!8m>R)nhNo7+(Pk`!ddMm(i8G>M0*PL(a`Mj!1A@EMFb_4itIvJ_+C>2_nH4umcNW z*ZVfJJ85KPk~**Ekee6eWJYX%O5k|&JTj@BB&GmyTUZj)jO|sX013moS&j2TowKbb z_(Ai9l+Goi{lq}j(WT8!73N~8Ef!c*Q#J9z7~Bk)F;6tU%TJ)~XKzOWQXI*$yAOJrg9TZ5-vxKr>ZV-G5kgYN39NBGb-1#(g4>#rjA7diSC+-RmRyP>yv55w!X><381I~aDzA_66jp!bB*=8mbPh`o^i@r9a5LopB4m~r=XuCCKliMS1;0Fe} zh@)`9s@a8)qVV(&h$y1{ZqqNO=`v0BkXF9`)LDn8%P#!twig)8)a{Z@y8BgTkKSW- zjK0 z#6UQYN&(^EU&DNwFSS}_KU>jU!tN#!A{j_!$8FE5>|x$m<3N6MrPTb}v@DiK#Z6gK zh%gE-A(ZuILUqyM&Uwnl%-bEr$gRR+ zk=$!@847p0=PIUnUHPUcz>;x{%0=+)QfzPdNIxHX#O!X$vpPnjW8aAk$LyZ)k@-38 z;UmY@@UkCbI!WJ=lzKO5s}THd$`4nLijCet8^vO2Cke+RkL0&FB_%0ykTixn*%L`@ z@}*}=abgXs#S}2NX;d{&znRJcw@UtTEUURe&a6#&H66KWFZF6&^11RVVXqHkH{4TD zjR$5ZB(#QBO}mdpi0mXHG+hNSFKU9@g?N@u25!^`9Uvo+6tYr~qnW*q$#<^JK;!J;2(|$T*YQi=BS;P6(e?t3Z$Ii-*?LRcOB0Khei4}(MBMfE)vH?*& zSq8(Z1&GGEOT{H*W5N~9(6QgeLw<8uR+nnum7-h;SyG{i7CSZn0}rMIdF$D4JHbeB zQ0SF_Izt!JuTJ4h>iF~NJd++U97&j_H1`9ak(4HP-^1@-@_xvvC!LLhTmO&i2*c z!Y#O|@=*zmjj?!ra$p>Ui0%AQ6NAq0?j>Q^c|b_OzmWzV-$(A|6IeMPUnKYrv>T4P z;Ol{vGjIs9SjkJKtjBXUk=YbZPKH1CpOZz_s7? zFmU6(vU{KduRD4VmQalC6}yuS0@mcs>-dD*9~FEk@W1IYNxXqn@-M`r4T06R1SPfV zEF%*?jdZ$m=jduAC#6dkO1%dkuah246!Sou5Fgckm!yg2{LZA&PcKAL`4dsBC?Siq zfC6L<+U2HM!LFup?RUuxq4}tI5HABvcNu!yc`7p?>tqPwYV3d$DO&?ahlO%pT3iQ< zN?5bw)z=XHaFm>o@&T}m&q*4TW?0)BNG3Zjf>XAFx{j&P-AeqDM zLYeb(sr;1GIge`-xNLP|VJG(q;ex#`+w^2DG9XWX z%^p4`a{0%hYs4whwOu-1{q+?=-rLoEd=b=rK@N$(#tnHTJ-uTyl$PbZnLZw>hF56f zr`Ql7A=NURBrZt=($^5?Y#PX;>A^JCltl{2h4;3;tUk`VU8oIRd=+`4&Q+x$k-L!N z3IMbHQHN>Zg8KG;m(03l3l8Y!5#`3S%iZ@g$~9APhrwy#$+7+ed5!hs1}}l;X@@r*#E~-`uGbn$H!0rmK3zW#&$c zUcbOvy#({RWyMmFc28)j_;YhYo|gDAC~KSzXTE}k66=+LF>@?FX|UC~3Fso;Ktp^AmHUCzb@m`F}Z|CuLln^R*Fbs$3jES(*sv(37*p|HdAjC2^RXthrT^>>DYRzB}?`{Q})1%Tkp@pWwJ zdcz+&r$6vOmz^3{Up~s?_*y4zlvV;?8dx>6NFM8*aEjYB{kDW|S{Dt1^sg=mwNFLe zvRXsajA}-HS_^eQ%Kql*&=S+aXYYb`LzMV!>4!Wekx8rP+`EtZhZ3JaV%tsqRwgy$ zZ}bKL-ACu_6tRJKt@~TTsp`>IN1vHoPwugU`x^_bu}@fu&e4P2){X%yvH$$#FQ&&i zT!i%^oMu0%#_ka0CDKaiBSRhffUMuF>UzgNxOL_373twBhw4NJ&?9|15aUeMr)9OC zd`*`2%?RV9W(|6~Pwt9+4I$NCJhs}D>wR(kF~lai52_2PM_|yeY+Rt#fUw8=)f9Xp zdj9TEZDFTXrkk_&9)3|u3GRsbf;-~%&e}c#GG5DBtFpZ3IBWe3N+$MXo*K{c7CLKf zykXJqr6{uo5&g{cE96pfsR{S(F%z)F+8_)pb?I8s*&97+cl-QOrh7Mt_B3C(J?xvv z$uZKUP;T1qU;ef&w{1>$d(<~sPLy2(Cj1>|c7X|=O0Ws_E#E6xVWQE$yKiN#r#)O; zhK#u`>|=?RgEV|0+E5~G=pSS)B_0b6y*nby)V)9yi5A)Q2RUT)RyUB`!=hX~GI+5L zX1ZxL>Y;l`SJ(n}>c5=}Y5suvC8IcLu<8b+#or06dWLL>LPwqR^qU#8o&2fLj0ZZl zM4T?>k6g(dk=Q7UKk-qhQH6tCF84*Q)UOSNeI?;`zffp{Jf$+=5BA63sZK1PT9h-} ziA^oQ?sEhU!VhiunyN=!5^3Q^?h}dki#_pg{_)jFex#ueh2Y=`f+&GbCg)lT- zY(9hTiI_L(c%Zmg{gS*q3s4Mh+@@|7kG>%Eld-7r>BJ3XIn5_k-B8xd)yGSm?HGh@ zPGOPyJ}Kc9iLl5GWsR>fR-e&L9A2}NcAvAe3dK~tY?~)>@zFBX>w(+EYTYp)3L4wh zDbnWqTh7@z*d1KpZ`^IQ9B6@iM$8e!46PKIzpfVHe&&OE8&0>pm*s3tY#eQOz2Q|m zB)M$V@=7HtPXkS~RL^^4ZDoUC)Un|J>QPp&XzuV1&ZX;-gxIEjNnt^h_nd9QoA2x-9cIpCUpF-j=DOJv=N9M$~|*P#=(10E4u^GJoR>x(%*= zT}FqaWYX?B=bsU%Ps|mVr(a_W){jyr>1r6!e%)s+N71i5^k+Rn3&sKcs28P5QMvI4FZVYh)}uw6^QEO4-)v#y761<4-@5BT7FC#aP}6}1$=;eE zqUN%r#Pd+oOZzP#Y5~zOY>WmdEMs_``CgNnIm&+WI#4J zsmLKaqx39f;CRYKE>a7ps4J;jK-udLUg2*<6WqL&Xz=DR4ysUp+$HoKFTVWNI11FA z+5xoz&KbBF&oHhP%;mn_^u33uAoF(9CQLO6V$}PqaN5+jZeoA^%-cOIO4dBvC4iJ5zjW6^0F7ew z=wTVjir7d_744+ceCmFVSrj<}=kJC(0eV6t4&iHAucAk=7do-(n{@aqo2i z5o=!$+pJ%`fy-SiPkxx0^iL7UVhKCx=8~xMio0HBIE5#vw;ALiEcXL{<16d)rI=q! zjw>o=%!$gzSM{9gSO3ehG|TI1^3m>W0SQg7e)i%pTiHU3 zMS#Kv*8A1zl)#fgTdNk7HCH3GQG&(x_&8A}?L?WhtZ{GpjP)Q%-ti=%^PVK*#l+eY z5V3^5NN7fI4_urn>1wy6B_m3@NN~wE^#BTiz$R)+3u%l}3orC@-Ly z0(wx)MU*s@&B!N`Oh$ittN}HHvF9X0Ld2=^YPj=`sfyf`2Mlu7osqein7Yv+qk@@_G;00;fP2v)5 zkdZ3;U$idsui|q zI@N}3Z-M3A&eZV-XWNbMMv|C21Pg9g-S08vRV$VTRtZCHJw4AFMt+Ocmp}bPj||s)-C-{Rg7ZroL_lzHBw5^Jb<96w zyo{i&esZfB>v~4eO;;}?XfxIp#UY{+MN{~=Z<~Q$I9%L9t(;&bi&uxe&?=h>!a>u# z8y9Jy>Kk&*xY9m5MS$=I@h^Npl2>}&^4=bm9ynm zMjGkf%B=jeU|M*e1gTRIBzk<(W>rWNh6V?PimhtP6GL1uS(yl{I}dQ`amY|Hy%jThPAjc5>U$10MvuJIqp2?W~2I;Y$? z+Up_8x2wy+6E3q$PncN5TwJ5&1N%#U!t%q#<4eua!*}bCf6D6CY)%W(t#hRlWHg1+ ztM_+EulC4LEv82?nKinOFDQ9#cF7;KnUY_Tf+$(HFigq+L4e$;Q?j5jnUA5Gj()4- zB`#HmK=vPiAnH7Y&SstSd=x7U&dmZio@Dap$8GtH;~J>C-FH+?;DOEBUlRy#59(Xu zra}h_bae>Wh2@D0?x}jxUaOL>K!nT#X$i}Iy82DfrRZg*k~K4T|FxwS2@Okj%q*C(|L*I>snfXL%r$^ zT213+Th+W#X6{md%vAHqI-B4J%;Rk1-$y8-hn<>f8p5f%)X;H zr9<{5TRpwui}Ej44}4)fd*M)g)_Eg%fxiZY^$w+v8jU4_p#Jg`LF|`l`Bd{#r`6;0 zdN@Ee?FqFZ(BciXPaGXP3quF;b1g5WPJ5^>ffyfhA9(|Ub(0DH0&HNhULE(j+} zH?aF2{>(!gIDn08!`c6DZTR*+*|SEpVMecS*N8SmlcoC)+Xm|*>tZO|kZkKEvaK%) ztUw-K9}0IF!-vlQ=zYJn>u!pyb2y@fN}Z?FH)Oj?-P}oF)WMWZNA)R$o@he%n9u@3 zPbKsS6DsqTv_*uTM(78^d`juL8|M5%Xc?iG5gP8e`t(Wrf?9s2v4S)qa;jreGB$S! zFG<7OwKt8tsghxIm%d22zGnrl{W|4IM)kk4X=>pCom4{z6(ui(i{7-}3#^#DV^QE* zU3CN@FJ3rHy{nuY3K~&@|?7ubx20L&UrVYCE(HVG4+&5GkA(` zw_I5qm4qvh%Iourur0#0SLk})v$43fjz-zR_hN7>Mu932xLt!b1U1)?bUH$}vr0(M zikMZ#LE7+^F-Y@r`giQ82G7Q2p}`f|qMp zYW2t+UsM!vE<$CZ_(t3LF|aT)eG%u{1`$T+l*X}|Yv0R&?}E*;Mehs6}gWfLPA5D$>!HKfwybqN~wFN(xqw-cYRVUo0Ci7i682_DIeI_2OFif3+;|a}R z@$B=3yu~{HPnk(Ra!$6sX|rL89II?aLo;)~W=Wx|u|-`qz_vUI3ZpkZ);#WZ1lRWA zsvGQhIlpD`Q}|sPKY{CdS&VU1px=o&Y*G+xeb+LJ_740f6oA$7j>v74a{BPckLaka z+PBL-ZnJ&z0SO%+u3S+C%c=6bm^btnQA264lhq-8rBb@*z+i8+k2QlRe|b-J4$luP z^9!xRz;dX8wgn|vQ{D-*9Z5Y==Sn2wjhv)RpoLsZ$0o=`>Fp74Ym#oBTm&zKcf$m} znk6%niMR~TZ7|%vOxtkJ6n!m4N1e+`qE(mqV`CqcIW=$;0F#n6rMXnd&E+EH+Ct|= zU{in~-<#yi0|`RF$agJYz$`+sKw1Y0p2ITs&m~BP;BFlJ3rX^OaLD(6_zHcmr(fqY zP;DLa|FZ-H=KktU`asA5uzUhYVb^ZI@?3PY!>N*T#{A~qq7 zmEr7#J;fWPVircsdE3rSTHIcpze&c=JSeEnaXr#Hb&Bih6i*SJHRiB*DW|nHwj*cq zTodYogqQFn!ay#9P=$Up;$gDGf;U$Gk6I@7ydTzY#bi>dhaWRdXpxHU6A$OZ2DR;M zUEDTxCqYi{fQZD(L=E$7P<{MZIk*!76ec|LJJ}iKa&*~JP96^(Ey48p1hgpy*fKSw zR=@kr=PTg_uD{6r7E3q-Ea^4bZzCheq}2@5pRrf>&(ab_@>EVfjB z3lacAQ*KqiK^j*$DItjbW(Z%-6P&YcZ>6vjL?ZNk0fmgPx}TE7vqwC?aXor8Zg)MM z64Qal9gVGwt2-Jo6Y7peqXFL+jmAnY;65`NXSx-&NJS@#M~}w&X#G&nHZ`6gkq8sW z$mQ(%=dmixQ+KnRCf|n$3lQ|uZupJh1n!Px8k{2=%|h<}7mEtXJhsk^6wRE4My>?m zHo!&TyW^&cswKNja1z{wd#LqCe*><-uF(SqI~07kY=AB&nc`xO&d{0 zHof1Vp8KNwDVc`K3w*Mw$j)D>Uc2{;+Gi^)p=4)1WRKKC{)`%tB&+cyO(_>zp0XQ%!8-EuDCsEpV8{gj;Idy*Kd+^aDAOU}x+|t=sHRd^E1{ zEp0%#+p&BWHVv7uWr4p&aliV-O2cb-(g-V1V&`%?6zE@GeRu~YfNxc91b6Y>Fa zHzxKy%NA%$m#GPV_?5vG+I)|`8K{kcGluGwJ8-GC>QaC69HuJQH+EG;Y@G0^JN>~+ z{qT%~vV%V~@X4Sd^^!*8Pl)QMgKS%^AI<787KBoTnlKLd zt?KDVn9Dj97=ZOWSYy>%L30UuL@v|Aav`hDoRq2a2@I#EbJySooJm0HhVfyB zJ`;4DNdvRrO*7U61cm|*&@@Vcd+Vl9CZJ#_K>8wxs!ZU(t5?8#t6Kj$^B{}HM*g_( zTe`_x)z1zS_iZ=sdg5fT#44(x&N=3GUP-LkZ(gK)Ly7$ZSL$L7{LR|Q-ON_?2u_Xo z%NSU>&L4>V-B7AoxPf=4zkg2#_pq#nO(Z|4MefFsg;A6741sfleX&t$T!R#xc&t*^ zc=o^HavkJ*M@d$VQx8+_{SSY(+`n8a<)%2}eF%}N-{Llvj4bz$l2w;Gu;8f!LVCAW z<@mhbxWCu($@J(>U~frZ;%e9 z3pPl9(_;wk-XNf`lBfU}+t|bXnBQ7;@U4%wo8H8VwXZ)`*jlyyqqh#m{H;~}eB)oA zv>6tuvt{LG3zQ$r4fG|{=vZAs<;BiTs9YYbR=(I*&VV=PiG4#t{pgm*@}I?}g?u-+9su|`Oa4rgJwDHpdo#Td zp20^Gxmsm30yic|)LM06Wcu{@vexVTmsl6q#2Z_$%U|?^SV{bQ7@$vCSar%>a>XJm zab00f{9@Rd)=Bq_j@RM1;LBagTQg~Q;JzJJ@X6Q|s$%4qoD)u6P-uZEInOUSrMh+M z*sH!3`&P9R9qWr7E6vLBEMWudu1U=u%Z>fVW0zyE=(29B*W;x%Rr^> z+CB-rs%2rgj246`-k*VgkOy$}IzUM}Gz?ai)tkMSYXdBr8>g?E4@8qW$qQJB`Sj1z zYss4CQz)OBw#|XIuh4`@YB5-rds!Il&aRZ5lbl_!=%F*&F1D!+@Y;jjD~@ocS0rom z^>Y?zf7Ip&r&nMiic-wZbK6ECl3}|zwfS(jE&Iya{6(!Fw*cw;b)v~0EJjo;LVT_pJb=;T8|y)45;j;*u5gDIImM%N zsf`b$FL&jL^3MBPo~HxSzO!v}tfu8aNer@xeYK_oTaTUeF+PTfi4t*|HHE8xoi@E^ za3jGJB-jnv>lPCKTk;YP4zf+&{8z!OLekipq{%|i!+10W-f#R>29J*9O^BS%m6yUv z`RB^r&c_Ip1={`_a9~V~Gx|gXpYls1iMMi5WVKKZVL>e)=b0a9lO#{s^(1)EU1{h+M-(o=KZ4$BMP$jX+_rtEJKt-xoX($2M)hx)lln zwV_(MS>NPO9L#Ohnu}ag_N&Fag|Wqj6PG{bFGZgCI2zm)AM1R)a(7_mw;%ygFoKL3 zaVp@!^7P}5RVhzoBts<~R>*1(F?o1IBI)U{EPSQ-FclZ_cUaT#B0Fsr;VtN}s_~xM zVVyT=Gmg`(cKbBm%z)L0_>Fab`?PUZW#s($k^G()KO#~UDT(EYRJx~qM!wiV*Xv%M z&~Tm`oG|bt#8%SF?si<~hdWLg?-|`a zd@t9Nn)-Ds_-SDEFJ)A?tSv+08@nb~m6kM}2XXso%W|j06CcAT-xHsh9<3d*6CIyZ z6?O_6k6rR*C+zre7d4JwG8VTVcX4y>U~kLv(upjN{7129?!^5q*Hb`odX#))Uk?XQ zY78tnEBJk1qkl&}Ormr?AzsybC%TIUQ{!Cf;PAJ^{6PUg&T!q50^1Z>3tKglXs z@_iCCeI@F+yh?coCRNyPr)CscgHNsOydih6dt~HfA^|ZxEuG*xY&1OL#6^OQTPT z7s2b~vVvrFu}t|P*-~SZ`t#a!lPMUWb3aI#dsU^|rr+x#KetU@b-~Cs0X5yG{oj#3 z(WdWxUYp>^r1G8tMU^e`UiaNEnXEwRh{^-Sq zwdX1(4jXr#ojf6ZK2hst3`Je7qhhZTI-_nV^dggiiPLST=J++e4pVuHoeT_C7wA`& za^cxB)S06!c5DXx1K$N9$XwJ$*l1if`yQ@$QqeTEeVwcbM>?uD8n?hfJz(4dCiQ#c z7AUI!GVVLXeT#9oi+h!J&s?g0Y#drld9c~#1kP)P|Da$0xi@MKd$h5D^7p^|k~k*uCNVqLkmZO!;s*$yN1 zMn~=W?CDdg&yS7Y#}1U(lRMa_HQBl27p?H(j@t94RG)|8cxMi+o?WJX^Zfq74|fer zWJ9Z|EYtG$fi_Vqhc*w+D3me|dV6s3xWy(h8@f_V;5jomXVbA%p>08t;HkxvZ>x(a zPN&)}Mbxn=4yVH7raCK=>Mx|~sI8`H@j$rp>gvEs+3=*L-w-d?BFv`2fuFd6CrMxo z;k?;Jc5r5q+cyaWjWHeIEn{t&CSiA>JZRVW~62X9aDan@OCR zOkZ|#ACxv&K~ErxQn!bQ57_X80Lhqa8rsvcjSXsAmpkV=XKz?v#xnxR)2-z9V5fbL z8%XH5O(PH4(g*ea?jN9__w1bc2M zfEea6QeZyO*0hMl%cOya_5_+v>{12yL}JID;sH~f>uG_2A0K#NXQcIp5|5t)M)ebI zJL5}5c`UDE&T;7lEz6Ge@HCNB`7CEUNU}>risNe=t`idO%R*|8`Lia~#%Z~0e_}AN z@wN2WjycCzf61BjR+?>WLiz?tyiL8tHZtk0&=g)7n#(Ndm{UZ{18s7SJrgbbP5F`a&p?&k95iT_OHZ_% z8D$Y?T7`Fuh*#U*cEQ-;%1iV0#W`fTW*}#9t#d&!^f&}zIOT)7u#n;xu4~t8d81UNJ77 zr}duO7v%QTihW}-qGjMUM5hS5S+^n6fEtng{--Z$8ZLTD$YLigWF}F2I}_l4xWlTl zc*u{oRLDL90h41mMyVO-K^L*zj@iKMEYVjPAPmi4%Dl7WM2gd2bLPJl$nDjGZ@ zY>}AygonAo>3R?6XbNFUZB_HZLAb{|GeLjV1wq^2xKR%{r|i$$YfZDVw!^Z+Z->M4 z!|a7+>bxg16J!Xt$v#uJa&rXvSkdN#J^pNLwACvU+rx7{#{6i@vN4`On{3m(=<$pl ztS)HuLNQKyi_>yLsPbxm>^N4C%^OiN#~J__#$05k5$&j!Wn(?D3C_%X;t5y(sOO+T z3^b{z$mZsJ+1XhBQPivK)2j>O$8*i(SHf!mE?9xWbC>aqmrP)|YWQMb(?H6?)sk+B zN%tC%EW#{g)tpk=B@sW={EYavm4ktm+~o|WyPQqwNA=x&dIM`^$eujVDEUHYyax;( z6#kI`i7*?SUI9qc7&y|f%p1};M3M^ikCtVAPb{y_`5=v5;3k_J0R3887B*9YB@K z{WW|UQ7Fm-J)sg$PpAmu*H-FH*d%MBshfqIw0d$Ng;%pF^0|G7paiMhEV5f$S4=xr@9}&VRUnm}33La!0P%ahg4w}mqA`R{0hF2U>%_x$M zKzz<>#V^TAqR!2GaMGq-e9#*2$LN*yANMJjU>P9%T zZCTAjs(Q6Hi9OtyuH&=dFm!GzrHfmzhOL$JnR`vDbtx_am}?}rEbEn>xO!Wp4I-4m z>8Mp!f)p)j$Oq5EXjF!soz8Q0UhWZ}eQ#ReTHg*dn-D2m;R))KkfoWUO(mq3s>79q zUO|$r_u#Re{9RTD?`@7rkE%-^X?1M0+ASU&{zWH|O(#a2#D+|kLOiC+ZkC8YJ-QC= zp~-X#nf7Ed?aI!i@C?-xDbSe^*O!`3B@>I&)U-c4lgI}QXDZT}icF@{$W$VkN@$Um z7tlSzQ%0zhe+M11l97`1=$hmOB_>Z9c|wwB9LXwi2o&Pp8fRloyLGzg;s`2XQLu|( z|Mb^#E;?c)nl^mY&pA&;w02PzjvsjhO3jp;p^D1}iXPV2D4F9=mX1UQ=iefBs)r`V zp?kt&_0_G1>Gi|CD8LRE8M^U@P(-i1(Dj{pI4y$3Z_qJAK)>mH1A z`1P$o8oNiyRVgj3pv}S?fGa`{TX+M4t-;~)SSkdBq_LA6*rxkU!}R?foTEe<76PXp zhxeVS!bZqYNTej(B_UyG0`3C!o^)a`XUWZUqCQ)9(C|y->>0!Uu2XHx*)w{`+0(1$ z{8FDiZD`rh(8+PH`e?2`dxq52#FH6{Ih3r9g0bt>^K{m!UK*@k{+ZJ9OleCgts$$l zdrfH#>MOdmrIf~Qyxd8&1Jq#RCVpeH&7l~%0$~;T)I)bm!*mMU`DqJDD3A-)K4I=$ zrVDf=RBa}qn=bh$2}$Y#orEBZU_!?2kixy{a#OgZpm4!QS*<-dNBZtasJccMuG1y| zBq2#%pq+B=mS}mq1b3&nFE#Ex;;u7pK@G|>Zdvi_myDK^kb_Jxp|4%gO_ulB`qb9D zbay3+7RgfAUEQx5Olu@Wy+|UH-A?{ZLXx@^S#CpovW)(!zLMAKNZRh@4(72&53ayW z#1R`j5L_$kDaQ!7)>1ss6=VL#xwlN9?K~_B%Ixc%UO?Bx?AAUT0UQvxDn zzD8m-gHeW1rFIPCLy;vqz9)kVGpVxUE7ieGnRtWW>m0w3y2V{iZk^-n>Zm@?>Zm_& z%Es6cLu{J#q>h>ATT|@wV*&1l)V}95GXhKE+{V;{O%SQtj~sK$vh%r`Ygu+4@4#|} z&II~g`ahL-S%VA&NGxKj%l=aaZDzfU@X&T4??H(E4G|S1BB(BVDvV&AW$zs|WW5Of z*~Vc8q51*pR?@H`L9AieWcHdD9~%jugcTZuyknlnu|1|qxvlDd?$mb1bQgu@V4qA_ ze$s(RpN&IEL<-bO(#Y}X$0O5baOeT_f?J%ieoCSV!6M|$9oUdObXVZ`XY_GLUJk<&zt|gw$ zigsyUd4Kdd=+ewPppjNY_1t$rPjQ7q3~~p zMS419w%P7<bFb@c+il)T=+UPl zPp>{EvGsT!tmj8OE!aYyEjxJx%g^)-Wv*OZz}KI?@P5_EQ-huA_#uBoxH0L z=b`HIH%k+;KuR(R>D7lKgSX}4yHfPnhxB}%x_Y%}a>oABXK^y|8g^eV$p-I;{B~YSg+gf{Y`az2ADV}&6vah>ctD~bCMhIQ9#P)%Mt@wQ(M z3eLn)qI?1axvosTdz%!nO%3oxm1Gxqs<+zD;Ca;35@jBKcZ1xwr5P4iI#uq_%Go5C z#8R!hGsg4vAw!)jb`gZnE6hY3NYPmSq?K=TDoI*JuqSbqB(1WfH4#@u(yB~aQ*nio z7Qa#c>XX*Iq%|Mc!lbo0XA!#j5TFdQZYYWXxwzlhEA@2!Vqdh`OCtLMlD%pCs z_S~a?$GQs{(aa4b!v|+@?`7dCP=10vUQjKeclA^(H^CL-!wro5t6O@_3tMp z$Rk8^s~pagts=TljUt1eY?X6(vQ?z^2`bSZIgKY<#~I@JVe0=!iy z_GGIN^S0CF@=(oK4`WjXoGVL7ua3J_M%B@B-T~v5+LYJ0r7`NmpXxLoai@)2x}sh( z?mgmu(zpc>s@J$X#r-$qUMKGRjQehJ|61Hv{z1UcW5P_bvv@<&t8L=F!-Pz%(GinJ z3sukNKVCKJ%-hxX`NGh{7K}L&ebW@$B8AQpcZ!!-)?&yH3^a403hHQ1iRa*uppYta zT@~th6Ir4r7)OE1*AB7AQYj)x1E1WItsylMw=7i0Ka`C(1V3l5-G!{(km~s{Z5kpQ z4~?%^TUKX;+lSVqu&drxUwashxH9S1`7~lk(1MjPnnMUOVZF7*5M6`4wK8Cy_S(`k zYZo5&m%|+1xn2vGZ$eD_4Fs#V$Z1k7w8cahB5%Es{)e|DY-KU@wi zRmto8$?NiwT*rbGs>fnm#;bm}mWCz0$)A(U3hDs?X8A+xEfA!Y;yG`Sy@KzTvjb0e125;U z(^1eY)nb`5WNYEFHjQg!b#*f)vbx~m+KyRUXs68bs{?0>R?BuZ4lXJdI-efcERdxK z(xEV_Zx!+Y7dBSebAMSUBx{jYyyVnd82P_?!bM{a(5Mgq$*ra?<+A_BQfv%7)1MbX z2WN`7{*ZBJ-Iu9eIa1S?*?x6~?n}S2Zl*8$;!n%l1~SfP<40&D@z`Sc`14%>!{K?D zs;g8pKF;jJnR#e*uI1LQV=uQum6yUCIl%K6aKRk^1`)fqtE>VsM%J%FTqz7Z&sr4I z)t;*F6~)GCyGH|k_^TG+l~fOLjO5AO?t%3ZdTe|&25$cwClM~?a zbwisMc%RC-#_!LW$dc{M=hd#pH$qe1i%mu!*JB#ddO(YU=ON}E|H>mifwI%jGtUK9 zvZ+Y@jyny^{0_)QA*RW#hmD-?)kq3f7t~aJ2fMl`?E2D2ScxBd#>U6v;Z+_Hh5f~i z2c%6R?%4nTG50RuQB_y_c)}zQAUIKpMx{DxsSI{yb#P-us+=TYK%b*IsMwwaqV}Ax1e}@b2{y%E|c)XC4k8m&Upa7M!%k9UUT7 zcO3)sRp5V&6{Cl5b%BEZuv73@cjAj2`H5AHY$Niv{|bxFlM)Y z25$`R7|kOfB!Sx67}KdQUR1ytjZJ=RQNi)i$dkG9`Z*}n<}~d`D}MUJ#09dctyR(U z6Bj_^`kXmMMJr&8xoEKTbp#Gr(3#kC=97zg!0Y3m4i1$5TQmMaX)F(5@YP*-h3~{$ z1>?Wur(z-Dap-{tqMNXT+;85;^K0a0DhuM3Me)i}2Chvvv7boHxCS?My@lp&HGi~> zG!$RVDT>b-B?nIES*+6>;>T%|r4u}@m7b*&j&H3z-k4AvuPk0Vp`^94Wa)%cS}P%& zoB&~MW!cgR-quR*(h23QmF18-`!Nsv=JAl8VZl?o3UMaadwnt|?6>iqYZw2e=L60F z3-392flZs!e!d6rRJC{yFEf#IsTdqEpKvd~Uz9H&&_}TsyZ#UcT6+R$$U8@Hok7SQ z*rel#e+dvW9JehXRuz}wTQH)w0R$u)-#-aE0Xh}qXRBq9zP{gf7Y&lo=FjW#^V=ESEInuh#jNwQt5BdKe zKy71kg0wE(DhnM##((WrRT-}`Nd7!ze9j>5m;WLrxJdbmJEQy0Li<8}h24zKpa*hWi@AE7i zKQv)E+R+ZJ&!2iPziQ%8*nPJeV)7P=WIEe`#UtM ziw7xjo5koYAz#wnJOqTo_zkBM4*lPHqd3e!9~Jtm+iF8drSu=vfyv0J8CwG7V(K&h zm|)_f><%!Q;QU-$u`1^HKHdfjbWB ze=%a=%W~G>ULMW2-+e;?R^E`YU2lIg=e$Jz4RmSdlZ~c37Q(Br@#|KUKEZqW(fN__ z>ySAs;MyK6KYro&Gy{aAS(*!TAZThk1KuBwi5AL7uAS$(&Vs#+Y7v#@Dx`WEbU_{m zA6vL}GvIrvAwfv_cHvk9Xey6DlaRz$6^~?F(Z6~;+0RBR-ekpFt@v^)E`0;#Ve*(^X24O;_W3M{o zgTWDqFFP?@9^W$!d%A`Pm+tNws`Tl>rEmFO_q$hg_~FB#laTSQ63mI_Y3F04F22Ica3zx8~O^}shDs)BaC-L58Ij+e5Qce;j(jA7{pI451Iw5^yo4&PXi6 z_C=kMH_bm_GwDII%nxJYUWHDH3vqZBu2tfV`M52z0M1q7jib6;VdM1x{JxO#;QF7S z3)irz7;o1hT?*5|p6}IpcQJue)4$XjF%WFd9-=2d)QwTQ;OrNKXRCQ78UR7J>>7Z- z&x@I(x_DBD|C`ZU{O9bCfT3v?{0h^Fm;p9B=XXZ2ubZ4FGGrJ zBG<4z*$+!9GuHtOxGy*49{)562ct+O&2>N87C7e(cW?ue8D}Gx6KF>PAdGMxnE6(O zSS++^OXLj8zsd+W14#K-dGKuNWVf3CSfVnMQs8@1r7qa6WKPddISYlXxVi$qYjGX$ zL*VpzeFI00%N*MoHtrFqEd`RZS4bwuWD~J4oaK1&S@Lgq8M<%sH5BhF!Y?=RAv@3e+wfdlb~= zh8rf`JwBBX5PyVNpT@(`Fbq0EklJ{RIe~9s3ZXJ{U=$xZBm~0>DpiXNbMzKm)75;p z8U%W6#Qi0{exQ$gjV$P|Ya)&eHy@PeR&x!W0)1qSZX?WZL5g^SOfbNWc(V%Ftle{c zz)j~9&LJSUnP#yfxFh4v5CG{#ve~l;4M3-MqOaHugC{EF24nec`+S3tIGCVY6(a3e&4TA#-cl-87X40{int!`!NoN^CjNX#a za8PEIRAm!T7Vhg_cm`;xRIs9$6~uamEIbxTtOXZ7#CnFht2SWOWOZG+lDEnjM?W)7 zm}T7LDb}T2ho9bGA}P$d31&8Du;$grxSM^*h|oB9%NZ(kuDj)AghJ)IN1cjAf7c)Z z4~!}w58{E|bD{Ps9x`$wJ=pLo*kB<l%9f!>0AEaRN6gk|ecXY&0cCx3 zFIfV3fjtW*78a*h$qf*ctoIPL{tAgCq%a}hCVC+A1md-M_{$H+YoWxgEy5pGO1SVm zxRvsDo5GOL%Jc$hA>?fAl9dw6$<4#RaITWB_?T9V+LUb9CvZ{;yj4*4HNv~_Z?L~k zFCq6Uq`|loC`p-Sz+7UI>7{fP5x${EOy9ON<*+Af30%`arDn~tHv!|+EPD}=_)F;L`rWj?6$Ax^aSp;zH^@;#KAkVGleRx_k?A_=#y9jeRlG7{C5*6EK zUTY_>#K2@U9(AwoCrwSWZ4Od+OMsw$);tYGiC?+baqh&L7LX@AA)emFJuz-l0+ev; zmf{(jCxgvX81dM3>_oQ?YUx2DpQNYaqmB;hpoBhAl$9_Q0#&JmSf>j_Nt`q>0|^c) z1F3=&$$L2M@M#{( zBuv%P1yMq?N=g(?Ep%nkDf==>Kn?B|5?(eSWsL-Zf{ha+-*n={w%U|)lvtnlJUOuYaf$JD|PZh3H@7J3o>Ic4sp{*qx>VUmJGdDkfJKuj;d6P|<>G?~e~@)xu<0LuFD z%4`RLN1ZE_+z6?w`OWEfaEXv%EobZOuU{7g$8=z^y%{Q2nURKEQscchp+<6dm@{f+ zbbEDTo;iNX@xMw+T;=FOjp6H8x@`-WVU z8#X=<82iXru`eu0Bunryowx#ywBR`e=nXXm+ew_OyaBKec;}3bj3x!<+DjtSig(l| zlt6VtX&~qd(x;cC@?G4Db>md>In2VW%tDLLt>(pGb(q2mR7w^6M(|sv^r;5vGAW+8 zPbP)cugX0cS{B2XY3Q#q29o;SsR#59<)^a(aW4mv z^)dT{Sd^j9gS8pdR@~D*$ko)yksV|`J_5*C#!g9oiJfjkMevkfAzU9<1(iOcB&*|%F-<7ruXt0w@cA5f%Nx=-?6;7D(ZPb=d?$jC zkte1P3M3}GU{Y%*Cil_+oGWv8x#@x|yJ}!EJ7X2l)F!>W4!XCqrkUJif_1= zFk(llAjJw9Qwdjg6Eb7;uof?j{3YX!oX0|#Nukm)Z46@%0SBA0M!cmDRuBC7a(a_w zvpEHrjpi-5E7LFmZPNIbtw#ZJ*}zcLwvL~1sPHu3`k&9B`=4B9zFSZ3z8l&CItK)U zkD>O%#jqdDy&s-k{T@8U#c(&8C}rR@m%u`EQGle9nx)uqd`u^>mK|ZkNFIDw7tMns zmhs@z0=ZfAQZXK2%3l*)x|P?hJ*(mJyBGj(WRFU$YERzAIC zej{xy0yO5_m-qolC=53eSAl~ZQ=*rSK^P1pdzXve1pxO}M4-71xL%>0lrh4YpXg|5 z!1WPkanQG*q~VdiGe0enQtheqOP4q@r^;H+K?)dST6w!`?3Z~&VMX1QQV(Z*VOcQ7 zWTWcYsQO>4Mm4HN1zrDOqZnhO_z4>oa_vEZ4ejaek!nb?3^hPv5x>8<1Qp{^rXFXp zMK2=WCQ!Gq64E9>JsW!8K%y!~a9_j=Emgn>1L^=Z+f;#MlT-5`e;u8^yeoz6=OhRx z#{3+gbE>h27OmHCBZ;>39czZzNE`6Z##A}c^>@g^*`8qe=?e?7B!svmjO>KRw08pD ztH-zlI4cB^7V0(kpH5%RP;*Kn)V(AsMzHV`_^!{^&UuGk0o2r>?-#Z??8+HJg1|5* z7il@rjurc0IH0=dUUES{l)X1a$FX%RFBk(9C)3*oD4v}te!U-sn(fz+>*%J~=-7B2 z=w|M1KwotvVy=X6Bm&y&E-*u%bFV@i(Qzs~wWr7f>Zq-Y-&+S^(4qVQDK*^GB;X%C z%8$gKL+8#ajvXTd`Kuhr&^28sgc8~W+AeN;*~mOc^C^djC>89AY4$%#&ekhWm+^V z>k=vtC2%DkaF`?D2XE4eG(JTK3t*EGG3Wf0JS9r5v%aEokMSlv*$+8t>TjKm134D+ z!ZR&(j7UL<)H1@-`KE>^HL97SO2VN5Mp!&w`+3^T%OCW8c5#Uf&_J<=C`8bQjn1&q zB+%!=KthVv@)8mAcWb~cnxor%rn_YumM0Fel#GPt_kk(tF$O|Z6Hhv6=+03AjsUhC zGL@qzh6yw^n;~kPAsKO&zv&5 zD!9B|DK)FjE9mm_K>S)5(kK$5!*~|HPc__Bm2FQXH164MHv%eP7w?60`9_{9YN@e1o(06lw?I-#|n`utMHcFvBF^` z2P>skF51wbPih;+NC^ktLuFzfx<|gY4yb1_d%|>G1Rnz6JH{dz;={Se@GZb@%@!(R z;E?SY;fQ6a*)FwX2AaQr7IzasE$yuWA?hN`?ekJt7Sx=n6_}={2_&e*2O@c!WWctP z$d!LfHfRNOt?llX&(H(_A&e@h1JZn5kzoOdEp;SBE@9_ZBWuXFB~qClz2zJ|Iu+i$ zZ>RWqc;hqpdH_XI>>fD5C7SU94}PIzBqMfhw~r$0oOHL`_AM)L%dvP1`ESd=PhgSO zQc>P4{=VHU9}fp#dnOC+M3qKejm%U|f=iFHO1SHItAu+TCCEk!ImpWx6GxTmdHZ%9 z^NuEN?X?r%bGIx#kuO0v@llAg%qV7HKZz}5Bs_SDC`y0Vij0Z5ef}oHl{TI1r-?t zTyBIEY>xHzf91f{-CsyV36KCd2Yx_4iyVZ-Hw3)ll6-gb6nqhMZ6L4D#Wzqs%)M+Z z(hMb60ITMmJjUJf9VD^&Wo-TjFvh~s|Mcc?4c*?rnjAUFM!{eGuGYQf?|zI zFDh7$&z4sDvBM$CVXlDCt4-6O3FB-Jc5wDos|YJgS4)zh!c@fXG?#1zS_Dn-MMvTkJw*u7FlNYudS`C6gKAy zj;;+`1vYzpI9d>t2VI}=rZz}MbL>$F`kr<#Sqy2p&F=PQg8~>+Zqb6SXKecq)I<^Uz!988Trx&@GOoSyQ$fE)6x$<9toov*TqB76R5hV12TSapLV#%|~EaT(d zD2tmy(|Yc~Q*8?P#=NbQ4OQGyU}@tipxE6|sR$OC+$czXAXb|sJuGb^-%JDxBiSwGvRg8+M^Acy>2&XWZxh#-?-~z`1 zAfS0MP6-gy*bEy+cdQ+sXt-Gg)_&Z%x~5|(QhZd%H?7#?zHv1^2zs%yMXnCPKZltM zWrATUuULp~z%21?UNl%UD$BZXNE0r4ET3Yihs^_IK<{yt0M%-$WMVtd|Nh-v2cpLh z&cTi1&bONDZ7f;RJJ?OIcaVhTBm&>c0>N!VsqQ6XuuHH3qxs&pX9&B=0r@gKm{%Bt zb9Z!AflyV_h3iw5kb-LB!xxe{SA543tTpSqQL%Z=55?jOS0n)uJI(PA5G|p^;~oMk z(EE#5V%^k%D)5CBL^H^5HC5wzTGBPWEgAwMpooo}H zZFsUKP8&UueS|;*e>4}%5za+;E5}AkokYAE+350C`yFh6o#M@f zkREp72AA{-#!od$Gu1xIoR2d#EQ%F4(|;5u#RcQGOx7JZG6Gl`y95QeTTVa>Gefqq zMzAs9%^ws!jlB^_xYEoaE0V?^KT|R9>~?Y-e&8c!B>+$Ei6GHnDF_JEs5heUBatx-Xz-9#b%dzYK*_q~11u+2n$> zcgamSM8RS6uvwr}oG|W|3o)ZY*#U5Rs9zYlAMeRzK?Nl`i?q`#)oyP%SufpA8ZL3PSy2*f1K_6$9&cruZ$_h1qEl{W}-~Do#E5{1NQ~-j-^1=CY$0TWaz0*)eCkwnJ zasWNr%8e`%pAxgp^IIKyv>+l-Z&@ZuDQlmDQ#+q3T^eo{%X6za3eSF2Xt_uh720P% zkg$-Hky$dl9`OyLvme* zb4YTYb_@xN?>!_LY4(sj(?DhF0IJR04bCBX>ITP<{0Wg>LlS_{U}8!qk{HtgV;cc< z+CDLa%fgj?;w=}kSHsPp%X6!F1D@8X?1^q0-9sZmTe|{3>Ub-IGm`=8H!oBHH8>eP z1AtcpgL?EhBc)TnJ8 zi^*3#j!{`CAhbrMe=>H{hl4Vf7#{dWAR2BFpWGp1ho+WVk z#|11H@D!r<@FYs0dtCax`)*=GSm8!wN(%h60nxmA+-$jwp+p{ZCQO6?-Ft--BX=oytmyk#{JL2p~L!_ zg?~aRsUg0X5qpT++MGi?@hQg;A2c&jF#7A_DBWYvn$@i6`+o$0c@R&DF5z{}VG{K$9F3=G;Eg|X zK>s>KoD)n435R9ZFFscwTR6o6**q1nL6+J0)yVTDM>Vm;FBi3krMfwh<#B0u=qc0C z$yk0dHv~HGSFb2Ic%jM~eIZ%iRF7*qO-T)Su{A02cCNDqfgh`Vf z6PPsElTcuib3eL05XRiL)(Mjf7CK;}BhoKS?!(4WCfP)35n!UJz&F8|ON+2?TWcE{+)&Xg7bE~lt~4VTQutC=Ke10Gx0oSXn_qP~Do3%+xsA)r`a z_zDp!hZ-{eq%7VS(agX%RqitP0rE zrY|UZvlzV=ST$|D*j>BVb}xQTVF^yJbaSCoxZ&_hXw#83VJAR-`;)}{mkfahq)NG$7rdR+czCjgFZbO7K`MEV6l zy zx44_X3x*oPvB$tQK|TBoWj>S^%DDq9y*Rq-ZqAo)AQ2ZIuW1~TxtOXV#jm9TWI{WKRx(U&}oJ&_K)Ys0W?U`+LLzy)B6J3%!z73wm2C*&;x1g9<3<_2wNE zJTi$3X)C`=%Vb_J5PqcZO4GYIfyET5&}PpjKjMVNe;>AD0*n9HQ$%2KEzB?nghc@~ zQ)#05%sdAyozYBp`hRl|K@F~F)@AywMY(Cq$akzN1K zpPkUyWlt7?#*gn}hXyLhkC*=BgvS;e{RJKu--#{{yym=5J~joB|DEfA$Zrto7b2fE zTM)S;U_s;-75Gw!q=f3d_z;T9{xssNg2W@W+8{aB27-WO(?d>>JZjGZ0g1l)fFOAl zHU?>s+zNda;jQhM4GRFatMnw9B$V*Esh+H?`g2-GI_)>@*2uTvK z1EUJ++jd!^f;v)ScDD=dj!L&5f56%8Kgd=VUrM*f!IJL)bK`Hqz~f_~7C--qquYxS z>9^av7hBzqX;!zdQ-QDDZEXB+9mZZ;b_CqyKW7%(r3wI!keCgCk#?5_0FV9A34r_U z`7QuB_csRwzzby9Q{$ev#sPpPMEV85>rEB_miR3IT&n_K8vrB@HxKOtffMXv1p*@_ zW<%gGyUzlFb$@U|;D7DuEf5&9>VObV8A+q&n7DRq>t_6`RRNxyzWRX}jWxyccE?8jj#aPy7!{D>uTfG(-+-1jv zrCMRnb%DW2|8qbXJWlRC1%v3-4j9xS(k~1;ermzsYH1S~%pa=2H-y3U^jMck8}jUe z1qPptal)Ycf1NP6-Hr(imfO=^U~mFW7YAk>){+BH!C>xH4j5dHNWU=HxX^;ZENK&9 zaES_hZ5WU?TnkexT54T&AuP4vXRgED+>XVXZIf=Tx96~QYtl-z+dBPvI1VL>3p#u% z2H$Uzl}>ePRh^?#HzCq*r{2BZ>eMfM7IL?$fa+9l4ZvzNt#_K*Ii$6@9m#uPpABmc z9@E0vwmLlAEj#dw?Us%QPOIIx5l?hBa+GhwyuzMmU|K}yadxMhuA!|4OnWdz%;zvx zsW)e0T=~YT)ZM(Bz~YUK_%urs`G3l1UvF4mW6yqpWe+UR2887;!ib|ivU*lu>40Sg zBK^YhuInsV2G6lzIYtG(2`n!HplEF3MgPJHy-a*F;Xn?NgHVyK?3@cz&wh|+p*_{* zt3wXd;d{wxr;umnEJugWN2K2lKXk3t;c4YohbO7PH|cO4_DCGzl?v6|%`d4D27Yj0 z#pz-1fn8xwXX)ojHy>bNpCtR7>gR$h9R0i+k$(HRrP1o=Y_HYN%TyrM%9Li0Enlr# zo{`pfk~C4yU?cns_SBUQT(KhE0hnZFDs(<0x0~v~qnA56@H<5M?LeO0f#08Pb>J=) z_}U$S+uWx(E_MeF%jm#8zdAq%d>I}1?`4h-n27Y-fyxEe2)zA$s{?PSz}N18a3#2X zjy_ZSx!u(1i`BZ{bkU`$I|ed+wI&iAadYOt?}^LixPjkq=lkQn}LI+ zZXd9IS-wsLJ3m1GqCS? zA6PzM`(!154|qR7`t;Sc;$@9WG@FHZH*`M#R%d*#>x`zIE+j}QDl zi*l*ScMdTAsrpOIjq?xOzKi>BpD46_2T}fIUsC?3Kj^prDfpfECHWu!lKgK)58S`& z`_C^D(2Kt{ApBG9i+xG?$Mj!*8|P2YMXA2`ra!fU{{z3L{fYV1!0%J-dn@uK{ZE&X z#y=xb!1^bjF+l&-d*FATdEbrwl%INEZ64EK`H6(sQ+#^N0Ocoe?gsV0UNazk%D^m{ zj}M$*wi>q${Jx&>yMEyJJTGC+9Jqd&f4*M%r;_3Q*0+b#4*nV3Tpc!!4*PB^7%@C6 zdT7}AK^`JSjN};GyK;}&5qr4@HxCaB#q)Z!qS0*^Z>%zo!Y4It!|;8TaS^|-WyJY? zWV`yR(fL(V@2_y}8E%^zt0%NPv?Epgp<&;z^mbMJ6cqofYDS#JtBg3`rxm0#t{Vhv zhKpekuFa6kxQVRzXW*<|U~xE=gloHkEnBq5f!gkfHfy;t2yX1>mGpE*3Swq{6j@A6a{ z+7n4d@IlKC_swmsi-{VNjQcKhbr>3VE!67pu~G*w@;oZ*4_|iYZqy2w`t)?|m3yl5 zKY)SxKC*Q%q)$D$&>Az=*0#J7IWAn{@t!8P7%Vp~#TC8z#j&?O(|oUK?(we?M@RUMjT~>D(TH7Wd`vNJaXZ({K?L`N zxtr;JFci-zjvuS_Y!12hhGLuYwAkml@U)S*c{LiMClc@DA;U2Xa>&KiJ)}nMxYl7q zPN-*_9@lo_uRDI-p7?d2>G5mVMPEjq?Z{*P>^kH(g0)s$kGGKK;X&3w+{wY?2^w$g zGyjZFd17r1^6^GZs5}vWSX}zfve5zf-r~WFSa*&)wwR5>aU^W>S{Y1K`Qf@^yLr{; z*w!WZnQjE0^&2b1$+lkJ{4D$7ZZ5_fpmFnR#9H>c<@}8CVe%J+(M2eMm3Bde;83O$F~kc+qew{Ge(-FXq0# zs^y=?1&xtHjtxfyCyj^drCW91^X|&$aZ^>Tu>ofyT5xbvRs{<}kwsOwIs~__sKip- zZUDdGM!Q}L)YiwMn(T(LI4Hy2HqW4Y!a!$_UfM#=Oi$phKHb&Q$(Js;5ywd3FbW)e zU{VmmBRP)NKz;98fqF9?G-^*GH?9H*@JgSI-r>UdjNTnCy>qwV#79pM_Ztmq*&8t4 zjo==@%}FS7d;H!fy})62^DQW_{K+zu%j-oDN#2UrYAkW_OKz+?FZv(AC~^qTNbvr; zT)n&~`Zw0Mob5--1&=+5P#`h>L4^*a5VMx!zsFiS7()Lr31 zz#?4e50wv%#_ghdIzRw%YIH+htS6_T4Lh=YG)3J@m>N~iRP_(~?(s%*oQ;@htk}}V z#*}U!-8Qjw#g^SnbgkI38qZL?K&H9S!sG^>rr)9|Dk@vtp4QI6zbd+$BUwZEfJCxnBbnKXRyl~C>r}K`s|LjvA z%lC*^;;Mu6`+{VL^F7LwYgPei{V8xDpc3$)KHNNP9)dAk!rJp(^gRMYXPf+))uFc} z-I!~6q~DA8P)w)elAwAZ5nKz`bPFx-hiH>zR~cs5_yiOm#Qk1KratP$XnXv`c5+K!dUJst$+5H;M#fx1s^;BcQ5h@T9n zYbPPXIdhC29~?hUiV&|>!2753NZi+T4HH_IJP{S^9UW*qTEDQkwhFhJ;g(y-*O49G zssWPjFBS~d6OHP3b~y)q%`PvlsWSF3qE{Iohg1;U$|OJUjwuPA z+2cR6=ghWNPZaOMOAybG09Cx-j)<&#E11XV#JT59O$_>0ZCZ_| zyCn!z;dwwAhSC`uJaRG5v<)7ieKI!ro#v&L5ShQv1>kfFob7^L(Oew|a)I9f|f531CkR8S&^YZR+@`S zL%!#uvjH@B%b|z`jEb57mZmZ`vr(Ff$p!R+3k@ANs*{Jq9n$WWPw@^HIP@6!wxvx@ z76#k__;KzG!+(6L(P{h(C-QNc6VR3u!)OE62B2&UY?_WkgHtGi0&9t@#FH>0UOxtO zFz%r%>6&68x;JF}-D*hmdijR0L$U52ck?Ee?0XUZnbE+I@5P3Z_*syP4n~Ioccjv; zJpWp_Ibij^zq^<4E-40XG!d{NUkRFBxhlg+Q=L0&W;{s zy|%lqh9ZbgjLpVo!rk&3I+^aWLNIFDWfEuRqVr|D?57o~qqzzXRJreZozy zkc_V~HiOIJ=bU1CsUkD*hv!~$ItTO=^+H$-_k^-MxW@4z|qAn+*IL+(#QWA zjA&KM4w>cd=8?FPjPUeZJXj5RFh5So?#0yzoVDN+;jKiPR^i1o!E_?A3l!Jk78lY> zxRjb!><<~wq>wrez-LsO&<7=d&+-ya6gRObFu}{w488OVwMZI=!$Zc|0*6D)tn?X+4{)MO0lRj;+Vj;N(BcX?V5^Y^g$gdl7{j_fw?e%w%vwhxL=JbkRahdzbTc z@(pQYW;rTD$AEACFyNbC4fLjKVc$M<)%BA$t#_IXbm20tv ztK|kuP4Tj3BV(NYW>sW z!{YiLj&u%6QH*t{rxW*QCUUDk#(g8WXgZO5l7uSq5d8EbCRP+6*r|eGX>)H@L9i3K zSF0dS&F79&!LmSWj6lVsoUn`=1T`Lnyq+**AQHKS6ClX~M6e4Qanuo?z-wl89<~dg znRPEfKcepQ#Fgk4AZ&P~#zWmp?KfZkuX9XvC2xA9d*4@=H+jFRys6VEZ`$5R-gKmp zS(&%C%*j#3PG{!b17zMqM6u;gj?A{eO5|aE1y-tN(r{$P{fm%xIWpH-(k{td-@mkL zJ~DcGXY@KV`uopF`C5rHBjsx~e{smyY8mNYzE+mbxNZ=p#KjxYa6|K^kwEqjM*_`l zeBgfKB}|HRydb#`5(Q^`ZN~eeM7+8=4dy6Ieuhf3Y_(V2K%l6wPVTVA50@o*g$|U|vZ=Es=-KuyWX$D<*aDM=|+sKiE!QlWC>q(H{)OqHhQE@kZw}YVuU{`fNb-l)p&{RN$S~# zxUulh^(gH`k!`*tatnJ;-B-T(=h`fH%T357`+LYY?*{KV$ara@YuZ}{fzl33z6rNq zAa!ITu=KWUfqe7s+QqX6Iva3@nt*GMs4y%Np=lWY4~0g2Bst*FP)iqv#5K8V&}shPHi z%aTkY?)>4dE0ODATH+#)9@n^XCA$V?DS-ieR8nHF`$vFBT4I3v8;^vpMNK9(kJv=V zWWpSP$&Q|w;*oVU9x4iAEe*$q1^Qh>|BXtYK&2g8J>cM0jWUp6HK;}f(U$fUK~wb1 zNgt90$sx`-`|m~nEpmbWPkSs6oB#Y_ zufE@S#FzG+BtY7iN&gCxS3295Eb2x7Jju$8_a&M1FIkp6+4+7?`pWqA_2`NDNFiE= z$>OI-1HQRB%_H~0(4uRQ&?m=QAnkMT1Ag;Q%-WkjbCll*xS1F9Eg!SspvphLPx=7O@weEV+#(VroNrT*w#Ntj-M(J@UWb!@fL^|o@xCB=pX2>hq2S-T zXU{(P!QWmL&vPiC4|6J@ahG^IG|+9aozws+4K!REU%XLe5D3~C1cDhEB5;E_DC~>1 zvh(Bl`4ra_{nPzv|%I_s72F_57<>f8YHIHwH&#f@U=!d)`-rw=R1GB?lMx?ppQ)t}p3f2bqqCkcd9j?%#82>ykYsO~`m zP#W~>zNhC6YiTpycQ?DRts32eZbfV`7~9UEK+SS>wQ}T z#%}kGPa;LOmWM)$Fk*2JE0K1>eg=w=`|+CF=FsT9_Wl-kI2f-Rf0y!KOPBwgv;1kr z^}ZJtTpr54!0-AHYh_)_j>v_+eGMDC1{)or?4Uo`^3P~k)fNrGBtoimHy2=8)(mtF z=Xl0p)0~Ak%p0J&_J|VR7ESsmNFRL$~XwKS_C`Llsa<8uP;+w>ZtH?1lcQpY7*G< z8!GRRWV0^{=%^^;*Ycr*ETDdD6lusrEqSN}P2-u^rqnJA<>_h3vJ{LtQbU9WR@=r8A>P3DcG!4hy^gxfkA`5X;x-q|*3jrz z;x&{FPntsz2eZ#>=6 zM4Ty~>~{xn3qtCH^o*nA2CyVWvmjU}Gb|H+XP(v@enOuGNw($M{OQEFj&>HFB0bd6 zJ2IFb%_bCtyX;g!rv63+$&NOtAONBR7`l?%*xO(=`SC0BG1l%e55LlyEViW7q7T;k z*^c!#$U9y(7}cX-E$1;mgov!~q8l(S8mVkLBSF$kWzYE?wf8&(@GOXRkBA%{>vl&9 z)kahHqlt>1v7o+L3%Yt)bUuYmsbf1S?R7s2p@FdjsQg7I1Wwr?@2#S4A4F|r8Ky1} z*6N1Uzan!qnwz2;sqUxv184t6qJKr^`2a}Y{$V4UjV;&LzgNNDti9la96b1oOO=QH z0oQvL)Pp=7{*h9w><_ltb{!r>) zSrfGkc}7D2tz-SJp;g2C=&yMwJ_?l2X$%-M>y64tz^GiPE<6i^!ocFb3a;HH>M;y1TMay8a&MX=youVC;sjEWbBzivG(CFya|)Iby-;jSrZfa z2?4CHv%&osLIs>HzGrlI<%W>2J;WRAb9;K+S^BrLvfc*)-ws|fM|W-4ecKzxP}N#b zZ`Do6$#yjAuIE8r&<%(hjT@OfdWXL6gRrj+vVMv+g6_a3O1I^%__Q5bN|7tOBd#x<_6j#E-*PVIg;5 zGgbBg=xmTC)T{Fo$%Tkt$2&>(GP#Y_Ai2z_oQ;QCJj~Z_<`4kT0b}R z9sCDc&$|eeO{QM%Vwwi?ufw0Bb-Y3Y-uhrlT~8=`DDaVhFzUYF@+ZDZnI0|PM6676 z(rO;)h6ZVPn%{{}8IwkCe4@vvKw*^5h4}~S$#s$30q}S97ASW@;82!j4)XWAx|mJ!fLOQ zZ{@!7HRM~E=(c=Iw**k4(yZ<+fU@OVBAl^L5r}XmLk+34rn?DjTVLy_;-HkhcdN|m zoPZ4RS+mmDIx-)Wve!~GE1j()bCFZ>=jko^vufT$gtPUwj?7+X zX8!=0Ej6>!-8wScN?#e10hGQ}GkZnp>&Upy39Pn0z)BtC@H;b7&1_m3MGX0V{~0NU z)YBQ)4NA-2h><;7wV_oz=Lh6#(=+tBef7;}XS^>+t_3Al=#BLp`jI0u-uKlv?}MFE z`uo26=9eHrF};Qas!>nOtcCi-Jm+yP3}dh`ctMs*NRhDFhOs=jU6krExBQY+ zne?YIG8FXZk@dI^gvuk>_NZ0o;o>GDdoUvb2knM+ND9 znl*eWev6wg$(G)))uPTm`!y_?iQGAVhcI;tNHF)J;}Nm;qDM+_3i=NPW+K;ok#DBd zBFY`XLx?`D)j?OLc@Vu0(fE|vh=G~T4I|b;J!An3D?`fBk^=3Iq+k)uHUGwM8bNjV zjUOqGN($VcN{Y@kuSt(DZj$&f6!6lzOZHjqPh(m&Bska)?1E`DKgRu?s7`fgu7pG- zJ3$56nPXIt9br4HPCTN5?7>nMWYcG=AlrPF3bL_7RS*Las^I|Tnh&6%x?xB=9Xo6w z&9C?GQ-USOR#}Ej^qXgA%!h(Z{Uqma#(Phuel}UsYrd2v|C6DQ@Mg@HSkj+_=YPk1 z*OP0Vap-&Z=R{79?H>_2#2j%raXE6XF#NM(=NukA2dV`v?ys?UefQcyv&J5t6+HyW zp}1eSl4p%A%!-25$VgO{ab%rsY{mEqe=87+*W|$%zQCT!IKc!F*c+J6D>Wl2{OyP4 z2rx*adth^Z1@=C&|2ZVp+QT^&JvaIAk1jGQ@-Qv%!(AcaFR;E@yo4CU#6I{JOSSi zCZ%4KIc99-#?HL{C{ZsGH`RD>;;F+4jqfXxx*AP1$305kISg6|AfL|&dTR1pB>I_n z4ZuUH2w7Qz5-6}`sID>W4(!Bs63!(^h0$kjC-H-MCYPgacI%+G#G z^zCs<9r~tH9o{#Ur;hrja@6{yK3zE$w(*l|AVCCj<$)I|uhb*Qnf2%5LoJT35$^O!Zk4Oo^STzBVQ=8ie7R0=*+7Wb!f`eij^az@c^kRUb z+RVA9U-67}K36--hAH-MSnkvNHy(%ZD50ZhDui5vihuxJTMKB{h?)WiO}X|q-!%ve z_O-z*ck@bwc(h5*p&EB`Qu8P(Pc@;6dK_`n;}#o~_+1mr13ADTUew4DYz!vmWGnlM ziW%hAvoUVctoAcemyaK5JCW-~kgoG4gD! zv@PtasO82}NEX(AYwJwXMZoCBv*t5*p=($tykX;4tj}0c%mxIqC)I?rC)KlARmKKV zym0B$@u}0QvRzHrPk@!>aGdJ&O{!}c%*)feT>dlP;h~imkrzAQ=ulW`x|=%?({NOz z`7a3go8O5HjeX*Bw{RZ&&)ggKbu?7L3{J+wP?ywHmHrJUO>$7BdC%b>>xMFMEH~J; z=y1Iw#sL4dK%a59q%QC`?~T^6rD2ppdMalb@YEB(Z^8o&!VScj#eiA8EFe|D%ucXmBm@vlix>H^zy&S$>3yS3yaILB4cpqlPHe0MBxGDxb;+|)Kb*Y zDviL7o)&v`TxYBmm3`fa)nGNA@FW~9YPvp<6*-w8jU12WHR|ORb&(^xRufvc#kyUQ zTjUGa0_H>w_yX&Suj4H3`mMbl!~RK7ne$Ao$WWgV3riqTgT6wP3%jr(AbIHW1AWVM z=-TQruP6)U*aL1)L%75hV=MM50K-wx!mX_BPRHzJ@#kyMg}$d4weu0Cxu~ZbuadC6BPA0!kj?$b3-p2umuUyT4x|i%3ZYlpMmU@v9i0D>;N@TsO#;3IODW=69}lv^US# z&-axFtj&1eR~~R{#{0hVfCU-v`^p2RXT0w#4>&vHeP4ONks0rM$piZ6FZ=9=Z`z6Z zo3SOnLsC+!xyAc(f~WX3*o52J4%ICq^cYk4Pcq2XEWIBA&BOABZ9*f~KQ3hTCY6`Q zhe5-{Viy^rb&!r=S-YpW2~V8D4S0Wehz5Q)nCM>bDwCD*Nk3zhhJsmsfbA--Zn|h?vodH&}&Wus5|N{^R2KkFnC=fa9cMs!dIN z955z%xZSDNO54LOFq(-;I_a6|i_@#_S4zpwxs-JL%V;>>(5~1!Kb)eCLw^sbpSb3m@uLy znFbY9ph&avU$QY0Lj0HzV(Z7|=e$_{$Wq+gCL1DQ>sqx5qqj7-VH6qQ#jx|@SYQRq4sKjHh zi2Uf}rz!dYu2FvU$!2olB5rvO!PY4O#JJ14Q({5HiV*v;#3mr-LF{{ob!fUJ9HRKe zg_QV|Fx~@wb6?!V_tm!WC_ZJH5=8w%sU~o;V@iE|%8WQ=Rlis%;1qGuMteW#$9@o9 zfP9ELD3mx4u7PBXuQ$53As61Q)B|8U!y? z!3Pm6Rl#-Un|GidV^XadKa&ANTAC>piopmfO?m_cWq((hA@3>ZWxrn3OFrc3B_As3 zB_Arw+z&z?>y!^!`i~7?gWvFcRes}O{3CJgRlJ6yc-y{LMEL{Q_wwME;xnv_Tzl)B z68ZXpCGv{Lkc{gF*=sKcq(}2WkdR(~WBZ4?TOI-mK^T_jZo&Q>V>>YqDtY(X>{%l` zS+mC0Wktuj*J^`Q?6RzA)V+2}PVDV69Fc(bcfqV#f$}MXqcel?>QP#}+9SMS%MM{L zu>;w#an{%$WkvnzA|hGQGXw6mp&TuiEYlL&7vO3acvLAzNp8v8vIC`!3c@N@N|Cf6 z5{(@akL^DqT8LH`x?4Wt6i~7`KQ?}`{YBA{6z;iO=t2N5AmA&O7X|PkizGWEwYFA9 zYVqKhB6=!|gu)rQbXclU8;fsz?d}^n(L^ci4s1YyImp0DqK&crZsYvC1=t#mNIbQ# z2RD5i%NGCBp9d#6TYBDzed1YgK{Q`-;@a(abQ&^k!nf= z@Sq(79@aaW4Iq}M9uy)7L^jS^z64d>j=z66{@M`UhCf>M{oYFdll5GW=ZjYOUOer! zf=}`b@XDk(1xt8o;C6SYA`DzC`H%7>*pto1L%g^$>|yIasDaeeNpo4jQ2&!O2>P# zFN$d?dFT{$W8Yu6o^i2$Rzk8^-=%_FsTZgqnOKpB0#|b{F&jRr z`F+b5ft>j7>SG^NyHihG#mx)tguuWC;#U;~TCW0=|E#hY3L9^lTX9G=Tz-1=+ZfzA z@XUj8sKLW@oe8rVj=>PyV(4%HM^7lr8S~*YvS}=oXH3oy#P1i2kjN;k2lHX8S6R@Z z+|wWf@JyM>zyr@JMp3|nc)~)`eE&CUdRB8Lx^Cx8T%|6f$DFNa;<5Y!&VyQBaX-3B zWa1RT(J=|t3-wj*z^i&%dc1qnd-Enp*nzV0A72{lVI(`$=k9e zURe~$!NHqy2)>$-1CqB75Nwm693YLn(bD#G<*4lFdCjz*0R@LAE@&n6^1;z6;g#a+hU}Hn{B(afnw~R$* z&G6^N7ZXFkek)5-C1v++jCF$zg0MVmmR1fMar(L8*I2sSBG+=5YH+AHa(1lS12uxJ zFtCil{h0gSjgFR6e4qI)-H*QS(!J{Q&e$0LH9^s?k%A>hjh7 z^-J`efzScIfMd|;*#uHybd>eoy8cQMMxswcM!H+hBCOCD&N^(OA-rX?xQ8x_exGdv z-x@v9$}ZMk_>N*w zWUx94VxJCa_*b%)QjKvqn$UV7La`oK^g{RAAfWqQ_}`l~_8cI@SiC^dVhZbFee=Hn z%?B|-Yo2vCAB)$sV#FM}OQZ-tAJ4JmirqJ6BN)G78QK*8i|S$WBTW7Z3bUta8~GIoQ}g!b4xuR0q&1c^GuK2HJL=|uj;Rf(Lx z!rx`L`Kppm=S!z^z)-%fPBow>Y4Hfe5~4x|Hak`ILXiHy;1|1pfTiVN`9)dZ1odTo z%g{$ZaI0ECYPeQxU<3cxqcPKwx&L|%`|Eiimo zFs4D(0Its*ehHR_PGFZ#Le5cQq2#K;z7>-2<4GMfT#7|=&7C)+g2kKIK#Tr>pR3H^ z$FP6z6@LlXVDl>$f2KSPKwpif$N;n7MEeKuSA~kytwrO52Th??1w7G}AotJQS2DY{HF=SpF~KqocyZ z63k`OEj9qsN$RLiUEiA(>s;z&#e{#&JRe$+A)iq4hMOPyX6sp_(=bp@!#XV~ZF|c# z&aWQqV?LM?*(-U2wOmqcU@wGX$k#Nm*r4xv2Di>QqMeo(aK2dnBWS^qEgRXMYHUZ< z?2`GNn6eY`IJCCo^Ex!MK!$F_8#{C0R>2Q@k7FzeHD)8ehlmR&K8%arC!MY1B3Dm( z&}dw{3a&0d5Wr7;un!)G!QKUr-tr`u6|L!wrhPkuN4IHByZymooq=JYl`TDiR^Wd$ z2UfuW*q!&lDNbjXD>1nT2`-!%XnO<3iWja98y&+s@#XQHRVSF!TC=kfb3p$t1P+By z^}uPM=|`HYOGtEp0_eY*CHrV6BW)2K9~-GOd6|ATAUI*;J(|&t_Qa>G#NVoLe9CJ4 ztpQ=&jrjXj@q#eiykIjQ#de;g%7eoJ!w82WqutmQ+dXb?J46K)D`8Ix31yM2F|nR( z4WbA2IU)-RtfdDZ*kw%2Ha50*4Q@Aw1dJ`Qx3abNWKQf2cfi%@+BH5A7(U3?9o>RM z{DZJZ4#p>~ig-eaNvjoc75gJt@{70W4WXVbbU6JUVD~L!t61;Q|0dptdfIe&0_fSI zwfcLa?`lTH%2;>N{Nb_gaj@5ZjOGQ>XlavXY)B&yXoSqCjtu+9yySfu`V*Wt&v;*u z#NC|f{Ug?oL~i0woa*3ljF)v|3bfhW{8dY$5SPL~^F5aU%4%suIbsQjMNeTfe)n#K z`2zAr@_4JZ+c5uR1wwFy^RQv5ECLjJHn<V&-|fzl1YEVL1mfz5z#v*d-qQcjmfV?oaP!T8eGP8qzP z0)DT10*n)&nb!Wo`4W3NFNlLp5VL77z%|*3C`cuA$GZE0r)39$4Mu$v>%t)g$_9E0 zr;?abrEUTT6NNbsD>?~V1!%O*yIVjcA-;#}?%7b5V3O+L#A+!aq1chno3ZXp&Iucp z9&ji{A=h8cw~;iuEtHsjRyX{U>+xWg`3`I&gT609#y{qJw8R`-QR8|IJ0Cf3w$)sZ z40_MA(YGZJovw0bz3}hRucYvioaM8c>vfwyh-U*kUE6#c8{lWO1LdG5bI6ix=+<9r z005^I*Rt-|h7$8mq*5wVGI|F;%Da(^#)yj_9)!)@i#>%uK!7tI>n6Jhj?POpf}czc zG(!Mw=5Q!9hgyznD=4GhQ|qziX+Xn%i~*sA3m8`w;LHnjanO!H3lug!h%7X&1K56; zC-x#CV^p#4`kd$t!-f6Xg`;;sgY_-?HBFqC2m6cV;y=lo9UUV`=Xa6 zMNJ}R3_O#UQkcLU-YIBBiMCGYd+BS^Ubw423Nv94X$<_}wQ66uA07V%DapTJ<#opWn?t4C$|9V( zR0G#(KD?N-e2xdUASK9Of@c|?z&^Oxj8}T`^yBG=Nj6rfLjq`buyhmKJSj2q4LZKc z4khy2%_A_G!p5G+>tcg~xvd+2XKr&v6R8S(KH9$P9X;yR8LmIJ|3|=+{vV$PPIZBa zwEIN<1b?CXZiHggM19}>L;6zi49D41{QiOHZ?9X7!VD1!9@309@2$~WE7eTk#KKl9*0s! zw{>tUoz+o60Y(r}SYiviD}=>| z#P7eXF-7_o0e!15N~C>Vj}ZQ8`7H+mm?Y8%dr}eUFrvG{iEpoaAxESUC16l{3XlFb zy?iD}?YEjOuz-b=;udiy%e2IZ@z3UDnb#oQ{iq3|50~b^mv6yhz?f9v8{SX@!#4v^ zFd_uo-bjG!NNUoaS7e%t2sZdrp8ymD0f|ol3QhnDP5=r{018e3R^hv!Z6Ua}ne*|C zRTMyBF$uk{AReENeIv}+qK8T|6C)n@E1I+v%pGoT1EoIzMbdk|edfPFwjJ~+QB`98 za-~wEDBrC_<38}K7E;75BM<5Feh1Ob$fz5JwN_h-o5J~5(3Lw__cq>SK^t(T;Fim& zaU(BL#Y%=0ITxI6a31)Bh`XXQ=^@NQ^SGzSQ6umUvIGN` ziQK7c$YGQ1K1D)cyDioSAcM)Sy3raqh8C4woV>>p8Km12`>8)Gg3~;pm2fBqI$6?{ zr*{NP)W~3P=ORm*Fk3Xr=EpN92}qVbKfsue-W#7=0Fi<4CGokQ_}r5C+*9@VTzodS zIzIO-Jb z;b2Kt;=F3J@@b;~PT``#KSwGO7lQ6~njhs+$p5YU--4@*!*68nUH39OW?UloN+cpk zRn`3v*C2=)p8L=lxeS(cf%(8SHm~pu6@s|e5umxeH-Qr4Ed2O}Zyx8v3)df)hJejiDHuj1+NGvl1UeTd>$n=fStH)Yxdp-B;L zm5^-2XG>53&Dn9v#{Or=i_n#0cARv#Jv(T659H_U2q6(c&W?94PR)+HPy0WZ9nSTI z^W%#?B*-~`u>QczsNea~@|*v^%nyI>`9U0Sb(#_NJ-D61aiV5l9JffZJm7KBB z_AwZay&c)~%Cgn`XB&}kBkAczQpx^S%=Gwv>H75iN4<$B`{U%_)^jSF-($59s(_iG z8V75^m5^^8)cK(^nKhICW|Y5%<-hn;@A6;jKFIQSFl+Df!P1cvOTYkM0j)Hgz{7SnBI8npJ8)Kgq%p0-q6}UCq zO{XPDVTtWsIdHhuQ(e;jRt`o*(w+FF%G36?fu-0LJylEi--ljr!HEz`1Dd%3Ut1KM z8f=qe?FBtkOP;P6nJuV>;+1r?SBb<=_$(>FJzP|FqC)z5g`^r=KAL z7R(;pQ(c86*+$>L{y*a020pH;O#Gk8OxkH1xGAI%u-a(D8W_}IswNJuxyc=Jr=8GX ziv*}^m0F9S#AK!oTakoGY%ZrGu+*3Rh|2EbiYxxm6-#*$GAV76mY0?;lA^LHpiVjh zsbb$Ko!|F4_fB3)VfFXf|L0$r+;i_e_q;skInQ~{^LA24ksTJFah?z#v#R|g!bcZ> zo*cP_siQ^7SqF>f@-CPw!LKDb|6p-|yHMhEGX3YjL%V46O!@Gq{;eO>USDo`zdF|| zuU?n;Ln#jpa#8ZMgT)!%1rz5{!maa z08l`!g}x_%TDQQhpRvPFDP8=lJH&pb+;#$eOXfSvDtPoA9z7`DfgZKL^1B|j*C#HV z^7x%1wcXBNU`Li(t+iSEeODS;3r||BkkFYD0vXq3yP9s7=Mj;uo&9#XcfL#r<>tV5@~qe_Mj(;oOMJ4p_w>{nzx`K(k7n5OJkrt{Wr`vPy-K8Y_*`?k|6w|y%n+xPA%?OQg{zH8q5 zH?(iyp*OWp>nleDJX1pwU|W{G;PZapBU!f5`FY1js1L)HQOU>V*bDcIsf;|yZn)M@ zrm?rr^jR|5Y1f)Yk4N^tFZq3FzTff4DqZX**91n2x6&6qoV`Fka9oGo33;&Al;-EX z@hg!OB<8tVcDF?^;J)m!YosZkeEX(6OQwgUQsN29x3+pV+o78${jS4<$m?Wn)3 z&IfjWXL5IHJBK=Tik!0WT|~Y^=@8VfvVLi# zyhL)Ay{mAOmGkQ-z(n>SWyn@<_mA5TW}o=Nn~Hf*KUxk{bVXNE5Qd;qLN%knHl+d` zk(hbr2m6}xXo^xCI+1$4qWvXW*!vF}HmAIguDl5n9+Xf&YkbR}^YLe7%RlX(USDN> ziYds5NJenUp4j}wJc;92OdJgfs{aAm*#mP#g(w_|XP3W}dPSM;H|)RxJG>XIcZpU% zQ&4g@6Ikz}-R+YUucZ=`GErGSq)c!kBRgX4*PJhq(4YmPA2IgN&r$eZEt%ZA+Qx4hu5EH8FjZmpOm_hHApwX_=R@h zupJ&rKKnL3QQLAa7JxdX{wbRRw^H%nul4FJ=vV%y`rmc(`ejQhK%5F)o@r3&7gO8A z!h=XjKAAeO;w2mw!xE7wCl|%*W+pyB&U(@R;Tl}UkFWG-o7xD1VIRtuT8bf+r?RQ8 zo4FRLE35>;CS{x@9#cSQx2_Cfgh&x42iqMV%@2`(ujVsE`YSCFW6!S$e*6ifOTI5w zCnq=*M6!tFd9XGB?~G85hAiWI8Y8xU4iO^j3~7gWQtEbaN|xIQcn{r(`bqroUjpx3 zpbi>ernZN4K^ko<0$+y3yOZGS$#=T-4oNp@q!#&lZbEjxLfniZhi#+jFxjxsDD!J& z*>JY$YUaCty@m*l`TNPw>9g=kTr`|zAWVTUYOYXW@C07z;$gi;6@O4@($g^`$``_` ze^6$=1FxPgmiQnfWhK4GO-@)CP-@66wN#f%mNA{h@4|qonhXFC1cFC6*64ArV{$G0 zllh4=dGeI}yxlzaS}jc{=hCS;-7hA}Q@c$}K}6^3D@Ch2O!l^207I8IoH(`SY2I|n zVt0vt>?w-%`}8U2{6?0QM|TUgoL7ajxD5BaAId$GvLe4rE@z>B!rN@fR;OIbX~nh} zWjm)VmZaVrNsFZIx_@d1EjZ&%4JPI(&ksIb=yD-{&9K{WD;*<-#yP)u%FE#KUqs|l z&rR~;v|!IKsTMKFg?9H5x?R5xT};N_oh3NkT3|IS(dZ8CwioWUBYWD<^op;L;`8$^ zqjWmNi2zeThIvxWaDHAy-lZlYD|_Q%tAWJa0thv1=D7JzG{|z{c{6|pLwB@mla^ES z^G>0|)EHMI$(a*ln(eYvqg`>aqnd=DJN{zRy7+K@-V-tc6E6k6njY(z9ePe2FgC}^ z9}*VQo6cXYu=l+9fba@-gc(0z8x4Ey$m<;ysXrj~DO=#h@5ATvBBryw)lDr)r5SRwM9aDg}(vpQ4IlG6cl-hSV?p!{lH(q-&Dm{&f!M5oIChRBp z=@X5lU(Anl-T6W4DR(l3CdmaXF>Yq?Xg>QvU(o%J2#$`~N<>D-TnT@_YKwHXK;La= zF1}|We$@uT^nTh>MBHmw z&arH!F|1M53J+2I^0bNPQm~^TrW)#Fkp?@lu+V?2j{|V~^O@8U6fzCMU-h8E$T|hoSrxAERylj`tNhXY@28jH87sGoyGOHLQ43U+HH|ofcDz;bIyp znpJ}hW^gzXp&sK#V}C3)dMq9whU6}z=W)swek!xC+%+n(<_xhxyZgA&BMPuv&2)EH zK0p4Wae(5UGmX9-v!4^QC%!s0XQGW0~G8yqOBj+rx&+IRc z`HM_{!*Wv%XU2+&?|P=0$<9da^P8zrKMVY!k%ha=bg)z~F)+k~Om+^$-nc)$c$IN) z#)^z|;I`JVVvCP?5XL5;cy%DN+H|Z>tQr&fxS7g4YlZu*@Cc|d0|t(~CdHF6>RhpM zH5iqb{P9n$TB3r)nIB#fv367>{?SqhUO6J#k%tY-8E&}+6Fnpp%f`Xj;iWtJ17 zc=$s3prL;3F0^6=y!Vg}VkAAT8IFaEV$NyuRQfFt)l(RjlYX>-p1f+weFWnEZqQi} zSR5JVS$($HgK<-|HpC6%KNjMAPAN$0WYWm@lNfz&0i&4(Z;#E?=`N=(y^(cvyLovr zl%~&Y?K7|FGp}U5U&*hHU#HV8x{5(lPi1@1T%+mlV0RZ!Wp)p0dueVAPGb@;3*Em- z3N|tQ*q{B4^vYh=b^R6CLE!t1caBfR3Sx^B+jBgn2&lWRp?a0tt*hBEu- zSdbFq{*5s~G2>on!hSy=ZkG;^b zB~CCi_5-GWbL4VckV!BoF?k%*$DEHOl2~V{y$(`=vX~42|G12OFctJdf(Hg!-5*rsK%$ zDKwu(du)iHwtFYuks<>Ye^a&^Z?=!CIasH)u{eB9R!wa2#t5%YALBE9Va`cypvpG6}!^UKmG+@v+OIyV55F@tn)cO|8AE?uzPUo8C$r@@kdL&3=G&0&j z_L&?+Z1(l&dWEZT%e2!?7?3vBk4c~jaz>5DQBGm7!-p}}^6HgxbI{*@=jYM^-szfX z_eHc@YEFKT{Te-4Qm=M}cVinxiO%ghbN5#12ll6ajAHUz`x+6wWFmh`VqTNLDF90^ z?3z}I2tup`^hVdU{t{XLy&IlYf_nD`X{zaU0ubK%E9FlVPWf!2;E*X_7jMX|1W?nr z$rmsQ*ChwcW72G&G&?8oXOHT}B}JA17Q|Bfz=vTom4zM;C*G&#N*U>!w&e52e{_s9 zQIc6>M|ih6ZM1A-2g8+nvQU(6;oneRV7D2VI{GIqPx?y(>9RCV_cu|EeGSbshBEy{ z1Ypnme#ClyJo!>MLj*MGr~iHOG&5Ah>P}~gr7{tO^=~OJkTC;)MR~_%yiw*DUvmI2 za=_WgX)$9X^o6z4^vj^Pk%|RU;~@sWXkq4K!aQ`mL*9H+@&r-`+&4qrN90xePFAki zaSY!4^1(TIj9fGO=Ey_~`gdn#hOGuq4#Vuio05?nt55NbQy}-|vG%XIm_vX6|7f4g zsK2UxarIm@-GWF9n)Kp$Jg}FgC(s<(Y4qFz6wI6bgm73=0|-h}qwg{?IyN>B?b)At zA&_`3mbT^|!zJq0@d@?G)WqsfSw?xM7VLe}1ud><6p6nfL2 z2U!;Q@g*_CGOa(JXU!V*>4)Z={yn1tNaWiu1d?};tpTM@oQxuGScc#wnr@XI*z~}b z9^?XX>46YzdVt_&Y8SRj7ib$?OCa&=5}}(Hr{i680)%nfe+j(IkaS?b*MX12m`!%z z0-P?)?ib}yAg=?v8q#IuLP$TJ7b-pKlk@_=Ip=AKL2=13VM(Yi`7dKvx!sshVS%{C zB8x2-@kj-sicssa**9+b3=CIPofXCq5ir9Kx$SFa)0%3~%ve#3ec^|){JZ;jG8D`2 z;Q!n^&E2^l`HmBUqgZZF)wkRmy^0R-h}cZ74XBi~L2M;a9t78|5*=mYAIRQ#bIIxX z%)!j@#4=Nzrk5bUUV@G%8_dj$foQtqY!h0fcB+9r*?d2hpSnf-759whhiZ25O0Ot> zlos9lDW3T=z{ckj$8u*-x|!M;)bVL}E$DDOVbhR~4pU9-PU1Y>hs58J<+v~L9yjN^ zBxh`T&g8G$j`Nqd>#BGwjE`S`k-QDW3U6hQAW4%=X*iZsqEvm+_EAv@jkLMn#Xu8zl62d`^$Yr>BhvTkz@Q(Ks<4kmo7mVN8z4fQj(= ze;kkx1EGllSt?-u$LV0^^ir3e{_l*-&1PUAaI$gfd8={pmfy#zBs=|Vc6}+pzJ4iL?c_1GYa{GW%*2u zjVFcaF9*!X&g5@%o$$wYc!wQ$Djpu5Aeh2Wid{fH6OHUK`>a{{Occg=7 z-tAA!Ha1(cVKs-V{ISST;!O88HC9w*`f2)X{Wv+sH!gkPeHqyiV)KOvuU`>0V3oiJ z^`H2rp#BqZc>)D{8FA~NtLizU2g;=Z71@esL#SB+zMbnn>5jYGV!fUQ=ipmKqfK8u z`|7Rn$Ut(;j69d9G{etXxU%VWcUpeB=efI$Lpm9OoQ;280saBXk`mqGtjHexkq+(k z@U4+f`n|VO)2q!0kGywsW$k7bF+d3S2K4w%kXDT{TAxx%2r`i>I?)u8a?DSH9e%@zs$$Wg!4+wqE_4A$U&v&k` zf*q>O9$-mB3SFJuLL&EmzDSm6r=a}2_schAYRS*rdB5N1tgloN*|4OS)H{J}=i@Ru zOYH1ZLwJ&{uk(IL^;yh`oYHYrOfP~cj`veW*5_l!N&QzqDehNVg70O=+Z?=?8)@^Cf=1z zK|-YWgN=f0zUU9rhBq?vp}FiG6RV0&4W5)ZMNG@)Mq>0gq^*P zgtNZfll!|HsS^|$(jMufr{HDGv=tjcBIt6mjxN_*Y9RX|h6IJmsZ`2VRvZf!>L*{F zZ`|JxS3a}rgi5x|5%{@whkbNNW}a$eMCt-|f%xLR5|Nc<4;4K`_5GDF3}3HJ^qQDd`P0E-&Q zVdX#ZD5@W!VV{HdlYEND&H`?UewD+1>#3|I+QMIdb(V4eue%xPDY2-vn&vHC_${0& zs+&1$3<=xcN$A=tB8&M^+vGEi;b02>h_&53*65U_pa`jfeDc2`BL!@6^(#_FtJbCq zz{)8~w5qs~;8uov=~$v!+yq+$nL+~cmF@)Bbi~+9bm;q=@%G!M$~Kss9f=gDX1BR; zcQ|-EQNoT{&ivc4hR{``gwsV(xVT<{I9Mc1Q%}J!>S`_VzS}KFSx4ezs&H2B>Da2LvQ6Hn ziRXe;(@rm{wA1%g21&=$t15$HR1qGr!{c^18xN17hqpLPExo258fm9nLeuCeS~t`I zG^@W9|GIs}=E7lfVWz=}2glXoKQ=f$!ErWYC%k%1EgqLwuT6jD#LwWP*BtBg@hKm~ z@=qXaFB^BP^Tw06vS_TK@p8&jNMG-&EafW4C*4HCl71zWPi3Poq)U@_Op9B1s z1Xw9l*+#*anoT_d2Y?)aT}0d${Fb1Q<&=Gkl>lp&93WUAVW6gGx?kR{J8C0N~{U*zs6+M=bob`A`>A!d-7PpBd6Hv*sQ~7=Mg( zEWfkCu}(Xj9F?=NY)bV|V`F7KH`DNOCN>87K~>g+bYE?)*)3fP2fry@aOQtgFcH)k zksj#L223GoC4elIE5~4QnOJk&{@5jjejtiN4o6Q$btH7~XEF?WPh}VR1YOee=^^KA zQBpkJQz@DijZ$1SO2s|=a`B1<*F~*(I2)}Y{3jcxuYz5>1-mrnPp;V$__ZDU(opJI zG(OYCsM(p(34GhQNaLBHoV5t_@v^>l(ySWq1O=@{uj#C4e@ZpZO%^!~87WQBPq1<_ zM=bvoh&gYZkINvjpJ2fOsGWc$4zNS|dS7KJS0Z(QmF4=$U6mDF1zW52^`6RFt}eE= zkpelp@@W1JvE452_)b$%Z_jPp9>ne3F&$MA{&$4(l@q>Jdjm zj)GB6KWm}*?At^%%eFnryso>aQncPi@2Gwys~?=qN?DoK$g_#Oem=YvI~AgIMyo6ptyMe1&1+z@^TX#hmxgycmxk1(*J7=4YxXx^ zq+jVvJCOrD5=u7~Il)I%^R@Dww#uXUcskgJhpZ1MV?!3Ntguvbt(o6c8KN2fPeDTk zVk7j%_7kz-nhcw}Z^Es}(;=Q_uV&Z;7&4YJj6eG2lws_-lB}}qv%i`aGepGl%n*iN z#PVWw)+!UyosV}5(B$kO3B8F4DBH#w;w!JjJfw3}^|n&e1HLZQ)#)`Pod)6s91RTD z{bB`o>Nr@Eu+poG=uBr~rt`tE7J6bv@O^eqp@}>0xju1Xc8l)7_*$7{(BM_yW#8&k zy)FEWY&CknCM`*)Z{;@IB@PcNZIecwl^LCDDXTlEO1jyNrTU8=kl}Ikm+0GToo`4< zkwMGR?d3E~(`gD?kzNd9eMU-3WD!GLS>gObI%GO>EH$p`8;l-8G5GLcFM<#La@6k| zTgSc9eO2GUjooLdUv%lqPujSIM_iCZhK$CcaE5MwCEriu8witThPUe!yW#LwE0SIN zZc{zMskH2L)i+l1adV*G3XfTiE-CvHtXb7Jr1M@!ZS{*zUDG$D+2G1;Se6GgCuwp( zLVA8TtYN3c>}+dJ&xZkgteGD048OE^H9HQ@5f`h(m8w@7PO4S)_DNBU5-jBk#$R?S zawG|6j4-o;gQ#lK(l{7uv1rU5!~tB}8I2k}KW9W>Slhss%2?R8`g^72bhTH2(ke}D(~bf)O1K+@2l+BR|uJ0yB`y5-a6PT$jSdc zbC5-t9q11{ddN&H*kf`0vTSSF85Z`?w$dTc)$!~nko2y;wXUPn=#_TxLYk3{@VKty zE6Fd^{ro;kPu^Jf^Lyk<&=NIDB0tanBX*`KdCC7ih3V|h)C-24dEqoWuq>3l8D|{# z+r-j|Z?@enWJ5{E9bImIF#BcZ_1IT{4KHq`sBHBCofar$S3Sf_ISs_yQUB&Y*cp52 zU6S_pYI-;08Di@BF7{}H-7=@-AGS=GJm}J2cvVVYER>;Y>=|wZT-tsudp};>1uMAj zKhjjTQ$78HW}Nev{Sg$0x4@kjQpePPtUoLx-h#rW2PBl`sJzA|R?le{YOsb_R(7>*Rti2p^z%q(FU59AnEzE1DrYHRj% zfX|MZedm%E9>3;2K87Kb2K7{@e3@xb+E&?btpuKHCZXcVBh+4kgbA{`u)l~>D7-`p zPq&n2KP;uDw9nFn{h<110gphiegbWe;8hH@{&K1FGa-J?&sy|fA#cvl1ph?|t^EhM z-iFOWT)o3tSLv(+O@;q@eLafk3UZp-(@TnI<-b)A1wIGIrT369HIU zd4f$pMaqE02A2v`qS)j1hb*HYdT?xBVDe39;>%S- z&}n3YfV&m$H0`srrHtRV7W-JvLqecJ_zxQBLuMdjLLO~HO>;knzA@i^7Dq``%TW?) zD7<7*ei)M9$ob;YN?opL zQn|XkLONDqt2)crY#5uD`Y%#teHQzBMAmFpn^ei|jco8OOjX~9>R(O|3h%59)+HC1 z;l1V{2+cx9hs~F)0jjzBYt+lQGplWdx?yU#ja*c)AxFq5d?r85~qyL{l)( zh;l-8&47t^m^K;r4>hSWP6pZ?RT~wv&1Xe^#Ssl~sCVew_Ly$z4zHyVwG}Ny9=pi; zpcGka)fFY*LE9-yx7};Cw1cLXV#?umE7c#eB2TuzXIj6@+ zObHTh^jONQg#WFu^L}`Yu4ak$6)R2r-L(Ccsi4?)P$2IVIg5h`V?yLi{sHSjK{J`p z?s}^(FV)0LVIHs*n-;z99#OGMn4F*Ihvhtvrc%`P&eF={2UAA^?f;-It58jg;JZu1 zyUi$ix&j}{45m&QycIQT~ZaV z>4$2W-J>!m8=PRbH0q=c`u^KB=zr|*m4)^My!JH2-S))Ql~wFqv6I`p*iXx3E89?{ zmQ`3a8BH^WJc41?^g}a{kRm_aq-V@qHnrzXO%?x)!$4fDB#XZmaG}xjL!l8vi7)Z{klEGw9vH6TG)o7^D6EFv!E=OTP`YIo#*6=w zVHJf4^~sLPX{gv*_^3RmKkEMDi67mo8=*hC@5GM+`}Ic~xlFI=D9n{t)2nXFE5yqF zcHB@fRjsGdEJ=lCebLK7hEKppN;jtv^J()<7zOd=L+b8Lwxiise$O`&7a{rwNqk*1 zesz*o-;^bgDMqZo5bh%rGr}JHnqC~G8NxH1fWv|>T}RnJ46nO9IrBl5w|wq2WAoE$ zN9qOt?y=O4qEu$io)^?W-}1^>EFCLFgwd>qOwHEyS(S^`0JeR+jAyb8-ttO&yM@4! zH%qeBAEi!V7`6yeb1VM;vOZtr&GpsvZ#Roa0U|oH@jmk)Qq8f$7fed@m0$c28o;5V z6^HUs)zcmNL5NLW#>|iQJiXnV!2v$OqgP5gesPsc+Op@Vzq$6-0+@}v@8wtcdXg9VJKS+PL@7dts|b@Zhov*aAi zX0@+bWiiOJiH+aIbED^cV50&A7&m&3X>`7(HWt~RJQeZKon#=#IFD0@XCxX@hi4`} z7E{4WIcufoCCQE2RPDs@x)REDsW1C(rf`fHZ+Tp**nVb=PXCKxLZzp=r0rOGvVR^kI*fVVOeU~(c?P$<&qiR)@1_KHxG&)~&Zn=O`e2K?)5Q|p)eI3{CB znaYG4ThPR>3vi;Be|?MSTLhA#ACRqM%pa^gx3MH1$g0MYLnH4PPVJlN%o&rjX8;T* zh1T?U9l6sqZts`Yjq29jK9Jq>{>i2d24qLg1^|)Q`y~U;eR0H7QcW|F1o86|aYL1* znOP4Y*?qc%O*&W1UsieUvJj!tY6({u4{wVH_FJ@yP0%%_njyzdepDG^3yl^}cNMypJ0}(oYMLULhN* z$abUW9-0S^3TFvIZay>HC2LfEkTa1?rzm-bEWE*w4jDavAR{AZq@E`sh0c;HdN~wC zN;P`#BQdtmeP{IalMwIT@Er4CL$Heg+7he?MG&W}4l$HNvB=Jt(X=z>UtDYZ=a}a% zF3~RoN8;hV9{!ll?A&Mg&fMNG!=F4QTKCydqFmFY+*bstO3c9`K^d#|y~!*1I^-2i zd3N9kMTU~t+cz+ocE%$kx>9PBs;Gm~N|J%7^ByzxQ4WvV!L-c1Qx92gfnG*(csBGm(Tzw9C zMca_&(I)pWULsAz9CntXrD_KKcQ-4rf6XqfNV*kH4GG&_m0eO)9pNN|DWnY@wRU(r zv$TB%v4Bm$pDv?$?x`Cxm2`d)u!w{hvP3|`Y`?G<9-7oNt$X9-NB#KVS#`Vu#-#lA@fSDAa&h?!*G+Pdq@6Q5B+|s+Ix{qT`?xKX8wt#y8ItRuS<1cKmiy2wajoE%7^P+vSA6^m{HLBu z_2PW(Z4@5tWOa6BJQxY!fUa3~&EpirTe!P24%OM#K?G7y4>nc^%E|t|?{<+4I=8Q( z?)2gc+gVm+JNmF;E?T&kPL-meV#xYhSd!SN5c8~9#2VNQC390N^L!`^atzInJRrqW z6N+;YSs)6;qp>@)i-*~Fz$|P}xf$F8jtg`F`j#k_$*sLBEkG(ho_WZB95ip&{>$JD;chUj8Q}KRX_^hlWlK>*>vcqMv>U7RGdPM$j)jo79m!w7KM@Yn-^$^qb z<=eHlgg@K4jV;l(tNZKWoX_bp5Zt83$wp@wgVbTUsEf);W_5CB_9F-3y`6^yqFiJw zEa#zn+{@kW!+!T75aZo0xzs(pBQNv;fytGu7!IcshyS`Z+iaR#~5JGxP(Vr8H6dcH4#$t!r{sx*tc6eDW zP!dA5Q$(z_a`0$H_IBAOD0%E(S_)0&yex@C^vM=@5cvd4`*mO+rRRPOOPV~NL8A!n z5sN0iZGX&HkAZh}T#d^CLsD>k?k1N%$(@a`EIB92*_tz~bZ0&{+p4{0MuI>|*OWA~ zFGpY0mN*Xs%wlZXQm^|H@4{#n?{c&b^(dfle5o}bLYj&;3SLF}BO7@-ZaLo(DBJ#& zfIixbAeEIgG0QXO$+e16CuFOZ^@h@!)p>2}QzRedm3)+g>c8&i16j5c^PpVJneZ4i z35c^$WbMEcw*TvT2%M!%YbC?0n&!*$%~D-B(5-O5+dZ1im5T-s$+vuTET3cDS+yFNZQAG zIW8xB@66s#TiiDLR7ztFMYR-;`-R8h==Q_KiD1paT(J%@@4*Po1Cymw=2aEA%F-#I zlyh%o(R9^W8?++PUDRyT}5@HZzP1mCR;~#GM6i#{x~_q?LN&etY9r~r7Q68 zkx|gGg7bQNYhCN(c#Ps!O0`Twr5isC&*5G?j6>l_o{$ zlI6Td^_(S^AZFw-Ii@QBho$zRAeDFrV*}C;GU!qCr$mfm)onaWtT>V%<9Q_s84^C! zl%~h6sIKR%YTg*V_fGwur!sBWLh;mcX2=s=;@&{zA3dY;`;q zIS9{roCyGBjH^MWfnQU9F)@zXfmiMDp@ggt)ijMA3yRyDpvF+TM+B~#gRhWrW2>Z{ z&@`g}_=Nit6<6z4z2VO7jyj){?ZB`*T6+F>O|Iwltmniw8SKu|Ncl%n=92u|-Yw(I z47-FGh8b;lrk{H~WwMtdPy&|iAnD!m5DGv@K(q)hvLu#Ftou5#j5#x84{AgrJ28P> zU6y>CEYsZW^r;IMLSc^n04Vg6FMA1fPN0>`<5q2zaJ#OKU#5jv8j4kT+hN#Ru@F;7 z5*OR)e-`w3q=F24O~sA^bqgSSZyt^5&vbUZ_&KT-QR@bj(F$P zbv-Ij@d?m2Oag7g1kg5kKohdWVwb+9pWFowIr~mDK}6i{eGzhRt8Yz#9a^cn4KmRZ z=49KXk(ar!)9-j)fZYZSyV)9cW1Dnw6yfM>Uai>$)Ml#<0vUadil;$V_a5en%%M)f z&^B2tTgWGqN7nK+&BD$s7bXn#I{PuLM{h`)!7>Wom4F`Jq{O&P2!5-ZIivSQ;*)s*S=&Etq>T*)uYlq364acEH*XP3-<<>%t#f*cn*V zvYG%?y{sxCEtm!V8a-!mXVtmKryD(UxJy0SB}M5ScJEPA1e!6`pW0y(zVAX{ zxh!r=+wAajcHmoCa-d#A^=szmJ|Npk^FjXb6?1QcK##+5c7T)eZIXpg_du+?bV9b2 z^+xZ%^RoA#(fhAlDZ5ihJ(a*ZW5f*mA){#YJS$RN^{sDnBlJBi=!Q5)xohD?CiXJW0&!aGaRoF?y6X} zdoAol@1!+mWP7sGs{2+K=|mL?jSdS6jjGdFkuY-a^zsOBS)%)MhL%OLtXPJ1z~pF2 zR@pY`HxO`VnXrOugqM)AMdqauY=U2NjXy9d5THyEal3Lu5x_7JTjjegWRSTkCT${# zc0K^jUQ2+NC{{|E;_HkBnN8m%EA^rGnmfG5JLS?uXj9LI#Y6#A57Ul;lr(KfnuZd9 zhY(Fd2&j4x+HBzpr+GDul1UR>P@x-ZlWFXH2BPsJ>5S$4NQ0|vuv@O6ApN8~9c+TH zWb8#?v{Ja#er}xZF8=bhU_KY=I)&WnUhdMD_ouEWaE*G#_}br7VC`+v`{^6b|9B5y z_5E00Oj&)P1``|2A4|Bi%fo(!qfF0Q#RxL2(p zbW#E{l@gVX#~dro%g}g%EgYidyg$2F^hrwF-mtHt4%u26N`DN^oX2fWg5H%B9$7|o z1k2Y+iJSJE}lW=o!bPHxLuQ& zf>klL5*}7Tc5UZ2!5=QVkIro^dL(XZ<>F%4n4HJ!if26gKerM$B!ZazHLD7Vg5X6p zlt>6QFMBfhI*rt_n{QSJwEX?3$UI<7HD23c9cv{X8Ev0i_oFTv5?7;oQ)EQ5B6PY_ zx6R#A)EvAO?pPWIUnI>;*7P1sGJnO~LNiWmUG!_my{N`Da7mI={t1IpD*M^nbr+S= zS;5G9aV*ozA~ztTtUgK!*2{|75>J=R_LwHtOktJvjMqq+&7s_rW1CuplG?Uk>9YMA zJ!1Y-***w#WByBh@$d`rz)=vR49Ta_tK-eF^z=%6ETMRsz7@#cR7xiekvV^-zTV^R zrl7@o*t#E+!I+?U&^u@nqq#yl!j1@c4bjnS@>i3OrR01@ln%@%ocY5h;ja4Oocr|? z=uv`@dL}lfGT#JTAafyhW4*Cy(Dq+c?ht~^3221Ylu}zSNp&e5KvD0)pdCC>gTjTj z>K6t4nPf0MBofwgbgHpFCiWsJjk60>p~3o)&sTxX!1iW*duyX}ID8Y2W!bMWOx4X! zb-J_Ijy%;d%S>N1bF7=8Mm!S3URHX~+B|5(XbR-ApQ)5Jc=M&R^;@_PfnPiwTp)cX zXy!(pO+t$bCClFQC1`;w4q!#@c@HHsb{`ResuU-_L}{}KNqfinIz*k{w{Btn$>zFb zY5Ed^tiGT8<|8777+XXV$-L1k^7UR!2wB!*iU?VDFX-A|Q<|6&JTZ|==|wzY|KMXDh)Kjy!%lARNByO?^)L~_c`>2qFDv^G~Vk^V`j?Cy%M^=y}cl-xpUdFlTS?3&?$_uhC!M*HI+8N78}5WkjdAVUADB zl;mI(7YvMbbk;*NsG_f-n6o>)$Ibp|DPHb?z}z04Lx`H_Y+s@rzOB&ht;F6I{%!07 z(m`i_B^z99H9Y%9iHBSP1Q4(kd6wp3u`C-WM)$^&7kU zkAnI!=So7jbr7cpUq0O4(o4SG>|6tNnrS ziYp8~fkjmly9((tGemqxaXQ zaq(}}pN)GNtX!n`*Eh~r+yD8{S1MbyYaP7Z6Y6=s`Dz7j^|^QS4&PWR?_bxax}w&3 zA~)0fn2Mn{E^R98&WN1^*Z%ohXKA&{ao!Bn#fU{7Yo8HI9pmszTOSVgRQ6n&DW7v( zAF^RSaXw$0Jk`YdUcGFp@v$`R=G&xUMt*zaY>p?h1-iNG<=KVmvLKub3WIdXH^>LO zJ=9)migFT|We5fWam%nxAxz?cU5wqZ&pyZJ8yC5wO?h7>G{doc0$I#bTaMX#%szC0 zLkH1eJ)Q`lJVT^PET>cHZx;6bW%U=+xOZIhr3C`*e0sT{wa^mhx)$M9WimPIVD#KY zYl3p0K&f4f5c#1H1V&Gb9I#_O@ZhcQpd%tp%yZH~KWDv7*I!8Pl#Z<^`U{i@vK&=p zyWc5$!1THO`_i>X;(V$L zq&zL(Kp4~1DGMTc9$gYq7@thO;6A+!0NyK|@G|JEvbW|~yf#k`4 zd}eK;GL*c8SfG`5dKqU)!JrpYa<&Qr2rc`7ym3V|=}mf@n*H7y{bruAxko=RF?I_1 z%|5GgRxTtv`DWeLjg?u-(E_NNei|y-W)m_w8>4xp(#95kt@3ydOAF>6+}c=7s3q{n z140H+2PNjjB2S5anc$Vr3L)kwh{!HeTS4Wx3TOX* zm9*FB{TUoj?qWE&=J2a-N%2TFZZu_OL`Eq=z7{H7f$eG8OqhBpR!l)?Se3w05*68Z zdF4qk7jf+9Cl^qsM(&O1IP;w`7<7Rnke5@<2DOz5%nyv862G66eqh{EIm6c1?}wp@VEZD5ZZ6rUxmM0=6L8Z%_?Vv6+U)kxxOSp$Ab z@lx6l*D(oH^U!Qk8hGEhN@~VBxjFDU1{R-?&WuQFP(c{cYb#rtBfm}7bC)=kI$MRw zlp)QP7E?sWsV3ZT9P) z9sz;+vov-ZA0ddNOD}Nm9w#A#eW%pw`6*2$mZW8UoTh(N4`-xoxZRBICfjVe6*gk8 zL?B>=1$S$n*2_0MK0--RC(DuwetHVX$OGTzqqSukOHO>rF?zc3gV_g=liKG#BzbNB zL-KSoFYwSj%E_rXozy7F)=xSv5a&=w_<<4R2pDg(^~*xjrOm~Rb#k^S84Sd=@a94@ z$L4`tc3R60CU_O$SGM!Gz^|m%xB88*|A3rzN>+R=t&$agFeSHwp4s_VYkg32FMLz+ zb;2Amubk*eWik&m@XDwCa!o>96GPbWeI<2%q{ zX?S?@QY}JY8&+UW|5~Jq*Zo{3Orj3EkVysz!~I()%{`#Wn*RaQ;~Y_t8q_S%e*+x( zR5h-!({9TgU6zw7*KJ1v5KXQR&r*C&B zf;MLWE!`!9<+5IeOa_SM{42r|Vl$`>vak?tR*z*lEh;}I4p@_u#N{UtidY=OKqT-B zg<)W!HaB20YO9u>TwFeu?X^I@+C z7a8{lFX%Wk9(lgQWa~^^-hI&o^jndkj+wMF@O17JE4&NC-t_X*h%b`iaPOb7 zYa2@kMI12p11<#R5kDxz86EHy*HmB^_Of(AH^L5Q*g@i~s-87A&#wa6_@7`ifnNj6 z(84`x%H!Z%k?!!8s_!yGG}3>fzgNHu3uiKTuQOW)$d#!fJk@$!vn znGY2#t({hZsh*cp>Fd2(M!}Ca*W%XUmA@2i4mbri-RMK)O>N${atz~Je@n3Np^gx_ z$7a!0w+&;WIG0{8ZJE|6ufSz*TcGZ2F$Lygx`;I=E28wTEMkWD@xv&OVf$|J!ApBX zVB9~~YtL3uH@yY+dBEWa@m-74^UX&?l#^R!+I9G~FXUaL=eo4TgWnc;~!18Ncv4b=Qe_|?D@ zX6jhbNX3|%W`xkb&savo0Vb=N`TdL~={weS(H3`pOwL!S|17WmG`<~g1+{7QpEzez zN9)_~Ne%R;js}gJWJPBFnY*))x75*7JLZ`SpE1>t-nBKSAHK|oT};k4Q~k$07C875*7~up-KZllW+6M0aGhD=pXSb+$r8@++TK`S6V-;(pz=LLxDN*0Lfc1?fp(*%Y;osn|PRf$vBVmqeAlQN!2(3_cFdcgJhkn=6{JK3c3 zg`0m;DtMwjF>H6iT8Cpp-sJ4i#yWR_?0Lm>PVE(!2B$9Sd{o6&bRRj5Cg|gJo-@gTtS8se!Tm5NO$PRH(*P*yVK4>mg(F3_HKQ&pK;O zzHwD7Pvp2KlcT(+V>7JU^=pzS5Q;la#b6o&Q=szr6Mli^=W2am1~=Cf64&a4Rkz-9 zmRy*eN1{GybRY2@%RkyaQ*9etLkqgsYhVsG)y5@E>yx zv2^gmA|7bd$AxL;s4Y7mmWN_`-cK!2?d~jgGT9d`KdoJFiE?ueS?iY6x8orq>`1j8 zezG@z{TZ>ylWXyXULh!@x1MAFB&`@kWSY#z{D?UM2jbxaI%4XF$-{_$febXzRIi#3 zOAnDeb-WjobA{W?_6}GZe|`|a_E~3tKEDl~g1qf7=8EM4H}rDiQrX=vTjPQu9WRHu zj0-?^W>04|5kUMODzccvZC3b2lVAc?K1O`oJL`5sGHVDbyW5`jssSYM={909)u zxWqYzD|r^^;DIK*ULeChN$gz845?|j(S;0R*%JYymaW2TnKPExz`9*OkE1inj1AAR zJ|bQ*ZrV*Eht&KraUts6%AN4(n%5=F)&#tI^(-)Y>u4qOxPaBtUZeMXR+<8d&}h|* z6ouF%=fQEwOc!qX+dLVRc_I@u&t>u<;FpK{xd@gSsgKc$X8)o}2!Z8vtV+CFFEhf5 zxjzVT@FTJOU~}YnL!TA-d^_`46c#-1^p61g+N1XqZr$dq-uIixepg|0K;(O)GU^B)hgHRTT2~s zt`i)%DNDJ~^;*%dRtc$Rx4ud?^?Jvvb(bfPyx_?vz^{t8_xEVavV)LqYg~O1;`3Ji z9_)0d_JTRCWapanTPxWvS8Z{@1|@@%eZ| zywlhbBQTZmnDF^Hcv7E_I_4Gw^8e=ZQHXDWl`1|Ea)zC2MA#_I9b5SmRcHl$00W3M zq8tRJ#}U!*&O9lY8@$t0)M=%@NbHctA5X(LC`h5IaqqS8qHCnfN8zqGMcyG%YR zE80}Q$?0yB7J}W1sjidDE8m0fBp6Im=B{Gc8(Q54dYR=h^Ac z+{_ctTmw_dEb^xb_|?mH8sP;6$3zkj5MT0uXl(w+VCD~@Ifn&;g@j)xH1r~ejRxKU zJH7^V_e_B9T25gUZ_{7?uYm4%-PXn^6qTcVQO|jLjYAA&IZIgJh(5dSh(6@7;joP43_C)I z*IJs9|0H;J3IiCH_Vd>LU ztqStET3Hkkz%{FPyTt6gLgd7new3ueoS|YnMK~uqGcdxpF=X@ z2q^JT(+usPSuK3dpjqKw=F3HI1%iZAcFgldNjGSgyKl?fOR0M)aW5gc6lzg(J$YXS z(;<6T>iVG1nM;7;qk$VwNykqs)yvabQcsCs~L(8^VOrkBg~C_+2Hl7T;|Hi>G06rxHcC<0erqf%9ji-&Q+;+FOcV2sgGyYd-ig%&dAzm{Q6_xyUUN$qScwhYUx@r; zB2>P;ZGqhP== zk8bi-lCQbRZ<9RcChsH}JXe2qH^~w=`BRc*ZgLCB`EIhGWTl%NB6+@>+(mMcn;ao| zp_?2fX}QTP$+(*wC%N2B9?t&Y&j3BGHXRa0^6ldcx(kNvM)yV85&fd=J@P`hvuua= z;&t~$rG5e6vR8O7aLpIkougmC)@Dub1)s}{b0=O@@j_`o99u10L;14Y?xEGH6PPy# z`ZYr_<|mssX5M(|F`67Q(^7hrC|wVc*@7B{5*RXf2w&nfARzcF8NRGeSLpN~Ndt{a zZoDCrgRn`;kS%hPGGrIJNf|QBP0EnP-J}fJayKbMw!%%ykgal)GGy1fNg1+)o0K8@ zikp-n>v5AZWE?KT(*ZqXGFEh0M$Q|uz3vMcD}e+f=M7oE_kyvK z7mOS)WXOK#yBugD>FgOwa4qGYDgD=5Na$&uy$d<6#My?V91R(_oK0tl9*MPtM69?)`~fUh zm1+~y3F8({6!LMWSB-Iw{o@~q>?6Ko(-6v<$bhk7uVi73dr%ew(^0}rMg}^9Y9KfJ z0gNnsYyq1x6mlU!vF)g%9wP%@6j3MDPfF^px9Z}Rix8njo!cMgnP`89O(*rZq`x8Q zzEqunih~h3jgp=14Kqj=QE4;ak@ocY~?!+$D4R^$|wmT7#S(-l$<_tYYF z%Q8)lNf8AKOR+MBdWXJoSxKX{Qm=EXUV>cWCuYO&qJ`y2>m%XIA)>-}%Svsl z>&uy0NL?^R_a#Z2Y{6VLB>WEovqn*eQC(w|1g3CzW{!9eOA*t!bx+?ZZ2y$vL+pXn zUU+dk^J;~bCknj`wV#RN@N_#bD52X?C=L-7+k1{M++SW_gt#HYxd(P+Nrkfvo3th6 zZk(nXA-<{grJT{;Jt!PsVZTPh^8NLFoqW>Ba?t4I4@mwz@tb~oLTytP2N!=eY^o)c z7~4|eXk0j%#HE%nh6juXUv;BOi#|l{vD8!Qsm$2S81b?>6A(3x)$HTAzyK~7oE2>hPn6|ZL zqYCF@DC+@zNON5M2_E{eEB(;p_bf+xMAh%;Qt)Ck^^%`1({U4CB5&?~Svtlb9`?F+ zxG;PR2@E?FILrVRhEGt?&l!#-7rUePhfsDL_8Dc_oZqCj<&)nO*+#|G?&&Q6Bjuiw zPFH1frZ;Vq-P8Ml6Z)qh>wF~pW1fzE95hR9JM12js~Zp)@DScaEAKV|e18Pl zzaZc5gX%AWWXYn;D@9AwtCBv3MQu-CoS!vE;#f<}L3$?=Jjf5g(~EO-<8=-J`P#~|V^@tig{1dS$4^>p0Ulc_8wBk(i$Y_fLQ zACiFh%`6ESj_b?pmjfK4YEd_@>r0!|g}2Fb{Ld_f{wUZDx>*>jcY#i3V7vJ0iK~&F zzMOai4No*1X;^P6r}I0HVJ+Fq2|Uu5nci=`e5;Rnx|$7steISC_L1cf=| zRhx zGruRg>l^;H)kF@d?4hS-`i6W#J{@CYXlaYSo!R}rr*$qc?EF*7mpo~)79Rh#B4ZYd zs=NfC)h5dY*(4g3=kk}qhZuJHTNPLZI!*QY%PL}n{iE0+#>0a#wkDawMZNt_Ac>N} zV^_4H@K((yu*5iU3hKUl%lSOA&Q1i?_S|Ci>3Ry?q8Z8&qiH06S-IMH?|ibv)2)y? zY5)m|1s|-51@^>6lQEE(i%`GBR~v%S!SqiKg^Godh6p#p8bN{6s-Lo0kksxwpm#_X z#_m6gJS25}oE?t1iLZela$Ut)J+i1NcbN}M(=f^%G#8Q8hd4woMsHf*NPIHh&N?Y< z23^?X;{l?-A@aI?nYyJZX z?0JG8teqjfC!*4UL-s^ErQ-QW_v5=z`YFd5{KyYbuHNA;~~?Gk~u7g%#_&Ck5(s_0)Lz`Ya9JZ zONVvFAo?r}wTqg=TSeazs`V#l=|jyTd)i-Va5k0nQBOm}jmNf@lhXF1`0YL_piL~u zl{5_Y=p1D7$ys0=sA8wKA-OpkZ12Xg0TUrhZR(LitjKPQA*^VBx0zm?H`AX$*L1J+ z)W0-N_wtuj!Sq**{r(Ja>_GduCcN6FTLhh0yxc4tM(BJ?zDT1}Bm4HT2gocN34(Fi z;UR+gB9^<**nF;KT=Xm2P^2sDILrFzUvNS^0jxLhE%d}vx5y61QlDRCsq3uO&W5l2 zg+g7UTR^fb4jopY?{&~pTPOtHg5VNA9M21c4DJ24o`4Ub~c3j zHSocFA&Q9;TnP;4LJmY72n=CsutY!ahvyX90TF`gnl+h2dk;Zh#)8(uU91Xr&2B4j z$G0j%6w`fJbICuIJz_=9b}Jj!6;0^Mgr-c;v$DpM&@wBq(JfBXv#2QWx4{h}+2k~V zU_8(dA?#@q5rfb_)w4nB9^7yjzz|eiJ-Fd}Bxz2%B;efgZPJq;=v7sJwo!j3Z>}qq zqVyZ)>>_zZX?4P;Y6evtbiY3iLaE6(QWv>E2XKS*!tSDJy-?Qt3vr zngA!d{jtE~bZM#XQuhH_x%D8cjk;S5itbnLIzGK)Y5fGqG`Lbn2~O)~NOGF}R(Uw_ z#m88;mbUVNmclauuy)a_SD_Yd|8#=<#YDipj_iHKpgDYhT@8O8fOHu&)>k z>bf5Kh>mYV+s_1tF)u6$L>9l+QR*zl4X1&_o=#gm*ns7si9t;)#W1DzSLjvpX<5+; zm@Kp3xaqgDo~N7r4~m$cXxPSv4^fY5f_Y3-E~+^wBdu{nkUnsqZW%I;q7DK(Ek2gR zJ7j|_S5_pncCKEsF|H*XauE;Kz1Dp7OS;up4ck|3kEgFbZl~|j+2etIfWA2H_2c67 zT|c~*DJo;8KE-ScR#Hc?sRRB+)AMIE8x7mKU+VO(ho40eH+H15V^(& zPUod?^muwHPR`&^iP0IlQ?{5s_SCk$4~m3^NakiJJ+s*W$;VFvXM1k>DHkg+5{+E( zTF1O}BckdnUW+=5kC{%kfw0rV(KxDG}^}Ls}*Bn7$`;s z1fLOT%dT{%ICH>I*aYQ$}kBf@QNG+CU(A1{*NtQQzFeA$wvAOL) z#$Ebt$8}UqmS7~wCVI`-SE);lt9$O(YyNd?wt1SEMMc|+59M>8J?ZLye_{2XzWU|# z>Nl2{%K~#=Z4?i9j)b!#W3ER2PHGY!ztarobS>2J8S02!XB(+!(R5kNT}U7k=S92% zpHw}OE-;kyal%6w^;@io@-`Vq{KY9(sfQziz>xNhwL;00XB(UEyC55yBS9=L6ya72 zzvbFsQr>KN(;Ly}$V`rWmCw~CUEFH1uc~01(EkY3h*R2EgYo|4hieXk>-EMzZBK2V znXB?za5N~N>)Jc8TitwL^$3;ZX6fXR)aSPJD0UqiYdfVDvs7r0kxEiHu|(f%p&U?I zS4ol<}oMrdHgxATV*ap2y;(FNH_)sU|km-79QdGSms69dEsLn4~!5Z zQmm)zWg+>rtVt|-C6% zFD&XPj(+sXL~%6o#@Y~eY-1M+Nzx3!1g1?6aKN@(Qes}!6bu*9?K0ihOG16MeM6-G)Ja)!m|6=4p#T-}~%B-^o$$t;a3YM)I?+T=@IB^4D| zg-^dNt6*{%ay3}H!rAl|U8F$YYP#!o5%kLiB|oc>anmy_KI$^8r*QedXs_(?R#cvE z^d`X=_5()G4U(YMJ-sC4i?5|gh!m`ziEdTGWk>x+ufs!qU&K8IV3M0dC?906>WdJ7 z^*N(yw;6eQ?Ym+)-#3SUN8VrwT==QNPGc*Y$FlnAc1%t;YBHyn0$D*TStl^LNEd&| zcL*cE9={MWYvIyW%#gV17ycs_7#1c$UnsoD+Cqm>In~w?==1{dOG-M~cpCh&qpq}yoJFDprIG}0z z1Lz|QsfHd#`V(n84UHx8!&PqxMY@~n2dzqp*5+f8Z4;gUO3gp8nfh%!j6@g@1(q?R z_fHHWca7dqOC&H?DQ!mft~~?MPXmK_Rzq#@++>L1vLZX%k+R=W*+tO+?1Cuj=leH( zT0jZx1a4vqM-!8no5AjlGK7N@95Jef>AW2~E03RZx#QfmJq4s$nyIKE@e?kmp*Dn-n+- z!z>I&dXwfPMWxfJ8IDo7%#F1&(&`OWWZ{HqMJ|EAw46&|Fgc3zg4Khj$6vx*!e5Sg z{3U*{7)*>)aB@~OeF4T@^~cqa3`MyN zh0%L~`!GN4;d^-KtpRbhZcS7j;S7_q4>TH^7cGDV_N%5yRkK|)OMx&*GYRNxDK*McG(^7MgY2cZHR z=L)Cc&ix%AgFELbcyb%1x%za2e__)qGk{!cW7Dng7OJ}o646vzQ6Le4{U;#XWC^k# zGw$DT_Wz~rT>$HHq<&m_dNqM%QSX3n9>x5ghFj`U`y}!?Q z?{ky16o1Zt{!_Tw=dsUztiATyYpuQZT4UryWWXXA&p%uw4E9NCRUiJ+>vwi?0Fc?Z zr25&EpPr~KEm68@P`VRMtaWC`&W;1Jial!1HdgmH_|wZ1k;jwuzhZ{`ilJ5f_K>(d zDdLij>|F?Ys%QR+i6M7Yzu9nBogao)wzJrsAys1QYNnxkmq9+)W`OwA8d8&VH`R*P zL45+RCkxM_GwnEIjC+GDg z>SuY6T>5sqf@^<YtLcJQXRg|hRL!!I@LH3^yUE<=5&s=Im3 z!}(^sLRg1OEpULUKuVYw8Ico|67%{R>zP`#+kouS+0<WL>@xYm89l3yvFpIYSu@8@oVfOFC?VY ztLk;@p1K_6JZV^g57wO2o^Af=SVP2*56jA z)u;14tUP7#Bjye*f1QxP?J?3X%TAq8L1g)((cW7`8WuTepw4>ph`%CoM3iPp_1^lu z98s_TS#-n4$RaO+algUR8jSKGHmv`D!yCWB(wfnSa`RRHGiMdEf~bxqOIQTc)qJff zAr;&-(A2E_4|g0e#TExX3H_6_18pj+;m?W>7ZSOFH*gQ|fe}Lh$|YYU;qPo8$= z1tJCW4kf$+tC!j?q6OfJ?0}_Nc0oXXW@2klV7u>5)@WlC7b9A*$3RI8_W~26 zRc(py<(rlOgH;e0gnkI83e<2WURnRU$k z#JXJ?=fX}i&xSW^^Kz8gIg0V2FI}`9iTjU#K7EVX=!Rzf6`qxKkawl zhDd=XW@4J<&lIrPQ1wyrDtU}%KkiIaPBZmkbwo4w$pEkhMr97ka>PP>@H_YCF33^L>G_|>rlTB|w zDu;`t{LbME$&z%xxs5Dz0!x&lv8UDMj~Ro0J2d_*TCt89ftFLgI7l;(*X;@@YSd)F zVOxX{%E#IZ*xmGY?TN4+ur$4tth+p|nU{D#YW!%gPEojQ9I+X)l8vGh9RCKhQPdPK ztYo8Tm)#8cT{<$Pc&b?EXQw1QH28~3^A{z&1Kxc~HH zl8vDs9A7o`no$iM&b*B!36l2l+R0K5arEG@_C{W_#ZC4uXWIS!nNo5%N$-;QzCkL;L`+zhM(-6O+ocI6b&S4z3a4(I>V3(NpO|gJ zI^6DyoVi^ioUG4Z8q>x|s`@FjT$=Vxg*Hz$LKzO6I{r}qIj0j>PQSdA6%7&sTEog{ z4+P~q71bWk_-V2vKLs8jxu6v`K`mG1n2k1`3kDE)>wCbK1*os_j@(Kj1ArGXz;jxpsyhAhW-$l%riU*i4~g4@K91 zpJ&PXXQCVaU1jp8!dXs=H9uaW^3K8_R5;D zoFk=8X*0a}2ty|J6A6_~PI0&}6Bp>@!xi!}EB29RZPJ3aVkXmKr?iZJa2t@cHcy|1 z#pFBbsDD+~A56nu3qswTCZ+&v6(%-^G0*BrNyx|4qt7W#bRnvGeKLtjR`-P_DNU}; z(m5UC+%7w;_dbJN$~j34VvGq3>#$MdRQKz{NdIcrs&%5bKVysFOMjX9^1yz+T$F_O z_|k~XWY1|A_kw3Dnad`<$BefImPq&fc6U+I*k%Rgzu=qm+e0Mt4lMBwjE={W$cwNS zG~g4m`lO5aC%~qJZg{3K)kQZgQP^w9vJ=FW{&aK5SI z&d~0wXw(8sLL;y-;}%aFee%Y1cHO!=xXMf&Fi&Kmn7c=Yp9EP60}B`5Et@#GJ9p0} zDU*tiVB$)OMd{g(xn}~F>=-pX4!6~1FOa)uFR$1`Col~<1Miu!{F$lVSr?qPH^}OE z9H-}!VYD}%HHsMxqpM;hcCddpn)9tj z;#0*;#ZH7hI0pRee_GfFW37)ch^6kb^04_DKHzSUiP0ixq-1OFsaKw%;s0esDk6t4 z&9q(Ex!Y`XXgu+MQvt4x@37FS*oorTZ`GL)k$y4OnF)T=5sWF_Wf+eA6k$PgH>R08 zOH-_($Y6@UTz9}>GGT7RM3bSWA|lE>wVkYo#c2+VhM%}!vo7s_uY>JTv*fa2@>%qw zn(gy`uGvRl2;`B-J%YVlko10n>j#1Esb43a`VID6-)yg9wnm=S*4sxRIg;-UK18=p zZvw8+D?3F$H69d^o5P)CygfX|Cv2k@9?}=md=f%lQ`XL_P-&vEWzk@iLlvigQUrL; z#%AW`n7g7U7QIUDW*z@Lbnf?~gyFE)T}D0ZR6m#Yug%cEwEtbLS!w^9(mFW)?IUa* zYsDkfMqV0A?s^azw zu`G=^<>0V7qM~TlzF~Gi<+~#N%KlVtfKe4KqAO;@U$EN8rom<-Z<}mT-buNIUNZ70+M|?^&CZBvoAY}1snoiiT6I&o1Kps2*k)(lLrPMG z$BIZJ{D}7c*&M64=%I6f+IWy?SZmU68~;91d$*|!-nW|cwyGAB-dxqJblon^%i>H8 z*F)y34P82FKdu>ylbKoY3n;VG?!RxL=Oj0gII^36B>3nMm%eWVO!pfVzI{QV-`sZo*(p4n9oqKqrNVyhnb~rljO1uBqmUjQQ4jma= zR~_)&NOpU&B^XAuwE-1z~OfAo#&OFgYaQb|K6xJ*_(MkCw1KO+Z^|s_Nj^cQ5ZcW?0(b2 zunCh)W4I|UvB;G$y`6N58xc zWk^X-;C-$kT|jO%Z6c#)nWIH2E@Prv)3RvKM~jGmU2eOMdzBE$hIW0EFgO^!Gla2x z1AbWQ7SGS(OU3kJ6>ToQ!=s8ZZo!g!mNe>K&n)Rc-P|^cVw+Cd#TbrGlHQsNyyg6D zPo4&dlAetM(*Hbs`R~0~Qjs6`|6v;Z&vM(}mrxtvqUJ{wVozFx;%V`yM~}%Df`bDe z%@1pSOmjQi1&+ml<0CVEbqMz*`#I~?Mx<6YXxbs@%L{NN5w`Op`#kSHNZNoQg5R+kAuoQsX41a`P zdb_KSIHe6;qmrx{zSXYn-`FtTw1*`Pu)LzKS1{Cq!tLrkKDx9bymdj!Jyy^%3c_&^ zC!XLUH#caDxqq0V9>x_-wD+4uIC_Vxi)U62^Gt`ddQ9fAk|u^tLe2WT6b}rI8h0=z zJVb?lq%`wxb*-8cMCc5F^tYZnR9KCP6uncf;rEHNu_t7B+soTAeC{z>XeeN=k(Cx; z9#&Jl{_4X9V#Px}g|L0LGh^-h$3Y7+ZImz9=C{=x0J75ToUq#Q^(l+Dc*SrJ<&A4M z4YqSi%~qbV8Y1Is&|djy4HL4nk5hxPc#B)|bB-=v1)En5E#BhuaO~tYtZ=y&?tJ8n zzdTfM7dN{ND~_Gny@ZGrl$qVpo`2KI4ZF@UXPrI9ArZ$Uo(O&jHUh(fcYsa_lG+9~ z*e7sa!bfj0YsMiBv*w*tMKNe}hMuK4ohWpA%!X1Tx0@=Lx2sx|J5uwU&~6XqGW83Bf1b7;32!I zVeK0`-dlTU~v)$l+4LD zv8R#Wm;;MA$+QO+g{{XqusCv@6K|sw_6#jnf$*)@dKU+&9 zwXiL&S~$KY%S@v=ut(UywZzyVHggv8uk4Ev%*Xoj+)lIp=GprKS(Bw&(@tNL)?GVJ z$wydy?Q)v^Qf_;|;Kw^Yrge1@>#A8_4>I-4No-?h_tf)g-{>!hDx?{wiFYvwR1PuD zZPzR>x$;Bf@`t(gSG_i6N+dhzOWN z6cKzaFPC937sjeXoUS8Iv02ImRMxOfQj6DUN6Qo{<_#8lLP_+hwUn8x7HlZDSl%-) zum$&n{8tS}2^)!L#Cybo8Ws#T)fi%+Q#U(B;%eeVe!?#y=lRf2++PB3ldZZ}EIZck zi7<5K29=QcD2{e`?qK!Lt$4UvcFU4=5AN67cXJ_bDEp3N?Sne`o?#P&{jV9u`hNHu ztZ-_k_-M3{tzxa3liqjx_{81tI)9Uq?~1*UyPXr$bq|W2&P++xeRq(DnN`POz9D4W zb)fC3U-Oc1%xzAD;JfBS*mnH#D9AP3IiWPTrd|ci+@Nx!J-;wDG0ab?X4h2W-S{VN ztjo*jjeX{#d*gY8s1sxB7pq^O<7$8AtR%;I2vQ+2!o>B# z<2W91BoE5-VekM)4#gPfRLg_S6pKg)-kyVO@JRIfKF6J7YYTqUrT7l{&xQp`5IJ6f z{*zLMXFe1b!S})Yej+~r`O}UAE+j~SA1e??;fhK<{J5&m_J+jAM+Zh16eW?pK0 zNtyjmBRLDBwDb7k+WHt2^B_G4jt(3M(iso zTRng~9a--vG|DiCF#qS%E zha*4 za6dhtA7@0@$uj94%|DG2qYUk4Xkr>?LQ}fCcwn5Jke!WW&@;hlGz(r_PZu6GcY<%D zd)5U{1=V{K?pduxt&sEs+q!uf>IYu(T#$RS_>-BWN zxR;IdbuiLjEa<7AqaSG6wMPimaTG955-iTo9B)6ayEHNmk$RC;Q{`!I zXYjw+7k5rSpAgjNLw8M|*;ulBjN@Dn+V!&-PfUg%R!Xk!AE!|Y;{z!o24?1$K;#q! zLqURw;ur?Q=;gr1uXwZ#?cox);f_Z4!am}F?xkHM!V?l9N@ikkv;5rD{fAjb!G_0z zTYs;Cc`Ru74TI<#Y-n`U+xEhsb!p-E3MX)lY87%qT*PzKlNLGH_jY>?Ui2Eg=pE&D zh8`>ynWhY|;;CO3%kX`J7f%KEk;n0RMZ~2J6s7hyS8dU)+q;oupwW=_q9PQ)F-~-U94d?I6I_leJ5CipD@QnkSe0YXE7uq5E z5o*Y?iwSR$!TdF+W;@>Xwn>ktt4(ClbR?&~l5~?4PDVRbNwEZahC5A#WoGWoPkay? z6SQux^Vjq!j#p&g6FU$*{{=ykmWc%t(isG%y)>06$IP&^38y~Hj4dhvbe@}H%Lq8M zsjg?jSr)9se3mw#e>6PWWFj4ileOjJ5OsO3NlpY3I>JemhV~!Dv+mZv1$3Zo7CUV*cHTxD4=*qZ zaMl*!q=FD~EjURTa{TjKOa#mQ!6#$D!M|j>$nnpiK*EPB+e)InPjiF%SK9@_)znYO zOA`)?{=sf}#OPa#)v6S2>AK5k>D%RjwkC7QlDbS)gRxlbp!QrfEnZVW|m43H6q>vb7O>60QmcV@fYO zjhv}TKAZGz*BT{FME!>ST)7)`-!XN-q+HU(`+NkkWb;g~#%PKM-CkS9SmRL{3&;|zo2Uf{Mcq%WLtoc)l-Q63kq)b~HQSc1? z_LsE;@tD4d_NEyY4D4)lEuuB8;lqGx3Y9Bo;K}cHSzCvXyv?`kq@sJHo)s!TznantR)Z%_N)|+i(+Up|Wo}8yKrDyP~oF&)F#hi>?s-IOw>$=M&wu z8sCRo)q0lvLM3M?aP;x#%tE>~#hjozjfN}s6T6%e5*<;%W-;z9n(?jL>=J3x-blAe zu}Osjpx$htB zI|#^r2vjkR8^O(*imCKA2%O$yZqKy_x(4FdL*MncY666G+QQvE=9=JMN%yiV!qF&H z{)ZwG`O8{^3u1OG!w){#`*THjA|`XswSu8(ws+gL29Vx;*9wp!oQ6LD_yn5X-@+vH z{yf^NL(~jei=ElQ=Vyz-kcV+QwR`5mVWGnq-fHqa5{&$f>{3qY%MDE*Rv4je(`oha z*SNw2ZTKssN6I!E|Gv zc`(|)L5n)ZhX&iTGh`taTuf6ai13ArX9ahhqMOWZrag9z80AXFZduL7ab(s%qFEep zX<8xFa@?7&Jg}WbW6y#R%GujlFoMm~&h7i%XH+!x*_SlA>;K3N^EdLb3kNR1@ z?V>CYVqwc3Yn*x?j02Xj_>gtgnOUdJ8C!*`zCIKwYVg%Xz@2OG%G zmnZHMC>!e!fUAwsMF+t7{vfi@&&-~8rNygwiLUNR^Gss|QV^x8 z{v|0cYNCr?(t6X-oSCo4Q;Zhoe_9M#@c=()+h7*DW(4GqVR8eo9#hoK2w$PfW|(R~ z5G@n#nu~%sY1;2>Pa)jwvNNc{@h^msJr>+$?ui~|=Hl-edTE!4`n;wUt0_YNdQS(< zc7B9;bkZMI%J)QqEb{W#7}S^LMiy%a`Q?F}>eQrgx0_YL-9AJ=-R-~7-#g6;x2DZq zbA`L6i=bZJJS8#o)ZKoay)&$@yM2qjyTvAV*kqec`bgaEgSzp(TA<__HD=PiRjc2a zF<$Vv9r;fxLZ0sl`_px&lkE(yvTsK%sg)75Sr07<&YER#rZOxuNPxJIhj-*7d_Epo zv?iJc0ms=UQ?z$KR4nD45S)i!90>i$+9}bkBkP_JZm~Mg^445LGSk~G!w9Y>duzHd z1-MY|mDGe>Ju#QZPs3d_M$_<2`PtDlyc1^ct)?MCKg={t6&}srrO{naX2vjBYtqMq zOGz;{7t5+e4J&`c+k?zP{I*e90Mrnw=XNyaU({^eBjM4|FC6ycp)Zmye5aWuh}Wa*3PQIUStn(syIie2$u zE>xb(ceyp?CE1AENy`+^%+TtiO%~qs`^XqQ4CUiQmkNZ$&2c>T!V@u6YkX~a;Ory| zTWA8jNgwYSJ;I9*`I!TBRlMX>)m!{NE2(8y~`1^n3Au$|K)p|pG=Afl622-GoPN8xJ zJMKHAKvN{dP6&(y0j#cQBxKev)^D zrp3KGLrO8kYaTQgDsPy;*cnL^sj~MF{ARMz8#tJ->)&R=#CVTKzuMoshwqwA-w^GRnHk0w z(ly&E`HAL+ahT~gwAyFNBp@O=rm8t^Fj5-5>RT$*4sD@wAtx*Tdqau4)$pWty|K`^ zbcU5pBpk%^#M*nSVO4B0c8z+cAx;{O^NG*k1U&v_U_=ysb@PNYCKN}D`bCX1{_xa z7l~S5T=D?9N}@eC73Dm&qKm6~uoe4R}(I2U}%FZ-CzENdPc&BOpLjy68*oGx6( zadTS<=y!p#XwtKV$zM_Bb_(~`9vRj>pAe{-hb{R|CSaF{`Z6Ep9$-492{&`RHl zDYWE?$3icEE%{fZ8U_ zPu`&?`L~(BGmd)Od#Dj*T^wmY^D)?zkIClK;vN$N3#){8cDWH{lioABa?Uf*z0)X->*gL(|z z@~8JX{(X;_^M?Mi+Tf2fwdNSnt|oZi++@ml@Gf)?bD>Df=4MZWyXz9)=M4nyCn$S{ zz2-5-pWM?64ebEIQQOY&7PsdPml|wMdizA|44@sopcWO8sY_>g+tP|p%2Nc1FdsK&>>-m_CdU{flrw(^ z6ah`Nw-ORim*g0J@5Nk+q^kMU6Ycp2^S~;xkv~(_e@bC+hSd-|1-Sz2#ts7`!x9uK zr@qE#rFV+FLKsCb|9h**>|>|SG&8IJpY!KSuL5sK^1sjmt@8%+!fuHaYgCHZi<%Sn z3(Q~9WCpg8fd$ILtC0pqr}Kv>aEwbidOml~&ci{5tDUt+G;_u^CZeN7{O&Ng1K#CN zHh(M3->K$rnf?}6GFY|R;2GE=abnf1f}xgb-jfMaY~AuWRtG|4#!E3#>z21=V3^Dd zj_1z-zcMF7z*;eA!cS#G8%j#vt+{|+@|3qz^weF`;=KUoP1Oy*X77bMyGfC$AYVLu zyuy3Id>5|+)1y5%3!pIPc+l1wYtTgYz?%(77218w{8>N_d!MR)Jym_EQ2D}$K@5u@ zMwCp}-8x9V|02V96gya`^e7hJ$~Yg<@|MIM3QNxBeg!7%rf?>U5Fpy~JN`nYPa7w-T#_`dRq6jJ zb)9Y5lfjehOnx)!w%-O^6q)(O%tvzOrX&kOHI=*J&W=`Yh^%5aXFn)5tQ}@ImU36l zDwwENdd@1hsj82H5RPhtJx0Nu{bK$jy?ZjKy}dYeB)HQ){d7K>kieH&YUfEqoD8+f z@Gmw4s;Q+l>^J;HVdZZ0t`bt1COw}~5Z4J(`QBHhjU#nkD~#s2bQp?BIEz&188hi! zRgOt-vvHiT6H)^>?jVWk7y;Qi8woJx_!nU;C@K20NYU9@m_m(p^A{*2HqxT(Ax*aj8U&q^U~@U2hM?NW zb#LLyGVhfU-SAsq&fL+>M%_maxL4R3HQFKb+ z_J1JAdQI*1BbUwguF#zPirxPLrb?b#+m(6p$vyqsd%n*a?AB`P?wHU!) zjn{b4@nGfRFtKcdp_61&hj^a9(^=?;SZ87#-%QqhN5zD~wOS!@Kl^USyJn-F*Im<1 zg3lkFh^9+cGawz|*zGof8A19O4FT|=Pq1482uebhUO@$c`64#$=e?M$`<|^@wqDVm zeH4Mn@^yNc{ZVr%E8T?9>OBS!Ua zXgpHh>?(W5o={^PZ|N-f z;LIfIo26K4O@Jio)DJo^6VB;NLCz&)YO0WOA-}}&ZtOOVzt%vr`e8y|vKyjIY4>*X z%1>6%kK6S=|2OPg^YuA!=1-!#XY;IxBVX*&Gz4%0drip4nAKGPHruQ$vh1xoa>G$p zf@3Z)D^bC(i~U^HM50QP?mG2a`cS$H6(cGUgT zNamd|q~$>#wE;S#;MYq1Wh237ScdiBN!U7*V~;MpbB02E|EHlMt}6vxdykLrsDm92 zrDd;Yu5^W{t|n@3g6)EIW2^}2e?;u_)vpl2)gf>j-MjbFQF{u)Z$r4iq~qav;y%;A z5Ze>=yR^|TkPw07$p4~UY4>|uxJ|6p>+cF^1| zmqz&ehpkCPrjhc`bNqi3mZj@1Gzhl1JhEQO)=36X|4-J&2=W?u5C%yUDBMb;BSjUH(#MOLWM>sBpZFt|Wx@c~;l@F>)N|5PIs?oIW7cJE zL&b2IX#U+R+$dE3^B-8qMhe+zp>jP1iOJZe7eeIXauv>3%>yYirJLp3dl^5diexR-&$-cY3`gt6|DHQ<&%7sPr>d?ND$fQcut|;^$spU)gPL=97AkM2 zeaFCu=SKkqc(C-G2h^zor8%2a#2P)OswZ*252b2TeUKgM`z>9u^=Zso9HjYonlRdf zh01*gcqa{a9~T1fsfEh-9NQ8Gy3KAIXtO|_x3{M8qq_$oy~LEOQl~)R{wJFZbLrfZ zFTSaB44a{^%D?K^{Qv7M^Rp(%&KTUqPNPq_CbAsraQ4jGqoFQSrwo7%l4quC9U+%N z7RH~#HL4>PhwWnldK7;Ziwp}SMy*h}HD4?&94)Z6P+9us2J-x<(JHPV;(aVP1ZG<6 zT_&Fp)GQ_npwzo3={@hS;T%(;^7jT7*~&uYH@NbiFw+I>ob2`ItHDCrmgLNU_v3kc z3Y8x&Hf~aOMxpW|^Qk)a9DZdibE9sL?tKUxHDgmIgg;#^R33ccm?4`(VaMR(*i5q> zEy8PxnJ5&4S$O4lZ9x{Z4Xl1bBh4&g#vC=vRX=z;}O!6lM0n} zw)8U?=#YYhV+Gn5DyQ)_oLN)Hauh0;(}H9AqR|oqvd1pC#$JshV0gUkFZ3n42fI0_ zKDlvYqL3Z#zM#BtrN$OCf(hYQvD%nO75l%#?tVe`w0_|7FzudO&JUJpbT38_!!2Nz z0JS>{d$!&B{qKMOm0y|2x%*xq5b{$)RN&4mJe--Gdm)lJfe5Aqyv_Aj4FA*R^l2bb zE;_bzAW^1thh4U9{p`Yd&lCnT)wv*odzO(&*)#N^seAZ)Yxx>uub}2KF5J-_xl~#3 zZ&@VX2F$ca+o1{Y?KSl`Q?SN3ky!T<7&3-L0cB)r36 zh3;?&pk$&=CfUUL=rUz^2|SLv6SiqxFb^zZg@18}ybVE*c4KHP`zxk;jLzcnaEV*d z=sn!~BzuIJnnv$uX1!AU>IWc|Y(`z<-3 zB#Pb1W5T9N4;X2L*@RBm7iip$JD+b~I9 zQ*eenSoqbU9g)n*26c8G+EJRBR=kX6|BSUY_QgAwus#lx!b5(Hua@vrx-d+2Ef8zG z=k?_-pUdiIc6`C@Qa3R_JH^Q6k7;}9&Fkx!{pT~`8tZs3d;N@rux2sna|QtpI#%TR zU&|ey)X52?hH?y*{TUy*UHh;YRX(05oYc=Um(tucc^X*4bfwZEXBZ89QGxt#&w>QnBT zAsG_Of>^Ho#gBp#C}!PGDlDGIKP*LsIL;;Rn!Y98f|N@n$Vn(~2TI|#=*bJwp2xXJ zx#tq|4gvzdj&cLD=jxwj-s9B&Qr5W9MZa_+=dee;GuZfLJ}RA)DxF>BMDh-CcfRbL zuDD2;EP9J7oXEZ)+O2Yz@yv$1nb)_yB+jF1RymXii^@~=&!wV^o^vAlW@qpbicFQh z%PF;|m(ta*VRl`S`G6@;;o#AV6Kctj#6{)V8PeR$geyj_T!ie5;xh#`DfUzIq1Evw zhgJjZsycOKwJJ{;uV@X;vUgd?PE$#f2IN-_slr$qAXNZF8OMdQx48ZtB|ka-!Z*ya zhv_Z*iIV|m82HhMs+w9g+M|}~nZ$v6#qgRqk5$J+&8_Pq};4Y(ui*hBZMIfE5Eak)oheyJekdEKJkndM*%k z$V{@+J1jSPH?yKYGjaHiMhl z6}82?MfZMZZDL@OkVk%UL=g05;UGF74ulzPw!&m&SpoDNF#G@c96;U=@7DYYW+ z5d(UvhA2RL_Hn}e6m4A#*GrsCq#9SRc*4R*Dfg9(Hke@z^v z_B&*Z+_l6bURNo@WrmenTk>c$0mWjb;-XpU$etyU$Jta%fUaAFQJj*mC=Avglw~3K zm8u3av8BkLO7<9Z5qnMvk8Wf zo@5`6KTIZ^`%<=ML5=nbQr=X(ZP5$u!o?_MO7Dvax2!6f3}{$e0+&^6zHQz?#Fi72 zb!&hQl6`hE%YVdOTuX1~$SS18#M&sLTdK4?9eKupH9uqQwNn}`b`j~2_KXfVt9Gfi zc*vh<-?(3dQ&<#BFjc<|5zbDO_-u)xuKnDOtz{y!qVMpS3-QRY5d9Fm z-D(20K;1_Pz5l66tCQ3EjPp*sE>8|pUqI_1@=oRqu?kHU;vBUU&D*m&_et=Hdqd=- zO_FKGq%7)l;0+}`f&@t2{u#X#0L@`-PJ7QA=E^81ZP^j!ZqI99)pz##K} zj^1_=GS(7*Mb!lSicFxqJ%ycyzwV!$D(tZQb!tjui5Tn?>VMYpjIDeXm5aHan0u`> zTZWl@*r>%kPkD_c@;c49z2HO!oO?{AB_-WXeShcv#-5+k4kPhg-i05}2PFjPiE<@b zdse|GAD)toE_#9!NTA+#kQA2svtx3lLi-*jyovb#9kegx|7l;j+D8z>eI5I41Lf9M z4V;pDl>;Z|!wG5Y9gnz$$j>-!WF-${2rMf~5%9*Vzg&?p<~WJm?<07!iT2E=kd&XD zC8|nOodN6*K&MqZY3I?6*hLBCXKc>1n0!CwG(M=_5h<2M1DqvoDi>m z72`dzH%Kv|%zG)-J*7sGqF$Nt@OdIIvJX+*u-b2+p311%F@b}!Z z?Q&9dgTTgtaDOQl3Ilj(ao3gyH*|o0h24?o@R${Atbe6*ssZZokD0-VLyZ+@;;vaL zs{9%C&&n13X`k|^$40>;f5)Z9)o~9S+^_Hr9*+-5W@k9WJhU3lQ&S6C1(zw7FRt0t zCL?pHCY___uaO5Ve_`B`aS%>Z$Vp!Pg$i6VM9g`8EU@uwM}%H;0zX;*d6~WkM69s6F_Ix;^1@BuTU`e;L zLRN}Qt=F}G!}#Ow{B200)_S}~+XcU4#fR@V#^o3kT5We{Iqgn(M~Z=lqdiAJH4$gg zgw(2s9KX|aB<c$%2ckWHA_uzCo{h|Q$D8N) zjckQ1vKoZeUJH)mIf6)ZUbT@g1WtS8({$WOU(JXE1 zNu0zVSeUQ)rDZ$p`Z}@kAm-ZcTExGm5!m@l&Fv(vUN=`p2-x?M@)#lDZhI*qU>cWN z=!(UwV!l3c6$CH%TbP!%hNU`!>tuUhuQFgLvRbHnJ{TX}NiKapt zwUkE9n28@Q?UScm4!F#iXYQ6Foy@4Umq={QbNS6;_xedBF!wqQf%Lf624|pK_W+5- zp^J=8jkg1xTd!>KwPwe}%IyZ_f*AfAiRbY~XuT*aUo)CwGNM=2G0n^w1dE4B_reNL z-XO>yG9SkC^F;6~y~5QbmY=^JN}&(VABaLwL+c`K5ANB#`<3)9`v_q zw{tFtP6?g!`5|zM1TgV4S`V4YA_2`J0SK!Q2y^6B1fm^c(6!SNn@8nu#l#HAT`_ak zshKIpuK?QBJHx4z^2(~bjRFdjh&hfS42Brfb#cGSXb>&rc!)qv%itqN z)tsXev-m6Zhfy)e!l@|~MuyWun-x53db}V>YxBfM%$blJ(MfOVt<9wdm z($CSWrnw=`nFUCt9=iZp`SuvmRt{_tZ;6e7DgJ)RwlYRho0%>~9VNMl6Q^!hpV%e< zobt+3E|NOu-oMPS_L$r$FG$PS%9=YT+y!XdzWy(V3it4 ziI5EOF1KJ3Mw!$|qzxk+Dejw-Y0@wC5z47K+l1ih-6@^R|9($4tBWsblJTBN~a^ z1HxbyIYgu+mDx?U6gB%x+f+V>nToj|cA||xH9Dq1(L=luZ~Yd&O}LX}0zxsuNd%wu znsVqmO7jsFgwUzl!UJm>+HVA6ZfwojNILSG2x;6Y{WpD>DrG22>1XL@HU2STQ)odu zwVaxe?W4V?a24IUv?7?KV!X96Ay>1v*K0b72tiI{<*eWe<9;ASu;$=m-HstxvqiAq zuclEUg3T)oOu{sw#9dnkf5jMwLt)@I)v0fq745yB4CBGdS&61twD&ta1|!_-zzAHH z&FJPLE5#&Orq4c^r%z#LuYz$6h1zzIYyx%NBmSHx0L(VJs zWxb+*F1Qx5RxhuoPxV@b3hq$*b27@hp)WBJxC^;evDb)dV~`HCxNoL9R-( zALzcX=1*MBy{u{;r;hu)pJo`f^~x6V%P=g5BiV{vS2=s5mdQGa;`OxSAb+P`_JZe> zYnjd+F3bK>(VzQbMBVJzbu|aV3Qa9EPUlZ^&qoI)F+5O9&XKumW5IQ}{HS}N3rJ(F z4Xo1MHCi3h-g-kKL{|J8Ouu%+si|LIC)b-`;?MY)4>)$?Sp-{=NK^SO5su=cz3w?v z*|9VQ?^8H)E;N;cOv$Q((5CJ6~+xS8I z0BH2qgI0e@*@N_4v)|i9h_2p0V^k8tMGIN8s0u*UGzbn;HB$%>5x6zc|s7mS09*Hi{%R#Yi^ zV2zn(eIMWFHkCztPc(%dJ!6N@RqH+Gkk?ehBGb77EuFWt6-;*O_p{$(R^=Tn0=}95 zP;%ZeZGvz7y~y@)5gP(q3w$p0Xy&W9$~)8_$t*=&h7nxZ$_S!U=zpO+@!>(dcO<-? zb*=3)O6KjLP(#QHSPB)IYLJ$ry`MAk&M|HNE=7Ww>|>3x56xEH49wYOs0n^Z+rrh- zQVaui!b;8=1^WR;S$Oz5gK!)W-+nc1UqD51glW=H?M@J`?Tz9yiB=1#~!%Rt@J5vGjGH zSyhs$I@A=80FmQl24wDn;Xbk!$?!M?r%Ul->BBdZ#~a9>CDSrkAyJs@DR2ytY$d8} zincBd@-^JiIC-0U&%pXCxFo0U-o8zb^A)@gpG-7r#W9U$QS)gvn{U>D7B!vWo(K%U zUP{EIc|VEvjJJPrO38nAPAOTLol$AzANxoi7Q+OCBaj$pmO^Z#MZKB1D92J_}WVA`UOMy;X{_zV`mtW5-MeisZWz}KNrP6ucDL=t4QeP^$Ms@d z9=%M~-L2syk2)g{Mb`{*A7r1xZJ8UsyC5_-x3;*B|3Eiqv85%%sm_69-PQmdMCRP2 zyR{Ae(ea+UAFR??2`9$-cVmm#%jKvr!u}c05;wN$n{bqrqu7P5dO?{g+*vmuTq24@ z_Bvw;8me1p0rsf@h^SN0c9_OGI1kkgp-6VxPan-tyxxE%f~2+gXnA&w+jA3z!wt$rgdbA*IMDdtWYp={qXris-v-v?Xy68-9(Dv zH@DGko1GYl#~5a0&-XKRIrVcw+uH>1+fEpv*-A^o`*Kot8c#SgOygl%8>aKaw1!mQ z#lmzhDF-8Np#_MqCp6nz`7+XNi@lYdn{HdVjrQJ2gOJ)!*w3Ac8X{HIL3lqBrU%1R zYxK4XZ%B78{^%`fvx7DZ`DJPVl*|Etq(QNi=nS!5|n!+(V#!Rq6 z@N;?WLzUugT2N!!kj6ryXMwQIDP4k3#Z3#!xlULAN}6(e1MVy+F)e#P)v29oNwlYu zJLK{O&EN%$sjIE$TjJ3kO#V3+(k$gm^!AqV0cQ~ysEKC7pnx#+p)YQhk<{N&n zO+^gv0*Cjj-3@l&4WI=&eW5Se-E^vLx#{{Lb1o%>hB~fXCat%*QGxJny8c($k0$Fb zo;7?YcLR;HRp9U;{^((}2kTZLh$guBDKaP-76crPqSsiFtUaSN^CjLuEO>Yhgvz$u zyVihD4&fH=#iJ8J4V=gxr*2tuCo9Uuv1HvBuRxOZHgxO5>J57};c1y(pHpk40P8VllS{ZC#7S+zM?ScA8v+$RgEk zqOk4S3ctzpsA%bSTh&J0OGQ3LY{&5faiil!xrhzU=L;gQx%;BRGSCd$a@w^yY6w46 z|ARCvs*XB!JyEeGomsZaXs#ARV;Xz&9OX^BH_tJls=Uo}lzp2Sa5lW6$-S~Y(Fbzz zmp#esnvCj-wJgBxxjm*7Dl=c}-jr6~&~hmfRRd$SDJ(zC>2IDRG-kTY5oy#HhE#_6 zuy(K|`kUtL2Xes^MiM5MW?yESe4At@6D2gm@xfzSQv{k_oV43ucJ0u%n#JBImo4+b z?u7X9TMW>i@m@9CkJ9&wN5Gap1g~HiquP{PR;BB?=DHDl2`Sem^IE+(m~Re$Upm!n zP7K?l**$YaDI_3&90561-;doO!7O{`sN6IYzWSg(A6JY0pw2#vUKP-y+^88;4h z8X!Ws>D8?_mvgNE;%0R0PumQ%qiBo@&mE)jhWT}?jcKG4?gK6(~DeouvO&Egcv_W*1c)OrG zrRI}V{jThLmqZTW%ph|*{n-M@bS~t{VBwFXCi8;qiKe{#kM8GHiF@lU{4LIl3=Zq! zCE0~Z_f8FjY?2K1y{oF5Tp>M{NCL;(DvHu21=>d5Y3005>AGD)mYrDUVJ>YaCS=v% zW3+b}3zqJ*LPmQN+)=Yx&p1fU{LuK>aNyu-PQcKbAKE!d1j6qCPX=<5KQ({@xtnOG zu99_k&53IanV-VMqMc-B=0I+Ln1>zkYKaI;UFQbj@sOu4m{)MrF46%x_~Dg)010GQvHgofNxb- z&=^WisWW~hvKOeF@nB#e#QTv5WwW24sY6@@U%g9G50)Her5Qe3LJo$IQ9QtWGVSdK zDS}6r2?%$Z>fJlrsEh8OLB`yxXoPh9#aL7SA1XOnceT%9mzYRG{iy+`u4wS(n@y3nI zEVV&H=~PQ=91hWRJnp2WUa@1v#x;IEisO@pNsU`>^{Ano_oT6lv7=STswfrL7K#ps z%PIBDKtv9U?3!ykVs`W#*Rx}DDcd{Y*kqO)xaBA$U3cdQJ;oDO^<$2lA;(KPa8_&P zTHyWAs@`{){S7nTqu~8piqrTCIKy`f)<+F?@f9uy?WpB6YDc30W7tN?cG>(KSK(CU zc{6vGfPR^y(`EwmG|kiEVo$35czYg*6mc{W+gQ3W!T%FBCL$XXWgJB2R0l_qIa9%5 zzYrveW%j&l%!;i)qLTgi%if*^!LOv^N7)di_S;rX$1Y z$n)&1ikMnQzA-g~0$!qnC_= z-{P8yGBG>?S&{eClHnCv-5_;>n>mIykf?=(l;nwaW1sS1Di4_8)~5*Ar#0TJ+E8UVPHGqx82Wf!v{%IVQ0g^)*hqI` zV$~T-Zk(6g*jdVN0>219BvWHNb;fuDD#x3k^C|Se@fM=Gz+-~95an6|gjpzx1}ZF! zrQN@8<(gS#7lt%v@6+xNyJ?J|MSk(5n=Q#*p2QEq4=o4qLvaAy_1Q#xPjtPO|6l|m12w8u#BP=3j^0x|Puksj zi%ssZ$u^RK4Pxy;ltLDVf7fd5#+y5T1&ZLz`@taQQ!IStxDQri%1+#1Y!sQoTz{RdYN2;+ z@Sz8Uyw_@B7JH;q4d%viuhkHE*T#7xKH^#d-@8`qg7M?$Ub}?{xwy_?-RVe&9xF3i3we6 zgf{=K+IH^qrzY#JH6ze-28DPx8a(iBGpncf$mnJ>fTHcS1)iF>7gx=cgk`;g+|l%!xF`IXiI% z0cRrFla+&v*-6QsmGR8ndnI7xPn9Wk8OPmb8TjO9=3bqdosM9gnY_Cpf(NjtlXabQ zvk@0pYSn{qUusXb#oHhVyA5XmI*Os~Cj&Gd!60_xQ5q=DAFfk>O-A0&&aIg{DXU!c zb$qV3DObZ>e6Zn(z-tO4s8G;8<9t ztNVbJ+Td*v3IeI@d+a5!IMKjjI^77tk)QG=2*@+VxwXWh|7*=ZA9Yx1aD@vgf$@Uc zDSmwGNc3);%6M$=JabEYW3uYh2c_=IusGwsKV9|i;Ax`w_+1kYHB@vo0{-M_0#@fg7+Lfiv`<1rdDCQ=q6{!{6&o0qe+jz3!KNrr{lMc!wAVE2p%%TB+yv>Z6l>+1fb_j@$Fmo9ev^G<@)&l{^v!OU-)A}q1gQ=A&+*Qu+tRxK15_kRHW=+Vn^!S!^zg* z>kran!TTO95rw|p39s6oJYpV1^Z^>}!A41hy>aQrZgw+V1o znnmw(^yILdJ92gGQgC>adP+N`p?wdIbVsjAZV zkvD|){NC;Tk-w+C@+@Z|JmQr+#35z&=z&9llzsu$&FW3eTZeI3e5Wi=iGt` z+?{F(O|br$?GO-pi`O(9nSJN>hL z6W8QuVxunww=_(xngHFV@fX2k<;I^hwj*SZdOJFvfnu#Z<&j)i!KrW3H8GBSkD&W9 zn}pb#asrlF&j~^pAad`|L0K2QqIh4hLi9WoXnswYLrD#ZA^wmy;-sS$aTI>l&Qw2e_h9%aP)E zA_ubx>o%kBZfJq0DA9K)xMVt4-2f%WZ@}VsI6}!z-MIiHFr|M2{|a-6rnbM5~*)07(STW5dheWArpdWjpW^ zp^>Dcg^5E$iEXVs2>x7;(iFvO$(2(r>q?)xwDGw`*-c*5ba!yWj=Q0`K`ljl-&CW# zt!faVaT43&TqNp=T-DZ}h%VdTd+17HUL~SSpY{GTdOJaG^Yy(yzjC&>`)jXI8G7UW zSwfpW=KYdy*2WUv{`@Jud#X?+uy)xng!=Z&Ahouq;s;C}rn3@Qx2VS3yZI~W>lWP^5NMb!d@Za-g<8&Q ztUF-yb6$nbLphkL&t2X^0cHs8I2%+%^t5BGyXdtaBe4!9b!cYYE|r3}FsTv-xMbEl zpqD8G9H_;pSfvgmx-8Nty9yd#_kw^@(i+-BUIFoKhLU0H|JK%bY2TuC9PWfMM)sYjiNYB?z=JW)6PHfJOaDovIheI*wv}6I7{afi# z_H*TUo2vGb234RRL&Zv0)EnMq^oB?iKM5wk*XnftU5^wO;kK$4KJwd2qhIZ*>J~WA zzJ0d1Md&>{&|~nB7tncIr^iMtK5OhIV)o?d-HXa7MxZ=0xUr{798Ug}+?CTy2&c8E zJS?D$r+i}BpA($^Ur%%fdS+`5pQ<;v&DQrpEzR55Gu!0yCti(R?7W|&u@be5J04A; ztzu^p-LHU4WF>CZyB4wGE64(>vcU$ zZm#O)8sf8&BsjJ93GmpG70a6 zpj+*O8mZ0tr0(XbF6B2K0$MB!O#@U0vU8=zbW+-HD$mc5QdG6)Bl0yu)3%vB@J%VX z{|A`0y)Q0_Hgc9p2YqnShnxuHRFr}75=FO=>?pr8WRLOPldxLBruPZ&OfXXE+#>hc zZ^+lGKy)PPWK6K3TD4p(yhO0 zl3+kvJK+FDw>s^?$$9`9#QpOomY6dO=wjgw{mo?+e8I`axDlsdO{k8u7o%MMdOh&) zk0Q={(80~NK+1uU^6Y0#i_P5%x+W|Wq*&xR$SSH%&edf!aQcWxEDJsg=urCRqhmOOnO^Ytx3OK z6<2F`@0j$is%DekT-B;{-L5v0o#{G@Aqn^7%=hfs$#0J_K!&-7Wll%fJ%Q*ItlOs1 z3K!j`KGv zg%%p?&}Qy}SH7ZsEBnU(w^5*2drxDCIR=lkd)jf}K_dkZ$s#cel(&Y*OZQ$kUwx*# zqvy4cvd1ohlbsuX;wgV-Doiw8cbi$%n)mW!}Ri(2&1^?2s%IGcQhFE=_TyD(N6U;)RdT$B}8ry;1!e*wD=wXwLFr zBguf#i5Z4rgXq1zx+%PJ1~zO7FWnpE&K`lT!ab+mJtyOy)8(Fn+8bO7f_G}RFRKWm zBJf6(AX=MY{^cb_V?{}~6bn5{&MKZ~MP=|za3xE3+B?O_?~j0yv2#;}RpX3bB`?@` zQ2wVY65G^Ft@t1QkWD$xM@)P3-KY^4=Z97C*gYKJx&wAND@Ifm^=Oz z7lXSTMdb*pHqR8a;#WiyprLGBAMSd;Q2VaK?P1zYDn0rACyR+qf(}ZF1 zUaUuXTlgl+O8c%LhW9rwXFh_2wK*J}_m?xw(5KWo!pf^L@LPJoVJ>^FChdRyra3B5 z1dwMM%#^(_X1VuB@z$5>>d2^xSL*6rycK8py8(11PHEa@21xT1kp*+0Il3+{gloP^R)CF&#A{-X`4wx61f?~3n-U(sZIzORD^)Ye81n`doBrRZ9mWR zf1dwy2J)_V@3r53S$plZ*S@X&SX2<#SmPEl0v8+e+!N#=hm8es+(dI!O)a$;JE89H zgpje|)cagx(@9aESQEgPe)NMR(#hAch!%Rw>?_=KlBxL2iC`^yd?>mtju_ChTa2we zR|aS~sX;^o9pj{+P~`h0L~Jh>F*wyxK=A_UCNeIzc(XugFK+N`u`IY;A4k2}eS%Yc zq8j^ispLwbmqr%Zsgs7+Fd+aE9CxK)YUmU2)hSP8c1CcFKlG;!@Q(*amKp2#h91w@ z$qR>0eiVT5IWZjS2x>S5)pMcmPYA%D+lMX~I)|tuFo&BtiN-D@W01K5#*OID3%Dv3 zd+K*&q2%7 zXKGJd{{&Y>xWuA89c>KcKfw7pDi6!WS=vTV{_@!Wxe9s1=#Dc2ip{s{;-*Nno4WWg zXB1pxvgQsJUq3C+(PxbK1(7yLweC!$be<-%aLKu6d_J`;M0{1yQOd)Aq zg_{@sB>u~6lhleYgV0m5jH%(0ovQFd#rQ0C=V)eY63X~cBiwA&vzf#2uLOUGA6iIX ziiSY=FZFoG>RFWpb;N}K@`wNrZ2-Wzd2mG^%#pl;>~Iv>u#+$0hnP{n%Zm%RCNOp= zA*f|vWV4M`>vMH$YxC(p?msA@f;JAWD=6s0!b3O2y{+;Dfkl!xekn z5E5iKb#RgxwZ!p&Y#hIXNdc-@HXtZEBx`LJ2hqiRyvP$&FaZ-cgKd^qP@v8PTdrwk zJ86;H)qmnlcO3}(&k$VDsC}W+LQ8K%Y0C@@2%(ONg3B=#-H*ae__%;_Yp`zY*BX8b z4}DyOlPlVz_=2u?>Atm6$(ldNX774!@odfOc^8;A7Qaih_y9Q(G?&yQ4|_7-=bJ{ zqZs)4HP1_B#;X_>%=-|7hnE78Uv(m1v>65tN!(<(QAlUGBvY}WY~2Z#oZ*5Opy@Fs zj!Li}fs7}*BbeQ!s*GNy3Bl+P-B3Ae1dj3)V`6&*MXj4%jqGn2038h`4`B8c#q}F6 z#uf?<978SaSYj2k0qe#QG3V|#A3>Ia_mUNuDM$c8NaKYvEMLYi6;09aYNLpd6Z+b^ zg@QGtwHBXo02hJ!3lBo}CxRy?*~R=nP97t;5MENYp@)c__*cp=4EGRenh-k#Z%i}M z7nQ&y&3ie8#6_Bq1FpX1^QJ_oFh(rHv1FsIeFq^Boe)jZk`H9sZg_r*Oj}VP3dr(v z)nyG0 zqprjDtpwkY#^NJGvQ(1qKnZ?L({)vfiJ4zb@ zgU~~81ws=?9*CF|9132 z0HfKHBc3tn7hAQ5aCs_7@G7+xgqbNOU1H{c!C>PkW`?u|wiR~hkWt}fHrTwhh;zEM zrBz!L@n(v(tfZl~*=p4bWl1i5K?|fl7{YoUV)PlG1n(6TgnTw0#G=SczDTw+VE)8c zxTh>K$rgJDi9EfnuQ8c8QtI1Oxwwc};XMQ!uBWDsc0G%+gL{k0qJvnT>V!;L{WbxktuE6S?D2U#x6lgnh}k0 zUp1#m{|W@%mqKY5_JtnrZ>95R*yMn1P^YRl_}Zh#nU}&2pZSzxkAHrWW!BdH3b8Pg zz62aybYX93NtUI_Oi(2 zv;N)P=x_>fhsBgg&IKEM=JKw98Lg|y?Z>>@u`<`5njsA9kG=^zet}GzKVpI5`KY?N zgq;DzLNFg4vM8Lx`MOh_g_s*KzG?S|TI~JWXPGgzPu!GJ1_kabe$2ianSn@lM!@xa z^-w>K9)_ABzn?F2HCO+&XE4|VTmo*e42M!$eKlkh1ESw5PbjJV51_LB#d9kfK+u~8nSwZi zHRPKcFqf12Ax5IUNbaUGZZN?uMz7Nix($BT1Sx{1CHp;mZ}Wn^P@#@M#BbN5J(*%} zH*&Z;2F(n$7S0G>1|IbqEfpKE8Cuw-Y=(WZj<9~F_Pt4l=V#*jLeO(be%|DWZE~Ro=+l%VDs!_72=045K?uUSki8D_hVna=@IY!%{SzunsMJ z4ntRpAxHG>0OG4a;QATusq6xJI(L~*c(S6}bML@xRLSw4+p$B{QX9}i9}ikSktsmM z!T0zUu`!*mG7%<#j}vLcZ0CvJBH~97uQS7s;=)XFJmA_sDU6b8>!|Dm43rF&gsg*b ztTV_1N`_FYEnWRh%15^lgqU)}R)*L^6vB{tp_Ux2;X9~#5B8@;uCSM!0JmtsVAmmA z1M`kI*pAyc+;m|Ur`J$f4L`C^8iI4{9keMY{JqYGkiB$>Nq&_F|Mhtc4;n{-GwhRx zm>5`iioiD`aD1C5Jn2krM2X#5khJ}EA&L;rGJ?fC#%(zs!#~0U8RvDDTf9zRw%3V< zGt*{#6JViTcc!+BLdqQDjz5nvmYeI-&b+w z;XM&Uvv`*=c$^Ew?K{W>KG2Pg{WH<*j~+(3Hl{Mdn4KF%RHz3Ly~q>15F_#jKJGzk z4G%Fd&gryg#v1q)voE|6%;WTC>!CKxv`wJCh?eJ;vn}i&jSpZe5?6oY{s1qI!iKgw zB&B*l7nHM2H#Vhb5EP@+>|UQ{AI3;Cg*CkV4}RNHUkty<+`<}+7dKiRr;dup=v=o zd;^;F0pAYy(}GsUUfeV!u%fD(Js7x8Kf^5GN@9_V&&f2O#xs7&g9*N2OGD%drV+@` z!8ZRW*GN`Qv5o&+wwd&4Y_o1J)K8%q@pfhs3L6eukb67Lj6%e*dMGoBDFc)wiN48Z zK_M`XzFA=SR#NjEf7SwniI0KJLWP6$b_8bA)Wg$ZWRJ0nc$i!&be9NC$#rhLC~<2l zB;z&Oozrtt(OQtB&x}{djC1nK7Uqw3r{#ii!?(#fJrB7}QKB?ac!+-yqPc&5SSN@+ z>B;^Vvk;iT^zE)EBCeMWX48enAX@_u(S&;GOJ5i__%6U|#Hmp~qOWF$uF5d&ZVpF!MUL)MiY| z9$T6nyGtlomwUoj8oa2i9VQ)jg(n@ynqhcSpHZ4k5&xW2p-~E-;2U;TMKa53J&BVl zLel$?IVg%<5aD|R*p+U)sWtr4N-@PFm^_NPuumSvtm^bduD{y_xsFqrLYt%f@*-+% z&CAL6Vsa2dB9L$K8s7nDHivWaOCpv}I4<*G#u-h`v(ApYL)PW##TzIooMsm z=}v@~Js>UFT0F+{lzyS34iD&tBM(phgk7M=dG~15jM;oCrJnpA`$2Lg-oML!54GAX z-r40r4{+9!m8BqSF*f}~g;(8WsaX%uVXI&pjPzmpe7}$@jlXMb2C;E(hMJU9H{vtgZc0|sG zWHcx>`wJPBu~GPw62eaqs>cQ@6wuFOypE1x9rGl*6li+RjelY>yhc=D_<_;voIaXV zF;1f#UgJqg3e1Ixx`T#*j`_(XCGaj17ir=ZQ5Y?0%;FOtg2W&x5+*50ancg}g47T# zdb1=skrBKLaz@h`26DOw6|=EFE!gj+(}G%t$PTEMpam)peu5T<71My?Aep2?JNk=X z3L?-C!IDfP`U^@a%w=D{`9|uPuNixwhe9pJT@1`PdJHJWTZ-&cSQZLed-Pp)7!a9b z>6rzEq=}R}#~YnG0*NnyON6sSITP&G0>yj1h=S=PLs9`p6|^x*9rh>Fk^({~`7%x- zWl*pal{Ge0=zEA(=Ioqk)!0F_@}zDr!$PAc1EC6#U?P4o77!_J6s16jhHF`75YU$B zDq0fZMURL$`njFcA@aPKUv9NK#UX*&Iu;^uilWS7Sn|CPVaL6Q3_Fy(@bA+R9-IAm zHP9Iy<7Z4kSpY477@T4Y9vn6}vH>x_^KX#w?19h=h++RKE&v2EL4+!GQ~DPRZ!q+&HOJ9?V9Lp%`kPoLCajQ&d=FA^ zc*b5ib`mpKFm@8NHkv)n*hy!2q8`&Mh(ZimnXhtYi9-poV}a?X(&emgk=SejA$HZx z8MN6R8I^baiTIzn3XVx~} z3R$CH#By=c3rB+XK+HK`^Ex|??Ox|TFFVHPb?07Oxrqc-Fv`W_tb8bEfR|vL57xu7Q}70a`yI{;V9ej(Cb<449h?2w<;QAZtN}r=2zEs=kJUu3 z&+q`p8BJgKiXXRN4o>h*c+3j%PL>K^Un}0hRNw2g)|_Ue(8KOC+S>V?bck4QLtgb~o8T=p zu8Ix1QGNy)g;W&QmkrCq&Sv->gIf>RAHifP1_QWWO?n@f-p2-b_aW$U_@zgJ*--fQ z!MFAo*evl9D$5tP{vKP>0otNwwaN!7|7?w*|6Bk;&oEwaNbgx@9#r-Ju|#|5)gRAT zBCZjBcpG{hWZ32tV_KR+7*RNZ@(E1BlZ1~;2(3S1_gyB28&b_bO zux89eeYfjkI{vew#fkmD9dp`#HnFm6aQh`U0in~vWYY+aIgD>+bJ$^gRSrVxV#W%b z7@6JWG3MlOoZ>MS*)iROLBstv4n{o23Ro{(39*9-&_7~32iL2)0L*zwETh<%^`2~Y zG#e1&X9%Gi+1KKHrPtW1M>~c`JF>LrJ;r2e=*{YA+ zsyo}g#wQ+g6TUNhF77j4_L!r_3tn?eblWA-jv>y@teyNeQy;ktb2-jVZzMm@Xdk)N zI0R{H1g<@!n=u92sy%ORW~w+Q4+|y6N4n993}p2(^$|#LWRLI}B+|wN*j9aHtL|(; z!f2QKi!)3}J@bswj?}&Q9~o`Od1jRZqaEoxF(7Cg*`5{C%`x*~UmSxy+zUa@0bOQG zx`apMa~}{4=Xmn-D3q0anP>zsZo^|HXk$c;ebMdW+vQf-evP?ErCO4F)iN>^?0*|=` zRMjInCSxlyX*XU@mXGvB9|@|0rsk#qbQ2r0Ynf%nsAyjp#_7>RBfrRX$54;xKRwL! zwp6B@MW#K-av+FokaHMut(!4p|wfhOHx`m~y`Z zXR*K(%sw;VF?(ztPy<~zbKaiF?WD+rA`+=16V(iU$a)|AHZtlNdD4@0%xgX-au14I zMtjU3ARy9`E4znR^pW7@44@@Fhs8wbc+*)jm$QR6m92*~l|Lt&PoyDZr z$hMKVqZs@`g}+5N_hCvGO9vO4PnV#=97vihVr(P1nxlu!UhpfrcYBKJts&j|Dg z*jm3{ogtDjg|C4uDMQAzb?dPn%q4OM+7_3{3k2LOjNNiW8~7VCX^Y&O2)s|YH;X-3 zTw<@Idrj+1fYi*96zTovk1$oF=8rI#!_MbU z4ZU?Ujwwao!Fie~ZZ3yMrd@A}?Gd>b3$Fp7 zL?rejwcE4~?Rl)eV%W9~Q)%HTXK1fIIfWLUYHKf*eQK%fQ%hx^S}Ob0QrV}L%09JJ z_Nk?!PbGDV<@8(fyuuY9;(S?b7dBl%NzRXrLhp_jSb4KvMr}E4HOhtNWx;hU_|$=e zp6%8TAAovBN`$XiLbUKtcKzZ7H(?@7*V7CPBi1#fT%)LG7N3vD8246^gif*&x3%^<%ukSGO>%m z3hona8Q9PoncNq122Nn;P}-M9wq(&`-@~UOEcju2%*LP?J-WpjcY;3gQAcn&Y<$!~ zJY~5&7(nMGH-Rx*v&jN8*$m1#y!63DEY?q?wUUYr@fWa}rXCrr)BJ_|8NP~Jf?nx) zrw2`tUn9|(%wRc3G1yem-)4euIw*iffIWC+;Nh|YY)rr(+E-Z-UW?Wg&#PJ570hn; zI*^FT@H%>|v1d^&tc<%`u$?%7(X-4n#55v9R%dY-9^?RLmD&r7m3RL<(CGtozak>+ z>%qSJ*L-GNYmZ!_7&xR>XGeRLW#-1GRP6#7}RLOj=mvO_wQ;Y2Z%yf|KONxT|EUPmDe>uy|36vjS_A+5ss zXW@*kcAbrfZYsup7!%ACM21wt9nbw4iyg_$TtqPBLqr}sjC4H46(M}a8F_39@!$b2 zcX*5;i{RUT2UAfzcY=)M_sm*k$a1)1zd7<<}JuFqfqu>rAR>;cn2 zAcL*UiBq`K^78_Pon>0do&^D8#3=5j;ijTXD)cpPnvRu4eqqA)b{$u@!M7HTh-QWG zEi%geRBoPMvpb&`(pmd;w0k&aBCr$!tQd<4e9^O*c>x`6oF{sTKr;B6H{cKM;znX(eT_{7LWX3*@Q)t8Ec9juX2BErOb`pDDUMw#)2xA&4g@HMX>zCsY3W}f z(+3$S1k_0<8ACFCM8Ujv4?7P%(&=@0ROzt+Q;zL|+-J9z40AO6bI)&f~VRlX*MK8JV-d=+|IoFIKI zfW7iCuU>OetoACjBT;2l&;-EB;mzXI2Q8lj*%Z+-w5Ks$QCeV3Xv7VUjXG8Yjh*34 zcwr1O)utOYBfQ4syfR}LyG;!5=1t(tY`Oj?lA3#|eYZxg>tL(KMY)Br#*2={^$PqQ z?9__Oa5s!LsRM(6d?b!NN3ai&Ur!*f+jYI5g($i(0ytdQi?b}eEWOR>;0US6SGWy# zB$gT50)^YKwThqNC^sYVAx)!MD`^le<02$gfF;>%dr5J&t#)#}Nr$Pl!jz9g`V7~u zWF*|?qJj<aF5M-R1aUHhrbSe z(x%5t^Wsr|+{{^g^v$5GSdL*YEmRMogrGl`b*RHQp6;5QU5o159y4f&KT*>=c5wJR zb^0AM^lAEZ{SJKw+ZPnF3hhev9F)qFb6|5WlbaZ~$8N(lbIZ23LwRX|k=^m^^|fS4#6-yJ1qb$XGnVqfLqhjA5wJdc++7aUKD@C30Om zISd=dmRn%mmGtqtdbH5L;{_x9!aaUhr#1;wq?ou6H4T&D&4I#Zuj>qUYQehOgclOT zH`Nxho}$4#6v30KKU3{6;a~fnF&I>x1gZ||xhMHmjolOelE>)s824h;;sFcH71E9A zlPd$p$;|Lfs%Z4lgSWWO4>cP={J8G?c5VCAiw~B!htf?j}hlERQu2R$h ziFg5SaR|vBkhp+NOou~A>G$gQ=y&V0?!D*k^hq-J)H`M zQEbDwt{zF=d(T}M5#x3tV{$>tat)epfP1FUdcDSJtV9hei`ImcfGgJ$_Y!n_I*VGXnt#s-Luj{lH z`UxYgB15bY>`x|9OOTe`+I6P4J8s?X^r*LJdxi$-XRsC@wqP0i7 zg>8Cd5>C_)!WD$PGTY;7ubpU=PQVNpxW{+_7hXeZ6dR=@C_KPySo1;i0MaIugxDbP zWg4|XY7o5p2ZB*ci@>HrK)v8G?!u0<$xsW1@jId4*HI`@P(dgzo=_I4uweZgLf5Sq z!gzEbf2Qu*gGS=!WxD8$Sc!+Np&sNJ{XVvkBIuJ%yiu3=mMdHmOBQ zsPX|4ir+&*F~&e-gpyEn=_qR;-I5{+~O{j4d8Leqtj+7kN%9)gWVAn0NZoP z`BKu_B%Vs%xk&?yO*M~l3a|{DZ$N{zbg$})_~>6aDS8C zeu`6KY3!EXxJP4IiaRUTO{Jy`dX=LWcHf(vc1dr~_p=YiBunV51?~9*93i4$PPONN zac^2%2Kf}5R@%G6Z;fHZisW(=e>&P3vD0<%(_Q9GiYgCLU*r9K|!rXqBh z%vZ6V8653}0r1c-iRK3H)K}WNM)8d|j>4+1AWd#W?+Nwhi;x~LIfk1)zLsqKwO0xcs5i2I zPa!Bj+NAs#mQrsZK3Z&u;r3*}nql7Xjb)LQ*oSpvQha#B*HL_U4QIOexCWP62od=i z@?&sPeq2Bu=j#+i#uQNpQD1zpYf5%_iVzwv#5ylZ3Jn;bO~{nb6dyR>$6eGA?|F9! zA@TtgjdFCNO20D}=Y&47NNG}qj=7sU2M!TXnrKgDK%jgx!{eM4f}tNhQ#U?lOL~_r z33LDOGIO3*m_!lu}AWUFs`547BMBlTcTHMbDK`OCdubo18}}n~a^1c^(7rQ_z!Ls@fw3m=IM$ zObH=Hp+zZ$t&~zSLF1vEg47xyqdr1GReSjy@=543Qm=qAgW(eO8R+R-lOm+HLr$ z^X)06#;C~&owZ6CbObXo-G4-GaWsiCh}?s{K;;lZ5HWDMGrb14+vscFl46K^likqe zUNoT5^W9AO6Me%fH2l(Hyd`vbDn0DPDTFLg*q`2(6iB-!g)Kk(yaMT`(B3{pAdS92 z=|5jd>OWBPW+s&&_f!luDT%q*F4Z4BbjDIMH1+|s3OO)!V-$23C`#=IhnEc4bOOPO zsr?jzv862`V=SDr{gLH3NsG}J3O)#bcOu^mab)B^E_d>LwWJ=AM z9LfGNVhkXJ>;2#*xHb+Y%P1a1B?o_`}TwygrUco+(fvjq9v_~oid6))~lOPT; z(yrJLTA5?1(Ku|s9ok2Hm#_+KOa;A5!HYH7#!8!~@FWxl-E|^h%qs{)CT#F%6Hn-c zC-jKr5?qczs;2ZiRAeCX%?vc@tg^^zi-(Prss&D`h(`UUY}DAYs+&Prw9)51h8)CT zXP`CUYz~ZUDm6AXumzWfw+N_*x2&g9j`^L^@Pv)=c0D}fj1Ik^c7rdR6<%p`tyFUl z5i4daPiX5X?>IVfC)ap5PH~7yh*{}6!};5*745YIs>eUlUa{5YA8fB^1rvpmkZQ35 zl!WuNU8x#;F>UwDXuDb{jOH+){jTWOev_)fE|Fs$E8bEyxScIqHS6KN!Vk17IcxF{ zx9mfoyJd@3{j2`1dc)t@s@(%xH5eQ7YyWuD=4IKRvuVGI;p69Q+NwP-QHQE$B%AYT zq4S&2o~^PyuSM@2f1&$1)~0OFXwH}+vgx$IU+6j;Fjk^DU$;SC2@9>8CZZb^@ zkNCbGZ_&edoMB7;ZXi4iyA3cmc9&|)QD}XT*6Y5SYRgNwUMnqoh_-w$7GaJQ#0I}B z%y)d9#BpH5EUu(5#WLTaCl=3s2A5CsVqy4-S8l+i#a^MEZvIeP4cF;yfb?LP;+snxiIBGxF^~#S;$SGO z)HJ&>&s+QZbn&xe>#1e?ww|hb-PHQlg1px=HnhIE;N}aresp>dUv6=1J-hm1x&Uu? z2rqXMIh&rs$rlWV4%ajOKW*)MAe979AiJVkNwhGES?dx@;=P3Dt&$ENyGlB;aa~i= zVK+*0l8zi);FNUa86_i<4({-AF!dj#k`^71n`)rL(CwH9D#Y=w+h!ur*!mSCO|>KI zS73?#iqMLnsrFLCU+yzcar+82pKIF#m_vIZwg$^>g#Ts(2T!tLv&D2{irqD3R&|*% zV+p1_aFsiZ7-A7DV8)*M83o4P!h09Sw-;{5?8Fp1j2pqS%ML_hOd4TquU~x?VuP9x zv6upyrz!SdB6@rc^tefRuwXw4<4}<#yNY|eGob3xegiCuL&$;&VWJJ48_bU5E-M&a zf^wnz@K=DXpALN3reB2(oU;E{q@^+ zAI8#KazFGoV>fp5zL}8-^FI^O%GNEN@Yq@_{EGiRo*a>S3(T^{N)?L{MF<%4TS5Tj21* zraY#6E%uRp)Zdd{JYL*8~Ax{39os6Jd*2^R3UaH3)ocI)l-yI!aP zpo;YZ@IWlT!DDpxjO9uKJjc`1=zte4mgA>>S9?tkUE5<{0uGBkz{XTtZ|#BDHTEVNLabhr+K2cV7DcV_gCMK`$MajfW8IT)rHgb?;azaFYt}~ zBTd%Sd|lESYovcd`q)dg7#b<4F0g{?xN-uvCZRJwfhs5xl-2*f99Nz@#_SX>|s zPA+z%kx5-GDc_;ca!hru!iLRR7m&)~OuQW;AB1tqT*Ugee6`oFK!FT>R_$1<{9>ug zx*c1&&*1bM#jEshp071zS#y7iei*wo=0vHs~T z@hXsJft*ZRXcFnhy>56FNQ*$8l|KA(-Vuew`+Y}~-|WX`2Jwwl&&B+mO~^<&Sj4R_ zAQNpcL0Gx*hATM>NN3^01^@O*Zn}kxYT3cEii{5eqhkBxx)&oQam~|Aag&8Z7%&kc zfdyc9^sp^!_wK`@;bl}`g%8cO!|}A@V{!BYyVJn#CiE9s%`LkR_eV)zm2n0BuJn-` zTULi`*wr}G-)Cm)Vv9>Agyg}&CCyn;*^!{vACi5{ZQxm)(b5Yr zKLBe$*&Fjo65Y{Y40+tXu=y|G zBvX57P!JiN$Vxi~7;U8vQ_LY+YN<^ekuj!@z!5*Jw#_`b7qjtsXh6o~e7&#}OSz?; zFo>fUa-DX$RWFS83`MgVRCvnV-ZPk&acJIlS95rBj#k=?J=oz=Y$%jlIUK2l6h&Xr zQzC0RvWDIT0+MGFY!Zk(w&{+vU9SeIKoaPX6N&^ENfN*$N%yeVgjKjy9U@?YBz{Gb z?)Ubk0tJlQ+y-VejG+i=_hVFx>hKkI1~{C3vtAFqvD$BbF<|Z}Ge=>{1lEi*{KmHK zpCJm7h@so!x>#<=!0B$6o~<=><49Dp4n_H{7Mj2ap$S}K+*S^GV7?8yz)-_K(}%v} zCr#L9BR^64ubN+Sz^0$Ek5zh=n!h{Iyc!oYWWoe~Nek2fSYK(zf=q8l?K?P;8hZCi z7|!d$mWj1K*DLHbtgWN5`3h@vh{6)@#9qYhln(3S!4Y!(k+$%cTN2StJR{=5W+>K| z7=Ke$NR~$JPjRq3V8+^Sh$0oUkRSvMg4ly&1BN~uJfRyMk@7gs_PClLu#MeV?#In_ zvvK760$g-hYXQf>(jPajdI~#msSeH!_{>=_mFY8+O|ecK?85j8OzY$3G2{ka3`}H) zOM}pqV84xiDAm&$xHhrWUdRRdnGgIPtF>klQtT?*ds1JKJ4KV5J2)+Z+l*jO3+s|{ z^M(g|)^Pg)nku(yyoR0~-5e}DK43xje9YteKnwjAO^UN9YJHo>A7K{Ii27YGqkBNT zr1oX(Ozy}Sv7J*rHf*vn)fRB)MRV{H>U$~Qi-j-tW0=E(v4o8680ASMw_{u>wqx9d zK58YlV~Fh;*p2~X?H_WED9`Kqux1z)E^h6cElSjVFKR>@rIhO5?qo!swm$fwR+)i6KL%CBCXT919Am+2wcieZ9b>6VKSoz+Ncyez zV|*2Li}<@&Bl1Lk86T4AsL<~3wBhkS9z7k53DK>-K%wX>@I)n2I{#-y48VaNqa5&E zG7n*jB&=i8<@QWT_L8%L<~`@uPcG}nZe(DuJ?q9I!^yf)GAn0|*1#J>LN{Gn zz`B_&>ShFtX^M2zdgm8ZKaVHt=Z_g*bp7<)npWTF&wUy!WH&j`bD-z4C0%%hFiJV? zaOS0)c_0(a;DMhMts9VKG-Q(70y381m?!wVH;vX5BVxYb-Iv9Df!1z){>x?)X8<4o z3tD1_UV)j!=V8X}$I*B+Jmd)=1tLxU+%W@{QlYKJ$#Oht&b1OFo}C5k-Jw z;RNU;2_r;f=KZ+OQY=y4%?&RZTJ`V8lS3ZvN z@ev=NRCu-Ct$gg{<7GY$AbQIM^ z`bvB4bBMn9&@S1j^9@QX3@3%?U-V#!x_ZCv3_lNb80KGp?P~z951ZoV#TuoxGCNWo&n5#+5)g zLCn>Hn5z@ST%921YC%ljn&u}ElP?s@(n9x&!vIZLsI{I6pjM_N^;S~FGY+W~AH;Q| zpejG8~gYxSHiA_Oy4N@l8*%v33vsZugirDUc` zFcUbm4rvR9tOP^edrwPR`}zX;6&5=$(bi7HX@adQB;&Tu{mz}E$MQ+WHO`t(@OHK) z@UM@yxZdi5h`Q4x z7;AmA?BKE1H@ye@TIb$$u&H(K^$0h248qMFb&$;JsQq(mbM2vBt<6ghz0%se;LyR= z=DQEQ)7m`!(0i@TWrt3*HhT}9Z7sR!P+M!s^$1ro2H{FZA>6zxim%RSjn?+=*m|`0 zcKg=9_TE{v^+503@~toTeyeKh&ffnbBW~T=`y9Dv>rK6DI|;AvefSvRnBE^!v)?+Z z_vb}~D=L3oPIz|pg`C@CKMT^FT@BKlT`fs-cC{qU+0~LX53ZJ^d2qEP&4a5YX&ziH zNCUo|jTlLPEhO#fHC?j%J?zG6g7;mRGwkLuzm{|OMae33qe4_^l|#X7-vn=0⩔R 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 b<*!aNZvGNB#j#u6N)MV?jO+FO2j~6|U}H>k literal 0 HcmV?d00001 diff --git a/installer/windows/installer/CMakeLists.txt b/installer/windows/installer/CMakeLists.txt index 9cffbfa12..1db373d01 100644 --- a/installer/windows/installer/CMakeLists.txt +++ b/installer/windows/installer/CMakeLists.txt @@ -5,7 +5,6 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>DLL") project(installer) -find_package(7zip CONFIG REQUIRED) find_package(Qt6 REQUIRED COMPONENTS Widgets Gui @@ -24,7 +23,7 @@ set(SOURCES ../utils/path.cpp ../utils/utils.cpp ../utils/windscribepathcheck.cpp - ../../../client/common/archive/archive.cpp + ../utils/archive.cpp installer/installer.cpp installer/installer_shim.cpp installer/installer_utils.cpp @@ -68,7 +67,6 @@ target_link_libraries(installer delayimp Crypt32.lib Qt6::Core Qt6::Gui Qt6::Widgets Qt6::Svg - 7zip::7zip ) target_include_directories(installer diff --git a/installer/windows/installer/installer.rc b/installer/windows/installer/installer.rc index 62362151b..530c7beeb 100644 --- a/installer/windows/installer/installer.rc +++ b/installer/windows/installer/installer.rc @@ -1,5 +1,5 @@ #include "resource.h" -Windscribe RCDATA "resources\windscribe.7z" +Windscribe BINARY "resources\\windscribe.7z" IDI_ICON1 ICON DISCARDABLE "resources\windscribe.ico" diff --git a/installer/windows/installer/installer/blocks/files.cpp b/installer/windows/installer/installer/blocks/files.cpp index 92ef5adb7..dfde0780e 100644 --- a/installer/windows/installer/installer/blocks/files.cpp +++ b/installer/windows/installer/installer/blocks/files.cpp @@ -6,8 +6,10 @@ #include "../installer_base.h" #include "../settings.h" #include "../../../utils/applicationinfo.h" +#include "../../../utils/archive.h" #include "../../../utils/logger.h" #include "../../../utils/path.h" +#include "../../../utils/utils.h" using namespace std; @@ -21,78 +23,46 @@ Files::~Files() int Files::executeStep() { - if (state_ == 0) - { - // Since we're running as root, we need to ensure no malicious hackery can be done with symbolic links. - // We'll install to the app's default, OS protected, 64-bit Program Files folder. If the user specified - // a custom install folder, we'll attempt to rename the install folder to the custom folder after the - // file extraction is complete. - installPath_ = ApplicationInfo::defaultInstallPath(); - - if (filesystem::exists(installPath_)) { - if (!filesystem::is_empty(installPath_)) { - Log::instance().out(L"Warning: the default install directory exists and is not empty."); - } - } - else { - auto result = ::SHCreateDirectoryEx(NULL, installPath_.c_str(), NULL); - if (result != ERROR_SUCCESS) { - Log::instance().out(L"Failed to create default install directory (%d)", result); - return -ERROR_OTHER; - } + // Since we're running as root, we need to ensure no malicious hackery can be done with symbolic links. + // We'll install to the app's default, OS protected, 64-bit Program Files folder. If the user specified + // a custom install folder, we'll attempt to rename the install folder to the custom folder after the + // file extraction is complete. + installPath_ = ApplicationInfo::defaultInstallPath(); + + if (filesystem::exists(installPath_)) { + if (!filesystem::is_empty(installPath_)) { + Log::instance().out(L"Warning: the default install directory exists and is not empty."); } - - archive_.reset(new Archive(L"Windscribe")); - archive_->setLogFunction([](const char* str) { - Log::instance().out((std::string("(archive) ") + str).c_str()); - }); - - SRes res = archive_->fileList(fileList_); - - if (res != SZ_OK) { - Log::instance().out(L"Failed to extract file list from archive."); + } + else { + auto result = ::SHCreateDirectoryEx(NULL, installPath_.c_str(), NULL); + if (result != ERROR_SUCCESS) { + Log::instance().out(L"Failed to create default install directory (%d)", result); return -ERROR_OTHER; } - - fillPathList(); - - archive_->calcTotal(fileList_, pathList_); - curFileInd_ = 0; - state_++; - return 0; } - SRes res = archive_->extractionFile(curFileInd_); - if (res != SZ_OK) - { - archive_->finish(); - Log::instance().out(L"Failed to extract file at index %u.", curFileInd_); + const wstring exePath = Utils::getExePath(); + if (exePath.empty()) { + Log::instance().out(L"Could not get exe path"); return -ERROR_OTHER; } - if (curFileInd_ >= (archive_->getNumFiles() - 1)) - { - archive_->finish(); - if (!copyLibs()) { - Log::instance().out(L"Failed to copy libs"); - return -ERROR_OTHER; - } - return moveFiles(); - } - - int progress = ((double)curFileInd_ / (double)archive_->getNumFiles()) * 100.0; - curFileInd_++; + wsl::Archive archive; + archive.setLogFunction([](const wstring &str) { + Log::instance().out(str); + }); - return progress; -} + if (!archive.extract(L"Windscribe", L"windscribe.7z", exePath, installPath_)) { + return -ERROR_OTHER; + } -void Files::fillPathList() -{ - pathList_.clear(); - for (auto it = fileList_.cbegin(); it != fileList_.cend(); it++) { - wstring file = Path::append(installPath_, *it); - pathList_.push_back(Path::extractDir(file)); + if (!copyLibs()) { + Log::instance().out(L"Failed to copy libs"); + return -ERROR_OTHER; } + + return moveFiles(); } int Files::moveFiles() @@ -137,7 +107,7 @@ int Files::moveFiles() // Delete "C:\Program Files\Windscribe" since we don't want to leave files behind. // SHFileOperation requires the path to be double-null terminated. - std::wstring installPathDoubleNull = installPath_ + L"\0"s; + wstring installPathDoubleNull = installPath_ + L"\0"s; SHFILEOPSTRUCT fileOp = { NULL, FO_DELETE, @@ -160,11 +130,11 @@ int Files::moveFiles() bool Files::copyLibs() { - std::error_code ec; - std::filesystem::copy_options opts = std::filesystem::copy_options::overwrite_existing; + error_code ec; + filesystem::copy_options opts = filesystem::copy_options::overwrite_existing; const filesystem::path installPath = installPath_; - const wstring exeStr = getExePath(); + const wstring exeStr = Utils::getExePath(); if (exeStr.empty()) { Log::instance().out(L"Could not get exe path"); return false; @@ -176,30 +146,20 @@ bool Files::copyLibs() if (entry.is_regular_file() && entry.path().extension() == ".dll") { filesystem::copy_file(entry.path(), installPath / entry.path().filename(), opts, ec); if (ec) { - Log::instance().out(L"Could not copy DLL %ls: %hs", entry.path().wstring().c_str(), ec.message().c_str()); + Log::instance().out(L"Could not copy DLL %s: %hs", entry.path().c_str(), ec.message().c_str()); return false; } } } // Copy Qt plugins - std::wstring paths[3] = { L"imageformats", L"platforms", L"styles" }; + wstring paths[3] = { L"imageformats", L"platforms", L"styles" }; for (auto p : paths) { filesystem::copy(exePath / p, installPath / p, opts, ec); if (ec) { - Log::instance().out(L"Could not copy %ls: %hs", p.c_str(), ec.message().c_str()); + Log::instance().out(L"Could not copy %s: %hs", p.c_str(), ec.message().c_str()); return false; } } return true; } - -wstring Files::getExePath() -{ - wchar_t path[MAX_PATH]; - int ret = GetModuleFileName(NULL, path, MAX_PATH); - if (ret == 0) { - return wstring(); - } - return filesystem::path(path).parent_path().wstring(); -} diff --git a/installer/windows/installer/installer/blocks/files.h b/installer/windows/installer/installer/blocks/files.h index 1b43ca730..594a60f22 100644 --- a/installer/windows/installer/installer/blocks/files.h +++ b/installer/windows/installer/installer/blocks/files.h @@ -1,10 +1,8 @@ #pragma once -#include #include #include "../iinstall_block.h" -#include "../../../../../client/common/archive/archive.h" class Files : public IInstallBlock { @@ -15,15 +13,8 @@ class Files : public IInstallBlock virtual int executeStep(); private: - std::unique_ptr archive_; - int state_ = 0; - unsigned int curFileInd_ = 0; - std::list fileList_; - std::list pathList_; std::wstring installPath_; - void fillPathList(); int moveFiles(); bool copyLibs(); - std::wstring getExePath(); }; diff --git a/installer/windows/installer/installer/blocks/icons.cpp b/installer/windows/installer/installer/blocks/icons.cpp index 844bd2c14..78737d240 100644 --- a/installer/windows/installer/installer/blocks/icons.cpp +++ b/installer/windows/installer/installer/blocks/icons.cpp @@ -30,11 +30,11 @@ int Icons::executeStep() if (isCreateShortcut_) { //C:\\Users\\Public\\Desktop\\Windscribe.lnk - wstring common_desktop = Utils::DesktopFolder(); + wstring common_desktop = Utils::desktopFolder(); createShortcut(common_desktop + L"\\" + ApplicationInfo::name() + L".lnk", appExe, L"", installPath, L"", 0); } - const wstring group = Utils::StartMenuProgramsFolder(); + const wstring group = Utils::startMenuProgramsFolder(); //C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\Windscribe\\Uninstall Windscribe.lnk createShortcut(group + L"\\"+ ApplicationInfo::name() + L"\\Uninstall Windscribe.lnk", @@ -70,35 +70,35 @@ void Icons::createShortcut(const wstring link, const wstring target, const wstri HRESULT result = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER,IID_IShellLink, reinterpret_cast(&psl)); if (result != S_OK) { - Log::instance().out("Could not create shell link"); + Log::instance().out(L"Could not create shell link"); return; } result = psl->SetPath(target.c_str()); if (result != S_OK) { - Log::instance().out("Could not set path"); + Log::instance().out(L"Could not set path"); return; } result = psl->SetArguments(params.c_str()); if (result != S_OK) { - Log::instance().out("Could not set arguments"); + Log::instance().out(L"Could not set arguments"); return; } result = psl->SetWorkingDirectory(workingDir.c_str()); if (result != S_OK) { - Log::instance().out("Could not set working dir"); + Log::instance().out(L"Could not set working dir"); return; } if (!icon.empty()) { result = psl->SetIconLocation(icon.c_str(), idx); if (result != S_OK) { - Log::instance().out("Could not set icon"); + Log::instance().out(L"Could not set icon"); return; } } result = psl->QueryInterface(IID_IPersistFile, reinterpret_cast(&ppf)); if (result != S_OK) { - Log::instance().out("Could not get ppf"); + Log::instance().out(L"Could not get ppf"); psl->Release(); return; } @@ -115,4 +115,4 @@ void Icons::createShortcut(const wstring link, const wstring target, const wstri SHChangeNotify(SHCNE_CREATE, SHCNF_PATH, link.c_str(), nullptr); SHChangeNotify(SHCNE_UPDATEDIR, SHCNF_PATH | SHCNF_FLUSH, dir.c_str(), nullptr); -} \ No newline at end of file +} diff --git a/installer/windows/installer/installer/blocks/install_authhelper.cpp b/installer/windows/installer/installer/blocks/install_authhelper.cpp index fa59ec92e..57cc04716 100644 --- a/installer/windows/installer/installer/blocks/install_authhelper.cpp +++ b/installer/windows/installer/installer/blocks/install_authhelper.cpp @@ -4,7 +4,6 @@ #include "../settings.h" #include "../../../Utils/logger.h" #include "../../../Utils/path.h" -#include "../../../utils/utils.h" InstallAuthHelper::InstallAuthHelper(double weight) : IInstallBlock(weight, L"AuthHelper") { @@ -28,7 +27,7 @@ int InstallAuthHelper::executeStep() // We still require run-time dynamic linking with DLL at executable location HINSTANCE hProxyStubLib = LoadLibrary(authProxyStubLib.c_str()); if (hProxyStubLib == NULL) { - Log::instance().out("Failed to load Auth Helper Proxy Stub Library"); + Log::instance().out(L"Failed to load Auth Helper Proxy Stub Library"); lastError_ = L"An error occurred when loading the Auth Helper Proxy Stub library"; return -ERROR_OTHER; } @@ -39,13 +38,13 @@ int InstallAuthHelper::executeStep() if (DllRegisterServer != NULL) { HRESULT result = DllRegisterServer(); if (FAILED(result)) { - Log::instance().out("Call to Proxy Stub DllRegisterServer failed"); + Log::instance().out(L"Call to Proxy Stub DllRegisterServer failed"); lastError_ = L"An error occurred when calling Proxy Stub DllRegisterServer"; FreeLibrary(hProxyStubLib); return -ERROR_OTHER; } } else { - Log::instance().out("Failed to get proxy stub DllRegisterServer"); + Log::instance().out(L"Failed to get proxy stub DllRegisterServer"); lastError_ = L"An error occurred when getting proxy stub DllRegisterServer"; FreeLibrary(hProxyStubLib); return -ERROR_OTHER; @@ -58,7 +57,7 @@ int InstallAuthHelper::executeStep() HINSTANCE hLib = LoadLibrary(authLib.c_str()); if (hLib == NULL) { - Log::instance().out("Failed to load Auth Helper Library"); + Log::instance().out(L"Failed to load Auth Helper Library"); lastError_ = L"An error occurred when loading the Auth Helper library: "; return -ERROR_OTHER; } @@ -71,18 +70,18 @@ int InstallAuthHelper::executeStep() HRESULT result = RegisterServerWithTargetPaths(installPath, installPath, installPath); if (FAILED(result)) { - Log::instance().out("Call to RegisterServerWithTargetPaths failed"); + Log::instance().out(L"Call to RegisterServerWithTargetPaths failed"); lastError_ = L"An error occurred when calling RegisterServerWithTargetPaths: "; FreeLibrary(hLib); return -ERROR_OTHER; } } else { - Log::instance().out("Failed to get reg server function"); + Log::instance().out(L"Failed to get reg server function"); lastError_ = L"An error occurred when getting RegisterServerWithTargetPaths: "; FreeLibrary(hLib); return -ERROR_OTHER; } FreeLibrary(hLib); - Log::instance().out("Auth helper installed successfully"); + Log::instance().out(L"Auth helper installed successfully"); return 100; } diff --git a/installer/windows/installer/installer/blocks/install_openvpn_dco.cpp b/installer/windows/installer/installer/blocks/install_openvpn_dco.cpp index fd4e6602f..72478be26 100644 --- a/installer/windows/installer/installer/blocks/install_openvpn_dco.cpp +++ b/installer/windows/installer/installer/blocks/install_openvpn_dco.cpp @@ -24,8 +24,8 @@ int InstallOpenVPNDCO::executeStep() DWORD buildNum = InstallerUtils::getOSBuildNumber(); if (buildNum < kMinWindowsBuildNumberForOpenVPNDCO) { Log::instance().out( - "WARNING: OS version is not compatible with the OpenVPN DCO driver. Windows 10 build %lu or newer is required" - " to use this driver.", kMinWindowsBuildNumberForOpenVPNDCO); + L"WARNING: OS version is not compatible with the OpenVPN DCO driver. Windows 10 build %lu or newer is required" + L" to use this driver.", kMinWindowsBuildNumberForOpenVPNDCO); return -ERROR_OTHER; } @@ -42,7 +42,7 @@ int InstallOpenVPNDCO::executeStep() if (process.exitCode() != 0) { Log::instance().out(L"InstallOpenVPNDCO: devcon.exe returned exit code %d", process.exitCode()); - Log::instance().out(L"InstallOpenVPNDCO: devcon.exe output (%S)", appOutput.constData()); + Log::instance().out(L"InstallOpenVPNDCO: devcon.exe output (%hs)", appOutput.constData()); return -ERROR_OTHER; } @@ -50,7 +50,7 @@ int InstallOpenVPNDCO::executeStep() QRegularExpression re("oem\\d+.inf"); const QRegularExpressionMatch match = re.match(appOutput); if (!match.hasMatch()) { - Log::instance().out(L"InstallOpenVPNDCO: failed to find OEM indentifier in devcon.exe output (%S)", appOutput.constData()); + Log::instance().out(L"InstallOpenVPNDCO: failed to find OEM indentifier in devcon.exe output (%hs)", appOutput.constData()); return -ERROR_OTHER; } diff --git a/installer/windows/installer/installer/blocks/install_splittunnel.cpp b/installer/windows/installer/installer/blocks/install_splittunnel.cpp index da902510e..23f5c0caa 100644 --- a/installer/windows/installer/installer/blocks/install_splittunnel.cpp +++ b/installer/windows/installer/installer/blocks/install_splittunnel.cpp @@ -24,16 +24,16 @@ int InstallSplitTunnel::executeStep() try { error_code ec; wstring sourceFile = Path::append(Settings::instance().getPath(), L"splittunnel\\windscribesplittunnel.sys"); - wstring targetFile = Path::append(Utils::GetSystemDir(), L"drivers\\windscribesplittunnel.sys"); + wstring targetFile = Path::append(Utils::getSystemDir(), L"drivers\\windscribesplittunnel.sys"); filesystem::copy(sourceFile, targetFile, filesystem::copy_options::overwrite_existing, ec); if (ec) { - throw system_error(ec, "InstallSplitTunnel failed to copy driver to system drivers folder"); + throw system_error(ec, "failed to copy driver to system drivers folder"); } hSCM = ::OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE); if (hSCM == NULL) { - throw system_error(::GetLastError(), system_category(), "InstallSplitTunnel OpenSCManager"); + throw system_error(::GetLastError(), system_category(), "OpenSCManager"); } // NOTE: the path we register for the service must use the 'SystemRoot' directive. Using C:/Windows @@ -44,11 +44,11 @@ int InstallSplitTunnel::executeStep() SERVICE_ERROR_NORMAL, L"\\SystemRoot\\system32\\DRIVERS\\WindscribeSplitTunnel.sys", L"NDIS", NULL, L"TCPIP", NULL, NULL); if (hService == NULL) { - throw system_error(::GetLastError(), system_category(), "InstallSplitTunnel CreateService"); + throw system_error(::GetLastError(), system_category(), "CreateService"); } } catch (system_error& ex) { - Log::instance().out(ex.what()); + Log::instance().out(L"InstallSplitTunnel %hs", ex.what()); result = -ERROR_OTHER; } diff --git a/installer/windows/installer/installer/blocks/service.cpp b/installer/windows/installer/installer/blocks/service.cpp index a8b2be061..1165782bf 100644 --- a/installer/windows/installer/installer/blocks/service.cpp +++ b/installer/windows/installer/installer/blocks/service.cpp @@ -41,6 +41,6 @@ void Service::installWindscribeService() scm.startService(); } catch (std::system_error& ex) { - Log::instance().out("installWindscribeService - %s", ex.what()); + Log::instance().out(L"installWindscribeService - %hs", ex.what()); } } diff --git a/installer/windows/installer/installer/blocks/uninstallprev.cpp b/installer/windows/installer/installer/blocks/uninstallprev.cpp index 8ed979319..39b9fee0f 100644 --- a/installer/windows/installer/installer/blocks/uninstallprev.cpp +++ b/installer/windows/installer/installer/blocks/uninstallprev.cpp @@ -3,12 +3,12 @@ #include #include -#include #include #include #include "../installer_base.h" #include "../../../utils/applicationinfo.h" +#include "../../../utils/archive.h" #include "../../../utils/logger.h" #include "../../../utils/path.h" #include "../../../utils/utils.h" @@ -53,15 +53,15 @@ int UninstallPrev::executeStep() else { Log::instance().out(L"Timeout exceeded when trying to close Windscribe. Killing the process."); - const wstring appName = Path::append(Utils::GetSystemDir(), L"taskkill.exe"); + const wstring appName = Path::append(Utils::getSystemDir(), L"taskkill.exe"); const wstring commandLine = L"/f /im " + ApplicationInfo::appExeName(); - const auto result = Utils::InstExec(appName, commandLine, INFINITE, SW_HIDE); + const auto result = Utils::instExec(appName, commandLine, INFINITE, SW_HIDE); if (!result.has_value()) { - Log::instance().out("WARNING: an error was encountered attempting to start taskkill.exe."); + Log::instance().out(L"WARNING: an error was encountered attempting to start taskkill.exe."); return -ERROR_OTHER; } else if (result.value() != NO_ERROR && result.value() != ERROR_WAIT_NO_CHILDREN) { - Log::instance().out("WARNING: unable to kill Windscribe (%lu).", result.value()); + Log::instance().out(L"WARNING: unable to kill Windscribe (%lu).", result.value()); return -ERROR_OTHER; } else { @@ -89,7 +89,7 @@ int UninstallPrev::executeStep() DWORD lastError = 0; if (!uninstallOldVersion(uninstallString, lastError)) { - Log::instance().out("UninstallPrev::executeStep: uninstallOldVersion failed: %lu", lastError); + Log::instance().out(L"UninstallPrev::executeStep: uninstallOldVersion failed: %lu", lastError); if (lastError != 2) { // Any error other than "Not found" return -ERROR_OTHER; } @@ -100,10 +100,10 @@ int UninstallPrev::executeStep() } if (!extractUninstaller()) { - Log::instance().out("UninstallPrev::executeStep: could not extract uninstaller."); + Log::instance().out(L"UninstallPrev::executeStep: could not extract uninstaller."); return -ERROR_OTHER; } - Log::instance().out("UninstallPrev::executeStep: successfully extracted uninstaller, trying again."); + Log::instance().out(L"UninstallPrev::executeStep: successfully extracted uninstaller, trying again."); return 65; } } @@ -145,7 +145,7 @@ bool UninstallPrev::uninstallOldVersion(const wstring &uninstallString, DWORD &l const wstring sUnInstallString = removeQuotes(uninstallString); DWORD error = 0; - const auto res = Utils::InstExec(sUnInstallString, L"/VERYSILENT", INFINITE, SW_HIDE, L"", &error); + const auto res = Utils::instExec(sUnInstallString, L"/VERYSILENT", INFINITE, SW_HIDE, L"", &error); if (!res.has_value() || res.value() == MAXDWORD) { lastError = error; return false; @@ -219,7 +219,7 @@ void UninstallPrev::stopService() scm.stopService(ApplicationInfo::serviceName().c_str()); } catch (system_error& ex) { - Log::instance().out("UninstallPrev::stopService %s (%lu)", ex.what(), ex.code().value()); + Log::instance().out(L"UninstallPrev::stopService %hs (%lu)", ex.what(), ex.code().value()); } } @@ -227,46 +227,22 @@ bool UninstallPrev::extractUninstaller() { Log::instance().out(L"Extracting uninstaller from the archive"); - archive_.reset(new Archive(L"Windscribe")); + wsl::Archive archive; + archive.setLogFunction([](const wstring &str) { + Log::instance().out(str); + }); - std::list fileList; - SRes res = archive_->fileList(fileList); - if (res != SZ_OK) { - Log::instance().out(L"Failed to extract file list from archive."); + const wstring exePath = Utils::getExePath(); + if (exePath.empty()) { + Log::instance().out(L"Could not get exe path"); return false; } - int index = 0; - auto it = fileList.begin(); - while(index < fileList.size()) { - // Note the trailing 's' on the std::wstring literal, this is necessary since the file string has an embedded null byte. - if (*it == std::wstring(L"uninstall.exe\0"s)) { - break; - } - index++; - it = std::next(it); - } - if (index >= fileList.size()) { - Log::instance().out(L"Failed to find uninstall.exe in the archive."); - return false; - } - - std::list pathList; - std::wstring extractionPath = Path::extractDir(removeQuotes(getUninstallString())); - for (auto it = fileList.cbegin(); it != fileList.cend(); it++) { - // We're only extracting uninstall.exe, other paths are irrelevant - pathList.push_back(extractionPath); - } - - archive_->calcTotal(fileList, pathList); + const wstring targetFolder = Path::extractDir(removeQuotes(getUninstallString())); - res = archive_->extractionFile(index); - if (res != SZ_OK) { - archive_->finish(); - Log::instance().out(L"Failed to extract from archive."); + if (!archive.extract(L"Windscribe", L"windscribe.7z", exePath, L"uninstall.exe", targetFolder)) { return false; } - archive_->finish(); return true; } diff --git a/installer/windows/installer/installer/blocks/uninstallprev.h b/installer/windows/installer/installer/blocks/uninstallprev.h index 8783f0562..39af4906e 100644 --- a/installer/windows/installer/installer/blocks/uninstallprev.h +++ b/installer/windows/installer/installer/blocks/uninstallprev.h @@ -1,9 +1,7 @@ #pragma once -#include #include -#include "archive/archive.h" #include "../iinstall_block.h" class UninstallPrev : public IInstallBlock @@ -15,7 +13,6 @@ class UninstallPrev : public IInstallBlock private: int state_; bool isFactoryReset_; - std::unique_ptr archive_; std::wstring getUninstallString(); bool uninstallOldVersion(const std::wstring &uninstallString, DWORD &lastError); diff --git a/installer/windows/installer/installer/installer_base.h b/installer/windows/installer/installer/installer_base.h index 2fec3efed..4690306e7 100644 --- a/installer/windows/installer/installer/installer_base.h +++ b/installer/windows/installer/installer/installer_base.h @@ -10,6 +10,7 @@ enum INSTALLER_CURRENT_STATE { STATE_FINISHED, STATE_ERROR, STATE_LAUNCHED, + STATE_EXTRACTED, }; enum INSTALLER_ERROR { ERROR_OTHER = 1, ERROR_PERMISSION, ERROR_KILL, ERROR_CONNECT_HELPER, ERROR_DELETE, ERROR_UNINSTALL, ERROR_MOVE_CUSTOM_DIR }; diff --git a/installer/windows/uninstaller/copy_and_run.cpp b/installer/windows/uninstaller/copy_and_run.cpp index 579c3f3d5..358e4bc93 100644 --- a/installer/windows/uninstaller/copy_and_run.cpp +++ b/installer/windows/uninstaller/copy_and_run.cpp @@ -49,7 +49,7 @@ int CopyAndRun::runFirstPhase(const std::wstring& uninstExeFile, const char *lps wchar_t tempPath[MAX_PATH]; DWORD result = GetTempPath(MAX_PATH, tempPath); if (result == 0) { - Log::instance().out("Couldn't locate Windows temporary directory (%lu)", GetLastError()); + Log::instance().out(L"Couldn't locate Windows temporary directory (%lu)", GetLastError()); return MAXDWORD; } @@ -59,14 +59,14 @@ int CopyAndRun::runFirstPhase(const std::wstring& uninstExeFile, const char *lps // they are running. std::wstring tempFile; if (!generateNonRandomUniqueFilename(tempPath, tempFile)) { - Log::instance().out("CopyAndRun::runFirstPhase generateNonRandomUniqueFilename failed"); + Log::instance().out(L"CopyAndRun::runFirstPhase generateNonRandomUniqueFilename failed"); return MAXDWORD; } // We should have generated a unique filename that does not exist in the target folder, // thus CopyFile should fail if the file already exists. if (!CopyFile(uninstExeFile.c_str(), tempFile.c_str(), TRUE)) { - Log::instance().out("CopyAndRun::runFirstPhase failed to copy [%s] to [%s] (%lu)", uninstExeFile.c_str(), tempFile.c_str(), ::GetLastError()); + Log::instance().out(L"CopyAndRun::runFirstPhase failed to copy [%s] to [%s] (%lu)", uninstExeFile.c_str(), tempFile.c_str(), ::GetLastError()); return MAXDWORD; } @@ -93,7 +93,7 @@ int CopyAndRun::runFirstPhase(const std::wstring& uninstExeFile, const char *lps result = ::CreateProcess(nullptr, const_cast(cmdLine.c_str()), nullptr, nullptr, false, 0, nullptr, tempPath, &startupInfo, &processInfo); if (result == FALSE) { - Log::instance().out("CopyAndRun::exec - CreateProcess(%s) failed (%lu)", cmdLine.c_str(), ::GetLastError()); + Log::instance().out(L"CopyAndRun::exec - CreateProcess(%s) failed (%lu)", cmdLine.c_str(), ::GetLastError()); return MAXDWORD; } diff --git a/installer/windows/uninstaller/uninstall.cpp b/installer/windows/uninstaller/uninstall.cpp index dc26ecad2..b74c2cd67 100644 --- a/installer/windows/uninstaller/uninstall.cpp +++ b/installer/windows/uninstaller/uninstall.cpp @@ -110,8 +110,8 @@ bool Uninstaller::PerformUninstall(const P_DeleteUninstallDataFilesProc DeleteUn Log::instance().out(L"Deleting shortcuts."); - wstring common_desktop = Utils::DesktopFolder(); - wstring group = Utils::StartMenuProgramsFolder(); + wstring common_desktop = Utils::desktopFolder(); + wstring group = Utils::startMenuProgramsFolder(); deleteFile(common_desktop + L"\\" + ApplicationInfo::name() + L".lnk"); RemoveDir::DelTree(group + L"\\" + ApplicationInfo::name(), true, true, true, false); @@ -151,11 +151,11 @@ void Uninstaller::RunSecondPhase() if (!isSilent_) { Log::instance().out(L"turn off firewall: %s", path_for_installation.c_str()); - Utils::InstExec(Path::append(path_for_installation, L"WindscribeService.exe"), L"-firewall_off", INFINITE, SW_HIDE); + Utils::instExec(Path::append(path_for_installation, L"WindscribeService.exe"), L"-firewall_off", INFINITE, SW_HIDE); } Log::instance().out(L"kill openvpn process"); - Utils::InstExec(Path::append(Utils::GetSystemDir(), L"taskkill.exe"), L"/f /im openvpn.exe", INFINITE, SW_HIDE); + Utils::instExec(Path::append(Utils::getSystemDir(), L"taskkill.exe"), L"/f /im openvpn.exe", INFINITE, SW_HIDE); Log::instance().out(L"uninstall split tunnel driver"); UninstallSplitTunnelDriver(); @@ -190,6 +190,13 @@ void Uninstaller::RunSecondPhase() Log::instance().out(L"Removing directory %s", path_for_installation.c_str()); ::RemoveDirectory(path_for_installation.c_str()); + // remove any leftover files in Program Files, since we write some configs/logs here regardless of installation path + wstring wsPath = Path::append(Utils::programFilesFolder(), L"Windscribe"); + if (!Path::equivalent(wsPath, path_for_installation)) { + Log::instance().out(L"Removing directory in Program Files"); + RemoveDir::DelTree(wsPath.c_str(), true, true, true, false); + } + if (!isSilent_) { messageBox(IDS_UNINSTALL_SUCCESS, MB_ICONINFORMATION | MB_OK | MB_SETFOREGROUND); } @@ -228,7 +235,7 @@ void Uninstaller::UninstallSplitTunnelDriver() throw system_error(ec); } - wstring targetFile = Path::append(Utils::GetSystemDir(), L"drivers\\windscribesplittunnel.sys"); + wstring targetFile = Path::append(Utils::getSystemDir(), L"drivers\\windscribesplittunnel.sys"); if (filesystem::exists(targetFile)) { filesystem::remove(targetFile, ec); if (ec) { @@ -237,7 +244,7 @@ void Uninstaller::UninstallSplitTunnelDriver() } } catch (system_error& ex) { - Log::instance().out("WARNING: failed to uninstall the split tunnel driver - %s", ex.what()); + Log::instance().out(L"WARNING: failed to uninstall the split tunnel driver - %hs", ex.what()); } } @@ -253,7 +260,7 @@ void Uninstaller::UninstallHelper() } } catch (system_error& ex) { - Log::instance().out("WARNING: failed to delete the Windscribe service - %s", ex.what()); + Log::instance().out(L"WARNING: failed to delete the Windscribe service - %hs", ex.what()); } } @@ -270,13 +277,13 @@ int Uninstaller::messageBox(const UINT resourceID, const UINT type, HWND ownerWi HMODULE hModule = ::GetModuleHandle(NULL); int result = ::LoadString(hModule, IDS_MSGBOX_TITLE, title, bufSize); if (result == 0) { - Log::instance().out("WARNING: loading of the message box title string resource failed (%lu).", ::GetLastError()); + Log::instance().out(L"WARNING: loading of the message box title string resource failed (%lu).", ::GetLastError()); wcscpy_s(title, bufSize, L"Uninstall Windscribe"); } result = ::LoadString(hModule, resourceID, message, bufSize); if (result == 0) { - Log::instance().out("WARNING: loading of string resource %d failed (%lu).", resourceID, ::GetLastError()); + Log::instance().out(L"WARNING: loading of string resource %d failed (%lu).", resourceID, ::GetLastError()); wcscpy_s(message, bufSize, L"Windscribe uninstall encountered a technical failure."); } @@ -291,23 +298,23 @@ void Uninstaller::UninstallOpenVPNDCODriver(const wstring& installationPath) const wstring keyName = ApplicationInfo::installerRegistryKey(false); Registry::RegQueryStringValue(HKEY_CURRENT_USER, keyName.c_str(), L"ovpnDCODriverOEMIdentifier", adapterOEMIdentifier); if (adapterOEMIdentifier.empty()) { - Log::instance().out("WARNING: failed to find 'ovpnDCODriverOEMIdentifier' entry in the installer registry key."); + Log::instance().out(L"WARNING: failed to find 'ovpnDCODriverOEMIdentifier' entry in the installer registry key."); return; } const wstring appName = Path::append(installationPath, L"devcon.exe"); const wstring commandLine = wstring(L"dp_delete ") + adapterOEMIdentifier; - auto result = Utils::InstExec(appName, commandLine, 30 * 1000, SW_HIDE); + auto result = Utils::instExec(appName, commandLine, 30 * 1000, SW_HIDE); if (!result.has_value()) { - Log::instance().out("WARNING: The OpenVPN DCO driver uninstall failed to launch."); + Log::instance().out(L"WARNING: The OpenVPN DCO driver uninstall failed to launch."); } else if (result.value() == WAIT_TIMEOUT) { - Log::instance().out("WARNING: The OpenVPN DCO driver uninstall stage timed out."); + Log::instance().out(L"WARNING: The OpenVPN DCO driver uninstall stage timed out."); } else if (result.value() != NO_ERROR) { - Log::instance().out("WARNING: The OpenVPN DCO driver uninstall returned a failure code (%lu).", result.value()); + Log::instance().out(L"WARNING: The OpenVPN DCO driver uninstall returned a failure code (%lu).", result.value()); } else { Log::instance().out(L"OpenVPN DCO driver (%s) successfully removed from the Windows driver store", adapterOEMIdentifier.c_str()); diff --git a/installer/windows/utils/applicationinfo.cpp b/installer/windows/utils/applicationinfo.cpp index 8905b2491..ebe19ddf8 100644 --- a/installer/windows/utils/applicationinfo.cpp +++ b/installer/windows/utils/applicationinfo.cpp @@ -1,8 +1,5 @@ #include "applicationinfo.h" -#include -#include - #include "path.h" #include "utils.h" #include "version/windscribe_version.h" diff --git a/installer/windows/utils/archive.cpp b/installer/windows/utils/archive.cpp new file mode 100644 index 000000000..f8d5bc2d5 --- /dev/null +++ b/installer/windows/utils/archive.cpp @@ -0,0 +1,232 @@ +#include "archive.h" + +#include +#include +#include +#include + +#include + +#include "win32handle.h" +#include "wsscopeguard.h" + +namespace wsl { + +static std::wstring to_wstring(const std::string &str) { + std::wstring_convert> converter; + return converter.from_bytes(str); +} + +Archive::Archive() +{ +} + +void Archive::setLogFunction(const std::function &func) +{ + logFunction_ = func; +} + +void Archive::log(const std::wstring& msg) +{ + if (logFunction_) { + logFunction_(msg); + } +} + +std::wstring Archive::extractor(const std::wstring &folder) const +{ + std::filesystem::path extractorUtility(folder); + extractorUtility.append(L"7zr.exe"); + return extractorUtility; +} + +bool Archive::extract(const std::wstring &resourceName, const std::wstring &archiveName, + const std::wstring &extractFolder, const std::wstring &targetFolder) +{ + bool isExtracted = false; + + try { + if (!std::filesystem::exists(extractor(extractFolder))) { + extractArchiveFromResource(L"7zExtractor", L"7zr.exe", extractFolder); + } + + extractArchiveFromResource(resourceName, archiveName, extractFolder); + extractFiles(archiveName, extractFolder, targetFolder); + isExtracted = true; + } + catch (std::system_error &ex) { + log(to_wstring(ex.what())); + } + + return isExtracted; +} + +bool Archive::extract(const std::wstring &resourceName, const std::wstring &archiveName, const std::wstring &extractFolder, + const std::wstring &filename, const std::wstring &targetFolder) +{ + // The extractor utility is expected to exist in the extractFolder, placed there by the bootstrapper. + if (!std::filesystem::exists(extractor(extractFolder))) { + log(L"The extraction utility was not found in " + extractFolder); + return false; + } + + bool isExtracted = false; + + try { + extractArchiveFromResource(resourceName, archiveName, extractFolder); + extractFile(archiveName, extractFolder, filename, targetFolder); + isExtracted = true; + } + catch (std::system_error &ex) { + log(to_wstring(ex.what())); + } + + return isExtracted; +} + +void Archive::extractArchiveFromResource(const std::wstring &resourceName, const std::wstring &archiveName, const std::wstring &extractFolder) +{ + log(L"Extracting resource: " + archiveName); + + HRSRC resource = ::FindResource(nullptr, resourceName.c_str(), L"BINARY"); + if (resource == NULL) { + throw std::system_error(::GetLastError(), std::system_category(), "extractArchiveFromResource FindResource failed"); + } + + HGLOBAL dataHandle = ::LoadResource(NULL, resource); + if (dataHandle == NULL) { + throw std::system_error(::GetLastError(), std::system_category(), "extractArchiveFromResource LoadResource failed"); + } + + size_t dataSize = ::SizeofResource(NULL, resource); + if (dataSize == 0) { + throw std::system_error(::GetLastError(), std::system_category(), "extractArchiveFromResource SizeofResource failed"); + } + + LPVOID data = ::LockResource(dataHandle); + if (data == NULL) { + throw std::system_error(::GetLastError(), std::system_category(), "extractArchiveFromResource LockResource failed"); + } + + std::filesystem::path target(extractFolder); + std::filesystem::create_directories(target); + target.append(archiveName); + + FILE* fileHandle = nullptr; + auto closeFile = wsl::wsScopeGuard([&] { + if (fileHandle != nullptr) { + fclose(fileHandle); + } + }); + + errno_t result = _wfopen_s(&fileHandle, target.c_str(), L"wb"); + if ((result != 0) || (fileHandle == nullptr)) { + throw std::system_error(result, std::system_category(), "extractArchiveFromResource _wfopen_s failed"); + } + + size_t bytesWritten = fwrite(data, 1, dataSize, fileHandle); + if (bytesWritten != dataSize) { + throw std::system_error(errno, std::system_category(), "extractArchiveFromResource fwrite failed to write entire resource"); + } + + fflush(fileHandle); +} + +void Archive::extractFile(const std::wstring &archiveName, const std::wstring &extractFolder, + const std::wstring &filename, const std::wstring &targetFolder) +{ + log(L"Extracting " + filename + L" from " + archiveName); + + std::filesystem::path archive(extractFolder); + archive.append(archiveName); + + std::wostringstream cmd; + cmd << L"\"" << extractor(extractFolder) << L"\" e -y -bb3 -bd -o\"" << targetFolder << "\" \"" << archive.c_str() << L"\" " << filename; + + executeCmd(cmd.str()); +} + +void Archive::extractFiles(const std::wstring &archiveName, const std::wstring &extractFolder, const std::wstring &targetFolder) +{ + log(L"Extracting files from " + archiveName); + + std::filesystem::path archive(extractFolder); + archive.append(archiveName); + + std::wostringstream cmd; + cmd << L"\"" << extractor(extractFolder) << L"\" x -y -bb3 -bd -o\"" << targetFolder << "\" \"" << archive.c_str() << L"\""; + + executeCmd(cmd.str()); +} + +void Archive::executeCmd(const std::wstring &cmd) +{ + SECURITY_ATTRIBUTES sa; + ::ZeroMemory(&sa, sizeof(sa)); + sa.nLength = sizeof(sa); + sa.bInheritHandle = TRUE; + + Win32Handle pipeRead; + Win32Handle pipeWrite; + if (!::CreatePipe(pipeRead.data(), pipeWrite.data(), &sa, 0)) { + throw std::system_error(::GetLastError(), std::system_category(), "executeCmd CreatePipe failed"); + } + + STARTUPINFO si; + ::ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + si.dwFlags |= STARTF_USESTDHANDLES; + si.hStdInput = NULL; + si.hStdError = pipeWrite.getHandle(); + si.hStdOutput = pipeWrite.getHandle(); + + // As per the Win32 docs; the Unicode version of CreateProcess 'may' modify its lpCommandLine + // parameter. Therefore, this parameter cannot be a pointer to read-only memory (such as a + // const variable or a literal string). If this parameter is a constant string, CreateProcess + // may cause an access violation. Maximum length of the lpCommandLine parameter is 32767. + std::unique_ptr exec(new wchar_t[32767]); + wcsncpy_s(exec.get(), 32767, cmd.c_str(), _TRUNCATE); + + log(L"executeCmd: " + cmd); + + PROCESS_INFORMATION pi; + ::ZeroMemory(&pi, sizeof(pi)); + auto result = ::CreateProcess(NULL, exec.get(), NULL, NULL, TRUE, CREATE_NO_WINDOW | NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi); + + if (result == FALSE) { + throw std::system_error(::GetLastError(), std::system_category(), "executeCmd CreateProcess failed"); + } + + ::CloseHandle(pi.hThread); + Win32Handle process(pi.hProcess); + + result = process.wait(INFINITE); + if (result != WAIT_OBJECT_0) { + throw std::system_error(::GetLastError(), std::system_category(), "executeCmd WaitForSingleObject failed"); + } + + DWORD exitCode; + result = ::GetExitCodeProcess(process.getHandle(), &exitCode); + if (result != FALSE) { + log(L"executeCmd completed with exit code " + std::to_wstring(exitCode)); + } + + pipeWrite.closeHandle(); + + char buf[1024]; + std::string programOutput; + while (true) { + DWORD bytesRead; + result = ::ReadFile(pipeRead.getHandle(), buf, 1024, &bytesRead, NULL); + if (result == FALSE || bytesRead == 0) { + break; + } + programOutput += std::string(buf, bytesRead); + } + + if (!programOutput.empty()) { + log(L"executeCmd output: " + to_wstring(programOutput)); + } +} + +} diff --git a/installer/windows/utils/archive.h b/installer/windows/utils/archive.h new file mode 100644 index 000000000..22285f09c --- /dev/null +++ b/installer/windows/utils/archive.h @@ -0,0 +1,39 @@ +#pragma once + +#include + +namespace wsl { + +class Archive +{ +public: + explicit Archive(); + void setLogFunction(const std::function& func); + + // Extract archiveName from the embedded resourceName to the extractFolder, then + // extract all files from the archive to the targetFolder. + bool extract(const std::wstring &resourceName, const std::wstring &archiveName, + const std::wstring &extractFolder, const std::wstring &targetFolder); + + // Extract archiveName from the embedded resourceName to the extractFolder, then + // extract filename from the archive to the targetFolder. + bool extract(const std::wstring &resourceName, const std::wstring &archiveName, + const std::wstring &extractFolder, const std::wstring &filename, + const std::wstring &targetFolder); + +private: + std::function logFunction_ = nullptr; + +private: + void executeCmd(const std::wstring &cmd); + void extractArchiveFromResource(const std::wstring &resourceName, const std::wstring &archiveName, + const std::wstring &extractFolder); + void extractFile(const std::wstring &archiveName, const std::wstring &extractFolder, + const std::wstring &filename, const std::wstring &targetFolder); + void extractFiles(const std::wstring &archiveName, const std::wstring &extractFolder, const std::wstring &targetFolder); + void log(const std::wstring& msg); + + std::wstring extractor(const std::wstring &folder) const; +}; + +} diff --git a/installer/windows/utils/logger.cpp b/installer/windows/utils/logger.cpp index 87d9023bf..c9975d83e 100644 --- a/installer/windows/utils/logger.cpp +++ b/installer/windows/utils/logger.cpp @@ -3,7 +3,6 @@ #include #include -#include #include #include @@ -23,22 +22,6 @@ void Log::init(bool installing) installing_ = installing; } -void Log::out(const char* format, ...) -{ - // Using dynamic allocation here for the string buffers as the VS2019 compiler was warning - // about stack overflow potential. - va_list args; - va_start(args, format); - unique_ptr logMsg(new char[10000]); - _vsnprintf_s(logMsg.get(), 10000, _TRUNCATE, format, args); - va_end(args); - - wostringstream stream; - stream << logMsg.get(); - - out(stream.str()); -} - void Log::out(const wchar_t* format, ...) { // Using dynamic allocation here for the string buffers as the VS2019 compiler was warning @@ -61,7 +44,6 @@ void Log::out(const wstring& message) now.time_since_epoch()) % 1000; const int millisecondsSinceStart = std::chrono::duration_cast(now - start_).count(); - wostringstream stream; stream << L"[" << std::put_time(utc_tm, L"%d%m%y %H:%M:%S") << L':' << std::setfill(L'0') << std::setw(3) << milliseconds.count() diff --git a/installer/windows/utils/logger.h b/installer/windows/utils/logger.h index 0e668a9dc..af871748e 100644 --- a/installer/windows/utils/logger.h +++ b/installer/windows/utils/logger.h @@ -21,7 +21,6 @@ class Log } void init(bool installing); - void out(const char* format, ...); void out(const wchar_t* format, ...); void out(const std::wstring& message); void writeFile(const std::wstring& installPath) const; diff --git a/installer/windows/utils/path.cpp b/installer/windows/utils/path.cpp index 0d8f03d4b..ec1095ba8 100644 --- a/installer/windows/utils/path.cpp +++ b/installer/windows/utils/path.cpp @@ -48,7 +48,7 @@ bool isRoot(const std::wstring& fileName) bool isOnSystemDrive(const std::wstring& fileName) { if (!fileName.empty()) { - wstring systemDir = Utils::GetSystemDir(); + wstring systemDir = Utils::getSystemDir(); if (!systemDir.empty()) { return ::PathIsSameRootW(systemDir.c_str(), fileName.c_str());; } diff --git a/installer/windows/utils/utils.cpp b/installer/windows/utils/utils.cpp index 1bbb381a4..fdc8a47ba 100644 --- a/installer/windows/utils/utils.cpp +++ b/installer/windows/utils/utils.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include "applicationinfo.h" @@ -16,19 +17,19 @@ using namespace std; namespace Utils { -wstring GetSystemDir() +wstring getSystemDir() { wchar_t path[MAX_PATH]; UINT result = ::GetSystemDirectory(path, MAX_PATH); if (result == 0 || result >= MAX_PATH) { - Log::instance().out("GetSystemDir failed (%lu)", ::GetLastError()); + Log::instance().out(L"GetSystemDir failed (%lu)", ::GetLastError()); return wstring(); } return wstring(path); } -optional InstExec(const wstring& appName, const wstring& commandLine, DWORD timeoutMS, +optional instExec(const wstring& appName, const wstring& commandLine, DWORD timeoutMS, WORD showWindowFlags, const wstring ¤tFolder, DWORD *lastError) { wostringstream stream; @@ -73,7 +74,7 @@ optional InstExec(const wstring& appName, const wstring& commandLine, DWO BOOL result = ::CreateProcess(nullptr, exec.get(), nullptr, nullptr, false, CREATE_DEFAULT_ERROR_MODE, nullptr, lpCurrentDirectory, &si, &pi); if (result == FALSE) { - Log::instance().out("Process::InstExec CreateProcess(%ls) failed (%lu)", exec.get(), ::GetLastError()); + Log::instance().out(L"Process::InstExec CreateProcess(%s) failed (%lu)", exec.get(), ::GetLastError()); if (lastError) { *lastError = ::GetLastError(); } @@ -90,14 +91,14 @@ optional InstExec(const wstring& appName, const wstring& commandLine, DWO if (waitResult == WAIT_FAILED) { if (::GetLastError() != ERROR_NOT_GUI_PROCESS) { // We're not treating this error as critical, but still want to note it. - Log::instance().out("Process::InstExec WaitForInputIdle failed (%lu)", ::GetLastError()); + Log::instance().out(L"Process::InstExec WaitForInputIdle failed (%lu)", ::GetLastError()); } } waitResult = ::WaitForSingleObject(pi.hProcess, timeoutMS); if (waitResult == WAIT_FAILED) { - Log::instance().out("Process::InstExec WaitForSingleObject failed (%lu)", ::GetLastError()); + Log::instance().out(L"Process::InstExec WaitForSingleObject failed (%lu)", ::GetLastError()); if (lastError) { *lastError = ::GetLastError(); } @@ -112,7 +113,7 @@ optional InstExec(const wstring& appName, const wstring& commandLine, DWO DWORD processExitCode; result = ::GetExitCodeProcess(pi.hProcess, &processExitCode); if (result == FALSE) { - Log::instance().out("Process::InstExec GetExitCodeProcess failed (%lu)", ::GetLastError()); + Log::instance().out(L"Process::InstExec GetExitCodeProcess failed (%lu)", ::GetLastError()); if (lastError) { *lastError = ::GetLastError(); } @@ -122,26 +123,26 @@ optional InstExec(const wstring& appName, const wstring& commandLine, DWO return processExitCode; } -wstring DesktopFolder() +wstring desktopFolder() { wchar_t szPath[MAX_PATH]; HRESULT hResult = ::SHGetFolderPath(NULL, CSIDL_COMMON_DESKTOPDIRECTORY, NULL, SHGFP_TYPE_CURRENT, szPath); if (FAILED(hResult)) { - Log::instance().out("SHGetFolderPath(CSIDL_DESKTOPDIRECTORY) failed (%lu)", ::GetLastError()); + Log::instance().out(L"SHGetFolderPath(CSIDL_DESKTOPDIRECTORY) failed (%lu)", ::GetLastError()); return wstring(); } return wstring(szPath); } -wstring StartMenuProgramsFolder() +wstring startMenuProgramsFolder() { wchar_t szPath[MAX_PATH]; HRESULT hResult = ::SHGetFolderPath(NULL, CSIDL_COMMON_PROGRAMS, NULL, SHGFP_TYPE_CURRENT, szPath); if (FAILED(hResult)) { - Log::instance().out("SHGetFolderPath(CSIDL_COMMON_PROGRAMS) failed (%lu)", ::GetLastError()); + Log::instance().out(L"SHGetFolderPath(CSIDL_COMMON_PROGRAMS) failed (%lu)", ::GetLastError()); return wstring(); } @@ -209,6 +210,16 @@ HWND appMainWindowHandle() return pWindowInfo->appMainWindow; } +wstring getExePath() +{ + wchar_t path[MAX_PATH]; + int ret = GetModuleFileName(NULL, path, MAX_PATH); + if (ret == 0) { + return wstring(); + } + return filesystem::path(path).parent_path().wstring(); +} + wstring programFilesFolder() { TCHAR programFilesPath[MAX_PATH]; diff --git a/installer/windows/utils/utils.h b/installer/windows/utils/utils.h index 1288f7bc3..31ba17bb9 100644 --- a/installer/windows/utils/utils.h +++ b/installer/windows/utils/utils.h @@ -11,16 +11,17 @@ namespace Utils // If the app is running, retrieve its main window handle. HWND appMainWindowHandle(); -std::wstring DesktopFolder(); -std::wstring GetSystemDir(); +std::wstring desktopFolder(); +std::wstring getExePath(); +std::wstring getSystemDir(); std::wstring programFilesFolder(); -std::wstring StartMenuProgramsFolder(); +std::wstring startMenuProgramsFolder(); // Run appName with the given commandLine and showWindowFlags. Wait timeoutMS milliseconds for // the process to complete. Use timeoutMS=INFINITE to wait forever and timeoutMS=0 to not wait. // Returns std::nullopt if there was a failure creating the process, waiting for it to complete, // or obtaining its exit code. Returns the process exit code or WAIT_TIMEOUT otherwise. -std::optional InstExec(const std::wstring &appName, const std::wstring &commandLine, +std::optional instExec(const std::wstring &appName, const std::wstring &commandLine, DWORD timeoutMS, WORD showWindowFlags, const std::wstring ¤tFolder = std::wstring(), DWORD *lastError = nullptr); diff --git a/libs/wsnet/include/wsnet/WSNetServerAPI.h b/libs/wsnet/include/wsnet/WSNetServerAPI.h index 0edf04f5b..bcb6364a8 100644 --- a/libs/wsnet/include/wsnet/WSNetServerAPI.h +++ b/libs/wsnet/include/wsnet/WSNetServerAPI.h @@ -77,8 +77,11 @@ class WSNetServerAPI : public scapix_object virtual std::shared_ptr wgConfigsInit(const std::string &authHash, const std::string &clientPublicKey, bool deleteOldestKey, WSNetRequestFinishedCallback callback) = 0; + // wgTtl - optional string parameter, for example "3600" + // this is a wait time before server will discard returned interface address if no handshake is made + // this helps with device sleep/Idle events where network connectivity be restricted to conserve battery periodically virtual std::shared_ptr wgConfigsConnect(const std::string &authHash, const std::string &clientPublicKey, - const std::string &hostname, const std::string &deviceId, + const std::string &hostname, const std::string &deviceId, const std::string &wgTtl, WSNetRequestFinishedCallback callback) = 0; virtual std::shared_ptr myIP(WSNetRequestFinishedCallback callback) = 0; @@ -125,6 +128,10 @@ class WSNetServerAPI : public scapix_object const std::string &score, const std::string &signature, WSNetRequestFinishedCallback callback) = 0; + virtual std::shared_ptr verifyTvLoginCode(const std::string &authHash, const std::string &xpressCode, + WSNetRequestFinishedCallback callback) = 0; + + }; } // namespace wsnet diff --git a/libs/wsnet/src/httpnetworkmanager/curlnetworkmanager.cpp b/libs/wsnet/src/httpnetworkmanager/curlnetworkmanager.cpp index 1406fbbea..298f74be3 100644 --- a/libs/wsnet/src/httpnetworkmanager/curlnetworkmanager.cpp +++ b/libs/wsnet/src/httpnetworkmanager/curlnetworkmanager.cpp @@ -254,6 +254,7 @@ bool CurlNetworkManager::setupOptions(RequestInfo *requestInfo, const std::share spdlog::debug("New curl request : {}", request->url().c_str()); + // Required for android only. if (curl_easy_setopt(requestInfo->curlEasyHandle, CURLOPT_FRESH_CONNECT, 1) != CURLE_OK) return false; if (curl_easy_setopt(requestInfo->curlEasyHandle, CURLOPT_CONNECTTIMEOUT_MS , request->timeoutMs()) != CURLE_OK) return false; diff --git a/libs/wsnet/src/serverapi/requestsfactory.cpp b/libs/wsnet/src/serverapi/requestsfactory.cpp index fcdcf1dfb..666e55938 100644 --- a/libs/wsnet/src/serverapi/requestsfactory.cpp +++ b/libs/wsnet/src/serverapi/requestsfactory.cpp @@ -238,13 +238,14 @@ BaseRequest *requests_factory::wgConfigsInit(const std::string &authHash, const return request; } -BaseRequest *requests_factory::wgConfigsConnect(const std::string &authHash, const std::string &clientPublicKey, const std::string &hostname, const std::string &deviceId, RequestFinishedCallback callback) +BaseRequest *requests_factory::wgConfigsConnect(const std::string &authHash, const std::string &clientPublicKey, const std::string &hostname, const std::string &deviceId, const std::string &wgTtl, RequestFinishedCallback callback) { std::map extraParams; extraParams["session_auth_hash"] = authHash; extraParams["wg_pubkey"] = clientPublicKey; extraParams["hostname"] = hostname; extraParams["device_id"] = deviceId; + extraParams["wg_ttl"] = wgTtl; auto request = new BaseRequest(HttpMethod::kPost, SubdomainType::kApi, RequestPriority::kHigh, "WgConfigs/connect", extraParams, callback); request->setContentTypeHeader("Content-type: text/html; charset=utf-8"); @@ -386,4 +387,14 @@ BaseRequest *requests_factory::shakeData(const std::string &authHash, RequestFin return request; } +BaseRequest *requests_factory::verifyTvLoginCode(const std::string &authHash, const std::string &xpressCode, RequestFinishedCallback callback) +{ + std::map extraParams; + extraParams["session_auth_hash"] = authHash; + extraParams["xpress_code"] = xpressCode; + auto request = new BaseRequest(HttpMethod::kPut, SubdomainType::kApi, RequestPriority::kNormal, "XpressLogin", extraParams, callback); + request->setContentTypeHeader("Content-type: text/html; charset=utf-8"); + return request; +} + } // namespace wsnet diff --git a/libs/wsnet/src/serverapi/requestsfactory.h b/libs/wsnet/src/serverapi/requestsfactory.h index a436f3b2a..fe86176a6 100644 --- a/libs/wsnet/src/serverapi/requestsfactory.h +++ b/libs/wsnet/src/serverapi/requestsfactory.h @@ -46,7 +46,7 @@ namespace requests_factory BaseRequest *wgConfigsInit(const std::string &authHash, const std::string &clientPublicKey, bool deleteOldestKey, RequestFinishedCallback callback); BaseRequest *wgConfigsConnect(const std::string &authHash, const std::string &clientPublicKey, - const std::string &hostname, const std::string &deviceId, + const std::string &hostname, const std::string &deviceId, const std::string &wgTtl, RequestFinishedCallback callback); BaseRequest *myIP(RequestFinishedCallback callback); @@ -80,6 +80,8 @@ namespace requests_factory BaseRequest *recordShakeForDataScore(const std::string &authHash, const std::string &platform, const std::string &score, const std::string &signature, RequestFinishedCallback callback); + + BaseRequest *verifyTvLoginCode(const std::string &authHash, const std::string &xpressCode, RequestFinishedCallback callback); } } // namespace wsnet diff --git a/libs/wsnet/src/serverapi/serverapi.cpp b/libs/wsnet/src/serverapi/serverapi.cpp index a12c5657f..6f9b56784 100644 --- a/libs/wsnet/src/serverapi/serverapi.cpp +++ b/libs/wsnet/src/serverapi/serverapi.cpp @@ -232,10 +232,10 @@ std::shared_ptr ServerAPI::wgConfigsInit(const std::str return cancelableCallback; } -std::shared_ptr ServerAPI::wgConfigsConnect(const std::string &authHash, const std::string &clientPublicKey, const std::string &hostname, const std::string &deviceId, WSNetRequestFinishedCallback callback) +std::shared_ptr ServerAPI::wgConfigsConnect(const std::string &authHash, const std::string &clientPublicKey, const std::string &hostname, const std::string &deviceId, const std::string &wgTtl, WSNetRequestFinishedCallback callback) { auto cancelableCallback = std::make_shared>(callback); - BaseRequest *request = requests_factory::wgConfigsConnect(authHash, clientPublicKey, hostname, deviceId, cancelableCallback); + BaseRequest *request = requests_factory::wgConfigsConnect(authHash, clientPublicKey, hostname, deviceId, wgTtl, cancelableCallback); boost::asio::post(io_context_, [this, request] { impl_->executeRequest(std::unique_ptr(request)); }); return cancelableCallback; } @@ -347,6 +347,14 @@ std::shared_ptr ServerAPI::recordShakeForDataScore(cons return cancelableCallback; } +std::shared_ptr ServerAPI::verifyTvLoginCode(const std::string &authHash, const std::string &xpressCode, WSNetRequestFinishedCallback callback) +{ + auto cancelableCallback = std::make_shared>(callback); + BaseRequest *request = requests_factory::verifyTvLoginCode(authHash, xpressCode, cancelableCallback); + boost::asio::post(io_context_, [this, request] { impl_->executeRequest(std::unique_ptr(request)); }); + return cancelableCallback; +} + void ServerAPI::onVPNConnectStateChanged(bool isConnected) { boost::asio::post(io_context_, [this, isConnected] { diff --git a/libs/wsnet/src/serverapi/serverapi.h b/libs/wsnet/src/serverapi/serverapi.h index b3d936790..e207a8037 100644 --- a/libs/wsnet/src/serverapi/serverapi.h +++ b/libs/wsnet/src/serverapi/serverapi.h @@ -66,7 +66,7 @@ class ServerAPI : public WSNetServerAPI std::shared_ptr wgConfigsInit(const std::string &authHash, const std::string &clientPublicKey, bool deleteOldestKey, WSNetRequestFinishedCallback callback) override; std::shared_ptr wgConfigsConnect(const std::string &authHash, const std::string &clientPublicKey, - const std::string &hostname, const std::string &deviceId, + const std::string &hostname, const std::string &deviceId, const std::string &wgTtl, WSNetRequestFinishedCallback callback) override; std::shared_ptr myIP(WSNetRequestFinishedCallback callback) override; @@ -101,6 +101,9 @@ class ServerAPI : public WSNetServerAPI const std::string &score, const std::string &signature, WSNetRequestFinishedCallback callback) override; + std::shared_ptr verifyTvLoginCode(const std::string &authHash, const std::string &xpressCode, + WSNetRequestFinishedCallback callback) override; + private: std::unique_ptr impl_; boost::asio::io_context &io_context_; diff --git a/libs/wsnet/tools/build_android.sh b/libs/wsnet/tools/build_android.sh index 5f9958a90..9128839d2 100755 --- a/libs/wsnet/tools/build_android.sh +++ b/libs/wsnet/tools/build_android.sh @@ -2,16 +2,16 @@ export JAVA_HOME="/Applications/Android Studio.app/Contents/jbr/Contents/Home" if [ ! -d "$ANDROID_NDK_HOME" ]; then - export ANDROID_NDK_HOME="/Users/aaa/Library/Android/sdk/ndk/25.2.9519653" + export ANDROID_NDK_HOME="$HOME/Library/Android/sdk/ndk/25.2.9519653" fi if [ ! -d "$VCPKG_ROOT" ]; then - export VCPKG_ROOT="/Users/aaa/vcpkg" + export VCPKG_ROOT="$HOME/vcpkg" fi export PATH=/opt/homebrew/bin:$PATH -ARCHITECTURES="arm64-v8a" +ARCHITECTURES=("arm64-v8a" "armeabi-v7a" "x86" "x86_64") if [ ! -d "$JAVA_HOME" ]; then echo "$JAVA_HOME does not exist." @@ -33,7 +33,7 @@ NumberOfCores=$(sysctl -n hw.ncpu) rm -r temp rm wsnet.aar -for arch in $ARCHITECTURES; do +for arch in "${ARCHITECTURES[@]}"; do rm -r ../generated mkdir -p "temp/build/$arch" diff --git a/tools/base/arghelper.py b/tools/base/arghelper.py index e09bdb48c..542a4ba58 100644 --- a/tools/base/arghelper.py +++ b/tools/base/arghelper.py @@ -26,7 +26,9 @@ class ArgHelper: OPTION_CI_MODE = "--ci-mode" # linux packaging OPTION_BUILD_RPM = "--build-rpm" + OPTION_BUILD_RPM_OPENSUSE = "--build-rpm-opensuse" OPTION_BUILD_DEB = "--build-deb" + OPTION_BUILD_CLI_ONLY = "--build-cli-only" # target architecture OPTION_TARGET_X86_64 = "--x86_64" # this is the default OPTION_TARGET_ARM64 = "--arm64" @@ -52,10 +54,12 @@ class ArgHelper: options.append((OPTION_CI_MODE, "Used to indicate app is building on CI")) options.append(("\nLinux packaging", "")) options.append((OPTION_BUILD_RPM, "Build .rpm package for Red Hat and derivative distros")) + options.append((OPTION_BUILD_RPM_OPENSUSE, "Build .rpm package for OpenSUSE and derivative distros")) options.append((OPTION_BUILD_DEB, "Build .deb package for Debian and derivative distros (default)")) + options.append((OPTION_BUILD_CLI_ONLY, "Build package without GUI (Linux only)")) options.append(("\nTarget Architecture", "")) - options.append((OPTION_TARGET_X86_64, "Build for the Intel/AMD 64-bit x86 CPU architecture (default) (Note: only used for Windows builds)")) - options.append((OPTION_TARGET_ARM64, "Build for the ARM 64-bit CPU architecture (Note: only used for Windows builds)")) + options.append((OPTION_TARGET_X86_64, "Build for the Intel/AMD 64-bit x86 CPU architecture (default) (Windows only)")) + options.append((OPTION_TARGET_ARM64, "Build for the ARM 64-bit CPU architecture (Windows only)")) def __init__(self, program_arg_list): self.args_only = program_arg_list[1:] @@ -93,10 +97,14 @@ def __init__(self, program_arg_list): self.mode_ci = ArgHelper.OPTION_CI_MODE in program_arg_list # linux packaging - no_packaging_selected = ArgHelper.OPTION_BUILD_RPM not in program_arg_list and ArgHelper.OPTION_BUILD_DEB not in program_arg_list + no_packaging_selected = ArgHelper.OPTION_BUILD_RPM not in program_arg_list and \ + ArgHelper.OPTION_BUILD_RPM_OPENSUSE not in program_arg_list and \ + ArgHelper.OPTION_BUILD_DEB not in program_arg_list self.mode_build_deb = no_packaging_selected or ArgHelper.OPTION_BUILD_DEB in program_arg_list self.mode_build_rpm = ArgHelper.OPTION_BUILD_RPM in program_arg_list + self.mode_build_rpm_opensuse = ArgHelper.OPTION_BUILD_RPM_OPENSUSE in program_arg_list + self.mode_build_cli_only = ArgHelper.OPTION_BUILD_CLI_ONLY in program_arg_list # target architecture no_arch_selected = ArgHelper.OPTION_TARGET_X86_64 not in program_arg_list and ArgHelper.OPTION_TARGET_ARM64 not in program_arg_list @@ -145,12 +153,17 @@ def build_mode(self): def help(self): return self.mode_help - def build_rpm(self): + def build_rpm(self, distro): + if (distro == "opensuse"): + return self.mode_build_rpm_opensuse return self.mode_build_rpm def build_deb(self): return self.mode_build_deb + def build_cli_only(self): + return self.mode_build_cli_only + def build_debug(self): return self.mode_debug diff --git a/tools/build_all.py b/tools/build_all.py index 49c4d19dc..f1a6ab5a5 100644 --- a/tools/build_all.py +++ b/tools/build_all.py @@ -243,6 +243,8 @@ def build_component(component, qt_root, buildenv=None): generate_cmd.extend(["-DDEFINE_USE_SIGNATURE_CHECK_MACRO=ON"]) if arghelper.build_tests(): generate_cmd.extend(["-DIS_BUILD_TESTS=ON"]) + if arghelper.build_cli_only() and CURRENT_OS == "linux": + generate_cmd.extend(["-DDEFINE_CLI_ONLY_MACRO=ON"]) if CURRENT_OS == "macos": # Build an universal binary only on CI if arghelper.ci_mode(): @@ -463,9 +465,8 @@ def prep_installer_win32(configdata, crt_root): additional_dir = os.path.join(pathhelper.ROOT_DIR, "installer", "windows", "additional_files") copy_files("additional x86_64", configdata["deploy_files"]["win32_x86_64"]["installer"]["additional_files"], additional_dir, BUILD_INSTALLER_FILES) - if "license_files" in configdata: - license_dir = os.path.join(pathhelper.COMMON_DIR, "licenses") - copy_files("license", configdata["license_files"], license_dir, BUILD_INSTALLER_FILES) + if "common_files" in configdata: + copy_files("common", configdata["common_files"], pathhelper.COMMON_DIR, BUILD_INSTALLER_FILES) # Pack symbols for crashdump analysis. pack_symbols() @@ -543,16 +544,14 @@ def sign_bootstrap_win32(configdata): def build_installer_mac(configdata, qt_root, build_path): - # Place everything in a 7z archive. + # Place everything in a lzma tarball. msg.Info("Zipping...") installer_info = configdata["installer"]["macos"] - arc_path = os.path.join(pathhelper.ROOT_DIR, installer_info["subdir"], "resources", "windscribe.7z") + arc_path = os.path.join(pathhelper.ROOT_DIR, installer_info["subdir"], "resources", "windscribe.tar.lzma") archive_filename = os.path.normpath(arc_path) if os.path.exists(archive_filename): utl.RemoveFile(archive_filename) - iutl.RunCommand(["7z", "a", archive_filename, - os.path.join(BUILD_INSTALLER_FILES, "Windscribe.app"), - "-y", "-bso0", "-bsp2"]) + iutl.RunCommand(["tar", "--lzma", "-cf", archive_filename, "-C", os.path.join(BUILD_INSTALLER_FILES, "Windscribe.app"), "."]) # Build and sign the installer. with utl.PushDir(): os.chdir(build_path) @@ -613,11 +612,17 @@ def build_installer_linux(configdata, qt_root): # Creates the following: # * windscribe_2.x.y_amd64.deb # * windscribe_2.x.y_x86_64.rpm - copy_libs(configdata, "linux", "installer", BUILD_INSTALLER_FILES) + + if arghelper.build_cli_only(): + build_config = "cli" + else: + build_config = "gui" + + copy_libs(configdata, "linux", "installer_" + build_config, BUILD_INSTALLER_FILES) msg.Info("Fixing rpaths...") - if "fix_rpath" in configdata["deploy_files"]["linux"]["installer"]: - for k in configdata["deploy_files"]["linux"]["installer"]["fix_rpath"]: + if "fix_rpath" in configdata["deploy_files"]["linux"]["installer_" + build_config]: + for k in configdata["deploy_files"]["linux"]["installer_" + build_config]["fix_rpath"]: dstfile = os.path.join(BUILD_INSTALLER_FILES, k) fix_rpath_linux(dstfile) @@ -626,23 +631,34 @@ def build_installer_linux(configdata, qt_root): for binary_name in configdata["codesign_files"]["linux"]: code_sign_linux(binary_name, BUILD_INSTALLER_FILES) - if "license_files" in configdata: - license_dir = os.path.join(pathhelper.COMMON_DIR, "licenses") - copy_files("license", configdata["license_files"], license_dir, BUILD_INSTALLER_FILES) + if "common_files" in configdata: + copy_files("common", configdata["common_files"], pathhelper.COMMON_DIR, BUILD_INSTALLER_FILES) # create .deb with dest_package if arghelper.build_deb(): msg.Info("Creating .deb package...") src_package_path = os.path.join(pathhelper.ROOT_DIR, "installer", "linux", "common") - deb_files_path = os.path.join(pathhelper.ROOT_DIR, "installer", "linux", "debian_package") + deb_files_path = os.path.join(pathhelper.ROOT_DIR, "installer", "linux", build_config, "debian_package") + if platform.processor() == "x86_64": - dest_package_name = "windscribe_{}_amd64".format(extractor.app_version(True)) + if arghelper.build_cli_only(): + dest_package_name = "windscribe-cli_{}_amd64".format(extractor.app_version(True)) + else: + dest_package_name = "windscribe_{}_amd64".format(extractor.app_version(True)) elif platform.processor() == "aarch64": - dest_package_name = "windscribe_{}_arm64".format(extractor.app_version(True)) + if arghelper.build_cli_only(): + dest_package_name = "windscribe-cli_{}_arm64".format(extractor.app_version(True)) + else: + dest_package_name = "windscribe_{}_arm64".format(extractor.app_version(True)) + dest_package_path = os.path.join(BUILD_INSTALLER_FILES, "..", dest_package_name) utl.CopyAllFiles(src_package_path, dest_package_path) + if arghelper.build_cli_only(): + utl.CopyAllFiles(os.path.join(src_package_path, "..", "cli", "overlay"), dest_package_path) + else: + utl.CopyAllFiles(os.path.join(src_package_path, "..", "gui", "overlay"), dest_package_path) utl.CopyAllFiles(deb_files_path, dest_package_path) utl.CopyAllFiles(BUILD_INSTALLER_FILES, os.path.join(dest_package_path, "opt", "windscribe")) @@ -650,17 +666,29 @@ def build_installer_linux(configdata, qt_root): update_arch_in_config(os.path.join(dest_package_path, "DEBIAN", "control")) iutl.RunCommand(["fakeroot", "dpkg-deb", "--build", dest_package_path]) - if arghelper.build_rpm(): - msg.Info("Creating .rpm package...") + for distro in ["fedora", "opensuse"]: + if arghelper.build_rpm(distro): + build_rpm(distro, build_config) + + +def build_rpm(distro, build_config): + msg.Info("Creating {} .rpm package...".format(distro)) + + utl.CopyAllFiles(os.path.join(pathhelper.ROOT_DIR, "installer", "linux", build_config, "rpm_{}_package".format(distro)), os.path.join(pathlib.Path.home(), "rpmbuild")) + utl.CopyAllFiles(os.path.join(pathhelper.ROOT_DIR, "installer", "linux", "common"), os.path.join(pathlib.Path.home(), "rpmbuild", "SOURCES")) + + if arghelper.build_cli_only(): + utl.CopyAllFiles(os.path.join(pathhelper.ROOT_DIR, "installer", "linux", "cli", "overlay"), os.path.join(pathlib.Path.home(), "rpmbuild", "SOURCES")) + else: + utl.CopyAllFiles(os.path.join(pathhelper.ROOT_DIR, "installer", "linux", "gui", "overlay"), os.path.join(pathlib.Path.home(), "rpmbuild", "SOURCES")) + + utl.CopyAllFiles(BUILD_INSTALLER_FILES, os.path.join(pathlib.Path.home(), "rpmbuild", "SOURCES", "opt", "windscribe")) - utl.CopyAllFiles(os.path.join(pathhelper.ROOT_DIR, "installer", "linux", "rpm_package"), os.path.join(pathlib.Path.home(), "rpmbuild")) - utl.CopyAllFiles(os.path.join(pathhelper.ROOT_DIR, "installer", "linux", "common"), os.path.join(pathlib.Path.home(), "rpmbuild", "SOURCES")) - utl.CopyAllFiles(BUILD_INSTALLER_FILES, os.path.join(pathlib.Path.home(), "rpmbuild", "SOURCES", "opt", "windscribe")) + update_version_in_config(os.path.join(pathlib.Path.home(), "rpmbuild", "SPECS", "windscribe_rpm.spec")) + iutl.RunCommand(["rpmbuild", "-bb", os.path.join(pathlib.Path.home(), "rpmbuild", "SPECS", "windscribe_rpm.spec")]) - update_version_in_config(os.path.join(pathlib.Path.home(), "rpmbuild", "SPECS", "windscribe_rpm.spec")) - iutl.RunCommand(["rpmbuild", "-bb", os.path.join(pathlib.Path.home(), "rpmbuild", "SPECS", "windscribe_rpm.spec")]) - utl.CopyFile(os.path.join(pathlib.Path.home(), "rpmbuild", "RPMS", "x86_64", "windscribe-{}-0.x86_64.rpm".format(extractor.app_version(False))), - os.path.join(BUILD_INSTALLER_FILES, "..", "windscribe_{}_x86_64.rpm".format(extractor.app_version(True)))) + utl.CopyFile(os.path.join(pathlib.Path.home(), "rpmbuild", "RPMS", "x86_64", "windscribe{}-{}-0.x86_64.rpm".format("-cli" if arghelper.build_cli_only() else "", extractor.app_version(False))), + os.path.join(BUILD_INSTALLER_FILES, "..", "windscribe{}_{}_x86_64_{}.rpm".format("-cli" if arghelper.build_cli_only() else "", extractor.app_version(True), distro))) def update_vcpkg_dependencies(): diff --git a/tools/build_all.yml b/tools/build_all.yml index 19500e118..221868947 100644 --- a/tools/build_all.yml +++ b/tools/build_all.yml @@ -215,9 +215,8 @@ deploy_files: remove: - Contents/Frameworks/QtQml.framework - Contents/Frameworks/QtQuick.framework - linux: - installer: + installer_gui: libs: qt: - lib/libQt6Core.so.6 -> lib/libQt6Core.so.6 @@ -253,7 +252,6 @@ deploy_files: - windscribewireguard -> windscribewireguard wstunnel: - windscribewstunnel -> windscribewstunnel - fix_rpath: - lib/libQt6Network.so.6 -> /opt/windscribe/lib - lib/libQt6XcbQpa.so.6 -> /opt/windscribe/lib @@ -266,6 +264,30 @@ deploy_files: - Windscribe -> /opt/windscribe/lib - windscribe-cli -> /opt/windscribe/lib - helper -> /opt/windscribe/lib + installer_cli: + libs: + qt: + - lib/libQt6Core.so.6 -> lib/libQt6Core.so.6 + - lib/libQt6Core5Compat.so.6 -> lib/libQt6Core5Compat.so.6 + - lib/libQt6DBus.so.6 -> lib/libQt6DBus.so.6 + - lib/libQt6Network.so.6 -> lib/libQt6Network.so.6 + - plugins/networkinformation/libqnetworkmanager.so -> plugins/networkinformation/libqnetworkmanager.so + - plugins/tls/libqopensslbackend.so -> plugins/tls/libqopensslbackend.so + wireguard: + - windscribewireguard -> windscribewireguard + wstunnel: + - windscribewstunnel -> windscribewstunnel + fix_rpath: + - lib/libQt6Network.so.6 -> /opt/windscribe/lib + - lib/libQt6DBus.so.6 -> /opt/windscribe/lib + - lib/libssl.so.3 -> /opt/windscribe/lib + - lib/libcrypto.so.3 -> /opt/windscribe/lib + - lib/libwsnet.so -> /opt/windscribe/lib + - windscribeopenvpn -> /opt/windscribe/lib + - windscribe-authhelper -> /opt/windscribe/lib + - Windscribe -> /opt/windscribe/lib + - windscribe-cli -> /opt/windscribe/lib + - helper -> /opt/windscribe/lib codesign_files: # TODO: shared libraries don't need to be signed? @@ -277,5 +299,6 @@ codesign_files: - windscribewireguard - Windscribe -license_files: - - open_source_licenses.txt +common_files: + - config/openssl.cnf -> openssl.cnf + - licenses/open_source_licenses.txt -> open_source_licenses.txt diff --git a/tools/vars/ctrld.yml b/tools/vars/ctrld.yml deleted file mode 100644 index e69de29bb..000000000 diff --git a/tools/vcpkg/ports/curl/portfile.cmake b/tools/vcpkg/ports/curl/portfile.cmake index 0ed2a06a1..90892ad3d 100644 --- a/tools/vcpkg/ports/curl/portfile.cmake +++ b/tools/vcpkg/ports/curl/portfile.cmake @@ -58,6 +58,11 @@ if(VCPKG_TARGET_IS_WINDOWS) list(APPEND OPTIONS -DENABLE_UNICODE=ON) endif() +#see https://github.com/curl/curl/issues/13826 +if(VCPKG_TARGET_IS_ANDROID) + list(APPEND OPTIONS -DHAVE_CLOCK_GETTIME_MONOTONIC_RAW=OFF) +endif() + vcpkg_cmake_configure( SOURCE_PATH "${SOURCE_PATH}" OPTIONS diff --git a/tools/vcpkg/ports/liboqs/0000-macos-universal.patch b/tools/vcpkg/ports/liboqs/0000-macos-universal.patch new file mode 100644 index 000000000..bbf5a917f --- /dev/null +++ b/tools/vcpkg/ports/liboqs/0000-macos-universal.patch @@ -0,0 +1,22 @@ +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 0e700c0d..55c0d986 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -44,7 +44,7 @@ if(EXISTS "/opt/vc/include/bcm_host.h") + add_definitions( -DOQS_USE_RASPBERRY_PI ) + endif() + +-if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|amd64|AMD64") ++if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|amd64|AMD64" OR (APPLE AND CMAKE_OSX_ARCHITECTURES MATCHES "x86_64")) + set(ARCH "x86_64") + set(ARCH_X86_64 ON) + if(${OQS_DIST_BUILD}) +@@ -56,7 +56,7 @@ elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "x86|i586|i686") + if(${OQS_DIST_BUILD}) + set(OQS_DIST_X86_BUILD ON) + endif() +-elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|arm64|arm64v8") ++elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|arm64|arm64v8" OR (APPLE AND CMAKE_OSX_ARCHITECTURES MATCHES "arm64")) + set(ARCH "arm64v8") + set(ARCH_ARM64v8 ON) + if(${OQS_DIST_BUILD}) diff --git a/tools/vcpkg/ports/liboqs/portfile.cmake b/tools/vcpkg/ports/liboqs/portfile.cmake new file mode 100644 index 000000000..05614a715 --- /dev/null +++ b/tools/vcpkg/ports/liboqs/portfile.cmake @@ -0,0 +1,129 @@ +vcpkg_check_linkage(ONLY_STATIC_LIBRARY) + +set(SHA512_VALUE 244d637b8754ee142e37734b4b8c250cda5dd031781ab68cf9990b624ad8b9801be31a64f9233a0dd78afbb10f62093b3ec55d87727cb98f0d71b7f13ed57853) + +set(VCPKG_BUILD_TYPE release) +set(VCPKG_POLICY_MISMATCHED_NUMBER_OF_BINARIES enabled) + +if (VCPKG_TARGET_IS_OSX) + set(VCPKG_TARGET_ARCHITECTURE_ORIGINAL "${VCPKG_TARGET_ARCHITECTURE}") + set(VCPKG_OSX_ARCHITECTURES_ORIGINAL "${VCPKG_OSX_ARCHITECTURES}") + + # On MacOS we need to build twice and lipo the results together, since liboqs build system can't support multiple architectures in one build. + if (VCPKG_OSX_ARCHITECTURES_ORIGINAL MATCHES "x86_64") + set(VCPKG_TARGET_ARCHITECTURE "x64") + set(VCPKG_OSX_ARCHITECTURES "x86_64") + vcpkg_from_github( + OUT_SOURCE_PATH SOURCE_PATH + REPO open-quantum-safe/liboqs + REF ${VERSION} + SHA512 ${SHA512_VALUE} + HEAD_REF main + PATCHES 0000-macos-universal.patch + ) + + vcpkg_cmake_configure( + SOURCE_PATH "${SOURCE_PATH}" + OPTIONS + -DCMAKE_INSTALL_PREFIX="${CURRENT_PACKAGES_DIR}"/x64 + -DOQS_BUILD_ONLY_LIB=ON + -DOQS_DIST_BUILD=ON + -DOQS_USE_OPENSSL=OFF + ) + vcpkg_cmake_install() + vcpkg_cmake_config_fixup(CONFIG_PATH "lib/cmake/liboqs") + endif() + + if (VCPKG_OSX_ARCHITECTURES_ORIGINAL MATCHES "arm64") + # Cleans source path + set(VCPKG_TARGET_ARCHITECTURE "arm64") + set(VCPKG_OSX_ARCHITECTURES "arm64") + vcpkg_from_github( + OUT_SOURCE_PATH SOURCE_PATH + REPO open-quantum-safe/liboqs + REF ${VERSION} + SHA512 ${SHA512_VALUE} + HEAD_REF main + PATCHES 0000-macos-universal.patch + ) + + vcpkg_cmake_configure( + SOURCE_PATH "${SOURCE_PATH}" + OPTIONS + -DCMAKE_INSTALL_PREFIX="${CURRENT_PACKAGES_DIR}"/arm64 + -DOQS_BUILD_ONLY_LIB=ON + -DOQS_DIST_BUILD=ON + -DOQS_USE_OPENSSL=OFF + ) + vcpkg_cmake_install() + endif() + + file(MAKE_DIRECTORY "${CURRENT_PACKAGES_DIR}/lib/pkgconfig") + file(MAKE_DIRECTORY "${CURRENT_PACKAGES_DIR}/lib/cmake") + if (VCPKG_OSX_ARCHITECTURES_ORIGINAL MATCHES "arm64" AND VCPKG_OSX_ARCHITECTURES_ORIGINAL MATCHES "x86_64") + vcpkg_execute_build_process( + COMMAND "lipo" "${CURRENT_PACKAGES_DIR}/arm64/lib/liboqs.a" "${CURRENT_PACKAGES_DIR}/x64/lib/liboqs.a" -create -output liboqs.a + WORKING_DIRECTORY "${CURRENT_PACKAGES_DIR}/lib" + LOGNAME "lipo_${TARGET_TRIPLET}.log" + ) + file(RENAME "${CURRENT_PACKAGES_DIR}/x64/lib/pkgconfig/liboqs.pc" "${CURRENT_PACKAGES_DIR}/lib/pkgconfig/liboqs.pc") + file(RENAME "${CURRENT_PACKAGES_DIR}/x64/lib/cmake/liboqs" "${CURRENT_PACKAGES_DIR}/lib/cmake/liboqs") + file(RENAME "${CURRENT_PACKAGES_DIR}/x64/include" "${CURRENT_PACKAGES_DIR}/include") + file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/arm64") + file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/x64") + vcpkg_fixup_pkgconfig() + elseif (VCPKG_OSX_ARCHITECTURES_ORIGINAL MATCHES "arm64") + file(RENAME "${CURRENT_PACKAGES_DIR}/arm64/lib/pkgconfig/liboqs.pc" "${CURRENT_PACKAGES_DIR}/lib/pkgconfig/liboqs.pc") + file(RENAME "${CURRENT_PACKAGES_DIR}/arm64/lib/cmake/liboqs" "${CURRENT_PACKAGES_DIR}/lib/cmake/liboqs") + file(RENAME "${CURRENT_PACKAGES_DIR}/arm64/include" "${CURRENT_PACKAGES_DIR}/include") + file(RENAME "${CURRENT_PACKAGES_DIR}/arm64/lib/liboqs.a" "${CURRENT_PACKAGES_DIR}/lib/liboqs.a") + file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/arm64") + vcpkg_fixup_pkgconfig() + elseif (VCPKG_OSX_ARCHITECTURES_ORIGINAL MATCHES "x86_64") + file(RENAME "${CURRENT_PACKAGES_DIR}/x64/lib/pkgconfig/liboqs.pc" "${CURRENT_PACKAGES_DIR}/lib/pkgconfig/liboqs.pc") + file(RENAME "${CURRENT_PACKAGES_DIR}/x64/lib/cmake/liboqs" "${CURRENT_PACKAGES_DIR}/lib/cmake/liboqs") + file(RENAME "${CURRENT_PACKAGES_DIR}/x64/include" "${CURRENT_PACKAGES_DIR}/include") + file(RENAME "${CURRENT_PACKAGES_DIR}/x64/lib/liboqs.a" "${CURRENT_PACKAGES_DIR}/lib/liboqs.a") + file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/x64") + vcpkg_fixup_pkgconfig() + endif() + + vcpkg_cmake_config_fixup(CONFIG_PATH "lib/cmake/liboqs") + + # reset variables + set(VCPKG_TARGET_ARCHITECTURE ${VCPKG_TARGET_ARCHITECTURE_ORIGINAL}) + set(VCPKG_OSX_ARCHITECTURES ${VCPKG_OSX_ARCHITECTURES_ORIGINAL}) + +else() + vcpkg_from_github( + OUT_SOURCE_PATH SOURCE_PATH + REPO open-quantum-safe/liboqs + REF ${VERSION} + SHA512 ${SHA512_VALUE} + HEAD_REF main + ) + + vcpkg_cmake_configure( + SOURCE_PATH "${SOURCE_PATH}" + OPTIONS + -DBUILD_SHARED_LIBS=OFF + -DOQS_BUILD_ONLY_LIB=ON + -DOQS_DIST_BUILD=ON + -DOQS_USE_OPENSSL=ON + # This flag is required to build on Windows arm64 since it's not an officially supported platform + -DOQS_PERMIT_UNSUPPORTED_ARCHITECTURE=ON + ) + + vcpkg_cmake_install() + vcpkg_cmake_config_fixup(CONFIG_PATH "lib/cmake/liboqs") + vcpkg_fixup_pkgconfig() +endif() + +file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/include") + +file( + INSTALL "${SOURCE_PATH}/LICENSE.txt" + DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}" + RENAME copyright +) + diff --git a/tools/vcpkg/ports/liboqs/vcpkg.json b/tools/vcpkg/ports/liboqs/vcpkg.json new file mode 100644 index 000000000..979d2c409 --- /dev/null +++ b/tools/vcpkg/ports/liboqs/vcpkg.json @@ -0,0 +1,19 @@ +{ + "name": "liboqs", + "version": "0.10.0", + "port-version": 1, + "description": "liboqs is an open source C library for quantum-safe cryptographic algorithms.", + "homepage": "https://github.com/open-quantum-safe/liboqs", + "license": "MIT", + "dependencies": [ + { + "name": "vcpkg-cmake", + "host": true + }, + { + "name": "vcpkg-cmake-config", + "host": true + }, + "openssl" + ] +} diff --git a/tools/vcpkg/ports/oqsprovider/portfile.cmake b/tools/vcpkg/ports/oqsprovider/portfile.cmake new file mode 100644 index 000000000..d35c8c2f7 --- /dev/null +++ b/tools/vcpkg/ports/oqsprovider/portfile.cmake @@ -0,0 +1,23 @@ +vcpkg_from_github( + OUT_SOURCE_PATH SOURCE_PATH + REPO open-quantum-safe/oqs-provider + REF ${VERSION} + SHA512 059f86466261bbf5cb75793c465e16de7c1b1d1c2cb08c9d2a0eb1b48a18c74de1d28c19f2a9d955d8faef83e3902690a0a8d72ff7dd45778ee6e8b82249198a + HEAD_REF main +) + +vcpkg_cmake_configure( + SOURCE_PATH "${SOURCE_PATH}" +) + +vcpkg_replace_string("${SOURCE_PATH}/oqsprov/CMakeLists.txt" "\${OPENSSL_MODULES_PATH}" "\${CMAKE_INSTALL_PREFIX}/bin") +vcpkg_cmake_install() + +set(VCPKG_POLICY_DLLS_WITHOUT_LIBS enabled) +set(VCPKG_POLICY_EMPTY_INCLUDE_FOLDER enabled) + +file( + INSTALL "${SOURCE_PATH}/LICENSE.txt" + DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}" + RENAME copyright +) diff --git a/tools/vcpkg/ports/oqsprovider/vcpkg.json b/tools/vcpkg/ports/oqsprovider/vcpkg.json new file mode 100644 index 000000000..a9825f2d2 --- /dev/null +++ b/tools/vcpkg/ports/oqsprovider/vcpkg.json @@ -0,0 +1,20 @@ +{ + "name": "oqsprovider", + "version": "0.5.3", + "port-version": 1, + "description": "oqs-provider is an OpenSSL provider for the Open Quantum Safe project", + "homepage": "https://github.com/open-quantum-safe/oqs-provider", + "license": "MIT", + "dependencies": [ + { + "name": "vcpkg-cmake", + "host": true + }, + { + "name": "vcpkg-cmake-config", + "host": true + }, + "liboqs", + "openssl" + ] +} diff --git a/tools/vcpkg/ports/reproc/latest_changes.patch b/tools/vcpkg/ports/reproc/latest_changes.patch deleted file mode 100644 index 2fa040ba7..000000000 --- a/tools/vcpkg/ports/reproc/latest_changes.patch +++ /dev/null @@ -1,268 +0,0 @@ -diff --git a/.clang-tidy b/.clang-tidy -index 35bb58f..de92997 100644 ---- a/.clang-tidy -+++ b/.clang-tidy -@@ -34,6 +34,7 @@ Checks: - -performance-no-int-to-ptr, - -readability-else-after-return, - -readability-function-cognitive-complexity, -+-readability-identifier-length, - -readability-magic-numbers, - ' - HeaderFilterRegex: '.*reproc\+\+.*$' -diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml -index 4000454..3858f59 100644 ---- a/.github/workflows/main.yml -+++ b/.github/workflows/main.yml -@@ -74,7 +74,7 @@ jobs: - - name: Install (Windows) - if: runner.os == 'Windows' - run: | -- Invoke-Expression (New-Object System.Net.WebClient).DownloadString('https://get.scoop.sh') -+ iex "& {$(irm get.scoop.sh)} -RunAsAdmin" - scoop install ninja llvm --global - - if ("${{ matrix.compiler }}" -eq "gcc") { -diff --git a/reproc++/include/reproc++/reproc.hpp b/reproc++/include/reproc++/reproc.hpp -index ab6f139..f722245 100644 ---- a/reproc++/include/reproc++/reproc.hpp -+++ b/reproc++/include/reproc++/reproc.hpp -@@ -88,18 +88,18 @@ struct redirect { - - struct options { - struct { -- env::type behavior; -+ reproc::env::type behavior; - /*! Implicitly converts from any STL container of string pairs to the - environment format expected by `reproc_start`. */ -- class env extra; -+ reproc::env extra; - } env = {}; - - const char *working_directory = nullptr; - - struct { -- redirect in; -- redirect out; -- redirect err; -+ struct redirect in; -+ struct redirect out; -+ struct redirect err; - bool parent; - bool discard; - FILE *file; -@@ -138,30 +138,12 @@ enum class stream { - err, - }; - --class process; -- - namespace event { - --enum { -- in = 1 << 0, -- out = 1 << 1, -- err = 1 << 2, -- exit = 1 << 3, -- deadline = 1 << 4, --}; -- --struct source { -- class process &process; -- int interests; -- int events; --}; -+struct source; - - } - --REPROCXX_EXPORT std::error_code poll(event::source *sources, -- size_t num_sources, -- milliseconds timeout = infinite); -- - /*! Improves on reproc's API by adding RAII and changing the API of some - functions to be more idiomatic C++. */ - class process { -@@ -220,4 +202,26 @@ private: - std::unique_ptr impl_; - }; - -+namespace event { -+ -+enum { -+ in = 1 << 0, -+ out = 1 << 1, -+ err = 1 << 2, -+ exit = 1 << 3, -+ deadline = 1 << 4, -+}; -+ -+struct source { -+ class process process; -+ int interests; -+ int events; -+}; -+ -+} -+ -+REPROCXX_EXPORT std::error_code poll(event::source *sources, -+ size_t num_sources, -+ milliseconds timeout = infinite); -+ - } -diff --git a/reproc++/src/reproc.cpp b/reproc++/src/reproc.cpp -index e4eed1a..534e9fb 100644 ---- a/reproc++/src/reproc.cpp -+++ b/reproc++/src/reproc.cpp -@@ -86,8 +86,9 @@ std::pair process::fork(const options &options) noexcept - std::pair process::poll(int interests, - milliseconds timeout) - { -- event::source source{ *this, interests, 0 }; -+ event::source source{ std::move(*this), interests, 0 }; - std::error_code ec = ::reproc::poll(&source, 1, timeout); -+ *this = std::move(source.process); - return { source.events, ec }; - } - -diff --git a/reproc/CMakeLists.txt b/reproc/CMakeLists.txt -index 949cc88..1bb4798 100644 ---- a/reproc/CMakeLists.txt -+++ b/reproc/CMakeLists.txt -@@ -1,6 +1,6 @@ - if(WIN32) - set(REPROC_WINSOCK_LIBRARY ws2_32) --elseif(NOT APPLE) -+elseif(CMAKE_SYSTEM_NAME MATCHES Linux) - set(REPROC_RT_LIBRARY rt) # clock_gettime - endif() - -diff --git a/reproc/src/clock.windows.c b/reproc/src/clock.windows.c -index 3130f85..8c6c85a 100644 ---- a/reproc/src/clock.windows.c -+++ b/reproc/src/clock.windows.c -@@ -1,4 +1,8 @@ --#define _WIN32_WINNT _WIN32_WINNT_VISTA -+#ifndef _WIN32_WINNT -+ #define _WIN32_WINNT 0x0600 // _WIN32_WINNT_VISTA -+#elif _WIN32_WINNT < 0x0600 -+ #error "_WIN32_WINNT must be greater than _WIN32_WINNT_VISTA (0x0600)" -+#endif - - #include "clock.h" - -diff --git a/reproc/src/error.windows.c b/reproc/src/error.windows.c -index b8d8234..9459027 100644 ---- a/reproc/src/error.windows.c -+++ b/reproc/src/error.windows.c -@@ -1,4 +1,8 @@ --#define _WIN32_WINNT _WIN32_WINNT_VISTA -+#ifndef _WIN32_WINNT -+ #define _WIN32_WINNT 0x0600 // _WIN32_WINNT_VISTA -+#elif _WIN32_WINNT < 0x0600 -+ #error "_WIN32_WINNT must be greater than _WIN32_WINNT_VISTA (0x0600)" -+#endif - - #include "error.h" - -diff --git a/reproc/src/handle.windows.c b/reproc/src/handle.windows.c -index e0cd500..f0fbe56 100644 ---- a/reproc/src/handle.windows.c -+++ b/reproc/src/handle.windows.c -@@ -1,4 +1,8 @@ --#define _WIN32_WINNT _WIN32_WINNT_VISTA -+#ifndef _WIN32_WINNT -+ #define _WIN32_WINNT 0x0600 // _WIN32_WINNT_VISTA -+#elif _WIN32_WINNT < 0x0600 -+ #error "_WIN32_WINNT must be greater than _WIN32_WINNT_VISTA (0x0600)" -+#endif - - #include "handle.h" - -diff --git a/reproc/src/init.windows.c b/reproc/src/init.windows.c -index 8357b7c..52519bf 100644 ---- a/reproc/src/init.windows.c -+++ b/reproc/src/init.windows.c -@@ -1,4 +1,8 @@ --#define _WIN32_WINNT _WIN32_WINNT_VISTA -+#ifndef _WIN32_WINNT -+ #define _WIN32_WINNT 0x0600 // _WIN32_WINNT_VISTA -+#elif _WIN32_WINNT < 0x0600 -+ #error "_WIN32_WINNT must be greater than _WIN32_WINNT_VISTA (0x0600)" -+#endif - - #include "init.h" - -diff --git a/reproc/src/pipe.windows.c b/reproc/src/pipe.windows.c -index bb355be..befeaf1 100644 ---- a/reproc/src/pipe.windows.c -+++ b/reproc/src/pipe.windows.c -@@ -1,4 +1,8 @@ --#define _WIN32_WINNT _WIN32_WINNT_VISTA -+#ifndef _WIN32_WINNT -+ #define _WIN32_WINNT 0x0600 // _WIN32_WINNT_VISTA -+#elif _WIN32_WINNT < 0x0600 -+ #error "_WIN32_WINNT must be greater than _WIN32_WINNT_VISTA (0x0600)" -+#endif - - #include "pipe.h" - -diff --git a/reproc/src/process.posix.c b/reproc/src/process.posix.c -index 0f0fe0d..8dcbfd1 100644 ---- a/reproc/src/process.posix.c -+++ b/reproc/src/process.posix.c -@@ -17,6 +17,8 @@ - #include "pipe.h" - #include "strv.h" - -+#define CWD_BUF_SIZE_INCREMENT 4096 -+ - const pid_t PROCESS_INVALID = -1; - - static int signal_mask(int how, const sigset_t *newmask, sigset_t *oldmask) -@@ -51,7 +53,7 @@ static char *path_prepend_cwd(const char *path) - ASSERT(path); - - size_t path_size = strlen(path); -- size_t cwd_size = PATH_MAX; -+ size_t cwd_size = CWD_BUF_SIZE_INCREMENT; - - // We always allocate sufficient space for `path` but do not include this - // space in `cwd_size` so we can be sure that when `getcwd` succeeds there is -@@ -70,7 +72,7 @@ static char *path_prepend_cwd(const char *path) - return NULL; - } - -- cwd_size += PATH_MAX; -+ cwd_size += CWD_BUF_SIZE_INCREMENT; - - char *result = realloc(cwd, cwd_size + path_size + 1); - if (result == NULL) { -diff --git a/reproc/src/process.windows.c b/reproc/src/process.windows.c -index 666f3cb..6e28589 100644 ---- a/reproc/src/process.windows.c -+++ b/reproc/src/process.windows.c -@@ -1,4 +1,8 @@ --#define _WIN32_WINNT _WIN32_WINNT_VISTA -+#ifndef _WIN32_WINNT -+ #define _WIN32_WINNT 0x0600 // _WIN32_WINNT_VISTA -+#elif _WIN32_WINNT < 0x0600 -+ #error "_WIN32_WINNT must be greater than _WIN32_WINNT_VISTA (0x0600)" -+#endif - - #include "process.h" - -diff --git a/reproc/src/redirect.windows.c b/reproc/src/redirect.windows.c -index c634145..151f407 100644 ---- a/reproc/src/redirect.windows.c -+++ b/reproc/src/redirect.windows.c -@@ -1,4 +1,8 @@ --#define _WIN32_WINNT _WIN32_WINNT_VISTA -+#ifndef _WIN32_WINNT -+ #define _WIN32_WINNT 0x0600 // _WIN32_WINNT_VISTA -+#elif _WIN32_WINNT < 0x0600 -+ #error "_WIN32_WINNT must be greater than _WIN32_WINNT_VISTA (0x0600)" -+#endif - - #include "redirect.h" - diff --git a/tools/vcpkg/ports/reproc/portfile.cmake b/tools/vcpkg/ports/reproc/portfile.cmake deleted file mode 100644 index 02e8bed12..000000000 --- a/tools/vcpkg/ports/reproc/portfile.cmake +++ /dev/null @@ -1,33 +0,0 @@ -vcpkg_from_github( - OUT_SOURCE_PATH SOURCE_PATH - REPO DaanDeMeyer/reproc - REF v14.2.4 - SHA512 c592521960f1950d626261738091d25efdf764ee1a0c72a58c28c66eaebf6073b2c978f1dc2c8dbe89b0be7ec1629a3a45cb1fafa0ebe21b5df8d4d27c992675 - HEAD_REF main - PATCHES - latest_changes.patch -) - -vcpkg_cmake_configure( - SOURCE_PATH "${SOURCE_PATH}" - OPTIONS - -DREPROC++=ON - -DREPROC_INSTALL_PKGCONFIG=OFF - -DREPROC_INSTALL_CMAKECONFIGDIR=share -) - -vcpkg_cmake_install() - -file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/include") - -foreach(TARGET reproc reproc++) - vcpkg_cmake_config_fixup( - PACKAGE_NAME ${TARGET} - ) -endforeach() - -file( - INSTALL "${SOURCE_PATH}/LICENSE" - DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}" - RENAME copyright -) diff --git a/tools/vcpkg/ports/reproc/readme.txt b/tools/vcpkg/ports/reproc/readme.txt deleted file mode 100644 index 0e9eab4f4..000000000 --- a/tools/vcpkg/ports/reproc/readme.txt +++ /dev/null @@ -1 +0,0 @@ -Applied the latest changes for the reproc library which are not included in the v14.2.4 \ No newline at end of file diff --git a/tools/vcpkg/ports/reproc/vcpkg.json b/tools/vcpkg/ports/reproc/vcpkg.json deleted file mode 100644 index 4326597ed..000000000 --- a/tools/vcpkg/ports/reproc/vcpkg.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "name": "reproc", - "version": "14.2.4", - "port-version": 2, - "description": "Cross-platform (C99/C++11) process library", - "homepage": "https://github.com/DaanDeMeyer/reproc", - "supports": "!uwp", - "dependencies": [ - { - "name": "vcpkg-cmake", - "host": true - }, - { - "name": "vcpkg-cmake-config", - "host": true - } - ] -} diff --git a/tools/vcpkg/ports/scapix-bin/CMakeLists.txt b/tools/vcpkg/ports/scapix-bin/CMakeLists.txt new file mode 100644 index 000000000..12ea87ad4 --- /dev/null +++ b/tools/vcpkg/ports/scapix-bin/CMakeLists.txt @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 3.19) + +project(scapix-bin LANGUAGES C CXX) + +set(SCAPIX_EXE ${SCAPIX_EXE_PATH} CACHE PATH "Location of scapix executables") + + +include(CMakePackageConfigHelpers) +configure_package_config_file( + "${CMAKE_CURRENT_SOURCE_DIR}/scapix-bin-config.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/scapix-bin-config.cmake" + INSTALL_DESTINATION "share/scapix-bin" + PATH_VARS SCAPIX_EXE +) +install( + FILES "${CMAKE_CURRENT_BINARY_DIR}/scapix-bin-config.cmake" + DESTINATION "share/scapix-bin" +) diff --git a/tools/vcpkg/ports/scapix-bin/portfile.cmake b/tools/vcpkg/ports/scapix-bin/portfile.cmake new file mode 100644 index 000000000..ccaa30b0e --- /dev/null +++ b/tools/vcpkg/ports/scapix-bin/portfile.cmake @@ -0,0 +1,55 @@ +set(VCPKG_POLICY_EMPTY_INCLUDE_FOLDER enabled) + +if(VCPKG_HOST_IS_OSX) + execute_process(COMMAND "uname" "-m" OUTPUT_VARIABLE HOST_ARCH OUTPUT_STRIP_TRAILING_WHITESPACE) +elseif(VCPKG_HOST_IS_WINDOWS) + set(HOST_ARCH "AMD64") +elseif(VCPKG_HOST_IS_LINUX) + set(HOST_ARCH "x86_64") +else() + message(FATAL_ERROR "Unsupported platform.") +endif() + +set(scapix_bin_hash_Darwin-arm64 49062f8dde6fac836b478b386c7d724be3f18ca8a84e4fe538b11a36d081772d34e7f9db7d218837f10e08a6a6f926e7ec7fe56a34c5bb58b473209e490fe94b) +set(scapix_bin_hash_Darwin-x86_64 3288a54ca6555223768241c3a731f53a770700c7cd2601067f209a0c14e3400c933359449ceeb679608eb2a8cba2c463b50e4559fc27767ff010c5a015df072a) +set(scapix_bin_hash_Linux-x86_64 b526df3bef8bf07a868a7ae2d6bf239599103abcc84551b681ddf6fe6c13bbab0d1b6abda6a910847aa0f143d4f802fd37c7c586952bbd1f273a9f2b154dab77) +set(scapix_bin_hash_Windows-AMD64 2047c7119022fee6236ba3d19113854ac7b0b42e3f663075f78fad27df9a1198c24740c92d89ae4840060e2d0341e31114fdf9509b8a51f0cf230287d24ab4fd) + +vcpkg_download_distfile(ARCHIVE + URLS "https://github.com/scapix-com/scapix-bin/archive/refs/tags/v${VERSION}-${CMAKE_HOST_SYSTEM_NAME}-${HOST_ARCH}.tar.gz" + FILENAME "${PORT}_${VERSION}.tar.gz" + SHA512 ${scapix_bin_hash_${CMAKE_HOST_SYSTEM_NAME}-${HOST_ARCH}} +) + +vcpkg_extract_source_archive(SOURCE_PATH + ARCHIVE "${ARCHIVE}" + SOURCE_BASE "scapix_bin" +) + +file(COPY "${CMAKE_CURRENT_LIST_DIR}/CMakeLists.txt" DESTINATION "${SOURCE_PATH}") +file(COPY "${CMAKE_CURRENT_LIST_DIR}/scapix-bin-config.cmake.in" DESTINATION "${SOURCE_PATH}") + +vcpkg_cmake_configure( + SOURCE_PATH "${SOURCE_PATH}" + OPTIONS + -DSCAPIX_EXE_PATH=${CURRENT_PACKAGES_DIR}/tools/${PORT}/${CMAKE_HOST_SYSTEM_NAME}-${HOST_ARCH} +) +vcpkg_cmake_install() +vcpkg_cmake_config_fixup() +file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug") + +vcpkg_copy_tools(TOOL_NAMES scapix scapix_java + SEARCH_DIR ${SOURCE_PATH}/${CMAKE_HOST_SYSTEM_NAME}-${HOST_ARCH} + DESTINATION ${CURRENT_PACKAGES_DIR}/tools/${PORT}/${CMAKE_HOST_SYSTEM_NAME}-${HOST_ARCH} + AUTO_CLEAN) + +file(INSTALL "${SOURCE_PATH}/lib" DESTINATION "${CURRENT_PACKAGES_DIR}/tools/${PORT}") + + +vcpkg_download_distfile(LICENSE_PATH + URLS "https://raw.githubusercontent.com/scapix-com/scapix/master/LICENSE.txt" + FILENAME "SCAPIX_LICENSE" + SKIP_SHA512 +) + +file(INSTALL "${LICENSE_PATH}" DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}" RENAME copyright) \ No newline at end of file diff --git a/tools/vcpkg/ports/scapix-bin/scapix-bin-config.cmake.in b/tools/vcpkg/ports/scapix-bin/scapix-bin-config.cmake.in new file mode 100644 index 000000000..438e395d3 --- /dev/null +++ b/tools/vcpkg/ports/scapix-bin/scapix-bin-config.cmake.in @@ -0,0 +1,15 @@ +@PACKAGE_INIT@ + +set_and_check(SCAPIX_EXE_PATH "@PACKAGE_SCAPIX_EXE@") + +set(SCAPIX_EXE "${SCAPIX_EXE_PATH}/scapix" CACHE INTERNAL "") + +if(NOT EXISTS "${SCAPIX_EXE}") + set(SCAPIX_EXE "${SCAPIX_EXE}.exe" CACHE INTERNAL "") +endif() + +set(SCAPIX_JAVA_EXE "${SCAPIX_EXE_PATH}/scapix_java" CACHE INTERNAL "") + +if(NOT EXISTS "${SCAPIX_JAVA_EXE}") + set(SCAPIX_JAVA_EXE "${SCAPIX_JAVA_EXE}.exe" CACHE INTERNAL "") +endif() diff --git a/tools/vcpkg/ports/scapix-bin/vcpkg.json b/tools/vcpkg/ports/scapix-bin/vcpkg.json new file mode 100644 index 000000000..fd8faee76 --- /dev/null +++ b/tools/vcpkg/ports/scapix-bin/vcpkg.json @@ -0,0 +1,17 @@ +{ + "name": "scapix-bin", + "version": "2.0.16", + "port-version": 0, + "description": "Binary files used by Scapix Language Bridge project.", + "homepage": "https://github.com/scapix-com/scapix-bin", + "dependencies": [ + { + "name": "vcpkg-cmake", + "host": true + }, + { + "name": "vcpkg-cmake-config", + "host": true + } + ] +} diff --git a/tools/vcpkg/ports/scapix/CMakeLists.txt b/tools/vcpkg/ports/scapix/CMakeLists.txt new file mode 100644 index 000000000..5f34d73ea --- /dev/null +++ b/tools/vcpkg/ports/scapix/CMakeLists.txt @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 3.19) + +project(scapix LANGUAGES C CXX) + +include(CMakePackageConfigHelpers) +configure_package_config_file( + "${CMAKE_CURRENT_SOURCE_DIR}/scapix-config.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/scapix-config.cmake" + INSTALL_DESTINATION "share/scapix" +) +install( + FILES "${CMAKE_CURRENT_BINARY_DIR}/scapix-config.cmake" + DESTINATION "share/scapix" +) diff --git a/tools/vcpkg/ports/scapix/fix-project.patch b/tools/vcpkg/ports/scapix/fix-project.patch new file mode 100644 index 000000000..843068511 --- /dev/null +++ b/tools/vcpkg/ports/scapix/fix-project.patch @@ -0,0 +1,28 @@ +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 683502c..b6d7973 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.14...3.26) + + project(scapix CXX) + +-find_package(ScapixBin REQUIRED) ++find_package(scapix-bin CONFIG REQUIRED) + + set(SCAPIX_BRIDGE "cpp" CACHE STRING "cpp, java, objc, python, js, cs") + set(SCAPIX_JAVA_API "jdk-11.0.2" CACHE STRING "subfolder of 'scapix/java_api' folder: jdk-11.0.2, android-28, etc.") +@@ -74,8 +74,12 @@ target_include_directories(scapix PUBLIC ${_SCAPIX_PATH}/java_api/${SCAPIX_JAVA_ + target_compile_definitions(scapix PUBLIC SCAPIX_BRIDGE=${SCAPIX_BRIDGE} SCAPIX_BRIDGE_${SCAPIX_BRIDGE}) + + if(${SCAPIX_BRIDGE} STREQUAL java) +- find_package(Boost REQUIRED config metaparse) +- target_link_libraries(scapix PUBLIC Boost::config Boost::metaparse) ++ find_package(Boost REQUIRED) ++ if(Boost_FOUND) ++ include_directories(${Boost_INCLUDE_DIRS}) ++ else() ++ message(STATUS "Boost NOT Found !") ++ endif(Boost_FOUND) + endif() + + if(${SCAPIX_BRIDGE} STREQUAL java AND NOT ANDROID) diff --git a/tools/vcpkg/ports/scapix/portfile.cmake b/tools/vcpkg/ports/scapix/portfile.cmake new file mode 100644 index 000000000..e796181f6 --- /dev/null +++ b/tools/vcpkg/ports/scapix/portfile.cmake @@ -0,0 +1,34 @@ +set(VCPKG_POLICY_EMPTY_INCLUDE_FOLDER enabled) + +vcpkg_download_distfile(ARCHIVE + URLS "https://github.com/scapix-com/scapix/archive/refs/tags/v${VERSION}.tar.gz" + FILENAME "scapix_${VERSION}.tar.gz" + SHA512 89669a4c04c35e6c4f218917adf3d34fd6e45f541f2a2b2050d9dc7b6e7d96c7bb14c59d23b1db01dd5fcc8c30422a3c99f4c45c9134d91fecab09179d9da585 +) +vcpkg_extract_source_archive(SOURCE_PATH + ARCHIVE "${ARCHIVE}" + SOURCE_BASE "scapix" + PATCHES + fix-project.patch +) + +file(COPY "${CMAKE_CURRENT_LIST_DIR}/CMakeLists.txt" DESTINATION "${SOURCE_PATH}/config") +file(COPY "${CMAKE_CURRENT_LIST_DIR}/scapix-config.cmake.in" DESTINATION "${SOURCE_PATH}/config") + +vcpkg_cmake_configure( + SOURCE_PATH "${SOURCE_PATH}/config" +) +vcpkg_cmake_install() +vcpkg_cmake_config_fixup() + +file(INSTALL "${SOURCE_PATH}/" DESTINATION "${CURRENT_PACKAGES_DIR}/src/${PORT}") + +# install license +vcpkg_download_distfile(LICENSE_PATH + URLS "https://raw.githubusercontent.com/scapix-com/scapix/master/LICENSE.txt" + FILENAME "SCAPIX_LICENSE" + SKIP_SHA512 +) +file(INSTALL "${LICENSE_PATH}" DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}" RENAME copyright) + +file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug") diff --git a/tools/vcpkg/ports/scapix/scapix-config.cmake.in b/tools/vcpkg/ports/scapix/scapix-config.cmake.in new file mode 100644 index 000000000..f1e1c9eea --- /dev/null +++ b/tools/vcpkg/ports/scapix/scapix-config.cmake.in @@ -0,0 +1,4 @@ +@PACKAGE_INIT@ + +set_and_check(SCAPIX_SRC "${PACKAGE_PREFIX_DIR}/src/scapix") +add_subdirectory(${SCAPIX_SRC} scapix) \ No newline at end of file diff --git a/tools/vcpkg/ports/scapix/vcpkg.json b/tools/vcpkg/ports/scapix/vcpkg.json new file mode 100644 index 000000000..19c481a61 --- /dev/null +++ b/tools/vcpkg/ports/scapix/vcpkg.json @@ -0,0 +1,18 @@ +{ + "name": "scapix", + "version": "1.0.39", + "port-version": 0, + "description": "Scapix Language Bridge.", + "homepage": "https://github.com/scapix-com/scapix", + "dependencies": [ + { + "name": "vcpkg-cmake", + "host": true + }, + { + "name": "vcpkg-cmake-config", + "host": true + }, + "scapix-bin" + ] +} diff --git a/tools/vcpkg/vcpkg.json b/tools/vcpkg/vcpkg.json index aaac33b27..6e351491c 100644 --- a/tools/vcpkg/vcpkg.json +++ b/tools/vcpkg/vcpkg.json @@ -6,9 +6,9 @@ "boost-serialization", "boost-thread", "boost-process", - "bshoshany-thread-pool", "openssl", "openvpn", + "oqsprovider", "gtest", "spdlog", { @@ -19,7 +19,6 @@ "skyr-url", "cmakerc", "cpp-base64", - "reproc", "c-ares", "advobfuscator", "7zip",