LCOV - code coverage report
Current view: top level - mhd - mhd_responses.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 112 160 70.0 %
Date: 2025-07-03 11:36:01 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 "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 */

Generated by: LCOV version 1.16