> 
  > Sounds good. I was hoping to stay away from domain based 
  > quotas outside of the file system controls, and just let
  > the file system handle that for us. 
  > 
  > I like the bounce message idea of coming from the virtual
  > domain. That's a good one ;)
  > 
  > When it's ready, send it over and I'll start the process
  > of adding it into the next development version, 4.9.7
  > 
  > Ken
  > 
  > PS: good work man!

There's a problem with quota handling in courier-IMAP.
If you've gone over quota, courier will not allow you
in to cleanup or delete your mail.  It wants to create
a cache file.  This is a problem with uid/filesystem
based quotas, if you use courier-imap.

I've been running with this for about 2 weeks now
so it appears to be OK.  I've labeled where my
changes are with a "/* bk */" right next to the change.

You just need to put the file ".domainquota" in the
domains/domain.com directory.  If it doesn't exist,
the domain quotas are unlimited.

There's more stuff that should be cleaned up, such as
the filter code, and more.  I just didn't have a chance
to do this yet, since I don't use or plan to use filters
implemented this way.

Another suggestion: the information that is domain
related should actually go into a database table.  This
would include things like domain quota, bounce message,
and limits (such as those in .qmailadmin-limits).

I've implemented a patch to courier-imap so I can use
filesystem quotas, but I don't know if Sam is going to
take it.  I essentially made cache file creation a "soft"
error and its ignored.  Bad for performance (if your over
quota), but it doesn't fail, which is what I consider to
be more critical.

Thanks,

Brian

/*****************************************************************************
**
** $Id: vdelivermail.c,v 1.1 1998/06/16 21:00:49 chris Exp $
** Deliver a mail to a virtual POP user - called from the .qmail-default file
** pointed to by users/assign
**
** Chris Johnson, Copyright (C) Jan '98
** Email: [EMAIL PROTECTED]
**
**    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.
**
**    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., 675 Mass Ave, Cambridge, MA 02139, USA.
**
*****************************************************************************/

#include <errno.h>
#include <fcntl.h>
#include <pwd.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <dirent.h>
#include <unistd.h>
#include <ctype.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "config.h"
#include "vdelivermail.h"
#include "safestring.h"
#include "vpopmail.h"
#include "vauth.h"

#include <syslog.h>

#define BOUNCE_ALL "bounce-no-mailbox"
#define DELETE_ALL "delete"

#define TMP_BUF_SIZE 1024
#define MAX_SMALL_BUF 100
#define MAX_BUFF 512

char curdir[256];
char tmp_file[256];
char tmp_buf[TMP_BUF_SIZE];
char msgbuf[32768];

off_t msg_size;
#ifdef HARD_QUOTA
 off_t cur_msg_bytes;
 off_t per_user_limit;
 /* bk */
 off_t cur_domain_bytes;
 off_t per_domain_limit = 0;
 int CurrentDomainQuotaSizeFd = -1;
#endif
/* bk */
int bounce_mail(char* message);
int bounce_quota(char* filename);
int bounce_nouser();
int bounce_writefail(int mailfile, size_t wrote, char* msgbuf, off_t size);
int notify_postmaster();

char *return_path_field = "Return-Path: ";
char *from_field = "From: ";
char *subject_field = "Subject: ";

char TheUser[MAX_BUFF];
char TheDomain[MAX_BUFF];
int CurrentQuotaSizeFd;

#ifdef USE_DELIVER_FILTER
void do_filter_delivery( char *tmp_file );

/* filter vaiables */

char tmp_file_f[256];

#define MAX_LOCATION_LEN	12
#define MAX_SSTRING_LEN		256
#define MAX_TARGET_LEN		256
#define MAX_FBUFF_SIZE		(MAX_LOCATION_LEN+MAX_SSTRING_LEN+MAX_TARGET_LEN+7)
#define MAX_FOLDER_SIZE		512

#define FLOC_T			1
#define FLOC_F			2
#define FLOC_S			3
#define FLOC_C			4
#define FLOC_R			5
#define FLOC_AH			6
#define FLOC_AR			7
#define FLOC_B			8
#define FLOC_UNDEF		999

typedef struct  _flc {
        char	*loc;
        int	tag;
} flc;

static flc filter_locs[] = {
	{"To",			FLOC_T	},
	{"From",		FLOC_F	},
	{"Subject",		FLOC_S	},
	{"Cc",			FLOC_C	},
	{"Reply-To",		FLOC_R	},
	{"AnyHeaders",		FLOC_AH	},
	{"AnyRecipents",	FLOC_AR	},
	{"Body",		FLOC_B	},
	{0,			0	}
};


typedef struct _filter_rule {
	char operator;
	char location[MAX_LOCATION_LEN];
	char loctag;
	char sstring[MAX_SSTRING_LEN];
	char target[MAX_TARGET_LEN];
	char action;
	char hit;
	struct _filter_rule *next;
} filter_rule;


filter_rule *filter_chain = (filter_rule *)NULL;

int run_filter = 0, hit_filter = 0, hit_body = 0;

char header_text[265];
int position_tag;
#endif

/*
 * Delete the temporary file 
 */
void delete_tmp()
{
	char message[1024];

	if (unlink(tmp_file) != 0) {
		switch (errno) {
			case EACCES:
				scopy (message,"EACCES: permission denied",sizeof(message));
				break;
			case EPERM:
				scopy (message,"EPERM: permission denied",sizeof(message));
				break;
			case ENOENT:
				scopy (message,"ENOENT: path doesn't exist",sizeof(message));
				break;
			case ENOMEM:
				scopy (message,"ENOMEM: out of kernel memory",sizeof(message));
				break;
			case EROFS:
				scopy (message,"EROFS: filesystem read-only",sizeof(message));
				break;
			default: sprintf (message,"Other code: %d",errno);
		}
		puts ("Yikes! Could create but can't delete temporary file!!");
		puts (message);
	}
}

#ifdef USE_DELIVER_FILTER
/*
 * Delete the temporary file used by filter_copy
 */
