siebeck@infoac.rmi.de (Wolfgang Siebeck ) (03/02/91)
Submitted-by: root@atreju.rmi.de (Wolfgang Siebeck)
Archive-name: Mailutil/part01
Since I received several answers to my offer of these tools within two
hours, I decided to post the stuff. Sorry about the documentation, this was
designed for local use, so the comments are mainly German, but I tried to
translate the important parts. There is a README file included, where I
explain the idea behind the package.
During the tests, I ran the package against the syslog of a SUN 3/50 and
a Intel machine running SYS V. Both gave the desired results :-).
Enjoy it and - well at least it should point to a way to interpret
the syslog.
Requests were coming from Dan Ehrlich, Kerry G. Forschler, David Asher,
Matt Goldman, and darrah. Thank You for encouraging me to try my first
posting!
---- Cut Here and feed the following to sh ----
#!/bin/sh
# This is Mailutil, a shell archive (produced by shar 3.49)
# To extract the files from this archive, save it to a file, remove
# everything above the "!/bin/sh" line above, and type "sh file_name".
#
# made 03/01/1991 16:08 UTC by root@atreju.rmi.de (Wolfgang Siebeck)
#
# existing files will NOT be overwritten unless -c is specified
#
# This shar contains:
# length mode name
# ------ ---------- ------------------------------------------
# 946 -rwxr-xr-x Mailutil/MailRech
# 338 -rw-r--r-- Mailutil/Makefile
# 6283 -rw-r--r-- Mailutil/README
# 4565 -rw-r--r-- Mailutil/auswlog.awk
# 13260 -rw------- Mailutil/auswlog.c
# 4176 -rw-r--r-- Mailutil/justlog.c
# 1359 -rw-r--r-- Mailutil/mailstat1.awk
# 1769 -rw-r--r-- Mailutil/mailstat2.awk
# 110 -rw-r--r-- Mailutil/mailutil.h
#
# ============= Mailutil/MailRech ==============
if test ! -d 'Mailutil'; then
echo 'x - creating directory Mailutil'
mkdir 'Mailutil'
fi
if test -f 'Mailutil/MailRech' -a X"$1" != X"-c"; then
echo 'x - skipping Mailutil/MailRech (File already exists)'
else
echo 'x - extracting Mailutil/MailRech (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'Mailutil/MailRech' &&
X :
#
# usage:
#
# MailRech syslogname [hostname]
#
# Generate invoices from syslog
#
X
HOST=`hostname`
if [ $# -gt 1 ]
then
X HOST=$2
fi
export $HOST
X
grep sendmail $1 | justlog | sort > Sendmail.srt
X
#
X
auswlog Sendmail.srt $HOST
rm -f Sendmail.srt
if [ -f $HOST.out ]
then
X rm -f $HOST.out
fi
mv Sendmail.out $HOST.out
echo "Input file for various statistics generated ($HOST.out)"
X
#
# Ok, go for the money:
#
X
# cat $HOST.out | nawk -f auswlog.awk host=$HOST > $HOST.bill
X
#
# Since the script 'auswlog.awk' makes use of functions, only nawk
# will work here. Anyway, it is a sample. You may try
# the other samples like
#
# cat $HOST.out | awk -f mailstat1.awk
#
# for a summary with fancy graphics :-)
#
# or
#
# cat $HOST.out | awk -f mailstat2.awk
#
# for a sample with calculated costs. If you have nawk, You
# might use the function round() to get real rounding instead
# of the floating point format in printf.
#
X
#
X
echo done.
Xexit 0
SHAR_EOF
chmod 0755 Mailutil/MailRech ||
echo 'restore of Mailutil/MailRech failed'
Wc_c="`wc -c < 'Mailutil/MailRech'`"
test 946 -eq "$Wc_c" ||
echo 'Mailutil/MailRech: original size 946, current size' "$Wc_c"
fi
# ============= Mailutil/Makefile ==============
if test -f 'Mailutil/Makefile' -a X"$1" != X"-c"; then
echo 'x - skipping Mailutil/Makefile (File already exists)'
else
echo 'x - extracting Mailutil/Makefile (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'Mailutil/Makefile' &&
#
# Makefile for the accounting utilities for
# sendmails log.
#
X
CC=gcc
X
CFLAGS=-O
X
LIBS=
X
.c.o:
X $(CC) $(CFLAGS) -c $<
X
all: auswlog justlog
X
auswlog: auswlog.o
X $(CC) $(CFLAGS) auswlog.o -o auswlog
X
justlog: justlog.o
X $(CC) $(CFLAGS) justlog.o -o justlog
X
clean:
X rm -f justlog.o auswlog.o core
X
clobber: clean
X rm -f justlog auswlog
SHAR_EOF
chmod 0644 Mailutil/Makefile ||
echo 'restore of Mailutil/Makefile failed'
Wc_c="`wc -c < 'Mailutil/Makefile'`"
test 338 -eq "$Wc_c" ||
echo 'Mailutil/Makefile: original size 338, current size' "$Wc_c"
fi
# ============= Mailutil/README ==============
if test -f 'Mailutil/README' -a X"$1" != X"-c"; then
echo 'x - skipping Mailutil/README (File already exists)'
else
echo 'x - extracting Mailutil/README (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'Mailutil/README' &&
README for the syslog accounting package.
X
(Excuse my lousy english! I hope, You get the idea, anyway...)
X
First let me express my thanks for Your interest. I am glad to
give some bytes in return to the information and help I got from
the usenet community.
X
-----------------
X
These utilities may be used to extract charging information from the
file /usr/spool/mqueue/syslog. We are using this package in a commercial
mailbox service.
X
Oh, I forgot: There are ANSI headers for gcc, the programs pass
X gcc without warnings.
X
The provided shell script 'MailRech' is a kind of front end to the stuff.
X
This package is not a masterpiece in software engineering, but it serves
us well. The base is: Never invent the wheel again. Use the Unix
programs whereever they will do the job. So the way from the syslog to
the accounting information is:
X
Step 1. use grep to extract the lines dealing with sendmail's output.
Step 2. Reformat the lines, so they can be sorted on the queue-id.
Step 3. Perform the sort.
X
What we have now, is a new logfile, in which only the relevant parts are
contained. By sorting, multiple to= lines are grouped together and are
easy to handle.
X
Step 4. Generate one log entry for each transmission. If one message is
X sent to multiple receivers, generate one line each.
X
Step 5. Now comes the best: use awk to generate whatever you want. Awk
X is not the best performer, but I am a real *fan* of it. Make bar
X charts, write bills, summarize, ... or throw it away.
X
X
Now we get the data:
X
Step 1.
-------
X
The input looks as follows (after grep sendmail ... ).
The file is generated on a SUN 3/60.
X
-------------
X
Jan 23 05:48:10 infohh: 6890 sendmail: AA06890: message-id=<9101230448.AA06890@infohh.rmi.de>
Jan 23 05:48:10 infohh: 6890 sendmail: AA06890: from=percomp, size=1034, class=0
Jan 23 05:48:18 infohh: 6893 sendmail: AA06890: to=frisk@rhi.hi.is, delay=00:00:10, stat=Sent
Jan 23 08:18:39 infohh: 7150 sendmail: AA07150: message-id=<9101230718.AA07150@infohh.rmi.de>
Jan 23 08:18:39 infohh: 7150 sendmail: AA07150: from=news, size=107, class=0
Jan 23 08:18:41 infohh: 7153 sendmail: AA07150: to=root, delay=00:00:04, stat=Sent
Jan 23 08:33:17 infohh: 7188 sendmail: AA07188: message-id=<9101230733.AA07188@infohh.rmi.de>
Jan 23 08:33:17 infohh: 7188 sendmail: AA07188: from=natec, size=514, class=0
Jan 23 08:33:53 infohh: 7199 sendmail: AA07199: message-id=<9101230733.AA07199@infohh.rmi.de>
Jan 23 08:33:53 infohh: 7199 sendmail: AA07199: from=natec, size=506, class=0
Jan 23 08:33:24 infohh: 7191 sendmail: AA07188: to=postmaster@sh.cs.net, delay=00:00:09, stat=Sent
Jan 23 08:33:56 infohh: 7201 sendmail: AA07199: to=service@infohh.rmi.de, delay=00:00:04, stat=Sent
X
-------------
X
Step 2.
-------
X
We feed this through justlog. All irrelevant information is stripped.
(Notice the AA07188 and AA07199!)
X
--------------
AA06890:01:23:05:48:10:from=percomp, size=1034, class=0
AA06890:01:23:05:48:18:to=frisk@rhi.hi.is, delay=00:00:10, stat=Sent
AA07150:01:23:08:18:39:from=news, size=107, class=0
AA07150:01:23:08:18:41:to=root, delay=00:00:04, stat=Sent
AA07188:01:23:08:33:17:from=natec, size=514, class=0
AA07199:01:23:08:33:53:from=natec, size=506, class=0
AA07188:01:23:08:33:24:to=postmaster@sh.cs.net, delay=00:00:09, stat=Sent
AA07199:01:23:08:33:56:to=service@infohh.rmi.de, delay=00:00:04, stat=Sent
--------------
X
Step 3.
-------
X
Sort the stuff.
X
--------------
AA06890:01:23:05:48:10:from=percomp, size=1034, class=0
AA06890:01:23:05:48:18:to=frisk@rhi.hi.is, delay=00:00:10, stat=Sent
AA07150:01:23:08:18:39:from=news, size=107, class=0
AA07150:01:23:08:18:41:to=root, delay=00:00:04, stat=Sent
AA07188:01:23:08:33:17:from=natec, size=514, class=0
AA07188:01:23:08:33:24:to=postmaster@sh.cs.net, delay=00:00:09, stat=Sent
AA07199:01:23:08:33:53:from=natec, size=506, class=0
AA07199:01:23:08:33:56:to=service@infohh.rmi.de, delay=00:00:04, stat=Sent
--------------
X
Step 4.
-------
X
Reformat the file. Generate one line per mail.
X
--------------
percomp~01~23~05~48~1034~percomp~frisk@rhi~infohh~rhi
news~01~23~08~18~107~news~root~infohh~infohh
natec~01~23~08~33~514~natec~postmaster@sh~infohh~sh
natec~01~23~08~33~506~natec~service@infohh~infohh~infohh
--------------
X
What we have now, may be the general purpose input. The fields are:
X
X 1. The user to be charged. (More on this later...)
X 2. Month
X 3. Day
X 4. Hour
X 5. Minute
X 6. Bytes
X 7. Sender
X 8. Receiver
X 9. Sending host
10. Receiving host
X
X
X
The charging algorithm:
X
We run a server for fax, telex, teletex, videotex and uucp message
handling in Germany. Our own "network" are three Unix-based mailbox
systems using this service as well as some MESS-DOS based systems. Fax,
telex, teletex and videotex mail is charged seperately, so these
messages and the confirmations are filtered. Furthermore, if a user
sends a telex message, a copy of the message is archived in his box.
Naturally, this is not charged.
X
Generally speaking, the sender has to pay. There are two exceptions:
X
X a) The message is from a remote host via uucp/smtp. We have to pay for
X incoming mail, so the receiver on the local host is charged.
X
X b) A non-user may log into the system as user 'zczc' and write a
X message to an user. So the receiver is charged for the online time
X of Mr. ZCZC.
X
X
If you don't need this information, just ignore the first field in the
output and You are done.
X
X
As a sample of analysis, some awk scripts for simple statistics are
included in the package.
X
-------------
X
Please redirect flames on the stuff to /dev/null, comments, critics or
enhancements to
X
siebeck@infoac.rmi.de or postmaster@atreju.rmi.de
X
======================================================================
X
And here comes the standard disclaimer:
X
I do in no way guarantee, that these programs serve any purpose. I
shall not be made responsible for loss of money, sanity, free disk space
or whatever caused by these programs. All I can say is, I use them, and
they work for me.
X
If you want to distribute the stuff, feel free to do so, but keep the
stuff together. I don't claim special Copyrights or licensing fees on
it, but please don't sell it, give it away for free.
X
======================================================================
X
SHAR_EOF
chmod 0644 Mailutil/README ||
echo 'restore of Mailutil/README failed'
Wc_c="`wc -c < 'Mailutil/README'`"
test 6283 -eq "$Wc_c" ||
echo 'Mailutil/README: original size 6283, current size' "$Wc_c"
fi
# ============= Mailutil/auswlog.awk ==============
if test -f 'Mailutil/auswlog.awk' -a X"$1" != X"-c"; then
echo 'x - skipping Mailutil/auswlog.awk (File already exists)'
else
echo 'x - extracting Mailutil/auswlog.awk (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'Mailutil/auswlog.awk' &&
#########################################################################
#
# Rechnungserstellung fuer Electronic Mail
#
# Version 1.0
#
# (c) 1990 by RMI Nachrichtentechnik GmbH
#
#########################################################################
X
function round(i) {
X i = int((i*100) + 0.5)/100;
X return i;
}
X
#########################################################################
X
function rechnung() {
X if (rechnr > 0)
X print("\n\n--------------------------------------\n");
X rechnr++;
X ggeb = round(localmails[uname] * lmail);
X hostlmail += ggeb;
X
X k = round(localbytes[uname] / 1024);
X if ((k == 0) && (localmails[uname] > 0))
X k = 1;
X mgeb = k * lkbyte;
X hostlkbyte += mgeb;
X
X msteuer = 0.0;
X
X print ("Rechnung fuer Electronic Mail");
X printf("Username %s@%s\n\n\n", uname, host);
X print (" Grund- Mengen-");
X print (" Mails Bytes gebuehr gebuehr");
X print ("-------------------------------------------");
X printf("local %5d %8d %8.2f %8.2f\n", \
X localmails[uname], \
X localbytes[uname], \
X ggeb, \
X mgeb);
X k = round(remotebytes[uname] / 1024);
X if ((k == 0) && (remotemails[uname] > 0))
X k = 1;
X printf("rem. %5d %8d %8.2f %8.2f\n", \
X remotemails[uname], \
X remotebytes[uname], \
X round(remotemails[uname] * rmail), \
X round(remotebytes[uname] / 1024 * rkbyte));
X ggeb += round(remotemails[uname] * rmail);
X hostrmail += round(remotemails[uname] * rmail);
X
X mgeb += round(k * rkbyte);
X hostrkbyte += round(k * rkbyte);
X
X print ("-------------------------------------------");
X printf("total %5d %8d %8.2f %8.2f = %8.2f\n", \
X mails[uname], \
X bytes[uname], \
X ggeb, \
X mgeb,
X ggeb + mgeb);
X msteuer = round((ggeb + mgeb) * mwst);
X printf(" + %2d%% MwSt = %8.2f\n", \
X mwst * 100, msteuer);
X print (" --------");
X printf(" %8.2f\n", \
X ggeb + mgeb + msteuer);
X print (" ========");
X hostmwst += msteuer;
X
# print ("Berechnungsgrundlage:");
# print ("---------------------");
# printf("Lokale Mail: %8.2f DM pro Mail\n", lmail);
# printf(" %8.2f DM pro kByte\n", lkbyte);
# printf("Remote Mail: %8.2f DM pro Mail\n", rmail);
# printf(" %8.2f DM pro kByte\n", rkbyte);
}
X
#########################################################################
X
BEGIN {
X FS="~";
X if (host == "")
X host = "infohh";
X lmail = 0.0;
X lkbyte = 0.0;
X rmail = 0.05;
X rkbyte = 0.20;
X mwst = 0.14;
X rechnr = 0;
X hostlmail = 0.0;
X hostlkbyte = 0.0;
X hostrmail = 0.0;
X hostrkbyte = 0.0;
X hostmwst = 0.0;
}
X
#########################################################################
X
# # ## # # #
## ## # # # ## #
# ## # # # # # # #
# # ###### # # # #
# # # # # # ##
# # # # # # #
X
#########################################################################
/MAILER-DAEMON/ {
X next
}
/\/dev\/null/ {
X next
}
/news/ {
X next
}
X
{
X if (substr($0,1,1) == "~")
X next;
X
X mails[$1]++;
X if ($9 == $10) {
X localmails[$1]++;
X localbytes[$1] += $6;
X }
X else {
X remotemails[$1]++;
X remotebytes[$1]+= $6;
X }
X bytes[$1] += $6;
}
X
#########################################################################
X
END {
# i = 99;
X for (usr in mails) {
X uname = usr;
X rechnung();
# if (i > 20) {
# print("Username Mails Bytes");
# print("------------------------------------");
# i = 0;
# }
# printf("%-20s %6d %8d\n", usr, mails[usr], bytes[usr]);
# i++;
X }
# Jetzt noch die Summen fuer den Host ausgeben:
X print ("\n\n\n\n================================================\n");
X printf("Gesamtbetrag fuer den Host %s\n", host);
X print (" Grund- Mengen-");
X print (" gebuehr gebuehr");
X print ("---------------------------");
X printf("local %8.2f %8.2f\n", hostlmail, hostlkbyte);
X printf("remote %8.2f %8.2f\n", hostrmail, hostrkbyte);
X print ("---------------------------");
X printf("Summe %8.2f %8.2f = %8.2f\n", \
X hostlmail+hostrmail, \
X hostlkbyte+hostrkbyte, \
X hostlmail+hostrmail+hostlkbyte+hostrkbyte);
X msteuer = round((hostlmail+hostrmail+hostlkbyte+hostrkbyte) * mwst);
X printf(" + %2d%% MwSt = %8.2f\n", \
X mwst * 100, msteuer);
X print (" --------");
X printf(" %8.2f\n", \
X hostlmail+hostrmail+hostlkbyte+hostrkbyte+msteuer);
X print (" ========");
X
}
SHAR_EOF
chmod 0644 Mailutil/auswlog.awk ||
echo 'restore of Mailutil/auswlog.awk failed'
Wc_c="`wc -c < 'Mailutil/auswlog.awk'`"
test 4565 -eq "$Wc_c" ||
echo 'Mailutil/auswlog.awk: original size 4565, current size' "$Wc_c"
fi
# ============= Mailutil/auswlog.c ==============
if test -f 'Mailutil/auswlog.c' -a X"$1" != X"-c"; then
echo 'x - skipping Mailutil/auswlog.c (File already exists)'
else
echo 'x - extracting Mailutil/auswlog.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'Mailutil/auswlog.c' &&
/*
** Auswertung eines Log's, das bereits durch justlog
** und sort gegangen ist.
**
** WARNING: This program is a quick hack. It's designed for
** real addresses only. By saying real addresses, I
** talk about domain addressing, like user@host.domain.
** I did not try to decipher BANG's.
**
** The input must be sorted, so all AA.. items are grouped together.
** sort is also helpful, since the from= line will always be in front
** of the to= line. Care is taken for multiple to= lines per message
** and multiple receivers per to= line, as well.
**
** In the end, there will be one or more lines per message. If user
** joe_doe sends one message to five users, there will be five lines.
**
** The algorithm:
**
** Read input file, prepared by justlog and sorted.
** Make a clean line from input.
** No LF's or CR's, please.
** Initialize date and user information.
** Check, if new AA.. job.
** If yes, write new log file and initialize new line.
** Determine type of line (from= vs. to=)
** If from=, extract sender and msg-size.
** If to=, extract receiver(s). delay= and stat= are of
** no interest. (see justlog.c)
** Cleanup addresses (try to do so)
** Fill new log line according to information.
** until eof().
** If there is still a new line to write, do so.
**
** The writing of the new log line might be a overkill for Your
** purpose. The underlying situation is described in the documentation.
**
**
** Updates:
**
** Version 1.1 added support for bang addressing. There is still
** a problem with addresses like blah%fasel!host.domain.
**
** Version 1.2 Since I had some problems, when compiling on a Sun
** I changed the declaration of LZ (Logzeile,
** means: log line) and NLZ (new log line). I had
** forgotten about the word alignment in nested
** structures. Now it really works.
*/
X
#include <stdio.h>
#include <string.h>
#include <memory.h>
X
#include "mailutil.h"
X
/* length of various strings */
#define FNAMELEN 80
#define MAXLINELEN 256
#define MAXFROMLEN 160
#define MAXTOLEN 160
#define MAXNAMELEN 20
#define HOSTNAMELEN 10
X
/* Types of syslog-lines we recognize */
#define UNKNOWN 0
#define FROM 1
#define TO 2
X
/* What happens to the lines ? */
#define DROPIT 1
#define KEEPIT 0
X
#ifndef DEBUG
#define DEBUG 0
#endif
X
#define VERSION "1.2"
X
FILE *ifile,
X *ofile;
X
#define KENNZSIZE 23
X
/* Definition for read the old lines */
typedef union {
X struct {
X char kennz[8]; /* AA... von sendmail */
X char mon[3]; /* Monat u.s.w. */
X char day[3];
X char hour[3];
X char minute[3];
X char second[3];
X char rest[MAXLINELEN - 22]; /* from= und to= Zeile */
X } inh;
X char alles[MAXLINELEN + 1];
} LZ, *LZPTR;
X
/* Definition for writing the new lines */
typedef struct {
X char kennz[8]; /* AA... von sendmail */
X char mon[3]; /* Monat u.s.w. */
X char day[3];
X char hour[3];
X char minute[3];
X char second[3];
X char zahler[MAXNAMELEN + 1]; /* Zahlungspflichtiger */
X char from[MAXFROMLEN + 1]; /* Absender der Nachricht */
X char to[MAXTOLEN + 1]; /* Empfaenger der Nachricht */
X long len; /* Laenge der Nachricht */
} NLZ, *NLZPTR;
X
char defaulthost[HOSTNAMELEN + 1];
X
/*
** Aufraeumen in der Eingabezeile
*/
X
void
clearline(LZPTR lz)
{
X char *s;
X
X if (s = strrchr(lz->alles, '\r')) /* You never know, but this */
X *s = '\0'; /* might run under MESS-DOS */
X if (s = strrchr(lz->alles, '\n'))
X *s = '\0';
X lz->inh.kennz[7] =
X lz->inh.mon[2] =
X lz->inh.day[2] =
X lz->inh.hour[2] =
X lz->inh.minute[2] =
X lz->inh.second[2] = '\0';
}
X
/*
** Determine type of log line
*/
X
int
gettype(LZPTR lz)
{
X
#if DEBUG
X printf("Rest:%10.10s\n", lz->inh.rest);
#endif
X
X if (!strncmp(lz->inh.rest, "to=", 3))
X return TO;
X if (!strncmp(lz->inh.rest, "from=", 5))
X return FROM;
X return UNKNOWN;
}
X
/*
** Determine, if a log line is to be dropped
*/
X
int
dropthis(NLZPTR nlz, char *empfhost)
{
X /* Es fallen keine Gebuehren bei: */
X /* 1. Qsl's, Confirmations */
X if (!strncmp(nlz->to, "qsl@", 4))
X return DROPIT;
X
X /* 2. Eingehende Telexe, incoming telexes */
X if (!strncmp(nlz->to, "ttxin@", 6))
X return DROPIT;
X
X /* 3. Abgehende Faxe, outgoing faxes */
X if (!strcmp(empfhost, "fax"))
X return DROPIT;
X
X /* 4. "Ge-pipe-te" Sachen, stuff piped into programs */
X if (!strncmp(nlz->to, "\"|", 2))
X return DROPIT;
X
X /* 5. Abgehende Telexe, outgoing telexes */
X if (!strncmp(nlz->to, "ttx@rmi", 7))
X return DROPIT;
X
X /* 6. Mail an sich selbst, z.B. archivierte Telexe, archived telexes */
X if (!strcmp(nlz->from, nlz->to))
X return DROPIT;
X
X return KEEPIT;
}
X
/*
** Wegschreiben einer Zeile in das neue Log.
**
** Es gibt pro Nachricht eine oder mehrere Zeilen, je nach
** dem, wieviele Empfaenger existieren.
**
** Vor dem Wegschreiben wird noch ueberprueft, ob der Absender
** oder der Empfaenger die Gebuehren tragen soll. Als Erkennung gilt
** dabei:
**
** Feststellen des Absender-Hosts und des Empfaenger-Hosts.
** Ist kein Host zu finden, so wird der default eingesetzt.
** -------------------------
** Write one line to the new log file. For each message, there may
** be one or more lines according to the number of receivers.
**
** Check for sending and receiving hosts. If no host is found,
** insert name of default host (local mail).
*/
X
void
writelog(NLZPTR nlz)
{
X char abshost[HOSTNAMELEN + 1],
X empfhost[HOSTNAMELEN + 1],
X zahler[MAXLINELEN + 1];
X char *hname,
X *punkt;
X
X hname = strchr(nlz->from, '@'); /* user@host.domain */
X if (hname) {
X hname++;
X if ((punkt = strchr(hname, '.')) != NULL)
X *punkt = '\0';
X strncpy(abshost, hname, HOSTNAMELEN);
X abshost[HOSTNAMELEN] = '\0';
X } else
X strcpy(abshost, defaulthost);
X
X hname = strchr(nlz->to, '@');
X if (hname) {
X hname++;
X if ((punkt = strchr(hname, '.')) != NULL)
X *punkt = '\0';
X strncpy(empfhost, hname, HOSTNAMELEN);
X empfhost[HOSTNAMELEN] = '\0';
X } else
X strcpy(empfhost, defaulthost);
X
/*
** Now: If neither the receiving nor the sending host
** is the defaulthost, the mail was just routed and the
** accounting will be done on another host.
*/
X
X if (strcmp(empfhost, defaulthost) && strcmp(abshost, defaulthost))
X return;
X
X if (dropthis(nlz, empfhost) == DROPIT)
X return;
X
X /* Who should pay? By default, it is the sender! */
X strcpy(zahler, nlz->from);
X
X /* Except in case the receiver is on the default host ... */
X if (!strcmp(empfhost, defaulthost)) {
X
X /* but only, if not local! */
X if (strcmp(abshost, empfhost))
X strcpy(zahler, nlz->to);
X }
X if ((punkt = strchr(zahler, '@')) != NULL)
X *punkt = '\0';
X
/*
** Und wieder ein Sonderfall: Falls der Absender
** zczc ist, muss auch der Empfaenger zahlen! (Mail from Mr. ZCZC)
*/
X if (!strcmp(nlz->from, "zczc")) {
X strcpy(zahler, nlz->to);
X }
X strncpy(nlz->zahler, zahler, MAXNAMELEN);
X nlz->zahler[MAXNAMELEN] = '\0';
X
#if DEBUG
X printf("%s~%s~%s~%s~%s~%ld~%s~%s\n",
X nlz->zahler,
X nlz->mon,
X nlz->day,
X nlz->hour,
X nlz->minute,
X nlz->len,
X nlz->from,
X nlz->to);
#endif
X
X fprintf(ofile, "%s~%s~%s~%s~%s~%ld~%s~%s~%s~%s\n",
X nlz->zahler,
X nlz->mon,
X nlz->day,
X nlz->hour,
X nlz->minute,
X nlz->len,
X nlz->from,
X nlz->to,
X abshost,
X empfhost);
}
X
/*
** Try to clean up an address.
**
** This doesn't look to good, but bang address are bad manners anyway.
*/
X
void
clean_address(char *addr)
{
X char wbuff[MAXTOLEN + 1],
X *s1,
X *s2,
X host[HOSTNAMELEN + 1],
X user[MAXNAMELEN + 1];
X
X strcpy(wbuff, addr); /* Work on temporary buffer */
X if ((s1 = strchr(wbuff, '@')) != NULL)
X return; /* assume standard format */
X
X if ((s1 = strrchr(wbuff, '!')) != NULL) {
X strncpy(user, &s1[1], MAXNAMELEN);
X user[MAXNAMELEN] = '\0';
X if ((s2 = strchr(wbuff, '!')) != NULL) {
X if (s1 == s2) { /* We have host!user */
X *s2 = '\0';
X strncpy(host, wbuff, HOSTNAMELEN);
X host[HOSTNAMELEN] = '\0';
X }
X /* Here we have a blah!fasel!host!user */
X s2 = s1; /* ^ */
X for (*s2 = '\0'; (s2 >= wbuff) && (*s2 != '!'); s2--);
X if (*s2 == '!') {
X strncpy(host, &s2[1], HOSTNAMELEN);
X host[HOSTNAMELEN] = '\0';
X if ((s1 = strchr(host, '.')) != NULL)
X /* blah!fasel!host.domain!user */
X /* ^ */
X *s1 = '\0';
X }
X sprintf(addr, "%s@%s", user, host);
X return;
X }
X }
}
X
/*
** Zerlegen der from= Zeile aus dem Log.
**
** Gefuellt wird das Absender/Laengenfeld
**
** Interpret the from= line.
** We extract the sending user and the file size
*/
X
void
parsefrom(LZPTR lz, NLZPTR nlz)
{
X char delim = ','; /* Delimiter fuer den Namen */
X int anfang = 5;
X char *src,
X *dest;
X
X memcpy(nlz, lz, KENNZSIZE); /* copy the ID-part */
X if (lz->inh.rest[anfang] == '<') {
X delim = '>';
X anfang++;
X }
X src = &(lz->inh.rest[anfang]);
X dest = &(nlz->from[0]);
X while (*src != delim) { /* copy senders name */
X *dest = *src;
X dest++;
X src++;
X }
X *dest = '\0';
X clean_address(&(nlz->from[0]));
/*
** Jetzt noch die Groesse der Nachricht setzen
** src zeigt mindestens auf das Ende des Absenders
** Get size of message
*/
X while (*src) {
X if (!strncmp(src, "size=", 5)) {
X /* Groesse gefunden! */
X src += 5;
X nlz->len = atol(src);
X return;
X }
X src++;
X }
X
}
X
/*
** Zerlegen einer to= Zeile.
**
** Es interessiert hiervon nur der echte to= Eintrag,
** delay= und stat= werden vernachlaessigt.
**
** Es koennen zu einer Nachricht mehrere to= Zeilen auftreten,
** eine to= Zeile kann aber auch mehrere Empfaenger haben.
** Sollte der to-Eintrag in nlz bereits belegt sein, so
** muss dieser erst weggeschrieben werden, bevor der neue
** Eintrag eingesetzt wird.
**
** Interpret the to= line.
**
** All we need is the to=receiver part.
*/
X
void
parseto(LZPTR lz, NLZPTR nlz)
{
X char delim = ','; /* Delimiter fuer den Namen */
X int anfang = 3;
X char *src,
X *dest;
X
X if (lz->inh.rest[anfang] == '<') {
X delim = '>';
X anfang++;
X }
X src = &(lz->inh.rest[anfang]);
X dest = &(nlz->to[0]);
X if (*dest != '\0') { /* There is one already */
X writelog(nlz); /* so save that line */
X memset(nlz->to, 0, MAXTOLEN + 1);
X }
X for (; *src;) {
X while (*src != delim) { /* Copy receiver */
X *dest = *src;
X dest++;
X src++;
X }
X *dest = '\0';
X clean_address(&(nlz->to[0]));
X
#if DEBUG
X printf("Empfaenger:%s<\n", nlz->to);
#endif
X
X /* Ok, but there might be another receiver */
X while (*src) {
X if (*src == ',' || *src == ' ' || *src == delim) {
X src++;
X continue;
X }
X
#if DEBUG
X printf("src:%s<\n", src);
X getchar();
#endif
X
X if (!strncmp(src, "delay=", 6)) /* We're done */
X return;
X writelog(nlz); /* This line to log */
X memset(nlz->to, 0, MAXTOLEN + 1);
X dest = &(nlz->to[0]);
X if (*src == '<') {
X delim = '>';
X src++;
X } else
X delim = ',';
X break;
X }
X }
X
}
X
/*
** Parsen einer Logzeile
**
** Jede Zeile aus dem Log geht hier hinein. Sie wird dann
** aufgrund des Kennzeichens zugeordnet. Pro versandte Nachricht
** entsteht so wieder eine Logzeile, die einen definierten Aufbau
** hat.
** Falls eine Nachricht an mehrere Empfaenger geht, entstehen auch
** mehrere Zeilen im neuen Log. Um die Zuordnung zum Zahler zu
** ermoeglichen, wird aus dem to= oder from= der Zahlungspflichtige
** in den Eintrag "zahler" eingesetzt.
** -----------------------
** See 'algorithm' at BOF.
*/
X
void
parseline(LZPTR lz, NLZPTR nlz)
{
X static char okz[16];
X char nkz[16];
X int nlztype;
X
X memcpy(nkz, lz, 15);
X nkz[15] = '\0';
X if (strcmp(nkz, okz)) { /* New message-id or date */
X if (okz[0] != '\0') { /* but not the first one! */
X writelog(nlz);
X memset(nlz, 0, sizeof (NLZ));
X }
X memcpy(okz, nkz, sizeof(okz));
X
#if DEBUG
X printf("%s\n", nkz);
#endif
X }
X nlztype = gettype(lz);
X
#if DEBUG
X printf("Type: %d\n", nlztype);
#endif
X
X switch (nlztype) {
X case FROM: /* Next sender */
X parsefrom(lz, nlz);
X break;
X case TO:
X parseto(lz, nlz);
X break;
X }
}
X
void main(int argc, char *argv[])
{
X LZ ibuf;
X NLZ nlz;
X char *s,
X oname[FNAMELEN];
X
X if (argc != 3) {
X fprintf(stderr, "%s %s\n", argv[0], VERSION);
X fprintf(stderr, "usage: %s infile hostname\n", argv[0]);
X fprintf(stderr, "convert infile to usable input for any\n");
X fprintf(stderr, "accounting purpose.\n\n");
X fprintf(stderr, "infile must be prepared by 'justlog'.\n");
X exit(1);
X }
X if ((ifile = fopen(argv[1], "r")) == NULL) {
X printf("\"%s\" not found.\n", argv[1]);
X exit(1);
X }
X strcpy(oname, argv[1]);
X s = strrchr(oname, '.');
X if (s)
X *s = '\0';
X strcat(oname, ".out");
X
X if ((ofile = fopen(oname, "w")) == NULL) {
X printf("Can't open output \"%s\".\n", oname);
X fclose(ifile);
X exit(1);
X }
X strcpy(defaulthost, argv[2]);
X
X while (fgets(ibuf.alles, MAXLINELEN, ifile)) {
X clearline(&ibuf);
X parseline(&ibuf, &nlz);
X }
X if (nlz.to[0] != '\0') /* The last one is to be written */
X writelog(&nlz);
X fclose(ifile);
X fclose(ofile);
}
SHAR_EOF
chmod 0600 Mailutil/auswlog.c ||
echo 'restore of Mailutil/auswlog.c failed'
Wc_c="`wc -c < 'Mailutil/auswlog.c'`"
test 13260 -eq "$Wc_c" ||
echo 'Mailutil/auswlog.c: original size 13260, current size' "$Wc_c"
fi
# ============= Mailutil/justlog.c ==============
if test -f 'Mailutil/justlog.c' -a X"$1" != X"-c"; then
echo 'x - skipping Mailutil/justlog.c (File already exists)'
else
echo 'x - extracting Mailutil/justlog.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'Mailutil/justlog.c' &&
/*
** Filter zur Umformatierung von syslog's
** zur Auswertung und Berechnung der Mailkosten
**
** The algorithm:
**
** Get a line from syslog
** Convert the date from 'verbose' to numeric format.
** Drop stuff like 'new aliases ...'
** Get the AA.. part of the line.
** Get the useful information (from=, to=, size=, stat=)
** Drop lines, if the to= line does not contain stat=Sent.
** Drop lines with message-id=
** Move the AA.. part to the beginning of the line.
** (We will need this later for sorting the stuff!)
** Output the converted line.
** until eof().
*/
X
#include <stdio.h>
#include <string.h>
X
#include "mailutil.h"
X
/* Max. Length exspected in the syslog */
#define LINELEN 256
X
#define VERSION "1.0"
X
static char *monate[] =
{
X "Jan", "Feb", "Mar", "Apr", "May", "Jun",
X "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
X NULL
};
X
/*
** Teil eins der Konvertierung.
**
** 1. Monat aus dem Klartext in lfd. Nummer uebersetzen
** 2. Zeilen wie newaliases ausfiltern
** 3. Die PID von sendmail ausfiltern
** 4. Im Datenteil (hinter AA...) alle Blanks loeschen
** 5. Blanks im Datum durch ':' ersetzen
**
** Uebergabeparameter:
** char *in Zeiger auf Eingabezeile
** char *out Zeiger auf Ausgabezeile
**
** Rueckgabe:
** 0 Konvertierung ok.
** 1 Zeile ist zu verwerfen.
**
** -------------------
** Part one of conversion
**
** Translate verbose date to numbers
** filter lines like 'blah new aliases' etc.
** strip blanks from the relevant part after AA...
** replace all blanks in date by ':'
*/
X
int
convert_buff(char *in, char *out)
{
X int i;
X char *s,
X *so;
X
X for (i = 0; monate[i]; i++) {
X if (!strncmp(in, monate[i], 3)) {
X sprintf(out, "%02d", i + 1);
X break;
X }
X }
X if (monate[i] == NULL) /* should never happen */
X strcpy(out, "13");
X s = in + 3; /* start behind month */
X so = out + 2;
X for (i = 0; i < 12; i++) { /* and copy date */
X *so = *s;
X s++;
X so++;
X }
X /* next thing to get is the AA... part */
X s = in + 15;
X while (*s) {
X if ((*s == ':') && (!strncmp(s, ": AA", 4)))
X break;
X s++;
X }
X if (!*s) /* No AA... found */
X return 1;
X *so++ = ':';
X s += 2;
X while (*s) { /* copy rest of line */
X *so = *s;
X so++;
X s++;
X }
X *so = '\0';
X *(out + 14) = ':'; /* insert ':' after time */
X if (*(out + 3) == ' ') /* make day numeric */
X *(out + 3) = '0';
X for (s = out, i = 0; *s && (i < 15); i++, s++)
X if (*s == ' ')
X *s = ':';
X return 0;
}
X
/*
** "Verdrehen" einer Zeile wg, Sortierung
**
** 1. Die AA... - Angabe von sendmail wird nach vorne gerueckt.
** 2. Zeilen mit "message-id" oder "locked" werden verworfen.
** 3. to= Zeilen, in denen nicht stat=Sent steht, werden auch
** verworfen.
**
** Uebergabeparameter:
** char buf[] Adresse der Zeile
**
** Rueckgabe:
** Adresse des statischen Buffers mit gedrehter Zeile,
** im "verwerflichen" Fall NULL
** -------------------
** Make lines sortable
**
** Move AA... as key to beginning of line
** drop lines containing 'message-id=' or 'locked'
** drop lines containing 'to=' AND 'stat!=Sent'
** i.e. Deferred, etc.
*/
X
char *
drehbuf(char buf[])
{
X static char workbuf[LINELEN + 1],
X *s;
X
X sprintf(workbuf, "%8.8s%15.15s%s", &buf[15], buf, &buf[24]);
X s = &workbuf[23];
X if ((!strncmp(s, "locked", 6)) || (!strncmp(s, "message-id=", 11)))
X return NULL;
X if (!strncmp(s, "to=", 3)) {
X /* This line should contain stat=Sent */
X while (*s) {
X if (!strncmp("stat=", s, 5)) {
X s += 5;
X if (strncmp("Sent", s, 4))
X return NULL;
X }
X s++;
X }
X }
X return workbuf;
X
}
X
void
main(int argc, char *argv[])
{
X char inbuf[LINELEN + 1],
X outbuf[LINELEN + 1],
X *s;
X
X if (isatty(fileno(stdin)) || isatty(fileno(stdout))) {
X fprintf(stderr, "%s %s\n", argv[0], VERSION);
X fprintf(stderr, "A filter for use on syslog for gathering\n");
X fprintf(stderr, "information about mail costs. Use as filter!\n");
X exit(1);
X }
X while (gets(inbuf)) {
X if (strlen(inbuf) < 30) /* useless information */
X continue;
X if (!convert_buff(inbuf, outbuf)) {
X s = drehbuf(outbuf);
X if (s)
X puts(s);
X }
X }
X
}
SHAR_EOF
chmod 0644 Mailutil/justlog.c ||
echo 'restore of Mailutil/justlog.c failed'
Wc_c="`wc -c < 'Mailutil/justlog.c'`"
test 4176 -eq "$Wc_c" ||
echo 'Mailutil/justlog.c: original size 4176, current size' "$Wc_c"
fi
# ============= Mailutil/mailstat1.awk ==============
if test -f 'Mailutil/mailstat1.awk' -a X"$1" != X"-c"; then
echo 'x - skipping Mailutil/mailstat1.awk (File already exists)'
else
echo 'x - extracting Mailutil/mailstat1.awk (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'Mailutil/mailstat1.awk' &&
#########################################################################
#
# Rechnungserstellung fuer Electronic Mail
#
# Version 1.0
#
# (c) 1990 by RMI Nachrichtentechnik GmbH
#
#########################################################################
X
X
BEGIN {
X FS="~";
}
X
#########################################################################
X
/MAILER/ {
X next
}
/\/dev\/null/ {
X next
}
/news/ {
X next
}
X
{
X if (substr($0,1,1) == "~")
X next;
X mails[$1]++;
X if ($9 == $10) {
X localmails[$1]++;
X localbytes[$1] += $6;
X }
X else {
X remotemails[$1]++;
X remotebytes[$1]+= $6;
X }
X bytes[$1] += $6;
}
X
#########################################################################
X
END {
X max = 0;
X tmail = 0;
X tbytes = 0;
X for (usr in mails) {
X tmail += mails[usr];
X tbytes += bytes[usr];
X if (bytes[usr] > max)
X max = bytes[usr];
X }
X
X for (usr in mails) {
X end = bytes[usr] / max * 60;
X if (end > 2) { # don't graph the underdogs
X printf("%-14s", usr);
X for (i = 0; i < end; i++)
X printf("#");
X print("");
X }
X }
X print("\n---\n");
X print("User Mails Bytes");
X print("------------------------------");
X for (usr in mails) {
X printf("%-14s %6d %8d\n", usr, mails[usr], bytes[usr]);
X }
X print("------------------------------");
X printf("%-14s %6d %8d\n", "Total", tmail, tbytes);
X print("==============================");
X
}
SHAR_EOF
chmod 0644 Mailutil/mailstat1.awk ||
echo 'restore of Mailutil/mailstat1.awk failed'
Wc_c="`wc -c < 'Mailutil/mailstat1.awk'`"
test 1359 -eq "$Wc_c" ||
echo 'Mailutil/mailstat1.awk: original size 1359, current size' "$Wc_c"
fi
# ============= Mailutil/mailstat2.awk ==============
if test -f 'Mailutil/mailstat2.awk' -a X"$1" != X"-c"; then
echo 'x - skipping Mailutil/mailstat2.awk (File already exists)'
else
echo 'x - extracting Mailutil/mailstat2.awk (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'Mailutil/mailstat2.awk' &&
#########################################################################
#
# Rechnungserstellung fuer Electronic Mail
#
# Version 1.0
#
# (c) 1990 by RMI Nachrichtentechnik GmbH
#
#########################################################################
X
#function round(i) {
# i = int((i*100) + 0.5)/100;
# return i;
#}
X
#########################################################################
X
BEGIN {
X FS="~";
X if (host == "")
X host = "infohh";
X lmail = 0.1;
X lbyte = 0.00001;
X rmail = 0.05;
X rbyte = 0.0002;
X mwst = 0.14;
X rechnr = 0;
X hostlmail = 0.0;
X hostlkbyte = 0.0;
X hostrmail = 0.0;
X hostrkbyte = 0.0;
X hostmwst = 0.0;
}
X
#########################################################################
X
/MAILER-DAEMON/ {
X next
}
/\/dev\/null/ {
X next
}
/news/ {
X next
}
X
{
X if (substr($0,1,1) == "~")
X next;
X
X mails[$1]++;
X if ($9 == $10) {
X localmails[$1]++;
X localbytes[$1] += $6;
X }
X else {
X remotemails[$1]++;
X remotebytes[$1]+= $6;
X }
X bytes[$1] += $6;
}
X
#########################################################################
X
END {
X i = 99;
X for (usr in mails) {
X if (i > 20) {
X print(" l o c a l r e m o t e");
X print("Username Mails Bytes Mails Bytes local remote total");
X print("-------------------------------------------------------------------------------");
X i = 0;
X }
X lcost = localmails[usr] * lmail + localbytes[usr] * lbyte;
X rcost = remotemails[usr] * rmail + remotebytes[usr] * rbyte;
X printf("%-20s %6d %8d %6d %8d %8.2f %8.2f %8.2f\n", usr, \
X localmails[usr], localbytes[usr], \
X remotemails[usr], remotebytes[usr], \
X lcost, rcost, lcost + rcost);
X i++;
X }
X print("-------------------------------------------------------------------------------");
X
}
SHAR_EOF
chmod 0644 Mailutil/mailstat2.awk ||
echo 'restore of Mailutil/mailstat2.awk failed'
Wc_c="`wc -c < 'Mailutil/mailstat2.awk'`"
test 1769 -eq "$Wc_c" ||
echo 'Mailutil/mailstat2.awk: original size 1769, current size' "$Wc_c"
fi
# ============= Mailutil/mailutil.h ==============
if test -f 'Mailutil/mailutil.h' -a X"$1" != X"-c"; then
echo 'x - skipping Mailutil/mailutil.h (File already exists)'
else
echo 'x - extracting Mailutil/mailutil.h (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'Mailutil/mailutil.h' &&
/*
** Prototypes for external functions
*/
X
void exit(int result);
long atol(char *src);
int isatty(int fh);
X
SHAR_EOF
chmod 0644 Mailutil/mailutil.h ||
echo 'restore of Mailutil/mailutil.h failed'
Wc_c="`wc -c < 'Mailutil/mailutil.h'`"
test 110 -eq "$Wc_c" ||
echo 'Mailutil/mailutil.h: original size 110, current size' "$Wc_c"
fi
exit 0
--
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
| Auch: wsiebeck@rmi.de | Hier koennte |
| siebeck@infoac.rmi.de | Ihre Anzeige |
| root@atreju.rmi.de | stehen |
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=