aboutsummaryrefslogtreecommitdiff
path: root/src/backend/melted/taler-merchant-httpd.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/melted/taler-merchant-httpd.c')
-rw-r--r--src/backend/melted/taler-merchant-httpd.c506
1 files changed, 506 insertions, 0 deletions
diff --git a/src/backend/melted/taler-merchant-httpd.c b/src/backend/melted/taler-merchant-httpd.c
new file mode 100644
index 00000000..336f8915
--- /dev/null
+++ b/src/backend/melted/taler-merchant-httpd.c
@@ -0,0 +1,506 @@
1/*
2 This file is part of TALER
3 (C) 2014 Christian Grothoff (and other contributing authors)
4
5 TALER is free software; you can redistribute it and/or modify it under the
6 terms of the GNU 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 General Public License for more details.
12
13 You should have received a copy of the GNU General Public License along with
14 TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
15*/
16
17/**
18* @file merchant/backend/taler-merchant-httpd.c
19* @brief HTTP serving layer mainly intended to communicate with the frontend
20* @author Marcello Stanisci
21*/
22
23#include "platform.h"
24#include <microhttpd.h>
25#include <jansson.h>
26#include <gnunet/gnunet_util_lib.h>
27#include <taler/taler_json_lib.h>
28#include "taler-mint-httpd_parsing.h"
29#include "taler-mint-httpd_mhd.h"
30#include "taler-mint-httpd_admin.h"
31#include "taler-mint-httpd_deposit.h"
32#include "taler-mint-httpd_withdraw.h"
33#include "taler-mint-httpd_refresh.h"
34#include "taler-mint-httpd_keystate.h"
35#include "merchant.h"
36#include "merchant_db.h"
37
38extern struct MERCHANT_WIREFORMAT_Sepa *
39TALER_MERCHANT_parse_wireformat_sepa (const struct GNUNET_CONFIGURATION_Handle *cfg);
40
41/**
42 * Shorthand for exit jumps.
43 */
44#define EXITIF(cond) \
45 do { \
46 if (cond) { GNUNET_break (0); goto EXITIF_exit; } \
47 } while (0)
48
49// task 1. Just implement a hello world server launched a` la GNUNET
50
51/**
52 * The port we are running on
53 */
54unsigned short port;
55
56/**
57 * The MHD Daemon
58 */
59static struct MHD_Daemon *mhd;
60
61/**
62 * Connection handle to the our database
63 */
64PGconn *db_conn;
65
66/**
67 * Which currency is used by this mint?
68 * (verbatim copy from mint's code, just to make this
69 * merchant's source compile)
70 */
71char *TMH_mint_currency_string;
72
73/* As above */
74struct TALER_MINTDB_Plugin *TMH_plugin;
75
76
77/**
78 * As above, though the merchant does need some form of
79 * configuration
80 */
81struct GNUNET_CONFIGURATION_Handle *cfg;
82
83
84/**
85 * As above
86 */
87int TMH_test_mode;
88
89
90/**
91 * As above
92 */
93char *TMH_mint_directory;
94
95
96/**
97 * As above
98 */
99struct GNUNET_CRYPTO_EddsaPublicKey TMH_master_public_key;
100
101/**
102 * As above
103 */
104char *TMH_expected_wire_format;
105
106/**
107 * Shutdown task identifier
108 */
109static struct GNUNET_SCHEDULER_Task *shutdown_task;
110
111/**
112 * Our wireformat
113 */
114static struct MERCHANT_WIREFORMAT_Sepa *wire;
115
116/**
117 * Should we do a dry run where temporary tables are used for storing the data.
118 */
119static int dry;
120
121/**
122 * Global return code
123 */
124static int result;
125
126/**
127* Return the given message to the other end of connection
128* @msg (0-terminated) message to show
129* @param connection a MHD connection
130* @param resp where to store the response for the calling function
131* @return HTTP status code reflecting the operation outcome
132*
133*/
134
135static unsigned int
136generate_message (struct MHD_Response **resp, const char *msg) // this parameter was preceded by a '_' in its original file. Why?
137{
138
139 unsigned int ret;
140
141 *resp = MHD_create_response_from_buffer (strlen (msg), (void *) msg,
142 MHD_RESPMEM_PERSISTENT);
143 ret = 200;
144 return ret;
145
146
147}
148
149/**
150* Generate the 'hello world' response
151* @param connection a MHD connection
152* @param resp where to store the response for the calling function
153* @return HTTP status code reflecting the operation outcome
154*
155*/
156
157static unsigned int
158generate_hello (struct MHD_Response **resp) // this parameter was preceded by a '_' in its original file. Why?
159{
160
161 const char *hello = "Hello customer";
162 unsigned int ret;
163
164 *resp = MHD_create_response_from_buffer (strlen (hello), (void *) hello,
165 MHD_RESPMEM_PERSISTENT);
166 ret = 200;
167 return ret;
168
169
170}
171
172
173/**
174* Manage a non 200 HTTP status. I.e. it shows a 'failure' page to
175* the client
176* @param connection the channel thorugh which send the message
177* @status the HTTP status to examine
178* @return GNUNET_OK on successful message sending, GNUNET_SYSERR upon error
179*
180*/
181
182static int
183failure_resp (struct MHD_Connection *connection, unsigned int status)
184{
185 printf ("called failure mgmt\n");
186 static char page_404[]="\
187<!DOCTYPE html> \
188<html><title>Resource not found</title><body><center> \
189<h3>The resource you are looking for is not found.</h3> \
190</center></body></html>";
191 static char page_500[]="\
192<!DOCTYPE html> <html><title>Internal Server Error</title><body><center> \
193<h3>The server experienced an internal error and hence cannot serve your \
194request</h3></center></body></html>";
195 struct MHD_Response *resp;
196 char *page;
197 size_t size;
198#define PAGE(number) \
199 do {page=page_ ## number; size=sizeof(page_ ## number)-1;} while(0)
200
201 GNUNET_assert (400 <= status);
202 resp = NULL;
203 switch (status)
204 {
205 case 404:
206 PAGE(404);
207 break;
208 default:
209 status = 500;
210 case 500:
211 PAGE(500);
212 }
213#undef PAGE
214
215 EXITIF (NULL == (resp = MHD_create_response_from_buffer (size,
216 page,
217 MHD_RESPMEM_PERSISTENT)));
218 EXITIF (MHD_YES != MHD_queue_response (connection, status, resp));
219 MHD_destroy_response (resp);
220 return GNUNET_OK;
221
222 EXITIF_exit:
223 if (NULL != resp)
224 MHD_destroy_response (resp);
225 return GNUNET_SYSERR;
226}
227
228
229/**
230* Generate the hash containing the information (= a nounce + merchant's IBAN) to
231* redeem money from mint in a subsequent /deposit operation
232* @param nounce the nounce
233* @return the hash to be included in the contract's blob
234*
235*/
236
237static struct GNUNET_HashCode
238hash_wireformat (uint64_t nounce)
239{
240 struct GNUNET_HashContext *hc;
241 struct GNUNET_HashCode hash;
242
243 hc = GNUNET_CRYPTO_hash_context_start ();
244 GNUNET_CRYPTO_hash_context_read (hc, wire->iban, strlen (wire->iban));
245 GNUNET_CRYPTO_hash_context_read (hc, wire->name, strlen (wire->name));
246 GNUNET_CRYPTO_hash_context_read (hc, wire->bic, strlen (wire->bic));
247 nounce = GNUNET_htonll (nounce);
248 GNUNET_CRYPTO_hash_context_read (hc, &nounce, sizeof (nounce));
249 GNUNET_CRYPTO_hash_context_finish (hc, &hash);
250 return hash;
251}
252
253
254/**
255 * A client has requested the given url using the given method
256 * (#MHD_HTTP_METHOD_GET, #MHD_HTTP_METHOD_PUT,
257 * #MHD_HTTP_METHOD_DELETE, #MHD_HTTP_METHOD_POST, etc). The callback
258 * must call MHD callbacks to provide content to give back to the
259 * client and return an HTTP status code (i.e. #MHD_HTTP_OK,
260 * #MHD_HTTP_NOT_FOUND, etc.).
261 *
262 * @param cls argument given together with the function
263 * pointer when the handler was registered with MHD
264 * @param url the requested url
265 * @param method the HTTP method used (#MHD_HTTP_METHOD_GET,
266 * #MHD_HTTP_METHOD_PUT, etc.)
267 * @param version the HTTP version string (i.e.
268 * #MHD_HTTP_VERSION_1_1)
269 * @param upload_data the data being uploaded (excluding HEADERS,
270 * for a POST that fits into memory and that is encoded
271 * with a supported encoding, the POST data will NOT be
272 * given in upload_data and is instead available as
273 * part of #MHD_get_connection_values; very large POST
274 * data *will* be made available incrementally in
275 * @a upload_data)
276 * @param upload_data_size set initially to the size of the
277 * @a upload_data provided; the method must update this
278 * value to the number of bytes NOT processed;
279 * @param con_cls pointer that the callback can set to some
280 * address and that will be preserved by MHD for future
281 * calls for this request; since the access handler may
282 * be called many times (i.e., for a PUT/POST operation
283 * with plenty of upload data) this allows the application
284 * to easily associate some request-specific state.
285 * If necessary, this state can be cleaned up in the
286 * global #MHD_RequestCompletedCallback (which
287 * can be set with the #MHD_OPTION_NOTIFY_COMPLETED).
288 * Initially, `*con_cls` will be NULL.
289 * @return #MHD_YES if the connection was handled successfully,
290 * #MHD_NO if the socket must be closed due to a serios
291 * error while handling the request
292 */
293
294static int
295url_handler (void *cls,
296 struct MHD_Connection *connection,
297 const char *url,
298 const char *method,
299 const char *version,
300 const char *upload_data,
301 size_t *upload_data_size,
302 void **connection_cls)
303{
304
305 unsigned int status;
306 unsigned int no_destroy;
307 struct MHD_Response *resp;
308 struct TALER_Amount price;
309 json_t json_price;
310 json_t *root;
311 int res;
312 char *desc;
313
314 #define URL_HELLO "/hello"
315 #define URL_CONTRACT "/contract"
316 no_destroy = 0;
317 resp = NULL;
318 status = 500;
319 if (0 == strncasecmp (url, URL_HELLO, sizeof (URL_HELLO)))
320 {
321 if (0 == strcmp (MHD_HTTP_METHOD_GET, method))
322 status = generate_hello (&resp); //TBD
323 else
324 GNUNET_break (0);
325 }
326
327 // to be called by the frontend passing all the product's information
328 // which are relevant for the contract's generation
329 if (0 == strncasecmp (url, URL_CONTRACT, sizeof (URL_CONTRACT)))
330 {
331 if (0 == strcmp (MHD_HTTP_METHOD_GET, method))
332 status = generate_message (&resp, "Sorry, only POST is allowed");
333 else
334
335 /*
336 1. parse the json
337 2. generate the contract
338 3. pack the contract's json
339 4. return it
340 */
341
342 res = TMH_PARSE_post_json (connection,
343 connection_cls,
344 upload_data,
345 upload_data_size,
346 &root);
347
348 if (GNUNET_SYSERR == res)
349 return MHD_NO;
350 if ( (GNUNET_NO == res) || (NULL == root) )
351 return MHD_YES;
352
353 /* not really needed for getting just a string. Though it'd be very handy
354 to enhace the mint's JSON-parsing capabilities with the merchant's needs.
355
356 struct TMH_PARSE_FieldSpecification spec[] = {
357 TMH_PARSE_member_variable ("desc", (void **) &desc, &desc_len),
358 TMH_PARSE_member_amount ("price", &price),
359 TMH_PARSE_MEMBER_END
360 };
361 res = TMH_PARSE_json_data (connection,
362 root,
363 spec); */
364 /*
365
366 The expected JSON :
367
368 {
369 "desc" : "some description",
370 "price" : a JSON compliant TALER_Amount objet
371 }
372
373 */
374
375 if (!json_unpack (root, "{s:s, s:o}", "desc", &desc, "price", &json_price))
376 status = generate_message (&resp, "unable to parse /contract JSON");
377 else
378 {
379 if (GNUNET_OK != TALER_json_to_amount (&json_price, &price))
380 status = generate_message (&resp, "unable to parse `price' field in /contract JSON");
381 else
382 {
383 /* Let's generate this contract! */
384 /* First, initialize the DB, since it'll be stored there */
385
386
387
388
389
390 }
391
392 }
393
394
395
396
397
398
399 }
400
401 if (NULL != resp)
402 {
403 EXITIF (MHD_YES != MHD_queue_response (connection, status, resp));
404 if (!no_destroy)
405 MHD_destroy_response (resp);
406 }
407 else
408 EXITIF (GNUNET_OK != failure_resp (connection, status));
409 return MHD_YES;
410
411 EXITIF_exit:
412 result = GNUNET_SYSERR;
413 //GNUNET_SCHEDULER_shutdown (); to a later stage, maybe
414 return MHD_NO;
415
416}
417
418/**
419 * Shutdown task (magically invoked when the application is being
420 * quit)
421 *
422 * @param cls NULL
423 * @param tc scheduler task context
424 */
425static void
426do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
427{
428
429 if (NULL != mhd)
430 {
431 MHD_stop_daemon (mhd);
432 mhd = NULL;
433 }
434
435}
436
437
438/**
439 * Main function that will be run by the scheduler.
440 *
441 * @param cls closure
442 * @param args remaining command-line arguments
443 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
444 * @param config configuration
445 */
446static void
447run (void *cls, char *const *args, const char *cfgfile,
448 const struct GNUNET_CONFIGURATION_Handle *config)
449{
450
451 port = 9966;
452
453
454 EXITIF (NULL == (wire = TALER_MERCHANT_parse_wireformat_sepa (config)));
455 EXITIF (GNUNET_OK != MERCHANT_DB_initialize (db_conn, dry));
456
457 shutdown_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
458 &do_shutdown, NULL);
459
460 mhd = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY,
461 port,
462 NULL, NULL,
463 &url_handler, NULL,
464 MHD_OPTION_END);
465
466
467 EXITIF (NULL == mhd);
468 result = GNUNET_OK;
469
470 EXITIF_exit:
471 if (GNUNET_OK != result)
472 GNUNET_SCHEDULER_shutdown ();
473
474
475}
476
477/**
478 * The main function of the serve tool
479 *
480 * @param argc number of arguments from the command line
481 * @param argv command line arguments
482 * @return 0 ok, 1 on error
483 */
484int
485main (int argc, char *const *argv)
486{
487
488 static const struct GNUNET_GETOPT_CommandLineOption options[] = {
489 {'t', "temp", NULL,
490 gettext_noop ("Use temporary database tables"), GNUNET_NO,
491 &GNUNET_GETOPT_set_one, &dry},
492 GNUNET_GETOPT_OPTION_END
493 };
494
495
496 if (GNUNET_OK !=
497 GNUNET_PROGRAM_run (argc, argv,
498 "taler-merchant-serve",
499 "Serve merchant's HTTP interface",
500 options, &run, NULL))
501 return 3;
502 return (GNUNET_OK == result) ? 0 : 1;
503
504
505
506}