void delete_tmp_f()
{
	char message[1024];

	if (unlink(tmp_file_f) != 0) {
		switch (errno) {
			case EACCES:
				scopy (message,"EACCES: permission denied",sizeof(message));
				break;
			case EPERM:
				scopy (message,"EPERM: permission denied",sizeof(message));
				break;
			case ENOENT:
				scopy (message,"ENOENT: path doesn't exist",sizeof(message));
				break;
			case ENOMEM:
				scopy (message,"ENOMEM: out of kernel memory",sizeof(message));
				break;
			case EROFS:
				scopy (message,"EROFS: filesystem read-only",sizeof(message));
				break;
			default: sprintf (message,"Other code: %d",errno);
		}
		puts ("Yikes! Could create but can't delete temporary file!! (filter)");
		puts (message);
	}
}
#endif

/****************************************************************************
** Temporary fail (exit 111), & display message */
int failtemp(char *err, ...)
{
	va_list args;

	va_start(args,err);
	vprintf(err,args);
	va_end(args);

	if (*tmp_file)
		delete_tmp();

	exit(111);
}

/****************************************************************************
** Permanant fail (exit 100), & display message */
int failperm(char *err, ...)
{
	va_list args;

	puts ("Reason for failure: ");
	va_start(args,err);
	vprintf(err,args);
	va_end(args);

	if (*tmp_file)
		delete_tmp();

	exit(100);
}

/****************************************************************************
** See if the POP user exists!! */
struct passwd* pop_user_exist(char *user, char *host, char *prefix, char *bounce)
{
 static struct passwd *pw_data;
 char localuser[MAX_SMALL_BUF];
 char user2[MAX_SMALL_BUF];
 int i;
 char *ttmp;
 char *tmpstr;
 char Dir[156];
 int uid;
 int gid;

	if ( user== NULL || host == NULL || prefix == NULL ) {
		printf("gag\n");
		exit(0);
	}

	for(ttmp=user;*ttmp!=0;++ttmp)
		if(isupper((int)*ttmp))
			*ttmp = tolower((int)*ttmp);
	for(ttmp=host;*ttmp!=0;++ttmp)
		if(isupper((int)*ttmp))
			*ttmp = tolower((int)*ttmp);
	for(ttmp=prefix;*ttmp!=0;++ttmp)
		if(isupper((int)*ttmp))
			*ttmp = tolower((int)*ttmp);


	if (*prefix) {
		scopy(localuser,prefix,sizeof(localuser));
		scat(localuser,user,sizeof(localuser));
	} else {
		scopy(localuser,user,sizeof(localuser));
	}

	if ( (pw_data = vauth_getpw(user, host))==NULL) { 
		for(i=0;i<(MAX_SMALL_BUF-1)&&user[i]!=0&&user[i]!='-';++i) {
			user2[i] = user[i];
		}
		user2[i] = 0;
		pw_data = vauth_getpw(user2, host);
	}


	/* if they don't have a dir, make one for them */
	if ( pw_data != NULL && 
		 (pw_data->pw_dir == NULL || slen(pw_data->pw_dir) == 0) ) {

		tmpstr = vget_assign(host, Dir, 156, &uid, &gid);
		chdir(Dir);
		tmpstr = make_user_dir(pw_data->pw_name, uid, gid);
		i = slen(tmpstr) + slen(Dir) + 12;
		
		pw_data->pw_dir = calloc(1,i); 
            	if ( slen(tmpstr) > 0 ) {
                	sprintf(pw_data->pw_dir, 
				"%s/%s/%s", Dir, tmpstr, user);
            	} else {
                	sprintf(pw_data->pw_dir, 
				"%s/%s", Dir, user);
            	}
		vauth_setpw( pw_data, host );
	}
	vclose();

	if (!pw_data && !*bounce) {
		/* bk */
		if (bounce_mail("Unknown email address (#5.1.1)\n"))
			failperm ("Unknown local POP user %s (#5.1.1)\n", localuser);
	}
	if (pw_data && pw_data->pw_gid & BOUNCE_MAIL ) {
		/* bk */
		if (bounce_mail("Account locked, mail bounced (#5.1.2)\n"))
			failperm ("Account locked and mail bounced %s (#5.1.2)\n", localuser);
	}
	return pw_data;
}

#ifdef USE_DELIVER_FILTER
/*****************************************************************************
** read filter sets */

void read_filter( void )
{
	FILE *ffp;
	char fbuff[MAX_FBUFF_SIZE+1];
	int ec;
	int linenum = 1;
	filter_rule *filter_chain_act = (filter_rule *)NULL;
	filter_rule *filter_chain_tmp = (filter_rule *)NULL;
	flc *flcp;


	run_filter = 0;

	if ( (ffp = fopen( ".vpopfilter", "r" )) != NULL )
	{
		while ( fgets( fbuff, MAX_FBUFF_SIZE, ffp ) != NULL )
		{
			if ( *fbuff == '#' || *fbuff == '@')
				continue;

			if ( (filter_chain_tmp = (filter_rule *) malloc( sizeof( filter_rule ) )) == NULL )
				failtemp ("FilterRuelRead: Out of memory (#F.0.1)\n"); 

			if ( filter_chain == (filter_rule *)NULL )
				filter_chain = filter_chain_tmp;

			if ( filter_chain_act == (filter_rule *)NULL )
				filter_chain_act = filter_chain_tmp;
			else
			{
				filter_chain_act->next = filter_chain_tmp;
				filter_chain_act = filter_chain_tmp;
			}

			filter_chain_act->next = (filter_rule *)NULL;
			
			ec=sscanf( fbuff, "%c\t%s\t%s\t%s\t%c\n", 
			      &filter_chain_act->operator, filter_chain_act->location, 
			      filter_chain_act->sstring, filter_chain_act->target, 
			      &filter_chain_act->action );
			if ( ec == 5)
			{
				filter_chain_act->hit = 0;
				filter_chain_act->loctag = 0;

				for(flcp = filter_locs; flcp->loc != (char *)0; flcp++)
					if (sstrcmp(flcp->loc,filter_chain_act->location) == 0 )
						filter_chain_act->loctag = flcp->tag;

				if ( filter_chain_act->loctag == 0 )
				{
					printf("FilterRuelRead: syntax error at line %d (%s) (#F.0.3)\n",linenum,filter_chain_act->location); 
					run_filter = 0;
					return;
				}
				if ( strchr( "-&|!", filter_chain_act->operator ) == (char *)NULL )
				{
					printf("FilterRuelRead: syntax error at line %d (invalid operator %c) (#F.0.3)\n",linenum,filter_chain_act->operator); 
					run_filter = 0;
					return;
				}
				if ( strchr( "+-*", filter_chain_act->action ) == (char *)NULL )
				{
					printf("FilterRuelRead: syntax error at line %d (invalid action %c) (#F.0.3)\n",linenum,filter_chain_act->action); 
					run_filter = 0;
					return;
				}
				if ( filter_chain_act->action == '+' && sstrcmp( filter_chain_act->target, "-" ) != 0 )
				{
					printf("FilterRuelRead: syntax error at line %d (target has to be '-' when 'inspect next rule' is set) (#F.0.3)\n",linenum); 
					run_filter = 0;
					return;
				}

				linenum++;
				continue;
			}
			printf("FilterRuelRead: syntax error at line %d (#F.0.3)\n",linenum); 
			run_filter = 0;
			return;
		}
		fclose( ffp );
	}
	if (filter_chain != (filter_rule *)NULL)
		run_filter = 1;
}

