talons

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

mbox.c (6210B)


      1 /*
      2  * Claws Mail -- a GTK based, lightweight, and fast e-mail client
      3  * Copyright (C) 1999-2022 the Claws Mail team and Hiroyuki Yamamoto
      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 
     20 #include <stdio.h>
     21 #include "defs.h"
     22 #include <glib.h>
     23 #include <glib/gi18n.h>
     24 #include <stdlib.h>
     25 #include <unistd.h>
     26 #include <string.h>
     27 #include <fcntl.h>
     28 #include <ctype.h>
     29 #include <time.h>
     30 #include <errno.h>
     31 
     32 #include "mbox.h"
     33 #include "procmsg.h"
     34 #include "folder.h"
     35 #include "prefs_common.h"
     36 #include "prefs_account.h"
     37 #include "account.h"
     38 #include "utils.h"
     39 #include "alertpanel.h"
     40 #include "statusbar.h"
     41 #include "file-utils.h"
     42 
     43 #define FPUTS_TO_TMP_ABORT_IF_FAIL(s) \
     44 { \
     45 	lines++; \
     46 	if (fputs(s, tmp_fp) == EOF) { \
     47 		g_warning("can't write to temporary file"); \
     48 		fclose(tmp_fp); \
     49 		fclose(mbox_fp); \
     50 		unlink(tmp_file); \
     51 		g_free(tmp_file); \
     52 		return -1; \
     53 	} \
     54 }
     55 
     56 gint proc_mbox(FolderItem *dest, const gchar *mbox, PrefsAccount *account)
     57 /* return values: -1 error, >=0 number of msgs added */
     58 {
     59 	FILE *mbox_fp;
     60 	char buf[BUFSIZ];
     61 	gchar *tmp_file;
     62 	gint msgs = 0;
     63 	gint lines;
     64 	gboolean more;
     65 	GSList *to_add = NULL;
     66 	gboolean printed = FALSE;
     67 	FolderItem *dropfolder;
     68 	GStatBuf src_stat;
     69 
     70 	cm_return_val_if_fail(dest != NULL, -1);
     71 	cm_return_val_if_fail(mbox != NULL, -1);
     72 
     73 	debug_print("Getting messages from %s into %s...\n", mbox, dest->path);
     74 
     75 	if (g_stat(mbox, &src_stat) < 0) {
     76 		FILE_OP_ERROR(mbox, "g_stat");
     77 		alertpanel_error(_("Could not stat mbox file:\n%s\n"), mbox);
     78 		return -1;
     79 	}
     80 
     81 	if ((mbox_fp = g_fopen(mbox, "rb")) == NULL) {
     82 		FILE_OP_ERROR(mbox, "g_fopen");
     83 		alertpanel_error(_("Could not open mbox file:\n%s\n"), mbox);
     84 		return -1;
     85 	}
     86 
     87 	/* ignore empty lines on the head */
     88 	do {
     89 		if (fgets(buf, sizeof(buf), mbox_fp) == NULL) {
     90 			g_warning("can't read mbox file");
     91 			fclose(mbox_fp);
     92 			return -1;
     93 		}
     94 	} while (buf[0] == '\n' || buf[0] == '\r');
     95 
     96 	if (strncmp(buf, "From ", 5) != 0) {
     97 		g_warning("invalid mbox format: %s", mbox);
     98 		fclose(mbox_fp);
     99 		return -1;
    100 	}
    101 
    102 	tmp_file = get_tmp_file();
    103 
    104 	folder_item_update_freeze();
    105 
    106 	dropfolder = dest;
    107 
    108 	do {
    109 		FILE *tmp_fp;
    110 		gint empty_lines;
    111 
    112 		if (msgs%10 == 0) {
    113 			long cur_offset_mb = ftell(mbox_fp) / (1024 * 1024);
    114 			if (printed)
    115 				statusbar_pop_all();
    116 			statusbar_print_all("Importing from mbox... (%ld MB imported)", cur_offset_mb);
    117 			statusbar_progress_all(cur_offset_mb, src_stat.st_size / (1024*1024), 1);
    118 			printed = TRUE;
    119 			GTK_EVENTS_FLUSH();
    120 		}
    121 
    122 		if ((tmp_fp = g_fopen(tmp_file, "wb")) == NULL) {
    123 			FILE_OP_ERROR(tmp_file, "g_fopen");
    124 			g_warning("can't open temporary file");
    125 			fclose(mbox_fp);
    126 			g_free(tmp_file);
    127 			return -1;
    128 		}
    129 
    130 		empty_lines = 0;
    131 		lines = 0;
    132 
    133 		/* process all lines from mboxrc file */
    134 		while (fgets(buf, sizeof(buf), mbox_fp) != NULL) {
    135 			int offset;
    136 
    137 			/* eat empty lines */
    138 			if (buf[0] == '\n' || buf[0] == '\r') {
    139 				empty_lines++;
    140 				continue;
    141 			}
    142 
    143 			/* From separator or quoted From */
    144 			offset = 0;
    145 			/* detect leading '>' char(s) */
    146 			while (buf[offset] == '>') {
    147 				offset++;
    148 			}
    149 			if (!strncmp(buf+offset, "From ", 5)) {
    150 				/* From separator: */
    151 				if (offset == 0) {
    152 					/* expect next mbox item */
    153 					break;
    154 				}
    155 
    156 				/* quoted From: */
    157 				/* flush any eaten empty line */
    158 				if (empty_lines > 0) {
    159 					while (empty_lines-- > 0) {
    160 						FPUTS_TO_TMP_ABORT_IF_FAIL("\n");
    161 				}
    162 					empty_lines = 0;
    163 				}
    164 				/* store the unquoted line */
    165 				FPUTS_TO_TMP_ABORT_IF_FAIL(buf + 1);
    166 				continue;
    167 			}
    168 
    169 			/* other line */
    170 			/* flush any eaten empty line */
    171 			if (empty_lines > 0) {
    172 				while (empty_lines-- > 0) {
    173 					FPUTS_TO_TMP_ABORT_IF_FAIL("\n");
    174 			}
    175 				empty_lines = 0;
    176 			}
    177 			/* store the line itself */
    178 					FPUTS_TO_TMP_ABORT_IF_FAIL(buf);
    179 		}
    180 		/* end of mbox item or end of mbox */
    181 
    182 		/* flush any eaten empty line (but the last one) */
    183 		if (empty_lines > 0) {
    184 			while (--empty_lines > 0) {
    185 				FPUTS_TO_TMP_ABORT_IF_FAIL("\n");
    186 			}
    187 		}
    188 
    189 		/* more emails to expect? */
    190 		more = !feof(mbox_fp);
    191 
    192 		/* warn if email part is empty (it's the minimum check
    193 		   we can do */
    194 		if (lines == 0) {
    195 			g_warning("malformed mbox: %s: message %d is empty", mbox, msgs);
    196 			fclose(tmp_fp);
    197 			fclose(mbox_fp);
    198 			unlink(tmp_file);
    199 			return -1;
    200 		}
    201 
    202 		if (fclose(tmp_fp) == EOF) {
    203 			FILE_OP_ERROR(tmp_file, "fclose");
    204 			g_warning("can't write to temporary file");
    205 			fclose(mbox_fp);
    206 			unlink(tmp_file);
    207 			g_free(tmp_file);
    208 			return -1;
    209 		}
    210 
    211 
    212 		MsgFileInfo *finfo = g_new0(MsgFileInfo, 1);
    213 		finfo->file = tmp_file;
    214 
    215 		to_add = g_slist_prepend(to_add, finfo);
    216 		tmp_file = get_tmp_file();
    217 
    218 		/* flush every 500 */
    219 		if (msgs > 0 && msgs % 500 == 0) {
    220 			folder_item_add_msgs(dropfolder, to_add, TRUE);
    221 			procmsg_message_file_list_free(to_add);
    222 			to_add = NULL;
    223 		}
    224 		msgs++;
    225 	} while (more);
    226 
    227 	if (printed) {
    228 		statusbar_pop_all();
    229 		statusbar_progress_all(0, 0, 0);
    230 	}
    231 
    232 	if (to_add) {
    233 		folder_item_add_msgs(dropfolder, to_add, TRUE);
    234 		procmsg_message_file_list_free(to_add);
    235 		to_add = NULL;
    236 	}
    237 
    238 	folder_item_update_thaw();
    239 
    240 	g_free(tmp_file);
    241 	fclose(mbox_fp);
    242 	debug_print("%d messages found.\n", msgs);
    243 
    244 	return msgs;
    245 }
    246 
    247 int copy_mbox(gint srcfd, const gchar *dest) {
    248 	ssize_t n_read;
    249 	char buf[BUFSIZ];
    250 
    251 	FILE *dest_fp = fopen(dest, "w");
    252 	if (dest_fp == NULL) {
    253 		warn("open %s", dest);
    254 		return -1;
    255 	}
    256 	if (chmod(dest, S_IRUSR|S_IWUSR) < 0)
    257 		warn("chmod %s", dest);
    258 
    259 	while ((n_read = read(srcfd, buf, sizeof(buf))) > 0) {
    260 		if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
    261 			g_warning("writing to %s failed", dest);
    262 			fclose(dest_fp);
    263 			unlink(dest);
    264 			return -1;
    265 		}
    266 	}
    267 	return fclose(dest_fp);
    268 }