Line data Source code
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 response
95 : */
96 : static void
97 0 : handle_download_finished (void *cls,
98 : long response_code,
99 : const void *data,
100 : size_t data_size)
101 : {
102 0 : struct SYNC_DownloadOperation *download = cls;
103 :
104 0 : download->job = NULL;
105 0 : switch (response_code)
106 : {
107 0 : case 0:
108 0 : break;
109 0 : case MHD_HTTP_OK:
110 : {
111 : struct SYNC_DownloadDetails dd;
112 0 : struct SYNC_UploadSignaturePS usp = {
113 0 : .purpose.purpose = htonl (TALER_SIGNATURE_SYNC_BACKUP_UPLOAD),
114 0 : .purpose.size = htonl (sizeof (usp)),
115 : .old_backup_hash = download->sync_previous
116 : };
117 :
118 0 : GNUNET_CRYPTO_hash (data,
119 : data_size,
120 : &usp.new_backup_hash);
121 0 : if (GNUNET_OK !=
122 0 : GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_SYNC_BACKUP_UPLOAD,
123 : &usp,
124 : &download->account_sig.eddsa_sig,
125 : &download->account_pub.eddsa_pub))
126 : {
127 0 : GNUNET_break_op (0);
128 0 : response_code = 0;
129 0 : break;
130 : }
131 : /* Success, call callback with all details! */
132 0 : memset (&dd, 0, sizeof (dd));
133 0 : dd.sig = download->account_sig;
134 0 : dd.prev_backup_hash = download->sync_previous;
135 0 : dd.curr_backup_hash = usp.new_backup_hash;
136 0 : dd.backup = data;
137 0 : dd.backup_size = data_size;
138 0 : download->cb (download->cb_cls,
139 : response_code,
140 : &dd);
141 0 : download->cb = NULL;
142 0 : SYNC_download_cancel (download);
143 0 : return;
144 : }
145 0 : case MHD_HTTP_BAD_REQUEST:
146 : /* This should never happen, either us or the sync server is buggy
147 : (or API version conflict); just pass JSON reply to the application */
148 0 : break;
149 0 : case MHD_HTTP_NOT_FOUND:
150 : /* Nothing really to verify */
151 0 : break;
152 0 : case MHD_HTTP_INTERNAL_SERVER_ERROR:
153 : /* Server had an internal issue; we should retry, but this API
154 : leaves this to the application */
155 0 : break;
156 0 : default:
157 : /* unexpected response code */
158 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
159 : "Unexpected response code %u\n",
160 : (unsigned int) response_code);
161 0 : GNUNET_break (0);
162 0 : response_code = 0;
163 0 : break;
164 : }
165 0 : if (NULL != download->cb)
166 : {
167 0 : download->cb (download->cb_cls,
168 : response_code,
169 : NULL);
170 0 : download->cb = NULL;
171 : }
172 0 : 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 0 : handle_header (char *buffer,
187 : size_t size,
188 : size_t nitems,
189 : void *userdata)
190 : {
191 0 : struct SYNC_DownloadOperation *download = userdata;
192 0 : size_t total = size * nitems;
193 : char *ndup;
194 : const char *hdr_type;
195 : char *hdr_val;
196 : char *sp;
197 :
198 0 : ndup = GNUNET_strndup (buffer,
199 : total);
200 0 : hdr_type = strtok_r (ndup,
201 : ":",
202 : &sp);
203 0 : if (NULL == hdr_type)
204 : {
205 0 : GNUNET_free (ndup);
206 0 : return total;
207 : }
208 0 : hdr_val = strtok_r (NULL,
209 : "\n\r",
210 : &sp);
211 0 : if (NULL == hdr_val)
212 : {
213 0 : GNUNET_free (ndup);
214 0 : return total;
215 : }
216 0 : if (' ' == *hdr_val)
217 0 : hdr_val++;
218 0 : if (0 == strcasecmp (hdr_type,
219 : "Sync-Signature"))
220 : {
221 0 : if (GNUNET_OK !=
222 0 : GNUNET_STRINGS_string_to_data (hdr_val,
223 : strlen (hdr_val),
224 0 : &download->account_sig,
225 : sizeof (struct SYNC_AccountSignatureP)))
226 : {
227 0 : GNUNET_break_op (0);
228 0 : GNUNET_free (ndup);
229 0 : return 0;
230 : }
231 : }
232 0 : if (0 == strcasecmp (hdr_type,
233 : "Sync-Previous"))
234 : {
235 0 : if (GNUNET_OK !=
236 0 : GNUNET_STRINGS_string_to_data (hdr_val,
237 : strlen (hdr_val),
238 0 : &download->sync_previous,
239 : sizeof (struct GNUNET_HashCode)))
240 : {
241 0 : GNUNET_break_op (0);
242 0 : GNUNET_free (ndup);
243 0 : return 0;
244 : }
245 : }
246 0 : GNUNET_free (ndup);
247 0 : return total;
248 : }
249 :
250 :
251 : /**
252 : * Download the latest version of a backup for account @a pub.
253 : *
254 : * @param ctx for HTTP client request processing
255 : * @param base_url base URL of the Sync server
256 : * @param pub account public key
257 : * @param cb function to call with the backup
258 : * @param cb_cls closure for @a cb
259 : * @return handle for the operation
260 : */
261 : struct SYNC_DownloadOperation *
262 0 : SYNC_download (struct GNUNET_CURL_Context *ctx,
263 : const char *base_url,
264 : const struct SYNC_AccountPublicKeyP *pub,
265 : SYNC_DownloadCallback cb,
266 : void *cb_cls)
267 : {
268 : struct SYNC_DownloadOperation *download;
269 : char *pub_str;
270 : CURL *eh;
271 :
272 0 : download = GNUNET_new (struct SYNC_DownloadOperation);
273 0 : download->account_pub = *pub;
274 0 : pub_str = GNUNET_STRINGS_data_to_string_alloc (pub,
275 : sizeof (*pub));
276 0 : GNUNET_asprintf (&download->url,
277 : "%s%sbackups/%s",
278 : base_url,
279 0 : '/' == base_url[strlen (base_url) - 1]
280 : ? ""
281 : : "/",
282 : pub_str);
283 0 : GNUNET_free (pub_str);
284 0 : eh = SYNC_curl_easy_get_ (download->url);
285 0 : GNUNET_assert (CURLE_OK ==
286 : curl_easy_setopt (eh,
287 : CURLOPT_HEADERFUNCTION,
288 : &handle_header));
289 0 : GNUNET_assert (CURLE_OK ==
290 : curl_easy_setopt (eh,
291 : CURLOPT_HEADERDATA,
292 : download));
293 0 : download->cb = cb;
294 0 : download->cb_cls = cb_cls;
295 0 : download->job = GNUNET_CURL_job_add_raw (ctx,
296 : eh,
297 : NULL,
298 : &handle_download_finished,
299 : download);
300 0 : return download;
301 : }
302 :
303 :
304 : /**
305 : * Cancel the download.
306 : *
307 : * @param do operation to cancel.
308 : */
309 : void
310 0 : SYNC_download_cancel (struct SYNC_DownloadOperation *download)
311 : {
312 0 : if (NULL != download->job)
313 : {
314 0 : GNUNET_CURL_job_cancel (download->job);
315 0 : download->job = NULL;
316 : }
317 0 : GNUNET_free (download->url);
318 0 : GNUNET_free (download);
319 0 : }
320 :
321 :
322 : /* end of sync_api_download.c */
|