~ubuntu-branches/ubuntu/precise/haskell-wai-extra/precise

« back to all changes in this revision

Viewing changes to Network/Wai/Middleware/CleanPath.hs

  • Committer: Bazaar Package Importer
  • Author(s): Clint Adams
  • Date: 2011-06-01 23:22:01 UTC
  • Revision ID: james.westby@ubuntu.com-20110601232201-y6ygzozvhbcjdaoq
Tags: upstream-0.2.4.2
ImportĀ upstreamĀ versionĀ 0.2.4.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
{-# LANGUAGE OverloadedStrings #-}
 
2
module Network.Wai.Middleware.CleanPath
 
3
    ( cleanPath
 
4
    , cleanPathRel
 
5
    , cleanPathFunc
 
6
    , splitPath
 
7
    ) where
 
8
 
 
9
import Network.Wai
 
10
import qualified Data.ByteString.Char8 as B
 
11
import qualified Data.ByteString.Lazy as L
 
12
import Network.URI (unEscapeString)
 
13
import qualified Data.ByteString.UTF8 as BSU
 
14
 
 
15
cleanPathFunc :: (B.ByteString -> Either B.ByteString [String])
 
16
              -> B.ByteString
 
17
              -> ([String] -> Request -> IO Response)
 
18
              -> Request
 
19
              -> IO Response
 
20
cleanPathFunc splitter prefix app env =
 
21
    case splitter $ pathInfo env of
 
22
        Right pieces -> app pieces env
 
23
        Left p -> return
 
24
                . Response status301
 
25
                  [("Location", B.concat [prefix, p, suffix])]
 
26
                $ ResponseLBS L.empty
 
27
    where
 
28
        -- include the query string if present
 
29
        suffix =
 
30
            case B.uncons $ queryString env of
 
31
                Nothing -> B.empty
 
32
                Just ('?', _) -> queryString env
 
33
                _ -> B.cons '?' $ queryString env
 
34
 
 
35
-- | Performs redirects as per 'splitPath'.
 
36
cleanPathRel :: B.ByteString -> ([String] -> Request -> IO Response)
 
37
             -> Request -> IO Response
 
38
cleanPathRel = cleanPathFunc splitPath
 
39
 
 
40
cleanPath :: ([String] -> Request -> IO Response) -> Request -> IO Response
 
41
cleanPath = cleanPathRel B.empty
 
42
 
 
43
-- | Given a certain requested path, return either a corrected path
 
44
-- to redirect to or the tokenized path.
 
45
--
 
46
-- This code corrects for the following issues:
 
47
--
 
48
-- * It is missing a trailing slash, and there is no period after the
 
49
-- last slash.
 
50
--
 
51
-- * There are any doubled slashes.
 
52
splitPath :: B.ByteString -> Either B.ByteString [String]
 
53
splitPath s =
 
54
    let corrected = B.pack $ rts $ ats $ rds $ B.unpack s
 
55
     in if corrected == s
 
56
            then Right $ map (BSU.toString . B.pack . unEscapeString . B.unpack)
 
57
                       $ filter (not . B.null)
 
58
                       $ B.split '/' s
 
59
            else Left corrected
 
60
 
 
61
-- | Remove double slashes
 
62
rds :: String -> String
 
63
rds [] = []
 
64
rds [x] = [x]
 
65
rds (a:b:c)
 
66
    | a == '/' && b == '/' = rds (b:c)
 
67
    | otherwise = a : rds (b:c)
 
68
 
 
69
-- | Add a trailing slash if it is missing. Empty string is left alone.
 
70
ats :: String -> String
 
71
ats [] = []
 
72
ats s =
 
73
    if last s == '/' || dbs (reverse s)
 
74
        then s
 
75
        else s ++ "/"
 
76
 
 
77
-- | Remove a trailing slash if the last piece has a period.
 
78
rts :: String -> String
 
79
rts [] = []
 
80
rts s =
 
81
    if last s == '/' && dbs (tail $ reverse s)
 
82
        then init s
 
83
        else s
 
84
 
 
85
-- | Is there a period before a slash here?
 
86
dbs :: String -> Bool
 
87
dbs ('/':_) = False
 
88
dbs ('.':_) = True
 
89
dbs (_:x) = dbs x
 
90
dbs [] = False