diff --git a/salvia-extras.cabal b/salvia-extras.cabal
--- a/salvia-extras.cabal
+++ b/salvia-extras.cabal
@@ -1,7 +1,7 @@
 Name:             salvia-extras
-Version:          0.1.2
-Description:      Collection of non-fundamental request handler for the Salvia web server.
-Synopsis:         Collection of non-fundamental request handler for the Salvia web server.
+Version:          1.0.0
+Description:      Collection of non-fundamental handlers for the Salvia web server.
+Synopsis:         Collection of non-fundamental handlers for the Salvia web server.
 Category:         Network, Web
 License:          BSD3
 License-file:     LICENSE
@@ -9,14 +9,42 @@
 Maintainer:       sfvisser@cs.uu.nl
 Cabal-version:    >= 1.6
 Build-Type:       Simple
-Build-Depends:    base ==3.0.*,
-                  fclabels ==0.1.*,
-                  clevercss ==0.1.*,
-                  hscolour ==1.11.*,
-                  salvia ==0.1.*
-GHC-Options:      -Wall
-HS-Source-Dirs:   src
-Exposed-modules:  Network.Salvia.Handler.CleverCSS,
-                  Network.Salvia.Handler.ExtendedFileSystem,
-                  Network.Salvia.Handler.HsColour
+
+Library
+  GHC-Options:      -Wall
+  HS-Source-Dirs:   src
+
+  Build-Depends:    base ==4.*,
+                    clevercss ==0.1.*,
+                    bytestring ==0.9.*,
+                    salvia ==1.0.*,
+                    salvia-protocol ==1.0.*,
+                    transformers ==0.1.*,
+                    fclabels ==0.4.*,
+                    hscolour ==1.15.*,
+                    text >= 0.5 && < 0.8,
+                    old-locale ==1.0.*,
+                    time ==1.1.*,
+                    filestore ==0.3.*,
+                    network >= 2.2.1.7 && < 2.3,
+                    monads-fd ==0.0.*,
+                    stm ==2.1.*,
+                    HStringTemplate ==0.6.*,
+                    sendfile ==0.6.*,
+                    utf8-string ==0.3.*,
+                    c10k ==0.2.0,
+                    pureMD5 ==1.0.*,
+                    split ==0.1.*,
+                    threadmanager ==0.1.*
+
+  Other-Modules:    Util.Terminal
+  Exposed-modules:  Network.Salvia.Handler.CleverCSS,
+                    Network.Salvia.Handler.ColorLog
+                    Network.Salvia.Handler.ExtendedFileSystem,
+                    Network.Salvia.Handler.FileStore
+                    Network.Salvia.Handler.HsColour
+                    Network.Salvia.Handler.SendFile
+                    Network.Salvia.Handler.StringTemplate
+                    Network.Salvia.Impl.C10k
+                    Network.Salvia.Impl.Cgi
 
