Make a PoC (interop with omnisette is broken, idk why)

This commit is contained in:
Dadoum 2023-06-14 21:53:52 +02:00
parent e536763d41
commit d5bd19baf2
4 changed files with 197 additions and 468 deletions

View File

@ -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"
}

View File

@ -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"
}
}

View File

@ -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 = "<MacBookPro13,2> <macOS;13.1;22C65> <com.apple.AuthKit/1 (com.apple.dt.Xcode/3594.4.19)>";
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();
});
}
}
}

View File

@ -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 = "<MacBookPro13,2> <macOS;13.1;22C65> <com.apple.AuthKit/1 (com.apple.dt.Xcode/3594.4.19)>";
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 = "<MacBookPro13,2> <macOS;13.1;22C65> <com.apple.AuthKit/1 (com.apple.dt.Xcode/3594.4.19)>";
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) {
try {
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");
auto json = parseJSON(context.getStringBody());
string identifier = json["identifier"].str();
string identifier = "(null)";
try {
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,20 +219,131 @@ 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();
@Get("v3/provisioning_session") class ProvisioningSocket : WebSocket {
SocketProvisioningSessionState state;
ADI adi;
uint session;
string ip;
void onConnect(ServerRequest request) {
getLogger().info("[<<] anisette-v3 /v3/provisioning_session open");
state = SocketProvisioningSessionState.waitingForIdentifier;
adi = null;
session = 0;
ip = "";
JSONValue giveIdentifier = [
"result": "GiveIdentifier"
];
send(giveIdentifier.toString(JSONOptions.doNotEscapeSlashes));
}
override void onClose() {
getLogger().infoF!("[<< %s] anisette-v3 /v3/provisioning_session close")(ip);
}
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")
)
)
];
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();
// });
}
}
}
}
enum SocketProvisioningSessionState {
@ -213,103 +351,3 @@ enum SocketProvisioningSessionState {
waitingForStartProvisioningData,
waitingForEndProvisioning,
}
struct SocketProvisioningSession {
SocketProvisioningSessionState state;
ADI adiHandle;
uint session;
}
class SocketProvisioningSessionHandler: AbstractWebSocketMessageHandler {
SocketProvisioningSession[WebSocketConnection] sessions;
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());
}
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 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();
});
}
}
}