# Copyright (C) 2003-2005 Kouichirou Eto, All rights reserved.

$LOAD_PATH << "../../lib" unless $LOAD_PATH.include?("../../lib")
require "qwik/mail"
require "qwik/password"
require "qwik/act-httpauth"
require "qwik/act-session"
require "qwik/act-typekey"
require "qwik/act-notice"

module Qwik
  class Action
    # ============================== show status
    def plg_login_status
      #return [:a, {:href=>".login"}, _("Login")] if @req.user.nil?
      # remove gettext for a while.
      return [:a, {:href=>".login"}, ("Login")] if @req.user.nil?
      [:span, {:class=>"loginstatus"},
	"user", " | ", plg_login_user,
#	" (", [:a, {:href=>".logout"}, _("Logout")], ")"]
	" (", [:a, {:href=>".logout"}, ("Logout")], ")"]
    end

    def plg_login_user
      [:em, @req.user]
    end

    # ============================== verify
    def get_user
      check_session # Check session id.
      return if @req.user

      check_cookie # Get user from cookie.
      return if @req.user

      check_basicauth # Get user from basicauth.
      return if @req.user
    end

    def check_cookie
      user = @req.cookies["user"]
      pass = @req.cookies["pass"]
      if user
	return if user.nil? || user == ""
	return unless MailAddress.new(user).valid?
	gen = @memory.passgen
	return if !gen.match?(user, pass)

	@req.user = user
	@req.auth = "cookie"

	# move to session id.  # comment out for now.
	# sid = session_store(user)
	# @res.set_cookie("sid", sid) # Set Session id by cookie
      end
    end

    def nu_check_user(user, pass)
      raise InvalidUserError if user.nil? || user == ""
      raise InvalidUserError unless MailAddress.new(user).valid?
      gen = @memory.passgen
      raise InvalidUserError if !gen.match?(user, pass)
    end

    def login_invalid_user
      c_nerror(_("Login Error")){[
	  [:p, [:strong, _("Invalid ID(E-mail) or Password.")]],
	  [:p, {:class=>"warning"},
	    _("If you don't have password, "), _("access here"), [:br],
	    [:a, {:href=>".getpass"}, [:em, _("Get Password")]]],
	  login_page_form,
	  login_page_menu,
	]}
    end

    # ============================== login
    def act_login
      if @req.user
	return login_already_logged_in(@req.user)
      end

      user = @req.query["user"] # login from query
      pass = @req.query["pass"]

      if !user
	return c_notice(_("Login")){
	  login_page(@site.url) # show login page
	}
      end

      begin
	raise InvalidUserError if user.nil? || user == ""
	raise InvalidUserError unless MailAddress.new(user).valid?
	gen = @memory.passgen
	raise InvalidUserError if !gen.match?(user, pass)

      rescue InvalidUserError
	@res.clear_cookies # important
	return login_invalid_user # password does not match
      end

      sid = session_store(user)
      @res.set_cookie("sid", sid) # Set Session id by cookie.

      return c_notice(_("Login")+" "+_("succeed"), "FrontPage.html"){[
	  [:p,_("Go next")," : ",[:a,{:href=>"FrontPage.html"},"FrontPage"]]
	]}
    end

    def login_already_logged_in(user)
      ar = []
      ar << [:p, _("You are now logged in as this user id."), [:br],
	[:strong, user]]
      ar << [:p, _("If you would like to login as another account,"), [:br],
	_("do logout at the first.")]
      ar << logout_form
      ar << [:hr]
      ar << [:p,_("Go back")," : ",[:a,{:href=>"FrontPage.html"},"FrontPage"]]
      return c_nerror(_("Already logged in")){ar}
    end

    def login_form_div
      [:div, {:class=>"login"},
	[:form, {:method=>"POST", :action=>".login"},
	  [:table,
	    [:tr, [:th, _("ID"), "(E-mail)", ": "],
	      #[:td, [:input, {:name=>"user", :istyle=>"3"}]]],
	      [:td, [:input, {:name=>"user", :istyle=>"3", :class=>"focus"}]]],
	    [:tr, [:th, _("Password"), ": "],
	      [:td, [:input, {:type=>"password", :name=>"pass"}]]]],
	  [:p, {:class=>"button"},
	    [:input, {:type=>"submit", :value=>_("Login")}]]]]
    end

    def login_page_form
      [:div, {:class=>"login"},
	[:form, {:method=>"POST", :action=>".login"},
	  [:dl,
	    [:dt, _("ID"), "(E-mail)", ": "],
	    #[:dd, [:input, {:name=>"user", :istyle=>"3"}]],
	    [:dd, [:input, {:name=>"user", :istyle=>"3", :class=>"focus"}]],
	    [:dt, _("Password"), ": "],
	    [:dd, [:input, {:type=>"password", :name=>"pass"}]]],
	  [:p,
	    [:input, {:type=>"submit", :value=>_("Login")}]]]]
    end

    def login_page_menu
      [:ul,
	[:li, _("If you don't have password"), " : ",
	  [:a, {:href=>".getpass"}, [:em, _("Get Password")]]],
	[:li, _("For mobile phone user"), " : ",
	  [:a, {:href=>".basicauth"}, _("Login by Basic Auth")]],
	[:li,
	  [:a, {:href=>".typekey"}, _("Login by TypeKey")]]]
    end

    def login_page(url)
      [
	[:p, _("Login to "), [:em, url], [:br],
	  _("Please input ID(E-mail) and password.")],
	login_page_form,
	login_page_menu,
      ]
    end

    # ============================== Logout
    def act_logout
      return basicauth_logout if @req.auth == "basicauth"

      confirm = @req.query["confirm"]
      if confirm.nil? || confirm != "yes"
	return logout_show_confirm
      end

      session_clear

      @res.clear_cookie("user") # remove cookies from browser
      @res.clear_cookie("pass")
      @res.clear_cookie("sid")

      return c_notice(_("Logout done."), "FrontPage.html"){[
	  [:p,_("Go back")," : ",[:a,{:href=>"FrontPage.html"},"FrontPage"]]
	]}
    end

    def logout_show_confirm
      c_notice(_("Logout")+" "+_("Confirm")){[
	  [:p, _('Push "Do Logout".')],
	  logout_form,
	  [:hr],
	  [:p,_("Go next")," : ",[:a,{:href=>"FrontPage.html"},"FrontPage"]],
	]}
    end

    def logout_form
      [:div, {:class=>"logout"},
	[:form, {:method=>"POST", :action=>".logout"},
	  [:input, {:type=>"hidden", :name=>"confirm", :value=>"yes"}],
	  #[:input, {:type=>"submit", :value=>_("Do Logout")}]]]
	  [:input, {:type=>"submit", :value=>_("Do Logout"), :class=>"focus"}]]]
    end
  end
