]> git.proxmox.com Git - pmg-log-tracker.git/blob - pmg-log-tracker.c
include local relayed mails (mail to root)
[pmg-log-tracker.git] / pmg-log-tracker.c
1 /*
2
3 (C) 2007-2017 Proxmox Server Solutions GmbH, All Rights Reserved
4
5 Proxmox Mail Tracker
6
7 This program is free software: you can redistribute it and/or modify
8 it under the terms of the GNU Affero General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU Affero General Public License for more details.
16
17 You should have received a copy of the GNU Affero General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
19
20 Author: Dietmar Maurer <dietmar@proxmox.com>
21
22 See http://www.proxmox.com for more information
23
24 */
25
26 #define _GNU_SOURCE
27
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <glib.h>
31 #include <string.h>
32 #include <ctype.h>
33 #include <sys/time.h>
34 #include <time.h>
35 #include <unistd.h>
36 #include <sys/types.h>
37 #include <zlib.h>
38 #include <fnmatch.h>
39
40 /*
41 We assume the syslog files belong to one host, i.e. we do not
42 consider the hostname at all
43 */
44
45 /*
46 REGEX: we do not use posix regular expressions (libc implementation
47 is too slow). fnmatch() is also slow.
48 Future versions may use our own implementation of a DFA. But currently we
49 just use strstr (no regex support at all)
50 */
51
52 //#define STRMATCH(pattern, string) (fnmatch (pattern, string, FNM_CASEFOLD) == 0)
53 #define STRMATCH(pattern, string) (strcasestr (string, pattern) != NULL)
54
55
56 //#define EPOOL_BLOCK_SIZE 512
57 //#define EPOOL_MAX_SIZE 128
58 #define EPOOL_BLOCK_SIZE 2048
59 #define EPOOL_MAX_SIZE 128
60 #define MAX_LOGFILES 31
61 //#define EPOOL_DEBUG
62 //#define DEBUG
63
64 typedef struct _SList SList;
65 struct _SList {
66 gpointer data;
67 SList *next;
68 };
69
70 typedef struct _NQList NQList;
71 struct _NQList {
72 char *from;
73 char *to;
74 char dstatus;
75 time_t ltime;
76 NQList *next;
77 };
78
79 typedef struct _TOList TOList;
80 struct _TOList {
81 char *to;
82 char *relay;
83 char dstatus;
84 time_t ltime;
85 TOList *next;
86 };
87
88 #ifdef DEBUG
89 GHashTable *smtpd_debug_alloc;
90 GHashTable *qmgr_debug_alloc;
91 GHashTable *filter_debug_alloc;
92 GHashTable *smtpd_debug_free;
93 GHashTable *qmgr_debug_free;
94 GHashTable *filter_debug_free;
95 #endif
96
97 // EPool: Entry related memory pools
98
99
100 #ifdef EPOOL_DEBUG
101 int ep_allocated;
102 int ep_maxalloc;
103 #endif
104
105 typedef struct _EPool EPool;
106 struct _EPool {
107 SList *blocks; // allocated usiing g_slice_alloc(EPOOL_BLOCK_SIZE)
108 SList *mblocks; // allocated use g_malloc
109 int cpos;
110 #ifdef EPOOL_DEBUG
111 int allocated;
112 #endif
113 };
114
115 typedef struct {
116 EPool ep;
117 GHashTable *smtpd_h;
118 GHashTable *qmgr_h;
119 GHashTable *filter_h;
120 //GHashTable *track_h;
121 gzFile stream[MAX_LOGFILES];
122 char *from;
123 char *to;
124 time_t year[MAX_LOGFILES];
125 time_t start;
126 time_t end;
127 time_t ctime;
128 char *server;
129 char *msgid;
130 char *qid;
131 char *strmatch;
132 unsigned long limit;
133 unsigned long count;
134 int verbose;
135 unsigned int exclude_greylist:1;
136 unsigned int exclude_ndrs:1;
137 } LParser;
138
139 typedef struct _SEntry SEntry;
140 typedef struct _QEntry QEntry;
141 typedef struct _FEntry FEntry;
142
143 typedef struct _LogEntry LogEntry;
144 typedef struct _LogList LogList;
145
146 struct _LogEntry {
147 const char *text;
148 LogEntry *next;
149 };
150
151 struct _LogList {
152 LogEntry *log;
153 LogEntry *logs_last; // pointer to last log (speedup add log)
154 };
155
156 // SEntry: Store SMTPD related logs
157
158 struct _SEntry {
159
160 EPool ep;
161 LogList loglist;
162
163 int pid;
164
165 SList *refs;
166
167 NQList *nqlist;
168
169 char *connect;
170
171 //unsigned int external:1; // not from local host
172 unsigned int disconnect:1;
173 unsigned int strmatch:1;
174
175 };
176
177 // QEntry: Store Queue (qmgr, smtp, lmtp) related logs
178
179 struct _QEntry {
180
181 EPool ep;
182 LogList loglist;
183
184 char *qid;
185
186 SEntry *smtpd;
187 FEntry *filter;
188
189 TOList *tolist;
190
191 unsigned int size;
192 char *from;
193 char *client;
194 char *msgid;
195
196 unsigned int cleanup:1;
197 unsigned int removed:1;
198 unsigned int filtered:1; // set when passed via lmtp to filter
199 unsigned int strmatch:1;
200 };
201
202 // FEntry: Store filter (proxprox) related logs
203
204 struct _FEntry {
205
206 EPool ep;
207 LogList loglist;
208
209 char *logid; // proxprox log id
210
211 TOList *tolist;
212
213 float ptime;
214
215 unsigned int finished:1;
216 unsigned int strmatch:1;
217 };
218
219 // Prototypes
220 void debug_error (char *msg, const char *line);
221
222 EPool *epool_init (EPool *ep);
223 void epool_free (EPool *ep);
224 gpointer epool_alloc (EPool *ep, int size);
225 char *epool_strndup (EPool *ep, const char *s, int len);
226 char *epool_strndup0 (EPool *ep, const char *s, int len);
227 char *epool_strdup (EPool *ep, const char *s);
228
229 void loglist_print (LogList *loglist);
230 void loglist_add (EPool *ep, LogList *loglist, const char *text, int len);
231
232 SEntry *sentry_new (int pid);
233 SEntry *sentry_get (LParser *parser, int pid);
234 void sentry_ref_add (SEntry *sentry, QEntry *qentry);
235 int sentry_ref_del (SEntry *sentry, QEntry *qentry);
236 void sentry_ref_finalize (LParser *parser, SEntry *sentry);
237 int sentry_ref_rem_unneeded (LParser *parser, SEntry *sentry);
238 void sentry_nqlist_add (SEntry *sentry, time_t ltime, const char *from, int from_len,
239 const char *to, int to_len, char dstatus);
240 void sentry_print (LParser *parser, SEntry *sentry);
241 void sentry_set_connect (SEntry *sentry, const char *connect, int len);
242 void sentry_free_noremove (SEntry *sentry);
243 void sentry_free (LParser *parser, SEntry *sentry);
244 void sentry_cleanup_hash (gpointer key, gpointer value, gpointer user_data);
245
246
247 QEntry *qentry_new (const char *qid);
248 QEntry *qentry_get (LParser *parser, const char *qid);
249 void qentry_tolist_add (QEntry *qentry, time_t ltime, char dstatus, const char *to,
250 int to_len, const char *relay, int relay_len);
251 void qentry_set_from (QEntry *qentry, const char *from, int len);
252 void qentry_set_msgid (QEntry *qentry, const char *msgid, int len);
253 void qentry_set_client (QEntry *qentry, const char *client, int len);
254 void qentry_print (LParser *parser, QEntry *qentry);
255 void qentry_finalize (LParser *parser, QEntry *qentry);
256 void qentry_free_noremove (LParser *parser, QEntry *qentry);
257 void qentry_free (LParser *parser, QEntry *qentry);
258 void qentry_cleanup_hash (gpointer key, gpointer value, gpointer user_data);
259
260
261 FEntry *fentry_new (const char *logid);
262 FEntry *fentry_get (LParser *parser, const char *logid);
263 void fentry_tolist_add (FEntry *fentry, char dstatus, const char *to,
264 int to_len, const char *qid, int qid_len);
265 void fentry_free_noremove (LParser *parser, FEntry *fentry);
266 void fentry_free (LParser *parser, FEntry *fentry);
267 void fentry_cleanup_hash (gpointer key, gpointer value, gpointer user_data);
268
269 LParser *parser_new ();
270 void parser_free (LParser *parser);
271
272 //char *parser_track (LParser *parser, const char *qid, gboolean insert);
273
274 // Implementations
275
276 //#define LOGPATH "./log/"
277 #define LOGPATH "/var/log/"
278 //#define LOGPATH "/var/log5/"
279
280 static const char *logfiles[] = {
281 LOGPATH "syslog",
282 LOGPATH "syslog.1",
283 LOGPATH "syslog.2.gz",
284 LOGPATH "syslog.3.gz",
285 LOGPATH "syslog.4.gz",
286 LOGPATH "syslog.5.gz",
287 LOGPATH "syslog.6.gz",
288 LOGPATH "syslog.7.gz",
289 LOGPATH "syslog.8.gz",
290 LOGPATH "syslog.9.gz",
291 LOGPATH "syslog.10.gz",
292 LOGPATH "syslog.11.gz",
293 LOGPATH "syslog.12.gz",
294 LOGPATH "syslog.13.gz",
295 LOGPATH "syslog.14.gz",
296 LOGPATH "syslog.15.gz",
297 LOGPATH "syslog.16.gz",
298 LOGPATH "syslog.17.gz",
299 LOGPATH "syslog.18.gz",
300 LOGPATH "syslog.19.gz",
301 LOGPATH "syslog.20.gz",
302 LOGPATH "syslog.21.gz",
303 LOGPATH "syslog.22.gz",
304 LOGPATH "syslog.23.gz",
305 LOGPATH "syslog.24.gz",
306 LOGPATH "syslog.25.gz",
307 LOGPATH "syslog.26.gz",
308 LOGPATH "syslog.27.gz",
309 LOGPATH "syslog.28.gz",
310 LOGPATH "syslog.29.gz",
311 LOGPATH "syslog.30.gz",
312 LOGPATH "syslog.31.gz",
313 };
314
315 void
316 debug_error (char *msg, const char *line)
317 {
318 #ifdef DEBUG
319 fprintf (stderr, "ERROR: %s\n", msg);
320 if (line) fprintf (stderr, "LINE: %s\n", line);
321
322 G_BREAKPOINT();
323
324 exit (-1);
325 #endif
326 }
327
328 EPool *
329 epool_init (EPool *ep)
330 {
331 gpointer data;
332 SList *blocks;
333
334 data = g_slice_alloc0(EPOOL_BLOCK_SIZE);
335 blocks = (SList *)data;
336
337 #ifdef EPOOL_DEBUG
338 ep->allocated += EPOOL_BLOCK_SIZE;
339 ep_allocated += EPOOL_BLOCK_SIZE;
340 ep_maxalloc = (ep_allocated > ep_maxalloc) ? ep_allocated : ep_maxalloc;
341 #endif
342
343
344 blocks->data = data;
345 blocks->next = NULL;
346
347 ep->blocks = blocks;
348 ep->cpos = sizeof (SList);
349
350 return ep;
351 }
352
353 void
354 epool_free (EPool *ep)
355 {
356 SList *l;
357 gpointer data;
358
359 #ifdef EPOOL_DEBUG
360 ep_allocated -= ep->allocated;
361 printf ("MEM: %d\n", ep_allocated);
362 #endif
363
364 #ifdef DEBUG
365 return;
366 #endif
367
368 l = ep->mblocks;
369 while (l) {
370 data = l->data;
371 l = l->next;
372 g_free (data);
373 }
374
375 l = ep->blocks;
376 while (l) {
377 data = l->data;
378 l = l->next;
379 g_slice_free1(EPOOL_BLOCK_SIZE, data);
380 }
381 }
382
383 gpointer
384 epool_alloc (EPool *ep, int size)
385 {
386 int rs = (size + 3) & ~3;
387 int space = EPOOL_BLOCK_SIZE - sizeof (SList) - ep->cpos;
388 gpointer data;
389
390 if (size > EPOOL_MAX_SIZE) {
391 SList *blocks;
392 if (space >= sizeof (SList)) {
393 blocks = ep->blocks->data + ep->cpos;
394 ep->cpos += sizeof (SList);
395 } else {
396 blocks = (SList *)epool_alloc (ep, sizeof (SList));
397 }
398
399 data = g_malloc (size);
400 #ifdef EPOOL_DEBUG
401 ep->allocated += size;
402 ep_allocated += size;
403 ep_maxalloc = (ep_allocated > ep_maxalloc) ? ep_allocated : ep_maxalloc;
404 #endif
405 blocks->data = data;
406 blocks->next = ep->mblocks;
407
408 ep->mblocks = blocks;
409
410 return data;
411
412 } else if (space >= rs) {
413 data = ep->blocks->data + ep->cpos;
414 ep->cpos += rs;
415
416 return data;
417
418 } else {
419 SList *blocks = ep->blocks->data + ep->cpos;
420
421 data = g_slice_alloc0 (EPOOL_BLOCK_SIZE);
422 blocks->data = data;
423 blocks->next = ep->blocks;
424
425 ep->blocks = blocks;
426 ep->cpos = rs;
427
428 #ifdef EPOOL_DEBUG
429 ep->allocated += EPOOL_BLOCK_SIZE;
430 ep_allocated += EPOOL_BLOCK_SIZE;
431 ep_maxalloc = (ep_allocated > ep_maxalloc) ? ep_allocated : ep_maxalloc;
432 #endif
433
434 return data;
435 }
436 }
437
438 char *
439 epool_strndup (EPool *ep, const char *s, int len)
440 {
441 int l = len + 1;
442 char *res = epool_alloc (ep, l);
443 strncpy (res, s, l);
444 return res;
445 }
446
447 char *
448 epool_strndup0 (EPool *ep, const char *s, int len)
449 {
450 char *res = epool_alloc (ep, len + 1);
451 strncpy (res, s, len);
452 res[len] = 0;
453
454 return res;
455 }
456
457 char *
458 epool_strdup (EPool *ep, const char *s)
459 {
460 int l = strlen (s) + 1;
461 char *res = epool_alloc (ep, l);
462 strncpy (res, s, l);
463 return res;
464 }
465
466 void
467 loglist_print (LogList *loglist)
468 {
469 LogEntry *log = loglist->log;
470 while (log) {
471 printf ("%s", log->text);
472 log = log->next;
473 }
474 }
475
476
477 void
478 loglist_add (EPool *ep, LogList *loglist, const char *text, int len)
479 {
480 LogEntry *log;
481
482 #ifdef DEBUG
483 if (len != strlen (text)) {
484 debug_error ("string with wrong len", NULL);
485 }
486 #endif
487 if (text[len] != 0) {
488 debug_error ("string is not null terminated", NULL);
489 return;
490 }
491
492 log = epool_alloc (ep, sizeof (LogEntry));
493
494 log->text = epool_strndup (ep, text, len);
495 log->next = NULL;
496
497 if (loglist->logs_last) {
498 loglist->logs_last->next = log;
499 loglist->logs_last = log;
500 } else {
501 loglist->log = log;
502 loglist->logs_last = log;
503 }
504
505 return;
506 }
507
508 SEntry*
509 sentry_new (int pid)
510 {
511 SEntry *sentry;
512 SList *blocks;
513 int cpos;
514
515 sentry = (SEntry *)g_slice_alloc0(EPOOL_BLOCK_SIZE);
516 sentry->pid = pid;
517
518 #ifdef EPOOL_DEBUG
519 sentry->ep.allocated += EPOOL_BLOCK_SIZE;
520 ep_allocated += EPOOL_BLOCK_SIZE;
521 ep_maxalloc = (ep_allocated > ep_maxalloc) ? ep_allocated : ep_maxalloc;
522 #endif
523
524 #ifdef DEBUG
525 {
526 SEntry *se;
527 if ((se = g_hash_table_lookup (smtpd_debug_alloc, sentry))) {
528 debug_error ("SEntry already alloced", NULL);
529 } else {
530 g_hash_table_insert (smtpd_debug_alloc, sentry, sentry);
531 }
532 }
533 #endif
534
535 cpos = sizeof (SEntry);
536
537 blocks = (SList *)((char *)sentry + cpos);
538
539 cpos += sizeof (SList);
540
541 blocks->data = sentry;
542 blocks->next = NULL;
543
544 sentry->ep.blocks = blocks;
545 sentry->ep.cpos = cpos;
546
547 return sentry;
548 }
549
550 SEntry *
551 sentry_get (LParser *parser, int pid)
552 {
553 SEntry *sentry;
554
555 if ((sentry = g_hash_table_lookup (parser->smtpd_h, &pid))) {
556 return sentry;
557 } else {
558
559 if ((sentry = sentry_new (pid))) {
560 g_hash_table_insert (parser->smtpd_h, &sentry->pid, sentry);
561 }
562
563 return sentry;
564 }
565 }
566
567 void
568 sentry_ref_add (SEntry *sentry, QEntry *qentry)
569 {
570 SList *l;
571
572 if (qentry->smtpd) {
573 if (qentry->smtpd != sentry) {
574 debug_error ("qentry ref already set", NULL);
575 }
576 return;
577 }
578
579 l = sentry->refs;
580 while (l) {
581 if (l->data == qentry) return;
582 l = l->next;
583 }
584
585 l = epool_alloc (&sentry->ep, sizeof (SList));
586
587 qentry->smtpd = sentry;
588
589 l->data = qentry;
590 l->next = sentry->refs;
591
592 sentry->refs = l;
593 }
594
595 int
596 sentry_ref_del (SEntry *sentry, QEntry *qentry)
597 {
598 SList *l = sentry->refs;
599 int count = 0;
600
601 if (!qentry->smtpd) {
602 debug_error ("qentry does not hav a qentry ref", NULL);
603 return 0;
604 }
605
606 l = sentry->refs;
607
608 while (l) {
609 QEntry *qe = (QEntry *)l->data;
610 if (qe == qentry) {
611 l->data = NULL;
612 } else {
613 if (qe && qe->cleanup) count++;
614 }
615 l = l->next;
616 }
617
618 return count;
619 }
620
621 void
622 sentry_ref_finalize (LParser *parser, SEntry *sentry)
623 {
624 SList *l = sentry->refs;
625
626 int count = 0;
627
628 while (l) {
629 SList *cl = l;
630 QEntry *qe = l->data;
631
632 l = l->next;
633
634 if (!qe) continue;
635
636 count++;
637
638 if (!qe->removed) continue;
639
640 FEntry *fe = qe->filter;
641
642 if (fe && !fe->finished) continue;
643
644 count--;
645
646 qentry_print (parser, qe);
647
648 cl->data = NULL;
649 qe->smtpd = NULL;
650
651 qentry_free (parser, qe);
652
653 if (fe) fentry_free (parser, fe);
654
655 }
656
657 if (!count) sentry_free_noremove (sentry);
658 }
659
660 int
661 sentry_ref_rem_unneeded (LParser *parser, SEntry *sentry)
662 {
663 SList *l = sentry->refs;
664 int count = 0;
665
666 while (l) {
667 QEntry *qe = (QEntry *)l->data;
668
669 if (qe) {
670 if (!qe->cleanup) {
671 qe->smtpd = NULL;
672 qentry_free (parser, qe);
673 l->data = NULL;
674 } else {
675 count++;
676 }
677 }
678
679 l = l->next;
680 }
681
682 return count;
683 }
684
685 void
686 sentry_nqlist_add (SEntry *sentry, time_t ltime, const char *from, int from_len,
687 const char *to, int to_len, char dstatus)
688 {
689 NQList *nq = (NQList *)epool_alloc (&sentry->ep, sizeof (NQList));
690
691 nq->from = epool_strndup0 (&sentry->ep, from, from_len);
692 nq->to = epool_strndup0 (&sentry->ep, to, to_len);
693 nq->dstatus = dstatus;
694
695 nq->next = sentry->nqlist;
696 nq->ltime = ltime;
697 sentry->nqlist = nq;
698 }
699
700 void
701 sentry_print (LParser *parser, SEntry *sentry)
702 {
703 NQList *nq;
704
705 if (parser->msgid || parser->qid) return;
706
707 if (parser->server) {
708 if (!sentry->connect) return;
709 if (!strcasestr (sentry->connect, parser->server)) return;
710 }
711
712 if (parser->from || parser->to ||
713 parser->exclude_greylist || parser->exclude_ndrs) {
714 nq = sentry->nqlist;
715 int found = 0;
716 while (nq) {
717 if (parser->from) {
718 if (!*(parser->from)) {
719 if (*(nq->from)) nq->dstatus = 0;
720 } else if (!STRMATCH(parser->from, nq->from)) {
721 nq->dstatus = 0;
722 }
723 }
724
725 if (parser->exclude_greylist && nq->dstatus == 'G') nq->dstatus = 0;
726
727 if (parser->exclude_ndrs && nq->from && !*nq->from) nq->dstatus = 0;
728
729 if (parser->to && nq->to && !STRMATCH(parser->to, nq->to)) {
730 nq->dstatus = 0;
731 }
732
733 if (nq->dstatus != 0) found = 1;
734
735 nq = nq->next;
736 }
737 if (!found) return;
738 }
739
740 if (parser->strmatch && !sentry->strmatch) return;
741
742 if (parser->verbose) {
743
744 printf ("SMTPD:\n");
745
746 printf ("CTIME: %08lX\n", parser->ctime);
747
748 if (sentry->connect) { printf ("CONNECT: %s\n", sentry->connect); }
749 //printf ("EXTERNAL: %d\n", sentry->external);
750
751 }
752
753 nq = sentry->nqlist;
754 while (nq) {
755 if (nq->from && nq->to && nq->dstatus) {
756 printf ("TO:%08lX:00000000000:%c: from <%s> to <%s>\n", nq->ltime, nq->dstatus, nq->from, nq->to);
757 parser->count++;
758 }
759 nq = nq->next;
760 }
761
762 if (parser->verbose) {
763 printf ("LOGS:\n");
764 loglist_print (&sentry->loglist);
765 printf ("\n");
766 }
767
768 fflush (stdout);
769 }
770
771 void
772 sentry_set_connect (SEntry *sentry, const char *connect, int len)
773 {
774 if (sentry->connect) {
775 #ifdef DEBUG
776 if (strncmp (sentry->connect, connect, len)) {
777 debug_error ("duplicate connect", NULL);
778 }
779 #endif
780 } else {
781 sentry->connect = epool_strndup0 (&sentry->ep, connect, len);
782 }
783 }
784
785 void
786 sentry_free_noremove (SEntry *sentry)
787 {
788 SList *l;
789 gpointer data;
790
791 #ifdef EPOOL_DEBUG
792 ep_allocated -= sentry->ep.allocated;
793 printf ("MEM: %d\n", ep_allocated);
794 #endif
795
796 #ifdef DEBUG
797 {
798 SEntry *se;
799 if ((se = g_hash_table_lookup (smtpd_debug_free, sentry))) {
800 debug_error ("SEntry already freed", NULL);
801 } else {
802 g_hash_table_insert (smtpd_debug_free, sentry, sentry);
803 }
804 }
805 return;
806 #endif
807
808 l = sentry->ep.mblocks;
809 while (l) {
810 data = l->data;
811 l = l->next;
812 g_free (data);
813 }
814
815 l = sentry->ep.blocks;
816 while (l) {
817 data = l->data;
818 l = l->next;
819 g_slice_free1(EPOOL_BLOCK_SIZE, data);
820 }
821 }
822
823 void
824 sentry_free (LParser *parser, SEntry *sentry)
825 {
826 g_hash_table_remove (parser->smtpd_h, &sentry->pid);
827
828 sentry_free_noremove (sentry);
829 }
830
831 void
832 sentry_cleanup_hash (gpointer key,
833 gpointer value,
834 gpointer user_data)
835 {
836 SEntry *se = value;
837 LParser *parser = (LParser *)user_data;
838
839 sentry_print (parser, se);
840 sentry_free_noremove (se);
841 }
842
843 #ifdef DEBUG
844 void
845 sentry_debug_alloc (gpointer key,
846 gpointer value,
847 gpointer user_data)
848 {
849 LParser *parser = (LParser *)user_data;
850 SEntry *se = value;
851 SEntry *fe;
852
853 if ((fe = g_hash_table_lookup (smtpd_debug_free, se))) {
854 return;
855 }
856
857 printf ("FOUND ALLOCATED SENTRY:\n");
858 sentry_print (parser, se);
859
860 exit (-1);
861 }
862 #endif
863
864 // QEntry
865
866 QEntry*
867 qentry_new (const char *qid)
868 {
869 QEntry *qentry;
870 SList *blocks;
871 int cpos;
872 char *qid_cp;
873
874 qentry = (QEntry *)g_slice_alloc0(EPOOL_BLOCK_SIZE);
875
876 #ifdef EPOOL_DEBUG
877 qentry->ep.allocated += EPOOL_BLOCK_SIZE;
878 ep_allocated += EPOOL_BLOCK_SIZE;
879 ep_maxalloc = (ep_allocated > ep_maxalloc) ? ep_allocated : ep_maxalloc;
880 #endif
881
882 #ifdef DEBUG
883 {
884 QEntry *qe;
885 if ((qe = g_hash_table_lookup (qmgr_debug_alloc, qentry))) {
886 debug_error ("QEntry already alloced", NULL);
887 } else {
888 g_hash_table_insert (qmgr_debug_alloc, qentry, qentry);
889 }
890 }
891 #endif
892
893 cpos = sizeof (QEntry);
894
895 blocks = (SList *)((char *)qentry + cpos);
896
897 cpos += sizeof (SList);
898
899 blocks->data = qentry;
900 blocks->next = NULL;
901
902 qentry->qid = qid_cp = (char *)qentry + cpos;
903 while ((*qid_cp++ = *qid++)) cpos++;
904
905 cpos = (cpos + 4) & ~3;
906
907 qentry->ep.blocks = blocks;
908 qentry->ep.cpos = cpos;;
909
910 return qentry;
911 }
912
913 void
914 qentry_tolist_add (QEntry *qentry, time_t ltime, char dstatus, const char *to, int to_len,
915 const char *relay, int relay_len)
916 {
917 TOList *tl = (TOList *)epool_alloc (&qentry->ep, sizeof (TOList));
918
919 tl->to = epool_strndup0 (&qentry->ep, to, to_len);
920 tl->relay = epool_strndup0 (&qentry->ep, relay, relay_len);
921 tl->dstatus = dstatus;
922 tl->ltime = ltime;
923 tl->next = qentry->tolist;
924
925 qentry->tolist = tl;
926 }
927
928 void
929 qentry_set_from (QEntry *qentry, const char *from, int len)
930 {
931 if (qentry->from) {
932 #ifdef DEBUG
933 if (strncmp (qentry->from, from, len)) {
934 debug_error ("duplicate from", NULL);
935 }
936 #endif
937 } else {
938 qentry->from = epool_strndup0 (&qentry->ep, from, len);
939 }
940 }
941
942 void
943 qentry_set_msgid (QEntry *qentry, const char *msgid, int len)
944 {
945 if (qentry->msgid) {
946 #ifdef DEBUG
947 if (strncmp (qentry->msgid, msgid, len)) {
948 debug_error ("duplicate msgid", NULL);
949 }
950 #endif
951 } else {
952 qentry->msgid = epool_strndup0 (&qentry->ep, msgid, len);
953 }
954 }
955
956 void
957 qentry_set_client (QEntry *qentry, const char *client, int len)
958 {
959 if (qentry->client) {
960 #ifdef DEBUG
961 if (strncmp (qentry->client, client, len)) {
962 debug_error ("duplicate client", NULL);
963 }
964 #endif
965 } else {
966 qentry->client = epool_strndup0 (&qentry->ep, client, len);
967 }
968 }
969
970 void
971 qentry_print (LParser *parser, QEntry *qentry)
972 {
973 TOList *tl, *fl;
974 SEntry *se = qentry->smtpd;
975 FEntry *fe = qentry->filter;
976
977 if (parser->msgid) {
978 if (!qentry->msgid) return;
979 if (strcasecmp (parser->msgid, qentry->msgid)) return;
980 }
981
982 if (parser->qid) {
983 int found = 0;
984 if (fe && !strcmp (fe->logid, parser->qid)) found = 1;
985 if (!strcmp (qentry->qid, parser->qid)) found = 1;
986
987 if (!found) return;
988 }
989
990 if (parser->server) {
991 int found = 0;
992 if (se && se->connect && strcasestr (se->connect, parser->server)) found = 1;
993 if (qentry->client && strcasestr (qentry->client, parser->server)) found = 1;
994
995 if (!found) return;
996 }
997
998 if (parser->from) {
999 if (!qentry->from) return;
1000 if (!*(parser->from)) {
1001 if (*(qentry->from)) return;
1002 } else if (!STRMATCH(parser->from, qentry->from)) {
1003 return;
1004 }
1005 } else {
1006 if (parser->exclude_ndrs && qentry->from && !*qentry->from) return;
1007 }
1008
1009 if (parser->to) {
1010 tl = qentry->tolist;
1011 int found = 0;
1012 while (tl) {
1013 if (parser->to && !STRMATCH(parser->to, tl->to)) {
1014 tl->to = NULL;
1015 } else {
1016 found = 1;
1017 }
1018 tl = tl->next;
1019 }
1020 if (!found) return;
1021 }
1022
1023 if (parser->strmatch &&
1024 !(qentry->strmatch || (se && se->strmatch) || (fe && fe->strmatch)))
1025 return;
1026
1027
1028 if (parser->verbose) {
1029
1030 printf ("QENTRY: %s\n", qentry->qid);
1031
1032 printf ("CTIME: %08lX\n", parser->ctime);
1033 printf ("SIZE: %u\n", qentry->size);
1034
1035 if (qentry->client) { printf ("CLIENT: %s\n", qentry->client); }
1036
1037 if (qentry->msgid) { printf ("MSGID: %s\n", qentry->msgid); }
1038
1039 }
1040
1041 tl = qentry->tolist;
1042 while (tl) {
1043 if (tl->to) {
1044 fl = NULL;
1045 if (fe && tl->dstatus == '2') {
1046 fl = fe->tolist;
1047 while (fl) {
1048 if (fl->to && !strcmp (tl->to, fl->to)) {
1049 break;
1050 }
1051 fl = fl->next;
1052 }
1053 }
1054 char *to;
1055 char dstatus;
1056 char *relay;
1057
1058 if (fl) {
1059 to = fl->to;
1060 dstatus = fl->dstatus;
1061 relay = fl->relay;
1062 } else {
1063 to = tl->to;
1064 dstatus = tl->dstatus;
1065 relay = tl->relay;
1066 }
1067
1068 printf ("TO:%08lX:%s:%c: from <%s> to <%s> (%s)\n", tl->ltime, qentry->qid, dstatus, qentry->from ? qentry->from : "", to ? to : "", relay ? relay : "none");
1069
1070 parser->count++;
1071 }
1072 tl = tl->next;
1073 }
1074
1075
1076 if (!parser->verbose) { fflush (stdout); return; }
1077
1078 if (se) {
1079
1080 if (se->loglist.log) {
1081 printf ("SMTP:\n");
1082 loglist_print (&se->loglist);
1083 }
1084 }
1085
1086 if (fe) {
1087 if (fe->loglist.log) {
1088 printf ("FILTER: %s\n", fe->logid);
1089 loglist_print (&fe->loglist);
1090 }
1091 }
1092
1093 if (qentry->loglist.log) {
1094 printf ("QMGR:\n");
1095 loglist_print (&qentry->loglist);
1096 }
1097
1098 printf ("\n");
1099
1100 fflush (stdout);
1101
1102 //sleep (1);
1103 }
1104
1105 QEntry *
1106 qentry_get (LParser *parser, const char *qid)
1107 {
1108 QEntry *qentry;
1109
1110 if ((qentry = g_hash_table_lookup (parser->qmgr_h, qid))) {
1111 return qentry;
1112 } else {
1113 if ((qentry = qentry_new (qid))) {
1114 g_hash_table_insert (parser->qmgr_h, qentry->qid, qentry);
1115 }
1116
1117 return qentry;
1118 }
1119 }
1120
1121 void
1122 qentry_free_noremove (LParser *parser, QEntry *qentry)
1123 {
1124 SList *l;
1125 gpointer data;
1126 SEntry *se;
1127
1128 if ((se = qentry->smtpd)) {
1129 if (sentry_ref_del (se, qentry) == 0) {
1130 if (se->disconnect) {
1131 sentry_free_noremove (se);
1132 }
1133 }
1134 }
1135
1136 #ifdef EPOOL_DEBUG
1137 ep_allocated -= qentry->ep.allocated;
1138 printf ("MEM: %d\n", ep_allocated);
1139 #endif
1140
1141 #ifdef DEBUG
1142 {
1143 QEntry *qe;
1144 if ((qe = g_hash_table_lookup (qmgr_debug_free, qentry))) {
1145 debug_error ("QEntry already freed", NULL);
1146 } else {
1147 g_hash_table_insert (qmgr_debug_free, qentry, qentry);
1148 }
1149 }
1150 return;
1151 #endif
1152
1153 l = qentry->ep.mblocks;
1154 while (l) {
1155 data = l->data;
1156 l = l->next;
1157 g_free (data);
1158 }
1159
1160 l = qentry->ep.blocks;
1161 while (l) {
1162 data = l->data;
1163 l = l->next;
1164 g_slice_free1(EPOOL_BLOCK_SIZE, data);
1165 }
1166 }
1167
1168 void
1169 qentry_free (LParser *parser, QEntry *qentry)
1170 {
1171 g_hash_table_remove (parser->qmgr_h, qentry->qid);
1172
1173 qentry_free_noremove (parser, qentry);
1174 }
1175
1176 void
1177 qentry_cleanup_hash (gpointer key,
1178 gpointer value,
1179 gpointer user_data)
1180 {
1181 QEntry *qe = value;
1182 LParser *parser = (LParser *)user_data;
1183
1184 qentry_print (parser, qe);
1185 qentry_free_noremove (parser, qe);
1186 }
1187
1188 void
1189 qentry_finalize (LParser *parser, QEntry *qentry)
1190 {
1191 if (qentry && qentry->removed) {
1192 SEntry *se = qentry->smtpd;
1193
1194 if (se && !se->disconnect) return;
1195
1196 FEntry *fe = qentry->filter;
1197
1198 if (fe && !fe->finished) return;
1199
1200 qentry_print (parser, qentry);
1201 qentry_free (parser, qentry);
1202
1203 if (fe) fentry_free (parser, fe);
1204 }
1205 }
1206
1207 // FEntry
1208
1209 FEntry*
1210 fentry_new (const char *logid)
1211 {
1212 FEntry *fentry;
1213 SList *blocks;
1214 int cpos;
1215 char *logid_cp;
1216
1217 fentry = (FEntry *)g_slice_alloc0(EPOOL_BLOCK_SIZE);
1218
1219 #ifdef EPOOL_DEBUG
1220 fentry->ep.allocated += EPOOL_BLOCK_SIZE;
1221 ep_allocated += EPOOL_BLOCK_SIZE;
1222 ep_maxalloc = (ep_allocated > ep_maxalloc) ? ep_allocated : ep_maxalloc;
1223 #endif
1224
1225 #ifdef DEBUG
1226 {
1227 FEntry *fe;
1228 if ((fe = g_hash_table_lookup (filter_debug_alloc, fentry))) {
1229 debug_error ("FEntry already alloced", NULL);
1230 } else {
1231 g_hash_table_insert (filter_debug_alloc, fentry, fentry);
1232 }
1233 }
1234 #endif
1235
1236 cpos = sizeof (FEntry);
1237
1238 blocks = (SList *)((char *)fentry + cpos);
1239
1240 cpos += sizeof (SList);
1241
1242 blocks->data = fentry;
1243 blocks->next = NULL;
1244
1245 fentry->logid = logid_cp = (char *)fentry + cpos;
1246 while ((*logid_cp++ = *logid++)) cpos++;
1247 cpos = (cpos + 4) & ~3;
1248
1249 fentry->ep.blocks = blocks;
1250 fentry->ep.cpos = cpos;;
1251
1252 return fentry;
1253 }
1254
1255 FEntry *
1256 fentry_get (LParser *parser, const char *logid)
1257 {
1258 FEntry *fentry;
1259
1260 if ((fentry = g_hash_table_lookup (parser->filter_h, logid))) {
1261 return fentry;
1262 } else {
1263 if ((fentry = fentry_new (logid))) {
1264 g_hash_table_insert (parser->filter_h, fentry->logid, fentry);
1265 }
1266
1267 return fentry;
1268 }
1269 }
1270
1271 void
1272 fentry_tolist_add (FEntry *fentry, char dstatus, const char *to, int to_len,
1273 const char *qid, int qid_len)
1274 {
1275 TOList *tl = (TOList *)epool_alloc (&fentry->ep, sizeof (TOList));
1276
1277 tl->to = epool_strndup0 (&fentry->ep, to, to_len);
1278
1279 if (qid) {
1280 tl->relay = epool_strndup0 (&fentry->ep, qid, qid_len);
1281 } else {
1282 tl->relay = NULL;
1283 }
1284 tl->dstatus = dstatus;
1285 tl->next = fentry->tolist;
1286
1287 fentry->tolist = tl;
1288 }
1289
1290 void
1291 fentry_free_noremove (LParser *parser, FEntry *fentry)
1292 {
1293 SList *l;
1294 gpointer data;
1295
1296 #ifdef EPOOL_DEBUG
1297 ep_allocated -= fentry->ep.allocated;
1298 printf ("MEM: %d\n", ep_allocated);
1299 #endif
1300
1301 #ifdef DEBUG
1302 {
1303 FEntry *fe;
1304 if ((fe = g_hash_table_lookup (filter_debug_free, fentry))) {
1305 debug_error ("FEntry already freed", NULL);
1306 } else {
1307 g_hash_table_insert (filter_debug_free, fentry, fentry);
1308 }
1309 }
1310 return;
1311 #endif
1312
1313 l = fentry->ep.mblocks;
1314 while (l) {
1315 data = l->data;
1316 l = l->next;
1317 g_free (data);
1318 }
1319
1320 l = fentry->ep.blocks;
1321 while (l) {
1322 data = l->data;
1323 l = l->next;
1324 g_slice_free1(EPOOL_BLOCK_SIZE, data);
1325 }
1326 }
1327
1328 void
1329 fentry_free (LParser *parser, FEntry *fentry)
1330 {
1331 g_hash_table_remove (parser->filter_h, fentry->logid);
1332
1333 fentry_free_noremove (parser, fentry);
1334 }
1335
1336 void
1337 fentry_cleanup_hash (gpointer key,
1338 gpointer value,
1339 gpointer user_data)
1340 {
1341 FEntry *fe = value;
1342 LParser *parser = (LParser *)user_data;
1343
1344 fentry_free_noremove (parser, fe);
1345 }
1346
1347 // Parser
1348
1349 LParser*
1350 parser_new ()
1351 {
1352 LParser *parser = g_malloc0 (sizeof (LParser));
1353 struct timeval tv;
1354 struct tm *ltime;
1355 int i;
1356
1357 epool_init (&parser->ep);
1358
1359 if (!(parser->smtpd_h = g_hash_table_new (g_int_hash, g_int_equal))) {
1360 return NULL;
1361 }
1362
1363 if (!(parser->qmgr_h = g_hash_table_new (g_str_hash, g_str_equal))) {
1364 return NULL;
1365 }
1366
1367 if (!(parser->filter_h = g_hash_table_new (g_str_hash, g_str_equal))) {
1368 return NULL;
1369 }
1370
1371 //if (!(parser->track_h = g_hash_table_new (g_str_hash, g_str_equal))) {
1372 //return NULL;
1373 //}
1374
1375 for (i = 0; i < MAX_LOGFILES; i++) {
1376 gettimeofday(&tv, NULL);
1377 tv.tv_sec -= 3600*24*i;
1378 ltime = localtime (&tv.tv_sec);
1379 parser->year[i] = ltime->tm_year + 1900;
1380 }
1381
1382 return parser;
1383 }
1384
1385 void
1386 parser_free (LParser *parser)
1387 {
1388 int i;
1389
1390 for (i = 0; i < MAX_LOGFILES; i++) {
1391 if (parser->stream[i]) gzclose (parser->stream[i]);
1392 }
1393
1394 epool_free (&parser->ep);
1395
1396 g_hash_table_destroy (parser->smtpd_h);
1397 g_hash_table_destroy (parser->qmgr_h);
1398 g_hash_table_destroy (parser->filter_h);
1399 //g_hash_table_destroy (parser->track_h);
1400
1401 g_free (parser);
1402 }
1403
1404 #if 0
1405 char *
1406 parser_track (LParser *parser, const char *qid, gboolean insert)
1407 {
1408 char *res;
1409
1410 if ((res = g_hash_table_lookup (parser->track_h, qid))) {
1411 return res;
1412 } else {
1413 if (insert && (res = epool_strdup (&parser->ep, qid))) {
1414 g_hash_table_insert (parser->track_h, res, res);
1415 return res;
1416 }
1417 }
1418 return NULL;
1419 }
1420 #endif
1421
1422 static const int linebufsize = 8192;
1423 static int cur_year;
1424 static int cur_month = 0;
1425 static int cal_mtod[12] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
1426
1427 static time_t
1428 mkgmtime (struct tm *tm)
1429 {
1430 time_t res;
1431
1432 int year = tm->tm_year + 1900;
1433 int mon = tm->tm_mon;
1434
1435 res = (year - 1970) * 365 + cal_mtod[mon];
1436
1437 // leap year corrections (gregorian calendar)
1438 if (mon <= 1) year -= 1;
1439 res += (year - 1968) / 4;
1440 res -= (year - 1900) / 100;
1441 res += (year - 1600) / 400;
1442
1443 res += tm->tm_mday - 1;
1444 res = res*24 + tm->tm_hour;
1445 res = res*60 + tm->tm_min;
1446 res = res*60 + tm->tm_sec;
1447
1448 return res;
1449 }
1450
1451 time_t
1452 parse_time (const char **text, int len)
1453 {
1454 time_t ltime = 0;
1455
1456 int year = cur_year;
1457
1458 int mon = 0;
1459 int mday = 0;
1460 int hour = 0;
1461 int min = 0;
1462 int sec = 0;
1463 int found;
1464
1465 const char *line = *text;
1466
1467 if (len == (linebufsize - 1)) {
1468 debug_error ("skipping long line data", line);
1469 return 0;
1470 }
1471
1472 if (len < 15) {
1473 debug_error ("skipping short line data", line);
1474 return 0;
1475 }
1476
1477 // parse month
1478 int csum = (line[0]<<16) + (line[1]<<8) + line[2];
1479
1480 switch (csum) {
1481 case 4874606: mon = 0; break;
1482 case 4613474: mon = 1; break;
1483 case 5071218: mon = 2; break;
1484 case 4288626: mon = 3; break;
1485 case 5071225: mon = 4; break;
1486 case 4879726: mon = 5; break;
1487 case 4879724: mon = 6; break;
1488 case 4289895: mon = 7; break;
1489 case 5465456: mon = 8; break;
1490 case 5202804: mon = 9; break;
1491 case 5140342: mon = 10; break;
1492 case 4482403: mon = 11; break;
1493 default:
1494 debug_error ("unable to parse month", line);
1495 return 0;
1496 }
1497
1498 // year change heuristik
1499 if (cur_month == 11 && mon == 0) {
1500 year++;
1501 }
1502 if (mon > cur_month) cur_month = mon;
1503
1504 ltime = (year - 1970)*365 + cal_mtod[mon];
1505
1506 // leap year corrections (gregorian calendar)
1507 if (mon <= 1) year -= 1;
1508 ltime += (year - 1968) / 4;
1509 ltime -= (year - 1900) / 100;
1510 ltime += (year - 1600) / 400;
1511
1512 const char *cpos = line + 3;
1513
1514 found = 0; while (isspace (*cpos)) { cpos++; found = 1; }
1515 if (!found) {
1516 debug_error ("missing spaces after month", line);
1517 return 0;
1518 }
1519
1520 found = 0; while (isdigit (*cpos)) { mday = mday*10 + *cpos - 48; cpos++; found++; }
1521 if (found < 1 || found > 2) {
1522 debug_error ("unable to parse day of month", line);
1523 return 0;
1524 }
1525
1526 ltime += mday - 1;
1527
1528 found = 0; while (isspace (*cpos)) { cpos++; found++; }
1529 if (!found) {
1530 debug_error ("missing spaces after day of month", line);
1531 return 0;
1532 }
1533
1534 found = 0; while (isdigit (*cpos)) { hour = hour*10 + *cpos - 48; cpos++; found++; }
1535 if (found < 1 || found > 2) {
1536 debug_error ("unable to parse hour", line);
1537 return 0;
1538 }
1539
1540 ltime *= 24;
1541 ltime += hour;
1542
1543 if (*cpos != ':') {
1544 debug_error ("missing collon after hour", line);
1545 return 0;
1546 }
1547 cpos++;
1548
1549 found = 0; while (isdigit (*cpos)) { min = min*10 + *cpos - 48; cpos++; found++; }
1550 if (found < 1 || found > 2) {
1551 debug_error ("unable to parse minute", line);
1552 return 0;
1553 }
1554
1555 ltime *= 60;
1556 ltime += min;
1557
1558 if (*cpos != ':') {
1559 debug_error ("missing collon after minute", line);
1560 return 0;
1561 }
1562 cpos++;
1563
1564 found = 0; while (isdigit (*cpos)) { sec = sec*10 + *cpos - 48; cpos++; found++; }
1565 if (found < 1 || found > 2) {
1566 debug_error ("unable to parse second", line);
1567 return 0;
1568 }
1569
1570 ltime *= 60;
1571 ltime += sec;
1572
1573 found = 0; while (isspace (*cpos)) { cpos++; found = 1; }
1574 if (!found) {
1575 debug_error ("missing spaces after time", line);
1576 return 0;
1577 }
1578
1579 *text = cpos;
1580
1581 return ltime;
1582 }
1583
1584
1585 int
1586 parser_count_files (LParser *parser)
1587 {
1588 int i;
1589 time_t start = parser->start;
1590 char linebuf[linebufsize];
1591 const char *line;
1592 gzFile stream;
1593
1594 for (i = 0; i < MAX_LOGFILES; i++) {
1595 cur_year = parser->year[i];
1596 cur_month = 0;
1597
1598 if ((stream = gzopen (logfiles[i], "r"))) {
1599 if ((line = gzgets (stream, linebuf, linebufsize))) {
1600 if (parse_time (&line, strlen (line)) < start) {
1601 break;
1602 }
1603 } else {
1604 return i;
1605 }
1606 gzclose (stream);
1607 } else {
1608 return i;
1609 }
1610 }
1611
1612 return i + 1;
1613 }
1614
1615 static char *
1616 parse_qid (const char **text, char *out, char delim, int maxlen)
1617 {
1618 const char *idx;
1619 char *copy = out;
1620
1621 int found = 0;
1622 idx = *text;
1623 while (isxdigit (*idx)) { *copy++ = *idx++; found++; if (found > maxlen) break; }
1624
1625 if (found > 1 && found < maxlen &&
1626 ((delim && (*idx == delim)) || (!delim && isspace (*idx)))) {
1627 *copy = 0;
1628 idx++;
1629 while (isspace (*idx)) idx++;
1630 *text = idx;
1631 return out;
1632 }
1633 return NULL;
1634 }
1635
1636 void
1637 print_usage (const char *name)
1638 {
1639 fprintf (stderr, "usage: %s [OPTIONS] [OUTPUTFILENAME]\n", name);
1640 fprintf (stderr, "\t-f SENDER mails from SENDER\n");
1641 fprintf (stderr, "\t-t RECIPIENT mails to RECIPIENT\n");
1642 fprintf (stderr, "\t-h Server Server IP or Hostname\n");
1643 fprintf (stderr, "\t-s START start time (YYYY-MM-DD HH:MM:SS)\n");
1644 fprintf (stderr, "\t or seconds since epoch\n");
1645 fprintf (stderr, "\t-e END end time (YYYY-MM-DD HH:MM:SS)\n");
1646 fprintf (stderr, "\t or seconds since epoch\n");
1647 fprintf (stderr, "\t-m MSGID message ID (exact match)\n");
1648 fprintf (stderr, "\t-q QID queue ID (exact match)\n");
1649 fprintf (stderr, "\t-x STRING search for strings\n");
1650 fprintf (stderr, "\t-l LIMIT print max limit entries\n");
1651 fprintf (stderr, "\t-v verbose output\n");
1652 }
1653
1654
1655 // gzgets is ways too slow, so we do it our own way
1656
1657 static int
1658 mygzgetc (gzFile stream)
1659 {
1660 int br;
1661 static char readbuf[16384];
1662 static char *readend = readbuf + sizeof (readbuf);
1663 static char *readpos = readbuf + sizeof (readbuf);
1664
1665 if (readpos < readend) return *readpos++;
1666
1667 if ((br = gzread (stream, readbuf, sizeof (readbuf))) <= 0) {
1668 return -1;
1669 } else {
1670 readpos = readbuf;
1671 readend = readbuf + br;
1672
1673 return *readpos++;
1674 }
1675 }
1676
1677 static char *
1678 mygzgets (gzFile stream, char *line, int bufsize)
1679 {
1680 int c=0;
1681 char *cpos;
1682
1683 cpos = line;
1684 while (--bufsize > 0 && (c = mygzgetc(stream)) != -1) {
1685 *cpos++ = c;
1686 if (c == '\n')
1687 break;
1688 }
1689 if (c == -1 && cpos == line)
1690 return NULL;
1691 *cpos++ = '\0';
1692 return line;
1693 }
1694
1695
1696 extern char *optarg;
1697 extern int optind, opterr, optopt;
1698
1699 int
1700 main (int argc, char * const argv[])
1701 {
1702 char linebuf[linebufsize];
1703 char *line;
1704 char *uniqueid = NULL;
1705
1706 const char *text;
1707 const char *idx1;
1708 const char *idx2;
1709 const char *cpos;
1710 int found = 0;
1711 int csum_prog;
1712 int lines = 0;
1713 char qidbuf[30];
1714 int i;
1715
1716 struct tm *ltime;
1717 struct timeval tv;
1718 time_t ctime, start, end;
1719
1720 LParser *parser;
1721 int opt;
1722
1723 #ifdef DEBUG
1724 smtpd_debug_alloc = g_hash_table_new (g_direct_hash, g_direct_equal);
1725 qmgr_debug_alloc = g_hash_table_new (g_direct_hash, g_direct_equal);
1726 filter_debug_alloc = g_hash_table_new (g_direct_hash, g_direct_equal);
1727 smtpd_debug_free = g_hash_table_new (g_direct_hash, g_direct_equal);
1728 qmgr_debug_free = g_hash_table_new (g_direct_hash, g_direct_equal);
1729 filter_debug_free = g_hash_table_new (g_direct_hash, g_direct_equal);
1730 #endif
1731
1732 if (!(parser = parser_new ())) {
1733 fprintf (stderr, "unable to alloc parser structure\n");
1734 exit (-1);
1735 }
1736
1737 while ((opt = getopt (argc, argv, "f:t:s:e:h:m:q:x:l:I:vgn")) != -1) {
1738 if (opt == 'f') {
1739 parser->from = epool_strdup (&parser->ep, optarg);
1740 } else if (opt == 't') {
1741 parser->to = epool_strdup (&parser->ep, optarg);
1742 } else if (opt == 'v') {
1743 parser->verbose = 1;
1744 } else if (opt == 'g') {
1745 parser->exclude_greylist = 1;
1746 } else if (opt == 'n') {
1747 parser->exclude_ndrs = 1;
1748 } else if (opt == 'I') {
1749 uniqueid = optarg;
1750 } else if (opt == 'h') {
1751 parser->server = epool_strdup (&parser->ep, optarg);
1752 } else if (opt == 'm') {
1753 parser->msgid = epool_strdup (&parser->ep, optarg);
1754 } else if (opt == 'q') {
1755 parser->qid = epool_strdup (&parser->ep, optarg);
1756 } else if (opt == 'x') {
1757 parser->strmatch = epool_strdup (&parser->ep, optarg);
1758 } else if (opt == 'l') {
1759 char *l;
1760 parser->limit = strtoul (optarg, &l, 0);
1761 if (!*optarg || *l) {
1762 fprintf (stderr, "unable to parse limit '%s'\n", optarg);
1763 exit (-1);
1764 }
1765 } else if (opt == 's') {
1766 // use strptime to convert time
1767 struct tm tm;
1768 char *res;
1769 if ((!(res = strptime (optarg, "%F %T", &tm)) &&
1770 !(res = strptime (optarg, "%s", &tm))) || *res) {
1771 fprintf (stderr, "unable to parse start time\n");
1772 exit (-1);
1773 } else {
1774 parser->start = mkgmtime (&tm);
1775 }
1776 } else if (opt == 'e') {
1777 struct tm tm;
1778 char *res;
1779 if ((!(res = strptime (optarg, "%F %T", &tm)) &&
1780 !(res = strptime (optarg, "%s", &tm))) || *res) {
1781 fprintf (stderr, "unable to parse end time\n");
1782 exit (-1);
1783 } else {
1784 parser->end = mkgmtime (&tm);
1785 }
1786 } else {
1787 print_usage (argv[0]);
1788 exit (-1);
1789 }
1790 }
1791
1792 if (optind < argc) {
1793
1794 if ((argc - optind) > 1) {
1795 print_usage (argv[0]);
1796 exit (-1);
1797 }
1798
1799 char *tmpfn = g_strdup_printf ("/tmp/.proxtrack-%08X.txt", getpid ());
1800
1801 if ((stdout = freopen (tmpfn, "w", stdout)) == NULL) {
1802 perror ("unable to open output file");
1803 exit (-1);
1804 }
1805 if (rename (tmpfn, argv[optind]) != 0) {
1806 perror ("unable to open output file");
1807 unlink (tmpfn);
1808 exit (-1);
1809 }
1810 }
1811
1812 // we use gmtime exerywhere to speedup things, this can cause problems
1813 // when daylight saving time changes
1814
1815 gettimeofday(&tv, NULL);
1816 ltime = localtime (&tv.tv_sec);
1817
1818 if (!parser->start) {
1819 ltime->tm_sec = 0;
1820 ltime->tm_min = 0;
1821 ltime->tm_hour = 0;
1822 parser->start = mkgmtime (ltime);
1823 }
1824
1825 ltime = localtime (&tv.tv_sec);
1826
1827 if (!parser->end) {
1828 parser->end = mkgmtime (ltime);
1829 }
1830
1831 if (parser->end < parser->start) {
1832 fprintf (stderr, "end time before start time\n");
1833 exit (-1);
1834 }
1835
1836 int filecount;
1837 if ((filecount = parser_count_files (parser)) <= 0) {
1838 fprintf (stderr, "unable to access log files\n");
1839 exit (-1);
1840 }
1841
1842 if (uniqueid) {
1843 printf ("# LogReader: %d %s\n", getpid(), uniqueid);
1844 } else {
1845 printf ("# LogReader: %d\n", getpid());
1846 }
1847
1848 printf ("# Query options\n");
1849 if (parser->from) printf ("# Sender: %s\n", parser->from);
1850 if (parser->to) printf ("# Recipient: %s\n", parser->to);
1851 if (parser->server) printf ("# Server: %s\n", parser->server);
1852 if (parser->msgid) printf ("# MsgID: %s\n", parser->msgid);
1853 if (parser->qid) printf ("# QID: %s\n", parser->qid);
1854 if (parser->strmatch) printf ("# Match: %s\n", parser->strmatch);
1855
1856 strftime (linebuf, 256, "%F %T", gmtime (&parser->start));
1857 printf ("# Start: %s (%lu)\n", linebuf, parser->start);
1858 strftime (linebuf, 256, "%F %T", gmtime (&parser->end));
1859 printf ("# END: %s (%lu)\n", linebuf, parser->end);
1860 printf ("# End Query Options\n\n");
1861
1862 fflush (stdout);
1863
1864 start = parser->start;
1865 end = parser->end;
1866 ctime = 0;
1867
1868 for (i = filecount - 1; i >= 0; i--) {
1869 gpointer stream;
1870
1871 // syslog files does not conain years, so we must compute then
1872 // cur_year is the assumed start year
1873
1874 cur_month = 0;
1875 cur_year = parser->year[i];
1876
1877 if (i <= 1) {
1878 if (!(stream = (gpointer) fopen (logfiles[i], "r"))) continue;
1879 } else {
1880 if (!(stream = (gpointer) gzopen (logfiles[i], "r"))) continue;
1881 }
1882
1883 while (1) {
1884
1885 if (parser->limit && (parser->count >= parser->limit)) {
1886 printf ("STATUS: aborted by limit (too many hits)\n");
1887 exit (0);
1888 }
1889
1890 if (i <= 1) {
1891 line = fgets (linebuf, linebufsize, (FILE *)stream);
1892 } else {
1893 line = mygzgets ((gzFile)stream, linebuf, linebufsize);
1894 }
1895
1896 if (!line) break;
1897
1898 int len = strlen (line);
1899 int pid = 0;
1900
1901 cpos = line;
1902 if (!(ctime = parse_time (&cpos, len))) {
1903 continue;
1904 }
1905
1906 if (ctime < start) continue;
1907 if (ctime > end) break;
1908
1909 lines++;
1910
1911 found = 0; while (!isspace (*cpos)) { cpos++; found = 1; }
1912 if (!found) {
1913 debug_error ("missing hostname", line);
1914 continue;
1915 }
1916
1917 found = 0; while (isspace (*cpos)) { cpos++; found = 1; }
1918 if (!found) {
1919 debug_error ("missing spaces after host", line);
1920 continue;
1921 }
1922
1923 if ((*cpos == 'l') && !strncmp (cpos, "last message repeated", 21)) {
1924 continue;
1925 }
1926
1927 //printf ("LINE: %s\n", line);
1928 //const char *prog = cpos;
1929
1930 csum_prog = 0;
1931 found = 0; while (*cpos && (*cpos != ':') && (*cpos != '[')) {
1932 csum_prog = (csum_prog <<8) + *cpos;
1933 cpos++;
1934 found++;
1935 }
1936
1937 //idx1 = g_strndup (prog, found);
1938 //printf ("TEST:%s:%08X\n", idx1, csum_prog);
1939 //free (idx1);
1940
1941 if (*cpos == '[') {
1942 cpos++;
1943 found = 0; while (isdigit (*cpos)) {
1944 pid = pid*10 + *cpos - 48;
1945 cpos++;
1946 found++;
1947 }
1948 if (found < 1 || found > 15 || *cpos != ']') {
1949 debug_error ("unable to parse pid", line);
1950 continue;
1951 }
1952 cpos++;
1953 }
1954
1955 if (*cpos++ != ':') {
1956 debug_error ("missing collon", line);
1957 continue;
1958 }
1959
1960
1961 if (!isspace (*cpos++)) {
1962 debug_error ("missing space after collon", line);
1963 continue;
1964 }
1965
1966 text = cpos;
1967
1968 parser->ctime = ctime;
1969
1970 int strmatch = 0;
1971
1972 if (parser->strmatch && STRMATCH(parser->strmatch, text)) {
1973 strmatch = 1;
1974 }
1975
1976 if (csum_prog == 0x6C746572) { // pmg-smtp-filter
1977
1978 if ((idx1 = parse_qid (&cpos, qidbuf, ':', 25))) {
1979
1980 FEntry *fe;
1981
1982 if (!(fe = fentry_get (parser, idx1))) {
1983 continue;
1984 }
1985
1986 loglist_add (&fe->ep, &fe->loglist, line, len);
1987
1988 if (strmatch) fe->strmatch = 1;
1989
1990 //fixme: BCC, Notify?
1991 //fixme: parse virus info
1992 //fixme: parse spam score
1993
1994 if ((*cpos == 'a') && !strncmp (cpos, "accept mail to <", 16)) {
1995
1996 const char *to_s, *to_e;
1997
1998 to_s = cpos = cpos + 16;
1999
2000 while (*cpos && (*cpos != '>')) { cpos++; }
2001
2002 if (*cpos != '>') continue;
2003
2004 to_e = cpos;
2005
2006 cpos++;
2007
2008 if ((*cpos++ != ' ') || (*cpos++ != '(')) continue;
2009
2010 if (!(idx1 = parse_qid (&cpos, qidbuf, ')', 15))) continue;
2011
2012 // parser_track (parser, idx1, 1);
2013
2014 fentry_tolist_add (fe, 'A', to_s, to_e - to_s, idx1, strlen (idx1));
2015
2016 } else if ((*cpos == 'm') && !strncmp (cpos, "moved mail for <", 16)) {
2017 const char *to_s, *to_e;
2018
2019 to_s = cpos = cpos + 16;
2020
2021 while (*cpos && (*cpos != '>')) { cpos++; }
2022
2023 to_e = cpos;
2024
2025 if (strncmp (cpos, "> to ", 5)) continue;
2026 cpos += 5;
2027
2028 if (!strncmp (cpos, "spam", 4)) {
2029 cpos += 4;
2030 } else if (!strncmp (cpos, "virus", 5)) {
2031 cpos += 5;
2032 } else {
2033 continue;
2034 }
2035
2036 if (strncmp (cpos, " quarantine - ", 14)) continue;
2037 cpos += 14;
2038
2039 if (!(idx1 = parse_qid (&cpos, qidbuf, 0, 25))) continue;
2040
2041 fentry_tolist_add (fe, 'Q', to_s, to_e - to_s, idx1, strlen (idx1));
2042
2043 } else if ((*cpos == 'b') && !strncmp (cpos, "block mail to <", 15)) {
2044
2045 const char *to_s;
2046
2047 to_s = cpos = cpos + 15;
2048
2049 while (*cpos && (*cpos != '>')) { cpos++; }
2050
2051 if (*cpos != '>') continue;
2052
2053 fentry_tolist_add (fe, 'B', to_s, cpos - to_s, NULL, 0);
2054
2055 } else if ((*cpos == 'p') && !strncmp (cpos, "processing time: ", 17)) {
2056 cpos += 17;
2057
2058 sscanf (cpos, "%f", &fe->ptime);
2059
2060 fe->finished = 1;
2061 }
2062
2063 }
2064
2065 } else if (csum_prog == 0x7265656E) { // postfix/postscreen
2066
2067 SEntry *se;
2068
2069 if (!pid) {
2070 debug_error ("no pid for postscreen", line);
2071 continue;
2072 }
2073
2074
2075 if ((*text == 'N') && !strncmp (text, "NOQUEUE: reject: RCPT from ", 27)) {
2076
2077 cpos = text + 27;
2078
2079 if (!(idx1 = strstr (cpos, "; client ["))) continue;
2080
2081 const char *client = cpos = idx1 + 10;
2082
2083 while (*cpos && (*cpos != ']')) { cpos++; }
2084
2085 const char *client_end = cpos;
2086
2087 if (!(idx1 = strstr (cpos, "; from=<"))) continue;
2088
2089 const char *from = cpos = idx1 + 8;
2090
2091 while (*cpos && (*cpos != '>')) { cpos++; }
2092 idx1 = cpos;
2093
2094 if ((*cpos == '>') && strncmp (cpos, ">, to=<", 7)) continue;
2095
2096 const char *to = cpos = cpos + 7;
2097
2098 while (*cpos && (*cpos != '>')) { cpos++; }
2099
2100 if (*cpos != '>') continue;
2101
2102 if (!(se = sentry_get (parser, pid))) {
2103 continue;
2104 }
2105
2106 if (strmatch) se->strmatch = 1;
2107
2108 loglist_add (&se->ep, &se->loglist, line, len);
2109
2110 sentry_nqlist_add (se, ctime, from, idx1 - from, to, cpos - to, 'N');
2111
2112 sentry_set_connect (se, client, client_end - client);
2113
2114 g_hash_table_remove (parser->smtpd_h, &se->pid);
2115
2116 se->disconnect = 1;
2117
2118 sentry_print (parser, se);
2119 sentry_free (parser, se);
2120 }
2121
2122 } else if (csum_prog == 0x716D6772) { // postfix/qmgr
2123
2124 if ((idx2 = text) && (idx1 = parse_qid (&idx2, qidbuf, ':', 15))) {
2125
2126 QEntry *qe;
2127
2128 if (!(qe = qentry_get (parser, idx1))) {
2129 continue;
2130 }
2131
2132 if (strmatch) qe->strmatch = 1;
2133
2134 qe->cleanup = 1;
2135
2136 loglist_add (&qe->ep, &qe->loglist, line, len);
2137
2138 if ((*idx2 == 'f') && !strncmp (idx2, "from=<", 6)) {
2139
2140 cpos = idx2 = idx2 + 6;
2141 while (*cpos && (*cpos != '>')) { cpos++; }
2142
2143 if (*cpos != '>') {
2144 debug_error ("unable to parse 'from' address", line);
2145 continue;
2146 }
2147
2148 qentry_set_from (qe, idx2, cpos - idx2);
2149
2150 cpos++;
2151
2152 if ((*cpos == ',') && !strncmp (cpos, ", size=", 7)) {
2153 int size = 0;
2154 cpos += 7;
2155
2156 while (isdigit (*cpos)) { size = size*10 + *cpos++ - 48; }
2157
2158 qe->size = size;
2159 }
2160
2161 } else if ((*idx2 == 'r') && !strncmp (idx2, "removed\n", 8)) {
2162
2163 qe->removed = 1;
2164
2165 qentry_finalize (parser, qe);
2166 }
2167 }
2168
2169 } else if ((csum_prog == 0x736D7470) || //postfix/smtp
2170 (csum_prog == 0x6C6D7470) || //postfix/lmtp
2171 (csum_prog == 0x6F63616C) || //postfix/local
2172 (csum_prog == 0x72726F72)) { //postfix/error
2173
2174 int lmtp = (csum_prog == 0x6C6D7470);
2175
2176 if ((cpos = text) && (idx1 = parse_qid (&cpos, qidbuf, ':', 15))) {
2177
2178 QEntry *qe;
2179
2180 if (!(qe = qentry_get (parser, idx1))) {
2181 continue;
2182 }
2183
2184 if (strmatch) qe->strmatch = 1;
2185
2186 qe->cleanup = 1;
2187
2188 loglist_add (&qe->ep, &qe->loglist, line, len);
2189
2190 if (strncmp (cpos, "to=<", 4)) continue;
2191 cpos += 4;
2192
2193 const char *to_s, *to_e;
2194
2195 to_s = cpos;
2196
2197 while (*cpos && (*cpos != '>')) { cpos++; }
2198
2199 if (*cpos != '>') continue;
2200
2201 to_e = cpos;
2202
2203 cpos ++;
2204
2205 if (!(cpos = strstr (cpos, ", relay="))) continue;
2206 cpos += 8;
2207
2208 const char *relay_s, *relay_e;
2209
2210 relay_s = cpos;
2211
2212 while (*cpos && (*cpos != ',')) { cpos++; }
2213
2214 if (*cpos != ',') continue;
2215
2216 relay_e = cpos;
2217
2218 if (!(idx1 = strstr (cpos + 1, ", dsn="))) continue;
2219
2220 cpos = idx1 + 6;
2221
2222 if (!isdigit (*cpos)) continue;
2223
2224 char dstatus = *cpos;
2225
2226 qentry_tolist_add (qe, ctime, dstatus, to_s, to_e - to_s,
2227 relay_s, relay_e - relay_s);
2228
2229 if (!lmtp) continue; // filter always uses lmtp
2230
2231 if (!(idx1 = strstr (cpos + 1, "status=sent (250 2.")))
2232 continue;
2233
2234 cpos = idx1 = idx1 + 19;
2235
2236 if (*cpos == '5' && (idx1 = strstr (cpos, "5.0 OK ("))) {
2237 cpos = idx1 = idx1 + 8;
2238 } else if (*cpos == '7' && (idx1 = strstr (cpos, "7.0 BLOCKED ("))) {
2239 cpos = idx1 = idx1 + 13;
2240 } else {
2241 continue;
2242 }
2243
2244 if (!(idx1 = parse_qid (&cpos, qidbuf, ')', 25)))
2245 continue;
2246
2247 FEntry *fe;
2248
2249 qe->filtered = 1;
2250
2251 if ((fe = g_hash_table_lookup (parser->filter_h, idx1))) {
2252 qe->filter = fe;
2253 }
2254 }
2255
2256 } else if (csum_prog == 0x6D747064) { // postfix/smtpd
2257 SEntry *se;
2258
2259 if (!pid) {
2260 debug_error ("no pid for smtpd", line);
2261 continue;
2262 }
2263
2264 if (!(se = sentry_get (parser, pid))) {
2265 continue;
2266 }
2267
2268 if (strmatch) se->strmatch = 1;
2269
2270 loglist_add (&se->ep, &se->loglist, line, len);
2271
2272 if ((*text == 'c') && !strncmp (text, "connect from ", 13)) {
2273
2274 cpos = idx1 = text + 13;
2275
2276 while (*idx1 && !isspace (*idx1)) { idx1++; }
2277
2278 sentry_set_connect (se, cpos, idx1 - cpos);
2279
2280 // fixme: do we need this?
2281 //if (strcmp (se->connect, "localhost[127.0.0.1]")) {
2282 // se->external = 1;
2283 //}
2284
2285 } else if ((*text == 'd') && !strncmp (text, "disconnect from", 15)) {
2286
2287 // unlink
2288 g_hash_table_remove (parser->smtpd_h, &se->pid);
2289
2290 se->disconnect = 1;
2291
2292 if (sentry_ref_rem_unneeded (parser, se) == 0) {
2293 sentry_print (parser, se);
2294 sentry_free (parser, se);
2295 } else {
2296 sentry_ref_finalize (parser, se);
2297 }
2298
2299 } else if ((*text == 'N') && !strncmp (text, "NOQUEUE:", 8)) {
2300
2301 cpos = text + 8;
2302
2303 // parse 'whatsup' (reject:)
2304 while (*cpos && (*cpos != ':')) { cpos++; }
2305 if (*cpos != ':') continue;
2306 cpos++;
2307
2308 // parse '%s from %s:'
2309 while (*cpos && (*cpos != ':')) { cpos++; }
2310 if (*cpos != ':') continue;
2311 idx1 = cpos++;
2312
2313 // parse '%s;'
2314 while (*cpos && (*cpos != ';')) { cpos++; }
2315 if (*cpos != ';') continue;
2316
2317 // greylisting test
2318 char dstatus = 'N';
2319 *(char *)cpos = 0; // dangerous hack: delimit string
2320 if (strstr (idx1, ": Recipient address rejected: Service is unavailable (try later)")) {
2321 dstatus = 'G';
2322 }
2323 *(char *)cpos = ';'; // dangerous hack: restore line
2324
2325 if (!(idx1 = strstr (cpos, "; from=<"))) continue;
2326
2327 const char *from = cpos = idx1 + 8;
2328
2329 while (*cpos && (*cpos != '>')) { cpos++; }
2330 idx1 = cpos;
2331
2332 if ((*cpos == '>') && strncmp (cpos, "> to=<", 6)) continue;
2333
2334 const char *to = cpos = cpos + 6;
2335
2336 while (*cpos && (*cpos != '>')) { cpos++; }
2337
2338 if (*cpos != '>') continue;
2339
2340 sentry_nqlist_add (se, ctime, from, idx1 - from, to, cpos - to, dstatus);
2341
2342 } else if ((idx2 = text) && (idx1 = parse_qid (&idx2, qidbuf, ':', 15))) {
2343
2344 QEntry *qe;
2345
2346 if ((qe = qentry_get (parser, idx1))) {
2347
2348 if (strmatch) qe->strmatch = 1;
2349
2350 sentry_ref_add (se, qe);
2351
2352 if ((*idx2 == 'c') && !strncmp (idx2, "client=", 7)) {
2353 cpos = idx2 = idx2 + 7;
2354
2355 while (*cpos && !isspace (*cpos)) { cpos++; }
2356
2357 qentry_set_client (qe, idx2, cpos - idx2);
2358 }
2359 }
2360
2361 }
2362
2363 } else if (csum_prog == 0x616E7570) { // postfix/cleanup
2364
2365 QEntry *qe;
2366
2367 idx2 = text;
2368 if ((idx1 = parse_qid (&idx2, qidbuf, ':', 15))) {
2369 if ((qe = qentry_get (parser, idx1))) {
2370
2371 if (strmatch) qe->strmatch = 1;
2372
2373 loglist_add (&qe->ep, &qe->loglist, line, len);
2374
2375 if ((*idx2 == 'm') && !strncmp (idx2, "message-id=", 11)) {
2376
2377 cpos = idx2 = idx2 + 11;
2378
2379 while (*cpos && !isspace(*cpos)) { cpos++; }
2380
2381 qentry_set_msgid (qe, idx2, cpos - idx2);
2382
2383 qe->cleanup = 1;
2384 }
2385 }
2386 }
2387 }
2388 }
2389
2390 if (i <= 1) {
2391 fclose ((FILE *)stream);
2392 } else {
2393 gzclose ((gzFile)stream);
2394 }
2395
2396 if (ctime > end) break;
2397 }
2398
2399
2400 #ifdef DEBUG
2401 printf ("LINES: %d\n", lines);
2402 printf ("MEM SMTPD entries: %d\n", g_hash_table_size (parser->smtpd_h));
2403 printf ("MEM QMGR entries: %d\n", g_hash_table_size (parser->qmgr_h));
2404 printf ("MEM FILTER entries: %d\n", g_hash_table_size (parser->filter_h));
2405
2406 printf ("MEMDEB SMTPD entries: %d %d\n",
2407 g_hash_table_size (smtpd_debug_alloc),
2408 g_hash_table_size (smtpd_debug_free));
2409 printf ("MEMDEB QMGR entries: %d %d\n",
2410 g_hash_table_size (qmgr_debug_alloc),
2411 g_hash_table_size (qmgr_debug_free));
2412 printf ("MEMDEB FILTER entries: %d %d\n",
2413 g_hash_table_size (filter_debug_alloc),
2414 g_hash_table_size (filter_debug_free));
2415 #endif
2416
2417 g_hash_table_foreach (parser->qmgr_h, qentry_cleanup_hash, parser);
2418 g_hash_table_foreach (parser->smtpd_h, sentry_cleanup_hash, parser);
2419 g_hash_table_foreach (parser->filter_h, fentry_cleanup_hash, parser);
2420
2421 #ifdef DEBUG
2422 printf ("MEMDEB SMTPD entries: %d %d\n",
2423 g_hash_table_size (smtpd_debug_alloc),
2424 g_hash_table_size (smtpd_debug_free));
2425 printf ("MEMDEB QMGR entries: %d %d\n",
2426 g_hash_table_size (qmgr_debug_alloc),
2427 g_hash_table_size (qmgr_debug_free));
2428 printf ("MEMDEB FILTER entries: %d %d\n",
2429 g_hash_table_size (filter_debug_alloc),
2430 g_hash_table_size (filter_debug_free));
2431
2432 g_hash_table_foreach (smtpd_debug_alloc, sentry_debug_alloc, parser);
2433
2434 #endif
2435
2436
2437 #ifdef EPOOL_DEBUG
2438 printf ("MEMMAX %d\n", ep_maxalloc);
2439 #endif
2440
2441 parser_free (parser);
2442
2443 fclose (stdout);
2444
2445 exit (0);
2446 }