Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2020, 2023, 2024 Taler Systems SA
4 :
5 : TALER is free software; you can redistribute it and/or modify it under the
6 : terms of the GNU General Public License as published by the Free Software
7 : Foundation; either version 3, or (at your option) any later version.
8 :
9 : TALER is distributed in the hope that it will be useful, but WITHOUT ANY
10 : WARRANTY; without even the implied warranty of EXCHANGEABILITY or FITNESS FOR
11 : A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 :
13 : You should have received a copy of the GNU General Public License along with
14 : TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
15 : */
16 : /**
17 : * @file mhd_spa.c
18 : * @brief logic to load single page apps
19 : * @author Christian Grothoff
20 : */
21 : #include "platform.h"
22 : #include <gnunet/gnunet_util_lib.h>
23 : #include "taler_util.h"
24 : #include "taler_mhd_lib.h"
25 : #include <gnunet/gnunet_mhd_compat.h>
26 :
27 :
28 : /**
29 : * Resource from the WebUi.
30 : */
31 : struct WebuiFile
32 : {
33 : /**
34 : * Kept in a DLL.
35 : */
36 : struct WebuiFile *next;
37 :
38 : /**
39 : * Kept in a DLL.
40 : */
41 : struct WebuiFile *prev;
42 :
43 : /**
44 : * Path this resource matches.
45 : */
46 : char *path;
47 :
48 : /**
49 : * SPA resource, compressed.
50 : */
51 : struct MHD_Response *zresponse;
52 :
53 : /**
54 : * SPA resource, vanilla.
55 : */
56 : struct MHD_Response *response;
57 :
58 : };
59 :
60 :
61 : /**
62 : * Resource from the WebUi.
63 : */
64 : struct TALER_MHD_Spa
65 : {
66 : /**
67 : * Resources of the WebUI, kept in a DLL.
68 : */
69 : struct WebuiFile *webui_head;
70 :
71 : /**
72 : * Resources of the WebUI, kept in a DLL.
73 : */
74 : struct WebuiFile *webui_tail;
75 : };
76 :
77 :
78 : MHD_RESULT
79 0 : TALER_MHD_spa_handler (const struct TALER_MHD_Spa *spa,
80 : struct MHD_Connection *connection,
81 : const char *path)
82 : {
83 0 : struct WebuiFile *w = NULL;
84 :
85 0 : if ( (NULL == path) ||
86 0 : (0 == strcmp (path,
87 : "")) )
88 0 : path = "index.html";
89 0 : for (struct WebuiFile *pos = spa->webui_head;
90 0 : NULL != pos;
91 0 : pos = pos->next)
92 0 : if (0 == strcmp (path,
93 0 : pos->path))
94 : {
95 0 : w = pos;
96 0 : break;
97 : }
98 0 : if (NULL == w)
99 0 : return TALER_MHD_reply_with_error (connection,
100 : MHD_HTTP_NOT_FOUND,
101 : TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
102 : path);
103 0 : if ( (MHD_YES ==
104 0 : TALER_MHD_can_compress (connection)) &&
105 0 : (NULL != w->zresponse) )
106 0 : return MHD_queue_response (connection,
107 : MHD_HTTP_OK,
108 : w->zresponse);
109 0 : return MHD_queue_response (connection,
110 : MHD_HTTP_OK,
111 : w->response);
112 : }
113 :
114 :
115 : /**
116 : * Function called on each file to load for the WebUI.
117 : *
118 : * @param cls the `struct TALER_MHD_Spa *` to build
119 : * @param dn name of the file to load
120 : */
121 : static enum GNUNET_GenericReturnValue
122 840 : build_webui (void *cls,
123 : const char *dn)
124 : {
125 : static const struct
126 : {
127 : const char *ext;
128 : const char *mime;
129 : } mime_map[] = {
130 : {
131 : .ext = "css",
132 : .mime = "text/css"
133 : },
134 : {
135 : .ext = "html",
136 : .mime = "text/html"
137 : },
138 : {
139 : .ext = "js",
140 : .mime = "text/javascript"
141 : },
142 : {
143 : .ext = "jpg",
144 : .mime = "image/jpeg"
145 : },
146 : {
147 : .ext = "jpeg",
148 : .mime = "image/jpeg"
149 : },
150 : {
151 : .ext = "png",
152 : .mime = "image/png"
153 : },
154 : {
155 : .ext = "svg",
156 : .mime = "image/svg+xml"
157 : },
158 : {
159 : .ext = NULL,
160 : .mime = NULL
161 : },
162 : };
163 840 : struct TALER_MHD_Spa *spa = cls;
164 : int fd;
165 : struct stat sb;
166 840 : struct MHD_Response *zresponse = NULL;
167 : struct MHD_Response *response;
168 : const char *ext;
169 : const char *mime;
170 :
171 840 : fd = open (dn,
172 : O_RDONLY);
173 840 : if (-1 == fd)
174 : {
175 0 : GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
176 : "open",
177 : dn);
178 0 : return GNUNET_SYSERR;
179 : }
180 840 : if (0 !=
181 840 : fstat (fd,
182 : &sb))
183 : {
184 0 : GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
185 : "open",
186 : dn);
187 0 : GNUNET_break (0 == close (fd));
188 0 : return GNUNET_SYSERR;
189 : }
190 :
191 840 : mime = NULL;
192 840 : ext = strrchr (dn, '.');
193 840 : if (NULL == ext)
194 : {
195 78 : GNUNET_break (0 == close (fd));
196 78 : return GNUNET_OK;
197 : }
198 762 : ext++;
199 4620 : for (unsigned int i = 0; NULL != mime_map[i].ext; i++)
200 4164 : if (0 == strcasecmp (ext,
201 4164 : mime_map[i].ext))
202 : {
203 306 : mime = mime_map[i].mime;
204 306 : break;
205 : }
206 :
207 : {
208 : void *in;
209 : ssize_t r;
210 : size_t csize;
211 :
212 762 : in = GNUNET_malloc_large (sb.st_size);
213 762 : if (NULL == in)
214 : {
215 0 : GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
216 : "malloc");
217 0 : GNUNET_break (0 == close (fd));
218 0 : return GNUNET_SYSERR;
219 : }
220 762 : r = read (fd,
221 : in,
222 762 : sb.st_size);
223 762 : if ( (-1 == r) ||
224 762 : (sb.st_size != (size_t) r) )
225 : {
226 0 : GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
227 : "read",
228 : dn);
229 0 : GNUNET_free (in);
230 0 : GNUNET_break (0 == close (fd));
231 0 : return GNUNET_SYSERR;
232 : }
233 762 : csize = (size_t) r;
234 762 : if (MHD_YES ==
235 762 : TALER_MHD_body_compress (&in,
236 : &csize))
237 : {
238 663 : zresponse = MHD_create_response_from_buffer (csize,
239 : in,
240 : MHD_RESPMEM_MUST_FREE);
241 663 : if (NULL != zresponse)
242 : {
243 663 : if (MHD_NO ==
244 663 : MHD_add_response_header (zresponse,
245 : MHD_HTTP_HEADER_CONTENT_ENCODING,
246 : "deflate"))
247 : {
248 0 : GNUNET_break (0);
249 0 : MHD_destroy_response (zresponse);
250 0 : zresponse = NULL;
251 : }
252 663 : if (NULL != mime)
253 306 : GNUNET_break (MHD_YES ==
254 : MHD_add_response_header (zresponse,
255 : MHD_HTTP_HEADER_CONTENT_TYPE,
256 : mime));
257 : }
258 : }
259 : else
260 : {
261 99 : GNUNET_free (in);
262 : }
263 : }
264 :
265 762 : response = MHD_create_response_from_fd (sb.st_size,
266 : fd);
267 762 : if (NULL == response)
268 : {
269 0 : GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
270 : "open",
271 : dn);
272 0 : GNUNET_break (0 == close (fd));
273 0 : if (NULL != zresponse)
274 : {
275 0 : MHD_destroy_response (zresponse);
276 0 : zresponse = NULL;
277 : }
278 0 : return GNUNET_SYSERR;
279 : }
280 762 : if (NULL != mime)
281 306 : GNUNET_break (MHD_YES ==
282 : MHD_add_response_header (response,
283 : MHD_HTTP_HEADER_CONTENT_TYPE,
284 : mime));
285 :
286 : {
287 : struct WebuiFile *w;
288 : const char *fn;
289 :
290 762 : fn = strrchr (dn, '/');
291 762 : GNUNET_assert (NULL != fn);
292 762 : w = GNUNET_new (struct WebuiFile);
293 762 : w->path = GNUNET_strdup (fn + 1);
294 762 : w->response = response;
295 762 : w->zresponse = zresponse;
296 762 : GNUNET_CONTAINER_DLL_insert (spa->webui_head,
297 : spa->webui_tail,
298 : w);
299 : }
300 762 : return GNUNET_OK;
301 : }
302 :
303 :
304 : struct TALER_MHD_Spa *
305 78 : TALER_MHD_spa_load (const struct GNUNET_OS_ProjectData *pd,
306 : const char *dir)
307 : {
308 : struct TALER_MHD_Spa *spa;
309 : char *dn;
310 :
311 : {
312 : char *path;
313 :
314 78 : path = GNUNET_OS_installation_get_path (pd,
315 : GNUNET_OS_IPK_DATADIR);
316 78 : if (NULL == path)
317 : {
318 0 : GNUNET_break (0);
319 0 : return NULL;
320 : }
321 78 : GNUNET_asprintf (&dn,
322 : "%s%s",
323 : path,
324 : dir);
325 78 : GNUNET_free (path);
326 : }
327 78 : spa = GNUNET_new (struct TALER_MHD_Spa);
328 78 : if (-1 ==
329 78 : GNUNET_DISK_directory_scan (dn,
330 : &build_webui,
331 : spa))
332 : {
333 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
334 : "Failed to load WebUI from `%s'\n",
335 : dn);
336 0 : GNUNET_free (dn);
337 0 : TALER_MHD_spa_free (spa);
338 0 : return NULL;
339 : }
340 78 : GNUNET_free (dn);
341 78 : return spa;
342 : }
343 :
344 :
345 : void
346 78 : TALER_MHD_spa_free (struct TALER_MHD_Spa *spa)
347 : {
348 : struct WebuiFile *w;
349 :
350 840 : while (NULL != (w = spa->webui_head))
351 : {
352 762 : GNUNET_CONTAINER_DLL_remove (spa->webui_head,
353 : spa->webui_tail,
354 : w);
355 762 : if (NULL != w->response)
356 : {
357 762 : MHD_destroy_response (w->response);
358 762 : w->response = NULL;
359 : }
360 762 : if (NULL != w->zresponse)
361 : {
362 663 : MHD_destroy_response (w->zresponse);
363 663 : w->zresponse = NULL;
364 : }
365 762 : GNUNET_free (w->path);
366 762 : GNUNET_free (w);
367 : }
368 78 : GNUNET_free (spa);
369 78 : }
|