Add support for the MaaS workflow

Added GetPrerollUserId() function.
Added optional accessCode parameter to StartAuthentication and modified it to POST to codeStatusURL.
Changed ListUsers and ListBackends function to return Status.
Fixed compilation error on Linux in JSON parser.
Changed SDK version string to "2.1.0".
Added possibility to test the MaaS workflow in cmdline_test.cpp.

Closes: MAASMOB-30
diff --git a/src/json/reader.inl b/src/json/reader.inl
index 9211675..313e5cb 100644
--- a/src/json/reader.inl
+++ b/src/json/reader.inl
@@ -303,8 +303,7 @@
 }
 
 static void ConsumeUnicode(std::istream& str, std::vector<utf8::uint16_t>& uni16) {
-    char bytes[4];
-    memset(bytes, 0, sizeof(bytes));
+    char bytes[4] = { '\0' };
     str.get(bytes[0]);
     str.get(bytes[1]);
     str.get(bytes[2]);
diff --git a/src/mpin_sdk.cpp b/src/mpin_sdk.cpp
index 3f94c73..c9a17f6 100644
--- a/src/mpin_sdk.cpp
+++ b/src/mpin_sdk.cpp
@@ -970,7 +970,7 @@
     return Status::OK;
 }
 
-Status MPinSDK::StartAuthentication(INOUT UserPtr user)
+Status MPinSDK::StartAuthentication(INOUT UserPtr user, const String& accessCode)
 {
     Status s = CheckIfBackendIsSet();
     if(s != Status::OK)
@@ -985,6 +985,16 @@
         return s;
     }
 
+    String codeStatusURL = m_clientSettings.GetStringParam("codeStatusURL");
+    if(!codeStatusURL.empty() && !accessCode.empty())
+    {
+        util::JsonObject data;
+        data["status"] = json::String("user");
+        data["wid"] = json::String(accessCode);
+        data["user"] = json::String(user->GetId());
+        MakeRequest(codeStatusURL, IHttpRequest::POST, data);
+    }
+
     bool useTimePermits = m_clientSettings.GetBoolParam("usePermits", true);
     if(!useTimePermits)
     {
@@ -1322,6 +1332,33 @@
     return calculatedCheckSum == checkSum;
 }
 
+String MPinSDK::GetPrerollUserId(const String& accessCode)
+{
+    Status s = CheckIfBackendIsSet();
+    if(s != Status::OK)
+    {
+        return "";
+    }
+
+    String codeStatusUrl = m_clientSettings.GetStringParam("codeStatusURL");
+    if(codeStatusUrl.empty())
+    {
+        return "";
+    }
+
+    util::JsonObject data;
+    data["status"] = json::String("wid");
+    data["wid"] = json::String(accessCode);
+
+    HttpResponse response = MakeRequest(codeStatusUrl, IHttpRequest::POST, data);
+    if(response.GetStatus() != HttpResponse::HTTP_OK)
+    {
+        return "";
+    }
+
+    return response.GetJsonData().GetStringParam("prerollId");
+}
+
 void MPinSDK::DeleteUser(UserPtr user)
 {
     UsersMap::iterator i = m_users.find(user->GetId());
@@ -1338,16 +1375,35 @@
     m_logoutData.erase(user);
 }
 
-void MPinSDK::ListUsers(std::vector<UserPtr>& users) const
+Status MPinSDK::ListUsers(std::vector<UserPtr>& users) const
 {
+    Status s = CheckIfBackendIsSet();
+    if(s != Status::OK)
+    {
+        return s;
+    }
+
     ListUsers(users, m_users);
+    return Status::OK;
 }
 
-void MPinSDK::ListUsers(OUT std::vector<UserPtr>& users, const String& backend) const
+Status MPinSDK::ListUsers(OUT std::vector<UserPtr>& users, const String& backend) const
 {
+    Status s = CheckIfIsInitialized();
+    if(s != Status::OK)
+    {
+        return s;
+    }
+
     UsersMap usersMap;
-    LoadUsersFromStorage(backend, usersMap);
+    s = LoadUsersFromStorage(backend, usersMap);
+    if(s != Status::OK)
+    {
+        return s;
+    }
+
     ListUsers(users, usersMap);
+    return Status::OK;
 }
 
 void MPinSDK::ListUsers(OUT std::vector<UserPtr>& users, const UsersMap& usersMap) const
@@ -1561,15 +1617,21 @@
 	return Status(Status::OK);
 }
 
-void MPinSDK::ListBackends(OUT std::vector<String>& backends) const
+Status MPinSDK::ListBackends(OUT std::vector<String>& backends) const
 {
-	IStorage* storage = m_context->GetStorage(IStorage::NONSECURE);
+    Status s = CheckIfIsInitialized();
+    if(s != Status::OK)
+    {
+        return s;
+    }
+
+    IStorage* storage = m_context->GetStorage(IStorage::NONSECURE);
 	String data;
 	storage->GetData(data);
     data.Trim();
 	if(data.empty())
     {
-		return;
+		return Status::OK;
 	}
 
 	try
@@ -1583,7 +1645,12 @@
             backends.push_back(i->name);
         }
     }
