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 (13565B)


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