-- | 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 trans' (InvalidUrlException url msg) = trans' $ InvalidUrl url msg
transHttp trans' (HttpExceptionRequest _ (TooManyRedirects _)) = trans' $ ExcessiveRedirects
transHttp trans' (HttpExceptionRequest _ ResponseTimeout) = trans' $ TimeoutResponse
transHttp trans' (HttpExceptionRequest _ ConnectionTimeout) = trans' $ TimeoutConnection
transHttp trans' (HttpExceptionRequest _ (ConnectionFailure err)) =
trans' $ FailedConnect $ displayException err
transHttp trans' (HttpExceptionRequest _ (StatusCodeException _ code)) =
trans' $ HTTPStatus (fromMaybe 500 $ readMaybe $ C8.unpack code) ""
transHttp trans' (HttpExceptionRequest _ OverlongHeaders) =
trans' $ HTTPStatus 431 "Overlong Headers"
transHttp trans' (HttpExceptionRequest _ (InvalidStatusLine why)) =
trans' $ MalformedResponse $ C8.unpack why
transHttp trans' (HttpExceptionRequest _ (InvalidHeader why)) =
trans' $ MalformedResponse $ C8.unpack why
transHttp trans' (HttpExceptionRequest _ (InvalidRequestHeader why)) =
trans' $ InvalidRequest $ C8.unpack why
transHttp trans' (HttpExceptionRequest _ (ProxyConnectException a b (Status code msg))) =
trans' $ ProxyError (C8.unpack a) b code $ C8.unpack msg
-- NOTE: Minor details are unlocalized for now... Can always come back to this!
transHttp trans' (HttpExceptionRequest _ NoResponseDataReceived) =
trans' $ MalformedResponse "Empty"
transHttp trans' (HttpExceptionRequest _ TlsNotSupported) =
trans' $ HandshakeMisc "Unsupported"
transHttp trans' (HttpExceptionRequest _ (WrongRequestBodyStreamSize a b)) =
trans' $ OtherException $ unlines ["Wrong request bodysize", show a, show b]
transHttp trans' (HttpExceptionRequest _ (ResponseBodyTooShort a b)) =
trans' $ MalformedResponse ("Too short " ++ show a ++ '<' : show b)
transHttp trans' (HttpExceptionRequest _ InvalidChunkHeaders) =
trans' $ MalformedResponse "Chunk headers"
transHttp trans' (HttpExceptionRequest _ IncompleteHeaders) =
trans' $ MalformedResponse "Incomplete headers"
transHttp trans' (HttpExceptionRequest _ (InvalidDestinationHost why)) =
trans' $ FailedConnect $ C8.unpack why
transHttp trans' (HttpExceptionRequest _ (HttpZlibException _)) =
trans' $ MalformedResponse "ZLib compression"
transHttp trans' (HttpExceptionRequest _ ConnectionClosed) =
trans' $ FailedConnect "already-closed"
transHttp trans' (HttpExceptionRequest _ (InvalidProxySettings why)) =
trans' $ FailedConnect ("proxy (" ++ Txt.unpack why ++ ")")
transHttp trans' (HttpExceptionRequest _ (InvalidProxyEnvironmentVariable key value)) =
trans' $ FailedConnect ("proxy (" ++ Txt.unpack key ++ '=' : Txt.unpack value ++ ")")
transHttp trans' (HttpExceptionRequest _ (InternalException e)) =
trans' $ 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' InsecureCertificateUnsupported
Just (HandshakeFailed (Error_Protocol (_, _, CertificateExpired))) ->
InsecureCertificate $ trans' InsecureCertificateExpired
Just (HandshakeFailed (Error_Protocol (_, _, CertificateRevoked))) ->
InsecureCertificate $ trans' InsecureCertificateRevoked
Just (HandshakeFailed (Error_Protocol (_, _, CertificateUnknown))) ->
InsecureCertificate $ trans' InsecureCertificateUnknown
Just (HandshakeFailed (Error_Protocol (_, _, UnknownCa))) ->
InsecureCertificate $ trans' 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