OpenVMS Source-Code Demos

SFF_DEMO_C

//================================================================================================
// title  : SFF_DEMO_c_101.c
// author : Neil Rieck	( http://neilrieck.net )
// created: 2014/11/19
// build  : $cc  sff_demo_c
//	    $link sff_demo_c		, -
//		sys$input/opt
//		sys$share:tcpip$smtp_mailshr.exe/share
// notes  :
// 1) tcpip_send_from_file() only works with "TCPIP Services for OpenVMS"
// 2) contents of my static test file "sff_demo.txt" ------------------------------------
//	MAIL FROM:<catbert@dilbert.com>			<--- no w/s after the colon
//	RCPT TO:<neil.rieck@bell.ca>			<--- no w/s after the colon
//	DATA
//	Date: Wed, 19 Nov 2014 14:48:14 -0400		<<--- should use the current UTC
//	Message-Id: <96080414481486@dilbert.com>	<<--- change to avoid some spam tests
//	From: catbert@dilbert.com (Funny Stuff)
//	To: neil.rieck@bell.ca
//	Subject: Test of SFF mechanism
//							<<--- blank line is recommended
//	This is the message text. Whatever.		<<--- body
//		----------------------------------------------------------------------------------
// 3) tcpip_send_from_file() requires that your data begins immediately after the colon in fields
//	"MAIL FROM:" and "RCPT TO:". No spaces or tabs. This is an oddity of tcpip_send_from_file
//	because you can use white-space when connecting directly to port 25
// 4) "Message-Id:" is not mandatory but some anti-SPAM detectors require it. It must be unique
//	because:
//	4a. some pop3 servers will throw away duplicate mail based upon "Message-Id:"
//	4b. some smtp clients will remember that you deleted a message with "Message-Id:" then
//	    will auto-delete new mail containing the same "Message-Id:" (Office Outlook does this)
// 5) "Date" is not mandatory but some anti-SPAM detectors require it
//	5a. This is nice to have since SMTP messages can be delayed by up to 4 hours
//	5b. some anti-SPAM detectors consider old stamps (which go to the bottom of a smtp message
//		window) as suspicious.
//	5c. some anti-SPAM detectors consider future stamps (which go to the top of a smtp message
//		window) as suspicious.
// history:
// ver who when     what
// --- --- -------- ------------------------------------------------------------------------------
// 100 NSR 20141119 1. original effort (only works with a static file)
// 101 NSR 20141120 1. added code to create a unique file, timestamp, and message-id
//================================================================================================
//
//	external references
//
#include <stdlib.h>								//
#include <stdio.h>								//
#include <time.h>								//
#include <errno.h>								//
#include <locale.h>								//
#define __NEW_STARLET	1							// enable new (strict) starlet (OpenVMS Alpha 7.0)
#include <starlet.h>								// vms specific - system library
#include <ssdef.h>								// vms specific - system services constants
#include <sjcdef.h>								// vms specific - system job control
#include <jbcmsgdef.h>								// vms specific - job control message
#include <iosbdef.h>								// vms specific - i/o status block
#include <iledef.h>								// vms specific - Item List Entry 3 structure
//
//	prototypes
//
long tcpip$smtp_send_from_file(char*, char*, long);				// found in "sys$share:tcpip$smtp_mailshr.exe"
void get_datetime_str(char*);							//
void get_datetime_smtp(char*);							//
//
//	variables
//
char	fs1[99];
char	fs2[99];
char	temp[99];
char	dtstamp[40];
long	debug;
long	rc;
FILE	*file1 = 0;
extern	int errno;								// needed for "fprintf(stderr"
//

