Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2014-2021 Taler Systems SA
4 :
5 : TALER is free software; you can redistribute it and/or modify it under the
6 : terms of the GNU Affero General Public License as published by the Free Software
7 : Foundation; either version 3, or (at your option) any later version.
8 :
9 : TALER 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 Affero General Public License for more details.
12 :
13 : You should have received a copy of the GNU Affero General Public License along with
14 : TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
15 : */
16 : /**
17 : * @file mhd_responses.c
18 : * @brief API for generating HTTP replies
19 : * @author Florian Dold
20 : * @author Benedikt Mueller
21 : * @author Christian Grothoff
22 : */
23 : #include "taler/platform.h"
24 : #include <zlib.h>
25 : #include "taler/taler_util.h"
26 : #include "taler/taler_mhd_lib.h"
27 :
28 :
29 : /**
30 : * Global options for response generation.
31 : */
32 : static enum TALER_MHD_GlobalOptions TM_go;
33 :
34 :
35 : void
36 59 : TALER_MHD_setup (enum TALER_MHD_GlobalOptions go)
37 : {
38 59 : TM_go = go;
39 59 : }
40 :
41 :
42 : void
43 7092 : TALER_MHD_add_global_headers (struct MHD_Response *response,
44 : bool allow_store)
45 : {
46 7092 : if (0 != (TM_go & TALER_MHD_GO_FORCE_CONNECTION_CLOSE))
47 0 : GNUNET_break (MHD_YES ==
48 : MHD_add_response_header (response,
49 : MHD_HTTP_HEADER_CONNECTION,
50 : "close"));
51 : /* The wallet, operating from a background page, needs CORS to
52 : be disabled otherwise browsers block access. */
53 7092 : GNUNET_break (MHD_YES ==
54 : MHD_add_response_header (response,
55 : MHD_HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
56 : "*"));
57 7092 : GNUNET_break (MHD_YES ==
58 : MHD_add_response_header (response,
59 : /* Not available as MHD constant yet */
60 : "Access-Control-Expose-Headers",
61 : "*"));
62 7092 : if (! allow_store)
63 1065 : GNUNET_break (MHD_YES ==
64 : MHD_add_response_header (response,
65 : MHD_HTTP_HEADER_CACHE_CONTROL,
66 : "no-store"));
67 7092 : }
68 :
69 :
70 : MHD_RESULT
71 1089 : TALER_MHD_can_compress (struct MHD_Connection *connection)
72 : {
73 : const char *ae;
74 : const char *de;
75 :
76 1089 : if (0 != (TM_go & TALER_MHD_GO_DISABLE_COMPRESSION))
77 0 : return MHD_NO;
78 1089 : ae = MHD_lookup_connection_value (connection,
79 : MHD_HEADER_KIND,
80 : MHD_HTTP_HEADER_ACCEPT_ENCODING);
81 1089 : if (NULL == ae)
82 475 : return MHD_NO;
83 614 : if (0 == strcmp (ae,
84 : "*"))
85 0 : return MHD_YES;
86 614 : de = strstr (ae,
87 : "deflate");
88 614 : if (NULL == de)
89 80 : return MHD_NO;
90 534 : if ( ( (de == ae) ||
91 0 : (de[-1] == ',') ||
92 0 : (de[-1] == ' ') ) &&
93 534 : ( (de[strlen ("deflate")] == '\0') ||
94 534 : (de[strlen ("deflate")] == ',') ||
95 0 : (de[strlen ("deflate")] == ';') ) )
96 534 : return MHD_YES;
97 0 : return MHD_NO;
98 : }
99 :
100 :
101 : MHD_RESULT
102 2154 : TALER_MHD_body_compress (void **buf,
103 : size_t *buf_size)
104 : {
105 : Bytef *cbuf;
106 : uLongf cbuf_size;
107 : MHD_RESULT ret;
108 :
109 2154 : cbuf_size = compressBound (*buf_size);
110 2154 : cbuf = malloc (cbuf_size);
111 2154 : if (NULL == cbuf)
112 0 : return MHD_NO;
113 2154 : ret = compress (cbuf,
114 : &cbuf_size,
115 : (const Bytef *) *buf,
116 : *buf_size);
117 2154 : if ( (Z_OK != ret) ||
118 2154 : (cbuf_size >= *buf_size) )
119 : {
120 : /* compression failed */
121 153 : free (cbuf);
122 153 : return MHD_NO;
123 : }
124 2001 : free (*buf);
125 2001 : *buf = (void *) cbuf;
126 2001 : *buf_size = (size_t) cbuf_size;
127 2001 : return MHD_YES;
128 : }
129 :
130 :
131 : struct MHD_Response *
132 43 : TALER_MHD_make_json (const json_t *json)
133 : {
134 : struct MHD_Response *response;
135 : char *json_str;
136 :
137 43 : json_str = json_dumps (json,
138 : JSON_INDENT (2));
139 43 : if (NULL == json_str)
140 : {
141 0 : GNUNET_break (0);
142 0 : return NULL;
143 : }
144 43 : response = MHD_create_response_from_buffer (strlen (json_str),
145 : json_str,
146 : MHD_RESPMEM_MUST_FREE);
147 43 : if (NULL == response)
148 : {
149 0 : free (json_str);
150 0 : GNUNET_break (0);
151 0 : return NULL;
152 : }
153 43 : TALER_MHD_add_global_headers (response,
154 : false);
155 43 : GNUNET_break (MHD_YES ==
156 : MHD_add_response_header (response,
157 : MHD_HTTP_HEADER_CONTENT_TYPE,
158 : "application/json"));
159 43 : return response;
160 : }
161 :
162 :
163 : struct MHD_Response *
164 43 : TALER_MHD_make_json_steal (json_t *json)
165 : {
166 : struct MHD_Response *res;
167 :
168 43 : res = TALER_MHD_make_json (json);
169 43 : json_decref (json);
170 43 : return res;
171 : }
172 :
173 :
174 : MHD_RESULT
175 1017 : TALER_MHD_reply_json (struct MHD_Connection *connection,
176 : const json_t *json,
177 : unsigned int response_code)
178 : {
179 : struct MHD_Response *response;
180 : void *json_str;
181 : size_t json_len;
182 : MHD_RESULT is_compressed;
183 :
184 1017 : json_str = json_dumps (json,
185 : JSON_INDENT (2));
186 1017 : if (NULL == json_str)
187 : {
188 : /**
189 : * This log helps to figure out which
190 : * function called this one and assert-failed.
191 : */
192 0 : TALER_LOG_ERROR ("Aborting json-packing for HTTP code: %u\n",
193 : response_code);
194 :
195 0 : GNUNET_assert (0);
196 : return MHD_NO;
197 : }
198 1017 : json_len = strlen (json_str);
199 : /* try to compress the body */
200 1017 : is_compressed = MHD_NO;
201 1017 : if (MHD_YES ==
202 1017 : TALER_MHD_can_compress (connection))
203 493 : is_compressed = TALER_MHD_body_compress (&json_str,
204 : &json_len);
205 1017 : response = MHD_create_response_from_buffer (json_len,
206 : json_str,
207 : MHD_RESPMEM_MUST_FREE);
208 1017 : if (NULL == response)
209 : {
210 0 : free (json_str);
211 0 : GNUNET_break (0);
212 0 : return MHD_NO;
213 : }
214 1017 : TALER_MHD_add_global_headers (response,
215 : false);
216 1017 : GNUNET_break (MHD_YES ==
217 : MHD_add_response_header (response,
218 : MHD_HTTP_HEADER_CONTENT_TYPE,
219 : "application/json"));
220 1017 : if (MHD_YES == is_compressed)
221 : {
222 : /* Need to indicate to client that body is compressed */
223 439 : if (MHD_NO ==
224 439 : MHD_add_response_header (response,
225 : MHD_HTTP_HEADER_CONTENT_ENCODING,
226 : "deflate"))
227 : {
228 0 : GNUNET_break (0);
229 0 : MHD_destroy_response (response);
230 0 : return MHD_NO;
231 : }
232 : }
233 :
234 : {
235 : MHD_RESULT ret;
236 :
237 1017 : ret = MHD_queue_response (connection,
238 : response_code,
239 : response);
240 1017 : MHD_destroy_response (response);
241 1017 : return ret;
242 : }
243 : }
244 :
245 :
246 : MHD_RESULT
247 933 : TALER_MHD_reply_json_steal (struct MHD_Connection *connection,
248 : json_t *json,
249 : unsigned int response_code)
250 : {
251 : MHD_RESULT ret;
252 :
253 933 : ret = TALER_MHD_reply_json (connection,
254 : json,
255 : response_code);
256 933 : json_decref (json);
257 933 : return ret;
258 : }
259 :
260 :
261 : MHD_RESULT
262 0 : TALER_MHD_reply_cors_preflight (struct MHD_Connection *connection)
263 : {
264 : struct MHD_Response *response;
265 :
266 0 : response = MHD_create_response_from_buffer (0,
267 : NULL,
268 : MHD_RESPMEM_PERSISTENT);
269 0 : if (NULL == response)
270 0 : return MHD_NO;
271 : /* This adds the Access-Control-Allow-Origin header.
272 : * All endpoints of the exchange allow CORS. */
273 0 : TALER_MHD_add_global_headers (response,
274 : true);
275 0 : GNUNET_break (MHD_YES ==
276 : MHD_add_response_header (response,
277 : /* Not available as MHD constant yet */
278 : "Access-Control-Allow-Headers",
279 : "*"));
280 0 : GNUNET_break (MHD_YES ==
281 : MHD_add_response_header (response,
282 : /* Not available as MHD constant yet */
283 : "Access-Control-Allow-Methods",
284 : "*"));
285 : {
286 : MHD_RESULT ret;
287 :
288 0 : ret = MHD_queue_response (connection,
289 : MHD_HTTP_NO_CONTENT,
290 : response);
291 0 : MHD_destroy_response (response);
292 0 : return ret;
293 : }
294 : }
295 :
296 :
297 : struct MHD_Response *
298 0 : TALER_MHD_make_error (enum TALER_ErrorCode ec,
299 : const char *detail)
300 : {
301 0 : return TALER_MHD_MAKE_JSON_PACK (
302 : TALER_MHD_PACK_EC (ec),
303 : GNUNET_JSON_pack_allow_null (
304 : GNUNET_JSON_pack_string ("detail", detail)));
305 : }
306 :
307 :
308 : MHD_RESULT
309 41 : TALER_MHD_reply_with_error (struct MHD_Connection *connection,
310 : unsigned int http_status,
311 : enum TALER_ErrorCode ec,
312 : const char *detail)
313 : {
314 41 : return TALER_MHD_REPLY_JSON_PACK (
315 : connection,
316 : http_status,
317 : TALER_MHD_PACK_EC (ec),
318 : GNUNET_JSON_pack_allow_null (
319 : GNUNET_JSON_pack_string ("detail", detail)));
320 : }
321 :
322 :
323 : MHD_RESULT
324 0 : TALER_MHD_reply_with_ec (struct MHD_Connection *connection,
325 : enum TALER_ErrorCode ec,
326 : const char *detail)
327 : {
328 0 : unsigned int hc = TALER_ErrorCode_get_http_status (ec);
329 :
330 0 : if ( (0 == hc) ||
331 : (UINT_MAX == hc) )
332 : {
333 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
334 : "Invalid Taler error code %d provided for response!\n",
335 : (int) ec);
336 0 : hc = MHD_HTTP_INTERNAL_SERVER_ERROR;
337 : }
338 0 : return TALER_MHD_reply_with_error (connection,
339 : hc,
340 : ec,
341 : detail);
342 : }
343 :
344 :
345 : MHD_RESULT
346 0 : TALER_MHD_reply_request_too_large (struct MHD_Connection *connection)
347 : {
348 0 : return TALER_MHD_reply_with_error (connection,
349 : MHD_HTTP_REQUEST_ENTITY_TOO_LARGE,
350 : TALER_EC_GENERIC_UPLOAD_EXCEEDS_LIMIT,
351 : NULL);
352 : }
353 :
354 :
355 : MHD_RESULT
356 4 : TALER_MHD_reply_agpl (struct MHD_Connection *connection,
357 : const char *url)
358 : {
359 4 : const char *agpl =
360 : "This server is licensed under the Affero GPL. You will now be redirected to the source code.";
361 : struct MHD_Response *response;
362 :
363 4 : response = MHD_create_response_from_buffer (strlen (agpl),
364 : (void *) agpl,
365 : MHD_RESPMEM_PERSISTENT);
366 4 : if (NULL == response)
367 : {
368 0 : GNUNET_break (0);
369 0 : return MHD_NO;
370 : }
371 4 : TALER_MHD_add_global_headers (response,
372 : true);
373 4 : GNUNET_break (MHD_YES ==
374 : MHD_add_response_header (response,
375 : MHD_HTTP_HEADER_CONTENT_TYPE,
376 : "text/plain"));
377 4 : if (MHD_NO ==
378 4 : MHD_add_response_header (response,
379 : MHD_HTTP_HEADER_LOCATION,
380 : url))
381 : {
382 0 : GNUNET_break (0);
383 0 : MHD_destroy_response (response);
384 0 : return MHD_NO;
385 : }
386 :
387 : {
388 : MHD_RESULT ret;
389 :
390 4 : ret = MHD_queue_response (connection,
391 : MHD_HTTP_FOUND,
392 : response);
393 4 : MHD_destroy_response (response);
394 4 : return ret;
395 : }
396 : }
397 :
398 :
399 : MHD_RESULT
400 4216 : TALER_MHD_reply_static (struct MHD_Connection *connection,
401 : unsigned int http_status,
402 : const char *mime_type,
403 : const char *body,
404 : size_t body_size)
405 : {
406 : struct MHD_Response *response;
407 :
408 4216 : response = MHD_create_response_from_buffer (body_size,
409 : (void *) body,
410 : MHD_RESPMEM_PERSISTENT);
411 4216 : if (NULL == response)
412 : {
413 0 : GNUNET_break (0);
414 0 : return MHD_NO;
415 : }
416 4216 : TALER_MHD_add_global_headers (response,
417 : true);
418 4216 : if (NULL != mime_type)
419 6 : GNUNET_break (MHD_YES ==
420 : MHD_add_response_header (response,
421 : MHD_HTTP_HEADER_CONTENT_TYPE,
422 : mime_type));
423 : {
424 : MHD_RESULT ret;
425 :
426 4216 : ret = MHD_queue_response (connection,
427 : http_status,
428 : response);
429 4216 : MHD_destroy_response (response);
430 4216 : return ret;
431 : }
432 : }
433 :
434 :
435 : void
436 1802 : TALER_MHD_get_date_string (struct GNUNET_TIME_Absolute at,
437 : char date[128])
438 : {
439 : static const char *const days[] =
440 : { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
441 : static const char *const mons[] =
442 : { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct",
443 : "Nov", "Dec"};
444 : struct tm now;
445 : time_t t;
446 : #if ! defined(HAVE_C11_GMTIME_S) && ! defined(HAVE_W32_GMTIME_S) && \
447 : ! defined(HAVE_GMTIME_R)
448 : struct tm*pNow;
449 : #endif
450 :
451 1802 : date[0] = 0;
452 1802 : t = (time_t) (at.abs_value_us / 1000LL / 1000LL);
453 : #if defined(HAVE_C11_GMTIME_S)
454 : if (NULL == gmtime_s (&t, &now))
455 : return;
456 : #elif defined(HAVE_W32_GMTIME_S)
457 : if (0 != gmtime_s (&now, &t))
458 : return;
459 : #elif defined(HAVE_GMTIME_R)
460 : if (NULL == gmtime_r (&t, &now))
461 : return;
462 : #else
463 1802 : pNow = gmtime (&t);
464 1802 : if (NULL == pNow)
465 0 : return;
466 1802 : now = *pNow;
467 : #endif
468 1802 : sprintf (date,
469 : "%3s, %02u %3s %04u %02u:%02u:%02u GMT",
470 1802 : days[now.tm_wday % 7],
471 1802 : (unsigned int) now.tm_mday,
472 1802 : mons[now.tm_mon % 12],
473 1802 : (unsigned int) (1900 + now.tm_year),
474 1802 : (unsigned int) now.tm_hour,
475 1802 : (unsigned int) now.tm_min,
476 1802 : (unsigned int) now.tm_sec);
477 : }
478 :
479 :
480 : /* end of mhd_responses.c */
|