Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
http_server.c
Go to the documentation of this file.
1/*
2 * This file is part of darktable,
3 * Copyright (C) 2015-2016 Roman Lebedev.
4 * Copyright (C) 2015-2016 Tobias Ellinghaus.
5 * Copyright (C) 2020 Pascal Obry.
6 * Copyright (C) 2022 Aurélien PIERRE.
7 * Copyright (C) 2022 Martin Bařinka.
8 * Copyright (C) 2023 Luca Zulberti.
9 * Copyright (C) 2023 Maurizio Paglia.
10 * Copyright (C) 2025 starapo7348.
11 *
12 * darktable is free software: you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation, either version 3 of the License, or
15 * (at your option) any later version.
16 *
17 * darktable is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with darktable. If not, see <http://www.gnu.org/licenses/>.
24 */
25
26#include <glib/gi18n.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include "system/macros.h"
30#include "system/mem_alloc.h"
31#include "common/logging.h"
32#include "common/http_server.h"
33#ifndef LIBSOUP_VERSION_MAJOR
34#error "LIBSOUP_VERSION_MAJOR not defined by CMake"
35#endif
36#if LIBSOUP_VERSION_MAJOR >= 3
37#define LIBSOUP3
38#else
39#define LIBSOUP2
40#endif
41
49
50static const char reply[]
51 = "<!DOCTYPE html>\n"
52 "<html>\n"
53 "<head>\n"
54 "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n"
55 "<title>%s</title>\n"
56 "<style>\n"
57 "html {\n"
58 " background-color: #575656;\n"
59 " font-family: \"Lucida Grande\",Verdana,\"Bitstream Vera Sans\",Arial,sans-serif;\n"
60 " font-size: 12px;\n"
61 " padding: 50px 100px 50px 100px;\n"
62 "}\n"
63 "#content {\n"
64 " background-color: #cfcece;\n"
65 " border: 1px solid #000;\n"
66 " padding: 0px 40px 40px 40px;\n"
67 "}\n"
68 "</style>\n"
69 "<script>\n"
70 " if(window.location.hash && %d) {\n"
71 " var hash = window.location.hash.substring(1);\n"
72 " window.location.search = hash;\n"
73 " }\n"
74 "</script>\n"
75 "</head>\n"
76 "<body><div id=\"content\">\n"
77 "<div style=\"font-size: 42pt; font-weight: bold; color: white; text-align: right;\">%s</div>\n"
78 "%s\n"
79 "</div>\n"
80 "</body>\n"
81 "</html>";
82
83// Libsoup3: Delayed kill helper
84#ifdef LIBSOUP3
85static gboolean _delayed_kill(gpointer user_data)
86{
88 return G_SOURCE_REMOVE;
89}
90#endif
91
92#ifdef LIBSOUP2
93// Libsoup2 callbacks
94static void _request_finished_callback(SoupServer *server, SoupMessage *message, SoupClientContext *client,
95 gpointer user_data)
96{
98}
99
100static void _new_connection(SoupServer *server, SoupMessage *msg, const char *path, GHashTable *query,
101 SoupClientContext *client, gpointer user_data)
102{
103 _connection_t *params = (_connection_t *)user_data;
104 gboolean res = TRUE;
105
106 if(msg->method != SOUP_METHOD_GET)
107 {
108 soup_message_set_status(msg, SOUP_STATUS_NOT_IMPLEMENTED);
109 goto end;
110 }
111
112 char *page_title = g_strdup_printf(_("ansel >> %s"), params->id);
113 const char *title = _(params->id);
114 const char *body = _("<h1>Sorry,</h1><p>something went wrong. Please try again.</p>");
115
116 res = params->callback(query, params->user_data);
117
118 if(res)
119 body = _("<h1>Thank you,</h1><p>everything should have worked, you can <b>close</b> your browser now and "
120 "<b>go back</b> to Ansel.</p>");
121
122 char *resp_body = g_strdup_printf(reply, page_title, res ? 0 : 1, title, body);
123 size_t resp_length = strlen(resp_body);
124 dt_free(page_title);
125
126 soup_message_set_status(msg, SOUP_STATUS_OK);
127 soup_message_set_response(msg, "text/html", SOUP_MEMORY_TAKE, resp_body, resp_length);
128
129end:
130 if(res)
131 {
132 dt_http_server_t *http_server = params->server;
133 soup_server_remove_handler(server, path);
134 g_signal_connect(G_OBJECT(server), "request-finished", G_CALLBACK(_request_finished_callback), http_server);
135 }
136}
137#endif // LIBSOUP2
138
139#ifdef LIBSOUP3
140// Libsoup3 callbacks
141static void _new_connection(SoupServer *server, SoupServerMessage *msg, gpointer user_data)
142{
143 _connection_t *params = (_connection_t *)user_data;
144
145 if(g_strcmp0(soup_server_message_get_method(msg), SOUP_METHOD_GET) != 0)
146 {
147 soup_server_message_set_status(msg, SOUP_STATUS_NOT_IMPLEMENTED, "Not Implemented");
148 return;
149 }
150
151 GHashTable *query = g_hash_table_new_full(g_str_hash, g_str_equal, dt_free_gpointer, dt_free_gpointer);
152
153 gboolean res = params->callback(query, params->user_data);
154 g_hash_table_unref(query);
155
156 const char *body = res ?
157 _("<h1>Thank you,</h1><p>everything should have worked, you can <b>close</b> your browser now and "
158 "<b>go back</b> to Ansel.</p>") :
159 _("<h1>Sorry,</h1><p>something went wrong. Please try again.</p>");
160
161 char *page_title = g_strdup_printf(_("ansel >> %s"), params->id);
162 char *resp_body = g_strdup_printf(reply, page_title, res ? 0 : 1, _(params->id), body);
163 dt_free(page_title);
164
165 soup_server_message_set_status(msg, SOUP_STATUS_OK, "OK");
166 soup_server_message_set_response(msg, "text/html; charset=utf-8",
167 SOUP_MEMORY_TAKE, resp_body, strlen(resp_body));
168
169 if(res)
170 g_timeout_add(100, _delayed_kill, params->server);
171}
172#endif // LIBSOUP3
173
174dt_http_server_t *dt_http_server_create(const int *ports, const int n_ports, const char *id,
175 const dt_http_server_callback callback, gpointer user_data)
176{
177 SoupServer *httpserver = NULL;
178 int port = 0;
179
180#ifdef LIBSOUP2
181
182 dt_print(DT_DEBUG_CONTROL, "[http server] using libsoup2\n");
183
184 httpserver = soup_server_new(SOUP_SERVER_SERVER_HEADER, "ansel internal server", NULL);
185 if(IS_NULL_PTR(httpserver))
186 {
187 fprintf(stderr, "error: couldn't create libsoup httpserver\n");
188 return NULL;
189 }
190
191 for(int i = 0; i < n_ports; i++)
192 {
193 port = ports[i];
194 if(soup_server_listen_local(httpserver, port, 0, NULL)) break;
195 port = 0;
196 }
197 if(port == 0)
198 {
199 g_object_unref(httpserver);
200 fprintf(stderr, "error: can't bind to any port from our pool\n");
201 return NULL;
202 }
203
204#ifdef LIBSOUP2
205
206#elif defined(LIBSOUP3)
207
208SoupServerListener *listener = soup_server_get_listener(httpserver);
209if (listener) {
210 GMainContext *ctx = g_main_context_default();
211 soup_server_listener_set_context(listener, ctx);
212}
213soup_server_run(httpserver);
214#endif
215
216#elif defined(LIBSOUP3)
217 // Libsoup3
218 dt_print(DT_DEBUG_CONTROL, "[http server] using libsoup3\n");
219
220 httpserver = soup_server_new("server-header", "ansel internal server", NULL);
221 if(IS_NULL_PTR(httpserver))
222 {
223 fprintf(stderr, "error: couldn't create libsoup httpserver\n");
224 return NULL;
225 }
226
227 for(int i = 0; i < n_ports; i++)
228 {
229 port = ports[i];
230 if(soup_server_listen_local(httpserver, port, SOUP_SERVER_LISTEN_IPV4_ONLY, NULL)) break;
231 port = 0;
232 }
233 if(port == 0)
234 {
235 g_object_unref(httpserver);
236 fprintf(stderr, "error: can't bind to any port from our pool\n");
237 return NULL;
238 }
239#endif
240
241 dt_http_server_t *server = g_new0(dt_http_server_t, 1);
242 server->server = httpserver;
243
244 _connection_t *params = g_new0(_connection_t, 1);
245 params->id = id;
246 params->server = server;
247 params->callback = callback;
248 params->user_data = user_data;
249
250 char *path = g_strdup_printf("/%s", id);
251 server->url = g_strdup_printf("http://localhost:%d%s", port, path);
252
253#ifdef LIBSOUP2
254 soup_server_add_handler(httpserver, path, _new_connection, params, (GDestroyNotify)free);
255#else
256 soup_server_add_early_handler(httpserver, path, (SoupServerCallback)_new_connection, params, (GDestroyNotify)g_free);
257#endif
258
259 dt_free(path);
260
261 dt_print(DT_DEBUG_CONTROL, "[http server] listening on %s\n", server->url);
262 return server;
263}
264
266{
267 if(server && server->server)
268 {
269 soup_server_disconnect(server->server);
270 g_object_unref(server->server);
271 server->server = NULL;
272 }
273 dt_free(server->url);
274 dt_free(server);
275}
276
277// clang-format off
278// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
279// vim: shiftwidth=2 expandtab tabstop=2 cindent
280// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
281// clang-format on
#define TRUE
Definition ashift_lsd.c:162
const int res
Definition dtpthread.h:351
void dt_http_server_kill(dt_http_server_t *server)
static void _request_finished_callback(SoupServer *server, SoupMessage *message, SoupClientContext *client, gpointer user_data)
Definition http_server.c:94
static void _new_connection(SoupServer *server, SoupMessage *msg, const char *path, GHashTable *query, SoupClientContext *client, gpointer user_data)
static const char reply[]
Definition http_server.c:51
dt_http_server_t * dt_http_server_create(const int *ports, const int n_ports, const char *id, const dt_http_server_callback callback, gpointer user_data)
gboolean(* dt_http_server_callback)(GHashTable *query, gpointer user_data)
Definition http_server.h:29
@ DT_DEBUG_CONTROL
Definition logging.h:52
void dt_print(dt_debug_thread_t thread, const char *msg,...) __attribute__((format(printf
Print to stdout when thread is enabled, prefixed with seconds since startup.
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition macros.h:96
static void dt_free_gpointer(gpointer ptr)
g_free() one pointer, with the signature GDestroyNotify wants.
Definition mem_alloc.h:184
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
Definition mem_alloc.h:171
const char * id
Definition http_server.c:44
gpointer user_data
Definition http_server.c:47
dt_http_server_callback callback
Definition http_server.c:46
dt_http_server_t * server
Definition http_server.c:45
SoupServer * server
Definition http_server.h:33