[net.sources] Next MEP shar

jantypas@hope.UUCP (John Antypas) (10/11/86)

Less buggy -- it works locally, but I can't be sure how many are
getting their requests out there.  It now has security.  Any command
requested NOT in a .commands file in your $HOME will be returned
unexecuted.


# To unbundle this package, use sh <name> NOT csh
echo commands.c	1>&2
cat >commands.c << 'End of commands.c'
# include <stdio.h>
# include "typedefs.h"
# include "globals.h"
# include "string2.h"

/*
 *  Check command to see if it's OK
 */
ok_command(line)
char line[];
{
	int	i;

	for (i=0; i<=num_cmds; i++)
	{
		if ((strindex(line,comlist[i]) != -1) && 
			(strcmp(line,"") != 0))
		{
			return(0);
		}
	}
	return(-1);
}

/*
 * Commands.c -- Does @ checking
 *
 * Called by check_commands()
 *
 */
do_command(line)
char	line[];
{
	char *c;
	FILE *fp, *fopen();
	char	name[80];
	char	buffer[80], buffer2[80];
	int	proc_id, result;
	int	status;
	char comline[80];

	line[strlen(line)-1] = '\0';
	if (strncmp(line,"@RR",3) == 0)
	{
		sprintf(namet,"/usr/ucb/Mail -s \"Return Receipt\" %s",fromline);
		fp = popen(namet,"w");
		fprintf(fp,"\nHere is the return receipt you requested.\n\n");
		pclose(fp);
	}
	if ((strncmp(line,"@SH",3) == 0) && 
	    (strcmp(subline,"Command") == 0))
	{
		c = line; c += 4;
		strcpy(buffer,c);
		if (ok_command(buffer) == 0)
		{
			sprintf(buffer2,"%s | /usr/ucb/Mail -s",buffer);
			sprintf(buffer2,"%s \"%s\" %s",buffer2,line,fromline);
			proc_id = fork();
			if (proc_id == 0)
			{ execlp("/bin/sh","sh","-c",buffer2,(char *)0); 	}
			wait(&status);
		} else
		{
			sprintf(namet,"/usr/ucb/Mail -s \"MEP\" %s",fromline);
			fp = popen(namet,"w");
			fprintf(fp,"\nYou do not have access to the command:\n");
			fprintf(fp,"\n%s\n",buffer);
			pclose(fp);
		}
	}
}

check_commands()
{
	FILE *md, *fopen();
	char	buffer[512];
	
	md = fopen(namem,"r");
	fgets(buffer,512,md);
	while (!feof(md))
	{
		if (buffer[0] == '@') do_command(buffer);
		fgets(buffer,512,md);
	}
	fclose(md);
}
End of commands.c
echo mep.c	1>&2
cat >mep.c << 'End of mep.c'
# include <stdio.h>
# include <sys/types.h>
# include <sys/timeb.h>
# include <time.h>

main(argc,argv)
int	argc;
char	*argv[];
{
	FILE *ip, *fp, *fopen();	 
	char buffer[80], buffer2[80];
	
	ip = fopen("/class/maj/jantypas/shar","r");
	sprintf(buffer2,"/usr/ucb/Mail -s \"MEP-Shar\" %s",argv[1]);
     fp = popen(buffer2,"w");
	fgets(buffer,512,ip);
	while (!(feof(ip)))
	{
		fputs(buffer,fp);
		fgets(buffer,512,ip);
	}
	fclose(ip);
	pclose(fp);
	printf("%s\n",buffer2);
	printf("A copy of MEP is on it's way\n");
}
End of mep.c
echo mepinit.c	1>&2
cat >mepinit.c << 'End of mepinit.c'
# include <stdio.h>
# include <pwd.h>
# include <string.h>
# include	"typedefs.h"
# include "globals.h"
/*
 * MepInit -- Will load configuration options at runtime
 *
 */

kill_newline(buffer)
char	*buffer;
{
	int i;

	for (i = 0; i< strlen(buffer); i++)
	{
		if (buffer[i] == '\n') 
		{
			buffer[i] = '\0';
			break;
		}
	}
}

