Line data Source code
1 : /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2 : /*
3 : * restore.c
4 : * This file is part of "Sauvegarde" project.
5 : *
6 : * (C) Copyright 2015 - 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 restore.c
25 : * This program should be able to restore files.
26 : */
27 :
28 : #include "restore.h"
29 :
30 : static res_struct_t *init_res_struct(int argc, char **argv);
31 : static gchar *encode_to_base64(gchar *string);
32 : static query_t *get_user_infos(gchar *hostname, gchar *filename, options_t *opt);
33 : static gchar *add_on_field_to_request(gchar *request, gchar *field, gchar *value);
34 : static gchar *make_base_request(query_t *query);
35 : static GSList *get_files_from_server(res_struct_t *res_struct, query_t *query);
36 : static void print_all_files(res_struct_t *res_struct, query_t *query);
37 : static void restore_data_to_stream(res_struct_t *res_struct, GFileOutputStream *stream, GList *hash_list);
38 : static void create_file(res_struct_t *res_struct, meta_data_t *meta);
39 : static void restore_last_file(res_struct_t *res_struct, query_t *query);
40 : static void free_res_struct_t(res_struct_t *res_struct);
41 :
42 :
43 : /**
44 : * Inits a res_struct_t * structure. Manages the command line options.
45 : * @param argc : number of arguments given on the command line.
46 : * @param argv : an array of strings that contains command line arguments.
47 : * @returns a res_struct_t * structure containing all of what is needed
48 : * by cdpfglrestore program.
49 : */
50 15 : static res_struct_t *init_res_struct(int argc, char **argv)
51 : {
52 15 : res_struct_t *res_struct = NULL;
53 15 : gchar *conn = NULL;
54 :
55 :
56 15 : res_struct = (res_struct_t *) g_malloc0(sizeof(res_struct_t));
57 :
58 15 : res_struct->hostname = (gchar *) g_get_host_name();
59 :
60 15 : res_struct->opt = do_what_is_needed_from_command_line_options(argc, argv);
61 :
62 12 : if (res_struct->opt != NULL && res_struct->opt->ip != NULL)
63 : {
64 : /* We keep conn string into comm_t structure: do not free it ! */
65 12 : conn = make_connexion_string(res_struct->opt->ip, res_struct->opt->port);
66 12 : res_struct->comm = init_comm_struct(conn);
67 : }
68 : else
69 : {
70 : /* This should never happen because we have default values... */
71 0 : res_struct->comm = NULL;
72 : }
73 :
74 12 : return res_struct;
75 : }
76 :
77 :
78 : /**
79 : * Encodes a gchar string into a base64 formated gchar * string.
80 : * @param string is the gchar string to be encoded (MUST be 0 terminated).
81 : * @returns a newly allocated gchar * string in base64 or NULL.
82 : */
83 48 : static gchar *encode_to_base64(gchar *string)
84 : {
85 48 : gchar *encoded_string = NULL;
86 :
87 48 : if (string != NULL)
88 : {
89 15 : encoded_string = g_base64_encode((const guchar *) string, strlen(string));
90 : }
91 :
92 48 : return encoded_string;
93 : }
94 :
95 :
96 :
97 : /**
98 : * Gets all user infos and fills a query_t * structure accordingly.
99 : * @param hostname the hostname where the program is run
100 : * @param filename is the name of the file we want to restore.
101 : * @param opt is the option structure that contains all options.
102 : * @return a pointer to a newly allocated query_t structure that may be
103 : * freed when no longer needed.
104 : */
105 12 : static query_t *get_user_infos(gchar *hostname, gchar *filename, options_t *opt)
106 : {
107 : uid_t uid;
108 12 : struct passwd *pass = NULL;
109 12 : struct group *grp = NULL;
110 12 : query_t *query = NULL;
111 12 : gchar *the_uid = NULL;
112 12 : gchar *the_gid = NULL;
113 12 : gchar *owner = NULL;
114 12 : gchar *group = NULL;
115 12 : gchar *encoded_date = NULL;
116 12 : gchar *encoded_filename = NULL;
117 12 : gchar *encoded_afterdate = NULL;
118 12 : gchar *encoded_beforedate = NULL;
119 :
120 12 : uid = geteuid();
121 12 : pass = getpwuid(uid);
122 :
123 12 : if (pass != NULL)
124 : {
125 12 : grp = getgrgid(pass->pw_gid);
126 12 : group = g_strdup(grp->gr_name);
127 12 : owner = g_strdup(pass->pw_name);
128 12 : the_uid = g_strdup_printf("%d", uid);
129 12 : the_gid = g_strdup_printf("%d", pass->pw_gid);
130 :
131 12 : encoded_filename = encode_to_base64(filename);
132 12 : encoded_date = encode_to_base64(opt->date);
133 12 : encoded_afterdate = encode_to_base64(opt->afterdate);
134 12 : encoded_beforedate = encode_to_base64(opt->beforedate);
135 :
136 12 : query = init_query_t(hostname, the_uid, the_gid, owner, group, encoded_filename, encoded_date, encoded_afterdate, encoded_beforedate);
137 12 : print_debug(_("hostname: %s, uid: %s, gid: %s, owner: %s, group: %s\n"), hostname, the_uid, the_gid, owner, group);
138 : }
139 :
140 12 : return query;
141 : }
142 :
143 :
144 : /**
145 : * Adds a field and its value to the request
146 : * @param request is the request where to add the field and its value.
147 : * This variable is freed here. Do not use its pointer after this
148 : * call !
149 : * @param field is the field to be added to the request
150 : * @param value is the value of the field
151 : * @returns a newly allocated gchar * request that may be freed when no
152 : * longer needed.
153 : */
154 36 : static gchar *add_on_field_to_request(gchar *request, gchar *field, gchar *value)
155 : {
156 36 : gchar *new_request = NULL;
157 :
158 36 : if (request != NULL && field != NULL && value != NULL)
159 : {
160 3 : new_request = g_strconcat(request, "&", field, "=", value, NULL);
161 : }
162 : else
163 : {
164 33 : new_request = g_strdup(request);
165 : }
166 :
167 36 : free_variable(request);
168 36 : request = new_request;
169 :
170 36 : return new_request;
171 : }
172 :
173 :
174 : /**
175 : * Makes the base URL for all requests
176 : * @param query is the structure that contains everything needed to
177 : * query the server (and filter a bit). It must not be NULL.
178 : * @returns always returns a newly allocated gchar * that represents
179 : * the base of the right part of the URL for all requests.
180 : */
181 12 : static gchar *make_base_request(query_t *query)
182 : {
183 12 : gchar *request = NULL;
184 :
185 12 : if (query != NULL)
186 : {
187 : /* This is the base request */
188 12 : request = g_strdup_printf("/File/List.json?hostname=%s&uid=%s&gid=%s&owner=%s&group=%s&filename=%s", query->hostname, query->uid, query->gid, query->owner, query->group, query->filename);
189 : }
190 : else
191 : {
192 0 : request = g_strdup_printf("/File/List.json?");
193 : }
194 :
195 12 : return request;
196 : }
197 :
198 :
199 : /**
200 : * Gets the file the server_meta_data_t * file list if any
201 : * @param res_struct is the main structure for cdpfglrestore program.
202 : * @param query is the structure that contains everything needed to
203 : * query the server (and filter a bit). It must not be NULL.
204 : * @returns a GSList * of server_meta_data_t *
205 : */
206 12 : static GSList *get_files_from_server(res_struct_t *res_struct, query_t *query)
207 : {
208 12 : gchar *request = NULL;
209 12 : json_t *root = NULL;
210 12 : GSList *list = NULL; /** List of server_meta_data_t * returned by this function */
211 12 : gint res = CURLE_FAILED_INIT;
212 :
213 12 : if (res_struct != NULL && query != NULL)
214 : {
215 12 : request = make_base_request(query);
216 12 : request = add_on_field_to_request(request, "date", query->date);
217 12 : request = add_on_field_to_request(request, "afterdate", query->afterdate);
218 12 : request = add_on_field_to_request(request, "beforedate", query->beforedate);
219 :
220 12 : print_debug(_("Query is: %s\n"), request);
221 12 : res = get_url(res_struct->comm, request);
222 :
223 12 : if (res == CURLE_OK && res_struct->comm->buffer != NULL)
224 : {
225 12 : root = load_json(res_struct->comm->buffer);
226 :
227 12 : list = extract_smeta_gslist_from_file_list(root);
228 12 : list = g_slist_sort(list, compare_filenames);
229 :
230 12 : json_decref(root);
231 12 : res_struct->comm->buffer = free_variable(res_struct->comm->buffer);
232 : }
233 :
234 12 : free_variable(request);
235 : }
236 :
237 12 : return list;
238 : }
239 :
240 :
241 : /**
242 : * Prints all saved files
243 : * @param res_struct is the main structure for cdpfglrestore program.
244 : * @param query is the structure that contains everything needed to
245 : * query the server (and filter a bit). It must not be NULL.
246 : */
247 4 : static void print_all_files(res_struct_t *res_struct, query_t *query)
248 : {
249 4 : GSList *list = NULL; /** List of server_meta_data_t * */
250 4 : GSList *head = NULL; /** head of the list to be freed */
251 4 : server_meta_data_t *smeta = NULL;
252 :
253 4 : if (res_struct != NULL && query != NULL)
254 : {
255 4 : list = get_files_from_server(res_struct, query);
256 4 : head = list;
257 :
258 182 : while (list != NULL)
259 : {
260 174 : smeta = (server_meta_data_t *) list->data;
261 174 : print_smeta_to_screen(smeta);
262 174 : free_smeta_data_t(smeta);
263 :
264 174 : list = g_slist_next(list);
265 : }
266 :
267 4 : g_slist_free(head);
268 : }
269 4 : }
270 :
271 :
272 : /**
273 : * Writes data obtained from the server with the hash_list hashs
274 : * to the stream.
275 : * @param stream is the stream where we are writing data (MUST be opened
276 : * and not NULL)
277 : * @param hash_list list of hashs of the file to be restored
278 : * @todo error management.
279 : */
280 4 : static void restore_data_to_stream(res_struct_t *res_struct, GFileOutputStream *stream, GList *hash_list)
281 : {
282 4 : gchar *hash = NULL;
283 4 : hash_data_t *hash_data = NULL;
284 4 : GError *error = NULL;
285 4 : gchar *request = NULL;
286 4 : gint res = CURLE_FAILED_INIT;
287 :
288 4 : if (stream != NULL)
289 : {
290 100 : while (hash_list != NULL)
291 : {
292 96 : hash_data = hash_list->data;
293 96 : hash = hash_to_string(hash_data->hash);
294 96 : request = g_strdup_printf("/Data/%s.json", hash);
295 :
296 96 : print_debug(_("Query is: %s\n"), request);
297 :
298 : /* This call fills res_struct->comm->buffer */
299 96 : res = get_url(res_struct->comm, request);
300 :
301 96 : if (res == CURLE_OK)
302 : {
303 : /** We need to save the retrieved buffer */
304 96 : if (res_struct->comm->buffer != NULL)
305 : {
306 96 : hash_data = convert_string_to_hash_data(res_struct->comm->buffer);
307 96 : res_struct->comm->buffer = free_variable(res_struct->comm->buffer);
308 :
309 96 : if (hash_data != NULL)
310 : {
311 96 : g_output_stream_write((GOutputStream *) stream, hash_data->data, hash_data->read, NULL, &error);
312 :
313 96 : free_hash_data_t(hash_data);
314 : }
315 : else
316 : {
317 0 : print_error(__FILE__, __LINE__, _("Error while trying to restore %s hash\n"), hash);
318 : }
319 : }
320 : }
321 : else
322 : {
323 0 : print_error(__FILE__, __LINE__, _("Error while getting hash %s"), hash);
324 : }
325 :
326 96 : hash_list = g_list_next(hash_list);
327 96 : free_variable(request);
328 96 : free_variable(hash);
329 : }
330 : }
331 4 : }
332 :
333 :
334 : /**
335 : * Creates the file to be restored.
336 : * @param res_struct is the main structure for cdpfglrestore program (used here
337 : * to communicate with cdpfglserver's server).
338 : * @param meta is the whole meta_data file describing the file to be
339 : * restored
340 : */
341 4 : static void create_file(res_struct_t *res_struct, meta_data_t *meta)
342 : {
343 4 : GFile *file = NULL;
344 4 : gchar *basename = NULL; /** basename for the file to be restored */
345 4 : gchar *where = NULL; /** directory where to restore the file */
346 4 : gchar *filename = NULL; /** filename of the restored file */
347 4 : GFileOutputStream *stream = NULL;
348 4 : GError *error = NULL;
349 4 : options_t *opt = NULL;
350 :
351 4 : if (res_struct != NULL && meta != NULL)
352 : {
353 : /* get the basename of the file to be restored */
354 4 : basename = g_path_get_basename(meta->name);
355 :
356 4 : opt = res_struct->opt;
357 :
358 4 : if (opt != NULL && opt->where != NULL && g_file_test(opt->where, G_FILE_TEST_IS_DIR))
359 : {
360 4 : where = g_strdup(opt->where);
361 : }
362 :
363 4 : if (where == NULL)
364 : {
365 : /* Fall back to get the current directory to make the file to be restored in it */
366 0 : where = g_get_current_dir();
367 : }
368 :
369 4 : filename = g_build_filename(where, basename, NULL);
370 4 : print_debug(_("filename to restore: %s\n"), filename);
371 4 : file = g_file_new_for_path(filename);
372 :
373 4 : if (g_strcmp0("", meta->link) == 0)
374 : {
375 4 : stream = g_file_replace(file, NULL, TRUE, G_FILE_CREATE_NONE, NULL, &error);
376 :
377 4 : if (stream != NULL)
378 : {
379 4 : restore_data_to_stream(res_struct, stream, meta->hash_data_list);
380 :
381 4 : g_output_stream_close((GOutputStream *) stream, NULL, &error);
382 4 : free_object(stream);
383 : }
384 0 : else if (error != NULL)
385 : {
386 0 : print_error(__FILE__, __LINE__, _("Error: unable to open file %s to write data in it (%s).\n"), filename, error->message);
387 0 : free_variable(error);
388 : }
389 :
390 : /* Setting before closing the file does not alter access and modification time */
391 4 : set_file_attributes(file, meta);
392 : }
393 : else
394 : {
395 0 : make_symbolic_link(file, meta->link);
396 : }
397 :
398 4 : free_object(file);
399 4 : free_variable(where);
400 4 : free_variable(basename);
401 4 : free_variable(filename);
402 : }
403 4 : }
404 :
405 :
406 : /**
407 : * Restores the last file that the fetched list contains.
408 : * @param res_struct is the main structure for cdpfglrestore program.
409 : * @param query is the structure that contains everything needed to
410 : * query the server (and filter a bit). It must not be NULL.
411 : */
412 8 : static void restore_last_file(res_struct_t *res_struct, query_t *query)
413 : {
414 8 : GSList *list = NULL; /** List of server_meta_data_t * */
415 8 : GSList *last = NULL; /** last element of the list */
416 8 : server_meta_data_t *smeta = NULL;
417 8 : meta_data_t *meta = NULL;
418 8 : gchar *string_inode = NULL;
419 8 : gchar *string_atime = NULL;
420 8 : gchar *string_ctime = NULL;
421 8 : gchar *string_mtime = NULL;
422 8 : gchar *string_size = NULL;
423 :
424 8 : if (res_struct != NULL && query != NULL)
425 : {
426 8 : list = get_files_from_server(res_struct, query);
427 8 : last = g_slist_last(list);
428 :
429 8 : if (last != NULL)
430 : {
431 4 : smeta = (server_meta_data_t *) last->data;
432 4 : meta = smeta->meta;
433 :
434 4 : if (get_debug_mode() == TRUE)
435 : {
436 4 : string_inode = g_strdup_printf("%"G_GUINT64_FORMAT, meta->inode);
437 4 : string_atime = g_strdup_printf("%"G_GUINT64_FORMAT, meta->atime);
438 4 : string_ctime = g_strdup_printf("%"G_GUINT64_FORMAT, meta->ctime);
439 4 : string_mtime = g_strdup_printf("%"G_GUINT64_FORMAT, meta->mtime);
440 4 : string_size = g_strdup_printf("%"G_GUINT64_FORMAT, meta->size);
441 :
442 4 : print_debug(_("File to be restored: type %d, inode: %s, mode: %d, atime: %s, ctime: %s, mtime: %s, size: %s, filename: %s, owner: %s, group: %s, uid: %d, gid: %d\n"), meta->file_type, string_inode, meta->mode, string_atime, string_ctime, string_mtime, string_size, meta->name, meta->owner, meta->group, meta->uid, meta->gid);
443 :
444 4 : free_variable(string_inode);
445 4 : free_variable(string_atime);
446 4 : free_variable(string_ctime);
447 4 : free_variable(string_mtime);
448 4 : free_variable(string_size);
449 : }
450 :
451 4 : create_file(res_struct, meta);
452 : }
453 :
454 8 : g_slist_free_full(list, gslist_free_smeta);
455 : }
456 8 : }
457 :
458 :
459 : /**
460 : * Frees a previously allocated res_struct_t * structure.
461 : * @param res_struct is the res_struct_t * structure to be freed.
462 : */
463 12 : static void free_res_struct_t(res_struct_t *res_struct)
464 : {
465 12 : if (res_struct != NULL)
466 : {
467 12 : free_options_t(res_struct->opt);
468 12 : free_comm_t(res_struct->comm);
469 12 : free_variable(res_struct->hostname);
470 12 : free_variable(res_struct);
471 : }
472 12 : }
473 :
474 :
475 : /**
476 : * Main function
477 : * @param argc : number of arguments given on the command line.
478 : * @param argv : an array of strings that contains command line arguments.
479 : * @returns
480 : */
481 15 : int main(int argc, char **argv)
482 : {
483 15 : res_struct_t *res_struct = NULL;
484 15 : query_t *query = NULL;
485 :
486 :
487 : #if !GLIB_CHECK_VERSION(2, 36, 0)
488 : g_type_init(); /** g_type_init() is deprecated since glib 2.36 */
489 : #endif
490 :
491 15 : init_international_languages();
492 :
493 15 : res_struct = init_res_struct(argc, argv);
494 :
495 12 : if (res_struct != NULL && res_struct->opt != NULL && res_struct->comm != NULL)
496 : {
497 :
498 12 : if (res_struct->opt->list != NULL)
499 : {
500 4 : query = get_user_infos(res_struct->hostname, res_struct->opt->list, res_struct->opt);
501 4 : print_all_files(res_struct, query);
502 4 : free_query_t(query);
503 : }
504 :
505 12 : if (res_struct->opt->restore != NULL)
506 : {
507 8 : query = get_user_infos(res_struct->hostname, res_struct->opt->restore, res_struct->opt);
508 8 : restore_last_file(res_struct, query);
509 8 : free_query_t(query);
510 : }
511 :
512 12 : free_res_struct_t(res_struct);
513 :
514 12 : return EXIT_SUCCESS;
515 : }
516 : else
517 : {
518 : return EXIT_FAILURE;
519 : }
520 : }
|