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