// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
// All rights reserved.
// 
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of the Andrey N. Sabelnikov nor the
// names of its contributors may be used to endorse or promote products
// derived from this software without specific prior written permission.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER  BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// 




#pragma once
#include <iostream>
#include <istream>
#include <ostream>
#include <string>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/archive/iterators/base64_from_binary.hpp>
#include <boost/archive/iterators/transform_width.hpp>
#include <boost/archive/iterators/ostream_iterator.hpp>


namespace epee
{
namespace net_utils
{
	namespace smtp
	{

		using boost::asio::ip::tcp;
		using namespace boost::archive::iterators;
		typedef base64_from_binary<transform_width<const char *,6,8> > base64_text;
	
		/************************************************************************/
		/*                                                                      */
		/************************************************************************/
		class smtp_client
		{
		public:
			smtp_client(std::string pServer,unsigned int pPort,std::string pUser,std::string pPassword):
			  mServer(pServer),mPort(pPort),mUserName(pUser),mPassword(pPassword),mSocket(mIOService),mResolver(mIOService)
			  {
				  tcp::resolver::query qry(mServer,boost::lexical_cast<std::string>( mPort ));
				  mResolver.async_resolve(qry,boost::bind(&smtp_client::handleResolve,this,boost::asio::placeholders::error,
					  boost::asio::placeholders::iterator));
			  }
			  bool Send(std::string pFrom,std::string pTo,std::string pSubject,std::string pMessage)
			  {
				  mHasError = true;
				  mFrom=pFrom;
				  mTo=pTo;
				  mSubject=pSubject;
				  mMessage=pMessage;
				  mIOService.run();
				  return !mHasError;
			  }
		private:
			std::string encodeBase64(std::string pData)
			{
				std::stringstream os;
				size_t sz=pData.size();
				std::copy(base64_text(pData.c_str()),base64_text(pData.c_str()+sz),std::ostream_iterator<char>(os));
				return os.str();
			}
			void handleResolve(const boost::system::error_code& err,tcp::resolver::iterator endpoint_iterator)
			{
				if(!err)
				{
					tcp::endpoint endpoint=*endpoint_iterator;
					mSocket.async_connect(endpoint,
						boost::bind(&smtp_client::handleConnect,this,boost::asio::placeholders::error,++endpoint_iterator));
				}
				else
				{
					mHasError=true;
					mErrorMsg= err.message();
				}
			}
			void writeLine(std::string pData)
			{
				std::ostream req_strm(&mRequest);
				req_strm << pData << "\r\n";
				boost::asio::write(mSocket,mRequest);
				req_strm.clear();
			}
			void readLine(std::string& pData)
			{
				boost::asio::streambuf response;
				boost::asio::read_until(mSocket, response, "\r\n");
				std::istream response_stream(&response);
				response_stream >> pData;
			}
			void handleConnect(const boost::system::error_code& err,tcp::resolver::iterator endpoint_iterator)
			{
				if (!err)
				{
					std::string read_buff;
					// The connection was successful. Send the request.
					std::ostream req_strm(&mRequest);
					writeLine("EHLO "+mServer);
					readLine(read_buff);//220
					writeLine("AUTH LOGIN");
					readLine(read_buff);//
					writeLine(encodeBase64(mUserName));
					readLine(read_buff);
					writeLine(encodeBase64(mPassword));
					readLine(read_buff);
					writeLine( "MAIL FROM:<"+mFrom+">");
					writeLine( "RCPT TO:<"+mTo+">");
					writeLine( "DATA");
					writeLine( "SUBJECT:"+mSubject);
					writeLine( "From:"+mFrom);
					writeLine( "To:"+mTo);
					writeLine( "");
					writeLine( mMessage );
					writeLine( "\r\n.\r\n");
					readLine(read_buff);
					if(read_buff == "250")
						mHasError = false;
					writeLine( "QUIT");
				}
				else
				{
					mHasError=true;
					mErrorMsg= err.message();
				}
			}
			std::string mServer;
			std::string mUserName;
			std::string mPassword;
			std::string mFrom;
			std::string mTo;
			std::string mSubject;
			std::string mMessage;
			unsigned int mPort;
			boost::asio::io_service mIOService;
			tcp::resolver mResolver;
			tcp::socket mSocket;
			boost::asio::streambuf mRequest;
			boost::asio::streambuf mResponse;
			bool mHasError;
			std::string mErrorMsg;
		};


		bool send_mail(const std::string& server, int port, const std::string& login, const std::string& pass, const std::string& from_email, /*"STIL CRAWLER",*/
			const std::string& maillist, const std::string& subject, const std::string& body)
		{
			STD_TRY_BEGIN();
			//smtp_client mailc("yoursmtpserver.com",25,"user@yourdomain.com","password");
			//mailc.Send("from@yourdomain.com","to@somewhere.com","subject","Hello from C++ SMTP Client!");
			smtp_client mailc(server,port,login,pass);
			return mailc.Send(from_email,maillist,subject,body);
			STD_TRY_CATCH("at send_mail", false);
		}

	}
}
}

//#include "smtp.inl"