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