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