exchange_api_post-kyc-upload-ID.c (7693B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2024-2026 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 15 <http://www.gnu.org/licenses/> 16 */ 17 /** 18 * @file lib/exchange_api_post-kyc-upload-ID.c 19 * @brief functions to upload client-provided KYC evidence 20 * @author Christian Grothoff 21 */ 22 #include "taler/taler_json_lib.h" 23 #include <microhttpd.h> 24 #include <gnunet/gnunet_curl_lib.h> 25 #include "taler/exchange/post-kyc-upload-ID.h" 26 #include "exchange_api_curl_defaults.h" 27 #include "taler/taler_signatures.h" 28 #include "taler/taler_curl_lib.h" 29 30 31 struct TALER_EXCHANGE_PostKycUploadHandle 32 { 33 34 /** 35 * The base URL for this request. 36 */ 37 char *base_url; 38 39 /** 40 * The full URL for this request. 41 */ 42 char *url; 43 44 /** 45 * Minor context that holds body and headers. 46 */ 47 struct TALER_CURL_PostContext post_ctx; 48 49 /** 50 * Handle for the request. 51 */ 52 struct GNUNET_CURL_Job *job; 53 54 /** 55 * Function to call with the result. 56 */ 57 TALER_EXCHANGE_PostKycUploadCallback cb; 58 59 /** 60 * Closure for @e cb. 61 */ 62 TALER_EXCHANGE_POST_KYC_UPLOAD_RESULT_CLOSURE *cb_cls; 63 64 /** 65 * Reference to the execution context. 66 */ 67 struct GNUNET_CURL_Context *ctx; 68 69 /** 70 * Identifier for the KYC process the upload is for. 71 */ 72 char *id; 73 74 /** 75 * Client-provided evidence to upload (`CustomerKycAttributes`). 76 */ 77 json_t *attributes; 78 79 }; 80 81 82 /** 83 * Function called when we're done processing the 84 * HTTP POST /kyc-upload/$ID request. 85 * 86 * @param cls the `struct TALER_EXCHANGE_PostKycUploadHandle *` 87 * @param response_code HTTP response code, 0 on error 88 * @param response response body, NULL if not in JSON 89 */ 90 static void 91 handle_kyc_upload_finished (void *cls, 92 long response_code, 93 const void *response) 94 { 95 struct TALER_EXCHANGE_PostKycUploadHandle *pksh = cls; 96 const json_t *json = response; 97 struct TALER_EXCHANGE_PostKycUploadResponse adr = { 98 .hr.http_status = (unsigned int) response_code, 99 .hr.reply = json 100 }; 101 102 pksh->job = NULL; 103 switch (response_code) 104 { 105 case 0: 106 /* no reply */ 107 adr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 108 adr.hr.hint = "server offline?"; 109 break; 110 case MHD_HTTP_NO_CONTENT: 111 /* success, nothing to parse */ 112 break; 113 case MHD_HTTP_BAD_REQUEST: 114 adr.hr.ec = TALER_JSON_get_error_code (json); 115 adr.hr.hint = TALER_JSON_get_error_hint (json); 116 break; 117 case MHD_HTTP_NOT_FOUND: 118 adr.hr.ec = TALER_JSON_get_error_code (json); 119 adr.hr.hint = TALER_JSON_get_error_hint (json); 120 break; 121 case MHD_HTTP_CONFLICT: 122 adr.hr.ec = TALER_JSON_get_error_code (json); 123 adr.hr.hint = TALER_JSON_get_error_hint (json); 124 break; 125 case MHD_HTTP_CONTENT_TOO_LARGE: 126 adr.hr.ec = TALER_JSON_get_error_code (json); 127 adr.hr.hint = TALER_JSON_get_error_hint (json); 128 break; 129 case MHD_HTTP_INTERNAL_SERVER_ERROR: 130 adr.hr.ec = TALER_JSON_get_error_code (json); 131 adr.hr.hint = TALER_JSON_get_error_hint (json); 132 break; 133 default: 134 /* unexpected response code */ 135 GNUNET_break_op (0); 136 adr.hr.ec = TALER_JSON_get_error_code (json); 137 adr.hr.hint = TALER_JSON_get_error_hint (json); 138 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 139 "Unexpected response code %u/%d for exchange POST kyc-upload\n", 140 (unsigned int) response_code, 141 (int) adr.hr.ec); 142 break; 143 } 144 if (NULL != pksh->cb) 145 { 146 pksh->cb (pksh->cb_cls, 147 &adr); 148 pksh->cb = NULL; 149 } 150 TALER_EXCHANGE_post_kyc_upload_cancel (pksh); 151 } 152 153 154 struct TALER_EXCHANGE_PostKycUploadHandle * 155 TALER_EXCHANGE_post_kyc_upload_create ( 156 struct GNUNET_CURL_Context *ctx, 157 const char *url, 158 const char *id, 159 const json_t *attributes) 160 { 161 struct TALER_EXCHANGE_PostKycUploadHandle *pksh; 162 163 pksh = GNUNET_new (struct TALER_EXCHANGE_PostKycUploadHandle); 164 pksh->ctx = ctx; 165 pksh->base_url = GNUNET_strdup (url); 166 pksh->id = GNUNET_strdup (id); 167 pksh->attributes = json_incref ((json_t *) attributes); 168 return pksh; 169 } 170 171 172 struct TALER_EXCHANGE_PostKycUploadHandle * 173 TALER_EXCHANGE_post_kyc_upload_accept_tos_create ( 174 struct GNUNET_CURL_Context *ctx, 175 const char *url, 176 const char *id, 177 const char *tos_etag) 178 { 179 struct TALER_EXCHANGE_PostKycUploadHandle *pksh; 180 json_t *attributes; 181 182 attributes = GNUNET_JSON_PACK ( 183 /* form ID of the terms-of-service acceptance form */ 184 GNUNET_JSON_pack_string ("FORM_ID", 185 "accept-tos"), 186 /* version (ETag) of the terms of service being accepted */ 187 GNUNET_JSON_pack_string ("ACCEPTED_TERMS_OF_SERVICE", 188 tos_etag), 189 /* affirm that the terms of service were read/downloaded */ 190 GNUNET_JSON_pack_bool ("DOWNLOADED_TERMS_OF_SERVICE", 191 true)); 192 pksh = TALER_EXCHANGE_post_kyc_upload_create (ctx, 193 url, 194 id, 195 attributes); 196 json_decref (attributes); 197 return pksh; 198 } 199 200 201 enum TALER_ErrorCode 202 TALER_EXCHANGE_post_kyc_upload_start ( 203 struct TALER_EXCHANGE_PostKycUploadHandle *pksh, 204 TALER_EXCHANGE_PostKycUploadCallback cb, 205 TALER_EXCHANGE_POST_KYC_UPLOAD_RESULT_CLOSURE *cb_cls) 206 { 207 CURL *eh; 208 char *path; 209 210 pksh->cb = cb; 211 pksh->cb_cls = cb_cls; 212 GNUNET_asprintf (&path, 213 "kyc-upload/%s", 214 pksh->id); 215 pksh->url = TALER_url_join (pksh->base_url, 216 path, 217 NULL); 218 GNUNET_free (path); 219 if (NULL == pksh->url) 220 { 221 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 222 "Could not construct request URL.\n"); 223 return TALER_EC_GENERIC_CONFIGURATION_INVALID; 224 } 225 eh = TALER_EXCHANGE_curl_easy_get_ (pksh->url); 226 if ( (NULL == eh) || 227 (GNUNET_OK != 228 TALER_curl_easy_post (&pksh->post_ctx, 229 eh, 230 pksh->attributes)) ) 231 { 232 GNUNET_break (0); 233 if (NULL != eh) 234 curl_easy_cleanup (eh); 235 GNUNET_free (pksh->url); 236 pksh->url = NULL; 237 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 238 } 239 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 240 "Requesting URL '%s'\n", 241 pksh->url); 242 pksh->job = GNUNET_CURL_job_add2 (pksh->ctx, 243 eh, 244 pksh->post_ctx.headers, 245 &handle_kyc_upload_finished, 246 pksh); 247 if (NULL == pksh->job) 248 { 249 TALER_curl_easy_post_finished (&pksh->post_ctx); 250 GNUNET_free (pksh->url); 251 pksh->url = NULL; 252 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 253 } 254 return TALER_EC_NONE; 255 } 256 257 258 void 259 TALER_EXCHANGE_post_kyc_upload_cancel ( 260 struct TALER_EXCHANGE_PostKycUploadHandle *pksh) 261 { 262 if (NULL != pksh->job) 263 { 264 GNUNET_CURL_job_cancel (pksh->job); 265 pksh->job = NULL; 266 } 267 TALER_curl_easy_post_finished (&pksh->post_ctx); 268 json_decref (pksh->attributes); 269 GNUNET_free (pksh->url); 270 GNUNET_free (pksh->base_url); 271 GNUNET_free (pksh->id); 272 GNUNET_free (pksh); 273 } 274 275 276 /* end of exchange_api_post-kyc-upload-ID.c */