# signature { license=0, seed=277031123, key="7282480ab95b7c07031e7dd25061b0cf" }


local smtp =
{


}
package.loaded.smtp = smtp

local parsecs = require( 'parsecs' )
local socket = require( 'socket' )
local oauth2 = require( 'oauth2' )
local unb64 = require( 'mime' ).unb64
local log = require( 'log' )


local ServerSocket


local ListenAddress = false and '*' or '127.0.0.1'

local TLSParams = { protocol='tlsv1_2',  mode='client', verify='none' }


local function authenticate( client, server, line )
	line = line:match( 'PLAIN (.*)' ) or ''
	local user = unb64( line ):match( '%c(.+)%c' )

	if not user then
		log( 'SMTP', 'Authentication failed:', 'no user' )
	else

		local reply, last
		for try = 1, 2 do
			line = 'AUTH XOAUTH2 ' .. oauth2.xoauth2( user )
			server:send( line .. '\r\n' )

			while true do

				repeat
					line = server:receive()
					if not line then return end

					reply, last = line:match( '^(...)(.?)' )
				until last ~= '-'

				if reply == '334' then


					server:send( '\r\n' )
				else
					break
				end
			end

			if reply == '235' then

				client:send( line .. '\r\n' )

				return true
			end

			if try == 2 then break end


			oauth2.useRefreshToken()
		end

		log( 'SMTP', 'Authentication failed:' )
	end


	client:send( '535 5.7.1 Authentication failed' )
end




local function SmtpHandler( client )
	local security = _G.cfg.SmtpSecurity or 'none'


	local server = parsecs.wrap( socket.tcp(), security == 'tls' and TLSParams or nil )
	local ok, whynot = server:connect( _G.cfg.SmtpServer, _G.cfg.SmtpServerPort or 465 )
	if not ok then
		log( 'SMTP', 'Could not connect to SMTP server:', whynot )
		client:send( '554 cannot connect to SMTP server' )
		return
	end

	while true do
		local nodata, line


		repeat
			line = server:receive()
			if not line then goto exit end

			local reply, last, param = line:match( '^(...)(.?)(.*)' )
			if param:find( '^AUTH' ) then

				line = reply .. last .. 'AUTH PLAIN'
			elseif param:find( '^STARTTLS' ) then

				line = reply .. last .. 'STRIPTLS'
			end
			client:send( line .. '\r\n' )



			nodata = reply ~= '354'


		until last ~= '-'


		repeat
			while true do
				line = client:receive()
				if not line then goto exit end

				local cmd, param = line:match( '^(%w+) ?(.*)' )
				if cmd == 'AUTH' then
					authenticate( client, server, param )
				else
					if cmd == 'EHLO' and security == 'starttls' then

						server:send( line .. '\r\n' )

						repeat
							local resp = server:receive()
							if not resp then goto exit end
							local last = resp:match( '^...(.?)' )
						until last ~= '-'


						server:send( 'STARTTLS\r\n' )
						server:receive()


						server:dohandshake( TLSParams )


					end

					server:send( line .. '\r\n' )
					break
				end
			end
		until nodata or line == '.'
	end

	::exit::
	server:close()
	client:close()
end



function smtp.stop()
	if ServerSocket then ServerSocket:close() end
end



function smtp.start()
	if ( _G.cfg.SmtpClientPort or 0 ) > 0 then
		ServerSocket = socket.bind( ListenAddress, _G.cfg.SmtpClientPort or 465 )
		if ServerSocket then
			parsecs.addserver( ServerSocket, SmtpHandler )
		else
			log( "Could not bind to SMTP socket" )
		end
	else
		log( "SMTP client port?", tostring(_G.cfg.SmtpClientPort ) )
	end
end

return smtp
