h30450
s 00009/00002/00451
d D 1.9 95/07/13 12:40:09 tim 9 8
c added code to support differing versions of the pop-3 implementation which seem to have different greeting formats with the message count in differing places
e
s 00004/00002/00449
d D 1.8 95/07/06 19:55:20 tim 8 7
c user the getpass command to get the users password as it is secure
e
s 00040/00011/00411
d D 1.7 95/07/06 14:53:45 tim 7 6
c modified talk to pop to operate on the config files
e
s 00001/00034/00421
d D 1.6 95/07/06 13:42:18 tim 6 5
c the #defines ar now in a seperate file
e
s 00001/00001/00454
d D 1.5 95/07/05 20:10:50 tim 5 4
c now deletes outgoing email files as well
e
s 00001/00001/00454
d D 1.4 95/07/05 11:43:15 tim 4 3
c added a newline print in one command
e
s 00235/00011/00220
d D 1.3 95/07/04 19:40:36 tim 3 2
c it seems to work now
e
s 00231/00000/00000
d D 1.2 95/07/03 13:04:08 tim 2 1
c the thing works in principle. it can connect, authenticate list and quit. 
c it will need defaults handling installing
e
s 00000/00000/00000
d D 1.1 95/07/01 16:19:59 tim 1 0
c 
e
u
U
f e 0
t
T
I 2
/* this program talks to pop3 servers */
/* (C) Tim Graves, Sun Microsystems 1995 */
I 6
#include "talktopop.h"
E 6
#include <netinet/in.h>
#include <sys/uio.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <stdio.h>
I 3
#include <stdlib.h>
E 3
D 6
#define POPNAME "pop-3"
#define PROTNAME "tcp"
#define POPSERVER "astir"

#define USERCMD "USER"
#define PASSCMD "PASS"
#define LISTCMD "LIST"
I 3
#define RETRCMD "RETR"
#define DELECMD "DELE"
E 3
#define QUITCMD "QUIT"
#define READY "."
#define BADCHAR '-'
#define OKCHAR '+'

/* standard defines */
#define TRUE 1
#define FALSE 0

/* error codes */
#define NETERR -1
#define NETOK -2
#define GOODCMD 1
#define BADCMD 0
I 3
#define BADFILE BADCMD
E 3

/* sizes and stuff */
#define MAXLINE 1024
I 3
#define MAXSIZE 2048 /* max size of an email to get */
#define MAXCOUNT 10 /* get 10 emails */
#define FIRST 1 
#define LAST  -1
E 3

I 3
/* skeleton for generating file names */
D 5
#define INSKELETON "in%d.msg"
E 5
I 5
#define INSKELETON "in%03d.msg"
E 6
E 5

E 3
/* info on the port we are dealing with etc */
struct sockaddr_in sin ;
int net ;
FILE *netinfd, *netoutfd ;

I 3
/* various strings etc that are set or reset from the command line */
char popserver[100] ;
char popname [100] ;
char protname [100] ;
char uname[100] ;
char passwd[100] ;
int filestartno = 0 ;
int dodel = FALSE ;
D 7
int direction = FIRST ;
E 7
I 7
int direction = TRUE ;
E 7
int verbose = FALSE ;
int maxsize = MAXSIZE ;
int maxemail = MAXCOUNT ;
I 7
int hnset, pwset, unset ;
E 7


E 3
/* message information */
int messcnt = 0 ;

/* debug information */
D 3
int debug = TRUE ;
E 3
I 3
int debug = FALSE ;
/* pointer to the message size array */
int * msglist ;
E 3

