2017-02-14 00:52:27 +01:00
|
|
|
import std.stdio;
|
2017-02-14 01:57:14 +01:00
|
|
|
import std.conv;
|
2017-02-14 21:43:55 +01:00
|
|
|
import std.algorithm;
|
|
|
|
import core.time : msecs;
|
2017-02-14 02:47:23 +01:00
|
|
|
import core.sys.linux.unistd;
|
|
|
|
|
2017-02-14 00:52:27 +01:00
|
|
|
import vibe.d;
|
|
|
|
import vibe.data.json;
|
|
|
|
import vibe.textfilter.urlencode;
|
|
|
|
|
|
|
|
void helloWorld(HTTPServerRequest req,
|
|
|
|
HTTPServerResponse res)
|
|
|
|
{
|
|
|
|
res.writeBody("Hello");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class ApiRequest
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2017-02-14 01:57:14 +01:00
|
|
|
struct ApiResponse
|
|
|
|
{
|
|
|
|
ResponseSet[] responseSets;
|
|
|
|
bool pendingResponseSets;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct ResponseSet
|
|
|
|
{
|
|
|
|
string applicationId;
|
|
|
|
int requestCounter;
|
|
|
|
Json[] responses;
|
|
|
|
}
|
|
|
|
|
2017-02-14 00:52:27 +01:00
|
|
|
class OpenSession : ApiRequest
|
|
|
|
{
|
|
|
|
string applicationType;
|
|
|
|
string locale;
|
|
|
|
string[string] attributes;
|
|
|
|
|
|
|
|
this(string applicationType, string locale, string[string] attributes)
|
|
|
|
{
|
|
|
|
this.applicationType = applicationType;
|
|
|
|
this.locale = locale;
|
|
|
|
this.attributes = attributes;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-14 01:57:14 +01:00
|
|
|
class StartFlow : ApiRequest
|
|
|
|
{
|
|
|
|
string applicationType;
|
|
|
|
string applicationId;
|
|
|
|
string flowName;
|
|
|
|
string[string] attributes;
|
|
|
|
string[string] privateAttributes;
|
|
|
|
|
|
|
|
this(string applicationType, string applicationId, string flowName, string[string] attributes, string[string] privateAttributes)
|
|
|
|
{
|
|
|
|
this.applicationType = applicationType;
|
|
|
|
this.applicationId = applicationId;
|
|
|
|
this.flowName = flowName;
|
|
|
|
this.attributes = attributes;
|
|
|
|
this.privateAttributes = privateAttributes;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-14 02:47:23 +01:00
|
|
|
class WidgetEvents : ApiRequest
|
|
|
|
{
|
|
|
|
string applicationId;
|
2017-02-14 21:43:55 +01:00
|
|
|
Json[] widgetEventInformations;
|
2017-02-14 02:47:23 +01:00
|
|
|
|
2017-02-14 21:43:55 +01:00
|
|
|
this(string applicationId, WidgetEventInformation[] events)
|
2017-02-14 02:47:23 +01:00
|
|
|
{
|
|
|
|
this.applicationId = applicationId;
|
|
|
|
|
2017-02-14 21:43:55 +01:00
|
|
|
foreach(event; events)
|
2017-02-14 02:47:23 +01:00
|
|
|
{
|
2017-02-14 21:43:55 +01:00
|
|
|
widgetEventInformations ~= event.serializeToJson;
|
2017-02-14 02:47:23 +01:00
|
|
|
}
|
2017-02-14 21:43:55 +01:00
|
|
|
}
|
|
|
|
}
|
2017-02-14 02:47:23 +01:00
|
|
|
|
2017-02-14 21:43:55 +01:00
|
|
|
struct WidgetEventInformation
|
|
|
|
{
|
|
|
|
string widgetId;
|
|
|
|
string eventType;
|
|
|
|
string widgetType;
|
|
|
|
string[string] attributes;
|
|
|
|
}
|
|
|
|
|
|
|
|
class TechnicalRequest : ApiRequest
|
|
|
|
{
|
|
|
|
string requestType;
|
|
|
|
|
|
|
|
this(string requestType)
|
|
|
|
{
|
|
|
|
this.requestType = requestType;
|
2017-02-14 02:47:23 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-14 00:52:27 +01:00
|
|
|
class ApiMessage
|
|
|
|
{
|
|
|
|
string executionMode;
|
|
|
|
string protocolVersion;
|
|
|
|
Json[string][] requests;
|
|
|
|
|
|
|
|
this(string executionMode)
|
|
|
|
{
|
|
|
|
this.executionMode = executionMode;
|
|
|
|
this.protocolVersion = "2.1";
|
|
|
|
|
|
|
|
Json[string] requestArray;
|
|
|
|
this.requests = [requestArray];
|
|
|
|
}
|
|
|
|
|
|
|
|
void addRequest(T : ApiRequest)(string name, T request)
|
|
|
|
{
|
|
|
|
requests[0][name] = request.serializeToJson;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-14 01:57:14 +01:00
|
|
|
class SessionApiMessage : ApiMessage
|
|
|
|
{
|
|
|
|
string sessionId;
|
|
|
|
string applicationId;
|
|
|
|
string requestCounter;
|
|
|
|
|
|
|
|
this(string executionMode, string sessionId, string applicationId, int requestCounter)
|
|
|
|
{
|
|
|
|
super(executionMode);
|
|
|
|
this.sessionId = sessionId;
|
|
|
|
this.applicationId = applicationId;
|
|
|
|
this.requestCounter = requestCounter.to!string;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-14 00:52:27 +01:00
|
|
|
class ApiClient
|
|
|
|
{
|
2017-02-14 02:47:23 +01:00
|
|
|
enum string Endpoint = "https://m.belfius.be/F2CRenderingDexiaToClient/GEPARendering/machineIdentifier=s4/";
|
2017-02-14 00:52:27 +01:00
|
|
|
|
|
|
|
private string _locale;
|
|
|
|
|
2017-02-14 01:57:14 +01:00
|
|
|
private string _sessionId;
|
|
|
|
private int _requestCounter;
|
|
|
|
|
2017-02-14 00:52:27 +01:00
|
|
|
this(string locale)
|
|
|
|
{
|
|
|
|
_locale = locale;
|
|
|
|
}
|
|
|
|
|
|
|
|
void connect()
|
|
|
|
{
|
|
|
|
auto openSessionRequest = new OpenSession("yui3a", _locale, ["application": "Container", "P": "P055"]);
|
|
|
|
|
|
|
|
auto response = sendRequest("sequential", openSessionRequest);
|
|
|
|
|
2017-02-14 01:57:14 +01:00
|
|
|
_sessionId = response.responseSets[0].responses[0]["SessionOpened"][0]["sessionId"].get!string;
|
|
|
|
_requestCounter = 1;
|
|
|
|
|
2017-02-14 02:47:23 +01:00
|
|
|
auto startFlowRequest = new StartFlow("yui3a", "Container", "gef0.gef1.gemd.Flow_MobileApplication.diamlflow",
|
|
|
|
["deviceType": "simple", "language": "2", "smsType": "otpUrl"], null);
|
2017-02-14 01:57:14 +01:00
|
|
|
response = sendRequest("sequential", startFlowRequest);
|
2017-02-14 00:52:27 +01:00
|
|
|
}
|
|
|
|
|
2017-02-14 02:47:23 +01:00
|
|
|
void login1(string cardNumber, string phoneZone, string phoneSubscriber)
|
|
|
|
{
|
2017-02-14 21:43:55 +01:00
|
|
|
auto widgetEvent = createClickImageEvent("Container@reuse_Screen@img_Operations");
|
|
|
|
sendRequest("sequential", new WidgetEvents("Container", [widgetEvent]));
|
2017-02-14 02:47:23 +01:00
|
|
|
|
2017-02-14 21:43:55 +01:00
|
|
|
widgetEvent = createChangeInputValueEvent("Container@reuse_flowAuthentication@inp_PanNumbersimple", cardNumber[0 .. 4]);
|
|
|
|
sendRequest("sequential", new WidgetEvents("Container", [widgetEvent]));
|
2017-02-14 02:47:23 +01:00
|
|
|
|
2017-02-14 21:43:55 +01:00
|
|
|
widgetEvent = createChangeInputValueEvent("Container@reuse_flowAuthentication@inp_PanNumbersimple", cardNumber);
|
|
|
|
sendRequest("sequential", new WidgetEvents("Container", [widgetEvent]));
|
2017-02-14 02:47:23 +01:00
|
|
|
|
2017-02-14 21:43:55 +01:00
|
|
|
widgetEvent = createChangeInputValueEvent("Container@reuse_flowAuthentication@inp_PhoneZone", phoneZone);
|
|
|
|
sendRequest("sequential", new WidgetEvents("Container", [widgetEvent]));
|
2017-02-14 02:47:23 +01:00
|
|
|
|
2017-02-14 21:43:55 +01:00
|
|
|
widgetEvent = createChangeInputValueEvent("Container@reuse_flowAuthentication@inp_PhoneSubscriber", phoneSubscriber);
|
|
|
|
sendRequest("sequential", new WidgetEvents("Container", [widgetEvent]));
|
2017-02-14 02:47:23 +01:00
|
|
|
|
2017-02-14 21:43:55 +01:00
|
|
|
widgetEvent = createClickButtonEvent("Container@reuse_flowAuthentication@btn_Identification");
|
|
|
|
sendRequest("sequential", new WidgetEvents("Container", [widgetEvent]));
|
2017-02-14 02:47:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void login2(string otp, string password)
|
2017-02-14 00:52:27 +01:00
|
|
|
{
|
2017-02-14 21:43:55 +01:00
|
|
|
auto widgetEvent = createClickImageEvent("Container@reuse_Screen@img_Operations");
|
|
|
|
sendRequest("sequential", new WidgetEvents("Container", [widgetEvent]));
|
2017-02-14 02:47:23 +01:00
|
|
|
|
2017-02-14 21:43:55 +01:00
|
|
|
widgetEvent = createChangeInputValueEvent("Container@reuse_flowAuthentication@inp_otp", otp);
|
|
|
|
sendRequest("sequential", new WidgetEvents("Container", [widgetEvent]));
|
2017-02-14 01:57:14 +01:00
|
|
|
|
2017-02-14 21:43:55 +01:00
|
|
|
widgetEvent = createChangeInputValueEvent("Container@reuse_flowAuthentication@inp_pw", password);
|
|
|
|
sendRequest("sequential", new WidgetEvents("Container", [widgetEvent]));
|
|
|
|
|
|
|
|
widgetEvent = createClickButtonEvent("Container@reuse_flowAuthentication@btn_Identification");
|
|
|
|
sendRequest("sequential", new WidgetEvents("Container", [widgetEvent]));
|
2017-02-14 02:47:23 +01:00
|
|
|
|
2017-02-14 21:43:55 +01:00
|
|
|
runTask(&heartbeatLoop);
|
|
|
|
}
|
|
|
|
|
|
|
|
string getAccountAvailable()
|
|
|
|
{
|
|
|
|
auto clickImageEvent = createClickImageEvent("Container@reuse_Screen@img_Account");
|
|
|
|
|
|
|
|
auto widgetEventsRequest = new WidgetEvents("Container", [clickImageEvent]);
|
2017-02-14 02:47:23 +01:00
|
|
|
sendRequest("sequential", widgetEventsRequest);
|
2017-02-14 21:43:55 +01:00
|
|
|
|
|
|
|
auto repeaterEvent = createChangeRepeaterValueEvent("Container@reuse_flowApplication@repeater_main", "1");
|
|
|
|
auto labelValueEvent = createChangeLabelValueEvent("Container@reuse_flowApplication@lbl_AccountLabel", "1", "INFO");
|
|
|
|
auto clickLabelEvent = createClickLabelEvent("Container@reuse_flowApplication@lbl_AccountLabel");
|
|
|
|
|
|
|
|
widgetEventsRequest = new WidgetEvents("Container", [repeaterEvent, labelValueEvent, repeaterEvent, clickLabelEvent]);
|
|
|
|
auto response = sendRequest("sequential", widgetEventsRequest);
|
|
|
|
|
|
|
|
foreach(widgetUpdate; response.responseSets[0].responses[0]["ScreenUpdate"])
|
|
|
|
{
|
|
|
|
if(widgetUpdate["widgetId"] == "Container@reuse_flowApplication@repeater_1292926459910")
|
|
|
|
{
|
|
|
|
return widgetUpdate["properties"][0]["contentList"]["dynamiccontent"][0]["repeatedPane_1292926472020"]["VerticalPane_1290438166429"]["gridPane_main"]["gridRow_available"]["gridItem_available_value"]["lbl_valueavailable"]["htmlText"].get!string;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
WidgetEventInformation createClickImageEvent(string widgetId)
|
|
|
|
{
|
|
|
|
return WidgetEventInformation(widgetId, "clicked", "Image", null);
|
|
|
|
}
|
|
|
|
|
|
|
|
WidgetEventInformation createClickButtonEvent(string widgetId)
|
|
|
|
{
|
|
|
|
return WidgetEventInformation(widgetId, "clicked", "ActionButton", null);
|
|
|
|
}
|
|
|
|
|
|
|
|
WidgetEventInformation createClickLabelEvent(string widgetId)
|
|
|
|
{
|
|
|
|
return WidgetEventInformation(widgetId, "linkClicked", "Label", null);
|
|
|
|
}
|
|
|
|
|
|
|
|
WidgetEventInformation createChangeInputValueEvent(string widgetId, string value)
|
|
|
|
{
|
|
|
|
return WidgetEventInformation(widgetId, "valueChanged", "Input", ["text": value]);
|
2017-02-14 02:47:23 +01:00
|
|
|
}
|
|
|
|
|
2017-02-14 21:43:55 +01:00
|
|
|
WidgetEventInformation createChangeRepeaterValueEvent(string widgetId, string activeElement)
|
2017-02-14 02:47:23 +01:00
|
|
|
{
|
2017-02-14 21:43:55 +01:00
|
|
|
return WidgetEventInformation(widgetId, "valueChanged", "Repeater", ["activeElement": activeElement]);
|
2017-02-14 02:47:23 +01:00
|
|
|
}
|
|
|
|
|
2017-02-14 21:43:55 +01:00
|
|
|
WidgetEventInformation createChangeLabelValueEvent(string widgetId, string currentLink, string currentTarget)
|
2017-02-14 02:47:23 +01:00
|
|
|
{
|
2017-02-14 21:43:55 +01:00
|
|
|
return WidgetEventInformation(widgetId, "valueChanged", "Label", ["currentLink": currentLink, "currentTarget": currentTarget]);
|
2017-02-14 02:47:23 +01:00
|
|
|
}
|
|
|
|
|
2017-02-14 21:43:55 +01:00
|
|
|
private void heartbeatLoop()
|
2017-02-14 02:47:23 +01:00
|
|
|
{
|
2017-02-14 21:43:55 +01:00
|
|
|
auto technicalRequest = new TechnicalRequest("heartbeat");
|
|
|
|
sendRequest("sequential", technicalRequest);
|
|
|
|
|
|
|
|
sleep(10000.msecs);
|
|
|
|
|
|
|
|
//TODO: Add a random action to prevent timeout
|
|
|
|
|
|
|
|
heartbeatLoop();
|
2017-02-14 02:47:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private ApiResponse sendRequest(T : ApiRequest)(string executionMode, T request)
|
|
|
|
{
|
2017-02-14 01:57:14 +01:00
|
|
|
string requestString;
|
|
|
|
if(_sessionId !is null)
|
|
|
|
{
|
|
|
|
auto message = new SessionApiMessage(executionMode, _sessionId, "Container", ++_requestCounter);
|
|
|
|
message.addRequest(T.stringof, request);
|
|
|
|
requestString = message.serializeToJsonString;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
auto message = new ApiMessage(executionMode);
|
|
|
|
message.addRequest(T.stringof, request);
|
|
|
|
requestString = message.serializeToJsonString;
|
|
|
|
}
|
2017-02-14 00:52:27 +01:00
|
|
|
|
2017-02-14 02:47:23 +01:00
|
|
|
writeln("C> " ~ requestString);
|
|
|
|
|
2017-02-14 00:52:27 +01:00
|
|
|
auto response = requestHTTP(Endpoint,
|
|
|
|
(scope request) {
|
|
|
|
request.method = HTTPMethod.POST;
|
|
|
|
request.headers["Accept"] = "*/*";
|
|
|
|
request.headers["Content-Type"] = "application/x-www-form-urlencoded";
|
|
|
|
request.headers["User-Agent"] = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.76 Safari/537.36";
|
2017-02-14 01:57:14 +01:00
|
|
|
request.writeBody(cast(ubyte[])("request=" ~ requestString.urlEncode));
|
2017-02-14 00:52:27 +01:00
|
|
|
}
|
|
|
|
).bodyReader.readAllUTF8;
|
|
|
|
|
2017-02-14 02:47:23 +01:00
|
|
|
writeln("S> " ~ response);
|
2017-02-14 01:57:14 +01:00
|
|
|
|
2017-02-14 02:47:23 +01:00
|
|
|
return response.parseJsonString.deserializeJson!ApiResponse;
|
2017-02-14 00:52:27 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
shared static this()
|
|
|
|
{
|
|
|
|
auto router = new URLRouter;
|
|
|
|
router.get("/hello", &helloWorld);
|
|
|
|
|
|
|
|
auto settings = new HTTPServerSettings;
|
|
|
|
settings.sessionStore = new MemorySessionStore;
|
|
|
|
settings.port = 8080;
|
|
|
|
settings.bindAddresses = ["::1", "127.0.0.1"];
|
|
|
|
|
|
|
|
listenHTTP(settings, router);
|
|
|
|
|
|
|
|
auto apiClient = new ApiClient("nl_BE");
|
|
|
|
apiClient.connect();
|
2017-02-14 02:47:23 +01:00
|
|
|
apiClient.login1("67030514325746009", "0470", "854404");
|
|
|
|
write("OTP: ");
|
|
|
|
auto otp = readln()[0 .. $-1];
|
|
|
|
auto password = fromStringz(getpass("Password: ")).idup;
|
|
|
|
apiClient.login2(otp, password);
|
2017-02-14 21:43:55 +01:00
|
|
|
|
|
|
|
writeln("Funds available: " ~ apiClient.getAccountAvailable());
|
2017-02-14 00:52:27 +01:00
|
|
|
}
|