/* This file is part of TALER Copyright (C) 2022, 2023 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. TALER 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 General Public License for more details. You should have received a copy of the GNU General Public License along with TALER; see the file COPYING. If not, see */ /** * @file backenddb/pg_lookup_instances.c * @brief Implementation of the lookup_instances function for Postgres * @author Christian Grothoff */ #include "platform.h" #include #include #include "pg_lookup_instances.h" #include "pg_helper.h" /** * Context for lookup_instances(). */ struct LookupInstancesContext { /** * Function to call with the results. */ TALER_MERCHANTDB_InstanceCallback cb; /** * Closure for @e cb. */ void *cb_cls; /** * Database context. */ struct PostgresClosure *pg; /** * Instance settings, valid only during find_instances_cb(). */ struct TALER_MERCHANTDB_InstanceSettings is; /** * Instance authentication settings, valid only during find_instances_cb(). */ struct TALER_MERCHANTDB_InstanceAuthSettings ias; /** * Instance serial number, valid only during find_instances_cb(). */ uint64_t instance_serial; /** * Public key of the current instance, valid only during find_instances_cb(). */ struct TALER_MerchantPublicKeyP merchant_pub; /** * Set to the return value on errors. */ enum GNUNET_DB_QueryStatus qs; /** * true if we only are interested in instances for which we have the private key. */ bool active_only; }; /** * Helper function to run PREPARE() macro. * * @param pg closure to pass * @return status of the preparation */ static enum GNUNET_DB_QueryStatus prepare (struct PostgresClosure *pg) { PREPARE (pg, "lookup_instance_private_key", "SELECT" " merchant_priv" " FROM merchant_keys" " WHERE merchant_serial=$1"); return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; } /** * We are processing an instances lookup and have the @a accounts. * Find the private key if possible, and invoke the callback. * * @param lic context we are handling */ static void call_cb (struct LookupInstancesContext *lic) { struct PostgresClosure *pg = lic->pg; enum GNUNET_DB_QueryStatus qs; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_uint64 (&lic->instance_serial), GNUNET_PQ_query_param_end }; struct TALER_MerchantPrivateKeyP merchant_priv; struct GNUNET_PQ_ResultSpec rs[] = { GNUNET_PQ_result_spec_auto_from_type ("merchant_priv", &merchant_priv), GNUNET_PQ_result_spec_end }; qs = prepare (pg); if (qs < 0) { GNUNET_break (0); lic->qs = GNUNET_DB_STATUS_HARD_ERROR; return; } qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, "lookup_instance_private_key", params, rs); if (qs < 0) { GNUNET_break (0); lic->qs = GNUNET_DB_STATUS_HARD_ERROR; return; } if ( (0 == qs) && (lic->active_only) ) return; /* skip, not interesting */ lic->cb (lic->cb_cls, &lic->merchant_pub, (0 == qs) ? NULL : &merchant_priv, &lic->is, &lic->ias); } /** * Function to be called with the results of a SELECT statement * that has returned @a num_results results about instances. * * @param cls of type `struct FindInstancesContext *` * @param result the postgres result * @param num_results the number of results in @a result */ static void lookup_instances_cb (void *cls, PGresult *result, unsigned int num_results) { struct LookupInstancesContext *lic = cls; struct PostgresClosure *pg = lic->pg; lic->qs = prepare (pg); if (lic->qs < 0) { GNUNET_break (0); return; } for (unsigned int i = 0; i < num_results; i++) { bool no_auth; bool no_salt; uint32_t ut32; struct GNUNET_PQ_ResultSpec rs[] = { GNUNET_PQ_result_spec_uint64 ("merchant_serial", &lic->instance_serial), GNUNET_PQ_result_spec_auto_from_type ("merchant_pub", &lic->merchant_pub), GNUNET_PQ_result_spec_allow_null ( GNUNET_PQ_result_spec_auto_from_type ("auth_hash", &lic->ias.auth_hash), &no_auth), GNUNET_PQ_result_spec_allow_null ( GNUNET_PQ_result_spec_auto_from_type ("auth_salt", &lic->ias.auth_salt), &no_salt), GNUNET_PQ_result_spec_string ("merchant_id", &lic->is.id), GNUNET_PQ_result_spec_string ("merchant_name", &lic->is.name), GNUNET_PQ_result_spec_uint32 ("user_type", &ut32), TALER_PQ_result_spec_json ("address", &lic->is.address), TALER_PQ_result_spec_json ("jurisdiction", &lic->is.jurisdiction), GNUNET_PQ_result_spec_bool ("use_stefan", &lic->is.use_stefan), GNUNET_PQ_result_spec_relative_time ("default_wire_transfer_delay", &lic->is.default_wire_transfer_delay), GNUNET_PQ_result_spec_relative_time ("default_pay_delay", &lic->is.default_pay_delay), GNUNET_PQ_result_spec_allow_null ( GNUNET_PQ_result_spec_string ("website", &lic->is.website), NULL), GNUNET_PQ_result_spec_allow_null ( GNUNET_PQ_result_spec_string ("email", &lic->is.email), NULL), GNUNET_PQ_result_spec_allow_null ( GNUNET_PQ_result_spec_string ("logo", &lic->is.logo), NULL), GNUNET_PQ_result_spec_end }; memset (&lic->ias.auth_salt, 0, sizeof (lic->ias.auth_salt)); memset (&lic->ias.auth_hash, 0, sizeof (lic->ias.auth_hash)); if (GNUNET_OK != GNUNET_PQ_extract_result (result, rs, i)) { GNUNET_break (0); lic->qs = GNUNET_DB_STATUS_HARD_ERROR; return; } lic->is.ut = (enum TALER_KYCLOGIC_KycUserType) ut32; call_cb (lic); GNUNET_PQ_cleanup_result (rs); if (0 > lic->qs) break; } } enum GNUNET_DB_QueryStatus TMH_PG_lookup_instances (void *cls, bool active_only, TALER_MERCHANTDB_InstanceCallback cb, void *cb_cls) { struct PostgresClosure *pg = cls; struct LookupInstancesContext lic = { .cb = cb, .cb_cls = cb_cls, .active_only = active_only, .pg = pg }; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_end }; enum GNUNET_DB_QueryStatus qs; check_connection (pg); PREPARE (pg, "lookup_instances", "SELECT" " merchant_serial" ",merchant_pub" ",auth_hash" ",auth_salt" ",merchant_id" ",merchant_name" ",user_type" ",address" ",jurisdiction" ",use_stefan" ",default_wire_transfer_delay" ",default_pay_delay" ",website" ",email" ",logo" " FROM merchant_instances"); qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, "lookup_instances", params, &lookup_instances_cb, &lic); if (0 > lic.qs) return lic.qs; return qs; } enum GNUNET_DB_QueryStatus TMH_PG_lookup_instance (void *cls, const char *id, bool active_only, TALER_MERCHANTDB_InstanceCallback cb, void *cb_cls) { struct PostgresClosure *pg = cls; struct LookupInstancesContext lic = { .cb = cb, .cb_cls = cb_cls, .active_only = active_only, .pg = pg }; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_string (id), GNUNET_PQ_query_param_end }; enum GNUNET_DB_QueryStatus qs; check_connection (pg); PREPARE (pg, "lookup_instance", "SELECT" " merchant_serial" ",merchant_pub" ",auth_hash" ",auth_salt" ",merchant_id" ",merchant_name" ",user_type" ",address" ",jurisdiction" ",use_stefan" ",default_wire_transfer_delay" ",default_pay_delay" ",website" ",email" ",logo" " FROM merchant_instances" " WHERE merchant_id=$1"); qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, "lookup_instance", params, &lookup_instances_cb, &lic); if (0 > lic.qs) return lic.qs; return qs; }