LCOV - code coverage report
Current view: top level - mhd - mhd_responses.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 104 153 68.0 %
Date: 2025-06-05 21:03:14 Functions: 12 16 75.0 %

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

Generated by: LCOV version 1.16