/* 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 /* atoi(), getenv(), malloc(), free() */ #include /* memcpy(), strchr() */ #include /* printf() */ #include /* getch() */ #include /* 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: [ []]\n\n"); printf("Connects to (default localhost) on 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(®Ptr, 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 \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++; } } } } }