eliminate /admin/add/incoming (fixes #5172)

This commit is contained in:
Christian Grothoff 2017-12-14 13:49:24 +01:00
parent f7c9de73e1
commit 1897d65af5
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC
6 changed files with 25 additions and 509 deletions

View File

@ -400,13 +400,10 @@ gap. See keys-duration.
@section Serving @section Serving
The exchange can serve HTTP over both TCP and UNIX domain socket. It The exchange can serve HTTP over both TCP and UNIX domain socket.
needs this configuration @emph{twice}, because it opens one connection
for ordinary REST calls, and one for "/admin" and "/test" REST calls,
because the operator may want to restrict the access to "/admin".
The following values are to be configured under the section The following values are to be configured in the section
@cite{[exchange]} and @cite{[exchange-admin]}: @cite{[exchange]}:
@itemize @itemize
@ -425,13 +422,9 @@ The following values are to be configured under the section
@item @item
@cite{unixpath_mode}: number giving the mode with the access @cite{unixpath_mode}: number giving the mode with the access
permissiON MASK for the @cite{unixpath} (i.e. 660 = rw-rw----). permission MASK for the @cite{unixpath} (i.e. 660 = rw-rw----).
@end itemize @end itemize
The exchange can be started with the @cite{-D} option to disable the administrative
functions entirely. It is recommended that the administrative API is only
accessible via a properly protected UNIX domain socket.
@node Currency @node Currency
@section Currency @section Currency
@ -673,7 +666,7 @@ get signed by every auditor he wishes (or is forced to) work with.
In a normal scenario, an auditor must have some way of receiving the blob to In a normal scenario, an auditor must have some way of receiving the blob to
sign (Website, manual delivery, ..). Nonetheless, the exchange admin can fake sign (Website, manual delivery, ..). Nonetheless, the exchange admin can fake
an auditor signature - for testing purposes - by running the following command an auditor signature --- for testing purposes --- by running the following command
@example @example
taler-auditor-sign -m EXCHANGE_MASTER_PUB -r BLOB -u AUDITOR_URI -o OUTPUT_FILE taler-auditor-sign -m EXCHANGE_MASTER_PUB -r BLOB -u AUDITOR_URI -o OUTPUT_FILE

View File

@ -43,7 +43,6 @@ taler_exchange_wirewatch_LDADD = \
taler_exchange_httpd_SOURCES = \ taler_exchange_httpd_SOURCES = \
taler-exchange-httpd.c taler-exchange-httpd.h \ taler-exchange-httpd.c taler-exchange-httpd.h \
taler-exchange-httpd_admin.c taler-exchange-httpd_admin.h \
taler-exchange-httpd_db.c taler-exchange-httpd_db.h \ taler-exchange-httpd_db.c taler-exchange-httpd_db.h \
taler-exchange-httpd_deposit.c taler-exchange-httpd_deposit.h \ taler-exchange-httpd_deposit.c taler-exchange-httpd_deposit.h \
taler-exchange-httpd_keystate.c taler-exchange-httpd_keystate.h \ taler-exchange-httpd_keystate.c taler-exchange-httpd_keystate.h \

View File

@ -36,18 +36,3 @@ PORT = 8081
# Required for wire transfers as we need to include it in the wire # Required for wire transfers as we need to include it in the wire
# transfers to enable tracking. # transfers to enable tracking.
BASE_URL = http://localhost:8081/ BASE_URL = http://localhost:8081/
[exchange-admin]
# Network configuration for the /admin HTTP server
# serve via tcp socket (on PORT)
SERVE = tcp
# Unix domain socket to listen on,
# only effective with "SERVE = unix"
UNIXPATH = ${TALER_RUNTIME_DIR}/exchange-admin.http
UNIXPATH_MODE = 660
# HTTP port the exchange listens to
PORT = 18080

View File

@ -28,7 +28,6 @@
#include <pthread.h> #include <pthread.h>
#include "taler-exchange-httpd_parsing.h" #include "taler-exchange-httpd_parsing.h"
#include "taler-exchange-httpd_mhd.h" #include "taler-exchange-httpd_mhd.h"
#include "taler-exchange-httpd_admin.h"
#include "taler-exchange-httpd_deposit.h" #include "taler-exchange-httpd_deposit.h"
#include "taler-exchange-httpd_refund.h" #include "taler-exchange-httpd_refund.h"
#include "taler-exchange-httpd_reserve_status.h" #include "taler-exchange-httpd_reserve_status.h"
@ -95,16 +94,6 @@ static unsigned int connection_timeout = 30;
*/ */
static struct MHD_Daemon *mhd; static struct MHD_Daemon *mhd;
/**
* The HTTP Daemon for /admin-requests.
*/
static struct MHD_Daemon *mhd_admin;
/**
* Do not offer /admin API.
*/
static int no_admin;
/** /**
* Initialize the database by creating tables and indices. * Initialize the database by creating tables and indices.
*/ */
@ -115,33 +104,17 @@ static int init_db;
*/ */
static uint16_t serve_port; static uint16_t serve_port;
/**
* Port to run the admin daemon on.
*/
static uint16_t serve_admin_port;
/** /**
* Path for the unix domain-socket * Path for the unix domain-socket
* to run the daemon on. * to run the daemon on.
*/ */
static char *serve_unixpath; static char *serve_unixpath;
/**
* Path for the unix domain-socket
* to run the admin daemon on.
*/
static char *serve_admin_unixpath;
/** /**
* File mode for unix-domain socket. * File mode for unix-domain socket.
*/ */
static mode_t unixpath_mode; static mode_t unixpath_mode;
/**
* File mode for unix-domain socket.
*/
static mode_t unixpath_admin_mode;
/** /**
* Function called whenever MHD is done with a request. If the * Function called whenever MHD is done with a request. If the
@ -307,76 +280,6 @@ handle_mhd_request (void *cls,
"Only GET is allowed", 0, "Only GET is allowed", 0,
&TEH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED }, &TEH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
{ NULL, NULL, NULL, NULL, 0, 0 }
};
static struct TEH_RequestHandler h404 =
{
"", NULL, "text/html",
"<html><title>404: not found</title></html>", 0,
&TEH_MHD_handler_static_response, MHD_HTTP_NOT_FOUND
};
struct TEH_RequestHandler *rh;
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Handling request for URL '%s'\n",
url);
if (0 == strcasecmp (method,
MHD_HTTP_METHOD_HEAD))
method = MHD_HTTP_METHOD_GET; /* treat HEAD as GET here, MHD will do the rest */
for (unsigned int i=0;NULL != handlers[i].url;i++)
{
rh = &handlers[i];
if ( (0 == strcasecmp (url,
rh->url)) &&
( (NULL == rh->method) ||
(0 == strcasecmp (method,
rh->method)) ) )
return rh->handler (rh,
connection,
con_cls,
upload_data,
upload_data_size);
}
return TEH_MHD_handler_static_response (&h404,
connection,
con_cls,
upload_data,
upload_data_size);
}
/**
* Handle incoming administrative HTTP request.
*
* @param cls closure for MHD daemon (unused)
* @param connection the connection
* @param url the requested url
* @param method the method (POST, GET, ...)
* @param version HTTP version (ignored)
* @param upload_data request data
* @param upload_data_size size of @a upload_data in bytes
* @param con_cls closure for request (a `struct Buffer *`)
* @return MHD result code
*/
static int
handle_mhd_admin_request (void *cls,
struct MHD_Connection *connection,
const char *url,
const char *method,
const char *version,
const char *upload_data,
size_t *upload_data_size,
void **con_cls)
{
static struct TEH_RequestHandler handlers[] =
{
{ "/admin/add/incoming", MHD_HTTP_METHOD_POST, "application/json",
NULL, 0,
&TEH_ADMIN_handler_admin_add_incoming, MHD_HTTP_OK },
{ "/admin/add/incoming", NULL, "text/plain",
"Only POST is allowed", 0,
&TEH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
#if HAVE_DEVELOPER #if HAVE_DEVELOPER
/* Client crypto-interoperability test functions */ /* Client crypto-interoperability test functions */
{ "/test", MHD_HTTP_METHOD_POST, "application/json", { "/test", MHD_HTTP_METHOD_POST, "application/json",
@ -452,12 +355,14 @@ handle_mhd_admin_request (void *cls,
&TEH_MHD_handler_static_response, MHD_HTTP_NOT_FOUND &TEH_MHD_handler_static_response, MHD_HTTP_NOT_FOUND
}; };
struct TEH_RequestHandler *rh; struct TEH_RequestHandler *rh;
unsigned int i;
GNUNET_log (GNUNET_ERROR_TYPE_INFO, GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Handling request for URL '%s'\n", "Handling request for URL '%s'\n",
url); url);
for (i=0;NULL != handlers[i].url;i++) if (0 == strcasecmp (method,
MHD_HTTP_METHOD_HEAD))
method = MHD_HTTP_METHOD_GET; /* treat HEAD as GET here, MHD will do the rest */
for (unsigned int i=0;NULL != handlers[i].url;i++)
{ {
rh = &handlers[i]; rh = &handlers[i];
if ( (0 == strcasecmp (url, if ( (0 == strcasecmp (url,
@ -691,15 +596,6 @@ exchange_serve_process_config ()
TEH_VALIDATION_done (); TEH_VALIDATION_done ();
return GNUNET_SYSERR; return GNUNET_SYSERR;
} }
if (GNUNET_OK !=
parse_port_config ("exchange-admin",
&serve_admin_port,
&serve_admin_unixpath,
&unixpath_admin_mode))
{
TEH_VALIDATION_done ();
return GNUNET_SYSERR;
}
return GNUNET_OK; return GNUNET_OK;
} }
@ -952,10 +848,6 @@ main (int argc,
"force HTTP connections to be closed after each request", "force HTTP connections to be closed after each request",
&TEH_exchange_connection_close), &TEH_exchange_connection_close),
GNUNET_GETOPT_option_cfgfile (&cfgfile), GNUNET_GETOPT_option_cfgfile (&cfgfile),
GNUNET_GETOPT_option_flag ('D',
"disable-admin",
"do not run the /admin-HTTP server",
&no_admin),
GNUNET_GETOPT_option_flag ('i', GNUNET_GETOPT_option_flag ('i',
"init-db", "init-db",
"create database tables and indicies if necessary", "create database tables and indicies if necessary",
@ -982,7 +874,6 @@ main (int argc,
const char *listen_pid; const char *listen_pid;
const char *listen_fds; const char *listen_fds;
int fh = -1; int fh = -1;
int fh_admin = -1;
if (0 >= if (0 >=
GNUNET_GETOPT_run ("taler-exchange-httpd", GNUNET_GETOPT_run ("taler-exchange-httpd",
@ -1018,12 +909,9 @@ main (int argc,
(getpid() == strtol (listen_pid, (getpid() == strtol (listen_pid,
NULL, NULL,
10)) && 10)) &&
( (1 == strtoul (listen_fds, (1 == strtoul (listen_fds,
NULL, NULL,
10)) || 10)) )
(2 == strtoul (listen_fds,
NULL,
10)) ) )
{ {
int flags; int flags;
@ -1044,29 +932,6 @@ main (int argc,
flags)) ) flags)) )
GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
"fcntl"); "fcntl");
if (2 == strtoul (listen_fds,
NULL,
10))
{
fh_admin = 4;
flags = fcntl (fh_admin,
F_GETFD);
if ( (-1 == flags) &&
(EBADF == errno) )
{
fprintf (stderr,
"Bad listen socket passed, ignored\n");
fh_admin = -1;
}
flags |= FD_CLOEXEC;
if ( (-1 != fh_admin) &&
(0 != fcntl (fh_admin,
F_SETFD,
flags)) )
GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
"fcntl");
}
} }
/* consider unix path */ /* consider unix path */
@ -1078,19 +943,6 @@ main (int argc,
if (-1 == fh) if (-1 == fh)
return 1; return 1;
} }
if ( (-1 == fh_admin) &&
(0 == no_admin) &&
(NULL != serve_admin_unixpath) )
{
fh_admin = open_unix_path (serve_admin_unixpath,
unixpath_admin_mode);
if (-1 == fh_admin)
{
if (-1 != fh)
GNUNET_break (0 == close (fh));
return 1;
}
}
mhd mhd
= MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_PIPE_FOR_SHUTDOWN | MHD_USE_DEBUG | MHD_USE_DUAL_STACK, = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_PIPE_FOR_SHUTDOWN | MHD_USE_DEBUG | MHD_USE_DUAL_STACK,
@ -1112,30 +964,6 @@ main (int argc,
return 1; return 1;
} }
if (0 == no_admin)
{
mhd_admin
= MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_PIPE_FOR_SHUTDOWN | MHD_USE_DEBUG,
(-1 == fh) ? serve_admin_port : 0,
NULL, NULL,
&handle_mhd_admin_request, NULL,
MHD_OPTION_LISTEN_SOCKET, fh_admin,
MHD_OPTION_EXTERNAL_LOGGER, &handle_mhd_logs, NULL,
MHD_OPTION_NOTIFY_COMPLETED, &handle_mhd_completion_callback, NULL,
MHD_OPTION_CONNECTION_TIMEOUT, connection_timeout,
#if HAVE_DEVELOPER
MHD_OPTION_NOTIFY_CONNECTION, &connection_done, NULL,
#endif
MHD_OPTION_END);
if (NULL == mhd_admin)
{
fprintf (stderr,
"Failed to start administrative HTTP server.\n");
MHD_stop_daemon (mhd);
return 1;
}
}
#if HAVE_DEVELOPER #if HAVE_DEVELOPER
if (NULL != input_filename) if (NULL != input_filename)
{ {
@ -1157,23 +985,14 @@ main (int argc,
case GNUNET_OK: case GNUNET_OK:
case GNUNET_SYSERR: case GNUNET_SYSERR:
MHD_stop_daemon (mhd); MHD_stop_daemon (mhd);
if (NULL != mhd_admin)
MHD_stop_daemon (mhd_admin);
break; break;
case GNUNET_NO: case GNUNET_NO:
{ {
MHD_socket sock = MHD_quiesce_daemon (mhd); MHD_socket sock = MHD_quiesce_daemon (mhd);
MHD_socket admin_sock;
int admin_sock_opened = GNUNET_NO;
pid_t chld; pid_t chld;
int flags; int flags;
/* Set flags to make 'sock' inherited by child */ /* Set flags to make 'sock' inherited by child */
if (NULL != mhd_admin)
{
admin_sock = MHD_quiesce_daemon (mhd_admin);
admin_sock_opened = GNUNET_YES;
}
flags = fcntl (sock, F_GETFD); flags = fcntl (sock, F_GETFD);
GNUNET_assert (-1 != flags); GNUNET_assert (-1 != flags);
flags &= ~FD_CLOEXEC; flags &= ~FD_CLOEXEC;
@ -1197,20 +1016,13 @@ main (int argc,
"dup2"); "dup2");
_exit (1); _exit (1);
} }
if ( (GNUNET_YES == admin_sock_opened) &&
(4 != dup2 (admin_sock, 4)) )
{
GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
"dup2");
_exit (1);
}
/* Tell the child that it is the desired recipient for FD #3 */ /* Tell the child that it is the desired recipient for FD #3 */
GNUNET_snprintf (pids, GNUNET_snprintf (pids,
sizeof (pids), sizeof (pids),
"%u", "%u",
getpid ()); getpid ());
setenv ("LISTEN_PID", pids, 1); setenv ("LISTEN_PID", pids, 1);
setenv ("LISTEN_FDS", (NULL != mhd_admin) ? "2" : "1", 1); setenv ("LISTEN_FDS", "1", 1);
/* Finally, exec the (presumably) more recent exchange binary */ /* Finally, exec the (presumably) more recent exchange binary */
execvp ("taler-exchange-httpd", execvp ("taler-exchange-httpd",
argv); argv);
@ -1222,25 +1034,16 @@ main (int argc,
before exiting; as the listen socket is no longer used, before exiting; as the listen socket is no longer used,
close it here */ close it here */
GNUNET_break (0 == close (sock)); GNUNET_break (0 == close (sock));
if (GNUNET_YES == admin_sock_opened) while (0 != MHD_get_daemon_info (mhd,
GNUNET_break (0 == close (admin_sock)); MHD_DAEMON_INFO_CURRENT_CONNECTIONS)->num_connections)
while ( (0 != MHD_get_daemon_info (mhd,
MHD_DAEMON_INFO_CURRENT_CONNECTIONS)->num_connections) ||
( (NULL != mhd_admin) &&
(0 != MHD_get_daemon_info (mhd_admin,
MHD_DAEMON_INFO_CURRENT_CONNECTIONS)->num_connections) ) )
sleep (1); sleep (1);
/* Now we're really done, practice clean shutdown */ /* Now we're really done, practice clean shutdown */
MHD_stop_daemon (mhd); MHD_stop_daemon (mhd);
if (NULL != mhd_admin)
MHD_stop_daemon (mhd_admin);
} }
break; break;
default: default:
GNUNET_break (0); GNUNET_break (0);
MHD_stop_daemon (mhd); MHD_stop_daemon (mhd);
if (NULL != mhd_admin)
MHD_stop_daemon (mhd_admin);
break; break;
} }
TALER_EXCHANGEDB_plugin_unload (TEH_plugin); TALER_EXCHANGEDB_plugin_unload (TEH_plugin);

