/*
 * Mail a tous les adherents de CRANS.
 *
 * Copyright (c) 1999 Association CRANS.
 *
 * Auteur: Olivier Saut <Olivier.Saut@CRANS.ENS-Cachan.Fr>
 *
 */

#include <sys/types.h>
#include <sys/param.h>
#include <sys/errno.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/wait.h>

#include <err.h>
#include <fcntl.h>
#include <paths.h>
#include <pwd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/uio.h>

#define kPCGuestUID 		502
#define kCorbeauUID		536
#define kNobodyUID		65534 
#define kRealUsers		500

/* Prototypes */
int mk_tmpfile();
void edit();
int sendmail(char *name);
void help();
int isValidUID(uid_t uid);
void mySig_handler(int iInt);

/* Variables globales */
char *gTempname;
uid_t gCurrent_UID;
uid_t gUid_start;
uid_t gUid_stop;
int   gVerbose;
int   gSimulate;

/* Programme principal */
int main(int argc, char *argv[])
{

	struct passwd *passwd_ent;
	struct passwd *user;
	char buf[1024];
	extern char *optarg;
    	extern int optind;
     	int  ch ;
	struct stat begin, end;
	sig_t previous_handler;
	char tildefn[MAXPATHLEN];
	int fd=-1;
	

	gUid_start=kRealUsers;	
	gUid_stop=kNobodyUID;
	gVerbose=0;
	gSimulate=0;
	while ((ch = getopt(argc, argv, "svhf:d:a:")) != -1)
             switch (ch) {
             case 'h':
                     help();
                     break;
	     case 'v':
		     gVerbose=1;
		     break;
	     case 's':
		     gVerbose=1;
		     gSimulate=1;
		     break;
             case 'f':
		     gTempname=optarg;
                     if ((fd = open(optarg, O_RDONLY, 0)) < 0) {
                             (void)fprintf(stderr,
                                 "myname: %s: %s\n", optarg, strerror(errno));
                             exit(1);
                     }
                     break;
	     case 'd':
		     gUid_start=atoi(optarg);
		     break;
             case 'a':
		     gUid_stop=atoi(optarg);
		     break;
		     
             }
     argc -= optind;
     argv += optind;

     previous_handler = signal(SIGTERM & SIGINT,(sig_t) mySig_handler);
     if(previous_handler==SIG_ERR) {
	     (void)fprintf(stderr,"Erreur lors de l'installation du sighandler.");
	     exit(EXIT_FAILURE);
     }
  

    if(gVerbose)
	    fprintf(stdout,"mailCRANS by OS. Version 0.1\n");

    if(fd==-1) {
	    fd=mk_tmpfile();
	    user=getpwuid(getuid());
	    sprintf(buf,"From: %s\nSubject: \nReply-To: \nContent-Type: text/plain; charset=iso-8859-1", user->pw_name);

	    if (write (fd,(void *)buf,strlen(buf)) < 0) {
		    close(fd);
		    fprintf(stderr, "Erreur en écriture.");
		    exit(EXIT_FAILURE);
	    }

	    (void) fsync(fd);
	    if(fstat(fd,&begin)) {
		    perror("Erreur stat(1)");
		    close(fd);
		    exit(EXIT_FAILURE);
	    }


	    (void) edit();

	    if(stat(gTempname,&end)) {
		    perror("Erreur stat(2)");
		    close(fd);
		    exit(EXIT_FAILURE);
	    }

	    if (begin.st_mtime == end.st_mtime) {
		    warnx("No changes made : exiting");
		    close(fd);
		    (void)unlink(gTempname);
		    exit(EXIT_FAILURE);
	    } 
    }



    setpwent();

    while((int)(passwd_ent=(struct passwd *)getpwent())) {
	    gCurrent_UID=passwd_ent->pw_uid;

	    if(isValidUID(gCurrent_UID))
		    if(sendmail(passwd_ent->pw_name )) {
			    exit(EXIT_FAILURE);
		    }
		    else
			    usleep(250000);
    }

    endpwent();	

    (void)unlink(gTempname);
    /* Bon on efface le fichier parasite */
    (void)strcpy((char *)tildefn,gTempname);
    (void)strcat((char *)tildefn,"~");

    if(!access(tildefn,F_OK))
	    (void)unlink(tildefn);

    return 0; 
}

int isValidUID(uid_t uid) {
	if(uid<gUid_start)
		return 0;

	if(uid>gUid_stop)
		return 0;
	
	if((uid==kPCGuestUID)|| (uid==kCorbeauUID) || (uid==kNobodyUID))
		return 0;

	return 1;	
}

void mySig_handler(int iInt) {
	(void) fprintf(stdout,"\n Signal %d intercepté.\n",iInt);
	(void) fprintf(stderr,"Le programme s'est arreté sur l'uid : %d\n",gCurrent_UID);
		exit(EXIT_SUCCESS);
}

int mk_tmpfile()
{
	int fd;
	char p[MAXPATHLEN];
	p[MAXPATHLEN-1]='\0';

	strcpy(p, "/tmp/EAll.XXXXXX");
	if ((fd = mkstemp(p)) == -1) 
		err(1, "%s", p);
	gTempname = malloc(strlen(p));
	(void)strncpy(gTempname,p,strlen(p));
	if(gVerbose)
		fprintf(stdout,"Tempfile name : %s\n",gTempname);
	return (fd);
}

void edit()
{
	int pstat;
	int editpid;
	char *p, *editor;

	if (!(editor = getenv("EDITOR")))
		editor = _PATH_VI;
	if ((p = strrchr(editor, '/')))
		++p;
	else
		p = editor;

	if (!(editpid = fork())) {
		errno = 0;
		execlp(editor, p, gTempname, NULL);
		_exit(errno);
	}
	for (;;) {
		editpid = waitpid(editpid, (int *)&pstat, WUNTRACED);
		errno = WEXITSTATUS(pstat);
		(void) fprintf(stderr,"\n *** %d ***\n",WEXITSTATUS(pstat));
		if (editpid == -1)
			exit(EXIT_FAILURE);
		else if (WIFSTOPPED(pstat))
			raise(WSTOPSIG(pstat));
		else if (WIFEXITED(pstat) && errno == 0)
			break;
		else
		exit(EXIT_FAILURE);
	}
	editpid = -1;
}



int sendmail(char *pw_name) {
	int res=0;
	char *buff;
	int length;
	buff=malloc(1024);	

	buff[1023]='\0';
	buff[0]='\0'; 
	
	(void)strcat((char *)buff,(const char *)"cat ");
	(void)strcat(buff,(const char *) gTempname);
	(void)strcat(buff, " | sendmail ");
	(void)strcat(buff, pw_name);
	if(gVerbose)
		fprintf(stdout,"\n ** %s ** \n", buff);
	if(!gSimulate)
		res=system(buff);
	return res;
}

void help() {
	fprintf(stdout,"usage: mailCRANS [-v] [-s] [-f filename] [-{d,a} uid]\n");
	fprintf(stdout,"		-v 		: mode bavard\n");
	fprintf(stdout,"		-f filename 	: envoie le fichier en argument\n");
	fprintf(stdout," (rajouter une ligne Subject: en première ligne du fichier pour avoir un sujet\n");
	fprintf(stdout,"		-d uid		: envoie à partir de l'UID argument\n");
	fprintf(stdout,"		-a uid		: arrete après l'UID argument\n");
	fprintf(stdout,"		-s		: mode simulation.");
	exit(EXIT_SUCCESS);

}


