/*  Email v0.1
 *  simple command line utility for sending electronic mail
 *  by trace@hysteria.sk 
 *
 *  TODO: - rawdata
 *        - attachments
 *		  - queue
 *        ? multi-user 
 *        ? ncurses GUI 
 *
 *  FEATURES: - gpg
 *            - external editor
 *            - userfriendly config file ;)
 *
 *
 *   Parameters:           in config file:
 *
 *   -h <smtp host>        smtphost=localhost
 *   -p <port>             port=25
 *   
 *   -t <to address>       to=<address>
 *   -f <from address>     from=<address>
 *   -s <subject>          subject=<subject>
 *   -e                    use editor=yes|no
 *                         editor=vi
 *   -E                    gpg encrypt=yes|no   (when this is set, 
 *						signing automaticaly)
 *   -S                    gpg sign=yes|no
 * 			   reply to=<address>
 *   -R (restores previous failed message)
 * 
 *   config file has lower priority than parametres on command line.
 *   program read config file from ~/.emailrc, than from ./.emailrc
 *
 *  Example:
 *
 *  ./email -t user@server.com < textfile
 *  sends textfile to user@server.com, smtp server is localhost (if not
 *  set in config file [cf]), from address is $USER@$HOSTNAME (if not set in cf)
 *  Subject is not set (if isn't set in cf) but if first line of textfile is
 *  Subject: sample
 *  subject will be 'sample'.
 *
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include <sys/types.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <netinet/in.h>

typedef
struct opt
{
	int verbose;
	int debug;
	int rawdata;
	int port;
	char addr_from[256];
	char *addr_to;
	char *host;
	char subject[64];
	char localname[256];
	char editor[32];
	int use_editor;
	int gpg_crypt;
	int gpg_sign;
	char gpg_default_key[64];
	char reply_to[256];
	int restore;
}opt;

/* default options */
opt options = { 1, 0, 0, 25, "\0", NULL, "localhost", "\0", "localhost","vi", 0, 0, 0, "\0", "\0", 0};

void usage();
void status(int ok);
int loadoptions();