/*****************************************************************************
** apply filter checks if one line matches filter set */

void apply_filter( char *buffer )
{
	filter_rule *filter_chain_act;
	char *ptr = buffer;
	flc *flcp;
	int len = 0;


	filter_chain_act = filter_chain;

	if ( hit_body == 0 && *buffer == '\n' )
		hit_body = 1;

	ptr = buffer;

	if ( hit_body == 0 )
	{
		if ( *buffer != '\t' && *buffer != ' ' ) /* if not continued header recalulate postion_tag */
		{
			while( *ptr != ' ' && *ptr != '\t' )
				ptr++;

			len = ( *ptr == ':' ) ? ptr - buffer - 2 : ptr - buffer - 1;

			while( *ptr == ' ' && *ptr == '\t' )
				ptr++;

			strncpy( header_text, buffer, len );
			header_text[len] = '\0';

			position_tag = FLOC_UNDEF;

			for(flcp = filter_locs; flcp->loc != (char *)0; flcp++)
				if (sstrcmp(flcp->loc,header_text) == 0 )
					position_tag = flcp->tag;
		}
	} else
		position_tag = FLOC_B;

	while ( *buffer != '\n' && filter_chain_act != (filter_rule *)NULL )
	{
		if ( filter_chain_act->loctag != position_tag && 
		     filter_chain_act->loctag != FLOC_AH && 
		    (filter_chain_act->loctag != FLOC_AR && (position_tag == FLOC_T || position_tag == FLOC_C)) )
		{
			filter_chain_act = filter_chain_act->next;
			continue;
		}

		switch ( filter_chain_act->loctag )
		{
		case FLOC_T :
		case FLOC_F :
		case FLOC_S :
		case FLOC_C :
		case FLOC_R :
		case FLOC_AH :
			if ( strstr(ptr, filter_chain_act->sstring) != (char *) NULL)
				filter_chain_act->hit = 1;
			break;
		case FLOC_AR :
			if ( position_tag == FLOC_T || position_tag == FLOC_C )
				if ( strstr(ptr, filter_chain_act->sstring) != (char *) NULL)
					filter_chain_act->hit = 1;
			break;
		case FLOC_B :
			if ( strstr(buffer, filter_chain_act->sstring) != (char *) NULL)
				filter_chain_act->hit = 1;
			break;
		}

		if ( filter_chain_act->hit == 1 )
			hit_filter = 1;
		
		filter_chain_act = filter_chain_act->next;
	}
}



/*****************************************************************************
** copy mail accoring to filter hit */

void copy_mail( char *mailfilename, char *target_folder )
{
 struct stat statdata;
 char mailname[256];
 char hostname[128];
 time_t tm;
 int pid,i;
 int mailfile;
 size_t bytes;
 int inputfile;
#ifdef HARD_QUOTA
 FILE *fs;
#endif


	gethostname(hostname,sizeof(hostname));
	pid=getpid();
	for (i=0; (i < 3) && (i > -1); i++) {
		time (&tm);
		sprintf(tmp_file_f,"%s/tmp/%lu.%d.%s",target_folder,tm,pid,hostname);
		if (stat(tmp_file_f,&statdata) == -1) {
			if (errno == ENOENT) {
				i=-2;	/* Not -1! Breaks program */
			}
		}
		if (i > -1) {
			sleep(2);
		}
	}
	if (i > 0)
		failtemp ("Unable to stat maildir (#4.3.1)\n");
	
	if ((mailfile = creat(tmp_file_f,S_IREAD | S_IWRITE)) == -1) {
		failtemp ("Can't create tempfile (#4.3.4)\n");
	}
	if ((inputfile = open(mailfilename,S_IREAD)) == -1) {
		failtemp ("Can't read tempfile (#4.3.4b)\n");
	}

	sprintf(msgbuf, "%sDelivered-To: %s@%s\n", 
		getenv("RPLINE"), TheUser, TheDomain);

	msg_size = slen(msgbuf);
	if (write(mailfile, msgbuf, msg_size) != msg_size) {
		delete_tmp_f();
		failtemp ("Failed to write RP & DT (#4.3.2)\n");
	}

#ifdef USE_DELIVER_FILTER
	hit_filter = 0;
#endif
	bytes = 0;
	while (fgets(msgbuf,sizeof(msgbuf),stdin) != NULL) {

#ifdef USE_DELIVER_FILTER
		/*
		 * filer hook up #2
		 * pipe lines throu filter
		 */
		if ( run_filter == 1 ) apply_filter( msgbuf );
#endif

		msg_size += bytes;
		if (write(mailfile,msgbuf,bytes) != bytes) {
			delete_tmp_f();
			failtemp ("Failed to write to tmp/ (#4.3.3)\n");
		}
	}

	/* Name the new file with Maildir++ extension */
	sprintf(mailname,"%s/new/%lu.%d.%s,S=%d",target_folder,tm,pid,hostname,(int)msg_size);

	if (bytes < 0) {
		delete_tmp_f();
		failtemp("Error occoured reading message (#4.3.4)\n");
	}
	if (fsync(mailfile) == -1) {
		delete_tmp_f();
		failtemp("Unable to sync file (#4.3.5)\n");
	}
	if (close(mailfile) == -1) {
		delete_tmp_f();
		failtemp("Unable to close() tmp file (#4.3.6)\n");
	}

#ifdef USE_DELIVER_FILTER
	/*
	 * filter hook up #3
	 * move / copy tmp file to folder(s) as to filter result
	 * link them to "new"
	 * delete temps
     */ 
	if ( hit_filter == 1 ) {
		do_filter_delivery( tmp_file );
	} else {
		if (link(tmp_file,mailname) == -1) {
			delete_tmp();
			failtemp("Unable to link tmp to new (#4.3.7)\n");
		}
	}
#else
	if (link(tmp_file_f,mailname) == -1) {
		delete_tmp_f();
		failtemp("Unable to link tmp to new (#4.3.7)\n");
	}
#endif

	delete_tmp_f();
}



