select_aml_decisions.c (8270B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2024 Taler Systems SA 4 5 TALER is free software; you can redistribute it and/or modify it under the 6 terms of the GNU General Public License as published by the Free Software 7 Foundation; either version 3, or (at your option) any later version. 8 9 TALER is distributed in the hope that it will be useful, but WITHOUT ANY 10 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 13 You should have received a copy of the GNU General Public License along with 14 TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 15 */ 16 /** 17 * @file exchangedb/select_aml_decisions.c 18 * @brief Implementation of the select_aml_decisions function for Postgres 19 * @author Christian Grothoff 20 */ 21 #include "taler/taler_pq_lib.h" 22 #include "exchange-database/select_aml_decisions.h" 23 #include "helper.h" 24 25 26 /** 27 * Closure for #handle_aml_result. 28 */ 29 struct AmlProcessResultContext 30 { 31 /** 32 * Function to call on each result. 33 */ 34 TALER_EXCHANGEDB_AmlDecisionCallback cb; 35 36 /** 37 * Closure for @e cb. 38 */ 39 void *cb_cls; 40 41 /** 42 * Plugin context. 43 */ 44 struct TALER_EXCHANGEDB_PostgresContext *pg; 45 46 /** 47 * Set to #GNUNET_SYSERR on serious errors. 48 */ 49 enum GNUNET_GenericReturnValue status; 50 }; 51 52 53 /** 54 * Function to be called with the results of a SELECT statement 55 * that has returned @a num_results results. Helper function 56 * for #TALER_EXCHANGEDB_select_aml_decisions(). 57 * 58 * @param cls closure of type `struct AmlProcessResultContext *` 59 * @param result the postgres result 60 * @param num_results the number of results in @a result 61 */ 62 static void 63 handle_aml_result (void *cls, 64 PGresult *result, 65 unsigned int num_results) 66 { 67 struct AmlProcessResultContext *ctx = cls; 68 69 for (unsigned int i = 0; i<num_results; i++) 70 { 71 struct TALER_NormalizedPaytoHashP h_payto; 72 uint64_t rowid; 73 char *justification = NULL; 74 struct GNUNET_TIME_Timestamp decision_time; 75 struct GNUNET_TIME_Absolute expiration_time; 76 json_t *jproperties = NULL; 77 bool is_wallet; 78 bool to_investigate; 79 bool is_active; 80 json_t *account_rules; 81 struct TALER_FullPayto payto; 82 struct GNUNET_PQ_ResultSpec rs[] = { 83 GNUNET_PQ_result_spec_uint64 ("outcome_serial_id", 84 &rowid), 85 GNUNET_PQ_result_spec_auto_from_type ("h_payto", 86 &h_payto), 87 GNUNET_PQ_result_spec_bool ("is_wallet", 88 &is_wallet), 89 GNUNET_PQ_result_spec_allow_null ( 90 GNUNET_PQ_result_spec_string ("justification", 91 &justification), 92 NULL), 93 GNUNET_PQ_result_spec_timestamp ("decision_time", 94 &decision_time), 95 GNUNET_PQ_result_spec_absolute_time ("expiration_time", 96 &expiration_time), 97 GNUNET_PQ_result_spec_allow_null ( 98 TALER_PQ_result_spec_json ("jproperties", 99 &jproperties), 100 NULL), 101 TALER_PQ_result_spec_json ("jnew_rules", 102 &account_rules), 103 GNUNET_PQ_result_spec_bool ("to_investigate", 104 &to_investigate), 105 GNUNET_PQ_result_spec_bool ("is_active", 106 &is_active), 107 GNUNET_PQ_result_spec_string ("payto_uri", 108 &payto.full_payto), 109 GNUNET_PQ_result_spec_end 110 }; 111 112 if (GNUNET_OK != 113 GNUNET_PQ_extract_result (result, 114 rs, 115 i)) 116 { 117 GNUNET_break (0); 118 ctx->status = GNUNET_SYSERR; 119 return; 120 } 121 if (GNUNET_TIME_absolute_is_past (expiration_time)) 122 is_active = false; 123 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 124 "Returning AML decisions for `%s' (%s)\n", 125 TALER_B2S (&h_payto), 126 is_wallet 127 ? "wallet" 128 : "account"); 129 ctx->cb (ctx->cb_cls, 130 rowid, 131 justification, 132 &h_payto, 133 decision_time, 134 expiration_time, 135 jproperties, 136 to_investigate, 137 is_active, 138 is_wallet, 139 payto, 140 account_rules); 141 GNUNET_PQ_cleanup_result (rs); 142 } 143 } 144 145 146 enum GNUNET_DB_QueryStatus 147 TALER_EXCHANGEDB_select_aml_decisions ( 148 struct TALER_EXCHANGEDB_PostgresContext *pg, 149 const struct TALER_NormalizedPaytoHashP *h_payto, 150 enum TALER_EXCHANGE_YesNoAll investigation_only, 151 enum TALER_EXCHANGE_YesNoAll active_only, 152 uint64_t offset, 153 int64_t limit, 154 TALER_EXCHANGEDB_AmlDecisionCallback cb, 155 void *cb_cls) 156 { 157 uint64_t ulimit = (limit > 0) ? limit : -limit; 158 struct GNUNET_PQ_QueryParam params[] = { 159 GNUNET_PQ_query_param_bool (NULL == h_payto), 160 NULL == h_payto 161 ? GNUNET_PQ_query_param_null () 162 : GNUNET_PQ_query_param_auto_from_type (h_payto), 163 GNUNET_PQ_query_param_bool ((TALER_EXCHANGE_YNA_ALL == 164 investigation_only)), 165 GNUNET_PQ_query_param_bool ((TALER_EXCHANGE_YNA_YES == 166 investigation_only)), 167 GNUNET_PQ_query_param_bool ((TALER_EXCHANGE_YNA_ALL == 168 active_only)), 169 GNUNET_PQ_query_param_bool ((TALER_EXCHANGE_YNA_YES == 170 active_only)), 171 GNUNET_PQ_query_param_uint64 (&offset), 172 GNUNET_PQ_query_param_uint64 (&ulimit), 173 GNUNET_PQ_query_param_end 174 }; 175 struct AmlProcessResultContext ctx = { 176 .cb = cb, 177 .cb_cls = cb_cls, 178 .pg = pg, 179 .status = GNUNET_OK 180 }; 181 enum GNUNET_DB_QueryStatus qs; 182 const char *stmt = (limit > 0) 183 ? "select_aml_decisions_inc" 184 : "select_aml_decisions_dec"; 185 186 PREPARE (pg, 187 "select_aml_decisions_inc", 188 "SELECT" 189 " lo.outcome_serial_id" 190 ",lo.h_payto" 191 ",ah.justification" 192 ",lo.decision_time" 193 ",lo.expiration_time" 194 ",lo.jproperties::TEXT" 195 ",lo.to_investigate" 196 ",lo.is_active" 197 ",lo.jnew_rules::TEXT" 198 ",kt.is_wallet" 199 ",wt.payto_uri" 200 " FROM legitimization_outcomes lo" 201 " JOIN kyc_targets kt" 202 " ON (lo.h_payto = kt.h_normalized_payto)" 203 " JOIN wire_targets wt" 204 " ON (lo.h_payto = wt.h_normalized_payto)" 205 " LEFT JOIN aml_history ah" 206 " USING (outcome_serial_id)" 207 " WHERE (outcome_serial_id > $7)" 208 " AND ($1 OR (lo.h_payto = $2))" 209 " AND ($3 OR (lo.to_investigate = $4))" 210 " AND ($5 OR (lo.is_active = $6))" 211 " ORDER BY lo.outcome_serial_id ASC" 212 " LIMIT $8"); 213 PREPARE (pg, 214 "select_aml_decisions_dec", 215 "SELECT" 216 " lo.outcome_serial_id" 217 ",lo.h_payto" 218 ",ah.justification" 219 ",lo.decision_time" 220 ",lo.expiration_time" 221 ",lo.jproperties::TEXT" 222 ",lo.to_investigate" 223 ",lo.is_active" 224 ",lo.jnew_rules::TEXT" 225 ",kt.is_wallet" 226 ",wt.payto_uri" 227 " FROM legitimization_outcomes lo" 228 " JOIN kyc_targets kt" 229 " ON (lo.h_payto = kt.h_normalized_payto)" 230 " JOIN wire_targets wt" 231 " ON (lo.h_payto = wt.h_normalized_payto)" 232 " LEFT JOIN aml_history ah" 233 " USING (outcome_serial_id)" 234 " WHERE lo.outcome_serial_id < $7" 235 " AND ($1 OR (lo.h_payto = $2))" 236 " AND ($3 OR (lo.to_investigate = $4))" 237 " AND ($5 OR (lo.is_active = $6))" 238 " ORDER BY lo.outcome_serial_id DESC" 239 " LIMIT $8"); 240 qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, 241 stmt, 242 params, 243 &handle_aml_result, 244 &ctx); 245 if (GNUNET_OK != ctx.status) 246 return GNUNET_DB_STATUS_HARD_ERROR; 247 return qs; 248 }