summaryrefslogtreecommitdiff
path: root/src/tools/anopesmtp.cpp
diff options
context:
space:
mode:
authorNaram Qashat <cyberbotx@cyberbotx.com>2010-06-27 23:15:05 -0400
committerNaram Qashat <cyberbotx@cyberbotx.com>2010-06-27 23:15:05 -0400
commit28e12bc24a9c85f4f0d1e37567618ec39cb501f6 (patch)
treecc70ebeef95a9d95174afe3ef038b0d673346f58 /src/tools/anopesmtp.cpp
parent051ebe3eea0f8529b64c0e443c61103ba2f7dee8 (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.cpp521
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;
+}