read_config()
{
	int	result;
	FILE	*fp, *fopen();
	char	buffer[512];
	char	buffer2[80];

	result = chdir(homedir);
	fp = fopen(".configmep","r");
	fgets(buffer,512,fp);
	kill_newline(buffer);
	while (!feof(fp))
	{
		if (strncmp(buffer,"notify=",6) == 0)
		{
			strcpy(buffer2,&buffer[7]);
			if (strcmp(buffer2,"yes") == 0)
			{ notify = TRUE; } else { notify = FALSE; }
		}
		if (strncmp(buffer,"stat=",5) == 0)
		{
			strcpy(buffer2,&buffer[5]);
			if (strcmp(buffer2,"none") == 0)
			{
				log = FALSE;
			}
			else
			{
				log = TRUE;
				strcpy(logfile,&buffer[5]);
			}
		}
		fgets(buffer,512,fp);
		kill_newline(buffer);
	}
	fclose(fp);	
}

mep_init(id)
int id;
{
	int	result;
	char	*p;
	char	buffer[80];
	char	envvalue[80];	/* Variable value	*/
	FILE	*fp, *fopen();
	struct passwd entry;
	
	/* Go to home and try to get the variables */
	memcpy(&entry, getpwuid(id), sizeof(entry));
	p = entry.pw_dir;
	if (p == NULL)
		error("Can't find home directory!",-1);
	strcpy(envvalue,p);
	result = chdir(envvalue);	/* Change to dir	*/
	if (result != 0)
	{
		fprintf(stderr,"Tried to switch to %s\n",envvalue);
		fprintf(stderr,"Got %d\n",result);
		error("Can't switch to home directory!",-1);
	}
	strcpy(homedir,envvalue);
	strcpy(username,entry.pw_name);

	read_config();
	result = chdir(homedir);
	/*
		 Only 100 twits allowed please.  If you have more 
		than 100 people you don't want mail from, you need
		an agent, not a mail extension
	*/
	num_twits = 0;	/* We assume no twits for now	*/
	fp = fopen(".twitlist","r");
	if (fp == NULL) { /* No twits lucky you	*/ }
	else	/* Oh well, you can't expect perfection	*/
	{
		strcpy(twitlist[0],"TWITS");
		while (!feof(fp))
		{
			num_twits++;
			fgets(buffer,80,fp);
			buffer[strlen(buffer)-1] = 0;
			strcpy(twitlist[num_twits],buffer);	
		}
		fclose(fp);
	}
	num_cmds = 0;
	fp = fopen(".commands","r");
	if (fp == NULL) { /* No stuff OK */ }
	else
	{
		strcpy(twitlist[0],"");
		while (!feof(fp))
		{
			num_cmds++;
			fgets(buffer,80,fp);
			buffer[strlen(buffer)-1] = 0;
			strcpy(comlist[num_cmds],buffer);
		}
		fclose(fp);
	}
}
End of mepinit.c
echo message.c	1>&2
cat >message.c << 'End of message.c'
/*
 * Message - Gathers incoming mail and sends it to be processed
 * John Antypas
 * ...!ucbvax!ucdavis!ucrmath!hope!jantypas
 *
 * For each message in the input stream, separate off the message and
 * record the following:
 * 
 * From line:
 * user
 * Subject
 *
 * And then process any message for twits and @ commands.
 *
 */
# include <stdio.h>		/* System file and string routines	*/
# include <sys/types.h>
# include <string.h>
# include <fcntl.h>
# include <errno.h>
# include "typedefs.h"
# include "globals.var"
# include "process.h"

extern	mep_init();

/* Error message,  Print message in s and return value v to shell */
error(s,v)
char	s[];
int	v;
{
	fprintf(stderr,"ERROR: \007%s\n",s);
	fprintf(stderr,"MEP returning to shell!\n");
	exit(v);
}

