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