find-patterns/killspam/killspam.c

446 lines
13 KiB
C

/* Copyright 2006 Neil Edelman, distributed under the terms of the
GNU General Public License, see copying.txt */
/* this is a LCC-WIN32 mail programme that goes with Find Patterns */
#include <stdlib.h> /* atoi(), getenv(), malloc(), free() */
#include <string.h> /* memcpy(), strchr() */
#include <stdio.h> /* printf() */
#include <conio.h> /* getch() */
#include <regexp.h> /* I'm lazy so I'll use LCC-WIN32's regexps */
#include "net.h"
#define MAX_REGX_SIZE 0x4000
#define BUF_SIZE 256
#define LOG_FILE_NAME "email"
#define SPAM_FILE_NAME "spamregx"
#define SPAM_ENV_VAR "SPAMREGX"
static const char *programme = "Vindicator";
static const char *year = "2006";
static const int versionMajor = 0;
static const int versionMinor = 1;
typedef struct tagMailSession {
enum {
WAIT_GREET,
WAIT_USEROK,
WAIT_PASSOK,
WAIT_STAT,
CHECKA_EMAIL,
WAIT_RETROK,
PARSE_DATA,
WAIT_DELEOK,
RUN_AWAY
} state;
int mailPos;
int mailCount;
int spamFactor;
unsigned int lnSize;
char line[BUF_SIZE];
unsigned int rpSize;
char resp[BUF_SIZE];
FILE *log;
regexp *subjRe;
regexp *fromRe;
regexp *spamRe;
} MailSession;
static MailSession *create_mail_session(void);
static void destroy_mail_session(MailSession *ms);
static int check_email(MailSession *ms, Net *net, char *name, unsigned int port);
static int get_response(MailSession *ms);
static int getstr(char **buf, int size, int echo);
int main(int argc, char **argv) {
Net *net;
MailSession *ms;
int ret, port;
char *server;
printf("%s\n\n", programme);
if(argc <= 1) {
printf("Arguments: [<server> [<port>]]\n\n");
printf("Connects to <server> (default localhost) on port <port> (default 110)\n");
printf("retreives emails, logging them into \"%s\" if it exists. If the\n", LOG_FILE_NAME);
printf("environment variable \"%s\" is defined or the file \"%s\"\n", SPAM_ENV_VAR, SPAM_FILE_NAME);
printf("exists, it loads the a regexp from them. Any emails matching the regexp\n");
printf("can then be annihilated.\n\n");
fprintf(stderr, "Version %d.%d.\n\n", versionMajor, versionMinor);
fprintf(stderr, "%s Copyright %s Neil Edelman\n", programme, year);
fprintf(stderr, "This program comes with ABSOLUTELY NO WARRANTY.\n");
fprintf(stderr, "This is free software, and you are welcome to redistribute it\n");
fprintf(stderr, "under certain conditions; see copying.txt.\n\n");
return 1;
}
port = (argc >= 3) ? atoi(argv[2]) : 110;
server = (argc >= 2) ? argv[1] : "localhost";
if(!(net = create_net(1024))) {
printf("Failed creating connection data.\n");
return 1;
}
if(!(ms = create_mail_session())) {
printf("Failed creating e-mail session data.\n");
destroy_net(net);
return 1;
}
ret = check_email(ms, net, server, port);
destroy_mail_session(ms);
destroy_net(net);
return ret ? 0 : 1;
}
static MailSession *create_mail_session(void) {
MailSession *ms;
/* create and initialize the memory space */
if(!(ms = malloc(sizeof(MailSession)))) {
perror("Error allocating memory for e-mail data.\n");
return 0;
}
printf("Created mail session data.\n");
ms->state = WAIT_GREET;
ms->mailPos = 1;
ms->mailCount = 0;
/* open the log file */
if(!(ms->log = fopen(LOG_FILE_NAME, "r"))) {
perror("Didn't find log file");
printf("To log email, create a file nammed \"%s.\"\n", LOG_FILE_NAME);
} else {
fclose(ms->log);
if(!(ms->log = fopen(LOG_FILE_NAME, "w")))
perror("Failed opening log file");
else
printf("Opened log file, \"%s,\" for writing.\n", LOG_FILE_NAME);
}
if(!(ms->subjRe = regcomp("^[Ss]ubject: ")))
printf("Error compiling subject regexp.\n");
if(!(ms->fromRe = regcomp("^[Ff]rom: ")))
printf("Error compiling sender regexp.\n");
ms->spamRe = 0;
/* read in the spam regexp */
{
FILE *fp = 0;
int len;
char regStr[MAX_REGX_SIZE] /* YIKES! */, *regPtr;
if(!(regPtr = getenv(SPAM_ENV_VAR))
&& !(fp = fopen(SPAM_FILE_NAME, "r"))) {
perror("Didn't find spam regexp file");
printf("To load a spam regexp, write it in a file nammed \"%s\" or set it\n", SPAM_FILE_NAME);
printf("as an environment variable, \"%s.\" Please enter the regexp with which to\n", SPAM_ENV_VAR);
printf("smite the vile spam:\n");
regPtr = regStr;
if((len = getstr(&regPtr, sizeof(regStr), 1)) < 0) {
destroy_mail_session(ms);
return 0;
}
putchar('\n');
} else if(fp) {
int ch;
printf("Reading regexp from file \"%s.\"\n", SPAM_FILE_NAME);
len = 0;
regPtr = regStr;
for( ; ; ) {
if((ch = fgetc(fp)) == EOF) break;
if(ch && ch != '\n' && ch != '\r') {
*(regPtr++) = ch;
if(len++ >= sizeof(regStr)) {
printf("Regexp size exceeded limit of %u bytes.\n", sizeof(regStr));
len = 0;
break;
}
}
}
fclose(fp);
*regPtr = 0;
} else {
if((len = strlen(regPtr)) >= sizeof(regStr)) {
printf("Regexp size exceeded limit of %u bytes.\n", sizeof(regStr));
len = 0;
} else if(len >= 2
&& ((regPtr[0] == '\'' && regPtr[len - 1] == '\'')
|| (regPtr[0] == '\"' && regPtr[len - 1] == '\"'))) {
memcpy(regStr, regPtr + 1, len - 2);
regStr[len - 2] = 0;
} else {
memcpy(regStr, regPtr, len);
regStr[len] = 0;
}
}
if(len > 0) {
if(!(ms->spamRe = regcomp(regStr)))
printf("Error compiling spam regexp.\n");
else
printf("Spam regexp is %s\n", regStr);
}
}
return ms;
}
static void destroy_mail_session(MailSession *ms) {
/* Email *eml, *nextEml; */
if(!ms) return;
if(ms->log) {
printf("Closing log file.\n");
fclose(ms->log); ms->log = 0;
}
if(ms->subjRe) { free(ms->subjRe); ms->subjRe = 0; }
if(ms->fromRe) { free(ms->fromRe); ms->fromRe = 0; }
if(ms->spamRe) { free(ms->spamRe); ms->spamRe = 0; }
/* for(eml = ms->emailList; eml; eml = nextEml) {
nextEml = eml->next;
free(eml);
} */
free(ms);
}
static int check_email(MailSession *ms, Net *net, char *name, unsigned int port) {
unsigned int lnRem, lnEol, svRem, svEol;
char *lnPtr, *svNl, *svPtr;
/* connect to the given host */
if(!set_net_host(net, name, port)
|| !connect_net(net)) return 0;
/* loop reading lines from the server */
lnPtr = ms->line;
lnRem = sizeof(ms->line) - 1;
svPtr = 0;
svRem = 0;
for( ; ; ) {
/* if the server string is empty, check for more */
if(!svRem && !read_net_string(net, &svPtr, &svRem, 100000)) return 0;
/* how many chars until the end of the line */
svEol = (svNl = strchr(svPtr, '\n')) ? svNl - svPtr + 1 : svRem;
lnEol = (svEol > lnRem) ? lnRem : svEol;
/* copy over a whole line from the server string into the local one */
memcpy(lnPtr, svPtr, lnEol);
lnPtr[lnEol] = 0;
/*printf("svPtr = %s; svRem = %u; lnRem = %u; svEol = %u; lnEol = %u; lnPtr = %s\n", svPtr, svRem, lnRem, svEol, lnEol, lnPtr);*/
/* shift all positions by this amount */
svPtr += lnEol;
svRem -= lnEol;
svEol -= lnEol;
lnPtr += lnEol;
lnRem -= lnEol;
/* check if there was a new line or the local buffer is full */
if(svNl || !lnRem) {
ms->lnSize = sizeof(ms->line) - lnRem - 1;
ms->rpSize = sizeof(ms->resp);
if(!get_response(ms)) break;
if(ms->rpSize && !send_net_string(net, ms->resp, ms->rpSize)) return 0;
lnPtr = ms->line;
lnRem = sizeof(ms->line) - 1;
}
}
return 1;
}
static int get_response(MailSession *ms) {
char *rpPtr;
if(ms->state != PARSE_DATA) {
printf("Mr. Mail Deamon: %s", ms->line);
if(!ms->lnSize || ms->line[ms->lnSize - 1] != '\n') putchar('\n');
}
switch(ms->state) {
case WAIT_GREET:
if(*ms->line != '+') {
printf("If this is a POP3 mail server, it seems to be having an identity crisis.\n");
return 0;
}
printf("The server is glad to see you. It wants your user name: ");
rpPtr = ms->resp + sprintf(ms->resp, "user ");
if(getstr(&rpPtr, ms->rpSize + ms->resp - rpPtr, 1) < 0) return 0;
printf("\n"); rpPtr += sprintf(rpPtr, "\n");
ms->rpSize = rpPtr - ms->resp;
printf("%s: %s", programme, ms->resp);
ms->state = WAIT_USEROK;
return 1;
case WAIT_USEROK:
if(*ms->line == '-') break;
if(*ms->line != '+') return 1;
printf("That user name seems acceptable. Now for a password: ");
rpPtr = ms->resp + sprintf(ms->resp, "pass ");
if(getstr(&rpPtr, ms->rpSize + ms->resp - rpPtr, 0) < 0) return 0;
printf("\n"); rpPtr += sprintf(rpPtr, "\n");
ms->rpSize = rpPtr - ms->resp;
printf("%s: pass <secret ninja-encrypted password>\n", programme/*, ms->resp*/);
ms->state = WAIT_PASSOK;
return 1;
case WAIT_PASSOK:
if(*ms->line == '-') break;
if(*ms->line != '+') return 1;
printf("The server has accepted you! It thinks you are Special.\n");
sprintf(ms->resp, "stat\n"); ms->rpSize = 5;
printf("%s: %s", programme, ms->resp);
ms->state = WAIT_STAT;
return 1;
case WAIT_STAT:
if(*ms->line == '-') break;
if(*ms->line != '+') return 1;
{
char *numStart;
rpPtr = ms->line;
while(*rpPtr && *rpPtr != ' ') rpPtr++;
while(*rpPtr == ' ') rpPtr++;
if(!*(numStart = rpPtr)) { printf("That doesn't make any sense!\n"); break; }
while(*rpPtr && *rpPtr != ' ') rpPtr++;
*rpPtr = 0;
ms->mailCount = atoi(numStart);
}
if(ms->mailPos <= ms->mailCount) {
rpPtr = ms->resp + sprintf(ms->resp, "retr %u\n", ms->mailPos);
ms->rpSize = rpPtr - ms->resp;
ms->state = WAIT_RETROK;
} else {
sprintf(ms->resp, "quit\n"); ms->rpSize = 5;
ms->state = RUN_AWAY;
}
printf("%s: %s", programme, ms->resp);
return 1;
case WAIT_RETROK:
if(*ms->line == '-') break;
if(*ms->line != '+') return 1;
*ms->resp = 0;
ms->rpSize = 0;
/* keep it simple . . . (who wants confirmation later anyway?)
{
Email *eml;
if(!(eml = malloc(sizeof(Email))))
perror("Failed allocating memory to store e-mail info.");
else {
eml->next = ms->emailList;
eml->subject[0] = 0;
eml->sender[0] = 0;
eml->number = ms->mailPos;
eml->spamFactor = 0;
ms->emailList = eml;
}
} */
ms->spamFactor = 0;
/* printf("[ Scouring message %u for unholy spam . . . ]\n", ms->mailPos); */
ms->state = PARSE_DATA;
return 1;
case PARSE_DATA:
if(ms->line[0] == '.' && (ms->line[1] == '\r' || ms->line[1] == '\n')) {
if(ms->log) fprintf(ms->log, ".\n[ End of Message %u. ]\n", ms->mailPos);
if(ms->spamFactor) {
rpPtr = ms->resp + sprintf(ms->resp, "dele %u\n", ms->mailPos);
ms->rpSize = rpPtr - ms->resp;
ms->state = WAIT_DELEOK;
} else if(++ms->mailPos <= ms->mailCount) {
rpPtr = ms->resp + sprintf(ms->resp, "retr %u\n", ms->mailPos);
ms->rpSize = rpPtr - ms->resp;
ms->state = WAIT_RETROK;
} else {
sprintf(ms->resp, "quit\n"); ms->rpSize = 5;
ms->state = RUN_AWAY;
}
printf("%s: %s", programme, ms->resp);
} else {
if(ms->log) fprintf(ms->log, "%s", ms->line);
/* if(ms->emailList
&& ms->emailList->number == ms->mailPos) {
if(!ms->emailList->subject[0]
&& ms->subjRe && regexec(ms->subjRe, ms->line)) {
strncpy(ms->emailList->subject, ms->line, sizeof(ms->emailList->subject));
printf("%s", ms->line);
} else if(!ms->emailList->sender[0]
&& ms->fromRe && regexec(ms->fromRe, ms->line)) {
strncpy(ms->emailList->subject, ms->line, sizeof(ms->emailList->subject));
printf("%s", ms->line);
}
if(!ms->emailList->spamFactor
&& ms->spamRe && regexec(ms->spamRe, ms->line)) {
ms->emailList->spamFactor++;
sm->spamFactor++;
printf("EVIL SPAM!->%s", ms->line);
}
} */
if(ms->subjRe && regexec(ms->subjRe, ms->line))
printf("%s", ms->line);
else if(ms->fromRe && regexec(ms->fromRe, ms->line))
printf("%s", ms->line);
if(ms->spamRe && regexec(ms->spamRe, ms->line)) {
ms->spamFactor++;
printf("---EVIL SPAM!--->%s", ms->line);
}
*ms->resp = 0;
ms->rpSize = 0;
}
return 1;
case WAIT_DELEOK:
if(*ms->line == '-') break;
if(*ms->line != '+') return 1;
if(++ms->mailPos <= ms->mailCount) {
rpPtr = ms->resp + sprintf(ms->resp, "retr %u\n", ms->mailPos);
ms->rpSize = rpPtr - ms->resp;
ms->state = WAIT_RETROK;
} else {
sprintf(ms->resp, "quit\n"); ms->rpSize = 5;
ms->state = RUN_AWAY;
}
printf("%s: %s", programme, ms->resp);
return 1;
case RUN_AWAY:
if(*ms->line != '+') printf("The server is very confused. ");
printf("Bye server.\n");
return 0;
}
printf("The server is confused!\n");
sprintf(ms->resp, "quit\n"); ms->rpSize = 5;
ms->state = RUN_AWAY;
return 1;
}
static int getstr(char **buf, int size, int echo) {
int ch;
int pos = 0;
char *str = *buf;
if(size < 1) return -1;
*str = 0;
for( ; ; ) {
ch = getch();
if(ch <= 0) return -1;
if((ch & 0x60) == 0x00) {
switch((ch & 0x1F) | 0x40) {
case '@': /* ^@ - null */
case 'C': /* ^C - end of text */
case 'D': /* ^D - end of transmission */
case '[': /* ^[ - escape */
*buf = str;
return -1;
case 'U':
if(echo) { while(pos--) printf("\b \b"); }
pos = 0; *(str = *buf) = 0;
break;
case 'H':
if(pos) {
pos--; *(--str) = 0;
if(echo) printf("\b \b");
}
break;
case 'J': /* ^J - LF (NL) new line */
case 'M': /* ^M - CR return */
*buf = str;
return pos;
}
} else if(ch == 0x7F) {
} else {
if(pos + 1 < size) {
if(echo) putchar(ch);
str[0] = ch;
str[1] = 0;
if(pos + 2 < size) { pos++; str++; }
}
}
}
}