smtp.c:_smtp_talk(): remove unused variable
[s-mailx.git] / auxlily.c
blobd4f95d900799481d6c020c8cb61cdec6f6f5d827
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 - 2014 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
6 */
7 /*
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
13 * are met:
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
37 * SUCH DAMAGE.
40 #ifndef HAVE_AMALGAMATION
41 # include "nail.h"
42 #endif
44 #include <sys/utsname.h>
46 #include <ctype.h>
47 #include <dirent.h>
48 #include <fcntl.h>
50 #ifdef HAVE_SOCKETS
51 # ifdef HAVE_IPV6
52 # include <sys/socket.h>
53 # endif
55 # include <netdb.h>
56 #endif
58 #ifdef HAVE_DEBUG
59 struct nyd_info {
60 char const *ni_file;
61 char const *ni_fun;
62 ui32_t ni_chirp_line;
63 ui32_t ni_level;
66 struct mem_chunk {
67 struct mem_chunk *mc_prev;
68 struct mem_chunk *mc_next;
69 char const *mc_file;
70 ui16_t mc_line;
71 ui8_t mc_isfree;
72 ui8_t __dummy;
73 ui32_t mc_size;
76 union mem_ptr {
77 void *p_p;
78 struct mem_chunk *p_c;
79 char *p_cp;
80 ui8_t *p_ui8p;
82 #endif /* HAVE_DEBUG */
84 /* NYD, memory pool debug */
85 #ifdef HAVE_DEBUG
86 static ui32_t _nyd_curr, _nyd_level;
87 static struct nyd_info _nyd_infos[NYD_CALLS_MAX];
89 static size_t _mem_aall, _mem_acur, _mem_amax,
90 _mem_mall, _mem_mcur, _mem_mmax;
92 static struct mem_chunk *_mem_list, *_mem_free;
93 #endif
95 /* {hold,rele}_all_sigs() */
96 static size_t _alls_depth;
97 static sigset_t _alls_nset, _alls_oset;
99 /* {hold,rele}_sigs() */
100 static size_t _hold_sigdepth;
101 static sigset_t _hold_nset, _hold_oset;
103 /* Create an ISO 6429 (ECMA-48/ANSI) terminal control escape sequence */
104 #ifdef HAVE_COLOUR
105 static char * _colour_iso6429(char const *wish);
106 #endif
108 #ifdef HAVE_DEBUG
109 static void _nyd_print(struct nyd_info *nip);
110 #endif
112 #ifdef HAVE_COLOUR
113 static char *
114 _colour_iso6429(char const *wish)
116 char const * const wish_orig = wish;
117 char *xwish, *cp, cfg[3] = {0, 0, 0};
118 NYD_ENTER;
120 /* Since we use salloc(), reuse the n_strsep() buffer also for the return
121 * value, ensure we have enough room for that */
123 size_t i = strlen(wish) +1;
124 xwish = salloc(MAX(i, sizeof("\033[1;30;40m")));
125 memcpy(xwish, wish, i);
126 wish = xwish;
129 /* Iterate over the colour spec */
130 while ((cp = n_strsep(&xwish, ',', TRU1)) != NULL) {
131 char *y, *x = strchr(cp, '=');
132 if (x == NULL) {
133 jbail:
134 fprintf(stderr, tr(527,
135 "Invalid colour specification \"%s\": >>> %s <<<\n"),
136 wish_orig, cp);
137 continue;
139 *x++ = '\0';
141 /* TODO convert the ft/fg/bg parser into a table-based one! */
142 if (!asccasecmp(cp, "ft")) {
143 if (!asccasecmp(x, "bold"))
144 cfg[0] = '1';
145 else if (!asccasecmp(x, "inverse"))
146 cfg[0] = '7';
147 else if (!asccasecmp(x, "underline"))
148 cfg[0] = '4';
149 else
150 goto jbail;
151 } else if (!asccasecmp(cp, "fg")) {
152 y = cfg + 1;
153 goto jiter_colour;
154 } else if (!asccasecmp(cp, "bg")) {
155 y = cfg + 2;
156 jiter_colour:
157 if (!asccasecmp(x, "black"))
158 *y = '0';
159 else if (!asccasecmp(x, "blue"))
160 *y = '4';
161 else if (!asccasecmp(x, "green"))
162 *y = '2';
163 else if (!asccasecmp(x, "red"))
164 *y = '1';
165 else if (!asccasecmp(x, "brown"))
166 *y = '3';
167 else if (!asccasecmp(x, "magenta"))
168 *y = '5';
169 else if (!asccasecmp(x, "cyan"))
170 *y = '6';
171 else if (!asccasecmp(x, "white"))
172 *y = '7';
173 else
174 goto jbail;
175 } else
176 goto jbail;
179 /* Restore our salloc() buffer, create return value */
180 xwish = UNCONST(wish);
181 if (cfg[0] || cfg[1] || cfg[2]) {
182 xwish[0] = '\033';
183 xwish[1] = '[';
184 xwish += 2;
185 if (cfg[0])
186 *xwish++ = cfg[0];
187 if (cfg[1]) {
188 if (cfg[0])
189 *xwish++ = ';';
190 xwish[0] = '3';
191 xwish[1] = cfg[1];
192 xwish += 2;
194 if (cfg[2]) {
195 if (cfg[0] || cfg[1])
196 *xwish++ = ';';
197 xwish[0] = '4';
198 xwish[1] = cfg[2];
199 xwish += 2;
201 *xwish++ = 'm';
203 *xwish = '\0';
204 NYD_LEAVE;
205 return UNCONST(wish);
207 #endif /* HAVE_COLOUR */
209 #ifdef HAVE_DEBUG
210 static void
211 _nyd_print(struct nyd_info *nip) /* XXX like SFSYS;no magics;jumps:lvl wrong */
213 char buf[80];
214 union {int i; size_t z;} u;
216 u.i = snprintf(buf, sizeof buf, "%c [%2u] %-25.25s %.16s:%-5u\n",
217 "=><"[(nip->ni_chirp_line >> 29) & 0x3], nip->ni_level, nip->ni_fun,
218 nip->ni_file, (nip->ni_chirp_line & 0x1FFFFFFFu));
219 if (u.i > 0) {
220 u.z = u.i;
221 if (u.z > sizeof buf)
222 u.z = sizeof buf - 1; /* (Skip \0) */
223 write(STDERR_FILENO, buf, u.z);
226 #endif
228 FL void
229 panic(char const *format, ...)
231 va_list ap;
232 NYD_ENTER;
234 fprintf(stderr, tr(1, "Panic: "));
236 va_start(ap, format);
237 vfprintf(stderr, format, ap);
238 va_end(ap);
240 fputs("\n", stderr);
241 fflush(stderr);
242 NYD_LEAVE;
243 abort(); /* Was exit(EXIT_ERR); for a while, but no */
246 FL void
247 alert(char const *format, ...)
249 va_list ap;
250 NYD_ENTER;
252 fprintf(stderr, tr(1, "Panic: "));
254 va_start(ap, format);
255 vfprintf(stderr, format, ap);
256 va_end(ap);
258 fputs("\n", stderr);
259 fflush(stderr);
260 NYD_LEAVE;
263 FL sighandler_type
264 safe_signal(int signum, sighandler_type handler)
266 struct sigaction nact, oact;
267 sighandler_type rv;
268 NYD_ENTER;
270 nact.sa_handler = handler;
271 sigemptyset(&nact.sa_mask);
272 nact.sa_flags = 0;
273 #ifdef SA_RESTART
274 nact.sa_flags |= SA_RESTART;
275 #endif
276 rv = (sigaction(signum, &nact, &oact) != 0) ? SIG_ERR : oact.sa_handler;
277 NYD_LEAVE;
278 return rv;
281 FL void
282 hold_all_sigs(void)
284 NYD_ENTER;
285 if (_alls_depth++ == 0) {
286 sigfillset(&_alls_nset);
287 sigdelset(&_alls_nset, SIGABRT);
288 #ifdef SIGBUS
289 sigdelset(&_alls_nset, SIGBUS);
290 #endif
291 sigdelset(&_alls_nset, SIGCHLD);
292 sigdelset(&_alls_nset, SIGFPE);
293 sigdelset(&_alls_nset, SIGILL);
294 sigdelset(&_alls_nset, SIGKILL);
295 sigdelset(&_alls_nset, SIGSEGV);
296 sigdelset(&_alls_nset, SIGSTOP);
297 sigprocmask(SIG_BLOCK, &_alls_nset, &_alls_oset);
299 NYD_LEAVE;
302 FL void
303 rele_all_sigs(void)
305 NYD_ENTER;
306 if (--_alls_depth == 0)
307 sigprocmask(SIG_SETMASK, &_alls_oset, (sigset_t*)NULL);
308 NYD_LEAVE;
311 FL void
312 hold_sigs(void)
314 NYD_ENTER;
315 if (_hold_sigdepth++ == 0) {
316 sigemptyset(&_hold_nset);
317 sigaddset(&_hold_nset, SIGHUP);
318 sigaddset(&_hold_nset, SIGINT);
319 sigaddset(&_hold_nset, SIGQUIT);
320 sigprocmask(SIG_BLOCK, &_hold_nset, &_hold_oset);
322 NYD_LEAVE;
325 FL void
326 rele_sigs(void)
328 NYD_ENTER;
329 if (--_hold_sigdepth == 0)
330 sigprocmask(SIG_SETMASK, &_hold_oset, NULL);
331 NYD_LEAVE;
334 #ifdef HAVE_DEBUG
335 FL void
336 _nyd_chirp(ui8_t act, char const *file, ui32_t line, char const *fun)
338 struct nyd_info *nip = _nyd_infos;
340 if (_nyd_curr != NELEM(_nyd_infos))
341 nip += _nyd_curr++;
342 else
343 _nyd_curr = 1;
344 nip->ni_file = file;
345 nip->ni_fun = fun;
346 nip->ni_chirp_line = ((ui32_t)(act & 0x3) << 29) | (line & 0x1FFFFFFFu);
347 nip->ni_level = ((act == 0) ? _nyd_level
348 : (act == 1) ? ++_nyd_level : _nyd_level--);
351 FL void
352 _nyd_oncrash(int signo)
354 struct sigaction xact;
355 sigset_t xset;
356 struct nyd_info *nip;
357 size_t i;
359 xact.sa_handler = SIG_DFL;
360 sigemptyset(&xact.sa_mask);
361 xact.sa_flags = 0;
362 sigaction(signo, &xact, NULL);
364 fprintf(stderr, "\n\nNYD: program dying due to signal %d:\n", signo);
365 if (_nyd_infos[NELEM(_nyd_infos) - 1].ni_file != NULL)
366 for (i = _nyd_curr, nip = _nyd_infos + i; i < NELEM(_nyd_infos); ++i)
367 _nyd_print(nip++);
368 for (i = 0, nip = _nyd_infos; i < _nyd_curr; ++i)
369 _nyd_print(nip++);
371 sigemptyset(&xset);
372 sigaddset(&xset, signo);
373 sigprocmask(SIG_UNBLOCK, &xset, NULL);
374 kill(0, signo);
375 for (;;)
376 _exit(EXIT_ERR);
378 #endif
380 FL void
381 touch(struct message *mp)
383 NYD_ENTER;
384 mp->m_flag |= MTOUCH;
385 if (!(mp->m_flag & MREAD))
386 mp->m_flag |= MREAD | MSTATUS;
387 NYD_LEAVE;
390 FL bool_t
391 is_dir(char const *name)
393 struct stat sbuf;
394 bool_t rv = FAL0;
395 NYD_ENTER;
397 if (!stat(name, &sbuf))
398 rv = (S_ISDIR(sbuf.st_mode) != 0);
399 NYD_LEAVE;
400 return rv;
403 FL int
404 argcount(char **argv)
406 char **ap;
407 NYD_ENTER;
409 for (ap = argv; *ap++ != NULL;)
411 NYD_LEAVE;
412 return (int)PTR2SIZE(ap - argv - 1);
415 FL int
416 screensize(void)
418 int s;
419 char *cp;
420 NYD_ENTER;
422 if ((cp = ok_vlook(screen)) == NULL || (s = atoi(cp)) <= 0)
423 s = scrnheight - 4; /* XXX no magics */
424 NYD_LEAVE;
425 return s;
428 FL char const *
429 get_pager(void)
431 char const *cp;
432 NYD_ENTER;
434 cp = ok_vlook(PAGER);
435 if (cp == NULL || *cp == '\0')
436 cp = XPAGER;
437 NYD_LEAVE;
438 return cp;
441 FL size_t
442 paging_seems_sensible(void)
444 size_t rv = 0;
445 char const *cp;
446 NYD_ENTER;
448 if (IS_TTY_SESSION() && (cp = ok_vlook(crt)) != NULL)
449 rv = (*cp != '\0') ? (size_t)atol(cp) : (size_t)scrnheight;
450 NYD_LEAVE;
451 return rv;
454 FL void
455 page_or_print(FILE *fp, size_t lines)
457 size_t rows;
458 int c;
459 NYD_ENTER;
461 fflush_rewind(fp);
463 if ((rows = paging_seems_sensible()) != 0 && lines == 0) {
464 while ((c = getc(fp)) != EOF)
465 if (c == '\n' && ++lines > rows)
466 break;
467 really_rewind(fp);
470 if (rows != 0 && lines >= rows)
471 run_command(get_pager(), 0, fileno(fp), -1, NULL, NULL, NULL);
472 else
473 while ((c = getc(fp)) != EOF)
474 putchar(c);
475 NYD_LEAVE;
478 FL enum protocol
479 which_protocol(char const *name) /* XXX (->URL (yet auxlily.c)) */
481 struct stat st;
482 char const *cp;
483 char *np;
484 size_t sz;
485 enum protocol rv = PROTO_UNKNOWN;
486 NYD_ENTER;
488 if (name[0] == '%' && name[1] == ':')
489 name += 2;
490 for (cp = name; *cp && *cp != ':'; cp++)
491 if (!alnumchar(*cp))
492 goto jfile;
494 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
495 if (!strncmp(name, "pop3://", 7)) {
496 #ifdef HAVE_POP3
497 rv = PROTO_POP3;
498 #else
499 fprintf(stderr, tr(216, "No POP3 support compiled in.\n"));
500 #endif
501 } else if (!strncmp(name, "pop3s://", 8)) {
502 #if defined HAVE_POP3 && defined HAVE_SSL
503 rv = PROTO_POP3;
504 #else
505 # ifndef HAVE_POP3
506 fprintf(stderr, tr(216, "No POP3 support compiled in.\n"));
507 # endif
508 # ifndef HAVE_SSL
509 fprintf(stderr, tr(225, "No SSL support compiled in.\n"));
510 # endif
511 #endif
512 } else if (!strncmp(name, "imap://", 7)) {
513 #ifdef HAVE_IMAP
514 rv = PROTO_IMAP;
515 #else
516 fprintf(stderr, tr(269, "No IMAP support compiled in.\n"));
517 #endif
518 } else if (!strncmp(name, "imaps://", 8)) {
519 #if defined HAVE_IMAP && defined HAVE_SSL
520 rv = PROTO_IMAP;
521 #else
522 # ifndef HAVE_IMAP
523 fprintf(stderr, tr(269, "No IMAP support compiled in.\n"));
524 # endif
525 # ifndef HAVE_SSL
526 fprintf(stderr, tr(225, "No SSL support compiled in.\n"));
527 # endif
528 #endif
530 goto jleave;
533 /* TODO This is the de facto maildir code and thus belongs into there!
534 * TODO and: we should have maildir:// and mbox:// pseudo-protos, instead of
535 * TODO or (more likely) in addition to *newfolders*) */
536 jfile:
537 rv = PROTO_FILE;
538 np = ac_alloc((sz = strlen(name)) + 4 +1);
539 memcpy(np, name, sz + 1);
540 if (!stat(name, &st)) {
541 if (S_ISDIR(st.st_mode) &&
542 (memcpy(np+sz, "/tmp", 4), !stat(np, &st) && S_ISDIR(st.st_mode)) &&
543 (memcpy(np+sz, "/new", 4), !stat(np, &st) && S_ISDIR(st.st_mode)) &&
544 (memcpy(np+sz, "/cur", 4), !stat(np, &st) && S_ISDIR(st.st_mode)))
545 rv = PROTO_MAILDIR;
546 } else if ((cp = ok_vlook(newfolders)) != NULL && !strcmp(cp, "maildir"))
547 rv = PROTO_MAILDIR;
548 ac_free(np);
549 jleave:
550 NYD_LEAVE;
551 return rv;
554 FL ui32_t
555 torek_hash(char const *name)
557 /* Chris Torek's hash.
558 * NOTE: need to change *at least* create-okey-map.pl when changing the
559 * algorithm!! */
560 ui32_t h = 0;
561 NYD_ENTER;
563 while (*name != '\0') {
564 h *= 33;
565 h += *name++;
567 NYD_LEAVE;
568 return h;
571 FL unsigned
572 pjw(char const *cp) /* TODO obsolete that -> torek_hash */
574 unsigned h = 0, g;
575 NYD_ENTER;
577 cp--;
578 while (*++cp) {
579 h = (h << 4 & 0xffffffff) + (*cp&0377);
580 if ((g = h & 0xf0000000) != 0) {
581 h = h ^ g >> 24;
582 h = h ^ g;
585 NYD_LEAVE;
586 return h;
589 FL ui32_t
590 nextprime(ui32_t n)
592 static ui32_t const primes[] = {
593 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
594 131071, 262139, 524287, 1048573, 2097143, 4194301,
595 8388593, 16777213, 33554393, 67108859, 134217689,
596 268435399, 536870909, 1073741789, 2147483647
599 ui32_t mprime = 7, cutlim;
600 size_t i;
601 NYD_ENTER;
603 cutlim = (n < 65536 ? n << 2 : (n < 262144 ? n << 1 : n));
605 for (i = 0; i < NELEM(primes); i++)
606 if ((mprime = primes[i]) >= cutlim)
607 break;
608 if (i == NELEM(primes) && mprime < n)
609 mprime = n;
610 NYD_LEAVE;
611 return mprime;
614 FL int
615 expand_shell_escape(char const **s, bool_t use_nail_extensions)
617 char const *xs;
618 int c, n;
619 NYD_ENTER;
621 xs = *s;
623 if ((c = *xs & 0xFF) == '\0')
624 goto jleave;
625 ++xs;
626 if (c != '\\')
627 goto jleave;
629 switch ((c = *xs & 0xFF)) {
630 case '\\': break;
631 case 'a': c = '\a'; break;
632 case 'b': c = '\b'; break;
633 case 'c': c = PROMPT_STOP; break;
634 case 'f': c = '\f'; break;
635 case 'n': c = '\n'; break;
636 case 'r': c = '\r'; break;
637 case 't': c = '\t'; break;
638 case 'v': c = '\v'; break;
639 case '0':
640 for (++xs, c = 0, n = 4; --n > 0 && octalchar(*xs); ++xs) {
641 c <<= 3;
642 c |= *xs - '0';
644 goto jleave;
645 /* S-nail extension for nice (get)prompt(()) support */
646 case '&':
647 case '?':
648 case '$':
649 case '@':
650 if (use_nail_extensions) {
651 switch (c) {
652 case '&': c = ok_blook(bsdcompat) ? '&' : '?'; break;
653 case '?': c = exec_last_comm_error ? '1' : '0'; break;
654 case '$': c = PROMPT_DOLLAR; break;
655 case '@': c = PROMPT_AT; break;
657 break;
659 /* FALLTHRU */
660 case '\0':
661 /* A sole <backslash> at EOS is treated as-is! */
662 /* FALLTHRU */
663 default:
664 c = '\\';
665 goto jleave;
667 ++xs;
668 jleave:
669 *s = xs;
670 NYD_LEAVE;
671 return c;
674 FL char *
675 getprompt(void) /* TODO evaluate only as necessary (needs a bit) */
677 static char buf[PROMPT_BUFFER_SIZE];
679 char *cp;
680 char const *ccp_base, *ccp;
681 size_t NATCH_CHAR( cclen_base COMMA cclen COMMA ) maxlen, dfmaxlen;
682 bool_t run2;
683 NYD_ENTER;
685 cp = buf;
686 if ((ccp_base = ok_vlook(prompt)) == NULL || *ccp_base == '\0')
687 goto jleave;
688 NATCH_CHAR( cclen_base = strlen(ccp_base); )
690 dfmaxlen = 0; /* keep CC happy */
691 run2 = FAL0;
692 jredo:
693 ccp = ccp_base;
694 NATCH_CHAR( cclen = cclen_base; )
695 maxlen = sizeof(buf) -1;
697 for (;;) {
698 size_t l;
699 int c;
701 if (maxlen == 0)
702 goto jleave;
703 #ifdef HAVE_NATCH_CHAR
704 c = mblen(ccp, cclen); /* TODO use mbrtowc() */
705 if (c <= 0) {
706 mblen(NULL, 0);
707 if (c < 0) {
708 *buf = '?';
709 cp = buf + 1;
710 goto jleave;
712 break;
713 } else if ((l = c) > 1) {
714 if (run2) {
715 memcpy(cp, ccp, l);
716 cp += l;
718 ccp += l;
719 maxlen -= l;
720 continue;
721 } else
722 #endif
723 if ((c = expand_shell_escape(&ccp, TRU1)) > 0) {
724 if (run2)
725 *cp++ = (char)c;
726 --maxlen;
727 continue;
729 if (c == 0 || c == PROMPT_STOP)
730 break;
732 if (run2) {
733 char const *a = (c == PROMPT_DOLLAR) ? account_name : displayname;
734 if (a == NULL)
735 a = "";
736 if ((l = field_put_bidi_clip(cp, dfmaxlen, a, strlen(a))) > 0) {
737 cp += l;
738 maxlen -= l;
739 dfmaxlen -= l;
744 if (!run2) {
745 run2 = TRU1;
746 dfmaxlen = maxlen;
747 goto jredo;
749 jleave:
750 *cp = '\0';
751 NYD_LEAVE;
752 return buf;
755 FL char *
756 nodename(int mayoverride)
758 static char *sys_hostname, *hostname; /* XXX free-at-exit */
760 struct utsname ut;
761 char *hn;
762 #ifdef HAVE_SOCKETS
763 # ifdef HAVE_IPV6
764 struct addrinfo hints, *res;
765 # else
766 struct hostent *hent;
767 # endif
768 #endif
769 NYD_ENTER;
771 if (mayoverride && (hn = ok_vlook(hostname)) != NULL && *hn != '\0') {
773 } else if ((hn = sys_hostname) == NULL) {
774 uname(&ut);
775 hn = ut.nodename;
776 #ifdef HAVE_SOCKETS
777 # ifdef HAVE_IPV6
778 memset(&hints, 0, sizeof hints);
779 hints.ai_family = AF_UNSPEC;
780 hints.ai_socktype = SOCK_DGRAM; /* (dummy) */
781 hints.ai_flags = AI_CANONNAME;
782 if (getaddrinfo(hn, "0", &hints, &res) == 0) {
783 if (res->ai_canonname != NULL) {
784 size_t l = strlen(res->ai_canonname) +1;
785 hn = ac_alloc(l);
786 memcpy(hn, res->ai_canonname, l);
788 freeaddrinfo(res);
790 # else
791 hent = gethostbyname(hn);
792 if (hent != NULL)
793 hn = hent->h_name;
794 # endif
795 #endif
796 sys_hostname = sstrdup(hn);
797 #if defined HAVE_SOCKETS && defined HAVE_IPV6
798 if (hn != ut.nodename)
799 ac_free(hn);
800 #endif
801 hn = sys_hostname;
804 if (hostname != NULL && hostname != sys_hostname)
805 free(hostname);
806 hostname = sstrdup(hn);
807 NYD_LEAVE;
808 return hostname;
811 FL bool_t
812 url_parse(struct url *urlp, enum cproto cproto, char const *data)
814 #if defined HAVE_SMTP && defined HAVE_POP3 && defined HAVE_IMAP
815 # define __ALLPROTO
816 #endif
817 #if defined HAVE_SMTP || defined HAVE_POP3 || defined HAVE_IMAP
818 # define __ANYPROTO
819 char *cp, *x;
820 #endif
821 bool_t rv = FAL0;
822 NYD_ENTER;
823 UNUSED(data);
825 memset(urlp, 0, sizeof *urlp);
826 urlp->url_input = data;
827 urlp->url_cproto = cproto;
829 /* Network protocol */
830 #define _protox(X,Y) \
831 urlp->url_portno = Y;\
832 memcpy(urlp->url_proto, X "://", sizeof(X "://"));\
833 urlp->url_proto[sizeof(X) -1] = '\0';\
834 urlp->url_proto_len = sizeof(X) -1;\
835 urlp->url_proto_xlen = sizeof(X "://") -1
836 #define __if(X,Y,Z) \
837 if (!ascncasecmp(data, X "://", sizeof(X "://") -1)) {\
838 _protox(X, Y);\
839 data += sizeof(X "://") -1;\
840 do { Z; } while (0);\
841 goto juser;\
843 #define _if(X,Y) __if(X, Y, (void)0)
844 #ifdef HAVE_SSL
845 # define _ifs(X,Y) __if(X, Y, urlp->url_needs_tls = TRU1)
846 #else
847 # define _ifs(X,Y) goto jeproto;
848 #endif
850 switch (cproto) {
851 case CPROTO_SMTP:
852 #ifdef HAVE_SMTP
853 _if ("smtp", 25)
854 _if ("submission", 587)
855 _ifs ("smtps", 465)
856 _protox("smtp", 25);
857 break;
858 #else
859 goto jeproto;
860 #endif
861 case CPROTO_POP3:
862 #ifdef HAVE_POP3
863 _if ("pop3", 110)
864 _ifs ("pop3s", 995)
865 _protox("pop3", 110);
866 break;
867 #else
868 goto jeproto;
869 #endif
870 case CPROTO_IMAP:
871 #ifdef HAVE_IMAP
872 _if ("imap", 143)
873 _ifs ("imaps", 993)
874 _protox("imap", 143);
875 break;
876 #else
877 goto jeproto;
878 #endif
881 #undef _ifs
882 #undef _if
883 #undef __if
884 #undef _protox
886 if (strstr(data, "://") != NULL) {
887 #if !defined __ALLPROTO || !defined HAVE_SSL
888 jeproto:
889 #endif
890 fprintf(stderr, tr(574, "URL `proto://' prefix invalid: `%s'\n"),
891 urlp->url_input);
892 goto jleave;
894 #ifdef __ANYPROTO
896 /* User and password, I */
897 juser:
898 if ((cp = UNCONST(last_at_before_slash(data))) == NULL)
899 goto jserver;
901 urlp->url_had_user = TRU1;
902 urlp->url_user.s = savestrbuf(data, urlp->url_user.l = PTR2SIZE(cp - data));
903 data = ++cp;
905 /* And also have a password? */
906 if ((cp = strchr(urlp->url_user.s, ':')) != NULL) {
907 x = urlp->url_user.s;
908 urlp->url_user.s = savestrbuf(x, urlp->url_user.l = PTR2SIZE(cp - x));
909 urlp->url_pass.l = strlen(urlp->url_pass.s = urlxdec(++cp));
910 urlp->url_pass_enc.l = strlen(
911 urlp->url_pass_enc.s = urlxenc(urlp->url_pass.s));
914 /* Servername and port -- and possible path suffix */
915 jserver:
916 if ((cp = strchr(data, ':')) != NULL) { /* TODO URL parse, IPv6 support */
917 char *eptr;
918 long l;
920 urlp->url_port = x = savestr(x = cp + 1);
921 if ((x = strchr(x, '/')) != NULL)
922 *x = '\0';
923 l = strtol(urlp->url_port, &eptr, 10);
924 if (*eptr != '\0' || l <= 0 || UICMP(32, l, >=, 0xFFFFu)) {
925 fprintf(stderr, tr(573, "URL with invalid port number: `%s'\n"),
926 urlp->url_input);
927 goto jleave;
929 urlp->url_portno = (ui16_t)l;
930 } else {
931 if ((x = strchr(data, '/')) != NULL)
932 data = savestrbuf(data, PTR2SIZE(x - data));
933 cp = UNCONST(data + strlen(data));
936 /* A (non-empty) path may only occur with IMAP */
937 if (x != NULL && x[1] != '\0') {
938 if (cproto != CPROTO_IMAP) {
939 fprintf(stderr, tr(575, "URL protocol doesn't support paths: `%s'\n"),
940 urlp->url_input);
941 goto jleave;
943 urlp->url_path.l = strlen(++x);
944 urlp->url_path.s = savestrbuf(x, urlp->url_path.l);
947 urlp->url_host.s = savestrbuf(data, urlp->url_host.l = PTR2SIZE(cp - data));
948 { size_t i;
949 for (cp = urlp->url_host.s, i = urlp->url_host.l; i != 0; ++cp, --i)
950 *cp = lowerconv(*cp);
953 /* HOST:PORT */
954 { size_t i;
955 struct str *s = &urlp->url_hp;
957 s->s = salloc(urlp->url_host.l + 1 + sizeof("65536")-1 +1);
958 memcpy(s->s, urlp->url_host.s, i = urlp->url_host.l);
959 if (urlp->url_port != NULL) {
960 size_t j = strlen(urlp->url_port);
961 s->s[i++] = ':';
962 memcpy(s->s + i, urlp->url_port, j);
963 i += j;
965 s->s[i] = '\0';
966 s->l = i;
969 /* User, II
970 * If there was no user in the URL, do we have *user-HOST* or *user*? */
971 if (!urlp->url_had_user) {
972 char const usrstr[] = "user-";
973 size_t const usrlen = sizeof(usrstr) -1;
975 cp = ac_alloc(usrlen + urlp->url_hp.l +1);
976 memcpy(cp, usrstr, usrlen);
977 memcpy(cp + usrlen, urlp->url_hp.s, urlp->url_hp.l +1);
978 if ((urlp->url_user.s = vok_vlook(cp)) == NULL) {
979 cp[usrlen - 1] = '\0';
980 if ((urlp->url_user.s = vok_vlook(cp)) == NULL)
981 urlp->url_user.s = UNCONST(myname);
983 ac_free(cp);
986 urlp->url_user.l = strlen(urlp->url_user.s = urlxdec(urlp->url_user.s));
987 urlp->url_user_enc.l = strlen(
988 urlp->url_user_enc.s = urlxenc(urlp->url_user.s));
990 /* USER@HOST:PORT */
991 if (urlp->url_user_enc.l == 0)
992 urlp->url_uhp = urlp->url_hp;
993 else {
994 struct str *s = &urlp->url_uhp;
995 size_t i = urlp->url_user_enc.l;
997 s->s = salloc(i + 1 + urlp->url_hp.l +1);
998 if (i > 0) {
999 memcpy(s->s, urlp->url_user_enc.s, i);
1000 s->s[i++] = '@';
1002 memcpy(s->s + i, urlp->url_hp.s, urlp->url_hp.l +1);
1003 i += urlp->url_hp.l;
1004 s->l = i;
1007 /* USER@HOST
1008 * For SMTP we apply ridiculously complicated *v15-compat* plus
1009 * *smtp-hostname* / *hostname* dependent rules */
1010 { struct str h;
1012 if (cproto == CPROTO_SMTP && ok_blook(v15_compat) &&
1013 (cp = ok_vlook(smtp_hostname)) != NULL) {
1014 if (*cp == '\0')
1015 cp = nodename(1);
1016 h.s = savestrbuf(cp, h.l = strlen(cp));
1017 } else
1018 h = urlp->url_host;
1020 if (urlp->url_user_enc.l == 0)
1021 urlp->url_uh = h;
1022 else {
1023 struct str *s = &urlp->url_uh;
1024 size_t i = urlp->url_user_enc.l;
1026 s->s = salloc(i + 1 + h.l +1);
1027 if (i > 0) {
1028 memcpy(s->s, urlp->url_user_enc.s, i);
1029 s->s[i++] = '@';
1031 memcpy(s->s + i, h.s, h.l +1);
1032 i += h.l;
1033 s->l = i;
1037 /* Finally, for fun: .url_proto://.url_uhp[/.url_path] */
1038 { size_t i;
1039 char *ud = salloc((i = urlp->url_proto_xlen + urlp->url_uhp.l) +
1040 1 + urlp->url_path.l +1);
1042 urlp->url_proto[urlp->url_proto_len] = ':';
1043 memcpy(sstpcpy(ud, urlp->url_proto), urlp->url_uhp.s, urlp->url_uhp.l +1);
1044 urlp->url_proto[urlp->url_proto_len] = '\0';
1046 if (urlp->url_path.l == 0)
1047 urlp->url_puhp = urlp->url_puhpp = ud;
1048 else {
1049 urlp->url_puhp = savestrbuf(ud, i);
1050 urlp->url_puhpp = ud;
1051 ud += i;
1052 *ud++ = '/';
1053 memcpy(ud, urlp->url_path.s, urlp->url_path.l +1);
1057 rv = TRU1;
1058 #endif /* __ANYPROTO */
1059 jleave:
1060 NYD_LEAVE;
1061 return rv;
1062 #undef __ANYPROTO
1063 #undef __ALLPROTO
1066 FL bool_t
1067 ccred_lookup_old(struct ccred *ccp, enum cproto cproto, char const *addr)
1069 char const *pname, *pxstr, *authdef;
1070 size_t pxlen, addrlen, i;
1071 char *vbuf, *s;
1072 ui8_t authmask;
1073 enum {NONE=0, WANT_PASS=1<<0, REQ_PASS=1<<1, WANT_USER=1<<2, REQ_USER=1<<3}
1074 ware = NONE;
1075 bool_t addr_is_nuser = FAL0; /* XXX v15.0 legacy! v15_compat */
1076 NYD_ENTER;
1078 memset(ccp, 0, sizeof *ccp);
1080 switch (cproto) {
1081 default:
1082 case CPROTO_SMTP:
1083 pname = "SMTP";
1084 pxstr = "smtp-auth";
1085 pxlen = sizeof("smtp-auth") -1;
1086 authmask = AUTHTYPE_NONE | AUTHTYPE_PLAIN | AUTHTYPE_LOGIN |
1087 AUTHTYPE_CRAM_MD5 | AUTHTYPE_GSSAPI;
1088 authdef = "none";
1089 addr_is_nuser = TRU1;
1090 break;
1091 case CPROTO_POP3:
1092 pname = "POP3";
1093 pxstr = "pop3-auth";
1094 pxlen = sizeof("pop3-auth") -1;
1095 authmask = AUTHTYPE_PLAIN;
1096 authdef = "plain";
1097 break;
1098 case CPROTO_IMAP:
1099 pname = "IMAP";
1100 pxstr = "imap-auth";
1101 pxlen = sizeof("imap-auth") -1;
1102 authmask = AUTHTYPE_LOGIN | AUTHTYPE_CRAM_MD5 | AUTHTYPE_GSSAPI;
1103 authdef = "login";
1104 break;
1107 ccp->cc_cproto = cproto;
1108 addrlen = strlen(addr);
1109 vbuf = ac_alloc(pxlen + addrlen + sizeof("-password-")-1 +1);
1110 memcpy(vbuf, pxstr, pxlen);
1112 /* Authentication type */
1113 vbuf[pxlen] = '-';
1114 memcpy(vbuf + pxlen + 1, addr, addrlen +1);
1115 if ((s = vok_vlook(vbuf)) == NULL) {
1116 vbuf[pxlen] = '\0';
1117 if ((s = vok_vlook(vbuf)) == NULL)
1118 s = UNCONST(authdef);
1121 if (!asccasecmp(s, "none")) {
1122 ccp->cc_auth = "NONE";
1123 ccp->cc_authtype = AUTHTYPE_NONE;
1124 /*ware = NONE;*/
1125 } else if (!asccasecmp(s, "plain")) {
1126 ccp->cc_auth = "PLAIN";
1127 ccp->cc_authtype = AUTHTYPE_PLAIN;
1128 ware = REQ_PASS | REQ_USER;
1129 } else if (!asccasecmp(s, "login")) {
1130 ccp->cc_auth = "LOGIN";
1131 ccp->cc_authtype = AUTHTYPE_LOGIN;
1132 ware = REQ_PASS | REQ_USER;
1133 } else if (!asccasecmp(s, "cram-md5")) {
1134 ccp->cc_auth = "CRAM-MD5";
1135 ccp->cc_authtype = AUTHTYPE_CRAM_MD5;
1136 ware = REQ_PASS | REQ_USER;
1137 } else if (!asccasecmp(s, "gssapi")) {
1138 ccp->cc_auth = "GSS-API";
1139 ccp->cc_authtype = AUTHTYPE_GSSAPI;
1140 ware = REQ_USER;
1141 #if 0 /* TODO SASL (homebrew `auto'matic indeed) */
1142 } else if (!asccasecmp(cp, "sasl")) {
1143 ware = REQ_USER | WANT_PASS;
1144 #endif
1147 /* Verify method */
1148 if (!(ccp->cc_authtype & authmask)) {
1149 fprintf(stderr, tr(273, "Unsupported %s authentication method: %s\n"),
1150 pname, s);
1151 ccp = NULL;
1152 goto jleave;
1154 #ifndef HAVE_MD5
1155 if (ccp->cc_authtype == AUTHTYPE_CRAM_MD5) {
1156 fprintf(stderr, tr(277, "No CRAM-MD5 support compiled in.\n"));
1157 ccp = NULL;
1158 goto jleave;
1160 #endif
1161 #ifndef HAVE_GSSAPI
1162 if (ccp->cc_authtype == AUTHTYPE_GSSAPI) {
1163 fprintf(stderr, tr(272, "No GSS-API support compiled in.\n"));
1164 ccp = NULL;
1165 goto jleave;
1167 #endif
1169 /* User name */
1170 if (!(ware & (WANT_USER | REQ_USER)))
1171 goto jpass;
1173 if (!addr_is_nuser) {
1174 if ((s = UNCONST(last_at_before_slash(addr))) != NULL) {
1175 ccp->cc_user.s = urlxdec(savestrbuf(addr, PTR2SIZE(s - addr)));
1176 ccp->cc_user.l = strlen(ccp->cc_user.s);
1177 } else if (ware & REQ_USER)
1178 goto jgetuser;
1179 goto jpass;
1182 memcpy(vbuf + pxlen, "-user-", i = sizeof("-user-") -1);
1183 i += pxlen;
1184 memcpy(vbuf + i, addr, addrlen +1);
1185 if ((s = vok_vlook(vbuf)) == NULL) {
1186 vbuf[--i] = '\0';
1187 if ((s = vok_vlook(vbuf)) == NULL && (ware & REQ_USER)) {
1188 if ((s = getuser(NULL)) != NULL)
1189 s = savestr(s);
1190 else {
1191 jgetuser: /* TODO v15.0: today we simply bail, but we should call getuser().
1192 * TODO even better: introduce `PROTO-user' and `PROTO-pass' and
1193 * TODO check that first, then! change control flow, grow `vbuf' */
1194 fprintf(stderr, tr(274,
1195 "A user is necessary for %s authentication.\n"), pname);
1196 ccp = NULL;
1197 goto jleave;
1201 ccp->cc_user.l = strlen(ccp->cc_user.s = s);
1203 /* Password */
1204 jpass:
1205 if (!(ware & (WANT_PASS | REQ_PASS)))
1206 goto jleave;
1208 if (!addr_is_nuser) {
1209 memcpy(vbuf, "password-", i = sizeof("password-") -1);
1210 } else {
1211 memcpy(vbuf + pxlen, "-password-", i = sizeof("-password-") -1);
1212 i += pxlen;
1214 memcpy(vbuf + i, addr, addrlen +1);
1215 if ((s = vok_vlook(vbuf)) == NULL) {
1216 vbuf[--i] = '\0';
1217 if ((!addr_is_nuser || (s = vok_vlook(vbuf)) == NULL) &&
1218 (ware & REQ_PASS)) {
1219 if ((s = getpassword(NULL)) == NULL) {
1220 fprintf(stderr, tr(275,
1221 "A password is necessary for %s authentication.\n"), pname);
1222 ccp = NULL;
1223 goto jleave;
1227 ccp->cc_pass.l = strlen(ccp->cc_pass.s = urlxdec(s));
1229 jleave:
1230 ac_free(vbuf);
1231 NYD_LEAVE;
1232 return (ccp != NULL);
1235 FL bool_t
1236 ccred_lookup(struct ccred *ccp, struct url *urlp)
1238 char const *pstr, *authdef;
1239 size_t plen, i;
1240 char *vbuf, *s;
1241 ui8_t authmask;
1242 enum {NONE=0, WANT_PASS=1<<0, REQ_PASS=1<<1, WANT_USER=1<<2, REQ_USER=1<<3}
1243 ware = NONE;
1244 NYD_ENTER;
1246 memset(ccp, 0, sizeof *ccp);
1247 ccp->cc_user = urlp->url_user;
1249 switch ((ccp->cc_cproto = urlp->url_cproto)) {
1250 default:
1251 case CPROTO_SMTP:
1252 pstr = "smtp";
1253 plen = sizeof("smtp") -1;
1254 authmask = AUTHTYPE_NONE | AUTHTYPE_PLAIN | AUTHTYPE_LOGIN |
1255 AUTHTYPE_CRAM_MD5 | AUTHTYPE_GSSAPI;
1256 authdef = "none";
1257 break;
1258 case CPROTO_POP3:
1259 pstr = "pop3";
1260 plen = sizeof("pop3") -1;
1261 authmask = AUTHTYPE_PLAIN;
1262 authdef = "plain";
1263 break;
1264 case CPROTO_IMAP:
1265 pstr = "imap";
1266 plen = sizeof("imap") -1;
1267 authmask = AUTHTYPE_LOGIN | AUTHTYPE_CRAM_MD5 | AUTHTYPE_GSSAPI;
1268 authdef = "login";
1269 break;
1272 /* Note: "password-" is longer than "-auth-", ditto .url_uhp and .url_hp */
1273 vbuf = ac_alloc(plen + sizeof("password-")-1 + urlp->url_uhp.l +1);
1274 memcpy(vbuf, pstr, plen);
1276 /* Authentication type */
1277 memcpy(vbuf + plen, "-auth-", i = sizeof("-auth-") -1);
1278 i += plen;
1279 /* -USER@HOST, -HOST, '' */
1280 memcpy(vbuf + i, urlp->url_uhp.s, urlp->url_uhp.l +1);
1281 if ((s = vok_vlook(vbuf)) == NULL) {
1282 memcpy(vbuf + i, urlp->url_hp.s, urlp->url_hp.l +1);
1283 if ((s = vok_vlook(vbuf)) == NULL) {
1284 vbuf[plen + sizeof("-auth") -1] = '\0';
1285 if ((s = vok_vlook(vbuf)) == NULL)
1286 s = UNCONST(authdef);
1290 if (!asccasecmp(s, "none")) {
1291 ccp->cc_auth = "NONE";
1292 ccp->cc_authtype = AUTHTYPE_NONE;
1293 /*ware = NONE;*/
1294 } else if (!asccasecmp(s, "plain")) {
1295 ccp->cc_auth = "PLAIN";
1296 ccp->cc_authtype = AUTHTYPE_PLAIN;
1297 ware = REQ_PASS | REQ_USER;
1298 } else if (!asccasecmp(s, "login")) {
1299 ccp->cc_auth = "LOGIN";
1300 ccp->cc_authtype = AUTHTYPE_LOGIN;
1301 ware = REQ_PASS | REQ_USER;
1302 } else if (!asccasecmp(s, "cram-md5")) {
1303 ccp->cc_auth = "CRAM-MD5";
1304 ccp->cc_authtype = AUTHTYPE_CRAM_MD5;
1305 ware = REQ_PASS | REQ_USER;
1306 } else if (!asccasecmp(s, "gssapi")) {
1307 ccp->cc_auth = "GSS-API";
1308 ccp->cc_authtype = AUTHTYPE_GSSAPI;
1309 ware = REQ_USER;
1310 #if 0 /* TODO SASL (homebrew `auto'matic indeed) */
1311 } else if (!asccasecmp(cp, "sasl")) {
1312 ware = REQ_USER | WANT_PASS;
1313 #endif
1316 /* Verify method */
1317 if (!(ccp->cc_authtype & authmask)) {
1318 fprintf(stderr, tr(273, "Unsupported %s authentication method: %s\n"),
1319 pstr, s);
1320 ccp = NULL;
1321 goto jleave;
1323 #ifndef HAVE_MD5
1324 if (ccp->cc_authtype == AUTHTYPE_CRAM_MD5) {
1325 fprintf(stderr, tr(277, "No CRAM-MD5 support compiled in.\n"));
1326 ccp = NULL;
1327 goto jleave;
1329 #endif
1330 #ifndef HAVE_GSSAPI
1331 if (ccp->cc_authtype == AUTHTYPE_GSSAPI) {
1332 fprintf(stderr, tr(272, "No GSS-API support compiled in.\n"));
1333 ccp = NULL;
1334 goto jleave;
1336 #endif
1338 /* Password */
1339 if ((ccp->cc_pass = urlp->url_pass).s != NULL)
1340 goto jleave;
1342 memcpy(vbuf, "password-", i = sizeof("password-") -1);
1343 /* -USER@HOST, -HOST, '' */
1344 memcpy(vbuf + i, urlp->url_uhp.s, urlp->url_uhp.l +1);
1345 if ((s = vok_vlook(vbuf)) == NULL) {
1346 memcpy(vbuf + i, urlp->url_hp.s, urlp->url_hp.l +1);
1347 if ((s = vok_vlook(vbuf)) == NULL) {
1348 vbuf[--i] = '\0';
1349 if ((s = vok_vlook(vbuf)) == NULL && (ware & REQ_PASS)) {
1350 if ((s = getpassword(NULL)) != NULL)
1351 s = savestr(s);
1352 else {
1353 fprintf(stderr, tr(275,
1354 "A password is necessary for %s authentication.\n"), pstr);
1355 ccp = NULL;
1356 goto jleave;
1361 ccp->cc_pass.l = strlen(ccp->cc_pass.s = urlxdec(s));
1363 jleave:
1364 ac_free(vbuf);
1365 NYD_LEAVE;
1366 return (ccp != NULL);
1369 FL char *
1370 getrandstring(size_t length)
1372 static unsigned char nodedigest[16];
1373 static pid_t pid;
1375 struct str b64;
1376 char *data, *cp;
1377 size_t i;
1378 int fd = -1;
1379 #ifdef HAVE_MD5
1380 md5_ctx ctx;
1381 #else
1382 size_t j;
1383 #endif
1384 NYD_ENTER;
1386 data = ac_alloc(length);
1388 if ((fd = open("/dev/urandom", O_RDONLY)) == -1 ||
1389 length != (size_t)read(fd, data, length)) {
1390 if (pid == 0) {
1391 pid = getpid();
1392 srand(pid);
1393 cp = nodename(0);
1394 #ifdef HAVE_MD5
1395 md5_init(&ctx);
1396 md5_update(&ctx, (unsigned char*)cp, strlen(cp));
1397 md5_final(nodedigest, &ctx);
1398 #else
1399 /* In that case it's only used for boundaries and Message-Id:s so that
1400 * srand(3) should suffice */
1401 j = strlen(cp) + 1;
1402 for (i = 0; i < sizeof(nodedigest); ++i)
1403 nodedigest[i] = (unsigned char)(cp[i % j] ^ rand());
1404 #endif
1406 for (i = 0; i < length; i++)
1407 data[i] = (char)((int)(255 * (rand() / (RAND_MAX + 1.0))) ^
1408 nodedigest[i % sizeof nodedigest]);
1410 if (fd >= 0)
1411 close(fd);
1413 b64_encode_buf(&b64, data, length, B64_SALLOC);
1414 ac_free(data);
1415 assert(length < b64.l);
1416 b64.s[length] = '\0';
1417 NYD_LEAVE;
1418 return b64.s;
1421 #ifdef HAVE_MD5
1422 FL char *
1423 md5tohex(char hex[MD5TOHEX_SIZE], void const *vp)
1425 char const *cp = vp;
1426 size_t i, j;
1427 NYD_ENTER;
1429 for (i = 0; i < MD5TOHEX_SIZE / 2; i++) {
1430 j = i << 1;
1431 hex[j] = hexchar((cp[i] & 0xf0) >> 4);
1432 hex[++j] = hexchar(cp[i] & 0x0f);
1434 NYD_LEAVE;
1435 return hex;
1438 FL char *
1439 cram_md5_string(char const *user, char const *pass, char const *b64)
1441 struct str in, out;
1442 char digest[16], *cp;
1443 size_t lu;
1444 NYD_ENTER;
1446 out.s = NULL;
1447 in.s = UNCONST(b64);
1448 in.l = strlen(in.s);
1449 b64_decode(&out, &in, NULL);
1450 assert(out.s != NULL);
1452 hmac_md5((unsigned char*)out.s, out.l, UNCONST(pass), strlen(pass), digest);
1453 free(out.s);
1454 cp = md5tohex(salloc(MD5TOHEX_SIZE +1), digest);
1456 lu = strlen(user);
1457 in.l = lu + MD5TOHEX_SIZE +1;
1458 in.s = ac_alloc(lu + 1 + MD5TOHEX_SIZE +1);
1459 memcpy(in.s, user, lu);
1460 in.s[lu++] = ' ';
1461 memcpy(in.s + lu, cp, MD5TOHEX_SIZE);
1462 b64_encode(&out, &in, B64_SALLOC | B64_CRLF);
1463 ac_free(in.s);
1464 NYD_LEAVE;
1465 return out.s;
1468 FL void
1469 hmac_md5(unsigned char *text, int text_len, unsigned char *key, int key_len,
1470 void *digest)
1473 * This code is taken from
1475 * Network Working Group H. Krawczyk
1476 * Request for Comments: 2104 IBM
1477 * Category: Informational M. Bellare
1478 * UCSD
1479 * R. Canetti
1480 * IBM
1481 * February 1997
1484 * HMAC: Keyed-Hashing for Message Authentication
1486 md5_ctx context;
1487 unsigned char k_ipad[65]; /* inner padding - key XORd with ipad */
1488 unsigned char k_opad[65]; /* outer padding - key XORd with opad */
1489 unsigned char tk[16];
1490 int i;
1491 NYD_ENTER;
1493 /* if key is longer than 64 bytes reset it to key=MD5(key) */
1494 if (key_len > 64) {
1495 md5_ctx tctx;
1497 md5_init(&tctx);
1498 md5_update(&tctx, key, key_len);
1499 md5_final(tk, &tctx);
1501 key = tk;
1502 key_len = 16;
1505 /* the HMAC_MD5 transform looks like:
1507 * MD5(K XOR opad, MD5(K XOR ipad, text))
1509 * where K is an n byte key
1510 * ipad is the byte 0x36 repeated 64 times
1511 * opad is the byte 0x5c repeated 64 times
1512 * and text is the data being protected */
1514 /* start out by storing key in pads */
1515 memset(k_ipad, 0, sizeof k_ipad);
1516 memset(k_opad, 0, sizeof k_opad);
1517 memcpy(k_ipad, key, key_len);
1518 memcpy(k_opad, key, key_len);
1520 /* XOR key with ipad and opad values */
1521 for (i=0; i<64; i++) {
1522 k_ipad[i] ^= 0x36;
1523 k_opad[i] ^= 0x5c;
1526 /* perform inner MD5 */
1527 md5_init(&context); /* init context for 1st pass */
1528 md5_update(&context, k_ipad, 64); /* start with inner pad */
1529 md5_update(&context, text, text_len); /* then text of datagram */
1530 md5_final(digest, &context); /* finish up 1st pass */
1532 /* perform outer MD5 */
1533 md5_init(&context); /* init context for 2nd pass */
1534 md5_update(&context, k_opad, 64); /* start with outer pad */
1535 md5_update(&context, digest, 16); /* then results of 1st hash */
1536 md5_final(digest, &context); /* finish up 2nd pass */
1537 NYD_LEAVE;
1539 #endif /* HAVE_MD5 */
1541 FL enum okay
1542 makedir(char const *name)
1544 struct stat st;
1545 enum okay rv = STOP;
1546 NYD_ENTER;
1548 if (!mkdir(name, 0700))
1549 rv = OKAY;
1550 else {
1551 int e = errno;
1552 if ((e == EEXIST || e == ENOSYS) && !stat(name, &st) &&
1553 S_ISDIR(st.st_mode))
1554 rv = OKAY;
1556 NYD_LEAVE;
1557 return rv;
1560 #ifdef HAVE_FCHDIR
1561 FL enum okay
1562 cwget(struct cw *cw)
1564 enum okay rv = STOP;
1565 NYD_ENTER;
1567 if ((cw->cw_fd = open(".", O_RDONLY)) == -1)
1568 goto jleave;
1569 if (fchdir(cw->cw_fd) == -1) {
1570 close(cw->cw_fd);
1571 goto jleave;
1573 rv = OKAY;
1574 jleave:
1575 NYD_LEAVE;
1576 return rv;
1579 FL enum okay
1580 cwret(struct cw *cw)
1582 enum okay rv = STOP;
1583 NYD_ENTER;
1585 if (!fchdir(cw->cw_fd))
1586 rv = OKAY;
1587 NYD_LEAVE;
1588 return rv;
1591 FL void
1592 cwrelse(struct cw *cw)
1594 NYD_ENTER;
1595 close(cw->cw_fd);
1596 NYD_LEAVE;
1599 #else /* !HAVE_FCHDIR */
1600 FL enum okay
1601 cwget(struct cw *cw)
1603 enum okay rv = STOP;
1604 NYD_ENTER;
1606 if (getcwd(cw->cw_wd, sizeof cw->cw_wd) != NULL && !chdir(cw->cw_wd))
1607 rv = OKAY;
1608 NYD_LEAVE;
1609 return rv;
1612 FL enum okay
1613 cwret(struct cw *cw)
1615 enum okay rv = STOP;
1616 NYD_ENTER;
1618 if (!chdir(cw->cw_wd))
1619 rv = OKAY;
1620 NYD_LEAVE;
1621 return rv;
1624 FL void
1625 cwrelse(struct cw *cw)
1627 NYD_ENTER;
1628 UNUSED(cw);
1629 NYD_LEAVE;
1631 #endif /* !HAVE_FCHDIR */
1633 FL size_t
1634 field_detect_clip(size_t maxlen, char const *buf, size_t blen)/*TODO mbrtowc()*/
1636 size_t rv;
1637 NYD_ENTER;
1639 #ifdef HAVE_NATCH_CHAR
1640 maxlen = MIN(maxlen, blen);
1641 for (rv = 0; maxlen > 0;) {
1642 int ml = mblen(buf, maxlen);
1643 if (ml <= 0) {
1644 mblen(NULL, 0);
1645 break;
1647 buf += ml;
1648 rv += ml;
1649 maxlen -= ml;
1651 #else
1652 rv = MIN(blen, maxlen);
1653 #endif
1654 NYD_LEAVE;
1655 return rv;
1658 FL size_t
1659 field_put_bidi_clip(char *store, size_t maxlen, char const *buf, size_t blen)
1661 NATCH_CHAR( struct bidi_info bi; )
1662 size_t rv NATCH_CHAR( COMMA i );
1663 NYD_ENTER;
1665 rv = 0;
1666 if (maxlen-- == 0)
1667 goto j_leave;
1669 #ifdef HAVE_NATCH_CHAR
1670 bidi_info_create(&bi);
1671 if (bi.bi_start.l == 0 || !bidi_info_needed(buf, blen)) {
1672 bi.bi_end.l = 0;
1673 goto jnobidi;
1676 if (maxlen >= (i = bi.bi_pad + bi.bi_end.l + bi.bi_start.l))
1677 maxlen -= i;
1678 else
1679 goto jleave;
1681 if ((i = bi.bi_start.l) > 0) {
1682 memcpy(store, bi.bi_start.s, i);
1683 store += i;
1684 rv += i;
1687 jnobidi:
1688 while (maxlen > 0) {
1689 int ml = mblen(buf, blen);
1690 if (ml <= 0) {
1691 mblen(NULL, 0);
1692 break;
1694 if (UICMP(z, maxlen, <, ml))
1695 break;
1696 if (ml == 1)
1697 *store = *buf;
1698 else
1699 memcpy(store, buf, ml);
1700 store += ml;
1701 buf += ml;
1702 rv += ml;
1703 maxlen -= ml;
1706 if ((i = bi.bi_end.l) > 0) {
1707 memcpy(store, bi.bi_end.s, i);
1708 store += i;
1709 rv += i;
1711 jleave:
1712 *store = '\0';
1714 #else
1715 rv = MIN(blen, maxlen);
1716 memcpy(store, buf, rv);
1717 store[rv] = '\0';
1718 #endif
1719 j_leave:
1720 NYD_LEAVE;
1721 return rv;
1724 FL char *
1725 colalign(char const *cp, int col, int fill, int *cols_decr_used_or_null)
1727 NATCH_CHAR( struct bidi_info bi; )
1728 int col_orig = col, n, sz;
1729 bool_t isbidi, isuni, isrepl;
1730 char *nb, *np;
1731 NYD_ENTER;
1733 /* Bidi only on request and when there is 8-bit data */
1734 isbidi = isuni = FAL0;
1735 #ifdef HAVE_NATCH_CHAR
1736 isuni = ((options & OPT_UNICODE) != 0);
1737 bidi_info_create(&bi);
1738 if (bi.bi_start.l == 0)
1739 goto jnobidi;
1740 if (!(isbidi = bidi_info_needed(cp, strlen(cp))))
1741 goto jnobidi;
1743 if ((size_t)col >= bi.bi_pad)
1744 col -= bi.bi_pad;
1745 else
1746 col = 0;
1747 jnobidi:
1748 #endif
1750 np = nb = salloc(mb_cur_max * strlen(cp) +
1751 ((fill ? col : 0)
1752 NATCH_CHAR( + (isbidi ? bi.bi_start.l + bi.bi_end.l : 0) )
1753 +1));
1755 #ifdef HAVE_NATCH_CHAR
1756 if (isbidi) {
1757 memcpy(np, bi.bi_start.s, bi.bi_start.l);
1758 np += bi.bi_start.l;
1760 #endif
1762 while (*cp != '\0') {
1763 #ifdef HAVE_C90AMEND1
1764 if (mb_cur_max > 1) {
1765 wchar_t wc;
1767 n = 1;
1768 isrepl = TRU1;
1769 if ((sz = mbtowc(&wc, cp, mb_cur_max)) == -1)
1770 sz = 1;
1771 else if (iswprint(wc)) {
1772 # ifndef HAVE_WCWIDTH
1773 n = 1 + (wc >= 0x1100u); /* TODO use S-CText isfullwidth() */
1774 # else
1775 if ((n = wcwidth(wc)) == -1)
1776 n = 1;
1777 else
1778 # endif
1779 isrepl = FAL0;
1781 } else
1782 #endif
1783 n = sz = 1,
1784 isrepl = !isprint(*cp);
1786 if (n > col)
1787 break;
1788 col -= n;
1790 if (isrepl) {
1791 if (isuni) {
1792 np[0] = (char)0xEFu;
1793 np[1] = (char)0xBFu;
1794 np[2] = (char)0xBDu;
1795 np += 3;
1796 } else
1797 *np++ = '?';
1798 cp += sz;
1799 } else if (sz == 1 && spacechar(*cp)) {
1800 *np++ = ' ';
1801 ++cp;
1802 } else
1803 while (sz--)
1804 *np++ = *cp++;
1807 if (fill && col != 0) {
1808 if (fill > 0) {
1809 memmove(nb + col, nb, PTR2SIZE(np - nb));
1810 memset(nb, ' ', col);
1811 } else
1812 memset(np, ' ', col);
1813 np += col;
1814 col = 0;
1817 #ifdef HAVE_NATCH_CHAR
1818 if (isbidi) {
1819 memcpy(np, bi.bi_end.s, bi.bi_end.l);
1820 np += bi.bi_end.l;
1822 #endif
1824 *np = '\0';
1825 if (cols_decr_used_or_null != NULL)
1826 *cols_decr_used_or_null -= col_orig - col;
1827 NYD_LEAVE;
1828 return nb;
1831 FL void
1832 makeprint(struct str const *in, struct str *out)
1834 static int print_all_chars = -1;
1836 char const *inp, *maxp;
1837 char *outp;
1838 DBG( size_t msz; )
1839 NYD_ENTER;
1841 if (print_all_chars == -1)
1842 print_all_chars = ok_blook(print_all_chars);
1844 out->s = outp = smalloc(DBG( msz = ) in->l*mb_cur_max + 2u*mb_cur_max);
1845 inp = in->s;
1846 maxp = inp + in->l;
1848 if (print_all_chars) {
1849 out->l = in->l;
1850 memcpy(outp, inp, out->l);
1851 goto jleave;
1854 #ifdef HAVE_NATCH_CHAR
1855 if (mb_cur_max > 1) {
1856 char mbb[MB_LEN_MAX + 1];
1857 wchar_t wc;
1858 int i, n;
1859 bool_t isuni = ((options & OPT_UNICODE) != 0);
1861 out->l = 0;
1862 while (inp < maxp) {
1863 if (*inp & 0200)
1864 n = mbtowc(&wc, inp, PTR2SIZE(maxp - inp));
1865 else {
1866 wc = *inp;
1867 n = 1;
1869 if (n == -1) {
1870 /* FIXME Why mbtowc() resetting here?
1871 * FIXME what about ISO 2022-JP plus -- those
1872 * FIXME will loose shifts, then!
1873 * FIXME THUS - we'd need special "known points"
1874 * FIXME to do so - say, after a newline!!
1875 * FIXME WE NEED TO CHANGE ALL USES +MBLEN! */
1876 mbtowc(&wc, NULL, mb_cur_max);
1877 wc = isuni ? 0xFFFD : '?';
1878 n = 1;
1879 } else if (n == 0)
1880 n = 1;
1881 inp += n;
1882 if (!iswprint(wc) && wc != '\n' && wc != '\r' && wc != '\b' &&
1883 wc != '\t') {
1884 if ((wc & ~(wchar_t)037) == 0)
1885 wc = isuni ? 0x2400 | wc : '?';
1886 else if (wc == 0177)
1887 wc = isuni ? 0x2421 : '?';
1888 else
1889 wc = isuni ? 0x2426 : '?';
1891 if ((n = wctomb(mbb, wc)) <= 0)
1892 continue;
1893 out->l += n;
1894 assert(out->l < msz);
1895 for (i = 0; i < n; ++i)
1896 *outp++ = mbb[i];
1898 } else
1899 #endif /* NATCH_CHAR */
1901 int c;
1902 while (inp < maxp) {
1903 c = *inp++ & 0377;
1904 if (!isprint(c) && c != '\n' && c != '\r' && c != '\b' && c != '\t')
1905 c = '?';
1906 *outp++ = c;
1908 out->l = in->l;
1910 jleave:
1911 out->s[out->l] = '\0';
1912 NYD_LEAVE;
1915 FL char *
1916 prstr(char const *s)
1918 struct str in, out;
1919 char *rp;
1920 NYD_ENTER;
1922 in.s = UNCONST(s);
1923 in.l = strlen(s);
1924 makeprint(&in, &out);
1925 rp = savestrbuf(out.s, out.l);
1926 free(out.s);
1927 NYD_LEAVE;
1928 return rp;
1931 FL int
1932 prout(char const *s, size_t sz, FILE *fp)
1934 struct str in, out;
1935 int n;
1936 NYD_ENTER;
1938 in.s = UNCONST(s);
1939 in.l = sz;
1940 makeprint(&in, &out);
1941 n = fwrite(out.s, 1, out.l, fp);
1942 free(out.s);
1943 NYD_LEAVE;
1944 return n;
1947 FL size_t
1948 putuc(int u, int c, FILE *fp)
1950 size_t rv;
1951 NYD_ENTER;
1952 UNUSED(u);
1954 #ifdef HAVE_NATCH_CHAR
1955 if ((options & OPT_UNICODE) && (u & ~(wchar_t)0177)) {
1956 char mbb[MB_LEN_MAX];
1957 int i, n;
1959 if ((n = wctomb(mbb, u)) > 0) {
1960 rv = wcwidth(u);
1961 for (i = 0; i < n; ++i)
1962 if (putc(mbb[i] & 0377, fp) == EOF) {
1963 rv = 0;
1964 break;
1966 } else if (n == 0)
1967 rv = (putc('\0', fp) != EOF);
1968 else
1969 rv = 0;
1970 } else
1971 #endif
1972 rv = (putc(c, fp) != EOF);
1973 NYD_LEAVE;
1974 return rv;
1977 FL bool_t
1978 bidi_info_needed(char const *bdat, size_t blen)
1980 bool_t rv = FAL0;
1981 NYD_ENTER;
1983 #ifdef HAVE_NATCH_CHAR
1984 if (options & OPT_UNICODE)
1985 for (; blen > 0; ++bdat, --blen) {
1986 if ((ui8_t)*bdat > 0x7F) {
1987 /* TODO Checking for BIDI character: use S-CText fromutf8
1988 * TODO plus isrighttoleft (or whatever there will be)! */
1989 ui32_t c, x = (ui8_t)*bdat;
1991 if ((x & 0xE0) == 0xC0) {
1992 if (blen < 2)
1993 break;
1994 blen -= 1;
1995 c = x & ~0xC0;
1996 } else if ((x & 0xF0) == 0xE0) {
1997 if (blen < 3)
1998 break;
1999 blen -= 2;
2000 c = x & ~0xE0;
2001 c <<= 6;
2002 x = (ui8_t)*++bdat;
2003 c |= x & 0x7F;
2004 } else {
2005 if (blen < 4)
2006 break;
2007 blen -= 3;
2008 c = x & ~0xF0;
2009 c <<= 6;
2010 x = (ui8_t)*++bdat;
2011 c |= x & 0x7F;
2012 c <<= 6;
2013 x = (ui8_t)*++bdat;
2014 c |= x & 0x7F;
2016 c <<= 6;
2017 x = (ui8_t)*++bdat;
2018 c |= x & 0x7F;
2020 /* (Very very fuzzy, awaiting S-CText for good) */
2021 if ((c >= 0x05BE && c <= 0x08E3) ||
2022 (c >= 0xFB1D && c <= 0xFEFC) ||
2023 (c >= 0x10800 && c <= 0x10C48) ||
2024 (c >= 0x1EE00 && c <= 0x1EEF1)) {
2025 rv = TRU1;
2026 break;
2030 #endif /* HAVE_NATCH_CHAR */
2031 NYD_LEAVE;
2032 return rv;
2035 FL void
2036 bidi_info_create(struct bidi_info *bip)
2038 /* Unicode: how to isolate RIGHT-TO-LEFT scripts via *headline-bidi*
2039 * 1.1 (Jun 1993): U+200E (E2 80 8E) LEFT-TO-RIGHT MARK
2040 * 6.3 (Sep 2013): U+2068 (E2 81 A8) FIRST STRONG ISOLATE,
2041 * U+2069 (E2 81 A9) POP DIRECTIONAL ISOLATE
2042 * Worse results seen for: U+202D "\xE2\x80\xAD" U+202C "\xE2\x80\xAC" */
2043 NATCH_CHAR( char const *hb; )
2044 NYD_ENTER;
2046 memset(bip, 0, sizeof *bip);
2047 bip->bi_start.s = bip->bi_end.s = UNCONST("");
2049 #ifdef HAVE_NATCH_CHAR
2050 if ((options & OPT_UNICODE) && (hb = ok_vlook(headline_bidi)) != NULL) {
2051 switch (*hb) {
2052 case '3':
2053 bip->bi_pad = 2;
2054 /* FALLTHRU */
2055 case '2':
2056 bip->bi_start.s = bip->bi_end.s = UNCONST("\xE2\x80\x8E");
2057 break;
2058 case '1':
2059 bip->bi_pad = 2;
2060 /* FALLTHRU */
2061 default:
2062 bip->bi_start.s = UNCONST("\xE2\x81\xA8");
2063 bip->bi_end.s = UNCONST("\xE2\x81\xA9");
2064 break;
2066 bip->bi_start.l = bip->bi_end.l = 3;
2068 #endif
2069 NYD_LEAVE;
2072 #ifdef HAVE_COLOUR
2073 FL void
2074 colour_table_create(char const *pager_used)
2076 union {char *cp; char const *ccp; void *vp; struct colour_table *ctp;} u;
2077 size_t i;
2078 struct colour_table *ct;
2079 NYD_ENTER;
2081 if (ok_blook(colour_disable))
2082 goto jleave;
2084 /* If pager, check wether it is allowed to use colour */
2085 if (pager_used != NULL) {
2086 char *pager;
2088 if ((u.cp = ok_vlook(colour_pagers)) == NULL)
2089 u.ccp = COLOUR_PAGERS;
2090 pager = savestr(u.cp);
2092 while ((u.cp = n_strsep(&pager, ',', TRU1)) != NULL)
2093 if (strstr(pager_used, u.cp) != NULL)
2094 goto jok;
2095 goto jleave;
2098 /* $TERM is different in that we default to false unless whitelisted */
2100 char *term, *okterms;
2102 /* Don't use getenv(), but force copy-in into our own tables.. */
2103 if ((term = _var_voklook("TERM")) == NULL)
2104 goto jleave;
2105 if ((okterms = ok_vlook(colour_terms)) == NULL)
2106 okterms = UNCONST(COLOUR_TERMS);
2107 okterms = savestr(okterms);
2109 i = strlen(term);
2110 while ((u.cp = n_strsep(&okterms, ',', TRU1)) != NULL)
2111 if (!strncmp(u.cp, term, i))
2112 goto jok;
2113 goto jleave;
2116 jok:
2117 colour_table = ct = salloc(sizeof *ct); /* XXX lex.c yet resets (FILTER!) */
2118 { static struct {
2119 enum okeys okey;
2120 enum colourspec cspec;
2121 char const *defval;
2122 } const map[] = {
2123 {ok_v_colour_msginfo, COLOURSPEC_MSGINFO, COLOUR_MSGINFO},
2124 {ok_v_colour_partinfo, COLOURSPEC_PARTINFO, COLOUR_PARTINFO},
2125 {ok_v_colour_from_, COLOURSPEC_FROM_, COLOUR_FROM_},
2126 {ok_v_colour_header, COLOURSPEC_HEADER, COLOUR_HEADER},
2127 {ok_v_colour_uheader, COLOURSPEC_UHEADER, COLOUR_UHEADER}
2130 for (i = 0; i < NELEM(map); ++i) {
2131 if ((u.cp = _var_oklook(map[i].okey)) == NULL)
2132 u.ccp = map[i].defval;
2133 u.cp = _colour_iso6429(u.ccp);
2134 ct->ct_csinfo[map[i].cspec].l = strlen(u.cp);
2135 ct->ct_csinfo[map[i].cspec].s = u.cp;
2138 ct->ct_csinfo[COLOURSPEC_RESET].l = sizeof("\033[0m") -1;
2139 ct->ct_csinfo[COLOURSPEC_RESET].s = UNCONST("\033[0m");
2141 if ((u.cp = ok_vlook(colour_user_headers)) == NULL)
2142 u.ccp = COLOUR_USER_HEADERS;
2143 ct->ct_csinfo[COLOURSPEC_RESET + 1].l = i = strlen(u.ccp);
2144 ct->ct_csinfo[COLOURSPEC_RESET + 1].s = (i == 0) ? NULL : savestr(u.ccp);
2145 jleave:
2146 NYD_LEAVE;
2149 FL void
2150 colour_put(FILE *fp, enum colourspec cs)
2152 NYD_ENTER;
2153 if (colour_table != NULL) {
2154 struct str const *cp = colour_get(cs);
2156 fwrite(cp->s, cp->l, 1, fp);
2158 NYD_LEAVE;
2161 FL void
2162 colour_put_header(FILE *fp, char const *name)
2164 enum colourspec cs = COLOURSPEC_HEADER;
2165 struct str const *uheads;
2166 char *cp, *cp_base, *x;
2167 size_t namelen;
2168 NYD_ENTER;
2170 if (colour_table == NULL)
2171 goto j_leave;
2172 /* Normal header colours if there are no user headers */
2173 uheads = colour_table->ct_csinfo + COLOURSPEC_RESET + 1;
2174 if (uheads->s == NULL)
2175 goto jleave;
2177 /* Iterate over all entries in the *colour-user-headers* list */
2178 cp = ac_alloc(uheads->l +1);
2179 memcpy(cp, uheads->s, uheads->l +1);
2180 cp_base = cp;
2181 namelen = strlen(name);
2182 while ((x = n_strsep(&cp, ',', TRU1)) != NULL) {
2183 size_t l = (cp != NULL) ? PTR2SIZE(cp - x) - 1 : strlen(x);
2184 if (l == namelen && !ascncasecmp(x, name, namelen)) {
2185 cs = COLOURSPEC_UHEADER;
2186 break;
2189 ac_free(cp_base);
2190 jleave:
2191 colour_put(fp, cs);
2192 j_leave:
2193 NYD_LEAVE;
2196 FL void
2197 colour_reset(FILE *fp)
2199 NYD_ENTER;
2200 if (colour_table != NULL)
2201 fwrite("\033[0m", 4, 1, fp);
2202 NYD_LEAVE;
2205 FL struct str const *
2206 colour_get(enum colourspec cs)
2208 struct str const *rv = NULL;
2209 NYD_ENTER;
2211 if (colour_table != NULL)
2212 if ((rv = colour_table->ct_csinfo + cs)->s == NULL)
2213 rv = NULL;
2214 NYD_LEAVE;
2215 return rv;
2217 #endif /* HAVE_COLOUR */
2219 FL void
2220 time_current_update(struct time_current *tc, bool_t full_update)
2222 NYD_ENTER;
2223 tc->tc_time = time(NULL);
2224 if (full_update) {
2225 memcpy(&tc->tc_gm, gmtime(&tc->tc_time), sizeof tc->tc_gm);
2226 memcpy(&tc->tc_local, localtime(&tc->tc_time), sizeof tc->tc_local);
2227 sstpcpy(tc->tc_ctime, ctime(&tc->tc_time));
2229 NYD_LEAVE;
2232 static void
2233 _out_of_memory(void)
2235 panic("no memory");
2238 #ifndef HAVE_DEBUG
2239 FL void *
2240 smalloc(size_t s SMALLOC_DEBUG_ARGS)
2242 void *rv;
2243 NYD_ENTER;
2245 if (s == 0)
2246 s = 1;
2247 if ((rv = malloc(s)) == NULL)
2248 _out_of_memory();
2249 NYD_LEAVE;
2250 return rv;
2253 FL void *
2254 srealloc(void *v, size_t s SMALLOC_DEBUG_ARGS)
2256 void *rv;
2257 NYD_ENTER;
2259 if (s == 0)
2260 s = 1;
2261 if (v == NULL)
2262 rv = smalloc(s);
2263 else if ((rv = realloc(v, s)) == NULL)
2264 _out_of_memory();
2265 NYD_LEAVE;
2266 return rv;
2269 FL void *
2270 scalloc(size_t nmemb, size_t size SMALLOC_DEBUG_ARGS)
2272 void *rv;
2273 NYD_ENTER;
2275 if (size == 0)
2276 size = 1;
2277 if ((rv = calloc(nmemb, size)) == NULL)
2278 _out_of_memory();
2279 NYD_LEAVE;
2280 return rv;
2283 #else /* !HAVE_DEBUG */
2284 CTA(sizeof(char) == sizeof(ui8_t));
2286 # define _HOPE_SIZE (2 * 8 * sizeof(char))
2287 # define _HOPE_SET(C) \
2288 do {\
2289 union mem_ptr __xl, __xu;\
2290 struct mem_chunk *__xc;\
2291 __xl.p_p = (C).p_p;\
2292 __xc = __xl.p_c - 1;\
2293 __xu.p_p = __xc;\
2294 (C).p_cp += 8;\
2295 __xl.p_ui8p[0]=0xDE; __xl.p_ui8p[1]=0xAA;\
2296 __xl.p_ui8p[2]=0x55; __xl.p_ui8p[3]=0xAD;\
2297 __xl.p_ui8p[4]=0xBE; __xl.p_ui8p[5]=0x55;\
2298 __xl.p_ui8p[6]=0xAA; __xl.p_ui8p[7]=0xEF;\
2299 __xu.p_ui8p += __xc->mc_size - 8;\
2300 __xu.p_ui8p[0]=0xDE; __xu.p_ui8p[1]=0xAA;\
2301 __xu.p_ui8p[2]=0x55; __xu.p_ui8p[3]=0xAD;\
2302 __xu.p_ui8p[4]=0xBE; __xu.p_ui8p[5]=0x55;\
2303 __xu.p_ui8p[6]=0xAA; __xu.p_ui8p[7]=0xEF;\
2304 } while (0)
2305 # define _HOPE_GET_TRACE(C,BAD) \
2306 do {\
2307 (C).p_cp += 8;\
2308 _HOPE_GET(C, BAD);\
2309 (C).p_cp += 8;\
2310 } while(0)
2311 # define _HOPE_GET(C,BAD) \
2312 do {\
2313 union mem_ptr __xl, __xu;\
2314 struct mem_chunk *__xc;\
2315 ui32_t __i;\
2316 __xl.p_p = (C).p_p;\
2317 __xl.p_cp -= 8;\
2318 (C).p_cp = __xl.p_cp;\
2319 __xc = __xl.p_c - 1;\
2320 (BAD) = FAL0;\
2321 __i = 0;\
2322 if (__xl.p_ui8p[0] != 0xDE) __i |= 1<<0;\
2323 if (__xl.p_ui8p[1] != 0xAA) __i |= 1<<1;\
2324 if (__xl.p_ui8p[2] != 0x55) __i |= 1<<2;\
2325 if (__xl.p_ui8p[3] != 0xAD) __i |= 1<<3;\
2326 if (__xl.p_ui8p[4] != 0xBE) __i |= 1<<4;\
2327 if (__xl.p_ui8p[5] != 0x55) __i |= 1<<5;\
2328 if (__xl.p_ui8p[6] != 0xAA) __i |= 1<<6;\
2329 if (__xl.p_ui8p[7] != 0xEF) __i |= 1<<7;\
2330 if (__i != 0) {\
2331 (BAD) = TRU1;\
2332 alert("%p: corrupt lower canary: 0x%02X: %s, line %u",\
2333 __xl.p_p, __i, mdbg_file, mdbg_line);\
2335 __xu.p_p = __xc;\
2336 __xu.p_ui8p += __xc->mc_size - 8;\
2337 __i = 0;\
2338 if (__xu.p_ui8p[0] != 0xDE) __i |= 1<<0;\
2339 if (__xu.p_ui8p[1] != 0xAA) __i |= 1<<1;\
2340 if (__xu.p_ui8p[2] != 0x55) __i |= 1<<2;\
2341 if (__xu.p_ui8p[3] != 0xAD) __i |= 1<<3;\
2342 if (__xu.p_ui8p[4] != 0xBE) __i |= 1<<4;\
2343 if (__xu.p_ui8p[5] != 0x55) __i |= 1<<5;\
2344 if (__xu.p_ui8p[6] != 0xAA) __i |= 1<<6;\
2345 if (__xu.p_ui8p[7] != 0xEF) __i |= 1<<7;\
2346 if (__i != 0) {\
2347 (BAD) = TRU1;\
2348 alert("%p: corrupt upper canary: 0x%02X: %s, line %u",\
2349 __xl.p_p, __i, mdbg_file, mdbg_line);\
2351 if (BAD)\
2352 alert(" ..canary last seen: %s, line %u",\
2353 __xc->mc_file, __xc->mc_line);\
2354 } while (0)
2356 FL void *
2357 (smalloc)(size_t s SMALLOC_DEBUG_ARGS)
2359 union mem_ptr p;
2360 NYD_ENTER;
2362 if (s == 0)
2363 s = 1;
2364 s += sizeof(struct mem_chunk) + _HOPE_SIZE;
2366 if ((p.p_p = (malloc)(s)) == NULL)
2367 _out_of_memory();
2368 p.p_c->mc_prev = NULL;
2369 if ((p.p_c->mc_next = _mem_list) != NULL)
2370 _mem_list->mc_prev = p.p_c;
2371 p.p_c->mc_file = mdbg_file;
2372 p.p_c->mc_line = (ui16_t)mdbg_line;
2373 p.p_c->mc_isfree = FAL0;
2374 p.p_c->mc_size = (ui32_t)s;
2375 _mem_list = p.p_c++;
2376 _HOPE_SET(p);
2378 ++_mem_aall;
2379 ++_mem_acur;
2380 _mem_amax = MAX(_mem_amax, _mem_acur);
2381 _mem_mall += s;
2382 _mem_mcur += s;
2383 _mem_mmax = MAX(_mem_mmax, _mem_mcur);
2384 NYD_LEAVE;
2385 return p.p_p;
2388 FL void *
2389 (srealloc)(void *v, size_t s SMALLOC_DEBUG_ARGS)
2391 union mem_ptr p;
2392 bool_t isbad;
2393 NYD_ENTER;
2395 if ((p.p_p = v) == NULL) {
2396 p.p_p = (smalloc)(s, mdbg_file, mdbg_line);
2397 goto jleave;
2400 _HOPE_GET(p, isbad);
2401 --p.p_c;
2402 if (p.p_c->mc_isfree) {
2403 fprintf(stderr, "srealloc(): region freed! At %s, line %d\n"
2404 "\tLast seen: %s, line %d\n",
2405 mdbg_file, mdbg_line, p.p_c->mc_file, p.p_c->mc_line);
2406 goto jforce;
2409 if (p.p_c == _mem_list)
2410 _mem_list = p.p_c->mc_next;
2411 else
2412 p.p_c->mc_prev->mc_next = p.p_c->mc_next;
2413 if (p.p_c->mc_next != NULL)
2414 p.p_c->mc_next->mc_prev = p.p_c->mc_prev;
2416 --_mem_acur;
2417 _mem_mcur -= p.p_c->mc_size;
2418 jforce:
2419 if (s == 0)
2420 s = 1;
2421 s += sizeof(struct mem_chunk) + _HOPE_SIZE;
2423 if ((p.p_p = (realloc)(p.p_c, s)) == NULL)
2424 _out_of_memory();
2425 p.p_c->mc_prev = NULL;
2426 if ((p.p_c->mc_next = _mem_list) != NULL)
2427 _mem_list->mc_prev = p.p_c;
2428 p.p_c->mc_file = mdbg_file;
2429 p.p_c->mc_line = (ui16_t)mdbg_line;
2430 p.p_c->mc_isfree = FAL0;
2431 p.p_c->mc_size = (ui32_t)s;
2432 _mem_list = p.p_c++;
2433 _HOPE_SET(p);
2435 ++_mem_aall;
2436 ++_mem_acur;
2437 _mem_amax = MAX(_mem_amax, _mem_acur);
2438 _mem_mall += s;
2439 _mem_mcur += s;
2440 _mem_mmax = MAX(_mem_mmax, _mem_mcur);
2441 jleave:
2442 NYD_LEAVE;
2443 return p.p_p;
2446 FL void *
2447 (scalloc)(size_t nmemb, size_t size SMALLOC_DEBUG_ARGS)
2449 union mem_ptr p;
2450 NYD_ENTER;
2452 if (size == 0)
2453 size = 1;
2454 if (nmemb == 0)
2455 nmemb = 1;
2456 size *= nmemb;
2457 size += sizeof(struct mem_chunk) + _HOPE_SIZE;
2459 if ((p.p_p = (malloc)(size)) == NULL)
2460 _out_of_memory();
2461 memset(p.p_p, 0, size);
2462 p.p_c->mc_prev = NULL;
2463 if ((p.p_c->mc_next = _mem_list) != NULL)
2464 _mem_list->mc_prev = p.p_c;
2465 p.p_c->mc_file = mdbg_file;
2466 p.p_c->mc_line = (ui16_t)mdbg_line;
2467 p.p_c->mc_isfree = FAL0;
2468 p.p_c->mc_size = (ui32_t)size;
2469 _mem_list = p.p_c++;
2470 _HOPE_SET(p);
2472 ++_mem_aall;
2473 ++_mem_acur;
2474 _mem_amax = MAX(_mem_amax, _mem_acur);
2475 _mem_mall += size;
2476 _mem_mcur += size;
2477 _mem_mmax = MAX(_mem_mmax, _mem_mcur);
2478 NYD_LEAVE;
2479 return p.p_p;
2482 FL void
2483 (sfree)(void *v SMALLOC_DEBUG_ARGS)
2485 union mem_ptr p;
2486 bool_t isbad;
2487 NYD_ENTER;
2489 if ((p.p_p = v) == NULL) {
2490 fprintf(stderr, "sfree(NULL) from %s, line %d\n", mdbg_file, mdbg_line);
2491 goto jleave;
2494 _HOPE_GET(p, isbad);
2495 --p.p_c;
2496 if (p.p_c->mc_isfree) {
2497 fprintf(stderr, "sfree(): double-free avoided at %s, line %d\n"
2498 "\tLast seen: %s, line %d\n",
2499 mdbg_file, mdbg_line, p.p_c->mc_file, p.p_c->mc_line);
2500 goto jleave;
2503 if (p.p_c == _mem_list)
2504 _mem_list = p.p_c->mc_next;
2505 else
2506 p.p_c->mc_prev->mc_next = p.p_c->mc_next;
2507 if (p.p_c->mc_next != NULL)
2508 p.p_c->mc_next->mc_prev = p.p_c->mc_prev;
2509 p.p_c->mc_isfree = TRU1;
2511 --_mem_acur;
2512 _mem_mcur -= p.p_c->mc_size;
2514 if (options & OPT_DEBUG) {
2515 p.p_c->mc_next = _mem_free;
2516 _mem_free = p.p_c;
2517 } else
2518 (free)(p.p_c);
2519 jleave:
2520 NYD_LEAVE;
2523 FL void
2524 smemreset(void)
2526 union mem_ptr p;
2527 size_t c = 0, s = 0;
2528 NYD_ENTER;
2530 for (p.p_c = _mem_free; p.p_c != NULL;) {
2531 void *vp = p.p_c;
2532 ++c;
2533 s += p.p_c->mc_size;
2534 p.p_c = p.p_c->mc_next;
2535 (free)(vp);
2537 _mem_free = NULL;
2539 if (options & OPT_DEBUG)
2540 fprintf(stderr, "smemreset(): freed %" ZFMT " chunks/%" ZFMT " bytes\n",
2541 c, s);
2542 NYD_LEAVE;
2545 FL int
2546 c_smemtrace(void *v)
2548 /* For _HOPE_GET() */
2549 char const * const mdbg_file = "smemtrace()";
2550 int const mdbg_line = -1;
2551 FILE *fp;
2552 union mem_ptr p, xp;
2553 bool_t isbad;
2554 size_t lines;
2555 NYD_ENTER;
2557 v = (void*)0x1;
2558 if ((fp = Ftmp(NULL, "memtr", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
2559 NULL) {
2560 perror("tmpfile");
2561 goto jleave;
2564 fprintf(fp, "Memory statistics:\n"
2565 " Count cur/peek/all: %7" ZFMT "/%7" ZFMT "/%10" ZFMT "\n"
2566 " Bytes cur/peek/all: %7" ZFMT "/%7" ZFMT "/%10" ZFMT "\n\n",
2567 _mem_acur, _mem_amax, _mem_aall, _mem_mcur, _mem_mmax, _mem_mall);
2569 fprintf(fp, "Currently allocated memory chunks:\n");
2570 for (lines = 0, p.p_c = _mem_list; p.p_c != NULL;
2571 ++lines, p.p_c = p.p_c->mc_next) {
2572 xp = p;
2573 ++xp.p_c;
2574 _HOPE_GET_TRACE(xp, isbad);
2575 fprintf(fp, "%s%p (%5" ZFMT " bytes): %s, line %u\n",
2576 (isbad ? "! CANARY ERROR: " : ""), xp.p_p,
2577 (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)), p.p_c->mc_file,
2578 p.p_c->mc_line);
2581 if (options & OPT_DEBUG) {
2582 fprintf(fp, "sfree()d memory chunks awaiting free():\n");
2583 for (p.p_c = _mem_free; p.p_c != NULL; ++lines, p.p_c = p.p_c->mc_next) {
2584 xp = p;
2585 ++xp.p_c;
2586 _HOPE_GET_TRACE(xp, isbad);
2587 fprintf(fp, "%s%p (%5" ZFMT " bytes): %s, line %u\n",
2588 (isbad ? "! CANARY ERROR: " : ""), xp.p_p,
2589 (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)),
2590 p.p_c->mc_file, p.p_c->mc_line);
2594 page_or_print(fp, lines);
2595 Fclose(fp);
2596 v = NULL;
2597 jleave:
2598 NYD_LEAVE;
2599 return (v != NULL);
2602 # ifdef _HAVE_MEMCHECK
2603 FL bool_t
2604 _smemcheck(char const *mdbg_file, int mdbg_line)
2606 union mem_ptr p, xp;
2607 bool_t anybad = FAL0, isbad;
2608 size_t lines;
2609 NYD_ENTER;
2611 for (lines = 0, p.p_c = _mem_list; p.p_c != NULL;
2612 ++lines, p.p_c = p.p_c->mc_next) {
2613 xp = p;
2614 ++xp.p_c;
2615 _HOPE_GET_TRACE(xp, isbad);
2616 if (isbad) {
2617 anybad = TRU1;
2618 fprintf(stderr,
2619 "! CANARY ERROR: %p (%5" ZFMT " bytes): %s, line %u\n",
2620 xp.p_p, (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)),
2621 p.p_c->mc_file, p.p_c->mc_line);
2625 if (options & OPT_DEBUG) {
2626 for (p.p_c = _mem_free; p.p_c != NULL; ++lines, p.p_c = p.p_c->mc_next) {
2627 xp = p;
2628 ++xp.p_c;
2629 _HOPE_GET_TRACE(xp, isbad);
2630 if (isbad) {
2631 anybad = TRU1;
2632 fprintf(stderr,
2633 "! CANARY ERROR: %p (%5" ZFMT " bytes): %s, line %u\n",
2634 xp.p_p, (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)),
2635 p.p_c->mc_file, p.p_c->mc_line);
2639 NYD_LEAVE;
2640 return anybad;
2642 # endif /* _HAVE_MEMCHECK */
2644 # undef _HOPE_SIZE
2645 # undef _HOPE_SET
2646 # undef _HOPE_GET_TRACE
2647 # undef _HOPE_GET
2648 #endif /* HAVE_DEBUG */
2650 /* vim:set fenc=utf-8:s-it-mode */