Line data Source code
1 : /*
2 : This file is part of TALER
3 : (C) 2022 Taler Systems SA
4 :
5 : TALER is free software; you can redistribute it and/or modify
6 : it under the terms of the GNU Affero General Public License as
7 : published by the Free Software Foundation; either version 3,
8 : or (at your option) any later version.
9 :
10 : TALER is distributed in the hope that it will be useful, but
11 : 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 TALER; see the file COPYING. If not,
17 : see <http://www.gnu.org/licenses/>
18 : */
19 :
20 : /**
21 : * @file taler-merchant-httpd_private-post-otp-devices.c
22 : * @brief implementing POST /otp-devices request handling
23 : * @author Christian Grothoff
24 : */
25 : #include "platform.h"
26 : #include "taler-merchant-httpd_private-post-otp-devices.h"
27 : #include "taler-merchant-httpd_helper.h"
28 : #include <taler/taler_json_lib.h>
29 :
30 :
31 : /**
32 : * How often do we retry the simple INSERT database transaction?
33 : */
34 : #define MAX_RETRIES 3
35 :
36 :
37 : /**
38 : * Check if the two otp-devices are identical.
39 : *
40 : * @param t1 device to compare
41 : * @param t2 other device to compare
42 : * @return true if they are 'equal', false if not or of payto_uris is not an array
43 : */
44 : static bool
45 0 : otp_devices_equal (const struct TALER_MERCHANTDB_OtpDeviceDetails *t1,
46 : const struct TALER_MERCHANTDB_OtpDeviceDetails *t2)
47 : {
48 0 : return ( (0 == strcmp (t1->otp_description,
49 0 : t2->otp_description)) &&
50 0 : (0 == strcmp (t1->otp_key,
51 0 : t2->otp_key) ) &&
52 0 : (t1->otp_ctr == t2->otp_ctr) &&
53 0 : (t1->otp_algorithm == t2->otp_algorithm) );
54 : }
55 :
56 :
57 : MHD_RESULT
58 4 : TMH_private_post_otp_devices (const struct TMH_RequestHandler *rh,
59 : struct MHD_Connection *connection,
60 : struct TMH_HandlerContext *hc)
61 : {
62 4 : struct TMH_MerchantInstance *mi = hc->instance;
63 4 : struct TALER_MERCHANTDB_OtpDeviceDetails tp = { 0 };
64 : const char *device_id;
65 : enum GNUNET_DB_QueryStatus qs;
66 : struct GNUNET_JSON_Specification spec[] = {
67 4 : GNUNET_JSON_spec_string ("otp_device_id",
68 : &device_id),
69 4 : GNUNET_JSON_spec_string ("otp_device_description",
70 : (const char **) &tp.otp_description),
71 4 : TALER_JSON_spec_otp_type ("otp_algorithm",
72 : &tp.otp_algorithm),
73 4 : GNUNET_JSON_spec_mark_optional (
74 : GNUNET_JSON_spec_uint64 ("otp_ctr",
75 : &tp.otp_ctr),
76 : NULL),
77 4 : TALER_JSON_spec_otp_key ("otp_key",
78 : (const char **) &tp.otp_key),
79 4 : GNUNET_JSON_spec_end ()
80 : };
81 :
82 4 : GNUNET_assert (NULL != mi);
83 : {
84 : enum GNUNET_GenericReturnValue res;
85 :
86 4 : res = TALER_MHD_parse_json_data (connection,
87 4 : hc->request_body,
88 : spec);
89 4 : if (GNUNET_OK != res)
90 : {
91 0 : GNUNET_break_op (0);
92 : return (GNUNET_NO == res)
93 : ? MHD_YES
94 0 : : MHD_NO;
95 : }
96 : }
97 :
98 : /* finally, interact with DB until no serialization error */
99 4 : for (unsigned int i = 0; i<MAX_RETRIES; i++)
100 : {
101 : /* Test if a OTP device of this id is known */
102 : struct TALER_MERCHANTDB_OtpDeviceDetails etp;
103 :
104 4 : if (GNUNET_OK !=
105 4 : TMH_db->start (TMH_db->cls,
106 : "/post otp-devices"))
107 : {
108 0 : GNUNET_break (0);
109 0 : GNUNET_JSON_parse_free (spec);
110 0 : return TALER_MHD_reply_with_error (connection,
111 : MHD_HTTP_INTERNAL_SERVER_ERROR,
112 : TALER_EC_GENERIC_DB_START_FAILED,
113 : NULL);
114 : }
115 4 : qs = TMH_db->select_otp (TMH_db->cls,
116 4 : mi->settings.id,
117 : device_id,
118 : &etp);
119 4 : switch (qs)
120 : {
121 0 : case GNUNET_DB_STATUS_HARD_ERROR:
122 : /* Clean up and fail hard */
123 0 : GNUNET_break (0);
124 0 : TMH_db->rollback (TMH_db->cls);
125 0 : GNUNET_JSON_parse_free (spec);
126 0 : return TALER_MHD_reply_with_error (connection,
127 : MHD_HTTP_INTERNAL_SERVER_ERROR,
128 : TALER_EC_GENERIC_DB_FETCH_FAILED,
129 : NULL);
130 0 : case GNUNET_DB_STATUS_SOFT_ERROR:
131 : /* restart transaction */
132 0 : goto retry;
133 4 : case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
134 : /* Good, we can proceed! */
135 4 : break;
136 0 : case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
137 : /* idempotency check: is etp == tp? */
138 : {
139 : bool eq;
140 :
141 0 : eq = otp_devices_equal (&tp,
142 : &etp);
143 0 : GNUNET_free (etp.otp_description);
144 0 : GNUNET_free (etp.otp_key);
145 0 : TMH_db->rollback (TMH_db->cls);
146 0 : GNUNET_JSON_parse_free (spec);
147 : return eq
148 0 : ? TALER_MHD_reply_static (connection,
149 : MHD_HTTP_NO_CONTENT,
150 : NULL,
151 : NULL,
152 : 0)
153 0 : : TALER_MHD_reply_with_error (connection,
154 : MHD_HTTP_CONFLICT,
155 : TALER_EC_MERCHANT_PRIVATE_POST_OTP_DEVICES_CONFLICT_OTP_DEVICE_EXISTS,
156 : device_id);
157 : }
158 : } /* end switch (qs) */
159 :
160 4 : qs = TMH_db->insert_otp (TMH_db->cls,
161 4 : mi->settings.id,
162 : device_id,
163 : &tp);
164 4 : if (GNUNET_DB_STATUS_HARD_ERROR == qs)
165 : {
166 0 : TMH_db->rollback (TMH_db->cls);
167 4 : break;
168 : }
169 4 : if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
170 : {
171 4 : qs = TMH_db->commit (TMH_db->cls);
172 4 : if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
173 4 : break;
174 : }
175 0 : retry:
176 0 : GNUNET_assert (GNUNET_DB_STATUS_SOFT_ERROR == qs);
177 0 : TMH_db->rollback (TMH_db->cls);
178 : } /* for RETRIES loop */
179 4 : GNUNET_JSON_parse_free (spec);
180 4 : if (qs < 0)
181 : {
182 0 : GNUNET_break (0);
183 0 : return TALER_MHD_reply_with_error (
184 : connection,
185 : MHD_HTTP_INTERNAL_SERVER_ERROR,
186 : (GNUNET_DB_STATUS_SOFT_ERROR == qs)
187 : ? TALER_EC_GENERIC_DB_SOFT_FAILURE
188 : : TALER_EC_GENERIC_DB_COMMIT_FAILED,
189 : NULL);
190 : }
191 4 : return TALER_MHD_reply_static (connection,
192 : MHD_HTTP_NO_CONTENT,
193 : NULL,
194 : NULL,
195 : 0);
196 : }
197 :
198 :
199 : /* end of taler-merchant-httpd_private-post-otp-devices.c */
|