1 /* GNU Mailutils -- a suite of utilities for electronic mail
2    Copyright (C) 1999-2021 Free Software Foundation, Inc.
3 
4    This library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public
6    License as published by the Free Software Foundation; either
7    version 3 of the License, or (at your option) any later version.
8 
9    This library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Lesser General Public License for more details.
13 
14    You should have received a copy of the GNU Lesser General
15    Public License along with this library.  If not, see
16    <http://www.gnu.org/licenses/>. */
17 
18 #ifdef HAVE_CONFIG_H
19 # include <config.h>
20 #endif
21 
22 #include <errno.h>
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <string.h>
26 
27 #include <mailutils/errno.h>
28 #include <mailutils/error.h>
29 #include <mailutils/nls.h>
30 #include <mailutils/cctype.h>
31 #include <mailutils/wordsplit.h>
32 #include <mailutils/stream.h>
33 #include <mailutils/stdstream.h>
34 #include <mailutils/iterator.h>
35 #include <mailutils/cstr.h>
36 #include <mailutils/io.h>
37 
38 int mu_debug_line_info;          /* Debug messages include source locations */
39 
40 struct debug_category
41 {
42   char *name;
43   mu_debug_level_t level;
44   int isset;
45 };
46 
47 static struct debug_category default_cattab[] = {
48   { "all" },
49 #define __MU_DEBCAT_C_ARRAY
50 #include <mailutils/sys/debcat.h>
51 #undef __MU_DEBCAT_C_ARRAY
52 };
53 
54 static struct debug_category *cattab = default_cattab;
55 static size_t catcnt = MU_ARRAY_SIZE (default_cattab);
56 static size_t catmax = 0;
57 #define MU_DEFAULT_CATMAX 256;
58 
59 size_t
mu_debug_register_category(char * name)60 mu_debug_register_category (char *name)
61 {
62   struct debug_category *newtab;
63   size_t n;
64 
65   if (cattab == default_cattab)
66     {
67       /* Reallocate the array */
68       n = 2 * catcnt;
69 
70       newtab = calloc (n, sizeof (newtab[0]));
71       if (!newtab)
72 	{
73 	  mu_error (_("cannot reallocate debug category table"));
74 	  return (size_t)-1;
75 	}
76       memcpy (newtab, cattab, catcnt * sizeof (cattab[0]));
77       cattab = newtab;
78       catmax = n;
79     }
80   else if (catcnt == catmax)
81     {
82       n = catmax + MU_DEFAULT_CATMAX;
83       newtab = realloc (cattab, n * sizeof (cattab[0]));
84       if (!newtab)
85 	{
86 	  mu_error (_("cannot reallocate debug category table"));
87 	  return (size_t)-1;
88 	}
89       else
90 	{
91 	  cattab = newtab;
92 	  catmax = n;
93 	}
94     }
95   cattab[catcnt].name = name;
96   cattab[catcnt].level = 0;
97   cattab[catcnt].isset = 0;
98   return catcnt++;
99 }
100 
101 size_t
mu_debug_next_handle()102 mu_debug_next_handle ()
103 {
104   return catcnt ? catcnt : 1;
105 }
106 
107 int
mu_debug_level_p(mu_debug_handle_t catn,mu_debug_level_t level)108 mu_debug_level_p (mu_debug_handle_t catn, mu_debug_level_t level)
109 {
110   return catn < catcnt
111     && ((cattab[catn].isset ? cattab[catn].level : cattab[0].level) &
112 	MU_DEBUG_LEVEL_MASK (level));
113 }
114 
115 int
mu_debug_category_match(mu_debug_handle_t catn,mu_debug_level_t mask)116 mu_debug_category_match (mu_debug_handle_t catn, mu_debug_level_t mask)
117 {
118   return catn < catcnt
119     && ((cattab[catn].isset ? cattab[catn].level : cattab[0].level) & mask);
120 }
121 
122 static mu_debug_handle_t
find_category(const char * name,size_t len)123 find_category (const char *name, size_t len)
124 {
125   mu_debug_handle_t i;
126 
127   for (i = 0; i < catcnt; i++)
128     {
129       if (strlen (cattab[i].name) == len &&
130 	  memcmp (cattab[i].name, name, len) == 0)
131 	return i;
132     }
133   return (mu_debug_handle_t)-1;
134 }
135 
136 void
mu_debug_enable_category(const char * catname,size_t catlen,mu_debug_level_t level)137 mu_debug_enable_category (const char *catname, size_t catlen,
138 			  mu_debug_level_t level)
139 {
140   mu_debug_handle_t catn = find_category (catname, catlen);
141   if (catn == (mu_debug_handle_t)-1)
142     {
143       mu_error (_("unknown category: %.*s"), (int) catlen, catname);
144       return;
145     }
146   cattab[catn].level = level;
147   cattab[catn].isset = 1;
148 }
149 
150 void
mu_debug_disable_category(const char * catname,size_t catlen)151 mu_debug_disable_category (const char *catname, size_t catlen)
152 {
153   size_t catn = find_category (catname, catlen);
154   if (catn == (mu_debug_handle_t)-1)
155     {
156       mu_error (_("unknown category: %.*s"), (int) catlen, catname);
157       return;
158     }
159   cattab[catn].isset = 0;
160 }
161 
162 int
mu_debug_category_level(const char * catname,size_t catlen,mu_debug_level_t * plev)163 mu_debug_category_level (const char *catname, size_t catlen,
164 			 mu_debug_level_t *plev)
165 {
166   mu_debug_handle_t catn;
167 
168   if (catname)
169     {
170       catn = find_category (catname, catlen);
171       if (catn == (mu_debug_handle_t)-1)
172 	return MU_ERR_NOENT;
173     }
174   else
175     catn = 0;
176   *plev = cattab[catn].level;
177   return 0;
178 }
179 
180 int
mu_debug_set_category_level(mu_debug_handle_t catn,mu_debug_level_t level)181 mu_debug_set_category_level (mu_debug_handle_t catn, mu_debug_level_t level)
182 {
183   if (catn < catcnt)
184     {
185       cattab[catn].isset = 1;
186       cattab[catn].level = level;
187       return 0;
188     }
189   return MU_ERR_NOENT;
190 }
191 
192 int
mu_debug_get_category_level(mu_debug_handle_t catn,mu_debug_level_t * plev)193 mu_debug_get_category_level (mu_debug_handle_t catn, mu_debug_level_t *plev)
194 {
195   if (catn < catcnt)
196     {
197       if (!cattab[catn].isset)
198 	*plev = 0;
199       else
200 	*plev = cattab[catn].level;
201       return 0;
202     }
203   return MU_ERR_NOENT;
204 }
205 
206 static char *mu_debug_level_str[] = {
207   "error",
208   "trace0",
209   "trace1",
210   "trace2",
211   "trace3",
212   "trace4",
213   "trace5",
214   "trace6",
215   "trace7",
216   "trace8",
217   "trace9",
218   "prot"
219 };
220 
221 static int
mu_debug_level_from_string(const char * str,mu_debug_level_t * lev,char ** endp)222 mu_debug_level_from_string (const char *str, mu_debug_level_t *lev,
223 			    char **endp)
224 {
225   int i;
226   const char *p;
227   char *q;
228 
229   for (i = 0; i < MU_ARRAY_SIZE (mu_debug_level_str); i++)
230     {
231       for (p = str, q = mu_debug_level_str[i]; ; p++, q++)
232 	{
233 	  if (!*q)
234 	    {
235 	      if (endp)
236 		*endp = (char*) p;
237 	      *lev = i;
238 	      return 0;
239 	    }
240 	  if (*q != *p)
241 	    break;
242 	}
243     }
244   return MU_ERR_NOENT;
245 }
246 
247 static void
parse_spec(const char * spec)248 parse_spec (const char *spec)
249 {
250   char *q;
251   mu_debug_level_t level;
252 
253   if (mu_isdigit (*spec))
254     {
255       level = strtoul (spec, &q, 0);
256       if (*q)
257 	mu_error (_("%s: wrong debug spec near %s"), spec, q);
258       else
259 	{
260 	  cattab[0].level = level;
261 	  cattab[0].isset = 1;
262 	}
263       return;
264     }
265 
266   if (*spec == '!')
267     mu_debug_disable_category (spec + 1, strlen (spec + 1));
268   else
269     {
270       char *p;
271       size_t len;
272 
273       p = strchr (spec, '.');
274       if (p)
275 	{
276 	  struct mu_wordsplit ws;
277 
278 	  len = p - spec;
279 	  ws.ws_delim = ",";
280 	  if (mu_wordsplit (p + 1, &ws,
281 			    MU_WRDSF_DELIM|MU_WRDSF_WS|
282 			    MU_WRDSF_NOVAR|MU_WRDSF_NOCMD))
283 	    {
284 	      mu_error (_("cannot split line `%s': %s"), p + 1,
285 			mu_wordsplit_strerror (&ws));
286 	      return;
287 	    }
288 	  else
289 	    {
290 	      size_t i;
291 	      mu_debug_level_t lev = 0;
292 	      mu_debug_level_t xlev = 0;
293 	      char *end;
294 
295 	      for (i = 0; i < ws.ws_wordc; i++)
296 		{
297 		  char *s = ws.ws_wordv[i];
298 		  int exact = 0;
299 		  mu_debug_level_t n;
300 		  mu_debug_level_t *tgt = &lev;
301 
302 		  if (*s == '!')
303 		    {
304 		      if (i == 0)
305 			lev = MU_DEBUG_LEVEL_UPTO (MU_DEBUG_PROT);
306 		      tgt = &xlev;
307 		      s++;
308 		    }
309 		  if (*s == '=')
310 		    {
311 		      exact = 1;
312 		      s++;
313 		    }
314 
315 		  if (mu_debug_level_from_string (s, &n, &end))
316 		    {
317 		      mu_error (_("unknown level `%s'"), s);
318 		      continue;
319 		    }
320 		  else if (*end == '-')
321 		    {
322 		      mu_debug_level_t l;
323 
324 		      s = end + 1;
325 		      if (mu_debug_level_from_string (s, &l, &end))
326 			{
327 			  mu_error (_("unknown level `%s'"), s);
328 			  continue;
329 			}
330 		      else if (*end)
331 			{
332 			  mu_error (_("invalid level: %s"), s);
333 			  continue;
334 			}
335 		      if (n < l)
336 			*tgt |= MU_DEBUG_LEVEL_RANGE (n, l);
337 		      else
338 			*tgt |= MU_DEBUG_LEVEL_RANGE (l, n);
339 		    }
340 		  else if (*end)
341 		    {
342 		      mu_error (_("invalid level: %s"), s);
343 		      continue;
344 		    }
345 		  else if (exact)
346 		    *tgt |= MU_DEBUG_LEVEL_MASK (n);
347 		  else
348 		    *tgt |= MU_DEBUG_LEVEL_UPTO (n);
349 		}
350 
351 	      level = lev & ~xlev;
352 	    }
353 	}
354       else
355 	{
356 	  len = strlen (spec);
357 	  level = MU_DEBUG_LEVEL_UPTO (MU_DEBUG_PROT);
358 	}
359       mu_debug_enable_category (spec, len, level);
360     }
361 }
362 
363 void
mu_debug_parse_spec(const char * spec)364 mu_debug_parse_spec (const char *spec)
365 {
366   struct mu_wordsplit ws;
367 
368   ws.ws_delim = ";";
369   if (mu_wordsplit (spec, &ws,
370 		    MU_WRDSF_DELIM|MU_WRDSF_WS|
371 		    MU_WRDSF_NOVAR|MU_WRDSF_NOCMD))
372     {
373       mu_error (_("cannot split line `%s': %s"), spec,
374 		mu_wordsplit_strerror (&ws));
375     }
376   else
377     {
378       size_t i;
379 
380       for (i = 0; i < ws.ws_wordc; i++)
381 	parse_spec (ws.ws_wordv[i]);
382       mu_wordsplit_free (&ws);
383     }
384 }
385 
386 void
mu_debug_clear_all()387 mu_debug_clear_all ()
388 {
389   int i;
390 
391   for (i = 0; i < catcnt; i++)
392     cattab[i].isset = 0;
393 }
394 
395 #define _LEVEL_ALL MU_DEBUG_LEVEL_UPTO(MU_DEBUG_PROT)
396 
397 static int
name_matches(char ** names,char * str)398 name_matches (char **names, char *str)
399 {
400   int i;
401 
402   for (i = 0; names[i]; i++)
403     if (strcmp (names[i], str) == 0)
404       return 1;
405   return 0;
406 }
407 
408 int
mu_debug_format_spec(mu_stream_t str,const char * names,int showunset)409 mu_debug_format_spec (mu_stream_t str, const char *names, int showunset)
410 {
411   int i;
412   size_t cnt = 0;
413   int rc = 0;
414   struct mu_wordsplit ws;
415 
416   if (names)
417     {
418       ws.ws_delim = ";";
419       if (mu_wordsplit (names, &ws,
420 			MU_WRDSF_DELIM|MU_WRDSF_WS|
421 			MU_WRDSF_NOVAR|MU_WRDSF_NOCMD))
422 	return errno;
423     }
424 
425   for (i = 0; i < catcnt; i++)
426     {
427       if (names && !name_matches (ws.ws_wordv, cattab[i].name))
428 	continue;
429 
430       if (cattab[i].isset && cattab[i].level)
431 	{
432 	  if (cnt)
433 	    {
434 	      rc = mu_stream_printf(str, ";");
435 	      if (rc)
436 		break;
437 	    }
438 	  rc = mu_stream_printf (str, "%s", cattab[i].name);
439 	  if (rc)
440 	    break;
441 	  if (cattab[i].level != _LEVEL_ALL)
442 	    {
443 	      mu_debug_level_t j = MU_DEBUG_ERROR, minl, maxl;
444 	      int delim = '.';
445 
446 	      while (1)
447 		{
448 		  /* Find the least bit set */
449 		  for (; j <= MU_DEBUG_PROT; j++)
450 		    if (cattab[i].level & MU_DEBUG_LEVEL_MASK (j))
451 		      break;
452 
453 		  if (j > MU_DEBUG_PROT)
454 		    break;
455 
456 		  minl = j;
457 
458 		  for (; j + 1 <= MU_DEBUG_PROT &&
459 			 cattab[i].level & MU_DEBUG_LEVEL_MASK (j + 1);
460 		       j++)
461 		    ;
462 
463 		  maxl = j++;
464 
465 		  if (minl == maxl)
466 		    rc = mu_stream_printf (str, "%c=%s", delim,
467 					   mu_debug_level_str[minl]);
468 		  else if (minl == 0)
469 		    rc = mu_stream_printf (str, "%c%s", delim,
470 					   mu_debug_level_str[maxl]);
471 		  else
472 		    rc = mu_stream_printf (str, "%c%s-%s", delim,
473 					   mu_debug_level_str[minl],
474 					   mu_debug_level_str[maxl]);
475 		  if (rc)
476 		    break;
477 		  delim = ',';
478 		}
479 	    }
480 	  cnt++;
481 	}
482       else if (showunset)
483 	{
484 	  if (cnt)
485 	    {
486 	      rc = mu_stream_printf(str, ";");
487 	      if (rc)
488 		break;
489 	    }
490 	  rc = mu_stream_printf(str, "!%s", cattab[i].name);
491 	  if (rc)
492 	    break;
493 	  cnt++;
494 	}
495     }
496   if (names)
497     mu_wordsplit_free (&ws);
498   return rc;
499 }
500 
501 
502 /* Iterator */
503 
504 static mu_iterator_t iterator_head;
505 
506 #define ITR_BACKWARDS 0x01
507 #define ITR_SKIPUNSET 0x02
508 #define ITR_FINISHED  0x04
509 
510 struct debug_iterator
511 {
512   size_t pos;
513   int flags;
514 };
515 
516 static int
first(void * owner)517 first (void *owner)
518 {
519   struct debug_iterator *itr = owner;
520   itr->flags &= ~ITR_FINISHED;
521   if (itr->flags & ITR_BACKWARDS)
522     itr->pos = catcnt - 1;
523   else
524     itr->pos = 0;
525   return 0;
526 }
527 
528 static int
next(void * owner)529 next (void *owner)
530 {
531   struct debug_iterator *itr = owner;
532   itr->flags &= ~ITR_FINISHED;
533   do
534     {
535       if (itr->flags & ITR_BACKWARDS)
536 	{
537 	  if (itr->pos)
538 	    itr->pos--;
539 	  else
540 	    itr->flags |= ITR_FINISHED;
541 	}
542       else
543 	{
544 	  if (itr->pos < catcnt - 1)
545 	    itr->pos++;
546 	  else
547 	    itr->flags |= ITR_FINISHED;
548 	}
549     }
550   while ((itr->flags & ITR_SKIPUNSET) &&
551 	 !(itr->flags & ITR_FINISHED) &&
552 	 !cattab[itr->pos].isset);
553 
554   return 0;
555 }
556 
557 static int
getitem(void * owner,void ** pret,const void ** pkey)558 getitem (void *owner, void **pret, const void **pkey)
559 {
560   struct debug_iterator *itr = owner;
561   *(mu_debug_level_t*) pret = cattab[itr->pos].level;
562   if (pkey)
563     *pkey = cattab[itr->pos].name;
564   return 0;
565 }
566 
567 static int
finished_p(void * owner)568 finished_p (void *owner)
569 {
570   struct debug_iterator *itr = owner;
571   return itr->flags & ITR_FINISHED;
572 }
573 
574 static int
delitem(void * owner,void * item)575 delitem (void *owner, void *item)
576 {
577   struct debug_iterator *itr = owner;
578   return mu_c_strcasecmp (cattab[itr->pos].name, (char *) item) == 0 ?
579      MU_ITR_DELITEM_NEXT : MU_ITR_DELITEM_NOTHING;
580 }
581 
582 static int
list_data_dup(void ** ptr,void * owner)583 list_data_dup (void **ptr, void *owner)
584 {
585   *ptr = malloc (sizeof (struct debug_iterator));
586   if (*ptr == NULL)
587     return ENOMEM;
588   memcpy (*ptr, owner, sizeof (struct debug_iterator));
589   return 0;
590 }
591 
592 static int
list_itrctl(void * owner,enum mu_itrctl_req req,void * arg)593 list_itrctl (void *owner, enum mu_itrctl_req req, void *arg)
594 {
595   struct debug_iterator *itr = owner;
596 
597   switch (req)
598     {
599     case mu_itrctl_tell:
600       /* Return current position in the object.
601 	 NOTE: Positions are 1-based.
602        */
603       if (!arg)
604 	return EINVAL;
605       *(size_t*)arg = itr->pos + 1;
606       break;
607 
608     case mu_itrctl_delete:
609     case mu_itrctl_delete_nd:
610       /* Delete current element */
611       cattab[itr->pos].level = 0;
612       cattab[itr->pos].isset = 0;
613       break;
614 
615     case mu_itrctl_replace:
616     case mu_itrctl_replace_nd:
617       if (!arg)
618 	return EINVAL;
619       cattab[itr->pos].level = *(mu_debug_level_t*)arg;
620       break;
621 
622     case mu_itrctl_qry_direction:
623       if (!arg)
624 	return EINVAL;
625       else
626 	*(int*)arg = itr->flags & ITR_BACKWARDS;
627       break;
628 
629     case mu_itrctl_set_direction:
630       if (!arg)
631 	return EINVAL;
632       else
633 	itr->flags |= ITR_BACKWARDS;
634       break;
635 
636     case mu_itrctl_count:
637       if (!arg)
638 	return EINVAL;
639       *(size_t*)arg = catcnt;
640       break;
641 
642     default:
643       return ENOSYS;
644     }
645   return 0;
646 }
647 
648 int
mu_debug_get_iterator(mu_iterator_t * piterator,int skipunset)649 mu_debug_get_iterator (mu_iterator_t *piterator, int skipunset)
650 {
651   int status;
652   mu_iterator_t iterator;
653   struct debug_iterator *itr;
654 
655   itr = malloc (sizeof *itr);
656   if (!itr)
657     return ENOMEM;
658   itr->pos = 0;
659   itr->flags = skipunset ? ITR_SKIPUNSET : 0;
660   status = mu_iterator_create (&iterator, itr);
661   if (status)
662     {
663       free (itr);
664       return status;
665     }
666 
667   mu_iterator_set_first (iterator, first);
668   mu_iterator_set_next (iterator, next);
669   mu_iterator_set_getitem (iterator, getitem);
670   mu_iterator_set_finished_p (iterator, finished_p);
671   mu_iterator_set_delitem (iterator, delitem);
672   mu_iterator_set_dup (iterator, list_data_dup);
673   mu_iterator_set_itrctl (iterator, list_itrctl);
674 
675   mu_iterator_attach (&iterator_head, iterator);
676 
677   *piterator = iterator;
678   return 0;
679 }
680 
681 
682 void
mu_debug_log(const char * fmt,...)683 mu_debug_log (const char *fmt, ...)
684 {
685   va_list ap;
686   char *buf = NULL;
687   size_t buflen = 0;
688   size_t n;
689   int rc;
690 
691   mu_diag_init ();
692   va_start (ap, fmt);
693   rc = mu_vasnprintf (&buf, &buflen, fmt, ap);
694   va_end (ap);
695   if (rc == 0)
696     {
697       size_t i;
698       int nl = 0;
699       for (i = 0; buf[i]; i += n)
700 	{
701 	  n = strcspn (buf + i, "\n");
702 	  if ((nl = buf[i + n]))
703 	    ++n;
704 	  mu_stream_printf (mu_strerr, "\033s<%d>", MU_LOG_DEBUG);
705 	  mu_stream_write (mu_strerr, buf + i, n, NULL);
706 	}
707       if (!nl)
708 	mu_stream_write (mu_strerr, "\n", 1, NULL);
709     }
710   free (buf);
711 }
712 
713 void
mu_debug_log_begin(const char * fmt,...)714 mu_debug_log_begin (const char *fmt, ...)
715 {
716   va_list ap;
717 
718   mu_diag_init ();
719   mu_stream_flush (mu_strerr);
720   va_start (ap, fmt);
721   mu_stream_printf (mu_strerr, "\033s<%d>", MU_LOG_DEBUG);
722   mu_stream_vprintf (mu_strerr, fmt, ap);
723   va_end (ap);
724 }
725 
726 void
mu_debug_log_cont(const char * fmt,...)727 mu_debug_log_cont (const char *fmt, ...)
728 {
729   va_list ap;
730 
731   va_start (ap, fmt);
732   mu_stream_vprintf (mu_strerr, fmt, ap);
733   va_end (ap);
734 }
735 
736 void
mu_debug_log_end(const char * fmt,...)737 mu_debug_log_end (const char *fmt, ...)
738 {
739   va_list ap;
740 
741   va_start (ap, fmt);
742   mu_stream_vprintf (mu_strerr, fmt, ap);
743   mu_stream_write (mu_strerr, "\n", 1, NULL);
744   va_end (ap);
745 }
746 
747 void
mu_debug_log_nl()748 mu_debug_log_nl ()
749 {
750   mu_stream_write (mu_strerr, "\n", 1, NULL);
751 }
752