/* ** Modular Logfile Analyzer ** Copyright 2000 Jan Kneschke ** Copyright 2002 Otto Bruggeman ** ** Homepage: http://www.modlogan.org ** This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version, and provided that the above copyright and permission notice is included with all distributed copies of this or derived software. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA ** ** $Id: parse.c,v 1.11 2004/03/18 02:31:51 ostborn Exp $ */ #include #include #include #include #include #include #include "mlocale.h" #include "mplugins.h" #include "mrecord.h" #include "mdatatypes.h" #include "misc.h" #include "plugin_config.h" #define DEBUG_PCRE typedef struct { int type; pcre *match; } Matches; const char *short_month[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL }; int parse_date_time( mconfig* ext_conf, mlogrec *record, char* _buffer ) { #define N 20 + 1 char buf[10]; int ovector[ 3 * N ], n; int j; struct tm tm; config_input *conf = ext_conf->plugin_conf; if ( ( n = pcre_exec( conf->match_postfix_time_stamp, NULL, _buffer, strlen( _buffer ), 0, 0, ovector, 3 * N ) ) < 0 ) { if ( n == PCRE_ERROR_NOMATCH ) { M_DEBUG1( ext_conf->debug_level, M_DEBUG_SECTION_PARSING, M_DEBUG_LEVEL_ERRORS, "string doesn't match: %s\n", _buffer ); return M_RECORD_CORRUPT; } else { M_DEBUG1( ext_conf->debug_level, M_DEBUG_SECTION_PARSING, M_DEBUG_LEVEL_ERRORS, "execution error while matching: %d\n", n ); return M_RECORD_HARD_ERROR; } } /* everything has matched, take the different pieces and be happy :) */ pcre_copy_substring( _buffer, ovector, n, 2, buf, sizeof( buf ) ); for ( j = 0; j < 12; j++ ) { if ( strcmp( buf, short_month[ j ] ) == 0 ) { tm.tm_mon = j; } } pcre_copy_substring( _buffer, ovector, n, 3, buf, sizeof( buf ) ); tm.tm_mday = strtol( buf, NULL, 10 ); pcre_copy_substring( _buffer, ovector, n, 4, buf, sizeof( buf ) ); tm.tm_hour = strtol( buf, NULL, 10 ); pcre_copy_substring( _buffer, ovector, n, 5, buf, sizeof( buf ) ); tm.tm_min = strtol( buf, NULL, 10 ); pcre_copy_substring( _buffer, ovector, n, 6, buf, sizeof( buf ) ); tm.tm_sec = strtol( buf, NULL, 10 ); if (conf->year == -1) { time_t t = time(NULL); struct tm *cur; cur = localtime(&t); conf->year = cur->tm_year + 1900; } if (conf->last_month != -1 && conf->last_month > tm.tm_mon) { conf->year++; } #if 0 fprintf(stderr, "%s.%d: %d %d - %d\n", __FILE__, __LINE__, conf->last_month, tm.tm_mon, conf->year); #endif conf->last_month = tm.tm_mon; tm.tm_year = conf->year - 1900; if (-1 == (record->timestamp = mktime ( &tm ))) { fprintf(stderr, "%s.%d: mktime failed: %p\n", __FILE__, __LINE__, &tm); } return M_RECORD_NO_ERROR; #undef N } int parse_record_pcre(mconfig *ext_conf, mlogrec *record, buffer *b) { #define N 20 + 1 const char **list; int ovector[ 3 * N ], n; int i, match; struct tm tm; from_entry* entry; int j; char* id; mlogrec_mail *recmail = mrecord_init_mail(); enum {M_POSTFIX_FROM=0, M_POSTFIX_TO=1, M_POSTFIX_CLEANUP=2, M_POSTFIX_SMTPD=3, M_POSTFIX_SMTP=4, M_POSTFIX_PICKUP=5, M_POSTFIX_PIPE=6, M_POSTFIX_RELOAD=7, M_POSTFIX_WARNING=8, M_POSTFIX_DAEMON=9, M_POSTFIX_TERM=10, M_POSTFIX_DICT=11, M_POSTFIX_FOREST=12, M_POSTFIX_QUEUE=13}; config_input *conf = ext_conf->plugin_conf; const Matches matches [] = { { M_POSTFIX_FROM, conf->match_postfix_from }, { M_POSTFIX_TO, conf->match_postfix_to }, { M_POSTFIX_CLEANUP, conf->match_postfix_cleanup }, { M_POSTFIX_SMTPD, conf->match_postfix_smtpd }, { M_POSTFIX_SMTP, conf->match_postfix_smtp }, { M_POSTFIX_PICKUP, conf->match_postfix_pickup }, { M_POSTFIX_PIPE, conf->match_postfix_pipe }, { M_POSTFIX_RELOAD, conf->match_postfix_reload }, { M_POSTFIX_WARNING, conf->match_postfix_warning }, { M_POSTFIX_DAEMON, conf->match_postfix_daemon }, { M_POSTFIX_TERM, conf->match_postfix_terminating }, { M_POSTFIX_DICT, conf->match_postfix_dict_eval_action }, { M_POSTFIX_FOREST, conf->match_postfix_dir_forest }, { M_POSTFIX_QUEUE, conf->match_postfix_queue }, { 0, NULL } }; /* try to match the syslog prefix */ if ( ( n = pcre_exec( conf->match_syslog_postfix, NULL, b->ptr, b->used - 1, 0, 0, ovector, 3 * N ) ) < 0 ) { if ( n == PCRE_ERROR_NOMATCH ) { #if 0 fprintf(stderr, "%s.%d: syslog_postfix prefix doesn't match: %s\n", __FILE__, __LINE__, b->ptr); #endif return M_RECORD_IGNORED; } else { fprintf( stderr, "%s.%d: execution error while matching: %d\n", __FILE__, __LINE__, n ); return M_RECORD_HARD_ERROR; } } memset(&tm, 0, sizeof(struct tm)); /* ok, it's a syslog entry for postfix - the rest should be postfix specific */ if ( n ) { i = 0; match = -1; /* ok now try and find out if there is more info in this record that we can use */ for ( i = 0, match = -1; matches[ i ].match != NULL; i++ ) { /* find the corresponding match */ if ( ( n = pcre_exec( matches[ i ].match, NULL, b->ptr, b->used - 1, 0, 0, ovector, 3 * N ) ) < 0 ) { if ( n == PCRE_ERROR_NOMATCH ) { /* switch( i ) { case M_POSTFIX_CLEANUP: fprintf( stderr, "string \"%s\" does not match CLEANUP regexp\n", b->ptr ); break; case M_POSTFIX_SMTPD: fprintf( stderr, "string \"%s\" does not match SMTPD regexp\n", b->ptr ); break; default: ; } */ } else { fprintf( stderr, "%s.%d: execution error while matching: %d\n", __FILE__, __LINE__, n ); return M_RECORD_HARD_ERROR; } } else { match = matches[ i ].type; break; } } /* fprintf( stderr, "Value of \"n\" is : %d\n", n ); fprintf( stderr, "Value of \"match\" is: %d\n", match ); */ if ( n > 1 && match != -1 ) { int ok = 0; pcre_get_substring_list( b->ptr, ovector, n, &list ); switch ( match ) { case M_POSTFIX_FROM: /* find an empty entry */ j = 0; id = ( char* )!NULL; while ( ( j < M_FROM_ARRAY_SIZE ) && ( conf->from_list[ j ].id != NULL ) ) { j++; } if ( j == M_FROM_ARRAY_SIZE ) { /* No empty slots found, some major shit is happening */ return M_RECORD_HARD_ERROR; } entry = &( conf->from_list[ j ] ); entry->id = malloc( strlen( ( char* )list[ 1 ] ) + 1 ); /* fprintf( stderr, "list[1] : %s\n", list[ 1 ] ); */ strcpy( entry->id, ( char* )list[ 1 ] ); /* fprintf( stderr, "entry.id : %s\n", entry->id ); fprintf( stderr, "conf->from_list[ %d ].id : %s\n", j, conf->from_list[ j ].id ); */ entry->sender = malloc( strlen( ( char * )list[ 2 ] ) + 1 ); strcpy( entry->sender, ( char * )list[ 2 ] ); /* store incoming bytes in from_entry list */ entry->bytes_in = strtol( ( char * )list[ 3 ], NULL, 10 ); /* set nrptcs */ entry->nrcpts = strtol( ( char * )list[ 4 ], NULL, 10 ); #if 0 fprintf( stderr, "Mailrecord:\n" ); fprintf( stderr, "ID : %s\n", entry->id ); fprintf( stderr, "From : %s\n", entry->sender ); fprintf( stderr, "Size : %d\n", entry->bytes_in ); fprintf( stderr, "Nrcpt : %d\n", entry->nrcpts ); #endif break; case M_POSTFIX_TO: record->ext_type = M_RECORD_TYPE_MAIL; record->ext = recmail; /* parse date/time */ if ( ( i = parse_date_time(ext_conf, record, b->ptr) ) != M_RECORD_NO_ERROR ) { // wrong date, quit processing return i; } /* fetch id and compare to stored ones */ for ( j = 0; j < M_FROM_ARRAY_SIZE; j++ ) { id = conf->from_list[ j ].id; /* fprintf( stderr, "Current ID in list is : %s\n", conf->from_list[ j ].id ); */ if ( ( id != NULL ) && ( strcmp( id, list[ 1 ] ) == 0 ) ) break; } if ( j != M_FROM_ARRAY_SIZE ) { /* fprintf( stderr, "We found a match for ID = %s\n", conf->from_list[ j ].id ); */ /* we found an id match */ entry = &( conf->from_list[ j ] ); recmail->sender = malloc( strlen( entry->sender ) + 1 ); strcpy( recmail->sender, entry->sender ); recmail->receipient = malloc( strlen( list[ 2 ] ) + 1 ); strcpy( recmail->receipient, ( char* )list[ 2 ] ); recmail->duration = strtol( ( char* )list[ 4 ], NULL, 10 ); recmail->bytes_in = entry->bytes_in; /* This will go wrong due to refcounting */ recmail->bytes_out = entry->bytes_in; /* FIXME: use real bytes out */ /* recmail->status_dsn = malloc( strlen( list[ 5 ] ) + 1 ); strcpy( recmail->status_dsn, ( char* )list[ 5 ] ); */ recmail->status_text = malloc( strlen( list[ 1 ] ) + 1 ); strcpy( recmail->status_text, ( char* )list[ 1 ] ); /* recmail->status_dsn = malloc( strlen( list[ 5 ] ) + 1 ); strcpy( recmail->status_dsn, ( char* )list[ 5 ] ); */ } else { M_DEBUG1( ext_conf->debug_level, M_DEBUG_SECTION_PARSING, M_DEBUG_LEVEL_ERRORS, "Found a 'to' record without a matching 'from' record. ID is : %s\n", list[ 1 ] ); fflush( stderr ); return M_RECORD_IGNORED; } if ( entry->nrcpts > 1 ) { entry->nrcpts--; } else { free( entry->id ); free( entry->sender ); entry->id = NULL; entry->sender = NULL; } ok = 1; break; case M_POSTFIX_CLEANUP: case M_POSTFIX_SMTPD: case M_POSTFIX_SMTP: case M_POSTFIX_PICKUP: break; default: #if 0 for ( i = 0; i < n; i++ ) { printf( "%d: %s\n", i, list[ i ] ); } #endif break; } free( list ); return ok ? M_RECORD_NO_ERROR : M_RECORD_IGNORED; } else { fprintf( stderr, "%s.%d: what's that ?? %s\n", __FILE__, __LINE__, b->ptr ); return M_RECORD_CORRUPT; } } else { return M_RECORD_CORRUPT; } fprintf(stderr, "%s.%d\n", __FILE__, __LINE__); return M_RECORD_IGNORED; #undef N } int mplugins_input_postfix_get_next_record(mconfig *ext_conf, mlogrec *record) { int ret = 0; config_input *conf = ext_conf->plugin_conf; if (record == NULL) return M_RECORD_HARD_ERROR; /* fill the line buffer */ if (NULL == mgets(&(conf->inputfile), conf->buf)) return M_RECORD_EOF; ret = parse_record_pcre (ext_conf, record, conf->buf); if (ret == M_RECORD_CORRUPT) { M_DEBUG1(ext_conf->debug_level, M_DEBUG_SECTION_PARSING, M_DEBUG_LEVEL_WARNINGS, "affected Record: %s\n", conf->buf->ptr ); } return ret; } /* vim: set ts=4 sw=4 noet: */