Files
CoreGrade/coregrade/src/mailsend/utils.c
T
2020-01-11 09:50:39 -05:00

647 lines
14 KiB
C

/*
** various utility routines for mailsend
**
**
** Development History:
** who when why
** muquit@muquit.com Mar-26-2001 first cut
*/
#include "mailsend.h"
#define DAY_MIN (24 * HOUR_MIN) /* minutes in a day */
#define HOUR_MIN 60 /* minutes in an hour */
#define MIN_SEC 60 /* seconds in a minute */
/*
** returns a positive number if the file descriptor is connected to
** console. 0 if not
*/
int isInConsole(int fd)
{
int
rc;
rc=_isatty(fd);
return (rc);
}
int isInteractive(void)
{
#ifdef WINNT
if (isInConsole(_fileno(stdout)))
return(1);
else
return(0);
#else
if (isatty(fileno(stdout)))
return(1);
else
return(0);
#endif /* ! WINNT */
return(0);
}
/*
** duplicate a string. exits on failure
*/
char *xStrdup (char *string)
{
char
*tmp;
if (string == (char *) NULL || *string == '\0')
return ((char *) NULL);
/* malloc twice as much memory. */
tmp = (char *) malloc ((int) strlen(string)+1);
if (tmp == (char *) NULL)
{
(void) fprintf(stderr,"Error: mystrdup(): memory allocation problem\n");
exit(0);
}
/* it's safe to copy this way */
(void) strcpy(tmp, string);
return (tmp);
}
void errorMsg(char *format,...)
{
va_list
args;
va_start(args,format);
(void) fprintf (stderr,"Error: ");
vfprintf(stderr,format,args);
(void) fprintf(stderr,"\n");
va_end(args);
}
void showVerbose(char *format,...)
{
va_list
args;
if (g_quiet)
return;
if (g_verbose == 1)
{
va_start(args,format);
vfprintf(stdout,format,args);
(void) fflush(stdout);
va_end(args);
}
}
void print_info(char *format,...)
{
va_list
args;
if (g_quiet)
return;
va_start(args,format);
vfprintf(stdout,format,args);
(void) fflush(stdout);
va_end(args);
}
/*
** Initialize TheMail structure. returns pointer to the TheMail sturcture on
** success, NULL on failure.
*/
TheMail *newTheMail(void)
{
TheMail
*the_mail=(TheMail *) NULL;
the_mail=(TheMail *) malloc(sizeof(TheMail));
if (the_mail == (TheMail *) NULL)
{
(void) fprintf(stderr,"initTheMail(): malloc failed\n");
return ((TheMail *) NULL);
}
memset(the_mail,0,sizeof(TheMail));
return (the_mail);
}
Address *newAddress(void)
{
Address
*a;
a=(Address *) malloc(sizeof(Address));
if (a == (Address *) NULL)
{
(void) fprintf(stderr," newAddress() malloc failed\n");
}
memset(a,0,sizeof(Address));
return (a);
}
int validateMusts(char *from,char *to,char *smtp_server,char *helo_domain)
{
int
err=0;
if (getAddressList() == NULL)
{
if (to == (char *) NULL)
{
errorMsg("No To address/es specified. Speicfy with: -t to,to,..");
err++;
}
}
if (from == (char *) NULL)
{
errorMsg("No From address specified. Specify with: -f");
err++;
}
if (smtp_server == (char *) NULL)
{
errorMsg("No SMTP server address or IP specified. Specify with -smtp");
err++;
}
if (helo_domain == (char *) NULL)
{
errorMsg("No domain specified. Specify with: -d");
err++;
}
if (err)
return (-1);
return (0);
}
char *askFor(char *buf,int buflen,char *label,int ask)
{
if (label == NULL || *label == '\0')
return (NULL);
again:
if (isInConsole(_fileno(stdin)))
{
(void) fprintf(stdout,label);
(void) fflush(stdout);
(void) fflush(stderr);
}
(void) fgets(buf,buflen,stdin);
if (*buf == '\0' || *buf == '\n')
{
if (ask == EMPTY_NOT_OK)
goto again;
}
mutilsChopNL(buf);
return (buf);
}
/*
** remote mailto from To
** remote mailto: from the address
** mailto:foo => foo
** mailto: => mailto:
** mailto:x => "x"
** mailto: x => " x"
*/
char *fix_to(char *to)
{
char
*p=to;
if (to == NULL || *to == '\0')
return(p);
/* mailto:foo */
if (strlen(to) > 7 && mutilsStrncasecmp(to,"mailto:",7) == 0)
{
p=to;
p=p+7;
return(p);
}
return(p);
}
/**
* return filepath and mime_type
* @param str String of the form "file,mime_type"
* example: "c:\usr\local\foo.txt,text/plain"
* "/usr/local/foo.html,text/html"
* @param filepath returns
* @param fp_size size of the buffer filepath
* @param mime_type returns
* @param mt_size size of the buffer mime_type
*
* @return 0 on success, -1 otherwise
*
* Note: it will not expand "~ $" etc in the filepath
* This function is used when a body is attached with -m flag
*
*/
int get_filepath_mimetype(char *str,char *filepath,int fp_size,char *mype_type,int mt_size)
{
int
rc=0;
char
*fp,
*mt;
if ((mt=strchr(str,ATTACHMENT_SEP)))
{
*mt++='\0';
}
else
{
errorMsg("Could not determine mime-type from input: %s\n",str);
return(-1);
}
mutilsSafeStrcpy(mype_type,mt,mt_size);
/* get the filepath out */
fp=str;
mutilsSafeStrcpy(filepath,fp,fp_size);
return(rc);
}
void initialize_openssl(char *cipher)
{
#ifdef HAVE_OPENSSL
static const char rnd_seed[]="my huge entropy for rng.. blah";
SSL_CTX *ssl_ctx=(SSL_CTX *) NULL;
SSL *ssl=NULL;
SSL_library_init();
SSL_load_error_strings();
RAND_seed(rnd_seed,sizeof(rnd_seed));
ssl_ctx=SSL_CTX_new(SSLv23_client_method());
if (ssl_ctx == NULL)
{
errorMsg("Could not create SSL context\n");
return;
}
if (cipher)
{
if (!SSL_CTX_set_cipher_list(ssl_ctx,cipher))
{
errorMsg("Could not set cipher list %s\n",cipher);
return;
}
}
ssl=SSL_new(ssl_ctx);
if (ssl == NULL)
{
errorMsg("SSL_new() failed\n");
return;
}
/* set ssl to msock's static */
msock_set_ssl(ssl);
#endif /* HAVE_OPENSSL */
}
/**
* Calculate Date for RFC822 Date: header
*
* @param when time since unix epoch
* @param datebuf returns NULL terminated date string.
* Example: Wed, 17 May 2006 13:55:35 -0400
* @param bufsiz size of buffer dates. It's callers responsibily to make sure
* datebuf contains enough space. It must be at least 64 bytes.
* Usually it'll contain 31 bytes.
*
* returns 0 on success, -1 on failure
*
* The code is adapted from postfix.
*
* Changes I made:
* - I use fixed size buffers instead of dynamic ones.
* - I don't use %e to calcuate day of the week, instead I use %d. Windows strftime
* does not have %e.
* - I don't add timzone name.
*
* Reference: http://cr.yp.to/immhf/date.html
*/
int rfc822_date(time_t when,char *datebuf,int bufsiz)
{
char
ts1[32],
ts2[32];
struct tm
*lt;
struct tm
gmt;
int
gmtoff;
if (bufsiz < 64)
{
(void) fprintf(stderr,"buffer size of date must be > 31, it is %d\n",bufsiz);
return(-1);
}
memset(datebuf,0,bufsiz);
memset(ts1,0,sizeof(ts1));
memset(ts2,0,sizeof(ts2));
gmt=*gmtime(&when);
lt=localtime(&when);
gmtoff = (lt->tm_hour - gmt.tm_hour) * HOUR_MIN + lt->tm_min - gmt.tm_min;
if (lt->tm_year < gmt.tm_year)
gmtoff -= DAY_MIN;
else if (lt->tm_year > gmt.tm_year)
gmtoff += DAY_MIN;
else if (lt->tm_yday < gmt.tm_yday)
gmtoff -= DAY_MIN;
else if (lt->tm_yday > gmt.tm_yday)
gmtoff += DAY_MIN;
if (lt->tm_sec <= gmt.tm_sec - MIN_SEC)
gmtoff -= 1;
else if (lt->tm_sec >= gmt.tm_sec + MIN_SEC)
gmtoff += 1;
/*
** windows strftime does not have %e, so I'll use %d instead.
** day 1 - 9 can be written as 01 - 09
*/
(void) strftime(ts1,sizeof(ts1)-1,"%a, %d %b %Y %H:%M:%S ",lt);
if (gmtoff < -DAY_MIN || gmtoff > DAY_MIN)
{
(void) fprintf(stderr,"UTC time offset %d is larger than one day",gmtoff);
return(-1);
}
(void) snprintf(ts2,sizeof(ts2)-1,"%+03d%02d",
(int) (gmtoff / HOUR_MIN),(int) (abs(gmtoff) % HOUR_MIN));
/* put everything in the buffer. it's usually 31 bytes */
(void) snprintf(datebuf,bufsiz,"%s%s",ts1,ts2);
/* I will not add timezone name, it's not required */
return(0);
}
/*
** return 0 on success, -1 on failure
*/
int guess_file_type(char *file,unsigned int *flag)
{
char
buf[BUFSIZ];
int
i,
c,
cr_found=0,
nbytes,
buflen;
FILE
*fp=NULL;
*flag=0;
buflen=sizeof(buf)-1;
fp=fopen(file,"rb");
if (fp == NULL)
{
errorMsg("Could not open file %s for reading (%s)\n",
file,ERR_STR);
return(-1);
}
while(!feof(fp))
{
nbytes=fread(buf,1,buflen,fp);
if ((nbytes != buflen) && ferror(fp))
{
errorMsg("Read error in guess_file_type\n");
goto ExitProcessing;
}
for (i=0; i < nbytes; i++)
{
c=buf[i];
if (cr_found)
{
cr_found=0;
if (c == '\n')
continue;
}
if (c == '\r')
{
cr_found=1;
*flag |= FILE_TYPE_DOS;
}
else if (c == '\n')
{
*flag |= FILE_TYPE_UNIX;
}
else if ((c < 32 || c > 126) && c != '\t')
{
*flag |= FILE_TYPE_BINARY;
(void) fclose(fp);
return(0);
}
}
}
if (fp)
(void) fclose(fp);
return(0);
ExitProcessing:
if (fp)
(void) fclose(fp);
return(-1);
}
static int unix2dos(FILE *ifp,FILE *ofp)
{
int
n,
j,
k,
c,
c2;
char
buf2[BUFSIZ+1],
buf[BUFSIZ+1];
if (ifp == NULL || ofp == NULL)
return(-1);
#ifdef WINNT
_setmode(_fileno(ofp),_O_BINARY);
#endif /* WINNT */
while(!feof(ifp))
{
n=fread(buf,1,BUFSIZ,ifp);
(void) fprintf(stderr,"n=\"%d\"\n",n);
k=0;
for (j=0; j < n; j++)
{
c=buf[j];
if (c == '\n')
{
(void) fprintf(stderr,"j=%d\n",j);
buf2[k++]='\r';
}
buf2[k++]=c;
}
c2=fwrite(buf2,sizeof(char),k,ofp);
if (c2 != k)
{
perror("fwrite");
return(1);
}
}
}
#ifdef HAVE_OPENSSL
void print_cert_info(SSL *ssl)
{
X509
*cert=NULL;
X509_NAME
*sub=NULL,
*issuer=NULL;
char
buf[BUFSIZ];
if (!g_verbose)
return;
print_info("Cipher: %s\n",SSL_get_cipher(ssl));
(void) fprintf(stdout,"Certificate information:\n");
cert=SSL_get_peer_certificate(ssl);
if (cert == NULL)
{
errorMsg("No peer certificate found\n");
return;
}
/* get subject name */
sub=X509_get_subject_name(cert);
if (sub == NULL)
{
errorMsg("Could not get subject name in certificate\n");
}
X509_NAME_oneline(sub,buf,sizeof(buf)-1);
(void) fprintf(stdout,"Subject: %s\n",buf);
issuer=X509_get_issuer_name(cert);
if (issuer == NULL)
{
errorMsg("Could not get issuer name in certificate\n");
}
X509_NAME_oneline(issuer,buf,sizeof(buf)-1);
(void) fprintf(stdout,"Issuer: %s\n",buf);
(void) fflush(stdout);
}
/*
** params:
** challenge: base64 encoded challenge
** user username
** seceret password
**
** Returns: NULL terminated base64 encoded CRAM-MD5 material on success,
** NULL on failure
*/
char *encode_cram_md5(char *challenge,char *user,char *secret)
{
unsigned char
*data;
unsigned long
data_len;
unsigned char
hmac_md5[16];
HMAC_CTX
ctx;
const EVP_MD
*md5=NULL;
unsigned int
hmac_len;
int
i;
char
*b64;
unsigned long
b64len=0;
char
hex[33],
buf[BUFSIZ];
if (challenge == NULL || *challenge == '\0' ||
user == NULL || *user == '\0' ||
secret == NULL || *secret == '\0')
return(NULL);
OpenSSL_add_all_digests();
/* decode the challenge */
data=mutils_decode_base64(challenge,strlen(challenge),&data_len);
if (data == NULL)
{
errorMsg("Could not base64 decode CRAM-MD5 challenge: %s",challenge);
return(NULL);
}
/* take HMAC-MD5 of the challenge*/
md5=EVP_get_digestbyname("md5");
HMAC_CTX_init(&ctx);
HMAC_Init(&ctx,secret,strlen(secret),md5);
HMAC_Update(&ctx,data,data_len);
HMAC_Final(&ctx,hmac_md5,&hmac_len);
/* convert the digest to hex */
memset(hex,0,sizeof(hex));
for (i=0; i < 16; i++)
{
(void) sprintf(hex+2*i,"%02x",hmac_md5[i]);
}
(void) snprintf(buf,sizeof(buf)-1,"%s %s",user,hex);
/* base64 encode "user hex_digest" */
b64=mutils_encode_base64(buf,strlen(buf),&b64len);
if (b64len <= 0)
return(NULL);
/* mutils_encode_base64 adds CRLF */
if (b64len > 2)
b64[b64len-2]='\0';
return(b64);
}
#else
char *encode_cram_md5(char *challenge,char *user,char *secret)
{
errorMsg("Must be compiled with OpenSSL in order to get CRAM-MD5 support\n");
return(NULL);
}
#endif /* HAVE_OPENSSL */