sync_api_download.c (8337B)
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_download.c 22 * @brief Implementation of the download 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 "sync_service.h" 33 #include "sync_api_curl_defaults.h" 34 35 36 /** 37 * @brief Handle for a download operation. 38 */ 39 struct SYNC_DownloadOperation 40 { 41 42 /** 43 * The url for this request. 44 */ 45 char *url; 46 47 /** 48 * Handle for the request. 49 */ 50 struct GNUNET_CURL_Job *job; 51 52 /** 53 * Reference to the execution context. 54 */ 55 struct GNUNET_CURL_Context *ctx; 56 57 /** 58 * Function to call with the result. 59 */ 60 SYNC_DownloadCallback cb; 61 62 /** 63 * Closure for @e cb. 64 */ 65 void *cb_cls; 66 67 /** 68 * Public key of the account we are downloading from. 69 */ 70 struct SYNC_AccountPublicKeyP account_pub; 71 72 /** 73 * Signature returned in the "Sync-Signature" 74 * header, or all zeros for none. 75 */ 76 struct SYNC_AccountSignatureP account_sig; 77 78 /** 79 * Hash code returned by the server in the 80 * "Sync-Previous" header, or all zeros for 81 * none. 82 */ 83 struct GNUNET_HashCode sync_previous; 84 85 }; 86 87 88 /** 89 * Function called when we're done processing the 90 * HTTP /backup request. 91 * 92 * @param cls the `struct SYNC_DownloadOperation` 93 * @param response_code HTTP response code, 0 on error 94 * @param data data we downloaded 95 * @param data_size number of bytes in @a data 96 */ 97 static void 98 handle_download_finished (void *cls, 99 long response_code, 100 const void *data, 101 size_t data_size) 102 { 103 struct SYNC_DownloadOperation *download = cls; 104 struct SYNC_DownloadDetails dd = { 105 .http_status = (unsigned int) response_code 106 }; 107 108 download->job = NULL; 109 switch (response_code) 110 { 111 case 0: 112 break; 113 case MHD_HTTP_OK: 114 { 115 struct SYNC_UploadSignaturePS usp = { 116 .purpose.purpose = htonl (TALER_SIGNATURE_SYNC_BACKUP_UPLOAD), 117 .purpose.size = htonl (sizeof (usp)), 118 .old_backup_hash = download->sync_previous 119 }; 120 121 GNUNET_CRYPTO_hash (data, 122 data_size, 123 &usp.new_backup_hash); 124 if (GNUNET_OK != 125 GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_SYNC_BACKUP_UPLOAD, 126 &usp, 127 &download->account_sig.eddsa_sig, 128 &download->account_pub.eddsa_pub)) 129 { 130 GNUNET_break_op (0); 131 dd.http_status = 0; 132 break; 133 } 134 /* Success, call callback with all details! */ 135 dd.details.ok.sig = download->account_sig; 136 dd.details.ok.prev_backup_hash = download->sync_previous; 137 dd.details.ok.curr_backup_hash = usp.new_backup_hash; 138 dd.details.ok.backup = data; 139 dd.details.ok.backup_size = data_size; 140 download->cb (download->cb_cls, 141 &dd); 142 download->cb = NULL; 143 SYNC_download_cancel (download); 144 return; 145 } 146 case MHD_HTTP_BAD_REQUEST: 147 /* This should never happen, either us or the sync server is buggy 148 (or API version conflict); just pass JSON reply to the application */ 149 break; 150 case MHD_HTTP_NOT_FOUND: 151 /* Nothing really to verify */ 152 break; 153 case MHD_HTTP_INTERNAL_SERVER_ERROR: 154 /* Server had an internal issue; we should retry, but this API 155 leaves this to the application */ 156 break; 157 default: 158 /* unexpected response code */ 159 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 160 "Unexpected response code %u\n", 161 (unsigned int) response_code); 162 GNUNET_break (0); 163 dd.http_status = 0; 164 break; 165 } 166 if (NULL != download->cb) 167 { 168 download->cb (download->cb_cls, 169 &dd); 170 download->cb = NULL; 171 } 172 SYNC_download_cancel (download); 173 } 174 175 176 /** 177 * Handle HTTP header received by curl. 178 * 179 * @param buffer one line of HTTP header data 180 * @param size size of an item 181 * @param nitems number of items passed 182 * @param userdata our `struct SYNC_DownloadOperation *` 183 * @return `size * nitems` 184 */ 185 static size_t 186 handle_header (char *buffer, 187 size_t size, 188 size_t nitems, 189 void *userdata) 190 { 191 struct SYNC_DownloadOperation *download = userdata; 192 size_t total = size * nitems; 193 char *ndup; 194 const char *hdr_type; 195 char *hdr_val; 196 char *sp; 197 198 ndup = GNUNET_strndup (buffer, 199 total); 200 hdr_type = strtok_r (ndup, 201 ":", 202 &sp); 203 if (NULL == hdr_type) 204 { 205 GNUNET_free (ndup); 206 return total; 207 } 208 hdr_val = strtok_r (NULL, 209 "\n\r", 210 &sp); 211 if (NULL == hdr_val) 212 { 213 GNUNET_free (ndup); 214 return total; 215 } 216 if (' ' == *hdr_val) 217 hdr_val++; 218 if (0 == strcasecmp (hdr_type, 219 "Sync-Signature")) 220 { 221 if (GNUNET_OK != 222 GNUNET_STRINGS_string_to_data (hdr_val, 223 strlen (hdr_val), 224 &download->account_sig, 225 sizeof (struct SYNC_AccountSignatureP))) 226 { 227 GNUNET_break_op (0); 228 GNUNET_free (ndup); 229 return 0; 230 } 231 } 232 if (0 == strcasecmp (hdr_type, 233 "Sync-Previous")) 234 { 235 if (GNUNET_OK != 236 GNUNET_STRINGS_string_to_data (hdr_val, 237 strlen (hdr_val), 238 &download->sync_previous, 239 sizeof (struct GNUNET_HashCode))) 240 { 241 GNUNET_break_op (0); 242 GNUNET_free (ndup); 243 return 0; 244 } 245 } 246 GNUNET_free (ndup); 247 return total; 248 } 249 250 251 struct SYNC_DownloadOperation * 252 SYNC_download (struct GNUNET_CURL_Context *ctx, 253 const char *base_url, 254 const struct SYNC_AccountPublicKeyP *pub, 255 SYNC_DownloadCallback cb, 256 void *cb_cls) 257 { 258 struct SYNC_DownloadOperation *download; 259 char *pub_str; 260 CURL *eh; 261 262 download = GNUNET_new (struct SYNC_DownloadOperation); 263 download->account_pub = *pub; 264 pub_str = GNUNET_STRINGS_data_to_string_alloc (pub, 265 sizeof (*pub)); 266 GNUNET_asprintf (&download->url, 267 "%s%sbackups/%s", 268 base_url, 269 '/' == base_url[strlen (base_url) - 1] 270 ? "" 271 : "/", 272 pub_str); 273 GNUNET_free (pub_str); 274 eh = SYNC_curl_easy_get_ (download->url); 275 GNUNET_assert (CURLE_OK == 276 curl_easy_setopt (eh, 277 CURLOPT_HEADERFUNCTION, 278 &handle_header)); 279 GNUNET_assert (CURLE_OK == 280 curl_easy_setopt (eh, 281 CURLOPT_HEADERDATA, 282 download)); 283 download->cb = cb; 284 download->cb_cls = cb_cls; 285 download->job = GNUNET_CURL_job_add_raw (ctx, 286 eh, 287 NULL, 288 &handle_download_finished, 289 download); 290 return download; 291 } 292 293 294 void 295 SYNC_download_cancel (struct SYNC_DownloadOperation *download) 296 { 297 if (NULL != download->job) 298 { 299 GNUNET_CURL_job_cancel (download->job); 300 download->job = NULL; 301 } 302 GNUNET_free (download->url); 303 GNUNET_free (download); 304 } 305 306 307 /* end of sync_api_download.c */