taler-merchant-httpd_post-private-templates.c (9155B)
1 /* 2 This file is part of TALER 3 (C) 2022-2024 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 src/backend/taler-merchant-httpd_post-private-templates.c 22 * @brief implementing POST /templates request handling 23 * @author Priscilla HUANG 24 */ 25 #include "platform.h" 26 #include "taler-merchant-httpd_post-private-templates.h" 27 #include "taler-merchant-httpd_helper.h" 28 #include <taler/taler_json_lib.h> 29 #include "merchant-database/insert_template.h" 30 #include "merchant-database/lookup_template.h" 31 #include "merchant-database/select_otp_serial.h" 32 33 34 /** 35 * Check if the two templates are identical. 36 * 37 * @param t1 template to compare 38 * @param t2 other template to compare 39 * @return true if they are 'equal', false if not or of payto_uris is not an array 40 */ 41 static bool 42 templates_equal (const struct TALER_MERCHANTDB_TemplateDetails *t1, 43 const struct TALER_MERCHANTDB_TemplateDetails *t2) 44 { 45 return ( (0 == strcmp (t1->template_description, 46 t2->template_description)) && 47 ( ( (NULL == t1->otp_id) && 48 (NULL == t2->otp_id) ) || 49 ( (NULL != t1->otp_id) && 50 (NULL != t2->otp_id) && 51 (0 == strcmp (t1->otp_id, 52 t2->otp_id))) ) && 53 ( ( (NULL == t1->editable_defaults) && 54 (NULL == t2->editable_defaults) ) || 55 ( (NULL != t1->editable_defaults) && 56 (NULL != t2->editable_defaults) && 57 (1 == json_equal (t1->editable_defaults, 58 t2->editable_defaults))) ) && 59 (1 == json_equal (t1->template_contract, 60 t2->template_contract)) ); 61 } 62 63 64 enum MHD_Result 65 TMH_private_post_templates (const struct TMH_RequestHandler *rh, 66 struct MHD_Connection *connection, 67 struct TMH_HandlerContext *hc) 68 { 69 struct TMH_MerchantInstance *mi = hc->instance; 70 struct TALER_MERCHANTDB_TemplateDetails tp = { 0 }; 71 const char *template_id; 72 enum GNUNET_DB_QueryStatus qs; 73 struct GNUNET_JSON_Specification spec[] = { 74 GNUNET_JSON_spec_string ("template_id", 75 &template_id), 76 GNUNET_JSON_spec_string ("template_description", 77 (const char **) &tp.template_description), 78 GNUNET_JSON_spec_mark_optional ( 79 GNUNET_JSON_spec_string ("otp_id", 80 (const char **) &tp.otp_id), 81 NULL), 82 GNUNET_JSON_spec_json ("template_contract", 83 &tp.template_contract), 84 GNUNET_JSON_spec_mark_optional ( 85 GNUNET_JSON_spec_json ("editable_defaults", 86 &tp.editable_defaults), 87 NULL), 88 GNUNET_JSON_spec_end () 89 }; 90 uint64_t otp_serial = 0; 91 92 GNUNET_assert (NULL != mi); 93 { 94 enum GNUNET_GenericReturnValue res; 95 96 res = TALER_MHD_parse_json_data (connection, 97 hc->request_body, 98 spec); 99 if (GNUNET_OK != res) 100 { 101 GNUNET_break_op (0); 102 return (GNUNET_NO == res) 103 ? MHD_YES 104 : MHD_NO; 105 } 106 } 107 if (! TALER_MERCHANT_template_contract_valid (tp.template_contract)) 108 { 109 GNUNET_break_op (0); 110 json_dumpf (tp.template_contract, 111 stderr, 112 JSON_INDENT (2)); 113 GNUNET_JSON_parse_free (spec); 114 return TALER_MHD_reply_with_error (connection, 115 MHD_HTTP_BAD_REQUEST, 116 TALER_EC_GENERIC_PARAMETER_MALFORMED, 117 "template_contract"); 118 } 119 120 if (NULL != tp.editable_defaults) 121 { 122 const char *key; 123 json_t *val; 124 125 json_object_foreach (tp.editable_defaults, key, val) 126 { 127 if (NULL != 128 json_object_get (tp.template_contract, 129 key)) 130 { 131 char *msg; 132 enum MHD_Result ret; 133 134 GNUNET_break_op (0); 135 GNUNET_asprintf (&msg, 136 "editable_defaults::%s conflicts with template_contract", 137 key); 138 GNUNET_JSON_parse_free (spec); 139 ret = TALER_MHD_reply_with_error (connection, 140 MHD_HTTP_BAD_REQUEST, 141 TALER_EC_GENERIC_PARAMETER_MALFORMED, 142 msg); 143 GNUNET_free (msg); 144 return ret; 145 } 146 } 147 } 148 149 if (NULL != tp.otp_id) 150 { 151 qs = TALER_MERCHANTDB_select_otp_serial (TMH_db, 152 mi->settings.id, 153 tp.otp_id, 154 &otp_serial); 155 switch (qs) 156 { 157 case GNUNET_DB_STATUS_HARD_ERROR: 158 case GNUNET_DB_STATUS_SOFT_ERROR: 159 GNUNET_break (0); 160 GNUNET_JSON_parse_free (spec); 161 return TALER_MHD_reply_with_error (connection, 162 MHD_HTTP_INTERNAL_SERVER_ERROR, 163 TALER_EC_GENERIC_DB_STORE_FAILED, 164 "select_otp_serial"); 165 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 166 GNUNET_JSON_parse_free (spec); 167 return TALER_MHD_reply_with_error (connection, 168 MHD_HTTP_NOT_FOUND, 169 TALER_EC_MERCHANT_GENERIC_OTP_DEVICE_UNKNOWN, 170 NULL); 171 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 172 break; 173 } 174 } 175 176 qs = TALER_MERCHANTDB_insert_template (TMH_db, 177 mi->settings.id, 178 template_id, 179 otp_serial, 180 &tp); 181 switch (qs) 182 { 183 case GNUNET_DB_STATUS_HARD_ERROR: 184 case GNUNET_DB_STATUS_SOFT_ERROR: 185 GNUNET_break (0); 186 GNUNET_JSON_parse_free (spec); 187 return TALER_MHD_reply_with_error (connection, 188 MHD_HTTP_INTERNAL_SERVER_ERROR, 189 TALER_EC_GENERIC_DB_STORE_FAILED, 190 NULL); 191 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 192 GNUNET_JSON_parse_free (spec); 193 return TALER_MHD_reply_static (connection, 194 MHD_HTTP_NO_CONTENT, 195 NULL, 196 NULL, 197 0); 198 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 199 break; 200 } 201 202 { 203 /* Test if a template of this id is known */ 204 struct TALER_MERCHANTDB_TemplateDetails etp; 205 206 qs = TALER_MERCHANTDB_lookup_template (TMH_db, 207 mi->settings.id, 208 template_id, 209 &etp); 210 switch (qs) 211 { 212 case GNUNET_DB_STATUS_HARD_ERROR: 213 case GNUNET_DB_STATUS_SOFT_ERROR: 214 /* Clean up and fail hard */ 215 GNUNET_break (0); 216 GNUNET_JSON_parse_free (spec); 217 return TALER_MHD_reply_with_error (connection, 218 MHD_HTTP_INTERNAL_SERVER_ERROR, 219 TALER_EC_GENERIC_DB_FETCH_FAILED, 220 NULL); 221 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 222 GNUNET_break (0); 223 GNUNET_JSON_parse_free (spec); 224 return TALER_MHD_reply_with_error (connection, 225 MHD_HTTP_INTERNAL_SERVER_ERROR, 226 TALER_EC_GENERIC_DB_FETCH_FAILED, 227 "logic error"); 228 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 229 break; 230 } 231 /* idempotency check: is etp == tp? */ 232 { 233 bool eq; 234 235 eq = templates_equal (&tp, 236 &etp); 237 TALER_MERCHANTDB_template_details_free (&etp); 238 GNUNET_JSON_parse_free (spec); 239 return eq 240 ? TALER_MHD_reply_static (connection, 241 MHD_HTTP_NO_CONTENT, 242 NULL, 243 NULL, 244 0) 245 : TALER_MHD_reply_with_error (connection, 246 MHD_HTTP_CONFLICT, 247 TALER_EC_MERCHANT_PRIVATE_POST_TEMPLATES_CONFLICT_TEMPLATE_EXISTS, 248 template_id); 249 } 250 } 251 } 252 253 254 /* end of taler-merchant-httpd_post-private-templates.c */