View File

@ -1,218 +0,0 @@
/*
This file is part of TALER
Copyright (C) 2014, 2015 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
TALER is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along with
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
* @file taler-exchange-httpd_admin.c
* @brief Handle /admin/ requests
* @author Christian Grothoff
*/
#include "platform.h"
#include <gnunet/gnunet_util_lib.h>
#include <jansson.h>
#include "taler-exchange-httpd_admin.h"
#include "taler-exchange-httpd_parsing.h"
#include "taler-exchange-httpd_responses.h"
#include "taler-exchange-httpd_validation.h"
/**
* Closure for #admin_add_incoming_transaction()
*/
struct AddIncomingContext
{
/**
* public key of the reserve
*/
struct TALER_ReservePublicKeyP reserve_pub;
/**
* amount to add to the reserve
*/
struct TALER_Amount amount;
/**
* When did we receive the wire transfer
*/
struct GNUNET_TIME_Absolute execution_time;
/**
* which account send the funds
*/
json_t *sender_account_details;
/**
* Information that uniquely identifies the transfer
*/
json_t *transfer_details;
/**
* Set to the transaction status.
*/
enum GNUNET_DB_QueryStatus qs;
};
/**
* Add an incoming transaction to the database. Checks if the
* transaction is fresh (not a duplicate) and if so adds it to the
* database.
*
* If it returns a non-error code, the transaction logic MUST
* NOT queue a MHD response. IF it returns an hard error, the
* transaction logic MUST queue a MHD response and set @a mhd_ret. IF
* it returns the soft error code, the function MAY be called again to
* retry and MUST not queue a MHD response.
*
* @param cls closure with the `struct AddIncomingContext *`
* @param connection MHD request which triggered the transaction
* @param session database session to use
* @param[out] mhd_ret set to MHD response status for @a connection,
* if transaction failed (!)
* @return transaction status
*/
static enum GNUNET_DB_QueryStatus
admin_add_incoming_transaction (void *cls,
struct MHD_Connection *connection,
struct TALER_EXCHANGEDB_Session *session,
int *mhd_ret)
{
struct AddIncomingContext *aic = cls;
void *json_str;
json_str = json_dumps (aic->transfer_details,
JSON_INDENT(2));
if (NULL == json_str)
{
GNUNET_break (0);
*mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
TALER_EC_PARSER_OUT_OF_MEMORY);
return GNUNET_DB_STATUS_HARD_ERROR;
}
aic->qs = TEH_plugin->reserves_in_insert (TEH_plugin->cls,
session,
&aic->reserve_pub,
&aic->amount,
aic->execution_time,
aic->sender_account_details,
json_str,
strlen (json_str));
free (json_str);
if (GNUNET_DB_STATUS_HARD_ERROR == aic->qs)
{
GNUNET_break (0);
*mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
TALER_EC_ADMIN_ADD_INCOMING_DB_STORE);
return GNUNET_DB_STATUS_HARD_ERROR;
}
return aic->qs;
}
/**
* Handle a "/admin/add/incoming" request. Parses the
* given "reserve_pub", "amount", "transaction" and "h_wire"
* details and adds the respective transaction to the database.
*
* @param rh context of the handler
* @param connection the MHD connection to handle
* @param[in,out] connection_cls the connection's closure (can be updated)
* @param upload_data upload data
* @param[in,out] upload_data_size number of bytes (left) in @a upload_data
* @return MHD result code
*/
int
TEH_ADMIN_handler_admin_add_incoming (struct TEH_RequestHandler *rh,
struct MHD_Connection *connection,
void **connection_cls,
const char *upload_data,
size_t *upload_data_size)
{
struct AddIncomingContext aic;
enum TALER_ErrorCode ec;
char *emsg;
json_t *root;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto ("reserve_pub", &aic.reserve_pub),
TALER_JSON_spec_amount ("amount", &aic.amount),
GNUNET_JSON_spec_absolute_time ("execution_date", &aic.execution_time),
GNUNET_JSON_spec_json ("sender_account_details", &aic.sender_account_details),
GNUNET_JSON_spec_json ("transfer_details", &aic.transfer_details),
GNUNET_JSON_spec_end ()
};
int res;
int mhd_ret;
res = TEH_PARSE_post_json (connection,
connection_cls,
upload_data,
upload_data_size,
&root);
if (GNUNET_SYSERR == res)
return MHD_NO;
if ( (GNUNET_NO == res) ||
(NULL == root) )
return MHD_YES;
res = TEH_PARSE_json_data (connection,
root,
spec);
json_decref (root);
if (GNUNET_OK != res)
{
GNUNET_break_op (0);
json_decref (root);
return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
}
if (TALER_EC_NONE !=
(ec = TEH_json_validate_wireformat (aic.sender_account_details,
GNUNET_NO,
&emsg)))
{
GNUNET_JSON_parse_free (spec);
mhd_ret = TEH_RESPONSE_reply_external_error (connection,
ec,
emsg);
GNUNET_free (emsg);
return mhd_ret;
}
if (0 != strcasecmp (aic.amount.currency,
TEH_exchange_currency_string))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Exchange uses currency `%s', but /admin/add/incoming tried to use currency `%s'\n",
TEH_exchange_currency_string,
aic.amount.currency);
GNUNET_JSON_parse_free (spec);
return TEH_RESPONSE_reply_arg_invalid (connection,
TALER_EC_ADMIN_ADD_INCOMING_CURRENCY_UNSUPPORTED,
"amount:currency");
}
res = TEH_DB_run_transaction (connection,
&mhd_ret,
&admin_add_incoming_transaction,
&aic);
GNUNET_JSON_parse_free (spec);
if (GNUNET_OK != res)
return mhd_ret;
return TEH_RESPONSE_reply_json_pack (connection,
MHD_HTTP_OK,
"{s:s}",
"status",
(GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == aic.qs)
? "NEW"
: "DUP");
}
/* end of taler-exchange-httpd_admin.c */

View File

@ -1,46 +0,0 @@
/*
This file is part of TALER
Copyright (C) 2014 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
TALER is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along with
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
* @file taler-exchange-httpd_admin.h
* @brief Handle /admin/ requests
* @author Christian Grothoff
*/
#ifndef TALER_EXCHANGE_HTTPD_ADMIN_H
#define TALER_EXCHANGE_HTTPD_ADMIN_H
#include <microhttpd.h>
#include "taler-exchange-httpd.h"
/**
* Handle a "/admin/add/incoming" request. Parses the
* given "reserve_pub", "amount", "transaction" and "h_wire"
* details and adds the respective transaction to the database.
*
* @param rh context of the handler
* @param connection the MHD connection to handle
* @param[in,out] connection_cls the connection's closure (can be updated)
* @param upload_data upload data
* @param[in,out] upload_data_size number of bytes (left) in @a upload_data
* @return MHD result code
*/
int
TEH_ADMIN_handler_admin_add_incoming (struct TEH_RequestHandler *rh,
struct MHD_Connection *connection,
void **connection_cls,
const char *upload_data,
size_t *upload_data_size);
#endif