diff --git a/dub.json b/dub.json index 7ca6059..990c0d1 100644 --- a/dub.json +++ b/dub.json @@ -6,10 +6,10 @@ ], "dependencies": { - "hunt-http": "~>0.8.2", + "lighttp": "~>0.5.4", "provision": { "repository": "git+https://github.com/Dadoum/Provision.git", - "version": "e3560d82997768a39550a773a75345a015b96548" + "version": "e302d138ce949e361b2ed939be82efe8bb15aab1" }, "slf4d": "~>2.1.1" } diff --git a/dub.selections.json b/dub.selections.json index 07ebab8..fcc23ed 100644 --- a/dub.selections.json +++ b/dub.selections.json @@ -7,8 +7,14 @@ "hunt-http": "0.8.2", "hunt-net": "0.7.1", "hunt-openssl": "1.0.5", + "libasync": "0.9.2", + "lighttp": "0.5.4", + "memutils": "1.0.9", "plist": "~master", - "provision": {"version":"e3560d82997768a39550a773a75345a015b96548","repository":"git+https://github.com/Dadoum/Provision.git"}, - "slf4d": "2.1.1" + "plist-d": {"version":"d494cf3fe79a2bb20583173c0c8cf85ef33b719e","repository":"git+https://github.com/Dadoum/libplist-d.git"}, + "provision": {"version":"e302d138ce949e361b2ed939be82efe8bb15aab1","repository":"git+https://github.com/Dadoum/Provision.git"}, + "slf4d": "2.1.1", + "urld": "2.1.1", + "xbuffer": "1.0.0" } } diff --git a/out/production/anisette-v3-server/app.d b/out/production/anisette-v3-server/app.d deleted file mode 100644 index a7fb217..0000000 --- a/out/production/anisette-v3-server/app.d +++ /dev/null @@ -1,315 +0,0 @@ -import std.algorithm.searching; -import std.array; -import std.base64; -import std.digest; -import file = std.file; -import std.format; -import std.getopt; -import std.json; -import std.math; -import std.net.curl; -import std.parallelism; -import std.path; -import std.uni; -import std.zip; - -import hunt.http; - -import slf4d; -import slf4d.default_provider; - -import provision; - -__gshared ADI adi; -__gshared Device device; -__gshared string libraryPath; - -void main(string[] args) -{ - debug { - configureLoggingProvider(new shared DefaultProvider(true, Levels.DEBUG)); - } else { - configureLoggingProvider(new shared DefaultProvider(true, Levels.INFO)); - } - - enum brandingCode = format!"anisette-v3-server v%s"(provisionVersion); - enum clientInfo = " "; - - Logger log = getLogger(); - log.info(brandingCode); - string hostname = "0.0.0.0"; - ushort port = 6969; - - string configurationPath = expandTilde("~/.config/anisette-v3"); - auto helpInformation = getopt( - args, - "n|host", format!"The hostname to bind to (default: %s)"(hostname), &hostname, - "p|port", format!"The port to bind to (default: %s)"(port), &port, - "a|adi-path", format!"Where the provisioning information should be stored on the computer for anisette-v1 backwards compat (default: %s)"(configurationPath), &configurationPath, - ); - - if (helpInformation.helpWanted) { - defaultGetoptPrinter("anisette-server with v3 support", helpInformation.options); - return; - } - - if (!file.exists(configurationPath)) { - file.mkdirRecurse(configurationPath); - } - - libraryPath = configurationPath.buildPath("lib"); - - string provisioningPathV3 = file.getcwd().buildPath("provisioning"); - - if (!file.exists(provisioningPathV3)) { - file.mkdir(provisioningPathV3); - } - - auto coreADIPath = libraryPath.buildPath("libCoreADI.so"); - auto SSCPath = libraryPath.buildPath("libstoreservicescore.so"); - - if (!(file.exists(coreADIPath) && file.exists(SSCPath))) { - auto http = HTTP(); - log.info("Downloading libraries from Apple servers..."); - auto apkData = get!(HTTP, ubyte)("https://apps.mzstatic.com/content/android-apple-music-apk/applemusic.apk", http); - log.info("Done !"); - auto apk = new ZipArchive(apkData); - auto dir = apk.directory(); - - if (!file.exists(libraryPath)) { - file.mkdirRecurse(libraryPath); - } - - version (X86_64) { - enum string architectureIdentifier = "x86_64"; - } else version (X86) { - enum string architectureIdentifier = "x86"; - } else version (AArch64) { - enum string architectureIdentifier = "arm64-v8a"; - } else version (ARM) { - enum string architectureIdentifier = "armeabi-v7a"; - } else { - static assert(false, "Architecture not supported :("); - } - - file.write(coreADIPath, apk.expand(dir["lib/" ~ architectureIdentifier ~ "/libCoreADI.so"])); - file.write(SSCPath, apk.expand(dir["lib/" ~ architectureIdentifier ~ "/libstoreservicescore.so"])); - } - - // Initializing ADI and machine if it has not already been made. - device = new Device(configurationPath.buildPath("device.json")); - adi = new ADI(libraryPath); - adi.provisioningPath = configurationPath; - - if (!device.initialized) { - log.info("Creating machine... "); - - import std.digest; - import std.random; - import std.range; - import std.uni; - import std.uuid; - device.serverFriendlyDescription = clientInfo; - device.uniqueDeviceIdentifier = randomUUID().toString().toUpper(); - device.adiIdentifier = (cast(ubyte[]) rndGen.take(2).array()).toHexString().toLower(); - device.localUserUUID = (cast(ubyte[]) rndGen.take(8).array()).toHexString().toUpper(); - - log.info("Machine creation done!"); - } - - enum dsId = -2; - - adi.identifier = device.adiIdentifier; - if (!adi.isMachineProvisioned(dsId)) { - log.info("Machine requires provisioning... "); - - ProvisioningSession provisioningSession = new ProvisioningSession(adi, device); - provisioningSession.provision(dsId); - log.info("Provisioning done!"); - } - - auto server = HttpServer.builder() - .setListener(port, hostname) - .addRoute("/", (RoutingContext context) { - import std.datetime.systime; - import std.datetime.timezone; - import core.time; - log.info("[<<] anisette-v1 request"); - auto time = Clock.currTime(); - - auto otp = adi.requestOTP(dsId); - - import std.conv; - import std.json; - - JSONValue response = [ - "X-Apple-I-Client-Time": time.toISOExtString.split('.')[0] ~ "Z", - "X-Apple-I-MD": Base64.encode(otp.oneTimePassword), - "X-Apple-I-MD-M": Base64.encode(otp.machineIdentifier), - "X-Apple-I-MD-RINFO": to!string(17106176), - "X-Apple-I-MD-LU": device.localUserUUID, - "X-Apple-I-SRL-NO": "0", - "X-MMe-Client-Info": device.serverFriendlyDescription, - "X-Apple-I-TimeZone": time.timezone.dstName, - "X-Apple-Locale": "en_US", - "X-Mme-Device-Id": device.uniqueDeviceIdentifier, - ]; - context.responseHeader(HttpHeader.CONTENT_TYPE, "application/json"); - context.responseHeader("Implementation-Version", brandingCode); - context.write(response.toString(JSONOptions.doNotEscapeSlashes)); - context.end(); - log.infoF!"[>>] 200 OK %s"(response); - }) - .addRoute("/v3/client_info", (RoutingContext context) { - log.info("[<<] anisette-v3 /v3/client_info"); - JSONValue response = [ - "client_info": clientInfo, - "user_agent": "akd/1.0 CFNetwork/1404.0.5 Darwin/22.3.0" - ]; - context.write(response.toString()); - context.end(); - }) - .addRoute("/v3/get_headers", (RoutingContext context) { - try { - log.info("[<<] anisette-v3 /v3/get_headers"); - auto json = parseJSON(context.getStringBody()); - string identifier = json["identifier"].str(); - ubyte[] adi_pb = Base64.decode(json["adi_pb"].str()); - auto provisioningPath = file.getcwd() - .buildPath("provisioning") - .buildPath(adi.identifier); - file.mkdir(provisioningPath); - file.write(provisioningPath.buildPath("adi.pb"), adi_pb); - ADI adi = new ADI(libraryPath); - adi.provisioningPath = provisioningPath; - - auto otp = adi.requestOTP(dsId); - file.rmdirRecurse(provisioningPath); - - JSONValue response = [ // Provision does no longer have a concept of 'request headers' - "result": "Headers", - "X-Apple-I-MD": Base64.encode(otp.oneTimePassword), - "X-Apple-I-MD-M": Base64.encode(otp.machineIdentifier), - "X-Apple-I-MD-RINFO": "17106176", - ]; - context.write(response.toString()); - } catch (Throwable t) { - JSONValue error = [ - "result": "GetHeadersError", - "message": typeid(t).name ~ ": " ~ t.msg - ]; - context.write(error.toString()); - } finally { - context.end(); - } - }) - .websocket("/v3/provisioning_session", new SocketProvisioningSessionHandler()).build(); - - server.start(); -} - -enum SocketProvisioningSessionState { - waitingForIdentifier, - waitingForStartProvisioningData, - waitingForEndProvisioning, -} - -struct SocketProvisioningSession { - SocketProvisioningSessionState state; - ADI adiHandle; - uint session; -} - -class SocketProvisioningSessionHandler: AbstractWebSocketMessageHandler { - SocketProvisioningSession[WebSocketConnection] sessions; - - override void onOpen(WebSocketConnection connection) { - log.info("[<<] anisette-v3 /v3/provisioning_session open"); - sessions[connection] = SocketProvisioningSession(SocketProvisioningSessionState.waitingForIdentifier, null, 0); - JSONValue giveIdentifier = [ - "result": "GiveIdentifier" - ]; - connection.sendText(giveIdentifier.toString()); - } - - override void onClosed(WebSocketConnection connection) { - scope(exit) log.info("[<<] anisette-v3 /v3/provisioning_session close"); - auto session = connection in sessions; - if (session) { - if (session.adiHandle) { - ADI adi = session.adiHandle; - if (file.exists( - file.getcwd() - .buildPath("provisioning") - .buildPath(adi.identifier) - )) { - file.rmdirRecurse( - file.getcwd() - .buildPath("provisioning") - .buildPath(adi.identifier) - ); - } - } - sessions.remove(connection); - } - } - - override void onText(WebSocketConnection connection, string text) { - try { - auto session = connection in sessions; - final switch (session.state) with (SocketProvisioningSessionState) { - case waitingForIdentifier: - auto res = parseJSON(text); - string requestedIdentifier = res["identifier"].str(); - auto adi = new ADI(libraryPath); - adi.identifier = requestedIdentifier; - adi.provisioningPath = file.getcwd().buildPath("provisioning").buildPath(requestedIdentifier); - session.adiHandle = adi; - session.state = waitingForStartProvisioningData; - break; - case waitingForStartProvisioningData: - auto res = parseJSON(text); - string spim = res["spim"].str(); - auto adi = session.adiHandle; - auto cpimAndCo = adi.startProvisioning(-2, Base64.decode(spim)); - session.session = cpimAndCo.session; - session.state = waitingForEndProvisioning; - JSONValue response = [ - "result": "GiveEndProvisioningData", - "message": Base64.encode(cpimAndCo.clientProvisioningIntermediateMetadata) - ]; - connection.sendText(response.toString()); - break; - case waitingForEndProvisioning: - auto res = parseJSON(text); - string ptm = res["ptm"].str(); - string tk = res["tk"].str(); - auto adi = session.adiHandle; - adi.endProvisioning(-2, Base64.decode(ptm), Base64.decode(tk)); - JSONValue response = [ - "result": "ProvisioningSuccess", - "message": Base64.encode( - cast(ubyte[]) file.read( - file.getcwd() - .buildPath("provisioning") - .buildPath(adi.identifier) - .buildPath("adi.pb") - ) - ) - ]; - connection.sendText(response.toString()).then((_) { - connection.close(); - }); - break; - } - } catch (Throwable t) { - JSONValue error = [ - "result": "NonStandard-" ~ typeid(t).name, - "message": t.msg - ]; - connection.sendText(error.toString()).then((_) { - connection.close(); - }); - } - } -} diff --git a/source/app.d b/source/app.d index a971aa8..8082b8a 100644 --- a/source/app.d +++ b/source/app.d @@ -11,19 +11,25 @@ import std.net.curl; import std.parallelism; import std.path; import std.uni; +import std.uuid; import std.zip; -import hunt.http; +import lighttp; import slf4d; import slf4d.default_provider; import provision; -__gshared ADI adi; -__gshared Device device; __gshared string libraryPath; +enum brandingCode = format!"anisette-v3-server v%s"(provisionVersion); +enum clientInfo = " "; +enum dsId = -2; + +__gshared ADI v1Adi; +__gshared Device v1Device; + void main(string[] args) { debug { @@ -32,8 +38,6 @@ void main(string[] args) configureLoggingProvider(new shared DefaultProvider(true, Levels.INFO)); } - enum brandingCode = format!"anisette-v3-server v%s"(provisionVersion); - enum clientInfo = " "; Logger log = getLogger(); log.info(brandingCode); @@ -97,91 +101,114 @@ void main(string[] args) } // Initializing ADI and machine if it has not already been made. - device = new Device(configurationPath.buildPath("device.json")); - adi = new ADI(libraryPath); - adi.provisioningPath = configurationPath; + v1Device = new Device(configurationPath.buildPath("device.json")); + v1Adi = new ADI(libraryPath); + v1Adi.provisioningPath = configurationPath; - if (!device.initialized) { + if (!v1Device.initialized) { log.info("Creating machine... "); - import std.digest; import std.random; import std.range; - import std.uni; - import std.uuid; - device.serverFriendlyDescription = clientInfo; - device.uniqueDeviceIdentifier = randomUUID().toString().toUpper(); - device.adiIdentifier = (cast(ubyte[]) rndGen.take(2).array()).toHexString().toLower(); - device.localUserUUID = (cast(ubyte[]) rndGen.take(8).array()).toHexString().toUpper(); + v1Device.serverFriendlyDescription = clientInfo; + v1Device.uniqueDeviceIdentifier = randomUUID().toString().toUpper(); + v1Device.adiIdentifier = (cast(ubyte[]) rndGen.take(2).array()).toHexString().toLower(); + v1Device.localUserUUID = (cast(ubyte[]) rndGen.take(8).array()).toHexString().toUpper(); log.info("Machine creation done!"); } - enum dsId = -2; - - adi.identifier = device.adiIdentifier; - if (!adi.isMachineProvisioned(dsId)) { + v1Adi.identifier = v1Device.adiIdentifier; + if (!v1Adi.isMachineProvisioned(dsId)) { log.info("Machine requires provisioning... "); - ProvisioningSession provisioningSession = new ProvisioningSession(adi, device); + ProvisioningSession provisioningSession = new ProvisioningSession(v1Adi, v1Device); provisioningSession.provision(dsId); log.info("Provisioning done!"); } + Server server = new Server(); + server.host(hostname, port); + server.router.add(new AnisetteUnifiedServer()); + server.run(); + /+ auto server = HttpServer.builder() .setListener(port, hostname) .addRoute("/", (RoutingContext context) { + }) + .addRoute("/v3/client_info", (RoutingContext context) { + }) + .addRoute("/v3/get_headers", (RoutingContext context) { + }) + .websocket("/v3/provisioning_session", new SocketProvisioningSessionHandler()).build(); + + server.start(); ++/ +} + +class AnisetteUnifiedServer { + @Get("") handleV1Request(ServerResponse response) { import std.datetime.systime; import std.datetime.timezone; import core.time; + auto log = getLogger(); log.info("[<<] anisette-v1 request"); auto time = Clock.currTime(); - auto otp = adi.requestOTP(dsId); + auto otp = v1Adi.requestOTP(dsId); import std.conv; import std.json; - JSONValue response = [ + JSONValue responseJson = [ "X-Apple-I-Client-Time": time.toISOExtString.split('.')[0] ~ "Z", "X-Apple-I-MD": Base64.encode(otp.oneTimePassword), "X-Apple-I-MD-M": Base64.encode(otp.machineIdentifier), "X-Apple-I-MD-RINFO": to!string(17106176), - "X-Apple-I-MD-LU": device.localUserUUID, + "X-Apple-I-MD-LU": v1Device.localUserUUID, "X-Apple-I-SRL-NO": "0", - "X-MMe-Client-Info": device.serverFriendlyDescription, + "X-MMe-Client-Info": v1Device.serverFriendlyDescription, "X-Apple-I-TimeZone": time.timezone.dstName, "X-Apple-Locale": "en_US", - "X-Mme-Device-Id": device.uniqueDeviceIdentifier, + "X-Mme-Device-Id": v1Device.uniqueDeviceIdentifier, ]; - context.responseHeader(HttpHeader.CONTENT_TYPE, "application/json"); - context.responseHeader("Implementation-Version", brandingCode); - context.write(response.toString(JSONOptions.doNotEscapeSlashes)); - context.end(); - log.infoF!"[>>] 200 OK %s"(response); - }) - .addRoute("/v3/client_info", (RoutingContext context) { + + response.contentType = "application/json"; + response.headers["Implementation-Version"] = brandingCode; + response.body = responseJson.toString(JSONOptions.doNotEscapeSlashes); + log.infoF!"[>>] 200 OK %s"(responseJson); + } + + @Get("v3/client_info") void getClientInfo(ServerResponse response) { + auto log = getLogger(); log.info("[<<] anisette-v3 /v3/client_info"); - JSONValue response = [ + JSONValue responseJson = [ "client_info": clientInfo, - "user_agent": "akd/1.0 CFNetwork/1404.0.5 Darwin/22.3.0" + "user_agent": "akd/1.0 CFNetwork/808.1.4" ]; - context.write(response.toString()); - context.end(); - }) - .addRoute("/v3/get_headers", (RoutingContext context) { + + response.headers["Implementation-Version"] = brandingCode; + response.body = responseJson.toString(JSONOptions.doNotEscapeSlashes); + } + + @Post("v3/get_headers") void getHeaders(ServerResponse res, ServerRequest req) { + auto log = getLogger(); + log.info("[<<] anisette-v3 /v3/get_headers"); + string identifier = "(null)"; try { - log.info("[<<] anisette-v3 /v3/get_headers"); - auto json = parseJSON(context.getStringBody()); - string identifier = json["identifier"].str(); + import std.uuid; + auto json = parseJSON(req.body()); + ubyte[] identifierBytes = Base64.decode(json["identifier"].str()); ubyte[] adi_pb = Base64.decode(json["adi_pb"].str()); + identifier = UUID(identifierBytes[0..16]).toString(); auto provisioningPath = file.getcwd() .buildPath("provisioning") - .buildPath(adi.identifier); + .buildPath(identifier); file.mkdir(provisioningPath); file.write(provisioningPath.buildPath("adi.pb"), adi_pb); ADI adi = new ADI(libraryPath); adi.provisioningPath = provisioningPath; + adi.identifier = identifier.toUpper()[0..16]; auto otp = adi.requestOTP(dsId); file.rmdirRecurse(provisioningPath); @@ -192,124 +219,135 @@ void main(string[] args) "X-Apple-I-MD-M": Base64.encode(otp.machineIdentifier), "X-Apple-I-MD-RINFO": "17106176", ]; - context.write(response.toString()); + res.headers["Implementation-Version"] = brandingCode; + res.body = response.toString(JSONOptions.doNotEscapeSlashes); } catch (Throwable t) { JSONValue error = [ "result": "GetHeadersError", "message": typeid(t).name ~ ": " ~ t.msg ]; - context.write(error.toString()); + res.headers["Implementation-Version"] = brandingCode; + res.body = error.toString(JSONOptions.doNotEscapeSlashes); } finally { - context.end(); + if (file.exists( + file.getcwd() + .buildPath("provisioning") + .buildPath(identifier) + )) { + file.rmdirRecurse( + file.getcwd() + .buildPath("provisioning") + .buildPath(identifier) + ); + } } - }) - .websocket("/v3/provisioning_session", new SocketProvisioningSessionHandler()).build(); - - server.start(); -} + } -enum SocketProvisioningSessionState { - waitingForIdentifier, - waitingForStartProvisioningData, - waitingForEndProvisioning, -} + @Get("v3/provisioning_session") class ProvisioningSocket : WebSocket { + SocketProvisioningSessionState state; + ADI adi; + uint session; -struct SocketProvisioningSession { - SocketProvisioningSessionState state; - ADI adiHandle; - uint session; -} + string ip; -class SocketProvisioningSessionHandler: AbstractWebSocketMessageHandler { - SocketProvisioningSession[WebSocketConnection] sessions; + void onConnect(ServerRequest request) { + getLogger().info("[<<] anisette-v3 /v3/provisioning_session open"); + state = SocketProvisioningSessionState.waitingForIdentifier; + adi = null; + session = 0; + ip = ""; - override void onOpen(WebSocketConnection connection) { - getLogger().info("[<<] anisette-v3 /v3/provisioning_session open"); - sessions[connection] = SocketProvisioningSession(SocketProvisioningSessionState.waitingForIdentifier, null, 0); - JSONValue giveIdentifier = [ - "result": "GiveIdentifier" - ]; - connection.sendText(giveIdentifier.toString()); - } + JSONValue giveIdentifier = [ + "result": "GiveIdentifier" + ]; + send(giveIdentifier.toString(JSONOptions.doNotEscapeSlashes)); + } - override void onClosed(WebSocketConnection connection) { - scope(exit) getLogger().info("[<<] anisette-v3 /v3/provisioning_session close"); - auto session = connection in sessions; - if (session) { - if (session.adiHandle) { - ADI adi = session.adiHandle; - if (file.exists( - file.getcwd() - .buildPath("provisioning") - .buildPath(adi.identifier) - )) { - file.rmdirRecurse( - file.getcwd() - .buildPath("provisioning") - .buildPath(adi.identifier) - ); - } - } - sessions.remove(connection); + override void onClose() { + getLogger().infoF!("[<< %s] anisette-v3 /v3/provisioning_session close")(ip); } - } - override void onText(WebSocketConnection connection, string text) { - try { - auto session = connection in sessions; - final switch (session.state) with (SocketProvisioningSessionState) { - case waitingForIdentifier: - auto res = parseJSON(text); - string requestedIdentifier = res["identifier"].str(); - auto adi = new ADI(libraryPath); - adi.identifier = requestedIdentifier; - adi.provisioningPath = file.getcwd().buildPath("provisioning").buildPath(requestedIdentifier); - session.adiHandle = adi; - session.state = waitingForStartProvisioningData; - break; - case waitingForStartProvisioningData: - auto res = parseJSON(text); - string spim = res["spim"].str(); - auto adi = session.adiHandle; - auto cpimAndCo = adi.startProvisioning(-2, Base64.decode(spim)); - session.session = cpimAndCo.session; - session.state = waitingForEndProvisioning; - JSONValue response = [ - "result": "GiveEndProvisioningData", - "message": Base64.encode(cpimAndCo.clientProvisioningIntermediateMetadata) - ]; - connection.sendText(response.toString()); - break; - case waitingForEndProvisioning: - auto res = parseJSON(text); - string ptm = res["ptm"].str(); - string tk = res["tk"].str(); - auto adi = session.adiHandle; - adi.endProvisioning(-2, Base64.decode(ptm), Base64.decode(tk)); - JSONValue response = [ - "result": "ProvisioningSuccess", - "message": Base64.encode( - cast(ubyte[]) file.read( - file.getcwd() - .buildPath("provisioning") - .buildPath(adi.identifier) + override void onReceive(ubyte[] data) { + string text = cast(string) data; + auto log = getLogger(); + try { + final switch (state) with (SocketProvisioningSessionState) { + case waitingForIdentifier: + auto res = parseJSON(text); + ubyte[] requestedIdentifier = Base64.decode(res["identifier"].str()); + + if (requestedIdentifier.length != 16) { + JSONValue response = [ + "result": "InvalidIdentifier" + ]; + + log.infoF!("[>> %s] It is invalid.")(ip); + send(response.toString()); + return; + } + string identifier = UUID(requestedIdentifier[0..16]).toString(); + log.infoF!("[<< %s] Received an identifier (%s).")(ip, identifier); + + adi = new ADI(libraryPath); + adi.provisioningPath = file.getcwd().buildPath("provisioning").buildPath(identifier); + adi.identifier = identifier.toUpper()[0..16]; + state = waitingForStartProvisioningData; + JSONValue response = [ + "result": "GiveStartProvisioningData" + ]; + log.infoF!("[>> %s] Okay gimme spim.")(ip); + send(response.toString(JSONOptions.doNotEscapeSlashes)); + break; + case waitingForStartProvisioningData: + auto res = parseJSON(text); + string spim = res["spim"].str(); + log.infoF!("[<< %s] Received SPIM.")(ip); + auto cpimAndCo = adi.startProvisioning(-2, Base64.decode(spim)); + session = cpimAndCo.session; + state = waitingForEndProvisioning; + JSONValue response = [ + "result": "GiveEndProvisioningData", + "cpim": Base64.encode(cpimAndCo.clientProvisioningIntermediateMetadata) + ]; + log.infoF!("[>> %s] Okay gimme ptm tk.")(ip); + send(response.toString(JSONOptions.doNotEscapeSlashes)); + break; + case waitingForEndProvisioning: + auto res = parseJSON(text); + string ptm = res["ptm"].str(); + string tk = res["tk"].str(); + log.infoF!("[<< %s] Received PTM and TK.")(ip); + adi.endProvisioning(session, Base64.decode(ptm), Base64.decode(tk)); + JSONValue response = [ + "result": "ProvisioningSuccess", + "adi_pb": Base64.encode( + cast(ubyte[]) file.read( + adi.provisioningPath() .buildPath("adi.pb") + ) ) - ) - ]; - connection.sendText(response.toString()).then((_) { - connection.close(); - }); - break; + ]; + log.infoF!("[>> %s] Okay all right here is your provisioning data.")(ip); + send(response.toString(JSONOptions.doNotEscapeSlashes)); + break; + // +/ + } + } catch (Throwable t) { + JSONValue error = [ + "result": "NonStandard-" ~ typeid(t).name, + "message": t.msg + ]; + log.errorF!"[>>] anisette-v3 error: %s"(t); + // connection.sendText(error.toString()).then((_) { + // connection.close(); + // }); } - } catch (Throwable t) { - JSONValue error = [ - "result": "NonStandard-" ~ typeid(t).name, - "message": t.msg - ]; - connection.sendText(error.toString()).then((_) { - connection.close(); - }); } } } + +enum SocketProvisioningSessionState { + waitingForIdentifier, + waitingForStartProvisioningData, + waitingForEndProvisioning, +}