main(argc,argv)
int	argc;
char	*argv[];
{
	char	*c, buffer[80];
	FILE	*inf;	/* Spool file	*/
	int	gfd;		/* Generic file descripter	*/
	int loop;
	int id;
	
	id = getuid();
	mep_init(id);
	firstmessage	= TRUE;	/* No messages read yet		*/
	while (!feof(stdin)) /* As long as we have input	*/
	{
		fgets(linebuffer,512,stdin);	/* Get line	*/
		/* Now see if this is a From line		*/
		/* If it is a From line, word = 'From '		*/
		if ((strncmp(linebuffer, "From ", 5) == 0) &&
		    (strlen(linebuffer) > 5))
		{
			/* Now see if this is our first From	*/
			if (!firstmessage)
			{
				/* This is after a previous 	*/
				/* Message, process the old one	*/
				fclose(md);
				process_message();
				strcpy(namem,mktemp("/tmp/XXXXXX"));
				md = fopen(namem,"w+");
			}
			/* New message, so open needed files,	*/
			/* and start gathering fields etc.	*/
			c = linebuffer;
			c += 5;
			strcpy(fromline,c);
			fromline[strlen(fromline)-1] = 0;
			for (loop = 0; (fromline[loop] != ' ') 
				&& (fromline[loop] != '\0'); loop++) ;
			fromline[loop] = '\0';
			strcpy(namem,mktemp("/tmp/XXXXXX"));
			md = fopen(namem,"w+");
			firstmessage = FALSE;
		}
		/* Not a from line so check To:/Subject:	*/
		if (strncmp(linebuffer, "To:", 3) == 0)
		{
			c = linebuffer; c += 3;
			strcpy(toline,c);
			toline[strlen(toline)-1] = 0;
		}
		/* Not a To: line so check subject		*/
		if (strncmp(linebuffer, "Subject:", 8) == 0)
		{
			c = linebuffer;
			if (strlen(linebuffer) > 9) 
			{ c += 9; } else {c += 8; }
			strcpy(subline,c);
			subline[strlen(subline)-1] = 0;
		}
		/* Not any of these so just copy to message	*/
		if (!feof(stdin))
		{
			fputs(linebuffer,md);
		}
	}
	if (!firstmessage) 
	{
		fclose(md);
		process_message();
	}
}
End of message.c
echo process.c	1>&2
cat >process.c << 'End of process.c'
# include	<stdio.h>
# include	"typedefs.h"
# include	"globals.h"
# include	"string2.h"

extern	int	check_commands();

/* 
 * Twit(name)
 *
 * Returns TRUE if name is a twit, false otherwise
 *
 */
twit(name)
char	name[];
{
	int	loop = 1;

	while (loop <= num_twits)
	{
		if (strindex(name,twitlist[loop]) != -1)
		{ 
			return(TRUE); 
		}
		loop++;
	}
	return(FALSE);
}

/*
 * Do_Notify.  Notify user if twit sends mail if NOTIFY variable is set
 */
do_notify()
{
	FILE	*od, *md, *fopen();
	char buffer[80];
	
	if (notify == TRUE)
	{
		strcpy(namet,mktemp("/tmp/XXXXXX"));
		md = fopen(namet,"w+");
		fprintf(md,"From MEP\n");
		fprintf(md,"To: %s\n",username);
		fprintf(md,"Subject: Twit Alert\n");
		fprintf(md,"\nFor: %s@%s\n\n",username,homedir);
		fprintf(md,"\nSomeone at address:\n");
		fprintf(md,"\t%s\n",fromline);
		fprintf(md,"has tried to send you mail.  ");
		fprintf(md,"TwitMinder has discarded it.\n\n");
		fclose(md);
		sprintf(buffer,"/usr/ucb/Mail -s Twit-Alert %s",username);
		od = popen(buffer,"w");
		md = fopen(namet,"r");
		fgets(buffer,512,md);
		while (!feof(md))
		{
			fputs(buffer,od);
			fgets(buffer,512,md);
		}
		fclose(md);
		pclose(od);
		unlink(namet);
	}
}

/*
 * Process_message
 *
 * When given a message, performs the following:
 *
 * - Checks to see if person wants logging done
 *   If so, performs the log entry
 * - Checks to see if sender is on twitlist and if
 *   so, will discard mail to bit bucket and if notify
 *   option is set will send mail to that effect
 * - Process @commands
 * 
 */
process_message()
{
	FILE	*fp, *md, *fopen();
	char	buffer[512];

	printf("\n\n");
	if (twit(fromline) == TRUE) 
	/*
		This is a twit, notify is requested and discard message
	*/
	{
		do_notify();
		unlink(namem);
		return(0);
	}
	else
	{
		if (log == TRUE)
		{
			/* 
				Ok, so write down in log file when each message
				arrives and the sender.
			*/
			char	buffer2[80];
			FILE *pd, *popen();

			pd = popen("date","r");
			fgets(buffer2,80,pd);
			pclose(pd);
			md = fopen(logfile,"a");
			sprintf(buffer,"From: %s @ %s",fromline,buffer2);
			fputs(buffer,md);
			fclose(md);
		}
		check_commands();
		fp = fopen(namem,"r");
		fgets(buffer,512,fp);
		while (!feof(fp))
		{
			fputs(buffer,stdout);
			fgets(buffer,512,fp);
		}
		fclose(fp);
		unlink(namem);
	}
}
End of process.c
echo string2.c	1>&2
cat >string2.c << 'End of string2.c'
/*
      Strings2 - Provides even more string functions for C.
      I don't know why these weren't included in strings(3).

      John Antypas -- ...!sdcsvax!jantypas
      
     strword1(r,s,c,i) -- Returns 1st word in s to r.  c is the character
                          which separates words.
     strindex(s,t) -- returns first occurrence of t in s or -1.
     strreplace(s,p,r) -- replace first pattern p with pattern r.
          This can be used to strinsert and delete functions.
          It returns a pointer to the new string s.

     All parameters are character pointers.

*/

