diff --git a/app/commands.py b/app/commands.py index 1e02ec2..6d4b3a3 100644 --- a/app/commands.py +++ b/app/commands.py @@ -10,14 +10,15 @@ def seed_defaults(): "butterbox_logo": current_app.config["BUTTERBOX_LOGO"], "ssid": current_app.config["BUTTERBOX_SSID"], "wifi_password": current_app.config["BUTTERBOX_WIFI_PASSWORD"], - "disable_access_point": current_app.config["DISABLE_ACCESS_POINT"], + "enable_access_point": current_app.config["ENABLE_ACCESS_POINT"], "apply_changes": "false", "onboarding_complete": "false", "lock_root_password": "false", - "disable_file_viewer": current_app.config["DISABLE_FILE_VIEWER"], - "disable_map_viewer": current_app.config["DISABLE_MAP_VIEWER"], - "disable_chat": current_app.config["DISABLE_CHAT"], - "disable_app_store": current_app.config["DISABLE_APP_STORE"], + "enable_file_viewer": current_app.config["ENABLE_FILE_VIEWER"], + "enable_map_viewer": current_app.config["ENABLE_MAP_VIEWER"], + "enable_chat": current_app.config["ENABLE_CHAT"], + "enable_app_store": current_app.config["ENABLE_APP_STORE"], + "enable_deltachat": current_app.config["ENABLE_DELTACHAT"], "ssh_password": "", "admin_password": current_app.config["ADMIN_PASSWORD"], } diff --git a/app/forms.py b/app/forms.py index 43548bd..72cf5e5 100644 --- a/app/forms.py +++ b/app/forms.py @@ -14,16 +14,17 @@ class LoginForm(FlaskForm): class SettingsForm(FlaskForm): # Access point settings ssid = StringField('SSID', validators=[DataRequired()]) - wifi_password = PasswordField(_l('WiFi Password')) - disable_access_point = BooleanField(_l('Disable Access Point')) + wifi_password = StringField(_l('WiFi Password')) + enable_access_point = BooleanField(_l('Enable Access Point')) # Customisation settings butterbox_name = StringField(_l('Butterbox Name'), validators=[DataRequired()]) butterbox_logo = FileField((_l('Butterbox Logo')), validators=[FileAllowed(['jpg', 'png', 'svg'], 'Images only!')]) # Services settings - disable_file_viewer = BooleanField(_l('Disable File Viewer')) - disable_map_viewer = BooleanField(_l('Disable Map Viewer')) - disable_chat = BooleanField(_l('Disable Chat')) - disable_app_store = BooleanField(_l('Disable App Store')) + enable_file_viewer = BooleanField(_l('Enable File Viewer')) + enable_map_viewer = BooleanField(_l('Enable Map Viewer')) + enable_chat = BooleanField(_l('Enable Chat')) + enable_app_store = BooleanField(_l('Enable App Store')) + enable_deltachat = BooleanField(_l('Enable DeltaChat')) # Access Settings admin_password = PasswordField(_l('Admin Password')) ssh_password = PasswordField(_l('SSH Password')) diff --git a/app/routes.py b/app/routes.py index f715551..11aefdc 100644 --- a/app/routes.py +++ b/app/routes.py @@ -1,18 +1,65 @@ -from email.mime import image - -from alembic.util import obfuscate_url_pw - from app import app -from flask import render_template, flash, redirect, url_for, request, session +from flask import render_template, flash, redirect, url_for, request, session, send_file from app.forms import LoginForm, SettingsForm from flask_login import login_user, current_user, logout_user, login_required import sqlalchemy as sa from app import db from app.models import User, Setting -from werkzeug.datastructures import FileStorage +import os import json from flask_babel import _ import base64 +import random +from wordfreq import top_n_list +import secrets +import string +import glob +import time +import qrcode +from datetime import datetime + +def gen_username() -> str: + words = top_n_list("en", 5000) + prefix = random.randint(1000, 9999) + return f"{random.choice(words)}{random.choice(words)}{prefix}" + +def gen_password() -> str: + characters = string.ascii_letters + string.digits + password = ''.join(secrets.choice(characters) for i in range(20)) + return password +def get_file_suffix(path: str) -> str: + return os.path.splitext(path)[1].strip('.') # in case of multiple exts, like .tar.gz, it only splits on the last one + +def get_file_icon_url(path: str) -> str: + if os.path.isdir(path): + return url_for("static", filename=f"images/extension-icons/directory.svg") + + suffix = get_file_suffix(path) + if suffix: + if suffix in ['apk', 'deb', 'dmg', 'exe', 'jpg', 'mp3', 'pbf', 'pdf', 'png', + 'ppt', 'pptx']: + return url_for("static", filename=f"images/extension-icons/ext-{suffix}.svg") + return url_for("static", filename=f"images/extension-icons/ext-unknown.svg") +def get_last_modified(path: str) -> str: + mod_time = time.ctime(os.path.getmtime(path)) + #print(mod_time) + #dt = datetime.strptime(mod_time, "%a %b %d %H:%M:%S %Y") + #formatted_time = dt.strftime("%Y-%m-%d %H:%M") + return mod_time + +def get_files_in_path(path: str): + file_list = [] + if os.path.exists(path): + list_of_files = glob.glob(path + "/*", recursive=True) + file_list = [ + {"name": x.replace(path, "").strip("/"), "path": x, "is_file": os.path.isfile(x), + "is_dir": os.path.isdir(x), + "filetype": get_file_suffix(x), "last_modified": get_last_modified(x), + "icon_url": get_file_icon_url(x), + "relative_path": x.replace(app.config["BUTTERBOX_USB_PATH"], "")} for x + in list_of_files] + print(file_list) + return file_list def get_setting(name) -> str: setting = db.session.scalar(sa.select(Setting).where(Setting.key == name)) @@ -20,7 +67,6 @@ def get_setting(name) -> str: def set_setting(name: str, value: str): setting = db.session.scalar(sa.select(Setting).where(Setting.key == name)) - print(f"I have changed {setting.key}") setting.value = value db.session.add(setting) @@ -33,33 +79,67 @@ def dump_settings(filename: str) -> None: @app.route('/') @app.route('/index') def index(): - disable_chat = get_setting("disable_chat") - disable_app_store = get_setting("disable_app_store") - disable_map_viewer = get_setting("disable_map_viewer") - disable_file_viewer = get_setting("disable_file_viewer") + display_wifi_password = False + wifi_password = get_setting("wifi_password") + if wifi_password: + wifi_ssid = get_setting("ssid") + wifi_encryption_type = "WPA2" + img = qrcode.make(f"WIFI:T:{wifi_encryption_type};S:{wifi_ssid};P:{wifi_password};;") + img.save("app/static/images/wifi_qr_code.png") + display_wifi_password = True + else: + os.remove("app/static/images/wifi_qr_code.png") + enable_chat = get_setting("enable_chat") + enable_app_store = get_setting("enable_app_store") + enable_map_viewer = get_setting("enable_map_viewer") + enable_file_viewer = get_setting("enable_file_viewer") + enable_deltachat = get_setting("enable_deltachat") service_array = [] usb_inserted = False # actual test of whether USB is inserted + if os.path.exists(app.config["BUTTERBOX_USB_PATH"]): + usb_inserted = True usb_has_maps = False # actual test of whether USB has maps folder usb_has_appstore = False # actual test of whether USB has an appstore - if disable_chat == 'false': + if enable_deltachat == 'true': + service_array.append({"name": "Secure Messaging", "image": url_for("static", filename="images/deltachat-icon.png"), "url": url_for("messaging") }) + if enable_chat == 'true': service_array.append({"name": "Message Board", "image": url_for("static", filename="images/chat-icon.png"), "url": app.config["CONVENE_INSTALL_PATH"] }) - if disable_app_store == 'false' and usb_has_appstore: + if enable_app_store == 'true' and usb_has_appstore: service_array.append({"name": "Apps", "image": url_for("static", filename="images/appstore-icon.svg")}) - if disable_map_viewer == 'false' and usb_has_maps: + if enable_map_viewer == 'true' and usb_has_maps: service_array.append({"name": "Offline Maps", "image": url_for("static", filename="images/maps-icon.png")}) - if disable_file_viewer == 'false': + if enable_file_viewer == 'true': name = "Files" if not usb_inserted: name = "Insert USB to browse files" service_array.append({ "name": name, "image": url_for("static", filename="images/explore-icon.svg"), - "url": url_for("usb")}) - return render_template('index.html', title='Home', get_setting=get_setting, services=service_array) + "url": url_for("files", path=""),}) + return render_template('index.html', title='Home', get_setting=get_setting, services=service_array, display_wifi_password=display_wifi_password) -@app.route('/usb') -def usb(): - return render_template('usb-file-viewer.html', title='File Viewer') +@app.route('/files/', defaults={'path': ''}) +@app.route('/files/') +def files(path): + base_path = app.config["BUTTERBOX_USB_PATH"] + if not os.path.exists(base_path): + flash(_('No USB inserted.')) + current_path = os.path.join(base_path, path) + render_files = [] + if os.path.exists(current_path): + render_files = get_files_in_path(current_path) + if not render_files: + flash(_('Empty directory.')) + else: + flash(_('Path does not exist.')) + return render_template('usb-file-viewer.html', title='File Viewer', current_path=current_path, render_files=render_files, get_setting=get_setting) + +@app.route('/serve_file/') +def serve_file(filepath): + full_path = os.path.realpath(filepath) + if not filepath.startswith(app.config["BUTTERBOX_USB_PATH"]): + redirect(url_for('files', path="")) + return send_file(full_path) @app.route('/login', methods=['GET', 'POST']) def login(): @@ -85,9 +165,9 @@ def logout(): @login_required def admin(): form = SettingsForm() - populate_settings = ['butterbox_name', 'wifi_password', 'disable_access_point', 'ssid', 'disable_file_viewer', 'disable_map_viewer', 'disable_app_store', 'disable_chat' ] + populate_settings = ['butterbox_name', 'wifi_password', 'enable_access_point', 'ssid', 'enable_file_viewer', 'enable_map_viewer', 'enable_app_store', 'enable_chat', 'enable_deltachat' ] - bool_settings = ['disable_access_point','disable_file_viewer', 'disable_map_viewer', 'disable_app_store', 'disable_chat'] + bool_settings = ['enable_access_point','enable_file_viewer', 'enable_map_viewer', 'enable_app_store', 'enable_chat', 'enable_deltachat'] if not form.is_submitted(): for s in populate_settings: @@ -95,7 +175,7 @@ def admin(): getattr(form, s).data = (get_setting(s) == "true") else: getattr(form, s).data = get_setting(s) - + non_admin_settings_changed = False if form.validate_on_submit(): if form.submit.data: for s in populate_settings: @@ -106,7 +186,8 @@ def admin(): if new_value != existing_value: print(f"New value was changed for {s}. Existing value was {existing_value}, new value was {new_value}") set_setting(s, new_value) - if s in ['butterbox_name', 'wifi_password', 'ssid', 'disable_access_point']: + non_admin_settings_changed = True + if s in ['butterbox_name', 'wifi_password', 'ssid', 'enable_access_point', 'enable_chat', 'enable_delta_chat']: app.config['SETTINGS_CHANGED'] = True new_logo = form.butterbox_logo.data @@ -117,18 +198,21 @@ def admin(): new_value = f"data:{file_mimetype};base64,{b64_logo}" existing_value = get_setting('butterbox_logo') if new_value != existing_value: - print( f"New value was changed for logo") set_setting('butterbox_logo', new_value) + non_admin_settings_changed = True new_admin_password = form.admin_password.data if new_admin_password: existing_admin_password = get_setting('admin_password') if new_admin_password != existing_admin_password: - print( f"New value was changed for admin password") set_setting('admin_password', new_admin_password) - print(get_setting('admin_password')) + non_admin_settings_changed = True if app.config['SETTINGS_CHANGED']: - flash(_("⚠️ Some settings won't take effect until the Butter Box restarts. Click 'Apply Changes' to restart.")) + flash(_("⚠️ Some settings won't fully take effect until the Butter Box restarts. Click 'Apply Changes' to restart.")) + else: + if non_admin_settings_changed: + flash( + _("Settings successfully changed.")) db.session.commit() if form.apply_changes.data: @@ -136,5 +220,20 @@ def admin(): dump_settings("settings.txt") flash(_("⚠️ Changes applied! Please wait for the box to restart.")) - return render_template('admin.html', get_setting=get_setting, form=form) + + +@app.route('/messaging', methods=['GET', 'POST']) +def messaging(): + return render_template('messaging.html', get_setting=get_setting) + +@app.route("/deltachat_credentials", methods=["POST"]) +def generate_random_deltachat_credentials(): + ip = app.config['BUTTERBOX_DEFAULT_IP'] + username = gen_username() + password = gen_password() + + flash(f"Username: {username}") + flash(f"Password: {password}") + flash(f"IP: {ip}") + return redirect(url_for("messaging")) \ No newline at end of file diff --git a/app/static/DeltaChat_2.35.0_APKPure.apk b/app/static/DeltaChat_2.35.0_APKPure.apk new file mode 100644 index 0000000..94b5a3a Binary files /dev/null and b/app/static/DeltaChat_2.35.0_APKPure.apk differ diff --git a/app/static/butter_styles.css b/app/static/butter_styles.css index c45feec..d7e2e4a 100644 --- a/app/static/butter_styles.css +++ b/app/static/butter_styles.css @@ -18,4 +18,11 @@ html { padding: 10px; } +} +@media (max-width: 468px) { + + .file-viewer-date-modified { + display: none; + } + } \ No newline at end of file diff --git a/app/static/images/deltachat-icon.png b/app/static/images/deltachat-icon.png new file mode 100644 index 0000000..0715643 Binary files /dev/null and b/app/static/images/deltachat-icon.png differ diff --git a/app/static/images/extension-icons/directory.svg b/app/static/images/extension-icons/directory.svg new file mode 100644 index 0000000..897a905 --- /dev/null +++ b/app/static/images/extension-icons/directory.svg @@ -0,0 +1,4 @@ + + + + diff --git a/app/static/images/extension-icons/ext-apk.svg b/app/static/images/extension-icons/ext-apk.svg new file mode 100644 index 0000000..89ef6bc --- /dev/null +++ b/app/static/images/extension-icons/ext-apk.svg @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/app/static/images/extension-icons/ext-deb.svg b/app/static/images/extension-icons/ext-deb.svg new file mode 100644 index 0000000..6aa2f95 --- /dev/null +++ b/app/static/images/extension-icons/ext-deb.svg @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/app/static/images/extension-icons/ext-dmg.svg b/app/static/images/extension-icons/ext-dmg.svg new file mode 100644 index 0000000..52c8da0 --- /dev/null +++ b/app/static/images/extension-icons/ext-dmg.svg @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/app/static/images/extension-icons/ext-exe.svg b/app/static/images/extension-icons/ext-exe.svg new file mode 100644 index 0000000..7014d94 --- /dev/null +++ b/app/static/images/extension-icons/ext-exe.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/static/images/extension-icons/ext-jpg.svg b/app/static/images/extension-icons/ext-jpg.svg new file mode 100644 index 0000000..31a1c39 --- /dev/null +++ b/app/static/images/extension-icons/ext-jpg.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/static/images/extension-icons/ext-mp3.svb b/app/static/images/extension-icons/ext-mp3.svb new file mode 100644 index 0000000..ed31bce --- /dev/null +++ b/app/static/images/extension-icons/ext-mp3.svb @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/static/images/extension-icons/ext-pbf.svg b/app/static/images/extension-icons/ext-pbf.svg new file mode 100644 index 0000000..de2298d --- /dev/null +++ b/app/static/images/extension-icons/ext-pbf.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/static/images/extension-icons/ext-pdf.svg b/app/static/images/extension-icons/ext-pdf.svg new file mode 100644 index 0000000..6ecc493 --- /dev/null +++ b/app/static/images/extension-icons/ext-pdf.svg @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/app/static/images/extension-icons/ext-png.svg b/app/static/images/extension-icons/ext-png.svg new file mode 100644 index 0000000..31a1c39 --- /dev/null +++ b/app/static/images/extension-icons/ext-png.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/static/images/extension-icons/ext-ppt.svg b/app/static/images/extension-icons/ext-ppt.svg new file mode 100644 index 0000000..a51103b --- /dev/null +++ b/app/static/images/extension-icons/ext-ppt.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/static/images/extension-icons/ext-pptx.svg b/app/static/images/extension-icons/ext-pptx.svg new file mode 100644 index 0000000..a51103b --- /dev/null +++ b/app/static/images/extension-icons/ext-pptx.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/static/images/extension-icons/ext-unknown.svg b/app/static/images/extension-icons/ext-unknown.svg new file mode 100644 index 0000000..287dd02 --- /dev/null +++ b/app/static/images/extension-icons/ext-unknown.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/templates/admin.html b/app/templates/admin.html index 2ed362e..9523433 100644 --- a/app/templates/admin.html +++ b/app/templates/admin.html @@ -1,13 +1,16 @@ {% extends "base.html" %} {% block content %} -

