# signature { license=0, seed=277031123, key="07b85a99ead3c1f7fa914a1f893c8f7d" }













local oauth2 =
{





}
package.loaded.oauth2 = oauth2




local base64 = require( 'mime' ).b64
local socket = require( 'socket' )
local format = string.format
local dump = _G.dump or function() end
local log = require( 'log' )



local accessToken






local requestf =
'%s /%s HTTP/1.1\r\n\z
 %sHost: %s\r\n\z
 Content-Length: %d\r\n\r\n%s'

local function http( method, url, headers, body )
	local scheme, host, port, path = url:match('^(https?)://([^:/]+):?(%d*)/?(.*)')
	local tls = scheme == 'https'
	if port == '' then port = tls and 443 or 80 end
	body = body or ''

	local sock = socket.tcp()
	sock:settimeout( 10 )

	local res, reason = sock:connect( host, tonumber( port ) )
	if not res then
		return nil, "Could not establish TCP connection to server, error:"..reason
	end

	local ok, err
	if tls then
		sock, err = require( 'ssl' ).wrap( sock, { protocol='tlsv1_2', mode='client', verify='none' } )
		if not sock then return nil, err end


		sock:settimeout( 10 )

		ok, err = sock:dohandshake()
		if not ok then return nil, err end
	end

	local request = requestf:format( method, path, headers or '', host, #body, body )
	sock:send( request )
	dump( request )


	local status = sock:receive()


	local D = { status }


	local length, chunked
	repeat
		local line = sock:receive()
		if not line then break end
		table.insert( D, line )
		line = line:lower()
		length = length or tonumber( line:match( 'content%-length%s*:%s*(%d+)' ) )
		chunked = chunked or line:match( 'transfer%-encoding%s*%:%s*(chunked)' )
	until line == ''


	local response = ''

	if length then
		response = sock:receive( length ) or ''
	end

	while chunked do

		length = sock:receive()
		table.insert( D, length )
		length = tonumber( length, 16 )


		if ( length or 0 ) == 0 then break end


		local chunk = sock:receive( length ) or ''
		table.insert( D, chunk..'\r' )
		response = response .. ( chunk )


		sock:receive( 2 )
	end

	sock:close()
	table.insert( D, response )
	dump( table.concat( D, '\n' ) )

	return response, status, err
end



local decode = function( json )
	json = json or '{}'


	json = json:gsub( '%[', '{' ):gsub( '%]', '}' )


	json  = json:gsub( '"([^"]+)"[ \t]*:', '%1=' )


	local code, error = load( 'return '..json, '','t', {} )

	if code then return code() end

	dump( error )

	return {}
end



local function checkRefreshToken( json )
	local token = json.refresh_token
	if token then
		_G.cfg.RefreshToken = token
		_G.cfg:write()
	end

	return token
end



function oauth2.urlencode( url )
	return url:gsub( '([^%w._~-])', function( c ) return ('%%%02X'):format( c:byte() ) end )
end



function oauth2.useAuthorizationCode( code )
	local enc = oauth2.urlencode
	local headers = 'Content-Type: application/x-www-form-urlencoded\r\n'
	local body = format
	(
		'code=%s&client_id=%s&client_secret=%s&redirect_uri=%s&grant_type=authorization_code&scope=%s&access_type=offline' ,
		enc( code ) ,
		enc( _G.cfg.ClientId ) ,
		enc( _G.cfg:password( 'Client' ) ) ,
		enc( _G.cfg.RedirectURI ) ,
		enc( _G.cfg.Scopes )
	)

	local response, problem = http( 'POST', _G.cfg.TokenServerURL, headers, body )
	if not response then
		log( 'OAuth2.0', problem )
		return
	end

	local json = decode( response )
	if json.error then
		log( 'OAuth2.0', 'Authorization failed:', json.error )
		return
	end

	local token = json.access_token
	if token then
		accessToken = token
	else
		log( 'OAuth2.0', 'Authorization yielded no access token' )
		return
	end

	token = checkRefreshToken( json )
	if not token then
		log( 'OAuth2.0', 'Authorization yielded no refresh token' )
	end


	return accessToken, token
end




oauth2.useRefreshToken = function()
	if not _G.cfg.RefreshToken then return nil, "no refresh token, did you authorize?" end

	local enc = oauth2.urlencode

	local headers = 'Content-Type: application/x-www-form-urlencoded\r\n'
	local body = format
	(
		'client_id=%s&client_secret=%s&refresh_token=%s&grant_type=refresh_token' ,
		enc( _G.cfg.ClientId ) ,
		enc( _G.cfg:password( 'Client' ) ) ,
		enc( _G.cfg.RefreshToken )
	)

	local response = http( 'POST', _G.cfg.TokenServerURL, headers, body )
	local json = decode( response )
	if json.error then
		log( 'OAuth2.0', 'Refresh failed:', json.error )
		return
	end

	local token = json.access_token
	if token then
		accessToken = token
		checkRefreshToken( json )
	else
		log( 'OAuth2.0', 'Refresh yielded no access token' )
	end

	return accessToken
end




oauth2.getAccessToken = function()
	if not accessToken then oauth2.useRefreshToken() end
	return accessToken
end



oauth2.xoauth2 = function( userName )
	local text = 'user=%s\1auth=Bearer %s\1\1'
	return base64( text:format( userName, oauth2.getAccessToken() ) )
end


return oauth2
