require 'socket'
require 'net/http'
require 'pathname'

# Validates and prepares invitations

# Called from invite.js.rb POST
# expects the following variables to be set:
# @pmc
# @iclaemail
# @iclaname
# @votelink
# @noticelink

# returns the following keys:
#  error
#  focus
#  token
#  pmcEmail
#  userEmail
#  invitation

# find pmc and user information
# all ppmcs are also pmcs but not all pmcs are ppmcs

pmc = ASF::Committee.find(@pmc)
ppmc = ASF::Podling.find(@pmc)
pmc_type = if ppmc and ppmc.status == 'current' then 'PPMC' else 'PMC' end

user = ASF::Person.find(env.user)

# prototype mail text
prototype_contributor =
"Based on your contributions to #{pmc.name}, you are invited to submit an ICLA
to The Apache Software Foundation, using the following form. Please see
http://apache.org/licenses for details.
"

prototype_committer =
"Congratulations! The #{pmc.name} #{pmc_type} hereby offers you committer privileges
to the #{pmc.name} project.

You can read more about what the expectations are for committers here:
http://www.apache.org/dev/committers.html#committer-responsibilities

These privileges are offered on the understanding that you'll use them
reasonably and with common sense. We like to work on trust rather than
unnecessary constraints.

Being a committer enables you to more easily make changes without needing to
go through the patch submission process.

Being a committer does not require you to participate any more than you already
do, but it does tend to make one even more committed ;-) You will probably find
that you spend more time here.

"

prototype_pmc =
"You are also invited to become a member of the #{pmc.name} PMC.
Being a PMC member enables you to help guide the direction of the project.
You can read more about what the expectations are for PMC members here:
http://www.apache.org/dev/pmc.html#audience

"

prototype_ppmc =
"You are also invited to become a member of the #{pmc.name} PPMC.
Being a PPMC member enables you to help guide the direction of the project.
You can read more about what the expectations are for PPMC members here:
https://incubator.apache.org/guides/ppmc.html

"

prototype_committer_or_pmc =
"Of course, you can decline and instead remain as a contributor, participating
as you do now.

This personal invitation is a chance for you to accept or decline in private.
Either way, please let us know in reply to the private@ address only.
"
# validate email address
if ASF::Person.find_by_email(@iclaemail)
  _error "ICLA already on file for #{@iclaemail}"
  _focus :iclaemail
  return # no point in continuing
end

begin
  Socket.getaddrinfo(@iclaemail[/@(.*)/, 1], 'smtp')
rescue
  _error 'Invalid domain name in email address'
  _focus :iclaemail
  return # no point in continuing
end

# validate vote link
if @votelink and not @votelink.empty?

# verify that the link refers to lists.apache.org message on the project list
  if not @votelink=~ /.*lists\.apache\.org.*/
    _error 'Please link to a message via lists.apache.org'
    return # no point in continuing
  end
  if not @votelink=~ /.*#{pmc.mail_list}(\.incubator)?\.apache\.org.*/
    _error 'Please link to the [RESULT][VOTE] message sent to the private list.'
    return # no point in continuing
  end

  # attempt to fetch the page
  if @votelink =~ /^https?:/i
    uri = URI.parse(@votelink)
    http = Net::HTTP.new(uri.host, uri.port)
    if uri.scheme == 'https'
      http.use_ssl = true
      http.verify_mode = OpenSSL::SSL::VERIFY_NONE
    end
    request = Net::HTTP::Head.new(uri.request_uri)
    response = http.request(request)
    unless response.code.to_i < 400
      _error "HTTP status #{response.code} for #{@votelink}"
      _focus :votelink
      return # no point in continuing
    end
  else
    _error 'Only http(s) links are accepted for vote links'
    _focus :votelink
    return # no point in continuing
  end

end

# validate notice link
if @noticelink and not @noticelink.empty?

  # verify that the link refers to lists.apache.org message on the proper list
  if not @noticelink=~ /.*lists\.apache\.org.*/
    _error 'Please link to a message via lists.apache.org'
    return # no point in continuing
  end
  if pmc_type == 'PMC' and not @noticelink=~ /.*board@apache\.org.*/
    _error 'Please link to the NOTICE message sent to the board list.'
    return # no point in continuing
  end
  if pmc_type == 'PPMC' and not @noticelink=~ /.*private@incubator\.apache\.org.*/
    _error 'Please link to the NOTICE message sent to the incubator private list.'
    return # no point in continuing
  end

  # attempt to fetch the page
  if @noticelink =~ /^https?:/i
    uri = URI.parse(@noticelink)
    http = Net::HTTP.new(uri.host, uri.port)
    if uri.scheme == 'https'
      http.use_ssl = true
      http.verify_mode = OpenSSL::SSL::VERIFY_NONE
    end
    request = Net::HTTP::Head.new(uri.request_uri)
    response = http.request(request)
    unless response.code.to_i < 400
      _error "HTTP status #{response.code} for #{@noticelink}"
      _focus :noticelink
      return # no point in continuing
    end
  else
    _error 'Only http(s) links are accepted for notice links'
    _focus :noticelink
    return # no point in continuing
  end

end

# add user and pmc emails to the response
_userEmail "#{user.public_name} <#{user.mail.first}>" if user
_pmcEmail pmc.private_mail_list if pmc

# generate an invitation token
token = Digest::MD5.hexdigest(@iclaemail)[0..15]
path = Pathname.new(env['REQUEST_URI']) + "../../form?token=#{token}"
scheme = env['rack.url_scheme'] || 'https'
link = "#{scheme}://#{env['HTTP_HOST']}#{path}"

# add token and invitation to the response
_token token
_invitation %{Dear #{@iclaname},

#{prototype_contributor if @votelink.empty?}\
#{prototype_committer if not @votelink.empty?}\
#{prototype_pmc if not @noticelink.empty? and (pmc_type == 'PMC')}\
#{prototype_ppmc if not @noticelink.empty? and (pmc_type == 'PPMC')}\
#{prototype_committer_or_pmc if not @votelink.empty?}
Click on this link to accept:
#{link}

Regards,
#{user.public_name if user}
On behalf of the #{pmc.display_name} project
}
