LCOV - code coverage report
Current view: top level - mhd - mhd_responses.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 69.8 % 162 113
Test Date: 2026-02-16 10:50:18 Functions: 75.0 % 16 12

            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           26 : TALER_MHD_setup (enum TALER_MHD_GlobalOptions go)
      37              : {
      38           26 :   TM_go = go;
      39           26 : }
      40              : 
      41              : 
      42              : void
      43         1476 : TALER_MHD_add_global_headers (struct MHD_Response *response,
      44              :                               bool allow_store)
      45              : {
      46         1476 :   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         1476 :   GNUNET_break (MHD_YES ==
      54              :                 MHD_add_response_header (response,
      55              :                                          MHD_HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
      56              :                                          "*"));
      57         1476 :   GNUNET_break (MHD_YES ==
      58              :                 MHD_add_response_header (response,
      59              :                                          /* Not available as MHD constant yet */
      60              :                                          "Access-Control-Expose-Headers",
      61              :                                          "*"));
      62         1476 :   if (! allow_store)
      63          829 :     GNUNET_break (MHD_YES ==
      64              :                   MHD_add_response_header (response,
      65              :                                            MHD_HTTP_HEADER_CACHE_CONTROL,
      66              :                                            "no-store"));
      67         1476 : }
      68              : 
      69              : 
      70              : enum TALER_MHD_CompressionType
      71          846 : 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          846 :   } 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          846 :   if (0 != (TM_go & TALER_MHD_GO_DISABLE_COMPRESSION))
      89            0 :     return TALER_MHD_CT_NONE;
      90          846 :   ae = MHD_lookup_connection_value (connection,
      91              :                                     MHD_HEADER_KIND,
      92              :                                     MHD_HTTP_HEADER_ACCEPT_ENCODING);
      93          846 :   if (NULL == ae)
      94          279 :     return TALER_MHD_CT_NONE;
      95          567 :   if (0 == strcmp (ae,
      96              :                    "*"))
      97            0 :     return MHD_YES;
      98         1753 :   for (unsigned int i=0; NULL != map[i].name; i++)
      99              :   {
     100         1701 :     const char *name = map[i].name;
     101              : 
     102         1701 :     if (map[i].ct > max)
     103         1134 :       continue; /* not allowed by client */
     104          567 :     de = strstr (ae,
     105              :                  name);
     106          567 :     if (NULL == de)
     107           52 :       continue;
     108          515 :     if ( ( (de == ae) ||
     109            0 :            (de[-1] == ',') ||
     110            0 :            (de[-1] == ' ') ) &&
     111          515 :          ( (de[strlen (name)] == '\0') ||
     112          515 :            (de[strlen (name)] == ',') ||
     113            0 :            (de[strlen (name)] == ';') ) )
     114          515 :       return map[i].ct;
     115              :   }
     116           52 :   return TALER_MHD_CT_NONE;
     117              : }
     118              : 
     119              : 
     120              : MHD_RESULT
     121          571 : 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          571 :   cbuf_size = compressBound (*buf_size);
     129          571 :   cbuf = malloc (cbuf_size);
     130          571 :   if (NULL == cbuf)
     131            0 :     return MHD_NO;
     132          571 :   ret = compress (cbuf,
     133              :                   &cbuf_size,
     134              :                   (const Bytef *) *buf,
     135              :                   *buf_size);
     136          571 :   if ( (Z_OK != ret) ||
     137          571 :        (cbuf_size >= *buf_size) )
     138              :   {
     139              :     /* compression failed */
     140           49 :     free (cbuf);
     141           49 :     return MHD_NO;
     142              :   }
     143          522 :   free (*buf);
     144          522 :   *buf = (void *) cbuf;
     145          522 :   *buf_size = (size_t) cbuf_size;
     146          522 :   return MHD_YES;
     147              : }
     148              : 
     149              : 
     150              : struct MHD_Response *
     151           40 : TALER_MHD_make_json (const json_t *json)
     152              : {
     153              :   struct MHD_Response *response;
     154              :   char *json_str;
     155              : 
     156           40 :   json_str = json_dumps (json,
     157              :                          JSON_INDENT (2));
     158           40 :   if (NULL == json_str)
     159              :   {
     160            0 :     GNUNET_break (0);
     161            0 :     return NULL;
     162              :   }
     163           40 :   response = MHD_create_response_from_buffer (strlen (json_str),
     164              :                                               json_str,
     165              :                                               MHD_RESPMEM_MUST_FREE);
     166           40 :   if (NULL == response)
     167              :   {
     168            0 :     free (json_str);
     169            0 :     GNUNET_break (0);
     170            0 :     return NULL;
     171              :   }
     172           40 :   TALER_MHD_add_global_headers (response,
     173              :                                 false);
     174           40 :   GNUNET_break (MHD_YES ==
     175              :                 MHD_add_response_header (response,
     176              :                                          MHD_HTTP_HEADER_CONTENT_TYPE,
     177              :                                          "application/json"));
     178           40 :   return response;
     179              : }
     180              : 
     181              : 
     182              : struct MHD_Response *
     183           40 : TALER_MHD_make_json_steal (json_t *json)
     184              : {
     185              :   struct MHD_Response *res;
     186              : 
     187           40 :   res = TALER_MHD_make_json (json);
     188           40 :   json_decref (json);
     189           40 :   return res;
     190              : }
     191              : 
     192              : 
     193              : MHD_RESULT
     194          784 : 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          784 :   json_str = json_dumps (json,
     204              :                          JSON_INDENT (2));
     205          784 :   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          784 :   json_len = strlen (json_str);
     218              :   /* try to compress the body */
     219          784 :   is_compressed = MHD_NO;
     220          784 :   if (TALER_MHD_CT_DEFLATE ==
     221          784 :       TALER_MHD_can_compress (connection,
     222              :                               TALER_MHD_CT_DEFLATE))
     223          478 :     is_compressed = TALER_MHD_body_compress (&json_str,
     224              :                                              &json_len);
     225          784 :   response = MHD_create_response_from_buffer (json_len,
     226              :                                               json_str,
     227              :                                               MHD_RESPMEM_MUST_FREE);
     228          784 :   if (NULL == response)
     229              :   {
     230            0 :     free (json_str);
     231            0 :     GNUNET_break (0);
     232            0 :     return MHD_NO;
     233              :   }
     234          784 :   TALER_MHD_add_global_headers (response,
     235              :                                 false);
     236          784 :   GNUNET_break (MHD_YES ==
     237              :                 MHD_add_response_header (response,
     238              :                                          MHD_HTTP_HEADER_CONTENT_TYPE,
     239              :                                          "application/json"));
     240          784 :   if (MHD_YES == is_compressed)
     241              :   {
     242              :     /* Need to indicate to client that body is compressed */
     243          429 :     if (MHD_NO ==
     244          429 :         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          784 :     ret = MHD_queue_response (connection,
     258              :                               response_code,
     259              :                               response);
     260          784 :     MHD_destroy_response (response);
     261          784 :     return ret;
     262              :   }
     263              : }
     264              : 
     265              : 
     266              : MHD_RESULT
     267          739 : 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          739 :   ret = TALER_MHD_reply_json (connection,
     274              :                               json,
     275              :                               response_code);
     276          739 :   json_decref (json);
     277          739 :   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 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     322              :               "Generating JSON response with code %d (%s)\n",
     323              :               (int) ec,
     324              :               detail);
     325            0 :   return TALER_MHD_MAKE_JSON_PACK (
     326              :     TALER_MHD_PACK_EC (ec),
     327              :     GNUNET_JSON_pack_allow_null (
     328              :       GNUNET_JSON_pack_string ("detail", detail)));
     329              : }
     330              : 
     331              : 
     332              : MHD_RESULT
     333           42 : TALER_MHD_reply_with_error (struct MHD_Connection *connection,
     334              :                             unsigned int http_status,
     335              :                             enum TALER_ErrorCode ec,
     336              :                             const char *detail)
     337              : {
     338           42 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     339              :               "Generating HTTP response with status %u and code %d (%s)\n",
     340              :               http_status,
     341              :               (int) ec,
     342              :               detail);
     343           42 :   return TALER_MHD_REPLY_JSON_PACK (
     344              :     connection,
     345              :     http_status,
     346              :     TALER_MHD_PACK_EC (ec),
     347              :     GNUNET_JSON_pack_allow_null (
     348              :       GNUNET_JSON_pack_string ("detail", detail)));
     349              : }
     350              : 
     351              : 
     352              : MHD_RESULT
     353            0 : TALER_MHD_reply_with_ec (struct MHD_Connection *connection,
     354              :                          enum TALER_ErrorCode ec,
     355              :                          const char *detail)
     356              : {
     357            0 :   unsigned int hc = TALER_ErrorCode_get_http_status (ec);
     358              : 
     359            0 :   if ( (0 == hc) ||
     360              :        (UINT_MAX == hc) )
     361              :   {
     362            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     363              :                 "Invalid Taler error code %d provided for response!\n",
     364              :                 (int) ec);
     365            0 :     hc = MHD_HTTP_INTERNAL_SERVER_ERROR;
     366              :   }
     367            0 :   return TALER_MHD_reply_with_error (connection,
     368              :                                      hc,
     369              :                                      ec,
     370              :                                      detail);
     371              : }
     372              : 
     373              : 
     374              : MHD_RESULT
     375            0 : TALER_MHD_reply_request_too_large (struct MHD_Connection *connection)
     376              : {
     377            0 :   return TALER_MHD_reply_with_error (connection,
     378              :                                      MHD_HTTP_REQUEST_ENTITY_TOO_LARGE,
     379              :                                      TALER_EC_GENERIC_UPLOAD_EXCEEDS_LIMIT,
     380              :                                      NULL);
     381              : }
     382              : 
     383              : 
     384              : MHD_RESULT
     385            4 : TALER_MHD_reply_agpl (struct MHD_Connection *connection,
     386              :                       const char *url)
     387              : {
     388            4 :   const char *agpl =
     389              :     "This server is licensed under the Affero GPL. You will now be redirected to the source code.";
     390              :   struct MHD_Response *response;
     391              : 
     392            4 :   response = MHD_create_response_from_buffer (strlen (agpl),
     393              :                                               (void *) agpl,
     394              :                                               MHD_RESPMEM_PERSISTENT);
     395            4 :   if (NULL == response)
     396              :   {
     397            0 :     GNUNET_break (0);
     398            0 :     return MHD_NO;
     399              :   }
     400            4 :   TALER_MHD_add_global_headers (response,
     401              :                                 true);
     402            4 :   GNUNET_break (MHD_YES ==
     403              :                 MHD_add_response_header (response,
     404              :                                          MHD_HTTP_HEADER_CONTENT_TYPE,
     405              :                                          "text/plain"));
     406            4 :   if (MHD_NO ==
     407            4 :       MHD_add_response_header (response,
     408              :                                MHD_HTTP_HEADER_LOCATION,
     409              :                                url))
     410              :   {
     411            0 :     GNUNET_break (0);
     412            0 :     MHD_destroy_response (response);
     413            0 :     return MHD_NO;
     414              :   }
     415              : 
     416              :   {
     417              :     MHD_RESULT ret;
     418              : 
     419            4 :     ret = MHD_queue_response (connection,
     420              :                               MHD_HTTP_FOUND,
     421              :                               response);
     422            4 :     MHD_destroy_response (response);
     423            4 :     return ret;
     424              :   }
     425              : }
     426              : 
     427              : 
     428              : MHD_RESULT
     429          450 : TALER_MHD_reply_static (struct MHD_Connection *connection,
     430              :                         unsigned int http_status,
     431              :                         const char *mime_type,
     432              :                         const char *body,
     433              :                         size_t body_size)
     434              : {
     435              :   struct MHD_Response *response;
     436              : 
     437          450 :   response = MHD_create_response_from_buffer (body_size,
     438              :                                               (void *) body,
     439              :                                               MHD_RESPMEM_PERSISTENT);
     440          450 :   if (NULL == response)
     441              :   {
     442            0 :     GNUNET_break (0);
     443            0 :     return MHD_NO;
     444              :   }
     445          450 :   TALER_MHD_add_global_headers (response,
     446              :                                 true);
     447          450 :   if (NULL != mime_type)
     448            5 :     GNUNET_break (MHD_YES ==
     449              :                   MHD_add_response_header (response,
     450              :                                            MHD_HTTP_HEADER_CONTENT_TYPE,
     451              :                                            mime_type));
     452              :   {
     453              :     MHD_RESULT ret;
     454              : 
     455          450 :     ret = MHD_queue_response (connection,
     456              :                               http_status,
     457              :                               response);
     458          450 :     MHD_destroy_response (response);
     459          450 :     return ret;
     460              :   }
     461              : }
     462              : 
     463              : 
     464              : void
     465          188 : TALER_MHD_get_date_string (struct GNUNET_TIME_Absolute at,
     466              :                            char date[128])
     467              : {
     468              :   static const char *const days[] =
     469              :   { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
     470              :   static const char *const mons[] =
     471              :   { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct",
     472              :     "Nov", "Dec"};
     473              :   struct tm now;
     474              :   time_t t;
     475              : #if ! defined(HAVE_C11_GMTIME_S) && ! defined(HAVE_W32_GMTIME_S) && \
     476              :   ! defined(HAVE_GMTIME_R)
     477              :   struct tm*pNow;
     478              : #endif
     479              : 
     480          188 :   date[0] = 0;
     481          188 :   t = (time_t) (at.abs_value_us / 1000LL / 1000LL);
     482              : #if defined(HAVE_C11_GMTIME_S)
     483              :   if (NULL == gmtime_s (&t, &now))
     484              :     return;
     485              : #elif defined(HAVE_W32_GMTIME_S)
     486              :   if (0 != gmtime_s (&now, &t))
     487              :     return;
     488              : #elif defined(HAVE_GMTIME_R)
     489              :   if (NULL == gmtime_r (&t, &now))
     490              :     return;
     491              : #else
     492          188 :   pNow = gmtime (&t);
     493          188 :   if (NULL == pNow)
     494            0 :     return;
     495          188 :   now = *pNow;
     496              : #endif
     497          188 :   sprintf (date,
     498              :            "%3s, %02u %3s %04u %02u:%02u:%02u GMT",
     499          188 :            days[now.tm_wday % 7],
     500          188 :            (unsigned int) now.tm_mday,
     501          188 :            mons[now.tm_mon % 12],
     502          188 :            (unsigned int) (1900 + now.tm_year),
     503          188 :            (unsigned int) now.tm_hour,
     504          188 :            (unsigned int) now.tm_min,
     505          188 :            (unsigned int) now.tm_sec);
     506              : }
     507              : 
     508              : 
     509              : /* end of mhd_responses.c */
        

Generated by: LCOV version 2.0-1