Commit 74df51c9 by 李定达

1.新增RES对接,并测试完成;2.main调用方法未编写;3本次提交exe的注意

parent 932408b6
No preview for this file type
......@@ -18,7 +18,7 @@
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>MultiByte</CharacterSet>
<UseOfMfc>false</UseOfMfc>
......@@ -91,11 +91,13 @@
<ClCompile Include="load.c" />
<ClCompile Include="memory.c" />
<ClCompile Include="pack_unpack.c" />
<ClCompile Include="resdata.cpp" />
<ClCompile Include="sbk_client.cpp" />
<ClCompile Include="strbuffer.c" />
<ClCompile Include="strconv.c" />
<ClCompile Include="tastdataprocess.cpp" />
<ClCompile Include="testlog.cpp" />
<ClCompile Include="testresdata.cpp" />
<ClCompile Include="testtool.cpp" />
<ClCompile Include="utf.c" />
<ClCompile Include="value.c" />
......@@ -114,10 +116,12 @@
<ClInclude Include="jansson_config.h" />
<ClInclude Include="jansson_private.h" />
<ClInclude Include="lookup3.h" />
<ClInclude Include="resdata.h" />
<ClInclude Include="sqlite3.h" />
<ClInclude Include="strbuffer.h" />
<ClInclude Include="testdataprocess.h" />
<ClInclude Include="testlog.h" />
<ClInclude Include="testresdata.h" />
<ClInclude Include="testtool.h" />
<ClInclude Include="utf.h" />
</ItemGroup>
......
......@@ -66,6 +66,12 @@
<ClCompile Include="testtool.cpp">
<Filter>Source Files\TestCase</Filter>
</ClCompile>
<ClCompile Include="resdata.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="testresdata.cpp">
<Filter>Source Files\TestCase</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="fmcrypt.h">
......@@ -125,5 +131,11 @@
<ClInclude Include="fmdatabase.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="resdata.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="testresdata.h">
<Filter>Header Files\TestCase</Filter>
</ClInclude>
</ItemGroup>
</Project>
\ No newline at end of file
......@@ -10,7 +10,12 @@
#define MAX_SIGN_LEN 256
#define CFG_FILE_NAME "fmclient.cfg"
#define RBG_FILE_NAME "fmclient.rbk"
#define REQUEST_CRET "{\r\n \"ver\": 1,\r\n \"reqType\": 99,\r\n \"partnerId\":%d,\r\n \"storeId\":\"%s\",\r\n \"stationId\": \"%s\"\r\n}"
#define REQUEST_0 "{\r\n \"ver\": 1,\r\n \"reqType\": %s,\r\n \"partnerId\":%d,\r\n \"storeId\":\"%s\",\r\n \"stationId\": \"%s\",\r\n \"operatorId\":\"%s\",\r\n \"code\":\"%s\"\r\n}"
#define REQUEST_3 "{\r\n \"ver\": 1,\r\n \"reqType\": %s,\r\n \"partnerId\":%d,\r\n \"storeId\":\"%s\",\r\n \"stationId\": \"%s\",\r\n \"operatorId\":\"%s\",\r\n \"code\":\"%s\",\r\n \"businessDate\":\"%s\",\r\n \"amount\":\"%d\",\r\n \"transId\":\"%s\"\r\n}"
#define REQUEST_71 "{\r\n \"ver\": 1,\r\n \"reqType\": %s,\r\n \"partnerId\":%d,\r\n \"storeId\":\"%s\",\r\n \"stationId\": \"%s\",\r\n \"operatorId\":\"%s\",\r\n \"code\":\"%s\",\r\n \"businessDate\":\"%s\",\r\n \"amount\":\"%d\",\r\n \"transId\":\"%s\"\r\n}"
#define DES3_KEY "ABCD@#9876DFSAAWKLDEOPDD"
#define CRET_FILE_NAME "client.p12"
#define JSON_KEY_ONLY "partnerOrderId"
......
#include "resdata.h"
#include "fmlog.h"
#include "fmglobal.h"
bool RESToRequest0(const string &indata, string &outdata)
{
int j = 0;
int tmp = 0;
Request_0 rsp;
outdata.clear();
for(int i = 0; i < indata.size() - 4; i ++)
{
if(indata[i] == '|')
{
rsp.data[j] = indata.substr(tmp, i - tmp);
tmp = i + 1;
j ++;
}
}
string str;
for (int i = 0; i < 6; i ++)
str.append(rsp.data[i]) ;
LOG() << "get struct : " << str;
if(rsp.data[0].empty() || rsp.data[1].empty() || rsp.data[2].empty() || rsp.data[3].empty() || rsp.data[4].empty() || rsp.data[5].empty())
{
LOG() << "error request";
return false;
}
char request[MAX_BUF_LEN] = { 0 };
sprintf_s(request, MAX_BUF_LEN, REQUEST_0, rsp.data[0].data(), atoi(rsp.data[1].data()), rsp.data[2].data(), rsp.data[3].data(), rsp.data[4].data(), rsp.data[5].data());
outdata.append(request);
LOG() << "get reqType 0 request : " << outdata;
return true;
}
bool RESToRequest71(const string &indata, string &outdata)
{
int j = 0;
int tmp = 0;
Request_71_3 rsp;
outdata.clear();
for(int i = 0; i < indata.size() - 4; i ++)
{
if(indata[i] == '|')
{
rsp.data[j] = indata.substr(tmp, i - tmp);
tmp = i + 1;
j ++;
}
}
string str;
for (int i = 0; i < 9; i ++)
str.append(rsp.data[i]) ;
LOG() << "get struct : " << str;
if(rsp.data[0].empty() || rsp.data[1].empty() || rsp.data[2].empty() || rsp.data[3].empty() || rsp.data[4].empty() || rsp.data[5].empty() || rsp.data[6].empty() || rsp.data[8].empty())
{
LOG() << "error request";
return false;
}
char request[MAX_BUF_LEN] = { 0 };
sprintf_s(request, MAX_BUF_LEN, REQUEST_71, rsp.data[0].data(), atoi(rsp.data[1].data()), rsp.data[2].data(), rsp.data[3].data(), rsp.data[4].data(), rsp.data[5].data(), rsp.data[6].data(), atoi(rsp.data[7].data()), rsp.data[8].data());
outdata.append(request);
LOG() << "get reqType 0 request : " << outdata;
return true;
}
bool RESToRequest3(const string &indata, string &outdata)
{
int j = 0;
int tmp = 0;
Request_71_3 rsp;
outdata.clear();
for(int i = 0; i < indata.size() - 4; i ++)
{
if(indata[i] == '|')
{
rsp.data[j] = indata.substr(tmp, i - tmp);
tmp = i + 1;
j ++;
}
}
string str;
for (int i = 0; i < 9; i ++)
str.append(rsp.data[i]) ;
LOG() << "get struct : " << str;
if(rsp.data[0].empty() || rsp.data[1].empty() || rsp.data[2].empty() || rsp.data[3].empty() || rsp.data[4].empty() || rsp.data[5].empty() || rsp.data[6].empty() || rsp.data[8].empty())
{
LOG() << "error request";
return false;
}
char request[MAX_BUF_LEN] = { 0 };
sprintf_s(request, MAX_BUF_LEN, REQUEST_3, rsp.data[0].data(), atoi(rsp.data[1].data()), rsp.data[2].data(), rsp.data[3].data(), rsp.data[4].data(), rsp.data[5].data(), rsp.data[6].data(), atoi(rsp.data[7].data()), rsp.data[8].data());
outdata.append(request);
LOG() << "get reqType 0 request : " << outdata;
return true;
}
bool Response0ToRES(const string &indata, string &outdata)
{
json_t *root;
json_error_t error;
Response_0 rsp;
outdata.clear();
rsp.init();
root = json_loads(indata.data(), 0, &error);
if(!root)
{
LOG() << "argv is not json";
return false;
}
//statusCode
json_t *statusCode = json_object_get(root, "statusCode");
if (statusCode == NULL || !json_is_integer(statusCode))
{
LOG() << "statusCode error";
json_decref(root);
return false;
}
{
char tmpbuf[20] = {0};
itoa(json_integer_value(statusCode), tmpbuf, 10);
rsp.data[0] = string(tmpbuf);
}
//message
json_t *message = json_object_get(root, "message");
if (message == NULL || !json_is_string(message))
{
LOG() << "message error";
json_decref(root);
return false;
}
rsp.data[1] = string(json_string_value(message));
//type
json_t *type = json_object_get(root, "type");
if (type == NULL || !json_is_integer(type))
{
LOG() << "type error";
}else
{
char tmpbuf[20] = {0};
itoa(json_integer_value(type), tmpbuf, 10);
rsp.data[2] = string(tmpbuf);
}
//info
bool _infoisobject = false;
json_t *info = json_object_get(root, "info");
if(info != NULL && json_is_object(info))
_infoisobject = true;
//code
if(_infoisobject)
{
json_t *code = json_object_get(info, "code");
if (code != NULL && json_is_string(code))
{
rsp.data[3] = json_string_value(code);
}
}
//status
if(_infoisobject)
{
json_t *status = json_object_get(info, "status");
if (status != NULL && json_is_string(status))
{
rsp.data[4] = json_string_value(status);
}
}
//statusDesc
if(_infoisobject)
{
json_t *statusDesc = json_object_get(info, "statusDesc");
if (statusDesc != NULL && json_is_string(statusDesc))
{
rsp.data[5] = json_string_value(statusDesc);
}
}
//actId
if(_infoisobject)
{
json_t *actId = json_object_get(info, "actId");
if (actId != NULL && json_is_string(actId))
{
rsp.data[6] = json_string_value(actId);
}
}
//actName
if(_infoisobject)
{
json_t *actName = json_object_get(info, "actName");
if (actName != NULL && json_is_string(actName))
{
rsp.data[7] = json_string_value(actName);
}
}
//paymentMethodCode
json_t *paymentMethodCode = json_object_get(root, "paymentMethodCode");
if (paymentMethodCode != NULL && json_is_string(paymentMethodCode))
{
rsp.data[8] = json_string_value(paymentMethodCode);
}
//paymentMethod
json_t *paymentMethod = json_object_get(root, "paymentMethod");
if (message != NULL || json_is_string(paymentMethod))
{
rsp.data[9] = json_string_value(paymentMethod);
}
//authCode
if(_infoisobject)
{
json_t *authCode = json_object_get(info, "authCode");
if (authCode != NULL && json_is_string(authCode))
{
rsp.data[10] = json_string_value(authCode);
}
}
//ext
bool _extisobject = false;
json_t *ext = json_object_get(root, "ext");
if(ext != NULL && json_is_object(ext))
_extisobject = true;
//screenEncoding
if(_extisobject)
{
json_t *screenEncoding = json_object_get(ext, "screenEncoding");
if (screenEncoding != NULL && json_is_string(screenEncoding))
{
rsp.data[11] = json_string_value(screenEncoding);
}
}
//product
bool _productsisobject = false;
json_t *product = NULL;
if(_infoisobject)
{
json_t *products = json_object_get(info, "products");
if (products != NULL && json_is_array(products))
{
product = json_array_get(products, 0);
if(product != NULL && json_is_object(product))
_productsisobject = true;
}
}
//pid
if(_productsisobject)
{
json_t *pid = json_object_get(product, "pid");
if (pid != NULL && json_is_string(pid))
{
rsp.data[12] = json_string_value(pid);
}
}
//name
if(_productsisobject)
{
json_t *name = json_object_get(product, "name");
if (name != NULL && json_is_string(name))
{
rsp.data[13] = json_string_value(name);
}
}
//scope
if(_productsisobject)
{
json_t *scope = json_object_get(product, "scope");
if (scope != NULL && json_is_string(scope))
{
rsp.data[14] = json_string_value(scope);
}
}
//discount
if(_productsisobject)
{
json_t *discount = json_object_get(product, "discount");
if (discount != NULL && json_is_integer(discount))
{
char tmpbuf[20] = {0};
itoa(json_integer_value(discount), tmpbuf, 10);
rsp.data[15] = string(tmpbuf);
}
}
//priceAct
if(_productsisobject)
{
json_t *priceAct = json_object_get(product, "priceAct");
if (priceAct != NULL && json_is_integer(priceAct))
{
char tmpbuf[20] = {0};
itoa(json_integer_value(priceAct), tmpbuf, 10);
rsp.data[16] = string(tmpbuf);
}
}
for(int i = 0; i < 17; i ++)
outdata.append(rsp.data[i]).append("|");
outdata.append("END|");
LOG() << "get RES Data : " << outdata;
json_decref(root);
return true;
}
bool Response3ToRES(const string &indata, string &outdata)
{
json_t *root;
json_error_t error;
Response_3 rsp;
outdata.clear();
rsp.init();
root = json_loads(indata.data(), 0, &error);
if(!root)
{
LOG() << "argv is not json";
return false;
}
//statusCode
json_t *statusCode = json_object_get(root, "statusCode");
if (statusCode == NULL || !json_is_integer(statusCode))
{
LOG() << "statusCode error";
json_decref(root);
return false;
}
{
char tmpbuf[20] = {0};
itoa(json_integer_value(statusCode), tmpbuf, 10);
rsp.data[0] = string(tmpbuf);
}
//message
json_t *message = json_object_get(root, "message");
if (message == NULL || !json_is_string(message))
{
LOG() << "message error";
json_decref(root);
return false;
}
rsp.data[1] = string(json_string_value(message));
for(int i = 0; i < 2; i ++)
outdata.append(rsp.data[i]).append("|");
outdata.append("END|");
LOG() << "get RES Data : " << outdata;
json_decref(root);
return true;
}
bool Response71ToRES(const string &indata, string &outdata)
{
json_t *root;
json_error_t error;
Response_71 rsp;
outdata.clear();
rsp.init();
root = json_loads(indata.data(), 0, &error);
if(!root)
{
LOG() << "argv is not json";
return false;
}
//statusCode
json_t *statusCode = json_object_get(root, "statusCode");
if (statusCode == NULL || !json_is_integer(statusCode))
{
LOG() << "statusCode error";
json_decref(root);
return false;
}
{
char tmpbuf[20] = {0};
itoa(json_integer_value(statusCode), tmpbuf, 10);
rsp.data[0] = string(tmpbuf);
}
//message
json_t *message = json_object_get(root, "message");
if (message == NULL || !json_is_string(message))
{
LOG() << "message error";
json_decref(root);
return false;
}
rsp.data[1] = string(json_string_value(message));
//type
json_t *type = json_object_get(root, "type");
if (type == NULL || !json_is_integer(type))
{
LOG() << "type error";
}else
{
char tmpbuf[20] = {0};
itoa(json_integer_value(type), tmpbuf, 10);
rsp.data[2] = string(tmpbuf);
}
//actInfo
bool _infoisobject = false;
json_t *info = json_object_get(root, "actInfo");
if(info != NULL && json_is_object(info))
_infoisobject = true;
//code
if(_infoisobject)
{
json_t *code = json_object_get(info, "code");
if (code != NULL && json_is_string(code))
{
rsp.data[3] = json_string_value(code);
}
}
//status
if(_infoisobject)
{
json_t *status = json_object_get(info, "status");
if (status != NULL && json_is_string(status))
{
rsp.data[4] = json_string_value(status);
}
}
//statusDesc
if(_infoisobject)
{
json_t *statusDesc = json_object_get(info, "statusDesc");
if (statusDesc != NULL && json_is_string(statusDesc))
{
rsp.data[5] = json_string_value(statusDesc);
}
}
//actId
if(_infoisobject)
{
json_t *actId = json_object_get(info, "actId");
if (actId != NULL && json_is_string(actId))
{
rsp.data[6] = json_string_value(actId);
}
}
//actName
if(_infoisobject)
{
json_t *actName = json_object_get(info, "actName");
if (actName != NULL && json_is_string(actName))
{
rsp.data[7] = json_string_value(actName);
}
}
//paymentMethodCode
json_t *paymentMethodCode = json_object_get(root, "paymentMethodCode");
if (paymentMethodCode != NULL && json_is_string(paymentMethodCode))
{
rsp.data[8] = json_string_value(paymentMethodCode);
}
//paymentMethod
json_t *paymentMethod = json_object_get(root, "paymentMethod");
if (message != NULL || json_is_string(paymentMethod))
{
rsp.data[9] = json_string_value(paymentMethod);
}
//fmId
json_t *fmId = json_object_get(root, "fmId");
if (fmId != NULL && json_is_string(fmId))
{
rsp.data[10] = json_string_value(fmId);
}
//ext
bool _extisobject = false;
json_t *ext = json_object_get(root, "ext");
if(ext != NULL && json_is_object(ext))
_extisobject = true;
//screenEncoding
if(_extisobject)
{
json_t *screenEncoding = json_object_get(ext, "screenEncoding");
if (screenEncoding != NULL && json_is_string(screenEncoding))
{
rsp.data[11] = json_string_value(screenEncoding);
}
}
//consumeFactAmount
json_t *consumeFactAmount = json_object_get(root, "consumeFactAmount");
if (consumeFactAmount != NULL && json_is_string(consumeFactAmount))
{
rsp.data[12] = json_string_value(consumeFactAmount);
}
//consumeDiscountAmount
json_t *consumeDiscountAmount = json_object_get(root, "consumeDiscountAmount");
if (fmId != NULL && json_is_string(consumeDiscountAmount))
{
rsp.data[13] = json_string_value(consumeDiscountAmount);
}
for(int i = 0; i < 14; i ++)
outdata.append(rsp.data[i]).append("|");
outdata.append("END|");
LOG() << "get RES Data : " << outdata;
json_decref(root);
return true;
}
bool RESToJson(const string &indata, string &outdata)
{
_reqType = -1;
if(indata.find_first_of("0") == 0)
{
_reqType = 0;
return RESToRequest0(indata, outdata);
}
if(indata.find_first_of("71") == 0)
{
_reqType = 71;
return RESToRequest71(indata, outdata);
}
if(indata.find_first_of("3") == 0)
{
_reqType = 3;
return RESToRequest3(indata, outdata);
}
return false;
}
bool JsonToRES(const string &indata, string &outdata)
{
if(_reqType == 0)
{
_reqType = -1;
return Response0ToRES(indata, outdata);
}
if(_reqType == 71)
{
_reqType = -1;
return Response71ToRES(indata, outdata);
}
_reqType = -1;
return Response3ToRES(indata, outdata);
}
\ No newline at end of file
#ifndef FM_RESDATA_H
#define FM_RESDATA_H
#include<string>
#include <jansson.h>
#include <jansson_private.h>
using std::string;
typedef struct
{
string data[6];
////1
//string reqType;
////2
//string partnerId;
////3
//string storeId;
////4
//string posNo;
////5
//string optId;
////6
//string code;
inline void init()
{
for(int i = 0; i < 6; i ++)
data[i].clear();
}
} Request_0;
typedef struct
{
string data[9];
////1
//string reqType;
////2
//string partnerId;
////3
//string storeId;
////4
//string posNo;
////5
//string optId;
////6
//string code;
////7
//string businessData;
////8
//string totalAmout;
////9
//string transId;
inline void init()
{
for(int i = 0; i < 9; i ++)
data[i].clear();
}
} Request_71_3;
typedef struct
{
string data[17];
//1返回查询结果状态码
//string statusCode;
////2状态码描述
//string statusCodeMSG;
////3券类型
//string type;
////4券码
//string code;
////5券状态
//string status;
////6券状态汉字
//string statusMSG;
////7活动编号
//string actId;
////8活动名称
//string actName;
////9支付渠道编号
//string payCode;
////10支付渠道名称
//string payCodeName;
////11授权码
//string autoCode;
////12脚本编码
//string SIMCode;
////13商品或券编号
//string counpId;
////14商品或券名称
//string counpName;
////15券使用范围
//string userRange;
////16折扣编号
//string discountNO;
////17券面值
//string counp_amout;
inline void init()
{
for(int i = 0; i < 17; i ++)
data[i].clear();
}
} Response_0;
typedef struct
{
string data[14];
//1返回查询结果状态码
//string statusCode;
////2状态码描述
//string statusCodeMSG;
////3券类型
//string type;
////4券码
//string code;
////5券状态
//string status;
////6券状态汉字
//string statusMSG;
////7活动编号
//string actId;
////8活动名称
//string actName;
////9支付渠道编号
//string payCode;
////10支付渠道名称
//string payCodeName;
////11非码交易
//string fmId;
////12脚本编码
//string SIMCode;
////13账户消费额
//string userAccount;
////14折扣账户消费金额
//string discountAccount;
inline void init()
{
for(int i = 0; i < 14; i ++)
data[i].clear();
}
} Response_71;
typedef struct
{
string data[2];
////1返回查询结果状态码
//string statusCode;
////2状态码描述
//string statusCodeMSG;
inline void init()
{
for(int i = 0; i < 2; i ++)
data[i].clear();
}
} Response_3;
extern bool JsonToRES(const string &indata, string &outdata);
extern bool RESToJson(const string &indata, string &outdata);
extern int _reqType;
#endif
\ No newline at end of file
......@@ -17,12 +17,17 @@
#include "fmtool.h"
#include "fmdatabase.h"
//#define FM_TEST
#include "resdata.h"
int _reqType = -1;
#define FM_TEST
//#define FM_TESTS
#include "testdataprocess.h"
#include "testlog.h"
#include "testtool.h"
#include "testresdata.h"
using std::cout;
using std::endl;
......@@ -1159,10 +1164,15 @@ void testCheckRollbackData()
int main()
{
_reqType = 3;
TestRESToJson();
TestJsonToRES();
char storeid[] = "12344";
char posno[] = "01";
//TestComplementJson();
//Test_RSASign();
......@@ -1188,7 +1198,7 @@ int main()
//GetRSACret(1443, storeid, posno);
//while(1)
//{
GetValue(a, b);
//GetValue(a, b);
// LOG() << b;
......
#include "testresdata.h"
#include "resdata.h"
#include <string>
#include "fmlog.h"
using std::string;
//char buf[] = "{\n \"ext\": {\n \"hint\": \"------------7310183410017522691 +++++++++\"\n },\n \"paymentMethodCode\": \"80022\",\n \"ver\": 1,\n \"paymentMethod\": \"****************\",\n \"message\": \"wdh----\",\n \"type\": 3,\n \"info\": {\n \"actId\": \"20000451\",\n \"actName\": \"zkq20000451\",\n \"authCode\": \"\",\n \"code\": \"7310183410017522691=4F28F2E86561C03F\",\n \"products\": [\n {\n \"discount\": 216,\n \"name\": \"sp85zq----\",\n \"priceAct\": 0,\n \"scope\": \"discountNo\"\n }\n ],\n \"status\": \"6\",\n \"statusDesc\": \"++++++++\"\n },\n \"statusCode\": 100\n}";
//char buf[] = "\{\"a\": 8}";
char buf[] = "{\n \"ext\": {\n \"hint\": \"shiping85zheyouhuiquan 7310183410017522691\"\n },\n \"paymentMethodCode\": \"80022\",\n \"actInfo\": {\n \"actId\": \"1402007\",\n \"actName\": \"zhekouquan20000451\",\n \"code\": \"7310183410017522691=4F28F2E86561C03F\"\n },\n \"fmId\": \"2691170714Z10000001\",\n \"ver\": 1,\n \"paymentMethod\": \"dianzhiquan-zhekoquan\",\n \"message\": \"chenggong\",\n \"type\": 3,\n \"statusCode\": 100\n}";
char ls[] = "0|1443|0001|FOE13|01|123456789987654321|END|";
char ls1[] = "3|1443|0001|FOE13|01|123456789987654321|20170714||8888|END|";
char ls2[] = "71|1443|0001|FOE13|01|123456789987654321|20170714||8888|END|";
void TestRESToJson()
{
string str(ls);
string str1(ls1);
string str2(ls2);
string out;
RESToJson(str, out);
RESToJson(str1, out);
RESToJson(str2, out);
}
void TestJsonToRES()
{
string str(buf);
string out;
JsonToRES(str, out);
LOG() << out;
}
\ No newline at end of file
#ifdef FM_TEST
void TestRESToJson();
void TestJsonToRES();
#endif
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment