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("PD9waHAgDQpyZXF1aXJlKCdsaXNhbnMucGhwJyk7Ly9saXNhbnMucGhwIGR..
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 = "-2018";//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 almak icin www.sorunsuzscript.com adresini ziyaret ediniz.\');//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
/**
Sorunsuz Script Lisans
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("PD9waHAgDQpyZXF1aXJlKCdsaXNhbnMucGhwJyk7Ly9saXNhbnMucGhwIGRvc3lhc2luaSBla2xlDQokbGlzYW5zWydzaXRlJ109Z2V0ZW52KCdIVFRQX0hPU1QnKTsvL3NpdGUgZG9tYWluaW5pIGFsDQppZiAoc3Vic3RyKCRsaXNhbnNbJ3NpdGUnXSwgMCwgNCkgPT0gInd3dy4iKS8vZG9tYWluIGJhc2luZGFraSB3d3cuIHZhcm1pIHlva211IGtvbnRyb2wgZXQNCiRsaXNhbnNbJ3NpdGUnXT1zdWJzdHIoJGxpc2Fuc1snc2l0ZSddLDQpOy8vd3d3LiBzaWwgdmUgc2FkZWNlIGRvbWFpbi5jb20gb2xhcmFrIGFkcmVzaSBhbA0KIA0KJGJhcyA9ICJTT1JVTlNVWlNDUlBULSI7Ly9MaXNhbnMga29kdSBiYW5hIGlzdGVkaWluaXogeWF6IGVrbGVuZWJpbGlyDQokc29uID0gIi0yMDE4IjsvL0xpc2FucyBrb2R1IHNvbnVuYSBpc3RlZGlpbml6IHlheiBla2xlbmViaWxpcg0KJG0gPSAibWQ1IjsvL21kNSBzaWZyZWxlbWUgDQokcyA9ICJzaGExIjsvL3NoYTEgc2lmcmVsZW1lIA0KIA0KIA0KJGxpc2Fuc1snaGFzaCddPXdvcmR3cmFwKHN0cnRvdXBwZXIoJHMgKCRzICgkcyAoJHMgKCRtICgkcyAoJHMgKCRtICgkbGlzYW5zWydzaXRlJ10uZGF0ZSgnWW1kJykpKSkpKSkpKSksNSwnLScsdHJ1ZSk7Ly9saXNhbnMga29kdW51IG9sdXN0dXIgdmUgNSBrYXJha3RlcmRlIGJpciAtIGtveQ0KJGxpc2tvZCA9ICRsaXNhbnNbJ2hhc2gnXTsNCiRjZXZpcj1zdHJyZXYoJGxpc2tvZCk7Ly9saXNhbnMga29kdW51IHRlcnNpbmUgY2V2aXINCiRiY3MgPSAiJGJhcyRjZXZpciRzb24iOw0KIA0KaWYoJGJjcyE9PSRsaXNhbnNrb2R1KS8vbGlzYW5zIGtvZHVudSBrb250cm9sIGV0IGZhcmsgdmFyc2Egc3VudWN1eWEgYmFnbGFuIC0gMjQgc2FhdHRlIGJpciBsaXNhbnMga29kdSBkZWdpc2lyDQp7DQokbGlzYW5zX2NldmFwPWZpbGVfZ2V0X2NvbnRlbnRzKCdodHRwOi8vaW5zdGEuYWRhbmFiaWxpc2ltLm5ldC9saXNhbnMva29udHJvbC5waHA/c2l0ZT0nLiRsaXNhbnNbJ3NpdGUnXSk7Ly9saXNhbnMgc3VudWN1c3VuZGFuIGxpc2FucyBrb250cm9sdSB5YXANCmlmKCRsaXNhbnNfY2V2YXAhPSdMSVNBTlNMSScpLy9zdW51Y3VkYW4gZ2VsZW4gY2V2YXAgTElTQU5TTEkgZGVnaWxzZQ0KIA0KZGllKCdCdSBzaXRleWUgYWl0IGxpc2FucyBidWx1bmFtYWRpLiBMaXNhbnMgYWxtYWsgaWNpbiB3d3cuc29ydW5zdXpzY3JpcHQuY29tIGFkcmVzaW5pIHppeWFyZXQgZWRpbml6LicpOy8vbGlzYW5zIGdlY2Vyc2l6IG1lc2FqaSB2ZXJpbGlyIGlzbGVtIGR1cnVyDQogDQokbHlheiA9IGZvcGVuKCdsaXNhbnMucGhwJywidysiKTsvL2xpc2Fucy5waHAgZG9zeWFzaW5pIGFjIHllbmkgbGlzYW5zIGtvZHVudSB5YXogYnUgZG9zeWEgeWF6bGFiaWxpciBvbG1hbGRyDQpAZndyaXRlKCRseWF6LCc8P3BocA0KLyoqDQogICAgU29ydW5zdXogU2NyaXB0IExpc2FucyANCgkNCiAgICBCdSBkb3N5YXlhIGhlcmhhbmdpIGJpciBsaXNhbnMga29kdSB5YXptYW5peiBnZXJla21leg0KICAgIEFsbWlzIG9sZHVndW51eiB1cnVuZSBhaXQgbGlzYW5zIHNpc3RlbWRlIGFrdGlmIG9sZHVndW5kYQ0KICAgIExpc2FucyBrb2R1bnV6IG90b21hdGlrIG9sYXJhayBnaXJpbGVjZWt0aXIuDQoJDQoJWWVuaSBsaXNhbnMgYWxtYWsgaWNpbiB3d3cuc29ydW5zdXpzY3JpcHQuY29tIGFkcmVzaW5pIHppeWFyZXQgZWRlYmlsaXJzaW5peg0KIA0KICovDQokbGlzYW5za29kdT0iJy4kYmNzLiciOw0KPz4nKTsNCmZjbG9zZSgkbHlheik7DQp9DQoNCmRlZmluZSgnSU5TVEFXRUJfVkVSU0lPTicsIHN0cl9yZXBsYWNlKCJJbnN0YVdlYlYiLCAiIiwgYmFzZW5hbWUoX19GSUxFX18sICIucGhwIikpKTsNCiAgICAgICAgaWYoJF9TRVJWRVJbIkhUVFBfVVNFUl9BR0VOVCJdID09ICIiKSB7DQogICAgICAgICAgICBoZWFkZXIoIkhUVFAvMS4xIDQwMyBGb3JiaWRkZW4iKTsNCiAgICAgICAgICAgIGVjaG8gJ0Jha20gTW9kdSEnOw0KICAgICAgICAgICAgZXhpdCgpOw0KICAgICAgICB9DQoNCmNsYXNzIEluc3RhZ3JhbQ0Kew0KICAgIHByb3RlY3RlZCAkdXNlcm5hbWU7DQogICAgcHJvdGVjdGVkICRwYXNzd29yZDsNCiAgICBwcm90ZWN0ZWQgJGRlYnVnOw0KICAgIHByb3RlY3RlZCAkdXVpZDsNCiAgICBwcm90ZWN0ZWQgJGRldmljZV9pZDsNCiAgICBwcm90ZWN0ZWQgJHVzZXJuYW1lX2lkOw0KLyoqDQogICAgICAgICAqIEB2YXIgU2V0dGluZ3MNCiAgICAgICAgICovDQogICAgcHVibGljICRzZXR0aW5nczsNCiAgICBwcm90ZWN0ZWQgJHRva2VuOw0KICAgIHByb3RlY3RlZCAkaXNMb2dnZWRJbiA9IGZhbHNlOw0KICAgIHByb3RlY3RlZCAkcmFua190b2tlbjsNCiAgICBwcm90ZWN0ZWQgJElHRGF0YVBhdGg7DQoNCiAgICAvKioNCiAgICAgICAgICogRGVmYXVsdCBjbGFzcyBjb25zdHJ1Y3Rvci4NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkdXNlcm5hbWUNCiAgICAgICAgICogICBZb3VyIEluc3RhZ3JhbSB1c2VybmFtZS4NCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkcGFzc3dvcmQNCiAgICAgICAgICogICBZb3VyIEluc3RhZ3JhbSBwYXNzd29yZC4NCiAgICAgICAgICogQHBhcmFtICAgICAgICAkZGVidWcNCiAgICAgICAgICogICBEZWJ1ZyBvbiBvciBvZmYsIGZhbHNlIGJ5IGRlZmF1bHQuDQogICAgICAgICAqIEBwYXJhbSAgICAgICAgJElHRGF0YVBhdGgNCiAgICAgICAgICogICBEZWZhdWx0IGZvbGRlciB0byBzdG9yZSBkYXRhLCB5b3UgY2FuIGNoYW5nZSBpdC4NCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gX19jb25zdHJ1Y3QoJHVzZXJuYW1lLCAkcGFzc3dvcmQsICRkZWJ1ZyA9IGZhbHNlLCAkSUdEYXRhUGF0aCA9IE5VTEwsICRmb3JjZVVzZXJJUCA9IGZhbHNlKQ0KICAgIHsNCiAgICAgICAgJHVzZXJuYW1lID0gdHJpbSgkdXNlcm5hbWUpOw0KICAgICAgICAkcGFzc3dvcmQgPSB0cmltKCRwYXNzd29yZCk7DQogICAgICAgICR0aGlzLT5kZWJ1ZyA9ICRkZWJ1ZzsNCiAgICAgICAgJHRoaXMtPmRldmljZV9pZCA9IFNpZ25hdHVyZVV0aWxzOjpnZW5lcmF0ZURldmljZUlkKG1kNSgkdXNlcm5hbWUgLiAkcGFzc3dvcmQpKTsNCiAgICAgICAgaWYoICFpc19udWxsKCRJR0RhdGFQYXRoKSApIA0KICAgICAgICB7DQogICAgICAgICAgICAkdGhpcy0+SUdEYXRhUGF0aCA9ICRJR0RhdGFQYXRoOw0KICAgICAgICB9DQogICAgICAgIGVsc2UNCiAgICAgICAgew0KICAgICAgICAgICAgJHRoaXMtPklHRGF0YVBhdGggPSBXb3c6OmdldCgicHJvamVjdC9jb29raWVQYXRoIikgLiAiaW5zdGFncmFtLyI7DQogICAgICAgIH0NCg0KICAgICAgICAkdGhpcy0+c2V0VXNlcigkdXNlcm5hbWUsICRwYXNzd29yZCwgJGZvcmNlVXNlcklQKTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogU2V0IHRoZSB1c2VyLiBNYW5hZ2UgbXVsdGlwbGUgYWNjb3VudHMuDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJHVzZXJuYW1lDQogICAgICAgICAqICAgWW91ciBJbnN0YWdyYW0gdXNlcm5hbWUuDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJHBhc3N3b3JkDQogICAgICAgICAqICAgWW91ciBJbnN0YWdyYW0gcGFzc3dvcmQuDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIHNldFVzZXIoJHVzZXJuYW1lLCAkcGFzc3dvcmQsICRmb3JjZVVzZXJJUCA9IGZhbHNlKQ0KICAgIHsNCiAgICAgICAgJHRoaXMtPnVzZXJuYW1lID0gJHVzZXJuYW1lOw0KICAgICAgICAkdGhpcy0+cGFzc3dvcmQgPSAkcGFzc3dvcmQ7DQogICAgICAgICR0aGlzLT5jaGVja1NldHRpbmdzKCR1c2VybmFtZSwgJGZvcmNlVXNlcklQKTsNCiAgICAgICAgJHRoaXMtPnV1aWQgPSBTaWduYXR1cmVVdGlsczo6Z2VuZXJhdGVVVUlEKHRydWUpOw0KICAgICAgICBpZiggZmlsZV9leGlzdHMoJHRoaXMtPklHRGF0YVBhdGggLiAoc3RyaW5nKSAkdGhpcy0+dXNlcm5hbWUgLiAiLmRhdCIpICYmICR0aGlzLT5zZXR0aW5ncy0+Z2V0KCJ1c2VybmFtZV9pZCIpICE9IE5VTEwgJiYgJHRoaXMtPnNldHRpbmdzLT5nZXQoInRva2VuIikgIT0gTlVMTCApIA0KICAgICAgICB7DQogICAgICAgICAgICAkdGhpcy0+aXNMb2dnZWRJbiA9IHRydWU7DQogICAgICAgICAgICAkdGhpcy0+dXNlcm5hbWVfaWQgPSAkdGhpcy0+c2V0dGluZ3MtPmdldCgidXNlcm5hbWVfaWQiKTsNCiAgICAgICAgICAgICR0aGlzLT5yYW5rX3Rva2VuID0gJHRoaXMtPnVzZXJuYW1lX2lkIC4gIl8iIC4gJHRoaXMtPnV1aWQ7DQogICAgICAgICAgICAkdGhpcy0+dG9rZW4gPSAkdGhpcy0+c2V0dGluZ3MtPmdldCgidG9rZW4iKTsNCiAgICAgICAgfQ0KICAgICAgICBlbHNlDQogICAgICAgIHsNCiAgICAgICAgICAgICR0aGlzLT5pc0xvZ2dlZEluID0gZmFsc2U7DQogICAgICAgIH0NCg0KICAgIH0NCg0KICAgIHByb3RlY3RlZCBmdW5jdGlvbiBjaGVja1NldHRpbmdzKCR1c2VybmFtZSwgJGZvcmNlVXNlcklQID0gZmFsc2UpDQogICAgew0KICAgICAgICAkdGhpcy0+c2V0dGluZ3MgPSBuZXcgU2V0dGluZ3MoJHRoaXMtPklHRGF0YVBhdGggLiAkdXNlcm5hbWUgLiAiLmNuZiIpOw0KICAgICAgICBpZiggJHRoaXMtPnNldHRpbmdzLT5nZXQoInZlcnNpb24iKSA9PSBOVUxMICkgDQogICAgICAgIHsNCiAgICAgICAgICAgICR0aGlzLT5zZXR0aW5ncy0+c2V0KCJ2ZXJzaW9uIiwgQ29uc3RhbnRzOjpWRVJTSU9OKTsNCiAgICAgICAgfQ0KDQogICAgICAgIGlmKCAkdGhpcy0+c2V0dGluZ3MtPmdldCgiaXAiKSA9PSBOVUxMIHx8ICRmb3JjZVVzZXJJUCApIA0KICAgICAgICB7DQogICAgICAgICAgICAkaXBBZHJlc3MgPSAiNzguIiAuIHJhbmQoMTYwLCAxOTEpIC4gIi4iIC4gcmFuZCgxLCAyNTUpIC4gIi4iIC4gcmFuZCgxLCAyNTUpOw0KICAgICAgICAgICAgaWYoICRmb3JjZVVzZXJJUCAmJiAhZW1wdHkoJF9TRVJWRVJbIlJFTU9URV9BRERSIl0pICkgDQogICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgJGlwQWRyZXNzID0gJF9TRVJWRVJbIlJFTU9URV9BRERSIl07DQogICAgICAgICAgICB9DQoNCiAgICAgICAgICAgICR0aGlzLT5zZXR0aW5ncy0+c2V0KCJpcCIsICRpcEFkcmVzcyk7DQogICAgICAgIH0NCg0KICAgICAgICBpZiggZmlsZV9leGlzdHMoJHRoaXMtPklHRGF0YVBhdGggLiAoc3RyaW5nKSAkdXNlcm5hbWUgLiAiLmRhdCIpICYmICgkdGhpcy0+c2V0dGluZ3MtPmdldCgidXNlcm5hbWVfaWQiKSA9PSBOVUxMIHx8ICR0aGlzLT5zZXR0aW5ncy0+Z2V0KCJ0b2tlbiIpID09IE5VTEwpICkgDQogICAgICAgIHsNCiAgICAgICAgICAgICRjb29raWVEYXRhID0gVXRpbHM6OmNvb2tpZVRvQXJyYXkoZmlsZV9nZXRfY29udGVudHMoJHRoaXMtPklHRGF0YVBhdGggLiAoc3RyaW5nKSAkdXNlcm5hbWUgLiAiLmRhdCIpLCAiaS5pbnN0YWdyYW0uY29tIik7DQogICAgICAgICAgICBpZiggJHRoaXMtPnNldHRpbmdzLT5nZXQoInVzZXJuYW1lX2lkIikgPT0gTlVMTCAmJiBpc3NldCgkY29va2llRGF0YVsiZHNfdXNlcl9pZCJdKSApIA0KICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICR0aGlzLT5zZXR0aW5ncy0+c2V0KCJ1c2VybmFtZV9pZCIsICRjb29raWVEYXRhWyJkc191c2VyX2lkIl0pOw0KICAgICAgICAgICAgfQ0KDQogICAgICAgICAgICBpZiggJHRoaXMtPnNldHRpbmdzLT5nZXQoInRva2VuIikgPT0gTlVMTCAmJiBpc3NldCgkY29va2llRGF0YVsiY3NyZnRva2VuIl0pICkgDQogICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgJHRoaXMtPnNldHRpbmdzLT5zZXQoInRva2VuIiwgJGNvb2tpZURhdGFbImNzcmZ0b2tlbiJdKTsNCiAgICAgICAgICAgIH0NCg0KICAgICAgICB9DQoNCiAgICAgICAgaWYoICR0aGlzLT5zZXR0aW5ncy0+Z2V0KCJ1c2VyX2FnZW50IikgPT0gTlVMTCB8fCB2ZXJzaW9uX2NvbXBhcmUoJHRoaXMtPnNldHRpbmdzLT5nZXQoInZlcnNpb24iKSwgQ29uc3RhbnRzOjpWRVJTSU9OKSA9PSAtMSApIA0KICAgICAgICB7DQogICAgICAgICAgICAkdXNlckFnZW50ID0gbmV3IFVzZXJBZ2VudCgkdGhpcyk7DQogICAgICAgICAgICAkdWEgPSAkdXNlckFnZW50LT5idWlsZFVzZXJBZ2VudCgpOw0KICAgICAgICAgICAgJHRoaXMtPnNldHRpbmdzLT5zZXQoInZlcnNpb24iLCBDb25zdGFudHM6OlZFUlNJT04pOw0KICAgICAgICAgICAgJHRoaXMtPnNldHRpbmdzLT5zZXQoInVzZXJfYWdlbnQiLCAkdWEpOw0KICAgICAgICB9DQoNCiAgICB9DQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gZ2V0RGF0YSgpDQogICAgew0KICAgICAgICByZXR1cm4gYXJyYXkoICJ1c2VybmFtZSIgPT4gJHRoaXMtPnVzZXJuYW1lLCAicGFzc3dvcmQiID0+ICR0aGlzLT5wYXNzd29yZCwgInVzZXJuYW1lX2lkIiA9PiAkdGhpcy0+dXNlcm5hbWVfaWQsICJ1dWlkIiA9PiAkdGhpcy0+dXVpZCwgInRva2VuIiA9PiAkdGhpcy0+dG9rZW4sICJyYW5rX3Rva2VuIiA9PiAkdGhpcy0+cmFua190b2tlbiwgInVzZXJfYWdlbnQiID0+ICR0aGlzLT5zZXR0aW5ncy0+Z2V0KCJ1c2VyX2FnZW50IiksICJpcCIgPT4gJHRoaXMtPnNldHRpbmdzLT5nZXQoImlwIikgKTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogTG9naW4gdG8gSW5zdGFncmFtLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcGFyYW0gYm9vbCAkZm9yY2UNCiAgICAgICAgICogICBGb3JjZSBsb2dpbiB0byBJbnN0YWdyYW0sIHRoaXMgd2lsbCBjcmVhdGUgYSBuZXcgc2Vzc2lvbg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgIExvZ2luIGRhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gbG9naW4oJGZvcmNlID0gZmFsc2UpDQogICAgew0KICAgICAgICBpZiggISR0aGlzLT5pc0xvZ2dlZEluIHx8ICRmb3JjZSApIA0KICAgICAgICB7DQogICAgICAgICAgICAkZmV0Y2ggPSAkdGhpcy0+cmVxdWVzdCgic2kvZmV0Y2hfaGVhZGVycy8/Y2hhbGxlbmdlX3R5cGU9c2lnbnVwJmd1aWQ9IiAuIFNpZ25hdHVyZVV0aWxzOjpnZW5lcmF0ZVVVSUQoZmFsc2UpLCBOVUxMLCB0cnVlKTsNCiAgICAgICAgICAgIHByZWdfbWF0Y2goIiNTZXQtQ29va2llOiBjc3JmdG9rZW49KFteO10rKSMiLCAkZmV0Y2hbMF0sICR0b2tlbik7DQogICAgICAgICAgICAkZGF0YSA9IGFycmF5KCAicGhvbmVfaWQiID0+IFNpZ25hdHVyZVV0aWxzOjpnZW5lcmF0ZVVVSUQodHJ1ZSksICJfY3NyZnRva2VuIiA9PiAkdG9rZW5bMF0sICJ1c2VybmFtZSIgPT4gJHRoaXMtPnVzZXJuYW1lLCAiZ3VpZCIgPT4gJHRoaXMtPnV1aWQsICJkZXZpY2VfaWQiID0+ICR0aGlzLT5kZXZpY2VfaWQsICJwYXNzd29yZCIgPT4gJHRoaXMtPnBhc3N3b3JkLCAibG9naW5fYXR0ZW1wdF9jb3VudCIgPT4gIjAiICk7DQogICAgICAgICAgICAkbG9naW4gPSAkdGhpcy0+cmVxdWVzdCgiYWNjb3VudHMvbG9naW4vIiwgU2lnbmF0dXJlVXRpbHM6OmdlbmVyYXRlU2lnbmF0dXJlKGpzb25fZW5jb2RlKCRkYXRhKSksIHRydWUpOw0KICAgICAgICAgICAgaWYoICRsb2dpblsxXVsic3RhdHVzIl0gPT0gImZhaWwiICkgDQogICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgdGhyb3cgbmV3IEV4Y2VwdGlvbigkbG9naW5bMV1bIm1lc3NhZ2UiXSk7DQogICAgICAgICAgICB9DQoNCiAgICAgICAgICAgICR0aGlzLT5pc0xvZ2dlZEluID0gdHJ1ZTsNCiAgICAgICAgICAgICR0aGlzLT51c2VybmFtZV9pZCA9ICRsb2dpblsxXVsibG9nZ2VkX2luX3VzZXIiXVsicGsiXTsNCiAgICAgICAgICAgICR0aGlzLT5zZXR0aW5ncy0+c2V0KCJ1c2VybmFtZV9pZCIsICR0aGlzLT51c2VybmFtZV9pZCk7DQogICAgICAgICAgICAkdGhpcy0+cmFua190b2tlbiA9ICR0aGlzLT51c2VybmFtZV9pZCAuICJfIiAuICR0aGlzLT51dWlkOw0KICAgICAgICAgICAgcHJlZ19tYXRjaCgiI1NldC1Db29raWU6IGNzcmZ0b2tlbj0oW147XSspIyIsICRsb2dpblswXSwgJG1hdGNoKTsNCiAgICAgICAgICAgICR0aGlzLT50b2tlbiA9ICRtYXRjaFsxXTsNCiAgICAgICAgICAgICR0aGlzLT5zZXR0aW5ncy0+c2V0KCJ0b2tlbiIsICR0aGlzLT50b2tlbik7DQogICAgICAgICAgICAkdGhpcy0+c3luY0ZlYXR1cmVzKCk7DQogICAgICAgICAgICAkdGhpcy0+bWVnYXBob25lTG9nKCk7DQogICAgICAgICAgICByZXR1cm4gJGxvZ2luWzFdOw0KICAgICAgICB9DQoNCiAgICAgICAgJGNoZWNrID0gJHRoaXMtPnRpbWVsaW5lRmVlZCgpOw0KICAgICAgICBpZiggaXNzZXQoJGNoZWNrWyJtZXNzYWdlIl0pICYmICRjaGVja1sibWVzc2FnZSJdID09ICJsb2dpbl9yZXF1aXJlZCIgKSANCiAgICAgICAgew0KICAgICAgICAgICAgcmV0dXJuICR0aGlzLT5sb2dpbih0cnVlKTsNCiAgICAgICAgfQ0KDQogICAgICAgICR0aGlzLT5tZWdhcGhvbmVMb2coKTsNCiAgICAgICAgcmV0dXJuIGFycmF5KCAic3RhdHVzIiA9PiAib2siICk7DQogICAgfQ0KDQogICAgcHVibGljIGZ1bmN0aW9uIHN5bmNGZWF0dXJlcygpDQogICAgew0KICAgICAgICAkZGF0YSA9IGpzb25fZW5jb2RlKGFycmF5KCAiX3V1aWQiID0+ICR0aGlzLT51dWlkLCAiX3VpZCIgPT4gJHRoaXMtPnVzZXJuYW1lX2lkLCAiaWQiID0+ICR0aGlzLT51c2VybmFtZV9pZCwgIl9jc3JmdG9rZW4iID0+ICR0aGlzLT50b2tlbiwgImV4cGVyaW1lbnRzIiA9PiBDb25zdGFudHM6OkVYUEVSSU1FTlRTICkpOw0KICAgICAgICByZXR1cm4gJHRoaXMtPnJlcXVlc3QoInFlL3N5bmMvIiwgU2lnbmF0dXJlVXRpbHM6OmdlbmVyYXRlU2lnbmF0dXJlKCRkYXRhKSlbMV07DQogICAgfQ0KDQogICAgcHJvdGVjdGVkIGZ1bmN0aW9uIGF1dG9Db21wbGV0ZVVzZXJMaXN0KCkNCiAgICB7DQogICAgICAgIHJldHVybiAkdGhpcy0+cmVxdWVzdCgiZnJpZW5kc2hpcHMvYXV0b2NvbXBsZXRlX3VzZXJfbGlzdC8iKVsxXTsNCiAgICB9DQoNCiAgICBwcm90ZWN0ZWQgZnVuY3Rpb24gdGltZWxpbmVGZWVkKCkNCiAgICB7DQogICAgICAgIHJldHVybiAkdGhpcy0+cmVxdWVzdCgiZmVlZC90aW1lbGluZS8iKVsxXTsNCiAgICB9DQoNCiAgICBwcm90ZWN0ZWQgZnVuY3Rpb24gbWVnYXBob25lTG9nKCkNCiAgICB7DQogICAgICAgIHJldHVybiAkdGhpcy0+cmVxdWVzdCgibWVnYXBob25lL2xvZy8iKVsxXTsNCiAgICB9DQoNCiAgICBwcm90ZWN0ZWQgZnVuY3Rpb24gZXhwb3NlKCkNCiAgICB7DQogICAgICAgICRkYXRhID0ganNvbl9lbmNvZGUoYXJyYXkoICJfdXVpZCIgPT4gJHRoaXMtPnV1aWQsICJfdWlkIiA9PiAkdGhpcy0+dXNlcm5hbWVfaWQsICJpZCIgPT4gJHRoaXMtPnVzZXJuYW1lX2lkLCAiX2NzcmZ0b2tlbiIgPT4gJHRoaXMtPnRva2VuLCAiZXhwZXJpbWVudCIgPT4gImlnX2FuZHJvaWRfcHJvZmlsZV9jb250ZXh0dWFsX2ZlZWQiICkpOw0KICAgICAgICAkdGhpcy0+cmVxdWVzdCgicWUvZXhwb3NlLyIsIFNpZ25hdHVyZVV0aWxzOjpnZW5lcmF0ZVNpZ25hdHVyZSgkZGF0YSkpOw0KICAgICAgICAkdGhpcy0+cmVxdWVzdCgicWUvZXhwb3NlLyIsIFNpZ25hdHVyZVV0aWxzOjpnZW5lcmF0ZVNpZ25hdHVyZSgkZGF0YSkpWzFdOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBMb2dpbiB0byBJbnN0YWdyYW0uDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYm9vbA0KICAgICAgICAgKiAgICBSZXR1cm5zIHRydWUgaWYgbG9nZ2VkIG91dCBjb3JyZWN0bHkNCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gbG9nb3V0KCkNCiAgICB7DQogICAgICAgICRsb2dvdXQgPSAkdGhpcy0+cmVxdWVzdCgiYWNjb3VudHMvbG9nb3V0LyIpOw0KICAgICAgICBpZiggJGxvZ291dCA9PSAib2siICkgDQogICAgICAgIHsNCiAgICAgICAgICAgIHJldHVybiB0cnVlOw0KICAgICAgICB9DQoNCiAgICAgICAgcmV0dXJuIGZhbHNlOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBVcGxvYWQgcGhvdG8gdG8gSW5zdGFncmFtLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICRwaG90bw0KICAgICAgICAgKiAgICAgICAgICAgICAgICAgICAgICAgIFBhdGggdG8geW91ciBwaG90bw0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICRjYXB0aW9uDQogICAgICAgICAqICAgICAgICAgICAgICAgICAgICAgICAgQ2FwdGlvbiB0byBiZSBpbmNsdWRlZCBpbiB5b3VyIHBob3RvLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgICAgICAgICAgICAgVXBsb2FkIGRhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gdXBsb2FkUGhvdG8oJHBob3RvLCAkY2FwdGlvbiA9IE5VTEwsICR1cGxvYWRfaWQgPSBOVUxMKQ0KICAgIHsNCiAgICAgICAgJGVuZHBvaW50ID0gQ29uc3RhbnRzOjpBUElfVVJMIC4gInVwbG9hZC9waG90by8iOw0KICAgICAgICAkYm91bmRhcnkgPSAkdGhpcy0+dXVpZDsNCiAgICAgICAgaWYoICFpc19udWxsKCR1cGxvYWRfaWQpICkgDQogICAgICAgIHsNCiAgICAgICAgICAgICRmaWxlVG9VcGxvYWQgPSBVdGlsczo6Y3JlYXRlVmlkZW9JY29uKCRwaG90byk7DQogICAgICAgIH0NCiAgICAgICAgZWxzZQ0KICAgICAgICB7DQogICAgICAgICAgICAkdXBsb2FkX2lkID0gbnVtYmVyX2Zvcm1hdChyb3VuZChtaWNyb3RpbWUodHJ1ZSkgKiAxMDAwKSwgMCwgIiIsICIiKTsNCiAgICAgICAgICAgICRmaWxlVG9VcGxvYWQgPSBmaWxlX2dldF9jb250ZW50cygkcGhvdG8pOw0KICAgICAgICB9DQoNCiAgICAgICAgJGJvZGllcyA9IGFycmF5KCBhcnJheSggInR5cGUiID0+ICJmb3JtLWRhdGEiLCAibmFtZSIgPT4gInVwbG9hZF9pZCIsICJkYXRhIiA9PiAkdXBsb2FkX2lkICksIGFycmF5KCAidHlwZSIgPT4gImZvcm0tZGF0YSIsICJuYW1lIiA9PiAiX3V1aWQiLCAiZGF0YSIgPT4gJHRoaXMtPnV1aWQgKSwgYXJyYXkoICJ0eXBlIiA9PiAiZm9ybS1kYXRhIiwgIm5hbWUiID0+ICJfY3NyZnRva2VuIiwgImRhdGEiID0+ICR0aGlzLT50b2tlbiApLCBhcnJheSggInR5cGUiID0+ICJmb3JtLWRhdGEiLCAibmFtZSIgPT4gImltYWdlX2NvbXByZXNzaW9uIiwgImRhdGEiID0+ICJ7XCJsaWJfbmFtZVwiOlwianRcIixcImxpYl92ZXJzaW9uXCI6XCIxLjMuMFwiLFwicXVhbGl0eVwiOlwiNzBcIn0iICksIGFycmF5KCAidHlwZSIgPT4gImZvcm0tZGF0YSIsICJuYW1lIiA9PiAicGhvdG8iLCAiZGF0YSIgPT4gJGZpbGVUb1VwbG9hZCwgImZpbGVuYW1lIiA9PiAicGVuZGluZ19tZWRpYV8iIC4gbnVtYmVyX2Zvcm1hdChyb3VuZChtaWNyb3RpbWUodHJ1ZSkgKiAxMDAwKSwgMCwgIiIsICIiKSAuICIuanBnIiwgImhlYWRlcnMiID0+IGFycmF5KCAiQ29udGVudC1UcmFuc2Zlci1FbmNvZGluZzogYmluYXJ5IiwgIkNvbnRlbnQtdHlwZTogYXBwbGljYXRpb24vb2N0ZXQtc3RyZWFtIiApICkgKTsNCiAgICAgICAgJGRhdGEgPSAkdGhpcy0+YnVpbGRCb2R5KCRib2RpZXMsICRib3VuZGFyeSk7DQogICAgICAgICRoZWFkZXJzID0gYXJyYXkoICJYLUlHLUNhcGFiaWxpdGllczogIiAuIENvbnN0YW50czo6WF9JR19DYXBhYmlsaXRpZXMsICJYLUlHLUNvbm5lY3Rpb24tVHlwZTogV0lGSSIsICJDb250ZW50LXR5cGU6IG11bHRpcGFydC9mb3JtLWRhdGE7IGJvdW5kYXJ5PSIgLiAkYm91bmRhcnksICJDb250ZW50LUxlbmd0aDogIiAuIHN0cmxlbigkZGF0YSksICJBY2NlcHQtTGFuZ3VhZ2U6IHRyLVRSIiwgIkFjY2VwdC1FbmNvZGluZzogZ3ppcCwgZGVmbGF0ZSIsICJDb25uZWN0aW9uOiBjbG9zZSIsICJYX0ZPUldBUkRFRF9GT1I6ICIgLiAkdGhpcy0+c2V0dGluZ3MtPmdldCgiaXAiKSwgIlJFTU9URV9BRERSOiAiIC4gJHRoaXMtPnNldHRpbmdzLT5nZXQoImlwIikgKTsNCiAgICAgICAgJGNoID0gY3VybF9pbml0KCk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9VUkwsICRlbmRwb2ludCk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9VU0VSQUdFTlQsICR0aGlzLT5zZXR0aW5ncy0+Z2V0KCJ1c2VyX2FnZW50IikpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfUkVUVVJOVFJBTlNGRVIsIHRydWUpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfRk9MTE9XTE9DQVRJT04sIHRydWUpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfSEVBREVSLCB0cnVlKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1ZFUkJPU0UsICR0aGlzLT5kZWJ1Zyk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9TU0xfVkVSSUZZUEVFUiwgZmFsc2UpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfU1NMX1ZFUklGWUhPU1QsIGZhbHNlKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX0hUVFBIRUFERVIsICRoZWFkZXJzKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX0NPT0tJRUZJTEUsICR0aGlzLT5JR0RhdGFQYXRoIC4gKHN0cmluZykgJHRoaXMtPnVzZXJuYW1lIC4gIi5kYXQiKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX0NPT0tJRUpBUiwgJHRoaXMtPklHRGF0YVBhdGggLiAoc3RyaW5nKSAkdGhpcy0+dXNlcm5hbWUgLiAiLmRhdCIpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfUE9TVCwgdHJ1ZSk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9QT1NURklFTERTLCAkZGF0YSk7DQogICAgICAgICRyZXNwID0gY3VybF9leGVjKCRjaCk7DQogICAgICAgICRoZWFkZXJfbGVuID0gY3VybF9nZXRpbmZvKCRjaCwgQ1VSTElORk9fSEVBREVSX1NJWkUpOw0KICAgICAgICAkaGVhZGVyID0gc3Vic3RyKCRyZXNwLCAwLCAkaGVhZGVyX2xlbik7DQogICAgICAgICR1cGxvYWQgPSBqc29uX2RlY29kZShzdWJzdHIoJHJlc3AsICRoZWFkZXJfbGVuKSwgdHJ1ZSk7DQogICAgICAgIGN1cmxfY2xvc2UoJGNoKTsNCiAgICAgICAgaWYoICR1cGxvYWRbInN0YXR1cyJdID09ICJmYWlsIiApIA0KICAgICAgICB7DQogICAgICAgICAgICB0aHJvdyBuZXcgRXhjZXB0aW9uKCR1cGxvYWRbIm1lc3NhZ2UiXSk7DQogICAgICAgIH0NCg0KICAgICAgICBpZiggJHRoaXMtPmRlYnVnICkgDQogICAgICAgIHsNCiAgICAgICAgICAgIGVjaG8gIlJFU1BPTlNFOiAiIC4gc3Vic3RyKCRyZXNwLCAkaGVhZGVyX2xlbikgLiAiXG5cbiI7DQogICAgICAgIH0NCg0KICAgICAgICAkY29uZmlndXJlID0gJHRoaXMtPmNvbmZpZ3VyZSgkdXBsb2FkWyJ1cGxvYWRfaWQiXSwgJHBob3RvLCAkY2FwdGlvbik7DQogICAgICAgICR0aGlzLT5leHBvc2UoKTsNCiAgICAgICAgcmV0dXJuICRjb25maWd1cmU7DQogICAgfQ0KDQogICAgcHVibGljIGZ1bmN0aW9uIHVwbG9hZFZpZGVvKCR2aWRlbywgJGNhcHRpb24gPSBOVUxMKQ0KICAgIHsNCiAgICAgICAgJHZpZGVvRGF0YSA9IGZpbGVfZ2V0X2NvbnRlbnRzKCR2aWRlbyk7DQogICAgICAgICRlbmRwb2ludCA9IENvbnN0YW50czo6QVBJX1VSTCAuICJ1cGxvYWQvdmlkZW8vIjsNCiAgICAgICAgJGJvdW5kYXJ5ID0gJHRoaXMtPnV1aWQ7DQogICAgICAgICR1cGxvYWRfaWQgPSByb3VuZChtaWNyb3RpbWUodHJ1ZSkgKiAxMDAwKTsNCiAgICAgICAgJGJvZGllcyA9IGFycmF5KCBhcnJheSggInR5cGUiID0+ICJmb3JtLWRhdGEiLCAibmFtZSIgPT4gInVwbG9hZF9pZCIsICJkYXRhIiA9PiAkdXBsb2FkX2lkICksIGFycmF5KCAidHlwZSIgPT4gImZvcm0tZGF0YSIsICJuYW1lIiA9PiAiX2NzcmZ0b2tlbiIsICJkYXRhIiA9PiAkdGhpcy0+dG9rZW4gKSwgYXJyYXkoICJ0eXBlIiA9PiAiZm9ybS1kYXRhIiwgIm5hbWUiID0+ICJtZWRpYV90eXBlIiwgImRhdGEiID0+ICIyIiApLCBhcnJheSggInR5cGUiID0+ICJmb3JtLWRhdGEiLCAibmFtZSIgPT4gIl91dWlkIiwgImRhdGEiID0+ICR0aGlzLT51dWlkICkgKTsNCiAgICAgICAgJGRhdGEgPSAkdGhpcy0+YnVpbGRCb2R5KCRib2RpZXMsICRib3VuZGFyeSk7DQogICAgICAgICRoZWFkZXJzID0gYXJyYXkoICJDb25uZWN0aW9uOiBrZWVwLWFsaXZlIiwgIkFjY2VwdDogKi8qIiwgIkhvc3Q6IGkuaW5zdGFncmFtLmNvbSIsICJDb250ZW50LXR5cGU6IG11bHRpcGFydC9mb3JtLWRhdGE7IGJvdW5kYXJ5PSIgLiAkYm91bmRhcnksICJBY2NlcHQtTGFuZ3VhZ2U6IHRyLVRSIiwgIlhfRk9SV0FSREVEX0ZPUjogIiAuICR0aGlzLT5zZXR0aW5ncy0+Z2V0KCJpcCIpLCAiUkVNT1RFX0FERFI6ICIgLiAkdGhpcy0+c2V0dGluZ3MtPmdldCgiaXAiKSApOw0KICAgICAgICAkY2ggPSBjdXJsX2luaXQoKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1VSTCwgJGVuZHBvaW50KTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1VTRVJBR0VOVCwgJHRoaXMtPnNldHRpbmdzLT5nZXQoInVzZXJfYWdlbnQiKSk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9SRVRVUk5UUkFOU0ZFUiwgdHJ1ZSk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9GT0xMT1dMT0NBVElPTiwgdHJ1ZSk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9IRUFERVIsIHRydWUpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfVkVSQk9TRSwgZmFsc2UpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfSFRUUEhFQURFUiwgJGhlYWRlcnMpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfQ09PS0lFRklMRSwgJHRoaXMtPklHRGF0YVBhdGggLiAoc3RyaW5nKSAkdGhpcy0+dXNlcm5hbWUgLiAiLmRhdCIpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfQ09PS0lFSkFSLCAkdGhpcy0+SUdEYXRhUGF0aCAuIChzdHJpbmcpICR0aGlzLT51c2VybmFtZSAuICIuZGF0Iik7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9QT1NULCB0cnVlKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1BPU1RGSUVMRFMsICRkYXRhKTsNCiAgICAgICAgJHJlc3AgPSBjdXJsX2V4ZWMoJGNoKTsNCiAgICAgICAgJGhlYWRlcl9sZW4gPSBjdXJsX2dldGluZm8oJGNoLCBDVVJMSU5GT19IRUFERVJfU0laRSk7DQogICAgICAgICRib2R5ID0ganNvbl9kZWNvZGUoc3Vic3RyKCRyZXNwLCAkaGVhZGVyX2xlbiksIHRydWUpOw0KICAgICAgICAkdXBsb2FkVXJsID0gJGJvZHlbInZpZGVvX3VwbG9hZF91cmxzIl1bM11bInVybCJdOw0KICAgICAgICAkam9iID0gJGJvZHlbInZpZGVvX3VwbG9hZF91cmxzIl1bM11bImpvYiJdOw0KICAgICAgICAkcmVxdWVzdF9zaXplID0gZmxvb3Ioc3RybGVuKCR2aWRlb0RhdGEpIC8gNCk7DQogICAgICAgICRsYXN0UmVxdWVzdEV4dHJhID0gc3RybGVuKCR2aWRlb0RhdGEpIC0gJHJlcXVlc3Rfc2l6ZSAqIDQ7DQogICAgICAgIGZvciggJGEgPSAwOyAkYSA8PSAzOyAkYSsrICkgDQogICAgICAgIHsNCiAgICAgICAgICAgICRzdGFydCA9ICRhICogJHJlcXVlc3Rfc2l6ZTsNCiAgICAgICAgICAgICRlbmQgPSAoJGEgKyAxKSAqICRyZXF1ZXN0X3NpemUgKyAoKCRhID09IDMgPyAkbGFzdFJlcXVlc3RFeHRyYSA6IDApKTsNCiAgICAgICAgICAgICRoZWFkZXJzID0gYXJyYXkoICJDb25uZWN0aW9uOiBrZWVwLWFsaXZlIiwgIkFjY2VwdDogKi8qIiwgIkhvc3Q6IHVwbG9hZC5pbnN0YWdyYW0uY29tIiwgIkNvb2tpZTI6IFwkVmVyc2lvbj0xIiwgIkFjY2VwdC1FbmNvZGluZzogZ3ppcCwgZGVmbGF0ZSIsICJDb250ZW50LVR5cGU6IGFwcGxpY2F0aW9uL29jdGV0LXN0cmVhbSIsICJTZXNzaW9uLUlEOiAiIC4gJHVwbG9hZF9pZCwgIkFjY2VwdC1MYW5ndWFnZTogdHItVFIiLCAiQ29udGVudC1EaXNwb3NpdGlvbjogYXR0YWNobWVudDsgZmlsZW5hbWU9XCJ2aWRlby5tb3ZcIiIsICJDb250ZW50LUxlbmd0aDogIiAuICgkZW5kIC0gJHN0YXJ0KSwgIkNvbnRlbnQtUmFuZ2U6ICIgLiAiYnl0ZXMgIiAuICRzdGFydCAuICItIiAuICgkZW5kIC0gMSkgLiAiLyIgLiBzdHJsZW4oJHZpZGVvRGF0YSksICJqb2I6ICIgLiAkam9iICk7DQogICAgICAgICAgICAkY2ggPSBjdXJsX2luaXQoKTsNCiAgICAgICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9VUkwsICR1cGxvYWRVcmwpOw0KICAgICAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1VTRVJBR0VOVCwgJHRoaXMtPnNldHRpbmdzLT5nZXQoInVzZXJfYWdlbnQiKSk7DQogICAgICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfQ1VTVE9NUkVRVUVTVCwgIlBPU1QiKTsNCiAgICAgICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9SRVRVUk5UUkFOU0ZFUiwgdHJ1ZSk7DQogICAgICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfRk9MTE9XTE9DQVRJT04sIHRydWUpOw0KICAgICAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX0hFQURFUiwgdHJ1ZSk7DQogICAgICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfVkVSQk9TRSwgZmFsc2UpOw0KICAgICAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX0hUVFBIRUFERVIsICRoZWFkZXJzKTsNCiAgICAgICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9DT09LSUVGSUxFLCAkdGhpcy0+SUdEYXRhUGF0aCAuIChzdHJpbmcpICR0aGlzLT51c2VybmFtZSAuICIuZGF0Iik7DQogICAgICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfQ09PS0lFSkFSLCAkdGhpcy0+SUdEYXRhUGF0aCAuIChzdHJpbmcpICR0aGlzLT51c2VybmFtZSAuICIuZGF0Iik7DQogICAgICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfUE9TVCwgdHJ1ZSk7DQogICAgICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfUE9TVEZJRUxEUywgc3Vic3RyKCR2aWRlb0RhdGEsICRzdGFydCwgJGVuZCkpOw0KICAgICAgICAgICAgJHJlc3VsdCA9IGN1cmxfZXhlYygkY2gpOw0KICAgICAgICAgICAgJGhlYWRlcl9sZW4gPSBjdXJsX2dldGluZm8oJGNoLCBDVVJMSU5GT19IRUFERVJfU0laRSk7DQogICAgICAgICAgICAkYm9keSA9IHN1YnN0cigkcmVzdWx0LCAkaGVhZGVyX2xlbik7DQogICAgICAgICAgICAkYXJyYXlbXSA9IGFycmF5KCAkYm9keSApOw0KICAgICAgICB9DQogICAgICAgICRyZXNwID0gY3VybF9leGVjKCRjaCk7DQogICAgICAgICRoZWFkZXJfbGVuID0gY3VybF9nZXRpbmZvKCRjaCwgQ1VSTElORk9fSEVBREVSX1NJWkUpOw0KICAgICAgICAkaGVhZGVyID0gc3Vic3RyKCRyZXNwLCAwLCAkaGVhZGVyX2xlbik7DQogICAgICAgICR1cGxvYWQgPSBqc29uX2RlY29kZShzdWJzdHIoJHJlc3AsICRoZWFkZXJfbGVuKSwgdHJ1ZSk7DQogICAgICAgIGN1cmxfY2xvc2UoJGNoKTsNCiAgICAgICAgaWYoICR1cGxvYWRbInN0YXR1cyJdID09ICJmYWlsIiApIA0KICAgICAgICB7DQogICAgICAgICAgICB0aHJvdyBuZXcgRXhjZXB0aW9uKCR1cGxvYWRbIm1lc3NhZ2UiXSk7DQogICAgICAgIH0NCg0KICAgICAgICBpZiggJHRoaXMtPmRlYnVnICkgDQogICAgICAgIHsNCiAgICAgICAgICAgIGVjaG8gIlJFU1BPTlNFOiAiIC4gc3Vic3RyKCRyZXNwLCAkaGVhZGVyX2xlbikgLiAiXG5cbiI7DQogICAgICAgIH0NCg0KICAgICAgICAkY29uZmlndXJlID0gJHRoaXMtPmNvbmZpZ3VyZVZpZGVvKCR1cGxvYWRfaWQsICR2aWRlbywgJGNhcHRpb24pOw0KICAgICAgICAkdGhpcy0+ZXhwb3NlKCk7DQogICAgICAgIHJldHVybiAkY29uZmlndXJlOw0KICAgIH0NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBkaXJlY3RfbWVzc2FnZSgkcmVjaXBpZW50cywgJHRleHQpDQogICAgew0KICAgICAgICBpZiggIWlzX2FycmF5KCRyZWNpcGllbnRzKSApIA0KICAgICAgICB7DQogICAgICAgICAgICAkcmVjaXBpZW50cyA9IGFycmF5KCAkcmVjaXBpZW50cyApOw0KICAgICAgICB9DQoNCiAgICAgICAgJHN0cmluZyA9IGFycmF5KCAgKTsNCiAgICAgICAgZm9yZWFjaCggJHJlY2lwaWVudHMgYXMgJHJlY2lwaWVudCApIA0KICAgICAgICB7DQogICAgICAgICAgICAkc3RyaW5nW10gPSAiXCIiIC4gJHJlY2lwaWVudCAuICJcIiI7DQogICAgICAgIH0NCiAgICAgICAgJHJlY2lwaWVudF91c2VycyA9IGltcGxvZGUoIiwiLCAkc3RyaW5nKTsNCiAgICAgICAgJGVuZHBvaW50ID0gQ29uc3RhbnRzOjpBUElfVVJMIC4gImRpcmVjdF92Mi90aHJlYWRzL2Jyb2FkY2FzdC90ZXh0LyI7DQogICAgICAgICRib3VuZGFyeSA9ICR0aGlzLT51dWlkOw0KICAgICAgICAkYm9kaWVzID0gYXJyYXkoIGFycmF5KCAidHlwZSIgPT4gImZvcm0tZGF0YSIsICJuYW1lIiA9PiAicmVjaXBpZW50X3VzZXJzIiwgImRhdGEiID0+ICJbWyIgLiAkcmVjaXBpZW50X3VzZXJzIC4gIl1dIiApLCBhcnJheSggInR5cGUiID0+ICJmb3JtLWRhdGEiLCAibmFtZSIgPT4gImNsaWVudF9jb250ZXh0IiwgImRhdGEiID0+ICR0aGlzLT51dWlkICksIGFycmF5KCAidHlwZSIgPT4gImZvcm0tZGF0YSIsICJuYW1lIiA9PiAidGhyZWFkX2lkcyIsICJkYXRhIiA9PiAiW1wiMFwiXSIgKSwgYXJyYXkoICJ0eXBlIiA9PiAiZm9ybS1kYXRhIiwgIm5hbWUiID0+ICJ0ZXh0IiwgImRhdGEiID0+IChpc19udWxsKCR0ZXh0KSA/ICIiIDogJHRleHQpICkgKTsNCiAgICAgICAgJGRhdGEgPSAkdGhpcy0+YnVpbGRCb2R5KCRib2RpZXMsICRib3VuZGFyeSk7DQogICAgICAgICRoZWFkZXJzID0gYXJyYXkoICJQcm94eS1Db25uZWN0aW9uOiBrZWVwLWFsaXZlIiwgIkNvbm5lY3Rpb246IGtlZXAtYWxpdmUiLCAiQWNjZXB0OiAqLyoiLCAiQ29udGVudC10eXBlOiBtdWx0aXBhcnQvZm9ybS1kYXRhOyBib3VuZGFyeT0iIC4gJGJvdW5kYXJ5LCAiQWNjZXB0LUxhbmd1YWdlOiB0ci1UUiIgKTsNCiAgICAgICAgJGNoID0gY3VybF9pbml0KCk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9VUkwsICRlbmRwb2ludCk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9VU0VSQUdFTlQsICR0aGlzLT5zZXR0aW5ncy0+Z2V0KCJ1c2VyX2FnZW50IikpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfUkVUVVJOVFJBTlNGRVIsIHRydWUpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfRk9MTE9XTE9DQVRJT04sIHRydWUpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfSEVBREVSLCB0cnVlKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1ZFUkJPU0UsICR0aGlzLT5kZWJ1Zyk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9TU0xfVkVSSUZZUEVFUiwgZmFsc2UpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfU1NMX1ZFUklGWUhPU1QsIGZhbHNlKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX0hUVFBIRUFERVIsICRoZWFkZXJzKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX0NPT0tJRUZJTEUsICR0aGlzLT5JR0RhdGFQYXRoIC4gKHN0cmluZykgJHRoaXMtPnVzZXJuYW1lIC4gIi5kYXQiKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX0NPT0tJRUpBUiwgJHRoaXMtPklHRGF0YVBhdGggLiAoc3RyaW5nKSAkdGhpcy0+dXNlcm5hbWUgLiAiLmRhdCIpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfUE9TVCwgdHJ1ZSk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9QT1NURklFTERTLCAkZGF0YSk7DQogICAgICAgICRyZXNwID0gY3VybF9leGVjKCRjaCk7DQogICAgICAgICRoZWFkZXJfbGVuID0gY3VybF9nZXRpbmZvKCRjaCwgQ1VSTElORk9fSEVBREVSX1NJWkUpOw0KICAgICAgICAkaGVhZGVyID0gc3Vic3RyKCRyZXNwLCAwLCAkaGVhZGVyX2xlbik7DQogICAgICAgICR1cGxvYWQgPSBqc29uX2RlY29kZShzdWJzdHIoJHJlc3AsICRoZWFkZXJfbGVuKSwgdHJ1ZSk7DQogICAgICAgIGN1cmxfY2xvc2UoJGNoKTsNCiAgICB9DQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gZGlyZWN0X3NoYXJlKCRtZWRpYV9pZCwgJHJlY2lwaWVudHMsICR0ZXh0ID0gTlVMTCkNCiAgICB7DQogICAgICAgIGlmKCAhaXNfYXJyYXkoJHJlY2lwaWVudHMpICkgDQogICAgICAgIHsNCiAgICAgICAgICAgICRyZWNpcGllbnRzID0gYXJyYXkoICRyZWNpcGllbnRzICk7DQogICAgICAgIH0NCg0KICAgICAgICAkc3RyaW5nID0gYXJyYXkoICApOw0KICAgICAgICBmb3JlYWNoKCAkcmVjaXBpZW50cyBhcyAkcmVjaXBpZW50ICkgDQogICAgICAgIHsNCiAgICAgICAgICAgICRzdHJpbmdbXSA9ICJcIiIgLiAkcmVjaXBpZW50IC4gIlwiIjsNCiAgICAgICAgfQ0KICAgICAgICAkcmVjaXBpZW50X3VzZXJzID0gaW1wbG9kZSgiLCIsICRzdHJpbmcpOw0KICAgICAgICAkZW5kcG9pbnQgPSBDb25zdGFudHM6OkFQSV9VUkwgLiAiZGlyZWN0X3YyL3RocmVhZHMvYnJvYWRjYXN0L21lZGlhX3NoYXJlLz9tZWRpYV90eXBlPXBob3RvIjsNCiAgICAgICAgJGJvdW5kYXJ5ID0gJHRoaXMtPnV1aWQ7DQogICAgICAgICRib2RpZXMgPSBhcnJheSggYXJyYXkoICJ0eXBlIiA9PiAiZm9ybS1kYXRhIiwgIm5hbWUiID0+ICJtZWRpYV9pZCIsICJkYXRhIiA9PiAkbWVkaWFfaWQgKSwgYXJyYXkoICJ0eXBlIiA9PiAiZm9ybS1kYXRhIiwgIm5hbWUiID0+ICJyZWNpcGllbnRfdXNlcnMiLCAiZGF0YSIgPT4gIltbIiAuICRyZWNpcGllbnRfdXNlcnMgLiAiXV0iICksIGFycmF5KCAidHlwZSIgPT4gImZvcm0tZGF0YSIsICJuYW1lIiA9PiAiY2xpZW50X2NvbnRleHQiLCAiZGF0YSIgPT4gJHRoaXMtPnV1aWQgKSwgYXJyYXkoICJ0eXBlIiA9PiAiZm9ybS1kYXRhIiwgIm5hbWUiID0+ICJ0aHJlYWRfaWRzIiwgImRhdGEiID0+ICJbXCIwXCJdIiApLCBhcnJheSggInR5cGUiID0+ICJmb3JtLWRhdGEiLCAibmFtZSIgPT4gInRleHQiLCAiZGF0YSIgPT4gKGlzX251bGwoJHRleHQpID8gIiIgOiAkdGV4dCkgKSApOw0KICAgICAgICAkZGF0YSA9ICR0aGlzLT5idWlsZEJvZHkoJGJvZGllcywgJGJvdW5kYXJ5KTsNCiAgICAgICAgJGhlYWRlcnMgPSBhcnJheSggIlByb3h5LUNvbm5lY3Rpb246IGtlZXAtYWxpdmUiLCAiQ29ubmVjdGlvbjoga2VlcC1hbGl2ZSIsICJBY2NlcHQ6ICovKiIsICJDb250ZW50LXR5cGU6IG11bHRpcGFydC9mb3JtLWRhdGE7IGJvdW5kYXJ5PSIgLiAkYm91bmRhcnksICJBY2NlcHQtTGFuZ3VhZ2U6IHRyLVRSIiApOw0KICAgICAgICAkY2ggPSBjdXJsX2luaXQoKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1VSTCwgJGVuZHBvaW50KTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1VTRVJBR0VOVCwgJHRoaXMtPnNldHRpbmdzLT5nZXQoInVzZXJfYWdlbnQiKSk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9SRVRVUk5UUkFOU0ZFUiwgdHJ1ZSk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9GT0xMT1dMT0NBVElPTiwgdHJ1ZSk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9IRUFERVIsIHRydWUpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfVkVSQk9TRSwgJHRoaXMtPmRlYnVnKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1NTTF9WRVJJRllQRUVSLCBmYWxzZSk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9TU0xfVkVSSUZZSE9TVCwgZmFsc2UpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfSFRUUEhFQURFUiwgJGhlYWRlcnMpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfQ09PS0lFRklMRSwgJHRoaXMtPklHRGF0YVBhdGggLiAoc3RyaW5nKSAkdGhpcy0+dXNlcm5hbWUgLiAiLmRhdCIpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfQ09PS0lFSkFSLCAkdGhpcy0+SUdEYXRhUGF0aCAuIChzdHJpbmcpICR0aGlzLT51c2VybmFtZSAuICIuZGF0Iik7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9QT1NULCB0cnVlKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1BPU1RGSUVMRFMsICRkYXRhKTsNCiAgICAgICAgJHJlc3AgPSBjdXJsX2V4ZWMoJGNoKTsNCiAgICAgICAgJGhlYWRlcl9sZW4gPSBjdXJsX2dldGluZm8oJGNoLCBDVVJMSU5GT19IRUFERVJfU0laRSk7DQogICAgICAgICRoZWFkZXIgPSBzdWJzdHIoJHJlc3AsIDAsICRoZWFkZXJfbGVuKTsNCiAgICAgICAgJHVwbG9hZCA9IGpzb25fZGVjb2RlKHN1YnN0cigkcmVzcCwgJGhlYWRlcl9sZW4pLCB0cnVlKTsNCiAgICAgICAgY3VybF9jbG9zZSgkY2gpOw0KICAgIH0NCg0KICAgIHByb3RlY3RlZCBmdW5jdGlvbiBjb25maWd1cmVWaWRlbygkdXBsb2FkX2lkLCAkdmlkZW8sICRjYXB0aW9uID0gIiIpDQogICAgew0KICAgICAgICAkdGhpcy0+dXBsb2FkUGhvdG8oJHZpZGVvLCAkY2FwdGlvbiwgJHVwbG9hZF9pZCk7DQogICAgICAgIGxpc3QoJHNpemUpID0gZ2V0aW1hZ2VzaXplKCR2aWRlbyk7DQogICAgICAgICRwb3N0ID0ganNvbl9lbmNvZGUoYXJyYXkoICJ1cGxvYWRfaWQiID0+ICR1cGxvYWRfaWQsICJzb3VyY2VfdHlwZSIgPT4gIjMiLCAicG9zdGVyX2ZyYW1lX2luZGV4IiA9PiAwLCAibGVuZ3RoIiA9PiAwLCAiYXVkaW9fbXV0ZWQiID0+IGZhbHNlLCAiZmlsdGVyX3R5cGUiID0+ICIwIiwgInZpZGVvX3Jlc3VsdCIgPT4gImRlcHJlY2F0ZWQiLCAiY2xpcHMiID0+IGFycmF5KCAibGVuZ3RoIiA9PiBVdGlsczo6Z2V0U2Vjb25kcygkdmlkZW8pLCAic291cmNlX3R5cGUiID0+ICIzIiwgImNhbWVyYV9wb3NpdGlvbiIgPT4gImJhY2siICksICJleHRyYSIgPT4gYXJyYXkoICJzb3VyY2Vfd2lkdGgiID0+IDk2MCwgInNvdXJjZV9oZWlnaHQiID0+IDEyODAgKSwgImRldmljZSIgPT4gYXJyYXkoICJtYW51ZmFjdHVyZXIiID0+ICJYaWFvbWkiLCAibW9kZWwiID0+ICJITSAxU1ciLCAiYW5kcm9pZF92ZXJzaW9uIiA9PiAxOCwgImFuZHJvaWRfcmVsZWFzZSIgPT4gIjQuMyIgKSwgIl9jc3JmdG9rZW4iID0+ICR0aGlzLT50b2tlbiwgIl91dWlkIiA9PiAkdGhpcy0+dXVpZCwgIl91aWQiID0+ICR0aGlzLT51c2VybmFtZV9pZCwgImNhcHRpb24iID0+ICRjYXB0aW9uICkpOw0KICAgICAgICAkcG9zdCA9IHN0cl9yZXBsYWNlKCJcImxlbmd0aFwiOjAiLCAiXCJsZW5ndGhcIjowLjAwIiwgJHBvc3QpOw0KICAgICAgICByZXR1cm4gJHRoaXMtPnJlcXVlc3QoIm1lZGlhL2NvbmZpZ3VyZS8/dmlkZW89MSIsIFNpZ25hdHVyZVV0aWxzOjpnZW5lcmF0ZVNpZ25hdHVyZSgkcG9zdCkpWzFdOw0KICAgIH0NCg0KICAgIHByb3RlY3RlZCBmdW5jdGlvbiBjb25maWd1cmUoJHVwbG9hZF9pZCwgJHBob3RvLCAkY2FwdGlvbiA9ICIiKQ0KICAgIHsNCiAgICAgICAgbGlzdCgkc2l6ZSkgPSBnZXRpbWFnZXNpemUoJHBob3RvKTsNCiAgICAgICAgJHBvc3QgPSBqc29uX2VuY29kZShhcnJheSggInVwbG9hZF9pZCIgPT4gJHVwbG9hZF9pZCwgImNhbWVyYV9tb2RlbCIgPT4gIkhNMVMiLCAic291cmNlX3R5cGUiID0+IDMsICJkYXRlX3RpbWVfb3JpZ2luYWwiID0+IGRhdGUoIlk6bTpkIEg6aTpzIiksICJjYW1lcmFfbWFrZSIgPT4gIlhJQU9NSSIsICJlZGl0cyIgPT4gYXJyYXkoICJjcm9wX29yaWdpbmFsX3NpemUiID0+IGFycmF5KCAkc2l6ZSwgJHNpemUgKSwgImNyb3Bfem9vbSIgPT4gMS4zMzMzMzM0LCAiY3JvcF9jZW50ZXIiID0+IGFycmF5KCAwLCAwICkgKSwgImV4dHJhIiA9PiBhcnJheSggInNvdXJjZV93aWR0aCIgPT4gJHNpemUsICJzb3VyY2VfaGVpZ2h0IiA9PiAkc2l6ZSApLCAiZGV2aWNlIiA9PiBhcnJheSggIm1hbnVmYWN0dXJlciIgPT4gIlhpYW9taSIsICJtb2RlbCIgPT4gIkhNIDFTVyIsICJhbmRyb2lkX3ZlcnNpb24iID0+IDE4LCAiYW5kcm9pZF9yZWxlYXNlIiA9PiAiNC4zIiApLCAiX2NzcmZ0b2tlbiIgPT4gJHRoaXMtPnRva2VuLCAiX3V1aWQiID0+ICR0aGlzLT51dWlkLCAiX3VpZCIgPT4gJHRoaXMtPnVzZXJuYW1lX2lkLCAiY2FwdGlvbiIgPT4gJGNhcHRpb24gKSk7DQogICAgICAgICRwb3N0ID0gc3RyX3JlcGxhY2UoIlwiY3JvcF9jZW50ZXJcIjpbMCwwXSIsICJcImNyb3BfY2VudGVyXCI6WzAuMCwtMC4wXSIsICRwb3N0KTsNCiAgICAgICAgcmV0dXJuICR0aGlzLT5yZXF1ZXN0KCJtZWRpYS9jb25maWd1cmUvIiwgU2lnbmF0dXJlVXRpbHM6OmdlbmVyYXRlU2lnbmF0dXJlKCRwb3N0KSlbMV07DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIEVkaXQgbWVkaWEuDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJG1lZGlhSWQNCiAgICAgICAgICogICBNZWRpYSBpZA0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICRjYXB0aW9uVGV4dA0KICAgICAgICAgKiAgIENhcHRpb24gdGV4dA0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgZWRpdCBtZWRpYSBkYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGVkaXRNZWRpYSgkbWVkaWFJZCwgJGNhcHRpb25UZXh0ID0gIiIpDQogICAgew0KICAgICAgICAkZGF0YSA9IGpzb25fZW5jb2RlKGFycmF5KCAiX3V1aWQiID0+ICR0aGlzLT51dWlkLCAiX3VpZCIgPT4gJHRoaXMtPnVzZXJuYW1lX2lkLCAiX2NzcmZ0b2tlbiIgPT4gJHRoaXMtPnRva2VuLCAiY2FwdGlvbl90ZXh0IiA9PiAkY2FwdGlvblRleHQgKSk7DQogICAgICAgIHJldHVybiAkdGhpcy0+cmVxdWVzdCgibWVkaWEvIiAuICRtZWRpYUlkIC4gIi9lZGl0X21lZGlhLyIsIFNpZ25hdHVyZVV0aWxzOjpnZW5lcmF0ZVNpZ25hdHVyZSgkZGF0YSkpWzFdOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBSZW1vdmUgeW91cnNlbGYgZnJvbSBhIHRhZ2dlZCBtZWRpYS4NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkbWVkaWFJZA0KICAgICAgICAgKiAgIE1lZGlhIGlkDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBlZGl0IG1lZGlhIGRhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gcmVtb3ZlU2VsZnRhZygkbWVkaWFJZCkNCiAgICB7DQogICAgICAgICRkYXRhID0ganNvbl9lbmNvZGUoYXJyYXkoICJfdXVpZCIgPT4gJHRoaXMtPnV1aWQsICJfdWlkIiA9PiAkdGhpcy0+dXNlcm5hbWVfaWQsICJfY3NyZnRva2VuIiA9PiAkdGhpcy0+dG9rZW4gKSk7DQogICAgICAgIHJldHVybiAkdGhpcy0+cmVxdWVzdCgidXNlcnRhZ3MvIiAuICRtZWRpYUlkIC4gIi9yZW1vdmUvIiwgU2lnbmF0dXJlVXRpbHM6OmdlbmVyYXRlU2lnbmF0dXJlKCRkYXRhKSlbMV07DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIE1lZGlhIGluZm8NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkbWVkaWFJZA0KICAgICAgICAgKiAgIE1lZGlhIGlkDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBkZWxldGUgcmVxdWVzdCBkYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIG1lZGlhSW5mbygkbWVkaWFJZCkNCiAgICB7DQogICAgICAgICRkYXRhID0ganNvbl9lbmNvZGUoYXJyYXkoICJfdXVpZCIgPT4gJHRoaXMtPnV1aWQsICJfdWlkIiA9PiAkdGhpcy0+dXNlcm5hbWVfaWQsICJfY3NyZnRva2VuIiA9PiAkdGhpcy0+dG9rZW4sICJtZWRpYV9pZCIgPT4gJG1lZGlhSWQgKSk7DQogICAgICAgIHJldHVybiAkdGhpcy0+cmVxdWVzdCgibWVkaWEvIiAuICRtZWRpYUlkIC4gIi9pbmZvLyIsIFNpZ25hdHVyZVV0aWxzOjpnZW5lcmF0ZVNpZ25hdHVyZSgkZGF0YSkpWzFdOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBEZWxldGUgcGhvdG8gb3IgdmlkZW8uDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJG1lZGlhSWQNCiAgICAgICAgICogICBNZWRpYSBpZA0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgZGVsZXRlIHJlcXVlc3QgZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBkZWxldGVNZWRpYSgkbWVkaWFJZCkNCiAgICB7DQogICAgICAgICRkYXRhID0ganNvbl9lbmNvZGUoYXJyYXkoICJfdXVpZCIgPT4gJHRoaXMtPnV1aWQsICJfdWlkIiA9PiAkdGhpcy0+dXNlcm5hbWVfaWQsICJfY3NyZnRva2VuIiA9PiAkdGhpcy0+dG9rZW4sICJtZWRpYV9pZCIgPT4gJG1lZGlhSWQgKSk7DQogICAgICAgIHJldHVybiAkdGhpcy0+cmVxdWVzdCgibWVkaWEvIiAuICRtZWRpYUlkIC4gIi9kZWxldGUvIiwgU2lnbmF0dXJlVXRpbHM6OmdlbmVyYXRlU2lnbmF0dXJlKCRkYXRhKSlbMV07DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIENvbW1lbnQgbWVkaWEuDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJG1lZGlhSWQNCiAgICAgICAgICogICBNZWRpYSBpZA0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICRjb21tZW50VGV4dA0KICAgICAgICAgKiAgIENvbW1lbnQgVGV4dA0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgY29tbWVudCBtZWRpYSBkYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGNvbW1lbnQoJG1lZGlhSWQsICRjb21tZW50VGV4dCkNCiAgICB7DQogICAgICAgICRkYXRhID0ganNvbl9lbmNvZGUoYXJyYXkoICJfdXVpZCIgPT4gJHRoaXMtPnV1aWQsICJfdWlkIiA9PiAkdGhpcy0+dXNlcm5hbWVfaWQsICJfY3NyZnRva2VuIiA9PiAkdGhpcy0+dG9rZW4sICJjb21tZW50X3RleHQiID0+ICRjb21tZW50VGV4dCApKTsNCiAgICAgICAgcmV0dXJuICR0aGlzLT5yZXF1ZXN0KCJtZWRpYS8iIC4gJG1lZGlhSWQgLiAiL2NvbW1lbnQvIiwgU2lnbmF0dXJlVXRpbHM6OmdlbmVyYXRlU2lnbmF0dXJlKCRkYXRhKSlbMV07DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIERlbGV0ZSBDb21tZW50Lg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICRtZWRpYUlkDQogICAgICAgICAqICAgTWVkaWEgSUQNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkY29tbWVudElkDQogICAgICAgICAqICAgQ29tbWVudCBJRA0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgRGVsZXRlIGNvbW1lbnQgZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBkZWxldGVDb21tZW50KCRtZWRpYUlkLCAkY2FwdGlvblRleHQsICRjb21tZW50SWQpDQogICAgew0KICAgICAgICAkZGF0YSA9IGpzb25fZW5jb2RlKGFycmF5KCAiX3V1aWQiID0+ICR0aGlzLT51dWlkLCAiX3VpZCIgPT4gJHRoaXMtPnVzZXJuYW1lX2lkLCAiX2NzcmZ0b2tlbiIgPT4gJHRoaXMtPnRva2VuLCAiY2FwdGlvbl90ZXh0IiA9PiAkY2FwdGlvblRleHQgKSk7DQogICAgICAgIHJldHVybiAkdGhpcy0+cmVxdWVzdCgibWVkaWEvIiAuICRtZWRpYUlkIC4gIi9jb21tZW50LyIgLiAkY29tbWVudElkIC4gIi9kZWxldGUvIiwgU2lnbmF0dXJlVXRpbHM6OmdlbmVyYXRlU2lnbmF0dXJlKCRkYXRhKSlbMV07DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIFNldHMgYWNjb3VudCB0byBwdWJsaWMuDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJHBob3RvDQogICAgICAgICAqICAgUGF0aCB0byBwaG90bw0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBjaGFuZ2VQcm9maWxlUGljdHVyZSgkcGhvdG8pDQogICAgew0KICAgICAgICBpZiggaXNfbnVsbCgkcGhvdG8pICkgDQogICAgICAgIHsNCiAgICAgICAgICAgIGVjaG8gIlBob3RvIG5vdCB2YWxpZFxuXG4iOw0KICAgICAgICB9DQogICAgICAgIGVsc2UNCiAgICAgICAgew0KICAgICAgICAgICAgJHVEYXRhID0ganNvbl9lbmNvZGUoYXJyYXkoICJfY3NyZnRva2VuIiA9PiAkdGhpcy0+dG9rZW4sICJfdXVpZCIgPT4gJHRoaXMtPnV1aWQsICJfdWlkIiA9PiAkdGhpcy0+dXNlcm5hbWVfaWQgKSk7DQogICAgICAgICAgICAkZW5kcG9pbnQgPSBDb25zdGFudHM6OkFQSV9VUkwgLiAiYWNjb3VudHMvY2hhbmdlX3Byb2ZpbGVfcGljdHVyZS8iOw0KICAgICAgICAgICAgJGJvdW5kYXJ5ID0gJHRoaXMtPnV1aWQ7DQogICAgICAgICAgICAkYm9kaWVzID0gYXJyYXkoIGFycmF5KCAidHlwZSIgPT4gImZvcm0tZGF0YSIsICJuYW1lIiA9PiAiaWdfc2lnX2tleV92ZXJzaW9uIiwgImRhdGEiID0+IENvbnN0YW50czo6U0lHX0tFWV9WRVJTSU9OICksIGFycmF5KCAidHlwZSIgPT4gImZvcm0tZGF0YSIsICJuYW1lIiA9PiAic2lnbmVkX2JvZHkiLCAiZGF0YSIgPT4gaGFzaF9obWFjKCJzaGEyNTYiLCAkdURhdGEsIENvbnN0YW50czo6SUdfU0lHX0tFWSkgLiAkdURhdGEgKSwgYXJyYXkoICJ0eXBlIiA9PiAiZm9ybS1kYXRhIiwgIm5hbWUiID0+ICJwcm9maWxlX3BpYyIsICJkYXRhIiA9PiBmaWxlX2dldF9jb250ZW50cygkcGhvdG8pLCAiZmlsZW5hbWUiID0+ICJwcm9maWxlX3BpYyIsICJoZWFkZXJzIiA9PiBhcnJheSggIkNvbnRlbnQtdHlwZTogYXBwbGljYXRpb24vb2N0ZXQtc3RyZWFtIiwgIkNvbnRlbnQtVHJhbnNmZXItRW5jb2Rpbmc6IGJpbmFyeSIgKSApICk7DQogICAgICAgICAgICAkZGF0YSA9ICR0aGlzLT5idWlsZEJvZHkoJGJvZGllcywgJGJvdW5kYXJ5KTsNCiAgICAgICAgICAgICRoZWFkZXJzID0gYXJyYXkoICJQcm94eS1Db25uZWN0aW9uOiBrZWVwLWFsaXZlIiwgIkNvbm5lY3Rpb246IGtlZXAtYWxpdmUiLCAiQWNjZXB0OiAqLyoiLCAiQ29udGVudC10eXBlOiBtdWx0aXBhcnQvZm9ybS1kYXRhOyBib3VuZGFyeT0iIC4gJGJvdW5kYXJ5LCAiQWNjZXB0LUxhbmd1YWdlOiB0ci1UUiIsICJBY2NlcHQtRW5jb2Rpbmc6IGd6aXAsIGRlZmxhdGUiICk7DQogICAgICAgICAgICAkY2ggPSBjdXJsX2luaXQoKTsNCiAgICAgICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9VUkwsICRlbmRwb2ludCk7DQogICAgICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfVVNFUkFHRU5ULCAkdGhpcy0+c2V0dGluZ3MtPmdldCgidXNlcl9hZ2VudCIpKTsNCiAgICAgICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9SRVRVUk5UUkFOU0ZFUiwgdHJ1ZSk7DQogICAgICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfRk9MTE9XTE9DQVRJT04sIHRydWUpOw0KICAgICAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX0hFQURFUiwgdHJ1ZSk7DQogICAgICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfVkVSQk9TRSwgJHRoaXMtPmRlYnVnKTsNCiAgICAgICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9TU0xfVkVSSUZZUEVFUiwgZmFsc2UpOw0KICAgICAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1NTTF9WRVJJRllIT1NULCBmYWxzZSk7DQogICAgICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfSFRUUEhFQURFUiwgJGhlYWRlcnMpOw0KICAgICAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX0NPT0tJRUZJTEUsICR0aGlzLT5JR0RhdGFQYXRoIC4gKHN0cmluZykgJHRoaXMtPnVzZXJuYW1lIC4gIi5kYXQiKTsNCiAgICAgICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9DT09LSUVKQVIsICR0aGlzLT5JR0RhdGFQYXRoIC4gKHN0cmluZykgJHRoaXMtPnVzZXJuYW1lIC4gIi5kYXQiKTsNCiAgICAgICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9QT1NULCB0cnVlKTsNCiAgICAgICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9QT1NURklFTERTLCAkZGF0YSk7DQogICAgICAgICAgICAkcmVzcCA9IGN1cmxfZXhlYygkY2gpOw0KICAgICAgICAgICAgJGhlYWRlcl9sZW4gPSBjdXJsX2dldGluZm8oJGNoLCBDVVJMSU5GT19IRUFERVJfU0laRSk7DQogICAgICAgICAgICAkaGVhZGVyID0gc3Vic3RyKCRyZXNwLCAwLCAkaGVhZGVyX2xlbik7DQogICAgICAgICAgICAkdXBsb2FkID0ganNvbl9kZWNvZGUoc3Vic3RyKCRyZXNwLCAkaGVhZGVyX2xlbiksIHRydWUpOw0KICAgICAgICAgICAgY3VybF9jbG9zZSgkY2gpOw0KICAgICAgICB9DQoNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogUmVtb3ZlIHByb2ZpbGUgcGljdHVyZS4NCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIHN0YXR1cyByZXF1ZXN0IGRhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gcmVtb3ZlUHJvZmlsZVBpY3R1cmUoKQ0KICAgIHsNCiAgICAgICAgJGRhdGEgPSBqc29uX2VuY29kZShhcnJheSggIl91dWlkIiA9PiAkdGhpcy0+dXVpZCwgIl91aWQiID0+ICR0aGlzLT51c2VybmFtZV9pZCwgIl9jc3JmdG9rZW4iID0+ICR0aGlzLT50b2tlbiApKTsNCiAgICAgICAgcmV0dXJuICR0aGlzLT5yZXF1ZXN0KCJhY2NvdW50cy9yZW1vdmVfcHJvZmlsZV9waWN0dXJlLyIsIFNpZ25hdHVyZVV0aWxzOjpnZW5lcmF0ZVNpZ25hdHVyZSgkZGF0YSkpWzFdOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBTZXRzIGFjY291bnQgdG8gcHJpdmF0ZS4NCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIHN0YXR1cyByZXF1ZXN0IGRhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gc2V0UHJpdmF0ZUFjY291bnQoKQ0KICAgIHsNCiAgICAgICAgJGRhdGEgPSBqc29uX2VuY29kZShhcnJheSggIl91dWlkIiA9PiAkdGhpcy0+dXVpZCwgIl91aWQiID0+ICR0aGlzLT51c2VybmFtZV9pZCwgIl9jc3JmdG9rZW4iID0+ICR0aGlzLT50b2tlbiApKTsNCiAgICAgICAgcmV0dXJuICR0aGlzLT5yZXF1ZXN0KCJhY2NvdW50cy9zZXRfcHJpdmF0ZS8iLCBTaWduYXR1cmVVdGlsczo6Z2VuZXJhdGVTaWduYXR1cmUoJGRhdGEpKVsxXTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogU2V0cyBhY2NvdW50IHRvIHB1YmxpYy4NCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIHN0YXR1cyByZXF1ZXN0IGRhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gc2V0UHVibGljQWNjb3VudCgpDQogICAgew0KICAgICAgICAkZGF0YSA9IGpzb25fZW5jb2RlKGFycmF5KCAiX3V1aWQiID0+ICR0aGlzLT51dWlkLCAiX3VpZCIgPT4gJHRoaXMtPnVzZXJuYW1lX2lkLCAiX2NzcmZ0b2tlbiIgPT4gJHRoaXMtPnRva2VuICkpOw0KICAgICAgICByZXR1cm4gJHRoaXMtPnJlcXVlc3QoImFjY291bnRzL3NldF9wdWJsaWMvIiwgU2lnbmF0dXJlVXRpbHM6OmdlbmVyYXRlU2lnbmF0dXJlKCRkYXRhKSlbMV07DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIEdldCBwZXJzb25hbCBwcm9maWxlIGRhdGEuDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBwcm9maWxlIGRhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gZ2V0UHJvZmlsZURhdGEoKQ0KICAgIHsNCiAgICAgICAgJGRhdGEgPSBqc29uX2VuY29kZShhcnJheSggIl91dWlkIiA9PiAkdGhpcy0+dXVpZCwgIl91aWQiID0+ICR0aGlzLT51c2VybmFtZV9pZCwgIl9jc3JmdG9rZW4iID0+ICR0aGlzLT50b2tlbiApKTsNCiAgICAgICAgcmV0dXJuICR0aGlzLT5yZXF1ZXN0KCJhY2NvdW50cy9jdXJyZW50X3VzZXIvP2VkaXQ9dHJ1ZSIsIFNpZ25hdHVyZVV0aWxzOjpnZW5lcmF0ZVNpZ25hdHVyZSgkZGF0YSkpWzFdOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBFZGl0IHByb2ZpbGUuDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJHVybA0KICAgICAgICAgKiAgIFVybCAtIHdlYnNpdGUuICIiIGZvciBub3RoaW5nDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJHBob25lDQogICAgICAgICAqICAgUGhvbmUgbnVtYmVyLiAiIiBmb3Igbm90aGluZw0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICRmaXJzdF9uYW1lDQogICAgICAgICAqICAgTmFtZS4gIiIgZm9yIG5vdGhpbmcNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkZW1haWwNCiAgICAgICAgICogICBFbWFpbC4gUmVxdWlyZWQuDQogICAgICAgICAqIEBwYXJhbSBpbnQgICAgJGdlbmRlcg0KICAgICAgICAgKiAgIEdlbmRlci4gbWFsZSA9IDEgLCBmZW1hbGUgPSAwDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBlZGl0IHByb2ZpbGUgZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBlZGl0UHJvZmlsZSgkdXJsLCAkcGhvbmUsICRmaXJzdF9uYW1lLCAkYmlvZ3JhcGh5LCAkZW1haWwsICRnZW5kZXIpDQogICAgew0KICAgICAgICAkZGF0YSA9IGpzb25fZW5jb2RlKGFycmF5KCAiX3V1aWQiID0+ICR0aGlzLT51dWlkLCAiX3VpZCIgPT4gJHRoaXMtPnVzZXJuYW1lX2lkLCAiX2NzcmZ0b2tlbiIgPT4gJHRoaXMtPnRva2VuLCAiZXh0ZXJuYWxfdXJsIiA9PiAkdXJsLCAicGhvbmVfbnVtYmVyIiA9PiAkcGhvbmUsICJ1c2VybmFtZSIgPT4gJHRoaXMtPnVzZXJuYW1lLCAiZmlyc3RfbmFtZSIgPT4gJGZpcnN0X25hbWUsICJiaW9ncmFwaHkiID0+ICRiaW9ncmFwaHksICJlbWFpbCIgPT4gJGVtYWlsLCAiZ2VuZGVyIiA9PiAkZ2VuZGVyICkpOw0KICAgICAgICByZXR1cm4gJHRoaXMtPnJlcXVlc3QoImFjY291bnRzL2VkaXRfcHJvZmlsZS8iLCBTaWduYXR1cmVVdGlsczo6Z2VuZXJhdGVTaWduYXR1cmUoJGRhdGEpKVsxXTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogR2V0IHVzZXJuYW1lIGluZm8uDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJHVzZXJuYW1lSWQNCiAgICAgICAgICogICBVc2VybmFtZSBpZA0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgVXNlcm5hbWUgZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBnZXRVc2VybmFtZUluZm8oJHVzZXJuYW1lSWQpDQogICAgew0KICAgICAgICByZXR1cm4gJHRoaXMtPnJlcXVlc3QoInVzZXJzLyIgLiAkdXNlcm5hbWVJZCAuICIvaW5mby8iKVsxXTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogR2V0IHNlbGYgdXNlcm5hbWUgaW5mby4NCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIFVzZXJuYW1lIGRhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gZ2V0U2VsZlVzZXJuYW1lSW5mbygpDQogICAgew0KICAgICAgICByZXR1cm4gJHRoaXMtPmdldFVzZXJuYW1lSW5mbygkdGhpcy0+dXNlcm5hbWVfaWQpOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBHZXQgcmVjZW50IGFjdGl2aXR5Lg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgUmVjZW50IGFjdGl2aXR5IGRhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gZ2V0UmVjZW50QWN0aXZpdHkoJG1heGlkID0gTlVMTCkNCiAgICB7DQogICAgICAgIGxpc3QoLCAkYWN0aXZpdHkpID0gJHRoaXMtPnJlcXVlc3QoIm5ld3MvaW5ib3gvPyIgLiAoKCFpc19udWxsKCRtYXhpZCkgPyAibWF4X2lkPSIgLiAkbWF4aWQgOiAiIikpKTsNCiAgICAgICAgaWYoICRhY3Rpdml0eVsic3RhdHVzIl0gIT0gIm9rIiApIA0KICAgICAgICB7DQogICAgICAgICAgICB0aHJvdyBuZXcgRXhjZXB0aW9uKCRhY3Rpdml0eVsibWVzc2FnZSJdIC4gIlxuIik7DQogICAgICAgIH0NCg0KICAgICAgICByZXR1cm4gJGFjdGl2aXR5Ow0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBHZXQgcmVjZW50IGFjdGl2aXR5IGZyb20gYWNjb3VudHMgZm9sbG93ZWQuDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBSZWNlbnQgYWN0aXZpdHkgZGF0YSBvZiBmb2xsb3dzDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGdldEZvbGxvd2luZ1JlY2VudEFjdGl2aXR5KCRtYXhpZCA9IE5VTEwpDQogICAgew0KICAgICAgICBsaXN0KCwgJGFjdGl2aXR5KSA9ICR0aGlzLT5yZXF1ZXN0KCJuZXdzLz8iIC4gKCghaXNfbnVsbCgkbWF4aWQpID8gIm1heF9pZD0iIC4gJG1heGlkIDogIiIpKSk7DQogICAgICAgIGlmKCAkYWN0aXZpdHlbInN0YXR1cyJdICE9ICJvayIgKSANCiAgICAgICAgew0KICAgICAgICAgICAgdGhyb3cgbmV3IEV4Y2VwdGlvbigkYWN0aXZpdHlbIm1lc3NhZ2UiXSAuICJcbiIpOw0KICAgICAgICB9DQoNCiAgICAgICAgcmV0dXJuICRhY3Rpdml0eTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogSSBkb250IGtub3cgdGhpcyB5ZXQuDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICB2MiBpbmJveCBkYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGdldHYySW5ib3goKQ0KICAgIHsNCiAgICAgICAgbGlzdCgsICRpbmJveCkgPSAkdGhpcy0+cmVxdWVzdCgiZGlyZWN0X3YyL2luYm94Lz8iKTsNCiAgICAgICAgaWYoICRpbmJveFsic3RhdHVzIl0gIT0gIm9rIiApIA0KICAgICAgICB7DQogICAgICAgICAgICB0aHJvdyBuZXcgRXhjZXB0aW9uKCRpbmJveFsibWVzc2FnZSJdIC4gIlxuIik7DQogICAgICAgIH0NCg0KICAgICAgICByZXR1cm4gJGluYm94Ow0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBEaXJlY3QgVGhyZWFkIERhdGEuDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSAgaW50ICR0aHJlYWRJZCBUaHJlYWQgSWQNCiAgICAgICAgICoNCiAgICAgICAgICogQHRocm93cyBFeGNlcHRpb24gRGlyZWN0IFRocmVhZCBEYXRhDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkgRGlyZWN0IFRocmVhZCBEYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGRpcmVjdFRocmVhZCgkdGhyZWFkSWQpDQogICAgew0KICAgICAgICBsaXN0KCwgJGRpcmVjdFRocmVhZCkgPSAkdGhpcy0+cmVxdWVzdCgiZGlyZWN0X3YyL3RocmVhZHMvIiAuICR0aHJlYWRJZCAuICIvPyIpOw0KICAgICAgICBpZiggJGRpcmVjdFRocmVhZFsic3RhdHVzIl0gIT0gIm9rIiApIA0KICAgICAgICB7DQogICAgICAgICAgICB0aHJvdyBuZXcgRXhjZXB0aW9uKCRkaXJlY3RUaHJlYWRbIm1lc3NhZ2UiXSAuICJcbiIpOw0KICAgICAgICB9DQoNCiAgICAgICAgcmV0dXJuICRkaXJlY3RUaHJlYWQ7DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIEdldCB1c2VyIHRhZ3MuDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJHVzZXJuYW1lSWQNCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIHVzZXIgdGFncyBkYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGdldFVzZXJUYWdzKCR1c2VybmFtZUlkLCAkbWF4aWQgPSBOVUxMKQ0KICAgIHsNCiAgICAgICAgbGlzdCgsICR0YWdzKSA9ICR0aGlzLT5yZXF1ZXN0KCJ1c2VydGFncy8iIC4gJHVzZXJuYW1lSWQgLiAiL2ZlZWQvP3JhbmtfdG9rZW49IiAuICR0aGlzLT5yYW5rX3Rva2VuIC4gIiZyYW5rZWRfY29udGVudD10cnVlIiAuICgoIWlzX251bGwoJG1heGlkKSA/ICJtYXhfaWQ9IiAuICRtYXhpZCA6ICIiKSkpOw0KICAgICAgICBpZiggJHRhZ3NbInN0YXR1cyJdICE9ICJvayIgKSANCiAgICAgICAgew0KICAgICAgICAgICAgdGhyb3cgbmV3IEV4Y2VwdGlvbigkdGFnc1sibWVzc2FnZSJdIC4gIlxuIik7DQogICAgICAgIH0NCg0KICAgICAgICByZXR1cm4gJHRhZ3M7DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIEdldCBzZWxmIHVzZXIgdGFncy4NCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIHNlbGYgdXNlciB0YWdzIGRhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gZ2V0U2VsZlVzZXJUYWdzKCRtYXhpZCA9IE5VTEwpDQogICAgew0KICAgICAgICByZXR1cm4gJHRoaXMtPmdldFVzZXJUYWdzKCR0aGlzLT51c2VybmFtZV9pZCwgJG1heGlkKTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogR2V0IHRhZ2dlZCBtZWRpYS4NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkdGFnDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gdGFnRmVlZCgkdGFnLCAkbWF4aWQgPSBOVUxMKQ0KICAgIHsNCiAgICAgICAgbGlzdCgsICR1c2VyRmVlZCkgPSAkdGhpcy0+cmVxdWVzdCgiZmVlZC90YWcvIiAuICR0YWcgLiAiLz9tYXhfaWQ9IiAuICRtYXhpZCAuICImcmFua190b2tlbj0iIC4gJHRoaXMtPnJhbmtfdG9rZW4gLiAiJnJhbmtlZF9jb250ZW50PXRydWUmIik7DQogICAgICAgIGlmKCAkdXNlckZlZWRbInN0YXR1cyJdICE9ICJvayIgKSANCiAgICAgICAgew0KICAgICAgICAgICAgdGhyb3cgbmV3IEV4Y2VwdGlvbigkdXNlckZlZWRbIm1lc3NhZ2UiXSAuICJcbiIpOw0KICAgICAgICB9DQoNCiAgICAgICAgcmV0dXJuICR1c2VyRmVlZDsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogR2V0IG1lZGlhIGxpa2Vycy4NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkbWVkaWFJZA0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGdldE1lZGlhTGlrZXJzKCRtZWRpYUlkKQ0KICAgIHsNCiAgICAgICAgbGlzdCgsICRsaWtlcnMpID0gJHRoaXMtPnJlcXVlc3QoIm1lZGlhLyIgLiAkbWVkaWFJZCAuICIvbGlrZXJzLz8iKTsNCiAgICAgICAgaWYoICRsaWtlcnNbInN0YXR1cyJdICE9ICJvayIgKSANCiAgICAgICAgew0KICAgICAgICAgICAgdGhyb3cgbmV3IEV4Y2VwdGlvbigkbGlrZXJzWyJtZXNzYWdlIl0gLiAiXG4iKTsNCiAgICAgICAgfQ0KDQogICAgICAgIHJldHVybiAkbGlrZXJzOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBHZXQgdXNlciBsb2NhdGlvbnMgbWVkaWEuDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJHVzZXJuYW1lSWQNCiAgICAgICAgICogICBVc2VybmFtZSBpZA0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgR2VvIE1lZGlhIGRhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gZ2V0R2VvTWVkaWEoJHVzZXJuYW1lSWQpDQogICAgew0KICAgICAgICBsaXN0KCwgJGxvY2F0aW9ucykgPSAkdGhpcy0+cmVxdWVzdCgibWFwcy91c2VyLyIgLiAkdXNlcm5hbWVJZCAuICIvIik7DQogICAgICAgIGlmKCAkbG9jYXRpb25zWyJzdGF0dXMiXSAhPSAib2siICkgDQogICAgICAgIHsNCiAgICAgICAgICAgIHRocm93IG5ldyBFeGNlcHRpb24oJGxvY2F0aW9uc1sibWVzc2FnZSJdIC4gIlxuIik7DQogICAgICAgIH0NCg0KICAgICAgICByZXR1cm4gJGxvY2F0aW9uczsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogR2V0IHNlbGYgdXNlciBsb2NhdGlvbnMgbWVkaWEuDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBHZW8gTWVkaWEgZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBnZXRTZWxmR2VvTWVkaWEoKQ0KICAgIHsNCiAgICAgICAgcmV0dXJuICR0aGlzLT5nZXRHZW9NZWRpYSgkdGhpcy0+dXNlcm5hbWVfaWQpOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBmYWNlYm9vayB1c2VyIHNlYXJjaC4NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkcXVlcnkNCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIHF1ZXJ5IGRhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gZmJVc2VyU2VhcmNoKCRxdWVyeSkNCiAgICB7DQogICAgICAgICRxdWVyeSA9IHJhd3VybGVuY29kZSgkcXVlcnkpOw0KICAgICAgICBsaXN0KCwgJHF1ZXJ5KSA9ICR0aGlzLT5yZXF1ZXN0KCJmYnNlYXJjaC90b3BzZWFyY2gvP2NvbnRleHQ9YmxlbmRlZCZxdWVyeT0iIC4gJHF1ZXJ5IC4gIiZyYW5rX3Rva2VuPSIgLiAkdGhpcy0+cmFua190b2tlbik7DQogICAgICAgIGlmKCAkcXVlcnlbInN0YXR1cyJdICE9ICJvayIgKSANCiAgICAgICAgew0KICAgICAgICAgICAgdGhyb3cgbmV3IEV4Y2VwdGlvbigkcXVlcnlbIm1lc3NhZ2UiXSAuICJcbiIpOw0KICAgICAgICB9DQoNCiAgICAgICAgcmV0dXJuICRxdWVyeTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogU2VhcmNoIHVzZXJzLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICRxdWVyeQ0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgcXVlcnkgZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBzZWFyY2hVc2VycygkcXVlcnkpDQogICAgew0KICAgICAgICBsaXN0KCwgJHF1ZXJ5KSA9ICR0aGlzLT5yZXF1ZXN0KCJ1c2Vycy9zZWFyY2gvP2lnX3NpZ19rZXlfdmVyc2lvbj0iIC4gQ29uc3RhbnRzOjpTSUdfS0VZX1ZFUlNJT04gLiAiJmlzX3R5cGVhaGVhZD10cnVlJnF1ZXJ5PSIgLiAkcXVlcnkgLiAiJnJhbmtfdG9rZW49IiAuICR0aGlzLT5yYW5rX3Rva2VuKTsNCiAgICAgICAgaWYoICRxdWVyeVsic3RhdHVzIl0gIT0gIm9rIiApIA0KICAgICAgICB7DQogICAgICAgICAgICB0aHJvdyBuZXcgRXhjZXB0aW9uKCRxdWVyeVsibWVzc2FnZSJdIC4gIlxuIik7DQogICAgICAgIH0NCg0KICAgICAgICByZXR1cm4gJHF1ZXJ5Ow0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBTZWFyY2ggZXhhY3QgdXNlcm5hbWUNCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyB1c2VybmFtZU5hbWUgdXNlcm5hbWUgYXMgU1RSSU5HIG5vdCBhbiBpZA0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgcXVlcnkgZGF0YQ0KICAgICAgICAgKg0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBzZWFyY2hVc2VybmFtZSgkdXNlcm5hbWVOYW1lKQ0KICAgIHsNCiAgICAgICAgbGlzdCgsICRxdWVyeSkgPSAkdGhpcy0+cmVxdWVzdCgidXNlcnMvIiAuICR1c2VybmFtZU5hbWUgLiAiL3VzZXJuYW1laW5mby8iKTsNCiAgICAgICAgcmV0dXJuICRxdWVyeTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogU2VhcmNoIHVzZXJzIHVzaW5nIGFkZHJlcyBib29rLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcGFyYW0gYXJyYXkgJGNvbnRhY3RzDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBxdWVyeSBkYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIHN5bmNGcm9tQWRyZXNzQm9vaygkY29udGFjdHMpDQogICAgew0KICAgICAgICAkZGF0YSA9ICJjb250YWN0cz0iIC4ganNvbl9lbmNvZGUoJGNvbnRhY3RzLCB0cnVlKTsNCiAgICAgICAgcmV0dXJuICR0aGlzLT5yZXF1ZXN0KCJhZGRyZXNzX2Jvb2svbGluay8/aW5jbHVkZT1leHRyYV9kaXNwbGF5X25hbWUsdGh1bWJuYWlscyIsICRkYXRhKVsxXTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogU2VhcmNoIHRhZ3MuDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJHF1ZXJ5DQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBxdWVyeSBkYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIHNlYXJjaFRhZ3MoJHF1ZXJ5KQ0KICAgIHsNCiAgICAgICAgbGlzdCgsICRxdWVyeSkgPSAkdGhpcy0+cmVxdWVzdCgidGFncy9zZWFyY2gvP2lzX3R5cGVhaGVhZD10cnVlJnE9IiAuICRxdWVyeSAuICImcmFua190b2tlbj0iIC4gJHRoaXMtPnJhbmtfdG9rZW4pOw0KICAgICAgICBpZiggJHF1ZXJ5WyJzdGF0dXMiXSAhPSAib2siICkgDQogICAgICAgIHsNCiAgICAgICAgICAgIHRocm93IG5ldyBFeGNlcHRpb24oJHF1ZXJ5WyJtZXNzYWdlIl0gLiAiXG4iKTsNCiAgICAgICAgfQ0KDQogICAgICAgIHJldHVybiAkcXVlcnk7DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIEdldCB0aW1lbGluZSBkYXRhLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgdGltZWxpbmUgZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBnZXRUaW1lbGluZSgkbWF4aWQgPSBOVUxMKQ0KICAgIHsNCiAgICAgICAgbGlzdCgsICR0aW1lbGluZSkgPSAkdGhpcy0+cmVxdWVzdCgiZmVlZC90aW1lbGluZS8/cmFua190b2tlbj0iIC4gJHRoaXMtPnJhbmtfdG9rZW4gLiAiJnJhbmtlZF9jb250ZW50PXRydWUiIC4gKCghaXNfbnVsbCgkbWF4aWQpID8gIiZtYXhfaWQ9IiAuICRtYXhpZCA6ICIiKSkpOw0KICAgICAgICBpZiggJHRpbWVsaW5lWyJzdGF0dXMiXSAhPSAib2siICkgDQogICAgICAgIHsNCiAgICAgICAgICAgIHRocm93IG5ldyBFeGNlcHRpb24oJHRpbWVsaW5lWyJtZXNzYWdlIl0gLiAiXG4iKTsNCiAgICAgICAgfQ0KDQogICAgICAgIHJldHVybiAkdGltZWxpbmU7DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIEB0aHJvd3MgRXhjZXB0aW9uDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXl8dm9pZA0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBnZXRSZWVsc1RyYXlGZWVkKCkNCiAgICB7DQogICAgICAgIGxpc3QoLCAkZmVlZCkgPSAkdGhpcy0+cmVxdWVzdCgiZmVlZC9yZWVsc190cmF5LyIpOw0KICAgICAgICBpZiggJGZlZWRbInN0YXR1cyJdICE9ICJvayIgKSANCiAgICAgICAgew0KICAgICAgICAgICAgdGhyb3cgbmV3IEV4Y2VwdGlvbigkZmVlZFsibWVzc2FnZSJdIC4gIlxuIik7DQogICAgICAgIH0NCg0KICAgICAgICByZXR1cm4gJGZlZWQ7DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIEdldCB1c2VyIGZlZWQuDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJHVzZXJuYW1lSWQNCiAgICAgICAgICogICAgVXNlcm5hbWUgaWQNCiAgICAgICAgICogQHBhcmFtIG51bGwgICAkbWF4aWQNCiAgICAgICAgICogICAgTWF4IElkDQogICAgICAgICAqIEBwYXJhbSBudWxsICAgJG1pblRpbWVzdGFtcA0KICAgICAgICAgKiAgICBNaW4gdGltZXN0YW1wDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkgVXNlciBmZWVkIGRhdGENCiAgICAgICAgICogICAgVXNlciBmZWVkIGRhdGENCiAgICAgICAgICogQHRocm93cyBFeGNlcHRpb24NCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gZ2V0VXNlckZlZWQoJHVzZXJuYW1lSWQsICRtYXhpZCA9IE5VTEwsICRtaW5UaW1lc3RhbXAgPSBOVUxMKQ0KICAgIHsNCiAgICAgICAgbGlzdCgsICR1c2VyRmVlZCkgPSAkdGhpcy0+cmVxdWVzdCgiZmVlZC91c2VyLyIgLiAkdXNlcm5hbWVJZCAuICIvP3JhbmtfdG9rZW49IiAuICR0aGlzLT5yYW5rX3Rva2VuIC4gKCghaXNfbnVsbCgkbWF4aWQpID8gIiZtYXhfaWQ9IiAuICRtYXhpZCA6ICIiKSkgLiAoKCFpc19udWxsKCRtaW5UaW1lc3RhbXApID8gIiZtaW5fdGltZXN0YW1wPSIgLiAkbWluVGltZXN0YW1wIDogIiIpKSAuICImcmFua2VkX2NvbnRlbnQ9dHJ1ZSIpOw0KICAgICAgICByZXR1cm4gJHVzZXJGZWVkOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBHZXQgaGFzaHRhZyBmZWVkLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICRoYXNodGFnU3RyaW5nDQogICAgICAgICAqICAgIEhhc2h0YWcgc3RyaW5nLCBub3QgaW5jbHVkaW5nIHRoZSAjDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBIYXNodGFnIGZlZWQgZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBnZXRIYXNodGFnRmVlZCgkaGFzaHRhZ1N0cmluZywgJG1heGlkID0gTlVMTCkNCiAgICB7DQogICAgICAgIGlmKCBpc19udWxsKCRtYXhpZCkgKSANCiAgICAgICAgew0KICAgICAgICAgICAgJGVuZHBvaW50ID0gImZlZWQvdGFnLyIgLiAkaGFzaHRhZ1N0cmluZyAuICIvP3JhbmtfdG9rZW49IiAuICR0aGlzLT5yYW5rX3Rva2VuIC4gIiZyYW5rZWRfY29udGVudD10cnVlJiI7DQogICAgICAgIH0NCiAgICAgICAgZWxzZQ0KICAgICAgICB7DQogICAgICAgICAgICAkZW5kcG9pbnQgPSAiZmVlZC90YWcvIiAuICRoYXNodGFnU3RyaW5nIC4gIi8/bWF4X2lkPSIgLiAkbWF4aWQgLiAiJnJhbmtfdG9rZW49IiAuICR0aGlzLT5yYW5rX3Rva2VuIC4gIiZyYW5rZWRfY29udGVudD10cnVlJiI7DQogICAgICAgIH0NCg0KICAgICAgICBsaXN0KCwgJGhhc2h0YWdGZWVkKSA9ICR0aGlzLT5yZXF1ZXN0KCRlbmRwb2ludCk7DQogICAgICAgIGlmKCAkaGFzaHRhZ0ZlZWRbInN0YXR1cyJdICE9ICJvayIgKSANCiAgICAgICAgew0KICAgICAgICAgICAgdGhyb3cgbmV3IEV4Y2VwdGlvbigkaGFzaHRhZ0ZlZWRbIm1lc3NhZ2UiXSAuICJcbiIpOw0KICAgICAgICB9DQoNCiAgICAgICAgcmV0dXJuICRoYXNodGFnRmVlZDsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogR2V0IGxvY2F0aW9ucy4NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkcXVlcnkNCiAgICAgICAgICogICAgc2VhcmNoIHF1ZXJ5DQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBMb2NhdGlvbiBsb2NhdGlvbiBkYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIHNlYXJjaExvY2F0aW9uKCRxdWVyeSkNCiAgICB7DQogICAgICAgICRxdWVyeSA9IHJhd3VybGVuY29kZSgkcXVlcnkpOw0KICAgICAgICAkZW5kcG9pbnQgPSAiZmJzZWFyY2gvcGxhY2VzLz9yYW5rX3Rva2VuPSIgLiAkdGhpcy0+cmFua190b2tlbiAuICImcXVlcnk9IiAuICRxdWVyeTsNCiAgICAgICAgbGlzdCgsICRsb2NhdGlvbkZlZWQpID0gJHRoaXMtPnJlcXVlc3QoJGVuZHBvaW50KTsNCiAgICAgICAgaWYoICRsb2NhdGlvbkZlZWRbInN0YXR1cyJdICE9ICJvayIgKSANCiAgICAgICAgew0KICAgICAgICAgICAgdGhyb3cgbmV3IEV4Y2VwdGlvbigkbG9jYXRpb25GZWVkWyJtZXNzYWdlIl0gLiAiXG4iKTsNCiAgICAgICAgfQ0KDQogICAgICAgIHJldHVybiAkbG9jYXRpb25GZWVkOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBHZXQgbG9jYXRpb24gZmVlZC4NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkbG9jYXRpb25JZA0KICAgICAgICAgKiAgICBsb2NhdGlvbiBpZA0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgTG9jYXRpb24gZmVlZCBkYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGdldExvY2F0aW9uRmVlZCgkbG9jYXRpb25JZCwgJG1heGlkID0gTlVMTCkNCiAgICB7DQogICAgICAgIGlmKCBpc19udWxsKCRtYXhpZCkgKSANCiAgICAgICAgew0KICAgICAgICAgICAgJGVuZHBvaW50ID0gImZlZWQvbG9jYXRpb24vIiAuICRsb2NhdGlvbklkIC4gIi8/cmFua190b2tlbj0iIC4gJHRoaXMtPnJhbmtfdG9rZW4gLiAiJnJhbmtlZF9jb250ZW50PXRydWUmIjsNCiAgICAgICAgfQ0KICAgICAgICBlbHNlDQogICAgICAgIHsNCiAgICAgICAgICAgICRlbmRwb2ludCA9ICJmZWVkL2xvY2F0aW9uLyIgLiAkbG9jYXRpb25JZCAuICIvP21heF9pZD0iIC4gJG1heGlkIC4gIiZyYW5rX3Rva2VuPSIgLiAkdGhpcy0+cmFua190b2tlbiAuICImcmFua2VkX2NvbnRlbnQ9dHJ1ZSYiOw0KICAgICAgICB9DQoNCiAgICAgICAgbGlzdCgsICRsb2NhdGlvbkZlZWQpID0gJHRoaXMtPnJlcXVlc3QoJGVuZHBvaW50KTsNCiAgICAgICAgaWYoICRsb2NhdGlvbkZlZWRbInN0YXR1cyJdICE9ICJvayIgKSANCiAgICAgICAgew0KICAgICAgICAgICAgdGhyb3cgbmV3IEV4Y2VwdGlvbigkbG9jYXRpb25GZWVkWyJtZXNzYWdlIl0gLiAiXG4iKTsNCiAgICAgICAgfQ0KDQogICAgICAgIHJldHVybiAkbG9jYXRpb25GZWVkOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBHZXQgc2VsZiB1c2VyIGZlZWQuDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBVc2VyIGZlZWQgZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBnZXRTZWxmVXNlckZlZWQoJG1heGlkID0gTlVMTCwgJG1pblRpbWVzdGFtcCA9IE5VTEwpDQogICAgew0KICAgICAgICByZXR1cm4gJHRoaXMtPmdldFVzZXJGZWVkKCR0aGlzLT51c2VybmFtZV9pZCwgJG1heGlkLCAkbWluVGltZXN0YW1wKTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogUmFua2VkIHJlY2lwaWVudHMuDQogICAgICAgICAqDQogICAgICAgICAqIEB0aHJvd3MgRXhjZXB0aW9uIFJhbmtlZCByZWNpcGllbnRzIERhdGENCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheXx2b2lkDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGdldFJhbmtlZFJlY2lwaWVudHMoKQ0KICAgIHsNCiAgICAgICAgbGlzdCgsICRyYW5rZWRfcmVjaXBpZW50cykgPSAkdGhpcy0+cmVxdWVzdCgiZGlyZWN0X3YyL3JhbmtlZF9yZWNpcGllbnRzLz9zaG93X3RocmVhZHM9dHJ1ZSIpOw0KICAgICAgICBpZiggJHJhbmtlZF9yZWNpcGllbnRzWyJzdGF0dXMiXSAhPSAib2siICkgDQogICAgICAgIHsNCiAgICAgICAgICAgIHRocm93IG5ldyBFeGNlcHRpb24oJHJhbmtlZF9yZWNpcGllbnRzWyJtZXNzYWdlIl0gLiAiXG4iKTsNCiAgICAgICAgfQ0KDQogICAgICAgIHJldHVybiAkcmFua2VkX3JlY2lwaWVudHM7DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIFJlY2VudCByZWNpcGllbnRzLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAdGhyb3dzIEV4Y2VwdGlvbiBSYW5rZWQgcmVjaXBpZW50cyBEYXRhDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXl8dm9pZA0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBnZXRSZWNlbnRSZWNpcGllbnRzKCkNCiAgICB7DQogICAgICAgIGxpc3QoLCAkcmVjZW50X3JlY2lwaWVudHMpID0gJHRoaXMtPnJlcXVlc3QoImRpcmVjdF9zaGFyZS9yZWNlbnRfcmVjaXBpZW50cy8iKTsNCiAgICAgICAgaWYoICRyZWNlbnRfcmVjaXBpZW50c1sic3RhdHVzIl0gIT0gIm9rIiApIA0KICAgICAgICB7DQogICAgICAgICAgICB0aHJvdyBuZXcgRXhjZXB0aW9uKCRyZWNlbnRfcmVjaXBpZW50c1sibWVzc2FnZSJdIC4gIlxuIik7DQogICAgICAgIH0NCg0KICAgICAgICByZXR1cm4gJHJlY2VudF9yZWNpcGllbnRzOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBFeHBsb3JlIFRhYi4NCiAgICAgICAgICoNCiAgICAgICAgICogQHRocm93cyBFeGNlcHRpb24gRXhwbG9yZSBkYXRhDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXl8dm9pZA0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBleHBsb3JlKCkNCiAgICB7DQogICAgICAgIGxpc3QoLCAkZXhwbG9yZSkgPSAkdGhpcy0+cmVxdWVzdCgiZGlzY292ZXIvZXhwbG9yZS8iKTsNCiAgICAgICAgaWYoICRleHBsb3JlWyJzdGF0dXMiXSAhPSAib2siICkgDQogICAgICAgIHsNCiAgICAgICAgICAgIHRocm93IG5ldyBFeGNlcHRpb24oJGV4cGxvcmVbIm1lc3NhZ2UiXSAuICJcbiIpOw0KICAgICAgICB9DQoNCiAgICAgICAgcmV0dXJuICRleHBsb3JlOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBHZXQgcG9wdWxhciBmZWVkLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgcG9wdWxhciBmZWVkIGRhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gZ2V0UG9wdWxhckZlZWQoJG1heGlkID0gTlVMTCkNCiAgICB7DQogICAgICAgIGxpc3QoLCAkcG9wdWxhckZlZWQpID0gJHRoaXMtPnJlcXVlc3QoImZlZWQvcG9wdWxhci8/bWF4X2lkPSIgLiAkbWF4aWQgLiAiJnBlb3BsZV90ZWFzZXJfc3VwcG9ydGVkPTEmcmFua190b2tlbj0iIC4gJHRoaXMtPnJhbmtfdG9rZW4gLiAiJnJhbmtlZF9jb250ZW50PXRydWUmIik7DQogICAgICAgIGlmKCAkcG9wdWxhckZlZWRbInN0YXR1cyJdICE9ICJvayIgKSANCiAgICAgICAgew0KICAgICAgICAgICAgdGhyb3cgbmV3IEV4Y2VwdGlvbigkcG9wdWxhckZlZWRbIm1lc3NhZ2UiXSAuICJcbiIpOw0KICAgICAgICB9DQoNCiAgICAgICAgcmV0dXJuICRwb3B1bGFyRmVlZDsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogR2V0IHVzZXIgZm9sbG93aW5ncy4NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkdXNlcm5hbWVJZA0KICAgICAgICAgKiAgIFVzZXJuYW1lIGlkDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBmb2xsb3dlcnMgZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBnZXRVc2VyRm9sbG93aW5ncygkdXNlcm5hbWVJZCwgJG1heGlkID0gTlVMTCkNCiAgICB7DQogICAgICAgIHJldHVybiAkdGhpcy0+cmVxdWVzdCgiZnJpZW5kc2hpcHMvIiAuICR1c2VybmFtZUlkIC4gIi9mb2xsb3dpbmcvP21heF9pZD0iIC4gJG1heGlkIC4gIiZpZ19zaWdfa2V5X3ZlcnNpb249IiAuIENvbnN0YW50czo6U0lHX0tFWV9WRVJTSU9OIC4gIiZyYW5rX3Rva2VuPSIgLiAkdGhpcy0+cmFua190b2tlbilbMV07DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIEdldCB1c2VyIGZvbGxvd2Vycy4NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkdXNlcm5hbWVJZA0KICAgICAgICAgKiAgIFVzZXJuYW1lIGlkDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBmb2xsb3dlcnMgZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBnZXRVc2VyRm9sbG93ZXJzKCR1c2VybmFtZUlkLCAkbWF4aWQgPSBOVUxMKQ0KICAgIHsNCiAgICAgICAgcmV0dXJuICR0aGlzLT5yZXF1ZXN0KCJmcmllbmRzaGlwcy8iIC4gJHVzZXJuYW1lSWQgLiAiL2ZvbGxvd2Vycy8/bWF4X2lkPSIgLiAkbWF4aWQgLiAiJmlnX3NpZ19rZXlfdmVyc2lvbj0iIC4gQ29uc3RhbnRzOjpTSUdfS0VZX1ZFUlNJT04gLiAiJnJhbmtfdG9rZW49IiAuICR0aGlzLT5yYW5rX3Rva2VuKVsxXTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogR2V0IHNlbGYgdXNlciBmb2xsb3dlcnMuDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBmb2xsb3dlcnMgZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBnZXRTZWxmVXNlckZvbGxvd2VycygkbWF4aWQgPSBOVUxMKQ0KICAgIHsNCiAgICAgICAgcmV0dXJuICR0aGlzLT5nZXRVc2VyRm9sbG93ZXJzKCR0aGlzLT51c2VybmFtZV9pZCwgJG1heGlkKTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogR2V0IHNlbGYgdXNlcnMgd2UgYXJlIGZvbGxvd2luZy4NCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIHVzZXJzIHdlIGFyZSBmb2xsb3dpbmcgZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBnZXRTZWxmVXNlcnNGb2xsb3dpbmcoJG1heGlkID0gTlVMTCkNCiAgICB7DQogICAgICAgIHJldHVybiAkdGhpcy0+cmVxdWVzdCgiZnJpZW5kc2hpcHMvZm9sbG93aW5nLz9tYXhfaWQ9IiAuICRtYXhpZCAuICImaWdfc2lnX2tleV92ZXJzaW9uPSIgLiBDb25zdGFudHM6OlNJR19LRVlfVkVSU0lPTiAuICImcmFua190b2tlbj0iIC4gJHRoaXMtPnJhbmtfdG9rZW4pWzFdOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBMaWtlIHBob3RvIG9yIHZpZGVvLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICRtZWRpYUlkDQogICAgICAgICAqICAgTWVkaWEgaWQNCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIHN0YXR1cyByZXF1ZXN0DQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGxpa2UoJG1lZGlhSWQpDQogICAgew0KICAgICAgICAkZGF0YSA9IGpzb25fZW5jb2RlKGFycmF5KCAiX3V1aWQiID0+ICR0aGlzLT51dWlkLCAiX3VpZCIgPT4gJHRoaXMtPnVzZXJuYW1lX2lkLCAiX2NzcmZ0b2tlbiIgPT4gJHRoaXMtPnRva2VuLCAibWVkaWFfaWQiID0+ICRtZWRpYUlkICkpOw0KICAgICAgICByZXR1cm4gJHRoaXMtPnJlcXVlc3QoIm1lZGlhLyIgLiAkbWVkaWFJZCAuICIvbGlrZS8iLCBTaWduYXR1cmVVdGlsczo6Z2VuZXJhdGVTaWduYXR1cmUoJGRhdGEpKVsxXTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogVW5saWtlIHBob3RvIG9yIHZpZGVvLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICRtZWRpYUlkDQogICAgICAgICAqICAgTWVkaWEgaWQNCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIHN0YXR1cyByZXF1ZXN0DQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIHVubGlrZSgkbWVkaWFJZCkNCiAgICB7DQogICAgICAgICRkYXRhID0ganNvbl9lbmNvZGUoYXJyYXkoICJfdXVpZCIgPT4gJHRoaXMtPnV1aWQsICJfdWlkIiA9PiAkdGhpcy0+dXNlcm5hbWVfaWQsICJfY3NyZnRva2VuIiA9PiAkdGhpcy0+dG9rZW4sICJtZWRpYV9pZCIgPT4gJG1lZGlhSWQgKSk7DQogICAgICAgIHJldHVybiAkdGhpcy0+cmVxdWVzdCgibWVkaWEvIiAuICRtZWRpYUlkIC4gIi91bmxpa2UvIiwgU2lnbmF0dXJlVXRpbHM6OmdlbmVyYXRlU2lnbmF0dXJlKCRkYXRhKSlbMV07DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIEdldCBtZWRpYSBjb21tZW50cy4NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkbWVkaWFJZA0KICAgICAgICAgKiAgIE1lZGlhIGlkDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBNZWRpYSBjb21tZW50cyBkYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGdldE1lZGlhQ29tbWVudHMoJG1lZGlhSWQpDQogICAgew0KICAgICAgICByZXR1cm4gJHRoaXMtPnJlcXVlc3QoIm1lZGlhLyIgLiAkbWVkaWFJZCAuICIvY29tbWVudHMvPyIpWzFdOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBTZXQgbmFtZSBhbmQgcGhvbmUgKE9wdGlvbmFsKS4NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkbmFtZQ0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICRwaG9uZQ0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgU2V0IHN0YXR1cyBkYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIHNldE5hbWVBbmRQaG9uZSgkbmFtZSA9ICIiLCAkcGhvbmUgPSAiIikNCiAgICB7DQogICAgICAgICRkYXRhID0ganNvbl9lbmNvZGUoYXJyYXkoICJfdXVpZCIgPT4gJHRoaXMtPnV1aWQsICJfdWlkIiA9PiAkdGhpcy0+dXNlcm5hbWVfaWQsICJmaXJzdF9uYW1lIiA9PiAkbmFtZSwgInBob25lX251bWJlciIgPT4gJHBob25lLCAiX2NzcmZ0b2tlbiIgPT4gJHRoaXMtPnRva2VuICkpOw0KICAgICAgICByZXR1cm4gJHRoaXMtPnJlcXVlc3QoImFjY291bnRzL3NldF9waG9uZV9hbmRfbmFtZS8iLCBTaWduYXR1cmVVdGlsczo6Z2VuZXJhdGVTaWduYXR1cmUoJGRhdGEpKVsxXTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogR2V0IGRpcmVjdCBzaGFyZS4NCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIERpcmVjdCBzaGFyZSBkYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGdldERpcmVjdFNoYXJlKCkNCiAgICB7DQogICAgICAgIHJldHVybiAkdGhpcy0+cmVxdWVzdCgiZGlyZWN0X3NoYXJlL2luYm94Lz8iKVsxXTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogQmFja3VwcyBhbGwgeW91ciB1cGxvYWRlZCBwaG90b3MgOikuDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGJhY2t1cCgpDQogICAgew0KICAgICAgICAkbXlVcGxvYWRzID0gJHRoaXMtPmdldFNlbGZVc2VyRmVlZCgpOw0KICAgICAgICBmb3JlYWNoKCAkbXlVcGxvYWRzWyJpdGVtcyJdIGFzICRpdGVtICkgDQogICAgICAgIHsNCiAgICAgICAgICAgIGlmKCAhaXNfZGlyKCR0aGlzLT5JR0RhdGFQYXRoIC4gImJhY2t1cC8iIC4gKHN0cmluZykgJHRoaXMtPnVzZXJuYW1lIC4gIi0iIC4gZGF0ZSgiWS1tLWQiKSkgKSANCiAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICBta2RpcigkdGhpcy0+SUdEYXRhUGF0aCAuICJiYWNrdXAvIiAuIChzdHJpbmcpICR0aGlzLT51c2VybmFtZSAuICItIiAuIGRhdGUoIlktbS1kIikpOw0KICAgICAgICAgICAgfQ0KDQogICAgICAgICAgICBmaWxlX3B1dF9jb250ZW50cygkdGhpcy0+SUdEYXRhUGF0aCAuICJiYWNrdXAvIiAuIChzdHJpbmcpICR0aGlzLT51c2VybmFtZSAuICItIiAuIGRhdGUoIlktbS1kIikgLiAiLyIgLiAkaXRlbVsiaWQiXSAuICIuanBnIiwgZmlsZV9nZXRfY29udGVudHMoJGl0ZW1bImltYWdlX3ZlcnNpb25zMiJdWyJjYW5kaWRhdGVzIl1bMF1bInVybCJdKSk7DQogICAgICAgIH0NCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogRm9sbG93Lg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICR1c2VySWQNCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIEZyaWVuZHNoaXAgc3RhdHVzIGRhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gZm9sbG93KCR1c2VySWQpDQogICAgew0KICAgICAgICAkZGF0YSA9IGpzb25fZW5jb2RlKGFycmF5KCAiX3V1aWQiID0+ICR0aGlzLT51dWlkLCAiX3VpZCIgPT4gJHRoaXMtPnVzZXJuYW1lX2lkLCAidXNlcl9pZCIgPT4gJHVzZXJJZCwgIl9jc3JmdG9rZW4iID0+ICR0aGlzLT50b2tlbiApKTsNCiAgICAgICAgcmV0dXJuICR0aGlzLT5yZXF1ZXN0KCJmcmllbmRzaGlwcy9jcmVhdGUvIiAuICR1c2VySWQgLiAiLyIsIFNpZ25hdHVyZVV0aWxzOjpnZW5lcmF0ZVNpZ25hdHVyZSgkZGF0YSkpWzFdOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBVbmZvbGxvdy4NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkdXNlcklkDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBGcmllbmRzaGlwIHN0YXR1cyBkYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIHVuZm9sbG93KCR1c2VySWQpDQogICAgew0KICAgICAgICAkZGF0YSA9IGpzb25fZW5jb2RlKGFycmF5KCAiX3V1aWQiID0+ICR0aGlzLT51dWlkLCAiX3VpZCIgPT4gJHRoaXMtPnVzZXJuYW1lX2lkLCAidXNlcl9pZCIgPT4gJHVzZXJJZCwgIl9jc3JmdG9rZW4iID0+ICR0aGlzLT50b2tlbiApKTsNCiAgICAgICAgcmV0dXJuICR0aGlzLT5yZXF1ZXN0KCJmcmllbmRzaGlwcy9kZXN0cm95LyIgLiAkdXNlcklkIC4gIi8iLCBTaWduYXR1cmVVdGlsczo6Z2VuZXJhdGVTaWduYXR1cmUoJGRhdGEpKVsxXTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogQmxvY2suDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJHVzZXJJZA0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgRnJpZW5kc2hpcCBzdGF0dXMgZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBibG9jaygkdXNlcklkKQ0KICAgIHsNCiAgICAgICAgJGRhdGEgPSBqc29uX2VuY29kZShhcnJheSggIl91dWlkIiA9PiAkdGhpcy0+dXVpZCwgIl91aWQiID0+ICR0aGlzLT51c2VybmFtZV9pZCwgInVzZXJfaWQiID0+ICR1c2VySWQsICJfY3NyZnRva2VuIiA9PiAkdGhpcy0+dG9rZW4gKSk7DQogICAgICAgIHJldHVybiAkdGhpcy0+cmVxdWVzdCgiZnJpZW5kc2hpcHMvYmxvY2svIiAuICR1c2VySWQgLiAiLyIsIFNpZ25hdHVyZVV0aWxzOjpnZW5lcmF0ZVNpZ25hdHVyZSgkZGF0YSkpWzFdOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBVbmJsb2NrLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICR1c2VySWQNCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIEZyaWVuZHNoaXAgc3RhdHVzIGRhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gdW5ibG9jaygkdXNlcklkKQ0KICAgIHsNCiAgICAgICAgJGRhdGEgPSBqc29uX2VuY29kZShhcnJheSggIl91dWlkIiA9PiAkdGhpcy0+dXVpZCwgIl91aWQiID0+ICR0aGlzLT51c2VybmFtZV9pZCwgInVzZXJfaWQiID0+ICR1c2VySWQsICJfY3NyZnRva2VuIiA9PiAkdGhpcy0+dG9rZW4gKSk7DQogICAgICAgIHJldHVybiAkdGhpcy0+cmVxdWVzdCgiZnJpZW5kc2hpcHMvdW5ibG9jay8iIC4gJHVzZXJJZCAuICIvIiwgU2lnbmF0dXJlVXRpbHM6OmdlbmVyYXRlU2lnbmF0dXJlKCRkYXRhKSlbMV07DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIFNob3cgVXNlciBGcmllbmRzaGlwLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICR1c2VySWQNCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIEZyaWVuZHNoaXAgcmVsYXRpb25zaGlwIGRhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gdXNlckZyaWVuZHNoaXAoJHVzZXJJZCkNCiAgICB7DQogICAgICAgICRkYXRhID0ganNvbl9lbmNvZGUoYXJyYXkoICJfdXVpZCIgPT4gJHRoaXMtPnV1aWQsICJfdWlkIiA9PiAkdGhpcy0+dXNlcm5hbWVfaWQsICJ1c2VyX2lkIiA9PiAkdXNlcklkLCAiX2NzcmZ0b2tlbiIgPT4gJHRoaXMtPnRva2VuICkpOw0KICAgICAgICByZXR1cm4gJHRoaXMtPnJlcXVlc3QoImZyaWVuZHNoaXBzL3Nob3cvIiAuICR1c2VySWQgLiAiLyIsIFNpZ25hdHVyZVV0aWxzOjpnZW5lcmF0ZVNpZ25hdHVyZSgkZGF0YSkpWzFdOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBHZXQgbGlrZWQgbWVkaWEuDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBMaWtlZCBtZWRpYSBkYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGdldExpa2VkTWVkaWEoJG1heGlkID0gTlVMTCkNCiAgICB7DQogICAgICAgIHJldHVybiAkdGhpcy0+cmVxdWVzdCgiZmVlZC9saWtlZC8/bWF4X2lkPSIgLiAkbWF4aWQpWzFdOw0KICAgIH0NCg0KICAgIHByb3RlY3RlZCBmdW5jdGlvbiBidWlsZEJvZHkoJGJvZGllcywgJGJvdW5kYXJ5KQ0KICAgIHsNCiAgICAgICAgJGJvZHkgPSAiIjsNCiAgICAgICAgZm9yZWFjaCggJGJvZGllcyBhcyAkYiApIA0KICAgICAgICB7DQogICAgICAgICAgICAkYm9keSAuPSAiLS0iIC4gJGJvdW5kYXJ5IC4gIlxyXG4iOw0KICAgICAgICAgICAgJGJvZHkgLj0gIkNvbnRlbnQtRGlzcG9zaXRpb246ICIgLiAkYlsidHlwZSJdIC4gIjsgbmFtZT1cIiIgLiAkYlsibmFtZSJdIC4gIlwiIjsNCiAgICAgICAgICAgIGlmKCBpc3NldCgkYlsiZmlsZW5hbWUiXSkgKSANCiAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAkZXh0ID0gcGF0aGluZm8oJGJbImZpbGVuYW1lIl0sIFBBVEhJTkZPX0VYVEVOU0lPTik7DQogICAgICAgICAgICAgICAgJGJvZHkgLj0gIjsgZmlsZW5hbWU9XCIiIC4gInBlbmRpbmdfbWVkaWFfIiAuIG51bWJlcl9mb3JtYXQocm91bmQobWljcm90aW1lKHRydWUpICogMTAwMCksIDAsICIiLCAiIikgLiAiLiIgLiAkZXh0IC4gIlwiIjsNCiAgICAgICAgICAgIH0NCg0KICAgICAgICAgICAgaWYoIGlzc2V0KCRiWyJoZWFkZXJzIl0pICYmIGlzX2FycmF5KCRiWyJoZWFkZXJzIl0pICkgDQogICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgZm9yZWFjaCggJGJbImhlYWRlcnMiXSBhcyAkaGVhZGVyICkgDQogICAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICAgICAkYm9keSAuPSAiXHJcbiIgLiAkaGVhZGVyOw0KICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgIH0NCg0KICAgICAgICAgICAgJGJvZHkgLj0gIlxyXG5cclxuIiAuICRiWyJkYXRhIl0gLiAiXHJcbiI7DQogICAgICAgIH0NCiAgICAgICAgJGJvZHkgLj0gIi0tIiAuICRib3VuZGFyeSAuICItLSI7DQogICAgICAgIHJldHVybiAkYm9keTsNCiAgICB9DQoNCiAgICBwcm90ZWN0ZWQgZnVuY3Rpb24gcmVxdWVzdCgkZW5kcG9pbnQsICRwb3N0ID0gTlVMTCwgJGxvZ2luID0gZmFsc2UpDQogICAgew0KICAgICAgICBpZiggISR0aGlzLT5pc0xvZ2dlZEluICYmICEkbG9naW4gKSANCiAgICAgICAgew0KICAgICAgICAgICAgdGhyb3cgbmV3IEV4Y2VwdGlvbigiTm90IGxvZ2dlZCBpblxuIik7DQogICAgICAgIH0NCg0KICAgICAgICAkaGVhZGVycyA9IGFycmF5KCAiQ29ubmVjdGlvbjogY2xvc2UiLCAiQWNjZXB0OiAqLyoiLCAiWC1JRy1DYXBhYmlsaXRpZXM6ICIgLiBDb25zdGFudHM6OlhfSUdfQ2FwYWJpbGl0aWVzLCAiWC1JRy1Db25uZWN0aW9uLVR5cGU6IFdJRkkiLCAiQ29udGVudC10eXBlOiBhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQ7IGNoYXJzZXQ9VVRGLTgiLCAiQWNjZXB0LUxhbmd1YWdlOiB0ci1UUiIsICJYX0ZPUldBUkRFRF9GT1I6ICIgLiAkdGhpcy0+c2V0dGluZ3MtPmdldCgiaXAiKSwgIlJFTU9URV9BRERSOiAiIC4gJHRoaXMtPnNldHRpbmdzLT5nZXQoImlwIikgKTsNCiAgICAgICAgJGNoID0gY3VybF9pbml0KCk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9VUkwsIENvbnN0YW50czo6QVBJX1VSTCAuICRlbmRwb2ludCk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9VU0VSQUdFTlQsICR0aGlzLT5zZXR0aW5ncy0+Z2V0KCJ1c2VyX2FnZW50IikpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfUkVUVVJOVFJBTlNGRVIsIHRydWUpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfRk9MTE9XTE9DQVRJT04sIHRydWUpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfSEVBREVSLCB0cnVlKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX0hUVFBIRUFERVIsICRoZWFkZXJzKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1ZFUkJPU0UsIGZhbHNlKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1NTTF9WRVJJRllQRUVSLCBmYWxzZSk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9TU0xfVkVSSUZZSE9TVCwgZmFsc2UpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfQ09PS0lFRklMRSwgJHRoaXMtPklHRGF0YVBhdGggLiAoc3RyaW5nKSAkdGhpcy0+dXNlcm5hbWUgLiAiLmRhdCIpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfQ09PS0lFSkFSLCAkdGhpcy0+SUdEYXRhUGF0aCAuIChzdHJpbmcpICR0aGlzLT51c2VybmFtZSAuICIuZGF0Iik7DQogICAgICAgIGlmKCAkcG9zdCApIA0KICAgICAgICB7DQogICAgICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfUE9TVCwgdHJ1ZSk7DQogICAgICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfUE9TVEZJRUxEUywgJHBvc3QpOw0KICAgICAgICB9DQoNCiAgICAgICAgJHJlc3AgPSBjdXJsX2V4ZWMoJGNoKTsNCiAgICAgICAgJGhlYWRlcl9sZW4gPSBjdXJsX2dldGluZm8oJGNoLCBDVVJMSU5GT19IRUFERVJfU0laRSk7DQogICAgICAgICRoZWFkZXIgPSBzdWJzdHIoJHJlc3AsIDAsICRoZWFkZXJfbGVuKTsNCiAgICAgICAgJGJvZHkgPSBzdWJzdHIoJHJlc3AsICRoZWFkZXJfbGVuKTsNCiAgICAgICAgY3VybF9jbG9zZSgkY2gpOw0KICAgICAgICBpZiggJHRoaXMtPmRlYnVnICkgDQogICAgICAgIHsNCiAgICAgICAgICAgIGVjaG8gIlJFUVVFU1Q6ICIgLiAkZW5kcG9pbnQgLiAiXG4iOw0KICAgICAgICAgICAgaWYoICFpc19udWxsKCRwb3N0KSAmJiAhaXNfYXJyYXkoJHBvc3QpICkgDQogICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgZWNobyAiREFUQTogIiAuIHVybGRlY29kZSgkcG9zdCkgLiAiXG4iOw0KICAgICAgICAgICAgfQ0KDQogICAgICAgICAgICBlY2hvICJSRVNQT05TRTogIiAuICRib2R5IC4gIlxuXG4iOw0KICAgICAgICB9DQoNCiAgICAgICAgcmV0dXJuIGFycmF5KCAkaGVhZGVyLCBqc29uX2RlY29kZSgkYm9keSwgdHJ1ZSkgKTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogSXMgVXNlciBMb2dnZWQgaW4/DQogICAgICAgICAqIEByZXR1cm4gYm9vbA0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBpc0xvZ2dlZEluKCkNCiAgICB7DQogICAgICAgIHJldHVybiAkdGhpcy0+aXNMb2dnZWRJbjsNCiAgICB9DQoNCn0NCg0KDQpjbGFzcyBJbnN0YWdyYW1XZWINCnsNCiAgICBwcm90ZWN0ZWQgJHVzZXJuYW1lID0gTlVMTDsNCiAgICBwcm90ZWN0ZWQgJHVzZXJuYW1lX2lkID0gTlVMTDsNCiAgICBwcm90ZWN0ZWQgJHRva2VuID0gTlVMTDsNCiAgICBwcm90ZWN0ZWQgJGlzTG9nZ2VkSW4gPSBmYWxzZTsNCiAgICBwcm90ZWN0ZWQgJElHRGF0YVBhdGggPSBOVUxMOw0KLyoqDQogICAgICAgICAqIEB2YXIgU2V0dGluZ3MNCiAgICAgICAgICovDQogICAgcHVibGljICRzZXR0aW5ncyA9IE5VTEw7DQoNCiAgICAvKioNCiAgICAgICAgICogRGVmYXVsdCBjbGFzcyBjb25zdHJ1Y3Rvci4NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkdXNlcm5hbWUNCiAgICAgICAgICogICBZb3VyIEluc3RhZ3JhbSB1c2VybmFtZS4NCiAgICAgICAgICogQHBhcmFtICAgICAgICAkSUdEYXRhUGF0aA0KICAgICAgICAgKiAgIERlZmF1bHQgZm9sZGVyIHRvIHN0b3JlIGRhdGEsIHlvdSBjYW4gY2hhbmdlIGl0Lg0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBfX2NvbnN0cnVjdCgkdXNlcm5hbWUsICRJR0RhdGFQYXRoID0gTlVMTCwgJGZvcmNlVXNlcklQID0gZmFsc2UpDQogICAgew0KICAgICAgICAkdXNlcm5hbWUgPSB0cmltKCR1c2VybmFtZSk7DQogICAgICAgIGlmKCAhaXNfbnVsbCgkSUdEYXRhUGF0aCkgKSANCiAgICAgICAgew0KICAgICAgICAgICAgJHRoaXMtPklHRGF0YVBhdGggPSAkSUdEYXRhUGF0aDsNCiAgICAgICAgfQ0KICAgICAgICBlbHNlDQogICAgICAgIHsNCiAgICAgICAgICAgICR0aGlzLT5JR0RhdGFQYXRoID0gV293OjpnZXQoInByb2plY3QvY29va2llUGF0aCIpIC4gImluc3RhZ3JhbS8iOw0KICAgICAgICB9DQoNCiAgICAgICAgJHRoaXMtPnNldFVzZXIoJHVzZXJuYW1lLCAkZm9yY2VVc2VySVApOw0KICAgIH0NCg0KICAgIHByb3RlY3RlZCBmdW5jdGlvbiBjaGVja1NldHRpbmdzKCR1c2VybmFtZSwgJGZvcmNlVXNlcklQID0gZmFsc2UpDQogICAgew0KICAgICAgICAkdGhpcy0+c2V0dGluZ3MgPSBuZXcgU2V0dGluZ3MoJHRoaXMtPklHRGF0YVBhdGggLiAkdXNlcm5hbWUgLiAiLmNuZiIpOw0KICAgICAgICBpZiggJHRoaXMtPnNldHRpbmdzLT5nZXQoImlwIikgPT0gTlVMTCB8fCAkZm9yY2VVc2VySVAgKSANCiAgICAgICAgew0KICAgICAgICAgICAgJGlwQWRyZXNzID0gIjc4LiIgLiByYW5kKDE2MCwgMTkxKSAuICIuIiAuIHJhbmQoMSwgMjU1KSAuICIuIiAuIHJhbmQoMSwgMjU1KTsNCiAgICAgICAgICAgIGlmKCAkZm9yY2VVc2VySVAgJiYgIWVtcHR5KCRfU0VSVkVSWyJSRU1PVEVfQUREUiJdKSApIA0KICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICRpcEFkcmVzcyA9ICRfU0VSVkVSWyJSRU1PVEVfQUREUiJdOw0KICAgICAgICAgICAgfQ0KDQogICAgICAgICAgICAkdGhpcy0+c2V0dGluZ3MtPnNldCgiaXAiLCAkaXBBZHJlc3MpOw0KICAgICAgICB9DQoNCiAgICAgICAgaWYoIGZpbGVfZXhpc3RzKCR0aGlzLT5JR0RhdGFQYXRoIC4gKHN0cmluZykgJHVzZXJuYW1lIC4gIi5kYXQiKSAmJiAoJHRoaXMtPnNldHRpbmdzLT5nZXQoInVzZXJuYW1lX2lkIikgPT0gTlVMTCB8fCAkdGhpcy0+c2V0dGluZ3MtPmdldCgidG9rZW4iKSA9PSBOVUxMKSApIA0KICAgICAgICB7DQogICAgICAgICAgICAkY29va2llRGF0YSA9IFV0aWxzOjpjb29raWVUb0FycmF5KGZpbGVfZ2V0X2NvbnRlbnRzKCR0aGlzLT5JR0RhdGFQYXRoIC4gKHN0cmluZykgJHVzZXJuYW1lIC4gIi5kYXQiKSwgInd3dy5pbnN0YWdyYW0uY29tIik7DQogICAgICAgICAgICBpZiggJHRoaXMtPnNldHRpbmdzLT5nZXQoInVzZXJuYW1lX2lkIikgPT0gTlVMTCAmJiBpc3NldCgkY29va2llRGF0YVsiZHNfdXNlcl9pZCJdKSApIA0KICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICR0aGlzLT5zZXR0aW5ncy0+c2V0KCJ1c2VybmFtZV9pZCIsICRjb29raWVEYXRhWyJkc191c2VyX2lkIl0pOw0KICAgICAgICAgICAgfQ0KDQogICAgICAgICAgICBpZiggJHRoaXMtPnNldHRpbmdzLT5nZXQoInRva2VuIikgPT0gTlVMTCAmJiBpc3NldCgkY29va2llRGF0YVsiY3NyZnRva2VuIl0pICkgDQogICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgJHRoaXMtPnNldHRpbmdzLT5zZXQoInRva2VuIiwgJGNvb2tpZURhdGFbImNzcmZ0b2tlbiJdKTsNCiAgICAgICAgICAgIH0NCg0KICAgICAgICB9DQoNCiAgICAgICAgaWYoICR0aGlzLT5zZXR0aW5ncy0+Z2V0KCJ3ZWJfdXNlcl9hZ2VudCIpID09IE5VTEwgKSANCiAgICAgICAgew0KICAgICAgICAgICAgJHVzZXJBZ2VudHMgPSBleHBsb2RlKFBIUF9FT0wsIGZpbGVfZ2V0X2NvbnRlbnRzKFdvdzo6Z2V0KCJwcm9qZWN0L2Nvb2tpZVBhdGgiKSAuICJkZXZpY2UvYnJvd3NlcnMuY3N2IikpOw0KICAgICAgICAgICAgJGFnZW50SW5kZXggPSByYW5kKDAsIGNvdW50KCR1c2VyQWdlbnRzKSAtIDEpOw0KICAgICAgICAgICAgJHVzZXJBZ2VudCA9ICR1c2VyQWdlbnRzWyRhZ2VudEluZGV4XTsNCiAgICAgICAgICAgICR0aGlzLT5zZXR0aW5ncy0+c2V0KCJ3ZWJfdXNlcl9hZ2VudCIsICR1c2VyQWdlbnQpOw0KICAgICAgICB9DQoNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogU2V0IHRoZSB1c2VyLiBNYW5hZ2UgbXVsdGlwbGUgYWNjb3VudHMuDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJHVzZXJuYW1lDQogICAgICAgICAqICAgWW91ciBJbnN0YWdyYW0gdXNlcm5hbWUuDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIHNldFVzZXIoJHVzZXJuYW1lLCAkZm9yY2VVc2VySVAgPSBmYWxzZSkNCiAgICB7DQogICAgICAgICR0aGlzLT51c2VybmFtZSA9ICR1c2VybmFtZTsNCiAgICAgICAgJHRoaXMtPmNoZWNrU2V0dGluZ3MoJHVzZXJuYW1lLCAkZm9yY2VVc2VySVApOw0KICAgICAgICBpZiggZmlsZV9leGlzdHMoJHRoaXMtPklHRGF0YVBhdGggLiAoc3RyaW5nKSAkdGhpcy0+dXNlcm5hbWUgLiAiLmRhdCIpICYmICR0aGlzLT5zZXR0aW5ncy0+Z2V0KCJ1c2VybmFtZV9pZCIpICE9IE5VTEwgJiYgJHRoaXMtPnNldHRpbmdzLT5nZXQoInRva2VuIikgIT0gTlVMTCApIA0KICAgICAgICB7DQogICAgICAgICAgICAkdGhpcy0+aXNMb2dnZWRJbiA9IHRydWU7DQogICAgICAgICAgICAkdGhpcy0+dXNlcm5hbWVfaWQgPSAkdGhpcy0+c2V0dGluZ3MtPmdldCgidXNlcm5hbWVfaWQiKTsNCiAgICAgICAgICAgICR0aGlzLT50b2tlbiA9ICR0aGlzLT5zZXR0aW5ncy0+Z2V0KCJ0b2tlbiIpOw0KICAgICAgICB9DQogICAgICAgIGVsc2UNCiAgICAgICAgew0KICAgICAgICAgICAgJHRoaXMtPmlzTG9nZ2VkSW4gPSBmYWxzZTsNCiAgICAgICAgfQ0KDQogICAgfQ0KDQogICAgcHVibGljIGZ1bmN0aW9uIGdldERhdGEoKQ0KICAgIHsNCiAgICAgICAgcmV0dXJuIGFycmF5KCAidXNlcm5hbWUiID0+ICR0aGlzLT51c2VybmFtZSwgInVzZXJuYW1lX2lkIiA9PiAkdGhpcy0+dXNlcm5hbWVfaWQsICJ0b2tlbiIgPT4gJHRoaXMtPnRva2VuLCAidXNlcl9hZ2VudCIgPT4gJHRoaXMtPnNldHRpbmdzLT5nZXQoIndlYl91c2VyX2FnZW50IiksICJpcCIgPT4gJHRoaXMtPnNldHRpbmdzLT5nZXQoImlwIikgKTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogQ29tbWVudCBtZWRpYS4NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkbWVkaWFJZA0KICAgICAgICAgKiAgIE1lZGlhIGlkDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJGNvbW1lbnRUZXh0DQogICAgICAgICAqICAgQ29tbWVudCBUZXh0DQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBjb21tZW50IG1lZGlhIGRhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gY29tbWVudCgkbWVkaWFJZCwgJGNvbW1lbnRUZXh0KQ0KICAgIHsNCiAgICAgICAgJGFyck1lZGlhSUQgPSBleHBsb2RlKCJfIiwgJG1lZGlhSWQpOw0KICAgICAgICAkbWVkaWFJZCA9ICRhcnJNZWRpYUlEWzBdOw0KICAgICAgICAkcG9zdERhdGEgPSAiY29tbWVudF90ZXh0PSIgLiAkY29tbWVudFRleHQ7DQogICAgICAgICRoZWFkZXJzID0gYXJyYXkoICApOw0KICAgICAgICAkaGVhZGVyc1tdID0gIlJlZmVyZXI6IGh0dHBzOi8vd3d3Lmluc3RhZ3JhbS5jb20vIjsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJETlQ6IDEiOw0KICAgICAgICAkaGVhZGVyc1tdID0gIk9yaWdpbjogaHR0cHM6Ly93d3cuaW5zdGFncmFtLmNvbS8iOw0KICAgICAgICAkaGVhZGVyc1tdID0gIlgtQ1NSRlRva2VuOiAiIC4gdHJpbSgkdGhpcy0+dG9rZW4pOw0KICAgICAgICAkaGVhZGVyc1tdID0gIlgtUmVxdWVzdGVkLVdpdGg6IFhNTEh0dHBSZXF1ZXN0IjsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJYLUluc3RhZ3JhbS1BSkFYOiAxIjsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJDb25uZWN0aW9uOiBjbG9zZSI7DQogICAgICAgICRoZWFkZXJzW10gPSAiQ2FjaGUtQ29udHJvbDogbWF4LWFnZT0wIjsNCiAgICAgICAgcmV0dXJuICR0aGlzLT5yZXF1ZXN0KCJ3ZWIvY29tbWVudHMvIiAuICRtZWRpYUlkIC4gIi9hZGQvIiwgJGhlYWRlcnMsICRwb3N0RGF0YSlbMV07DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIEdldCB1c2VybmFtZSBpbmZvLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICR1c2VybmFtZQ0KICAgICAgICAgKiAgIFVzZXJuYW1lDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBVc2VybmFtZSBkYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGdldFVzZXJuYW1lSW5mbygkdXNlcm5hbWUpDQogICAgew0KICAgICAgICAkaGVhZGVycyA9IGFycmF5KCAgKTsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJSZWZlcmVyOiBodHRwczovL3d3dy5pbnN0YWdyYW0uY29tLyI7DQogICAgICAgICRoZWFkZXJzW10gPSAiRE5UOiAxIjsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJPcmlnaW46IGh0dHBzOi8vd3d3Lmluc3RhZ3JhbS5jb20vIjsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJYLUNTUkZUb2tlbjogIiAuIHRyaW0oJHRoaXMtPnRva2VuKTsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJYLVJlcXVlc3RlZC1XaXRoOiBYTUxIdHRwUmVxdWVzdCI7DQogICAgICAgICRoZWFkZXJzW10gPSAiWC1JbnN0YWdyYW0tQUpBWDogMSI7DQogICAgICAgICRoZWFkZXJzW10gPSAiQ29ubmVjdGlvbjogY2xvc2UiOw0KICAgICAgICAkaGVhZGVyc1tdID0gIkNhY2hlLUNvbnRyb2w6IG1heC1hZ2U9MCI7DQogICAgICAgIHJldHVybiAkdGhpcy0+cmVxdWVzdCgoc3RyaW5nKSAkdXNlcm5hbWUgLiAiLz9fX2E9MSIsICRoZWFkZXJzKVsxXTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogTWVkaWEgaW5mbw0KICAgICAgICAgKg0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICRtZWRpYUNvZGUNCiAgICAgICAgICogICBNZWRpYSBjb2RlDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBkZWxldGUgcmVxdWVzdCBkYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIG1lZGlhSW5mbygkbWVkaWFDb2RlKQ0KICAgIHsNCiAgICAgICAgJGhlYWRlcnMgPSBhcnJheSggICk7DQogICAgICAgICRoZWFkZXJzW10gPSAiUmVmZXJlcjogaHR0cHM6Ly93d3cuaW5zdGFncmFtLmNvbS8iOw0KICAgICAgICAkaGVhZGVyc1tdID0gIkROVDogMSI7DQogICAgICAgICRoZWFkZXJzW10gPSAiT3JpZ2luOiBodHRwczovL3d3dy5pbnN0YWdyYW0uY29tLyI7DQogICAgICAgICRoZWFkZXJzW10gPSAiWC1DU1JGVG9rZW46ICIgLiB0cmltKCR0aGlzLT50b2tlbik7DQogICAgICAgICRoZWFkZXJzW10gPSAiWC1SZXF1ZXN0ZWQtV2l0aDogWE1MSHR0cFJlcXVlc3QiOw0KICAgICAgICAkaGVhZGVyc1tdID0gIlgtSW5zdGFncmFtLUFKQVg6IDEiOw0KICAgICAgICAkaGVhZGVyc1tdID0gIkNvbm5lY3Rpb246IGNsb3NlIjsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJDYWNoZS1Db250cm9sOiBtYXgtYWdlPTAiOw0KICAgICAgICByZXR1cm4gJHRoaXMtPnJlcXVlc3QoInAvIiAuICRtZWRpYUNvZGUgLiAiLz9fX2E9MSIsICRoZWFkZXJzKVsxXTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogTGlrZSBwaG90byBvciB2aWRlby4NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkbWVkaWFJZA0KICAgICAgICAgKiAgIE1lZGlhIGlkDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBzdGF0dXMgcmVxdWVzdA0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBsaWtlKCRtZWRpYUlkKQ0KICAgIHsNCiAgICAgICAgJGFyck1lZGlhSUQgPSBleHBsb2RlKCJfIiwgJG1lZGlhSWQpOw0KICAgICAgICAkbWVkaWFJZCA9ICRhcnJNZWRpYUlEWzBdOw0KICAgICAgICAkaGVhZGVycyA9IGFycmF5KCAgKTsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJSZWZlcmVyOiBodHRwczovL3d3dy5pbnN0YWdyYW0uY29tL2luc3RhZ3JhbS8iOw0KICAgICAgICAkaGVhZGVyc1tdID0gIkROVDogMSI7DQogICAgICAgICRoZWFkZXJzW10gPSAiT3JpZ2luOiBodHRwczovL3d3dy5pbnN0YWdyYW0uY29tLyI7DQogICAgICAgICRoZWFkZXJzW10gPSAiWC1DU1JGVG9rZW46ICIgLiB0cmltKCR0aGlzLT50b2tlbik7DQogICAgICAgICRoZWFkZXJzW10gPSAiWC1SZXF1ZXN0ZWQtV2l0aDogWE1MSHR0cFJlcXVlc3QiOw0KICAgICAgICAkaGVhZGVyc1tdID0gIlgtSW5zdGFncmFtLUFKQVg6IDEiOw0KICAgICAgICAkaGVhZGVyc1tdID0gIkNvbm5lY3Rpb246IGNsb3NlIjsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJDYWNoZS1Db250cm9sOiBtYXgtYWdlPTAiOw0KICAgICAgICByZXR1cm4gJHRoaXMtPnJlcXVlc3QoIndlYi9saWtlcy8iIC4gJG1lZGlhSWQgLiAiL2xpa2UvIiwgJGhlYWRlcnMsIHRydWUpWzFdOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBVbmxpa2UgcGhvdG8gb3IgdmlkZW8uDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJG1lZGlhSWQNCiAgICAgICAgICogICBNZWRpYSBpZA0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgc3RhdHVzIHJlcXVlc3QNCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gdW5saWtlKCRtZWRpYUlkKQ0KICAgIHsNCiAgICAgICAgJGhlYWRlcnMgPSBhcnJheSggICk7DQogICAgICAgICRoZWFkZXJzW10gPSAiUmVmZXJlcjogaHR0cHM6Ly93d3cuaW5zdGFncmFtLmNvbS8iOw0KICAgICAgICAkaGVhZGVyc1tdID0gIkROVDogMSI7DQogICAgICAgICRoZWFkZXJzW10gPSAiT3JpZ2luOiBodHRwczovL3d3dy5pbnN0YWdyYW0uY29tLyI7DQogICAgICAgICRoZWFkZXJzW10gPSAiWC1DU1JGVG9rZW46ICIgLiB0cmltKCR0aGlzLT50b2tlbik7DQogICAgICAgICRoZWFkZXJzW10gPSAiWC1SZXF1ZXN0ZWQtV2l0aDogWE1MSHR0cFJlcXVlc3QiOw0KICAgICAgICAkaGVhZGVyc1tdID0gIlgtSW5zdGFncmFtLUFKQVg6IDEiOw0KICAgICAgICAkaGVhZGVyc1tdID0gIkNvbm5lY3Rpb246IGNsb3NlIjsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJDYWNoZS1Db250cm9sOiBtYXgtYWdlPTAiOw0KICAgICAgICByZXR1cm4gJHRoaXMtPnJlcXVlc3QoIndlYi9saWtlcy8iIC4gJG1lZGlhSWQgLiAiL3VubGlrZS8iLCAkaGVhZGVycywgdHJ1ZSlbMV07DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIEZvbGxvdy4NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkdXNlcklkDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBGcmllbmRzaGlwIHN0YXR1cyBkYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGZvbGxvdygkdXNlcklkKQ0KICAgIHsNCiAgICAgICAgJGhlYWRlcnMgPSBhcnJheSggICk7DQogICAgICAgICRoZWFkZXJzW10gPSAiUmVmZXJlcjogaHR0cHM6Ly93d3cuaW5zdGFncmFtLmNvbS9pbnN0YWdyYW0vIjsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJETlQ6IDEiOw0KICAgICAgICAkaGVhZGVyc1tdID0gIk9yaWdpbjogaHR0cHM6Ly93d3cuaW5zdGFncmFtLmNvbS8iOw0KICAgICAgICAkaGVhZGVyc1tdID0gIlgtQ1NSRlRva2VuOiAiIC4gdHJpbSgkdGhpcy0+dG9rZW4pOw0KICAgICAgICAkaGVhZGVyc1tdID0gIlgtUmVxdWVzdGVkLVdpdGg6IFhNTEh0dHBSZXF1ZXN0IjsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJYLUluc3RhZ3JhbS1BSkFYOiAxIjsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJDb25uZWN0aW9uOiBjbG9zZSI7DQogICAgICAgICRoZWFkZXJzW10gPSAiQ2FjaGUtQ29udHJvbDogbWF4LWFnZT0wIjsNCiAgICAgICAgcmV0dXJuICR0aGlzLT5yZXF1ZXN0KCJ3ZWIvZnJpZW5kc2hpcHMvIiAuICR1c2VySWQgLiAiL2ZvbGxvdy8iLCAkaGVhZGVycywgdHJ1ZSlbMV07DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIFVuZm9sbG93Lg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICR1c2VySWQNCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIEZyaWVuZHNoaXAgc3RhdHVzIGRhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gdW5mb2xsb3coJHVzZXJJZCkNCiAgICB7DQogICAgICAgICRoZWFkZXJzID0gYXJyYXkoICApOw0KICAgICAgICAkaGVhZGVyc1tdID0gIlJlZmVyZXI6IGh0dHBzOi8vd3d3Lmluc3RhZ3JhbS5jb20vaW5zdGFncmFtLyI7DQogICAgICAgICRoZWFkZXJzW10gPSAiRE5UOiAxIjsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJPcmlnaW46IGh0dHBzOi8vd3d3Lmluc3RhZ3JhbS5jb20vIjsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJYLUNTUkZUb2tlbjogIiAuIHRyaW0oJHRoaXMtPnRva2VuKTsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJYLVJlcXVlc3RlZC1XaXRoOiBYTUxIdHRwUmVxdWVzdCI7DQogICAgICAgICRoZWFkZXJzW10gPSAiWC1JbnN0YWdyYW0tQUpBWDogMSI7DQogICAgICAgICRoZWFkZXJzW10gPSAiQ29ubmVjdGlvbjogY2xvc2UiOw0KICAgICAgICAkaGVhZGVyc1tdID0gIkNhY2hlLUNvbnRyb2w6IG1heC1hZ2U9MCI7DQogICAgICAgIHJldHVybiAkdGhpcy0+cmVxdWVzdCgid2ViL2ZyaWVuZHNoaXBzLyIgLiAkdXNlcklkIC4gIi91bmZvbGxvdy8iLCAkaGVhZGVycywgdHJ1ZSlbMV07DQogICAgfQ0KDQogICAgcHJvdGVjdGVkIGZ1bmN0aW9uIHJlcXVlc3QoJGVuZHBvaW50LCBhcnJheSAkb3B0aW9uYWxoZWFkZXJzLCAkcG9zdCA9IE5VTEwpDQogICAgew0KICAgICAgICBpZiggISR0aGlzLT5pc0xvZ2dlZEluICkgDQogICAgICAgIHsNCiAgICAgICAgICAgIHRocm93IG5ldyBFeGNlcHRpb24oIk5vdCBsb2dnZWQgaW5cbiIpOw0KICAgICAgICB9DQoNCiAgICAgICAgJGhlYWRlcnMgPSBhcnJheSggIkFjY2VwdDogKi8qIiwgIkFjY2VwdC1MYW5ndWFnZTogdHI7cT0xIiwgIlhfRk9SV0FSREVEX0ZPUjogIiAuICR0aGlzLT5zZXR0aW5ncy0+Z2V0KCJpcCIpLCAiUkVNT1RFX0FERFI6ICIgLiAkdGhpcy0+c2V0dGluZ3MtPmdldCgiaXAiKSApOw0KICAgICAgICAkaGVhZGVycyA9IGFycmF5X21lcmdlKCRoZWFkZXJzLCAkb3B0aW9uYWxoZWFkZXJzKTsNCiAgICAgICAgJGNoID0gY3VybF9pbml0KCk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9VUkwsIENvbnN0YW50czo6V0VCX1VSTCAuICRlbmRwb2ludCk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9VU0VSQUdFTlQsICR0aGlzLT5zZXR0aW5ncy0+Z2V0KCJ3ZWJfdXNlcl9hZ2VudCIpKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1JFVFVSTlRSQU5TRkVSLCB0cnVlKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX0ZPTExPV0xPQ0FUSU9OLCB0cnVlKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX0hFQURFUiwgdHJ1ZSk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9IVFRQSEVBREVSLCAkaGVhZGVycyk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9WRVJCT1NFLCBmYWxzZSk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9TU0xfVkVSSUZZUEVFUiwgZmFsc2UpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfU1NMX1ZFUklGWUhPU1QsIGZhbHNlKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX0NPT0tJRUZJTEUsICR0aGlzLT5JR0RhdGFQYXRoIC4gKHN0cmluZykgJHRoaXMtPnVzZXJuYW1lIC4gIi5kYXQiKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX0NPT0tJRUpBUiwgJHRoaXMtPklHRGF0YVBhdGggLiAoc3RyaW5nKSAkdGhpcy0+dXNlcm5hbWUgLiAiLmRhdCIpOw0KICAgICAgICBpZiggJHBvc3QgKSANCiAgICAgICAgew0KICAgICAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1BPU1QsIHRydWUpOw0KICAgICAgICAgICAgaWYoIGlzX3N0cmluZygkcG9zdCkgKSANCiAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfUE9TVEZJRUxEUywgJHBvc3QpOw0KICAgICAgICAgICAgfQ0KDQogICAgICAgIH0NCg0KICAgICAgICAkcmVzcCA9IGN1cmxfZXhlYygkY2gpOw0KICAgICAgICAkaGVhZGVyX2xlbiA9IGN1cmxfZ2V0aW5mbygkY2gsIENVUkxJTkZPX0hFQURFUl9TSVpFKTsNCiAgICAgICAgJGhlYWRlciA9IHN1YnN0cigkcmVzcCwgMCwgJGhlYWRlcl9sZW4pOw0KICAgICAgICAkYm9keSA9IHN1YnN0cigkcmVzcCwgJGhlYWRlcl9sZW4pOw0KICAgICAgICBjdXJsX2Nsb3NlKCRjaCk7DQogICAgICAgIHJldHVybiBhcnJheSggJGhlYWRlciwganNvbl9kZWNvZGUoJGJvZHksIHRydWUpICk7DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIElzIFVzZXIgTG9nZ2VkIGluPw0KICAgICAgICAgKiBAcmV0dXJuIGJvb2wNCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gaXNMb2dnZWRJbigpDQogICAgew0KICAgICAgICByZXR1cm4gJHRoaXMtPmlzTG9nZ2VkSW47DQogICAgfQ0KDQogICAgcHVibGljIGZ1bmN0aW9uIGlzVmFsaWQoKQ0KICAgIHsNCiAgICAgICAgJGhlYWRlcnMgPSBhcnJheSggICk7DQogICAgICAgICRoZWFkZXJzW10gPSAiUmVmZXJlcjogaHR0cHM6Ly93d3cuaW5zdGFncmFtLmNvbS8iOw0KICAgICAgICAkaGVhZGVyc1tdID0gIkROVDogMSI7DQogICAgICAgICRoZWFkZXJzW10gPSAiT3JpZ2luOiBodHRwczovL3d3dy5pbnN0YWdyYW0uY29tLyI7DQogICAgICAgICRoZWFkZXJzW10gPSAiWC1DU1JGVG9rZW46ICIgLiB0cmltKCR0aGlzLT50b2tlbik7DQogICAgICAgICRoZWFkZXJzW10gPSAiWC1SZXF1ZXN0ZWQtV2l0aDogWE1MSHR0cFJlcXVlc3QiOw0KICAgICAgICAkaGVhZGVyc1tdID0gIlgtSW5zdGFncmFtLUFKQVg6IDEiOw0KICAgICAgICAkaGVhZGVyc1tdID0gIkNvbm5lY3Rpb246IGNsb3NlIjsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJDYWNoZS1Db250cm9sOiBtYXgtYWdlPTAiOw0KICAgICAgICBsaXN0KCRoZWFkZXIpID0gJHRoaXMtPnJlcXVlc3QoImFjY291bnRzL2FjdGl2aXR5Lz9fX2E9MSIsICRoZWFkZXJzKTsNCiAgICAgICAgcmV0dXJuIChzdHJwb3MoJGhlYWRlciwgIkhUVFAvMS4xIDIwMCIpID09PSBmYWxzZSA/IGZhbHNlIDogdHJ1ZSk7DQogICAgfQ0KDQp9DQoNCg0KY2xhc3MgU2lnbmF0dXJlVXRpbHMNCnsNCiAgICBwdWJsaWMgc3RhdGljIGZ1bmN0aW9uIGdlbmVyYXRlU2lnbmF0dXJlKCRkYXRhKQ0KICAgIHsNCiAgICAgICAgJGhhc2ggPSBoYXNoX2htYWMoInNoYTI1NiIsICRkYXRhLCBDb25zdGFudHM6OklHX1NJR19LRVkpOw0KICAgICAgICByZXR1cm4gImlnX3NpZ19rZXlfdmVyc2lvbj0iIC4gQ29uc3RhbnRzOjpTSUdfS0VZX1ZFUlNJT04gLiAiJnNpZ25lZF9ib2R5PSIgLiAkaGFzaCAuICIuIiAuIHVybGVuY29kZSgkZGF0YSk7DQogICAgfQ0KDQogICAgcHVibGljIHN0YXRpYyBmdW5jdGlvbiBnZW5lcmF0ZURldmljZUlkKCRzZWVkKQ0KICAgIHsNCiAgICAgICAgJHZvbGF0aWxlX3NlZWQgPSBmaWxlbXRpbWUoX19ESVJfXyk7DQogICAgICAgIHJldHVybiAiYW5kcm9pZC0iIC4gc3Vic3RyKG1kNSgkc2VlZCAuICR2b2xhdGlsZV9zZWVkKSwgMTYpOw0KICAgIH0NCg0KICAgIHB1YmxpYyBzdGF0aWMgZnVuY3Rpb24gZ2VuZXJhdGVVVUlEKCR0eXBlKQ0KICAgIHsNCiAgICAgICAgJHV1aWQgPSBzcHJpbnRmKCIlMDR4JTA0eC0lMDR4LSUwNHgtJTA0eC0lMDR4JTA0eCUwNHgiLCBtdF9yYW5kKDAsIDY1NTM1KSwgbXRfcmFuZCgwLCA2NTUzNSksIG10X3JhbmQoMCwgNjU1MzUpLCBtdF9yYW5kKDAsIDQwOTUpIHwgMTYzODQsIG10X3JhbmQoMCwgMTYzODMpIHwgMzI3NjgsIG10X3JhbmQoMCwgNjU1MzUpLCBtdF9yYW5kKDAsIDY1NTM1KSwgbXRfcmFuZCgwLCA2NTUzNSkpOw0KICAgICAgICByZXR1cm4gKCR0eXBlID8gJHV1aWQgOiBzdHJfcmVwbGFjZSgiLSIsICIiLCAkdXVpZCkpOw0KICAgIH0NCg0KfQ0KDQoNCmNsYXNzIFVzZXJBZ2VudA0Kew0KICAgIHByb3RlY3RlZCAkcGFyZW50ID0gTlVMTDsNCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBfX2NvbnN0cnVjdCgkcGFyZW50KQ0KICAgIHsNCiAgICAgICAgJHRoaXMtPnBhcmVudCA9ICRwYXJlbnQ7DQogICAgfQ0KDQogICAgcHJvdGVjdGVkIGZ1bmN0aW9uIGdldERldmljZURhdGEoKQ0KICAgIHsNCiAgICAgICAgJGNzdmZpbGUgPSBXb3c6OmdldCgicHJvamVjdC9jb29raWVQYXRoIikgLiAiZGV2aWNlL2RldmljZXMuY3N2IjsNCiAgICAgICAgJGZpbGVfaGFuZGxlID0gZm9wZW4oJGNzdmZpbGUsICJyIik7DQogICAgICAgICRsaW5lX29mX3RleHQgPSBhcnJheSggICk7DQogICAgICAgIHdoaWxlKCAhZmVvZigkZmlsZV9oYW5kbGUpICkgDQogICAgICAgIHsNCiAgICAgICAgICAgICRsaW5lX29mX3RleHRbXSA9IGZnZXRjc3YoJGZpbGVfaGFuZGxlLCAxMDI0KTsNCiAgICAgICAgfQ0KICAgICAgICAkZGV2aWNlRGF0YSA9IGV4cGxvZGUoIjsiLCAkbGluZV9vZl90ZXh0W210X3JhbmQoMCwgMTE4NjcpXVswXSk7DQogICAgICAgIGZjbG9zZSgkZmlsZV9oYW5kbGUpOw0KICAgICAgICByZXR1cm4gJGRldmljZURhdGE7DQogICAgfQ0KDQogICAgcHVibGljIGZ1bmN0aW9uIGJ1aWxkVXNlckFnZW50KCkNCiAgICB7DQogICAgICAgICRkZXZpY2VEYXRhID0gJHRoaXMtPmdldERldmljZURhdGEoKTsNCiAgICAgICAgJHRoaXMtPnBhcmVudC0+c2V0dGluZ3MtPnNldCgibWFudWZhY3R1cmVyIiwgJGRldmljZURhdGFbMF0pOw0KICAgICAgICAkdGhpcy0+cGFyZW50LT5zZXR0aW5ncy0+c2V0KCJkZXZpY2UiLCAkZGV2aWNlRGF0YVsxXSk7DQogICAgICAgICR0aGlzLT5wYXJlbnQtPnNldHRpbmdzLT5zZXQoIm1vZGVsIiwgJGRldmljZURhdGFbMl0pOw0KICAgICAgICAkbG9jYWxlTGFuZyA9ICR0aGlzLT5kZXRlY3RVc2VyTG9jYWxlKCk7DQogICAgICAgIHJldHVybiBzcHJpbnRmKCJJbnN0YWdyYW0gJXMgQW5kcm9pZCAoMTgvNC4zOyAzMjBkcGk7IDcyMHgxMjgwOyAlczsgJXM7ICVzOyBxY29tOyAiIC4gJGxvY2FsZUxhbmcgLiAiKSIsIENvbnN0YW50czo6VkVSU0lPTiwgJGRldmljZURhdGFbMF0sICRkZXZpY2VEYXRhWzFdLCAkZGV2aWNlRGF0YVsyXSk7DQogICAgfQ0KDQogICAgcHJpdmF0ZSBmdW5jdGlvbiBkZXRlY3RVc2VyTG9jYWxlKCRmYWxsYmFja0xvY2FsZSA9ICJlbl9VUyIpDQogICAgew0KICAgICAgICAkdXNlckxvY2FsZSA9IE5VTEw7DQogICAgICAgIGlmKCBpc3NldCgkX1NFUlZFUlsiSFRUUF9BQ0NFUFRfTEFOR1VBR0UiXSkgKSANCiAgICAgICAgew0KICAgICAgICAgICAgJGFyckxhbmcgPSBleHBsb2RlKCIsIiwgJF9TRVJWRVJbIkhUVFBfQUNDRVBUX0xBTkdVQUdFIl0pOw0KICAgICAgICAgICAgJHByZWZlcnJlZExhbmdzID0gYXJyYXkoICApOw0KICAgICAgICAgICAgZm9yZWFjaCggJGFyckxhbmcgYXMgJHN0ckxhbmcgKSANCiAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAkYXJyTGFuZ0RldGFpbHMgPSBleHBsb2RlKCI7cT0iLCAkc3RyTGFuZyk7DQogICAgICAgICAgICAgICAgJGxhbmdDb2RlID0gc3RyX3JlcGxhY2UoIi0iLCAiXyIsICRhcnJMYW5nRGV0YWlsc1swXSk7DQogICAgICAgICAgICAgICAgJGxhbmdQcmlvcml0eSA9IGZsb2F0dmFsKChpc3NldCgkYXJyTGFuZ0RldGFpbHNbMV0pID8gJGFyckxhbmdEZXRhaWxzWzFdIDogMSkpOw0KICAgICAgICAgICAgICAgICRwcmVmZXJyZWRMYW5nc1skbGFuZ0NvZGVdID0gJGxhbmdQcmlvcml0eTsNCiAgICAgICAgICAgIH0NCiAgICAgICAgICAgIGFyc29ydCgkcHJlZmVycmVkTGFuZ3MpOw0KICAgICAgICAgICAgcmVzZXQoJHByZWZlcnJlZExhbmdzKTsNCiAgICAgICAgICAgICR1c2VyTG9jYWxlID0ga2V5KCRwcmVmZXJyZWRMYW5ncyk7DQogICAgICAgIH0NCg0KICAgICAgICByZXR1cm4gKGlzX251bGwoJHVzZXJMb2NhbGUpID8gJGZhbGxiYWNrTG9jYWxlIDogJHVzZXJMb2NhbGUpOw0KICAgIH0NCg0KfQ0KDQoNCmNsYXNzIFV0aWxzDQp7DQogICAgLyoqDQogICAgICAgICAqIExlbmd0aCBvZiB0aGUgZmlsZSBpbiBTZWNvbmRzLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICRmaWxlDQogICAgICAgICAqICAgICAgICAgICAgICAgICAgICAgcGF0aCB0byB0aGUgZmlsZSBuYW1lDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gaW50DQogICAgICAgICAqICAgICAgICAgICAgIGxlbmd0aCBvZiB0aGUgZmlsZSBpbiBzZWNvbmRzDQogICAgICAgICAqLw0KDQogICAgcHVibGljIHN0YXRpYyBmdW5jdGlvbiBnZXRTZWNvbmRzKCRmaWxlKQ0KICAgIHsNCiAgICAgICAgJGZmbXBlZyA9IHNlbGY6OmNoZWNrRkZNUEVHKCk7DQogICAgICAgIGlmKCAkZmZtcGVnICkgDQogICAgICAgIHsNCiAgICAgICAgICAgICR0aW1lID0gZXhlYygoc3RyaW5nKSAkZmZtcGVnIC4gIiAtaSAiIC4gJGZpbGUgLiAiIDI+JjEgfCBncmVwICdEdXJhdGlvbicgfCBjdXQgLWQgJyAnIC1mIDQiKTsNCiAgICAgICAgICAgICRkdXJhdGlvbiA9IGV4cGxvZGUoIjoiLCAkdGltZSk7DQogICAgICAgICAgICAkc2Vjb25kcyA9ICRkdXJhdGlvblswXSAqIDM2MDAgKyAkZHVyYXRpb25bMV0gKiA2MCArIHJvdW5kKCRkdXJhdGlvblsyXSk7DQogICAgICAgICAgICByZXR1cm4gJHNlY29uZHM7DQogICAgICAgIH0NCg0KICAgICAgICByZXR1cm4gbXRfcmFuZCgxNSwgMzAwKTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogQ2hlY2sgZm9yIGZmbXBlZy9hdmNvbnYgZGVwZW5kZW5jaWVzLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIHN0cmluZy9ib29sZWFuDQogICAgICAgICAqICAgICAgICAgICAgICAgICAgICAgICAgbmFtZSBvZiB0aGUgbGlicmFyeSBpZiBwcmVzZW50LCBmYWxzZSBvdGhlcndpc2UNCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgc3RhdGljIGZ1bmN0aW9uIGNoZWNrRkZNUEVHKCkNCiAgICB7DQogICAgICAgIEBleGVjKCJmZm1wZWcgLXZlcnNpb24gMj4mMSIsICRvdXRwdXQsICRyZXR1cm52YWx1ZSk7DQogICAgICAgIGlmKCAkcmV0dXJudmFsdWUgPT09IDAgKSANCiAgICAgICAgew0KICAgICAgICAgICAgcmV0dXJuICJmZm1wZWciOw0KICAgICAgICB9DQoNCiAgICAgICAgQGV4ZWMoImF2Y29udiAtdmVyc2lvbiAyPiYxIiwgJG91dHB1dCwgJHJldHVybnZhbHVlKTsNCiAgICAgICAgaWYoICRyZXR1cm52YWx1ZSA9PT0gMCApIA0KICAgICAgICB7DQogICAgICAgICAgICByZXR1cm4gImF2Y29udiI7DQogICAgICAgIH0NCg0KICAgICAgICByZXR1cm4gZmFsc2U7DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIENyZWF0aW5nIGEgdmlkZW8gaWNvbi90aHVtYm5haWwuDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJGZpbGUNCiAgICAgICAgICogICAgICAgICAgICAgICAgICAgICBwYXRoIHRvIHRoZSB2aWRlbyBmaWxlDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gaW1hZ2UNCiAgICAgICAgICogICAgICAgICAgICAgICBpY29uL3RodW1ibmFpbCBmb3IgdGhlIHZpZGVvDQogICAgICAgICAqLw0KDQogICAgcHVibGljIHN0YXRpYyBmdW5jdGlvbiBjcmVhdGVWaWRlb0ljb24oJGZpbGUpDQogICAgew0KICAgICAgICAkZmZtcGVnID0gc2VsZjo6Y2hlY2tGRk1QRUcoKTsNCiAgICAgICAgaWYoICRmZm1wZWcgKSANCiAgICAgICAgew0KICAgICAgICAgICAgJHByZXZpZXcgPSBzeXNfZ2V0X3RlbXBfZGlyKCkgLiAiLyIgLiBtZDUoJGZpbGUpIC4gIi5qcGciOw0KICAgICAgICAgICAgQHVubGluaygkcHJldmlldyk7DQogICAgICAgICAgICAkY29tbWFuZCA9ICRmZm1wZWcgLiAiIC1pIFwiIiAuICRmaWxlIC4gIlwiIC1mIG1qcGVnIC1zcyAwMDowMDowMSAtdmZyYW1lcyAxIFwiIiAuICRwcmV2aWV3IC4gIlwiIDI+JjEiOw0KICAgICAgICAgICAgQGV4ZWMoJGNvbW1hbmQpOw0KICAgICAgICAgICAgcmV0dXJuIGZpbGVfZ2V0X2NvbnRlbnRzKCRwcmV2aWV3KTsNCiAgICAgICAgfQ0KDQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIEltcGxlbWVudHMgdGhlIGFjdHVhbCBsb2dpYyBiZWhpbmQgY3JlYXRpbmcgdGhlIGljb24vdGh1bWJuYWlsLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICRmaWxlDQogICAgICAgICAqICAgICAgICAgICAgICAgICAgICAgcGF0aCB0byB0aGUgZmlsZSBuYW1lDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gaW1hZ2UNCiAgICAgICAgICogICAgICAgICAgICAgICBpY29uL3RodW1ibmFpbCBmb3IgdGhlIHZpZGVvDQogICAgICAgICAqLw0KDQogICAgcHVibGljIHN0YXRpYyBmdW5jdGlvbiBjcmVhdGVJY29uR0QoJGZpbGUsICRzaXplID0gMTAwLCAkcmF3ID0gdHJ1ZSkNCiAgICB7DQogICAgICAgIGxpc3QoJHdpZHRoLCAkaGVpZ2h0KSA9IGdldGltYWdlc2l6ZSgkZmlsZSk7DQogICAgICAgIGlmKCAkaGVpZ2h0IDwgJHdpZHRoICkgDQogICAgICAgIHsNCiAgICAgICAgICAgICR5ID0gMDsNCiAgICAgICAgICAgICR4ID0gKCR3aWR0aCAtICRoZWlnaHQpIC8gMjsNCiAgICAgICAgICAgICRzbWFsbGVzdFNpZGUgPSAkaGVpZ2h0Ow0KICAgICAgICB9DQogICAgICAgIGVsc2UNCiAgICAgICAgew0KICAgICAgICAgICAgJHggPSAwOw0KICAgICAgICAgICAgJHkgPSAoJGhlaWdodCAtICR3aWR0aCkgLyAyOw0KICAgICAgICAgICAgJHNtYWxsZXN0U2lkZSA9ICR3aWR0aDsNCiAgICAgICAgfQ0KDQogICAgICAgICRpbWFnZV9wID0gaW1hZ2VjcmVhdGV0cnVlY29sb3IoJHNpemUsICRzaXplKTsNCiAgICAgICAgJGltYWdlID0gaW1hZ2VjcmVhdGVmcm9tc3RyaW5nKGZpbGVfZ2V0X2NvbnRlbnRzKCRmaWxlKSk7DQogICAgICAgIGltYWdlY29weXJlc2FtcGxlZCgkaW1hZ2VfcCwgJGltYWdlLCAwLCAwLCAkeCwgJHksICRzaXplLCAkc2l6ZSwgJHNtYWxsZXN0U2lkZSwgJHNtYWxsZXN0U2lkZSk7DQogICAgICAgIG9iX3N0YXJ0KCk7DQogICAgICAgIGltYWdlanBlZygkaW1hZ2VfcCwgTlVMTCwgOTUpOw0KICAgICAgICAkaSA9IG9iX2dldF9jb250ZW50cygpOw0KICAgICAgICBvYl9lbmRfY2xlYW4oKTsNCiAgICAgICAgaW1hZ2VkZXN0cm95KCRpbWFnZSk7DQogICAgICAgIGltYWdlZGVzdHJveSgkaW1hZ2VfcCk7DQogICAgICAgIHJldHVybiAkaTsNCiAgICB9DQoNCiAgICBwdWJsaWMgc3RhdGljIGZ1bmN0aW9uIGZvcm1hdEJ5dGVzKCRieXRlcywgJHByZWNpc2lvbiA9IDIpDQogICAgew0KICAgICAgICAkdW5pdHMgPSBhcnJheSggIkIiLCAia0IiLCAibUIiLCAiZ0IiLCAidEIiICk7DQogICAgICAgICRieXRlcyA9IG1heCgkYnl0ZXMsIDApOw0KICAgICAgICAkcG93ID0gZmxvb3IoKCgkYnl0ZXMgPyBsb2coJGJ5dGVzKSA6IDApKSAvIGxvZygxMDI0KSk7DQogICAgICAgICRwb3cgPSBtaW4oJHBvdywgY291bnQoJHVuaXRzKSAtIDEpOw0KICAgICAgICAkYnl0ZXMgLz0gcG93KDEwMjQsICRwb3cpOw0KICAgICAgICByZXR1cm4gcm91bmQoJGJ5dGVzLCAkcHJlY2lzaW9uKSAuICIiIC4gJHVuaXRzWyRwb3ddOw0KICAgIH0NCg0KICAgIHB1YmxpYyBzdGF0aWMgZnVuY3Rpb24gY29sb3VyZWRTdHJpbmcoJHN0cmluZywgJGNvbG91cikNCiAgICB7DQogICAgICAgICRjb2xvdXJzWyJibGFjayJdID0gIjA7MzAiOw0KICAgICAgICAkY29sb3Vyc1siZGFya19ncmF5Il0gPSAiMTszMCI7DQogICAgICAgICRjb2xvdXJzWyJibHVlIl0gPSAiMDszNCI7DQogICAgICAgICRjb2xvdXJzWyJsaWdodF9ibHVlIl0gPSAiMTszNCI7DQogICAgICAgICRjb2xvdXJzWyJncmVlbiJdID0gIjA7MzIiOw0KICAgICAgICAkY29sb3Vyc1sibGlnaHRfZ3JlZW4iXSA9ICIxOzMyIjsNCiAgICAgICAgJGNvbG91cnNbImN5YW4iXSA9ICIwOzM2IjsNCiAgICAgICAgJGNvbG91cnNbImxpZ2h0X2N5YW4iXSA9ICIxOzM2IjsNCiAgICAgICAgJGNvbG91cnNbInJlZCJdID0gIjA7MzEiOw0KICAgICAgICAkY29sb3Vyc1sibGlnaHRfcmVkIl0gPSAiMTszMSI7DQogICAgICAgICRjb2xvdXJzWyJwdXJwbGUiXSA9ICIwOzM1IjsNCiAgICAgICAgJGNvbG91cnNbImxpZ2h0X3B1cnBsZSJdID0gIjE7MzUiOw0KICAgICAgICAkY29sb3Vyc1siYnJvd24iXSA9ICIwOzMzIjsNCiAgICAgICAgJGNvbG91cnNbInllbGxvdyJdID0gIjE7MzMiOw0KICAgICAgICAkY29sb3Vyc1sibGlnaHRfZ3JheSJdID0gIjA7MzciOw0KICAgICAgICAkY29sb3Vyc1sid2hpdGUiXSA9ICIxOzM3IjsNCiAgICAgICAgJGNvbG9yZWRfc3RyaW5nID0gIiI7DQogICAgICAgIGlmKCBpc3NldCgkY29sb3Vyc1skY29sb3VyXSkgKSANCiAgICAgICAgew0KICAgICAgICAgICAgJGNvbG9yZWRfc3RyaW5nIC49ICJceDFCWyIgLiAkY29sb3Vyc1skY29sb3VyXSAuICJtIjsNCiAgICAgICAgfQ0KDQogICAgICAgICRjb2xvcmVkX3N0cmluZyAuPSAkc3RyaW5nIC4gIlx4MUJbMG0iOw0KICAgICAgICByZXR1cm4gJGNvbG9yZWRfc3RyaW5nOw0KICAgIH0NCg0KICAgIHB1YmxpYyBzdGF0aWMgZnVuY3Rpb24gZ2V0RmlsdGVyQ29kZSgkZmlsdGVyKQ0KICAgIHsNCiAgICAgICAgJGZpbHRlcnMgPSBhcnJheSggICk7DQogICAgICAgICRmaWx0ZXJzWzEwOF0gPSAiQ2hhcm1lcyI7DQogICAgICAgICRmaWx0ZXJzWzExNl0gPSAiQXNoYnkiOw0KICAgICAgICAkZmlsdGVyc1sxMTddID0gIkhlbGVuYSI7DQogICAgICAgICRmaWx0ZXJzWzExNV0gPSAiQnJvb2tseW4iOw0KICAgICAgICAkZmlsdGVyc1sxMDVdID0gIkRvZ3BhdGNoIjsNCiAgICAgICAgJGZpbHRlcnNbMTEzXSA9ICJTa3lsaW5lIjsNCiAgICAgICAgJGZpbHRlcnNbMTA3XSA9ICJHaW56YSI7DQogICAgICAgICRmaWx0ZXJzWzExOF0gPSAiTWF2ZW4iOw0KICAgICAgICAkZmlsdGVyc1sxNl0gPSAiS2VsdmluIjsNCiAgICAgICAgJGZpbHRlcnNbMTRdID0gIjE5NzciOw0KICAgICAgICAkZmlsdGVyc1syMF0gPSAiV2FsZGVuIjsNCiAgICAgICAgJGZpbHRlcnNbMTldID0gIlRvYXN0ZXIiOw0KICAgICAgICAkZmlsdGVyc1sxOF0gPSAiU3V0cm8iOw0KICAgICAgICAkZmlsdGVyc1syMl0gPSAiQnJhbm5hbiI7DQogICAgICAgICRmaWx0ZXJzWzNdID0gIkVhcmx5YmlyZCI7DQogICAgICAgICRmaWx0ZXJzWzEwNl0gPSAiVmVzcGVyIjsNCiAgICAgICAgJGZpbHRlcnNbMTA5XSA9ICJTdGluc29uIjsNCiAgICAgICAgJGZpbHRlcnNbMTVdID0gIk5hc2h2aWxsZSI7DQogICAgICAgICRmaWx0ZXJzWzIxXSA9ICJIZWZlIjsNCiAgICAgICAgJGZpbHRlcnNbMTBdID0gIklua3dlbGwiOw0KICAgICAgICAkZmlsdGVyc1syXSA9ICJMby1GaSI7DQogICAgICAgICRmaWx0ZXJzWzI4XSA9ICJXaWxsb3ciOw0KICAgICAgICAkZmlsdGVyc1syN10gPSAiU2llcnJhIjsNCiAgICAgICAgJGZpbHRlcnNbMV0gPSAiWCBQcm8gSUkiOw0KICAgICAgICAkZmlsdGVyc1syNV0gPSAiVmFsZW5jaWEiOw0KICAgICAgICAkZmlsdGVyc1syNl0gPSAiSHVkc29uIjsNCiAgICAgICAgJGZpbHRlcnNbMjNdID0gIlJpc2UiOw0KICAgICAgICAkZmlsdGVyc1sxN10gPSAiTWF5ZmFpciI7DQogICAgICAgICRmaWx0ZXJzWzI0XSA9ICJBbWFybyI7DQogICAgICAgICRmaWx0ZXJzWzYwOF0gPSAiUGVycGV0dWEiOw0KICAgICAgICAkZmlsdGVyc1s2MTJdID0gIkFkZW4iOw0KICAgICAgICAkZmlsdGVyc1s2MDNdID0gIkx1ZHdpZyI7DQogICAgICAgICRmaWx0ZXJzWzYxNl0gPSAiQ3JlbWEiOw0KICAgICAgICAkZmlsdGVyc1s2MDVdID0gIlNsdW1iZXIiOw0KICAgICAgICAkZmlsdGVyc1s2MTNdID0gIkp1bm8iOw0KICAgICAgICAkZmlsdGVyc1s2MTRdID0gIlJleWVzIjsNCiAgICAgICAgJGZpbHRlcnNbNjE1XSA9ICJMYXJrIjsNCiAgICAgICAgJGZpbHRlcnNbMTExXSA9ICJNb29uIjsNCiAgICAgICAgJGZpbHRlcnNbMTE0XSA9ICJHaW5naGFtIjsNCiAgICAgICAgJGZpbHRlcnNbMTEyXSA9ICJDbGFyZW5kb24iOw0KICAgICAgICAkZmlsdGVyc1swXSA9ICJOb3JtYWwiOw0KICAgICAgICByZXR1cm4gYXJyYXlfc2VhcmNoKCRmaWx0ZXIsICRmaWx0ZXJzKTsNCiAgICB9DQoNCiAgICBwdWJsaWMgc3RhdGljIGZ1bmN0aW9uIGNvb2tpZVRvQXJyYXkoJHN0cmluZywgJGRvbWFpbikNCiAgICB7DQogICAgICAgICRhcnJDb29raWVzID0gYXJyYXkoICApOw0KICAgICAgICAkZmlsZVZhbHMgPSBzZWxmOjpleHRyYWN0Q29va2llcygkc3RyaW5nKTsNCiAgICAgICAgZm9yZWFjaCggJGZpbGVWYWxzIGFzICRjb29raWUgKSANCiAgICAgICAgew0KICAgICAgICAgICAgaWYoICRjb29raWVbImRvbWFpbiJdID09ICRkb21haW4gKSANCiAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAkYXJyQ29va2llc1skY29va2llWyJuYW1lIl1dID0gJGNvb2tpZVsidmFsdWUiXTsNCiAgICAgICAgICAgIH0NCg0KICAgICAgICB9DQogICAgICAgIHJldHVybiAkYXJyQ29va2llczsNCiAgICB9DQoNCiAgICBwdWJsaWMgc3RhdGljIGZ1bmN0aW9uIGV4dHJhY3RDb29raWVzKCRzdHJpbmcpDQogICAgew0KICAgICAgICAkbGluZXMgPSBleHBsb2RlKFBIUF9FT0wsICRzdHJpbmcpOw0KICAgICAgICAkY29va2llcyA9IGFycmF5KCAgKTsNCiAgICAgICAgZm9yZWFjaCggJGxpbmVzIGFzICRsaW5lICkgDQogICAgICAgIHsNCiAgICAgICAgICAgICRjb29raWUgPSBhcnJheSggICk7DQogICAgICAgICAgICBpZiggc3Vic3RyKCRsaW5lLCAwLCAxMCkgPT0gIiNIdHRwT25seV8iICkgDQogICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgJGxpbmUgPSBzdWJzdHIoJGxpbmUsIDEwKTsNCiAgICAgICAgICAgICAgICAkY29va2llWyJodHRwb25seSJdID0gdHJ1ZTsNCiAgICAgICAgICAgIH0NCiAgICAgICAgICAgIGVsc2UNCiAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAkY29va2llWyJodHRwb25seSJdID0gZmFsc2U7DQogICAgICAgICAgICB9DQoNCiAgICAgICAgICAgIGlmKCBzdWJzdHIoJGxpbmUsIDAsIDEpICE9ICIjIiAmJiBzdWJzdHJfY291bnQoJGxpbmUsICJcdCIpID09IDYgKSANCiAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAkdG9rZW5zID0gZXhwbG9kZSgiXHQiLCAkbGluZSk7DQogICAgICAgICAgICAgICAgJHRva2VucyA9IGFycmF5X21hcCgidHJpbSIsICR0b2tlbnMpOw0KICAgICAgICAgICAgICAgIGxpc3QoJGNvb2tpZVsiZG9tYWluIl0sICRjb29raWVbImZsYWciXSwgJGNvb2tpZVsicGF0aCJdLCAkY29va2llWyJzZWN1cmUiXSwgJGNvb2tpZVsiZXhwaXJhdGlvbi1lcG9jaCJdKSA9ICR0b2tlbnM7DQogICAgICAgICAgICAgICAgJGNvb2tpZVsibmFtZSJdID0gdXJsZGVjb2RlKCR0b2tlbnNbNV0pOw0KICAgICAgICAgICAgICAgICRjb29raWVbInZhbHVlIl0gPSB1cmxkZWNvZGUoJHRva2Vuc1s2XSk7DQogICAgICAgICAgICAgICAgJGNvb2tpZVsiZXhwaXJhdGlvbiJdID0gZGF0ZSgiWS1tLWQgaDppOnMiLCAkdG9rZW5zWzRdKTsNCiAgICAgICAgICAgICAgICAkY29va2llc1tdID0gJGNvb2tpZTsNCiAgICAgICAgICAgIH0NCg0KICAgICAgICB9DQogICAgICAgIHJldHVybiAkY29va2llczsNCiAgICB9DQoNCn0NCg0KDQpjbGFzcyBTZXR0aW5ncw0Kew0KICAgIHByaXZhdGUgJHBhdGggPSBOVUxMOw0KICAgIHByaXZhdGUgJHNldHMgPSBOVUxMOw0KDQogICAgcHVibGljIGZ1bmN0aW9uIF9fY29uc3RydWN0KCRwYXRoKQ0KICAgIHsNCiAgICAgICAgJHRoaXMtPnBhdGggPSAkcGF0aDsNCiAgICAgICAgJHRoaXMtPnNldHMgPSBhcnJheSggICk7DQogICAgICAgIGlmKCBmaWxlX2V4aXN0cygkcGF0aCkgKSANCiAgICAgICAgew0KICAgICAgICAgICAgJGZwID0gZm9wZW4oJHBhdGgsICJyYiIpOw0KICAgICAgICAgICAgd2hpbGUoICRsaW5lID0gZmdldHMoJGZwLCAyMDQ4KSApIA0KICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICRsaW5lID0gdHJpbSgkbGluZSwgIiAiKTsNCiAgICAgICAgICAgICAgICBpZiggJGxpbmVbMF0gPT0gIiMiICkgDQogICAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICAgICBjb250aW51ZTsNCiAgICAgICAgICAgICAgICB9DQoNCiAgICAgICAgICAgICAgICAka3YgPSBleHBsb2RlKCI9IiwgJGxpbmUsIDIpOw0KICAgICAgICAgICAgICAgICR0aGlzLT5zZXRzWyRrdlswXV0gPSB0cmltKCRrdlsxXSwgIlxyXG4gIik7DQogICAgICAgICAgICB9DQogICAgICAgICAgICBmY2xvc2UoJGZwKTsNCiAgICAgICAgfQ0KDQogICAgfQ0KDQogICAgcHVibGljIGZ1bmN0aW9uIGdldCgka2V5LCAkZGVmYXVsdCA9IE5VTEwpDQogICAgew0KICAgICAgICBpZiggJGtleSA9PSAic2V0cyIgKSANCiAgICAgICAgew0KICAgICAgICAgICAgcmV0dXJuICR0aGlzLT5zZXRzOw0KICAgICAgICB9DQoNCiAgICAgICAgaWYoIGlzc2V0KCR0aGlzLT5zZXRzWyRrZXldKSApIA0KICAgICAgICB7DQogICAgICAgICAgICByZXR1cm4gJHRoaXMtPnNldHNbJGtleV07DQogICAgICAgIH0NCg0KICAgICAgICByZXR1cm4gJGRlZmF1bHQ7DQogICAgfQ0KDQogICAgcHVibGljIGZ1bmN0aW9uIHNldCgka2V5LCAkdmFsdWUpDQogICAgew0KICAgICAgICBpZiggJGtleSA9PSAic2V0cyIgKSANCiAgICAgICAgew0KICAgICAgICAgICAgcmV0dXJuIE5VTEw7DQogICAgICAgIH0NCg0KICAgICAgICAkdGhpcy0+c2V0c1ska2V5XSA9ICR2YWx1ZTsNCiAgICAgICAgJHRoaXMtPlNhdmUoKTsNCiAgICB9DQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gU2F2ZSgpDQogICAgew0KICAgICAgICBpZiggZmlsZV9leGlzdHMoJHRoaXMtPnBhdGgpICkgDQogICAgICAgIHsNCiAgICAgICAgICAgIHVubGluaygkdGhpcy0+cGF0aCk7DQogICAgICAgIH0NCg0KICAgICAgICAkZnAgPSBmb3BlbigkdGhpcy0+cGF0aCwgIndiIik7DQogICAgICAgIGZzZWVrKCRmcCwgMCk7DQogICAgICAgIGZvcmVhY2goICR0aGlzLT5zZXRzIGFzICRrZXkgPT4gJHZhbHVlICkgDQogICAgICAgIHsNCiAgICAgICAgICAgIGZ3cml0ZSgkZnAsICRrZXkgLiAiPSIgLiAkdmFsdWUgLiAiXG4iKTsNCiAgICAgICAgfQ0KICAgICAgICBmY2xvc2UoJGZwKTsNCiAgICB9DQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gX19zZXQoJHByb3AsICR2YWx1ZSkNCiAgICB7DQogICAgICAgICR0aGlzLT5zZXQoJHByb3AsICR2YWx1ZSk7DQogICAgfQ0KDQogICAgcHVibGljIGZ1bmN0aW9uIF9fZ2V0KCRwcm9wKQ0KICAgIHsNCiAgICAgICAgcmV0dXJuICR0aGlzLT5nZXQoJHByb3ApOw0KICAgIH0NCg0KfQ0KDQoNCmNsYXNzIENvbnN0YW50cw0Kew0KICAgIGNvbnN0IEFQSV9VUkwgPSAiaHR0cHM6Ly9pLmluc3RhZ3JhbS5jb20vYXBpL3YxLyI7DQogICAgY29uc3QgVkVSU0lPTiA9ICIxMC4zLjIiOw0KICAgIGNvbnN0IElHX1NJR19LRVkgPSAiNWFkN2Q2ZjAxMzY2NmNjOTNjODhmYzhhZjk0MDM0OGJkMDY3YjY4ZjBkY2UzYzg1MTIyYTkyM2Y0Zjc0YjI1MSI7DQogICAgY29uc3QgRVhQRVJJTUVOVFMgPSAiaWdfYW5kcm9pZF9hZF9ob2xkb3V0XzE2bTVfdW5pdmVyc2UsaWdfYW5kcm9pZF9wcm9ncmVzc2l2ZV9qcGVnLGlnX2NyZWF0aW9uX2dyb3d0aF9ob2xkb3V0LGlnX2FuZHJvaWRfb3Bwb19hcHBfYmFkZ2luZyxpZ19hbmRyb2lkX2FkX3JlbW92ZV91c2VybmFtZV9mcm9tX2NhcHRpb25fdW5pdmVyc2UsaWdfYW5kcm9pZF9lbmFibGVfc2hhcmVfdG9fd2hhdHNhcHAsaWdfYW5kcm9pZF9kaXJlY3RfZHJhd2luZ19pbl9xdWlja19jYW1fdW5pdmVyc2UsaWdfYW5kcm9pZF9hZF9hbHdheXNfc2VuZF9hZF9hdHRyaWJ1dGlvbl9pZF91bml2ZXJzZSxpZ19hbmRyb2lkX3VuaXZlcnNlX3ZpZGVvX3Byb2R1Y3Rpb24saWdfYW5kcm9pZF9kaXJlY3RfcGx1c19idXR0b24saWdfYW5kcm9pZF9hZHNfaGVhdG1hcF9vdmVybGF5X3VuaXZlcnNlLGlnX2FuZHJvaWRfaHR0cF9zdGFja19leHBlcmltZW50XzIwMTYsaWdfYW5kcm9pZF9pbmZpbml0ZV9zY3JvbGxpbmcsaWdfZmJuc19ibG9ja2VkLGlnX2FuZHJvaWRfcG9zdF9hdXRvX3JldHJ5X3Y3XzIxLGlnX2ZibnNfcHVzaCxpZ19hbmRyb2lkX3ZpZGVvX3BsYXliYWNrX2JhbmR3aWR0aF90aHJlc2hvbGQsaWdfYW5kcm9pZF9kaXJlY3RfbGlua19wcmV2aWV3LGlnX2FuZHJvaWRfZGlyZWN0X3R5cGluZ19pbmRpY2F0b3IsaWdfYW5kcm9pZF9wcmV2aWV3X2NhcHR1cmUsaWdfYW5kcm9pZF9mZWVkX3BpbGwsaWdfYW5kcm9pZF9wcm9maWxlX2xpbmtfaWFiLGlnX2FuZHJvaWRfc3RvcnlfY2FwdGlvbixpZ19hbmRyb2lkX25ldHdvcmtfY2FuY2VsbGF0aW9uLGlnX2FuZHJvaWRfaGlzdG9ncmFtX3JlcG9ydGVyLGlnX2FuZHJvaWRfYW5yd2F0Y2hkb2csaWdfYW5kcm9pZF9zZWFyY2hfY2xpZW50X21hdGNoaW5nLGlnX2FuZHJvaWRfZm9sbG93X3JlcXVlc3RfdGV4dF9idXR0b25zLGlnX2FuZHJvaWRfZmVlZF96b29tLGlnX2FuZHJvaWRfZHJhZnRzX3VuaXZlcnNlLGlnX2FuZHJvaWRfZGlzYWJsZV9jb21tZW50LGlnX2FuZHJvaWRfdXNlcl9kZXRhaWxfZW5kcG9pbnQsaWdfYW5kcm9pZF9vc192ZXJzaW9uX2Jsb2NraW5nLGlnX2FuZHJvaWRfYmxvY2tlZF9saXN0LGlnX2FuZHJvaWRfZXZlbnRfY3JlYXRpb24saWdfYW5kcm9pZF9oaWdoX3Jlc191cGxvYWRfMixpZ19hbmRyb2lkXzJmYWMsaWdfYW5kcm9pZF9tYXJrX3JlZWxfc2Vlbl9vbl9Td2lwZV9mb3J3YXJkLGlnX2FuZHJvaWRfY29tbWVudF9yZWRlc2lnbixpZ19hbmRyb2lkX2FkX3Nwb25zb3JlZF9sYWJlbF91bml2ZXJzZSxpZ19hbmRyb2lkX21lbnRpb25zX2Rpc21pc3NfcnVsZSxpZ19hbmRyb2lkX2Rpc2FibGVfY2hyb21hX3N1YnNhbXBsaW5nLGlnX2FuZHJvaWRfc2hhcmVfc3Bpbm5lcixpZ19hbmRyb2lkX3ZpZGVvX3JldXNlX3N1cmZhY2UsaWdfZXhwbG9yZV92M19hbmRyb2lkX3VuaXZlcnNlLGlnX2FuZHJvaWRfbWVkaWFfZmF2b3JpdGVzLGlnX2FuZHJvaWRfbnV4X2hvbGRvdXQsaWdfYW5kcm9pZF9pbnN0YV92aWRlb191bml2ZXJzZSxpZ19hbmRyb2lkX3NlYXJjaF9udWxsX3N0YXRlLGlnX2FuZHJvaWRfdW5pdmVyc2VfcmVlbF92aWRlb19wcm9kdWN0aW9uLGxpZ2VyX2luc3RhZ3JhbV9hbmRyb2lkX3VuaXYsaWdfYW5kcm9pZF9kaXJlY3RfZW1vamlfcGlja2VyLGlnX2ZlZWRfaG9sZG91dF91bml2ZXJzZSxpZ19hbmRyb2lkX2RpcmVjdF9zZW5kX2F1dG9fcmV0cnlfdW5pdmVyc2UsaWdfYW5kcm9pZF9zYW1zdW5nX2FwcF9iYWRnaW5nLGlnX2FuZHJvaWRfZGlza191c2FnZSxpZ19hbmRyb2lkX2J1c2luZXNzX3Byb21vdGlvbixpZ19hbmRyb2lkX2RpcmVjdF9zd2lwZV90b19pbmJveCxpZ19hbmRyb2lkX2ZlZWRfcmVzaGFyZV9idXR0b25fbnV4LGlnX2FuZHJvaWRfcmVhY3RfbmF0aXZlX2Jvb3N0X3Bvc3QsaWdfYW5kcm9pZF9ib29tZXJhbmdfZmVlZF9hdHRyaWJ1dGlvbixpZ19mYm5zX3NoYXJlZCxpZ19mYm5zX2R1bXBfaWRzLGlnX2FuZHJvaWRfcmVhY3RfbmF0aXZlX3VuaXZlcnNlLGlnX3Nob3dfcHJvbW90ZV9idXR0b25faW5fZmVlZCxpZ19hbmRyb2lkX2FkX21ldGFkYXRhX2JlaGF2aW9yX3VuaXZlcnNlLGlnX2FuZHJvaWRfdmlkZW9fbG9vcGNvdW50X2ludCxpZ19hbmRyb2lkX2lubGluZV9nYWxsZXJ5X2JhY2tvZmZfaG91cnNfdW5pdmVyc2UsaWdfYW5kcm9pZF9yZW5kZXJpbmdfY29udHJvbHMsaWdfYW5kcm9pZF9wcm9maWxlX3Bob3RvX2FzX21lZGlhLGlnX2FuZHJvaWRfYXN5bmNfc3RhY2tfaW1hZ2VfY2FjaGUsaWdfdmlkZW9fbWF4X2R1cmF0aW9uX3FlX3ByZXVuaXZlcnNlLGlnX3ZpZGVvX2NvcHlyaWdodF93aGl0ZWxpc3QsaWdfYW5kcm9pZF9yZW5kZXJfc3Rvcmllc193aXRoX2NvbnRlbnRfb3ZlcnJpZGUsaWdfYW5kcm9pZF9hZF9pbnRlbnRfdG9faGlnaGxpZ2h0X3VuaXZlcnNlLGlnX2FuZHJvaWRfc3dpcGVfbmF2aWdhdGlvbl94X2FuZ2xlX3VuaXZlcnNlLGlnX2FuZHJvaWRfZGlzYWJsZV9jb21tZW50X3B1YmxpY190ZXN0LGlnX2FuZHJvaWRfcHJvZmlsZSxpZ19hbmRyb2lkX2RpcmVjdF9ibHVlX3RhYixpZ19hbmRyb2lkX2VuYWJsZV9zaGFyZV90b19tZXNzZW5nZXIsaWdfYW5kcm9pZF9mZXRjaF9yZWVsX3RyYXlfb25fcmVzdW1lX3VuaXZlcnNlLGlnX2FuZHJvaWRfcHJvbW90ZV9hZ2FpbixpZ19mZWVkX2V2ZW50X2xhbmRpbmdfcGFnZV9jaGFubmVsLGlnX3JhbmtpbmdfZm9sbG93aW5nLGlnX2FuZHJvaWRfcGVuZGluZ19yZXF1ZXN0X3NlYXJjaF9iYXIsaWdfYW5kcm9pZF9mZWVkX3VmaV9yZWRlc2lnbixpZ19hbmRyb2lkX3BlbmRpbmdfZWRpdHNfZGlhbG9nX3VuaXZlcnNlLGlnX2FuZHJvaWRfYnVzaW5lc3NfY29udmVyc2lvbl9mbG93X3VuaXZlcnNlLGlnX2FuZHJvaWRfc2hvd195b3VyX3N0b3J5X3doZW5fZW1wdHlfdW5pdmVyc2UsaWdfYW5kcm9pZF9hZF9kcm9wX2Nvb2tpZV9lYXJseSxpZ19hbmRyb2lkX2FwcF9zdGFydF9jb25maWcsaWdfYW5kcm9pZF9maXhfaXNlX3R3b19waGFzZSxpZ19hbmRyb2lkX3BwYWdlX3RvZ2dsZV91bml2ZXJzZSxpZ19hbmRyb2lkX3BiaWFfbm9ybWFsX3dlaWdodF91bml2ZXJzZSxpZ19hbmRyb2lkX3Byb2Zhbml0eV9maWx0ZXIsaWdfaW9zX3N1X2FjdGl2aXR5X2ZlZWQsaWdfYW5kcm9pZF9zZWFyY2gsaWdfYW5kcm9pZF9ib29tZXJhbmdfZW50cnksaWdfYW5kcm9pZF9tdXRlX3N0b3J5LGlnX2FuZHJvaWRfaW5saW5lX2dhbGxlcnlfdW5pdmVyc2UsaWdfYW5kcm9pZF9hZF9yZW1vdmVfb25lX3RhcF9pbmRpY2F0b3JfdW5pdmVyc2UsaWdfYW5kcm9pZF92aWV3X2NvdW50X2RlY291cGxlX2xpa2VzX3VuaXZlcnNlLGlnX2FuZHJvaWRfY29udGFjdF9idXR0b25fcmVkZXNpZ25fdjIsaWdfYW5kcm9pZF9wZXJpb2RpY19hbmFseXRpY3NfdXBsb2FkX3YyLGlnX2FuZHJvaWRfc2VuZF9kaXJlY3RfdHlwaW5nX2luZGljYXRvcixpZ19hbmRyb2lkX2FkX2hvbGRvdXRfMTZoMm0xX3VuaXZlcnNlLGlnX2FuZHJvaWRfcmVhY3RfbmF0aXZlX2NvbW1lbnRfbW9kZXJhdGlvbl9zZXR0aW5ncyxpZ192aWRlb191c2Vfc3ZlX3VuaXZlcnNlLGlnX2FuZHJvaWRfaW5saW5lX2dhbGxlcnlfbm9fYmFja29mZl9vbl9sYXVuY2hfdW5pdmVyc2UsaWdfYW5kcm9pZF9pbW1lcnNpdmVfdmlld2VyLGlnX2FuZHJvaWRfZGlzY292ZXJfcGVvcGxlX2ljb24saWdfYW5kcm9pZF9wcm9maWxlX2ZvbGxvd19iYWNrX2J1dHRvbixpc19hbmRyb2lkX2ZlZWRfc2Vlbl9zdGF0ZSxpZ19hbmRyb2lkX2RlbnNlX2ZlZWRfdW5pdF9jYXJkcyxpZ19hbmRyb2lkX2RyYWZ0c192aWRlb191bml2ZXJzZSxpZ19hbmRyb2lkX2V4b3BsYXllcixpZ19hbmRyb2lkX2FkZF90b19sYXN0X3Bvc3QsaWdfYW5kcm9pZF9hZF9yZW1vdmVfY3RhX2NoZXZyb25fdW5pdmVyc2UsaWdfYW5kcm9pZF9hZF9jb21tZW50X2N0YV91bml2ZXJzZSxpZ19hbmRyb2lkX3NlYXJjaF9ldmVudF9pY29uLGlnX2FuZHJvaWRfY2hhbm5lbHNfaG9tZSxpZ19hbmRyb2lkX2ZlZWQsaWdfYW5kcm9pZF9kdjJfcmVhbHRpbWVfcHJpdmF0ZV9zaGFyZSxpZ19hbmRyb2lkX25vbl9zcXVhcmVfZmlyc3QsaWdfYW5kcm9pZF92aWRlb19pbnRlcmxlYXZlZF92MixpZ19hbmRyb2lkX3ZpZGVvX2NhY2hlX3BvbGljeSxpZ19hbmRyb2lkX3JlYWN0X25hdGl2ZV91bml2ZXJzZV9raWxsX3N3aXRjaCxpZ19hbmRyb2lkX3ZpZGVvX2NhcHRpb25zX3VuaXZlcnNlLGlnX2FuZHJvaWRfZm9sbG93X3NlYXJjaF9iYXIsaWdfYW5kcm9pZF9sYXN0X2VkaXRzLGlnX2FuZHJvaWRfdHdvX3N0ZXBfY2FwdHVyZV9mbG93LGlnX2FuZHJvaWRfdmlkZW9fZG93bmxvYWRfbG9nZ2luZyxpZ19hbmRyb2lkX3NoYXJlX2xpbmtfdG9fd2hhdHNhcHAsaWdfYW5kcm9pZF9mYWNlYm9va190d2l0dGVyX3Byb2ZpbGVfcGhvdG9zLGlnX2FuZHJvaWRfc3dpcGVhYmxlX2ZpbHRlcnNfYmxhY2tsaXN0LGlnX2FuZHJvaWRfYWRfcGJpYV9wcm9maWxlX3RhcF91bml2ZXJzZSxpZ19hbmRyb2lkX3VzZV9zb2Z0d2FyZV9sYXllcl9mb3Jfa2NfZHJhd2luZ191bml2ZXJzZSxpZ19hbmRyb2lkX3JlYWN0X25hdGl2ZV9vdGEsaWdfYW5kcm9pZF9kaXJlY3RfbXV0dWFsbHlfZXhjbHVzaXZlX2V4cGVyaW1lbnRfdW5pdmVyc2UsaWdfYW5kcm9pZF9mb2xsb3dpbmdfZm9sbG93ZXJfc29jaWFsX2NvbnRleHQiOw0KICAgIGNvbnN0IExPR0lOX0VYUEVSSU1FTlRTID0gImlnX2FuZHJvaWRfcmVnX2xvZ2luX2J0bl9hY3RpdmVfc3RhdGUsaWdfYW5kcm9pZF9jaV9vcHRfaW5fYXRfcmVnLGlnX2FuZHJvaWRfb25lX2NsaWNrX2luX29sZF9mbG93LGlnX2FuZHJvaWRfbWVyZ2VfZmJfYW5kX2NpX2ZyaWVuZHNfcGFnZSxpZ19hbmRyb2lkX25vbl9mYl9zc28saWdfYW5kcm9pZF9tYW5kYXRvcnlfZnVsbF9uYW1lLGlnX2FuZHJvaWRfcmVnX2VuYWJsZV9sb2dpbl9wYXNzd29yZF9idG4saWdfYW5kcm9pZF9yZWdfcGhvbmVfZW1haWxfYWN0aXZlX3N0YXRlLGlnX2FuZHJvaWRfYW5hbHl0aWNzX2RhdGFfbG9zcyxpZ19mYm5zX2Jsb2NrZWQsaWdfYW5kcm9pZF9jb250YWN0X3BvaW50X3RyaWFnZSxpZ19hbmRyb2lkX3JlZ19uZXh0X2J0bl9hY3RpdmVfc3RhdGUsaWdfYW5kcm9pZF9wcmVmaWxsX3Bob25lX251bWJlcixpZ19hbmRyb2lkX3Nob3dfZmJfc29jaWFsX2NvbnRleHRfaW5fbnV4LGlnX2FuZHJvaWRfb25lX3RhcF9sb2dpbl91cHNlbGwsaWdfZmJuc19wdXNoLGlnX2FuZHJvaWRfcGhvbmVpZF9zeW5jX2ludGVydmFsIjsNCiAgICBjb25zdCBTSUdfS0VZX1ZFUlNJT04gPSAiNCI7DQogICAgY29uc3QgWF9JR19DYXBhYmlsaXRpZXMgPSAiM1RvQUFBPT0iOw0KICAgIGNvbnN0IEFORFJPSURfVkVSU0lPTiA9IDE4Ow0KICAgIGNvbnN0IEFORFJPSURfUkVMRUFTRSA9ICI0LjMiOw0KICAgIGNvbnN0IFdFQl9VUkwgPSAiaHR0cHM6Ly93d3cuaW5zdGFncmFtLmNvbS8iOw0KDQp9DQoNCg0KY2xhc3MgQnVsa1JlYWN0aW9uDQp7DQovKioNCiAgICAgICAgICogQHZhciBhcnJheSAkdXNlcnMNCiAgICAgICAgICovDQogICAgcHJvdGVjdGVkICR1c2VycyA9IGFycmF5KCAgKTsNCi8qKg0KICAgICAgICAgKiBAdmFyIGludCAkc2ltdWx0YW5lcG9zdHNpemUNCiAgICAgICAgICovDQogICAgcHJvdGVjdGVkICRzaW11bHRhbmVwb3N0c2l6ZSA9IE5VTEw7DQovKioNCiAgICAgICAgICogQHZhciBzdHJpbmcNCiAgICAgICAgICovDQogICAgcHJvdGVjdGVkICRJR0RhdGFQYXRoID0gTlVMTDsNCg0KICAgIC8qKg0KICAgICAgICAgKiBCdWxrUmVhY3Rpb24gY29uc3RydWN0b3IuDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBhcnJheSAgJHVzZXJzDQogICAgICAgICAqIEBwYXJhbSBpbnQgICAgJHNpbXVsdGFuZXBvc3RzaXplDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJElHRGF0YVBhdGgNCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gX19jb25zdHJ1Y3QoJHVzZXJzLCAkc2ltdWx0YW5lcG9zdHNpemUgPSAxMDAsICRJR0RhdGFQYXRoID0gTlVMTCkNCiAgICB7DQogICAgICAgICR0aGlzLT5zaW11bHRhbmVwb3N0c2l6ZSA9ICRzaW11bHRhbmVwb3N0c2l6ZTsNCiAgICAgICAgaWYoICFpc19udWxsKCRJR0RhdGFQYXRoKSApIA0KICAgICAgICB7DQogICAgICAgICAgICAkdGhpcy0+SUdEYXRhUGF0aCA9ICRJR0RhdGFQYXRoOw0KICAgICAgICB9DQogICAgICAgIGVsc2UNCiAgICAgICAgew0KICAgICAgICAgICAgJHRoaXMtPklHRGF0YVBhdGggPSBXb3c6OmdldCgicHJvamVjdC9jb29raWVQYXRoIikgLiAiaW5zdGFncmFtLyI7DQogICAgICAgIH0NCg0KICAgICAgICBmb3JlYWNoKCAkdXNlcnMgYXMgJHVzZXIgKSANCiAgICAgICAgew0KICAgICAgICAgICAgJHRoaXMtPnVzZXJzW10gPSBhcnJheSggImRhdGEiID0+ICR1c2VyLCAib2JqZWN0IiA9PiAoJHVzZXJbImlzV2ViQ29va2llIl0gPT0gMSA/IG5ldyBJbnN0YWdyYW1XZWIoJHVzZXJbImt1bGxhbmljaUFkaSJdKSA6IG5ldyBJbnN0YWdyYW0oJHVzZXJbImt1bGxhbmljaUFkaSJdLCAkdXNlclsic2lmcmUiXSkpICk7DQogICAgICAgIH0NCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAgJG1lZGlhSUQNCiAgICAgICAgICogQHBhcmFtICBzdHJpbmcgJG1lZGlhQ29kZQ0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGxpa2UoJG1lZGlhSUQsICRtZWRpYUNvZGUpDQogICAgew0KICAgICAgICAkdG90YWxTdWNjZXNzQ291bnQgPSAwOw0KICAgICAgICAkdHJpZWRVc2VycyA9IGFycmF5KCAgKTsNCiAgICAgICAgJHJvbGxpbmdDdXJsID0gbmV3IFJvbGxpbmdDdXJsXFJvbGxpbmdDdXJsKCk7DQogICAgICAgICRhcnJNZWRpYUlEID0gZXhwbG9kZSgiXyIsICRtZWRpYUlEKTsNCiAgICAgICAgJG1lZGlhSURCZWZvcmVyID0gJGFyck1lZGlhSURbMF07DQogICAgICAgIGZvcmVhY2goICR0aGlzLT51c2VycyBhcyAkdXNlciApIA0KICAgICAgICB7DQogICAgICAgICAgICBpZiggJHVzZXJbImRhdGEiXVsiaXNXZWJDb29raWUiXSA9PSAxICkgDQogICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgJG9iakluc3RhZ3JhbVdlYiA9ICR1c2VyWyJvYmplY3QiXTsNCiAgICAgICAgICAgICAgICAkb2JqRGF0YSA9ICRvYmpJbnN0YWdyYW1XZWItPmdldERhdGEoKTsNCiAgICAgICAgICAgICAgICAkaGVhZGVycyA9IGFycmF5KCAiUmVmZXJlcjogaHR0cHM6Ly93d3cuaW5zdGFncmFtLmNvbS9pbnN0YWdyYW0vIiwgIkROVDogMSIsICJPcmlnaW46IGh0dHBzOi8vd3d3Lmluc3RhZ3JhbS5jb20vIiwgIlgtQ1NSRlRva2VuOiAiIC4gdHJpbSgkb2JqRGF0YVsidG9rZW4iXSksICJYLVJlcXVlc3RlZC1XaXRoOiBYTUxIdHRwUmVxdWVzdCIsICJYLUluc3RhZ3JhbS1BSkFYOiAxIiwgIkNvbm5lY3Rpb246IGNsb3NlIiwgIkNhY2hlLUNvbnRyb2w6IG1heC1hZ2U9MCIsICJBY2NlcHQ6ICovKiIsICJBY2NlcHQtTGFuZ3VhZ2U6IHRyO3E9MSIsICJYX0ZPUldBUkRFRF9GT1I6ICIgLiAkb2JqRGF0YVsiaXAiXSwgIlJFTU9URV9BRERSOiAiIC4gJG9iakRhdGFbImlwIl0gKTsNCiAgICAgICAgICAgICAgICAkb3B0aW9ucyA9IGFycmF5KCBDVVJMT1BUX1VTRVJBR0VOVCA9PiAkb2JqRGF0YVsidXNlcl9hZ2VudCJdLCBDVVJMT1BUX1JFVFVSTlRSQU5TRkVSID0+IHRydWUsIENVUkxPUFRfRk9MTE9XTE9DQVRJT04gPT4gdHJ1ZSwgQ1VSTE9QVF9WRVJCT1NFID0+IGZhbHNlLCBDVVJMT1BUX1NTTF9WRVJJRllQRUVSID0+IGZhbHNlLCBDVVJMT1BUX1NTTF9WRVJJRllIT1NUID0+IGZhbHNlLCBDVVJMT1BUX0NPT0tJRUZJTEUgPT4gJHRoaXMtPklHRGF0YVBhdGggLiAkb2JqRGF0YVsidXNlcm5hbWUiXSAuICIuZGF0IiwgQ1VSTE9QVF9DT09LSUVKQVIgPT4gJHRoaXMtPklHRGF0YVBhdGggLiAkb2JqRGF0YVsidXNlcm5hbWUiXSAuICIuZGF0IiApOw0KICAgICAgICAgICAgICAgICRyb2xsaW5nQ3VybC0+cG9zdChDb25zdGFudHM6OldFQl9VUkwgLiAid2ViL2xpa2VzLyIgLiAkbWVkaWFJREJlZm9yZXIgLiAiL2xpa2UvIiwgTlVMTCwgJGhlYWRlcnMsICRvcHRpb25zLCAkdXNlclsiZGF0YSJdKTsNCiAgICAgICAgICAgIH0NCiAgICAgICAgICAgIGVsc2UNCiAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAkb2JqSW5zdGFncmFtID0gJHVzZXJbIm9iamVjdCJdOw0KICAgICAgICAgICAgICAgICRvYmpEYXRhID0gJG9iakluc3RhZ3JhbS0+Z2V0RGF0YSgpOw0KICAgICAgICAgICAgICAgICRkYXRhID0ganNvbl9lbmNvZGUoYXJyYXkoICJfdXVpZCIgPT4gJG9iakRhdGFbInV1aWQiXSwgIl91aWQiID0+ICRvYmpEYXRhWyJ1c2VybmFtZV9pZCJdLCAiX2NzcmZ0b2tlbiIgPT4gJG9iakRhdGFbInRva2VuIl0sICJtZWRpYV9pZCIgPT4gJG1lZGlhSUQgKSk7DQogICAgICAgICAgICAgICAgJGhlYWRlcnMgPSBhcnJheSggIkNvbm5lY3Rpb246IGNsb3NlIiwgIkFjY2VwdDogKi8qIiwgIlgtSUctQ2FwYWJpbGl0aWVzOiAiIC4gQ29uc3RhbnRzOjpYX0lHX0NhcGFiaWxpdGllcywgIlgtSUctQ29ubmVjdGlvbi1UeXBlOiBXSUZJIiwgIkNvbnRlbnQtdHlwZTogYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkOyBjaGFyc2V0PVVURi04IiwgIkFjY2VwdC1MYW5ndWFnZTogdHItVFIiLCAiWF9GT1JXQVJERURfRk9SOiAiIC4gJG9iakRhdGFbImlwIl0sICJSRU1PVEVfQUREUjogIiAuICRvYmpEYXRhWyJpcCJdICk7DQogICAgICAgICAgICAgICAgJG9wdGlvbnMgPSBhcnJheSggQ1VSTE9QVF9VU0VSQUdFTlQgPT4gJG9iakRhdGFbInVzZXJfYWdlbnQiXSwgQ1VSTE9QVF9SRVRVUk5UUkFOU0ZFUiA9PiB0cnVlLCBDVVJMT1BUX0ZPTExPV0xPQ0FUSU9OID0+IHRydWUsIENVUkxPUFRfVkVSQk9TRSA9PiBmYWxzZSwgQ1VSTE9QVF9TU0xfVkVSSUZZUEVFUiA9PiBmYWxzZSwgQ1VSTE9QVF9TU0xfVkVSSUZZSE9TVCA9PiBmYWxzZSwgQ1VSTE9QVF9DT09LSUVGSUxFID0+ICR0aGlzLT5JR0RhdGFQYXRoIC4gJG9iakRhdGFbInVzZXJuYW1lIl0gLiAiLmRhdCIsIENVUkxPUFRfQ09PS0lFSkFSID0+ICR0aGlzLT5JR0RhdGFQYXRoIC4gJG9iakRhdGFbInVzZXJuYW1lIl0gLiAiLmRhdCIgKTsNCiAgICAgICAgICAgICAgICAkcm9sbGluZ0N1cmwtPnBvc3QoQ29uc3RhbnRzOjpBUElfVVJMIC4gIm1lZGlhLyIgLiAkbWVkaWFJRCAuICIvbGlrZS8iLCBTaWduYXR1cmVVdGlsczo6Z2VuZXJhdGVTaWduYXR1cmUoJGRhdGEpLCAkaGVhZGVycywgJG9wdGlvbnMsICR1c2VyWyJkYXRhIl0pOw0KICAgICAgICAgICAgfQ0KDQogICAgICAgIH0NCiAgICAgICAgJHJvbGxpbmdDdXJsLT5zZXRDYWxsYmFjayhmdW5jdGlvbihSb2xsaW5nQ3VybFxSZXF1ZXN0ICRyZXF1ZXN0LCBSb2xsaW5nQ3VybFxSb2xsaW5nQ3VybCAkcm9sbGluZ0N1cmwpIHVzZSAoJiR0cmllZFVzZXJzLCAmJHRvdGFsU3VjY2Vzc0NvdW50KQ0Kew0KICAgICR0cmllZFVzZXIgPSBhcnJheSggInVzZXJJRCIgPT4gJHJlcXVlc3QtPmlkZW50aWZpZXJQYXJhbXNbInV5ZUlEIl0sICJpbnN0YUlEIiA9PiAkcmVxdWVzdC0+aWRlbnRpZmllclBhcmFtc1siaW5zdGFJRCJdLCAidXNlck5pY2siID0+ICRyZXF1ZXN0LT5pZGVudGlmaWVyUGFyYW1zWyJrdWxsYW5pY2lBZGkiXSwgInN0YXR1cyIgPT4gImZhaWwiICk7DQogICAgJGlzRXJyb3JlZCA9ICRyZXF1ZXN0LT5nZXRSZXNwb25zZUVycm9yKCk7DQogICAgaWYoIGVtcHR5KCRpc0Vycm9yZWQpICkgDQogICAgew0KICAgICAgICAkZG9uZW5Tb251YyA9IGpzb25fZGVjb2RlKCRyZXF1ZXN0LT5nZXRSZXNwb25zZVRleHQoKSwgdHJ1ZSk7DQogICAgICAgIGlmKCAkZG9uZW5Tb251YyApIA0KICAgICAgICB7DQogICAgICAgICAgICBpZiggJHJlcXVlc3QtPmlkZW50aWZpZXJQYXJhbXNbImlzV2ViQ29va2llIl0gPT0gMSApIA0KICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgIGlmKCAkZG9uZW5Tb251Y1sic3RhdHVzIl0gPT0gIm9rIiApIA0KICAgICAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAgICAgJHRvdGFsU3VjY2Vzc0NvdW50Kys7DQogICAgICAgICAgICAgICAgICAgICR0cmllZFVzZXJbInN0YXR1cyJdID0gInN1Y2Nlc3MiOw0KICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICBlbHNlDQogICAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICAgICAkdHJpZWRVc2VyWyJzdGF0dXMiXSA9ICJmYWlsIjsNCiAgICAgICAgICAgICAgICB9DQoNCiAgICAgICAgICAgIH0NCiAgICAgICAgICAgIGVsc2UNCiAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICBpZiggJGRvbmVuU29udWNbInN0YXR1cyJdID09ICJvayIgKSANCiAgICAgICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgICAgICR0b3RhbFN1Y2Nlc3NDb3VudCsrOw0KICAgICAgICAgICAgICAgICAgICAkdHJpZWRVc2VyWyJzdGF0dXMiXSA9ICJzdWNjZXNzIjsNCiAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICAgZWxzZQ0KICAgICAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAgICAgJHRyaWVkVXNlclsic3RhdHVzIl0gPSAiZmFpbCI7DQogICAgICAgICAgICAgICAgfQ0KDQogICAgICAgICAgICB9DQoNCiAgICAgICAgfQ0KICAgICAgICBlbHNlDQogICAgICAgIHsNCiAgICAgICAgICAgICR0cmllZFVzZXJbInN0YXR1cyJdID0gImZhaWwiOw0KICAgICAgICB9DQoNCiAgICB9DQogICAgZWxzZQ0KICAgIHsNCiAgICAgICAgJHRyaWVkVXNlclsic3RhdHVzIl0gPSAiZmFpbCI7DQogICAgfQ0KDQogICAgJHRyaWVkVXNlcnNbXSA9ICR0cmllZFVzZXI7DQogICAgJHJvbGxpbmdDdXJsLT5jbGVhckNvbXBsZXRlZCgpOw0KICAgICRyb2xsaW5nQ3VybC0+cHJ1bmVQZW5kaW5nUmVxdWVzdFF1ZXVlKCk7DQp9DQoNCik7DQogICAgICAgICRyb2xsaW5nQ3VybC0+c2V0U2ltdWx0YW5lb3VzTGltaXQoJHRoaXMtPnNpbXVsdGFuZXBvc3RzaXplKTsNCiAgICAgICAgJHJvbGxpbmdDdXJsLT5leGVjdXRlKCk7DQogICAgICAgIHJldHVybiBhcnJheSggInRvdGFsU3VjY2Vzc0NvdW50IiA9PiAkdG90YWxTdWNjZXNzQ291bnQsICJ1c2VycyIgPT4gJHRyaWVkVXNlcnMgKTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkdXNlcklEDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJHVzZXJOYW1lDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gZm9sbG93KCR1c2VySUQsICR1c2VyTmFtZSkNCiAgICB7DQogICAgICAgICR0b3RhbFN1Y2Nlc3NDb3VudCA9IDA7DQogICAgICAgICR0cmllZFVzZXJzID0gYXJyYXkoICApOw0KICAgICAgICAkcm9sbGluZ0N1cmwgPSBuZXcgUm9sbGluZ0N1cmxcUm9sbGluZ0N1cmwoKTsNCiAgICAgICAgZm9yZWFjaCggJHRoaXMtPnVzZXJzIGFzICR1c2VyICkgDQogICAgICAgIHsNCiAgICAgICAgICAgIGlmKCAkdXNlclsiZGF0YSJdWyJpc1dlYkNvb2tpZSJdID09IDEgKSANCiAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAkb2JqSW5zdGFncmFtV2ViID0gJHVzZXJbIm9iamVjdCJdOw0KICAgICAgICAgICAgICAgICRvYmpEYXRhID0gJG9iakluc3RhZ3JhbVdlYi0+Z2V0RGF0YSgpOw0KICAgICAgICAgICAgICAgICRoZWFkZXJzID0gYXJyYXkoICJSZWZlcmVyOiBodHRwczovL3d3dy5pbnN0YWdyYW0uY29tL2luc3RhZ3JhbS8iLCAiRE5UOiAxIiwgIk9yaWdpbjogaHR0cHM6Ly93d3cuaW5zdGFncmFtLmNvbS8iLCAiWC1DU1JGVG9rZW46ICIgLiB0cmltKCRvYmpEYXRhWyJ0b2tlbiJdKSwgIlgtUmVxdWVzdGVkLVdpdGg6IFhNTEh0dHBSZXF1ZXN0IiwgIlgtSW5zdGFncmFtLUFKQVg6IDEiLCAiQ29ubmVjdGlvbjogY2xvc2UiLCAiQ2FjaGUtQ29udHJvbDogbWF4LWFnZT0wIiwgIkFjY2VwdDogKi8qIiwgIkFjY2VwdC1MYW5ndWFnZTogdHI7cT0xIiwgIlhfRk9SV0FSREVEX0ZPUjogIiAuICRvYmpEYXRhWyJpcCJdLCAiUkVNT1RFX0FERFI6ICIgLiAkb2JqRGF0YVsiaXAiXSApOw0KICAgICAgICAgICAgICAgICRvcHRpb25zID0gYXJyYXkoIENVUkxPUFRfVVNFUkFHRU5UID0+ICRvYmpEYXRhWyJ1c2VyX2FnZW50Il0sIENVUkxPUFRfUkVUVVJOVFJBTlNGRVIgPT4gdHJ1ZSwgQ1VSTE9QVF9GT0xMT1dMT0NBVElPTiA9PiB0cnVlLCBDVVJMT1BUX1ZFUkJPU0UgPT4gZmFsc2UsIENVUkxPUFRfU1NMX1ZFUklGWVBFRVIgPT4gZmFsc2UsIENVUkxPUFRfU1NMX1ZFUklGWUhPU1QgPT4gZmFsc2UsIENVUkxPUFRfQ09PS0lFRklMRSA9PiAkdGhpcy0+SUdEYXRhUGF0aCAuICRvYmpEYXRhWyJ1c2VybmFtZSJdIC4gIi5kYXQiLCBDVVJMT1BUX0NPT0tJRUpBUiA9PiAkdGhpcy0+SUdEYXRhUGF0aCAuICRvYmpEYXRhWyJ1c2VybmFtZSJdIC4gIi5kYXQiICk7DQogICAgICAgICAgICAgICAgJHJvbGxpbmdDdXJsLT5wb3N0KENvbnN0YW50czo6V0VCX1VSTCAuICJ3ZWIvZnJpZW5kc2hpcHMvIiAuICR1c2VySUQgLiAiL2ZvbGxvdy8iLCBOVUxMLCAkaGVhZGVycywgJG9wdGlvbnMsICR1c2VyWyJkYXRhIl0pOw0KICAgICAgICAgICAgfQ0KICAgICAgICAgICAgZWxzZQ0KICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICRvYmpJbnN0YWdyYW0gPSAkdXNlclsib2JqZWN0Il07DQogICAgICAgICAgICAgICAgJG9iakRhdGEgPSAkb2JqSW5zdGFncmFtLT5nZXREYXRhKCk7DQogICAgICAgICAgICAgICAgJGRhdGEgPSBqc29uX2VuY29kZShhcnJheSggIl91dWlkIiA9PiAkb2JqRGF0YVsidXVpZCJdLCAiX3VpZCIgPT4gJG9iakRhdGFbInVzZXJuYW1lX2lkIl0sICJ1c2VyX2lkIiA9PiAkdXNlcklELCAiX2NzcmZ0b2tlbiIgPT4gJG9iakRhdGFbInRva2VuIl0gKSk7DQogICAgICAgICAgICAgICAgJGhlYWRlcnMgPSBhcnJheSggIkNvbm5lY3Rpb246IGNsb3NlIiwgIkFjY2VwdDogKi8qIiwgIlgtSUctQ2FwYWJpbGl0aWVzOiAiIC4gQ29uc3RhbnRzOjpYX0lHX0NhcGFiaWxpdGllcywgIlgtSUctQ29ubmVjdGlvbi1UeXBlOiBXSUZJIiwgIkNvbnRlbnQtdHlwZTogYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkOyBjaGFyc2V0PVVURi04IiwgIkFjY2VwdC1MYW5ndWFnZTogdHItVFIiLCAiWF9GT1JXQVJERURfRk9SOiAiIC4gJG9iakRhdGFbImlwIl0sICJSRU1PVEVfQUREUjogIiAuICRvYmpEYXRhWyJpcCJdICk7DQogICAgICAgICAgICAgICAgJG9wdGlvbnMgPSBhcnJheSggQ1VSTE9QVF9VU0VSQUdFTlQgPT4gJG9iakRhdGFbInVzZXJfYWdlbnQiXSwgQ1VSTE9QVF9SRVRVUk5UUkFOU0ZFUiA9PiB0cnVlLCBDVVJMT1BUX0ZPTExPV0xPQ0FUSU9OID0+IHRydWUsIENVUkxPUFRfVkVSQk9TRSA9PiBmYWxzZSwgQ1VSTE9QVF9TU0xfVkVSSUZZUEVFUiA9PiBmYWxzZSwgQ1VSTE9QVF9TU0xfVkVSSUZZSE9TVCA9PiBmYWxzZSwgQ1VSTE9QVF9DT09LSUVGSUxFID0+ICR0aGlzLT5JR0RhdGFQYXRoIC4gJG9iakRhdGFbInVzZXJuYW1lIl0gLiAiLmRhdCIsIENVUkxPUFRfQ09PS0lFSkFSID0+ICR0aGlzLT5JR0RhdGFQYXRoIC4gJG9iakRhdGFbInVzZXJuYW1lIl0gLiAiLmRhdCIgKTsNCiAgICAgICAgICAgICAgICAkcm9sbGluZ0N1cmwtPnBvc3QoQ29uc3RhbnRzOjpBUElfVVJMIC4gImZyaWVuZHNoaXBzL2NyZWF0ZS8iIC4gJHVzZXJJRCAuICIvIiwgU2lnbmF0dXJlVXRpbHM6OmdlbmVyYXRlU2lnbmF0dXJlKCRkYXRhKSwgJGhlYWRlcnMsICRvcHRpb25zLCAkdXNlclsiZGF0YSJdKTsNCiAgICAgICAgICAgIH0NCg0KICAgICAgICB9DQogICAgICAgICRyb2xsaW5nQ3VybC0+c2V0Q2FsbGJhY2soZnVuY3Rpb24oUm9sbGluZ0N1cmxcUmVxdWVzdCAkcmVxdWVzdCwgUm9sbGluZ0N1cmxcUm9sbGluZ0N1cmwgJHJvbGxpbmdDdXJsKSB1c2UgKCYkdHJpZWRVc2VycywgJiR0b3RhbFN1Y2Nlc3NDb3VudCkNCnsNCiAgICAkdHJpZWRVc2VyID0gYXJyYXkoICJ1c2VySUQiID0+ICRyZXF1ZXN0LT5pZGVudGlmaWVyUGFyYW1zWyJ1eWVJRCJdLCAiaW5zdGFJRCIgPT4gJHJlcXVlc3QtPmlkZW50aWZpZXJQYXJhbXNbImluc3RhSUQiXSwgInVzZXJOaWNrIiA9PiAkcmVxdWVzdC0+aWRlbnRpZmllclBhcmFtc1sia3VsbGFuaWNpQWRpIl0sICJzdGF0dXMiID0+ICJmYWlsIiApOw0KICAgICRpc0Vycm9yZWQgPSAkcmVxdWVzdC0+Z2V0UmVzcG9uc2VFcnJvcigpOw0KICAgIGlmKCBlbXB0eSgkaXNFcnJvcmVkKSApIA0KICAgIHsNCiAgICAgICAgJGRvbmVuU29udWMgPSBqc29uX2RlY29kZSgkcmVxdWVzdC0+Z2V0UmVzcG9uc2VUZXh0KCksIHRydWUpOw0KICAgICAgICBpZiggJGRvbmVuU29udWMgKSANCiAgICAgICAgew0KICAgICAgICAgICAgaWYoICRyZXF1ZXN0LT5pZGVudGlmaWVyUGFyYW1zWyJpc1dlYkNvb2tpZSJdID09IDEgKSANCiAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICBpZiggJGRvbmVuU29udWNbInN0YXR1cyJdID09ICJvayIgJiYgJGRvbmVuU29udWNbInJlc3VsdCJdID09ICJmb2xsb3dpbmciICkgDQogICAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICAgICAkdG90YWxTdWNjZXNzQ291bnQrKzsNCiAgICAgICAgICAgICAgICAgICAgJHRyaWVkVXNlclsic3RhdHVzIl0gPSAic3VjY2VzcyI7DQogICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgIGVsc2UNCiAgICAgICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgICAgICR0cmllZFVzZXJbInN0YXR1cyJdID0gImZhaWwiOw0KICAgICAgICAgICAgICAgIH0NCg0KICAgICAgICAgICAgfQ0KICAgICAgICAgICAgZWxzZQ0KICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgIGlmKCAkZG9uZW5Tb251Y1sic3RhdHVzIl0gPT0gIm9rIiApIA0KICAgICAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAgICAgJHRvdGFsU3VjY2Vzc0NvdW50Kys7DQogICAgICAgICAgICAgICAgICAgICR0cmllZFVzZXJbInN0YXR1cyJdID0gInN1Y2Nlc3MiOw0KICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICBlbHNlDQogICAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICAgICAkdHJpZWRVc2VyWyJzdGF0dXMiXSA9ICJmYWlsIjsNCiAgICAgICAgICAgICAgICB9DQoNCiAgICAgICAgICAgIH0NCg0KICAgICAgICB9DQogICAgICAgIGVsc2UNCiAgICAgICAgew0KICAgICAgICAgICAgJHRyaWVkVXNlclsic3RhdHVzIl0gPSAiZmFpbCI7DQogICAgICAgIH0NCg0KICAgIH0NCiAgICBlbHNlDQogICAgew0KICAgICAgICAkdHJpZWRVc2VyWyJzdGF0dXMiXSA9ICJmYWlsIjsNCiAgICB9DQoNCiAgICAkdHJpZWRVc2Vyc1tdID0gJHRyaWVkVXNlcjsNCiAgICAkcm9sbGluZ0N1cmwtPmNsZWFyQ29tcGxldGVkKCk7DQogICAgJHJvbGxpbmdDdXJsLT5wcnVuZVBlbmRpbmdSZXF1ZXN0UXVldWUoKTsNCn0NCg0KKTsNCiAgICAgICAgJHJvbGxpbmdDdXJsLT5zZXRTaW11bHRhbmVvdXNMaW1pdCgkdGhpcy0+c2ltdWx0YW5lcG9zdHNpemUpOw0KICAgICAgICAkcm9sbGluZ0N1cmwtPmV4ZWN1dGUoKTsNCiAgICAgICAgcmV0dXJuIGFycmF5KCAidG90YWxTdWNjZXNzQ291bnQiID0+ICR0b3RhbFN1Y2Nlc3NDb3VudCwgInVzZXJzIiA9PiAkdHJpZWRVc2VycyApOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICRtZWRpYUlEDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJG1lZGlhQ29kZQ0KICAgICAgICAgKiBAcGFyYW0gYXJyYXkgICRjb21tZW50VGV4dHMNCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBjb21tZW50KCRtZWRpYUlELCAkbWVkaWFDb2RlLCAkY29tbWVudFRleHRzKQ0KICAgIHsNCiAgICAgICAgJHRvdGFsU3VjY2Vzc0NvdW50ID0gMDsNCiAgICAgICAgJHRyaWVkVXNlcnMgPSBhcnJheSggICk7DQogICAgICAgICRjb21tZW50SW5kZXggPSAtMTsNCiAgICAgICAgaWYoIGlzX2FycmF5KCRjb21tZW50VGV4dHMpICYmICFlbXB0eSgkY29tbWVudFRleHRzKSApIA0KICAgICAgICB7DQogICAgICAgICAgICAkYXJyTWVkaWFJRCA9IGV4cGxvZGUoIl8iLCAkbWVkaWFJRCk7DQogICAgICAgICAgICAkbWVkaWFJREJlZm9yZXIgPSAkYXJyTWVkaWFJRFswXTsNCiAgICAgICAgICAgICRyb2xsaW5nQ3VybCA9IG5ldyBSb2xsaW5nQ3VybFxSb2xsaW5nQ3VybCgpOw0KICAgICAgICAgICAgZm9yZWFjaCggJHRoaXMtPnVzZXJzIGFzICR1c2VyICkgDQogICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgJGNvbW1lbnRJbmRleCsrOw0KICAgICAgICAgICAgICAgICRjb21tZW50SW5kZXggPSAoaXNzZXQoJGNvbW1lbnRUZXh0c1skY29tbWVudEluZGV4XSkgPyAkY29tbWVudEluZGV4IDogMCk7DQogICAgICAgICAgICAgICAgaWYoICR1c2VyWyJkYXRhIl1bImlzV2ViQ29va2llIl0gPT0gMSApIA0KICAgICAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAgICAgJG9iakluc3RhZ3JhbVdlYiA9ICR1c2VyWyJvYmplY3QiXTsNCiAgICAgICAgICAgICAgICAgICAgJG9iakRhdGEgPSAkb2JqSW5zdGFncmFtV2ViLT5nZXREYXRhKCk7DQogICAgICAgICAgICAgICAgICAgICRwb3N0RGF0YSA9ICJjb21tZW50X3RleHQ9IiAuICRjb21tZW50VGV4dHNbJGNvbW1lbnRJbmRleF07DQogICAgICAgICAgICAgICAgICAgICRoZWFkZXJzID0gYXJyYXkoICJSZWZlcmVyOiBodHRwczovL3d3dy5pbnN0YWdyYW0uY29tLyIsICJETlQ6IDEiLCAiT3JpZ2luOiBodHRwczovL3d3dy5pbnN0YWdyYW0uY29tLyIsICJYLUNTUkZUb2tlbjogIiAuIHRyaW0oJG9iakRhdGFbInRva2VuIl0pLCAiWC1SZXF1ZXN0ZWQtV2l0aDogWE1MSHR0cFJlcXVlc3QiLCAiWC1JbnN0YWdyYW0tQUpBWDogMSIsICJDb25uZWN0aW9uOiBjbG9zZSIsICJDYWNoZS1Db250cm9sOiBtYXgtYWdlPTAiLCAiQWNjZXB0OiAqLyoiLCAiQWNjZXB0LUxhbmd1YWdlOiB0cjtxPTEiLCAiWF9GT1JXQVJERURfRk9SOiAiIC4gJG9iakRhdGFbImlwIl0sICJSRU1PVEVfQUREUjogIiAuICRvYmpEYXRhWyJpcCJdICk7DQogICAgICAgICAgICAgICAgICAgICRvcHRpb25zID0gYXJyYXkoIENVUkxPUFRfVVNFUkFHRU5UID0+ICRvYmpEYXRhWyJ1c2VyX2FnZW50Il0sIENVUkxPUFRfUkVUVVJOVFJBTlNGRVIgPT4gdHJ1ZSwgQ1VSTE9QVF9GT0xMT1dMT0NBVElPTiA9PiB0cnVlLCBDVVJMT1BUX1ZFUkJPU0UgPT4gZmFsc2UsIENVUkxPUFRfU1NMX1ZFUklGWVBFRVIgPT4gZmFsc2UsIENVUkxPUFRfU1NMX1ZFUklGWUhPU1QgPT4gZmFsc2UsIENVUkxPUFRfQ09PS0lFRklMRSA9PiAkdGhpcy0+SUdEYXRhUGF0aCAuICRvYmpEYXRhWyJ1c2VybmFtZSJdIC4gIi5kYXQiLCBDVVJMT1BUX0NPT0tJRUpBUiA9PiAkdGhpcy0+SUdEYXRhUGF0aCAuICRvYmpEYXRhWyJ1c2VybmFtZSJdIC4gIi5kYXQiICk7DQogICAgICAgICAgICAgICAgICAgICRyb2xsaW5nQ3VybC0+cG9zdChDb25zdGFudHM6OldFQl9VUkwgLiAid2ViL2NvbW1lbnRzLyIgLiAkbWVkaWFJREJlZm9yZXIgLiAiL2FkZC8iLCAkcG9zdERhdGEsICRoZWFkZXJzLCAkb3B0aW9ucywgJHVzZXJbImRhdGEiXSk7DQogICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgIGVsc2UNCiAgICAgICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgICAgICRvYmpJbnN0YWdyYW0gPSAkdXNlclsib2JqZWN0Il07DQogICAgICAgICAgICAgICAgICAgICRvYmpEYXRhID0gJG9iakluc3RhZ3JhbS0+Z2V0RGF0YSgpOw0KICAgICAgICAgICAgICAgICAgICAkZGF0YSA9IGpzb25fZW5jb2RlKGFycmF5KCAiX3V1aWQiID0+ICRvYmpEYXRhWyJ1dWlkIl0sICJfdWlkIiA9PiAkb2JqRGF0YVsidXNlcm5hbWVfaWQiXSwgIl9jc3JmdG9rZW4iID0+ICRvYmpEYXRhWyJ0b2tlbiJdLCAiY29tbWVudF90ZXh0IiA9PiAkY29tbWVudFRleHRzWyRjb21tZW50SW5kZXhdICkpOw0KICAgICAgICAgICAgICAgICAgICAkaGVhZGVycyA9IGFycmF5KCAiQ29ubmVjdGlvbjogY2xvc2UiLCAiQWNjZXB0OiAqLyoiLCAiWC1JRy1DYXBhYmlsaXRpZXM6ICIgLiBDb25zdGFudHM6OlhfSUdfQ2FwYWJpbGl0aWVzLCAiWC1JRy1Db25uZWN0aW9uLVR5cGU6IFdJRkkiLCAiQ29udGVudC10eXBlOiBhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQ7IGNoYXJzZXQ9VVRGLTgiLCAiQWNjZXB0LUxhbmd1YWdlOiB0ci1UUiIsICJYX0ZPUldBUkRFRF9GT1I6ICIgLiAkb2JqRGF0YVsiaXAiXSwgIlJFTU9URV9BRERSOiAiIC4gJG9iakRhdGFbImlwIl0gKTsNCiAgICAgICAgICAgICAgICAgICAgJG9wdGlvbnMgPSBhcnJheSggQ1VSTE9QVF9VU0VSQUdFTlQgPT4gJG9iakRhdGFbInVzZXJfYWdlbnQiXSwgQ1VSTE9QVF9SRVRVUk5UUkFOU0ZFUiA9PiB0cnVlLCBDVVJMT1BUX0ZPTExPV0xPQ0FUSU9OID0+IHRydWUsIENVUkxPUFRfVkVSQk9TRSA9PiBmYWxzZSwgQ1VSTE9QVF9TU0xfVkVSSUZZUEVFUiA9PiBmYWxzZSwgQ1VSTE9QVF9TU0xfVkVSSUZZSE9TVCA9PiBmYWxzZSwgQ1VSTE9QVF9DT09LSUVGSUxFID0+ICR0aGlzLT5JR0RhdGFQYXRoIC4gJG9iakRhdGFbInVzZXJuYW1lIl0gLiAiLmRhdCIsIENVUkxPUFRfQ09PS0lFSkFSID0+ICR0aGlzLT5JR0RhdGFQYXRoIC4gJG9iakRhdGFbInVzZXJuYW1lIl0gLiAiLmRhdCIgKTsNCiAgICAgICAgICAgICAgICAgICAgJHJvbGxpbmdDdXJsLT5wb3N0KENvbnN0YW50czo6QVBJX1VSTCAuICJtZWRpYS8iIC4gJG1lZGlhSUQgLiAiL2NvbW1lbnQvIiwgU2lnbmF0dXJlVXRpbHM6OmdlbmVyYXRlU2lnbmF0dXJlKCRkYXRhKSwgJGhlYWRlcnMsICRvcHRpb25zLCAkdXNlclsiZGF0YSJdKTsNCiAgICAgICAgICAgICAgICB9DQoNCiAgICAgICAgICAgIH0NCiAgICAgICAgICAgICRyb2xsaW5nQ3VybC0+c2V0Q2FsbGJhY2soZnVuY3Rpb24oUm9sbGluZ0N1cmxcUmVxdWVzdCAkcmVxdWVzdCwgUm9sbGluZ0N1cmxcUm9sbGluZ0N1cmwgJHJvbGxpbmdDdXJsKSB1c2UgKCYkdHJpZWRVc2VycywgJiR0b3RhbFN1Y2Nlc3NDb3VudCkNCnsNCiAgICAkdHJpZWRVc2VyID0gYXJyYXkoICJ1c2VySUQiID0+ICRyZXF1ZXN0LT5pZGVudGlmaWVyUGFyYW1zWyJ1eWVJRCJdLCAiaW5zdGFJRCIgPT4gJHJlcXVlc3QtPmlkZW50aWZpZXJQYXJhbXNbImluc3RhSUQiXSwgInVzZXJOaWNrIiA9PiAkcmVxdWVzdC0+aWRlbnRpZmllclBhcmFtc1sia3VsbGFuaWNpQWRpIl0sICJzdGF0dXMiID0+ICJmYWlsIiApOw0KICAgICRpc0Vycm9yZWQgPSAkcmVxdWVzdC0+Z2V0UmVzcG9uc2VFcnJvcigpOw0KICAgIGlmKCBlbXB0eSgkaXNFcnJvcmVkKSApIA0KICAgIHsNCiAgICAgICAgJGRvbmVuU29udWMgPSBqc29uX2RlY29kZSgkcmVxdWVzdC0+Z2V0UmVzcG9uc2VUZXh0KCksIHRydWUpOw0KICAgICAgICBpZiggJGRvbmVuU29udWMgKSANCiAgICAgICAgew0KICAgICAgICAgICAgaWYoICRyZXF1ZXN0LT5pZGVudGlmaWVyUGFyYW1zWyJpc1dlYkNvb2tpZSJdID09IDEgKSANCiAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICBpZiggaXNzZXQoJGRvbmVuU29udWNbInN0YXR1cyJdKSAmJiAkZG9uZW5Tb251Y1sic3RhdHVzIl0gPT0gIm9rIiApIA0KICAgICAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAgICAgJHRvdGFsU3VjY2Vzc0NvdW50Kys7DQogICAgICAgICAgICAgICAgICAgICR0cmllZFVzZXJbInN0YXR1cyJdID0gInN1Y2Nlc3MiOw0KICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICBlbHNlDQogICAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICAgICAkdHJpZWRVc2VyWyJzdGF0dXMiXSA9ICJmYWlsIjsNCiAgICAgICAgICAgICAgICB9DQoNCiAgICAgICAgICAgIH0NCiAgICAgICAgICAgIGVsc2UNCiAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICBpZiggaXNzZXQoJGRvbmVuU29udWNbInN0YXR1cyJdKSAmJiAkZG9uZW5Tb251Y1sic3RhdHVzIl0gPT0gIm9rIiApIA0KICAgICAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAgICAgJHRvdGFsU3VjY2Vzc0NvdW50Kys7DQogICAgICAgICAgICAgICAgICAgICR0cmllZFVzZXJbInN0YXR1cyJdID0gInN1Y2Nlc3MiOw0KICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICBlbHNlDQogICAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICAgICAkdHJpZWRVc2VyWyJzdGF0dXMiXSA9ICJmYWlsIjsNCiAgICAgICAgICAgICAgICB9DQoNCiAgICAgICAgICAgIH0NCg0KICAgICAgICB9DQogICAgICAgIGVsc2UNCiAgICAgICAgew0KICAgICAgICAgICAgJHRyaWVkVXNlclsic3RhdHVzIl0gPSAiZmFpbCI7DQogICAgICAgIH0NCg0KICAgIH0NCiAgICBlbHNlDQogICAgew0KICAgICAgICAkdHJpZWRVc2VyWyJzdGF0dXMiXSA9ICJmYWlsIjsNCiAgICB9DQoNCiAgICAkdHJpZWRVc2Vyc1tdID0gJHRyaWVkVXNlcjsNCiAgICAkcm9sbGluZ0N1cmwtPmNsZWFyQ29tcGxldGVkKCk7DQogICAgJHJvbGxpbmdDdXJsLT5wcnVuZVBlbmRpbmdSZXF1ZXN0UXVldWUoKTsNCn0NCg0KKTsNCiAgICAgICAgICAgICRyb2xsaW5nQ3VybC0+c2V0U2ltdWx0YW5lb3VzTGltaXQoJHRoaXMtPnNpbXVsdGFuZXBvc3RzaXplKTsNCiAgICAgICAgICAgICRyb2xsaW5nQ3VybC0+ZXhlY3V0ZSgpOw0KICAgICAgICB9DQoNCiAgICAgICAgcmV0dXJuIGFycmF5KCAidG90YWxTdWNjZXNzQ291bnQiID0+ICR0b3RhbFN1Y2Nlc3NDb3VudCwgInVzZXJzIiA9PiAkdHJpZWRVc2VycyApOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIHZhbGlkYXRlKCkNCiAgICB7DQogICAgICAgICR0b3RhbFN1Y2Nlc3NDb3VudCA9IDA7DQogICAgICAgICR0cmllZFVzZXJzID0gYXJyYXkoICApOw0KICAgICAgICAkcm9sbGluZ0N1cmwgPSBuZXcgUm9sbGluZ0N1cmxcUm9sbGluZ0N1cmwoKTsNCiAgICAgICAgZm9yZWFjaCggJHRoaXMtPnVzZXJzIGFzICR1c2VyICkgDQogICAgICAgIHsNCiAgICAgICAgICAgIGlmKCAkdXNlclsiZGF0YSJdWyJpc1dlYkNvb2tpZSJdID09IDEgKSANCiAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAkb2JqSW5zdGFncmFtV2ViID0gJHVzZXJbIm9iamVjdCJdOw0KICAgICAgICAgICAgICAgICRvYmpEYXRhID0gJG9iakluc3RhZ3JhbVdlYi0+Z2V0RGF0YSgpOw0KICAgICAgICAgICAgICAgICRoZWFkZXJzID0gYXJyYXkoICJSZWZlcmVyOiBodHRwczovL3d3dy5pbnN0YWdyYW0uY29tL2luc3RhZ3JhbS8iLCAiRE5UOiAxIiwgIk9yaWdpbjogaHR0cHM6Ly93d3cuaW5zdGFncmFtLmNvbS8iLCAiWC1DU1JGVG9rZW46ICIgLiB0cmltKCRvYmpEYXRhWyJ0b2tlbiJdKSwgIlgtUmVxdWVzdGVkLVdpdGg6IFhNTEh0dHBSZXF1ZXN0IiwgIlgtSW5zdGFncmFtLUFKQVg6IDEiLCAiQ29ubmVjdGlvbjogY2xvc2UiLCAiQ2FjaGUtQ29udHJvbDogbWF4LWFnZT0wIiwgIkFjY2VwdDogKi8qIiwgIkFjY2VwdC1MYW5ndWFnZTogdHI7cT0xIiwgIlhfRk9SV0FSREVEX0ZPUjogIiAuICRvYmpEYXRhWyJpcCJdLCAiUkVNT1RFX0FERFI6ICIgLiAkb2JqRGF0YVsiaXAiXSApOw0KICAgICAgICAgICAgICAgICRvcHRpb25zID0gYXJyYXkoIENVUkxPUFRfVVNFUkFHRU5UID0+ICRvYmpEYXRhWyJ1c2VyX2FnZW50Il0sIENVUkxPUFRfUkVUVVJOVFJBTlNGRVIgPT4gdHJ1ZSwgQ1VSTE9QVF9GT0xMT1dMT0NBVElPTiA9PiB0cnVlLCBDVVJMT1BUX1ZFUkJPU0UgPT4gZmFsc2UsIENVUkxPUFRfU1NMX1ZFUklGWVBFRVIgPT4gZmFsc2UsIENVUkxPUFRfU1NMX1ZFUklGWUhPU1QgPT4gZmFsc2UsIENVUkxPUFRfQ09PS0lFRklMRSA9PiAkdGhpcy0+SUdEYXRhUGF0aCAuICRvYmpEYXRhWyJ1c2VybmFtZSJdIC4gIi5kYXQiLCBDVVJMT1BUX0NPT0tJRUpBUiA9PiAkdGhpcy0+SUdEYXRhUGF0aCAuICRvYmpEYXRhWyJ1c2VybmFtZSJdIC4gIi5kYXQiICk7DQogICAgICAgICAgICAgICAgJHJvbGxpbmdDdXJsLT5nZXQoQ29uc3RhbnRzOjpXRUJfVVJMIC4gImFjY291bnRzL2FjdGl2aXR5Lz9fX2E9MSIsICRoZWFkZXJzLCAkb3B0aW9ucywgJHVzZXJbImRhdGEiXSk7DQogICAgICAgICAgICB9DQogICAgICAgICAgICBlbHNlDQogICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgJG9iakluc3RhZ3JhbSA9ICR1c2VyWyJvYmplY3QiXTsNCiAgICAgICAgICAgICAgICAkb2JqRGF0YSA9ICRvYmpJbnN0YWdyYW0tPmdldERhdGEoKTsNCiAgICAgICAgICAgICAgICAkaGVhZGVycyA9IGFycmF5KCAiQ29ubmVjdGlvbjogY2xvc2UiLCAiQWNjZXB0OiAqLyoiLCAiWC1JRy1DYXBhYmlsaXRpZXM6ICIgLiBDb25zdGFudHM6OlhfSUdfQ2FwYWJpbGl0aWVzLCAiWC1JRy1Db25uZWN0aW9uLVR5cGU6IFdJRkkiLCAiQ29udGVudC10eXBlOiBhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQ7IGNoYXJzZXQ9VVRGLTgiLCAiQWNjZXB0LUxhbmd1YWdlOiB0ci1UUiIsICJYX0ZPUldBUkRFRF9GT1I6ICIgLiAkb2JqRGF0YVsiaXAiXSwgIlJFTU9URV9BRERSOiAiIC4gJG9iakRhdGFbImlwIl0gKTsNCiAgICAgICAgICAgICAgICAkb3B0aW9ucyA9IGFycmF5KCBDVVJMT1BUX1VTRVJBR0VOVCA9PiAkb2JqRGF0YVsidXNlcl9hZ2VudCJdLCBDVVJMT1BUX1JFVFVSTlRSQU5TRkVSID0+IHRydWUsIENVUkxPUFRfRk9MTE9XTE9DQVRJT04gPT4gdHJ1ZSwgQ1VSTE9QVF9WRVJCT1NFID0+IGZhbHNlLCBDVVJMT1BUX1NTTF9WRVJJRllQRUVSID0+IGZhbHNlLCBDVVJMT1BUX1NTTF9WRVJJRllIT1NUID0+IGZhbHNlLCBDVVJMT1BUX0NPT0tJRUZJTEUgPT4gJHRoaXMtPklHRGF0YVBhdGggLiAkb2JqRGF0YVsidXNlcm5hbWUiXSAuICIuZGF0IiwgQ1VSTE9QVF9DT09LSUVKQVIgPT4gJHRoaXMtPklHRGF0YVBhdGggLiAkb2JqRGF0YVsidXNlcm5hbWUiXSAuICIuZGF0IiApOw0KICAgICAgICAgICAgICAgICRyb2xsaW5nQ3VybC0+Z2V0KENvbnN0YW50czo6QVBJX1VSTCAuICJmZWVkL3RpbWVsaW5lLz9yYW5rX3Rva2VuPSIgLiAkb2JqRGF0YVsicmFua190b2tlbiJdIC4gIiZyYW5rZWRfY29udGVudD10cnVlIiwgJGhlYWRlcnMsICRvcHRpb25zLCAkdXNlclsiZGF0YSJdKTsNCiAgICAgICAgICAgIH0NCg0KICAgICAgICB9DQogICAgICAgICRyb2xsaW5nQ3VybC0+c2V0Q2FsbGJhY2soZnVuY3Rpb24oUm9sbGluZ0N1cmxcUmVxdWVzdCAkcmVxdWVzdCwgUm9sbGluZ0N1cmxcUm9sbGluZ0N1cmwgJHJvbGxpbmdDdXJsKSB1c2UgKCYkdHJpZWRVc2VycywgJiR0b3RhbFN1Y2Nlc3NDb3VudCkNCnsNCiAgICAkdHJpZWRVc2VyID0gYXJyYXkoICJ1c2VySUQiID0+ICRyZXF1ZXN0LT5pZGVudGlmaWVyUGFyYW1zWyJ1eWVJRCJdLCAiaW5zdGFJRCIgPT4gJHJlcXVlc3QtPmlkZW50aWZpZXJQYXJhbXNbImluc3RhSUQiXSwgInVzZXJOaWNrIiA9PiAkcmVxdWVzdC0+aWRlbnRpZmllclBhcmFtc1sia3VsbGFuaWNpQWRpIl0sICJzdGF0dXMiID0+ICJmYWlsIiApOw0KICAgICRpc0Vycm9yZWQgPSAkcmVxdWVzdC0+Z2V0UmVzcG9uc2VFcnJvcigpOw0KICAgIGlmKCBlbXB0eSgkaXNFcnJvcmVkKSApIA0KICAgIHsNCiAgICAgICAgJGRvbmVuU29udWMgPSBqc29uX2RlY29kZSgkcmVxdWVzdC0+Z2V0UmVzcG9uc2VUZXh0KCksIHRydWUpOw0KICAgICAgICBpZiggJGRvbmVuU29udWMgKSANCiAgICAgICAgew0KICAgICAgICAgICAgaWYoICRyZXF1ZXN0LT5pZGVudGlmaWVyUGFyYW1zWyJpc1dlYkNvb2tpZSJdID09IDEgKSANCiAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAkcmVzcG9uc2VEYXRhID0gJHJlcXVlc3QtPmdldFJlc3BvbnNlSW5mbygpOw0KICAgICAgICAgICAgICAgIGlmKCAkcmVzcG9uc2VEYXRhWyJodHRwX2NvZGUiXSA9PSAiMjAwIiApIA0KICAgICAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAgICAgJHRvdGFsU3VjY2Vzc0NvdW50Kys7DQogICAgICAgICAgICAgICAgICAgICR0cmllZFVzZXJbInN0YXR1cyJdID0gInN1Y2Nlc3MiOw0KICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICBlbHNlDQogICAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICAgICAkdHJpZWRVc2VyWyJzdGF0dXMiXSA9ICJmYWlsIjsNCiAgICAgICAgICAgICAgICB9DQoNCiAgICAgICAgICAgIH0NCiAgICAgICAgICAgIGVsc2UNCiAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICBpZiggJGRvbmVuU29udWNbInN0YXR1cyJdID09ICJvayIgKSANCiAgICAgICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgICAgICR0b3RhbFN1Y2Nlc3NDb3VudCsrOw0KICAgICAgICAgICAgICAgICAgICAkdHJpZWRVc2VyWyJzdGF0dXMiXSA9ICJzdWNjZXNzIjsNCiAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICAgZWxzZQ0KICAgICAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAgICAgJHRyaWVkVXNlclsic3RhdHVzIl0gPSAiZmFpbCI7DQogICAgICAgICAgICAgICAgfQ0KDQogICAgICAgICAgICB9DQoNCiAgICAgICAgfQ0KICAgICAgICBlbHNlDQogICAgICAgIHsNCiAgICAgICAgICAgICR0cmllZFVzZXJbInN0YXR1cyJdID0gImZhaWwiOw0KICAgICAgICB9DQoNCiAgICB9DQogICAgZWxzZQ0KICAgIHsNCiAgICAgICAgJHRyaWVkVXNlclsic3RhdHVzIl0gPSAiZmFpbCI7DQogICAgfQ0KDQogICAgJHRyaWVkVXNlcnNbXSA9ICR0cmllZFVzZXI7DQogICAgJHJvbGxpbmdDdXJsLT5jbGVhckNvbXBsZXRlZCgpOw0KICAgICRyb2xsaW5nQ3VybC0+cHJ1bmVQZW5kaW5nUmVxdWVzdFF1ZXVlKCk7DQp9DQoNCik7DQogICAgICAgICRyb2xsaW5nQ3VybC0+c2V0U2ltdWx0YW5lb3VzTGltaXQoJHRoaXMtPnNpbXVsdGFuZXBvc3RzaXplKTsNCiAgICAgICAgJHJvbGxpbmdDdXJsLT5leGVjdXRlKCk7DQogICAgICAgIHJldHVybiBhcnJheSggInRvdGFsU3VjY2Vzc0NvdW50IiA9PiAkdG90YWxTdWNjZXNzQ291bnQsICJ1c2VycyIgPT4gJHRyaWVkVXNlcnMgKTsNCiAgICB9DQoNCn0NCg0KDQo=")); ?>
Function Calls
| base64_decode | 1 |
Stats
| MD5 | e9ad051c6d90e84dee7b6aa0ceb2d6e4 |
| Eval Count | 1 |
| Decode Time | 212 ms |