main(argc, argv, environ)
int argc ;
char * argv[] ;
char * environ[] ;
{
	int ret ;
I 3
	int startno ;
	init() ;
	/* handle the command line */
	cmdline(argc, argv) ;
	if (verbose == TRUE)
		dogreet() ;
I 7
	/* check that the arguments have worked out right and
	   get the password if required */
	checkflags() ;
	/* maxemail can viable be zero if there are more saved emails than
	   the default value of maxemail. in this case just crunch out */
	if (maxemail == 0)
		exit(0) ;
E 7
E 3
	/* setup the network connection */
	if (startnet() == NETERR)
	{
		printf("Network initialisation error\nExiting\n") ;
		return ;
	}
	ret = doconnect() ;
	if (ret == BADCMD)
	{
		printf("Pop server returns bad\n") ;
		return ;
	}
	ret = dologin() ;
	if (ret == GOODCMD)
	{
		dolist() ;
I 3
D 7
		if (direction == LAST)
E 7
I 7
		if (direction == FALSE)
E 7
			startno = findfromlast() ;
		else
			startno = 1 ;
		doget(startno) ;
E 3
	}
	doquit() ;
	endnet() ;
}
I 7
checkflags() 
{
	char tmp[1024] ;
I 8
	char * pass ;
E 8
	/* ensure that we have a host name and user name */
	if (hnset == FALSE)
	{
		printf("Error: talktopop cannot find your pop server name in the config file or the command list") ;
		exit(1) ;
	}
	if (pwset == FALSE)
	{
D 8
		sprintf(tmp, "the password for user %s on machine %s", uname, popserver) ;
		getstr(passwd, tmp, TRUE) ;
E 8
I 8
		sprintf(tmp, "Please enter the password for user %s on machine %s:", uname, popserver) ;
		pass = getpass(tmp) ;
		strcpy(passwd, pass) ;
E 8
	}
}
E 7
I 3
init()
{
D 7
	strcpy(popname, POPNAME) ;
	strcpy(protname, PROTNAME) ;
	strcpy(popserver, POPSERVER) ;
E 7
I 7
	confinit() ;
	getuname(uname) ;
	unset = TRUE ;
	readconf() ;
E 7
}
cmdline(argc, argv) 
int argc ;
char * argv[] ;
{
	int c ;
	extern int optind ;
	extern char * optarg ;
D 7
	int hnset = FALSE, unset = FALSE, pwset = FALSE ;
E 7
I 7
	int cset = FALSE ;
E 7
	while ((c = getopt(argc, argv, "h:u:p:c:s:flvdS:n:")) != EOF)
	{
		switch(c)
		{
			case 'h': /* hostname for pop server */
				strcpy(popserver, optarg) ;
				hnset = TRUE ;
				break ;
			case 'u': /* username to use */
				strcpy(uname, optarg) ;
				unset = TRUE ;
				break ;
			case 'p': /* password to use */
				strcpy(passwd, optarg) ;
				pwset = TRUE ;
				break ;
			case 'c': /* max number of emails to retrieve */
				maxemail = atoi(optarg) ;
I 7
				cset = TRUE ;
E 7
				break ;
			case 's': /* max size of email to retrieve */
				maxsize = atoi(optarg) ;
				break ;
			case 'f': /* retrieve maxemails from the start of the email box */
D 7
				direction = FIRST ;
E 7
I 7
				direction = TRUE ;
E 7
				break ;
			case 'l': /* retrieve maxemails from the end of the email box */
D 7
				direction = LAST ;
E 7
I 7
				direction = FALSE ;
E 7
				break ;
			case 'v': /* be verbose for debugging */
				verbose = TRUE ;
				break ;
			case 'S': /* service name (defaults to pop-3) */
				strcpy(popname, optarg) ;
				break ;
			case 'n': /* number to start the file count at */
				filestartno = atoi(optarg) ;
				break ;
			case 'd': /* delete the messages once rtead */
				dodel = TRUE ;
				break ;
		}
	}
D 7
	if ((hnset == FALSE) || (unset == FALSE) || (pwset == FALSE))
E 7
I 7
	/* calculate the number of emails to actually retrieve. 
	   this is maxemail - the findnumber.
	   if the count has been specificaly set dont do this */
	if (cset == FALSE)
E 7
	{
D 7
		fprintf(stderr, "Usage:  talktopop -h <popserver name> -u <your username> -p <your password> [-c <max number of emails to retrieve>] [-s <max size of email to retrieve in bytes>] [-f] [-l] [-v] [-S <pop name in services file, defaults to pop-3>] [-n <start number>]") ;
		exit(1) ;
E 7
I 7
		maxemail = maxemail - filestartno ;
		if (maxemail < 0)
			maxemail = 0 ;
E 7
	}
}
dogreet()
{
	printf("Talktopop, Version %I%\n(C) Tim Graves, Sun Microsystems 1995\n") ;
}
E 3
doquit()
{
	char tmp[MAXLINE] ;
	sprintf(tmp, "%s", QUITCMD) ;
	tonet(tmp) ;
	fromnet(tmp) ;
I 3
	free(msglist) ;
E 3
}
I 3
dodelete(i)
int i ;
{
	char tmp[MAXLINE] ;
	if (verbose == TRUE)
		printf("Deleting mail no %i\n", i) ;
	sprintf(tmp, "%s %d", DELECMD, i) ;
	tonet(tmp) ;
	fromnet(tmp) ;
	if (tmp[0] == BADCHAR)
		return(BADCMD) ;
	else
		return(GOODCMD) ;
}