{{ _('Application Settings') }}

+

{{ _('Application Settings') }}

+ {% import "bulma_wtf.html" as wtf %}
{{ form.hidden_tag() }} - {% if config['SETTINGS_CHANGED'] %} -

{{ form.apply_changes(class="button is-warning") }}

- {% endif %} +

{{ form.submit( class="button is-link") }} + {% if config['SETTINGS_CHANGED'] %} + {{ form.apply_changes(class="button is-warning") }} + {% endif %} +

{{ wtf.form_input_field(form.ssid) }}

This is the name of the advertised Wi-Fi network. Current SSID: {{ get_setting('ssid') }}

@@ -23,23 +26,27 @@ Current name: {{ get_setting('butterbox_name') }}, accessed at {{ get_setting('butterbox_name') }}.local.

- {{ wtf.form_bool_field(form.disable_access_point) }} + {{ wtf.form_bool_field(form.enable_access_point) }}

Whether this box will advertise a WiFi network.

- {{ wtf.form_bool_field(form.disable_map_viewer) }} + {{ wtf.form_bool_field(form.enable_map_viewer) }}

Whether map services are enabled.

- {{ wtf.form_bool_field(form.disable_chat) }} + {{ wtf.form_bool_field(form.enable_chat) }}

Whether chat services are enabled.

