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