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 = "-2017";//Lisans kodu sonuna istediiniz yaz eklenebilir
$m = "md5";//md5 sifreleme
$s = "sha1";//sha1 sifreleme
$lisans[\'hash\']=wordwrap(strtoupper($s ($s ($s ($s ($m ($s ($s ($m ($lisans[\'site\'].date(\'Ymd\')))))))))),5,\'-\',true);//lisans kodunu olustur ve 5 karakterde bir - koy
$liskod = $lisans[\'hash\'];
$cevir=strrev($liskod);//lisans kodunu tersine cevir
$bcs = "$bas$cevir$son";
if($bcs!==$lisanskodu)//lisans kodunu kontrol et fark varsa sunucuya baglan - 24 saatte bir lisans kodu degisir
{
$lisans_cevap=file_get_contents(\'http://insta.adanabilisim.net/lisans/kontrol.php?site=\'.$lisans[\'site\']);//lisans sunucusundan lisans kontrolu yap
if($lisans_cevap!=\'LISANSLI\')//sunucudan gelen cevap LISANSLI degilse
die(\'Bu siteye ait lisans bulunamadi. Hosting Bilgileriniz Site Sahibine Gonderilmistir\');//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("PD9waHAgDQpyZXF1aXJlKCdsaXNhbnMucGhwJyk7Ly9saXNhbnMucGhwIGRvc3lhc2luaSBla2xlDQokbGlzYW5zWydzaXRlJ109Z2V0ZW52KCdIVFRQX0hPU1QnKTsvL3NpdGUgZG9tYWluaW5pIGFsDQppZiAoc3Vic3RyKCRsaXNhbnNbJ3NpdGUnXSwgMCwgNCkgPT0gInd3dy4iKS8vZG9tYWluIGJhc2luZGFraSB3d3cuIHZhcm1pIHlva211IGtvbnRyb2wgZXQNCiRsaXNhbnNbJ3NpdGUnXT1zdWJzdHIoJGxpc2Fuc1snc2l0ZSddLDQpOy8vd3d3LiBzaWwgdmUgc2FkZWNlIGRvbWFpbi5jb20gb2xhcmFrIGFkcmVzaSBhbA0KIA0KJGJhcyA9ICJTT1JVTlNVWlNDUt1QVC0iOy8vTGlzYW5zIGtvZHUgYmH+/W5hIGlzdGVkafBpbml6IHlhev0gZWtsZW5lYmlsaXINCiRzb24gPSAiLTIwMTciOy8vTGlzYW5zIGtvZHUgc29udW5hIGlzdGVkafBpbml6IHlhev0gZWtsZW5lYmlsaXINCiRtID0gIm1kNSI7Ly9tZDUgc2lmcmVsZW1lIA0KJHMgPSAic2hhMSI7Ly9zaGExIHNpZnJlbGVtZSANCiANCiANCiRsaXNhbnNbJ2hhc2gnXT13b3Jkd3JhcChzdHJ0b3VwcGVyKCRzICgkcyAoJHMgKCRzICgkbSAoJHMgKCRzICgkbSAoJGxpc2Fuc1snc2l0ZSddLmRhdGUoJ1ltZCcpKSkpKSkpKSkpLDUsJy0nLHRydWUpOy8vbGlzYW5zIGtvZHVudSBvbHVzdHVyIHZlIDUga2FyYWt0ZXJkZSBiaXIgLSBrb3kNCiRsaXNrb2QgPSAkbGlzYW5zWydoYXNoJ107DQokY2V2aXI9c3RycmV2KCRsaXNrb2QpOy8vbGlzYW5zIGtvZHVudSB0ZXJzaW5lIGNldmlyDQokYmNzID0gIiRiYXMkY2V2aXIkc29uIjsNCiANCmlmKCRiY3MhPT0kbGlzYW5za29kdSkvL2xpc2FucyBrb2R1bnUga29udHJvbCBldCBmYXJrIHZhcnNhIHN1bnVjdXlhIGJhZ2xhbiAtIDI0IHNhYXR0ZSBiaXIgbGlzYW5zIGtvZHUgZGVnaXNpcg0Kew0KJGxpc2Fuc19jZXZhcD1maWxlX2dldF9jb250ZW50cygnaHR0cDovL2luc3RhLmFkYW5hYmlsaXNpbS5uZXQvbGlzYW5zL2tvbnRyb2wucGhwP3NpdGU9Jy4kbGlzYW5zWydzaXRlJ10pOy8vbGlzYW5zIHN1bnVjdXN1bmRhbiBsaXNhbnMga29udHJvbHUgeWFwDQppZigkbGlzYW5zX2NldmFwIT0nTElTQU5TTEknKS8vc3VudWN1ZGFuIGdlbGVuIGNldmFwIExJU0FOU0xJIGRlZ2lsc2UNCiANCmRpZSgnQnUgc2l0ZXllIGFpdCBsaXNhbnMgYnVsdW5hbWFkaS4gSG9zdGluZyBCaWxnaWxlcmluaXogU2l0ZSBTYWhpYmluZSBHb25kZXJpbG1pc3RpcicpOy8vbGlzYW5zIGdlY2Vyc2l6IG1lc2FqaSB2ZXJpbGlyIGlzbGVtIGR1cnVyDQogDQokbHlheiA9IGZvcGVuKCdsaXNhbnMucGhwJywidysiKTsvL2xpc2Fucy5waHAgZG9zeWFzaW5pIGFjIHllbmkgbGlzYW5zIGtvZHVudSB5YXogYnUgZG9zeWEgeWF6/WxhYmlsaXIgb2xtYWz9ZP1yDQpAZndyaXRlKCRseWF6LCc8P3BocA0KLyoqDQogICAgU29ydW5zdXogU2NyaXB0IExpc2FucyANCgkNCiAgICBCdSBkb3N5YXlhIGhlcmhhbmdpIGJpciBsaXNhbnMga29kdSB5YXptYW5peiBnZXJla21leg0KICAgIEFsbWlzIG9sZHVndW51eiB1cnVuZSBhaXQgbGlzYW5zIHNpc3RlbWRlIGFrdGlmIG9sZHVndW5kYQ0KICAgIExpc2FucyBrb2R1bnV6IG90b21hdGlrIG9sYXJhayBnaXJpbGVjZWt0aXIuDQoJDQoJWWVuaSBsaXNhbnMgYWxtYWsgaWNpbiB3d3cuc29ydW5zdXpzY3JpcHQuY29tIGFkcmVzaW5pIHppeWFyZXQgZWRlYmlsaXJzaW5peg0KIA0KICovDQokbGlzYW5za29kdT0iJy4kYmNzLiciOw0KPz4nKTsNCmZjbG9zZSgkbHlheik7DQp9DQoNCmRlZmluZSgnSU5TVEFXRUJfVkVSU0lPTicsIHN0cl9yZXBsYWNlKCJJbnN0YVdlYlYiLCAiIiwgYmFzZW5hbWUoX19GSUxFX18sICIucGhwIikpKTsNCiAgICAgICAgaWYoJF9TRVJWRVJbIkhUVFBfVVNFUl9BR0VOVCJdID09ICIiKSB7DQogICAgICAgICAgICBoZWFkZXIoIkhUVFAvMS4xIDQwMyBGb3JiaWRkZW4iKTsNCiAgICAgICAgICAgIGVjaG8gJ0Jha/1tIE1vZHUhJzsNCiAgICAgICAgICAgIGV4aXQoKTsNCiAgICAgICAgfQ0KDQpjbGFzcyBJbnN0YWdyYW0NCnsNCiAgICBwcm90ZWN0ZWQgJHVzZXJuYW1lOw0KICAgIHByb3RlY3RlZCAkcGFzc3dvcmQ7DQogICAgcHJvdGVjdGVkICRkZWJ1ZzsNCiAgICBwcm90ZWN0ZWQgJHV1aWQ7DQogICAgcHJvdGVjdGVkICRkZXZpY2VfaWQ7DQogICAgcHJvdGVjdGVkICR1c2VybmFtZV9pZDsNCi8qKg0KICAgICAgICAgKiBAdmFyIFNldHRpbmdzDQogICAgICAgICAqLw0KICAgIHB1YmxpYyAkc2V0dGluZ3M7DQogICAgcHJvdGVjdGVkICR0b2tlbjsNCiAgICBwcm90ZWN0ZWQgJGlzTG9nZ2VkSW4gPSBmYWxzZTsNCiAgICBwcm90ZWN0ZWQgJHJhbmtfdG9rZW47DQogICAgcHJvdGVjdGVkICRJR0RhdGFQYXRoOw0KDQogICAgLyoqDQogICAgICAgICAqIERlZmF1bHQgY2xhc3MgY29uc3RydWN0b3IuDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJHVzZXJuYW1lDQogICAgICAgICAqICAgWW91ciBJbnN0YWdyYW0gdXNlcm5hbWUuDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJHBhc3N3b3JkDQogICAgICAgICAqICAgWW91ciBJbnN0YWdyYW0gcGFzc3dvcmQuDQogICAgICAgICAqIEBwYXJhbSAgICAgICAgJGRlYnVnDQogICAgICAgICAqICAgRGVidWcgb24gb3Igb2ZmLCBmYWxzZSBieSBkZWZhdWx0Lg0KICAgICAgICAgKiBAcGFyYW0gICAgICAgICRJR0RhdGFQYXRoDQogICAgICAgICAqICAgRGVmYXVsdCBmb2xkZXIgdG8gc3RvcmUgZGF0YSwgeW91IGNhbiBjaGFuZ2UgaXQuDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIF9fY29uc3RydWN0KCR1c2VybmFtZSwgJHBhc3N3b3JkLCAkZGVidWcgPSBmYWxzZSwgJElHRGF0YVBhdGggPSBOVUxMLCAkZm9yY2VVc2VySVAgPSBmYWxzZSkNCiAgICB7DQogICAgICAgICR1c2VybmFtZSA9IHRyaW0oJHVzZXJuYW1lKTsNCiAgICAgICAgJHBhc3N3b3JkID0gdHJpbSgkcGFzc3dvcmQpOw0KICAgICAgICAkdGhpcy0+ZGVidWcgPSAkZGVidWc7DQogICAgICAgICR0aGlzLT5kZXZpY2VfaWQgPSBTaWduYXR1cmVVdGlsczo6Z2VuZXJhdGVEZXZpY2VJZChtZDUoJHVzZXJuYW1lIC4gJHBhc3N3b3JkKSk7DQogICAgICAgIGlmKCAhaXNfbnVsbCgkSUdEYXRhUGF0aCkgKSANCiAgICAgICAgew0KICAgICAgICAgICAgJHRoaXMtPklHRGF0YVBhdGggPSAkSUdEYXRhUGF0aDsNCiAgICAgICAgfQ0KICAgICAgICBlbHNlDQogICAgICAgIHsNCiAgICAgICAgICAgICR0aGlzLT5JR0RhdGFQYXRoID0gV293OjpnZXQoInByb2plY3QvY29va2llUGF0aCIpIC4gImluc3RhZ3JhbS8iOw0KICAgICAgICB9DQoNCiAgICAgICAgJHRoaXMtPnNldFVzZXIoJHVzZXJuYW1lLCAkcGFzc3dvcmQsICRmb3JjZVVzZXJJUCk7DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIFNldCB0aGUgdXNlci4gTWFuYWdlIG11bHRpcGxlIGFjY291bnRzLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICR1c2VybmFtZQ0KICAgICAgICAgKiAgIFlvdXIgSW5zdGFncmFtIHVzZXJuYW1lLg0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICRwYXNzd29yZA0KICAgICAgICAgKiAgIFlvdXIgSW5zdGFncmFtIHBhc3N3b3JkLg0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBzZXRVc2VyKCR1c2VybmFtZSwgJHBhc3N3b3JkLCAkZm9yY2VVc2VySVAgPSBmYWxzZSkNCiAgICB7DQogICAgICAgICR0aGlzLT51c2VybmFtZSA9ICR1c2VybmFtZTsNCiAgICAgICAgJHRoaXMtPnBhc3N3b3JkID0gJHBhc3N3b3JkOw0KICAgICAgICAkdGhpcy0+Y2hlY2tTZXR0aW5ncygkdXNlcm5hbWUsICRmb3JjZVVzZXJJUCk7DQogICAgICAgICR0aGlzLT51dWlkID0gU2lnbmF0dXJlVXRpbHM6OmdlbmVyYXRlVVVJRCh0cnVlKTsNCiAgICAgICAgaWYoIGZpbGVfZXhpc3RzKCR0aGlzLT5JR0RhdGFQYXRoIC4gKHN0cmluZykgJHRoaXMtPnVzZXJuYW1lIC4gIi5kYXQiKSAmJiAkdGhpcy0+c2V0dGluZ3MtPmdldCgidXNlcm5hbWVfaWQiKSAhPSBOVUxMICYmICR0aGlzLT5zZXR0aW5ncy0+Z2V0KCJ0b2tlbiIpICE9IE5VTEwgKSANCiAgICAgICAgew0KICAgICAgICAgICAgJHRoaXMtPmlzTG9nZ2VkSW4gPSB0cnVlOw0KICAgICAgICAgICAgJHRoaXMtPnVzZXJuYW1lX2lkID0gJHRoaXMtPnNldHRpbmdzLT5nZXQoInVzZXJuYW1lX2lkIik7DQogICAgICAgICAgICAkdGhpcy0+cmFua190b2tlbiA9ICR0aGlzLT51c2VybmFtZV9pZCAuICJfIiAuICR0aGlzLT51dWlkOw0KICAgICAgICAgICAgJHRoaXMtPnRva2VuID0gJHRoaXMtPnNldHRpbmdzLT5nZXQoInRva2VuIik7DQogICAgICAgIH0NCiAgICAgICAgZWxzZQ0KICAgICAgICB7DQogICAgICAgICAgICAkdGhpcy0+aXNMb2dnZWRJbiA9IGZhbHNlOw0KICAgICAgICB9DQoNCiAgICB9DQoNCiAgICBwcm90ZWN0ZWQgZnVuY3Rpb24gY2hlY2tTZXR0aW5ncygkdXNlcm5hbWUsICRmb3JjZVVzZXJJUCA9IGZhbHNlKQ0KICAgIHsNCiAgICAgICAgJHRoaXMtPnNldHRpbmdzID0gbmV3IFNldHRpbmdzKCR0aGlzLT5JR0RhdGFQYXRoIC4gJHVzZXJuYW1lIC4gIi5jbmYiKTsNCiAgICAgICAgaWYoICR0aGlzLT5zZXR0aW5ncy0+Z2V0KCJ2ZXJzaW9uIikgPT0gTlVMTCApIA0KICAgICAgICB7DQogICAgICAgICAgICAkdGhpcy0+c2V0dGluZ3MtPnNldCgidmVyc2lvbiIsIENvbnN0YW50czo6VkVSU0lPTik7DQogICAgICAgIH0NCg0KICAgICAgICBpZiggJHRoaXMtPnNldHRpbmdzLT5nZXQoImlwIikgPT0gTlVMTCB8fCAkZm9yY2VVc2VySVAgKSANCiAgICAgICAgew0KICAgICAgICAgICAgJGlwQWRyZXNzID0gIjc4LiIgLiByYW5kKDE2MCwgMTkxKSAuICIuIiAuIHJhbmQoMSwgMjU1KSAuICIuIiAuIHJhbmQoMSwgMjU1KTsNCiAgICAgICAgICAgIGlmKCAkZm9yY2VVc2VySVAgJiYgIWVtcHR5KCRfU0VSVkVSWyJSRU1PVEVfQUREUiJdKSApIA0KICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICRpcEFkcmVzcyA9ICRfU0VSVkVSWyJSRU1PVEVfQUREUiJdOw0KICAgICAgICAgICAgfQ0KDQogICAgICAgICAgICAkdGhpcy0+c2V0dGluZ3MtPnNldCgiaXAiLCAkaXBBZHJlc3MpOw0KICAgICAgICB9DQoNCiAgICAgICAgaWYoIGZpbGVfZXhpc3RzKCR0aGlzLT5JR0RhdGFQYXRoIC4gKHN0cmluZykgJHVzZXJuYW1lIC4gIi5kYXQiKSAmJiAoJHRoaXMtPnNldHRpbmdzLT5nZXQoInVzZXJuYW1lX2lkIikgPT0gTlVMTCB8fCAkdGhpcy0+c2V0dGluZ3MtPmdldCgidG9rZW4iKSA9PSBOVUxMKSApIA0KICAgICAgICB7DQogICAgICAgICAgICAkY29va2llRGF0YSA9IFV0aWxzOjpjb29raWVUb0FycmF5KGZpbGVfZ2V0X2NvbnRlbnRzKCR0aGlzLT5JR0RhdGFQYXRoIC4gKHN0cmluZykgJHVzZXJuYW1lIC4gIi5kYXQiKSwgImkuaW5zdGFncmFtLmNvbSIpOw0KICAgICAgICAgICAgaWYoICR0aGlzLT5zZXR0aW5ncy0+Z2V0KCJ1c2VybmFtZV9pZCIpID09IE5VTEwgJiYgaXNzZXQoJGNvb2tpZURhdGFbImRzX3VzZXJfaWQiXSkgKSANCiAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAkdGhpcy0+c2V0dGluZ3MtPnNldCgidXNlcm5hbWVfaWQiLCAkY29va2llRGF0YVsiZHNfdXNlcl9pZCJdKTsNCiAgICAgICAgICAgIH0NCg0KICAgICAgICAgICAgaWYoICR0aGlzLT5zZXR0aW5ncy0+Z2V0KCJ0b2tlbiIpID09IE5VTEwgJiYgaXNzZXQoJGNvb2tpZURhdGFbImNzcmZ0b2tlbiJdKSApIA0KICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICR0aGlzLT5zZXR0aW5ncy0+c2V0KCJ0b2tlbiIsICRjb29raWVEYXRhWyJjc3JmdG9rZW4iXSk7DQogICAgICAgICAgICB9DQoNCiAgICAgICAgfQ0KDQogICAgICAgIGlmKCAkdGhpcy0+c2V0dGluZ3MtPmdldCgidXNlcl9hZ2VudCIpID09IE5VTEwgfHwgdmVyc2lvbl9jb21wYXJlKCR0aGlzLT5zZXR0aW5ncy0+Z2V0KCJ2ZXJzaW9uIiksIENvbnN0YW50czo6VkVSU0lPTikgPT0gLTEgKSANCiAgICAgICAgew0KICAgICAgICAgICAgJHVzZXJBZ2VudCA9IG5ldyBVc2VyQWdlbnQoJHRoaXMpOw0KICAgICAgICAgICAgJHVhID0gJHVzZXJBZ2VudC0+YnVpbGRVc2VyQWdlbnQoKTsNCiAgICAgICAgICAgICR0aGlzLT5zZXR0aW5ncy0+c2V0KCJ2ZXJzaW9uIiwgQ29uc3RhbnRzOjpWRVJTSU9OKTsNCiAgICAgICAgICAgICR0aGlzLT5zZXR0aW5ncy0+c2V0KCJ1c2VyX2FnZW50IiwgJHVhKTsNCiAgICAgICAgfQ0KDQogICAgfQ0KDQogICAgcHVibGljIGZ1bmN0aW9uIGdldERhdGEoKQ0KICAgIHsNCiAgICAgICAgcmV0dXJuIGFycmF5KCAidXNlcm5hbWUiID0+ICR0aGlzLT51c2VybmFtZSwgInBhc3N3b3JkIiA9PiAkdGhpcy0+cGFzc3dvcmQsICJ1c2VybmFtZV9pZCIgPT4gJHRoaXMtPnVzZXJuYW1lX2lkLCAidXVpZCIgPT4gJHRoaXMtPnV1aWQsICJ0b2tlbiIgPT4gJHRoaXMtPnRva2VuLCAicmFua190b2tlbiIgPT4gJHRoaXMtPnJhbmtfdG9rZW4sICJ1c2VyX2FnZW50IiA9PiAkdGhpcy0+c2V0dGluZ3MtPmdldCgidXNlcl9hZ2VudCIpLCAiaXAiID0+ICR0aGlzLT5zZXR0aW5ncy0+Z2V0KCJpcCIpICk7DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIExvZ2luIHRvIEluc3RhZ3JhbS4NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIGJvb2wgJGZvcmNlDQogICAgICAgICAqICAgRm9yY2UgbG9naW4gdG8gSW5zdGFncmFtLCB0aGlzIHdpbGwgY3JlYXRlIGEgbmV3IHNlc3Npb24NCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgICBMb2dpbiBkYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGxvZ2luKCRmb3JjZSA9IGZhbHNlKQ0KICAgIHsNCiAgICAgICAgaWYoICEkdGhpcy0+aXNMb2dnZWRJbiB8fCAkZm9yY2UgKSANCiAgICAgICAgew0KICAgICAgICAgICAgJGZldGNoID0gJHRoaXMtPnJlcXVlc3QoInNpL2ZldGNoX2hlYWRlcnMvP2NoYWxsZW5nZV90eXBlPXNpZ251cCZndWlkPSIgLiBTaWduYXR1cmVVdGlsczo6Z2VuZXJhdGVVVUlEKGZhbHNlKSwgTlVMTCwgdHJ1ZSk7DQogICAgICAgICAgICBwcmVnX21hdGNoKCIjU2V0LUNvb2tpZTogY3NyZnRva2VuPShbXjtdKykjIiwgJGZldGNoWzBdLCAkdG9rZW4pOw0KICAgICAgICAgICAgJGRhdGEgPSBhcnJheSggInBob25lX2lkIiA9PiBTaWduYXR1cmVVdGlsczo6Z2VuZXJhdGVVVUlEKHRydWUpLCAiX2NzcmZ0b2tlbiIgPT4gJHRva2VuWzBdLCAidXNlcm5hbWUiID0+ICR0aGlzLT51c2VybmFtZSwgImd1aWQiID0+ICR0aGlzLT51dWlkLCAiZGV2aWNlX2lkIiA9PiAkdGhpcy0+ZGV2aWNlX2lkLCAicGFzc3dvcmQiID0+ICR0aGlzLT5wYXNzd29yZCwgImxvZ2luX2F0dGVtcHRfY291bnQiID0+ICIwIiApOw0KICAgICAgICAgICAgJGxvZ2luID0gJHRoaXMtPnJlcXVlc3QoImFjY291bnRzL2xvZ2luLyIsIFNpZ25hdHVyZVV0aWxzOjpnZW5lcmF0ZVNpZ25hdHVyZShqc29uX2VuY29kZSgkZGF0YSkpLCB0cnVlKTsNCiAgICAgICAgICAgIGlmKCAkbG9naW5bMV1bInN0YXR1cyJdID09ICJmYWlsIiApIA0KICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgIHRocm93IG5ldyBFeGNlcHRpb24oJGxvZ2luWzFdWyJtZXNzYWdlIl0pOw0KICAgICAgICAgICAgfQ0KDQogICAgICAgICAgICAkdGhpcy0+aXNMb2dnZWRJbiA9IHRydWU7DQogICAgICAgICAgICAkdGhpcy0+dXNlcm5hbWVfaWQgPSAkbG9naW5bMV1bImxvZ2dlZF9pbl91c2VyIl1bInBrIl07DQogICAgICAgICAgICAkdGhpcy0+c2V0dGluZ3MtPnNldCgidXNlcm5hbWVfaWQiLCAkdGhpcy0+dXNlcm5hbWVfaWQpOw0KICAgICAgICAgICAgJHRoaXMtPnJhbmtfdG9rZW4gPSAkdGhpcy0+dXNlcm5hbWVfaWQgLiAiXyIgLiAkdGhpcy0+dXVpZDsNCiAgICAgICAgICAgIHByZWdfbWF0Y2goIiNTZXQtQ29va2llOiBjc3JmdG9rZW49KFteO10rKSMiLCAkbG9naW5bMF0sICRtYXRjaCk7DQogICAgICAgICAgICAkdGhpcy0+dG9rZW4gPSAkbWF0Y2hbMV07DQogICAgICAgICAgICAkdGhpcy0+c2V0dGluZ3MtPnNldCgidG9rZW4iLCAkdGhpcy0+dG9rZW4pOw0KICAgICAgICAgICAgJHRoaXMtPnN5bmNGZWF0dXJlcygpOw0KICAgICAgICAgICAgJHRoaXMtPm1lZ2FwaG9uZUxvZygpOw0KICAgICAgICAgICAgcmV0dXJuICRsb2dpblsxXTsNCiAgICAgICAgfQ0KDQogICAgICAgICRjaGVjayA9ICR0aGlzLT50aW1lbGluZUZlZWQoKTsNCiAgICAgICAgaWYoIGlzc2V0KCRjaGVja1sibWVzc2FnZSJdKSAmJiAkY2hlY2tbIm1lc3NhZ2UiXSA9PSAibG9naW5fcmVxdWlyZWQiICkgDQogICAgICAgIHsNCiAgICAgICAgICAgIHJldHVybiAkdGhpcy0+bG9naW4odHJ1ZSk7DQogICAgICAgIH0NCg0KICAgICAgICAkdGhpcy0+bWVnYXBob25lTG9nKCk7DQogICAgICAgIHJldHVybiBhcnJheSggInN0YXR1cyIgPT4gIm9rIiApOw0KICAgIH0NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBzeW5jRmVhdHVyZXMoKQ0KICAgIHsNCiAgICAgICAgJGRhdGEgPSBqc29uX2VuY29kZShhcnJheSggIl91dWlkIiA9PiAkdGhpcy0+dXVpZCwgIl91aWQiID0+ICR0aGlzLT51c2VybmFtZV9pZCwgImlkIiA9PiAkdGhpcy0+dXNlcm5hbWVfaWQsICJfY3NyZnRva2VuIiA9PiAkdGhpcy0+dG9rZW4sICJleHBlcmltZW50cyIgPT4gQ29uc3RhbnRzOjpFWFBFUklNRU5UUyApKTsNCiAgICAgICAgcmV0dXJuICR0aGlzLT5yZXF1ZXN0KCJxZS9zeW5jLyIsIFNpZ25hdHVyZVV0aWxzOjpnZW5lcmF0ZVNpZ25hdHVyZSgkZGF0YSkpWzFdOw0KICAgIH0NCg0KICAgIHByb3RlY3RlZCBmdW5jdGlvbiBhdXRvQ29tcGxldGVVc2VyTGlzdCgpDQogICAgew0KICAgICAgICByZXR1cm4gJHRoaXMtPnJlcXVlc3QoImZyaWVuZHNoaXBzL2F1dG9jb21wbGV0ZV91c2VyX2xpc3QvIilbMV07DQogICAgfQ0KDQogICAgcHJvdGVjdGVkIGZ1bmN0aW9uIHRpbWVsaW5lRmVlZCgpDQogICAgew0KICAgICAgICByZXR1cm4gJHRoaXMtPnJlcXVlc3QoImZlZWQvdGltZWxpbmUvIilbMV07DQogICAgfQ0KDQogICAgcHJvdGVjdGVkIGZ1bmN0aW9uIG1lZ2FwaG9uZUxvZygpDQogICAgew0KICAgICAgICByZXR1cm4gJHRoaXMtPnJlcXVlc3QoIm1lZ2FwaG9uZS9sb2cvIilbMV07DQogICAgfQ0KDQogICAgcHJvdGVjdGVkIGZ1bmN0aW9uIGV4cG9zZSgpDQogICAgew0KICAgICAgICAkZGF0YSA9IGpzb25fZW5jb2RlKGFycmF5KCAiX3V1aWQiID0+ICR0aGlzLT51dWlkLCAiX3VpZCIgPT4gJHRoaXMtPnVzZXJuYW1lX2lkLCAiaWQiID0+ICR0aGlzLT51c2VybmFtZV9pZCwgIl9jc3JmdG9rZW4iID0+ICR0aGlzLT50b2tlbiwgImV4cGVyaW1lbnQiID0+ICJpZ19hbmRyb2lkX3Byb2ZpbGVfY29udGV4dHVhbF9mZWVkIiApKTsNCiAgICAgICAgJHRoaXMtPnJlcXVlc3QoInFlL2V4cG9zZS8iLCBTaWduYXR1cmVVdGlsczo6Z2VuZXJhdGVTaWduYXR1cmUoJGRhdGEpKTsNCiAgICAgICAgJHRoaXMtPnJlcXVlc3QoInFlL2V4cG9zZS8iLCBTaWduYXR1cmVVdGlsczo6Z2VuZXJhdGVTaWduYXR1cmUoJGRhdGEpKVsxXTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogTG9naW4gdG8gSW5zdGFncmFtLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGJvb2wNCiAgICAgICAgICogICAgUmV0dXJucyB0cnVlIGlmIGxvZ2dlZCBvdXQgY29ycmVjdGx5DQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGxvZ291dCgpDQogICAgew0KICAgICAgICAkbG9nb3V0ID0gJHRoaXMtPnJlcXVlc3QoImFjY291bnRzL2xvZ291dC8iKTsNCiAgICAgICAgaWYoICRsb2dvdXQgPT0gIm9rIiApIA0KICAgICAgICB7DQogICAgICAgICAgICByZXR1cm4gdHJ1ZTsNCiAgICAgICAgfQ0KDQogICAgICAgIHJldHVybiBmYWxzZTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogVXBsb2FkIHBob3RvIHRvIEluc3RhZ3JhbS4NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkcGhvdG8NCiAgICAgICAgICogICAgICAgICAgICAgICAgICAgICAgICBQYXRoIHRvIHlvdXIgcGhvdG8NCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkY2FwdGlvbg0KICAgICAgICAgKiAgICAgICAgICAgICAgICAgICAgICAgIENhcHRpb24gdG8gYmUgaW5jbHVkZWQgaW4geW91ciBwaG90by4NCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgICAgICAgICAgICAgIFVwbG9hZCBkYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIHVwbG9hZFBob3RvKCRwaG90bywgJGNhcHRpb24gPSBOVUxMLCAkdXBsb2FkX2lkID0gTlVMTCkNCiAgICB7DQogICAgICAgICRlbmRwb2ludCA9IENvbnN0YW50czo6QVBJX1VSTCAuICJ1cGxvYWQvcGhvdG8vIjsNCiAgICAgICAgJGJvdW5kYXJ5ID0gJHRoaXMtPnV1aWQ7DQogICAgICAgIGlmKCAhaXNfbnVsbCgkdXBsb2FkX2lkKSApIA0KICAgICAgICB7DQogICAgICAgICAgICAkZmlsZVRvVXBsb2FkID0gVXRpbHM6OmNyZWF0ZVZpZGVvSWNvbigkcGhvdG8pOw0KICAgICAgICB9DQogICAgICAgIGVsc2UNCiAgICAgICAgew0KICAgICAgICAgICAgJHVwbG9hZF9pZCA9IG51bWJlcl9mb3JtYXQocm91bmQobWljcm90aW1lKHRydWUpICogMTAwMCksIDAsICIiLCAiIik7DQogICAgICAgICAgICAkZmlsZVRvVXBsb2FkID0gZmlsZV9nZXRfY29udGVudHMoJHBob3RvKTsNCiAgICAgICAgfQ0KDQogICAgICAgICRib2RpZXMgPSBhcnJheSggYXJyYXkoICJ0eXBlIiA9PiAiZm9ybS1kYXRhIiwgIm5hbWUiID0+ICJ1cGxvYWRfaWQiLCAiZGF0YSIgPT4gJHVwbG9hZF9pZCApLCBhcnJheSggInR5cGUiID0+ICJmb3JtLWRhdGEiLCAibmFtZSIgPT4gIl91dWlkIiwgImRhdGEiID0+ICR0aGlzLT51dWlkICksIGFycmF5KCAidHlwZSIgPT4gImZvcm0tZGF0YSIsICJuYW1lIiA9PiAiX2NzcmZ0b2tlbiIsICJkYXRhIiA9PiAkdGhpcy0+dG9rZW4gKSwgYXJyYXkoICJ0eXBlIiA9PiAiZm9ybS1kYXRhIiwgIm5hbWUiID0+ICJpbWFnZV9jb21wcmVzc2lvbiIsICJkYXRhIiA9PiAie1wibGliX25hbWVcIjpcImp0XCIsXCJsaWJfdmVyc2lvblwiOlwiMS4zLjBcIixcInF1YWxpdHlcIjpcIjcwXCJ9IiApLCBhcnJheSggInR5cGUiID0+ICJmb3JtLWRhdGEiLCAibmFtZSIgPT4gInBob3RvIiwgImRhdGEiID0+ICRmaWxlVG9VcGxvYWQsICJmaWxlbmFtZSIgPT4gInBlbmRpbmdfbWVkaWFfIiAuIG51bWJlcl9mb3JtYXQocm91bmQobWljcm90aW1lKHRydWUpICogMTAwMCksIDAsICIiLCAiIikgLiAiLmpwZyIsICJoZWFkZXJzIiA9PiBhcnJheSggIkNvbnRlbnQtVHJhbnNmZXItRW5jb2Rpbmc6IGJpbmFyeSIsICJDb250ZW50LXR5cGU6IGFwcGxpY2F0aW9uL29jdGV0LXN0cmVhbSIgKSApICk7DQogICAgICAgICRkYXRhID0gJHRoaXMtPmJ1aWxkQm9keSgkYm9kaWVzLCAkYm91bmRhcnkpOw0KICAgICAgICAkaGVhZGVycyA9IGFycmF5KCAiWC1JRy1DYXBhYmlsaXRpZXM6ICIgLiBDb25zdGFudHM6OlhfSUdfQ2FwYWJpbGl0aWVzLCAiWC1JRy1Db25uZWN0aW9uLVR5cGU6IFdJRkkiLCAiQ29udGVudC10eXBlOiBtdWx0aXBhcnQvZm9ybS1kYXRhOyBib3VuZGFyeT0iIC4gJGJvdW5kYXJ5LCAiQ29udGVudC1MZW5ndGg6ICIgLiBzdHJsZW4oJGRhdGEpLCAiQWNjZXB0LUxhbmd1YWdlOiB0ci1UUiIsICJBY2NlcHQtRW5jb2Rpbmc6IGd6aXAsIGRlZmxhdGUiLCAiQ29ubmVjdGlvbjogY2xvc2UiLCAiWF9GT1JXQVJERURfRk9SOiAiIC4gJHRoaXMtPnNldHRpbmdzLT5nZXQoImlwIiksICJSRU1PVEVfQUREUjogIiAuICR0aGlzLT5zZXR0aW5ncy0+Z2V0KCJpcCIpICk7DQogICAgICAgICRjaCA9IGN1cmxfaW5pdCgpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfVVJMLCAkZW5kcG9pbnQpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfVVNFUkFHRU5ULCAkdGhpcy0+c2V0dGluZ3MtPmdldCgidXNlcl9hZ2VudCIpKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1JFVFVSTlRSQU5TRkVSLCB0cnVlKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX0ZPTExPV0xPQ0FUSU9OLCB0cnVlKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX0hFQURFUiwgdHJ1ZSk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9WRVJCT1NFLCAkdGhpcy0+ZGVidWcpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfU1NMX1ZFUklGWVBFRVIsIGZhbHNlKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1NTTF9WRVJJRllIT1NULCBmYWxzZSk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9IVFRQSEVBREVSLCAkaGVhZGVycyk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9DT09LSUVGSUxFLCAkdGhpcy0+SUdEYXRhUGF0aCAuIChzdHJpbmcpICR0aGlzLT51c2VybmFtZSAuICIuZGF0Iik7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9DT09LSUVKQVIsICR0aGlzLT5JR0RhdGFQYXRoIC4gKHN0cmluZykgJHRoaXMtPnVzZXJuYW1lIC4gIi5kYXQiKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1BPU1QsIHRydWUpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfUE9TVEZJRUxEUywgJGRhdGEpOw0KICAgICAgICAkcmVzcCA9IGN1cmxfZXhlYygkY2gpOw0KICAgICAgICAkaGVhZGVyX2xlbiA9IGN1cmxfZ2V0aW5mbygkY2gsIENVUkxJTkZPX0hFQURFUl9TSVpFKTsNCiAgICAgICAgJGhlYWRlciA9IHN1YnN0cigkcmVzcCwgMCwgJGhlYWRlcl9sZW4pOw0KICAgICAgICAkdXBsb2FkID0ganNvbl9kZWNvZGUoc3Vic3RyKCRyZXNwLCAkaGVhZGVyX2xlbiksIHRydWUpOw0KICAgICAgICBjdXJsX2Nsb3NlKCRjaCk7DQogICAgICAgIGlmKCAkdXBsb2FkWyJzdGF0dXMiXSA9PSAiZmFpbCIgKSANCiAgICAgICAgew0KICAgICAgICAgICAgdGhyb3cgbmV3IEV4Y2VwdGlvbigkdXBsb2FkWyJtZXNzYWdlIl0pOw0KICAgICAgICB9DQoNCiAgICAgICAgaWYoICR0aGlzLT5kZWJ1ZyApIA0KICAgICAgICB7DQogICAgICAgICAgICBlY2hvICJSRVNQT05TRTogIiAuIHN1YnN0cigkcmVzcCwgJGhlYWRlcl9sZW4pIC4gIlxuXG4iOw0KICAgICAgICB9DQoNCiAgICAgICAgJGNvbmZpZ3VyZSA9ICR0aGlzLT5jb25maWd1cmUoJHVwbG9hZFsidXBsb2FkX2lkIl0sICRwaG90bywgJGNhcHRpb24pOw0KICAgICAgICAkdGhpcy0+ZXhwb3NlKCk7DQogICAgICAgIHJldHVybiAkY29uZmlndXJlOw0KICAgIH0NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiB1cGxvYWRWaWRlbygkdmlkZW8sICRjYXB0aW9uID0gTlVMTCkNCiAgICB7DQogICAgICAgICR2aWRlb0RhdGEgPSBmaWxlX2dldF9jb250ZW50cygkdmlkZW8pOw0KICAgICAgICAkZW5kcG9pbnQgPSBDb25zdGFudHM6OkFQSV9VUkwgLiAidXBsb2FkL3ZpZGVvLyI7DQogICAgICAgICRib3VuZGFyeSA9ICR0aGlzLT51dWlkOw0KICAgICAgICAkdXBsb2FkX2lkID0gcm91bmQobWljcm90aW1lKHRydWUpICogMTAwMCk7DQogICAgICAgICRib2RpZXMgPSBhcnJheSggYXJyYXkoICJ0eXBlIiA9PiAiZm9ybS1kYXRhIiwgIm5hbWUiID0+ICJ1cGxvYWRfaWQiLCAiZGF0YSIgPT4gJHVwbG9hZF9pZCApLCBhcnJheSggInR5cGUiID0+ICJmb3JtLWRhdGEiLCAibmFtZSIgPT4gIl9jc3JmdG9rZW4iLCAiZGF0YSIgPT4gJHRoaXMtPnRva2VuICksIGFycmF5KCAidHlwZSIgPT4gImZvcm0tZGF0YSIsICJuYW1lIiA9PiAibWVkaWFfdHlwZSIsICJkYXRhIiA9PiAiMiIgKSwgYXJyYXkoICJ0eXBlIiA9PiAiZm9ybS1kYXRhIiwgIm5hbWUiID0+ICJfdXVpZCIsICJkYXRhIiA9PiAkdGhpcy0+dXVpZCApICk7DQogICAgICAgICRkYXRhID0gJHRoaXMtPmJ1aWxkQm9keSgkYm9kaWVzLCAkYm91bmRhcnkpOw0KICAgICAgICAkaGVhZGVycyA9IGFycmF5KCAiQ29ubmVjdGlvbjoga2VlcC1hbGl2ZSIsICJBY2NlcHQ6ICovKiIsICJIb3N0OiBpLmluc3RhZ3JhbS5jb20iLCAiQ29udGVudC10eXBlOiBtdWx0aXBhcnQvZm9ybS1kYXRhOyBib3VuZGFyeT0iIC4gJGJvdW5kYXJ5LCAiQWNjZXB0LUxhbmd1YWdlOiB0ci1UUiIsICJYX0ZPUldBUkRFRF9GT1I6ICIgLiAkdGhpcy0+c2V0dGluZ3MtPmdldCgiaXAiKSwgIlJFTU9URV9BRERSOiAiIC4gJHRoaXMtPnNldHRpbmdzLT5nZXQoImlwIikgKTsNCiAgICAgICAgJGNoID0gY3VybF9pbml0KCk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9VUkwsICRlbmRwb2ludCk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9VU0VSQUdFTlQsICR0aGlzLT5zZXR0aW5ncy0+Z2V0KCJ1c2VyX2FnZW50IikpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfUkVUVVJOVFJBTlNGRVIsIHRydWUpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfRk9MTE9XTE9DQVRJT04sIHRydWUpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfSEVBREVSLCB0cnVlKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1ZFUkJPU0UsIGZhbHNlKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX0hUVFBIRUFERVIsICRoZWFkZXJzKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX0NPT0tJRUZJTEUsICR0aGlzLT5JR0RhdGFQYXRoIC4gKHN0cmluZykgJHRoaXMtPnVzZXJuYW1lIC4gIi5kYXQiKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX0NPT0tJRUpBUiwgJHRoaXMtPklHRGF0YVBhdGggLiAoc3RyaW5nKSAkdGhpcy0+dXNlcm5hbWUgLiAiLmRhdCIpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfUE9TVCwgdHJ1ZSk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9QT1NURklFTERTLCAkZGF0YSk7DQogICAgICAgICRyZXNwID0gY3VybF9leGVjKCRjaCk7DQogICAgICAgICRoZWFkZXJfbGVuID0gY3VybF9nZXRpbmZvKCRjaCwgQ1VSTElORk9fSEVBREVSX1NJWkUpOw0KICAgICAgICAkYm9keSA9IGpzb25fZGVjb2RlKHN1YnN0cigkcmVzcCwgJGhlYWRlcl9sZW4pLCB0cnVlKTsNCiAgICAgICAgJHVwbG9hZFVybCA9ICRib2R5WyJ2aWRlb191cGxvYWRfdXJscyJdWzNdWyJ1cmwiXTsNCiAgICAgICAgJGpvYiA9ICRib2R5WyJ2aWRlb191cGxvYWRfdXJscyJdWzNdWyJqb2IiXTsNCiAgICAgICAgJHJlcXVlc3Rfc2l6ZSA9IGZsb29yKHN0cmxlbigkdmlkZW9EYXRhKSAvIDQpOw0KICAgICAgICAkbGFzdFJlcXVlc3RFeHRyYSA9IHN0cmxlbigkdmlkZW9EYXRhKSAtICRyZXF1ZXN0X3NpemUgKiA0Ow0KICAgICAgICBmb3IoICRhID0gMDsgJGEgPD0gMzsgJGErKyApIA0KICAgICAgICB7DQogICAgICAgICAgICAkc3RhcnQgPSAkYSAqICRyZXF1ZXN0X3NpemU7DQogICAgICAgICAgICAkZW5kID0gKCRhICsgMSkgKiAkcmVxdWVzdF9zaXplICsgKCgkYSA9PSAzID8gJGxhc3RSZXF1ZXN0RXh0cmEgOiAwKSk7DQogICAgICAgICAgICAkaGVhZGVycyA9IGFycmF5KCAiQ29ubmVjdGlvbjoga2VlcC1hbGl2ZSIsICJBY2NlcHQ6ICovKiIsICJIb3N0OiB1cGxvYWQuaW5zdGFncmFtLmNvbSIsICJDb29raWUyOiBcJFZlcnNpb249MSIsICJBY2NlcHQtRW5jb2Rpbmc6IGd6aXAsIGRlZmxhdGUiLCAiQ29udGVudC1UeXBlOiBhcHBsaWNhdGlvbi9vY3RldC1zdHJlYW0iLCAiU2Vzc2lvbi1JRDogIiAuICR1cGxvYWRfaWQsICJBY2NlcHQtTGFuZ3VhZ2U6IHRyLVRSIiwgIkNvbnRlbnQtRGlzcG9zaXRpb246IGF0dGFjaG1lbnQ7IGZpbGVuYW1lPVwidmlkZW8ubW92XCIiLCAiQ29udGVudC1MZW5ndGg6ICIgLiAoJGVuZCAtICRzdGFydCksICJDb250ZW50LVJhbmdlOiAiIC4gImJ5dGVzICIgLiAkc3RhcnQgLiAiLSIgLiAoJGVuZCAtIDEpIC4gIi8iIC4gc3RybGVuKCR2aWRlb0RhdGEpLCAiam9iOiAiIC4gJGpvYiApOw0KICAgICAgICAgICAgJGNoID0gY3VybF9pbml0KCk7DQogICAgICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfVVJMLCAkdXBsb2FkVXJsKTsNCiAgICAgICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9VU0VSQUdFTlQsICR0aGlzLT5zZXR0aW5ncy0+Z2V0KCJ1c2VyX2FnZW50IikpOw0KICAgICAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX0NVU1RPTVJFUVVFU1QsICJQT1NUIik7DQogICAgICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfUkVUVVJOVFJBTlNGRVIsIHRydWUpOw0KICAgICAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX0ZPTExPV0xPQ0FUSU9OLCB0cnVlKTsNCiAgICAgICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9IRUFERVIsIHRydWUpOw0KICAgICAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1ZFUkJPU0UsIGZhbHNlKTsNCiAgICAgICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9IVFRQSEVBREVSLCAkaGVhZGVycyk7DQogICAgICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfQ09PS0lFRklMRSwgJHRoaXMtPklHRGF0YVBhdGggLiAoc3RyaW5nKSAkdGhpcy0+dXNlcm5hbWUgLiAiLmRhdCIpOw0KICAgICAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX0NPT0tJRUpBUiwgJHRoaXMtPklHRGF0YVBhdGggLiAoc3RyaW5nKSAkdGhpcy0+dXNlcm5hbWUgLiAiLmRhdCIpOw0KICAgICAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1BPU1QsIHRydWUpOw0KICAgICAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1BPU1RGSUVMRFMsIHN1YnN0cigkdmlkZW9EYXRhLCAkc3RhcnQsICRlbmQpKTsNCiAgICAgICAgICAgICRyZXN1bHQgPSBjdXJsX2V4ZWMoJGNoKTsNCiAgICAgICAgICAgICRoZWFkZXJfbGVuID0gY3VybF9nZXRpbmZvKCRjaCwgQ1VSTElORk9fSEVBREVSX1NJWkUpOw0KICAgICAgICAgICAgJGJvZHkgPSBzdWJzdHIoJHJlc3VsdCwgJGhlYWRlcl9sZW4pOw0KICAgICAgICAgICAgJGFycmF5W10gPSBhcnJheSggJGJvZHkgKTsNCiAgICAgICAgfQ0KICAgICAgICAkcmVzcCA9IGN1cmxfZXhlYygkY2gpOw0KICAgICAgICAkaGVhZGVyX2xlbiA9IGN1cmxfZ2V0aW5mbygkY2gsIENVUkxJTkZPX0hFQURFUl9TSVpFKTsNCiAgICAgICAgJGhlYWRlciA9IHN1YnN0cigkcmVzcCwgMCwgJGhlYWRlcl9sZW4pOw0KICAgICAgICAkdXBsb2FkID0ganNvbl9kZWNvZGUoc3Vic3RyKCRyZXNwLCAkaGVhZGVyX2xlbiksIHRydWUpOw0KICAgICAgICBjdXJsX2Nsb3NlKCRjaCk7DQogICAgICAgIGlmKCAkdXBsb2FkWyJzdGF0dXMiXSA9PSAiZmFpbCIgKSANCiAgICAgICAgew0KICAgICAgICAgICAgdGhyb3cgbmV3IEV4Y2VwdGlvbigkdXBsb2FkWyJtZXNzYWdlIl0pOw0KICAgICAgICB9DQoNCiAgICAgICAgaWYoICR0aGlzLT5kZWJ1ZyApIA0KICAgICAgICB7DQogICAgICAgICAgICBlY2hvICJSRVNQT05TRTogIiAuIHN1YnN0cigkcmVzcCwgJGhlYWRlcl9sZW4pIC4gIlxuXG4iOw0KICAgICAgICB9DQoNCiAgICAgICAgJGNvbmZpZ3VyZSA9ICR0aGlzLT5jb25maWd1cmVWaWRlbygkdXBsb2FkX2lkLCAkdmlkZW8sICRjYXB0aW9uKTsNCiAgICAgICAgJHRoaXMtPmV4cG9zZSgpOw0KICAgICAgICByZXR1cm4gJGNvbmZpZ3VyZTsNCiAgICB9DQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gZGlyZWN0X21lc3NhZ2UoJHJlY2lwaWVudHMsICR0ZXh0KQ0KICAgIHsNCiAgICAgICAgaWYoICFpc19hcnJheSgkcmVjaXBpZW50cykgKSANCiAgICAgICAgew0KICAgICAgICAgICAgJHJlY2lwaWVudHMgPSBhcnJheSggJHJlY2lwaWVudHMgKTsNCiAgICAgICAgfQ0KDQogICAgICAgICRzdHJpbmcgPSBhcnJheSggICk7DQogICAgICAgIGZvcmVhY2goICRyZWNpcGllbnRzIGFzICRyZWNpcGllbnQgKSANCiAgICAgICAgew0KICAgICAgICAgICAgJHN0cmluZ1tdID0gIlwiIiAuICRyZWNpcGllbnQgLiAiXCIiOw0KICAgICAgICB9DQogICAgICAgICRyZWNpcGllbnRfdXNlcnMgPSBpbXBsb2RlKCIsIiwgJHN0cmluZyk7DQogICAgICAgICRlbmRwb2ludCA9IENvbnN0YW50czo6QVBJX1VSTCAuICJkaXJlY3RfdjIvdGhyZWFkcy9icm9hZGNhc3QvdGV4dC8iOw0KICAgICAgICAkYm91bmRhcnkgPSAkdGhpcy0+dXVpZDsNCiAgICAgICAgJGJvZGllcyA9IGFycmF5KCBhcnJheSggInR5cGUiID0+ICJmb3JtLWRhdGEiLCAibmFtZSIgPT4gInJlY2lwaWVudF91c2VycyIsICJkYXRhIiA9PiAiW1siIC4gJHJlY2lwaWVudF91c2VycyAuICJdXSIgKSwgYXJyYXkoICJ0eXBlIiA9PiAiZm9ybS1kYXRhIiwgIm5hbWUiID0+ICJjbGllbnRfY29udGV4dCIsICJkYXRhIiA9PiAkdGhpcy0+dXVpZCApLCBhcnJheSggInR5cGUiID0+ICJmb3JtLWRhdGEiLCAibmFtZSIgPT4gInRocmVhZF9pZHMiLCAiZGF0YSIgPT4gIltcIjBcIl0iICksIGFycmF5KCAidHlwZSIgPT4gImZvcm0tZGF0YSIsICJuYW1lIiA9PiAidGV4dCIsICJkYXRhIiA9PiAoaXNfbnVsbCgkdGV4dCkgPyAiIiA6ICR0ZXh0KSApICk7DQogICAgICAgICRkYXRhID0gJHRoaXMtPmJ1aWxkQm9keSgkYm9kaWVzLCAkYm91bmRhcnkpOw0KICAgICAgICAkaGVhZGVycyA9IGFycmF5KCAiUHJveHktQ29ubmVjdGlvbjoga2VlcC1hbGl2ZSIsICJDb25uZWN0aW9uOiBrZWVwLWFsaXZlIiwgIkFjY2VwdDogKi8qIiwgIkNvbnRlbnQtdHlwZTogbXVsdGlwYXJ0L2Zvcm0tZGF0YTsgYm91bmRhcnk9IiAuICRib3VuZGFyeSwgIkFjY2VwdC1MYW5ndWFnZTogdHItVFIiICk7DQogICAgICAgICRjaCA9IGN1cmxfaW5pdCgpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfVVJMLCAkZW5kcG9pbnQpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfVVNFUkFHRU5ULCAkdGhpcy0+c2V0dGluZ3MtPmdldCgidXNlcl9hZ2VudCIpKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1JFVFVSTlRSQU5TRkVSLCB0cnVlKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX0ZPTExPV0xPQ0FUSU9OLCB0cnVlKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX0hFQURFUiwgdHJ1ZSk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9WRVJCT1NFLCAkdGhpcy0+ZGVidWcpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfU1NMX1ZFUklGWVBFRVIsIGZhbHNlKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1NTTF9WRVJJRllIT1NULCBmYWxzZSk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9IVFRQSEVBREVSLCAkaGVhZGVycyk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9DT09LSUVGSUxFLCAkdGhpcy0+SUdEYXRhUGF0aCAuIChzdHJpbmcpICR0aGlzLT51c2VybmFtZSAuICIuZGF0Iik7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9DT09LSUVKQVIsICR0aGlzLT5JR0RhdGFQYXRoIC4gKHN0cmluZykgJHRoaXMtPnVzZXJuYW1lIC4gIi5kYXQiKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1BPU1QsIHRydWUpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfUE9TVEZJRUxEUywgJGRhdGEpOw0KICAgICAgICAkcmVzcCA9IGN1cmxfZXhlYygkY2gpOw0KICAgICAgICAkaGVhZGVyX2xlbiA9IGN1cmxfZ2V0aW5mbygkY2gsIENVUkxJTkZPX0hFQURFUl9TSVpFKTsNCiAgICAgICAgJGhlYWRlciA9IHN1YnN0cigkcmVzcCwgMCwgJGhlYWRlcl9sZW4pOw0KICAgICAgICAkdXBsb2FkID0ganNvbl9kZWNvZGUoc3Vic3RyKCRyZXNwLCAkaGVhZGVyX2xlbiksIHRydWUpOw0KICAgICAgICBjdXJsX2Nsb3NlKCRjaCk7DQogICAgfQ0KDQogICAgcHVibGljIGZ1bmN0aW9uIGRpcmVjdF9zaGFyZSgkbWVkaWFfaWQsICRyZWNpcGllbnRzLCAkdGV4dCA9IE5VTEwpDQogICAgew0KICAgICAgICBpZiggIWlzX2FycmF5KCRyZWNpcGllbnRzKSApIA0KICAgICAgICB7DQogICAgICAgICAgICAkcmVjaXBpZW50cyA9IGFycmF5KCAkcmVjaXBpZW50cyApOw0KICAgICAgICB9DQoNCiAgICAgICAgJHN0cmluZyA9IGFycmF5KCAgKTsNCiAgICAgICAgZm9yZWFjaCggJHJlY2lwaWVudHMgYXMgJHJlY2lwaWVudCApIA0KICAgICAgICB7DQogICAgICAgICAgICAkc3RyaW5nW10gPSAiXCIiIC4gJHJlY2lwaWVudCAuICJcIiI7DQogICAgICAgIH0NCiAgICAgICAgJHJlY2lwaWVudF91c2VycyA9IGltcGxvZGUoIiwiLCAkc3RyaW5nKTsNCiAgICAgICAgJGVuZHBvaW50ID0gQ29uc3RhbnRzOjpBUElfVVJMIC4gImRpcmVjdF92Mi90aHJlYWRzL2Jyb2FkY2FzdC9tZWRpYV9zaGFyZS8/bWVkaWFfdHlwZT1waG90byI7DQogICAgICAgICRib3VuZGFyeSA9ICR0aGlzLT51dWlkOw0KICAgICAgICAkYm9kaWVzID0gYXJyYXkoIGFycmF5KCAidHlwZSIgPT4gImZvcm0tZGF0YSIsICJuYW1lIiA9PiAibWVkaWFfaWQiLCAiZGF0YSIgPT4gJG1lZGlhX2lkICksIGFycmF5KCAidHlwZSIgPT4gImZvcm0tZGF0YSIsICJuYW1lIiA9PiAicmVjaXBpZW50X3VzZXJzIiwgImRhdGEiID0+ICJbWyIgLiAkcmVjaXBpZW50X3VzZXJzIC4gIl1dIiApLCBhcnJheSggInR5cGUiID0+ICJmb3JtLWRhdGEiLCAibmFtZSIgPT4gImNsaWVudF9jb250ZXh0IiwgImRhdGEiID0+ICR0aGlzLT51dWlkICksIGFycmF5KCAidHlwZSIgPT4gImZvcm0tZGF0YSIsICJuYW1lIiA9PiAidGhyZWFkX2lkcyIsICJkYXRhIiA9PiAiW1wiMFwiXSIgKSwgYXJyYXkoICJ0eXBlIiA9PiAiZm9ybS1kYXRhIiwgIm5hbWUiID0+ICJ0ZXh0IiwgImRhdGEiID0+IChpc19udWxsKCR0ZXh0KSA/ICIiIDogJHRleHQpICkgKTsNCiAgICAgICAgJGRhdGEgPSAkdGhpcy0+YnVpbGRCb2R5KCRib2RpZXMsICRib3VuZGFyeSk7DQogICAgICAgICRoZWFkZXJzID0gYXJyYXkoICJQcm94eS1Db25uZWN0aW9uOiBrZWVwLWFsaXZlIiwgIkNvbm5lY3Rpb246IGtlZXAtYWxpdmUiLCAiQWNjZXB0OiAqLyoiLCAiQ29udGVudC10eXBlOiBtdWx0aXBhcnQvZm9ybS1kYXRhOyBib3VuZGFyeT0iIC4gJGJvdW5kYXJ5LCAiQWNjZXB0LUxhbmd1YWdlOiB0ci1UUiIgKTsNCiAgICAgICAgJGNoID0gY3VybF9pbml0KCk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9VUkwsICRlbmRwb2ludCk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9VU0VSQUdFTlQsICR0aGlzLT5zZXR0aW5ncy0+Z2V0KCJ1c2VyX2FnZW50IikpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfUkVUVVJOVFJBTlNGRVIsIHRydWUpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfRk9MTE9XTE9DQVRJT04sIHRydWUpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfSEVBREVSLCB0cnVlKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1ZFUkJPU0UsICR0aGlzLT5kZWJ1Zyk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9TU0xfVkVSSUZZUEVFUiwgZmFsc2UpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfU1NMX1ZFUklGWUhPU1QsIGZhbHNlKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX0hUVFBIRUFERVIsICRoZWFkZXJzKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX0NPT0tJRUZJTEUsICR0aGlzLT5JR0RhdGFQYXRoIC4gKHN0cmluZykgJHRoaXMtPnVzZXJuYW1lIC4gIi5kYXQiKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX0NPT0tJRUpBUiwgJHRoaXMtPklHRGF0YVBhdGggLiAoc3RyaW5nKSAkdGhpcy0+dXNlcm5hbWUgLiAiLmRhdCIpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfUE9TVCwgdHJ1ZSk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9QT1NURklFTERTLCAkZGF0YSk7DQogICAgICAgICRyZXNwID0gY3VybF9leGVjKCRjaCk7DQogICAgICAgICRoZWFkZXJfbGVuID0gY3VybF9nZXRpbmZvKCRjaCwgQ1VSTElORk9fSEVBREVSX1NJWkUpOw0KICAgICAgICAkaGVhZGVyID0gc3Vic3RyKCRyZXNwLCAwLCAkaGVhZGVyX2xlbik7DQogICAgICAgICR1cGxvYWQgPSBqc29uX2RlY29kZShzdWJzdHIoJHJlc3AsICRoZWFkZXJfbGVuKSwgdHJ1ZSk7DQogICAgICAgIGN1cmxfY2xvc2UoJGNoKTsNCiAgICB9DQoNCiAgICBwcm90ZWN0ZWQgZnVuY3Rpb24gY29uZmlndXJlVmlkZW8oJHVwbG9hZF9pZCwgJHZpZGVvLCAkY2FwdGlvbiA9ICIiKQ0KICAgIHsNCiAgICAgICAgJHRoaXMtPnVwbG9hZFBob3RvKCR2aWRlbywgJGNhcHRpb24sICR1cGxvYWRfaWQpOw0KICAgICAgICBsaXN0KCRzaXplKSA9IGdldGltYWdlc2l6ZSgkdmlkZW8pOw0KICAgICAgICAkcG9zdCA9IGpzb25fZW5jb2RlKGFycmF5KCAidXBsb2FkX2lkIiA9PiAkdXBsb2FkX2lkLCAic291cmNlX3R5cGUiID0+ICIzIiwgInBvc3Rlcl9mcmFtZV9pbmRleCIgPT4gMCwgImxlbmd0aCIgPT4gMCwgImF1ZGlvX211dGVkIiA9PiBmYWxzZSwgImZpbHRlcl90eXBlIiA9PiAiMCIsICJ2aWRlb19yZXN1bHQiID0+ICJkZXByZWNhdGVkIiwgImNsaXBzIiA9PiBhcnJheSggImxlbmd0aCIgPT4gVXRpbHM6OmdldFNlY29uZHMoJHZpZGVvKSwgInNvdXJjZV90eXBlIiA9PiAiMyIsICJjYW1lcmFfcG9zaXRpb24iID0+ICJiYWNrIiApLCAiZXh0cmEiID0+IGFycmF5KCAic291cmNlX3dpZHRoIiA9PiA5NjAsICJzb3VyY2VfaGVpZ2h0IiA9PiAxMjgwICksICJkZXZpY2UiID0+IGFycmF5KCAibWFudWZhY3R1cmVyIiA9PiAiWGlhb21pIiwgIm1vZGVsIiA9PiAiSE0gMVNXIiwgImFuZHJvaWRfdmVyc2lvbiIgPT4gMTgsICJhbmRyb2lkX3JlbGVhc2UiID0+ICI0LjMiICksICJfY3NyZnRva2VuIiA9PiAkdGhpcy0+dG9rZW4sICJfdXVpZCIgPT4gJHRoaXMtPnV1aWQsICJfdWlkIiA9PiAkdGhpcy0+dXNlcm5hbWVfaWQsICJjYXB0aW9uIiA9PiAkY2FwdGlvbiApKTsNCiAgICAgICAgJHBvc3QgPSBzdHJfcmVwbGFjZSgiXCJsZW5ndGhcIjowIiwgIlwibGVuZ3RoXCI6MC4wMCIsICRwb3N0KTsNCiAgICAgICAgcmV0dXJuICR0aGlzLT5yZXF1ZXN0KCJtZWRpYS9jb25maWd1cmUvP3ZpZGVvPTEiLCBTaWduYXR1cmVVdGlsczo6Z2VuZXJhdGVTaWduYXR1cmUoJHBvc3QpKVsxXTsNCiAgICB9DQoNCiAgICBwcm90ZWN0ZWQgZnVuY3Rpb24gY29uZmlndXJlKCR1cGxvYWRfaWQsICRwaG90bywgJGNhcHRpb24gPSAiIikNCiAgICB7DQogICAgICAgIGxpc3QoJHNpemUpID0gZ2V0aW1hZ2VzaXplKCRwaG90byk7DQogICAgICAgICRwb3N0ID0ganNvbl9lbmNvZGUoYXJyYXkoICJ1cGxvYWRfaWQiID0+ICR1cGxvYWRfaWQsICJjYW1lcmFfbW9kZWwiID0+ICJITTFTIiwgInNvdXJjZV90eXBlIiA9PiAzLCAiZGF0ZV90aW1lX29yaWdpbmFsIiA9PiBkYXRlKCJZOm06ZCBIOmk6cyIpLCAiY2FtZXJhX21ha2UiID0+ICJYSUFPTUkiLCAiZWRpdHMiID0+IGFycmF5KCAiY3JvcF9vcmlnaW5hbF9zaXplIiA9PiBhcnJheSggJHNpemUsICRzaXplICksICJjcm9wX3pvb20iID0+IDEuMzMzMzMzNCwgImNyb3BfY2VudGVyIiA9PiBhcnJheSggMCwgMCApICksICJleHRyYSIgPT4gYXJyYXkoICJzb3VyY2Vfd2lkdGgiID0+ICRzaXplLCAic291cmNlX2hlaWdodCIgPT4gJHNpemUgKSwgImRldmljZSIgPT4gYXJyYXkoICJtYW51ZmFjdHVyZXIiID0+ICJYaWFvbWkiLCAibW9kZWwiID0+ICJITSAxU1ciLCAiYW5kcm9pZF92ZXJzaW9uIiA9PiAxOCwgImFuZHJvaWRfcmVsZWFzZSIgPT4gIjQuMyIgKSwgIl9jc3JmdG9rZW4iID0+ICR0aGlzLT50b2tlbiwgIl91dWlkIiA9PiAkdGhpcy0+dXVpZCwgIl91aWQiID0+ICR0aGlzLT51c2VybmFtZV9pZCwgImNhcHRpb24iID0+ICRjYXB0aW9uICkpOw0KICAgICAgICAkcG9zdCA9IHN0cl9yZXBsYWNlKCJcImNyb3BfY2VudGVyXCI6WzAsMF0iLCAiXCJjcm9wX2NlbnRlclwiOlswLjAsLTAuMF0iLCAkcG9zdCk7DQogICAgICAgIHJldHVybiAkdGhpcy0+cmVxdWVzdCgibWVkaWEvY29uZmlndXJlLyIsIFNpZ25hdHVyZVV0aWxzOjpnZW5lcmF0ZVNpZ25hdHVyZSgkcG9zdCkpWzFdOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBFZGl0IG1lZGlhLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICRtZWRpYUlkDQogICAgICAgICAqICAgTWVkaWEgaWQNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkY2FwdGlvblRleHQNCiAgICAgICAgICogICBDYXB0aW9uIHRleHQNCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIGVkaXQgbWVkaWEgZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBlZGl0TWVkaWEoJG1lZGlhSWQsICRjYXB0aW9uVGV4dCA9ICIiKQ0KICAgIHsNCiAgICAgICAgJGRhdGEgPSBqc29uX2VuY29kZShhcnJheSggIl91dWlkIiA9PiAkdGhpcy0+dXVpZCwgIl91aWQiID0+ICR0aGlzLT51c2VybmFtZV9pZCwgIl9jc3JmdG9rZW4iID0+ICR0aGlzLT50b2tlbiwgImNhcHRpb25fdGV4dCIgPT4gJGNhcHRpb25UZXh0ICkpOw0KICAgICAgICByZXR1cm4gJHRoaXMtPnJlcXVlc3QoIm1lZGlhLyIgLiAkbWVkaWFJZCAuICIvZWRpdF9tZWRpYS8iLCBTaWduYXR1cmVVdGlsczo6Z2VuZXJhdGVTaWduYXR1cmUoJGRhdGEpKVsxXTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogUmVtb3ZlIHlvdXJzZWxmIGZyb20gYSB0YWdnZWQgbWVkaWEuDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJG1lZGlhSWQNCiAgICAgICAgICogICBNZWRpYSBpZA0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgZWRpdCBtZWRpYSBkYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIHJlbW92ZVNlbGZ0YWcoJG1lZGlhSWQpDQogICAgew0KICAgICAgICAkZGF0YSA9IGpzb25fZW5jb2RlKGFycmF5KCAiX3V1aWQiID0+ICR0aGlzLT51dWlkLCAiX3VpZCIgPT4gJHRoaXMtPnVzZXJuYW1lX2lkLCAiX2NzcmZ0b2tlbiIgPT4gJHRoaXMtPnRva2VuICkpOw0KICAgICAgICByZXR1cm4gJHRoaXMtPnJlcXVlc3QoInVzZXJ0YWdzLyIgLiAkbWVkaWFJZCAuICIvcmVtb3ZlLyIsIFNpZ25hdHVyZVV0aWxzOjpnZW5lcmF0ZVNpZ25hdHVyZSgkZGF0YSkpWzFdOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBNZWRpYSBpbmZvDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJG1lZGlhSWQNCiAgICAgICAgICogICBNZWRpYSBpZA0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgZGVsZXRlIHJlcXVlc3QgZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBtZWRpYUluZm8oJG1lZGlhSWQpDQogICAgew0KICAgICAgICAkZGF0YSA9IGpzb25fZW5jb2RlKGFycmF5KCAiX3V1aWQiID0+ICR0aGlzLT51dWlkLCAiX3VpZCIgPT4gJHRoaXMtPnVzZXJuYW1lX2lkLCAiX2NzcmZ0b2tlbiIgPT4gJHRoaXMtPnRva2VuLCAibWVkaWFfaWQiID0+ICRtZWRpYUlkICkpOw0KICAgICAgICByZXR1cm4gJHRoaXMtPnJlcXVlc3QoIm1lZGlhLyIgLiAkbWVkaWFJZCAuICIvaW5mby8iLCBTaWduYXR1cmVVdGlsczo6Z2VuZXJhdGVTaWduYXR1cmUoJGRhdGEpKVsxXTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogRGVsZXRlIHBob3RvIG9yIHZpZGVvLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICRtZWRpYUlkDQogICAgICAgICAqICAgTWVkaWEgaWQNCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIGRlbGV0ZSByZXF1ZXN0IGRhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gZGVsZXRlTWVkaWEoJG1lZGlhSWQpDQogICAgew0KICAgICAgICAkZGF0YSA9IGpzb25fZW5jb2RlKGFycmF5KCAiX3V1aWQiID0+ICR0aGlzLT51dWlkLCAiX3VpZCIgPT4gJHRoaXMtPnVzZXJuYW1lX2lkLCAiX2NzcmZ0b2tlbiIgPT4gJHRoaXMtPnRva2VuLCAibWVkaWFfaWQiID0+ICRtZWRpYUlkICkpOw0KICAgICAgICByZXR1cm4gJHRoaXMtPnJlcXVlc3QoIm1lZGlhLyIgLiAkbWVkaWFJZCAuICIvZGVsZXRlLyIsIFNpZ25hdHVyZVV0aWxzOjpnZW5lcmF0ZVNpZ25hdHVyZSgkZGF0YSkpWzFdOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBDb21tZW50IG1lZGlhLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICRtZWRpYUlkDQogICAgICAgICAqICAgTWVkaWEgaWQNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkY29tbWVudFRleHQNCiAgICAgICAgICogICBDb21tZW50IFRleHQNCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIGNvbW1lbnQgbWVkaWEgZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBjb21tZW50KCRtZWRpYUlkLCAkY29tbWVudFRleHQpDQogICAgew0KICAgICAgICAkZGF0YSA9IGpzb25fZW5jb2RlKGFycmF5KCAiX3V1aWQiID0+ICR0aGlzLT51dWlkLCAiX3VpZCIgPT4gJHRoaXMtPnVzZXJuYW1lX2lkLCAiX2NzcmZ0b2tlbiIgPT4gJHRoaXMtPnRva2VuLCAiY29tbWVudF90ZXh0IiA9PiAkY29tbWVudFRleHQgKSk7DQogICAgICAgIHJldHVybiAkdGhpcy0+cmVxdWVzdCgibWVkaWEvIiAuICRtZWRpYUlkIC4gIi9jb21tZW50LyIsIFNpZ25hdHVyZVV0aWxzOjpnZW5lcmF0ZVNpZ25hdHVyZSgkZGF0YSkpWzFdOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBEZWxldGUgQ29tbWVudC4NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkbWVkaWFJZA0KICAgICAgICAgKiAgIE1lZGlhIElEDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJGNvbW1lbnRJZA0KICAgICAgICAgKiAgIENvbW1lbnQgSUQNCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIERlbGV0ZSBjb21tZW50IGRhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gZGVsZXRlQ29tbWVudCgkbWVkaWFJZCwgJGNhcHRpb25UZXh0LCAkY29tbWVudElkKQ0KICAgIHsNCiAgICAgICAgJGRhdGEgPSBqc29uX2VuY29kZShhcnJheSggIl91dWlkIiA9PiAkdGhpcy0+dXVpZCwgIl91aWQiID0+ICR0aGlzLT51c2VybmFtZV9pZCwgIl9jc3JmdG9rZW4iID0+ICR0aGlzLT50b2tlbiwgImNhcHRpb25fdGV4dCIgPT4gJGNhcHRpb25UZXh0ICkpOw0KICAgICAgICByZXR1cm4gJHRoaXMtPnJlcXVlc3QoIm1lZGlhLyIgLiAkbWVkaWFJZCAuICIvY29tbWVudC8iIC4gJGNvbW1lbnRJZCAuICIvZGVsZXRlLyIsIFNpZ25hdHVyZVV0aWxzOjpnZW5lcmF0ZVNpZ25hdHVyZSgkZGF0YSkpWzFdOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBTZXRzIGFjY291bnQgdG8gcHVibGljLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICRwaG90bw0KICAgICAgICAgKiAgIFBhdGggdG8gcGhvdG8NCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gY2hhbmdlUHJvZmlsZVBpY3R1cmUoJHBob3RvKQ0KICAgIHsNCiAgICAgICAgaWYoIGlzX251bGwoJHBob3RvKSApIA0KICAgICAgICB7DQogICAgICAgICAgICBlY2hvICJQaG90byBub3QgdmFsaWRcblxuIjsNCiAgICAgICAgfQ0KICAgICAgICBlbHNlDQogICAgICAgIHsNCiAgICAgICAgICAgICR1RGF0YSA9IGpzb25fZW5jb2RlKGFycmF5KCAiX2NzcmZ0b2tlbiIgPT4gJHRoaXMtPnRva2VuLCAiX3V1aWQiID0+ICR0aGlzLT51dWlkLCAiX3VpZCIgPT4gJHRoaXMtPnVzZXJuYW1lX2lkICkpOw0KICAgICAgICAgICAgJGVuZHBvaW50ID0gQ29uc3RhbnRzOjpBUElfVVJMIC4gImFjY291bnRzL2NoYW5nZV9wcm9maWxlX3BpY3R1cmUvIjsNCiAgICAgICAgICAgICRib3VuZGFyeSA9ICR0aGlzLT51dWlkOw0KICAgICAgICAgICAgJGJvZGllcyA9IGFycmF5KCBhcnJheSggInR5cGUiID0+ICJmb3JtLWRhdGEiLCAibmFtZSIgPT4gImlnX3NpZ19rZXlfdmVyc2lvbiIsICJkYXRhIiA9PiBDb25zdGFudHM6OlNJR19LRVlfVkVSU0lPTiApLCBhcnJheSggInR5cGUiID0+ICJmb3JtLWRhdGEiLCAibmFtZSIgPT4gInNpZ25lZF9ib2R5IiwgImRhdGEiID0+IGhhc2hfaG1hYygic2hhMjU2IiwgJHVEYXRhLCBDb25zdGFudHM6OklHX1NJR19LRVkpIC4gJHVEYXRhICksIGFycmF5KCAidHlwZSIgPT4gImZvcm0tZGF0YSIsICJuYW1lIiA9PiAicHJvZmlsZV9waWMiLCAiZGF0YSIgPT4gZmlsZV9nZXRfY29udGVudHMoJHBob3RvKSwgImZpbGVuYW1lIiA9PiAicHJvZmlsZV9waWMiLCAiaGVhZGVycyIgPT4gYXJyYXkoICJDb250ZW50LXR5cGU6IGFwcGxpY2F0aW9uL29jdGV0LXN0cmVhbSIsICJDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiBiaW5hcnkiICkgKSApOw0KICAgICAgICAgICAgJGRhdGEgPSAkdGhpcy0+YnVpbGRCb2R5KCRib2RpZXMsICRib3VuZGFyeSk7DQogICAgICAgICAgICAkaGVhZGVycyA9IGFycmF5KCAiUHJveHktQ29ubmVjdGlvbjoga2VlcC1hbGl2ZSIsICJDb25uZWN0aW9uOiBrZWVwLWFsaXZlIiwgIkFjY2VwdDogKi8qIiwgIkNvbnRlbnQtdHlwZTogbXVsdGlwYXJ0L2Zvcm0tZGF0YTsgYm91bmRhcnk9IiAuICRib3VuZGFyeSwgIkFjY2VwdC1MYW5ndWFnZTogdHItVFIiLCAiQWNjZXB0LUVuY29kaW5nOiBnemlwLCBkZWZsYXRlIiApOw0KICAgICAgICAgICAgJGNoID0gY3VybF9pbml0KCk7DQogICAgICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfVVJMLCAkZW5kcG9pbnQpOw0KICAgICAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1VTRVJBR0VOVCwgJHRoaXMtPnNldHRpbmdzLT5nZXQoInVzZXJfYWdlbnQiKSk7DQogICAgICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfUkVUVVJOVFJBTlNGRVIsIHRydWUpOw0KICAgICAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX0ZPTExPV0xPQ0FUSU9OLCB0cnVlKTsNCiAgICAgICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9IRUFERVIsIHRydWUpOw0KICAgICAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1ZFUkJPU0UsICR0aGlzLT5kZWJ1Zyk7DQogICAgICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfU1NMX1ZFUklGWVBFRVIsIGZhbHNlKTsNCiAgICAgICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9TU0xfVkVSSUZZSE9TVCwgZmFsc2UpOw0KICAgICAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX0hUVFBIRUFERVIsICRoZWFkZXJzKTsNCiAgICAgICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9DT09LSUVGSUxFLCAkdGhpcy0+SUdEYXRhUGF0aCAuIChzdHJpbmcpICR0aGlzLT51c2VybmFtZSAuICIuZGF0Iik7DQogICAgICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfQ09PS0lFSkFSLCAkdGhpcy0+SUdEYXRhUGF0aCAuIChzdHJpbmcpICR0aGlzLT51c2VybmFtZSAuICIuZGF0Iik7DQogICAgICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfUE9TVCwgdHJ1ZSk7DQogICAgICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfUE9TVEZJRUxEUywgJGRhdGEpOw0KICAgICAgICAgICAgJHJlc3AgPSBjdXJsX2V4ZWMoJGNoKTsNCiAgICAgICAgICAgICRoZWFkZXJfbGVuID0gY3VybF9nZXRpbmZvKCRjaCwgQ1VSTElORk9fSEVBREVSX1NJWkUpOw0KICAgICAgICAgICAgJGhlYWRlciA9IHN1YnN0cigkcmVzcCwgMCwgJGhlYWRlcl9sZW4pOw0KICAgICAgICAgICAgJHVwbG9hZCA9IGpzb25fZGVjb2RlKHN1YnN0cigkcmVzcCwgJGhlYWRlcl9sZW4pLCB0cnVlKTsNCiAgICAgICAgICAgIGN1cmxfY2xvc2UoJGNoKTsNCiAgICAgICAgfQ0KDQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIFJlbW92ZSBwcm9maWxlIHBpY3R1cmUuDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBzdGF0dXMgcmVxdWVzdCBkYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIHJlbW92ZVByb2ZpbGVQaWN0dXJlKCkNCiAgICB7DQogICAgICAgICRkYXRhID0ganNvbl9lbmNvZGUoYXJyYXkoICJfdXVpZCIgPT4gJHRoaXMtPnV1aWQsICJfdWlkIiA9PiAkdGhpcy0+dXNlcm5hbWVfaWQsICJfY3NyZnRva2VuIiA9PiAkdGhpcy0+dG9rZW4gKSk7DQogICAgICAgIHJldHVybiAkdGhpcy0+cmVxdWVzdCgiYWNjb3VudHMvcmVtb3ZlX3Byb2ZpbGVfcGljdHVyZS8iLCBTaWduYXR1cmVVdGlsczo6Z2VuZXJhdGVTaWduYXR1cmUoJGRhdGEpKVsxXTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogU2V0cyBhY2NvdW50IHRvIHByaXZhdGUuDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBzdGF0dXMgcmVxdWVzdCBkYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIHNldFByaXZhdGVBY2NvdW50KCkNCiAgICB7DQogICAgICAgICRkYXRhID0ganNvbl9lbmNvZGUoYXJyYXkoICJfdXVpZCIgPT4gJHRoaXMtPnV1aWQsICJfdWlkIiA9PiAkdGhpcy0+dXNlcm5hbWVfaWQsICJfY3NyZnRva2VuIiA9PiAkdGhpcy0+dG9rZW4gKSk7DQogICAgICAgIHJldHVybiAkdGhpcy0+cmVxdWVzdCgiYWNjb3VudHMvc2V0X3ByaXZhdGUvIiwgU2lnbmF0dXJlVXRpbHM6OmdlbmVyYXRlU2lnbmF0dXJlKCRkYXRhKSlbMV07DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIFNldHMgYWNjb3VudCB0byBwdWJsaWMuDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBzdGF0dXMgcmVxdWVzdCBkYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIHNldFB1YmxpY0FjY291bnQoKQ0KICAgIHsNCiAgICAgICAgJGRhdGEgPSBqc29uX2VuY29kZShhcnJheSggIl91dWlkIiA9PiAkdGhpcy0+dXVpZCwgIl91aWQiID0+ICR0aGlzLT51c2VybmFtZV9pZCwgIl9jc3JmdG9rZW4iID0+ICR0aGlzLT50b2tlbiApKTsNCiAgICAgICAgcmV0dXJuICR0aGlzLT5yZXF1ZXN0KCJhY2NvdW50cy9zZXRfcHVibGljLyIsIFNpZ25hdHVyZVV0aWxzOjpnZW5lcmF0ZVNpZ25hdHVyZSgkZGF0YSkpWzFdOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBHZXQgcGVyc29uYWwgcHJvZmlsZSBkYXRhLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgcHJvZmlsZSBkYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGdldFByb2ZpbGVEYXRhKCkNCiAgICB7DQogICAgICAgICRkYXRhID0ganNvbl9lbmNvZGUoYXJyYXkoICJfdXVpZCIgPT4gJHRoaXMtPnV1aWQsICJfdWlkIiA9PiAkdGhpcy0+dXNlcm5hbWVfaWQsICJfY3NyZnRva2VuIiA9PiAkdGhpcy0+dG9rZW4gKSk7DQogICAgICAgIHJldHVybiAkdGhpcy0+cmVxdWVzdCgiYWNjb3VudHMvY3VycmVudF91c2VyLz9lZGl0PXRydWUiLCBTaWduYXR1cmVVdGlsczo6Z2VuZXJhdGVTaWduYXR1cmUoJGRhdGEpKVsxXTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogRWRpdCBwcm9maWxlLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICR1cmwNCiAgICAgICAgICogICBVcmwgLSB3ZWJzaXRlLiAiIiBmb3Igbm90aGluZw0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICRwaG9uZQ0KICAgICAgICAgKiAgIFBob25lIG51bWJlci4gIiIgZm9yIG5vdGhpbmcNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkZmlyc3RfbmFtZQ0KICAgICAgICAgKiAgIE5hbWUuICIiIGZvciBub3RoaW5nDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJGVtYWlsDQogICAgICAgICAqICAgRW1haWwuIFJlcXVpcmVkLg0KICAgICAgICAgKiBAcGFyYW0gaW50ICAgICRnZW5kZXINCiAgICAgICAgICogICBHZW5kZXIuIG1hbGUgPSAxICwgZmVtYWxlID0gMA0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgZWRpdCBwcm9maWxlIGRhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gZWRpdFByb2ZpbGUoJHVybCwgJHBob25lLCAkZmlyc3RfbmFtZSwgJGJpb2dyYXBoeSwgJGVtYWlsLCAkZ2VuZGVyKQ0KICAgIHsNCiAgICAgICAgJGRhdGEgPSBqc29uX2VuY29kZShhcnJheSggIl91dWlkIiA9PiAkdGhpcy0+dXVpZCwgIl91aWQiID0+ICR0aGlzLT51c2VybmFtZV9pZCwgIl9jc3JmdG9rZW4iID0+ICR0aGlzLT50b2tlbiwgImV4dGVybmFsX3VybCIgPT4gJHVybCwgInBob25lX251bWJlciIgPT4gJHBob25lLCAidXNlcm5hbWUiID0+ICR0aGlzLT51c2VybmFtZSwgImZpcnN0X25hbWUiID0+ICRmaXJzdF9uYW1lLCAiYmlvZ3JhcGh5IiA9PiAkYmlvZ3JhcGh5LCAiZW1haWwiID0+ICRlbWFpbCwgImdlbmRlciIgPT4gJGdlbmRlciApKTsNCiAgICAgICAgcmV0dXJuICR0aGlzLT5yZXF1ZXN0KCJhY2NvdW50cy9lZGl0X3Byb2ZpbGUvIiwgU2lnbmF0dXJlVXRpbHM6OmdlbmVyYXRlU2lnbmF0dXJlKCRkYXRhKSlbMV07DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIEdldCB1c2VybmFtZSBpbmZvLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICR1c2VybmFtZUlkDQogICAgICAgICAqICAgVXNlcm5hbWUgaWQNCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIFVzZXJuYW1lIGRhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gZ2V0VXNlcm5hbWVJbmZvKCR1c2VybmFtZUlkKQ0KICAgIHsNCiAgICAgICAgcmV0dXJuICR0aGlzLT5yZXF1ZXN0KCJ1c2Vycy8iIC4gJHVzZXJuYW1lSWQgLiAiL2luZm8vIilbMV07DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIEdldCBzZWxmIHVzZXJuYW1lIGluZm8uDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBVc2VybmFtZSBkYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGdldFNlbGZVc2VybmFtZUluZm8oKQ0KICAgIHsNCiAgICAgICAgcmV0dXJuICR0aGlzLT5nZXRVc2VybmFtZUluZm8oJHRoaXMtPnVzZXJuYW1lX2lkKTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogR2V0IHJlY2VudCBhY3Rpdml0eS4NCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIFJlY2VudCBhY3Rpdml0eSBkYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGdldFJlY2VudEFjdGl2aXR5KCRtYXhpZCA9IE5VTEwpDQogICAgew0KICAgICAgICBsaXN0KCwgJGFjdGl2aXR5KSA9ICR0aGlzLT5yZXF1ZXN0KCJuZXdzL2luYm94Lz8iIC4gKCghaXNfbnVsbCgkbWF4aWQpID8gIm1heF9pZD0iIC4gJG1heGlkIDogIiIpKSk7DQogICAgICAgIGlmKCAkYWN0aXZpdHlbInN0YXR1cyJdICE9ICJvayIgKSANCiAgICAgICAgew0KICAgICAgICAgICAgdGhyb3cgbmV3IEV4Y2VwdGlvbigkYWN0aXZpdHlbIm1lc3NhZ2UiXSAuICJcbiIpOw0KICAgICAgICB9DQoNCiAgICAgICAgcmV0dXJuICRhY3Rpdml0eTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogR2V0IHJlY2VudCBhY3Rpdml0eSBmcm9tIGFjY291bnRzIGZvbGxvd2VkLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgUmVjZW50IGFjdGl2aXR5IGRhdGEgb2YgZm9sbG93cw0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBnZXRGb2xsb3dpbmdSZWNlbnRBY3Rpdml0eSgkbWF4aWQgPSBOVUxMKQ0KICAgIHsNCiAgICAgICAgbGlzdCgsICRhY3Rpdml0eSkgPSAkdGhpcy0+cmVxdWVzdCgibmV3cy8/IiAuICgoIWlzX251bGwoJG1heGlkKSA/ICJtYXhfaWQ9IiAuICRtYXhpZCA6ICIiKSkpOw0KICAgICAgICBpZiggJGFjdGl2aXR5WyJzdGF0dXMiXSAhPSAib2siICkgDQogICAgICAgIHsNCiAgICAgICAgICAgIHRocm93IG5ldyBFeGNlcHRpb24oJGFjdGl2aXR5WyJtZXNzYWdlIl0gLiAiXG4iKTsNCiAgICAgICAgfQ0KDQogICAgICAgIHJldHVybiAkYWN0aXZpdHk7DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIEkgZG9udCBrbm93IHRoaXMgeWV0Lg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgdjIgaW5ib3ggZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBnZXR2MkluYm94KCkNCiAgICB7DQogICAgICAgIGxpc3QoLCAkaW5ib3gpID0gJHRoaXMtPnJlcXVlc3QoImRpcmVjdF92Mi9pbmJveC8/Iik7DQogICAgICAgIGlmKCAkaW5ib3hbInN0YXR1cyJdICE9ICJvayIgKSANCiAgICAgICAgew0KICAgICAgICAgICAgdGhyb3cgbmV3IEV4Y2VwdGlvbigkaW5ib3hbIm1lc3NhZ2UiXSAuICJcbiIpOw0KICAgICAgICB9DQoNCiAgICAgICAgcmV0dXJuICRpbmJveDsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogRGlyZWN0IFRocmVhZCBEYXRhLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcGFyYW0gIGludCAkdGhyZWFkSWQgVGhyZWFkIElkDQogICAgICAgICAqDQogICAgICAgICAqIEB0aHJvd3MgRXhjZXB0aW9uIERpcmVjdCBUaHJlYWQgRGF0YQ0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5IERpcmVjdCBUaHJlYWQgRGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBkaXJlY3RUaHJlYWQoJHRocmVhZElkKQ0KICAgIHsNCiAgICAgICAgbGlzdCgsICRkaXJlY3RUaHJlYWQpID0gJHRoaXMtPnJlcXVlc3QoImRpcmVjdF92Mi90aHJlYWRzLyIgLiAkdGhyZWFkSWQgLiAiLz8iKTsNCiAgICAgICAgaWYoICRkaXJlY3RUaHJlYWRbInN0YXR1cyJdICE9ICJvayIgKSANCiAgICAgICAgew0KICAgICAgICAgICAgdGhyb3cgbmV3IEV4Y2VwdGlvbigkZGlyZWN0VGhyZWFkWyJtZXNzYWdlIl0gLiAiXG4iKTsNCiAgICAgICAgfQ0KDQogICAgICAgIHJldHVybiAkZGlyZWN0VGhyZWFkOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBHZXQgdXNlciB0YWdzLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICR1c2VybmFtZUlkDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICB1c2VyIHRhZ3MgZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBnZXRVc2VyVGFncygkdXNlcm5hbWVJZCwgJG1heGlkID0gTlVMTCkNCiAgICB7DQogICAgICAgIGxpc3QoLCAkdGFncykgPSAkdGhpcy0+cmVxdWVzdCgidXNlcnRhZ3MvIiAuICR1c2VybmFtZUlkIC4gIi9mZWVkLz9yYW5rX3Rva2VuPSIgLiAkdGhpcy0+cmFua190b2tlbiAuICImcmFua2VkX2NvbnRlbnQ9dHJ1ZSIgLiAoKCFpc19udWxsKCRtYXhpZCkgPyAibWF4X2lkPSIgLiAkbWF4aWQgOiAiIikpKTsNCiAgICAgICAgaWYoICR0YWdzWyJzdGF0dXMiXSAhPSAib2siICkgDQogICAgICAgIHsNCiAgICAgICAgICAgIHRocm93IG5ldyBFeGNlcHRpb24oJHRhZ3NbIm1lc3NhZ2UiXSAuICJcbiIpOw0KICAgICAgICB9DQoNCiAgICAgICAgcmV0dXJuICR0YWdzOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBHZXQgc2VsZiB1c2VyIHRhZ3MuDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBzZWxmIHVzZXIgdGFncyBkYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGdldFNlbGZVc2VyVGFncygkbWF4aWQgPSBOVUxMKQ0KICAgIHsNCiAgICAgICAgcmV0dXJuICR0aGlzLT5nZXRVc2VyVGFncygkdGhpcy0+dXNlcm5hbWVfaWQsICRtYXhpZCk7DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIEdldCB0YWdnZWQgbWVkaWEuDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJHRhZw0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIHRhZ0ZlZWQoJHRhZywgJG1heGlkID0gTlVMTCkNCiAgICB7DQogICAgICAgIGxpc3QoLCAkdXNlckZlZWQpID0gJHRoaXMtPnJlcXVlc3QoImZlZWQvdGFnLyIgLiAkdGFnIC4gIi8/bWF4X2lkPSIgLiAkbWF4aWQgLiAiJnJhbmtfdG9rZW49IiAuICR0aGlzLT5yYW5rX3Rva2VuIC4gIiZyYW5rZWRfY29udGVudD10cnVlJiIpOw0KICAgICAgICBpZiggJHVzZXJGZWVkWyJzdGF0dXMiXSAhPSAib2siICkgDQogICAgICAgIHsNCiAgICAgICAgICAgIHRocm93IG5ldyBFeGNlcHRpb24oJHVzZXJGZWVkWyJtZXNzYWdlIl0gLiAiXG4iKTsNCiAgICAgICAgfQ0KDQogICAgICAgIHJldHVybiAkdXNlckZlZWQ7DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIEdldCBtZWRpYSBsaWtlcnMuDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJG1lZGlhSWQNCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBnZXRNZWRpYUxpa2VycygkbWVkaWFJZCkNCiAgICB7DQogICAgICAgIGxpc3QoLCAkbGlrZXJzKSA9ICR0aGlzLT5yZXF1ZXN0KCJtZWRpYS8iIC4gJG1lZGlhSWQgLiAiL2xpa2Vycy8/Iik7DQogICAgICAgIGlmKCAkbGlrZXJzWyJzdGF0dXMiXSAhPSAib2siICkgDQogICAgICAgIHsNCiAgICAgICAgICAgIHRocm93IG5ldyBFeGNlcHRpb24oJGxpa2Vyc1sibWVzc2FnZSJdIC4gIlxuIik7DQogICAgICAgIH0NCg0KICAgICAgICByZXR1cm4gJGxpa2VyczsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogR2V0IHVzZXIgbG9jYXRpb25zIG1lZGlhLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICR1c2VybmFtZUlkDQogICAgICAgICAqICAgVXNlcm5hbWUgaWQNCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIEdlbyBNZWRpYSBkYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGdldEdlb01lZGlhKCR1c2VybmFtZUlkKQ0KICAgIHsNCiAgICAgICAgbGlzdCgsICRsb2NhdGlvbnMpID0gJHRoaXMtPnJlcXVlc3QoIm1hcHMvdXNlci8iIC4gJHVzZXJuYW1lSWQgLiAiLyIpOw0KICAgICAgICBpZiggJGxvY2F0aW9uc1sic3RhdHVzIl0gIT0gIm9rIiApIA0KICAgICAgICB7DQogICAgICAgICAgICB0aHJvdyBuZXcgRXhjZXB0aW9uKCRsb2NhdGlvbnNbIm1lc3NhZ2UiXSAuICJcbiIpOw0KICAgICAgICB9DQoNCiAgICAgICAgcmV0dXJuICRsb2NhdGlvbnM7DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIEdldCBzZWxmIHVzZXIgbG9jYXRpb25zIG1lZGlhLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgR2VvIE1lZGlhIGRhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gZ2V0U2VsZkdlb01lZGlhKCkNCiAgICB7DQogICAgICAgIHJldHVybiAkdGhpcy0+Z2V0R2VvTWVkaWEoJHRoaXMtPnVzZXJuYW1lX2lkKTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogZmFjZWJvb2sgdXNlciBzZWFyY2guDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJHF1ZXJ5DQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBxdWVyeSBkYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGZiVXNlclNlYXJjaCgkcXVlcnkpDQogICAgew0KICAgICAgICAkcXVlcnkgPSByYXd1cmxlbmNvZGUoJHF1ZXJ5KTsNCiAgICAgICAgbGlzdCgsICRxdWVyeSkgPSAkdGhpcy0+cmVxdWVzdCgiZmJzZWFyY2gvdG9wc2VhcmNoLz9jb250ZXh0PWJsZW5kZWQmcXVlcnk9IiAuICRxdWVyeSAuICImcmFua190b2tlbj0iIC4gJHRoaXMtPnJhbmtfdG9rZW4pOw0KICAgICAgICBpZiggJHF1ZXJ5WyJzdGF0dXMiXSAhPSAib2siICkgDQogICAgICAgIHsNCiAgICAgICAgICAgIHRocm93IG5ldyBFeGNlcHRpb24oJHF1ZXJ5WyJtZXNzYWdlIl0gLiAiXG4iKTsNCiAgICAgICAgfQ0KDQogICAgICAgIHJldHVybiAkcXVlcnk7DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIFNlYXJjaCB1c2Vycy4NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkcXVlcnkNCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIHF1ZXJ5IGRhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gc2VhcmNoVXNlcnMoJHF1ZXJ5KQ0KICAgIHsNCiAgICAgICAgbGlzdCgsICRxdWVyeSkgPSAkdGhpcy0+cmVxdWVzdCgidXNlcnMvc2VhcmNoLz9pZ19zaWdfa2V5X3ZlcnNpb249IiAuIENvbnN0YW50czo6U0lHX0tFWV9WRVJTSU9OIC4gIiZpc190eXBlYWhlYWQ9dHJ1ZSZxdWVyeT0iIC4gJHF1ZXJ5IC4gIiZyYW5rX3Rva2VuPSIgLiAkdGhpcy0+cmFua190b2tlbik7DQogICAgICAgIGlmKCAkcXVlcnlbInN0YXR1cyJdICE9ICJvayIgKSANCiAgICAgICAgew0KICAgICAgICAgICAgdGhyb3cgbmV3IEV4Y2VwdGlvbigkcXVlcnlbIm1lc3NhZ2UiXSAuICJcbiIpOw0KICAgICAgICB9DQoNCiAgICAgICAgcmV0dXJuICRxdWVyeTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogU2VhcmNoIGV4YWN0IHVzZXJuYW1lDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgdXNlcm5hbWVOYW1lIHVzZXJuYW1lIGFzIFNUUklORyBub3QgYW4gaWQNCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIHF1ZXJ5IGRhdGENCiAgICAgICAgICoNCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gc2VhcmNoVXNlcm5hbWUoJHVzZXJuYW1lTmFtZSkNCiAgICB7DQogICAgICAgIGxpc3QoLCAkcXVlcnkpID0gJHRoaXMtPnJlcXVlc3QoInVzZXJzLyIgLiAkdXNlcm5hbWVOYW1lIC4gIi91c2VybmFtZWluZm8vIik7DQogICAgICAgIHJldHVybiAkcXVlcnk7DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIFNlYXJjaCB1c2VycyB1c2luZyBhZGRyZXMgYm9vay4NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIGFycmF5ICRjb250YWN0cw0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgcXVlcnkgZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBzeW5jRnJvbUFkcmVzc0Jvb2soJGNvbnRhY3RzKQ0KICAgIHsNCiAgICAgICAgJGRhdGEgPSAiY29udGFjdHM9IiAuIGpzb25fZW5jb2RlKCRjb250YWN0cywgdHJ1ZSk7DQogICAgICAgIHJldHVybiAkdGhpcy0+cmVxdWVzdCgiYWRkcmVzc19ib29rL2xpbmsvP2luY2x1ZGU9ZXh0cmFfZGlzcGxheV9uYW1lLHRodW1ibmFpbHMiLCAkZGF0YSlbMV07DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIFNlYXJjaCB0YWdzLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICRxdWVyeQ0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgcXVlcnkgZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBzZWFyY2hUYWdzKCRxdWVyeSkNCiAgICB7DQogICAgICAgIGxpc3QoLCAkcXVlcnkpID0gJHRoaXMtPnJlcXVlc3QoInRhZ3Mvc2VhcmNoLz9pc190eXBlYWhlYWQ9dHJ1ZSZxPSIgLiAkcXVlcnkgLiAiJnJhbmtfdG9rZW49IiAuICR0aGlzLT5yYW5rX3Rva2VuKTsNCiAgICAgICAgaWYoICRxdWVyeVsic3RhdHVzIl0gIT0gIm9rIiApIA0KICAgICAgICB7DQogICAgICAgICAgICB0aHJvdyBuZXcgRXhjZXB0aW9uKCRxdWVyeVsibWVzc2FnZSJdIC4gIlxuIik7DQogICAgICAgIH0NCg0KICAgICAgICByZXR1cm4gJHF1ZXJ5Ow0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBHZXQgdGltZWxpbmUgZGF0YS4NCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIHRpbWVsaW5lIGRhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gZ2V0VGltZWxpbmUoJG1heGlkID0gTlVMTCkNCiAgICB7DQogICAgICAgIGxpc3QoLCAkdGltZWxpbmUpID0gJHRoaXMtPnJlcXVlc3QoImZlZWQvdGltZWxpbmUvP3JhbmtfdG9rZW49IiAuICR0aGlzLT5yYW5rX3Rva2VuIC4gIiZyYW5rZWRfY29udGVudD10cnVlIiAuICgoIWlzX251bGwoJG1heGlkKSA/ICImbWF4X2lkPSIgLiAkbWF4aWQgOiAiIikpKTsNCiAgICAgICAgaWYoICR0aW1lbGluZVsic3RhdHVzIl0gIT0gIm9rIiApIA0KICAgICAgICB7DQogICAgICAgICAgICB0aHJvdyBuZXcgRXhjZXB0aW9uKCR0aW1lbGluZVsibWVzc2FnZSJdIC4gIlxuIik7DQogICAgICAgIH0NCg0KICAgICAgICByZXR1cm4gJHRpbWVsaW5lOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBAdGhyb3dzIEV4Y2VwdGlvbg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5fHZvaWQNCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gZ2V0UmVlbHNUcmF5RmVlZCgpDQogICAgew0KICAgICAgICBsaXN0KCwgJGZlZWQpID0gJHRoaXMtPnJlcXVlc3QoImZlZWQvcmVlbHNfdHJheS8iKTsNCiAgICAgICAgaWYoICRmZWVkWyJzdGF0dXMiXSAhPSAib2siICkgDQogICAgICAgIHsNCiAgICAgICAgICAgIHRocm93IG5ldyBFeGNlcHRpb24oJGZlZWRbIm1lc3NhZ2UiXSAuICJcbiIpOw0KICAgICAgICB9DQoNCiAgICAgICAgcmV0dXJuICRmZWVkOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBHZXQgdXNlciBmZWVkLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICR1c2VybmFtZUlkDQogICAgICAgICAqICAgIFVzZXJuYW1lIGlkDQogICAgICAgICAqIEBwYXJhbSBudWxsICAgJG1heGlkDQogICAgICAgICAqICAgIE1heCBJZA0KICAgICAgICAgKiBAcGFyYW0gbnVsbCAgICRtaW5UaW1lc3RhbXANCiAgICAgICAgICogICAgTWluIHRpbWVzdGFtcA0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5IFVzZXIgZmVlZCBkYXRhDQogICAgICAgICAqICAgIFVzZXIgZmVlZCBkYXRhDQogICAgICAgICAqIEB0aHJvd3MgRXhjZXB0aW9uDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGdldFVzZXJGZWVkKCR1c2VybmFtZUlkLCAkbWF4aWQgPSBOVUxMLCAkbWluVGltZXN0YW1wID0gTlVMTCkNCiAgICB7DQogICAgICAgIGxpc3QoLCAkdXNlckZlZWQpID0gJHRoaXMtPnJlcXVlc3QoImZlZWQvdXNlci8iIC4gJHVzZXJuYW1lSWQgLiAiLz9yYW5rX3Rva2VuPSIgLiAkdGhpcy0+cmFua190b2tlbiAuICgoIWlzX251bGwoJG1heGlkKSA/ICImbWF4X2lkPSIgLiAkbWF4aWQgOiAiIikpIC4gKCghaXNfbnVsbCgkbWluVGltZXN0YW1wKSA/ICImbWluX3RpbWVzdGFtcD0iIC4gJG1pblRpbWVzdGFtcCA6ICIiKSkgLiAiJnJhbmtlZF9jb250ZW50PXRydWUiKTsNCiAgICAgICAgcmV0dXJuICR1c2VyRmVlZDsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogR2V0IGhhc2h0YWcgZmVlZC4NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkaGFzaHRhZ1N0cmluZw0KICAgICAgICAgKiAgICBIYXNodGFnIHN0cmluZywgbm90IGluY2x1ZGluZyB0aGUgIw0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgSGFzaHRhZyBmZWVkIGRhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gZ2V0SGFzaHRhZ0ZlZWQoJGhhc2h0YWdTdHJpbmcsICRtYXhpZCA9IE5VTEwpDQogICAgew0KICAgICAgICBpZiggaXNfbnVsbCgkbWF4aWQpICkgDQogICAgICAgIHsNCiAgICAgICAgICAgICRlbmRwb2ludCA9ICJmZWVkL3RhZy8iIC4gJGhhc2h0YWdTdHJpbmcgLiAiLz9yYW5rX3Rva2VuPSIgLiAkdGhpcy0+cmFua190b2tlbiAuICImcmFua2VkX2NvbnRlbnQ9dHJ1ZSYiOw0KICAgICAgICB9DQogICAgICAgIGVsc2UNCiAgICAgICAgew0KICAgICAgICAgICAgJGVuZHBvaW50ID0gImZlZWQvdGFnLyIgLiAkaGFzaHRhZ1N0cmluZyAuICIvP21heF9pZD0iIC4gJG1heGlkIC4gIiZyYW5rX3Rva2VuPSIgLiAkdGhpcy0+cmFua190b2tlbiAuICImcmFua2VkX2NvbnRlbnQ9dHJ1ZSYiOw0KICAgICAgICB9DQoNCiAgICAgICAgbGlzdCgsICRoYXNodGFnRmVlZCkgPSAkdGhpcy0+cmVxdWVzdCgkZW5kcG9pbnQpOw0KICAgICAgICBpZiggJGhhc2h0YWdGZWVkWyJzdGF0dXMiXSAhPSAib2siICkgDQogICAgICAgIHsNCiAgICAgICAgICAgIHRocm93IG5ldyBFeGNlcHRpb24oJGhhc2h0YWdGZWVkWyJtZXNzYWdlIl0gLiAiXG4iKTsNCiAgICAgICAgfQ0KDQogICAgICAgIHJldHVybiAkaGFzaHRhZ0ZlZWQ7DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIEdldCBsb2NhdGlvbnMuDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJHF1ZXJ5DQogICAgICAgICAqICAgIHNlYXJjaCBxdWVyeQ0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgTG9jYXRpb24gbG9jYXRpb24gZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBzZWFyY2hMb2NhdGlvbigkcXVlcnkpDQogICAgew0KICAgICAgICAkcXVlcnkgPSByYXd1cmxlbmNvZGUoJHF1ZXJ5KTsNCiAgICAgICAgJGVuZHBvaW50ID0gImZic2VhcmNoL3BsYWNlcy8/cmFua190b2tlbj0iIC4gJHRoaXMtPnJhbmtfdG9rZW4gLiAiJnF1ZXJ5PSIgLiAkcXVlcnk7DQogICAgICAgIGxpc3QoLCAkbG9jYXRpb25GZWVkKSA9ICR0aGlzLT5yZXF1ZXN0KCRlbmRwb2ludCk7DQogICAgICAgIGlmKCAkbG9jYXRpb25GZWVkWyJzdGF0dXMiXSAhPSAib2siICkgDQogICAgICAgIHsNCiAgICAgICAgICAgIHRocm93IG5ldyBFeGNlcHRpb24oJGxvY2F0aW9uRmVlZFsibWVzc2FnZSJdIC4gIlxuIik7DQogICAgICAgIH0NCg0KICAgICAgICByZXR1cm4gJGxvY2F0aW9uRmVlZDsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogR2V0IGxvY2F0aW9uIGZlZWQuDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJGxvY2F0aW9uSWQNCiAgICAgICAgICogICAgbG9jYXRpb24gaWQNCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIExvY2F0aW9uIGZlZWQgZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBnZXRMb2NhdGlvbkZlZWQoJGxvY2F0aW9uSWQsICRtYXhpZCA9IE5VTEwpDQogICAgew0KICAgICAgICBpZiggaXNfbnVsbCgkbWF4aWQpICkgDQogICAgICAgIHsNCiAgICAgICAgICAgICRlbmRwb2ludCA9ICJmZWVkL2xvY2F0aW9uLyIgLiAkbG9jYXRpb25JZCAuICIvP3JhbmtfdG9rZW49IiAuICR0aGlzLT5yYW5rX3Rva2VuIC4gIiZyYW5rZWRfY29udGVudD10cnVlJiI7DQogICAgICAgIH0NCiAgICAgICAgZWxzZQ0KICAgICAgICB7DQogICAgICAgICAgICAkZW5kcG9pbnQgPSAiZmVlZC9sb2NhdGlvbi8iIC4gJGxvY2F0aW9uSWQgLiAiLz9tYXhfaWQ9IiAuICRtYXhpZCAuICImcmFua190b2tlbj0iIC4gJHRoaXMtPnJhbmtfdG9rZW4gLiAiJnJhbmtlZF9jb250ZW50PXRydWUmIjsNCiAgICAgICAgfQ0KDQogICAgICAgIGxpc3QoLCAkbG9jYXRpb25GZWVkKSA9ICR0aGlzLT5yZXF1ZXN0KCRlbmRwb2ludCk7DQogICAgICAgIGlmKCAkbG9jYXRpb25GZWVkWyJzdGF0dXMiXSAhPSAib2siICkgDQogICAgICAgIHsNCiAgICAgICAgICAgIHRocm93IG5ldyBFeGNlcHRpb24oJGxvY2F0aW9uRmVlZFsibWVzc2FnZSJdIC4gIlxuIik7DQogICAgICAgIH0NCg0KICAgICAgICByZXR1cm4gJGxvY2F0aW9uRmVlZDsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogR2V0IHNlbGYgdXNlciBmZWVkLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgVXNlciBmZWVkIGRhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gZ2V0U2VsZlVzZXJGZWVkKCRtYXhpZCA9IE5VTEwsICRtaW5UaW1lc3RhbXAgPSBOVUxMKQ0KICAgIHsNCiAgICAgICAgcmV0dXJuICR0aGlzLT5nZXRVc2VyRmVlZCgkdGhpcy0+dXNlcm5hbWVfaWQsICRtYXhpZCwgJG1pblRpbWVzdGFtcCk7DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIFJhbmtlZCByZWNpcGllbnRzLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAdGhyb3dzIEV4Y2VwdGlvbiBSYW5rZWQgcmVjaXBpZW50cyBEYXRhDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXl8dm9pZA0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBnZXRSYW5rZWRSZWNpcGllbnRzKCkNCiAgICB7DQogICAgICAgIGxpc3QoLCAkcmFua2VkX3JlY2lwaWVudHMpID0gJHRoaXMtPnJlcXVlc3QoImRpcmVjdF92Mi9yYW5rZWRfcmVjaXBpZW50cy8/c2hvd190aHJlYWRzPXRydWUiKTsNCiAgICAgICAgaWYoICRyYW5rZWRfcmVjaXBpZW50c1sic3RhdHVzIl0gIT0gIm9rIiApIA0KICAgICAgICB7DQogICAgICAgICAgICB0aHJvdyBuZXcgRXhjZXB0aW9uKCRyYW5rZWRfcmVjaXBpZW50c1sibWVzc2FnZSJdIC4gIlxuIik7DQogICAgICAgIH0NCg0KICAgICAgICByZXR1cm4gJHJhbmtlZF9yZWNpcGllbnRzOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBSZWNlbnQgcmVjaXBpZW50cy4NCiAgICAgICAgICoNCiAgICAgICAgICogQHRocm93cyBFeGNlcHRpb24gUmFua2VkIHJlY2lwaWVudHMgRGF0YQ0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5fHZvaWQNCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gZ2V0UmVjZW50UmVjaXBpZW50cygpDQogICAgew0KICAgICAgICBsaXN0KCwgJHJlY2VudF9yZWNpcGllbnRzKSA9ICR0aGlzLT5yZXF1ZXN0KCJkaXJlY3Rfc2hhcmUvcmVjZW50X3JlY2lwaWVudHMvIik7DQogICAgICAgIGlmKCAkcmVjZW50X3JlY2lwaWVudHNbInN0YXR1cyJdICE9ICJvayIgKSANCiAgICAgICAgew0KICAgICAgICAgICAgdGhyb3cgbmV3IEV4Y2VwdGlvbigkcmVjZW50X3JlY2lwaWVudHNbIm1lc3NhZ2UiXSAuICJcbiIpOw0KICAgICAgICB9DQoNCiAgICAgICAgcmV0dXJuICRyZWNlbnRfcmVjaXBpZW50czsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogRXhwbG9yZSBUYWIuDQogICAgICAgICAqDQogICAgICAgICAqIEB0aHJvd3MgRXhjZXB0aW9uIEV4cGxvcmUgZGF0YQ0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5fHZvaWQNCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gZXhwbG9yZSgpDQogICAgew0KICAgICAgICBsaXN0KCwgJGV4cGxvcmUpID0gJHRoaXMtPnJlcXVlc3QoImRpc2NvdmVyL2V4cGxvcmUvIik7DQogICAgICAgIGlmKCAkZXhwbG9yZVsic3RhdHVzIl0gIT0gIm9rIiApIA0KICAgICAgICB7DQogICAgICAgICAgICB0aHJvdyBuZXcgRXhjZXB0aW9uKCRleHBsb3JlWyJtZXNzYWdlIl0gLiAiXG4iKTsNCiAgICAgICAgfQ0KDQogICAgICAgIHJldHVybiAkZXhwbG9yZTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogR2V0IHBvcHVsYXIgZmVlZC4NCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIHBvcHVsYXIgZmVlZCBkYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGdldFBvcHVsYXJGZWVkKCRtYXhpZCA9IE5VTEwpDQogICAgew0KICAgICAgICBsaXN0KCwgJHBvcHVsYXJGZWVkKSA9ICR0aGlzLT5yZXF1ZXN0KCJmZWVkL3BvcHVsYXIvP21heF9pZD0iIC4gJG1heGlkIC4gIiZwZW9wbGVfdGVhc2VyX3N1cHBvcnRlZD0xJnJhbmtfdG9rZW49IiAuICR0aGlzLT5yYW5rX3Rva2VuIC4gIiZyYW5rZWRfY29udGVudD10cnVlJiIpOw0KICAgICAgICBpZiggJHBvcHVsYXJGZWVkWyJzdGF0dXMiXSAhPSAib2siICkgDQogICAgICAgIHsNCiAgICAgICAgICAgIHRocm93IG5ldyBFeGNlcHRpb24oJHBvcHVsYXJGZWVkWyJtZXNzYWdlIl0gLiAiXG4iKTsNCiAgICAgICAgfQ0KDQogICAgICAgIHJldHVybiAkcG9wdWxhckZlZWQ7DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIEdldCB1c2VyIGZvbGxvd2luZ3MuDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJHVzZXJuYW1lSWQNCiAgICAgICAgICogICBVc2VybmFtZSBpZA0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgZm9sbG93ZXJzIGRhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gZ2V0VXNlckZvbGxvd2luZ3MoJHVzZXJuYW1lSWQsICRtYXhpZCA9IE5VTEwpDQogICAgew0KICAgICAgICByZXR1cm4gJHRoaXMtPnJlcXVlc3QoImZyaWVuZHNoaXBzLyIgLiAkdXNlcm5hbWVJZCAuICIvZm9sbG93aW5nLz9tYXhfaWQ9IiAuICRtYXhpZCAuICImaWdfc2lnX2tleV92ZXJzaW9uPSIgLiBDb25zdGFudHM6OlNJR19LRVlfVkVSU0lPTiAuICImcmFua190b2tlbj0iIC4gJHRoaXMtPnJhbmtfdG9rZW4pWzFdOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBHZXQgdXNlciBmb2xsb3dlcnMuDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJHVzZXJuYW1lSWQNCiAgICAgICAgICogICBVc2VybmFtZSBpZA0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgZm9sbG93ZXJzIGRhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gZ2V0VXNlckZvbGxvd2VycygkdXNlcm5hbWVJZCwgJG1heGlkID0gTlVMTCkNCiAgICB7DQogICAgICAgIHJldHVybiAkdGhpcy0+cmVxdWVzdCgiZnJpZW5kc2hpcHMvIiAuICR1c2VybmFtZUlkIC4gIi9mb2xsb3dlcnMvP21heF9pZD0iIC4gJG1heGlkIC4gIiZpZ19zaWdfa2V5X3ZlcnNpb249IiAuIENvbnN0YW50czo6U0lHX0tFWV9WRVJTSU9OIC4gIiZyYW5rX3Rva2VuPSIgLiAkdGhpcy0+cmFua190b2tlbilbMV07DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIEdldCBzZWxmIHVzZXIgZm9sbG93ZXJzLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgZm9sbG93ZXJzIGRhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gZ2V0U2VsZlVzZXJGb2xsb3dlcnMoJG1heGlkID0gTlVMTCkNCiAgICB7DQogICAgICAgIHJldHVybiAkdGhpcy0+Z2V0VXNlckZvbGxvd2VycygkdGhpcy0+dXNlcm5hbWVfaWQsICRtYXhpZCk7DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIEdldCBzZWxmIHVzZXJzIHdlIGFyZSBmb2xsb3dpbmcuDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICB1c2VycyB3ZSBhcmUgZm9sbG93aW5nIGRhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gZ2V0U2VsZlVzZXJzRm9sbG93aW5nKCRtYXhpZCA9IE5VTEwpDQogICAgew0KICAgICAgICByZXR1cm4gJHRoaXMtPnJlcXVlc3QoImZyaWVuZHNoaXBzL2ZvbGxvd2luZy8/bWF4X2lkPSIgLiAkbWF4aWQgLiAiJmlnX3NpZ19rZXlfdmVyc2lvbj0iIC4gQ29uc3RhbnRzOjpTSUdfS0VZX1ZFUlNJT04gLiAiJnJhbmtfdG9rZW49IiAuICR0aGlzLT5yYW5rX3Rva2VuKVsxXTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogTGlrZSBwaG90byBvciB2aWRlby4NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkbWVkaWFJZA0KICAgICAgICAgKiAgIE1lZGlhIGlkDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBzdGF0dXMgcmVxdWVzdA0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBsaWtlKCRtZWRpYUlkKQ0KICAgIHsNCiAgICAgICAgJGRhdGEgPSBqc29uX2VuY29kZShhcnJheSggIl91dWlkIiA9PiAkdGhpcy0+dXVpZCwgIl91aWQiID0+ICR0aGlzLT51c2VybmFtZV9pZCwgIl9jc3JmdG9rZW4iID0+ICR0aGlzLT50b2tlbiwgIm1lZGlhX2lkIiA9PiAkbWVkaWFJZCApKTsNCiAgICAgICAgcmV0dXJuICR0aGlzLT5yZXF1ZXN0KCJtZWRpYS8iIC4gJG1lZGlhSWQgLiAiL2xpa2UvIiwgU2lnbmF0dXJlVXRpbHM6OmdlbmVyYXRlU2lnbmF0dXJlKCRkYXRhKSlbMV07DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIFVubGlrZSBwaG90byBvciB2aWRlby4NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkbWVkaWFJZA0KICAgICAgICAgKiAgIE1lZGlhIGlkDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBzdGF0dXMgcmVxdWVzdA0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiB1bmxpa2UoJG1lZGlhSWQpDQogICAgew0KICAgICAgICAkZGF0YSA9IGpzb25fZW5jb2RlKGFycmF5KCAiX3V1aWQiID0+ICR0aGlzLT51dWlkLCAiX3VpZCIgPT4gJHRoaXMtPnVzZXJuYW1lX2lkLCAiX2NzcmZ0b2tlbiIgPT4gJHRoaXMtPnRva2VuLCAibWVkaWFfaWQiID0+ICRtZWRpYUlkICkpOw0KICAgICAgICByZXR1cm4gJHRoaXMtPnJlcXVlc3QoIm1lZGlhLyIgLiAkbWVkaWFJZCAuICIvdW5saWtlLyIsIFNpZ25hdHVyZVV0aWxzOjpnZW5lcmF0ZVNpZ25hdHVyZSgkZGF0YSkpWzFdOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBHZXQgbWVkaWEgY29tbWVudHMuDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJG1lZGlhSWQNCiAgICAgICAgICogICBNZWRpYSBpZA0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgTWVkaWEgY29tbWVudHMgZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBnZXRNZWRpYUNvbW1lbnRzKCRtZWRpYUlkKQ0KICAgIHsNCiAgICAgICAgcmV0dXJuICR0aGlzLT5yZXF1ZXN0KCJtZWRpYS8iIC4gJG1lZGlhSWQgLiAiL2NvbW1lbnRzLz8iKVsxXTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogU2V0IG5hbWUgYW5kIHBob25lIChPcHRpb25hbCkuDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJG5hbWUNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkcGhvbmUNCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIFNldCBzdGF0dXMgZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBzZXROYW1lQW5kUGhvbmUoJG5hbWUgPSAiIiwgJHBob25lID0gIiIpDQogICAgew0KICAgICAgICAkZGF0YSA9IGpzb25fZW5jb2RlKGFycmF5KCAiX3V1aWQiID0+ICR0aGlzLT51dWlkLCAiX3VpZCIgPT4gJHRoaXMtPnVzZXJuYW1lX2lkLCAiZmlyc3RfbmFtZSIgPT4gJG5hbWUsICJwaG9uZV9udW1iZXIiID0+ICRwaG9uZSwgIl9jc3JmdG9rZW4iID0+ICR0aGlzLT50b2tlbiApKTsNCiAgICAgICAgcmV0dXJuICR0aGlzLT5yZXF1ZXN0KCJhY2NvdW50cy9zZXRfcGhvbmVfYW5kX25hbWUvIiwgU2lnbmF0dXJlVXRpbHM6OmdlbmVyYXRlU2lnbmF0dXJlKCRkYXRhKSlbMV07DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIEdldCBkaXJlY3Qgc2hhcmUuDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBEaXJlY3Qgc2hhcmUgZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBnZXREaXJlY3RTaGFyZSgpDQogICAgew0KICAgICAgICByZXR1cm4gJHRoaXMtPnJlcXVlc3QoImRpcmVjdF9zaGFyZS9pbmJveC8/IilbMV07DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIEJhY2t1cHMgYWxsIHlvdXIgdXBsb2FkZWQgcGhvdG9zIDopLg0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBiYWNrdXAoKQ0KICAgIHsNCiAgICAgICAgJG15VXBsb2FkcyA9ICR0aGlzLT5nZXRTZWxmVXNlckZlZWQoKTsNCiAgICAgICAgZm9yZWFjaCggJG15VXBsb2Fkc1siaXRlbXMiXSBhcyAkaXRlbSApIA0KICAgICAgICB7DQogICAgICAgICAgICBpZiggIWlzX2RpcigkdGhpcy0+SUdEYXRhUGF0aCAuICJiYWNrdXAvIiAuIChzdHJpbmcpICR0aGlzLT51c2VybmFtZSAuICItIiAuIGRhdGUoIlktbS1kIikpICkgDQogICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgbWtkaXIoJHRoaXMtPklHRGF0YVBhdGggLiAiYmFja3VwLyIgLiAoc3RyaW5nKSAkdGhpcy0+dXNlcm5hbWUgLiAiLSIgLiBkYXRlKCJZLW0tZCIpKTsNCiAgICAgICAgICAgIH0NCg0KICAgICAgICAgICAgZmlsZV9wdXRfY29udGVudHMoJHRoaXMtPklHRGF0YVBhdGggLiAiYmFja3VwLyIgLiAoc3RyaW5nKSAkdGhpcy0+dXNlcm5hbWUgLiAiLSIgLiBkYXRlKCJZLW0tZCIpIC4gIi8iIC4gJGl0ZW1bImlkIl0gLiAiLmpwZyIsIGZpbGVfZ2V0X2NvbnRlbnRzKCRpdGVtWyJpbWFnZV92ZXJzaW9uczIiXVsiY2FuZGlkYXRlcyJdWzBdWyJ1cmwiXSkpOw0KICAgICAgICB9DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIEZvbGxvdy4NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkdXNlcklkDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBGcmllbmRzaGlwIHN0YXR1cyBkYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGZvbGxvdygkdXNlcklkKQ0KICAgIHsNCiAgICAgICAgJGRhdGEgPSBqc29uX2VuY29kZShhcnJheSggIl91dWlkIiA9PiAkdGhpcy0+dXVpZCwgIl91aWQiID0+ICR0aGlzLT51c2VybmFtZV9pZCwgInVzZXJfaWQiID0+ICR1c2VySWQsICJfY3NyZnRva2VuIiA9PiAkdGhpcy0+dG9rZW4gKSk7DQogICAgICAgIHJldHVybiAkdGhpcy0+cmVxdWVzdCgiZnJpZW5kc2hpcHMvY3JlYXRlLyIgLiAkdXNlcklkIC4gIi8iLCBTaWduYXR1cmVVdGlsczo6Z2VuZXJhdGVTaWduYXR1cmUoJGRhdGEpKVsxXTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogVW5mb2xsb3cuDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJHVzZXJJZA0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgRnJpZW5kc2hpcCBzdGF0dXMgZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiB1bmZvbGxvdygkdXNlcklkKQ0KICAgIHsNCiAgICAgICAgJGRhdGEgPSBqc29uX2VuY29kZShhcnJheSggIl91dWlkIiA9PiAkdGhpcy0+dXVpZCwgIl91aWQiID0+ICR0aGlzLT51c2VybmFtZV9pZCwgInVzZXJfaWQiID0+ICR1c2VySWQsICJfY3NyZnRva2VuIiA9PiAkdGhpcy0+dG9rZW4gKSk7DQogICAgICAgIHJldHVybiAkdGhpcy0+cmVxdWVzdCgiZnJpZW5kc2hpcHMvZGVzdHJveS8iIC4gJHVzZXJJZCAuICIvIiwgU2lnbmF0dXJlVXRpbHM6OmdlbmVyYXRlU2lnbmF0dXJlKCRkYXRhKSlbMV07DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIEJsb2NrLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICR1c2VySWQNCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIEZyaWVuZHNoaXAgc3RhdHVzIGRhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gYmxvY2soJHVzZXJJZCkNCiAgICB7DQogICAgICAgICRkYXRhID0ganNvbl9lbmNvZGUoYXJyYXkoICJfdXVpZCIgPT4gJHRoaXMtPnV1aWQsICJfdWlkIiA9PiAkdGhpcy0+dXNlcm5hbWVfaWQsICJ1c2VyX2lkIiA9PiAkdXNlcklkLCAiX2NzcmZ0b2tlbiIgPT4gJHRoaXMtPnRva2VuICkpOw0KICAgICAgICByZXR1cm4gJHRoaXMtPnJlcXVlc3QoImZyaWVuZHNoaXBzL2Jsb2NrLyIgLiAkdXNlcklkIC4gIi8iLCBTaWduYXR1cmVVdGlsczo6Z2VuZXJhdGVTaWduYXR1cmUoJGRhdGEpKVsxXTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogVW5ibG9jay4NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkdXNlcklkDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBGcmllbmRzaGlwIHN0YXR1cyBkYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIHVuYmxvY2soJHVzZXJJZCkNCiAgICB7DQogICAgICAgICRkYXRhID0ganNvbl9lbmNvZGUoYXJyYXkoICJfdXVpZCIgPT4gJHRoaXMtPnV1aWQsICJfdWlkIiA9PiAkdGhpcy0+dXNlcm5hbWVfaWQsICJ1c2VyX2lkIiA9PiAkdXNlcklkLCAiX2NzcmZ0b2tlbiIgPT4gJHRoaXMtPnRva2VuICkpOw0KICAgICAgICByZXR1cm4gJHRoaXMtPnJlcXVlc3QoImZyaWVuZHNoaXBzL3VuYmxvY2svIiAuICR1c2VySWQgLiAiLyIsIFNpZ25hdHVyZVV0aWxzOjpnZW5lcmF0ZVNpZ25hdHVyZSgkZGF0YSkpWzFdOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBTaG93IFVzZXIgRnJpZW5kc2hpcC4NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkdXNlcklkDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBGcmllbmRzaGlwIHJlbGF0aW9uc2hpcCBkYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIHVzZXJGcmllbmRzaGlwKCR1c2VySWQpDQogICAgew0KICAgICAgICAkZGF0YSA9IGpzb25fZW5jb2RlKGFycmF5KCAiX3V1aWQiID0+ICR0aGlzLT51dWlkLCAiX3VpZCIgPT4gJHRoaXMtPnVzZXJuYW1lX2lkLCAidXNlcl9pZCIgPT4gJHVzZXJJZCwgIl9jc3JmdG9rZW4iID0+ICR0aGlzLT50b2tlbiApKTsNCiAgICAgICAgcmV0dXJuICR0aGlzLT5yZXF1ZXN0KCJmcmllbmRzaGlwcy9zaG93LyIgLiAkdXNlcklkIC4gIi8iLCBTaWduYXR1cmVVdGlsczo6Z2VuZXJhdGVTaWduYXR1cmUoJGRhdGEpKVsxXTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogR2V0IGxpa2VkIG1lZGlhLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgTGlrZWQgbWVkaWEgZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBnZXRMaWtlZE1lZGlhKCRtYXhpZCA9IE5VTEwpDQogICAgew0KICAgICAgICByZXR1cm4gJHRoaXMtPnJlcXVlc3QoImZlZWQvbGlrZWQvP21heF9pZD0iIC4gJG1heGlkKVsxXTsNCiAgICB9DQoNCiAgICBwcm90ZWN0ZWQgZnVuY3Rpb24gYnVpbGRCb2R5KCRib2RpZXMsICRib3VuZGFyeSkNCiAgICB7DQogICAgICAgICRib2R5ID0gIiI7DQogICAgICAgIGZvcmVhY2goICRib2RpZXMgYXMgJGIgKSANCiAgICAgICAgew0KICAgICAgICAgICAgJGJvZHkgLj0gIi0tIiAuICRib3VuZGFyeSAuICJcclxuIjsNCiAgICAgICAgICAgICRib2R5IC49ICJDb250ZW50LURpc3Bvc2l0aW9uOiAiIC4gJGJbInR5cGUiXSAuICI7IG5hbWU9XCIiIC4gJGJbIm5hbWUiXSAuICJcIiI7DQogICAgICAgICAgICBpZiggaXNzZXQoJGJbImZpbGVuYW1lIl0pICkgDQogICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgJGV4dCA9IHBhdGhpbmZvKCRiWyJmaWxlbmFtZSJdLCBQQVRISU5GT19FWFRFTlNJT04pOw0KICAgICAgICAgICAgICAgICRib2R5IC49ICI7IGZpbGVuYW1lPVwiIiAuICJwZW5kaW5nX21lZGlhXyIgLiBudW1iZXJfZm9ybWF0KHJvdW5kKG1pY3JvdGltZSh0cnVlKSAqIDEwMDApLCAwLCAiIiwgIiIpIC4gIi4iIC4gJGV4dCAuICJcIiI7DQogICAgICAgICAgICB9DQoNCiAgICAgICAgICAgIGlmKCBpc3NldCgkYlsiaGVhZGVycyJdKSAmJiBpc19hcnJheSgkYlsiaGVhZGVycyJdKSApIA0KICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgIGZvcmVhY2goICRiWyJoZWFkZXJzIl0gYXMgJGhlYWRlciApIA0KICAgICAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAgICAgJGJvZHkgLj0gIlxyXG4iIC4gJGhlYWRlcjsNCiAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICB9DQoNCiAgICAgICAgICAgICRib2R5IC49ICJcclxuXHJcbiIgLiAkYlsiZGF0YSJdIC4gIlxyXG4iOw0KICAgICAgICB9DQogICAgICAgICRib2R5IC49ICItLSIgLiAkYm91bmRhcnkgLiAiLS0iOw0KICAgICAgICByZXR1cm4gJGJvZHk7DQogICAgfQ0KDQogICAgcHJvdGVjdGVkIGZ1bmN0aW9uIHJlcXVlc3QoJGVuZHBvaW50LCAkcG9zdCA9IE5VTEwsICRsb2dpbiA9IGZhbHNlKQ0KICAgIHsNCiAgICAgICAgaWYoICEkdGhpcy0+aXNMb2dnZWRJbiAmJiAhJGxvZ2luICkgDQogICAgICAgIHsNCiAgICAgICAgICAgIHRocm93IG5ldyBFeGNlcHRpb24oIk5vdCBsb2dnZWQgaW5cbiIpOw0KICAgICAgICB9DQoNCiAgICAgICAgJGhlYWRlcnMgPSBhcnJheSggIkNvbm5lY3Rpb246IGNsb3NlIiwgIkFjY2VwdDogKi8qIiwgIlgtSUctQ2FwYWJpbGl0aWVzOiAiIC4gQ29uc3RhbnRzOjpYX0lHX0NhcGFiaWxpdGllcywgIlgtSUctQ29ubmVjdGlvbi1UeXBlOiBXSUZJIiwgIkNvbnRlbnQtdHlwZTogYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkOyBjaGFyc2V0PVVURi04IiwgIkFjY2VwdC1MYW5ndWFnZTogdHItVFIiLCAiWF9GT1JXQVJERURfRk9SOiAiIC4gJHRoaXMtPnNldHRpbmdzLT5nZXQoImlwIiksICJSRU1PVEVfQUREUjogIiAuICR0aGlzLT5zZXR0aW5ncy0+Z2V0KCJpcCIpICk7DQogICAgICAgICRjaCA9IGN1cmxfaW5pdCgpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfVVJMLCBDb25zdGFudHM6OkFQSV9VUkwgLiAkZW5kcG9pbnQpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfVVNFUkFHRU5ULCAkdGhpcy0+c2V0dGluZ3MtPmdldCgidXNlcl9hZ2VudCIpKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1JFVFVSTlRSQU5TRkVSLCB0cnVlKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX0ZPTExPV0xPQ0FUSU9OLCB0cnVlKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX0hFQURFUiwgdHJ1ZSk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9IVFRQSEVBREVSLCAkaGVhZGVycyk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9WRVJCT1NFLCBmYWxzZSk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9TU0xfVkVSSUZZUEVFUiwgZmFsc2UpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfU1NMX1ZFUklGWUhPU1QsIGZhbHNlKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX0NPT0tJRUZJTEUsICR0aGlzLT5JR0RhdGFQYXRoIC4gKHN0cmluZykgJHRoaXMtPnVzZXJuYW1lIC4gIi5kYXQiKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX0NPT0tJRUpBUiwgJHRoaXMtPklHRGF0YVBhdGggLiAoc3RyaW5nKSAkdGhpcy0+dXNlcm5hbWUgLiAiLmRhdCIpOw0KICAgICAgICBpZiggJHBvc3QgKSANCiAgICAgICAgew0KICAgICAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1BPU1QsIHRydWUpOw0KICAgICAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1BPU1RGSUVMRFMsICRwb3N0KTsNCiAgICAgICAgfQ0KDQogICAgICAgICRyZXNwID0gY3VybF9leGVjKCRjaCk7DQogICAgICAgICRoZWFkZXJfbGVuID0gY3VybF9nZXRpbmZvKCRjaCwgQ1VSTElORk9fSEVBREVSX1NJWkUpOw0KICAgICAgICAkaGVhZGVyID0gc3Vic3RyKCRyZXNwLCAwLCAkaGVhZGVyX2xlbik7DQogICAgICAgICRib2R5ID0gc3Vic3RyKCRyZXNwLCAkaGVhZGVyX2xlbik7DQogICAgICAgIGN1cmxfY2xvc2UoJGNoKTsNCiAgICAgICAgaWYoICR0aGlzLT5kZWJ1ZyApIA0KICAgICAgICB7DQogICAgICAgICAgICBlY2hvICJSRVFVRVNUOiAiIC4gJGVuZHBvaW50IC4gIlxuIjsNCiAgICAgICAgICAgIGlmKCAhaXNfbnVsbCgkcG9zdCkgJiYgIWlzX2FycmF5KCRwb3N0KSApIA0KICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgIGVjaG8gIkRBVEE6ICIgLiB1cmxkZWNvZGUoJHBvc3QpIC4gIlxuIjsNCiAgICAgICAgICAgIH0NCg0KICAgICAgICAgICAgZWNobyAiUkVTUE9OU0U6ICIgLiAkYm9keSAuICJcblxuIjsNCiAgICAgICAgfQ0KDQogICAgICAgIHJldHVybiBhcnJheSggJGhlYWRlciwganNvbl9kZWNvZGUoJGJvZHksIHRydWUpICk7DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIElzIFVzZXIgTG9nZ2VkIGluPw0KICAgICAgICAgKiBAcmV0dXJuIGJvb2wNCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gaXNMb2dnZWRJbigpDQogICAgew0KICAgICAgICByZXR1cm4gJHRoaXMtPmlzTG9nZ2VkSW47DQogICAgfQ0KDQp9DQoNCg0KY2xhc3MgSW5zdGFncmFtV2ViDQp7DQogICAgcHJvdGVjdGVkICR1c2VybmFtZSA9IE5VTEw7DQogICAgcHJvdGVjdGVkICR1c2VybmFtZV9pZCA9IE5VTEw7DQogICAgcHJvdGVjdGVkICR0b2tlbiA9IE5VTEw7DQogICAgcHJvdGVjdGVkICRpc0xvZ2dlZEluID0gZmFsc2U7DQogICAgcHJvdGVjdGVkICRJR0RhdGFQYXRoID0gTlVMTDsNCi8qKg0KICAgICAgICAgKiBAdmFyIFNldHRpbmdzDQogICAgICAgICAqLw0KICAgIHB1YmxpYyAkc2V0dGluZ3MgPSBOVUxMOw0KDQogICAgLyoqDQogICAgICAgICAqIERlZmF1bHQgY2xhc3MgY29uc3RydWN0b3IuDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJHVzZXJuYW1lDQogICAgICAgICAqICAgWW91ciBJbnN0YWdyYW0gdXNlcm5hbWUuDQogICAgICAgICAqIEBwYXJhbSAgICAgICAgJElHRGF0YVBhdGgNCiAgICAgICAgICogICBEZWZhdWx0IGZvbGRlciB0byBzdG9yZSBkYXRhLCB5b3UgY2FuIGNoYW5nZSBpdC4NCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gX19jb25zdHJ1Y3QoJHVzZXJuYW1lLCAkSUdEYXRhUGF0aCA9IE5VTEwsICRmb3JjZVVzZXJJUCA9IGZhbHNlKQ0KICAgIHsNCiAgICAgICAgJHVzZXJuYW1lID0gdHJpbSgkdXNlcm5hbWUpOw0KICAgICAgICBpZiggIWlzX251bGwoJElHRGF0YVBhdGgpICkgDQogICAgICAgIHsNCiAgICAgICAgICAgICR0aGlzLT5JR0RhdGFQYXRoID0gJElHRGF0YVBhdGg7DQogICAgICAgIH0NCiAgICAgICAgZWxzZQ0KICAgICAgICB7DQogICAgICAgICAgICAkdGhpcy0+SUdEYXRhUGF0aCA9IFdvdzo6Z2V0KCJwcm9qZWN0L2Nvb2tpZVBhdGgiKSAuICJpbnN0YWdyYW0vIjsNCiAgICAgICAgfQ0KDQogICAgICAgICR0aGlzLT5zZXRVc2VyKCR1c2VybmFtZSwgJGZvcmNlVXNlcklQKTsNCiAgICB9DQoNCiAgICBwcm90ZWN0ZWQgZnVuY3Rpb24gY2hlY2tTZXR0aW5ncygkdXNlcm5hbWUsICRmb3JjZVVzZXJJUCA9IGZhbHNlKQ0KICAgIHsNCiAgICAgICAgJHRoaXMtPnNldHRpbmdzID0gbmV3IFNldHRpbmdzKCR0aGlzLT5JR0RhdGFQYXRoIC4gJHVzZXJuYW1lIC4gIi5jbmYiKTsNCiAgICAgICAgaWYoICR0aGlzLT5zZXR0aW5ncy0+Z2V0KCJpcCIpID09IE5VTEwgfHwgJGZvcmNlVXNlcklQICkgDQogICAgICAgIHsNCiAgICAgICAgICAgICRpcEFkcmVzcyA9ICI3OC4iIC4gcmFuZCgxNjAsIDE5MSkgLiAiLiIgLiByYW5kKDEsIDI1NSkgLiAiLiIgLiByYW5kKDEsIDI1NSk7DQogICAgICAgICAgICBpZiggJGZvcmNlVXNlcklQICYmICFlbXB0eSgkX1NFUlZFUlsiUkVNT1RFX0FERFIiXSkgKSANCiAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAkaXBBZHJlc3MgPSAkX1NFUlZFUlsiUkVNT1RFX0FERFIiXTsNCiAgICAgICAgICAgIH0NCg0KICAgICAgICAgICAgJHRoaXMtPnNldHRpbmdzLT5zZXQoImlwIiwgJGlwQWRyZXNzKTsNCiAgICAgICAgfQ0KDQogICAgICAgIGlmKCBmaWxlX2V4aXN0cygkdGhpcy0+SUdEYXRhUGF0aCAuIChzdHJpbmcpICR1c2VybmFtZSAuICIuZGF0IikgJiYgKCR0aGlzLT5zZXR0aW5ncy0+Z2V0KCJ1c2VybmFtZV9pZCIpID09IE5VTEwgfHwgJHRoaXMtPnNldHRpbmdzLT5nZXQoInRva2VuIikgPT0gTlVMTCkgKSANCiAgICAgICAgew0KICAgICAgICAgICAgJGNvb2tpZURhdGEgPSBVdGlsczo6Y29va2llVG9BcnJheShmaWxlX2dldF9jb250ZW50cygkdGhpcy0+SUdEYXRhUGF0aCAuIChzdHJpbmcpICR1c2VybmFtZSAuICIuZGF0IiksICJ3d3cuaW5zdGFncmFtLmNvbSIpOw0KICAgICAgICAgICAgaWYoICR0aGlzLT5zZXR0aW5ncy0+Z2V0KCJ1c2VybmFtZV9pZCIpID09IE5VTEwgJiYgaXNzZXQoJGNvb2tpZURhdGFbImRzX3VzZXJfaWQiXSkgKSANCiAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAkdGhpcy0+c2V0dGluZ3MtPnNldCgidXNlcm5hbWVfaWQiLCAkY29va2llRGF0YVsiZHNfdXNlcl9pZCJdKTsNCiAgICAgICAgICAgIH0NCg0KICAgICAgICAgICAgaWYoICR0aGlzLT5zZXR0aW5ncy0+Z2V0KCJ0b2tlbiIpID09IE5VTEwgJiYgaXNzZXQoJGNvb2tpZURhdGFbImNzcmZ0b2tlbiJdKSApIA0KICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICR0aGlzLT5zZXR0aW5ncy0+c2V0KCJ0b2tlbiIsICRjb29raWVEYXRhWyJjc3JmdG9rZW4iXSk7DQogICAgICAgICAgICB9DQoNCiAgICAgICAgfQ0KDQogICAgICAgIGlmKCAkdGhpcy0+c2V0dGluZ3MtPmdldCgid2ViX3VzZXJfYWdlbnQiKSA9PSBOVUxMICkgDQogICAgICAgIHsNCiAgICAgICAgICAgICR1c2VyQWdlbnRzID0gZXhwbG9kZShQSFBfRU9MLCBmaWxlX2dldF9jb250ZW50cyhXb3c6OmdldCgicHJvamVjdC9jb29raWVQYXRoIikgLiAiZGV2aWNlL2Jyb3dzZXJzLmNzdiIpKTsNCiAgICAgICAgICAgICRhZ2VudEluZGV4ID0gcmFuZCgwLCBjb3VudCgkdXNlckFnZW50cykgLSAxKTsNCiAgICAgICAgICAgICR1c2VyQWdlbnQgPSAkdXNlckFnZW50c1skYWdlbnRJbmRleF07DQogICAgICAgICAgICAkdGhpcy0+c2V0dGluZ3MtPnNldCgid2ViX3VzZXJfYWdlbnQiLCAkdXNlckFnZW50KTsNCiAgICAgICAgfQ0KDQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIFNldCB0aGUgdXNlci4gTWFuYWdlIG11bHRpcGxlIGFjY291bnRzLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICR1c2VybmFtZQ0KICAgICAgICAgKiAgIFlvdXIgSW5zdGFncmFtIHVzZXJuYW1lLg0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBzZXRVc2VyKCR1c2VybmFtZSwgJGZvcmNlVXNlcklQID0gZmFsc2UpDQogICAgew0KICAgICAgICAkdGhpcy0+dXNlcm5hbWUgPSAkdXNlcm5hbWU7DQogICAgICAgICR0aGlzLT5jaGVja1NldHRpbmdzKCR1c2VybmFtZSwgJGZvcmNlVXNlcklQKTsNCiAgICAgICAgaWYoIGZpbGVfZXhpc3RzKCR0aGlzLT5JR0RhdGFQYXRoIC4gKHN0cmluZykgJHRoaXMtPnVzZXJuYW1lIC4gIi5kYXQiKSAmJiAkdGhpcy0+c2V0dGluZ3MtPmdldCgidXNlcm5hbWVfaWQiKSAhPSBOVUxMICYmICR0aGlzLT5zZXR0aW5ncy0+Z2V0KCJ0b2tlbiIpICE9IE5VTEwgKSANCiAgICAgICAgew0KICAgICAgICAgICAgJHRoaXMtPmlzTG9nZ2VkSW4gPSB0cnVlOw0KICAgICAgICAgICAgJHRoaXMtPnVzZXJuYW1lX2lkID0gJHRoaXMtPnNldHRpbmdzLT5nZXQoInVzZXJuYW1lX2lkIik7DQogICAgICAgICAgICAkdGhpcy0+dG9rZW4gPSAkdGhpcy0+c2V0dGluZ3MtPmdldCgidG9rZW4iKTsNCiAgICAgICAgfQ0KICAgICAgICBlbHNlDQogICAgICAgIHsNCiAgICAgICAgICAgICR0aGlzLT5pc0xvZ2dlZEluID0gZmFsc2U7DQogICAgICAgIH0NCg0KICAgIH0NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBnZXREYXRhKCkNCiAgICB7DQogICAgICAgIHJldHVybiBhcnJheSggInVzZXJuYW1lIiA9PiAkdGhpcy0+dXNlcm5hbWUsICJ1c2VybmFtZV9pZCIgPT4gJHRoaXMtPnVzZXJuYW1lX2lkLCAidG9rZW4iID0+ICR0aGlzLT50b2tlbiwgInVzZXJfYWdlbnQiID0+ICR0aGlzLT5zZXR0aW5ncy0+Z2V0KCJ3ZWJfdXNlcl9hZ2VudCIpLCAiaXAiID0+ICR0aGlzLT5zZXR0aW5ncy0+Z2V0KCJpcCIpICk7DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIENvbW1lbnQgbWVkaWEuDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJG1lZGlhSWQNCiAgICAgICAgICogICBNZWRpYSBpZA0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICRjb21tZW50VGV4dA0KICAgICAgICAgKiAgIENvbW1lbnQgVGV4dA0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgY29tbWVudCBtZWRpYSBkYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGNvbW1lbnQoJG1lZGlhSWQsICRjb21tZW50VGV4dCkNCiAgICB7DQogICAgICAgICRhcnJNZWRpYUlEID0gZXhwbG9kZSgiXyIsICRtZWRpYUlkKTsNCiAgICAgICAgJG1lZGlhSWQgPSAkYXJyTWVkaWFJRFswXTsNCiAgICAgICAgJHBvc3REYXRhID0gImNvbW1lbnRfdGV4dD0iIC4gJGNvbW1lbnRUZXh0Ow0KICAgICAgICAkaGVhZGVycyA9IGFycmF5KCAgKTsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJSZWZlcmVyOiBodHRwczovL3d3dy5pbnN0YWdyYW0uY29tLyI7DQogICAgICAgICRoZWFkZXJzW10gPSAiRE5UOiAxIjsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJPcmlnaW46IGh0dHBzOi8vd3d3Lmluc3RhZ3JhbS5jb20vIjsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJYLUNTUkZUb2tlbjogIiAuIHRyaW0oJHRoaXMtPnRva2VuKTsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJYLVJlcXVlc3RlZC1XaXRoOiBYTUxIdHRwUmVxdWVzdCI7DQogICAgICAgICRoZWFkZXJzW10gPSAiWC1JbnN0YWdyYW0tQUpBWDogMSI7DQogICAgICAgICRoZWFkZXJzW10gPSAiQ29ubmVjdGlvbjogY2xvc2UiOw0KICAgICAgICAkaGVhZGVyc1tdID0gIkNhY2hlLUNvbnRyb2w6IG1heC1hZ2U9MCI7DQogICAgICAgIHJldHVybiAkdGhpcy0+cmVxdWVzdCgid2ViL2NvbW1lbnRzLyIgLiAkbWVkaWFJZCAuICIvYWRkLyIsICRoZWFkZXJzLCAkcG9zdERhdGEpWzFdOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBHZXQgdXNlcm5hbWUgaW5mby4NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkdXNlcm5hbWUNCiAgICAgICAgICogICBVc2VybmFtZQ0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgVXNlcm5hbWUgZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBnZXRVc2VybmFtZUluZm8oJHVzZXJuYW1lKQ0KICAgIHsNCiAgICAgICAgJGhlYWRlcnMgPSBhcnJheSggICk7DQogICAgICAgICRoZWFkZXJzW10gPSAiUmVmZXJlcjogaHR0cHM6Ly93d3cuaW5zdGFncmFtLmNvbS8iOw0KICAgICAgICAkaGVhZGVyc1tdID0gIkROVDogMSI7DQogICAgICAgICRoZWFkZXJzW10gPSAiT3JpZ2luOiBodHRwczovL3d3dy5pbnN0YWdyYW0uY29tLyI7DQogICAgICAgICRoZWFkZXJzW10gPSAiWC1DU1JGVG9rZW46ICIgLiB0cmltKCR0aGlzLT50b2tlbik7DQogICAgICAgICRoZWFkZXJzW10gPSAiWC1SZXF1ZXN0ZWQtV2l0aDogWE1MSHR0cFJlcXVlc3QiOw0KICAgICAgICAkaGVhZGVyc1tdID0gIlgtSW5zdGFncmFtLUFKQVg6IDEiOw0KICAgICAgICAkaGVhZGVyc1tdID0gIkNvbm5lY3Rpb246IGNsb3NlIjsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJDYWNoZS1Db250cm9sOiBtYXgtYWdlPTAiOw0KICAgICAgICByZXR1cm4gJHRoaXMtPnJlcXVlc3QoKHN0cmluZykgJHVzZXJuYW1lIC4gIi8/X19hPTEiLCAkaGVhZGVycylbMV07DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIE1lZGlhIGluZm8NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkbWVkaWFDb2RlDQogICAgICAgICAqICAgTWVkaWEgY29kZQ0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgZGVsZXRlIHJlcXVlc3QgZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBtZWRpYUluZm8oJG1lZGlhQ29kZSkNCiAgICB7DQogICAgICAgICRoZWFkZXJzID0gYXJyYXkoICApOw0KICAgICAgICAkaGVhZGVyc1tdID0gIlJlZmVyZXI6IGh0dHBzOi8vd3d3Lmluc3RhZ3JhbS5jb20vIjsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJETlQ6IDEiOw0KICAgICAgICAkaGVhZGVyc1tdID0gIk9yaWdpbjogaHR0cHM6Ly93d3cuaW5zdGFncmFtLmNvbS8iOw0KICAgICAgICAkaGVhZGVyc1tdID0gIlgtQ1NSRlRva2VuOiAiIC4gdHJpbSgkdGhpcy0+dG9rZW4pOw0KICAgICAgICAkaGVhZGVyc1tdID0gIlgtUmVxdWVzdGVkLVdpdGg6IFhNTEh0dHBSZXF1ZXN0IjsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJYLUluc3RhZ3JhbS1BSkFYOiAxIjsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJDb25uZWN0aW9uOiBjbG9zZSI7DQogICAgICAgICRoZWFkZXJzW10gPSAiQ2FjaGUtQ29udHJvbDogbWF4LWFnZT0wIjsNCiAgICAgICAgcmV0dXJuICR0aGlzLT5yZXF1ZXN0KCJwLyIgLiAkbWVkaWFDb2RlIC4gIi8/X19hPTEiLCAkaGVhZGVycylbMV07DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIExpa2UgcGhvdG8gb3IgdmlkZW8uDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJG1lZGlhSWQNCiAgICAgICAgICogICBNZWRpYSBpZA0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgc3RhdHVzIHJlcXVlc3QNCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gbGlrZSgkbWVkaWFJZCkNCiAgICB7DQogICAgICAgICRhcnJNZWRpYUlEID0gZXhwbG9kZSgiXyIsICRtZWRpYUlkKTsNCiAgICAgICAgJG1lZGlhSWQgPSAkYXJyTWVkaWFJRFswXTsNCiAgICAgICAgJGhlYWRlcnMgPSBhcnJheSggICk7DQogICAgICAgICRoZWFkZXJzW10gPSAiUmVmZXJlcjogaHR0cHM6Ly93d3cuaW5zdGFncmFtLmNvbS9pbnN0YWdyYW0vIjsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJETlQ6IDEiOw0KICAgICAgICAkaGVhZGVyc1tdID0gIk9yaWdpbjogaHR0cHM6Ly93d3cuaW5zdGFncmFtLmNvbS8iOw0KICAgICAgICAkaGVhZGVyc1tdID0gIlgtQ1NSRlRva2VuOiAiIC4gdHJpbSgkdGhpcy0+dG9rZW4pOw0KICAgICAgICAkaGVhZGVyc1tdID0gIlgtUmVxdWVzdGVkLVdpdGg6IFhNTEh0dHBSZXF1ZXN0IjsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJYLUluc3RhZ3JhbS1BSkFYOiAxIjsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJDb25uZWN0aW9uOiBjbG9zZSI7DQogICAgICAgICRoZWFkZXJzW10gPSAiQ2FjaGUtQ29udHJvbDogbWF4LWFnZT0wIjsNCiAgICAgICAgcmV0dXJuICR0aGlzLT5yZXF1ZXN0KCJ3ZWIvbGlrZXMvIiAuICRtZWRpYUlkIC4gIi9saWtlLyIsICRoZWFkZXJzLCB0cnVlKVsxXTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogVW5saWtlIHBob3RvIG9yIHZpZGVvLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICRtZWRpYUlkDQogICAgICAgICAqICAgTWVkaWEgaWQNCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIHN0YXR1cyByZXF1ZXN0DQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIHVubGlrZSgkbWVkaWFJZCkNCiAgICB7DQogICAgICAgICRoZWFkZXJzID0gYXJyYXkoICApOw0KICAgICAgICAkaGVhZGVyc1tdID0gIlJlZmVyZXI6IGh0dHBzOi8vd3d3Lmluc3RhZ3JhbS5jb20vIjsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJETlQ6IDEiOw0KICAgICAgICAkaGVhZGVyc1tdID0gIk9yaWdpbjogaHR0cHM6Ly93d3cuaW5zdGFncmFtLmNvbS8iOw0KICAgICAgICAkaGVhZGVyc1tdID0gIlgtQ1NSRlRva2VuOiAiIC4gdHJpbSgkdGhpcy0+dG9rZW4pOw0KICAgICAgICAkaGVhZGVyc1tdID0gIlgtUmVxdWVzdGVkLVdpdGg6IFhNTEh0dHBSZXF1ZXN0IjsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJYLUluc3RhZ3JhbS1BSkFYOiAxIjsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJDb25uZWN0aW9uOiBjbG9zZSI7DQogICAgICAgICRoZWFkZXJzW10gPSAiQ2FjaGUtQ29udHJvbDogbWF4LWFnZT0wIjsNCiAgICAgICAgcmV0dXJuICR0aGlzLT5yZXF1ZXN0KCJ3ZWIvbGlrZXMvIiAuICRtZWRpYUlkIC4gIi91bmxpa2UvIiwgJGhlYWRlcnMsIHRydWUpWzFdOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBGb2xsb3cuDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJHVzZXJJZA0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgRnJpZW5kc2hpcCBzdGF0dXMgZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBmb2xsb3coJHVzZXJJZCkNCiAgICB7DQogICAgICAgICRoZWFkZXJzID0gYXJyYXkoICApOw0KICAgICAgICAkaGVhZGVyc1tdID0gIlJlZmVyZXI6IGh0dHBzOi8vd3d3Lmluc3RhZ3JhbS5jb20vaW5zdGFncmFtLyI7DQogICAgICAgICRoZWFkZXJzW10gPSAiRE5UOiAxIjsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJPcmlnaW46IGh0dHBzOi8vd3d3Lmluc3RhZ3JhbS5jb20vIjsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJYLUNTUkZUb2tlbjogIiAuIHRyaW0oJHRoaXMtPnRva2VuKTsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJYLVJlcXVlc3RlZC1XaXRoOiBYTUxIdHRwUmVxdWVzdCI7DQogICAgICAgICRoZWFkZXJzW10gPSAiWC1JbnN0YWdyYW0tQUpBWDogMSI7DQogICAgICAgICRoZWFkZXJzW10gPSAiQ29ubmVjdGlvbjogY2xvc2UiOw0KICAgICAgICAkaGVhZGVyc1tdID0gIkNhY2hlLUNvbnRyb2w6IG1heC1hZ2U9MCI7DQogICAgICAgIHJldHVybiAkdGhpcy0+cmVxdWVzdCgid2ViL2ZyaWVuZHNoaXBzLyIgLiAkdXNlcklkIC4gIi9mb2xsb3cvIiwgJGhlYWRlcnMsIHRydWUpWzFdOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBVbmZvbGxvdy4NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkdXNlcklkDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBGcmllbmRzaGlwIHN0YXR1cyBkYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIHVuZm9sbG93KCR1c2VySWQpDQogICAgew0KICAgICAgICAkaGVhZGVycyA9IGFycmF5KCAgKTsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJSZWZlcmVyOiBodHRwczovL3d3dy5pbnN0YWdyYW0uY29tL2luc3RhZ3JhbS8iOw0KICAgICAgICAkaGVhZGVyc1tdID0gIkROVDogMSI7DQogICAgICAgICRoZWFkZXJzW10gPSAiT3JpZ2luOiBodHRwczovL3d3dy5pbnN0YWdyYW0uY29tLyI7DQogICAgICAgICRoZWFkZXJzW10gPSAiWC1DU1JGVG9rZW46ICIgLiB0cmltKCR0aGlzLT50b2tlbik7DQogICAgICAgICRoZWFkZXJzW10gPSAiWC1SZXF1ZXN0ZWQtV2l0aDogWE1MSHR0cFJlcXVlc3QiOw0KICAgICAgICAkaGVhZGVyc1tdID0gIlgtSW5zdGFncmFtLUFKQVg6IDEiOw0KICAgICAgICAkaGVhZGVyc1tdID0gIkNvbm5lY3Rpb246IGNsb3NlIjsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJDYWNoZS1Db250cm9sOiBtYXgtYWdlPTAiOw0KICAgICAgICByZXR1cm4gJHRoaXMtPnJlcXVlc3QoIndlYi9mcmllbmRzaGlwcy8iIC4gJHVzZXJJZCAuICIvdW5mb2xsb3cvIiwgJGhlYWRlcnMsIHRydWUpWzFdOw0KICAgIH0NCg0KICAgIHByb3RlY3RlZCBmdW5jdGlvbiByZXF1ZXN0KCRlbmRwb2ludCwgYXJyYXkgJG9wdGlvbmFsaGVhZGVycywgJHBvc3QgPSBOVUxMKQ0KICAgIHsNCiAgICAgICAgaWYoICEkdGhpcy0+aXNMb2dnZWRJbiApIA0KICAgICAgICB7DQogICAgICAgICAgICB0aHJvdyBuZXcgRXhjZXB0aW9uKCJOb3QgbG9nZ2VkIGluXG4iKTsNCiAgICAgICAgfQ0KDQogICAgICAgICRoZWFkZXJzID0gYXJyYXkoICJBY2NlcHQ6ICovKiIsICJBY2NlcHQtTGFuZ3VhZ2U6IHRyO3E9MSIsICJYX0ZPUldBUkRFRF9GT1I6ICIgLiAkdGhpcy0+c2V0dGluZ3MtPmdldCgiaXAiKSwgIlJFTU9URV9BRERSOiAiIC4gJHRoaXMtPnNldHRpbmdzLT5nZXQoImlwIikgKTsNCiAgICAgICAgJGhlYWRlcnMgPSBhcnJheV9tZXJnZSgkaGVhZGVycywgJG9wdGlvbmFsaGVhZGVycyk7DQogICAgICAgICRjaCA9IGN1cmxfaW5pdCgpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfVVJMLCBDb25zdGFudHM6OldFQl9VUkwgLiAkZW5kcG9pbnQpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfVVNFUkFHRU5ULCAkdGhpcy0+c2V0dGluZ3MtPmdldCgid2ViX3VzZXJfYWdlbnQiKSk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9SRVRVUk5UUkFOU0ZFUiwgdHJ1ZSk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9GT0xMT1dMT0NBVElPTiwgdHJ1ZSk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9IRUFERVIsIHRydWUpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfSFRUUEhFQURFUiwgJGhlYWRlcnMpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfVkVSQk9TRSwgZmFsc2UpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfU1NMX1ZFUklGWVBFRVIsIGZhbHNlKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1NTTF9WRVJJRllIT1NULCBmYWxzZSk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9DT09LSUVGSUxFLCAkdGhpcy0+SUdEYXRhUGF0aCAuIChzdHJpbmcpICR0aGlzLT51c2VybmFtZSAuICIuZGF0Iik7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9DT09LSUVKQVIsICR0aGlzLT5JR0RhdGFQYXRoIC4gKHN0cmluZykgJHRoaXMtPnVzZXJuYW1lIC4gIi5kYXQiKTsNCiAgICAgICAgaWYoICRwb3N0ICkgDQogICAgICAgIHsNCiAgICAgICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9QT1NULCB0cnVlKTsNCiAgICAgICAgICAgIGlmKCBpc19zdHJpbmcoJHBvc3QpICkgDQogICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1BPU1RGSUVMRFMsICRwb3N0KTsNCiAgICAgICAgICAgIH0NCg0KICAgICAgICB9DQoNCiAgICAgICAgJHJlc3AgPSBjdXJsX2V4ZWMoJGNoKTsNCiAgICAgICAgJGhlYWRlcl9sZW4gPSBjdXJsX2dldGluZm8oJGNoLCBDVVJMSU5GT19IRUFERVJfU0laRSk7DQogICAgICAgICRoZWFkZXIgPSBzdWJzdHIoJHJlc3AsIDAsICRoZWFkZXJfbGVuKTsNCiAgICAgICAgJGJvZHkgPSBzdWJzdHIoJHJlc3AsICRoZWFkZXJfbGVuKTsNCiAgICAgICAgY3VybF9jbG9zZSgkY2gpOw0KICAgICAgICByZXR1cm4gYXJyYXkoICRoZWFkZXIsIGpzb25fZGVjb2RlKCRib2R5LCB0cnVlKSApOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBJcyBVc2VyIExvZ2dlZCBpbj8NCiAgICAgICAgICogQHJldHVybiBib29sDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGlzTG9nZ2VkSW4oKQ0KICAgIHsNCiAgICAgICAgcmV0dXJuICR0aGlzLT5pc0xvZ2dlZEluOw0KICAgIH0NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBpc1ZhbGlkKCkNCiAgICB7DQogICAgICAgICRoZWFkZXJzID0gYXJyYXkoICApOw0KICAgICAgICAkaGVhZGVyc1tdID0gIlJlZmVyZXI6IGh0dHBzOi8vd3d3Lmluc3RhZ3JhbS5jb20vIjsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJETlQ6IDEiOw0KICAgICAgICAkaGVhZGVyc1tdID0gIk9yaWdpbjogaHR0cHM6Ly93d3cuaW5zdGFncmFtLmNvbS8iOw0KICAgICAgICAkaGVhZGVyc1tdID0gIlgtQ1NSRlRva2VuOiAiIC4gdHJpbSgkdGhpcy0+dG9rZW4pOw0KICAgICAgICAkaGVhZGVyc1tdID0gIlgtUmVxdWVzdGVkLVdpdGg6IFhNTEh0dHBSZXF1ZXN0IjsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJYLUluc3RhZ3JhbS1BSkFYOiAxIjsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJDb25uZWN0aW9uOiBjbG9zZSI7DQogICAgICAgICRoZWFkZXJzW10gPSAiQ2FjaGUtQ29udHJvbDogbWF4LWFnZT0wIjsNCiAgICAgICAgbGlzdCgkaGVhZGVyKSA9ICR0aGlzLT5yZXF1ZXN0KCJhY2NvdW50cy9hY3Rpdml0eS8/X19hPTEiLCAkaGVhZGVycyk7DQogICAgICAgIHJldHVybiAoc3RycG9zKCRoZWFkZXIsICJIVFRQLzEuMSAyMDAiKSA9PT0gZmFsc2UgPyBmYWxzZSA6IHRydWUpOw0KICAgIH0NCg0KfQ0KDQoNCmNsYXNzIFNpZ25hdHVyZVV0aWxzDQp7DQogICAgcHVibGljIHN0YXRpYyBmdW5jdGlvbiBnZW5lcmF0ZVNpZ25hdHVyZSgkZGF0YSkNCiAgICB7DQogICAgICAgICRoYXNoID0gaGFzaF9obWFjKCJzaGEyNTYiLCAkZGF0YSwgQ29uc3RhbnRzOjpJR19TSUdfS0VZKTsNCiAgICAgICAgcmV0dXJuICJpZ19zaWdfa2V5X3ZlcnNpb249IiAuIENvbnN0YW50czo6U0lHX0tFWV9WRVJTSU9OIC4gIiZzaWduZWRfYm9keT0iIC4gJGhhc2ggLiAiLiIgLiB1cmxlbmNvZGUoJGRhdGEpOw0KICAgIH0NCg0KICAgIHB1YmxpYyBzdGF0aWMgZnVuY3Rpb24gZ2VuZXJhdGVEZXZpY2VJZCgkc2VlZCkNCiAgICB7DQogICAgICAgICR2b2xhdGlsZV9zZWVkID0gZmlsZW10aW1lKF9fRElSX18pOw0KICAgICAgICByZXR1cm4gImFuZHJvaWQtIiAuIHN1YnN0cihtZDUoJHNlZWQgLiAkdm9sYXRpbGVfc2VlZCksIDE2KTsNCiAgICB9DQoNCiAgICBwdWJsaWMgc3RhdGljIGZ1bmN0aW9uIGdlbmVyYXRlVVVJRCgkdHlwZSkNCiAgICB7DQogICAgICAgICR1dWlkID0gc3ByaW50ZigiJTA0eCUwNHgtJTA0eC0lMDR4LSUwNHgtJTA0eCUwNHglMDR4IiwgbXRfcmFuZCgwLCA2NTUzNSksIG10X3JhbmQoMCwgNjU1MzUpLCBtdF9yYW5kKDAsIDY1NTM1KSwgbXRfcmFuZCgwLCA0MDk1KSB8IDE2Mzg0LCBtdF9yYW5kKDAsIDE2MzgzKSB8IDMyNzY4LCBtdF9yYW5kKDAsIDY1NTM1KSwgbXRfcmFuZCgwLCA2NTUzNSksIG10X3JhbmQoMCwgNjU1MzUpKTsNCiAgICAgICAgcmV0dXJuICgkdHlwZSA/ICR1dWlkIDogc3RyX3JlcGxhY2UoIi0iLCAiIiwgJHV1aWQpKTsNCiAgICB9DQoNCn0NCg0KDQpjbGFzcyBVc2VyQWdlbnQNCnsNCiAgICBwcm90ZWN0ZWQgJHBhcmVudCA9IE5VTEw7DQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gX19jb25zdHJ1Y3QoJHBhcmVudCkNCiAgICB7DQogICAgICAgICR0aGlzLT5wYXJlbnQgPSAkcGFyZW50Ow0KICAgIH0NCg0KICAgIHByb3RlY3RlZCBmdW5jdGlvbiBnZXREZXZpY2VEYXRhKCkNCiAgICB7DQogICAgICAgICRjc3ZmaWxlID0gV293OjpnZXQoInByb2plY3QvY29va2llUGF0aCIpIC4gImRldmljZS9kZXZpY2VzLmNzdiI7DQogICAgICAgICRmaWxlX2hhbmRsZSA9IGZvcGVuKCRjc3ZmaWxlLCAiciIpOw0KICAgICAgICAkbGluZV9vZl90ZXh0ID0gYXJyYXkoICApOw0KICAgICAgICB3aGlsZSggIWZlb2YoJGZpbGVfaGFuZGxlKSApIA0KICAgICAgICB7DQogICAgICAgICAgICAkbGluZV9vZl90ZXh0W10gPSBmZ2V0Y3N2KCRmaWxlX2hhbmRsZSwgMTAyNCk7DQogICAgICAgIH0NCiAgICAgICAgJGRldmljZURhdGEgPSBleHBsb2RlKCI7IiwgJGxpbmVfb2ZfdGV4dFttdF9yYW5kKDAsIDExODY3KV1bMF0pOw0KICAgICAgICBmY2xvc2UoJGZpbGVfaGFuZGxlKTsNCiAgICAgICAgcmV0dXJuICRkZXZpY2VEYXRhOw0KICAgIH0NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBidWlsZFVzZXJBZ2VudCgpDQogICAgew0KICAgICAgICAkZGV2aWNlRGF0YSA9ICR0aGlzLT5nZXREZXZpY2VEYXRhKCk7DQogICAgICAgICR0aGlzLT5wYXJlbnQtPnNldHRpbmdzLT5zZXQoIm1hbnVmYWN0dXJlciIsICRkZXZpY2VEYXRhWzBdKTsNCiAgICAgICAgJHRoaXMtPnBhcmVudC0+c2V0dGluZ3MtPnNldCgiZGV2aWNlIiwgJGRldmljZURhdGFbMV0pOw0KICAgICAgICAkdGhpcy0+cGFyZW50LT5zZXR0aW5ncy0+c2V0KCJtb2RlbCIsICRkZXZpY2VEYXRhWzJdKTsNCiAgICAgICAgJGxvY2FsZUxhbmcgPSAkdGhpcy0+ZGV0ZWN0VXNlckxvY2FsZSgpOw0KICAgICAgICByZXR1cm4gc3ByaW50ZigiSW5zdGFncmFtICVzIEFuZHJvaWQgKDE4LzQuMzsgMzIwZHBpOyA3MjB4MTI4MDsgJXM7ICVzOyAlczsgcWNvbTsgIiAuICRsb2NhbGVMYW5nIC4gIikiLCBDb25zdGFudHM6OlZFUlNJT04sICRkZXZpY2VEYXRhWzBdLCAkZGV2aWNlRGF0YVsxXSwgJGRldmljZURhdGFbMl0pOw0KICAgIH0NCg0KICAgIHByaXZhdGUgZnVuY3Rpb24gZGV0ZWN0VXNlckxvY2FsZSgkZmFsbGJhY2tMb2NhbGUgPSAiZW5fVVMiKQ0KICAgIHsNCiAgICAgICAgJHVzZXJMb2NhbGUgPSBOVUxMOw0KICAgICAgICBpZiggaXNzZXQoJF9TRVJWRVJbIkhUVFBfQUNDRVBUX0xBTkdVQUdFIl0pICkgDQogICAgICAgIHsNCiAgICAgICAgICAgICRhcnJMYW5nID0gZXhwbG9kZSgiLCIsICRfU0VSVkVSWyJIVFRQX0FDQ0VQVF9MQU5HVUFHRSJdKTsNCiAgICAgICAgICAgICRwcmVmZXJyZWRMYW5ncyA9IGFycmF5KCAgKTsNCiAgICAgICAgICAgIGZvcmVhY2goICRhcnJMYW5nIGFzICRzdHJMYW5nICkgDQogICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgJGFyckxhbmdEZXRhaWxzID0gZXhwbG9kZSgiO3E9IiwgJHN0ckxhbmcpOw0KICAgICAgICAgICAgICAgICRsYW5nQ29kZSA9IHN0cl9yZXBsYWNlKCItIiwgIl8iLCAkYXJyTGFuZ0RldGFpbHNbMF0pOw0KICAgICAgICAgICAgICAgICRsYW5nUHJpb3JpdHkgPSBmbG9hdHZhbCgoaXNzZXQoJGFyckxhbmdEZXRhaWxzWzFdKSA/ICRhcnJMYW5nRGV0YWlsc1sxXSA6IDEpKTsNCiAgICAgICAgICAgICAgICAkcHJlZmVycmVkTGFuZ3NbJGxhbmdDb2RlXSA9ICRsYW5nUHJpb3JpdHk7DQogICAgICAgICAgICB9DQogICAgICAgICAgICBhcnNvcnQoJHByZWZlcnJlZExhbmdzKTsNCiAgICAgICAgICAgIHJlc2V0KCRwcmVmZXJyZWRMYW5ncyk7DQogICAgICAgICAgICAkdXNlckxvY2FsZSA9IGtleSgkcHJlZmVycmVkTGFuZ3MpOw0KICAgICAgICB9DQoNCiAgICAgICAgcmV0dXJuIChpc19udWxsKCR1c2VyTG9jYWxlKSA/ICRmYWxsYmFja0xvY2FsZSA6ICR1c2VyTG9jYWxlKTsNCiAgICB9DQoNCn0NCg0KDQpjbGFzcyBVdGlscw0Kew0KICAgIC8qKg0KICAgICAgICAgKiBMZW5ndGggb2YgdGhlIGZpbGUgaW4gU2Vjb25kcy4NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkZmlsZQ0KICAgICAgICAgKiAgICAgICAgICAgICAgICAgICAgIHBhdGggdG8gdGhlIGZpbGUgbmFtZQ0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGludA0KICAgICAgICAgKiAgICAgICAgICAgICBsZW5ndGggb2YgdGhlIGZpbGUgaW4gc2Vjb25kcw0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBzdGF0aWMgZnVuY3Rpb24gZ2V0U2Vjb25kcygkZmlsZSkNCiAgICB7DQogICAgICAgICRmZm1wZWcgPSBzZWxmOjpjaGVja0ZGTVBFRygpOw0KICAgICAgICBpZiggJGZmbXBlZyApIA0KICAgICAgICB7DQogICAgICAgICAgICAkdGltZSA9IGV4ZWMoKHN0cmluZykgJGZmbXBlZyAuICIgLWkgIiAuICRmaWxlIC4gIiAyPiYxIHwgZ3JlcCAnRHVyYXRpb24nIHwgY3V0IC1kICcgJyAtZiA0Iik7DQogICAgICAgICAgICAkZHVyYXRpb24gPSBleHBsb2RlKCI6IiwgJHRpbWUpOw0KICAgICAgICAgICAgJHNlY29uZHMgPSAkZHVyYXRpb25bMF0gKiAzNjAwICsgJGR1cmF0aW9uWzFdICogNjAgKyByb3VuZCgkZHVyYXRpb25bMl0pOw0KICAgICAgICAgICAgcmV0dXJuICRzZWNvbmRzOw0KICAgICAgICB9DQoNCiAgICAgICAgcmV0dXJuIG10X3JhbmQoMTUsIDMwMCk7DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIENoZWNrIGZvciBmZm1wZWcvYXZjb252IGRlcGVuZGVuY2llcy4NCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBzdHJpbmcvYm9vbGVhbg0KICAgICAgICAgKiAgICAgICAgICAgICAgICAgICAgICAgIG5hbWUgb2YgdGhlIGxpYnJhcnkgaWYgcHJlc2VudCwgZmFsc2Ugb3RoZXJ3aXNlDQogICAgICAgICAqLw0KDQogICAgcHVibGljIHN0YXRpYyBmdW5jdGlvbiBjaGVja0ZGTVBFRygpDQogICAgew0KICAgICAgICBAZXhlYygiZmZtcGVnIC12ZXJzaW9uIDI+JjEiLCAkb3V0cHV0LCAkcmV0dXJudmFsdWUpOw0KICAgICAgICBpZiggJHJldHVybnZhbHVlID09PSAwICkgDQogICAgICAgIHsNCiAgICAgICAgICAgIHJldHVybiAiZmZtcGVnIjsNCiAgICAgICAgfQ0KDQogICAgICAgIEBleGVjKCJhdmNvbnYgLXZlcnNpb24gMj4mMSIsICRvdXRwdXQsICRyZXR1cm52YWx1ZSk7DQogICAgICAgIGlmKCAkcmV0dXJudmFsdWUgPT09IDAgKSANCiAgICAgICAgew0KICAgICAgICAgICAgcmV0dXJuICJhdmNvbnYiOw0KICAgICAgICB9DQoNCiAgICAgICAgcmV0dXJuIGZhbHNlOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBDcmVhdGluZyBhIHZpZGVvIGljb24vdGh1bWJuYWlsLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICRmaWxlDQogICAgICAgICAqICAgICAgICAgICAgICAgICAgICAgcGF0aCB0byB0aGUgdmlkZW8gZmlsZQ0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGltYWdlDQogICAgICAgICAqICAgICAgICAgICAgICAgaWNvbi90aHVtYm5haWwgZm9yIHRoZSB2aWRlbw0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBzdGF0aWMgZnVuY3Rpb24gY3JlYXRlVmlkZW9JY29uKCRmaWxlKQ0KICAgIHsNCiAgICAgICAgJGZmbXBlZyA9IHNlbGY6OmNoZWNrRkZNUEVHKCk7DQogICAgICAgIGlmKCAkZmZtcGVnICkgDQogICAgICAgIHsNCiAgICAgICAgICAgICRwcmV2aWV3ID0gc3lzX2dldF90ZW1wX2RpcigpIC4gIi8iIC4gbWQ1KCRmaWxlKSAuICIuanBnIjsNCiAgICAgICAgICAgIEB1bmxpbmsoJHByZXZpZXcpOw0KICAgICAgICAgICAgJGNvbW1hbmQgPSAkZmZtcGVnIC4gIiAtaSBcIiIgLiAkZmlsZSAuICJcIiAtZiBtanBlZyAtc3MgMDA6MDA6MDEgLXZmcmFtZXMgMSBcIiIgLiAkcHJldmlldyAuICJcIiAyPiYxIjsNCiAgICAgICAgICAgIEBleGVjKCRjb21tYW5kKTsNCiAgICAgICAgICAgIHJldHVybiBmaWxlX2dldF9jb250ZW50cygkcHJldmlldyk7DQogICAgICAgIH0NCg0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBJbXBsZW1lbnRzIHRoZSBhY3R1YWwgbG9naWMgYmVoaW5kIGNyZWF0aW5nIHRoZSBpY29uL3RodW1ibmFpbC4NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkZmlsZQ0KICAgICAgICAgKiAgICAgICAgICAgICAgICAgICAgIHBhdGggdG8gdGhlIGZpbGUgbmFtZQ0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGltYWdlDQogICAgICAgICAqICAgICAgICAgICAgICAgaWNvbi90aHVtYm5haWwgZm9yIHRoZSB2aWRlbw0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBzdGF0aWMgZnVuY3Rpb24gY3JlYXRlSWNvbkdEKCRmaWxlLCAkc2l6ZSA9IDEwMCwgJHJhdyA9IHRydWUpDQogICAgew0KICAgICAgICBsaXN0KCR3aWR0aCwgJGhlaWdodCkgPSBnZXRpbWFnZXNpemUoJGZpbGUpOw0KICAgICAgICBpZiggJGhlaWdodCA8ICR3aWR0aCApIA0KICAgICAgICB7DQogICAgICAgICAgICAkeSA9IDA7DQogICAgICAgICAgICAkeCA9ICgkd2lkdGggLSAkaGVpZ2h0KSAvIDI7DQogICAgICAgICAgICAkc21hbGxlc3RTaWRlID0gJGhlaWdodDsNCiAgICAgICAgfQ0KICAgICAgICBlbHNlDQogICAgICAgIHsNCiAgICAgICAgICAgICR4ID0gMDsNCiAgICAgICAgICAgICR5ID0gKCRoZWlnaHQgLSAkd2lkdGgpIC8gMjsNCiAgICAgICAgICAgICRzbWFsbGVzdFNpZGUgPSAkd2lkdGg7DQogICAgICAgIH0NCg0KICAgICAgICAkaW1hZ2VfcCA9IGltYWdlY3JlYXRldHJ1ZWNvbG9yKCRzaXplLCAkc2l6ZSk7DQogICAgICAgICRpbWFnZSA9IGltYWdlY3JlYXRlZnJvbXN0cmluZyhmaWxlX2dldF9jb250ZW50cygkZmlsZSkpOw0KICAgICAgICBpbWFnZWNvcHlyZXNhbXBsZWQoJGltYWdlX3AsICRpbWFnZSwgMCwgMCwgJHgsICR5LCAkc2l6ZSwgJHNpemUsICRzbWFsbGVzdFNpZGUsICRzbWFsbGVzdFNpZGUpOw0KICAgICAgICBvYl9zdGFydCgpOw0KICAgICAgICBpbWFnZWpwZWcoJGltYWdlX3AsIE5VTEwsIDk1KTsNCiAgICAgICAgJGkgPSBvYl9nZXRfY29udGVudHMoKTsNCiAgICAgICAgb2JfZW5kX2NsZWFuKCk7DQogICAgICAgIGltYWdlZGVzdHJveSgkaW1hZ2UpOw0KICAgICAgICBpbWFnZWRlc3Ryb3koJGltYWdlX3ApOw0KICAgICAgICByZXR1cm4gJGk7DQogICAgfQ0KDQogICAgcHVibGljIHN0YXRpYyBmdW5jdGlvbiBmb3JtYXRCeXRlcygkYnl0ZXMsICRwcmVjaXNpb24gPSAyKQ0KICAgIHsNCiAgICAgICAgJHVuaXRzID0gYXJyYXkoICJCIiwgImtCIiwgIm1CIiwgImdCIiwgInRCIiApOw0KICAgICAgICAkYnl0ZXMgPSBtYXgoJGJ5dGVzLCAwKTsNCiAgICAgICAgJHBvdyA9IGZsb29yKCgoJGJ5dGVzID8gbG9nKCRieXRlcykgOiAwKSkgLyBsb2coMTAyNCkpOw0KICAgICAgICAkcG93ID0gbWluKCRwb3csIGNvdW50KCR1bml0cykgLSAxKTsNCiAgICAgICAgJGJ5dGVzIC89IHBvdygxMDI0LCAkcG93KTsNCiAgICAgICAgcmV0dXJuIHJvdW5kKCRieXRlcywgJHByZWNpc2lvbikgLiAiIiAuICR1bml0c1skcG93XTsNCiAgICB9DQoNCiAgICBwdWJsaWMgc3RhdGljIGZ1bmN0aW9uIGNvbG91cmVkU3RyaW5nKCRzdHJpbmcsICRjb2xvdXIpDQogICAgew0KICAgICAgICAkY29sb3Vyc1siYmxhY2siXSA9ICIwOzMwIjsNCiAgICAgICAgJGNvbG91cnNbImRhcmtfZ3JheSJdID0gIjE7MzAiOw0KICAgICAgICAkY29sb3Vyc1siYmx1ZSJdID0gIjA7MzQiOw0KICAgICAgICAkY29sb3Vyc1sibGlnaHRfYmx1ZSJdID0gIjE7MzQiOw0KICAgICAgICAkY29sb3Vyc1siZ3JlZW4iXSA9ICIwOzMyIjsNCiAgICAgICAgJGNvbG91cnNbImxpZ2h0X2dyZWVuIl0gPSAiMTszMiI7DQogICAgICAgICRjb2xvdXJzWyJjeWFuIl0gPSAiMDszNiI7DQogICAgICAgICRjb2xvdXJzWyJsaWdodF9jeWFuIl0gPSAiMTszNiI7DQogICAgICAgICRjb2xvdXJzWyJyZWQiXSA9ICIwOzMxIjsNCiAgICAgICAgJGNvbG91cnNbImxpZ2h0X3JlZCJdID0gIjE7MzEiOw0KICAgICAgICAkY29sb3Vyc1sicHVycGxlIl0gPSAiMDszNSI7DQogICAgICAgICRjb2xvdXJzWyJsaWdodF9wdXJwbGUiXSA9ICIxOzM1IjsNCiAgICAgICAgJGNvbG91cnNbImJyb3duIl0gPSAiMDszMyI7DQogICAgICAgICRjb2xvdXJzWyJ5ZWxsb3ciXSA9ICIxOzMzIjsNCiAgICAgICAgJGNvbG91cnNbImxpZ2h0X2dyYXkiXSA9ICIwOzM3IjsNCiAgICAgICAgJGNvbG91cnNbIndoaXRlIl0gPSAiMTszNyI7DQogICAgICAgICRjb2xvcmVkX3N0cmluZyA9ICIiOw0KICAgICAgICBpZiggaXNzZXQoJGNvbG91cnNbJGNvbG91cl0pICkgDQogICAgICAgIHsNCiAgICAgICAgICAgICRjb2xvcmVkX3N0cmluZyAuPSAiXHgxQlsiIC4gJGNvbG91cnNbJGNvbG91cl0gLiAibSI7DQogICAgICAgIH0NCg0KICAgICAgICAkY29sb3JlZF9zdHJpbmcgLj0gJHN0cmluZyAuICJceDFCWzBtIjsNCiAgICAgICAgcmV0dXJuICRjb2xvcmVkX3N0cmluZzsNCiAgICB9DQoNCiAgICBwdWJsaWMgc3RhdGljIGZ1bmN0aW9uIGdldEZpbHRlckNvZGUoJGZpbHRlcikNCiAgICB7DQogICAgICAgICRmaWx0ZXJzID0gYXJyYXkoICApOw0KICAgICAgICAkZmlsdGVyc1sxMDhdID0gIkNoYXJtZXMiOw0KICAgICAgICAkZmlsdGVyc1sxMTZdID0gIkFzaGJ5IjsNCiAgICAgICAgJGZpbHRlcnNbMTE3XSA9ICJIZWxlbmEiOw0KICAgICAgICAkZmlsdGVyc1sxMTVdID0gIkJyb29rbHluIjsNCiAgICAgICAgJGZpbHRlcnNbMTA1XSA9ICJEb2dwYXRjaCI7DQogICAgICAgICRmaWx0ZXJzWzExM10gPSAiU2t5bGluZSI7DQogICAgICAgICRmaWx0ZXJzWzEwN10gPSAiR2luemEiOw0KICAgICAgICAkZmlsdGVyc1sxMThdID0gIk1hdmVuIjsNCiAgICAgICAgJGZpbHRlcnNbMTZdID0gIktlbHZpbiI7DQogICAgICAgICRmaWx0ZXJzWzE0XSA9ICIxOTc3IjsNCiAgICAgICAgJGZpbHRlcnNbMjBdID0gIldhbGRlbiI7DQogICAgICAgICRmaWx0ZXJzWzE5XSA9ICJUb2FzdGVyIjsNCiAgICAgICAgJGZpbHRlcnNbMThdID0gIlN1dHJvIjsNCiAgICAgICAgJGZpbHRlcnNbMjJdID0gIkJyYW5uYW4iOw0KICAgICAgICAkZmlsdGVyc1szXSA9ICJFYXJseWJpcmQiOw0KICAgICAgICAkZmlsdGVyc1sxMDZdID0gIlZlc3BlciI7DQogICAgICAgICRmaWx0ZXJzWzEwOV0gPSAiU3RpbnNvbiI7DQogICAgICAgICRmaWx0ZXJzWzE1XSA9ICJOYXNodmlsbGUiOw0KICAgICAgICAkZmlsdGVyc1syMV0gPSAiSGVmZSI7DQogICAgICAgICRmaWx0ZXJzWzEwXSA9ICJJbmt3ZWxsIjsNCiAgICAgICAgJGZpbHRlcnNbMl0gPSAiTG8tRmkiOw0KICAgICAgICAkZmlsdGVyc1syOF0gPSAiV2lsbG93IjsNCiAgICAgICAgJGZpbHRlcnNbMjddID0gIlNpZXJyYSI7DQogICAgICAgICRmaWx0ZXJzWzFdID0gIlggUHJvIElJIjsNCiAgICAgICAgJGZpbHRlcnNbMjVdID0gIlZhbGVuY2lhIjsNCiAgICAgICAgJGZpbHRlcnNbMjZdID0gIkh1ZHNvbiI7DQogICAgICAgICRmaWx0ZXJzWzIzXSA9ICJSaXNlIjsNCiAgICAgICAgJGZpbHRlcnNbMTddID0gIk1heWZhaXIiOw0KICAgICAgICAkZmlsdGVyc1syNF0gPSAiQW1hcm8iOw0KICAgICAgICAkZmlsdGVyc1s2MDhdID0gIlBlcnBldHVhIjsNCiAgICAgICAgJGZpbHRlcnNbNjEyXSA9ICJBZGVuIjsNCiAgICAgICAgJGZpbHRlcnNbNjAzXSA9ICJMdWR3aWciOw0KICAgICAgICAkZmlsdGVyc1s2MTZdID0gIkNyZW1hIjsNCiAgICAgICAgJGZpbHRlcnNbNjA1XSA9ICJTbHVtYmVyIjsNCiAgICAgICAgJGZpbHRlcnNbNjEzXSA9ICJKdW5vIjsNCiAgICAgICAgJGZpbHRlcnNbNjE0XSA9ICJSZXllcyI7DQogICAgICAgICRmaWx0ZXJzWzYxNV0gPSAiTGFyayI7DQogICAgICAgICRmaWx0ZXJzWzExMV0gPSAiTW9vbiI7DQogICAgICAgICRmaWx0ZXJzWzExNF0gPSAiR2luZ2hhbSI7DQogICAgICAgICRmaWx0ZXJzWzExMl0gPSAiQ2xhcmVuZG9uIjsNCiAgICAgICAgJGZpbHRlcnNbMF0gPSAiTm9ybWFsIjsNCiAgICAgICAgcmV0dXJuIGFycmF5X3NlYXJjaCgkZmlsdGVyLCAkZmlsdGVycyk7DQogICAgfQ0KDQogICAgcHVibGljIHN0YXRpYyBmdW5jdGlvbiBjb29raWVUb0FycmF5KCRzdHJpbmcsICRkb21haW4pDQogICAgew0KICAgICAgICAkYXJyQ29va2llcyA9IGFycmF5KCAgKTsNCiAgICAgICAgJGZpbGVWYWxzID0gc2VsZjo6ZXh0cmFjdENvb2tpZXMoJHN0cmluZyk7DQogICAgICAgIGZvcmVhY2goICRmaWxlVmFscyBhcyAkY29va2llICkgDQogICAgICAgIHsNCiAgICAgICAgICAgIGlmKCAkY29va2llWyJkb21haW4iXSA9PSAkZG9tYWluICkgDQogICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgJGFyckNvb2tpZXNbJGNvb2tpZVsibmFtZSJdXSA9ICRjb29raWVbInZhbHVlIl07DQogICAgICAgICAgICB9DQoNCiAgICAgICAgfQ0KICAgICAgICByZXR1cm4gJGFyckNvb2tpZXM7DQogICAgfQ0KDQogICAgcHVibGljIHN0YXRpYyBmdW5jdGlvbiBleHRyYWN0Q29va2llcygkc3RyaW5nKQ0KICAgIHsNCiAgICAgICAgJGxpbmVzID0gZXhwbG9kZShQSFBfRU9MLCAkc3RyaW5nKTsNCiAgICAgICAgJGNvb2tpZXMgPSBhcnJheSggICk7DQogICAgICAgIGZvcmVhY2goICRsaW5lcyBhcyAkbGluZSApIA0KICAgICAgICB7DQogICAgICAgICAgICAkY29va2llID0gYXJyYXkoICApOw0KICAgICAgICAgICAgaWYoIHN1YnN0cigkbGluZSwgMCwgMTApID09ICIjSHR0cE9ubHlfIiApIA0KICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICRsaW5lID0gc3Vic3RyKCRsaW5lLCAxMCk7DQogICAgICAgICAgICAgICAgJGNvb2tpZVsiaHR0cG9ubHkiXSA9IHRydWU7DQogICAgICAgICAgICB9DQogICAgICAgICAgICBlbHNlDQogICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgJGNvb2tpZVsiaHR0cG9ubHkiXSA9IGZhbHNlOw0KICAgICAgICAgICAgfQ0KDQogICAgICAgICAgICBpZiggc3Vic3RyKCRsaW5lLCAwLCAxKSAhPSAiIyIgJiYgc3Vic3RyX2NvdW50KCRsaW5lLCAiXHQiKSA9PSA2ICkgDQogICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgJHRva2VucyA9IGV4cGxvZGUoIlx0IiwgJGxpbmUpOw0KICAgICAgICAgICAgICAgICR0b2tlbnMgPSBhcnJheV9tYXAoInRyaW0iLCAkdG9rZW5zKTsNCiAgICAgICAgICAgICAgICBsaXN0KCRjb29raWVbImRvbWFpbiJdLCAkY29va2llWyJmbGFnIl0sICRjb29raWVbInBhdGgiXSwgJGNvb2tpZVsic2VjdXJlIl0sICRjb29raWVbImV4cGlyYXRpb24tZXBvY2giXSkgPSAkdG9rZW5zOw0KICAgICAgICAgICAgICAgICRjb29raWVbIm5hbWUiXSA9IHVybGRlY29kZSgkdG9rZW5zWzVdKTsNCiAgICAgICAgICAgICAgICAkY29va2llWyJ2YWx1ZSJdID0gdXJsZGVjb2RlKCR0b2tlbnNbNl0pOw0KICAgICAgICAgICAgICAgICRjb29raWVbImV4cGlyYXRpb24iXSA9IGRhdGUoIlktbS1kIGg6aTpzIiwgJHRva2Vuc1s0XSk7DQogICAgICAgICAgICAgICAgJGNvb2tpZXNbXSA9ICRjb29raWU7DQogICAgICAgICAgICB9DQoNCiAgICAgICAgfQ0KICAgICAgICByZXR1cm4gJGNvb2tpZXM7DQogICAgfQ0KDQp9DQoNCg0KY2xhc3MgU2V0dGluZ3MNCnsNCiAgICBwcml2YXRlICRwYXRoID0gTlVMTDsNCiAgICBwcml2YXRlICRzZXRzID0gTlVMTDsNCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBfX2NvbnN0cnVjdCgkcGF0aCkNCiAgICB7DQogICAgICAgICR0aGlzLT5wYXRoID0gJHBhdGg7DQogICAgICAgICR0aGlzLT5zZXRzID0gYXJyYXkoICApOw0KICAgICAgICBpZiggZmlsZV9leGlzdHMoJHBhdGgpICkgDQogICAgICAgIHsNCiAgICAgICAgICAgICRmcCA9IGZvcGVuKCRwYXRoLCAicmIiKTsNCiAgICAgICAgICAgIHdoaWxlKCAkbGluZSA9IGZnZXRzKCRmcCwgMjA0OCkgKSANCiAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAkbGluZSA9IHRyaW0oJGxpbmUsICIgIik7DQogICAgICAgICAgICAgICAgaWYoICRsaW5lWzBdID09ICIjIiApIA0KICAgICAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAgICAgY29udGludWU7DQogICAgICAgICAgICAgICAgfQ0KDQogICAgICAgICAgICAgICAgJGt2ID0gZXhwbG9kZSgiPSIsICRsaW5lLCAyKTsNCiAgICAgICAgICAgICAgICAkdGhpcy0+c2V0c1ska3ZbMF1dID0gdHJpbSgka3ZbMV0sICJcclxuICIpOw0KICAgICAgICAgICAgfQ0KICAgICAgICAgICAgZmNsb3NlKCRmcCk7DQogICAgICAgIH0NCg0KICAgIH0NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBnZXQoJGtleSwgJGRlZmF1bHQgPSBOVUxMKQ0KICAgIHsNCiAgICAgICAgaWYoICRrZXkgPT0gInNldHMiICkgDQogICAgICAgIHsNCiAgICAgICAgICAgIHJldHVybiAkdGhpcy0+c2V0czsNCiAgICAgICAgfQ0KDQogICAgICAgIGlmKCBpc3NldCgkdGhpcy0+c2V0c1ska2V5XSkgKSANCiAgICAgICAgew0KICAgICAgICAgICAgcmV0dXJuICR0aGlzLT5zZXRzWyRrZXldOw0KICAgICAgICB9DQoNCiAgICAgICAgcmV0dXJuICRkZWZhdWx0Ow0KICAgIH0NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBzZXQoJGtleSwgJHZhbHVlKQ0KICAgIHsNCiAgICAgICAgaWYoICRrZXkgPT0gInNldHMiICkgDQogICAgICAgIHsNCiAgICAgICAgICAgIHJldHVybiBOVUxMOw0KICAgICAgICB9DQoNCiAgICAgICAgJHRoaXMtPnNldHNbJGtleV0gPSAkdmFsdWU7DQogICAgICAgICR0aGlzLT5TYXZlKCk7DQogICAgfQ0KDQogICAgcHVibGljIGZ1bmN0aW9uIFNhdmUoKQ0KICAgIHsNCiAgICAgICAgaWYoIGZpbGVfZXhpc3RzKCR0aGlzLT5wYXRoKSApIA0KICAgICAgICB7DQogICAgICAgICAgICB1bmxpbmsoJHRoaXMtPnBhdGgpOw0KICAgICAgICB9DQoNCiAgICAgICAgJGZwID0gZm9wZW4oJHRoaXMtPnBhdGgsICJ3YiIpOw0KICAgICAgICBmc2VlaygkZnAsIDApOw0KICAgICAgICBmb3JlYWNoKCAkdGhpcy0+c2V0cyBhcyAka2V5ID0+ICR2YWx1ZSApIA0KICAgICAgICB7DQogICAgICAgICAgICBmd3JpdGUoJGZwLCAka2V5IC4gIj0iIC4gJHZhbHVlIC4gIlxuIik7DQogICAgICAgIH0NCiAgICAgICAgZmNsb3NlKCRmcCk7DQogICAgfQ0KDQogICAgcHVibGljIGZ1bmN0aW9uIF9fc2V0KCRwcm9wLCAkdmFsdWUpDQogICAgew0KICAgICAgICAkdGhpcy0+c2V0KCRwcm9wLCAkdmFsdWUpOw0KICAgIH0NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBfX2dldCgkcHJvcCkNCiAgICB7DQogICAgICAgIHJldHVybiAkdGhpcy0+Z2V0KCRwcm9wKTsNCiAgICB9DQoNCn0NCg0KDQpjbGFzcyBDb25zdGFudHMNCnsNCiAgICBjb25zdCBBUElfVVJMID0gImh0dHBzOi8vaS5pbnN0YWdyYW0uY29tL2FwaS92MS8iOw0KICAgIGNvbnN0IFZFUlNJT04gPSAiMTAuMy4yIjsNCiAgICBjb25zdCBJR19TSUdfS0VZID0gIjVhZDdkNmYwMTM2NjZjYzkzYzg4ZmM4YWY5NDAzNDhiZDA2N2I2OGYwZGNlM2M4NTEyMmE5MjNmNGY3NGIyNTEiOw0KICAgIGNvbnN0IEVYUEVSSU1FTlRTID0gImlnX2FuZHJvaWRfYWRfaG9sZG91dF8xNm01X3VuaXZlcnNlLGlnX2FuZHJvaWRfcHJvZ3Jlc3NpdmVfanBlZyxpZ19jcmVhdGlvbl9ncm93dGhfaG9sZG91dCxpZ19hbmRyb2lkX29wcG9fYXBwX2JhZGdpbmcsaWdfYW5kcm9pZF9hZF9yZW1vdmVfdXNlcm5hbWVfZnJvbV9jYXB0aW9uX3VuaXZlcnNlLGlnX2FuZHJvaWRfZW5hYmxlX3NoYXJlX3RvX3doYXRzYXBwLGlnX2FuZHJvaWRfZGlyZWN0X2RyYXdpbmdfaW5fcXVpY2tfY2FtX3VuaXZlcnNlLGlnX2FuZHJvaWRfYWRfYWx3YXlzX3NlbmRfYWRfYXR0cmlidXRpb25faWRfdW5pdmVyc2UsaWdfYW5kcm9pZF91bml2ZXJzZV92aWRlb19wcm9kdWN0aW9uLGlnX2FuZHJvaWRfZGlyZWN0X3BsdXNfYnV0dG9uLGlnX2FuZHJvaWRfYWRzX2hlYXRtYXBfb3ZlcmxheV91bml2ZXJzZSxpZ19hbmRyb2lkX2h0dHBfc3RhY2tfZXhwZXJpbWVudF8yMDE2LGlnX2FuZHJvaWRfaW5maW5pdGVfc2Nyb2xsaW5nLGlnX2ZibnNfYmxvY2tlZCxpZ19hbmRyb2lkX3Bvc3RfYXV0b19yZXRyeV92N18yMSxpZ19mYm5zX3B1c2gsaWdfYW5kcm9pZF92aWRlb19wbGF5YmFja19iYW5kd2lkdGhfdGhyZXNob2xkLGlnX2FuZHJvaWRfZGlyZWN0X2xpbmtfcHJldmlldyxpZ19hbmRyb2lkX2RpcmVjdF90eXBpbmdfaW5kaWNhdG9yLGlnX2FuZHJvaWRfcHJldmlld19jYXB0dXJlLGlnX2FuZHJvaWRfZmVlZF9waWxsLGlnX2FuZHJvaWRfcHJvZmlsZV9saW5rX2lhYixpZ19hbmRyb2lkX3N0b3J5X2NhcHRpb24saWdfYW5kcm9pZF9uZXR3b3JrX2NhbmNlbGxhdGlvbixpZ19hbmRyb2lkX2hpc3RvZ3JhbV9yZXBvcnRlcixpZ19hbmRyb2lkX2FucndhdGNoZG9nLGlnX2FuZHJvaWRfc2VhcmNoX2NsaWVudF9tYXRjaGluZyxpZ19hbmRyb2lkX2ZvbGxvd19yZXF1ZXN0X3RleHRfYnV0dG9ucyxpZ19hbmRyb2lkX2ZlZWRfem9vbSxpZ19hbmRyb2lkX2RyYWZ0c191bml2ZXJzZSxpZ19hbmRyb2lkX2Rpc2FibGVfY29tbWVudCxpZ19hbmRyb2lkX3VzZXJfZGV0YWlsX2VuZHBvaW50LGlnX2FuZHJvaWRfb3NfdmVyc2lvbl9ibG9ja2luZyxpZ19hbmRyb2lkX2Jsb2NrZWRfbGlzdCxpZ19hbmRyb2lkX2V2ZW50X2NyZWF0aW9uLGlnX2FuZHJvaWRfaGlnaF9yZXNfdXBsb2FkXzIsaWdfYW5kcm9pZF8yZmFjLGlnX2FuZHJvaWRfbWFya19yZWVsX3NlZW5fb25fU3dpcGVfZm9yd2FyZCxpZ19hbmRyb2lkX2NvbW1lbnRfcmVkZXNpZ24saWdfYW5kcm9pZF9hZF9zcG9uc29yZWRfbGFiZWxfdW5pdmVyc2UsaWdfYW5kcm9pZF9tZW50aW9uc19kaXNtaXNzX3J1bGUsaWdfYW5kcm9pZF9kaXNhYmxlX2Nocm9tYV9zdWJzYW1wbGluZyxpZ19hbmRyb2lkX3NoYXJlX3NwaW5uZXIsaWdfYW5kcm9pZF92aWRlb19yZXVzZV9zdXJmYWNlLGlnX2V4cGxvcmVfdjNfYW5kcm9pZF91bml2ZXJzZSxpZ19hbmRyb2lkX21lZGlhX2Zhdm9yaXRlcyxpZ19hbmRyb2lkX251eF9ob2xkb3V0LGlnX2FuZHJvaWRfaW5zdGFfdmlkZW9fdW5pdmVyc2UsaWdfYW5kcm9pZF9zZWFyY2hfbnVsbF9zdGF0ZSxpZ19hbmRyb2lkX3VuaXZlcnNlX3JlZWxfdmlkZW9fcHJvZHVjdGlvbixsaWdlcl9pbnN0YWdyYW1fYW5kcm9pZF91bml2LGlnX2FuZHJvaWRfZGlyZWN0X2Vtb2ppX3BpY2tlcixpZ19mZWVkX2hvbGRvdXRfdW5pdmVyc2UsaWdfYW5kcm9pZF9kaXJlY3Rfc2VuZF9hdXRvX3JldHJ5X3VuaXZlcnNlLGlnX2FuZHJvaWRfc2Ftc3VuZ19hcHBfYmFkZ2luZyxpZ19hbmRyb2lkX2Rpc2tfdXNhZ2UsaWdfYW5kcm9pZF9idXNpbmVzc19wcm9tb3Rpb24saWdfYW5kcm9pZF9kaXJlY3Rfc3dpcGVfdG9faW5ib3gsaWdfYW5kcm9pZF9mZWVkX3Jlc2hhcmVfYnV0dG9uX251eCxpZ19hbmRyb2lkX3JlYWN0X25hdGl2ZV9ib29zdF9wb3N0LGlnX2FuZHJvaWRfYm9vbWVyYW5nX2ZlZWRfYXR0cmlidXRpb24saWdfZmJuc19zaGFyZWQsaWdfZmJuc19kdW1wX2lkcyxpZ19hbmRyb2lkX3JlYWN0X25hdGl2ZV91bml2ZXJzZSxpZ19zaG93X3Byb21vdGVfYnV0dG9uX2luX2ZlZWQsaWdfYW5kcm9pZF9hZF9tZXRhZGF0YV9iZWhhdmlvcl91bml2ZXJzZSxpZ19hbmRyb2lkX3ZpZGVvX2xvb3Bjb3VudF9pbnQsaWdfYW5kcm9pZF9pbmxpbmVfZ2FsbGVyeV9iYWNrb2ZmX2hvdXJzX3VuaXZlcnNlLGlnX2FuZHJvaWRfcmVuZGVyaW5nX2NvbnRyb2xzLGlnX2FuZHJvaWRfcHJvZmlsZV9waG90b19hc19tZWRpYSxpZ19hbmRyb2lkX2FzeW5jX3N0YWNrX2ltYWdlX2NhY2hlLGlnX3ZpZGVvX21heF9kdXJhdGlvbl9xZV9wcmV1bml2ZXJzZSxpZ192aWRlb19jb3B5cmlnaHRfd2hpdGVsaXN0LGlnX2FuZHJvaWRfcmVuZGVyX3N0b3JpZXNfd2l0aF9jb250ZW50X292ZXJyaWRlLGlnX2FuZHJvaWRfYWRfaW50ZW50X3RvX2hpZ2hsaWdodF91bml2ZXJzZSxpZ19hbmRyb2lkX3N3aXBlX25hdmlnYXRpb25feF9hbmdsZV91bml2ZXJzZSxpZ19hbmRyb2lkX2Rpc2FibGVfY29tbWVudF9wdWJsaWNfdGVzdCxpZ19hbmRyb2lkX3Byb2ZpbGUsaWdfYW5kcm9pZF9kaXJlY3RfYmx1ZV90YWIsaWdfYW5kcm9pZF9lbmFibGVfc2hhcmVfdG9fbWVzc2VuZ2VyLGlnX2FuZHJvaWRfZmV0Y2hfcmVlbF90cmF5X29uX3Jlc3VtZV91bml2ZXJzZSxpZ19hbmRyb2lkX3Byb21vdGVfYWdhaW4saWdfZmVlZF9ldmVudF9sYW5kaW5nX3BhZ2VfY2hhbm5lbCxpZ19yYW5raW5nX2ZvbGxvd2luZyxpZ19hbmRyb2lkX3BlbmRpbmdfcmVxdWVzdF9zZWFyY2hfYmFyLGlnX2FuZHJvaWRfZmVlZF91ZmlfcmVkZXNpZ24saWdfYW5kcm9pZF9wZW5kaW5nX2VkaXRzX2RpYWxvZ191bml2ZXJzZSxpZ19hbmRyb2lkX2J1c2luZXNzX2NvbnZlcnNpb25fZmxvd191bml2ZXJzZSxpZ19hbmRyb2lkX3Nob3dfeW91cl9zdG9yeV93aGVuX2VtcHR5X3VuaXZlcnNlLGlnX2FuZHJvaWRfYWRfZHJvcF9jb29raWVfZWFybHksaWdfYW5kcm9pZF9hcHBfc3RhcnRfY29uZmlnLGlnX2FuZHJvaWRfZml4X2lzZV90d29fcGhhc2UsaWdfYW5kcm9pZF9wcGFnZV90b2dnbGVfdW5pdmVyc2UsaWdfYW5kcm9pZF9wYmlhX25vcm1hbF93ZWlnaHRfdW5pdmVyc2UsaWdfYW5kcm9pZF9wcm9mYW5pdHlfZmlsdGVyLGlnX2lvc19zdV9hY3Rpdml0eV9mZWVkLGlnX2FuZHJvaWRfc2VhcmNoLGlnX2FuZHJvaWRfYm9vbWVyYW5nX2VudHJ5LGlnX2FuZHJvaWRfbXV0ZV9zdG9yeSxpZ19hbmRyb2lkX2lubGluZV9nYWxsZXJ5X3VuaXZlcnNlLGlnX2FuZHJvaWRfYWRfcmVtb3ZlX29uZV90YXBfaW5kaWNhdG9yX3VuaXZlcnNlLGlnX2FuZHJvaWRfdmlld19jb3VudF9kZWNvdXBsZV9saWtlc191bml2ZXJzZSxpZ19hbmRyb2lkX2NvbnRhY3RfYnV0dG9uX3JlZGVzaWduX3YyLGlnX2FuZHJvaWRfcGVyaW9kaWNfYW5hbHl0aWNzX3VwbG9hZF92MixpZ19hbmRyb2lkX3NlbmRfZGlyZWN0X3R5cGluZ19pbmRpY2F0b3IsaWdfYW5kcm9pZF9hZF9ob2xkb3V0XzE2aDJtMV91bml2ZXJzZSxpZ19hbmRyb2lkX3JlYWN0X25hdGl2ZV9jb21tZW50X21vZGVyYXRpb25fc2V0dGluZ3MsaWdfdmlkZW9fdXNlX3N2ZV91bml2ZXJzZSxpZ19hbmRyb2lkX2lubGluZV9nYWxsZXJ5X25vX2JhY2tvZmZfb25fbGF1bmNoX3VuaXZlcnNlLGlnX2FuZHJvaWRfaW1tZXJzaXZlX3ZpZXdlcixpZ19hbmRyb2lkX2Rpc2NvdmVyX3Blb3BsZV9pY29uLGlnX2FuZHJvaWRfcHJvZmlsZV9mb2xsb3dfYmFja19idXR0b24saXNfYW5kcm9pZF9mZWVkX3NlZW5fc3RhdGUsaWdfYW5kcm9pZF9kZW5zZV9mZWVkX3VuaXRfY2FyZHMsaWdfYW5kcm9pZF9kcmFmdHNfdmlkZW9fdW5pdmVyc2UsaWdfYW5kcm9pZF9leG9wbGF5ZXIsaWdfYW5kcm9pZF9hZGRfdG9fbGFzdF9wb3N0LGlnX2FuZHJvaWRfYWRfcmVtb3ZlX2N0YV9jaGV2cm9uX3VuaXZlcnNlLGlnX2FuZHJvaWRfYWRfY29tbWVudF9jdGFfdW5pdmVyc2UsaWdfYW5kcm9pZF9zZWFyY2hfZXZlbnRfaWNvbixpZ19hbmRyb2lkX2NoYW5uZWxzX2hvbWUsaWdfYW5kcm9pZF9mZWVkLGlnX2FuZHJvaWRfZHYyX3JlYWx0aW1lX3ByaXZhdGVfc2hhcmUsaWdfYW5kcm9pZF9ub25fc3F1YXJlX2ZpcnN0LGlnX2FuZHJvaWRfdmlkZW9faW50ZXJsZWF2ZWRfdjIsaWdfYW5kcm9pZF92aWRlb19jYWNoZV9wb2xpY3ksaWdfYW5kcm9pZF9yZWFjdF9uYXRpdmVfdW5pdmVyc2Vfa2lsbF9zd2l0Y2gsaWdfYW5kcm9pZF92aWRlb19jYXB0aW9uc191bml2ZXJzZSxpZ19hbmRyb2lkX2ZvbGxvd19zZWFyY2hfYmFyLGlnX2FuZHJvaWRfbGFzdF9lZGl0cyxpZ19hbmRyb2lkX3R3b19zdGVwX2NhcHR1cmVfZmxvdyxpZ19hbmRyb2lkX3ZpZGVvX2Rvd25sb2FkX2xvZ2dpbmcsaWdfYW5kcm9pZF9zaGFyZV9saW5rX3RvX3doYXRzYXBwLGlnX2FuZHJvaWRfZmFjZWJvb2tfdHdpdHRlcl9wcm9maWxlX3Bob3RvcyxpZ19hbmRyb2lkX3N3aXBlYWJsZV9maWx0ZXJzX2JsYWNrbGlzdCxpZ19hbmRyb2lkX2FkX3BiaWFfcHJvZmlsZV90YXBfdW5pdmVyc2UsaWdfYW5kcm9pZF91c2Vfc29mdHdhcmVfbGF5ZXJfZm9yX2tjX2RyYXdpbmdfdW5pdmVyc2UsaWdfYW5kcm9pZF9yZWFjdF9uYXRpdmVfb3RhLGlnX2FuZHJvaWRfZGlyZWN0X211dHVhbGx5X2V4Y2x1c2l2ZV9leHBlcmltZW50X3VuaXZlcnNlLGlnX2FuZHJvaWRfZm9sbG93aW5nX2ZvbGxvd2VyX3NvY2lhbF9jb250ZXh0IjsNCiAgICBjb25zdCBMT0dJTl9FWFBFUklNRU5UUyA9ICJpZ19hbmRyb2lkX3JlZ19sb2dpbl9idG5fYWN0aXZlX3N0YXRlLGlnX2FuZHJvaWRfY2lfb3B0X2luX2F0X3JlZyxpZ19hbmRyb2lkX29uZV9jbGlja19pbl9vbGRfZmxvdyxpZ19hbmRyb2lkX21lcmdlX2ZiX2FuZF9jaV9mcmllbmRzX3BhZ2UsaWdfYW5kcm9pZF9ub25fZmJfc3NvLGlnX2FuZHJvaWRfbWFuZGF0b3J5X2Z1bGxfbmFtZSxpZ19hbmRyb2lkX3JlZ19lbmFibGVfbG9naW5fcGFzc3dvcmRfYnRuLGlnX2FuZHJvaWRfcmVnX3Bob25lX2VtYWlsX2FjdGl2ZV9zdGF0ZSxpZ19hbmRyb2lkX2FuYWx5dGljc19kYXRhX2xvc3MsaWdfZmJuc19ibG9ja2VkLGlnX2FuZHJvaWRfY29udGFjdF9wb2ludF90cmlhZ2UsaWdfYW5kcm9pZF9yZWdfbmV4dF9idG5fYWN0aXZlX3N0YXRlLGlnX2FuZHJvaWRfcHJlZmlsbF9waG9uZV9udW1iZXIsaWdfYW5kcm9pZF9zaG93X2ZiX3NvY2lhbF9jb250ZXh0X2luX251eCxpZ19hbmRyb2lkX29uZV90YXBfbG9naW5fdXBzZWxsLGlnX2ZibnNfcHVzaCxpZ19hbmRyb2lkX3Bob25laWRfc3luY19pbnRlcnZhbCI7DQogICAgY29uc3QgU0lHX0tFWV9WRVJTSU9OID0gIjQiOw0KICAgIGNvbnN0IFhfSUdfQ2FwYWJpbGl0aWVzID0gIjNUb0FBQT09IjsNCiAgICBjb25zdCBBTkRST0lEX1ZFUlNJT04gPSAxODsNCiAgICBjb25zdCBBTkRST0lEX1JFTEVBU0UgPSAiNC4zIjsNCiAgICBjb25zdCBXRUJfVVJMID0gImh0dHBzOi8vd3d3Lmluc3RhZ3JhbS5jb20vIjsNCg0KfQ0KDQoNCmNsYXNzIEJ1bGtSZWFjdGlvbg0Kew0KLyoqDQogICAgICAgICAqIEB2YXIgYXJyYXkgJHVzZXJzDQogICAgICAgICAqLw0KICAgIHByb3RlY3RlZCAkdXNlcnMgPSBhcnJheSggICk7DQovKioNCiAgICAgICAgICogQHZhciBpbnQgJHNpbXVsdGFuZXBvc3RzaXplDQogICAgICAgICAqLw0KICAgIHByb3RlY3RlZCAkc2ltdWx0YW5lcG9zdHNpemUgPSBOVUxMOw0KLyoqDQogICAgICAgICAqIEB2YXIgc3RyaW5nDQogICAgICAgICAqLw0KICAgIHByb3RlY3RlZCAkSUdEYXRhUGF0aCA9IE5VTEw7DQoNCiAgICAvKioNCiAgICAgICAgICogQnVsa1JlYWN0aW9uIGNvbnN0cnVjdG9yLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcGFyYW0gYXJyYXkgICR1c2Vycw0KICAgICAgICAgKiBAcGFyYW0gaW50ICAgICRzaW11bHRhbmVwb3N0c2l6ZQ0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICRJR0RhdGFQYXRoDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIF9fY29uc3RydWN0KCR1c2VycywgJHNpbXVsdGFuZXBvc3RzaXplID0gMTAwLCAkSUdEYXRhUGF0aCA9IE5VTEwpDQogICAgew0KICAgICAgICAkdGhpcy0+c2ltdWx0YW5lcG9zdHNpemUgPSAkc2ltdWx0YW5lcG9zdHNpemU7DQogICAgICAgIGlmKCAhaXNfbnVsbCgkSUdEYXRhUGF0aCkgKSANCiAgICAgICAgew0KICAgICAgICAgICAgJHRoaXMtPklHRGF0YVBhdGggPSAkSUdEYXRhUGF0aDsNCiAgICAgICAgfQ0KICAgICAgICBlbHNlDQogICAgICAgIHsNCiAgICAgICAgICAgICR0aGlzLT5JR0RhdGFQYXRoID0gV293OjpnZXQoInByb2plY3QvY29va2llUGF0aCIpIC4gImluc3RhZ3JhbS8iOw0KICAgICAgICB9DQoNCiAgICAgICAgZm9yZWFjaCggJHVzZXJzIGFzICR1c2VyICkgDQogICAgICAgIHsNCiAgICAgICAgICAgICR0aGlzLT51c2Vyc1tdID0gYXJyYXkoICJkYXRhIiA9PiAkdXNlciwgIm9iamVjdCIgPT4gKCR1c2VyWyJpc1dlYkNvb2tpZSJdID09IDEgPyBuZXcgSW5zdGFncmFtV2ViKCR1c2VyWyJrdWxsYW5pY2lBZGkiXSkgOiBuZXcgSW5zdGFncmFtKCR1c2VyWyJrdWxsYW5pY2lBZGkiXSwgJHVzZXJbInNpZnJlIl0pKSApOw0KICAgICAgICB9DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgICRtZWRpYUlEDQogICAgICAgICAqIEBwYXJhbSAgc3RyaW5nICRtZWRpYUNvZGUNCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBsaWtlKCRtZWRpYUlELCAkbWVkaWFDb2RlKQ0KICAgIHsNCiAgICAgICAgJHRvdGFsU3VjY2Vzc0NvdW50ID0gMDsNCiAgICAgICAgJHRyaWVkVXNlcnMgPSBhcnJheSggICk7DQogICAgICAgICRyb2xsaW5nQ3VybCA9IG5ldyBSb2xsaW5nQ3VybFxSb2xsaW5nQ3VybCgpOw0KICAgICAgICAkYXJyTWVkaWFJRCA9IGV4cGxvZGUoIl8iLCAkbWVkaWFJRCk7DQogICAgICAgICRtZWRpYUlEQmVmb3JlciA9ICRhcnJNZWRpYUlEWzBdOw0KICAgICAgICBmb3JlYWNoKCAkdGhpcy0+dXNlcnMgYXMgJHVzZXIgKSANCiAgICAgICAgew0KICAgICAgICAgICAgaWYoICR1c2VyWyJkYXRhIl1bImlzV2ViQ29va2llIl0gPT0gMSApIA0KICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICRvYmpJbnN0YWdyYW1XZWIgPSAkdXNlclsib2JqZWN0Il07DQogICAgICAgICAgICAgICAgJG9iakRhdGEgPSAkb2JqSW5zdGFncmFtV2ViLT5nZXREYXRhKCk7DQogICAgICAgICAgICAgICAgJGhlYWRlcnMgPSBhcnJheSggIlJlZmVyZXI6IGh0dHBzOi8vd3d3Lmluc3RhZ3JhbS5jb20vaW5zdGFncmFtLyIsICJETlQ6IDEiLCAiT3JpZ2luOiBodHRwczovL3d3dy5pbnN0YWdyYW0uY29tLyIsICJYLUNTUkZUb2tlbjogIiAuIHRyaW0oJG9iakRhdGFbInRva2VuIl0pLCAiWC1SZXF1ZXN0ZWQtV2l0aDogWE1MSHR0cFJlcXVlc3QiLCAiWC1JbnN0YWdyYW0tQUpBWDogMSIsICJDb25uZWN0aW9uOiBjbG9zZSIsICJDYWNoZS1Db250cm9sOiBtYXgtYWdlPTAiLCAiQWNjZXB0OiAqLyoiLCAiQWNjZXB0LUxhbmd1YWdlOiB0cjtxPTEiLCAiWF9GT1JXQVJERURfRk9SOiAiIC4gJG9iakRhdGFbImlwIl0sICJSRU1PVEVfQUREUjogIiAuICRvYmpEYXRhWyJpcCJdICk7DQogICAgICAgICAgICAgICAgJG9wdGlvbnMgPSBhcnJheSggQ1VSTE9QVF9VU0VSQUdFTlQgPT4gJG9iakRhdGFbInVzZXJfYWdlbnQiXSwgQ1VSTE9QVF9SRVRVUk5UUkFOU0ZFUiA9PiB0cnVlLCBDVVJMT1BUX0ZPTExPV0xPQ0FUSU9OID0+IHRydWUsIENVUkxPUFRfVkVSQk9TRSA9PiBmYWxzZSwgQ1VSTE9QVF9TU0xfVkVSSUZZUEVFUiA9PiBmYWxzZSwgQ1VSTE9QVF9TU0xfVkVSSUZZSE9TVCA9PiBmYWxzZSwgQ1VSTE9QVF9DT09LSUVGSUxFID0+ICR0aGlzLT5JR0RhdGFQYXRoIC4gJG9iakRhdGFbInVzZXJuYW1lIl0gLiAiLmRhdCIsIENVUkxPUFRfQ09PS0lFSkFSID0+ICR0aGlzLT5JR0RhdGFQYXRoIC4gJG9iakRhdGFbInVzZXJuYW1lIl0gLiAiLmRhdCIgKTsNCiAgICAgICAgICAgICAgICAkcm9sbGluZ0N1cmwtPnBvc3QoQ29uc3RhbnRzOjpXRUJfVVJMIC4gIndlYi9saWtlcy8iIC4gJG1lZGlhSURCZWZvcmVyIC4gIi9saWtlLyIsIE5VTEwsICRoZWFkZXJzLCAkb3B0aW9ucywgJHVzZXJbImRhdGEiXSk7DQogICAgICAgICAgICB9DQogICAgICAgICAgICBlbHNlDQogICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgJG9iakluc3RhZ3JhbSA9ICR1c2VyWyJvYmplY3QiXTsNCiAgICAgICAgICAgICAgICAkb2JqRGF0YSA9ICRvYmpJbnN0YWdyYW0tPmdldERhdGEoKTsNCiAgICAgICAgICAgICAgICAkZGF0YSA9IGpzb25fZW5jb2RlKGFycmF5KCAiX3V1aWQiID0+ICRvYmpEYXRhWyJ1dWlkIl0sICJfdWlkIiA9PiAkb2JqRGF0YVsidXNlcm5hbWVfaWQiXSwgIl9jc3JmdG9rZW4iID0+ICRvYmpEYXRhWyJ0b2tlbiJdLCAibWVkaWFfaWQiID0+ICRtZWRpYUlEICkpOw0KICAgICAgICAgICAgICAgICRoZWFkZXJzID0gYXJyYXkoICJDb25uZWN0aW9uOiBjbG9zZSIsICJBY2NlcHQ6ICovKiIsICJYLUlHLUNhcGFiaWxpdGllczogIiAuIENvbnN0YW50czo6WF9JR19DYXBhYmlsaXRpZXMsICJYLUlHLUNvbm5lY3Rpb24tVHlwZTogV0lGSSIsICJDb250ZW50LXR5cGU6IGFwcGxpY2F0aW9uL3gtd3d3LWZvcm0tdXJsZW5jb2RlZDsgY2hhcnNldD1VVEYtOCIsICJBY2NlcHQtTGFuZ3VhZ2U6IHRyLVRSIiwgIlhfRk9SV0FSREVEX0ZPUjogIiAuICRvYmpEYXRhWyJpcCJdLCAiUkVNT1RFX0FERFI6ICIgLiAkb2JqRGF0YVsiaXAiXSApOw0KICAgICAgICAgICAgICAgICRvcHRpb25zID0gYXJyYXkoIENVUkxPUFRfVVNFUkFHRU5UID0+ICRvYmpEYXRhWyJ1c2VyX2FnZW50Il0sIENVUkxPUFRfUkVUVVJOVFJBTlNGRVIgPT4gdHJ1ZSwgQ1VSTE9QVF9GT0xMT1dMT0NBVElPTiA9PiB0cnVlLCBDVVJMT1BUX1ZFUkJPU0UgPT4gZmFsc2UsIENVUkxPUFRfU1NMX1ZFUklGWVBFRVIgPT4gZmFsc2UsIENVUkxPUFRfU1NMX1ZFUklGWUhPU1QgPT4gZmFsc2UsIENVUkxPUFRfQ09PS0lFRklMRSA9PiAkdGhpcy0+SUdEYXRhUGF0aCAuICRvYmpEYXRhWyJ1c2VybmFtZSJdIC4gIi5kYXQiLCBDVVJMT1BUX0NPT0tJRUpBUiA9PiAkdGhpcy0+SUdEYXRhUGF0aCAuICRvYmpEYXRhWyJ1c2VybmFtZSJdIC4gIi5kYXQiICk7DQogICAgICAgICAgICAgICAgJHJvbGxpbmdDdXJsLT5wb3N0KENvbnN0YW50czo6QVBJX1VSTCAuICJtZWRpYS8iIC4gJG1lZGlhSUQgLiAiL2xpa2UvIiwgU2lnbmF0dXJlVXRpbHM6OmdlbmVyYXRlU2lnbmF0dXJlKCRkYXRhKSwgJGhlYWRlcnMsICRvcHRpb25zLCAkdXNlclsiZGF0YSJdKTsNCiAgICAgICAgICAgIH0NCg0KICAgICAgICB9DQogICAgICAgICRyb2xsaW5nQ3VybC0+c2V0Q2FsbGJhY2soZnVuY3Rpb24oUm9sbGluZ0N1cmxcUmVxdWVzdCAkcmVxdWVzdCwgUm9sbGluZ0N1cmxcUm9sbGluZ0N1cmwgJHJvbGxpbmdDdXJsKSB1c2UgKCYkdHJpZWRVc2VycywgJiR0b3RhbFN1Y2Nlc3NDb3VudCkNCnsNCiAgICAkdHJpZWRVc2VyID0gYXJyYXkoICJ1c2VySUQiID0+ICRyZXF1ZXN0LT5pZGVudGlmaWVyUGFyYW1zWyJ1eWVJRCJdLCAiaW5zdGFJRCIgPT4gJHJlcXVlc3QtPmlkZW50aWZpZXJQYXJhbXNbImluc3RhSUQiXSwgInVzZXJOaWNrIiA9PiAkcmVxdWVzdC0+aWRlbnRpZmllclBhcmFtc1sia3VsbGFuaWNpQWRpIl0sICJzdGF0dXMiID0+ICJmYWlsIiApOw0KICAgICRpc0Vycm9yZWQgPSAkcmVxdWVzdC0+Z2V0UmVzcG9uc2VFcnJvcigpOw0KICAgIGlmKCBlbXB0eSgkaXNFcnJvcmVkKSApIA0KICAgIHsNCiAgICAgICAgJGRvbmVuU29udWMgPSBqc29uX2RlY29kZSgkcmVxdWVzdC0+Z2V0UmVzcG9uc2VUZXh0KCksIHRydWUpOw0KICAgICAgICBpZiggJGRvbmVuU29udWMgKSANCiAgICAgICAgew0KICAgICAgICAgICAgaWYoICRyZXF1ZXN0LT5pZGVudGlmaWVyUGFyYW1zWyJpc1dlYkNvb2tpZSJdID09IDEgKSANCiAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICBpZiggJGRvbmVuU29udWNbInN0YXR1cyJdID09ICJvayIgKSANCiAgICAgICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgICAgICR0b3RhbFN1Y2Nlc3NDb3VudCsrOw0KICAgICAgICAgICAgICAgICAgICAkdHJpZWRVc2VyWyJzdGF0dXMiXSA9ICJzdWNjZXNzIjsNCiAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICAgZWxzZQ0KICAgICAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAgICAgJHRyaWVkVXNlclsic3RhdHVzIl0gPSAiZmFpbCI7DQogICAgICAgICAgICAgICAgfQ0KDQogICAgICAgICAgICB9DQogICAgICAgICAgICBlbHNlDQogICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgaWYoICRkb25lblNvbnVjWyJzdGF0dXMiXSA9PSAib2siICkgDQogICAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICAgICAkdG90YWxTdWNjZXNzQ291bnQrKzsNCiAgICAgICAgICAgICAgICAgICAgJHRyaWVkVXNlclsic3RhdHVzIl0gPSAic3VjY2VzcyI7DQogICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgIGVsc2UNCiAgICAgICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgICAgICR0cmllZFVzZXJbInN0YXR1cyJdID0gImZhaWwiOw0KICAgICAgICAgICAgICAgIH0NCg0KICAgICAgICAgICAgfQ0KDQogICAgICAgIH0NCiAgICAgICAgZWxzZQ0KICAgICAgICB7DQogICAgICAgICAgICAkdHJpZWRVc2VyWyJzdGF0dXMiXSA9ICJmYWlsIjsNCiAgICAgICAgfQ0KDQogICAgfQ0KICAgIGVsc2UNCiAgICB7DQogICAgICAgICR0cmllZFVzZXJbInN0YXR1cyJdID0gImZhaWwiOw0KICAgIH0NCg0KICAgICR0cmllZFVzZXJzW10gPSAkdHJpZWRVc2VyOw0KICAgICRyb2xsaW5nQ3VybC0+Y2xlYXJDb21wbGV0ZWQoKTsNCiAgICAkcm9sbGluZ0N1cmwtPnBydW5lUGVuZGluZ1JlcXVlc3RRdWV1ZSgpOw0KfQ0KDQopOw0KICAgICAgICAkcm9sbGluZ0N1cmwtPnNldFNpbXVsdGFuZW91c0xpbWl0KCR0aGlzLT5zaW11bHRhbmVwb3N0c2l6ZSk7DQogICAgICAgICRyb2xsaW5nQ3VybC0+ZXhlY3V0ZSgpOw0KICAgICAgICByZXR1cm4gYXJyYXkoICJ0b3RhbFN1Y2Nlc3NDb3VudCIgPT4gJHRvdGFsU3VjY2Vzc0NvdW50LCAidXNlcnMiID0+ICR0cmllZFVzZXJzICk7DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJHVzZXJJRA0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICR1c2VyTmFtZQ0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGZvbGxvdygkdXNlcklELCAkdXNlck5hbWUpDQogICAgew0KICAgICAgICAkdG90YWxTdWNjZXNzQ291bnQgPSAwOw0KICAgICAgICAkdHJpZWRVc2VycyA9IGFycmF5KCAgKTsNCiAgICAgICAgJHJvbGxpbmdDdXJsID0gbmV3IFJvbGxpbmdDdXJsXFJvbGxpbmdDdXJsKCk7DQogICAgICAgIGZvcmVhY2goICR0aGlzLT51c2VycyBhcyAkdXNlciApIA0KICAgICAgICB7DQogICAgICAgICAgICBpZiggJHVzZXJbImRhdGEiXVsiaXNXZWJDb29raWUiXSA9PSAxICkgDQogICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgJG9iakluc3RhZ3JhbVdlYiA9ICR1c2VyWyJvYmplY3QiXTsNCiAgICAgICAgICAgICAgICAkb2JqRGF0YSA9ICRvYmpJbnN0YWdyYW1XZWItPmdldERhdGEoKTsNCiAgICAgICAgICAgICAgICAkaGVhZGVycyA9IGFycmF5KCAiUmVmZXJlcjogaHR0cHM6Ly93d3cuaW5zdGFncmFtLmNvbS9pbnN0YWdyYW0vIiwgIkROVDogMSIsICJPcmlnaW46IGh0dHBzOi8vd3d3Lmluc3RhZ3JhbS5jb20vIiwgIlgtQ1NSRlRva2VuOiAiIC4gdHJpbSgkb2JqRGF0YVsidG9rZW4iXSksICJYLVJlcXVlc3RlZC1XaXRoOiBYTUxIdHRwUmVxdWVzdCIsICJYLUluc3RhZ3JhbS1BSkFYOiAxIiwgIkNvbm5lY3Rpb246IGNsb3NlIiwgIkNhY2hlLUNvbnRyb2w6IG1heC1hZ2U9MCIsICJBY2NlcHQ6ICovKiIsICJBY2NlcHQtTGFuZ3VhZ2U6IHRyO3E9MSIsICJYX0ZPUldBUkRFRF9GT1I6ICIgLiAkb2JqRGF0YVsiaXAiXSwgIlJFTU9URV9BRERSOiAiIC4gJG9iakRhdGFbImlwIl0gKTsNCiAgICAgICAgICAgICAgICAkb3B0aW9ucyA9IGFycmF5KCBDVVJMT1BUX1VTRVJBR0VOVCA9PiAkb2JqRGF0YVsidXNlcl9hZ2VudCJdLCBDVVJMT1BUX1JFVFVSTlRSQU5TRkVSID0+IHRydWUsIENVUkxPUFRfRk9MTE9XTE9DQVRJT04gPT4gdHJ1ZSwgQ1VSTE9QVF9WRVJCT1NFID0+IGZhbHNlLCBDVVJMT1BUX1NTTF9WRVJJRllQRUVSID0+IGZhbHNlLCBDVVJMT1BUX1NTTF9WRVJJRllIT1NUID0+IGZhbHNlLCBDVVJMT1BUX0NPT0tJRUZJTEUgPT4gJHRoaXMtPklHRGF0YVBhdGggLiAkb2JqRGF0YVsidXNlcm5hbWUiXSAuICIuZGF0IiwgQ1VSTE9QVF9DT09LSUVKQVIgPT4gJHRoaXMtPklHRGF0YVBhdGggLiAkb2JqRGF0YVsidXNlcm5hbWUiXSAuICIuZGF0IiApOw0KICAgICAgICAgICAgICAgICRyb2xsaW5nQ3VybC0+cG9zdChDb25zdGFudHM6OldFQl9VUkwgLiAid2ViL2ZyaWVuZHNoaXBzLyIgLiAkdXNlcklEIC4gIi9mb2xsb3cvIiwgTlVMTCwgJGhlYWRlcnMsICRvcHRpb25zLCAkdXNlclsiZGF0YSJdKTsNCiAgICAgICAgICAgIH0NCiAgICAgICAgICAgIGVsc2UNCiAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAkb2JqSW5zdGFncmFtID0gJHVzZXJbIm9iamVjdCJdOw0KICAgICAgICAgICAgICAgICRvYmpEYXRhID0gJG9iakluc3RhZ3JhbS0+Z2V0RGF0YSgpOw0KICAgICAgICAgICAgICAgICRkYXRhID0ganNvbl9lbmNvZGUoYXJyYXkoICJfdXVpZCIgPT4gJG9iakRhdGFbInV1aWQiXSwgIl91aWQiID0+ICRvYmpEYXRhWyJ1c2VybmFtZV9pZCJdLCAidXNlcl9pZCIgPT4gJHVzZXJJRCwgIl9jc3JmdG9rZW4iID0+ICRvYmpEYXRhWyJ0b2tlbiJdICkpOw0KICAgICAgICAgICAgICAgICRoZWFkZXJzID0gYXJyYXkoICJDb25uZWN0aW9uOiBjbG9zZSIsICJBY2NlcHQ6ICovKiIsICJYLUlHLUNhcGFiaWxpdGllczogIiAuIENvbnN0YW50czo6WF9JR19DYXBhYmlsaXRpZXMsICJYLUlHLUNvbm5lY3Rpb24tVHlwZTogV0lGSSIsICJDb250ZW50LXR5cGU6IGFwcGxpY2F0aW9uL3gtd3d3LWZvcm0tdXJsZW5jb2RlZDsgY2hhcnNldD1VVEYtOCIsICJBY2NlcHQtTGFuZ3VhZ2U6IHRyLVRSIiwgIlhfRk9SV0FSREVEX0ZPUjogIiAuICRvYmpEYXRhWyJpcCJdLCAiUkVNT1RFX0FERFI6ICIgLiAkb2JqRGF0YVsiaXAiXSApOw0KICAgICAgICAgICAgICAgICRvcHRpb25zID0gYXJyYXkoIENVUkxPUFRfVVNFUkFHRU5UID0+ICRvYmpEYXRhWyJ1c2VyX2FnZW50Il0sIENVUkxPUFRfUkVUVVJOVFJBTlNGRVIgPT4gdHJ1ZSwgQ1VSTE9QVF9GT0xMT1dMT0NBVElPTiA9PiB0cnVlLCBDVVJMT1BUX1ZFUkJPU0UgPT4gZmFsc2UsIENVUkxPUFRfU1NMX1ZFUklGWVBFRVIgPT4gZmFsc2UsIENVUkxPUFRfU1NMX1ZFUklGWUhPU1QgPT4gZmFsc2UsIENVUkxPUFRfQ09PS0lFRklMRSA9PiAkdGhpcy0+SUdEYXRhUGF0aCAuICRvYmpEYXRhWyJ1c2VybmFtZSJdIC4gIi5kYXQiLCBDVVJMT1BUX0NPT0tJRUpBUiA9PiAkdGhpcy0+SUdEYXRhUGF0aCAuICRvYmpEYXRhWyJ1c2VybmFtZSJdIC4gIi5kYXQiICk7DQogICAgICAgICAgICAgICAgJHJvbGxpbmdDdXJsLT5wb3N0KENvbnN0YW50czo6QVBJX1VSTCAuICJmcmllbmRzaGlwcy9jcmVhdGUvIiAuICR1c2VySUQgLiAiLyIsIFNpZ25hdHVyZVV0aWxzOjpnZW5lcmF0ZVNpZ25hdHVyZSgkZGF0YSksICRoZWFkZXJzLCAkb3B0aW9ucywgJHVzZXJbImRhdGEiXSk7DQogICAgICAgICAgICB9DQoNCiAgICAgICAgfQ0KICAgICAgICAkcm9sbGluZ0N1cmwtPnNldENhbGxiYWNrKGZ1bmN0aW9uKFJvbGxpbmdDdXJsXFJlcXVlc3QgJHJlcXVlc3QsIFJvbGxpbmdDdXJsXFJvbGxpbmdDdXJsICRyb2xsaW5nQ3VybCkgdXNlICgmJHRyaWVkVXNlcnMsICYkdG90YWxTdWNjZXNzQ291bnQpDQp7DQogICAgJHRyaWVkVXNlciA9IGFycmF5KCAidXNlcklEIiA9PiAkcmVxdWVzdC0+aWRlbnRpZmllclBhcmFtc1sidXllSUQiXSwgImluc3RhSUQiID0+ICRyZXF1ZXN0LT5pZGVudGlmaWVyUGFyYW1zWyJpbnN0YUlEIl0sICJ1c2VyTmljayIgPT4gJHJlcXVlc3QtPmlkZW50aWZpZXJQYXJhbXNbImt1bGxhbmljaUFkaSJdLCAic3RhdHVzIiA9PiAiZmFpbCIgKTsNCiAgICAkaXNFcnJvcmVkID0gJHJlcXVlc3QtPmdldFJlc3BvbnNlRXJyb3IoKTsNCiAgICBpZiggZW1wdHkoJGlzRXJyb3JlZCkgKSANCiAgICB7DQogICAgICAgICRkb25lblNvbnVjID0ganNvbl9kZWNvZGUoJHJlcXVlc3QtPmdldFJlc3BvbnNlVGV4dCgpLCB0cnVlKTsNCiAgICAgICAgaWYoICRkb25lblNvbnVjICkgDQogICAgICAgIHsNCiAgICAgICAgICAgIGlmKCAkcmVxdWVzdC0+aWRlbnRpZmllclBhcmFtc1siaXNXZWJDb29raWUiXSA9PSAxICkgDQogICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgaWYoICRkb25lblNvbnVjWyJzdGF0dXMiXSA9PSAib2siICYmICRkb25lblNvbnVjWyJyZXN1bHQiXSA9PSAiZm9sbG93aW5nIiApIA0KICAgICAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAgICAgJHRvdGFsU3VjY2Vzc0NvdW50Kys7DQogICAgICAgICAgICAgICAgICAgICR0cmllZFVzZXJbInN0YXR1cyJdID0gInN1Y2Nlc3MiOw0KICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICBlbHNlDQogICAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICAgICAkdHJpZWRVc2VyWyJzdGF0dXMiXSA9ICJmYWlsIjsNCiAgICAgICAgICAgICAgICB9DQoNCiAgICAgICAgICAgIH0NCiAgICAgICAgICAgIGVsc2UNCiAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICBpZiggJGRvbmVuU29udWNbInN0YXR1cyJdID09ICJvayIgKSANCiAgICAgICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgICAgICR0b3RhbFN1Y2Nlc3NDb3VudCsrOw0KICAgICAgICAgICAgICAgICAgICAkdHJpZWRVc2VyWyJzdGF0dXMiXSA9ICJzdWNjZXNzIjsNCiAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICAgZWxzZQ0KICAgICAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAgICAgJHRyaWVkVXNlclsic3RhdHVzIl0gPSAiZmFpbCI7DQogICAgICAgICAgICAgICAgfQ0KDQogICAgICAgICAgICB9DQoNCiAgICAgICAgfQ0KICAgICAgICBlbHNlDQogICAgICAgIHsNCiAgICAgICAgICAgICR0cmllZFVzZXJbInN0YXR1cyJdID0gImZhaWwiOw0KICAgICAgICB9DQoNCiAgICB9DQogICAgZWxzZQ0KICAgIHsNCiAgICAgICAgJHRyaWVkVXNlclsic3RhdHVzIl0gPSAiZmFpbCI7DQogICAgfQ0KDQogICAgJHRyaWVkVXNlcnNbXSA9ICR0cmllZFVzZXI7DQogICAgJHJvbGxpbmdDdXJsLT5jbGVhckNvbXBsZXRlZCgpOw0KICAgICRyb2xsaW5nQ3VybC0+cHJ1bmVQZW5kaW5nUmVxdWVzdFF1ZXVlKCk7DQp9DQoNCik7DQogICAgICAgICRyb2xsaW5nQ3VybC0+c2V0U2ltdWx0YW5lb3VzTGltaXQoJHRoaXMtPnNpbXVsdGFuZXBvc3RzaXplKTsNCiAgICAgICAgJHJvbGxpbmdDdXJsLT5leGVjdXRlKCk7DQogICAgICAgIHJldHVybiBhcnJheSggInRvdGFsU3VjY2Vzc0NvdW50IiA9PiAkdG90YWxTdWNjZXNzQ291bnQsICJ1c2VycyIgPT4gJHRyaWVkVXNlcnMgKTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkbWVkaWFJRA0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICRtZWRpYUNvZGUNCiAgICAgICAgICogQHBhcmFtIGFycmF5ICAkY29tbWVudFRleHRzDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gY29tbWVudCgkbWVkaWFJRCwgJG1lZGlhQ29kZSwgJGNvbW1lbnRUZXh0cykNCiAgICB7DQogICAgICAgICR0b3RhbFN1Y2Nlc3NDb3VudCA9IDA7DQogICAgICAgICR0cmllZFVzZXJzID0gYXJyYXkoICApOw0KICAgICAgICAkY29tbWVudEluZGV4ID0gLTE7DQogICAgICAgIGlmKCBpc19hcnJheSgkY29tbWVudFRleHRzKSAmJiAhZW1wdHkoJGNvbW1lbnRUZXh0cykgKSANCiAgICAgICAgew0KICAgICAgICAgICAgJGFyck1lZGlhSUQgPSBleHBsb2RlKCJfIiwgJG1lZGlhSUQpOw0KICAgICAgICAgICAgJG1lZGlhSURCZWZvcmVyID0gJGFyck1lZGlhSURbMF07DQogICAgICAgICAgICAkcm9sbGluZ0N1cmwgPSBuZXcgUm9sbGluZ0N1cmxcUm9sbGluZ0N1cmwoKTsNCiAgICAgICAgICAgIGZvcmVhY2goICR0aGlzLT51c2VycyBhcyAkdXNlciApIA0KICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICRjb21tZW50SW5kZXgrKzsNCiAgICAgICAgICAgICAgICAkY29tbWVudEluZGV4ID0gKGlzc2V0KCRjb21tZW50VGV4dHNbJGNvbW1lbnRJbmRleF0pID8gJGNvbW1lbnRJbmRleCA6IDApOw0KICAgICAgICAgICAgICAgIGlmKCAkdXNlclsiZGF0YSJdWyJpc1dlYkNvb2tpZSJdID09IDEgKSANCiAgICAgICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgICAgICRvYmpJbnN0YWdyYW1XZWIgPSAkdXNlclsib2JqZWN0Il07DQogICAgICAgICAgICAgICAgICAgICRvYmpEYXRhID0gJG9iakluc3RhZ3JhbVdlYi0+Z2V0RGF0YSgpOw0KICAgICAgICAgICAgICAgICAgICAkcG9zdERhdGEgPSAiY29tbWVudF90ZXh0PSIgLiAkY29tbWVudFRleHRzWyRjb21tZW50SW5kZXhdOw0KICAgICAgICAgICAgICAgICAgICAkaGVhZGVycyA9IGFycmF5KCAiUmVmZXJlcjogaHR0cHM6Ly93d3cuaW5zdGFncmFtLmNvbS8iLCAiRE5UOiAxIiwgIk9yaWdpbjogaHR0cHM6Ly93d3cuaW5zdGFncmFtLmNvbS8iLCAiWC1DU1JGVG9rZW46ICIgLiB0cmltKCRvYmpEYXRhWyJ0b2tlbiJdKSwgIlgtUmVxdWVzdGVkLVdpdGg6IFhNTEh0dHBSZXF1ZXN0IiwgIlgtSW5zdGFncmFtLUFKQVg6IDEiLCAiQ29ubmVjdGlvbjogY2xvc2UiLCAiQ2FjaGUtQ29udHJvbDogbWF4LWFnZT0wIiwgIkFjY2VwdDogKi8qIiwgIkFjY2VwdC1MYW5ndWFnZTogdHI7cT0xIiwgIlhfRk9SV0FSREVEX0ZPUjogIiAuICRvYmpEYXRhWyJpcCJdLCAiUkVNT1RFX0FERFI6ICIgLiAkb2JqRGF0YVsiaXAiXSApOw0KICAgICAgICAgICAgICAgICAgICAkb3B0aW9ucyA9IGFycmF5KCBDVVJMT1BUX1VTRVJBR0VOVCA9PiAkb2JqRGF0YVsidXNlcl9hZ2VudCJdLCBDVVJMT1BUX1JFVFVSTlRSQU5TRkVSID0+IHRydWUsIENVUkxPUFRfRk9MTE9XTE9DQVRJT04gPT4gdHJ1ZSwgQ1VSTE9QVF9WRVJCT1NFID0+IGZhbHNlLCBDVVJMT1BUX1NTTF9WRVJJRllQRUVSID0+IGZhbHNlLCBDVVJMT1BUX1NTTF9WRVJJRllIT1NUID0+IGZhbHNlLCBDVVJMT1BUX0NPT0tJRUZJTEUgPT4gJHRoaXMtPklHRGF0YVBhdGggLiAkb2JqRGF0YVsidXNlcm5hbWUiXSAuICIuZGF0IiwgQ1VSTE9QVF9DT09LSUVKQVIgPT4gJHRoaXMtPklHRGF0YVBhdGggLiAkb2JqRGF0YVsidXNlcm5hbWUiXSAuICIuZGF0IiApOw0KICAgICAgICAgICAgICAgICAgICAkcm9sbGluZ0N1cmwtPnBvc3QoQ29uc3RhbnRzOjpXRUJfVVJMIC4gIndlYi9jb21tZW50cy8iIC4gJG1lZGlhSURCZWZvcmVyIC4gIi9hZGQvIiwgJHBvc3REYXRhLCAkaGVhZGVycywgJG9wdGlvbnMsICR1c2VyWyJkYXRhIl0pOw0KICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICBlbHNlDQogICAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICAgICAkb2JqSW5zdGFncmFtID0gJHVzZXJbIm9iamVjdCJdOw0KICAgICAgICAgICAgICAgICAgICAkb2JqRGF0YSA9ICRvYmpJbnN0YWdyYW0tPmdldERhdGEoKTsNCiAgICAgICAgICAgICAgICAgICAgJGRhdGEgPSBqc29uX2VuY29kZShhcnJheSggIl91dWlkIiA9PiAkb2JqRGF0YVsidXVpZCJdLCAiX3VpZCIgPT4gJG9iakRhdGFbInVzZXJuYW1lX2lkIl0sICJfY3NyZnRva2VuIiA9PiAkb2JqRGF0YVsidG9rZW4iXSwgImNvbW1lbnRfdGV4dCIgPT4gJGNvbW1lbnRUZXh0c1skY29tbWVudEluZGV4XSApKTsNCiAgICAgICAgICAgICAgICAgICAgJGhlYWRlcnMgPSBhcnJheSggIkNvbm5lY3Rpb246IGNsb3NlIiwgIkFjY2VwdDogKi8qIiwgIlgtSUctQ2FwYWJpbGl0aWVzOiAiIC4gQ29uc3RhbnRzOjpYX0lHX0NhcGFiaWxpdGllcywgIlgtSUctQ29ubmVjdGlvbi1UeXBlOiBXSUZJIiwgIkNvbnRlbnQtdHlwZTogYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkOyBjaGFyc2V0PVVURi04IiwgIkFjY2VwdC1MYW5ndWFnZTogdHItVFIiLCAiWF9GT1JXQVJERURfRk9SOiAiIC4gJG9iakRhdGFbImlwIl0sICJSRU1PVEVfQUREUjogIiAuICRvYmpEYXRhWyJpcCJdICk7DQogICAgICAgICAgICAgICAgICAgICRvcHRpb25zID0gYXJyYXkoIENVUkxPUFRfVVNFUkFHRU5UID0+ICRvYmpEYXRhWyJ1c2VyX2FnZW50Il0sIENVUkxPUFRfUkVUVVJOVFJBTlNGRVIgPT4gdHJ1ZSwgQ1VSTE9QVF9GT0xMT1dMT0NBVElPTiA9PiB0cnVlLCBDVVJMT1BUX1ZFUkJPU0UgPT4gZmFsc2UsIENVUkxPUFRfU1NMX1ZFUklGWVBFRVIgPT4gZmFsc2UsIENVUkxPUFRfU1NMX1ZFUklGWUhPU1QgPT4gZmFsc2UsIENVUkxPUFRfQ09PS0lFRklMRSA9PiAkdGhpcy0+SUdEYXRhUGF0aCAuICRvYmpEYXRhWyJ1c2VybmFtZSJdIC4gIi5kYXQiLCBDVVJMT1BUX0NPT0tJRUpBUiA9PiAkdGhpcy0+SUdEYXRhUGF0aCAuICRvYmpEYXRhWyJ1c2VybmFtZSJdIC4gIi5kYXQiICk7DQogICAgICAgICAgICAgICAgICAgICRyb2xsaW5nQ3VybC0+cG9zdChDb25zdGFudHM6OkFQSV9VUkwgLiAibWVkaWEvIiAuICRtZWRpYUlEIC4gIi9jb21tZW50LyIsIFNpZ25hdHVyZVV0aWxzOjpnZW5lcmF0ZVNpZ25hdHVyZSgkZGF0YSksICRoZWFkZXJzLCAkb3B0aW9ucywgJHVzZXJbImRhdGEiXSk7DQogICAgICAgICAgICAgICAgfQ0KDQogICAgICAgICAgICB9DQogICAgICAgICAgICAkcm9sbGluZ0N1cmwtPnNldENhbGxiYWNrKGZ1bmN0aW9uKFJvbGxpbmdDdXJsXFJlcXVlc3QgJHJlcXVlc3QsIFJvbGxpbmdDdXJsXFJvbGxpbmdDdXJsICRyb2xsaW5nQ3VybCkgdXNlICgmJHRyaWVkVXNlcnMsICYkdG90YWxTdWNjZXNzQ291bnQpDQp7DQogICAgJHRyaWVkVXNlciA9IGFycmF5KCAidXNlcklEIiA9PiAkcmVxdWVzdC0+aWRlbnRpZmllclBhcmFtc1sidXllSUQiXSwgImluc3RhSUQiID0+ICRyZXF1ZXN0LT5pZGVudGlmaWVyUGFyYW1zWyJpbnN0YUlEIl0sICJ1c2VyTmljayIgPT4gJHJlcXVlc3QtPmlkZW50aWZpZXJQYXJhbXNbImt1bGxhbmljaUFkaSJdLCAic3RhdHVzIiA9PiAiZmFpbCIgKTsNCiAgICAkaXNFcnJvcmVkID0gJHJlcXVlc3QtPmdldFJlc3BvbnNlRXJyb3IoKTsNCiAgICBpZiggZW1wdHkoJGlzRXJyb3JlZCkgKSANCiAgICB7DQogICAgICAgICRkb25lblNvbnVjID0ganNvbl9kZWNvZGUoJHJlcXVlc3QtPmdldFJlc3BvbnNlVGV4dCgpLCB0cnVlKTsNCiAgICAgICAgaWYoICRkb25lblNvbnVjICkgDQogICAgICAgIHsNCiAgICAgICAgICAgIGlmKCAkcmVxdWVzdC0+aWRlbnRpZmllclBhcmFtc1siaXNXZWJDb29raWUiXSA9PSAxICkgDQogICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgaWYoIGlzc2V0KCRkb25lblNvbnVjWyJzdGF0dXMiXSkgJiYgJGRvbmVuU29udWNbInN0YXR1cyJdID09ICJvayIgKSANCiAgICAgICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgICAgICR0b3RhbFN1Y2Nlc3NDb3VudCsrOw0KICAgICAgICAgICAgICAgICAgICAkdHJpZWRVc2VyWyJzdGF0dXMiXSA9ICJzdWNjZXNzIjsNCiAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICAgZWxzZQ0KICAgICAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAgICAgJHRyaWVkVXNlclsic3RhdHVzIl0gPSAiZmFpbCI7DQogICAgICAgICAgICAgICAgfQ0KDQogICAgICAgICAgICB9DQogICAgICAgICAgICBlbHNlDQogICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgaWYoIGlzc2V0KCRkb25lblNvbnVjWyJzdGF0dXMiXSkgJiYgJGRvbmVuU29udWNbInN0YXR1cyJdID09ICJvayIgKSANCiAgICAgICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgICAgICR0b3RhbFN1Y2Nlc3NDb3VudCsrOw0KICAgICAgICAgICAgICAgICAgICAkdHJpZWRVc2VyWyJzdGF0dXMiXSA9ICJzdWNjZXNzIjsNCiAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICAgZWxzZQ0KICAgICAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAgICAgJHRyaWVkVXNlclsic3RhdHVzIl0gPSAiZmFpbCI7DQogICAgICAgICAgICAgICAgfQ0KDQogICAgICAgICAgICB9DQoNCiAgICAgICAgfQ0KICAgICAgICBlbHNlDQogICAgICAgIHsNCiAgICAgICAgICAgICR0cmllZFVzZXJbInN0YXR1cyJdID0gImZhaWwiOw0KICAgICAgICB9DQoNCiAgICB9DQogICAgZWxzZQ0KICAgIHsNCiAgICAgICAgJHRyaWVkVXNlclsic3RhdHVzIl0gPSAiZmFpbCI7DQogICAgfQ0KDQogICAgJHRyaWVkVXNlcnNbXSA9ICR0cmllZFVzZXI7DQogICAgJHJvbGxpbmdDdXJsLT5jbGVhckNvbXBsZXRlZCgpOw0KICAgICRyb2xsaW5nQ3VybC0+cHJ1bmVQZW5kaW5nUmVxdWVzdFF1ZXVlKCk7DQp9DQoNCik7DQogICAgICAgICAgICAkcm9sbGluZ0N1cmwtPnNldFNpbXVsdGFuZW91c0xpbWl0KCR0aGlzLT5zaW11bHRhbmVwb3N0c2l6ZSk7DQogICAgICAgICAgICAkcm9sbGluZ0N1cmwtPmV4ZWN1dGUoKTsNCiAgICAgICAgfQ0KDQogICAgICAgIHJldHVybiBhcnJheSggInRvdGFsU3VjY2Vzc0NvdW50IiA9PiAkdG90YWxTdWNjZXNzQ291bnQsICJ1c2VycyIgPT4gJHRyaWVkVXNlcnMgKTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiB2YWxpZGF0ZSgpDQogICAgew0KICAgICAgICAkdG90YWxTdWNjZXNzQ291bnQgPSAwOw0KICAgICAgICAkdHJpZWRVc2VycyA9IGFycmF5KCAgKTsNCiAgICAgICAgJHJvbGxpbmdDdXJsID0gbmV3IFJvbGxpbmdDdXJsXFJvbGxpbmdDdXJsKCk7DQogICAgICAgIGZvcmVhY2goICR0aGlzLT51c2VycyBhcyAkdXNlciApIA0KICAgICAgICB7DQogICAgICAgICAgICBpZiggJHVzZXJbImRhdGEiXVsiaXNXZWJDb29raWUiXSA9PSAxICkgDQogICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgJG9iakluc3RhZ3JhbVdlYiA9ICR1c2VyWyJvYmplY3QiXTsNCiAgICAgICAgICAgICAgICAkb2JqRGF0YSA9ICRvYmpJbnN0YWdyYW1XZWItPmdldERhdGEoKTsNCiAgICAgICAgICAgICAgICAkaGVhZGVycyA9IGFycmF5KCAiUmVmZXJlcjogaHR0cHM6Ly93d3cuaW5zdGFncmFtLmNvbS9pbnN0YWdyYW0vIiwgIkROVDogMSIsICJPcmlnaW46IGh0dHBzOi8vd3d3Lmluc3RhZ3JhbS5jb20vIiwgIlgtQ1NSRlRva2VuOiAiIC4gdHJpbSgkb2JqRGF0YVsidG9rZW4iXSksICJYLVJlcXVlc3RlZC1XaXRoOiBYTUxIdHRwUmVxdWVzdCIsICJYLUluc3RhZ3JhbS1BSkFYOiAxIiwgIkNvbm5lY3Rpb246IGNsb3NlIiwgIkNhY2hlLUNvbnRyb2w6IG1heC1hZ2U9MCIsICJBY2NlcHQ6ICovKiIsICJBY2NlcHQtTGFuZ3VhZ2U6IHRyO3E9MSIsICJYX0ZPUldBUkRFRF9GT1I6ICIgLiAkb2JqRGF0YVsiaXAiXSwgIlJFTU9URV9BRERSOiAiIC4gJG9iakRhdGFbImlwIl0gKTsNCiAgICAgICAgICAgICAgICAkb3B0aW9ucyA9IGFycmF5KCBDVVJMT1BUX1VTRVJBR0VOVCA9PiAkb2JqRGF0YVsidXNlcl9hZ2VudCJdLCBDVVJMT1BUX1JFVFVSTlRSQU5TRkVSID0+IHRydWUsIENVUkxPUFRfRk9MTE9XTE9DQVRJT04gPT4gdHJ1ZSwgQ1VSTE9QVF9WRVJCT1NFID0+IGZhbHNlLCBDVVJMT1BUX1NTTF9WRVJJRllQRUVSID0+IGZhbHNlLCBDVVJMT1BUX1NTTF9WRVJJRllIT1NUID0+IGZhbHNlLCBDVVJMT1BUX0NPT0tJRUZJTEUgPT4gJHRoaXMtPklHRGF0YVBhdGggLiAkb2JqRGF0YVsidXNlcm5hbWUiXSAuICIuZGF0IiwgQ1VSTE9QVF9DT09LSUVKQVIgPT4gJHRoaXMtPklHRGF0YVBhdGggLiAkb2JqRGF0YVsidXNlcm5hbWUiXSAuICIuZGF0IiApOw0KICAgICAgICAgICAgICAgICRyb2xsaW5nQ3VybC0+Z2V0KENvbnN0YW50czo6V0VCX1VSTCAuICJhY2NvdW50cy9hY3Rpdml0eS8/X19hPTEiLCAkaGVhZGVycywgJG9wdGlvbnMsICR1c2VyWyJkYXRhIl0pOw0KICAgICAgICAgICAgfQ0KICAgICAgICAgICAgZWxzZQ0KICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICRvYmpJbnN0YWdyYW0gPSAkdXNlclsib2JqZWN0Il07DQogICAgICAgICAgICAgICAgJG9iakRhdGEgPSAkb2JqSW5zdGFncmFtLT5nZXREYXRhKCk7DQogICAgICAgICAgICAgICAgJGhlYWRlcnMgPSBhcnJheSggIkNvbm5lY3Rpb246IGNsb3NlIiwgIkFjY2VwdDogKi8qIiwgIlgtSUctQ2FwYWJpbGl0aWVzOiAiIC4gQ29uc3RhbnRzOjpYX0lHX0NhcGFiaWxpdGllcywgIlgtSUctQ29ubmVjdGlvbi1UeXBlOiBXSUZJIiwgIkNvbnRlbnQtdHlwZTogYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkOyBjaGFyc2V0PVVURi04IiwgIkFjY2VwdC1MYW5ndWFnZTogdHItVFIiLCAiWF9GT1JXQVJERURfRk9SOiAiIC4gJG9iakRhdGFbImlwIl0sICJSRU1PVEVfQUREUjogIiAuICRvYmpEYXRhWyJpcCJdICk7DQogICAgICAgICAgICAgICAgJG9wdGlvbnMgPSBhcnJheSggQ1VSTE9QVF9VU0VSQUdFTlQgPT4gJG9iakRhdGFbInVzZXJfYWdlbnQiXSwgQ1VSTE9QVF9SRVRVUk5UUkFOU0ZFUiA9PiB0cnVlLCBDVVJMT1BUX0ZPTExPV0xPQ0FUSU9OID0+IHRydWUsIENVUkxPUFRfVkVSQk9TRSA9PiBmYWxzZSwgQ1VSTE9QVF9TU0xfVkVSSUZZUEVFUiA9PiBmYWxzZSwgQ1VSTE9QVF9TU0xfVkVSSUZZSE9TVCA9PiBmYWxzZSwgQ1VSTE9QVF9DT09LSUVGSUxFID0+ICR0aGlzLT5JR0RhdGFQYXRoIC4gJG9iakRhdGFbInVzZXJuYW1lIl0gLiAiLmRhdCIsIENVUkxPUFRfQ09PS0lFSkFSID0+ICR0aGlzLT5JR0RhdGFQYXRoIC4gJG9iakRhdGFbInVzZXJuYW1lIl0gLiAiLmRhdCIgKTsNCiAgICAgICAgICAgICAgICAkcm9sbGluZ0N1cmwtPmdldChDb25zdGFudHM6OkFQSV9VUkwgLiAiZmVlZC90aW1lbGluZS8/cmFua190b2tlbj0iIC4gJG9iakRhdGFbInJhbmtfdG9rZW4iXSAuICImcmFua2VkX2NvbnRlbnQ9dHJ1ZSIsICRoZWFkZXJzLCAkb3B0aW9ucywgJHVzZXJbImRhdGEiXSk7DQogICAgICAgICAgICB9DQoNCiAgICAgICAgfQ0KICAgICAgICAkcm9sbGluZ0N1cmwtPnNldENhbGxiYWNrKGZ1bmN0aW9uKFJvbGxpbmdDdXJsXFJlcXVlc3QgJHJlcXVlc3QsIFJvbGxpbmdDdXJsXFJvbGxpbmdDdXJsICRyb2xsaW5nQ3VybCkgdXNlICgmJHRyaWVkVXNlcnMsICYkdG90YWxTdWNjZXNzQ291bnQpDQp7DQogICAgJHRyaWVkVXNlciA9IGFycmF5KCAidXNlcklEIiA9PiAkcmVxdWVzdC0+aWRlbnRpZmllclBhcmFtc1sidXllSUQiXSwgImluc3RhSUQiID0+ICRyZXF1ZXN0LT5pZGVudGlmaWVyUGFyYW1zWyJpbnN0YUlEIl0sICJ1c2VyTmljayIgPT4gJHJlcXVlc3QtPmlkZW50aWZpZXJQYXJhbXNbImt1bGxhbmljaUFkaSJdLCAic3RhdHVzIiA9PiAiZmFpbCIgKTsNCiAgICAkaXNFcnJvcmVkID0gJHJlcXVlc3QtPmdldFJlc3BvbnNlRXJyb3IoKTsNCiAgICBpZiggZW1wdHkoJGlzRXJyb3JlZCkgKSANCiAgICB7DQogICAgICAgICRkb25lblNvbnVjID0ganNvbl9kZWNvZGUoJHJlcXVlc3QtPmdldFJlc3BvbnNlVGV4dCgpLCB0cnVlKTsNCiAgICAgICAgaWYoICRkb25lblNvbnVjICkgDQogICAgICAgIHsNCiAgICAgICAgICAgIGlmKCAkcmVxdWVzdC0+aWRlbnRpZmllclBhcmFtc1siaXNXZWJDb29raWUiXSA9PSAxICkgDQogICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgJHJlc3BvbnNlRGF0YSA9ICRyZXF1ZXN0LT5nZXRSZXNwb25zZUluZm8oKTsNCiAgICAgICAgICAgICAgICBpZiggJHJlc3BvbnNlRGF0YVsiaHR0cF9jb2RlIl0gPT0gIjIwMCIgKSANCiAgICAgICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgICAgICR0b3RhbFN1Y2Nlc3NDb3VudCsrOw0KICAgICAgICAgICAgICAgICAgICAkdHJpZWRVc2VyWyJzdGF0dXMiXSA9ICJzdWNjZXNzIjsNCiAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICAgZWxzZQ0KICAgICAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAgICAgJHRyaWVkVXNlclsic3RhdHVzIl0gPSAiZmFpbCI7DQogICAgICAgICAgICAgICAgfQ0KDQogICAgICAgICAgICB9DQogICAgICAgICAgICBlbHNlDQogICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgaWYoICRkb25lblNvbnVjWyJzdGF0dXMiXSA9PSAib2siICkgDQogICAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICAgICAkdG90YWxTdWNjZXNzQ291bnQrKzsNCiAgICAgICAgICAgICAgICAgICAgJHRyaWVkVXNlclsic3RhdHVzIl0gPSAic3VjY2VzcyI7DQogICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgIGVsc2UNCiAgICAgICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgICAgICR0cmllZFVzZXJbInN0YXR1cyJdID0gImZhaWwiOw0KICAgICAgICAgICAgICAgIH0NCg0KICAgICAgICAgICAgfQ0KDQogICAgICAgIH0NCiAgICAgICAgZWxzZQ0KICAgICAgICB7DQogICAgICAgICAgICAkdHJpZWRVc2VyWyJzdGF0dXMiXSA9ICJmYWlsIjsNCiAgICAgICAgfQ0KDQogICAgfQ0KICAgIGVsc2UNCiAgICB7DQogICAgICAgICR0cmllZFVzZXJbInN0YXR1cyJdID0gImZhaWwiOw0KICAgIH0NCg0KICAgICR0cmllZFVzZXJzW10gPSAkdHJpZWRVc2VyOw0KICAgICRyb2xsaW5nQ3VybC0+Y2xlYXJDb21wbGV0ZWQoKTsNCiAgICAkcm9sbGluZ0N1cmwtPnBydW5lUGVuZGluZ1JlcXVlc3RRdWV1ZSgpOw0KfQ0KDQopOw0KICAgICAgICAkcm9sbGluZ0N1cmwtPnNldFNpbXVsdGFuZW91c0xpbWl0KCR0aGlzLT5zaW11bHRhbmVwb3N0c2l6ZSk7DQogICAgICAgICRyb2xsaW5nQ3VybC0+ZXhlY3V0ZSgpOw0KICAgICAgICByZXR1cm4gYXJyYXkoICJ0b3RhbFN1Y2Nlc3NDb3VudCIgPT4gJHRvdGFsU3VjY2Vzc0NvdW50LCAidXNlcnMiID0+ICR0cmllZFVzZXJzICk7DQogICAgfQ0KDQp9DQoNCg0K")); ?>
Function Calls
| base64_decode | 1 |
Stats
| MD5 | 34ad9f4ed823e72bd6a5eb3c93ec317c |
| Eval Count | 1 |
| Decode Time | 187 ms |