Refactor code to comply with #8 (#39)

Reviewed-on: Telodendria/Telodendria#39
Co-authored-by: lda <lda@freetards.xyz>
Co-committed-by: lda <lda@freetards.xyz>
This commit is contained in:
lda 2023-11-21 09:11:45 -05:00 committed by Jordan Bancino
parent 2d30719be4
commit 3dae19e82d
7 changed files with 203 additions and 229 deletions

38
Schema/LoginRequest.json Normal file
View file

@ -0,0 +1,38 @@
{
"header": "Schema\/LoginRequest.h",
"types": {
"LoginRequestType": {
"fields": {
"m.login.password": { "name": "REQUEST_TYPE_PASSWORD" }
},
"type": "enum"
},
"LoginRequestUserIdentifier": {
"fields": {
"type": { "type": "string" },
"user": { "type": "string" }
},
"type": "struct"
},
"LoginRequest": {
"fields": {
"type": { "type": "LoginRequestType" },
"identifier": { "type": "object" },
"password": { "type": "string" },
"address": { "type": "string" },
"user": { "type": "string" },
"device_id": { "type": "string" },
"initial_device_display_name": { "type": "string" },
"medium": { "type": "string" },
"token": { "type": "string" },
"refresh_token": { "type": "boolean" }
},
"type": "struct"
}
},
"guard": "TELODENDRIA_SCHEMA_LOGIN_REQUEST_H"
}

17
Schema/Registration.json Normal file
View file

@ -0,0 +1,17 @@
{
"header": "Schema\/Registration.h",
"types": {
"RegistrationRequest": {
"fields": {
"username": { "type": "string" },
"password": { "type": "string" },
"device_id": { "type": "string" },
"inhibit_login": { "type": "boolean" },
"initial_device_display_name": { "type": "string" },
"refresh_token": { "type": "boolean" }
},
"type": "struct"
}
},
"guard": "TELODENDRIA_SCHEMA_REGISTRATION_H"
}

21
Schema/RequestToken.json Normal file
View file

@ -0,0 +1,21 @@
{
"header": "Schema\/RequestToken.h",
"types": {
"RequestToken": {
"fields": {
"client_secret": { "type": "string" },
"send_attempt": { "type": "integer" },
"next_link": { "type": "string" },
"id_access_token": { "type": "string" },
"id_server": { "type": "string" },
"email": { "type": "string" },
"country": { "type": "string" },
"phone_number": { "type": "string" }
},
"type": "struct"
}
},
"guard": "TELODENDRIA_SCHEMA_REQUESTTOKEN_H"
}

View file

