packages feed

cgi-utils (empty) → 0.0

raw patch · 4 files changed

+187/−0 lines, 4 filesdep +basedep +cgidep +containerssetup-changed

Dependencies added: base, cgi, containers, mtl, random

Files

+ LICENSE view
@@ -0,0 +1,29 @@+Copyright 2010 Chris Done. All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are+met:++   1. Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++   2. Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++THIS SOFTWARE IS PROVIDED BY CHRIS DONE ``AS IS'' AND ANY+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR+PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL CHRIS DONE OR+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.++The views and conclusions contained in the software and documentation+are those of the authors and should not be interpreted as representing+official policies, either expressed or implied, of Chris Done.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ cgi-utils.cabal view
@@ -0,0 +1,18 @@+Name: cgi-utils+Version: 0.0+Maintainer: Chris Done (chrisdone@gmail.com)+Author: Chris Done (chrisdone@gmail.com)+Copyright: 2010 Chris Done+License: BSD3+Category: CGI, Web+Synopsis: Simple modular utilities for CGI/FastCGI (sessions, etc.)+Description: Simple modular utilities for CGI/FastCGI that one tends to always need including sessions state.+Homepage: http://github.com/chrisdone/haskell-cgi-utils+Build-Type: Simple+Cabal-Version: >= 1.2+License-File: LICENSE++Library+  Exposed-Modules: Network.CGI.Session+  Hs-Source-Dirs: src+  Build-Depends: base >= 2 && < 4, cgi, random, containers, mtl
+ src/Network/CGI/Session.hs view
@@ -0,0 +1,138 @@+module Network.CGI.Session +    (-- * Types+     Session+    ,Sessions+    ,SessionName+    ,SessionM+     -- * Initialising and querying/updating+    ,makeSessions+    ,initSession+    ,updateSession+    -- * A simple Session monad.+    ,runSessionCGI+    ,runSession+    ,sessionIns+    ,sessionDel+    ,sessionGet+    -- * Utilities+    ,makeSession+    ,getSession)+        where++import Control.Applicative+import Control.Concurrent.MVar (MVar,modifyMVar,readMVar,modifyMVar_,newMVar)+import Control.Monad.Trans (liftIO,lift)+import Control.Monad.State (StateT,evalStateT,get,modify)+import Foreign.Marshal.Alloc (alloca)+import Foreign.Storable (peek,sizeOf)+import Data.Function+import Data.List (nub)+import Data.Map (Map)+import qualified Data.Map as M+import System.IO (openBinaryFile,IOMode(ReadMode),hGetBuf,hClose)+import System.Random (mkStdGen,StdGen,randomRs)+import qualified Network.CGI as CGI++--------------------------------------------------------------------------------+-- Section: Sessions implementation+-- Keywords: sessions+-- Description: A poor man's implementation of sessions for CGI.++-- | A simple Session monad. Recommend you define your own.+type SessionM = StateT Session (CGI.CGIT IO)++-- | Initialise a session state and start a F/CGI process. This is a+--   bit of a pattern so I've included it here for convenience.+runSessionCGI :: SessionName+              -> (CGI.CGI CGI.CGIResult -> IO ())+              -> SessionM CGI.CGIResult+              -> IO ()+runSessionCGI name run m = do+  sessions <- makeSessions+  run $ CGI.handleErrors $ do+    session <- initSession name sessions+    runSession sessions m session++-- | Simple session runner.+runSession :: Sessions -> SessionM a -> Session -> CGI.CGI a+runSession sessions = evalStateT++-- | Session value getter.+sessionGet :: Read a => String -> SessionM (Maybe a)+sessionGet k = get >>= \Session{sess_values=vs} ->+           return $ read <$> M.lookup k vs++-- | Session value inserter/updater.+sessionIns :: (Read a,Show a) => String -> a -> (a -> a -> a) -> SessionM ()+sessionIns k v f =+    -- Technically the other two accessors could be defined in terms of this+    -- but it might confuse (me).+    modify $ \s@Session{sess_values = vs} ->+         s {sess_values = M.insertWith lf k (show v) vs }+            where lf = (show .) . (f `on` read)++-- | Session value deleter.+sessionDel ::  String -> SessionM ()+sessionDel k =+    modify $ \s@Session{sess_values = vs} ->+        s {sess_values = M.delete k vs }++-- | A session consists of a unique id and a map.+data Session = Session+    { sess_id     :: Integer+    , sess_values :: Map String String+    } deriving (Eq,Show)++-- | Sessions and unique ids are stored in an MVar.+type Sessions = MVar ([Integer],Map Integer Session)++-- | The cookie prefix (e.g. MYHASKELLCOOKIE).+type SessionName = String++-- | Make the sessions state.+makeSessions :: IO Sessions+makeSessions = genIds >>= newMVar . flip (,) M.empty++-- | Generate random.+genIds :: IO [Integer]+genIds = nub . randomRs (1,1000^(20::Int)) <$> betterStdGen++-- | An OK-ish random generator.+betterStdGen :: IO StdGen+betterStdGen = alloca $ \p -> do+    h <- openBinaryFile "/dev/urandom" ReadMode+    hGetBuf h p $ sizeOf (undefined :: Int)+    hClose h+    mkStdGen <$> peek p++-- | Grab the session or create a new one.+initSession :: SessionName -> Sessions -> CGI.CGI Session+initSession sessionName var = do+  s <- getSession sessionName var+  case s of+    Just s  -> return s+    Nothing -> makeSession sessionName var++-- | Create a new session and update the Mvar.+makeSession :: SessionName -> Sessions -> CGI.CGI Session+makeSession sessionName var = do+  sess <- liftIO $ modifyMVar var $ \state@(id:ids,sessions) ->+                let session = Session id M.empty+                    newState = (ids,M.insert id session sessions)+                in return (newState,session)+  CGI.setCookie (CGI.newCookie sessionName $ show $ sess_id sess)+         { CGI.cookiePath = Just "/" }+  return sess++-- | Try to get the current session.+getSession :: SessionName -> Sessions -> CGI.CGI (Maybe Session)+getSession sessionName var = do+  sId <- CGI.readCookie sessionName+  (ids,sessions) <- liftIO $ readMVar var+  return $ sId >>= flip M.lookup sessions++-- | Update a session in the map.+updateSession :: Sessions -> Session -> CGI.CGI ()+updateSession var session@(Session id _) = do+  liftIO $ modifyMVar_ var $ \(ids,sessions) -> do+    return (ids,M.insert id session sessions)