mhd2_responses.c (13565B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2014-2025 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 mhd2_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_mhd2_lib.h" 27 28 29 /** 30 * Global options for response generation. 31 */ 32 static enum TALER_MHD2_GlobalOptions TM_go; 33 34 35 void 36 TALER_MHD2_setup (enum TALER_MHD2_GlobalOptions go) 37 { 38 TM_go = go; 39 } 40 41 42 void 43 TALER_MHD2_add_global_headers (struct MHD_Response *response, 44 bool allow_store) 45 { 46 if (0 != (TM_go & TALER_MHD2_GO_FORCE_CONNECTION_CLOSE)) 47 GNUNET_break (MHD_SC_OK == 48 MHD_response_add_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 GNUNET_break (MHD_SC_OK == 54 MHD_response_add_header (response, 55 MHD_HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN, 56 "*")); 57 GNUNET_break (MHD_SC_OK == 58 MHD_response_add_header (response, 59 /* Not available as MHD constant yet */ 60 "Access-Control-Expose-Headers", 61 "*")); 62 if (! allow_store) 63 GNUNET_break (MHD_SC_OK == 64 MHD_response_add_header (response, 65 MHD_HTTP_HEADER_CACHE_CONTROL, 66 "no-store")); 67 } 68 69 70 bool 71 TALER_MHD2_can_compress (struct MHD_Request *request) 72 { 73 struct MHD_StringNullable aeb; 74 const char *ae; 75 const char *de; 76 77 if (0 != (TM_go & TALER_MHD2_GO_DISABLE_COMPRESSION)) 78 return MHD_NO; 79 if ( (MHD_NO == 80 MHD_request_get_value (request, 81 MHD_VK_HEADER, 82 MHD_HTTP_HEADER_ACCEPT_ENCODING, 83 &aeb)) || 84 (NULL == aeb.cstr) ) 85 return false; 86 ae = aeb.cstr; 87 if (NULL == ae) 88 return false; 89 if (0 == strcmp (ae, 90 "*")) 91 return true; 92 de = strstr (ae, 93 "deflate"); 94 if (NULL == de) 95 return false; 96 if ( ( (de == ae) || 97 (de[-1] == ',') || 98 (de[-1] == ' ') ) && 99 ( (de[strlen ("deflate")] == '\0') || 100 (de[strlen ("deflate")] == ',') || 101 (de[strlen ("deflate")] == ';') ) ) 102 return true; 103 return false; 104 } 105 106 107 bool 108 TALER_MHD2_body_compress (void **buf, 109 size_t *buf_size) 110 { 111 Bytef *cbuf; 112 uLongf cbuf_size; 113 int ret; 114 115 cbuf_size = compressBound (*buf_size); 116 cbuf = malloc (cbuf_size); 117 if (NULL == cbuf) 118 return false; 119 ret = compress (cbuf, 120 &cbuf_size, 121 (const Bytef *) *buf, 122 *buf_size); 123 if ( (Z_OK != ret) || 124 (cbuf_size >= *buf_size) ) 125 { 126 /* compression failed */ 127 free (cbuf); 128 return false; 129 } 130 free (*buf); 131 *buf = (void *) cbuf; 132 *buf_size = (size_t) cbuf_size; 133 return true; 134 } 135 136 137 struct MHD_Response * 138 TALER_MHD2_make_json (enum MHD_HTTP_StatusCode sc, 139 const json_t *json) 140 { 141 struct MHD_Response *response; 142 char *json_str; 143 144 json_str = json_dumps (json, 145 JSON_INDENT (2)); 146 if (NULL == json_str) 147 { 148 GNUNET_break (0); 149 return NULL; 150 } 151 response = MHD_response_from_buffer (sc, 152 strlen (json_str), 153 json_str, 154 &free, 155 json_str); 156 if (NULL == response) 157 { 158 free (json_str); 159 GNUNET_break (0); 160 return NULL; 161 } 162 TALER_MHD2_add_global_headers (response, 163 false); 164 GNUNET_break (MHD_SC_OK == 165 MHD_response_add_header (response, 166 MHD_HTTP_HEADER_CONTENT_TYPE, 167 "application/json")); 168 return response; 169 } 170 171 172 struct MHD_Response * 173 TALER_MHD2_make_json_steal (enum MHD_HTTP_StatusCode sc, 174 json_t *json) 175 { 176 struct MHD_Response *res; 177 178 res = TALER_MHD2_make_json (sc, 179 json); 180 json_decref (json); 181 return res; 182 } 183 184 185 const struct MHD_Action * 186 TALER_MHD2_reply_json (struct MHD_Request *request, 187 const json_t *json, 188 enum MHD_HTTP_StatusCode sc) 189 { 190 struct MHD_Response *response; 191 void *json_str; 192 size_t json_len; 193 bool is_compressed; 194 195 json_str = json_dumps (json, 196 JSON_INDENT (2)); 197 if (NULL == json_str) 198 { 199 /** 200 * This log helps to figure out which 201 * function called this one and assert-failed. 202 */ 203 TALER_LOG_ERROR ("Aborting json-packing for HTTP code: %u\n", 204 sc); 205 206 GNUNET_assert (0); 207 return NULL; 208 } 209 json_len = strlen (json_str); 210 /* try to compress the body */ 211 is_compressed = MHD_NO; 212 if (TALER_MHD2_can_compress (request)) 213 is_compressed = TALER_MHD2_body_compress (&json_str, 214 &json_len); 215 response = MHD_response_from_buffer (sc, 216 json_len, 217 json_str, 218 &free, 219 json_str); 220 if (NULL == response) 221 { 222 free (json_str); 223 GNUNET_break (0); 224 return NULL; 225 } 226 TALER_MHD2_add_global_headers (response, 227 false); 228 GNUNET_break (MHD_SC_OK == 229 MHD_response_add_header (response, 230 MHD_HTTP_HEADER_CONTENT_TYPE, 231 "application/json")); 232 if (is_compressed) 233 { 234 /* Need to indicate to client that body is compressed */ 235 if (MHD_SC_OK == 236 MHD_response_add_header (response, 237 MHD_HTTP_HEADER_CONTENT_ENCODING, 238 "deflate")) 239 { 240 GNUNET_break (0); 241 MHD_response_destroy (response); 242 return NULL; 243 } 244 } 245 246 return MHD_action_from_response (request, 247 response); 248 } 249 250 251 const struct MHD_Action * 252 TALER_MHD2_reply_json_steal (struct MHD_Request *request, 253 json_t *json, 254 enum MHD_HTTP_StatusCode sc) 255 { 256 const struct MHD_Action *ret; 257 258 ret = TALER_MHD2_reply_json (request, 259 json, 260 sc); 261 json_decref (json); 262 return ret; 263 } 264 265 266 const struct MHD_Action * 267 TALER_MHD2_reply_cors_preflight (struct MHD_Request *request) 268 { 269 struct MHD_Response *response; 270 271 response = MHD_response_from_empty (MHD_HTTP_STATUS_NO_CONTENT); 272 if (NULL == response) 273 return NULL; 274 /* This adds the Access-Control-Allow-Origin header. 275 * All endpoints of the exchange allow CORS. */ 276 TALER_MHD2_add_global_headers (response, 277 true); 278 GNUNET_break (MHD_SC_OK == 279 MHD_response_add_header (response, 280 /* Not available as MHD constant yet */ 281 "Access-Control-Allow-Headers", 282 "*")); 283 GNUNET_break (MHD_SC_OK == 284 MHD_response_add_header (response, 285 /* Not available as MHD constant yet */ 286 "Access-Control-Allow-Methods", 287 "*")); 288 return MHD_action_from_response (request, 289 response); 290 } 291 292 293 struct MHD_Response * 294 TALER_MHD2_make_error (enum TALER_ErrorCode ec, 295 const char *detail) 296 { 297 return TALER_MHD2_MAKE_JSON_PACK ( 298 TALER_ErrorCode_get_http_status (ec), 299 TALER_MHD2_PACK_EC (ec), 300 GNUNET_JSON_pack_allow_null ( 301 GNUNET_JSON_pack_string ("detail", detail))); 302 } 303 304 305 const struct MHD_Action * 306 TALER_MHD2_reply_with_error (struct MHD_Request *request, 307 enum MHD_HTTP_StatusCode sc, 308 enum TALER_ErrorCode ec, 309 const char *detail) 310 { 311 return TALER_MHD2_REPLY_JSON_PACK ( 312 request, 313 sc, 314 TALER_MHD2_PACK_EC (ec), 315 GNUNET_JSON_pack_allow_null ( 316 GNUNET_JSON_pack_string ("detail", detail))); 317 } 318 319 320 const struct MHD_Action * 321 TALER_MHD2_reply_with_ec (struct MHD_Request *request, 322 enum TALER_ErrorCode ec, 323 const char *detail) 324 { 325 unsigned int hc 326 = TALER_ErrorCode_get_http_status (ec); 327 328 if ( (0 == hc) || 329 (UINT_MAX == hc) ) 330 { 331 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 332 "Invalid Taler error code %u provided for response!\n", 333 (unsigned int) ec); 334 hc = MHD_HTTP_STATUS_INTERNAL_SERVER_ERROR; 335 } 336 return TALER_MHD2_reply_with_error (request, 337 (enum MHD_HTTP_StatusCode) hc, 338 ec, 339 detail); 340 } 341 342 343 const struct MHD_Action * 344 TALER_MHD2_reply_request_too_large (struct MHD_Request *request) 345 { 346 return TALER_MHD2_reply_with_error ( 347 request, 348 MHD_HTTP_STATUS_CONTENT_TOO_LARGE, 349 TALER_EC_GENERIC_UPLOAD_EXCEEDS_LIMIT, 350 NULL); 351 } 352 353 354 const struct MHD_Action * 355 TALER_MHD2_reply_agpl (struct MHD_Request *request, 356 const char *url) 357 { 358 const char *agpl = 359 "This server is licensed under the Affero GPL. You will now be redirected to the source code."; 360 struct MHD_Response *response; 361 362 response = MHD_response_from_buffer_static (MHD_HTTP_STATUS_FOUND, 363 strlen (agpl), 364 (void *) agpl); 365 if (NULL == response) 366 { 367 GNUNET_break (0); 368 return MHD_NO; 369 } 370 TALER_MHD2_add_global_headers (response, 371 true); 372 GNUNET_break (MHD_SC_OK == 373 MHD_response_add_header (response, 374 MHD_HTTP_HEADER_CONTENT_TYPE, 375 "text/plain")); 376 if (MHD_SC_OK == 377 MHD_response_add_header (response, 378 MHD_HTTP_HEADER_LOCATION, 379 url)) 380 { 381 GNUNET_break (0); 382 MHD_response_destroy (response); 383 return NULL; 384 } 385 return MHD_action_from_response (request, 386 response); 387 } 388 389 390 const struct MHD_Action * 391 TALER_MHD2_reply_static (struct MHD_Request *request, 392 enum MHD_HTTP_StatusCode sc, 393 const char *mime_type, 394 const char *body, 395 size_t body_size) 396 { 397 struct MHD_Response *response; 398 399 response = MHD_response_from_buffer_static (sc, 400 body_size, 401 body); 402 if (NULL == response) 403 { 404 GNUNET_break (0); 405 return NULL; 406 } 407 TALER_MHD2_add_global_headers (response, 408 true); 409 if (NULL != mime_type) 410 GNUNET_break (MHD_SC_OK == 411 MHD_response_add_header (response, 412 MHD_HTTP_HEADER_CONTENT_TYPE, 413 mime_type)); 414 return MHD_action_from_response (request, 415 response); 416 } 417 418 419 void 420 TALER_MHD2_get_date_string (struct GNUNET_TIME_Absolute at, 421 char date[128]) 422 { 423 static const char *const days[] = 424 { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; 425 static const char *const mons[] = 426 { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", 427 "Nov", "Dec"}; 428 struct tm now; 429 time_t t; 430 #if ! defined(HAVE_C11_GMTIME_S) && ! defined(HAVE_W32_GMTIME_S) && \ 431 ! defined(HAVE_GMTIME_R) 432 struct tm*pNow; 433 #endif 434 435 date[0] = 0; 436 t = (time_t) (at.abs_value_us / 1000LL / 1000LL); 437 #if defined(HAVE_C11_GMTIME_S) 438 if (NULL == gmtime_s (&t, &now)) 439 return; 440 #elif defined(HAVE_W32_GMTIME_S) 441 if (0 != gmtime_s (&now, &t)) 442 return; 443 #elif defined(HAVE_GMTIME_R) 444 if (NULL == gmtime_r (&t, &now)) 445 return; 446 #else 447 pNow = gmtime (&t); 448 if (NULL == pNow) 449 return; 450 now = *pNow; 451 #endif 452 sprintf (date, 453 "%3s, %02u %3s %04u %02u:%02u:%02u GMT", 454 days[now.tm_wday % 7], 455 (unsigned int) now.tm_mday, 456 mons[now.tm_mon % 12], 457 (unsigned int) (1900 + now.tm_year), 458 (unsigned int) now.tm_hour, 459 (unsigned int) now.tm_min, 460 (unsigned int) now.tm_sec); 461 } 462 463 464 /* end of mhd_responses.c */