<?php
define('CONTACTS_FILE_DIR', __DIR__
. '/data/'); define('CONTACTS_FILE_NAME', 'contacts.tsv'); define('CONTACTS_FILE_PATH', CONTACTS_FILE_DIR
. CONTACTS_FILE_NAME
); define('MAX_NAME_LENGTH', 50); define('MAX_MESSAGE_LENGTH', 1000);
define('STATUS_UNCONFIRMED', 0); define('STATUS_CONFIRMED', 1);
if (!is_dir(CONTACTS_FILE_DIR
)) { if (!mkdir(CONTACTS_FILE_DIR
, 0755, true)) { die('連絡事項保存用ディレクトリの作成に失敗しました。パーミッションを確認してください。'); }
}
$errors = [];
$form_data = [
'name' => '',
'message_text' => ''
];
// 連絡事項の追加処理 (変更なし)
if ($_SERVER["REQUEST_METHOD"] == "POST" && isset($_POST['action']) && $_POST['action'] == 'add_contact') { $form_data['name'] = isset($_POST['name']) ?
trim($_POST['name']) : ''; $form_data['message_text'] = isset($_POST['message_text']) ?
trim($_POST['message_text']) : '';
if (empty($form_data['name'])) $errors[] = "名前を入力してください。"; elseif (mb_strlen($form_data['name']) > MAX_NAME_LENGTH
) $errors[] = "名前は" . MAX_NAME_LENGTH
. "文字以内で入力してください。"; if (empty($form_data['message_text'])) $errors[] = "連絡事項を入力してください。"; elseif (mb_strlen($form_data['message_text']) > MAX_MESSAGE_LENGTH
) $errors[] = "連絡事項は" . MAX_MESSAGE_LENGTH
. "文字以内で入力してください。";
$id = uniqid('contact_', true); $timestamp = date("Y-m-d H:i:s"); $name_to_save = str_replace(["\t", "\r", "\n"], " ", $form_data['name']); $message_to_save = str_replace(["\r\n", "\r", "\n"], "<br_temp>", $form_data['message_text']); $message_to_save = str_replace("\t", " ", $message_to_save); $status = STATUS_UNCONFIRMED;
$data_line = $id . "\t" . $timestamp . "\t" . $name_to_save . "\t" . $message_to_save . "\t" . $status . PHP_EOL;
$fp = fopen(CONTACTS_FILE_PATH
, 'ab'); if ($fp) {
if (flock($fp, LOCK_EX
)) { if (fwrite($fp, $data_line) === false) { $_SESSION['message_display'] = "連絡事項の保存に失敗しました。(書き込みエラー)"; $_SESSION['message_type'] = "error";
} else {
$_SESSION['message_display'] = "連絡事項が追加されました!"; $_SESSION['message_type'] = "success";
$form_data = ['name' => '', 'message_text' => ''];
}
} else { $_SESSION['message_display'] = "連絡事項の保存に失敗しました。(ロック取得失敗)"; $_SESSION['message_type'] = "error"; }
} else { $_SESSION['message_display'] = "連絡事項の保存に失敗しました。(ファイルオープン失敗)"; $_SESSION['message_type'] = "error"; }
header("Location: " . $_SERVER['PHP_SELF']); exit; } else {
$_SESSION['message_display'] = implode("<br>", $errors); $_SESSION['message_type'] = "error"; $_SESSION['form_data'] = $form_data;
header("Location: " . $_SERVER['PHP_SELF']); exit; }
}
// 連絡事項の削除処理 (変更なし)
if ($_SERVER["REQUEST_METHOD"] == "POST" && isset($_POST['action']) && $_POST['action'] == 'delete_contact' && isset($_POST['contact_id'])) { $contact_id_to_delete = trim($_POST['contact_id']); $temp_file_path = CONTACTS_FILE_DIR . 'contacts_temp.tsv';
$deleted = false;
$fp_read = fopen(CONTACTS_FILE_PATH
, 'rb'); $fp_write = fopen($temp_file_path, 'wb'); if ($fp_read && $fp_write) {
if (flock($fp_read, LOCK_SH
) && flock($fp_write, LOCK_EX
)) { while (($line = fgets($fp_read)) !== false) { if (isset($parts[0]) && trim($parts[0]) === $contact_id_to_delete) { $deleted = true; } else { fwrite($fp_write, $line); } }
flock($fp_write, LOCK_UN
); flock($fp_read, LOCK_UN
); } else { $_SESSION['message_display'] = "ファイルのロックに失敗しました(削除時)。"; $_SESSION['message_type'] = "error"; }
if ($deleted) {
if (rename($temp_file_path, CONTACTS_FILE_PATH
)) { $_SESSION['message_display'] = "連絡事項が削除されました。"; $_SESSION['message_type'] = "success"; } else { $_SESSION['message_display'] = "ファイル更新に失敗しました(削除時)。"; $_SESSION['message_type'] = "error"; @unlink($temp_file_path); } } else { $_SESSION['message_display'] = "削除対象の連絡事項が見つかりませんでした。"; $_SESSION['message_type'] = "error"; @unlink($temp_file_path); } } else { if($fp_read) fclose($fp_read); if($fp_write) fclose($fp_write); @unlink($temp_file_path); $_SESSION['message_display'] = "ファイル操作に失敗しました(削除時)。"; $_SESSION['message_type'] = "error"; } } else { $_SESSION['message_display'] = "連絡事項ファイルが見つかりません。"; $_SESSION['message_type'] = "error"; }
header("Location: " . $_SERVER['PHP_SELF']); exit; }
// 確認状態のトグル処理 (変更なし)
if ($_SERVER["REQUEST_METHOD"] == "POST" && isset($_POST['action']) && $_POST['action'] == 'toggle_confirm' && isset($_POST['contact_id'])) { $contact_id_to_toggle = trim($_POST['contact_id']); $temp_file_path = CONTACTS_FILE_DIR . 'contacts_temp.tsv';
$toggled = false;
$fp_read = fopen(CONTACTS_FILE_PATH
, 'rb'); $fp_write = fopen($temp_file_path, 'wb'); if ($fp_read && $fp_write) {
if (flock($fp_read, LOCK_SH
) && flock($fp_write, LOCK_EX
)) { while (($line = fgets($fp_read)) !== false) { if (isset($parts[0]) && trim($parts[0]) === $contact_id_to_toggle) { $current_status = isset($parts[4]) ?
(int
)$parts[4] : STATUS_UNCONFIRMED
; $new_status = ($current_status == STATUS_CONFIRMED) ? STATUS_UNCONFIRMED : STATUS_CONFIRMED;
$parts[4] = $new_status;
$line = implode("\t", $parts) . PHP_EOL
; $toggled = true;
}
}
flock($fp_write, LOCK_UN
); flock($fp_read, LOCK_UN
); } else { $_SESSION['message_display'] = "ファイルのロックに失敗しました(状態更新時)。"; $_SESSION['message_type'] = "error"; }
if ($toggled) {
if (rename($temp_file_path, CONTACTS_FILE_PATH
)) { $_SESSION['message_display'] = "確認状態が更新されました。"; $_SESSION['message_type'] = "success"; } else { $_SESSION['message_display'] = "ファイル更新に失敗しました(状態更新時)。"; $_SESSION['message_type'] = "error"; @unlink($temp_file_path); } } else { $_SESSION['message_display'] = "対象の連絡事項が見つかりませんでした(状態更新時)。"; $_SESSION['message_type'] = "error"; @unlink($temp_file_path); } } else { if($fp_read) fclose($fp_read); if($fp_write) fclose($fp_write); @unlink($temp_file_path); $_SESSION['message_display'] = "ファイル操作に失敗しました(状態更新時)。"; $_SESSION['message_type'] = "error"; } } else { $_SESSION['message_display'] = "連絡事項ファイルが見つかりません。"; $_SESSION['message_type'] = "error"; }
header("Location: " . $_SERVER['PHP_SELF']); exit; }
if (isset($_SESSION['message_display'])) { $page_message = $_SESSION['message_display']; $page_message_type = $_SESSION['message_type']; unset($_SESSION['message_display']); unset($_SESSION['message_type']); } if (isset($_SESSION['form_data'])) { $form_data = $_SESSION['form_data']; unset($_SESSION['form_data']); }
function display_contacts() {
echo "<p>まだ連絡事項はありません。</p>"; return;
}
$lines_array = file(CONTACTS_FILE_PATH
, FILE_IGNORE_NEW_LINES
| FILE_SKIP_EMPTY_LINES
); if ($lines_array === false) { echo "<p>連絡事項ファイルの読み込みに失敗しました。</p>"; return; }
$contacts_html = "";
foreach ($lines_array as $line) {
if (count($parts) >= 4) { $message_text_raw = str_replace("<br_temp>", "\n", $parts[3]); $status = isset($parts[4]) ?
(int
)$parts[4] : STATUS_UNCONFIRMED
;
$confirm_button_text = "";
$confirm_dialog_message = "";
$button_confirm_class = "";
if ($status == STATUS_CONFIRMED) {
$confirm_button_text = "確認済み";
$confirm_dialog_message = "この連絡事項を未確認に戻しますか?";
$button_confirm_class = "is-confirmed";
} else {
$confirm_button_text = "確認済みにする";
$confirm_dialog_message = "この連絡事項を確認済みにしますか?";
$button_confirm_class = "is-unconfirmed";
}
$item_class = ($status == STATUS_CONFIRMED) ? "contact-item confirmed" : "contact-item";
$contacts_html .= "<div class='" . $item_class . "'>" .
"<div class='contact-header'>" .
"<strong>" . $name . "</strong>" .
"<small class='contact-date'> (" . $timestamp . ")</small>" .
"<form method='post' action='" . htmlspecialchars($_SERVER["PHP_SELF"], ENT_QUOTES, 'UTF-8') . "' class='confirm-button-form' onsubmit='return confirm(\"" . htmlspecialchars($confirm_dialog_message, ENT_QUOTES, 'UTF-8') . "\");'>" . "<input type='hidden' name='action' value='toggle_confirm'>" .
"<input type='hidden' name='contact_id' value='" . $id . "'>" .
"<button type='submit' class='button-confirm " . $button_confirm_class . "'>" . $confirm_button_text . "</button>" .
"</form>" .
"<form method='post' action='" . htmlspecialchars($_SERVER["PHP_SELF"], ENT_QUOTES, 'UTF-8') . "' class='delete-button-form' onsubmit='return confirm(\"この連絡事項を削除してもよろしいですか?\");'>" . "<input type='hidden' name='action' value='delete_contact'>" .
"<input type='hidden' name='contact_id' value='" . $id . "'>" .
"<button type='submit' class='button-delete-item'>削除</button>" .
"</form>" .
"</div>" .
"<p>" . $message_text_display . "</p>" .
"</div>";
}
}
echo $contacts_html;
}
?>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>連絡事項</title>
<style>
body { font-family: sans-serif; line-height: 1.6; margin: 20px; background-color: #f4f4f4; color: #333; }
.container { max-width: 600px; margin: auto; background: #fff; padding: 20px; border-radius: 8px; box-shadow: 0 0 10px rgba(0,0,0,0.1); }
h1 { text-align: center; color: #333; }
label { display: block; margin-bottom: 8px; font-weight: bold; }
input[type="text"], textarea {
width: calc(100% - 22px); padding: 10px; margin-bottom: 15px; border: 1px solid #ddd; border-radius: 4px; box-sizing: border-box;
}
textarea { min-height: 60px; resize: vertical; }
button[type="submit"] { background-color: #28a745; color: white; padding: 10px 15px; border: none; border-radius: 4px; cursor: pointer; font-size: 16px; }
button[type="submit"]:hover { background-color: #218838; }
.message { padding: 10px; margin-bottom: 15px; border-radius: 4px; border-width: 1px; border-style: solid; }
.success { background-color: #d4edda; color: #155724; border-color: #c3e6cb; }
.error { background-color: #f8d7da; color: #721c24; border-color: #f5c6cb; }
.info { background-color: #d1ecf1; color: #0c5460; border-color: #bee5eb; }
.contacts-display { margin-top: 30px; border-top: 1px solid #eee; padding-top: 20px; }
.contact-item {
padding: 10px 15px; border-width: 1px 1px 1px 5px; border-style: solid; border-color: #eee #eee #eee; /* 右と下は薄いグレー、左は状態に応じて */
border-radius: 4px; margin-bottom: 10px;
transition: background-color 0.3s ease, border-left-color 0.3s ease;
}
.contact-item.confirmed {
background-color: #e6ffe6; /* 確認済みの背景色 (薄い緑) */
border-left-color: #28a745; /* 緑のアクセントボーダー */
}
.contact-item:not(.confirmed) {
background-color: #e0f7fa; /* 未確認の背景色 (薄い水色) */
border-left-color: #007bff; /* 青のアクセントボーダー */
}
.contact-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; flex-wrap: wrap; }
.contact-item strong { margin-right: 5px; }
.contact-item .contact-date { font-size: 0.9em; color: #777; flex-grow: 1; min-width: 150px; }
.contact-item p { margin-top: 5px; margin-bottom: 0; word-wrap: break-word; border: 1px solid #ccc; padding: 8px 10px; background-color: #fff; border-radius: 3px; }
.delete-button-form, .confirm-button-form { display: inline-block; margin-left: 5px; }
.button-delete-item, .button-confirm { color: white; padding: 3px 8px; border: none; border-radius: 3px; cursor: pointer; font-size: 0.85em; transition: background-color 0.2s ease; }
.button-delete-item { background-color: #dc3545; }
.button-delete-item:hover { background-color: #c82333; }
.button-confirm.is-unconfirmed { background-color: #007bff; }
.button-confirm.is-unconfirmed:hover { background-color: #0056b3; }
.button-confirm.is-confirmed { background-color: #ffc107; color: #212529; }
.button-confirm.is-confirmed:hover { background-color: #e0a800; }
</style>
</head>
<body>
<div class="container">
<h1>連絡事項</h1>
<?php if (isset($page_message) && !empty($page_message)): ?> <div class="message
<?php echo htmlspecialchars($page_message_type, ENT_QUOTES, 'UTF-8'); ?>">
<?php echo $page_message; ?>
</div>
<?php endif; ?>
<form action="
<?php echo htmlspecialchars($_SERVER["PHP_SELF"], ENT_QUOTES, 'UTF-8'); ?>" method="post">
<input type="hidden" name="action" value="add_contact">
<div>
<label for="name">名前:</label>
<input type="text" id="name" name="name" value="
<?php echo htmlspecialchars($form_data['name'], ENT_QUOTES, 'UTF-8'); ?>" required maxlength="
<?php echo MAX_NAME_LENGTH
; ?>">
</div>
<div>
<label for="message_text">連絡事項:</label>
<textarea id="message_text" name="message_text" rows="3" required maxlength="
<?php echo MAX_MESSAGE_LENGTH
; ?>">
<?php echo htmlspecialchars($form_data['message_text'], ENT_QUOTES, 'UTF-8'); ?></textarea>
</div>
<div>
<button type="submit">追加</button>
</div>
</form>
<div class="contacts-display">
<?php display_contacts(); ?>
</div>
</div>
</body>
</html>
PD9waHAKc2Vzc2lvbl9zdGFydCgpOwoKZGVmaW5lKCdDT05UQUNUU19GSUxFX0RJUicsIF9fRElSX18gLiAnL2RhdGEvJyk7CmRlZmluZSgnQ09OVEFDVFNfRklMRV9OQU1FJywgJ2NvbnRhY3RzLnRzdicpOwpkZWZpbmUoJ0NPTlRBQ1RTX0ZJTEVfUEFUSCcsIENPTlRBQ1RTX0ZJTEVfRElSIC4gQ09OVEFDVFNfRklMRV9OQU1FKTsKZGVmaW5lKCdNQVhfTkFNRV9MRU5HVEgnLCA1MCk7CmRlZmluZSgnTUFYX01FU1NBR0VfTEVOR1RIJywgMTAwMCk7CgpkZWZpbmUoJ1NUQVRVU19VTkNPTkZJUk1FRCcsIDApOwpkZWZpbmUoJ1NUQVRVU19DT05GSVJNRUQnLCAxKTsKCmlmICghaXNfZGlyKENPTlRBQ1RTX0ZJTEVfRElSKSkgewogICAgaWYgKCFta2RpcihDT05UQUNUU19GSUxFX0RJUiwgMDc1NSwgdHJ1ZSkpIHsKICAgICAgICBkaWUoJ+mAo+e1oeS6i+mgheS/neWtmOeUqOODh+OCo+ODrOOCr+ODiOODquOBruS9nOaIkOOBq+WkseaVl+OBl+OBvuOBl+OBn+OAguODkeODvOODn+ODg+OCt+ODp+ODs+OCkueiuuiqjeOBl+OBpuOBj+OBoOOBleOBhOOAgicpOwogICAgfQp9CgokZXJyb3JzID0gW107CiRmb3JtX2RhdGEgPSBbCiAgICAnbmFtZScgPT4gJycsCiAgICAnbWVzc2FnZV90ZXh0JyA9PiAnJwpdOwoKLy8g6YCj57Wh5LqL6aCF44Gu6L+95Yqg5Yem55CGICjlpInmm7TjgarjgZcpCmlmICgkX1NFUlZFUlsiUkVRVUVTVF9NRVRIT0QiXSA9PSAiUE9TVCIgJiYgaXNzZXQoJF9QT1NUWydhY3Rpb24nXSkgJiYgJF9QT1NUWydhY3Rpb24nXSA9PSAnYWRkX2NvbnRhY3QnKSB7CiAgICAkZm9ybV9kYXRhWyduYW1lJ10gPSBpc3NldCgkX1BPU1RbJ25hbWUnXSkgPyB0cmltKCRfUE9TVFsnbmFtZSddKSA6ICcnOwogICAgJGZvcm1fZGF0YVsnbWVzc2FnZV90ZXh0J10gPSBpc3NldCgkX1BPU1RbJ21lc3NhZ2VfdGV4dCddKSA/IHRyaW0oJF9QT1NUWydtZXNzYWdlX3RleHQnXSkgOiAnJzsKCiAgICBpZiAoZW1wdHkoJGZvcm1fZGF0YVsnbmFtZSddKSkgJGVycm9yc1tdID0gIuWQjeWJjeOCkuWFpeWKm+OBl+OBpuOBj+OBoOOBleOBhOOAgiI7CiAgICBlbHNlaWYgKG1iX3N0cmxlbigkZm9ybV9kYXRhWyduYW1lJ10pID4gTUFYX05BTUVfTEVOR1RIKSAkZXJyb3JzW10gPSAi5ZCN5YmN44GvIiAuIE1BWF9OQU1FX0xFTkdUSCAuICLmloflrZfku6XlhoXjgaflhaXlipvjgZfjgabjgY/jgaDjgZXjgYTjgIIiOwogICAgaWYgKGVtcHR5KCRmb3JtX2RhdGFbJ21lc3NhZ2VfdGV4dCddKSkgJGVycm9yc1tdID0gIumAo+e1oeS6i+mgheOCkuWFpeWKm+OBl+OBpuOBj+OBoOOBleOBhOOAgiI7CiAgICBlbHNlaWYgKG1iX3N0cmxlbigkZm9ybV9kYXRhWydtZXNzYWdlX3RleHQnXSkgPiBNQVhfTUVTU0FHRV9MRU5HVEgpICRlcnJvcnNbXSA9ICLpgKPntaHkuovpoIXjga8iIC4gTUFYX01FU1NBR0VfTEVOR1RIIC4gIuaWh+Wtl+S7peWGheOBp+WFpeWKm+OBl+OBpuOBj+OBoOOBleOBhOOAgiI7CgogICAgaWYgKGVtcHR5KCRlcnJvcnMpKSB7CiAgICAgICAgJGlkID0gdW5pcWlkKCdjb250YWN0XycsIHRydWUpOwogICAgICAgICR0aW1lc3RhbXAgPSBkYXRlKCJZLW0tZCBIOmk6cyIpOwogICAgICAgICRuYW1lX3RvX3NhdmUgPSBzdHJfcmVwbGFjZShbIlx0IiwgIlxyIiwgIlxuIl0sICIgIiwgJGZvcm1fZGF0YVsnbmFtZSddKTsKICAgICAgICAkbWVzc2FnZV90b19zYXZlID0gc3RyX3JlcGxhY2UoWyJcclxuIiwgIlxyIiwgIlxuIl0sICI8YnJfdGVtcD4iLCAkZm9ybV9kYXRhWydtZXNzYWdlX3RleHQnXSk7CiAgICAgICAgJG1lc3NhZ2VfdG9fc2F2ZSA9IHN0cl9yZXBsYWNlKCJcdCIsICIgICAgIiwgJG1lc3NhZ2VfdG9fc2F2ZSk7CiAgICAgICAgJHN0YXR1cyA9IFNUQVRVU19VTkNPTkZJUk1FRDsKCiAgICAgICAgJGRhdGFfbGluZSA9ICRpZCAuICJcdCIgLiAkdGltZXN0YW1wIC4gIlx0IiAuICRuYW1lX3RvX3NhdmUgLiAiXHQiIC4gJG1lc3NhZ2VfdG9fc2F2ZSAuICJcdCIgLiAkc3RhdHVzIC4gUEhQX0VPTDsKCiAgICAgICAgJGZwID0gZm9wZW4oQ09OVEFDVFNfRklMRV9QQVRILCAnYWInKTsKICAgICAgICBpZiAoJGZwKSB7CiAgICAgICAgICAgIGlmIChmbG9jaygkZnAsIExPQ0tfRVgpKSB7CiAgICAgICAgICAgICAgICBpZiAoZndyaXRlKCRmcCwgJGRhdGFfbGluZSkgPT09IGZhbHNlKSB7CiAgICAgICAgICAgICAgICAgICAgJF9TRVNTSU9OWydtZXNzYWdlX2Rpc3BsYXknXSA9ICLpgKPntaHkuovpoIXjga7kv53lrZjjgavlpLHmlZfjgZfjgb7jgZfjgZ/jgIIo5pu444GN6L6844G/44Ko44Op44O8KSI7ICRfU0VTU0lPTlsnbWVzc2FnZV90eXBlJ10gPSAiZXJyb3IiOwogICAgICAgICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgICAgICAgICAkX1NFU1NJT05bJ21lc3NhZ2VfZGlzcGxheSddID0gIumAo+e1oeS6i+mgheOBjOi/veWKoOOBleOCjOOBvuOBl+OBn++8gSI7ICRfU0VTU0lPTlsnbWVzc2FnZV90eXBlJ10gPSAic3VjY2VzcyI7CiAgICAgICAgICAgICAgICAgICAgJGZvcm1fZGF0YSA9IFsnbmFtZScgPT4gJycsICdtZXNzYWdlX3RleHQnID0+ICcnXTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIGZsb2NrKCRmcCwgTE9DS19VTik7CiAgICAgICAgICAgIH0gZWxzZSB7ICRfU0VTU0lPTlsnbWVzc2FnZV9kaXNwbGF5J10gPSAi6YCj57Wh5LqL6aCF44Gu5L+d5a2Y44Gr5aSx5pWX44GX44G+44GX44Gf44CCKOODreODg+OCr+WPluW+l+WkseaVlykiOyAkX1NFU1NJT05bJ21lc3NhZ2VfdHlwZSddID0gImVycm9yIjsgfQogICAgICAgICAgICBmY2xvc2UoJGZwKTsKICAgICAgICB9IGVsc2UgeyAkX1NFU1NJT05bJ21lc3NhZ2VfZGlzcGxheSddID0gIumAo+e1oeS6i+mgheOBruS/neWtmOOBq+WkseaVl+OBl+OBvuOBl+OBn+OAgijjg5XjgqHjgqTjg6vjgqrjg7zjg5fjg7PlpLHmlZcpIjsgJF9TRVNTSU9OWydtZXNzYWdlX3R5cGUnXSA9ICJlcnJvciI7IH0KICAgICAgICBoZWFkZXIoIkxvY2F0aW9uOiAiIC4gJF9TRVJWRVJbJ1BIUF9TRUxGJ10pOyBleGl0OwogICAgfSBlbHNlIHsKICAgICAgICAkX1NFU1NJT05bJ21lc3NhZ2VfZGlzcGxheSddID0gaW1wbG9kZSgiPGJyPiIsICRlcnJvcnMpOyAkX1NFU1NJT05bJ21lc3NhZ2VfdHlwZSddID0gImVycm9yIjsKICAgICAgICAkX1NFU1NJT05bJ2Zvcm1fZGF0YSddID0gJGZvcm1fZGF0YTsKICAgICAgICBoZWFkZXIoIkxvY2F0aW9uOiAiIC4gJF9TRVJWRVJbJ1BIUF9TRUxGJ10pOyBleGl0OwogICAgfQp9CgovLyDpgKPntaHkuovpoIXjga7liYrpmaTlh6bnkIYgKOWkieabtOOBquOBlykKaWYgKCRfU0VSVkVSWyJSRVFVRVNUX01FVEhPRCJdID09ICJQT1NUIiAmJiBpc3NldCgkX1BPU1RbJ2FjdGlvbiddKSAmJiAkX1BPU1RbJ2FjdGlvbiddID09ICdkZWxldGVfY29udGFjdCcgJiYgaXNzZXQoJF9QT1NUWydjb250YWN0X2lkJ10pKSB7CiAgICAkY29udGFjdF9pZF90b19kZWxldGUgPSB0cmltKCRfUE9TVFsnY29udGFjdF9pZCddKTsKICAgICR0ZW1wX2ZpbGVfcGF0aCA9IENPTlRBQ1RTX0ZJTEVfRElSIC4gJ2NvbnRhY3RzX3RlbXAudHN2JzsKICAgICRkZWxldGVkID0gZmFsc2U7CiAgICBpZiAoZmlsZV9leGlzdHMoQ09OVEFDVFNfRklMRV9QQVRIKSkgewogICAgICAgICRmcF9yZWFkID0gZm9wZW4oQ09OVEFDVFNfRklMRV9QQVRILCAncmInKTsgJGZwX3dyaXRlID0gZm9wZW4oJHRlbXBfZmlsZV9wYXRoLCAnd2InKTsKICAgICAgICBpZiAoJGZwX3JlYWQgJiYgJGZwX3dyaXRlKSB7CiAgICAgICAgICAgIGlmIChmbG9jaygkZnBfcmVhZCwgTE9DS19TSCkgJiYgZmxvY2soJGZwX3dyaXRlLCBMT0NLX0VYKSkgewogICAgICAgICAgICAgICAgd2hpbGUgKCgkbGluZSA9IGZnZXRzKCRmcF9yZWFkKSkgIT09IGZhbHNlKSB7CiAgICAgICAgICAgICAgICAgICAgJHBhcnRzID0gZXhwbG9kZSgiXHQiLCAkbGluZSk7CiAgICAgICAgICAgICAgICAgICAgaWYgKGlzc2V0KCRwYXJ0c1swXSkgJiYgdHJpbSgkcGFydHNbMF0pID09PSAkY29udGFjdF9pZF90b19kZWxldGUpIHsgJGRlbGV0ZWQgPSB0cnVlOyB9CiAgICAgICAgICAgICAgICAgICAgZWxzZSB7IGZ3cml0ZSgkZnBfd3JpdGUsICRsaW5lKTsgfQogICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgZmxvY2soJGZwX3dyaXRlLCBMT0NLX1VOKTsgZmxvY2soJGZwX3JlYWQsIExPQ0tfVU4pOwogICAgICAgICAgICB9IGVsc2UgeyAkX1NFU1NJT05bJ21lc3NhZ2VfZGlzcGxheSddID0gIuODleOCoeOCpOODq+OBruODreODg+OCr+OBq+WkseaVl+OBl+OBvuOBl+OBnyjliYrpmaTmmYIp44CCIjsgJF9TRVNTSU9OWydtZXNzYWdlX3R5cGUnXSA9ICJlcnJvciI7IH0KICAgICAgICAgICAgZmNsb3NlKCRmcF9yZWFkKTsgZmNsb3NlKCRmcF93cml0ZSk7CiAgICAgICAgICAgIGlmICgkZGVsZXRlZCkgewogICAgICAgICAgICAgICAgaWYgKHJlbmFtZSgkdGVtcF9maWxlX3BhdGgsIENPTlRBQ1RTX0ZJTEVfUEFUSCkpIHsgJF9TRVNTSU9OWydtZXNzYWdlX2Rpc3BsYXknXSA9ICLpgKPntaHkuovpoIXjgYzliYrpmaTjgZXjgozjgb7jgZfjgZ/jgIIiOyAkX1NFU1NJT05bJ21lc3NhZ2VfdHlwZSddID0gInN1Y2Nlc3MiOyB9CiAgICAgICAgICAgICAgICBlbHNlIHsgJF9TRVNTSU9OWydtZXNzYWdlX2Rpc3BsYXknXSA9ICLjg5XjgqHjgqTjg6vmm7TmlrDjgavlpLHmlZfjgZfjgb7jgZfjgZ8o5YmK6Zmk5pmCKeOAgiI7ICRfU0VTU0lPTlsnbWVzc2FnZV90eXBlJ10gPSAiZXJyb3IiOyBAdW5saW5rKCR0ZW1wX2ZpbGVfcGF0aCk7IH0KICAgICAgICAgICAgfSBlbHNlIHsgJF9TRVNTSU9OWydtZXNzYWdlX2Rpc3BsYXknXSA9ICLliYrpmaTlr77osaHjga7pgKPntaHkuovpoIXjgYzopovjgaTjgYvjgorjgb7jgZvjgpPjgafjgZfjgZ/jgIIiOyAkX1NFU1NJT05bJ21lc3NhZ2VfdHlwZSddID0gImVycm9yIjsgQHVubGluaygkdGVtcF9maWxlX3BhdGgpOyB9CiAgICAgICAgfSBlbHNlIHsgaWYoJGZwX3JlYWQpIGZjbG9zZSgkZnBfcmVhZCk7IGlmKCRmcF93cml0ZSkgZmNsb3NlKCRmcF93cml0ZSk7IEB1bmxpbmsoJHRlbXBfZmlsZV9wYXRoKTsgJF9TRVNTSU9OWydtZXNzYWdlX2Rpc3BsYXknXSA9ICLjg5XjgqHjgqTjg6vmk43kvZzjgavlpLHmlZfjgZfjgb7jgZfjgZ8o5YmK6Zmk5pmCKeOAgiI7ICRfU0VTU0lPTlsnbWVzc2FnZV90eXBlJ10gPSAiZXJyb3IiOyB9CiAgICB9IGVsc2UgeyAkX1NFU1NJT05bJ21lc3NhZ2VfZGlzcGxheSddID0gIumAo+e1oeS6i+mgheODleOCoeOCpOODq+OBjOimi+OBpOOBi+OCiuOBvuOBm+OCk+OAgiI7ICRfU0VTU0lPTlsnbWVzc2FnZV90eXBlJ10gPSAiZXJyb3IiOyB9CiAgICBoZWFkZXIoIkxvY2F0aW9uOiAiIC4gJF9TRVJWRVJbJ1BIUF9TRUxGJ10pOyBleGl0Owp9CgovLyDnorroqo3nirbmhYvjga7jg4jjgrDjg6vlh6bnkIYgKOWkieabtOOBquOBlykKaWYgKCRfU0VSVkVSWyJSRVFVRVNUX01FVEhPRCJdID09ICJQT1NUIiAmJiBpc3NldCgkX1BPU1RbJ2FjdGlvbiddKSAmJiAkX1BPU1RbJ2FjdGlvbiddID09ICd0b2dnbGVfY29uZmlybScgJiYgaXNzZXQoJF9QT1NUWydjb250YWN0X2lkJ10pKSB7CiAgICAkY29udGFjdF9pZF90b190b2dnbGUgPSB0cmltKCRfUE9TVFsnY29udGFjdF9pZCddKTsKICAgICR0ZW1wX2ZpbGVfcGF0aCA9IENPTlRBQ1RTX0ZJTEVfRElSIC4gJ2NvbnRhY3RzX3RlbXAudHN2JzsKICAgICR0b2dnbGVkID0gZmFsc2U7CiAgICBpZiAoZmlsZV9leGlzdHMoQ09OVEFDVFNfRklMRV9QQVRIKSkgewogICAgICAgICRmcF9yZWFkID0gZm9wZW4oQ09OVEFDVFNfRklMRV9QQVRILCAncmInKTsgJGZwX3dyaXRlID0gZm9wZW4oJHRlbXBfZmlsZV9wYXRoLCAnd2InKTsKICAgICAgICBpZiAoJGZwX3JlYWQgJiYgJGZwX3dyaXRlKSB7CiAgICAgICAgICAgIGlmIChmbG9jaygkZnBfcmVhZCwgTE9DS19TSCkgJiYgZmxvY2soJGZwX3dyaXRlLCBMT0NLX0VYKSkgewogICAgICAgICAgICAgICAgd2hpbGUgKCgkbGluZSA9IGZnZXRzKCRmcF9yZWFkKSkgIT09IGZhbHNlKSB7CiAgICAgICAgICAgICAgICAgICAgJHBhcnRzID0gZXhwbG9kZSgiXHQiLCBydHJpbSgkbGluZSwgUEhQX0VPTCkpOwogICAgICAgICAgICAgICAgICAgIGlmIChpc3NldCgkcGFydHNbMF0pICYmIHRyaW0oJHBhcnRzWzBdKSA9PT0gJGNvbnRhY3RfaWRfdG9fdG9nZ2xlKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICRjdXJyZW50X3N0YXR1cyA9IGlzc2V0KCRwYXJ0c1s0XSkgPyAoaW50KSRwYXJ0c1s0XSA6IFNUQVRVU19VTkNPTkZJUk1FRDsKICAgICAgICAgICAgICAgICAgICAgICAgJG5ld19zdGF0dXMgPSAoJGN1cnJlbnRfc3RhdHVzID09IFNUQVRVU19DT05GSVJNRUQpID8gU1RBVFVTX1VOQ09ORklSTUVEIDogU1RBVFVTX0NPTkZJUk1FRDsKICAgICAgICAgICAgICAgICAgICAgICAgJHBhcnRzWzRdID0gJG5ld19zdGF0dXM7CiAgICAgICAgICAgICAgICAgICAgICAgICRsaW5lID0gaW1wbG9kZSgiXHQiLCAkcGFydHMpIC4gUEhQX0VPTDsKICAgICAgICAgICAgICAgICAgICAgICAgJHRvZ2dsZWQgPSB0cnVlOwogICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICBmd3JpdGUoJGZwX3dyaXRlLCAkbGluZSk7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICBmbG9jaygkZnBfd3JpdGUsIExPQ0tfVU4pOyBmbG9jaygkZnBfcmVhZCwgTE9DS19VTik7CiAgICAgICAgICAgIH0gZWxzZSB7ICRfU0VTU0lPTlsnbWVzc2FnZV9kaXNwbGF5J10gPSAi44OV44Kh44Kk44Or44Gu44Ot44OD44Kv44Gr5aSx5pWX44GX44G+44GX44GfKOeKtuaFi+abtOaWsOaZginjgIIiOyAkX1NFU1NJT05bJ21lc3NhZ2VfdHlwZSddID0gImVycm9yIjsgfQogICAgICAgICAgICBmY2xvc2UoJGZwX3JlYWQpOyBmY2xvc2UoJGZwX3dyaXRlKTsKICAgICAgICAgICAgaWYgKCR0b2dnbGVkKSB7CiAgICAgICAgICAgICAgICBpZiAocmVuYW1lKCR0ZW1wX2ZpbGVfcGF0aCwgQ09OVEFDVFNfRklMRV9QQVRIKSkgeyAkX1NFU1NJT05bJ21lc3NhZ2VfZGlzcGxheSddID0gIueiuuiqjeeKtuaFi+OBjOabtOaWsOOBleOCjOOBvuOBl+OBn+OAgiI7ICRfU0VTU0lPTlsnbWVzc2FnZV90eXBlJ10gPSAic3VjY2VzcyI7IH0KICAgICAgICAgICAgICAgIGVsc2UgeyAkX1NFU1NJT05bJ21lc3NhZ2VfZGlzcGxheSddID0gIuODleOCoeOCpOODq+abtOaWsOOBq+WkseaVl+OBl+OBvuOBl+OBnyjnirbmhYvmm7TmlrDmmYIp44CCIjsgJF9TRVNTSU9OWydtZXNzYWdlX3R5cGUnXSA9ICJlcnJvciI7IEB1bmxpbmsoJHRlbXBfZmlsZV9wYXRoKTsgfQogICAgICAgICAgICB9IGVsc2UgeyAkX1NFU1NJT05bJ21lc3NhZ2VfZGlzcGxheSddID0gIuWvvuixoeOBrumAo+e1oeS6i+mgheOBjOimi+OBpOOBi+OCiuOBvuOBm+OCk+OBp+OBl+OBnyjnirbmhYvmm7TmlrDmmYIp44CCIjsgJF9TRVNTSU9OWydtZXNzYWdlX3R5cGUnXSA9ICJlcnJvciI7IEB1bmxpbmsoJHRlbXBfZmlsZV9wYXRoKTsgfQogICAgICAgIH0gZWxzZSB7IGlmKCRmcF9yZWFkKSBmY2xvc2UoJGZwX3JlYWQpOyBpZigkZnBfd3JpdGUpIGZjbG9zZSgkZnBfd3JpdGUpOyBAdW5saW5rKCR0ZW1wX2ZpbGVfcGF0aCk7ICRfU0VTU0lPTlsnbWVzc2FnZV9kaXNwbGF5J10gPSAi44OV44Kh44Kk44Or5pON5L2c44Gr5aSx5pWX44GX44G+44GX44GfKOeKtuaFi+abtOaWsOaZginjgIIiOyAkX1NFU1NJT05bJ21lc3NhZ2VfdHlwZSddID0gImVycm9yIjsgfQogICAgfSBlbHNlIHsgJF9TRVNTSU9OWydtZXNzYWdlX2Rpc3BsYXknXSA9ICLpgKPntaHkuovpoIXjg5XjgqHjgqTjg6vjgYzopovjgaTjgYvjgorjgb7jgZvjgpPjgIIiOyAkX1NFU1NJT05bJ21lc3NhZ2VfdHlwZSddID0gImVycm9yIjsgfQogICAgaGVhZGVyKCJMb2NhdGlvbjogIiAuICRfU0VSVkVSWydQSFBfU0VMRiddKTsgZXhpdDsKfQoKaWYgKGlzc2V0KCRfU0VTU0lPTlsnbWVzc2FnZV9kaXNwbGF5J10pKSB7ICRwYWdlX21lc3NhZ2UgPSAkX1NFU1NJT05bJ21lc3NhZ2VfZGlzcGxheSddOyAkcGFnZV9tZXNzYWdlX3R5cGUgPSAkX1NFU1NJT05bJ21lc3NhZ2VfdHlwZSddOyB1bnNldCgkX1NFU1NJT05bJ21lc3NhZ2VfZGlzcGxheSddKTsgdW5zZXQoJF9TRVNTSU9OWydtZXNzYWdlX3R5cGUnXSk7IH0KaWYgKGlzc2V0KCRfU0VTU0lPTlsnZm9ybV9kYXRhJ10pKSB7ICRmb3JtX2RhdGEgPSAkX1NFU1NJT05bJ2Zvcm1fZGF0YSddOyB1bnNldCgkX1NFU1NJT05bJ2Zvcm1fZGF0YSddKTsgfQoKZnVuY3Rpb24gZGlzcGxheV9jb250YWN0cygpIHsKICAgIGlmICghZmlsZV9leGlzdHMoQ09OVEFDVFNfRklMRV9QQVRIKSB8fCBmaWxlc2l6ZShDT05UQUNUU19GSUxFX1BBVEgpID09PSAwKSB7CiAgICAgICAgZWNobyAiPHA+44G+44Gg6YCj57Wh5LqL6aCF44Gv44GC44KK44G+44Gb44KT44CCPC9wPiI7IHJldHVybjsKICAgIH0KICAgICRsaW5lc19hcnJheSA9IGZpbGUoQ09OVEFDVFNfRklMRV9QQVRILCBGSUxFX0lHTk9SRV9ORVdfTElORVMgfCBGSUxFX1NLSVBfRU1QVFlfTElORVMpOwogICAgaWYgKCRsaW5lc19hcnJheSA9PT0gZmFsc2UpIHsgZWNobyAiPHA+6YCj57Wh5LqL6aCF44OV44Kh44Kk44Or44Gu6Kqt44G/6L6844G/44Gr5aSx5pWX44GX44G+44GX44Gf44CCPC9wPiI7IHJldHVybjsgfQogICAgJGxpbmVzX2FycmF5ID0gYXJyYXlfcmV2ZXJzZSgkbGluZXNfYXJyYXkpOwoKICAgICRjb250YWN0c19odG1sID0gIiI7CiAgICBmb3JlYWNoICgkbGluZXNfYXJyYXkgYXMgJGxpbmUpIHsKICAgICAgICAkcGFydHMgPSBleHBsb2RlKCJcdCIsICRsaW5lLCA1KTsKICAgICAgICBpZiAoY291bnQoJHBhcnRzKSA+PSA0KSB7CiAgICAgICAgICAgICRpZCA9IGh0bWxzcGVjaWFsY2hhcnMoJHBhcnRzWzBdLCBFTlRfUVVPVEVTLCAnVVRGLTgnKTsKICAgICAgICAgICAgJHRpbWVzdGFtcCA9IGh0bWxzcGVjaWFsY2hhcnMoJHBhcnRzWzFdLCBFTlRfUVVPVEVTLCAnVVRGLTgnKTsKICAgICAgICAgICAgJG5hbWUgPSBodG1sc3BlY2lhbGNoYXJzKCRwYXJ0c1syXSwgRU5UX1FVT1RFUywgJ1VURi04Jyk7CiAgICAgICAgICAgICRtZXNzYWdlX3RleHRfcmF3ID0gc3RyX3JlcGxhY2UoIjxicl90ZW1wPiIsICJcbiIsICRwYXJ0c1szXSk7CiAgICAgICAgICAgICRtZXNzYWdlX3RleHRfZGlzcGxheSA9IG5sMmJyKGh0bWxzcGVjaWFsY2hhcnMoJG1lc3NhZ2VfdGV4dF9yYXcsIEVOVF9RVU9URVMsICdVVEYtOCcpKTsKICAgICAgICAgICAgJHN0YXR1cyA9IGlzc2V0KCRwYXJ0c1s0XSkgPyAoaW50KSRwYXJ0c1s0XSA6IFNUQVRVU19VTkNPTkZJUk1FRDsKCiAgICAgICAgICAgICRjb25maXJtX2J1dHRvbl90ZXh0ID0gIiI7CiAgICAgICAgICAgICRjb25maXJtX2RpYWxvZ19tZXNzYWdlID0gIiI7CiAgICAgICAgICAgICRidXR0b25fY29uZmlybV9jbGFzcyA9ICIiOwoKICAgICAgICAgICAgaWYgKCRzdGF0dXMgPT0gU1RBVFVTX0NPTkZJUk1FRCkgewogICAgICAgICAgICAgICAgJGNvbmZpcm1fYnV0dG9uX3RleHQgPSAi56K66KqN5riI44G/IjsKICAgICAgICAgICAgICAgICRjb25maXJtX2RpYWxvZ19tZXNzYWdlID0gIuOBk+OBrumAo+e1oeS6i+mgheOCkuacqueiuuiqjeOBq+aIu+OBl+OBvuOBmeOBi++8nyI7CiAgICAgICAgICAgICAgICAkYnV0dG9uX2NvbmZpcm1fY2xhc3MgPSAiaXMtY29uZmlybWVkIjsKICAgICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgICAgICRjb25maXJtX2J1dHRvbl90ZXh0ID0gIueiuuiqjea4iOOBv+OBq+OBmeOCiyI7CiAgICAgICAgICAgICAgICAkY29uZmlybV9kaWFsb2dfbWVzc2FnZSA9ICLjgZPjga7pgKPntaHkuovpoIXjgpLnorroqo3muIjjgb/jgavjgZfjgb7jgZnjgYvvvJ8iOwogICAgICAgICAgICAgICAgJGJ1dHRvbl9jb25maXJtX2NsYXNzID0gImlzLXVuY29uZmlybWVkIjsKICAgICAgICAgICAgfQoKICAgICAgICAgICAgJGl0ZW1fY2xhc3MgPSAoJHN0YXR1cyA9PSBTVEFUVVNfQ09ORklSTUVEKSA/ICJjb250YWN0LWl0ZW0gY29uZmlybWVkIiA6ICJjb250YWN0LWl0ZW0iOwoKICAgICAgICAgICAgJGNvbnRhY3RzX2h0bWwgLj0gIjxkaXYgY2xhc3M9JyIgLiAkaXRlbV9jbGFzcyAuICInPiIgLgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICI8ZGl2IGNsYXNzPSdjb250YWN0LWhlYWRlcic+IiAuCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjxzdHJvbmc+IiAuICRuYW1lIC4gIjwvc3Ryb25nPiIgLgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICI8c21hbGwgY2xhc3M9J2NvbnRhY3QtZGF0ZSc+ICgiIC4gJHRpbWVzdGFtcCAuICIpPC9zbWFsbD4iIC4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiPGZvcm0gbWV0aG9kPSdwb3N0JyBhY3Rpb249JyIgLiBodG1sc3BlY2lhbGNoYXJzKCRfU0VSVkVSWyJQSFBfU0VMRiJdLCBFTlRfUVVPVEVTLCAnVVRGLTgnKSAuICInIGNsYXNzPSdjb25maXJtLWJ1dHRvbi1mb3JtJyBvbnN1Ym1pdD0ncmV0dXJuIGNvbmZpcm0oXCIiIC4gaHRtbHNwZWNpYWxjaGFycygkY29uZmlybV9kaWFsb2dfbWVzc2FnZSwgRU5UX1FVT1RFUywgJ1VURi04JykgLiAiXCIpOyc+IiAuCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjxpbnB1dCB0eXBlPSdoaWRkZW4nIG5hbWU9J2FjdGlvbicgdmFsdWU9J3RvZ2dsZV9jb25maXJtJz4iIC4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiPGlucHV0IHR5cGU9J2hpZGRlbicgbmFtZT0nY29udGFjdF9pZCcgdmFsdWU9JyIgLiAkaWQgLiAiJz4iIC4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiPGJ1dHRvbiB0eXBlPSdzdWJtaXQnIGNsYXNzPSdidXR0b24tY29uZmlybSAiIC4gJGJ1dHRvbl9jb25maXJtX2NsYXNzIC4gIic+IiAuICRjb25maXJtX2J1dHRvbl90ZXh0IC4gIjwvYnV0dG9uPiIgLgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICI8L2Zvcm0+IiAuCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjxmb3JtIG1ldGhvZD0ncG9zdCcgYWN0aW9uPSciIC4gaHRtbHNwZWNpYWxjaGFycygkX1NFUlZFUlsiUEhQX1NFTEYiXSwgRU5UX1FVT1RFUywgJ1VURi04JykgLiAiJyBjbGFzcz0nZGVsZXRlLWJ1dHRvbi1mb3JtJyBvbnN1Ym1pdD0ncmV0dXJuIGNvbmZpcm0oXCLjgZPjga7pgKPntaHkuovpoIXjgpLliYrpmaTjgZfjgabjgoLjgojjgo3jgZfjgYTjgafjgZnjgYvvvJ9cIik7Jz4iIC4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiPGlucHV0IHR5cGU9J2hpZGRlbicgbmFtZT0nYWN0aW9uJyB2YWx1ZT0nZGVsZXRlX2NvbnRhY3QnPiIgLgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICI8aW5wdXQgdHlwZT0naGlkZGVuJyBuYW1lPSdjb250YWN0X2lkJyB2YWx1ZT0nIiAuICRpZCAuICInPiIgLgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICI8YnV0dG9uIHR5cGU9J3N1Ym1pdCcgY2xhc3M9J2J1dHRvbi1kZWxldGUtaXRlbSc+5YmK6ZmkPC9idXR0b24+IiAuCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjwvZm9ybT4iIC4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiPC9kaXY+IiAuCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjxwPiIgLiAkbWVzc2FnZV90ZXh0X2Rpc3BsYXkgLiAiPC9wPiIgLgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICI8L2Rpdj4iOwogICAgICAgIH0KICAgIH0KICAgIGVjaG8gJGNvbnRhY3RzX2h0bWw7Cn0KPz4KPCFET0NUWVBFIGh0bWw+CjxodG1sIGxhbmc9ImphIj4KPGhlYWQ+CiAgICA8bWV0YSBjaGFyc2V0PSJVVEYtOCI+CiAgICA8bWV0YSBuYW1lPSJ2aWV3cG9ydCIgY29udGVudD0id2lkdGg9ZGV2aWNlLXdpZHRoLCBpbml0aWFsLXNjYWxlPTEuMCI+CiAgICA8dGl0bGU+6YCj57Wh5LqL6aCFPC90aXRsZT4KICAgIDxzdHlsZT4KICAgICAgICBib2R5IHsgZm9udC1mYW1pbHk6IHNhbnMtc2VyaWY7IGxpbmUtaGVpZ2h0OiAxLjY7IG1hcmdpbjogMjBweDsgYmFja2dyb3VuZC1jb2xvcjogI2Y0ZjRmNDsgY29sb3I6ICMzMzM7IH0KICAgICAgICAuY29udGFpbmVyIHsgbWF4LXdpZHRoOiA2MDBweDsgbWFyZ2luOiBhdXRvOyBiYWNrZ3JvdW5kOiAjZmZmOyBwYWRkaW5nOiAyMHB4OyBib3JkZXItcmFkaXVzOiA4cHg7IGJveC1zaGFkb3c6IDAgMCAxMHB4IHJnYmEoMCwwLDAsMC4xKTsgfQogICAgICAgIGgxIHsgdGV4dC1hbGlnbjogY2VudGVyOyBjb2xvcjogIzMzMzsgfQogICAgICAgIGxhYmVsIHsgZGlzcGxheTogYmxvY2s7IG1hcmdpbi1ib3R0b206IDhweDsgZm9udC13ZWlnaHQ6IGJvbGQ7IH0KICAgICAgICBpbnB1dFt0eXBlPSJ0ZXh0Il0sIHRleHRhcmVhIHsKICAgICAgICAgICAgd2lkdGg6IGNhbGMoMTAwJSAtIDIycHgpOyBwYWRkaW5nOiAxMHB4OyBtYXJnaW4tYm90dG9tOiAxNXB4OyBib3JkZXI6IDFweCBzb2xpZCAjZGRkOyBib3JkZXItcmFkaXVzOiA0cHg7IGJveC1zaXppbmc6IGJvcmRlci1ib3g7CiAgICAgICAgfQogICAgICAgIHRleHRhcmVhIHsgbWluLWhlaWdodDogNjBweDsgcmVzaXplOiB2ZXJ0aWNhbDsgfQogICAgICAgIGJ1dHRvblt0eXBlPSJzdWJtaXQiXSB7IGJhY2tncm91bmQtY29sb3I6ICMyOGE3NDU7IGNvbG9yOiB3aGl0ZTsgcGFkZGluZzogMTBweCAxNXB4OyBib3JkZXI6IG5vbmU7IGJvcmRlci1yYWRpdXM6IDRweDsgY3Vyc29yOiBwb2ludGVyOyBmb250LXNpemU6IDE2cHg7IH0KICAgICAgICBidXR0b25bdHlwZT0ic3VibWl0Il06aG92ZXIgeyBiYWNrZ3JvdW5kLWNvbG9yOiAjMjE4ODM4OyB9CgogICAgICAgIC5tZXNzYWdlIHsgcGFkZGluZzogMTBweDsgbWFyZ2luLWJvdHRvbTogMTVweDsgYm9yZGVyLXJhZGl1czogNHB4OyBib3JkZXItd2lkdGg6IDFweDsgYm9yZGVyLXN0eWxlOiBzb2xpZDsgfQogICAgICAgIC5zdWNjZXNzIHsgYmFja2dyb3VuZC1jb2xvcjogI2Q0ZWRkYTsgY29sb3I6ICMxNTU3MjQ7IGJvcmRlci1jb2xvcjogI2MzZTZjYjsgfQogICAgICAgIC5lcnJvciB7IGJhY2tncm91bmQtY29sb3I6ICNmOGQ3ZGE7IGNvbG9yOiAjNzIxYzI0OyBib3JkZXItY29sb3I6ICNmNWM2Y2I7IH0KICAgICAgICAuaW5mbyB7IGJhY2tncm91bmQtY29sb3I6ICNkMWVjZjE7IGNvbG9yOiAjMGM1NDYwOyBib3JkZXItY29sb3I6ICNiZWU1ZWI7IH0KCiAgICAgICAgLmNvbnRhY3RzLWRpc3BsYXkgeyBtYXJnaW4tdG9wOiAzMHB4OyBib3JkZXItdG9wOiAxcHggc29saWQgI2VlZTsgcGFkZGluZy10b3A6IDIwcHg7IH0KICAgICAgICAuY29udGFjdC1pdGVtIHsKICAgICAgICAgICAgcGFkZGluZzogMTBweCAxNXB4OyBib3JkZXItd2lkdGg6IDFweCAxcHggMXB4IDVweDsgYm9yZGVyLXN0eWxlOiBzb2xpZDsgYm9yZGVyLWNvbG9yOiAjZWVlICNlZWUgI2VlZTsgLyog5Y+z44Go5LiL44Gv6JaE44GE44Kw44Os44O844CB5bem44Gv54q25oWL44Gr5b+c44GY44GmICovCiAgICAgICAgICAgIGJvcmRlci1yYWRpdXM6IDRweDsgbWFyZ2luLWJvdHRvbTogMTBweDsKICAgICAgICAgICAgdHJhbnNpdGlvbjogYmFja2dyb3VuZC1jb2xvciAwLjNzIGVhc2UsIGJvcmRlci1sZWZ0LWNvbG9yIDAuM3MgZWFzZTsKICAgICAgICB9CiAgICAgICAgLmNvbnRhY3QtaXRlbS5jb25maXJtZWQgewogICAgICAgICAgICBiYWNrZ3JvdW5kLWNvbG9yOiAjZTZmZmU2OyAvKiDnorroqo3muIjjgb/jga7og4zmma/oibIgKOiWhOOBhOe3kSkgKi8KICAgICAgICAgICAgYm9yZGVyLWxlZnQtY29sb3I6ICMyOGE3NDU7IC8qIOe3keOBruOCouOCr+OCu+ODs+ODiOODnOODvOODgOODvCAqLwogICAgICAgIH0KICAgICAgICAuY29udGFjdC1pdGVtOm5vdCguY29uZmlybWVkKSB7CiAgICAgICAgICAgIGJhY2tncm91bmQtY29sb3I6ICNlMGY3ZmE7IC8qIOacqueiuuiqjeOBruiDjOaZr+iJsiAo6JaE44GE5rC06ImyKSAqLwogICAgICAgICAgICBib3JkZXItbGVmdC1jb2xvcjogIzAwN2JmZjsgLyog6Z2S44Gu44Ki44Kv44K744Oz44OI44Oc44O844OA44O8ICovCiAgICAgICAgfQoKICAgICAgICAuY29udGFjdC1oZWFkZXIgeyBkaXNwbGF5OiBmbGV4OyBqdXN0aWZ5LWNvbnRlbnQ6IHNwYWNlLWJldHdlZW47IGFsaWduLWl0ZW1zOiBjZW50ZXI7IG1hcmdpbi1ib3R0b206IDhweDsgZmxleC13cmFwOiB3cmFwOyB9CiAgICAgICAgLmNvbnRhY3QtaXRlbSBzdHJvbmcgeyBtYXJnaW4tcmlnaHQ6IDVweDsgfQogICAgICAgIC5jb250YWN0LWl0ZW0gLmNvbnRhY3QtZGF0ZSB7IGZvbnQtc2l6ZTogMC45ZW07IGNvbG9yOiAjNzc3OyBmbGV4LWdyb3c6IDE7IG1pbi13aWR0aDogMTUwcHg7IH0KICAgICAgICAuY29udGFjdC1pdGVtIHAgeyBtYXJnaW4tdG9wOiA1cHg7IG1hcmdpbi1ib3R0b206IDA7IHdvcmQtd3JhcDogYnJlYWstd29yZDsgYm9yZGVyOiAxcHggc29saWQgI2NjYzsgcGFkZGluZzogOHB4IDEwcHg7IGJhY2tncm91bmQtY29sb3I6ICNmZmY7IGJvcmRlci1yYWRpdXM6IDNweDsgfQogICAgICAgIC5kZWxldGUtYnV0dG9uLWZvcm0sIC5jb25maXJtLWJ1dHRvbi1mb3JtIHsgZGlzcGxheTogaW5saW5lLWJsb2NrOyBtYXJnaW4tbGVmdDogNXB4OyB9CiAgICAgICAgLmJ1dHRvbi1kZWxldGUtaXRlbSwgLmJ1dHRvbi1jb25maXJtIHsgY29sb3I6IHdoaXRlOyBwYWRkaW5nOiAzcHggOHB4OyBib3JkZXI6IG5vbmU7IGJvcmRlci1yYWRpdXM6IDNweDsgY3Vyc29yOiBwb2ludGVyOyBmb250LXNpemU6IDAuODVlbTsgdHJhbnNpdGlvbjogYmFja2dyb3VuZC1jb2xvciAwLjJzIGVhc2U7IH0KICAgICAgICAuYnV0dG9uLWRlbGV0ZS1pdGVtIHsgYmFja2dyb3VuZC1jb2xvcjogI2RjMzU0NTsgfQogICAgICAgIC5idXR0b24tZGVsZXRlLWl0ZW06aG92ZXIgeyBiYWNrZ3JvdW5kLWNvbG9yOiAjYzgyMzMzOyB9CgogICAgICAgIC5idXR0b24tY29uZmlybS5pcy11bmNvbmZpcm1lZCB7IGJhY2tncm91bmQtY29sb3I6ICMwMDdiZmY7IH0KICAgICAgICAuYnV0dG9uLWNvbmZpcm0uaXMtdW5jb25maXJtZWQ6aG92ZXIgeyBiYWNrZ3JvdW5kLWNvbG9yOiAjMDA1NmIzOyB9CiAgICAgICAgLmJ1dHRvbi1jb25maXJtLmlzLWNvbmZpcm1lZCB7IGJhY2tncm91bmQtY29sb3I6ICNmZmMxMDc7IGNvbG9yOiAjMjEyNTI5OyB9CiAgICAgICAgLmJ1dHRvbi1jb25maXJtLmlzLWNvbmZpcm1lZDpob3ZlciB7IGJhY2tncm91bmQtY29sb3I6ICNlMGE4MDA7IH0KICAgIDwvc3R5bGU+CjwvaGVhZD4KPGJvZHk+CiAgICA8ZGl2IGNsYXNzPSJjb250YWluZXIiPgogICAgICAgIDxoMT7pgKPntaHkuovpoIU8L2gxPgogICAgICAgIDw/cGhwIGlmIChpc3NldCgkcGFnZV9tZXNzYWdlKSAmJiAhZW1wdHkoJHBhZ2VfbWVzc2FnZSkpOiA/PgogICAgICAgICAgICA8ZGl2IGNsYXNzPSJtZXNzYWdlIDw/cGhwIGVjaG8gaHRtbHNwZWNpYWxjaGFycygkcGFnZV9tZXNzYWdlX3R5cGUsIEVOVF9RVU9URVMsICdVVEYtOCcpOyA/PiI+CiAgICAgICAgICAgICAgICA8P3BocCBlY2hvICRwYWdlX21lc3NhZ2U7ID8+CiAgICAgICAgICAgIDwvZGl2PgogICAgICAgIDw/cGhwIGVuZGlmOyA/PgogICAgICAgIDxmb3JtIGFjdGlvbj0iPD9waHAgZWNobyBodG1sc3BlY2lhbGNoYXJzKCRfU0VSVkVSWyJQSFBfU0VMRiJdLCBFTlRfUVVPVEVTLCAnVVRGLTgnKTsgPz4iIG1ldGhvZD0icG9zdCI+CiAgICAgICAgICAgIDxpbnB1dCB0eXBlPSJoaWRkZW4iIG5hbWU9ImFjdGlvbiIgdmFsdWU9ImFkZF9jb250YWN0Ij4KICAgICAgICAgICAgPGRpdj4KICAgICAgICAgICAgICAgIDxsYWJlbCBmb3I9Im5hbWUiPuWQjeWJjTo8L2xhYmVsPgogICAgICAgICAgICAgICAgPGlucHV0IHR5cGU9InRleHQiIGlkPSJuYW1lIiBuYW1lPSJuYW1lIiB2YWx1ZT0iPD9waHAgZWNobyBodG1sc3BlY2lhbGNoYXJzKCRmb3JtX2RhdGFbJ25hbWUnXSwgRU5UX1FVT1RFUywgJ1VURi04Jyk7ID8+IiByZXF1aXJlZCBtYXhsZW5ndGg9Ijw/cGhwIGVjaG8gTUFYX05BTUVfTEVOR1RIOyA/PiI+CiAgICAgICAgICAgIDwvZGl2PgogICAgICAgICAgICA8ZGl2PgogICAgICAgICAgICAgICAgPGxhYmVsIGZvcj0ibWVzc2FnZV90ZXh0Ij7pgKPntaHkuovpoIU6PC9sYWJlbD4KICAgICAgICAgICAgICAgIDx0ZXh0YXJlYSBpZD0ibWVzc2FnZV90ZXh0IiBuYW1lPSJtZXNzYWdlX3RleHQiIHJvd3M9IjMiIHJlcXVpcmVkIG1heGxlbmd0aD0iPD9waHAgZWNobyBNQVhfTUVTU0FHRV9MRU5HVEg7ID8+Ij48P3BocCBlY2hvIGh0bWxzcGVjaWFsY2hhcnMoJGZvcm1fZGF0YVsnbWVzc2FnZV90ZXh0J10sIEVOVF9RVU9URVMsICdVVEYtOCcpOyA/PjwvdGV4dGFyZWE+CiAgICAgICAgICAgIDwvZGl2PgogICAgICAgICAgICA8ZGl2PgogICAgICAgICAgICAgICAgPGJ1dHRvbiB0eXBlPSJzdWJtaXQiPui/veWKoDwvYnV0dG9uPgogICAgICAgICAgICA8L2Rpdj4KICAgICAgICA8L2Zvcm0+CiAgICAgICAgPGRpdiBjbGFzcz0iY29udGFjdHMtZGlzcGxheSI+CiAgICAgICAgICAgIDw/cGhwIGRpc3BsYXlfY29udGFjdHMoKTsgPz4KICAgICAgICA8L2Rpdj4KICAgIDwvZGl2Pgo8L2JvZHk+CjwvaHRtbD4=