#define		NULL	0
#include	<string.h>	/* Need strcat, strcpy	*/


int	strindex(s,t)
char *s, *t;
{
	int loop, n;		/* Loop counter		*/

	n = strlen(t);
	for (loop=0; s[loop] != '\0'; loop++)
	{
        if ((strncmp(s+loop, t, n) == 0) && (n != 0)) { return(loop); }
	}
	return (-1);
}

char	*strreplace(s,p,r)
char	*s, *p, *r;
{
	int	f, n;
	char	*m;		/* Used for temporary	*/

	m = (char *)malloc( strlen(s) + strlen(r) - strlen(p) );
	/* Get enough memory for the new string		*/
	/* If it doesn't work, die here			*/
	if (m == NULL) return(m);

	f = strindex(s,p);
	strncpy(m,s,f);	/* Copy up to p.	*/
	strcat(m,r);		/* Add in r.		*/
	
        /* Now add the part after the replacement	*/
	strcat(m,s+f+strlen(p));
	/* Copy it back to s				*/
	strcpy(s,m);
	free(m);
	return(s);
}

int	strword(r,s,c,i)
char	*s, *r, c;
int	i;
/*
	Function: Returns number of words/specific word in string s.
                  Returns in string r the word requested.  If i<0;
                  returns # words in s, else reurns ith word in r.
		  c is the character used to mark separate words.
		  Usually is a space (32).
*/
{
	int l;

	l = strlen(s);
	if (i<0)		/* Count words		*/
	{
		int nw,j;	/* # words and loop var	*/

		for (j=0,nw=0; j<l; j++)
		{
			if (*(s+j) == c) nw++;
		}
		if (nw != 0) nw++;
		return(nw);
	}
	else
	{
		char *sp,t[2];
		int nw,m;		/* Loops and length(s)	*/

		t[0] = c;  t[1] = 0;
		/* Find begining of ith word		*/
		nw = 0; sp = s;
		while ((nw<i) && (*sp != NULL))
		{
			if ((*sp) == c) nw++;
			sp++;
		}
		/* Now find end of that word		*/
		m = strindex(sp,t);
		if (m>0) { strncpy(r,sp,m); } else { strcpy(r,sp); }
		return(0);
	}
}
End of string2.c
echo globals.h	1>&2
cat >globals.h << 'End of globals.h'
# define	TRUE	1
# define	FALSE	0

extern	int	notify, log;
extern	int	firstmessage;	/* First message read = 1 else 0	*/

extern	char	fromline[2048],/* From line from message	*/
		toline[2048],		/* To: line from message		*/
		subline[2048],		/* Message subject			*/
		linebuffer[8192],	/* Big buffer for messages	*/
		logfile[80];
extern	char	namem[80], names[80], namet[80];
extern	char	username[20], homedir[80];

extern	int	num_cmds;
extern	int	num_twits;
extern	STRING	twitlist[100];	/* Twit list	*/
extern	STRING	comlist[100];
extern	error();			/* Error messages			*/

End of globals.h
echo process.h	1>&2
cat >process.h << 'End of process.h'
/*
 * Process.c header file process.h
 * John Antypas
 * Used in message.c to handle each individual mail message
 * All messages are passed through process_message here.
 * It can call features such as twit_list(name) which will 
 * return TRUE if that person is on the twitlist and
 * command(line) which will handle the @ commands
 */

extern	void	proces_message();	/* Handles message	*/

End of process.h
echo string2.h	1>&2
cat >string2.h << 'End of string2.h'
/*
      String2 -- String2 header file

      Make sure to link with string2.o

      strindex(s,t) -- Return index to pattern t in string s or -1
      strreplace(s,p,r) -- Replace pattern p by pattern r in string s.
                           Return char pointer to s or NULL if error.
      strword(r,s,c,i) -- Returns:
            If (i<0) ; the number of words in string s is returned sa result.
               (i>0) ; the ith word is returned in r from string s.
*/
extern	int	strword();
extern	int	strindex();
extern	char	*strreplace();
#define	strdelete(s,p,q)	strreplace(s,p,q,"");
End of string2.h
echo typedefs.h	1>&2
cat >typedefs.h << 'End of typedefs.h'
/* 
 * System data types
 */

