LCOV - code coverage report
Current view: top level - mhd - mhd_responses.c (source / functions) Hit Total Coverage
Test: GNU Taler exchange coverage report Lines: 34 175 19.4 %
Date: 2022-08-25 06:15:09 Functions: 6 18 33.3 %
Legend: Lines: hit not hit

          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           2 : TALER_MHD_setup (enum TALER_MHD_GlobalOptions go)
      37             : {
      38           2 :   TM_go = go;
      39           2 : }
      40             : 
      41             : 
      42             : void
      43          14 : TALER_MHD_add_global_headers (struct MHD_Response *response)
      44             : {
      45          14 :   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          14 :   GNUNET_break (MHD_YES ==
      53             :                 MHD_add_response_header (response,
      54             :                                          MHD_HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
      55             :                                          "*"));
      56          14 :   GNUNET_break (MHD_YES ==
      57             :                 MHD_add_response_header (response,
      58             :                                          /* Not available as MHD constant yet */
      59             :                                          "Access-Control-Expose-Headers",
      60             :                                          "*"));
      61          14 : }
      62             : 
      63             : 
      64             : MHD_RESULT
      65          14 : TALER_MHD_can_compress (struct MHD_Connection *connection)
      66             : {
      67             :   const char *ae;
      68             :   const char *de;
      69             : 
      70          14 :   if (0 != (TM_go & TALER_MHD_GO_DISABLE_COMPRESSION))
      71           0 :     return MHD_NO;
      72          14 :   ae = MHD_lookup_connection_value (connection,
      73             :                                     MHD_HEADER_KIND,
      74             :                                     MHD_HTTP_HEADER_ACCEPT_ENCODING);
      75          14 :   if (NULL == ae)
      76          14 :     return MHD_NO;
      77           0 :   if (0 == strcmp (ae,
      78             :                    "*"))
      79           0 :     return MHD_YES;
      80           0 :   de = strstr (ae,
      81             :                "deflate");
      82           0 :   if (NULL == de)
      83           0 :     return MHD_NO;
      84           0 :   if ( ( (de == ae) ||
      85           0 :          (de[-1] == ',') ||
      86           0 :          (de[-1] == ' ') ) &&
      87           0 :        ( (de[strlen ("deflate")] == '\0') ||
      88           0 :          (de[strlen ("deflate")] == ',') ||
      89           0 :          (de[strlen ("deflate")] == ';') ) )
      90           0 :     return MHD_YES;
      91           0 :   return MHD_NO;
      92             : }
      93             : 
      94             : 
      95             : MHD_RESULT
      96           0 : 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           0 :   cbuf_size = compressBound (*buf_size);
     104           0 :   cbuf = malloc (cbuf_size);
     105           0 :   if (NULL == cbuf)
     106           0 :     return MHD_NO;
     107           0 :   ret = compress (cbuf,
     108             :                   &cbuf_size,
     109             :                   (const Bytef *) *buf,
     110             :                   *buf_size);
     111           0 :   if ( (Z_OK != ret) ||
     112           0 :        (cbuf_size >= *buf_size) )
     113             :   {
     114             :     /* compression failed */
     115           0 :     free (cbuf);
     116           0 :     return MHD_NO;
     117             :   }
     118           0 :   free (*buf);
     119           0 :   *buf = (void *) cbuf;
     120           0 :   *buf_size = (size_t) cbuf_size;
     121           0 :   return MHD_YES;
     122             : }
     123             : 
     124             : 
     125             : struct MHD_Response *
     126           0 : TALER_MHD_make_json (const json_t *json)
     127             : {
     128             :   struct MHD_Response *response;
     129             :   char *json_str;
     130             : 
     131           0 :   json_str = json_dumps (json,
     132             :                          JSON_INDENT (2));
     133           0 :   if (NULL == json_str)
     134             :   {
     135           0 :     GNUNET_break (0);
     136           0 :     return NULL;
     137             :   }
     138           0 :   response = MHD_create_response_from_buffer (strlen (json_str),
     139             :                                               json_str,
     140             :                                               MHD_RESPMEM_MUST_FREE);
     141           0 :   if (NULL == response)
     142             :   {
     143           0 :     free (json_str);
     144           0 :     GNUNET_break (0);
     145           0 :     return NULL;
     146             :   }
     147           0 :   TALER_MHD_add_global_headers (response);
     148           0 :   GNUNET_break (MHD_YES ==
     149             :                 MHD_add_response_header (response,
     150             :                                          MHD_HTTP_HEADER_CONTENT_TYPE,
     151             :                                          "application/json"));
     152           0 :   return response;
     153             : }
     154             : 
     155             : 
     156             : struct MHD_Response *
     157           0 : TALER_MHD_make_json_steal (json_t *json)
     158             : {
     159             :   struct MHD_Response *res;
     160             : 
     161           0 :   res = TALER_MHD_make_json (json);
     162           0 :   json_decref (json);
     163           0 :   return res;
     164             : }
     165             : 
     166             : 
     167             : MHD_RESULT
     168          14 : 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          14 :   json_str = json_dumps (json,
     178             :                          JSON_INDENT (2));
     179          14 :   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          14 :   json_len = strlen (json_str);
     192             :   /* try to compress the body */
     193          14 :   is_compressed = MHD_NO;
     194          14 :   if (MHD_YES ==
     195          14 :       TALER_MHD_can_compress (connection))
     196           0 :     is_compressed = TALER_MHD_body_compress (&json_str,
     197             :                                              &json_len);
     198          14 :   response = MHD_create_response_from_buffer (json_len,
     199             :                                               json_str,
     200             :                                               MHD_RESPMEM_MUST_FREE);
     201          14 :   if (NULL == response)
     202             :   {
     203           0 :     free (json_str);
     204           0 :     GNUNET_break (0);
     205           0 :     return MHD_NO;
     206             :   }
     207          14 :   TALER_MHD_add_global_headers (response);
     208          14 :   GNUNET_break (MHD_YES ==
     209             :                 MHD_add_response_header (response,
     210             :                                          MHD_HTTP_HEADER_CONTENT_TYPE,
     211             :                                          "application/json"));
     212          14 :   if (MHD_YES == is_compressed)
     213             :   {
     214             :     /* Need to indicate to client that body is compressed */
     215           0 :     if (MHD_NO ==
     216           0 :         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          14 :     ret = MHD_queue_response (connection,
     230             :                               response_code,
     231             :                               response);
     232          14 :     MHD_destroy_response (response);
     233          14 :     return ret;
     234             :   }
     235             : }
     236             : 
     237             : 
     238             : MHD_RESULT
     239          14 : 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          14 :   ret = TALER_MHD_reply_json (connection,
     246             :                               json,
     247             :                               response_code);
     248          14 :   json_decref (json);
     249          14 :   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             : MHD_RESULT
     289           0 : TALER_MHD_reply_json_pack (struct MHD_Connection *connection,
     290             :                            unsigned int response_code,
     291             :                            const char *fmt,
     292             :                            ...)
     293             : {
     294             :   json_t *json;
     295             :   json_error_t jerror;
     296             : 
     297             :   {
     298             :     va_list argp;
     299             : 
     300           0 :     va_start (argp,
     301             :               fmt);
     302           0 :     json = json_vpack_ex (&jerror,
     303             :                           0,
     304             :                           fmt,
     305             :                           argp);
     306           0 :     va_end (argp);
     307             :   }
     308             : 
     309           0 :   if (NULL == json)
     310             :   {
     311           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     312             :                 "Failed to pack JSON with format `%s': %s\n",
     313             :                 fmt,
     314             :                 jerror.text);
     315           0 :     GNUNET_break (0);
     316           0 :     return MHD_NO;
     317             :   }
     318             : 
     319             :   {
     320             :     MHD_RESULT ret;
     321             : 
     322           0 :     ret = TALER_MHD_reply_json (connection,
     323             :                                 json,
     324             :                                 response_code);
     325           0 :     json_decref (json);
     326           0 :     return ret;
     327             :   }
     328             : }
     329             : 
     330             : 
     331             : struct MHD_Response *
     332           0 : TALER_MHD_make_json_pack (const char *fmt,
     333             :                           ...)
     334             : {
     335             :   json_t *json;
     336             :   json_error_t jerror;
     337             : 
     338             :   {
     339             :     va_list argp;
     340             : 
     341           0 :     va_start (argp, fmt);
     342           0 :     json = json_vpack_ex (&jerror,
     343             :                           0,
     344             :                           fmt,
     345             :                           argp);
     346           0 :     va_end (argp);
     347             :   }
     348             : 
     349           0 :   if (NULL == json)
     350             :   {
     351           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     352             :                 "Failed to pack JSON with format `%s': %s\n",
     353             :                 fmt,
     354             :                 jerror.text);
     355           0 :     GNUNET_break (0);
     356           0 :     return NULL;
     357             :   }
     358             : 
     359             :   {
     360             :     struct MHD_Response *response;
     361             : 
     362           0 :     response = TALER_MHD_make_json (json);
     363           0 :     json_decref (json);
     364           0 :     return response;
     365             :   }
     366             : }
     367             : 
     368             : 
     369             : struct MHD_Response *
     370           0 : TALER_MHD_make_error (enum TALER_ErrorCode ec,
     371             :                       const char *detail)
     372             : {
     373           0 :   return TALER_MHD_MAKE_JSON_PACK (
     374             :     TALER_MHD_PACK_EC (ec),
     375             :     GNUNET_JSON_pack_allow_null (
     376             :       GNUNET_JSON_pack_string ("detail", detail)));
     377             : }
     378             : 
     379             : 
     380             : MHD_RESULT
     381           2 : TALER_MHD_reply_with_error (struct MHD_Connection *connection,
     382             :                             unsigned int http_status,
     383             :                             enum TALER_ErrorCode ec,
     384             :                             const char *detail)
     385             : {
     386           2 :   return TALER_MHD_REPLY_JSON_PACK (
     387             :     connection,
     388             :     http_status,
     389             :     TALER_MHD_PACK_EC (ec),
     390             :     GNUNET_JSON_pack_allow_null (
     391             :       GNUNET_JSON_pack_string ("detail", detail)));
     392             : }
     393             : 
     394             : 
     395             : MHD_RESULT
     396           0 : TALER_MHD_reply_with_ec (struct MHD_Connection *connection,
     397             :                          enum TALER_ErrorCode ec,
     398             :                          const char *detail)
     399             : {
     400           0 :   unsigned int hc = TALER_ErrorCode_get_http_status (ec);
     401             : 
     402           0 :   if ( (0 == hc) ||
     403             :        (UINT_MAX == hc) )
     404             :   {
     405           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     406             :                 "Invalid Taler error code %d provided for response!\n",
     407             :                 (int) ec);
     408           0 :     hc = MHD_HTTP_INTERNAL_SERVER_ERROR;
     409             :   }
     410           0 :   return TALER_MHD_reply_with_error (connection,
     411             :                                      hc,
     412             :                                      ec,
     413             :                                      detail);
     414             : }
     415             : 
     416             : 
     417             : MHD_RESULT
     418           0 : TALER_MHD_reply_request_too_large (struct MHD_Connection *connection)
     419             : {
     420           0 :   return TALER_MHD_reply_with_error (connection,
     421             :                                      MHD_HTTP_REQUEST_ENTITY_TOO_LARGE,
     422             :                                      TALER_EC_GENERIC_UPLOAD_EXCEEDS_LIMIT,
     423             :                                      NULL);
     424             : }
     425             : 
     426             : 
     427             : MHD_RESULT
     428           0 : TALER_MHD_reply_agpl (struct MHD_Connection *connection,
     429             :                       const char *url)
     430             : {
     431           0 :   const char *agpl =
     432             :     "This server is licensed under the Affero GPL. You will now be redirected to the source code.";
     433             :   struct MHD_Response *response;
     434             : 
     435           0 :   response = MHD_create_response_from_buffer (strlen (agpl),
     436             :                                               (void *) agpl,
     437             :                                               MHD_RESPMEM_PERSISTENT);
     438           0 :   if (NULL == response)
     439             :   {
     440           0 :     GNUNET_break (0);
     441           0 :     return MHD_NO;
     442             :   }
     443           0 :   TALER_MHD_add_global_headers (response);
     444           0 :   GNUNET_break (MHD_YES ==
     445             :                 MHD_add_response_header (response,
     446             :                                          MHD_HTTP_HEADER_CONTENT_TYPE,
     447             :                                          "text/plain"));
     448           0 :   if (MHD_NO ==
     449           0 :       MHD_add_response_header (response,
     450             :                                MHD_HTTP_HEADER_LOCATION,
     451             :                                url))
     452             :   {
     453           0 :     GNUNET_break (0);
     454           0 :     MHD_destroy_response (response);
     455           0 :     return MHD_NO;
     456             :   }
     457             : 
     458             :   {
     459             :     MHD_RESULT ret;
     460             : 
     461           0 :     ret = MHD_queue_response (connection,
     462             :                               MHD_HTTP_FOUND,
     463             :                               response);
     464           0 :     MHD_destroy_response (response);
     465           0 :     return ret;
     466             :   }
     467             : }
     468             : 
     469             : 
     470             : MHD_RESULT
     471           0 : TALER_MHD_reply_static (struct MHD_Connection *connection,
     472             :                         unsigned int http_status,
     473             :                         const char *mime_type,
     474             :                         const char *body,
     475             :                         size_t body_size)
     476             : {
     477             :   struct MHD_Response *response;
     478             : 
     479           0 :   response = MHD_create_response_from_buffer (body_size,
     480             :                                               (void *) body,
     481             :                                               MHD_RESPMEM_PERSISTENT);
     482           0 :   if (NULL == response)
     483             :   {
     484           0 :     GNUNET_break (0);
     485           0 :     return MHD_NO;
     486             :   }
     487           0 :   TALER_MHD_add_global_headers (response);
     488           0 :   if (NULL != mime_type)
     489           0 :     GNUNET_break (MHD_YES ==
     490             :                   MHD_add_response_header (response,
     491             :                                            MHD_HTTP_HEADER_CONTENT_TYPE,
     492             :                                            mime_type));
     493             :   {
     494             :     MHD_RESULT ret;
     495             : 
     496           0 :     ret = MHD_queue_response (connection,
     497             :                               http_status,
     498             :                               response);
     499           0 :     MHD_destroy_response (response);
     500           0 :     return ret;
     501             :   }
     502             : }
     503             : 
     504             : 
     505             : void
     506           0 : TALER_MHD_get_date_string (struct GNUNET_TIME_Absolute at,
     507             :                            char date[128])
     508             : {
     509             :   static const char *const days[] =
     510             :   { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
     511             :   static const char *const mons[] =
     512             :   { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct",
     513             :     "Nov", "Dec"};
     514             :   struct tm now;
     515             :   time_t t;
     516             : #if ! defined(HAVE_C11_GMTIME_S) && ! defined(HAVE_W32_GMTIME_S) && \
     517             :   ! defined(HAVE_GMTIME_R)
     518             :   struct tm*pNow;
     519             : #endif
     520             : 
     521           0 :   date[0] = 0;
     522           0 :   t = (time_t) (at.abs_value_us / 1000LL / 1000LL);
     523             : #if defined(HAVE_C11_GMTIME_S)
     524             :   if (NULL == gmtime_s (&t, &now))
     525             :     return;
     526             : #elif defined(HAVE_W32_GMTIME_S)
     527             :   if (0 != gmtime_s (&now, &t))
     528             :     return;
     529             : #elif defined(HAVE_GMTIME_R)
     530             :   if (NULL == gmtime_r (&t, &now))
     531             :     return;
     532             : #else
     533           0 :   pNow = gmtime (&t);
     534           0 :   if (NULL == pNow)
     535           0 :     return;
     536           0 :   now = *pNow;
     537             : #endif
     538           0 :   sprintf (date,
     539             :            "%3s, %02u %3s %04u %02u:%02u:%02u GMT",
     540           0 :            days[now.tm_wday % 7],
     541           0 :            (unsigned int) now.tm_mday,
     542           0 :            mons[now.tm_mon % 12],
     543           0 :            (unsigned int) (1900 + now.tm_year),
     544           0 :            (unsigned int) now.tm_hour,
     545           0 :            (unsigned int) now.tm_min,
     546           0 :            (unsigned int) now.tm_sec);
     547             : }
     548             : 
     549             : 
     550             : /* end of mhd_responses.c */

Generated by: LCOV version 1.14