end

if $0 == __FILE__
  require "t/test"
  $debug = true
end

if $debug
  class TC_ActLogin < Test::Unit::TestCase
    include TestSession

    def assert_cookie(hash, cookies)
      cookies.each {|cookie|
	assert_equal(cookie.value, hash[cookie.name])
      }
    end

    def test_private_site
      t_add_user

      # See FrontPage.
      session("/test/") {|req|
	req.cookies.clear
      }
      assert_text("Login", "title")
      assert_xpath([:meta, {:content=>"0; url=.login",
		       "http-equiv"=>"Refresh"}],
		   "//meta[2]")

      # See login page.
      session("/test/.login") {|req|
	req.cookies.clear
      }
      assert_text("Login", "title")
      assert_xpath([:input, {:istyle=>"3", :name=>"user"}], "//input")
      assert_xpath([:a, {:href=>".getpass"}, [:em, "Get Password"]], "//a")
      assert_cookie({"user"=>"", "pass"=>""}, @res.cookies)

      # Get password by e-mail.  See act-getpass.

      # Invalid mail address
      session("/test/.login?user=test@example") {|req|
	req.cookies.clear
      }
      assert_text("Invalid ID(E-mail) or Password.", "p")

      # Invalid password
      session("/test/.login?user=user@e.com&pass=wrongpassword") {|req|
	req.cookies.clear
      }
      assert_text("Invalid ID(E-mail) or Password.", "p")

      # Login by GET method. Set cookies and redirect to FrontPage.
      session("/test/.login?user=user@e.com&pass=95988593") {|req|
	req.cookies.clear
      }
      assert_text("Login succeed", "title")
      #assert_cookie({"user"=>"user@e.com", "pass"=>"95988593"}, @res.cookies)
      assert_equal("sid", @res.cookies[0].name)
      assert_equal(32, @res.cookies[0].value.length)
      #pw("//head")
      assert_xpath([:meta, {:content=>"0; url=FrontPage.html",
		       "http-equiv"=>"Refresh"}],
		   "//meta[2]") # force redirect for security reason.

      # Set the cookie
      session("/test/") {|req|
	req.cookies.update({"user"=>"user@e.com", "pass"=>"95988593"})
      }
      assert_text("FrontPage", "title")
      assert_cookie({"user"=>"user@e.com", "pass"=>"95988593"},
		    @res.cookies)
