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