-- | Internal module for retrieving languages to localize to.
-- Also provides decoupling layers between Network.URI.Messages & optional dependencies.
{-# LANGUAGE CPP #-}
module Network.URI.Locale(rfc2616Locale
#ifdef WITH_HTTP_URI
, transHttp
#endif
) where
import System.Environment (lookupEnv)
import Control.Monad (forM)
import Data.Maybe (mapMaybe, fromMaybe)
import Data.Char (toLower)
#ifdef WITH_HTTP_URI
import Network.HTTP.Client (HttpException(..), HttpExceptionContent(..))
import Control.Exception (displayException)
import Network.TLS (TLSException(..), TLSError(..), AlertDescription(..))
import Control.Exception.Base (fromException)
import Network.HTTP.Types (Status(..))
import Network.URI.Messages
import qualified Data.ByteString.Char8 as C8
import qualified Data.Text as Txt
import Text.Read (readMaybe)
#endif
--- This file is based on logic in GNOME's LibSoup & GLib.
-- | Returns the languages to which responses should be localized.
-- Retrieved from Gettext configuration & reformatted for use in the
-- HTTP Accept-Language request header.
rfc2616Locale :: IO ([String], [String])
rfc2616Locale = do
locales <- forM ["LANGUAGE", "LC_ALL", "LC_MESSAGES", "LANG"] lookupEnv
let posix = split ":" $ firstJust locales "en_US"
let ietf = mapMaybe toRFC2616Lang posix
return (explode ietf, explode posix)
toRFC2616Lang "C" = Nothing
toRFC2616Lang ('C':'.':_) = Nothing
toRFC2616Lang ('C':'@':_) = Nothing
toRFC2616Lang lang = case toRFC2616Lang' lang of
"" -> Nothing
lang' -> Just lang'
toRFC2616Lang' ('_':cs) = '-' : toRFC2616Lang' cs
toRFC2616Lang' ('.':_) = []
toRFC2616Lang' ('@':_) = []
toRFC2616Lang' (c:cs) = toLower c : toRFC2616Lang' cs
toRFC2616Lang' [] = []
-- Makes sure to include the raw languages, and not just localized variants.
extractLangs (locale:locales) | (lang:_) <- split "-_.@" locale = lang : extractLangs locales
extractLangs (_:locales) = extractLangs locales
extractLangs [] = []
explode locales = locales ++ [l | l <- extractLangs locales, l `notElem` locales]
firstJust (Just a:_) _ | a /= "" = a
firstJust (_:maybes) fallback = firstJust maybes fallback
firstJust [] fallback = fallback
split b (a:as) | a `elem` b = [] : split b as
| (head':tail') <- split b as = (a:head') : tail'
| otherwise = [a:as]
split _ [] = [[]]
--------
---- Decoupling Layer
--------
#ifdef WITH_HTTP_URI
transHttp locales (InvalidUrlException url msg) = trans locales $ InvalidUrl url msg
transHttp locales (HttpExceptionRequest _ (TooManyRedirects _)) = trans locales $ ExcessiveRedirects
transHttp locales (HttpExceptionRequest _ ResponseTimeout) = trans locales $ TimeoutResponse
transHttp locales (HttpExceptionRequest _ ConnectionTimeout) = trans locales $ TimeoutConnection
transHttp locales (HttpExceptionRequest _ (ConnectionFailure err)) =
trans locales $ FailedConnect $ displayException err
transHttp locales (HttpExceptionRequest _ (StatusCodeException _ code)) =
trans locales $ HTTPStatus (fromMaybe 500 $ readMaybe $ C8.unpack code) ""
transHttp locales (HttpExceptionRequest _ OverlongHeaders) =
trans locales $ HTTPStatus 431 "Overlong Headers"
transHttp locales (HttpExceptionRequest _ (InvalidStatusLine why)) =
trans locales $ MalformedResponse $ C8.unpack why
transHttp locales (HttpExceptionRequest _ (InvalidHeader why)) =
trans locales $ MalformedResponse $ C8.unpack why
transHttp locales (HttpExceptionRequest _ (InvalidRequestHeader why)) =
trans locales $ InvalidRequest $ C8.unpack why
transHttp locales (HttpExceptionRequest _ (ProxyConnectException a b (Status code msg))) =
trans locales $ ProxyError (C8.unpack a) b code $ C8.unpack msg
-- NOTE: Minor details are unlocalized for now... Can always come back to this!
transHttp locales (HttpExceptionRequest _ NoResponseDataReceived) =
trans locales $ MalformedResponse "Empty"
transHttp locales (HttpExceptionRequest _ TlsNotSupported) =
trans locales $ HandshakeMisc "Unsupported"
transHttp locales (HttpExceptionRequest _ (WrongRequestBodyStreamSize a b)) =
trans locales $ OtherException $ unlines ["Wrong request bodysize", show a, show b]
transHttp locales (HttpExceptionRequest _ (ResponseBodyTooShort a b)) =
trans locales $ MalformedResponse ("Too short " ++ show a ++ '<' : show b)
transHttp locales (HttpExceptionRequest _ InvalidChunkHeaders) =
trans locales $ MalformedResponse "Chunk headers"
transHttp locales (HttpExceptionRequest _ IncompleteHeaders) =
trans locales $ MalformedResponse "Incomplete headers"
transHttp locales (HttpExceptionRequest _ (InvalidDestinationHost why)) =
trans locales $ FailedConnect $ C8.unpack why
transHttp locales (HttpExceptionRequest _ (HttpZlibException _)) =
trans locales $ MalformedResponse "ZLib compression"
transHttp locales (HttpExceptionRequest _ ConnectionClosed) =
trans locales $ FailedConnect "already-closed"
transHttp locales (HttpExceptionRequest _ (InvalidProxySettings why)) =
trans locales $ FailedConnect ("proxy (" ++ Txt.unpack why ++ ")")
transHttp locales (HttpExceptionRequest _ (InvalidProxyEnvironmentVariable key value)) =
trans locales $ FailedConnect ("proxy (" ++ Txt.unpack key ++ '=' : Txt.unpack value ++ ")")
transHttp locales (HttpExceptionRequest _ (InternalException e)) =
trans locales $ case fromException e of
Just (Terminated _ why _) -> InsecureTerminated why
Just (HandshakeFailed (Error_Misc msg)) -> HandshakeMisc msg
Just (HandshakeFailed (Error_Protocol (_, _, CloseNotify))) -> HandshakeClosed
Just (HandshakeFailed (Error_Protocol (_, _, HandshakeFailure))) -> HandshakeError
Just (HandshakeFailed (Error_Protocol (_, _, BadCertificate))) -> InsecureCertificate ""
Just (HandshakeFailed (Error_Protocol (_, _, UnsupportedCertificate))) ->
InsecureCertificate $ trans locales InsecureCertificateUnsupported
Just (HandshakeFailed (Error_Protocol (_, _, CertificateExpired))) ->
InsecureCertificate $ trans locales InsecureCertificateExpired
Just (HandshakeFailed (Error_Protocol (_, _, CertificateRevoked))) ->
InsecureCertificate $ trans locales InsecureCertificateRevoked
Just (HandshakeFailed (Error_Protocol (_, _, CertificateUnknown))) ->
InsecureCertificate $ trans locales InsecureCertificateUnknown
Just (HandshakeFailed (Error_Protocol (_, _, UnknownCa))) ->
InsecureCertificate $ trans locales InsecureCertificateUnknownCA
Just (HandshakeFailed (Error_Protocol (why, _, _))) -> HandshakeMisc why
Just (HandshakeFailed (Error_Certificate why)) -> InsecureCertificate why
Just (HandshakeFailed (Error_HandshakePolicy why)) -> HandshakePolicy why
Just (HandshakeFailed Error_EOF) -> HandshakeEOF
Just (HandshakeFailed (Error_Packet why)) -> HandshakePacketInvalid why
Just (HandshakeFailed (Error_Packet_unexpected a b)) -> HandshakePacketUnexpected a b
Just (HandshakeFailed (Error_Packet_Parsing why)) -> HandshakePacketUnparsed why
Just ConnectionNotEstablished -> InsecureUnestablished
Nothing -> OtherException $ displayException e
#endif