Merge branch 'topic/obsd56'
[s-mailx.git] / auxlily.c
blob40ee2a063520c7df7a6ba679515c63814f8b339a
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)
677 static char buf[PROMPT_BUFFER_SIZE];
679 char *cp = buf;
680 char const *ccp;
681 NYD_ENTER;
683 if ((ccp = ok_vlook(prompt)) == NULL || *ccp == '\0')
684 goto jleave;
686 for (; PTRCMP(cp, <, buf + sizeof(buf) - 1); ++cp) {
687 char const *a;
688 size_t l;
689 int c = expand_shell_escape(&ccp, TRU1);
691 if (c > 0) {
692 *cp = (char)c;
693 continue;
695 if (c == 0 || c == PROMPT_STOP)
696 break;
698 a = (c == PROMPT_DOLLAR) ? account_name : displayname;
699 if (a == NULL)
700 a = "";
701 l = strlen(a);
702 if (PTRCMP(cp + l, >=, buf + sizeof(buf) - 1))
703 *cp++ = '?';
704 else {
705 memcpy(cp, a, l);
706 cp += --l;
709 jleave:
710 *cp = '\0';
711 NYD_LEAVE;
712 return buf;
715 FL char *
716 nodename(int mayoverride)
718 static char *sys_hostname, *hostname; /* XXX free-at-exit */
720 struct utsname ut;
721 char *hn;
722 #ifdef HAVE_SOCKETS
723 # ifdef HAVE_IPV6
724 struct addrinfo hints, *res;
725 # else
726 struct hostent *hent;
727 # endif
728 #endif
729 NYD_ENTER;
731 if (mayoverride && (hn = ok_vlook(hostname)) != NULL && *hn != '\0') {
733 } else if ((hn = sys_hostname) == NULL) {
734 uname(&ut);
735 hn = ut.nodename;
736 #ifdef HAVE_SOCKETS
737 # ifdef HAVE_IPV6
738 memset(&hints, 0, sizeof hints);
739 hints.ai_family = AF_UNSPEC;
740 hints.ai_socktype = SOCK_DGRAM; /* (dummy) */
741 hints.ai_flags = AI_CANONNAME;
742 if (getaddrinfo(hn, "0", &hints, &res) == 0) {
743 if (res->ai_canonname != NULL) {
744 size_t l = strlen(res->ai_canonname) +1;
745 hn = ac_alloc(l);
746 memcpy(hn, res->ai_canonname, l);
748 freeaddrinfo(res);
750 # else
751 hent = gethostbyname(hn);
752 if (hent != NULL)
753 hn = hent->h_name;
754 # endif
755 #endif
756 sys_hostname = sstrdup(hn);
757 #if defined HAVE_SOCKETS && defined HAVE_IPV6
758 if (hn != ut.nodename)
759 ac_free(hn);
760 #endif
761 hn = sys_hostname;
764 if (hostname != NULL && hostname != sys_hostname)
765 free(hostname);
766 hostname = sstrdup(hn);
767 NYD_LEAVE;
768 return hostname;
771 FL bool_t
772 url_parse(struct url *urlp, enum cproto cproto, char const *data)
774 #if defined HAVE_SMTP && defined HAVE_POP3 && defined HAVE_IMAP
775 # define __ALLPROTO
776 #endif
777 #if defined HAVE_SMTP || defined HAVE_POP3 || defined HAVE_IMAP
778 # define __ANYPROTO
779 char *cp, *x;
780 #endif
781 bool_t rv = FAL0;
782 NYD_ENTER;
783 UNUSED(data);
785 memset(urlp, 0, sizeof *urlp);
786 urlp->url_input = data;
787 urlp->url_cproto = cproto;
789 /* Network protocol */
790 #define _protox(X,Y) \
791 urlp->url_portno = Y;\
792 memcpy(urlp->url_proto, X "://", sizeof(X "://"));\
793 urlp->url_proto[sizeof(X) -1] = '\0';\
794 urlp->url_proto_len = sizeof(X) -1;\
795 urlp->url_proto_xlen = sizeof(X "://") -1
796 #define __if(X,Y,Z) \
797 if (!ascncasecmp(data, X "://", sizeof(X "://") -1)) {\
798 _protox(X, Y);\
799 data += sizeof(X "://") -1;\
800 do { Z; } while (0);\
801 goto juser;\
803 #define _if(X,Y) __if(X, Y, (void)0)
804 #ifdef HAVE_SSL
805 # define _ifs(X,Y) __if(X, Y, urlp->url_needs_tls = TRU1)
806 #else
807 # define _ifs(X,Y) goto jeproto;
808 #endif
810 switch (cproto) {
811 case CPROTO_SMTP:
812 #ifdef HAVE_SMTP
813 _if ("smtp", 25)
814 _if ("submission", 587)
815 _ifs ("smtps", 465)
816 _protox("smtp", 25);
817 break;
818 #else
819 goto jeproto;
820 #endif
821 case CPROTO_POP3:
822 #ifdef HAVE_POP3
823 _if ("pop3", 110)
824 _ifs ("pop3s", 995)
825 _protox("pop3", 110);
826 break;
827 #else
828 goto jeproto;
829 #endif
830 case CPROTO_IMAP:
831 #ifdef HAVE_IMAP
832 _if ("imap", 143)
833 _ifs ("imaps", 993)
834 _protox("imap", 143);
835 break;
836 #else
837 goto jeproto;
838 #endif
841 #undef _ifs
842 #undef _if
843 #undef __if
844 #undef _protox
846 if (strstr(data, "://") != NULL) {
847 #if !defined __ALLPROTO || !defined HAVE_SSL
848 jeproto:
849 #endif
850 fprintf(stderr, tr(574, "URL `proto://' prefix invalid: `%s'\n"),
851 urlp->url_input);
852 goto jleave;
854 #ifdef __ANYPROTO
856 /* User and password, I */
857 juser:
858 if ((cp = UNCONST(last_at_before_slash(data))) == NULL)
859 goto jserver;
861 urlp->url_had_user = TRU1;
862 urlp->url_user.s = savestrbuf(data, urlp->url_user.l = PTR2SIZE(cp - data));
863 data = ++cp;
865 /* And also have a password? */
866 if ((cp = strchr(urlp->url_user.s, ':')) != NULL) {
867 x = urlp->url_user.s;
868 urlp->url_user.s = savestrbuf(x, urlp->url_user.l = PTR2SIZE(cp - x));
869 urlp->url_pass.l = strlen(urlp->url_pass.s = urlxdec(++cp));
870 urlp->url_pass_enc.l = strlen(
871 urlp->url_pass_enc.s = urlxenc(urlp->url_pass.s));
874 /* Servername and port -- and possible path suffix */
875 jserver:
876 if ((cp = strchr(data, ':')) != NULL) { /* TODO URL parse, IPv6 support */
877 char *eptr;
878 long l;
880 urlp->url_port = x = savestr(x = cp + 1);
881 if ((x = strchr(x, '/')) != NULL)
882 *x = '\0';
883 l = strtol(urlp->url_port, &eptr, 10);
884 if (*eptr != '\0' || l <= 0 || UICMP(32, l, >=, 0xFFFFu)) {
885 fprintf(stderr, tr(573, "URL with invalid port number: `%s'\n"),
886 urlp->url_input);
887 goto jleave;
889 urlp->url_portno = (ui16_t)l;
890 } else {
891 if ((x = strchr(data, '/')) != NULL)
892 data = savestrbuf(data, PTR2SIZE(x - data));
893 cp = UNCONST(data + strlen(data));
896 /* A (non-empty) path may only occur with IMAP */
897 if (x != NULL && x[1] != '\0') {
898 if (cproto != CPROTO_IMAP) {
899 fprintf(stderr, tr(575, "URL protocol doesn't support paths: `%s'\n"),
900 urlp->url_input);
901 goto jleave;
903 urlp->url_path.l = strlen(++x);
904 urlp->url_path.s = savestrbuf(x, urlp->url_path.l);
907 urlp->url_host.s = savestrbuf(data, urlp->url_host.l = PTR2SIZE(cp - data));
908 { size_t i;
909 for (cp = urlp->url_host.s, i = urlp->url_host.l; i != 0; ++cp, --i)
910 *cp = lowerconv(*cp);
913 /* HOST:PORT */
914 { size_t i;
915 struct str *s = &urlp->url_hp;
917 s->s = salloc(urlp->url_host.l + 1 + sizeof("65536")-1 +1);
918 memcpy(s->s, urlp->url_host.s, i = urlp->url_host.l);
919 if (urlp->url_port != NULL) {
920 size_t j = strlen(urlp->url_port);
921 s->s[i++] = ':';
922 memcpy(s->s + i, urlp->url_port, j);
923 i += j;
925 s->s[i] = '\0';
926 s->l = i;
929 /* User, II
930 * If there was no user in the URL, do we have *user-HOST* or *user*? */
931 if (!urlp->url_had_user) {
932 char const usrstr[] = "user-";
933 size_t const usrlen = sizeof(usrstr) -1;
935 cp = ac_alloc(usrlen + urlp->url_hp.l +1);
936 memcpy(cp, usrstr, usrlen);
937 memcpy(cp + usrlen, urlp->url_hp.s, urlp->url_hp.l +1);
938 if ((urlp->url_user.s = vok_vlook(cp)) == NULL) {
939 cp[usrlen - 1] = '\0';
940 if ((urlp->url_user.s = vok_vlook(cp)) == NULL)
941 urlp->url_user.s = UNCONST(myname);
943 ac_free(cp);
946 urlp->url_user.l = strlen(urlp->url_user.s = urlxdec(urlp->url_user.s));
947 urlp->url_user_enc.l = strlen(
948 urlp->url_user_enc.s = urlxenc(urlp->url_user.s));
950 /* USER@HOST:PORT */
951 if (urlp->url_user_enc.l == 0)
952 urlp->url_uhp = urlp->url_hp;
953 else {
954 struct str *s = &urlp->url_uhp;
955 size_t i = urlp->url_user_enc.l;
957 s->s = salloc(i + 1 + urlp->url_hp.l +1);
958 if (i > 0) {
959 memcpy(s->s, urlp->url_user_enc.s, i);
960 s->s[i++] = '@';
962 memcpy(s->s + i, urlp->url_hp.s, urlp->url_hp.l +1);
963 i += urlp->url_hp.l;
964 s->l = i;
967 /* USER@HOST
968 * For SMTP we apply ridiculously complicated *v15-compat* plus
969 * *smtp-hostname* / *hostname* dependent rules */
970 { struct str h;
972 if (cproto == CPROTO_SMTP && ok_blook(v15_compat) &&
973 (cp = ok_vlook(smtp_hostname)) != NULL) {
974 if (*cp == '\0')
975 cp = nodename(1);
976 h.s = savestrbuf(cp, h.l = strlen(cp));
977 } else
978 h = urlp->url_host;
980 if (urlp->url_user_enc.l == 0)
981 urlp->url_uh = h;
982 else {
983 struct str *s = &urlp->url_uh;
984 size_t i = urlp->url_user_enc.l;
986 s->s = salloc(i + 1 + h.l +1);
987 if (i > 0) {
988 memcpy(s->s, urlp->url_user_enc.s, i);
989 s->s[i++] = '@';
991 memcpy(s->s + i, h.s, h.l +1);
992 i += h.l;
993 s->l = i;
997 /* Finally, for fun: .url_proto://.url_uhp[/.url_path] */
998 { size_t i;
999 char *ud = salloc((i = urlp->url_proto_xlen + urlp->url_uhp.l) +
1000 1 + urlp->url_path.l +1);
1002 urlp->url_proto[urlp->url_proto_len] = ':';
1003 memcpy(sstpcpy(ud, urlp->url_proto), urlp->url_uhp.s, urlp->url_uhp.l +1);
1004 urlp->url_proto[urlp->url_proto_len] = '\0';
1006 if (urlp->url_path.l == 0)
1007 urlp->url_puhp = urlp->url_puhpp = ud;
1008 else {
1009 urlp->url_puhp = savestrbuf(ud, i);
1010 urlp->url_puhpp = ud;
1011 ud += i;
1012 *ud++ = '/';
1013 memcpy(ud, urlp->url_path.s, urlp->url_path.l +1);
1017 rv = TRU1;
1018 #endif /* __ANYPROTO */
1019 jleave:
1020 NYD_LEAVE;
1021 return rv;
1022 #undef __ANYPROTO
1023 #undef __ALLPROTO
1026 FL bool_t
1027 ccred_lookup_old(struct ccred *ccp, enum cproto cproto, char const *addr)
1029 char const *pname, *pxstr, *authdef;
1030 size_t pxlen, addrlen, i;
1031 char *vbuf, *s;
1032 ui8_t authmask;
1033 enum {NONE=0, WANT_PASS=1<<0, REQ_PASS=1<<1, WANT_USER=1<<2, REQ_USER=1<<3}
1034 ware = NONE;
1035 bool_t addr_is_nuser = FAL0; /* XXX v15.0 legacy! v15_compat */
1036 NYD_ENTER;
1038 memset(ccp, 0, sizeof *ccp);
1040 switch (cproto) {
1041 default:
1042 case CPROTO_SMTP:
1043 pname = "SMTP";
1044 pxstr = "smtp-auth";
1045 pxlen = sizeof("smtp-auth") -1;
1046 authmask = AUTHTYPE_NONE | AUTHTYPE_PLAIN | AUTHTYPE_LOGIN |
1047 AUTHTYPE_CRAM_MD5 | AUTHTYPE_GSSAPI;
1048 authdef = "none";
1049 addr_is_nuser = TRU1;
1050 break;
1051 case CPROTO_POP3:
1052 pname = "POP3";
1053 pxstr = "pop3-auth";
1054 pxlen = sizeof("pop3-auth") -1;
1055 authmask = AUTHTYPE_PLAIN;
1056 authdef = "plain";
1057 break;
1058 case CPROTO_IMAP:
1059 pname = "IMAP";
1060 pxstr = "imap-auth";
1061 pxlen = sizeof("imap-auth") -1;
1062 authmask = AUTHTYPE_LOGIN | AUTHTYPE_CRAM_MD5 | AUTHTYPE_GSSAPI;
1063 authdef = "login";
1064 break;
1067 ccp->cc_cproto = cproto;
1068 addrlen = strlen(addr);
1069 vbuf = ac_alloc(pxlen + addrlen + sizeof("-password-")-1 +1);
1070 memcpy(vbuf, pxstr, pxlen);
1072 /* Authentication type */
1073 vbuf[pxlen] = '-';
1074 memcpy(vbuf + pxlen + 1, addr, addrlen +1);
1075 if ((s = vok_vlook(vbuf)) == NULL) {
1076 vbuf[pxlen] = '\0';
1077 if ((s = vok_vlook(vbuf)) == NULL)
1078 s = UNCONST(authdef);
1081 if (!asccasecmp(s, "none")) {
1082 ccp->cc_auth = "NONE";
1083 ccp->cc_authtype = AUTHTYPE_NONE;
1084 /*ware = NONE;*/
1085 } else if (!asccasecmp(s, "plain")) {
1086 ccp->cc_auth = "PLAIN";
1087 ccp->cc_authtype = AUTHTYPE_PLAIN;
1088 ware = REQ_PASS | REQ_USER;
1089 } else if (!asccasecmp(s, "login")) {
1090 ccp->cc_auth = "LOGIN";
1091 ccp->cc_authtype = AUTHTYPE_LOGIN;
1092 ware = REQ_PASS | REQ_USER;
1093 } else if (!asccasecmp(s, "cram-md5")) {
1094 ccp->cc_auth = "CRAM-MD5";
1095 ccp->cc_authtype = AUTHTYPE_CRAM_MD5;
1096 ware = REQ_PASS | REQ_USER;
1097 } else if (!asccasecmp(s, "gssapi")) {
1098 ccp->cc_auth = "GSS-API";
1099 ccp->cc_authtype = AUTHTYPE_GSSAPI;
1100 ware = REQ_USER;
1101 #if 0 /* TODO SASL (homebrew `auto'matic indeed) */
1102 } else if (!asccasecmp(cp, "sasl")) {
1103 ware = REQ_USER | WANT_PASS;
1104 #endif
1107 /* Verify method */
1108 if (!(ccp->cc_authtype & authmask)) {
1109 fprintf(stderr, tr(273, "Unsupported %s authentication method: %s\n"),
1110 pname, s);
1111 ccp = NULL;
1112 goto jleave;
1114 #ifndef HAVE_MD5
1115 if (ccp->cc_authtype == AUTHTYPE_CRAM_MD5) {
1116 fprintf(stderr, tr(277, "No CRAM-MD5 support compiled in.\n"));
1117 ccp = NULL;
1118 goto jleave;
1120 #endif
1121 #ifndef HAVE_GSSAPI
1122 if (ccp->cc_authtype == AUTHTYPE_GSSAPI) {
1123 fprintf(stderr, tr(272, "No GSS-API support compiled in.\n"));
1124 ccp = NULL;
1125 goto jleave;
1127 #endif
1129 /* User name */
1130 if (!(ware & (WANT_USER | REQ_USER)))
1131 goto jpass;
1133 if (!addr_is_nuser) {
1134 if ((s = UNCONST(last_at_before_slash(addr))) != NULL) {
1135 ccp->cc_user.s = urlxdec(savestrbuf(addr, PTR2SIZE(s - addr)));
1136 ccp->cc_user.l = strlen(ccp->cc_user.s);
1137 } else if (ware & REQ_USER)
1138 goto jgetuser;
1139 goto jpass;
1142 memcpy(vbuf + pxlen, "-user-", i = sizeof("-user-") -1);
1143 i += pxlen;
1144 memcpy(vbuf + i, addr, addrlen +1);
1145 if ((s = vok_vlook(vbuf)) == NULL) {
1146 vbuf[--i] = '\0';
1147 if ((s = vok_vlook(vbuf)) == NULL && (ware & REQ_USER)) {
1148 if ((s = getuser(NULL)) != NULL)
1149 s = savestr(s);
1150 else {
1151 jgetuser: /* TODO v15.0: today we simply bail, but we should call getuser().
1152 * TODO even better: introduce `PROTO-user' and `PROTO-pass' and
1153 * TODO check that first, then! change control flow, grow `vbuf' */
1154 fprintf(stderr, tr(274,
1155 "A user is necessary for %s authentication.\n"), pname);
1156 ccp = NULL;
1157 goto jleave;
1161 ccp->cc_user.l = strlen(ccp->cc_user.s = s);
1163 /* Password */
1164 jpass:
1165 if (!(ware & (WANT_PASS | REQ_PASS)))
1166 goto jleave;
1168 if (!addr_is_nuser) {
1169 memcpy(vbuf, "password-", i = sizeof("password-") -1);
1170 } else {
1171 memcpy(vbuf + pxlen, "-password-", i = sizeof("-password-") -1);
1172 i += pxlen;
1174 memcpy(vbuf + i, addr, addrlen +1);
1175 if ((s = vok_vlook(vbuf)) == NULL) {
1176 vbuf[--i] = '\0';
1177 if ((!addr_is_nuser || (s = vok_vlook(vbuf)) == NULL) &&
1178 (ware & REQ_PASS)) {
1179 if ((s = getpassword(NULL)) == NULL) {
1180 fprintf(stderr, tr(275,
1181 "A password is necessary for %s authentication.\n"), pname);
1182 ccp = NULL;
1183 goto jleave;
1187 ccp->cc_pass.l = strlen(ccp->cc_pass.s = urlxdec(s));
1189 jleave:
1190 ac_free(vbuf);
1191 NYD_LEAVE;
1192 return (ccp != NULL);
1195 FL bool_t
1196 ccred_lookup(struct ccred *ccp, struct url *urlp)
1198 char const *pstr, *authdef;
1199 size_t plen, i;
1200 char *vbuf, *s;
1201 ui8_t authmask;
1202 enum {NONE=0, WANT_PASS=1<<0, REQ_PASS=1<<1, WANT_USER=1<<2, REQ_USER=1<<3}
1203 ware = NONE;
1204 NYD_ENTER;
1206 memset(ccp, 0, sizeof *ccp);
1207 ccp->cc_user = urlp->url_user;
1209 switch ((ccp->cc_cproto = urlp->url_cproto)) {
1210 default:
1211 case CPROTO_SMTP:
1212 pstr = "smtp";
1213 plen = sizeof("smtp") -1;
1214 authmask = AUTHTYPE_NONE | AUTHTYPE_PLAIN | AUTHTYPE_LOGIN |
1215 AUTHTYPE_CRAM_MD5 | AUTHTYPE_GSSAPI;
1216 authdef = "none";
1217 break;
1218 case CPROTO_POP3:
1219 pstr = "pop3";
1220 plen = sizeof("pop3") -1;
1221 authmask = AUTHTYPE_PLAIN;
1222 authdef = "plain";
1223 break;
1224 case CPROTO_IMAP:
1225 pstr = "imap";
1226 plen = sizeof("imap") -1;
1227 authmask = AUTHTYPE_LOGIN | AUTHTYPE_CRAM_MD5 | AUTHTYPE_GSSAPI;
1228 authdef = "login";
1229 break;
1232 /* Note: "password-" is longer than "-auth-", ditto .url_uhp and .url_hp */
1233 vbuf = ac_alloc(plen + sizeof("password-")-1 + urlp->url_uhp.l +1);
1234 memcpy(vbuf, pstr, plen);
1236 /* Authentication type */
1237 memcpy(vbuf + plen, "-auth-", i = sizeof("-auth-") -1);
1238 i += plen;
1239 /* -USER@HOST, -HOST, '' */
1240 memcpy(vbuf + i, urlp->url_uhp.s, urlp->url_uhp.l +1);
1241 if ((s = vok_vlook(vbuf)) == NULL) {
1242 memcpy(vbuf + i, urlp->url_hp.s, urlp->url_hp.l +1);
1243 if ((s = vok_vlook(vbuf)) == NULL) {
1244 vbuf[plen + sizeof("-auth") -1] = '\0';
1245 if ((s = vok_vlook(vbuf)) == NULL)
1246 s = UNCONST(authdef);
1250 if (!asccasecmp(s, "none")) {
1251 ccp->cc_auth = "NONE";
1252 ccp->cc_authtype = AUTHTYPE_NONE;
1253 /*ware = NONE;*/
1254 } else if (!asccasecmp(s, "plain")) {
1255 ccp->cc_auth = "PLAIN";
1256 ccp->cc_authtype = AUTHTYPE_PLAIN;
1257 ware = REQ_PASS | REQ_USER;
1258 } else if (!asccasecmp(s, "login")) {
1259 ccp->cc_auth = "LOGIN";
1260 ccp->cc_authtype = AUTHTYPE_LOGIN;
1261 ware = REQ_PASS | REQ_USER;
1262 } else if (!asccasecmp(s, "cram-md5")) {
1263 ccp->cc_auth = "CRAM-MD5";
1264 ccp->cc_authtype = AUTHTYPE_CRAM_MD5;
1265 ware = REQ_PASS | REQ_USER;
1266 } else if (!asccasecmp(s, "gssapi")) {
1267 ccp->cc_auth = "GSS-API";
1268 ccp->cc_authtype = AUTHTYPE_GSSAPI;
1269 ware = REQ_USER;
1270 #if 0 /* TODO SASL (homebrew `auto'matic indeed) */
1271 } else if (!asccasecmp(cp, "sasl")) {
1272 ware = REQ_USER | WANT_PASS;
1273 #endif
1276 /* Verify method */
1277 if (!(ccp->cc_authtype & authmask)) {
1278 fprintf(stderr, tr(273, "Unsupported %s authentication method: %s\n"),
1279 pstr, s);
1280 ccp = NULL;
1281 goto jleave;
1283 #ifndef HAVE_MD5
1284 if (ccp->cc_authtype == AUTHTYPE_CRAM_MD5) {
1285 fprintf(stderr, tr(277, "No CRAM-MD5 support compiled in.\n"));
1286 ccp = NULL;
1287 goto jleave;
1289 #endif
1290 #ifndef HAVE_GSSAPI
1291 if (ccp->cc_authtype == AUTHTYPE_GSSAPI) {
1292 fprintf(stderr, tr(272, "No GSS-API support compiled in.\n"));
1293 ccp = NULL;
1294 goto jleave;
1296 #endif
1298 /* Password */
1299 if ((ccp->cc_pass = urlp->url_pass).s != NULL)
1300 goto jleave;
1302 memcpy(vbuf, "password-", i = sizeof("password-") -1);
1303 /* -USER@HOST, -HOST, '' */
1304 memcpy(vbuf + i, urlp->url_uhp.s, urlp->url_uhp.l +1);
1305 if ((s = vok_vlook(vbuf)) == NULL) {
1306 memcpy(vbuf + i, urlp->url_hp.s, urlp->url_hp.l +1);
1307 if ((s = vok_vlook(vbuf)) == NULL) {
1308 vbuf[--i] = '\0';
1309 if ((s = vok_vlook(vbuf)) == NULL && (ware & REQ_PASS)) {
1310 if ((s = getpassword(NULL)) != NULL)
1311 s = savestr(s);
1312 else {
1313 fprintf(stderr, tr(275,
1314 "A password is necessary for %s authentication.\n"), pstr);
1315 ccp = NULL;
1316 goto jleave;
1321 ccp->cc_pass.l = strlen(ccp->cc_pass.s = urlxdec(s));
1323 jleave:
1324 ac_free(vbuf);
1325 NYD_LEAVE;
1326 return (ccp != NULL);
1329 FL char *
1330 getrandstring(size_t length)
1332 static unsigned char nodedigest[16];
1333 static pid_t pid;
1335 struct str b64;
1336 char *data, *cp;
1337 size_t i;
1338 int fd = -1;
1339 #ifdef HAVE_MD5
1340 md5_ctx ctx;
1341 #else
1342 size_t j;
1343 #endif
1344 NYD_ENTER;
1346 data = ac_alloc(length);
1348 if ((fd = open("/dev/urandom", O_RDONLY)) == -1 ||
1349 length != (size_t)read(fd, data, length)) {
1350 if (pid == 0) {
1351 pid = getpid();
1352 srand(pid);
1353 cp = nodename(0);
1354 #ifdef HAVE_MD5
1355 md5_init(&ctx);
1356 md5_update(&ctx, (unsigned char*)cp, strlen(cp));
1357 md5_final(nodedigest, &ctx);
1358 #else
1359 /* In that case it's only used for boundaries and Message-Id:s so that
1360 * srand(3) should suffice */
1361 j = strlen(cp) + 1;
1362 for (i = 0; i < sizeof(nodedigest); ++i)
1363 nodedigest[i] = (unsigned char)(cp[i % j] ^ rand());
1364 #endif
1366 for (i = 0; i < length; i++)
1367 data[i] = (char)((int)(255 * (rand() / (RAND_MAX + 1.0))) ^
1368 nodedigest[i % sizeof nodedigest]);
1370 if (fd >= 0)
1371 close(fd);
1373 b64_encode_buf(&b64, data, length, B64_SALLOC);
1374 ac_free(data);
1375 assert(length < b64.l);
1376 b64.s[length] = '\0';
1377 NYD_LEAVE;
1378 return b64.s;
1381 #ifdef HAVE_MD5
1382 FL char *
1383 md5tohex(char hex[MD5TOHEX_SIZE], void const *vp)
1385 char const *cp = vp;
1386 size_t i, j;
1387 NYD_ENTER;
1389 for (i = 0; i < MD5TOHEX_SIZE / 2; i++) {
1390 j = i << 1;
1391 hex[j] = hexchar((cp[i] & 0xf0) >> 4);
1392 hex[++j] = hexchar(cp[i] & 0x0f);
1394 NYD_LEAVE;
1395 return hex;
1398 FL char *
1399 cram_md5_string(char const *user, char const *pass, char const *b64)
1401 struct str in, out;
1402 char digest[16], *cp;
1403 size_t lu;
1404 NYD_ENTER;
1406 out.s = NULL;
1407 in.s = UNCONST(b64);
1408 in.l = strlen(in.s);
1409 b64_decode(&out, &in, NULL);
1410 assert(out.s != NULL);
1412 hmac_md5((unsigned char*)out.s, out.l, UNCONST(pass), strlen(pass), digest);
1413 free(out.s);
1414 cp = md5tohex(salloc(MD5TOHEX_SIZE +1), digest);
1416 lu = strlen(user);
1417 in.l = lu + MD5TOHEX_SIZE +1;
1418 in.s = ac_alloc(lu + 1 + MD5TOHEX_SIZE +1);
1419 memcpy(in.s, user, lu);
1420 in.s[lu++] = ' ';
1421 memcpy(in.s + lu, cp, MD5TOHEX_SIZE);
1422 b64_encode(&out, &in, B64_SALLOC | B64_CRLF);
1423 ac_free(in.s);
1424 NYD_LEAVE;
1425 return out.s;
1428 FL void
1429 hmac_md5(unsigned char *text, int text_len, unsigned char *key, int key_len,
1430 void *digest)
1433 * This code is taken from
1435 * Network Working Group H. Krawczyk
1436 * Request for Comments: 2104 IBM
1437 * Category: Informational M. Bellare
1438 * UCSD
1439 * R. Canetti
1440 * IBM
1441 * February 1997
1444 * HMAC: Keyed-Hashing for Message Authentication
1446 md5_ctx context;
1447 unsigned char k_ipad[65]; /* inner padding - key XORd with ipad */
1448 unsigned char k_opad[65]; /* outer padding - key XORd with opad */
1449 unsigned char tk[16];
1450 int i;
1451 NYD_ENTER;
1453 /* if key is longer than 64 bytes reset it to key=MD5(key) */
1454 if (key_len > 64) {
1455 md5_ctx tctx;
1457 md5_init(&tctx);
1458 md5_update(&tctx, key, key_len);
1459 md5_final(tk, &tctx);
1461 key = tk;
1462 key_len = 16;
1465 /* the HMAC_MD5 transform looks like:
1467 * MD5(K XOR opad, MD5(K XOR ipad, text))
1469 * where K is an n byte key
1470 * ipad is the byte 0x36 repeated 64 times
1471 * opad is the byte 0x5c repeated 64 times
1472 * and text is the data being protected */
1474 /* start out by storing key in pads */
1475 memset(k_ipad, 0, sizeof k_ipad);
1476 memset(k_opad, 0, sizeof k_opad);
1477 memcpy(k_ipad, key, key_len);
1478 memcpy(k_opad, key, key_len);
1480 /* XOR key with ipad and opad values */
1481 for (i=0; i<64; i++) {
1482 k_ipad[i] ^= 0x36;
1483 k_opad[i] ^= 0x5c;
1486 /* perform inner MD5 */
1487 md5_init(&context); /* init context for 1st pass */
1488 md5_update(&context, k_ipad, 64); /* start with inner pad */
1489 md5_update(&context, text, text_len); /* then text of datagram */
1490 md5_final(digest, &context); /* finish up 1st pass */
1492 /* perform outer MD5 */
1493 md5_init(&context); /* init context for 2nd pass */
1494 md5_update(&context, k_opad, 64); /* start with outer pad */
1495 md5_update(&context, digest, 16); /* then results of 1st hash */
1496 md5_final(digest, &context); /* finish up 2nd pass */
1497 NYD_LEAVE;
1499 #endif /* HAVE_MD5 */
1501 FL enum okay
1502 makedir(char const *name)
1504 struct stat st;
1505 enum okay rv = STOP;
1506 NYD_ENTER;
1508 if (!mkdir(name, 0700))
1509 rv = OKAY;
1510 else {
1511 int e = errno;
1512 if ((e == EEXIST || e == ENOSYS) && !stat(name, &st) &&
1513 S_ISDIR(st.st_mode))
1514 rv = OKAY;
1516 NYD_LEAVE;
1517 return rv;
1520 #ifdef HAVE_FCHDIR
1521 FL enum okay
1522 cwget(struct cw *cw)
1524 enum okay rv = STOP;
1525 NYD_ENTER;
1527 if ((cw->cw_fd = open(".", O_RDONLY)) == -1)
1528 goto jleave;
1529 if (fchdir(cw->cw_fd) == -1) {
1530 close(cw->cw_fd);
1531 goto jleave;
1533 rv = OKAY;
1534 jleave:
1535 NYD_LEAVE;
1536 return rv;
1539 FL enum okay
1540 cwret(struct cw *cw)
1542 enum okay rv = STOP;
1543 NYD_ENTER;
1545 if (!fchdir(cw->cw_fd))
1546 rv = OKAY;
1547 NYD_LEAVE;
1548 return rv;
1551 FL void
1552 cwrelse(struct cw *cw)
1554 NYD_ENTER;
1555 close(cw->cw_fd);
1556 NYD_LEAVE;
1559 #else /* !HAVE_FCHDIR */
1560 FL enum okay
1561 cwget(struct cw *cw)
1563 enum okay rv = STOP;
1564 NYD_ENTER;
1566 if (getcwd(cw->cw_wd, sizeof cw->cw_wd) != NULL && !chdir(cw->cw_wd))
1567 rv = OKAY;
1568 NYD_LEAVE;
1569 return rv;
1572 FL enum okay
1573 cwret(struct cw *cw)
1575 enum okay rv = STOP;
1576 NYD_ENTER;
1578 if (!chdir(cw->cw_wd))
1579 rv = OKAY;
1580 NYD_LEAVE;
1581 return rv;
1584 FL void
1585 cwrelse(struct cw *cw)
1587 NYD_ENTER;
1588 UNUSED(cw);
1589 NYD_LEAVE;
1591 #endif /* !HAVE_FCHDIR */
1593 FL char *
1594 colalign(char const *cp, int col, int fill, int *cols_decr_used_or_null)
1596 /* Unicode: how to isolate RIGHT-TO-LEFT scripts via *headline-bidi*
1597 * 1.1 (Jun 1993): U+200E (E2 80 8E) LEFT-TO-RIGHT MARK
1598 * 6.3 (Sep 2013): U+2068 (E2 81 A8) FIRST STRONG ISOLATE,
1599 * U+2069 (E2 81 A9) POP DIRECTIONAL ISOLATE */
1600 #ifdef HAVE_UNICODE
1601 char const _bire[2][6] = { "\xE2\x81\xA8" "\xE2\x81\xA9",
1602 "\xE2\x80\x8E" "\xE2\x80\x8E"
1603 /* worse results: U+202D "\xE2\x80\xAD" U+202C "\xE2\x80\xAC" */
1604 }, *birep = NULL /* old gcc warning */;
1605 #endif
1606 int col_orig = col, n, sz;
1607 bool_t isbidi, isuni, isrepl;
1608 char *nb, *np;
1609 NYD_ENTER;
1611 /* Bidi only on request and when there is 8-bit data */
1612 isbidi = isuni = FAL0;
1613 #ifdef HAVE_UNICODE
1614 if ((isuni = ((options & OPT_UNICODE) != 0))) {
1615 if ((nb = ok_vlook(headline_bidi)) == NULL)
1616 goto jnobidi;
1617 for (birep = cp;; ++birep) {
1618 if (*birep == '\0')
1619 goto jnobidi;
1620 else if (*birep & (char)0x80) {
1621 /* TODO Checking for BIDI character: use S-CText fromutf8
1622 * TODO plus isrighttoleft (or whatever there will be)! */
1623 ui32_t c, x = (ui8_t)*birep;
1624 if ((x & 0xE0) == 0xC0) {
1625 c = x & ~0xC0;
1626 if ((x = (ui8_t)*++birep) == '\0')
1627 goto jnobidi;
1628 } else if ((x & 0xF0) == 0xE0) {
1629 c = x & ~0xE0;
1630 if ((x = (ui8_t)*++birep) == '\0' || *++birep == '\0')
1631 goto jnobidi;
1632 c <<= 6;
1633 c |= x & 0x7F;
1634 x = (ui8_t)*birep;
1635 } else {
1636 c = x & ~0xF0;
1637 if ((x = (ui8_t)*++birep) == '\0' ||
1638 *++birep == '\0' || birep[1] == '\0')
1639 goto jnobidi;
1640 c <<= 6;
1641 c |= x & 0x7F;
1642 c <<= 6;
1643 c |= (x = (ui8_t)*birep) & 0x7F;
1644 x = (ui8_t)*++birep;
1646 c <<= 6;
1647 c |= x & 0x7F;
1648 /* (Very very fuzzy, awaiting S-CText for good) */
1649 if ((c >= 0x05BE && c <= 0x08E3) ||
1650 (c >= 0xFB1D && c <= 0xFEFC) ||
1651 (c >= 0x10800 && c <= 0x10C48) ||
1652 (c >= 0x1EE00 && c <= 0x1EEF1))
1653 break;
1657 isbidi = TRU1;
1658 birep = _bire[0];
1659 switch (*nb) {
1660 case '3': col -= 2;
1661 case '2': birep = _bire[1];
1662 break;
1663 case '1': col -= 2;
1664 default: break;
1666 if (col < 0)
1667 col = 0;
1669 jnobidi:
1670 #endif /* HAVE_UNICODE */
1672 np = nb = salloc(mb_cur_max * strlen(cp) +
1673 ((fill ? col : 0) + (isbidi ? sizeof(_bire[0]) : 0) +1));
1675 #ifdef HAVE_UNICODE
1676 if (isbidi) {
1677 np[0] = birep[0];
1678 np[1] = birep[1];
1679 np[2] = birep[2];
1680 np += 3;
1682 #endif
1684 while (*cp != '\0') {
1685 #ifdef HAVE_C90AMEND1
1686 if (mb_cur_max > 1) {
1687 wchar_t wc;
1689 n = 1;
1690 isrepl = TRU1;
1691 if ((sz = mbtowc(&wc, cp, mb_cur_max)) == -1)
1692 sz = 1;
1693 else if (iswprint(wc)) {
1694 # ifndef HAVE_WCWIDTH
1695 n = 1 + (wc >= 0x1100u); /* TODO use S-CText isfullwidth() */
1696 # else
1697 if ((n = wcwidth(wc)) == -1)
1698 n = 1;
1699 else
1700 # endif
1701 isrepl = FAL0;
1703 } else
1704 #endif
1705 n = sz = 1,
1706 isrepl = !isprint(*cp);
1708 if (n > col)
1709 break;
1710 col -= n;
1712 if (isrepl) {
1713 if (isuni) {
1714 np[0] = (char)0xEFu;
1715 np[1] = (char)0xBFu;
1716 np[2] = (char)0xBDu;
1717 np += 3;
1718 } else
1719 *np++ = '?';
1720 cp += sz;
1721 } else if (sz == 1 && spacechar(*cp)) {
1722 *np++ = ' ';
1723 ++cp;
1724 } else
1725 while (sz--)
1726 *np++ = *cp++;
1729 if (fill && col != 0) {
1730 if (fill > 0) {
1731 memmove(nb + col, nb, PTR2SIZE(np - nb));
1732 memset(nb, ' ', col);
1733 } else
1734 memset(np, ' ', col);
1735 np += col;
1736 col = 0;
1739 #ifdef HAVE_UNICODE
1740 if (isbidi) {
1741 np[0] = birep[3];
1742 np[1] = birep[4];
1743 np[2] = birep[5];
1744 np += 3;
1746 #endif
1748 *np = '\0';
1749 if (cols_decr_used_or_null != NULL)
1750 *cols_decr_used_or_null -= col_orig - col;
1751 NYD_LEAVE;
1752 return nb;
1755 FL void
1756 makeprint(struct str const *in, struct str *out)
1758 static int print_all_chars = -1;
1760 char const *inp, *maxp;
1761 char *outp;
1762 DBG( size_t msz; )
1763 NYD_ENTER;
1765 if (print_all_chars == -1)
1766 print_all_chars = ok_blook(print_all_chars);
1768 out->s = outp = smalloc(DBG( msz = ) in->l*mb_cur_max + 2u*mb_cur_max);
1769 inp = in->s;
1770 maxp = inp + in->l;
1772 if (print_all_chars) {
1773 out->l = in->l;
1774 memcpy(outp, inp, out->l);
1775 goto jleave;
1778 #ifdef HAVE_C90AMEND1
1779 if (mb_cur_max > 1) {
1780 char mbb[MB_LEN_MAX + 1];
1781 wchar_t wc;
1782 int i, n;
1783 bool_t isuni = ((options & OPT_UNICODE) != 0);
1785 out->l = 0;
1786 while (inp < maxp) {
1787 if (*inp & 0200)
1788 n = mbtowc(&wc, inp, PTR2SIZE(maxp - inp));
1789 else {
1790 wc = *inp;
1791 n = 1;
1793 if (n == -1) {
1794 /* FIXME Why mbtowc() resetting here?
1795 * FIXME what about ISO 2022-JP plus -- those
1796 * FIXME will loose shifts, then!
1797 * FIXME THUS - we'd need special "known points"
1798 * FIXME to do so - say, after a newline!!
1799 * FIXME WE NEED TO CHANGE ALL USES +MBLEN! */
1800 mbtowc(&wc, NULL, mb_cur_max);
1801 wc = isuni ? 0xFFFD : '?';
1802 n = 1;
1803 } else if (n == 0)
1804 n = 1;
1805 inp += n;
1806 if (!iswprint(wc) && wc != '\n' && wc != '\r' && wc != '\b' &&
1807 wc != '\t') {
1808 if ((wc & ~(wchar_t)037) == 0)
1809 wc = isuni ? 0x2400 | wc : '?';
1810 else if (wc == 0177)
1811 wc = isuni ? 0x2421 : '?';
1812 else
1813 wc = isuni ? 0x2426 : '?';
1815 if ((n = wctomb(mbb, wc)) <= 0)
1816 continue;
1817 out->l += n;
1818 assert(out->l < msz);
1819 for (i = 0; i < n; ++i)
1820 *outp++ = mbb[i];
1822 } else
1823 #endif /* C90AMEND1 */
1825 int c;
1826 while (inp < maxp) {
1827 c = *inp++ & 0377;
1828 if (!isprint(c) && c != '\n' && c != '\r' && c != '\b' && c != '\t')
1829 c = '?';
1830 *outp++ = c;
1832 out->l = in->l;
1834 jleave:
1835 out->s[out->l] = '\0';
1836 NYD_LEAVE;
1839 FL char *
1840 prstr(char const *s)
1842 struct str in, out;
1843 char *rp;
1844 NYD_ENTER;
1846 in.s = UNCONST(s);
1847 in.l = strlen(s);
1848 makeprint(&in, &out);
1849 rp = savestrbuf(out.s, out.l);
1850 free(out.s);
1851 NYD_LEAVE;
1852 return rp;
1855 FL int
1856 prout(char const *s, size_t sz, FILE *fp)
1858 struct str in, out;
1859 int n;
1860 NYD_ENTER;
1862 in.s = UNCONST(s);
1863 in.l = sz;
1864 makeprint(&in, &out);
1865 n = fwrite(out.s, 1, out.l, fp);
1866 free(out.s);
1867 NYD_LEAVE;
1868 return n;
1871 FL size_t
1872 putuc(int u, int c, FILE *fp)
1874 size_t rv;
1875 UNUSED(u);
1876 NYD_ENTER;
1878 #ifdef HAVE_C90AMEND1
1879 if ((options & OPT_UNICODE) && (u & ~(wchar_t)0177)) {
1880 char mbb[MB_LEN_MAX];
1881 int i, n;
1883 if ((n = wctomb(mbb, u)) > 0) {
1884 rv = wcwidth(u);
1885 for (i = 0; i < n; ++i)
1886 if (putc(mbb[i] & 0377, fp) == EOF) {
1887 rv = 0;
1888 break;
1890 } else if (n == 0)
1891 rv = (putc('\0', fp) != EOF);
1892 else
1893 rv = 0;
1894 } else
1895 #endif
1896 rv = (putc(c, fp) != EOF);
1897 NYD_LEAVE;
1898 return rv;
1901 #ifdef HAVE_COLOUR
1902 FL void
1903 colour_table_create(char const *pager_used)
1905 union {char *cp; char const *ccp; void *vp; struct colour_table *ctp;} u;
1906 size_t i;
1907 struct colour_table *ct;
1908 NYD_ENTER;
1910 if (ok_blook(colour_disable))
1911 goto jleave;
1913 /* If pager, check wether it is allowed to use colour */
1914 if (pager_used != NULL) {
1915 char *pager;
1917 if ((u.cp = ok_vlook(colour_pagers)) == NULL)
1918 u.ccp = COLOUR_PAGERS;
1919 pager = savestr(u.cp);
1921 while ((u.cp = n_strsep(&pager, ',', TRU1)) != NULL)
1922 if (strstr(pager_used, u.cp) != NULL)
1923 goto jok;
1924 goto jleave;
1927 /* $TERM is different in that we default to false unless whitelisted */
1929 char *term, *okterms;
1931 /* Don't use getenv(), but force copy-in into our own tables.. */
1932 if ((term = _var_voklook("TERM")) == NULL)
1933 goto jleave;
1934 if ((okterms = ok_vlook(colour_terms)) == NULL)
1935 okterms = UNCONST(COLOUR_TERMS);
1936 okterms = savestr(okterms);
1938 i = strlen(term);
1939 while ((u.cp = n_strsep(&okterms, ',', TRU1)) != NULL)
1940 if (!strncmp(u.cp, term, i))
1941 goto jok;
1942 goto jleave;
1945 jok:
1946 colour_table = ct = salloc(sizeof *ct); /* XXX lex.c yet resets (FILTER!) */
1947 { static struct {
1948 enum okeys okey;
1949 enum colourspec cspec;
1950 char const *defval;
1951 } const map[] = {
1952 {ok_v_colour_msginfo, COLOURSPEC_MSGINFO, COLOUR_MSGINFO},
1953 {ok_v_colour_partinfo, COLOURSPEC_PARTINFO, COLOUR_PARTINFO},
1954 {ok_v_colour_from_, COLOURSPEC_FROM_, COLOUR_FROM_},
1955 {ok_v_colour_header, COLOURSPEC_HEADER, COLOUR_HEADER},
1956 {ok_v_colour_uheader, COLOURSPEC_UHEADER, COLOUR_UHEADER}
1959 for (i = 0; i < NELEM(map); ++i) {
1960 if ((u.cp = _var_oklook(map[i].okey)) == NULL)
1961 u.ccp = map[i].defval;
1962 u.cp = _colour_iso6429(u.ccp);
1963 ct->ct_csinfo[map[i].cspec].l = strlen(u.cp);
1964 ct->ct_csinfo[map[i].cspec].s = u.cp;
1967 ct->ct_csinfo[COLOURSPEC_RESET].l = sizeof("\033[0m") -1;
1968 ct->ct_csinfo[COLOURSPEC_RESET].s = UNCONST("\033[0m");
1970 if ((u.cp = ok_vlook(colour_user_headers)) == NULL)
1971 u.ccp = COLOUR_USER_HEADERS;
1972 ct->ct_csinfo[COLOURSPEC_RESET + 1].l = i = strlen(u.ccp);
1973 ct->ct_csinfo[COLOURSPEC_RESET + 1].s = (i == 0) ? NULL : savestr(u.ccp);
1974 jleave:
1975 NYD_LEAVE;
1978 FL void
1979 colour_put(FILE *fp, enum colourspec cs)
1981 NYD_ENTER;
1982 if (colour_table != NULL) {
1983 struct str const *cp = colour_get(cs);
1985 fwrite(cp->s, cp->l, 1, fp);
1987 NYD_LEAVE;
1990 FL void
1991 colour_put_header(FILE *fp, char const *name)
1993 enum colourspec cs = COLOURSPEC_HEADER;
1994 struct str const *uheads;
1995 char *cp, *cp_base, *x;
1996 size_t namelen;
1997 NYD_ENTER;
1999 if (colour_table == NULL)
2000 goto j_leave;
2001 /* Normal header colours if there are no user headers */
2002 uheads = colour_table->ct_csinfo + COLOURSPEC_RESET + 1;
2003 if (uheads->s == NULL)
2004 goto jleave;
2006 /* Iterate over all entries in the *colour-user-headers* list */
2007 cp = ac_alloc(uheads->l +1);
2008 memcpy(cp, uheads->s, uheads->l +1);
2009 cp_base = cp;
2010 namelen = strlen(name);
2011 while ((x = n_strsep(&cp, ',', TRU1)) != NULL) {
2012 size_t l = (cp != NULL) ? PTR2SIZE(cp - x) - 1 : strlen(x);
2013 if (l == namelen && !ascncasecmp(x, name, namelen)) {
2014 cs = COLOURSPEC_UHEADER;
2015 break;
2018 ac_free(cp_base);
2019 jleave:
2020 colour_put(fp, cs);
2021 j_leave:
2022 NYD_LEAVE;
2025 FL void
2026 colour_reset(FILE *fp)
2028 NYD_ENTER;
2029 if (colour_table != NULL)
2030 fwrite("\033[0m", 4, 1, fp);
2031 NYD_LEAVE;
2034 FL struct str const *
2035 colour_get(enum colourspec cs)
2037 struct str const *rv = NULL;
2038 NYD_ENTER;
2040 if (colour_table != NULL)
2041 if ((rv = colour_table->ct_csinfo + cs)->s == NULL)
2042 rv = NULL;
2043 NYD_LEAVE;
2044 return rv;
2046 #endif /* HAVE_COLOUR */
2048 FL void
2049 time_current_update(struct time_current *tc, bool_t full_update)
2051 NYD_ENTER;
2052 tc->tc_time = time(NULL);
2053 if (full_update) {
2054 memcpy(&tc->tc_gm, gmtime(&tc->tc_time), sizeof tc->tc_gm);
2055 memcpy(&tc->tc_local, localtime(&tc->tc_time), sizeof tc->tc_local);
2056 sstpcpy(tc->tc_ctime, ctime(&tc->tc_time));
2058 NYD_LEAVE;
2061 static void
2062 _out_of_memory(void)
2064 panic("no memory");
2067 #ifndef HAVE_DEBUG
2068 FL void *
2069 smalloc(size_t s SMALLOC_DEBUG_ARGS)
2071 void *rv;
2072 NYD_ENTER;
2074 if (s == 0)
2075 s = 1;
2076 if ((rv = malloc(s)) == NULL)
2077 _out_of_memory();
2078 NYD_LEAVE;
2079 return rv;
2082 FL void *
2083 srealloc(void *v, size_t s SMALLOC_DEBUG_ARGS)
2085 void *rv;
2086 NYD_ENTER;
2088 if (s == 0)
2089 s = 1;
2090 if (v == NULL)
2091 rv = smalloc(s);
2092 else if ((rv = realloc(v, s)) == NULL)
2093 _out_of_memory();
2094 NYD_LEAVE;
2095 return rv;
2098 FL void *
2099 scalloc(size_t nmemb, size_t size SMALLOC_DEBUG_ARGS)
2101 void *rv;
2102 NYD_ENTER;
2104 if (size == 0)
2105 size = 1;
2106 if ((rv = calloc(nmemb, size)) == NULL)
2107 _out_of_memory();
2108 NYD_LEAVE;
2109 return rv;
2112 #else /* !HAVE_DEBUG */
2113 CTA(sizeof(char) == sizeof(ui8_t));
2115 # define _HOPE_SIZE (2 * 8 * sizeof(char))
2116 # define _HOPE_SET(C) \
2117 do {\
2118 union mem_ptr __xl, __xu;\
2119 struct mem_chunk *__xc;\
2120 __xl.p_p = (C).p_p;\
2121 __xc = __xl.p_c - 1;\
2122 __xu.p_p = __xc;\
2123 (C).p_cp += 8;\
2124 __xl.p_ui8p[0]=0xDE; __xl.p_ui8p[1]=0xAA;\
2125 __xl.p_ui8p[2]=0x55; __xl.p_ui8p[3]=0xAD;\
2126 __xl.p_ui8p[4]=0xBE; __xl.p_ui8p[5]=0x55;\
2127 __xl.p_ui8p[6]=0xAA; __xl.p_ui8p[7]=0xEF;\
2128 __xu.p_ui8p += __xc->mc_size - 8;\
2129 __xu.p_ui8p[0]=0xDE; __xu.p_ui8p[1]=0xAA;\
2130 __xu.p_ui8p[2]=0x55; __xu.p_ui8p[3]=0xAD;\
2131 __xu.p_ui8p[4]=0xBE; __xu.p_ui8p[5]=0x55;\
2132 __xu.p_ui8p[6]=0xAA; __xu.p_ui8p[7]=0xEF;\
2133 } while (0)
2134 # define _HOPE_GET_TRACE(C,BAD) \
2135 do {\
2136 (C).p_cp += 8;\
2137 _HOPE_GET(C, BAD);\
2138 (C).p_cp += 8;\
2139 } while(0)
2140 # define _HOPE_GET(C,BAD) \
2141 do {\
2142 union mem_ptr __xl, __xu;\
2143 struct mem_chunk *__xc;\
2144 ui32_t __i;\
2145 __xl.p_p = (C).p_p;\
2146 __xl.p_cp -= 8;\
2147 (C).p_cp = __xl.p_cp;\
2148 __xc = __xl.p_c - 1;\
2149 (BAD) = FAL0;\
2150 __i = 0;\
2151 if (__xl.p_ui8p[0] != 0xDE) __i |= 1<<0;\
2152 if (__xl.p_ui8p[1] != 0xAA) __i |= 1<<1;\
2153 if (__xl.p_ui8p[2] != 0x55) __i |= 1<<2;\
2154 if (__xl.p_ui8p[3] != 0xAD) __i |= 1<<3;\
2155 if (__xl.p_ui8p[4] != 0xBE) __i |= 1<<4;\
2156 if (__xl.p_ui8p[5] != 0x55) __i |= 1<<5;\
2157 if (__xl.p_ui8p[6] != 0xAA) __i |= 1<<6;\
2158 if (__xl.p_ui8p[7] != 0xEF) __i |= 1<<7;\
2159 if (__i != 0) {\
2160 (BAD) = TRU1;\
2161 alert("%p: corrupt lower canary: 0x%02X: %s, line %u",\
2162 __xl.p_p, __i, mdbg_file, mdbg_line);\
2164 __xu.p_p = __xc;\
2165 __xu.p_ui8p += __xc->mc_size - 8;\
2166 __i = 0;\
2167 if (__xu.p_ui8p[0] != 0xDE) __i |= 1<<0;\
2168 if (__xu.p_ui8p[1] != 0xAA) __i |= 1<<1;\
2169 if (__xu.p_ui8p[2] != 0x55) __i |= 1<<2;\
2170 if (__xu.p_ui8p[3] != 0xAD) __i |= 1<<3;\
2171 if (__xu.p_ui8p[4] != 0xBE) __i |= 1<<4;\
2172 if (__xu.p_ui8p[5] != 0x55) __i |= 1<<5;\
2173 if (__xu.p_ui8p[6] != 0xAA) __i |= 1<<6;\
2174 if (__xu.p_ui8p[7] != 0xEF) __i |= 1<<7;\
2175 if (__i != 0) {\
2176 (BAD) = TRU1;\
2177 alert("%p: corrupt upper canary: 0x%02X: %s, line %u",\
2178 __xl.p_p, __i, mdbg_file, mdbg_line);\
2180 if (BAD)\
2181 alert(" ..canary last seen: %s, line %u",\
2182 __xc->mc_file, __xc->mc_line);\
2183 } while (0)
2185 FL void *
2186 (smalloc)(size_t s SMALLOC_DEBUG_ARGS)
2188 union mem_ptr p;
2189 NYD_ENTER;
2191 if (s == 0)
2192 s = 1;
2193 s += sizeof(struct mem_chunk) + _HOPE_SIZE;
2195 if ((p.p_p = (malloc)(s)) == NULL)
2196 _out_of_memory();
2197 p.p_c->mc_prev = NULL;
2198 if ((p.p_c->mc_next = _mem_list) != NULL)
2199 _mem_list->mc_prev = p.p_c;
2200 p.p_c->mc_file = mdbg_file;
2201 p.p_c->mc_line = (ui16_t)mdbg_line;
2202 p.p_c->mc_isfree = FAL0;
2203 p.p_c->mc_size = (ui32_t)s;
2204 _mem_list = p.p_c++;
2205 _HOPE_SET(p);
2207 ++_mem_aall;
2208 ++_mem_acur;
2209 _mem_amax = MAX(_mem_amax, _mem_acur);
2210 _mem_mall += s;
2211 _mem_mcur += s;
2212 _mem_mmax = MAX(_mem_mmax, _mem_mcur);
2213 NYD_LEAVE;
2214 return p.p_p;
2217 FL void *
2218 (srealloc)(void *v, size_t s SMALLOC_DEBUG_ARGS)
2220 union mem_ptr p;
2221 bool_t isbad;
2222 NYD_ENTER;
2224 if ((p.p_p = v) == NULL) {
2225 p.p_p = (smalloc)(s, mdbg_file, mdbg_line);
2226 goto jleave;
2229 _HOPE_GET(p, isbad);
2230 --p.p_c;
2231 if (p.p_c->mc_isfree) {
2232 fprintf(stderr, "srealloc(): region freed! At %s, line %d\n"
2233 "\tLast seen: %s, line %d\n",
2234 mdbg_file, mdbg_line, p.p_c->mc_file, p.p_c->mc_line);
2235 goto jforce;
2238 if (p.p_c == _mem_list)
2239 _mem_list = p.p_c->mc_next;
2240 else
2241 p.p_c->mc_prev->mc_next = p.p_c->mc_next;
2242 if (p.p_c->mc_next != NULL)
2243 p.p_c->mc_next->mc_prev = p.p_c->mc_prev;
2245 --_mem_acur;
2246 _mem_mcur -= p.p_c->mc_size;
2247 jforce:
2248 if (s == 0)
2249 s = 1;
2250 s += sizeof(struct mem_chunk) + _HOPE_SIZE;
2252 if ((p.p_p = (realloc)(p.p_c, s)) == NULL)
2253 _out_of_memory();
2254 p.p_c->mc_prev = NULL;
2255 if ((p.p_c->mc_next = _mem_list) != NULL)
2256 _mem_list->mc_prev = p.p_c;
2257 p.p_c->mc_file = mdbg_file;
2258 p.p_c->mc_line = (ui16_t)mdbg_line;
2259 p.p_c->mc_isfree = FAL0;
2260 p.p_c->mc_size = (ui32_t)s;
2261 _mem_list = p.p_c++;
2262 _HOPE_SET(p);
2264 ++_mem_aall;
2265 ++_mem_acur;
2266 _mem_amax = MAX(_mem_amax, _mem_acur);
2267 _mem_mall += s;
2268 _mem_mcur += s;
2269 _mem_mmax = MAX(_mem_mmax, _mem_mcur);
2270 jleave:
2271 NYD_LEAVE;
2272 return p.p_p;
2275 FL void *
2276 (scalloc)(size_t nmemb, size_t size SMALLOC_DEBUG_ARGS)
2278 union mem_ptr p;
2279 NYD_ENTER;
2281 if (size == 0)
2282 size = 1;
2283 if (nmemb == 0)
2284 nmemb = 1;
2285 size *= nmemb;
2286 size += sizeof(struct mem_chunk) + _HOPE_SIZE;
2288 if ((p.p_p = (malloc)(size)) == NULL)
2289 _out_of_memory();
2290 memset(p.p_p, 0, size);
2291 p.p_c->mc_prev = NULL;
2292 if ((p.p_c->mc_next = _mem_list) != NULL)
2293 _mem_list->mc_prev = p.p_c;
2294 p.p_c->mc_file = mdbg_file;
2295 p.p_c->mc_line = (ui16_t)mdbg_line;
2296 p.p_c->mc_isfree = FAL0;
2297 p.p_c->mc_size = (ui32_t)size;
2298 _mem_list = p.p_c++;
2299 _HOPE_SET(p);
2301 ++_mem_aall;
2302 ++_mem_acur;
2303 _mem_amax = MAX(_mem_amax, _mem_acur);
2304 _mem_mall += size;
2305 _mem_mcur += size;
2306 _mem_mmax = MAX(_mem_mmax, _mem_mcur);
2307 NYD_LEAVE;
2308 return p.p_p;
2311 FL void
2312 (sfree)(void *v SMALLOC_DEBUG_ARGS)
2314 union mem_ptr p;
2315 bool_t isbad;
2316 NYD_ENTER;
2318 if ((p.p_p = v) == NULL) {
2319 fprintf(stderr, "sfree(NULL) from %s, line %d\n", mdbg_file, mdbg_line);
2320 goto jleave;
2323 _HOPE_GET(p, isbad);
2324 --p.p_c;
2325 if (p.p_c->mc_isfree) {
2326 fprintf(stderr, "sfree(): double-free avoided at %s, line %d\n"
2327 "\tLast seen: %s, line %d\n",
2328 mdbg_file, mdbg_line, p.p_c->mc_file, p.p_c->mc_line);
2329 goto jleave;
2332 if (p.p_c == _mem_list)
2333 _mem_list = p.p_c->mc_next;
2334 else
2335 p.p_c->mc_prev->mc_next = p.p_c->mc_next;
2336 if (p.p_c->mc_next != NULL)
2337 p.p_c->mc_next->mc_prev = p.p_c->mc_prev;
2338 p.p_c->mc_isfree = TRU1;
2340 --_mem_acur;
2341 _mem_mcur -= p.p_c->mc_size;
2343 if (options & OPT_DEBUG) {
2344 p.p_c->mc_next = _mem_free;
2345 _mem_free = p.p_c;
2346 } else
2347 (free)(p.p_c);
2348 jleave:
2349 NYD_LEAVE;
2352 FL void
2353 smemreset(void)
2355 union mem_ptr p;
2356 size_t c = 0, s = 0;
2357 NYD_ENTER;
2359 for (p.p_c = _mem_free; p.p_c != NULL;) {
2360 void *vp = p.p_c;
2361 ++c;
2362 s += p.p_c->mc_size;
2363 p.p_c = p.p_c->mc_next;
2364 (free)(vp);
2366 _mem_free = NULL;
2368 if (options & OPT_DEBUG)
2369 fprintf(stderr, "smemreset(): freed %" ZFMT " chunks/%" ZFMT " bytes\n",
2370 c, s);
2371 NYD_LEAVE;
2374 FL int
2375 c_smemtrace(void *v)
2377 /* For _HOPE_GET() */
2378 char const * const mdbg_file = "smemtrace()";
2379 int const mdbg_line = -1;
2380 FILE *fp;
2381 union mem_ptr p, xp;
2382 bool_t isbad;
2383 size_t lines;
2384 NYD_ENTER;
2386 v = (void*)0x1;
2387 if ((fp = Ftmp(NULL, "memtr", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
2388 NULL) {
2389 perror("tmpfile");
2390 goto jleave;
2393 fprintf(fp, "Memory statistics:\n"
2394 " Count cur/peek/all: %7" ZFMT "/%7" ZFMT "/%10" ZFMT "\n"
2395 " Bytes cur/peek/all: %7" ZFMT "/%7" ZFMT "/%10" ZFMT "\n\n",
2396 _mem_acur, _mem_amax, _mem_aall, _mem_mcur, _mem_mmax, _mem_mall);
2398 fprintf(fp, "Currently allocated memory chunks:\n");
2399 for (lines = 0, p.p_c = _mem_list; p.p_c != NULL;
2400 ++lines, p.p_c = p.p_c->mc_next) {
2401 xp = p;
2402 ++xp.p_c;
2403 _HOPE_GET_TRACE(xp, isbad);
2404 fprintf(fp, "%s%p (%5" ZFMT " bytes): %s, line %u\n",
2405 (isbad ? "! CANARY ERROR: " : ""), xp.p_p,
2406 (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)), p.p_c->mc_file,
2407 p.p_c->mc_line);
2410 if (options & OPT_DEBUG) {
2411 fprintf(fp, "sfree()d memory chunks awaiting free():\n");
2412 for (p.p_c = _mem_free; p.p_c != NULL; ++lines, p.p_c = p.p_c->mc_next) {
2413 xp = p;
2414 ++xp.p_c;
2415 _HOPE_GET_TRACE(xp, isbad);
2416 fprintf(fp, "%s%p (%5" ZFMT " bytes): %s, line %u\n",
2417 (isbad ? "! CANARY ERROR: " : ""), xp.p_p,
2418 (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)),
2419 p.p_c->mc_file, p.p_c->mc_line);
2423 page_or_print(fp, lines);
2424 Fclose(fp);
2425 v = NULL;
2426 jleave:
2427 NYD_LEAVE;
2428 return (v != NULL);
2431 # ifdef _HAVE_MEMCHECK
2432 FL bool_t
2433 _smemcheck(char const *mdbg_file, int mdbg_line)
2435 union mem_ptr p, xp;
2436 bool_t anybad = FAL0, isbad;
2437 size_t lines;
2438 NYD_ENTER;
2440 for (lines = 0, p.p_c = _mem_list; p.p_c != NULL;
2441 ++lines, p.p_c = p.p_c->mc_next) {
2442 xp = p;
2443 ++xp.p_c;
2444 _HOPE_GET_TRACE(xp, isbad);
2445 if (isbad) {
2446 anybad = TRU1;
2447 fprintf(stderr,
2448 "! CANARY ERROR: %p (%5" ZFMT " bytes): %s, line %u\n",
2449 xp.p_p, (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)),
2450 p.p_c->mc_file, p.p_c->mc_line);
2454 if (options & OPT_DEBUG) {
2455 for (p.p_c = _mem_free; p.p_c != NULL; ++lines, p.p_c = p.p_c->mc_next) {
2456 xp = p;
2457 ++xp.p_c;
2458 _HOPE_GET_TRACE(xp, isbad);
2459 if (isbad) {
2460 anybad = TRU1;
2461 fprintf(stderr,
2462 "! CANARY ERROR: %p (%5" ZFMT " bytes): %s, line %u\n",
2463 xp.p_p, (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)),
2464 p.p_c->mc_file, p.p_c->mc_line);
2468 NYD_LEAVE;
2469 return anybad;
2471 # endif /* _HAVE_MEMCHECK */
2473 # undef _HOPE_SIZE
2474 # undef _HOPE_SET
2475 # undef _HOPE_GET_TRACE
2476 # undef _HOPE_GET
2477 #endif /* HAVE_DEBUG */
2479 /* vim:set fenc=utf-8:s-it-mode */