anastasis_api_truth_store.c (10166B)
1 /* 2 This file is part of Anastasis 3 Copyright (C) 2020, 2021 Anastasis SARL 4 5 Anastasis 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 Anastasis 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 Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> 15 */ 16 /** 17 * @file restclient/anastasis_api_truth_store.c 18 * @brief Implementation of the /truth GET and POST 19 * @author Christian Grothoff 20 * @author Dennis Neufeld 21 * @author Dominik Meister 22 */ 23 #include "platform.h" 24 #include <curl/curl.h> 25 #include <jansson.h> 26 #include <microhttpd.h> /* just for HTTP status codes */ 27 #include "anastasis_service.h" 28 #include "anastasis_api_curl_defaults.h" 29 #include <taler/taler_json_lib.h> 30 #include <taler/taler_merchant_service.h> 31 32 33 struct ANASTASIS_TruthStoreOperation 34 { 35 /** 36 * Complete URL where the backend offers /truth 37 */ 38 char *url; 39 40 /** 41 * Handle for the request. 42 */ 43 struct GNUNET_CURL_Job *job; 44 45 /** 46 * The CURL context to connect to the backend 47 */ 48 struct GNUNET_CURL_Context *ctx; 49 50 /** 51 * The callback to pass the backend response to 52 */ 53 ANASTASIS_TruthStoreCallback cb; 54 55 /** 56 * Closure for @e cb. 57 */ 58 void *cb_cls; 59 60 /** 61 * Reference to data (for cleanup). 62 */ 63 char *data; 64 65 /** 66 * Payment URI we received from the service, or NULL. 67 */ 68 char *pay_uri; 69 }; 70 71 72 void 73 ANASTASIS_truth_store_cancel ( 74 struct ANASTASIS_TruthStoreOperation *tso) 75 { 76 if (NULL != tso->job) 77 { 78 GNUNET_CURL_job_cancel (tso->job); 79 tso->job = NULL; 80 } 81 GNUNET_free (tso->pay_uri); 82 GNUNET_free (tso->url); 83 GNUNET_free (tso->data); 84 GNUNET_free (tso); 85 } 86 87 88 /** 89 * Callback to process POST /truth response 90 * 91 * @param cls the `struct ANASTASIS_TruthStoreOperation` 92 * @param response_code HTTP response code, 0 on error 93 * @param data 94 * @param data_size 95 */ 96 static void 97 handle_truth_store_finished (void *cls, 98 long response_code, 99 const void *data, 100 size_t data_size) 101 { 102 struct ANASTASIS_TruthStoreOperation *tso = cls; 103 struct ANASTASIS_UploadDetails ud; 104 105 tso->job = NULL; 106 memset (&ud, 0, sizeof (ud)); 107 ud.http_status = response_code; 108 ud.ec = TALER_EC_NONE; 109 switch (response_code) 110 { 111 case 0: 112 break; 113 case MHD_HTTP_NO_CONTENT: 114 ud.us = ANASTASIS_US_SUCCESS; 115 break; 116 case MHD_HTTP_NOT_MODIFIED: 117 ud.us = ANASTASIS_US_SUCCESS; 118 break; 119 case MHD_HTTP_BAD_REQUEST: 120 GNUNET_break (0); 121 ud.ec = TALER_JSON_get_error_code2 (data, 122 data_size); 123 break; 124 case MHD_HTTP_PAYMENT_REQUIRED: 125 { 126 struct TALER_MERCHANT_PayUriData pd; 127 128 if ( (NULL == tso->pay_uri) || 129 (GNUNET_OK != 130 TALER_MERCHANT_parse_pay_uri (tso->pay_uri, 131 &pd)) ) 132 { 133 GNUNET_break_op (0); 134 ud.ec = TALER_EC_ANASTASIS_GENERIC_INVALID_PAYMENT_REQUEST; 135 break; 136 } 137 if (GNUNET_OK != 138 GNUNET_STRINGS_string_to_data ( 139 pd.order_id, 140 strlen (pd.order_id), 141 &ud.details.payment.ps, 142 sizeof (ud.details.payment.ps))) 143 { 144 GNUNET_break (0); 145 ud.ec = TALER_EC_ANASTASIS_GENERIC_INVALID_PAYMENT_REQUEST; 146 TALER_MERCHANT_parse_pay_uri_free (&pd); 147 break; 148 } 149 TALER_MERCHANT_parse_pay_uri_free (&pd); 150 } 151 ud.us = ANASTASIS_US_PAYMENT_REQUIRED; 152 ud.details.payment.payment_request = tso->pay_uri; 153 break; 154 case MHD_HTTP_CONFLICT: 155 ud.us = ANASTASIS_US_CONFLICTING_TRUTH; 156 break; 157 case MHD_HTTP_LENGTH_REQUIRED: 158 GNUNET_break (0); 159 break; 160 case MHD_HTTP_REQUEST_ENTITY_TOO_LARGE: 161 ud.ec = TALER_JSON_get_error_code2 (data, 162 data_size); 163 break; 164 case MHD_HTTP_TOO_MANY_REQUESTS: 165 ud.ec = TALER_JSON_get_error_code2 (data, 166 data_size); 167 break; 168 case MHD_HTTP_INTERNAL_SERVER_ERROR: 169 ud.ec = TALER_JSON_get_error_code2 (data, 170 data_size); 171 break; 172 case MHD_HTTP_BAD_GATEWAY: 173 ud.ec = TALER_JSON_get_error_code2 (data, 174 data_size); 175 break; 176 default: 177 ud.ec = TALER_JSON_get_error_code2 (data, 178 data_size); 179 GNUNET_break (0); 180 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 181 "Unexpected HTTP status code %u/%d\n", 182 (unsigned int) response_code, 183 ud.ec); 184 break; 185 } 186 tso->cb (tso->cb_cls, 187 &ud); 188 tso->cb = NULL; 189 ANASTASIS_truth_store_cancel (tso); 190 } 191 192 193 /** 194 * Handle HTTP header received by curl. 195 * 196 * @param buffer one line of HTTP header data 197 * @param size size of an item 198 * @param nitems number of items passed 199 * @param userdata our `struct ANASTASIS_StorePolicyOperation *` 200 * @return `size * nitems` 201 */ 202 static size_t 203 handle_header (char *buffer, 204 size_t size, 205 size_t nitems, 206 void *userdata) 207 { 208 struct ANASTASIS_TruthStoreOperation *tso = userdata; 209 size_t total = size * nitems; 210 char *ndup; 211 const char *hdr_type; 212 char *hdr_val; 213 char *sp; 214 215 ndup = GNUNET_strndup (buffer, 216 total); 217 hdr_type = strtok_r (ndup, 218 ":", 219 &sp); 220 if (NULL == hdr_type) 221 { 222 GNUNET_free (ndup); 223 return total; 224 } 225 hdr_val = strtok_r (NULL, 226 "", 227 &sp); 228 if (NULL == hdr_val) 229 { 230 GNUNET_free (ndup); 231 return total; 232 } 233 if (' ' == *hdr_val) 234 hdr_val++; 235 if (0 == strcasecmp (hdr_type, 236 ANASTASIS_HTTP_HEADER_TALER)) 237 { 238 size_t len; 239 240 /* found payment URI we care about! */ 241 tso->pay_uri = GNUNET_strdup (hdr_val); 242 len = strlen (tso->pay_uri); 243 while ( (len > 0) && 244 ( ('\n' == tso->pay_uri[len - 1]) || 245 ('\r' == tso->pay_uri[len - 1]) ) ) 246 { 247 len--; 248 tso->pay_uri[len] = '\0'; 249 } 250 } 251 GNUNET_free (ndup); 252 return total; 253 } 254 255 256 struct ANASTASIS_TruthStoreOperation * 257 ANASTASIS_truth_store ( 258 struct GNUNET_CURL_Context *ctx, 259 const char *backend_url, 260 const struct ANASTASIS_CRYPTO_TruthUUIDP *uuid, 261 const char *type, 262 const struct ANASTASIS_CRYPTO_EncryptedKeyShareP *encrypted_keyshare, 263 const char *truth_mime, 264 size_t encrypted_truth_size, 265 const void *encrypted_truth, 266 uint32_t payment_years_requested, 267 struct GNUNET_TIME_Relative payment_timeout, 268 ANASTASIS_TruthStoreCallback cb, 269 void *cb_cls) 270 { 271 struct ANASTASIS_TruthStoreOperation *tso; 272 CURL *eh; 273 char *json_str; 274 unsigned long long tms; 275 276 tms = (unsigned long long) (payment_timeout.rel_value_us 277 / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us); 278 tso = GNUNET_new (struct ANASTASIS_TruthStoreOperation); 279 { 280 char *uuid_str; 281 char *path; 282 char timeout_ms[32]; 283 284 GNUNET_snprintf (timeout_ms, 285 sizeof (timeout_ms), 286 "%llu", 287 tms); 288 uuid_str = GNUNET_STRINGS_data_to_string_alloc (uuid, 289 sizeof (*uuid)); 290 GNUNET_asprintf (&path, 291 "truth/%s", 292 uuid_str); 293 tso->url = TALER_url_join (backend_url, 294 path, 295 "timeout_ms", 296 (0 != payment_timeout.rel_value_us) 297 ? timeout_ms 298 : NULL, 299 NULL); 300 GNUNET_free (path); 301 GNUNET_free (uuid_str); 302 } 303 { 304 json_t *truth_data; 305 306 truth_data = GNUNET_JSON_PACK ( 307 GNUNET_JSON_pack_data_auto ("key_share_data", 308 encrypted_keyshare), 309 GNUNET_JSON_pack_string ("type", 310 type), 311 GNUNET_JSON_pack_data_varsize ("encrypted_truth", 312 encrypted_truth, 313 encrypted_truth_size), 314 GNUNET_JSON_pack_allow_null ( 315 GNUNET_JSON_pack_string ("truth_mime", 316 truth_mime)), 317 GNUNET_JSON_pack_uint64 ("storage_duration_years", 318 payment_years_requested)); 319 json_str = json_dumps (truth_data, 320 JSON_COMPACT); 321 GNUNET_assert (NULL != json_str); 322 json_decref (truth_data); 323 } 324 tso->ctx = ctx; 325 tso->data = json_str; 326 tso->cb = cb; 327 tso->cb_cls = cb_cls; 328 eh = ANASTASIS_curl_easy_get_ (tso->url); 329 if (0 != tms) 330 GNUNET_assert (CURLE_OK == 331 curl_easy_setopt (eh, 332 CURLOPT_TIMEOUT_MS, 333 (long) (tms + 5000))); 334 GNUNET_assert (CURLE_OK == 335 curl_easy_setopt (eh, 336 CURLOPT_POSTFIELDS, 337 json_str)); 338 GNUNET_assert (CURLE_OK == 339 curl_easy_setopt (eh, 340 CURLOPT_POSTFIELDSIZE, 341 strlen (json_str))); 342 GNUNET_assert (CURLE_OK == 343 curl_easy_setopt (eh, 344 CURLOPT_HEADERFUNCTION, 345 &handle_header)); 346 GNUNET_assert (CURLE_OK == 347 curl_easy_setopt (eh, 348 CURLOPT_HEADERDATA, 349 tso)); 350 tso->job = GNUNET_CURL_job_add_raw (ctx, 351 eh, 352 NULL, 353 &handle_truth_store_finished, 354 tso); 355 return tso; 356 }