exchange

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

mhd_responses.c (14385B)


      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 TALER_MHD_setup (enum TALER_MHD_GlobalOptions go)
     37 {
     38   TM_go = go;
     39 }
     40 
     41 
     42 void
     43 TALER_MHD_add_global_headers (struct MHD_Response *response,
     44                               bool allow_store)
     45 {
     46   if (0 != (TM_go & TALER_MHD_GO_FORCE_CONNECTION_CLOSE))
     47     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   GNUNET_break (MHD_YES ==
     54                 MHD_add_response_header (response,
     55                                          MHD_HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
     56                                          "*"));
     57   GNUNET_break (MHD_YES ==
     58                 MHD_add_response_header (response,
     59                                          /* Not available as MHD constant yet */
     60                                          "Access-Control-Expose-Headers",
     61                                          "*"));
     62   if (! allow_store)
     63     GNUNET_break (MHD_YES ==
     64                   MHD_add_response_header (response,
     65                                            MHD_HTTP_HEADER_CACHE_CONTROL,
     66                                            "no-store"));
     67 }
     68 
     69 
     70 enum TALER_MHD_CompressionType
     71 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   } 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   if (0 != (TM_go & TALER_MHD_GO_DISABLE_COMPRESSION))
     89     return TALER_MHD_CT_NONE;
     90   ae = MHD_lookup_connection_value (connection,
     91                                     MHD_HEADER_KIND,
     92                                     MHD_HTTP_HEADER_ACCEPT_ENCODING);
     93   if (NULL == ae)
     94     return TALER_MHD_CT_NONE;
     95   if (0 == strcmp (ae,
     96                    "*"))
     97     return MHD_YES;
     98   for (unsigned int i=0; NULL != map[i].name; i++)
     99   {
    100     const char *name = map[i].name;
    101 
    102     if (map[i].ct > max)
    103       continue; /* not allowed by client */
    104     de = strstr (ae,
    105                  name);
    106     if (NULL == de)
    107       continue;
    108     if ( ( (de == ae) ||
    109            (de[-1] == ',') ||
    110            (de[-1] == ' ') ) &&
    111          ( (de[strlen (name)] == '\0') ||
    112            (de[strlen (name)] == ',') ||
    113            (de[strlen (name)] == ';') ) )
    114       return map[i].ct;
    115   }
    116   return TALER_MHD_CT_NONE;
    117 }
    118 
    119 
    120 MHD_RESULT
    121 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   cbuf_size = compressBound (*buf_size);
    129   cbuf = malloc (cbuf_size);
    130   if (NULL == cbuf)
    131     return MHD_NO;
    132   ret = compress (cbuf,
    133                   &cbuf_size,
    134                   (const Bytef *) *buf,
    135                   *buf_size);
    136   if ( (Z_OK != ret) ||
    137        (cbuf_size >= *buf_size) )
    138   {
    139     /* compression failed */
    140     free (cbuf);
    141     return MHD_NO;
    142   }
    143   free (*buf);
    144   *buf = (void *) cbuf;
    145   *buf_size = (size_t) cbuf_size;
    146   return MHD_YES;
    147 }
    148 
    149 
    150 struct MHD_Response *
    151 TALER_MHD_make_json (const json_t *json)
    152 {
    153   struct MHD_Response *response;
    154   char *json_str;
    155 
    156   json_str = json_dumps (json,
    157                          JSON_INDENT (2));
    158   if (NULL == json_str)
    159   {
    160     GNUNET_break (0);
    161     return NULL;
    162   }
    163   response = MHD_create_response_from_buffer (strlen (json_str),
    164                                               json_str,
    165                                               MHD_RESPMEM_MUST_FREE);
    166   if (NULL == response)
    167   {
    168     free (json_str);
    169     GNUNET_break (0);
    170     return NULL;
    171   }
    172   TALER_MHD_add_global_headers (response,
    173                                 false);
    174   GNUNET_break (MHD_YES ==
    175                 MHD_add_response_header (response,
    176                                          MHD_HTTP_HEADER_CONTENT_TYPE,
    177                                          "application/json"));
    178   return response;
    179 }
    180 
    181 
    182 struct MHD_Response *
    183 TALER_MHD_make_json_steal (json_t *json)
    184 {
    185   struct MHD_Response *res;
    186 
    187   res = TALER_MHD_make_json (json);
    188   json_decref (json);
    189   return res;
    190 }
    191 
    192 
    193 MHD_RESULT
    194 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   json_str = json_dumps (json,
    204                          JSON_INDENT (2));
    205   if (NULL == json_str)
    206   {
    207     /**
    208      * This log helps to figure out which
    209      * function called this one and assert-failed.
    210      */
    211     TALER_LOG_ERROR ("Aborting json-packing for HTTP code: %u\n",
    212                      response_code);
    213 
    214     GNUNET_assert (0);
    215     return MHD_NO;
    216   }
    217   json_len = strlen (json_str);
    218   /* try to compress the body */
    219   is_compressed = MHD_NO;
    220   if (TALER_MHD_CT_DEFLATE ==
    221       TALER_MHD_can_compress (connection,
    222                               TALER_MHD_CT_DEFLATE))
    223     is_compressed = TALER_MHD_body_compress (&json_str,
    224                                              &json_len);
    225   response = MHD_create_response_from_buffer (json_len,
    226                                               json_str,
    227                                               MHD_RESPMEM_MUST_FREE);
    228   if (NULL == response)
    229   {
    230     free (json_str);
    231     GNUNET_break (0);
    232     return MHD_NO;
    233   }
    234   TALER_MHD_add_global_headers (response,
    235                                 false);
    236   GNUNET_break (MHD_YES ==
    237                 MHD_add_response_header (response,
    238                                          MHD_HTTP_HEADER_CONTENT_TYPE,
    239                                          "application/json"));
    240   if (MHD_YES == is_compressed)
    241   {
    242     /* Need to indicate to client that body is compressed */
    243     if (MHD_NO ==
    244         MHD_add_response_header (response,
    245                                  MHD_HTTP_HEADER_CONTENT_ENCODING,
    246                                  "deflate"))
    247     {
    248       GNUNET_break (0);
    249       MHD_destroy_response (response);
    250       return MHD_NO;
    251     }
    252   }
    253 
    254   {
    255     MHD_RESULT ret;
    256 
    257     ret = MHD_queue_response (connection,
    258                               response_code,
    259                               response);
    260     MHD_destroy_response (response);
    261     return ret;
    262   }
    263 }
    264 
    265 
    266 MHD_RESULT
    267 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   ret = TALER_MHD_reply_json (connection,
    274                               json,
    275                               response_code);
    276   json_decref (json);
    277   return ret;
    278 }
    279 
    280 
    281 MHD_RESULT
    282 TALER_MHD_reply_cors_preflight (struct MHD_Connection *connection)
    283 {
    284   struct MHD_Response *response;
    285 
    286   response = MHD_create_response_from_buffer (0,
    287                                               NULL,
    288                                               MHD_RESPMEM_PERSISTENT);
    289   if (NULL == response)
    290     return MHD_NO;
    291   /* This adds the Access-Control-Allow-Origin header.
    292    * All endpoints of the exchange allow CORS. */
    293   TALER_MHD_add_global_headers (response,
    294                                 true);
    295   GNUNET_break (MHD_YES ==
    296                 MHD_add_response_header (response,
    297                                          /* Not available as MHD constant yet */
    298                                          "Access-Control-Allow-Headers",
    299                                          "*"));
    300   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     ret = MHD_queue_response (connection,
    309                               MHD_HTTP_NO_CONTENT,
    310                               response);
    311     MHD_destroy_response (response);
    312     return ret;
    313   }
    314 }
    315 
    316 
    317 struct MHD_Response *
    318 TALER_MHD_make_error (enum TALER_ErrorCode ec,
    319                       const char *detail)
    320 {
    321   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 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   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 TALER_MHD_reply_with_ec (struct MHD_Connection *connection,
    345                          enum TALER_ErrorCode ec,
    346                          const char *detail)
    347 {
    348   unsigned int hc = TALER_ErrorCode_get_http_status (ec);
    349 
    350   if ( (0 == hc) ||
    351        (UINT_MAX == hc) )
    352   {
    353     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    354                 "Invalid Taler error code %d provided for response!\n",
    355                 (int) ec);
    356     hc = MHD_HTTP_INTERNAL_SERVER_ERROR;
    357   }
    358   return TALER_MHD_reply_with_error (connection,
    359                                      hc,
    360                                      ec,
    361                                      detail);
    362 }
    363 
    364 
    365 MHD_RESULT
    366 TALER_MHD_reply_request_too_large (struct MHD_Connection *connection)
    367 {
    368   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 TALER_MHD_reply_agpl (struct MHD_Connection *connection,
    377                       const char *url)
    378 {
    379   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   response = MHD_create_response_from_buffer (strlen (agpl),
    384                                               (void *) agpl,
    385                                               MHD_RESPMEM_PERSISTENT);
    386   if (NULL == response)
    387   {
    388     GNUNET_break (0);
    389     return MHD_NO;
    390   }
    391   TALER_MHD_add_global_headers (response,
    392                                 true);
    393   GNUNET_break (MHD_YES ==
    394                 MHD_add_response_header (response,
    395                                          MHD_HTTP_HEADER_CONTENT_TYPE,
    396                                          "text/plain"));
    397   if (MHD_NO ==
    398       MHD_add_response_header (response,
    399                                MHD_HTTP_HEADER_LOCATION,
    400                                url))
    401   {
    402     GNUNET_break (0);
    403     MHD_destroy_response (response);
    404     return MHD_NO;
    405   }
    406 
    407   {
    408     MHD_RESULT ret;
    409 
    410     ret = MHD_queue_response (connection,
    411                               MHD_HTTP_FOUND,
    412                               response);
    413     MHD_destroy_response (response);
    414     return ret;
    415   }
    416 }
    417 
    418 
    419 MHD_RESULT
    420 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   response = MHD_create_response_from_buffer (body_size,
    429                                               (void *) body,
    430                                               MHD_RESPMEM_PERSISTENT);
    431   if (NULL == response)
    432   {
    433     GNUNET_break (0);
    434     return MHD_NO;
    435   }
    436   TALER_MHD_add_global_headers (response,
    437                                 true);
    438   if (NULL != mime_type)
    439     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     ret = MHD_queue_response (connection,
    447                               http_status,
    448                               response);
    449     MHD_destroy_response (response);
    450     return ret;
    451   }
    452 }
    453 
    454 
    455 void
    456 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   date[0] = 0;
    472   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   pNow = gmtime (&t);
    484   if (NULL == pNow)
    485     return;
    486   now = *pNow;
    487 #endif
    488   sprintf (date,
    489            "%3s, %02u %3s %04u %02u:%02u:%02u GMT",
    490            days[now.tm_wday % 7],
    491            (unsigned int) now.tm_mday,
    492            mons[now.tm_mon % 12],
    493            (unsigned int) (1900 + now.tm_year),
    494            (unsigned int) now.tm_hour,
    495            (unsigned int) now.tm_min,
    496            (unsigned int) now.tm_sec);
    497 }
    498 
    499 
    500 /* end of mhd_responses.c */