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 61 : TALER_MHD_setup (enum TALER_MHD_GlobalOptions go)
37 : {
38 61 : TM_go = go;
39 61 : }
40 :
41 :
42 : void
43 7133 : TALER_MHD_add_global_headers (struct MHD_Response *response,
44 : bool allow_store)
45 : {
46 7133 : 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 7133 : GNUNET_break (MHD_YES ==
54 : MHD_add_response_header (response,
55 : MHD_HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
56 : "*"));
57 7133 : GNUNET_break (MHD_YES ==
58 : MHD_add_response_header (response,
59 : /* Not available as MHD constant yet */
60 : "Access-Control-Expose-Headers",
61 : "*"));
62 7133 : if (! allow_store)
63 1104 : GNUNET_break (MHD_YES ==
64 : MHD_add_response_header (response,
65 : MHD_HTTP_HEADER_CACHE_CONTROL,
66 : "no-store"));
67 7133 : }
68 :
69 :
70 : enum TALER_MHD_CompressionType
71 1126 : TALER_MHD_can_compress (struct MHD_Connection *connection,
72 : enum TALER_MHD_CompressionType max)
73 : {
74 : struct NameMap
75 : {
76 : const char *name;
77 : enum TALER_MHD_CompressionType ct;
78 1126 : } map[] = {
79 : /* sorted largest to smallest on purpose! */
80 : { "zstd", TALER_MHD_CT_ZSTD },
81 : { "gzip", TALER_MHD_CT_GZIP },
82 : { "deflate", TALER_MHD_CT_DEFLATE },
83 : { NULL, TALER_MHD_CT_NONE }
84 : };
85 : const char *ae;
86 : const char *de;
87 :
88 1126 : if (0 != (TM_go & TALER_MHD_GO_DISABLE_COMPRESSION))
89 0 : return TALER_MHD_CT_NONE;
90 1126 : ae = MHD_lookup_connection_value (connection,
91 : MHD_HEADER_KIND,
92 : MHD_HTTP_HEADER_ACCEPT_ENCODING);
93 1126 : if (NULL == ae)
94 513 : return TALER_MHD_CT_NONE;
95 613 : if (0 == strcmp (ae,
96 : "*"))
97 0 : return MHD_YES;
98 1919 : for (unsigned int i=0; NULL != map[i].name; i++)
99 : {
100 1839 : const char *name = map[i].name;
101 :
102 1839 : if (map[i].ct > max)
103 1226 : continue; /* not allowed by client */
104 613 : de = strstr (ae,
105 : name);
106 613 : if (NULL == de)
107 80 : continue;
108 533 : if ( ( (de == ae) ||
109 0 : (de[-1] == ',') ||
110 0 : (de[-1] == ' ') ) &&
111 533 : ( (de[strlen (name)] == '\0') ||
112 533 : (de[strlen (name)] == ',') ||
113 0 : (de[strlen (name)] == ';') ) )
114 533 : return map[i].ct;
115 : }
116 80 : return TALER_MHD_CT_NONE;
117 : }
118 :
119 :
120 : MHD_RESULT
121 1391 : TALER_MHD_body_compress (void **buf,
122 : size_t *buf_size)
123 : {
124 : Bytef *cbuf;
125 : uLongf cbuf_size;
126 : MHD_RESULT ret;
127 :
128 1391 : cbuf_size = compressBound (*buf_size);
129 1391 : cbuf = malloc (cbuf_size);
130 1391 : if (NULL == cbuf)
131 0 : return MHD_NO;
132 1391 : ret = compress (cbuf,
133 : &cbuf_size,
134 : (const Bytef *) *buf,
135 : *buf_size);
136 1391 : if ( (Z_OK != ret) ||
137 1391 : (cbuf_size >= *buf_size) )
138 : {
139 : /* compression failed */
140 53 : free (cbuf);
141 53 : return MHD_NO;
142 : }
143 1338 : free (*buf);
144 1338 : *buf = (void *) cbuf;
145 1338 : *buf_size = (size_t) cbuf_size;
146 1338 : return MHD_YES;
147 : }
148 :
149 :
150 : struct MHD_Response *
151 45 : TALER_MHD_make_json (const json_t *json)
152 : {
153 : struct MHD_Response *response;
154 : char *json_str;
155 :
156 45 : json_str = json_dumps (json,
157 : JSON_INDENT (2));
158 45 : if (NULL == json_str)
159 : {
160 0 : GNUNET_break (0);
161 0 : return NULL;
162 : }
163 45 : response = MHD_create_response_from_buffer (strlen (json_str),
164 : json_str,
165 : MHD_RESPMEM_MUST_FREE);
166 45 : if (NULL == response)
167 : {
168 0 : free (json_str);
169 0 : GNUNET_break (0);
170 0 : return NULL;
171 : }
172 45 : TALER_MHD_add_global_headers (response,
173 : false);
174 45 : GNUNET_break (MHD_YES ==
175 : MHD_add_response_header (response,
176 : MHD_HTTP_HEADER_CONTENT_TYPE,
177 : "application/json"));
178 45 : return response;
179 : }
180 :
181 :
182 : struct MHD_Response *
183 45 : TALER_MHD_make_json_steal (json_t *json)
184 : {
185 : struct MHD_Response *res;
186 :
187 45 : res = TALER_MHD_make_json (json);
188 45 : json_decref (json);
189 45 : return res;
190 : }
191 :
192 :
193 : MHD_RESULT
194 1054 : TALER_MHD_reply_json (struct MHD_Connection *connection,
195 : const json_t *json,
196 : unsigned int response_code)
197 : {
198 : struct MHD_Response *response;
199 : void *json_str;
200 : size_t json_len;
201 : MHD_RESULT is_compressed;
202 :
203 1054 : json_str = json_dumps (json,
204 : JSON_INDENT (2));
205 1054 : if (NULL == json_str)
206 : {
207 : /**
208 : * This log helps to figure out which
209 : * function called this one and assert-failed.
210 : */
211 0 : TALER_LOG_ERROR ("Aborting json-packing for HTTP code: %u\n",
212 : response_code);
213 :
214 0 : GNUNET_assert (0);
215 : return MHD_NO;
216 : }
217 1054 : json_len = strlen (json_str);
218 : /* try to compress the body */
219 1054 : is_compressed = MHD_NO;
220 1054 : if (TALER_MHD_CT_DEFLATE ==
221 1054 : TALER_MHD_can_compress (connection,
222 : TALER_MHD_CT_DEFLATE))
223 492 : is_compressed = TALER_MHD_body_compress (&json_str,
224 : &json_len);
225 1054 : response = MHD_create_response_from_buffer (json_len,
226 : json_str,
227 : MHD_RESPMEM_MUST_FREE);
228 1054 : if (NULL == response)
229 : {
230 0 : free (json_str);
231 0 : GNUNET_break (0);
232 0 : return MHD_NO;
233 : }
234 1054 : TALER_MHD_add_global_headers (response,
235 : false);
236 1054 : GNUNET_break (MHD_YES ==
237 : MHD_add_response_header (response,
238 : MHD_HTTP_HEADER_CONTENT_TYPE,
239 : "application/json"));
240 1054 : if (MHD_YES == is_compressed)
241 : {
242 : /* Need to indicate to client that body is compressed */
243 439 : if (MHD_NO ==
244 439 : MHD_add_response_header (response,
245 : MHD_HTTP_HEADER_CONTENT_ENCODING,
246 : "deflate"))
247 : {
248 0 : GNUNET_break (0);
249 0 : MHD_destroy_response (response);
250 0 : return MHD_NO;
251 : }
252 : }
253 :
254 : {
255 : MHD_RESULT ret;
256 :
257 1054 : ret = MHD_queue_response (connection,
258 : response_code,
259 : response);
260 1054 : MHD_destroy_response (response);
261 1054 : return ret;
262 : }
263 : }
264 :
265 :
266 : MHD_RESULT
267 970 : TALER_MHD_reply_json_steal (struct MHD_Connection *connection,
268 : json_t *json,
269 : unsigned int response_code)
270 : {
271 : MHD_RESULT ret;
272 :
273 970 : ret = TALER_MHD_reply_json (connection,
274 : json,
275 : response_code);
276 970 : json_decref (json);
277 970 : return ret;
278 : }
279 :
280 :
281 : MHD_RESULT
282 0 : TALER_MHD_reply_cors_preflight (struct MHD_Connection *connection)
283 : {
284 : struct MHD_Response *response;
285 :
286 0 : response = MHD_create_response_from_buffer (0,
287 : NULL,
288 : MHD_RESPMEM_PERSISTENT);
289 0 : if (NULL == response)
290 0 : return MHD_NO;
291 : /* This adds the Access-Control-Allow-Origin header.
292 : * All endpoints of the exchange allow CORS. */
293 0 : TALER_MHD_add_global_headers (response,
294 : true);
295 0 : GNUNET_break (MHD_YES ==
296 : MHD_add_response_header (response,
297 : /* Not available as MHD constant yet */
298 : "Access-Control-Allow-Headers",
299 : "*"));
300 0 : GNUNET_break (MHD_YES ==
301 : MHD_add_response_header (response,
302 : /* Not available as MHD constant yet */
303 : "Access-Control-Allow-Methods",
304 : "*"));
305 : {
306 : MHD_RESULT ret;
307 :
308 0 : ret = MHD_queue_response (connection,
309 : MHD_HTTP_NO_CONTENT,
310 : response);
311 0 : MHD_destroy_response (response);
312 0 : return ret;
313 : }
314 : }
315 :
316 :
317 : struct MHD_Response *
318 0 : TALER_MHD_make_error (enum TALER_ErrorCode ec,
319 : const char *detail)
320 : {
321 0 : return TALER_MHD_MAKE_JSON_PACK (
322 : TALER_MHD_PACK_EC (ec),
323 : GNUNET_JSON_pack_allow_null (
324 : GNUNET_JSON_pack_string ("detail", detail)));
325 : }
326 :
327 :
328 : MHD_RESULT
329 41 : TALER_MHD_reply_with_error (struct MHD_Connection *connection,
330 : unsigned int http_status,
331 : enum TALER_ErrorCode ec,
332 : const char *detail)
333 : {
334 41 : return TALER_MHD_REPLY_JSON_PACK (
335 : connection,
336 : http_status,
337 : TALER_MHD_PACK_EC (ec),
338 : GNUNET_JSON_pack_allow_null (
339 : GNUNET_JSON_pack_string ("detail", detail)));
340 : }
341 :
342 :
343 : MHD_RESULT
344 0 : TALER_MHD_reply_with_ec (struct MHD_Connection *connection,
345 : enum TALER_ErrorCode ec,
346 : const char *detail)
347 : {
348 0 : unsigned int hc = TALER_ErrorCode_get_http_status (ec);
349 :
350 0 : if ( (0 == hc) ||
351 : (UINT_MAX == hc) )
352 : {
353 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
354 : "Invalid Taler error code %d provided for response!\n",
355 : (int) ec);
356 0 : hc = MHD_HTTP_INTERNAL_SERVER_ERROR;
357 : }
358 0 : return TALER_MHD_reply_with_error (connection,
359 : hc,
360 : ec,
361 : detail);
362 : }
363 :
364 :
365 : MHD_RESULT
366 0 : TALER_MHD_reply_request_too_large (struct MHD_Connection *connection)
367 : {
368 0 : return TALER_MHD_reply_with_error (connection,
369 : MHD_HTTP_REQUEST_ENTITY_TOO_LARGE,
370 : TALER_EC_GENERIC_UPLOAD_EXCEEDS_LIMIT,
371 : NULL);
372 : }
373 :
374 :
375 : MHD_RESULT
376 4 : TALER_MHD_reply_agpl (struct MHD_Connection *connection,
377 : const char *url)
378 : {
379 4 : const char *agpl =
380 : "This server is licensed under the Affero GPL. You will now be redirected to the source code.";
381 : struct MHD_Response *response;
382 :
383 4 : response = MHD_create_response_from_buffer (strlen (agpl),
384 : (void *) agpl,
385 : MHD_RESPMEM_PERSISTENT);
386 4 : if (NULL == response)
387 : {
388 0 : GNUNET_break (0);
389 0 : return MHD_NO;
390 : }
391 4 : TALER_MHD_add_global_headers (response,
392 : true);
393 4 : GNUNET_break (MHD_YES ==
394 : MHD_add_response_header (response,
395 : MHD_HTTP_HEADER_CONTENT_TYPE,
396 : "text/plain"));
397 4 : if (MHD_NO ==
398 4 : MHD_add_response_header (response,
399 : MHD_HTTP_HEADER_LOCATION,
400 : url))
401 : {
402 0 : GNUNET_break (0);
403 0 : MHD_destroy_response (response);
404 0 : return MHD_NO;
405 : }
406 :
407 : {
408 : MHD_RESULT ret;
409 :
410 4 : ret = MHD_queue_response (connection,
411 : MHD_HTTP_FOUND,
412 : response);
413 4 : MHD_destroy_response (response);
414 4 : return ret;
415 : }
416 : }
417 :
418 :
419 : MHD_RESULT
420 4218 : TALER_MHD_reply_static (struct MHD_Connection *connection,
421 : unsigned int http_status,
422 : const char *mime_type,
423 : const char *body,
424 : size_t body_size)
425 : {
426 : struct MHD_Response *response;
427 :
428 4218 : response = MHD_create_response_from_buffer (body_size,
429 : (void *) body,
430 : MHD_RESPMEM_PERSISTENT);
431 4218 : if (NULL == response)
432 : {
433 0 : GNUNET_break (0);
434 0 : return MHD_NO;
435 : }
436 4218 : TALER_MHD_add_global_headers (response,
437 : true);
438 4218 : if (NULL != mime_type)
439 6 : GNUNET_break (MHD_YES ==
440 : MHD_add_response_header (response,
441 : MHD_HTTP_HEADER_CONTENT_TYPE,
442 : mime_type));
443 : {
444 : MHD_RESULT ret;
445 :
446 4218 : ret = MHD_queue_response (connection,
447 : http_status,
448 : response);
449 4218 : MHD_destroy_response (response);
450 4218 : return ret;
451 : }
452 : }
453 :
454 :
455 : void
456 1802 : TALER_MHD_get_date_string (struct GNUNET_TIME_Absolute at,
457 : char date[128])
458 : {
459 : static const char *const days[] =
460 : { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
461 : static const char *const mons[] =
462 : { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct",
463 : "Nov", "Dec"};
464 : struct tm now;
465 : time_t t;
466 : #if ! defined(HAVE_C11_GMTIME_S) && ! defined(HAVE_W32_GMTIME_S) && \
467 : ! defined(HAVE_GMTIME_R)
468 : struct tm*pNow;
469 : #endif
470 :
471 1802 : date[0] = 0;
472 1802 : t = (time_t) (at.abs_value_us / 1000LL / 1000LL);
473 : #if defined(HAVE_C11_GMTIME_S)
474 : if (NULL == gmtime_s (&t, &now))
475 : return;
476 : #elif defined(HAVE_W32_GMTIME_S)
477 : if (0 != gmtime_s (&now, &t))
478 : return;
479 : #elif defined(HAVE_GMTIME_R)
480 : if (NULL == gmtime_r (&t, &now))
481 : return;
482 : #else
483 1802 : pNow = gmtime (&t);
484 1802 : if (NULL == pNow)
485 0 : return;
486 1802 : now = *pNow;
487 : #endif
488 1802 : sprintf (date,
489 : "%3s, %02u %3s %04u %02u:%02u:%02u GMT",
490 1802 : days[now.tm_wday % 7],
491 1802 : (unsigned int) now.tm_mday,
492 1802 : mons[now.tm_mon % 12],
493 1802 : (unsigned int) (1900 + now.tm_year),
494 1802 : (unsigned int) now.tm_hour,
495 1802 : (unsigned int) now.tm_min,
496 1802 : (unsigned int) now.tm_sec);
497 : }
498 :
499 :
500 : /* end of mhd_responses.c */
|