int main(int argc, char *argv[])
{
	int sockfd;
	struct sockaddr_in address;
	char buffer[1024];
	int opt;
	struct hostent *hostinfo;

	if (loadoptions()==-1 && options.debug) 
		printf("Could not load config file!\n");

/* parsing command-line options */
	while ((opt=getopt(argc, argv, "rh:p:f:t:s:eESR"))!=-1) 
		switch (opt) {
			case 'r':
				options.rawdata=1;
				break;
			case 'h':
				options.host=optarg;
				break;
			case 'p':
				options.port=atoi(optarg);
				break;
			case 'f':
				snprintf(options.addr_from,sizeof(options.addr_from),"%s",optarg);
				break;
			case 't':
				options.addr_to=optarg;
				break;
			case 's':
				snprintf(options.subject,sizeof(options.subject),"%s",optarg);
				break;
			case 'e':
				options.use_editor=1;
				break;
			case 'E':
				options.gpg_crypt=1;
				options.use_editor=1;	
				break;
			case 'S':
				options.gpg_sign=1;
				options.use_editor=1;
				break;
			case 'R':
				options.restore=1;
				options.use_editor=1;
				break;
		}

	if (options.host=="localhost" && options.debug) 
		printf("using localhost as smtp server\n");

	if (!options.rawdata && options.addr_to==NULL) {
		if (options.verbose) 
			printf("Destination address is not specified!\n");
		usage();
		exit(-1);
	}
	
	if (strlen(options.addr_from)==0)	
		snprintf(options.addr_from,sizeof(options.addr_from),"%s@%s",getenv("USER"),getenv("HOSTNAME"));
	
	hostinfo=gethostbyname(options.host);
	if (!hostinfo){
		perror("gethostbyname");
		exit(-1);
	}

	gethostname(options.localname,255);

	if ((sockfd=socket(PF_INET,SOCK_STREAM,0))==-1){
		perror("socket");
		exit(-1);
	}
	
	address.sin_port=htons(options.port);
	address.sin_family=AF_INET;
	address.sin_addr=*(struct in_addr *)*hostinfo->h_addr_list;
	
	if (connect(sockfd, (struct sockaddr_in *) &address, sizeof(address))==-1){
		perror("connect");
		exit(-1);
	}

	memset(buffer,0,sizeof(buffer));

	recv(sockfd, buffer, sizeof(buffer), 0);
	if (options.debug) 
		printf("%s",buffer);
	printf("                    -[\e[36mE M A I L\e[0m]-\n");
	

/* pokud vstupni data nejsou raw data urcena SMTP serveru, 
   posle program autentizaci, urceni prijemce a odesilatele
   a zahlavi mejlu */
	
	if (!options.rawdata) {
		snprintf(buffer,sizeof(buffer),"HELO %s\r\n",options.localname);
		send(sockfd,buffer,strlen(buffer),0);
		memset(buffer,0,sizeof(buffer));
		recv(sockfd,buffer,sizeof(buffer),0);
		if (options.debug) printf("HELO ... ");
		status(strstr(buffer,"250 "));

		snprintf(buffer,sizeof(buffer),"MAIL FROM: %s\r\n",options.addr_from);
		send(sockfd,buffer,strlen(buffer),0);
		memset(buffer,0,sizeof(buffer));
		recv(sockfd,buffer,sizeof(buffer),0);
		if (options.debug) printf("MAIL FROM: ... ");
		status(strstr(buffer,"250 "));

		snprintf(buffer,sizeof(buffer),"RCPT TO: %s\r\n",options.addr_to);
		send(sockfd,buffer,strlen(buffer),0);
		memset(buffer,0,sizeof(buffer));
		recv(sockfd,buffer,sizeof(buffer),0);
		if (options.debug) printf("RCPT TO: ... ");
		status(strstr(buffer,"250 "));

		sprintf(buffer,"DATA\r\n");
		send(sockfd,buffer,strlen(buffer),0);
		memset(buffer,0,sizeof(buffer));
		recv(sockfd,buffer,sizeof(buffer),0);
		status(strstr(buffer,"354 "));
	
		/* send headers */
		sprintf(buffer,"MIME-version: 1.0\r\n");
		send(sockfd,buffer,strlen(buffer),0);	

		sprintf(buffer,"Content-type: text/plain\r\n");
		send(sockfd,buffer,strlen(buffer),0);	

		snprintf(buffer,sizeof(buffer),"Disposition-Notification-To: %s\r\n",options.addr_from);
		send(sockfd,buffer,strlen(buffer),0);	
		
		if (strlen(options.reply_to)==0) 
			snprintf(options.reply_to,sizeof(options.reply_to),"%s",options.addr_from);
		snprintf(buffer, sizeof(buffer),"Reply-To: %s\r\n",options.reply_to);
		send(sockfd,buffer,strlen(buffer),0);

		if (strlen(options.subject)>0) {
			snprintf(buffer,sizeof(buffer),"Subject: %s\r\n",options.subject);
			send(sockfd,buffer,strlen(buffer),0);
		}
	}		

	if (options.verbose)
		printf("write message (end with ^D at the beginning of line): \n");
	
/* if we use editor to write a message, open editor and send text from file */
	if (options.use_editor) {
		char tmp[128], tmp2[128];
		
		if (options.restore) 
			system("mv ~/.restore /tmp/email.message.tmp");
				
		snprintf(tmp,sizeof(tmp),"%s /tmp/email.message.tmp",options.editor);
		system(tmp);

	/* save filename to tmp */
		snprintf(tmp,sizeof(tmp),"/tmp/email.message.tmp");
		
	/* if we use gpg encrypt */
		if (options.gpg_sign || options.gpg_crypt) gpg(&tmp);
	
	/* send message from textfile */
		if (sendtextfromfile(sockfd, tmp)==-1){
			if (options.verbose) 
				printf("no mail to send...\n");
			exit(-1);
		}

	 /* erase sent file */	
		snprintf(tmp2,sizeof(tmp2),"rm %s",tmp);
		system(tmp2);
	} 
	else
/* if we dont use editor, read message from stdin a send line by line */
		while (1){
			if (fgets(buffer,sizeof(buffer),stdin)==NULL) break;
			send(sockfd, buffer, strlen(buffer), 0);
		}
	
	printf("sending email..");
	
	if (!options.rawdata) {
		send(sockfd, "\r\n.\r\n\n", 5,0); 
		memset(buffer,0,sizeof(buffer));
		recv(sockfd,buffer,sizeof(buffer));	
		if (strstr(buffer,"250 ")) 
			printf("OK\n");
		else 
			printf("failed!\n");

		send(sockfd, "QUIT\r\n",6,0);
		memset(buffer,0,sizeof(buffer));
		recv(sockfd,buffer,sizeof(buffer));	
	}
	close(sockfd);
}

void usage(){
	printf("\e[36memail 0.1\e[0m \n");
	printf("usage: email [-h <smtp_host>] [-p port] -r | -t to_addr [-f from_addr] [-s subject] < textfile\n");
}

void status(int ok){
	if (ok) {
		if (options.debug)
			printf("OK\n");
	} else {
		printf("sending failed !\n");
		exit(-1);
	}
}

/* returns option name */
char *option(char *buff) {
	int i=0;
	char *str;
	while (buff[i]!='=') i++;
	str=malloc(i+1);
	str[i]='\0';
	while (i>0) {
		str[i-1]=buff[i-1];
		i--;
	}
	return str;
}

