/*
This file is part of Anastasis
Copyright (C) 2022 Anastasis SARL
Anastasis is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along with
Anastasis; see the file COPYING. If not, see
*/
/**
* @file anastasis-httpd_policy-meta.c
* @brief functions to handle incoming requests on /policy/$PID/meta
* @author Christian Grothoff
*/
#include "platform.h"
#include "anastasis-httpd.h"
#include "anastasis-httpd_policy-meta.h"
#include "anastasis_service.h"
#include
#include
#include
#include
#include
/**
* Function called on matching meta data. Note that if the client did
* not provide meta data for @a version, the function will be called
* with @a recovery_meta_data being NULL.
*
* @param cls closure with a `json_t *` to build up
* @param version the version of the recovery document
* @param ts timestamp when the document was created
* @param recovery_meta_data contains meta data about the encrypted recovery document
* @param recovery_meta_data_size size of @a recovery_meta_data blob
* @return #GNUNET_OK to continue to iterate, #GNUNET_NO to abort iteration
*/
static enum GNUNET_GenericReturnValue
build_meta_result (void *cls,
uint32_t version,
struct GNUNET_TIME_Timestamp ts,
const void *recovery_meta_data,
size_t recovery_meta_data_size)
{
json_t *result = cls;
char version_s[14];
GNUNET_snprintf (version_s,
sizeof (version_s),
"%u",
(unsigned int) version);
GNUNET_assert (0 ==
json_object_set_new (
result,
version_s,
GNUNET_JSON_PACK (
GNUNET_JSON_pack_data_varsize (
"meta",
recovery_meta_data,
recovery_meta_data_size),
GNUNET_JSON_pack_timestamp (
"upload_time",
ts))));
return GNUNET_OK;
}
/**
* Return the meta data on recovery documents of @a account on @a
* connection.
*
* @param connection MHD connection to use
* @param account_pub account to query
* @return MHD result code
*/
static MHD_RESULT
return_policy_meta (
struct MHD_Connection *connection,
const struct ANASTASIS_CRYPTO_AccountPublicKeyP *account_pub)
{
enum GNUNET_DB_QueryStatus qs;
uint32_t max_version = INT32_MAX; /* Postgres is using signed ints... */
json_t *result;
{
const char *version_s;
version_s = MHD_lookup_connection_value (connection,
MHD_GET_ARGUMENT_KIND,
"max_version");
if (NULL != version_s)
{
char dummy;
if (1 != sscanf (version_s,
"%u%c",
&max_version,
&dummy))
{
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_BAD_REQUEST,
TALER_EC_GENERIC_PARAMETER_MALFORMED,
"version");
}
if (max_version > INT32_MAX)
max_version = INT32_MAX; /* cap to signed range */
}
}
result = json_object ();
GNUNET_assert (NULL != result);
qs = db->get_recovery_meta_data (db->cls,
account_pub,
max_version,
&build_meta_result,
result);
switch (qs)
{
case GNUNET_DB_STATUS_HARD_ERROR:
GNUNET_break (0);
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_GENERIC_DB_FETCH_FAILED,
"get_recovery_document");
case GNUNET_DB_STATUS_SOFT_ERROR:
GNUNET_break (0);
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_GENERIC_DB_SOFT_FAILURE,
"get_recovery_document");
case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_NOT_FOUND,
TALER_EC_ANASTASIS_POLICY_NOT_FOUND,
NULL);
default:
/* interesting case below */
break;
}
return TALER_MHD_reply_json_steal (connection,
result,
MHD_HTTP_OK);
}
MHD_RESULT
AH_policy_meta_get (
struct MHD_Connection *connection,
const struct ANASTASIS_CRYPTO_AccountPublicKeyP *account_pub)
{
struct GNUNET_HashCode recovery_data_hash;
enum ANASTASIS_DB_AccountStatus as;
uint32_t version;
struct GNUNET_TIME_Timestamp expiration;
as = db->lookup_account (db->cls,
account_pub,
&expiration,
&recovery_data_hash,
&version);
switch (as)
{
case ANASTASIS_DB_ACCOUNT_STATUS_PAYMENT_REQUIRED:
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_NOT_FOUND,
TALER_EC_ANASTASIS_POLICY_NOT_FOUND,
"account expired: payment required");
case ANASTASIS_DB_ACCOUNT_STATUS_HARD_ERROR:
GNUNET_break (0);
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_GENERIC_DB_FETCH_FAILED,
"lookup account");
case ANASTASIS_DB_ACCOUNT_STATUS_NO_RESULTS:
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_NOT_FOUND,
TALER_EC_ANASTASIS_POLICY_NOT_FOUND,
"no such account");
case ANASTASIS_DB_ACCOUNT_STATUS_VALID_HASH_RETURNED:
/* We have results, should fetch and return them! */
break;
}
return return_policy_meta (connection,
account_pub);
}