# signature { license=0, seed=277031123, key="741f08df1543b93d4598c7401b8aaadd" }


local pop3 =
{


}
package.loaded.pop3 = pop3

local parsecs = require( 'parsecs' )
local socket = require( 'socket' )
local oauth2 = require( 'oauth2' )
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, user )
	local line

	for try = 1, 2 do

		server:send( 'AUTH XOAUTH2\r\n' )


		line = server:receive() or ''
		if not line:find( '^%+' ) then break end


		line = oauth2.xoauth2( user )
		server:send( line .. '\r\n' )


		line = server:receive() or ''
		if not line then break end

		if line:find( '^%+OK' ) then

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

			return true
		end

		if try == 2 then break end


		oauth2.useRefreshToken()
	end


	client:send( '-ERR Authentication failed' )
	log( 'POP3', 'Authentication failed' )
end




local hasdata = { TOP = 1, RETR = 1, UIDL = 1, CAPA = 1 }



local function pop3Handler( client )
	local security = 'tls'


	local server = parsecs.wrap( socket.tcp(), security == 'tls' and TLSParams or nil )

	local ok, whynot = server:connect( _G.cfg.Pop3Server, _G.cfg.Pop3ServerPort or 995 )
	if not ok then
		log( 'POP3', 'Could not connect to POP3 server:', whynot )
		return
	end



	local startTLS = security == 'starttls'

	local data = false
	while true do
		local user, line


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


		if not startTLS then
			client:send( line .. '\r\n' )
		end


		data = data and not line:find( '^-ERR' )


		if data then
			while line ~= '.' do
				line = server:receive()
				if not line then goto exit end


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


		if startTLS then
			server:send( 'STLS\r\n' )
			line = server:receive()
			if not line then goto exit end
			server:dohandshake( TLSParams )


			startTLS = false
		else



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

				local cmd, param = line:match( '^(%w+) ?(.*)' )

				if cmd == 'USER' then
					user = param
					client:send( '+OK\r\n' )
				elseif cmd == 'PASS' then
					authenticate( client, server, user )
				else
					server:send( line .. '\r\n' )
					data = hasdata[ cmd ]
					break
				end
			end
		end
	end

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



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



function pop3.start()
	local error
	if ( _G.cfg.Pop3ClientPort or 0 ) > 0 then
		ServerSocket, error = socket.bind( ListenAddress, _G.cfg.Pop3ClientPort or 995 )
		if ServerSocket then
			parsecs.addserver( ServerSocket, pop3Handler )
		else
			log( 'POP3', 'Could not bind to POP3 socket:', error )
		end
	else
		log( "POP3 client port?", tostring(_G.cfg.Pop3ClientPort ) )
	end
end

return pop3
