sync_api_upload.c (12535B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2014-2019 Taler Systems SA 4 5 TALER is free software; you can redistribute it and/or modify 6 it under the terms of the GNU Lesser General Public License as 7 published by the Free Software Foundation; either version 2.1, 8 or (at your option) any later version. 9 10 TALER is distributed in the hope that it will be useful, but 11 WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU Lesser General Public License for more details. 14 15 You should have received a copy of the GNU Lesser General Public 16 License along with TALER; see the file COPYING.LGPL. If not, 17 see <http://www.gnu.org/licenses/> 18 */ 19 20 /** 21 * @file lib/sync_api_upload.c 22 * @brief Implementation of the upload POST 23 * @author Christian Grothoff 24 */ 25 #include "platform.h" 26 #include <curl/curl.h> 27 #include <jansson.h> 28 #include <microhttpd.h> /* just for HTTP status codes */ 29 #include <gnunet/gnunet_util_lib.h> 30 #include <gnunet/gnunet_curl_lib.h> 31 #include <taler/taler_signatures.h> 32 #include <taler/taler_json_lib.h> 33 #include "sync_service.h" 34 #include "sync_api_curl_defaults.h" 35 36 37 /** 38 * @brief Handle for an upload operation. 39 */ 40 struct SYNC_UploadOperation 41 { 42 43 /** 44 * The url for this request. 45 */ 46 char *url; 47 48 /** 49 * Handle for the request. 50 */ 51 struct GNUNET_CURL_Job *job; 52 53 /** 54 * Reference to the execution context. 55 */ 56 struct GNUNET_CURL_Context *ctx; 57 58 /** 59 * Function to call with the result. 60 */ 61 SYNC_UploadCallback cb; 62 63 /** 64 * Closure for @e cb. 65 */ 66 void *cb_cls; 67 68 /** 69 * Payment URI we received from the service, or NULL. 70 */ 71 char *pay_uri; 72 73 /** 74 * Hash of the data we are uploading. 75 */ 76 struct GNUNET_HashCode new_upload_hash; 77 }; 78 79 80 /** 81 * Function called when we're done processing the 82 * HTTP /backup request. 83 * 84 * @param cls the `struct SYNC_UploadOperation` 85 * @param response_code HTTP response code, 0 on error 86 * @param data data we downloaded 87 * @param data_size number of bytes in @a data 88 */ 89 static void 90 handle_upload_finished (void *cls, 91 long response_code, 92 const void *data, 93 size_t data_size) 94 { 95 struct SYNC_UploadOperation *uo = cls; 96 struct SYNC_UploadDetails ud = { 97 .http_status = (unsigned int) response_code, 98 .ec = TALER_EC_INVALID 99 }; 100 101 uo->job = NULL; 102 switch (response_code) 103 { 104 case 0: 105 break; 106 case MHD_HTTP_NO_CONTENT: 107 ud.us = SYNC_US_SUCCESS; 108 ud.details.success.curr_backup_hash = &uo->new_upload_hash; 109 ud.ec = TALER_EC_NONE; 110 break; 111 case MHD_HTTP_NOT_MODIFIED: 112 ud.us = SYNC_US_SUCCESS; 113 ud.details.success.curr_backup_hash = &uo->new_upload_hash; 114 ud.ec = TALER_EC_NONE; 115 break; 116 case MHD_HTTP_BAD_REQUEST: 117 GNUNET_break (0); 118 ud.ec = TALER_JSON_get_error_code2 (data, 119 data_size); 120 break; 121 case MHD_HTTP_PAYMENT_REQUIRED: 122 ud.us = SYNC_US_PAYMENT_REQUIRED; 123 ud.details.payment_required.payment_request = uo->pay_uri; 124 ud.ec = TALER_EC_NONE; 125 break; 126 case MHD_HTTP_FORBIDDEN: 127 GNUNET_break (0); 128 ud.ec = TALER_JSON_get_error_code2 (data, 129 data_size); 130 break; 131 case MHD_HTTP_CONFLICT: 132 ud.us = SYNC_US_CONFLICTING_BACKUP; 133 GNUNET_CRYPTO_hash (data, 134 data_size, 135 &ud.details.recovered_backup.existing_backup_hash); 136 ud.details.recovered_backup.existing_backup_size 137 = data_size; 138 ud.details.recovered_backup.existing_backup 139 = data; 140 ud.ec = TALER_EC_NONE; 141 break; 142 case MHD_HTTP_GONE: 143 ud.ec = TALER_JSON_get_error_code2 (data, 144 data_size); 145 break; 146 case MHD_HTTP_LENGTH_REQUIRED: 147 GNUNET_break (0); 148 break; 149 case MHD_HTTP_REQUEST_ENTITY_TOO_LARGE: 150 ud.ec = TALER_JSON_get_error_code2 (data, 151 data_size); 152 break; 153 case MHD_HTTP_TOO_MANY_REQUESTS: 154 ud.ec = TALER_JSON_get_error_code2 (data, 155 data_size); 156 break; 157 case MHD_HTTP_INTERNAL_SERVER_ERROR: 158 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 159 "Internal server error: `%.*s\n", 160 (int) data_size, 161 (const char *) data); 162 break; 163 } 164 if (NULL != uo->cb) 165 { 166 uo->cb (uo->cb_cls, 167 &ud); 168 uo->cb = NULL; 169 } 170 SYNC_upload_cancel (uo); 171 } 172 173 174 /** 175 * Handle HTTP header received by curl. 176 * 177 * @param buffer one line of HTTP header data 178 * @param size size of an item 179 * @param nitems number of items passed 180 * @param userdata our `struct SYNC_DownloadOperation *` 181 * @return `size * nitems` 182 */ 183 static size_t 184 handle_header (char *buffer, 185 size_t size, 186 size_t nitems, 187 void *userdata) 188 { 189 struct SYNC_UploadOperation *uo = userdata; 190 size_t total = size * nitems; 191 char *ndup; 192 const char *hdr_type; 193 char *hdr_val; 194 char *sp; 195 196 ndup = GNUNET_strndup (buffer, 197 total); 198 hdr_type = strtok_r (ndup, 199 ":", 200 &sp); 201 if (NULL == hdr_type) 202 { 203 GNUNET_free (ndup); 204 return total; 205 } 206 hdr_val = strtok_r (NULL, 207 "", 208 &sp); 209 if (NULL == hdr_val) 210 { 211 GNUNET_free (ndup); 212 return total; 213 } 214 if (' ' == *hdr_val) 215 hdr_val++; 216 if (0 == strcasecmp (hdr_type, 217 "Taler")) 218 { 219 size_t len; 220 221 /* found payment URI we care about! */ 222 uo->pay_uri = GNUNET_strdup (hdr_val); 223 len = strlen (uo->pay_uri); 224 while ( (len > 0) && 225 ( ('\n' == uo->pay_uri[len - 1]) || 226 ('\r' == uo->pay_uri[len - 1]) ) ) 227 { 228 len--; 229 uo->pay_uri[len] = '\0'; 230 } 231 } 232 GNUNET_free (ndup); 233 return total; 234 } 235 236 237 struct SYNC_UploadOperation * 238 SYNC_upload (struct GNUNET_CURL_Context *ctx, 239 const char *base_url, 240 struct SYNC_AccountPrivateKeyP *priv, 241 const struct GNUNET_HashCode *prev_backup_hash, 242 size_t backup_size, 243 const void *backup, 244 enum SYNC_PaymentOptions po, 245 const char *paid_order_id, 246 SYNC_UploadCallback cb, 247 void *cb_cls) 248 { 249 struct SYNC_AccountSignatureP account_sig; 250 struct SYNC_UploadOperation *uo; 251 CURL *eh; 252 struct curl_slist *job_headers; 253 struct SYNC_UploadSignaturePS usp = { 254 .purpose.purpose = htonl (TALER_SIGNATURE_SYNC_BACKUP_UPLOAD), 255 .purpose.size = htonl (sizeof (usp)) 256 }; 257 258 if (NULL != prev_backup_hash) 259 usp.old_backup_hash = *prev_backup_hash; 260 GNUNET_CRYPTO_hash (backup, 261 backup_size, 262 &usp.new_backup_hash); 263 GNUNET_CRYPTO_eddsa_sign (&priv->eddsa_priv, 264 &usp, 265 &account_sig.eddsa_sig); 266 267 /* setup our HTTP headers */ 268 job_headers = NULL; 269 { 270 struct curl_slist *ext; 271 char *val; 272 char *hdr; 273 274 /* Set Sync-Signature header */ 275 val = GNUNET_STRINGS_data_to_string_alloc (&account_sig, 276 sizeof (account_sig)); 277 GNUNET_asprintf (&hdr, 278 "Sync-Signature: %s", 279 val); 280 GNUNET_free (val); 281 ext = curl_slist_append (job_headers, 282 hdr); 283 GNUNET_free (hdr); 284 if (NULL == ext) 285 { 286 GNUNET_break (0); 287 curl_slist_free_all (job_headers); 288 return NULL; 289 } 290 job_headers = ext; 291 292 /* set If-None-Match header */ 293 val = GNUNET_STRINGS_data_to_string_alloc (&usp.new_backup_hash, 294 sizeof (struct GNUNET_HashCode)); 295 GNUNET_asprintf (&hdr, 296 "%s: \"%s\"", 297 MHD_HTTP_HEADER_IF_NONE_MATCH, 298 val); 299 GNUNET_free (val); 300 ext = curl_slist_append (job_headers, 301 hdr); 302 GNUNET_free (hdr); 303 if (NULL == ext) 304 { 305 GNUNET_break (0); 306 curl_slist_free_all (job_headers); 307 return NULL; 308 } 309 job_headers = ext; 310 311 /* Setup If-Match header */ 312 if (NULL != prev_backup_hash) 313 { 314 val = GNUNET_STRINGS_data_to_string_alloc (&usp.old_backup_hash, 315 sizeof (struct 316 GNUNET_HashCode)); 317 GNUNET_asprintf (&hdr, 318 "If-Match: \"%s\"", 319 val); 320 GNUNET_free (val); 321 ext = curl_slist_append (job_headers, 322 hdr); 323 GNUNET_free (hdr); 324 if (NULL == ext) 325 { 326 GNUNET_break (0); 327 curl_slist_free_all (job_headers); 328 return NULL; 329 } 330 job_headers = ext; 331 } 332 } 333 /* Finished setting up headers */ 334 335 uo = GNUNET_new (struct SYNC_UploadOperation); 336 uo->new_upload_hash = usp.new_backup_hash; 337 { 338 char *path; 339 char *account_s; 340 struct SYNC_AccountPublicKeyP pub; 341 342 GNUNET_CRYPTO_eddsa_key_get_public (&priv->eddsa_priv, 343 &pub.eddsa_pub); 344 account_s = GNUNET_STRINGS_data_to_string_alloc (&pub, 345 sizeof (pub)); 346 GNUNET_asprintf (&path, 347 "backups/%s", 348 account_s); 349 GNUNET_free (account_s); 350 if (0 != (po & SYNC_PO_FRESH_ORDER)) 351 { 352 uo->url = (0 != (po & SYNC_PO_FORCE_PAYMENT)) 353 ? TALER_url_join (base_url, 354 path, 355 "fresh", 356 "y", 357 "pay", 358 "y", 359 (NULL != paid_order_id) 360 ? "paying" 361 : NULL, 362 paid_order_id, 363 NULL) 364 : TALER_url_join (base_url, 365 path, 366 "fresh", 367 "y", 368 (NULL != paid_order_id) 369 ? "paying" 370 : NULL, 371 paid_order_id, 372 NULL); 373 } 374 else 375 { 376 uo->url = (0 != (po & SYNC_PO_FORCE_PAYMENT)) 377 ? TALER_url_join (base_url, 378 path, 379 "pay", 380 "y", 381 (NULL != paid_order_id) 382 ? "paying" 383 : NULL, 384 paid_order_id, 385 NULL) 386 : TALER_url_join (base_url, 387 path, 388 (NULL != paid_order_id) 389 ? "paying" 390 : NULL, 391 paid_order_id, 392 NULL); 393 } 394 395 GNUNET_free (path); 396 } 397 uo->ctx = ctx; 398 uo->cb = cb; 399 uo->cb_cls = cb_cls; 400 eh = SYNC_curl_easy_get_ (uo->url); 401 GNUNET_assert (CURLE_OK == 402 curl_easy_setopt (eh, 403 CURLOPT_POSTFIELDS, 404 backup)); 405 GNUNET_assert (CURLE_OK == 406 curl_easy_setopt (eh, 407 CURLOPT_POSTFIELDSIZE, 408 (long) backup_size)); 409 GNUNET_assert (CURLE_OK == 410 curl_easy_setopt (eh, 411 CURLOPT_HEADERFUNCTION, 412 &handle_header)); 413 GNUNET_assert (CURLE_OK == 414 curl_easy_setopt (eh, 415 CURLOPT_HEADERDATA, 416 uo)); 417 uo->job = GNUNET_CURL_job_add_raw (ctx, 418 eh, 419 job_headers, 420 &handle_upload_finished, 421 uo); 422 curl_slist_free_all (job_headers); 423 return uo; 424 } 425 426 427 void 428 SYNC_upload_cancel (struct SYNC_UploadOperation *uo) 429 { 430 if (NULL != uo->job) 431 { 432 GNUNET_CURL_job_cancel (uo->job); 433 uo->job = NULL; 434 } 435 GNUNET_free (uo->pay_uri); 436 GNUNET_free (uo->url); 437 GNUNET_free (uo); 438 } 439 440 441 /* end of sync_api_upload.c */