diff --git a/src/Network/Salvia/Handler/CleverCSS.hs b/src/Network/Salvia/Handler/CleverCSS.hs
--- a/src/Network/Salvia/Handler/CleverCSS.hs
+++ b/src/Network/Salvia/Handler/CleverCSS.hs
@@ -1,29 +1,28 @@
-module Network.Salvia.Handler.CleverCSS (
-    hFilterCSS
-  , hCleverCSS
-  , hParametrizedCleverCSS
-  ) where
+{-# LANGUAGE FlexibleContexts #-}
+module Network.Salvia.Handler.CleverCSS
+( hFilterCSS
+, hCleverCSS
+, hParametrizedCleverCSS
+)
+where
 
-import Network.Protocol.Uri (Parameters)
-import Network.Salvia.Handler.ExtensionDispatcher (hExtension)
-import Network.Salvia.Handler.Fallback (hOr)
-import Network.Salvia.Handler.File (hFile, hFileFilter)
-import Network.Salvia.Handler.PathRouter (hParameters)
-import Network.Salvia.Handler.Rewrite (hRewriteExt)
-import Network.Salvia.Httpd (Handler)
-import Text.CSS.CleverCSS (cleverCSSConvert)
+import Control.Applicative
+import Control.Monad.State
+import Data.Record.Label hiding (get)
+import Network.Protocol.Uri
+import Network.Protocol.Http
+import Network.Salvia
+import Text.CSS.CleverCSS 
 
-hFilterCSS :: Handler () -> Handler () -> Handler ()
-hFilterCSS cssfilter handler = do
-  hExtension (Just "css")
-    (hFile `hOr` cssfilter)
-    handler
+hFilterCSS :: (MonadIO m, HttpM' m, SendM m, Alternative m) => m () -> m () -> m ()
+hFilterCSS css = hExtension (Just "css") (hFile <|> css)
 
-hCleverCSS :: Handler ()
-hCleverCSS = hParameters >>= hParametrizedCleverCSS
+hCleverCSS :: (MonadIO m, BodyM Request m, HttpM' m, SendM m) => m ()
+hCleverCSS = hRequestParameters "utf-8" >>= hParametrizedCleverCSS
 
-hParametrizedCleverCSS :: Parameters -> Handler ()
-hParametrizedCleverCSS p = do
-  hRewriteExt (fmap ('c':)) (hFileFilter convert)
+hParametrizedCleverCSS :: (MonadIO m, HttpM' m, SendM m) => Parameters -> m ()
+hParametrizedCleverCSS p =
+  do hRewriteExt (fmap ('c':)) (hFileFilter convert)
+     response (contentType =: Just ("text/css", Just "utf-8"))
   where convert = either id id . flip (cleverCSSConvert "") (map (fmap $ maybe "" id) p)
 
diff --git a/src/Network/Salvia/Handler/ColorLog.hs b/src/Network/Salvia/Handler/ColorLog.hs
new file mode 100644
--- /dev/null
+++ b/src/Network/Salvia/Handler/ColorLog.hs
@@ -0,0 +1,82 @@
+{-# LANGUAGE FlexibleContexts #-}
+module Network.Salvia.Handler.ColorLog
+( Counter (..)
+, hCounter
+, hColorLog
+, hColorLogWithCounter
+)
+where
+
+import Control.Applicative
+import Control.Monad.State
+import Data.List
+import Data.Record.Label hiding (get)
+import Data.Time.Clock
+import Data.Time.Format
+import Data.Time.LocalTime
+import Network.Protocol.Http
+import Network.Salvia.Interface
+import System.IO
+import System.Locale
+import Util.Terminal
+
+newtype Counter = Counter { unCounter :: Integer }
+
+-- | This handler simply increases the request counter variable.
+
+hCounter :: PayloadM p Counter m => m Counter
+hCounter = payload (modify (Counter . (+1) . unCounter) >> get)
+
+{- |
+A simple logger that prints a summery of the request information to the
+specified file handle.
+-}
+
+hColorLog :: (AddressM' m, MonadIO m, HttpM' m) => Handle -> m ()
+hColorLog = logger Nothing
+
+-- | Like `hLog` but also prints the request count since server startup.
+
+hColorLogWithCounter :: (PayloadM p Counter m, AddressM' m, MonadIO m, HttpM' m) => Handle -> m ()
+hColorLogWithCounter h = hCounter >>= flip logger h . Just
+
+-- Helper functions.
+
+logger :: (AddressM' m, MonadIO m, HttpM' m) => Maybe Counter -> Handle -> m ()
+logger mcount h =
+  do let count = maybe "-" (show . unCounter) mcount
+     mt <- request (getM method)
+     ur <- request (getM uri)
+     st <- response (getM status)
+     ca <- clientAddress
+     sa <- serverAddress
+     dt <- liftIO $
+       do zone <- getCurrentTimeZone
+          time <- utcToLocalTime zone <$> getCurrentTime
+          return $ formatTime defaultTimeLocale "%a, %d %b %Y %H:%M:%S %z" time
+     liftIO
+       . hPutStrLn h
+       $ intercalate " ; "
+         [ dt
+         , show sa
+         , count
+         , methodToColor mt ++ show mt ++ reset
+         , show ca
+         , whiteBold ++ ur ++ reset
+         , statusToColor st ++ show (codeFromStatus st) ++
+           " " ++ show st ++ reset
+         ]
+
+statusToColor :: Status -> String
+statusToColor st =
+  case codeFromStatus st of
+    c | c <= 199 -> blueBold
+      | c <= 299 -> greenBold
+      | c <= 399 -> yellowBold
+      | c <= 499 -> redBold
+    _            -> magentaBold
+
+methodToColor :: Method -> String
+methodToColor GET = whiteBold
+methodToColor _   = yellowBold
+
diff --git a/src/Network/Salvia/Handler/ExtendedFileSystem.hs b/src/Network/Salvia/Handler/ExtendedFileSystem.hs
--- a/src/Network/Salvia/Handler/ExtendedFileSystem.hs
+++ b/src/Network/Salvia/Handler/ExtendedFileSystem.hs
@@ -1,21 +1,36 @@
-module Network.Salvia.Handler.ExtendedFileSystem (hExtendedFileSystem) where
+{-# LANGUAGE FlexibleContexts #-}
+module Network.Salvia.Handler.ExtendedFileSystem
+( hExtendedFileSystemSendFile
+, hExtendedFileSystem
+) where
 
-import Network.Salvia.Handler.CleverCSS (hFilterCSS, hCleverCSS)
-import Network.Salvia.Handler.HsColour (hHighlightHaskell, hHsColour)
-import Network.Salvia.Handler.Directory (hDirectoryResource)
-import Network.Salvia.Handler.File (hFileResource)
-import Network.Salvia.Handler.FileSystem (hFileTypeDispatcher)
-import Network.Salvia.Handler.Rewrite (hWithDir)
-import Network.Salvia.Httpd (Handler)
+import Control.Applicative
+import Control.Monad.Trans
+import Network.Protocol.Http
+import Network.Salvia
+import Network.Salvia.Handler.CleverCSS
+import Network.Salvia.Handler.HsColour
+import Network.Salvia.Handler.SendFile
 
-hExtendedFileSystem :: FilePath -> Handler ()
-hExtendedFileSystem dir =
-    hFileTypeDispatcher
-    hDirectoryResource
-  ( \r -> hHighlightHaskell (hHsColour r)
-  $ hWithDir dir
-  $ hFilterCSS hCleverCSS
-  $ hFileResource r)
-  dir
+hExtendedFileSystemSendFile
+  :: (MonadIO m, HttpM' m, SocketQueueM m, SendM m, BodyM Request m, Alternative m)
+  => String -> m ()
+hExtendedFileSystemSendFile dir = hFileTypeDispatcher hDirectoryResource f dir
+  where
+  f file = hHighlightHaskell (hHsColour file)
+         . hWithDir dir
+         . hFilterCSS hCleverCSS
+         . hSendFileResource 
+         $ file
 
+hExtendedFileSystem
+  :: (MonadIO m, HttpM' m, SendM m, BodyM Request m, Alternative m)
+  => String -> m ()
+hExtendedFileSystem dir = hFileTypeDispatcher hDirectoryResource f dir
+  where
+  f file = hHighlightHaskell (hHsColour file)
+         . hWithDir dir
+         . hFilterCSS hCleverCSS
+         . hFileResource 
+         $ file
 
diff --git a/src/Network/Salvia/Handler/FileStore.hs b/src/Network/Salvia/Handler/FileStore.hs
new file mode 100644
--- /dev/null
+++ b/src/Network/Salvia/Handler/FileStore.hs
@@ -0,0 +1,127 @@
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, UndecidableInstances #-}
+module Network.Salvia.Handler.FileStore (hFileStore, hFileStoreFile, hFileStoreDirectory) where
+
+import Control.Exception
+import Control.Monad.Trans
+import Data.FileStore
+import Data.List (intercalate)
+import Data.List.Split
+import Data.Record.Label
+import Network.Protocol.Http hiding (NotFound)
+import Network.Protocol.Uri
+import Network.Salvia.Handlers
+import Network.Salvia.Interface
+import qualified Network.Protocol.Http as Http
+
+-- Top level filestore server.
+
+hFileStore
+  :: (MonadIO m, BodyM Request m, HttpM' m, SendM m)
+  => FileStore -> Author -> FilePath -> m ()
+hFileStore fs author =
+  hFileTypeDispatcher
+    (hFileStoreDirectory fs)
+    (hFileStoreFile fs author)
+
+hFileStoreFile
+  :: (MonadIO m, BodyM Request m, HttpM' m, SendM m)
+  => FileStore -> Author -> FilePath -> m ()
+hFileStoreFile fs author _ =
+  do m <- request (getM method)
+     u <- request (getM asUri)
+     let p = mkRelative (get path u)
+         q = get query u
+     -- Default content type to text/plain, and override in hLatest
+     -- and hRetrieve.
+     response (contentType =: Just ("text/plain", Nothing))
+
+     -- REST based routing.
+     case (p, m, q) of
+       ("index",  GET,    _        ) -> hIndex     fs
+       ("search", GET,    _        ) -> hSearch    fs q
+       (_,        GET,    "history") -> hHistory   fs p
+       (_,        GET,    "latest" ) -> hLatest    fs p
+       (_,        GET,    _        ) -> hRetrieve  fs p q
+       (_,        PUT,    _        ) -> hSave      fs p q author
+       (_,        DELETE, _        ) -> hDelete    fs p q author
+       _                             -> hError Http.NotFound
+
+hFileStoreDirectory
+  :: (MonadIO m, BodyM Request m, HttpM' m, SendM m)
+  => FileStore -> FilePath -> m ()
+hFileStoreDirectory fs _ =
+  do u <- request (getM asUri)
+     let p = mkRelative (get path u)
+     run (directory fs p) (intercalate "\n" . map showFS)
+  where showFS (FSFile      f) = f
+        showFS (FSDirectory d) = d ++ "/"
+
+-- Type class alias.
+
+class    (MonadIO m, BodyM Request m, HttpM' m, SendM m) => F m where
+instance (MonadIO m, BodyM Request m, HttpM' m, SendM m) => F m
+
+-- Specific filestore handlers.
+
+hIndex :: F m => FileStore -> m ()
+hIndex fs = run (index fs) (intercalate "\n")
+
+hSearch :: F m => FileStore -> String -> m ()
+hSearch fs q =
+  run (search fs sq) showMatches
+  where showMatches = intercalate "\n" . map showMatch
+        showMatch (SearchMatch f n l) = intercalate ":" [f, show n, l]
+        sq = defaultSearchQuery
+               { queryMatchAll   = False
+               , queryWholeWords = False
+               , queryIgnoreCase = False
+               , queryPatterns   = splitOn "&" q
+               }
+
+hRetrieve :: F m => FileStore -> FilePath -> String -> m ()
+hRetrieve fs p q =
+  do response (contentType =: Just (fileMime p, Nothing))
+     run (retrieve fs p (if null q then Nothing else Just q)) id
+
+hLatest :: F m => FileStore -> FilePath -> m ()
+hLatest fs p =
+  do response (contentType =: Just (fileMime p, Nothing))
+     run (latest fs p) id
+
+hSave :: F m => FileStore -> FilePath -> Description -> Author -> m ()
+hSave fs p q author =
+  do b <- hRawRequestBody
+     run (save fs p author q b) (const "document saved\n")
+
+hDelete :: F m => FileStore -> FilePath -> Description -> Author -> m ()
+hDelete fs p q author = run (delete fs p author q) (const "document deleted\n")
+
+hHistory :: F m => FileStore -> FilePath -> m ()
+hHistory fs p =
+  run (history fs [p] (TimeRange Nothing Nothing)) showHistory
+  where showHistory = intercalate "\n" . map showRevision
+        showRevision (Revision i d a s _) = intercalate "," [i, show d, showAuthor a, s]
+        showAuthor (Author n e) = n ++ " <" ++ e ++ ">"
+
+-- Helper functions.
+
+run :: F m => IO a -> (a -> String) -> m ()
+run action f =
+  do e <- liftIO (try action)
+     case e of
+       Left err  -> hCustomError (mkError err) (show err)
+       Right res -> send (f res)
+
+mkError :: FileStoreError -> Status
+mkError RepositoryExists     = Http.BadRequest
+mkError ResourceExists       = Http.BadRequest
+mkError NotFound             = Http.NotFound
+mkError IllegalResourceName  = Http.NotFound
+mkError Unchanged            = Http.NotFound
+mkError UnsupportedOperation = Http.BadRequest
+mkError NoMaxCount           = Http.InternalServerError
+mkError _                    = Http.BadRequest
+
+mkRelative :: String -> String
+mkRelative = dropWhile (=='/')
+
diff --git a/src/Network/Salvia/Handler/HsColour.hs b/src/Network/Salvia/Handler/HsColour.hs
--- a/src/Network/Salvia/Handler/HsColour.hs
+++ b/src/Network/Salvia/Handler/HsColour.hs
@@ -1,52 +1,55 @@
-module Network.Salvia.Handler.HsColour (
-    hHighlightHaskell
-  , hHsColour
-  , hHsColourCustomStyle
-
-  , defaultStyleSheet
-  ) where
+{-# LANGUAGE FlexibleContexts #-}
+module Network.Salvia.Handler.HsColour
+( hHighlightHaskell
+, hHsColour
+, hHsColourCustomStyle
+, defaultStyleSheet
+)
+where
 
+import Control.Monad.Trans
+import Data.List
 import Data.Record.Label
 import Language.Haskell.HsColour.CSS
-import Network.Protocol.Http (contentType, utf8)
+import Network.Protocol.Http
+import Network.Salvia.Interface
 import Network.Salvia.Handlers
-import Network.Salvia.Httpd
 
-hHighlightHaskell :: Handler () -> Handler () -> Handler ()
+hHighlightHaskell :: HttpM Request m => m a -> m a -> m a
 hHighlightHaskell highlighter = 
-  hExtensionRouter [
-    (Just "hs",  highlighter)
-  , (Just "lhs", highlighter)
-  , (Just "ag",  highlighter)
-  ]
+  hExtensionRouter
+    [ (Just "hs",  highlighter) -- Haskell sources.
+    , (Just "lhs", highlighter) -- Literate Haskell sources.
+    , (Just "ag",  highlighter) -- Attribute grammar files.
+    ]
 
-hHsColour :: ResourceHandler ()
+hHsColour :: (SendM m, HttpM Response m, MonadIO m) => FilePath -> m ()
 hHsColour = hHsColourCustomStyle (Left defaultStyleSheet)
 
--- Left means direct inclusion of stylesheet, right means link to external
+-- | Left means direct inclusion of stylesheet, right means link to external
 -- stylesheet.
 
-hHsColourCustomStyle :: Either String String -> ResourceHandler ()
-hHsColourCustomStyle style r = do
-  sendStr (either id makeStyleLink style)
-  hFileResourceFilter (hscolour False True "") r
-  setM (contentType % response) ("text/html", Just utf8)
+hHsColourCustomStyle :: (SendM m, HttpM Response m, MonadIO m) => Either String String -> FilePath -> m ()
+hHsColourCustomStyle style file =
+  do send (either id makeStyleLink style)
+     hFileResourceFilter (hscolour True) file
+     response (contentType =: Just ("text/html", Just "utf-8"))
 
 makeStyleLink :: String -> String
 makeStyleLink css = "<link rel=\"stylesheet\" type=\"text/css\" href=\"" ++ css ++ "\"></link>"
 
 defaultStyleSheet :: String
-defaultStyleSheet = filter (/=' ') $ concat [
-    "<style>"
-  , ".varop      { color : #960; font-weight : normal; }"
-  , ".keyglyph   { color : #960; font-weight : normal; }"
-  , ".definition { color : #005; font-weight : bold;   }"
-  , ".varid      { color : #444; font-weight : normal; }"
-  , ".keyword    { color : #000; font-weight : bold;   }"
-  , ".comment    { color : #44f; font-weight : normal; }"
-  , ".conid      { color : #000; font-weight : normal; }"
-  , ".num        { color : #00a; font-weight : normal; }"
-  , ".str        { color : #a00; font-weight : normal; }"
+defaultStyleSheet = filter (/=' ') $ intercalate "\n"
+  [ "<style>"
+  , ".hs-varop      { color : #960; font-weight : normal; }"
+  , ".hs-keyglyph   { color : #960; font-weight : normal; }"
+  , ".hs-definition { color : #005; font-weight : bold;   }"
+  , ".hs-varid      { color : #444; font-weight : normal; }"
+  , ".hs-keyword    { color : #000; font-weight : bold;   }"
+  , ".hs-comment    { color : #44f; font-weight : normal; }"
+  , ".hs-conid      { color : #000; font-weight : normal; }"
+  , ".hs-num        { color : #00a; font-weight : normal; }"
+  , ".hs-str        { color : #a00; font-weight : normal; }"
   , "</style>"
   , ""
   ]
diff --git a/src/Network/Salvia/Handler/SendFile.hs b/src/Network/Salvia/Handler/SendFile.hs
new file mode 100644
--- /dev/null
+++ b/src/Network/Salvia/Handler/SendFile.hs
@@ -0,0 +1,22 @@
+{-# LANGUAGE FlexibleContexts #-}
+module Network.Salvia.Handler.SendFile (hSendFileResource) where
+
+import Control.Monad.Trans
+import Data.Record.Label
+import Network.Protocol.Http
+import Network.Salvia
+import Network.Socket.SendFile
+import System.IO
+
+-- TODO: closing of file after getting filesize?
+
+hSendFileResource :: (MonadIO m, HttpM Response m, SendM m, SocketQueueM m) => FilePath -> m ()
+hSendFileResource f =
+  hSafeIO (openBinaryFile f ReadMode) $ \fd ->
+  hSafeIO (hFileSize fd)              $ \fs ->
+    do response $
+         do status        =: OK
+            contentType   =: Just (fileMime f, Just "utf-8")
+            contentLength =: Just fs
+       enqueueSock (\s -> sendFile' s f 0 fs)
+
diff --git a/src/Network/Salvia/Handler/StringTemplate.hs b/src/Network/Salvia/Handler/StringTemplate.hs
new file mode 100644
--- /dev/null
+++ b/src/Network/Salvia/Handler/StringTemplate.hs
@@ -0,0 +1,14 @@
+{-# LANGUAGE FlexibleContexts #-}
+module Network.Salvia.Handler.StringTemplate where
+
+import Control.Monad.Trans
+import Network.Protocol.Http
+import Network.Salvia
+import Text.StringTemplate
+
+hStringTemplate
+  :: (ToSElem a, MonadIO m, HttpM Response m, SendM m)
+  => FilePath -> [(String, a)] -> m ()
+hStringTemplate template attrs =
+  hFileResourceFilter (render . setManyAttrib attrs . newSTMP) template
+
diff --git a/src/Network/Salvia/Impl/C10k.hs b/src/Network/Salvia/Impl/C10k.hs
new file mode 100644
--- /dev/null
+++ b/src/Network/Salvia/Impl/C10k.hs
@@ -0,0 +1,69 @@
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+module Network.Salvia.Impl.C10k where
+
+import Data.Monoid
+import Control.Monad.State
+import Control.Applicative
+import Network.C10kServer
+import Network.Protocol.Http hiding (accept, hostname)
+import Network.Salvia.Impl.Context 
+import Network.Salvia.Interface
+import Network.Salvia.Impl.Handler
+import Network.Socket
+import System.IO
+
+newtype C10kHandler p a = C10kHandler (Handler p a)
+  deriving
+  ( BodyM Request 
+  , Alternative 
+  , Applicative 
+  , BodyM Response 
+  , ClientAddressM 
+  , FlushM Request 
+  , FlushM Response 
+  , ForkM IO
+  , Functor 
+  , HandleM 
+  , HttpM Request 
+  , HttpM Response 
+  , Monad
+  , MonadIO 
+  , MonadPlus 
+  , Monoid
+  , QueueM 
+  , RawHttpM Request 
+  , RawHttpM Response 
+  , SendM 
+  , ServerAddressM 
+  , ServerM 
+  , SocketM 
+  )
+
+runC10kHandler :: C10kHandler p a -> Context p -> IO (a, Context p)
+runC10kHandler (C10kHandler h) = runHandler h
+
+start :: String -> String -> C10kConfig -> C10kHandler p () -> p -> IO ()
+start hst admn conf handler pyld =
+   do putStrLn ("Starting listening server on: 0.0.0.0:" ++ portName conf)
+      flip runC10kServer conf $ \s ->
+        do p <- getPeerName s
+           n <- getSocketName s
+           h <- socketToHandle s ReadWriteMode
+           _ <- runC10kHandler handler
+             Context
+             { _cServerHost  = hst
+             , _cAdminMail   = admn
+             , _cListenOn    = [n]
+             , _cPayload     = pyld
+             , _cRequest     = emptyRequest
+             , _cResponse    = emptyResponse
+             , _cRawRequest  = emptyRequest
+             , _cRawResponse = emptyResponse
+             , _cSocket      = s
+             , _cHandle      = h
+             , _cClientAddr  = p
+             , _cServerAddr  = n
+             , _cQueue       = []
+             }
+           return ()
+
diff --git a/src/Network/Salvia/Impl/Cgi.hs b/src/Network/Salvia/Impl/Cgi.hs
new file mode 100644
--- /dev/null
+++ b/src/Network/Salvia/Impl/Cgi.hs
@@ -0,0 +1,108 @@
+{-# LANGUAGE GeneralizedNewtypeDeriving, FlexibleContexts #-}
+module Network.Salvia.Impl.Cgi
+( CgiHandler (..)
+, hCgiEnv
+, runCgiHandler
+, start
+)
+where
+
+import Control.Applicative
+import Control.Monad
+import Control.Monad.Trans
+import Data.List
+import Data.Maybe
+import Data.Monoid
+import Data.Record.Label
+import Network.Protocol.Http hiding (accept, hostname)
+import Network.Salvia.Handlers
+import Network.Salvia.Impl.Context 
+import Network.Salvia.Impl.Handler
+import Network.Salvia.Interface
+import Network.Socket
+import System.Environment
+import System.IO
+
+newtype CgiHandler p a = CgiHandler (Handler p a)
+  deriving
+  ( BodyM Request 
+  , Alternative 
+  , Applicative 
+  , ClientAddressM 
+  , FlushM Request 
+  , FlushM Response 
+  , Functor 
+  , HandleM
+  , HttpM Request 
+  , HttpM Response 
+  , Monad
+  , MonadIO 
+  , MonadPlus 
+  , Monoid
+  , HandleQueueM 
+  , QueueM 
+  , RawHttpM Request 
+  , RawHttpM Response 
+  , SendM 
+  , ForkM IO
+  , ServerAddressM 
+  , ServerM 
+  )
+
+hCgiEnv :: (FlushM Response m, MonadIO m, QueueM m, HttpM' m, HandleM m) => m a -> m ()
+hCgiEnv handler =
+  do hBanner "salvia-httpd"
+     _ <- hHead handler
+     h <- handle
+     st <- response (getM status)
+     liftIO $ hPutStr h (intercalate " " ["Status:", show (codeFromStatus st), show st] ++ "\r\n")
+     hFlushHeadersOnly forResponse
+     flushQueue forResponse
+
+runCgiHandler :: CgiHandler p a -> Context p -> IO (a, Context p)
+runCgiHandler (CgiHandler h) = runHandler h
+
+start :: Show p => String -> CgiHandler p () -> p -> IO ()
+start prefix handler pyld =
+  do env <- getEnvironment
+
+     -- Setup HTTP request from environment variables.
+     let ur   = fromMaybe ""                   (lookup "REQUEST_URI"     env)
+         qy   = maybe "" ('?':)                (lookup "QUERY_STRING"    env)
+         mthd = maybe GET methodFromString     (lookup "REQUEST_METHOD"  env)
+         prot = maybe http11 versionFromString (lookup "SERVER_PROTOCOL" env)
+         req  = Http (Request mthd (fromMaybe ur (stripPrefix prefix ur) ++ qy)) prot (getHeaders env)
+
+     -- Both the server and client address/port combinations.
+     sa <- getAddrInfo Nothing (lookup "SERVER_ADDR" env) (lookup "SERVER_PORT" env)
+     ca <- getAddrInfo Nothing (lookup "REMOTE_ADDR" env) (lookup "REMOTE_PORT" env)
+
+     -- Run the handler with the context from the CGI environment.
+     _ <- runCgiHandler handler
+       Context
+       { _cServerHost  = fromMaybe "" (lookup "SERVER_NAME"  env)
+       , _cAdminMail   = fromMaybe "" (lookup "SERVER_ADMIN" env)
+       , _cListenOn    = map addrAddress sa
+       , _cPayload     = pyld
+       , _cRequest     = req
+       , _cResponse    = emptyResponse
+       , _cRawRequest  = req
+       , _cRawResponse = emptyResponse
+       , _cSocket      = error "No socket available in CGI mode."
+       , _cHandle      = stdout
+       , _cClientAddr  = addrAddress (head ca)
+       , _cServerAddr  = addrAddress (head sa)
+       , _cQueue       = []
+       }
+     return ()
+
+getHeaders :: [(String, String)] -> Headers
+getHeaders =
+    Headers
+  . map (\(a, b) -> (norm a, b))
+  . filter (("HTTP_" `isPrefixOf`) . fst)
+  where norm = normalizeHeader . replace '_' '-' . fromJust . stripPrefix "HTTP_"
+
+replace :: Eq a => a -> a -> [a] -> [a]
+replace x y = map (\z -> if z == x then y else z)
+
diff --git a/src/Util/Terminal.hs b/src/Util/Terminal.hs
new file mode 100644
--- /dev/null
+++ b/src/Util/Terminal.hs
@@ -0,0 +1,211 @@
+module Util.Terminal
+( esc
+
+, clearAll
+, clearEol
+, clear   
+
+, move
+, moveUp     
+, moveDown   
+, moveBack   
+, moveForward
+
+, save
+, load
+
+, clr
+, fg
+, bg
+
+, normal   
+, bold     
+, faint    
+, standout 
+, underline
+, blink    
+, inverse  
+, invisible
+
+, Color (..)
+
+, reset
+, black  
+, red    
+, green  
+, yellow 
+, blue   
+, magenta
+, cyan   
+, white  
+
+, blackBold  
+, redBold    
+, greenBold  
+, yellowBold 
+, blueBold   
+, magentaBold
+, cyanBold   
+, whiteBold  
+
+, blackBg  
+, redBg    
+, greenBg  
+, yellowBg 
+, blueBg   
+, magentaBg
+, cyanBg   
+, whiteBg  
+, resetBg  
+
+, width
+, height
+, geometry
+)
+where
+
+import Control.Applicative
+import Data.List (intercalate)
+import System.Environment (getEnvironment)
+
+-- Ansi escape sequence generation.
+
+-- Generic function for producing ANSI escape sequences.
+esc :: String -> [String] -> String -> String
+esc a args b = concat ["\ESC[", a, intercalate ";" $ args, b]
+
+-- Clear screen and end-of-line
+clearAll, clearEol, clear :: String
+clearAll = esc "2J" [] ""
+clearEol = esc "K"  [] "" 
+clear    = clearAll ++ move 1 1
+
+-- Move the cursor to the specified row and column.
+move :: Int -> Int -> String
+move row col = esc "" [show col, show row] "H"
+
+-- Relative cursor movements.
+moveUp, moveDown, moveBack, moveForward :: Int -> String
+
+moveUp      rs = esc "" [show rs] "A"
+moveDown    rs = esc "" [show rs] "B"
+moveBack    cs = esc "" [show cs] "D"
+moveForward cs = esc "" [show cs] "C"
+
+-- Load and store the current cursor position.
+save :: String
+save = esc "s" [] ""
+
+load :: String
+load = esc "u" [] ""
+
+-- Generic function for creating (foreground) color sequences.
+clr :: [String] -> String
+clr codes = esc "" codes "m"
+
+-- Create foreground and background colors.
+fg :: Color -> [String]
+fg c = [show ((num c :: Int) + 30)]
+
+bg :: Color -> [String]
+bg c = [show ((num c :: Int) + 40)]
+
+-- Style modifiers.
+normal, bold, faint, standout, underline, blink, inverse, invisible
+  :: [String] -> [String]
+
+normal    = ("0":)
+bold      = ("1":)
+faint     = ("2":)
+standout  = ("3":)
+underline = ("4":)
+blink     = ("5":)
+inverse   = ("7":)
+invisible = ("8":)
+
+-- Ansi color listing.
+
+data Color =
+    Black
+  | Red
+  | Green
+  | Yellow
+  | Blue
+  | Magenta
+  | Cyan
+  | White
+  | Reset
+  deriving (Show, Eq)
+
+-- Ansi codes offsets for color values.
+num :: Num a => Color -> a
+num Black   = 0
+num Red     = 1
+num Green   = 2
+num Yellow  = 3
+num Blue    = 4
+num Magenta = 5
+num Cyan    = 6
+num White   = 7
+num Reset   = 9
+
+-- Shortcut functions for common actions.
+
+-- Reset all color and style information.
+reset :: String
+reset = esc "" ["0", "39", "49"] "m"
+
+-- Shortcut for setting foreground colors.
+black, red, green, yellow, blue,
+  magenta, cyan, white :: String
+
+black   = clr $ fg Black
+red     = clr $ fg Red
+green   = clr $ fg Green
+yellow  = clr $ fg Yellow
+blue    = clr $ fg Blue
+magenta = clr $ fg Magenta
+cyan    = clr $ fg Cyan
+white   = clr $ fg White
+
+-- Shortcut for setting bold foreground colors.
+blackBold, redBold, greenBold, yellowBold, blueBold,
+  magentaBold, cyanBold, whiteBold :: String
+
+blackBold   = clr $ bold $ fg Black
+redBold     = clr $ bold $ fg Red
+greenBold   = clr $ bold $ fg Green
+yellowBold  = clr $ bold $ fg Yellow
+blueBold    = clr $ bold $ fg Blue
+magentaBold = clr $ bold $ fg Magenta
+cyanBold    = clr $ bold $ fg Cyan
+whiteBold   = clr $ bold $ fg White
+
+-- Shortcut for setting background colors.
+blackBg, redBg, greenBg, yellowBg, blueBg,
+  magentaBg, cyanBg, whiteBg, resetBg :: String
+
+blackBg   = clr $ bg Black
+redBg     = clr $ bg Red
+greenBg   = clr $ bg Green
+yellowBg  = clr $ bg Yellow
+blueBg    = clr $ bg Blue
+magentaBg = clr $ bg Magenta
+cyanBg    = clr $ bg Cyan
+whiteBg   = clr $ bg White
+resetBg   = clr $ bg Reset
+
+-- Terminal geometry.
+
+-- Try to read terminal width from environment variable.
+width :: IO Int
+width = (maybe 80 read . lookup "COLUMNS") <$> getEnvironment
+
+-- Try to read terminal height from environment variable.
+height :: IO Int
+height = (maybe 24 read . lookup "LINES") <$> getEnvironment
+
+-- Try to read terminal width and height from environment variables.
+geometry :: IO (Int, Int)
+geometry = (,) <$> width <*> height
+
