taler-merchant-httpd_private-post-otp-devices.c (6590B)
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 otp_devices_equal (const struct TALER_MERCHANTDB_OtpDeviceDetails *t1, 46 const struct TALER_MERCHANTDB_OtpDeviceDetails *t2) 47 { 48 return ( (0 == strcmp (t1->otp_description, 49 t2->otp_description)) && 50 (0 == strcmp (t1->otp_key, 51 t2->otp_key) ) && 52 (t1->otp_ctr == t2->otp_ctr) && 53 (t1->otp_algorithm == t2->otp_algorithm) ); 54 } 55 56 57 MHD_RESULT 58 TMH_private_post_otp_devices (const struct TMH_RequestHandler *rh, 59 struct MHD_Connection *connection, 60 struct TMH_HandlerContext *hc) 61 { 62 struct TMH_MerchantInstance *mi = hc->instance; 63 struct TALER_MERCHANTDB_OtpDeviceDetails tp = { 0 }; 64 const char *device_id; 65 enum GNUNET_DB_QueryStatus qs; 66 struct GNUNET_JSON_Specification spec[] = { 67 GNUNET_JSON_spec_string ("otp_device_id", 68 &device_id), 69 GNUNET_JSON_spec_string ("otp_device_description", 70 (const char **) &tp.otp_description), 71 TALER_JSON_spec_otp_type ("otp_algorithm", 72 &tp.otp_algorithm), 73 GNUNET_JSON_spec_mark_optional ( 74 GNUNET_JSON_spec_uint64 ("otp_ctr", 75 &tp.otp_ctr), 76 NULL), 77 TALER_JSON_spec_otp_key ("otp_key", 78 (const char **) &tp.otp_key), 79 GNUNET_JSON_spec_end () 80 }; 81 82 GNUNET_assert (NULL != mi); 83 { 84 enum GNUNET_GenericReturnValue res; 85 86 res = TALER_MHD_parse_json_data (connection, 87 hc->request_body, 88 spec); 89 if (GNUNET_OK != res) 90 { 91 GNUNET_break_op (0); 92 return (GNUNET_NO == res) 93 ? MHD_YES 94 : MHD_NO; 95 } 96 } 97 98 /* finally, interact with DB until no serialization error */ 99 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 if (GNUNET_OK != 105 TMH_db->start (TMH_db->cls, 106 "/post otp-devices")) 107 { 108 GNUNET_break (0); 109 GNUNET_JSON_parse_free (spec); 110 return TALER_MHD_reply_with_error (connection, 111 MHD_HTTP_INTERNAL_SERVER_ERROR, 112 TALER_EC_GENERIC_DB_START_FAILED, 113 NULL); 114 } 115 qs = TMH_db->select_otp (TMH_db->cls, 116 mi->settings.id, 117 device_id, 118 &etp); 119 switch (qs) 120 { 121 case GNUNET_DB_STATUS_HARD_ERROR: 122 /* Clean up and fail hard */ 123 GNUNET_break (0); 124 TMH_db->rollback (TMH_db->cls); 125 GNUNET_JSON_parse_free (spec); 126 return TALER_MHD_reply_with_error (connection, 127 MHD_HTTP_INTERNAL_SERVER_ERROR, 128 TALER_EC_GENERIC_DB_FETCH_FAILED, 129 NULL); 130 case GNUNET_DB_STATUS_SOFT_ERROR: 131 /* restart transaction */ 132 goto retry; 133 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 134 /* Good, we can proceed! */ 135 break; 136 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 137 /* idempotency check: is etp == tp? */ 138 { 139 bool eq; 140 141 eq = otp_devices_equal (&tp, 142 &etp); 143 GNUNET_free (etp.otp_description); 144 GNUNET_free (etp.otp_key); 145 TMH_db->rollback (TMH_db->cls); 146 GNUNET_JSON_parse_free (spec); 147 return eq 148 ? TALER_MHD_reply_static (connection, 149 MHD_HTTP_NO_CONTENT, 150 NULL, 151 NULL, 152 0) 153 : 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 qs = TMH_db->insert_otp (TMH_db->cls, 161 mi->settings.id, 162 device_id, 163 &tp); 164 if (GNUNET_DB_STATUS_HARD_ERROR == qs) 165 { 166 TMH_db->rollback (TMH_db->cls); 167 break; 168 } 169 if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) 170 { 171 qs = TMH_db->commit (TMH_db->cls); 172 if (GNUNET_DB_STATUS_SOFT_ERROR != qs) 173 break; 174 } 175 retry: 176 GNUNET_assert (GNUNET_DB_STATUS_SOFT_ERROR == qs); 177 TMH_db->rollback (TMH_db->cls); 178 } /* for RETRIES loop */ 179 GNUNET_JSON_parse_free (spec); 180 if (qs < 0) 181 { 182 GNUNET_break (0); 183 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 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 */