1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2018 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
8 * Copyright (c) 1980, 1993
9 * The Regents of the University of California. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38 #ifndef HAVE_AMALGAMATION
42 /* line is a buffer with the result of fgets(). Returns the first newline or
43 * the last character read */
44 static size_t _length_of_line(char const *line
, size_t linesize
);
46 /* Read a line, one character at a time */
47 static char * _fgetline_byone(char **line
, size_t *linesize
, size_t *llen
,
48 FILE *fp
, int appendnl
, size_t n n_MEMORY_DEBUG_ARGS
);
51 static bool_t
a_file_lock(int fd
, enum n_file_lock_type ft
, off_t off
,
55 _length_of_line(char const *line
, size_t linesize
)
60 /* Last character is always '\0' and was added by fgets() */
61 for (--linesize
, i
= 0; i
< linesize
; i
++)
64 i
= (i
< linesize
) ? i
+ 1 : linesize
;
70 _fgetline_byone(char **line
, size_t *linesize
, size_t *llen
, FILE *fp
,
71 int appendnl
, size_t n n_MEMORY_DEBUG_ARGS
)
77 assert(*linesize
== 0 || *line
!= NULL
);
78 n_pstate
&= ~n_PS_READLINE_NL
;
81 if (*linesize
<= LINESIZE
|| n
>= *linesize
- 128) {
82 *linesize
+= ((rv
== NULL
) ? LINESIZE
+ n
+ 1 : 256);
83 *line
= rv
= (n_realloc
)(rv
, *linesize n_MEMORY_DEBUG_ARGSCALL
);
90 n_pstate
|= n_PS_READLINE_NL
;
114 a_file_lock(int fd
, enum n_file_lock_type flt
, off_t off
, off_t len
)
120 memset(&flp
, 0, sizeof flp
);
124 case FLT_READ
: rv
= F_RDLCK
; break;
125 case FLT_WRITE
: rv
= F_WRLCK
; break;
129 flp
.l_whence
= SEEK_SET
;
132 if (!(rv
= (fcntl(fd
, F_SETLK
, &flp
) != -1)))
144 (fgetline
)(char **line
, size_t *linesize
, size_t *cnt
, size_t *llen
, FILE *fp
,
145 int appendnl n_MEMORY_DEBUG_ARGS
)
152 /* Without count, we can't determine where the chars returned by fgets()
153 * end if there's no newline. We have to read one character by one */
154 rv
= _fgetline_byone(line
, linesize
, llen
, fp
, appendnl
, 0
155 n_MEMORY_DEBUG_ARGSCALL
);
159 n_pstate
&= ~n_PS_READLINE_NL
;
161 if ((rv
= *line
) == NULL
|| *linesize
< LINESIZE
)
162 *line
= rv
= (n_realloc
)(rv
, *linesize
= LINESIZE
163 n_MEMORY_DEBUG_ARGSCALL
);
164 sz
= (*linesize
<= *cnt
) ? *linesize
: *cnt
+ 1;
165 if (sz
<= 1 || fgets(rv
, sz
, fp
) == NULL
) {
166 /* Leave llen untouched; it is used to determine whether the last line
167 * was \n-terminated in some callers */
172 i_llen
= _length_of_line(rv
, sz
);
174 while (rv
[i_llen
- 1] != '\n') {
175 *line
= rv
= (n_realloc
)(rv
, *linesize
+= 256 n_MEMORY_DEBUG_ARGSCALL
);
176 sz
= *linesize
- i_llen
;
177 sz
= (sz
<= *cnt
) ? sz
: *cnt
+ 1;
178 if (sz
<= 1 || fgets(rv
+ i_llen
, sz
, fp
) == NULL
) {
185 sz
= _length_of_line(rv
+ i_llen
, sz
);
197 (readline_restart
)(FILE *ibuf
, char **linebuf
, size_t *linesize
, size_t n
200 /* TODO readline_restart(): always *appends* LF just to strip it again;
201 * TODO should be configurable just as for fgetline(); ..or whatever..
209 /* Interrupts will cause trouble if we are inside a stdio call. As this is
210 * only relevant if input is from tty, bypass it by read(), then */
211 if ((n_psonce
& n_PSO_TTYIN
) && fileno(ibuf
) == 0) {
212 assert(*linesize
== 0 || *linebuf
!= NULL
);
213 n_pstate
&= ~n_PS_READLINE_NL
;
215 if (*linesize
<= LINESIZE
|| n
>= *linesize
- 128) {
216 *linesize
+= ((*linebuf
== NULL
) ? LINESIZE
+ n
+ 1 : 256);
217 *linebuf
= (n_realloc
)(*linebuf
, *linesize n_MEMORY_DEBUG_ARGSCALL
);
220 sz
= read(0, *linebuf
+ n
, *linesize
- n
- 1);
223 (*linebuf
)[n
] = '\0';
224 if ((*linebuf
)[n
- 1] == '\n') {
225 n_pstate
|= n_PS_READLINE_NL
;
229 if (sz
< 0 && n_err_no
== n_ERR_INTR
)
231 /* TODO eh. what is this? that now supposed to be a line?!? */
233 if ((*linebuf
)[n
- 1] != '\n') {
234 (*linebuf
)[n
++] = '\n';
235 (*linebuf
)[n
] = '\0';
237 n_pstate
|= n_PS_READLINE_NL
;
244 /* Not reading from standard input or standard input not a terminal. We
245 * read one char at a time as it is the only way to get lines with
246 * embedded NUL characters in standard stdio */
247 if (_fgetline_byone(linebuf
, linesize
, &n
, ibuf
, 1, n
248 n_MEMORY_DEBUG_ARGSCALL
) == NULL
)
251 if (n
> 0 && (*linebuf
)[n
- 1] == '\n')
252 (*linebuf
)[--n
] = '\0';
260 setptr(FILE *ibuf
, off_t offset
)
263 char *cp
, *linebuf
= NULL
;
266 bool_t need_rfc4155
, maybe
, inhead
, from_
;
267 size_t linesize
= 0, filesize
, cnt
;
270 memset(&self
, 0, sizeof self
);
271 self
.m_flag
= MUSED
| MNEW
| MNEWEST
;
272 filesize
= mailsize
- offset
;
273 offset
= ftell(mb
.mb_otf
);
274 need_rfc4155
= ok_blook(mbox_rfc4155
);
279 if (fgetline(&linebuf
, &linesize
, &filesize
, &cnt
, ibuf
, 0) == NULL
) {
280 self
.m_xsize
= self
.m_size
;
281 self
.m_xlines
= self
.m_lines
;
282 self
.m_content_info
= CI_HAVE_HEADER
| CI_HAVE_BODY
;
284 message_append(&self
);
285 message_append_null();
292 if (linebuf
[0] == '\0')
295 /* XXX Convert CRLF to LF; this should be rethought in that
296 * XXX CRLF input should possibly end as CRLF output? */
297 if (cnt
>= 2 && linebuf
[cnt
- 1] == '\n' && linebuf
[cnt
- 2] == '\r')
298 linebuf
[--cnt
- 1] = '\n';
299 fwrite(linebuf
, sizeof *linebuf
, cnt
, mb
.mb_otf
);
300 if (ferror(mb
.mb_otf
)) {
301 n_perr(_("/tmp"), 0);
304 if (linebuf
[cnt
- 1] == '\n')
305 linebuf
[cnt
- 1] = '\0';
306 /* TODO In v15 this should use a/the flat MIME parser in order to ignore
307 * TODO "From " when MIME boundaries are active -- whereas this opens
308 * TODO another can of worms, it very likely is better than messing up
309 * TODO MIME because of a "From " line! */
310 if (maybe
&& linebuf
[0] == 'F' &&
311 (from_
= is_head(linebuf
, cnt
, TRU1
)) &&
312 (from_
== TRU1
|| !need_rfc4155
)) {
313 /* TODO char date[n_FROM_DATEBUF];
314 * TODO extract_date_from_from_(linebuf, cnt, date);
315 * TODO self.m_time = 10000; */
316 if (from_
== TRUM1
) {
317 if (n_poption
& n_PO_D_V
)
318 n_err(_("Invalid MBOX \"From_ line\": %.*s\n"),
320 else if (!(mb
.mb_active
& MB_FROM__WARNED
))
321 n_err(_("MBOX mailbox contains non-conforming From_ line(s)!\n"
322 " Message boundaries may have been falsely detected!\n"
323 " Setting variable *mbox-rfc4155* and reopen should improve "
325 " If so, make changes permanent: \"copy * SOME-FILE\". "
326 "Then unset *mbox-rfc4155*\n"));
327 mb
.mb_active
|= MB_FROM__WARNED
;
329 self
.m_xsize
= self
.m_size
;
330 self
.m_xlines
= self
.m_lines
;
331 self
.m_content_info
= CI_HAVE_HEADER
| CI_HAVE_BODY
;
333 message_append(&self
);
335 self
.m_flag
= MUSED
| MNEW
| MNEWEST
;
338 self
.m_block
= mailx_blockof(offset
);
339 self
.m_offset
= mailx_offsetof(offset
);
341 } else if (linebuf
[0] == 0) {
344 for (cp
= linebuf
, cp2
= "status";; ++cp
) {
345 if ((c
= *cp2
++) == 0) {
346 while (c
= *cp
++, whitechar(c
))
350 while ((c
= *cp
++) != '\0')
352 self
.m_flag
|= MREAD
;
354 self
.m_flag
&= ~MNEW
;
357 if (*cp
!= c
&& *cp
!= upperconv(c
))
360 for (cp
= linebuf
, cp2
= "x-status";; ++cp
) {
361 if ((c
= *cp2
++) == 0) {
362 while ((c
= *cp
++, whitechar(c
)))
366 while ((c
= *cp
++) != '\0')
368 self
.m_flag
|= MFLAGGED
;
370 self
.m_flag
|= MANSWERED
;
372 self
.m_flag
|= MDRAFTED
;
375 if (*cp
!= c
&& *cp
!= upperconv(c
))
382 maybe
= (linebuf
[0] == 0);
394 rv
= (fstat(fileno(iob
), &sbuf
) == -1) ? 0 : sbuf
.st_size
;
400 n_file_lock(int fd
, enum n_file_lock_type flt
, off_t off
, off_t len
,
407 if(pollmsecs
== UIZ_MAX
)
408 pollmsecs
= FILE_LOCK_MILLIS
;
411 for (didmsg
= FAL0
, tries
= 0; tries
<= FILE_LOCK_TRIES
; ++tries
) {
412 rv
= a_file_lock(fd
, flt
, off
, len
);
418 if (rv
|| pollmsecs
== 0)
422 n_err(_("Failed to create a file lock, waiting %lu milliseconds "),
427 n_msleep(pollmsecs
, FAL0
);
431 n_err(" %s\n", (rv
? _("ok") : _("failure")));