LCOV - code coverage report
Current view: top level - libcdpfgl - communique.c (source / functions) Hit Total Coverage
Test: coverage-libcdpfgl.info Lines: 111 131 84.7 %
Date: 2016-02-03 22:31:46 Functions: 8 9 88.9 %

          Line data    Source code
       1             : /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
       2             : /*
       3             :  *    communique.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 communique.c
      25             :  * This file contains every call to the libcurl library in order to make a
      26             :  * wrapper to this library.
      27             :  *  * Defined errors are: http://curl.haxx.se/libcurl/c/libcurl-errors.html
      28             :  *  * How to use a buffer to get a message is at:
      29             :  *    http://curl.haxx.se/libcurl/c/CURLOPT_ERRORBUFFER.html
      30             :  */
      31             : 
      32             : #include "libcdpfgl.h"
      33             : 
      34             : static size_t write_data(void *buffer, size_t size, size_t nmemb, void *userp);
      35             : 
      36             : 
      37             : /**
      38             :  * Gets the version for the communication library
      39             :  * @returns a newly allocated string that contains the version and that
      40             :  *          may be freed with free_variable() when no longer needed.
      41             :  */
      42           2 : gchar *get_communication_library_version(void)
      43             : {
      44           2 :     curl_version_info_data *data = NULL;
      45             : 
      46           2 :     data = curl_version_info(CURLVERSION_NOW);
      47             : 
      48           2 :     return g_strdup_printf(_("\t. LIBCURL version: %s\n"), data->version);
      49             : }
      50             : 
      51             : 
      52             : /**
      53             :  * Makes the connexion string that is used by ZMQ to create a new socket
      54             :  * and verifies that port number is between 1025 and 65534 included.
      55             :  * @param ip : a gchar * that contains either an ip address or a hostname
      56             :  * @param port : a gint that is comprised between 1025 and 65534 included
      57             :  * @returns a newly allocated string that may be freed with free_variable()
      58             :  *          function.
      59             :  */
      60          13 : gchar *make_connexion_string(gchar *ip, gint port)
      61             : {
      62             :     /**
      63             :      * @todo check the ip string to be sure that it correspond to something that
      64             :      *       we can join (IP or hostname).
      65             :      */
      66          13 :     gchar *conn = NULL;
      67             : 
      68          13 :     if (ip != NULL && port > 1024 && port < 65535)
      69             :         {
      70             :             /* We must ensure that ip is correct before doing this ! */
      71          13 :             conn = g_strdup_printf("http://%s:%d", ip, port);
      72             :         }
      73             : 
      74          13 :     return conn;
      75             : }
      76             : 
      77             : 
      78             : /**
      79             :  * Used by libcurl to retrieve informations
      80             :  * @param buffer is the buffer where received data are written by libcurl
      81             :  * @param size is the size of an element in buffer
      82             :  * @param nmemb is the number of elements in buffer
      83             :  * @param[in,out] userp is a user pointer and MUST be a pointer to comm_t *
      84             :  *                structure
      85             :  * @returns should return the size of the data taken into account.
      86             :  *          Everything different from the size passed to this function is
      87             :  *          considered as an error by libcurl.
      88             :  */
      89       97014 : static size_t write_data(void *buffer, size_t size, size_t nmemb, void *userp)
      90             : {
      91       97014 :     comm_t *comm = (comm_t *) userp;
      92       97014 :     gchar *buf1 = NULL;
      93       97014 :     gchar *concat = NULL;
      94             : 
      95       97014 :     if (comm != NULL)
      96             :         {
      97       97014 :             if (comm->seq == 0)
      98             :                 {
      99       96993 :                     comm->buffer = g_strndup(buffer, size * nmemb);
     100             :                 }
     101             :             else
     102             :                 {
     103          21 :                     buf1 = g_strndup(buffer, size * nmemb);
     104          21 :                     concat = g_strdup_printf("%s%s", comm->buffer, buf1);
     105          21 :                     free_variable(buf1);
     106          21 :                     free_variable(comm->buffer);
     107          21 :                     comm->buffer = concat;
     108             :                 }
     109             : 
     110       97014 :             comm->seq = comm->seq + 1;
     111             :         }
     112             : 
     113       97014 :     return (size * nmemb);
     114             : }
     115             : 
     116             : 
     117             : 
     118             : 
     119             : /**
     120             :  * Used by libcurl to retrieve informations
     121             :  * @param buffer is the buffer where received data are written by libcurl
     122             :  * @param size is the size of an element in buffer
     123             :  * @param nitems is the number of elements in buffer
     124             :  * @param[in,out] userp is a user pointer and MUST be a pointer to comm_t *
     125             :  *                structure
     126             :  * @returns should return the size of the data taken into account.
     127             :  *          Everything different from the size passed to this function is
     128             :  *          considered as an error by libcurl.
     129             :  */
     130      342683 : static size_t read_data(char *buffer, size_t size, size_t nitems, void *userp)
     131             : {
     132      342683 :     comm_t *comm = (comm_t *) userp;
     133      342683 :     size_t whole_size = 0;
     134             : 
     135      342683 :     if (comm != NULL)
     136             :         {
     137      342683 :             if (comm->pos >= comm->length)
     138             :                 {
     139             :                     return 0;
     140             :                 }
     141             :             else
     142             :                 {
     143      245798 :                     whole_size = size * nitems;
     144             : 
     145      245798 :                     if ((comm->pos + whole_size) > comm->length)
     146             :                         {
     147       96883 :                             whole_size = comm->length - comm->pos;
     148       96883 :                             memcpy(buffer, comm->readbuffer + comm->pos, whole_size);
     149       96883 :                             comm->pos = comm->length;
     150       96883 :                             return (whole_size);
     151             :                         }
     152             :                     else
     153             :                         {
     154      148915 :                             memcpy(buffer, comm->readbuffer + comm->pos, whole_size);
     155      148915 :                             comm->pos = comm->pos + whole_size;
     156      148915 :                             return whole_size;
     157             :                         }
     158             :                 }
     159             :         }
     160             : 
     161             :     return 0;
     162             : }
     163             : 
     164             : 
     165             : /**
     166             :  * Uses curl to send a GET command to the http url
     167             :  * @param comm a comm_t * structure that must contain an initialized
     168             :  *        curl_handle (must not be NULL)
     169             :  * @param url a gchar * url where to send the command to. It must NOT
     170             :  *        contain the http://ip:port string. And must contain the first '/'
     171             :  *        ie to get 'http://127.0.0.1:5468/Version' url must be '/Version'.
     172             :  * @returns a CURLcode (http://curl.haxx.se/libcurl/c/libcurl-errors.html)
     173             :  *          CURLE_OK upon success, any other error code in any other
     174             :  *          situation. When CURLE_OK is returned, the data that the server
     175             :  *          sent is in the comm->buffer gchar * string.
     176             :  */
     177         108 : gint get_url(comm_t *comm, gchar *url)
     178             : {
     179         108 :     gint success = CURLE_FAILED_INIT;
     180         108 :     gchar *real_url = NULL;
     181         108 :     gchar *error_buf = NULL;
     182             : 
     183         108 :     if (comm != NULL && url != NULL && comm->curl_handle != NULL && comm->conn != NULL)
     184             :         {
     185         108 :             error_buf = (gchar *) g_malloc(CURL_ERROR_SIZE + 1);
     186         108 :             comm->seq = 0;
     187         108 :             comm->length = 0;
     188         108 :             comm->pos = 0;
     189         108 :             real_url = g_strdup_printf("%s%s", comm->conn, url);
     190             : 
     191         108 :             curl_easy_setopt(comm->curl_handle, CURLOPT_URL, real_url);
     192         108 :             curl_easy_setopt(comm->curl_handle, CURLOPT_WRITEFUNCTION, write_data);
     193         108 :             curl_easy_setopt(comm->curl_handle, CURLOPT_WRITEDATA, comm);
     194         108 :             curl_easy_setopt(comm->curl_handle, CURLOPT_ERRORBUFFER, error_buf);
     195             : 
     196         108 :             success = curl_easy_perform(comm->curl_handle);
     197             : 
     198         108 :             real_url = free_variable(real_url);
     199             : 
     200         108 :             if (success == CURLE_OK && comm->buffer != NULL)
     201             :                 {
     202         108 :                     print_debug(_("Answer is: \"%s\"\n"), comm->buffer);
     203             :                 }
     204             :             else
     205             :                 {
     206           0 :                     print_error(__FILE__, __LINE__, _("Error while sending GET command and receiving data: %s\n"), error_buf);
     207             :                 }
     208             : 
     209         108 :             free_variable(error_buf);
     210             :         }
     211             : 
     212         108 :     return success;
     213             : }
     214             : 
     215             : 
     216             : /**
     217             :  * Uses curl to send a POST command to the http server url
     218             :  * @param comm a comm_t * structure that must contain an initialized
     219             :  *        curl_handle (must not be NULL). buffer field of this structure
     220             :  *        is sent as data in the POST command.
     221             :  * @param url a gchar * url where to send the command to. It must NOT
     222             :  *        contain the http://ip:port string. And must contain the first '/'
     223             :  *        ie to get 'http://127.0.0.1:5468/Version' url must be '/Version'.
     224             :  * @returns a CURLcode (http://curl.haxx.se/libcurl/c/libcurl-errors.html)
     225             :  *          CURLE_OK upon success, any other error code in any other
     226             :  *          situation. When CURLE_OK is returned, the data that the server
     227             :  *          sent is in the comm->buffer gchar * string.
     228             :  * @todo manage errors codes
     229             :  */
     230       96885 : gint post_url(comm_t *comm, gchar *url)
     231             : {
     232       96885 :     gint success = CURLE_FAILED_INIT;
     233       96885 :     gchar *real_url = NULL;
     234       96885 :     gchar *error_buf = NULL;
     235       96885 :     gchar *len = NULL;
     236       96885 :     struct curl_slist *chunk = NULL;
     237             : 
     238       96885 :     if (comm != NULL && url != NULL && comm->curl_handle != NULL && comm->conn != NULL && comm->readbuffer != NULL)
     239             :         {
     240       96885 :             error_buf = (gchar *) g_malloc(CURL_ERROR_SIZE + 1);
     241       96885 :             comm->seq = 0;
     242       96885 :             comm->pos = 0;
     243       96885 :             real_url = g_strdup_printf("%s%s", comm->conn, url);
     244             : 
     245       96885 :             comm->length = strlen(comm->readbuffer);
     246             : 
     247       96885 :             curl_easy_reset(comm->curl_handle);
     248       96885 :             curl_easy_setopt(comm->curl_handle, CURLOPT_POST, 1);
     249       96885 :             curl_easy_setopt(comm->curl_handle, CURLOPT_READFUNCTION, read_data);
     250       96885 :             curl_easy_setopt(comm->curl_handle, CURLOPT_READDATA, comm);
     251       96885 :             curl_easy_setopt(comm->curl_handle, CURLOPT_URL, real_url);
     252       96885 :             curl_easy_setopt(comm->curl_handle, CURLOPT_WRITEFUNCTION, write_data);
     253       96885 :             curl_easy_setopt(comm->curl_handle, CURLOPT_WRITEDATA, comm);
     254       96885 :             curl_easy_setopt(comm->curl_handle, CURLOPT_ERRORBUFFER, error_buf);
     255             :             /* curl_easy_setopt(comm->curl_handle, CURLOPT_VERBOSE, 1L); */
     256       96885 :             chunk = curl_slist_append(chunk, "Transfer-Encoding: chunked");
     257             : 
     258       96885 :             len = g_strdup_printf("Content-Length: %zd", comm->length);
     259       96885 :             chunk = curl_slist_append(chunk, len);
     260             : 
     261       96885 :             if (g_str_has_suffix(url, ".json"))
     262             :                 {
     263       96885 :                     chunk = curl_slist_append(chunk, "Content-Type: application/json");
     264             :                 }
     265             :             else
     266             :                 {
     267           0 :                     chunk = curl_slist_append(chunk, "Content-Type: text/plain");
     268             :                 }
     269             : 
     270       96885 :             curl_easy_setopt(comm->curl_handle, CURLOPT_HTTPHEADER, chunk);
     271             : 
     272       96885 :             success = curl_easy_perform(comm->curl_handle);
     273       96885 :             curl_slist_free_all(chunk);
     274             : 
     275       96885 :             if (success != CURLE_OK)
     276             :                 {
     277           0 :                     print_error(__FILE__, __LINE__, _("Error while sending POST command (to \"%s\"): %s\n"), real_url, error_buf);
     278             :                 }
     279       96885 :             else if (comm->buffer != NULL)
     280             :                 {
     281       96885 :                     print_debug(_("Answer is: \"%s\"\n"), comm->buffer); /** @todo  Not sure that we will need this debug information later */
     282             :                 }
     283             : 
     284       96885 :             free_variable(real_url);
     285       96885 :             free_variable(error_buf);
     286       96885 :             free_variable(len);
     287             :         }
     288             : 
     289       96885 :     return success;
     290             : }
     291             : 
     292             : 
     293             : /**
     294             :  * Checks wether the server is alive or not and checks its version
     295             :  * @param comm a comm_t * structure that must contain an initialized
     296             :  *        curl_handle (must not be NULL).
     297             :  * @returns TRUE if the server is alive and has a correct version.
     298             :  *          FALSE otherwise
     299             :  */
     300           0 : gboolean is_server_alive(comm_t *comm)
     301             : {
     302           0 :     gint success = CURLE_FAILED_INIT;
     303           0 :     gchar *version = NULL;
     304             : 
     305           0 :     success = get_url(comm, "/Version.json");
     306           0 :     version = get_json_version(comm->buffer);
     307             : 
     308           0 :     free_variable(comm->buffer);
     309             : 
     310           0 :     if (success == CURLE_OK && version !=  NULL)
     311             :         {
     312           0 :             if (comm->conn != NULL)
     313             :                 {
     314           0 :                     print_debug("Server (version %s) is alive at %s.\n", version, comm->conn);
     315             :                 }
     316             :             else
     317             :                 {
     318           0 :                     print_debug("Server (version %s) is alive.\n", version);
     319             :                 }
     320             : 
     321           0 :             free_variable(version);
     322           0 :             return TRUE;
     323             :         }
     324             :     else
     325             :         {
     326           0 :             if (comm->conn != NULL)
     327             :                 {
     328           0 :                     print_debug("Server is not alive (%s).\n", comm->conn);
     329             :                 }
     330             :             else
     331             :                 {
     332           0 :                     print_debug("Server is not alive.\n");
     333             :                 }
     334             : 
     335           0 :             free_variable(version);
     336           0 :             return FALSE;
     337             :         }
     338             : }
     339             : 
     340             : 
     341             : /**
     342             :  * Creates a new communication comm_t * structure.
     343             :  * @param conn a gchar * connection string that should be some url like
     344             :  *        string : http://ip:port or http://servername:port
     345             :  * @returns a newly allocated comm_t * structure where sender and receiver
     346             :  *          are set to NULL.
     347             :  */
     348          14 : comm_t *init_comm_struct(gchar *conn)
     349             : {
     350          14 :     comm_t *comm = NULL;
     351             : 
     352          14 :     comm = (comm_t *) g_malloc0(sizeof(comm_t));
     353             : 
     354          14 :     comm->curl_handle = curl_easy_init();
     355          14 :     comm->buffer = NULL;
     356          14 :     comm->conn = g_strdup(conn);
     357          14 :     comm->readbuffer = NULL;
     358          14 :     comm->seq = 0;
     359          14 :     comm->pos = 0;
     360          14 :     comm->length = 0;
     361             : 
     362          14 :     return comm;
     363             : }
     364             : 
     365             : 
     366             : /**
     367             :  * Frees and releases a comm_t * structure
     368             :  * @param comm a comm_t * structure to be freed
     369             :  */
     370          12 : void free_comm_t(comm_t *comm)
     371             : {
     372          12 :     if (comm != NULL)
     373             :         {
     374          12 :             curl_easy_cleanup(comm->curl_handle);
     375          12 :             free_variable(comm->buffer);
     376          12 :             free_variable(comm->readbuffer);
     377          12 :             free_variable(comm->conn);
     378          12 :             free_variable(comm);
     379             :         }
     380          12 : }

Generated by: LCOV version 1.11