- {{ wtf.form_bool_field(form.disable_file_viewer) }} + {{ wtf.form_bool_field(form.enable_deltachat) }} +

Whether secure messaging using DeltaChat is enabled.

+
+
+ {{ wtf.form_bool_field(form.enable_file_viewer) }}

Whether files services via USB are enabled.

- {{ wtf.form_bool_field(form.disable_app_store) }} + {{ wtf.form_bool_field(form.enable_app_store) }}

Whether app store services are enabled.

@@ -55,7 +62,7 @@

This is the logo shown in the UI. Current logo:

-

{{ form.submit( class="button is-link") }}

+
Logout diff --git a/app/templates/base.html b/app/templates/base.html index c869fc7..47d81e2 100644 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -19,7 +19,7 @@

{{ get_setting('butterbox_name') }}

-
{% block content %}{% endblock %}
+
{% with messages = get_flashed_messages() %} {% if messages %}
@@ -31,6 +31,9 @@
{% endif %} {% endwith %} + + {% block content %}{% endblock %} + diff --git a/app/templates/index.html b/app/templates/index.html index c258b68..b995f90 100644 --- a/app/templates/index.html +++ b/app/templates/index.html @@ -2,7 +2,8 @@ {% block content %}

Hi, welcome to the {{get_setting('butterbox_name')}}.

-

View and download the information you want from this offline box.

+

View and download the information you want from this offline box.

+
{% for service in services %} @@ -11,7 +12,14 @@
{% endfor %} - - + + + {% if display_wifi_password %} +
+

Connect to this box with wifi: + +

+ {% endif %} + {% endblock %} \ No newline at end of file diff --git a/app/templates/messaging.html b/app/templates/messaging.html new file mode 100644 index 0000000..dc09044 --- /dev/null +++ b/app/templates/messaging.html @@ -0,0 +1,19 @@ +{% extends "base.html" %} + +{% block content %} +

