exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

mhd2_responses.c (13731B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2014-2025 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 mhd2_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/taler_util.h"
     26 #include "taler/taler_mhd2_lib.h"
     27 
     28 
     29 /**
     30  * Global options for response generation.
     31  */
     32 static enum TALER_MHD2_GlobalOptions TM_go;
     33 
     34 
     35 void
     36 TALER_MHD2_setup (enum TALER_MHD2_GlobalOptions go)
     37 {
     38   TM_go = go;
     39 }
     40 
     41 
     42 void
     43 TALER_MHD2_add_global_headers (struct MHD_Response *response,
     44                                bool allow_store)
     45 {
     46   if (0 != (TM_go & TALER_MHD2_GO_FORCE_CONNECTION_CLOSE))
     47     GNUNET_break (MHD_SC_OK ==
     48                   MHD_response_add_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   GNUNET_break (MHD_SC_OK ==
     54                 MHD_response_add_header (response,
     55                                          MHD_HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
     56                                          "*"));
     57   GNUNET_break (MHD_SC_OK ==
     58                 MHD_response_add_header (response,
     59                                          /* Not available as MHD constant yet */
     60                                          "Access-Control-Expose-Headers",
     61                                          "*"));
     62   if (! allow_store)
     63     GNUNET_break (MHD_SC_OK ==
     64                   MHD_response_add_header (response,
     65                                            MHD_HTTP_HEADER_CACHE_CONTROL,
     66                                            "no-store"));
     67 }
     68 
     69 
     70 bool
     71 TALER_MHD2_can_compress (struct MHD_Request *request)
     72 {
     73   struct MHD_StringNullable aeb;
     74   const char *ae;
     75   const char *de;
     76 
     77   if (0 != (TM_go & TALER_MHD2_GO_DISABLE_COMPRESSION))
     78     return MHD_NO;
     79   if ( (MHD_NO ==
     80         MHD_request_get_value (request,
     81                                MHD_VK_HEADER,
     82                                MHD_HTTP_HEADER_ACCEPT_ENCODING,
     83                                &aeb)) ||
     84        (NULL == aeb.cstr) )
     85     return false;
     86   ae = aeb.cstr;
     87   if (0 == strcmp (ae,
     88                    "*"))
     89     return true;
     90   de = strstr (ae,
     91                "deflate");
     92   if (NULL == de)
     93     return false;
     94   if ( ( (de == ae) ||
     95          (de[-1] == ',') ||
     96          (de[-1] == ' ') ) &&
     97        ( (de[strlen ("deflate")] == '\0') ||
     98          (de[strlen ("deflate")] == ',') ||
     99          (de[strlen ("deflate")] == ';') ) )
    100     return true;
    101   return false;
    102 }
    103 
    104 
    105 bool
    106 TALER_MHD2_body_compress (void **buf,
    107                           size_t *buf_size)
    108 {
    109   Bytef *cbuf;
    110   uLongf cbuf_size;
    111   int ret;
    112 
    113   cbuf_size = compressBound (*buf_size);
    114   cbuf = malloc (cbuf_size);
    115   if (NULL == cbuf)
    116     return false;
    117   ret = compress (cbuf,
    118                   &cbuf_size,
    119                   (const Bytef *) *buf,
    120                   *buf_size);
    121   if ( (Z_OK != ret) ||
    122        (cbuf_size >= *buf_size) )
    123   {
    124     /* compression failed */
    125     free (cbuf);
    126     return false;
    127   }
    128   free (*buf);
    129   *buf = (void *) cbuf;
    130   *buf_size = (size_t) cbuf_size;
    131   return true;
    132 }
    133 
    134 
    135 struct MHD_Response *
    136 TALER_MHD2_make_json (enum MHD_HTTP_StatusCode sc,
    137                       const json_t *json)
    138 {
    139   struct MHD_Response *response;
    140   char *json_str;
    141 
    142   json_str = json_dumps (json,
    143                          JSON_INDENT (2));
    144   if (NULL == json_str)
    145   {
    146     GNUNET_break (0);
    147     return NULL;
    148   }
    149   response = MHD_response_from_buffer (sc,
    150                                        strlen (json_str),
    151                                        json_str,
    152                                        &free,
    153                                        json_str);
    154   if (NULL == response)
    155   {
    156     free (json_str);
    157     GNUNET_break (0);
    158     return NULL;
    159   }
    160   TALER_MHD2_add_global_headers (response,
    161                                  false);
    162   GNUNET_break (MHD_SC_OK ==
    163                 MHD_response_add_header (response,
    164                                          MHD_HTTP_HEADER_CONTENT_TYPE,
    165                                          "application/json"));
    166   return response;
    167 }
    168 
    169 
    170 struct MHD_Response *
    171 TALER_MHD2_make_json_steal (enum MHD_HTTP_StatusCode sc,
    172                             json_t *json)
    173 {
    174   struct MHD_Response *res;
    175 
    176   res = TALER_MHD2_make_json (sc,
    177                               json);
    178   json_decref (json);
    179   return res;
    180 }
    181 
    182 
    183 const struct MHD_Action *
    184 TALER_MHD2_reply_json (struct MHD_Request *request,
    185                        const json_t *json,
    186                        enum MHD_HTTP_StatusCode sc)
    187 {
    188   struct MHD_Response *response;
    189   void *json_str;
    190   size_t json_len;
    191   bool is_compressed;
    192 
    193   json_str = json_dumps (json,
    194                          JSON_INDENT (2));
    195   if (NULL == json_str)
    196   {
    197     /**
    198      * This log helps to figure out which
    199      * function called this one and assert-failed.
    200      */
    201     TALER_LOG_ERROR ("Aborting json-packing for HTTP code: %u\n",
    202                      sc);
    203 
    204     GNUNET_assert (0);
    205     return MHD_action_abort_request (request);
    206   }
    207   json_len = strlen (json_str);
    208   /* try to compress the body */
    209   is_compressed = MHD_NO;
    210   if (TALER_MHD2_can_compress (request))
    211     is_compressed = TALER_MHD2_body_compress (&json_str,
    212                                               &json_len);
    213   response = MHD_response_from_buffer (sc,
    214                                        json_len,
    215                                        json_str,
    216                                        &free,
    217                                        json_str);
    218   if (NULL == response)
    219   {
    220     free (json_str);
    221     GNUNET_break (0);
    222     return MHD_action_abort_request (request);
    223   }
    224   TALER_MHD2_add_global_headers (response,
    225                                  false);
    226   GNUNET_break (MHD_SC_OK ==
    227                 MHD_response_add_header (response,
    228                                          MHD_HTTP_HEADER_CONTENT_TYPE,
    229                                          "application/json"));
    230   if (is_compressed)
    231   {
    232     /* Need to indicate to client that body is compressed */
    233     if (MHD_SC_OK !=
    234         MHD_response_add_header (response,
    235                                  MHD_HTTP_HEADER_CONTENT_ENCODING,
    236                                  "deflate"))
    237     {
    238       GNUNET_break (0);
    239       MHD_response_destroy (response);
    240       return MHD_action_abort_request (request);
    241     }
    242   }
    243 
    244   return MHD_action_from_response (request,
    245                                    response);
    246 }
    247 
    248 
    249 const struct MHD_Action *
    250 TALER_MHD2_reply_json_steal (struct MHD_Request *request,
    251                              json_t *json,
    252                              enum MHD_HTTP_StatusCode sc)
    253 {
    254   const struct MHD_Action *ret;
    255 
    256   ret = TALER_MHD2_reply_json (request,
    257                                json,
    258                                sc);
    259   json_decref (json);
    260   return ret;
    261 }
    262 
    263 
    264 const struct MHD_Action *
    265 TALER_MHD2_reply_cors_preflight (struct MHD_Request *request)
    266 {
    267   struct MHD_Response *response;
    268 
    269   response = MHD_response_from_empty (MHD_HTTP_STATUS_NO_CONTENT);
    270   if (NULL == response)
    271     return MHD_action_abort_request (request);
    272   /* This adds the Access-Control-Allow-Origin header.
    273    * All endpoints of the exchange allow CORS. */
    274   TALER_MHD2_add_global_headers (response,
    275                                  true);
    276   GNUNET_break (MHD_SC_OK ==
    277                 MHD_response_add_header (response,
    278                                          /* Not available as MHD constant yet */
    279                                          "Access-Control-Allow-Headers",
    280                                          "*"));
    281   GNUNET_break (MHD_SC_OK ==
    282                 MHD_response_add_header (response,
    283                                          /* Not available as MHD constant yet */
    284                                          "Access-Control-Allow-Methods",
    285                                          "*"));
    286   return MHD_action_from_response (request,
    287                                    response);
    288 }
    289 
    290 
    291 struct MHD_Response *
    292 TALER_MHD2_make_error (enum TALER_ErrorCode ec,
    293                        const char *detail)
    294 {
    295   return TALER_MHD2_MAKE_JSON_PACK (
    296     TALER_ErrorCode_get_http_status (ec),
    297     TALER_MHD2_PACK_EC (ec),
    298     GNUNET_JSON_pack_allow_null (
    299       GNUNET_JSON_pack_string ("detail", detail)));
    300 }
    301 
    302 
    303 const struct MHD_Action *
    304 TALER_MHD2_reply_with_error (struct MHD_Request *request,
    305                              enum MHD_HTTP_StatusCode sc,
    306                              enum TALER_ErrorCode ec,
    307                              const char *detail)
    308 {
    309   return TALER_MHD2_REPLY_JSON_PACK (
    310     request,
    311     sc,
    312     TALER_MHD2_PACK_EC (ec),
    313     GNUNET_JSON_pack_allow_null (
    314       GNUNET_JSON_pack_string ("detail", detail)));
    315 }
    316 
    317 
    318 const struct MHD_Action *
    319 TALER_MHD2_reply_with_ec (struct MHD_Request *request,
    320                           enum TALER_ErrorCode ec,
    321                           const char *detail)
    322 {
    323   unsigned int hc
    324     = TALER_ErrorCode_get_http_status (ec);
    325 
    326   if ( (0 == hc) ||
    327        (UINT_MAX == hc) )
    328   {
    329     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    330                 "Invalid Taler error code %u provided for response!\n",
    331                 (unsigned int) ec);
    332     hc = MHD_HTTP_STATUS_INTERNAL_SERVER_ERROR;
    333   }
    334   return TALER_MHD2_reply_with_error (request,
    335                                       (enum MHD_HTTP_StatusCode) hc,
    336                                       ec,
    337                                       detail);
    338 }
    339 
    340 
    341 const struct MHD_Action *
    342 TALER_MHD2_reply_request_too_large (struct MHD_Request *request)
    343 {
    344   return TALER_MHD2_reply_with_error (
    345     request,
    346     MHD_HTTP_STATUS_CONTENT_TOO_LARGE,
    347     TALER_EC_GENERIC_UPLOAD_EXCEEDS_LIMIT,
    348     NULL);
    349 }
    350 
    351 
    352 const struct MHD_Action *
    353 TALER_MHD2_reply_agpl (struct MHD_Request *request,
    354                        const char *url)
    355 {
    356   const char *agpl =
    357     "This server is licensed under the Affero GPL. You will now be redirected to the source code.";
    358   struct MHD_Response *response;
    359 
    360   response = MHD_response_from_buffer_static (MHD_HTTP_STATUS_FOUND,
    361                                               strlen (agpl),
    362                                               (void *) agpl);
    363   if (NULL == response)
    364   {
    365     GNUNET_break (0);
    366     return MHD_action_abort_request (request);
    367   }
    368   TALER_MHD2_add_global_headers (response,
    369                                  true);
    370   GNUNET_break (MHD_SC_OK ==
    371                 MHD_response_add_header (response,
    372                                          MHD_HTTP_HEADER_CONTENT_TYPE,
    373                                          "text/plain"));
    374   if (MHD_SC_OK ==
    375       MHD_response_add_header (response,
    376                                MHD_HTTP_HEADER_LOCATION,
    377                                url))
    378   {
    379     GNUNET_break (0);
    380     MHD_response_destroy (response);
    381     return MHD_action_abort_request (request);
    382   }
    383   return MHD_action_from_response (request,
    384                                    response);
    385 }
    386 
    387 
    388 const struct MHD_Action *
    389 TALER_MHD2_reply_static (struct MHD_Request *request,
    390                          enum MHD_HTTP_StatusCode sc,
    391                          const char *mime_type,
    392                          const char *body,
    393                          size_t body_size)
    394 {
    395   struct MHD_Response *response;
    396 
    397   response = MHD_response_from_buffer_static (sc,
    398                                               body_size,
    399                                               body);
    400   if (NULL == response)
    401   {
    402     GNUNET_break (0);
    403     return MHD_action_abort_request (request);
    404   }
    405   TALER_MHD2_add_global_headers (response,
    406                                  true);
    407   if (NULL != mime_type)
    408     GNUNET_break (MHD_SC_OK ==
    409                   MHD_response_add_header (response,
    410                                            MHD_HTTP_HEADER_CONTENT_TYPE,
    411                                            mime_type));
    412   return MHD_action_from_response (request,
    413                                    response);
    414 }
    415 
    416 
    417 void
    418 TALER_MHD2_get_date_string (struct GNUNET_TIME_Absolute at,
    419                             char date[128])
    420 {
    421   static const char *const days[] =
    422   { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
    423   static const char *const mons[] =
    424   { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct",
    425     "Nov", "Dec"};
    426   struct tm now;
    427   time_t t;
    428 #if ! defined(HAVE_C11_GMTIME_S) && ! defined(HAVE_W32_GMTIME_S) && \
    429   ! defined(HAVE_GMTIME_R)
    430   struct tm*pNow;
    431 #endif
    432 
    433   date[0] = 0;
    434   t = (time_t) (at.abs_value_us / 1000LL / 1000LL);
    435 #if defined(HAVE_C11_GMTIME_S)
    436   if (NULL == gmtime_s (&t, &now))
    437     return;
    438 #elif defined(HAVE_W32_GMTIME_S)
    439   if (0 != gmtime_s (&now, &t))
    440     return;
    441 #elif defined(HAVE_GMTIME_R)
    442   if (NULL == gmtime_r (&t, &now))
    443     return;
    444 #else
    445   pNow = gmtime (&t);
    446   if (NULL == pNow)
    447     return;
    448   now = *pNow;
    449 #endif
    450   sprintf (date,
    451            "%3s, %02u %3s %04u %02u:%02u:%02u GMT",
    452            days[now.tm_wday % 7],
    453            (unsigned int) now.tm_mday,
    454            mons[now.tm_mon % 12],
    455            (unsigned int) (1900 + now.tm_year),
    456            (unsigned int) now.tm_hour,
    457            (unsigned int) now.tm_min,
    458            (unsigned int) now.tm_sec);
    459 }
    460 
    461 
    462 /* end of mhd_responses.c */