1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Maildir folder support. FIXME rewrite - why do we chdir(2)??
3 *@ FIXME Simply truncating paths isn't really it.
5 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
6 * Copyright (c) 2012 - 2017 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
10 * Gunnar Ritter. All rights reserved.
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. All advertising materials mentioning features or use of this software
21 * must display the following acknowledgement:
22 * This product includes software developed by Gunnar Ritter
23 * and his contributors.
24 * 4. Neither the name of Gunnar Ritter nor the names of his contributors
25 * may be used to endorse or promote products derived from this software
26 * without specific prior written permission.
28 * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND
29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31 * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE
32 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
41 #define n_FILE maildir
43 #ifndef HAVE_AMALGAMATION
49 static struct message
**a_maildir_table
;
50 static ui32_t a_maildir_prime
;
51 static sigjmp_buf _maildir_jmp
;
53 static void __maildircatch(int s
);
54 static void __maildircatch_hold(int s
);
56 /* Do some cleanup in the tmp/ subdir */
57 static void _cleantmp(void);
59 static int _maildir_setfile1(char const *name
, enum fedit_mode fm
,
62 static int a_maildir_cmp(void const *a
, void const *b
);
64 static int _maildir_subdir(char const *name
, char const *sub
,
67 static void _maildir_append(char const *name
, char const *sub
,
70 static void readin(char const *name
, struct message
*m
);
72 static void maildir_update(void);
74 static void _maildir_move(struct n_timespec
const *tsp
,
77 static char * mkname(struct n_timespec
const *tsp
, enum mflag f
,
80 static enum okay
maildir_append1(struct n_timespec
const *tsp
,
81 char const *name
, FILE *fp
, off_t off1
,
82 long size
, enum mflag flag
);
84 static enum okay
trycreate(char const *name
);
86 static enum okay
mkmaildir(char const *name
);
88 static struct message
* mdlook(char const *name
, struct message
*data
);
90 static void mktable(void);
92 static enum okay
subdir_remove(char const *name
, char const *sub
);
97 NYD_X
; /* Signal handler */
98 siglongjmp(_maildir_jmp
, s
);
102 __maildircatch_hold(int s
)
104 NYD_X
; /* Signal handler */
106 /* TODO no STDIO in signal handler, no _() tr's -- pre-translate interrupt
108 n_err_sighdl(_("\nImportant operation in progress: "
109 "interrupt again to forcefully abort\n"));
110 safe_signal(SIGINT
, &__maildircatch
);
117 struct n_string s
, *sp
;
123 if ((dirp
= opendir("tmp")) == NULL
)
126 now
= n_time_now(FAL0
)->ts_sec
;
127 sp
= n_string_creat_auto(&s
);
129 while ((dp
= readdir(dirp
)) != NULL
) {
130 if (dp
->d_name
[0] == '.')
133 sp
= n_string_trunc(sp
, 0);
134 sp
= n_string_push_buf(sp
, "tmp/", sizeof("tmp/") -1);
135 sp
= n_string_push_cp(sp
, dp
->d_name
);
136 if (stat(n_string_cp(sp
), &st
) == -1)
138 if (st
.st_atime
+ 36*3600 < now
)
147 _maildir_setfile1(char const *name
, enum fedit_mode fm
, int omsgCount
)
152 if (!(fm
& FEDIT_NEWMAIL
))
155 mb
.mb_perm
= ((n_poption
& n_PO_R_FLAG
) || (fm
& FEDIT_RDONLY
))
157 if ((i
= _maildir_subdir(name
, "cur", fm
)) != 0)
159 if ((i
= _maildir_subdir(name
, "new", fm
)) != 0)
161 _maildir_append(name
, NULL
, NULL
);
163 n_autorec_relax_create();
164 for (i
= ((fm
& FEDIT_NEWMAIL
) ? omsgCount
: 0); i
< msgCount
; ++i
) {
165 readin(name
, message
+ i
);
166 n_autorec_relax_unroll();
168 n_autorec_relax_gut();
170 if (fm
& FEDIT_NEWMAIL
) {
171 if (msgCount
> omsgCount
)
172 qsort(&message
[omsgCount
], msgCount
- omsgCount
, sizeof *message
,
175 qsort(message
, msgCount
, sizeof *message
, &a_maildir_cmp
);
183 a_maildir_cmp(void const *xa
, void const *xb
){
184 char const *cpa
, *cpa_pid
, *cpb
, *cpb_pid
;
185 union {struct message
const *mp
; char const *cp
;} a
, b
;
193 /* We could have parsed the time somewhen in the past, do a quick shot */
194 at
= (si64_t
)a
.mp
->m_time
;
195 bt
= (si64_t
)b
.mp
->m_time
;
196 if(at
!= 0 && bt
!= 0 && (at
-= bt
) != 0)
199 /* Otherwise we need to parse the name */
200 a
.cp
= &a
.mp
->m_maildir_file
[4];
201 b
.cp
= &b
.mp
->m_maildir_file
[4];
203 /* Interpret time stored in name, and use it for comparison */
204 if(((n_idec_si64_cp(&at
, a
.cp
, 10, &cpa
)
205 ) & n_IDEC_STATE_EMASK
) != n_IDEC_STATE_EBASE
|| *cpa
!= '.' ||
207 goto jm1
; /* Fishy */
208 if(((n_idec_si64_cp(&bt
, b
.cp
, 10, &cpb
)
209 ) & n_IDEC_STATE_EMASK
) != n_IDEC_STATE_EBASE
|| *cpb
!= '.' ||
216 /* If the seconds part does not work, go deeper.
217 * We use de-facto standard "maildir — E-mail directory" from the Courier
218 * mail server, also used by, e.g., Dovecot: sec.MusecPpid.hostname:2,flags.
219 * However, a different name convention exists which uses
220 * sec.pid_counter.hostname:2,flags.
221 * First go for usec/counter, then pid */
223 /* A: exact "standard"? */
226 if((rv
= *a
.cp
) == 'M')
229 else if(digitchar(rv
)){
231 while((rv
= *a
.cp
) != '\0' && rv
!= '_')
234 goto jm1
; /* Fishy */
236 /* This is compatible to what dovecot does, it surely does not do so
237 * for nothing, but i have no idea, but am too stupid to ask */
238 else for(;; rv
= *++a
.cp
){
241 if(rv
== '\0' || rv
== '.' || rv
== n_MAILDIR_SEPARATOR
)
242 goto jm1
; /* Fishy */
245 if(((n_idec_si64_cp(&at
, a
.cp
, 10, &cpa
)
246 ) & n_IDEC_STATE_EMASK
) != n_IDEC_STATE_EBASE
)
247 goto jm1
; /* Fishy */
252 if((rv
= *b
.cp
) == 'M')
254 else if(digitchar(rv
)){
256 while((rv
= *b
.cp
) != '\0' && rv
!= '_')
260 }else for(;; rv
= *++b
.cp
){
263 if(rv
== '\0' || rv
== '.' || rv
== n_MAILDIR_SEPARATOR
)
267 if(((n_idec_si64_cp(&bt
, b
.cp
, 10, &cpb
)
268 ) & n_IDEC_STATE_EMASK
) != n_IDEC_STATE_EBASE
)
274 /* So this gets hairy: sort by PID, then hostname */
281 goto jm1
; /* Fishy */
283 if(((n_idec_si64_cp(&at
, a
.cp
, 10, &cpa
)
284 ) & n_IDEC_STATE_EMASK
) != n_IDEC_STATE_EBASE
)
285 goto jm1
; /* Fishy */
295 if(((n_idec_si64_cp(&bt
, b
.cp
, 10, &cpb
)
296 ) & n_IDEC_STATE_EMASK
) != n_IDEC_STATE_EBASE
)
297 goto jm1
; /* Fishy */
303 a
.cp
= (cpa_pid
!= NULL
) ? xa
: cpa
;
304 b
.cp
= (cpb_pid
!= NULL
) ? xb
: cpb
;
305 for(;; ++a
.cp
, ++b
.cp
){
309 at
= (ac
!= '\0' && ac
!= n_MAILDIR_SEPARATOR
);
311 bt
= (bc
!= '\0' && bc
!= n_MAILDIR_SEPARATOR
);
320 rv
= (at
== 0 ? 0 : (at
< 0 ? -1 : 1));
333 _maildir_subdir(char const *name
, char const *sub
, enum fedit_mode fm
)
340 if ((dirp
= opendir(sub
)) == NULL
) {
341 n_err(_("Cannot open directory %s\n"),
342 n_shexp_quote_cp(savecatsep(name
, '/', sub
), FAL0
));
346 if (access(sub
, W_OK
) == -1)
348 while ((dp
= readdir(dirp
)) != NULL
) {
349 if (dp
->d_name
[0] == '.' && (dp
->d_name
[1] == '\0' ||
350 (dp
->d_name
[1] == '.' && dp
->d_name
[2] == '\0')))
352 if (dp
->d_name
[0] == '.')
354 if (!(fm
& FEDIT_NEWMAIL
) || mdlook(dp
->d_name
, NULL
) == NULL
)
355 _maildir_append(name
, sub
, dp
->d_name
);
365 _maildir_append(char const *name
, char const *sub
, char const *fn
)
369 enum mflag f
= MUSED
| MNOFROM
| MNEWEST
;
374 if (fn
!= NULL
&& sub
!= NULL
) {
375 if (!strcmp(sub
, "new"))
381 (void)/*TODO*/n_idec_si64_cp(&tib
, fn
, 10, &xp
);
385 if ((cp
= strrchr(xp
, ',')) != NULL
&& PTRCMP(cp
, >, xp
+ 2) &&
386 cp
[-1] == '2' && cp
[-2] == n_MAILDIR_SEPARATOR
) {
387 while (*++cp
!= '\0') {
409 /* Ensure room (and a NULLified last entry) */
411 message_append(NULL
);
414 if (fn
== NULL
|| sub
== NULL
)
417 m
= &message
[msgCount
++];
424 m
->m_maildir_file
= tmp
= smalloc(sz
+ 1 + i
);
425 memcpy(tmp
, sub
, sz
);
427 memcpy(&tmp
[sz
], fn
, i
);
431 m
->m_maildir_hash
= ~n_torek_hash(fn
);
438 readin(char const *name
, struct message
*m
)
441 size_t bufsize
, buflen
, cnt
;
442 long size
= 0, lines
= 0;
448 if ((fp
= Fopen(m
->m_maildir_file
, "r")) == NULL
) {
449 n_err(_("Cannot read %s for message %lu\n"),
450 n_shexp_quote_cp(savecatsep(name
, '/', m
->m_maildir_file
), FAL0
),
451 (ul_i
)PTR2SIZE(m
- message
+ 1));
452 m
->m_flag
|= MHIDDEN
;
457 fseek(mb
.mb_otf
, 0L, SEEK_END
);
458 offset
= ftell(mb
.mb_otf
);
459 buf
= smalloc(bufsize
= LINESIZE
);
461 while (fgetline(&buf
, &bufsize
, &cnt
, &buflen
, fp
, 1) != NULL
) {
462 /* Since we simply copy over data without doing any transfer
463 * encoding reclassification/adjustment we *have* to perform
464 * RFC 4155 compliant From_ quoting here */
465 if (emptyline
&& is_head(buf
, buflen
, FAL0
)) {
466 putc('>', mb
.mb_otf
);
469 size
+= fwrite(buf
, 1, buflen
, mb
.mb_otf
);/*XXX err hdling*/
470 emptyline
= (*buf
== '\n');
475 putc('\n', mb
.mb_otf
);
482 m
->m_size
= m
->m_xsize
= size
;
483 m
->m_lines
= m
->m_xlines
= lines
;
484 m
->m_block
= mailx_blockof(offset
);
485 m
->m_offset
= mailx_offsetof(offset
);
495 struct n_timespec
const *tsp
;
496 int dodel
, c
, gotcha
= 0, held
= 0, modflags
= 0;
502 if (!(n_pstate
& n_PS_EDIT
)) {
504 for (m
= message
, c
= 0; PTRCMP(m
, <, message
+ msgCount
); ++m
) {
505 if (m
->m_flag
& MBOX
)
509 if (makembox() == STOP
)
513 tsp
= n_time_now(TRU1
); /* TODO FAL0, eventloop update! */
515 n_autorec_relax_create();
516 for (m
= message
, gotcha
= 0, held
= 0; PTRCMP(m
, <, message
+ msgCount
);
518 if (n_pstate
& n_PS_EDIT
)
519 dodel
= m
->m_flag
& MDELETED
;
521 dodel
= !((m
->m_flag
& MPRESERVE
) || !(m
->m_flag
& MTOUCH
));
523 if (unlink(m
->m_maildir_file
) < 0)
524 n_err(_("Cannot delete file %s for message %lu\n"),
525 n_shexp_quote_cp(savecatsep(mailname
, '/', m
->m_maildir_file
),
526 FAL0
), (ul_i
)PTR2SIZE(m
- message
+ 1));
530 if ((m
->m_flag
& (MREAD
| MSTATUS
)) == (MREAD
| MSTATUS
) ||
531 (m
->m_flag
& (MNEW
| MBOXED
| MSAVED
| MSTATUS
| MFLAG
|
532 MUNFLAG
| MANSWER
| MUNANSWER
| MDRAFT
| MUNDRAFT
))) {
533 _maildir_move(tsp
, m
);
534 n_autorec_relax_unroll();
540 n_autorec_relax_gut();
543 if ((gotcha
|| modflags
) && (n_pstate
& n_PS_EDIT
)) {
544 fprintf(n_stdout
, "%s %s\n",
545 n_shexp_quote_cp(displayname
, FAL0
),
546 ((ok_blook(bsdcompat
) || ok_blook(bsdmsgs
))
547 ? _("complete") : _("updated.")));
548 } else if (held
&& !(n_pstate
& n_PS_EDIT
) && mb
.mb_perm
!= 0) {
550 fprintf(n_stdout
, _("Held 1 message in %s\n"), displayname
);
552 fprintf(n_stdout
, _("Held %d messages in %s\n"), held
, displayname
);
556 for (m
= message
; PTRCMP(m
, <, message
+ msgCount
); ++m
)
557 free(n_UNCONST(m
->m_maildir_file
));
562 _maildir_move(struct n_timespec
const *tsp
, struct message
*m
)
567 fn
= mkname(tsp
, m
->m_flag
, m
->m_maildir_file
+ 4);
568 newfn
= savecat("cur/", fn
);
569 if (!strcmp(m
->m_maildir_file
, newfn
))
571 if (link(m
->m_maildir_file
, newfn
) == -1) {
572 n_err(_("Cannot link %s to %s: message %lu not touched\n"),
573 n_shexp_quote_cp(savecatsep(mailname
, '/', m
->m_maildir_file
), FAL0
),
574 n_shexp_quote_cp(savecatsep(mailname
, '/', newfn
), FAL0
),
575 (ul_i
)PTR2SIZE(m
- message
+ 1));
578 if (unlink(m
->m_maildir_file
) == -1)
579 n_err(_("Cannot unlink %s\n"),
580 n_shexp_quote_cp(savecatsep(mailname
, '/', m
->m_maildir_file
), FAL0
));
586 mkname(struct n_timespec
const *tsp
, enum mflag f
, char const *pref
)
589 static struct n_timespec ts
;
602 cp
= n_nodename(FAL0
);
605 if (UICMP(32, n
, <, size
+ 8))
606 node
= srealloc(node
, size
+= 20);
609 node
[n
++] = '\\', node
[n
++] = '0',
610 node
[n
++] = '5', node
[n
++] = '7';
613 node
[n
++] = '\\', node
[n
++] = '0',
614 node
[n
++] = '7', node
[n
++] = '2';
619 } while (*cp
++ != '\0');
622 /* Problem: Courier spec uses microseconds, not nanoseconds */
623 if((s
= tsp
->ts_sec
) > ts
.ts_sec
){
625 ts
.ts_nsec
= tsp
->ts_nsec
/ (n_DATE_NANOSSEC
/ n_DATE_MICROSSEC
);
627 s
= tsp
->ts_nsec
/ (n_DATE_NANOSSEC
/ n_DATE_MICROSSEC
);
630 if(s
< n_DATE_MICROSSEC
)
638 /* Create a name according to Courier spec */
639 size
= 60 + strlen(node
);
640 cp
= n_autorec_alloc(size
);
641 n
= snprintf(cp
, size
, "%" PRId64
".M%" PRIdZ
"P%ld.%s:2,",
642 ts
.ts_sec
, ts
.ts_nsec
, (long)n_pid
, node
);
644 size
= (n
= strlen(pref
)) + 13;
646 memcpy(cp
, pref
, n
+1);
647 for (i
= n
; i
> 3; --i
)
648 if (cp
[i
- 1] == ',' && cp
[i
- 2] == '2' &&
649 cp
[i
- 3] == n_MAILDIR_SEPARATOR
) {
654 memcpy(cp
+ n
, ":2,", 3 +1);
676 maildir_append1(struct n_timespec
const *tsp
, char const *name
, FILE *fp
,
677 off_t off1
, long size
, enum mflag flag
)
679 char buf
[4096], *fn
, *tfn
, *nfn
;
682 size_t nlen
, flen
, n
;
688 /* Create a unique temporary file */
689 for (nfn
= (char*)0xA /* XXX no magic */;; n_msleep(500, FAL0
)) {
690 flen
= strlen(fn
= mkname(tsp
, flag
, NULL
));
691 tfn
= n_autorec_alloc(n
= nlen
+ flen
+ 6);
692 snprintf(tfn
, n
, "%s/tmp/%s", name
, fn
);
694 /* Use "wx" for O_EXCL XXX stat(2) rather redundant; coverity:TOCTOU */
695 if ((!stat(tfn
, &st
) || n_err_no
== n_ERR_NOENT
) &&
696 (op
= Fopen(tfn
, "wx")) != NULL
)
699 nfn
= (char*)(PTR2SIZE(nfn
) - 1);
701 n_err(_("Can't create an unique file name in %s\n"),
702 n_shexp_quote_cp(savecat(name
, "/tmp"), FAL0
));
707 if (fseek(fp
, off1
, SEEK_SET
) == -1)
710 size_t z
= UICMP(z
, size
, >, sizeof buf
) ? sizeof buf
: (size_t)size
;
712 if (z
!= (n
= fread(buf
, 1, z
, fp
)) || n
!= fwrite(buf
, 1, n
, op
)) {
714 n_err(_("Error writing to %s\n"), n_shexp_quote_cp(tfn
, FAL0
));
722 nfn
= n_autorec_alloc(n
= nlen
+ flen
+ 6);
723 snprintf(nfn
, n
, "%s/new/%s", name
, fn
);
724 if (link(tfn
, nfn
) == -1) {
725 n_err(_("Cannot link %s to %s\n"), n_shexp_quote_cp(tfn
, FAL0
),
726 n_shexp_quote_cp(nfn
, FAL0
));
731 if (unlink(tfn
) == -1)
732 n_err(_("Cannot unlink %s\n"), n_shexp_quote_cp(tfn
, FAL0
));
739 trycreate(char const *name
)
745 if (!stat(name
, &st
)) {
746 if (!S_ISDIR(st
.st_mode
)) {
747 n_err(_("%s is not a directory\n"), n_shexp_quote_cp(name
, FAL0
));
750 } else if (!n_path_mkdir(name
)) {
751 n_err(_("Cannot create directory %s\n"), n_shexp_quote_cp(name
, FAL0
));
761 mkmaildir(char const *name
) /* TODO proper cleanup on error; use path[] loop */
768 if (trycreate(name
) == OKAY
) {
769 np
= ac_alloc((sz
= strlen(name
)) + 4 +1);
770 memcpy(np
, name
, sz
);
771 memcpy(np
+ sz
, "/tmp", 4 +1);
772 if (trycreate(np
) == OKAY
) {
773 memcpy(np
+ sz
, "/new", 4 +1);
774 if (trycreate(np
) == OKAY
) {
775 memcpy(np
+ sz
, "/cur", 4 +1);
785 static struct message
*
786 mdlook(char const *name
, struct message
*data
)
792 if (data
&& data
->m_maildir_hash
)
793 h
= ~data
->m_maildir_hash
;
795 h
= n_torek_hash(name
);
796 h
%= a_maildir_prime
;
798 mp
= a_maildir_table
[c
];
801 if (!strcmp(mp
->m_maildir_file
+ 4, name
))
803 c
+= (n
& 1) ? -((n
+1)/2) * ((n
+1)/2) : ((n
+1)/2) * ((n
+1)/2);
805 while (c
>= a_maildir_prime
)
806 c
-= a_maildir_prime
;
807 mp
= a_maildir_table
[c
];
809 if (data
!= NULL
&& mp
== NULL
)
822 a_maildir_prime
= n_prime_next(msgCount
);
823 a_maildir_table
= scalloc(a_maildir_prime
, sizeof *a_maildir_table
);
824 for (mp
= message
, i
= msgCount
; i
-- != 0; ++mp
)
825 mdlook(mp
->m_maildir_file
+ 4, mp
);
830 subdir_remove(char const *name
, char const *sub
)
833 int pathsize
, pathend
, namelen
, sublen
, n
;
839 namelen
= strlen(name
);
840 sublen
= strlen(sub
);
841 path
= smalloc(pathsize
= namelen
+ sublen
+ 30 +1);
842 memcpy(path
, name
, namelen
);
844 memcpy(path
+ namelen
+ 1, sub
, sublen
);
845 path
[namelen
+ sublen
+ 1] = '/';
846 path
[pathend
= namelen
+ sublen
+ 2] = '\0';
848 if ((dirp
= opendir(path
)) == NULL
) {
852 while ((dp
= readdir(dirp
)) != NULL
) {
853 if (dp
->d_name
[0] == '.' && (dp
->d_name
[1] == '\0' ||
854 (dp
->d_name
[1] == '.' && dp
->d_name
[2] == '\0')))
856 if (dp
->d_name
[0] == '.')
858 n
= strlen(dp
->d_name
);
859 if (UICMP(32, pathend
+ n
+1, >, pathsize
))
860 path
= srealloc(path
, pathsize
= pathend
+ n
+ 30);
861 memcpy(path
+ pathend
, dp
->d_name
, n
+1);
862 if (unlink(path
) == -1) {
870 path
[pathend
] = '\0';
871 if (rmdir(path
) == -1) {
883 maildir_setfile(char const * volatile name
, enum fedit_mode fm
)
885 sighandler_type
volatile saveint
;
892 omsgCount
= msgCount
;
893 if (cwget(&cw
) == STOP
) {
894 n_alert(_("Cannot open current directory"));
898 if (!(fm
& FEDIT_NEWMAIL
) && !quit(FAL0
))
901 saveint
= safe_signal(SIGINT
, SIG_IGN
);
903 if (!(fm
& FEDIT_NEWMAIL
)) {
904 if (fm
& FEDIT_SYSBOX
)
905 n_pstate
&= ~n_PS_EDIT
;
907 n_pstate
|= n_PS_EDIT
;
917 mb
.mb_type
= MB_MAILDIR
;
920 if(!n_is_dir(name
, FAL0
)){
921 emsg
= N_("Not a maildir: %s\n");
923 }else if(chdir(name
) < 0){
924 emsg
= N_("Cannot enter maildir://%s\n");
926 n_err(V_(emsg
), n_shexp_quote_cp(name
, FAL0
));
927 mb
.mb_type
= MB_VOID
;
931 safe_signal(SIGINT
, saveint
);
935 a_maildir_table
= NULL
;
936 if (sigsetjmp(_maildir_jmp
, 1) == 0) {
937 if (fm
& FEDIT_NEWMAIL
)
939 if (saveint
!= SIG_IGN
)
940 safe_signal(SIGINT
, &__maildircatch
);
941 i
= _maildir_setfile1(name
, fm
, omsgCount
);
943 if ((fm
& FEDIT_NEWMAIL
) && a_maildir_table
!= NULL
)
944 free(a_maildir_table
);
946 safe_signal(SIGINT
, saveint
);
949 mb
.mb_type
= MB_VOID
;
954 if (cwret(&cw
) == STOP
)
955 n_panic(_("Cannot change back to current directory"));
959 if ((fm
& FEDIT_NEWMAIL
) && mb
.mb_sorted
&& msgCount
> omsgCount
) {
964 if (!(fm
& FEDIT_NEWMAIL
)) {
965 n_pstate
&= ~n_PS_SAW_COMMAND
;
966 n_pstate
|= n_PS_SETFILE_OPENED
;
969 if ((n_poption
& n_PO_EXISTONLY
) && !(n_poption
& n_PO_HEADERLIST
)) {
974 if (!(fm
& FEDIT_NEWMAIL
) && (fm
& FEDIT_SYSBOX
) && msgCount
== 0) {
975 if (mb
.mb_type
== MB_MAILDIR
/* XXX ?? */ && !ok_blook(emptystart
))
976 n_err(_("No mail at %s\n"), n_shexp_quote_cp(name
, FAL0
));
981 if ((fm
& FEDIT_NEWMAIL
) && msgCount
> omsgCount
)
982 newmailinfo(omsgCount
);
990 maildir_quit(bool_t hold_sigs_on
)
992 sighandler_type saveint
;
1002 if (cwget(&cw
) == STOP
) {
1003 n_alert(_("Cannot open current directory"));
1007 saveint
= safe_signal(SIGINT
, SIG_IGN
);
1009 if (chdir(mailname
) == -1) {
1010 n_err(_("Cannot change directory to %s\n"),
1011 n_shexp_quote_cp(mailname
, FAL0
));
1013 safe_signal(SIGINT
, saveint
);
1017 if (sigsetjmp(_maildir_jmp
, 1) == 0) {
1018 if (saveint
!= SIG_IGN
)
1019 safe_signal(SIGINT
, &__maildircatch_hold
);
1023 safe_signal(SIGINT
, saveint
);
1025 if (cwret(&cw
) == STOP
)
1026 n_panic(_("Cannot change back to current directory"));
1037 maildir_append(char const *name
, FILE *fp
, long offset
)
1039 struct n_timespec
const *tsp
;
1040 char *buf
, *bp
, *lp
;
1041 size_t bufsize
, buflen
, cnt
;
1042 off_t off1
= -1, offs
;
1045 enum {_NONE
= 0, _INHEAD
= 1<<0, _NLSEP
= 1<<1} state
;
1049 if ((rv
= mkmaildir(name
)) != OKAY
)
1052 buf
= smalloc(bufsize
= LINESIZE
); /* TODO line pool; signals */
1055 offs
= offset
/* BSD will move due to O_APPEND! ftell(fp) */;
1057 tsp
= n_time_now(TRU1
); /* TODO -> eventloop */
1059 n_autorec_relax_create();
1060 for (flag
= MNEW
, state
= _NLSEP
;;) {
1061 bp
= fgetline(&buf
, &bufsize
, &cnt
, &buflen
, fp
, 1);
1064 ((state
& (_INHEAD
| _NLSEP
)) == _NLSEP
&&
1065 is_head(buf
, buflen
, FAL0
))) {
1066 if (off1
!= (off_t
)-1) {
1067 if ((rv
= maildir_append1(tsp
, name
, fp
, off1
, size
, flag
)) == STOP
)
1069 n_autorec_relax_unroll();
1070 if (fseek(fp
, offs
+ buflen
, SEEK_SET
) == -1) {
1075 off1
= offs
+ buflen
;
1087 if (buf
[0] == '\n') {
1090 } else if (state
& _INHEAD
) {
1091 if (!ascncasecmp(buf
, "status", 6)) {
1093 while (whitechar(*lp
))
1096 while (*++lp
!= '\0')
1105 } else if (!ascncasecmp(buf
, "x-status", 8)) {
1107 while (whitechar(*lp
))
1110 while (*++lp
!= '\0')
1128 n_autorec_relax_gut();
1136 maildir_remove(char const *name
)
1138 enum okay rv
= STOP
;
1141 if (subdir_remove(name
, "tmp") == STOP
||
1142 subdir_remove(name
, "new") == STOP
||
1143 subdir_remove(name
, "cur") == STOP
)
1145 if (rmdir(name
) == -1) {