/*****************************************************************************
** do the actions defined in the filter chains */

void do_filter_delivery( char *tmp_file )
{
	filter_rule *filter_chain_act;
	int result = 0;

	filter_chain_act = filter_chain;

	while ( filter_chain_act != (filter_rule *)NULL )
	{
		switch( filter_chain_act->operator )
		{
		case '-': /* start new chain */
			result = filter_chain_act->hit;
			break;
		case '&': /* and */
			result = (result == 1 && filter_chain_act->hit == 1) ? 1 : 0;
			break;
		case '|': /* or */
			result = (result == 1 || filter_chain_act->hit == 1) ? 1 : 0;
			break;
		case '!': /* not */
			result = (filter_chain_act->hit == 1) ? 0 : result;
			break;
		default:  /* undefined - error ! */
			result = 0;
			break;
		}

		if ( (filter_chain_act->action == '-' || filter_chain_act->action == '*') && result == 1 )
		{
			if ( *filter_chain_act->target == '.')
			{
				if ( sstrcmp( filter_chain_act->target, ".$$DELETE$$") == 0)
					break;
				copy_mail( tmp_file, filter_chain_act->target );
				result = 0;
			} else
			{
				if ( is_email_addr( filter_chain_act->target ) == 1 )
				{
					sprintf(tmp_buf, "/bin/cat %s | %s/bin/qmail-inject %s", 
						tmp_file, QMAILDIR, filter_chain_act->target ); 
					system(tmp_buf);
				}
			}
		}

		if ( filter_chain_act->action == '*' )
			break;

		filter_chain_act = filter_chain_act->next;
	}
	
}

/*****************************************************************************
** prit filter chain for debugging */

void print_filter_chain()
{
	filter_rule *filter_chain_act;

	filter_chain_act = filter_chain;
	printf("FilterChain:\n");

	while ( filter_chain_act != (filter_rule *)NULL )
	{
		printf( "op:%c\tloc:%s\tlt:%d\tss:%s\ttg:%s\tac:%c\th:%d\n", 
		filter_chain_act->operator, 
		filter_chain_act->location, 
		filter_chain_act->loctag, 
		filter_chain_act->sstring, 
		filter_chain_act->target, 
		filter_chain_act->action, 
		filter_chain_act->hit );
		filter_chain_act = filter_chain_act->next;
	}
}
#endif

/*****************************************************************************
** Deliver mail to Maildir in directory 'deliverto' **
** Follows procedure outlined in maildir(5).        */
void deliver_mail(char *deliverto, struct passwd *pw_data )
{
 struct stat statdata;
 char mailname[256];
 char hostname[128];
 time_t tm;
 int pid,i;
 int mailfile;
 size_t bytes;
 size_t wrote;
/*
#ifdef HARD_QUOTA
 FILE *fs;
#endif
*/

	getcwd(curdir,256);
	if (chdir(deliverto) == -1) {
		if ( vmake_maildir(deliverto, pw_data->pw_uid, pw_data->pw_gid)== -1) {
			failtemp ("Could not make user dirs (#4.2.2) %s\n");
		}
	}
#ifdef USE_DELIVER_FILTER
/*
   filter hookup #1

   read filter table ".vpopfilter"
*/
	read_filter();
#endif

	if (chdir("Maildir") == -1) {
		getcwd(mailname,256);
		failtemp ("Can't change to Maildir (#4.2.1) %s\n", mailname); 
	}

#ifdef HARD_QUOTA
	if ( pw_data!=NULL && 
	     sstrncmp(pw_data->pw_shell, "NO",2)!=0 ) {
         int i;
		per_user_limit = atol(pw_data->pw_shell);
		for(i=0;pw_data->pw_shell[i]!=0;++i){
			if ( pw_data->pw_shell[i] == 'k' || 
			     pw_data->pw_shell[i] == 'K' ) {
				per_user_limit = per_user_limit * 1000;
				break;
			}
			if ( pw_data->pw_shell[i] == 'm' || 
			     pw_data->pw_shell[i] == 'M' ) {
				per_user_limit = per_user_limit * 1000000;
				break;
			}
		}
	} else {
		per_user_limit = HARD_QUOTA; 
	}
	cur_msg_bytes = check_quota();
#endif

	gethostname(hostname,sizeof(hostname));
	pid=getpid();
	for (i=0; (i < 3) && (i > -1); i++) {
		time (&tm);
		sprintf(tmp_file,"tmp/%lu.%d.%s",tm,pid,hostname);
		if (stat(tmp_file,&statdata) == -1) {
			if (errno == ENOENT) {
				i=-2;	/* Not -1! Breaks program */
			}
		}
		if (i > -1) {
			sleep(2);
		}
	}
	if (i > 0)
		failtemp ("Unable to stat maildir (#4.3.1)\n");
	
	alarm(86400);
	if ((mailfile = creat(tmp_file,S_IREAD | S_IWRITE)) == -1) {
		failtemp ("Can't create tempfile (#4.3.4)\n");
	}

	sprintf(msgbuf, "%sDelivered-To: %s@%s\n", 
		getenv("RPLINE"), TheUser, TheDomain);

	msg_size = slen(msgbuf);
	if ((wrote = write(mailfile, msgbuf, msg_size)) != msg_size) {
		/* bk: check quota */
		if (errno == EDQUOT)
			bounce_writefail(mailfile, wrote, msgbuf, msg_size);
		delete_tmp();
		failtemp ("Failed to write RP & DT (#4.3.2)\n");
	}

	bytes=read(0,msgbuf,sizeof(msgbuf));
	while (bytes > 0) {
		msg_size += bytes;
		if ((wrote = write(mailfile,msgbuf,bytes)) != bytes) {
			/* bk: check quota */
			if (errno == EDQUOT)
				bounce_writefail(mailfile, wrote, msgbuf, bytes);
			delete_tmp();
			failtemp ("Failed to write to tmp/ (#4.3.3)\n");
		}
		bytes=read(0,msgbuf,sizeof(msgbuf));
	}

	/* Name the new file with Maildir++ extension */
	sprintf(mailname,"new/%lu.%d.%s,S=%d",tm,pid,hostname,(int)msg_size);

	if (bytes < 0) {
		delete_tmp();
		failtemp("Error occoured reading message (#4.3.4)\n");
	}
	if (close(mailfile) == -1) {
		delete_tmp();
		failtemp("Unable to close() tmp file (#4.3.6)\n");
	}
	if (link(tmp_file,mailname) == -1) {
		delete_tmp();
		failtemp("Unable to link tmp to new (#4.3.7)\n");
	}
	delete_tmp();

#ifdef HARD_QUOTA
/*bk:*/
	/*
	if ( pw_data != NULL && sstrncmp(pw_data->pw_shell, "NO",2)!=0 &&
	     msg_size > 1000 && cur_msg_bytes + msg_size > per_user_limit ) {
		cur_msg_bytes = recalc_quota(".");
	    if ( cur_msg_bytes + msg_size > per_user_limit ) {
	*/
	if ( msg_size > 1000 &&
	    ((per_domain_limit > 0 && cur_domain_bytes + msg_size > per_domain_limit) ||
	     (pw_data != NULL && sstrncmp(pw_data->pw_shell, "NO",2)!=0 &&
	      cur_msg_bytes + msg_size > per_user_limit)) ) {
		cur_msg_bytes = recalc_quota(".");
	    if ( cur_msg_bytes + msg_size > per_user_limit ||
	         (per_domain_limit > 0 && cur_domain_bytes + msg_size > per_domain_limit) ) {
/*bk*/
			if (bounce_quota(mailname)) {
				printf("User is over quota, email returned\n");
				exit(100);
			}

			/*
			sprintf(tmp_file, "%s/domains/.over-quota.msg",VPOPMAILDIR);
			fs=fopen(tmp_file, "r");

			if ( fs == NULL ) {
				printf("User is over quota email returned\n");
			} else {
				while( fgets( tmp_file, 256, fs ) != NULL ) {
					fputs(tmp_file, stdout);
				}
				fclose(fs);
			}
			unlink(mailname);
			*/

			/* exit 100 will cause the email to be bounced back to sender */
			/*printf("bounce\n"); */
			/*
			exit(100);
			*/
		}
	} else {
		update_quota(cur_msg_bytes + msg_size);
	}
#endif
}

