talons

Fork of Claws Mail https://www.claws-mail
Log | Files | Refs | README | LICENSE

file-utils.c (12867B)


      1 /*
      2  * Claws Mail -- a GTK based, lightweight, and fast e-mail client
      3  * Copyright (C) 1999-2024 the Claws Mail team and Colin Leroy
      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 #include <glib.h>
     20 
     21 #include <sys/wait.h>
     22 
     23 #include <err.h>
     24 #include <errno.h>
     25 #include <stdio.h>
     26 #include <unistd.h>
     27 #include <fcntl.h>
     28 
     29 #include "defs.h"
     30 #include "codeconv.h"
     31 #include "utils.h"
     32 #include "file-utils.h"
     33 
     34 gint file_strip_crs(const gchar *file)
     35 {
     36 	FILE *fp = NULL, *outfp = NULL;
     37 	gchar buf[4096];
     38 	gchar *out = get_tmp_file();
     39 	if (file == NULL)
     40 		goto freeout;
     41 
     42 	fp = g_fopen(file, "rb");
     43 	if (!fp)
     44 		goto freeout;
     45 
     46 	outfp = g_fopen(out, "wb");
     47 	if (!outfp) {
     48 		fclose(fp);
     49 		goto freeout;
     50 	}
     51 
     52 	while (fgets(buf, sizeof (buf), fp) != NULL) {
     53 		strcrchomp(buf);
     54 		if (fputs(buf, outfp) == EOF) {
     55 			fclose(fp);
     56 			fclose(outfp);
     57 			goto unlinkout;
     58 		}
     59 	}
     60 
     61 	fclose(fp);
     62 	if (fclose(outfp) == EOF) {
     63 		goto unlinkout;
     64 	}
     65 	if (rename(out, file) < 0)
     66 		goto unlinkout;
     67 
     68 	g_free(out);
     69 	return 0;
     70 unlinkout:
     71 	unlink(out);
     72 freeout:
     73 	g_free(out);
     74 	return -1;
     75 }
     76 
     77 /*
     78  * Append src file body to the tail of dest file.
     79  * Now keep_backup has no effects.
     80  */
     81 gint append_file(const gchar *src, const gchar *dest, gboolean keep_backup)
     82 {
     83 	FILE *src_fp, *dest_fp;
     84 	gint n_read;
     85 	gchar buf[BUFSIZ];
     86 
     87 	gboolean err = FALSE;
     88 
     89 	if ((src_fp = g_fopen(src, "rb")) == NULL) {
     90 		FILE_OP_ERROR(src, "g_fopen");
     91 		return -1;
     92 	}
     93 
     94 	if ((dest_fp = g_fopen(dest, "ab")) == NULL) {
     95 		FILE_OP_ERROR(dest, "g_fopen");
     96 		fclose(src_fp);
     97 		return -1;
     98 	}
     99 
    100 	while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
    101 		if (n_read < sizeof(buf) && ferror(src_fp))
    102 			break;
    103 		if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
    104 			g_warning("writing to %s failed", dest);
    105 			fclose(dest_fp);
    106 			fclose(src_fp);
    107 			unlink(dest);
    108 			return -1;
    109 		}
    110 	}
    111 
    112 	if (ferror(src_fp)) {
    113 		FILE_OP_ERROR(src, "fread");
    114 		err = TRUE;
    115 	}
    116 	fclose(src_fp);
    117 	if (fclose(dest_fp) == EOF) {
    118 		FILE_OP_ERROR(dest, "fclose");
    119 		err = TRUE;
    120 	}
    121 
    122 	if (err) {
    123 		unlink(dest);
    124 		return -1;
    125 	}
    126 
    127 	return 0;
    128 }
    129 
    130 gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup)
    131 {
    132 	FILE *src_fp, *dest_fp;
    133 	gint n_read;
    134 	gchar buf[BUFSIZ];
    135 	gchar *dest_bak = NULL;
    136 	gboolean err = FALSE;
    137 
    138 	if ((src_fp = g_fopen(src, "rb")) == NULL) {
    139 		FILE_OP_ERROR(src, "g_fopen");
    140 		return -1;
    141 	}
    142 	if (is_file_exist(dest)) {
    143 		dest_bak = g_strconcat(dest, ".bak", NULL);
    144 		if (rename(dest, dest_bak) < 0) {
    145 			warn("rename %s to %s", dest, dest_bak);
    146 			fclose(src_fp);
    147 			g_free(dest_bak);
    148 			return -1;
    149 		}
    150 	}
    151 
    152 	if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
    153 		FILE_OP_ERROR(dest, "g_fopen");
    154 		fclose(src_fp);
    155 		if (dest_bak) {
    156 			if (rename(dest_bak, dest) < 0)
    157 				FILE_OP_ERROR(dest_bak, "rename");
    158 			g_free(dest_bak);
    159 		}
    160 		return -1;
    161 	}
    162 
    163 	while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
    164 		if (n_read < sizeof(buf) && ferror(src_fp))
    165 			break;
    166 		if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
    167 			g_warning("writing to %s failed", dest);
    168 			fclose(dest_fp);
    169 			fclose(src_fp);
    170 			if (unlink(dest) < 0)
    171                                 FILE_OP_ERROR(dest, "unlink");
    172 			if (dest_bak) {
    173 				if (rename(dest_bak, dest) < 0)
    174 					FILE_OP_ERROR(dest_bak, "rename");
    175 				g_free(dest_bak);
    176 			}
    177 			return -1;
    178 		}
    179 	}
    180 
    181 	if (ferror(src_fp)) {
    182 		FILE_OP_ERROR(src, "fread");
    183 		err = TRUE;
    184 	}
    185 	fclose(src_fp);
    186 	if (fclose(dest_fp) == EOF) {
    187 		FILE_OP_ERROR(dest, "fclose");
    188 		err = TRUE;
    189 	}
    190 
    191 	if (err) {
    192 		if (unlink(dest) < 0)
    193                         FILE_OP_ERROR(dest, "unlink");
    194 		if (dest_bak) {
    195 			if (rename(dest_bak, dest) < 0)
    196 				FILE_OP_ERROR(dest_bak, "rename");
    197 			g_free(dest_bak);
    198 		}
    199 		return -1;
    200 	}
    201 
    202 	if (keep_backup == FALSE && dest_bak)
    203 		if (unlink(dest_bak) < 0)
    204                         FILE_OP_ERROR(dest_bak, "unlink");
    205 
    206 	g_free(dest_bak);
    207 
    208 	return 0;
    209 }
    210 
    211 gint copy_file_part_to_fp(FILE *fp, off_t offset, size_t length, FILE *dest_fp)
    212 {
    213 	gint n_read;
    214 	gint bytes_left, to_read;
    215 	gchar buf[BUFSIZ];
    216 
    217 	if (fseek(fp, offset, SEEK_SET) < 0) {
    218 		perror("fseek");
    219 		return -1;
    220 	}
    221 
    222 	bytes_left = length;
    223 	to_read = MIN(bytes_left, sizeof(buf));
    224 
    225 	while ((n_read = fread(buf, sizeof(gchar), to_read, fp)) > 0) {
    226 		if (n_read < to_read && ferror(fp))
    227 			break;
    228 		if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
    229 			return -1;
    230 		}
    231 		bytes_left -= n_read;
    232 		if (bytes_left == 0)
    233 			break;
    234 		to_read = MIN(bytes_left, sizeof(buf));
    235 	}
    236 
    237 	if (ferror(fp)) {
    238 		perror("fread");
    239 		return -1;
    240 	}
    241 
    242 	return 0;
    243 }
    244 
    245 gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
    246 {
    247 	FILE *dest_fp;
    248 	gboolean err = FALSE;
    249 
    250 	if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
    251 		FILE_OP_ERROR(dest, "g_fopen");
    252 		return -1;
    253 	}
    254 
    255 	if (copy_file_part_to_fp(fp, offset, length, dest_fp) < 0)
    256 		err = TRUE;
    257 
    258 	if (fclose(dest_fp) == EOF) {
    259 		FILE_OP_ERROR(dest, "fclose");
    260 		err = TRUE;
    261 	}
    262 
    263 	if (err) {
    264 		g_warning("writing to %s failed", dest);
    265 		unlink(dest);
    266 		return -1;
    267 	}
    268 
    269 	return 0;
    270 }
    271 
    272 gint canonicalize_file(const gchar *src, const gchar *dest)
    273 {
    274 	FILE *src_fp, *dest_fp;
    275 	gchar buf[BUFFSIZE];
    276 	gint len;
    277 	gboolean err = FALSE;
    278 	gboolean last_linebreak = FALSE;
    279 
    280 	if (src == NULL || dest == NULL)
    281 		return -1;
    282 
    283 	if ((src_fp = g_fopen(src, "rb")) == NULL) {
    284 		FILE_OP_ERROR(src, "g_fopen");
    285 		return -1;
    286 	}
    287 
    288 	if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
    289 		FILE_OP_ERROR(dest, "g_fopen");
    290 		fclose(src_fp);
    291 		return -1;
    292 	}
    293 
    294 	while (fgets(buf, sizeof(buf), src_fp) != NULL) {
    295 		gint r = 0;
    296 
    297 		len = strlen(buf);
    298 		if (len == 0) break;
    299 		last_linebreak = FALSE;
    300 
    301 		if (buf[len - 1] != '\n') {
    302 			last_linebreak = TRUE;
    303 			r = fputs(buf, dest_fp);
    304 		} else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
    305 			r = fputs(buf, dest_fp);
    306 		} else {
    307 			if (len > 1) {
    308 				r = fwrite(buf, 1, len - 1, dest_fp);
    309 				if (r != (len -1))
    310 					r = EOF;
    311 			}
    312 			if (r != EOF)
    313 				r = fputs("\r\n", dest_fp);
    314 		}
    315 
    316 		if (r == EOF) {
    317 			g_warning("writing to %s failed", dest);
    318 			fclose(dest_fp);
    319 			fclose(src_fp);
    320 			unlink(dest);
    321 			return -1;
    322 		}
    323 	}
    324 
    325 	if (last_linebreak == TRUE) {
    326 		if (fputs("\r\n", dest_fp) == EOF)
    327 			err = TRUE;
    328 	}
    329 
    330 	if (ferror(src_fp)) {
    331 		FILE_OP_ERROR(src, "fgets");
    332 		err = TRUE;
    333 	}
    334 	fclose(src_fp);
    335 	if (fclose(dest_fp) == EOF) {
    336 		FILE_OP_ERROR(dest, "fclose");
    337 		err = TRUE;
    338 	}
    339 
    340 	if (err) {
    341 		unlink(dest);
    342 		return -1;
    343 	}
    344 
    345 	return 0;
    346 }
    347 
    348 gint str_write_to_file(const gchar *str, char *file)
    349 {
    350 	if (strlen(str) == 0)
    351 		return 0;
    352 
    353 	FILE *fp = fopen(file, "wb");
    354 	if (fp == NULL) {
    355 		warn("open %s", file);
    356 		return -1;
    357 	}
    358 
    359 	size_t len = strlen(str);
    360 	size_t n = fwrite(str, 1, strlen(str), fp);
    361 	if (n != len) {
    362 		warn("short write to %s: expected %d bytes but wrote %d", file, len, n);
    363 		fclose(fp);
    364 		remove(file);
    365 		return -1;
    366 	}
    367 
    368 	if (fclose(fp) == EOF) {
    369 		warn("close %s", file);
    370 		remove(file);
    371 		return -1;
    372 	}
    373 	return 0;
    374 }
    375 
    376 static gchar *file_read_stream_to_str_full(FILE *fp, gboolean recode)
    377 {
    378 	GByteArray *array;
    379 	guchar buf[BUFSIZ];
    380 	gint n_read;
    381 	gchar *str;
    382 
    383 	cm_return_val_if_fail(fp != NULL, NULL);
    384 
    385 	array = g_byte_array_new();
    386 
    387 	while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
    388 		if (n_read < sizeof(buf) && ferror(fp))
    389 			break;
    390 		g_byte_array_append(array, buf, n_read);
    391 	}
    392 
    393 	if (ferror(fp)) {
    394 		FILE_OP_ERROR("file stream", "fread");
    395 		g_byte_array_free(array, TRUE);
    396 		return NULL;
    397 	}
    398 
    399 	buf[0] = '\0';
    400 	g_byte_array_append(array, buf, 1);
    401 	str = (gchar *)array->data;
    402 	g_byte_array_free(array, FALSE);
    403 
    404 	if (recode && !g_utf8_validate(str, -1, NULL)) {
    405 		const gchar *src_codeset, *dest_codeset;
    406 		gchar *tmp = NULL;
    407 		src_codeset = conv_get_locale_charset_str();
    408 		dest_codeset = CS_UTF_8;
    409 		tmp = conv_codeset_strdup(str, src_codeset, dest_codeset);
    410 		g_free(str);
    411 		str = tmp;
    412 	}
    413 
    414 	return str;
    415 }
    416 
    417 static gchar *file_read_to_str_full(const gchar *file, gboolean recode)
    418 {
    419 	FILE *fp;
    420 	gchar *str;
    421 	GStatBuf s;
    422 	gint fd, err;
    423 	struct timeval timeout = {1, 0};
    424 	fd_set fds;
    425 	int fflags = 0;
    426 
    427 	cm_return_val_if_fail(file != NULL, NULL);
    428 
    429 	if (g_stat(file, &s) != 0) {
    430 		FILE_OP_ERROR(file, "stat");
    431 		return NULL;
    432 	}
    433 	if (S_ISDIR(s.st_mode)) {
    434 		g_warning("%s: is a directory", file);
    435 		return NULL;
    436 	}
    437 
    438 	/* test whether the file is readable without blocking */
    439 	fd = g_open(file, O_RDONLY | O_NONBLOCK, 0);
    440 	if (fd == -1) {
    441 		FILE_OP_ERROR(file, "open");
    442 		return NULL;
    443 	}
    444 
    445 	FD_ZERO(&fds);
    446 	FD_SET(fd, &fds);
    447 
    448 	/* allow for one second */
    449 	err = select(fd+1, &fds, NULL, NULL, &timeout);
    450 	if (err <= 0 || !FD_ISSET(fd, &fds)) {
    451 		if (err < 0) {
    452 			FILE_OP_ERROR(file, "select");
    453 		} else {
    454 			g_warning("%s: doesn't seem readable", file);
    455 		}
    456 		close(fd);
    457 		return NULL;
    458 	}
    459 
    460 	/* Now clear O_NONBLOCK */
    461 	if ((fflags = fcntl(fd, F_GETFL)) < 0) {
    462 		FILE_OP_ERROR(file, "fcntl (F_GETFL)");
    463 		close(fd);
    464 		return NULL;
    465 	}
    466 	if (fcntl(fd, F_SETFL, (fflags & ~O_NONBLOCK)) < 0) {
    467 		FILE_OP_ERROR(file, "fcntl (F_SETFL)");
    468 		close(fd);
    469 		return NULL;
    470 	}
    471 
    472 	/* get the FILE pointer */
    473 	fp = fdopen(fd, "rb");
    474 
    475 	if (fp == NULL) {
    476 		FILE_OP_ERROR(file, "fdopen");
    477 		close(fd); /* if fp isn't NULL, we'll use fclose instead! */
    478 		return NULL;
    479 	}
    480 
    481 	str = file_read_stream_to_str_full(fp, recode);
    482 
    483 	fclose(fp);
    484 
    485 	return str;
    486 }
    487 
    488 gchar *file_read_to_str(const gchar *file)
    489 {
    490 	return file_read_to_str_full(file, TRUE);
    491 }
    492 gchar *file_read_stream_to_str(FILE *fp)
    493 {
    494 	return file_read_stream_to_str_full(fp, TRUE);
    495 }
    496 
    497 gint copy_dir(const gchar *src, const gchar *dst)
    498 {
    499 	GDir *dir;
    500 	const gchar *name;
    501 
    502 	if ((dir = g_dir_open(src, 0, NULL)) == NULL) {
    503 		warn("open %s", src);
    504 		return -1;
    505 	}
    506 
    507 	if (mkdir(dst, 0700) < 0) {
    508 		g_dir_close(dir);
    509 		return -1;
    510 	}
    511 
    512 	while ((name = g_dir_read_name(dir)) != NULL) {
    513 		gchar *old_file, *new_file;
    514 		gint r = 0;
    515 		old_file = g_strconcat(src, G_DIR_SEPARATOR_S, name, NULL);
    516 		new_file = g_strconcat(dst, G_DIR_SEPARATOR_S, name, NULL);
    517 		debug_print("copying: %s -> %s\n", old_file, new_file);
    518 		if (g_file_test(old_file, G_FILE_TEST_IS_REGULAR)) {
    519 			r = copy_file(old_file, new_file, TRUE);
    520 		}
    521 		else if (g_file_test(old_file, G_FILE_TEST_IS_SYMLINK)) {
    522 			GError *error = NULL;
    523 			gchar *target = g_file_read_link(old_file, &error);
    524 			if (error) {
    525 				g_warning("couldn't read link: %s", error->message);
    526 				g_error_free(error);
    527 			}
    528 			if (target) {
    529 				r = symlink(target, new_file);
    530 				g_free(target);
    531 			}
    532 		}
    533 		else if (g_file_test(old_file, G_FILE_TEST_IS_DIR)) {
    534 			r = copy_dir(old_file, new_file);
    535 		}
    536 		g_free(old_file);
    537 		g_free(new_file);
    538 		if (r < 0) {
    539 			g_dir_close(dir);
    540 			return r;
    541 		}
    542 	}
    543 	g_dir_close(dir);
    544 	return 0;
    545 }
    546 
    547 FILE *my_tmpfile(void)
    548 {
    549 	const gchar suffix[] = ".XXXXXX";
    550 	const gchar *tmpdir;
    551 	guint tmplen;
    552 	const gchar *progname;
    553 	guint proglen;
    554 	gchar *fname;
    555 	gint fd;
    556 	FILE *fp;
    557 	gchar buf[2]="\0";
    558 
    559 	tmpdir = get_tmp_dir();
    560 	tmplen = strlen(tmpdir);
    561 	progname = g_get_prgname();
    562 	if (progname == NULL)
    563 		progname = "claws-mail";
    564 	proglen = strlen(progname);
    565 	Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
    566 		return tmpfile());
    567 
    568 	memcpy(fname, tmpdir, tmplen);
    569 	fname[tmplen] = G_DIR_SEPARATOR;
    570 	memcpy(fname + tmplen + 1, progname, proglen);
    571 	memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
    572 
    573 	fd = g_mkstemp(fname);
    574 	if (fd < 0)
    575 		return tmpfile();
    576 
    577 	unlink(fname);
    578 
    579 	/* verify that we can write in the file after unlinking */
    580 	if (write(fd, buf, 1) < 0) {
    581 		close(fd);
    582 		return tmpfile();
    583 	}
    584 
    585 	fp = fdopen(fd, "w+b");
    586 	if (!fp)
    587 		close(fd);
    588 	else {
    589 		rewind(fp);
    590 		return fp;
    591 	}
    592 
    593 	return tmpfile();
    594 }
    595 
    596 FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
    597 {
    598 	int fd;
    599 	*filename = g_strdup_printf("%s%cclaws.XXXXXX", dir, G_DIR_SEPARATOR);
    600 	fd = g_mkstemp(*filename);
    601 	if (fd < 0)
    602 		return NULL;
    603 	return fdopen(fd, "w+");
    604 }
    605 
    606 FILE *str_open_as_stream(const gchar *str)
    607 {
    608 	FILE *fp;
    609 	size_t len;
    610 
    611 	cm_return_val_if_fail(str != NULL, NULL);
    612 
    613 	fp = my_tmpfile();
    614 	if (!fp) {
    615 		FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
    616 		return NULL;
    617 	}
    618 
    619 	len = strlen(str);
    620 	if (len == 0) return fp;
    621 
    622 	if (fwrite(str, 1, len, fp) != len) {
    623 		FILE_OP_ERROR("str_open_as_stream", "fwrite");
    624 		fclose(fp);
    625 		return NULL;
    626 	}
    627 
    628 	rewind(fp);
    629 	return fp;
    630 }
    631 
    632 gint prefs_chmod_mode(gchar *chmod_pref)
    633 {
    634 	gint newmode = 0;
    635 	gchar *tmp;
    636 
    637 	if (chmod_pref) {
    638 		newmode = strtol(chmod_pref, &tmp, 8);
    639 		if (!(*(chmod_pref) && !(*tmp)))
    640 			newmode = 0;
    641 	}
    642 
    643 	return newmode;
    644 }