doretrieve(i, fname)
int i ;
char * fname ;
{
	FILE *fd ;
	char tmp[MAXLINE] ;
	if (verbose == TRUE)
D 4
		printf("Retrieving mail no %d to file %s", i, fname) ;
E 4
I 4
		printf("Retrieving mail no %d to file %s\n", i, fname) ;
E 4
	if ((fd = fopen(fname, "w")) == NULL)
	{
		return(BADFILE) ;
	}
	sprintf(tmp, "%s %d", RETRCMD, i) ;
	tonet(tmp) ;
	/* do the first line, if there is a - there its an error */
	fromnet(tmp) ;
	if (tmp[0] == BADCHAR)
	{
		fclose(fd) ;
		return(BADCMD) ;
	}
	/* the line is OK, do the message */
	while(fromnet(tmp) == TRUE)
	{
		/* NOTE fromnet removes andy \r o \n stuff, we need to 
		   reinsert this into the file */
		fprintf(fd, "%s\n", tmp) ;
	}
	fclose(fd) ;
	return(GOODCMD) ;
}
int findfromlast()
{
	/* counting backwards from the last entry in msglist count 
	   maxemails which are <= maxsize back and return the number of 
	   the last one */
	int i, count ;
	count = 0 ;
	for ( i = messcnt ; ((i >= 0 ) && ( count < maxemail)) ; i--)
		if (msglist[i] <= maxsize)
			count ++ ;
	return(i) ;
}
/* get maxcount emails from the pop server and save then away */
doget(startno)
int startno ;
{
	int i, count;
	char tmp[100] ;
	if (verbose == TRUE)
		printf("Retrieving emails from %d\n", startno) ;
	count = 0 ;
	for (i = startno ; ((count < maxemail) && (i <= messcnt)) ; i ++)
	{
		/* is the email the right size ? */
		if (msglist[i] > maxsize)
			continue ;
		/* OK we should be looking at this email */
		/* create the filename */
		sprintf(tmp, INSKELETON, count+filestartno) ;
		/* get the file */
		doretrieve(i, tmp) ;
		/* are we deleting retrieved messages ? */
		if (dodel == TRUE)
			dodelete(i) ;
		/* increment the count */
		count ++ ;
	}
}
E 3
dolist()
{
	char tmp [MAXLINE] ;
D 3
	int i ;
	for (i = 1 ; i <= messcnt ; i ++)
E 3
I 3
	int i, mailno, mailsize ;
	if (verbose == TRUE)
		printf("Listing email size\n") ;
	for (i = 1 ; i <= messcnt; i ++) 
E 3
	{
		sprintf(tmp, "%s %d", LISTCMD, i) ;
		tonet(tmp) ;
		fromnet(tmp) ;
D 3
		if (tmp[0] == BADCMD)
E 3
I 3
		if (tmp[0] == BADCHAR) /* this could get things out of sync but its usefull for debugging */
E 3
		{
			return(BADCMD) ;
		}
I 3
		/* skip over the "+OK " */
		sscanf(&tmp[4],"%d %d", &mailno, &mailsize) ;
		if (mailno > messcnt)
			return(BADCMD) ;
		if (mailno != i)
			return(BADCMD) ;
		msglist[i] = mailsize ;
E 3
	}
	return(GOODCMD) ;
}
doconnect()
{
	char tmp[MAXLINE] ;
I 3
	if (verbose == TRUE)
		printf("Connecting to pop server\n") ;
E 3
	fromnet(tmp) ;
	if (tmp[0] == BADCHAR)
		return(BADCMD) ;
	else
		return(GOODCMD) ;
}
int dologin()
{
	char tmp[MAXLINE] ;
I 9
	size_t i ;
E 9
I 3
	if (verbose == TRUE)
		printf("Logging in to pop server\n") ;
E 3
	/* request the user name */
D 3
	sprintf(tmp, "%s %s", USERCMD, UNAME) ;
E 3
I 3
	sprintf(tmp, "%s %s", USERCMD, uname) ;
E 3
	tonet(tmp) ;
	fromnet(tmp) ;
	if (tmp[0] == BADCHAR)
	{
		return(BADCMD) ;
	}
D 3
	sprintf(tmp, "%s %s", PASSCMD, PASSWORD) ;
E 3
I 3
	sprintf(tmp, "%s %s", PASSCMD, passwd) ;
E 3
	tonet(tmp) ;
	fromnet(tmp) ;
	if (tmp[0] == BADCHAR)
	{
		return(BADCMD) ;
	}
	/* the format is something like 
	+OK 360 messages ready for tim in /usr/spool/mail/tim
D 9
	extract the number */
	messcnt = atoi(&tmp[4]) ;
E 9
I 9
	for a strict pop3 implementation but some are like
	+OK tim has 77 message(s) (709547 octets).
	find the first number and then extract it (assume the first
	number is the message count  */
	for (i = 0 ; i < strlen(tmp) ; i ++)
		if (isdigit(tmp[i]))
			break ;
	messcnt = atoi(&tmp[i]) ;
E 9
I 3
	msglist = calloc((size_t) messcnt + 1, (size_t) sizeof(int) );
E 3
	return(GOODCMD) ;
}
endnet()
{
	fclose(netinfd) ;
	fclose(netoutfd) ;
	close(net) ;
}
int startnet()
{
	/* hard coded at the moment, connect, list and quit */
	struct servent * sp ;
	struct hostent *hp ;
	int ret ;
	
	/* get the port information etc for pop */
D 3
	if ((sp = getservbyname(POPNAME, PROTNAME)) == NULL)
E 3
I 3
	if ((sp = getservbyname(popname, protname)) == NULL)
E 3
	{
D 3
		printf("%s/%s: unknown service\n", POPNAME, PROTNAME) ;
E 3
I 3
		printf("%s/%s: unknown service\n", popname, protname) ;
E 3
		return(NETERR) ;
	}
D 3
	if ((hp = gethostbyname(POPSERVER)) == NULL)
E 3
I 3
	if ((hp = gethostbyname(popserver)) == NULL)
E 3
	{
D 3
		printf("%s: unknown hostname\n", POPSERVER) ;
E 3
I 3
		printf("%s: unknown hostname\n", popserver) ;
E 3
		return(NETERR) ;
	}
	/* build up the sin structure */
	sin.sin_family = (short) AF_INET ;
	memcpy((char *) &sin.sin_addr, (char *) hp->h_addr, hp->h_length) ;
	sin.sin_port = htons((u_short) sp->s_port) ;
	/* create the socket */
	net = socket(AF_INET, SOCK_STREAM,0) ;
	if (net < 0)
	{
		printf("socket error\n") ;
		return(NETERR);
	}
	ret = connect(net, (struct sockaddr *) &sin, sizeof(sin)) ;
	if (ret < 0)
	{
		printf("connect error\n") ;
		return(NETERR) ;
	}
	netinfd = fdopen(net,"r") ;
	netoutfd = fdopen(net, "w") ;
	if ((netinfd == NULL) || (netoutfd == NULL))
	{
		printf("fdopen error\n") ;
		if (netinfd != NULL)
			fclose(netinfd) ;
		if (netoutfd != NULL) 
			fclose(netoutfd) ;
		close(net) ;
		return(NETERR) ;
	}
	return(NETOK) ;
}

tonet(str)
char * str ;
{
	int len ;
	int ret ;
	char tmp[MAXLINE] ;
	sprintf(tmp, "%s\r\n", str) ;
	len = strlen(tmp) ;
	if (debug == TRUE)
		printf("Sending :%slength %d\n", tmp, len) ;
	ret = fprintf(netoutfd, "%s", tmp) ;
	fflush(netoutfd) ;
	if (ret == -1)
	{
		printf("error in send\n") ;
	}
	return ;
}
D 3
fromnet(str)
E 3
I 3
int fromnet(str)
E 3
char * str ;
{
	char tmp[MAXLINE] ;
	char ch ;
	int i, ret ;
	
	ch = '\0' ;
	i = 0 ;
	while (ch != '\n')
	{
		ch = getc(netinfd) ;
		if (ch == EOF)
		{
			printf("recv error") ;
			tmp[i] = '\0' ;
			return ;
		}
		tmp[i] = ch ;
		if (debug == TRUE)
			printf("%c", ch );
		i ++ ;
	}
	/* to get here we must have  tmp[i-1] == '\n' so remove it */
	tmp[i-1] = '\0' ;
	/* if tmp[i-2] is '\r' remove it */
	if (tmp[i-2] == '\r') 
		tmp[i-2] = '\0' ;
I 3
	/* if the first char is . and the length is greater than 1 remove the first .
	   (pop3 replaces .<text> with ..<text> and also uses . by itself to mean
	   end of the multi line message */
	if (tmp[0] == '.') 
	{
		if (tmp[1] == '\0')
		{
			/* end of multi line stuff */
			/* empty the return string */
			str[0] = '\0' ;
			return (FALSE) ;
		}
		else
		{
			/* the origional text started with a . remove the padding */
			strcpy(str, &tmp[1]) ;
			return(TRUE) ;
		}
	}
	/* no special handling required */
E 3
	strcpy(str, tmp) ;
I 3
	return(TRUE) ;
E 3
}
E 2
I 1
E 1
