Find this useful? Enter your email to receive occasional updates for securing PHP code.
Signing you up...
Thank you for signing up!
PHP Decode
<?php eval("?>" . base64_decode("PD9waHANCnJlcXVpcmUoJ2xpc2Fucy5waHAnKTsvL2xpc2Fucy5waHAgZ..
Decoded Output download
?>b'<?php
require(\'lisans.php\');//lisans.php dosyasini ekle
$lisans[\'site\']=getenv(\'HTTP_HOST\');//site domainini al
if (substr($lisans[\'site\'], 0, 4) == "www.")//domain basindaki www. varmi yokmu kontrol et
$lisans[\'site\']=substr($lisans[\'site\'],4);//www. sil ve sadece domain.com olarak adresi al
$bas = "SORUNSUZSCRPT-";//Lisans kodu bana istediiniz yaz eklenebilir
$son = "-2017";//Lisans kodu sonuna istediiniz yaz eklenebilir
$m = "md5";//md5 sifreleme
$s = "sha1";//sha1 sifreleme
$lisans[\'hash\']=wordwrap(strtoupper($s ($s ($s ($s ($m ($s ($s ($m ($lisans[\'site\'].date(\'Ymd\')))))))))),5,\'-\',true);//lisans kodunu olustur ve 5 karakterde bir - koy
$liskod = $lisans[\'hash\'];
$cevir=strrev($liskod);//lisans kodunu tersine cevir
$bcs = "$bas$cevir$son";
if($bcs!==$lisanskodu)//lisans kodunu kontrol et fark varsa sunucuya baglan - 24 saatte bir lisans kodu degisir
{
$lisans_cevap=file_get_contents(\'http://insta.adanabilisim.net/lisans/kontrol.php?site=\'.$lisans[\'site\']);//lisans sunucusundan lisans kontrolu yap
if($lisans_cevap!=\'LISANSLI\')//sunucudan gelen cevap LISANSLI degilse
die(\'Bu siteye ait lisans bulunamadi!!!\');//lisans gecersiz mesaji verilir islem durur
$lyaz = fopen(\'lisans.php\',"w+");//lisans.php dosyasini ac yeni lisans kodunu yaz bu dosya yazlabilir olmaldr
@fwrite($lyaz,\'<?php
/**
www.sorunsuzscript .com
Bu dosyaya herhangi bir lisans kodu yazmaniz gerekmez
Almis oldugunuz urune ait lisans sistemde aktif oldugunda
Lisans kodunuz otomatik olarak girilecektir.
Yeni lisans almak icin www.sorunsuzscript .com adresini ziyaret edebilirsiniz
*/
$lisanskodu="\'.$bcs.\'";
?>\');
fclose($lyaz);
}
define(\'INSTAWEB_VERSION\', str_replace("InstaWebV", "", basename(__FILE__, ".php")));
if($_SERVER["HTTP_USER_AGENT"] == "") {
header("HTTP/1.1 403 Forbidden");
echo \'Bakm Modu!\';
exit();
}
class Instagram
{
protected $username;
protected $password;
protected $debug;
protected $uuid;
protected $device_id;
protected $username_id;
/**
* @var Settings
*/
public $settings;
protected $token;
protected $isLoggedIn = false;
protected $rank_token;
protected $IGDataPath;
/**
* Default class constructor.
*
* @param string $username
* Your Instagram username.
* @param string $password
* Your Instagram password.
* @param $debug
* Debug on or off, false by default.
* @param $IGDataPath
* Default folder to store data, you can change it.
*/
public function __construct($username, $password, $debug = false, $IGDataPath = NULL, $forceUserIP = false)
{
$username = trim($username);
$password = trim($password);
$this->debug = $debug;
$this->device_id = SignatureUtils::generateDeviceId(md5($username . $password));
if( !is_null($IGDataPath) )
{
$this->IGDataPath = $IGDataPath;
}
else
{
$this->IGDataPath = Wow::get("project/cookiePath") . "instagram/";
}
$this->setUser($username, $password, $forceUserIP);
}
/**
* Set the user. Manage multiple accounts.
*
* @param string $username
* Your Instagram username.
* @param string $password
* Your Instagram password.
*/
public function setUser($username, $password, $forceUserIP = false)
{
$this->username = $username;
$this->password = $password;
$this->checkSettings($username, $forceUserIP);
$this->uuid = SignatureUtils::generateUUID(true);
if( file_exists($this->IGDataPath . (string) $this->username . ".dat") && $this->settings->get("username_id") != NULL && $this->settings->get("token") != NULL )
{
$this->isLoggedIn = true;
$this->username_id = $this->settings->get("username_id");
$this->rank_token = $this->username_id . "_" . $this->uuid;
$this->token = $this->settings->get("token");
}
else
{
$this->isLoggedIn = false;
}
}
protected function checkSettings($username, $forceUserIP = false)
{
$this->settings = new Settings($this->IGDataPath . $username . ".cnf");
if( $this->settings->get("version") == NULL )
{
$this->settings->set("version", Constants::VERSION);
}
if( $this->settings->get("ip") == NULL || $forceUserIP )
{
$ipAdress = "78." . rand(160, 191) . "." . rand(1, 255) . "." . rand(1, 255);
if( $forceUserIP && !empty($_SERVER["REMOTE_ADDR"]) )
{
$ipAdress = $_SERVER["REMOTE_ADDR"];
}
$this->settings->set("ip", $ipAdress);
}
if( file_exists($this->IGDataPath . (string) $username . ".dat") && ($this->settings->get("username_id") == NULL || $this->settings->get("token") == NULL) )
{
$cookieData = Utils::cookieToArray(file_get_contents($this->IGDataPath . (string) $username . ".dat"), "i.instagram.com");
if( $this->settings->get("username_id") == NULL && isset($cookieData["ds_user_id"]) )
{
$this->settings->set("username_id", $cookieData["ds_user_id"]);
}
if( $this->settings->get("token") == NULL && isset($cookieData["csrftoken"]) )
{
$this->settings->set("token", $cookieData["csrftoken"]);
}
}
if( $this->settings->get("user_agent") == NULL || version_compare($this->settings->get("version"), Constants::VERSION) == -1 )
{
$userAgent = new UserAgent($this);
$ua = $userAgent->buildUserAgent();
$this->settings->set("version", Constants::VERSION);
$this->settings->set("user_agent", $ua);
}
}
public function getData()
{
return array( "username" => $this->username, "password" => $this->password, "username_id" => $this->username_id, "uuid" => $this->uuid, "token" => $this->token, "rank_token" => $this->rank_token, "user_agent" => $this->settings->get("user_agent"), "ip" => $this->settings->get("ip") );
}
/**
* Login to Instagram.
*
* @param bool $force
* Force login to Instagram, this will create a new session
*
* @return array
* Login data
*/
public function login($force = false)
{
if( !$this->isLoggedIn || $force )
{
$fetch = $this->request("si/fetch_headers/?challenge_type=signup&guid=" . SignatureUtils::generateUUID(false), NULL, true);
preg_match("#Set-Cookie: csrftoken=([^;]+)#", $fetch[0], $token);
$data = array( "phone_id" => SignatureUtils::generateUUID(true), "_csrftoken" => $token[0], "username" => $this->username, "guid" => $this->uuid, "device_id" => $this->device_id, "password" => $this->password, "login_attempt_count" => "0" );
$login = $this->request("accounts/login/", SignatureUtils::generateSignature(json_encode($data)), true);
if( $login[1]["status"] == "fail" )
{
throw new Exception($login[1]["message"]);
}
$this->isLoggedIn = true;
$this->username_id = $login[1]["logged_in_user"]["pk"];
$this->settings->set("username_id", $this->username_id);
$this->rank_token = $this->username_id . "_" . $this->uuid;
preg_match("#Set-Cookie: csrftoken=([^;]+)#", $login[0], $match);
$this->token = $match[1];
$this->settings->set("token", $this->token);
$this->syncFeatures();
$this->megaphoneLog();
return $login[1];
}
$check = $this->timelineFeed();
if( isset($check["message"]) && $check["message"] == "login_required" )
{
return $this->login(true);
}
$this->megaphoneLog();
return array( "status" => "ok" );
}
public function syncFeatures()
{
$data = json_encode(array( "_uuid" => $this->uuid, "_uid" => $this->username_id, "id" => $this->username_id, "_csrftoken" => $this->token, "experiments" => Constants::EXPERIMENTS ));
return $this->request("qe/sync/", SignatureUtils::generateSignature($data))[1];
}
protected function autoCompleteUserList()
{
return $this->request("friendships/autocomplete_user_list/")[1];
}
protected function timelineFeed()
{
return $this->request("feed/timeline/")[1];
}
protected function megaphoneLog()
{
return $this->request("megaphone/log/")[1];
}
protected function expose()
{
$data = json_encode(array( "_uuid" => $this->uuid, "_uid" => $this->username_id, "id" => $this->username_id, "_csrftoken" => $this->token, "experiment" => "ig_android_profile_contextual_feed" ));
$this->request("qe/expose/", SignatureUtils::generateSignature($data));
$this->request("qe/expose/", SignatureUtils::generateSignature($data))[1];
}
/**
* Login to Instagram.
*
* @return bool
* Returns true if logged out correctly
*/
public function logout()
{
$logout = $this->request("accounts/logout/");
if( $logout == "ok" )
{
return true;
}
return false;
}
/**
* Upload photo to Instagram.
*
* @param string $photo
* Path to your photo
* @param string $caption
* Caption to be included in your photo.
*
* @return array
* Upload data
*/
public function uploadPhoto($photo, $caption = NULL, $upload_id = NULL)
{
$endpoint = Constants::API_URL . "upload/photo/";
$boundary = $this->uuid;
if( !is_null($upload_id) )
{
$fileToUpload = Utils::createVideoIcon($photo);
}
else
{
$upload_id = number_format(round(microtime(true) * 1000), 0, "", "");
$fileToUpload = file_get_contents($photo);
}
$bodies = array( array( "type" => "form-data", "name" => "upload_id", "data" => $upload_id ), array( "type" => "form-data", "name" => "_uuid", "data" => $this->uuid ), array( "type" => "form-data", "name" => "_csrftoken", "data" => $this->token ), array( "type" => "form-data", "name" => "image_compression", "data" => "{\"lib_name\":\"jt\",\"lib_version\":\"1.3.0\",\"quality\":\"70\"}" ), array( "type" => "form-data", "name" => "photo", "data" => $fileToUpload, "filename" => "pending_media_" . number_format(round(microtime(true) * 1000), 0, "", "") . ".jpg", "headers" => array( "Content-Transfer-Encoding: binary", "Content-type: application/octet-stream" ) ) );
$data = $this->buildBody($bodies, $boundary);
$headers = array( "X-IG-Capabilities: " . Constants::X_IG_Capabilities, "X-IG-Connection-Type: WIFI", "Content-type: multipart/form-data; boundary=" . $boundary, "Content-Length: " . strlen($data), "Accept-Language: tr-TR", "Accept-Encoding: gzip, deflate", "Connection: close", "X_FORWARDED_FOR: " . $this->settings->get("ip"), "REMOTE_ADDR: " . $this->settings->get("ip") );
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $endpoint);
curl_setopt($ch, CURLOPT_USERAGENT, $this->settings->get("user_agent"));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_VERBOSE, $this->debug);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_COOKIEFILE, $this->IGDataPath . (string) $this->username . ".dat");
curl_setopt($ch, CURLOPT_COOKIEJAR, $this->IGDataPath . (string) $this->username . ".dat");
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
$resp = curl_exec($ch);
$header_len = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$header = substr($resp, 0, $header_len);
$upload = json_decode(substr($resp, $header_len), true);
curl_close($ch);
if( $upload["status"] == "fail" )
{
throw new Exception($upload["message"]);
}
if( $this->debug )
{
echo "RESPONSE: " . substr($resp, $header_len) . "
";
}
$configure = $this->configure($upload["upload_id"], $photo, $caption);
$this->expose();
return $configure;
}
public function uploadVideo($video, $caption = NULL)
{
$videoData = file_get_contents($video);
$endpoint = Constants::API_URL . "upload/video/";
$boundary = $this->uuid;
$upload_id = round(microtime(true) * 1000);
$bodies = array( array( "type" => "form-data", "name" => "upload_id", "data" => $upload_id ), array( "type" => "form-data", "name" => "_csrftoken", "data" => $this->token ), array( "type" => "form-data", "name" => "media_type", "data" => "2" ), array( "type" => "form-data", "name" => "_uuid", "data" => $this->uuid ) );
$data = $this->buildBody($bodies, $boundary);
$headers = array( "Connection: keep-alive", "Accept: */*", "Host: i.instagram.com", "Content-type: multipart/form-data; boundary=" . $boundary, "Accept-Language: tr-TR", "X_FORWARDED_FOR: " . $this->settings->get("ip"), "REMOTE_ADDR: " . $this->settings->get("ip") );
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $endpoint);
curl_setopt($ch, CURLOPT_USERAGENT, $this->settings->get("user_agent"));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_VERBOSE, false);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_COOKIEFILE, $this->IGDataPath . (string) $this->username . ".dat");
curl_setopt($ch, CURLOPT_COOKIEJAR, $this->IGDataPath . (string) $this->username . ".dat");
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
$resp = curl_exec($ch);
$header_len = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$body = json_decode(substr($resp, $header_len), true);
$uploadUrl = $body["video_upload_urls"][3]["url"];
$job = $body["video_upload_urls"][3]["job"];
$request_size = floor(strlen($videoData) / 4);
$lastRequestExtra = strlen($videoData) - $request_size * 4;
for( $a = 0; $a <= 3; $a++ )
{
$start = $a * $request_size;
$end = ($a + 1) * $request_size + (($a == 3 ? $lastRequestExtra : 0));
$headers = array( "Connection: keep-alive", "Accept: */*", "Host: upload.instagram.com", "Cookie2: \$Version=1", "Accept-Encoding: gzip, deflate", "Content-Type: application/octet-stream", "Session-ID: " . $upload_id, "Accept-Language: tr-TR", "Content-Disposition: attachment; filename=\"video.mov\"", "Content-Length: " . ($end - $start), "Content-Range: " . "bytes " . $start . "-" . ($end - 1) . "/" . strlen($videoData), "job: " . $job );
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $uploadUrl);
curl_setopt($ch, CURLOPT_USERAGENT, $this->settings->get("user_agent"));
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_VERBOSE, false);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_COOKIEFILE, $this->IGDataPath . (string) $this->username . ".dat");
curl_setopt($ch, CURLOPT_COOKIEJAR, $this->IGDataPath . (string) $this->username . ".dat");
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, substr($videoData, $start, $end));
$result = curl_exec($ch);
$header_len = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$body = substr($result, $header_len);
$array[] = array( $body );
}
$resp = curl_exec($ch);
$header_len = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$header = substr($resp, 0, $header_len);
$upload = json_decode(substr($resp, $header_len), true);
curl_close($ch);
if( $upload["status"] == "fail" )
{
throw new Exception($upload["message"]);
}
if( $this->debug )
{
echo "RESPONSE: " . substr($resp, $header_len) . "
";
}
$configure = $this->configureVideo($upload_id, $video, $caption);
$this->expose();
return $configure;
}
public function direct_message($recipients, $text)
{
if( !is_array($recipients) )
{
$recipients = array( $recipients );
}
$string = array( );
foreach( $recipients as $recipient )
{
$string[] = "\"" . $recipient . "\"";
}
$recipient_users = implode(",", $string);
$endpoint = Constants::API_URL . "direct_v2/threads/broadcast/text/";
$boundary = $this->uuid;
$bodies = array( array( "type" => "form-data", "name" => "recipient_users", "data" => "[[" . $recipient_users . "]]" ), array( "type" => "form-data", "name" => "client_context", "data" => $this->uuid ), array( "type" => "form-data", "name" => "thread_ids", "data" => "[\"0\"]" ), array( "type" => "form-data", "name" => "text", "data" => (is_null($text) ? "" : $text) ) );
$data = $this->buildBody($bodies, $boundary);
$headers = array( "Proxy-Connection: keep-alive", "Connection: keep-alive", "Accept: */*", "Content-type: multipart/form-data; boundary=" . $boundary, "Accept-Language: tr-TR" );
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $endpoint);
curl_setopt($ch, CURLOPT_USERAGENT, $this->settings->get("user_agent"));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_VERBOSE, $this->debug);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_COOKIEFILE, $this->IGDataPath . (string) $this->username . ".dat");
curl_setopt($ch, CURLOPT_COOKIEJAR, $this->IGDataPath . (string) $this->username . ".dat");
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
$resp = curl_exec($ch);
$header_len = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$header = substr($resp, 0, $header_len);
$upload = json_decode(substr($resp, $header_len), true);
curl_close($ch);
}
public function direct_share($media_id, $recipients, $text = NULL)
{
if( !is_array($recipients) )
{
$recipients = array( $recipients );
}
$string = array( );
foreach( $recipients as $recipient )
{
$string[] = "\"" . $recipient . "\"";
}
$recipient_users = implode(",", $string);
$endpoint = Constants::API_URL . "direct_v2/threads/broadcast/media_share/?media_type=photo";
$boundary = $this->uuid;
$bodies = array( array( "type" => "form-data", "name" => "media_id", "data" => $media_id ), array( "type" => "form-data", "name" => "recipient_users", "data" => "[[" . $recipient_users . "]]" ), array( "type" => "form-data", "name" => "client_context", "data" => $this->uuid ), array( "type" => "form-data", "name" => "thread_ids", "data" => "[\"0\"]" ), array( "type" => "form-data", "name" => "text", "data" => (is_null($text) ? "" : $text) ) );
$data = $this->buildBody($bodies, $boundary);
$headers = array( "Proxy-Connection: keep-alive", "Connection: keep-alive", "Accept: */*", "Content-type: multipart/form-data; boundary=" . $boundary, "Accept-Language: tr-TR" );
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $endpoint);
curl_setopt($ch, CURLOPT_USERAGENT, $this->settings->get("user_agent"));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_VERBOSE, $this->debug);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_COOKIEFILE, $this->IGDataPath . (string) $this->username . ".dat");
curl_setopt($ch, CURLOPT_COOKIEJAR, $this->IGDataPath . (string) $this->username . ".dat");
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
$resp = curl_exec($ch);
$header_len = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$header = substr($resp, 0, $header_len);
$upload = json_decode(substr($resp, $header_len), true);
curl_close($ch);
}
protected function configureVideo($upload_id, $video, $caption = "")
{
$this->uploadPhoto($video, $caption, $upload_id);
list($size) = getimagesize($video);
$post = json_encode(array( "upload_id" => $upload_id, "source_type" => "3", "poster_frame_index" => 0, "length" => 0, "audio_muted" => false, "filter_type" => "0", "video_result" => "deprecated", "clips" => array( "length" => Utils::getSeconds($video), "source_type" => "3", "camera_position" => "back" ), "extra" => array( "source_width" => 960, "source_height" => 1280 ), "device" => array( "manufacturer" => "Xiaomi", "model" => "HM 1SW", "android_version" => 18, "android_release" => "4.3" ), "_csrftoken" => $this->token, "_uuid" => $this->uuid, "_uid" => $this->username_id, "caption" => $caption ));
$post = str_replace("\"length\":0", "\"length\":0.00", $post);
return $this->request("media/configure/?video=1", SignatureUtils::generateSignature($post))[1];
}
protected function configure($upload_id, $photo, $caption = "")
{
list($size) = getimagesize($photo);
$post = json_encode(array( "upload_id" => $upload_id, "camera_model" => "HM1S", "source_type" => 3, "date_time_original" => date("Y:m:d H:i:s"), "camera_make" => "XIAOMI", "edits" => array( "crop_original_size" => array( $size, $size ), "crop_zoom" => 1.3333334, "crop_center" => array( 0, 0 ) ), "extra" => array( "source_width" => $size, "source_height" => $size ), "device" => array( "manufacturer" => "Xiaomi", "model" => "HM 1SW", "android_version" => 18, "android_release" => "4.3" ), "_csrftoken" => $this->token, "_uuid" => $this->uuid, "_uid" => $this->username_id, "caption" => $caption ));
$post = str_replace("\"crop_center\":[0,0]", "\"crop_center\":[0.0,-0.0]", $post);
return $this->request("media/configure/", SignatureUtils::generateSignature($post))[1];
}
/**
* Edit media.
*
* @param string $mediaId
* Media id
* @param string $captionText
* Caption text
*
* @return array
* edit media data
*/
public function editMedia($mediaId, $captionText = "")
{
$data = json_encode(array( "_uuid" => $this->uuid, "_uid" => $this->username_id, "_csrftoken" => $this->token, "caption_text" => $captionText ));
return $this->request("media/" . $mediaId . "/edit_media/", SignatureUtils::generateSignature($data))[1];
}
/**
* Remove yourself from a tagged media.
*
* @param string $mediaId
* Media id
*
* @return array
* edit media data
*/
public function removeSelftag($mediaId)
{
$data = json_encode(array( "_uuid" => $this->uuid, "_uid" => $this->username_id, "_csrftoken" => $this->token ));
return $this->request("usertags/" . $mediaId . "/remove/", SignatureUtils::generateSignature($data))[1];
}
/**
* Media info
*
* @param string $mediaId
* Media id
*
* @return array
* delete request data
*/
public function mediaInfo($mediaId)
{
$data = json_encode(array( "_uuid" => $this->uuid, "_uid" => $this->username_id, "_csrftoken" => $this->token, "media_id" => $mediaId ));
return $this->request("media/" . $mediaId . "/info/", SignatureUtils::generateSignature($data))[1];
}
/**
* Delete photo or video.
*
* @param string $mediaId
* Media id
*
* @return array
* delete request data
*/
public function deleteMedia($mediaId)
{
$data = json_encode(array( "_uuid" => $this->uuid, "_uid" => $this->username_id, "_csrftoken" => $this->token, "media_id" => $mediaId ));
return $this->request("media/" . $mediaId . "/delete/", SignatureUtils::generateSignature($data))[1];
}
/**
* Comment media.
*
* @param string $mediaId
* Media id
* @param string $commentText
* Comment Text
*
* @return array
* comment media data
*/
public function comment($mediaId, $commentText)
{
$data = json_encode(array( "_uuid" => $this->uuid, "_uid" => $this->username_id, "_csrftoken" => $this->token, "comment_text" => $commentText ));
return $this->request("media/" . $mediaId . "/comment/", SignatureUtils::generateSignature($data))[1];
}
/**
* Delete Comment.
*
* @param string $mediaId
* Media ID
* @param string $commentId
* Comment ID
*
* @return array
* Delete comment data
*/
public function deleteComment($mediaId, $captionText, $commentId)
{
$data = json_encode(array( "_uuid" => $this->uuid, "_uid" => $this->username_id, "_csrftoken" => $this->token, "caption_text" => $captionText ));
return $this->request("media/" . $mediaId . "/comment/" . $commentId . "/delete/", SignatureUtils::generateSignature($data))[1];
}
/**
* Sets account to public.
*
* @param string $photo
* Path to photo
*/
public function changeProfilePicture($photo)
{
if( is_null($photo) )
{
echo "Photo not valid
";
}
else
{
$uData = json_encode(array( "_csrftoken" => $this->token, "_uuid" => $this->uuid, "_uid" => $this->username_id ));
$endpoint = Constants::API_URL . "accounts/change_profile_picture/";
$boundary = $this->uuid;
$bodies = array( array( "type" => "form-data", "name" => "ig_sig_key_version", "data" => Constants::SIG_KEY_VERSION ), array( "type" => "form-data", "name" => "signed_body", "data" => hash_hmac("sha256", $uData, Constants::IG_SIG_KEY) . $uData ), array( "type" => "form-data", "name" => "profile_pic", "data" => file_get_contents($photo), "filename" => "profile_pic", "headers" => array( "Content-type: application/octet-stream", "Content-Transfer-Encoding: binary" ) ) );
$data = $this->buildBody($bodies, $boundary);
$headers = array( "Proxy-Connection: keep-alive", "Connection: keep-alive", "Accept: */*", "Content-type: multipart/form-data; boundary=" . $boundary, "Accept-Language: tr-TR", "Accept-Encoding: gzip, deflate" );
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $endpoint);
curl_setopt($ch, CURLOPT_USERAGENT, $this->settings->get("user_agent"));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_VERBOSE, $this->debug);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_COOKIEFILE, $this->IGDataPath . (string) $this->username . ".dat");
curl_setopt($ch, CURLOPT_COOKIEJAR, $this->IGDataPath . (string) $this->username . ".dat");
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
$resp = curl_exec($ch);
$header_len = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$header = substr($resp, 0, $header_len);
$upload = json_decode(substr($resp, $header_len), true);
curl_close($ch);
}
}
/**
* Remove profile picture.
*
* @return array
* status request data
*/
public function removeProfilePicture()
{
$data = json_encode(array( "_uuid" => $this->uuid, "_uid" => $this->username_id, "_csrftoken" => $this->token ));
return $this->request("accounts/remove_profile_picture/", SignatureUtils::generateSignature($data))[1];
}
/**
* Sets account to private.
*
* @return array
* status request data
*/
public function setPrivateAccount()
{
$data = json_encode(array( "_uuid" => $this->uuid, "_uid" => $this->username_id, "_csrftoken" => $this->token ));
return $this->request("accounts/set_private/", SignatureUtils::generateSignature($data))[1];
}
/**
* Sets account to public.
*
* @return array
* status request data
*/
public function setPublicAccount()
{
$data = json_encode(array( "_uuid" => $this->uuid, "_uid" => $this->username_id, "_csrftoken" => $this->token ));
return $this->request("accounts/set_public/", SignatureUtils::generateSignature($data))[1];
}
/**
* Get personal profile data.
*
* @return array
* profile data
*/
public function getProfileData()
{
$data = json_encode(array( "_uuid" => $this->uuid, "_uid" => $this->username_id, "_csrftoken" => $this->token ));
return $this->request("accounts/current_user/?edit=true", SignatureUtils::generateSignature($data))[1];
}
/**
* Edit profile.
*
* @param string $url
* Url - website. "" for nothing
* @param string $phone
* Phone number. "" for nothing
* @param string $first_name
* Name. "" for nothing
* @param string $email
* Email. Required.
* @param int $gender
* Gender. male = 1 , female = 0
*
* @return array
* edit profile data
*/
public function editProfile($url, $phone, $first_name, $biography, $email, $gender)
{
$data = json_encode(array( "_uuid" => $this->uuid, "_uid" => $this->username_id, "_csrftoken" => $this->token, "external_url" => $url, "phone_number" => $phone, "username" => $this->username, "first_name" => $first_name, "biography" => $biography, "email" => $email, "gender" => $gender ));
return $this->request("accounts/edit_profile/", SignatureUtils::generateSignature($data))[1];
}
/**
* Get username info.
*
* @param string $usernameId
* Username id
*
* @return array
* Username data
*/
public function getUsernameInfo($usernameId)
{
return $this->request("users/" . $usernameId . "/info/")[1];
}
/**
* Get self username info.
*
* @return array
* Username data
*/
public function getSelfUsernameInfo()
{
return $this->getUsernameInfo($this->username_id);
}
/**
* Get recent activity.
*
* @return array
* Recent activity data
*/
public function getRecentActivity($maxid = NULL)
{
list(, $activity) = $this->request("news/inbox/?" . ((!is_null($maxid) ? "max_id=" . $maxid : "")));
if( $activity["status"] != "ok" )
{
throw new Exception($activity["message"] . "
");
}
return $activity;
}
/**
* Get recent activity from accounts followed.
*
* @return array
* Recent activity data of follows
*/
public function getFollowingRecentActivity($maxid = NULL)
{
list(, $activity) = $this->request("news/?" . ((!is_null($maxid) ? "max_id=" . $maxid : "")));
if( $activity["status"] != "ok" )
{
throw new Exception($activity["message"] . "
");
}
return $activity;
}
/**
* I dont know this yet.
*
* @return array
* v2 inbox data
*/
public function getv2Inbox()
{
list(, $inbox) = $this->request("direct_v2/inbox/?");
if( $inbox["status"] != "ok" )
{
throw new Exception($inbox["message"] . "
");
}
return $inbox;
}
/**
* Direct Thread Data.
*
* @param int $threadId Thread Id
*
* @throws Exception Direct Thread Data
*
* @return array Direct Thread Data
*/
public function directThread($threadId)
{
list(, $directThread) = $this->request("direct_v2/threads/" . $threadId . "/?");
if( $directThread["status"] != "ok" )
{
throw new Exception($directThread["message"] . "
");
}
return $directThread;
}
/**
* Get user tags.
*
* @param string $usernameId
*
* @return array
* user tags data
*/
public function getUserTags($usernameId, $maxid = NULL)
{
list(, $tags) = $this->request("usertags/" . $usernameId . "/feed/?rank_token=" . $this->rank_token . "&ranked_content=true" . ((!is_null($maxid) ? "max_id=" . $maxid : "")));
if( $tags["status"] != "ok" )
{
throw new Exception($tags["message"] . "
");
}
return $tags;
}
/**
* Get self user tags.
*
* @return array
* self user tags data
*/
public function getSelfUserTags($maxid = NULL)
{
return $this->getUserTags($this->username_id, $maxid);
}
/**
* Get tagged media.
*
* @param string $tag
*
* @return array
*/
public function tagFeed($tag, $maxid = NULL)
{
list(, $userFeed) = $this->request("feed/tag/" . $tag . "/?max_id=" . $maxid . "&rank_token=" . $this->rank_token . "&ranked_content=true&");
if( $userFeed["status"] != "ok" )
{
throw new Exception($userFeed["message"] . "
");
}
return $userFeed;
}
/**
* Get media likers.
*
* @param string $mediaId
*
* @return array
*/
public function getMediaLikers($mediaId)
{
list(, $likers) = $this->request("media/" . $mediaId . "/likers/?");
if( $likers["status"] != "ok" )
{
throw new Exception($likers["message"] . "
");
}
return $likers;
}
/**
* Get user locations media.
*
* @param string $usernameId
* Username id
*
* @return array
* Geo Media data
*/
public function getGeoMedia($usernameId)
{
list(, $locations) = $this->request("maps/user/" . $usernameId . "/");
if( $locations["status"] != "ok" )
{
throw new Exception($locations["message"] . "
");
}
return $locations;
}
/**
* Get self user locations media.
*
* @return array
* Geo Media data
*/
public function getSelfGeoMedia()
{
return $this->getGeoMedia($this->username_id);
}
/**
* facebook user search.
*
* @param string $query
*
* @return array
* query data
*/
public function fbUserSearch($query)
{
$query = rawurlencode($query);
list(, $query) = $this->request("fbsearch/topsearch/?context=blended&query=" . $query . "&rank_token=" . $this->rank_token);
if( $query["status"] != "ok" )
{
throw new Exception($query["message"] . "
");
}
return $query;
}
/**
* Search users.
*
* @param string $query
*
* @return array
* query data
*/
public function searchUsers($query)
{
list(, $query) = $this->request("users/search/?ig_sig_key_version=" . Constants::SIG_KEY_VERSION . "&is_typeahead=true&query=" . $query . "&rank_token=" . $this->rank_token);
if( $query["status"] != "ok" )
{
throw new Exception($query["message"] . "
");
}
return $query;
}
/**
* Search exact username
*
* @param string usernameName username as STRING not an id
*
* @return array
* query data
*
*/
public function searchUsername($usernameName)
{
list(, $query) = $this->request("users/" . $usernameName . "/usernameinfo/");
return $query;
}
/**
* Search users using addres book.
*
* @param array $contacts
*
* @return array
* query data
*/
public function syncFromAdressBook($contacts)
{
$data = "contacts=" . json_encode($contacts, true);
return $this->request("address_book/link/?include=extra_display_name,thumbnails", $data)[1];
}
/**
* Search tags.
*
* @param string $query
*
* @return array
* query data
*/
public function searchTags($query)
{
list(, $query) = $this->request("tags/search/?is_typeahead=true&q=" . $query . "&rank_token=" . $this->rank_token);
if( $query["status"] != "ok" )
{
throw new Exception($query["message"] . "
");
}
return $query;
}
/**
* Get timeline data.
*
* @return array
* timeline data
*/
public function getTimeline($maxid = NULL)
{
list(, $timeline) = $this->request("feed/timeline/?rank_token=" . $this->rank_token . "&ranked_content=true" . ((!is_null($maxid) ? "&max_id=" . $maxid : "")));
if( $timeline["status"] != "ok" )
{
throw new Exception($timeline["message"] . "
");
}
return $timeline;
}
/**
* @throws Exception
*
* @return array|void
*/
public function getReelsTrayFeed()
{
list(, $feed) = $this->request("feed/reels_tray/");
if( $feed["status"] != "ok" )
{
throw new Exception($feed["message"] . "
");
}
return $feed;
}
/**
* Get user feed.
*
* @param string $usernameId
* Username id
* @param null $maxid
* Max Id
* @param null $minTimestamp
* Min timestamp
*
* @return array User feed data
* User feed data
* @throws Exception
*/
public function getUserFeed($usernameId, $maxid = NULL, $minTimestamp = NULL)
{
list(, $userFeed) = $this->request("feed/user/" . $usernameId . "/?rank_token=" . $this->rank_token . ((!is_null($maxid) ? "&max_id=" . $maxid : "")) . ((!is_null($minTimestamp) ? "&min_timestamp=" . $minTimestamp : "")) . "&ranked_content=true");
return $userFeed;
}
/**
* Get hashtag feed.
*
* @param string $hashtagString
* Hashtag string, not including the #
*
* @return array
* Hashtag feed data
*/
public function getHashtagFeed($hashtagString, $maxid = NULL)
{
if( is_null($maxid) )
{
$endpoint = "feed/tag/" . $hashtagString . "/?rank_token=" . $this->rank_token . "&ranked_content=true&";
}
else
{
$endpoint = "feed/tag/" . $hashtagString . "/?max_id=" . $maxid . "&rank_token=" . $this->rank_token . "&ranked_content=true&";
}
list(, $hashtagFeed) = $this->request($endpoint);
if( $hashtagFeed["status"] != "ok" )
{
throw new Exception($hashtagFeed["message"] . "
");
}
return $hashtagFeed;
}
/**
* Get locations.
*
* @param string $query
* search query
*
* @return array
* Location location data
*/
public function searchLocation($query)
{
$query = rawurlencode($query);
$endpoint = "fbsearch/places/?rank_token=" . $this->rank_token . "&query=" . $query;
list(, $locationFeed) = $this->request($endpoint);
if( $locationFeed["status"] != "ok" )
{
throw new Exception($locationFeed["message"] . "
");
}
return $locationFeed;
}
/**
* Get location feed.
*
* @param string $locationId
* location id
*
* @return array
* Location feed data
*/
public function getLocationFeed($locationId, $maxid = NULL)
{
if( is_null($maxid) )
{
$endpoint = "feed/location/" . $locationId . "/?rank_token=" . $this->rank_token . "&ranked_content=true&";
}
else
{
$endpoint = "feed/location/" . $locationId . "/?max_id=" . $maxid . "&rank_token=" . $this->rank_token . "&ranked_content=true&";
}
list(, $locationFeed) = $this->request($endpoint);
if( $locationFeed["status"] != "ok" )
{
throw new Exception($locationFeed["message"] . "
");
}
return $locationFeed;
}
/**
* Get self user feed.
*
* @return array
* User feed data
*/
public function getSelfUserFeed($maxid = NULL, $minTimestamp = NULL)
{
return $this->getUserFeed($this->username_id, $maxid, $minTimestamp);
}
/**
* Ranked recipients.
*
* @throws Exception Ranked recipients Data
*
* @return array|void
*/
public function getRankedRecipients()
{
list(, $ranked_recipients) = $this->request("direct_v2/ranked_recipients/?show_threads=true");
if( $ranked_recipients["status"] != "ok" )
{
throw new Exception($ranked_recipients["message"] . "
");
}
return $ranked_recipients;
}
/**
* Recent recipients.
*
* @throws Exception Ranked recipients Data
*
* @return array|void
*/
public function getRecentRecipients()
{
list(, $recent_recipients) = $this->request("direct_share/recent_recipients/");
if( $recent_recipients["status"] != "ok" )
{
throw new Exception($recent_recipients["message"] . "
");
}
return $recent_recipients;
}
/**
* Explore Tab.
*
* @throws Exception Explore data
*
* @return array|void
*/
public function explore()
{
list(, $explore) = $this->request("discover/explore/");
if( $explore["status"] != "ok" )
{
throw new Exception($explore["message"] . "
");
}
return $explore;
}
/**
* Get popular feed.
*
* @return array
* popular feed data
*/
public function getPopularFeed($maxid = NULL)
{
list(, $popularFeed) = $this->request("feed/popular/?max_id=" . $maxid . "&people_teaser_supported=1&rank_token=" . $this->rank_token . "&ranked_content=true&");
if( $popularFeed["status"] != "ok" )
{
throw new Exception($popularFeed["message"] . "
");
}
return $popularFeed;
}
/**
* Get user followings.
*
* @param string $usernameId
* Username id
*
* @return array
* followers data
*/
public function getUserFollowings($usernameId, $maxid = NULL)
{
return $this->request("friendships/" . $usernameId . "/following/?max_id=" . $maxid . "&ig_sig_key_version=" . Constants::SIG_KEY_VERSION . "&rank_token=" . $this->rank_token)[1];
}
/**
* Get user followers.
*
* @param string $usernameId
* Username id
*
* @return array
* followers data
*/
public function getUserFollowers($usernameId, $maxid = NULL)
{
return $this->request("friendships/" . $usernameId . "/followers/?max_id=" . $maxid . "&ig_sig_key_version=" . Constants::SIG_KEY_VERSION . "&rank_token=" . $this->rank_token)[1];
}
/**
* Get self user followers.
*
* @return array
* followers data
*/
public function getSelfUserFollowers($maxid = NULL)
{
return $this->getUserFollowers($this->username_id, $maxid);
}
/**
* Get self users we are following.
*
* @return array
* users we are following data
*/
public function getSelfUsersFollowing($maxid = NULL)
{
return $this->request("friendships/following/?max_id=" . $maxid . "&ig_sig_key_version=" . Constants::SIG_KEY_VERSION . "&rank_token=" . $this->rank_token)[1];
}
/**
* Like photo or video.
*
* @param string $mediaId
* Media id
*
* @return array
* status request
*/
public function like($mediaId)
{
$data = json_encode(array( "_uuid" => $this->uuid, "_uid" => $this->username_id, "_csrftoken" => $this->token, "media_id" => $mediaId ));
return $this->request("media/" . $mediaId . "/like/", SignatureUtils::generateSignature($data))[1];
}
/**
* Unlike photo or video.
*
* @param string $mediaId
* Media id
*
* @return array
* status request
*/
public function unlike($mediaId)
{
$data = json_encode(array( "_uuid" => $this->uuid, "_uid" => $this->username_id, "_csrftoken" => $this->token, "media_id" => $mediaId ));
return $this->request("media/" . $mediaId . "/unlike/", SignatureUtils::generateSignature($data))[1];
}
/**
* Get media comments.
*
* @param string $mediaId
* Media id
*
* @return array
* Media comments data
*/
public function getMediaComments($mediaId)
{
return $this->request("media/" . $mediaId . "/comments/?")[1];
}
/**
* Set name and phone (Optional).
*
* @param string $name
* @param string $phone
*
* @return array
* Set status data
*/
public function setNameAndPhone($name = "", $phone = "")
{
$data = json_encode(array( "_uuid" => $this->uuid, "_uid" => $this->username_id, "first_name" => $name, "phone_number" => $phone, "_csrftoken" => $this->token ));
return $this->request("accounts/set_phone_and_name/", SignatureUtils::generateSignature($data))[1];
}
/**
* Get direct share.
*
* @return array
* Direct share data
*/
public function getDirectShare()
{
return $this->request("direct_share/inbox/?")[1];
}
/**
* Backups all your uploaded photos :).
*/
public function backup()
{
$myUploads = $this->getSelfUserFeed();
foreach( $myUploads["items"] as $item )
{
if( !is_dir($this->IGDataPath . "backup/" . (string) $this->username . "-" . date("Y-m-d")) )
{
mkdir($this->IGDataPath . "backup/" . (string) $this->username . "-" . date("Y-m-d"));
}
file_put_contents($this->IGDataPath . "backup/" . (string) $this->username . "-" . date("Y-m-d") . "/" . $item["id"] . ".jpg", file_get_contents($item["image_versions2"]["candidates"][0]["url"]));
}
}
/**
* Follow.
*
* @param string $userId
*
* @return array
* Friendship status data
*/
public function follow($userId)
{
$data = json_encode(array( "_uuid" => $this->uuid, "_uid" => $this->username_id, "user_id" => $userId, "_csrftoken" => $this->token ));
return $this->request("friendships/create/" . $userId . "/", SignatureUtils::generateSignature($data))[1];
}
/**
* Unfollow.
*
* @param string $userId
*
* @return array
* Friendship status data
*/
public function unfollow($userId)
{
$data = json_encode(array( "_uuid" => $this->uuid, "_uid" => $this->username_id, "user_id" => $userId, "_csrftoken" => $this->token ));
return $this->request("friendships/destroy/" . $userId . "/", SignatureUtils::generateSignature($data))[1];
}
/**
* Block.
*
* @param string $userId
*
* @return array
* Friendship status data
*/
public function block($userId)
{
$data = json_encode(array( "_uuid" => $this->uuid, "_uid" => $this->username_id, "user_id" => $userId, "_csrftoken" => $this->token ));
return $this->request("friendships/block/" . $userId . "/", SignatureUtils::generateSignature($data))[1];
}
/**
* Unblock.
*
* @param string $userId
*
* @return array
* Friendship status data
*/
public function unblock($userId)
{
$data = json_encode(array( "_uuid" => $this->uuid, "_uid" => $this->username_id, "user_id" => $userId, "_csrftoken" => $this->token ));
return $this->request("friendships/unblock/" . $userId . "/", SignatureUtils::generateSignature($data))[1];
}
/**
* Show User Friendship.
*
* @param string $userId
*
* @return array
* Friendship relationship data
*/
public function userFriendship($userId)
{
$data = json_encode(array( "_uuid" => $this->uuid, "_uid" => $this->username_id, "user_id" => $userId, "_csrftoken" => $this->token ));
return $this->request("friendships/show/" . $userId . "/", SignatureUtils::generateSignature($data))[1];
}
/**
* Get liked media.
*
* @return array
* Liked media data
*/
public function getLikedMedia($maxid = NULL)
{
return $this->request("feed/liked/?max_id=" . $maxid)[1];
}
protected function buildBody($bodies, $boundary)
{
$body = "";
foreach( $bodies as $b )
{
$body .= "--" . $boundary . "
";
$body .= "Content-Disposition: " . $b["type"] . "; name=\"" . $b["name"] . "\"";
if( isset($b["filename"]) )
{
$ext = pathinfo($b["filename"], PATHINFO_EXTENSION);
$body .= "; filename=\"" . "pending_media_" . number_format(round(microtime(true) * 1000), 0, "", "") . "." . $ext . "\"";
}
if( isset($b["headers"]) && is_array($b["headers"]) )
{
foreach( $b["headers"] as $header )
{
$body .= "
" . $header;
}
}
$body .= "
" . $b["data"] . "
";
}
$body .= "--" . $boundary . "--";
return $body;
}
protected function request($endpoint, $post = NULL, $login = false)
{
if( !$this->isLoggedIn && !$login )
{
throw new Exception("Not logged in
");
}
$headers = array( "Connection: close", "Accept: */*", "X-IG-Capabilities: " . Constants::X_IG_Capabilities, "X-IG-Connection-Type: WIFI", "Content-type: application/x-www-form-urlencoded; charset=UTF-8", "Accept-Language: tr-TR", "X_FORWARDED_FOR: " . $this->settings->get("ip"), "REMOTE_ADDR: " . $this->settings->get("ip") );
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, Constants::API_URL . $endpoint);
curl_setopt($ch, CURLOPT_USERAGENT, $this->settings->get("user_agent"));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_VERBOSE, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_COOKIEFILE, $this->IGDataPath . (string) $this->username . ".dat");
curl_setopt($ch, CURLOPT_COOKIEJAR, $this->IGDataPath . (string) $this->username . ".dat");
if( $post )
{
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
}
$resp = curl_exec($ch);
$header_len = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$header = substr($resp, 0, $header_len);
$body = substr($resp, $header_len);
curl_close($ch);
if( $this->debug )
{
echo "REQUEST: " . $endpoint . "
";
if( !is_null($post) && !is_array($post) )
{
echo "DATA: " . urldecode($post) . "
";
}
echo "RESPONSE: " . $body . "
";
}
return array( $header, json_decode($body, true) );
}
/**
* Is User Logged in?
* @return bool
*/
public function isLoggedIn()
{
return $this->isLoggedIn;
}
}
class InstagramWeb
{
protected $username = NULL;
protected $username_id = NULL;
protected $token = NULL;
protected $isLoggedIn = false;
protected $IGDataPath = NULL;
/**
* @var Settings
*/
public $settings = NULL;
/**
* Default class constructor.
*
* @param string $username
* Your Instagram username.
* @param $IGDataPath
* Default folder to store data, you can change it.
*/
public function __construct($username, $IGDataPath = NULL, $forceUserIP = false)
{
$username = trim($username);
if( !is_null($IGDataPath) )
{
$this->IGDataPath = $IGDataPath;
}
else
{
$this->IGDataPath = Wow::get("project/cookiePath") . "instagram/";
}
$this->setUser($username, $forceUserIP);
}
protected function checkSettings($username, $forceUserIP = false)
{
$this->settings = new Settings($this->IGDataPath . $username . ".cnf");
if( $this->settings->get("ip") == NULL || $forceUserIP )
{
$ipAdress = "78." . rand(160, 191) . "." . rand(1, 255) . "." . rand(1, 255);
if( $forceUserIP && !empty($_SERVER["REMOTE_ADDR"]) )
{
$ipAdress = $_SERVER["REMOTE_ADDR"];
}
$this->settings->set("ip", $ipAdress);
}
if( file_exists($this->IGDataPath . (string) $username . ".dat") && ($this->settings->get("username_id") == NULL || $this->settings->get("token") == NULL) )
{
$cookieData = Utils::cookieToArray(file_get_contents($this->IGDataPath . (string) $username . ".dat"), "www.instagram.com");
if( $this->settings->get("username_id") == NULL && isset($cookieData["ds_user_id"]) )
{
$this->settings->set("username_id", $cookieData["ds_user_id"]);
}
if( $this->settings->get("token") == NULL && isset($cookieData["csrftoken"]) )
{
$this->settings->set("token", $cookieData["csrftoken"]);
}
}
if( $this->settings->get("web_user_agent") == NULL )
{
$userAgents = explode(PHP_EOL, file_get_contents(Wow::get("project/cookiePath") . "device/browsers.csv"));
$agentIndex = rand(0, count($userAgents) - 1);
$userAgent = $userAgents[$agentIndex];
$this->settings->set("web_user_agent", $userAgent);
}
}
/**
* Set the user. Manage multiple accounts.
*
* @param string $username
* Your Instagram username.
*/
public function setUser($username, $forceUserIP = false)
{
$this->username = $username;
$this->checkSettings($username, $forceUserIP);
if( file_exists($this->IGDataPath . (string) $this->username . ".dat") && $this->settings->get("username_id") != NULL && $this->settings->get("token") != NULL )
{
$this->isLoggedIn = true;
$this->username_id = $this->settings->get("username_id");
$this->token = $this->settings->get("token");
}
else
{
$this->isLoggedIn = false;
}
}
public function getData()
{
return array( "username" => $this->username, "username_id" => $this->username_id, "token" => $this->token, "user_agent" => $this->settings->get("web_user_agent"), "ip" => $this->settings->get("ip") );
}
/**
* Comment media.
*
* @param string $mediaId
* Media id
* @param string $commentText
* Comment Text
*
* @return array
* comment media data
*/
public function comment($mediaId, $commentText)
{
$arrMediaID = explode("_", $mediaId);
$mediaId = $arrMediaID[0];
$postData = "comment_text=" . $commentText;
$headers = array( );
$headers[] = "Referer: https://www.instagram.com/";
$headers[] = "DNT: 1";
$headers[] = "Origin: https://www.instagram.com/";
$headers[] = "X-CSRFToken: " . trim($this->token);
$headers[] = "X-Requested-With: XMLHttpRequest";
$headers[] = "X-Instagram-AJAX: 1";
$headers[] = "Connection: close";
$headers[] = "Cache-Control: max-age=0";
return $this->request("web/comments/" . $mediaId . "/add/", $headers, $postData)[1];
}
/**
* Get username info.
*
* @param string $username
* Username
*
* @return array
* Username data
*/
public function getUsernameInfo($username)
{
$headers = array( );
$headers[] = "Referer: https://www.instagram.com/";
$headers[] = "DNT: 1";
$headers[] = "Origin: https://www.instagram.com/";
$headers[] = "X-CSRFToken: " . trim($this->token);
$headers[] = "X-Requested-With: XMLHttpRequest";
$headers[] = "X-Instagram-AJAX: 1";
$headers[] = "Connection: close";
$headers[] = "Cache-Control: max-age=0";
return $this->request((string) $username . "/?__a=1", $headers)[1];
}
/**
* Media info
*
* @param string $mediaCode
* Media code
*
* @return array
* delete request data
*/
public function mediaInfo($mediaCode)
{
$headers = array( );
$headers[] = "Referer: https://www.instagram.com/";
$headers[] = "DNT: 1";
$headers[] = "Origin: https://www.instagram.com/";
$headers[] = "X-CSRFToken: " . trim($this->token);
$headers[] = "X-Requested-With: XMLHttpRequest";
$headers[] = "X-Instagram-AJAX: 1";
$headers[] = "Connection: close";
$headers[] = "Cache-Control: max-age=0";
return $this->request("p/" . $mediaCode . "/?__a=1", $headers)[1];
}
/**
* Like photo or video.
*
* @param string $mediaId
* Media id
*
* @return array
* status request
*/
public function like($mediaId)
{
$arrMediaID = explode("_", $mediaId);
$mediaId = $arrMediaID[0];
$headers = array( );
$headers[] = "Referer: https://www.instagram.com/instagram/";
$headers[] = "DNT: 1";
$headers[] = "Origin: https://www.instagram.com/";
$headers[] = "X-CSRFToken: " . trim($this->token);
$headers[] = "X-Requested-With: XMLHttpRequest";
$headers[] = "X-Instagram-AJAX: 1";
$headers[] = "Connection: close";
$headers[] = "Cache-Control: max-age=0";
return $this->request("web/likes/" . $mediaId . "/like/", $headers, true)[1];
}
/**
* Unlike photo or video.
*
* @param string $mediaId
* Media id
*
* @return array
* status request
*/
public function unlike($mediaId)
{
$headers = array( );
$headers[] = "Referer: https://www.instagram.com/";
$headers[] = "DNT: 1";
$headers[] = "Origin: https://www.instagram.com/";
$headers[] = "X-CSRFToken: " . trim($this->token);
$headers[] = "X-Requested-With: XMLHttpRequest";
$headers[] = "X-Instagram-AJAX: 1";
$headers[] = "Connection: close";
$headers[] = "Cache-Control: max-age=0";
return $this->request("web/likes/" . $mediaId . "/unlike/", $headers, true)[1];
}
/**
* Follow.
*
* @param string $userId
*
* @return array
* Friendship status data
*/
public function follow($userId)
{
$headers = array( );
$headers[] = "Referer: https://www.instagram.com/instagram/";
$headers[] = "DNT: 1";
$headers[] = "Origin: https://www.instagram.com/";
$headers[] = "X-CSRFToken: " . trim($this->token);
$headers[] = "X-Requested-With: XMLHttpRequest";
$headers[] = "X-Instagram-AJAX: 1";
$headers[] = "Connection: close";
$headers[] = "Cache-Control: max-age=0";
return $this->request("web/friendships/" . $userId . "/follow/", $headers, true)[1];
}
/**
* Unfollow.
*
* @param string $userId
*
* @return array
* Friendship status data
*/
public function unfollow($userId)
{
$headers = array( );
$headers[] = "Referer: https://www.instagram.com/instagram/";
$headers[] = "DNT: 1";
$headers[] = "Origin: https://www.instagram.com/";
$headers[] = "X-CSRFToken: " . trim($this->token);
$headers[] = "X-Requested-With: XMLHttpRequest";
$headers[] = "X-Instagram-AJAX: 1";
$headers[] = "Connection: close";
$headers[] = "Cache-Control: max-age=0";
return $this->request("web/friendships/" . $userId . "/unfollow/", $headers, true)[1];
}
protected function request($endpoint, array $optionalheaders, $post = NULL)
{
if( !$this->isLoggedIn )
{
throw new Exception("Not logged in
");
}
$headers = array( "Accept: */*", "Accept-Language: tr;q=1", "X_FORWARDED_FOR: " . $this->settings->get("ip"), "REMOTE_ADDR: " . $this->settings->get("ip") );
$headers = array_merge($headers, $optionalheaders);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, Constants::WEB_URL . $endpoint);
curl_setopt($ch, CURLOPT_USERAGENT, $this->settings->get("web_user_agent"));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_VERBOSE, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_COOKIEFILE, $this->IGDataPath . (string) $this->username . ".dat");
curl_setopt($ch, CURLOPT_COOKIEJAR, $this->IGDataPath . (string) $this->username . ".dat");
if( $post )
{
curl_setopt($ch, CURLOPT_POST, true);
if( is_string($post) )
{
curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
}
}
$resp = curl_exec($ch);
$header_len = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$header = substr($resp, 0, $header_len);
$body = substr($resp, $header_len);
curl_close($ch);
return array( $header, json_decode($body, true) );
}
/**
* Is User Logged in?
* @return bool
*/
public function isLoggedIn()
{
return $this->isLoggedIn;
}
public function isValid()
{
$headers = array( );
$headers[] = "Referer: https://www.instagram.com/";
$headers[] = "DNT: 1";
$headers[] = "Origin: https://www.instagram.com/";
$headers[] = "X-CSRFToken: " . trim($this->token);
$headers[] = "X-Requested-With: XMLHttpRequest";
$headers[] = "X-Instagram-AJAX: 1";
$headers[] = "Connection: close";
$headers[] = "Cache-Control: max-age=0";
list($header) = $this->request("accounts/activity/?__a=1", $headers);
return (strpos($header, "HTTP/1.1 200") === false ? false : true);
}
}
class SignatureUtils
{
public static function generateSignature($data)
{
$hash = hash_hmac("sha256", $data, Constants::IG_SIG_KEY);
return "ig_sig_key_version=" . Constants::SIG_KEY_VERSION . "&signed_body=" . $hash . "." . urlencode($data);
}
public static function generateDeviceId($seed)
{
$volatile_seed = filemtime(__DIR__);
return "android-" . substr(md5($seed . $volatile_seed), 16);
}
public static function generateUUID($type)
{
$uuid = sprintf("%04x%04x-%04x-%04x-%04x-%04x%04x%04x", mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(0, 4095) | 16384, mt_rand(0, 16383) | 32768, mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(0, 65535));
return ($type ? $uuid : str_replace("-", "", $uuid));
}
}
class UserAgent
{
protected $parent = NULL;
public function __construct($parent)
{
$this->parent = $parent;
}
protected function getDeviceData()
{
$csvfile = Wow::get("project/cookiePath") . "device/devices.csv";
$file_handle = fopen($csvfile, "r");
$line_of_text = array( );
while( !feof($file_handle) )
{
$line_of_text[] = fgetcsv($file_handle, 1024);
}
$deviceData = explode(";", $line_of_text[mt_rand(0, 11867)][0]);
fclose($file_handle);
return $deviceData;
}
public function buildUserAgent()
{
$deviceData = $this->getDeviceData();
$this->parent->settings->set("manufacturer", $deviceData[0]);
$this->parent->settings->set("device", $deviceData[1]);
$this->parent->settings->set("model", $deviceData[2]);
$localeLang = $this->detectUserLocale();
return sprintf("Instagram %s Android (18/4.3; 320dpi; 720x1280; %s; %s; %s; qcom; " . $localeLang . ")", Constants::VERSION, $deviceData[0], $deviceData[1], $deviceData[2]);
}
private function detectUserLocale($fallbackLocale = "en_US")
{
$userLocale = NULL;
if( isset($_SERVER["HTTP_ACCEPT_LANGUAGE"]) )
{
$arrLang = explode(",", $_SERVER["HTTP_ACCEPT_LANGUAGE"]);
$preferredLangs = array( );
foreach( $arrLang as $strLang )
{
$arrLangDetails = explode(";q=", $strLang);
$langCode = str_replace("-", "_", $arrLangDetails[0]);
$langPriority = floatval((isset($arrLangDetails[1]) ? $arrLangDetails[1] : 1));
$preferredLangs[$langCode] = $langPriority;
}
arsort($preferredLangs);
reset($preferredLangs);
$userLocale = key($preferredLangs);
}
return (is_null($userLocale) ? $fallbackLocale : $userLocale);
}
}
class Utils
{
/**
* Length of the file in Seconds.
*
* @param string $file
* path to the file name
*
* @return int
* length of the file in seconds
*/
public static function getSeconds($file)
{
$ffmpeg = self::checkFFMPEG();
if( $ffmpeg )
{
$time = exec((string) $ffmpeg . " -i " . $file . " 2>&1 | grep \'Duration\' | cut -d \' \' -f 4");
$duration = explode(":", $time);
$seconds = $duration[0] * 3600 + $duration[1] * 60 + round($duration[2]);
return $seconds;
}
return mt_rand(15, 300);
}
/**
* Check for ffmpeg/avconv dependencies.
*
* @return string/boolean
* name of the library if present, false otherwise
*/
public static function checkFFMPEG()
{
@exec("ffmpeg -version 2>&1", $output, $returnvalue);
if( $returnvalue === 0 )
{
return "ffmpeg";
}
@exec("avconv -version 2>&1", $output, $returnvalue);
if( $returnvalue === 0 )
{
return "avconv";
}
return false;
}
/**
* Creating a video icon/thumbnail.
*
* @param string $file
* path to the video file
*
* @return image
* icon/thumbnail for the video
*/
public static function createVideoIcon($file)
{
$ffmpeg = self::checkFFMPEG();
if( $ffmpeg )
{
$preview = sys_get_temp_dir() . "/" . md5($file) . ".jpg";
@unlink($preview);
$command = $ffmpeg . " -i \"" . $file . "\" -f mjpeg -ss 00:00:01 -vframes 1 \"" . $preview . "\" 2>&1";
@exec($command);
return file_get_contents($preview);
}
}
/**
* Implements the actual logic behind creating the icon/thumbnail.
*
* @param string $file
* path to the file name
*
* @return image
* icon/thumbnail for the video
*/
public static function createIconGD($file, $size = 100, $raw = true)
{
list($width, $height) = getimagesize($file);
if( $height < $width )
{
$y = 0;
$x = ($width - $height) / 2;
$smallestSide = $height;
}
else
{
$x = 0;
$y = ($height - $width) / 2;
$smallestSide = $width;
}
$image_p = imagecreatetruecolor($size, $size);
$image = imagecreatefromstring(file_get_contents($file));
imagecopyresampled($image_p, $image, 0, 0, $x, $y, $size, $size, $smallestSide, $smallestSide);
ob_start();
imagejpeg($image_p, NULL, 95);
$i = ob_get_contents();
ob_end_clean();
imagedestroy($image);
imagedestroy($image_p);
return $i;
}
public static function formatBytes($bytes, $precision = 2)
{
$units = array( "B", "kB", "mB", "gB", "tB" );
$bytes = max($bytes, 0);
$pow = floor((($bytes ? log($bytes) : 0)) / log(1024));
$pow = min($pow, count($units) - 1);
$bytes /= pow(1024, $pow);
return round($bytes, $precision) . "" . $units[$pow];
}
public static function colouredString($string, $colour)
{
$colours["black"] = "0;30";
$colours["dark_gray"] = "1;30";
$colours["blue"] = "0;34";
$colours["light_blue"] = "1;34";
$colours["green"] = "0;32";
$colours["light_green"] = "1;32";
$colours["cyan"] = "0;36";
$colours["light_cyan"] = "1;36";
$colours["red"] = "0;31";
$colours["light_red"] = "1;31";
$colours["purple"] = "0;35";
$colours["light_purple"] = "1;35";
$colours["brown"] = "0;33";
$colours["yellow"] = "1;33";
$colours["light_gray"] = "0;37";
$colours["white"] = "1;37";
$colored_string = "";
if( isset($colours[$colour]) )
{
$colored_string .= "[" . $colours[$colour] . "m";
}
$colored_string .= $string . "[0m";
return $colored_string;
}
public static function getFilterCode($filter)
{
$filters = array( );
$filters[108] = "Charmes";
$filters[116] = "Ashby";
$filters[117] = "Helena";
$filters[115] = "Brooklyn";
$filters[105] = "Dogpatch";
$filters[113] = "Skyline";
$filters[107] = "Ginza";
$filters[118] = "Maven";
$filters[16] = "Kelvin";
$filters[14] = "1977";
$filters[20] = "Walden";
$filters[19] = "Toaster";
$filters[18] = "Sutro";
$filters[22] = "Brannan";
$filters[3] = "Earlybird";
$filters[106] = "Vesper";
$filters[109] = "Stinson";
$filters[15] = "Nashville";
$filters[21] = "Hefe";
$filters[10] = "Inkwell";
$filters[2] = "Lo-Fi";
$filters[28] = "Willow";
$filters[27] = "Sierra";
$filters[1] = "X Pro II";
$filters[25] = "Valencia";
$filters[26] = "Hudson";
$filters[23] = "Rise";
$filters[17] = "Mayfair";
$filters[24] = "Amaro";
$filters[608] = "Perpetua";
$filters[612] = "Aden";
$filters[603] = "Ludwig";
$filters[616] = "Crema";
$filters[605] = "Slumber";
$filters[613] = "Juno";
$filters[614] = "Reyes";
$filters[615] = "Lark";
$filters[111] = "Moon";
$filters[114] = "Gingham";
$filters[112] = "Clarendon";
$filters[0] = "Normal";
return array_search($filter, $filters);
}
public static function cookieToArray($string, $domain)
{
$arrCookies = array( );
$fileVals = self::extractCookies($string);
foreach( $fileVals as $cookie )
{
if( $cookie["domain"] == $domain )
{
$arrCookies[$cookie["name"]] = $cookie["value"];
}
}
return $arrCookies;
}
public static function extractCookies($string)
{
$lines = explode(PHP_EOL, $string);
$cookies = array( );
foreach( $lines as $line )
{
$cookie = array( );
if( substr($line, 0, 10) == "#HttpOnly_" )
{
$line = substr($line, 10);
$cookie["httponly"] = true;
}
else
{
$cookie["httponly"] = false;
}
if( substr($line, 0, 1) != "#" && substr_count($line, " ") == 6 )
{
$tokens = explode(" ", $line);
$tokens = array_map("trim", $tokens);
list($cookie["domain"], $cookie["flag"], $cookie["path"], $cookie["secure"], $cookie["expiration-epoch"]) = $tokens;
$cookie["name"] = urldecode($tokens[5]);
$cookie["value"] = urldecode($tokens[6]);
$cookie["expiration"] = date("Y-m-d h:i:s", $tokens[4]);
$cookies[] = $cookie;
}
}
return $cookies;
}
}
class Settings
{
private $path = NULL;
private $sets = NULL;
public function __construct($path)
{
$this->path = $path;
$this->sets = array( );
if( file_exists($path) )
{
$fp = fopen($path, "rb");
while( $line = fgets($fp, 2048) )
{
$line = trim($line, " ");
if( $line[0] == "#" )
{
continue;
}
$kv = explode("=", $line, 2);
$this->sets[$kv[0]] = trim($kv[1], "
");
}
fclose($fp);
}
}
public function get($key, $default = NULL)
{
if( $key == "sets" )
{
return $this->sets;
}
if( isset($this->sets[$key]) )
{
return $this->sets[$key];
}
return $default;
}
public function set($key, $value)
{
if( $key == "sets" )
{
return NULL;
}
$this->sets[$key] = $value;
$this->Save();
}
public function Save()
{
if( file_exists($this->path) )
{
unlink($this->path);
}
$fp = fopen($this->path, "wb");
fseek($fp, 0);
foreach( $this->sets as $key => $value )
{
fwrite($fp, $key . "=" . $value . "
");
}
fclose($fp);
}
public function __set($prop, $value)
{
$this->set($prop, $value);
}
public function __get($prop)
{
return $this->get($prop);
}
}
class Constants
{
const API_URL = "https://i.instagram.com/api/v1/";
const VERSION = "10.3.2";
const IG_SIG_KEY = "5ad7d6f013666cc93c88fc8af940348bd067b68f0dce3c85122a923f4f74b251";
const EXPERIMENTS = "ig_android_ad_holdout_16m5_universe,ig_android_progressive_jpeg,ig_creation_growth_holdout,ig_android_oppo_app_badging,ig_android_ad_remove_username_from_caption_universe,ig_android_enable_share_to_whatsapp,ig_android_direct_drawing_in_quick_cam_universe,ig_android_ad_always_send_ad_attribution_id_universe,ig_android_universe_video_production,ig_android_direct_plus_button,ig_android_ads_heatmap_overlay_universe,ig_android_http_stack_experiment_2016,ig_android_infinite_scrolling,ig_fbns_blocked,ig_android_post_auto_retry_v7_21,ig_fbns_push,ig_android_video_playback_bandwidth_threshold,ig_android_direct_link_preview,ig_android_direct_typing_indicator,ig_android_preview_capture,ig_android_feed_pill,ig_android_profile_link_iab,ig_android_story_caption,ig_android_network_cancellation,ig_android_histogram_reporter,ig_android_anrwatchdog,ig_android_search_client_matching,ig_android_follow_request_text_buttons,ig_android_feed_zoom,ig_android_drafts_universe,ig_android_disable_comment,ig_android_user_detail_endpoint,ig_android_os_version_blocking,ig_android_blocked_list,ig_android_event_creation,ig_android_high_res_upload_2,ig_android_2fac,ig_android_mark_reel_seen_on_Swipe_forward,ig_android_comment_redesign,ig_android_ad_sponsored_label_universe,ig_android_mentions_dismiss_rule,ig_android_disable_chroma_subsampling,ig_android_share_spinner,ig_android_video_reuse_surface,ig_explore_v3_android_universe,ig_android_media_favorites,ig_android_nux_holdout,ig_android_insta_video_universe,ig_android_search_null_state,ig_android_universe_reel_video_production,liger_instagram_android_univ,ig_android_direct_emoji_picker,ig_feed_holdout_universe,ig_android_direct_send_auto_retry_universe,ig_android_samsung_app_badging,ig_android_disk_usage,ig_android_business_promotion,ig_android_direct_swipe_to_inbox,ig_android_feed_reshare_button_nux,ig_android_react_native_boost_post,ig_android_boomerang_feed_attribution,ig_fbns_shared,ig_fbns_dump_ids,ig_android_react_native_universe,ig_show_promote_button_in_feed,ig_android_ad_metadata_behavior_universe,ig_android_video_loopcount_int,ig_android_inline_gallery_backoff_hours_universe,ig_android_rendering_controls,ig_android_profile_photo_as_media,ig_android_async_stack_image_cache,ig_video_max_duration_qe_preuniverse,ig_video_copyright_whitelist,ig_android_render_stories_with_content_override,ig_android_ad_intent_to_highlight_universe,ig_android_swipe_navigation_x_angle_universe,ig_android_disable_comment_public_test,ig_android_profile,ig_android_direct_blue_tab,ig_android_enable_share_to_messenger,ig_android_fetch_reel_tray_on_resume_universe,ig_android_promote_again,ig_feed_event_landing_page_channel,ig_ranking_following,ig_android_pending_request_search_bar,ig_android_feed_ufi_redesign,ig_android_pending_edits_dialog_universe,ig_android_business_conversion_flow_universe,ig_android_show_your_story_when_empty_universe,ig_android_ad_drop_cookie_early,ig_android_app_start_config,ig_android_fix_ise_two_phase,ig_android_ppage_toggle_universe,ig_android_pbia_normal_weight_universe,ig_android_profanity_filter,ig_ios_su_activity_feed,ig_android_search,ig_android_boomerang_entry,ig_android_mute_story,ig_android_inline_gallery_universe,ig_android_ad_remove_one_tap_indicator_universe,ig_android_view_count_decouple_likes_universe,ig_android_contact_button_redesign_v2,ig_android_periodic_analytics_upload_v2,ig_android_send_direct_typing_indicator,ig_android_ad_holdout_16h2m1_universe,ig_android_react_native_comment_moderation_settings,ig_video_use_sve_universe,ig_android_inline_gallery_no_backoff_on_launch_universe,ig_android_immersive_viewer,ig_android_discover_people_icon,ig_android_profile_follow_back_button,is_android_feed_seen_state,ig_android_dense_feed_unit_cards,ig_android_drafts_video_universe,ig_android_exoplayer,ig_android_add_to_last_post,ig_android_ad_remove_cta_chevron_universe,ig_android_ad_comment_cta_universe,ig_android_search_event_icon,ig_android_channels_home,ig_android_feed,ig_android_dv2_realtime_private_share,ig_android_non_square_first,ig_android_video_interleaved_v2,ig_android_video_cache_policy,ig_android_react_native_universe_kill_switch,ig_android_video_captions_universe,ig_android_follow_search_bar,ig_android_last_edits,ig_android_two_step_capture_flow,ig_android_video_download_logging,ig_android_share_link_to_whatsapp,ig_android_facebook_twitter_profile_photos,ig_android_swipeable_filters_blacklist,ig_android_ad_pbia_profile_tap_universe,ig_android_use_software_layer_for_kc_drawing_universe,ig_android_react_native_ota,ig_android_direct_mutually_exclusive_experiment_universe,ig_android_following_follower_social_context";
const LOGIN_EXPERIMENTS = "ig_android_reg_login_btn_active_state,ig_android_ci_opt_in_at_reg,ig_android_one_click_in_old_flow,ig_android_merge_fb_and_ci_friends_page,ig_android_non_fb_sso,ig_android_mandatory_full_name,ig_android_reg_enable_login_password_btn,ig_android_reg_phone_email_active_state,ig_android_analytics_data_loss,ig_fbns_blocked,ig_android_contact_point_triage,ig_android_reg_next_btn_active_state,ig_android_prefill_phone_number,ig_android_show_fb_social_context_in_nux,ig_android_one_tap_login_upsell,ig_fbns_push,ig_android_phoneid_sync_interval";
const SIG_KEY_VERSION = "4";
const X_IG_Capabilities = "3ToAAA==";
const ANDROID_VERSION = 18;
const ANDROID_RELEASE = "4.3";
const WEB_URL = "https://www.instagram.com/";
}
class BulkReaction
{
/**
* @var array $users
*/
protected $users = array( );
/**
* @var int $simultanepostsize
*/
protected $simultanepostsize = NULL;
/**
* @var string
*/
protected $IGDataPath = NULL;
/**
* BulkReaction constructor.
*
* @param array $users
* @param int $simultanepostsize
* @param string $IGDataPath
*/
public function __construct($users, $simultanepostsize = 100, $IGDataPath = NULL)
{
$this->simultanepostsize = $simultanepostsize;
if( !is_null($IGDataPath) )
{
$this->IGDataPath = $IGDataPath;
}
else
{
$this->IGDataPath = Wow::get("project/cookiePath") . "instagram/";
}
foreach( $users as $user )
{
$this->users[] = array( "data" => $user, "object" => ($user["isWebCookie"] == 1 ? new InstagramWeb($user["kullaniciAdi"]) : new Instagram($user["kullaniciAdi"], $user["sifre"])) );
}
}
/**
* @param string $mediaID
* @param string $mediaCode
*
* @return array
*/
public function like($mediaID, $mediaCode)
{
$totalSuccessCount = 0;
$triedUsers = array( );
$rollingCurl = new RollingCurl\RollingCurl();
$arrMediaID = explode("_", $mediaID);
$mediaIDBeforer = $arrMediaID[0];
foreach( $this->users as $user )
{
if( $user["data"]["isWebCookie"] == 1 )
{
$objInstagramWeb = $user["object"];
$objData = $objInstagramWeb->getData();
$headers = array( "Referer: https://www.instagram.com/instagram/", "DNT: 1", "Origin: https://www.instagram.com/", "X-CSRFToken: " . trim($objData["token"]), "X-Requested-With: XMLHttpRequest", "X-Instagram-AJAX: 1", "Connection: close", "Cache-Control: max-age=0", "Accept: */*", "Accept-Language: tr;q=1", "X_FORWARDED_FOR: " . $objData["ip"], "REMOTE_ADDR: " . $objData["ip"] );
$options = array( CURLOPT_USERAGENT => $objData["user_agent"], CURLOPT_RETURNTRANSFER => true, CURLOPT_FOLLOWLOCATION => true, CURLOPT_VERBOSE => false, CURLOPT_SSL_VERIFYPEER => false, CURLOPT_SSL_VERIFYHOST => false, CURLOPT_COOKIEFILE => $this->IGDataPath . $objData["username"] . ".dat", CURLOPT_COOKIEJAR => $this->IGDataPath . $objData["username"] . ".dat" );
$rollingCurl->post(Constants::WEB_URL . "web/likes/" . $mediaIDBeforer . "/like/", NULL, $headers, $options, $user["data"]);
}
else
{
$objInstagram = $user["object"];
$objData = $objInstagram->getData();
$data = json_encode(array( "_uuid" => $objData["uuid"], "_uid" => $objData["username_id"], "_csrftoken" => $objData["token"], "media_id" => $mediaID ));
$headers = array( "Connection: close", "Accept: */*", "X-IG-Capabilities: " . Constants::X_IG_Capabilities, "X-IG-Connection-Type: WIFI", "Content-type: application/x-www-form-urlencoded; charset=UTF-8", "Accept-Language: tr-TR", "X_FORWARDED_FOR: " . $objData["ip"], "REMOTE_ADDR: " . $objData["ip"] );
$options = array( CURLOPT_USERAGENT => $objData["user_agent"], CURLOPT_RETURNTRANSFER => true, CURLOPT_FOLLOWLOCATION => true, CURLOPT_VERBOSE => false, CURLOPT_SSL_VERIFYPEER => false, CURLOPT_SSL_VERIFYHOST => false, CURLOPT_COOKIEFILE => $this->IGDataPath . $objData["username"] . ".dat", CURLOPT_COOKIEJAR => $this->IGDataPath . $objData["username"] . ".dat" );
$rollingCurl->post(Constants::API_URL . "media/" . $mediaID . "/like/", SignatureUtils::generateSignature($data), $headers, $options, $user["data"]);
}
}
$rollingCurl->setCallback(function(RollingCurl\Request $request, RollingCurl\RollingCurl $rollingCurl) use (&$triedUsers, &$totalSuccessCount)
{
$triedUser = array( "userID" => $request->identifierParams["uyeID"], "instaID" => $request->identifierParams["instaID"], "userNick" => $request->identifierParams["kullaniciAdi"], "status" => "fail" );
$isErrored = $request->getResponseError();
if( empty($isErrored) )
{
$donenSonuc = json_decode($request->getResponseText(), true);
if( $donenSonuc )
{
if( $request->identifierParams["isWebCookie"] == 1 )
{
if( $donenSonuc["status"] == "ok" )
{
$totalSuccessCount++;
$triedUser["status"] = "success";
}
else
{
$triedUser["status"] = "fail";
}
}
else
{
if( $donenSonuc["status"] == "ok" )
{
$totalSuccessCount++;
$triedUser["status"] = "success";
}
else
{
$triedUser["status"] = "fail";
}
}
}
else
{
$triedUser["status"] = "fail";
}
}
else
{
$triedUser["status"] = "fail";
}
$triedUsers[] = $triedUser;
$rollingCurl->clearCompleted();
$rollingCurl->prunePendingRequestQueue();
}
);
$rollingCurl->setSimultaneousLimit($this->simultanepostsize);
$rollingCurl->execute();
return array( "totalSuccessCount" => $totalSuccessCount, "users" => $triedUsers );
}
/**
* @param string $userID
* @param string $userName
*
* @return array
*/
public function follow($userID, $userName)
{
$totalSuccessCount = 0;
$triedUsers = array( );
$rollingCurl = new RollingCurl\RollingCurl();
foreach( $this->users as $user )
{
if( $user["data"]["isWebCookie"] == 1 )
{
$objInstagramWeb = $user["object"];
$objData = $objInstagramWeb->getData();
$headers = array( "Referer: https://www.instagram.com/instagram/", "DNT: 1", "Origin: https://www.instagram.com/", "X-CSRFToken: " . trim($objData["token"]), "X-Requested-With: XMLHttpRequest", "X-Instagram-AJAX: 1", "Connection: close", "Cache-Control: max-age=0", "Accept: */*", "Accept-Language: tr;q=1", "X_FORWARDED_FOR: " . $objData["ip"], "REMOTE_ADDR: " . $objData["ip"] );
$options = array( CURLOPT_USERAGENT => $objData["user_agent"], CURLOPT_RETURNTRANSFER => true, CURLOPT_FOLLOWLOCATION => true, CURLOPT_VERBOSE => false, CURLOPT_SSL_VERIFYPEER => false, CURLOPT_SSL_VERIFYHOST => false, CURLOPT_COOKIEFILE => $this->IGDataPath . $objData["username"] . ".dat", CURLOPT_COOKIEJAR => $this->IGDataPath . $objData["username"] . ".dat" );
$rollingCurl->post(Constants::WEB_URL . "web/friendships/" . $userID . "/follow/", NULL, $headers, $options, $user["data"]);
}
else
{
$objInstagram = $user["object"];
$objData = $objInstagram->getData();
$data = json_encode(array( "_uuid" => $objData["uuid"], "_uid" => $objData["username_id"], "user_id" => $userID, "_csrftoken" => $objData["token"] ));
$headers = array( "Connection: close", "Accept: */*", "X-IG-Capabilities: " . Constants::X_IG_Capabilities, "X-IG-Connection-Type: WIFI", "Content-type: application/x-www-form-urlencoded; charset=UTF-8", "Accept-Language: tr-TR", "X_FORWARDED_FOR: " . $objData["ip"], "REMOTE_ADDR: " . $objData["ip"] );
$options = array( CURLOPT_USERAGENT => $objData["user_agent"], CURLOPT_RETURNTRANSFER => true, CURLOPT_FOLLOWLOCATION => true, CURLOPT_VERBOSE => false, CURLOPT_SSL_VERIFYPEER => false, CURLOPT_SSL_VERIFYHOST => false, CURLOPT_COOKIEFILE => $this->IGDataPath . $objData["username"] . ".dat", CURLOPT_COOKIEJAR => $this->IGDataPath . $objData["username"] . ".dat" );
$rollingCurl->post(Constants::API_URL . "friendships/create/" . $userID . "/", SignatureUtils::generateSignature($data), $headers, $options, $user["data"]);
}
}
$rollingCurl->setCallback(function(RollingCurl\Request $request, RollingCurl\RollingCurl $rollingCurl) use (&$triedUsers, &$totalSuccessCount)
{
$triedUser = array( "userID" => $request->identifierParams["uyeID"], "instaID" => $request->identifierParams["instaID"], "userNick" => $request->identifierParams["kullaniciAdi"], "status" => "fail" );
$isErrored = $request->getResponseError();
if( empty($isErrored) )
{
$donenSonuc = json_decode($request->getResponseText(), true);
if( $donenSonuc )
{
if( $request->identifierParams["isWebCookie"] == 1 )
{
if( $donenSonuc["status"] == "ok" && $donenSonuc["result"] == "following" )
{
$totalSuccessCount++;
$triedUser["status"] = "success";
}
else
{
$triedUser["status"] = "fail";
}
}
else
{
if( $donenSonuc["status"] == "ok" )
{
$totalSuccessCount++;
$triedUser["status"] = "success";
}
else
{
$triedUser["status"] = "fail";
}
}
}
else
{
$triedUser["status"] = "fail";
}
}
else
{
$triedUser["status"] = "fail";
}
$triedUsers[] = $triedUser;
$rollingCurl->clearCompleted();
$rollingCurl->prunePendingRequestQueue();
}
);
$rollingCurl->setSimultaneousLimit($this->simultanepostsize);
$rollingCurl->execute();
return array( "totalSuccessCount" => $totalSuccessCount, "users" => $triedUsers );
}
/**
* @param string $mediaID
* @param string $mediaCode
* @param array $commentTexts
*
* @return array
*/
public function comment($mediaID, $mediaCode, $commentTexts)
{
$totalSuccessCount = 0;
$triedUsers = array( );
$commentIndex = -1;
if( is_array($commentTexts) && !empty($commentTexts) )
{
$arrMediaID = explode("_", $mediaID);
$mediaIDBeforer = $arrMediaID[0];
$rollingCurl = new RollingCurl\RollingCurl();
foreach( $this->users as $user )
{
$commentIndex++;
$commentIndex = (isset($commentTexts[$commentIndex]) ? $commentIndex : 0);
if( $user["data"]["isWebCookie"] == 1 )
{
$objInstagramWeb = $user["object"];
$objData = $objInstagramWeb->getData();
$postData = "comment_text=" . $commentTexts[$commentIndex];
$headers = array( "Referer: https://www.instagram.com/", "DNT: 1", "Origin: https://www.instagram.com/", "X-CSRFToken: " . trim($objData["token"]), "X-Requested-With: XMLHttpRequest", "X-Instagram-AJAX: 1", "Connection: close", "Cache-Control: max-age=0", "Accept: */*", "Accept-Language: tr;q=1", "X_FORWARDED_FOR: " . $objData["ip"], "REMOTE_ADDR: " . $objData["ip"] );
$options = array( CURLOPT_USERAGENT => $objData["user_agent"], CURLOPT_RETURNTRANSFER => true, CURLOPT_FOLLOWLOCATION => true, CURLOPT_VERBOSE => false, CURLOPT_SSL_VERIFYPEER => false, CURLOPT_SSL_VERIFYHOST => false, CURLOPT_COOKIEFILE => $this->IGDataPath . $objData["username"] . ".dat", CURLOPT_COOKIEJAR => $this->IGDataPath . $objData["username"] . ".dat" );
$rollingCurl->post(Constants::WEB_URL . "web/comments/" . $mediaIDBeforer . "/add/", $postData, $headers, $options, $user["data"]);
}
else
{
$objInstagram = $user["object"];
$objData = $objInstagram->getData();
$data = json_encode(array( "_uuid" => $objData["uuid"], "_uid" => $objData["username_id"], "_csrftoken" => $objData["token"], "comment_text" => $commentTexts[$commentIndex] ));
$headers = array( "Connection: close", "Accept: */*", "X-IG-Capabilities: " . Constants::X_IG_Capabilities, "X-IG-Connection-Type: WIFI", "Content-type: application/x-www-form-urlencoded; charset=UTF-8", "Accept-Language: tr-TR", "X_FORWARDED_FOR: " . $objData["ip"], "REMOTE_ADDR: " . $objData["ip"] );
$options = array( CURLOPT_USERAGENT => $objData["user_agent"], CURLOPT_RETURNTRANSFER => true, CURLOPT_FOLLOWLOCATION => true, CURLOPT_VERBOSE => false, CURLOPT_SSL_VERIFYPEER => false, CURLOPT_SSL_VERIFYHOST => false, CURLOPT_COOKIEFILE => $this->IGDataPath . $objData["username"] . ".dat", CURLOPT_COOKIEJAR => $this->IGDataPath . $objData["username"] . ".dat" );
$rollingCurl->post(Constants::API_URL . "media/" . $mediaID . "/comment/", SignatureUtils::generateSignature($data), $headers, $options, $user["data"]);
}
}
$rollingCurl->setCallback(function(RollingCurl\Request $request, RollingCurl\RollingCurl $rollingCurl) use (&$triedUsers, &$totalSuccessCount)
{
$triedUser = array( "userID" => $request->identifierParams["uyeID"], "instaID" => $request->identifierParams["instaID"], "userNick" => $request->identifierParams["kullaniciAdi"], "status" => "fail" );
$isErrored = $request->getResponseError();
if( empty($isErrored) )
{
$donenSonuc = json_decode($request->getResponseText(), true);
if( $donenSonuc )
{
if( $request->identifierParams["isWebCookie"] == 1 )
{
if( isset($donenSonuc["status"]) && $donenSonuc["status"] == "ok" )
{
$totalSuccessCount++;
$triedUser["status"] = "success";
}
else
{
$triedUser["status"] = "fail";
}
}
else
{
if( isset($donenSonuc["status"]) && $donenSonuc["status"] == "ok" )
{
$totalSuccessCount++;
$triedUser["status"] = "success";
}
else
{
$triedUser["status"] = "fail";
}
}
}
else
{
$triedUser["status"] = "fail";
}
}
else
{
$triedUser["status"] = "fail";
}
$triedUsers[] = $triedUser;
$rollingCurl->clearCompleted();
$rollingCurl->prunePendingRequestQueue();
}
);
$rollingCurl->setSimultaneousLimit($this->simultanepostsize);
$rollingCurl->execute();
}
return array( "totalSuccessCount" => $totalSuccessCount, "users" => $triedUsers );
}
/**
* @return array
*/
public function validate()
{
$totalSuccessCount = 0;
$triedUsers = array( );
$rollingCurl = new RollingCurl\RollingCurl();
foreach( $this->users as $user )
{
if( $user["data"]["isWebCookie"] == 1 )
{
$objInstagramWeb = $user["object"];
$objData = $objInstagramWeb->getData();
$headers = array( "Referer: https://www.instagram.com/instagram/", "DNT: 1", "Origin: https://www.instagram.com/", "X-CSRFToken: " . trim($objData["token"]), "X-Requested-With: XMLHttpRequest", "X-Instagram-AJAX: 1", "Connection: close", "Cache-Control: max-age=0", "Accept: */*", "Accept-Language: tr;q=1", "X_FORWARDED_FOR: " . $objData["ip"], "REMOTE_ADDR: " . $objData["ip"] );
$options = array( CURLOPT_USERAGENT => $objData["user_agent"], CURLOPT_RETURNTRANSFER => true, CURLOPT_FOLLOWLOCATION => true, CURLOPT_VERBOSE => false, CURLOPT_SSL_VERIFYPEER => false, CURLOPT_SSL_VERIFYHOST => false, CURLOPT_COOKIEFILE => $this->IGDataPath . $objData["username"] . ".dat", CURLOPT_COOKIEJAR => $this->IGDataPath . $objData["username"] . ".dat" );
$rollingCurl->get(Constants::WEB_URL . "accounts/activity/?__a=1", $headers, $options, $user["data"]);
}
else
{
$objInstagram = $user["object"];
$objData = $objInstagram->getData();
$headers = array( "Connection: close", "Accept: */*", "X-IG-Capabilities: " . Constants::X_IG_Capabilities, "X-IG-Connection-Type: WIFI", "Content-type: application/x-www-form-urlencoded; charset=UTF-8", "Accept-Language: tr-TR", "X_FORWARDED_FOR: " . $objData["ip"], "REMOTE_ADDR: " . $objData["ip"] );
$options = array( CURLOPT_USERAGENT => $objData["user_agent"], CURLOPT_RETURNTRANSFER => true, CURLOPT_FOLLOWLOCATION => true, CURLOPT_VERBOSE => false, CURLOPT_SSL_VERIFYPEER => false, CURLOPT_SSL_VERIFYHOST => false, CURLOPT_COOKIEFILE => $this->IGDataPath . $objData["username"] . ".dat", CURLOPT_COOKIEJAR => $this->IGDataPath . $objData["username"] . ".dat" );
$rollingCurl->get(Constants::API_URL . "feed/timeline/?rank_token=" . $objData["rank_token"] . "&ranked_content=true", $headers, $options, $user["data"]);
}
}
$rollingCurl->setCallback(function(RollingCurl\Request $request, RollingCurl\RollingCurl $rollingCurl) use (&$triedUsers, &$totalSuccessCount)
{
$triedUser = array( "userID" => $request->identifierParams["uyeID"], "instaID" => $request->identifierParams["instaID"], "userNick" => $request->identifierParams["kullaniciAdi"], "status" => "fail" );
$isErrored = $request->getResponseError();
if( empty($isErrored) )
{
$donenSonuc = json_decode($request->getResponseText(), true);
if( $donenSonuc )
{
if( $request->identifierParams["isWebCookie"] == 1 )
{
$responseData = $request->getResponseInfo();
if( $responseData["http_code"] == "200" )
{
$totalSuccessCount++;
$triedUser["status"] = "success";
}
else
{
$triedUser["status"] = "fail";
}
}
else
{
if( $donenSonuc["status"] == "ok" )
{
$totalSuccessCount++;
$triedUser["status"] = "success";
}
else
{
$triedUser["status"] = "fail";
}
}
}
else
{
$triedUser["status"] = "fail";
}
}
else
{
$triedUser["status"] = "fail";
}
$triedUsers[] = $triedUser;
$rollingCurl->clearCompleted();
$rollingCurl->prunePendingRequestQueue();
}
);
$rollingCurl->setSimultaneousLimit($this->simultanepostsize);
$rollingCurl->execute();
return array( "totalSuccessCount" => $totalSuccessCount, "users" => $triedUsers );
}
}
'
Did this file decode correctly?
Original Code
<?php eval("?>" . base64_decode("PD9waHANCnJlcXVpcmUoJ2xpc2Fucy5waHAnKTsvL2xpc2Fucy5waHAgZG9zeWFzaW5pIGVrbGUNCiRsaXNhbnNbJ3NpdGUnXT1nZXRlbnYoJ0hUVFBfSE9TVCcpOy8vc2l0ZSBkb21haW5pbmkgYWwNCmlmIChzdWJzdHIoJGxpc2Fuc1snc2l0ZSddLCAwLCA0KSA9PSAid3d3LiIpLy9kb21haW4gYmFzaW5kYWtpIHd3dy4gdmFybWkgeW9rbXUga29udHJvbCBldA0KJGxpc2Fuc1snc2l0ZSddPXN1YnN0cigkbGlzYW5zWydzaXRlJ10sNCk7Ly93d3cuIHNpbCB2ZSBzYWRlY2UgZG9tYWluLmNvbSBvbGFyYWsgYWRyZXNpIGFsDQogDQokYmFzID0gIlNPUlVOU1VaU0NSxLBQVC0iOy8vTGlzYW5zIGtvZHUgYmHFn8SxbmEgaXN0ZWRpxJ9pbml6IHlhesSxIGVrbGVuZWJpbGlyDQokc29uID0gIi0yMDE3IjsvL0xpc2FucyBrb2R1IHNvbnVuYSBpc3RlZGnEn2luaXogeWF6xLEgZWtsZW5lYmlsaXINCiRtID0gIm1kNSI7Ly9tZDUgc2lmcmVsZW1lIA0KJHMgPSAic2hhMSI7Ly9zaGExIHNpZnJlbGVtZSANCiANCiANCiRsaXNhbnNbJ2hhc2gnXT13b3Jkd3JhcChzdHJ0b3VwcGVyKCRzICgkcyAoJHMgKCRzICgkbSAoJHMgKCRzICgkbSAoJGxpc2Fuc1snc2l0ZSddLmRhdGUoJ1ltZCcpKSkpKSkpKSkpLDUsJy0nLHRydWUpOy8vbGlzYW5zIGtvZHVudSBvbHVzdHVyIHZlIDUga2FyYWt0ZXJkZSBiaXIgLSBrb3kNCiRsaXNrb2QgPSAkbGlzYW5zWydoYXNoJ107DQokY2V2aXI9c3RycmV2KCRsaXNrb2QpOy8vbGlzYW5zIGtvZHVudSB0ZXJzaW5lIGNldmlyDQokYmNzID0gIiRiYXMkY2V2aXIkc29uIjsNCiANCmlmKCRiY3MhPT0kbGlzYW5za29kdSkvL2xpc2FucyBrb2R1bnUga29udHJvbCBldCBmYXJrIHZhcnNhIHN1bnVjdXlhIGJhZ2xhbiAtIDI0IHNhYXR0ZSBiaXIgbGlzYW5zIGtvZHUgZGVnaXNpcg0Kew0KJGxpc2Fuc19jZXZhcD1maWxlX2dldF9jb250ZW50cygnaHR0cDovL2luc3RhLmFkYW5hYmlsaXNpbS5uZXQvbGlzYW5zL2tvbnRyb2wucGhwP3NpdGU9Jy4kbGlzYW5zWydzaXRlJ10pOy8vbGlzYW5zIHN1bnVjdXN1bmRhbiBsaXNhbnMga29udHJvbHUgeWFwDQppZigkbGlzYW5zX2NldmFwIT0nTElTQU5TTEknKS8vc3VudWN1ZGFuIGdlbGVuIGNldmFwIExJU0FOU0xJIGRlZ2lsc2UNCiANCmRpZSgnQnUgc2l0ZXllIGFpdCBsaXNhbnMgYnVsdW5hbWFkaSEhIScpOy8vbGlzYW5zIGdlY2Vyc2l6IG1lc2FqaSB2ZXJpbGlyIGlzbGVtIGR1cnVyDQogDQokbHlheiA9IGZvcGVuKCdsaXNhbnMucGhwJywidysiKTsvL2xpc2Fucy5waHAgZG9zeWFzaW5pIGFjIHllbmkgbGlzYW5zIGtvZHVudSB5YXogYnUgZG9zeWEgeWF6xLFsYWJpbGlyIG9sbWFsxLFkxLFyDQpAZndyaXRlKCRseWF6LCc8P3BocA0KLyoqDQogICAgd3d3LnNvcnVuc3V6c2NyaXB0IC5jb20NCgkNCiAgICBCdSBkb3N5YXlhIGhlcmhhbmdpIGJpciBsaXNhbnMga29kdSB5YXptYW5peiBnZXJla21leg0KICAgIEFsbWlzIG9sZHVndW51eiB1cnVuZSBhaXQgbGlzYW5zIHNpc3RlbWRlIGFrdGlmIG9sZHVndW5kYQ0KICAgIExpc2FucyBrb2R1bnV6IG90b21hdGlrIG9sYXJhayBnaXJpbGVjZWt0aXIuDQoJDQoJWWVuaSBsaXNhbnMgYWxtYWsgaWNpbiB3d3cuc29ydW5zdXpzY3JpcHQgLmNvbSBhZHJlc2luaSB6aXlhcmV0IGVkZWJpbGlyc2luaXoNCiANCiAqLw0KJGxpc2Fuc2tvZHU9IicuJGJjcy4nIjsNCj8+Jyk7DQpmY2xvc2UoJGx5YXopOw0KfSANCmRlZmluZSgnSU5TVEFXRUJfVkVSU0lPTicsIHN0cl9yZXBsYWNlKCJJbnN0YVdlYlYiLCAiIiwgYmFzZW5hbWUoX19GSUxFX18sICIucGhwIikpKTsNCiAgICAgICAgaWYoJF9TRVJWRVJbIkhUVFBfVVNFUl9BR0VOVCJdID09ICIiKSB7DQogICAgICAgICAgICBoZWFkZXIoIkhUVFAvMS4xIDQwMyBGb3JiaWRkZW4iKTsNCiAgICAgICAgICAgIGVjaG8gJ0Jha8SxbSBNb2R1ISc7DQogICAgICAgICAgICBleGl0KCk7DQogICAgICAgIH0NCg0KY2xhc3MgSW5zdGFncmFtDQp7DQogICAgcHJvdGVjdGVkICR1c2VybmFtZTsNCiAgICBwcm90ZWN0ZWQgJHBhc3N3b3JkOw0KICAgIHByb3RlY3RlZCAkZGVidWc7DQogICAgcHJvdGVjdGVkICR1dWlkOw0KICAgIHByb3RlY3RlZCAkZGV2aWNlX2lkOw0KICAgIHByb3RlY3RlZCAkdXNlcm5hbWVfaWQ7DQovKioNCiAgICAgICAgICogQHZhciBTZXR0aW5ncw0KICAgICAgICAgKi8NCiAgICBwdWJsaWMgJHNldHRpbmdzOw0KICAgIHByb3RlY3RlZCAkdG9rZW47DQogICAgcHJvdGVjdGVkICRpc0xvZ2dlZEluID0gZmFsc2U7DQogICAgcHJvdGVjdGVkICRyYW5rX3Rva2VuOw0KICAgIHByb3RlY3RlZCAkSUdEYXRhUGF0aDsNCg0KICAgIC8qKg0KICAgICAgICAgKiBEZWZhdWx0IGNsYXNzIGNvbnN0cnVjdG9yLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICR1c2VybmFtZQ0KICAgICAgICAgKiAgIFlvdXIgSW5zdGFncmFtIHVzZXJuYW1lLg0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICRwYXNzd29yZA0KICAgICAgICAgKiAgIFlvdXIgSW5zdGFncmFtIHBhc3N3b3JkLg0KICAgICAgICAgKiBAcGFyYW0gICAgICAgICRkZWJ1Zw0KICAgICAgICAgKiAgIERlYnVnIG9uIG9yIG9mZiwgZmFsc2UgYnkgZGVmYXVsdC4NCiAgICAgICAgICogQHBhcmFtICAgICAgICAkSUdEYXRhUGF0aA0KICAgICAgICAgKiAgIERlZmF1bHQgZm9sZGVyIHRvIHN0b3JlIGRhdGEsIHlvdSBjYW4gY2hhbmdlIGl0Lg0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBfX2NvbnN0cnVjdCgkdXNlcm5hbWUsICRwYXNzd29yZCwgJGRlYnVnID0gZmFsc2UsICRJR0RhdGFQYXRoID0gTlVMTCwgJGZvcmNlVXNlcklQID0gZmFsc2UpDQogICAgew0KICAgICAgICAkdXNlcm5hbWUgPSB0cmltKCR1c2VybmFtZSk7DQogICAgICAgICRwYXNzd29yZCA9IHRyaW0oJHBhc3N3b3JkKTsNCiAgICAgICAgJHRoaXMtPmRlYnVnID0gJGRlYnVnOw0KICAgICAgICAkdGhpcy0+ZGV2aWNlX2lkID0gU2lnbmF0dXJlVXRpbHM6OmdlbmVyYXRlRGV2aWNlSWQobWQ1KCR1c2VybmFtZSAuICRwYXNzd29yZCkpOw0KICAgICAgICBpZiggIWlzX251bGwoJElHRGF0YVBhdGgpICkgDQogICAgICAgIHsNCiAgICAgICAgICAgICR0aGlzLT5JR0RhdGFQYXRoID0gJElHRGF0YVBhdGg7DQogICAgICAgIH0NCiAgICAgICAgZWxzZQ0KICAgICAgICB7DQogICAgICAgICAgICAkdGhpcy0+SUdEYXRhUGF0aCA9IFdvdzo6Z2V0KCJwcm9qZWN0L2Nvb2tpZVBhdGgiKSAuICJpbnN0YWdyYW0vIjsNCiAgICAgICAgfQ0KDQogICAgICAgICR0aGlzLT5zZXRVc2VyKCR1c2VybmFtZSwgJHBhc3N3b3JkLCAkZm9yY2VVc2VySVApOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBTZXQgdGhlIHVzZXIuIE1hbmFnZSBtdWx0aXBsZSBhY2NvdW50cy4NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkdXNlcm5hbWUNCiAgICAgICAgICogICBZb3VyIEluc3RhZ3JhbSB1c2VybmFtZS4NCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkcGFzc3dvcmQNCiAgICAgICAgICogICBZb3VyIEluc3RhZ3JhbSBwYXNzd29yZC4NCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gc2V0VXNlcigkdXNlcm5hbWUsICRwYXNzd29yZCwgJGZvcmNlVXNlcklQID0gZmFsc2UpDQogICAgew0KICAgICAgICAkdGhpcy0+dXNlcm5hbWUgPSAkdXNlcm5hbWU7DQogICAgICAgICR0aGlzLT5wYXNzd29yZCA9ICRwYXNzd29yZDsNCiAgICAgICAgJHRoaXMtPmNoZWNrU2V0dGluZ3MoJHVzZXJuYW1lLCAkZm9yY2VVc2VySVApOw0KICAgICAgICAkdGhpcy0+dXVpZCA9IFNpZ25hdHVyZVV0aWxzOjpnZW5lcmF0ZVVVSUQodHJ1ZSk7DQogICAgICAgIGlmKCBmaWxlX2V4aXN0cygkdGhpcy0+SUdEYXRhUGF0aCAuIChzdHJpbmcpICR0aGlzLT51c2VybmFtZSAuICIuZGF0IikgJiYgJHRoaXMtPnNldHRpbmdzLT5nZXQoInVzZXJuYW1lX2lkIikgIT0gTlVMTCAmJiAkdGhpcy0+c2V0dGluZ3MtPmdldCgidG9rZW4iKSAhPSBOVUxMICkgDQogICAgICAgIHsNCiAgICAgICAgICAgICR0aGlzLT5pc0xvZ2dlZEluID0gdHJ1ZTsNCiAgICAgICAgICAgICR0aGlzLT51c2VybmFtZV9pZCA9ICR0aGlzLT5zZXR0aW5ncy0+Z2V0KCJ1c2VybmFtZV9pZCIpOw0KICAgICAgICAgICAgJHRoaXMtPnJhbmtfdG9rZW4gPSAkdGhpcy0+dXNlcm5hbWVfaWQgLiAiXyIgLiAkdGhpcy0+dXVpZDsNCiAgICAgICAgICAgICR0aGlzLT50b2tlbiA9ICR0aGlzLT5zZXR0aW5ncy0+Z2V0KCJ0b2tlbiIpOw0KICAgICAgICB9DQogICAgICAgIGVsc2UNCiAgICAgICAgew0KICAgICAgICAgICAgJHRoaXMtPmlzTG9nZ2VkSW4gPSBmYWxzZTsNCiAgICAgICAgfQ0KDQogICAgfQ0KDQogICAgcHJvdGVjdGVkIGZ1bmN0aW9uIGNoZWNrU2V0dGluZ3MoJHVzZXJuYW1lLCAkZm9yY2VVc2VySVAgPSBmYWxzZSkNCiAgICB7DQogICAgICAgICR0aGlzLT5zZXR0aW5ncyA9IG5ldyBTZXR0aW5ncygkdGhpcy0+SUdEYXRhUGF0aCAuICR1c2VybmFtZSAuICIuY25mIik7DQogICAgICAgIGlmKCAkdGhpcy0+c2V0dGluZ3MtPmdldCgidmVyc2lvbiIpID09IE5VTEwgKSANCiAgICAgICAgew0KICAgICAgICAgICAgJHRoaXMtPnNldHRpbmdzLT5zZXQoInZlcnNpb24iLCBDb25zdGFudHM6OlZFUlNJT04pOw0KICAgICAgICB9DQoNCiAgICAgICAgaWYoICR0aGlzLT5zZXR0aW5ncy0+Z2V0KCJpcCIpID09IE5VTEwgfHwgJGZvcmNlVXNlcklQICkgDQogICAgICAgIHsNCiAgICAgICAgICAgICRpcEFkcmVzcyA9ICI3OC4iIC4gcmFuZCgxNjAsIDE5MSkgLiAiLiIgLiByYW5kKDEsIDI1NSkgLiAiLiIgLiByYW5kKDEsIDI1NSk7DQogICAgICAgICAgICBpZiggJGZvcmNlVXNlcklQICYmICFlbXB0eSgkX1NFUlZFUlsiUkVNT1RFX0FERFIiXSkgKSANCiAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAkaXBBZHJlc3MgPSAkX1NFUlZFUlsiUkVNT1RFX0FERFIiXTsNCiAgICAgICAgICAgIH0NCg0KICAgICAgICAgICAgJHRoaXMtPnNldHRpbmdzLT5zZXQoImlwIiwgJGlwQWRyZXNzKTsNCiAgICAgICAgfQ0KDQogICAgICAgIGlmKCBmaWxlX2V4aXN0cygkdGhpcy0+SUdEYXRhUGF0aCAuIChzdHJpbmcpICR1c2VybmFtZSAuICIuZGF0IikgJiYgKCR0aGlzLT5zZXR0aW5ncy0+Z2V0KCJ1c2VybmFtZV9pZCIpID09IE5VTEwgfHwgJHRoaXMtPnNldHRpbmdzLT5nZXQoInRva2VuIikgPT0gTlVMTCkgKSANCiAgICAgICAgew0KICAgICAgICAgICAgJGNvb2tpZURhdGEgPSBVdGlsczo6Y29va2llVG9BcnJheShmaWxlX2dldF9jb250ZW50cygkdGhpcy0+SUdEYXRhUGF0aCAuIChzdHJpbmcpICR1c2VybmFtZSAuICIuZGF0IiksICJpLmluc3RhZ3JhbS5jb20iKTsNCiAgICAgICAgICAgIGlmKCAkdGhpcy0+c2V0dGluZ3MtPmdldCgidXNlcm5hbWVfaWQiKSA9PSBOVUxMICYmIGlzc2V0KCRjb29raWVEYXRhWyJkc191c2VyX2lkIl0pICkgDQogICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgJHRoaXMtPnNldHRpbmdzLT5zZXQoInVzZXJuYW1lX2lkIiwgJGNvb2tpZURhdGFbImRzX3VzZXJfaWQiXSk7DQogICAgICAgICAgICB9DQoNCiAgICAgICAgICAgIGlmKCAkdGhpcy0+c2V0dGluZ3MtPmdldCgidG9rZW4iKSA9PSBOVUxMICYmIGlzc2V0KCRjb29raWVEYXRhWyJjc3JmdG9rZW4iXSkgKSANCiAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAkdGhpcy0+c2V0dGluZ3MtPnNldCgidG9rZW4iLCAkY29va2llRGF0YVsiY3NyZnRva2VuIl0pOw0KICAgICAgICAgICAgfQ0KDQogICAgICAgIH0NCg0KICAgICAgICBpZiggJHRoaXMtPnNldHRpbmdzLT5nZXQoInVzZXJfYWdlbnQiKSA9PSBOVUxMIHx8IHZlcnNpb25fY29tcGFyZSgkdGhpcy0+c2V0dGluZ3MtPmdldCgidmVyc2lvbiIpLCBDb25zdGFudHM6OlZFUlNJT04pID09IC0xICkgDQogICAgICAgIHsNCiAgICAgICAgICAgICR1c2VyQWdlbnQgPSBuZXcgVXNlckFnZW50KCR0aGlzKTsNCiAgICAgICAgICAgICR1YSA9ICR1c2VyQWdlbnQtPmJ1aWxkVXNlckFnZW50KCk7DQogICAgICAgICAgICAkdGhpcy0+c2V0dGluZ3MtPnNldCgidmVyc2lvbiIsIENvbnN0YW50czo6VkVSU0lPTik7DQogICAgICAgICAgICAkdGhpcy0+c2V0dGluZ3MtPnNldCgidXNlcl9hZ2VudCIsICR1YSk7DQogICAgICAgIH0NCg0KICAgIH0NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBnZXREYXRhKCkNCiAgICB7DQogICAgICAgIHJldHVybiBhcnJheSggInVzZXJuYW1lIiA9PiAkdGhpcy0+dXNlcm5hbWUsICJwYXNzd29yZCIgPT4gJHRoaXMtPnBhc3N3b3JkLCAidXNlcm5hbWVfaWQiID0+ICR0aGlzLT51c2VybmFtZV9pZCwgInV1aWQiID0+ICR0aGlzLT51dWlkLCAidG9rZW4iID0+ICR0aGlzLT50b2tlbiwgInJhbmtfdG9rZW4iID0+ICR0aGlzLT5yYW5rX3Rva2VuLCAidXNlcl9hZ2VudCIgPT4gJHRoaXMtPnNldHRpbmdzLT5nZXQoInVzZXJfYWdlbnQiKSwgImlwIiA9PiAkdGhpcy0+c2V0dGluZ3MtPmdldCgiaXAiKSApOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBMb2dpbiB0byBJbnN0YWdyYW0uDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBib29sICRmb3JjZQ0KICAgICAgICAgKiAgIEZvcmNlIGxvZ2luIHRvIEluc3RhZ3JhbSwgdGhpcyB3aWxsIGNyZWF0ZSBhIG5ldyBzZXNzaW9uDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICAgTG9naW4gZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBsb2dpbigkZm9yY2UgPSBmYWxzZSkNCiAgICB7DQogICAgICAgIGlmKCAhJHRoaXMtPmlzTG9nZ2VkSW4gfHwgJGZvcmNlICkgDQogICAgICAgIHsNCiAgICAgICAgICAgICRmZXRjaCA9ICR0aGlzLT5yZXF1ZXN0KCJzaS9mZXRjaF9oZWFkZXJzLz9jaGFsbGVuZ2VfdHlwZT1zaWdudXAmZ3VpZD0iIC4gU2lnbmF0dXJlVXRpbHM6OmdlbmVyYXRlVVVJRChmYWxzZSksIE5VTEwsIHRydWUpOw0KICAgICAgICAgICAgcHJlZ19tYXRjaCgiI1NldC1Db29raWU6IGNzcmZ0b2tlbj0oW147XSspIyIsICRmZXRjaFswXSwgJHRva2VuKTsNCiAgICAgICAgICAgICRkYXRhID0gYXJyYXkoICJwaG9uZV9pZCIgPT4gU2lnbmF0dXJlVXRpbHM6OmdlbmVyYXRlVVVJRCh0cnVlKSwgIl9jc3JmdG9rZW4iID0+ICR0b2tlblswXSwgInVzZXJuYW1lIiA9PiAkdGhpcy0+dXNlcm5hbWUsICJndWlkIiA9PiAkdGhpcy0+dXVpZCwgImRldmljZV9pZCIgPT4gJHRoaXMtPmRldmljZV9pZCwgInBhc3N3b3JkIiA9PiAkdGhpcy0+cGFzc3dvcmQsICJsb2dpbl9hdHRlbXB0X2NvdW50IiA9PiAiMCIgKTsNCiAgICAgICAgICAgICRsb2dpbiA9ICR0aGlzLT5yZXF1ZXN0KCJhY2NvdW50cy9sb2dpbi8iLCBTaWduYXR1cmVVdGlsczo6Z2VuZXJhdGVTaWduYXR1cmUoanNvbl9lbmNvZGUoJGRhdGEpKSwgdHJ1ZSk7DQogICAgICAgICAgICBpZiggJGxvZ2luWzFdWyJzdGF0dXMiXSA9PSAiZmFpbCIgKSANCiAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICB0aHJvdyBuZXcgRXhjZXB0aW9uKCRsb2dpblsxXVsibWVzc2FnZSJdKTsNCiAgICAgICAgICAgIH0NCg0KICAgICAgICAgICAgJHRoaXMtPmlzTG9nZ2VkSW4gPSB0cnVlOw0KICAgICAgICAgICAgJHRoaXMtPnVzZXJuYW1lX2lkID0gJGxvZ2luWzFdWyJsb2dnZWRfaW5fdXNlciJdWyJwayJdOw0KICAgICAgICAgICAgJHRoaXMtPnNldHRpbmdzLT5zZXQoInVzZXJuYW1lX2lkIiwgJHRoaXMtPnVzZXJuYW1lX2lkKTsNCiAgICAgICAgICAgICR0aGlzLT5yYW5rX3Rva2VuID0gJHRoaXMtPnVzZXJuYW1lX2lkIC4gIl8iIC4gJHRoaXMtPnV1aWQ7DQogICAgICAgICAgICBwcmVnX21hdGNoKCIjU2V0LUNvb2tpZTogY3NyZnRva2VuPShbXjtdKykjIiwgJGxvZ2luWzBdLCAkbWF0Y2gpOw0KICAgICAgICAgICAgJHRoaXMtPnRva2VuID0gJG1hdGNoWzFdOw0KICAgICAgICAgICAgJHRoaXMtPnNldHRpbmdzLT5zZXQoInRva2VuIiwgJHRoaXMtPnRva2VuKTsNCiAgICAgICAgICAgICR0aGlzLT5zeW5jRmVhdHVyZXMoKTsNCiAgICAgICAgICAgICR0aGlzLT5tZWdhcGhvbmVMb2coKTsNCiAgICAgICAgICAgIHJldHVybiAkbG9naW5bMV07DQogICAgICAgIH0NCg0KICAgICAgICAkY2hlY2sgPSAkdGhpcy0+dGltZWxpbmVGZWVkKCk7DQogICAgICAgIGlmKCBpc3NldCgkY2hlY2tbIm1lc3NhZ2UiXSkgJiYgJGNoZWNrWyJtZXNzYWdlIl0gPT0gImxvZ2luX3JlcXVpcmVkIiApIA0KICAgICAgICB7DQogICAgICAgICAgICByZXR1cm4gJHRoaXMtPmxvZ2luKHRydWUpOw0KICAgICAgICB9DQoNCiAgICAgICAgJHRoaXMtPm1lZ2FwaG9uZUxvZygpOw0KICAgICAgICByZXR1cm4gYXJyYXkoICJzdGF0dXMiID0+ICJvayIgKTsNCiAgICB9DQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gc3luY0ZlYXR1cmVzKCkNCiAgICB7DQogICAgICAgICRkYXRhID0ganNvbl9lbmNvZGUoYXJyYXkoICJfdXVpZCIgPT4gJHRoaXMtPnV1aWQsICJfdWlkIiA9PiAkdGhpcy0+dXNlcm5hbWVfaWQsICJpZCIgPT4gJHRoaXMtPnVzZXJuYW1lX2lkLCAiX2NzcmZ0b2tlbiIgPT4gJHRoaXMtPnRva2VuLCAiZXhwZXJpbWVudHMiID0+IENvbnN0YW50czo6RVhQRVJJTUVOVFMgKSk7DQogICAgICAgIHJldHVybiAkdGhpcy0+cmVxdWVzdCgicWUvc3luYy8iLCBTaWduYXR1cmVVdGlsczo6Z2VuZXJhdGVTaWduYXR1cmUoJGRhdGEpKVsxXTsNCiAgICB9DQoNCiAgICBwcm90ZWN0ZWQgZnVuY3Rpb24gYXV0b0NvbXBsZXRlVXNlckxpc3QoKQ0KICAgIHsNCiAgICAgICAgcmV0dXJuICR0aGlzLT5yZXF1ZXN0KCJmcmllbmRzaGlwcy9hdXRvY29tcGxldGVfdXNlcl9saXN0LyIpWzFdOw0KICAgIH0NCg0KICAgIHByb3RlY3RlZCBmdW5jdGlvbiB0aW1lbGluZUZlZWQoKQ0KICAgIHsNCiAgICAgICAgcmV0dXJuICR0aGlzLT5yZXF1ZXN0KCJmZWVkL3RpbWVsaW5lLyIpWzFdOw0KICAgIH0NCg0KICAgIHByb3RlY3RlZCBmdW5jdGlvbiBtZWdhcGhvbmVMb2coKQ0KICAgIHsNCiAgICAgICAgcmV0dXJuICR0aGlzLT5yZXF1ZXN0KCJtZWdhcGhvbmUvbG9nLyIpWzFdOw0KICAgIH0NCg0KICAgIHByb3RlY3RlZCBmdW5jdGlvbiBleHBvc2UoKQ0KICAgIHsNCiAgICAgICAgJGRhdGEgPSBqc29uX2VuY29kZShhcnJheSggIl91dWlkIiA9PiAkdGhpcy0+dXVpZCwgIl91aWQiID0+ICR0aGlzLT51c2VybmFtZV9pZCwgImlkIiA9PiAkdGhpcy0+dXNlcm5hbWVfaWQsICJfY3NyZnRva2VuIiA9PiAkdGhpcy0+dG9rZW4sICJleHBlcmltZW50IiA9PiAiaWdfYW5kcm9pZF9wcm9maWxlX2NvbnRleHR1YWxfZmVlZCIgKSk7DQogICAgICAgICR0aGlzLT5yZXF1ZXN0KCJxZS9leHBvc2UvIiwgU2lnbmF0dXJlVXRpbHM6OmdlbmVyYXRlU2lnbmF0dXJlKCRkYXRhKSk7DQogICAgICAgICR0aGlzLT5yZXF1ZXN0KCJxZS9leHBvc2UvIiwgU2lnbmF0dXJlVXRpbHM6OmdlbmVyYXRlU2lnbmF0dXJlKCRkYXRhKSlbMV07DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIExvZ2luIHRvIEluc3RhZ3JhbS4NCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBib29sDQogICAgICAgICAqICAgIFJldHVybnMgdHJ1ZSBpZiBsb2dnZWQgb3V0IGNvcnJlY3RseQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBsb2dvdXQoKQ0KICAgIHsNCiAgICAgICAgJGxvZ291dCA9ICR0aGlzLT5yZXF1ZXN0KCJhY2NvdW50cy9sb2dvdXQvIik7DQogICAgICAgIGlmKCAkbG9nb3V0ID09ICJvayIgKSANCiAgICAgICAgew0KICAgICAgICAgICAgcmV0dXJuIHRydWU7DQogICAgICAgIH0NCg0KICAgICAgICByZXR1cm4gZmFsc2U7DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIFVwbG9hZCBwaG90byB0byBJbnN0YWdyYW0uDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJHBob3RvDQogICAgICAgICAqICAgICAgICAgICAgICAgICAgICAgICAgUGF0aCB0byB5b3VyIHBob3RvDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJGNhcHRpb24NCiAgICAgICAgICogICAgICAgICAgICAgICAgICAgICAgICBDYXB0aW9uIHRvIGJlIGluY2x1ZGVkIGluIHlvdXIgcGhvdG8uDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICAgICAgICAgICAgICBVcGxvYWQgZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiB1cGxvYWRQaG90bygkcGhvdG8sICRjYXB0aW9uID0gTlVMTCwgJHVwbG9hZF9pZCA9IE5VTEwpDQogICAgew0KICAgICAgICAkZW5kcG9pbnQgPSBDb25zdGFudHM6OkFQSV9VUkwgLiAidXBsb2FkL3Bob3RvLyI7DQogICAgICAgICRib3VuZGFyeSA9ICR0aGlzLT51dWlkOw0KICAgICAgICBpZiggIWlzX251bGwoJHVwbG9hZF9pZCkgKSANCiAgICAgICAgew0KICAgICAgICAgICAgJGZpbGVUb1VwbG9hZCA9IFV0aWxzOjpjcmVhdGVWaWRlb0ljb24oJHBob3RvKTsNCiAgICAgICAgfQ0KICAgICAgICBlbHNlDQogICAgICAgIHsNCiAgICAgICAgICAgICR1cGxvYWRfaWQgPSBudW1iZXJfZm9ybWF0KHJvdW5kKG1pY3JvdGltZSh0cnVlKSAqIDEwMDApLCAwLCAiIiwgIiIpOw0KICAgICAgICAgICAgJGZpbGVUb1VwbG9hZCA9IGZpbGVfZ2V0X2NvbnRlbnRzKCRwaG90byk7DQogICAgICAgIH0NCg0KICAgICAgICAkYm9kaWVzID0gYXJyYXkoIGFycmF5KCAidHlwZSIgPT4gImZvcm0tZGF0YSIsICJuYW1lIiA9PiAidXBsb2FkX2lkIiwgImRhdGEiID0+ICR1cGxvYWRfaWQgKSwgYXJyYXkoICJ0eXBlIiA9PiAiZm9ybS1kYXRhIiwgIm5hbWUiID0+ICJfdXVpZCIsICJkYXRhIiA9PiAkdGhpcy0+dXVpZCApLCBhcnJheSggInR5cGUiID0+ICJmb3JtLWRhdGEiLCAibmFtZSIgPT4gIl9jc3JmdG9rZW4iLCAiZGF0YSIgPT4gJHRoaXMtPnRva2VuICksIGFycmF5KCAidHlwZSIgPT4gImZvcm0tZGF0YSIsICJuYW1lIiA9PiAiaW1hZ2VfY29tcHJlc3Npb24iLCAiZGF0YSIgPT4gIntcImxpYl9uYW1lXCI6XCJqdFwiLFwibGliX3ZlcnNpb25cIjpcIjEuMy4wXCIsXCJxdWFsaXR5XCI6XCI3MFwifSIgKSwgYXJyYXkoICJ0eXBlIiA9PiAiZm9ybS1kYXRhIiwgIm5hbWUiID0+ICJwaG90byIsICJkYXRhIiA9PiAkZmlsZVRvVXBsb2FkLCAiZmlsZW5hbWUiID0+ICJwZW5kaW5nX21lZGlhXyIgLiBudW1iZXJfZm9ybWF0KHJvdW5kKG1pY3JvdGltZSh0cnVlKSAqIDEwMDApLCAwLCAiIiwgIiIpIC4gIi5qcGciLCAiaGVhZGVycyIgPT4gYXJyYXkoICJDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiBiaW5hcnkiLCAiQ29udGVudC10eXBlOiBhcHBsaWNhdGlvbi9vY3RldC1zdHJlYW0iICkgKSApOw0KICAgICAgICAkZGF0YSA9ICR0aGlzLT5idWlsZEJvZHkoJGJvZGllcywgJGJvdW5kYXJ5KTsNCiAgICAgICAgJGhlYWRlcnMgPSBhcnJheSggIlgtSUctQ2FwYWJpbGl0aWVzOiAiIC4gQ29uc3RhbnRzOjpYX0lHX0NhcGFiaWxpdGllcywgIlgtSUctQ29ubmVjdGlvbi1UeXBlOiBXSUZJIiwgIkNvbnRlbnQtdHlwZTogbXVsdGlwYXJ0L2Zvcm0tZGF0YTsgYm91bmRhcnk9IiAuICRib3VuZGFyeSwgIkNvbnRlbnQtTGVuZ3RoOiAiIC4gc3RybGVuKCRkYXRhKSwgIkFjY2VwdC1MYW5ndWFnZTogdHItVFIiLCAiQWNjZXB0LUVuY29kaW5nOiBnemlwLCBkZWZsYXRlIiwgIkNvbm5lY3Rpb246IGNsb3NlIiwgIlhfRk9SV0FSREVEX0ZPUjogIiAuICR0aGlzLT5zZXR0aW5ncy0+Z2V0KCJpcCIpLCAiUkVNT1RFX0FERFI6ICIgLiAkdGhpcy0+c2V0dGluZ3MtPmdldCgiaXAiKSApOw0KICAgICAgICAkY2ggPSBjdXJsX2luaXQoKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1VSTCwgJGVuZHBvaW50KTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1VTRVJBR0VOVCwgJHRoaXMtPnNldHRpbmdzLT5nZXQoInVzZXJfYWdlbnQiKSk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9SRVRVUk5UUkFOU0ZFUiwgdHJ1ZSk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9GT0xMT1dMT0NBVElPTiwgdHJ1ZSk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9IRUFERVIsIHRydWUpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfVkVSQk9TRSwgJHRoaXMtPmRlYnVnKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1NTTF9WRVJJRllQRUVSLCBmYWxzZSk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9TU0xfVkVSSUZZSE9TVCwgZmFsc2UpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfSFRUUEhFQURFUiwgJGhlYWRlcnMpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfQ09PS0lFRklMRSwgJHRoaXMtPklHRGF0YVBhdGggLiAoc3RyaW5nKSAkdGhpcy0+dXNlcm5hbWUgLiAiLmRhdCIpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfQ09PS0lFSkFSLCAkdGhpcy0+SUdEYXRhUGF0aCAuIChzdHJpbmcpICR0aGlzLT51c2VybmFtZSAuICIuZGF0Iik7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9QT1NULCB0cnVlKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1BPU1RGSUVMRFMsICRkYXRhKTsNCiAgICAgICAgJHJlc3AgPSBjdXJsX2V4ZWMoJGNoKTsNCiAgICAgICAgJGhlYWRlcl9sZW4gPSBjdXJsX2dldGluZm8oJGNoLCBDVVJMSU5GT19IRUFERVJfU0laRSk7DQogICAgICAgICRoZWFkZXIgPSBzdWJzdHIoJHJlc3AsIDAsICRoZWFkZXJfbGVuKTsNCiAgICAgICAgJHVwbG9hZCA9IGpzb25fZGVjb2RlKHN1YnN0cigkcmVzcCwgJGhlYWRlcl9sZW4pLCB0cnVlKTsNCiAgICAgICAgY3VybF9jbG9zZSgkY2gpOw0KICAgICAgICBpZiggJHVwbG9hZFsic3RhdHVzIl0gPT0gImZhaWwiICkgDQogICAgICAgIHsNCiAgICAgICAgICAgIHRocm93IG5ldyBFeGNlcHRpb24oJHVwbG9hZFsibWVzc2FnZSJdKTsNCiAgICAgICAgfQ0KDQogICAgICAgIGlmKCAkdGhpcy0+ZGVidWcgKSANCiAgICAgICAgew0KICAgICAgICAgICAgZWNobyAiUkVTUE9OU0U6ICIgLiBzdWJzdHIoJHJlc3AsICRoZWFkZXJfbGVuKSAuICJcblxuIjsNCiAgICAgICAgfQ0KDQogICAgICAgICRjb25maWd1cmUgPSAkdGhpcy0+Y29uZmlndXJlKCR1cGxvYWRbInVwbG9hZF9pZCJdLCAkcGhvdG8sICRjYXB0aW9uKTsNCiAgICAgICAgJHRoaXMtPmV4cG9zZSgpOw0KICAgICAgICByZXR1cm4gJGNvbmZpZ3VyZTsNCiAgICB9DQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gdXBsb2FkVmlkZW8oJHZpZGVvLCAkY2FwdGlvbiA9IE5VTEwpDQogICAgew0KICAgICAgICAkdmlkZW9EYXRhID0gZmlsZV9nZXRfY29udGVudHMoJHZpZGVvKTsNCiAgICAgICAgJGVuZHBvaW50ID0gQ29uc3RhbnRzOjpBUElfVVJMIC4gInVwbG9hZC92aWRlby8iOw0KICAgICAgICAkYm91bmRhcnkgPSAkdGhpcy0+dXVpZDsNCiAgICAgICAgJHVwbG9hZF9pZCA9IHJvdW5kKG1pY3JvdGltZSh0cnVlKSAqIDEwMDApOw0KICAgICAgICAkYm9kaWVzID0gYXJyYXkoIGFycmF5KCAidHlwZSIgPT4gImZvcm0tZGF0YSIsICJuYW1lIiA9PiAidXBsb2FkX2lkIiwgImRhdGEiID0+ICR1cGxvYWRfaWQgKSwgYXJyYXkoICJ0eXBlIiA9PiAiZm9ybS1kYXRhIiwgIm5hbWUiID0+ICJfY3NyZnRva2VuIiwgImRhdGEiID0+ICR0aGlzLT50b2tlbiApLCBhcnJheSggInR5cGUiID0+ICJmb3JtLWRhdGEiLCAibmFtZSIgPT4gIm1lZGlhX3R5cGUiLCAiZGF0YSIgPT4gIjIiICksIGFycmF5KCAidHlwZSIgPT4gImZvcm0tZGF0YSIsICJuYW1lIiA9PiAiX3V1aWQiLCAiZGF0YSIgPT4gJHRoaXMtPnV1aWQgKSApOw0KICAgICAgICAkZGF0YSA9ICR0aGlzLT5idWlsZEJvZHkoJGJvZGllcywgJGJvdW5kYXJ5KTsNCiAgICAgICAgJGhlYWRlcnMgPSBhcnJheSggIkNvbm5lY3Rpb246IGtlZXAtYWxpdmUiLCAiQWNjZXB0OiAqLyoiLCAiSG9zdDogaS5pbnN0YWdyYW0uY29tIiwgIkNvbnRlbnQtdHlwZTogbXVsdGlwYXJ0L2Zvcm0tZGF0YTsgYm91bmRhcnk9IiAuICRib3VuZGFyeSwgIkFjY2VwdC1MYW5ndWFnZTogdHItVFIiLCAiWF9GT1JXQVJERURfRk9SOiAiIC4gJHRoaXMtPnNldHRpbmdzLT5nZXQoImlwIiksICJSRU1PVEVfQUREUjogIiAuICR0aGlzLT5zZXR0aW5ncy0+Z2V0KCJpcCIpICk7DQogICAgICAgICRjaCA9IGN1cmxfaW5pdCgpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfVVJMLCAkZW5kcG9pbnQpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfVVNFUkFHRU5ULCAkdGhpcy0+c2V0dGluZ3MtPmdldCgidXNlcl9hZ2VudCIpKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1JFVFVSTlRSQU5TRkVSLCB0cnVlKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX0ZPTExPV0xPQ0FUSU9OLCB0cnVlKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX0hFQURFUiwgdHJ1ZSk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9WRVJCT1NFLCBmYWxzZSk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9IVFRQSEVBREVSLCAkaGVhZGVycyk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9DT09LSUVGSUxFLCAkdGhpcy0+SUdEYXRhUGF0aCAuIChzdHJpbmcpICR0aGlzLT51c2VybmFtZSAuICIuZGF0Iik7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9DT09LSUVKQVIsICR0aGlzLT5JR0RhdGFQYXRoIC4gKHN0cmluZykgJHRoaXMtPnVzZXJuYW1lIC4gIi5kYXQiKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1BPU1QsIHRydWUpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfUE9TVEZJRUxEUywgJGRhdGEpOw0KICAgICAgICAkcmVzcCA9IGN1cmxfZXhlYygkY2gpOw0KICAgICAgICAkaGVhZGVyX2xlbiA9IGN1cmxfZ2V0aW5mbygkY2gsIENVUkxJTkZPX0hFQURFUl9TSVpFKTsNCiAgICAgICAgJGJvZHkgPSBqc29uX2RlY29kZShzdWJzdHIoJHJlc3AsICRoZWFkZXJfbGVuKSwgdHJ1ZSk7DQogICAgICAgICR1cGxvYWRVcmwgPSAkYm9keVsidmlkZW9fdXBsb2FkX3VybHMiXVszXVsidXJsIl07DQogICAgICAgICRqb2IgPSAkYm9keVsidmlkZW9fdXBsb2FkX3VybHMiXVszXVsiam9iIl07DQogICAgICAgICRyZXF1ZXN0X3NpemUgPSBmbG9vcihzdHJsZW4oJHZpZGVvRGF0YSkgLyA0KTsNCiAgICAgICAgJGxhc3RSZXF1ZXN0RXh0cmEgPSBzdHJsZW4oJHZpZGVvRGF0YSkgLSAkcmVxdWVzdF9zaXplICogNDsNCiAgICAgICAgZm9yKCAkYSA9IDA7ICRhIDw9IDM7ICRhKysgKSANCiAgICAgICAgew0KICAgICAgICAgICAgJHN0YXJ0ID0gJGEgKiAkcmVxdWVzdF9zaXplOw0KICAgICAgICAgICAgJGVuZCA9ICgkYSArIDEpICogJHJlcXVlc3Rfc2l6ZSArICgoJGEgPT0gMyA/ICRsYXN0UmVxdWVzdEV4dHJhIDogMCkpOw0KICAgICAgICAgICAgJGhlYWRlcnMgPSBhcnJheSggIkNvbm5lY3Rpb246IGtlZXAtYWxpdmUiLCAiQWNjZXB0OiAqLyoiLCAiSG9zdDogdXBsb2FkLmluc3RhZ3JhbS5jb20iLCAiQ29va2llMjogXCRWZXJzaW9uPTEiLCAiQWNjZXB0LUVuY29kaW5nOiBnemlwLCBkZWZsYXRlIiwgIkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24vb2N0ZXQtc3RyZWFtIiwgIlNlc3Npb24tSUQ6ICIgLiAkdXBsb2FkX2lkLCAiQWNjZXB0LUxhbmd1YWdlOiB0ci1UUiIsICJDb250ZW50LURpc3Bvc2l0aW9uOiBhdHRhY2htZW50OyBmaWxlbmFtZT1cInZpZGVvLm1vdlwiIiwgIkNvbnRlbnQtTGVuZ3RoOiAiIC4gKCRlbmQgLSAkc3RhcnQpLCAiQ29udGVudC1SYW5nZTogIiAuICJieXRlcyAiIC4gJHN0YXJ0IC4gIi0iIC4gKCRlbmQgLSAxKSAuICIvIiAuIHN0cmxlbigkdmlkZW9EYXRhKSwgImpvYjogIiAuICRqb2IgKTsNCiAgICAgICAgICAgICRjaCA9IGN1cmxfaW5pdCgpOw0KICAgICAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1VSTCwgJHVwbG9hZFVybCk7DQogICAgICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfVVNFUkFHRU5ULCAkdGhpcy0+c2V0dGluZ3MtPmdldCgidXNlcl9hZ2VudCIpKTsNCiAgICAgICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9DVVNUT01SRVFVRVNULCAiUE9TVCIpOw0KICAgICAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1JFVFVSTlRSQU5TRkVSLCB0cnVlKTsNCiAgICAgICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9GT0xMT1dMT0NBVElPTiwgdHJ1ZSk7DQogICAgICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfSEVBREVSLCB0cnVlKTsNCiAgICAgICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9WRVJCT1NFLCBmYWxzZSk7DQogICAgICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfSFRUUEhFQURFUiwgJGhlYWRlcnMpOw0KICAgICAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX0NPT0tJRUZJTEUsICR0aGlzLT5JR0RhdGFQYXRoIC4gKHN0cmluZykgJHRoaXMtPnVzZXJuYW1lIC4gIi5kYXQiKTsNCiAgICAgICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9DT09LSUVKQVIsICR0aGlzLT5JR0RhdGFQYXRoIC4gKHN0cmluZykgJHRoaXMtPnVzZXJuYW1lIC4gIi5kYXQiKTsNCiAgICAgICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9QT1NULCB0cnVlKTsNCiAgICAgICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9QT1NURklFTERTLCBzdWJzdHIoJHZpZGVvRGF0YSwgJHN0YXJ0LCAkZW5kKSk7DQogICAgICAgICAgICAkcmVzdWx0ID0gY3VybF9leGVjKCRjaCk7DQogICAgICAgICAgICAkaGVhZGVyX2xlbiA9IGN1cmxfZ2V0aW5mbygkY2gsIENVUkxJTkZPX0hFQURFUl9TSVpFKTsNCiAgICAgICAgICAgICRib2R5ID0gc3Vic3RyKCRyZXN1bHQsICRoZWFkZXJfbGVuKTsNCiAgICAgICAgICAgICRhcnJheVtdID0gYXJyYXkoICRib2R5ICk7DQogICAgICAgIH0NCiAgICAgICAgJHJlc3AgPSBjdXJsX2V4ZWMoJGNoKTsNCiAgICAgICAgJGhlYWRlcl9sZW4gPSBjdXJsX2dldGluZm8oJGNoLCBDVVJMSU5GT19IRUFERVJfU0laRSk7DQogICAgICAgICRoZWFkZXIgPSBzdWJzdHIoJHJlc3AsIDAsICRoZWFkZXJfbGVuKTsNCiAgICAgICAgJHVwbG9hZCA9IGpzb25fZGVjb2RlKHN1YnN0cigkcmVzcCwgJGhlYWRlcl9sZW4pLCB0cnVlKTsNCiAgICAgICAgY3VybF9jbG9zZSgkY2gpOw0KICAgICAgICBpZiggJHVwbG9hZFsic3RhdHVzIl0gPT0gImZhaWwiICkgDQogICAgICAgIHsNCiAgICAgICAgICAgIHRocm93IG5ldyBFeGNlcHRpb24oJHVwbG9hZFsibWVzc2FnZSJdKTsNCiAgICAgICAgfQ0KDQogICAgICAgIGlmKCAkdGhpcy0+ZGVidWcgKSANCiAgICAgICAgew0KICAgICAgICAgICAgZWNobyAiUkVTUE9OU0U6ICIgLiBzdWJzdHIoJHJlc3AsICRoZWFkZXJfbGVuKSAuICJcblxuIjsNCiAgICAgICAgfQ0KDQogICAgICAgICRjb25maWd1cmUgPSAkdGhpcy0+Y29uZmlndXJlVmlkZW8oJHVwbG9hZF9pZCwgJHZpZGVvLCAkY2FwdGlvbik7DQogICAgICAgICR0aGlzLT5leHBvc2UoKTsNCiAgICAgICAgcmV0dXJuICRjb25maWd1cmU7DQogICAgfQ0KDQogICAgcHVibGljIGZ1bmN0aW9uIGRpcmVjdF9tZXNzYWdlKCRyZWNpcGllbnRzLCAkdGV4dCkNCiAgICB7DQogICAgICAgIGlmKCAhaXNfYXJyYXkoJHJlY2lwaWVudHMpICkgDQogICAgICAgIHsNCiAgICAgICAgICAgICRyZWNpcGllbnRzID0gYXJyYXkoICRyZWNpcGllbnRzICk7DQogICAgICAgIH0NCg0KICAgICAgICAkc3RyaW5nID0gYXJyYXkoICApOw0KICAgICAgICBmb3JlYWNoKCAkcmVjaXBpZW50cyBhcyAkcmVjaXBpZW50ICkgDQogICAgICAgIHsNCiAgICAgICAgICAgICRzdHJpbmdbXSA9ICJcIiIgLiAkcmVjaXBpZW50IC4gIlwiIjsNCiAgICAgICAgfQ0KICAgICAgICAkcmVjaXBpZW50X3VzZXJzID0gaW1wbG9kZSgiLCIsICRzdHJpbmcpOw0KICAgICAgICAkZW5kcG9pbnQgPSBDb25zdGFudHM6OkFQSV9VUkwgLiAiZGlyZWN0X3YyL3RocmVhZHMvYnJvYWRjYXN0L3RleHQvIjsNCiAgICAgICAgJGJvdW5kYXJ5ID0gJHRoaXMtPnV1aWQ7DQogICAgICAgICRib2RpZXMgPSBhcnJheSggYXJyYXkoICJ0eXBlIiA9PiAiZm9ybS1kYXRhIiwgIm5hbWUiID0+ICJyZWNpcGllbnRfdXNlcnMiLCAiZGF0YSIgPT4gIltbIiAuICRyZWNpcGllbnRfdXNlcnMgLiAiXV0iICksIGFycmF5KCAidHlwZSIgPT4gImZvcm0tZGF0YSIsICJuYW1lIiA9PiAiY2xpZW50X2NvbnRleHQiLCAiZGF0YSIgPT4gJHRoaXMtPnV1aWQgKSwgYXJyYXkoICJ0eXBlIiA9PiAiZm9ybS1kYXRhIiwgIm5hbWUiID0+ICJ0aHJlYWRfaWRzIiwgImRhdGEiID0+ICJbXCIwXCJdIiApLCBhcnJheSggInR5cGUiID0+ICJmb3JtLWRhdGEiLCAibmFtZSIgPT4gInRleHQiLCAiZGF0YSIgPT4gKGlzX251bGwoJHRleHQpID8gIiIgOiAkdGV4dCkgKSApOw0KICAgICAgICAkZGF0YSA9ICR0aGlzLT5idWlsZEJvZHkoJGJvZGllcywgJGJvdW5kYXJ5KTsNCiAgICAgICAgJGhlYWRlcnMgPSBhcnJheSggIlByb3h5LUNvbm5lY3Rpb246IGtlZXAtYWxpdmUiLCAiQ29ubmVjdGlvbjoga2VlcC1hbGl2ZSIsICJBY2NlcHQ6ICovKiIsICJDb250ZW50LXR5cGU6IG11bHRpcGFydC9mb3JtLWRhdGE7IGJvdW5kYXJ5PSIgLiAkYm91bmRhcnksICJBY2NlcHQtTGFuZ3VhZ2U6IHRyLVRSIiApOw0KICAgICAgICAkY2ggPSBjdXJsX2luaXQoKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1VSTCwgJGVuZHBvaW50KTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1VTRVJBR0VOVCwgJHRoaXMtPnNldHRpbmdzLT5nZXQoInVzZXJfYWdlbnQiKSk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9SRVRVUk5UUkFOU0ZFUiwgdHJ1ZSk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9GT0xMT1dMT0NBVElPTiwgdHJ1ZSk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9IRUFERVIsIHRydWUpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfVkVSQk9TRSwgJHRoaXMtPmRlYnVnKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1NTTF9WRVJJRllQRUVSLCBmYWxzZSk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9TU0xfVkVSSUZZSE9TVCwgZmFsc2UpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfSFRUUEhFQURFUiwgJGhlYWRlcnMpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfQ09PS0lFRklMRSwgJHRoaXMtPklHRGF0YVBhdGggLiAoc3RyaW5nKSAkdGhpcy0+dXNlcm5hbWUgLiAiLmRhdCIpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfQ09PS0lFSkFSLCAkdGhpcy0+SUdEYXRhUGF0aCAuIChzdHJpbmcpICR0aGlzLT51c2VybmFtZSAuICIuZGF0Iik7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9QT1NULCB0cnVlKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1BPU1RGSUVMRFMsICRkYXRhKTsNCiAgICAgICAgJHJlc3AgPSBjdXJsX2V4ZWMoJGNoKTsNCiAgICAgICAgJGhlYWRlcl9sZW4gPSBjdXJsX2dldGluZm8oJGNoLCBDVVJMSU5GT19IRUFERVJfU0laRSk7DQogICAgICAgICRoZWFkZXIgPSBzdWJzdHIoJHJlc3AsIDAsICRoZWFkZXJfbGVuKTsNCiAgICAgICAgJHVwbG9hZCA9IGpzb25fZGVjb2RlKHN1YnN0cigkcmVzcCwgJGhlYWRlcl9sZW4pLCB0cnVlKTsNCiAgICAgICAgY3VybF9jbG9zZSgkY2gpOw0KICAgIH0NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBkaXJlY3Rfc2hhcmUoJG1lZGlhX2lkLCAkcmVjaXBpZW50cywgJHRleHQgPSBOVUxMKQ0KICAgIHsNCiAgICAgICAgaWYoICFpc19hcnJheSgkcmVjaXBpZW50cykgKSANCiAgICAgICAgew0KICAgICAgICAgICAgJHJlY2lwaWVudHMgPSBhcnJheSggJHJlY2lwaWVudHMgKTsNCiAgICAgICAgfQ0KDQogICAgICAgICRzdHJpbmcgPSBhcnJheSggICk7DQogICAgICAgIGZvcmVhY2goICRyZWNpcGllbnRzIGFzICRyZWNpcGllbnQgKSANCiAgICAgICAgew0KICAgICAgICAgICAgJHN0cmluZ1tdID0gIlwiIiAuICRyZWNpcGllbnQgLiAiXCIiOw0KICAgICAgICB9DQogICAgICAgICRyZWNpcGllbnRfdXNlcnMgPSBpbXBsb2RlKCIsIiwgJHN0cmluZyk7DQogICAgICAgICRlbmRwb2ludCA9IENvbnN0YW50czo6QVBJX1VSTCAuICJkaXJlY3RfdjIvdGhyZWFkcy9icm9hZGNhc3QvbWVkaWFfc2hhcmUvP21lZGlhX3R5cGU9cGhvdG8iOw0KICAgICAgICAkYm91bmRhcnkgPSAkdGhpcy0+dXVpZDsNCiAgICAgICAgJGJvZGllcyA9IGFycmF5KCBhcnJheSggInR5cGUiID0+ICJmb3JtLWRhdGEiLCAibmFtZSIgPT4gIm1lZGlhX2lkIiwgImRhdGEiID0+ICRtZWRpYV9pZCApLCBhcnJheSggInR5cGUiID0+ICJmb3JtLWRhdGEiLCAibmFtZSIgPT4gInJlY2lwaWVudF91c2VycyIsICJkYXRhIiA9PiAiW1siIC4gJHJlY2lwaWVudF91c2VycyAuICJdXSIgKSwgYXJyYXkoICJ0eXBlIiA9PiAiZm9ybS1kYXRhIiwgIm5hbWUiID0+ICJjbGllbnRfY29udGV4dCIsICJkYXRhIiA9PiAkdGhpcy0+dXVpZCApLCBhcnJheSggInR5cGUiID0+ICJmb3JtLWRhdGEiLCAibmFtZSIgPT4gInRocmVhZF9pZHMiLCAiZGF0YSIgPT4gIltcIjBcIl0iICksIGFycmF5KCAidHlwZSIgPT4gImZvcm0tZGF0YSIsICJuYW1lIiA9PiAidGV4dCIsICJkYXRhIiA9PiAoaXNfbnVsbCgkdGV4dCkgPyAiIiA6ICR0ZXh0KSApICk7DQogICAgICAgICRkYXRhID0gJHRoaXMtPmJ1aWxkQm9keSgkYm9kaWVzLCAkYm91bmRhcnkpOw0KICAgICAgICAkaGVhZGVycyA9IGFycmF5KCAiUHJveHktQ29ubmVjdGlvbjoga2VlcC1hbGl2ZSIsICJDb25uZWN0aW9uOiBrZWVwLWFsaXZlIiwgIkFjY2VwdDogKi8qIiwgIkNvbnRlbnQtdHlwZTogbXVsdGlwYXJ0L2Zvcm0tZGF0YTsgYm91bmRhcnk9IiAuICRib3VuZGFyeSwgIkFjY2VwdC1MYW5ndWFnZTogdHItVFIiICk7DQogICAgICAgICRjaCA9IGN1cmxfaW5pdCgpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfVVJMLCAkZW5kcG9pbnQpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfVVNFUkFHRU5ULCAkdGhpcy0+c2V0dGluZ3MtPmdldCgidXNlcl9hZ2VudCIpKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1JFVFVSTlRSQU5TRkVSLCB0cnVlKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX0ZPTExPV0xPQ0FUSU9OLCB0cnVlKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX0hFQURFUiwgdHJ1ZSk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9WRVJCT1NFLCAkdGhpcy0+ZGVidWcpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfU1NMX1ZFUklGWVBFRVIsIGZhbHNlKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1NTTF9WRVJJRllIT1NULCBmYWxzZSk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9IVFRQSEVBREVSLCAkaGVhZGVycyk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9DT09LSUVGSUxFLCAkdGhpcy0+SUdEYXRhUGF0aCAuIChzdHJpbmcpICR0aGlzLT51c2VybmFtZSAuICIuZGF0Iik7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9DT09LSUVKQVIsICR0aGlzLT5JR0RhdGFQYXRoIC4gKHN0cmluZykgJHRoaXMtPnVzZXJuYW1lIC4gIi5kYXQiKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1BPU1QsIHRydWUpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfUE9TVEZJRUxEUywgJGRhdGEpOw0KICAgICAgICAkcmVzcCA9IGN1cmxfZXhlYygkY2gpOw0KICAgICAgICAkaGVhZGVyX2xlbiA9IGN1cmxfZ2V0aW5mbygkY2gsIENVUkxJTkZPX0hFQURFUl9TSVpFKTsNCiAgICAgICAgJGhlYWRlciA9IHN1YnN0cigkcmVzcCwgMCwgJGhlYWRlcl9sZW4pOw0KICAgICAgICAkdXBsb2FkID0ganNvbl9kZWNvZGUoc3Vic3RyKCRyZXNwLCAkaGVhZGVyX2xlbiksIHRydWUpOw0KICAgICAgICBjdXJsX2Nsb3NlKCRjaCk7DQogICAgfQ0KDQogICAgcHJvdGVjdGVkIGZ1bmN0aW9uIGNvbmZpZ3VyZVZpZGVvKCR1cGxvYWRfaWQsICR2aWRlbywgJGNhcHRpb24gPSAiIikNCiAgICB7DQogICAgICAgICR0aGlzLT51cGxvYWRQaG90bygkdmlkZW8sICRjYXB0aW9uLCAkdXBsb2FkX2lkKTsNCiAgICAgICAgbGlzdCgkc2l6ZSkgPSBnZXRpbWFnZXNpemUoJHZpZGVvKTsNCiAgICAgICAgJHBvc3QgPSBqc29uX2VuY29kZShhcnJheSggInVwbG9hZF9pZCIgPT4gJHVwbG9hZF9pZCwgInNvdXJjZV90eXBlIiA9PiAiMyIsICJwb3N0ZXJfZnJhbWVfaW5kZXgiID0+IDAsICJsZW5ndGgiID0+IDAsICJhdWRpb19tdXRlZCIgPT4gZmFsc2UsICJmaWx0ZXJfdHlwZSIgPT4gIjAiLCAidmlkZW9fcmVzdWx0IiA9PiAiZGVwcmVjYXRlZCIsICJjbGlwcyIgPT4gYXJyYXkoICJsZW5ndGgiID0+IFV0aWxzOjpnZXRTZWNvbmRzKCR2aWRlbyksICJzb3VyY2VfdHlwZSIgPT4gIjMiLCAiY2FtZXJhX3Bvc2l0aW9uIiA9PiAiYmFjayIgKSwgImV4dHJhIiA9PiBhcnJheSggInNvdXJjZV93aWR0aCIgPT4gOTYwLCAic291cmNlX2hlaWdodCIgPT4gMTI4MCApLCAiZGV2aWNlIiA9PiBhcnJheSggIm1hbnVmYWN0dXJlciIgPT4gIlhpYW9taSIsICJtb2RlbCIgPT4gIkhNIDFTVyIsICJhbmRyb2lkX3ZlcnNpb24iID0+IDE4LCAiYW5kcm9pZF9yZWxlYXNlIiA9PiAiNC4zIiApLCAiX2NzcmZ0b2tlbiIgPT4gJHRoaXMtPnRva2VuLCAiX3V1aWQiID0+ICR0aGlzLT51dWlkLCAiX3VpZCIgPT4gJHRoaXMtPnVzZXJuYW1lX2lkLCAiY2FwdGlvbiIgPT4gJGNhcHRpb24gKSk7DQogICAgICAgICRwb3N0ID0gc3RyX3JlcGxhY2UoIlwibGVuZ3RoXCI6MCIsICJcImxlbmd0aFwiOjAuMDAiLCAkcG9zdCk7DQogICAgICAgIHJldHVybiAkdGhpcy0+cmVxdWVzdCgibWVkaWEvY29uZmlndXJlLz92aWRlbz0xIiwgU2lnbmF0dXJlVXRpbHM6OmdlbmVyYXRlU2lnbmF0dXJlKCRwb3N0KSlbMV07DQogICAgfQ0KDQogICAgcHJvdGVjdGVkIGZ1bmN0aW9uIGNvbmZpZ3VyZSgkdXBsb2FkX2lkLCAkcGhvdG8sICRjYXB0aW9uID0gIiIpDQogICAgew0KICAgICAgICBsaXN0KCRzaXplKSA9IGdldGltYWdlc2l6ZSgkcGhvdG8pOw0KICAgICAgICAkcG9zdCA9IGpzb25fZW5jb2RlKGFycmF5KCAidXBsb2FkX2lkIiA9PiAkdXBsb2FkX2lkLCAiY2FtZXJhX21vZGVsIiA9PiAiSE0xUyIsICJzb3VyY2VfdHlwZSIgPT4gMywgImRhdGVfdGltZV9vcmlnaW5hbCIgPT4gZGF0ZSgiWTptOmQgSDppOnMiKSwgImNhbWVyYV9tYWtlIiA9PiAiWElBT01JIiwgImVkaXRzIiA9PiBhcnJheSggImNyb3Bfb3JpZ2luYWxfc2l6ZSIgPT4gYXJyYXkoICRzaXplLCAkc2l6ZSApLCAiY3JvcF96b29tIiA9PiAxLjMzMzMzMzQsICJjcm9wX2NlbnRlciIgPT4gYXJyYXkoIDAsIDAgKSApLCAiZXh0cmEiID0+IGFycmF5KCAic291cmNlX3dpZHRoIiA9PiAkc2l6ZSwgInNvdXJjZV9oZWlnaHQiID0+ICRzaXplICksICJkZXZpY2UiID0+IGFycmF5KCAibWFudWZhY3R1cmVyIiA9PiAiWGlhb21pIiwgIm1vZGVsIiA9PiAiSE0gMVNXIiwgImFuZHJvaWRfdmVyc2lvbiIgPT4gMTgsICJhbmRyb2lkX3JlbGVhc2UiID0+ICI0LjMiICksICJfY3NyZnRva2VuIiA9PiAkdGhpcy0+dG9rZW4sICJfdXVpZCIgPT4gJHRoaXMtPnV1aWQsICJfdWlkIiA9PiAkdGhpcy0+dXNlcm5hbWVfaWQsICJjYXB0aW9uIiA9PiAkY2FwdGlvbiApKTsNCiAgICAgICAgJHBvc3QgPSBzdHJfcmVwbGFjZSgiXCJjcm9wX2NlbnRlclwiOlswLDBdIiwgIlwiY3JvcF9jZW50ZXJcIjpbMC4wLC0wLjBdIiwgJHBvc3QpOw0KICAgICAgICByZXR1cm4gJHRoaXMtPnJlcXVlc3QoIm1lZGlhL2NvbmZpZ3VyZS8iLCBTaWduYXR1cmVVdGlsczo6Z2VuZXJhdGVTaWduYXR1cmUoJHBvc3QpKVsxXTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogRWRpdCBtZWRpYS4NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkbWVkaWFJZA0KICAgICAgICAgKiAgIE1lZGlhIGlkDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJGNhcHRpb25UZXh0DQogICAgICAgICAqICAgQ2FwdGlvbiB0ZXh0DQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBlZGl0IG1lZGlhIGRhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gZWRpdE1lZGlhKCRtZWRpYUlkLCAkY2FwdGlvblRleHQgPSAiIikNCiAgICB7DQogICAgICAgICRkYXRhID0ganNvbl9lbmNvZGUoYXJyYXkoICJfdXVpZCIgPT4gJHRoaXMtPnV1aWQsICJfdWlkIiA9PiAkdGhpcy0+dXNlcm5hbWVfaWQsICJfY3NyZnRva2VuIiA9PiAkdGhpcy0+dG9rZW4sICJjYXB0aW9uX3RleHQiID0+ICRjYXB0aW9uVGV4dCApKTsNCiAgICAgICAgcmV0dXJuICR0aGlzLT5yZXF1ZXN0KCJtZWRpYS8iIC4gJG1lZGlhSWQgLiAiL2VkaXRfbWVkaWEvIiwgU2lnbmF0dXJlVXRpbHM6OmdlbmVyYXRlU2lnbmF0dXJlKCRkYXRhKSlbMV07DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIFJlbW92ZSB5b3Vyc2VsZiBmcm9tIGEgdGFnZ2VkIG1lZGlhLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICRtZWRpYUlkDQogICAgICAgICAqICAgTWVkaWEgaWQNCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIGVkaXQgbWVkaWEgZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiByZW1vdmVTZWxmdGFnKCRtZWRpYUlkKQ0KICAgIHsNCiAgICAgICAgJGRhdGEgPSBqc29uX2VuY29kZShhcnJheSggIl91dWlkIiA9PiAkdGhpcy0+dXVpZCwgIl91aWQiID0+ICR0aGlzLT51c2VybmFtZV9pZCwgIl9jc3JmdG9rZW4iID0+ICR0aGlzLT50b2tlbiApKTsNCiAgICAgICAgcmV0dXJuICR0aGlzLT5yZXF1ZXN0KCJ1c2VydGFncy8iIC4gJG1lZGlhSWQgLiAiL3JlbW92ZS8iLCBTaWduYXR1cmVVdGlsczo6Z2VuZXJhdGVTaWduYXR1cmUoJGRhdGEpKVsxXTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogTWVkaWEgaW5mbw0KICAgICAgICAgKg0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICRtZWRpYUlkDQogICAgICAgICAqICAgTWVkaWEgaWQNCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIGRlbGV0ZSByZXF1ZXN0IGRhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gbWVkaWFJbmZvKCRtZWRpYUlkKQ0KICAgIHsNCiAgICAgICAgJGRhdGEgPSBqc29uX2VuY29kZShhcnJheSggIl91dWlkIiA9PiAkdGhpcy0+dXVpZCwgIl91aWQiID0+ICR0aGlzLT51c2VybmFtZV9pZCwgIl9jc3JmdG9rZW4iID0+ICR0aGlzLT50b2tlbiwgIm1lZGlhX2lkIiA9PiAkbWVkaWFJZCApKTsNCiAgICAgICAgcmV0dXJuICR0aGlzLT5yZXF1ZXN0KCJtZWRpYS8iIC4gJG1lZGlhSWQgLiAiL2luZm8vIiwgU2lnbmF0dXJlVXRpbHM6OmdlbmVyYXRlU2lnbmF0dXJlKCRkYXRhKSlbMV07DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIERlbGV0ZSBwaG90byBvciB2aWRlby4NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkbWVkaWFJZA0KICAgICAgICAgKiAgIE1lZGlhIGlkDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBkZWxldGUgcmVxdWVzdCBkYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGRlbGV0ZU1lZGlhKCRtZWRpYUlkKQ0KICAgIHsNCiAgICAgICAgJGRhdGEgPSBqc29uX2VuY29kZShhcnJheSggIl91dWlkIiA9PiAkdGhpcy0+dXVpZCwgIl91aWQiID0+ICR0aGlzLT51c2VybmFtZV9pZCwgIl9jc3JmdG9rZW4iID0+ICR0aGlzLT50b2tlbiwgIm1lZGlhX2lkIiA9PiAkbWVkaWFJZCApKTsNCiAgICAgICAgcmV0dXJuICR0aGlzLT5yZXF1ZXN0KCJtZWRpYS8iIC4gJG1lZGlhSWQgLiAiL2RlbGV0ZS8iLCBTaWduYXR1cmVVdGlsczo6Z2VuZXJhdGVTaWduYXR1cmUoJGRhdGEpKVsxXTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogQ29tbWVudCBtZWRpYS4NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkbWVkaWFJZA0KICAgICAgICAgKiAgIE1lZGlhIGlkDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJGNvbW1lbnRUZXh0DQogICAgICAgICAqICAgQ29tbWVudCBUZXh0DQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBjb21tZW50IG1lZGlhIGRhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gY29tbWVudCgkbWVkaWFJZCwgJGNvbW1lbnRUZXh0KQ0KICAgIHsNCiAgICAgICAgJGRhdGEgPSBqc29uX2VuY29kZShhcnJheSggIl91dWlkIiA9PiAkdGhpcy0+dXVpZCwgIl91aWQiID0+ICR0aGlzLT51c2VybmFtZV9pZCwgIl9jc3JmdG9rZW4iID0+ICR0aGlzLT50b2tlbiwgImNvbW1lbnRfdGV4dCIgPT4gJGNvbW1lbnRUZXh0ICkpOw0KICAgICAgICByZXR1cm4gJHRoaXMtPnJlcXVlc3QoIm1lZGlhLyIgLiAkbWVkaWFJZCAuICIvY29tbWVudC8iLCBTaWduYXR1cmVVdGlsczo6Z2VuZXJhdGVTaWduYXR1cmUoJGRhdGEpKVsxXTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogRGVsZXRlIENvbW1lbnQuDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJG1lZGlhSWQNCiAgICAgICAgICogICBNZWRpYSBJRA0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICRjb21tZW50SWQNCiAgICAgICAgICogICBDb21tZW50IElEDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBEZWxldGUgY29tbWVudCBkYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGRlbGV0ZUNvbW1lbnQoJG1lZGlhSWQsICRjYXB0aW9uVGV4dCwgJGNvbW1lbnRJZCkNCiAgICB7DQogICAgICAgICRkYXRhID0ganNvbl9lbmNvZGUoYXJyYXkoICJfdXVpZCIgPT4gJHRoaXMtPnV1aWQsICJfdWlkIiA9PiAkdGhpcy0+dXNlcm5hbWVfaWQsICJfY3NyZnRva2VuIiA9PiAkdGhpcy0+dG9rZW4sICJjYXB0aW9uX3RleHQiID0+ICRjYXB0aW9uVGV4dCApKTsNCiAgICAgICAgcmV0dXJuICR0aGlzLT5yZXF1ZXN0KCJtZWRpYS8iIC4gJG1lZGlhSWQgLiAiL2NvbW1lbnQvIiAuICRjb21tZW50SWQgLiAiL2RlbGV0ZS8iLCBTaWduYXR1cmVVdGlsczo6Z2VuZXJhdGVTaWduYXR1cmUoJGRhdGEpKVsxXTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogU2V0cyBhY2NvdW50IHRvIHB1YmxpYy4NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkcGhvdG8NCiAgICAgICAgICogICBQYXRoIHRvIHBob3RvDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGNoYW5nZVByb2ZpbGVQaWN0dXJlKCRwaG90bykNCiAgICB7DQogICAgICAgIGlmKCBpc19udWxsKCRwaG90bykgKSANCiAgICAgICAgew0KICAgICAgICAgICAgZWNobyAiUGhvdG8gbm90IHZhbGlkXG5cbiI7DQogICAgICAgIH0NCiAgICAgICAgZWxzZQ0KICAgICAgICB7DQogICAgICAgICAgICAkdURhdGEgPSBqc29uX2VuY29kZShhcnJheSggIl9jc3JmdG9rZW4iID0+ICR0aGlzLT50b2tlbiwgIl91dWlkIiA9PiAkdGhpcy0+dXVpZCwgIl91aWQiID0+ICR0aGlzLT51c2VybmFtZV9pZCApKTsNCiAgICAgICAgICAgICRlbmRwb2ludCA9IENvbnN0YW50czo6QVBJX1VSTCAuICJhY2NvdW50cy9jaGFuZ2VfcHJvZmlsZV9waWN0dXJlLyI7DQogICAgICAgICAgICAkYm91bmRhcnkgPSAkdGhpcy0+dXVpZDsNCiAgICAgICAgICAgICRib2RpZXMgPSBhcnJheSggYXJyYXkoICJ0eXBlIiA9PiAiZm9ybS1kYXRhIiwgIm5hbWUiID0+ICJpZ19zaWdfa2V5X3ZlcnNpb24iLCAiZGF0YSIgPT4gQ29uc3RhbnRzOjpTSUdfS0VZX1ZFUlNJT04gKSwgYXJyYXkoICJ0eXBlIiA9PiAiZm9ybS1kYXRhIiwgIm5hbWUiID0+ICJzaWduZWRfYm9keSIsICJkYXRhIiA9PiBoYXNoX2htYWMoInNoYTI1NiIsICR1RGF0YSwgQ29uc3RhbnRzOjpJR19TSUdfS0VZKSAuICR1RGF0YSApLCBhcnJheSggInR5cGUiID0+ICJmb3JtLWRhdGEiLCAibmFtZSIgPT4gInByb2ZpbGVfcGljIiwgImRhdGEiID0+IGZpbGVfZ2V0X2NvbnRlbnRzKCRwaG90byksICJmaWxlbmFtZSIgPT4gInByb2ZpbGVfcGljIiwgImhlYWRlcnMiID0+IGFycmF5KCAiQ29udGVudC10eXBlOiBhcHBsaWNhdGlvbi9vY3RldC1zdHJlYW0iLCAiQ29udGVudC1UcmFuc2Zlci1FbmNvZGluZzogYmluYXJ5IiApICkgKTsNCiAgICAgICAgICAgICRkYXRhID0gJHRoaXMtPmJ1aWxkQm9keSgkYm9kaWVzLCAkYm91bmRhcnkpOw0KICAgICAgICAgICAgJGhlYWRlcnMgPSBhcnJheSggIlByb3h5LUNvbm5lY3Rpb246IGtlZXAtYWxpdmUiLCAiQ29ubmVjdGlvbjoga2VlcC1hbGl2ZSIsICJBY2NlcHQ6ICovKiIsICJDb250ZW50LXR5cGU6IG11bHRpcGFydC9mb3JtLWRhdGE7IGJvdW5kYXJ5PSIgLiAkYm91bmRhcnksICJBY2NlcHQtTGFuZ3VhZ2U6IHRyLVRSIiwgIkFjY2VwdC1FbmNvZGluZzogZ3ppcCwgZGVmbGF0ZSIgKTsNCiAgICAgICAgICAgICRjaCA9IGN1cmxfaW5pdCgpOw0KICAgICAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1VSTCwgJGVuZHBvaW50KTsNCiAgICAgICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9VU0VSQUdFTlQsICR0aGlzLT5zZXR0aW5ncy0+Z2V0KCJ1c2VyX2FnZW50IikpOw0KICAgICAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1JFVFVSTlRSQU5TRkVSLCB0cnVlKTsNCiAgICAgICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9GT0xMT1dMT0NBVElPTiwgdHJ1ZSk7DQogICAgICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfSEVBREVSLCB0cnVlKTsNCiAgICAgICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9WRVJCT1NFLCAkdGhpcy0+ZGVidWcpOw0KICAgICAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1NTTF9WRVJJRllQRUVSLCBmYWxzZSk7DQogICAgICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfU1NMX1ZFUklGWUhPU1QsIGZhbHNlKTsNCiAgICAgICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9IVFRQSEVBREVSLCAkaGVhZGVycyk7DQogICAgICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfQ09PS0lFRklMRSwgJHRoaXMtPklHRGF0YVBhdGggLiAoc3RyaW5nKSAkdGhpcy0+dXNlcm5hbWUgLiAiLmRhdCIpOw0KICAgICAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX0NPT0tJRUpBUiwgJHRoaXMtPklHRGF0YVBhdGggLiAoc3RyaW5nKSAkdGhpcy0+dXNlcm5hbWUgLiAiLmRhdCIpOw0KICAgICAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1BPU1QsIHRydWUpOw0KICAgICAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1BPU1RGSUVMRFMsICRkYXRhKTsNCiAgICAgICAgICAgICRyZXNwID0gY3VybF9leGVjKCRjaCk7DQogICAgICAgICAgICAkaGVhZGVyX2xlbiA9IGN1cmxfZ2V0aW5mbygkY2gsIENVUkxJTkZPX0hFQURFUl9TSVpFKTsNCiAgICAgICAgICAgICRoZWFkZXIgPSBzdWJzdHIoJHJlc3AsIDAsICRoZWFkZXJfbGVuKTsNCiAgICAgICAgICAgICR1cGxvYWQgPSBqc29uX2RlY29kZShzdWJzdHIoJHJlc3AsICRoZWFkZXJfbGVuKSwgdHJ1ZSk7DQogICAgICAgICAgICBjdXJsX2Nsb3NlKCRjaCk7DQogICAgICAgIH0NCg0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBSZW1vdmUgcHJvZmlsZSBwaWN0dXJlLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgc3RhdHVzIHJlcXVlc3QgZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiByZW1vdmVQcm9maWxlUGljdHVyZSgpDQogICAgew0KICAgICAgICAkZGF0YSA9IGpzb25fZW5jb2RlKGFycmF5KCAiX3V1aWQiID0+ICR0aGlzLT51dWlkLCAiX3VpZCIgPT4gJHRoaXMtPnVzZXJuYW1lX2lkLCAiX2NzcmZ0b2tlbiIgPT4gJHRoaXMtPnRva2VuICkpOw0KICAgICAgICByZXR1cm4gJHRoaXMtPnJlcXVlc3QoImFjY291bnRzL3JlbW92ZV9wcm9maWxlX3BpY3R1cmUvIiwgU2lnbmF0dXJlVXRpbHM6OmdlbmVyYXRlU2lnbmF0dXJlKCRkYXRhKSlbMV07DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIFNldHMgYWNjb3VudCB0byBwcml2YXRlLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgc3RhdHVzIHJlcXVlc3QgZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBzZXRQcml2YXRlQWNjb3VudCgpDQogICAgew0KICAgICAgICAkZGF0YSA9IGpzb25fZW5jb2RlKGFycmF5KCAiX3V1aWQiID0+ICR0aGlzLT51dWlkLCAiX3VpZCIgPT4gJHRoaXMtPnVzZXJuYW1lX2lkLCAiX2NzcmZ0b2tlbiIgPT4gJHRoaXMtPnRva2VuICkpOw0KICAgICAgICByZXR1cm4gJHRoaXMtPnJlcXVlc3QoImFjY291bnRzL3NldF9wcml2YXRlLyIsIFNpZ25hdHVyZVV0aWxzOjpnZW5lcmF0ZVNpZ25hdHVyZSgkZGF0YSkpWzFdOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBTZXRzIGFjY291bnQgdG8gcHVibGljLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgc3RhdHVzIHJlcXVlc3QgZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBzZXRQdWJsaWNBY2NvdW50KCkNCiAgICB7DQogICAgICAgICRkYXRhID0ganNvbl9lbmNvZGUoYXJyYXkoICJfdXVpZCIgPT4gJHRoaXMtPnV1aWQsICJfdWlkIiA9PiAkdGhpcy0+dXNlcm5hbWVfaWQsICJfY3NyZnRva2VuIiA9PiAkdGhpcy0+dG9rZW4gKSk7DQogICAgICAgIHJldHVybiAkdGhpcy0+cmVxdWVzdCgiYWNjb3VudHMvc2V0X3B1YmxpYy8iLCBTaWduYXR1cmVVdGlsczo6Z2VuZXJhdGVTaWduYXR1cmUoJGRhdGEpKVsxXTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogR2V0IHBlcnNvbmFsIHByb2ZpbGUgZGF0YS4NCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIHByb2ZpbGUgZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBnZXRQcm9maWxlRGF0YSgpDQogICAgew0KICAgICAgICAkZGF0YSA9IGpzb25fZW5jb2RlKGFycmF5KCAiX3V1aWQiID0+ICR0aGlzLT51dWlkLCAiX3VpZCIgPT4gJHRoaXMtPnVzZXJuYW1lX2lkLCAiX2NzcmZ0b2tlbiIgPT4gJHRoaXMtPnRva2VuICkpOw0KICAgICAgICByZXR1cm4gJHRoaXMtPnJlcXVlc3QoImFjY291bnRzL2N1cnJlbnRfdXNlci8/ZWRpdD10cnVlIiwgU2lnbmF0dXJlVXRpbHM6OmdlbmVyYXRlU2lnbmF0dXJlKCRkYXRhKSlbMV07DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIEVkaXQgcHJvZmlsZS4NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkdXJsDQogICAgICAgICAqICAgVXJsIC0gd2Vic2l0ZS4gIiIgZm9yIG5vdGhpbmcNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkcGhvbmUNCiAgICAgICAgICogICBQaG9uZSBudW1iZXIuICIiIGZvciBub3RoaW5nDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJGZpcnN0X25hbWUNCiAgICAgICAgICogICBOYW1lLiAiIiBmb3Igbm90aGluZw0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICRlbWFpbA0KICAgICAgICAgKiAgIEVtYWlsLiBSZXF1aXJlZC4NCiAgICAgICAgICogQHBhcmFtIGludCAgICAkZ2VuZGVyDQogICAgICAgICAqICAgR2VuZGVyLiBtYWxlID0gMSAsIGZlbWFsZSA9IDANCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIGVkaXQgcHJvZmlsZSBkYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGVkaXRQcm9maWxlKCR1cmwsICRwaG9uZSwgJGZpcnN0X25hbWUsICRiaW9ncmFwaHksICRlbWFpbCwgJGdlbmRlcikNCiAgICB7DQogICAgICAgICRkYXRhID0ganNvbl9lbmNvZGUoYXJyYXkoICJfdXVpZCIgPT4gJHRoaXMtPnV1aWQsICJfdWlkIiA9PiAkdGhpcy0+dXNlcm5hbWVfaWQsICJfY3NyZnRva2VuIiA9PiAkdGhpcy0+dG9rZW4sICJleHRlcm5hbF91cmwiID0+ICR1cmwsICJwaG9uZV9udW1iZXIiID0+ICRwaG9uZSwgInVzZXJuYW1lIiA9PiAkdGhpcy0+dXNlcm5hbWUsICJmaXJzdF9uYW1lIiA9PiAkZmlyc3RfbmFtZSwgImJpb2dyYXBoeSIgPT4gJGJpb2dyYXBoeSwgImVtYWlsIiA9PiAkZW1haWwsICJnZW5kZXIiID0+ICRnZW5kZXIgKSk7DQogICAgICAgIHJldHVybiAkdGhpcy0+cmVxdWVzdCgiYWNjb3VudHMvZWRpdF9wcm9maWxlLyIsIFNpZ25hdHVyZVV0aWxzOjpnZW5lcmF0ZVNpZ25hdHVyZSgkZGF0YSkpWzFdOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBHZXQgdXNlcm5hbWUgaW5mby4NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkdXNlcm5hbWVJZA0KICAgICAgICAgKiAgIFVzZXJuYW1lIGlkDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBVc2VybmFtZSBkYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGdldFVzZXJuYW1lSW5mbygkdXNlcm5hbWVJZCkNCiAgICB7DQogICAgICAgIHJldHVybiAkdGhpcy0+cmVxdWVzdCgidXNlcnMvIiAuICR1c2VybmFtZUlkIC4gIi9pbmZvLyIpWzFdOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBHZXQgc2VsZiB1c2VybmFtZSBpbmZvLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgVXNlcm5hbWUgZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBnZXRTZWxmVXNlcm5hbWVJbmZvKCkNCiAgICB7DQogICAgICAgIHJldHVybiAkdGhpcy0+Z2V0VXNlcm5hbWVJbmZvKCR0aGlzLT51c2VybmFtZV9pZCk7DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIEdldCByZWNlbnQgYWN0aXZpdHkuDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBSZWNlbnQgYWN0aXZpdHkgZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBnZXRSZWNlbnRBY3Rpdml0eSgkbWF4aWQgPSBOVUxMKQ0KICAgIHsNCiAgICAgICAgbGlzdCgsICRhY3Rpdml0eSkgPSAkdGhpcy0+cmVxdWVzdCgibmV3cy9pbmJveC8/IiAuICgoIWlzX251bGwoJG1heGlkKSA/ICJtYXhfaWQ9IiAuICRtYXhpZCA6ICIiKSkpOw0KICAgICAgICBpZiggJGFjdGl2aXR5WyJzdGF0dXMiXSAhPSAib2siICkgDQogICAgICAgIHsNCiAgICAgICAgICAgIHRocm93IG5ldyBFeGNlcHRpb24oJGFjdGl2aXR5WyJtZXNzYWdlIl0gLiAiXG4iKTsNCiAgICAgICAgfQ0KDQogICAgICAgIHJldHVybiAkYWN0aXZpdHk7DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIEdldCByZWNlbnQgYWN0aXZpdHkgZnJvbSBhY2NvdW50cyBmb2xsb3dlZC4NCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIFJlY2VudCBhY3Rpdml0eSBkYXRhIG9mIGZvbGxvd3MNCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gZ2V0Rm9sbG93aW5nUmVjZW50QWN0aXZpdHkoJG1heGlkID0gTlVMTCkNCiAgICB7DQogICAgICAgIGxpc3QoLCAkYWN0aXZpdHkpID0gJHRoaXMtPnJlcXVlc3QoIm5ld3MvPyIgLiAoKCFpc19udWxsKCRtYXhpZCkgPyAibWF4X2lkPSIgLiAkbWF4aWQgOiAiIikpKTsNCiAgICAgICAgaWYoICRhY3Rpdml0eVsic3RhdHVzIl0gIT0gIm9rIiApIA0KICAgICAgICB7DQogICAgICAgICAgICB0aHJvdyBuZXcgRXhjZXB0aW9uKCRhY3Rpdml0eVsibWVzc2FnZSJdIC4gIlxuIik7DQogICAgICAgIH0NCg0KICAgICAgICByZXR1cm4gJGFjdGl2aXR5Ow0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBJIGRvbnQga25vdyB0aGlzIHlldC4NCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIHYyIGluYm94IGRhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gZ2V0djJJbmJveCgpDQogICAgew0KICAgICAgICBsaXN0KCwgJGluYm94KSA9ICR0aGlzLT5yZXF1ZXN0KCJkaXJlY3RfdjIvaW5ib3gvPyIpOw0KICAgICAgICBpZiggJGluYm94WyJzdGF0dXMiXSAhPSAib2siICkgDQogICAgICAgIHsNCiAgICAgICAgICAgIHRocm93IG5ldyBFeGNlcHRpb24oJGluYm94WyJtZXNzYWdlIl0gLiAiXG4iKTsNCiAgICAgICAgfQ0KDQogICAgICAgIHJldHVybiAkaW5ib3g7DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIERpcmVjdCBUaHJlYWQgRGF0YS4NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtICBpbnQgJHRocmVhZElkIFRocmVhZCBJZA0KICAgICAgICAgKg0KICAgICAgICAgKiBAdGhyb3dzIEV4Y2VwdGlvbiBEaXJlY3QgVGhyZWFkIERhdGENCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheSBEaXJlY3QgVGhyZWFkIERhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gZGlyZWN0VGhyZWFkKCR0aHJlYWRJZCkNCiAgICB7DQogICAgICAgIGxpc3QoLCAkZGlyZWN0VGhyZWFkKSA9ICR0aGlzLT5yZXF1ZXN0KCJkaXJlY3RfdjIvdGhyZWFkcy8iIC4gJHRocmVhZElkIC4gIi8/Iik7DQogICAgICAgIGlmKCAkZGlyZWN0VGhyZWFkWyJzdGF0dXMiXSAhPSAib2siICkgDQogICAgICAgIHsNCiAgICAgICAgICAgIHRocm93IG5ldyBFeGNlcHRpb24oJGRpcmVjdFRocmVhZFsibWVzc2FnZSJdIC4gIlxuIik7DQogICAgICAgIH0NCg0KICAgICAgICByZXR1cm4gJGRpcmVjdFRocmVhZDsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogR2V0IHVzZXIgdGFncy4NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkdXNlcm5hbWVJZA0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgdXNlciB0YWdzIGRhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gZ2V0VXNlclRhZ3MoJHVzZXJuYW1lSWQsICRtYXhpZCA9IE5VTEwpDQogICAgew0KICAgICAgICBsaXN0KCwgJHRhZ3MpID0gJHRoaXMtPnJlcXVlc3QoInVzZXJ0YWdzLyIgLiAkdXNlcm5hbWVJZCAuICIvZmVlZC8/cmFua190b2tlbj0iIC4gJHRoaXMtPnJhbmtfdG9rZW4gLiAiJnJhbmtlZF9jb250ZW50PXRydWUiIC4gKCghaXNfbnVsbCgkbWF4aWQpID8gIm1heF9pZD0iIC4gJG1heGlkIDogIiIpKSk7DQogICAgICAgIGlmKCAkdGFnc1sic3RhdHVzIl0gIT0gIm9rIiApIA0KICAgICAgICB7DQogICAgICAgICAgICB0aHJvdyBuZXcgRXhjZXB0aW9uKCR0YWdzWyJtZXNzYWdlIl0gLiAiXG4iKTsNCiAgICAgICAgfQ0KDQogICAgICAgIHJldHVybiAkdGFnczsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogR2V0IHNlbGYgdXNlciB0YWdzLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgc2VsZiB1c2VyIHRhZ3MgZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBnZXRTZWxmVXNlclRhZ3MoJG1heGlkID0gTlVMTCkNCiAgICB7DQogICAgICAgIHJldHVybiAkdGhpcy0+Z2V0VXNlclRhZ3MoJHRoaXMtPnVzZXJuYW1lX2lkLCAkbWF4aWQpOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBHZXQgdGFnZ2VkIG1lZGlhLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICR0YWcNCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiB0YWdGZWVkKCR0YWcsICRtYXhpZCA9IE5VTEwpDQogICAgew0KICAgICAgICBsaXN0KCwgJHVzZXJGZWVkKSA9ICR0aGlzLT5yZXF1ZXN0KCJmZWVkL3RhZy8iIC4gJHRhZyAuICIvP21heF9pZD0iIC4gJG1heGlkIC4gIiZyYW5rX3Rva2VuPSIgLiAkdGhpcy0+cmFua190b2tlbiAuICImcmFua2VkX2NvbnRlbnQ9dHJ1ZSYiKTsNCiAgICAgICAgaWYoICR1c2VyRmVlZFsic3RhdHVzIl0gIT0gIm9rIiApIA0KICAgICAgICB7DQogICAgICAgICAgICB0aHJvdyBuZXcgRXhjZXB0aW9uKCR1c2VyRmVlZFsibWVzc2FnZSJdIC4gIlxuIik7DQogICAgICAgIH0NCg0KICAgICAgICByZXR1cm4gJHVzZXJGZWVkOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBHZXQgbWVkaWEgbGlrZXJzLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICRtZWRpYUlkDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gZ2V0TWVkaWFMaWtlcnMoJG1lZGlhSWQpDQogICAgew0KICAgICAgICBsaXN0KCwgJGxpa2VycykgPSAkdGhpcy0+cmVxdWVzdCgibWVkaWEvIiAuICRtZWRpYUlkIC4gIi9saWtlcnMvPyIpOw0KICAgICAgICBpZiggJGxpa2Vyc1sic3RhdHVzIl0gIT0gIm9rIiApIA0KICAgICAgICB7DQogICAgICAgICAgICB0aHJvdyBuZXcgRXhjZXB0aW9uKCRsaWtlcnNbIm1lc3NhZ2UiXSAuICJcbiIpOw0KICAgICAgICB9DQoNCiAgICAgICAgcmV0dXJuICRsaWtlcnM7DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIEdldCB1c2VyIGxvY2F0aW9ucyBtZWRpYS4NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkdXNlcm5hbWVJZA0KICAgICAgICAgKiAgIFVzZXJuYW1lIGlkDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBHZW8gTWVkaWEgZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBnZXRHZW9NZWRpYSgkdXNlcm5hbWVJZCkNCiAgICB7DQogICAgICAgIGxpc3QoLCAkbG9jYXRpb25zKSA9ICR0aGlzLT5yZXF1ZXN0KCJtYXBzL3VzZXIvIiAuICR1c2VybmFtZUlkIC4gIi8iKTsNCiAgICAgICAgaWYoICRsb2NhdGlvbnNbInN0YXR1cyJdICE9ICJvayIgKSANCiAgICAgICAgew0KICAgICAgICAgICAgdGhyb3cgbmV3IEV4Y2VwdGlvbigkbG9jYXRpb25zWyJtZXNzYWdlIl0gLiAiXG4iKTsNCiAgICAgICAgfQ0KDQogICAgICAgIHJldHVybiAkbG9jYXRpb25zOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBHZXQgc2VsZiB1c2VyIGxvY2F0aW9ucyBtZWRpYS4NCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIEdlbyBNZWRpYSBkYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGdldFNlbGZHZW9NZWRpYSgpDQogICAgew0KICAgICAgICByZXR1cm4gJHRoaXMtPmdldEdlb01lZGlhKCR0aGlzLT51c2VybmFtZV9pZCk7DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIGZhY2Vib29rIHVzZXIgc2VhcmNoLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICRxdWVyeQ0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgcXVlcnkgZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBmYlVzZXJTZWFyY2goJHF1ZXJ5KQ0KICAgIHsNCiAgICAgICAgJHF1ZXJ5ID0gcmF3dXJsZW5jb2RlKCRxdWVyeSk7DQogICAgICAgIGxpc3QoLCAkcXVlcnkpID0gJHRoaXMtPnJlcXVlc3QoImZic2VhcmNoL3RvcHNlYXJjaC8/Y29udGV4dD1ibGVuZGVkJnF1ZXJ5PSIgLiAkcXVlcnkgLiAiJnJhbmtfdG9rZW49IiAuICR0aGlzLT5yYW5rX3Rva2VuKTsNCiAgICAgICAgaWYoICRxdWVyeVsic3RhdHVzIl0gIT0gIm9rIiApIA0KICAgICAgICB7DQogICAgICAgICAgICB0aHJvdyBuZXcgRXhjZXB0aW9uKCRxdWVyeVsibWVzc2FnZSJdIC4gIlxuIik7DQogICAgICAgIH0NCg0KICAgICAgICByZXR1cm4gJHF1ZXJ5Ow0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBTZWFyY2ggdXNlcnMuDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJHF1ZXJ5DQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBxdWVyeSBkYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIHNlYXJjaFVzZXJzKCRxdWVyeSkNCiAgICB7DQogICAgICAgIGxpc3QoLCAkcXVlcnkpID0gJHRoaXMtPnJlcXVlc3QoInVzZXJzL3NlYXJjaC8/aWdfc2lnX2tleV92ZXJzaW9uPSIgLiBDb25zdGFudHM6OlNJR19LRVlfVkVSU0lPTiAuICImaXNfdHlwZWFoZWFkPXRydWUmcXVlcnk9IiAuICRxdWVyeSAuICImcmFua190b2tlbj0iIC4gJHRoaXMtPnJhbmtfdG9rZW4pOw0KICAgICAgICBpZiggJHF1ZXJ5WyJzdGF0dXMiXSAhPSAib2siICkgDQogICAgICAgIHsNCiAgICAgICAgICAgIHRocm93IG5ldyBFeGNlcHRpb24oJHF1ZXJ5WyJtZXNzYWdlIl0gLiAiXG4iKTsNCiAgICAgICAgfQ0KDQogICAgICAgIHJldHVybiAkcXVlcnk7DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIFNlYXJjaCBleGFjdCB1c2VybmFtZQ0KICAgICAgICAgKg0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nIHVzZXJuYW1lTmFtZSB1c2VybmFtZSBhcyBTVFJJTkcgbm90IGFuIGlkDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBxdWVyeSBkYXRhDQogICAgICAgICAqDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIHNlYXJjaFVzZXJuYW1lKCR1c2VybmFtZU5hbWUpDQogICAgew0KICAgICAgICBsaXN0KCwgJHF1ZXJ5KSA9ICR0aGlzLT5yZXF1ZXN0KCJ1c2Vycy8iIC4gJHVzZXJuYW1lTmFtZSAuICIvdXNlcm5hbWVpbmZvLyIpOw0KICAgICAgICByZXR1cm4gJHF1ZXJ5Ow0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBTZWFyY2ggdXNlcnMgdXNpbmcgYWRkcmVzIGJvb2suDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBhcnJheSAkY29udGFjdHMNCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIHF1ZXJ5IGRhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gc3luY0Zyb21BZHJlc3NCb29rKCRjb250YWN0cykNCiAgICB7DQogICAgICAgICRkYXRhID0gImNvbnRhY3RzPSIgLiBqc29uX2VuY29kZSgkY29udGFjdHMsIHRydWUpOw0KICAgICAgICByZXR1cm4gJHRoaXMtPnJlcXVlc3QoImFkZHJlc3NfYm9vay9saW5rLz9pbmNsdWRlPWV4dHJhX2Rpc3BsYXlfbmFtZSx0aHVtYm5haWxzIiwgJGRhdGEpWzFdOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBTZWFyY2ggdGFncy4NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkcXVlcnkNCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIHF1ZXJ5IGRhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gc2VhcmNoVGFncygkcXVlcnkpDQogICAgew0KICAgICAgICBsaXN0KCwgJHF1ZXJ5KSA9ICR0aGlzLT5yZXF1ZXN0KCJ0YWdzL3NlYXJjaC8/aXNfdHlwZWFoZWFkPXRydWUmcT0iIC4gJHF1ZXJ5IC4gIiZyYW5rX3Rva2VuPSIgLiAkdGhpcy0+cmFua190b2tlbik7DQogICAgICAgIGlmKCAkcXVlcnlbInN0YXR1cyJdICE9ICJvayIgKSANCiAgICAgICAgew0KICAgICAgICAgICAgdGhyb3cgbmV3IEV4Y2VwdGlvbigkcXVlcnlbIm1lc3NhZ2UiXSAuICJcbiIpOw0KICAgICAgICB9DQoNCiAgICAgICAgcmV0dXJuICRxdWVyeTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogR2V0IHRpbWVsaW5lIGRhdGEuDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICB0aW1lbGluZSBkYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGdldFRpbWVsaW5lKCRtYXhpZCA9IE5VTEwpDQogICAgew0KICAgICAgICBsaXN0KCwgJHRpbWVsaW5lKSA9ICR0aGlzLT5yZXF1ZXN0KCJmZWVkL3RpbWVsaW5lLz9yYW5rX3Rva2VuPSIgLiAkdGhpcy0+cmFua190b2tlbiAuICImcmFua2VkX2NvbnRlbnQ9dHJ1ZSIgLiAoKCFpc19udWxsKCRtYXhpZCkgPyAiJm1heF9pZD0iIC4gJG1heGlkIDogIiIpKSk7DQogICAgICAgIGlmKCAkdGltZWxpbmVbInN0YXR1cyJdICE9ICJvayIgKSANCiAgICAgICAgew0KICAgICAgICAgICAgdGhyb3cgbmV3IEV4Y2VwdGlvbigkdGltZWxpbmVbIm1lc3NhZ2UiXSAuICJcbiIpOw0KICAgICAgICB9DQoNCiAgICAgICAgcmV0dXJuICR0aW1lbGluZTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogQHRocm93cyBFeGNlcHRpb24NCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheXx2b2lkDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGdldFJlZWxzVHJheUZlZWQoKQ0KICAgIHsNCiAgICAgICAgbGlzdCgsICRmZWVkKSA9ICR0aGlzLT5yZXF1ZXN0KCJmZWVkL3JlZWxzX3RyYXkvIik7DQogICAgICAgIGlmKCAkZmVlZFsic3RhdHVzIl0gIT0gIm9rIiApIA0KICAgICAgICB7DQogICAgICAgICAgICB0aHJvdyBuZXcgRXhjZXB0aW9uKCRmZWVkWyJtZXNzYWdlIl0gLiAiXG4iKTsNCiAgICAgICAgfQ0KDQogICAgICAgIHJldHVybiAkZmVlZDsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogR2V0IHVzZXIgZmVlZC4NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkdXNlcm5hbWVJZA0KICAgICAgICAgKiAgICBVc2VybmFtZSBpZA0KICAgICAgICAgKiBAcGFyYW0gbnVsbCAgICRtYXhpZA0KICAgICAgICAgKiAgICBNYXggSWQNCiAgICAgICAgICogQHBhcmFtIG51bGwgICAkbWluVGltZXN0YW1wDQogICAgICAgICAqICAgIE1pbiB0aW1lc3RhbXANCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheSBVc2VyIGZlZWQgZGF0YQ0KICAgICAgICAgKiAgICBVc2VyIGZlZWQgZGF0YQ0KICAgICAgICAgKiBAdGhyb3dzIEV4Y2VwdGlvbg0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBnZXRVc2VyRmVlZCgkdXNlcm5hbWVJZCwgJG1heGlkID0gTlVMTCwgJG1pblRpbWVzdGFtcCA9IE5VTEwpDQogICAgew0KICAgICAgICBsaXN0KCwgJHVzZXJGZWVkKSA9ICR0aGlzLT5yZXF1ZXN0KCJmZWVkL3VzZXIvIiAuICR1c2VybmFtZUlkIC4gIi8/cmFua190b2tlbj0iIC4gJHRoaXMtPnJhbmtfdG9rZW4gLiAoKCFpc19udWxsKCRtYXhpZCkgPyAiJm1heF9pZD0iIC4gJG1heGlkIDogIiIpKSAuICgoIWlzX251bGwoJG1pblRpbWVzdGFtcCkgPyAiJm1pbl90aW1lc3RhbXA9IiAuICRtaW5UaW1lc3RhbXAgOiAiIikpIC4gIiZyYW5rZWRfY29udGVudD10cnVlIik7DQogICAgICAgIHJldHVybiAkdXNlckZlZWQ7DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIEdldCBoYXNodGFnIGZlZWQuDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJGhhc2h0YWdTdHJpbmcNCiAgICAgICAgICogICAgSGFzaHRhZyBzdHJpbmcsIG5vdCBpbmNsdWRpbmcgdGhlICMNCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIEhhc2h0YWcgZmVlZCBkYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGdldEhhc2h0YWdGZWVkKCRoYXNodGFnU3RyaW5nLCAkbWF4aWQgPSBOVUxMKQ0KICAgIHsNCiAgICAgICAgaWYoIGlzX251bGwoJG1heGlkKSApIA0KICAgICAgICB7DQogICAgICAgICAgICAkZW5kcG9pbnQgPSAiZmVlZC90YWcvIiAuICRoYXNodGFnU3RyaW5nIC4gIi8/cmFua190b2tlbj0iIC4gJHRoaXMtPnJhbmtfdG9rZW4gLiAiJnJhbmtlZF9jb250ZW50PXRydWUmIjsNCiAgICAgICAgfQ0KICAgICAgICBlbHNlDQogICAgICAgIHsNCiAgICAgICAgICAgICRlbmRwb2ludCA9ICJmZWVkL3RhZy8iIC4gJGhhc2h0YWdTdHJpbmcgLiAiLz9tYXhfaWQ9IiAuICRtYXhpZCAuICImcmFua190b2tlbj0iIC4gJHRoaXMtPnJhbmtfdG9rZW4gLiAiJnJhbmtlZF9jb250ZW50PXRydWUmIjsNCiAgICAgICAgfQ0KDQogICAgICAgIGxpc3QoLCAkaGFzaHRhZ0ZlZWQpID0gJHRoaXMtPnJlcXVlc3QoJGVuZHBvaW50KTsNCiAgICAgICAgaWYoICRoYXNodGFnRmVlZFsic3RhdHVzIl0gIT0gIm9rIiApIA0KICAgICAgICB7DQogICAgICAgICAgICB0aHJvdyBuZXcgRXhjZXB0aW9uKCRoYXNodGFnRmVlZFsibWVzc2FnZSJdIC4gIlxuIik7DQogICAgICAgIH0NCg0KICAgICAgICByZXR1cm4gJGhhc2h0YWdGZWVkOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBHZXQgbG9jYXRpb25zLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICRxdWVyeQ0KICAgICAgICAgKiAgICBzZWFyY2ggcXVlcnkNCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIExvY2F0aW9uIGxvY2F0aW9uIGRhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gc2VhcmNoTG9jYXRpb24oJHF1ZXJ5KQ0KICAgIHsNCiAgICAgICAgJHF1ZXJ5ID0gcmF3dXJsZW5jb2RlKCRxdWVyeSk7DQogICAgICAgICRlbmRwb2ludCA9ICJmYnNlYXJjaC9wbGFjZXMvP3JhbmtfdG9rZW49IiAuICR0aGlzLT5yYW5rX3Rva2VuIC4gIiZxdWVyeT0iIC4gJHF1ZXJ5Ow0KICAgICAgICBsaXN0KCwgJGxvY2F0aW9uRmVlZCkgPSAkdGhpcy0+cmVxdWVzdCgkZW5kcG9pbnQpOw0KICAgICAgICBpZiggJGxvY2F0aW9uRmVlZFsic3RhdHVzIl0gIT0gIm9rIiApIA0KICAgICAgICB7DQogICAgICAgICAgICB0aHJvdyBuZXcgRXhjZXB0aW9uKCRsb2NhdGlvbkZlZWRbIm1lc3NhZ2UiXSAuICJcbiIpOw0KICAgICAgICB9DQoNCiAgICAgICAgcmV0dXJuICRsb2NhdGlvbkZlZWQ7DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIEdldCBsb2NhdGlvbiBmZWVkLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICRsb2NhdGlvbklkDQogICAgICAgICAqICAgIGxvY2F0aW9uIGlkDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBMb2NhdGlvbiBmZWVkIGRhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gZ2V0TG9jYXRpb25GZWVkKCRsb2NhdGlvbklkLCAkbWF4aWQgPSBOVUxMKQ0KICAgIHsNCiAgICAgICAgaWYoIGlzX251bGwoJG1heGlkKSApIA0KICAgICAgICB7DQogICAgICAgICAgICAkZW5kcG9pbnQgPSAiZmVlZC9sb2NhdGlvbi8iIC4gJGxvY2F0aW9uSWQgLiAiLz9yYW5rX3Rva2VuPSIgLiAkdGhpcy0+cmFua190b2tlbiAuICImcmFua2VkX2NvbnRlbnQ9dHJ1ZSYiOw0KICAgICAgICB9DQogICAgICAgIGVsc2UNCiAgICAgICAgew0KICAgICAgICAgICAgJGVuZHBvaW50ID0gImZlZWQvbG9jYXRpb24vIiAuICRsb2NhdGlvbklkIC4gIi8/bWF4X2lkPSIgLiAkbWF4aWQgLiAiJnJhbmtfdG9rZW49IiAuICR0aGlzLT5yYW5rX3Rva2VuIC4gIiZyYW5rZWRfY29udGVudD10cnVlJiI7DQogICAgICAgIH0NCg0KICAgICAgICBsaXN0KCwgJGxvY2F0aW9uRmVlZCkgPSAkdGhpcy0+cmVxdWVzdCgkZW5kcG9pbnQpOw0KICAgICAgICBpZiggJGxvY2F0aW9uRmVlZFsic3RhdHVzIl0gIT0gIm9rIiApIA0KICAgICAgICB7DQogICAgICAgICAgICB0aHJvdyBuZXcgRXhjZXB0aW9uKCRsb2NhdGlvbkZlZWRbIm1lc3NhZ2UiXSAuICJcbiIpOw0KICAgICAgICB9DQoNCiAgICAgICAgcmV0dXJuICRsb2NhdGlvbkZlZWQ7DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIEdldCBzZWxmIHVzZXIgZmVlZC4NCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIFVzZXIgZmVlZCBkYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGdldFNlbGZVc2VyRmVlZCgkbWF4aWQgPSBOVUxMLCAkbWluVGltZXN0YW1wID0gTlVMTCkNCiAgICB7DQogICAgICAgIHJldHVybiAkdGhpcy0+Z2V0VXNlckZlZWQoJHRoaXMtPnVzZXJuYW1lX2lkLCAkbWF4aWQsICRtaW5UaW1lc3RhbXApOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBSYW5rZWQgcmVjaXBpZW50cy4NCiAgICAgICAgICoNCiAgICAgICAgICogQHRocm93cyBFeGNlcHRpb24gUmFua2VkIHJlY2lwaWVudHMgRGF0YQ0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5fHZvaWQNCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gZ2V0UmFua2VkUmVjaXBpZW50cygpDQogICAgew0KICAgICAgICBsaXN0KCwgJHJhbmtlZF9yZWNpcGllbnRzKSA9ICR0aGlzLT5yZXF1ZXN0KCJkaXJlY3RfdjIvcmFua2VkX3JlY2lwaWVudHMvP3Nob3dfdGhyZWFkcz10cnVlIik7DQogICAgICAgIGlmKCAkcmFua2VkX3JlY2lwaWVudHNbInN0YXR1cyJdICE9ICJvayIgKSANCiAgICAgICAgew0KICAgICAgICAgICAgdGhyb3cgbmV3IEV4Y2VwdGlvbigkcmFua2VkX3JlY2lwaWVudHNbIm1lc3NhZ2UiXSAuICJcbiIpOw0KICAgICAgICB9DQoNCiAgICAgICAgcmV0dXJuICRyYW5rZWRfcmVjaXBpZW50czsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogUmVjZW50IHJlY2lwaWVudHMuDQogICAgICAgICAqDQogICAgICAgICAqIEB0aHJvd3MgRXhjZXB0aW9uIFJhbmtlZCByZWNpcGllbnRzIERhdGENCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheXx2b2lkDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGdldFJlY2VudFJlY2lwaWVudHMoKQ0KICAgIHsNCiAgICAgICAgbGlzdCgsICRyZWNlbnRfcmVjaXBpZW50cykgPSAkdGhpcy0+cmVxdWVzdCgiZGlyZWN0X3NoYXJlL3JlY2VudF9yZWNpcGllbnRzLyIpOw0KICAgICAgICBpZiggJHJlY2VudF9yZWNpcGllbnRzWyJzdGF0dXMiXSAhPSAib2siICkgDQogICAgICAgIHsNCiAgICAgICAgICAgIHRocm93IG5ldyBFeGNlcHRpb24oJHJlY2VudF9yZWNpcGllbnRzWyJtZXNzYWdlIl0gLiAiXG4iKTsNCiAgICAgICAgfQ0KDQogICAgICAgIHJldHVybiAkcmVjZW50X3JlY2lwaWVudHM7DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIEV4cGxvcmUgVGFiLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAdGhyb3dzIEV4Y2VwdGlvbiBFeHBsb3JlIGRhdGENCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheXx2b2lkDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGV4cGxvcmUoKQ0KICAgIHsNCiAgICAgICAgbGlzdCgsICRleHBsb3JlKSA9ICR0aGlzLT5yZXF1ZXN0KCJkaXNjb3Zlci9leHBsb3JlLyIpOw0KICAgICAgICBpZiggJGV4cGxvcmVbInN0YXR1cyJdICE9ICJvayIgKSANCiAgICAgICAgew0KICAgICAgICAgICAgdGhyb3cgbmV3IEV4Y2VwdGlvbigkZXhwbG9yZVsibWVzc2FnZSJdIC4gIlxuIik7DQogICAgICAgIH0NCg0KICAgICAgICByZXR1cm4gJGV4cGxvcmU7DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIEdldCBwb3B1bGFyIGZlZWQuDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBwb3B1bGFyIGZlZWQgZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBnZXRQb3B1bGFyRmVlZCgkbWF4aWQgPSBOVUxMKQ0KICAgIHsNCiAgICAgICAgbGlzdCgsICRwb3B1bGFyRmVlZCkgPSAkdGhpcy0+cmVxdWVzdCgiZmVlZC9wb3B1bGFyLz9tYXhfaWQ9IiAuICRtYXhpZCAuICImcGVvcGxlX3RlYXNlcl9zdXBwb3J0ZWQ9MSZyYW5rX3Rva2VuPSIgLiAkdGhpcy0+cmFua190b2tlbiAuICImcmFua2VkX2NvbnRlbnQ9dHJ1ZSYiKTsNCiAgICAgICAgaWYoICRwb3B1bGFyRmVlZFsic3RhdHVzIl0gIT0gIm9rIiApIA0KICAgICAgICB7DQogICAgICAgICAgICB0aHJvdyBuZXcgRXhjZXB0aW9uKCRwb3B1bGFyRmVlZFsibWVzc2FnZSJdIC4gIlxuIik7DQogICAgICAgIH0NCg0KICAgICAgICByZXR1cm4gJHBvcHVsYXJGZWVkOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBHZXQgdXNlciBmb2xsb3dpbmdzLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICR1c2VybmFtZUlkDQogICAgICAgICAqICAgVXNlcm5hbWUgaWQNCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIGZvbGxvd2VycyBkYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGdldFVzZXJGb2xsb3dpbmdzKCR1c2VybmFtZUlkLCAkbWF4aWQgPSBOVUxMKQ0KICAgIHsNCiAgICAgICAgcmV0dXJuICR0aGlzLT5yZXF1ZXN0KCJmcmllbmRzaGlwcy8iIC4gJHVzZXJuYW1lSWQgLiAiL2ZvbGxvd2luZy8/bWF4X2lkPSIgLiAkbWF4aWQgLiAiJmlnX3NpZ19rZXlfdmVyc2lvbj0iIC4gQ29uc3RhbnRzOjpTSUdfS0VZX1ZFUlNJT04gLiAiJnJhbmtfdG9rZW49IiAuICR0aGlzLT5yYW5rX3Rva2VuKVsxXTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogR2V0IHVzZXIgZm9sbG93ZXJzLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICR1c2VybmFtZUlkDQogICAgICAgICAqICAgVXNlcm5hbWUgaWQNCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIGZvbGxvd2VycyBkYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGdldFVzZXJGb2xsb3dlcnMoJHVzZXJuYW1lSWQsICRtYXhpZCA9IE5VTEwpDQogICAgew0KICAgICAgICByZXR1cm4gJHRoaXMtPnJlcXVlc3QoImZyaWVuZHNoaXBzLyIgLiAkdXNlcm5hbWVJZCAuICIvZm9sbG93ZXJzLz9tYXhfaWQ9IiAuICRtYXhpZCAuICImaWdfc2lnX2tleV92ZXJzaW9uPSIgLiBDb25zdGFudHM6OlNJR19LRVlfVkVSU0lPTiAuICImcmFua190b2tlbj0iIC4gJHRoaXMtPnJhbmtfdG9rZW4pWzFdOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBHZXQgc2VsZiB1c2VyIGZvbGxvd2Vycy4NCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIGZvbGxvd2VycyBkYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGdldFNlbGZVc2VyRm9sbG93ZXJzKCRtYXhpZCA9IE5VTEwpDQogICAgew0KICAgICAgICByZXR1cm4gJHRoaXMtPmdldFVzZXJGb2xsb3dlcnMoJHRoaXMtPnVzZXJuYW1lX2lkLCAkbWF4aWQpOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBHZXQgc2VsZiB1c2VycyB3ZSBhcmUgZm9sbG93aW5nLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgdXNlcnMgd2UgYXJlIGZvbGxvd2luZyBkYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGdldFNlbGZVc2Vyc0ZvbGxvd2luZygkbWF4aWQgPSBOVUxMKQ0KICAgIHsNCiAgICAgICAgcmV0dXJuICR0aGlzLT5yZXF1ZXN0KCJmcmllbmRzaGlwcy9mb2xsb3dpbmcvP21heF9pZD0iIC4gJG1heGlkIC4gIiZpZ19zaWdfa2V5X3ZlcnNpb249IiAuIENvbnN0YW50czo6U0lHX0tFWV9WRVJTSU9OIC4gIiZyYW5rX3Rva2VuPSIgLiAkdGhpcy0+cmFua190b2tlbilbMV07DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIExpa2UgcGhvdG8gb3IgdmlkZW8uDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJG1lZGlhSWQNCiAgICAgICAgICogICBNZWRpYSBpZA0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgc3RhdHVzIHJlcXVlc3QNCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gbGlrZSgkbWVkaWFJZCkNCiAgICB7DQogICAgICAgICRkYXRhID0ganNvbl9lbmNvZGUoYXJyYXkoICJfdXVpZCIgPT4gJHRoaXMtPnV1aWQsICJfdWlkIiA9PiAkdGhpcy0+dXNlcm5hbWVfaWQsICJfY3NyZnRva2VuIiA9PiAkdGhpcy0+dG9rZW4sICJtZWRpYV9pZCIgPT4gJG1lZGlhSWQgKSk7DQogICAgICAgIHJldHVybiAkdGhpcy0+cmVxdWVzdCgibWVkaWEvIiAuICRtZWRpYUlkIC4gIi9saWtlLyIsIFNpZ25hdHVyZVV0aWxzOjpnZW5lcmF0ZVNpZ25hdHVyZSgkZGF0YSkpWzFdOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBVbmxpa2UgcGhvdG8gb3IgdmlkZW8uDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJG1lZGlhSWQNCiAgICAgICAgICogICBNZWRpYSBpZA0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgc3RhdHVzIHJlcXVlc3QNCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gdW5saWtlKCRtZWRpYUlkKQ0KICAgIHsNCiAgICAgICAgJGRhdGEgPSBqc29uX2VuY29kZShhcnJheSggIl91dWlkIiA9PiAkdGhpcy0+dXVpZCwgIl91aWQiID0+ICR0aGlzLT51c2VybmFtZV9pZCwgIl9jc3JmdG9rZW4iID0+ICR0aGlzLT50b2tlbiwgIm1lZGlhX2lkIiA9PiAkbWVkaWFJZCApKTsNCiAgICAgICAgcmV0dXJuICR0aGlzLT5yZXF1ZXN0KCJtZWRpYS8iIC4gJG1lZGlhSWQgLiAiL3VubGlrZS8iLCBTaWduYXR1cmVVdGlsczo6Z2VuZXJhdGVTaWduYXR1cmUoJGRhdGEpKVsxXTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogR2V0IG1lZGlhIGNvbW1lbnRzLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICRtZWRpYUlkDQogICAgICAgICAqICAgTWVkaWEgaWQNCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIE1lZGlhIGNvbW1lbnRzIGRhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gZ2V0TWVkaWFDb21tZW50cygkbWVkaWFJZCkNCiAgICB7DQogICAgICAgIHJldHVybiAkdGhpcy0+cmVxdWVzdCgibWVkaWEvIiAuICRtZWRpYUlkIC4gIi9jb21tZW50cy8/IilbMV07DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIFNldCBuYW1lIGFuZCBwaG9uZSAoT3B0aW9uYWwpLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICRuYW1lDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJHBob25lDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBTZXQgc3RhdHVzIGRhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gc2V0TmFtZUFuZFBob25lKCRuYW1lID0gIiIsICRwaG9uZSA9ICIiKQ0KICAgIHsNCiAgICAgICAgJGRhdGEgPSBqc29uX2VuY29kZShhcnJheSggIl91dWlkIiA9PiAkdGhpcy0+dXVpZCwgIl91aWQiID0+ICR0aGlzLT51c2VybmFtZV9pZCwgImZpcnN0X25hbWUiID0+ICRuYW1lLCAicGhvbmVfbnVtYmVyIiA9PiAkcGhvbmUsICJfY3NyZnRva2VuIiA9PiAkdGhpcy0+dG9rZW4gKSk7DQogICAgICAgIHJldHVybiAkdGhpcy0+cmVxdWVzdCgiYWNjb3VudHMvc2V0X3Bob25lX2FuZF9uYW1lLyIsIFNpZ25hdHVyZVV0aWxzOjpnZW5lcmF0ZVNpZ25hdHVyZSgkZGF0YSkpWzFdOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBHZXQgZGlyZWN0IHNoYXJlLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgRGlyZWN0IHNoYXJlIGRhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gZ2V0RGlyZWN0U2hhcmUoKQ0KICAgIHsNCiAgICAgICAgcmV0dXJuICR0aGlzLT5yZXF1ZXN0KCJkaXJlY3Rfc2hhcmUvaW5ib3gvPyIpWzFdOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBCYWNrdXBzIGFsbCB5b3VyIHVwbG9hZGVkIHBob3RvcyA6KS4NCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gYmFja3VwKCkNCiAgICB7DQogICAgICAgICRteVVwbG9hZHMgPSAkdGhpcy0+Z2V0U2VsZlVzZXJGZWVkKCk7DQogICAgICAgIGZvcmVhY2goICRteVVwbG9hZHNbIml0ZW1zIl0gYXMgJGl0ZW0gKSANCiAgICAgICAgew0KICAgICAgICAgICAgaWYoICFpc19kaXIoJHRoaXMtPklHRGF0YVBhdGggLiAiYmFja3VwLyIgLiAoc3RyaW5nKSAkdGhpcy0+dXNlcm5hbWUgLiAiLSIgLiBkYXRlKCJZLW0tZCIpKSApIA0KICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgIG1rZGlyKCR0aGlzLT5JR0RhdGFQYXRoIC4gImJhY2t1cC8iIC4gKHN0cmluZykgJHRoaXMtPnVzZXJuYW1lIC4gIi0iIC4gZGF0ZSgiWS1tLWQiKSk7DQogICAgICAgICAgICB9DQoNCiAgICAgICAgICAgIGZpbGVfcHV0X2NvbnRlbnRzKCR0aGlzLT5JR0RhdGFQYXRoIC4gImJhY2t1cC8iIC4gKHN0cmluZykgJHRoaXMtPnVzZXJuYW1lIC4gIi0iIC4gZGF0ZSgiWS1tLWQiKSAuICIvIiAuICRpdGVtWyJpZCJdIC4gIi5qcGciLCBmaWxlX2dldF9jb250ZW50cygkaXRlbVsiaW1hZ2VfdmVyc2lvbnMyIl1bImNhbmRpZGF0ZXMiXVswXVsidXJsIl0pKTsNCiAgICAgICAgfQ0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBGb2xsb3cuDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJHVzZXJJZA0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgRnJpZW5kc2hpcCBzdGF0dXMgZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBmb2xsb3coJHVzZXJJZCkNCiAgICB7DQogICAgICAgICRkYXRhID0ganNvbl9lbmNvZGUoYXJyYXkoICJfdXVpZCIgPT4gJHRoaXMtPnV1aWQsICJfdWlkIiA9PiAkdGhpcy0+dXNlcm5hbWVfaWQsICJ1c2VyX2lkIiA9PiAkdXNlcklkLCAiX2NzcmZ0b2tlbiIgPT4gJHRoaXMtPnRva2VuICkpOw0KICAgICAgICByZXR1cm4gJHRoaXMtPnJlcXVlc3QoImZyaWVuZHNoaXBzL2NyZWF0ZS8iIC4gJHVzZXJJZCAuICIvIiwgU2lnbmF0dXJlVXRpbHM6OmdlbmVyYXRlU2lnbmF0dXJlKCRkYXRhKSlbMV07DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIFVuZm9sbG93Lg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICR1c2VySWQNCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIEZyaWVuZHNoaXAgc3RhdHVzIGRhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gdW5mb2xsb3coJHVzZXJJZCkNCiAgICB7DQogICAgICAgICRkYXRhID0ganNvbl9lbmNvZGUoYXJyYXkoICJfdXVpZCIgPT4gJHRoaXMtPnV1aWQsICJfdWlkIiA9PiAkdGhpcy0+dXNlcm5hbWVfaWQsICJ1c2VyX2lkIiA9PiAkdXNlcklkLCAiX2NzcmZ0b2tlbiIgPT4gJHRoaXMtPnRva2VuICkpOw0KICAgICAgICByZXR1cm4gJHRoaXMtPnJlcXVlc3QoImZyaWVuZHNoaXBzL2Rlc3Ryb3kvIiAuICR1c2VySWQgLiAiLyIsIFNpZ25hdHVyZVV0aWxzOjpnZW5lcmF0ZVNpZ25hdHVyZSgkZGF0YSkpWzFdOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBCbG9jay4NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkdXNlcklkDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBGcmllbmRzaGlwIHN0YXR1cyBkYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGJsb2NrKCR1c2VySWQpDQogICAgew0KICAgICAgICAkZGF0YSA9IGpzb25fZW5jb2RlKGFycmF5KCAiX3V1aWQiID0+ICR0aGlzLT51dWlkLCAiX3VpZCIgPT4gJHRoaXMtPnVzZXJuYW1lX2lkLCAidXNlcl9pZCIgPT4gJHVzZXJJZCwgIl9jc3JmdG9rZW4iID0+ICR0aGlzLT50b2tlbiApKTsNCiAgICAgICAgcmV0dXJuICR0aGlzLT5yZXF1ZXN0KCJmcmllbmRzaGlwcy9ibG9jay8iIC4gJHVzZXJJZCAuICIvIiwgU2lnbmF0dXJlVXRpbHM6OmdlbmVyYXRlU2lnbmF0dXJlKCRkYXRhKSlbMV07DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIFVuYmxvY2suDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJHVzZXJJZA0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgRnJpZW5kc2hpcCBzdGF0dXMgZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiB1bmJsb2NrKCR1c2VySWQpDQogICAgew0KICAgICAgICAkZGF0YSA9IGpzb25fZW5jb2RlKGFycmF5KCAiX3V1aWQiID0+ICR0aGlzLT51dWlkLCAiX3VpZCIgPT4gJHRoaXMtPnVzZXJuYW1lX2lkLCAidXNlcl9pZCIgPT4gJHVzZXJJZCwgIl9jc3JmdG9rZW4iID0+ICR0aGlzLT50b2tlbiApKTsNCiAgICAgICAgcmV0dXJuICR0aGlzLT5yZXF1ZXN0KCJmcmllbmRzaGlwcy91bmJsb2NrLyIgLiAkdXNlcklkIC4gIi8iLCBTaWduYXR1cmVVdGlsczo6Z2VuZXJhdGVTaWduYXR1cmUoJGRhdGEpKVsxXTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogU2hvdyBVc2VyIEZyaWVuZHNoaXAuDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJHVzZXJJZA0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgRnJpZW5kc2hpcCByZWxhdGlvbnNoaXAgZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiB1c2VyRnJpZW5kc2hpcCgkdXNlcklkKQ0KICAgIHsNCiAgICAgICAgJGRhdGEgPSBqc29uX2VuY29kZShhcnJheSggIl91dWlkIiA9PiAkdGhpcy0+dXVpZCwgIl91aWQiID0+ICR0aGlzLT51c2VybmFtZV9pZCwgInVzZXJfaWQiID0+ICR1c2VySWQsICJfY3NyZnRva2VuIiA9PiAkdGhpcy0+dG9rZW4gKSk7DQogICAgICAgIHJldHVybiAkdGhpcy0+cmVxdWVzdCgiZnJpZW5kc2hpcHMvc2hvdy8iIC4gJHVzZXJJZCAuICIvIiwgU2lnbmF0dXJlVXRpbHM6OmdlbmVyYXRlU2lnbmF0dXJlKCRkYXRhKSlbMV07DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIEdldCBsaWtlZCBtZWRpYS4NCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIExpa2VkIG1lZGlhIGRhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gZ2V0TGlrZWRNZWRpYSgkbWF4aWQgPSBOVUxMKQ0KICAgIHsNCiAgICAgICAgcmV0dXJuICR0aGlzLT5yZXF1ZXN0KCJmZWVkL2xpa2VkLz9tYXhfaWQ9IiAuICRtYXhpZClbMV07DQogICAgfQ0KDQogICAgcHJvdGVjdGVkIGZ1bmN0aW9uIGJ1aWxkQm9keSgkYm9kaWVzLCAkYm91bmRhcnkpDQogICAgew0KICAgICAgICAkYm9keSA9ICIiOw0KICAgICAgICBmb3JlYWNoKCAkYm9kaWVzIGFzICRiICkgDQogICAgICAgIHsNCiAgICAgICAgICAgICRib2R5IC49ICItLSIgLiAkYm91bmRhcnkgLiAiXHJcbiI7DQogICAgICAgICAgICAkYm9keSAuPSAiQ29udGVudC1EaXNwb3NpdGlvbjogIiAuICRiWyJ0eXBlIl0gLiAiOyBuYW1lPVwiIiAuICRiWyJuYW1lIl0gLiAiXCIiOw0KICAgICAgICAgICAgaWYoIGlzc2V0KCRiWyJmaWxlbmFtZSJdKSApIA0KICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICRleHQgPSBwYXRoaW5mbygkYlsiZmlsZW5hbWUiXSwgUEFUSElORk9fRVhURU5TSU9OKTsNCiAgICAgICAgICAgICAgICAkYm9keSAuPSAiOyBmaWxlbmFtZT1cIiIgLiAicGVuZGluZ19tZWRpYV8iIC4gbnVtYmVyX2Zvcm1hdChyb3VuZChtaWNyb3RpbWUodHJ1ZSkgKiAxMDAwKSwgMCwgIiIsICIiKSAuICIuIiAuICRleHQgLiAiXCIiOw0KICAgICAgICAgICAgfQ0KDQogICAgICAgICAgICBpZiggaXNzZXQoJGJbImhlYWRlcnMiXSkgJiYgaXNfYXJyYXkoJGJbImhlYWRlcnMiXSkgKSANCiAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICBmb3JlYWNoKCAkYlsiaGVhZGVycyJdIGFzICRoZWFkZXIgKSANCiAgICAgICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgICAgICRib2R5IC49ICJcclxuIiAuICRoZWFkZXI7DQogICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgfQ0KDQogICAgICAgICAgICAkYm9keSAuPSAiXHJcblxyXG4iIC4gJGJbImRhdGEiXSAuICJcclxuIjsNCiAgICAgICAgfQ0KICAgICAgICAkYm9keSAuPSAiLS0iIC4gJGJvdW5kYXJ5IC4gIi0tIjsNCiAgICAgICAgcmV0dXJuICRib2R5Ow0KICAgIH0NCg0KICAgIHByb3RlY3RlZCBmdW5jdGlvbiByZXF1ZXN0KCRlbmRwb2ludCwgJHBvc3QgPSBOVUxMLCAkbG9naW4gPSBmYWxzZSkNCiAgICB7DQogICAgICAgIGlmKCAhJHRoaXMtPmlzTG9nZ2VkSW4gJiYgISRsb2dpbiApIA0KICAgICAgICB7DQogICAgICAgICAgICB0aHJvdyBuZXcgRXhjZXB0aW9uKCJOb3QgbG9nZ2VkIGluXG4iKTsNCiAgICAgICAgfQ0KDQogICAgICAgICRoZWFkZXJzID0gYXJyYXkoICJDb25uZWN0aW9uOiBjbG9zZSIsICJBY2NlcHQ6ICovKiIsICJYLUlHLUNhcGFiaWxpdGllczogIiAuIENvbnN0YW50czo6WF9JR19DYXBhYmlsaXRpZXMsICJYLUlHLUNvbm5lY3Rpb24tVHlwZTogV0lGSSIsICJDb250ZW50LXR5cGU6IGFwcGxpY2F0aW9uL3gtd3d3LWZvcm0tdXJsZW5jb2RlZDsgY2hhcnNldD1VVEYtOCIsICJBY2NlcHQtTGFuZ3VhZ2U6IHRyLVRSIiwgIlhfRk9SV0FSREVEX0ZPUjogIiAuICR0aGlzLT5zZXR0aW5ncy0+Z2V0KCJpcCIpLCAiUkVNT1RFX0FERFI6ICIgLiAkdGhpcy0+c2V0dGluZ3MtPmdldCgiaXAiKSApOw0KICAgICAgICAkY2ggPSBjdXJsX2luaXQoKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1VSTCwgQ29uc3RhbnRzOjpBUElfVVJMIC4gJGVuZHBvaW50KTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1VTRVJBR0VOVCwgJHRoaXMtPnNldHRpbmdzLT5nZXQoInVzZXJfYWdlbnQiKSk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9SRVRVUk5UUkFOU0ZFUiwgdHJ1ZSk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9GT0xMT1dMT0NBVElPTiwgdHJ1ZSk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9IRUFERVIsIHRydWUpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfSFRUUEhFQURFUiwgJGhlYWRlcnMpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfVkVSQk9TRSwgZmFsc2UpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfU1NMX1ZFUklGWVBFRVIsIGZhbHNlKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1NTTF9WRVJJRllIT1NULCBmYWxzZSk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9DT09LSUVGSUxFLCAkdGhpcy0+SUdEYXRhUGF0aCAuIChzdHJpbmcpICR0aGlzLT51c2VybmFtZSAuICIuZGF0Iik7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9DT09LSUVKQVIsICR0aGlzLT5JR0RhdGFQYXRoIC4gKHN0cmluZykgJHRoaXMtPnVzZXJuYW1lIC4gIi5kYXQiKTsNCiAgICAgICAgaWYoICRwb3N0ICkgDQogICAgICAgIHsNCiAgICAgICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9QT1NULCB0cnVlKTsNCiAgICAgICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9QT1NURklFTERTLCAkcG9zdCk7DQogICAgICAgIH0NCg0KICAgICAgICAkcmVzcCA9IGN1cmxfZXhlYygkY2gpOw0KICAgICAgICAkaGVhZGVyX2xlbiA9IGN1cmxfZ2V0aW5mbygkY2gsIENVUkxJTkZPX0hFQURFUl9TSVpFKTsNCiAgICAgICAgJGhlYWRlciA9IHN1YnN0cigkcmVzcCwgMCwgJGhlYWRlcl9sZW4pOw0KICAgICAgICAkYm9keSA9IHN1YnN0cigkcmVzcCwgJGhlYWRlcl9sZW4pOw0KICAgICAgICBjdXJsX2Nsb3NlKCRjaCk7DQogICAgICAgIGlmKCAkdGhpcy0+ZGVidWcgKSANCiAgICAgICAgew0KICAgICAgICAgICAgZWNobyAiUkVRVUVTVDogIiAuICRlbmRwb2ludCAuICJcbiI7DQogICAgICAgICAgICBpZiggIWlzX251bGwoJHBvc3QpICYmICFpc19hcnJheSgkcG9zdCkgKSANCiAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICBlY2hvICJEQVRBOiAiIC4gdXJsZGVjb2RlKCRwb3N0KSAuICJcbiI7DQogICAgICAgICAgICB9DQoNCiAgICAgICAgICAgIGVjaG8gIlJFU1BPTlNFOiAiIC4gJGJvZHkgLiAiXG5cbiI7DQogICAgICAgIH0NCg0KICAgICAgICByZXR1cm4gYXJyYXkoICRoZWFkZXIsIGpzb25fZGVjb2RlKCRib2R5LCB0cnVlKSApOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBJcyBVc2VyIExvZ2dlZCBpbj8NCiAgICAgICAgICogQHJldHVybiBib29sDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGlzTG9nZ2VkSW4oKQ0KICAgIHsNCiAgICAgICAgcmV0dXJuICR0aGlzLT5pc0xvZ2dlZEluOw0KICAgIH0NCg0KfQ0KDQoNCmNsYXNzIEluc3RhZ3JhbVdlYg0Kew0KICAgIHByb3RlY3RlZCAkdXNlcm5hbWUgPSBOVUxMOw0KICAgIHByb3RlY3RlZCAkdXNlcm5hbWVfaWQgPSBOVUxMOw0KICAgIHByb3RlY3RlZCAkdG9rZW4gPSBOVUxMOw0KICAgIHByb3RlY3RlZCAkaXNMb2dnZWRJbiA9IGZhbHNlOw0KICAgIHByb3RlY3RlZCAkSUdEYXRhUGF0aCA9IE5VTEw7DQovKioNCiAgICAgICAgICogQHZhciBTZXR0aW5ncw0KICAgICAgICAgKi8NCiAgICBwdWJsaWMgJHNldHRpbmdzID0gTlVMTDsNCg0KICAgIC8qKg0KICAgICAgICAgKiBEZWZhdWx0IGNsYXNzIGNvbnN0cnVjdG9yLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICR1c2VybmFtZQ0KICAgICAgICAgKiAgIFlvdXIgSW5zdGFncmFtIHVzZXJuYW1lLg0KICAgICAgICAgKiBAcGFyYW0gICAgICAgICRJR0RhdGFQYXRoDQogICAgICAgICAqICAgRGVmYXVsdCBmb2xkZXIgdG8gc3RvcmUgZGF0YSwgeW91IGNhbiBjaGFuZ2UgaXQuDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIF9fY29uc3RydWN0KCR1c2VybmFtZSwgJElHRGF0YVBhdGggPSBOVUxMLCAkZm9yY2VVc2VySVAgPSBmYWxzZSkNCiAgICB7DQogICAgICAgICR1c2VybmFtZSA9IHRyaW0oJHVzZXJuYW1lKTsNCiAgICAgICAgaWYoICFpc19udWxsKCRJR0RhdGFQYXRoKSApIA0KICAgICAgICB7DQogICAgICAgICAgICAkdGhpcy0+SUdEYXRhUGF0aCA9ICRJR0RhdGFQYXRoOw0KICAgICAgICB9DQogICAgICAgIGVsc2UNCiAgICAgICAgew0KICAgICAgICAgICAgJHRoaXMtPklHRGF0YVBhdGggPSBXb3c6OmdldCgicHJvamVjdC9jb29raWVQYXRoIikgLiAiaW5zdGFncmFtLyI7DQogICAgICAgIH0NCg0KICAgICAgICAkdGhpcy0+c2V0VXNlcigkdXNlcm5hbWUsICRmb3JjZVVzZXJJUCk7DQogICAgfQ0KDQogICAgcHJvdGVjdGVkIGZ1bmN0aW9uIGNoZWNrU2V0dGluZ3MoJHVzZXJuYW1lLCAkZm9yY2VVc2VySVAgPSBmYWxzZSkNCiAgICB7DQogICAgICAgICR0aGlzLT5zZXR0aW5ncyA9IG5ldyBTZXR0aW5ncygkdGhpcy0+SUdEYXRhUGF0aCAuICR1c2VybmFtZSAuICIuY25mIik7DQogICAgICAgIGlmKCAkdGhpcy0+c2V0dGluZ3MtPmdldCgiaXAiKSA9PSBOVUxMIHx8ICRmb3JjZVVzZXJJUCApIA0KICAgICAgICB7DQogICAgICAgICAgICAkaXBBZHJlc3MgPSAiNzguIiAuIHJhbmQoMTYwLCAxOTEpIC4gIi4iIC4gcmFuZCgxLCAyNTUpIC4gIi4iIC4gcmFuZCgxLCAyNTUpOw0KICAgICAgICAgICAgaWYoICRmb3JjZVVzZXJJUCAmJiAhZW1wdHkoJF9TRVJWRVJbIlJFTU9URV9BRERSIl0pICkgDQogICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgJGlwQWRyZXNzID0gJF9TRVJWRVJbIlJFTU9URV9BRERSIl07DQogICAgICAgICAgICB9DQoNCiAgICAgICAgICAgICR0aGlzLT5zZXR0aW5ncy0+c2V0KCJpcCIsICRpcEFkcmVzcyk7DQogICAgICAgIH0NCg0KICAgICAgICBpZiggZmlsZV9leGlzdHMoJHRoaXMtPklHRGF0YVBhdGggLiAoc3RyaW5nKSAkdXNlcm5hbWUgLiAiLmRhdCIpICYmICgkdGhpcy0+c2V0dGluZ3MtPmdldCgidXNlcm5hbWVfaWQiKSA9PSBOVUxMIHx8ICR0aGlzLT5zZXR0aW5ncy0+Z2V0KCJ0b2tlbiIpID09IE5VTEwpICkgDQogICAgICAgIHsNCiAgICAgICAgICAgICRjb29raWVEYXRhID0gVXRpbHM6OmNvb2tpZVRvQXJyYXkoZmlsZV9nZXRfY29udGVudHMoJHRoaXMtPklHRGF0YVBhdGggLiAoc3RyaW5nKSAkdXNlcm5hbWUgLiAiLmRhdCIpLCAid3d3Lmluc3RhZ3JhbS5jb20iKTsNCiAgICAgICAgICAgIGlmKCAkdGhpcy0+c2V0dGluZ3MtPmdldCgidXNlcm5hbWVfaWQiKSA9PSBOVUxMICYmIGlzc2V0KCRjb29raWVEYXRhWyJkc191c2VyX2lkIl0pICkgDQogICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgJHRoaXMtPnNldHRpbmdzLT5zZXQoInVzZXJuYW1lX2lkIiwgJGNvb2tpZURhdGFbImRzX3VzZXJfaWQiXSk7DQogICAgICAgICAgICB9DQoNCiAgICAgICAgICAgIGlmKCAkdGhpcy0+c2V0dGluZ3MtPmdldCgidG9rZW4iKSA9PSBOVUxMICYmIGlzc2V0KCRjb29raWVEYXRhWyJjc3JmdG9rZW4iXSkgKSANCiAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAkdGhpcy0+c2V0dGluZ3MtPnNldCgidG9rZW4iLCAkY29va2llRGF0YVsiY3NyZnRva2VuIl0pOw0KICAgICAgICAgICAgfQ0KDQogICAgICAgIH0NCg0KICAgICAgICBpZiggJHRoaXMtPnNldHRpbmdzLT5nZXQoIndlYl91c2VyX2FnZW50IikgPT0gTlVMTCApIA0KICAgICAgICB7DQogICAgICAgICAgICAkdXNlckFnZW50cyA9IGV4cGxvZGUoUEhQX0VPTCwgZmlsZV9nZXRfY29udGVudHMoV293OjpnZXQoInByb2plY3QvY29va2llUGF0aCIpIC4gImRldmljZS9icm93c2Vycy5jc3YiKSk7DQogICAgICAgICAgICAkYWdlbnRJbmRleCA9IHJhbmQoMCwgY291bnQoJHVzZXJBZ2VudHMpIC0gMSk7DQogICAgICAgICAgICAkdXNlckFnZW50ID0gJHVzZXJBZ2VudHNbJGFnZW50SW5kZXhdOw0KICAgICAgICAgICAgJHRoaXMtPnNldHRpbmdzLT5zZXQoIndlYl91c2VyX2FnZW50IiwgJHVzZXJBZ2VudCk7DQogICAgICAgIH0NCg0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBTZXQgdGhlIHVzZXIuIE1hbmFnZSBtdWx0aXBsZSBhY2NvdW50cy4NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkdXNlcm5hbWUNCiAgICAgICAgICogICBZb3VyIEluc3RhZ3JhbSB1c2VybmFtZS4NCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gc2V0VXNlcigkdXNlcm5hbWUsICRmb3JjZVVzZXJJUCA9IGZhbHNlKQ0KICAgIHsNCiAgICAgICAgJHRoaXMtPnVzZXJuYW1lID0gJHVzZXJuYW1lOw0KICAgICAgICAkdGhpcy0+Y2hlY2tTZXR0aW5ncygkdXNlcm5hbWUsICRmb3JjZVVzZXJJUCk7DQogICAgICAgIGlmKCBmaWxlX2V4aXN0cygkdGhpcy0+SUdEYXRhUGF0aCAuIChzdHJpbmcpICR0aGlzLT51c2VybmFtZSAuICIuZGF0IikgJiYgJHRoaXMtPnNldHRpbmdzLT5nZXQoInVzZXJuYW1lX2lkIikgIT0gTlVMTCAmJiAkdGhpcy0+c2V0dGluZ3MtPmdldCgidG9rZW4iKSAhPSBOVUxMICkgDQogICAgICAgIHsNCiAgICAgICAgICAgICR0aGlzLT5pc0xvZ2dlZEluID0gdHJ1ZTsNCiAgICAgICAgICAgICR0aGlzLT51c2VybmFtZV9pZCA9ICR0aGlzLT5zZXR0aW5ncy0+Z2V0KCJ1c2VybmFtZV9pZCIpOw0KICAgICAgICAgICAgJHRoaXMtPnRva2VuID0gJHRoaXMtPnNldHRpbmdzLT5nZXQoInRva2VuIik7DQogICAgICAgIH0NCiAgICAgICAgZWxzZQ0KICAgICAgICB7DQogICAgICAgICAgICAkdGhpcy0+aXNMb2dnZWRJbiA9IGZhbHNlOw0KICAgICAgICB9DQoNCiAgICB9DQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gZ2V0RGF0YSgpDQogICAgew0KICAgICAgICByZXR1cm4gYXJyYXkoICJ1c2VybmFtZSIgPT4gJHRoaXMtPnVzZXJuYW1lLCAidXNlcm5hbWVfaWQiID0+ICR0aGlzLT51c2VybmFtZV9pZCwgInRva2VuIiA9PiAkdGhpcy0+dG9rZW4sICJ1c2VyX2FnZW50IiA9PiAkdGhpcy0+c2V0dGluZ3MtPmdldCgid2ViX3VzZXJfYWdlbnQiKSwgImlwIiA9PiAkdGhpcy0+c2V0dGluZ3MtPmdldCgiaXAiKSApOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBDb21tZW50IG1lZGlhLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICRtZWRpYUlkDQogICAgICAgICAqICAgTWVkaWEgaWQNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkY29tbWVudFRleHQNCiAgICAgICAgICogICBDb21tZW50IFRleHQNCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIGNvbW1lbnQgbWVkaWEgZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBjb21tZW50KCRtZWRpYUlkLCAkY29tbWVudFRleHQpDQogICAgew0KICAgICAgICAkYXJyTWVkaWFJRCA9IGV4cGxvZGUoIl8iLCAkbWVkaWFJZCk7DQogICAgICAgICRtZWRpYUlkID0gJGFyck1lZGlhSURbMF07DQogICAgICAgICRwb3N0RGF0YSA9ICJjb21tZW50X3RleHQ9IiAuICRjb21tZW50VGV4dDsNCiAgICAgICAgJGhlYWRlcnMgPSBhcnJheSggICk7DQogICAgICAgICRoZWFkZXJzW10gPSAiUmVmZXJlcjogaHR0cHM6Ly93d3cuaW5zdGFncmFtLmNvbS8iOw0KICAgICAgICAkaGVhZGVyc1tdID0gIkROVDogMSI7DQogICAgICAgICRoZWFkZXJzW10gPSAiT3JpZ2luOiBodHRwczovL3d3dy5pbnN0YWdyYW0uY29tLyI7DQogICAgICAgICRoZWFkZXJzW10gPSAiWC1DU1JGVG9rZW46ICIgLiB0cmltKCR0aGlzLT50b2tlbik7DQogICAgICAgICRoZWFkZXJzW10gPSAiWC1SZXF1ZXN0ZWQtV2l0aDogWE1MSHR0cFJlcXVlc3QiOw0KICAgICAgICAkaGVhZGVyc1tdID0gIlgtSW5zdGFncmFtLUFKQVg6IDEiOw0KICAgICAgICAkaGVhZGVyc1tdID0gIkNvbm5lY3Rpb246IGNsb3NlIjsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJDYWNoZS1Db250cm9sOiBtYXgtYWdlPTAiOw0KICAgICAgICByZXR1cm4gJHRoaXMtPnJlcXVlc3QoIndlYi9jb21tZW50cy8iIC4gJG1lZGlhSWQgLiAiL2FkZC8iLCAkaGVhZGVycywgJHBvc3REYXRhKVsxXTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogR2V0IHVzZXJuYW1lIGluZm8uDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJHVzZXJuYW1lDQogICAgICAgICAqICAgVXNlcm5hbWUNCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIFVzZXJuYW1lIGRhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gZ2V0VXNlcm5hbWVJbmZvKCR1c2VybmFtZSkNCiAgICB7DQogICAgICAgICRoZWFkZXJzID0gYXJyYXkoICApOw0KICAgICAgICAkaGVhZGVyc1tdID0gIlJlZmVyZXI6IGh0dHBzOi8vd3d3Lmluc3RhZ3JhbS5jb20vIjsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJETlQ6IDEiOw0KICAgICAgICAkaGVhZGVyc1tdID0gIk9yaWdpbjogaHR0cHM6Ly93d3cuaW5zdGFncmFtLmNvbS8iOw0KICAgICAgICAkaGVhZGVyc1tdID0gIlgtQ1NSRlRva2VuOiAiIC4gdHJpbSgkdGhpcy0+dG9rZW4pOw0KICAgICAgICAkaGVhZGVyc1tdID0gIlgtUmVxdWVzdGVkLVdpdGg6IFhNTEh0dHBSZXF1ZXN0IjsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJYLUluc3RhZ3JhbS1BSkFYOiAxIjsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJDb25uZWN0aW9uOiBjbG9zZSI7DQogICAgICAgICRoZWFkZXJzW10gPSAiQ2FjaGUtQ29udHJvbDogbWF4LWFnZT0wIjsNCiAgICAgICAgcmV0dXJuICR0aGlzLT5yZXF1ZXN0KChzdHJpbmcpICR1c2VybmFtZSAuICIvP19fYT0xIiwgJGhlYWRlcnMpWzFdOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBNZWRpYSBpbmZvDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJG1lZGlhQ29kZQ0KICAgICAgICAgKiAgIE1lZGlhIGNvZGUNCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIGRlbGV0ZSByZXF1ZXN0IGRhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gbWVkaWFJbmZvKCRtZWRpYUNvZGUpDQogICAgew0KICAgICAgICAkaGVhZGVycyA9IGFycmF5KCAgKTsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJSZWZlcmVyOiBodHRwczovL3d3dy5pbnN0YWdyYW0uY29tLyI7DQogICAgICAgICRoZWFkZXJzW10gPSAiRE5UOiAxIjsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJPcmlnaW46IGh0dHBzOi8vd3d3Lmluc3RhZ3JhbS5jb20vIjsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJYLUNTUkZUb2tlbjogIiAuIHRyaW0oJHRoaXMtPnRva2VuKTsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJYLVJlcXVlc3RlZC1XaXRoOiBYTUxIdHRwUmVxdWVzdCI7DQogICAgICAgICRoZWFkZXJzW10gPSAiWC1JbnN0YWdyYW0tQUpBWDogMSI7DQogICAgICAgICRoZWFkZXJzW10gPSAiQ29ubmVjdGlvbjogY2xvc2UiOw0KICAgICAgICAkaGVhZGVyc1tdID0gIkNhY2hlLUNvbnRyb2w6IG1heC1hZ2U9MCI7DQogICAgICAgIHJldHVybiAkdGhpcy0+cmVxdWVzdCgicC8iIC4gJG1lZGlhQ29kZSAuICIvP19fYT0xIiwgJGhlYWRlcnMpWzFdOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBMaWtlIHBob3RvIG9yIHZpZGVvLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICRtZWRpYUlkDQogICAgICAgICAqICAgTWVkaWEgaWQNCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIHN0YXR1cyByZXF1ZXN0DQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGxpa2UoJG1lZGlhSWQpDQogICAgew0KICAgICAgICAkYXJyTWVkaWFJRCA9IGV4cGxvZGUoIl8iLCAkbWVkaWFJZCk7DQogICAgICAgICRtZWRpYUlkID0gJGFyck1lZGlhSURbMF07DQogICAgICAgICRoZWFkZXJzID0gYXJyYXkoICApOw0KICAgICAgICAkaGVhZGVyc1tdID0gIlJlZmVyZXI6IGh0dHBzOi8vd3d3Lmluc3RhZ3JhbS5jb20vaW5zdGFncmFtLyI7DQogICAgICAgICRoZWFkZXJzW10gPSAiRE5UOiAxIjsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJPcmlnaW46IGh0dHBzOi8vd3d3Lmluc3RhZ3JhbS5jb20vIjsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJYLUNTUkZUb2tlbjogIiAuIHRyaW0oJHRoaXMtPnRva2VuKTsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJYLVJlcXVlc3RlZC1XaXRoOiBYTUxIdHRwUmVxdWVzdCI7DQogICAgICAgICRoZWFkZXJzW10gPSAiWC1JbnN0YWdyYW0tQUpBWDogMSI7DQogICAgICAgICRoZWFkZXJzW10gPSAiQ29ubmVjdGlvbjogY2xvc2UiOw0KICAgICAgICAkaGVhZGVyc1tdID0gIkNhY2hlLUNvbnRyb2w6IG1heC1hZ2U9MCI7DQogICAgICAgIHJldHVybiAkdGhpcy0+cmVxdWVzdCgid2ViL2xpa2VzLyIgLiAkbWVkaWFJZCAuICIvbGlrZS8iLCAkaGVhZGVycywgdHJ1ZSlbMV07DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIFVubGlrZSBwaG90byBvciB2aWRlby4NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkbWVkaWFJZA0KICAgICAgICAgKiAgIE1lZGlhIGlkDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBzdGF0dXMgcmVxdWVzdA0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiB1bmxpa2UoJG1lZGlhSWQpDQogICAgew0KICAgICAgICAkaGVhZGVycyA9IGFycmF5KCAgKTsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJSZWZlcmVyOiBodHRwczovL3d3dy5pbnN0YWdyYW0uY29tLyI7DQogICAgICAgICRoZWFkZXJzW10gPSAiRE5UOiAxIjsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJPcmlnaW46IGh0dHBzOi8vd3d3Lmluc3RhZ3JhbS5jb20vIjsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJYLUNTUkZUb2tlbjogIiAuIHRyaW0oJHRoaXMtPnRva2VuKTsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJYLVJlcXVlc3RlZC1XaXRoOiBYTUxIdHRwUmVxdWVzdCI7DQogICAgICAgICRoZWFkZXJzW10gPSAiWC1JbnN0YWdyYW0tQUpBWDogMSI7DQogICAgICAgICRoZWFkZXJzW10gPSAiQ29ubmVjdGlvbjogY2xvc2UiOw0KICAgICAgICAkaGVhZGVyc1tdID0gIkNhY2hlLUNvbnRyb2w6IG1heC1hZ2U9MCI7DQogICAgICAgIHJldHVybiAkdGhpcy0+cmVxdWVzdCgid2ViL2xpa2VzLyIgLiAkbWVkaWFJZCAuICIvdW5saWtlLyIsICRoZWFkZXJzLCB0cnVlKVsxXTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogRm9sbG93Lg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICR1c2VySWQNCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIEZyaWVuZHNoaXAgc3RhdHVzIGRhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gZm9sbG93KCR1c2VySWQpDQogICAgew0KICAgICAgICAkaGVhZGVycyA9IGFycmF5KCAgKTsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJSZWZlcmVyOiBodHRwczovL3d3dy5pbnN0YWdyYW0uY29tL2luc3RhZ3JhbS8iOw0KICAgICAgICAkaGVhZGVyc1tdID0gIkROVDogMSI7DQogICAgICAgICRoZWFkZXJzW10gPSAiT3JpZ2luOiBodHRwczovL3d3dy5pbnN0YWdyYW0uY29tLyI7DQogICAgICAgICRoZWFkZXJzW10gPSAiWC1DU1JGVG9rZW46ICIgLiB0cmltKCR0aGlzLT50b2tlbik7DQogICAgICAgICRoZWFkZXJzW10gPSAiWC1SZXF1ZXN0ZWQtV2l0aDogWE1MSHR0cFJlcXVlc3QiOw0KICAgICAgICAkaGVhZGVyc1tdID0gIlgtSW5zdGFncmFtLUFKQVg6IDEiOw0KICAgICAgICAkaGVhZGVyc1tdID0gIkNvbm5lY3Rpb246IGNsb3NlIjsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJDYWNoZS1Db250cm9sOiBtYXgtYWdlPTAiOw0KICAgICAgICByZXR1cm4gJHRoaXMtPnJlcXVlc3QoIndlYi9mcmllbmRzaGlwcy8iIC4gJHVzZXJJZCAuICIvZm9sbG93LyIsICRoZWFkZXJzLCB0cnVlKVsxXTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogVW5mb2xsb3cuDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJHVzZXJJZA0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgRnJpZW5kc2hpcCBzdGF0dXMgZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiB1bmZvbGxvdygkdXNlcklkKQ0KICAgIHsNCiAgICAgICAgJGhlYWRlcnMgPSBhcnJheSggICk7DQogICAgICAgICRoZWFkZXJzW10gPSAiUmVmZXJlcjogaHR0cHM6Ly93d3cuaW5zdGFncmFtLmNvbS9pbnN0YWdyYW0vIjsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJETlQ6IDEiOw0KICAgICAgICAkaGVhZGVyc1tdID0gIk9yaWdpbjogaHR0cHM6Ly93d3cuaW5zdGFncmFtLmNvbS8iOw0KICAgICAgICAkaGVhZGVyc1tdID0gIlgtQ1NSRlRva2VuOiAiIC4gdHJpbSgkdGhpcy0+dG9rZW4pOw0KICAgICAgICAkaGVhZGVyc1tdID0gIlgtUmVxdWVzdGVkLVdpdGg6IFhNTEh0dHBSZXF1ZXN0IjsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJYLUluc3RhZ3JhbS1BSkFYOiAxIjsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJDb25uZWN0aW9uOiBjbG9zZSI7DQogICAgICAgICRoZWFkZXJzW10gPSAiQ2FjaGUtQ29udHJvbDogbWF4LWFnZT0wIjsNCiAgICAgICAgcmV0dXJuICR0aGlzLT5yZXF1ZXN0KCJ3ZWIvZnJpZW5kc2hpcHMvIiAuICR1c2VySWQgLiAiL3VuZm9sbG93LyIsICRoZWFkZXJzLCB0cnVlKVsxXTsNCiAgICB9DQoNCiAgICBwcm90ZWN0ZWQgZnVuY3Rpb24gcmVxdWVzdCgkZW5kcG9pbnQsIGFycmF5ICRvcHRpb25hbGhlYWRlcnMsICRwb3N0ID0gTlVMTCkNCiAgICB7DQogICAgICAgIGlmKCAhJHRoaXMtPmlzTG9nZ2VkSW4gKSANCiAgICAgICAgew0KICAgICAgICAgICAgdGhyb3cgbmV3IEV4Y2VwdGlvbigiTm90IGxvZ2dlZCBpblxuIik7DQogICAgICAgIH0NCg0KICAgICAgICAkaGVhZGVycyA9IGFycmF5KCAiQWNjZXB0OiAqLyoiLCAiQWNjZXB0LUxhbmd1YWdlOiB0cjtxPTEiLCAiWF9GT1JXQVJERURfRk9SOiAiIC4gJHRoaXMtPnNldHRpbmdzLT5nZXQoImlwIiksICJSRU1PVEVfQUREUjogIiAuICR0aGlzLT5zZXR0aW5ncy0+Z2V0KCJpcCIpICk7DQogICAgICAgICRoZWFkZXJzID0gYXJyYXlfbWVyZ2UoJGhlYWRlcnMsICRvcHRpb25hbGhlYWRlcnMpOw0KICAgICAgICAkY2ggPSBjdXJsX2luaXQoKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1VSTCwgQ29uc3RhbnRzOjpXRUJfVVJMIC4gJGVuZHBvaW50KTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1VTRVJBR0VOVCwgJHRoaXMtPnNldHRpbmdzLT5nZXQoIndlYl91c2VyX2FnZW50IikpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfUkVUVVJOVFJBTlNGRVIsIHRydWUpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfRk9MTE9XTE9DQVRJT04sIHRydWUpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfSEVBREVSLCB0cnVlKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX0hUVFBIRUFERVIsICRoZWFkZXJzKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1ZFUkJPU0UsIGZhbHNlKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1NTTF9WRVJJRllQRUVSLCBmYWxzZSk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9TU0xfVkVSSUZZSE9TVCwgZmFsc2UpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfQ09PS0lFRklMRSwgJHRoaXMtPklHRGF0YVBhdGggLiAoc3RyaW5nKSAkdGhpcy0+dXNlcm5hbWUgLiAiLmRhdCIpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfQ09PS0lFSkFSLCAkdGhpcy0+SUdEYXRhUGF0aCAuIChzdHJpbmcpICR0aGlzLT51c2VybmFtZSAuICIuZGF0Iik7DQogICAgICAgIGlmKCAkcG9zdCApIA0KICAgICAgICB7DQogICAgICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfUE9TVCwgdHJ1ZSk7DQogICAgICAgICAgICBpZiggaXNfc3RyaW5nKCRwb3N0KSApIA0KICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9QT1NURklFTERTLCAkcG9zdCk7DQogICAgICAgICAgICB9DQoNCiAgICAgICAgfQ0KDQogICAgICAgICRyZXNwID0gY3VybF9leGVjKCRjaCk7DQogICAgICAgICRoZWFkZXJfbGVuID0gY3VybF9nZXRpbmZvKCRjaCwgQ1VSTElORk9fSEVBREVSX1NJWkUpOw0KICAgICAgICAkaGVhZGVyID0gc3Vic3RyKCRyZXNwLCAwLCAkaGVhZGVyX2xlbik7DQogICAgICAgICRib2R5ID0gc3Vic3RyKCRyZXNwLCAkaGVhZGVyX2xlbik7DQogICAgICAgIGN1cmxfY2xvc2UoJGNoKTsNCiAgICAgICAgcmV0dXJuIGFycmF5KCAkaGVhZGVyLCBqc29uX2RlY29kZSgkYm9keSwgdHJ1ZSkgKTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogSXMgVXNlciBMb2dnZWQgaW4/DQogICAgICAgICAqIEByZXR1cm4gYm9vbA0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBpc0xvZ2dlZEluKCkNCiAgICB7DQogICAgICAgIHJldHVybiAkdGhpcy0+aXNMb2dnZWRJbjsNCiAgICB9DQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gaXNWYWxpZCgpDQogICAgew0KICAgICAgICAkaGVhZGVycyA9IGFycmF5KCAgKTsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJSZWZlcmVyOiBodHRwczovL3d3dy5pbnN0YWdyYW0uY29tLyI7DQogICAgICAgICRoZWFkZXJzW10gPSAiRE5UOiAxIjsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJPcmlnaW46IGh0dHBzOi8vd3d3Lmluc3RhZ3JhbS5jb20vIjsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJYLUNTUkZUb2tlbjogIiAuIHRyaW0oJHRoaXMtPnRva2VuKTsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJYLVJlcXVlc3RlZC1XaXRoOiBYTUxIdHRwUmVxdWVzdCI7DQogICAgICAgICRoZWFkZXJzW10gPSAiWC1JbnN0YWdyYW0tQUpBWDogMSI7DQogICAgICAgICRoZWFkZXJzW10gPSAiQ29ubmVjdGlvbjogY2xvc2UiOw0KICAgICAgICAkaGVhZGVyc1tdID0gIkNhY2hlLUNvbnRyb2w6IG1heC1hZ2U9MCI7DQogICAgICAgIGxpc3QoJGhlYWRlcikgPSAkdGhpcy0+cmVxdWVzdCgiYWNjb3VudHMvYWN0aXZpdHkvP19fYT0xIiwgJGhlYWRlcnMpOw0KICAgICAgICByZXR1cm4gKHN0cnBvcygkaGVhZGVyLCAiSFRUUC8xLjEgMjAwIikgPT09IGZhbHNlID8gZmFsc2UgOiB0cnVlKTsNCiAgICB9DQoNCn0NCg0KDQpjbGFzcyBTaWduYXR1cmVVdGlscw0Kew0KICAgIHB1YmxpYyBzdGF0aWMgZnVuY3Rpb24gZ2VuZXJhdGVTaWduYXR1cmUoJGRhdGEpDQogICAgew0KICAgICAgICAkaGFzaCA9IGhhc2hfaG1hYygic2hhMjU2IiwgJGRhdGEsIENvbnN0YW50czo6SUdfU0lHX0tFWSk7DQogICAgICAgIHJldHVybiAiaWdfc2lnX2tleV92ZXJzaW9uPSIgLiBDb25zdGFudHM6OlNJR19LRVlfVkVSU0lPTiAuICImc2lnbmVkX2JvZHk9IiAuICRoYXNoIC4gIi4iIC4gdXJsZW5jb2RlKCRkYXRhKTsNCiAgICB9DQoNCiAgICBwdWJsaWMgc3RhdGljIGZ1bmN0aW9uIGdlbmVyYXRlRGV2aWNlSWQoJHNlZWQpDQogICAgew0KICAgICAgICAkdm9sYXRpbGVfc2VlZCA9IGZpbGVtdGltZShfX0RJUl9fKTsNCiAgICAgICAgcmV0dXJuICJhbmRyb2lkLSIgLiBzdWJzdHIobWQ1KCRzZWVkIC4gJHZvbGF0aWxlX3NlZWQpLCAxNik7DQogICAgfQ0KDQogICAgcHVibGljIHN0YXRpYyBmdW5jdGlvbiBnZW5lcmF0ZVVVSUQoJHR5cGUpDQogICAgew0KICAgICAgICAkdXVpZCA9IHNwcmludGYoIiUwNHglMDR4LSUwNHgtJTA0eC0lMDR4LSUwNHglMDR4JTA0eCIsIG10X3JhbmQoMCwgNjU1MzUpLCBtdF9yYW5kKDAsIDY1NTM1KSwgbXRfcmFuZCgwLCA2NTUzNSksIG10X3JhbmQoMCwgNDA5NSkgfCAxNjM4NCwgbXRfcmFuZCgwLCAxNjM4MykgfCAzMjc2OCwgbXRfcmFuZCgwLCA2NTUzNSksIG10X3JhbmQoMCwgNjU1MzUpLCBtdF9yYW5kKDAsIDY1NTM1KSk7DQogICAgICAgIHJldHVybiAoJHR5cGUgPyAkdXVpZCA6IHN0cl9yZXBsYWNlKCItIiwgIiIsICR1dWlkKSk7DQogICAgfQ0KDQp9DQoNCg0KY2xhc3MgVXNlckFnZW50DQp7DQogICAgcHJvdGVjdGVkICRwYXJlbnQgPSBOVUxMOw0KDQogICAgcHVibGljIGZ1bmN0aW9uIF9fY29uc3RydWN0KCRwYXJlbnQpDQogICAgew0KICAgICAgICAkdGhpcy0+cGFyZW50ID0gJHBhcmVudDsNCiAgICB9DQoNCiAgICBwcm90ZWN0ZWQgZnVuY3Rpb24gZ2V0RGV2aWNlRGF0YSgpDQogICAgew0KICAgICAgICAkY3N2ZmlsZSA9IFdvdzo6Z2V0KCJwcm9qZWN0L2Nvb2tpZVBhdGgiKSAuICJkZXZpY2UvZGV2aWNlcy5jc3YiOw0KICAgICAgICAkZmlsZV9oYW5kbGUgPSBmb3BlbigkY3N2ZmlsZSwgInIiKTsNCiAgICAgICAgJGxpbmVfb2ZfdGV4dCA9IGFycmF5KCAgKTsNCiAgICAgICAgd2hpbGUoICFmZW9mKCRmaWxlX2hhbmRsZSkgKSANCiAgICAgICAgew0KICAgICAgICAgICAgJGxpbmVfb2ZfdGV4dFtdID0gZmdldGNzdigkZmlsZV9oYW5kbGUsIDEwMjQpOw0KICAgICAgICB9DQogICAgICAgICRkZXZpY2VEYXRhID0gZXhwbG9kZSgiOyIsICRsaW5lX29mX3RleHRbbXRfcmFuZCgwLCAxMTg2NyldWzBdKTsNCiAgICAgICAgZmNsb3NlKCRmaWxlX2hhbmRsZSk7DQogICAgICAgIHJldHVybiAkZGV2aWNlRGF0YTsNCiAgICB9DQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gYnVpbGRVc2VyQWdlbnQoKQ0KICAgIHsNCiAgICAgICAgJGRldmljZURhdGEgPSAkdGhpcy0+Z2V0RGV2aWNlRGF0YSgpOw0KICAgICAgICAkdGhpcy0+cGFyZW50LT5zZXR0aW5ncy0+c2V0KCJtYW51ZmFjdHVyZXIiLCAkZGV2aWNlRGF0YVswXSk7DQogICAgICAgICR0aGlzLT5wYXJlbnQtPnNldHRpbmdzLT5zZXQoImRldmljZSIsICRkZXZpY2VEYXRhWzFdKTsNCiAgICAgICAgJHRoaXMtPnBhcmVudC0+c2V0dGluZ3MtPnNldCgibW9kZWwiLCAkZGV2aWNlRGF0YVsyXSk7DQogICAgICAgICRsb2NhbGVMYW5nID0gJHRoaXMtPmRldGVjdFVzZXJMb2NhbGUoKTsNCiAgICAgICAgcmV0dXJuIHNwcmludGYoIkluc3RhZ3JhbSAlcyBBbmRyb2lkICgxOC80LjM7IDMyMGRwaTsgNzIweDEyODA7ICVzOyAlczsgJXM7IHFjb207ICIgLiAkbG9jYWxlTGFuZyAuICIpIiwgQ29uc3RhbnRzOjpWRVJTSU9OLCAkZGV2aWNlRGF0YVswXSwgJGRldmljZURhdGFbMV0sICRkZXZpY2VEYXRhWzJdKTsNCiAgICB9DQoNCiAgICBwcml2YXRlIGZ1bmN0aW9uIGRldGVjdFVzZXJMb2NhbGUoJGZhbGxiYWNrTG9jYWxlID0gImVuX1VTIikNCiAgICB7DQogICAgICAgICR1c2VyTG9jYWxlID0gTlVMTDsNCiAgICAgICAgaWYoIGlzc2V0KCRfU0VSVkVSWyJIVFRQX0FDQ0VQVF9MQU5HVUFHRSJdKSApIA0KICAgICAgICB7DQogICAgICAgICAgICAkYXJyTGFuZyA9IGV4cGxvZGUoIiwiLCAkX1NFUlZFUlsiSFRUUF9BQ0NFUFRfTEFOR1VBR0UiXSk7DQogICAgICAgICAgICAkcHJlZmVycmVkTGFuZ3MgPSBhcnJheSggICk7DQogICAgICAgICAgICBmb3JlYWNoKCAkYXJyTGFuZyBhcyAkc3RyTGFuZyApIA0KICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICRhcnJMYW5nRGV0YWlscyA9IGV4cGxvZGUoIjtxPSIsICRzdHJMYW5nKTsNCiAgICAgICAgICAgICAgICAkbGFuZ0NvZGUgPSBzdHJfcmVwbGFjZSgiLSIsICJfIiwgJGFyckxhbmdEZXRhaWxzWzBdKTsNCiAgICAgICAgICAgICAgICAkbGFuZ1ByaW9yaXR5ID0gZmxvYXR2YWwoKGlzc2V0KCRhcnJMYW5nRGV0YWlsc1sxXSkgPyAkYXJyTGFuZ0RldGFpbHNbMV0gOiAxKSk7DQogICAgICAgICAgICAgICAgJHByZWZlcnJlZExhbmdzWyRsYW5nQ29kZV0gPSAkbGFuZ1ByaW9yaXR5Ow0KICAgICAgICAgICAgfQ0KICAgICAgICAgICAgYXJzb3J0KCRwcmVmZXJyZWRMYW5ncyk7DQogICAgICAgICAgICByZXNldCgkcHJlZmVycmVkTGFuZ3MpOw0KICAgICAgICAgICAgJHVzZXJMb2NhbGUgPSBrZXkoJHByZWZlcnJlZExhbmdzKTsNCiAgICAgICAgfQ0KDQogICAgICAgIHJldHVybiAoaXNfbnVsbCgkdXNlckxvY2FsZSkgPyAkZmFsbGJhY2tMb2NhbGUgOiAkdXNlckxvY2FsZSk7DQogICAgfQ0KDQp9DQoNCg0KY2xhc3MgVXRpbHMNCnsNCiAgICAvKioNCiAgICAgICAgICogTGVuZ3RoIG9mIHRoZSBmaWxlIGluIFNlY29uZHMuDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJGZpbGUNCiAgICAgICAgICogICAgICAgICAgICAgICAgICAgICBwYXRoIHRvIHRoZSBmaWxlIG5hbWUNCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBpbnQNCiAgICAgICAgICogICAgICAgICAgICAgbGVuZ3RoIG9mIHRoZSBmaWxlIGluIHNlY29uZHMNCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgc3RhdGljIGZ1bmN0aW9uIGdldFNlY29uZHMoJGZpbGUpDQogICAgew0KICAgICAgICAkZmZtcGVnID0gc2VsZjo6Y2hlY2tGRk1QRUcoKTsNCiAgICAgICAgaWYoICRmZm1wZWcgKSANCiAgICAgICAgew0KICAgICAgICAgICAgJHRpbWUgPSBleGVjKChzdHJpbmcpICRmZm1wZWcgLiAiIC1pICIgLiAkZmlsZSAuICIgMj4mMSB8IGdyZXAgJ0R1cmF0aW9uJyB8IGN1dCAtZCAnICcgLWYgNCIpOw0KICAgICAgICAgICAgJGR1cmF0aW9uID0gZXhwbG9kZSgiOiIsICR0aW1lKTsNCiAgICAgICAgICAgICRzZWNvbmRzID0gJGR1cmF0aW9uWzBdICogMzYwMCArICRkdXJhdGlvblsxXSAqIDYwICsgcm91bmQoJGR1cmF0aW9uWzJdKTsNCiAgICAgICAgICAgIHJldHVybiAkc2Vjb25kczsNCiAgICAgICAgfQ0KDQogICAgICAgIHJldHVybiBtdF9yYW5kKDE1LCAzMDApOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBDaGVjayBmb3IgZmZtcGVnL2F2Y29udiBkZXBlbmRlbmNpZXMuDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gc3RyaW5nL2Jvb2xlYW4NCiAgICAgICAgICogICAgICAgICAgICAgICAgICAgICAgICBuYW1lIG9mIHRoZSBsaWJyYXJ5IGlmIHByZXNlbnQsIGZhbHNlIG90aGVyd2lzZQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBzdGF0aWMgZnVuY3Rpb24gY2hlY2tGRk1QRUcoKQ0KICAgIHsNCiAgICAgICAgQGV4ZWMoImZmbXBlZyAtdmVyc2lvbiAyPiYxIiwgJG91dHB1dCwgJHJldHVybnZhbHVlKTsNCiAgICAgICAgaWYoICRyZXR1cm52YWx1ZSA9PT0gMCApIA0KICAgICAgICB7DQogICAgICAgICAgICByZXR1cm4gImZmbXBlZyI7DQogICAgICAgIH0NCg0KICAgICAgICBAZXhlYygiYXZjb252IC12ZXJzaW9uIDI+JjEiLCAkb3V0cHV0LCAkcmV0dXJudmFsdWUpOw0KICAgICAgICBpZiggJHJldHVybnZhbHVlID09PSAwICkgDQogICAgICAgIHsNCiAgICAgICAgICAgIHJldHVybiAiYXZjb252IjsNCiAgICAgICAgfQ0KDQogICAgICAgIHJldHVybiBmYWxzZTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogQ3JlYXRpbmcgYSB2aWRlbyBpY29uL3RodW1ibmFpbC4NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkZmlsZQ0KICAgICAgICAgKiAgICAgICAgICAgICAgICAgICAgIHBhdGggdG8gdGhlIHZpZGVvIGZpbGUNCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBpbWFnZQ0KICAgICAgICAgKiAgICAgICAgICAgICAgIGljb24vdGh1bWJuYWlsIGZvciB0aGUgdmlkZW8NCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgc3RhdGljIGZ1bmN0aW9uIGNyZWF0ZVZpZGVvSWNvbigkZmlsZSkNCiAgICB7DQogICAgICAgICRmZm1wZWcgPSBzZWxmOjpjaGVja0ZGTVBFRygpOw0KICAgICAgICBpZiggJGZmbXBlZyApIA0KICAgICAgICB7DQogICAgICAgICAgICAkcHJldmlldyA9IHN5c19nZXRfdGVtcF9kaXIoKSAuICIvIiAuIG1kNSgkZmlsZSkgLiAiLmpwZyI7DQogICAgICAgICAgICBAdW5saW5rKCRwcmV2aWV3KTsNCiAgICAgICAgICAgICRjb21tYW5kID0gJGZmbXBlZyAuICIgLWkgXCIiIC4gJGZpbGUgLiAiXCIgLWYgbWpwZWcgLXNzIDAwOjAwOjAxIC12ZnJhbWVzIDEgXCIiIC4gJHByZXZpZXcgLiAiXCIgMj4mMSI7DQogICAgICAgICAgICBAZXhlYygkY29tbWFuZCk7DQogICAgICAgICAgICByZXR1cm4gZmlsZV9nZXRfY29udGVudHMoJHByZXZpZXcpOw0KICAgICAgICB9DQoNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogSW1wbGVtZW50cyB0aGUgYWN0dWFsIGxvZ2ljIGJlaGluZCBjcmVhdGluZyB0aGUgaWNvbi90aHVtYm5haWwuDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJGZpbGUNCiAgICAgICAgICogICAgICAgICAgICAgICAgICAgICBwYXRoIHRvIHRoZSBmaWxlIG5hbWUNCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBpbWFnZQ0KICAgICAgICAgKiAgICAgICAgICAgICAgIGljb24vdGh1bWJuYWlsIGZvciB0aGUgdmlkZW8NCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgc3RhdGljIGZ1bmN0aW9uIGNyZWF0ZUljb25HRCgkZmlsZSwgJHNpemUgPSAxMDAsICRyYXcgPSB0cnVlKQ0KICAgIHsNCiAgICAgICAgbGlzdCgkd2lkdGgsICRoZWlnaHQpID0gZ2V0aW1hZ2VzaXplKCRmaWxlKTsNCiAgICAgICAgaWYoICRoZWlnaHQgPCAkd2lkdGggKSANCiAgICAgICAgew0KICAgICAgICAgICAgJHkgPSAwOw0KICAgICAgICAgICAgJHggPSAoJHdpZHRoIC0gJGhlaWdodCkgLyAyOw0KICAgICAgICAgICAgJHNtYWxsZXN0U2lkZSA9ICRoZWlnaHQ7DQogICAgICAgIH0NCiAgICAgICAgZWxzZQ0KICAgICAgICB7DQogICAgICAgICAgICAkeCA9IDA7DQogICAgICAgICAgICAkeSA9ICgkaGVpZ2h0IC0gJHdpZHRoKSAvIDI7DQogICAgICAgICAgICAkc21hbGxlc3RTaWRlID0gJHdpZHRoOw0KICAgICAgICB9DQoNCiAgICAgICAgJGltYWdlX3AgPSBpbWFnZWNyZWF0ZXRydWVjb2xvcigkc2l6ZSwgJHNpemUpOw0KICAgICAgICAkaW1hZ2UgPSBpbWFnZWNyZWF0ZWZyb21zdHJpbmcoZmlsZV9nZXRfY29udGVudHMoJGZpbGUpKTsNCiAgICAgICAgaW1hZ2Vjb3B5cmVzYW1wbGVkKCRpbWFnZV9wLCAkaW1hZ2UsIDAsIDAsICR4LCAkeSwgJHNpemUsICRzaXplLCAkc21hbGxlc3RTaWRlLCAkc21hbGxlc3RTaWRlKTsNCiAgICAgICAgb2Jfc3RhcnQoKTsNCiAgICAgICAgaW1hZ2VqcGVnKCRpbWFnZV9wLCBOVUxMLCA5NSk7DQogICAgICAgICRpID0gb2JfZ2V0X2NvbnRlbnRzKCk7DQogICAgICAgIG9iX2VuZF9jbGVhbigpOw0KICAgICAgICBpbWFnZWRlc3Ryb3koJGltYWdlKTsNCiAgICAgICAgaW1hZ2VkZXN0cm95KCRpbWFnZV9wKTsNCiAgICAgICAgcmV0dXJuICRpOw0KICAgIH0NCg0KICAgIHB1YmxpYyBzdGF0aWMgZnVuY3Rpb24gZm9ybWF0Qnl0ZXMoJGJ5dGVzLCAkcHJlY2lzaW9uID0gMikNCiAgICB7DQogICAgICAgICR1bml0cyA9IGFycmF5KCAiQiIsICJrQiIsICJtQiIsICJnQiIsICJ0QiIgKTsNCiAgICAgICAgJGJ5dGVzID0gbWF4KCRieXRlcywgMCk7DQogICAgICAgICRwb3cgPSBmbG9vcigoKCRieXRlcyA/IGxvZygkYnl0ZXMpIDogMCkpIC8gbG9nKDEwMjQpKTsNCiAgICAgICAgJHBvdyA9IG1pbigkcG93LCBjb3VudCgkdW5pdHMpIC0gMSk7DQogICAgICAgICRieXRlcyAvPSBwb3coMTAyNCwgJHBvdyk7DQogICAgICAgIHJldHVybiByb3VuZCgkYnl0ZXMsICRwcmVjaXNpb24pIC4gIiIgLiAkdW5pdHNbJHBvd107DQogICAgfQ0KDQogICAgcHVibGljIHN0YXRpYyBmdW5jdGlvbiBjb2xvdXJlZFN0cmluZygkc3RyaW5nLCAkY29sb3VyKQ0KICAgIHsNCiAgICAgICAgJGNvbG91cnNbImJsYWNrIl0gPSAiMDszMCI7DQogICAgICAgICRjb2xvdXJzWyJkYXJrX2dyYXkiXSA9ICIxOzMwIjsNCiAgICAgICAgJGNvbG91cnNbImJsdWUiXSA9ICIwOzM0IjsNCiAgICAgICAgJGNvbG91cnNbImxpZ2h0X2JsdWUiXSA9ICIxOzM0IjsNCiAgICAgICAgJGNvbG91cnNbImdyZWVuIl0gPSAiMDszMiI7DQogICAgICAgICRjb2xvdXJzWyJsaWdodF9ncmVlbiJdID0gIjE7MzIiOw0KICAgICAgICAkY29sb3Vyc1siY3lhbiJdID0gIjA7MzYiOw0KICAgICAgICAkY29sb3Vyc1sibGlnaHRfY3lhbiJdID0gIjE7MzYiOw0KICAgICAgICAkY29sb3Vyc1sicmVkIl0gPSAiMDszMSI7DQogICAgICAgICRjb2xvdXJzWyJsaWdodF9yZWQiXSA9ICIxOzMxIjsNCiAgICAgICAgJGNvbG91cnNbInB1cnBsZSJdID0gIjA7MzUiOw0KICAgICAgICAkY29sb3Vyc1sibGlnaHRfcHVycGxlIl0gPSAiMTszNSI7DQogICAgICAgICRjb2xvdXJzWyJicm93biJdID0gIjA7MzMiOw0KICAgICAgICAkY29sb3Vyc1sieWVsbG93Il0gPSAiMTszMyI7DQogICAgICAgICRjb2xvdXJzWyJsaWdodF9ncmF5Il0gPSAiMDszNyI7DQogICAgICAgICRjb2xvdXJzWyJ3aGl0ZSJdID0gIjE7MzciOw0KICAgICAgICAkY29sb3JlZF9zdHJpbmcgPSAiIjsNCiAgICAgICAgaWYoIGlzc2V0KCRjb2xvdXJzWyRjb2xvdXJdKSApIA0KICAgICAgICB7DQogICAgICAgICAgICAkY29sb3JlZF9zdHJpbmcgLj0gIlx4MUJbIiAuICRjb2xvdXJzWyRjb2xvdXJdIC4gIm0iOw0KICAgICAgICB9DQoNCiAgICAgICAgJGNvbG9yZWRfc3RyaW5nIC49ICRzdHJpbmcgLiAiXHgxQlswbSI7DQogICAgICAgIHJldHVybiAkY29sb3JlZF9zdHJpbmc7DQogICAgfQ0KDQogICAgcHVibGljIHN0YXRpYyBmdW5jdGlvbiBnZXRGaWx0ZXJDb2RlKCRmaWx0ZXIpDQogICAgew0KICAgICAgICAkZmlsdGVycyA9IGFycmF5KCAgKTsNCiAgICAgICAgJGZpbHRlcnNbMTA4XSA9ICJDaGFybWVzIjsNCiAgICAgICAgJGZpbHRlcnNbMTE2XSA9ICJBc2hieSI7DQogICAgICAgICRmaWx0ZXJzWzExN10gPSAiSGVsZW5hIjsNCiAgICAgICAgJGZpbHRlcnNbMTE1XSA9ICJCcm9va2x5biI7DQogICAgICAgICRmaWx0ZXJzWzEwNV0gPSAiRG9ncGF0Y2giOw0KICAgICAgICAkZmlsdGVyc1sxMTNdID0gIlNreWxpbmUiOw0KICAgICAgICAkZmlsdGVyc1sxMDddID0gIkdpbnphIjsNCiAgICAgICAgJGZpbHRlcnNbMTE4XSA9ICJNYXZlbiI7DQogICAgICAgICRmaWx0ZXJzWzE2XSA9ICJLZWx2aW4iOw0KICAgICAgICAkZmlsdGVyc1sxNF0gPSAiMTk3NyI7DQogICAgICAgICRmaWx0ZXJzWzIwXSA9ICJXYWxkZW4iOw0KICAgICAgICAkZmlsdGVyc1sxOV0gPSAiVG9hc3RlciI7DQogICAgICAgICRmaWx0ZXJzWzE4XSA9ICJTdXRybyI7DQogICAgICAgICRmaWx0ZXJzWzIyXSA9ICJCcmFubmFuIjsNCiAgICAgICAgJGZpbHRlcnNbM10gPSAiRWFybHliaXJkIjsNCiAgICAgICAgJGZpbHRlcnNbMTA2XSA9ICJWZXNwZXIiOw0KICAgICAgICAkZmlsdGVyc1sxMDldID0gIlN0aW5zb24iOw0KICAgICAgICAkZmlsdGVyc1sxNV0gPSAiTmFzaHZpbGxlIjsNCiAgICAgICAgJGZpbHRlcnNbMjFdID0gIkhlZmUiOw0KICAgICAgICAkZmlsdGVyc1sxMF0gPSAiSW5rd2VsbCI7DQogICAgICAgICRmaWx0ZXJzWzJdID0gIkxvLUZpIjsNCiAgICAgICAgJGZpbHRlcnNbMjhdID0gIldpbGxvdyI7DQogICAgICAgICRmaWx0ZXJzWzI3XSA9ICJTaWVycmEiOw0KICAgICAgICAkZmlsdGVyc1sxXSA9ICJYIFBybyBJSSI7DQogICAgICAgICRmaWx0ZXJzWzI1XSA9ICJWYWxlbmNpYSI7DQogICAgICAgICRmaWx0ZXJzWzI2XSA9ICJIdWRzb24iOw0KICAgICAgICAkZmlsdGVyc1syM10gPSAiUmlzZSI7DQogICAgICAgICRmaWx0ZXJzWzE3XSA9ICJNYXlmYWlyIjsNCiAgICAgICAgJGZpbHRlcnNbMjRdID0gIkFtYXJvIjsNCiAgICAgICAgJGZpbHRlcnNbNjA4XSA9ICJQZXJwZXR1YSI7DQogICAgICAgICRmaWx0ZXJzWzYxMl0gPSAiQWRlbiI7DQogICAgICAgICRmaWx0ZXJzWzYwM10gPSAiTHVkd2lnIjsNCiAgICAgICAgJGZpbHRlcnNbNjE2XSA9ICJDcmVtYSI7DQogICAgICAgICRmaWx0ZXJzWzYwNV0gPSAiU2x1bWJlciI7DQogICAgICAgICRmaWx0ZXJzWzYxM10gPSAiSnVubyI7DQogICAgICAgICRmaWx0ZXJzWzYxNF0gPSAiUmV5ZXMiOw0KICAgICAgICAkZmlsdGVyc1s2MTVdID0gIkxhcmsiOw0KICAgICAgICAkZmlsdGVyc1sxMTFdID0gIk1vb24iOw0KICAgICAgICAkZmlsdGVyc1sxMTRdID0gIkdpbmdoYW0iOw0KICAgICAgICAkZmlsdGVyc1sxMTJdID0gIkNsYXJlbmRvbiI7DQogICAgICAgICRmaWx0ZXJzWzBdID0gIk5vcm1hbCI7DQogICAgICAgIHJldHVybiBhcnJheV9zZWFyY2goJGZpbHRlciwgJGZpbHRlcnMpOw0KICAgIH0NCg0KICAgIHB1YmxpYyBzdGF0aWMgZnVuY3Rpb24gY29va2llVG9BcnJheSgkc3RyaW5nLCAkZG9tYWluKQ0KICAgIHsNCiAgICAgICAgJGFyckNvb2tpZXMgPSBhcnJheSggICk7DQogICAgICAgICRmaWxlVmFscyA9IHNlbGY6OmV4dHJhY3RDb29raWVzKCRzdHJpbmcpOw0KICAgICAgICBmb3JlYWNoKCAkZmlsZVZhbHMgYXMgJGNvb2tpZSApIA0KICAgICAgICB7DQogICAgICAgICAgICBpZiggJGNvb2tpZVsiZG9tYWluIl0gPT0gJGRvbWFpbiApIA0KICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICRhcnJDb29raWVzWyRjb29raWVbIm5hbWUiXV0gPSAkY29va2llWyJ2YWx1ZSJdOw0KICAgICAgICAgICAgfQ0KDQogICAgICAgIH0NCiAgICAgICAgcmV0dXJuICRhcnJDb29raWVzOw0KICAgIH0NCg0KICAgIHB1YmxpYyBzdGF0aWMgZnVuY3Rpb24gZXh0cmFjdENvb2tpZXMoJHN0cmluZykNCiAgICB7DQogICAgICAgICRsaW5lcyA9IGV4cGxvZGUoUEhQX0VPTCwgJHN0cmluZyk7DQogICAgICAgICRjb29raWVzID0gYXJyYXkoICApOw0KICAgICAgICBmb3JlYWNoKCAkbGluZXMgYXMgJGxpbmUgKSANCiAgICAgICAgew0KICAgICAgICAgICAgJGNvb2tpZSA9IGFycmF5KCAgKTsNCiAgICAgICAgICAgIGlmKCBzdWJzdHIoJGxpbmUsIDAsIDEwKSA9PSAiI0h0dHBPbmx5XyIgKSANCiAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAkbGluZSA9IHN1YnN0cigkbGluZSwgMTApOw0KICAgICAgICAgICAgICAgICRjb29raWVbImh0dHBvbmx5Il0gPSB0cnVlOw0KICAgICAgICAgICAgfQ0KICAgICAgICAgICAgZWxzZQ0KICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICRjb29raWVbImh0dHBvbmx5Il0gPSBmYWxzZTsNCiAgICAgICAgICAgIH0NCg0KICAgICAgICAgICAgaWYoIHN1YnN0cigkbGluZSwgMCwgMSkgIT0gIiMiICYmIHN1YnN0cl9jb3VudCgkbGluZSwgIlx0IikgPT0gNiApIA0KICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICR0b2tlbnMgPSBleHBsb2RlKCJcdCIsICRsaW5lKTsNCiAgICAgICAgICAgICAgICAkdG9rZW5zID0gYXJyYXlfbWFwKCJ0cmltIiwgJHRva2Vucyk7DQogICAgICAgICAgICAgICAgbGlzdCgkY29va2llWyJkb21haW4iXSwgJGNvb2tpZVsiZmxhZyJdLCAkY29va2llWyJwYXRoIl0sICRjb29raWVbInNlY3VyZSJdLCAkY29va2llWyJleHBpcmF0aW9uLWVwb2NoIl0pID0gJHRva2VuczsNCiAgICAgICAgICAgICAgICAkY29va2llWyJuYW1lIl0gPSB1cmxkZWNvZGUoJHRva2Vuc1s1XSk7DQogICAgICAgICAgICAgICAgJGNvb2tpZVsidmFsdWUiXSA9IHVybGRlY29kZSgkdG9rZW5zWzZdKTsNCiAgICAgICAgICAgICAgICAkY29va2llWyJleHBpcmF0aW9uIl0gPSBkYXRlKCJZLW0tZCBoOmk6cyIsICR0b2tlbnNbNF0pOw0KICAgICAgICAgICAgICAgICRjb29raWVzW10gPSAkY29va2llOw0KICAgICAgICAgICAgfQ0KDQogICAgICAgIH0NCiAgICAgICAgcmV0dXJuICRjb29raWVzOw0KICAgIH0NCg0KfQ0KDQoNCmNsYXNzIFNldHRpbmdzDQp7DQogICAgcHJpdmF0ZSAkcGF0aCA9IE5VTEw7DQogICAgcHJpdmF0ZSAkc2V0cyA9IE5VTEw7DQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gX19jb25zdHJ1Y3QoJHBhdGgpDQogICAgew0KICAgICAgICAkdGhpcy0+cGF0aCA9ICRwYXRoOw0KICAgICAgICAkdGhpcy0+c2V0cyA9IGFycmF5KCAgKTsNCiAgICAgICAgaWYoIGZpbGVfZXhpc3RzKCRwYXRoKSApIA0KICAgICAgICB7DQogICAgICAgICAgICAkZnAgPSBmb3BlbigkcGF0aCwgInJiIik7DQogICAgICAgICAgICB3aGlsZSggJGxpbmUgPSBmZ2V0cygkZnAsIDIwNDgpICkgDQogICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgJGxpbmUgPSB0cmltKCRsaW5lLCAiICIpOw0KICAgICAgICAgICAgICAgIGlmKCAkbGluZVswXSA9PSAiIyIgKSANCiAgICAgICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgICAgIGNvbnRpbnVlOw0KICAgICAgICAgICAgICAgIH0NCg0KICAgICAgICAgICAgICAgICRrdiA9IGV4cGxvZGUoIj0iLCAkbGluZSwgMik7DQogICAgICAgICAgICAgICAgJHRoaXMtPnNldHNbJGt2WzBdXSA9IHRyaW0oJGt2WzFdLCAiXHJcbiAiKTsNCiAgICAgICAgICAgIH0NCiAgICAgICAgICAgIGZjbG9zZSgkZnApOw0KICAgICAgICB9DQoNCiAgICB9DQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gZ2V0KCRrZXksICRkZWZhdWx0ID0gTlVMTCkNCiAgICB7DQogICAgICAgIGlmKCAka2V5ID09ICJzZXRzIiApIA0KICAgICAgICB7DQogICAgICAgICAgICByZXR1cm4gJHRoaXMtPnNldHM7DQogICAgICAgIH0NCg0KICAgICAgICBpZiggaXNzZXQoJHRoaXMtPnNldHNbJGtleV0pICkgDQogICAgICAgIHsNCiAgICAgICAgICAgIHJldHVybiAkdGhpcy0+c2V0c1ska2V5XTsNCiAgICAgICAgfQ0KDQogICAgICAgIHJldHVybiAkZGVmYXVsdDsNCiAgICB9DQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gc2V0KCRrZXksICR2YWx1ZSkNCiAgICB7DQogICAgICAgIGlmKCAka2V5ID09ICJzZXRzIiApIA0KICAgICAgICB7DQogICAgICAgICAgICByZXR1cm4gTlVMTDsNCiAgICAgICAgfQ0KDQogICAgICAgICR0aGlzLT5zZXRzWyRrZXldID0gJHZhbHVlOw0KICAgICAgICAkdGhpcy0+U2F2ZSgpOw0KICAgIH0NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBTYXZlKCkNCiAgICB7DQogICAgICAgIGlmKCBmaWxlX2V4aXN0cygkdGhpcy0+cGF0aCkgKSANCiAgICAgICAgew0KICAgICAgICAgICAgdW5saW5rKCR0aGlzLT5wYXRoKTsNCiAgICAgICAgfQ0KDQogICAgICAgICRmcCA9IGZvcGVuKCR0aGlzLT5wYXRoLCAid2IiKTsNCiAgICAgICAgZnNlZWsoJGZwLCAwKTsNCiAgICAgICAgZm9yZWFjaCggJHRoaXMtPnNldHMgYXMgJGtleSA9PiAkdmFsdWUgKSANCiAgICAgICAgew0KICAgICAgICAgICAgZndyaXRlKCRmcCwgJGtleSAuICI9IiAuICR2YWx1ZSAuICJcbiIpOw0KICAgICAgICB9DQogICAgICAgIGZjbG9zZSgkZnApOw0KICAgIH0NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBfX3NldCgkcHJvcCwgJHZhbHVlKQ0KICAgIHsNCiAgICAgICAgJHRoaXMtPnNldCgkcHJvcCwgJHZhbHVlKTsNCiAgICB9DQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gX19nZXQoJHByb3ApDQogICAgew0KICAgICAgICByZXR1cm4gJHRoaXMtPmdldCgkcHJvcCk7DQogICAgfQ0KDQp9DQoNCg0KY2xhc3MgQ29uc3RhbnRzDQp7DQogICAgY29uc3QgQVBJX1VSTCA9ICJodHRwczovL2kuaW5zdGFncmFtLmNvbS9hcGkvdjEvIjsNCiAgICBjb25zdCBWRVJTSU9OID0gIjEwLjMuMiI7DQogICAgY29uc3QgSUdfU0lHX0tFWSA9ICI1YWQ3ZDZmMDEzNjY2Y2M5M2M4OGZjOGFmOTQwMzQ4YmQwNjdiNjhmMGRjZTNjODUxMjJhOTIzZjRmNzRiMjUxIjsNCiAgICBjb25zdCBFWFBFUklNRU5UUyA9ICJpZ19hbmRyb2lkX2FkX2hvbGRvdXRfMTZtNV91bml2ZXJzZSxpZ19hbmRyb2lkX3Byb2dyZXNzaXZlX2pwZWcsaWdfY3JlYXRpb25fZ3Jvd3RoX2hvbGRvdXQsaWdfYW5kcm9pZF9vcHBvX2FwcF9iYWRnaW5nLGlnX2FuZHJvaWRfYWRfcmVtb3ZlX3VzZXJuYW1lX2Zyb21fY2FwdGlvbl91bml2ZXJzZSxpZ19hbmRyb2lkX2VuYWJsZV9zaGFyZV90b193aGF0c2FwcCxpZ19hbmRyb2lkX2RpcmVjdF9kcmF3aW5nX2luX3F1aWNrX2NhbV91bml2ZXJzZSxpZ19hbmRyb2lkX2FkX2Fsd2F5c19zZW5kX2FkX2F0dHJpYnV0aW9uX2lkX3VuaXZlcnNlLGlnX2FuZHJvaWRfdW5pdmVyc2VfdmlkZW9fcHJvZHVjdGlvbixpZ19hbmRyb2lkX2RpcmVjdF9wbHVzX2J1dHRvbixpZ19hbmRyb2lkX2Fkc19oZWF0bWFwX292ZXJsYXlfdW5pdmVyc2UsaWdfYW5kcm9pZF9odHRwX3N0YWNrX2V4cGVyaW1lbnRfMjAxNixpZ19hbmRyb2lkX2luZmluaXRlX3Njcm9sbGluZyxpZ19mYm5zX2Jsb2NrZWQsaWdfYW5kcm9pZF9wb3N0X2F1dG9fcmV0cnlfdjdfMjEsaWdfZmJuc19wdXNoLGlnX2FuZHJvaWRfdmlkZW9fcGxheWJhY2tfYmFuZHdpZHRoX3RocmVzaG9sZCxpZ19hbmRyb2lkX2RpcmVjdF9saW5rX3ByZXZpZXcsaWdfYW5kcm9pZF9kaXJlY3RfdHlwaW5nX2luZGljYXRvcixpZ19hbmRyb2lkX3ByZXZpZXdfY2FwdHVyZSxpZ19hbmRyb2lkX2ZlZWRfcGlsbCxpZ19hbmRyb2lkX3Byb2ZpbGVfbGlua19pYWIsaWdfYW5kcm9pZF9zdG9yeV9jYXB0aW9uLGlnX2FuZHJvaWRfbmV0d29ya19jYW5jZWxsYXRpb24saWdfYW5kcm9pZF9oaXN0b2dyYW1fcmVwb3J0ZXIsaWdfYW5kcm9pZF9hbnJ3YXRjaGRvZyxpZ19hbmRyb2lkX3NlYXJjaF9jbGllbnRfbWF0Y2hpbmcsaWdfYW5kcm9pZF9mb2xsb3dfcmVxdWVzdF90ZXh0X2J1dHRvbnMsaWdfYW5kcm9pZF9mZWVkX3pvb20saWdfYW5kcm9pZF9kcmFmdHNfdW5pdmVyc2UsaWdfYW5kcm9pZF9kaXNhYmxlX2NvbW1lbnQsaWdfYW5kcm9pZF91c2VyX2RldGFpbF9lbmRwb2ludCxpZ19hbmRyb2lkX29zX3ZlcnNpb25fYmxvY2tpbmcsaWdfYW5kcm9pZF9ibG9ja2VkX2xpc3QsaWdfYW5kcm9pZF9ldmVudF9jcmVhdGlvbixpZ19hbmRyb2lkX2hpZ2hfcmVzX3VwbG9hZF8yLGlnX2FuZHJvaWRfMmZhYyxpZ19hbmRyb2lkX21hcmtfcmVlbF9zZWVuX29uX1N3aXBlX2ZvcndhcmQsaWdfYW5kcm9pZF9jb21tZW50X3JlZGVzaWduLGlnX2FuZHJvaWRfYWRfc3BvbnNvcmVkX2xhYmVsX3VuaXZlcnNlLGlnX2FuZHJvaWRfbWVudGlvbnNfZGlzbWlzc19ydWxlLGlnX2FuZHJvaWRfZGlzYWJsZV9jaHJvbWFfc3Vic2FtcGxpbmcsaWdfYW5kcm9pZF9zaGFyZV9zcGlubmVyLGlnX2FuZHJvaWRfdmlkZW9fcmV1c2Vfc3VyZmFjZSxpZ19leHBsb3JlX3YzX2FuZHJvaWRfdW5pdmVyc2UsaWdfYW5kcm9pZF9tZWRpYV9mYXZvcml0ZXMsaWdfYW5kcm9pZF9udXhfaG9sZG91dCxpZ19hbmRyb2lkX2luc3RhX3ZpZGVvX3VuaXZlcnNlLGlnX2FuZHJvaWRfc2VhcmNoX251bGxfc3RhdGUsaWdfYW5kcm9pZF91bml2ZXJzZV9yZWVsX3ZpZGVvX3Byb2R1Y3Rpb24sbGlnZXJfaW5zdGFncmFtX2FuZHJvaWRfdW5pdixpZ19hbmRyb2lkX2RpcmVjdF9lbW9qaV9waWNrZXIsaWdfZmVlZF9ob2xkb3V0X3VuaXZlcnNlLGlnX2FuZHJvaWRfZGlyZWN0X3NlbmRfYXV0b19yZXRyeV91bml2ZXJzZSxpZ19hbmRyb2lkX3NhbXN1bmdfYXBwX2JhZGdpbmcsaWdfYW5kcm9pZF9kaXNrX3VzYWdlLGlnX2FuZHJvaWRfYnVzaW5lc3NfcHJvbW90aW9uLGlnX2FuZHJvaWRfZGlyZWN0X3N3aXBlX3RvX2luYm94LGlnX2FuZHJvaWRfZmVlZF9yZXNoYXJlX2J1dHRvbl9udXgsaWdfYW5kcm9pZF9yZWFjdF9uYXRpdmVfYm9vc3RfcG9zdCxpZ19hbmRyb2lkX2Jvb21lcmFuZ19mZWVkX2F0dHJpYnV0aW9uLGlnX2ZibnNfc2hhcmVkLGlnX2ZibnNfZHVtcF9pZHMsaWdfYW5kcm9pZF9yZWFjdF9uYXRpdmVfdW5pdmVyc2UsaWdfc2hvd19wcm9tb3RlX2J1dHRvbl9pbl9mZWVkLGlnX2FuZHJvaWRfYWRfbWV0YWRhdGFfYmVoYXZpb3JfdW5pdmVyc2UsaWdfYW5kcm9pZF92aWRlb19sb29wY291bnRfaW50LGlnX2FuZHJvaWRfaW5saW5lX2dhbGxlcnlfYmFja29mZl9ob3Vyc191bml2ZXJzZSxpZ19hbmRyb2lkX3JlbmRlcmluZ19jb250cm9scyxpZ19hbmRyb2lkX3Byb2ZpbGVfcGhvdG9fYXNfbWVkaWEsaWdfYW5kcm9pZF9hc3luY19zdGFja19pbWFnZV9jYWNoZSxpZ192aWRlb19tYXhfZHVyYXRpb25fcWVfcHJldW5pdmVyc2UsaWdfdmlkZW9fY29weXJpZ2h0X3doaXRlbGlzdCxpZ19hbmRyb2lkX3JlbmRlcl9zdG9yaWVzX3dpdGhfY29udGVudF9vdmVycmlkZSxpZ19hbmRyb2lkX2FkX2ludGVudF90b19oaWdobGlnaHRfdW5pdmVyc2UsaWdfYW5kcm9pZF9zd2lwZV9uYXZpZ2F0aW9uX3hfYW5nbGVfdW5pdmVyc2UsaWdfYW5kcm9pZF9kaXNhYmxlX2NvbW1lbnRfcHVibGljX3Rlc3QsaWdfYW5kcm9pZF9wcm9maWxlLGlnX2FuZHJvaWRfZGlyZWN0X2JsdWVfdGFiLGlnX2FuZHJvaWRfZW5hYmxlX3NoYXJlX3RvX21lc3NlbmdlcixpZ19hbmRyb2lkX2ZldGNoX3JlZWxfdHJheV9vbl9yZXN1bWVfdW5pdmVyc2UsaWdfYW5kcm9pZF9wcm9tb3RlX2FnYWluLGlnX2ZlZWRfZXZlbnRfbGFuZGluZ19wYWdlX2NoYW5uZWwsaWdfcmFua2luZ19mb2xsb3dpbmcsaWdfYW5kcm9pZF9wZW5kaW5nX3JlcXVlc3Rfc2VhcmNoX2JhcixpZ19hbmRyb2lkX2ZlZWRfdWZpX3JlZGVzaWduLGlnX2FuZHJvaWRfcGVuZGluZ19lZGl0c19kaWFsb2dfdW5pdmVyc2UsaWdfYW5kcm9pZF9idXNpbmVzc19jb252ZXJzaW9uX2Zsb3dfdW5pdmVyc2UsaWdfYW5kcm9pZF9zaG93X3lvdXJfc3Rvcnlfd2hlbl9lbXB0eV91bml2ZXJzZSxpZ19hbmRyb2lkX2FkX2Ryb3BfY29va2llX2Vhcmx5LGlnX2FuZHJvaWRfYXBwX3N0YXJ0X2NvbmZpZyxpZ19hbmRyb2lkX2ZpeF9pc2VfdHdvX3BoYXNlLGlnX2FuZHJvaWRfcHBhZ2VfdG9nZ2xlX3VuaXZlcnNlLGlnX2FuZHJvaWRfcGJpYV9ub3JtYWxfd2VpZ2h0X3VuaXZlcnNlLGlnX2FuZHJvaWRfcHJvZmFuaXR5X2ZpbHRlcixpZ19pb3Nfc3VfYWN0aXZpdHlfZmVlZCxpZ19hbmRyb2lkX3NlYXJjaCxpZ19hbmRyb2lkX2Jvb21lcmFuZ19lbnRyeSxpZ19hbmRyb2lkX211dGVfc3RvcnksaWdfYW5kcm9pZF9pbmxpbmVfZ2FsbGVyeV91bml2ZXJzZSxpZ19hbmRyb2lkX2FkX3JlbW92ZV9vbmVfdGFwX2luZGljYXRvcl91bml2ZXJzZSxpZ19hbmRyb2lkX3ZpZXdfY291bnRfZGVjb3VwbGVfbGlrZXNfdW5pdmVyc2UsaWdfYW5kcm9pZF9jb250YWN0X2J1dHRvbl9yZWRlc2lnbl92MixpZ19hbmRyb2lkX3BlcmlvZGljX2FuYWx5dGljc191cGxvYWRfdjIsaWdfYW5kcm9pZF9zZW5kX2RpcmVjdF90eXBpbmdfaW5kaWNhdG9yLGlnX2FuZHJvaWRfYWRfaG9sZG91dF8xNmgybTFfdW5pdmVyc2UsaWdfYW5kcm9pZF9yZWFjdF9uYXRpdmVfY29tbWVudF9tb2RlcmF0aW9uX3NldHRpbmdzLGlnX3ZpZGVvX3VzZV9zdmVfdW5pdmVyc2UsaWdfYW5kcm9pZF9pbmxpbmVfZ2FsbGVyeV9ub19iYWNrb2ZmX29uX2xhdW5jaF91bml2ZXJzZSxpZ19hbmRyb2lkX2ltbWVyc2l2ZV92aWV3ZXIsaWdfYW5kcm9pZF9kaXNjb3Zlcl9wZW9wbGVfaWNvbixpZ19hbmRyb2lkX3Byb2ZpbGVfZm9sbG93X2JhY2tfYnV0dG9uLGlzX2FuZHJvaWRfZmVlZF9zZWVuX3N0YXRlLGlnX2FuZHJvaWRfZGVuc2VfZmVlZF91bml0X2NhcmRzLGlnX2FuZHJvaWRfZHJhZnRzX3ZpZGVvX3VuaXZlcnNlLGlnX2FuZHJvaWRfZXhvcGxheWVyLGlnX2FuZHJvaWRfYWRkX3RvX2xhc3RfcG9zdCxpZ19hbmRyb2lkX2FkX3JlbW92ZV9jdGFfY2hldnJvbl91bml2ZXJzZSxpZ19hbmRyb2lkX2FkX2NvbW1lbnRfY3RhX3VuaXZlcnNlLGlnX2FuZHJvaWRfc2VhcmNoX2V2ZW50X2ljb24saWdfYW5kcm9pZF9jaGFubmVsc19ob21lLGlnX2FuZHJvaWRfZmVlZCxpZ19hbmRyb2lkX2R2Ml9yZWFsdGltZV9wcml2YXRlX3NoYXJlLGlnX2FuZHJvaWRfbm9uX3NxdWFyZV9maXJzdCxpZ19hbmRyb2lkX3ZpZGVvX2ludGVybGVhdmVkX3YyLGlnX2FuZHJvaWRfdmlkZW9fY2FjaGVfcG9saWN5LGlnX2FuZHJvaWRfcmVhY3RfbmF0aXZlX3VuaXZlcnNlX2tpbGxfc3dpdGNoLGlnX2FuZHJvaWRfdmlkZW9fY2FwdGlvbnNfdW5pdmVyc2UsaWdfYW5kcm9pZF9mb2xsb3dfc2VhcmNoX2JhcixpZ19hbmRyb2lkX2xhc3RfZWRpdHMsaWdfYW5kcm9pZF90d29fc3RlcF9jYXB0dXJlX2Zsb3csaWdfYW5kcm9pZF92aWRlb19kb3dubG9hZF9sb2dnaW5nLGlnX2FuZHJvaWRfc2hhcmVfbGlua190b193aGF0c2FwcCxpZ19hbmRyb2lkX2ZhY2Vib29rX3R3aXR0ZXJfcHJvZmlsZV9waG90b3MsaWdfYW5kcm9pZF9zd2lwZWFibGVfZmlsdGVyc19ibGFja2xpc3QsaWdfYW5kcm9pZF9hZF9wYmlhX3Byb2ZpbGVfdGFwX3VuaXZlcnNlLGlnX2FuZHJvaWRfdXNlX3NvZnR3YXJlX2xheWVyX2Zvcl9rY19kcmF3aW5nX3VuaXZlcnNlLGlnX2FuZHJvaWRfcmVhY3RfbmF0aXZlX290YSxpZ19hbmRyb2lkX2RpcmVjdF9tdXR1YWxseV9leGNsdXNpdmVfZXhwZXJpbWVudF91bml2ZXJzZSxpZ19hbmRyb2lkX2ZvbGxvd2luZ19mb2xsb3dlcl9zb2NpYWxfY29udGV4dCI7DQogICAgY29uc3QgTE9HSU5fRVhQRVJJTUVOVFMgPSAiaWdfYW5kcm9pZF9yZWdfbG9naW5fYnRuX2FjdGl2ZV9zdGF0ZSxpZ19hbmRyb2lkX2NpX29wdF9pbl9hdF9yZWcsaWdfYW5kcm9pZF9vbmVfY2xpY2tfaW5fb2xkX2Zsb3csaWdfYW5kcm9pZF9tZXJnZV9mYl9hbmRfY2lfZnJpZW5kc19wYWdlLGlnX2FuZHJvaWRfbm9uX2ZiX3NzbyxpZ19hbmRyb2lkX21hbmRhdG9yeV9mdWxsX25hbWUsaWdfYW5kcm9pZF9yZWdfZW5hYmxlX2xvZ2luX3Bhc3N3b3JkX2J0bixpZ19hbmRyb2lkX3JlZ19waG9uZV9lbWFpbF9hY3RpdmVfc3RhdGUsaWdfYW5kcm9pZF9hbmFseXRpY3NfZGF0YV9sb3NzLGlnX2ZibnNfYmxvY2tlZCxpZ19hbmRyb2lkX2NvbnRhY3RfcG9pbnRfdHJpYWdlLGlnX2FuZHJvaWRfcmVnX25leHRfYnRuX2FjdGl2ZV9zdGF0ZSxpZ19hbmRyb2lkX3ByZWZpbGxfcGhvbmVfbnVtYmVyLGlnX2FuZHJvaWRfc2hvd19mYl9zb2NpYWxfY29udGV4dF9pbl9udXgsaWdfYW5kcm9pZF9vbmVfdGFwX2xvZ2luX3Vwc2VsbCxpZ19mYm5zX3B1c2gsaWdfYW5kcm9pZF9waG9uZWlkX3N5bmNfaW50ZXJ2YWwiOw0KICAgIGNvbnN0IFNJR19LRVlfVkVSU0lPTiA9ICI0IjsNCiAgICBjb25zdCBYX0lHX0NhcGFiaWxpdGllcyA9ICIzVG9BQUE9PSI7DQogICAgY29uc3QgQU5EUk9JRF9WRVJTSU9OID0gMTg7DQogICAgY29uc3QgQU5EUk9JRF9SRUxFQVNFID0gIjQuMyI7DQogICAgY29uc3QgV0VCX1VSTCA9ICJodHRwczovL3d3dy5pbnN0YWdyYW0uY29tLyI7DQoNCn0NCg0KDQpjbGFzcyBCdWxrUmVhY3Rpb24NCnsNCi8qKg0KICAgICAgICAgKiBAdmFyIGFycmF5ICR1c2Vycw0KICAgICAgICAgKi8NCiAgICBwcm90ZWN0ZWQgJHVzZXJzID0gYXJyYXkoICApOw0KLyoqDQogICAgICAgICAqIEB2YXIgaW50ICRzaW11bHRhbmVwb3N0c2l6ZQ0KICAgICAgICAgKi8NCiAgICBwcm90ZWN0ZWQgJHNpbXVsdGFuZXBvc3RzaXplID0gTlVMTDsNCi8qKg0KICAgICAgICAgKiBAdmFyIHN0cmluZw0KICAgICAgICAgKi8NCiAgICBwcm90ZWN0ZWQgJElHRGF0YVBhdGggPSBOVUxMOw0KDQogICAgLyoqDQogICAgICAgICAqIEJ1bGtSZWFjdGlvbiBjb25zdHJ1Y3Rvci4NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIGFycmF5ICAkdXNlcnMNCiAgICAgICAgICogQHBhcmFtIGludCAgICAkc2ltdWx0YW5lcG9zdHNpemUNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkSUdEYXRhUGF0aA0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBfX2NvbnN0cnVjdCgkdXNlcnMsICRzaW11bHRhbmVwb3N0c2l6ZSA9IDEwMCwgJElHRGF0YVBhdGggPSBOVUxMKQ0KICAgIHsNCiAgICAgICAgJHRoaXMtPnNpbXVsdGFuZXBvc3RzaXplID0gJHNpbXVsdGFuZXBvc3RzaXplOw0KICAgICAgICBpZiggIWlzX251bGwoJElHRGF0YVBhdGgpICkgDQogICAgICAgIHsNCiAgICAgICAgICAgICR0aGlzLT5JR0RhdGFQYXRoID0gJElHRGF0YVBhdGg7DQogICAgICAgIH0NCiAgICAgICAgZWxzZQ0KICAgICAgICB7DQogICAgICAgICAgICAkdGhpcy0+SUdEYXRhUGF0aCA9IFdvdzo6Z2V0KCJwcm9qZWN0L2Nvb2tpZVBhdGgiKSAuICJpbnN0YWdyYW0vIjsNCiAgICAgICAgfQ0KDQogICAgICAgIGZvcmVhY2goICR1c2VycyBhcyAkdXNlciApIA0KICAgICAgICB7DQogICAgICAgICAgICAkdGhpcy0+dXNlcnNbXSA9IGFycmF5KCAiZGF0YSIgPT4gJHVzZXIsICJvYmplY3QiID0+ICgkdXNlclsiaXNXZWJDb29raWUiXSA9PSAxID8gbmV3IEluc3RhZ3JhbVdlYigkdXNlclsia3VsbGFuaWNpQWRpIl0pIDogbmV3IEluc3RhZ3JhbSgkdXNlclsia3VsbGFuaWNpQWRpIl0sICR1c2VyWyJzaWZyZSJdKSkgKTsNCiAgICAgICAgfQ0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICAkbWVkaWFJRA0KICAgICAgICAgKiBAcGFyYW0gIHN0cmluZyAkbWVkaWFDb2RlDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gbGlrZSgkbWVkaWFJRCwgJG1lZGlhQ29kZSkNCiAgICB7DQogICAgICAgICR0b3RhbFN1Y2Nlc3NDb3VudCA9IDA7DQogICAgICAgICR0cmllZFVzZXJzID0gYXJyYXkoICApOw0KICAgICAgICAkcm9sbGluZ0N1cmwgPSBuZXcgUm9sbGluZ0N1cmxcUm9sbGluZ0N1cmwoKTsNCiAgICAgICAgJGFyck1lZGlhSUQgPSBleHBsb2RlKCJfIiwgJG1lZGlhSUQpOw0KICAgICAgICAkbWVkaWFJREJlZm9yZXIgPSAkYXJyTWVkaWFJRFswXTsNCiAgICAgICAgZm9yZWFjaCggJHRoaXMtPnVzZXJzIGFzICR1c2VyICkgDQogICAgICAgIHsNCiAgICAgICAgICAgIGlmKCAkdXNlclsiZGF0YSJdWyJpc1dlYkNvb2tpZSJdID09IDEgKSANCiAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAkb2JqSW5zdGFncmFtV2ViID0gJHVzZXJbIm9iamVjdCJdOw0KICAgICAgICAgICAgICAgICRvYmpEYXRhID0gJG9iakluc3RhZ3JhbVdlYi0+Z2V0RGF0YSgpOw0KICAgICAgICAgICAgICAgICRoZWFkZXJzID0gYXJyYXkoICJSZWZlcmVyOiBodHRwczovL3d3dy5pbnN0YWdyYW0uY29tL2luc3RhZ3JhbS8iLCAiRE5UOiAxIiwgIk9yaWdpbjogaHR0cHM6Ly93d3cuaW5zdGFncmFtLmNvbS8iLCAiWC1DU1JGVG9rZW46ICIgLiB0cmltKCRvYmpEYXRhWyJ0b2tlbiJdKSwgIlgtUmVxdWVzdGVkLVdpdGg6IFhNTEh0dHBSZXF1ZXN0IiwgIlgtSW5zdGFncmFtLUFKQVg6IDEiLCAiQ29ubmVjdGlvbjogY2xvc2UiLCAiQ2FjaGUtQ29udHJvbDogbWF4LWFnZT0wIiwgIkFjY2VwdDogKi8qIiwgIkFjY2VwdC1MYW5ndWFnZTogdHI7cT0xIiwgIlhfRk9SV0FSREVEX0ZPUjogIiAuICRvYmpEYXRhWyJpcCJdLCAiUkVNT1RFX0FERFI6ICIgLiAkb2JqRGF0YVsiaXAiXSApOw0KICAgICAgICAgICAgICAgICRvcHRpb25zID0gYXJyYXkoIENVUkxPUFRfVVNFUkFHRU5UID0+ICRvYmpEYXRhWyJ1c2VyX2FnZW50Il0sIENVUkxPUFRfUkVUVVJOVFJBTlNGRVIgPT4gdHJ1ZSwgQ1VSTE9QVF9GT0xMT1dMT0NBVElPTiA9PiB0cnVlLCBDVVJMT1BUX1ZFUkJPU0UgPT4gZmFsc2UsIENVUkxPUFRfU1NMX1ZFUklGWVBFRVIgPT4gZmFsc2UsIENVUkxPUFRfU1NMX1ZFUklGWUhPU1QgPT4gZmFsc2UsIENVUkxPUFRfQ09PS0lFRklMRSA9PiAkdGhpcy0+SUdEYXRhUGF0aCAuICRvYmpEYXRhWyJ1c2VybmFtZSJdIC4gIi5kYXQiLCBDVVJMT1BUX0NPT0tJRUpBUiA9PiAkdGhpcy0+SUdEYXRhUGF0aCAuICRvYmpEYXRhWyJ1c2VybmFtZSJdIC4gIi5kYXQiICk7DQogICAgICAgICAgICAgICAgJHJvbGxpbmdDdXJsLT5wb3N0KENvbnN0YW50czo6V0VCX1VSTCAuICJ3ZWIvbGlrZXMvIiAuICRtZWRpYUlEQmVmb3JlciAuICIvbGlrZS8iLCBOVUxMLCAkaGVhZGVycywgJG9wdGlvbnMsICR1c2VyWyJkYXRhIl0pOw0KICAgICAgICAgICAgfQ0KICAgICAgICAgICAgZWxzZQ0KICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICRvYmpJbnN0YWdyYW0gPSAkdXNlclsib2JqZWN0Il07DQogICAgICAgICAgICAgICAgJG9iakRhdGEgPSAkb2JqSW5zdGFncmFtLT5nZXREYXRhKCk7DQogICAgICAgICAgICAgICAgJGRhdGEgPSBqc29uX2VuY29kZShhcnJheSggIl91dWlkIiA9PiAkb2JqRGF0YVsidXVpZCJdLCAiX3VpZCIgPT4gJG9iakRhdGFbInVzZXJuYW1lX2lkIl0sICJfY3NyZnRva2VuIiA9PiAkb2JqRGF0YVsidG9rZW4iXSwgIm1lZGlhX2lkIiA9PiAkbWVkaWFJRCApKTsNCiAgICAgICAgICAgICAgICAkaGVhZGVycyA9IGFycmF5KCAiQ29ubmVjdGlvbjogY2xvc2UiLCAiQWNjZXB0OiAqLyoiLCAiWC1JRy1DYXBhYmlsaXRpZXM6ICIgLiBDb25zdGFudHM6OlhfSUdfQ2FwYWJpbGl0aWVzLCAiWC1JRy1Db25uZWN0aW9uLVR5cGU6IFdJRkkiLCAiQ29udGVudC10eXBlOiBhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQ7IGNoYXJzZXQ9VVRGLTgiLCAiQWNjZXB0LUxhbmd1YWdlOiB0ci1UUiIsICJYX0ZPUldBUkRFRF9GT1I6ICIgLiAkb2JqRGF0YVsiaXAiXSwgIlJFTU9URV9BRERSOiAiIC4gJG9iakRhdGFbImlwIl0gKTsNCiAgICAgICAgICAgICAgICAkb3B0aW9ucyA9IGFycmF5KCBDVVJMT1BUX1VTRVJBR0VOVCA9PiAkb2JqRGF0YVsidXNlcl9hZ2VudCJdLCBDVVJMT1BUX1JFVFVSTlRSQU5TRkVSID0+IHRydWUsIENVUkxPUFRfRk9MTE9XTE9DQVRJT04gPT4gdHJ1ZSwgQ1VSTE9QVF9WRVJCT1NFID0+IGZhbHNlLCBDVVJMT1BUX1NTTF9WRVJJRllQRUVSID0+IGZhbHNlLCBDVVJMT1BUX1NTTF9WRVJJRllIT1NUID0+IGZhbHNlLCBDVVJMT1BUX0NPT0tJRUZJTEUgPT4gJHRoaXMtPklHRGF0YVBhdGggLiAkb2JqRGF0YVsidXNlcm5hbWUiXSAuICIuZGF0IiwgQ1VSTE9QVF9DT09LSUVKQVIgPT4gJHRoaXMtPklHRGF0YVBhdGggLiAkb2JqRGF0YVsidXNlcm5hbWUiXSAuICIuZGF0IiApOw0KICAgICAgICAgICAgICAgICRyb2xsaW5nQ3VybC0+cG9zdChDb25zdGFudHM6OkFQSV9VUkwgLiAibWVkaWEvIiAuICRtZWRpYUlEIC4gIi9saWtlLyIsIFNpZ25hdHVyZVV0aWxzOjpnZW5lcmF0ZVNpZ25hdHVyZSgkZGF0YSksICRoZWFkZXJzLCAkb3B0aW9ucywgJHVzZXJbImRhdGEiXSk7DQogICAgICAgICAgICB9DQoNCiAgICAgICAgfQ0KICAgICAgICAkcm9sbGluZ0N1cmwtPnNldENhbGxiYWNrKGZ1bmN0aW9uKFJvbGxpbmdDdXJsXFJlcXVlc3QgJHJlcXVlc3QsIFJvbGxpbmdDdXJsXFJvbGxpbmdDdXJsICRyb2xsaW5nQ3VybCkgdXNlICgmJHRyaWVkVXNlcnMsICYkdG90YWxTdWNjZXNzQ291bnQpDQp7DQogICAgJHRyaWVkVXNlciA9IGFycmF5KCAidXNlcklEIiA9PiAkcmVxdWVzdC0+aWRlbnRpZmllclBhcmFtc1sidXllSUQiXSwgImluc3RhSUQiID0+ICRyZXF1ZXN0LT5pZGVudGlmaWVyUGFyYW1zWyJpbnN0YUlEIl0sICJ1c2VyTmljayIgPT4gJHJlcXVlc3QtPmlkZW50aWZpZXJQYXJhbXNbImt1bGxhbmljaUFkaSJdLCAic3RhdHVzIiA9PiAiZmFpbCIgKTsNCiAgICAkaXNFcnJvcmVkID0gJHJlcXVlc3QtPmdldFJlc3BvbnNlRXJyb3IoKTsNCiAgICBpZiggZW1wdHkoJGlzRXJyb3JlZCkgKSANCiAgICB7DQogICAgICAgICRkb25lblNvbnVjID0ganNvbl9kZWNvZGUoJHJlcXVlc3QtPmdldFJlc3BvbnNlVGV4dCgpLCB0cnVlKTsNCiAgICAgICAgaWYoICRkb25lblNvbnVjICkgDQogICAgICAgIHsNCiAgICAgICAgICAgIGlmKCAkcmVxdWVzdC0+aWRlbnRpZmllclBhcmFtc1siaXNXZWJDb29raWUiXSA9PSAxICkgDQogICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgaWYoICRkb25lblNvbnVjWyJzdGF0dXMiXSA9PSAib2siICkgDQogICAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICAgICAkdG90YWxTdWNjZXNzQ291bnQrKzsNCiAgICAgICAgICAgICAgICAgICAgJHRyaWVkVXNlclsic3RhdHVzIl0gPSAic3VjY2VzcyI7DQogICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgIGVsc2UNCiAgICAgICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgICAgICR0cmllZFVzZXJbInN0YXR1cyJdID0gImZhaWwiOw0KICAgICAgICAgICAgICAgIH0NCg0KICAgICAgICAgICAgfQ0KICAgICAgICAgICAgZWxzZQ0KICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgIGlmKCAkZG9uZW5Tb251Y1sic3RhdHVzIl0gPT0gIm9rIiApIA0KICAgICAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAgICAgJHRvdGFsU3VjY2Vzc0NvdW50Kys7DQogICAgICAgICAgICAgICAgICAgICR0cmllZFVzZXJbInN0YXR1cyJdID0gInN1Y2Nlc3MiOw0KICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICBlbHNlDQogICAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICAgICAkdHJpZWRVc2VyWyJzdGF0dXMiXSA9ICJmYWlsIjsNCiAgICAgICAgICAgICAgICB9DQoNCiAgICAgICAgICAgIH0NCg0KICAgICAgICB9DQogICAgICAgIGVsc2UNCiAgICAgICAgew0KICAgICAgICAgICAgJHRyaWVkVXNlclsic3RhdHVzIl0gPSAiZmFpbCI7DQogICAgICAgIH0NCg0KICAgIH0NCiAgICBlbHNlDQogICAgew0KICAgICAgICAkdHJpZWRVc2VyWyJzdGF0dXMiXSA9ICJmYWlsIjsNCiAgICB9DQoNCiAgICAkdHJpZWRVc2Vyc1tdID0gJHRyaWVkVXNlcjsNCiAgICAkcm9sbGluZ0N1cmwtPmNsZWFyQ29tcGxldGVkKCk7DQogICAgJHJvbGxpbmdDdXJsLT5wcnVuZVBlbmRpbmdSZXF1ZXN0UXVldWUoKTsNCn0NCg0KKTsNCiAgICAgICAgJHJvbGxpbmdDdXJsLT5zZXRTaW11bHRhbmVvdXNMaW1pdCgkdGhpcy0+c2ltdWx0YW5lcG9zdHNpemUpOw0KICAgICAgICAkcm9sbGluZ0N1cmwtPmV4ZWN1dGUoKTsNCiAgICAgICAgcmV0dXJuIGFycmF5KCAidG90YWxTdWNjZXNzQ291bnQiID0+ICR0b3RhbFN1Y2Nlc3NDb3VudCwgInVzZXJzIiA9PiAkdHJpZWRVc2VycyApOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICR1c2VySUQNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkdXNlck5hbWUNCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBmb2xsb3coJHVzZXJJRCwgJHVzZXJOYW1lKQ0KICAgIHsNCiAgICAgICAgJHRvdGFsU3VjY2Vzc0NvdW50ID0gMDsNCiAgICAgICAgJHRyaWVkVXNlcnMgPSBhcnJheSggICk7DQogICAgICAgICRyb2xsaW5nQ3VybCA9IG5ldyBSb2xsaW5nQ3VybFxSb2xsaW5nQ3VybCgpOw0KICAgICAgICBmb3JlYWNoKCAkdGhpcy0+dXNlcnMgYXMgJHVzZXIgKSANCiAgICAgICAgew0KICAgICAgICAgICAgaWYoICR1c2VyWyJkYXRhIl1bImlzV2ViQ29va2llIl0gPT0gMSApIA0KICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICRvYmpJbnN0YWdyYW1XZWIgPSAkdXNlclsib2JqZWN0Il07DQogICAgICAgICAgICAgICAgJG9iakRhdGEgPSAkb2JqSW5zdGFncmFtV2ViLT5nZXREYXRhKCk7DQogICAgICAgICAgICAgICAgJGhlYWRlcnMgPSBhcnJheSggIlJlZmVyZXI6IGh0dHBzOi8vd3d3Lmluc3RhZ3JhbS5jb20vaW5zdGFncmFtLyIsICJETlQ6IDEiLCAiT3JpZ2luOiBodHRwczovL3d3dy5pbnN0YWdyYW0uY29tLyIsICJYLUNTUkZUb2tlbjogIiAuIHRyaW0oJG9iakRhdGFbInRva2VuIl0pLCAiWC1SZXF1ZXN0ZWQtV2l0aDogWE1MSHR0cFJlcXVlc3QiLCAiWC1JbnN0YWdyYW0tQUpBWDogMSIsICJDb25uZWN0aW9uOiBjbG9zZSIsICJDYWNoZS1Db250cm9sOiBtYXgtYWdlPTAiLCAiQWNjZXB0OiAqLyoiLCAiQWNjZXB0LUxhbmd1YWdlOiB0cjtxPTEiLCAiWF9GT1JXQVJERURfRk9SOiAiIC4gJG9iakRhdGFbImlwIl0sICJSRU1PVEVfQUREUjogIiAuICRvYmpEYXRhWyJpcCJdICk7DQogICAgICAgICAgICAgICAgJG9wdGlvbnMgPSBhcnJheSggQ1VSTE9QVF9VU0VSQUdFTlQgPT4gJG9iakRhdGFbInVzZXJfYWdlbnQiXSwgQ1VSTE9QVF9SRVRVUk5UUkFOU0ZFUiA9PiB0cnVlLCBDVVJMT1BUX0ZPTExPV0xPQ0FUSU9OID0+IHRydWUsIENVUkxPUFRfVkVSQk9TRSA9PiBmYWxzZSwgQ1VSTE9QVF9TU0xfVkVSSUZZUEVFUiA9PiBmYWxzZSwgQ1VSTE9QVF9TU0xfVkVSSUZZSE9TVCA9PiBmYWxzZSwgQ1VSTE9QVF9DT09LSUVGSUxFID0+ICR0aGlzLT5JR0RhdGFQYXRoIC4gJG9iakRhdGFbInVzZXJuYW1lIl0gLiAiLmRhdCIsIENVUkxPUFRfQ09PS0lFSkFSID0+ICR0aGlzLT5JR0RhdGFQYXRoIC4gJG9iakRhdGFbInVzZXJuYW1lIl0gLiAiLmRhdCIgKTsNCiAgICAgICAgICAgICAgICAkcm9sbGluZ0N1cmwtPnBvc3QoQ29uc3RhbnRzOjpXRUJfVVJMIC4gIndlYi9mcmllbmRzaGlwcy8iIC4gJHVzZXJJRCAuICIvZm9sbG93LyIsIE5VTEwsICRoZWFkZXJzLCAkb3B0aW9ucywgJHVzZXJbImRhdGEiXSk7DQogICAgICAgICAgICB9DQogICAgICAgICAgICBlbHNlDQogICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgJG9iakluc3RhZ3JhbSA9ICR1c2VyWyJvYmplY3QiXTsNCiAgICAgICAgICAgICAgICAkb2JqRGF0YSA9ICRvYmpJbnN0YWdyYW0tPmdldERhdGEoKTsNCiAgICAgICAgICAgICAgICAkZGF0YSA9IGpzb25fZW5jb2RlKGFycmF5KCAiX3V1aWQiID0+ICRvYmpEYXRhWyJ1dWlkIl0sICJfdWlkIiA9PiAkb2JqRGF0YVsidXNlcm5hbWVfaWQiXSwgInVzZXJfaWQiID0+ICR1c2VySUQsICJfY3NyZnRva2VuIiA9PiAkb2JqRGF0YVsidG9rZW4iXSApKTsNCiAgICAgICAgICAgICAgICAkaGVhZGVycyA9IGFycmF5KCAiQ29ubmVjdGlvbjogY2xvc2UiLCAiQWNjZXB0OiAqLyoiLCAiWC1JRy1DYXBhYmlsaXRpZXM6ICIgLiBDb25zdGFudHM6OlhfSUdfQ2FwYWJpbGl0aWVzLCAiWC1JRy1Db25uZWN0aW9uLVR5cGU6IFdJRkkiLCAiQ29udGVudC10eXBlOiBhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQ7IGNoYXJzZXQ9VVRGLTgiLCAiQWNjZXB0LUxhbmd1YWdlOiB0ci1UUiIsICJYX0ZPUldBUkRFRF9GT1I6ICIgLiAkb2JqRGF0YVsiaXAiXSwgIlJFTU9URV9BRERSOiAiIC4gJG9iakRhdGFbImlwIl0gKTsNCiAgICAgICAgICAgICAgICAkb3B0aW9ucyA9IGFycmF5KCBDVVJMT1BUX1VTRVJBR0VOVCA9PiAkb2JqRGF0YVsidXNlcl9hZ2VudCJdLCBDVVJMT1BUX1JFVFVSTlRSQU5TRkVSID0+IHRydWUsIENVUkxPUFRfRk9MTE9XTE9DQVRJT04gPT4gdHJ1ZSwgQ1VSTE9QVF9WRVJCT1NFID0+IGZhbHNlLCBDVVJMT1BUX1NTTF9WRVJJRllQRUVSID0+IGZhbHNlLCBDVVJMT1BUX1NTTF9WRVJJRllIT1NUID0+IGZhbHNlLCBDVVJMT1BUX0NPT0tJRUZJTEUgPT4gJHRoaXMtPklHRGF0YVBhdGggLiAkb2JqRGF0YVsidXNlcm5hbWUiXSAuICIuZGF0IiwgQ1VSTE9QVF9DT09LSUVKQVIgPT4gJHRoaXMtPklHRGF0YVBhdGggLiAkb2JqRGF0YVsidXNlcm5hbWUiXSAuICIuZGF0IiApOw0KICAgICAgICAgICAgICAgICRyb2xsaW5nQ3VybC0+cG9zdChDb25zdGFudHM6OkFQSV9VUkwgLiAiZnJpZW5kc2hpcHMvY3JlYXRlLyIgLiAkdXNlcklEIC4gIi8iLCBTaWduYXR1cmVVdGlsczo6Z2VuZXJhdGVTaWduYXR1cmUoJGRhdGEpLCAkaGVhZGVycywgJG9wdGlvbnMsICR1c2VyWyJkYXRhIl0pOw0KICAgICAgICAgICAgfQ0KDQogICAgICAgIH0NCiAgICAgICAgJHJvbGxpbmdDdXJsLT5zZXRDYWxsYmFjayhmdW5jdGlvbihSb2xsaW5nQ3VybFxSZXF1ZXN0ICRyZXF1ZXN0LCBSb2xsaW5nQ3VybFxSb2xsaW5nQ3VybCAkcm9sbGluZ0N1cmwpIHVzZSAoJiR0cmllZFVzZXJzLCAmJHRvdGFsU3VjY2Vzc0NvdW50KQ0Kew0KICAgICR0cmllZFVzZXIgPSBhcnJheSggInVzZXJJRCIgPT4gJHJlcXVlc3QtPmlkZW50aWZpZXJQYXJhbXNbInV5ZUlEIl0sICJpbnN0YUlEIiA9PiAkcmVxdWVzdC0+aWRlbnRpZmllclBhcmFtc1siaW5zdGFJRCJdLCAidXNlck5pY2siID0+ICRyZXF1ZXN0LT5pZGVudGlmaWVyUGFyYW1zWyJrdWxsYW5pY2lBZGkiXSwgInN0YXR1cyIgPT4gImZhaWwiICk7DQogICAgJGlzRXJyb3JlZCA9ICRyZXF1ZXN0LT5nZXRSZXNwb25zZUVycm9yKCk7DQogICAgaWYoIGVtcHR5KCRpc0Vycm9yZWQpICkgDQogICAgew0KICAgICAgICAkZG9uZW5Tb251YyA9IGpzb25fZGVjb2RlKCRyZXF1ZXN0LT5nZXRSZXNwb25zZVRleHQoKSwgdHJ1ZSk7DQogICAgICAgIGlmKCAkZG9uZW5Tb251YyApIA0KICAgICAgICB7DQogICAgICAgICAgICBpZiggJHJlcXVlc3QtPmlkZW50aWZpZXJQYXJhbXNbImlzV2ViQ29va2llIl0gPT0gMSApIA0KICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgIGlmKCAkZG9uZW5Tb251Y1sic3RhdHVzIl0gPT0gIm9rIiAmJiAkZG9uZW5Tb251Y1sicmVzdWx0Il0gPT0gImZvbGxvd2luZyIgKSANCiAgICAgICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgICAgICR0b3RhbFN1Y2Nlc3NDb3VudCsrOw0KICAgICAgICAgICAgICAgICAgICAkdHJpZWRVc2VyWyJzdGF0dXMiXSA9ICJzdWNjZXNzIjsNCiAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICAgZWxzZQ0KICAgICAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAgICAgJHRyaWVkVXNlclsic3RhdHVzIl0gPSAiZmFpbCI7DQogICAgICAgICAgICAgICAgfQ0KDQogICAgICAgICAgICB9DQogICAgICAgICAgICBlbHNlDQogICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgaWYoICRkb25lblNvbnVjWyJzdGF0dXMiXSA9PSAib2siICkgDQogICAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICAgICAkdG90YWxTdWNjZXNzQ291bnQrKzsNCiAgICAgICAgICAgICAgICAgICAgJHRyaWVkVXNlclsic3RhdHVzIl0gPSAic3VjY2VzcyI7DQogICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgIGVsc2UNCiAgICAgICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgICAgICR0cmllZFVzZXJbInN0YXR1cyJdID0gImZhaWwiOw0KICAgICAgICAgICAgICAgIH0NCg0KICAgICAgICAgICAgfQ0KDQogICAgICAgIH0NCiAgICAgICAgZWxzZQ0KICAgICAgICB7DQogICAgICAgICAgICAkdHJpZWRVc2VyWyJzdGF0dXMiXSA9ICJmYWlsIjsNCiAgICAgICAgfQ0KDQogICAgfQ0KICAgIGVsc2UNCiAgICB7DQogICAgICAgICR0cmllZFVzZXJbInN0YXR1cyJdID0gImZhaWwiOw0KICAgIH0NCg0KICAgICR0cmllZFVzZXJzW10gPSAkdHJpZWRVc2VyOw0KICAgICRyb2xsaW5nQ3VybC0+Y2xlYXJDb21wbGV0ZWQoKTsNCiAgICAkcm9sbGluZ0N1cmwtPnBydW5lUGVuZGluZ1JlcXVlc3RRdWV1ZSgpOw0KfQ0KDQopOw0KICAgICAgICAkcm9sbGluZ0N1cmwtPnNldFNpbXVsdGFuZW91c0xpbWl0KCR0aGlzLT5zaW11bHRhbmVwb3N0c2l6ZSk7DQogICAgICAgICRyb2xsaW5nQ3VybC0+ZXhlY3V0ZSgpOw0KICAgICAgICByZXR1cm4gYXJyYXkoICJ0b3RhbFN1Y2Nlc3NDb3VudCIgPT4gJHRvdGFsU3VjY2Vzc0NvdW50LCAidXNlcnMiID0+ICR0cmllZFVzZXJzICk7DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJG1lZGlhSUQNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkbWVkaWFDb2RlDQogICAgICAgICAqIEBwYXJhbSBhcnJheSAgJGNvbW1lbnRUZXh0cw0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGNvbW1lbnQoJG1lZGlhSUQsICRtZWRpYUNvZGUsICRjb21tZW50VGV4dHMpDQogICAgew0KICAgICAgICAkdG90YWxTdWNjZXNzQ291bnQgPSAwOw0KICAgICAgICAkdHJpZWRVc2VycyA9IGFycmF5KCAgKTsNCiAgICAgICAgJGNvbW1lbnRJbmRleCA9IC0xOw0KICAgICAgICBpZiggaXNfYXJyYXkoJGNvbW1lbnRUZXh0cykgJiYgIWVtcHR5KCRjb21tZW50VGV4dHMpICkgDQogICAgICAgIHsNCiAgICAgICAgICAgICRhcnJNZWRpYUlEID0gZXhwbG9kZSgiXyIsICRtZWRpYUlEKTsNCiAgICAgICAgICAgICRtZWRpYUlEQmVmb3JlciA9ICRhcnJNZWRpYUlEWzBdOw0KICAgICAgICAgICAgJHJvbGxpbmdDdXJsID0gbmV3IFJvbGxpbmdDdXJsXFJvbGxpbmdDdXJsKCk7DQogICAgICAgICAgICBmb3JlYWNoKCAkdGhpcy0+dXNlcnMgYXMgJHVzZXIgKSANCiAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAkY29tbWVudEluZGV4Kys7DQogICAgICAgICAgICAgICAgJGNvbW1lbnRJbmRleCA9IChpc3NldCgkY29tbWVudFRleHRzWyRjb21tZW50SW5kZXhdKSA/ICRjb21tZW50SW5kZXggOiAwKTsNCiAgICAgICAgICAgICAgICBpZiggJHVzZXJbImRhdGEiXVsiaXNXZWJDb29raWUiXSA9PSAxICkgDQogICAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICAgICAkb2JqSW5zdGFncmFtV2ViID0gJHVzZXJbIm9iamVjdCJdOw0KICAgICAgICAgICAgICAgICAgICAkb2JqRGF0YSA9ICRvYmpJbnN0YWdyYW1XZWItPmdldERhdGEoKTsNCiAgICAgICAgICAgICAgICAgICAgJHBvc3REYXRhID0gImNvbW1lbnRfdGV4dD0iIC4gJGNvbW1lbnRUZXh0c1skY29tbWVudEluZGV4XTsNCiAgICAgICAgICAgICAgICAgICAgJGhlYWRlcnMgPSBhcnJheSggIlJlZmVyZXI6IGh0dHBzOi8vd3d3Lmluc3RhZ3JhbS5jb20vIiwgIkROVDogMSIsICJPcmlnaW46IGh0dHBzOi8vd3d3Lmluc3RhZ3JhbS5jb20vIiwgIlgtQ1NSRlRva2VuOiAiIC4gdHJpbSgkb2JqRGF0YVsidG9rZW4iXSksICJYLVJlcXVlc3RlZC1XaXRoOiBYTUxIdHRwUmVxdWVzdCIsICJYLUluc3RhZ3JhbS1BSkFYOiAxIiwgIkNvbm5lY3Rpb246IGNsb3NlIiwgIkNhY2hlLUNvbnRyb2w6IG1heC1hZ2U9MCIsICJBY2NlcHQ6ICovKiIsICJBY2NlcHQtTGFuZ3VhZ2U6IHRyO3E9MSIsICJYX0ZPUldBUkRFRF9GT1I6ICIgLiAkb2JqRGF0YVsiaXAiXSwgIlJFTU9URV9BRERSOiAiIC4gJG9iakRhdGFbImlwIl0gKTsNCiAgICAgICAgICAgICAgICAgICAgJG9wdGlvbnMgPSBhcnJheSggQ1VSTE9QVF9VU0VSQUdFTlQgPT4gJG9iakRhdGFbInVzZXJfYWdlbnQiXSwgQ1VSTE9QVF9SRVRVUk5UUkFOU0ZFUiA9PiB0cnVlLCBDVVJMT1BUX0ZPTExPV0xPQ0FUSU9OID0+IHRydWUsIENVUkxPUFRfVkVSQk9TRSA9PiBmYWxzZSwgQ1VSTE9QVF9TU0xfVkVSSUZZUEVFUiA9PiBmYWxzZSwgQ1VSTE9QVF9TU0xfVkVSSUZZSE9TVCA9PiBmYWxzZSwgQ1VSTE9QVF9DT09LSUVGSUxFID0+ICR0aGlzLT5JR0RhdGFQYXRoIC4gJG9iakRhdGFbInVzZXJuYW1lIl0gLiAiLmRhdCIsIENVUkxPUFRfQ09PS0lFSkFSID0+ICR0aGlzLT5JR0RhdGFQYXRoIC4gJG9iakRhdGFbInVzZXJuYW1lIl0gLiAiLmRhdCIgKTsNCiAgICAgICAgICAgICAgICAgICAgJHJvbGxpbmdDdXJsLT5wb3N0KENvbnN0YW50czo6V0VCX1VSTCAuICJ3ZWIvY29tbWVudHMvIiAuICRtZWRpYUlEQmVmb3JlciAuICIvYWRkLyIsICRwb3N0RGF0YSwgJGhlYWRlcnMsICRvcHRpb25zLCAkdXNlclsiZGF0YSJdKTsNCiAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICAgZWxzZQ0KICAgICAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAgICAgJG9iakluc3RhZ3JhbSA9ICR1c2VyWyJvYmplY3QiXTsNCiAgICAgICAgICAgICAgICAgICAgJG9iakRhdGEgPSAkb2JqSW5zdGFncmFtLT5nZXREYXRhKCk7DQogICAgICAgICAgICAgICAgICAgICRkYXRhID0ganNvbl9lbmNvZGUoYXJyYXkoICJfdXVpZCIgPT4gJG9iakRhdGFbInV1aWQiXSwgIl91aWQiID0+ICRvYmpEYXRhWyJ1c2VybmFtZV9pZCJdLCAiX2NzcmZ0b2tlbiIgPT4gJG9iakRhdGFbInRva2VuIl0sICJjb21tZW50X3RleHQiID0+ICRjb21tZW50VGV4dHNbJGNvbW1lbnRJbmRleF0gKSk7DQogICAgICAgICAgICAgICAgICAgICRoZWFkZXJzID0gYXJyYXkoICJDb25uZWN0aW9uOiBjbG9zZSIsICJBY2NlcHQ6ICovKiIsICJYLUlHLUNhcGFiaWxpdGllczogIiAuIENvbnN0YW50czo6WF9JR19DYXBhYmlsaXRpZXMsICJYLUlHLUNvbm5lY3Rpb24tVHlwZTogV0lGSSIsICJDb250ZW50LXR5cGU6IGFwcGxpY2F0aW9uL3gtd3d3LWZvcm0tdXJsZW5jb2RlZDsgY2hhcnNldD1VVEYtOCIsICJBY2NlcHQtTGFuZ3VhZ2U6IHRyLVRSIiwgIlhfRk9SV0FSREVEX0ZPUjogIiAuICRvYmpEYXRhWyJpcCJdLCAiUkVNT1RFX0FERFI6ICIgLiAkb2JqRGF0YVsiaXAiXSApOw0KICAgICAgICAgICAgICAgICAgICAkb3B0aW9ucyA9IGFycmF5KCBDVVJMT1BUX1VTRVJBR0VOVCA9PiAkb2JqRGF0YVsidXNlcl9hZ2VudCJdLCBDVVJMT1BUX1JFVFVSTlRSQU5TRkVSID0+IHRydWUsIENVUkxPUFRfRk9MTE9XTE9DQVRJT04gPT4gdHJ1ZSwgQ1VSTE9QVF9WRVJCT1NFID0+IGZhbHNlLCBDVVJMT1BUX1NTTF9WRVJJRllQRUVSID0+IGZhbHNlLCBDVVJMT1BUX1NTTF9WRVJJRllIT1NUID0+IGZhbHNlLCBDVVJMT1BUX0NPT0tJRUZJTEUgPT4gJHRoaXMtPklHRGF0YVBhdGggLiAkb2JqRGF0YVsidXNlcm5hbWUiXSAuICIuZGF0IiwgQ1VSTE9QVF9DT09LSUVKQVIgPT4gJHRoaXMtPklHRGF0YVBhdGggLiAkb2JqRGF0YVsidXNlcm5hbWUiXSAuICIuZGF0IiApOw0KICAgICAgICAgICAgICAgICAgICAkcm9sbGluZ0N1cmwtPnBvc3QoQ29uc3RhbnRzOjpBUElfVVJMIC4gIm1lZGlhLyIgLiAkbWVkaWFJRCAuICIvY29tbWVudC8iLCBTaWduYXR1cmVVdGlsczo6Z2VuZXJhdGVTaWduYXR1cmUoJGRhdGEpLCAkaGVhZGVycywgJG9wdGlvbnMsICR1c2VyWyJkYXRhIl0pOw0KICAgICAgICAgICAgICAgIH0NCg0KICAgICAgICAgICAgfQ0KICAgICAgICAgICAgJHJvbGxpbmdDdXJsLT5zZXRDYWxsYmFjayhmdW5jdGlvbihSb2xsaW5nQ3VybFxSZXF1ZXN0ICRyZXF1ZXN0LCBSb2xsaW5nQ3VybFxSb2xsaW5nQ3VybCAkcm9sbGluZ0N1cmwpIHVzZSAoJiR0cmllZFVzZXJzLCAmJHRvdGFsU3VjY2Vzc0NvdW50KQ0Kew0KICAgICR0cmllZFVzZXIgPSBhcnJheSggInVzZXJJRCIgPT4gJHJlcXVlc3QtPmlkZW50aWZpZXJQYXJhbXNbInV5ZUlEIl0sICJpbnN0YUlEIiA9PiAkcmVxdWVzdC0+aWRlbnRpZmllclBhcmFtc1siaW5zdGFJRCJdLCAidXNlck5pY2siID0+ICRyZXF1ZXN0LT5pZGVudGlmaWVyUGFyYW1zWyJrdWxsYW5pY2lBZGkiXSwgInN0YXR1cyIgPT4gImZhaWwiICk7DQogICAgJGlzRXJyb3JlZCA9ICRyZXF1ZXN0LT5nZXRSZXNwb25zZUVycm9yKCk7DQogICAgaWYoIGVtcHR5KCRpc0Vycm9yZWQpICkgDQogICAgew0KICAgICAgICAkZG9uZW5Tb251YyA9IGpzb25fZGVjb2RlKCRyZXF1ZXN0LT5nZXRSZXNwb25zZVRleHQoKSwgdHJ1ZSk7DQogICAgICAgIGlmKCAkZG9uZW5Tb251YyApIA0KICAgICAgICB7DQogICAgICAgICAgICBpZiggJHJlcXVlc3QtPmlkZW50aWZpZXJQYXJhbXNbImlzV2ViQ29va2llIl0gPT0gMSApIA0KICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgIGlmKCBpc3NldCgkZG9uZW5Tb251Y1sic3RhdHVzIl0pICYmICRkb25lblNvbnVjWyJzdGF0dXMiXSA9PSAib2siICkgDQogICAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICAgICAkdG90YWxTdWNjZXNzQ291bnQrKzsNCiAgICAgICAgICAgICAgICAgICAgJHRyaWVkVXNlclsic3RhdHVzIl0gPSAic3VjY2VzcyI7DQogICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgIGVsc2UNCiAgICAgICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgICAgICR0cmllZFVzZXJbInN0YXR1cyJdID0gImZhaWwiOw0KICAgICAgICAgICAgICAgIH0NCg0KICAgICAgICAgICAgfQ0KICAgICAgICAgICAgZWxzZQ0KICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgIGlmKCBpc3NldCgkZG9uZW5Tb251Y1sic3RhdHVzIl0pICYmICRkb25lblNvbnVjWyJzdGF0dXMiXSA9PSAib2siICkgDQogICAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICAgICAkdG90YWxTdWNjZXNzQ291bnQrKzsNCiAgICAgICAgICAgICAgICAgICAgJHRyaWVkVXNlclsic3RhdHVzIl0gPSAic3VjY2VzcyI7DQogICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgIGVsc2UNCiAgICAgICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgICAgICR0cmllZFVzZXJbInN0YXR1cyJdID0gImZhaWwiOw0KICAgICAgICAgICAgICAgIH0NCg0KICAgICAgICAgICAgfQ0KDQogICAgICAgIH0NCiAgICAgICAgZWxzZQ0KICAgICAgICB7DQogICAgICAgICAgICAkdHJpZWRVc2VyWyJzdGF0dXMiXSA9ICJmYWlsIjsNCiAgICAgICAgfQ0KDQogICAgfQ0KICAgIGVsc2UNCiAgICB7DQogICAgICAgICR0cmllZFVzZXJbInN0YXR1cyJdID0gImZhaWwiOw0KICAgIH0NCg0KICAgICR0cmllZFVzZXJzW10gPSAkdHJpZWRVc2VyOw0KICAgICRyb2xsaW5nQ3VybC0+Y2xlYXJDb21wbGV0ZWQoKTsNCiAgICAkcm9sbGluZ0N1cmwtPnBydW5lUGVuZGluZ1JlcXVlc3RRdWV1ZSgpOw0KfQ0KDQopOw0KICAgICAgICAgICAgJHJvbGxpbmdDdXJsLT5zZXRTaW11bHRhbmVvdXNMaW1pdCgkdGhpcy0+c2ltdWx0YW5lcG9zdHNpemUpOw0KICAgICAgICAgICAgJHJvbGxpbmdDdXJsLT5leGVjdXRlKCk7DQogICAgICAgIH0NCg0KICAgICAgICByZXR1cm4gYXJyYXkoICJ0b3RhbFN1Y2Nlc3NDb3VudCIgPT4gJHRvdGFsU3VjY2Vzc0NvdW50LCAidXNlcnMiID0+ICR0cmllZFVzZXJzICk7DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gdmFsaWRhdGUoKQ0KICAgIHsNCiAgICAgICAgJHRvdGFsU3VjY2Vzc0NvdW50ID0gMDsNCiAgICAgICAgJHRyaWVkVXNlcnMgPSBhcnJheSggICk7DQogICAgICAgICRyb2xsaW5nQ3VybCA9IG5ldyBSb2xsaW5nQ3VybFxSb2xsaW5nQ3VybCgpOw0KICAgICAgICBmb3JlYWNoKCAkdGhpcy0+dXNlcnMgYXMgJHVzZXIgKSANCiAgICAgICAgew0KICAgICAgICAgICAgaWYoICR1c2VyWyJkYXRhIl1bImlzV2ViQ29va2llIl0gPT0gMSApIA0KICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICRvYmpJbnN0YWdyYW1XZWIgPSAkdXNlclsib2JqZWN0Il07DQogICAgICAgICAgICAgICAgJG9iakRhdGEgPSAkb2JqSW5zdGFncmFtV2ViLT5nZXREYXRhKCk7DQogICAgICAgICAgICAgICAgJGhlYWRlcnMgPSBhcnJheSggIlJlZmVyZXI6IGh0dHBzOi8vd3d3Lmluc3RhZ3JhbS5jb20vaW5zdGFncmFtLyIsICJETlQ6IDEiLCAiT3JpZ2luOiBodHRwczovL3d3dy5pbnN0YWdyYW0uY29tLyIsICJYLUNTUkZUb2tlbjogIiAuIHRyaW0oJG9iakRhdGFbInRva2VuIl0pLCAiWC1SZXF1ZXN0ZWQtV2l0aDogWE1MSHR0cFJlcXVlc3QiLCAiWC1JbnN0YWdyYW0tQUpBWDogMSIsICJDb25uZWN0aW9uOiBjbG9zZSIsICJDYWNoZS1Db250cm9sOiBtYXgtYWdlPTAiLCAiQWNjZXB0OiAqLyoiLCAiQWNjZXB0LUxhbmd1YWdlOiB0cjtxPTEiLCAiWF9GT1JXQVJERURfRk9SOiAiIC4gJG9iakRhdGFbImlwIl0sICJSRU1PVEVfQUREUjogIiAuICRvYmpEYXRhWyJpcCJdICk7DQogICAgICAgICAgICAgICAgJG9wdGlvbnMgPSBhcnJheSggQ1VSTE9QVF9VU0VSQUdFTlQgPT4gJG9iakRhdGFbInVzZXJfYWdlbnQiXSwgQ1VSTE9QVF9SRVRVUk5UUkFOU0ZFUiA9PiB0cnVlLCBDVVJMT1BUX0ZPTExPV0xPQ0FUSU9OID0+IHRydWUsIENVUkxPUFRfVkVSQk9TRSA9PiBmYWxzZSwgQ1VSTE9QVF9TU0xfVkVSSUZZUEVFUiA9PiBmYWxzZSwgQ1VSTE9QVF9TU0xfVkVSSUZZSE9TVCA9PiBmYWxzZSwgQ1VSTE9QVF9DT09LSUVGSUxFID0+ICR0aGlzLT5JR0RhdGFQYXRoIC4gJG9iakRhdGFbInVzZXJuYW1lIl0gLiAiLmRhdCIsIENVUkxPUFRfQ09PS0lFSkFSID0+ICR0aGlzLT5JR0RhdGFQYXRoIC4gJG9iakRhdGFbInVzZXJuYW1lIl0gLiAiLmRhdCIgKTsNCiAgICAgICAgICAgICAgICAkcm9sbGluZ0N1cmwtPmdldChDb25zdGFudHM6OldFQl9VUkwgLiAiYWNjb3VudHMvYWN0aXZpdHkvP19fYT0xIiwgJGhlYWRlcnMsICRvcHRpb25zLCAkdXNlclsiZGF0YSJdKTsNCiAgICAgICAgICAgIH0NCiAgICAgICAgICAgIGVsc2UNCiAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAkb2JqSW5zdGFncmFtID0gJHVzZXJbIm9iamVjdCJdOw0KICAgICAgICAgICAgICAgICRvYmpEYXRhID0gJG9iakluc3RhZ3JhbS0+Z2V0RGF0YSgpOw0KICAgICAgICAgICAgICAgICRoZWFkZXJzID0gYXJyYXkoICJDb25uZWN0aW9uOiBjbG9zZSIsICJBY2NlcHQ6ICovKiIsICJYLUlHLUNhcGFiaWxpdGllczogIiAuIENvbnN0YW50czo6WF9JR19DYXBhYmlsaXRpZXMsICJYLUlHLUNvbm5lY3Rpb24tVHlwZTogV0lGSSIsICJDb250ZW50LXR5cGU6IGFwcGxpY2F0aW9uL3gtd3d3LWZvcm0tdXJsZW5jb2RlZDsgY2hhcnNldD1VVEYtOCIsICJBY2NlcHQtTGFuZ3VhZ2U6IHRyLVRSIiwgIlhfRk9SV0FSREVEX0ZPUjogIiAuICRvYmpEYXRhWyJpcCJdLCAiUkVNT1RFX0FERFI6ICIgLiAkb2JqRGF0YVsiaXAiXSApOw0KICAgICAgICAgICAgICAgICRvcHRpb25zID0gYXJyYXkoIENVUkxPUFRfVVNFUkFHRU5UID0+ICRvYmpEYXRhWyJ1c2VyX2FnZW50Il0sIENVUkxPUFRfUkVUVVJOVFJBTlNGRVIgPT4gdHJ1ZSwgQ1VSTE9QVF9GT0xMT1dMT0NBVElPTiA9PiB0cnVlLCBDVVJMT1BUX1ZFUkJPU0UgPT4gZmFsc2UsIENVUkxPUFRfU1NMX1ZFUklGWVBFRVIgPT4gZmFsc2UsIENVUkxPUFRfU1NMX1ZFUklGWUhPU1QgPT4gZmFsc2UsIENVUkxPUFRfQ09PS0lFRklMRSA9PiAkdGhpcy0+SUdEYXRhUGF0aCAuICRvYmpEYXRhWyJ1c2VybmFtZSJdIC4gIi5kYXQiLCBDVVJMT1BUX0NPT0tJRUpBUiA9PiAkdGhpcy0+SUdEYXRhUGF0aCAuICRvYmpEYXRhWyJ1c2VybmFtZSJdIC4gIi5kYXQiICk7DQogICAgICAgICAgICAgICAgJHJvbGxpbmdDdXJsLT5nZXQoQ29uc3RhbnRzOjpBUElfVVJMIC4gImZlZWQvdGltZWxpbmUvP3JhbmtfdG9rZW49IiAuICRvYmpEYXRhWyJyYW5rX3Rva2VuIl0gLiAiJnJhbmtlZF9jb250ZW50PXRydWUiLCAkaGVhZGVycywgJG9wdGlvbnMsICR1c2VyWyJkYXRhIl0pOw0KICAgICAgICAgICAgfQ0KDQogICAgICAgIH0NCiAgICAgICAgJHJvbGxpbmdDdXJsLT5zZXRDYWxsYmFjayhmdW5jdGlvbihSb2xsaW5nQ3VybFxSZXF1ZXN0ICRyZXF1ZXN0LCBSb2xsaW5nQ3VybFxSb2xsaW5nQ3VybCAkcm9sbGluZ0N1cmwpIHVzZSAoJiR0cmllZFVzZXJzLCAmJHRvdGFsU3VjY2Vzc0NvdW50KQ0Kew0KICAgICR0cmllZFVzZXIgPSBhcnJheSggInVzZXJJRCIgPT4gJHJlcXVlc3QtPmlkZW50aWZpZXJQYXJhbXNbInV5ZUlEIl0sICJpbnN0YUlEIiA9PiAkcmVxdWVzdC0+aWRlbnRpZmllclBhcmFtc1siaW5zdGFJRCJdLCAidXNlck5pY2siID0+ICRyZXF1ZXN0LT5pZGVudGlmaWVyUGFyYW1zWyJrdWxsYW5pY2lBZGkiXSwgInN0YXR1cyIgPT4gImZhaWwiICk7DQogICAgJGlzRXJyb3JlZCA9ICRyZXF1ZXN0LT5nZXRSZXNwb25zZUVycm9yKCk7DQogICAgaWYoIGVtcHR5KCRpc0Vycm9yZWQpICkgDQogICAgew0KICAgICAgICAkZG9uZW5Tb251YyA9IGpzb25fZGVjb2RlKCRyZXF1ZXN0LT5nZXRSZXNwb25zZVRleHQoKSwgdHJ1ZSk7DQogICAgICAgIGlmKCAkZG9uZW5Tb251YyApIA0KICAgICAgICB7DQogICAgICAgICAgICBpZiggJHJlcXVlc3QtPmlkZW50aWZpZXJQYXJhbXNbImlzV2ViQ29va2llIl0gPT0gMSApIA0KICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICRyZXNwb25zZURhdGEgPSAkcmVxdWVzdC0+Z2V0UmVzcG9uc2VJbmZvKCk7DQogICAgICAgICAgICAgICAgaWYoICRyZXNwb25zZURhdGFbImh0dHBfY29kZSJdID09ICIyMDAiICkgDQogICAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICAgICAkdG90YWxTdWNjZXNzQ291bnQrKzsNCiAgICAgICAgICAgICAgICAgICAgJHRyaWVkVXNlclsic3RhdHVzIl0gPSAic3VjY2VzcyI7DQogICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgIGVsc2UNCiAgICAgICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgICAgICR0cmllZFVzZXJbInN0YXR1cyJdID0gImZhaWwiOw0KICAgICAgICAgICAgICAgIH0NCg0KICAgICAgICAgICAgfQ0KICAgICAgICAgICAgZWxzZQ0KICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgIGlmKCAkZG9uZW5Tb251Y1sic3RhdHVzIl0gPT0gIm9rIiApIA0KICAgICAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAgICAgJHRvdGFsU3VjY2Vzc0NvdW50Kys7DQogICAgICAgICAgICAgICAgICAgICR0cmllZFVzZXJbInN0YXR1cyJdID0gInN1Y2Nlc3MiOw0KICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICBlbHNlDQogICAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICAgICAkdHJpZWRVc2VyWyJzdGF0dXMiXSA9ICJmYWlsIjsNCiAgICAgICAgICAgICAgICB9DQoNCiAgICAgICAgICAgIH0NCg0KICAgICAgICB9DQogICAgICAgIGVsc2UNCiAgICAgICAgew0KICAgICAgICAgICAgJHRyaWVkVXNlclsic3RhdHVzIl0gPSAiZmFpbCI7DQogICAgICAgIH0NCg0KICAgIH0NCiAgICBlbHNlDQogICAgew0KICAgICAgICAkdHJpZWRVc2VyWyJzdGF0dXMiXSA9ICJmYWlsIjsNCiAgICB9DQoNCiAgICAkdHJpZWRVc2Vyc1tdID0gJHRyaWVkVXNlcjsNCiAgICAkcm9sbGluZ0N1cmwtPmNsZWFyQ29tcGxldGVkKCk7DQogICAgJHJvbGxpbmdDdXJsLT5wcnVuZVBlbmRpbmdSZXF1ZXN0UXVldWUoKTsNCn0NCg0KKTsNCiAgICAgICAgJHJvbGxpbmdDdXJsLT5zZXRTaW11bHRhbmVvdXNMaW1pdCgkdGhpcy0+c2ltdWx0YW5lcG9zdHNpemUpOw0KICAgICAgICAkcm9sbGluZ0N1cmwtPmV4ZWN1dGUoKTsNCiAgICAgICAgcmV0dXJuIGFycmF5KCAidG90YWxTdWNjZXNzQ291bnQiID0+ICR0b3RhbFN1Y2Nlc3NDb3VudCwgInVzZXJzIiA9PiAkdHJpZWRVc2VycyApOw0KICAgIH0NCg0KfQ0KDQoNCg=="));
Function Calls
base64_decode | 1 |
Stats
MD5 | 20a243ceb7ae63b29bd8a817d09875dc |
Eval Count | 1 |
Decode Time | 180 ms |