//==============================================================================
//	main
//==============================================================================
void main(int argc, char *argv[]) {
	printf("-i-pgm: %s\n",argv[0]);						//
	get_datetime_smtp(dtstamp);						// get current date-time for SMTP
	get_datetime_str(temp);							// get current date-time for fname
	sprintf(fs1,"%s%s%s","smtp-scratch-",temp,".txt");			// build fname string
	//
	printf("-i-creating file: %s\n",fs1);
	file1 = fopen(fs1,"w", "rfm=var","rat=cr", "mrs=132");			// rec-fmt: variable rec-att: cr
	if (file1==NULL) {							//
	    perror("-e-The following error occurred");				// this function tacks on human-readable text
	    fprintf(stderr,"-e-error: %d opening %s\n",errno,fs1);		// this function provides the error number
	    exit (2);								// vms-e-
	}else{									//
	    printf("-i-writing to file: %s\n",fs1);
	    fprintf(file1,"MAIL FROM:<catbert@dilbert.com>\n"		);
	    fprintf(file1,"RCPT TO:<neil.rieck@bell.ca>\n"		);
	    fprintf(file1,"DATA\n"					);
	    fprintf(file1,"Date: %s\n", dtstamp				);	//
	    fprintf(file1,"Message-Id: <%s@dilbert.com>\n",temp		);
	    fprintf(file1,"From: catbert@dilbert.com (Funny Stuff)\n"	);
	    fprintf(file1,"To: neil.rieck@bell.ca\n"			);
	    fprintf(file1,"Subject: Test of SFF mechanism\n"		);
	    fprintf(file1,"\n"						);	// new rules say we need a blank line
	    fprintf(file1,"Test of SFF mechanism.\n\n"			);
	    printf("-i-closing file: %s\n",fs1);
	    fclose(file1);							// flush and close
	}									//
	printf("-i-invoking: tcpip$smtp_send_from_file\n");
	fs2[0] = '\0';								// init
	debug = 0;								//
	rc = tcpip$smtp_send_from_file(fs1,fs2,debug);				//
	printf("status: %ld\n",rc);						// display return code
	printf("-i-deleting file: %s\n",fs1);					//
	rc = remove(fs1);							//
	if (rc!=0) {								//
	    perror("-e-The following error occurred");				// this function tacks on human-readable text
	    fprintf(stderr,"-e-error: %d deleting %s\n",errno,fs1);		// this function provides the error number
	    exit (2);								// vms-e-
	}
	printf("-i-exiting: %s\n",argv[0]);					//
	exit(1);								// vms-s-
}

//==============================================================================
//	get_time_str
//	note: date + time are concatinated to produce a unique file name
//==============================================================================
void get_datetime_str(char *buffer) {
	struct	timeb	timebuffer;						// for ftime()
	struct	tm	*time_fields;						// for localtime()
	char		millisecs[5];						//
	char		my_date_time[30];					//
	//
	ftime( &timebuffer );							// record current system time
	sprintf(millisecs, "%03hu", timebuffer.millitm);			// extract milliseconds as three chars
	time_fields = localtime( &timebuffer.time );				// breakout other time fields
	strftime(	my_date_time,						// yields: ccyymmddhhmmss
			sizeof(my_date_time),					//
			"%Y%m%d%H%M%S",						//
			time_fields );						//
	sprintf(	buffer,							// yields: ccyymmddhhmmssxxx
			"%s%s",							//
			my_date_time,						// ccyymmddhhmmss
			millisecs);						// xxx
}
//==============================================================================
//	get_time_smtp
//	note: produces a date-time stamp suitable for use with SMTP
//==============================================================================
void get_datetime_smtp(char *buffer) {
	struct	timeb	timebuffer;						// for ftime()
	struct	tm	*time_fields;						// for localtime()
	char		my_date_time[30];					//
	long temp_mins, temp_hours;						//
	//
	//	here we want to return an SMTP date string like this:
	//		Wed, 19 Nov 2014 14:48:14 -0500
	//	see: RFC-5322, ISO-8601
	//
	ftime( &timebuffer );							// record current system time (snapshot)
	setlocale(LC_ALL, "C");							//
	time_fields = localtime( &timebuffer.time );				// breakout other time fields
	//
	//	notes:
	//	1) in 2014-11-xx "HP C V7.3-010 on OpenVMS Alpha V8.4" still does not support "%z" but does support "%Z"
	//	2) divide by 36 only returns the correct result for so-called whole time zones. It does not return the
	//	   correct result for half-shifted timezones like NST (Newfoundland Standard Time). See: ISO-8601
	//
	strftime(	my_date_time,						//
			sizeof(my_date_time),					//
			"%a, %d %b %Y %H:%M:%S",				// day dnum mon ccyy hh:mm:ss
			time_fields );						//
#if 1										// this is the preferred universal solution
	temp_mins  = (time_fields->tm_gmtoff/60);				// secs->mins  (could be negative)
	temp_hours =    (temp_mins / 60);					// mins->hours (could be negative)
	temp_mins  = abs(temp_mins % 60);					// remainder   (positive)
	strftime(	temp,							//
			sizeof(temp),						//
			"%a, %d %b %Y %H:%M:%S",				// day dnum mon ccyy hh:mm:ss
			time_fields );						//
	sprintf(buffer,	"%s %+03d%02d", temp, temp_hours, temp_mins);		// tack offset onto date-time
#else										// this common hack is sometimes wrong
	//
	//	notes:	time_fields->tm_gmtoff);	yields: -18000 for EST
	//		time_fields->tm_gmtoff/36);	yields: -500   for EST (yields wrong answer for NST/NDT)
	//
	sprintf(buffer,	"%s %+05d", my_date_time, time_fields->tm_gmtoff/36);	// tack offset onto date-time
#endif										//
}