LCOV - code coverage report
Current view: top level - mhd - mhd_responses.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 70.0 % 160 112
Test Date: 2026-01-12 22:36:41 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         5605 : TALER_MHD_add_global_headers (struct MHD_Response *response,
      44              :                               bool allow_store)
      45              : {
      46         5605 :   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         5605 :   GNUNET_break (MHD_YES ==
      54              :                 MHD_add_response_header (response,
      55              :                                          MHD_HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
      56              :                                          "*"));
      57         5605 :   GNUNET_break (MHD_YES ==
      58              :                 MHD_add_response_header (response,
      59              :                                          /* Not available as MHD constant yet */
      60              :                                          "Access-Control-Expose-Headers",
      61              :                                          "*"));
      62         5605 :   if (! allow_store)
      63          828 :     GNUNET_break (MHD_YES ==
      64              :                   MHD_add_response_header (response,
      65              :                                            MHD_HTTP_HEADER_CACHE_CONTROL,
      66              :                                            "no-store"));
      67         5605 : }
      68              : 
      69              : 
      70              : enum TALER_MHD_CompressionType
      71          845 : 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          845 :   } 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          845 :   if (0 != (TM_go & TALER_MHD_GO_DISABLE_COMPRESSION))
      89            0 :     return TALER_MHD_CT_NONE;
      90          845 :   ae = MHD_lookup_connection_value (connection,
      91              :                                     MHD_HEADER_KIND,
      92              :                                     MHD_HTTP_HEADER_ACCEPT_ENCODING);
      93          845 :   if (NULL == ae)
      94          279 :     return TALER_MHD_CT_NONE;
      95          566 :   if (0 == strcmp (ae,
      96              :                    "*"))
      97            0 :     return MHD_YES;
      98         1750 :   for (unsigned int i=0; NULL != map[i].name; i++)
      99              :   {
     100         1698 :     const char *name = map[i].name;
     101              : 
     102         1698 :     if (map[i].ct > max)
     103         1132 :       continue; /* not allowed by client */
     104          566 :     de = strstr (ae,
     105              :                  name);
     106          566 :     if (NULL == de)
     107           52 :       continue;
     108          514 :     if ( ( (de == ae) ||
     109            0 :            (de[-1] == ',') ||
     110            0 :            (de[-1] == ' ') ) &&
     111          514 :          ( (de[strlen (name)] == '\0') ||
     112          514 :            (de[strlen (name)] == ',') ||
     113            0 :            (de[strlen (name)] == ';') ) )
     114          514 :       return map[i].ct;
     115              :   }
     116           52 :   return TALER_MHD_CT_NONE;
     117              : }
     118              : 
     119              : 
     120              : MHD_RESULT
     121         1165 : 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         1165 :   cbuf_size = compressBound (*buf_size);
     129         1165 :   cbuf = malloc (cbuf_size);
     130         1165 :   if (NULL == cbuf)
     131            0 :     return MHD_NO;
     132         1165 :   ret = compress (cbuf,
     133              :                   &cbuf_size,
     134              :                   (const Bytef *) *buf,
     135              :                   *buf_size);
     136         1165 :   if ( (Z_OK != ret) ||
     137         1165 :        (cbuf_size >= *buf_size) )
     138              :   {
     139              :     /* compression failed */
     140           48 :     free (cbuf);
     141           48 :     return MHD_NO;
     142              :   }
     143         1117 :   free (*buf);
     144         1117 :   *buf = (void *) cbuf;
     145         1117 :   *buf_size = (size_t) cbuf_size;
     146         1117 :   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          783 : 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          783 :   json_str = json_dumps (json,
     204              :                          JSON_INDENT (2));
     205          783 :   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          783 :   json_len = strlen (json_str);
     218              :   /* try to compress the body */
     219          783 :   is_compressed = MHD_NO;
     220          783 :   if (TALER_MHD_CT_DEFLATE ==
     221          783 :       TALER_MHD_can_compress (connection,
     222              :                               TALER_MHD_CT_DEFLATE))
     223          477 :     is_compressed = TALER_MHD_body_compress (&json_str,
     224              :                                              &json_len);
     225          783 :   response = MHD_create_response_from_buffer (json_len,
     226              :                                               json_str,
     227              :                                               MHD_RESPMEM_MUST_FREE);
     228          783 :   if (NULL == response)
     229              :   {
     230            0 :     free (json_str);
     231            0 :     GNUNET_break (0);
     232            0 :     return MHD_NO;
     233              :   }
     234          783 :   TALER_MHD_add_global_headers (response,
     235              :                                 false);
     236          783 :   GNUNET_break (MHD_YES ==
     237              :                 MHD_add_response_header (response,
     238              :                                          MHD_HTTP_HEADER_CONTENT_TYPE,
     239              :                                          "application/json"));
     240          783 :   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          783 :     ret = MHD_queue_response (connection,
     258              :                               response_code,
     259              :                               response);
     260          783 :     MHD_destroy_response (response);
     261          783 :     return ret;
     262              :   }
     263              : }
     264              : 
     265              : 
     266              : MHD_RESULT
     267          738 : 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          738 :   ret = TALER_MHD_reply_json (connection,
     274              :                               json,
     275              :                               response_code);
     276          738 :   json_decref (json);
     277          738 :   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           42 : 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           42 :   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         3390 : 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         3390 :   response = MHD_create_response_from_buffer (body_size,
     429              :                                               (void *) body,
     430              :                                               MHD_RESPMEM_PERSISTENT);
     431         3390 :   if (NULL == response)
     432              :   {
     433            0 :     GNUNET_break (0);
     434            0 :     return MHD_NO;
     435              :   }
     436         3390 :   TALER_MHD_add_global_headers (response,
     437              :                                 true);
     438         3390 :   if (NULL != mime_type)
     439            5 :     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         3390 :     ret = MHD_queue_response (connection,
     447              :                               http_status,
     448              :                               response);
     449         3390 :     MHD_destroy_response (response);
     450         3390 :     return ret;
     451              :   }
     452              : }
     453              : 
     454              : 
     455              : void
     456         1378 : 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         1378 :   date[0] = 0;
     472         1378 :   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         1378 :   pNow = gmtime (&t);
     484         1378 :   if (NULL == pNow)
     485            0 :     return;
     486         1378 :   now = *pNow;
     487              : #endif
     488         1378 :   sprintf (date,
     489              :            "%3s, %02u %3s %04u %02u:%02u:%02u GMT",
     490         1378 :            days[now.tm_wday % 7],
     491         1378 :            (unsigned int) now.tm_mday,
     492         1378 :            mons[now.tm_mon % 12],
     493         1378 :            (unsigned int) (1900 + now.tm_year),
     494         1378 :            (unsigned int) now.tm_hour,
     495         1378 :            (unsigned int) now.tm_min,
     496         1378 :            (unsigned int) now.tm_sec);
     497              : }
     498              : 
     499              : 
     500              : /* end of mhd_responses.c */
        

Generated by: LCOV version 2.0-1