diff options
author | Naram Qashat <cyberbotx@cyberbotx.com> | 2010-06-27 23:15:05 -0400 |
---|---|---|
committer | Naram Qashat <cyberbotx@cyberbotx.com> | 2010-06-27 23:15:05 -0400 |
commit | 28e12bc24a9c85f4f0d1e37567618ec39cb501f6 (patch) | |
tree | cc70ebeef95a9d95174afe3ef038b0d673346f58 /src/tools/anopesmtp.cpp | |
parent | 051ebe3eea0f8529b64c0e443c61103ba2f7dee8 (diff) |
The next of a few "CBX OCDing over code style" commits, maybe the last.
NOTES: I have been unable to compile the db_mysql_* functions on my system here, so those are untested. db-convert seems to be badly programmed and needs more work in my opinion.
Diffstat (limited to 'src/tools/anopesmtp.cpp')
-rw-r--r-- | src/tools/anopesmtp.cpp | 521 |
1 files changed, 521 insertions, 0 deletions
diff --git a/src/tools/anopesmtp.cpp b/src/tools/anopesmtp.cpp new file mode 100644 index 000000000..aedbee725 --- /dev/null +++ b/src/tools/anopesmtp.cpp @@ -0,0 +1,521 @@ +/* smtp stuff handler for win32. + * + * (C) 2003-2010 Anope Team + * Contact us at team@anope.org + * + * Please read COPYING and README for furhter details. + * + * Based on the original code of Epona by Lara. + * Based on the original code of Services by Andy Church. + * + * Written by Dominick Meglio <codemastr@unrealircd.com> + * *nix port by Trystan Scott Lee <trystan@nomadirc.net> + */ + +#include "smtp.h" + +static FILE *logfile; +static int curday = 0; + +/*************************************************************************/ + +static int get_logname(std::string &name, struct tm *tm = NULL) +{ + char timestamp[32]; + + if (!tm) + { + time_t t = time(NULL); + tm = localtime(&t); + } + + strftime(timestamp, sizeof(timestamp), "%Y%m%d", tm); + name = std::string("logs/anopesmtp.") + timestamp; + curday = tm->tm_yday; + + return 1; +} + +/*************************************************************************/ + +/* Close the log file. */ + +void close_log() +{ + if (!logfile) + return; + fclose(logfile); + logfile = NULL; +} + +/*************************************************************************/ + +static void remove_log() +{ + time_t t = time(NULL); + t -= 2592000; // 30 days ago + struct tm *tm = localtime(&t); + + std::string name; + if (!get_logname(name, tm)) + return; + unlink(name.c_str()); +} + +/*************************************************************************/ + +/* Open the log file. Return -1 if the log file could not be opened, else + * return 0. */ + +int open_log() +{ + if (logfile) + return 0; + + std::string name; + if (!get_logname(name)) + return 0; + logfile = fopen(name.c_str(), "w"); + return logfile ? 0 : -1; +} + +/*************************************************************************/ + +static void checkday() +{ + time_t t = time(NULL); + struct tm *tm = localtime(&t); + + if (curday != tm->tm_yday) + { + close_log(); + remove_log(); + open_log(); + } +} + +/*************************************************************************/ + +/* Log stuff to the log file with a datestamp. Note that errno is + * preserved by this routine and log_perror(). + */ + +void alog(const char *fmt, ...) +{ + int errno_save = errno; + + if (!smtp_debug) + return; + + checkday(); + + if (!fmt) + return; + + va_list args; + va_start(args, fmt); + + time_t t = time(NULL); + struct tm *tm = localtime(&t); + + char buf[256]; + strftime(buf, sizeof(buf) - 1, "[%b %d %H:%M:%S %Y] ", tm); + if (logfile) + { + fputs(buf, logfile); + vfprintf(logfile, fmt, args); + fputc('\n', logfile); + } + va_end(args); + errno = errno_save; +} + +/*************************************************************************/ + +/* Remove a trailing \r\n */ +ci::string strip(const ci::string &buf) +{ + ci::string newbuf = buf; + char c = newbuf[newbuf.size() - 1]; + while (c == '\n' || c == '\r') + { + newbuf.erase(newbuf.end() - 1); + c = newbuf[newbuf.size() - 1]; + } + return newbuf; +} + +/*************************************************************************/ + +/* Is the buffer a header? */ +bool smtp_is_header(const ci::string &buf) +{ + size_t tmp = buf.find(' '); + + if (tmp == ci::string::npos) + return false; + + if (buf[tmp + 1] == ':') + return true; + return false; +} + +/*************************************************************************/ + +/* Parse a header into a name and value */ +void smtp_parse_header(const ci::string &buf, ci::string &header, ci::string &value) +{ + ci::string newbuf = strip(buf); + + size_t space = newbuf.find(' '); + if (space != ci::string::npos) + { + header = newbuf.substr(0, space); + value = newbuf.substr(space + 1); + } + else + { + header = newbuf; + value = ""; + } +} + +/*************************************************************************/ + +/* Have we reached the end of input? */ +bool smtp_is_end(const ci::string &buf) +{ + if (buf[0] == '.') + if (buf[1] == '\r' || buf[1] == '\n') + return true; + + return false; +} + +/*************************************************************************/ + +/* Set who the email is to */ +void smtp_set_to(const ci::string &to) +{ + mail.to = to; + size_t c = mail.to.rfind('<'); + if (c != ci::string::npos && c + 1 < mail.to.size()) + { + mail.to = mail.to.substr(c + 1); + mail.to.erase(mail.to.end() - 1); + } +} + +/*************************************************************************/ + +/* Establish a connection to the SMTP server */ +int smtp_connect(const char *host, unsigned short port) +{ + struct sockaddr_in addr; + + if ((mail.sock = socket(AF_INET, SOCK_STREAM, 0)) == SOCKET_ERROR) + return 0; + + if ((addr.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE) + { + struct hostent *hent; + if (!(hent = gethostbyname(host))) + return 0; + memcpy(&addr.sin_addr, hent->h_addr, hent->h_length); + } + addr.sin_family = AF_INET; + addr.sin_port = htons(port ? port : 25); + if (connect(mail.sock, reinterpret_cast<struct sockaddr *>(&addr), sizeof(struct sockaddr_in)) == SOCKET_ERROR) + { + ano_sockclose(mail.sock); + return 0; + } + + return 1; +} + +/*************************************************************************/ + +/* Send a line of text */ +int smtp_send(const char *text) +{ + int result = ano_sockwrite(mail.sock, text, strlen(text)); + + alog("SMTP: sent %s",text); + + if (result == SOCKET_ERROR) + ano_sockclose(mail.sock); + + return result; +} + +/*************************************************************************/ + +/* Read a line of text */ +int smtp_read(char *buf, int len) +{ + int result; + + memset(buf, 0, len); + result = ano_sockread(mail.sock, buf, len); + + if (result == SOCKET_ERROR) + ano_sockclose(mail.sock); + + return result; +} + +/*************************************************************************/ + +/* Retrieve a response code */ +int smtp_get_code(const std::string &text) +{ + size_t tmp = text.find(' '); + + if (tmp == ci::string::npos) + return 0; + + return atol(text.c_str()); +} + +/*************************************************************************/ + +/* Send the email */ +int smtp_send_email() +{ + char buf[1024]; + if (!smtp_read(buf, 1024)) + { + alog("SMTP: error reading buffer"); + return 0; + } + + int code = smtp_get_code(buf); + if (code != 220) + { + alog("SMTP: error expected code 220 got %d",code); + return 0; + } + + if (!smtp_send("HELO anope\r\n")) + { + alog("SMTP: error writting to socket"); + return 0; + } + + if (!smtp_read(buf, 1024)) + { + alog("SMTP: error reading buffer"); + return 0; + } + + code = smtp_get_code(buf); + if (code != 250) + { + alog("SMTP: error expected code 250 got %d",code); + return 0; + } + + strcpy(buf, "MAIL FROM: <"); + strcat(buf, mail.from.c_str()); + strcat(buf, ">\r\n"); + + if (!smtp_send(buf)) + { + alog("SMTP: error writting to socket"); + return 0; + } + + if (!smtp_read(buf, 1024)) + { + alog("SMTP: error reading buffer"); + return 0; + } + + code = smtp_get_code(buf); + if (code != 250) + return 0; + + strcpy(buf, "RCPT TO: <"); + strcat(buf, mail.to.c_str()); + strcat(buf, ">\r\n"); + + if (!smtp_send(buf)) + { + alog("SMTP: error writting to socket"); + return 0; + } + + if (!smtp_read(buf, 1024)) + { + alog("SMTP: error reading buffer"); + return 0; + } + + code = smtp_get_code(buf); + if (smtp_get_code(buf) != 250) + { + alog("SMTP: error expected code 250 got %d",code); + return 0; + } + + if (!smtp_send("DATA\r\n")) + { + alog("SMTP: error writting to socket"); + return 0; + } + + if (!smtp_read(buf, 1024)) + { + alog("SMTP: error reading buffer"); + return 0; + } + + code = smtp_get_code(buf); + if (code != 354) + { + alog("SMTP: error expected code 354 got %d",code); + return 0; + } + + for (std::vector<ci::string>::const_iterator it = mail.smtp_headers.begin(), it_end = mail.smtp_headers.end(); it != it_end; ++it) + if (!smtp_send(it->c_str())) + { + alog("SMTP: error writting to socket"); + return 0; + } + + if (!smtp_send("\r\n")) + { + alog("SMTP: error writting to socket"); + return 0; + } + + bool skip_done = false; + for (std::vector<ci::string>::const_iterator it = mail.smtp_body.begin(), it_end = mail.smtp_body.end(); it != it_end; ++it) + if (skip_done) + { + if (!smtp_send(it->c_str())) + { + alog("SMTP: error writting to socket"); + return 0; + } + } + else + skip_done = true; + + if (!smtp_send("\r\n.\r\n")) + { + alog("SMTP: error writting to socket"); + return 0; + } + + return 1; +} + +/*************************************************************************/ + +void smtp_disconnect() +{ + smtp_send("QUIT\r\n"); + ano_sockclose(mail.sock); +} + +/*************************************************************************/ + +void mail_cleanup() +{ + mail.from.clear(); + mail.to.clear(); + + mail.smtp_headers.clear(); + + mail.smtp_body.clear(); +} + +/*************************************************************************/ + +int main(int argc, char *argv[]) +{ + /* Win32 stuff */ +#ifdef _WIN32 + WSADATA wsa; +#endif + + if (argc == 1) + return 0; + + char *server = strtok(argv[1], ":"), *aport; + short port; + if ((aport = strtok(NULL, ""))) + port = atoi(aport); + else + port = 25; + + if (!server) + { + alog("No Server"); + /* Bad, bad, bad. This was a return from main with no value! -GD */ + return 0; + } + else + alog("SMTP: server %s port %d",server,port); + + memset(&mail, 0, sizeof(mail)); + + /* The WSAStartup function initiates use of WS2_32.DLL by a process. */ + /* guessing we can skip it under *nix */ +#ifdef _WIN32 + if (WSAStartup(MAKEWORD(1, 1), &wsa)) + return 0; +#endif + + char buf[8192]; + bool headers_done = false; + /* Read the message and parse it */ + while (fgets(buf, 8192, stdin)) + { + if (smtp_is_header(buf) && !headers_done) + { + mail.smtp_headers.push_back(strip(buf) + "\r\n"); + ci::string header, value; + smtp_parse_header(buf, header, value); + if (header == "from") + { + alog("SMTP: from: %s", value.c_str()); + mail.from = value; + } + else if (header == "to") + { + alog("SMTP: to: %s", value.c_str()); + smtp_set_to(value); + } + else if (smtp_is_end(buf)) + break; + else + { + headers_done = true; + mail.smtp_body.push_back(strip(buf) + "\r\n"); + } + } + else + mail.smtp_body.push_back(strip(buf) + "\r\n"); + } + + if (!smtp_connect(server, port)) + { + alog("SMTP: failed to connect to %s:%d", server, port); + mail_cleanup(); + return 0; + } + if (!smtp_send_email()) + { + alog("SMTP: error during sending of mail"); + mail_cleanup(); + return 0; + } + smtp_disconnect(); + mail_cleanup(); + + return 1; +} |