typedef	char	STRING[80];
End of typedefs.h
echo globals.var	1>&2
cat >globals.var << 'End of globals.var'
# define	TRUE	1
# define	FALSE	0

int	notify, log = FALSE;
int	firstmessage;		/* First message read = 1 else 0	*/

char	fromline[2048],	/* From line from message		*/
	toline[2048],		/* To: line from message		*/
	subline[2048],		/* Message subject			*/
	linebuffer[8192],	/* Big buffer for messages		*/
	logfile[80];
char namem[80], names[80], namet[80];
char username[20], homedir[80];

FILE	*md;			/* Message file, item file		*/
FILE	*fopen();

int num_cmds;
int	num_twits;	/* # of twits in twitlist		*/
STRING	comlist[100];
STRING	twitlist[100];
End of globals.var
echo Makefile	1>&2
cat >Makefile << 'End of Makefile'
# Makefile for Mail Extensions Package
# John Antypas
CFILES	= message.c process.c commands.c mepinit.c string2.c
OFILES	= message.o process.o commands.o mepinit.o string2.o
CFLAGS	= -O 

mep:	$(CFILES)
	cc $(CFLAGS) $(CFILES) -o message
End of Makefile
echo README	1>&2
cat >README << 'End of README'
Instructions for Installing MEP:

I. Compile the following programs:

cc -O message.c process.c commands.c mepinit.c string2.c -o message

II  Put the following line in your .forward home in your home
directory:

"|/fullpathnameforyou/youruserid/message >> /usr/spool/mail/youruserid"

III. Place message in /pathname/youruserid/

Example:  Let us say your account is frob and the full path from root
	(easily obtained by saying ~<return>) is /usr/acct/frob.

	Compile the code above and place message in /usr/acct/frob

	cd ~
	ls
	....files
	message

	Fix the .forward file.
	vi .forward
	a
	"|/usr/acct/frob/message >> /usr/spool/mail/frob"
	<esc>
	:wq

IV. Test

This is not the complete version of MEP.  It only support three of the
four functions in plan. twitlist, logging and @RR.

Twitlist:

There are people you want mail from and people you DON'T want mail
from.  TwitMinder will handle that.  All twits will be discarded.
Merely place a file called .twitlist containing the userids (one/line)
in the file.

bob
ralph
MAILER-DAEMON
root

then if any of these people send you mail, that mail will be
discarded.  If you want to still be told that they tried, but don't
want to actually READ their mail, setenv NOTIFY.

setenv NOTIFY<return>

This will have the mailer send you a TwitAlert message.

NOTE:  You are only allowed to have 100 twits.  If you have more,
contact me for a bigger version or so I can modify the code to
discard everyone BUT the people in the twitlist.

Logging:

setenv STAT=<pathname/filename>

When this is set, all mail coming in will have a timestamp put into a
file at /pathname/filename.  (Usually just filename will do as it will
go into your home directory.)  Entries are:

From <path!user> @ Date/Time
From <path!user> @ Date/Time
...

 @RR:

This is a neat little feature which allows you ask someone who i

lwall@sdcrdcf.UUCP (Larry Wall) (10/29/86)

I hate to say it, but this version of mep DOESN'T add security as claimed.  It
is trivial to execute any command you want using @SH.  Details are left as an
exercise for the reader.  The fix is left as an exercise for the writer.

And now, the requisite source, a self-reproducing nroff script:

.ec8
.emZZ
.nryy1000-12
.nrzz100-1
.nf
.deXX
.tr88n(yy
88!88!88$1
.tr88n(zz
.amZZ..
88!88!.XX88$1
.nrnl0-1
...
..
.XX.ec9
.XX.emZZ
.XX.nryy1000-12
.XX.nrzz100-1
.XX.nf
.XX.deXX
.XX.tr99n(yy
.XX99!99!99$1
.XX.tr99n(zz
.XX.amZZ..
.XX99!99!.XX99$1
.XX.nrnl0-1
.XX...
.XX..

I dare you to write a shorter one that contains any commands.

Larry Wall
{allegra,burdvax,cbosgd,hplabs,ihnp4,sdcsvax}!sdcrdcf!lwall