/* 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); }