/*****************************************************************************
** The main bit :) If it gets to the end of here, delivery was successful   */
int main(int argc, char *argv[])
{
 static struct passwd *pw_data;
 char *deliverto;
 char bounce[1024];
 char prefix[1024];
 char *tmpstr;

	if (argc > 3) {
		failtemp ("Syntax: %s [prefix [bounceable_mail]]\n",argv[0]);
	}

	openlog("vdelivermail",LOG_PID,LOG_MAIL);

	*bounce = 0;
	*prefix = 0;
	*tmp_file = 0;
	if (argc > 1) {
		scopy(prefix,argv[1],sizeof(prefix));
		if (argc == 3) {
			scopy (bounce,argv[2],sizeof(prefix));
		}
	}

	scopy(TheUser, getenv("EXT"), MAX_BUFF);
	scopy(TheDomain, getenv("HOST"), MAX_BUFF);
	lowerit(TheUser);
	lowerit(TheDomain);
	vget_real_domain(TheDomain,MAX_BUFF);

	pw_data=pop_user_exist(TheUser,TheDomain,prefix,bounce);

	if (!pw_data) {
		if ( is_delete(bounce) ) {
			exit(0);
		}
		if ( !is_bounce(bounce) ) {
			printf ("POP user does not exist, but will deliver to %s\n",bounce);
		}
		deliverto = bounce;
	} else {
		if ( check_forward_deliver(pw_data->pw_dir) == 1 ) {
			exit(0);
		}
        deliverto = pw_data->pw_dir;
	}

	if ( is_email_addr(deliverto) == 1) {
		tmpstr = email_it(deliverto);
		unlink(tmpstr);
	} else if ( is_bounce(deliverto) == 1) {
		/* bk */
		if (bounce_nouser()) {
			FILE *fs;
			if ( (fs=fopen(".no-user.msg","r")) != NULL ) {
				while( fgets( bounce, 1024, fs ) != NULL ) {
					printf("%s", bounce);
				}
				fclose(fs);
			} else {
				printf(
			   "Sorry, no mailbox here by that name. vpopmail (#5.1.1)\n");
			}
			exit(100);
		}
	} else { 
		deliver_mail(deliverto, pw_data);
	}
	exit(0);
}

int is_bounce(deliverto)
 char *deliverto;
{
	if ( sstrcmp( deliverto, BOUNCE_ALL ) == 0 ) {
		return(1);
	} 
	return(0);
}

int is_delete(deliverto)
 char *deliverto;
{
	if ( sstrcmp( deliverto, DELETE_ALL ) == 0 ) {
		return(1);
	} 
	return(0);
}



#ifdef HARD_QUOTA

/*
 * Assumes the current working directory is user/Maildir
 *
 * We go off to look at cur and tmp dirs
 * 
 * return size of files
 *
 */
off_t check_quota()
{
 off_t mail_size = 0;
 char tmpbuf[100];
 char *cwd;
 int fd;

 	if ((CurrentQuotaSizeFd=
		open(".current_size", O_CREAT|O_RDWR, S_IWUSR|S_IRUSR))==-1){
		return(mail_size);
	}
	read(CurrentQuotaSizeFd, tmpbuf, 100);
	mail_size = (off_t)atoi(tmpbuf);

	/* bk: get domain quota size */
 	if (CurrentDomainQuotaSizeFd >= 0) {
		close(CurrentDomainQuotaSizeFd);
		CurrentDomainQuotaSizeFd = -1;
	}
	if ((cwd = getcwd(NULL, 128)) != NULL) {
		chdir("../..");
		per_domain_limit = 0;
		cur_domain_bytes = 0;
		/* First get the quota, if any */
		if ((fd=open(".domainquota", O_RDONLY)) >= 0) {
			read(fd, tmpbuf, 100);
			per_domain_limit = (off_t)atoi(tmpbuf);
			close(fd);
		}
		if (per_domain_limit > 0)
			CurrentDomainQuotaSizeFd=open(".current_size", O_CREAT|O_RDWR, S_IWUSR|S_IRUSR);
		chdir(cwd);
		free(cwd);
		if (CurrentDomainQuotaSizeFd >= 0) {
			read(CurrentQuotaSizeFd, tmpbuf, 100);
			cur_domain_bytes = (off_t)atoi(tmpbuf);
		}
	}
	/* bk */

	return(mail_size);
}

