Line data Source code
1 : /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2 : /*
3 : * server.c
4 : * This file is part of "Sauvegarde" project.
5 : *
6 : * (C) Copyright 2014 - 2016 Olivier Delhomme
7 : * e-mail : olivier.delhomme@free.fr
8 : *
9 : * "Sauvegarde" is free software: you can redistribute it and/or modify
10 : * it under the terms of the GNU General Public License as published by
11 : * the Free Software Foundation, either version 3 of the License, or
12 : * (at your option) any later version.
13 : *
14 : * "Sauvegarde" is distributed in the hope that it will be useful,
15 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : * GNU General Public License for more details.
18 : *
19 : * You should have received a copy of the GNU General Public License
20 : * along with "Sauvegarde". If not, see <http://www.gnu.org/licenses/>
21 : */
22 :
23 : /**
24 : * @file server.c
25 : * This file contains all the stuff for the cdpfglserver program of "Sauvegarde"
26 : * project. The aim of this program is to save every checksum and data and
27 : * meta data of every 'client' program that is connected to.
28 : */
29 :
30 : #include "server.h"
31 :
32 : static void free_server_struct_t(server_struct_t *server_struct);
33 : static gboolean int_signal_handler(gpointer user_data);
34 : static server_struct_t *init_server_main_structure(int argc, char **argv);
35 : static gchar *get_data_from_a_specific_hash(server_struct_t *server_struct, gchar *hash);
36 : static gchar *get_argument_value_from_key(struct MHD_Connection *connection, gchar *key, gboolean encoded);
37 : static gchar *get_a_list_of_files(server_struct_t *server_struct, struct MHD_Connection *connection);
38 : static gchar *get_json_answer(server_struct_t *server_struct, struct MHD_Connection *connection, const char *url);
39 : static gchar *get_unformatted_answer(server_struct_t *server_struct, const char *url);
40 : static int process_get_request(server_struct_t *server_struct, struct MHD_Connection *connection, const char *url, void **con_cls);
41 : static int answer_meta_json_post_request(server_struct_t *server_struct, struct MHD_Connection *connection, gchar *received_data);
42 : static int process_received_data(server_struct_t *server_struct, struct MHD_Connection *connection, const char *url, gchar *received_data);
43 : static int process_post_request(server_struct_t *server_struct, struct MHD_Connection *connection, const char *url, void **con_cls, const char *upload_data, size_t *upload_data_size);
44 : static int print_out_key(void *cls, enum MHD_ValueKind kind, const char *key, const char *value);
45 : static void print_headers(struct MHD_Connection *connection);
46 : static int ahc(void *cls, struct MHD_Connection *connection, const char *url, const char *method, const char *version, const char *upload_data, size_t *upload_data_size, void **con_cls);
47 : static gpointer meta_data_thread(gpointer user_data);
48 : static gpointer data_thread(gpointer user_data);
49 :
50 :
51 : /**
52 : * Frees server's structure
53 : * @param server_struct is the structure to be freed
54 : */
55 1 : void free_server_struct_t(server_struct_t *server_struct)
56 : {
57 :
58 1 : if (server_struct != NULL)
59 : {
60 1 : MHD_stop_daemon(server_struct->d);
61 1 : print_debug(_("\tMHD daemon stopped.\n"));
62 1 : free_variable(server_struct->backend); /** we need a backend function to be called to free th backend structure */
63 1 : print_debug(_("\tbackend variable freed.\n"));
64 1 : g_thread_unref(server_struct->data_thread);
65 1 : print_debug(_("\tdata thread unrefed.\n"));
66 1 : g_thread_unref(server_struct->meta_thread);
67 1 : print_debug(_("\tmeta thread unrefed.\n"));
68 1 : free_options_t(server_struct->opt);
69 1 : print_debug(_("\toption structure freed.\n"));
70 1 : free_variable(server_struct);
71 1 : print_debug(_("\tmain structure freed.\n"));
72 : }
73 1 : }
74 :
75 :
76 : /**
77 : * SIGINT signal handler function
78 : * @param user_data is a gpointer that MUST be a pointer to the
79 : * server_struct_t *
80 : * @returns FALSE if user_data is NULL and frees memory and exits if TRUE.
81 : */
82 1 : static gboolean int_signal_handler(gpointer user_data)
83 : {
84 1 : server_struct_t *server_struct = (server_struct_t *) user_data;
85 :
86 1 : if (server_struct != NULL)
87 : {
88 1 : print_debug(_("\nEnding the program:\n"));
89 1 : g_main_loop_quit(server_struct->loop);
90 1 : print_debug(_("\tMain loop exited.\n"));
91 1 : free_server_struct_t(server_struct);
92 : }
93 :
94 : /** we can remove the handler as we are exiting the program anyway */
95 1 : return FALSE;
96 : }
97 :
98 :
99 : /**
100 : * Inits main server's structure
101 : * @param argc : number of arguments given on the command line.
102 : * @param argv : an array of strings that contains command line arguments.
103 : * @returns a server_struct_t * structure that contains everything that is
104 : * needed for 'cdpfglserver' program.
105 : */
106 4 : static server_struct_t *init_server_main_structure(int argc, char **argv)
107 : {
108 4 : server_struct_t *server_struct = NULL; /** main structure for 'server' program. */
109 :
110 4 : server_struct = (server_struct_t *) g_malloc0(sizeof(server_struct_t));
111 :
112 4 : server_struct->data_thread = NULL;
113 4 : server_struct->meta_thread = NULL;
114 4 : server_struct->opt = do_what_is_needed_from_command_line_options(argc, argv);
115 1 : server_struct->d = NULL; /* libmicrohttpd daemon pointer */
116 1 : server_struct->meta_queue = g_async_queue_new();
117 1 : server_struct->data_queue = g_async_queue_new();
118 1 : server_struct->loop = NULL;
119 :
120 : /* default backend (file_backend) */
121 1 : server_struct->backend = init_backend_structure(file_store_smeta, file_store_data, file_init_backend, file_build_needed_hash_list, file_get_list_of_files, file_retrieve_data);
122 :
123 1 : return server_struct;
124 : }
125 :
126 :
127 : /**
128 : * Function that gets the data of a specific hash
129 : * @param server_struct is the main structure for the server.
130 : * @param hash is the hash (in hex format) of which we want the data.
131 : * @returns a json formatted string.
132 : */
133 0 : static gchar *get_data_from_a_specific_hash(server_struct_t *server_struct, gchar *hash)
134 : {
135 0 : gchar *answer = NULL;
136 0 : backend_t *backend = NULL;
137 0 : hash_data_t *hash_data = NULL;
138 :
139 0 : if (server_struct != NULL && server_struct->backend != NULL)
140 : {
141 0 : backend = server_struct->backend;
142 :
143 0 : if (backend->retrieve_data != NULL)
144 : {
145 0 : hash_data = backend->retrieve_data(server_struct, hash);
146 0 : answer = convert_hash_data_t_to_string(hash_data);
147 0 : free_hash_data_t(hash_data);
148 :
149 0 : if (answer == NULL)
150 : {
151 0 : answer = g_strdup_printf("Error while trying to get data from hash %s", hash);
152 : }
153 : }
154 : else
155 : {
156 0 : answer = g_strdup(_("This backend's missing a retrieve_data function!"));
157 : }
158 : }
159 : else
160 : {
161 0 : answer = g_strdup(_("Something's wrong with server's initialisation!"));
162 : }
163 :
164 0 : return answer;
165 : }
166 :
167 :
168 : /**
169 : * Function that gets the argument corresponding to the key 'key' in the
170 : * url (from connection)
171 : * @param connection is the connection in MHD
172 : * @param key the key to look for into the url
173 : * @param encoded is a boolean that is TRUE if value is base64 encoded
174 : * @returns a gchar * string that may be freed when no longer needed
175 : */
176 0 : static gchar *get_argument_value_from_key(struct MHD_Connection *connection, gchar *key, gboolean encoded)
177 : {
178 0 : const char *value = NULL;
179 0 : gchar *value_dup = NULL;
180 0 : gsize len = 0;
181 :
182 0 : if (connection != NULL && key != NULL)
183 : {
184 0 : value = MHD_lookup_connection_value(connection, MHD_GET_ARGUMENT_KIND, key);
185 :
186 0 : if (value != NULL)
187 : {
188 0 : if (encoded == TRUE)
189 : {
190 0 : value_dup = (gchar *) g_base64_decode(value, &len);
191 : }
192 : else
193 : {
194 0 : value_dup = g_strdup(value);
195 : }
196 : }
197 : }
198 :
199 0 : return value_dup;
200 : }
201 :
202 :
203 : /**
204 : * Function to get a list of saved files.
205 : * @param server_struct is the main structure for the server.
206 : * @param connection is the connection in MHD
207 : * @param url is the requested url
208 : * @returns a json formatted string or NULL
209 : */
210 0 : static gchar *get_a_list_of_files(server_struct_t *server_struct, struct MHD_Connection *connection)
211 : {
212 0 : gchar *answer = NULL;
213 0 : backend_t *backend = NULL;
214 0 : query_t *query = NULL;
215 :
216 :
217 0 : if (server_struct != NULL && server_struct->backend != NULL)
218 : {
219 0 : backend = server_struct->backend;
220 :
221 0 : if (backend->get_list_of_files != NULL)
222 : {
223 0 : query = init_query_t(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
224 :
225 0 : query->hostname = get_argument_value_from_key(connection, "hostname", FALSE);
226 0 : query->uid = get_argument_value_from_key(connection, "uid", FALSE);
227 0 : query->gid = get_argument_value_from_key(connection, "gid", FALSE);
228 0 : query->owner = get_argument_value_from_key(connection, "owner", FALSE);
229 0 : query->group = get_argument_value_from_key(connection, "group", FALSE);
230 0 : query->filename = get_argument_value_from_key(connection, "filename", TRUE);
231 0 : query->date = get_argument_value_from_key(connection, "date", TRUE);
232 0 : query->afterdate = get_argument_value_from_key(connection, "afterdate", TRUE);
233 0 : query->beforedate = get_argument_value_from_key(connection, "beforedate", TRUE);
234 :
235 0 : print_debug(_("hostname: %s, uid: %s, gid: %s, owner: %s, group: %s, filter: %s && %s && %s && %s \n"), \
236 : query->hostname, query->uid, query->gid, query->owner, query->group, \
237 : query->filename, query->date, query->afterdate, query->beforedate);
238 :
239 0 : if (query->hostname != NULL && query-> uid != NULL && query->gid != NULL && query->owner != NULL && query->group != NULL)
240 : {
241 0 : answer = backend->get_list_of_files(server_struct, query);
242 : }
243 : else
244 : {
245 0 : answer = g_strdup_printf(_("Malformed request. hostname: %s, uid: %s, gid: %s, owner: %s, group: %s"), \
246 : query->hostname, query->uid, query->gid, query->owner, query->group);
247 : }
248 :
249 0 : free_query_t(query); /** All variables hostname, uid... are freed there ! */
250 : }
251 : else
252 : {
253 0 : print_error(__FILE__, __LINE__, _("Error: no backend defined to get a list of files from it.\n"));
254 : }
255 : }
256 :
257 0 : return answer;
258 : }
259 :
260 :
261 : /**
262 : * Function to answer to get requests in a json way. This mode should be
263 : * prefered.
264 : * @param server_struct is the main structure for the server.
265 : * @param connection is the connection in MHD
266 : * @param url is the requested url
267 : * @note to translators all json requests MUST NOT be translated because
268 : * it is the protocol itself !
269 : * @returns a newlly allocated gchar * string that contains the anwser to be
270 : * sent back to the client.
271 : */
272 0 : static gchar *get_json_answer(server_struct_t *server_struct, struct MHD_Connection *connection, const char *url)
273 : {
274 0 : gchar *answer = NULL;
275 0 : gchar *hash = NULL;
276 0 : size_t hlen = 0;
277 :
278 0 : if (g_strcmp0(url, "/Version.json") == 0)
279 : {
280 0 : answer = convert_version_to_json(PROGRAM_NAME, SERVER_DATE, SERVER_VERSION, SERVER_AUTHORS, SERVER_LICENSE);
281 : }
282 0 : else if (g_str_has_prefix(url, "/File/List.json"))
283 : {
284 0 : answer = get_a_list_of_files(server_struct, connection);
285 : }
286 0 : else if (g_str_has_prefix(url, "/Data/"))
287 : {
288 0 : hash = g_strndup((const gchar *) url + 6, HASH_LEN*2); /* HASH_LEN is expressed when hash is in binary form */
289 0 : hash = g_strcanon(hash, "abcdef0123456789", '\0'); /* replace anything not in hexadecimal format with \0 */
290 :
291 0 : hlen = strlen(hash);
292 0 : if (hlen == HASH_LEN*2)
293 : {
294 0 : print_debug(_("Trying to get data for hash %s\n"), hash);
295 0 : answer = get_data_from_a_specific_hash(server_struct, hash);
296 : }
297 : else
298 : {
299 0 : answer = g_strdup_printf("{\"Invalid url: in %s hash has length\": %zd instead of %d}", url, hlen, HASH_LEN*2);
300 : }
301 :
302 0 : free_variable(hash);
303 : }
304 : else
305 : { /* Some sort of echo to the invalid request */
306 0 : answer = g_strdup_printf("{\"Invalid url\": %s}", url);
307 : }
308 :
309 0 : return answer;
310 : }
311 :
312 :
313 : /**
314 : * Function to answer to get requests in an unformatted way. Only some urls
315 : * May be like this. As we prefer to speak in json format in normal operation
316 : * mode
317 : * @param server_struct is the main structure for the server.
318 : * @param url is the requested url
319 : * @returns a newlly allocated gchar * string that contains the anwser to be
320 : * sent back to the client.
321 : */
322 0 : static gchar *get_unformatted_answer(server_struct_t *server_struct, const char *url)
323 : {
324 0 : gchar *answer = NULL;
325 0 : gchar *buf1 = NULL;
326 0 : gchar *buf2 = NULL;
327 0 : gchar *buf3 = NULL;
328 :
329 0 : if (g_strcmp0(url, "/Version") == 0)
330 : {
331 0 : buf1 = buffer_program_version(PROGRAM_NAME, SERVER_DATE, SERVER_VERSION, SERVER_AUTHORS, SERVER_LICENSE);
332 0 : buf2 = buffer_libraries_versions(PROGRAM_NAME);
333 0 : buf3 = buffer_selected_option(server_struct->opt);
334 :
335 0 : answer = g_strconcat(buf1, buf2, buf3, NULL);
336 :
337 0 : buf1 = free_variable(buf1);
338 0 : buf2 = free_variable(buf2);
339 0 : buf3 = free_variable(buf3);
340 : }
341 : else
342 : { /* Some sort of echo to the invalid request */
343 0 : answer = g_strdup_printf(_("Error: invalid url: %s\n"), url);
344 : }
345 :
346 0 : return answer;
347 : }
348 :
349 :
350 : /**
351 : * Creates a response sent to the client via MHD_queue_response
352 : * @param connection is the MHD_Connection connection
353 : * @param answer is the gchar * string to be sent.
354 : */
355 96885 : static int create_MHD_response(struct MHD_Connection *connection, gchar *answer)
356 : {
357 96885 : struct MHD_Response *response = NULL;
358 96885 : int success = MHD_NO;
359 :
360 96885 : response = MHD_create_response_from_buffer(strlen(answer), (void *) answer, MHD_RESPMEM_MUST_FREE);
361 96885 : success = MHD_queue_response(connection, MHD_HTTP_OK, response);
362 96885 : MHD_destroy_response(response);
363 :
364 96885 : return success;
365 : }
366 :
367 :
368 : /**
369 : * Function to process get requests received from clients.
370 : * @param server_struct is the main structure for the server.
371 : * @param connection is the connection in MHD
372 : * @param url is the requested url
373 : * @param con_cls is a pointer used to know if this is the first call or not
374 : * @returns an int that is either MHD_NO or MHD_YES upon failure or not.
375 : */
376 0 : static int process_get_request(server_struct_t *server_struct, struct MHD_Connection *connection, const char *url, void **con_cls)
377 : {
378 : static int aptr = 0;
379 0 : int success = MHD_NO;
380 0 : gchar *answer = NULL;
381 :
382 :
383 0 : if (&aptr != *con_cls)
384 : {
385 : /* do never respond on first call */
386 0 : *con_cls = &aptr;
387 :
388 0 : success = MHD_YES;
389 : }
390 : else
391 : {
392 0 : if (get_debug_mode() == TRUE)
393 : {
394 0 : print_debug(_("Requested get url: %s\n"), url);
395 0 : print_headers(connection);
396 : }
397 :
398 0 : if (g_str_has_suffix(url, ".json"))
399 : { /* A json format answer was requested */
400 0 : answer = get_json_answer(server_struct, connection, url);
401 : }
402 : else
403 : { /* An "unformatted" answer was requested */
404 0 : answer = get_unformatted_answer(server_struct, url);
405 : }
406 :
407 : /* reset when done */
408 0 : *con_cls = NULL;
409 :
410 : /* Do not free answer variable as MHD will do it for us ! */
411 0 : success = create_MHD_response(connection, answer);
412 : }
413 :
414 0 : return success;
415 :
416 : }
417 :
418 :
419 : /**
420 : * Answers /Meta.json POST request by storing data and answering to the
421 : * client.
422 : * @param server_struct is the main structure for the server.
423 : * @param connection is the connection in MHD
424 : * @param received_data is a gchar * string to the data that was received
425 : * by the POST request.
426 : */
427 49745 : static int answer_meta_json_post_request(server_struct_t *server_struct, struct MHD_Connection *connection, gchar *received_data)
428 : {
429 49745 : server_meta_data_t *smeta = NULL;
430 49745 : gchar *answer = NULL; /** gchar *answer : Do not free answer variable as MHD will do it for us ! */
431 49745 : json_t *root = NULL; /** json_t *root is the root that will contain all meta data json formatted */
432 49745 : json_t *array = NULL; /** json_t *array is the array that will receive base64 encoded hashs */
433 49745 : GList *needed = NULL;
434 :
435 49745 : smeta = convert_json_to_smeta_data(received_data);
436 :
437 49745 : if (smeta != NULL && smeta->meta != NULL)
438 : { /* The convertion went well and smeta contains the meta data */
439 :
440 49745 : print_debug(_("Received meta data (%zd bytes) for file %s\n"), strlen(received_data), smeta->meta->name);
441 :
442 49745 : if (smeta->data_sent == FALSE)
443 : {
444 : /**
445 : * Creating an answer and sending the hashs that are needed. If
446 : * the selected backend does not have a build_needed_hash_list
447 : * function we are returning the whole hash_list !
448 : */
449 :
450 49742 : if (server_struct != NULL && server_struct->backend != NULL && server_struct->backend->build_needed_hash_list != NULL)
451 : {
452 49742 : needed = server_struct->backend->build_needed_hash_list(server_struct, smeta->meta->hash_data_list);
453 49742 : array = convert_hash_list_to_json(needed);
454 49742 : g_list_free_full(needed, free_hdt_struct);
455 : }
456 : else
457 : {
458 0 : array = convert_hash_list_to_json(smeta->meta->hash_data_list);
459 : }
460 : }
461 : else
462 : {
463 3 : array = json_array();
464 : }
465 :
466 49745 : root = json_object();
467 49745 : insert_json_value_into_json_root(root, "hash_list", array);
468 49745 : answer = json_dumps(root, 0);
469 49745 : json_decref(root);
470 :
471 :
472 : /**
473 : * Sending smeta data into the queue in order to be treated by
474 : * the corresponding thread. smeta is freed there and should not
475 : * be used after this "call" here.
476 : */
477 49745 : g_async_queue_push(server_struct->meta_queue, smeta);
478 : }
479 : else
480 : {
481 0 : answer = g_strdup_printf(_("Error: could not convert json to metadata\n"));
482 : }
483 :
484 49745 : return create_MHD_response(connection, answer);
485 : }
486 :
487 :
488 : /**
489 : * Prints a debug information about received data for a specific hash.
490 : * @param hash is the binary representation of the hash obtained with
491 : * the read data
492 : * @param read is the size of the data beeing read and leading to this
493 : * hash
494 : */
495 229178 : static void print_received_data_for_hash(guint8 *hash, gssize read)
496 : {
497 229178 : gchar *encoded_hash = NULL;
498 229178 : gchar *string_read = NULL;
499 :
500 229178 : encoded_hash = g_base64_encode(hash, HASH_LEN);
501 229178 : string_read = g_strdup_printf("%"G_GSSIZE_FORMAT, read);
502 :
503 229178 : print_debug(_("Received data for hash: \"%s\" (%s bytes)\n"), encoded_hash, string_read);
504 :
505 229178 : free_variable(string_read);
506 229178 : free_variable(encoded_hash);
507 229178 : }
508 :
509 :
510 : /**
511 : * Function that process the received data from the POST command and
512 : * answers to the client.
513 : * Here we may do something with this data (we may want to store it
514 : * somewhere).
515 : *
516 : * @param server_struct is the main structure for the server.
517 : * @param connection is the connection in MHD
518 : * @param url is the requested url
519 : * @param received_data is a gchar * string to the data that was received
520 : * by the POST request.
521 : */
522 96885 : static int process_received_data(server_struct_t *server_struct, struct MHD_Connection *connection, const char *url, gchar *received_data)
523 : {
524 96885 : gchar *answer = NULL; /** gchar *answer : Do not free answer variable as MHD will do it for us ! */
525 96885 : int success = MHD_NO;
526 96885 : gboolean debug = FALSE;
527 96885 : hash_data_t *hash_data = NULL;
528 96885 : json_t *root = NULL;
529 96885 : GList *hash_data_list = NULL;
530 96885 : GList *head = NULL;
531 96885 : a_clock_t *elapsed = NULL;
532 :
533 96885 : if (g_strcmp0(url, "/Meta.json") == 0 && received_data != NULL)
534 : {
535 49745 : success = answer_meta_json_post_request(server_struct, connection, received_data);
536 : }
537 47140 : else if (g_strcmp0(url, "/Hash_Array.json") == 0 && received_data != NULL)
538 : {
539 : /* Here we will try to answer which hashs are needed and then
540 : * send thoses hashs back in the answer
541 : */
542 : }
543 47140 : else if (g_strcmp0(url, "/Data.json") == 0 && received_data != NULL)
544 : {
545 :
546 3017 : hash_data = convert_string_to_hash_data(received_data);
547 :
548 3017 : if (get_debug_mode() == TRUE)
549 : {
550 3017 : print_received_data_for_hash(hash_data->hash, hash_data->read);
551 : }
552 :
553 : /**
554 : * Sending received_data into the queue in order to be treated by
555 : * the corresponding thread. hash_data is freed by data_thread
556 : * and should not be used after this "call" here.
557 : */
558 3017 : g_async_queue_push(server_struct->data_queue, hash_data);
559 :
560 :
561 : /**
562 : * creating an answer for the client to say that everything went Ok!
563 : */
564 3017 : answer = g_strdup_printf(_("Ok!"));
565 3017 : success = create_MHD_response(connection, answer);
566 : }
567 44123 : else if (g_strcmp0(url, "/Data_Array.json") == 0 && received_data != NULL)
568 : {
569 : /* print_debug("/Data_Array.json: %s\n", received_data); */
570 44123 : elapsed = new_clock_t();
571 44123 : root = load_json(received_data);
572 44123 : end_clock(elapsed, "load_json");
573 44123 : hash_data_list = extract_glist_from_array(root, "data_array", FALSE);
574 44123 : head = hash_data_list;
575 44123 : json_decref(root);
576 44123 : debug = get_debug_mode();
577 :
578 314407 : while (hash_data_list != NULL)
579 : {
580 226161 : hash_data = hash_data_list->data;
581 :
582 226161 : if (debug == TRUE)
583 : {
584 : /* Only for debbugging ! */
585 226161 : print_received_data_for_hash(hash_data->hash, hash_data->read);
586 : }
587 :
588 : /** Sending hash_data into the queue. */
589 226161 : g_async_queue_push(server_struct->data_queue, hash_data);
590 226161 : hash_data_list = g_list_next(hash_data_list);
591 : }
592 :
593 44123 : g_list_free(head);
594 :
595 : /**
596 : * creating an answer for the client to say that everything went Ok!
597 : */
598 :
599 44123 : answer = g_strdup_printf(_("Ok!"));
600 44123 : success = create_MHD_response(connection, answer);
601 : }
602 : else
603 : {
604 : /* The url is unknown to the server and we can not process the request ! */
605 0 : print_error(__FILE__, __LINE__, "Error: invalid url: %s\n", url);
606 0 : answer = g_strdup_printf(_("Error: invalid url!\n"));
607 0 : success = create_MHD_response(connection, answer);
608 : }
609 :
610 96885 : return success;
611 : }
612 :
613 :
614 : /**
615 : * @param connection is the connection in MHD
616 : * @returns in bytes the value (a guint) of the Content-Length: header
617 : */
618 96885 : static guint64 get_content_length(struct MHD_Connection *connection)
619 : {
620 96885 : const char *length = NULL;
621 96885 : guint64 len = DEFAULT_SERVER_BUFFER_SIZE;
622 :
623 96885 : length = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, "Content-Length");
624 :
625 96885 : if (length != NULL)
626 : {
627 : /** @todo test len and return code for sscanf to validate a correct entry */
628 96885 : if (sscanf(length, "%"G_GUINT64_FORMAT, &len) <= 0)
629 : {
630 0 : print_error(__FILE__, __LINE__, _("Could not guess Content-Length header value: %s\n"), strerror(errno));
631 0 : len = DEFAULT_SERVER_BUFFER_SIZE;
632 : }
633 :
634 96885 : if (len < 0 || len > 4294967296)
635 : {
636 0 : len = DEFAULT_SERVER_BUFFER_SIZE;
637 : }
638 : }
639 :
640 96885 : return len;
641 : }
642 :
643 :
644 : /**
645 : * Function to process post requests.
646 : * @param server_struct is the main structure for the server.
647 : * @param connection is the connection in MHD
648 : * @param url is the requested url
649 : * @param con_cls is a pointer used to know if this is the first call or not
650 : * @param upload_data is a char * pointer to the data being uploaded at this call
651 : * @param upload_size is a pointer to an size_t value that says how many data
652 : * is ti be copied from upload_data string.
653 : * @returns an int that is either MHD_NO or MHD_YES upon failure or not.
654 : */
655 478307 : static int process_post_request(server_struct_t *server_struct, struct MHD_Connection *connection, const char *url, void **con_cls, const char *upload_data, size_t *upload_data_size)
656 : {
657 478307 : int success = MHD_NO;
658 478307 : upload_t *pp = (upload_t *) *con_cls;
659 478307 : guint64 len = 0;
660 :
661 : /* print_debug("%ld, %s, %p\n", *upload_data_size, url, pp); */ /* This is for early debug only ! */
662 :
663 478307 : if (pp == NULL)
664 : {
665 : /* print_headers(connection); */ /* Used for debugging */
666 : /* Initialzing the structure at first connection */
667 96885 : len = get_content_length(connection);
668 96885 : pp = (upload_t *) g_malloc(sizeof(upload_t));
669 96885 : pp->pos = 0;
670 96885 : pp->buffer = g_malloc(sizeof(gchar) * (len + 1)); /* not using g_malloc0 here because it's 1000 times slower */
671 96885 : pp->number = 0;
672 96885 : *con_cls = pp;
673 :
674 96885 : success = MHD_YES;
675 : }
676 381422 : else if (*upload_data_size != 0)
677 : {
678 : /* Getting data whatever they are */
679 284537 : memcpy(pp->buffer + pp->pos, upload_data, *upload_data_size);
680 284537 : pp->pos = pp->pos + *upload_data_size;
681 :
682 284537 : pp->number = pp->number + 1;
683 :
684 284537 : *con_cls = pp;
685 284537 : *upload_data_size = 0;
686 :
687 284537 : success = MHD_YES;
688 : }
689 : else
690 : {
691 : /* reset when done */
692 96885 : *con_cls = NULL;
693 96885 : pp->buffer[pp->pos] = '\0';
694 :
695 : /* Do something with received_data */
696 96885 : success = process_received_data(server_struct, connection, url, pp->buffer);
697 :
698 96885 : free_variable(pp->buffer);
699 96885 : free_variable(pp);
700 : }
701 :
702 478307 : return success;
703 : }
704 :
705 :
706 : /**
707 : * Prints all keys-values pairs contained in an HTTP header.
708 : * @param cls cls may be NULL here (not used).
709 : * @param[in] kind is the MHD_ValueKind requested
710 : * @param[in] key is the key to be printed
711 : * @param[in] value is the value of the corresponding key
712 : *
713 : */
714 0 : static int print_out_key(void *cls, enum MHD_ValueKind kind, const char *key, const char *value)
715 : {
716 0 : fprintf(stdout, "\t%s: %s\n", key, value);
717 :
718 0 : return MHD_YES;
719 : }
720 :
721 :
722 : /**
723 : * Prints everything in the header of a connection
724 : * @param connection the connection that we want to print all headers.
725 : */
726 0 : static void print_headers(struct MHD_Connection *connection)
727 : {
728 0 : fprintf(stdout, _("Headers for this connection are:\n"));
729 0 : MHD_get_connection_values(connection, MHD_HEADER_KIND, &print_out_key, NULL);
730 0 : }
731 :
732 :
733 : /**
734 : * MHD_AccessHandlerCallback function that manages all connections requests
735 : * @param cls is the server_struct_t * server_struct main server
736 : * structure.
737 : * @todo . free some memory where needed
738 : * . manage errors codes
739 : */
740 478307 : static int ahc(void *cls, struct MHD_Connection *connection, const char *url, const char *method, const char *version, const char *upload_data, size_t *upload_data_size, void **con_cls)
741 : {
742 478307 : server_struct_t *ahc_server_struct = (server_struct_t *) cls;
743 478307 : int success = MHD_NO;
744 :
745 :
746 478307 : if (g_strcmp0(method, "GET") == 0)
747 : {
748 : /* We have a GET method that needs to be processed */
749 0 : success = process_get_request(ahc_server_struct, connection, url, con_cls);
750 : }
751 478307 : else if (g_strcmp0(method, "POST") == 0)
752 : { /* We have a POST method that needs to be processed */
753 478307 : success = process_post_request(ahc_server_struct, connection, url, con_cls, upload_data, upload_data_size);
754 : }
755 : else
756 : { /* not a GET nor a POST -> we do not know what to do ! */
757 : success = MHD_NO;
758 : }
759 :
760 478307 : return success;
761 : }
762 :
763 :
764 : /**
765 : * Thread whose aim is to store meta-data according to the selected backend
766 : * @param data : server_struct_t * structure.
767 : * @returns NULL to fullfill the template needed to create a GThread
768 : */
769 1 : static gpointer meta_data_thread(gpointer user_data)
770 : {
771 1 : server_struct_t *server_struct = user_data;
772 1 : server_meta_data_t *smeta = NULL;
773 :
774 1 : if (server_struct != NULL && server_struct->meta_queue != NULL)
775 : {
776 :
777 1 : if (server_struct->backend != NULL && server_struct->backend->store_smeta != NULL)
778 : {
779 :
780 : while (TRUE)
781 : {
782 49746 : smeta = g_async_queue_pop(server_struct->meta_queue);
783 :
784 49745 : if (smeta != NULL && smeta->meta != NULL)
785 : {
786 49745 : print_debug("meta_data_thread: received from %s meta for file %s\n", smeta->hostname, smeta->meta->name);
787 49745 : server_struct->backend->store_smeta(server_struct, smeta);
788 49745 : free_smeta_data_t(smeta);
789 : }
790 : else
791 : {
792 0 : print_error(__FILE__, __LINE__, _("Error: received a NULL pointer.\n"));
793 : }
794 : }
795 : }
796 : else
797 : {
798 0 : print_error(__FILE__, __LINE__, _("Error: no meta data store backend defined, meta-data's thread terminating...\n"));
799 : }
800 : }
801 : else
802 : {
803 0 : print_error(__FILE__, __LINE__, _("Error: unable to launch meta-data thread.\n"));
804 : }
805 :
806 0 : return NULL;
807 : }
808 :
809 :
810 : /**
811 : * Thread whose aim is to store data according to the selected backend
812 : * @param data : server_struct_t * structure.
813 : * @returns NULL to fullfill the template needed to create a GThread
814 : */
815 1 : static gpointer data_thread(gpointer user_data)
816 : {
817 1 : server_struct_t *dt_server_struct = user_data;
818 1 : hash_data_t *hash_data = NULL;
819 :
820 1 : if (dt_server_struct != NULL && dt_server_struct->meta_queue != NULL)
821 : {
822 :
823 1 : if (dt_server_struct->backend != NULL && dt_server_struct->backend->store_data != NULL)
824 : {
825 :
826 : while (TRUE)
827 : {
828 229179 : hash_data = g_async_queue_pop(dt_server_struct->data_queue);
829 :
830 229178 : if (hash_data != NULL)
831 : {
832 229178 : dt_server_struct->backend->store_data(dt_server_struct, hash_data);
833 : }
834 : }
835 : }
836 : else
837 : {
838 0 : print_error(__FILE__, __LINE__, _("Error: no data store backend defined, data's thread terminating...\n"));
839 : }
840 :
841 : }
842 : else
843 : {
844 0 : print_error(__FILE__, __LINE__, _("Error while trying to launch data thread"));
845 : }
846 :
847 0 : return NULL;
848 : }
849 :
850 :
851 : /**
852 : * Main function
853 : * @param argc : number of arguments given on the command line.
854 : * @param argv : an array of strings that contains command line arguments.
855 : * @returns always 0
856 : */
857 4 : int main(int argc, char **argv)
858 : {
859 4 : server_struct_t *server_struct = NULL; /** main structure for 'server' program. */
860 4 : guint id_int = 0;
861 4 : guint id_term = 0;
862 :
863 : #if !GLIB_CHECK_VERSION(2, 36, 0)
864 : g_type_init(); /** g_type_init() is deprecated since glib 2.36 */
865 : #endif
866 :
867 4 : ignore_sigpipe(); /** into order to get libmicrohttpd portable */
868 :
869 4 : init_international_languages();
870 :
871 4 : server_struct = init_server_main_structure(argc, argv);
872 :
873 1 : if (server_struct != NULL && server_struct->opt != NULL && server_struct->backend != NULL)
874 : {
875 1 : server_struct->loop = g_main_loop_new(g_main_context_default(), FALSE);
876 1 : id_int = g_unix_signal_add(SIGINT, int_signal_handler, server_struct);
877 1 : id_term = g_unix_signal_add(SIGTERM, int_signal_handler, server_struct);
878 :
879 1 : if (id_int <= 0 || id_term <= 0)
880 : {
881 0 : print_error(__FILE__, __LINE__, _("Unable to add signal handler\n"));
882 : }
883 :
884 : /* Initializing the choosen backend by calling it's function */
885 1 : if (server_struct->backend->init_backend != NULL)
886 : {
887 1 : server_struct->backend->init_backend(server_struct);
888 : }
889 :
890 : /* Before starting anything else, start the threads */
891 1 : server_struct->meta_thread = g_thread_new("meta-data", meta_data_thread, server_struct);
892 1 : server_struct->data_thread = g_thread_new("data", data_thread, server_struct);
893 :
894 : /* Starting the libmicrohttpd daemon */
895 1 : server_struct->d = MHD_start_daemon(MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG, server_struct->opt->port, NULL, NULL, &ahc, server_struct, MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) 131070 , MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 120, MHD_OPTION_END);
896 :
897 1 : if (server_struct->d == NULL)
898 : {
899 0 : print_error(__FILE__, __LINE__, _("Error while spawning libmicrohttpd daemon\n"));
900 0 : return 1;
901 : }
902 :
903 : /* Unless on error we will never join the threads as they
904 : * contain a while (TRUE) loop !
905 : */
906 1 : g_main_loop_run(server_struct->loop);
907 :
908 : /* g_thread_join(server_struct->meta_thread); */
909 : /* g_thread_join(server_struct->data_thread); */
910 :
911 :
912 : }
913 : else
914 : {
915 0 : print_error(__FILE__, __LINE__, _("Error: initialization failed.\n"));
916 : }
917 :
918 : return 0;
919 : }
|