anastasis_api_truth_store.c (10270B)
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 /* free any value from a previous (duplicate) Taler header first */ 242 GNUNET_free (tso->pay_uri); 243 tso->pay_uri = GNUNET_strdup (hdr_val); 244 len = strlen (tso->pay_uri); 245 while ( (len > 0) && 246 ( ('\n' == tso->pay_uri[len - 1]) || 247 ('\r' == tso->pay_uri[len - 1]) ) ) 248 { 249 len--; 250 tso->pay_uri[len] = '\0'; 251 } 252 } 253 GNUNET_free (ndup); 254 return total; 255 } 256 257 258 struct ANASTASIS_TruthStoreOperation * 259 ANASTASIS_truth_store ( 260 struct GNUNET_CURL_Context *ctx, 261 const char *backend_url, 262 const struct ANASTASIS_CRYPTO_TruthUUIDP *uuid, 263 const char *type, 264 const struct ANASTASIS_CRYPTO_EncryptedKeyShareP *encrypted_keyshare, 265 const char *truth_mime, 266 size_t encrypted_truth_size, 267 const void *encrypted_truth, 268 uint32_t payment_years_requested, 269 struct GNUNET_TIME_Relative payment_timeout, 270 ANASTASIS_TruthStoreCallback cb, 271 void *cb_cls) 272 { 273 struct ANASTASIS_TruthStoreOperation *tso; 274 CURL *eh; 275 char *json_str; 276 unsigned long long tms; 277 278 tms = (unsigned long long) (payment_timeout.rel_value_us 279 / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us); 280 tso = GNUNET_new (struct ANASTASIS_TruthStoreOperation); 281 { 282 char *uuid_str; 283 char *path; 284 char timeout_ms[32]; 285 286 GNUNET_snprintf (timeout_ms, 287 sizeof (timeout_ms), 288 "%llu", 289 tms); 290 uuid_str = GNUNET_STRINGS_data_to_string_alloc (uuid, 291 sizeof (*uuid)); 292 GNUNET_asprintf (&path, 293 "truth/%s", 294 uuid_str); 295 tso->url = TALER_url_join (backend_url, 296 path, 297 "timeout_ms", 298 (0 != payment_timeout.rel_value_us) 299 ? timeout_ms 300 : NULL, 301 NULL); 302 GNUNET_free (path); 303 GNUNET_free (uuid_str); 304 } 305 { 306 json_t *truth_data; 307 308 truth_data = GNUNET_JSON_PACK ( 309 GNUNET_JSON_pack_data_auto ("key_share_data", 310 encrypted_keyshare), 311 GNUNET_JSON_pack_string ("type", 312 type), 313 GNUNET_JSON_pack_data_varsize ("encrypted_truth", 314 encrypted_truth, 315 encrypted_truth_size), 316 GNUNET_JSON_pack_allow_null ( 317 GNUNET_JSON_pack_string ("truth_mime", 318 truth_mime)), 319 GNUNET_JSON_pack_uint64 ("storage_duration_years", 320 payment_years_requested)); 321 json_str = json_dumps (truth_data, 322 JSON_COMPACT); 323 GNUNET_assert (NULL != json_str); 324 json_decref (truth_data); 325 } 326 tso->ctx = ctx; 327 tso->data = json_str; 328 tso->cb = cb; 329 tso->cb_cls = cb_cls; 330 eh = ANASTASIS_curl_easy_get_ (tso->url); 331 if (0 != tms) 332 GNUNET_assert (CURLE_OK == 333 curl_easy_setopt (eh, 334 CURLOPT_TIMEOUT_MS, 335 (long) (tms + 5000))); 336 GNUNET_assert (CURLE_OK == 337 curl_easy_setopt (eh, 338 CURLOPT_POSTFIELDS, 339 json_str)); 340 GNUNET_assert (CURLE_OK == 341 curl_easy_setopt (eh, 342 CURLOPT_POSTFIELDSIZE, 343 strlen (json_str))); 344 GNUNET_assert (CURLE_OK == 345 curl_easy_setopt (eh, 346 CURLOPT_HEADERFUNCTION, 347 &handle_header)); 348 GNUNET_assert (CURLE_OK == 349 curl_easy_setopt (eh, 350 CURLOPT_HEADERDATA, 351 tso)); 352 tso->job = GNUNET_CURL_job_add_raw (ctx, 353 eh, 354 NULL, 355 &handle_truth_store_finished, 356 tso); 357 return tso; 358 }