#      assert_equal("sid", @res.cookies[0].name)
#      assert_equal(32, @res.cookies[0].value.length)

      # Use POST method to set user and pass by queries.
      session("POST /test/.login?user=user@e.com&pass=95988593") {|req|
	req.cookies.clear
      }
      assert_text("Login succeed", "title")
      assert_equal(200, @res.status)
      #assert_cookie({"user"=>"user@e.com", "pass"=>"95988593"}, @res.cookies)
      assert_equal("sid", @res.cookies[0].name)
      assert_equal(32, @res.cookies[0].value.length)
      assert_xpath([:meta, {:content=>"0; url=FrontPage.html",
		       "http-equiv"=>"Refresh"}],
		   "//meta[2]") # force redirect for security reason.

      # test_login_status
      session("/test/")
      assert_xml(["user", " | ", [:em, "user@e.com"],
		   " (", [:a, {:href=>".logout"}, "Logout"], ")"],
		 "//span[@class='loginstatus']")

      # See TextFormat
      session("/test/TextFormat.html")

      # See the Logout page.
      session("/test/.logout")
      assert_text("Logout Confirm", "title")
      assert_attr({"action"=>".logout", "method"=>"POST"}, "form")
      assert_attr({"name"=>"confirm", "type"=>"hidden", "value"=>"yes"},"input")
      assert_attr({"type"=>"submit", "value"=>"Do Logout"}, "input[2]")

      # Confirm Logout.
      session("/test/.logout?confirm=yes")
      assert_text("Logout done.", "title")
      assert_text("Logout done.", "h1")
      assert_xpath([:p, "Go back", " : ",
		     [:a, {:href=>"FrontPage.html"}, "FrontPage"]],
		 "//div[@class='section']/p")
      #assert_cookie({"user"=>"", "pass"=>""}, @res.cookies)
      assert_cookie({"user"=>"", "pass"=>"", "sid"=>""}, @res.cookies)
      #pp @res.cookies
#      assert_equal("sid", @res.cookies[0].name)
#      assert_equal(32, @res.cookies[0].value.length)
    end

    def test_open_site
      t_add_user
      t_site_open # OPEN site

      # See FrontPage. Check login_status before login.
      session("/test/") {|req|
	req.cookies.clear
      }
      assert_text("FrontPage", "title")
      assert_xml("Login", "//div[@class='adminmenu']//a")
      assert_xml([[:a, {:href=>".login"}, "Login"], ["\n"], ["\n"]],
		 "//div[@class='adminmenu']")

      # You can see login page before login.
      session("/test/.login") {|req|
	req.cookies.clear
      }
      assert_text("Login", "title")
    end
  end
end
