1 /*
2  * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2018 Michael Rasmussen and the Claws Mail Team
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #ifdef HAVE_CONFIG_H
20 #  include "config.h"
21 #include "claws-features.h"
22 #endif
23 
24 #include <glib.h>
25 #include <glib/gi18n.h>
26 #include <glib/gstdio.h>
27 
28 #include "libarchive_archive.h"
29 
30 #ifndef _TEST
31 #	include "archiver.h"
32 #	include "utils.h"
33 #	include "mainwindow.h"
34 #   include "folder.h"
35 #endif
36 
37 #include <sys/types.h>
38 #include <sys/stat.h>
39 
40 #include <archive.h>
41 #include <archive_entry.h>
42 #include <fcntl.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <unistd.h>
47 #include <dirent.h>
48 #include <libgen.h>
49 
50 #define READ_BLOCK_SIZE 10240
51 
52 struct file_info {
53 	char* path;
54 	char* name;
55 };
56 
57 static GSList* msg_trash_list = NULL;
58 static GSList* file_list = NULL;
59 static gboolean stop_action = FALSE;
60 
61 #ifdef _TEST
62 static int permissions = 0;
63 #endif
64 
free_msg_trash(MsgTrash * trash)65 static void free_msg_trash(MsgTrash* trash) {
66     if (trash) {
67         debug_print("Freeing files in %s\n", folder_item_get_name(trash->item));
68         if (trash->msgs) {
69             g_slist_free(trash->msgs);
70         }
71         g_free(trash);
72     }
73 }
74 
new_msg_trash(FolderItem * item)75 MsgTrash* new_msg_trash(FolderItem* item) {
76     MsgTrash* msg_trash;
77     FolderType  type;
78 
79     g_return_val_if_fail(item != NULL, NULL);
80 
81     /* FolderType must be F_MH, F_MBOX, F_MAILDIR or F_IMAP */
82     type = item->folder->klass->type;
83     if (!(type == F_MH || type == F_MBOX ||
84             type == F_MAILDIR || type == F_IMAP))
85        return NULL;
86     msg_trash = g_new0(MsgTrash, 1);
87     msg_trash->item = item;
88     msg_trash->msgs = NULL;
89     msg_trash_list = g_slist_prepend(msg_trash_list, msg_trash);
90 
91     return msg_trash;
92     }
93 
archive_free_archived_files()94 void archive_free_archived_files() {
95     MsgTrash* mt = NULL;
96     gint    res;
97     GSList* l = NULL;
98 
99     for (l = msg_trash_list; l; l = g_slist_next(l)) {
100         mt = (MsgTrash *) l->data;
101         debug_print("Trashing messages in folder: %s\n",
102                 folder_item_get_name(mt->item));
103         res = folder_item_remove_msgs(mt->item, mt->msgs);
104         debug_print("Result was %d\n", res);
105         free_msg_trash(mt);
106     }
107     g_slist_free(msg_trash_list);
108     msg_trash_list = NULL;
109 }
110 
archive_add_msg_mark(MsgTrash * trash,MsgInfo * msg)111 void archive_add_msg_mark(MsgTrash* trash, MsgInfo* msg) {
112     g_return_if_fail(trash != NULL || msg != NULL);
113     debug_print("Marking msg #%d for removal\n", msg->msgnum);
114     trash->msgs = g_slist_prepend(trash->msgs, msg);
115 }
116 
free_all(GDate * date,gchar ** parts)117 static void free_all(GDate* date, gchar** parts) {
118     if (date)
119         g_date_free(date);
120     if (parts)
121         g_strfreev(parts);
122 }
123 
is_iso_string(gchar ** items)124 static gboolean is_iso_string(gchar** items) {
125     int i = -1;
126     gchar* item;
127 
128     while (*items) {
129         i++;
130         item = *items++;
131         debug_print("Date part %d: %s\n", i, item);
132         switch(i) {
133             case 0:
134                 if (strlen(item) != 4)
135                     return FALSE;
136                 break;
137             case 1:
138             case 2:
139                 if (strlen(item) != 2)
140                     return FALSE;
141                 break;
142             default:
143                 return FALSE;
144         }
145     }
146     debug_print("Leaving\n");
147     return (i == 2);
148 }
149 
iso2GDate(const gchar * date)150 static GDate* iso2GDate(const gchar* date) {
151     GDate*  gdate;
152     gchar** parts = NULL;
153     int     i;
154 
155     g_return_val_if_fail(date != NULL, NULL);
156 
157     gdate = g_date_new();
158     parts = g_strsplit(date, "-", 3);
159     if (!parts)
160         return NULL;
161     if (! is_iso_string(parts))
162         return NULL;
163     for (i = 0; i < 3; i++) {
164         int t = atoi(parts[i]);
165         switch (i) {
166             case 0:
167                 if (t < 1 || t > 9999) {
168                     free_all(gdate, parts);
169                     return NULL;
170                 }
171                 g_date_set_year(gdate, t);
172                 break;
173             case 1:
174                 if (t < 1 || t > 12) {
175                     free_all(gdate, parts);
176                     return NULL;
177                 }
178                 g_date_set_month(gdate, t);
179                 break;
180             case 2:
181                 if (t < 1 || t > 31) {
182                     free_all(gdate, parts);
183                     return NULL;
184                 }
185                 g_date_set_day(gdate, t);
186                 break;
187         }
188     }
189     g_strfreev(parts);
190     return gdate;
191 }
192 
before_date(time_t msg_mtime,const gchar * before)193 gboolean before_date(time_t msg_mtime, const gchar* before) {
194     gchar*      pos = NULL;
195     GDate*      date;
196     GDate*      file_t;
197     gboolean    res;
198 
199     debug_print("Cut-off date: %s\n", before);
200     if ((date = iso2GDate(before)) == NULL) {
201         g_warning("Bad date format: %s", before);
202         return FALSE;
203     }
204 
205     file_t = g_date_new();
206     g_date_set_time_t(file_t, msg_mtime);
207 
208     if (debug_get_mode()) {
209         pos = g_new0(char, 100);
210         g_date_strftime(pos, 100, "%F", file_t);
211         fprintf(stderr, "File date: %s\n", pos);
212         g_free(pos);
213     }
214 
215     if (! g_date_valid(file_t)) {
216         g_warning("Invalid msg date");
217         return FALSE;
218     }
219 
220     res = (g_date_compare(file_t, date) >= 0) ? FALSE : TRUE;
221     g_date_free(file_t);
222     return res;
223 }
224 
archive_free_file_info(struct file_info * file)225 static void archive_free_file_info(struct file_info* file) {
226 	if (! file)
227 		return;
228 	if (file->path)
229 		g_free(file->path);
230 	if (file->name)
231 		g_free(file->name);
232 	g_free(file);
233 	file = NULL;
234 }
235 
stop_archiving()236 void stop_archiving() {
237 	debug_print("stop action set to true\n");
238 	stop_action = TRUE;
239 }
240 
archive_free_file_list(gboolean md5,gboolean rename)241 void archive_free_file_list(gboolean md5, gboolean rename) {
242 	struct file_info* file = NULL;
243 	gchar* path = NULL;
244 
245 	debug_print("freeing file list\n");
246 	if (! file_list)
247 		return;
248 	while (file_list) {
249 		file = (struct file_info *) file_list->data;
250 		if (!rename && md5 && g_str_has_suffix(file->name, ".md5")) {
251 			path = g_strdup_printf("%s/%s", file->path, file->name);
252 			debug_print("unlinking %s\n", path);
253 			g_unlink(path);
254 			g_free(path);
255 		}
256 		if (rename) {
257 			path = g_strdup_printf("%s/%s", file->path, file->name);
258 			debug_print("unlinking %s\n", path);
259 			g_unlink(path);
260 			g_free(path);
261 		}
262 		archive_free_file_info(file);
263 		file_list->data = NULL;
264 		file_list = g_slist_next(file_list);
265 	}
266 	if (file_list) {
267 		g_slist_free(file_list);
268 		file_list = NULL;
269 	}
270 }
271 
archive_new_file_info()272 static struct file_info* archive_new_file_info() {
273 	struct file_info* new_file_info = malloc(sizeof(struct file_info));
274 
275 	new_file_info->path = NULL;
276 	new_file_info->name = NULL;
277 	return new_file_info;
278 }
279 
archive_add_to_list(struct file_info * file)280 static void archive_add_to_list(struct file_info* file) {
281 	if (! file)
282 		return;
283 	file_list = g_slist_prepend(file_list, (gpointer) file);
284 }
285 
strip_leading_dot_slash(gchar * path)286 static gchar* strip_leading_dot_slash(gchar* path) {
287 	if (path && strlen(path) > 1 && path[0] == '.' && path[1] == '/')
288 		return g_strdup(&(path[2]));
289 
290 	return g_strdup(path);
291 }
292 
get_full_path(struct file_info * file)293 static gchar* get_full_path(struct file_info* file) {
294 	char* path = malloc(PATH_MAX);
295 
296 	if (file->path && *(file->path))
297 		sprintf(path, "%s/%s", file->path, file->name);
298 	else
299 		sprintf(path, "%s", file->name);
300 	return path;
301 }
302 
303 #ifdef _TEST
strip_leading_slash(gchar * path)304 static gchar* strip_leading_slash(gchar* path) {
305 	gchar* stripped = path;
306 	gchar* result = NULL;
307 
308 	if (stripped && stripped[0] == '/') {
309 		++stripped;
310 		result = g_strdup(stripped);
311 	}
312 	else
313 		result = g_strdup(path);
314 	return result;
315 }
316 
archive_get_permissions()317 static int archive_get_permissions() {
318 	return permissions;
319 }
320 
321 
archive_set_permissions(int perm)322 void archive_set_permissions(int perm) {
323 	permissions = perm;
324 }
325 
archive_copy_data(struct archive * in,struct archive * out)326 static int archive_copy_data(struct archive* in, struct archive* out) {
327 	const void* buf;
328 	size_t size;
329 	off_t offset;
330 	int res = ARCHIVE_OK;
331 
332 	while (res == ARCHIVE_OK) {
333 		res = archive_read_data_block(in, &buf, &size, &offset);
334 		if (res == ARCHIVE_OK) {
335 			res = archive_write_data_block(out, buf, size, offset);
336 		}
337 	}
338 	return (res == ARCHIVE_EOF) ? ARCHIVE_OK : res;
339 }
340 #endif
341 
archive_add_file(gchar * path)342 void archive_add_file(gchar* path) {
343 	struct file_info* file;
344 	gchar* filename = NULL;
345 
346 	g_return_if_fail(path != NULL);
347 
348 #ifndef _TEST
349 	debug_print("add %s to list\n", path);
350 #endif
351 	filename = g_strrstr_len(path, strlen(path), "/");
352 	if (! filename)
353 		g_warning("no filename in path '%s'", path);
354 	g_return_if_fail(filename != NULL);
355 
356 	filename++;
357 	file = archive_new_file_info();
358 	file->name = g_strdup(filename);
359 	file->path = strip_leading_dot_slash(dirname(path));
360 	archive_add_to_list(file);
361 }
362 
archive_get_file_list()363 GSList* archive_get_file_list() {
364 	return file_list;
365 }
366 
367 #ifdef _TEST
archive_extract(const char * archive_name,int flags)368 const gchar* archive_extract(const char* archive_name, int flags) {
369 	struct archive* in;
370 	struct archive* out;
371 	struct archive_entry* entry;
372 	int res = ARCHIVE_OK;
373 	gchar* buf = NULL;
374 	const char* result == NULL;
375 
376 	g_return_val_if_fail(archive_name != NULL, ARCHIVE_FATAL);
377 
378 	fprintf(stdout, "%s: extracting\n", archive_name);
379 	in = archive_read_new();
380 	if ((res = archive_read_support_format_tar(in)) == ARCHIVE_OK) {
381 		if ((res = archive_read_support_compression_gzip(in)) == ARCHIVE_OK) {
382 #if ARCHIVE_VERSION_NUMBER < 3000000
383 			if ((res = archive_read_open_file(
384 #else
385 			if ((res = archive_read_open_filename(
386 #endif
387 				in, archive_name, READ_BLOCK_SIZE)) != ARCHIVE_OK) {
388 				g_warning("%s: %s", archive_name, archive_error_string(in));
389 				result = archive_error_string(in);
390 			}
391 			else {
392 				out = archive_write_disk_new();
393 				if ((res = archive_write_disk_set_options(
394 								out, flags)) == ARCHIVE_OK) {
395 					res = archive_read_next_header(in, &entry);
396 					while (res == ARCHIVE_OK) {
397 						res = archive_write_header(out, entry);
398 						if (res != ARCHIVE_OK) {
399 							g_warning("%s", archive_error_string(out));
400 							/* skip this file an continue */
401 							res = ARCHIVE_OK;
402 						}
403 						else {
404 							res = archive_copy_data(in, out);
405 							if (res != ARCHIVE_OK) {
406 								g_warning("%s", archive_error_string(in));
407 								/* skip this file an continue */
408 								res = ARCHIVE_OK;
409 							}
410 							else
411 								res = archive_read_next_header(in, &entry);
412 						}
413 					}
414 					if (res == ARCHIVE_EOF)
415 						res = ARCHIVE_OK;
416 					if (res != ARCHIVE_OK) {
417 						gchar *e = archive_error_string(in);
418 						g_warning("%s: %s", archive_name, e? e: "unknown error");
419 						result = e;
420 					}
421 				}
422 				else
423 					result = archive_error_string(out);
424 				archive_read_close(in);
425 			}
426 #if ARCHIVE_VERSION_NUMBER < 3000000
427 			archive_read_finish(in);
428 #else
429 			archive_read_free(in);
430 #endif
431 		}
432 		else
433 			result = archive_error_string(in);
434 	}
435 	else
436 		result = archive_error_string(in);
437 	return result;
438 }
439 #endif
440 
archive_create(const char * archive_name,GSList * files,COMPRESS_METHOD method,ARCHIVE_FORMAT format)441 const gchar* archive_create(const char* archive_name, GSList* files,
442 			COMPRESS_METHOD method, ARCHIVE_FORMAT format) {
443 	struct archive* arch;
444 
445 #ifndef _TEST
446 	gint num = 0;
447 	gint total = g_slist_length (files);
448 #endif
449 
450 	g_return_val_if_fail(files != NULL, "No files for archiving");
451 
452 	debug_print("File: %s\n", archive_name);
453 	arch = archive_write_new();
454 	switch (method) {
455 		case GZIP:
456 #if ARCHIVE_VERSION_NUMBER < 3000000
457 			if (archive_write_set_compression_gzip(arch) != ARCHIVE_OK)
458 #else
459 			if (archive_write_add_filter_gzip(arch) != ARCHIVE_OK)
460 #endif
461 				return archive_error_string(arch);
462 			break;
463 		case BZIP2:
464 #if ARCHIVE_VERSION_NUMBER < 3000000
465 			if (archive_write_set_compression_bzip2(arch) != ARCHIVE_OK)
466 #else
467 			if (archive_write_add_filter_bzip2(arch) != ARCHIVE_OK)
468 #endif
469 				return archive_error_string(arch);
470 			break;
471 		case COMPRESS:
472 #if ARCHIVE_VERSION_NUMBER < 3000000
473 			if (archive_write_set_compression_compress(arch) != ARCHIVE_OK)
474 #else
475 			if (archive_write_add_filter_compress(arch) != ARCHIVE_OK)
476 #endif
477     			        return archive_error_string(arch);
478 			break;
479 #if ARCHIVE_VERSION_NUMBER >= 2006990
480 		case LZMA:
481 #if ARCHIVE_VERSION_NUMBER < 3000000
482 			if (archive_write_set_compression_lzma(arch) != ARCHIVE_OK)
483 #else
484 			if (archive_write_add_filter_lzma(arch) != ARCHIVE_OK)
485 #endif
486 				return archive_error_string(arch);
487 			break;
488 		case XZ:
489 #if ARCHIVE_VERSION_NUMBER < 3000000
490 			if (archive_write_set_compression_xz(arch) != ARCHIVE_OK)
491 #else
492 			if (archive_write_add_filter_xz(arch) != ARCHIVE_OK)
493 #endif
494 				return archive_error_string(arch);
495 			break;
496 #endif
497 #if ARCHIVE_VERSION_NUMBER >= 3000000
498 		case LZIP:
499 			if (archive_write_add_filter_lzip(arch) != ARCHIVE_OK)
500 				return archive_error_string(arch);
501 			break;
502 #endif
503 #if ARCHIVE_VERSION_NUMBER >= 3001000
504 		case LRZIP:
505 			if (archive_write_add_filter_lrzip(arch) != ARCHIVE_OK)
506 				return archive_error_string(arch);
507 			break;
508 		case LZOP:
509 			if (archive_write_add_filter_lzop(arch) != ARCHIVE_OK)
510 				return archive_error_string(arch);
511 			break;
512 		case GRZIP:
513 			if (archive_write_add_filter_grzip(arch) != ARCHIVE_OK)
514 				return archive_error_string(arch);
515 			break;
516 #endif
517 #if ARCHIVE_VERSION_NUMBER >= 3001900
518 		case LZ4:
519 			if (archive_write_add_filter_lz4(arch) != ARCHIVE_OK)
520 				return archive_error_string(arch);
521 			break;
522 #endif
523 		case NO_COMPRESS:
524 #if ARCHIVE_VERSION_NUMBER < 3000000
525 			if (archive_write_set_compression_none(arch) != ARCHIVE_OK)
526 #else
527 			if (archive_write_add_filter_none(arch) != ARCHIVE_OK)
528 #endif
529 				return archive_error_string(arch);
530 			break;
531 	}
532 	switch (format) {
533 		case TAR:
534 			if (archive_write_set_format_ustar(arch) != ARCHIVE_OK)
535 				return archive_error_string(arch);
536 			break;
537 		case SHAR:
538 			if (archive_write_set_format_shar(arch) != ARCHIVE_OK)
539 				return archive_error_string(arch);
540 			break;
541 		case PAX:
542 			if (archive_write_set_format_pax(arch) != ARCHIVE_OK)
543 				return archive_error_string(arch);
544 			break;
545 		case CPIO:
546 			if (archive_write_set_format_cpio(arch) != ARCHIVE_OK)
547 				return archive_error_string(arch);
548 			break;
549 		case NO_FORMAT:
550 			return "Missing archive format";
551 	}
552 #if ARCHIVE_VERSION_NUMBER < 3000000
553 	if (archive_write_open_file(arch, archive_name) != ARCHIVE_OK)
554 #else
555 	if (archive_write_open_filename(arch, archive_name) != ARCHIVE_OK)
556 #endif
557 		return archive_error_string(arch);
558 
559 	while (files && ! stop_action) {
560 		struct file_info* file;
561 		gchar* filename = NULL;
562 
563 #ifndef _TEST
564 		set_progress_print_all(num++, total, 30);
565 #endif
566 		file = (struct file_info *) files->data;
567 		if (!file)
568 			continue;
569 		filename = get_full_path(file);
570 		/* libarchive will crash if instructed to add archive to it self */
571 		if (g_utf8_collate(archive_name, filename) == 0) {
572 			g_warning("%s: not dumping to '%s'", archive_name, filename);
573 #ifndef _TEST
574 			debug_print("%s: not dumping to '%s'\n", archive_name, filename);
575 #endif
576 		}
577 		else {
578 			struct archive_entry* entry;
579 			char* buf = NULL;
580 			ssize_t len;
581 			GError* err = NULL;
582 			GStatBuf st;
583 			int fd;
584 			gchar* msg = NULL;
585 
586 #ifndef _TEST
587 			debug_print("Adding: %s\n", filename);
588 			msg = g_strdup_printf("%s", filename);
589 			set_progress_file_label(msg);
590 			g_free(msg);
591 #endif
592 			if ((fd = g_open(filename, O_RDONLY, 0)) == -1) {
593 				FILE_OP_ERROR(filename, "g_open");
594 			}
595 			else {
596 				if (g_stat(filename, &st) == -1) {
597 					FILE_OP_ERROR(filename, "g_stat");
598 				} else {
599 					entry = archive_entry_new();
600 					archive_entry_copy_stat(entry, &st);
601 					archive_entry_set_pathname(entry, filename);
602 					if (S_ISLNK(st.st_mode)) {
603 
604 						buf = g_file_read_link(filename, &err);
605 						if (err) {
606 							FILE_OP_ERROR(filename, "g_file_read_link");
607 						} else {
608 							archive_entry_set_symlink(entry, buf);
609 							g_free(buf);
610 							archive_entry_set_size(entry, 0);
611 							archive_write_header(arch, entry);
612 						}
613 					}
614 					else {
615 						if (archive_write_header(arch, entry) != ARCHIVE_OK)
616 							g_warning("%s", archive_error_string(arch));
617 						if ((buf = malloc(READ_BLOCK_SIZE)) != NULL) {
618 							len = read(fd, buf, READ_BLOCK_SIZE);
619 							while (len > 0) {
620 								if (archive_write_data(arch, buf, len) == -1)
621 									g_warning("%s", archive_error_string(arch));
622 								memset(buf, 0, READ_BLOCK_SIZE);
623 								len = read(fd, buf, READ_BLOCK_SIZE);
624 							}
625 							g_free(buf);
626 						}
627 					}
628 					archive_entry_free(entry);
629 				}
630 				if (!g_close(fd, &err) || err)
631 					FILE_OP_ERROR(filename, "g_close");
632 			}
633 		}
634 		g_free(filename);
635 		files = g_slist_next(files);
636 	}
637 #ifndef _TEST
638 	if (stop_action)
639 		unlink(archive_name);
640 	stop_action = FALSE;
641 #endif
642 	archive_write_close(arch);
643 #if ARCHIVE_VERSION_NUMBER < 3000000
644 	archive_write_finish(arch);
645 #else
646 	archive_write_free(arch);
647 #endif
648 	return NULL;
649 }
650 
651 #ifdef _TEST
archive_scan_folder(const char * dir)652 void archive_scan_folder(const char* dir) {
653 	GStatBuf st;
654 	DIR* root;
655 	struct dirent* ent;
656 	gchar cwd[PATH_MAX];
657 	gchar path[PATH_MAX];
658 
659 	getcwd(cwd, PATH_MAX);
660 
661 	if (g_stat(dir, &st) == -1)
662 		return;
663 	if (! S_ISDIR(st.st_mode))
664 		return;
665 	if (!(root = opendir(dir)))
666 		return;
667 	chdir(dir);
668 
669 	while ((ent = readdir(root)) != NULL) {
670 		if (strcmp(".", ent->d_name) == 0 || strcmp("..", ent->d_name) == 0)
671 			continue;
672 		if (g_stat(ent->d_name, &st) == -1) {
673 			FILE_OP_ERROR(filename, "g_stat");
674 			continue;
675 		}
676 		sprintf(path, "%s/%s", dir, ent->d_name);
677 		if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) {
678 			archive_add_file(path);
679 		}
680 		else if (S_ISDIR(st.st_mode)) {
681 			archive_scan_folder(path);
682 		}
683 	}
684 	chdir(cwd);
685 	closedir(root);
686 }
687 
main(int argc,char ** argv)688 int main(int argc, char** argv) {
689 	char* archive = NULL;
690 	char buf[PATH_MAX];
691 	int pid;
692 	int opt;
693 	int perm = ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_TIME |
694 		ARCHIVE_EXTRACT_ACL | ARCHIVE_EXTRACT_FFLAGS | ARCHIVE_EXTRACT_SECURE_SYMLINKS;
695 	gchar cwd[PATH_MAX];
696 	gboolean remove = FALSE;
697 	const char *p = NULL;
698 	int res;
699 
700 	getcwd(cwd, PATH_MAX);
701 
702 	while (*++argv && **argv == '-') {
703 		p = *argv + 1;
704 
705 		while ((opt = *p++) != '\0') {
706 			switch(opt) {
707 				case 'a':
708 					if (*p != '\0')
709 						archive = (char *) p;
710 					else
711 						archive = *++argv;
712 					p += strlen(p);
713 					break;
714 				case 'r':
715 					remove = TRUE;
716 					break;
717 			}
718 		}
719 	}
720 	if (! archive) {
721 		fprintf(stderr, "Missing archive name!\n");
722 		return EXIT_FAILURE;
723 	}
724 	if (!*argv) {
725 		fprintf(stderr, "Expected arguments after options!\n");
726 		return EXIT_FAILURE;
727 	}
728 
729 	while (*argv) {
730 		archive_scan_folder(*argv++);
731 		res = archive_create(archive, file_list);
732 		if (res != ARCHIVE_OK) {
733 			fprintf(stderr, "%s: Creating archive failed\n", archive);
734 			return EXIT_FAILURE;
735 		}
736 	}
737 	pid = (int) getpid();
738 	sprintf(buf, "/tmp/%d", pid);
739 	fprintf(stdout, "Creating: %s\n", buf);
740 	mkdir(buf, 0700);
741 	chdir(buf);
742 	if (strcmp(dirname(archive), ".") == 0)
743 		sprintf(buf, "%s/%s", cwd, basename(archive));
744 	else
745 		sprintf(buf, "%s", archive);
746 	archive_extract(buf, perm);
747 	chdir(cwd);
748 	if (remove) {
749 		sprintf(buf, "rm -rf /tmp/%d", pid);
750 		fprintf(stdout, "Executing: %s\n", buf);
751 		system(buf);
752 	}
753 	archive_free_list(file_list);
754 	return EXIT_SUCCESS;
755 }
756 #endif
757 
archiver_set_tooltip(GtkWidget * widget,gchar * text)758 void archiver_set_tooltip(GtkWidget* widget, gchar* text) {
759     gtk_widget_set_tooltip_text(widget, text);
760     g_free(text);
761 }
762