off_t recalc_quota(char *dir_name)
{
 off_t mail_size = 0;
 char tmpbuf[100];
 char *cwd;

	mail_size = count_dir(dir_name);

	/* bk: now calculate domain quota */
 	if (CurrentDomainQuotaSizeFd >= 0) {
		if ((cwd = getcwd(NULL, 128)) != NULL) {
			chdir("../..");
			cur_domain_bytes = count_dir(".");
			chdir(cwd);
			free(cwd);
		}
	}

	if ( msg_size > 1000 && 
	     (mail_size > per_user_limit || (per_domain_limit > 0 && mail_size > per_domain_limit ))) {
		mail_size -= msg_size;
		cur_domain_bytes -= msg_size;
	}

	sprintf(tmpbuf, "%d\n", (int)mail_size);
	lseek(CurrentQuotaSizeFd, 0L, SEEK_SET);
	write(CurrentQuotaSizeFd, tmpbuf, slen(tmpbuf));
	close(CurrentQuotaSizeFd);

	/* bk: update domain quota */
	if (CurrentDomainQuotaSizeFd >= 0) {
		sprintf(tmpbuf, "%d\n", (int)cur_domain_bytes);
		lseek(CurrentDomainQuotaSizeFd, 0L, SEEK_SET);
		write(CurrentDomainQuotaSizeFd, tmpbuf, slen(tmpbuf));
		close(CurrentDomainQuotaSizeFd);
		CurrentDomainQuotaSizeFd = -1;
	}

	return(mail_size);
}

void update_quota(off_t new_size)
{
 char tmpbuf[100];

	sprintf(tmpbuf, "%d\n", (int)new_size);
	lseek(CurrentQuotaSizeFd, 0L, SEEK_SET);
	write(CurrentQuotaSizeFd, tmpbuf, slen(tmpbuf));
	close(CurrentQuotaSizeFd);

	/* bk: update domain quota */
	if (CurrentDomainQuotaSizeFd >= 0) {
		sprintf(tmpbuf, "%d\n", (int)cur_domain_bytes);
		lseek(CurrentDomainQuotaSizeFd, 0L, SEEK_SET);
		write(CurrentDomainQuotaSizeFd, tmpbuf, slen(tmpbuf));
		close(CurrentDomainQuotaSizeFd);
		CurrentDomainQuotaSizeFd = -1;
	}
}

off_t count_dir(dir_name)
 char *dir_name;
{
 DIR *mydir;
 struct dirent *mydirent;
 struct stat statbuf;
 off_t file_size = 0;
 char *tmpstr;

	if ( dir_name != NULL ) if (chdir(dir_name) == -1) return(0);
	if ( (mydir = opendir(".")) == NULL ) return(0);

	while( (mydirent=readdir(mydir)) != NULL ) {
		if ( sstrcmp( mydirent->d_name, "..") == 0 ) continue;
		if ( sstrcmp( mydirent->d_name, ".") == 0 ) continue;
		if ( (tmpstr=strstr(mydirent->d_name, ",S="))!=NULL) {
			tmpstr += 3;
			file_size += atoi(tmpstr);
		} else if (stat(mydirent->d_name,&statbuf)==0 && 
		           (statbuf.st_mode & S_IFDIR) ) {
			file_size +=  count_dir(mydirent->d_name);
		}
	}
	closedir(mydir);
	if ( dir_name != NULL && sstrcmp(dir_name, ".." )!=0 && 
	                         sstrcmp(dir_name, "." )!=0) {
		chdir("..");
	}
	return(file_size);
}
#endif

int is_email_addr(deliverto)
 char *deliverto;
{
 int i;

	for(i=0;deliverto[i]!=0;++i){
		if ( deliverto[i] == '@' ) return(1);
	}
	return(0);
}

int email_file(char *filename, char *address)
{
 char tmp_buf[256];

	sprintf(tmp_buf, "/bin/cat %s | %s/bin/qmail-inject %s", 
		filename, QMAILDIR, address ); 
	system(tmp_buf);
	return(0);
}

char *email_it(deliverto)
 char *deliverto;
{
 static char tmp_file[256];
 char hostname[128];
 time_t tm;
 int pid;
 int mailfile;
 size_t bytes;

	gethostname(hostname,sizeof(hostname));
	pid=getpid();
	time (&tm);
	sprintf(tmp_file,"/tmp/%lu.%d.%s",tm,pid,hostname);
	if ((mailfile = creat(tmp_file,S_IREAD | S_IWRITE)) == -1) {
		failtemp ("Can't create tempfile (#4.3.8)\n");
	}

	sprintf(msgbuf, "%sDelivered-To: %s@%s\n", 
		getenv("RPLINE"), TheUser, TheDomain);
	if (write(mailfile, msgbuf, slen(msgbuf)) != slen(msgbuf)) {
		delete_tmp();
		failtemp ("Failed to write RP & DT (#4.3.9)\n");
	}

	while((bytes=read(0,msgbuf,sizeof(msgbuf)))>0) {
		if (write(mailfile,msgbuf,bytes) != bytes) {
			delete_tmp();
			failtemp ("Failed to write to tmp/ (#4.3.10)\n");
		}
	}
	if (bytes < 0) {
		delete_tmp();
		failtemp("Error occoured reading message (#4.3.11)\n");
	}
	if (fsync(mailfile) == -1) {
		delete_tmp();
		failtemp("Unable to sync file (#4.3.12)\n");
	}
	if (close(mailfile) == -1) {
		delete_tmp();
		failtemp("Unable to close() tmp file (#4.3.13)\n");
	}

	sprintf(tmp_buf, "/bin/cat %s | %s/bin/qmail-inject %s", 
		tmp_file, QMAILDIR, deliverto ); 
	system(tmp_buf);

	return(tmp_file);
}