@ -28,7 +28,6 @@
#include <Cytoplasm/Str.h> #include <Cytoplasm/Str.h>
#include <User.h> #include <User.h>
#include <Cytoplasm/Log.h>
ROUTE_IMPL(RouteAdminDeactivate, path, argp) ROUTE_IMPL(RouteAdminDeactivate, path, argp)
{ {

View file

@ -25,6 +25,8 @@
#include <string.h> #include <string.h>
#include <Schema/LoginRequest.h>
#include <Cytoplasm/Json.h> #include <Cytoplasm/Json.h>
#include <Cytoplasm/HashMap.h> #include <Cytoplasm/HashMap.h>
#include <Cytoplasm/Str.h> #include <Cytoplasm/Str.h>
@ -43,12 +45,9 @@ ROUTE_IMPL(RouteLogin, path, argp)
HashMap *identifier; HashMap *identifier;
char *deviceId = NULL; LoginRequest loginRequest;
char *initialDeviceDisplayName = NULL; LoginRequestUserIdentifier userIdentifier;
int refreshToken = 0;
char *password;
char *type;
UserId *userId = NULL; UserId *userId = NULL;
Db *db = args->matrixArgs->db; Db *db = args->matrixArgs->db;
@ -57,6 +56,14 @@ ROUTE_IMPL(RouteLogin, path, argp)
UserLoginInfo *loginInfo; UserLoginInfo *loginInfo;
char *fullUsername; char *fullUsername;
char *type;
char *initialDeviceDisplayName;
char *deviceId;
char *password;
int refreshToken;
char *msg;
Config *config = ConfigLock(db); Config *config = ConfigLock(db);
if (!config) if (!config)
@ -68,6 +75,9 @@ ROUTE_IMPL(RouteLogin, path, argp)
(void) path; (void) path;
memset(&loginRequest, 0, sizeof(LoginRequest));
memset(&userIdentifier, 0, sizeof(LoginRequestUserIdentifier));
switch (HttpRequestMethodGet(args->context)) switch (HttpRequestMethodGet(args->context))
{ {
case HTTP_GET: case HTTP_GET:
@ -88,45 +98,21 @@ ROUTE_IMPL(RouteLogin, path, argp)
break; break;
} }
val = HashMapGet(request, "type"); if (!LoginRequestFromJson(request, &loginRequest, &msg))
if (!val)
{ {
HttpResponseStatus(args->context, HTTP_BAD_REQUEST); HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
response = MatrixErrorCreate(M_MISSING_PARAM, NULL); response = MatrixErrorCreate(M_BAD_JSON, msg);
break; break;
} }
if (JsonValueType(val) != JSON_STRING) if (loginRequest.type != REQUEST_TYPE_PASSWORD)
{
HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
response = MatrixErrorCreate(M_BAD_JSON, NULL);
break;
}
type = JsonValueAsString(val);
if (!StrEquals(type, "m.login.password"))
{ {
HttpResponseStatus(args->context, HTTP_BAD_REQUEST); HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
response = MatrixErrorCreate(M_UNRECOGNIZED, NULL); response = MatrixErrorCreate(M_UNRECOGNIZED, NULL);
break; break;
} }
val = HashMapGet(request, "identifier"); identifier = loginRequest.identifier;
if (!val)
{
HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
response = MatrixErrorCreate(M_MISSING_PARAM, NULL);
break;
}
if (JsonValueType(val) != JSON_OBJECT)
{
HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
response = MatrixErrorCreate(M_BAD_JSON, NULL);
break;
}
identifier = JsonValueAsObject(val);
val = HashMapGet(identifier, "type"); val = HashMapGet(identifier, "type");
if (!val) if (!val)
@ -150,23 +136,16 @@ ROUTE_IMPL(RouteLogin, path, argp)
response = MatrixErrorCreate(M_UNRECOGNIZED, NULL); response = MatrixErrorCreate(M_UNRECOGNIZED, NULL);
break; break;
} }
if (!LoginRequestUserIdentifierFromJson(identifier,
val = HashMapGet(identifier, "user"); &userIdentifier, &msg))
if (!val)
{ {
HttpResponseStatus(args->context, HTTP_BAD_REQUEST); HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
response = MatrixErrorCreate(M_MISSING_PARAM, NULL); response = MatrixErrorCreate(M_BAD_JSON, msg);
break; break;
} }
if (JsonValueType(val) != JSON_STRING)
{
HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
response = MatrixErrorCreate(M_BAD_JSON, NULL);
break;
}
userId = UserIdParse(JsonValueAsString(val), config->serverName); userId = UserIdParse(userIdentifier.user, config->serverName);
if (!userId) if (!userId)
{ {
HttpResponseStatus(args->context, HTTP_BAD_REQUEST); HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
@ -181,62 +160,12 @@ ROUTE_IMPL(RouteLogin, path, argp)
response = MatrixErrorCreate(M_FORBIDDEN, NULL); response = MatrixErrorCreate(M_FORBIDDEN, NULL);
break; break;
} }
deviceId = loginRequest.device_id;
val = HashMapGet(request, "device_id"); initialDeviceDisplayName =loginRequest.initial_device_display_name;
if (val) password = loginRequest.password;
{ refreshToken = loginRequest.refresh_token;
if (JsonValueType(val) != JSON_STRING)
{
HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
response = MatrixErrorCreate(M_BAD_JSON, NULL);
break;
}
deviceId = JsonValueAsString(val);
}
val = HashMapGet(request, "initial_device_display_name");
if (val)
{
if (JsonValueType(val) != JSON_STRING)
{
HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
response = MatrixErrorCreate(M_BAD_JSON, NULL);
break;
}
initialDeviceDisplayName = JsonValueAsString(val);
}
val = HashMapGet(request, "password");
if (!val)
{
HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
response = MatrixErrorCreate(M_MISSING_PARAM, NULL);
break;
}
if (JsonValueType(val) != JSON_STRING)
{
HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
response = MatrixErrorCreate(M_BAD_JSON, NULL);
break;
}
password = JsonValueAsString(val);
val = HashMapGet(request, "refresh_token");
if (val)
{
if (JsonValueType(val) != JSON_BOOLEAN)
{
HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
response = MatrixErrorCreate(M_BAD_JSON, NULL);
break;
}
refreshToken = JsonValueAsBoolean(val);
}
user = UserLock(db, userId->localpart); user = UserLock(db, userId->localpart);
@ -309,5 +238,8 @@ ROUTE_IMPL(RouteLogin, path, argp)
JsonFree(request); JsonFree(request);
ConfigUnlock(config); ConfigUnlock(config);
LoginRequestFree(&loginRequest);
LoginRequestUserIdentifierFree(&userIdentifier);
return response; return response;
} }

