aboutsummaryrefslogtreecommitdiff
path: root/src/authorization/libanastasiseufin/lae_credit.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/authorization/libanastasiseufin/lae_credit.c')
-rw-r--r--src/authorization/libanastasiseufin/lae_credit.c333
1 files changed, 333 insertions, 0 deletions
diff --git a/src/authorization/libanastasiseufin/lae_credit.c b/src/authorization/libanastasiseufin/lae_credit.c
new file mode 100644
index 0000000..b97536b
--- /dev/null
+++ b/src/authorization/libanastasiseufin/lae_credit.c
@@ -0,0 +1,333 @@
1/*
2 This file is part of Anastasis
3 Copyright (C) 2017--2021 Anastasis SARL
4
5 Anastasis is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 3,
8 or (at your option) any later version.
9
10 Anastasis is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public
16 License along with Anastasis; see the file COPYING. If not,
17 see <http://www.gnu.org/licenses/>
18*/
19/**
20 * @file libanastasiseufin/lae_credit.c
21 * @brief Implementation of the /history/incoming
22 * requests of the libeufin's Anastasis facade
23 * @author Christian Grothoff
24 * @author Marcello Stanisci
25 */
26#include "platform.h"
27#include "lae_common.h"
28#include <microhttpd.h> /* just for HTTP status codes */
29
30
31/**
32 * @brief A /history/incoming Handle
33 */
34struct ANASTASIS_EUFIN_CreditHistoryHandle
35{
36
37 /**
38 * The url for this request.
39 */
40 char *request_url;
41
42 /**
43 * Handle for the request.
44 */
45 struct GNUNET_CURL_Job *job;
46
47 /**
48 * Function to call with the result.
49 */
50 ANASTASIS_EUFIN_CreditHistoryCallback hcb;
51
52 /**
53 * Closure for @a cb.
54 */
55 void *hcb_cls;
56};
57
58
59/**
60 * Parse history given in JSON format and invoke the callback on each item.
61 *
62 * @param hh handle to the account history request
63 * @param history JSON array with the history
64 * @return #GNUNET_OK if history was valid and @a rhistory and @a balance
65 * were set,
66 * #GNUNET_SYSERR if there was a protocol violation in @a history
67 */
68static int
69parse_account_history (struct ANASTASIS_EUFIN_CreditHistoryHandle *hh,
70 const json_t *history)
71{
72 json_t *history_array;
73
74 if (NULL == (history_array = json_object_get (history,
75 "incoming_transactions")))
76 {
77 GNUNET_break_op (0);
78 return GNUNET_SYSERR;
79 }
80 if (! json_is_array (history_array))
81 {
82 GNUNET_break_op (0);
83 return GNUNET_SYSERR;
84 }
85 for (unsigned int i = 0; i<json_array_size (history_array); i++)
86 {
87 struct ANASTASIS_EUFIN_CreditDetails td;
88 uint64_t row_id;
89 struct GNUNET_JSON_Specification hist_spec[] = {
90 TALER_JSON_spec_amount_any ("amount",
91 &td.amount),
92 TALER_JSON_spec_absolute_time ("date",
93 &td.execution_date),
94 GNUNET_JSON_spec_uint64 ("row_id",
95 &row_id),
96 GNUNET_JSON_spec_string ("subject",
97 &td.wire_subject),
98 GNUNET_JSON_spec_string ("debit_account",
99 &td.debit_account_uri),
100 GNUNET_JSON_spec_string ("credit_account",
101 &td.credit_account_uri),
102 GNUNET_JSON_spec_end ()
103 };
104 json_t *transaction = json_array_get (history_array,
105 i);
106
107 if (GNUNET_OK !=
108 GNUNET_JSON_parse (transaction,
109 hist_spec,
110 NULL, NULL))
111 {
112 GNUNET_break_op (0);
113 return GNUNET_SYSERR;
114 }
115 if (GNUNET_OK !=
116 hh->hcb (hh->hcb_cls,
117 MHD_HTTP_OK,
118 TALER_EC_NONE,
119 row_id,
120 &td,
121 transaction))
122 {
123 hh->hcb = NULL;
124 GNUNET_JSON_parse_free (hist_spec);
125 return GNUNET_OK;
126 }
127 GNUNET_JSON_parse_free (hist_spec);
128 }
129 return GNUNET_OK;
130}
131
132
133/**
134 * Function called when we're done processing the
135 * HTTP /history/incoming request.
136 *
137 * @param cls the `struct ANASTASIS_EUFIN_CreditHistoryHandle`
138 * @param response_code HTTP response code, 0 on error
139 * @param response parsed JSON result, NULL on error
140 */
141static void
142handle_credit_history_finished (void *cls,
143 long response_code,
144 const void *response)
145{
146 struct ANASTASIS_EUFIN_CreditHistoryHandle *hh = cls;
147 const json_t *j = response;
148 enum TALER_ErrorCode ec;
149
150 hh->job = NULL;
151 switch (response_code)
152 {
153 case 0:
154 ec = TALER_EC_GENERIC_INVALID_RESPONSE;
155 break;
156 case MHD_HTTP_OK:
157 if (GNUNET_OK !=
158 parse_account_history (hh,
159 j))
160 {
161 GNUNET_break_op (0);
162 response_code = 0;
163 ec = TALER_EC_GENERIC_INVALID_RESPONSE;
164 break;
165 }
166 response_code = MHD_HTTP_NO_CONTENT; /* signal end of list */
167 ec = TALER_EC_NONE;
168 break;
169 case MHD_HTTP_NO_CONTENT:
170 ec = TALER_EC_NONE;
171 break;
172 case MHD_HTTP_BAD_REQUEST:
173 /* This should never happen, either us or the bank is buggy
174 (or API version conflict); just pass JSON reply to the application */
175 GNUNET_break_op (0);
176 ec = TALER_JSON_get_error_code (j);
177 break;
178 case MHD_HTTP_UNAUTHORIZED:
179 /* Nothing really to verify, bank says the HTTP Authentication
180 failed. May happen if HTTP authentication is used and the
181 user supplied a wrong username/password combination. */
182 ec = TALER_JSON_get_error_code (j);
183 break;
184 case MHD_HTTP_NOT_FOUND:
185 /* Nothing really to verify: the bank is either unaware
186 of the endpoint (not a bank), or of the account.
187 We should pass the JSON (?) reply to the application */
188 ec = TALER_JSON_get_error_code (j);
189 break;
190 case MHD_HTTP_INTERNAL_SERVER_ERROR:
191 /* Server had an internal issue; we should retry, but this API
192 leaves this to the application */
193 ec = TALER_JSON_get_error_code (j);
194 break;
195 default:
196 /* unexpected response code */
197 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
198 "Unexpected response code %u\n",
199 (unsigned int) response_code);
200 ec = TALER_JSON_get_error_code (j);
201 break;
202 }
203 if (NULL != hh->hcb)
204 hh->hcb (hh->hcb_cls,
205 response_code,
206 ec,
207 0LLU,
208 NULL,
209 j);
210 ANASTASIS_EUFIN_credit_history_cancel (hh);
211}
212
213
214struct ANASTASIS_EUFIN_CreditHistoryHandle *
215ANASTASIS_EUFIN_credit_history (
216 struct GNUNET_CURL_Context *ctx,
217 const struct ANASTASIS_EUFIN_AuthenticationData *auth,
218 uint64_t start_row,
219 int64_t num_results,
220 struct GNUNET_TIME_Relative timeout,
221 ANASTASIS_EUFIN_CreditHistoryCallback hres_cb,
222 void *hres_cb_cls)
223{
224 char url[128];
225 struct ANASTASIS_EUFIN_CreditHistoryHandle *hh;
226 CURL *eh;
227 unsigned long long tms;
228
229 if (0 == num_results)
230 {
231 GNUNET_break (0);
232 return NULL;
233 }
234
235 tms = (unsigned long long) (timeout.rel_value_us
236 / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us);
237 if ( ( (UINT64_MAX == start_row) &&
238 (0 > num_results) ) ||
239 ( (0 == start_row) &&
240 (0 < num_results) ) )
241 {
242 if ( (0 < num_results) &&
243 (! GNUNET_TIME_relative_is_zero (timeout)) )
244 GNUNET_snprintf (url,
245 sizeof (url),
246 "history/incoming?delta=%lld&long_poll_ms=%llu",
247 (long long) num_results,
248 tms);
249 else
250 GNUNET_snprintf (url,
251 sizeof (url),
252 "history/incoming?delta=%lld",
253 (long long) num_results);
254 }
255 else
256 {
257 if ( (0 < num_results) &&
258 (! GNUNET_TIME_relative_is_zero (timeout)) )
259 GNUNET_snprintf (url,
260 sizeof (url),
261 "history/incoming?delta=%lld&start=%llu&long_poll_ms=%llu",
262 (long long) num_results,
263 (unsigned long long) start_row,
264 tms);
265 else
266 GNUNET_snprintf (url,
267 sizeof (url),
268 "history/incoming?delta=%lld&start=%llu",
269 (long long) num_results,
270 (unsigned long long) start_row);
271 }
272 hh = GNUNET_new (struct ANASTASIS_EUFIN_CreditHistoryHandle);
273 hh->hcb = hres_cb;
274 hh->hcb_cls = hres_cb_cls;
275 hh->request_url = Anastasis_url_join (auth->wire_gateway_url,
276 url,
277 NULL);
278 if (NULL == hh->request_url)
279 {
280 GNUNET_free (hh);
281 GNUNET_break (0);
282 return NULL;
283 }
284 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
285 "Requesting credit history at `%s'\n",
286 hh->request_url);
287 eh = curl_easy_init ();
288 if ( (NULL == eh) ||
289 (GNUNET_OK !=
290 ANASTASIS_EUFIN_setup_auth_ (eh,
291 auth)) ||
292 (CURLE_OK !=
293 curl_easy_setopt (eh,
294 CURLOPT_URL,
295 hh->request_url)) )
296 {
297 GNUNET_break (0);
298 ANASTASIS_EUFIN_credit_history_cancel (hh);
299 if (NULL != eh)
300 curl_easy_cleanup (eh);
301 return NULL;
302 }
303 if (0 != tms)
304 {
305 GNUNET_break (CURLE_OK ==
306 curl_easy_setopt (eh,
307 CURLOPT_TIMEOUT_MS,
308 (long) tms));
309 }
310 hh->job = GNUNET_CURL_job_add2 (ctx,
311 eh,
312 NULL,
313 &handle_credit_history_finished,
314 hh);
315 return hh;
316}
317
318
319void
320ANASTASIS_EUFIN_credit_history_cancel (
321 struct ANASTASIS_EUFIN_CreditHistoryHandle *hh)
322{
323 if (NULL != hh->job)
324 {
325 GNUNET_CURL_job_cancel (hh->job);
326 hh->job = NULL;
327 }
328 GNUNET_free (hh->request_url);
329 GNUNET_free (hh);
330}
331
332
333/* end of lae_credit.c */