/*
* mreport.c
* Jason Armstrong <jason@datrix.co.za>
*
* $Id: mreport.c,v 1.22 2000/01/25 12:29:10 jason Exp $
*/
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <unistd.h>
/*
#ifndef _NO_GETOPT_H_
#include <getopt.h>
#endif
*/
#include "mreport.h"
#include "timer.h"
#include "hash.h"
struct timeval tv;
int
main(int argc, char *argv[])
{
struct options o;
if (m_getconfig(&o, argc, argv) < 0 || m_getdata(&o) < 0) {
return -1;
}
return 0;
}
int
m_getdata(struct options *o)
{
struct data d;
char *p;
int rc,
i = 0;
if (m_init(o, &d) < 0) {
return -1;
}
while ( (rc = m_getline(o, &d)) == 0) {
if (m_getid(o, d.id, sizeof(d.id) - 1, d.mbuf) == 1) {
continue;
}
if (!o->summary && !o->nostats && !(i++)) {
strncpy(d.first, d.mbuf, sizeof(d.first) - 1);
}
memset(&(d.mfrom), 0, sizeof(struct from));
memset(&(d.mto), 0, sizeof(struct to));
if ( (p = strstr(d.mbuf, FROM))) {
if (m_add_from(o, &d, &p) < 0) {
return -1;
}
} else if ( (p = strstr(d.mbuf, STAT))) {
if (m_process_stat(o, &d, &p) < 0) {
return -1;
}
} else {
if (m_last_resort(o, &d) < 0) {
return -1;
}
}
}
if (rc < 0) {
return rc;
}
if (!o->summary && !o->nostats) {
strncpy(d.last, d.mbuf, sizeof(d.last) - 1);
}
if (m_report(o, &d) < 0) {
return -1;
}
return 0;
}
int
m_process_stat(struct options *o, struct data *d, char **p)
{
int rc;
if ( (rc = m_get_stat(o, d, *p))) {
return rc;
}
if ( (rc = m_get_ctrl(o, d)) < 0) {
return rc;
}
if ( (rc = m_get_to(o, d)) < 0) {
return rc;
}
return 0;
}
int
m_add_to(struct options *o, struct data *d)
{
if (d->toi >= d->tocount) {
if ( !(d->to = realloc( d->toi ? d->to : NULL,
(d->toi + 1 + M_INCREMENT) * sizeof(struct to *) ))) {
return (m_err(__LINE__, "realloc", strerror(errno)));
}
d->tocount += M_INCREMENT;
}
if ( !(d->to[d->toi] = malloc(sizeof(struct to)))) {
return (m_err(__LINE__, "malloc", strerror(errno)));
}
memcpy(d->to[d->toi], &(d->mto), sizeof(struct to));
if (d->keytoi >= d->keytocount) {
if ( !(d->keysto = realloc( d->keytoi ? d->keysto : NULL,
(d->keytoi + 1 + M_INCREMENT) * sizeof(char *) ))) {
return (m_err(__LINE__, "realloc", strerror(errno)));
}
d->keytocount += M_INCREMENT;
}
if ( !(d->keysto[d->keytoi] = (char *) malloc(M_KEYSIZE + 1))) {
return (m_err(__LINE__, "malloc", strerror(errno)));
}
if (d->duplicate && m_lookup_duplicate(o, d)) {
} else {
snprintf(d->keysto[d->keytoi], M_KEYSIZE - 1, d->id);
}
if (hash_insert_str(d->hto, d->keysto[d->keytoi], d->to[d->toi])) {
return (m_err(__LINE__, "hash_insert_str", ""));
}
(d->keytoi)++;
(d->toi)++;
return 0;
}
int
m_get_to(struct options *o, struct data *d)
{
char *p,
*cp;
int i = 0,
s = sizeof(d->mto.email) - 1;
p = strstr(d->mbuf, TO) + strlen(TO);
while (*p) {
if ( !(cp = strchr(p, ','))) {
fprintf(stderr,
"We should have a comma here :\n[%s]\n",
d->mbuf);
return 0;
}
*cp = 0;
if (strstr(p, CTL) || strstr(p, DELAY)) {
*cp = ',';
break;
}
if ( !(i = m_get_email(p, d->mto.email, s))) {
strncpy(d->mto.email, M_UNKNOWN, s);
} else {
while (d->mto.email[--i] == ' ') {
d->mto.email[i] = 0;
}
}
if (!o->from) {
if (o->dom &&
!strchr(d->mto.email, '@') &&
!strchr(d->mto.email, '|') &&
!strchr(d->mto.email, '/')) {
strncat(d->mto.email, "@",
s - strlen(d->mfrom.email));
strncat(d->mto.email, o->domain,
s - strlen(d->mfrom.email));
}
}
/*
if (o->alias) {
if (m_check_alias(o, d) < 0) {
fprintf(stderr, "ERROR: m_check_alias()\n");
return 0;
}
}
*/
if (o->dbg) {
printf("TO : [%s] [%s]\n",
d->id, d->mto.email);
}
if (d->mto.status) {
if (strcmp(d->mto.stat, HOST_UNKNOWN) == 0) {
d->merrs.hostunknown++;
} else if (strcmp(d->mto.stat, USER_UNKNOWN) == 0) {
d->merrs.userunknown++;
} else if (strcmp(d->mto.stat, NO_SERVICE) == 0) {
d->merrs.noservice++;
}
}
if (m_add_to(o, d) < 0) {
return -1;
}
*cp = ',';
p = cp + 1;
}
return 0;
}
int
m_get_ctrl(struct options *o, struct data *d)
{
char *cp;
int i,
size = sizeof(d->mto.ctrl) - 1;
if ( !(cp = strstr(d->mbuf, CTL))) {
return 1;
}
cp += strlen(CTL);
if ( !(i = m_get_email(cp, d->mto.ctrl, size))) {
strncpy(d->mto.ctrl, M_UNKNOWN, size);
} else {
while (d->mto.ctrl[--i] == ' ') {
d->mto.ctrl[i] = 0;
}
if (o->dom && !strchr(d->mto.ctrl, '@')) {
strncat(d->mto.ctrl, "@", size - strlen(d->mto.ctrl) - 1);
strncat(d->mto.ctrl, o->domain, size - strlen(d->mto.ctrl) - 1);
}
}
return 0;
}
int
m_get_stat(struct options *o, struct data *d, char *p)
{
char *cp;
if (!p) {
return -1;
}
cp = p + strlen(STAT);
if (strncasecmp(cp, SENT, strlen(SENT)) == 0) {
snprintf(d->mto.stat, sizeof(d->mto.stat) - 1, STAT);
return 0;
} else if (strncasecmp(cp, DEFERRED, strlen(DEFERRED)) == 0) {
d->merrs.deferred++;
} else if (strncasecmp(cp, USER_UNKNOWN, strlen(USER_UNKNOWN)) == 0) {
snprintf(d->mto.stat, sizeof(d->mto.stat) - 1, USER_UNKNOWN);
d->mto.status = 1;
return 0;
} else if (strncasecmp(cp, HOST_UNKNOWN, strlen(HOST_UNKNOWN)) == 0) {
snprintf(d->mto.stat, sizeof(d->mto.stat) - 1, HOST_UNKNOWN);
d->mto.status = 1;
return 0;
} else if (strncasecmp(cp, NO_SERVICE, strlen(NO_SERVICE)) == 0) {
snprintf(d->mto.stat, sizeof(d->mto.stat) - 1, NO_SERVICE);
d->mto.status = 1;
return 0;
} else if (strncasecmp(cp, IO_ERROR, strlen(IO_ERROR)) == 0) {
d->merrs.ioerror++;
} else {
if (o->dbg) {
printf("STAT : %s\n", cp);
}
}
return 1;
}
int
m_add_from(struct options *o, struct data *d, char **p)
{
if (!strstr(d->mbuf, SIZE) || !strstr(d->mbuf, NRCPTS)) {
return 0;
}
memset( &(d->mfrom), 0, sizeof(struct from));
if (m_get_from_email(o, p, d->mfrom.email, sizeof(d->mfrom.email) - 1) < 0) {
return -1;
}
if (m_get_from_size(o, *p, &(d->mfrom.size)) < 0) {
return -1;
}
if (o->dbg) {
printf("FROM : [%s] [%s] [%lu]\n",
d->id, d->mfrom.email, d->mfrom.size);
}
/*
if (m_get_nrcpts(o, d) < 0) {
return -1;
}
*/
if (d->fromi >= d->fromcount) {
if ( !(d->from = realloc( d->fromi ? d->from : NULL,
(d->fromi + 1 + M_INCREMENT) * sizeof(struct from *) ))) {
return (m_err(__LINE__, "realloc", strerror(errno)));
}
d->fromcount += M_INCREMENT;
}
if ( !(d->from[d->fromi] = malloc(sizeof(struct from)))) {
return (m_err(__LINE__, "malloc", strerror(errno)));
}
memcpy(d->from[d->fromi], &(d->mfrom), sizeof(struct from));
if (d->keyi >= d->keycount) {
if ( !(d->keys = realloc( d->keyi ? d->keys : NULL,
(d->keyi + 1 + M_INCREMENT) * sizeof(char *) ))) {
return (m_err(__LINE__, "realloc", strerror(errno)));
}
d->keycount += M_INCREMENT;
}
if ( !(d->keys[d->keyi] = (char *) malloc(M_KEYSIZE + 1))) {
return (m_err(__LINE__, "malloc", strerror(errno)));
}
if (hash_lookup_str(d->hfrom, d->id)) {
if (m_register_duplicate(o, d) < 0) {
return -1;
}
} else {
snprintf(d->keys[d->keyi], M_KEYSIZE - 1, d->id);
}
if (hash_insert_str(d->hfrom, d->keys[d->keyi], d->from[d->fromi])) {
return (m_err(__LINE__, "hash_insert_str", ""));
}
(d->keyi)++;
(d->fromi)++;
return 0;
}
int
m_register_duplicate(struct options *o, struct data *d)
{
if (d->dupi >= d->dupcount) {
if ( !(d->dup = realloc( d->dupi ? d->dup : NULL,
(d->dupi + 1 + M_INCREMENT) * sizeof(struct duplicate *) ))) {
return (m_err(__LINE__, "realloc", strerror(errno)));
}
d->dupcount += M_INCREMENT;
}
if ( !(d->dup[d->dupi] = malloc(sizeof(struct duplicate)))) {
return (m_err(__LINE__, "malloc", strerror(errno)));
}
snprintf(d->dup[d->dupi]->id1, sizeof(d->dup[d->dupi]->id1) - 1, d->id);
snprintf(d->dup[d->dupi]->id2, sizeof(d->dup[d->dupi]->id2) - 1,
"%d%s", d->dupi, d->id);
snprintf(d->keys[d->keyi], M_KEYSIZE - 1, d->dup[d->dupi]->id2);
if (o->dbg) {
printf("DUPLICATE REGISTERED: %s\n", d->id);
}
(d->dupi)++;
(d->duplicate)++;
return 0;
}
int
m_lookup_duplicate(struct options *o, struct data *d)
{
int i;
for (i = 0; i < d->dupi; i++) {
if (strcmp(d->dup[i]->id1, d->id) == 0) {
snprintf(d->keysto[d->keytoi], M_KEYSIZE - 1, d->dup[i]->id2);
if (o->dbg) {
printf("DUPLICATE TO: %s\n", d->keysto[d->keytoi]);
}
return 1;
}
}
return 0;
}
int
m_getline(struct options *o, struct data *d)
{
long pos;
int c = 0,
i = 0,
perbyte = 0;
if ( (pos = ftell(d->in)) < 0) {
return (m_err(__LINE__, "ftell", strerror(errno)));
}
for (;8;) {
if (perbyte) {
if (fseek(d->in, pos, SEEK_SET) < 0) {
return (m_err(__LINE__, "fseek", strerror(errno)));
}
while ( (c = fgetc(d->in)) != EOF) {
if (i >= d->mbufsize - 1) {
if ( !(d->mbuf = realloc(d->mbuf,
d->mbufsize + (M_BUFSIZE * sizeof(char)) + 1))) {
return (m_err(__LINE__, "realloc", strerror(errno)));
}
d->mbufsize += (M_BUFSIZE * sizeof(char));
if (o->dbg) {
printf("Buffer size change [%d]\n",
d->mbufsize);
}
}
if ( (d->mbuf[i] = c) == '\n') {
break;
} else {
i++;
}
}
d->mbuf[i] = 0;
break;
} else {
if (!fgets(d->mbuf, d->mbufsize, d->in)) {
if (feof(d->in)) {
return 1;
} else {
return (m_err(__LINE__, "fgets", strerror(errno)));
}
}
if ( !strchr(d->mbuf, '\n')) {
DBG("Switching to Per Byte mode (long line)");
perbyte++;
continue;
}
break;
}
}
return 0;
}
int
m_check_alias(struct options *o, struct data *d)
{
void *v;
if ( (v = hash_lookup_str(d->halias, d->mto.email))) {
if (o->dbg) {
printf("ALIAS: [%s] [%s]\n",
((struct alias *)(v))->alias,
((struct alias *)(v))->account);
}
snprintf(d->mto.email,
sizeof(d->mto.email),
((struct alias *)(v))->account);
}
return 0;
}
int
m_load_aliases(struct options *o, struct data *d)
{
FILE *f;
char buf[256];
if ( !(f = fopen(o->aliases, "r"))) {
return (m_err(__LINE__, o->aliases, strerror(errno)));
}
while (fgets(buf, sizeof(buf), f)) {
if (buf[0] == '#' || buf[0] == '\n' || !buf[0]) {
continue;
}
buf[strlen(buf) - 1] = 0;
m_process_alias(o, d, buf);
}
fclose(f);
return 0;
}
int
m_process_alias(struct options *o, struct data *d, char *buf)
{
char *p = buf,
account[128],
alias[128];
int i = 0,
gotcolon = 0,
needq = 0;
while (*p && i < sizeof(alias) - 1) {
if (*p == ':') {
p++;
gotcolon = 1;
break;
}
/*
if (!isspace(*p)) {
alias[i++] = *p;
}
*/
alias[i++] = *p;
p++;
}
alias[i] = 0;
if (!i || !gotcolon) {
return (m_err(__LINE__, "Corrupt Alias", buf));
}
if (o->dom &&
!strchr(alias, '@') &&
!strchr(alias, '|') &&
!strchr(alias, '/')) {
strncat(alias, "@", sizeof(alias) - strlen(alias));
strncat(alias, o->domain, sizeof(alias) - strlen(alias));
}
while (isspace(*p)) {
p++;
}
i = 0;
if (*p == '"') {
needq = 1;
p++;
}
while (*p && i < sizeof(account) - 1) {
if (*p == ',') {
if (!i) {
return (m_err(__LINE__, "Corrupt Alias", buf));
}
account[i] = 0;
if (m_add_alias(o, d, account, alias) < 0) {
return -1;
}
i = 0;
p++;
while (isspace(*p)) {
p++;
}
if (*p == '"') {
needq = 1;
p++;
}
continue;
}
if (needq && *p == '"') {
needq = 0;
p++;
continue;
}
account[i++] = *p++;
}
account[i] = 0;
if (!i) {
return (m_err(__LINE__, "Corrupt Alias", buf));
}
if (m_add_alias(o, d, account, alias) < 0) {
return -1;
}
return 0;
}
int
m_add_alias(struct options *o, struct data *d, char *account, char *alias)
{
if (d->ali >= d->alcount) {
if ( !(d->al = realloc( d->ali ? d->al : NULL,
(d->ali + 1 + M_INCREMENT) * sizeof(struct alias *) ))) {
return (m_err(__LINE__, "realloc", strerror(errno)));
}
d->alcount += M_INCREMENT;
}
if ( !(d->al[d->ali] = malloc(sizeof(struct alias)))) {
return (m_err(__LINE__, "malloc", strerror(errno)));
}
snprintf(d->al[d->ali]->account,
sizeof(d->al[d->ali]->account) - 1,
account);
snprintf(d->al[d->ali]->alias,
sizeof(d->al[d->ali]->alias) - 1,
alias);
if (hash_insert_str(d->halias, d->al[d->ali]->alias, d->al[d->ali])) {
return (m_err(__LINE__, "hash_insert_str", ""));
}
(d->ali)++;
return 0;
}
int
m_init(struct options *o, struct data *d)
{
memset(d, 0, sizeof(struct data));
if ( !(d->in = fopen(o->in, "r"))) {
return (m_err(__LINE__, o->in, strerror(errno)));
}
if (!strlen(o->out)) {
d->out = stdout;
} else {
if ( !(d->out = fopen(o->out, "w"))) {
return (m_err(__LINE__, o->out, strerror(errno)));
}
}
if ( !(d->mbuf = malloc( (M_BUFSIZE * sizeof(char)) + 1)) ) {
return (m_err(__LINE__, "malloc", strerror(errno)));
}
d->mbufsize = M_BUFSIZE * sizeof(char);
if ( !(d->hfrom = hash_alloc(2)) ||
!(d->hto = hash_alloc(2))) {
return (m_err(__LINE__, "hash_alloc", "No memory"));
}
/*
if (o->alias) {
m_load_aliases(o, d);
}
*/
return 0;
}
int
m_last_resort(struct options *o, struct data *d)
{
char *p;
if (strstr(d->mbuf, USER_UNKNOWN) &&
!(strstr(d->mbuf, DSN)) &&
!(strstr(d->mbuf, RETURN))) {
d->merrs.userunknown++;
if (m_add_problem(o, d, M_NO_USER) < 0) {
return -1;
}
} else if (strstr(d->mbuf, NORELAY) && !(strstr(d->mbuf, RULESET))) {
d->merrs.norelay++;
if (m_add_problem(o, d, M_NO_RELAY) < 0) {
return -1;
}
} else if (strstr(d->mbuf, LOSTINPUT)) {
snprintf(d->mto.stat, sizeof(d->mto.stat) - 1, LOSTINPUT);
d->mto.status = 1;
if (m_add_to(o, d) < 0) {
return -1;
}
} else if ( (p = strstr(d->mbuf, CLONE))) {
snprintf(d->mto.stat, sizeof(d->mto.stat) - 1, CLONE);
d->mto.status = 1;
if (m_get_clone_id(o, d, p) < 0) {
return -1;
}
if (m_add_to(o, d) < 0) {
return -1;
}
} else if (strstr(d->mbuf, EOM_ERROR)) {
d->merrs.eomerror++;
snprintf(d->mto.stat, sizeof(d->mto.stat) - 1, EOM_ERROR);
d->mto.status = 1;
if (m_add_to(o, d) < 0) {
return -1;
}
}
return 0;
}
int
m_get_clone_id(struct options *o, struct data *d, char *p)
{
int i = 0;
if (!p) {
return -1;
}
p += strlen(CLONE);
while (isspace(*p)) {
p++;
}
while (*p && *p != ',' && i < sizeof(d->id) - 1) {
d->id[i++] = *p++;
}
d->id[i] = 0;
if (!i) {
fprintf(stderr, "Error getting clone id\n%s\n", d->mbuf);
}
if (o->dbg) {
printf("CLONE: %s\n", d->id);
}
return 0;
}
int
m_add_problem(struct options *o, struct data *d, int flag)
{
char *p;
int needgt,
i,
size = sizeof(d->mto.email) - 1;
if ( !(p = strstr(d->mbuf, d->id))) {
return (m_err(__LINE__, "No ID", d->mbuf));
}
p += strlen(d->id) + 1;
while (isspace(*p)) {
p++;
}
needgt = i = 0;
if (*p == '<') {
needgt = 1;
p++;
}
while (*p && strncmp(p, "...", 3) != 0 && i < size) {
if (needgt && *p == '>') {
break;
}
d->mto.email[i++] = *p++;
}
d->mto.email[i] = 0;
if (!i) {
strncpy(d->mto.email, M_UNKNOWN, size);
} else {
while (d->mto.email[--i] == ' ') {
d->mto.email[i] = 0;
}
if (o->dom && !strchr(d->mto.email, '@')) {
strncat(d->mto.email, "@", size - strlen(d->mto.email) - 1);
strncat(d->mto.email, o->domain, size - strlen(d->mto.email) - 1);
}
}
if (flag == M_NO_USER) {
snprintf(d->mto.stat, sizeof(d->mto.stat) - 1, USER_UNKNOWN);
if (o->dbg) {
printf("TO : [%s] (User Unknown)\n", d->mto.email);
}
} else if (flag == M_NO_RELAY) {
snprintf(d->mto.stat, sizeof(d->mto.stat) - 1, NORELAY);
if (o->dbg) {
printf("TO : [%s] (No Relay)\n", d->mto.email);
}
}
d->mto.status = 1;
if (m_add_to(o, d) < 0) {
return -1;
}
return 0;
}
int
m_get_from_email(struct options *o, char **p, char *email, int size)
{
char *cp = *p + strlen(FROM);
int i;
if ( !(i = m_get_email(cp, email, size))) {
strncpy(email, M_UNKNOWN, size);
} else {
while (email[--i] == ' ') {
email[i] = 0;
}
if (o->dom && !strchr(email, '@')) {
strncat(email, "@", size - strlen(email) - 1);
strncat(email, o->domain, size - strlen(email) - 1);
}
}
*p = strstr(cp, SIZE);
return 0;
}
int
m_get_nrcpts(struct options *o, struct data *d)
{
char *cp,
*cp2;
if ( !(cp = strstr(d->mbuf, NRCPTS))) {
DBG("Possibly incorrect data line, no nrcpts=");
return 1;
}
cp += strlen(NRCPTS);
if ( !(cp2 = strchr(cp, ','))) {
DBG("Returning, no comma found");
return 1;
}
*cp2 = 0;
d->mfrom.nrcpts = atoi(cp);
*cp2 = ',';
if (o->dbg) {
printf("NRCPT: [%d]\n", d->mfrom.nrcpts);
}
return 0;
}
int
m_get_from_size(struct options *o, char *p, unsigned long *size)
{
char *cp,
*cp2;
if (!p) {
DBG("Possibly incorrect data line, no size");
return 1;
}
cp = p + strlen(SIZE);
if ( !(cp2 = strchr(cp, ','))) {
DBG("Returning, no comma found");
return 1;
}
*cp2 = 0;
*size = atol(cp);
*cp2 = ',';
return 0;
}
int
m_getid(struct options *o, char *id, int size, char *buf)
{
char *p;
int i = 0;
if ( !(p = strchr(buf, ']'))) {
m_corruptline(o, buf);
return 1;
}
while (!isalnum(*p)) {
p++;
}
if (strncasecmp(p, M_LOGIN, strlen(M_LOGIN)) == 0 ||
strncasecmp(p, M_LOGOUT, strlen(M_LOGOUT)) == 0 ||
strncasecmp(p, M_DAEMON, strlen(M_DAEMON)) == 0 ||
strncasecmp(p, M_ALIAS, strlen(M_ALIAS)) == 0) {
return 1;
}
while (*p != ':') {
if (i >= size - 1) {
id[i] = 0;
return 1;
}
if (!isspace(*p)) {
id[i++] = *p;
}
p++;
}
id[i] = 0;
if (!i) {
m_corruptline(o, buf);
return 1;
}
return 0;
}
int
m_report(struct options *o, struct data *d)
{
if (m_get_final(o, d) < 0) {
return -1;
}
m_sort(o, d);
if (m_print_output(o, d) < 0) {
return -1;
}
if (o->showerrs) {
m_show_errors(o, d);
}
if (!o->summary && !o->nostats) {
m_show_stats(o, d);
}
return 0;
}
int
m_print_output(struct options *o, struct data *d)
{
int i;
if (!d->finali) {
fprintf(stderr, "\
\n\
+++++++++++++++++++++++++++++++++++++++++++++\n\
+ No records found +\n\
+ Are you sure this is a sendmail log file? +\n\
+++++++++++++++++++++++++++++++++++++++++++++\n\n");
fflush(stderr);
return -1;
}
if (o->dbg) {
printf("\n---------[ OUTPUT FOLLOWS ]--------------\n\n");
}
if (!o->summary) {
fprintf(d->out, " [%s] [%s] ", o->machine, o->in);
if (o->from) {
fprintf(d->out, "[Summary: From]");
} else if (o->to) {
fprintf(d->out, "[Summary: To]");
}
fprintf(d->out, "\n\n");
}
for (i = 0; i < ( (o->max && o->max < d->finali) ? o->max : d->finali); i++) {
d->total += d->mfinal[i]->size;
if (o->max && !o->summary) {
fprintf(d->out, "%3d. ", i + 1);
}
fprintf(d->out,
"%s %c%4d%c",
!o->summary && d->mfinal[i]->size > o->big ? "*" : " ",
o->summary ? ' ' : '[',
d->mfinal[i]->num,
o->summary ? ' ' : ']');
fprintf(d->out, "%9lu ", d->mfinal[i]->size);
if (o->from) {
fprintf(d->out, "%-30s ", d->mfinal[i]->from);
} else if (o->to) {
fprintf(d->out, "%-30s ", d->mfinal[i]->to);
} else {
fprintf(d->out, "%-30s %-30s",
d->mfinal[i]->from, d->mfinal[i]->to);
}
fprintf(d->out, "\n");
}
return 0;
}
int
m_show_errors(struct options *o, struct data *d)
{
int i;
char tmp[32] = "";
if (!d->erri) {
return 0;
}
fprintf(d->out, "\n\n\tError information\n");
fprintf(d->out, "\t-----------------\n");
qsort(d->merrinfo, d->erri, sizeof(struct errinfo *), m_cmperrs);
for (i = 0; i < d->erri; i++) {
if (strcmp(d->merrinfo[i]->stat, tmp) != 0) {
fprintf(d->out, "\n[%s]\n", d->merrinfo[i]->stat);
strncpy(tmp, d->merrinfo[i]->stat, sizeof(tmp) - 1);
}
fprintf(d->out, " o [%d] [%s]->[%s]\n",
d->merrinfo[i]->num,
d->merrinfo[i]->from,
d->merrinfo[i]->to);
}
return 0;
}
int
m_show_stats(struct options *o, struct data *d)
{
char t[32];
int line = 0;
fprintf(d->out, "\n\
=====================\n\
Total Bytes : %lu\n\
Number of Records : %d\n\
---------------------\n",
d->total,
(o->max && o->max < d->finali) ? o->max : d->finali);
m_errs_summary(d, d->merrs.deferred, &line, "Deferred Messages :");
m_errs_summary(d, d->merrs.hostunknown, &line, "Host Unknown :");
m_errs_summary(d, d->merrs.userunknown, &line, "User Unknown :");
m_errs_summary(d, d->merrs.eomerror, &line, "Premature EOM Error :");
m_errs_summary(d, d->merrs.ioerror, &line, "I/O Error :");
m_errs_summary(d, d->merrs.noservice, &line, "Service Unavailable :");
m_errs_summary(d, d->merrs.norelay, &line, "We Don't Relay :");
if (line) {
fprintf(d->out, "---------------------\n");
}
fprintf(d->out, "\
Host Name : %s\n\
Input File : %s\n\
Output File : %s\n\
First Record : %s\n\
Last Record : %s\n\
---------------------\n\
Time Taken : %s\n\
=====================\n",
o->machine,
o->in,
strlen(o->out) ? o->out : "stdout",
d->first, d->last,
dx_timerStopPrint(&tv, t, sizeof(t) - 1));
fprintf(d->out, "%s-%s by %s\n\n",
o->prog, M_VERSION, M_DEVELOPER);
return 0;
}
int
m_errs_summary(struct data *d, int n, int *l, char *s)
{
if (n) {
fprintf(d->out, "%s %d\n", s, n);
(*l)++;
}
return 0;
}
int
m_get_final(struct options *o, struct data *d)
{
unsigned int x;
int found = 0;
char t[32];
void *vp;
struct hash_node *hn,
*hn2;
if (o->timer) {
printf("Time [scanning file] : %s\n",
dx_timerStopPrint(&tv, t, sizeof(t) - 1));
}
for (x = 0; x < d->hfrom->h_hashsize; ++x) {
for (hn = d->hfrom->h_hash[x]; hn; hn = hn->h_next) {
found = 0;
hn2 = NULL;
while ( (vp = hash_lookup_str2(d->hto,
(char *) hn->h_key,
&found,
&hn2))) {
/*
if (strlen( ((struct to *)(vp))->ctrl) &&
strcmp( ((struct from *)(hn->h_data))->email,
((struct to *)(vp))->ctrl) != 0) {
printf("ERROR_DUPLICATE\n");
continue;
}
*/
if ( !((struct to *)(vp))->status) {
if (m_add_final(o, d, hn->h_data, vp) < 0) {
return -1;
}
} else {
if (m_add_error(o, d, hn->h_data, vp) < 0) {
return -1;
}
}
}
if (!found && o->dbg) {
printf("NOT FOUND: %s [%9lu] [%s]\n",
(char *) hn->h_key,
((struct from *)(hn->h_data))->size,
((struct from *)(hn->h_data))->email);
}
}
}
if (o->timer) {
printf("Time [processing %d records] : %s\n",
(o->max && o->max < d->finali) ? o->max : d->finali,
dx_timerStopPrint(&tv, t, sizeof(t) - 1));
}
return 0;
}
int
m_add_error(struct options *o, struct data *d, void *from, void *to)
{
int i;
if ( (o->str || o->ex) && m_checkstring(o,
((struct from *)(from))->email,
((struct to *)(to))->email)) {
return 0;
}
for (i = 0; i < d->erri; i++) {
if (strcasecmp(d->merrinfo[i]->from,
((struct from *)(from))->email) == 0 &&
strcasecmp(d->merrinfo[i]->to,
((struct to *)(to))->email) == 0 &&
strcasecmp(d->merrinfo[i]->stat,
((struct to *)(to))->stat) == 0) {
(d->merrinfo[i]->num)++;
return 0;
}
}
if (d->erri >= d->errcount) {
if ( !(d->merrinfo = realloc( d->erri ? d->merrinfo : NULL,
(d->erri + 1 + M_INCREMENT) * sizeof(struct errinfo *) ))) {
return (m_err(__LINE__, "realloc", strerror(errno)));
}
d->errcount += M_INCREMENT;
}
if ( !(d->merrinfo[d->erri] = malloc(sizeof(struct errinfo)))) {
return (m_err(__LINE__, "malloc", strerror(errno)));
}
snprintf(d->merrinfo[d->erri]->from,
sizeof(d->merrinfo[d->erri]->from) - 1,
((struct from *)(from))->email);
snprintf(d->merrinfo[d->erri]->to,
sizeof(d->merrinfo[d->erri]->to) - 1,
((struct to *)(to))->email);
snprintf(d->merrinfo[d->erri]->stat,
sizeof(d->mfinal[d->finali]->to) - 1,
((struct to *)(to))->stat);
d->merrinfo[d->erri]->num = 1;
(d->erri)++;
return 0;
}
int
m_add_final(struct options *o, struct data *d, void *from, void *to)
{
int i,
found;
if ( (o->str || o->ex) && m_checkstring(o,
((struct from *)(from))->email,
((struct to *)(to))->email)) {
return 0;
}
for (i = found = 0; i < d->finali; i++) {
if (o->from) {
if (strcasecmp(d->mfinal[i]->from,
((struct from *)(from))->email) == 0) {
found = 1;
break;
}
} else if (o->to) {
if (strcasecmp(d->mfinal[i]->to,
((struct to *)(to))->email) == 0) {
found = 1;
break;
}
} else {
if (strcasecmp(d->mfinal[i]->to,
((struct to *)(to))->email) == 0 &&
strcasecmp(d->mfinal[i]->from,
((struct from *)(from))->email) == 0) {
found = 1;
break;
}
}
}
if (found) {
d->mfinal[i]->size += ((struct from *)(from))->size;
d->mfinal[i]->num++;
} else {
if (d->finali >= d->finalcount) {
if ( !(d->mfinal = realloc( d->finali ? d->mfinal : NULL,
(d->finali + 1 + M_INCREMENT) * sizeof(struct final *) ))) {
return (m_err(__LINE__, "realloc", strerror(errno)));
}
d->finalcount += M_INCREMENT;
}
if ( !(d->mfinal[d->finali] = malloc(sizeof(struct final)))) {
return (m_err(__LINE__, "malloc", strerror(errno)));
}
memcpy(d->mfinal[d->finali]->from,
((struct from *)(from))->email,
sizeof(d->mfinal[d->finali]->from) - 1);
d->mfinal[d->finali]->from[(strlen((char *)((struct from *)(from))->email))] = 0;
memcpy(d->mfinal[d->finali]->to,
((struct to *)(to))->email,
sizeof(d->mfinal[d->finali]->to) - 1);
d->mfinal[d->finali]->to[(strlen((char *)((struct to *)(to))->email))] = 0;
d->mfinal[d->finali]->size = ((struct from *)(from))->size;
d->mfinal[d->finali]->num = 1;
(d->finali)++;
}
return 0;
}
int
m_sort(struct options *o, struct data *d)
{
char t[32];
switch (o->order) {
case 2 :
qsort(d->mfinal,
d->finali,
sizeof(struct final *),
o->to ? m_cmpemailto : m_cmpemailfrom);
break;
case 1 :
qsort(d->mfinal,
d->finali,
sizeof(struct final *),
m_cmpnum);
break;
case 0 :
default:
qsort(d->mfinal,
d->finali,
sizeof(struct final *),
m_cmpsize);
}
if (o->timer) {
printf("Time [sorting] : %s\n",
dx_timerStopPrint(&tv, t, sizeof(t) - 1));
}
return 0;
}
int
m_cmpnum(const void *n1, const void *n2)
{
struct final **ms1 = (struct final **) n1;
struct final **ms2 = (struct final **) n2;
int num1 = (*ms1)->num;
int num2 = (*ms2)->num;
if (num1 != num2) {
return (num2 - num1);
}
return 0;
}
int
m_cmpsize(const void *s1, const void *s2)
{
struct final **ms1 = (struct final **) s1;
struct final **ms2 = (struct final **) s2;
unsigned long n1 = (*ms1)->size;
unsigned long n2 = (*ms2)->size;
if (n1 == n2) {
return 0;
} else if (n1 > n2) {
return -1;
} else {
return 1;
}
}
int
m_cmpemailfrom(const void *s1, const void *s2)
{
struct final **ms1 = (struct final**) s1;
struct final **ms2 = (struct final **) s2;
char *p1 = (*ms1)->from;
char *p2 = (*ms2)->from;
return (strcasecmp(p1, p2));
}
int
m_cmpemailto(const void *s1, const void *s2)
{
struct final **ms1 = (struct final**) s1;
struct final **ms2 = (struct final **) s2;
char *p1 = (*ms1)->to;
char *p2 = (*ms2)->to;
return (strcasecmp(p1, p2));
}
int
m_get_email(char *str, char *email, int size)
{
char *cp = str;
int i = 0,
needgt = 0,
needq = 0;
if (*cp == '<') {
needgt = 1;
cp++;
}
if (*cp == '"') {
needq = 1;
cp++;
}
while (*cp && *cp != ',' && i < size) {
if ( (needgt && *cp == '>') ||
(needq && *cp == '"')) {
break;
}
email[i++] = *cp++;
}
email[i] = 0;
return (i);
}
int
m_cmperrs(const void *s1, const void *s2)
{
struct errinfo **ms1 = (struct errinfo**) s1;
struct errinfo **ms2 = (struct errinfo **) s2;
char *p1 = (*ms1)->stat;
char *p2 = (*ms2)->stat;
return (strcasecmp(p1, p2));
}
int
m_checkstring(struct options *o, char *from, char *to)
{
char *p,
*cp;
if (o->to) {
if (o->ex) {
if (strstr(to, o->exclude)) {
return 1;
}
}
p = strrchr(to, '@');
switch (o->str) {
case 1 :
if (!strstr(p ? p : to, o->string)) {
return 1;
}
break;
case 2 :
if (!strstr(to, o->string2)) {
return 1;
}
break;
case 3 :
if (!strstr(p ? p : to, o->string) ||
!strstr(to, o->string2)) {
return 1;
}
break;
default :
return 0;
}
} else if (o->from) {
if (o->ex) {
if (strstr(from, o->exclude)) {
return 1;
}
}
p = strrchr(from, '@');
switch (o->str) {
case 1 :
if (!strstr(p ? p : from, o->string)) {
return 1;
}
break;
case 2 :
if (!strstr(from, o->string2)) {
return 1;
}
break;
case 3 :
if (!strstr(p ? p : from, o->string) ||
!strstr(from, o->string2)) {
return 1;
}
break;
default :
return 0;
}
} else {
if (o->ex) {
if ( (strstr(to, o->exclude)) ||
(strstr(from, o->exclude)) ) {
return 1;
}
}
p = strrchr(from, '@');
cp = strrchr(to, '@');
switch (o->str) {
case 1 :
if (!strstr(p ? p : from, o->string) &&
!strstr(cp ? cp : to, o->string)) {
return 1;
}
break;
case 2 :
if (!strstr(from, o->string2) &&
!strstr(to, o->string2)) {
return 1;
}
break;
case 3 :
if ( (!strstr(p ? p : from, o->string) ||
!strstr(from, o->string2)) &&
(!strstr(cp ? cp : to, o->string) ||
!strstr(to, o->string2)) ) {
return 1;
}
break;
default :
return 0;
}
}
return 0;
}
int
m_corruptline(struct options *o, char *buf)
{
if (o->dbg) {
printf(" -> Corrupt Line (ID), skipping : \n%s\n", buf);
}
return 0;
}
int
m_getconfig(struct options *o, int ac, char *av[])
{
char *optstr = "i:o:c:hd:ftrsx:b:-vgm:el:p:an:yzu:",
*p,
t[32];
int opt;
dx_startTimer(&tv);
memset(o, 0, sizeof(struct options));
strncpy(o->prog,
(p = strrchr(av[0], '/')) ? p + 1 : av[0],
sizeof(o->prog) - 1);
while ( (opt = getopt(ac, av, optstr)) != EOF) {
switch (opt) {
case 'i' :
strncpy(o->in, optarg, sizeof(o->in) - 1);
break;
case 'o' :
strncpy(o->out, optarg, sizeof(o->out) - 1);
break;
case 'c' :
strncpy(o->config, optarg, sizeof(o->config) - 1);
break;
case 'd' :
strncpy(o->domain, optarg, sizeof(o->domain) - 1);
o->dom = 1;
break;
case 'f' :
o->from = 1;
break;
case 't' :
o->to = 1;
break;
case 'l' :
strncpy(o->string, optarg, sizeof(o->string) - 1);
o->str += 1;
break;
case 'p' :
strncpy(o->string2, optarg, sizeof(o->string2) - 1);
o->str += 2;
break;
case 'x' :
strncpy(o->exclude, optarg, sizeof(o->exclude) - 1);
o->ex = 1;
break;
case 'g' :
o->dbg = 1;
break;
case 'm' :
o->max = atoi(optarg);
break;
case 'n' :
o->nice = atoi(optarg);
break;
case 'z' :
o->timer = 1;
break;
case 'r' :
o->order = 1;
break;
case 'e' :
o->order = 2;
break;
case 's' :
o->summary = 1;
break;
case 'b' :
o->big = atoi(optarg);
break;
case 'v' :
version(o->prog);
return -1;
case 'a' :
o->showerrs = 1;
break;
case 'y' :
o->nostats = 1;
break;
case 'u' :
strncpy(o->aliases, optarg, sizeof(o->aliases) - 1);
o->alias = 1;
break;
case '-' :
case 'h' :
default :
usage(o->prog);
return -1;
}
}
if (m_checkopt(o) < 0) {
usage(o->prog);
return -1;
}
if (o->timer) {
printf("Time [getconfig] : %s\n",
dx_timerStopPrint(&tv, t, sizeof(t) - 1));
}
return 0;
}
int
m_checkopt(struct options *o)
{
if (!strlen(o->in)) {
DBG("Using Default Maillog");
strncpy(o->in, DEFAULT_MAILLOG, sizeof(o->in) - 1);
}
if (o->from && o->to) {
printf("Error: Summarise either -f <from> or -t <to>, not both\n\n");
return -1;
}
if (gethostname(o->machine, sizeof(o->machine) - 1) < 0) {
strncpy(o->machine, "Unknown", sizeof(o->machine) - 1);
}
if (o->big <= 0) {
o->big = DEFAULT_BIG;
}
if (setpriority(PRIO_PROCESS, 0, o->nice) < 0) {
fprintf(stderr, "Error : setpriority(%d): %s\n",
o->nice, strerror(errno));
}
DBG("- - - - - - - ");
DBG(o->in);
DBG(o->out);
DBG("- - - - - - - ");
return 0;
}
int
m_err(int line, char *s, char *err)
{
fprintf(stderr, "[%d] Error: %s: %s\n", line, s, err);
fflush(stderr);
return -1;
}
void
version(char *progname)
{
printf("%s-%s\n", progname, M_VERSION);
}
void
usage(char *progname)
{
printf("\
usage: %s [options]\n\
\n\
-f | -t Summarise from or to\n\
-i <input file> Mail Log File (default: %s)\n\
-o <output file> Output File (default: stdout)\n\
-d <domain> Append <domain> to addresses without one\n\
-p <string> Only addresses with <string> in them\n\
-l <string> Only addresses with <string> in domain\n\
-x <string> Exclude addresses containing <string>\n\
-r Order by number of messages, not by size\n\
-e Order by email address, not by size\n\
-b <number> Flag msgs > (default: %d)\n\
-n <number> Nice (priority) (default: %d)\n\
-m <number> Show only <number> records\n\
-s Short form (summary only)\n\
-a Show error summary\n\
-y Don't show statistical information\n\
-v Version\n\
-h Help\n\
\n\
Version: %s\n\
Author : %s\n",
progname,
DEFAULT_MAILLOG, DEFAULT_BIG, DEFAULT_NICE,
M_VERSION, M_DEVELOPER);
}
syntax highlighted by Code2HTML, v. 0.9.1