1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Auxiliary functions.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2013 Steffen "Daode" Nurpmeso <sdaoden@users.sf.net>.
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. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the University of
22 * California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
42 #include <sys/utsname.h>
51 # include <sys/socket.h>
61 /* {hold,rele}_all_sigs() */
62 static size_t _alls_depth
;
63 static sigset_t _alls_nset
, _alls_oset
;
66 panic(char const *format
, ...)
70 fprintf(stderr
, tr(1, "Panic: "));
73 vfprintf(stderr
, format
, ap
);
84 if (_alls_depth
++ == 0) {
85 sigfillset(&_alls_nset
);
86 sigdelset(&_alls_nset
, SIGKILL
);
87 sigdelset(&_alls_nset
, SIGSTOP
);
88 sigprocmask(SIG_BLOCK
, &_alls_nset
, &_alls_oset
);
95 if (--_alls_depth
== 0)
96 sigprocmask(SIG_SETMASK
, &_alls_oset
, (sigset_t
*)NULL
);
100 * Touch the named message by setting its MTOUCH flag.
101 * Touched messages have the effect of not being sent
102 * back to the system mailbox on exit.
105 touch(struct message
*mp
)
108 mp
->m_flag
|= MTOUCH
;
109 if ((mp
->m_flag
& MREAD
) == 0)
110 mp
->m_flag
|= MREAD
|MSTATUS
;
114 * Test to see if the passed file name is a directory.
115 * Return true if it is.
118 is_dir(char const *name
)
122 if (stat(name
, &sbuf
) < 0)
124 return(S_ISDIR(sbuf
.st_mode
));
128 * Count the number of arguments in the given string raw list.
131 argcount(char **argv
)
135 for (ap
= argv
; *ap
++ != NULL
;)
137 return ap
- argv
- 1;
141 colalign(const char *cp
, int col
, int fill
, int *cols_decr_used_or_null
)
143 int col_orig
= col
, n
, sz
;
146 np
= nb
= salloc(mb_cur_max
* strlen(cp
) + col
+ 1);
148 #if defined (HAVE_MBTOWC) && defined (HAVE_WCWIDTH)
149 if (mb_cur_max
> 1) {
152 if ((sz
= mbtowc(&wc
, cp
, mb_cur_max
)) < 0) {
155 if ((n
= wcwidth(wc
)) < 0)
159 #endif /* HAVE_MBTOWC && HAVE_WCWIDTH */
166 if (sz
== 1 && spacechar(*cp
)) {
174 if (fill
&& col
!= 0) {
176 memmove(nb
+ col
, nb
, (size_t)(np
- nb
));
177 memset(nb
, ' ', col
);
179 memset(np
, ' ', col
);
185 if (cols_decr_used_or_null
!= NULL
)
186 *cols_decr_used_or_null
-= col_orig
- col
;
191 paging_seems_sensible(void)
196 if (IS_TTY_SESSION() && (cp
= voption("crt")) != NULL
)
197 ret
= (*cp
!= '\0') ? (size_t)atol(cp
) : (size_t)scrnheight
;
202 page_or_print(FILE *fp
, size_t lines
)
209 if ((rows
= paging_seems_sensible()) != 0 && lines
== 0) {
210 while ((c
= getc(fp
)) != EOF
)
211 if (c
== '\n' && ++lines
> rows
)
216 if (rows
!= 0 && lines
>= rows
)
217 run_command(get_pager(), 0, fileno(fp
), -1, NULL
, NULL
, NULL
);
219 while ((c
= getc(fp
)) != EOF
)
224 which_protocol(const char *name
)
226 register const char *cp
;
232 if (name
[0] == '%' && name
[1] == ':')
234 for (cp
= name
; *cp
&& *cp
!= ':'; cp
++)
235 if (!alnumchar(*cp
&0377))
237 if (cp
[0] == ':' && cp
[1] == '/' && cp
[2] == '/') {
238 if (strncmp(name
, "pop3://", 7) == 0)
243 tr(216, "No POP3 support compiled in.\n"));
245 if (strncmp(name
, "pop3s://", 8) == 0)
250 tr(225, "No SSL support compiled in.\n"));
252 if (strncmp(name
, "imap://", 7) == 0)
257 tr(269, "No IMAP support compiled in.\n"));
259 if (strncmp(name
, "imaps://", 8) == 0)
264 tr(225, "No SSL support compiled in.\n"));
266 return PROTO_UNKNOWN
;
268 /* TODO This is the de facto maildir code and thus belongs
269 * TODO into maildir! */
270 file
: p
= PROTO_FILE
;
271 np
= ac_alloc((sz
= strlen(name
)) + 5);
272 memcpy(np
, name
, sz
+ 1);
273 if (stat(name
, &st
) == 0) {
274 if (S_ISDIR(st
.st_mode
)) {
275 strcpy(&np
[sz
], "/tmp");
276 if (stat(np
, &st
) == 0 && S_ISDIR(st
.st_mode
)) {
277 strcpy(&np
[sz
], "/new");
278 if (stat(np
, &st
) == 0 &&
279 S_ISDIR(st
.st_mode
)) {
280 strcpy(&np
[sz
], "/cur");
281 if (stat(np
, &st
) == 0 &&
288 strcpy(&np
[sz
], ".gz");
289 if (stat(np
, &st
) < 0) {
290 strcpy(&np
[sz
], ".bz2");
291 if (stat(np
, &st
) < 0) {
292 if ((cp
= value("newfolders")) != 0 &&
293 strcmp(cp
, "maildir") == 0)
310 h
= (h
<< 4 & 0xffffffff) + (*cp
&0377);
311 if ((g
= h
& 0xf0000000) != 0) {
322 const long primes
[] = {
323 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
324 131071, 262139, 524287, 1048573, 2097143, 4194301,
325 8388593, 16777213, 33554393, 67108859, 134217689,
326 268435399, 536870909, 1073741789, 2147483647
331 for (i
= 0; i
< sizeof primes
/ sizeof *primes
; i
++)
332 if ((mprime
= primes
[i
]) >= (n
< 65536 ? n
*4 :
333 n
< 262144 ? n
*2 : n
))
335 if (i
== sizeof primes
/ sizeof *primes
)
336 mprime
= n
; /* not so prime, but better than failure */
341 expand_shell_escape(char const **s
, bool_t use_nail_extensions
)
346 if ((c
= *xs
& 0xFF) == '\0')
352 switch ((c
= *xs
& 0xFF)) {
354 case 'a': c
= '\a'; break;
355 case 'b': c
= '\b'; break;
356 case 'c': c
= -1; break;
357 case 'f': c
= '\f'; break;
358 case 'n': c
= '\n'; break;
359 case 'r': c
= '\r'; break;
360 case 't': c
= '\t'; break;
361 case 'v': c
= '\v'; break;
363 for (++xs
, c
= 0, n
= 4; --n
> 0 && octalchar(*xs
); ++xs
) {
368 /* S-nail extension for nice prompt support */
370 if (use_nail_extensions
) {
371 c
= exec_last_comm_error
? '1' : '0';
376 /* A sole <backslash> at EOS is treated as-is! */
394 if ((ccp
= value("prompt")) == NULL
) {
395 buf
[0] = value("bsdcompat") ? '&' : '?';
401 for (cp
= buf
; cp
< buf
+ sizeof(buf
) - 1; ++cp
) {
402 int c
= expand_shell_escape(&ccp
, TRU1
);
415 struct passwd
*pw
= getpwuid(uid
);
417 return pw
== NULL
? NULL
: pw
->pw_name
;
426 if ((np
= getenv("USER")) != NULL
)
428 if ((np
= getname(uid
= getuid())) != NULL
)
430 panic(tr(201, "Cannot associate a name with uid %d\n"), (int)uid
);
436 nodename(int mayoverride
)
438 static char *hostname
;
443 struct addrinfo hints
, *res
;
445 struct hostent
*hent
;
449 if (mayoverride
&& (hn
= value("hostname")) != NULL
&& *hn
!= '\0') {
450 if (hostname
!= NULL
)
452 hostname
= sstrdup(hn
);
453 } else if (hostname
== NULL
) {
458 memset(&hints
, 0, sizeof hints
);
459 hints
.ai_family
= AF_UNSPEC
;
460 hints
.ai_socktype
= SOCK_DGRAM
; /* dummy */
461 hints
.ai_flags
= AI_CANONNAME
;
462 if (getaddrinfo(hn
, "0", &hints
, &res
) == 0) {
463 if (res
->ai_canonname
!= NULL
) {
464 size_t l
= strlen(res
->ai_canonname
);
465 hn
= ac_alloc(l
+ 1);
466 memcpy(hn
, res
->ai_canonname
, l
+ 1);
471 hent
= gethostbyname(hn
);
477 hostname
= sstrdup(hn
);
478 #if defined HAVE_SOCKETS && defined HAVE_IPV6
479 if (hn
!= ut
.nodename
)
487 lookup_password_for_token(char const *token
)
493 var
= ac_alloc(tl
+ 10);
495 memcpy(var
, "password-", 9);
496 memcpy(var
+ 9, token
, tl
);
499 if ((cp
= value(var
)) != NULL
)
506 getrandstring(size_t length
)
508 static unsigned char nodedigest
[16];
520 data
= ac_alloc(length
);
521 if ((fd
= open("/dev/urandom", O_RDONLY
)) < 0 ||
522 length
!= (size_t)read(fd
, data
, length
)) {
529 MD5Update(&ctx
, (unsigned char *)cp
, strlen(cp
));
530 MD5Final(nodedigest
, &ctx
);
532 /* In that case it's only used for boundaries and
533 * Message-Id:s so that srand(3) should suffice */
535 for (i
= 0; i
< sizeof(nodedigest
); ++i
)
536 nodedigest
[i
] = (unsigned char)(
540 for (i
= 0; i
< length
; i
++)
542 (int)(255 * (rand() / (RAND_MAX
+ 1.0))) ^
543 nodedigest
[i
% sizeof nodedigest
]);
548 (void)b64_encode_buf(&b64
, data
, length
, B64_SALLOC
);
550 assert(length
< b64
.l
);
551 b64
.s
[length
] = '\0';
557 md5tohex(char hex
[MD5TOHEX_SIZE
], void const *vp
)
562 for (i
= 0; i
< MD5TOHEX_SIZE
/ 2; i
++) {
564 hex
[j
] = hexchar((cp
[i
] & 0xf0) >> 4);
565 hex
[++j
] = hexchar(cp
[i
] & 0x0f);
567 hex
[MD5TOHEX_SIZE
] = '\0';
572 cram_md5_string(char const *user
, char const *pass
, char const *b64
)
575 char digest
[16], *cp
;
581 (void)b64_decode(&out
, &in
, NULL
);
582 assert(out
.s
!= NULL
);
584 hmac_md5((unsigned char*)out
.s
, out
.l
, UNCONST(pass
), strlen(pass
),
587 cp
= md5tohex(salloc(MD5TOHEX_SIZE
+ 1), digest
);
590 in
.l
= lu
+ MD5TOHEX_SIZE
+1;
591 in
.s
= ac_alloc(lu
+ 1 + MD5TOHEX_SIZE
+1);
592 memcpy(in
.s
, user
, lu
);
594 memcpy(in
.s
+ lu
+ 1, cp
, MD5TOHEX_SIZE
);
595 (void)b64_encode(&out
, &in
, B64_SALLOC
|B64_CRLF
);
599 #endif /* HAVE_MD5 */
602 makedir(const char *name
)
607 if (mkdir(name
, 0700) < 0) {
609 if ((e
== EEXIST
|| e
== ENOSYS
) &&
610 stat(name
, &st
) == 0 &&
611 (st
.st_mode
&S_IFMT
) == S_IFDIR
)
622 if ((cw
->cw_fd
= open(".", O_RDONLY
)) < 0)
624 if (fchdir(cw
->cw_fd
) < 0) {
634 if (fchdir(cw
->cw_fd
) < 0)
640 cwrelse(struct cw
*cw
)
644 #else /* !HAVE_FCHDIR */
648 if (getcwd(cw
->cw_wd
, sizeof cw
->cw_wd
) == NULL
|| chdir(cw
->cw_wd
) < 0)
656 if (chdir(cw
->cw_wd
) < 0)
663 cwrelse(struct cw
*cw
)
667 #endif /* !HAVE_FCHDIR */
670 makeprint(struct str
const *in
, struct str
*out
)
672 static int print_all_chars
= -1;
673 char const *inp
, *maxp
;
677 if (print_all_chars
== -1)
678 print_all_chars
= (value("print-all-chars") != NULL
);
681 out
->s
= outp
= smalloc(msz
);
685 if (print_all_chars
) {
687 memcpy(outp
, inp
, out
->l
);
691 #if defined (HAVE_MBTOWC) && defined (HAVE_WCTYPE_H)
692 if (mb_cur_max
> 1) {
693 char mbb
[MB_LEN_MAX
+ 1];
699 n
= mbtowc(&wc
, inp
, maxp
- inp
);
705 /* FIXME Why mbtowc() resetting here?
706 * FIXME what about ISO 2022-JP plus -- those
707 * FIXME will loose shifts, then!
708 * FIXME THUS - we'd need special "known points"
709 * FIXME to do so - say, after a newline!!
710 * FIXME WE NEED TO CHANGE ALL USES +MBLEN! */
711 (void)mbtowc(&wc
, NULL
, mb_cur_max
);
712 wc
= utf8
? 0xFFFD : '?';
717 if (!iswprint(wc
) && wc
!= '\n' && wc
!= '\r' &&
718 wc
!= '\b' && wc
!= '\t') {
719 if ((wc
& ~(wchar_t)037) == 0)
720 wc
= utf8
? 0x2400 | wc
: '?';
722 wc
= utf8
? 0x2421 : '?';
724 wc
= utf8
? 0x2426 : '?';
726 if ((n
= wctomb(mbb
, wc
)) <= 0)
729 if (out
->l
>= msz
- 1) {
730 dist
= outp
- out
->s
;
731 out
->s
= srealloc(out
->s
, msz
+= 32);
732 outp
= &out
->s
[dist
];
734 for (i
= 0; i
< n
; i
++)
738 #endif /* HAVE_MBTOWC && HAVE_WCTYPE_H */
743 if (!isprint(c
) && c
!= '\n' && c
!= '\r' &&
744 c
!= '\b' && c
!= '\t')
751 out
->s
[out
->l
] = '\0';
762 makeprint(&in
, &out
);
763 rp
= salloc(out
.l
+ 1);
764 memcpy(rp
, out
.s
, out
.l
);
771 prout(const char *s
, size_t sz
, FILE *fp
)
778 makeprint(&in
, &out
);
779 n
= fwrite(out
.s
, 1, out
.l
, fp
);
785 putuc(int u
, int c
, FILE *fp
)
789 #if defined HAVE_MBTOWC && defined HAVE_WCTYPE_H
790 if (utf8
&& u
& ~(wchar_t)0177) {
791 char mbb
[MB_LEN_MAX
];
793 if ((n
= wctomb(mbb
, u
)) > 0) {
795 for (i
= 0; i
< n
; ++i
)
796 if (putc(mbb
[i
] & 0377, fp
) == EOF
) {
801 rv
= (putc('\0', fp
) != EOF
);
805 #endif /* HAVE_MBTOWC && HAVE_WCTYPE_H */
806 rv
= (putc(c
, fp
) != EOF
);
811 time_current_update(struct time_current
*tc
, bool_t full_update
)
813 tc
->tc_time
= time(NULL
);
815 memcpy(&tc
->tc_gm
, gmtime(&tc
->tc_time
), sizeof tc
->tc_gm
);
816 memcpy(&tc
->tc_local
, localtime(&tc
->tc_time
),
817 sizeof tc
->tc_local
);
818 sstpcpy(tc
->tc_ctime
, ctime(&tc
->tc_time
));
824 int my_optind
= 1, /*my_opterr = 1,*/ my_optopt
;
827 my_getopt(int argc
, char *const argv
[], char const *optstring
) /* XXX */
829 static char const *lastp
;
833 if (optstring
[0] == ':') {
842 if (optind
>= argc
|| argv
[optind
] == 0 ||
843 argv
[optind
][0] != '-' ||
844 argv
[optind
][1] == '\0')
846 if (argv
[optind
][1] == '-' && argv
[optind
][2] == '\0') {
850 curp
= &argv
[optind
][1];
853 while (optstring
[0]) {
854 if (optstring
[0] == ':' || optstring
[0] != optopt
) {
858 if (optstring
[1] == ':') {
859 if (curp
[1] != '\0') {
860 optarg
= UNCONST(&curp
[1]);
863 if ((optind
+= 2) > argc
) {
864 if (!colon
/*&& opterr*/) {
865 fprintf(stderr
, tr(79,
866 "%s: option requires an argument -- %c\n"),
867 argv
[0], (char)optopt
);
869 return colon
? ':' : '?';
871 optarg
= argv
[optind
- 1];
882 if (!colon
/*&& opterr*/)
883 fprintf(stderr
, tr(78, "%s: illegal option -- %c\n"),
892 #endif /* HAVE_GETOPT */
901 (smalloc_safe
)(size_t s SMALLOC_DEBUG_ARGS
)
906 rv
= (smalloc
)(s SMALLOC_DEBUG_ARGSCALL
);
912 (srealloc_safe
)(void *v
, size_t s SMALLOC_DEBUG_ARGS
)
917 rv
= (srealloc
)(v
, s SMALLOC_DEBUG_ARGSCALL
);
923 (scalloc_safe
)(size_t nmemb
, size_t size SMALLOC_DEBUG_ARGS
)
928 rv
= (scalloc
)(nmemb
, size SMALLOC_DEBUG_ARGSCALL
);
935 smalloc(size_t s SMALLOC_DEBUG_ARGS
)
941 if ((p
= malloc(s
)) == NULL
)
947 srealloc(void *v
, size_t s SMALLOC_DEBUG_ARGS
)
955 if ((r
= realloc(v
, s
)) == NULL
)
961 scalloc(size_t nmemb
, size_t size SMALLOC_DEBUG_ARGS
)
967 if ((vp
= calloc(nmemb
, size
)) == NULL
)
972 #else /* !HAVE_ASSERTS */
988 struct chunk
*_mlist
, *_mfree
;
991 (smalloc
)(size_t s SMALLOC_DEBUG_ARGS
)
997 s
+= sizeof(struct chunk
);
999 if ((p
.p
= malloc(s
)) == NULL
)
1002 if ((p
.c
->next
= _mlist
) != NULL
)
1004 p
.c
->file
= mdbg_file
;
1005 p
.c
->line
= (us_it
)mdbg_line
;
1007 p
.c
->size
= (ui_it
)s
;
1013 (srealloc
)(void *v
, size_t s SMALLOC_DEBUG_ARGS
)
1017 if ((p
.p
= v
) == NULL
) {
1018 p
.p
= (smalloc
)(s
, mdbg_file
, mdbg_line
);
1024 fprintf(stderr
, "srealloc(): region freed! At %s, line %d\n"
1025 "\tLast seen: %s, line %d\n",
1026 mdbg_file
, mdbg_line
, p
.c
->file
, p
.c
->line
);
1033 p
.c
->prev
->next
= p
.c
->next
;
1034 if (p
.c
->next
!= NULL
)
1035 p
.c
->next
->prev
= p
.c
->prev
;
1040 s
+= sizeof(struct chunk
);
1042 if ((p
.p
= realloc(p
.c
, s
)) == NULL
)
1045 if ((p
.c
->next
= _mlist
) != NULL
)
1047 p
.c
->file
= mdbg_file
;
1048 p
.c
->line
= (us_it
)mdbg_line
;
1050 p
.c
->size
= (ui_it
)s
;
1057 (scalloc
)(size_t nmemb
, size_t size SMALLOC_DEBUG_ARGS
)
1066 size
+= sizeof(struct chunk
);
1068 if ((p
.p
= malloc(size
)) == NULL
)
1070 memset(p
.p
, 0, size
);
1072 if ((p
.c
->next
= _mlist
) != NULL
)
1074 p
.c
->file
= mdbg_file
;
1075 p
.c
->line
= (us_it
)mdbg_line
;
1077 p
.c
->size
= (ui_it
)size
;
1083 (sfree
)(void *v SMALLOC_DEBUG_ARGS
)
1087 if ((p
.p
= v
) == NULL
) {
1088 fprintf(stderr
, "sfree(NULL) from %s, line %d\n",
1089 mdbg_file
, mdbg_line
);
1095 fprintf(stderr
, "sfree(): double-free avoided at %s, line %d\n"
1096 "\tLast seen: %s, line %d\n",
1097 mdbg_file
, mdbg_line
, p
.c
->file
, p
.c
->line
);
1104 p
.c
->prev
->next
= p
.c
->next
;
1105 if (p
.c
->next
!= NULL
)
1106 p
.c
->next
->prev
= p
.c
->prev
;
1109 if (options
& OPT_DEBUG
) {
1123 for (p
.c
= _mfree
; p
.c
!= NULL
;) {
1132 if (options
& OPT_DEBUG
)
1133 fprintf(stderr
, "smemreset(): freed %lu regions/%lu bytes\n",
1138 (smemtrace
)(void *v
)
1146 if ((fp
= Ftemp(&cp
, "Ra", "w+", 0600, 1)) == NULL
) {
1153 fprintf(fp
, "Currently allocated memory chunks:\n");
1154 for (p
.c
= _mlist
; p
.c
!= NULL
; ++lines
, p
.c
= p
.c
->next
)
1155 fprintf(fp
, "%p (%5u bytes): %s, line %hu\n",
1157 (ui_it
)(p
.c
->size
- sizeof(struct chunk
)),
1158 p
.c
->file
, p
.c
->line
);
1160 if (options
& OPT_DEBUG
) {
1161 fprintf(fp
, "sfree()d memory chunks awaiting free():\n");
1162 for (p
.c
= _mfree
; p
.c
!= NULL
; ++lines
, p
.c
= p
.c
->next
)
1163 fprintf(fp
, "%p (%5u bytes): %s, line %hu\n",
1165 (ui_it
)(p
.c
->size
- sizeof(struct chunk
)),
1166 p
.c
->file
, p
.c
->line
);
1169 page_or_print(fp
, lines
);
1175 #endif /* HAVE_ASSERTS */