text:
{-# LANGUAGE FlexibleContexts           #-}
{-# LANGUAGE ConstraintKinds            #-}
{-# LANGUAGE FlexibleInstances          #-}
{-# LANGUAGE FunctionalDependencies     #-}
{-# LANGUAGE OverloadedStrings          #-}
{-# LANGUAGE QuasiQuotes                #-}
{-# LANGUAGE RecordWildCards            #-}
{-# LANGUAGE TupleSections              #-}
{-# LANGUAGE TypeFamilies               #-}
{-# LANGUAGE RankNTypes                 #-}
{-# LANGUAGE DeriveDataTypeable         #-}
{-# LANGUAGE ScopedTypeVariables        #-}
---------------------------------------------------------
--
-- Module        : Yesod.Handler
-- Copyright     : Michael Snoyman
-- License       : BSD3
--
-- Maintainer    : Michael Snoyman <michael@snoyman.com>
-- Stability     : stable
-- Portability   : portable
--
-- Define Handler stuff.
--
---------------------------------------------------------
module Yesod.Core.Handler
    ( -- * Handler monad
      HandlerT
    , HandlerFor
      -- ** Read information from handler
    , getYesod
    , getsYesod
    , getUrlRender
    , getUrlRenderParams
    , getPostParams
    , getCurrentRoute
    , getRequest
    , waiRequest
    , runRequestBody
    , rawRequestBody
      -- ** Request information
      -- *** Request datatype
    , RequestBodyContents
    , YesodRequest (..)
    , FileInfo
    , fileName
    , fileContentType
    , fileSource
    , fileSourceByteString
    , fileMove
      -- *** Convenience functions
    , languages
      -- *** Lookup parameters
    , lookupGetParam
    , lookupPostParam
    , lookupCookie
    , lookupFile
    , lookupHeader
      -- **** Lookup authentication data
    , lookupBasicAuth
    , lookupBearerAuth
      -- **** Multi-lookup
    , lookupGetParams
    , lookupPostParams
    , lookupCookies
    , lookupFiles
    , lookupHeaders
      -- * Responses
      -- ** Pure
    , respond
      -- ** Streaming
    , respondSource
    , sendChunk
    , sendFlush
    , sendChunkBS
    , sendChunkLBS
    , sendChunkText
    , sendChunkLazyText
    , sendChunkHtml
      -- ** Redirecting
    , RedirectUrl (..)
    , redirect
    , redirectWith
    , redirectToPost
    , Fragment(..)
      -- ** Errors
    , notFound
    , badMethod
    , notAuthenticated
    , permissionDenied
    , permissionDeniedI
    , invalidArgs
    , invalidArgsI
      -- ** Short-circuit responses.
    , sendFile
    , sendFilePart
    , sendResponse
    , sendResponseStatus
      -- ** Type specific response with custom status
    , sendStatusJSON
    , sendResponseCreated
    , sendWaiResponse
    , sendWaiApplication
    , sendRawResponse
    , sendRawResponseNoConduit
    , notModified
      -- * Different representations
      -- $representations
    , selectRep
    , provideRep
    , provideRepType
    , ProvidedRep
      -- * Setting headers
    , setCookie
    , getExpires
    , deleteCookie
    , addHeader
    , setHeader
    , replaceOrAddHeader
    , setLanguage
    , addContentDispositionFileName
      -- ** Content caching and expiration
    , cacheSeconds
    , neverExpires
    , alreadyExpired
    , expiresAt
    , setEtag
    , setWeakEtag
      -- * Session
    , SessionMap
    , lookupSession
    , lookupSessionBS
    , getSession
    , setSession
    , setSessionBS
    , deleteSession
    , clearSession
      -- ** Ultimate destination
    , setUltDest
    , setUltDestCurrent
    , setUltDestReferer
    , redirectUltDest
    , clearUltDest
      -- ** Messages
    , addMessage
    , addMessageI
    , getMessages
    , setMessage
    , setMessageI
    , getMessage
      -- * Subsites
    , SubHandlerFor
    , getSubYesod
    , getRouteToParent
    , getSubCurrentRoute
      -- * Helpers for specific content
      -- ** Hamlet
    , hamletToRepHtml
    , giveUrlRenderer
    , withUrlRenderer
      -- ** Misc
    , newIdent
      -- * Lifting
    , handlerToIO
    , forkHandler
      -- * i18n
    , getMessageRender
      -- * Per-request caching
    , cached
    , cachedBy
      -- * AJAX CSRF protection

      -- $ajaxCSRFOverview

      -- ** Setting CSRF Cookies
    , setCsrfCookie
    , setCsrfCookieWithCookie
    , defaultCsrfCookieName
      -- ** Looking up CSRF Headers
    , checkCsrfHeaderNamed
    , hasValidCsrfHeaderNamed
    , defaultCsrfHeaderName
      -- ** Looking up CSRF POST Parameters
    , hasValidCsrfParamNamed
    , checkCsrfParamNamed
    , defaultCsrfParamName
      -- ** Checking CSRF Headers or POST Parameters
    , checkCsrfHeaderOrParam
    ) where

import           Data.Time                     (UTCTime, addUTCTime,
                                                getCurrentTime)
import           Yesod.Core.Internal.Request   (langKey, mkFileInfoFile,
                                                mkFileInfoLBS, mkFileInfoSource)

import           Control.Applicative           ((<|>))
import qualified Data.CaseInsensitive          as CI
import           Control.Exception             (evaluate, SomeException, throwIO)
import           Control.Exception             (handle)

import           Control.Monad                 (void, liftM, unless)
import qualified Control.Monad.Trans.Writer    as Writer

import           UnliftIO                      (MonadIO, liftIO, MonadUnliftIO, withRunInIO)

import qualified Network.HTTP.Types            as H
import qualified Network.Wai                   as W
import           Network.Wai.Middleware.HttpAuth
    ( extractBasicAuth, extractBearerAuth )
import Control.Monad.Trans.Class (lift)

import           Data.Aeson                    (ToJSON(..))
import qualified Data.Text                     as T
import           Data.Text.Encoding            (decodeUtf8With, encodeUtf8, decodeUtf8)
import           Data.Text.Encoding.Error      (lenientDecode)
import qualified Data.Text.Lazy                as TL
import           Text.Blaze.Html.Renderer.Utf8 (renderHtml)
import           Text.Hamlet                   (Html, HtmlUrl, hamlet)

import qualified Data.ByteString               as S
import qualified Data.ByteString.Lazy          as L
import qualified Data.Map                      as Map
import qualified Data.HashMap.Strict           as HM

import           Data.Byteable                 (constEqBytes)

import           Control.Arrow                 ((***))
import qualified Data.ByteString.Char8         as S8
import           Data.Monoid                   (Endo (..))
import           Data.Text                     (Text)
import qualified Network.Wai.Parse             as NWP
import           Text.Shakespeare.I18N         (RenderMessage (..))
import           Web.Cookie                    (SetCookie (..), defaultSetCookie)
import           Yesod.Core.Content            (ToTypedContent (..), simpleContentType, contentTypeTypes, HasContentType (..), ToContent (..), ToFlushBuilder (..))
import           Yesod.Core.Internal.Util      (formatRFC1123)
import           Text.Blaze.Html               (preEscapedToHtml, toHtml)

import qualified Data.IORef                    as I
import           Data.Maybe                    (listToMaybe, mapMaybe)
import           Data.Typeable                 (Typeable)
import           Web.PathPieces                (PathPiece(..))
import           Yesod.Core.Class.Handler
import           Yesod.Core.Types
import           Yesod.Routes.Class            (Route)
import           Data.ByteString.Builder (Builder)
import           Data.CaseInsensitive (CI, original)
import qualified Data.Conduit.List as CL
import           Control.Monad.Trans.Resource  (MonadResource, InternalState, runResourceT, withInternalState, getInternalState, liftResourceT, resourceForkIO)
import qualified System.PosixCompat.Files as PC
import           Conduit ((.|), runConduit, sinkLazy)
import           Data.Conduit (ConduitT, transPipe, Flush (Flush), yield, Void)
import qualified Yesod.Core.TypeCache as Cache
import qualified Data.Word8 as W8
import qualified Data.Foldable as Fold
import           Control.Monad.Logger (MonadLogger, logWarnS)

type HandlerT site (m :: * -> *) = HandlerFor site
{-# DEPRECATED HandlerT "Use HandlerFor directly" #-}

get :: MonadHandler m => m GHState
get = liftHandler $ HandlerFor $ I.readIORef . handlerState

put :: MonadHandler m => GHState -> m ()
put x = liftHandler $ HandlerFor $ flip I.writeIORef x . handlerState

modify :: MonadHandler m => (GHState -> GHState) -> m ()
modify f = liftHandler $ HandlerFor $ flip I.modifyIORef f . handlerState

tell :: MonadHandler m => Endo [Header] -> m ()
tell hs = modify $ \g -> g { ghsHeaders = ghsHeaders g `mappend` hs }

handlerError :: MonadHandler m => HandlerContents -> m a
handlerError = liftIO . throwIO

hcError :: MonadHandler m => ErrorResponse -> m a
hcError = handlerError . HCError

getRequest :: MonadHandler m => m YesodRequest
getRequest = liftHandler $ HandlerFor $ return . handlerRequest

runRequestBody :: MonadHandler m => m RequestBodyContents
runRequestBody = do
    HandlerData
        { handlerEnv = RunHandlerEnv {..}
        , handlerRequest = req
        } <- liftHandler $ HandlerFor return
    let len = W.requestBodyLength $ reqWaiRequest req
        upload = rheUpload len
    x <- get
    case ghsRBC x of
        Just rbc -> return rbc
        Nothing -> do
            rr <- waiRequest
            internalState <- liftResourceT getInternalState
            rbc <- liftIO $ rbHelper upload rr internalState
            put x { ghsRBC = Just rbc }
            return rbc

rbHelper :: FileUpload -> W.Request -> InternalState -> IO RequestBodyContents
rbHelper upload req internalState =
    case upload of
        FileUploadMemory s -> rbHelper' s mkFileInfoLBS req
        FileUploadDisk s -> rbHelper' (s internalState) mkFileInfoFile req
        FileUploadSource s -> rbHelper' s mkFileInfoSource req

rbHelper' :: NWP.BackEnd x
          -> (Text -> Text -> x -> FileInfo)
          -> W.Request
          -> IO ([(Text, Text)], [(Text, FileInfo)])
rbHelper' backend mkFI req =
    (map fix1 *** mapMaybe fix2) <$> NWP.parseRequestBody backend req
  where
    fix1 = go *** go
    fix2 (x, NWP.FileInfo a' b c)
        | S.null a = Nothing
        | otherwise = Just (go x, mkFI (go a) (go b) c)
      where
        a
            | S.length a' < 2 = a'
            | S8.head a' == '"' && S8.last a' == '"' = S.tail $ S.init a'
            | S8.head a' == '\'' && S8.last a' == '\'' = S.tail $ S.init a'
            | otherwise = a'
    go = decodeUtf8With lenientDecode

askHandlerEnv :: MonadHandler m => m (RunHandlerEnv (HandlerSite m) (HandlerSite m))
askHandlerEnv = liftHandler $ HandlerFor $ return . handlerEnv

-- | Get the master site application argument.
getYesod :: MonadHandler m => m (HandlerSite m)
getYesod = rheSite <$> askHandlerEnv

-- | Get a specific component of the master site application argument.
--   Analogous to the 'gets' function for operating on 'StateT'.
getsYesod :: MonadHandler m => (HandlerSite m -> a) -> m a
getsYesod f = (f . rheSite) <$> askHandlerEnv

-- | Get the URL rendering function.
getUrlRender :: MonadHandler m => m (Route (HandlerSite m) -> Text)
getUrlRender = do
    x <- rheRender <$> askHandlerEnv
    return $ flip x []

-- | The URL rendering function with query-string parameters.
getUrlRenderParams
    :: MonadHandler m
    => m (Route (HandlerSite m) -> [(Text, Text)] -> Text)
getUrlRenderParams = rheRender <$> askHandlerEnv

-- | Get all the post parameters passed to the handler. To also get
-- the submitted files (if any), you have to use 'runRequestBody'
-- instead of this function.
--
-- @since 1.4.33
getPostParams
  :: MonadHandler m
  => m [(Text, Text)]
getPostParams = do
  reqBodyContent <- runRequestBody
  return $ fst reqBodyContent

-- | Get the route requested by the user. If this is a 404 response- where the
-- user requested an invalid route- this function will return 'Nothing'.
getCurrentRoute :: MonadHandler m => m (Maybe (Route (HandlerSite m)))
getCurrentRoute = rheRoute <$> askHandlerEnv

-- | Returns a function that runs 'HandlerT' actions inside @IO@.
--
-- Sometimes you want to run an inner 'HandlerT' action outside
-- the control flow of an HTTP request (on the outer 'HandlerT'
-- action).  For example, you may want to spawn a new thread:
--
-- @
-- getFooR :: Handler RepHtml
-- getFooR = do
--   runInnerHandler <- handlerToIO
--   liftIO $ forkIO $ runInnerHandler $ do
--     /Code here runs inside GHandler but on a new thread./
--     /This is the inner GHandler./
--     ...
--   /Code here runs inside the request's control flow./
--   /This is the outer GHandler./
--   ...
-- @
--
-- Another use case for this function is creating a stream of
-- server-sent events using 'GHandler' actions (see
-- @yesod-eventsource@).
--
-- Most of the environment from the outer 'GHandler' is preserved
-- on the inner 'GHandler', however:
--
--  * The request body is cleared (otherwise it would be very
--  difficult to prevent huge memory leaks).
--
--  * The cache is cleared (see 'CacheKey').
--
-- Changes to the response made inside the inner 'GHandler' are
-- ignored (e.g., session variables, cookies, response headers).
-- This allows the inner 'GHandler' to outlive the outer
-- 'GHandler' (e.g., on the @forkIO@ example above, a response
-- may be sent to the client without killing the new thread).
handlerToIO :: MonadIO m => HandlerFor site (HandlerFor site a -> m a)
handlerToIO =
  HandlerFor $ \oldHandlerData -> do
    -- Take just the bits we need from oldHandlerData.
    let newReq = oldReq { reqWaiRequest = newWaiReq }
          where
            oldReq    = handlerRequest oldHandlerData
            oldWaiReq = reqWaiRequest oldReq
            newWaiReq = oldWaiReq { W.requestBody = return mempty
                                  , W.requestBodyLength = W.KnownLength 0
                                  }
        oldEnv = handlerEnv oldHandlerData
    newState <- liftIO $ do
      oldState <- I.readIORef (handlerState oldHandlerData)
      return $ oldState { ghsRBC = Nothing
                        , ghsIdent = 1
                        , ghsCache = mempty
                        , ghsCacheBy = mempty
                        , ghsHeaders = mempty }

    -- xx From this point onwards, no references to oldHandlerData xx
    liftIO $ evaluate (newReq `seq` oldEnv `seq` newState `seq` ())

    -- Return GHandler running function.
    return $ \(HandlerFor f) ->
      liftIO $
      runResourceT $ withInternalState $ \resState -> do
        -- The state IORef needs to be created here, otherwise it
        -- will be shared by different invocations of this function.
        newStateIORef <- liftIO (I.newIORef newState)
        let newHandlerData =
              HandlerData
                { handlerRequest  = newReq
                , handlerEnv      = oldEnv
                , handlerState    = newStateIORef
                , handlerResource = resState
                }
        liftIO (f newHandlerData)

-- | forkIO for a Handler (run an action in the background)
--
-- Uses 'handlerToIO', liftResourceT, and resourceForkIO
-- for correctness and efficiency
--
-- @since 1.2.8
forkHandler :: (SomeException -> HandlerFor site ()) -- ^ error handler
              -> HandlerFor site ()
              -> HandlerFor site ()
forkHandler onErr handler = do
    yesRunner <- handlerToIO
    void $ liftResourceT $ resourceForkIO $
      liftIO $ handle (yesRunner . onErr) (yesRunner handler)

-- | Redirect to the given route.
-- HTTP status code 303 for HTTP 1.1 clients and 302 for HTTP 1.0
-- This is the appropriate choice for a get-following-post
-- technique, which should be the usual use case.
--
-- If you want direct control of the final status code, or need a different
-- status code, please use 'redirectWith'.
redirect :: (MonadHandler m, RedirectUrl (HandlerSite m) url)
         => url -> m a
redirect url = do
    req <- waiRequest
    let status =
            if W.httpVersion req == H.http11
                then H.status303
                else H.status302
    redirectWith status url

-- | Redirect to the given URL with the specified status code.
redirectWith :: (MonadHandler m, RedirectUrl (HandlerSite m) url)
             => H.Status
             -> url
             -> m a
redirectWith status url = do
    urlText <- toTextUrl url
    handlerError $ HCRedirect status urlText

ultDestKey :: Text
ultDestKey = "_ULT"

-- | Sets the ultimate destination variable to the given route.
--
-- An ultimate destination is stored in the user session and can be loaded
-- later by 'redirectUltDest'.
setUltDest :: (MonadHandler m, RedirectUrl (HandlerSite m) url)
           => url
           -> m ()
setUltDest url = do
    urlText <- toTextUrl url
    setSession ultDestKey urlText

-- | Same as 'setUltDest', but uses the current page.
--
-- If this is a 404 handler, there is no current page, and then this call does
-- nothing.
setUltDestCurrent :: MonadHandler m => m ()
setUltDestCurrent = do
    route <- getCurrentRoute
    case route of
        Nothing -> return ()
        Just r -> do
            gets' <- reqGetParams <$> getRequest
            setUltDest (r, gets')

-- | Sets the ultimate destination to the referer request header, if present.
--
-- This function will not overwrite an existing ultdest.
setUltDestReferer :: MonadHandler m => m ()
setUltDestReferer = do
    mdest <- lookupSession ultDestKey
    maybe
        (waiRequest >>= maybe (return ()) setUltDestBS . lookup "referer" . W.requestHeaders)
        (const $ return ())
        mdest
  where
    setUltDestBS = setUltDest . T.pack . S8.unpack

-- | Redirect to the ultimate destination in the user's session. Clear the
-- value from the session.
--
-- The ultimate destination is set with 'setUltDest'.
--
-- This function uses 'redirect', and thus will perform a temporary redirect to
-- a GET request.
redirectUltDest :: (RedirectUrl (HandlerSite m) url, MonadHandler m)
                => url -- ^ default destination if nothing in session
                -> m a
redirectUltDest defaultDestination = do
    mdest <- lookupSession ultDestKey
    deleteSession ultDestKey
    maybe (redirect defaultDestination) redirect mdest

-- | Remove a previously set ultimate destination. See 'setUltDest'.
clearUltDest :: MonadHandler m => m ()
clearUltDest = deleteSession ultDestKey

msgKey :: Text
msgKey = "_MSG"

-- | Adds a status and message in the user's session.
--
-- See 'getMessages'.
--
-- @since 1.4.20
addMessage :: MonadHandler m
           => Text -- ^ status
           -> Html -- ^ message
           -> m ()
addMessage status msg = do
    val <- lookupSessionBS msgKey
    setSessionBS msgKey $ addMsg val
  where
    addMsg = maybe msg' (S.append msg' . S.cons W8._nul)
    msg' = S.append
        (encodeUtf8 status)
        (W8._nul `S.cons` L.toStrict (renderHtml msg))

-- | Adds a message in the user's session but uses RenderMessage to allow for i18n
--
-- See 'getMessages'.
--
-- @since 1.4.20
addMessageI :: (MonadHandler m, RenderMessage (HandlerSite m) msg)
            => Text -> msg -> m ()
addMessageI status msg = do
    mr <- getMessageRender
    addMessage status $ toHtml $ mr msg

-- | Gets all messages in the user's session, and then clears the variable.
--
-- See 'addMessage'.
--
-- @since 1.4.20
getMessages :: MonadHandler m => m [(Text, Html)]
getMessages = do
    bs <- lookupSessionBS msgKey
    let ms = maybe [] enlist bs
    deleteSession msgKey
    return ms
  where
    enlist = pairup . S.split W8._nul
    pairup [] = []
    pairup [_] = []
    pairup (s:v:xs) = (decode s, preEscapedToHtml (decode v)) : pairup xs
    decode = decodeUtf8With lenientDecode

-- | Calls 'addMessage' with an empty status
setMessage :: MonadHandler m => Html -> m ()
setMessage = addMessage ""

-- | Calls 'addMessageI' with an empty status
setMessageI :: (MonadHandler m, RenderMessage (HandlerSite m) msg)
            => msg -> m ()
setMessageI = addMessageI ""

-- | Gets just the last message in the user's session,
-- discards the rest and the status
getMessage :: MonadHandler m => m (Maybe Html)
getMessage = fmap (fmap snd . listToMaybe) getMessages

-- | Bypass remaining handler code and output the given file.
--
-- For some backends, this is more efficient than reading in the file to
-- memory, since they can optimize file sending via a system call to sendfile.
sendFile :: MonadHandler m => ContentType -> FilePath -> m a
sendFile ct fp = handlerError $ HCSendFile ct fp Nothing

-- | Same as 'sendFile', but only sends part of a file.
sendFilePart :: MonadHandler m
             => ContentType
             -> FilePath
             -> Integer -- ^ offset
             -> Integer -- ^ count
             -> m a
sendFilePart ct fp off count = do
    fs <- liftIO $ PC.getFileStatus fp
    handlerError $ HCSendFile ct fp $ Just W.FilePart
        { W.filePartOffset = off
        , W.filePartByteCount = count
        , W.filePartFileSize = fromIntegral $ PC.fileSize fs
        }

-- | Bypass remaining handler code and output the given content with a 200
-- status code.
sendResponse :: (MonadHandler m, ToTypedContent c) => c -> m a
sendResponse = handlerError . HCContent H.status200 . toTypedContent

-- | Bypass remaining handler code and output the given content with the given
-- status code.
sendResponseStatus :: (MonadHandler m, ToTypedContent c) => H.Status -> c -> m a
sendResponseStatus s = handlerError . HCContent s . toTypedContent

-- | Bypass remaining handler code and output the given JSON with the given
-- status code.
--
-- @since 1.4.18
sendStatusJSON :: (MonadHandler m, ToJSON c) => H.Status -> c -> m a
sendStatusJSON s v = sendResponseStatus s (toEncoding v)

-- | Send a 201 "Created" response with the given route as the Location
-- response header.
sendResponseCreated :: MonadHandler m => Route (HandlerSite m) -> m a
sendResponseCreated url = do
    r <- getUrlRender
    handlerError $ HCCreated $ r url

-- | Send a 'W.Response'. Please note: this function is rarely
-- necessary, and will /disregard/ any changes to response headers and session
-- that you have already specified. This function short-circuits. It should be
-- considered only for very specific needs. If you are not sure if you need it,
-- you don't.
sendWaiResponse :: MonadHandler m => W.Response -> m b
sendWaiResponse = handlerError . HCWai

-- | Switch over to handling the current request with a WAI @Application@.
--
-- @since 1.2.17
sendWaiApplication :: MonadHandler m => W.Application -> m b
sendWaiApplication = handlerError . HCWaiApp

-- | Send a raw response without conduit. This is used for cases such as
-- WebSockets. Requires WAI 3.0 or later, and a web server which supports raw
-- responses (e.g., Warp).
--
-- @since 1.2.16
sendRawResponseNoConduit
    :: (MonadHandler m, MonadUnliftIO m)
    => (IO S8.ByteString -> (S8.ByteString -> IO ()) -> m ())
    -> m a
sendRawResponseNoConduit raw = withRunInIO $ \runInIO ->
    liftIO $ throwIO $ HCWai $ flip W.responseRaw fallback
    $ \src sink -> void $ runInIO (raw src sink)
  where
    fallback = W.responseLBS H.status500 [("Content-Type", "text/plain")]
        "sendRawResponse: backend does not support raw responses"

-- | Send a raw response. This is used for cases such as WebSockets. Requires
-- WAI 2.1 or later, and a web server which supports raw responses (e.g.,
-- Warp).
--
-- @since 1.2.7
sendRawResponse
  :: (MonadHandler m, MonadUnliftIO m)
  => (ConduitT () S8.ByteString IO () -> ConduitT S8.ByteString Void IO () -> m ())
  -> m a
sendRawResponse raw = withRunInIO $ \runInIO ->
    liftIO $ throwIO $ HCWai $ flip W.responseRaw fallback
    $ \src sink -> void $ runInIO $ raw (src' src) (CL.mapM_ sink)
  where
    fallback = W.responseLBS H.status500 [("Content-Type", "text/plain")]
        "sendRawResponse: backend does not support raw responses"
    src' src = do
        bs <- liftIO src
        unless (S.null bs) $ do
            yield bs
            src' src

-- | Send a 304 not modified response immediately. This is a short-circuiting
-- action.
--
-- @since 1.4.4
notModified :: MonadHandler m => m a
notModified = sendWaiResponse $ W.responseBuilder H.status304 [] mempty

-- | Return a 404 not found page. Also denotes no handler available.
notFound :: MonadHandler m => m a
notFound = hcError NotFound

-- | Return a 405 method not supported page.
badMethod :: MonadHandler m => m a
badMethod = do
    w <- waiRequest
    hcError $ BadMethod $ W.requestMethod w

-- | Return a 401 status code
notAuthenticated :: MonadHandler m => m a
notAuthenticated = hcError NotAuthenticated

-- | Return a 403 permission denied page.
permissionDenied :: MonadHandler m => Text -> m a
permissionDenied = hcError . PermissionDenied

-- | Return a 403 permission denied page.
permissionDeniedI :: (RenderMessage (HandlerSite m) msg, MonadHandler m)
                  => msg
                  -> m a
permissionDeniedI msg = do
    mr <- getMessageRender
    permissionDenied $ mr msg

-- | Return a 400 invalid arguments page.
invalidArgs :: MonadHandler m => [Text] -> m a
invalidArgs = hcError . InvalidArgs

-- | Return a 400 invalid arguments page.
invalidArgsI :: (MonadHandler m, RenderMessage (HandlerSite m) msg) => [msg] -> m a
invalidArgsI msg = do
    mr <- getMessageRender
    invalidArgs $ map mr msg

------- Headers
-- | Set the cookie on the client.

setCookie :: MonadHandler m => SetCookie -> m ()
setCookie sc = do
  addHeaderInternal (DeleteCookie name path)
  addHeaderInternal (AddCookie sc)
  where name = setCookieName sc
        path = maybe "/" id (setCookiePath sc)

-- | Helper function for setCookieExpires value
getExpires :: MonadIO m
           => Int -- ^ minutes
           -> m UTCTime
getExpires m = do
    now <- liftIO getCurrentTime
    return $ fromIntegral (m * 60) `addUTCTime` now

-- | Unset the cookie on the client.
--
-- Note: although the value used for key and path is 'Text', you should only
-- use ASCII values to be HTTP compliant.
deleteCookie :: MonadHandler m
             => Text -- ^ key
             -> Text -- ^ path
             -> m ()
deleteCookie a = addHeaderInternal . DeleteCookie (encodeUtf8 a) . encodeUtf8

-- | Set the language in the user session. Will show up in 'languages' on the
-- next request.
setLanguage :: MonadHandler m => Text -> m ()
setLanguage = setSession langKey

-- | Set attachment file name.
--
-- Allows Unicode characters by encoding to UTF-8.
-- Some modurn browser parse UTF-8 characters with out encoding setting.
-- But, for example IE9 can't parse UTF-8 characters.
-- This function use
-- <https://tools.ietf.org/html/rfc6266 RFC 6266>(<https://tools.ietf.org/html/rfc5987 RFC 5987>)
--
-- @since 1.6.4
addContentDispositionFileName :: MonadHandler m => T.Text -> m ()
addContentDispositionFileName fileName
    = addHeader "Content-Disposition" $ rfc6266Utf8FileName fileName

-- | <https://tools.ietf.org/html/rfc6266 RFC 6266> Unicode attachment filename.
--
-- > rfc6266Utf8FileName (Data.Text.pack "€")
-- "attachment; filename*=UTF-8''%E2%82%AC"
rfc6266Utf8FileName :: T.Text -> T.Text
rfc6266Utf8FileName fileName = "attachment; filename*=UTF-8''" `mappend` decodeUtf8 (H.urlEncode True (encodeUtf8 fileName))

-- | Set an arbitrary response header.
--
-- Note that, while the data type used here is 'Text', you must provide only
-- ASCII value to be HTTP compliant.
--
-- @since 1.2.0
addHeader :: MonadHandler m => Text -> Text -> m ()
addHeader a = addHeaderInternal . Header (CI.mk $ encodeUtf8 a) . encodeUtf8

-- | Deprecated synonym for addHeader.
setHeader :: MonadHandler m => Text -> Text -> m ()
setHeader = addHeader
{-# DEPRECATED setHeader "Please use addHeader instead" #-}

-- | Replace an existing header with a new value or add a new header
-- if not present.
--
-- Note that, while the data type used here is 'Text', you must provide only
-- ASCII value to be HTTP compliant.
--
-- @since 1.4.36
replaceOrAddHeader :: MonadHandler m => Text -> Text -> m ()
replaceOrAddHeader a b =
  modify $ \g -> g {ghsHeaders = replaceHeader (ghsHeaders g)}
  where
    repHeader = Header (CI.mk $ encodeUtf8 a) (encodeUtf8 b)

    sameHeaderName :: Header -> Header -> Bool
    sameHeaderName (Header n1 _) (Header n2 _) = n1 == n2
    sameHeaderName _ _ = False

    replaceIndividualHeader :: [Header] -> [Header]
    replaceIndividualHeader [] = [repHeader]
    replaceIndividualHeader xs = aux xs []
      where
        aux [] acc = acc ++ [repHeader]
        aux (x:xs') acc =
          if sameHeaderName repHeader x
            then acc ++
                 [repHeader] ++
                 (filter (\header -> not (sameHeaderName header repHeader)) xs')
            else aux xs' (acc ++ [x])

    replaceHeader :: Endo [Header] -> Endo [Header]
    replaceHeader endo =
      let allHeaders :: [Header] = appEndo endo []
      in Endo (\rest -> replaceIndividualHeader allHeaders ++ rest)

-- | Set the Cache-Control header to indicate this response should be cached
-- for the given number of seconds.
cacheSeconds :: MonadHandler m => Int -> m ()
cacheSeconds i = setHeader "Cache-Control" $ T.concat
    [ "max-age="
    , T.pack $ show i
    , ", public"
    ]

-- | Set the Expires header to some date in 2037. In other words, this content
-- is never (realistically) expired.
neverExpires :: MonadHandler m => m ()
neverExpires = do
    setHeader "Expires" . rheMaxExpires =<< askHandlerEnv
    cacheSeconds oneYear
  where
    oneYear :: Int
    oneYear = 60 * 60 * 24 * 365

-- | Set an Expires header in the past, meaning this content should not be
-- cached.
alreadyExpired :: MonadHandler m => m ()
alreadyExpired = setHeader "Expires" "Thu, 01 Jan 1970 05:05:05 GMT"

-- | Set an Expires header to the given date.
expiresAt :: MonadHandler m => UTCTime -> m ()
expiresAt = setHeader "Expires" . formatRFC1123

data Etag
  = WeakEtag !S.ByteString
  -- ^ Prefixed by W/ and surrounded in quotes. Signifies that contents are
  -- semantically identical but make no guarantees about being bytewise identical.
  | StrongEtag !S.ByteString
  -- ^ Signifies that contents should be byte-for-byte identical if they match
  -- the provided ETag
  | InvalidEtag !S.ByteString
  -- ^ Anything else that ends up in a header that expects an ETag but doesn't
  -- properly follow the ETag format specified in RFC 7232, section 2.3
  deriving (Show, Eq)

-- | Check the if-none-match header and, if it matches the given value, return
-- a 304 not modified response. Otherwise, set the etag header to the given
-- value.
--
-- Note that it is the responsibility of the caller to ensure that the provided
-- value is a valid etag value, no sanity checking is performed by this
-- function.
--
-- @since 1.4.4
setEtag :: MonadHandler m => Text -> m ()
setEtag etag = do
    mmatch <- lookupHeader "if-none-match"
    let matches = maybe [] parseMatch mmatch
        baseTag = encodeUtf8 etag
        strongTag = StrongEtag baseTag
        badTag = InvalidEtag baseTag
    if any (\tag -> tag == strongTag || tag == badTag) matches
        then notModified
        else addHeader "etag" $ T.concat ["\"", etag, "\""]

-- | Parse an if-none-match field according to the spec.
parseMatch :: S.ByteString -> [Etag]
parseMatch =
    map clean . S.split W8._comma
  where
    clean = classify . fst . S.spanEnd W8.isSpace . S.dropWhile W8.isSpace

    classify bs
        | S.length bs >= 2 && S.head bs == W8._quotedbl && S.last bs == W8._quotedbl
            = StrongEtag $ S.init $ S.tail bs
        | S.length bs >= 4 &&
          S.head bs == W8._W &&
          S.index bs 1 == W8._slash &&
          S.index bs 2 == W8._quotedbl &&
          S.last bs == W8._quotedbl
            = WeakEtag $ S.init $ S.drop 3 bs
        | otherwise = InvalidEtag bs

-- | Check the if-none-match header and, if it matches the given value, return
-- a 304 not modified response. Otherwise, set the etag header to the given
-- value.
--
-- A weak etag is only expected to be semantically identical to the prior content,
-- but doesn't have to be byte-for-byte identical. Therefore it can be useful for
-- dynamically generated content that may be difficult to perform bytewise hashing
-- upon.
--
-- Note that it is the responsibility of the caller to ensure that the provided
-- value is a valid etag value, no sanity checking is performed by this
-- function.
--
-- @since 1.4.37
setWeakEtag :: MonadHandler m => Text -> m ()
setWeakEtag etag = do
    mmatch <- lookupHeader "if-none-match"
    let matches = maybe [] parseMatch mmatch
    if WeakEtag (encodeUtf8 etag) `elem` matches
        then notModified
        else addHeader "etag" $ T.concat ["W/\"", etag, "\""]

-- | Set a variable in the user's session.
--
-- The session is handled by the clientsession package: it sets an encrypted
-- and hashed cookie on the client. This ensures that all data is secure and
-- not tampered with.
setSession :: MonadHandler m
           => Text -- ^ key
           -> Text -- ^ value
           -> m ()
setSession k = setSessionBS k . encodeUtf8

-- | Same as 'setSession', but uses binary data for the value.
setSessionBS :: MonadHandler m
             => Text
             -> S.ByteString
             -> m ()
setSessionBS k = modify . modSession . Map.insert k

-- | Unsets a session variable. See 'setSession'.
deleteSession :: MonadHandler m => Text -> m ()
deleteSession = modify . modSession . Map.delete

-- | Clear all session variables.
--
-- @since: 1.0.1
clearSession :: MonadHandler m => m ()
clearSession = modify $ \x -> x { ghsSession = Map.empty }

modSession :: (SessionMap -> SessionMap) -> GHState -> GHState
modSession f x = x { ghsSession = f $ ghsSession x }

-- | Internal use only, not to be confused with 'setHeader'.
addHeaderInternal :: MonadHandler m => Header -> m ()
addHeaderInternal = tell . Endo . (:)

-- | Some value which can be turned into a URL for redirects.
class RedirectUrl master a where
    -- | Converts the value to the URL and a list of query-string parameters.
    toTextUrl :: (MonadHandler m, HandlerSite m ~ master) => a -> m Text

instance RedirectUrl master Text where
    toTextUrl = return

instance RedirectUrl master String where
    toTextUrl = toTextUrl . T.pack

instance RedirectUrl master (Route master) where
    toTextUrl url = do
        r <- getUrlRender
        return $ r url

instance (key ~ Text, val ~ Text) => RedirectUrl master (Route master, [(key, val)]) where
    toTextUrl (url, params) = do
        r <- getUrlRenderParams
        return $ r url params

instance (key ~ Text, val ~ Text) => RedirectUrl master (Route master, Map.Map key val) where
    toTextUrl (url, params) = toTextUrl (url, Map.toList params)

-- | Add a fragment identifier to a route to be used when
-- redirecting.  For example:
--
-- > redirect (NewsfeedR :#: storyId)
--
-- @since 1.2.9.
data Fragment a b = a :#: b deriving (Show, Typeable)

instance (RedirectUrl master a, PathPiece b) => RedirectUrl master (Fragment a b) where
  toTextUrl (a :#: b) = (\ua -> T.concat [ua, "#", toPathPiece b]) <$> toTextUrl a

-- | Lookup for session data.
lookupSession :: MonadHandler m => Text -> m (Maybe Text)
lookupSession = (fmap . fmap) (decodeUtf8With lenientDecode) . lookupSessionBS

-- | Lookup for session data in binary format.
lookupSessionBS :: MonadHandler m => Text -> m (Maybe S.ByteString)
lookupSessionBS n = do
    m <- fmap ghsSession get
    return $ Map.lookup n m

-- | Get all session variables.
getSession :: MonadHandler m => m SessionMap
getSession = fmap ghsSession get

-- | Get a unique identifier.
newIdent :: MonadHandler m => m Text
newIdent = do
    x <- get
    let i' = ghsIdent x + 1
    put x { ghsIdent = i' }
    return $ T.pack $ "hident" ++ show i'

-- | Redirect to a POST resource.
--
-- This is not technically a redirect; instead, it returns an HTML page with a
-- POST form, and some Javascript to automatically submit the form. This can be
-- useful when you need to post a plain link somewhere that needs to cause
-- changes on the server.
redirectToPost :: (MonadHandler m, RedirectUrl (HandlerSite m) url)
               => url
               -> m a
redirectToPost url = do
    urlText <- toTextUrl url
    req <- getRequest
    withUrlRenderer [hamlet|
$newline never
$doctype 5

<html>
    <head>
        <title>Redirecting...
    <body onload="document.getElementById('form').submit()">
        <form id="form" method="post" action=#{urlText}>
            $maybe token <- reqToken req
                <input type=hidden name=#{defaultCsrfParamName} value=#{token}>
            <noscript>
                <p>Javascript has been disabled; please click on the button below to be redirected.
            <input type="submit" value="Continue">
|] >>= sendResponse

-- | Wraps the 'Content' generated by 'hamletToContent' in a 'RepHtml'.
hamletToRepHtml :: MonadHandler m => HtmlUrl (Route (HandlerSite m)) -> m Html
hamletToRepHtml = withUrlRenderer
{-# DEPRECATED hamletToRepHtml "Use withUrlRenderer instead" #-}

-- | Deprecated synonym for 'withUrlRenderer'.
--
-- @since 1.2.0
giveUrlRenderer :: MonadHandler m
                => ((Route (HandlerSite m) -> [(Text, Text)] -> Text) -> output)
                -> m output
giveUrlRenderer = withUrlRenderer
{-# DEPRECATED giveUrlRenderer "Use withUrlRenderer instead" #-}

-- | Provide a URL rendering function to the given function and return the
-- result. Useful for processing Shakespearean templates.
--
-- @since 1.2.20
withUrlRenderer :: MonadHandler m
                => ((Route (HandlerSite m) -> [(Text, Text)] -> Text) -> output)
                -> m output
withUrlRenderer f = do
    render <- getUrlRenderParams
    return $ f render

-- | Get the request\'s 'W.Request' value.
waiRequest :: MonadHandler m => m W.Request
waiRequest = reqWaiRequest <$> getRequest

getMessageRender :: (MonadHandler m, RenderMessage (HandlerSite m) message)
                 => m (message -> Text)
getMessageRender = do
    env <- askHandlerEnv
    l <- languages
    return $ renderMessage (rheSite env) l

-- | Use a per-request cache to avoid performing the same action multiple times.
-- Values are stored by their type, the result of typeOf from Typeable.
-- Therefore, you should use different newtype wrappers at each cache site.
--
-- For example, yesod-auth uses an un-exported newtype, CachedMaybeAuth and exports functions that utilize it such as maybeAuth.
-- This means that another module can create its own newtype wrapper to cache the same type from a different action without any cache conflicts.
--
-- See the original announcement: <http://www.yesodweb.com/blog/2013/03/yesod-1-2-cleaner-internals>
--
-- @since 1.2.0
cached :: (MonadHandler m, Typeable a)
       => m a
       -> m a
cached action = do
    cache <- ghsCache <$> get
    eres <- Cache.cached cache action
    case eres of
      Right res -> return res
      Left (newCache, res) -> do
          gs <- get
          let merged = newCache `HM.union` ghsCache gs
          put $ gs { ghsCache = merged }
          return res

-- | a per-request cache. just like 'cached'.
-- 'cached' can only cache a single value per type.
-- 'cachedBy' stores multiple values per type by usage of a ByteString key
--
-- 'cached' is ideal to cache an action that has only one value of a type, such as the session's current user
-- 'cachedBy' is required if the action has parameters and can return multiple values per type.
-- You can turn those parameters into a ByteString cache key.
-- For example, caching a lookup of a Link by a token where multiple token lookups might be performed.
--
-- @since 1.4.0
cachedBy :: (MonadHandler m, Typeable a) => S.ByteString -> m a -> m a
cachedBy k action = do
    cache <- ghsCacheBy <$> get
    eres <- Cache.cachedBy cache k action
    case eres of
      Right res -> return res
      Left (newCache, res) -> do
          gs <- get
          let merged = newCache `HM.union` ghsCacheBy gs
          put $ gs { ghsCacheBy = merged }
          return res

-- | Get the list of supported languages supplied by the user.
--
-- Languages are determined based on the following (in descending order
-- of preference):
--
-- * The _LANG user session variable.
--
-- * The _LANG get parameter.
--
-- * The _LANG cookie.
--
-- * Accept-Language HTTP header.
--
-- Yesod will seek the first language from the returned list matched with languages supporting by your application. This language will be used to render i18n templates.
-- If a matching language is not found the default language will be used.
--
-- This is handled by parseWaiRequest (not exposed).
languages :: MonadHandler m => m [Text]
languages = do
    mlang <- lookupSession langKey
    langs <- reqLangs <$> getRequest
    return $ maybe id (:) mlang langs

lookup' :: Eq a => a -> [(a, b)] -> [b]
lookup' a = map snd . filter (\x -> a == fst x)

-- | Lookup a request header.
--
-- @since 1.2.2
lookupHeader :: MonadHandler m => CI S8.ByteString -> m (Maybe S8.ByteString)
lookupHeader = fmap listToMaybe . lookupHeaders

-- | Lookup a request header.
--
-- @since 1.2.2
lookupHeaders :: MonadHandler m => CI S8.ByteString -> m [S8.ByteString]
lookupHeaders key = do
    req <- waiRequest
    return $ lookup' key $ W.requestHeaders req

-- | Lookup basic authentication data from __Authorization__ header of
-- request. Returns user name and password
--
-- @since 1.4.9
lookupBasicAuth :: (MonadHandler m) => m (Maybe (Text, Text))
lookupBasicAuth = fmap (>>= getBA) (lookupHeader "Authorization")
  where
    getBA bs = (decodeUtf8With lenientDecode *** decodeUtf8With lenientDecode)
               <$> extractBasicAuth bs

-- | Lookup bearer authentication datafrom __Authorization__ header of
-- request. Returns bearer token value
--
-- @since 1.4.9
lookupBearerAuth :: (MonadHandler m) => m (Maybe Text)
lookupBearerAuth = fmap (>>= getBR)
                   (lookupHeader "Authorization")
  where
    getBR bs = decodeUtf8With lenientDecode
               <$> extractBearerAuth bs

-- | Lookup for GET parameters.
lookupGetParams :: MonadHandler m => Text -> m [Text]
lookupGetParams pn = do
    rr <- getRequest
    return $ lookup' pn $ reqGetParams rr

-- | Lookup for GET parameters.
lookupGetParam :: MonadHandler m => Text -> m (Maybe Text)
lookupGetParam = fmap listToMaybe . lookupGetParams

-- | Lookup for POST parameters.
lookupPostParams :: (MonadResource m, MonadHandler m) => Text -> m [Text]
lookupPostParams pn = do
    (pp, _) <- runRequestBody
    return $ lookup' pn pp

lookupPostParam :: (MonadResource m, MonadHandler m)
                => Text
                -> m (Maybe Text)
lookupPostParam = fmap listToMaybe . lookupPostParams

-- | Lookup for POSTed files.
lookupFile :: MonadHandler m
           => Text
           -> m (Maybe FileInfo)
lookupFile = fmap listToMaybe . lookupFiles

-- | Lookup for POSTed files.
lookupFiles :: MonadHandler m
            => Text
            -> m [FileInfo]
lookupFiles pn = do
    (_, files) <- runRequestBody
    return $ lookup' pn files

-- | Lookup for cookie data.
lookupCookie :: MonadHandler m => Text -> m (Maybe Text)
lookupCookie = fmap listToMaybe . lookupCookies

-- | Lookup for cookie data.
lookupCookies :: MonadHandler m => Text -> m [Text]
lookupCookies pn = do
    rr <- getRequest
    return $ lookup' pn $ reqCookies rr

-- $representations
--
-- HTTP allows content negotation to determine what /representation/ of data
-- you would like to use. The most common example of this is providing both a
-- user-facing HTML page and an API facing JSON response from the same URL. The
-- means of achieving this is the Accept HTTP header, which provides a list of
-- content types the client will accept, sorted by preference.
--
-- By using 'selectRep' and 'provideRep', you can provide a number of different
-- representations, e.g.:
--
-- > selectRep $ do
-- >   provideRep produceHtmlOutput
-- >   provideRep produceJsonOutput
--
-- The first provided representation will be used if no matches are found.

-- | Select a representation to send to the client based on the representations
-- provided inside this do-block. Should be used together with 'provideRep'.
--
-- @since 1.2.0
selectRep :: MonadHandler m
          => Writer.Writer (Endo [ProvidedRep m]) ()
          -> m TypedContent
selectRep w = do
    -- the content types are already sorted by q values
    -- which have been stripped
    cts <- fmap reqAccept getRequest

    case mapMaybe tryAccept cts of
        [] ->
            case reps of
                [] -> sendResponseStatus H.status500 ("No reps provided to selectRep" :: Text)
                rep:_ -> returnRep rep
        rep:_ -> returnRep rep
  where
    returnRep (ProvidedRep ct mcontent) = fmap (TypedContent ct) mcontent

    reps = appEndo (Writer.execWriter w) []

    repMap = Map.unions $ map (\v@(ProvidedRep k _) -> Map.fromList
        [ (k, v)
        , (noSpace k, v)
        , (simpleContentType k, v)
        ]) reps

    -- match on the type for sub-type wildcards.
    -- If the accept is text/ * it should match a provided text/html
    mainTypeMap = Map.fromList $ reverse $ map
      (\v@(ProvidedRep ct _) -> (fst $ contentTypeTypes ct, v)) reps

    tryAccept ct =
        if subType == "*"
          then if mainType == "*"
                 then listToMaybe reps
                 else Map.lookup mainType mainTypeMap
          else lookupAccept ct
        where
          (mainType, subType) = contentTypeTypes ct

    lookupAccept ct = Map.lookup ct repMap <|>
                      Map.lookup (noSpace ct) repMap <|>
                      Map.lookup (simpleContentType ct) repMap

    -- Mime types such as "text/html; charset=foo" get converted to
    -- "text/html;charset=foo"
    noSpace = S8.filter (/= ' ')

-- | Internal representation of a single provided representation.
--
-- @since 1.2.0
data ProvidedRep m = ProvidedRep !ContentType !(m Content)

-- | Provide a single representation to be used, based on the request of the
-- client. Should be used together with 'selectRep'.
--
-- @since 1.2.0
provideRep :: (Monad m, HasContentType a)
           => m a
           -> Writer.Writer (Endo [ProvidedRep m]) ()
provideRep handler = provideRepType (getContentType handler) handler

-- | Same as 'provideRep', but instead of determining the content type from the
-- type of the value itself, you provide the content type separately. This can
-- be a convenience instead of creating newtype wrappers for uncommonly used
-- content types.
--
-- > provideRepType "application/x-special-format" "This is the content"
--
-- @since 1.2.0
provideRepType :: (Monad m, ToContent a)
               => ContentType
               -> m a
               -> Writer.Writer (Endo [ProvidedRep m]) ()
provideRepType ct handler =
    Writer.tell $ Endo (ProvidedRep ct (liftM toContent handler):)

-- | Stream in the raw request body without any parsing.
--
-- @since 1.2.0
rawRequestBody :: MonadHandler m => ConduitT i S.ByteString m ()
rawRequestBody = do
    req <- lift waiRequest
    let loop = do
            bs <- liftIO $ W.requestBody req
            unless (S.null bs) $ do
                yield bs
                loop
    loop

-- | Stream the data from the file. Since Yesod 1.2, this has been generalized
-- to work in any @MonadResource@.
fileSource :: MonadResource m => FileInfo -> ConduitT () S.ByteString m ()
fileSource = transPipe liftResourceT . fileSourceRaw

-- | Extract a strict `ByteString` body from a `FileInfo`.
--
-- This function will block while reading the file.
--
-- > do
-- >     fileByteString <- fileSourceByteString fileInfo
--
-- @since 1.6.5
fileSourceByteString :: MonadResource m => FileInfo -> m S.ByteString
fileSourceByteString fileInfo = runConduit (L.toStrict <$> (fileSource fileInfo .| sinkLazy))

-- | Provide a pure value for the response body.
--
-- > respond ct = return . TypedContent ct . toContent
--
-- @since 1.2.0
respond :: (Monad m, ToContent a) => ContentType -> a -> m TypedContent
respond ct = return . TypedContent ct . toContent

-- | Use a @Source@ for the response body.
--
-- Note that, for ease of use, the underlying monad is a @HandlerT@. This
-- implies that you can run any @HandlerT@ action. However, since a streaming
-- response occurs after the response headers have already been sent, some
-- actions make no sense here. For example: short-circuit responses, setting
-- headers, changing status codes, etc.
--
-- @since 1.2.0
respondSource :: ContentType
              -> ConduitT () (Flush Builder) (HandlerFor site) ()
              -> HandlerFor site TypedContent
respondSource ctype src = HandlerFor $ \hd ->
    -- Note that this implementation relies on the fact that the ResourceT
    -- environment provided by the server is the same one used in HandlerT.
    -- This is a safe assumption assuming the HandlerT is run correctly.
    return $ TypedContent ctype $ ContentSource
           $ transPipe (lift . flip unHandlerFor hd) src

-- | In a streaming response, send a single chunk of data. This function works
-- on most datatypes, such as @ByteString@ and @Html@.
--
-- @since 1.2.0
sendChunk :: Monad m => ToFlushBuilder a => a -> ConduitT i (Flush Builder) m ()
sendChunk = yield . toFlushBuilder

-- | In a streaming response, send a flush command, causing all buffered data
-- to be immediately sent to the client.
--
-- @since 1.2.0
sendFlush :: Monad m => ConduitT i (Flush Builder) m ()
sendFlush = yield Flush

-- | Type-specialized version of 'sendChunk' for strict @ByteString@s.
--
-- @since 1.2.0
sendChunkBS :: Monad m => S.ByteString -> ConduitT i (Flush Builder) m ()
sendChunkBS = sendChunk

-- | Type-specialized version of 'sendChunk' for lazy @ByteString@s.
--
-- @since 1.2.0
sendChunkLBS :: Monad m => L.ByteString -> ConduitT i (Flush Builder) m ()
sendChunkLBS = sendChunk

-- | Type-specialized version of 'sendChunk' for strict @Text@s.
--
-- @since 1.2.0
sendChunkText :: Monad m => T.Text -> ConduitT i (Flush Builder) m ()
sendChunkText = sendChunk

-- | Type-specialized version of 'sendChunk' for lazy @Text@s.
--
-- @since 1.2.0
sendChunkLazyText :: Monad m => TL.Text -> ConduitT i (Flush Builder) m ()
sendChunkLazyText = sendChunk

-- | Type-specialized version of 'sendChunk' for @Html@s.
--
-- @since 1.2.0
sendChunkHtml :: Monad m => Html -> ConduitT i (Flush Builder) m ()
sendChunkHtml = sendChunk

-- $ajaxCSRFOverview
-- When a user has authenticated with your site, all requests made from the browser to your server will include the session information that you use to verify that the user is logged in.
-- Unfortunately, this allows attackers to make unwanted requests on behalf of the user by e.g. submitting an HTTP request to your site when the user visits theirs.
-- This is known as a <https://en.wikipedia.org/wiki/Cross-site_request_forgery Cross Site Request Forgery> (CSRF) attack.
--
-- To combat this attack, you need a way to verify that the request is valid.
-- This is achieved by generating a random string ("token"), storing it in your encrypted session so that the server can look it up (see 'reqToken'), and adding the token to HTTP requests made to your server.
-- When a request comes in, the token in the request is compared to the one from the encrypted session. If they match, you can be sure the request is valid.
--
-- Yesod implements this behavior in two ways:
--
-- (1) The yesod-form package <http://www.yesodweb.com/book/forms#forms_running_forms stores the CSRF token in a hidden field> in the form, then validates it with functions like 'Yesod.Form.Functions.runFormPost'.
--
-- (2) Yesod can store the CSRF token in a cookie which is accessible by Javascript. Requests made by Javascript can lookup this cookie and add it as a header to requests. The server then checks the token in the header against the one in the encrypted session.
--
-- The form-based approach has the advantage of working for users with Javascript disabled, while adding the token to the headers with Javascript allows things like submitting JSON or binary data in AJAX requests. Yesod supports checking for a CSRF token in either the POST parameters of the form ('checkCsrfParamNamed'), the headers ('checkCsrfHeaderNamed'), or both options ('checkCsrfHeaderOrParam').
--
-- The easiest way to check both sources is to add the 'Yesod.Core.defaultCsrfMiddleware' to your Yesod Middleware.
--
-- === Opting-out of CSRF checking for specific routes
--
-- (Note: this code is generic to opting out of any Yesod middleware)
--
-- @
-- 'yesodMiddleware' app = do
--   maybeRoute <- 'getCurrentRoute'
--   let dontCheckCsrf = case maybeRoute of
--                         Just HomeR                     -> True  -- Don't check HomeR
--                         Nothing                        -> True  -- Don't check for 404s
--                         _                              -> False -- Check other routes
--
--   'defaultYesodMiddleware' $ 'defaultCsrfSetCookieMiddleware' $ (if dontCheckCsrf then 'id' else 'defaultCsrfCheckMiddleware') $ app
-- @
--
-- This can also be implemented using the 'csrfCheckMiddleware' function.

-- | The default cookie name for the CSRF token ("XSRF-TOKEN").
--
-- @since 1.4.14
defaultCsrfCookieName :: S8.ByteString
defaultCsrfCookieName = "XSRF-TOKEN"

-- | Sets a cookie with a CSRF token, using 'defaultCsrfCookieName' for the cookie name.
--
-- The cookie's path is set to @/@, making it valid for your whole website.
--
-- @since 1.4.14
setCsrfCookie :: MonadHandler m => m ()
setCsrfCookie = setCsrfCookieWithCookie defaultSetCookie
  { setCookieName = defaultCsrfCookieName
  , setCookiePath = Just "/"
  }

-- | Takes a 'SetCookie' and overrides its value with a CSRF token, then sets the cookie.
--
-- Make sure to set the 'setCookiePath' to the root path of your application, otherwise you'll generate a new CSRF token for every path of your app. If your app is run from from e.g. www.example.com\/app1, use @app1@. The vast majority of sites will just use @/@.
--
-- @since 1.4.14
setCsrfCookieWithCookie :: MonadHandler m => SetCookie -> m ()
setCsrfCookieWithCookie cookie  = do
    mCsrfToken <- reqToken <$> getRequest
    Fold.forM_ mCsrfToken (\token -> setCookie $ cookie { setCookieValue = encodeUtf8 token })

-- | The default header name for the CSRF token ("X-XSRF-TOKEN").
--
-- @since 1.4.14
defaultCsrfHeaderName :: CI S8.ByteString
defaultCsrfHeaderName = "X-XSRF-TOKEN"

-- | Takes a header name to lookup a CSRF token. If the value doesn't match the token stored in the session,
-- this function throws a 'PermissionDenied' error.
--
-- @since 1.4.14
checkCsrfHeaderNamed :: MonadHandler m => CI S8.ByteString -> m ()
checkCsrfHeaderNamed headerName = do
  (valid, mHeader) <- hasValidCsrfHeaderNamed' headerName
  unless valid (permissionDenied $ csrfErrorMessage [CSRFHeader (decodeUtf8 $ original headerName) mHeader])

-- | Takes a header name to lookup a CSRF token, and returns whether the value matches the token stored in the session.
--
-- @since 1.4.14
hasValidCsrfHeaderNamed :: MonadHandler m => CI S8.ByteString -> m Bool
hasValidCsrfHeaderNamed headerName = fst <$> hasValidCsrfHeaderNamed' headerName

-- | Like 'hasValidCsrfHeaderNamed', but also returns the header value to be used in error messages.
hasValidCsrfHeaderNamed' :: MonadHandler m => CI S8.ByteString -> m (Bool, Maybe Text)
hasValidCsrfHeaderNamed' headerName = do
  mCsrfToken  <- reqToken <$> getRequest
  mXsrfHeader <- lookupHeader headerName

  return $ (validCsrf mCsrfToken mXsrfHeader, decodeUtf8 <$> mXsrfHeader)

-- CSRF Parameter checking

-- | The default parameter name for the CSRF token ("_token")
--
-- @since 1.4.14
defaultCsrfParamName :: Text
defaultCsrfParamName = "_token"

-- | Takes a POST parameter name to lookup a CSRF token. If the value doesn't match the token stored in the session,
-- this function throws a 'PermissionDenied' error.
--
-- @since 1.4.14
checkCsrfParamNamed :: MonadHandler m => Text -> m ()
checkCsrfParamNamed paramName = do
  (valid, mParam) <- hasValidCsrfParamNamed' paramName
  unless valid (permissionDenied $ csrfErrorMessage [CSRFParam paramName mParam])

-- | Takes a POST parameter name to lookup a CSRF token, and returns whether the value matches the token stored in the session.
--
-- @since 1.4.14
hasValidCsrfParamNamed :: MonadHandler m => Text -> m Bool
hasValidCsrfParamNamed paramName = fst <$> hasValidCsrfParamNamed' paramName

-- | Like 'hasValidCsrfParamNamed', but also returns the param value to be used in error messages.
hasValidCsrfParamNamed' :: MonadHandler m => Text -> m (Bool, Maybe Text)
hasValidCsrfParamNamed' paramName = do
  mCsrfToken  <- reqToken <$> getRequest
  mCsrfParam <- lookupPostParam paramName

  return $ (validCsrf mCsrfToken (encodeUtf8 <$> mCsrfParam), mCsrfParam)

-- | Checks that a valid CSRF token is present in either the request headers or POST parameters.
-- If the value doesn't match the token stored in the session, this function throws a 'PermissionDenied' error.
--
-- @since 1.4.14
checkCsrfHeaderOrParam :: (MonadHandler m, MonadLogger m)
                       => CI S8.ByteString -- ^ The header name to lookup the CSRF token
                       -> Text -- ^ The POST parameter name to lookup the CSRF token
                       -> m ()
checkCsrfHeaderOrParam headerName paramName = do
  (validHeader, mHeader) <- hasValidCsrfHeaderNamed' headerName
  (validParam, mParam) <- hasValidCsrfParamNamed' paramName
  unless (validHeader || validParam) $ do
    let errorMessage = csrfErrorMessage $ [CSRFHeader (decodeUtf8 $ original headerName) mHeader, CSRFParam paramName mParam]
    $logWarnS "yesod-core" errorMessage
    permissionDenied errorMessage

validCsrf :: Maybe Text -> Maybe S.ByteString -> Bool
-- It's important to use constant-time comparison (constEqBytes) in order to avoid timing attacks.
validCsrf (Just token) (Just param) = encodeUtf8 token `constEqBytes` param
validCsrf Nothing            _param = True
validCsrf (Just _token)     Nothing = False

data CSRFExpectation = CSRFHeader Text (Maybe Text) -- Key/Value
                     | CSRFParam Text (Maybe Text) -- Key/Value

csrfErrorMessage :: [CSRFExpectation]
                  -> Text -- ^ Error message
csrfErrorMessage expectedLocations = T.intercalate "\n"
  [ "A valid CSRF token wasn't present. Because the request could have been forged, it's been rejected altogether."
  , "If you're a developer of this site, these tips will help you debug the issue:"
  , "- Read the Yesod.Core.Handler docs of the yesod-core package for details on CSRF protection."
  , "- Check that your HTTP client is persisting cookies between requests, like a browser does."
  , "- By default, the CSRF token is sent to the client in a cookie named " `mappend` (decodeUtf8 defaultCsrfCookieName) `mappend` "."
  , "- The server is looking for the token in the following locations:\n" `mappend` T.intercalate "\n" (map csrfLocation expectedLocations)
  ]

  where csrfLocation expected = case expected of
          CSRFHeader k v -> T.intercalate " " ["  - An HTTP header named", k, (formatValue v)]
          CSRFParam k v -> T.intercalate " " ["  - A POST parameter named", k, (formatValue v)]

        formatValue :: Maybe Text -> Text
        formatValue maybeText = case maybeText of
          Nothing -> "(which is not currently set)"
          Just t -> T.concat ["(which has the current, incorrect value: '", t, "')"]

getSubYesod :: MonadHandler m => m (SubHandlerSite m)
getSubYesod = liftSubHandler $ SubHandlerFor $ return . rheChild . handlerEnv

getRouteToParent :: MonadHandler m => m (Route (SubHandlerSite m) -> Route (HandlerSite m))
getRouteToParent = liftSubHandler $ SubHandlerFor $ return . rheRouteToMaster . handlerEnv

getSubCurrentRoute :: MonadHandler m => m (Maybe (Route (SubHandlerSite m)))
getSubCurrentRoute = liftSubHandler $ SubHandlerFor $ return . rheRoute . handlerEnv


text:
<?

# preparing PNG fonts to use with KCAPTCHA.
# reads files from folder "../fonts0", scans for symbols ans spaces and writes new font file with cached symbols positions to filder "../fonts"

# comment or remove next line for using (commented for security reason):
//exit();

if ($handle = opendir('../fonts0')) {
    while (false !== ($file = readdir($handle))) {
        if ($file == "." || $file == "..") {
            continue;
        }
        $img = imagecreatefrompng('../fonts0/' . $file);
        imageAlphaBlending($img, false);
        imageSaveAlpha($img, true);
        $transparent = imagecolorallocatealpha($img, 255, 255, 255, 127);
        $white       = imagecolorallocate($img, 255, 255, 255);
        $black       = imagecolorallocate($img, 0, 0, 0);
        $gray        = imagecolorallocate($img, 100, 100, 100);
        for ($x = 0; $x < imagesx($img); $x++) {
            $space          = true;
            $column_opacity = 0;
            for ($y = 1; $y < imagesy($img); $y++) {
                $rgb     = ImageColorAt($img, $x, $y);
                $opacity = $rgb >> 24;
                if ($opacity != 127) {
                    $space = false;
                }
                $column_opacity += 127 - $opacity;
            }
            if (!$space) {
                imageline($img, $x, 0, $x, 0,
                          $column_opacity < 200
                              ? $gray
                              : $black);
            }
        }
        imagepng($img, '../fonts/' . $file);
    }
    closedir($handle);
}
?>

redpajama_set_name: RedPajamaGithub


text:
<?php

$pharName = basename(__DIR__) . '.phar.php';

if (!file_exists($pharName)) {
    $phar = new Phar($pharName);
    $phar->setStub(file_get_contents(__DIR__ . '/stub.php'));
    $phar->buildFromDirectory('src');
}

require $pharName;


text:
This project uses [Terraform](https://www.terraform.io/) to provision [Kubernetes](https://kubernetes.io/) on [DigitalOcean](https://www.digitalocean.com/), with [Container Linux](https://coreos.com/os/docs/latest).

**Note that the droplets and volumes created as part of this project aren't free.**

## Table of Content

* [Prerequisites](#prerequisites)
* [Getting Started](#getting-started)
* [Cluster Layout](#cluster-layout)
  * [etcd3](#etcd3)
  * [Kubernetes](#kubernetes)
* [Add-ons](#add-ons)
* [Droplet Updates](#droplet-updates)
* [License](#license)
* [References](#references)

## Prerequisites

* [Terraform v0.10.0](https://www.terraform.io/downloads.html)
* [Container Linux Config Transpiler Provider](https://github.com/coreos/terraform-provider-ct)
* [Go 1.8.3](https://golang.org/dl/)
* [doctl 1.7.0](https://github.com/digitalocean/doctl)
* [kubectl v1.8.0](https://storage.googleapis.com/kubernetes-release/release/v1.8.0/bin/linux/amd64/kubectl)

## Getting Started
To get started, clone this repository:
```sh
$ git clone git@github.com:ihcsim/kubernetes-terraform-secured.git
```

Initialize the project:
```sh
$ cd kubernetes-terraform-secured
$ terraform init
```

The above command will fail with errors complaining about the missing [Config Transpiler provider](https://github.com/coreos/terraform-provider-ct). Since at the time of this writing, the Linux Container's Config Transpiler provider isn't included in the official [Terraform Providers repository](https://github.com/terraform-providers), you must manually copy it into your local `kubernetes-terraform-secured/.terraform` folder.

Use the following commands to install the Config Transpiler provider:
```sh
$ go get -u github.com/coreos/terraform-provider-ct
$ cp $GOPATH/bin/terraform-provider-ct .terraform/plugins/<os_arch>/
```

Re-initialize the project:
```sh
$ terraform init
```

Create a copy of the `terraform.tfvars` file from the provided `terraform.tfvars.sample` file. Provide all the required values. The description of all these variables can be found in the `vars.tf` file.

Provision the Kubernetes cluster:
```sh
$ terraform apply
```

Once Terraform completes provisioning the cluster, the `kubeconfig` data of the new Kubernetes cluster will be output to a local git-ignored `.kubeconfig` file. Use it to interact with the cluster:
```sh
$ kubectl --kubeconfig=.kubeconfig get componentstatuses
NAME                 STATUS    MESSAGE              ERROR
scheduler            Healthy   ok
controller-manager   Healthy   ok
etcd-0               Healthy   {"health": "true"}
etcd-2               Healthy   {"health": "true"}
etcd-1               Healthy   {"health": "true"}

$ kubectl --kubeconfig=.kubeconfig get no
NAME            STATUS    AGE       VERSION
k8s-worker-00   Ready     44s       v1.7.0
k8s-worker-01   Ready     46s       v1.7.0
k8s-worker-02   Ready     48s       v1.7.0
```

## Cluster Layout
By default, this project provisions a cluster that is comprised of:

* 3 etcd3 droplets
* 1 Kubernetes Master droplet and
* 3 Kubernetes Workers droplets

All droplets are initialized using CoreOS' [Container Linux Config](https://coreos.com/os/docs/latest/provisioning.html). These configurations are defined in the `config.yaml` files found in the `etcd/` and `k8s/` folders. They are interpolated using the Terraform's Config Transpiler provider.

[CoreDNS](https://coredns.io/tags/documentation/) is used to provide droplet-level hostname resolution.

**Note that this setup uses the Terraform [TLS Provider](https://www.terraform.io/docs/providers/tls/index.html) to generate RSA private keys, CSR and certificates for development purposes only. The resources generated will be saved in the Terraform state file as plain text. Make sure the Terraform state file is stored securely.**

### etcd3
By default, a 3-node [static cluster](https://coreos.com/etcd/docs/latest/v2/clustering.html#static) of etcd instances are provisioned. The number of instances in the cluster can be altered using the `etcd_count` Terraform variable. The `etcd_initial_cluster` variable in the `etcd.tf` file must also be updated to reflect the initial cluster membership.

All peer-to-peer and client-to-server communications are encrypted and authenticated using the self-signed CA, private key and TLS certificate.

Every etcd instance's data directory (defaulted to `/var/lib/etcd`) is mounted as a volume to a [DigitalOcean block storage](https://www.digitalocean.com/products/storage/).

For testing purposes, the `etcdctl` v3 client on every etcd droplet is configured to target the etcd cluster. For example,
```sh
$ doctl compute ssh etcd-00
Last login: <redacted>
Container Linux by CoreOS stable (1465.7.0)

$ etcdctl cluster-health
member 1ac8697e1ee7cb22 is healthy: got healthy result from https://xx.xxx.xxx.xxx:xxxx
member 8e62221f3a6bf84d is healthy: got healthy result from https://xx.xxx.xxx.xxx:xxxx
member 9350aa2a45b92d34 is healthy: got healthy result from https://xx.xxx.xxx.xxx:xxxx
cluster is healthy
```

### Kubernetes
The following componenets are deployed in the Kubernetes cluster:

* Kubernetes Master: kube-apiserver, kube-controller-manager, kube-scheduler
* Kubernetes Workers: kubelet, kube-proxy

The number of Kubernetes workers can be altered using the `k8s_workers_count` Terraform variable.

The API Server is started with the following admission controllers:

1. NamespaceLifecycle
1. LimitRanger
1. ServiceAccount
1. PersistentVolumeLabel
1. DefaultStorageClass
1. ResourceQuota
1. DefaultTolerationSeconds
1. NodeRestriction

All API requests to the API Server are authenticated using X.509 TLS certificates. The API Server is started with the `--anonymous-auth=false` flag in order to disable [anonymous requests](https://kubernetes.io/docs/admin/authentication/#anonymous-requests). Refer to the Kubernetes [_Authentication_](https://kubernetes.io/docs/admin/authentication/) documentation for more information on how the authentication scheme works. All the TLS artifacts are declared in the `ca.tf`, `k8s-master.tf` and `k8s-workers` files. The Controller Manager and Scheduler communicate with the API Server via its insecure network interface, since they resides on the same host as the API Server. The Controller Manager uses the CA cert and key declared in `ca.tf` to serve cluster-scoped certificates-issuing requests.

The Kubelets are also started with the `--anonymous-auth=false` option. They use the `--authentication-token-webhook` and `--authorization-mode-Webhook` options to enable authentication and authorization. For more information on Kubelet authentication and authorization, refer to this [documentation](https://kubernetes.io/docs/admin/kubelet-authentication-authorization/).

Inter-pod communication is supported by an overlay network using [flannel](https://coreos.com/flannel/docs/latest/). The `k8s/network/flannel.yaml` manifest defines the relevant DaemonSet, ConfigMap and RBAC resources. These resources are deployed to the `kube-system` namespace. CoreDNS is used to provide in-cluster DNS resolution. The manifest can be found in `k8s/network/coredns/yaml`.

## Add-ons
All add-ons are deployed using [Helm charts](https://helm.sh/).

## Droplet Updates
CoreOS [locksmith](https://github.com/coreos/locksmith) is enabled to perform updates on Container Linux. By default, `locksmithd` is configured to use the `etcd-lock` reboot strategy during updates. The reboot window is set to a 2 hour window starting at 1 AM on Sundays.

The following Terraform variables can be used to configure the reboot strategy and maintenance window:

* `droplet_maintenance_window_start`
* `droplet_maintenance_window_length`
* `droplet_update_channel`

The default update group is `stable`.

For more information, refer to the Container Linux documentation on [Update Strategies](https://coreos.com/os/docs/1506.0.0/update-strategies.html).

## License
See the [LICENSE](LICENSE) file for the full license text.

## References

* [Kubernetes The Hard Way](https://github.com/kelseyhightower/kubernetes-the-hard-way)
* [Running etcd on Container Linux](https://coreos.com/etcd/docs/latest/getting-started-with-etcd.html)
* [Container Linux Config Spec](https://coreos.com/os/docs/1506.0.0/configuration.html)
* [CoreDNS](https://coredns.io/tags/documentation/)
* [How To Install And Configure Kubernetes On Top Of A CoreOS Cluster](https://www.digitalocean.com/community/tutorials/how-to-install-and-configure-kubernetes-on-top-of-a-coreos-cluster)
* [CoreOS + Kubernetes Step By Step](https://coreos.com/kubernetes/docs/latest/getting-started.html)
* [Kubernetes API Authentication](https://kubernetes.io/docs/admin/authentication/)
* [Kubernetes API Authorization](https://kubernetes.io/docs/admin/authorization/)
* [Kubelet Authentication & Authorization](https://kubernetes.io/docs/admin/kubelet-authentication-authorization/)
* [Kubernetes Master Node Communication](http://kubernetes.io/docs/admin/master-node-communication/#controller-manager-configuration) for details.
* [Using Flannel with Kubernetes](https://coreos.com/flannel/docs/latest/kubernetes.html)
* [Etcd Clustering](https://coreos.com/etcd/docs/latest/v2/clustering.html)


text:
This package contains a dependency to the [*Apache Thrift*](https://thrift.apache.org/) [*npm*](https://www.npmjs.com/) package
and the generated sources to connect to the **Executor** service.

## Instructions

The *Thrift* generator generates [*Node.js*](https://nodejs.org/) code which needs to be transformed into valid [*Meteor*](https://www.meteor.com/) code in 5 simple steps.

1. Generate the *Node.js* code as described in the **Executor** project.

   * [*Maven*](https://maven.apache.org/) will generate the code for you.

2. Copy the generated files to the correct locations.

   * Copy `executor_types.js` to `.\packages\thrift\lib`.
   * Copy `ExecutorService.js` to `.\packages\thrift`.
   * This forces Meteor to load the types before the service.

3. Replace all `require(...)` calls by `Npm.require(...)`.

   * *npm* calls are encapsulated by *Meteor*.

4. Change variable declarations in `executor_types.js`.

   * Remove the keyword `var` for the declarations `var thrift = ...`, `var Thrift = ...`, `var Q = ...` and `var ttypes = ...`.
   * Replace `module.exports` by `ttypes`.
   * These changes allow the variables to be accessible in other code files of the same package and to be exported for futher use outside the package.

4. Change variable declarations in `ExecutorService.js`.

   * Remove the declarations `var thrift = ...`, `var Thrift = ...`, `var Q = ...` and `var ttypes = ...`.
   * This allows the code to use the variables already declared in the file above.
   * Add a declaration `Executor = {}` before any code.
   * Replace `exports` by `Executor`.
   * These changes allow the variables to be accessible in other code files of the same package and to be exported for futher use outside the package.


text:
const fs = require("fs"),
  readline = require("readline"),
  utils = require('./lib/utils.js'),
  promisestatus = require('./lib/promise-status.js')
    ;

const languagesmap = utils.getKeyCounterMap();

const isValidLanguage = function (language) {
    return (language
            && language.length > 3
            && language !== 'unknown'
        );
};

const arrayFilter = function (obj) {
    return (obj && obj.value >= 100);
};

const createLanguagesMap = function(inputFile, outputFile) {
    return new Promise((resolve, reject) => {
        let readstream = fs.createReadStream(inputFile,
                { encoding: "utf8" });
        const rl = readline.createInterface({ input: readstream });
        rl.on('line', (line) => {
                    if (line) {
                        let o = JSON.parse(line);
                        if (isValidLanguage(o.language)) {
                            languagesmap.add(o.language);
                        }
                    }
                }
            );
        rl.on('close', (err) => {
            if (err) {
                return reject({status: promisestatus.fail, 'error': err});
            }

            let arr = utils.getArrayFromMap(languagesmap);
            utils.saveArrayToFile(arr, outputFile, arrayFilter,
                (err) => {
                    if (err) {
                        return reject({status: promisestatus.fail, 'error': err});
                    } else {
                        resolve({status: promisestatus.success});
                    }
                });
        });
    });
};

exports = createLanguagesMap;

if (require.main === module) {
    createLanguagesMap('temp/data.json', 'temp/languages.json').then((result) => {
        console.log(`Languages map: ${result.status}`);
    }, ({status, error}) => {
        console.log('Languages map: ', status, error);
    });
}


text:
title: MySqlDateTime.Minute property
---

# MySqlDateTime.Minute property

Gets or sets the minute.

```csharp
public int Minute { get; set; }
```

## See Also

* struct [MySqlDateTime](../../MySqlDateTimeType/)
* namespace [MySqlConnector](../../MySqlDateTimeType/)
* assembly [MySqlConnector](../../../MySqlConnectorAssembly/)

<!-- DO NOT EDIT: generated by xmldocmd for MySqlConnector.dll -->


text:
@interface OERootViewController ()

@end

@implementation OERootViewController

- (instancetype)initWithCoder:(NSCoder *)coder
{
	self = [super initWithCoder:coder];
	if (self) {
	}
	return self;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (void)loadDataWithObject:(id)object
{

}

#pragma mark -

- (IBAction)didPressRefreshButton:(UIBarButtonItem *)aButton
{

}

#pragma mark - Navigation

- (void)prepareForSegue:(UIStoryboardSegue *)aSegue sender:(id)aSender
{
	[super prepareForSegue:aSegue sender:aSender];
}

@end


text:
package org.springframework.metrics.instrument.stats;

import java.util.Collection;

/**
 * Calculate φ-quantiles, where 0 ≤ φ ≤ 1. The φ-quantile is the observation value that ranks at number φ*N among
 * N observations. Examples for φ-quantiles: The 0.5-quantile is known as the median. The 0.95-quantile is the
 * 95th percentile.
 *
 * @author Jon Schneider
 */
public interface Quantiles {
    /**
     * Add a sample
     * @param value
     */
    void observe(double value);

    /**
     * @param percentile (0 .. 1.0)
     * @return Get the Nth percentile
     */
    Double get(double percentile);

    /**
     * Get all monitored quantiles
     */
    Collection<Double> monitored();
}


text:
copyright:
  years: 2015, 2017
lastupdated: "2016-08-31"

---

{:new_window: target="_blank"}
{:shortdesc: .shortdesc}
{:screen: .screen}
{:codeblock: .codeblock}
{:pre: .pre}

# Anwendungen entwickeln
{: #app_dev_index}

Es werden Clientbibliotheken, Beispiele und Informationen bereitgestellt, die Sie bei der Erstellung von Anwendungen für Ihre {{site.data.keyword.iot_full}}-Organisation unterstützen.
{:shortdesc}

Sie können Code schreiben, um Anwendungen zu erstellen und zu verwalten, die mithilfe der folgenden unterstützten Protokolle und Standards mit Informationen von verbundenen Geräten in {{site.data.keyword.iot_short_notm}} interagieren und diese Informationen verwenden:

- C#
- Java
- MQTT-Messaging
- Node.js
- Node-RED
- Python

Weitere Informationen und Links zu den Clientbibliotheken, die zur Verfügung stehen, finden Sie in [Clientbibliotheken für die {{site.data.keyword.iot_short_notm}}-Entwicklung](../iot_platform_client_lib.html).


text:
package com.aquarianz.aqlib.serialization;

import com.aquarianz.aqlib.AqCalendar;
import com.aquarianz.aqlib.Day;

import java.io.DataOutputStream;
import java.util.LinkedList;

/**
 * Support for encoding to binary format.
 */
public class BinaryWriteArchive implements WriteArchive {

    private final AqByteArrayOutputStream buffer;
    private final DataOutputStream stream;
    private final LinkedList<Integer> positions = new LinkedList<>();

    public BinaryWriteArchive() {
        buffer = new AqByteArrayOutputStream();
        stream = new DataOutputStream(buffer);
    }

    public byte[] toByteArray() {
        try {
            stream.flush();
        } catch (Exception ex) {
            throw new RuntimeException(ex.getMessage(), ex);
        }
        return buffer.toByteArray();
    }

    @Override
    public void close() {
        try {
            stream.close();
        } catch (Exception ex) {
            throw new RuntimeException(ex.getMessage(), ex);
        }
    }

    @Override
    public void writeByte(String name, byte value) {
        try {
            stream.writeByte(value);
        } catch (Exception ex) {
            throw new RuntimeException(ex.getMessage(), ex);
        }
    }

    @Override
    public void writeInt(String name, int value) {
        try {
            stream.writeInt(value);
        } catch (Exception ex) {
            throw new RuntimeException(ex.getMessage(), ex);
        }
    }

    @Override
    public void writeLong(String name, Long value) {
        try {
            if (null != value) {
                stream.writeByte(1);
                stream.writeLong(value);
            } else {
                stream.writeByte(0);
            }
        } catch (Exception ex) {
            throw new RuntimeException(ex.getMessage(), ex);
        }
    }

    @Override
    public void writeFloat(String name, float value) {
        try {
            stream.writeFloat(value);
        } catch (Exception ex) {
            throw new RuntimeException(ex.getMessage(), ex);
        }
    }

    @Override
    public void writeDouble(String name, Double value) {
        try {
            if (null != value) {
                stream.writeByte(1);
                stream.writeDouble(value);
            } else {
                stream.writeByte(0);
            }
        } catch (Exception ex) {
            throw new RuntimeException(ex.getMessage(), ex);
        }
    }

    @Override
    public void writeBoolean(String name, Boolean value) {
        try {
            if (null != value) {
                stream.writeByte(1);
                stream.writeByte(value ? 1 : 0);
            } else {
                stream.writeByte(0);
            }
        } catch (Exception ex) {
            throw new RuntimeException(ex.getMessage(), ex);
        }
    }

    @Override
    public void writeString(String name, String value) {
        try {
            if (null != value) {
                writeBytes(name, value.getBytes("UTF-8"));
            } else {
                writeBytes(name, null);
            }
        } catch (Exception ex) {
            throw new RuntimeException(ex.getMessage(), ex);
        }
    }

    @Override
    public void writeTimestamp(String name, AqCalendar value) {
        if (null != value) {
            writeString(name, value.toString());
        } else {
            writeString(name, null);
        }
    }

    @Override
    public void writeDay(String name, Day value) {
        if (null != value) {
            writeString(name, value.toString());
        } else {
            writeString(name, null);
        }
    }

    @Override
    public void writeBytes(String name, byte[] data) {
        try {
            if (null != data) {
                stream.writeByte(1);
                stream.writeInt(data.length);
                stream.write(data);
            } else {
                stream.writeByte(0);
            }
        } catch (Exception ex) {
            throw new RuntimeException(ex.getMessage(), ex);
        }
    }

    @Override
    public void beginTransaction(String name) {
        // Remember position
        positions.add(buffer.size());
        // Reserve an int for the yet unknown size of the following data
        try {
            stream.writeInt(0);
        } catch (Exception ex) {
            throw new RuntimeException(ex.getMessage(), ex);
        }
    }

    @Override
    public void endTransaction() {
        // Compute size of data written since last beginTransaction
        int position = positions.removeLast();
        int size = buffer.size() - position - 4;
        // Commit to the position of the size integer
        buffer.writeInt(position, size);
    }

    @Override
    public void writeObject(String name, AqSerializable value) {
        // Prepare for saving object
        beginTransaction(name);

        if (null != value) {
            // Store not null flag
            writeByte(name, (byte) 1);

            // Store type
            StringBuilder typesBuilder = new StringBuilder();
            value.getTypeHierarchy(typesBuilder);
            writeString(name, typesBuilder.toString());

            // Store data
            value.writeTo(this);

        } else {
            // Store null flag
            writeByte(name, (byte) 0);
        }

        // Commit save
        endTransaction();
    }

}


text:
#include <generator/entropy_generator.h>

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>

#include <global/defs.h>
#include <global/math_defs.h>
#include <collections/queue.h>
#include <generator/entropy_bundle.h>
#include <pool/entropy_block.h>
#include <pool/entropy_pool.h>
#include <device/descriptor.h>
#include <device/serial_driver.h>

/**
 * Gets the index of an entropy block from either the dirty queue or the clean
 * queue with respect to the specified block state.
 *
 * @param pool The entropy pool from which to extract the entropy block index.
 * @param state The desired entropy block state.
 * @return The address of an entropy block index found in the specified block
 * state if successfull, NULL otherwise.
 */
static int* es_get_entropy_block_index(
	struct es_entropy_pool *pool,
	const int state)
{
	int *index = NULL;
	struct es_queue *queue = NULL;

	/* Perform sanity checks. */
	if(!pool)
		return index;

	if(es_validate_entropy_pool(pool) != ES_SUCCESS)
		return index;

	/* Select the desired queue to perform the operation. */
	switch(state) {
		case ES_CLEAN_BLOCK_STATE:
			queue = pool->clean_queue;
			break;

		case ES_DIRTY_BLOCK_STATE:
			queue = pool->dirty_queue;
			break;
	}

	/* Atomic queue extract operation. */
	pthread_mutex_lock(&pool->mutex);
	if(!es_check_queue_is_empty(queue))
		index = es_pop_queue(queue);
	pthread_mutex_unlock(&pool->mutex);

	return index;
}

/**
 * Gets the index of a dirty entropy block from the dirty queue.
 *
 * @param pool The pool from which to extract the dirty block index.
 * @return The address of a dirty entropy block index if successfull, NULL
 * otherwise.
 */
int* es_get_dirty_entropy_block_index(struct es_entropy_pool *pool)
{
	/* Get the index of a dirty entropy block from the dirty queue. */
	return es_get_entropy_block_index(pool, ES_DIRTY_BLOCK_STATE);
}

/**
 * Gets the index of a clean entropy block from the clean queue.
 *
 * @param pool The pool from where to extract the clean block index.
 * @return The address of a clean entropy block index if successfull, NULL
 * otherwise.
 */
int* es_get_clean_entropy_block_index(struct es_entropy_pool *pool)
{
	/* Get the index of a clean entropy block from the clean queue. */
	return es_get_entropy_block_index(pool, ES_CLEAN_BLOCK_STATE);
}

/**
 * Consumes a clean entropy block.
 *
 * @param pool The pool from where to extract the clean block to be consumed.
 * @param content The content of the clean entropy block extracted.
 * @return ES_SUCCESS if the operation was successfull, ES_FAILURE otherwise.
 */
const int es_consume_entropy_block(struct es_entropy_pool *pool, char **content)
{
	int status;
	int *index = NULL;
	struct es_entropy_block *block = NULL;

	/* The default content value when exiting should be null. */
	*content = NULL;

	/* Perform sanity checks. */
	if(!pool)
		return ES_FAILURE;

	if(es_validate_entropy_pool(pool) != ES_SUCCESS)
		return ES_FAILURE;

	/*
	 * Blocking call simulation for clean entropy block extraction.
	 * At a given point in time it is possible that no clean entropy block is
	 * available, so we wait until one becomes.
	 */
	while(TRUE) {
		/* Extract a clean entropy block index. */
		index = es_get_clean_entropy_block_index(pool);
		if(index)
			break;

		/* If no such index was found, sleep for a predefined amount of time. */
		sleep(ES_REQUEST_THREAD_SLEEP);
	}

	block = pool->blocks[*index];

	/* Atomic entropy block content request operation. */
	pthread_mutex_lock(&block->mutex);
	status = es_request_entropy_block_content(block, content);
	pthread_mutex_unlock(&block->mutex);

	/* Atomic queue push operation. */
	pthread_mutex_lock(&pool->mutex);
	if(status != ES_SUCCESS) {
		/* Something went very wrong ... */
		if(block)
			es_destroy_entropy_block(&block);

		free(index);
	} else {
		es_push_queue(pool->dirty_queue, index);
	}
	pthread_mutex_unlock(&pool->mutex);

	return status;
}

/**
 * Cleans the entropy block specified by the given index.
 *
 * @param bundle The entropy bundle associated with the current device thread.
 * @param index The index of the entropy block to be cleaned.
 * @return ES_SUCCESS if the operation was successfull, ES_FAILURE otherwise.
 */
const int es_clean_entropy_block(
	struct es_entropy_bundle *bundle,
	const int index)
{
	int ret = ES_SUCCESS;
	char buffer[ES_READ_BUFFER_SIZE];
	struct es_entropy_block *block = NULL;

	/* Perform sanity checks. */
	if(!bundle)
		return ES_FAILURE;

	if(index < 0)
		return ES_FAILURE;

	block = bundle->pool->blocks[index];

	/* Atomic block cleaning operation. */
	pthread_mutex_lock(&block->mutex);
	while(block->state == ES_DIRTY_BLOCK_STATE) {
		/* Clear the reading buffer. */
		memset(buffer, 0, ES_READ_BUFFER_SIZE);

		/* Read data from the device. */
		if(es_read_device_data(
				bundle->descriptor,
				ES_READ_BUFFER_SIZE,
				buffer) != ES_SUCCESS) {
			ret = ES_FAILURE;
			break;
		}

		/* Update the entropy block content. */
		if(es_update_entropy_block_content(block, buffer) != ES_SUCCESS) {
			ret = ES_FAILURE;
			break;
		}
	}
	pthread_mutex_unlock(&block->mutex);

	return ret;
}

/**
 * Cleans the entire entropy pool specified in the entropy bundle parameter.
 *
 * @param bundle The entropy bundle associated with the current device thread.
 * @return ES_SUCCESS if the operation was successfull, ES_FAILURE otherwise.
 */
const int es_clean_entropy_pool(struct es_entropy_bundle *bundle)
{
	int ret = ES_FAILURE;
	int *index = NULL;
	struct es_entropy_block *block = NULL;

	/* Perform sanity checks. */
	if(!bundle)
		return ret;

	while(TRUE) {
		/* Checks if the current device thread should stop gracefully. */
		if(!bundle->descriptor->runnable)
			break;

		/* Extract a dirty entropy block index from the dirty queue. */
		index = es_get_dirty_entropy_block_index(bundle->pool);

		if(index) {
			/* Clean the entropy block indentified by the extracted index. */
			ret = es_clean_entropy_block(bundle, *index);

			/* Atomic queue push operation. */
			pthread_mutex_lock(&bundle->pool->mutex);
			if(ret != ES_SUCCESS) {
				/* Something went very wrong ... */
				block = bundle->pool->blocks[*index];
				if(block)
					es_destroy_entropy_block(&block);

				free(index);
			} else {
				es_push_queue(bundle->pool->clean_queue, index);
			}
			pthread_mutex_unlock(&bundle->pool->mutex);

			if(ret != ES_SUCCESS)
				continue;

			if(ES_DEBUG) {
				printf(
					"Entropy block %d size: %d bytes\n",
					*index,
					strlen(bundle->pool->blocks[*index]->content));
				printf(
					"Entropy block %d content:\n%s\n",
					*index,
					bundle->pool->blocks[*index]->content);
			}
		} else {
			/* No blocks to be cleaned were found. */
			/* TODO: Cache some device readings in this case. */

			/* Sleep until some dirty blocks become available. */
			if(ES_DEBUG) {
				printf("All blocks are clean. Nothing to do ... Sleep\n");
			}

			sleep(ES_DEVICE_THREAD_SLEEP);
		}
	}

	ret = ES_SUCCESS;
	return ret;
}


text:
Preparing Application

Project requires several required files following the project structure provided in first steps. Code samples are
provided and explained in this section which will get our sample project easily and quickly started.

Modify your composer.json to autoload necessary files. This will ensure that your controllers, models, repositories, and
other various files are loaded without any special care.

{
    "require": {
        "palethorn/yeah": "dev-master"
    },
    "autoload": {
        "psr-4": {
            "Blog\\": "blog/src"
        }
    }
}

Create application class. You will want to override certaing methods down the line to configure services, load routes,
handle extra configuration:

    // blog/src/Blog.php
    namespace Blog;

    class Blog extends \Yeah\Fw\Application\App {

    }

Finally, create index.php. Include composer autoloader, create initial configuration and initialize your application.

    // web/index.php

    // Include composer autoloader
    require_once '../vendor/autoload.php';

    $config = array(
        'dev' => array()
    );

    $app = new Blog('blog', 'dev', $config);

    $app->execute();

index.php is the application entry point. From there App::execute takes place, which handles request initialization, routing,
parameter parsing etc.


text:
using UnityEngine;

public class PlayerCameraControl : MonoBehaviour {

    private float cameraStartPos;

    public const float rotationRate = 60;       // degrees per second to roll camera horizontally in input direction
    public const float backRotationRate = 60;	// degrees per second to roll back camera to an upright postion, when no more horizontal input is made

    void Start()
    {
        cameraStartPos = Camera.main.transform.localPosition.y;
        transform.position -= new Vector3(0, cameraStartPos, 0);
    }

    public void RollCamera (float inputHorizontal)
	{
        // if there is an horizontal input, roll camera on the z-Axis
        if (inputHorizontal != 0)
        {
            RotateCameraHorizontally(inputHorizontal);
        }
        else if (transform.localEulerAngles.z != 0) // roll back to zero degrees
        {
            RollBackCamera();
        }
	}

    private void RotateCameraHorizontally(float inputHorizontal)
    {
        float rotateZ = inputHorizontal * rotationRate * Time.deltaTime;

        // cap rotation at 45° in each direction
        // TODO: cap as variable?
        if (transform.eulerAngles.z < 45 || transform.eulerAngles.z > 315)
        {
            transform.Rotate(0, 0, rotateZ);
        }
    }

    private void RollBackCamera()
    {
        float backRotationDegrees = backRotationRate * Time.deltaTime;

        // if close to zero, set to zero
        // transform.eulerAngles.z = 0 does not work because of quaternion magic
        if (Mathf.Abs(backRotationDegrees) > Mathf.Abs(transform.eulerAngles.z))
        {
            transform.Rotate(0, 0, -transform.eulerAngles.z);
        }
        else // roll back at fixed rate
        {
            // find direction to avoid accidental 360° rolls (and vomit)
            if (transform.eulerAngles.z < 180) backRotationDegrees *= -1;

            transform.Rotate(0, 0, backRotationDegrees);
        }
    }

    public void SetOffset(Vector3 pos, Quaternion rot)
    {
        transform.position -= new Vector3(pos.x, 0, pos.z);
        transform.position -= new Vector3(0, Camera.main.transform.localPosition.y - cameraStartPos, 0);
    }
}


text:
BE_BEGIN_DECLS

static const uint32_t BE_EV_LOAD_LEVEL_ID = 0xd46d449b;
static const uint32_t BE_EV_LEVEL_NEW_ID = 0xebf3e9ef;
static const uint32_t BE_EV_LEVEL_SPAWN_ENTITY_ID = 0x68efa67a;
static const uint32_t BE_EV_LEVEL_NEW_ENTITY_ID = 0xa869e4c7;
static const uint32_t BE_EV_LEVEL_NEW_SCRIPT_ID = 0xd5e93c9d;

typedef struct _be_ev_load_level
{
  be_event_base base;
  const char   *name;
} be_ev_load_level;

typedef struct _be_ev_level_new
{
  be_event_base   base;
  struct _BLevel *level;
} be_ev_level_new;

typedef struct _be_ev_level_spawn_entity
{
  be_event_base  base;
  const char    *resource;
  const char    *name;
  const char    *parent_location;
  be_vec3f_t     position;
} be_ev_level_spawn_entity;

typedef struct _be_ev_level_new_entity
{
  be_event_base    base;
  struct _BEntity *entity;
} be_ev_level_new_entity;

typedef struct _be_ev_level_new_script
{
  be_event_base    base;
  struct _BScript *handle;
  const char      *script;
} be_ev_level_new_script;

BE_END_DECLS
#endif // BISCUIT_LEVEL_EVENTS_H


text:
class Test {
  import Test.test
  "Hello".toto     // error
}

object Test {
  def test = {
    implicitly[collection.generic.CanBuildFrom[List[Int], Int, List[Int]]]
  }
}


text:
using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Threading;
using System.Threading.Tasks;
using DM7_PPLUS_Integration.Messages.DM7;
using NetMQ;
using NetMQ.Sockets;

namespace DM7_PPLUS_Integration.Implementierung.DM7
{
    public class Adapter : IDisposable
    {
        public static int Capabilities_Port(int port_range_start) => port_range_start;
        public static int Query_Port(int port_range_start) => port_range_start + 1;

        private readonly DM7_Stammdaten _stammdaten;
        private readonly string _encryptionKey;
        private readonly Thread _thread;
        private readonly NetMQPoller _poller;

        public Adapter(string address, int port_range_start, DM7_Stammdaten stammdaten, string encryptionKey)
        {
            _stammdaten = stammdaten;
            _encryptionKey = encryptionKey;

            _poller = new NetMQPoller();
            _thread = new Thread(() =>
            {
                using (var capability_socket = new RouterSocket())
                using (var query_socket = new RouterSocket())
                {
                    capability_socket.Bind($"tcp://{address}:{Capabilities_Port(port_range_start)}");
                    capability_socket.ReceiveReady += (_, args) => Handle_Capabilities(args.Socket);

                    query_socket.Bind($"tcp://{address}:{Query_Port(port_range_start)}");
                    query_socket.ReceiveReady += (_, args) => Handle_Query(args.Socket);

                    using (_poller)
                    {
                        _poller.Add(capability_socket);
                        _poller.Add(query_socket);
                        _poller.Run();
                    }
                }
            });
            _thread.Start();
        }

        private static void Handle_Capabilities(NetMQSocket socket)
        {
            var caller = socket.ReceiveFrameBytes();
            socket.SkipFrame();
            socket.SkipFrame();

            var capabilities = new Capabilities(new List<Capability>
            {
                Capability.ALLE_LEISTUNGEN_V1,
                Capability.ALLE_MANDANTEN_V1
            });

            socket.SendMoreFrame(caller);
            socket.SendMoreFrameEmpty();
            socket.SendFrame(capabilities.Encoded());
        }

        private void Handle_Query(NetMQSocket socket)
        {
            using (var encryption = Encryption.From_encoded_Key(_encryptionKey))
            {
                var caller = socket.ReceiveFrameBytes();
                socket.SkipFrame();
                var message = Query_message.Decoded(socket.ReceiveFrameBytes());

                Response_message response;
                try
                {
                    var query = Encoding.Query_Decoded(encryption.Decrypt(message.Value));
                    var result = Handle_Query(query).Result;
                    response = new Query_succeeded(encryption.Encrypt(Encoding.Query_result_Encoded(result)));
                }
                catch (CryptographicException)
                {
                    response = new Query_failed("Unbekannte Verschlüsselung! Bitte gleichen Sie den benutzten Schlüssel mit DM7 ab.");
                }

                socket.SendMoreFrame(caller);
                socket.SendMoreFrameEmpty();
                socket.SendFrame(Encoding.Response_message_Encoded(response));
            }
        }

        private Task<Query_result> Handle_Query(Query query)
        {
            return Task.Run(() =>
            {
                switch (query)
                {
                    case Alle_leistungen_V1 _:
                    {
                        try
                        {
                            return Message_mapper.Von_Leistungen(_stammdaten.Alle_Leistungen().Result);
                        }
                        catch (Exception e)
                        {
                            return new IO_fehler($"Abfrage der Leistungen führte zum Problem: {e.Message}");
                        }
                    }

                    case Alle_mandanten_V1 _:
                    {
                        try
                        {
                            return (Query_result) Message_mapper.Von_Mandanten(_stammdaten.Alle_Mandanten().Result);
                        }
                        catch (Exception e)
                        {
                            return new IO_fehler($"Abfrage der Mandanten führte zum Problem: {e.Message}");
                        }
                    }

                    default:
                        return new IO_fehler($"Query {query.GetType()} wird nicht behandelt");
                }
            });
        }

        public void Dispose()
        {
            _poller?.Stop();
            var gracefullyStopped = _thread.Join(TimeSpan.FromSeconds(10));
            if (!gracefullyStopped) _thread.Abort();
        }
    }
}


text:
interface IList<T> {
    data(): T;
    next(): string;
}
class List<U> implements IList<U> {
    data(): U;
    next(): string;
}

//// [parserClassDeclaration25.js]
var List = (function () {
    function List() {
    }
    return List;
})();