#include #include #include #include /* generic socket DLL support */ #include "gensock.h" #ifdef WIN32 #define __far far #define huge far #define __near near #endif #define MAXOUTLINE 255 HANDLE gensock_lib = 0; int (FAR PASCAL *pgensock_connect) (char FAR * hostname, char FAR * service, socktag FAR * pst); int (FAR PASCAL *pgensock_getchar) (socktag st, int wait, char FAR * ch); int (FAR PASCAL *pgensock_put_data) (socktag st, char FAR * data, unsigned long length); int (FAR PASCAL *pgensock_close) (socktag st); int (FAR PASCAL *pgensock_gethostname) (char FAR * name, int namelen); int (FAR PASCAL *pgensock_put_data_buffered) (socktag st, char FAR * data, unsigned long length); int (FAR PASCAL *pgensock_put_data_flush) (socktag st); socktag SMTPSock; #define SERVER_SIZE 256 // #defines (bleah!) from Beverly Brown "beverly@datacube.com" #define SENDER_SIZE 256 char SMTPHost[SERVER_SIZE]; char Sender[SENDER_SIZE]; char *Recipients; char my_hostname[1024]; char *destination=""; char *cc_list=""; char *bcc_list=""; char *loginname=""; char *senderid=""; char *subject=""; int quiet=0; char *usage[]= { "Blat v1.4: WinNT console utility to mail a file via SMTP", "", "syntax:", "Blat -t [optional switches (see below)]", "Blat -install [-q]", "Blat -h [-q]", "", "-install : set's default SMTP server and sender", "", " : file with the message body ('-' for console input, end with ^Z)", "-t : recipient list (comma separated)", "-s : subject line", "-f : overrides the default sender address (must be known to server)", "-i : a 'From:' address, not necessarily known to the SMTP server.", "-c : carbon copy recipient list (comma separated)", "-b : blind carbon copy recipient list (comma separated)", "-h : displays this help.", "-q : supresses *all* output.", "-server : overrides the default SMTP server to be used.", "", "Note that if the '-i' option is used, is included in 'Reply-to:'", "and 'Sender:' fields in the header of the message." }; const NMLINES=22; void gensock_error (char * function, int retval) { if( ! quiet ) cout << "error " << retval << " in function '" << function; } // loads the GENSOCK DLL file int load_gensock() { if( (gensock_lib = LoadLibrary("gwinsock.dll")) == NULL ) { if( (gensock_lib = LoadLibrary("gensock.dll")) == NULL ) { if( ! quiet ) cout << "Couldn't load either 'GWINSOCK.DLL' or 'GENSOCK.DLL'\nInstall one of these in your path."; return -1; } } if( ( pgensock_connect = ( int (FAR PASCAL *)(char FAR *, char FAR *, socktag FAR *) ) GetProcAddress(gensock_lib, "gensock_connect") ) == NULL ) { if( ! quiet ) cout << "couldn't getprocaddress for gensock_connect\n"; return -1; } if ( ( pgensock_getchar = ( int (FAR PASCAL *) (socktag, int, char FAR *) ) GetProcAddress(gensock_lib, "gensock_getchar") ) == NULL ) { if( ! quiet ) cout << "couldn't getprocaddress for gensock_getchar\n"; return -1; } if( ( pgensock_put_data = ( int (FAR PASCAL *) (socktag, char FAR *, unsigned long) ) GetProcAddress(gensock_lib, "gensock_put_data") ) == NULL ) { if( ! quiet ) cout << "couldn't getprocaddress for gensock_put_data\n"; return -1; } if( ( pgensock_close = (int (FAR PASCAL *) (socktag) ) GetProcAddress(gensock_lib, "gensock_close") ) == NULL ) { if( ! quiet ) cout << "couldn't getprocaddress for gensock_close\n"; return -1; } if( ( pgensock_gethostname = (int (FAR PASCAL *) (char FAR *, int) ) GetProcAddress(gensock_lib, "gensock_gethostname") ) == NULL ) { if( ! quiet ) cout << "couldn't getprocaddress for gensock_gethostname\n"; return -1; } if( ( pgensock_put_data_buffered = ( int (FAR PASCAL *) (socktag, char FAR *, unsigned long) ) GetProcAddress(gensock_lib, "gensock_put_data_buffered") ) == NULL ) { if( ! quiet ) cout << "couldn't getprocaddress for gensock_put_data_buffered\n"; return -1; } if( ( pgensock_put_data_flush = ( int (FAR PASCAL *) (socktag) ) GetProcAddress(gensock_lib, "gensock_put_data_flush") ) == NULL ) { if( ! quiet ) cout << "couldn't getprocaddress for gensock_put_data_flush\n"; return -1; } return 0; } int open_smtp_socket( void ) { int retval; /* load the library if it's not loaded */ // if (!gensock_lib) if ( ( retval = load_gensock() ) ) return ( retval ); if ( (retval = (*pgensock_connect) ((LPSTR) SMTPHost, (LPSTR)"smtp", &SMTPSock))) { if (retval == ERR_CANT_RESOLVE_SERVICE) { if ((retval = (*pgensock_connect) ((LPSTR)SMTPHost, (LPSTR)"25", &SMTPSock))) { gensock_error ("gensock_connect", retval); return -1; } } // error other than can't resolve service else { gensock_error ("gensock_connect", retval); return -1; } } // we wait to do this until here because WINSOCK is // guaranteed to be already initialized at this point. // get the local hostname (needed by SMTP) if ((retval = (*pgensock_gethostname) (my_hostname, sizeof(my_hostname)))) { gensock_error ("gensock_gethostname", retval); return -1; } return 0; } int close_smtp_socket( void ) { int retval; if( (retval = (*pgensock_close) (SMTPSock)) ) { gensock_error ("gensock_close", retval); return -1; } FreeLibrary( gensock_lib ); return (0); } int get_smtp_line( void ) { char ch = '.'; char in_data [MAXOUTLINE]; char * index; int retval = 0; index = in_data; while (ch != '\n') { if( (retval = (*pgensock_getchar) (SMTPSock, 0, &ch) ) ) { gensock_error ("gensock_getchar", retval); return -1; } else { *index = ch; index++; } } /* this is to support multi-line responses, common with */ /* servers that speak ESMTP */ /* I know, I know, it's a hack 8^) */ if( in_data[3] == '-' ) return( get_smtp_line() ); else return atoi(in_data); } int put_smtp_line( socktag sock, char far * line, unsigned int nchars ) { int retval; if( (retval = (*pgensock_put_data) (sock, line, (unsigned long) nchars))) { gensock_error ("gensock_put_data", retval); return -1; } return (0); } int putline_internal (socktag sock, char * line, unsigned int nchars) { int retval; if ((retval = (*pgensock_put_data) (sock, (char FAR *) line, (unsigned long) nchars))) { switch (retval) { case ERR_NOT_CONNECTED: gensock_error( "SMTP server has closed the connection", retval ); break; default: gensock_error ("gensock_put_data", retval); } return -1; } return (0); } void smtp_error (char * message) { if( ! quiet ) cout << message << "\n"; put_smtp_line (SMTPSock, "QUIT\r\n", 6); close_smtp_socket(); } // 'destination' is the address the message is to be sent to // 'message' is a pointer to a null-terminated 'string' containing the // entire text of the message. int prepare_smtp_message(char * MailAddress, char * destination) { char out_data[MAXOUTLINE]; char str[1024]; char *ptr; int len, startLen; if ( open_smtp_socket() ) return -1; if ( get_smtp_line() != 220 ) { smtp_error ("SMTP server error"); return(-1); } sprintf( out_data, "HELO %s\r\n", my_hostname ); put_smtp_line( SMTPSock, out_data, strlen (out_data) ); if ( get_smtp_line() != 250 ) { smtp_error ("SMTP server error"); return -1; } sprintf (out_data, "MAIL From:<%s>\r\n", loginname); put_smtp_line( SMTPSock, out_data, strlen (out_data) ); if (get_smtp_line() != 250) { smtp_error ("The mail server doesn't like the sender name,\nhave you set your mail address correctly?"); return -1; } // do a series of RCPT lines for each name in address line for (ptr = destination; *ptr; ptr += len + 1) { // if there's only one token left, then len will = startLen, // and we'll iterate once only startLen = strlen (ptr); if ((len = strcspn (ptr, " ,\n\t\r")) != startLen) { ptr[len] = '\0'; // replace delim with NULL char while (strchr (" ,\n\t\r", ptr[len+1])) // eat white space ptr[len++] = '\0'; } sprintf (out_data, "RCPT To: <%s>\r\n", ptr); putline_internal( SMTPSock, out_data, strlen (out_data) ); if (get_smtp_line() != 250) { sprintf (str, "The mail server doesn't like the name %s.\nHave you set the 'To: ' field correctly?", ptr); smtp_error (str); return -1; } if (len == startLen) // last token, we're done break; } sprintf (out_data, "DATA\r\n"); put_smtp_line (SMTPSock, out_data, strlen (out_data)); if (get_smtp_line() != 354) { smtp_error ("Mail server error accepting message data"); return -1; } return(0); } int transform_and_send_edit_data( socktag sock, char * editptr ) { char *index; char *header_end; char previous_char = 'x'; unsigned int send_len; int retval; BOOL done = 0; send_len = lstrlen(editptr); index = editptr; header_end = strstr (editptr, "\r\n\r\n"); while (!done) { // room for extra char for double dot on end case while ((unsigned int) (index - editptr) < send_len) { switch (*index) { case '.': if (previous_char == '\n') /* send _two_ dots... */ if ((retval = (*pgensock_put_data_buffered) (sock, index, 1))) return (retval); if ((retval = (*pgensock_put_data_buffered) (sock, index, 1))) return (retval); break; case '\r': // watch for soft-breaks in the header, and ignore them if (index < header_end && (strncmp (index, "\r\r\n", 3) == 0)) index += 2; else if (previous_char != '\r') if ((retval = (*pgensock_put_data_buffered) (sock, index, 1))) return (retval); // soft line-break (see EM_FMTLINES), skip extra CR */ break; default: if ((retval = (*pgensock_put_data_buffered) (sock, index, 1))) return (retval); } previous_char = *index; index++; } if( (unsigned int) (index - editptr) == send_len) done = 1; } // this handles the case where the user doesn't end the last // line with a if (editptr[send_len-1] != '\n') { if ((retval = (*pgensock_put_data_buffered) (sock, "\r\n.\r\n", 5))) return (retval); } else if ((retval = (*pgensock_put_data_buffered) (sock, ".\r\n", 3))) return (retval); /* now make sure it's all sent... */ if ((retval = (*pgensock_put_data_flush)(sock))) return (retval); return (TRUE); } int send_smtp_edit_data (char * message) { transform_and_send_edit_data( SMTPSock, message ); if (get_smtp_line() != 250) { smtp_error ("Message not accepted by server"); return -1; } return(0); } int finish_smtp_message( void ) { return put_smtp_line( SMTPSock, "QUIT\r\n", 6 ); } // create a registry entries for this program int CreateRegEntry( void ) { HKEY hKey1; DWORD dwDisposition; LONG lRetCode; /* try to create the .INI file key */ lRetCode = RegCreateKeyEx ( HKEY_LOCAL_MACHINE, "SOFTWARE\\Public Domain\\Blat", 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE,NULL, &hKey1,&dwDisposition ); /* if we failed, note it, and leave */ if (lRetCode != ERROR_SUCCESS) { if( ! quiet ) printf ("Error in creating blat key in the registry\n"); return 10; } /* try to set a section value */ lRetCode = RegSetValueEx( hKey1,"SMTP server",0,REG_SZ, (BYTE *) &SMTPHost[0], (strlen(SMTPHost)+1)); /* if we failed, note it, and leave */ if (lRetCode != ERROR_SUCCESS) { if( ! quiet ) printf ( "Error in setting SMTP server value in the registry\n"); return 11; } /* try to set another section value */ lRetCode = RegSetValueEx( hKey1,"Sender",0,REG_SZ, (BYTE *) &Sender[0], (strlen(Sender)+1)); /* if we failed, note it, and leave */ if (lRetCode != ERROR_SUCCESS) { if( ! quiet ) printf ( "Error in setting sender address value in the registry\n"); return 11; } return 0; } // get the registry entries for this program int GetRegEntry( void ) { HKEY hKey1; DWORD dwType; DWORD dwBytesRead; LONG lRetCode; // open the registry key in read mode lRetCode = RegOpenKeyEx( HKEY_LOCAL_MACHINE, "SOFTWARE\\Public Domain\\Blat", 0, KEY_READ, &hKey1 ); if( lRetCode != ERROR_SUCCESS ) { if( ! quiet ) printf( "Failed to open registry key for Blat\n" ); return 12; } // set the size of the buffer to contain the data returned from the registry // thanks to Beverly Brown "beverly@datacube.com" and "chick@cyberspace.com" for spotting it... dwBytesRead=SERVER_SIZE; // read the value of the SMTP server entry lRetCode = RegQueryValueEx( hKey1, "SMTP server", NULL , &dwType, (BYTE *) &SMTPHost, &dwBytesRead); // if we failed, note it, and leave if( lRetCode != ERROR_SUCCESS ) { if( ! quiet ) printf( "Failed to read SMTP server value from the registry\n" ); return 12; } dwBytesRead=SENDER_SIZE; // read the value of the SMTP server entry lRetCode = RegQueryValueEx( hKey1, "Sender", NULL , &dwType, (BYTE *) &Sender, &dwBytesRead); // if we failed, note it, and leave if( lRetCode != ERROR_SUCCESS ) { if( ! quiet ) printf( "Failed to read senders user name from the registry\n" ); return 12; } return 0; } int main( int argc, /* Number of strings in array argv */ char *argv[], /* Array of command-line argument strings */ char **envp ) /* Array of environment variable strings */ { int next_arg=2; int impersonating = 0; int penguin = 0; int i, j; char tempdir[MAX_PATH+1]; char tempfile[MAX_PATH+1]; HANDLE fileh; FILE *tf; int hours, minutes; OFSTRUCT of; // by default Blat is very noisy! quiet = 0; // no tempfile so far... tempfile[0] = '\0'; if(argc<2) { // must have at least file name to send for(i=0;i' and '-f '\n"); printf( "aborting, nothing sent\n" ); } return 12; } // make sure filename exists, get full pathname if (lstrcmp(filename, "-") != 0) if(lstrlen(filename)<=0 || OpenFile(filename,&of,OF_EXIST)==HFILE_ERROR) { if( ! quiet ) cout< 0 ) { strcat(Recipients, "," ); strcat(Recipients, temp); } // Parse the "Bcc:" line for (i = j = 0; i < (int) strlen(bcc_list); i++) { // strip white space while (bcc_list[i]==' ') i++; // look for comments in brackets, and omit if (bcc_list[i]=='(') { while (bcc_list[i]!=')') i++; i++; } // look for comments in quotes, and omit if (bcc_list[i]=='\'') { i++; while (bcc_list[i]!='\'') i++; i++; } temp[j++] = bcc_list[i]; } if( strlen(bcc_list) > 0 ) { strcat(Recipients, "," ); strcat(Recipients, temp); } // create a header for the message char tmpstr[256]; char header[2048]; int headerlen; SYSTEMTIME curtime; TIME_ZONE_INFORMATION tzinfo; char * days[] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"}; char * months[] = { "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"}; DWORD retval; GetLocalTime( &curtime ); retval = GetTimeZoneInformation( &tzinfo ); hours = (int) tzinfo.Bias / 60; minutes = (int) tzinfo.Bias % 60; if( retval == TIME_ZONE_ID_STANDARD ) { hours += (int) tzinfo.StandardBias / 60; minutes += (int) tzinfo.StandardBias % 60; } else { hours += (int) tzinfo.DaylightBias / 60; minutes += (int) tzinfo.DaylightBias % 60; } // rfc1036 & rfc822 acceptable format // Mon, 29 Jun 94 02:15:23 GMT sprintf (tmpstr, "Date: %s, %.2d %s %.2d %.2d:%.2d:%.2d ", days[curtime.wDayOfWeek], curtime.wDay, months[curtime.wMonth - 1], curtime.wYear, curtime.wHour, curtime.wMinute, curtime.wSecond); strcpy( header, tmpstr ); sprintf( tmpstr, "%+03d%02d", -hours, -minutes ); //for(i=0;i<32;i++) //{ // if( retval == TIME_ZONE_ID_STANDARD ) tmpstr[i] = (char) tzinfo.StandardName[i]; // else tmpstr[i] = (char) tzinfo.DaylightName[i]; //} strcat( header, tmpstr ); strcat( header, "\r\n" ); sprintf( tmpstr, "From: %s\r\n", senderid ); strcat( header, tmpstr ); if( impersonating ) { sprintf( tmpstr, "Sender: %s\r\n", loginname ); strcat( header, tmpstr ); if (!(penguin == 1)) { sprintf( tmpstr, "Reply-to: %s\r\n", loginname ); strcat( header, tmpstr ); } } if( *subject ) { sprintf( tmpstr, "Subject: %s\r\n", subject ); strcat( header, tmpstr ); } else { if (!(penguin == 1)) { if (lstrcmp(filename, "-") == 0) sprintf( tmpstr, "Subject: Contents of console input\r\n", filename ); else sprintf( tmpstr, "Subject: Contents of file: %s\r\n", filename ); strcat( header, tmpstr ); } } sprintf( tmpstr, "To: %s\r\n", destination ); strcat( header, tmpstr ); if( *cc_list ) { // Add line for the Carbon Copies sprintf( tmpstr, "Cc: %s\r\n", cc_list ); strcat( header, tmpstr ); } strcat( header, "X-Mailer: \r\n" ); if (!(penguin == 1)) strcat( header, "\r\n" ); headerlen = strlen( header ); // if reading from the console, read everything into a temporary file first if (lstrcmp(filename, "-") == 0) { // create a unique temporary file name GetTempPath( MAX_PATH, tempdir ); GetTempFileName( tempdir, "blt", 0, tempfile ); // open the file in write mode tf = fopen(tempfile,"w"); if( tf==NULL ) { if( ! quiet ) cout<<"error opening temporary file "<")<<'\n'; if(lstrlen(subject)) cout<<"Subject:"<