summaryrefslogtreecommitdiff
path: root/libsylph/mbox.c
diff options
context:
space:
mode:
Diffstat (limited to 'libsylph/mbox.c')
-rw-r--r--libsylph/mbox.c572
1 files changed, 572 insertions, 0 deletions
diff --git a/libsylph/mbox.c b/libsylph/mbox.c
new file mode 100644
index 0000000..4c0b2da
--- /dev/null
+++ b/libsylph/mbox.c
@@ -0,0 +1,572 @@
1/*
2 * LibSylph -- E-Mail client library
3 * Copyright (C) 1999-2012 Hiroyuki Yamamoto
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library 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 GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20#ifdef HAVE_CONFIG_H
21# include "config.h"
22#endif
23
24#include "defs.h"
25
26#include <glib.h>
27#include <glib/gi18n.h>
28#include <stdio.h>
29#include <unistd.h>
30#include <string.h>
31#include <fcntl.h>
32#include <sys/file.h>
33#include <ctype.h>
34#include <time.h>
35
36#ifdef HAVE_LOCKFILE_H
37# include <lockfile.h>
38#endif
39
40#include "mbox.h"
41#include "procmsg.h"
42#include "procheader.h"
43#include "folder.h"
44#include "filter.h"
45#include "prefs_common.h"
46#include "prefs_account.h"
47#include "account.h"
48#include "utils.h"
49
50#define FPUTS_TO_TMP_ABORT_IF_FAIL(s) \
51{ \
52 if (fputs(s, tmp_fp) == EOF) { \
53 g_warning(_("can't write to temporary file\n")); \
54 fclose(tmp_fp); \
55 fclose(mbox_fp); \
56 g_unlink(tmp_file); \
57 g_free(tmp_file); \
58 return -1; \
59 } \
60}
61
62gint proc_mbox(FolderItem *dest, const gchar *mbox, GHashTable *folder_table)
63{
64 return proc_mbox_full(dest, mbox, folder_table,
65 folder_table ? TRUE : FALSE,
66 folder_table && prefs_common.enable_junk &&
67 prefs_common.filter_junk_on_recv ? TRUE : FALSE);
68}
69
70gint proc_mbox_full(FolderItem *dest, const gchar *mbox,
71 GHashTable *folder_table, gboolean apply_filter,
72 gboolean filter_junk)
73{
74 FILE *mbox_fp;
75 gchar buf[BUFFSIZE], from_line[BUFFSIZE];
76 gchar *tmp_file;
77 gint new_msgs = 0;
78 guint count = 0;
79 Folder *folder;
80 FilterRule *junk_rule = NULL;
81 GSList junk_fltlist = {NULL, NULL};
82 FolderItem *junk;
83
84 g_return_val_if_fail(dest != NULL, -1);
85 g_return_val_if_fail(dest->folder != NULL, -1);
86 g_return_val_if_fail(mbox != NULL, -1);
87
88 debug_print(_("Getting messages from %s into %s...\n"), mbox, dest->path);
89
90 folder = dest->folder;
91
92 if ((mbox_fp = g_fopen(mbox, "rb")) == NULL) {
93 FILE_OP_ERROR(mbox, "fopen");
94 return -1;
95 }
96
97 /* ignore empty lines on the head */
98 do {
99 if (fgets(buf, sizeof(buf), mbox_fp) == NULL) {
100 g_warning(_("can't read mbox file.\n"));
101 fclose(mbox_fp);
102 return -1;
103 }
104 } while (buf[0] == '\n' || buf[0] == '\r');
105
106 if (strncmp(buf, "From ", 5) != 0) {
107 g_warning(_("invalid mbox format: %s\n"), mbox);
108 fclose(mbox_fp);
109 return -1;
110 }
111
112 strcpy(from_line, buf);
113 if (fgets(buf, sizeof(buf), mbox_fp) == NULL) {
114 g_warning(_("malformed mbox: %s\n"), mbox);
115 fclose(mbox_fp);
116 return -1;
117 }
118
119 tmp_file = get_tmp_file();
120
121 if (filter_junk) {
122 junk = folder_get_junk(folder);
123 junk_rule = filter_junk_rule_create(NULL, junk, FALSE);
124 junk_fltlist.data = junk_rule;
125 }
126
127 do {
128 FILE *tmp_fp;
129 GSList *cur;
130 gchar *startp, *endp, *rpath;
131 gint empty_line;
132 gboolean is_next_msg = FALSE;
133 gboolean is_junk = FALSE;
134 FilterInfo *fltinfo;
135 MsgInfo *msginfo;
136
137 count++;
138 if (folder->ui_func)
139 folder->ui_func(folder, dest, folder->ui_func_data ? dest->folder->ui_func_data : GUINT_TO_POINTER(count));
140 if (folder_call_ui_func2(folder, dest, count, 0) == FALSE) {
141 debug_print("Import of mbox cancelled at %u\n", count);
142 break;
143 }
144
145 if ((tmp_fp = g_fopen(tmp_file, "wb")) == NULL) {
146 FILE_OP_ERROR(tmp_file, "fopen");
147 g_warning(_("can't open temporary file\n"));
148 filter_rule_free(junk_rule);
149 g_free(tmp_file);
150 fclose(mbox_fp);
151 return -1;
152 }
153 if (change_file_mode_rw(tmp_fp, tmp_file) < 0)
154 FILE_OP_ERROR(tmp_file, "chmod");
155
156 /* convert unix From into Return-Path */
157 startp = from_line + 5;
158 endp = strchr(startp, ' ');
159 if (endp == NULL)
160 rpath = g_strdup(startp);
161 else
162 rpath = g_strndup(startp, endp - startp);
163 g_strstrip(rpath);
164 g_snprintf(from_line, sizeof(from_line),
165 "Return-Path: %s\n", rpath);
166 g_free(rpath);
167
168 FPUTS_TO_TMP_ABORT_IF_FAIL(from_line);
169 FPUTS_TO_TMP_ABORT_IF_FAIL(buf);
170 from_line[0] = '\0';
171
172 empty_line = 0;
173
174 while (fgets(buf, sizeof(buf), mbox_fp) != NULL) {
175 if (buf[0] == '\n' || buf[0] == '\r') {
176 empty_line++;
177 buf[0] = '\0';
178 continue;
179 }
180
181 /* From separator */
182 while (!strncmp(buf, "From ", 5)) {
183 strcpy(from_line, buf);
184 if (fgets(buf, sizeof(buf), mbox_fp) == NULL) {
185 buf[0] = '\0';
186 break;
187 }
188
189 if (is_header_line(buf)) {
190 is_next_msg = TRUE;
191 break;
192 } else if (!strncmp(buf, "From ", 5)) {
193 continue;
194 } else if (!strncmp(buf, ">From ", 6)) {
195 g_memmove(buf, buf + 1, strlen(buf));
196 is_next_msg = TRUE;
197 break;
198 } else {
199 g_warning(_("unescaped From found:\n%s"),
200 from_line);
201 break;
202 }
203 }
204 if (is_next_msg) break;
205
206 if (empty_line > 0) {
207 while (empty_line--)
208 FPUTS_TO_TMP_ABORT_IF_FAIL("\n");
209 empty_line = 0;
210 }
211
212 if (from_line[0] != '\0') {
213 FPUTS_TO_TMP_ABORT_IF_FAIL(from_line);
214 from_line[0] = '\0';
215 }
216
217 if (buf[0] != '\0') {
218 if (!strncmp(buf, ">From ", 6)) {
219 FPUTS_TO_TMP_ABORT_IF_FAIL(buf + 1);
220 } else
221 FPUTS_TO_TMP_ABORT_IF_FAIL(buf);
222
223 buf[0] = '\0';
224 }
225 }
226
227 if (empty_line > 0) {
228 while (--empty_line)
229 FPUTS_TO_TMP_ABORT_IF_FAIL("\n");
230 }
231
232 if (fclose(tmp_fp) == EOF) {
233 FILE_OP_ERROR(tmp_file, "fclose");
234 g_warning(_("can't write to temporary file\n"));
235 filter_rule_free(junk_rule);
236 g_unlink(tmp_file);
237 g_free(tmp_file);
238 fclose(mbox_fp);
239 return -1;
240 }
241
242 fltinfo = filter_info_new();
243 fltinfo->flags.perm_flags = MSG_NEW|MSG_UNREAD;
244 fltinfo->flags.tmp_flags = MSG_RECEIVED;
245
246 msginfo = procheader_parse_file(tmp_file, fltinfo->flags,
247 FALSE);
248 if (!msginfo) {
249 g_warning("proc_mbox_full: procheader_parse_file failed");
250 filter_info_free(fltinfo);
251 filter_rule_free(junk_rule);
252 g_unlink(tmp_file);
253 g_free(tmp_file);
254 fclose(mbox_fp);
255 return -1;
256 }
257 fltinfo->flags = msginfo->flags;
258 msginfo->file_path = g_strdup(tmp_file);
259
260 if (filter_junk && prefs_common.enable_junk &&
261 prefs_common.filter_junk_before && junk_rule) {
262 filter_apply_msginfo(&junk_fltlist, msginfo, fltinfo);
263 if (fltinfo->drop_done)
264 is_junk = TRUE;
265 }
266
267 if (!fltinfo->drop_done && apply_filter)
268 filter_apply_msginfo(prefs_common.fltlist, msginfo,
269 fltinfo);
270
271 if (!fltinfo->drop_done &&
272 filter_junk && prefs_common.enable_junk &&
273 !prefs_common.filter_junk_before && junk_rule) {
274 filter_apply_msginfo(&junk_fltlist, msginfo, fltinfo);
275 if (fltinfo->drop_done)
276 is_junk = TRUE;
277 }
278
279 if (fltinfo->actions[FLT_ACTION_MOVE] == FALSE &&
280 fltinfo->actions[FLT_ACTION_DELETE] == FALSE) {
281 msginfo->flags = fltinfo->flags;
282 if (folder_item_add_msg_msginfo(dest, msginfo, FALSE) < 0) {
283 procmsg_msginfo_free(msginfo);
284 filter_info_free(fltinfo);
285 filter_rule_free(junk_rule);
286 g_unlink(tmp_file);
287 g_free(tmp_file);
288 fclose(mbox_fp);
289 return -1;
290 }
291 fltinfo->dest_list = g_slist_append(fltinfo->dest_list,
292 dest);
293 }
294
295 for (cur = fltinfo->dest_list; cur != NULL; cur = cur->next) {
296 FolderItem *drop_folder = (FolderItem *)cur->data;
297 gint val = 0;
298
299 if (folder_table) {
300 val = GPOINTER_TO_INT(g_hash_table_lookup
301 (folder_table,
302 drop_folder));
303 }
304 if (val == 0) {
305 if (folder_table) {
306 g_hash_table_insert(folder_table,
307 drop_folder,
308 GINT_TO_POINTER(1));
309 }
310 }
311 }
312
313 if (!is_junk &&
314 fltinfo->actions[FLT_ACTION_DELETE] == FALSE &&
315 fltinfo->actions[FLT_ACTION_MARK_READ] == FALSE)
316 new_msgs++;
317
318 procmsg_msginfo_free(msginfo);
319 filter_info_free(fltinfo);
320 g_unlink(tmp_file);
321 } while (from_line[0] != '\0');
322
323 if (junk_rule)
324 filter_rule_free(junk_rule);
325
326 g_free(tmp_file);
327 fclose(mbox_fp);
328 debug_print("%d new messages found.\n", new_msgs);
329
330 return new_msgs;
331}
332
333gint lock_mbox(const gchar *base, LockType type)
334{
335#ifdef G_OS_UNIX
336 gint retval = 0;
337
338 if (type == LOCK_FILE) {
339#if HAVE_LIBLOCKFILE
340 gchar *lockfile;
341
342 lockfile = g_strconcat(base, ".lock", NULL);
343 if (lockfile_create(lockfile, 0, L_PID) != L_SUCCESS) {
344 FILE_OP_ERROR(lockfile, "lockfile_create");
345 g_free(lockfile);
346 return -1;
347 }
348 g_free(lockfile);
349#else
350 gchar *lockfile, *locklink;
351 gint retry = 0;
352 FILE *lockfp;
353
354 lockfile = g_strdup_printf("%s.%d", base, getpid());
355 if ((lockfp = g_fopen(lockfile, "wb")) == NULL) {
356 FILE_OP_ERROR(lockfile, "fopen");
357 g_warning(_("can't create lock file %s\n"), lockfile);
358 g_warning(_("use 'flock' instead of 'file' if possible.\n"));
359 g_free(lockfile);
360 return -1;
361 }
362
363 fprintf(lockfp, "%d\n", getpid());
364 fclose(lockfp);
365
366 locklink = g_strconcat(base, ".lock", NULL);
367 while (link(lockfile, locklink) < 0) {
368 FILE_OP_ERROR(lockfile, "link");
369 if (retry >= 5) {
370 g_warning(_("can't create %s\n"), lockfile);
371 g_unlink(lockfile);
372 g_free(lockfile);
373 return -1;
374 }
375 if (retry == 0)
376 g_warning(_("mailbox is owned by another"
377 " process, waiting...\n"));
378 retry++;
379 sleep(5);
380 }
381 g_unlink(lockfile);
382 g_free(lockfile);
383#endif /* HAVE_LIBLOCKFILE */
384 } else if (type == LOCK_FLOCK) {
385 gint lockfd;
386
387 if ((lockfd = open(base, O_RDWR)) < 0) {
388 FILE_OP_ERROR(base, "open");
389 return -1;
390 }
391#if HAVE_LOCKF
392 if (lockf(lockfd, F_TLOCK, 0) < 0) {
393 perror("lockf");
394#elif HAVE_FLOCK
395 if (flock(lockfd, LOCK_EX|LOCK_NB) < 0) {
396 perror("flock");
397#else
398 {
399#endif /* HAVE_LOCKF */
400 g_warning(_("can't lock %s\n"), base);
401 if (close(lockfd) < 0)
402 perror("close");
403 return -1;
404 }
405 retval = lockfd;
406 } else {
407 g_warning(_("invalid lock type\n"));
408 return -1;
409 }
410
411 return retval;
412#else
413 return -1;
414#endif /* G_OS_UNIX */
415}
416
417gint unlock_mbox(const gchar *base, gint fd, LockType type)
418{
419 if (type == LOCK_FILE) {
420 gchar *lockfile;
421
422 lockfile = g_strconcat(base, ".lock", NULL);
423#if HAVE_LIBLOCKFILE
424 if (lockfile_remove(lockfile) != L_SUCCESS) {
425 FILE_OP_ERROR(lockfile, "lockfile_remove");
426#else
427 if (g_unlink(lockfile) < 0) {
428 FILE_OP_ERROR(lockfile, "unlink");
429#endif /* HAVE_LIBLOCKFILE */
430 g_free(lockfile);
431 return -1;
432 }
433 g_free(lockfile);
434
435 return 0;
436 } else if (type == LOCK_FLOCK) {
437#if HAVE_LOCKF
438 if (lockf(fd, F_ULOCK, 0) < 0) {
439 perror("lockf");
440#elif HAVE_FLOCK
441 if (flock(fd, LOCK_UN) < 0) {
442 perror("flock");
443#else
444 {
445#endif /* HAVE_LOCKF */
446 g_warning(_("can't unlock %s\n"), base);
447 if (close(fd) < 0)
448 perror("close");
449 return -1;
450 }
451
452 if (close(fd) < 0) {
453 perror("close");
454 return -1;
455 }
456
457 return 0;
458 }
459
460 g_warning(_("invalid lock type\n"));
461 return -1;
462}
463
464gint copy_mbox(const gchar *src, const gchar *dest)
465{
466 return copy_file(src, dest, TRUE);
467}
468
469void empty_mbox(const gchar *mbox)
470{
471#if HAVE_TRUNCATE
472 if (truncate(mbox, 0) < 0) {
473#endif
474 FILE *fp;
475
476#if HAVE_TRUNCATE
477 FILE_OP_ERROR(mbox, "truncate");
478#endif
479 if ((fp = g_fopen(mbox, "wb")) == NULL) {
480 FILE_OP_ERROR(mbox, "fopen");
481 g_warning(_("can't truncate mailbox to zero.\n"));
482 return;
483 }
484 fclose(fp);
485#if HAVE_TRUNCATE
486 }
487#endif
488}
489
490/* read all messages in SRC, and store them into one MBOX file. */
491gint export_to_mbox(FolderItem *src, const gchar *mbox)
492{
493 GSList *mlist;
494 gint ret = 0;
495
496 mlist = folder_item_get_msg_list(src, TRUE);
497 if (mlist) {
498 ret = export_msgs_to_mbox(src, mlist, mbox);
499 procmsg_msg_list_free(mlist);
500 }
501
502 return ret;
503}
504
505/* store MLIST into one MBOX file. */
506gint export_msgs_to_mbox(FolderItem *src, GSList *mlist, const gchar *mbox)
507{
508 GSList *cur;
509 MsgInfo *msginfo;
510 FILE *msg_fp;
511 FILE *mbox_fp;
512 gchar buf[BUFFSIZE];
513 PrefsAccount *cur_ac;
514 guint count = 0, length;
515 time_t date_t_;
516
517 g_return_val_if_fail(src != NULL, -1);
518 g_return_val_if_fail(src->folder != NULL, -1);
519 g_return_val_if_fail(mlist != NULL, -1);
520 g_return_val_if_fail(mbox != NULL, -1);
521
522 debug_print(_("Exporting messages from %s into %s...\n"),
523 src->path, mbox);
524
525 if ((mbox_fp = g_fopen(mbox, "wb")) == NULL) {
526 FILE_OP_ERROR(mbox, "fopen");
527 return -1;
528 }
529
530 cur_ac = account_get_current_account();
531
532 length = g_slist_length(mlist);
533
534 for (cur = mlist; cur != NULL; cur = cur->next) {
535 msginfo = (MsgInfo *)cur->data;
536
537 count++;
538 if (src->folder->ui_func)
539 src->folder->ui_func(src->folder, src, src->folder->ui_func_data ? src->folder->ui_func_data : GUINT_TO_POINTER(count));
540 if (folder_call_ui_func2(src->folder, src, count, length) == FALSE) {
541 debug_print("Export to mbox cancelled at %u/%u\n", count, length);
542 break;
543 }
544
545 msg_fp = procmsg_open_message(msginfo);
546 if (!msg_fp)
547 continue;
548
549 strncpy2(buf,
550 msginfo->from ? msginfo->from :
551 cur_ac && cur_ac->address ?
552 cur_ac->address : "unknown",
553 sizeof(buf));
554 extract_address(buf);
555
556 date_t_ = msginfo->date_t;
557 fprintf(mbox_fp, "From %s %s", buf, ctime(&date_t_));
558
559 while (fgets(buf, sizeof(buf), msg_fp) != NULL) {
560 if (!strncmp(buf, "From ", 5))
561 fputc('>', mbox_fp);
562 fputs(buf, mbox_fp);
563 }
564 fputc('\n', mbox_fp);
565
566 fclose(msg_fp);
567 }
568
569 fclose(mbox_fp);
570
571 return 0;
572}