/* returns options value */
char *value(char *buff) {
	int i=0;
	char *str, *tmp;
	while (buff[i]!='=') i++;
	i++;

	str=malloc(strlen(buff)-i-1);
	tmp=str;

	while (i<strlen(buff)-1) 
		*(tmp++)=buff[i++];
	*tmp='\0';
	return str;		
}

int loadoptions(){
	FILE *fd;
	char filename[512];
	char buffer[512];

	memset(buffer,0,512);
	
	/* search .emailrc in ~ and in actual path */
	snprintf(filename,sizeof(filename),"%s/.emailrc",getenv("HOME"));
	if ((fd=fopen(filename,"r"))==NULL) {
		sprintf(filename,"./.emailrc");
		fd=fopen(filename, "r");	
	} 

	if (fd!=NULL) {
		char opt[512], val[512];
	
		while (fgets(buffer, sizeof(buffer), fd)!=NULL) {

			if (buffer[0]=='#' || !strstr(buffer,"=")) 
				continue;

			snprintf(opt,sizeof(opt),"%s",option(buffer));
			snprintf(val,sizeof(val),"%s",value(buffer));

			/* variables */
			if (strstr(val,"$to"))
				snprintf(val,sizeof(val),"%s",options.addr_to);
			if (strstr(val,"$from"))
				snprintf(val,sizeof(val),"%s",options.addr_from);
			
			if (strstr(opt,"rawdata"))
				options.rawdata=strstr(val,"yes");	
		        else if (strstr(opt,"debug")) 
				options.debug=strstr(val,"yes");
		        else if (strstr(opt,"verbose")) 
				options.verbose=strstr(val,"yes");
		        else if (strstr(opt,"from")) 
				snprintf(options.addr_from,sizeof(options.addr_from),"%s",val);
		        else if (strstr(opt,"port")) 
				options.port=atoi(val);
		        else if (strstr(opt,"subject")) 
				snprintf(options.subject,sizeof(options.subject),"%s",val);
			else if (strstr(opt,"localname"))
				snprintf(options.localname,sizeof(options.localname),"%s",val);
			else if (strstr(opt,"use editor"))
				options.use_editor=strstr(val,"yes");
			else if (strstr(opt,"editor")) 
				snprintf(options.editor,sizeof(options.editor),"%s",val);
			else if (strstr(opt,"reply to"))
				snprintf(options.reply_to,sizeof(options.reply_to),"%s",val);
			else if (strstr(opt,"to"))
				options.addr_to=val;
			else if (strstr(opt,"gpg encrypt"))
				options.gpg_crypt=strstr(val,"yes");
			else if (strstr(opt,"gpg sign"))
				options.gpg_sign=strstr(val,"yes");
			else if (strstr(opt,"gpg default key"))
				snprintf(options.gpg_default_key,sizeof(options.gpg_default_key),"%s",val);
		}
		fclose(fd);
	} else  return(-1);
	return(0);
}

int sendtextfromfile(int sock, char *filename) {
	FILE *fd;
	char buffer[512];

	memset(buffer,0,512);

	if ((fd=fopen(filename,"r"))==NULL) {
		if (options.verbose)  
			printf("cant read from tempfile...\n");
		return(-1);
	}

	while (fgets(buffer, sizeof(buffer), fd)!=NULL)
		send(sock,buffer,strlen(buffer),0);

	fclose(fd);
	return (0);
}

int gpg(char **filename){
	char tmp[1024];
	int err;

	memset(tmp,0,1024);

	/* just sign message */
	if (options.gpg_sign && !options.gpg_crypt) 
		snprintf(tmp,sizeof(tmp),"gpg --sign --yes 
			--default-key %s -u %s -t -a -o /tmp/emailgpg.txt %s", 
			options.gpg_default_key, options.gpg_default_key, 
			filename);
	/* encrypt and sign message */
	else if (options.gpg_crypt)
		snprintf(tmp,sizeof(tmp),"gpg --encrypt --yes --default-key %s 
			    -u %s --sign -a -t -r %s -o /tmp/emailgpg.txt %s", 
			options.gpg_default_key,options.gpg_default_key,
			options.addr_to, filename);

/* if gpg failed (for example no public or secret key, bad passphrase etc.) */
	if ((err=system(tmp))!=0) {

		snprintf(tmp,sizeof(tmp),"mv %s ~/.restore",filename);
		system(tmp);

		if (options.debug)
			printf("\e[31mgpg error code: %d\e[0m\n",err);
		if (options.verbose) {
			printf("\e[31mmessage is saved as ~/.restore\n");
			printf("Run email with -R if u want to restore message.\n");
			printf(".. or just ./email <params> < ~/.restore\e[0m\n");
		}
		return -1;
	}
	else {
		snprintf(tmp,sizeof(tmp),"rm %s",filename);
		system(tmp);	

		*filename="/tmp/emailgpg.txt";
		return 0;
	}
}


