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 - 2020 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
7 * SPDX-License-Identifier: BSD-4-Clause
11 * Gunnar Ritter. All rights reserved.
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
16 * 1. Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
21 * 3. All advertising materials mentioning features or use of this software
22 * must display the following acknowledgement:
23 * This product includes software developed by Gunnar Ritter
24 * and his contributors.
25 * 4. Neither the name of Gunnar Ritter nor the names of his contributors
26 * may be used to endorse or promote products derived from this software
27 * without specific prior written permission.
29 * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND
30 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32 * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE
33 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
35 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
37 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
38 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
42 #define su_FILE maildir
45 #ifndef mx_HAVE_AMALGAMATION
50 #ifdef mx_HAVE_MAILDIR
55 #include <su/icodec.h>
59 #include "mx/file-streams.h"
63 #include "su/code-in.h"
65 /* a_maildir_tbl should be a hash-indexed array of trees! */
66 static struct message
**a_maildir_tbl
, **a_maildir_tbl_top
;
67 static u32 a_maildir_tbl_prime
, a_maildir_tbl_maxdist
;
68 static sigjmp_buf _maildir_jmp
;
70 static void __maildircatch(int s
);
71 static void __maildircatch_hold(int s
);
73 /* Do some cleanup in the tmp/ subdir */
74 static void _cleantmp(void);
76 static int a_maildir_setfile1(char const *name
, enum fedit_mode fm
,
79 static int a_maildir_cmp(void const *a
, void const *b
);
81 static int _maildir_subdir(char const *name
, char const *sub
,
84 static void _maildir_append(char const *name
, char const *sub
,
87 static boole
a_maildir_readin(char const *name
, struct message
*mp
);
89 static void maildir_update(void);
91 static void _maildir_move(struct n_timespec
const *tsp
,
94 static char * mkname(struct n_timespec
const *tsp
, enum mflag f
,
97 static enum okay
maildir_append1(struct n_timespec
const *tsp
,
98 char const *name
, FILE *fp
, off_t off1
,
99 long size
, enum mflag flag
);
101 static enum okay
trycreate(char const *name
);
103 static enum okay
mkmaildir(char const *name
);
105 static struct message
* mdlook(char const *name
, struct message
*data
);
107 static void mktable(void);
109 static enum okay
subdir_remove(char const *name
, char const *sub
);
112 __maildircatch(int s
)
114 NYD
; /* Signal handler */
115 siglongjmp(_maildir_jmp
, s
);
119 __maildircatch_hold(int s
)
121 NYD
; /* Signal handler */
123 /* TODO no STDIO in signal handler, no _() tr's -- pre-translate interrupt
125 n_err_sighdl(_("\nImportant operation in progress: "
126 "interrupt again to forcefully abort\n"));
127 safe_signal(SIGINT
, &__maildircatch
);
134 struct n_string s_b
, *s
;
140 if ((dirp
= opendir("tmp")) == NULL
)
143 now
= n_time_now(FAL0
)->ts_sec
- 36*3600;
144 s
= n_string_creat_auto(&s_b
);
146 while ((dp
= readdir(dirp
)) != NULL
) {
147 if (dp
->d_name
[0] == '.')
150 s
= n_string_trunc(s
, 0);
151 s
= n_string_push_buf(s
, "tmp/", sizeof("tmp/") -1);
152 s
= n_string_push_cp(s
, dp
->d_name
);
153 if (stat(n_string_cp(s
), &st
) == -1)
155 if (st
.st_atime
<= now
)
164 a_maildir_setfile1(char const *name
, enum fedit_mode fm
, int omsgCount
)
169 if (!(fm
& FEDIT_NEWMAIL
))
172 mb
.mb_perm
= ((n_poption
& n_PO_R_FLAG
) || (fm
& FEDIT_RDONLY
))
174 if ((i
= _maildir_subdir(name
, "cur", fm
)) != 0)
176 if ((i
= _maildir_subdir(name
, "new", fm
)) != 0)
178 _maildir_append(name
, NULL
, NULL
);
180 n_autorec_relax_create();
181 for(i
= ((fm
& FEDIT_NEWMAIL
) ? omsgCount
: 0); i
< msgCount
; ++i
){
182 if(!a_maildir_readin(name
, &message
[i
])){
186 n_autorec_relax_unroll();
188 n_autorec_relax_gut();
192 if (fm
& FEDIT_NEWMAIL
) {
193 if (msgCount
> omsgCount
)
194 qsort(&message
[omsgCount
], msgCount
- omsgCount
, sizeof *message
,
197 qsort(message
, msgCount
, sizeof *message
, &a_maildir_cmp
);
205 a_maildir_cmp(void const *xa
, void const *xb
){
206 char const *cpa
, *cpa_pid
, *cpb
, *cpb_pid
;
207 union {struct message
const *mp
; char const *cp
;} a
, b
;
215 /* We could have parsed the time somewhen in the past, do a quick shot */
216 at
= (s64
)a
.mp
->m_time
;
217 bt
= (s64
)b
.mp
->m_time
;
218 if(at
!= 0 && bt
!= 0 && (at
-= bt
) != 0)
221 /* Otherwise we need to parse the name */
222 a
.cp
= &a
.mp
->m_maildir_file
[4];
223 b
.cp
= &b
.mp
->m_maildir_file
[4];
225 /* Interpret time stored in name, and use it for comparison */
226 if(((su_idec_s64_cp(&at
, a
.cp
, 10, &cpa
)
227 ) & su_IDEC_STATE_EMASK
) != su_IDEC_STATE_EBASE
|| *cpa
!= '.' ||
229 goto jm1
; /* Fishy */
230 if(((su_idec_s64_cp(&bt
, b
.cp
, 10, &cpb
)
231 ) & su_IDEC_STATE_EMASK
) != su_IDEC_STATE_EBASE
|| *cpb
!= '.' ||
238 /* If the seconds part does not work, go deeper.
239 * We use de-facto standard "maildir — E-mail directory" from the Courier
240 * mail server, also used by, e.g., Dovecot: sec.MusecPpid.hostname:2,flags.
241 * However, a different name convention exists which uses
242 * sec.pid_counter.hostname:2,flags.
243 * First go for usec/counter, then pid */
245 /* A: exact "standard"? */
248 if((rv
= *a
.cp
) == 'M')
251 else if(su_cs_is_digit(rv
)){
253 while((rv
= *a
.cp
) != '\0' && rv
!= '_')
256 goto jm1
; /* Fishy */
258 /* This is compatible to what dovecot does, it surely does not do so
259 * for nothing, but i have no idea, but am too stupid to ask */
260 else for(;; rv
= *++a
.cp
){
263 if(rv
== '\0' || rv
== '.' || rv
== n_MAILDIR_SEPARATOR
)
264 goto jm1
; /* Fishy */
267 if(((su_idec_s64_cp(&at
, a
.cp
, 10, &cpa
)
268 ) & su_IDEC_STATE_EMASK
) != su_IDEC_STATE_EBASE
)
269 goto jm1
; /* Fishy */
274 if((rv
= *b
.cp
) == 'M')
276 else if(su_cs_is_digit(rv
)){
278 while((rv
= *b
.cp
) != '\0' && rv
!= '_')
282 }else for(;; rv
= *++b
.cp
){
285 if(rv
== '\0' || rv
== '.' || rv
== n_MAILDIR_SEPARATOR
)
289 if(((su_idec_s64_cp(&bt
, b
.cp
, 10, &cpb
)
290 ) & su_IDEC_STATE_EMASK
) != su_IDEC_STATE_EBASE
)
296 /* So this gets hairy: sort by PID, then hostname */
303 goto jm1
; /* Fishy */
305 if(((su_idec_s64_cp(&at
, a
.cp
, 10, &cpa
)
306 ) & su_IDEC_STATE_EMASK
) != su_IDEC_STATE_EBASE
)
307 goto jm1
; /* Fishy */
317 if(((su_idec_s64_cp(&bt
, b
.cp
, 10, &cpb
)
318 ) & su_IDEC_STATE_EMASK
) != su_IDEC_STATE_EBASE
)
319 goto jm1
; /* Fishy */
325 a
.cp
= (cpa_pid
!= NULL
) ? xa
: cpa
;
326 b
.cp
= (cpb_pid
!= NULL
) ? xb
: cpb
;
327 for(;; ++a
.cp
, ++b
.cp
){
331 at
= (ac
!= '\0' && ac
!= n_MAILDIR_SEPARATOR
);
333 bt
= (bc
!= '\0' && bc
!= n_MAILDIR_SEPARATOR
);
344 rv
= (at
== 0 ? 0 : (at
< 0 ? -1 : 1));
357 _maildir_subdir(char const *name
, char const *sub
, enum fedit_mode fm
)
364 if ((dirp
= opendir(sub
)) == NULL
) {
365 n_err(_("Cannot open directory %s\n"),
366 n_shexp_quote_cp(savecatsep(name
, '/', sub
), FAL0
));
370 if (access(sub
, W_OK
) == -1)
372 while ((dp
= readdir(dirp
)) != NULL
) {
373 if (dp
->d_name
[0] == '.' && (dp
->d_name
[1] == '\0' ||
374 (dp
->d_name
[1] == '.' && dp
->d_name
[2] == '\0')))
376 if (dp
->d_name
[0] == '.')
378 if (!(fm
& FEDIT_NEWMAIL
) || mdlook(dp
->d_name
, NULL
) == NULL
)
379 _maildir_append(name
, sub
, dp
->d_name
);
389 _maildir_append(char const *name
, char const *sub
, char const *fn
)
393 enum mflag f
= MUSED
| MNOFROM
| MNEWEST
;
398 if (fn
!= NULL
&& sub
!= NULL
) {
399 if (!su_cs_cmp(sub
, "new"))
405 (void)/*TODO*/su_idec_s64_cp(&tib
, fn
, 10, &xp
);
409 if ((cp
= su_cs_rfind_c(xp
, ',')) != NULL
&& PCMP(cp
, >, xp
+ 2) &&
410 cp
[-1] == '2' && cp
[-2] == n_MAILDIR_SEPARATOR
) {
411 while (*++cp
!= '\0') {
433 /* Ensure room (and a NULLified last entry) */
435 message_append(NULL
);
438 if (fn
== NULL
|| sub
== NULL
)
441 m
= &message
[msgCount
++];
446 i
= su_cs_len(fn
) +1;
448 m
->m_maildir_file
= tmp
= n_alloc(j
+ 1 + i
);
449 su_mem_copy(tmp
, sub
, j
);
451 su_mem_copy(&tmp
[j
], fn
, i
);
455 m
->m_maildir_hash
= su_cs_hash(fn
);
462 a_maildir_readin(char const *name
, struct message
*mp
){
467 uz bufsize
, cnt
, buflen
;
473 mx_fs_linepool_aquire(&buf
, &bufsize
);
475 if((fp
= mx_fs_open(mp
->m_maildir_file
, "r")) == NIL
){
476 emsg
= _("Cannot read %s for message %lu\n");
479 emsg
= _("I/O error reading %s for message %lu\n");
481 offset
= ftell(mb
.mb_otf
);
486 while(fgetline(&buf
, &bufsize
, &cnt
, &buflen
, fp
, TRU1
) != NIL
){
487 /* Since we simply copy over data without doing any transfer
488 * encoding reclassification/adjustment we *have* to perform
489 * RFC 4155 compliant From_ quoting here */
490 if(b
&& is_head(buf
, buflen
, FAL0
)){
491 if(putc('>', mb
.mb_otf
) == EOF
)
495 if(fwrite(buf
, 1, buflen
, mb
.mb_otf
) != buflen
)
505 /* TODO we need \n\n for mbox format.
506 * TODO That is to say we do it wrong here in order to get it right
507 * TODO when send.c stuff or with MBOX handling, even though THIS
508 * TODO line is solely a property of the MBOX database format! */
509 if(putc('\n', mb
.mb_otf
) == EOF
)
515 if(fflush(mb
.mb_otf
) == EOF
)
518 mp
->m_size
= mp
->m_xsize
= size
;
519 mp
->m_lines
= mp
->m_xlines
= lines
;
520 mp
->m_block
= mailx_blockof(offset
);
521 mp
->m_offset
= mailx_offsetof(offset
);
529 mx_fs_linepool_release(buf
, bufsize
);
536 n_shexp_quote_cp(savecatsep(name
, '/', mp
->m_maildir_file
), FAL0
),
537 S(ul
,P2UZ(mp
- message
+ 1)));
545 struct n_timespec
const *tsp
;
546 int dodel
, c
, gotcha
= 0, held
= 0, modflags
= 0;
552 if (!(n_pstate
& n_PS_EDIT
)) {
554 for (m
= message
, c
= 0; PCMP(m
, <, message
+ msgCount
); ++m
) {
555 if (m
->m_flag
& MBOX
)
559 if (makembox() == STOP
)
563 tsp
= n_time_now(TRU1
); /* TODO FAL0, eventloop update! */
565 n_autorec_relax_create();
566 for (m
= message
, gotcha
= 0, held
= 0; PCMP(m
, <, message
+ msgCount
);
568 if (n_pstate
& n_PS_EDIT
)
569 dodel
= m
->m_flag
& MDELETED
;
571 dodel
= !((m
->m_flag
& MPRESERVE
) || !(m
->m_flag
& MTOUCH
));
573 if (unlink(m
->m_maildir_file
) < 0)
574 n_err(_("Cannot delete file %s for message %lu\n"),
575 n_shexp_quote_cp(savecatsep(mailname
, '/', m
->m_maildir_file
),
576 FAL0
), (ul
)P2UZ(m
- message
+ 1));
580 if ((m
->m_flag
& (MREAD
| MSTATUS
)) == (MREAD
| MSTATUS
) ||
581 (m
->m_flag
& (MNEW
| MBOXED
| MSAVED
| MSTATUS
| MFLAG
|
582 MUNFLAG
| MANSWER
| MUNANSWER
| MDRAFT
| MUNDRAFT
))) {
583 _maildir_move(tsp
, m
);
584 n_autorec_relax_unroll();
590 n_autorec_relax_gut();
593 if ((gotcha
|| modflags
) && (n_pstate
& n_PS_EDIT
)) {
594 fprintf(n_stdout
, "%s %s\n",
595 n_shexp_quote_cp(displayname
, FAL0
),
596 ((ok_blook(bsdcompat
) || ok_blook(bsdmsgs
))
597 ? _("complete") : _("updated.")));
598 } else if (held
&& !(n_pstate
& n_PS_EDIT
) && mb
.mb_perm
!= 0) {
600 fprintf(n_stdout
, _("Held 1 message in %s\n"), displayname
);
602 fprintf(n_stdout
, _("Held %d messages in %s\n"), held
, displayname
);
606 for (m
= message
; PCMP(m
, <, message
+ msgCount
); ++m
)
607 n_free(n_UNCONST(m
->m_maildir_file
));
612 _maildir_move(struct n_timespec
const *tsp
, struct message
*m
)
617 fn
= mkname(tsp
, m
->m_flag
, m
->m_maildir_file
+ 4);
618 newfn
= savecat("cur/", fn
);
619 if (!su_cs_cmp(m
->m_maildir_file
, newfn
))
621 if (link(m
->m_maildir_file
, newfn
) == -1) {
622 n_err(_("Cannot link %s to %s: message %lu not touched\n"),
623 n_shexp_quote_cp(savecatsep(mailname
, '/', m
->m_maildir_file
), FAL0
),
624 n_shexp_quote_cp(savecatsep(mailname
, '/', newfn
), FAL0
),
625 (ul
)P2UZ(m
- message
+ 1));
628 if (unlink(m
->m_maildir_file
) == -1)
629 n_err(_("Cannot unlink %s\n"),
630 n_shexp_quote_cp(savecatsep(mailname
, '/', m
->m_maildir_file
), FAL0
));
636 mkname(struct n_timespec
const *tsp
, enum mflag f
, char const *pref
)
639 static struct n_timespec ts
;
652 cp
= n_nodename(FAL0
);
655 if (UCMP(32, n
, <, size
+ 8))
656 node
= n_realloc(node
, size
+= 20);
659 node
[n
++] = '\\', node
[n
++] = '0',
660 node
[n
++] = '5', node
[n
++] = '7';
663 node
[n
++] = '\\', node
[n
++] = '0',
664 node
[n
++] = '7', node
[n
++] = '2';
669 } while (*cp
++ != '\0');
672 /* Problem: Courier spec uses microseconds, not nanoseconds */
673 if((s
= tsp
->ts_sec
) > ts
.ts_sec
){
675 ts
.ts_nsec
= tsp
->ts_nsec
/ (n_DATE_NANOSSEC
/ n_DATE_MICROSSEC
);
677 s
= tsp
->ts_nsec
/ (n_DATE_NANOSSEC
/ n_DATE_MICROSSEC
);
680 if(s
< n_DATE_MICROSSEC
)
688 /* Create a name according to Courier spec */
689 size
= 60 + su_cs_len(node
);
690 cp
= n_autorec_alloc(size
);
691 n
= snprintf(cp
, size
, "%" PRId64
".M%" PRIdZ
"P%ld.%s:2,",
692 ts
.ts_sec
, ts
.ts_nsec
, (long)n_pid
, node
);
694 size
= (n
= su_cs_len(pref
)) + 13;
695 cp
= n_autorec_alloc(size
);
696 su_mem_copy(cp
, pref
, n
+1);
697 for (i
= n
; i
> 3; --i
)
698 if (cp
[i
- 1] == ',' && cp
[i
- 2] == '2' &&
699 cp
[i
- 3] == n_MAILDIR_SEPARATOR
) {
704 su_mem_copy(cp
+ n
, ":2,", 3 +1);
726 maildir_append1(struct n_timespec
const *tsp
, char const *name
, FILE *fp
,
727 off_t off1
, long size
, enum mflag flag
)
729 char buf
[4096], *fn
, *tfn
, *nfn
;
736 nlen
= su_cs_len(name
);
738 /* Create a unique temporary file */
739 for (nfn
= (char*)0xA /* XXX no magic */;; n_msleep(500, FAL0
)) {
740 flen
= su_cs_len(fn
= mkname(tsp
, flag
, NULL
));
741 tfn
= n_autorec_alloc(n
= nlen
+ flen
+ 6);
742 snprintf(tfn
, n
, "%s/tmp/%s", name
, fn
);
744 /* Use "wx" for O_EXCL XXX stat(2) rather redundant; coverity:TOCTOU */
745 if((!stat(tfn
, &st
) || su_err_no() == su_ERR_NOENT
) &&
746 (op
= mx_fs_open(tfn
, "wx")) != NIL
)
749 nfn
= (char*)(P2UZ(nfn
) - 1);
751 n_err(_("Can't create an unique file name in %s\n"),
752 n_shexp_quote_cp(savecat(name
, "/tmp"), FAL0
));
757 if (fseek(fp
, off1
, SEEK_SET
) == -1)
760 uz z
= UCMP(z
, size
, >, sizeof buf
) ? sizeof buf
: S(uz
,size
);
762 if (z
!= (n
= fread(buf
, 1, z
, fp
)) || n
!= fwrite(buf
, 1, n
, op
)) {
764 n_err(_("Error writing to %s\n"), n_shexp_quote_cp(tfn
, FAL0
));
772 nfn
= n_autorec_alloc(n
= nlen
+ flen
+ 6);
773 snprintf(nfn
, n
, "%s/new/%s", name
, fn
);
774 if (link(tfn
, nfn
) == -1) {
775 n_err(_("Cannot link %s to %s\n"), n_shexp_quote_cp(tfn
, FAL0
),
776 n_shexp_quote_cp(nfn
, FAL0
));
781 if (unlink(tfn
) == -1)
782 n_err(_("Cannot unlink %s\n"), n_shexp_quote_cp(tfn
, FAL0
));
789 trycreate(char const *name
)
795 if (!stat(name
, &st
)) {
796 if (!S_ISDIR(st
.st_mode
)) {
797 n_err(_("%s is not a directory\n"), n_shexp_quote_cp(name
, FAL0
));
800 } else if (!n_path_mkdir(name
)) {
801 n_err(_("Cannot create directory %s\n"), n_shexp_quote_cp(name
, FAL0
));
811 mkmaildir(char const *name
) /* TODO proper cleanup on error; use path[] loop */
818 if (trycreate(name
) == OKAY
) {
819 np
= n_lofi_alloc((i
= su_cs_len(name
)) + 4 +1);
820 su_mem_copy(np
, name
, i
);
821 su_mem_copy(&np
[i
], "/tmp", 4 +1);
822 if (trycreate(np
) == OKAY
) {
823 su_mem_copy(&np
[i
], "/new", 4);
824 if (trycreate(np
) == OKAY
) {
825 su_mem_copy(&np
[i
], "/cur", 4);
835 static struct message
*
836 mdlook(char const *name
, struct message
*data
)
838 struct message
**mpp
, *mp
;
843 i
= data
->m_maildir_hash
;
845 i
= su_cs_hash(name
);
847 mpp
= &a_maildir_tbl
[i
%= a_maildir_tbl_prime
];
850 if((mp
= *mpp
) == NULL
){
851 if(UNLIKELY(data
!= NULL
)){
853 if(i
> a_maildir_tbl_maxdist
)
854 a_maildir_tbl_maxdist
= i
;
857 }else if(mp
->m_maildir_hash
== h
&&
858 !su_cs_cmp(&mp
->m_maildir_file
[4], name
))
861 if(UNLIKELY(mpp
++ == a_maildir_tbl_top
))
863 if(++i
> a_maildir_tbl_maxdist
&& UNLIKELY(data
== NULL
)){
879 i
= a_maildir_tbl_prime
= msgCount
;
882 a_maildir_tbl_prime
= su_prime_lookup_next(a_maildir_tbl_prime
);
883 while(a_maildir_tbl_prime
< i
);
884 a_maildir_tbl
= n_calloc(a_maildir_tbl_prime
, sizeof *a_maildir_tbl
);
885 a_maildir_tbl_top
= &a_maildir_tbl
[a_maildir_tbl_prime
- 1];
886 a_maildir_tbl_maxdist
= 0;
887 for(mp
= message
, i
= msgCount
; i
-- != 0; ++mp
)
888 mdlook(&mp
->m_maildir_file
[4], mp
);
893 subdir_remove(char const *name
, char const *sub
)
896 int pathsize
, pathend
, namelen
, sublen
, n
;
902 namelen
= su_cs_len(name
);
903 sublen
= su_cs_len(sub
);
904 path
= n_alloc(pathsize
= namelen
+ sublen
+ 30 +1);
905 su_mem_copy(path
, name
, namelen
);
907 su_mem_copy(path
+ namelen
+ 1, sub
, sublen
);
908 path
[namelen
+ sublen
+ 1] = '/';
909 path
[pathend
= namelen
+ sublen
+ 2] = '\0';
911 if ((dirp
= opendir(path
)) == NULL
) {
915 while ((dp
= readdir(dirp
)) != NULL
) {
916 if (dp
->d_name
[0] == '.' && (dp
->d_name
[1] == '\0' ||
917 (dp
->d_name
[1] == '.' && dp
->d_name
[2] == '\0')))
919 if (dp
->d_name
[0] == '.')
921 n
= su_cs_len(dp
->d_name
);
922 if (UCMP(32, pathend
+ n
+1, >, pathsize
))
923 path
= n_realloc(path
, pathsize
= pathend
+ n
+ 30);
924 su_mem_copy(path
+ pathend
, dp
->d_name
, n
+1);
925 if (unlink(path
) == -1) {
933 path
[pathend
] = '\0';
934 if (rmdir(path
) == -1) {
946 maildir_setfile(char const *who
, char const * volatile name
,
949 n_sighdl_t
volatile saveint
;
956 omsgCount
= msgCount
;
957 if (cwget(&cw
) == STOP
) {
958 n_alert(_("Cannot open current directory"));
962 if (!(fm
& FEDIT_NEWMAIL
) && !quit(FAL0
))
965 saveint
= safe_signal(SIGINT
, SIG_IGN
);
967 if (!(fm
& FEDIT_NEWMAIL
)) {
968 if (fm
& FEDIT_SYSBOX
)
969 n_pstate
&= ~n_PS_EDIT
;
971 n_pstate
|= n_PS_EDIT
;
981 mb
.mb_type
= MB_MAILDIR
;
984 if(!n_is_dir(name
, FAL0
)){
985 emsg
= N_("Not a maildir: %s\n");
987 }else if(chdir(name
) < 0){
988 emsg
= N_("Cannot enter maildir://%s\n");
990 n_err(V_(emsg
), n_shexp_quote_cp(name
, FAL0
));
992 mb
.mb_type
= MB_VOID
;
996 safe_signal(SIGINT
, saveint
);
1000 a_maildir_tbl
= NULL
;
1001 if (sigsetjmp(_maildir_jmp
, 1) == 0) {
1002 if (fm
& FEDIT_NEWMAIL
)
1004 if (saveint
!= SIG_IGN
)
1005 safe_signal(SIGINT
, &__maildircatch
);
1006 if(a_maildir_setfile1(name
, fm
, omsgCount
) < 0){
1007 if((fm
& FEDIT_NEWMAIL
) && a_maildir_tbl
!= NIL
)
1008 n_free(a_maildir_tbl
);
1009 emsg
= N_("Cannot setup maildir://%s\n");
1013 if ((fm
& FEDIT_NEWMAIL
) && a_maildir_tbl
!= NULL
)
1014 n_free(a_maildir_tbl
);
1016 safe_signal(SIGINT
, saveint
);
1018 if (cwret(&cw
) == STOP
)
1019 n_panic(_("Cannot change back to current directory"));
1023 if ((fm
& FEDIT_NEWMAIL
) && mb
.mb_sorted
&& msgCount
> omsgCount
) {
1028 if (!(fm
& FEDIT_NEWMAIL
)) {
1029 n_pstate
&= ~n_PS_SAW_COMMAND
;
1030 n_pstate
|= n_PS_SETFILE_OPENED
;
1033 if ((n_poption
& n_PO_EXISTONLY
) && !(n_poption
& n_PO_HEADERLIST
)) {
1034 i
= (msgCount
== 0);
1038 if (!(fm
& FEDIT_NEWMAIL
) && (fm
& FEDIT_SYSBOX
) && msgCount
== 0) {
1039 if (mb
.mb_type
== MB_MAILDIR
/* XXX ?? */ && !ok_blook(emptystart
))
1040 n_err(_("No mail for %s at %s\n"), who
, n_shexp_quote_cp(name
, FAL0
));
1045 if ((fm
& FEDIT_NEWMAIL
) && msgCount
> omsgCount
)
1046 newmailinfo(omsgCount
);
1054 maildir_quit(boole hold_sigs_on
)
1066 if (cwget(&cw
) == STOP
) {
1067 n_alert(_("Cannot open current directory"));
1071 saveint
= safe_signal(SIGINT
, SIG_IGN
);
1073 if (chdir(mailname
) == -1) {
1074 n_err(_("Cannot change directory to %s\n"),
1075 n_shexp_quote_cp(mailname
, FAL0
));
1077 safe_signal(SIGINT
, saveint
);
1081 if (sigsetjmp(_maildir_jmp
, 1) == 0) {
1082 if (saveint
!= SIG_IGN
)
1083 safe_signal(SIGINT
, &__maildircatch_hold
);
1087 safe_signal(SIGINT
, saveint
);
1089 if (cwret(&cw
) == STOP
)
1090 n_panic(_("Cannot change back to current directory"));
1101 maildir_append(char const *name
, FILE *fp
, long offset
)
1103 struct n_timespec
const *tsp
;
1104 char *buf
, *bp
, *lp
;
1105 uz bufsize
, buflen
, cnt
;
1106 off_t off1
= -1, offs
;
1109 enum {_NONE
= 0, _INHEAD
= 1<<0, _NLSEP
= 1<<1} state
;
1113 if ((rv
= mkmaildir(name
)) != OKAY
)
1116 mx_fs_linepool_aquire(&buf
, &bufsize
);
1119 offs
= offset
/* BSD will move due to O_APPEND! ftell(fp) */;
1121 tsp
= n_time_now(TRU1
); /* TODO -> eventloop */
1123 n_autorec_relax_create();
1124 for (flag
= MNEW
, state
= _NLSEP
;;) {
1125 bp
= fgetline(&buf
, &bufsize
, &cnt
, &buflen
, fp
, TRU1
);
1128 ((state
& (_INHEAD
| _NLSEP
)) == _NLSEP
&&
1129 is_head(buf
, buflen
, FAL0
))) {
1130 if (off1
!= (off_t
)-1) {
1131 if((rv
= maildir_append1(tsp
, name
, fp
, off1
, size
, flag
)) == STOP
)
1133 n_autorec_relax_unroll();
1134 if (fseek(fp
, offs
+ buflen
, SEEK_SET
) == -1) {
1139 off1
= offs
+ buflen
;
1156 if (buf
[0] == '\n') {
1159 } else if (state
& _INHEAD
) {
1160 if (!su_cs_cmp_case_n(buf
, "status", 6)) {
1162 while (su_cs_is_white(*lp
))
1165 while (*++lp
!= '\0')
1174 } else if (!su_cs_cmp_case_n(buf
, "x-status", 8)) {
1176 while (su_cs_is_white(*lp
))
1179 while (*++lp
!= '\0')
1198 n_autorec_relax_gut();
1199 mx_fs_linepool_release(buf
, bufsize
);
1206 maildir_remove(char const *name
)
1208 enum okay rv
= STOP
;
1211 if (subdir_remove(name
, "tmp") == STOP
||
1212 subdir_remove(name
, "new") == STOP
||
1213 subdir_remove(name
, "cur") == STOP
)
1215 if (rmdir(name
) == -1) {
1225 #include "su/code-ou.h"
1226 #endif /* mx_HAVE_MAILDIR */