-- -----------------------------------------------------------------------------
-- Copyright 2002, Simon Marlow.
-- Copyright 2006, Bjorn Bringert.
-- All rights reserved.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions are
-- met:
--
--  * Redistributions of source code must retain the above copyright notice,
--    this list of conditions and the following disclaimer.
--
--  * Redistributions in binary form must reproduce the above copyright
--    notice, this list of conditions and the following disclaimer in the
--    documentation and/or other materials provided with the distribution.
--
--  * Neither the name of the copyright holder(s) nor the names of
--    contributors may be used to endorse or promote products derived from
--    this software without specific prior written permission.
--
-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-- OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-- -----------------------------------------------------------------------------

module AccessLogger (
                     AccessLoggerHandle,
                     AccessLogRequest(..),
                     startAccessLogger,
                     stopAccessLogger,
                     mkAccessLogRequest,
                     logAccessLogRequest
                    ) where

import Logger
import Headers
import Response
import ServerRequest
import Util

import Network.BSD (HostEntry, hostName)
import Network.Socket (inet_ntoa)
import System.Time
import System.IO.Unsafe (unsafePerformIO)

type AccessLoggerHandle = LoggerHandle AccessLogRequest

data AccessLogRequest = AccessLogRequest
    {
     log_request        :: ServerRequest,
     log_response       :: Response,
     log_server_host    :: HostEntry,
     log_time           :: ClockTime,
     log_delay          :: TimeDiff
    }


startAccessLogger :: String -> FilePath -> IO AccessLoggerHandle
startAccessLogger format file = startLogger f file
  where f = mkLogLine format

mkLogLine :: String -> AccessLogRequest -> String
mkLogLine "" _ = ""
mkLogLine ('%':'{':rest) r =
    case span (/= '}') rest of
      (str, '}':c:rest1) -> expand (Just str) c r ++ mkLogLine rest1 r
      _                  -> '%':'{':mkLogLine rest r
mkLogLine ('%':c:rest) r = expand Nothing c r ++ mkLogLine rest r
mkLogLine (c:rest) r = c : mkLogLine rest r

expand :: Maybe String -> Char -> AccessLogRequest -> String
expand arg c info =
          case c of
            'b' -> let len = responseBodyLength (respBody resp)
                    in if len == 0 then "-" else show len
            'f' -> serverFilename sreq

            -- %h is the hostname if hostnameLookups is on, otherwise the
            -- IP address.
            'h' -> maybe addr hostName (clientName sreq)
            'a' -> addr
            'l' -> "-" -- FIXME: does anyone use identd these days?
            'r' -> show req
            -- ToDo: 'p' -> canonical port number of server
            's' -> show (respCode resp)
            't' -> formatTimeSensibly (toUTCTime (log_time info))
            'T' -> timeDiffToString (log_delay info)
            'v' -> hostName (log_server_host info)
            'u' -> "-" -- FIXME: implement HTTP auth

            'i' -> header req arg
            'o' -> header resp arg

            -- ToDo: other stuff
            _ -> ['%',c]
  where
   resp = log_response info
   sreq = log_request info
   req  = clientRequest sreq
--   host = clientName (log_request info)
   header _ Nothing  = ""
   header x (Just n) = unwords (lookupHeaders (mkHeaderName n) x)
   addr = unsafePerformIO (inet_ntoa (clientAddress sreq))

stopAccessLogger :: AccessLoggerHandle -> IO ()
stopAccessLogger l = stopLogger l

mkAccessLogRequest :: ServerRequest -> Response -> HostEntry -> TimeDiff -> IO AccessLogRequest
mkAccessLogRequest req resp host delay =
    do time <- getClockTime
       return $ AccessLogRequest
                  {
                   log_request     = req,
                   log_response    = resp,
                   log_server_host = host,
                   log_time        = time,
                   log_delay       = delay
                  }

logAccessLogRequest :: AccessLoggerHandle -> AccessLogRequest -> IO ()
logAccessLogRequest l r = logMessage l r
