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 }