-    catch(const json::Exception&) {}
+    catch(const json::Exception& e)
+    {
+        return Status(Status::STORAGE_ERROR, e.what());
+    }
+
+    return Status::OK;
 }
 
 const char * MPinSDK::GetVersion()
diff --git a/src/mpin_sdk.h b/src/mpin_sdk.h
index 902189a..ec69d58 100644
--- a/src/mpin_sdk.h
+++ b/src/mpin_sdk.h
@@ -236,17 +236,19 @@
     Status ConfirmRegistration(INOUT UserPtr user, const String& pushMessageIdentifier = "");
     Status FinishRegistration(INOUT UserPtr user, const String& pin);
 
-    Status StartAuthentication(INOUT UserPtr user);
+    Status StartAuthentication(INOUT UserPtr user, const String& accessCode = "");
     Status CheckAccessNumber(const String& accessNumber);
     Status FinishAuthentication(INOUT UserPtr user, const String& pin);
     Status FinishAuthentication(INOUT UserPtr user, const String& pin, OUT String& authResultData);
     Status FinishAuthenticationOTP(INOUT UserPtr user, const String& pin, OUT OTP& otp);
     Status FinishAuthenticationAN(INOUT UserPtr user, const String& pin, const String& accessNumber);
 
+    String GetPrerollUserId(const String& accessCode);
+
     void DeleteUser(INOUT UserPtr user);
-    void ListUsers(OUT std::vector<UserPtr>& users) const;
-    void ListUsers(OUT std::vector<UserPtr>& users, const String& backend) const;
-    void ListBackends(OUT std::vector<String>& backends) const;
+    Status ListUsers(OUT std::vector<UserPtr>& users) const;
+    Status ListUsers(OUT std::vector<UserPtr>& users, const String& backend) const;
+    Status ListBackends(OUT std::vector<String>& backends) const;
     const char * GetVersion();
     bool CanLogout(IN UserPtr user);
     bool Logout(IN UserPtr user);
diff --git a/src/version.h b/src/version.h
index fc4b9bd..a9b268d 100644
--- a/src/version.h
+++ b/src/version.h
@@ -25,6 +25,6 @@
 #define _MPIN_SDK_VERSION_H_
 
 #define MPIN_SDK_VERSION "1.0.0"
-#define MPIN_SDK_V2_VERSION "2.0.0"
+#define MPIN_SDK_V2_VERSION "2.1.0"
 
 #endif // _MPIN_SDK_VERSION_H_
diff --git a/tests/cmdline_test.cpp b/tests/cmdline_test.cpp
index 6957afd..685a37a 100644
--- a/tests/cmdline_test.cpp
+++ b/tests/cmdline_test.cpp
@@ -27,7 +27,10 @@
 #include "contexts/cmdline_context.h"
 #include "CvLogger.h"
 
-using namespace std;
+using std::cout;
+using std::cin;
+using std::endl;
+using std::vector;
 
 struct Backend
 {
@@ -78,6 +81,13 @@
         TestBackend(sdk, backends[i].backend, backends[i].rpsPrefix);
     }
 
+    bool testMaasWorkflow = false;
+    const char *maasBackend = "http://192.168.98.141:8001";
+    if(testMaasWorkflow)
+    {
+        s = sdk.SetBackend(maasBackend);
+    }
+
     //s = sdk.SetBackend(backends[1].backend, backends[1].rpsPrefix);
     if(s != MPinSDK::Status::OK)
     {
@@ -146,7 +156,16 @@
         _getch();
     }
 
-    s = sdk.StartAuthentication(user);
+    MPinSDK::String accessCode;
+    if(testMaasWorkflow)
+    {
+        cout << "Enter access code: ";
+        cin >> accessCode;
+        MPinSDK::String userId = sdk.GetPrerollUserId(accessCode);
+        cout << "GetPrerollUserId() returned '" << userId << "'. Press any key to continue..." << endl;
+    }
+
+    s = sdk.StartAuthentication(user, accessCode);
     if(s != MPinSDK::Status::OK)
     {
         cout << "Failed to start user authentication: status code = " << s.GetStatusCode() << ", error: " << s.GetErrorMessage() << endl;
@@ -160,7 +179,14 @@
     cin >> pin;
 
     MPinSDK::String authData;
-    s = sdk.FinishAuthentication(user, pin, authData);
+    if(!testMaasWorkflow)
+    {
+        s = sdk.FinishAuthentication(user, pin, authData);
+    }
+    else
+    {
+        s = sdk.FinishAuthenticationAN(user, pin, accessCode);
+    }
     if(s != MPinSDK::Status::OK)
     {
         cout << "Failed to authenticate user: status code = " << s.GetStatusCode() << ", error: " << s.GetErrorMessage() << endl;