taler-merchant-httpd_post-private-webhooks.c (7530B)
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 src/backend/taler-merchant-httpd_post-private-webhooks.c 22 * @brief implementing POST /webhooks request handling 23 * @author Priscilla HUANG 24 */ 25 #include "platform.h" 26 #include "taler-merchant-httpd_post-private-webhooks.h" 27 #include "taler-merchant-httpd_helper.h" 28 #include <taler/taler_json_lib.h> 29 #include "merchant-database/insert_webhook.h" 30 #include "merchant-database/lookup_webhook.h" 31 #include "merchant-database/start.h" 32 33 /** 34 * How often do we retry the simple INSERT database transaction? 35 */ 36 #define MAX_RETRIES 3 37 38 39 /** 40 * Check if the two webhooks are identical. 41 * 42 * @param w1 webhook to compare 43 * @param w2 other webhook to compare 44 * @return true if they are 'equal', false if not or of payto_uris is not an array 45 */ 46 static bool 47 webhooks_equal (const struct TALER_MERCHANTDB_WebhookDetails *w1, 48 const struct TALER_MERCHANTDB_WebhookDetails *w2) 49 { 50 return ( (0 == strcmp (w1->event_type, 51 w2->event_type)) && 52 (0 == strcmp (w1->url, 53 w2->url)) && 54 (0 == strcmp (w1->http_method, 55 w2->http_method)) && 56 ( ( (NULL == w1->header_template) && 57 (NULL == w2->header_template) ) || 58 ( (NULL != w1->header_template) && 59 (NULL != w2->header_template) && 60 (0 == strcmp (w1->header_template, 61 w2->header_template)) ) ) && 62 ( ( (NULL == w1->body_template) && 63 (NULL == w2->body_template) ) || 64 ( (NULL != w1->body_template) && 65 (NULL != w2->body_template) && 66 (0 == strcmp (w1->body_template, 67 w2->body_template)) ) ) ); 68 } 69 70 71 enum MHD_Result 72 TMH_private_post_webhooks (const struct TMH_RequestHandler *rh, 73 struct MHD_Connection *connection, 74 struct TMH_HandlerContext *hc) 75 { 76 struct TMH_MerchantInstance *mi = hc->instance; 77 struct TALER_MERCHANTDB_WebhookDetails wb = { 0 }; 78 const char *webhook_id; 79 enum GNUNET_DB_QueryStatus qs; 80 struct GNUNET_JSON_Specification spec[] = { 81 GNUNET_JSON_spec_string ("webhook_id", 82 &webhook_id), 83 GNUNET_JSON_spec_string ("event_type", 84 (const char **) &wb.event_type), 85 TALER_JSON_spec_web_url ("url", 86 (const char **) &wb.url), 87 GNUNET_JSON_spec_string ("http_method", 88 (const char **) &wb.http_method), 89 GNUNET_JSON_spec_mark_optional ( 90 GNUNET_JSON_spec_string ("header_template", 91 (const char **) &wb.header_template), 92 NULL), 93 GNUNET_JSON_spec_mark_optional ( 94 GNUNET_JSON_spec_string ("body_template", 95 (const char **) &wb.body_template), 96 NULL), 97 GNUNET_JSON_spec_end () 98 }; 99 100 GNUNET_assert (NULL != mi); 101 { 102 enum GNUNET_GenericReturnValue res; 103 104 res = TALER_MHD_parse_json_data (connection, 105 hc->request_body, 106 spec); 107 if (GNUNET_OK != res) 108 { 109 GNUNET_break_op (0); 110 return (GNUNET_NO == res) 111 ? MHD_YES 112 : MHD_NO; 113 } 114 } 115 116 117 /* finally, interact with DB until no serialization error */ 118 for (unsigned int i = 0; i<MAX_RETRIES; i++) 119 { 120 /* Test if a webhook of this id is known */ 121 struct TALER_MERCHANTDB_WebhookDetails ewb; 122 123 if (GNUNET_OK != 124 TALER_MERCHANTDB_start (TMH_db, 125 "/post webhooks")) 126 { 127 GNUNET_break (0); 128 GNUNET_JSON_parse_free (spec); 129 return TALER_MHD_reply_with_error (connection, 130 MHD_HTTP_INTERNAL_SERVER_ERROR, 131 TALER_EC_GENERIC_DB_START_FAILED, 132 NULL); 133 } 134 qs = TALER_MERCHANTDB_lookup_webhook (TMH_db, 135 mi->settings.id, 136 webhook_id, 137 &ewb); 138 switch (qs) 139 { 140 case GNUNET_DB_STATUS_HARD_ERROR: 141 /* Clean up and fail hard */ 142 GNUNET_break (0); 143 TALER_MERCHANTDB_rollback (TMH_db); 144 GNUNET_JSON_parse_free (spec); 145 return TALER_MHD_reply_with_error (connection, 146 MHD_HTTP_INTERNAL_SERVER_ERROR, 147 TALER_EC_GENERIC_DB_FETCH_FAILED, 148 NULL); 149 case GNUNET_DB_STATUS_SOFT_ERROR: 150 /* restart transaction */ 151 goto retry; 152 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 153 /* Good, we can proceed! */ 154 break; 155 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 156 /* idempotency check: is ewb == wb? */ 157 { 158 bool eq; 159 160 eq = webhooks_equal (&wb, 161 &ewb); 162 TALER_MERCHANTDB_webhook_details_free (&ewb); 163 TALER_MERCHANTDB_rollback (TMH_db); 164 GNUNET_JSON_parse_free (spec); 165 return eq 166 ? TALER_MHD_reply_static (connection, 167 MHD_HTTP_NO_CONTENT, 168 NULL, 169 NULL, 170 0) 171 : TALER_MHD_reply_with_error (connection, 172 MHD_HTTP_CONFLICT, 173 TALER_EC_MERCHANT_PRIVATE_POST_WEBHOOKS_CONFLICT_WEBHOOK_EXISTS, 174 webhook_id); 175 } 176 } /* end switch (qs) */ 177 178 qs = TALER_MERCHANTDB_insert_webhook (TMH_db, 179 mi->settings.id, 180 webhook_id, 181 &wb); 182 if (GNUNET_DB_STATUS_HARD_ERROR == qs) 183 { 184 TALER_MERCHANTDB_rollback (TMH_db); 185 break; 186 } 187 if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) 188 { 189 qs = TALER_MERCHANTDB_commit (TMH_db); 190 if (GNUNET_DB_STATUS_SOFT_ERROR != qs) 191 break; 192 } 193 retry: 194 GNUNET_assert (GNUNET_DB_STATUS_SOFT_ERROR == qs); 195 TALER_MERCHANTDB_rollback (TMH_db); 196 } /* for RETRIES loop */ 197 GNUNET_JSON_parse_free (spec); 198 if (qs < 0) 199 { 200 GNUNET_break (0); 201 return TALER_MHD_reply_with_error ( 202 connection, 203 MHD_HTTP_INTERNAL_SERVER_ERROR, 204 (GNUNET_DB_STATUS_SOFT_ERROR == qs) 205 ? TALER_EC_GENERIC_DB_SOFT_FAILURE 206 : TALER_EC_GENERIC_DB_COMMIT_FAILED, 207 NULL); 208 } 209 return TALER_MHD_reply_static (connection, 210 MHD_HTTP_NO_CONTENT, 211 NULL, 212 NULL, 213 0); 214 } 215 216 217 /* end of taler-merchant-httpd_post-private-webhooks.c */