int check_forward_deliver(char *dir)
{
 static char email_address[100];
 char tmpbuf[500];
 FILE *fs;
 int i;
 int return_value = 0;
 char *tmpstr = NULL;

	sprintf(tmpbuf, "%s/.qmail", dir);
	fs = fopen(tmpbuf,"r");
	if ( fs == NULL ) {
		return(-1);
	}
	while ( fgets(email_address, 100, fs ) != NULL ) {
		for(i=0;email_address[i]!=0;++i) {
			if (email_address[i] == '\n') email_address[i] = 0;
		}
		if ( return_value == 0 ) {
			tmpstr = email_it(email_address);
		} else {
			email_file(tmpstr, email_address);
		}
		return_value = 1;
	}
	fclose(fs);
	if ( tmpstr != NULL ) {
		unlink(tmpstr);
	}
	return(return_value);
}

int bounce_it_back( char *filename )
{		
 FILE *fs;
 FILE *fs1;
 char tmpbuf1[300];
 int i,j;
 int first_line = 0;
 int subject_found = 0;
 int from_found = 0;

	if ( (fs = fopen(filename,"r+")) == NULL ) {
		unlink(filename);
		return(-1);
	}
	/* find return path or from variable */

	while ( fgets(tmp_buf, TMP_BUF_SIZE, fs ) != NULL ) {
		for(i=0;return_path_field[i] != 0; ++i){
			if ( tmp_buf[i] != return_path_field[i] ) break;
		}
		if ( return_path_field[i] == 0 ) {
			while(tmp_buf[i] == ' ' || tmp_buf[i] == '<') ++i;
			for(j=0;tmp_buf[i]!='>' && tmp_buf[i]!=0;++i,++j){
				tmp_file[j] = tmp_buf[i];
			}
			tmp_file[j] = 0;
			break;
		} else {
			for(i=0;from_field[i] != 0; ++i){
				if ( tmp_buf[i] != return_path_field[i] ) break;
			}
			if ( from_field[i] == 0 ) {
				while(tmp_buf[i]==' '|| tmp_buf[i] == '<') ++i;
				for(j=0;tmp_buf[i]!='>'&&tmp_buf[i]!=0;++i,++j){
					tmp_file[j] = tmp_buf[i];
				}
				tmp_file[j] = 0;
				break;
			}
		}
	}
	rewind(fs);

	sprintf(tmpbuf1,"%s.bak", filename);
	if ( (fs1 = fopen(tmpbuf1,"w+")) == NULL ) {
		unlink(filename);
		return(-1);
	}

	while ( fgets(tmp_buf, TMP_BUF_SIZE, fs ) != NULL ) {
		if ( subject_found == 0 ) {
			for(i=0;subject_field[i] != 0; ++i){
				if ( tmp_buf[i] != subject_field[i] ) break;
			}
			if ( subject_field[i] == 0 ) {
				subject_found = 1;
				fprintf( fs1, "Subject: User over quota RE: %s",
					&tmp_buf[i]);
				continue;
			}
		} 

		if ( from_found == 0 ) {
			for(i=0;from_field[i] != 0; ++i){
				if ( tmp_buf[i] != from_field[i] ) break;
			}
			if ( from_field[i] == 0 ) {
				from_found = 1;
				fprintf( fs1, "From: postmaster@%s\n",
					 getenv("HOST"));
				continue;
			}
		} 

		if ( first_line == 0 ) {
			for(i=0;tmp_buf[i]!=0;++i) 
				if ( !isspace(tmp_buf[i]) ) break;
			if ( tmp_buf[i] == 0 ) {
				first_line = 1;
				fputs(tmp_buf, fs1);
				fprintf(fs1, 
				"User is over quota, message bounced.\n\n");
				continue;
			}
		} 

		fputs(tmp_buf, fs1);

		/* only return 15 lines of the message */
		if (first_line > 0) {
			++first_line;
			if ( first_line > 15 ) break;
		}
	}
	fclose(fs);
	fclose(fs1);

	sprintf(tmp_buf, "/bin/cat %s | %s/bin/qmail-inject %s", 
		tmpbuf1, QMAILDIR, tmp_file ); 
	system(tmp_buf);

	unlink(filename);
	unlink(tmpbuf1);
	return(0);
}

int bounce_quota(char* filename)
{
	char* to = getenv("EXT");
	char* domain = getenv("HOST");
	char* sender = getenv("SENDER");
	char tmp_filename[256];
	char buf[300];
	FILE* fp1;
	FILE* fp2;

	syslog(LOG_INFO, "bounce quota to <%s@%s> from <%s>", to, domain, sender);
	sprintf(buf, "%s/bin/qmail-inject -f postmaster@%s %s", QMAILDIR, domain, sender);
	if ((fp1 = popen(buf, "w")) == NULL)
		return(-1);
	fprintf(fp1, "From: postmaster@%s\n", domain);
	fprintf(fp1, "To: %s\n", sender);
	fprintf(fp1, "Subject: delivery failure notice\n");
	fprintf(fp1, "\n<%s@%s>:\n", to, domain);

	/* get message, use default if none supplied */
	sprintf(tmp_filename, "%s/domains/.over-quota.msg",VPOPMAILDIR);
	if ((fp2 = fopen(tmp_filename, "r")) != NULL) {
		while (fgets(buf, sizeof(buf), fp2) != NULL) {
			fputs(buf, fp1);
		}
		fclose(fp2);
	} else {
		fprintf(fp1, "This mailbox is currently full, email returned.\n");
	}

	if ((fp2 = fopen(filename, "r")) != NULL) {
		fprintf(fp1, "\n\n--- Below is a copy of the message.\n\n");
		while (fgets(buf, sizeof(buf), fp2) != NULL) {
			fputs(buf, fp1);
		}
		fclose(fp2);
	}
	fclose(fp1);
	unlink(filename);
	notify_postmaster();
	return(0);
}

