Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #include "stdafx.h"
- #include "SMTP.h"
- bool SMTP::send()
- {
- std::string from = "MAIL FROM: <" + fromAddress.a + ">\r\n";
- sock->Send(from);
- if(!is_successful())
- {
- currentError = SENDER_NOT_ACCEPTED;
- return false;
- }
- currentStatus = SENDER_ACCEPTED;
- // Send addresses
- if(!send_recipents(toAddress) || !send_recipents(ccAddress) || !send_recipents(bccAddress))
- {
- currentError = RECIPENTS_NOT_ACCEPTED;
- return false;
- }
- currentStatus = RECIPENTS_ACCEPTED;
- // Start delivery
- sock->Send("DATA\r\n");
- if(!is_successful("354"))
- {
- currentError = DATA_NOT_ACCEPTED;
- // Fixme: More codes? 421 is from Mercury/Greywall
- if(0 == strcmp(get_reply_code(), "421"))
- {
- currentError = GREYLISTED;
- }
- return false;
- }
- currentStatus = DATA_ACCEPTED;
- std::string envelopeHeader;
- build_envelope_header(envelopeHeader);
- sock->Send(envelopeHeader);
- sock->Send("\r\n");
- std::string body;
- build_message_body(body);
- sock->Send(body);
- sock->Send("\r\n.\r\n");
- // 451 (Or just 4XX?) == Greylisting
- // "451 Temporary local problem, please try again!"
- // TODO: Decide whether content blacklisting or Greylisting? am I even informed in case of blacklisting
- bool b = is_successful();
- if(b)
- currentStatus = DELIVERY_SUCCESSFUL;
- return b;
- }
- void SMTP::add_attachment(std::string data, std::string name, std::string type)
- {
- add_attachment((const unsigned char*)data.c_str(), data.length(), name, type);
- }
- void SMTP::add_attachment(const unsigned char* data, size_t len, std::string name, std::string type)
- {
- Attachment a;
- append_base64(a.data, data, len, false);
- a.name = name;
- a.type = type;
- attachments.push_back(a);
- }
- std::string SMTP::get_boundary(std::string seed)
- {
- std::string boundary = "";
- while(boundary.length() < 24)
- {
- int n = (seed.at(boundary.length() % seed.length()) ^ 'a') % (boundary.length() % 2 ? 10 : 100);
- add_int_to_string(boundary, n);
- }
- return boundary;
- }
- void SMTP::build_message_body(std::string& body)
- {
- if(0 == attachments.size() && 0 == htmlMessage.length())
- {
- body = message;
- return;
- }
- std::string mainBoundary = get_boundary(message);
- std::string htmlBoundary = get_boundary(htmlMessage);
- body = "This is a multi-part message in MIME format.\r\n\r\n";
- body += "--" + mainBoundary + "\r\n";
- if(attachments.size())
- {
- body += "Content-Type: multipart/alternative;\r\n"
- " boundary=\"" + htmlBoundary + "\"\r\n\r\n";
- body += "--" + htmlBoundary + "\r\n";
- }
- // Increasing order of preference
- body += std::string("Content-Type: text/plain\r\n") +
- std::string("Content-Transfer-Encoding: 7bit\r\n");
- body += "\r\n";
- body += message;
- body += "\r\n";
- if(htmlMessage.size())
- {
- body += "--" + (attachments.size() ? htmlBoundary : mainBoundary) + std::string("\r\n");
- body += std::string("Content-Type: text/html\r\n") +
- std::string("Content-Transfer-Encoding: 7bit\r\n\r\n");
- body += htmlMessage + std::string("\r\n");
- body += "--" + (attachments.size() ? htmlBoundary : mainBoundary) + std::string("--\r\n");
- }
- for(std::vector<Attachment>::iterator i = attachments.begin(); i != attachments.end(); i++)
- {
- body += "--" + mainBoundary + "\r\n";
- body += "Content-Type: " + i->type + ";\r\n";
- body += " name=\"" + i->name + "\"\r\n";
- body += "Content-Transfer-Encoding: base64\r\n"
- "Content-Disposition: attachment;\r\n";
- body += " filename=\"" + i->name + "\"\r\n";
- body += "\r\n";
- body += i->data;
- body += "\r\n";
- }
- body += "--" + mainBoundary + std::string("--");
- }
- void SMTP::build_envelope_header(std::string& header)
- {
- //FIXME: Message ID
- std::string date;
- get_date(date);
- header += std::string("Date: ") + date + std::string("\r\n");
- std::string envelopeFrom = "From: ";
- format_address(fromAddress, envelopeFrom);
- envelopeFrom += "\r\n";
- header += envelopeFrom;
- add_envelope_recipent("To: ", toAddress, header);
- add_envelope_recipent("CC: ", ccAddress, header);
- std::string envelopSubject = "Subject: " + subject + "\r\n";
- header += envelopSubject;
- // If there are attachments always use this header
- if(attachments.size() > 0)
- {
- header += "MIME-Version: 1.0\r\n";
- header += "Content-Type: multipart/mixed;\r\n"
- " boundary=\"" + get_boundary(message) + "\"\r\n";
- }
- else if(htmlMessage.size())
- {
- header += "MIME-Version: 1.0\r\n";
- header += "Content-Type: multipart/alternative;\r\n"
- " boundary=\"" + get_boundary(message) + "\"\r\n";
- }
- if(returnAddress.a.length())
- {
- header += "Return-Path: ";
- format_address(returnAddress, header);
- header += "\r\n";
- }
- }
- void SMTP::format_address(Address& a, std::string& res)
- {
- if(a.friendlyName.length())
- res += a.friendlyName + std::string(" <") + a.a + std::string(">");
- else
- res += a.a;
- }
- bool SMTP::get_supported_functions()
- {
- bool lastFunction = false;
- bool successReply = false;
- while(!lastFunction)
- {
- recv_reply();
- // If the server sent all functions in one go this will probably contain them all
- std::vector<std::string> temp;
- string_explode(buffer, "\r\n", temp);
- for(std::vector<std::string>::iterator i = temp.begin(); i != temp.end(); i++)
- {
- // Set status code
- successReply = ('2' == i->at(0));
- // Skip status code (3 chars) and " " or "-"
- std::string function = i->substr(4);
- supportedFunctions.push_back(function);
- // Is last feature?
- lastFunction = (' ' == i->at(3));
- }
- }
- return successReply;
- }
- void SMTP::get_date(std::string& res)
- {
- const char* szWeekDays[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
- const char* szMonths[] = {NULL, "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
- SYSTEMTIME systemTime = {0};
- GetLocalTime(&systemTime);
- // Date in format "Weekday, Day Month Year "
- res += std::string(szWeekDays[systemTime.wDayOfWeek]) + ", ";
- add_int_to_string(res, systemTime.wDay);
- res += " ";
- res += std::string(szMonths[systemTime.wMonth]) + " ";
- add_int_to_string(res, systemTime.wYear);
- res += " ";
- // "Hour:Minute:Second"
- add_int_to_string(res, systemTime.wHour);
- res += ":";
- add_int_to_string(res, systemTime.wMinute);
- res += ":";
- add_int_to_string(res, systemTime.wSecond);
- TIME_ZONE_INFORMATION timezoneinfo;
- GetTimeZoneInformation(&timezoneinfo);
- int h = (-timezoneinfo.Bias / 60), m = (timezoneinfo.Bias % 60);
- if(h > 0)
- m = -m;
- char dateOffset[6] = {0};
- sprintf_s(dateOffset, 6, "%+03i%02u", h, m);
- res += dateOffset;
- }
- bool SMTP::send_recipents(std::vector<Address>& addresses)
- {
- for(std::vector<Address>::iterator i = addresses.begin(); i != addresses.end(); i++)
- {
- std::string address = std::string("RCPT TO: <") + i->a + std::string(">\r\n");
- sock->Send(address);
- if(!is_successful())
- return false;
- }
- return TRUE;
- }
- void SMTP::add_envelope_recipent( std::string recipentType, std::vector<Address>& addresses, std::string& res)
- {
- if(!addresses.size())
- return;
- std::string envelopeRecipents = recipentType;
- for(std::vector<Address>::iterator i = addresses.begin(); i != addresses.end(); i++)
- {
- format_address(*i, envelopeRecipents);
- envelopeRecipents += std::string(",");
- }
- envelopeRecipents = envelopeRecipents.substr(0, envelopeRecipents.length() - 1);
- envelopeRecipents += "\r\n";
- res += envelopeRecipents;
- }
- VOID SMTP::recv_reply()
- {
- #if 0
- memset(buffer, 0, sizeof(buffer));
- sock->RecvLine(buffer, length, sizeof(buffer));
- #else
- length = sizeof(buffer);
- memset(buffer, 0, sizeof(buffer));
- sock->Recv(buffer, length);
- #endif
- }
- VOID SMTP::send_NOOP()
- {
- sock->Send("NOOP\r\n", strlen("NOOP\r\n"));
- recv_reply();
- }
- VOID SMTP::add_address( Address a, AddressType type )
- {
- switch(type)
- {
- case RETURN:
- returnAddress = a;
- break;
- case FROM:
- fromAddress = a;
- break;
- case BCC:
- bccAddress.push_back(a);
- break;
- case CC:
- ccAddress.push_back(a);
- break;
- case TO:
- default:
- toAddress.push_back(a);
- break;
- }
- }
- bool SMTP::is_successful()
- {
- recv_reply();
- if(*get_reply_code() != '2')
- return false;
- return true;
- }
- bool SMTP::is_successful(char* successStatusCode)
- {
- recv_reply();
- return 0 == strcmp(get_reply_code(), successStatusCode);
- }
- bool SMTP::supports_feature(std::string feature)
- {
- for(std::vector<std::string>::iterator i = supportedFunctions.begin(); i != supportedFunctions.end(); i++)
- {
- if(i->find(feature) != std::string::npos)
- return true;
- }
- return false;
- }
- // Crypto & Auth
- bool SMTP::switch_to_TLS()
- {
- if(!is_TLS_possible())
- return false;
- sock->Send("STARTTLS\r\n");
- if(!is_successful("220"))
- {
- currentError = SSL_ERROR_SERVER_NOT_READY;
- puts(buffer);
- return false;
- }
- // Save all mandatory information about the connection
- SOCKET s = sock->GetSocket();
- // Delete the old NormalSocket
- //FIXME: delete sock;
- //
- SSLSocket* ssls = new SSLSocket();
- sock = ssls;
- ssls->Disconnect(); // free newly created socket
- sock->SetSocket(s);
- if(!ssls->StartSSL(server.c_str()))
- {
- currentError = SSL_ERROR_CONNECT;
- puts(__FUNCTION__ " Cannot connect");
- return false;
- }
- // RFC dictates that both sides must discard any information already obtained
- supportedFunctions.clear();
- std::string EHLO = "EHLO " + ehloName + "\r\n";
- sock->Send(EHLO);
- // Read in second banner
- // This response contains both banner and commands -> Read only banner (first line)
- sock->RecvLine(buffer, length, _countof(buffer));
- connected = ('2' == buffer[0]);
- if(!connected)
- {
- currentError = SSL_HELO_NOT_ACCEPTED;
- return false;
- }
- currentStatus = BANNER_RECVIVED;
- usingSSL = true;
- if(get_supported_functions())
- currentStatus = EHLO_ACCEPTED;
- return connected;
- }
- bool SMTP::is_login_possible()
- {
- return supports_feature("LOGIN") || supports_feature("AUTH");
- }
- bool SMTP::is_TLS_possible()
- {
- return supports_feature("STARTTLS");
- }
- bool SMTP::authenticate(std::string user, std::string plainPassword)
- {
- if((user.empty() && plainPassword.empty()) || !supports_feature("LOGIN"))
- return TRUE;
- // Send user
- std::string authLoginWithUser = "AUTH LOGIN ";
- append_base64(authLoginWithUser, (const unsigned char*)user.c_str(), user.length(), true);
- authLoginWithUser.append("\r\n");
- sock->Send(authLoginWithUser);
- // Did anything go wrong?
- if(!is_successful("334"))
- {
- currentError = LOGIN_NOT_POSSIBLE;
- return false;
- }
- std::string base64Pass = "";
- append_base64(base64Pass, (const unsigned char*)plainPassword.c_str(), plainPassword.length(), false);
- base64Pass.append("\r\n");
- sock->Send(base64Pass);
- // Did anything go wrong?
- if(!is_successful())
- {
- currentError = CREDENTIALS_WRONG;
- return false;
- }
- authenticated = true;
- return true;
- }
- // Initalization
- SMTP::SMTP()
- {
- length = 0;
- connected = false; // Actually having a ready connection
- currentError = NONE;
- currentStatus = NOTHING;
- sock = new NormalSocket();
- sock->SetTimeout(SMTP_CONNECT_TIMEOUT);
- }
- SMTP::~SMTP()
- {
- if(!connected)
- return;
- connected = false;
- // disconnect nicely
- sock->Send("QUIT\r\n", strlen("QUIT\r\n"));
- recv_reply();
- sock->Disconnect();
- }
- bool SMTP::connect(std::string server, std::string ehloName, bool useAuthenticatedPort)
- {
- // Possibly needed for SSL
- this->ehloName = ehloName;
- this->server = server;
- if(!sock->Connect(server.c_str(), (useAuthenticatedPort ? 587 : 25)))
- {
- currentError = CANNOT_CONNECT;
- wsprintfA(buffer, "sock->t Error: %i\n", sock->GetLastError());
- sock->Disconnect();
- return false;
- }
- currentStatus = CONNECTED;
- // Read welcome message
- // TODO: If(!strstr(reply, "ESMTP")) send("HELO");
- if(!is_successful())
- {
- currentError = REJECT_ON_CONNECT;
- return false;
- }
- currentStatus = BANNER_RECVIVED;
- std::string EHLO = "EHLO " + ehloName + "\r\n";
- sock->Send(EHLO);
- if(!get_supported_functions())
- {
- currentError = HELO_NOT_ACCEPTED;
- return false;
- }
- currentStatus = EHLO_ACCEPTED;
- connected = true;
- return true;
- }
- bool SMTP::connect(std::vector<MX_Information> servers, std::string ehloName, bool useAuthenticatedConnection)
- {
- for(std::vector<MX_Information>::iterator i = servers.begin(); i != servers.end(); i++)
- {
- if(connect(i->name, ehloName, useAuthenticatedConnection))
- {
- //printf("Y%50s (%i)\n", i->name.c_str(), i->prefernce);
- return true;
- }
- /*
- else
- {
- printf("N%50s (%i)\n", i->name.c_str(), i->prefernce);
- }*/
- }
- return false;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement