LCOV - code coverage report
Current view: top level - server - server.c (source / functions) Hit Total Coverage
Test: coverage-server.info Lines: 186 312 59.6 %
Date: 2016-02-03 22:31:45 Functions: 13 21 61.9 %

          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             : }

Generated by: LCOV version 1.11