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

Generated by: LCOV version 1.16