require 'crypto_methods'

class OpenidController < ApplicationController

  def login
    if params[:cancel]
      redirect_to params[:fail_to]
    elsif params[:user]
      cookies['user'] = params[:user]
      redirect_to params[:success_to]
    end
  end

  def identity
     @user = params[:user]
  end

  def whoami
     @user = cookies[:user]
  end

  def loggedin
     @user = cookies[:user]
  end

  def server
    bad_request :mode unless respond_to? "server_" + openid.mode

    send "server_" + openid.mode

  rescue BadRequest => error
    @field = error.field
    render(:file => "#{RAILS_ROOT}/public/400.html",
      :status => '400 Bad Request')
  end

  def server_associate
    openid.assoc_type ||= 'HMAC-SHA1'
    bad_request :assoc_type unless openid.assoc_type=='HMAC-SHA1'

    assoc = Assoc.new

    if openid.session_type == 'DH-SHA1'
      dh = DiffieHellman.new
      dh.p = openid.dh_modulus.unbase64.unbtwoc if openid.dh_modulus
      dh.g = openid.dh_gen.unbase64.unbtwoc     if openid.dh_gen
      dh_shared = dh.decryptKeyExchange openid.dh_consumer_public!.unbase64.unbtwoc

      reply = {
        :session_type => openid.session_type!,
        :dh_server_public => dh.createKeyExchange.btwoc.base64,
        :enc_mac_key => (assoc.secret ^ dh_shared.btwoc.sha1).base64
      }
    elsif [nil,''].include? openid.session_type
      reply = { :mac_key => assoc.secret.base64 }
    else
      bad_request :session_type
    end

    reply.update(
      :assoc_type => openid.assoc_type,
      :assoc_handle => assoc.handle,
      :expires_in => assoc.expires_in
    )

    assoc.save!
    render :text => reply.to_kv
  end

  def bad_request field
    bad_request 'openid.'+field.to_s if field.is_a? Symbol
    raise BadRequest.new(field.sub(/[?=!]$/, ''))
  end

private

  class BadRequest < Exception
    attr_reader :field
    def initialize field
      @field = field
    end
  end

  def openid
    if ! @openid
      @openid = params.dup
      @openid['openid.controller'] = self
      def @openid.method_missing symbol, *args
        if symbol.to_s[-1] == ?=
          self['openid.'+symbol.to_s[0..-2]]=args[0]
        elsif symbol.to_s[-1] == ?!
          self['openid.'+symbol.to_s[0..-2]] or controller.bad_request(symbol)
        else
          self['openid.'+symbol.to_s]
        end
      end
    end
    @openid
  end

end