View file

@ -30,6 +30,8 @@
#include <Cytoplasm/Str.h> #include <Cytoplasm/Str.h>
#include <Cytoplasm/Memory.h> #include <Cytoplasm/Memory.h>
#include <Schema/Registration.h>
#include <User.h> #include <User.h>
#include <Uia.h> #include <Uia.h>
#include <RegToken.h> #include <RegToken.h>
@ -55,22 +57,15 @@ ROUTE_IMPL(RouteRegister, path, argp)
HashMap *request = NULL; HashMap *request = NULL;
HashMap *response = NULL; HashMap *response = NULL;
JsonValue *val; RegistrationRequest regReq;
char *kind; char *kind;
char *username = NULL;
char *password = NULL;
char *initialDeviceDisplayName = NULL;
int refreshToken = 0;
int inhibitLogin = 0;
char *deviceId = NULL;
char *fullUsername; char *fullUsername;
char *msg;
char *username;
Db *db = args->matrixArgs->db; Db *db = args->matrixArgs->db;
User *user = NULL; User *user = NULL;
Array *uiaFlows = NULL; Array *uiaFlows = NULL;
int uiaResult; int uiaResult;
@ -79,6 +74,16 @@ ROUTE_IMPL(RouteRegister, path, argp)
Config *config = ConfigLock(db); Config *config = ConfigLock(db);
regReq.username = NULL;
regReq.password = NULL;
regReq.device_id = NULL;
regReq.initial_device_display_name = NULL;
regReq.refresh_token = 0;
regReq.inhibit_login = 0;
if (!config) if (!config)
{ {
Log(LOG_ERR, "Registration endpoint failed to lock configuration."); Log(LOG_ERR, "Registration endpoint failed to lock configuration.");
@ -102,26 +107,23 @@ ROUTE_IMPL(RouteRegister, path, argp)
response = MatrixErrorCreate(M_NOT_JSON, NULL); response = MatrixErrorCreate(M_NOT_JSON, NULL);
goto end; goto end;
} }
if (!RegistrationRequestFromJson(request, &regReq, &msg))
val = HashMapGet(request, "username");
if (val)
{ {
if (JsonValueType(val) != JSON_STRING) HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
{ response = MatrixErrorCreate(M_NOT_JSON, msg);
HttpResponseStatus(args->context, HTTP_BAD_REQUEST); goto end;
response = MatrixErrorCreate(M_BAD_JSON, NULL); }
goto finish;
}
username = StrDuplicate(JsonValueAsString(val));
if (!UserValidate(username, config->serverName)) if (regReq.username)
{
if (!UserValidate(regReq.username, config->serverName))
{ {
HttpResponseStatus(args->context, HTTP_BAD_REQUEST); HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
response = MatrixErrorCreate(M_INVALID_USERNAME, NULL); response = MatrixErrorCreate(M_INVALID_USERNAME, NULL);
goto finish; goto finish;
} }
if (UserExists(db, username)) if (UserExists(db, regReq.username))
{ {
HttpResponseStatus(args->context, HTTP_BAD_REQUEST); HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
response = MatrixErrorCreate(M_USER_IN_USE, NULL); response = MatrixErrorCreate(M_USER_IN_USE, NULL);
@ -158,99 +160,44 @@ ROUTE_IMPL(RouteRegister, path, argp)
/* We don't support guest accounts yet */ /* We don't support guest accounts yet */
if (kind && !StrEquals(kind, "user")) if (kind && !StrEquals(kind, "user"))
{ {
msg = "Guest accounts are currently not supported";
HttpResponseStatus(args->context, HTTP_FORBIDDEN); HttpResponseStatus(args->context, HTTP_FORBIDDEN);
response = MatrixErrorCreate(M_INVALID_PARAM, NULL); response = MatrixErrorCreate(M_INVALID_PARAM, msg);
goto finish; goto finish;
} }
val = HashMapGet(request, "password"); if (!regReq.password)
if (!val)
{ {
msg = "'password' field is unset";
HttpResponseStatus(args->context, HTTP_BAD_REQUEST); HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
response = MatrixErrorCreate(M_MISSING_PARAM, NULL); response = MatrixErrorCreate(M_MISSING_PARAM, msg);
goto finish; goto finish;
} }
if (JsonValueType(val) != JSON_STRING) /* All of the other fields are optional, we don't have to check
{ * them. */
HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
response = MatrixErrorCreate(M_BAD_JSON, NULL);
goto finish;
}
password = StrDuplicate(JsonValueAsString(val)); user = UserCreate(db, regReq.username, regReq.password);
val = HashMapGet(request, "device_id");
if (val)
{
if (JsonValueType(val) != JSON_STRING)
{
HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
response = MatrixErrorCreate(M_BAD_JSON, NULL);
goto finish;
}
deviceId = StrDuplicate(JsonValueAsString(val));
}
val = HashMapGet(request, "inhibit_login");
if (val)
{
if (JsonValueType(val) != JSON_BOOLEAN)
{
HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
response = MatrixErrorCreate(M_BAD_JSON, NULL);
goto finish;
}
inhibitLogin = JsonValueAsBoolean(val);
}
val = HashMapGet(request, "initial_device_display_name");
if (val)
{
if (JsonValueType(val) != JSON_STRING)
{
HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
response = MatrixErrorCreate(M_BAD_JSON, NULL);
goto finish;
}
initialDeviceDisplayName = StrDuplicate(JsonValueAsString(val));
}
val = HashMapGet(request, "refresh_token");
if (val)
{
if (JsonValueType(val) != JSON_BOOLEAN)
{
HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
response = MatrixErrorCreate(M_BAD_JSON, NULL);
goto finish;
}
refreshToken = JsonValueAsBoolean(val);
}
user = UserCreate(db, username, password);
response = HashMapCreate(); response = HashMapCreate();
fullUsername = StrConcat(4, "@", UserGetName(user), ":", config->serverName); fullUsername = StrConcat(4,
"@", UserGetName(user), ":", config->serverName);
HashMapSet(response, "user_id", JsonValueString(fullUsername)); HashMapSet(response, "user_id", JsonValueString(fullUsername));
Free(fullUsername); Free(fullUsername);
HttpResponseStatus(args->context, HTTP_OK); HttpResponseStatus(args->context, HTTP_OK);
if (!inhibitLogin) if (!regReq.inhibit_login)
{ {
UserLoginInfo *loginInfo = UserLogin(user, password, deviceId, UserLoginInfo *loginInfo = UserLogin(user, regReq.password,
initialDeviceDisplayName, refreshToken); regReq.device_id, regReq.initial_device_display_name,
regReq.refresh_token);
HashMapSet(response, "access_token", HashMapSet(response, "access_token",
JsonValueString(loginInfo->accessToken->string)); JsonValueString(loginInfo->accessToken->string));
HashMapSet(response, "device_id", HashMapSet(response, "device_id",
JsonValueString(loginInfo->accessToken->deviceId)); JsonValueString(loginInfo->accessToken->deviceId));
if (refreshToken) if (regReq.refresh_token)
{ {
HashMapSet(response, "expires_in_ms", HashMapSet(response, "expires_in_ms",
JsonValueInteger(loginInfo->accessToken->lifetime)); JsonValueInteger(loginInfo->accessToken->lifetime));
@ -294,10 +241,7 @@ ROUTE_IMPL(RouteRegister, path, argp)
UserUnlock(user); UserUnlock(user);
finish: finish:
UiaFlowsFree(uiaFlows); UiaFlowsFree(uiaFlows);
Free(username); RegistrationRequestFree(&regReq);
Free(password);
Free(deviceId);
Free(initialDeviceDisplayName);
JsonFree(request); JsonFree(request);
} }
else else

View file

@ -26,14 +26,31 @@
#include <Cytoplasm/Str.h> #include <Cytoplasm/Str.h>
#include <Cytoplasm/Json.h> #include <Cytoplasm/Json.h>
#include <Schema/RequestToken.h>
ROUTE_IMPL(RouteRequestToken, path, argp) ROUTE_IMPL(RouteRequestToken, path, argp)
{ {
RouteArgs *args = argp; RouteArgs *args = argp;
char *type = ArrayGet(path, 0); char *type = ArrayGet(path, 0);
HashMap *request; HashMap *request;
HashMap *response; HashMap *response;
JsonValue *val;
char *str; char *msg;
RequestToken reqTok;
Int64 minusOne = Int64Neg(Int64Create(0, 1));
reqTok.client_secret = NULL;
reqTok.next_link = NULL;
reqTok.id_access_token = NULL;
reqTok.id_server = NULL;
reqTok.email = NULL;
reqTok.country = NULL;
reqTok.phone_number = NULL;
reqTok.send_attempt = minusOne;
if (HttpRequestMethodGet(args->context) != HTTP_POST) if (HttpRequestMethodGet(args->context) != HTTP_POST)
{ {
@ -48,87 +65,92 @@ ROUTE_IMPL(RouteRequestToken, path, argp)
return MatrixErrorCreate(M_NOT_JSON, NULL); return MatrixErrorCreate(M_NOT_JSON, NULL);
} }
val = HashMapGet(request, "client_secret"); if (!RequestTokenFromJson(request, &reqTok, &msg))
if (!val || JsonValueType(val) != JSON_STRING)
{ {
HttpResponseStatus(args->context, HTTP_BAD_REQUEST); HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
response = MatrixErrorCreate(M_BAD_JSON, NULL); response = MatrixErrorCreate(M_BAD_JSON, msg);
goto finish; goto finish;
} }
str = JsonValueAsString(val); if (!reqTok.client_secret)
if (strlen(str) > 255 || StrBlank(str))
{ {
msg = "'client_secret' is not set";
HttpResponseStatus(args->context, HTTP_BAD_REQUEST); HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
response = MatrixErrorCreate(M_BAD_JSON, NULL); response = MatrixErrorCreate(M_BAD_JSON, msg);
goto finish; goto finish;
} }
val = HashMapGet(request, "send_attempt"); if (strlen(reqTok.client_secret) > 255 || StrBlank(reqTok.client_secret))
if (!val || JsonValueType(val) != JSON_INTEGER)
{ {
msg = "'client_secret' is blank or too long";
HttpResponseStatus(args->context, HTTP_BAD_REQUEST); HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
response = MatrixErrorCreate(M_BAD_JSON, NULL); response = MatrixErrorCreate(M_BAD_JSON, msg);
goto finish; goto finish;
} }
val = HashMapGet(request, "next_link"); if (Int64Eq(reqTok.send_attempt, minusOne))
if (val && JsonValueType(val) != JSON_STRING)
{ {
msg = "Invalid or inexistent 'send_attempt'";
HttpResponseStatus(args->context, HTTP_BAD_REQUEST); HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
response = MatrixErrorCreate(M_BAD_JSON, NULL); response = MatrixErrorCreate(M_BAD_JSON, msg);
goto finish; goto finish;
} }
val = HashMapGet(request, "id_access_token"); if (!reqTok.next_link)
if (val && JsonValueType(val) != JSON_STRING)
{ {
msg = "'next_link' is not set";
HttpResponseStatus(args->context, HTTP_BAD_REQUEST); HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
response = MatrixErrorCreate(M_BAD_JSON, NULL); response = MatrixErrorCreate(M_BAD_JSON, msg);
goto finish; goto finish;
} }
if (!reqTok.id_access_token)
val = HashMapGet(request, "id_server");
if (val && JsonValueType(val) != JSON_STRING)
{ {
msg = "'id_access_token' is not set";
HttpResponseStatus(args->context, HTTP_BAD_REQUEST); HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
response = MatrixErrorCreate(M_BAD_JSON, NULL); response = MatrixErrorCreate(M_BAD_JSON, msg);
goto finish;
}
if (!reqTok.id_server)
{
msg = "'id_server' is not set";
HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
response = MatrixErrorCreate(M_BAD_JSON, msg);
goto finish; goto finish;
} }
if (StrEquals(type, "email")) if (StrEquals(type, "email"))
{ {
val = HashMapGet(request, "email"); if (!reqTok.email)
if (val && JsonValueType(val) != JSON_STRING)
{ {
msg = "Type is set to 'email' yet none was set";
HttpResponseStatus(args->context, HTTP_BAD_REQUEST); HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
response = MatrixErrorCreate(M_BAD_JSON, NULL); response = MatrixErrorCreate(M_BAD_JSON, msg);
goto finish; goto finish;
} }
} }
else if (StrEquals(type, "msisdn")) else if (StrEquals(type, "msisdn"))
{ {
val = HashMapGet(request, "country"); if (!reqTok.country)
if (val && JsonValueType(val) != JSON_STRING)
{ {
msg = "Type is set to 'msisdn' yet no country is set";
HttpResponseStatus(args->context, HTTP_BAD_REQUEST); HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
response = MatrixErrorCreate(M_BAD_JSON, NULL); response = MatrixErrorCreate(M_BAD_JSON, msg);
goto finish; goto finish;
} }
str = JsonValueAsString(val); if (strlen(reqTok.country) != 2)
if (strlen(str) != 2)
{ {
msg = "Invalid country tag, length must be 2";
HttpResponseStatus(args->context, HTTP_BAD_REQUEST); HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
response = MatrixErrorCreate(M_BAD_JSON, NULL); response = MatrixErrorCreate(M_BAD_JSON, msg);
goto finish; goto finish;
} }
val = HashMapGet(request, "phone_number"); if (!reqTok.phone_number)
if (val && JsonValueType(val) != JSON_STRING)
{ {
msg = "Type is set to 'msisdn' yet phone_number is unset";
HttpResponseStatus(args->context, HTTP_BAD_REQUEST); HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
response = MatrixErrorCreate(M_BAD_JSON, NULL); response = MatrixErrorCreate(M_BAD_JSON, msg);
goto finish; goto finish;
} }
} }
@ -145,5 +167,6 @@ ROUTE_IMPL(RouteRequestToken, path, argp)
finish: finish:
JsonFree(request); JsonFree(request);
RequestTokenFree(&reqTok);
return response; return response;
} }