{{ _('Secure Messaging') }}

+ +
+

To use secure messaging, install Delta Chat and then return to this page to create your local offline account

+
+ + +
+
+ +
+{% endblock %} \ No newline at end of file diff --git a/app/templates/usb-file-viewer.html b/app/templates/usb-file-viewer.html new file mode 100644 index 0000000..dba7fbb --- /dev/null +++ b/app/templates/usb-file-viewer.html @@ -0,0 +1,39 @@ +{% extends "base.html" %} + +{% block content %} +

{{ _('File Viewer') }}

+ +
+ + + + + + + + + + + {% for f in render_files %} + + + {% if f.is_dir %} + + {% endif %} + + {% if f.is_file %} + + {% endif %} + + + {% if f.is_file %} + + {% endif %} + {% if f.is_dir %} + + {% endif %} + + {% endfor %} + +
File NameDate modifiedDownload
{{ f.name}}{{ f.name}}{{ f.last_modified}}Download
+ {% endblock %} \ No newline at end of file diff --git a/config.py b/config.py index 1b77d7e..c8a9fb8 100644 --- a/config.py +++ b/config.py @@ -9,14 +9,17 @@ class Config: BUTTERBOX_SSID= "butterbox" BUTTERBOX_WIFI_PASSWORD= "" LANGUAGES = ['en', 'ro'] + BUTTERBOX_DEFAULT_IP = "10.0.1.37" + BUTTERBOX_USB_PATH = "/media/usb-butter" BUTTERBOX_NAME = "Butter Box" BUTTERBOX_LOGO = "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjc5IiBoZWlnaHQ9IjIzNiIgdmlld0JveD0iMCAwIDI3OSAyMzYiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxnIG9wYWNpdHk9IjAuOSI+CjxwYXRoIG9wYWNpdHk9IjAuOSIgZD0iTTEzOS4zODggMTk3LjYzNkMxNzIuMjg0IDE5Ny42MzYgMTk4Ljk1MiAxNzAuOTY4IDE5OC45NTIgMTM4LjA3MkMxOTguOTUyIDEwNS4xNzUgMTcyLjI4NCA3OC41MDcgMTM5LjM4OCA3OC41MDdDMTA2LjQ5MSA3OC41MDcgNzkuODIzIDEwNS4xNzUgNzkuODIzIDEzOC4wNzJDNzkuODIzIDE3MC45NjggMTA2LjQ5MSAxOTcuNjM2IDEzOS4zODggMTk3LjYzNloiIGZpbGw9IiNGRUY1QjgiIHN0cm9rZT0iI0ZFRjVCOCIgc3Ryb2tlLXdpZHRoPSI0IiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiLz4KPHBhdGggb3BhY2l0eT0iMC45IiBkPSJNMTM5LjYxMiA0LjAzMjU5VjYwLjM3NTRDMTM0LjgxNyA1Ny40NTM0IDEzMS4xNDYgNDUuOTE1MSAxMzEuMTQ2IDMyLjIwNEMxMzEuMjIxIDE4LjQ5MjkgMTM0LjgxNyA2Ljk1NDYzIDEzOS42MTIgNC4wMzI1OVoiIGZpbGw9IiNGRUY1QjgiIHN0cm9rZT0iI0ZFRjVCOCIgc3Ryb2tlLXdpZHRoPSI0IiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiLz4KPHBhdGggb3BhY2l0eT0iMC45IiBkPSJNNjAuNDE3OCAxMzkuNDJINEM2LjkyMjA0IDE0NC4yMTUgMTguNDYwMyAxNDcuODg3IDMyLjE3MTQgMTQ3Ljg4N0M0NS45NTc0IDE0Ny44ODcgNTcuNDk1NyAxNDQuMjE1IDYwLjQxNzggMTM5LjQyWiIgZmlsbD0iI0ZFRjVCOCIgc3Ryb2tlPSIjRkVGNUI4IiBzdHJva2Utd2lkdGg9IjQiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIvPgo8cGF0aCBvcGFjaXR5PSIwLjkiIGQ9Ik0yNzUgMTM5LjY0NUgyMTguNjU3QzIyMS41NzkgMTM0Ljg1IDIzMy4xMTggMTMxLjE3OSAyNDYuODI5IDEzMS4xNzlDMjYwLjU0IDEzMS4yNTQgMjcyLjA3OCAxMzQuODUgMjc1IDEzOS42NDVaIiBmaWxsPSIjRkVGNUI4IiBzdHJva2U9IiNGRUY1QjgiIHN0cm9rZS13aWR0aD0iNCIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIi8+CjxwYXRoIG9wYWNpdHk9IjAuOSIgZD0iTTIzNS4zNjUgNDMuODE3M0wxOTUuNTA2IDgzLjY3NjlDMTk0LjE1NyA3OC4yMDc0IDE5OS43NzYgNjcuNDkzMyAyMDkuNDQyIDU3LjgyODFDMjE5LjI1NyA0OC4wODggMjI5Ljg5NiA0Mi40Njg3IDIzNS4zNjUgNDMuODE3M1oiIGZpbGw9IiNGRUY1QjgiIHN0cm9rZT0iI0ZFRjVCOCIgc3Ryb2tlLXdpZHRoPSI0IiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiLz4KPHBhdGggb3BhY2l0eT0iMC45IiBkPSJNODMuNjQ0MiA4My41MjY5TDQzLjc4NDcgNDMuNjY3NEM0Mi40MzYxIDQ5LjEzNjggNDguMDU1NCA1OS44NTA5IDU3LjcyMDUgNjkuNTE2MUM2Ny40NjA3IDc5LjI1NjIgNzguMTc0OCA4NC44NzU1IDgzLjY0NDIgODMuNTI2OVoiIGZpbGw9IiNGRUY1QjgiIHN0cm9rZT0iI0ZFRjVCOCIgc3Ryb2tlLXdpZHRoPSI0IiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiLz4KPHBhdGggb3BhY2l0eT0iMC45IiBkPSJNMTgyLjkxOCAzNC45MDEyTDE2Ny41NTkgNzIuMTM4NUMxNjUuMTYxIDY4LjkxNjcgMTY1LjkxMSA2MC4zMDA1IDE2OS42NTcgNTEuMjM0N0MxNzMuNDAzIDQyLjE2ODkgMTc4Ljk0NyAzNS41MDA2IDE4Mi45MTggMzQuOTAxMloiIGZpbGw9IiNGRUY1QjgiIHN0cm9rZT0iI0ZFRjVCOCIgc3Ryb2tlLXdpZHRoPSI0IiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiLz4KPHBhdGggb3BhY2l0eT0iMC45IiBkPSJNNzMuMDA1MSAxMTQuMDIxTDM1Ljg0MjggOTguNjYxNkMzNi40NDIyIDEwMi42MzMgNDMuMTEwNCAxMDguMTc3IDUyLjE3NjIgMTExLjkyM0M2MS4yNDIgMTE1LjY2OSA2OS43ODMzIDExNi40MTkgNzMuMDA1MSAxMTQuMDIxWiIgZmlsbD0iI0ZFRjVCOCIgc3Ryb2tlPSIjRkVGNUI4IiBzdHJva2Utd2lkdGg9IjQiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIvPgo8cGF0aCBvcGFjaXR5PSIwLjkiIGQ9Ik0yNDcuNjUzIDE4NC41MjRMMjEwLjQxNiAxNjkuMjRDMjEzLjYzNyAxNjYuODQyIDIyMi4yNTQgMTY3LjU5MiAyMzEuMzE5IDE3MS4zMzhDMjQwLjM4NSAxNzUuMDg0IDI0Ny4wNTMgMTgwLjU1MyAyNDcuNjUzIDE4NC41MjRaIiBmaWxsPSIjRkVGNUI4IiBzdHJva2U9IiNGRUY1QjgiIHN0cm9rZS13aWR0aD0iNCIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIi8+CjxwYXRoIG9wYWNpdHk9IjAuOSIgZD0iTTY3LjIzNTkgMTcwLjg4OEwzMC4wNzM2IDE4Ni4zMjNDMzMuMjk1MyAxODguNzIgNDEuODM2NyAxODcuOTcxIDUwLjkwMjUgMTg0LjIyNUM2MC4wNDMyIDE4MC40MDQgNjYuNjM2NSAxNzQuODU5IDY3LjIzNTkgMTcwLjg4OFoiIGZpbGw9IiNGRUY1QjgiIHN0cm9rZT0iI0ZFRjVCOCIgc3Ryb2tlLXdpZHRoPSI0IiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiLz4KPHBhdGggb3BhY2l0eT0iMC45IiBkPSJNMjQ1LjEwNSA5NS4yODk5TDIwNy45NDMgMTEwLjc5OUMyMDguNTQyIDEwNi44MjggMjE1LjEzNiAxMDEuMjg0IDIyNC4yMDIgOTcuNTM3NkMyMzMuMjY3IDkzLjcxNjUgMjQxLjgwOSA5Mi44OTI0IDI0NS4xMDUgOTUuMjg5OVoiIGZpbGw9IiNGRUY1QjgiIHN0cm9rZT0iI0ZFRjVCOCIgc3Ryb2tlLXdpZHRoPSI0IiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiLz4KPHBhdGggb3BhY2l0eT0iMC45IiBkPSJNMTEwLjA5MiA3My4wMzc3TDk0LjY1OCAzNS44NzU0QzkyLjI2MDQgMzkuMDk3MSA5My4wODQ2IDQ3LjcxMzQgOTYuODMwOCA1Ni43NzkyQzEwMC41NzcgNjUuODQ1IDEwNi4xMjEgNzIuNDM4MyAxMTAuMDkyIDczLjAzNzdaIiBmaWxsPSIjRkVGNUI4IiBzdHJva2U9IiNGRUY1QjgiIHN0cm9rZS13aWR0aD0iNCIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIi8+CjwvZz4KPHBhdGggZD0iTTIwMy4xMyAxNzguOTM2QzE5OC4xMTkgMTc4LjIyIDE5Mi4zOTMgMTc3Ljc5MSAxOTAuMjQ2IDE3NS41NzJDMTg4LjQ1NyAxNzMuNzExIDE5Mi44OTQgMTY3LjYyNyAxODguNzQzIDE2MS44M0MxODcuODg0IDE2MC42ODUgMTg2LjUyNCAxNTkuNTM5IDE4NC45NSAxNTguNjA5VjEzOS43MTNDMTg0Ljk1IDEzNi4xMzUgMTgzLjE2IDEzMi44NDIgMTgwLjE1NCAxMzAuODM4TDE0NC40MzkgMTA4LjA3OEMxNDAuOTMyIDEwNS43ODcgMTM2LjQyMyAxMDUuNzg3IDEzMi45MTUgMTA4LjA3OEw5Ny4xMjg1IDEzMC45MUM5NC4xMjI0IDEzMi44NDIgOTIuMzMzIDEzNi4yMDYgOTIuMzMzIDEzOS43MTNWMTU4Ljc1MkM5MS4yNTk0IDE1OS4zOTYgOTAuMzI5IDE1OS45NjkgODkuNzU2NCAxNjAuMzk4QzgzLjk1ODkgMTY0LjMzNSA4OC4wMzg2IDE3MS40OTIgODUuODE5OCAxNzUuMjg2QzgzLjAyODQgMTc5Ljg2NiA4MC40NTE3IDE3OC44NjQgNzMuMDA4IDE4MC45NEM2Ni40OTQ4IDE4Mi42NTggNjMuNDE3MSAxOTQuMTgxIDg2LjUzNTUgMTg4LjQ1NUM4OS4xMTIyIDE4Ny44MTEgOTAuNTQzNiAxODcuNTI1IDkzLjQ3ODIgMTg5LjQ1N0MxMDAuNTY0IDE5My45NjYgOTMuMzM1MSAyMDguMjEgOTkuOTkxNCAyMTAuNDI4QzEwOS44NjkgMjEzLjcyMSAxMDIuNDI1IDE4OS40NTcgMTA4LjE1MSAxOTEuMDMyQzExMC44NzEgMTkyLjM5MiAxMTMuMzA0IDE5NC4zOTYgMTE0LjMwNiAxOTcuNjE3QzExNy4xNjkgMjA1LjIwNCAxMTIuNzMyIDIzMy4yNjEgMTIwLjAzMiAyMzQuNDc3QzEyOC40NzggMjM1Ljc2NiAxMjQuMTEyIDIwMy43IDEyOC4wNDggMjAyLjI2OUMxMzAuNDEgMjAxLjQxIDEzMi43MDEgMjA1LjU2MSAxMzguNDk4IDIwNi4yMDZDMTQzLjE1MSAyMDYuNzA3IDE0Ny45NDYgMjAzLjcgMTQ5LjY2NCAyMDIuMjY5QzE1NC41MzEgMTk4LjExOCAxNTcuODk1IDE5Ni41NDMgMTYxLjgzMSAxOTYuMTE0QzE3Mi4zNTMgMTk0Ljg5NyAxNjMuMTkxIDIyMC45NSAxNzIuOTk3IDIxOS44MDVDMTgxLjY1NyAyMTYuOTQyIDE3MS4yMDggMTk1LjA0IDE4Ni44MTEgMTg5LjI0M0MxOTIuODk0IDE4Ny4wOTUgMjA1LjA2MiAxOTAuMDMgMjA5LjI4NSAxODguMzEyQzIxMy4zNjUgMTg2LjUyMyAyMTEuNjQ3IDE3OS43OTUgMjAzLjEzIDE3OC45MzZaIiBmaWxsPSIjRkNFMzYzIi8+CjxwYXRoIGQ9Ik0xMjAuNTMzIDIzNS45MDlDMTIwLjMxOCAyMzUuOTA5IDEyMC4wMzIgMjM1LjkwOSAxMTkuODE3IDIzNS44MzdDMTE0LjE2MyAyMzQuOTA3IDExNC4wOTIgMjI0LjA5OSAxMTQuMDIgMjEyLjY0N0MxMTQuMDIgMjA2Ljc3OCAxMTMuOTQ4IDIwMC42OTQgMTEyLjk0NiAxOTguMDQ2VjE5Ny45NzVDMTEyLjIzMSAxOTUuNjg0IDExMC41MTMgMTkzLjgyMyAxMDcuNzIxIDE5Mi4zMkMxMDcuMTQ5IDE5My4xMDggMTA3LjA3NyAxOTYuODI5IDEwNy4wNzcgMTk5LjEyQzEwNy4wNzcgMjA0LjEzIDEwNy4wMDYgMjA5LjM1NSAxMDQuMjE0IDIxMS4zNTlDMTAyLjk5OCAyMTIuMjE4IDEwMS40MjMgMjEyLjM2MSA5OS41NjIgMjExLjc4OEM5NS4yNjc2IDIxMC4zNTcgOTUuMzM5MiAyMDUuNTYxIDk1LjQ4MjMgMjAwLjk4MUM5NS41NTM5IDE5Ni44MjkgOTUuNjI1NCAxOTIuNTM1IDkyLjc2MjUgMTkwLjY3NEM5MC4yNTc0IDE4OS4wMjggODkuMjU1NCAxODkuMjQzIDg2Ljk2NSAxODkuODg3QzcwLjcxNzcgMTkzLjg5NSA2Ny42NCAxODkuMzg2IDY3LjEzOSAxODcuMjM5QzY2LjM1MTcgMTg0LjE2MSA2OC45OTk5IDE4MC41ODIgNzIuNzkzMyAxNzkuNThDNzQuODY5IDE3OS4wMDggNzYuNTg2OCAxNzguNzIxIDc3Ljk0NjcgMTc4LjQzNUM4MS42Njg1IDE3Ny43MTkgODMuMDI4NCAxNzcuNDMzIDg0Ljc0NjIgMTc0LjY0MUM4NS4zOTA0IDE3My41NjggODUuMzE4OCAxNzEuNzc5IDg1LjI0NzIgMTY5LjkxOEM4NS4xMDQgMTY2LjQ4MiA4NC44ODkzIDE2Mi4xODggODkuMTEyMiAxNTkuMzI1Qzg5LjU0MTYgMTU4Ljk2NyA5MC4xODU4IDE1OC41MzcgOTEuMDQ0NyAxNTguMDM2VjEzOS43ODVDOTEuMDQ0NyAxMzUuNzc3IDkzLjEyMDMgMTMxLjkxMiA5Ni40ODQzIDEyOS43NjVMMTMyLjI3MSAxMDYuOTMyQzEzNi4yMDggMTA0LjM1NiAxNDEuMzYxIDEwNC4zNTYgMTQ1LjM2OSAxMDYuOTMyTDE4MS4wODUgMTI5LjY5M0MxODQuNTIgMTMxLjk4MyAxODYuNTI0IDEzNS43MDUgMTg2LjUyNCAxMzkuNzg1VjE1Ny44OTNDMTg3Ljk1NiAxNTguODI0IDE4OS4xNzMgMTU5Ljk2OSAxOTAuMDMyIDE2MS4wNDJDMTkyLjk2NiAxNjUuMTIyIDE5Mi4xNzkgMTY5LjI3MyAxOTEuNjc4IDE3Mi4wNjVDMTkxLjUzNSAxNzIuOTk1IDE5MS4yNDggMTc0LjM1NSAxOTEuNDYzIDE3NC42NDFDMTkyLjk2NiAxNzYuMjE2IDE5Ny40MDQgMTc2Ljc4OSAyMDEuMzQgMTc3LjI5QzIwMi4wNTYgMTc3LjM2MSAyMDIuNzcyIDE3Ny41MDQgMjAzLjQxNiAxNzcuNTc2QzIwOS43ODYgMTc4LjI5MiAyMTIuNTA2IDE4MS43MjcgMjEyLjkzNSAxODQuNDQ3QzIxMy4yOTMgMTg2LjY2NiAyMTIuMTQ4IDE4OC43NDIgMjEwLjAwMSAxODkuNjcyQzIwNy43MSAxOTAuNjAyIDIwMy45MTcgMTkwLjM4OCAxOTkuNjIyIDE5MC4xMDFDMTk1LjMyOCAxODkuODg3IDE5MC4zODkgMTg5LjYgMTg3LjQ1NSAxOTAuNjAyQzE3OS40MzkgMTkzLjYwOSAxNzkuMDgxIDIwMS4xOTUgMTc4Ljc5NCAyMDguNTY4QzE3OC41MDggMjE0LjI5MyAxNzguMjkzIDIxOS42NjIgMTczLjY0MSAyMjEuMjM2TDE3My4zNTUgMjIxLjMwOEMxNzEuNzggMjIxLjUyMiAxNzAuNDIgMjIxLjE2NSAxNjkuMzQ3IDIyMC4yMzRDMTY2Ljc3IDIxOC4wODcgMTY2LjYyNyAyMTMuMzYzIDE2Ni40MTIgMjA4LjQyNEMxNjYuMjY5IDIwNC4zNDUgMTY2LjA1NCAxOTkuNjkyIDE2NC4zMzYgMTk4LjE4OUMxNjMuOTc5IDE5Ny45MDMgMTYzLjQwNiAxOTcuNTQ1IDE2Mi4xODkgMTk3LjY4OEMxNTguMzk2IDE5OC4xMTggMTU1LjI0NyAxOTkuNjkyIDE1MC44MDkgMjAzLjQ4NkMxNTAuNTIzIDIwMy43MDEgMTQ0Ljc5NyAyMDguNDI0IDEzOC41NyAyMDcuNzhDMTM1LjEzNCAyMDcuNDIyIDEzMi44NDQgMjA1LjkxOSAxMzEuMTI2IDIwNC44NDZDMTMwLjEyNCAyMDQuMjAyIDEyOS4yNjUgMjAzLjcwMSAxMjguODM2IDIwMy43NzJDMTI4LjA0OCAyMDQuODQ2IDEyNy43NjIgMjEwLjcxNSAxMjcuNjE5IDIxNS4wMDlDMTI3LjI2MSAyMjMuNTI3IDEyNi45MDMgMjMyLjMzIDEyMy4zOTYgMjM1LjE5M0MxMjIuMzk0IDIzNS41NTEgMTIxLjQ2NCAyMzUuOTA5IDEyMC41MzMgMjM1LjkwOVpNMTE1LjY2NiAxOTcuMTE2QzExNi44MTEgMjAwLjI2NSAxMTYuODgzIDIwNi4yNzcgMTE2Ljg4MyAyMTIuNjQ3QzExNi44ODMgMjIwLjUyIDExNi45NTQgMjMyLjQ3MyAxMjAuMjQ3IDIzMy4wNDZDMTIwLjY3NiAyMzMuMTE3IDEyMS4wMzQgMjMzLjA0NiAxMjEuMzIxIDIzMi43NkMxMjMuODI2IDIzMC43NTYgMTI0LjE4MyAyMjEuNDUxIDEyNC40NyAyMTQuNjUxQzEyNC44MjggMjA1Ljk5MSAxMjUuMTE0IDIwMS43NjggMTI3LjQ3NiAyMDAuOTA5QzEyOS4yNjUgMjAwLjI2NSAxMzAuNzY4IDIwMS4yNjcgMTMyLjQxNCAyMDIuMjY5QzEzNC4wNjEgMjAzLjI3MSAxMzUuODUgMjA0LjQ4OCAxMzguNTcgMjA0Ljc3NEMxNDIuNzkzIDIwNS4yMDQgMTQ3LjMwMiAyMDIuMzQxIDE0OC42NjIgMjAxLjE5NUMxNTMuNTI5IDE5Ny4wNDQgMTU3LjE3OSAxOTUuMTgzIDE2MS42MTcgMTk0LjY4MkMxNjMuMzM0IDE5NC40NjcgMTY0LjgzOCAxOTQuODk3IDE2NS45ODMgMTk1LjgyN0MxNjguNzAzIDE5OC4xMTggMTY4LjkxNyAyMDIuOTg1IDE2OS4wNiAyMDguMTM4QzE2OS4yMDQgMjEyLjA3NSAxNjkuNDE4IDIxNi41ODQgMTcwLjk5MyAyMTcuOTQ0QzE3MS4yNzkgMjE4LjE1OCAxNzEuNzA5IDIxOC40NDUgMTcyLjYzOSAyMTguMzczQzE3NS4xNDQgMjE3LjQ0MyAxNzUuNDMgMjEzLjkzNiAxNzUuNjQ1IDIwOC4zNTNDMTc2LjAwMyAyMDAuODM4IDE3Ni4zNjEgMTkxLjUzMyAxODYuMjM4IDE4Ny44ODNDMTg5Ljc0NSAxODYuNjY2IDE5NC43NTUgMTg2Ljk1MiAxOTkuNTUxIDE4Ny4yMzlDMjAzLjIwMSAxODcuNDUzIDIwNi45OTUgMTg3LjY2OCAyMDguNjQxIDE4Ny4wMjRDMjA5LjU3MSAxODYuNjY2IDIxMC4wMDEgMTg1Ljg3OSAyMDkuODU4IDE4NC44NzdDMjA5LjU3MSAxODMuMDg3IDIwNy4yODEgMTgwLjg2OCAyMDIuODQzIDE4MC40MzlIMjAyLjc3MkMyMDIuMDU2IDE4MC4zNjcgMjAxLjQxMiAxODAuMjI0IDIwMC42OTYgMTgwLjE1M0MxOTUuODI5IDE3OS41MDkgMTkxLjMyIDE3OC45MzYgMTg5LjEwMSAxNzYuNjQ2QzE4Ny44ODQgMTc1LjM1NyAxODguMjQyIDE3My40OTYgMTg4LjYgMTcxLjU2NEMxODkuMTAxIDE2OC45ODcgMTg5LjY3NCAxNjUuNzY2IDE4Ny40NTUgMTYyLjc2QzE4Ni43MzkgMTYxLjgzIDE4NS41MjIgMTYwLjc1NiAxODQuMDkxIDE1OS45NjlMMTgzLjM3NSAxNTkuNTM5VjEzOS44NTdDMTgzLjM3NSAxMzYuNzc5IDE4MS44MDEgMTMzLjkxNiAxNzkuMjI0IDEzMi4xOThMMTQzLjY1MiAxMDkuMjIzQzE0MC42NDUgMTA3LjIxOSAxMzYuNzA5IDEwNy4yMTkgMTMzLjcwMyAxMDkuMjIzTDk3LjkxNTggMTMyLjA1NUM5NS4zMzkxIDEzMy43MDEgOTMuNzY0NSAxMzYuNjM2IDkzLjc2NDUgMTM5LjY0MlYxNTkuNDY4TDkzLjA0ODggMTU5Ljg5N0M5Mi4xMTgzIDE2MC40NyA5MS4xMTYzIDE2MS4wNDIgOTAuNjE1MiAxNjEuNDcyQzg3LjY4MDcgMTYzLjQ3NiA4Ny44MjM5IDE2Ni40ODIgODcuOTY3IDE2OS42MzFDODguMDM4NiAxNzEuODUgODguMTgxOCAxNzQuMTQgODcuMTA4MSAxNzYuMDAxQzg0Ljc0NjIgMTc5LjkzOCA4Mi4yNDExIDE4MC40MzkgNzguNDQ3NyAxODEuMTU1Qzc3LjAxNjIgMTgxLjQ0MSA3NS40NDE2IDE4MS43MjcgNzMuNTA5MSAxODIuM0M3MS4wNzU2IDE4Mi45NDQgNjkuNTcyNSAxODUuMDkxIDY5LjkzMDQgMTg2LjQ1MUM3MC4zNTk4IDE4OC4wOTcgNzQuMjk2NCAxOTAuMDMgODYuMzIwOCAxODcuMDk1Qzg5LjExMjIgMTg2LjM4IDkxLjA0NDcgMTg2LjA5MyA5NC40MDg3IDE4OC4zMTJDOTguNzAzMSAxOTEuMDMyIDk4LjU2IDE5Ni40IDk4LjQ4ODQgMjAxLjEyNEM5OC40MTY4IDIwNS42MzMgOTguNDg4NCAyMDguNDk2IDEwMC42MzYgMjA5LjIxMkMxMDIuMDY3IDIwOS43MTMgMTAyLjU2OCAyMDkuMzU1IDEwMi43MTEgMjA5LjIxMkMxMDQuMzU3IDIwOC4wNjcgMTA0LjM1NyAyMDIuOTg1IDEwNC4zNTcgMTk5LjMzNUMxMDQuMzU3IDE5NC45NjggMTA0LjQyOSAxOTEuNjA1IDEwNi4yMTggMTkwLjI0NUMxMDYuNzE5IDE4OS44ODcgMTA3LjUwNyAxODkuNTI5IDEwOC43MjMgMTg5LjgxNUwxMDkuMDEgMTg5Ljg4N0MxMTIuNDQ1IDE5MS41MzMgMTE0LjczNiAxOTMuOTY2IDExNS42NjYgMTk3LjExNloiIGZpbGw9IiMwMTAwMDQiLz4KPHBhdGggZD0iTTE1MC4wMjIgMTY2LjEyNEMxNTIuODEzIDE3MC4zNDcgMTQ2LjA4NSAxOTQuMTEgMTM4LjI4NCAxOTQuMTFDMTI4LjY5MyAxOTQuMTEgMTIzLjg5NyAxNjkuODQ2IDEyNi40NzQgMTY2LjEyNEMxMjcuOTA1IDE2NC4wNDkgMTMxLjQxMyAxNjguMzQzIDEzOC42NDEgMTY4LjM0M0MxNDUuMzY5IDE2OC4zNDMgMTQ4LjU5IDE2My45MDUgMTUwLjAyMiAxNjYuMTI0WiIgZmlsbD0iIzAxMDAwNCIvPgo8cGF0aCBkPSJNMTc1LjIxNiAxNTkuNjgzQzE3Ni45MzQgMTYwLjExMiAxNzguMjkzIDE1OC4zMjMgMTc3LjA3NyAxNTcuMTc4QzE3NS4yODcgMTU1LjYwMyAxNzIuMTM4IDE1My4xNjkgMTY2LjU1NSAxNTMuMTY5QzE2MS4xMTYgMTUzLjE2OSAxNTcuNzUyIDE1NS42MDMgMTU1LjY3NiAxNTcuMjQ5QzE1NC4yNDUgMTU4LjM5NCAxNTUuNjc2IDE2MC4zMjcgMTU3LjUzNyAxNTkuODI2QzE2MC4wNDIgMTU5LjE4MiAxNjMuMjYzIDE1Ny42MDcgMTY2LjY5OCAxNTcuNjA3QzE3MC4xMzQgMTU3Ljc1IDE3My4wNjkgMTU5LjE4MiAxNzUuMjE2IDE1OS42ODNaIiBmaWxsPSIjMDEwMDA0Ii8+CjxwYXRoIGQ9Ik0xMTkuMTAyIDE1OS42ODNDMTIwLjgxOSAxNjAuMTEyIDEyMi4xNzkgMTU4LjMyMyAxMjAuOTYzIDE1Ny4xNzhDMTE5LjE3MyAxNTUuNjAzIDExNi4wMjQgMTUzLjE2OSAxMTAuNDQxIDE1My4xNjlDMTA1LjAwMiAxNTMuMTY5IDEwMS42MzggMTU1LjYwMyA5OS41NjIgMTU3LjI0OUM5OC4xMzA1IDE1OC4zOTQgOTkuNTYyIDE2MC4zMjcgMTAxLjQyMyAxNTkuODI2QzEwMy45MjggMTU5LjE4MiAxMDcuMTQ5IDE1Ny42MDcgMTEwLjU4NCAxNTcuNjA3QzExNC4wOTIgMTU3Ljc1IDExNi45NTUgMTU5LjE4MiAxMTkuMTAyIDE1OS42ODNaIiBmaWxsPSIjMDEwMDA0Ii8+CjxwYXRoIGQ9Ik0xMDkuODY5IDE4Mi41ODZDMTE2LjE1NCAxODIuNTg2IDEyMS4yNDkgMTc4LjUxNyAxMjEuMjQ5IDE3My40OTZDMTIxLjI0OSAxNjguNDc2IDExNi4xNTQgMTY0LjQwNiAxMDkuODY5IDE2NC40MDZDMTAzLjU4MyAxNjQuNDA2IDk4LjQ4ODMgMTY4LjQ3NiA5OC40ODgzIDE3My40OTZDOTguNDg4MyAxNzguNTE3IDEwMy41ODMgMTgyLjU4NiAxMDkuODY5IDE4Mi41ODZaIiBmaWxsPSIjRkVGNUI4Ii8+CjxwYXRoIGQ9Ik0xNjYuNjk4IDE4Mi41ODZDMTcyLjk4MyAxODIuNTg2IDE3OC4wNzkgMTc4LjUxNyAxNzguMDc5IDE3My40OTZDMTc4LjA3OSAxNjguNDc2IDE3Mi45ODMgMTY0LjQwNiAxNjYuNjk4IDE2NC40MDZDMTYwLjQxMyAxNjQuNDA2IDE1NS4zMTggMTY4LjQ3NiAxNTUuMzE4IDE3My40OTZDMTU1LjMxOCAxNzguNTE3IDE2MC40MTMgMTgyLjU4NiAxNjYuNjk4IDE4Mi41ODZaIiBmaWxsPSIjRkVGNUI4Ii8+CjxwYXRoIGQ9Ik0xMzMuMjAyIDE1Mi4wOTZMMTA3LjQzNSAxMzYuMjA2QzEwNS45MzIgMTM1LjI3NiAxMDUuOTMyIDEzMy4wNTcgMTA3LjQzNSAxMzIuMTI3TDEzMy4yMDIgMTE2LjIzN0MxMzYuMzUxIDExNC4zMDUgMTQwLjI4OCAxMTQuMzA1IDE0My40MzcgMTE2LjIzN0wxNjkuMjAzIDEzMi4xMjdDMTcwLjcwNiAxMzMuMDU3IDE3MC43MDYgMTM1LjI3NiAxNjkuMjAzIDEzNi4yMDZMMTQzLjQzNyAxNTIuMDk2QzE0MC4yODggMTU0LjAyOCAxMzYuMjc5IDE1NC4wMjggMTMzLjIwMiAxNTIuMDk2WiIgZmlsbD0iI0ZFRjVCOCIvPgo8cGF0aCBkPSJNMTM1LjYzNSAxOTAuMzg4QzEzMi4yNzEgMTg3LjgxMSAxMzMuMjAyIDE3OC41NzggMTM5LjE0MiAxNzcuNTA0QzE0NC4xNTMgMTc2LjU3NCAxNDYuNTE1IDE3OC4yMiAxNDMuMzY1IDE4NC45NDhDMTQwLjIxNiAxOTEuODkxIDEzNy4yMSAxOTEuNTMzIDEzNS42MzUgMTkwLjM4OFoiIGZpbGw9IiNGRUY1QjgiLz4KPC9zdmc+Cg==" ADMIN_PASSWORD = "admin" - DISABLE_ACCESS_POINT = "false" - DISABLE_CHAT = "false" - DISABLE_APP_STORE = "false" - DISABLE_MAP_VIEWER = "false" - DISABLE_FILE_VIEWER = "false" + ENABLE_ACCESS_POINT = "true" + ENABLE_CHAT = "true" + ENABLE_APP_STORE = "true" + ENABLE_MAP_VIEWER = "true" + ENABLE_FILE_VIEWER = "true" + ENABLE_DELTACHAT = "true" CONVENE_INSTALL_PATH = "/chat" \ No newline at end of file