int bounce_mail(char* message)
{
	char* to = getenv("EXT");
	char* domain = getenv("HOST");
	char* sender = getenv("SENDER");
	char buf[300];
	FILE* fp1;
	int bytes;

	syslog(LOG_INFO, "bounce mail - quota to <%s@%s> from <%s>", to, domain, sender);
	sprintf(buf, "%s/bin/qmail-inject -f postmaster@%s %s", QMAILDIR, domain, sender);
	if ((fp1 = popen(buf, "w")) == NULL)
		return(-1);
	fprintf(fp1, "From: postmaster@%s\n", domain);
	fprintf(fp1, "To: %s\n", sender);
	fprintf(fp1, "Subject: delivery failure notice\n");
	fprintf(fp1, "\n<%s@%s>:\n", to, domain);
	fprintf(fp1, "%s\n", message);
	fprintf(fp1, "\n\n--- Below is a copy of the message.\n\n");

	bytes = read(0, buf, sizeof(buf));
	while (bytes > 0) {
		if (fwrite(buf, 1, bytes, fp1) != bytes) {
			failperm("User is over quota, email returned\n");
		}
		bytes = read(0, buf, sizeof(buf));
	}
	fclose(fp1);
	notify_postmaster();
	return(0);
}

int bounce_nouser()
{
	char* to = getenv("EXT");
	char* domain = getenv("HOST");
	char* sender = getenv("SENDER");
	char tmp_filename1[256];
	char buf[300];
	FILE* fp1;
	FILE* fp2;
	int bytes;

	syslog(LOG_INFO, "bounce nouser to <%s@%s> from <%s>", to, domain, sender);
	sprintf(buf, "%s/bin/qmail-inject -f postmaster@%s %s", QMAILDIR, domain, sender);
	if ((fp1 = popen(buf, "w")) == NULL)
		return(-1);
	fprintf(fp1, "From: postmaster@%s\n", domain);
	fprintf(fp1, "To: %s\n", sender);
	fprintf(fp1, "Subject: delivery failure notice\n");
	fprintf(fp1, "\n<%s@%s>:\n", to, domain);

	if ((fp2 = fopen(".no-user.msg","r")) != NULL ) {
		while (fgets(buf, sizeof(buf), fp2) != NULL) {
			fprintf(fp1, "%s", buf);
		}
		fclose(fp2);
	} else {
		fprintf(fp1, "Sorry, no mailbox here by that name. vpopmail (#5.1.1)\n");
	}
	fprintf(fp1, "\n\n--- Below is a copy of the message.\n\n");

	bytes = read(0, buf, sizeof(buf));
	while (bytes > 0) {
		if (fwrite(buf, 1, bytes, fp1) != bytes) {
			failperm("Sorry, no mailbox here by that name. vpopmail (#5.1.1)\n");
		}
		bytes = read(0, buf, sizeof(buf));
	}
	fclose(fp1);

	unlink(tmp_filename1);
	return(0);
}

int bounce_writefail(int mailfile, size_t wrote, char* msgbuf, off_t size)
{
	char* to = getenv("EXT");
	char* domain = getenv("HOST");
	char* sender = getenv("SENDER");
	char tmp_filename[256];
	char buf[300];
	int bytes;
	FILE* fp1;
	FILE* fp2;

	syslog(LOG_INFO, "bounce writefail to <%s@%s> from <%s>", to, domain, sender);
	/*
	 * drop message on floor if both sender and recipient are same 
	 * postmaster for local domain
	 */
	sprintf(buf, "postmaster@%s", domain);
	if (!strcmp(sender, buf)) {
		syslog(LOG_INFO, "bounce writefail - dropping postmaster notification");
		delete_tmp();
		exit(0);
	}

	sprintf(buf, "%s/bin/qmail-inject -f postmaster@%s %s", QMAILDIR, domain, sender);
	if ((fp1 = popen(buf, "w")) == NULL)
		return(-1);
	fprintf(fp1, "From: postmaster@%s\n", domain);
	fprintf(fp1, "To: %s\n", sender);
	fprintf(fp1, "Subject: delivery failure notice\n\n");
	fprintf(fp1, "\n<%s@%s>:\n", to, domain);

	/* get message, use default if none supplied */
	sprintf(tmp_filename, "%s/domains/.over-quota.msg",VPOPMAILDIR);
	if ((fp2 = fopen(tmp_filename, "r")) != NULL) {
		while (fgets(buf, sizeof(buf), fp2) != NULL) {
			fputs(buf, fp1);
		}
		fclose(fp2);
	} else {
		fprintf(fp1, "This mailbox is currently full, email returned.\n");
	}
	fprintf(fp1, "\n\n--- Below is a copy of the message.\n\n");

	/* forward what made it to file */
	if (mailfile > 0) {
		lseek(mailfile, 0, SEEK_SET);
		bytes = read(mailfile, buf, sizeof(buf));
		while (bytes > 0) {
			if (fwrite(buf, 1, bytes, fp1) != bytes) {
				failperm("This mailbox is currently full, email returned\n");
			}
			bytes = read(0, buf, sizeof(buf));
		}
	}

	if (size > 0 && size > wrote) {
		if (wrote < 0)
			wrote = 0;
		/* forward what was left in buffer */
		if (fwrite(msgbuf, 1, size - wrote, fp1) != size - wrote) {
			failperm("This mailbox is currently full, email returned\n");
		}
	}

	/* forward rest of STDIN */
	bytes = read(0, buf, sizeof(buf));
	while (bytes > 0) {
		if (fwrite(buf, 1, bytes, fp1) != bytes) {
			failperm("This mailbox is currently full, email returned\n");
		}
		bytes = read(0, buf, sizeof(buf));
	}

	fclose(fp1);
	delete_tmp();
	notify_postmaster();
	exit(0);
}

int notify_postmaster()
{
	char* to = getenv("EXT");
	char* domain = getenv("HOST");
	char* sender = getenv("SENDER");
	char buf[300];
	FILE* fp1;

	syslog(LOG_INFO, "notify postmaster of quota bounce to <%s@%s> from <%s>", to, domain, sender);
	sprintf(buf, "%s/bin/qmail-inject -f postmaster@%s postmaster@%s", QMAILDIR, domain, domain);
	if ((fp1 = popen(buf, "w")) == NULL)
		return(-1);
	fprintf(fp1, "From: postmaster@%s\n", domain);
	fprintf(fp1, "To: postmaster@%s\n", domain);
	fprintf(fp1, "Subject: delivery failure notice\n\n");
	fprintf(fp1, "Delivery From: <%s>\n", sender);
	fprintf(fp1, "         To: <%s@%s>\n\n", to, domain);
	fprintf(fp1, "Over Quota\n");
	fclose(fp1);
	return(0);
}

Reply via email to