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("PD9waHANCg0KDQpyZXF1aXJlKCdsaXNhbnMucGhwJyk7Ly9saXNhbnMucGh..
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 = "MYA-";//Lisans kodu bana istediiniz yaz eklenebilir
$son = "-2018";//Lisans kodu sonuna istediiniz yaz eklenebilir
$m = "md5";//md5 sifreleme
$s = "sha1";//sha1 sifreleme
$lisans[\'hash\']=wordwrap(strtoupper($s ($s ($s ($s ($m ($s ($s ($m ($lisans[\'site\'].date(\'Ymd\')))))))))),5,\'-\',true);//lisans kodunu olustur ve 5 karakterde bir - koy
$liskod = $lisans[\'hash\'];
$cevir=strrev($liskod);//lisans kodunu tersine cevir
$bcs = "$bas$cevir$son";
if($bcs!==$lisanskodu)//lisans kodunu kontrol et fark varsa sunucuya baglan - 24 saatte bir lisans kodu degisir
{
$lisans_cevap=file_get_contents(\'http://insta.adanabilisim.net/lisans/kontrol.php?site=\'.$lisans[\'site\']);//lisans sunucusundan lisans kontrolu yap
if($lisans_cevap!=\'LISANSLI\')//sunucudan gelen cevap LISANSLI degilse
die(\'Bu siteye ait lisans bulunamadi!!! Lutfen lisans almak icin sorunsuzscript.com adresini ziyaret ediniz\');//lisans gecersiz mesaji verilir islem durur
$lyaz = fopen(\'lisans.php\',"w+");//lisans.php dosyasini ac yeni lisans kodunu yaz bu dosya yazlabilir olmaldr
@fwrite($lyaz,\'<?php
/**
Sorunsuz Script Php Lisans Sistemi
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);
}
class Instagram
{
protected $username;
protected $password;
protected $debug;
protected $uuid;
protected $device_id;
protected $username_id;
/**
* @var Settings
*/
public $settings;
protected $userAgent;
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)
{
$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->checkSettings($username);
$this->setUser($username, $password);
}
/**
* Set the user. Manage multiple accounts.
*
* @param string $username
* Your Instagram username.
* @param string $password
* Your Instagram password.
*/
public function setUser($username, $password)
{
$this->username = $username;
$this->password = $password;
$this->checkSettings($username);
$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)
{
$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 )
{
$this->settings->set("ip", "78." . rand(160, 191) . "." . rand(1, 255) . "." . rand(1, 255));
}
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);
}
$this->userAgent = $this->settings->get("user_agent");
}
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->userAgent, "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->autoCompleteUserList();
$this->timelineFeed();
$this->getRankedRecipients();
$this->getRecentRecipients();
$this->megaphoneLog();
$this->getv2Inbox();
$this->getRecentActivity();
$this->getReelsTrayFeed();
$this->explore();
return $login[1];
}
$check = $this->timelineFeed();
if( isset($check["message"]) && $check["message"] == "login_required" )
{
return $this->login(true);
}
$this->autoCompleteUserList();
$this->getReelsTrayFeed();
$this->getRankedRecipients();
$this->getRecentRecipients();
$this->megaphoneLog();
$this->getv2Inbox();
$this->getRecentActivity();
$this->explore();
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->userAgent);
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->userAgent);
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->userAgent);
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->userAgent);
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->userAgent);
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->userAgent);
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->userAgent);
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 $fake_ip = NULL;
protected $isLoggedIn = false;
protected $IGDataPath = 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)
{
if( !is_null($IGDataPath) )
{
$this->IGDataPath = $IGDataPath;
}
else
{
$this->IGDataPath = Wow::get("project/cookiePath") . "instagram/";
}
$this->setUser($username);
}
/**
* Set the user. Manage multiple accounts.
*
* @param string $username
* Your Instagram username.
*/
public function setUser($username)
{
$this->username = $username;
if( file_exists($this->IGDataPath . (string) $this->username . ".dat") )
{
$cookieFile = file_get_contents($this->IGDataPath . (string) $username . ".dat");
$userCookies = Utils::cookieToArray($cookieFile);
$this->isLoggedIn = true;
$this->username_id = trim($userCookies["ds_user_id"]);
$this->token = trim($userCookies["csrftoken"]);
$this->fake_ip = "78." . rand(160, 191) . "." . rand(1, 255) . "." . rand(1, 255);
}
}
public function getData()
{
return array( "username" => $this->username, "username_id" => $this->username_id, "token" => $this->token, "ip" => $this->fake_ip );
}
/**
* Comment media.
*
* @param string $mediaId
* Media id
* @param string $commentText
* Comment Text
*
* @return array
* comment media data
*/
public function comment($mediaId, $commentText)
{
$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)
{
$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->fake_ip, "REMOTE_ADDR: " . $this->fake_ip );
$headers = array_merge($headers, $optionalheaders);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, Constants::WEB_URL . $endpoint);
curl_setopt($ch, CURLOPT_USERAGENT, Constants::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/accounts/edit/";
$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, true);
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)
{
$arrCookies = array();
$fileVals = self::extractCookies($string);
foreach( $fileVals as $cookie )
{
$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( $line[0] != "#" && 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 = "9.4.0";
const IG_SIG_KEY = "fc4720e1bf9d79463f62608c86fbddd374cc71bbfb98216b52e3f75333bd130d";
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 = "3R4=";
const ANDROID_VERSION = 18;
const ANDROID_RELEASE = "4.3";
const WEB_URL = "https://www.instagram.com/";
const WEB_USER_AGENT = "Mozilla/5.0 (Linux; Android 4.4.2; HUAWEI MT7-L09 Build/HuaweiMT7-L09) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.76 Mobile Safari/537.36";
}
class BulkReaction
{
/**
* @var array $users
*/
protected $users = NULL;
/**
* @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();
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 => Constants::WEB_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/" . $mediaID . "/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"] );
$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";
}
$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 => Constants::WEB_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"] );
$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";
}
$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) )
{
$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 => Constants::WEB_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/" . $mediaID . "/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"] );
$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";
}
$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 => Constants::WEB_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 . "accounts/activity/?__a=1", NULL, $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"] );
$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";
}
$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("PD9waHANCg0KDQpyZXF1aXJlKCdsaXNhbnMucGhwJyk7Ly9saXNhbnMucGhwIGRvc3lhc2luaSBla2xlDQokbGlzYW5zWydzaXRlJ109Z2V0ZW52KCdIVFRQX0hPU1QnKTsvL3NpdGUgZG9tYWluaW5pIGFsDQppZiAoc3Vic3RyKCRsaXNhbnNbJ3NpdGUnXSwgMCwgNCkgPT0gInd3dy4iKS8vZG9tYWluIGJhc2luZGFraSB3d3cuIHZhcm1pIHlva211IGtvbnRyb2wgZXQNCiRsaXNhbnNbJ3NpdGUnXT1zdWJzdHIoJGxpc2Fuc1snc2l0ZSddLDQpOy8vd3d3LiBzaWwgdmUgc2FkZWNlIGRvbWFpbi5jb20gb2xhcmFrIGFkcmVzaSBhbA0KIA0KJGJhcyA9ICJNWUEtIjsvL0xpc2FucyBrb2R1IGJhxZ/EsW5hIGlzdGVkacSfaW5peiB5YXrEsSBla2xlbmViaWxpcg0KJHNvbiA9ICItMjAxOCI7Ly9MaXNhbnMga29kdSBzb251bmEgaXN0ZWRpxJ9pbml6IHlhesSxIGVrbGVuZWJpbGlyDQokbSA9ICJtZDUiOy8vbWQ1IHNpZnJlbGVtZSANCiRzID0gInNoYTEiOy8vc2hhMSBzaWZyZWxlbWUgDQogDQogDQokbGlzYW5zWydoYXNoJ109d29yZHdyYXAoc3RydG91cHBlcigkcyAoJHMgKCRzICgkcyAoJG0gKCRzICgkcyAoJG0gKCRsaXNhbnNbJ3NpdGUnXS5kYXRlKCdZbWQnKSkpKSkpKSkpKSw1LCctJyx0cnVlKTsvL2xpc2FucyBrb2R1bnUgb2x1c3R1ciB2ZSA1IGthcmFrdGVyZGUgYmlyIC0ga295DQokbGlza29kID0gJGxpc2Fuc1snaGFzaCddOw0KJGNldmlyPXN0cnJldigkbGlza29kKTsvL2xpc2FucyBrb2R1bnUgdGVyc2luZSBjZXZpcg0KJGJjcyA9ICIkYmFzJGNldmlyJHNvbiI7DQogDQppZigkYmNzIT09JGxpc2Fuc2tvZHUpLy9saXNhbnMga29kdW51IGtvbnRyb2wgZXQgZmFyayB2YXJzYSBzdW51Y3V5YSBiYWdsYW4gLSAyNCBzYWF0dGUgYmlyIGxpc2FucyBrb2R1IGRlZ2lzaXINCnsNCiRsaXNhbnNfY2V2YXA9ZmlsZV9nZXRfY29udGVudHMoJ2h0dHA6Ly9pbnN0YS5hZGFuYWJpbGlzaW0ubmV0L2xpc2Fucy9rb250cm9sLnBocD9zaXRlPScuJGxpc2Fuc1snc2l0ZSddKTsvL2xpc2FucyBzdW51Y3VzdW5kYW4gbGlzYW5zIGtvbnRyb2x1IHlhcA0KaWYoJGxpc2Fuc19jZXZhcCE9J0xJU0FOU0xJJykvL3N1bnVjdWRhbiBnZWxlbiBjZXZhcCBMSVNBTlNMSSBkZWdpbHNlDQogDQpkaWUoJ0J1IHNpdGV5ZSBhaXQgbGlzYW5zIGJ1bHVuYW1hZGkhISEgTHV0ZmVuIGxpc2FucyBhbG1hayBpY2luIHNvcnVuc3V6c2NyaXB0LmNvbSBhZHJlc2luaSB6aXlhcmV0IGVkaW5peicpOy8vbGlzYW5zIGdlY2Vyc2l6IG1lc2FqaSB2ZXJpbGlyIGlzbGVtIGR1cnVyDQogDQokbHlheiA9IGZvcGVuKCdsaXNhbnMucGhwJywidysiKTsvL2xpc2Fucy5waHAgZG9zeWFzaW5pIGFjIHllbmkgbGlzYW5zIGtvZHVudSB5YXogYnUgZG9zeWEgeWF6xLFsYWJpbGlyIG9sbWFsxLFkxLFyDQpAZndyaXRlKCRseWF6LCc8P3BocA0KLyoqDQogICAgU29ydW5zdXogU2NyaXB0IFBocCBMaXNhbnMgU2lzdGVtaQ0KICAgIA0KICAgIA0KICAgIEJ1IGRvc3lheWEgaGVyaGFuZ2kgYmlyIGxpc2FucyBrb2R1IHlhem1hbml6IGdlcmVrbWV6DQogICAgQWxtaXMgb2xkdWd1bnV6IHVydW5lIGFpdCBsaXNhbnMgc2lzdGVtZGUgYWt0aWYgb2xkdWd1bmRhDQogICAgTGlzYW5zIGtvZHVudXogb3RvbWF0aWsgb2xhcmFrIGdpcmlsZWNla3Rpci4NCgkNCglZZW5pIGxpc2FucyBhbG1hayBpY2luIHd3dy5zb3J1bnN1enNjcmlwdC5jb20gYWRyZXNpbmkgeml5YXJldCBlZGViaWxpcnNpbml6DQogDQogKi8NCiRsaXNhbnNrb2R1PSInLiRiY3MuJyI7DQo/PicpOw0KZmNsb3NlKCRseWF6KTsNCn0NCg0KY2xhc3MgSW5zdGFncmFtDQp7DQogICAgcHJvdGVjdGVkICR1c2VybmFtZTsNCiAgICBwcm90ZWN0ZWQgJHBhc3N3b3JkOw0KICAgIHByb3RlY3RlZCAkZGVidWc7DQogICAgcHJvdGVjdGVkICR1dWlkOw0KICAgIHByb3RlY3RlZCAkZGV2aWNlX2lkOw0KICAgIHByb3RlY3RlZCAkdXNlcm5hbWVfaWQ7DQovKioNCiAgICAgICAgICogQHZhciBTZXR0aW5ncw0KICAgICAgICAgKi8NCiAgICBwdWJsaWMgJHNldHRpbmdzOw0KICAgIHByb3RlY3RlZCAkdXNlckFnZW50Ow0KICAgIHByb3RlY3RlZCAkdG9rZW47DQogICAgcHJvdGVjdGVkICRpc0xvZ2dlZEluID0gZmFsc2U7DQogICAgcHJvdGVjdGVkICRyYW5rX3Rva2VuOw0KICAgIHByb3RlY3RlZCAkSUdEYXRhUGF0aDsNCg0KICAgIC8qKg0KICAgICAgICAgKiBEZWZhdWx0IGNsYXNzIGNvbnN0cnVjdG9yLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICR1c2VybmFtZQ0KICAgICAgICAgKiAgIFlvdXIgSW5zdGFncmFtIHVzZXJuYW1lLg0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICRwYXNzd29yZA0KICAgICAgICAgKiAgIFlvdXIgSW5zdGFncmFtIHBhc3N3b3JkLg0KICAgICAgICAgKiBAcGFyYW0gICAgICAgICRkZWJ1Zw0KICAgICAgICAgKiAgIERlYnVnIG9uIG9yIG9mZiwgZmFsc2UgYnkgZGVmYXVsdC4NCiAgICAgICAgICogQHBhcmFtICAgICAgICAkSUdEYXRhUGF0aA0KICAgICAgICAgKiAgIERlZmF1bHQgZm9sZGVyIHRvIHN0b3JlIGRhdGEsIHlvdSBjYW4gY2hhbmdlIGl0Lg0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBfX2NvbnN0cnVjdCgkdXNlcm5hbWUsICRwYXNzd29yZCwgJGRlYnVnID0gZmFsc2UsICRJR0RhdGFQYXRoID0gTlVMTCkNCiAgICB7DQogICAgICAgICR1c2VybmFtZSA9IHRyaW0oJHVzZXJuYW1lKTsNCiAgICAgICAgJHBhc3N3b3JkID0gdHJpbSgkcGFzc3dvcmQpOw0KICAgICAgICAkdGhpcy0+ZGVidWcgPSAkZGVidWc7DQogICAgICAgICR0aGlzLT5kZXZpY2VfaWQgPSBTaWduYXR1cmVVdGlsczo6Z2VuZXJhdGVEZXZpY2VJZChtZDUoJHVzZXJuYW1lIC4gJHBhc3N3b3JkKSk7DQogICAgICAgIGlmKCAhaXNfbnVsbCgkSUdEYXRhUGF0aCkgKSANCiAgICAgICAgew0KICAgICAgICAgICAgJHRoaXMtPklHRGF0YVBhdGggPSAkSUdEYXRhUGF0aDsNCiAgICAgICAgfQ0KICAgICAgICBlbHNlDQogICAgICAgIHsNCiAgICAgICAgICAgICR0aGlzLT5JR0RhdGFQYXRoID0gV293OjpnZXQoInByb2plY3QvY29va2llUGF0aCIpIC4gImluc3RhZ3JhbS8iOw0KICAgICAgICB9DQoNCiAgICAgICAgJHRoaXMtPmNoZWNrU2V0dGluZ3MoJHVzZXJuYW1lKTsNCiAgICAgICAgJHRoaXMtPnNldFVzZXIoJHVzZXJuYW1lLCAkcGFzc3dvcmQpOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBTZXQgdGhlIHVzZXIuIE1hbmFnZSBtdWx0aXBsZSBhY2NvdW50cy4NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkdXNlcm5hbWUNCiAgICAgICAgICogICBZb3VyIEluc3RhZ3JhbSB1c2VybmFtZS4NCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkcGFzc3dvcmQNCiAgICAgICAgICogICBZb3VyIEluc3RhZ3JhbSBwYXNzd29yZC4NCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gc2V0VXNlcigkdXNlcm5hbWUsICRwYXNzd29yZCkNCiAgICB7DQogICAgICAgICR0aGlzLT51c2VybmFtZSA9ICR1c2VybmFtZTsNCiAgICAgICAgJHRoaXMtPnBhc3N3b3JkID0gJHBhc3N3b3JkOw0KICAgICAgICAkdGhpcy0+Y2hlY2tTZXR0aW5ncygkdXNlcm5hbWUpOw0KICAgICAgICAkdGhpcy0+dXVpZCA9IFNpZ25hdHVyZVV0aWxzOjpnZW5lcmF0ZVVVSUQodHJ1ZSk7DQogICAgICAgIGlmKCBmaWxlX2V4aXN0cygkdGhpcy0+SUdEYXRhUGF0aCAuIChzdHJpbmcpICR0aGlzLT51c2VybmFtZSAuICIuZGF0IikgJiYgJHRoaXMtPnNldHRpbmdzLT5nZXQoInVzZXJuYW1lX2lkIikgIT0gTlVMTCAmJiAkdGhpcy0+c2V0dGluZ3MtPmdldCgidG9rZW4iKSAhPSBOVUxMICkgDQogICAgICAgIHsNCiAgICAgICAgICAgICR0aGlzLT5pc0xvZ2dlZEluID0gdHJ1ZTsNCiAgICAgICAgICAgICR0aGlzLT51c2VybmFtZV9pZCA9ICR0aGlzLT5zZXR0aW5ncy0+Z2V0KCJ1c2VybmFtZV9pZCIpOw0KICAgICAgICAgICAgJHRoaXMtPnJhbmtfdG9rZW4gPSAkdGhpcy0+dXNlcm5hbWVfaWQgLiAiXyIgLiAkdGhpcy0+dXVpZDsNCiAgICAgICAgICAgICR0aGlzLT50b2tlbiA9ICR0aGlzLT5zZXR0aW5ncy0+Z2V0KCJ0b2tlbiIpOw0KICAgICAgICB9DQogICAgICAgIGVsc2UNCiAgICAgICAgew0KICAgICAgICAgICAgJHRoaXMtPmlzTG9nZ2VkSW4gPSBmYWxzZTsNCiAgICAgICAgfQ0KDQogICAgfQ0KDQogICAgcHJvdGVjdGVkIGZ1bmN0aW9uIGNoZWNrU2V0dGluZ3MoJHVzZXJuYW1lKQ0KICAgIHsNCiAgICAgICAgJHRoaXMtPnNldHRpbmdzID0gbmV3IFNldHRpbmdzKCR0aGlzLT5JR0RhdGFQYXRoIC4gJHVzZXJuYW1lIC4gIi5jbmYiKTsNCiAgICAgICAgaWYoICR0aGlzLT5zZXR0aW5ncy0+Z2V0KCJ2ZXJzaW9uIikgPT0gTlVMTCApIA0KICAgICAgICB7DQogICAgICAgICAgICAkdGhpcy0+c2V0dGluZ3MtPnNldCgidmVyc2lvbiIsIENvbnN0YW50czo6VkVSU0lPTik7DQogICAgICAgIH0NCg0KICAgICAgICBpZiggJHRoaXMtPnNldHRpbmdzLT5nZXQoImlwIikgPT0gTlVMTCApIA0KICAgICAgICB7DQogICAgICAgICAgICAkdGhpcy0+c2V0dGluZ3MtPnNldCgiaXAiLCAiNzguIiAuIHJhbmQoMTYwLCAxOTEpIC4gIi4iIC4gcmFuZCgxLCAyNTUpIC4gIi4iIC4gcmFuZCgxLCAyNTUpKTsNCiAgICAgICAgfQ0KDQogICAgICAgIGlmKCAkdGhpcy0+c2V0dGluZ3MtPmdldCgidXNlcl9hZ2VudCIpID09IE5VTEwgfHwgdmVyc2lvbl9jb21wYXJlKCR0aGlzLT5zZXR0aW5ncy0+Z2V0KCJ2ZXJzaW9uIiksIENvbnN0YW50czo6VkVSU0lPTikgPT0gLTEgKSANCiAgICAgICAgew0KICAgICAgICAgICAgJHVzZXJBZ2VudCA9IG5ldyBVc2VyQWdlbnQoJHRoaXMpOw0KICAgICAgICAgICAgJHVhID0gJHVzZXJBZ2VudC0+YnVpbGRVc2VyQWdlbnQoKTsNCiAgICAgICAgICAgICR0aGlzLT5zZXR0aW5ncy0+c2V0KCJ2ZXJzaW9uIiwgQ29uc3RhbnRzOjpWRVJTSU9OKTsNCiAgICAgICAgICAgICR0aGlzLT5zZXR0aW5ncy0+c2V0KCJ1c2VyX2FnZW50IiwgJHVhKTsNCiAgICAgICAgfQ0KDQogICAgICAgICR0aGlzLT51c2VyQWdlbnQgPSAkdGhpcy0+c2V0dGluZ3MtPmdldCgidXNlcl9hZ2VudCIpOw0KICAgIH0NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBnZXREYXRhKCkNCiAgICB7DQogICAgICAgIHJldHVybiBhcnJheSggInVzZXJuYW1lIiA9PiAkdGhpcy0+dXNlcm5hbWUsICJwYXNzd29yZCIgPT4gJHRoaXMtPnBhc3N3b3JkLCAidXNlcm5hbWVfaWQiID0+ICR0aGlzLT51c2VybmFtZV9pZCwgInV1aWQiID0+ICR0aGlzLT51dWlkLCAidG9rZW4iID0+ICR0aGlzLT50b2tlbiwgInJhbmtfdG9rZW4iID0+ICR0aGlzLT5yYW5rX3Rva2VuLCAidXNlcl9hZ2VudCIgPT4gJHRoaXMtPnVzZXJBZ2VudCwgImlwIiA9PiAkdGhpcy0+c2V0dGluZ3MtPmdldCgiaXAiKSApOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBMb2dpbiB0byBJbnN0YWdyYW0uDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBib29sICRmb3JjZQ0KICAgICAgICAgKiAgIEZvcmNlIGxvZ2luIHRvIEluc3RhZ3JhbSwgdGhpcyB3aWxsIGNyZWF0ZSBhIG5ldyBzZXNzaW9uDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICAgTG9naW4gZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBsb2dpbigkZm9yY2UgPSBmYWxzZSkNCiAgICB7DQogICAgICAgIGlmKCAhJHRoaXMtPmlzTG9nZ2VkSW4gfHwgJGZvcmNlICkgDQogICAgICAgIHsNCiAgICAgICAgICAgICRmZXRjaCA9ICR0aGlzLT5yZXF1ZXN0KCJzaS9mZXRjaF9oZWFkZXJzLz9jaGFsbGVuZ2VfdHlwZT1zaWdudXAmZ3VpZD0iIC4gU2lnbmF0dXJlVXRpbHM6OmdlbmVyYXRlVVVJRChmYWxzZSksIE5VTEwsIHRydWUpOw0KICAgICAgICAgICAgcHJlZ19tYXRjaCgiI1NldC1Db29raWU6IGNzcmZ0b2tlbj0oW147XSspIyIsICRmZXRjaFswXSwgJHRva2VuKTsNCiAgICAgICAgICAgICRkYXRhID0gYXJyYXkoICJwaG9uZV9pZCIgPT4gU2lnbmF0dXJlVXRpbHM6OmdlbmVyYXRlVVVJRCh0cnVlKSwgIl9jc3JmdG9rZW4iID0+ICR0b2tlblswXSwgInVzZXJuYW1lIiA9PiAkdGhpcy0+dXNlcm5hbWUsICJndWlkIiA9PiAkdGhpcy0+dXVpZCwgImRldmljZV9pZCIgPT4gJHRoaXMtPmRldmljZV9pZCwgInBhc3N3b3JkIiA9PiAkdGhpcy0+cGFzc3dvcmQsICJsb2dpbl9hdHRlbXB0X2NvdW50IiA9PiAiMCIgKTsNCiAgICAgICAgICAgICRsb2dpbiA9ICR0aGlzLT5yZXF1ZXN0KCJhY2NvdW50cy9sb2dpbi8iLCBTaWduYXR1cmVVdGlsczo6Z2VuZXJhdGVTaWduYXR1cmUoanNvbl9lbmNvZGUoJGRhdGEpKSwgdHJ1ZSk7DQogICAgICAgICAgICBpZiggJGxvZ2luWzFdWyJzdGF0dXMiXSA9PSAiZmFpbCIgKSANCiAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICB0aHJvdyBuZXcgRXhjZXB0aW9uKCRsb2dpblsxXVsibWVzc2FnZSJdKTsNCiAgICAgICAgICAgIH0NCg0KICAgICAgICAgICAgJHRoaXMtPmlzTG9nZ2VkSW4gPSB0cnVlOw0KICAgICAgICAgICAgJHRoaXMtPnVzZXJuYW1lX2lkID0gJGxvZ2luWzFdWyJsb2dnZWRfaW5fdXNlciJdWyJwayJdOw0KICAgICAgICAgICAgJHRoaXMtPnNldHRpbmdzLT5zZXQoInVzZXJuYW1lX2lkIiwgJHRoaXMtPnVzZXJuYW1lX2lkKTsNCiAgICAgICAgICAgICR0aGlzLT5yYW5rX3Rva2VuID0gJHRoaXMtPnVzZXJuYW1lX2lkIC4gIl8iIC4gJHRoaXMtPnV1aWQ7DQogICAgICAgICAgICBwcmVnX21hdGNoKCIjU2V0LUNvb2tpZTogY3NyZnRva2VuPShbXjtdKykjIiwgJGxvZ2luWzBdLCAkbWF0Y2gpOw0KICAgICAgICAgICAgJHRoaXMtPnRva2VuID0gJG1hdGNoWzFdOw0KICAgICAgICAgICAgJHRoaXMtPnNldHRpbmdzLT5zZXQoInRva2VuIiwgJHRoaXMtPnRva2VuKTsNCiAgICAgICAgICAgICR0aGlzLT5zeW5jRmVhdHVyZXMoKTsNCiAgICAgICAgICAgICR0aGlzLT5hdXRvQ29tcGxldGVVc2VyTGlzdCgpOw0KICAgICAgICAgICAgJHRoaXMtPnRpbWVsaW5lRmVlZCgpOw0KICAgICAgICAgICAgJHRoaXMtPmdldFJhbmtlZFJlY2lwaWVudHMoKTsNCiAgICAgICAgICAgICR0aGlzLT5nZXRSZWNlbnRSZWNpcGllbnRzKCk7DQogICAgICAgICAgICAkdGhpcy0+bWVnYXBob25lTG9nKCk7DQogICAgICAgICAgICAkdGhpcy0+Z2V0djJJbmJveCgpOw0KICAgICAgICAgICAgJHRoaXMtPmdldFJlY2VudEFjdGl2aXR5KCk7DQogICAgICAgICAgICAkdGhpcy0+Z2V0UmVlbHNUcmF5RmVlZCgpOw0KICAgICAgICAgICAgJHRoaXMtPmV4cGxvcmUoKTsNCiAgICAgICAgICAgIHJldHVybiAkbG9naW5bMV07DQogICAgICAgIH0NCg0KICAgICAgICAkY2hlY2sgPSAkdGhpcy0+dGltZWxpbmVGZWVkKCk7DQogICAgICAgIGlmKCBpc3NldCgkY2hlY2tbIm1lc3NhZ2UiXSkgJiYgJGNoZWNrWyJtZXNzYWdlIl0gPT0gImxvZ2luX3JlcXVpcmVkIiApIA0KICAgICAgICB7DQogICAgICAgICAgICByZXR1cm4gJHRoaXMtPmxvZ2luKHRydWUpOw0KICAgICAgICB9DQoNCiAgICAgICAgJHRoaXMtPmF1dG9Db21wbGV0ZVVzZXJMaXN0KCk7DQogICAgICAgICR0aGlzLT5nZXRSZWVsc1RyYXlGZWVkKCk7DQogICAgICAgICR0aGlzLT5nZXRSYW5rZWRSZWNpcGllbnRzKCk7DQogICAgICAgICR0aGlzLT5nZXRSZWNlbnRSZWNpcGllbnRzKCk7DQogICAgICAgICR0aGlzLT5tZWdhcGhvbmVMb2coKTsNCiAgICAgICAgJHRoaXMtPmdldHYySW5ib3goKTsNCiAgICAgICAgJHRoaXMtPmdldFJlY2VudEFjdGl2aXR5KCk7DQogICAgICAgICR0aGlzLT5leHBsb3JlKCk7DQogICAgICAgIHJldHVybiBhcnJheSggInN0YXR1cyIgPT4gIm9rIiApOw0KICAgIH0NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBzeW5jRmVhdHVyZXMoKQ0KICAgIHsNCiAgICAgICAgJGRhdGEgPSBqc29uX2VuY29kZShhcnJheSggIl91dWlkIiA9PiAkdGhpcy0+dXVpZCwgIl91aWQiID0+ICR0aGlzLT51c2VybmFtZV9pZCwgImlkIiA9PiAkdGhpcy0+dXNlcm5hbWVfaWQsICJfY3NyZnRva2VuIiA9PiAkdGhpcy0+dG9rZW4sICJleHBlcmltZW50cyIgPT4gQ29uc3RhbnRzOjpFWFBFUklNRU5UUyApKTsNCiAgICAgICAgcmV0dXJuICR0aGlzLT5yZXF1ZXN0KCJxZS9zeW5jLyIsIFNpZ25hdHVyZVV0aWxzOjpnZW5lcmF0ZVNpZ25hdHVyZSgkZGF0YSkpWzFdOw0KICAgIH0NCg0KICAgIHByb3RlY3RlZCBmdW5jdGlvbiBhdXRvQ29tcGxldGVVc2VyTGlzdCgpDQogICAgew0KICAgICAgICByZXR1cm4gJHRoaXMtPnJlcXVlc3QoImZyaWVuZHNoaXBzL2F1dG9jb21wbGV0ZV91c2VyX2xpc3QvIilbMV07DQogICAgfQ0KDQogICAgcHJvdGVjdGVkIGZ1bmN0aW9uIHRpbWVsaW5lRmVlZCgpDQogICAgew0KICAgICAgICByZXR1cm4gJHRoaXMtPnJlcXVlc3QoImZlZWQvdGltZWxpbmUvIilbMV07DQogICAgfQ0KDQogICAgcHJvdGVjdGVkIGZ1bmN0aW9uIG1lZ2FwaG9uZUxvZygpDQogICAgew0KICAgICAgICByZXR1cm4gJHRoaXMtPnJlcXVlc3QoIm1lZ2FwaG9uZS9sb2cvIilbMV07DQogICAgfQ0KDQogICAgcHJvdGVjdGVkIGZ1bmN0aW9uIGV4cG9zZSgpDQogICAgew0KICAgICAgICAkZGF0YSA9IGpzb25fZW5jb2RlKGFycmF5KCAiX3V1aWQiID0+ICR0aGlzLT51dWlkLCAiX3VpZCIgPT4gJHRoaXMtPnVzZXJuYW1lX2lkLCAiaWQiID0+ICR0aGlzLT51c2VybmFtZV9pZCwgIl9jc3JmdG9rZW4iID0+ICR0aGlzLT50b2tlbiwgImV4cGVyaW1lbnQiID0+ICJpZ19hbmRyb2lkX3Byb2ZpbGVfY29udGV4dHVhbF9mZWVkIiApKTsNCiAgICAgICAgJHRoaXMtPnJlcXVlc3QoInFlL2V4cG9zZS8iLCBTaWduYXR1cmVVdGlsczo6Z2VuZXJhdGVTaWduYXR1cmUoJGRhdGEpKTsNCiAgICAgICAgJHRoaXMtPnJlcXVlc3QoInFlL2V4cG9zZS8iLCBTaWduYXR1cmVVdGlsczo6Z2VuZXJhdGVTaWduYXR1cmUoJGRhdGEpKVsxXTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogTG9naW4gdG8gSW5zdGFncmFtLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGJvb2wNCiAgICAgICAgICogICAgUmV0dXJucyB0cnVlIGlmIGxvZ2dlZCBvdXQgY29ycmVjdGx5DQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGxvZ291dCgpDQogICAgew0KICAgICAgICAkbG9nb3V0ID0gJHRoaXMtPnJlcXVlc3QoImFjY291bnRzL2xvZ291dC8iKTsNCiAgICAgICAgaWYoICRsb2dvdXQgPT0gIm9rIiApIA0KICAgICAgICB7DQogICAgICAgICAgICByZXR1cm4gdHJ1ZTsNCiAgICAgICAgfQ0KDQogICAgICAgIHJldHVybiBmYWxzZTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogVXBsb2FkIHBob3RvIHRvIEluc3RhZ3JhbS4NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkcGhvdG8NCiAgICAgICAgICogICAgICAgICAgICAgICAgICAgICAgICBQYXRoIHRvIHlvdXIgcGhvdG8NCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkY2FwdGlvbg0KICAgICAgICAgKiAgICAgICAgICAgICAgICAgICAgICAgIENhcHRpb24gdG8gYmUgaW5jbHVkZWQgaW4geW91ciBwaG90by4NCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgICAgICAgICAgICAgIFVwbG9hZCBkYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIHVwbG9hZFBob3RvKCRwaG90bywgJGNhcHRpb24gPSBOVUxMLCAkdXBsb2FkX2lkID0gTlVMTCkNCiAgICB7DQogICAgICAgICRlbmRwb2ludCA9IENvbnN0YW50czo6QVBJX1VSTCAuICJ1cGxvYWQvcGhvdG8vIjsNCiAgICAgICAgJGJvdW5kYXJ5ID0gJHRoaXMtPnV1aWQ7DQogICAgICAgIGlmKCAhaXNfbnVsbCgkdXBsb2FkX2lkKSApIA0KICAgICAgICB7DQogICAgICAgICAgICAkZmlsZVRvVXBsb2FkID0gVXRpbHM6OmNyZWF0ZVZpZGVvSWNvbigkcGhvdG8pOw0KICAgICAgICB9DQogICAgICAgIGVsc2UNCiAgICAgICAgew0KICAgICAgICAgICAgJHVwbG9hZF9pZCA9IG51bWJlcl9mb3JtYXQocm91bmQobWljcm90aW1lKHRydWUpICogMTAwMCksIDAsICIiLCAiIik7DQogICAgICAgICAgICAkZmlsZVRvVXBsb2FkID0gZmlsZV9nZXRfY29udGVudHMoJHBob3RvKTsNCiAgICAgICAgfQ0KDQogICAgICAgICRib2RpZXMgPSBhcnJheSggYXJyYXkoICJ0eXBlIiA9PiAiZm9ybS1kYXRhIiwgIm5hbWUiID0+ICJ1cGxvYWRfaWQiLCAiZGF0YSIgPT4gJHVwbG9hZF9pZCApLCBhcnJheSggInR5cGUiID0+ICJmb3JtLWRhdGEiLCAibmFtZSIgPT4gIl91dWlkIiwgImRhdGEiID0+ICR0aGlzLT51dWlkICksIGFycmF5KCAidHlwZSIgPT4gImZvcm0tZGF0YSIsICJuYW1lIiA9PiAiX2NzcmZ0b2tlbiIsICJkYXRhIiA9PiAkdGhpcy0+dG9rZW4gKSwgYXJyYXkoICJ0eXBlIiA9PiAiZm9ybS1kYXRhIiwgIm5hbWUiID0+ICJpbWFnZV9jb21wcmVzc2lvbiIsICJkYXRhIiA9PiAie1wibGliX25hbWVcIjpcImp0XCIsXCJsaWJfdmVyc2lvblwiOlwiMS4zLjBcIixcInF1YWxpdHlcIjpcIjcwXCJ9IiApLCBhcnJheSggInR5cGUiID0+ICJmb3JtLWRhdGEiLCAibmFtZSIgPT4gInBob3RvIiwgImRhdGEiID0+ICRmaWxlVG9VcGxvYWQsICJmaWxlbmFtZSIgPT4gInBlbmRpbmdfbWVkaWFfIiAuIG51bWJlcl9mb3JtYXQocm91bmQobWljcm90aW1lKHRydWUpICogMTAwMCksIDAsICIiLCAiIikgLiAiLmpwZyIsICJoZWFkZXJzIiA9PiBhcnJheSggIkNvbnRlbnQtVHJhbnNmZXItRW5jb2Rpbmc6IGJpbmFyeSIsICJDb250ZW50LXR5cGU6IGFwcGxpY2F0aW9uL29jdGV0LXN0cmVhbSIgKSApICk7DQogICAgICAgICRkYXRhID0gJHRoaXMtPmJ1aWxkQm9keSgkYm9kaWVzLCAkYm91bmRhcnkpOw0KICAgICAgICAkaGVhZGVycyA9IGFycmF5KCAiWC1JRy1DYXBhYmlsaXRpZXM6ICIgLiBDb25zdGFudHM6OlhfSUdfQ2FwYWJpbGl0aWVzLCAiWC1JRy1Db25uZWN0aW9uLVR5cGU6IFdJRkkiLCAiQ29udGVudC10eXBlOiBtdWx0aXBhcnQvZm9ybS1kYXRhOyBib3VuZGFyeT0iIC4gJGJvdW5kYXJ5LCAiQ29udGVudC1MZW5ndGg6ICIgLiBzdHJsZW4oJGRhdGEpLCAiQWNjZXB0LUxhbmd1YWdlOiB0ci1UUiIsICJBY2NlcHQtRW5jb2Rpbmc6IGd6aXAsIGRlZmxhdGUiLCAiQ29ubmVjdGlvbjogY2xvc2UiLCAiWF9GT1JXQVJERURfRk9SOiAiIC4gJHRoaXMtPnNldHRpbmdzLT5nZXQoImlwIiksICJSRU1PVEVfQUREUjogIiAuICR0aGlzLT5zZXR0aW5ncy0+Z2V0KCJpcCIpICk7DQogICAgICAgICRjaCA9IGN1cmxfaW5pdCgpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfVVJMLCAkZW5kcG9pbnQpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfVVNFUkFHRU5ULCAkdGhpcy0+dXNlckFnZW50KTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1JFVFVSTlRSQU5TRkVSLCB0cnVlKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX0ZPTExPV0xPQ0FUSU9OLCB0cnVlKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX0hFQURFUiwgdHJ1ZSk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9WRVJCT1NFLCAkdGhpcy0+ZGVidWcpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfU1NMX1ZFUklGWVBFRVIsIGZhbHNlKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1NTTF9WRVJJRllIT1NULCBmYWxzZSk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9IVFRQSEVBREVSLCAkaGVhZGVycyk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9DT09LSUVGSUxFLCAkdGhpcy0+SUdEYXRhUGF0aCAuIChzdHJpbmcpICR0aGlzLT51c2VybmFtZSAuICIuZGF0Iik7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9DT09LSUVKQVIsICR0aGlzLT5JR0RhdGFQYXRoIC4gKHN0cmluZykgJHRoaXMtPnVzZXJuYW1lIC4gIi5kYXQiKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1BPU1QsIHRydWUpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfUE9TVEZJRUxEUywgJGRhdGEpOw0KICAgICAgICAkcmVzcCA9IGN1cmxfZXhlYygkY2gpOw0KICAgICAgICAkaGVhZGVyX2xlbiA9IGN1cmxfZ2V0aW5mbygkY2gsIENVUkxJTkZPX0hFQURFUl9TSVpFKTsNCiAgICAgICAgJGhlYWRlciA9IHN1YnN0cigkcmVzcCwgMCwgJGhlYWRlcl9sZW4pOw0KICAgICAgICAkdXBsb2FkID0ganNvbl9kZWNvZGUoc3Vic3RyKCRyZXNwLCAkaGVhZGVyX2xlbiksIHRydWUpOw0KICAgICAgICBjdXJsX2Nsb3NlKCRjaCk7DQogICAgICAgIGlmKCAkdXBsb2FkWyJzdGF0dXMiXSA9PSAiZmFpbCIgKSANCiAgICAgICAgew0KICAgICAgICAgICAgdGhyb3cgbmV3IEV4Y2VwdGlvbigkdXBsb2FkWyJtZXNzYWdlIl0pOw0KICAgICAgICB9DQoNCiAgICAgICAgaWYoICR0aGlzLT5kZWJ1ZyApIA0KICAgICAgICB7DQogICAgICAgICAgICBlY2hvICJSRVNQT05TRTogIiAuIHN1YnN0cigkcmVzcCwgJGhlYWRlcl9sZW4pIC4gIlxuXG4iOw0KICAgICAgICB9DQoNCiAgICAgICAgJGNvbmZpZ3VyZSA9ICR0aGlzLT5jb25maWd1cmUoJHVwbG9hZFsidXBsb2FkX2lkIl0sICRwaG90bywgJGNhcHRpb24pOw0KICAgICAgICAkdGhpcy0+ZXhwb3NlKCk7DQogICAgICAgIHJldHVybiAkY29uZmlndXJlOw0KICAgIH0NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiB1cGxvYWRWaWRlbygkdmlkZW8sICRjYXB0aW9uID0gTlVMTCkNCiAgICB7DQogICAgICAgICR2aWRlb0RhdGEgPSBmaWxlX2dldF9jb250ZW50cygkdmlkZW8pOw0KICAgICAgICAkZW5kcG9pbnQgPSBDb25zdGFudHM6OkFQSV9VUkwgLiAidXBsb2FkL3ZpZGVvLyI7DQogICAgICAgICRib3VuZGFyeSA9ICR0aGlzLT51dWlkOw0KICAgICAgICAkdXBsb2FkX2lkID0gcm91bmQobWljcm90aW1lKHRydWUpICogMTAwMCk7DQogICAgICAgICRib2RpZXMgPSBhcnJheSggYXJyYXkoICJ0eXBlIiA9PiAiZm9ybS1kYXRhIiwgIm5hbWUiID0+ICJ1cGxvYWRfaWQiLCAiZGF0YSIgPT4gJHVwbG9hZF9pZCApLCBhcnJheSggInR5cGUiID0+ICJmb3JtLWRhdGEiLCAibmFtZSIgPT4gIl9jc3JmdG9rZW4iLCAiZGF0YSIgPT4gJHRoaXMtPnRva2VuICksIGFycmF5KCAidHlwZSIgPT4gImZvcm0tZGF0YSIsICJuYW1lIiA9PiAibWVkaWFfdHlwZSIsICJkYXRhIiA9PiAiMiIgKSwgYXJyYXkoICJ0eXBlIiA9PiAiZm9ybS1kYXRhIiwgIm5hbWUiID0+ICJfdXVpZCIsICJkYXRhIiA9PiAkdGhpcy0+dXVpZCApICk7DQogICAgICAgICRkYXRhID0gJHRoaXMtPmJ1aWxkQm9keSgkYm9kaWVzLCAkYm91bmRhcnkpOw0KICAgICAgICAkaGVhZGVycyA9IGFycmF5KCAiQ29ubmVjdGlvbjoga2VlcC1hbGl2ZSIsICJBY2NlcHQ6ICovKiIsICJIb3N0OiBpLmluc3RhZ3JhbS5jb20iLCAiQ29udGVudC10eXBlOiBtdWx0aXBhcnQvZm9ybS1kYXRhOyBib3VuZGFyeT0iIC4gJGJvdW5kYXJ5LCAiQWNjZXB0LUxhbmd1YWdlOiB0ci1UUiIsICJYX0ZPUldBUkRFRF9GT1I6ICIgLiAkdGhpcy0+c2V0dGluZ3MtPmdldCgiaXAiKSwgIlJFTU9URV9BRERSOiAiIC4gJHRoaXMtPnNldHRpbmdzLT5nZXQoImlwIikgKTsNCiAgICAgICAgJGNoID0gY3VybF9pbml0KCk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9VUkwsICRlbmRwb2ludCk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9VU0VSQUdFTlQsICR0aGlzLT51c2VyQWdlbnQpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfUkVUVVJOVFJBTlNGRVIsIHRydWUpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfRk9MTE9XTE9DQVRJT04sIHRydWUpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfSEVBREVSLCB0cnVlKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1ZFUkJPU0UsIGZhbHNlKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX0hUVFBIRUFERVIsICRoZWFkZXJzKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX0NPT0tJRUZJTEUsICR0aGlzLT5JR0RhdGFQYXRoIC4gKHN0cmluZykgJHRoaXMtPnVzZXJuYW1lIC4gIi5kYXQiKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX0NPT0tJRUpBUiwgJHRoaXMtPklHRGF0YVBhdGggLiAoc3RyaW5nKSAkdGhpcy0+dXNlcm5hbWUgLiAiLmRhdCIpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfUE9TVCwgdHJ1ZSk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9QT1NURklFTERTLCAkZGF0YSk7DQogICAgICAgICRyZXNwID0gY3VybF9leGVjKCRjaCk7DQogICAgICAgICRoZWFkZXJfbGVuID0gY3VybF9nZXRpbmZvKCRjaCwgQ1VSTElORk9fSEVBREVSX1NJWkUpOw0KICAgICAgICAkYm9keSA9IGpzb25fZGVjb2RlKHN1YnN0cigkcmVzcCwgJGhlYWRlcl9sZW4pLCB0cnVlKTsNCiAgICAgICAgJHVwbG9hZFVybCA9ICRib2R5WyJ2aWRlb191cGxvYWRfdXJscyJdWzNdWyJ1cmwiXTsNCiAgICAgICAgJGpvYiA9ICRib2R5WyJ2aWRlb191cGxvYWRfdXJscyJdWzNdWyJqb2IiXTsNCiAgICAgICAgJHJlcXVlc3Rfc2l6ZSA9IGZsb29yKHN0cmxlbigkdmlkZW9EYXRhKSAvIDQpOw0KICAgICAgICAkbGFzdFJlcXVlc3RFeHRyYSA9IHN0cmxlbigkdmlkZW9EYXRhKSAtICRyZXF1ZXN0X3NpemUgKiA0Ow0KICAgICAgICBmb3IoICRhID0gMDsgJGEgPD0gMzsgJGErKyApIA0KICAgICAgICB7DQogICAgICAgICAgICAkc3RhcnQgPSAkYSAqICRyZXF1ZXN0X3NpemU7DQogICAgICAgICAgICAkZW5kID0gKCRhICsgMSkgKiAkcmVxdWVzdF9zaXplICsgKCgkYSA9PSAzID8gJGxhc3RSZXF1ZXN0RXh0cmEgOiAwKSk7DQogICAgICAgICAgICAkaGVhZGVycyA9IGFycmF5KCAiQ29ubmVjdGlvbjoga2VlcC1hbGl2ZSIsICJBY2NlcHQ6ICovKiIsICJIb3N0OiB1cGxvYWQuaW5zdGFncmFtLmNvbSIsICJDb29raWUyOiBcJFZlcnNpb249MSIsICJBY2NlcHQtRW5jb2Rpbmc6IGd6aXAsIGRlZmxhdGUiLCAiQ29udGVudC1UeXBlOiBhcHBsaWNhdGlvbi9vY3RldC1zdHJlYW0iLCAiU2Vzc2lvbi1JRDogIiAuICR1cGxvYWRfaWQsICJBY2NlcHQtTGFuZ3VhZ2U6IHRyLVRSIiwgIkNvbnRlbnQtRGlzcG9zaXRpb246IGF0dGFjaG1lbnQ7IGZpbGVuYW1lPVwidmlkZW8ubW92XCIiLCAiQ29udGVudC1MZW5ndGg6ICIgLiAoJGVuZCAtICRzdGFydCksICJDb250ZW50LVJhbmdlOiAiIC4gImJ5dGVzICIgLiAkc3RhcnQgLiAiLSIgLiAoJGVuZCAtIDEpIC4gIi8iIC4gc3RybGVuKCR2aWRlb0RhdGEpLCAiam9iOiAiIC4gJGpvYiApOw0KICAgICAgICAgICAgJGNoID0gY3VybF9pbml0KCk7DQogICAgICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfVVJMLCAkdXBsb2FkVXJsKTsNCiAgICAgICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9VU0VSQUdFTlQsICR0aGlzLT51c2VyQWdlbnQpOw0KICAgICAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX0NVU1RPTVJFUVVFU1QsICJQT1NUIik7DQogICAgICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfUkVUVVJOVFJBTlNGRVIsIHRydWUpOw0KICAgICAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX0ZPTExPV0xPQ0FUSU9OLCB0cnVlKTsNCiAgICAgICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9IRUFERVIsIHRydWUpOw0KICAgICAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1ZFUkJPU0UsIGZhbHNlKTsNCiAgICAgICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9IVFRQSEVBREVSLCAkaGVhZGVycyk7DQogICAgICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfQ09PS0lFRklMRSwgJHRoaXMtPklHRGF0YVBhdGggLiAoc3RyaW5nKSAkdGhpcy0+dXNlcm5hbWUgLiAiLmRhdCIpOw0KICAgICAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX0NPT0tJRUpBUiwgJHRoaXMtPklHRGF0YVBhdGggLiAoc3RyaW5nKSAkdGhpcy0+dXNlcm5hbWUgLiAiLmRhdCIpOw0KICAgICAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1BPU1QsIHRydWUpOw0KICAgICAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1BPU1RGSUVMRFMsIHN1YnN0cigkdmlkZW9EYXRhLCAkc3RhcnQsICRlbmQpKTsNCiAgICAgICAgICAgICRyZXN1bHQgPSBjdXJsX2V4ZWMoJGNoKTsNCiAgICAgICAgICAgICRoZWFkZXJfbGVuID0gY3VybF9nZXRpbmZvKCRjaCwgQ1VSTElORk9fSEVBREVSX1NJWkUpOw0KICAgICAgICAgICAgJGJvZHkgPSBzdWJzdHIoJHJlc3VsdCwgJGhlYWRlcl9sZW4pOw0KICAgICAgICAgICAgJGFycmF5W10gPSBhcnJheSggJGJvZHkgKTsNCiAgICAgICAgfQ0KICAgICAgICAkcmVzcCA9IGN1cmxfZXhlYygkY2gpOw0KICAgICAgICAkaGVhZGVyX2xlbiA9IGN1cmxfZ2V0aW5mbygkY2gsIENVUkxJTkZPX0hFQURFUl9TSVpFKTsNCiAgICAgICAgJGhlYWRlciA9IHN1YnN0cigkcmVzcCwgMCwgJGhlYWRlcl9sZW4pOw0KICAgICAgICAkdXBsb2FkID0ganNvbl9kZWNvZGUoc3Vic3RyKCRyZXNwLCAkaGVhZGVyX2xlbiksIHRydWUpOw0KICAgICAgICBjdXJsX2Nsb3NlKCRjaCk7DQogICAgICAgIGlmKCAkdXBsb2FkWyJzdGF0dXMiXSA9PSAiZmFpbCIgKSANCiAgICAgICAgew0KICAgICAgICAgICAgdGhyb3cgbmV3IEV4Y2VwdGlvbigkdXBsb2FkWyJtZXNzYWdlIl0pOw0KICAgICAgICB9DQoNCiAgICAgICAgaWYoICR0aGlzLT5kZWJ1ZyApIA0KICAgICAgICB7DQogICAgICAgICAgICBlY2hvICJSRVNQT05TRTogIiAuIHN1YnN0cigkcmVzcCwgJGhlYWRlcl9sZW4pIC4gIlxuXG4iOw0KICAgICAgICB9DQoNCiAgICAgICAgJGNvbmZpZ3VyZSA9ICR0aGlzLT5jb25maWd1cmVWaWRlbygkdXBsb2FkX2lkLCAkdmlkZW8sICRjYXB0aW9uKTsNCiAgICAgICAgJHRoaXMtPmV4cG9zZSgpOw0KICAgICAgICByZXR1cm4gJGNvbmZpZ3VyZTsNCiAgICB9DQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gZGlyZWN0X21lc3NhZ2UoJHJlY2lwaWVudHMsICR0ZXh0KQ0KICAgIHsNCiAgICAgICAgaWYoICFpc19hcnJheSgkcmVjaXBpZW50cykgKSANCiAgICAgICAgew0KICAgICAgICAgICAgJHJlY2lwaWVudHMgPSBhcnJheSggJHJlY2lwaWVudHMgKTsNCiAgICAgICAgfQ0KDQogICAgICAgICRzdHJpbmcgPSBhcnJheSgpOw0KICAgICAgICBmb3JlYWNoKCAkcmVjaXBpZW50cyBhcyAkcmVjaXBpZW50ICkgDQogICAgICAgIHsNCiAgICAgICAgICAgICRzdHJpbmdbXSA9ICJcIiIgLiAkcmVjaXBpZW50IC4gIlwiIjsNCiAgICAgICAgfQ0KICAgICAgICAkcmVjaXBpZW50X3VzZXJzID0gaW1wbG9kZSgiLCIsICRzdHJpbmcpOw0KICAgICAgICAkZW5kcG9pbnQgPSBDb25zdGFudHM6OkFQSV9VUkwgLiAiZGlyZWN0X3YyL3RocmVhZHMvYnJvYWRjYXN0L3RleHQvIjsNCiAgICAgICAgJGJvdW5kYXJ5ID0gJHRoaXMtPnV1aWQ7DQogICAgICAgICRib2RpZXMgPSBhcnJheSggYXJyYXkoICJ0eXBlIiA9PiAiZm9ybS1kYXRhIiwgIm5hbWUiID0+ICJyZWNpcGllbnRfdXNlcnMiLCAiZGF0YSIgPT4gIltbIiAuICRyZWNpcGllbnRfdXNlcnMgLiAiXV0iICksIGFycmF5KCAidHlwZSIgPT4gImZvcm0tZGF0YSIsICJuYW1lIiA9PiAiY2xpZW50X2NvbnRleHQiLCAiZGF0YSIgPT4gJHRoaXMtPnV1aWQgKSwgYXJyYXkoICJ0eXBlIiA9PiAiZm9ybS1kYXRhIiwgIm5hbWUiID0+ICJ0aHJlYWRfaWRzIiwgImRhdGEiID0+ICJbXCIwXCJdIiApLCBhcnJheSggInR5cGUiID0+ICJmb3JtLWRhdGEiLCAibmFtZSIgPT4gInRleHQiLCAiZGF0YSIgPT4gKGlzX251bGwoJHRleHQpID8gIiIgOiAkdGV4dCkgKSApOw0KICAgICAgICAkZGF0YSA9ICR0aGlzLT5idWlsZEJvZHkoJGJvZGllcywgJGJvdW5kYXJ5KTsNCiAgICAgICAgJGhlYWRlcnMgPSBhcnJheSggIlByb3h5LUNvbm5lY3Rpb246IGtlZXAtYWxpdmUiLCAiQ29ubmVjdGlvbjoga2VlcC1hbGl2ZSIsICJBY2NlcHQ6ICovKiIsICJDb250ZW50LXR5cGU6IG11bHRpcGFydC9mb3JtLWRhdGE7IGJvdW5kYXJ5PSIgLiAkYm91bmRhcnksICJBY2NlcHQtTGFuZ3VhZ2U6IHRyLVRSIiApOw0KICAgICAgICAkY2ggPSBjdXJsX2luaXQoKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1VSTCwgJGVuZHBvaW50KTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1VTRVJBR0VOVCwgJHRoaXMtPnVzZXJBZ2VudCk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9SRVRVUk5UUkFOU0ZFUiwgdHJ1ZSk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9GT0xMT1dMT0NBVElPTiwgdHJ1ZSk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9IRUFERVIsIHRydWUpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfVkVSQk9TRSwgJHRoaXMtPmRlYnVnKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1NTTF9WRVJJRllQRUVSLCBmYWxzZSk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9TU0xfVkVSSUZZSE9TVCwgZmFsc2UpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfSFRUUEhFQURFUiwgJGhlYWRlcnMpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfQ09PS0lFRklMRSwgJHRoaXMtPklHRGF0YVBhdGggLiAoc3RyaW5nKSAkdGhpcy0+dXNlcm5hbWUgLiAiLmRhdCIpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfQ09PS0lFSkFSLCAkdGhpcy0+SUdEYXRhUGF0aCAuIChzdHJpbmcpICR0aGlzLT51c2VybmFtZSAuICIuZGF0Iik7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9QT1NULCB0cnVlKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1BPU1RGSUVMRFMsICRkYXRhKTsNCiAgICAgICAgJHJlc3AgPSBjdXJsX2V4ZWMoJGNoKTsNCiAgICAgICAgJGhlYWRlcl9sZW4gPSBjdXJsX2dldGluZm8oJGNoLCBDVVJMSU5GT19IRUFERVJfU0laRSk7DQogICAgICAgICRoZWFkZXIgPSBzdWJzdHIoJHJlc3AsIDAsICRoZWFkZXJfbGVuKTsNCiAgICAgICAgJHVwbG9hZCA9IGpzb25fZGVjb2RlKHN1YnN0cigkcmVzcCwgJGhlYWRlcl9sZW4pLCB0cnVlKTsNCiAgICAgICAgY3VybF9jbG9zZSgkY2gpOw0KICAgIH0NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBkaXJlY3Rfc2hhcmUoJG1lZGlhX2lkLCAkcmVjaXBpZW50cywgJHRleHQgPSBOVUxMKQ0KICAgIHsNCiAgICAgICAgaWYoICFpc19hcnJheSgkcmVjaXBpZW50cykgKSANCiAgICAgICAgew0KICAgICAgICAgICAgJHJlY2lwaWVudHMgPSBhcnJheSggJHJlY2lwaWVudHMgKTsNCiAgICAgICAgfQ0KDQogICAgICAgICRzdHJpbmcgPSBhcnJheSgpOw0KICAgICAgICBmb3JlYWNoKCAkcmVjaXBpZW50cyBhcyAkcmVjaXBpZW50ICkgDQogICAgICAgIHsNCiAgICAgICAgICAgICRzdHJpbmdbXSA9ICJcIiIgLiAkcmVjaXBpZW50IC4gIlwiIjsNCiAgICAgICAgfQ0KICAgICAgICAkcmVjaXBpZW50X3VzZXJzID0gaW1wbG9kZSgiLCIsICRzdHJpbmcpOw0KICAgICAgICAkZW5kcG9pbnQgPSBDb25zdGFudHM6OkFQSV9VUkwgLiAiZGlyZWN0X3YyL3RocmVhZHMvYnJvYWRjYXN0L21lZGlhX3NoYXJlLz9tZWRpYV90eXBlPXBob3RvIjsNCiAgICAgICAgJGJvdW5kYXJ5ID0gJHRoaXMtPnV1aWQ7DQogICAgICAgICRib2RpZXMgPSBhcnJheSggYXJyYXkoICJ0eXBlIiA9PiAiZm9ybS1kYXRhIiwgIm5hbWUiID0+ICJtZWRpYV9pZCIsICJkYXRhIiA9PiAkbWVkaWFfaWQgKSwgYXJyYXkoICJ0eXBlIiA9PiAiZm9ybS1kYXRhIiwgIm5hbWUiID0+ICJyZWNpcGllbnRfdXNlcnMiLCAiZGF0YSIgPT4gIltbIiAuICRyZWNpcGllbnRfdXNlcnMgLiAiXV0iICksIGFycmF5KCAidHlwZSIgPT4gImZvcm0tZGF0YSIsICJuYW1lIiA9PiAiY2xpZW50X2NvbnRleHQiLCAiZGF0YSIgPT4gJHRoaXMtPnV1aWQgKSwgYXJyYXkoICJ0eXBlIiA9PiAiZm9ybS1kYXRhIiwgIm5hbWUiID0+ICJ0aHJlYWRfaWRzIiwgImRhdGEiID0+ICJbXCIwXCJdIiApLCBhcnJheSggInR5cGUiID0+ICJmb3JtLWRhdGEiLCAibmFtZSIgPT4gInRleHQiLCAiZGF0YSIgPT4gKGlzX251bGwoJHRleHQpID8gIiIgOiAkdGV4dCkgKSApOw0KICAgICAgICAkZGF0YSA9ICR0aGlzLT5idWlsZEJvZHkoJGJvZGllcywgJGJvdW5kYXJ5KTsNCiAgICAgICAgJGhlYWRlcnMgPSBhcnJheSggIlByb3h5LUNvbm5lY3Rpb246IGtlZXAtYWxpdmUiLCAiQ29ubmVjdGlvbjoga2VlcC1hbGl2ZSIsICJBY2NlcHQ6ICovKiIsICJDb250ZW50LXR5cGU6IG11bHRpcGFydC9mb3JtLWRhdGE7IGJvdW5kYXJ5PSIgLiAkYm91bmRhcnksICJBY2NlcHQtTGFuZ3VhZ2U6IHRyLVRSIiApOw0KICAgICAgICAkY2ggPSBjdXJsX2luaXQoKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1VSTCwgJGVuZHBvaW50KTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1VTRVJBR0VOVCwgJHRoaXMtPnVzZXJBZ2VudCk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9SRVRVUk5UUkFOU0ZFUiwgdHJ1ZSk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9GT0xMT1dMT0NBVElPTiwgdHJ1ZSk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9IRUFERVIsIHRydWUpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfVkVSQk9TRSwgJHRoaXMtPmRlYnVnKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1NTTF9WRVJJRllQRUVSLCBmYWxzZSk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9TU0xfVkVSSUZZSE9TVCwgZmFsc2UpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfSFRUUEhFQURFUiwgJGhlYWRlcnMpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfQ09PS0lFRklMRSwgJHRoaXMtPklHRGF0YVBhdGggLiAoc3RyaW5nKSAkdGhpcy0+dXNlcm5hbWUgLiAiLmRhdCIpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfQ09PS0lFSkFSLCAkdGhpcy0+SUdEYXRhUGF0aCAuIChzdHJpbmcpICR0aGlzLT51c2VybmFtZSAuICIuZGF0Iik7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9QT1NULCB0cnVlKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1BPU1RGSUVMRFMsICRkYXRhKTsNCiAgICAgICAgJHJlc3AgPSBjdXJsX2V4ZWMoJGNoKTsNCiAgICAgICAgJGhlYWRlcl9sZW4gPSBjdXJsX2dldGluZm8oJGNoLCBDVVJMSU5GT19IRUFERVJfU0laRSk7DQogICAgICAgICRoZWFkZXIgPSBzdWJzdHIoJHJlc3AsIDAsICRoZWFkZXJfbGVuKTsNCiAgICAgICAgJHVwbG9hZCA9IGpzb25fZGVjb2RlKHN1YnN0cigkcmVzcCwgJGhlYWRlcl9sZW4pLCB0cnVlKTsNCiAgICAgICAgY3VybF9jbG9zZSgkY2gpOw0KICAgIH0NCg0KICAgIHByb3RlY3RlZCBmdW5jdGlvbiBjb25maWd1cmVWaWRlbygkdXBsb2FkX2lkLCAkdmlkZW8sICRjYXB0aW9uID0gIiIpDQogICAgew0KICAgICAgICAkdGhpcy0+dXBsb2FkUGhvdG8oJHZpZGVvLCAkY2FwdGlvbiwgJHVwbG9hZF9pZCk7DQogICAgICAgIGxpc3QoJHNpemUpID0gZ2V0aW1hZ2VzaXplKCR2aWRlbyk7DQogICAgICAgICRwb3N0ID0ganNvbl9lbmNvZGUoYXJyYXkoICJ1cGxvYWRfaWQiID0+ICR1cGxvYWRfaWQsICJzb3VyY2VfdHlwZSIgPT4gIjMiLCAicG9zdGVyX2ZyYW1lX2luZGV4IiA9PiAwLCAibGVuZ3RoIiA9PiAwLCAiYXVkaW9fbXV0ZWQiID0+IGZhbHNlLCAiZmlsdGVyX3R5cGUiID0+ICIwIiwgInZpZGVvX3Jlc3VsdCIgPT4gImRlcHJlY2F0ZWQiLCAiY2xpcHMiID0+IGFycmF5KCAibGVuZ3RoIiA9PiBVdGlsczo6Z2V0U2Vjb25kcygkdmlkZW8pLCAic291cmNlX3R5cGUiID0+ICIzIiwgImNhbWVyYV9wb3NpdGlvbiIgPT4gImJhY2siICksICJleHRyYSIgPT4gYXJyYXkoICJzb3VyY2Vfd2lkdGgiID0+IDk2MCwgInNvdXJjZV9oZWlnaHQiID0+IDEyODAgKSwgImRldmljZSIgPT4gYXJyYXkoICJtYW51ZmFjdHVyZXIiID0+ICJYaWFvbWkiLCAibW9kZWwiID0+ICJITSAxU1ciLCAiYW5kcm9pZF92ZXJzaW9uIiA9PiAxOCwgImFuZHJvaWRfcmVsZWFzZSIgPT4gIjQuMyIgKSwgIl9jc3JmdG9rZW4iID0+ICR0aGlzLT50b2tlbiwgIl91dWlkIiA9PiAkdGhpcy0+dXVpZCwgIl91aWQiID0+ICR0aGlzLT51c2VybmFtZV9pZCwgImNhcHRpb24iID0+ICRjYXB0aW9uICkpOw0KICAgICAgICAkcG9zdCA9IHN0cl9yZXBsYWNlKCJcImxlbmd0aFwiOjAiLCAiXCJsZW5ndGhcIjowLjAwIiwgJHBvc3QpOw0KICAgICAgICByZXR1cm4gJHRoaXMtPnJlcXVlc3QoIm1lZGlhL2NvbmZpZ3VyZS8/dmlkZW89MSIsIFNpZ25hdHVyZVV0aWxzOjpnZW5lcmF0ZVNpZ25hdHVyZSgkcG9zdCkpWzFdOw0KICAgIH0NCg0KICAgIHByb3RlY3RlZCBmdW5jdGlvbiBjb25maWd1cmUoJHVwbG9hZF9pZCwgJHBob3RvLCAkY2FwdGlvbiA9ICIiKQ0KICAgIHsNCiAgICAgICAgbGlzdCgkc2l6ZSkgPSBnZXRpbWFnZXNpemUoJHBob3RvKTsNCiAgICAgICAgJHBvc3QgPSBqc29uX2VuY29kZShhcnJheSggInVwbG9hZF9pZCIgPT4gJHVwbG9hZF9pZCwgImNhbWVyYV9tb2RlbCIgPT4gIkhNMVMiLCAic291cmNlX3R5cGUiID0+IDMsICJkYXRlX3RpbWVfb3JpZ2luYWwiID0+IGRhdGUoIlk6bTpkIEg6aTpzIiksICJjYW1lcmFfbWFrZSIgPT4gIlhJQU9NSSIsICJlZGl0cyIgPT4gYXJyYXkoICJjcm9wX29yaWdpbmFsX3NpemUiID0+IGFycmF5KCAkc2l6ZSwgJHNpemUgKSwgImNyb3Bfem9vbSIgPT4gMS4zMzMzMzM0LCAiY3JvcF9jZW50ZXIiID0+IGFycmF5KCAwLCAwICkgKSwgImV4dHJhIiA9PiBhcnJheSggInNvdXJjZV93aWR0aCIgPT4gJHNpemUsICJzb3VyY2VfaGVpZ2h0IiA9PiAkc2l6ZSApLCAiZGV2aWNlIiA9PiBhcnJheSggIm1hbnVmYWN0dXJlciIgPT4gIlhpYW9taSIsICJtb2RlbCIgPT4gIkhNIDFTVyIsICJhbmRyb2lkX3ZlcnNpb24iID0+IDE4LCAiYW5kcm9pZF9yZWxlYXNlIiA9PiAiNC4zIiApLCAiX2NzcmZ0b2tlbiIgPT4gJHRoaXMtPnRva2VuLCAiX3V1aWQiID0+ICR0aGlzLT51dWlkLCAiX3VpZCIgPT4gJHRoaXMtPnVzZXJuYW1lX2lkLCAiY2FwdGlvbiIgPT4gJGNhcHRpb24gKSk7DQogICAgICAgICRwb3N0ID0gc3RyX3JlcGxhY2UoIlwiY3JvcF9jZW50ZXJcIjpbMCwwXSIsICJcImNyb3BfY2VudGVyXCI6WzAuMCwtMC4wXSIsICRwb3N0KTsNCiAgICAgICAgcmV0dXJuICR0aGlzLT5yZXF1ZXN0KCJtZWRpYS9jb25maWd1cmUvIiwgU2lnbmF0dXJlVXRpbHM6OmdlbmVyYXRlU2lnbmF0dXJlKCRwb3N0KSlbMV07DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIEVkaXQgbWVkaWEuDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJG1lZGlhSWQNCiAgICAgICAgICogICBNZWRpYSBpZA0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICRjYXB0aW9uVGV4dA0KICAgICAgICAgKiAgIENhcHRpb24gdGV4dA0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgZWRpdCBtZWRpYSBkYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGVkaXRNZWRpYSgkbWVkaWFJZCwgJGNhcHRpb25UZXh0ID0gIiIpDQogICAgew0KICAgICAgICAkZGF0YSA9IGpzb25fZW5jb2RlKGFycmF5KCAiX3V1aWQiID0+ICR0aGlzLT51dWlkLCAiX3VpZCIgPT4gJHRoaXMtPnVzZXJuYW1lX2lkLCAiX2NzcmZ0b2tlbiIgPT4gJHRoaXMtPnRva2VuLCAiY2FwdGlvbl90ZXh0IiA9PiAkY2FwdGlvblRleHQgKSk7DQogICAgICAgIHJldHVybiAkdGhpcy0+cmVxdWVzdCgibWVkaWEvIiAuICRtZWRpYUlkIC4gIi9lZGl0X21lZGlhLyIsIFNpZ25hdHVyZVV0aWxzOjpnZW5lcmF0ZVNpZ25hdHVyZSgkZGF0YSkpWzFdOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBSZW1vdmUgeW91cnNlbGYgZnJvbSBhIHRhZ2dlZCBtZWRpYS4NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkbWVkaWFJZA0KICAgICAgICAgKiAgIE1lZGlhIGlkDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBlZGl0IG1lZGlhIGRhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gcmVtb3ZlU2VsZnRhZygkbWVkaWFJZCkNCiAgICB7DQogICAgICAgICRkYXRhID0ganNvbl9lbmNvZGUoYXJyYXkoICJfdXVpZCIgPT4gJHRoaXMtPnV1aWQsICJfdWlkIiA9PiAkdGhpcy0+dXNlcm5hbWVfaWQsICJfY3NyZnRva2VuIiA9PiAkdGhpcy0+dG9rZW4gKSk7DQogICAgICAgIHJldHVybiAkdGhpcy0+cmVxdWVzdCgidXNlcnRhZ3MvIiAuICRtZWRpYUlkIC4gIi9yZW1vdmUvIiwgU2lnbmF0dXJlVXRpbHM6OmdlbmVyYXRlU2lnbmF0dXJlKCRkYXRhKSlbMV07DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIE1lZGlhIGluZm8NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkbWVkaWFJZA0KICAgICAgICAgKiAgIE1lZGlhIGlkDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBkZWxldGUgcmVxdWVzdCBkYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIG1lZGlhSW5mbygkbWVkaWFJZCkNCiAgICB7DQogICAgICAgICRkYXRhID0ganNvbl9lbmNvZGUoYXJyYXkoICJfdXVpZCIgPT4gJHRoaXMtPnV1aWQsICJfdWlkIiA9PiAkdGhpcy0+dXNlcm5hbWVfaWQsICJfY3NyZnRva2VuIiA9PiAkdGhpcy0+dG9rZW4sICJtZWRpYV9pZCIgPT4gJG1lZGlhSWQgKSk7DQogICAgICAgIHJldHVybiAkdGhpcy0+cmVxdWVzdCgibWVkaWEvIiAuICRtZWRpYUlkIC4gIi9pbmZvLyIsIFNpZ25hdHVyZVV0aWxzOjpnZW5lcmF0ZVNpZ25hdHVyZSgkZGF0YSkpWzFdOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBEZWxldGUgcGhvdG8gb3IgdmlkZW8uDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJG1lZGlhSWQNCiAgICAgICAgICogICBNZWRpYSBpZA0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgZGVsZXRlIHJlcXVlc3QgZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBkZWxldGVNZWRpYSgkbWVkaWFJZCkNCiAgICB7DQogICAgICAgICRkYXRhID0ganNvbl9lbmNvZGUoYXJyYXkoICJfdXVpZCIgPT4gJHRoaXMtPnV1aWQsICJfdWlkIiA9PiAkdGhpcy0+dXNlcm5hbWVfaWQsICJfY3NyZnRva2VuIiA9PiAkdGhpcy0+dG9rZW4sICJtZWRpYV9pZCIgPT4gJG1lZGlhSWQgKSk7DQogICAgICAgIHJldHVybiAkdGhpcy0+cmVxdWVzdCgibWVkaWEvIiAuICRtZWRpYUlkIC4gIi9kZWxldGUvIiwgU2lnbmF0dXJlVXRpbHM6OmdlbmVyYXRlU2lnbmF0dXJlKCRkYXRhKSlbMV07DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIENvbW1lbnQgbWVkaWEuDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJG1lZGlhSWQNCiAgICAgICAgICogICBNZWRpYSBpZA0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICRjb21tZW50VGV4dA0KICAgICAgICAgKiAgIENvbW1lbnQgVGV4dA0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgY29tbWVudCBtZWRpYSBkYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGNvbW1lbnQoJG1lZGlhSWQsICRjb21tZW50VGV4dCkNCiAgICB7DQogICAgICAgICRkYXRhID0ganNvbl9lbmNvZGUoYXJyYXkoICJfdXVpZCIgPT4gJHRoaXMtPnV1aWQsICJfdWlkIiA9PiAkdGhpcy0+dXNlcm5hbWVfaWQsICJfY3NyZnRva2VuIiA9PiAkdGhpcy0+dG9rZW4sICJjb21tZW50X3RleHQiID0+ICRjb21tZW50VGV4dCApKTsNCiAgICAgICAgcmV0dXJuICR0aGlzLT5yZXF1ZXN0KCJtZWRpYS8iIC4gJG1lZGlhSWQgLiAiL2NvbW1lbnQvIiwgU2lnbmF0dXJlVXRpbHM6OmdlbmVyYXRlU2lnbmF0dXJlKCRkYXRhKSlbMV07DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIERlbGV0ZSBDb21tZW50Lg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICRtZWRpYUlkDQogICAgICAgICAqICAgTWVkaWEgSUQNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkY29tbWVudElkDQogICAgICAgICAqICAgQ29tbWVudCBJRA0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgRGVsZXRlIGNvbW1lbnQgZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBkZWxldGVDb21tZW50KCRtZWRpYUlkLCAkY2FwdGlvblRleHQsICRjb21tZW50SWQpDQogICAgew0KICAgICAgICAkZGF0YSA9IGpzb25fZW5jb2RlKGFycmF5KCAiX3V1aWQiID0+ICR0aGlzLT51dWlkLCAiX3VpZCIgPT4gJHRoaXMtPnVzZXJuYW1lX2lkLCAiX2NzcmZ0b2tlbiIgPT4gJHRoaXMtPnRva2VuLCAiY2FwdGlvbl90ZXh0IiA9PiAkY2FwdGlvblRleHQgKSk7DQogICAgICAgIHJldHVybiAkdGhpcy0+cmVxdWVzdCgibWVkaWEvIiAuICRtZWRpYUlkIC4gIi9jb21tZW50LyIgLiAkY29tbWVudElkIC4gIi9kZWxldGUvIiwgU2lnbmF0dXJlVXRpbHM6OmdlbmVyYXRlU2lnbmF0dXJlKCRkYXRhKSlbMV07DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIFNldHMgYWNjb3VudCB0byBwdWJsaWMuDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJHBob3RvDQogICAgICAgICAqICAgUGF0aCB0byBwaG90bw0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBjaGFuZ2VQcm9maWxlUGljdHVyZSgkcGhvdG8pDQogICAgew0KICAgICAgICBpZiggaXNfbnVsbCgkcGhvdG8pICkgDQogICAgICAgIHsNCiAgICAgICAgICAgIGVjaG8gIlBob3RvIG5vdCB2YWxpZFxuXG4iOw0KICAgICAgICB9DQogICAgICAgIGVsc2UNCiAgICAgICAgew0KICAgICAgICAgICAgJHVEYXRhID0ganNvbl9lbmNvZGUoYXJyYXkoICJfY3NyZnRva2VuIiA9PiAkdGhpcy0+dG9rZW4sICJfdXVpZCIgPT4gJHRoaXMtPnV1aWQsICJfdWlkIiA9PiAkdGhpcy0+dXNlcm5hbWVfaWQgKSk7DQogICAgICAgICAgICAkZW5kcG9pbnQgPSBDb25zdGFudHM6OkFQSV9VUkwgLiAiYWNjb3VudHMvY2hhbmdlX3Byb2ZpbGVfcGljdHVyZS8iOw0KICAgICAgICAgICAgJGJvdW5kYXJ5ID0gJHRoaXMtPnV1aWQ7DQogICAgICAgICAgICAkYm9kaWVzID0gYXJyYXkoIGFycmF5KCAidHlwZSIgPT4gImZvcm0tZGF0YSIsICJuYW1lIiA9PiAiaWdfc2lnX2tleV92ZXJzaW9uIiwgImRhdGEiID0+IENvbnN0YW50czo6U0lHX0tFWV9WRVJTSU9OICksIGFycmF5KCAidHlwZSIgPT4gImZvcm0tZGF0YSIsICJuYW1lIiA9PiAic2lnbmVkX2JvZHkiLCAiZGF0YSIgPT4gaGFzaF9obWFjKCJzaGEyNTYiLCAkdURhdGEsIENvbnN0YW50czo6SUdfU0lHX0tFWSkgLiAkdURhdGEgKSwgYXJyYXkoICJ0eXBlIiA9PiAiZm9ybS1kYXRhIiwgIm5hbWUiID0+ICJwcm9maWxlX3BpYyIsICJkYXRhIiA9PiBmaWxlX2dldF9jb250ZW50cygkcGhvdG8pLCAiZmlsZW5hbWUiID0+ICJwcm9maWxlX3BpYyIsICJoZWFkZXJzIiA9PiBhcnJheSggIkNvbnRlbnQtdHlwZTogYXBwbGljYXRpb24vb2N0ZXQtc3RyZWFtIiwgIkNvbnRlbnQtVHJhbnNmZXItRW5jb2Rpbmc6IGJpbmFyeSIgKSApICk7DQogICAgICAgICAgICAkZGF0YSA9ICR0aGlzLT5idWlsZEJvZHkoJGJvZGllcywgJGJvdW5kYXJ5KTsNCiAgICAgICAgICAgICRoZWFkZXJzID0gYXJyYXkoICJQcm94eS1Db25uZWN0aW9uOiBrZWVwLWFsaXZlIiwgIkNvbm5lY3Rpb246IGtlZXAtYWxpdmUiLCAiQWNjZXB0OiAqLyoiLCAiQ29udGVudC10eXBlOiBtdWx0aXBhcnQvZm9ybS1kYXRhOyBib3VuZGFyeT0iIC4gJGJvdW5kYXJ5LCAiQWNjZXB0LUxhbmd1YWdlOiB0ci1UUiIsICJBY2NlcHQtRW5jb2Rpbmc6IGd6aXAsIGRlZmxhdGUiICk7DQogICAgICAgICAgICAkY2ggPSBjdXJsX2luaXQoKTsNCiAgICAgICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9VUkwsICRlbmRwb2ludCk7DQogICAgICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfVVNFUkFHRU5ULCAkdGhpcy0+dXNlckFnZW50KTsNCiAgICAgICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9SRVRVUk5UUkFOU0ZFUiwgdHJ1ZSk7DQogICAgICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfRk9MTE9XTE9DQVRJT04sIHRydWUpOw0KICAgICAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX0hFQURFUiwgdHJ1ZSk7DQogICAgICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfVkVSQk9TRSwgJHRoaXMtPmRlYnVnKTsNCiAgICAgICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9TU0xfVkVSSUZZUEVFUiwgZmFsc2UpOw0KICAgICAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1NTTF9WRVJJRllIT1NULCBmYWxzZSk7DQogICAgICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfSFRUUEhFQURFUiwgJGhlYWRlcnMpOw0KICAgICAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX0NPT0tJRUZJTEUsICR0aGlzLT5JR0RhdGFQYXRoIC4gKHN0cmluZykgJHRoaXMtPnVzZXJuYW1lIC4gIi5kYXQiKTsNCiAgICAgICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9DT09LSUVKQVIsICR0aGlzLT5JR0RhdGFQYXRoIC4gKHN0cmluZykgJHRoaXMtPnVzZXJuYW1lIC4gIi5kYXQiKTsNCiAgICAgICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9QT1NULCB0cnVlKTsNCiAgICAgICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9QT1NURklFTERTLCAkZGF0YSk7DQogICAgICAgICAgICAkcmVzcCA9IGN1cmxfZXhlYygkY2gpOw0KICAgICAgICAgICAgJGhlYWRlcl9sZW4gPSBjdXJsX2dldGluZm8oJGNoLCBDVVJMSU5GT19IRUFERVJfU0laRSk7DQogICAgICAgICAgICAkaGVhZGVyID0gc3Vic3RyKCRyZXNwLCAwLCAkaGVhZGVyX2xlbik7DQogICAgICAgICAgICAkdXBsb2FkID0ganNvbl9kZWNvZGUoc3Vic3RyKCRyZXNwLCAkaGVhZGVyX2xlbiksIHRydWUpOw0KICAgICAgICAgICAgY3VybF9jbG9zZSgkY2gpOw0KICAgICAgICB9DQoNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogUmVtb3ZlIHByb2ZpbGUgcGljdHVyZS4NCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIHN0YXR1cyByZXF1ZXN0IGRhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gcmVtb3ZlUHJvZmlsZVBpY3R1cmUoKQ0KICAgIHsNCiAgICAgICAgJGRhdGEgPSBqc29uX2VuY29kZShhcnJheSggIl91dWlkIiA9PiAkdGhpcy0+dXVpZCwgIl91aWQiID0+ICR0aGlzLT51c2VybmFtZV9pZCwgIl9jc3JmdG9rZW4iID0+ICR0aGlzLT50b2tlbiApKTsNCiAgICAgICAgcmV0dXJuICR0aGlzLT5yZXF1ZXN0KCJhY2NvdW50cy9yZW1vdmVfcHJvZmlsZV9waWN0dXJlLyIsIFNpZ25hdHVyZVV0aWxzOjpnZW5lcmF0ZVNpZ25hdHVyZSgkZGF0YSkpWzFdOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBTZXRzIGFjY291bnQgdG8gcHJpdmF0ZS4NCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIHN0YXR1cyByZXF1ZXN0IGRhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gc2V0UHJpdmF0ZUFjY291bnQoKQ0KICAgIHsNCiAgICAgICAgJGRhdGEgPSBqc29uX2VuY29kZShhcnJheSggIl91dWlkIiA9PiAkdGhpcy0+dXVpZCwgIl91aWQiID0+ICR0aGlzLT51c2VybmFtZV9pZCwgIl9jc3JmdG9rZW4iID0+ICR0aGlzLT50b2tlbiApKTsNCiAgICAgICAgcmV0dXJuICR0aGlzLT5yZXF1ZXN0KCJhY2NvdW50cy9zZXRfcHJpdmF0ZS8iLCBTaWduYXR1cmVVdGlsczo6Z2VuZXJhdGVTaWduYXR1cmUoJGRhdGEpKVsxXTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogU2V0cyBhY2NvdW50IHRvIHB1YmxpYy4NCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIHN0YXR1cyByZXF1ZXN0IGRhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gc2V0UHVibGljQWNjb3VudCgpDQogICAgew0KICAgICAgICAkZGF0YSA9IGpzb25fZW5jb2RlKGFycmF5KCAiX3V1aWQiID0+ICR0aGlzLT51dWlkLCAiX3VpZCIgPT4gJHRoaXMtPnVzZXJuYW1lX2lkLCAiX2NzcmZ0b2tlbiIgPT4gJHRoaXMtPnRva2VuICkpOw0KICAgICAgICByZXR1cm4gJHRoaXMtPnJlcXVlc3QoImFjY291bnRzL3NldF9wdWJsaWMvIiwgU2lnbmF0dXJlVXRpbHM6OmdlbmVyYXRlU2lnbmF0dXJlKCRkYXRhKSlbMV07DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIEdldCBwZXJzb25hbCBwcm9maWxlIGRhdGEuDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBwcm9maWxlIGRhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gZ2V0UHJvZmlsZURhdGEoKQ0KICAgIHsNCiAgICAgICAgJGRhdGEgPSBqc29uX2VuY29kZShhcnJheSggIl91dWlkIiA9PiAkdGhpcy0+dXVpZCwgIl91aWQiID0+ICR0aGlzLT51c2VybmFtZV9pZCwgIl9jc3JmdG9rZW4iID0+ICR0aGlzLT50b2tlbiApKTsNCiAgICAgICAgcmV0dXJuICR0aGlzLT5yZXF1ZXN0KCJhY2NvdW50cy9jdXJyZW50X3VzZXIvP2VkaXQ9dHJ1ZSIsIFNpZ25hdHVyZVV0aWxzOjpnZW5lcmF0ZVNpZ25hdHVyZSgkZGF0YSkpWzFdOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBFZGl0IHByb2ZpbGUuDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJHVybA0KICAgICAgICAgKiAgIFVybCAtIHdlYnNpdGUuICIiIGZvciBub3RoaW5nDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJHBob25lDQogICAgICAgICAqICAgUGhvbmUgbnVtYmVyLiAiIiBmb3Igbm90aGluZw0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICRmaXJzdF9uYW1lDQogICAgICAgICAqICAgTmFtZS4gIiIgZm9yIG5vdGhpbmcNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkZW1haWwNCiAgICAgICAgICogICBFbWFpbC4gUmVxdWlyZWQuDQogICAgICAgICAqIEBwYXJhbSBpbnQgICAgJGdlbmRlcg0KICAgICAgICAgKiAgIEdlbmRlci4gbWFsZSA9IDEgLCBmZW1hbGUgPSAwDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBlZGl0IHByb2ZpbGUgZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBlZGl0UHJvZmlsZSgkdXJsLCAkcGhvbmUsICRmaXJzdF9uYW1lLCAkYmlvZ3JhcGh5LCAkZW1haWwsICRnZW5kZXIpDQogICAgew0KICAgICAgICAkZGF0YSA9IGpzb25fZW5jb2RlKGFycmF5KCAiX3V1aWQiID0+ICR0aGlzLT51dWlkLCAiX3VpZCIgPT4gJHRoaXMtPnVzZXJuYW1lX2lkLCAiX2NzcmZ0b2tlbiIgPT4gJHRoaXMtPnRva2VuLCAiZXh0ZXJuYWxfdXJsIiA9PiAkdXJsLCAicGhvbmVfbnVtYmVyIiA9PiAkcGhvbmUsICJ1c2VybmFtZSIgPT4gJHRoaXMtPnVzZXJuYW1lLCAiZmlyc3RfbmFtZSIgPT4gJGZpcnN0X25hbWUsICJiaW9ncmFwaHkiID0+ICRiaW9ncmFwaHksICJlbWFpbCIgPT4gJGVtYWlsLCAiZ2VuZGVyIiA9PiAkZ2VuZGVyICkpOw0KICAgICAgICByZXR1cm4gJHRoaXMtPnJlcXVlc3QoImFjY291bnRzL2VkaXRfcHJvZmlsZS8iLCBTaWduYXR1cmVVdGlsczo6Z2VuZXJhdGVTaWduYXR1cmUoJGRhdGEpKVsxXTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogR2V0IHVzZXJuYW1lIGluZm8uDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJHVzZXJuYW1lSWQNCiAgICAgICAgICogICBVc2VybmFtZSBpZA0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgVXNlcm5hbWUgZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBnZXRVc2VybmFtZUluZm8oJHVzZXJuYW1lSWQpDQogICAgew0KICAgICAgICByZXR1cm4gJHRoaXMtPnJlcXVlc3QoInVzZXJzLyIgLiAkdXNlcm5hbWVJZCAuICIvaW5mby8iKVsxXTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogR2V0IHNlbGYgdXNlcm5hbWUgaW5mby4NCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIFVzZXJuYW1lIGRhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gZ2V0U2VsZlVzZXJuYW1lSW5mbygpDQogICAgew0KICAgICAgICByZXR1cm4gJHRoaXMtPmdldFVzZXJuYW1lSW5mbygkdGhpcy0+dXNlcm5hbWVfaWQpOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBHZXQgcmVjZW50IGFjdGl2aXR5Lg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgUmVjZW50IGFjdGl2aXR5IGRhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gZ2V0UmVjZW50QWN0aXZpdHkoJG1heGlkID0gTlVMTCkNCiAgICB7DQogICAgICAgIGxpc3QoLCAkYWN0aXZpdHkpID0gJHRoaXMtPnJlcXVlc3QoIm5ld3MvaW5ib3gvPyIgLiAoKCFpc19udWxsKCRtYXhpZCkgPyAibWF4X2lkPSIgLiAkbWF4aWQgOiAiIikpKTsNCiAgICAgICAgaWYoICRhY3Rpdml0eVsic3RhdHVzIl0gIT0gIm9rIiApIA0KICAgICAgICB7DQogICAgICAgICAgICB0aHJvdyBuZXcgRXhjZXB0aW9uKCRhY3Rpdml0eVsibWVzc2FnZSJdIC4gIlxuIik7DQogICAgICAgIH0NCg0KICAgICAgICByZXR1cm4gJGFjdGl2aXR5Ow0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBHZXQgcmVjZW50IGFjdGl2aXR5IGZyb20gYWNjb3VudHMgZm9sbG93ZWQuDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBSZWNlbnQgYWN0aXZpdHkgZGF0YSBvZiBmb2xsb3dzDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGdldEZvbGxvd2luZ1JlY2VudEFjdGl2aXR5KCRtYXhpZCA9IE5VTEwpDQogICAgew0KICAgICAgICBsaXN0KCwgJGFjdGl2aXR5KSA9ICR0aGlzLT5yZXF1ZXN0KCJuZXdzLz8iIC4gKCghaXNfbnVsbCgkbWF4aWQpID8gIm1heF9pZD0iIC4gJG1heGlkIDogIiIpKSk7DQogICAgICAgIGlmKCAkYWN0aXZpdHlbInN0YXR1cyJdICE9ICJvayIgKSANCiAgICAgICAgew0KICAgICAgICAgICAgdGhyb3cgbmV3IEV4Y2VwdGlvbigkYWN0aXZpdHlbIm1lc3NhZ2UiXSAuICJcbiIpOw0KICAgICAgICB9DQoNCiAgICAgICAgcmV0dXJuICRhY3Rpdml0eTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogSSBkb250IGtub3cgdGhpcyB5ZXQuDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICB2MiBpbmJveCBkYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGdldHYySW5ib3goKQ0KICAgIHsNCiAgICAgICAgbGlzdCgsICRpbmJveCkgPSAkdGhpcy0+cmVxdWVzdCgiZGlyZWN0X3YyL2luYm94Lz8iKTsNCiAgICAgICAgaWYoICRpbmJveFsic3RhdHVzIl0gIT0gIm9rIiApIA0KICAgICAgICB7DQogICAgICAgICAgICB0aHJvdyBuZXcgRXhjZXB0aW9uKCRpbmJveFsibWVzc2FnZSJdIC4gIlxuIik7DQogICAgICAgIH0NCg0KICAgICAgICByZXR1cm4gJGluYm94Ow0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBEaXJlY3QgVGhyZWFkIERhdGEuDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSAgaW50ICR0aHJlYWRJZCBUaHJlYWQgSWQNCiAgICAgICAgICoNCiAgICAgICAgICogQHRocm93cyBFeGNlcHRpb24gRGlyZWN0IFRocmVhZCBEYXRhDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkgRGlyZWN0IFRocmVhZCBEYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGRpcmVjdFRocmVhZCgkdGhyZWFkSWQpDQogICAgew0KICAgICAgICBsaXN0KCwgJGRpcmVjdFRocmVhZCkgPSAkdGhpcy0+cmVxdWVzdCgiZGlyZWN0X3YyL3RocmVhZHMvIiAuICR0aHJlYWRJZCAuICIvPyIpOw0KICAgICAgICBpZiggJGRpcmVjdFRocmVhZFsic3RhdHVzIl0gIT0gIm9rIiApIA0KICAgICAgICB7DQogICAgICAgICAgICB0aHJvdyBuZXcgRXhjZXB0aW9uKCRkaXJlY3RUaHJlYWRbIm1lc3NhZ2UiXSAuICJcbiIpOw0KICAgICAgICB9DQoNCiAgICAgICAgcmV0dXJuICRkaXJlY3RUaHJlYWQ7DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIEdldCB1c2VyIHRhZ3MuDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJHVzZXJuYW1lSWQNCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIHVzZXIgdGFncyBkYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGdldFVzZXJUYWdzKCR1c2VybmFtZUlkLCAkbWF4aWQgPSBOVUxMKQ0KICAgIHsNCiAgICAgICAgbGlzdCgsICR0YWdzKSA9ICR0aGlzLT5yZXF1ZXN0KCJ1c2VydGFncy8iIC4gJHVzZXJuYW1lSWQgLiAiL2ZlZWQvP3JhbmtfdG9rZW49IiAuICR0aGlzLT5yYW5rX3Rva2VuIC4gIiZyYW5rZWRfY29udGVudD10cnVlIiAuICgoIWlzX251bGwoJG1heGlkKSA/ICJtYXhfaWQ9IiAuICRtYXhpZCA6ICIiKSkpOw0KICAgICAgICBpZiggJHRhZ3NbInN0YXR1cyJdICE9ICJvayIgKSANCiAgICAgICAgew0KICAgICAgICAgICAgdGhyb3cgbmV3IEV4Y2VwdGlvbigkdGFnc1sibWVzc2FnZSJdIC4gIlxuIik7DQogICAgICAgIH0NCg0KICAgICAgICByZXR1cm4gJHRhZ3M7DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIEdldCBzZWxmIHVzZXIgdGFncy4NCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIHNlbGYgdXNlciB0YWdzIGRhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gZ2V0U2VsZlVzZXJUYWdzKCRtYXhpZCA9IE5VTEwpDQogICAgew0KICAgICAgICByZXR1cm4gJHRoaXMtPmdldFVzZXJUYWdzKCR0aGlzLT51c2VybmFtZV9pZCwgJG1heGlkKTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogR2V0IHRhZ2dlZCBtZWRpYS4NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkdGFnDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gdGFnRmVlZCgkdGFnLCAkbWF4aWQgPSBOVUxMKQ0KICAgIHsNCiAgICAgICAgbGlzdCgsICR1c2VyRmVlZCkgPSAkdGhpcy0+cmVxdWVzdCgiZmVlZC90YWcvIiAuICR0YWcgLiAiLz9tYXhfaWQ9IiAuICRtYXhpZCAuICImcmFua190b2tlbj0iIC4gJHRoaXMtPnJhbmtfdG9rZW4gLiAiJnJhbmtlZF9jb250ZW50PXRydWUmIik7DQogICAgICAgIGlmKCAkdXNlckZlZWRbInN0YXR1cyJdICE9ICJvayIgKSANCiAgICAgICAgew0KICAgICAgICAgICAgdGhyb3cgbmV3IEV4Y2VwdGlvbigkdXNlckZlZWRbIm1lc3NhZ2UiXSAuICJcbiIpOw0KICAgICAgICB9DQoNCiAgICAgICAgcmV0dXJuICR1c2VyRmVlZDsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogR2V0IG1lZGlhIGxpa2Vycy4NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkbWVkaWFJZA0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGdldE1lZGlhTGlrZXJzKCRtZWRpYUlkKQ0KICAgIHsNCiAgICAgICAgbGlzdCgsICRsaWtlcnMpID0gJHRoaXMtPnJlcXVlc3QoIm1lZGlhLyIgLiAkbWVkaWFJZCAuICIvbGlrZXJzLz8iKTsNCiAgICAgICAgaWYoICRsaWtlcnNbInN0YXR1cyJdICE9ICJvayIgKSANCiAgICAgICAgew0KICAgICAgICAgICAgdGhyb3cgbmV3IEV4Y2VwdGlvbigkbGlrZXJzWyJtZXNzYWdlIl0gLiAiXG4iKTsNCiAgICAgICAgfQ0KDQogICAgICAgIHJldHVybiAkbGlrZXJzOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBHZXQgdXNlciBsb2NhdGlvbnMgbWVkaWEuDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJHVzZXJuYW1lSWQNCiAgICAgICAgICogICBVc2VybmFtZSBpZA0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgR2VvIE1lZGlhIGRhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gZ2V0R2VvTWVkaWEoJHVzZXJuYW1lSWQpDQogICAgew0KICAgICAgICBsaXN0KCwgJGxvY2F0aW9ucykgPSAkdGhpcy0+cmVxdWVzdCgibWFwcy91c2VyLyIgLiAkdXNlcm5hbWVJZCAuICIvIik7DQogICAgICAgIGlmKCAkbG9jYXRpb25zWyJzdGF0dXMiXSAhPSAib2siICkgDQogICAgICAgIHsNCiAgICAgICAgICAgIHRocm93IG5ldyBFeGNlcHRpb24oJGxvY2F0aW9uc1sibWVzc2FnZSJdIC4gIlxuIik7DQogICAgICAgIH0NCg0KICAgICAgICByZXR1cm4gJGxvY2F0aW9uczsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogR2V0IHNlbGYgdXNlciBsb2NhdGlvbnMgbWVkaWEuDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBHZW8gTWVkaWEgZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBnZXRTZWxmR2VvTWVkaWEoKQ0KICAgIHsNCiAgICAgICAgcmV0dXJuICR0aGlzLT5nZXRHZW9NZWRpYSgkdGhpcy0+dXNlcm5hbWVfaWQpOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBmYWNlYm9vayB1c2VyIHNlYXJjaC4NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkcXVlcnkNCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIHF1ZXJ5IGRhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gZmJVc2VyU2VhcmNoKCRxdWVyeSkNCiAgICB7DQogICAgICAgICRxdWVyeSA9IHJhd3VybGVuY29kZSgkcXVlcnkpOw0KICAgICAgICBsaXN0KCwgJHF1ZXJ5KSA9ICR0aGlzLT5yZXF1ZXN0KCJmYnNlYXJjaC90b3BzZWFyY2gvP2NvbnRleHQ9YmxlbmRlZCZxdWVyeT0iIC4gJHF1ZXJ5IC4gIiZyYW5rX3Rva2VuPSIgLiAkdGhpcy0+cmFua190b2tlbik7DQogICAgICAgIGlmKCAkcXVlcnlbInN0YXR1cyJdICE9ICJvayIgKSANCiAgICAgICAgew0KICAgICAgICAgICAgdGhyb3cgbmV3IEV4Y2VwdGlvbigkcXVlcnlbIm1lc3NhZ2UiXSAuICJcbiIpOw0KICAgICAgICB9DQoNCiAgICAgICAgcmV0dXJuICRxdWVyeTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogU2VhcmNoIHVzZXJzLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICRxdWVyeQ0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgcXVlcnkgZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBzZWFyY2hVc2VycygkcXVlcnkpDQogICAgew0KICAgICAgICBsaXN0KCwgJHF1ZXJ5KSA9ICR0aGlzLT5yZXF1ZXN0KCJ1c2Vycy9zZWFyY2gvP2lnX3NpZ19rZXlfdmVyc2lvbj0iIC4gQ29uc3RhbnRzOjpTSUdfS0VZX1ZFUlNJT04gLiAiJmlzX3R5cGVhaGVhZD10cnVlJnF1ZXJ5PSIgLiAkcXVlcnkgLiAiJnJhbmtfdG9rZW49IiAuICR0aGlzLT5yYW5rX3Rva2VuKTsNCiAgICAgICAgaWYoICRxdWVyeVsic3RhdHVzIl0gIT0gIm9rIiApIA0KICAgICAgICB7DQogICAgICAgICAgICB0aHJvdyBuZXcgRXhjZXB0aW9uKCRxdWVyeVsibWVzc2FnZSJdIC4gIlxuIik7DQogICAgICAgIH0NCg0KICAgICAgICByZXR1cm4gJHF1ZXJ5Ow0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBTZWFyY2ggZXhhY3QgdXNlcm5hbWUNCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyB1c2VybmFtZU5hbWUgdXNlcm5hbWUgYXMgU1RSSU5HIG5vdCBhbiBpZA0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgcXVlcnkgZGF0YQ0KICAgICAgICAgKg0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBzZWFyY2hVc2VybmFtZSgkdXNlcm5hbWVOYW1lKQ0KICAgIHsNCiAgICAgICAgbGlzdCgsICRxdWVyeSkgPSAkdGhpcy0+cmVxdWVzdCgidXNlcnMvIiAuICR1c2VybmFtZU5hbWUgLiAiL3VzZXJuYW1laW5mby8iKTsNCiAgICAgICAgcmV0dXJuICRxdWVyeTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogU2VhcmNoIHVzZXJzIHVzaW5nIGFkZHJlcyBib29rLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcGFyYW0gYXJyYXkgJGNvbnRhY3RzDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBxdWVyeSBkYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIHN5bmNGcm9tQWRyZXNzQm9vaygkY29udGFjdHMpDQogICAgew0KICAgICAgICAkZGF0YSA9ICJjb250YWN0cz0iIC4ganNvbl9lbmNvZGUoJGNvbnRhY3RzLCB0cnVlKTsNCiAgICAgICAgcmV0dXJuICR0aGlzLT5yZXF1ZXN0KCJhZGRyZXNzX2Jvb2svbGluay8/aW5jbHVkZT1leHRyYV9kaXNwbGF5X25hbWUsdGh1bWJuYWlscyIsICRkYXRhKVsxXTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogU2VhcmNoIHRhZ3MuDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJHF1ZXJ5DQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBxdWVyeSBkYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIHNlYXJjaFRhZ3MoJHF1ZXJ5KQ0KICAgIHsNCiAgICAgICAgbGlzdCgsICRxdWVyeSkgPSAkdGhpcy0+cmVxdWVzdCgidGFncy9zZWFyY2gvP2lzX3R5cGVhaGVhZD10cnVlJnE9IiAuICRxdWVyeSAuICImcmFua190b2tlbj0iIC4gJHRoaXMtPnJhbmtfdG9rZW4pOw0KICAgICAgICBpZiggJHF1ZXJ5WyJzdGF0dXMiXSAhPSAib2siICkgDQogICAgICAgIHsNCiAgICAgICAgICAgIHRocm93IG5ldyBFeGNlcHRpb24oJHF1ZXJ5WyJtZXNzYWdlIl0gLiAiXG4iKTsNCiAgICAgICAgfQ0KDQogICAgICAgIHJldHVybiAkcXVlcnk7DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIEdldCB0aW1lbGluZSBkYXRhLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgdGltZWxpbmUgZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBnZXRUaW1lbGluZSgkbWF4aWQgPSBOVUxMKQ0KICAgIHsNCiAgICAgICAgbGlzdCgsICR0aW1lbGluZSkgPSAkdGhpcy0+cmVxdWVzdCgiZmVlZC90aW1lbGluZS8/cmFua190b2tlbj0iIC4gJHRoaXMtPnJhbmtfdG9rZW4gLiAiJnJhbmtlZF9jb250ZW50PXRydWUiIC4gKCghaXNfbnVsbCgkbWF4aWQpID8gIiZtYXhfaWQ9IiAuICRtYXhpZCA6ICIiKSkpOw0KICAgICAgICBpZiggJHRpbWVsaW5lWyJzdGF0dXMiXSAhPSAib2siICkgDQogICAgICAgIHsNCiAgICAgICAgICAgIHRocm93IG5ldyBFeGNlcHRpb24oJHRpbWVsaW5lWyJtZXNzYWdlIl0gLiAiXG4iKTsNCiAgICAgICAgfQ0KDQogICAgICAgIHJldHVybiAkdGltZWxpbmU7DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIEB0aHJvd3MgRXhjZXB0aW9uDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXl8dm9pZA0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBnZXRSZWVsc1RyYXlGZWVkKCkNCiAgICB7DQogICAgICAgIGxpc3QoLCAkZmVlZCkgPSAkdGhpcy0+cmVxdWVzdCgiZmVlZC9yZWVsc190cmF5LyIpOw0KICAgICAgICBpZiggJGZlZWRbInN0YXR1cyJdICE9ICJvayIgKSANCiAgICAgICAgew0KICAgICAgICAgICAgdGhyb3cgbmV3IEV4Y2VwdGlvbigkZmVlZFsibWVzc2FnZSJdIC4gIlxuIik7DQogICAgICAgIH0NCg0KICAgICAgICByZXR1cm4gJGZlZWQ7DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIEdldCB1c2VyIGZlZWQuDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJHVzZXJuYW1lSWQNCiAgICAgICAgICogICAgVXNlcm5hbWUgaWQNCiAgICAgICAgICogQHBhcmFtIG51bGwgICAkbWF4aWQNCiAgICAgICAgICogICAgTWF4IElkDQogICAgICAgICAqIEBwYXJhbSBudWxsICAgJG1pblRpbWVzdGFtcA0KICAgICAgICAgKiAgICBNaW4gdGltZXN0YW1wDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkgVXNlciBmZWVkIGRhdGENCiAgICAgICAgICogICAgVXNlciBmZWVkIGRhdGENCiAgICAgICAgICogQHRocm93cyBFeGNlcHRpb24NCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gZ2V0VXNlckZlZWQoJHVzZXJuYW1lSWQsICRtYXhpZCA9IE5VTEwsICRtaW5UaW1lc3RhbXAgPSBOVUxMKQ0KICAgIHsNCiAgICAgICAgbGlzdCgsICR1c2VyRmVlZCkgPSAkdGhpcy0+cmVxdWVzdCgiZmVlZC91c2VyLyIgLiAkdXNlcm5hbWVJZCAuICIvP3JhbmtfdG9rZW49IiAuICR0aGlzLT5yYW5rX3Rva2VuIC4gKCghaXNfbnVsbCgkbWF4aWQpID8gIiZtYXhfaWQ9IiAuICRtYXhpZCA6ICIiKSkgLiAoKCFpc19udWxsKCRtaW5UaW1lc3RhbXApID8gIiZtaW5fdGltZXN0YW1wPSIgLiAkbWluVGltZXN0YW1wIDogIiIpKSAuICImcmFua2VkX2NvbnRlbnQ9dHJ1ZSIpOw0KICAgICAgICByZXR1cm4gJHVzZXJGZWVkOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBHZXQgaGFzaHRhZyBmZWVkLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICRoYXNodGFnU3RyaW5nDQogICAgICAgICAqICAgIEhhc2h0YWcgc3RyaW5nLCBub3QgaW5jbHVkaW5nIHRoZSAjDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBIYXNodGFnIGZlZWQgZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBnZXRIYXNodGFnRmVlZCgkaGFzaHRhZ1N0cmluZywgJG1heGlkID0gTlVMTCkNCiAgICB7DQogICAgICAgIGlmKCBpc19udWxsKCRtYXhpZCkgKSANCiAgICAgICAgew0KICAgICAgICAgICAgJGVuZHBvaW50ID0gImZlZWQvdGFnLyIgLiAkaGFzaHRhZ1N0cmluZyAuICIvP3JhbmtfdG9rZW49IiAuICR0aGlzLT5yYW5rX3Rva2VuIC4gIiZyYW5rZWRfY29udGVudD10cnVlJiI7DQogICAgICAgIH0NCiAgICAgICAgZWxzZQ0KICAgICAgICB7DQogICAgICAgICAgICAkZW5kcG9pbnQgPSAiZmVlZC90YWcvIiAuICRoYXNodGFnU3RyaW5nIC4gIi8/bWF4X2lkPSIgLiAkbWF4aWQgLiAiJnJhbmtfdG9rZW49IiAuICR0aGlzLT5yYW5rX3Rva2VuIC4gIiZyYW5rZWRfY29udGVudD10cnVlJiI7DQogICAgICAgIH0NCg0KICAgICAgICBsaXN0KCwgJGhhc2h0YWdGZWVkKSA9ICR0aGlzLT5yZXF1ZXN0KCRlbmRwb2ludCk7DQogICAgICAgIGlmKCAkaGFzaHRhZ0ZlZWRbInN0YXR1cyJdICE9ICJvayIgKSANCiAgICAgICAgew0KICAgICAgICAgICAgdGhyb3cgbmV3IEV4Y2VwdGlvbigkaGFzaHRhZ0ZlZWRbIm1lc3NhZ2UiXSAuICJcbiIpOw0KICAgICAgICB9DQoNCiAgICAgICAgcmV0dXJuICRoYXNodGFnRmVlZDsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogR2V0IGxvY2F0aW9ucy4NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkcXVlcnkNCiAgICAgICAgICogICAgc2VhcmNoIHF1ZXJ5DQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBMb2NhdGlvbiBsb2NhdGlvbiBkYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIHNlYXJjaExvY2F0aW9uKCRxdWVyeSkNCiAgICB7DQogICAgICAgICRxdWVyeSA9IHJhd3VybGVuY29kZSgkcXVlcnkpOw0KICAgICAgICAkZW5kcG9pbnQgPSAiZmJzZWFyY2gvcGxhY2VzLz9yYW5rX3Rva2VuPSIgLiAkdGhpcy0+cmFua190b2tlbiAuICImcXVlcnk9IiAuICRxdWVyeTsNCiAgICAgICAgbGlzdCgsICRsb2NhdGlvbkZlZWQpID0gJHRoaXMtPnJlcXVlc3QoJGVuZHBvaW50KTsNCiAgICAgICAgaWYoICRsb2NhdGlvbkZlZWRbInN0YXR1cyJdICE9ICJvayIgKSANCiAgICAgICAgew0KICAgICAgICAgICAgdGhyb3cgbmV3IEV4Y2VwdGlvbigkbG9jYXRpb25GZWVkWyJtZXNzYWdlIl0gLiAiXG4iKTsNCiAgICAgICAgfQ0KDQogICAgICAgIHJldHVybiAkbG9jYXRpb25GZWVkOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBHZXQgbG9jYXRpb24gZmVlZC4NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkbG9jYXRpb25JZA0KICAgICAgICAgKiAgICBsb2NhdGlvbiBpZA0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgTG9jYXRpb24gZmVlZCBkYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGdldExvY2F0aW9uRmVlZCgkbG9jYXRpb25JZCwgJG1heGlkID0gTlVMTCkNCiAgICB7DQogICAgICAgIGlmKCBpc19udWxsKCRtYXhpZCkgKSANCiAgICAgICAgew0KICAgICAgICAgICAgJGVuZHBvaW50ID0gImZlZWQvbG9jYXRpb24vIiAuICRsb2NhdGlvbklkIC4gIi8/cmFua190b2tlbj0iIC4gJHRoaXMtPnJhbmtfdG9rZW4gLiAiJnJhbmtlZF9jb250ZW50PXRydWUmIjsNCiAgICAgICAgfQ0KICAgICAgICBlbHNlDQogICAgICAgIHsNCiAgICAgICAgICAgICRlbmRwb2ludCA9ICJmZWVkL2xvY2F0aW9uLyIgLiAkbG9jYXRpb25JZCAuICIvP21heF9pZD0iIC4gJG1heGlkIC4gIiZyYW5rX3Rva2VuPSIgLiAkdGhpcy0+cmFua190b2tlbiAuICImcmFua2VkX2NvbnRlbnQ9dHJ1ZSYiOw0KICAgICAgICB9DQoNCiAgICAgICAgbGlzdCgsICRsb2NhdGlvbkZlZWQpID0gJHRoaXMtPnJlcXVlc3QoJGVuZHBvaW50KTsNCiAgICAgICAgaWYoICRsb2NhdGlvbkZlZWRbInN0YXR1cyJdICE9ICJvayIgKSANCiAgICAgICAgew0KICAgICAgICAgICAgdGhyb3cgbmV3IEV4Y2VwdGlvbigkbG9jYXRpb25GZWVkWyJtZXNzYWdlIl0gLiAiXG4iKTsNCiAgICAgICAgfQ0KDQogICAgICAgIHJldHVybiAkbG9jYXRpb25GZWVkOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBHZXQgc2VsZiB1c2VyIGZlZWQuDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBVc2VyIGZlZWQgZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBnZXRTZWxmVXNlckZlZWQoJG1heGlkID0gTlVMTCwgJG1pblRpbWVzdGFtcCA9IE5VTEwpDQogICAgew0KICAgICAgICByZXR1cm4gJHRoaXMtPmdldFVzZXJGZWVkKCR0aGlzLT51c2VybmFtZV9pZCwgJG1heGlkLCAkbWluVGltZXN0YW1wKTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogUmFua2VkIHJlY2lwaWVudHMuDQogICAgICAgICAqDQogICAgICAgICAqIEB0aHJvd3MgRXhjZXB0aW9uIFJhbmtlZCByZWNpcGllbnRzIERhdGENCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheXx2b2lkDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGdldFJhbmtlZFJlY2lwaWVudHMoKQ0KICAgIHsNCiAgICAgICAgbGlzdCgsICRyYW5rZWRfcmVjaXBpZW50cykgPSAkdGhpcy0+cmVxdWVzdCgiZGlyZWN0X3YyL3JhbmtlZF9yZWNpcGllbnRzLz9zaG93X3RocmVhZHM9dHJ1ZSIpOw0KICAgICAgICBpZiggJHJhbmtlZF9yZWNpcGllbnRzWyJzdGF0dXMiXSAhPSAib2siICkgDQogICAgICAgIHsNCiAgICAgICAgICAgIHRocm93IG5ldyBFeGNlcHRpb24oJHJhbmtlZF9yZWNpcGllbnRzWyJtZXNzYWdlIl0gLiAiXG4iKTsNCiAgICAgICAgfQ0KDQogICAgICAgIHJldHVybiAkcmFua2VkX3JlY2lwaWVudHM7DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIFJlY2VudCByZWNpcGllbnRzLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAdGhyb3dzIEV4Y2VwdGlvbiBSYW5rZWQgcmVjaXBpZW50cyBEYXRhDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXl8dm9pZA0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBnZXRSZWNlbnRSZWNpcGllbnRzKCkNCiAgICB7DQogICAgICAgIGxpc3QoLCAkcmVjZW50X3JlY2lwaWVudHMpID0gJHRoaXMtPnJlcXVlc3QoImRpcmVjdF9zaGFyZS9yZWNlbnRfcmVjaXBpZW50cy8iKTsNCiAgICAgICAgaWYoICRyZWNlbnRfcmVjaXBpZW50c1sic3RhdHVzIl0gIT0gIm9rIiApIA0KICAgICAgICB7DQogICAgICAgICAgICB0aHJvdyBuZXcgRXhjZXB0aW9uKCRyZWNlbnRfcmVjaXBpZW50c1sibWVzc2FnZSJdIC4gIlxuIik7DQogICAgICAgIH0NCg0KICAgICAgICByZXR1cm4gJHJlY2VudF9yZWNpcGllbnRzOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBFeHBsb3JlIFRhYi4NCiAgICAgICAgICoNCiAgICAgICAgICogQHRocm93cyBFeGNlcHRpb24gRXhwbG9yZSBkYXRhDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXl8dm9pZA0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBleHBsb3JlKCkNCiAgICB7DQogICAgICAgIGxpc3QoLCAkZXhwbG9yZSkgPSAkdGhpcy0+cmVxdWVzdCgiZGlzY292ZXIvZXhwbG9yZS8iKTsNCiAgICAgICAgaWYoICRleHBsb3JlWyJzdGF0dXMiXSAhPSAib2siICkgDQogICAgICAgIHsNCiAgICAgICAgICAgIHRocm93IG5ldyBFeGNlcHRpb24oJGV4cGxvcmVbIm1lc3NhZ2UiXSAuICJcbiIpOw0KICAgICAgICB9DQoNCiAgICAgICAgcmV0dXJuICRleHBsb3JlOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBHZXQgcG9wdWxhciBmZWVkLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgcG9wdWxhciBmZWVkIGRhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gZ2V0UG9wdWxhckZlZWQoJG1heGlkID0gTlVMTCkNCiAgICB7DQogICAgICAgIGxpc3QoLCAkcG9wdWxhckZlZWQpID0gJHRoaXMtPnJlcXVlc3QoImZlZWQvcG9wdWxhci8/bWF4X2lkPSIgLiAkbWF4aWQgLiAiJnBlb3BsZV90ZWFzZXJfc3VwcG9ydGVkPTEmcmFua190b2tlbj0iIC4gJHRoaXMtPnJhbmtfdG9rZW4gLiAiJnJhbmtlZF9jb250ZW50PXRydWUmIik7DQogICAgICAgIGlmKCAkcG9wdWxhckZlZWRbInN0YXR1cyJdICE9ICJvayIgKSANCiAgICAgICAgew0KICAgICAgICAgICAgdGhyb3cgbmV3IEV4Y2VwdGlvbigkcG9wdWxhckZlZWRbIm1lc3NhZ2UiXSAuICJcbiIpOw0KICAgICAgICB9DQoNCiAgICAgICAgcmV0dXJuICRwb3B1bGFyRmVlZDsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogR2V0IHVzZXIgZm9sbG93aW5ncy4NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkdXNlcm5hbWVJZA0KICAgICAgICAgKiAgIFVzZXJuYW1lIGlkDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBmb2xsb3dlcnMgZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBnZXRVc2VyRm9sbG93aW5ncygkdXNlcm5hbWVJZCwgJG1heGlkID0gTlVMTCkNCiAgICB7DQogICAgICAgIHJldHVybiAkdGhpcy0+cmVxdWVzdCgiZnJpZW5kc2hpcHMvIiAuICR1c2VybmFtZUlkIC4gIi9mb2xsb3dpbmcvP21heF9pZD0iIC4gJG1heGlkIC4gIiZpZ19zaWdfa2V5X3ZlcnNpb249IiAuIENvbnN0YW50czo6U0lHX0tFWV9WRVJTSU9OIC4gIiZyYW5rX3Rva2VuPSIgLiAkdGhpcy0+cmFua190b2tlbilbMV07DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIEdldCB1c2VyIGZvbGxvd2Vycy4NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkdXNlcm5hbWVJZA0KICAgICAgICAgKiAgIFVzZXJuYW1lIGlkDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBmb2xsb3dlcnMgZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBnZXRVc2VyRm9sbG93ZXJzKCR1c2VybmFtZUlkLCAkbWF4aWQgPSBOVUxMKQ0KICAgIHsNCiAgICAgICAgcmV0dXJuICR0aGlzLT5yZXF1ZXN0KCJmcmllbmRzaGlwcy8iIC4gJHVzZXJuYW1lSWQgLiAiL2ZvbGxvd2Vycy8/bWF4X2lkPSIgLiAkbWF4aWQgLiAiJmlnX3NpZ19rZXlfdmVyc2lvbj0iIC4gQ29uc3RhbnRzOjpTSUdfS0VZX1ZFUlNJT04gLiAiJnJhbmtfdG9rZW49IiAuICR0aGlzLT5yYW5rX3Rva2VuKVsxXTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogR2V0IHNlbGYgdXNlciBmb2xsb3dlcnMuDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBmb2xsb3dlcnMgZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBnZXRTZWxmVXNlckZvbGxvd2VycygkbWF4aWQgPSBOVUxMKQ0KICAgIHsNCiAgICAgICAgcmV0dXJuICR0aGlzLT5nZXRVc2VyRm9sbG93ZXJzKCR0aGlzLT51c2VybmFtZV9pZCwgJG1heGlkKTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogR2V0IHNlbGYgdXNlcnMgd2UgYXJlIGZvbGxvd2luZy4NCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIHVzZXJzIHdlIGFyZSBmb2xsb3dpbmcgZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBnZXRTZWxmVXNlcnNGb2xsb3dpbmcoJG1heGlkID0gTlVMTCkNCiAgICB7DQogICAgICAgIHJldHVybiAkdGhpcy0+cmVxdWVzdCgiZnJpZW5kc2hpcHMvZm9sbG93aW5nLz9tYXhfaWQ9IiAuICRtYXhpZCAuICImaWdfc2lnX2tleV92ZXJzaW9uPSIgLiBDb25zdGFudHM6OlNJR19LRVlfVkVSU0lPTiAuICImcmFua190b2tlbj0iIC4gJHRoaXMtPnJhbmtfdG9rZW4pWzFdOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBMaWtlIHBob3RvIG9yIHZpZGVvLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICRtZWRpYUlkDQogICAgICAgICAqICAgTWVkaWEgaWQNCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIHN0YXR1cyByZXF1ZXN0DQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGxpa2UoJG1lZGlhSWQpDQogICAgew0KICAgICAgICAkZGF0YSA9IGpzb25fZW5jb2RlKGFycmF5KCAiX3V1aWQiID0+ICR0aGlzLT51dWlkLCAiX3VpZCIgPT4gJHRoaXMtPnVzZXJuYW1lX2lkLCAiX2NzcmZ0b2tlbiIgPT4gJHRoaXMtPnRva2VuLCAibWVkaWFfaWQiID0+ICRtZWRpYUlkICkpOw0KICAgICAgICByZXR1cm4gJHRoaXMtPnJlcXVlc3QoIm1lZGlhLyIgLiAkbWVkaWFJZCAuICIvbGlrZS8iLCBTaWduYXR1cmVVdGlsczo6Z2VuZXJhdGVTaWduYXR1cmUoJGRhdGEpKVsxXTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogVW5saWtlIHBob3RvIG9yIHZpZGVvLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICRtZWRpYUlkDQogICAgICAgICAqICAgTWVkaWEgaWQNCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIHN0YXR1cyByZXF1ZXN0DQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIHVubGlrZSgkbWVkaWFJZCkNCiAgICB7DQogICAgICAgICRkYXRhID0ganNvbl9lbmNvZGUoYXJyYXkoICJfdXVpZCIgPT4gJHRoaXMtPnV1aWQsICJfdWlkIiA9PiAkdGhpcy0+dXNlcm5hbWVfaWQsICJfY3NyZnRva2VuIiA9PiAkdGhpcy0+dG9rZW4sICJtZWRpYV9pZCIgPT4gJG1lZGlhSWQgKSk7DQogICAgICAgIHJldHVybiAkdGhpcy0+cmVxdWVzdCgibWVkaWEvIiAuICRtZWRpYUlkIC4gIi91bmxpa2UvIiwgU2lnbmF0dXJlVXRpbHM6OmdlbmVyYXRlU2lnbmF0dXJlKCRkYXRhKSlbMV07DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIEdldCBtZWRpYSBjb21tZW50cy4NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkbWVkaWFJZA0KICAgICAgICAgKiAgIE1lZGlhIGlkDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBNZWRpYSBjb21tZW50cyBkYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGdldE1lZGlhQ29tbWVudHMoJG1lZGlhSWQpDQogICAgew0KICAgICAgICByZXR1cm4gJHRoaXMtPnJlcXVlc3QoIm1lZGlhLyIgLiAkbWVkaWFJZCAuICIvY29tbWVudHMvPyIpWzFdOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBTZXQgbmFtZSBhbmQgcGhvbmUgKE9wdGlvbmFsKS4NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkbmFtZQ0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICRwaG9uZQ0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgU2V0IHN0YXR1cyBkYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIHNldE5hbWVBbmRQaG9uZSgkbmFtZSA9ICIiLCAkcGhvbmUgPSAiIikNCiAgICB7DQogICAgICAgICRkYXRhID0ganNvbl9lbmNvZGUoYXJyYXkoICJfdXVpZCIgPT4gJHRoaXMtPnV1aWQsICJfdWlkIiA9PiAkdGhpcy0+dXNlcm5hbWVfaWQsICJmaXJzdF9uYW1lIiA9PiAkbmFtZSwgInBob25lX251bWJlciIgPT4gJHBob25lLCAiX2NzcmZ0b2tlbiIgPT4gJHRoaXMtPnRva2VuICkpOw0KICAgICAgICByZXR1cm4gJHRoaXMtPnJlcXVlc3QoImFjY291bnRzL3NldF9waG9uZV9hbmRfbmFtZS8iLCBTaWduYXR1cmVVdGlsczo6Z2VuZXJhdGVTaWduYXR1cmUoJGRhdGEpKVsxXTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogR2V0IGRpcmVjdCBzaGFyZS4NCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIERpcmVjdCBzaGFyZSBkYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGdldERpcmVjdFNoYXJlKCkNCiAgICB7DQogICAgICAgIHJldHVybiAkdGhpcy0+cmVxdWVzdCgiZGlyZWN0X3NoYXJlL2luYm94Lz8iKVsxXTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogQmFja3VwcyBhbGwgeW91ciB1cGxvYWRlZCBwaG90b3MgOikuDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGJhY2t1cCgpDQogICAgew0KICAgICAgICAkbXlVcGxvYWRzID0gJHRoaXMtPmdldFNlbGZVc2VyRmVlZCgpOw0KICAgICAgICBmb3JlYWNoKCAkbXlVcGxvYWRzWyJpdGVtcyJdIGFzICRpdGVtICkgDQogICAgICAgIHsNCiAgICAgICAgICAgIGlmKCAhaXNfZGlyKCR0aGlzLT5JR0RhdGFQYXRoIC4gImJhY2t1cC8iIC4gKHN0cmluZykgJHRoaXMtPnVzZXJuYW1lIC4gIi0iIC4gZGF0ZSgiWS1tLWQiKSkgKSANCiAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICBta2RpcigkdGhpcy0+SUdEYXRhUGF0aCAuICJiYWNrdXAvIiAuIChzdHJpbmcpICR0aGlzLT51c2VybmFtZSAuICItIiAuIGRhdGUoIlktbS1kIikpOw0KICAgICAgICAgICAgfQ0KDQogICAgICAgICAgICBmaWxlX3B1dF9jb250ZW50cygkdGhpcy0+SUdEYXRhUGF0aCAuICJiYWNrdXAvIiAuIChzdHJpbmcpICR0aGlzLT51c2VybmFtZSAuICItIiAuIGRhdGUoIlktbS1kIikgLiAiLyIgLiAkaXRlbVsiaWQiXSAuICIuanBnIiwgZmlsZV9nZXRfY29udGVudHMoJGl0ZW1bImltYWdlX3ZlcnNpb25zMiJdWyJjYW5kaWRhdGVzIl1bMF1bInVybCJdKSk7DQogICAgICAgIH0NCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogRm9sbG93Lg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICR1c2VySWQNCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIEZyaWVuZHNoaXAgc3RhdHVzIGRhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gZm9sbG93KCR1c2VySWQpDQogICAgew0KICAgICAgICAkZGF0YSA9IGpzb25fZW5jb2RlKGFycmF5KCAiX3V1aWQiID0+ICR0aGlzLT51dWlkLCAiX3VpZCIgPT4gJHRoaXMtPnVzZXJuYW1lX2lkLCAidXNlcl9pZCIgPT4gJHVzZXJJZCwgIl9jc3JmdG9rZW4iID0+ICR0aGlzLT50b2tlbiApKTsNCiAgICAgICAgcmV0dXJuICR0aGlzLT5yZXF1ZXN0KCJmcmllbmRzaGlwcy9jcmVhdGUvIiAuICR1c2VySWQgLiAiLyIsIFNpZ25hdHVyZVV0aWxzOjpnZW5lcmF0ZVNpZ25hdHVyZSgkZGF0YSkpWzFdOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBVbmZvbGxvdy4NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkdXNlcklkDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBGcmllbmRzaGlwIHN0YXR1cyBkYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIHVuZm9sbG93KCR1c2VySWQpDQogICAgew0KICAgICAgICAkZGF0YSA9IGpzb25fZW5jb2RlKGFycmF5KCAiX3V1aWQiID0+ICR0aGlzLT51dWlkLCAiX3VpZCIgPT4gJHRoaXMtPnVzZXJuYW1lX2lkLCAidXNlcl9pZCIgPT4gJHVzZXJJZCwgIl9jc3JmdG9rZW4iID0+ICR0aGlzLT50b2tlbiApKTsNCiAgICAgICAgcmV0dXJuICR0aGlzLT5yZXF1ZXN0KCJmcmllbmRzaGlwcy9kZXN0cm95LyIgLiAkdXNlcklkIC4gIi8iLCBTaWduYXR1cmVVdGlsczo6Z2VuZXJhdGVTaWduYXR1cmUoJGRhdGEpKVsxXTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogQmxvY2suDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJHVzZXJJZA0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgRnJpZW5kc2hpcCBzdGF0dXMgZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBibG9jaygkdXNlcklkKQ0KICAgIHsNCiAgICAgICAgJGRhdGEgPSBqc29uX2VuY29kZShhcnJheSggIl91dWlkIiA9PiAkdGhpcy0+dXVpZCwgIl91aWQiID0+ICR0aGlzLT51c2VybmFtZV9pZCwgInVzZXJfaWQiID0+ICR1c2VySWQsICJfY3NyZnRva2VuIiA9PiAkdGhpcy0+dG9rZW4gKSk7DQogICAgICAgIHJldHVybiAkdGhpcy0+cmVxdWVzdCgiZnJpZW5kc2hpcHMvYmxvY2svIiAuICR1c2VySWQgLiAiLyIsIFNpZ25hdHVyZVV0aWxzOjpnZW5lcmF0ZVNpZ25hdHVyZSgkZGF0YSkpWzFdOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBVbmJsb2NrLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICR1c2VySWQNCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIEZyaWVuZHNoaXAgc3RhdHVzIGRhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gdW5ibG9jaygkdXNlcklkKQ0KICAgIHsNCiAgICAgICAgJGRhdGEgPSBqc29uX2VuY29kZShhcnJheSggIl91dWlkIiA9PiAkdGhpcy0+dXVpZCwgIl91aWQiID0+ICR0aGlzLT51c2VybmFtZV9pZCwgInVzZXJfaWQiID0+ICR1c2VySWQsICJfY3NyZnRva2VuIiA9PiAkdGhpcy0+dG9rZW4gKSk7DQogICAgICAgIHJldHVybiAkdGhpcy0+cmVxdWVzdCgiZnJpZW5kc2hpcHMvdW5ibG9jay8iIC4gJHVzZXJJZCAuICIvIiwgU2lnbmF0dXJlVXRpbHM6OmdlbmVyYXRlU2lnbmF0dXJlKCRkYXRhKSlbMV07DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIFNob3cgVXNlciBGcmllbmRzaGlwLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICR1c2VySWQNCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIEZyaWVuZHNoaXAgcmVsYXRpb25zaGlwIGRhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gdXNlckZyaWVuZHNoaXAoJHVzZXJJZCkNCiAgICB7DQogICAgICAgICRkYXRhID0ganNvbl9lbmNvZGUoYXJyYXkoICJfdXVpZCIgPT4gJHRoaXMtPnV1aWQsICJfdWlkIiA9PiAkdGhpcy0+dXNlcm5hbWVfaWQsICJ1c2VyX2lkIiA9PiAkdXNlcklkLCAiX2NzcmZ0b2tlbiIgPT4gJHRoaXMtPnRva2VuICkpOw0KICAgICAgICByZXR1cm4gJHRoaXMtPnJlcXVlc3QoImZyaWVuZHNoaXBzL3Nob3cvIiAuICR1c2VySWQgLiAiLyIsIFNpZ25hdHVyZVV0aWxzOjpnZW5lcmF0ZVNpZ25hdHVyZSgkZGF0YSkpWzFdOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBHZXQgbGlrZWQgbWVkaWEuDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBMaWtlZCBtZWRpYSBkYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIGdldExpa2VkTWVkaWEoJG1heGlkID0gTlVMTCkNCiAgICB7DQogICAgICAgIHJldHVybiAkdGhpcy0+cmVxdWVzdCgiZmVlZC9saWtlZC8/bWF4X2lkPSIgLiAkbWF4aWQpWzFdOw0KICAgIH0NCg0KICAgIHByb3RlY3RlZCBmdW5jdGlvbiBidWlsZEJvZHkoJGJvZGllcywgJGJvdW5kYXJ5KQ0KICAgIHsNCiAgICAgICAgJGJvZHkgPSAiIjsNCiAgICAgICAgZm9yZWFjaCggJGJvZGllcyBhcyAkYiApIA0KICAgICAgICB7DQogICAgICAgICAgICAkYm9keSAuPSAiLS0iIC4gJGJvdW5kYXJ5IC4gIlxyXG4iOw0KICAgICAgICAgICAgJGJvZHkgLj0gIkNvbnRlbnQtRGlzcG9zaXRpb246ICIgLiAkYlsidHlwZSJdIC4gIjsgbmFtZT1cIiIgLiAkYlsibmFtZSJdIC4gIlwiIjsNCiAgICAgICAgICAgIGlmKCBpc3NldCgkYlsiZmlsZW5hbWUiXSkgKSANCiAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAkZXh0ID0gcGF0aGluZm8oJGJbImZpbGVuYW1lIl0sIFBBVEhJTkZPX0VYVEVOU0lPTik7DQogICAgICAgICAgICAgICAgJGJvZHkgLj0gIjsgZmlsZW5hbWU9XCIiIC4gInBlbmRpbmdfbWVkaWFfIiAuIG51bWJlcl9mb3JtYXQocm91bmQobWljcm90aW1lKHRydWUpICogMTAwMCksIDAsICIiLCAiIikgLiAiLiIgLiAkZXh0IC4gIlwiIjsNCiAgICAgICAgICAgIH0NCg0KICAgICAgICAgICAgaWYoIGlzc2V0KCRiWyJoZWFkZXJzIl0pICYmIGlzX2FycmF5KCRiWyJoZWFkZXJzIl0pICkgDQogICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgZm9yZWFjaCggJGJbImhlYWRlcnMiXSBhcyAkaGVhZGVyICkgDQogICAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICAgICAkYm9keSAuPSAiXHJcbiIgLiAkaGVhZGVyOw0KICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgIH0NCg0KICAgICAgICAgICAgJGJvZHkgLj0gIlxyXG5cclxuIiAuICRiWyJkYXRhIl0gLiAiXHJcbiI7DQogICAgICAgIH0NCiAgICAgICAgJGJvZHkgLj0gIi0tIiAuICRib3VuZGFyeSAuICItLSI7DQogICAgICAgIHJldHVybiAkYm9keTsNCiAgICB9DQoNCiAgICBwcm90ZWN0ZWQgZnVuY3Rpb24gcmVxdWVzdCgkZW5kcG9pbnQsICRwb3N0ID0gTlVMTCwgJGxvZ2luID0gZmFsc2UpDQogICAgew0KICAgICAgICBpZiggISR0aGlzLT5pc0xvZ2dlZEluICYmICEkbG9naW4gKSANCiAgICAgICAgew0KICAgICAgICAgICAgdGhyb3cgbmV3IEV4Y2VwdGlvbigiTm90IGxvZ2dlZCBpblxuIik7DQogICAgICAgIH0NCg0KICAgICAgICAkaGVhZGVycyA9IGFycmF5KCAiQ29ubmVjdGlvbjogY2xvc2UiLCAiQWNjZXB0OiAqLyoiLCAiWC1JRy1DYXBhYmlsaXRpZXM6ICIgLiBDb25zdGFudHM6OlhfSUdfQ2FwYWJpbGl0aWVzLCAiWC1JRy1Db25uZWN0aW9uLVR5cGU6IFdJRkkiLCAiQ29udGVudC10eXBlOiBhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQ7IGNoYXJzZXQ9VVRGLTgiLCAiQWNjZXB0LUxhbmd1YWdlOiB0ci1UUiIsICJYX0ZPUldBUkRFRF9GT1I6ICIgLiAkdGhpcy0+c2V0dGluZ3MtPmdldCgiaXAiKSwgIlJFTU9URV9BRERSOiAiIC4gJHRoaXMtPnNldHRpbmdzLT5nZXQoImlwIikgKTsNCiAgICAgICAgJGNoID0gY3VybF9pbml0KCk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9VUkwsIENvbnN0YW50czo6QVBJX1VSTCAuICRlbmRwb2ludCk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9VU0VSQUdFTlQsICR0aGlzLT51c2VyQWdlbnQpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfUkVUVVJOVFJBTlNGRVIsIHRydWUpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfRk9MTE9XTE9DQVRJT04sIHRydWUpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfSEVBREVSLCB0cnVlKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX0hUVFBIRUFERVIsICRoZWFkZXJzKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1ZFUkJPU0UsIGZhbHNlKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1NTTF9WRVJJRllQRUVSLCBmYWxzZSk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9TU0xfVkVSSUZZSE9TVCwgZmFsc2UpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfQ09PS0lFRklMRSwgJHRoaXMtPklHRGF0YVBhdGggLiAoc3RyaW5nKSAkdGhpcy0+dXNlcm5hbWUgLiAiLmRhdCIpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfQ09PS0lFSkFSLCAkdGhpcy0+SUdEYXRhUGF0aCAuIChzdHJpbmcpICR0aGlzLT51c2VybmFtZSAuICIuZGF0Iik7DQogICAgICAgIGlmKCAkcG9zdCApIA0KICAgICAgICB7DQogICAgICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfUE9TVCwgdHJ1ZSk7DQogICAgICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfUE9TVEZJRUxEUywgJHBvc3QpOw0KICAgICAgICB9DQoNCiAgICAgICAgJHJlc3AgPSBjdXJsX2V4ZWMoJGNoKTsNCiAgICAgICAgJGhlYWRlcl9sZW4gPSBjdXJsX2dldGluZm8oJGNoLCBDVVJMSU5GT19IRUFERVJfU0laRSk7DQogICAgICAgICRoZWFkZXIgPSBzdWJzdHIoJHJlc3AsIDAsICRoZWFkZXJfbGVuKTsNCiAgICAgICAgJGJvZHkgPSBzdWJzdHIoJHJlc3AsICRoZWFkZXJfbGVuKTsNCiAgICAgICAgY3VybF9jbG9zZSgkY2gpOw0KICAgICAgICBpZiggJHRoaXMtPmRlYnVnICkgDQogICAgICAgIHsNCiAgICAgICAgICAgIGVjaG8gIlJFUVVFU1Q6ICIgLiAkZW5kcG9pbnQgLiAiXG4iOw0KICAgICAgICAgICAgaWYoICFpc19udWxsKCRwb3N0KSAmJiAhaXNfYXJyYXkoJHBvc3QpICkgDQogICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgZWNobyAiREFUQTogIiAuIHVybGRlY29kZSgkcG9zdCkgLiAiXG4iOw0KICAgICAgICAgICAgfQ0KDQogICAgICAgICAgICBlY2hvICJSRVNQT05TRTogIiAuICRib2R5IC4gIlxuXG4iOw0KICAgICAgICB9DQoNCiAgICAgICAgcmV0dXJuIGFycmF5KCAkaGVhZGVyLCBqc29uX2RlY29kZSgkYm9keSwgdHJ1ZSkgKTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogSXMgVXNlciBMb2dnZWQgaW4/DQogICAgICAgICAqIEByZXR1cm4gYm9vbA0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBpc0xvZ2dlZEluKCkNCiAgICB7DQogICAgICAgIHJldHVybiAkdGhpcy0+aXNMb2dnZWRJbjsNCiAgICB9DQoNCn0NCg0KDQpjbGFzcyBJbnN0YWdyYW1XZWINCnsNCiAgICBwcm90ZWN0ZWQgJHVzZXJuYW1lID0gTlVMTDsNCiAgICBwcm90ZWN0ZWQgJHVzZXJuYW1lX2lkID0gTlVMTDsNCiAgICBwcm90ZWN0ZWQgJHRva2VuID0gTlVMTDsNCiAgICBwcm90ZWN0ZWQgJGZha2VfaXAgPSBOVUxMOw0KICAgIHByb3RlY3RlZCAkaXNMb2dnZWRJbiA9IGZhbHNlOw0KICAgIHByb3RlY3RlZCAkSUdEYXRhUGF0aCA9IE5VTEw7DQoNCiAgICAvKioNCiAgICAgICAgICogRGVmYXVsdCBjbGFzcyBjb25zdHJ1Y3Rvci4NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkdXNlcm5hbWUNCiAgICAgICAgICogICBZb3VyIEluc3RhZ3JhbSB1c2VybmFtZS4NCiAgICAgICAgICogQHBhcmFtICAgICAgICAkSUdEYXRhUGF0aA0KICAgICAgICAgKiAgIERlZmF1bHQgZm9sZGVyIHRvIHN0b3JlIGRhdGEsIHlvdSBjYW4gY2hhbmdlIGl0Lg0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBfX2NvbnN0cnVjdCgkdXNlcm5hbWUsICRJR0RhdGFQYXRoID0gTlVMTCkNCiAgICB7DQogICAgICAgIGlmKCAhaXNfbnVsbCgkSUdEYXRhUGF0aCkgKSANCiAgICAgICAgew0KICAgICAgICAgICAgJHRoaXMtPklHRGF0YVBhdGggPSAkSUdEYXRhUGF0aDsNCiAgICAgICAgfQ0KICAgICAgICBlbHNlDQogICAgICAgIHsNCiAgICAgICAgICAgICR0aGlzLT5JR0RhdGFQYXRoID0gV293OjpnZXQoInByb2plY3QvY29va2llUGF0aCIpIC4gImluc3RhZ3JhbS8iOw0KICAgICAgICB9DQoNCiAgICAgICAgJHRoaXMtPnNldFVzZXIoJHVzZXJuYW1lKTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogU2V0IHRoZSB1c2VyLiBNYW5hZ2UgbXVsdGlwbGUgYWNjb3VudHMuDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJHVzZXJuYW1lDQogICAgICAgICAqICAgWW91ciBJbnN0YWdyYW0gdXNlcm5hbWUuDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIHNldFVzZXIoJHVzZXJuYW1lKQ0KICAgIHsNCiAgICAgICAgJHRoaXMtPnVzZXJuYW1lID0gJHVzZXJuYW1lOw0KICAgICAgICBpZiggZmlsZV9leGlzdHMoJHRoaXMtPklHRGF0YVBhdGggLiAoc3RyaW5nKSAkdGhpcy0+dXNlcm5hbWUgLiAiLmRhdCIpICkgDQogICAgICAgIHsNCiAgICAgICAgICAgICRjb29raWVGaWxlID0gZmlsZV9nZXRfY29udGVudHMoJHRoaXMtPklHRGF0YVBhdGggLiAoc3RyaW5nKSAkdXNlcm5hbWUgLiAiLmRhdCIpOw0KICAgICAgICAgICAgJHVzZXJDb29raWVzID0gVXRpbHM6OmNvb2tpZVRvQXJyYXkoJGNvb2tpZUZpbGUpOw0KICAgICAgICAgICAgJHRoaXMtPmlzTG9nZ2VkSW4gPSB0cnVlOw0KICAgICAgICAgICAgJHRoaXMtPnVzZXJuYW1lX2lkID0gdHJpbSgkdXNlckNvb2tpZXNbImRzX3VzZXJfaWQiXSk7DQogICAgICAgICAgICAkdGhpcy0+dG9rZW4gPSB0cmltKCR1c2VyQ29va2llc1siY3NyZnRva2VuIl0pOw0KICAgICAgICAgICAgJHRoaXMtPmZha2VfaXAgPSAiNzguIiAuIHJhbmQoMTYwLCAxOTEpIC4gIi4iIC4gcmFuZCgxLCAyNTUpIC4gIi4iIC4gcmFuZCgxLCAyNTUpOw0KICAgICAgICB9DQoNCiAgICB9DQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gZ2V0RGF0YSgpDQogICAgew0KICAgICAgICByZXR1cm4gYXJyYXkoICJ1c2VybmFtZSIgPT4gJHRoaXMtPnVzZXJuYW1lLCAidXNlcm5hbWVfaWQiID0+ICR0aGlzLT51c2VybmFtZV9pZCwgInRva2VuIiA9PiAkdGhpcy0+dG9rZW4sICJpcCIgPT4gJHRoaXMtPmZha2VfaXAgKTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogQ29tbWVudCBtZWRpYS4NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkbWVkaWFJZA0KICAgICAgICAgKiAgIE1lZGlhIGlkDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJGNvbW1lbnRUZXh0DQogICAgICAgICAqICAgQ29tbWVudCBUZXh0DQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBjb21tZW50IG1lZGlhIGRhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gY29tbWVudCgkbWVkaWFJZCwgJGNvbW1lbnRUZXh0KQ0KICAgIHsNCiAgICAgICAgJHBvc3REYXRhID0gImNvbW1lbnRfdGV4dD0iIC4gJGNvbW1lbnRUZXh0Ow0KICAgICAgICAkaGVhZGVycyA9IGFycmF5KCk7DQogICAgICAgICRoZWFkZXJzW10gPSAiUmVmZXJlcjogaHR0cHM6Ly93d3cuaW5zdGFncmFtLmNvbS8iOw0KICAgICAgICAkaGVhZGVyc1tdID0gIkROVDogMSI7DQogICAgICAgICRoZWFkZXJzW10gPSAiT3JpZ2luOiBodHRwczovL3d3dy5pbnN0YWdyYW0uY29tLyI7DQogICAgICAgICRoZWFkZXJzW10gPSAiWC1DU1JGVG9rZW46ICIgLiB0cmltKCR0aGlzLT50b2tlbik7DQogICAgICAgICRoZWFkZXJzW10gPSAiWC1SZXF1ZXN0ZWQtV2l0aDogWE1MSHR0cFJlcXVlc3QiOw0KICAgICAgICAkaGVhZGVyc1tdID0gIlgtSW5zdGFncmFtLUFKQVg6IDEiOw0KICAgICAgICAkaGVhZGVyc1tdID0gIkNvbm5lY3Rpb246IGNsb3NlIjsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJDYWNoZS1Db250cm9sOiBtYXgtYWdlPTAiOw0KICAgICAgICByZXR1cm4gJHRoaXMtPnJlcXVlc3QoIndlYi9jb21tZW50cy8iIC4gJG1lZGlhSWQgLiAiL2FkZC8iLCAkaGVhZGVycywgJHBvc3REYXRhKVsxXTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogR2V0IHVzZXJuYW1lIGluZm8uDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJHVzZXJuYW1lDQogICAgICAgICAqICAgVXNlcm5hbWUNCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKiAgIFVzZXJuYW1lIGRhdGENCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gZ2V0VXNlcm5hbWVJbmZvKCR1c2VybmFtZSkNCiAgICB7DQogICAgICAgICRoZWFkZXJzID0gYXJyYXkoKTsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJSZWZlcmVyOiBodHRwczovL3d3dy5pbnN0YWdyYW0uY29tLyI7DQogICAgICAgICRoZWFkZXJzW10gPSAiRE5UOiAxIjsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJPcmlnaW46IGh0dHBzOi8vd3d3Lmluc3RhZ3JhbS5jb20vIjsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJYLUNTUkZUb2tlbjogIiAuIHRyaW0oJHRoaXMtPnRva2VuKTsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJYLVJlcXVlc3RlZC1XaXRoOiBYTUxIdHRwUmVxdWVzdCI7DQogICAgICAgICRoZWFkZXJzW10gPSAiWC1JbnN0YWdyYW0tQUpBWDogMSI7DQogICAgICAgICRoZWFkZXJzW10gPSAiQ29ubmVjdGlvbjogY2xvc2UiOw0KICAgICAgICAkaGVhZGVyc1tdID0gIkNhY2hlLUNvbnRyb2w6IG1heC1hZ2U9MCI7DQogICAgICAgIHJldHVybiAkdGhpcy0+cmVxdWVzdCgoc3RyaW5nKSAkdXNlcm5hbWUgLiAiLz9fX2E9MSIsICRoZWFkZXJzKVsxXTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogTWVkaWEgaW5mbw0KICAgICAgICAgKg0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICRtZWRpYUNvZGUNCiAgICAgICAgICogICBNZWRpYSBjb2RlDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICogICBkZWxldGUgcmVxdWVzdCBkYXRhDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIG1lZGlhSW5mbygkbWVkaWFDb2RlKQ0KICAgIHsNCiAgICAgICAgJGhlYWRlcnMgPSBhcnJheSgpOw0KICAgICAgICAkaGVhZGVyc1tdID0gIlJlZmVyZXI6IGh0dHBzOi8vd3d3Lmluc3RhZ3JhbS5jb20vIjsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJETlQ6IDEiOw0KICAgICAgICAkaGVhZGVyc1tdID0gIk9yaWdpbjogaHR0cHM6Ly93d3cuaW5zdGFncmFtLmNvbS8iOw0KICAgICAgICAkaGVhZGVyc1tdID0gIlgtQ1NSRlRva2VuOiAiIC4gdHJpbSgkdGhpcy0+dG9rZW4pOw0KICAgICAgICAkaGVhZGVyc1tdID0gIlgtUmVxdWVzdGVkLVdpdGg6IFhNTEh0dHBSZXF1ZXN0IjsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJYLUluc3RhZ3JhbS1BSkFYOiAxIjsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJDb25uZWN0aW9uOiBjbG9zZSI7DQogICAgICAgICRoZWFkZXJzW10gPSAiQ2FjaGUtQ29udHJvbDogbWF4LWFnZT0wIjsNCiAgICAgICAgcmV0dXJuICR0aGlzLT5yZXF1ZXN0KCJwLyIgLiAkbWVkaWFDb2RlIC4gIi8/X19hPTEiLCAkaGVhZGVycylbMV07DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIExpa2UgcGhvdG8gb3IgdmlkZW8uDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJG1lZGlhSWQNCiAgICAgICAgICogICBNZWRpYSBpZA0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgc3RhdHVzIHJlcXVlc3QNCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gbGlrZSgkbWVkaWFJZCkNCiAgICB7DQogICAgICAgICRoZWFkZXJzID0gYXJyYXkoKTsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJSZWZlcmVyOiBodHRwczovL3d3dy5pbnN0YWdyYW0uY29tL2luc3RhZ3JhbS8iOw0KICAgICAgICAkaGVhZGVyc1tdID0gIkROVDogMSI7DQogICAgICAgICRoZWFkZXJzW10gPSAiT3JpZ2luOiBodHRwczovL3d3dy5pbnN0YWdyYW0uY29tLyI7DQogICAgICAgICRoZWFkZXJzW10gPSAiWC1DU1JGVG9rZW46ICIgLiB0cmltKCR0aGlzLT50b2tlbik7DQogICAgICAgICRoZWFkZXJzW10gPSAiWC1SZXF1ZXN0ZWQtV2l0aDogWE1MSHR0cFJlcXVlc3QiOw0KICAgICAgICAkaGVhZGVyc1tdID0gIlgtSW5zdGFncmFtLUFKQVg6IDEiOw0KICAgICAgICAkaGVhZGVyc1tdID0gIkNvbm5lY3Rpb246IGNsb3NlIjsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJDYWNoZS1Db250cm9sOiBtYXgtYWdlPTAiOw0KICAgICAgICByZXR1cm4gJHRoaXMtPnJlcXVlc3QoIndlYi9saWtlcy8iIC4gJG1lZGlhSWQgLiAiL2xpa2UvIiwgJGhlYWRlcnMsIHRydWUpWzFdOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBVbmxpa2UgcGhvdG8gb3IgdmlkZW8uDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJG1lZGlhSWQNCiAgICAgICAgICogICBNZWRpYSBpZA0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgc3RhdHVzIHJlcXVlc3QNCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gdW5saWtlKCRtZWRpYUlkKQ0KICAgIHsNCiAgICAgICAgJGhlYWRlcnMgPSBhcnJheSgpOw0KICAgICAgICAkaGVhZGVyc1tdID0gIlJlZmVyZXI6IGh0dHBzOi8vd3d3Lmluc3RhZ3JhbS5jb20vIjsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJETlQ6IDEiOw0KICAgICAgICAkaGVhZGVyc1tdID0gIk9yaWdpbjogaHR0cHM6Ly93d3cuaW5zdGFncmFtLmNvbS8iOw0KICAgICAgICAkaGVhZGVyc1tdID0gIlgtQ1NSRlRva2VuOiAiIC4gdHJpbSgkdGhpcy0+dG9rZW4pOw0KICAgICAgICAkaGVhZGVyc1tdID0gIlgtUmVxdWVzdGVkLVdpdGg6IFhNTEh0dHBSZXF1ZXN0IjsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJYLUluc3RhZ3JhbS1BSkFYOiAxIjsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJDb25uZWN0aW9uOiBjbG9zZSI7DQogICAgICAgICRoZWFkZXJzW10gPSAiQ2FjaGUtQ29udHJvbDogbWF4LWFnZT0wIjsNCiAgICAgICAgcmV0dXJuICR0aGlzLT5yZXF1ZXN0KCJ3ZWIvbGlrZXMvIiAuICRtZWRpYUlkIC4gIi91bmxpa2UvIiwgJGhlYWRlcnMsIHRydWUpWzFdOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBGb2xsb3cuDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJHVzZXJJZA0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgRnJpZW5kc2hpcCBzdGF0dXMgZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBmb2xsb3coJHVzZXJJZCkNCiAgICB7DQogICAgICAgICRoZWFkZXJzID0gYXJyYXkoKTsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJSZWZlcmVyOiBodHRwczovL3d3dy5pbnN0YWdyYW0uY29tL2luc3RhZ3JhbS8iOw0KICAgICAgICAkaGVhZGVyc1tdID0gIkROVDogMSI7DQogICAgICAgICRoZWFkZXJzW10gPSAiT3JpZ2luOiBodHRwczovL3d3dy5pbnN0YWdyYW0uY29tLyI7DQogICAgICAgICRoZWFkZXJzW10gPSAiWC1DU1JGVG9rZW46ICIgLiB0cmltKCR0aGlzLT50b2tlbik7DQogICAgICAgICRoZWFkZXJzW10gPSAiWC1SZXF1ZXN0ZWQtV2l0aDogWE1MSHR0cFJlcXVlc3QiOw0KICAgICAgICAkaGVhZGVyc1tdID0gIlgtSW5zdGFncmFtLUFKQVg6IDEiOw0KICAgICAgICAkaGVhZGVyc1tdID0gIkNvbm5lY3Rpb246IGNsb3NlIjsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJDYWNoZS1Db250cm9sOiBtYXgtYWdlPTAiOw0KICAgICAgICByZXR1cm4gJHRoaXMtPnJlcXVlc3QoIndlYi9mcmllbmRzaGlwcy8iIC4gJHVzZXJJZCAuICIvZm9sbG93LyIsICRoZWFkZXJzLCB0cnVlKVsxXTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogVW5mb2xsb3cuDQogICAgICAgICAqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJHVzZXJJZA0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqICAgRnJpZW5kc2hpcCBzdGF0dXMgZGF0YQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiB1bmZvbGxvdygkdXNlcklkKQ0KICAgIHsNCiAgICAgICAgJGhlYWRlcnMgPSBhcnJheSgpOw0KICAgICAgICAkaGVhZGVyc1tdID0gIlJlZmVyZXI6IGh0dHBzOi8vd3d3Lmluc3RhZ3JhbS5jb20vaW5zdGFncmFtLyI7DQogICAgICAgICRoZWFkZXJzW10gPSAiRE5UOiAxIjsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJPcmlnaW46IGh0dHBzOi8vd3d3Lmluc3RhZ3JhbS5jb20vIjsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJYLUNTUkZUb2tlbjogIiAuIHRyaW0oJHRoaXMtPnRva2VuKTsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJYLVJlcXVlc3RlZC1XaXRoOiBYTUxIdHRwUmVxdWVzdCI7DQogICAgICAgICRoZWFkZXJzW10gPSAiWC1JbnN0YWdyYW0tQUpBWDogMSI7DQogICAgICAgICRoZWFkZXJzW10gPSAiQ29ubmVjdGlvbjogY2xvc2UiOw0KICAgICAgICAkaGVhZGVyc1tdID0gIkNhY2hlLUNvbnRyb2w6IG1heC1hZ2U9MCI7DQogICAgICAgIHJldHVybiAkdGhpcy0+cmVxdWVzdCgid2ViL2ZyaWVuZHNoaXBzLyIgLiAkdXNlcklkIC4gIi91bmZvbGxvdy8iLCAkaGVhZGVycywgdHJ1ZSlbMV07DQogICAgfQ0KDQogICAgcHJvdGVjdGVkIGZ1bmN0aW9uIHJlcXVlc3QoJGVuZHBvaW50LCBhcnJheSAkb3B0aW9uYWxoZWFkZXJzLCAkcG9zdCA9IE5VTEwpDQogICAgew0KICAgICAgICBpZiggISR0aGlzLT5pc0xvZ2dlZEluICkgDQogICAgICAgIHsNCiAgICAgICAgICAgIHRocm93IG5ldyBFeGNlcHRpb24oIk5vdCBsb2dnZWQgaW5cbiIpOw0KICAgICAgICB9DQoNCiAgICAgICAgJGhlYWRlcnMgPSBhcnJheSggIkFjY2VwdDogKi8qIiwgIkFjY2VwdC1MYW5ndWFnZTogdHI7cT0xIiwgIlhfRk9SV0FSREVEX0ZPUjogIiAuICR0aGlzLT5mYWtlX2lwLCAiUkVNT1RFX0FERFI6ICIgLiAkdGhpcy0+ZmFrZV9pcCApOw0KICAgICAgICAkaGVhZGVycyA9IGFycmF5X21lcmdlKCRoZWFkZXJzLCAkb3B0aW9uYWxoZWFkZXJzKTsNCiAgICAgICAgJGNoID0gY3VybF9pbml0KCk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9VUkwsIENvbnN0YW50czo6V0VCX1VSTCAuICRlbmRwb2ludCk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9VU0VSQUdFTlQsIENvbnN0YW50czo6V0VCX1VTRVJfQUdFTlQpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfUkVUVVJOVFJBTlNGRVIsIHRydWUpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfRk9MTE9XTE9DQVRJT04sIHRydWUpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfSEVBREVSLCB0cnVlKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX0hUVFBIRUFERVIsICRoZWFkZXJzKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1ZFUkJPU0UsIGZhbHNlKTsNCiAgICAgICAgY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1NTTF9WRVJJRllQRUVSLCBmYWxzZSk7DQogICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9TU0xfVkVSSUZZSE9TVCwgZmFsc2UpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfQ09PS0lFRklMRSwgJHRoaXMtPklHRGF0YVBhdGggLiAoc3RyaW5nKSAkdGhpcy0+dXNlcm5hbWUgLiAiLmRhdCIpOw0KICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfQ09PS0lFSkFSLCAkdGhpcy0+SUdEYXRhUGF0aCAuIChzdHJpbmcpICR0aGlzLT51c2VybmFtZSAuICIuZGF0Iik7DQogICAgICAgIGlmKCAkcG9zdCApIA0KICAgICAgICB7DQogICAgICAgICAgICBjdXJsX3NldG9wdCgkY2gsIENVUkxPUFRfUE9TVCwgdHJ1ZSk7DQogICAgICAgICAgICBpZiggaXNfc3RyaW5nKCRwb3N0KSApIA0KICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgIGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9QT1NURklFTERTLCAkcG9zdCk7DQogICAgICAgICAgICB9DQoNCiAgICAgICAgfQ0KDQogICAgICAgICRyZXNwID0gY3VybF9leGVjKCRjaCk7DQogICAgICAgICRoZWFkZXJfbGVuID0gY3VybF9nZXRpbmZvKCRjaCwgQ1VSTElORk9fSEVBREVSX1NJWkUpOw0KICAgICAgICAkaGVhZGVyID0gc3Vic3RyKCRyZXNwLCAwLCAkaGVhZGVyX2xlbik7DQogICAgICAgICRib2R5ID0gc3Vic3RyKCRyZXNwLCAkaGVhZGVyX2xlbik7DQogICAgICAgIGN1cmxfY2xvc2UoJGNoKTsNCiAgICAgICAgcmV0dXJuIGFycmF5KCAkaGVhZGVyLCBqc29uX2RlY29kZSgkYm9keSwgdHJ1ZSkgKTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogSXMgVXNlciBMb2dnZWQgaW4/DQogICAgICAgICAqIEByZXR1cm4gYm9vbA0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBpc0xvZ2dlZEluKCkNCiAgICB7DQogICAgICAgIHJldHVybiAkdGhpcy0+aXNMb2dnZWRJbjsNCiAgICB9DQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gaXNWYWxpZCgpDQogICAgew0KICAgICAgICAkaGVhZGVycyA9IGFycmF5KCk7DQogICAgICAgICRoZWFkZXJzW10gPSAiUmVmZXJlcjogaHR0cHM6Ly93d3cuaW5zdGFncmFtLmNvbS9hY2NvdW50cy9lZGl0LyI7DQogICAgICAgICRoZWFkZXJzW10gPSAiRE5UOiAxIjsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJPcmlnaW46IGh0dHBzOi8vd3d3Lmluc3RhZ3JhbS5jb20vIjsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJYLUNTUkZUb2tlbjogIiAuIHRyaW0oJHRoaXMtPnRva2VuKTsNCiAgICAgICAgJGhlYWRlcnNbXSA9ICJYLVJlcXVlc3RlZC1XaXRoOiBYTUxIdHRwUmVxdWVzdCI7DQogICAgICAgICRoZWFkZXJzW10gPSAiWC1JbnN0YWdyYW0tQUpBWDogMSI7DQogICAgICAgICRoZWFkZXJzW10gPSAiQ29ubmVjdGlvbjogY2xvc2UiOw0KICAgICAgICAkaGVhZGVyc1tdID0gIkNhY2hlLUNvbnRyb2w6IG1heC1hZ2U9MCI7DQogICAgICAgIGxpc3QoJGhlYWRlcikgPSAkdGhpcy0+cmVxdWVzdCgiYWNjb3VudHMvYWN0aXZpdHkvP19fYT0xIiwgJGhlYWRlcnMsIHRydWUpOw0KICAgICAgICByZXR1cm4gKHN0cnBvcygkaGVhZGVyLCAiSFRUUC8xLjEgMjAwIikgPT09IGZhbHNlID8gZmFsc2UgOiB0cnVlKTsNCiAgICB9DQoNCn0NCg0KDQpjbGFzcyBTaWduYXR1cmVVdGlscw0Kew0KICAgIHB1YmxpYyBzdGF0aWMgZnVuY3Rpb24gZ2VuZXJhdGVTaWduYXR1cmUoJGRhdGEpDQogICAgew0KICAgICAgICAkaGFzaCA9IGhhc2hfaG1hYygic2hhMjU2IiwgJGRhdGEsIENvbnN0YW50czo6SUdfU0lHX0tFWSk7DQogICAgICAgIHJldHVybiAiaWdfc2lnX2tleV92ZXJzaW9uPSIgLiBDb25zdGFudHM6OlNJR19LRVlfVkVSU0lPTiAuICImc2lnbmVkX2JvZHk9IiAuICRoYXNoIC4gIi4iIC4gdXJsZW5jb2RlKCRkYXRhKTsNCiAgICB9DQoNCiAgICBwdWJsaWMgc3RhdGljIGZ1bmN0aW9uIGdlbmVyYXRlRGV2aWNlSWQoJHNlZWQpDQogICAgew0KICAgICAgICAkdm9sYXRpbGVfc2VlZCA9IGZpbGVtdGltZShfX0RJUl9fKTsNCiAgICAgICAgcmV0dXJuICJhbmRyb2lkLSIgLiBzdWJzdHIobWQ1KCRzZWVkIC4gJHZvbGF0aWxlX3NlZWQpLCAxNik7DQogICAgfQ0KDQogICAgcHVibGljIHN0YXRpYyBmdW5jdGlvbiBnZW5lcmF0ZVVVSUQoJHR5cGUpDQogICAgew0KICAgICAgICAkdXVpZCA9IHNwcmludGYoIiUwNHglMDR4LSUwNHgtJTA0eC0lMDR4LSUwNHglMDR4JTA0eCIsIG10X3JhbmQoMCwgNjU1MzUpLCBtdF9yYW5kKDAsIDY1NTM1KSwgbXRfcmFuZCgwLCA2NTUzNSksIG10X3JhbmQoMCwgNDA5NSkgfCAxNjM4NCwgbXRfcmFuZCgwLCAxNjM4MykgfCAzMjc2OCwgbXRfcmFuZCgwLCA2NTUzNSksIG10X3JhbmQoMCwgNjU1MzUpLCBtdF9yYW5kKDAsIDY1NTM1KSk7DQogICAgICAgIHJldHVybiAoJHR5cGUgPyAkdXVpZCA6IHN0cl9yZXBsYWNlKCItIiwgIiIsICR1dWlkKSk7DQogICAgfQ0KDQp9DQoNCg0KY2xhc3MgVXNlckFnZW50DQp7DQogICAgcHJvdGVjdGVkICRwYXJlbnQgPSBOVUxMOw0KDQogICAgcHVibGljIGZ1bmN0aW9uIF9fY29uc3RydWN0KCRwYXJlbnQpDQogICAgew0KICAgICAgICAkdGhpcy0+cGFyZW50ID0gJHBhcmVudDsNCiAgICB9DQoNCiAgICBwcm90ZWN0ZWQgZnVuY3Rpb24gZ2V0RGV2aWNlRGF0YSgpDQogICAgew0KICAgICAgICAkY3N2ZmlsZSA9IFdvdzo6Z2V0KCJwcm9qZWN0L2Nvb2tpZVBhdGgiKSAuICJkZXZpY2UvZGV2aWNlcy5jc3YiOw0KICAgICAgICAkZmlsZV9oYW5kbGUgPSBmb3BlbigkY3N2ZmlsZSwgInIiKTsNCiAgICAgICAgJGxpbmVfb2ZfdGV4dCA9IGFycmF5KCk7DQogICAgICAgIHdoaWxlKCAhZmVvZigkZmlsZV9oYW5kbGUpICkgDQogICAgICAgIHsNCiAgICAgICAgICAgICRsaW5lX29mX3RleHRbXSA9IGZnZXRjc3YoJGZpbGVfaGFuZGxlLCAxMDI0KTsNCiAgICAgICAgfQ0KICAgICAgICAkZGV2aWNlRGF0YSA9IGV4cGxvZGUoIjsiLCAkbGluZV9vZl90ZXh0W210X3JhbmQoMCwgMTE4NjcpXVswXSk7DQogICAgICAgIGZjbG9zZSgkZmlsZV9oYW5kbGUpOw0KICAgICAgICByZXR1cm4gJGRldmljZURhdGE7DQogICAgfQ0KDQogICAgcHVibGljIGZ1bmN0aW9uIGJ1aWxkVXNlckFnZW50KCkNCiAgICB7DQogICAgICAgICRkZXZpY2VEYXRhID0gJHRoaXMtPmdldERldmljZURhdGEoKTsNCiAgICAgICAgJHRoaXMtPnBhcmVudC0+c2V0dGluZ3MtPnNldCgibWFudWZhY3R1cmVyIiwgJGRldmljZURhdGFbMF0pOw0KICAgICAgICAkdGhpcy0+cGFyZW50LT5zZXR0aW5ncy0+c2V0KCJkZXZpY2UiLCAkZGV2aWNlRGF0YVsxXSk7DQogICAgICAgICR0aGlzLT5wYXJlbnQtPnNldHRpbmdzLT5zZXQoIm1vZGVsIiwgJGRldmljZURhdGFbMl0pOw0KICAgICAgICAkbG9jYWxlTGFuZyA9ICR0aGlzLT5kZXRlY3RVc2VyTG9jYWxlKCk7DQogICAgICAgIHJldHVybiBzcHJpbnRmKCJJbnN0YWdyYW0gJXMgQW5kcm9pZCAoMTgvNC4zOyAzMjBkcGk7IDcyMHgxMjgwOyAlczsgJXM7ICVzOyBxY29tOyAiIC4gJGxvY2FsZUxhbmcgLiAiKSIsIENvbnN0YW50czo6VkVSU0lPTiwgJGRldmljZURhdGFbMF0sICRkZXZpY2VEYXRhWzFdLCAkZGV2aWNlRGF0YVsyXSk7DQogICAgfQ0KDQogICAgcHJpdmF0ZSBmdW5jdGlvbiBkZXRlY3RVc2VyTG9jYWxlKCRmYWxsYmFja0xvY2FsZSA9ICJlbl9VUyIpDQogICAgew0KICAgICAgICAkdXNlckxvY2FsZSA9IE5VTEw7DQogICAgICAgIGlmKCBpc3NldCgkX1NFUlZFUlsiSFRUUF9BQ0NFUFRfTEFOR1VBR0UiXSkgKSANCiAgICAgICAgew0KICAgICAgICAgICAgJGFyckxhbmcgPSBleHBsb2RlKCIsIiwgJF9TRVJWRVJbIkhUVFBfQUNDRVBUX0xBTkdVQUdFIl0pOw0KICAgICAgICAgICAgJHByZWZlcnJlZExhbmdzID0gYXJyYXkoKTsNCiAgICAgICAgICAgIGZvcmVhY2goICRhcnJMYW5nIGFzICRzdHJMYW5nICkgDQogICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgJGFyckxhbmdEZXRhaWxzID0gZXhwbG9kZSgiO3E9IiwgJHN0ckxhbmcpOw0KICAgICAgICAgICAgICAgICRsYW5nQ29kZSA9IHN0cl9yZXBsYWNlKCItIiwgIl8iLCAkYXJyTGFuZ0RldGFpbHNbMF0pOw0KICAgICAgICAgICAgICAgICRsYW5nUHJpb3JpdHkgPSBmbG9hdHZhbCgoaXNzZXQoJGFyckxhbmdEZXRhaWxzWzFdKSA/ICRhcnJMYW5nRGV0YWlsc1sxXSA6IDEpKTsNCiAgICAgICAgICAgICAgICAkcHJlZmVycmVkTGFuZ3NbJGxhbmdDb2RlXSA9ICRsYW5nUHJpb3JpdHk7DQogICAgICAgICAgICB9DQogICAgICAgICAgICBhcnNvcnQoJHByZWZlcnJlZExhbmdzKTsNCiAgICAgICAgICAgIHJlc2V0KCRwcmVmZXJyZWRMYW5ncyk7DQogICAgICAgICAgICAkdXNlckxvY2FsZSA9IGtleSgkcHJlZmVycmVkTGFuZ3MpOw0KICAgICAgICB9DQoNCiAgICAgICAgcmV0dXJuIChpc19udWxsKCR1c2VyTG9jYWxlKSA/ICRmYWxsYmFja0xvY2FsZSA6ICR1c2VyTG9jYWxlKTsNCiAgICB9DQoNCn0NCg0KDQpjbGFzcyBVdGlscw0Kew0KICAgIC8qKg0KICAgICAgICAgKiBMZW5ndGggb2YgdGhlIGZpbGUgaW4gU2Vjb25kcy4NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkZmlsZQ0KICAgICAgICAgKiAgICAgICAgICAgICAgICAgICAgIHBhdGggdG8gdGhlIGZpbGUgbmFtZQ0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGludA0KICAgICAgICAgKiAgICAgICAgICAgICBsZW5ndGggb2YgdGhlIGZpbGUgaW4gc2Vjb25kcw0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBzdGF0aWMgZnVuY3Rpb24gZ2V0U2Vjb25kcygkZmlsZSkNCiAgICB7DQogICAgICAgICRmZm1wZWcgPSBzZWxmOjpjaGVja0ZGTVBFRygpOw0KICAgICAgICBpZiggJGZmbXBlZyApIA0KICAgICAgICB7DQogICAgICAgICAgICAkdGltZSA9IGV4ZWMoKHN0cmluZykgJGZmbXBlZyAuICIgLWkgIiAuICRmaWxlIC4gIiAyPiYxIHwgZ3JlcCAnRHVyYXRpb24nIHwgY3V0IC1kICcgJyAtZiA0Iik7DQogICAgICAgICAgICAkZHVyYXRpb24gPSBleHBsb2RlKCI6IiwgJHRpbWUpOw0KICAgICAgICAgICAgJHNlY29uZHMgPSAkZHVyYXRpb25bMF0gKiAzNjAwICsgJGR1cmF0aW9uWzFdICogNjAgKyByb3VuZCgkZHVyYXRpb25bMl0pOw0KICAgICAgICAgICAgcmV0dXJuICRzZWNvbmRzOw0KICAgICAgICB9DQoNCiAgICAgICAgcmV0dXJuIG10X3JhbmQoMTUsIDMwMCk7DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIENoZWNrIGZvciBmZm1wZWcvYXZjb252IGRlcGVuZGVuY2llcy4NCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBzdHJpbmcvYm9vbGVhbg0KICAgICAgICAgKiAgICAgICAgICAgICAgICAgICAgICAgIG5hbWUgb2YgdGhlIGxpYnJhcnkgaWYgcHJlc2VudCwgZmFsc2Ugb3RoZXJ3aXNlDQogICAgICAgICAqLw0KDQogICAgcHVibGljIHN0YXRpYyBmdW5jdGlvbiBjaGVja0ZGTVBFRygpDQogICAgew0KICAgICAgICBAZXhlYygiZmZtcGVnIC12ZXJzaW9uIDI+JjEiLCAkb3V0cHV0LCAkcmV0dXJudmFsdWUpOw0KICAgICAgICBpZiggJHJldHVybnZhbHVlID09PSAwICkgDQogICAgICAgIHsNCiAgICAgICAgICAgIHJldHVybiAiZmZtcGVnIjsNCiAgICAgICAgfQ0KDQogICAgICAgIEBleGVjKCJhdmNvbnYgLXZlcnNpb24gMj4mMSIsICRvdXRwdXQsICRyZXR1cm52YWx1ZSk7DQogICAgICAgIGlmKCAkcmV0dXJudmFsdWUgPT09IDAgKSANCiAgICAgICAgew0KICAgICAgICAgICAgcmV0dXJuICJhdmNvbnYiOw0KICAgICAgICB9DQoNCiAgICAgICAgcmV0dXJuIGZhbHNlOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBDcmVhdGluZyBhIHZpZGVvIGljb24vdGh1bWJuYWlsLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICRmaWxlDQogICAgICAgICAqICAgICAgICAgICAgICAgICAgICAgcGF0aCB0byB0aGUgdmlkZW8gZmlsZQ0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGltYWdlDQogICAgICAgICAqICAgICAgICAgICAgICAgaWNvbi90aHVtYm5haWwgZm9yIHRoZSB2aWRlbw0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBzdGF0aWMgZnVuY3Rpb24gY3JlYXRlVmlkZW9JY29uKCRmaWxlKQ0KICAgIHsNCiAgICAgICAgJGZmbXBlZyA9IHNlbGY6OmNoZWNrRkZNUEVHKCk7DQogICAgICAgIGlmKCAkZmZtcGVnICkgDQogICAgICAgIHsNCiAgICAgICAgICAgICRwcmV2aWV3ID0gc3lzX2dldF90ZW1wX2RpcigpIC4gIi8iIC4gbWQ1KCRmaWxlKSAuICIuanBnIjsNCiAgICAgICAgICAgIEB1bmxpbmsoJHByZXZpZXcpOw0KICAgICAgICAgICAgJGNvbW1hbmQgPSAkZmZtcGVnIC4gIiAtaSBcIiIgLiAkZmlsZSAuICJcIiAtZiBtanBlZyAtc3MgMDA6MDA6MDEgLXZmcmFtZXMgMSBcIiIgLiAkcHJldmlldyAuICJcIiAyPiYxIjsNCiAgICAgICAgICAgIEBleGVjKCRjb21tYW5kKTsNCiAgICAgICAgICAgIHJldHVybiBmaWxlX2dldF9jb250ZW50cygkcHJldmlldyk7DQogICAgICAgIH0NCg0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBJbXBsZW1lbnRzIHRoZSBhY3R1YWwgbG9naWMgYmVoaW5kIGNyZWF0aW5nIHRoZSBpY29uL3RodW1ibmFpbC4NCiAgICAgICAgICoNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkZmlsZQ0KICAgICAgICAgKiAgICAgICAgICAgICAgICAgICAgIHBhdGggdG8gdGhlIGZpbGUgbmFtZQ0KICAgICAgICAgKg0KICAgICAgICAgKiBAcmV0dXJuIGltYWdlDQogICAgICAgICAqICAgICAgICAgICAgICAgaWNvbi90aHVtYm5haWwgZm9yIHRoZSB2aWRlbw0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBzdGF0aWMgZnVuY3Rpb24gY3JlYXRlSWNvbkdEKCRmaWxlLCAkc2l6ZSA9IDEwMCwgJHJhdyA9IHRydWUpDQogICAgew0KICAgICAgICBsaXN0KCR3aWR0aCwgJGhlaWdodCkgPSBnZXRpbWFnZXNpemUoJGZpbGUpOw0KICAgICAgICBpZiggJGhlaWdodCA8ICR3aWR0aCApIA0KICAgICAgICB7DQogICAgICAgICAgICAkeSA9IDA7DQogICAgICAgICAgICAkeCA9ICgkd2lkdGggLSAkaGVpZ2h0KSAvIDI7DQogICAgICAgICAgICAkc21hbGxlc3RTaWRlID0gJGhlaWdodDsNCiAgICAgICAgfQ0KICAgICAgICBlbHNlDQogICAgICAgIHsNCiAgICAgICAgICAgICR4ID0gMDsNCiAgICAgICAgICAgICR5ID0gKCRoZWlnaHQgLSAkd2lkdGgpIC8gMjsNCiAgICAgICAgICAgICRzbWFsbGVzdFNpZGUgPSAkd2lkdGg7DQogICAgICAgIH0NCg0KICAgICAgICAkaW1hZ2VfcCA9IGltYWdlY3JlYXRldHJ1ZWNvbG9yKCRzaXplLCAkc2l6ZSk7DQogICAgICAgICRpbWFnZSA9IGltYWdlY3JlYXRlZnJvbXN0cmluZyhmaWxlX2dldF9jb250ZW50cygkZmlsZSkpOw0KICAgICAgICBpbWFnZWNvcHlyZXNhbXBsZWQoJGltYWdlX3AsICRpbWFnZSwgMCwgMCwgJHgsICR5LCAkc2l6ZSwgJHNpemUsICRzbWFsbGVzdFNpZGUsICRzbWFsbGVzdFNpZGUpOw0KICAgICAgICBvYl9zdGFydCgpOw0KICAgICAgICBpbWFnZWpwZWcoJGltYWdlX3AsIE5VTEwsIDk1KTsNCiAgICAgICAgJGkgPSBvYl9nZXRfY29udGVudHMoKTsNCiAgICAgICAgb2JfZW5kX2NsZWFuKCk7DQogICAgICAgIGltYWdlZGVzdHJveSgkaW1hZ2UpOw0KICAgICAgICBpbWFnZWRlc3Ryb3koJGltYWdlX3ApOw0KICAgICAgICByZXR1cm4gJGk7DQogICAgfQ0KDQogICAgcHVibGljIHN0YXRpYyBmdW5jdGlvbiBmb3JtYXRCeXRlcygkYnl0ZXMsICRwcmVjaXNpb24gPSAyKQ0KICAgIHsNCiAgICAgICAgJHVuaXRzID0gYXJyYXkoICJCIiwgImtCIiwgIm1CIiwgImdCIiwgInRCIiApOw0KICAgICAgICAkYnl0ZXMgPSBtYXgoJGJ5dGVzLCAwKTsNCiAgICAgICAgJHBvdyA9IGZsb29yKCgoJGJ5dGVzID8gbG9nKCRieXRlcykgOiAwKSkgLyBsb2coMTAyNCkpOw0KICAgICAgICAkcG93ID0gbWluKCRwb3csIGNvdW50KCR1bml0cykgLSAxKTsNCiAgICAgICAgJGJ5dGVzIC89IHBvdygxMDI0LCAkcG93KTsNCiAgICAgICAgcmV0dXJuIHJvdW5kKCRieXRlcywgJHByZWNpc2lvbikgLiAiIiAuICR1bml0c1skcG93XTsNCiAgICB9DQoNCiAgICBwdWJsaWMgc3RhdGljIGZ1bmN0aW9uIGNvbG91cmVkU3RyaW5nKCRzdHJpbmcsICRjb2xvdXIpDQogICAgew0KICAgICAgICAkY29sb3Vyc1siYmxhY2siXSA9ICIwOzMwIjsNCiAgICAgICAgJGNvbG91cnNbImRhcmtfZ3JheSJdID0gIjE7MzAiOw0KICAgICAgICAkY29sb3Vyc1siYmx1ZSJdID0gIjA7MzQiOw0KICAgICAgICAkY29sb3Vyc1sibGlnaHRfYmx1ZSJdID0gIjE7MzQiOw0KICAgICAgICAkY29sb3Vyc1siZ3JlZW4iXSA9ICIwOzMyIjsNCiAgICAgICAgJGNvbG91cnNbImxpZ2h0X2dyZWVuIl0gPSAiMTszMiI7DQogICAgICAgICRjb2xvdXJzWyJjeWFuIl0gPSAiMDszNiI7DQogICAgICAgICRjb2xvdXJzWyJsaWdodF9jeWFuIl0gPSAiMTszNiI7DQogICAgICAgICRjb2xvdXJzWyJyZWQiXSA9ICIwOzMxIjsNCiAgICAgICAgJGNvbG91cnNbImxpZ2h0X3JlZCJdID0gIjE7MzEiOw0KICAgICAgICAkY29sb3Vyc1sicHVycGxlIl0gPSAiMDszNSI7DQogICAgICAgICRjb2xvdXJzWyJsaWdodF9wdXJwbGUiXSA9ICIxOzM1IjsNCiAgICAgICAgJGNvbG91cnNbImJyb3duIl0gPSAiMDszMyI7DQogICAgICAgICRjb2xvdXJzWyJ5ZWxsb3ciXSA9ICIxOzMzIjsNCiAgICAgICAgJGNvbG91cnNbImxpZ2h0X2dyYXkiXSA9ICIwOzM3IjsNCiAgICAgICAgJGNvbG91cnNbIndoaXRlIl0gPSAiMTszNyI7DQogICAgICAgICRjb2xvcmVkX3N0cmluZyA9ICIiOw0KICAgICAgICBpZiggaXNzZXQoJGNvbG91cnNbJGNvbG91cl0pICkgDQogICAgICAgIHsNCiAgICAgICAgICAgICRjb2xvcmVkX3N0cmluZyAuPSAiXHgxQlsiIC4gJGNvbG91cnNbJGNvbG91cl0gLiAibSI7DQogICAgICAgIH0NCg0KICAgICAgICAkY29sb3JlZF9zdHJpbmcgLj0gJHN0cmluZyAuICJceDFCWzBtIjsNCiAgICAgICAgcmV0dXJuICRjb2xvcmVkX3N0cmluZzsNCiAgICB9DQoNCiAgICBwdWJsaWMgc3RhdGljIGZ1bmN0aW9uIGdldEZpbHRlckNvZGUoJGZpbHRlcikNCiAgICB7DQogICAgICAgICRmaWx0ZXJzID0gYXJyYXkoKTsNCiAgICAgICAgJGZpbHRlcnNbMTA4XSA9ICJDaGFybWVzIjsNCiAgICAgICAgJGZpbHRlcnNbMTE2XSA9ICJBc2hieSI7DQogICAgICAgICRmaWx0ZXJzWzExN10gPSAiSGVsZW5hIjsNCiAgICAgICAgJGZpbHRlcnNbMTE1XSA9ICJCcm9va2x5biI7DQogICAgICAgICRmaWx0ZXJzWzEwNV0gPSAiRG9ncGF0Y2giOw0KICAgICAgICAkZmlsdGVyc1sxMTNdID0gIlNreWxpbmUiOw0KICAgICAgICAkZmlsdGVyc1sxMDddID0gIkdpbnphIjsNCiAgICAgICAgJGZpbHRlcnNbMTE4XSA9ICJNYXZlbiI7DQogICAgICAgICRmaWx0ZXJzWzE2XSA9ICJLZWx2aW4iOw0KICAgICAgICAkZmlsdGVyc1sxNF0gPSAiMTk3NyI7DQogICAgICAgICRmaWx0ZXJzWzIwXSA9ICJXYWxkZW4iOw0KICAgICAgICAkZmlsdGVyc1sxOV0gPSAiVG9hc3RlciI7DQogICAgICAgICRmaWx0ZXJzWzE4XSA9ICJTdXRybyI7DQogICAgICAgICRmaWx0ZXJzWzIyXSA9ICJCcmFubmFuIjsNCiAgICAgICAgJGZpbHRlcnNbM10gPSAiRWFybHliaXJkIjsNCiAgICAgICAgJGZpbHRlcnNbMTA2XSA9ICJWZXNwZXIiOw0KICAgICAgICAkZmlsdGVyc1sxMDldID0gIlN0aW5zb24iOw0KICAgICAgICAkZmlsdGVyc1sxNV0gPSAiTmFzaHZpbGxlIjsNCiAgICAgICAgJGZpbHRlcnNbMjFdID0gIkhlZmUiOw0KICAgICAgICAkZmlsdGVyc1sxMF0gPSAiSW5rd2VsbCI7DQogICAgICAgICRmaWx0ZXJzWzJdID0gIkxvLUZpIjsNCiAgICAgICAgJGZpbHRlcnNbMjhdID0gIldpbGxvdyI7DQogICAgICAgICRmaWx0ZXJzWzI3XSA9ICJTaWVycmEiOw0KICAgICAgICAkZmlsdGVyc1sxXSA9ICJYIFBybyBJSSI7DQogICAgICAgICRmaWx0ZXJzWzI1XSA9ICJWYWxlbmNpYSI7DQogICAgICAgICRmaWx0ZXJzWzI2XSA9ICJIdWRzb24iOw0KICAgICAgICAkZmlsdGVyc1syM10gPSAiUmlzZSI7DQogICAgICAgICRmaWx0ZXJzWzE3XSA9ICJNYXlmYWlyIjsNCiAgICAgICAgJGZpbHRlcnNbMjRdID0gIkFtYXJvIjsNCiAgICAgICAgJGZpbHRlcnNbNjA4XSA9ICJQZXJwZXR1YSI7DQogICAgICAgICRmaWx0ZXJzWzYxMl0gPSAiQWRlbiI7DQogICAgICAgICRmaWx0ZXJzWzYwM10gPSAiTHVkd2lnIjsNCiAgICAgICAgJGZpbHRlcnNbNjE2XSA9ICJDcmVtYSI7DQogICAgICAgICRmaWx0ZXJzWzYwNV0gPSAiU2x1bWJlciI7DQogICAgICAgICRmaWx0ZXJzWzYxM10gPSAiSnVubyI7DQogICAgICAgICRmaWx0ZXJzWzYxNF0gPSAiUmV5ZXMiOw0KICAgICAgICAkZmlsdGVyc1s2MTVdID0gIkxhcmsiOw0KICAgICAgICAkZmlsdGVyc1sxMTFdID0gIk1vb24iOw0KICAgICAgICAkZmlsdGVyc1sxMTRdID0gIkdpbmdoYW0iOw0KICAgICAgICAkZmlsdGVyc1sxMTJdID0gIkNsYXJlbmRvbiI7DQogICAgICAgICRmaWx0ZXJzWzBdID0gIk5vcm1hbCI7DQogICAgICAgIHJldHVybiBhcnJheV9zZWFyY2goJGZpbHRlciwgJGZpbHRlcnMpOw0KICAgIH0NCg0KICAgIHB1YmxpYyBzdGF0aWMgZnVuY3Rpb24gY29va2llVG9BcnJheSgkc3RyaW5nKQ0KICAgIHsNCiAgICAgICAgJGFyckNvb2tpZXMgPSBhcnJheSgpOw0KICAgICAgICAkZmlsZVZhbHMgPSBzZWxmOjpleHRyYWN0Q29va2llcygkc3RyaW5nKTsNCiAgICAgICAgZm9yZWFjaCggJGZpbGVWYWxzIGFzICRjb29raWUgKSANCiAgICAgICAgew0KICAgICAgICAgICAgJGFyckNvb2tpZXNbJGNvb2tpZVsibmFtZSJdXSA9ICRjb29raWVbInZhbHVlIl07DQogICAgICAgIH0NCiAgICAgICAgcmV0dXJuICRhcnJDb29raWVzOw0KICAgIH0NCg0KICAgIHB1YmxpYyBzdGF0aWMgZnVuY3Rpb24gZXh0cmFjdENvb2tpZXMoJHN0cmluZykNCiAgICB7DQogICAgICAgICRsaW5lcyA9IGV4cGxvZGUoUEhQX0VPTCwgJHN0cmluZyk7DQogICAgICAgICRjb29raWVzID0gYXJyYXkoKTsNCiAgICAgICAgZm9yZWFjaCggJGxpbmVzIGFzICRsaW5lICkgDQogICAgICAgIHsNCiAgICAgICAgICAgICRjb29raWUgPSBhcnJheSgpOw0KICAgICAgICAgICAgaWYoIHN1YnN0cigkbGluZSwgMCwgMTApID09ICIjSHR0cE9ubHlfIiApIA0KICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICRsaW5lID0gc3Vic3RyKCRsaW5lLCAxMCk7DQogICAgICAgICAgICAgICAgJGNvb2tpZVsiaHR0cG9ubHkiXSA9IHRydWU7DQogICAgICAgICAgICB9DQogICAgICAgICAgICBlbHNlDQogICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgJGNvb2tpZVsiaHR0cG9ubHkiXSA9IGZhbHNlOw0KICAgICAgICAgICAgfQ0KDQogICAgICAgICAgICBpZiggJGxpbmVbMF0gIT0gIiMiICYmIHN1YnN0cl9jb3VudCgkbGluZSwgIlx0IikgPT0gNiApIA0KICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICR0b2tlbnMgPSBleHBsb2RlKCJcdCIsICRsaW5lKTsNCiAgICAgICAgICAgICAgICAkdG9rZW5zID0gYXJyYXlfbWFwKCJ0cmltIiwgJHRva2Vucyk7DQogICAgICAgICAgICAgICAgbGlzdCgkY29va2llWyJkb21haW4iXSwgJGNvb2tpZVsiZmxhZyJdLCAkY29va2llWyJwYXRoIl0sICRjb29raWVbInNlY3VyZSJdLCAkY29va2llWyJleHBpcmF0aW9uLWVwb2NoIl0pID0gJHRva2VuczsNCiAgICAgICAgICAgICAgICAkY29va2llWyJuYW1lIl0gPSB1cmxkZWNvZGUoJHRva2Vuc1s1XSk7DQogICAgICAgICAgICAgICAgJGNvb2tpZVsidmFsdWUiXSA9IHVybGRlY29kZSgkdG9rZW5zWzZdKTsNCiAgICAgICAgICAgICAgICAkY29va2llWyJleHBpcmF0aW9uIl0gPSBkYXRlKCJZLW0tZCBoOmk6cyIsICR0b2tlbnNbNF0pOw0KICAgICAgICAgICAgICAgICRjb29raWVzW10gPSAkY29va2llOw0KICAgICAgICAgICAgfQ0KDQogICAgICAgIH0NCiAgICAgICAgcmV0dXJuICRjb29raWVzOw0KICAgIH0NCg0KfQ0KDQoNCmNsYXNzIFNldHRpbmdzDQp7DQogICAgcHJpdmF0ZSAkcGF0aCA9IE5VTEw7DQogICAgcHJpdmF0ZSAkc2V0cyA9IE5VTEw7DQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gX19jb25zdHJ1Y3QoJHBhdGgpDQogICAgew0KICAgICAgICAkdGhpcy0+cGF0aCA9ICRwYXRoOw0KICAgICAgICAkdGhpcy0+c2V0cyA9IGFycmF5KCk7DQogICAgICAgIGlmKCBmaWxlX2V4aXN0cygkcGF0aCkgKSANCiAgICAgICAgew0KICAgICAgICAgICAgJGZwID0gZm9wZW4oJHBhdGgsICJyYiIpOw0KICAgICAgICAgICAgd2hpbGUoICRsaW5lID0gZmdldHMoJGZwLCAyMDQ4KSApIA0KICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICRsaW5lID0gdHJpbSgkbGluZSwgIiAiKTsNCiAgICAgICAgICAgICAgICBpZiggJGxpbmVbMF0gPT0gIiMiICkgDQogICAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICAgICBjb250aW51ZTsNCiAgICAgICAgICAgICAgICB9DQoNCiAgICAgICAgICAgICAgICAka3YgPSBleHBsb2RlKCI9IiwgJGxpbmUsIDIpOw0KICAgICAgICAgICAgICAgICR0aGlzLT5zZXRzWyRrdlswXV0gPSB0cmltKCRrdlsxXSwgIlxyXG4gIik7DQogICAgICAgICAgICB9DQogICAgICAgICAgICBmY2xvc2UoJGZwKTsNCiAgICAgICAgfQ0KDQogICAgfQ0KDQogICAgcHVibGljIGZ1bmN0aW9uIGdldCgka2V5LCAkZGVmYXVsdCA9IE5VTEwpDQogICAgew0KICAgICAgICBpZiggJGtleSA9PSAic2V0cyIgKSANCiAgICAgICAgew0KICAgICAgICAgICAgcmV0dXJuICR0aGlzLT5zZXRzOw0KICAgICAgICB9DQoNCiAgICAgICAgaWYoIGlzc2V0KCR0aGlzLT5zZXRzWyRrZXldKSApIA0KICAgICAgICB7DQogICAgICAgICAgICByZXR1cm4gJHRoaXMtPnNldHNbJGtleV07DQogICAgICAgIH0NCg0KICAgICAgICByZXR1cm4gJGRlZmF1bHQ7DQogICAgfQ0KDQogICAgcHVibGljIGZ1bmN0aW9uIHNldCgka2V5LCAkdmFsdWUpDQogICAgew0KICAgICAgICBpZiggJGtleSA9PSAic2V0cyIgKSANCiAgICAgICAgew0KICAgICAgICAgICAgcmV0dXJuIE5VTEw7DQogICAgICAgIH0NCg0KICAgICAgICAkdGhpcy0+c2V0c1ska2V5XSA9ICR2YWx1ZTsNCiAgICAgICAgJHRoaXMtPlNhdmUoKTsNCiAgICB9DQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gU2F2ZSgpDQogICAgew0KICAgICAgICBpZiggZmlsZV9leGlzdHMoJHRoaXMtPnBhdGgpICkgDQogICAgICAgIHsNCiAgICAgICAgICAgIHVubGluaygkdGhpcy0+cGF0aCk7DQogICAgICAgIH0NCg0KICAgICAgICAkZnAgPSBmb3BlbigkdGhpcy0+cGF0aCwgIndiIik7DQogICAgICAgIGZzZWVrKCRmcCwgMCk7DQogICAgICAgIGZvcmVhY2goICR0aGlzLT5zZXRzIGFzICRrZXkgPT4gJHZhbHVlICkgDQogICAgICAgIHsNCiAgICAgICAgICAgIGZ3cml0ZSgkZnAsICRrZXkgLiAiPSIgLiAkdmFsdWUgLiAiXG4iKTsNCiAgICAgICAgfQ0KICAgICAgICBmY2xvc2UoJGZwKTsNCiAgICB9DQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gX19zZXQoJHByb3AsICR2YWx1ZSkNCiAgICB7DQogICAgICAgICR0aGlzLT5zZXQoJHByb3AsICR2YWx1ZSk7DQogICAgfQ0KDQogICAgcHVibGljIGZ1bmN0aW9uIF9fZ2V0KCRwcm9wKQ0KICAgIHsNCiAgICAgICAgcmV0dXJuICR0aGlzLT5nZXQoJHByb3ApOw0KICAgIH0NCg0KfQ0KDQoNCmNsYXNzIENvbnN0YW50cw0Kew0KICAgIGNvbnN0IEFQSV9VUkwgPSAiaHR0cHM6Ly9pLmluc3RhZ3JhbS5jb20vYXBpL3YxLyI7DQogICAgY29uc3QgVkVSU0lPTiA9ICI5LjQuMCI7DQogICAgY29uc3QgSUdfU0lHX0tFWSA9ICJmYzQ3MjBlMWJmOWQ3OTQ2M2Y2MjYwOGM4NmZiZGRkMzc0Y2M3MWJiZmI5ODIxNmI1MmUzZjc1MzMzYmQxMzBkIjsNCiAgICBjb25zdCBFWFBFUklNRU5UUyA9ICJpZ19hbmRyb2lkX2FkX2hvbGRvdXRfMTZtNV91bml2ZXJzZSxpZ19hbmRyb2lkX3Byb2dyZXNzaXZlX2pwZWcsaWdfY3JlYXRpb25fZ3Jvd3RoX2hvbGRvdXQsaWdfYW5kcm9pZF9vcHBvX2FwcF9iYWRnaW5nLGlnX2FuZHJvaWRfYWRfcmVtb3ZlX3VzZXJuYW1lX2Zyb21fY2FwdGlvbl91bml2ZXJzZSxpZ19hbmRyb2lkX2VuYWJsZV9zaGFyZV90b193aGF0c2FwcCxpZ19hbmRyb2lkX2RpcmVjdF9kcmF3aW5nX2luX3F1aWNrX2NhbV91bml2ZXJzZSxpZ19hbmRyb2lkX2FkX2Fsd2F5c19zZW5kX2FkX2F0dHJpYnV0aW9uX2lkX3VuaXZlcnNlLGlnX2FuZHJvaWRfdW5pdmVyc2VfdmlkZW9fcHJvZHVjdGlvbixpZ19hbmRyb2lkX2RpcmVjdF9wbHVzX2J1dHRvbixpZ19hbmRyb2lkX2Fkc19oZWF0bWFwX292ZXJsYXlfdW5pdmVyc2UsaWdfYW5kcm9pZF9odHRwX3N0YWNrX2V4cGVyaW1lbnRfMjAxNixpZ19hbmRyb2lkX2luZmluaXRlX3Njcm9sbGluZyxpZ19mYm5zX2Jsb2NrZWQsaWdfYW5kcm9pZF9wb3N0X2F1dG9fcmV0cnlfdjdfMjEsaWdfZmJuc19wdXNoLGlnX2FuZHJvaWRfdmlkZW9fcGxheWJhY2tfYmFuZHdpZHRoX3RocmVzaG9sZCxpZ19hbmRyb2lkX2RpcmVjdF9saW5rX3ByZXZpZXcsaWdfYW5kcm9pZF9kaXJlY3RfdHlwaW5nX2luZGljYXRvcixpZ19hbmRyb2lkX3ByZXZpZXdfY2FwdHVyZSxpZ19hbmRyb2lkX2ZlZWRfcGlsbCxpZ19hbmRyb2lkX3Byb2ZpbGVfbGlua19pYWIsaWdfYW5kcm9pZF9zdG9yeV9jYXB0aW9uLGlnX2FuZHJvaWRfbmV0d29ya19jYW5jZWxsYXRpb24saWdfYW5kcm9pZF9oaXN0b2dyYW1fcmVwb3J0ZXIsaWdfYW5kcm9pZF9hbnJ3YXRjaGRvZyxpZ19hbmRyb2lkX3NlYXJjaF9jbGllbnRfbWF0Y2hpbmcsaWdfYW5kcm9pZF9mb2xsb3dfcmVxdWVzdF90ZXh0X2J1dHRvbnMsaWdfYW5kcm9pZF9mZWVkX3pvb20saWdfYW5kcm9pZF9kcmFmdHNfdW5pdmVyc2UsaWdfYW5kcm9pZF9kaXNhYmxlX2NvbW1lbnQsaWdfYW5kcm9pZF91c2VyX2RldGFpbF9lbmRwb2ludCxpZ19hbmRyb2lkX29zX3ZlcnNpb25fYmxvY2tpbmcsaWdfYW5kcm9pZF9ibG9ja2VkX2xpc3QsaWdfYW5kcm9pZF9ldmVudF9jcmVhdGlvbixpZ19hbmRyb2lkX2hpZ2hfcmVzX3VwbG9hZF8yLGlnX2FuZHJvaWRfMmZhYyxpZ19hbmRyb2lkX21hcmtfcmVlbF9zZWVuX29uX1N3aXBlX2ZvcndhcmQsaWdfYW5kcm9pZF9jb21tZW50X3JlZGVzaWduLGlnX2FuZHJvaWRfYWRfc3BvbnNvcmVkX2xhYmVsX3VuaXZlcnNlLGlnX2FuZHJvaWRfbWVudGlvbnNfZGlzbWlzc19ydWxlLGlnX2FuZHJvaWRfZGlzYWJsZV9jaHJvbWFfc3Vic2FtcGxpbmcsaWdfYW5kcm9pZF9zaGFyZV9zcGlubmVyLGlnX2FuZHJvaWRfdmlkZW9fcmV1c2Vfc3VyZmFjZSxpZ19leHBsb3JlX3YzX2FuZHJvaWRfdW5pdmVyc2UsaWdfYW5kcm9pZF9tZWRpYV9mYXZvcml0ZXMsaWdfYW5kcm9pZF9udXhfaG9sZG91dCxpZ19hbmRyb2lkX2luc3RhX3ZpZGVvX3VuaXZlcnNlLGlnX2FuZHJvaWRfc2VhcmNoX251bGxfc3RhdGUsaWdfYW5kcm9pZF91bml2ZXJzZV9yZWVsX3ZpZGVvX3Byb2R1Y3Rpb24sbGlnZXJfaW5zdGFncmFtX2FuZHJvaWRfdW5pdixpZ19hbmRyb2lkX2RpcmVjdF9lbW9qaV9waWNrZXIsaWdfZmVlZF9ob2xkb3V0X3VuaXZlcnNlLGlnX2FuZHJvaWRfZGlyZWN0X3NlbmRfYXV0b19yZXRyeV91bml2ZXJzZSxpZ19hbmRyb2lkX3NhbXN1bmdfYXBwX2JhZGdpbmcsaWdfYW5kcm9pZF9kaXNrX3VzYWdlLGlnX2FuZHJvaWRfYnVzaW5lc3NfcHJvbW90aW9uLGlnX2FuZHJvaWRfZGlyZWN0X3N3aXBlX3RvX2luYm94LGlnX2FuZHJvaWRfZmVlZF9yZXNoYXJlX2J1dHRvbl9udXgsaWdfYW5kcm9pZF9yZWFjdF9uYXRpdmVfYm9vc3RfcG9zdCxpZ19hbmRyb2lkX2Jvb21lcmFuZ19mZWVkX2F0dHJpYnV0aW9uLGlnX2ZibnNfc2hhcmVkLGlnX2ZibnNfZHVtcF9pZHMsaWdfYW5kcm9pZF9yZWFjdF9uYXRpdmVfdW5pdmVyc2UsaWdfc2hvd19wcm9tb3RlX2J1dHRvbl9pbl9mZWVkLGlnX2FuZHJvaWRfYWRfbWV0YWRhdGFfYmVoYXZpb3JfdW5pdmVyc2UsaWdfYW5kcm9pZF92aWRlb19sb29wY291bnRfaW50LGlnX2FuZHJvaWRfaW5saW5lX2dhbGxlcnlfYmFja29mZl9ob3Vyc191bml2ZXJzZSxpZ19hbmRyb2lkX3JlbmRlcmluZ19jb250cm9scyxpZ19hbmRyb2lkX3Byb2ZpbGVfcGhvdG9fYXNfbWVkaWEsaWdfYW5kcm9pZF9hc3luY19zdGFja19pbWFnZV9jYWNoZSxpZ192aWRlb19tYXhfZHVyYXRpb25fcWVfcHJldW5pdmVyc2UsaWdfdmlkZW9fY29weXJpZ2h0X3doaXRlbGlzdCxpZ19hbmRyb2lkX3JlbmRlcl9zdG9yaWVzX3dpdGhfY29udGVudF9vdmVycmlkZSxpZ19hbmRyb2lkX2FkX2ludGVudF90b19oaWdobGlnaHRfdW5pdmVyc2UsaWdfYW5kcm9pZF9zd2lwZV9uYXZpZ2F0aW9uX3hfYW5nbGVfdW5pdmVyc2UsaWdfYW5kcm9pZF9kaXNhYmxlX2NvbW1lbnRfcHVibGljX3Rlc3QsaWdfYW5kcm9pZF9wcm9maWxlLGlnX2FuZHJvaWRfZGlyZWN0X2JsdWVfdGFiLGlnX2FuZHJvaWRfZW5hYmxlX3NoYXJlX3RvX21lc3NlbmdlcixpZ19hbmRyb2lkX2ZldGNoX3JlZWxfdHJheV9vbl9yZXN1bWVfdW5pdmVyc2UsaWdfYW5kcm9pZF9wcm9tb3RlX2FnYWluLGlnX2ZlZWRfZXZlbnRfbGFuZGluZ19wYWdlX2NoYW5uZWwsaWdfcmFua2luZ19mb2xsb3dpbmcsaWdfYW5kcm9pZF9wZW5kaW5nX3JlcXVlc3Rfc2VhcmNoX2JhcixpZ19hbmRyb2lkX2ZlZWRfdWZpX3JlZGVzaWduLGlnX2FuZHJvaWRfcGVuZGluZ19lZGl0c19kaWFsb2dfdW5pdmVyc2UsaWdfYW5kcm9pZF9idXNpbmVzc19jb252ZXJzaW9uX2Zsb3dfdW5pdmVyc2UsaWdfYW5kcm9pZF9zaG93X3lvdXJfc3Rvcnlfd2hlbl9lbXB0eV91bml2ZXJzZSxpZ19hbmRyb2lkX2FkX2Ryb3BfY29va2llX2Vhcmx5LGlnX2FuZHJvaWRfYXBwX3N0YXJ0X2NvbmZpZyxpZ19hbmRyb2lkX2ZpeF9pc2VfdHdvX3BoYXNlLGlnX2FuZHJvaWRfcHBhZ2VfdG9nZ2xlX3VuaXZlcnNlLGlnX2FuZHJvaWRfcGJpYV9ub3JtYWxfd2VpZ2h0X3VuaXZlcnNlLGlnX2FuZHJvaWRfcHJvZmFuaXR5X2ZpbHRlcixpZ19pb3Nfc3VfYWN0aXZpdHlfZmVlZCxpZ19hbmRyb2lkX3NlYXJjaCxpZ19hbmRyb2lkX2Jvb21lcmFuZ19lbnRyeSxpZ19hbmRyb2lkX211dGVfc3RvcnksaWdfYW5kcm9pZF9pbmxpbmVfZ2FsbGVyeV91bml2ZXJzZSxpZ19hbmRyb2lkX2FkX3JlbW92ZV9vbmVfdGFwX2luZGljYXRvcl91bml2ZXJzZSxpZ19hbmRyb2lkX3ZpZXdfY291bnRfZGVjb3VwbGVfbGlrZXNfdW5pdmVyc2UsaWdfYW5kcm9pZF9jb250YWN0X2J1dHRvbl9yZWRlc2lnbl92MixpZ19hbmRyb2lkX3BlcmlvZGljX2FuYWx5dGljc191cGxvYWRfdjIsaWdfYW5kcm9pZF9zZW5kX2RpcmVjdF90eXBpbmdfaW5kaWNhdG9yLGlnX2FuZHJvaWRfYWRfaG9sZG91dF8xNmgybTFfdW5pdmVyc2UsaWdfYW5kcm9pZF9yZWFjdF9uYXRpdmVfY29tbWVudF9tb2RlcmF0aW9uX3NldHRpbmdzLGlnX3ZpZGVvX3VzZV9zdmVfdW5pdmVyc2UsaWdfYW5kcm9pZF9pbmxpbmVfZ2FsbGVyeV9ub19iYWNrb2ZmX29uX2xhdW5jaF91bml2ZXJzZSxpZ19hbmRyb2lkX2ltbWVyc2l2ZV92aWV3ZXIsaWdfYW5kcm9pZF9kaXNjb3Zlcl9wZW9wbGVfaWNvbixpZ19hbmRyb2lkX3Byb2ZpbGVfZm9sbG93X2JhY2tfYnV0dG9uLGlzX2FuZHJvaWRfZmVlZF9zZWVuX3N0YXRlLGlnX2FuZHJvaWRfZGVuc2VfZmVlZF91bml0X2NhcmRzLGlnX2FuZHJvaWRfZHJhZnRzX3ZpZGVvX3VuaXZlcnNlLGlnX2FuZHJvaWRfZXhvcGxheWVyLGlnX2FuZHJvaWRfYWRkX3RvX2xhc3RfcG9zdCxpZ19hbmRyb2lkX2FkX3JlbW92ZV9jdGFfY2hldnJvbl91bml2ZXJzZSxpZ19hbmRyb2lkX2FkX2NvbW1lbnRfY3RhX3VuaXZlcnNlLGlnX2FuZHJvaWRfc2VhcmNoX2V2ZW50X2ljb24saWdfYW5kcm9pZF9jaGFubmVsc19ob21lLGlnX2FuZHJvaWRfZmVlZCxpZ19hbmRyb2lkX2R2Ml9yZWFsdGltZV9wcml2YXRlX3NoYXJlLGlnX2FuZHJvaWRfbm9uX3NxdWFyZV9maXJzdCxpZ19hbmRyb2lkX3ZpZGVvX2ludGVybGVhdmVkX3YyLGlnX2FuZHJvaWRfdmlkZW9fY2FjaGVfcG9saWN5LGlnX2FuZHJvaWRfcmVhY3RfbmF0aXZlX3VuaXZlcnNlX2tpbGxfc3dpdGNoLGlnX2FuZHJvaWRfdmlkZW9fY2FwdGlvbnNfdW5pdmVyc2UsaWdfYW5kcm9pZF9mb2xsb3dfc2VhcmNoX2JhcixpZ19hbmRyb2lkX2xhc3RfZWRpdHMsaWdfYW5kcm9pZF90d29fc3RlcF9jYXB0dXJlX2Zsb3csaWdfYW5kcm9pZF92aWRlb19kb3dubG9hZF9sb2dnaW5nLGlnX2FuZHJvaWRfc2hhcmVfbGlua190b193aGF0c2FwcCxpZ19hbmRyb2lkX2ZhY2Vib29rX3R3aXR0ZXJfcHJvZmlsZV9waG90b3MsaWdfYW5kcm9pZF9zd2lwZWFibGVfZmlsdGVyc19ibGFja2xpc3QsaWdfYW5kcm9pZF9hZF9wYmlhX3Byb2ZpbGVfdGFwX3VuaXZlcnNlLGlnX2FuZHJvaWRfdXNlX3NvZnR3YXJlX2xheWVyX2Zvcl9rY19kcmF3aW5nX3VuaXZlcnNlLGlnX2FuZHJvaWRfcmVhY3RfbmF0aXZlX290YSxpZ19hbmRyb2lkX2RpcmVjdF9tdXR1YWxseV9leGNsdXNpdmVfZXhwZXJpbWVudF91bml2ZXJzZSxpZ19hbmRyb2lkX2ZvbGxvd2luZ19mb2xsb3dlcl9zb2NpYWxfY29udGV4dCI7DQogICAgY29uc3QgTE9HSU5fRVhQRVJJTUVOVFMgPSAiaWdfYW5kcm9pZF9yZWdfbG9naW5fYnRuX2FjdGl2ZV9zdGF0ZSxpZ19hbmRyb2lkX2NpX29wdF9pbl9hdF9yZWcsaWdfYW5kcm9pZF9vbmVfY2xpY2tfaW5fb2xkX2Zsb3csaWdfYW5kcm9pZF9tZXJnZV9mYl9hbmRfY2lfZnJpZW5kc19wYWdlLGlnX2FuZHJvaWRfbm9uX2ZiX3NzbyxpZ19hbmRyb2lkX21hbmRhdG9yeV9mdWxsX25hbWUsaWdfYW5kcm9pZF9yZWdfZW5hYmxlX2xvZ2luX3Bhc3N3b3JkX2J0bixpZ19hbmRyb2lkX3JlZ19waG9uZV9lbWFpbF9hY3RpdmVfc3RhdGUsaWdfYW5kcm9pZF9hbmFseXRpY3NfZGF0YV9sb3NzLGlnX2ZibnNfYmxvY2tlZCxpZ19hbmRyb2lkX2NvbnRhY3RfcG9pbnRfdHJpYWdlLGlnX2FuZHJvaWRfcmVnX25leHRfYnRuX2FjdGl2ZV9zdGF0ZSxpZ19hbmRyb2lkX3ByZWZpbGxfcGhvbmVfbnVtYmVyLGlnX2FuZHJvaWRfc2hvd19mYl9zb2NpYWxfY29udGV4dF9pbl9udXgsaWdfYW5kcm9pZF9vbmVfdGFwX2xvZ2luX3Vwc2VsbCxpZ19mYm5zX3B1c2gsaWdfYW5kcm9pZF9waG9uZWlkX3N5bmNfaW50ZXJ2YWwiOw0KICAgIGNvbnN0IFNJR19LRVlfVkVSU0lPTiA9ICI0IjsNCiAgICBjb25zdCBYX0lHX0NhcGFiaWxpdGllcyA9ICIzUjQ9IjsNCiAgICBjb25zdCBBTkRST0lEX1ZFUlNJT04gPSAxODsNCiAgICBjb25zdCBBTkRST0lEX1JFTEVBU0UgPSAiNC4zIjsNCiAgICBjb25zdCBXRUJfVVJMID0gImh0dHBzOi8vd3d3Lmluc3RhZ3JhbS5jb20vIjsNCiAgICBjb25zdCBXRUJfVVNFUl9BR0VOVCA9ICJNb3ppbGxhLzUuMCAoTGludXg7IEFuZHJvaWQgNC40LjI7IEhVQVdFSSBNVDctTDA5IEJ1aWxkL0h1YXdlaU1UNy1MMDkpIEFwcGxlV2ViS2l0LzUzNy4zNiAoS0hUTUwsIGxpa2UgR2Vja28pIENocm9tZS80Ni4wLjI0OTAuNzYgTW9iaWxlIFNhZmFyaS81MzcuMzYiOw0KDQp9DQoNCg0KY2xhc3MgQnVsa1JlYWN0aW9uDQp7DQovKioNCiAgICAgICAgICogQHZhciBhcnJheSAkdXNlcnMNCiAgICAgICAgICovDQogICAgcHJvdGVjdGVkICR1c2VycyA9IE5VTEw7DQovKioNCiAgICAgICAgICogQHZhciBpbnQgJHNpbXVsdGFuZXBvc3RzaXplDQogICAgICAgICAqLw0KICAgIHByb3RlY3RlZCAkc2ltdWx0YW5lcG9zdHNpemUgPSBOVUxMOw0KLyoqDQogICAgICAgICAqIEB2YXIgc3RyaW5nDQogICAgICAgICAqLw0KICAgIHByb3RlY3RlZCAkSUdEYXRhUGF0aCA9IE5VTEw7DQoNCiAgICAvKioNCiAgICAgICAgICogQnVsa1JlYWN0aW9uIGNvbnN0cnVjdG9yLg0KICAgICAgICAgKg0KICAgICAgICAgKiBAcGFyYW0gYXJyYXkgICR1c2Vycw0KICAgICAgICAgKiBAcGFyYW0gaW50ICAgICRzaW11bHRhbmVwb3N0c2l6ZQ0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICRJR0RhdGFQYXRoDQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIF9fY29uc3RydWN0KCR1c2VycywgJHNpbXVsdGFuZXBvc3RzaXplID0gMTAwLCAkSUdEYXRhUGF0aCA9IE5VTEwpDQogICAgew0KICAgICAgICAkdGhpcy0+c2ltdWx0YW5lcG9zdHNpemUgPSAkc2ltdWx0YW5lcG9zdHNpemU7DQogICAgICAgIGlmKCAhaXNfbnVsbCgkSUdEYXRhUGF0aCkgKSANCiAgICAgICAgew0KICAgICAgICAgICAgJHRoaXMtPklHRGF0YVBhdGggPSAkSUdEYXRhUGF0aDsNCiAgICAgICAgfQ0KICAgICAgICBlbHNlDQogICAgICAgIHsNCiAgICAgICAgICAgICR0aGlzLT5JR0RhdGFQYXRoID0gV293OjpnZXQoInByb2plY3QvY29va2llUGF0aCIpIC4gImluc3RhZ3JhbS8iOw0KICAgICAgICB9DQoNCiAgICAgICAgZm9yZWFjaCggJHVzZXJzIGFzICR1c2VyICkgDQogICAgICAgIHsNCiAgICAgICAgICAgICR0aGlzLT51c2Vyc1tdID0gYXJyYXkoICJkYXRhIiA9PiAkdXNlciwgIm9iamVjdCIgPT4gKCR1c2VyWyJpc1dlYkNvb2tpZSJdID09IDEgPyBuZXcgSW5zdGFncmFtV2ViKCR1c2VyWyJrdWxsYW5pY2lBZGkiXSkgOiBuZXcgSW5zdGFncmFtKCR1c2VyWyJrdWxsYW5pY2lBZGkiXSwgJHVzZXJbInNpZnJlIl0pKSApOw0KICAgICAgICB9DQogICAgfQ0KDQogICAgLyoqDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgICRtZWRpYUlEDQogICAgICAgICAqIEBwYXJhbSAgc3RyaW5nICRtZWRpYUNvZGUNCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBsaWtlKCRtZWRpYUlELCAkbWVkaWFDb2RlKQ0KICAgIHsNCiAgICAgICAgJHRvdGFsU3VjY2Vzc0NvdW50ID0gMDsNCiAgICAgICAgJHRyaWVkVXNlcnMgPSBhcnJheSgpOw0KICAgICAgICAkcm9sbGluZ0N1cmwgPSBuZXcgUm9sbGluZ0N1cmxcUm9sbGluZ0N1cmwoKTsNCiAgICAgICAgZm9yZWFjaCggJHRoaXMtPnVzZXJzIGFzICR1c2VyICkgDQogICAgICAgIHsNCiAgICAgICAgICAgIGlmKCAkdXNlclsiZGF0YSJdWyJpc1dlYkNvb2tpZSJdID09IDEgKSANCiAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAkb2JqSW5zdGFncmFtV2ViID0gJHVzZXJbIm9iamVjdCJdOw0KICAgICAgICAgICAgICAgICRvYmpEYXRhID0gJG9iakluc3RhZ3JhbVdlYi0+Z2V0RGF0YSgpOw0KICAgICAgICAgICAgICAgICRoZWFkZXJzID0gYXJyYXkoICJSZWZlcmVyOiBodHRwczovL3d3dy5pbnN0YWdyYW0uY29tL2luc3RhZ3JhbS8iLCAiRE5UOiAxIiwgIk9yaWdpbjogaHR0cHM6Ly93d3cuaW5zdGFncmFtLmNvbS8iLCAiWC1DU1JGVG9rZW46ICIgLiB0cmltKCRvYmpEYXRhWyJ0b2tlbiJdKSwgIlgtUmVxdWVzdGVkLVdpdGg6IFhNTEh0dHBSZXF1ZXN0IiwgIlgtSW5zdGFncmFtLUFKQVg6IDEiLCAiQ29ubmVjdGlvbjogY2xvc2UiLCAiQ2FjaGUtQ29udHJvbDogbWF4LWFnZT0wIiwgIkFjY2VwdDogKi8qIiwgIkFjY2VwdC1MYW5ndWFnZTogdHI7cT0xIiwgIlhfRk9SV0FSREVEX0ZPUjogIiAuICRvYmpEYXRhWyJpcCJdLCAiUkVNT1RFX0FERFI6ICIgLiAkb2JqRGF0YVsiaXAiXSApOw0KICAgICAgICAgICAgICAgICRvcHRpb25zID0gYXJyYXkoIENVUkxPUFRfVVNFUkFHRU5UID0+IENvbnN0YW50czo6V0VCX1VTRVJfQUdFTlQsIENVUkxPUFRfUkVUVVJOVFJBTlNGRVIgPT4gdHJ1ZSwgQ1VSTE9QVF9GT0xMT1dMT0NBVElPTiA9PiB0cnVlLCBDVVJMT1BUX1ZFUkJPU0UgPT4gZmFsc2UsIENVUkxPUFRfU1NMX1ZFUklGWVBFRVIgPT4gZmFsc2UsIENVUkxPUFRfU1NMX1ZFUklGWUhPU1QgPT4gZmFsc2UsIENVUkxPUFRfQ09PS0lFRklMRSA9PiAkdGhpcy0+SUdEYXRhUGF0aCAuICRvYmpEYXRhWyJ1c2VybmFtZSJdIC4gIi5kYXQiLCBDVVJMT1BUX0NPT0tJRUpBUiA9PiAkdGhpcy0+SUdEYXRhUGF0aCAuICRvYmpEYXRhWyJ1c2VybmFtZSJdIC4gIi5kYXQiICk7DQogICAgICAgICAgICAgICAgJHJvbGxpbmdDdXJsLT5wb3N0KENvbnN0YW50czo6V0VCX1VSTCAuICJ3ZWIvbGlrZXMvIiAuICRtZWRpYUlEIC4gIi9saWtlLyIsIE5VTEwsICRoZWFkZXJzLCAkb3B0aW9ucywgJHVzZXJbImRhdGEiXSk7DQogICAgICAgICAgICB9DQogICAgICAgICAgICBlbHNlDQogICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgJG9iakluc3RhZ3JhbSA9ICR1c2VyWyJvYmplY3QiXTsNCiAgICAgICAgICAgICAgICAkb2JqRGF0YSA9ICRvYmpJbnN0YWdyYW0tPmdldERhdGEoKTsNCiAgICAgICAgICAgICAgICAkZGF0YSA9IGpzb25fZW5jb2RlKGFycmF5KCAiX3V1aWQiID0+ICRvYmpEYXRhWyJ1dWlkIl0sICJfdWlkIiA9PiAkb2JqRGF0YVsidXNlcm5hbWVfaWQiXSwgIl9jc3JmdG9rZW4iID0+ICRvYmpEYXRhWyJ0b2tlbiJdLCAibWVkaWFfaWQiID0+ICRtZWRpYUlEICkpOw0KICAgICAgICAgICAgICAgICRoZWFkZXJzID0gYXJyYXkoICJDb25uZWN0aW9uOiBjbG9zZSIsICJBY2NlcHQ6ICovKiIsICJYLUlHLUNhcGFiaWxpdGllczogIiAuIENvbnN0YW50czo6WF9JR19DYXBhYmlsaXRpZXMsICJYLUlHLUNvbm5lY3Rpb24tVHlwZTogV0lGSSIsICJDb250ZW50LXR5cGU6IGFwcGxpY2F0aW9uL3gtd3d3LWZvcm0tdXJsZW5jb2RlZDsgY2hhcnNldD1VVEYtOCIsICJBY2NlcHQtTGFuZ3VhZ2U6IHRyLVRSIiwgIlhfRk9SV0FSREVEX0ZPUjogIiAuICRvYmpEYXRhWyJpcCJdLCAiUkVNT1RFX0FERFI6ICIgLiAkb2JqRGF0YVsiaXAiXSApOw0KICAgICAgICAgICAgICAgICRvcHRpb25zID0gYXJyYXkoIENVUkxPUFRfVVNFUkFHRU5UID0+ICRvYmpEYXRhWyJ1c2VyX2FnZW50Il0sIENVUkxPUFRfUkVUVVJOVFJBTlNGRVIgPT4gdHJ1ZSwgQ1VSTE9QVF9GT0xMT1dMT0NBVElPTiA9PiB0cnVlLCBDVVJMT1BUX1ZFUkJPU0UgPT4gZmFsc2UsIENVUkxPUFRfU1NMX1ZFUklGWVBFRVIgPT4gZmFsc2UsIENVUkxPUFRfU1NMX1ZFUklGWUhPU1QgPT4gZmFsc2UsIENVUkxPUFRfQ09PS0lFRklMRSA9PiAkdGhpcy0+SUdEYXRhUGF0aCAuICRvYmpEYXRhWyJ1c2VybmFtZSJdIC4gIi5kYXQiLCBDVVJMT1BUX0NPT0tJRUpBUiA9PiAkdGhpcy0+SUdEYXRhUGF0aCAuICRvYmpEYXRhWyJ1c2VybmFtZSJdIC4gIi5kYXQiICk7DQogICAgICAgICAgICAgICAgJHJvbGxpbmdDdXJsLT5wb3N0KENvbnN0YW50czo6QVBJX1VSTCAuICJtZWRpYS8iIC4gJG1lZGlhSUQgLiAiL2xpa2UvIiwgU2lnbmF0dXJlVXRpbHM6OmdlbmVyYXRlU2lnbmF0dXJlKCRkYXRhKSwgJGhlYWRlcnMsICRvcHRpb25zLCAkdXNlclsiZGF0YSJdKTsNCiAgICAgICAgICAgIH0NCg0KICAgICAgICB9DQogICAgICAgICRyb2xsaW5nQ3VybC0+c2V0Q2FsbGJhY2soZnVuY3Rpb24oUm9sbGluZ0N1cmxcUmVxdWVzdCAkcmVxdWVzdCwgUm9sbGluZ0N1cmxcUm9sbGluZ0N1cmwgJHJvbGxpbmdDdXJsKSB1c2UgKCYkdHJpZWRVc2VycywgJiR0b3RhbFN1Y2Nlc3NDb3VudCkNCnsNCiAgICAkdHJpZWRVc2VyID0gYXJyYXkoICJ1c2VySUQiID0+ICRyZXF1ZXN0LT5pZGVudGlmaWVyUGFyYW1zWyJ1eWVJRCJdLCAiaW5zdGFJRCIgPT4gJHJlcXVlc3QtPmlkZW50aWZpZXJQYXJhbXNbImluc3RhSUQiXSwgInVzZXJOaWNrIiA9PiAkcmVxdWVzdC0+aWRlbnRpZmllclBhcmFtc1sia3VsbGFuaWNpQWRpIl0gKTsNCiAgICAkaXNFcnJvcmVkID0gJHJlcXVlc3QtPmdldFJlc3BvbnNlRXJyb3IoKTsNCiAgICBpZiggZW1wdHkoJGlzRXJyb3JlZCkgKSANCiAgICB7DQogICAgICAgICRkb25lblNvbnVjID0ganNvbl9kZWNvZGUoJHJlcXVlc3QtPmdldFJlc3BvbnNlVGV4dCgpLCB0cnVlKTsNCiAgICAgICAgaWYoICRkb25lblNvbnVjICkgDQogICAgICAgIHsNCiAgICAgICAgICAgIGlmKCAkcmVxdWVzdC0+aWRlbnRpZmllclBhcmFtc1siaXNXZWJDb29raWUiXSA9PSAxICkgDQogICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgaWYoICRkb25lblNvbnVjWyJzdGF0dXMiXSA9PSAib2siICkgDQogICAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICAgICAkdG90YWxTdWNjZXNzQ291bnQrKzsNCiAgICAgICAgICAgICAgICAgICAgJHRyaWVkVXNlclsic3RhdHVzIl0gPSAic3VjY2VzcyI7DQogICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgIGVsc2UNCiAgICAgICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgICAgICR0cmllZFVzZXJbInN0YXR1cyJdID0gImZhaWwiOw0KICAgICAgICAgICAgICAgIH0NCg0KICAgICAgICAgICAgfQ0KICAgICAgICAgICAgZWxzZQ0KICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgIGlmKCAkZG9uZW5Tb251Y1sic3RhdHVzIl0gPT0gIm9rIiApIA0KICAgICAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAgICAgJHRvdGFsU3VjY2Vzc0NvdW50Kys7DQogICAgICAgICAgICAgICAgICAgICR0cmllZFVzZXJbInN0YXR1cyJdID0gInN1Y2Nlc3MiOw0KICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICBlbHNlDQogICAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICAgICAkdHJpZWRVc2VyWyJzdGF0dXMiXSA9ICJmYWlsIjsNCiAgICAgICAgICAgICAgICB9DQoNCiAgICAgICAgICAgIH0NCg0KICAgICAgICB9DQoNCiAgICB9DQogICAgZWxzZQ0KICAgIHsNCiAgICAgICAgJHRyaWVkVXNlclsic3RhdHVzIl0gPSAiZmFpbCI7DQogICAgfQ0KDQogICAgJHRyaWVkVXNlcnNbXSA9ICR0cmllZFVzZXI7DQogICAgJHJvbGxpbmdDdXJsLT5jbGVhckNvbXBsZXRlZCgpOw0KICAgICRyb2xsaW5nQ3VybC0+cHJ1bmVQZW5kaW5nUmVxdWVzdFF1ZXVlKCk7DQp9DQoNCik7DQogICAgICAgICRyb2xsaW5nQ3VybC0+c2V0U2ltdWx0YW5lb3VzTGltaXQoJHRoaXMtPnNpbXVsdGFuZXBvc3RzaXplKTsNCiAgICAgICAgJHJvbGxpbmdDdXJsLT5leGVjdXRlKCk7DQogICAgICAgIHJldHVybiBhcnJheSggInRvdGFsU3VjY2Vzc0NvdW50IiA9PiAkdG90YWxTdWNjZXNzQ291bnQsICJ1c2VycyIgPT4gJHRyaWVkVXNlcnMgKTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgICAgICogQHBhcmFtIHN0cmluZyAkdXNlcklEDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJHVzZXJOYW1lDQogICAgICAgICAqDQogICAgICAgICAqIEByZXR1cm4gYXJyYXkNCiAgICAgICAgICovDQoNCiAgICBwdWJsaWMgZnVuY3Rpb24gZm9sbG93KCR1c2VySUQsICR1c2VyTmFtZSkNCiAgICB7DQogICAgICAgICR0b3RhbFN1Y2Nlc3NDb3VudCA9IDA7DQogICAgICAgICR0cmllZFVzZXJzID0gYXJyYXkoKTsNCiAgICAgICAgJHJvbGxpbmdDdXJsID0gbmV3IFJvbGxpbmdDdXJsXFJvbGxpbmdDdXJsKCk7DQogICAgICAgIGZvcmVhY2goICR0aGlzLT51c2VycyBhcyAkdXNlciApIA0KICAgICAgICB7DQogICAgICAgICAgICBpZiggJHVzZXJbImRhdGEiXVsiaXNXZWJDb29raWUiXSA9PSAxICkgDQogICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgJG9iakluc3RhZ3JhbVdlYiA9ICR1c2VyWyJvYmplY3QiXTsNCiAgICAgICAgICAgICAgICAkb2JqRGF0YSA9ICRvYmpJbnN0YWdyYW1XZWItPmdldERhdGEoKTsNCiAgICAgICAgICAgICAgICAkaGVhZGVycyA9IGFycmF5KCAiUmVmZXJlcjogaHR0cHM6Ly93d3cuaW5zdGFncmFtLmNvbS9pbnN0YWdyYW0vIiwgIkROVDogMSIsICJPcmlnaW46IGh0dHBzOi8vd3d3Lmluc3RhZ3JhbS5jb20vIiwgIlgtQ1NSRlRva2VuOiAiIC4gdHJpbSgkb2JqRGF0YVsidG9rZW4iXSksICJYLVJlcXVlc3RlZC1XaXRoOiBYTUxIdHRwUmVxdWVzdCIsICJYLUluc3RhZ3JhbS1BSkFYOiAxIiwgIkNvbm5lY3Rpb246IGNsb3NlIiwgIkNhY2hlLUNvbnRyb2w6IG1heC1hZ2U9MCIsICJBY2NlcHQ6ICovKiIsICJBY2NlcHQtTGFuZ3VhZ2U6IHRyO3E9MSIsICJYX0ZPUldBUkRFRF9GT1I6ICIgLiAkb2JqRGF0YVsiaXAiXSwgIlJFTU9URV9BRERSOiAiIC4gJG9iakRhdGFbImlwIl0gKTsNCiAgICAgICAgICAgICAgICAkb3B0aW9ucyA9IGFycmF5KCBDVVJMT1BUX1VTRVJBR0VOVCA9PiBDb25zdGFudHM6OldFQl9VU0VSX0FHRU5ULCBDVVJMT1BUX1JFVFVSTlRSQU5TRkVSID0+IHRydWUsIENVUkxPUFRfRk9MTE9XTE9DQVRJT04gPT4gdHJ1ZSwgQ1VSTE9QVF9WRVJCT1NFID0+IGZhbHNlLCBDVVJMT1BUX1NTTF9WRVJJRllQRUVSID0+IGZhbHNlLCBDVVJMT1BUX1NTTF9WRVJJRllIT1NUID0+IGZhbHNlLCBDVVJMT1BUX0NPT0tJRUZJTEUgPT4gJHRoaXMtPklHRGF0YVBhdGggLiAkb2JqRGF0YVsidXNlcm5hbWUiXSAuICIuZGF0IiwgQ1VSTE9QVF9DT09LSUVKQVIgPT4gJHRoaXMtPklHRGF0YVBhdGggLiAkb2JqRGF0YVsidXNlcm5hbWUiXSAuICIuZGF0IiApOw0KICAgICAgICAgICAgICAgICRyb2xsaW5nQ3VybC0+cG9zdChDb25zdGFudHM6OldFQl9VUkwgLiAid2ViL2ZyaWVuZHNoaXBzLyIgLiAkdXNlcklEIC4gIi9mb2xsb3cvIiwgTlVMTCwgJGhlYWRlcnMsICRvcHRpb25zLCAkdXNlclsiZGF0YSJdKTsNCiAgICAgICAgICAgIH0NCiAgICAgICAgICAgIGVsc2UNCiAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAkb2JqSW5zdGFncmFtID0gJHVzZXJbIm9iamVjdCJdOw0KICAgICAgICAgICAgICAgICRvYmpEYXRhID0gJG9iakluc3RhZ3JhbS0+Z2V0RGF0YSgpOw0KICAgICAgICAgICAgICAgICRkYXRhID0ganNvbl9lbmNvZGUoYXJyYXkoICJfdXVpZCIgPT4gJG9iakRhdGFbInV1aWQiXSwgIl91aWQiID0+ICRvYmpEYXRhWyJ1c2VybmFtZV9pZCJdLCAidXNlcl9pZCIgPT4gJHVzZXJJRCwgIl9jc3JmdG9rZW4iID0+ICRvYmpEYXRhWyJ0b2tlbiJdICkpOw0KICAgICAgICAgICAgICAgICRoZWFkZXJzID0gYXJyYXkoICJDb25uZWN0aW9uOiBjbG9zZSIsICJBY2NlcHQ6ICovKiIsICJYLUlHLUNhcGFiaWxpdGllczogIiAuIENvbnN0YW50czo6WF9JR19DYXBhYmlsaXRpZXMsICJYLUlHLUNvbm5lY3Rpb24tVHlwZTogV0lGSSIsICJDb250ZW50LXR5cGU6IGFwcGxpY2F0aW9uL3gtd3d3LWZvcm0tdXJsZW5jb2RlZDsgY2hhcnNldD1VVEYtOCIsICJBY2NlcHQtTGFuZ3VhZ2U6IHRyLVRSIiwgIlhfRk9SV0FSREVEX0ZPUjogIiAuICRvYmpEYXRhWyJpcCJdLCAiUkVNT1RFX0FERFI6ICIgLiAkb2JqRGF0YVsiaXAiXSApOw0KICAgICAgICAgICAgICAgICRvcHRpb25zID0gYXJyYXkoIENVUkxPUFRfVVNFUkFHRU5UID0+ICRvYmpEYXRhWyJ1c2VyX2FnZW50Il0sIENVUkxPUFRfUkVUVVJOVFJBTlNGRVIgPT4gdHJ1ZSwgQ1VSTE9QVF9GT0xMT1dMT0NBVElPTiA9PiB0cnVlLCBDVVJMT1BUX1ZFUkJPU0UgPT4gZmFsc2UsIENVUkxPUFRfU1NMX1ZFUklGWVBFRVIgPT4gZmFsc2UsIENVUkxPUFRfU1NMX1ZFUklGWUhPU1QgPT4gZmFsc2UsIENVUkxPUFRfQ09PS0lFRklMRSA9PiAkdGhpcy0+SUdEYXRhUGF0aCAuICRvYmpEYXRhWyJ1c2VybmFtZSJdIC4gIi5kYXQiLCBDVVJMT1BUX0NPT0tJRUpBUiA9PiAkdGhpcy0+SUdEYXRhUGF0aCAuICRvYmpEYXRhWyJ1c2VybmFtZSJdIC4gIi5kYXQiICk7DQogICAgICAgICAgICAgICAgJHJvbGxpbmdDdXJsLT5wb3N0KENvbnN0YW50czo6QVBJX1VSTCAuICJmcmllbmRzaGlwcy9jcmVhdGUvIiAuICR1c2VySUQgLiAiLyIsIFNpZ25hdHVyZVV0aWxzOjpnZW5lcmF0ZVNpZ25hdHVyZSgkZGF0YSksICRoZWFkZXJzLCAkb3B0aW9ucywgJHVzZXJbImRhdGEiXSk7DQogICAgICAgICAgICB9DQoNCiAgICAgICAgfQ0KICAgICAgICAkcm9sbGluZ0N1cmwtPnNldENhbGxiYWNrKGZ1bmN0aW9uKFJvbGxpbmdDdXJsXFJlcXVlc3QgJHJlcXVlc3QsIFJvbGxpbmdDdXJsXFJvbGxpbmdDdXJsICRyb2xsaW5nQ3VybCkgdXNlICgmJHRyaWVkVXNlcnMsICYkdG90YWxTdWNjZXNzQ291bnQpDQp7DQogICAgJHRyaWVkVXNlciA9IGFycmF5KCAidXNlcklEIiA9PiAkcmVxdWVzdC0+aWRlbnRpZmllclBhcmFtc1sidXllSUQiXSwgImluc3RhSUQiID0+ICRyZXF1ZXN0LT5pZGVudGlmaWVyUGFyYW1zWyJpbnN0YUlEIl0sICJ1c2VyTmljayIgPT4gJHJlcXVlc3QtPmlkZW50aWZpZXJQYXJhbXNbImt1bGxhbmljaUFkaSJdICk7DQogICAgJGlzRXJyb3JlZCA9ICRyZXF1ZXN0LT5nZXRSZXNwb25zZUVycm9yKCk7DQogICAgaWYoIGVtcHR5KCRpc0Vycm9yZWQpICkgDQogICAgew0KICAgICAgICAkZG9uZW5Tb251YyA9IGpzb25fZGVjb2RlKCRyZXF1ZXN0LT5nZXRSZXNwb25zZVRleHQoKSwgdHJ1ZSk7DQogICAgICAgIGlmKCAkZG9uZW5Tb251YyApIA0KICAgICAgICB7DQogICAgICAgICAgICBpZiggJHJlcXVlc3QtPmlkZW50aWZpZXJQYXJhbXNbImlzV2ViQ29va2llIl0gPT0gMSApIA0KICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgIGlmKCAkZG9uZW5Tb251Y1sic3RhdHVzIl0gPT0gIm9rIiAmJiAkZG9uZW5Tb251Y1sicmVzdWx0Il0gPT0gImZvbGxvd2luZyIgKSANCiAgICAgICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgICAgICR0b3RhbFN1Y2Nlc3NDb3VudCsrOw0KICAgICAgICAgICAgICAgICAgICAkdHJpZWRVc2VyWyJzdGF0dXMiXSA9ICJzdWNjZXNzIjsNCiAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICAgZWxzZQ0KICAgICAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAgICAgJHRyaWVkVXNlclsic3RhdHVzIl0gPSAiZmFpbCI7DQogICAgICAgICAgICAgICAgfQ0KDQogICAgICAgICAgICB9DQogICAgICAgICAgICBlbHNlDQogICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgaWYoICRkb25lblNvbnVjWyJzdGF0dXMiXSA9PSAib2siICkgDQogICAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICAgICAkdG90YWxTdWNjZXNzQ291bnQrKzsNCiAgICAgICAgICAgICAgICAgICAgJHRyaWVkVXNlclsic3RhdHVzIl0gPSAic3VjY2VzcyI7DQogICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgIGVsc2UNCiAgICAgICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgICAgICR0cmllZFVzZXJbInN0YXR1cyJdID0gImZhaWwiOw0KICAgICAgICAgICAgICAgIH0NCg0KICAgICAgICAgICAgfQ0KDQogICAgICAgIH0NCg0KICAgIH0NCiAgICBlbHNlDQogICAgew0KICAgICAgICAkdHJpZWRVc2VyWyJzdGF0dXMiXSA9ICJmYWlsIjsNCiAgICB9DQoNCiAgICAkdHJpZWRVc2Vyc1tdID0gJHRyaWVkVXNlcjsNCiAgICAkcm9sbGluZ0N1cmwtPmNsZWFyQ29tcGxldGVkKCk7DQogICAgJHJvbGxpbmdDdXJsLT5wcnVuZVBlbmRpbmdSZXF1ZXN0UXVldWUoKTsNCn0NCg0KKTsNCiAgICAgICAgJHJvbGxpbmdDdXJsLT5zZXRTaW11bHRhbmVvdXNMaW1pdCgkdGhpcy0+c2ltdWx0YW5lcG9zdHNpemUpOw0KICAgICAgICAkcm9sbGluZ0N1cmwtPmV4ZWN1dGUoKTsNCiAgICAgICAgcmV0dXJuIGFycmF5KCAidG90YWxTdWNjZXNzQ291bnQiID0+ICR0b3RhbFN1Y2Nlc3NDb3VudCwgInVzZXJzIiA9PiAkdHJpZWRVc2VycyApOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBAcGFyYW0gc3RyaW5nICRtZWRpYUlEDQogICAgICAgICAqIEBwYXJhbSBzdHJpbmcgJG1lZGlhQ29kZQ0KICAgICAgICAgKiBAcGFyYW0gYXJyYXkgICRjb21tZW50VGV4dHMNCiAgICAgICAgICoNCiAgICAgICAgICogQHJldHVybiBhcnJheQ0KICAgICAgICAgKi8NCg0KICAgIHB1YmxpYyBmdW5jdGlvbiBjb21tZW50KCRtZWRpYUlELCAkbWVkaWFDb2RlLCAkY29tbWVudFRleHRzKQ0KICAgIHsNCiAgICAgICAgJHRvdGFsU3VjY2Vzc0NvdW50ID0gMDsNCiAgICAgICAgJHRyaWVkVXNlcnMgPSBhcnJheSgpOw0KICAgICAgICAkY29tbWVudEluZGV4ID0gLTE7DQogICAgICAgIGlmKCBpc19hcnJheSgkY29tbWVudFRleHRzKSAmJiAhZW1wdHkoJGNvbW1lbnRUZXh0cykgKSANCiAgICAgICAgew0KICAgICAgICAgICAgJHJvbGxpbmdDdXJsID0gbmV3IFJvbGxpbmdDdXJsXFJvbGxpbmdDdXJsKCk7DQogICAgICAgICAgICBmb3JlYWNoKCAkdGhpcy0+dXNlcnMgYXMgJHVzZXIgKSANCiAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAkY29tbWVudEluZGV4Kys7DQogICAgICAgICAgICAgICAgJGNvbW1lbnRJbmRleCA9IChpc3NldCgkY29tbWVudFRleHRzWyRjb21tZW50SW5kZXhdKSA/ICRjb21tZW50SW5kZXggOiAwKTsNCiAgICAgICAgICAgICAgICBpZiggJHVzZXJbImRhdGEiXVsiaXNXZWJDb29raWUiXSA9PSAxICkgDQogICAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICAgICAkb2JqSW5zdGFncmFtV2ViID0gJHVzZXJbIm9iamVjdCJdOw0KICAgICAgICAgICAgICAgICAgICAkb2JqRGF0YSA9ICRvYmpJbnN0YWdyYW1XZWItPmdldERhdGEoKTsNCiAgICAgICAgICAgICAgICAgICAgJHBvc3REYXRhID0gImNvbW1lbnRfdGV4dD0iIC4gJGNvbW1lbnRUZXh0c1skY29tbWVudEluZGV4XTsNCiAgICAgICAgICAgICAgICAgICAgJGhlYWRlcnMgPSBhcnJheSggIlJlZmVyZXI6IGh0dHBzOi8vd3d3Lmluc3RhZ3JhbS5jb20vIiwgIkROVDogMSIsICJPcmlnaW46IGh0dHBzOi8vd3d3Lmluc3RhZ3JhbS5jb20vIiwgIlgtQ1NSRlRva2VuOiAiIC4gdHJpbSgkb2JqRGF0YVsidG9rZW4iXSksICJYLVJlcXVlc3RlZC1XaXRoOiBYTUxIdHRwUmVxdWVzdCIsICJYLUluc3RhZ3JhbS1BSkFYOiAxIiwgIkNvbm5lY3Rpb246IGNsb3NlIiwgIkNhY2hlLUNvbnRyb2w6IG1heC1hZ2U9MCIsICJBY2NlcHQ6ICovKiIsICJBY2NlcHQtTGFuZ3VhZ2U6IHRyO3E9MSIsICJYX0ZPUldBUkRFRF9GT1I6ICIgLiAkb2JqRGF0YVsiaXAiXSwgIlJFTU9URV9BRERSOiAiIC4gJG9iakRhdGFbImlwIl0gKTsNCiAgICAgICAgICAgICAgICAgICAgJG9wdGlvbnMgPSBhcnJheSggQ1VSTE9QVF9VU0VSQUdFTlQgPT4gQ29uc3RhbnRzOjpXRUJfVVNFUl9BR0VOVCwgQ1VSTE9QVF9SRVRVUk5UUkFOU0ZFUiA9PiB0cnVlLCBDVVJMT1BUX0ZPTExPV0xPQ0FUSU9OID0+IHRydWUsIENVUkxPUFRfVkVSQk9TRSA9PiBmYWxzZSwgQ1VSTE9QVF9TU0xfVkVSSUZZUEVFUiA9PiBmYWxzZSwgQ1VSTE9QVF9TU0xfVkVSSUZZSE9TVCA9PiBmYWxzZSwgQ1VSTE9QVF9DT09LSUVGSUxFID0+ICR0aGlzLT5JR0RhdGFQYXRoIC4gJG9iakRhdGFbInVzZXJuYW1lIl0gLiAiLmRhdCIsIENVUkxPUFRfQ09PS0lFSkFSID0+ICR0aGlzLT5JR0RhdGFQYXRoIC4gJG9iakRhdGFbInVzZXJuYW1lIl0gLiAiLmRhdCIgKTsNCiAgICAgICAgICAgICAgICAgICAgJHJvbGxpbmdDdXJsLT5wb3N0KENvbnN0YW50czo6V0VCX1VSTCAuICJ3ZWIvY29tbWVudHMvIiAuICRtZWRpYUlEIC4gIi9hZGQvIiwgJHBvc3REYXRhLCAkaGVhZGVycywgJG9wdGlvbnMsICR1c2VyWyJkYXRhIl0pOw0KICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICBlbHNlDQogICAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICAgICAkb2JqSW5zdGFncmFtID0gJHVzZXJbIm9iamVjdCJdOw0KICAgICAgICAgICAgICAgICAgICAkb2JqRGF0YSA9ICRvYmpJbnN0YWdyYW0tPmdldERhdGEoKTsNCiAgICAgICAgICAgICAgICAgICAgJGRhdGEgPSBqc29uX2VuY29kZShhcnJheSggIl91dWlkIiA9PiAkb2JqRGF0YVsidXVpZCJdLCAiX3VpZCIgPT4gJG9iakRhdGFbInVzZXJuYW1lX2lkIl0sICJfY3NyZnRva2VuIiA9PiAkb2JqRGF0YVsidG9rZW4iXSwgImNvbW1lbnRfdGV4dCIgPT4gJGNvbW1lbnRUZXh0c1skY29tbWVudEluZGV4XSApKTsNCiAgICAgICAgICAgICAgICAgICAgJGhlYWRlcnMgPSBhcnJheSggIkNvbm5lY3Rpb246IGNsb3NlIiwgIkFjY2VwdDogKi8qIiwgIlgtSUctQ2FwYWJpbGl0aWVzOiAiIC4gQ29uc3RhbnRzOjpYX0lHX0NhcGFiaWxpdGllcywgIlgtSUctQ29ubmVjdGlvbi1UeXBlOiBXSUZJIiwgIkNvbnRlbnQtdHlwZTogYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkOyBjaGFyc2V0PVVURi04IiwgIkFjY2VwdC1MYW5ndWFnZTogdHItVFIiLCAiWF9GT1JXQVJERURfRk9SOiAiIC4gJG9iakRhdGFbImlwIl0sICJSRU1PVEVfQUREUjogIiAuICRvYmpEYXRhWyJpcCJdICk7DQogICAgICAgICAgICAgICAgICAgICRvcHRpb25zID0gYXJyYXkoIENVUkxPUFRfVVNFUkFHRU5UID0+ICRvYmpEYXRhWyJ1c2VyX2FnZW50Il0sIENVUkxPUFRfUkVUVVJOVFJBTlNGRVIgPT4gdHJ1ZSwgQ1VSTE9QVF9GT0xMT1dMT0NBVElPTiA9PiB0cnVlLCBDVVJMT1BUX1ZFUkJPU0UgPT4gZmFsc2UsIENVUkxPUFRfU1NMX1ZFUklGWVBFRVIgPT4gZmFsc2UsIENVUkxPUFRfU1NMX1ZFUklGWUhPU1QgPT4gZmFsc2UsIENVUkxPUFRfQ09PS0lFRklMRSA9PiAkdGhpcy0+SUdEYXRhUGF0aCAuICRvYmpEYXRhWyJ1c2VybmFtZSJdIC4gIi5kYXQiLCBDVVJMT1BUX0NPT0tJRUpBUiA9PiAkdGhpcy0+SUdEYXRhUGF0aCAuICRvYmpEYXRhWyJ1c2VybmFtZSJdIC4gIi5kYXQiICk7DQogICAgICAgICAgICAgICAgICAgICRyb2xsaW5nQ3VybC0+cG9zdChDb25zdGFudHM6OkFQSV9VUkwgLiAibWVkaWEvIiAuICRtZWRpYUlEIC4gIi9jb21tZW50LyIsIFNpZ25hdHVyZVV0aWxzOjpnZW5lcmF0ZVNpZ25hdHVyZSgkZGF0YSksICRoZWFkZXJzLCAkb3B0aW9ucywgJHVzZXJbImRhdGEiXSk7DQogICAgICAgICAgICAgICAgfQ0KDQogICAgICAgICAgICB9DQogICAgICAgICAgICAkcm9sbGluZ0N1cmwtPnNldENhbGxiYWNrKGZ1bmN0aW9uKFJvbGxpbmdDdXJsXFJlcXVlc3QgJHJlcXVlc3QsIFJvbGxpbmdDdXJsXFJvbGxpbmdDdXJsICRyb2xsaW5nQ3VybCkgdXNlICgmJHRyaWVkVXNlcnMsICYkdG90YWxTdWNjZXNzQ291bnQpDQp7DQogICAgJHRyaWVkVXNlciA9IGFycmF5KCAidXNlcklEIiA9PiAkcmVxdWVzdC0+aWRlbnRpZmllclBhcmFtc1sidXllSUQiXSwgImluc3RhSUQiID0+ICRyZXF1ZXN0LT5pZGVudGlmaWVyUGFyYW1zWyJpbnN0YUlEIl0sICJ1c2VyTmljayIgPT4gJHJlcXVlc3QtPmlkZW50aWZpZXJQYXJhbXNbImt1bGxhbmljaUFkaSJdICk7DQogICAgJGlzRXJyb3JlZCA9ICRyZXF1ZXN0LT5nZXRSZXNwb25zZUVycm9yKCk7DQogICAgaWYoIGVtcHR5KCRpc0Vycm9yZWQpICkgDQogICAgew0KICAgICAgICAkZG9uZW5Tb251YyA9IGpzb25fZGVjb2RlKCRyZXF1ZXN0LT5nZXRSZXNwb25zZVRleHQoKSwgdHJ1ZSk7DQogICAgICAgIGlmKCAkZG9uZW5Tb251YyApIA0KICAgICAgICB7DQogICAgICAgICAgICBpZiggJHJlcXVlc3QtPmlkZW50aWZpZXJQYXJhbXNbImlzV2ViQ29va2llIl0gPT0gMSApIA0KICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgIGlmKCBpc3NldCgkZG9uZW5Tb251Y1sic3RhdHVzIl0pICYmICRkb25lblNvbnVjWyJzdGF0dXMiXSA9PSAib2siICkgDQogICAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICAgICAkdG90YWxTdWNjZXNzQ291bnQrKzsNCiAgICAgICAgICAgICAgICAgICAgJHRyaWVkVXNlclsic3RhdHVzIl0gPSAic3VjY2VzcyI7DQogICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgIGVsc2UNCiAgICAgICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgICAgICR0cmllZFVzZXJbInN0YXR1cyJdID0gImZhaWwiOw0KICAgICAgICAgICAgICAgIH0NCg0KICAgICAgICAgICAgfQ0KICAgICAgICAgICAgZWxzZQ0KICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgIGlmKCBpc3NldCgkZG9uZW5Tb251Y1sic3RhdHVzIl0pICYmICRkb25lblNvbnVjWyJzdGF0dXMiXSA9PSAib2siICkgDQogICAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICAgICAkdG90YWxTdWNjZXNzQ291bnQrKzsNCiAgICAgICAgICAgICAgICAgICAgJHRyaWVkVXNlclsic3RhdHVzIl0gPSAic3VjY2VzcyI7DQogICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgIGVsc2UNCiAgICAgICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgICAgICR0cmllZFVzZXJbInN0YXR1cyJdID0gImZhaWwiOw0KICAgICAgICAgICAgICAgIH0NCg0KICAgICAgICAgICAgfQ0KDQogICAgICAgIH0NCg0KICAgIH0NCiAgICBlbHNlDQogICAgew0KICAgICAgICAkdHJpZWRVc2VyWyJzdGF0dXMiXSA9ICJmYWlsIjsNCiAgICB9DQoNCiAgICAkdHJpZWRVc2Vyc1tdID0gJHRyaWVkVXNlcjsNCiAgICAkcm9sbGluZ0N1cmwtPmNsZWFyQ29tcGxldGVkKCk7DQogICAgJHJvbGxpbmdDdXJsLT5wcnVuZVBlbmRpbmdSZXF1ZXN0UXVldWUoKTsNCn0NCg0KKTsNCiAgICAgICAgICAgICRyb2xsaW5nQ3VybC0+c2V0U2ltdWx0YW5lb3VzTGltaXQoJHRoaXMtPnNpbXVsdGFuZXBvc3RzaXplKTsNCiAgICAgICAgICAgICRyb2xsaW5nQ3VybC0+ZXhlY3V0ZSgpOw0KICAgICAgICB9DQoNCiAgICAgICAgcmV0dXJuIGFycmF5KCAidG90YWxTdWNjZXNzQ291bnQiID0+ICR0b3RhbFN1Y2Nlc3NDb3VudCwgInVzZXJzIiA9PiAkdHJpZWRVc2VycyApOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAgICAgKiBAcmV0dXJuIGFycmF5DQogICAgICAgICAqLw0KDQogICAgcHVibGljIGZ1bmN0aW9uIHZhbGlkYXRlKCkNCiAgICB7DQogICAgICAgICR0b3RhbFN1Y2Nlc3NDb3VudCA9IDA7DQogICAgICAgICR0cmllZFVzZXJzID0gYXJyYXkoKTsNCiAgICAgICAgJHJvbGxpbmdDdXJsID0gbmV3IFJvbGxpbmdDdXJsXFJvbGxpbmdDdXJsKCk7DQogICAgICAgIGZvcmVhY2goICR0aGlzLT51c2VycyBhcyAkdXNlciApIA0KICAgICAgICB7DQogICAgICAgICAgICBpZiggJHVzZXJbImRhdGEiXVsiaXNXZWJDb29raWUiXSA9PSAxICkgDQogICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgJG9iakluc3RhZ3JhbVdlYiA9ICR1c2VyWyJvYmplY3QiXTsNCiAgICAgICAgICAgICAgICAkb2JqRGF0YSA9ICRvYmpJbnN0YWdyYW1XZWItPmdldERhdGEoKTsNCiAgICAgICAgICAgICAgICAkaGVhZGVycyA9IGFycmF5KCAiUmVmZXJlcjogaHR0cHM6Ly93d3cuaW5zdGFncmFtLmNvbS9pbnN0YWdyYW0vIiwgIkROVDogMSIsICJPcmlnaW46IGh0dHBzOi8vd3d3Lmluc3RhZ3JhbS5jb20vIiwgIlgtQ1NSRlRva2VuOiAiIC4gdHJpbSgkb2JqRGF0YVsidG9rZW4iXSksICJYLVJlcXVlc3RlZC1XaXRoOiBYTUxIdHRwUmVxdWVzdCIsICJYLUluc3RhZ3JhbS1BSkFYOiAxIiwgIkNvbm5lY3Rpb246IGNsb3NlIiwgIkNhY2hlLUNvbnRyb2w6IG1heC1hZ2U9MCIsICJBY2NlcHQ6ICovKiIsICJBY2NlcHQtTGFuZ3VhZ2U6IHRyO3E9MSIsICJYX0ZPUldBUkRFRF9GT1I6ICIgLiAkb2JqRGF0YVsiaXAiXSwgIlJFTU9URV9BRERSOiAiIC4gJG9iakRhdGFbImlwIl0gKTsNCiAgICAgICAgICAgICAgICAkb3B0aW9ucyA9IGFycmF5KCBDVVJMT1BUX1VTRVJBR0VOVCA9PiBDb25zdGFudHM6OldFQl9VU0VSX0FHRU5ULCBDVVJMT1BUX1JFVFVSTlRSQU5TRkVSID0+IHRydWUsIENVUkxPUFRfRk9MTE9XTE9DQVRJT04gPT4gdHJ1ZSwgQ1VSTE9QVF9WRVJCT1NFID0+IGZhbHNlLCBDVVJMT1BUX1NTTF9WRVJJRllQRUVSID0+IGZhbHNlLCBDVVJMT1BUX1NTTF9WRVJJRllIT1NUID0+IGZhbHNlLCBDVVJMT1BUX0NPT0tJRUZJTEUgPT4gJHRoaXMtPklHRGF0YVBhdGggLiAkb2JqRGF0YVsidXNlcm5hbWUiXSAuICIuZGF0IiwgQ1VSTE9QVF9DT09LSUVKQVIgPT4gJHRoaXMtPklHRGF0YVBhdGggLiAkb2JqRGF0YVsidXNlcm5hbWUiXSAuICIuZGF0IiApOw0KICAgICAgICAgICAgICAgICRyb2xsaW5nQ3VybC0+cG9zdChDb25zdGFudHM6OldFQl9VUkwgLiAiYWNjb3VudHMvYWN0aXZpdHkvP19fYT0xIiwgTlVMTCwgJGhlYWRlcnMsICRvcHRpb25zLCAkdXNlclsiZGF0YSJdKTsNCiAgICAgICAgICAgIH0NCiAgICAgICAgICAgIGVsc2UNCiAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAkb2JqSW5zdGFncmFtID0gJHVzZXJbIm9iamVjdCJdOw0KICAgICAgICAgICAgICAgICRvYmpEYXRhID0gJG9iakluc3RhZ3JhbS0+Z2V0RGF0YSgpOw0KICAgICAgICAgICAgICAgICRoZWFkZXJzID0gYXJyYXkoICJDb25uZWN0aW9uOiBjbG9zZSIsICJBY2NlcHQ6ICovKiIsICJYLUlHLUNhcGFiaWxpdGllczogIiAuIENvbnN0YW50czo6WF9JR19DYXBhYmlsaXRpZXMsICJYLUlHLUNvbm5lY3Rpb24tVHlwZTogV0lGSSIsICJDb250ZW50LXR5cGU6IGFwcGxpY2F0aW9uL3gtd3d3LWZvcm0tdXJsZW5jb2RlZDsgY2hhcnNldD1VVEYtOCIsICJBY2NlcHQtTGFuZ3VhZ2U6IHRyLVRSIiwgIlhfRk9SV0FSREVEX0ZPUjogIiAuICRvYmpEYXRhWyJpcCJdLCAiUkVNT1RFX0FERFI6ICIgLiAkb2JqRGF0YVsiaXAiXSApOw0KICAgICAgICAgICAgICAgICRvcHRpb25zID0gYXJyYXkoIENVUkxPUFRfVVNFUkFHRU5UID0+ICRvYmpEYXRhWyJ1c2VyX2FnZW50Il0sIENVUkxPUFRfUkVUVVJOVFJBTlNGRVIgPT4gdHJ1ZSwgQ1VSTE9QVF9GT0xMT1dMT0NBVElPTiA9PiB0cnVlLCBDVVJMT1BUX1ZFUkJPU0UgPT4gZmFsc2UsIENVUkxPUFRfU1NMX1ZFUklGWVBFRVIgPT4gZmFsc2UsIENVUkxPUFRfU1NMX1ZFUklGWUhPU1QgPT4gZmFsc2UsIENVUkxPUFRfQ09PS0lFRklMRSA9PiAkdGhpcy0+SUdEYXRhUGF0aCAuICRvYmpEYXRhWyJ1c2VybmFtZSJdIC4gIi5kYXQiLCBDVVJMT1BUX0NPT0tJRUpBUiA9PiAkdGhpcy0+SUdEYXRhUGF0aCAuICRvYmpEYXRhWyJ1c2VybmFtZSJdIC4gIi5kYXQiICk7DQogICAgICAgICAgICAgICAgJHJvbGxpbmdDdXJsLT5nZXQoQ29uc3RhbnRzOjpBUElfVVJMIC4gImZlZWQvdGltZWxpbmUvP3JhbmtfdG9rZW49IiAuICRvYmpEYXRhWyJyYW5rX3Rva2VuIl0gLiAiJnJhbmtlZF9jb250ZW50PXRydWUiLCAkaGVhZGVycywgJG9wdGlvbnMsICR1c2VyWyJkYXRhIl0pOw0KICAgICAgICAgICAgfQ0KDQogICAgICAgIH0NCiAgICAgICAgJHJvbGxpbmdDdXJsLT5zZXRDYWxsYmFjayhmdW5jdGlvbihSb2xsaW5nQ3VybFxSZXF1ZXN0ICRyZXF1ZXN0LCBSb2xsaW5nQ3VybFxSb2xsaW5nQ3VybCAkcm9sbGluZ0N1cmwpIHVzZSAoJiR0cmllZFVzZXJzLCAmJHRvdGFsU3VjY2Vzc0NvdW50KQ0Kew0KICAgICR0cmllZFVzZXIgPSBhcnJheSggInVzZXJJRCIgPT4gJHJlcXVlc3QtPmlkZW50aWZpZXJQYXJhbXNbInV5ZUlEIl0sICJpbnN0YUlEIiA9PiAkcmVxdWVzdC0+aWRlbnRpZmllclBhcmFtc1siaW5zdGFJRCJdLCAidXNlck5pY2siID0+ICRyZXF1ZXN0LT5pZGVudGlmaWVyUGFyYW1zWyJrdWxsYW5pY2lBZGkiXSApOw0KICAgICRpc0Vycm9yZWQgPSAkcmVxdWVzdC0+Z2V0UmVzcG9uc2VFcnJvcigpOw0KICAgIGlmKCBlbXB0eSgkaXNFcnJvcmVkKSApIA0KICAgIHsNCiAgICAgICAgJGRvbmVuU29udWMgPSBqc29uX2RlY29kZSgkcmVxdWVzdC0+Z2V0UmVzcG9uc2VUZXh0KCksIHRydWUpOw0KICAgICAgICBpZiggJGRvbmVuU29udWMgKSANCiAgICAgICAgew0KICAgICAgICAgICAgaWYoICRyZXF1ZXN0LT5pZGVudGlmaWVyUGFyYW1zWyJpc1dlYkNvb2tpZSJdID09IDEgKSANCiAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAkcmVzcG9uc2VEYXRhID0gJHJlcXVlc3QtPmdldFJlc3BvbnNlSW5mbygpOw0KICAgICAgICAgICAgICAgIGlmKCAkcmVzcG9uc2VEYXRhWyJodHRwX2NvZGUiXSA9PSAiMjAwIiApIA0KICAgICAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAgICAgJHRvdGFsU3VjY2Vzc0NvdW50Kys7DQogICAgICAgICAgICAgICAgICAgICR0cmllZFVzZXJbInN0YXR1cyJdID0gInN1Y2Nlc3MiOw0KICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICBlbHNlDQogICAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICAgICAkdHJpZWRVc2VyWyJzdGF0dXMiXSA9ICJmYWlsIjsNCiAgICAgICAgICAgICAgICB9DQoNCiAgICAgICAgICAgIH0NCiAgICAgICAgICAgIGVsc2UNCiAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICBpZiggJGRvbmVuU29udWNbInN0YXR1cyJdID09ICJvayIgKSANCiAgICAgICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgICAgICR0b3RhbFN1Y2Nlc3NDb3VudCsrOw0KICAgICAgICAgICAgICAgICAgICAkdHJpZWRVc2VyWyJzdGF0dXMiXSA9ICJzdWNjZXNzIjsNCiAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICAgZWxzZQ0KICAgICAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAgICAgJHRyaWVkVXNlclsic3RhdHVzIl0gPSAiZmFpbCI7DQogICAgICAgICAgICAgICAgfQ0KDQogICAgICAgICAgICB9DQoNCiAgICAgICAgfQ0KDQogICAgfQ0KICAgIGVsc2UNCiAgICB7DQogICAgICAgICR0cmllZFVzZXJbInN0YXR1cyJdID0gImZhaWwiOw0KICAgIH0NCg0KICAgICR0cmllZFVzZXJzW10gPSAkdHJpZWRVc2VyOw0KICAgICRyb2xsaW5nQ3VybC0+Y2xlYXJDb21wbGV0ZWQoKTsNCiAgICAkcm9sbGluZ0N1cmwtPnBydW5lUGVuZGluZ1JlcXVlc3RRdWV1ZSgpOw0KfQ0KDQopOw0KICAgICAgICAkcm9sbGluZ0N1cmwtPnNldFNpbXVsdGFuZW91c0xpbWl0KCR0aGlzLT5zaW11bHRhbmVwb3N0c2l6ZSk7DQogICAgICAgICRyb2xsaW5nQ3VybC0+ZXhlY3V0ZSgpOw0KICAgICAgICByZXR1cm4gYXJyYXkoICJ0b3RhbFN1Y2Nlc3NDb3VudCIgPT4gJHRvdGFsU3VjY2Vzc0NvdW50LCAidXNlcnMiID0+ICR0cmllZFVzZXJzICk7DQogICAgfQ0KDQp9")); ?>
Function Calls
| base64_decode | 1 |
Stats
| MD5 | 7ed2784b2052ebc6afeae5170a52fbee |
| Eval Count | 1 |
| Decode Time | 224 ms |