LCOV - code coverage report
Current view: top level - lib - merchant_api_common.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 75.1 % 269 202
Test Date: 2025-12-16 19:21:50 Functions: 100.0 % 9 9

            Line data    Source code
       1              : /*
       2              :   This file is part of TALER
       3              :   Copyright (C) 2020-2023 Taler Systems SA
       4              : 
       5              :   TALER is free software; you can redistribute it and/or modify it under the
       6              :   terms of the GNU Lesser General Public License as published by the Free Software
       7              :   Foundation; either version 2.1, 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 Lesser General Public License for more details.
      12              : 
      13              :   You should have received a copy of the GNU Lesser General Public License along with
      14              :   TALER; see the file COPYING.LGPL.  If not, see
      15              :   <http://www.gnu.org/licenses/>
      16              : */
      17              : /**
      18              :  * @file merchant_api_common.c
      19              :  * @brief Implementation of common logic for libtalermerchant
      20              :  * @author Christian Grothoff
      21              :  * @author Priscilla Huang
      22              :  */
      23              : #include "platform.h"
      24              : #include "microhttpd.h"
      25              : #include <curl/curl.h>
      26              : #include "taler_merchant_service.h"
      27              : #include "merchant_api_common.h"
      28              : #include <gnunet/gnunet_uri_lib.h>
      29              : #include <taler/taler_json_lib.h>
      30              : #include "taler_merchant_util.h"
      31              : 
      32              : 
      33              : static void
      34           94 : TALER_MERCHANT_format_fractional_string (uint64_t integer,
      35              :                                          uint32_t fractional,
      36              :                                          char *buffer,
      37              :                                          size_t buffer_length)
      38              : {
      39           94 :   GNUNET_assert (NULL != buffer);
      40           94 :   GNUNET_assert (0 < buffer_length);
      41           94 :   GNUNET_assert (fractional < MERCHANT_UNIT_FRAC_BASE);
      42              : 
      43           94 :   if (0 == fractional)
      44              :   {
      45           78 :     GNUNET_snprintf (buffer,
      46              :                      buffer_length,
      47              :                      "%lu",
      48              :                      integer);
      49           78 :     return;
      50              :   }
      51              :   {
      52              :     char frac_buf[MERCHANT_UNIT_FRAC_MAX_DIGITS + 1];
      53              :     size_t idx;
      54              : 
      55           16 :     GNUNET_snprintf (frac_buf,
      56              :                      sizeof (frac_buf),
      57              :                      "%0*u",
      58              :                      MERCHANT_UNIT_FRAC_MAX_DIGITS,
      59              :                      (unsigned int) fractional);
      60           88 :     for (idx = strlen (frac_buf); idx > 0; idx--)
      61              :     {
      62           88 :       if ('0' != frac_buf[idx - 1])
      63           16 :         break;
      64           72 :       frac_buf[idx - 1] = '\0';
      65              :     }
      66           16 :     if ('\0' == frac_buf[0])
      67            0 :       GNUNET_strlcpy (frac_buf,
      68              :                       "0",
      69              :                       sizeof (frac_buf));
      70           16 :     GNUNET_snprintf (buffer,
      71              :                      buffer_length,
      72              :                      "%lu.%s",
      73              :                      integer,
      74              :                      frac_buf);
      75              :   }
      76              : }
      77              : 
      78              : 
      79              : void
      80           26 : TALER_MERCHANT_format_quantity_string (uint64_t quantity,
      81              :                                        uint32_t quantity_frac,
      82              :                                        char *buffer,
      83              :                                        size_t buffer_length)
      84              : {
      85           26 :   TALER_MERCHANT_format_fractional_string (quantity,
      86              :                                            quantity_frac,
      87              :                                            buffer,
      88              :                                            buffer_length);
      89           26 : }
      90              : 
      91              : 
      92              : void
      93           68 : TALER_MERCHANT_format_stock_string (uint64_t total_stock,
      94              :                                     uint32_t total_stock_frac,
      95              :                                     char *buffer,
      96              :                                     size_t buffer_length)
      97              : {
      98           68 :   if ( (INT64_MAX == (int64_t) total_stock) &&
      99              :        (INT32_MAX == (int32_t) total_stock_frac) )
     100              :   {
     101            0 :     GNUNET_snprintf (buffer,
     102              :                      buffer_length,
     103              :                      "-1");
     104            0 :     return;
     105              :   }
     106           68 :   TALER_MERCHANT_format_fractional_string (total_stock,
     107              :                                            total_stock_frac,
     108              :                                            buffer,
     109              :                                            buffer_length);
     110              : }
     111              : 
     112              : 
     113              : void
     114           10 : TALER_MERCHANT_parse_error_details_ (const json_t *response,
     115              :                                      unsigned int http_status,
     116              :                                      struct TALER_MERCHANT_HttpResponse *hr)
     117              : {
     118              :   const json_t *jc;
     119              : 
     120           10 :   memset (hr, 0, sizeof (*hr));
     121           10 :   hr->reply = response;
     122           10 :   hr->http_status = http_status;
     123           10 :   if (NULL == response)
     124              :   {
     125            0 :     hr->ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     126            0 :     return;
     127              :   }
     128           10 :   hr->ec = TALER_JSON_get_error_code (response);
     129           10 :   hr->hint = TALER_JSON_get_error_hint (response);
     130              : 
     131              :   /* handle 'exchange_http_status' */
     132           10 :   jc = json_object_get (response,
     133              :                         "exchange_http_status");
     134              :   /* The caller already knows that the JSON represents an error,
     135              :      so we are dealing with a missing error code here.  */
     136           10 :   if (NULL == jc)
     137            4 :     return; /* no need to bother with exchange_code/hint if we had no status */
     138            6 :   if (! json_is_integer (jc))
     139              :   {
     140            0 :     GNUNET_break_op (0);
     141            0 :     return;
     142              :   }
     143            6 :   hr->exchange_http_status = (unsigned int) json_integer_value (jc);
     144              : 
     145              :   /* handle 'exchange_reply' */
     146            6 :   jc = json_object_get (response,
     147              :                         "exchange_reply");
     148            6 :   if (! json_is_object (jc))
     149              :   {
     150            0 :     GNUNET_break_op (0);
     151              :   }
     152              :   else
     153              :   {
     154            6 :     hr->exchange_reply = jc;
     155              :   }
     156              : 
     157              :   /* handle 'exchange_code' */
     158            6 :   jc = json_object_get (response,
     159              :                         "exchange_code");
     160              :   /* The caller already knows that the JSON represents an error,
     161              :      so we are dealing with a missing error code here.  */
     162            6 :   if (NULL == jc)
     163            0 :     return; /* no need to bother with exchange-hint if we had no code */
     164            6 :   if (! json_is_integer (jc))
     165              :   {
     166            0 :     GNUNET_break_op (0);
     167            0 :     hr->exchange_code = TALER_EC_INVALID;
     168              :   }
     169              :   else
     170              :   {
     171            6 :     hr->exchange_code = (enum TALER_ErrorCode) (int) json_integer_value (jc);
     172              :   }
     173              : 
     174              :   /* handle 'exchange-hint' */
     175            6 :   jc = json_object_get (response,
     176              :                         "exchange-hint");
     177              :   /* The caller already knows that the JSON represents an error,
     178              :      so we are dealing with a missing error code here.  */
     179            6 :   if (NULL == jc)
     180            6 :     return;
     181            0 :   if (! json_is_string (jc))
     182              :   {
     183            0 :     GNUNET_break_op (0);
     184              :   }
     185              :   else
     186              :   {
     187            0 :     hr->exchange_hint = json_string_value (jc);
     188              :   }
     189              : }
     190              : 
     191              : 
     192              : enum GNUNET_GenericReturnValue
     193           20 : TALER_MERCHANT_parse_pay_uri (const char *pay_uri,
     194              :                               struct TALER_MERCHANT_PayUriData *parse_data)
     195              : {
     196           20 :   char *cp = GNUNET_strdup (pay_uri);
     197              :   struct GNUNET_Uri u;
     198              : 
     199           20 :   if (0 !=
     200           20 :       GNUNET_uri_parse (&u,
     201              :                         cp))
     202              :   {
     203            0 :     GNUNET_free (cp);
     204            0 :     GNUNET_break_op (0);
     205            0 :     return GNUNET_SYSERR;
     206              :   }
     207           20 :   if ((0 != strcasecmp ("taler",
     208           20 :                         u.scheme)) &&
     209           16 :       (0 != strcasecmp ("taler+http",
     210           16 :                         u.scheme)))
     211              :   {
     212            1 :     fprintf (stderr,
     213              :              "Bad schema %s\n",
     214              :              u.scheme);
     215            1 :     GNUNET_free (cp);
     216            1 :     GNUNET_break_op (0);
     217            1 :     return GNUNET_SYSERR;
     218              :   }
     219           19 :   parse_data->use_http = (0 == strcasecmp ("taler+http",
     220           19 :                                            u.scheme));
     221           19 :   if (0 != strcasecmp ("pay",
     222           19 :                        u.host))
     223              :   {
     224            1 :     GNUNET_break_op (0);
     225            1 :     GNUNET_free (cp);
     226            1 :     return GNUNET_SYSERR;
     227              :   }
     228              : 
     229              :   {
     230              :     char *order_id;
     231              :     char *mpp;
     232           18 :     char *session_id = strrchr (u.path,
     233              :                                 '/');
     234           18 :     struct TALER_ClaimTokenP *claim_token = NULL;
     235              : 
     236           18 :     if (NULL == session_id)
     237              :     {
     238            0 :       GNUNET_break_op (0);
     239            0 :       GNUNET_free (cp);
     240            0 :       return GNUNET_SYSERR;
     241              :     }
     242           18 :     *session_id = '\0';
     243           18 :     ++session_id;
     244              : 
     245           18 :     order_id = strrchr (u.path,
     246              :                         '/');
     247           18 :     if (NULL == order_id)
     248              :     {
     249            1 :       GNUNET_break_op (0);
     250            1 :       GNUNET_free (cp);
     251            1 :       return GNUNET_SYSERR;
     252              :     }
     253           17 :     *order_id = '\0';
     254           17 :     ++order_id;
     255           17 :     mpp = strchr (u.path,
     256              :                   '/');
     257           17 :     if (NULL != mpp)
     258              :     {
     259            1 :       *mpp = '\0';
     260            1 :       ++mpp;
     261              :     }
     262              : 
     263              :     {
     264           17 :       char *ct_str = u.query;
     265              :       char *ct_data;
     266              : 
     267           17 :       if (NULL != ct_str)
     268              :       {
     269            7 :         ct_data = strchr (u.query,
     270              :                           '=');
     271            7 :         if (NULL == ct_data)
     272              :         {
     273            0 :           GNUNET_break_op (0);
     274            0 :           GNUNET_free (cp);
     275            0 :           return GNUNET_SYSERR;
     276              :         }
     277            7 :         *ct_data = '\0';
     278            7 :         ++ct_data;
     279            7 :         claim_token = GNUNET_new (struct TALER_ClaimTokenP);
     280            7 :         if ( (0 != strcmp ("c",
     281           14 :                            u.query)) ||
     282              :              (GNUNET_OK !=
     283            7 :               GNUNET_STRINGS_string_to_data (ct_data,
     284              :                                              strlen (ct_data),
     285              :                                              claim_token,
     286              :                                              sizeof (*claim_token))) )
     287              :         {
     288            0 :           GNUNET_break_op (0);
     289            0 :           GNUNET_free (claim_token);
     290            0 :           GNUNET_free (cp);
     291            0 :           return GNUNET_SYSERR;
     292              :         }
     293              :       }
     294              :     }
     295              : 
     296              :     parse_data->merchant_prefix_path
     297           17 :       = (NULL == mpp)
     298              :         ? NULL
     299           17 :         : GNUNET_strdup (mpp);
     300           17 :     parse_data->merchant_host = GNUNET_strdup (u.path);
     301           17 :     parse_data->order_id = GNUNET_strdup (order_id);
     302              :     parse_data->session_id
     303           34 :       = (0 < strlen (session_id))
     304           10 :         ? GNUNET_strdup (session_id)
     305           17 :         : NULL;
     306           17 :     parse_data->claim_token = claim_token;
     307              :     parse_data->ssid
     308           34 :       = (NULL == u.fragment)
     309              :         ? NULL
     310           17 :         : GNUNET_strdup (u.fragment);
     311              :   }
     312           17 :   GNUNET_free (cp);
     313           17 :   return GNUNET_OK;
     314              : }
     315              : 
     316              : 
     317              : void
     318           17 : TALER_MERCHANT_parse_pay_uri_free (
     319              :   struct TALER_MERCHANT_PayUriData *parse_data)
     320              : {
     321           17 :   GNUNET_free (parse_data->merchant_host);
     322           17 :   GNUNET_free (parse_data->merchant_prefix_path);
     323           17 :   GNUNET_free (parse_data->order_id);
     324           17 :   GNUNET_free (parse_data->session_id);
     325           17 :   GNUNET_free (parse_data->claim_token);
     326           17 :   GNUNET_free (parse_data->ssid);
     327           17 : }
     328              : 
     329              : 
     330              : enum GNUNET_GenericReturnValue
     331           11 : TALER_MERCHANT_parse_refund_uri (
     332              :   const char *refund_uri,
     333              :   struct TALER_MERCHANT_RefundUriData *parse_data)
     334              : {
     335           11 :   char *cp = GNUNET_strdup (refund_uri);
     336              :   struct GNUNET_Uri u;
     337              : 
     338           11 :   if (0 !=
     339           11 :       GNUNET_uri_parse (&u,
     340              :                         cp))
     341              :   {
     342            0 :     GNUNET_free (cp);
     343            0 :     GNUNET_break_op (0);
     344            0 :     return GNUNET_SYSERR;
     345              :   }
     346           11 :   if ((0 != strcasecmp ("taler",
     347           11 :                         u.scheme)) &&
     348            8 :       (0 != strcasecmp ("taler+http",
     349            8 :                         u.scheme)))
     350              :   {
     351            1 :     GNUNET_free (cp);
     352            1 :     GNUNET_break_op (0);
     353            1 :     return GNUNET_SYSERR;
     354              :   }
     355           10 :   parse_data->use_http = (0 == strcasecmp ("taler+http",
     356           10 :                                            u.scheme));
     357              : 
     358           10 :   if (0 != strcasecmp ("refund",
     359           10 :                        u.host))
     360              :   {
     361            1 :     GNUNET_break_op (0);
     362            1 :     GNUNET_free (cp);
     363            1 :     return GNUNET_SYSERR;
     364              :   }
     365              : 
     366              :   {
     367              :     char *order_id;
     368            9 :     char *last_seg = strrchr (u.path,
     369              :                               '/');
     370              : 
     371            9 :     if (NULL == last_seg)
     372              :     {
     373            0 :       GNUNET_break_op (0);
     374            0 :       GNUNET_free (cp);
     375            0 :       return GNUNET_SYSERR;
     376              :     }
     377            9 :     *last_seg = '\0';
     378            9 :     ++last_seg;
     379              : 
     380            9 :     order_id = strrchr (u.path,
     381              :                         '/');
     382            9 :     if (NULL == order_id)
     383              :     {
     384            1 :       GNUNET_break_op (0);
     385            1 :       GNUNET_free (cp);
     386            1 :       return GNUNET_SYSERR;
     387              :     }
     388            8 :     *order_id = '\0';
     389            8 :     ++order_id;
     390            8 :     if (0 != strlen (last_seg))
     391              :     {
     392            0 :       GNUNET_break_op (0);
     393            0 :       GNUNET_free (cp);
     394            0 :       return GNUNET_SYSERR;
     395              :     }
     396              : 
     397              :     {
     398              :       char *mpp;
     399              : 
     400            8 :       mpp = strchr (u.path,
     401              :                     '/');
     402            8 :       if (NULL != mpp)
     403              :       {
     404            1 :         *mpp = '\0';
     405            1 :         ++mpp;
     406              :       }
     407              : 
     408              :       parse_data->merchant_prefix_path
     409            8 :         = (NULL == mpp)
     410              :           ? NULL
     411            8 :           : GNUNET_strdup (mpp);
     412              :     }
     413            8 :     parse_data->merchant_host = GNUNET_strdup (u.path);
     414            8 :     parse_data->order_id = GNUNET_strdup (order_id);
     415              :     parse_data->ssid
     416           16 :       = (NULL == u.fragment)
     417              :         ? NULL
     418            8 :         : GNUNET_strdup (u.fragment);
     419              :   }
     420            8 :   GNUNET_free (cp);
     421            8 :   return GNUNET_OK;
     422              : }
     423              : 
     424              : 
     425              : void
     426            8 : TALER_MERCHANT_parse_refund_uri_free (
     427              :   struct TALER_MERCHANT_RefundUriData *parse_data)
     428              : {
     429            8 :   GNUNET_free (parse_data->merchant_host);
     430            8 :   GNUNET_free (parse_data->merchant_prefix_path);
     431            8 :   GNUNET_free (parse_data->order_id);
     432            8 :   GNUNET_free (parse_data->ssid);
     433            8 : }
     434              : 
     435              : 
     436              : void
     437           69 : TALER_MERCHANT_handle_order_creation_response_ (
     438              :   TALER_MERCHANT_PostOrdersCallback cb,
     439              :   void *cb_cls,
     440              :   long response_code,
     441              :   const json_t *json)
     442              : {
     443           69 :   struct TALER_MERCHANT_PostOrdersReply por = {
     444           69 :     .hr.http_status = (unsigned int) response_code,
     445              :     .hr.reply = json
     446              :   };
     447              :   struct TALER_ClaimTokenP token;
     448              : 
     449           69 :   switch (response_code)
     450              :   {
     451            0 :   case 0:
     452            0 :     por.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     453            0 :     break;
     454           47 :   case MHD_HTTP_OK:
     455              :     {
     456              :       bool no_token;
     457              :       bool no_pay_deadline;
     458              :       struct GNUNET_JSON_Specification spec[] = {
     459           47 :         GNUNET_JSON_spec_string ("order_id",
     460              :                                  &por.details.ok.order_id),
     461           47 :         GNUNET_JSON_spec_mark_optional (
     462              :           GNUNET_JSON_spec_fixed_auto ("token",
     463              :                                        &token),
     464              :           &no_token),
     465           47 :         GNUNET_JSON_spec_mark_optional (
     466              :           /* optional for pre-v21 compatibility */
     467              :           GNUNET_JSON_spec_timestamp ("pay_deadline",
     468              :                                       &por.details.ok.pay_deadline),
     469              :           &no_pay_deadline),
     470           47 :         GNUNET_JSON_spec_end ()
     471              :       };
     472              : 
     473           47 :       if (GNUNET_OK !=
     474           47 :           GNUNET_JSON_parse (json,
     475              :                              spec,
     476              :                              NULL, NULL))
     477              :       {
     478            0 :         GNUNET_break_op (0);
     479            0 :         por.hr.http_status = 0;
     480            0 :         por.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
     481              :       }
     482              :       else
     483              :       {
     484           47 :         if (! no_token)
     485           43 :           por.details.ok.token = &token;
     486           47 :         if (no_pay_deadline)
     487              :         {
     488           45 :           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     489              :                       "Talking to outdated merchant backend, payment deadline unavailable.\n");
     490           45 :           por.details.ok.pay_deadline = GNUNET_TIME_UNIT_ZERO_TS;
     491              :         }
     492              :       }
     493              :     }
     494           47 :     break;
     495            2 :   case MHD_HTTP_BAD_REQUEST:
     496            2 :     json_dumpf (json,
     497              :                 stderr,
     498              :                 JSON_INDENT (2));
     499            2 :     por.hr.ec = TALER_JSON_get_error_code (json);
     500            2 :     por.hr.hint = TALER_JSON_get_error_hint (json);
     501              :     /* This should never happen, either us or
     502              :        the merchant is buggy (or API version conflict);
     503              :        just pass JSON reply to the application */
     504            2 :     break;
     505            0 :   case MHD_HTTP_UNAUTHORIZED:
     506            0 :     por.hr.ec = TALER_JSON_get_error_code (json);
     507            0 :     por.hr.hint = TALER_JSON_get_error_hint (json);
     508              :     /* Nothing really to verify, merchant says we need to authenticate. */
     509            0 :     break;
     510            0 :   case MHD_HTTP_FORBIDDEN:
     511              :     /* Nothing really to verify, merchant says one
     512              :        of the signatures is invalid; as we checked them,
     513              :        this should never happen, we should pass the JSON
     514              :        reply to the application */
     515            0 :     por.hr.ec = TALER_JSON_get_error_code (json);
     516            0 :     por.hr.hint = TALER_JSON_get_error_hint (json);
     517            0 :     break;
     518            6 :   case MHD_HTTP_NOT_FOUND:
     519              :     /* Nothing really to verify, this should never
     520              :        happen, we should pass the JSON reply to the application */
     521            6 :     por.hr.ec = TALER_JSON_get_error_code (json);
     522            6 :     por.hr.hint = TALER_JSON_get_error_hint (json);
     523            6 :     break;
     524           12 :   case MHD_HTTP_CONFLICT:
     525           12 :     por.hr.ec = TALER_JSON_get_error_code (json);
     526           12 :     por.hr.hint = TALER_JSON_get_error_hint (json);
     527           12 :     break;
     528            2 :   case MHD_HTTP_GONE:
     529              :     /* The quantity of some product requested was not available. */
     530              :     {
     531              :       bool rq_frac_missing;
     532              :       bool aq_frac_missing;
     533              : 
     534              :       struct GNUNET_JSON_Specification spec[] = {
     535            2 :         GNUNET_JSON_spec_string (
     536              :           "product_id",
     537              :           &por.details.gone.product_id),
     538            2 :         GNUNET_JSON_spec_uint64 (
     539              :           "requested_quantity",
     540              :           &por.details.gone.requested_quantity),
     541            2 :         GNUNET_JSON_spec_mark_optional (
     542              :           GNUNET_JSON_spec_uint32 (
     543              :             "requested_quantity_frac",
     544              :             &por.details.gone.requested_quantity_frac),
     545              :           &rq_frac_missing),
     546            2 :         GNUNET_JSON_spec_uint64 (
     547              :           "available_quantity",
     548              :           &por.details.gone.available_quantity),
     549            2 :         GNUNET_JSON_spec_mark_optional (
     550              :           GNUNET_JSON_spec_uint32 (
     551              :             "available_quantity_frac",
     552              :             &por.details.gone.available_quantity_frac),
     553              :           &aq_frac_missing),
     554            2 :         GNUNET_JSON_spec_mark_optional (
     555              :           GNUNET_JSON_spec_timestamp (
     556              :             "restock_expected",
     557              :             &por.details.gone.restock_expected),
     558              :           NULL),
     559            2 :         GNUNET_JSON_spec_end ()
     560              :       };
     561              : 
     562            2 :       if (GNUNET_OK !=
     563            2 :           GNUNET_JSON_parse (json,
     564              :                              spec,
     565              :                              NULL, NULL))
     566              :       {
     567            0 :         GNUNET_break_op (0);
     568            0 :         por.hr.http_status = 0;
     569            0 :         por.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
     570              :       }
     571              :       else
     572              :       {
     573            2 :         if (rq_frac_missing)
     574            2 :           por.details.gone.requested_quantity_frac = 0;
     575            2 :         if (aq_frac_missing)
     576            2 :           por.details.gone.available_quantity_frac = 0;
     577              :       }
     578            2 :       break;
     579              :     }
     580            0 :   case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
     581              :     /* Order total too high for legal reasons */
     582            0 :     por.hr.ec = TALER_JSON_get_error_code (json);
     583            0 :     por.hr.hint = TALER_JSON_get_error_hint (json);
     584            0 :     break;
     585            0 :   case MHD_HTTP_INTERNAL_SERVER_ERROR:
     586              :     /* Server had an internal issue; we should retry,
     587              :        but this API leaves this to the application */
     588            0 :     por.hr.ec = TALER_JSON_get_error_code (json);
     589            0 :     por.hr.hint = TALER_JSON_get_error_hint (json);
     590            0 :     break;
     591            0 :   default:
     592              :     /* unexpected response code */
     593            0 :     por.hr.ec = TALER_JSON_get_error_code (json);
     594            0 :     por.hr.hint = TALER_JSON_get_error_hint (json);
     595            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     596              :                 "Unexpected response code %u/%d\n",
     597              :                 (unsigned int) response_code,
     598              :                 (int) por.hr.ec);
     599            0 :     GNUNET_break_op (0);
     600            0 :     break;
     601              :   } // end of switch
     602           69 :   cb (cb_cls,
     603              :       &por);
     604           69 : }
        

Generated by: LCOV version 2.0-1