packages feed

VKHS 0.1.1 → 0.1.2

raw patch · 32 files changed

+4194/−36 lines, 32 filessetup-changed

Files

LICENSE view
@@ -1,30 +1,30 @@-Copyright (c) 2012, Sergey Mironov--All rights reserved.--Redistribution and use in source and binary forms, with or without-modification, are permitted provided that the following conditions are met:--    * Redistributions of source code must retain the above copyright-      notice, this list of conditions and the following disclaimer.--    * 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.--    * Neither the name of Sergey Mironov nor the names of other-      contributors may be used to endorse or promote products derived-      from this software without specific prior written permission.--THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS-"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 THE COPYRIGHT-OWNER 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.+Copyright (c) 2012, Sergey Mironov
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * 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.
+
+    * Neither the name of Sergey Mironov nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"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 THE COPYRIGHT
+OWNER 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.
Setup.hs view
@@ -1,2 +1,2 @@-import Distribution.Simple-main = defaultMain+import Distribution.Simple
+main = defaultMain
VKHS.cabal view
@@ -1,6 +1,6 @@  name:                VKHS-version:             0.1.1+version:             0.1.2 synopsis:            Provides access to Vkontakte social network, popular in Russia description:     Provides access to Vkontakte API methods. Library requires no interaction@@ -13,7 +13,7 @@ copyright:           Copyright (c) 2012, Sergey Mironov category:            Web build-type:          Simple-cabal-version:       >=1.8+cabal-version:       >=1.6 homepage:            http://github.com/ierton/vkhs  source-repository head@@ -22,11 +22,13 @@  library   hs-source-dirs:    src+  other-modules:     Test.Debug, Test.Data, Test.Api, Network.Shpider.Forms, Network.Protocol.Uri, Network.Protocol.Mime, Network.Protocol.Http, Network.Protocol.Cookie, Network.Protocol.Uri.Remap, Network.Protocol.Uri.Query, Network.Protocol.Uri.Printer, Network.Protocol.Uri.Path, Network.Protocol.Uri.Parser, Network.Protocol.Uri.Encode, Network.Protocol.Uri.Data, Network.Protocol.Uri.Chars, Network.Protocol.Http.Status, Network.Protocol.Http.Printer, Network.Protocol.Http.Parser, Network.Protocol.Http.Headers, Network.Protocol.Http.Data, Data.Label, Data.Label.PureM, Data.Label.Pure, Data.Label.MaybeM, Data.Label.Maybe, Data.Label.Derive, Data.Label.Abstract   exposed-modules:   Web.VKHS-                     Web.VKHS.Types-                     Web.VKHS.Login                      Web.VKHS.API                      Web.VKHS.API.JSON+                     Web.VKHS.Curl+                     Web.VKHS.Login+                     Web.VKHS.Types    build-depends:     base >=4.5 && <5,                      json ==0.5.*,
+ src/Data/Label.hs view
@@ -0,0 +1,196 @@+{-+Copyright (c) Erik Hesselink & Sebastiaan Visser 2008++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.+3. Neither the name of the author nor the names of his contributors+   may be used to endorse or promote products derived from this software+   without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 THE AUTHORS 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.+-}++{-# LANGUAGE TypeOperators #-}+{- |+This package provides first class labels that can act as bidirectional record+fields. The labels can be derived automatically using Template Haskell which+means you don't have to write any boilerplate yourself. The labels are+implemented as lenses and are fully composable. Labels can be used to /get/,+/set/ and /modify/ parts of a datatype in a consistent way.+-}++module Data.Label+(++-- * Working with @fclabels@.++{- |+The lens datatype, conveniently called `:->', is an instance of the+"Control.Category" type class: meaning it has a proper identity and+composition. The library has support for automatically deriving labels from+record selectors that start with an underscore.++To illustrate this package, let's take the following two example datatypes.+-}++-- |+-- >{-# LANGUAGE TemplateHaskell, TypeOperators #-}+-- >import Control.Category+-- >import Data.Label+-- >import Prelude hiding ((.), id)+-- >+-- >data Person = Person+-- >  { _name   :: String+-- >  , _age    :: Int+-- >  , _isMale :: Bool+-- >  , _place  :: Place+-- >  } deriving Show+-- >+-- >data Place = Place+-- >  { _city+-- >  , _country+-- >  , _continent :: String+-- >  } deriving Show++{- |+Both datatypes are record types with all the labels prefixed with an+underscore. This underscore is an indication for our Template Haskell code to+derive lenses for these fields. Deriving lenses can be done with this simple+one-liner:++>mkLabels [''Person, ''Place]++For all labels a lens will created.++Now let's look at this example. This 71 year old fellow, my neighbour called+Jan, didn't mind using him as an example:++>jan :: Person+>jan = Person "Jan" 71 True (Place "Utrecht" "The Netherlands" "Europe")++When we want to be sure Jan is really as old as he claims we can use the `get`+function to get the age out as an integer:++>hisAge :: Int+>hisAge = get age jan++Consider he now wants to move to Amsterdam: what better place to spend your old+days. Using composition we can change the city value deep inside the structure:++>moveToAmsterdam :: Person -> Person+>moveToAmsterdam = set (city . place) "Amsterdam"++And now:++>ghci> moveToAmsterdam jan+>Person "Jan" 71 True (Place "Amsterdam" "The Netherlands" "Europe")++Composition is done using the @(`.`)@ operator which is part of the+"Control.Category" module. Make sure to import this module and hide the default+@(`.`)@, `id` function from the Haskell "Prelude".++-}++-- * Pure lenses.++  (:->)+, lens+, get+, set+, modify+-- , glue+, fmapL++-- * Views using @Applicative@.++{- |++Now, because Jan is an old guy, moving to another city is not a very easy task,+this really takes a while. It will probably take no less than two years before+he will actually be settled. To reflect this change it might be useful to have+a first class view on the `Person` datatype that only reveals the age and+city.  This can be done by using a neat `Applicative` functor instance:++>import Control.Applicative++>ageAndCity :: Person :-> (Int, String)+>ageAndCity = Lens $ (,) <$> fst `for` age <*> snd `for` city . place++Because the applicative type class on its own is not very capable of expressing+bidirectional relations, which we need for our lenses, the actual instance is+defined for an internal helper structure called `Point`. Points are a bit more+general than lenses. As you can see above, the `Label` constructor has to be+used to convert a `Point` back into a `Label`. The `for` function must be used+to indicate which partial destructor to use for which lens in the applicative+composition.++Now that we have an appropriate age+city view on the `Person` datatype (which+is itself a lens again), we can use the `modify` function to make Jan move to+Amsterdam over exactly two years:++>moveToAmsterdamOverTwoYears :: Person -> Person+>moveToAmsterdamOverTwoYears = modify ageAndCity (\(a, _) -> (a+2, "Amsterdam"))++>ghci> moveToAmsterdamOverTwoYears jan+>Person "Jan" 73 True (Place "Amsterdam" "The Netherlands" "Europe")++-}++, Lens (Lens)++-- * Working with bijections and isomorphisms.+-- +-- | This package contains a bijection datatype that encodes bidirectional+-- functions. Just like lenses, bijections can be composed using the+-- "Control.Category" type class. Bijections can be used to change the type of+-- a lens. The `Iso` type class, which can be seen as a bidirectional functor,+-- can be used to apply lenses to lenses.+-- +-- For example, when we want to treat the age of a person as a string we can do+-- the following:+-- +-- > ageAsString :: Person :-> String+-- > ageAsString = (Bij show read) `iso` age++, Bijection (..)+, Iso (..)+, for++-- * Derive labels using Template Haskell.+--+-- | We can either derive labels with or without type signatures. In the case+-- of multi-constructor datatypes some fields might not always be available and+-- the derived labels will be partial. Partial labels are provided with an+-- additional type context that forces them to be only usable using the+-- functions from "Data.Label.Maybe".++, mkLabels+, mkLabel+, mkLabelsWith+, mkLabelsMono+, mkLabelsNoTypes+)+where++import Data.Label.Abstract (Bijection(..), Iso(..), for, Lens(..))+import Data.Label.Pure+import Data.Label.Derive+
+ src/Data/Label/Abstract.hs view
@@ -0,0 +1,162 @@+{-+Copyright (c) Erik Hesselink & Sebastiaan Visser 2008++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.+3. Neither the name of the author nor the names of his contributors+   may be used to endorse or promote products derived from this software+   without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 THE AUTHORS 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.+-}+{-# LANGUAGE+    TypeOperators+  , Arrows+  , TupleSections+  , FlexibleInstances+  , MultiParamTypeClasses+  #-}+module Data.Label.Abstract where++import Control.Arrow+import Prelude hiding ((.), id)+import Control.Applicative+import Control.Category++{-# INLINE _modify #-}+{-# INLINE lens    #-}+{-# INLINE get     #-}+{-# INLINE set     #-}+{-# INLINE modify  #-}+{-# INLINE bimap   #-}+{-# INLINE for     #-}+{-# INLINE liftBij #-}++-- | Abstract Point datatype. The getter and setter functions work in some+-- arrow.++data Point (~>) f i o = Point+  { _get :: f ~> o+  , _set :: (i, f) ~> f+  }++-- | Modification as a compositon of a getter and setter. Unfortunately,+-- `ArrowApply' is needed for this composition.++_modify :: ArrowApply (~>) => Point (~>) f i o -> (o ~> i, f) ~> f+_modify l = proc (m, f) -> do i <- m . _get l -<< f; _set l -< (i, f)++-- | Abstract Lens datatype. The getter and setter functions work in some+-- arrow. Arrows allow for effectful lenses, for example, lenses that might+-- fail or use state.++newtype Lens (~>) f a = Lens { unLens :: Point (~>) f a a }++-- | Create a lens out of a getter and setter.++lens :: (f ~> a) -> ((a, f) ~> f) -> Lens (~>) f a+lens g s = Lens (Point g s)++-- | Get the getter arrow from a lens.++get :: Arrow (~>) => Lens (~>) f a -> f ~> a+get = _get . unLens++-- | Get the setter arrow from a lens.++set :: Arrow (~>) => Lens (~>) f a -> (a, f) ~> f+set = _set . unLens++-- | Get the modifier arrow from a lens.++modify :: ArrowApply (~>) => Lens (~>) f o -> (o ~> o, f) ~> f+modify = _modify . unLens++instance ArrowApply (~>) => Category (Lens (~>)) where+  id = lens id (arr fst)+  Lens a . Lens b = lens (_get a . _get b) (_modify b . first (curryA (_set a)))+    where curryA f = arr (\i -> f . arr (i,))+  {-# INLINE id #-}+  {-# INLINE (.) #-}++instance Arrow (~>) => Functor (Point (~>) f i) where+  fmap f x = Point (arr f . _get x) (_set x)+  {-# INLINE fmap #-}++instance Arrow (~>) => Applicative (Point (~>) f i) where+  pure a  = Point (arr (const a)) (arr snd)+  a <*> b = Point (arr app . (_get a &&& _get b)) (_set b . (arr fst &&& _set a))+  {-# INLINE pure #-}+  {-# INLINE (<*>) #-}++-- | Make a 'Point' diverge in two directions.++bimap :: Arrow (~>) => (o' ~> o) -> (i ~> i') -> Point (~>) f i' o' -> Point (~>) f i o+bimap f g l = Point (f . _get l) (_set l . first g)++infix 8 `for`++for :: Arrow (~>) => (i ~> o) -> Lens (~>) f o -> Point (~>) f i o+for p = bimap id p . unLens++-- | The bijections datatype, an arrow that works in two directions. ++data Bijection (~>) a b = Bij { fw :: a ~> b, bw :: b ~> a }++-- | Bijections as categories.++instance Category (~>) => Category (Bijection (~>)) where+  id = Bij id id+  Bij a b . Bij c d = Bij (a . c) (d . b)+  {-# INLINE id #-}+  {-# INLINE (.) #-}++-- | Lifting 'Bijection's.++liftBij :: Functor f => Bijection (->) a b -> Bijection (->) (f a) (f b)+liftBij a = Bij (fmap (fw a)) (fmap (bw a))++-- | Apply Bijection to Lens++-- mapBij :: Bijection (->) b c -> Lens (->) a b -> Lens (->) a c+-- mapBij b l = lens g s where+--    g x = fw b (get l x)+--    s (a,x) = set l ((bw b a),x)++-- | The isomorphism type class is like a `Functor' but works in two directions.++infixr 8 `iso`++class Iso (~>) f where+  iso :: Bijection (~>) a b -> f a ~> f b++-- | We can diverge 'Lens'es using an isomorphism.++instance Arrow (~>) => Iso (~>) (Lens (~>) f) where+  iso bi = arr ((\a -> lens (fw bi . _get a) (_set a . first (bw bi))) . unLens)+  {-# INLINE iso #-}++-- | We can diverge 'Bijection's using an isomorphism.++instance Arrow (~>) => Iso (~>) (Bijection (~>) a) where+  iso = arr . (.)+  {-# INLINE iso #-}+
+ src/Data/Label/Derive.hs view
@@ -0,0 +1,240 @@+{-+Copyright (c) Erik Hesselink & Sebastiaan Visser 2008++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.+3. Neither the name of the author nor the names of his contributors+   may be used to endorse or promote products derived from this software+   without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 THE AUTHORS 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.+-}+{-# OPTIONS -fno-warn-orphans #-}+{-# LANGUAGE+    TemplateHaskell+  , OverloadedStrings+  , FlexibleContexts+  , FlexibleInstances+  , TypeOperators+  #-}+module Data.Label.Derive+( mkLabels+, mkLabel+, mkLabelsWith+, mkLabelsMono+, mkLabelsNoTypes+) where++import Control.Arrow+import Control.Category+import Control.Monad+import Data.Char+import Data.Function (on)+import Data.Label.Abstract+import Data.Label.Pure ((:->))+import Data.Label.Maybe ((:~>))+import Data.List+import Data.Ord+import Data.String+import Language.Haskell.TH+import Language.Haskell.TH.Syntax+import Prelude hiding ((.), id)++-- Throw a fclabels specific error.++fclError :: String -> a+fclError err = error ("Data.Label.Derive: " ++ err)++-- | Derive lenses including type signatures for all the record selectors for a+-- collection of datatypes. The types will be polymorphic and can be used in an+-- arbitrary context.++mkLabels :: [Name] -> Q [Dec]+mkLabels = mkLabelsWith defaultMakeLabel++-- | Derive lenses including type signatures for all the record selectors in a+-- single datatype. The types will be polymorphic and can be used in an+-- arbitrary context.++mkLabel :: Name -> Q [Dec]+mkLabel = mkLabels . return++-- | Generate the label name from the record field name.+-- For instance, @drop 1 . dropWhile (/='_')@ creates a label @val@ from a+-- record @Rec { rec_val :: X }@.++mkLabelsWith :: (String -> String) -> [Name] -> Q [Dec]+mkLabelsWith makeLabel = liftM concat . mapM (derive1 makeLabel True False)++-- | Derive lenses including type signatures for all the record selectors in a+-- datatype. The signatures will be concrete and can only be used in the+-- appropriate context.++mkLabelsMono :: [Name] -> Q [Dec]+mkLabelsMono = liftM concat . mapM (derive1 defaultMakeLabel True True)++-- | Derive lenses without type signatures for all the record selectors in a+-- datatype.++mkLabelsNoTypes :: [Name] -> Q [Dec]+mkLabelsNoTypes = liftM concat . mapM (derive1 defaultMakeLabel False False)++-- Helpers to generate all labels for one datatype.++derive1 :: (String -> String) -> Bool -> Bool -> Name -> Q [Dec]+derive1 makeLabel signatures concrete datatype =+ do i <- reify datatype+    let -- Only process data and newtype declarations, filter out all+        -- constructors and the type variables.+        (tyname, cons, vars) =+          case i of+            TyConI (DataD    _ n vs cs _) -> (n, cs,  vs)+            TyConI (NewtypeD _ n vs c  _) -> (n, [c], vs)+            _                             -> fclError "Can only derive labels for datatypes and newtypes."++        -- We are only interested in lenses of record constructors.+        recordOnly = groupByCtor [ (f, n) | RecC n fs <- cons, f <- fs ]++    concat `liftM`+        mapM (derive makeLabel signatures concrete tyname vars (length cons))+            recordOnly++    where groupByCtor = map (\xs -> (fst (head xs), map snd xs))+                      . groupBy ((==) `on` (fst3 . fst))+                      . sortBy (comparing (fst3 . fst))+                      where fst3 (a, _, _) = a++-- Generate the code for the labels.++-- | Generate a name for the label. If the original selector starts with an+-- underscore, remove it and make the next character lowercase. Otherwise,+-- add 'l', and make the next character uppercase.+defaultMakeLabel :: String -> String+defaultMakeLabel field =+  case field of+    '_' : c : rest -> toLower c : rest+    f : rest       -> 'l' : toUpper f : rest+    n              -> fclError ("Cannot derive label for record selector with name: " ++ n)++derive :: (String -> String)+       -> Bool -> Bool -> Name -> [TyVarBndr] -> Int+       -> (VarStrictType, [Name]) -> Q [Dec]+derive makeLabel signatures concrete tyname vars total ((field, _, fieldtyp), ctors) =+  do (sign, body) <-+       if length ctors == total+       then function derivePureLabel+       else function deriveMaybeLabel++     return $+       if signatures+       then [sign, inline, body]+       else [inline, body]++  where++    -- Generate an inline declaration for the label.+    inline = PragmaD (InlineP labelName (InlineSpec True True (Just (True, 0))))+    labelName = mkName (makeLabel (nameBase field))++    -- Build a single record label definition for labels that might fail.+    deriveMaybeLabel = (if concrete then mono else poly, body)+      where+        mono = forallT prettyVars (return []) [t| $(inputType) :~> $(return prettyFieldtyp) |]+        poly = forallT forallVars (return [])+          [t| (ArrowChoice $(arrow), ArrowZero $(arrow))+              => Lens $(arrow) $(inputType) $(return prettyFieldtyp) |]+        body = [| lens (fromRight . $(getter)) (fromRight . $(setter)) |]+          where+            getter    = [| arr (\    p  -> $(caseE [|p|] (cases (bodyG [|p|]      ) ++ wild))) |]+            setter    = [| arr (\(v, p) -> $(caseE [|p|] (cases (bodyS [|p|] [|v|]) ++ wild))) |]+            cases b   = map (\ctor -> match (recP ctor []) (normalB b) []) ctors+            wild      = [match wildP (normalB [| Left () |]) []]+            bodyS p v = [| Right $( record p field v ) |]+            bodyG p   = [| Right $( varE field `appE` p ) |]++    -- Build a single record label definition for labels that cannot fail.+    derivePureLabel = (if concrete then mono else poly, body)+      where+        mono = forallT prettyVars (return []) [t| $(inputType) :-> $(return prettyFieldtyp) |]+        poly = forallT forallVars (return [])+          [t| Arrow $(arrow) => Lens $(arrow) $(inputType) $(return prettyFieldtyp) |]+        body = [| lens $(getter) $(setter) |]+          where+            getter = [| arr $(varE field) |]+            setter = [| arr (\(v, p) -> $(record [| p |] field [| v |])) |]++    -- Compute the type (including type variables of the record datatype.+    inputType = return $ foldr (flip AppT) (ConT tyname) (map tvToVarT (reverse prettyVars))++    -- Convert a type variable binder to a regular type variable.+    tvToVarT (PlainTV tv) = VarT tv+    tvToVarT _            = fclError "No support for special-kinded type variables."++    -- Prettify type variables.+    arrow          = varT (mkName "~>")+    prettyVars     = map prettyTyVar vars+    forallVars     = PlainTV (mkName "~>") : prettyVars+    prettyFieldtyp = prettyType fieldtyp++    -- Q style record updating.+    record rec fld val = val >>= \v -> recUpdE rec [return (fld, v)]++    -- Build a function declaration with both a type signature and body.+    function (s, b) = liftM2 (,) +        (sigD labelName s)+        (funD labelName [ clause [] (normalB b) [] ])++fromRight :: (ArrowChoice a, ArrowZero a) => a (Either b d) d+fromRight = zeroArrow ||| returnA++-------------------------------------------------------------------------------++-- Helper functions to prettify type variables.++prettyName :: Name -> Name+prettyName tv = mkName (takeWhile (/='_') (show tv))++prettyTyVar :: TyVarBndr -> TyVarBndr+prettyTyVar (PlainTV  tv   ) = PlainTV (prettyName tv)+prettyTyVar (KindedTV tv ki) = KindedTV (prettyName tv) ki++prettyType :: Type -> Type+prettyType (ForallT xs cx ty) = ForallT (map prettyTyVar xs) (map prettyPred cx) (prettyType ty)+prettyType (VarT nm         ) = VarT (prettyName nm)+prettyType (AppT ty tx      ) = AppT (prettyType ty) (prettyType tx)+prettyType (SigT ty ki      ) = SigT (prettyType ty) ki+prettyType ty                 = ty++prettyPred :: Pred -> Pred+prettyPred (ClassP nm tys) = ClassP (prettyName nm) (map prettyType tys)+prettyPred (EqualP ty tx ) = EqualP (prettyType ty) (prettyType tx)++-- IsString instances for TH types.++instance IsString Exp where+  fromString = VarE . mkName++instance IsString (Q Pat) where+  fromString = varP . mkName++instance IsString (Q Exp) where+  fromString = varE . mkName+
+ src/Data/Label/Maybe.hs view
@@ -0,0 +1,111 @@+{-+Copyright (c) Erik Hesselink & Sebastiaan Visser 2008++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.+3. Neither the name of the author nor the names of his contributors+   may be used to endorse or promote products derived from this software+   without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 THE AUTHORS 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.+-}+{-# LANGUAGE TypeOperators, TupleSections #-}+module Data.Label.Maybe+( (:~>)+, lens+, get+, set+, set'+, modify+, modify'+, embed+, lift+)+where++import Control.Arrow+import Control.Category+import Control.Monad.Identity+import Control.Monad.Trans.Maybe+import Data.Maybe+import Prelude hiding ((.), id)+import qualified Data.Label.Abstract as A++type MaybeLens f a = A.Lens (Kleisli (MaybeT Identity)) f a++-- | Lens type for situations in which the accessor functions can fail. This is+-- useful, for example, when accessing fields in datatypes with multiple+-- constructors.++type f :~> a = MaybeLens f a++run :: Kleisli (MaybeT Identity) f a -> f -> Maybe a+run l = runIdentity . runMaybeT . runKleisli l++-- | Create a lens that can fail from a getter and a setter that can themselves+-- potentially fail.++lens :: (f -> Maybe a) -> (a -> f -> Maybe f) -> f :~> a+lens g s = A.lens (kl g) (kl (uncurry s))+  where kl a = Kleisli (MaybeT . Identity . a)++-- | Getter for a lens that can fail. When the field to which the lens points+-- is not accessible the getter returns 'Nothing'.++get :: (f :~> a) -> f -> Maybe a+get l = run (A.get l)++-- | Setter for a lens that can fail. When the field to which the lens points+-- is not accessible this function returns 'Nothing'.++set :: f :~> a -> a -> f -> Maybe f+set l v = run (A.set l . arr (v,))++-- | Like 'set' but return behaves like the identity function when the field+-- could not be set.++set' :: (f :~> a) -> a -> f -> f+set' l v f = f `fromMaybe` set l v f++-- | Modifier for a lens that can fail. When the field to which the lens points+-- is not accessible this function returns 'Nothing'.++modify :: (f :~> a) -> (a -> a) -> f -> Maybe f+modify l m = run (A.modify l . arr (arr m,))++-- | Like 'modify' but return behaves like the identity function when the field+-- could not be set.++modify' :: (f :~> a) -> (a -> a) -> f -> f+modify' l m f = f `fromMaybe` modify l m f++-- | Embed a pure lens that points to a `Maybe` field into a lens that might+-- fail.++embed :: A.Lens (->) f (Maybe a) -> f :~> a+embed l = lens (A.get l) (\a f -> Just (A.set l (Just a, f)))+++lift :: A.Lens(->) f a -> f :~> a+lift l = lens (\f -> Just $ A.get l f) (\a f -> Just $ A.set l (a,f))+++
+ src/Data/Label/MaybeM.hs view
@@ -0,0 +1,60 @@+{-+Copyright (c) Erik Hesselink & Sebastiaan Visser 2008++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.+3. Neither the name of the author nor the names of his contributors+   may be used to endorse or promote products derived from this software+   without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 THE AUTHORS 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.+-}+{-# LANGUAGE TypeOperators #-}+module Data.Label.MaybeM+(+-- * 'MonadState' lens operations.+  gets++-- * 'MonadReader' lens operations.+, asks+)+where++import Control.Monad+import Data.Label.Maybe ((:~>))+import qualified Control.Monad.Reader as M+import qualified Control.Monad.State  as M+import qualified Data.Label.Maybe     as L++-- | Get a value out of state, pointed to by the specified lens that might+-- fail.  When the lens getter fails this computation will fall back to+-- `mzero'.++gets :: (M.MonadState f m, MonadPlus m) => (f :~> a) -> m a+gets l = (L.get l `liftM` M.get) >>= (mzero `maybe` return)++-- | Fetch a value, pointed to by a lens that might fail, out of a reader+-- environment. When the lens getter fails this computation will fall back to+-- `mzero'.++asks :: (M.MonadReader f m, MonadPlus m) => (f :~> a) -> m a+asks l = (L.get l `liftM` M.ask) >>= (mzero `maybe` return)+
+ src/Data/Label/Pure.hs view
@@ -0,0 +1,94 @@+{-+Copyright (c) Erik Hesselink & Sebastiaan Visser 2008++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.+3. Neither the name of the author nor the names of his contributors+   may be used to endorse or promote products derived from this software+   without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 THE AUTHORS 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.+-}+{-# LANGUAGE TypeOperators #-}+module Data.Label.Pure+( (:->)+, lens+, get+, set+, modify+-- , glue+, fmapL+)+where++import qualified Data.Label.Abstract as A+import Control.Applicative++type PureLens f a = A.Lens (->) f a++-- | Pure lens type specialized for pure accessor functions.++type (f :-> a) = PureLens f a++-- | Create a pure lens from a getter and a setter.+--+-- We expect the following law to hold:+--+-- > get l (set l a f) == a+--+-- Or, equivalently:+--+-- > set l (get l f) f == f++lens :: (f -> a) -> (a -> f -> f) -> f :-> a+lens g s = A.lens g (uncurry s)++-- | Getter for a pure lens.++get :: (f :-> a) -> f -> a+get = A.get++-- | Setter for a pure lens.++set :: (f :-> a) -> a -> f -> f+set = curry . A.set++-- | Modifier for a pure lens.++modify :: (f :-> a) -> (a -> a) -> f -> f+modify = curry . A.modify+++fmapL :: (Alternative f) => (a :-> b) -> (f a :-> f b)+fmapL (A.Lens (A.Point g' s')) = lens g s where+    g a = fmap g' a+    s fb fa = ((\b a->s' (b,a)) <$> fb <*> fa) <|> fa++-- glue :: (Alternative f) => (o :-> f a) -> (a :-> b) -> (o :-> f b)+-- glue l1 l@(A.Lens (A.Point g2 s2)) = lens g s where+--     g o = fmap g2 (get l1 o)+--     s fb o = set l1 (set ll fb fa) o where+--         fa = get l1 o+--         ll = fmapL l++-- glue2 :: (Alternative f) => (o :-> f a) -> (a :-> f b) -> (o :-> f b)+-- glue2 l1 l2 = lens g s where+
+ src/Data/Label/PureM.hs view
@@ -0,0 +1,89 @@+{-+Copyright (c) Erik Hesselink & Sebastiaan Visser 2008++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.+3. Neither the name of the author nor the names of his contributors+   may be used to endorse or promote products derived from this software+   without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 THE AUTHORS 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.+-}+{-# LANGUAGE TypeOperators  #-}+module Data.Label.PureM+(+-- * 'MonadState' lens operations.+  gets+, puts+, modify+, (=:)+, (=.)++-- * 'MonadReader' lens operations.+, asks+, local+)+where++import Data.Label.Pure ((:->))+import qualified Control.Monad.Reader as M+import qualified Control.Monad.State  as M+import qualified Data.Label.Pure      as L++-- | Get a value out of the state, pointed to by the specified lens.++gets :: M.MonadState s m => s :-> a -> m a+gets = M.gets . L.get++-- | Set a value somewhere in the state, pointed to by the specified lens.++puts :: M.MonadState s m => s :-> a -> a -> m ()+puts l = M.modify . L.set l++-- | Modify a value with a function somewhere in the state, pointed to by the+-- specified lens.++modify :: M.MonadState s m => s :-> a -> (a -> a) -> m ()+modify l = M.modify . L.modify l++-- | Alias for `puts' that reads like an assignment.++infixr 2 =:+(=:) :: M.MonadState s m => s :-> a -> a -> m ()+(=:) = puts++-- | Alias for `modify' that reads more or less like an assignment.++infixr 2 =.+(=.) :: M.MonadState s m => s :-> a -> (a -> a) -> m ()+(=.) = modify++-- | Fetch a value pointed to by a lens out of a reader environment.++asks :: M.MonadReader r m => (r :-> a) -> m a+asks = M.asks . L.get++-- | Execute a computation in a modified environment. The lens is used to+-- point out the part to modify.++local :: M.MonadReader r m => (r :-> b) -> (b -> b) -> m a -> m a+local l f = M.local (L.modify l f)+
+ src/Network/Protocol/Cookie.hs view
@@ -0,0 +1,238 @@+{-+Copyright (c) Sebastiaan Visser 2008++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.+3. Neither the name of the author nor the names of his contributors+   may be used to endorse or promote products derived from this software+   without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 THE AUTHORS 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.+-}++{-# LANGUAGE TemplateHaskell, TypeOperators #-}+module Network.Protocol.Cookie+(++-- | For more information:+--   http://tools.ietf.org/html/rfc6265++-- * Cookie datatype.+  Cookie (Cookie)+, emptyShort+, empty+, cookie+, setCookie++, CookieShort (CookieShort)+, toShort++-- * Accessing cookies.+, name+, value+, comment+, commentURL+, discard+, domain+, maxAge+, expires+, path+, port+, secure+, version++-- * Collection of cookies.+, Cookies+, unCookies+, gather+, pickCookie+)+where++import Prelude hiding ((.), id)+import Control.Category+import Control.Monad (join)+import Data.Label+import qualified Data.Label.Abstract as A+import Data.Maybe+import Data.Char+import Data.Monoid+import Safe+import Data.List+import Network.Protocol.Uri.Query+import qualified Data.Map as M++-- | The `Cookie` data type containg one key/value pair with all the+-- (potentially optional) meta-data.++data Cookie =+  Cookie+    { _name       :: String+    , _value      :: String+    , _comment    :: Maybe String+    , _commentURL :: Maybe String+    , _discard    :: Maybe String+    , _domain     :: Maybe String+    , _maxAge     :: Maybe Int+    , _expires    :: Maybe String+    , _path       :: Maybe String+    , _port       :: [Int]+    , _secure     :: Bool+    , _version    :: Int+    } deriving(Show, Read, Eq)++$(mkLabels [''Cookie])+++-- | Short notation for cookies++data CookieShort = +  CookieShort+    { _s_name  :: String+    , _s_value   :: String+    } deriving (Show, Eq)++$(mkLabels [''CookieShort])++-- | Convert Cookie to CookieShort+toShort :: Cookie -> CookieShort+toShort c = CookieShort (get name c) (get value c)++emptyShort :: CookieShort+emptyShort = CookieShort "" ""++-- | Create an empty cookie.++empty :: Cookie+empty = Cookie "" "" Nothing Nothing Nothing Nothing Nothing Nothing Nothing [] False 0++-- | Show a semicolon separated list of attribute/value pairs. Only meta pairs+-- with significant values will be pretty printed.++showSetCookie :: Cookie -> ShowS+showSetCookie c =+    pair (get name c) (get value c)+  . opt  "comment"    (get comment c)+  . opt  "commentURL" (get commentURL c)+  . opt  "discard"    (get discard c)+  . opt  "domain"     (get domain c)+  . opt  "maxAge"     (fmap show $ get maxAge c)+  . opt  "expires"    (get expires c)+  . opt  "path"       (get path c)+  . lst  "port"       (map show $ get port c)+  . bool "secure"     (get secure c)+  . opt  "version"    (optval $ get version c)+  where+    attr a       = showString a+    val v        = showString ("=" ++ v)+    end          = showString "; "+    single a     = attr a . end+    pair a v     = attr a . val v . end+    opt a        = maybe id (pair a)+    lst _ []     = id+    lst a xs     = pair a $ intercalate "," xs+    bool _ False = id+    bool a True  = single a+    optval 0     = Nothing+    optval i     = Just (show i)++(!$) a b = b $ a+infixr 0 !$++parseSetCookie :: String -> Cookie+parseSetCookie s = +  let p = fw (keyValues ";" "=") s+  in Cookie+    { _name       = p !$ headMay >>> fmap fst >>> fromMaybe "" +    , _value      = p !$ headMay >>> fmap snd >>> fromMaybe ""+    , _comment    = p !$ lookup "comment"+    , _commentURL = p !$ lookup "commentURL"+    , _discard    = p !$ lookup "discard"+    , _domain     = p !$ lookup "domain"+    , _maxAge     = p !$ lookup "maxAge" >>> fmap readMay >>> join+    , _expires    = p !$ lookup "expires"+    , _path       = p !$ lookup "path"+    , _port       = p !$ lookup "port" >>> maybe [] (readDef [-1])+    , _secure     = p !$ lookup "secure" >>> maybe False (const True)+    , _version    = p !$ lookup "version" >>> maybe 1 (readDef 1)+    }++showCookie :: CookieShort -> String+showCookie c = _s_name c ++ "=" ++ _s_value c++parseCookie :: String -> CookieShort+parseCookie s =+  let p = fw (values "=") s+  in emptyShort+       { _s_name  = atDef "" p 0+       , _s_value = atDef "" p 1+       }++-- | Cookie parser and pretty printer as a lens. To be used in combination with+-- the /Set-Cookie/ header field.++setCookie :: Bijection (->) String Cookie+setCookie = Bij parseSetCookie (flip showSetCookie "")++-- cookieShort :: Bijection (->) String [CookieShort]+-- cookieShort = Bij parseCookie showCookie++-- | Cookie parser and pretty printer as a lens. To be used in combination with+-- the /Cookie/ header field.++cookie :: Bijection (->) String [CookieShort]+cookie = (A.liftBij $ Bij parseCookie showCookie) . values ";"++-- | A collection of multiple cookies. These can all be set in one single HTTP+-- /Set-Cookie/ header field.++data Cookies = Cookies { _unCookies :: M.Map String Cookie }+  deriving (Eq, Show, Read)++emptyC = Cookies M.empty++instance Monoid Cookies where+    mempty = emptyC+    mappend (Cookies a) (Cookies b) = Cookies (a`mappend`b)++$(mkLabels [''Cookies])++-- | Case-insensitive way of getting a cookie out of a collection by name.++pickCookie :: String -> Cookies :-> Maybe Cookie+pickCookie n = lookupL (map toLower n) . unCookies where+    lookupL k = lens (M.lookup k) (flip M.alter k . const)++-- | Convert a list to a cookies collection.++fromList :: [Cookie] -> Cookies+fromList = Cookies . M.fromList . map (\a -> (map toLower $ get name a, a))++-- | Get the cookies as a list.++toList :: Cookies -> [Cookie]+toList = map snd . M.toList . get unCookies++-- | Converts to/from cookie collection using a list++gather :: Bijection (->) [Cookie] Cookies+gather = Bij fromList toList+
+ src/Network/Protocol/Http.hs view
@@ -0,0 +1,130 @@+{-+Copyright (c) Sebastiaan Visser 2008++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.+3. Neither the name of the author nor the names of his contributors+   may be used to endorse or promote products derived from this software+   without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 THE AUTHORS 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.+-}+module Network.Protocol.Http+  (++  -- * HTTP message data types.++    Method (..)+  , Version+  , Key+  , Value+  , Headers (..)+  , Request (Request)+  , Response (Response)+  , Http (Http)++  -- * Creating (parts of) messages.++  , http10+  , http11+  , emptyHeaders+  , emptyRequest+  , emptyResponse++  -- * Accessing fields.++  , methods+  , major+  , minor+  , headers+  , version+  , headline+  , method+  , uri+  , asUri+  , status+  , normalizeHeader+  , header++  -- * Accessing specific header fields.++  , contentLength+  , connection+  , accept+  , acceptEncoding+  , acceptLanguage+  , cacheControl+  , keepAlive+  , setCookies+  , cookiesShort+  , location+  , contentType+  , date+  , hostname+  , server+  , userAgent+  , upgrade+  , lastModified+  , acceptRanges+  , eTag++  -- * Parsing HTTP messages.++  , parseRequest+  , parseResponse+  , parseResponseHead+  , parseHeaders++  -- * Exposure of internal parsec parsers.++  , pRequest+  , pResponse+  , pHeaders+  , pVersion+  , pMethod++  -- * Parser helper methods.++  , versionFromString+  , methodFromString++  -- * Printer helper methods.++  , showRequestLine+  , showResponseLine+  , printResponse+  , printRequest++  -- * Handling HTTP status codes.++  , Status (..)+  , statusFailure+  , statusFromCode+  , codeFromStatus++  ) where++import Network.Protocol.Http.Data+import Network.Protocol.Http.Parser+import Network.Protocol.Http.Printer+import Network.Protocol.Http.Headers+import Network.Protocol.Http.Status+
+ src/Network/Protocol/Http/Data.hs view
@@ -0,0 +1,176 @@+{-+Copyright (c) Sebastiaan Visser 2008++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.+3. Neither the name of the author nor the names of his contributors+   may be used to endorse or promote products derived from this software+   without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 THE AUTHORS 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.+-}+{-# LANGUAGE TemplateHaskell, TypeOperators #-}+module Network.Protocol.Http.Data where++import Control.Category+import Data.Char+import Data.List+import Data.List.Split+import Data.Label+import Network.Protocol.Http.Status+import Network.Protocol.Uri+import Prelude hiding ((.), id, lookup, mod)++-- | List of HTTP request methods.++data Method =+    OPTIONS+  | GET+  | HEAD+  | POST+  | PUT+  | DELETE+  | TRACE+  | CONNECT+  | OTHER String+  deriving (Read, Show, Eq)++-- | HTTP protocol version.++data Version = Version {_major :: Int, _minor :: Int}+  deriving (Read, Show, Eq, Ord)++type Key   = String+type Value = String++-- | HTTP headers as mapping from keys to values.++newtype Headers = Headers { unHeaders :: [(Key, Value)] } -- order seems to matter+  deriving (Read, Show, Eq)++-- | Request specific part of HTTP messages.++data Request = Request  { __method :: Method, __uri :: String }+  deriving (Read, Show, Eq)++-- | Response specific part of HTTP messages.++data Response = Response { __status :: Status }+  deriving (Read, Show, Eq)++-- | An HTTP message. The message body is *not* included.++data Http a = Http+  { _headline :: a+  , _version  :: Version+  , _headers  :: Headers+  } deriving (Read, Show, Eq)++-- | All recognized method constructors as a list.++methods :: [Method]+methods = [OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT]++-- | Create HTTP 1.0 version.++http10 :: Version+http10 = Version 1 0++-- | Create HTTP 1.1 version.++http11 :: Version+http11 = Version 1 1++-- | Create an empty set of headers.++emptyHeaders :: Headers+emptyHeaders = Headers []++-- | Create an empty HTTP request message.++emptyRequest :: Http Request+emptyRequest = Http (Request GET "") http11 emptyHeaders++-- | Create an empty HTTP response message.++emptyResponse :: Http Response+emptyResponse = Http (Response OK) http11 emptyHeaders++$(mkLabels [''Version, ''Request, ''Response, ''Http])++-- | Label to access the method part of an HTTP request message.++method :: Http Request :-> Method+method = _method . headline++-- | Label to access the URI part of an HTTP request message.++uri :: Http Request :-> String+uri = _uri . headline++-- | Label to access the URI part of an HTTP request message and access it as a+-- true URI data type.++asUri :: Http Request :-> Uri+asUri = (Bij toUri showUri) `iso` uri++-- | Label to access the status part of an HTTP response message.++status :: Http Response :-> Status+status = _status . headline++-- | Normalize the capitalization of an HTTP header key.++normalizeHeader :: Key -> Key+normalizeHeader = intercalate "-" . map casing . splitOn "-"+  where+  casing ""     = ""+  casing (x:xs) = toUpper x : map toLower xs++-- | Generic label to access an HTTP header field by key. Returns first matching+-- field++header :: Key -> Http a :-> Maybe Value+header key = lens+  (lookup (normalizeHeader key) . unHeaders . get headers)+  (\x -> modify headers (Headers . alter (normalizeHeader key) x . unHeaders))+  where+  alter :: Eq a => a -> Maybe b -> [(a, b)] -> [(a, b)]+  alter k v []                      = maybe [] (\w -> (k, w):[]) v+  alter k v ((x, y):xs) | k == x    = maybe xs (\w -> (k, w):xs) v+                        | otherwise = (x, y) : alter k v xs++-- | Label to access HTTP header fields, return a list of matching fields++headerMany :: Key -> Http a :-> [Value]+headerMany key = lens+  (map snd . filter (key_is (normalizeHeader key)) . unHeaders . get headers)+  (\x -> modify headers (Headers . alter (normalizeHeader key) x . unHeaders))+  where+  alter :: Eq a => a -> [b] -> [(a, b)] -> [(a, b)]+  alter k []     []                      = []+  alter k []     ((x, y):xs) | k == x    =          alter k [] xs+                             | otherwise = (x, y) : alter k [] xs+  alter k (v:vs) []                      = (k, v) : alter k vs []+  alter k (v:vs) ((x, y):xs) | k == x    = (k, v) : alter k vs xs+                             | otherwise = (x, y) : alter k (v:vs) xs+  key_is k' (k,v) = k==k'+
+ src/Network/Protocol/Http/Headers.hs view
@@ -0,0 +1,153 @@+{-+Copyright (c) Sebastiaan Visser 2008++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.+3. Neither the name of the author nor the names of his contributors+   may be used to endorse or promote products derived from this software+   without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 THE AUTHORS 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.++-}+{-# LANGUAGE TypeOperators #-}+module Network.Protocol.Http.Headers where {- doc ok -}++import Control.Category+import Control.Monad+import Data.Label as L+import Data.Monoid+import Data.Label.Abstract as A+import Network.Protocol.Http.Data+import Network.Protocol.Uri.Query+import Network.Protocol.Uri+import Network.Protocol.Cookie+import Prelude hiding ((.), id)+import Safe++-- | Access the /Content-Length/ header field.++contentLength :: (Show i, Read i, Integral i) => Http a :-> Maybe i+contentLength = (Bij (join . fmap readMay) (fmap show)) `iso` header "Content-Length"++-- | Access the /Connection/ header field.++connection :: Http a :-> Maybe String+connection = header "Connection"++-- | Access the /Accept/ header field.++accept :: Http a :-> Maybe Parameters+accept = liftBij (keyValues "," ";") `iso` header "Accept"++-- | Access the /Accept-Encoding/ header field.++acceptEncoding :: Http a :-> Maybe [String]+acceptEncoding = liftBij (values ",") `iso` header "Accept-Encoding"++-- | Access the /Accept-Language/ header field.++acceptLanguage :: Http a :-> Maybe [String]+acceptLanguage = liftBij (values ",") `iso` header "Accept-Language"++-- | Access the /Connection/ header field.++cacheControl :: Http a :-> Maybe String+cacheControl = header "Cache-Control"++-- | Access the /Keep-Alive/ header field.++keepAlive :: (Show i, Read i, Integral i) => Http a :-> Maybe i+keepAlive = (Bij (join . fmap readMay) (fmap show)) `iso` header "Keep-Alive"++maybe2list :: Bijection (->) b [c] -> (a :-> (Maybe b)) -> (a :-> [c])+maybe2list b l = L.lens g s where+   g x = maybe [] (fw b) (L.get l x)+   s [] x = L.set l (Nothing) x+   s c x = L.set l (Just $ bw b c) x++-- | Access the /Cookie/ header field.++cookiesShort :: Http Request :-> [CookieShort]+cookiesShort = maybe2list (cookie) (header "Cookie")++-- | Access the /Set-Cookie/ fields.++setCookies :: Http Response :-> Cookies+setCookies = (gather . liftBij setCookie) `iso` (headerMany "Set-Cookie")++-- | Access the /Location/ header field.++location :: Http a :-> Maybe Uri+location = liftBij (Bij toUri showUri) `iso` (header "Location")++-- | Access the /Content-Type/ header field. The content-type will be parsed+-- into a mimetype and optional charset.++contentType :: Http a :-> Maybe (String, String)+contentType = +        liftBij (Bij parser printer)+  `iso` liftBij (keyValues ";" "=")+  `iso` header "Content-Type"+  where +    printer (x, y) = (x, "") : ("charset", y) : []+    parser ((m, _):("charset", c):_) = (m, c)++-- | Access the /Date/ header field.++date :: Http a :-> Maybe String+date = header "Date"++-- | Access the /Host/ header field.++hostname :: Http a :-> Maybe String+hostname = header "Host"++-- | Access the /Server/ header field.++server :: Http a :-> Maybe String+server = header "Server"++-- | Access the /User-Agent/ header field.++userAgent :: Http a :-> Maybe String+userAgent = header "User-Agent"++-- | Access the /Upgrade/ header field.++upgrade :: Http a :-> Maybe String+upgrade = header "Upgrade"++-- | Access the /Last-Modified/ header field.++lastModified :: Http a :-> Maybe Value+lastModified = header "Last-Modified"++-- | Access the /Accept-Ranges/ header field.++acceptRanges :: Http a :-> Maybe Value+acceptRanges = header "Accept-Ranges"++-- | Access the /ETag/ header field.++eTag :: Http a :-> Maybe Value+eTag = header "ETag"+
+ src/Network/Protocol/Http/Parser.hs view
@@ -0,0 +1,174 @@+{-+Copyright (c) Sebastiaan Visser 2008++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.+3. Neither the name of the author nor the names of his contributors+   may be used to endorse or promote products derived from this software+   without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 THE AUTHORS 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.+-}+{-# LANGUAGE TypeOperators, FlexibleContexts #-}+module Network.Protocol.Http.Parser+(++-- * Top level message parsers.++  parseRequest+, parseResponseHead+, parseResponse+, parseHeaders++-- * Exposure of internal parsec parsers.++, pRequest+, pResponse+, pHeaders+, pVersion+, pMethod++-- * Helper methods.++, versionFromString+, methodFromString++)+where++import Control.Applicative hiding (empty)+import Data.Char+import Data.List hiding (insert)+import Network.Protocol.Http.Data+import Network.Protocol.Http.Status+import Text.Parsec hiding (many, (<|>))+import Text.Parsec.String++-- | Parse a string as an HTTP request message. This parser is very forgiving.++parseRequest :: String -> Either String (Http Request)+parseRequest = either (Left . show) (Right . id) . parse pRequest ""++-- | Parse a string as an HTTP response header message. This parser is very forgiving.++parseResponseHead :: String -> Either String (Http Response)+parseResponseHead = either (Left . show) (Right . id) . parse pResponse ""++-- | Parse a string as an HTTP response header and body. This parser is very forgiving.++parseResponse :: String -> Either String (Http Response, String)+parseResponse = either (Left . show) (Right . id) . parse fp ""+    where fp = (\a b->(a,b)) <$> pResponse <*> manyTill anyChar eof++-- | Parse a string as a list of HTTP headers.++parseHeaders :: String -> Either String Headers+parseHeaders = either (Left . show) (Right . id) . parse pHeaders ""++-- | Parsec parser to parse the header part of an HTTP request.++pRequest :: GenParser Char st (Http Request)+pRequest =+      (\m u v h -> Http (Request m u) v h)+  <$> (pMethod <* many1 (oneOf ls))+  <*> (many1 (noneOf ws) <* many1 (oneOf ls))+  <*> (pVersion <* eol)+  <*> (pHeaders <* eol)++-- | Parsec parser to parse the header part of an HTTP response.++pResponse :: GenParser Char st (Http Response)+pResponse =+      (\v s h -> Http (Response (statusFromCode $ read s)) v h)+  <$> (pVersion <* many1 (oneOf ls))+  <*> (many1 digit <* many1 (oneOf ls) <* many1 (noneOf lf) <* eol)+  <*> (pHeaders <* eol)++-- | Parsec parser to parse one or more, possibly multiline, HTTP header lines.++pHeaders :: GenParser Char st Headers+pHeaders = Headers <$> p+  where+    p = (\k v -> ((k, v):))+        <$> many1 (noneOf (':':ws)) <* string ":"+        <*> (intercalate ws <$> (many $ many1 (oneOf ls) *> many1 (noneOf lf) <* eol))+        <*> option [] p++-- | Parsec parser to parse HTTP versions. Recognizes X.X versions only.++pVersion :: GenParser Char st Version+pVersion = +      (\h l -> Version (ord h - ord '0') (ord l  - ord '0'))+  <$> (istring "HTTP/" *> digit)+  <*> (char '.'       *> digit)++-- | Parsec parser to parse an HTTP method. Parses arbitrary method but+-- actually recognizes the ones listed as a constructor for `Method'.++pMethod :: GenParser Char st Method+pMethod =+     choice+   $ map (\a -> a <$ (try . istring . show $ a)) methods+  ++ [OTHER <$> many (noneOf ws)]++-- | Recognizes HTTP protocol version 1.0 and 1.1, all other string will+-- produce version 1.1.++versionFromString :: String -> Version+versionFromString "HTTP/1.1" = http11+versionFromString "HTTP/1.0" = http10+versionFromString _          = http11++-- | Helper to turn fully capitalized string into request method.++methodFromString :: String -> Method+methodFromString "OPTIONS" = OPTIONS+methodFromString "GET"     = GET +methodFromString "HEAD"    = HEAD+methodFromString "POST"    = POST+methodFromString "PUT"     = PUT +methodFromString "DELETE"  = DELETE+methodFromString "TRACE"   = TRACE+methodFromString "CONNECT" = CONNECT+methodFromString xs        = OTHER xs++-- Helpers.++lf, ws, ls :: String+lf = "\r\n"+ws = " \t\r\n"+ls = " \t"++-- Optional parser with maybe result.++pMaybe :: GenParser Char st a -> GenParser Char st (Maybe a)+pMaybe a = option Nothing (Just <$> a)++-- Parse end of line, \r, \n or \r\n.++eol :: GenParser Char st ()+eol = () <$ ((char '\r' <* pMaybe (char '\n')) <|> char '\n')++-- Case insensitive string parser.++istring :: String -> GenParser Char st String+istring s = sequence (map (\c -> satisfy (\d -> toUpper c == toUpper d)) s)+
+ src/Network/Protocol/Http/Printer.hs view
@@ -0,0 +1,88 @@+{-+Copyright (c) Sebastiaan Visser 2008++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.+3. Neither the name of the author nor the names of his contributors+   may be used to endorse or promote products derived from this software+   without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 THE AUTHORS 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.+-}+{-# LANGUAGE FlexibleInstances #-}+module Network.Protocol.Http.Printer where++import Network.Protocol.Http.Data+import Network.Protocol.Http.Status++-- instance Show (Http Request) where+--   showsPrec _ r@(Http Request {} _ hs) =+--     showRequestLine r . shows hs . eol++printRequest :: Http Request -> String+printRequest r@(Http Request {} _ hs) = (showRequestLine r . showHeaders hs . eol) ""++-- instance Show (Http Response) where+--   showsPrec _ r@(Http Response {} _ hs) =+--     showResponseLine r . shows hs . eol++printResponse :: Http Response -> String+printResponse r@(Http Response {} _ hs) = (showResponseLine r . showHeaders hs . eol) ""++-- | Show HTTP request status line.++showRequestLine :: Http Request -> String -> String+showRequestLine (Http (Request m u) v _) =+    shows m . ss " " . ss u . ss " "+  . showVersion v . eol++-- | Show HTTP response status line.++showResponseLine :: Http Response -> String -> String+showResponseLine (Http (Response s) v _) =+    showVersion v . ss " "+  . shows (codeFromStatus s)+  . ss " " . shows s . eol++showHeaders :: Headers -> ShowS+showHeaders =+      foldr (\a b -> a . eol . b) id+    . map (\(k, a) -> ss k . ss ": " . ss a)+    . unHeaders++-- instance Show Headers where+--   showsPrec _ =+--       foldr (\a b -> a . eol . b) id+--     . map (\(k, a) -> ss k . ss ": " . ss a)+--     . unHeaders++showVersion :: Version -> ShowS+showVersion (Version a b) = ss "HTTP/" . shows a . ss "." . shows b++-- instance Show Version where+--   showsPrec _ (Version a b) = ss "HTTP/" . shows a . ss "." . shows b++eol :: ShowS+eol = ss "\r\n"++ss :: String -> ShowS+ss = showString+
+ src/Network/Protocol/Http/Status.hs view
@@ -0,0 +1,193 @@+{-+Copyright (c) Sebastiaan Visser 2008++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.+3. Neither the name of the author nor the names of his contributors+   may be used to endorse or promote products derived from this software+   without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 THE AUTHORS 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.+-}+module Network.Protocol.Http.Status where {- doc ok -}++import Data.Bimap+import Data.Maybe+import Prelude hiding (lookup)++{- | HTTP status codes. -}++data Status =+    Continue                     -- ^ 100+  | SwitchingProtocols           -- ^ 101+  | OK                           -- ^ 200+  | Created                      -- ^ 201+  | Accepted                     -- ^ 202+  | NonAuthoritativeInformation  -- ^ 203+  | NoContent                    -- ^ 204+  | ResetContent                 -- ^ 205+  | PartialContent               -- ^ 206+  | MultipleChoices              -- ^ 300+  | MovedPermanently             -- ^ 301+  | Found                        -- ^ 302+  | SeeOther                     -- ^ 303+  | NotModified                  -- ^ 304+  | UseProxy                     -- ^ 305+  | TemporaryRedirect            -- ^ 307+  | BadRequest                   -- ^ 400+  | Unauthorized                 -- ^ 401+  | PaymentRequired              -- ^ 402+  | Forbidden                    -- ^ 403+  | NotFound                     -- ^ 404+  | MethodNotAllowed             -- ^ 405+  | NotAcceptable                -- ^ 406+  | ProxyAuthenticationRequired  -- ^ 407+  | RequestTimeOut               -- ^ 408+  | Conflict                     -- ^ 409+  | Gone                         -- ^ 410+  | LengthRequired               -- ^ 411+  | PreconditionFailed           -- ^ 412+  | RequestEntityTooLarge        -- ^ 413+  | RequestURITooLarge           -- ^ 414+  | UnsupportedMediaType         -- ^ 415+  | RequestedRangeNotSatisfiable -- ^ 416+  | ExpectationFailed            -- ^ 417+  | InternalServerError          -- ^ 500+  | NotImplemented               -- ^ 501+  | BadGateway                   -- ^ 502+  | ServiceUnavailable           -- ^ 503+  | GatewayTimeOut               -- ^ 504+  | HTTPVersionNotSupported      -- ^ 505+  | CustomStatus Int String+  deriving (Show, Read, Eq, Ord)++-- | rfc2616 sec6.1.1 Status Code and Reason Phrase.++printStatus :: Status -> String+printStatus Continue                     = "Continue"+printStatus SwitchingProtocols           = "Switching Protocols"+printStatus OK                           = "OK"+printStatus Created                      = "Created"+printStatus Accepted                     = "Accepted"+printStatus NonAuthoritativeInformation  = "Non-Authoritative Information"+printStatus NoContent                    = "No Content"+printStatus ResetContent                 = "Reset Content"+printStatus PartialContent               = "Partial Content"+printStatus MultipleChoices              = "Multiple Choices"+printStatus MovedPermanently             = "Moved Permanently"+printStatus Found                        = "Found"+printStatus SeeOther                     = "See Other"+printStatus NotModified                  = "Not Modified"+printStatus UseProxy                     = "Use Proxy"+printStatus TemporaryRedirect            = "Temporary Redirect"+printStatus BadRequest                   = "Bad Request"+printStatus Unauthorized                 = "Unauthorized"+printStatus PaymentRequired              = "Payment Required"+printStatus Forbidden                    = "Forbidden"+printStatus NotFound                     = "Not Found"+printStatus MethodNotAllowed             = "Method Not Allowed"+printStatus NotAcceptable                = "Not Acceptable"+printStatus ProxyAuthenticationRequired  = "Proxy Authentication Required"+printStatus RequestTimeOut               = "Request Time-out"+printStatus Conflict                     = "Conflict"+printStatus Gone                         = "Gone"+printStatus LengthRequired               = "Length Required"+printStatus PreconditionFailed           = "Precondition Failed"+printStatus RequestEntityTooLarge        = "Request Entity Too Large"+printStatus RequestURITooLarge           = "Request-URI Too Large"+printStatus UnsupportedMediaType         = "Unsupported Media Type"+printStatus RequestedRangeNotSatisfiable = "Requested range not satisfiable"+printStatus ExpectationFailed            = "Expectation Failed"+printStatus InternalServerError          = "Internal Server Error"+printStatus NotImplemented               = "Not Implemented"+printStatus BadGateway                   = "Bad Gateway"+printStatus ServiceUnavailable           = "Service Unavailable"+printStatus GatewayTimeOut               = "Gateway Time-out"+printStatus HTTPVersionNotSupported      = "HTTP Version not supported"+printStatus (CustomStatus _ s)           = s++{- |+RFC2616 sec6.1.1 Status Code and Reason Phrase.++Bidirectional mapping from status numbers to codes.+-}++statusCodes :: Bimap Int Status+statusCodes = fromList [+    (100, Continue)+  , (101, SwitchingProtocols)+  , (200, OK)+  , (201, Created)+  , (202, Accepted)+  , (203, NonAuthoritativeInformation)+  , (204, NoContent)+  , (205, ResetContent)+  , (206, PartialContent)+  , (300, MultipleChoices)+  , (301, MovedPermanently)+  , (302, Found)+  , (303, SeeOther)+  , (304, NotModified)+  , (305, UseProxy)+  , (307, TemporaryRedirect)+  , (400, BadRequest)+  , (401, Unauthorized)+  , (402, PaymentRequired)+  , (403, Forbidden)+  , (404, NotFound)+  , (405, MethodNotAllowed)+  , (406, NotAcceptable)+  , (407, ProxyAuthenticationRequired)+  , (408, RequestTimeOut)+  , (409, Conflict)+  , (410, Gone)+  , (411, LengthRequired)+  , (412, PreconditionFailed)+  , (413, RequestEntityTooLarge)+  , (414, RequestURITooLarge)+  , (415, UnsupportedMediaType)+  , (416, RequestedRangeNotSatisfiable)+  , (417, ExpectationFailed)+  , (500, InternalServerError)+  , (501, NotImplemented)+  , (502, BadGateway)+  , (503, ServiceUnavailable)+  , (504, GatewayTimeOut)+  , (505, HTTPVersionNotSupported)+  ]++-- | Every status greater-than or equal to 400 is considered to be a failure.+statusFailure :: Status -> Bool+statusFailure st = codeFromStatus st >= 400++-- | Conversion from status numbers to codes.+statusFromCode :: Int -> Status+statusFromCode num =+    fromMaybe (CustomStatus num "Unknown Status")+  $ lookup num statusCodes++-- | Conversion from status codes to numbers.+codeFromStatus :: Status -> Int+codeFromStatus (CustomStatus i _) = i+codeFromStatus st =+    fromMaybe 0 -- function is total, should not happen.+  $ lookupR st statusCodes+
+ src/Network/Protocol/Mime.hs view
@@ -0,0 +1,695 @@+{-+Copyright (c) Sebastiaan Visser 2008++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.+3. Neither the name of the author nor the names of his contributors+   may be used to endorse or promote products derived from this software+   without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 THE AUTHORS 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.+-}++module Network.Protocol.Mime where++import Data.Map+++{- |+Handling mime types. This module contains a mapping from file extensions to+mime-types taken from the Apache webserver project.+-}++type Mime = String++{- | Get the mimetype for the specified extension. -}++mime :: String -> Maybe Mime+mime ext = Data.Map.lookup ext extensionToMime++{- | The default mimetype is text/plain. -}++defaultMime :: Mime+defaultMime = "text/plain"++{- | The mapping from extension to mimetype. -}++extensionToMime :: Map String Mime+extensionToMime = fromList+  [ ("123",       "application/vnd.lotus-1-2-3")+  , ("3dml",      "text/vnd.in3d.3dml")+  , ("3g2",       "video/3gpp2")+  , ("3gp",       "video/3gpp")+  , ("ace",       "application/x-ace-compressed")+  , ("acu",       "application/vnd.acucobol")+  , ("acutc",     "application/vnd.acucorp")+  , ("aep",       "application/vnd.audiograph")+  , ("afp",       "application/vnd.ibm.modcap")+  , ("ai",        "application/postscript")+  , ("aif",       "audio/x-aiff")+  , ("aifc",      "audio/x-aiff")+  , ("aiff",      "audio/x-aiff")+  , ("ami",       "application/vnd.amiga.ami")+  , ("apr",       "application/vnd.lotus-approach")+  , ("asc",       "application/pgp-signature")+  , ("asf",       "application/vnd.ms-asf")+  , ("asf",       "video/x-ms-asf")+  , ("asm",       "text/x-asm")+  , ("aso",       "application/vnd.accpac.simply.aso")+  , ("asx",       "video/x-ms-asf")+  , ("atc",       "application/vnd.acucorp")+  , ("atom",      "application/atom+xml")+  , ("atomcat",   "application/atomcat+xml")+  , ("atomsvc",   "application/atomsvc+xml")+  , ("atx",       "application/vnd.antix.game-component")+  , ("au",        "audio/basic")+  , ("bat",       "application/x-msdownload")+  , ("bcpio",     "application/x-bcpio")+  , ("bdm",       "application/vnd.syncml.dm+wbxml")+  , ("bh2",       "application/vnd.fujitsu.oasysprs")+  , ("bin",       "application/octet-stream")+  , ("bmi",       "application/vnd.bmi")+  , ("bmp",       "image/bmp")+  , ("box",       "application/vnd.previewsystems.box")+  , ("boz",       "application/x-bzip2")+  , ("bpk",       "application/octet-stream")+  , ("btif",      "image/prs.btif")+  , ("bz",        "application/x-bzip")+  , ("bz2",       "application/x-bzip2")+  , ("c",         "text/x-c")+  , ("c4d",       "application/vnd.clonk.c4group")+  , ("c4f",       "application/vnd.clonk.c4group")+  , ("c4g",       "application/vnd.clonk.c4group")+  , ("c4p",       "application/vnd.clonk.c4group")+  , ("c4u",       "application/vnd.clonk.c4group")+  , ("cab",       "application/vnd.ms-cab-compressed")+  , ("cc",        "text/x-c")+  , ("ccxml",     "application/ccxml+xml")+  , ("cdbcmsg",   "application/vnd.contact.cmsg")+  , ("cdf",       "application/x-netcdf")+  , ("cdkey",     "application/vnd.mediastation.cdkey")+  , ("cdx",       "chemical/x-cdx")+  , ("cdxml",     "application/vnd.chemdraw+xml")+  , ("cdy",       "application/vnd.cinderella")+  , ("cer",       "application/pkix-cert")+  , ("cgm",       "image/cgm")+  , ("chat",      "application/x-chat")+  , ("chm",       "application/vnd.ms-htmlhelp")+  , ("chrt",      "application/vnd.kde.kchart")+  , ("cif",       "chemical/x-cif")+  , ("cii",       "application/vnd.anser-web-certificate-issue-initiation")+  , ("cil",       "application/vnd.ms-artgalry")+  , ("cla",       "application/vnd.claymore")+  , ("class",     "application/octet-stream")+  , ("clkk",      "application/vnd.crick.clicker.keyboard")+  , ("clkp",      "application/vnd.crick.clicker.palette")+  , ("clkt",      "application/vnd.crick.clicker.template")+  , ("clkw",      "application/vnd.crick.clicker.wordbank")+  , ("clkx",      "application/vnd.crick.clicker")+  , ("clp",       "application/x-msclip")+  , ("cmc",       "application/vnd.cosmocaller")+  , ("cmdf",      "chemical/x-cmdf")+  , ("cml",       "chemical/x-cml")+  , ("cmp",       "application/vnd.yellowriver-custom-menu")+  , ("cmx",       "image/x-cmx")+  , ("com",       "application/x-msdownload")+  , ("conf",      "text/plain")+  , ("cpio",      "application/x-cpio")+  , ("cpp",       "text/x-c")+  , ("cpt",       "application/mac-compactpro")+  , ("crd",       "application/x-mscardfile")+  , ("crl",       "application/pkix-crl")+  , ("crt",       "application/x-x509-ca-cert")+  , ("csh",       "application/x-csh")+  , ("csml",      "chemical/x-csml")+  , ("csp",       "application/vnd.commonspace")+  , ("css",       "text/css")+  , ("cst",       "application/vnd.commonspace")+  , ("csv",       "text/csv")+  , ("curl",      "application/vnd.curl")+  , ("cww",       "application/prs.cww")+  , ("cxx",       "text/x-c")+  , ("daf",       "application/vnd.mobius.daf")+  , ("davmount",  "application/davmount+xml")+  , ("dcr",       "application/x-director")+  , ("dd2",       "application/vnd.oma.dd2+xml")+  , ("ddd",       "application/vnd.fujixerox.ddd")+  , ("def",       "text/plain")+  , ("der",       "application/x-x509-ca-cert")+  , ("dfac",      "application/vnd.dreamfactory")+  , ("dic",       "text/x-c")+  , ("dir",       "application/x-director")+  , ("dis",       "application/vnd.mobius.dis")+  , ("dist",      "application/octet-stream")+  , ("distz",     "application/octet-stream")+  , ("djv",       "image/vnd.djvu")+  , ("djvu",      "image/vnd.djvu")+  , ("dll",       "application/x-msdownload")+  , ("dmg",       "application/octet-stream")+  , ("dms",       "application/octet-stream")+  , ("dna",       "application/vnd.dna")+  , ("doc",       "application/msword")+  , ("dot",       "application/msword")+  , ("dp",        "application/vnd.osgi.dp")+  , ("dpg",       "application/vnd.dpgraph")+  , ("dsc",       "text/prs.lines.tag")+  , ("dtd",       "application/xml-dtd")+  , ("dump",      "application/octet-stream")+  , ("dvi",       "application/x-dvi")+  , ("dwf",       "model/vnd.dwf")+  , ("dwg",       "image/vnd.dwg")+  , ("dxf",       "image/vnd.dxf")+  , ("dxp",       "application/vnd.spotfire.dxp")+  , ("dxr",       "application/x-director")+  , ("ecelp4800", "audio/vnd.nuera.ecelp4800")+  , ("ecelp7470", "audio/vnd.nuera.ecelp7470")+  , ("ecelp9600", "audio/vnd.nuera.ecelp9600")+  , ("ecma",      "application/ecmascript")+  , ("edm",       "application/vnd.novadigm.edm")+  , ("edx",       "application/vnd.novadigm.edx")+  , ("efif",      "application/vnd.picsel")+  , ("ei6",       "application/vnd.pg.osasli")+  , ("elc",       "application/octet-stream")+  , ("eml",       "message/rfc822")+  , ("eol",       "audio/vnd.digital-winds")+  , ("eot",       "application/vnd.ms-fontobject")+  , ("eps",       "application/postscript")+  , ("es3",       "application/vnd.eszigno3+xml")+  , ("esf",       "application/vnd.epson.esf")+  , ("et3",       "application/vnd.eszigno3+xml")+  , ("etx",       "text/x-setext")+  , ("exe",       "application/x-msdownload")+  , ("ext",       "application/vnd.novadigm.ext")+  , ("ez",        "application/andrew-inset")+  , ("ez2",       "application/vnd.ezpix-album")+  , ("ez3",       "application/vnd.ezpix-package")+  , ("f",         "text/x-fortran")+  , ("f77",       "text/x-fortran")+  , ("f90",       "text/x-fortran")+  , ("fbs",       "image/vnd.fastbidsheet")+  , ("fdf",       "application/vnd.fdf")+  , ("fe_launch", "application/vnd.denovo.fcselayout-link")+  , ("fg5",       "application/vnd.fujitsu.oasysgp")+  , ("fgd",       "application/x-director")+  , ("fli",       "video/x-fli")+  , ("flo",       "application/vnd.micrografx.flo")+  , ("flw",       "application/vnd.kde.kivio")+  , ("flx",       "text/vnd.fmi.flexstor")+  , ("fly",       "text/vnd.fly")+  , ("fm",        "application/vnd.framemaker")+  , ("fnc",       "application/vnd.frogans.fnc")+  , ("for",       "text/x-fortran")+  , ("fpx",       "image/vnd.fpx")+  , ("frame",     "application/vnd.framemaker")+  , ("fsc",       "application/vnd.fsc.weblaunch")+  , ("fst",       "image/vnd.fst")+  , ("ftc",       "application/vnd.fluxtime.clip")+  , ("fti",       "application/vnd.anser-web-funds-transfer-initiation")+  , ("fvt",       "video/vnd.fvt")+  , ("fzs",       "application/vnd.fuzzysheet")+  , ("g3",        "image/g3fax")+  , ("gac",       "application/vnd.groove-account")+  , ("gdl",       "model/vnd.gdl")+  , ("ghf",       "application/vnd.groove-help")+  , ("gif",       "image/gif")+  , ("gim",       "application/vnd.groove-identity-message")+  , ("gph",       "application/vnd.flographit")+  , ("gqf",       "application/vnd.grafeq")+  , ("gqs",       "application/vnd.grafeq")+  , ("gram",      "application/srgs")+  , ("grv",       "application/vnd.groove-injector")+  , ("grxml",     "application/srgs+xml")+  , ("gtar",      "application/x-gtar")+  , ("gtm",       "application/vnd.groove-tool-message")+  , ("gtw",       "model/vnd.gtw")+  , ("h",         "text/x-c")+  , ("h261",      "video/h261")+  , ("h263",      "video/h263")+  , ("h264",      "video/h264")+  , ("hbci",      "application/vnd.hbci")+  , ("hdf",       "application/x-hdf")+  , ("hh",        "text/x-c")+  , ("hlp",       "application/winhlp")+  , ("hpgl",      "application/vnd.hp-hpgl")+  , ("hpid",      "application/vnd.hp-hpid")+  , ("hps",       "application/vnd.hp-hps")+  , ("hqx",       "application/mac-binhex40")+  , ("htke",      "application/vnd.kenameaapp")+  , ("htm",       "text/html")+  , ("html",      "text/html")+  , ("hvd",       "application/vnd.yamaha.hv-dic")+  , ("hvp",       "application/vnd.yamaha.hv-voice")+  , ("hvs",       "application/vnd.yamaha.hv-script")+  , ("ico",       "image/vnd.microsoft.icon")+  , ("ics",       "text/calendar")+  , ("ief",       "image/ief")+  , ("ifb",       "text/calendar")+  , ("ifm",       "application/vnd.shana.informed.formdata")+  , ("iges",      "model/iges")+  , ("igl",       "application/vnd.igloader")+  , ("igs",       "model/iges")+  , ("igx",       "application/vnd.micrografx.igx")+  , ("iif",       "application/vnd.shana.informed.interchange")+  , ("imp",       "application/vnd.accpac.simply.imp")+  , ("ims",       "application/vnd.ms-ims")+  , ("in",        "text/plain")+  , ("ipk",       "application/vnd.shana.informed.package")+  , ("irm",       "application/vnd.ibm.rights-management")+  , ("irp",       "application/vnd.irepository.package+xml")+  , ("iso",       "application/octet-stream")+  , ("itp",       "application/vnd.shana.informed.formtemplate")+  , ("ivp",       "application/vnd.immervision-ivp")+  , ("ivu",       "application/vnd.immervision-ivu")+  , ("jad",       "text/vnd.sun.j2me.app-descriptor")+  , ("jam",       "application/vnd.jam")+  , ("java",      "text/x-java-source")+  , ("jisp",      "application/vnd.jisp")+  , ("jlt",       "application/vnd.hp-jlyt")+  , ("jpe",       "image/jpeg")+  , ("jpeg",      "image/jpeg")+  , ("jpg",       "image/jpeg")+  , ("jpgm",      "video/jpm")+  , ("jpgv",      "video/jpeg")+  , ("jpm",       "video/jpm")+  , ("js",        "application/javascript")+  , ("json",      "application/json")+  , ("kar",       "audio/midi")+  , ("karbon",    "application/vnd.kde.karbon")+  , ("kfo",       "application/vnd.kde.kformula")+  , ("kia",       "application/vnd.kidspiration")+  , ("kml",       "application/vnd.google-earth.kml+xml")+  , ("kmz",       "application/vnd.google-earth.kmz")+  , ("kne",       "application/vnd.kinar")+  , ("knp",       "application/vnd.kinar")+  , ("kon",       "application/vnd.kde.kontour")+  , ("kpr",       "application/vnd.kde.kpresenter")+  , ("kpt",       "application/vnd.kde.kpresenter")+  , ("ksp",       "application/vnd.kde.kspread")+  , ("ktr",       "application/vnd.kahootz")+  , ("ktz",       "application/vnd.kahootz")+  , ("kwd",       "application/vnd.kde.kword")+  , ("kwt",       "application/vnd.kde.kword")+  , ("latex",     "application/x-latex")+  , ("lbd",       "application/vnd.llamagraphics.life-balance.desktop")+  , ("lbe",       "application/vnd.llamagraphics.life-balance.exchange+xml")+  , ("les",       "application/vnd.hhe.lesson-player")+  , ("lha",       "application/octet-stream")+  , ("list",      "text/plain")+  , ("list3820",  "application/vnd.ibm.modcap")+  , ("listafp",   "application/vnd.ibm.modcap")+  , ("log",       "text/plain")+  , ("lrm",       "application/vnd.ms-lrm")+  , ("ltf",       "application/vnd.frogans.ltf")+  , ("lvp",       "audio/vnd.lucent.voice")+  , ("lwp",       "application/vnd.lotus-wordpro")+  , ("lzh",       "application/octet-stream")+  , ("m13",       "application/x-msmediaview")+  , ("m14",       "application/x-msmediaview")+  , ("m1v",       "video/mpeg")+  , ("m2a",       "audio/mpeg")+  , ("m2v",       "video/mpeg")+  , ("m3a",       "audio/mpeg")+  , ("m3u",       "audio/x-mpegurl")+  , ("m4u",       "video/vnd.mpegurl")+  , ("ma",        "application/mathematica")+  , ("mag",       "application/vnd.ecowin.chart")+  , ("maker",     "application/vnd.framemaker")+  , ("man",       "text/troff")+  , ("mathml",    "application/mathml+xml")+  , ("mb",        "application/mathematica")+  , ("mbk",       "application/vnd.mobius.mbk")+  , ("mbox",      "application/mbox")+  , ("mc1",       "application/vnd.medcalcdata")+  , ("mcd",       "application/vnd.mcd")+  , ("mdb",       "application/x-msaccess")+  , ("mdi",       "image/vnd.ms-modi")+  , ("me",        "text/troff")+  , ("mesh",      "model/mesh")+  , ("mfm",       "application/vnd.mfmp")+  , ("mgz",       "application/vnd.proteus.magazine")+  , ("mid",       "audio/midi")+  , ("midi",      "audio/midi")+  , ("mif",       "application/vnd.mif")+  , ("mime",      "message/rfc822")+  , ("mj2",       "video/mj2")+  , ("mjp2",      "video/mj2")+  , ("mlp",       "application/vnd.dolby.mlp")+  , ("mmd",       "application/vnd.chipnuts.karaoke-mmd")+  , ("mmf",       "application/vnd.smaf")+  , ("mmr",       "image/vnd.fujixerox.edmics-mmr")+  , ("mny",       "application/x-msmoney")+  , ("mov",       "video/quicktime")+  , ("mp2",       "audio/mpeg")+  , ("mp2a",      "audio/mpeg")+  , ("mp3",       "audio/mpeg")+  , ("mp4",       "video/mp4")+  , ("mp4a",      "audio/mp4")+  , ("mp4s",      "application/mp4")+  , ("mp4v",      "video/mp4")+  , ("mpc",       "application/vnd.mophun.certificate")+  , ("mpe",       "video/mpeg")+  , ("mpeg",      "video/mpeg")+  , ("mpg",       "video/mpeg")+  , ("mpg4",      "video/mp4")+  , ("mpga",      "audio/mpeg")+  , ("mpkg",      "application/vnd.apple.installer+xml")+  , ("mpm",       "application/vnd.blueice.multipass")+  , ("mpn",       "application/vnd.mophun.application")+  , ("mpp",       "application/vnd.ms-project")+  , ("mpt",       "application/vnd.ms-project")+  , ("mpy",       "application/vnd.ibm.minipay")+  , ("mqy",       "application/vnd.mobius.mqy")+  , ("mrc",       "application/marc")+  , ("ms",        "text/troff")+  , ("mscml",     "application/mediaservercontrol+xml")+  , ("mseq",      "application/vnd.mseq")+  , ("msf",       "application/vnd.epson.msf")+  , ("msh",       "model/mesh")+  , ("msi",       "application/x-msdownload")+  , ("msl",       "application/vnd.mobius.msl")+  , ("mts",       "model/vnd.mts")+  , ("mus",       "application/vnd.musician")+  , ("mvb",       "application/x-msmediaview")+  , ("mwf",       "application/vnd.mfer")+  , ("mxf",       "application/mxf")+  , ("mxl",       "application/vnd.recordare.musicxml")+  , ("mxml",      "application/xv+xml")+  , ("mxs",       "application/vnd.triscape.mxs")+  , ("mxu",       "video/vnd.mpegurl")+  , ("n-gage",    "application/vnd.nokia.n-gage.symbian.install")+  , ("nb",        "application/mathematica")+  , ("nc",        "application/x-netcdf")+  , ("ngdat",     "application/vnd.nokia.n-gage.data")+  , ("nlu",       "application/vnd.neurolanguage.nlu")+  , ("nml",       "application/vnd.enliven")+  , ("nnd",       "application/vnd.noblenet-directory")+  , ("nns",       "application/vnd.noblenet-sealer")+  , ("nnw",       "application/vnd.noblenet-web")+  , ("npx",       "image/vnd.net-fpx")+  , ("nsf",       "application/vnd.lotus-notes")+  , ("oa2",       "application/vnd.fujitsu.oasys2")+  , ("oa3",       "application/vnd.fujitsu.oasys3")+  , ("oas",       "application/vnd.fujitsu.oasys")+  , ("obd",       "application/x-msbinder")+  , ("oda",       "application/oda")+  , ("odc",       "application/vnd.oasis.opendocument.chart")+  , ("odf",       "application/vnd.oasis.opendocument.formula")+  , ("odg",       "application/vnd.oasis.opendocument.graphics")+  , ("odi",       "application/vnd.oasis.opendocument.image")+  , ("odp",       "application/vnd.oasis.opendocument.presentation")+  , ("ods",       "application/vnd.oasis.opendocument.spreadsheet")+  , ("odt",       "application/vnd.oasis.opendocument.text")+  , ("ogg",       "application/ogg")+  , ("oprc",      "application/vnd.palm")+  , ("org",       "application/vnd.lotus-organizer")+  , ("otc",       "application/vnd.oasis.opendocument.chart-template")+  , ("otf",       "application/vnd.oasis.opendocument.formula-template")+  , ("otg",       "application/vnd.oasis.opendocument.graphics-template")+  , ("oth",       "application/vnd.oasis.opendocument.text-web")+  , ("oti",       "application/vnd.oasis.opendocument.image-template")+  , ("otm",       "application/vnd.oasis.opendocument.text-master")+  , ("otp",       "application/vnd.oasis.opendocument.presentation-template")+  , ("ots",       "application/vnd.oasis.opendocument.spreadsheet-template")+  , ("ott",       "application/vnd.oasis.opendocument.text-template")+  , ("oxt",       "application/vnd.openofficeorg.extension")+  , ("p",         "text/x-pascal")+  , ("p10",       "application/pkcs10")+  , ("p12",       "application/x-pkcs12")+  , ("p7b",       "application/x-pkcs7-certificates")+  , ("p7c",       "application/pkcs7-mime")+  , ("p7m",       "application/pkcs7-mime")+  , ("p7r",       "application/x-pkcs7-certreqresp")+  , ("p7s",       "application/pkcs7-signature")+  , ("pas",       "text/x-pascal")+  , ("pbd",       "application/vnd.powerbuilder6")+  , ("pbm",       "image/x-portable-bitmap")+  , ("pcl",       "application/vnd.hp-pcl")+  , ("pclxl",     "application/vnd.hp-pclxl")+  , ("pct",       "image/x-pict")+  , ("pcx",       "image/x-pcx")+  , ("pdb",       "application/vnd.palm")+  , ("pdb",       "chemical/x-pdb")+  , ("pdf",       "application/pdf")+  , ("pfr",       "application/font-tdpfr")+  , ("pfx",       "application/x-pkcs12")+  , ("pgm",       "image/x-portable-graymap")+  , ("pgn",       "application/x-chess-pgn")+  , ("pgp",       "application/pgp-encrypted")+  , ("pic",       "image/x-pict")+  , ("pkg",       "application/octet-stream")+  , ("pki",       "application/pkixcmp")+  , ("pkipath",   "application/pkix-pkipath")+  , ("plb",       "application/vnd.3gpp.pic-bw-large")+  , ("plc",       "application/vnd.mobius.plc")+  , ("plf",       "application/vnd.pocketlearn")+  , ("pls",       "application/pls+xml")+  , ("pml",       "application/vnd.ctc-posml")+  , ("png",       "image/png")+  , ("pnm",       "image/x-portable-anymap")+  , ("portpkg",   "application/vnd.macports.portpkg")+  , ("pot",       "application/vnd.ms-powerpoint")+  , ("ppd",       "application/vnd.cups-ppd")+  , ("ppm",       "image/x-portable-pixmap")+  , ("pps",       "application/vnd.ms-powerpoint")+  , ("ppt",       "application/vnd.ms-powerpoint")+  , ("pqa",       "application/vnd.palm")+  , ("prc",       "application/vnd.palm")+  , ("pre",       "application/vnd.lotus-freelance")+  , ("prf",       "application/pics-rules")+  , ("ps",        "application/postscript")+  , ("psb",       "application/vnd.3gpp.pic-bw-small")+  , ("psd",       "image/vnd.adobe.photoshop")+  , ("ptid",      "application/vnd.pvi.ptid1")+  , ("pub",       "application/x-mspublisher")+  , ("pvb",       "application/vnd.3gpp.pic-bw-var")+  , ("pwn",       "application/vnd.3m.post-it-notes")+  , ("qam",       "application/vnd.epson.quickanime")+  , ("qbo",       "application/vnd.intu.qbo")+  , ("qfx",       "application/vnd.intu.qfx")+  , ("qps",       "application/vnd.publishare-delta-tree")+  , ("qt",        "video/quicktime")+  , ("qwd",       "application/vnd.quark.quarkxpress")+  , ("qwt",       "application/vnd.quark.quarkxpress")+  , ("qxb",       "application/vnd.quark.quarkxpress")+  , ("qxd",       "application/vnd.quark.quarkxpress")+  , ("qxl",       "application/vnd.quark.quarkxpress")+  , ("qxt",       "application/vnd.quark.quarkxpress")+  , ("ra",        "audio/x-pn-realaudio")+  , ("ram",       "audio/x-pn-realaudio")+  , ("rar",       "application/x-rar-compressed")+  , ("ras",       "image/x-cmu-raster")+  , ("rcprofile", "application/vnd.ipunplugged.rcprofile")+  , ("rdf",       "application/rdf+xml")+  , ("rdz",       "application/vnd.data-vision.rdz")+  , ("rep",       "application/vnd.businessobjects")+  , ("rgb",       "image/x-rgb")+  , ("rif",       "application/reginfo+xml")+  , ("rl",        "application/resource-lists+xml")+  , ("rlc",       "image/vnd.fujixerox.edmics-rlc")+  , ("rm",        "application/vnd.rn-realmedia")+  , ("rmi",       "audio/midi")+  , ("rmp",       "audio/x-pn-realaudio-plugin")+  , ("rms",       "application/vnd.jcp.javame.midlet-rms")+  , ("rnc",       "application/relax-ng-compact-syntax")+  , ("roff",      "text/troff")+  , ("rpss",      "application/vnd.nokia.radio-presets")+  , ("rpst",      "application/vnd.nokia.radio-preset")+  , ("rs",        "application/rls-services+xml")+  , ("rsd",       "application/rsd+xml")+  , ("rss",       "application/rss+xml")+  , ("rtf",       "application/rtf")+  , ("rtx",       "text/richtext")+  , ("s",         "text/x-asm")+  , ("saf",       "application/vnd.yamaha.smaf-audio")+  , ("sbml",      "application/sbml+xml")+  , ("sc",        "application/vnd.ibm.secure-container")+  , ("scd",       "application/x-msschedule")+  , ("scm",       "application/vnd.lotus-screencam")+  , ("sdkd",      "application/vnd.solent.sdkm+xml")+  , ("sdkm",      "application/vnd.solent.sdkm+xml")+  , ("sdp",       "application/sdp")+  , ("see",       "application/vnd.seemail")+  , ("sema",      "application/vnd.sema")+  , ("semd",      "application/vnd.semd")+  , ("semf",      "application/vnd.semf")+  , ("setpay",    "application/set-payment-initiation")+  , ("setreg",    "application/set-registration-initiation")+  , ("sfs",       "application/vnd.spotfire.sfs")+  , ("sgm",       "text/sgml")+  , ("sgml",      "text/sgml")+  , ("sh",        "application/x-sh")+  , ("shar",      "application/x-shar")+  , ("shf",       "application/shf+xml")+  , ("sig",       "application/pgp-signature")+  , ("silo",      "model/mesh")+  , ("sit",       "application/x-stuffit")+  , ("sitx",      "application/x-stuffitx")+  , ("skd",       "application/vnd.koan")+  , ("skm",       "application/vnd.koan")+  , ("skp",       "application/vnd.koan")+  , ("skt",       "application/vnd.koan")+  , ("slt",       "application/vnd.epson.salt")+  , ("smi",       "application/smil+xml")+  , ("smil",      "application/smil+xml")+  , ("snd",       "audio/basic")+  , ("so",        "application/octet-stream")+  , ("spc",       "application/x-pkcs7-certificates")+  , ("spf",       "application/vnd.yamaha.smaf-phrase")+  , ("spl",       "application/x-futuresplash")+  , ("spot",      "text/vnd.in3d.spot")+  , ("src",       "application/x-wais-source")+  , ("ssf",       "application/vnd.epson.ssf")+  , ("ssml",      "application/ssml+xml")+  , ("stf",       "application/vnd.wt.stf")+  , ("stk",       "application/hyperstudio")+  , ("str",       "application/vnd.pg.format")+  , ("sus",       "application/vnd.sus-calendar")+  , ("susp",      "application/vnd.sus-calendar")+  , ("sv4cpio",   "application/x-sv4cpio")+  , ("sv4crc",    "application/x-sv4crc")+  , ("svd",       "application/vnd.svd")+  , ("svg",       "image/svg+xml")+  , ("svgz",      "image/svg+xml")+  , ("swf",       "application/x-shockwave-flash")+  , ("t",         "text/troff")+  , ("tao",       "application/vnd.tao.intent-module-archive")+  , ("tar",       "application/x-tar")+  , ("tcl",       "application/x-tcl")+  , ("tex",       "application/x-tex")+  , ("texi",      "application/x-texinfo")+  , ("texinfo",   "application/x-texinfo")+  , ("text",      "text/plain")+  , ("tif",       "image/tiff")+  , ("tiff",      "image/tiff")+  , ("tmo",       "application/vnd.tmobile-livetv")+  , ("torrent",   "application/x-bittorrent")+  , ("tpl",       "application/vnd.groove-tool-template")+  , ("tpt",       "application/vnd.trid.tpt")+  , ("tr",        "text/troff")+  , ("tra",       "application/vnd.trueapp")+  , ("trm",       "application/x-msterminal")+  , ("tsv",       "text/tab-separated-values")+  , ("twd",       "application/vnd.simtech-mindmapper")+  , ("twds",      "application/vnd.simtech-mindmapper")+  , ("txd",       "application/vnd.genomatix.tuxedo")+  , ("txf",       "application/vnd.mobius.txf")+  , ("txt",       "text/plain")+  , ("ufd",       "application/vnd.ufdl")+  , ("ufdl",      "application/vnd.ufdl")+  , ("umj",       "application/vnd.umajin")+  , ("unityweb",  "application/vnd.unity")+  , ("uoml",      "application/vnd.uoml+xml")+  , ("uri",       "text/uri-list")+  , ("uris",      "text/uri-list")+  , ("urls",      "text/uri-list")+  , ("ustar",     "application/x-ustar")+  , ("utz",       "application/vnd.uiq.theme")+  , ("uu",        "text/x-uuencode")+  , ("vcd",       "application/x-cdlink")+  , ("vcf",       "text/x-vcard")+  , ("vcg",       "application/vnd.groove-vcard")+  , ("vcs",       "text/x-vcalendar")+  , ("vcx",       "application/vnd.vcx")+  , ("vis",       "application/vnd.visionary")+  , ("viv",       "video/vnd.vivo")+  , ("vrml",      "model/vrml")+  , ("vsd",       "application/vnd.visio")+  , ("vsf",       "application/vnd.vsf")+  , ("vss",       "application/vnd.visio")+  , ("vst",       "application/vnd.visio")+  , ("vsw",       "application/vnd.visio")+  , ("vtu",       "model/vnd.vtu")+  , ("vxml",      "application/voicexml+xml")+  , ("wav",       "audio/wav")+  , ("wav",       "audio/x-wav")+  , ("wax",       "audio/x-ms-wax")+  , ("wbmp",      "image/vnd.wap.wbmp")+  , ("wbs",       "application/vnd.criticaltools.wbs+xml")+  , ("wbxml",     "application/vnd.wap.wbxml")+  , ("wcm",       "application/vnd.ms-works")+  , ("wdb",       "application/vnd.ms-works")+  , ("wks",       "application/vnd.ms-works")+  , ("wm",        "video/x-ms-wm")+  , ("wma",       "audio/x-ms-wma")+  , ("wmd",       "application/x-ms-wmd")+  , ("wmf",       "application/x-msmetafile")+  , ("wml",       "text/vnd.wap.wml")+  , ("wmlc",      "application/vnd.wap.wmlc")+  , ("wmls",      "text/vnd.wap.wmlscript")+  , ("wmlsc",     "application/vnd.wap.wmlscriptc")+  , ("wmv",       "video/x-ms-wmv")+  , ("wmx",       "video/x-ms-wmx")+  , ("wmz",       "application/x-ms-wmz")+  , ("wpd",       "application/vnd.wordperfect")+  , ("wpl",       "application/vnd.ms-wpl")+  , ("wps",       "application/vnd.ms-works")+  , ("wqd",       "application/vnd.wqd")+  , ("wri",       "application/x-mswrite")+  , ("wrl",       "model/vrml")+  , ("wsdl",      "application/wsdl+xml")+  , ("wspolicy",  "application/wspolicy+xml")+  , ("wtb",       "application/vnd.webturbo")+  , ("wvx",       "video/x-ms-wvx")+  , ("x3d",       "application/vnd.hzn-3d-crossword")+  , ("xar",       "application/vnd.xara")+  , ("xbd",       "application/vnd.fujixerox.docuworks.binder")+  , ("xbm",       "image/x-xbitmap")+  , ("xdm",       "application/vnd.syncml.dm+xml")+  , ("xdp",       "application/vnd.adobe.xdp+xml")+  , ("xdw",       "application/vnd.fujixerox.docuworks")+  , ("xenc",      "application/xenc+xml")+  , ("xfdf",      "application/vnd.adobe.xfdf")+  , ("xfdl",      "application/vnd.xfdl")+  , ("xht",       "application/xhtml+xml")+  , ("xhtml",     "application/xhtml+xml")+  , ("xhvml",     "application/xv+xml")+  , ("xif",       "image/vnd.xiff")+  , ("xla",       "application/vnd.ms-excel")+  , ("xlc",       "application/vnd.ms-excel")+  , ("xlm",       "application/vnd.ms-excel")+  , ("xls",       "application/vnd.ms-excel")+  , ("xlt",       "application/vnd.ms-excel")+  , ("xlw",       "application/vnd.ms-excel")+  , ("xml",       "application/xml")+  , ("xo",        "application/vnd.olpc-sugar")+  , ("xop",       "application/xop+xml")+  , ("xpm",       "image/x-xpixmap")+  , ("xpr",       "application/vnd.is-xpr")+  , ("xps",       "application/vnd.ms-xpsdocument")+  , ("xpw",       "application/vnd.intercon.formnet")+  , ("xpx",       "application/vnd.intercon.formnet")+  , ("xsl",       "application/xml")+  , ("xslt",      "application/xslt+xml")+  , ("xsm",       "application/vnd.syncml+xml")+  , ("xspf",      "application/xspf+xml")+  , ("xul",       "application/vnd.mozilla.xul+xml")+  , ("xvm",       "application/xv+xml")+  , ("xvml",      "application/xv+xml")+  , ("xwd",       "image/x-xwindowdump")+  , ("xyz",       "chemical/x-xyz")+  , ("zaz",       "application/vnd.zzazz.deck+xml")+  , ("zip",       "application/zip")+  , ("zmm",       "application/vnd.handheld-entertainment+xml")+  , ("avi",       "video/x-msvideo")+  , ("movie",     "video/x-sgi-movie")+  , ("ice",       "x-conference/x-cooltalk")+  ]+
+ src/Network/Protocol/Uri.hs view
@@ -0,0 +1,128 @@+{-+Copyright (c) Sebastiaan Visser 2008++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.+3. Neither the name of the author nor the names of his contributors+   may be used to endorse or promote products derived from this software+   without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 THE AUTHORS 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.+-}++{-# LANGUAGE TemplateHaskell #-}+module Network.Protocol.Uri (++    {- | See rfc2396 for more info. -}++  -- * URI datatype.++    Scheme+  , RegName+  , Port+  , Query+  , Fragment+  , Hash+  , UserInfo+  , PathSegment+  , Parameters++  , Domain (..)+  , IPv4 (..)+  , Path (..)+  , Host (..)+  , Authority (..)+  , Uri (..)++  -- * Accessing parts of URIs.++  , relative+  , scheme+  , userinfo+  , authority+  , host+  , domain+  , ipv4+  , regname+  , port+  , path+  , segments+  , query+  , fragment++  -- * More advanced labels and functions.++  , pathAndQuery+  , queryParams+  , params+  , extension++  , remap++  -- * Encoding/decoding URI encoded strings.++  , encode+  , decode+  , encoded++  -- * Creating empty URIs.++  , mkUri+  , mkScheme+  , mkPath+  , mkAuthority+  , mkQuery+  , mkFragment+  , mkUserinfo+  , mkHost+  , mkPort++  -- * Parsing URIs.++  , toUri+  , parseUri+  , parseAbsoluteUri+  , parseAuthority+  , parsePath+  , parseHost++  -- * Printing URIs+  , showUri+  , showPath+  , showAuthority++  -- * Filename related utilities.++  , mimetype+  , normalize+  , jail+  , (/+)++  ) where++import Network.Protocol.Uri.Data+import Network.Protocol.Uri.Encode+import Network.Protocol.Uri.Parser+import Network.Protocol.Uri.Path+import Network.Protocol.Uri.Printer+import Network.Protocol.Uri.Query+import Network.Protocol.Uri.Remap+
+ src/Network/Protocol/Uri/Chars.hs view
@@ -0,0 +1,48 @@+{-+Copyright (c) Sebastiaan Visser 2008++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.+3. Neither the name of the author nor the names of his contributors+   may be used to endorse or promote products derived from this software+   without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 THE AUTHORS 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.+-}+module Network.Protocol.Uri.Chars+  ( unreserved+  , genDelims+  , subDelims+  ) where++import Data.Char++-- 2.3.  Unreserved Characters+unreserved :: Char -> Bool+unreserved c = isAlphaNum c || elem c "-._~"++-- 2.2.  Reserved Characters+genDelims :: Char -> Bool+genDelims = flip elem ":/?#[]@"++subDelims :: Char -> Bool+subDelims = flip elem "!$&'()*+,;="+
+ src/Network/Protocol/Uri/Data.hs view
@@ -0,0 +1,214 @@+{-+Copyright (c) Sebastiaan Visser 2008++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.+3. Neither the name of the author nor the names of his contributors+   may be used to endorse or promote products derived from this software+   without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 THE AUTHORS 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.+-}+{-# LANGUAGE TemplateHaskell, TypeOperators #-}+module Network.Protocol.Uri.Data where++import Prelude hiding ((.), id)+import Control.Category+import Data.Label+import Data.Maybe+import Network.Protocol.Uri.Encode++type Scheme      = String+type RegName     = String+type Port        = Int+type Query       = String+type Fragment    = String+type Hash        = String+type UserInfo    = String+type PathSegment = String++data IPv4 = IPv4 Int Int Int Int+  deriving (Show, Read, Eq, Ord)++data Domain = Domain { __parts :: [String] }+  deriving (Show, Read, Eq, Ord)++data Host =+    Hostname { __domain  :: Domain }+  | RegName  { __regname :: RegName }+  | IP       { __ipv4    :: IPv4   }+--  | IPv6     { __ipv6    :: IPv6   }+  deriving (Show, Read, Eq, Ord)++data Authority = Authority+  { __userinfo :: UserInfo+  , __host     :: Host+  , __port     :: Maybe Port+  }+  deriving (Show, Read, Eq, Ord)++data Path = Path { __segments :: [PathSegment] }+  deriving (Show, Read, Eq, Ord)++data Uri = Uri+  { _relative  :: Bool+  , _scheme    :: Scheme+  , _authority :: Authority+  , __path     :: Path+  , __query    :: Query+  , __fragment :: Fragment+  }+  deriving (Show, Read, Eq, Ord)++$(mkLabels [''Domain, ''Path, ''Host, ''Authority, ''Uri])++-- _parts    :: Domain :-> [String]+-- _domain   :: Host :-> Domain+-- _ipv4     :: Host :-> IPv4+-- _regname  :: Host :-> String+-- _host     :: Authority :-> Host+-- _port     :: Authority :-> Maybe Port+-- _userinfo :: Authority :-> UserInfo+-- _segments :: Path :-> [PathSegment]+-- _path     :: Uri :-> Path++-- | Access raw (URI-encoded) query.++-- _query :: Uri :-> Query++-- | Access authority part of the URI.++-- authority :: Uri :-> Authority++-- | Access domain part of the URI, returns `Nothing' when the host is a+-- regname or IP-address.++domain :: Uri :-> Maybe Domain+domain = (Bij f (Hostname . fromJust)) `iso` (_host . authority)+  where+    f (Hostname d) = Just d+    f _            = Nothing++-- | Access regname part of the URI, returns `Nothing' when the host is a+-- domain or IP-address.++regname :: Uri :-> Maybe RegName+regname = (Bij f (RegName . fromJust)) `iso` (_host . authority)+  where+    f (RegName r) = Just r+    f _           = Nothing++-- | Access IPv4-address part of the URI, returns `Nothing' when the host is a+-- domain or regname.++ipv4 :: Uri :-> Maybe IPv4+ipv4 = (Bij f (IP . fromJust)) `iso` (_host . authority)+  where+    f (IP i) = Just i+    f _      = Nothing++-- | Access raw (URI-encoded) fragment.++-- _fragment :: Uri :-> Fragment++-- | Access the port number part of the URI when available.++port :: Uri :-> Maybe Port+port = _port . authority++-- | Access the query part of the URI, the part that follows the ?. The query+-- will be properly decoded when reading and encoded when writing.++query :: Uri :-> Query+query = encoded `iso` _query++-- | Access the fragment part of the URI, the part that follows the #. The+-- fragment will be properly decoded when reading and encoded when writing.++fragment :: Uri :-> Fragment+fragment = encoded `iso` _fragment++-- | Is a URI relative?++-- relative :: Uri :-> Bool++-- | Access the scheme part of the URI. A scheme is probably the protocol+-- indicator like /http/, /ftp/, etc.++-- scheme :: Uri :-> Scheme++-- | Access the path part of the URI as a list of path segments. The segments+-- will still be URI-encoded.++segments :: Uri :-> [PathSegment]+segments = _segments . _path++-- | Access the userinfo part of the URI. The userinfo contains an optional+-- username and password or some other credentials.++userinfo :: Uri :-> String+userinfo = _userinfo . authority++-- | Constructors for making empty URI.++mkUri :: Uri+mkUri = Uri False mkScheme mkAuthority mkPath mkQuery mkFragment++-- | Constructors for making empty `Scheme`.++mkScheme :: Scheme+mkScheme = ""++-- | Constructors for making empty `Path`.++mkPath :: Path+mkPath = Path []++-- | Constructors for making empty `Authority`.++mkAuthority :: Authority+mkAuthority = Authority "" mkHost mkPort++-- | Constructors for making empty `Query`.++mkQuery :: Query+mkQuery = ""++-- | Constructors for making empty `Fragment`.++mkFragment :: Fragment+mkFragment = ""++-- | Constructors for making empty `UserInfo`.++mkUserinfo :: UserInfo+mkUserinfo = ""++-- | Constructors for making empty `Host`.++mkHost :: Host+mkHost = Hostname (Domain [])++-- | Constructors for making empty `Port`.++mkPort :: Maybe Port+mkPort = Nothing+
+ src/Network/Protocol/Uri/Encode.hs view
@@ -0,0 +1,66 @@+{-+Copyright (c) Sebastiaan Visser 2008++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.+3. Neither the name of the author nor the names of his contributors+   may be used to endorse or promote products derived from this software+   without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 THE AUTHORS 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.+-}+{-# LANGUAGE TypeOperators #-}+module Network.Protocol.Uri.Encode where++import Data.Bits+import Data.Char+import Data.Maybe+import Data.Label+import Network.Protocol.Uri.Chars+import qualified Data.ByteString as B+import qualified Data.ByteString.UTF8 as U++-- | URI encode a string.++encode :: String -> String+encode = concatMap encodeChr+  where+    encodeChr c+      | unreserved c || genDelims c || subDelims c = [c]+      | otherwise = '%' :+          intToDigit (shiftR (ord c) 4) :+          intToDigit ((ord c) .&. 0x0F) : []++-- | URI decode a string.++decode :: String -> String+decode = U.toString . B.pack . map (fromIntegral . ord) . dec+  where+    dec [] = []+    dec ('%':d:e:ds) | isHexDigit d && isHexDigit e = (chr $ digs d * 16 + digs e) : dec ds +      where digs a = fromJust $ lookup (toLower a) $ zip "0123456789abcdef" [0..]+    dec (d:ds) = d : dec ds++-- | Decoding and encoding as a label.++encoded :: Bijection (->) String String+encoded = Bij decode encode+
+ src/Network/Protocol/Uri/Parser.hs view
@@ -0,0 +1,308 @@+{-+Copyright (c) Sebastiaan Visser 2008++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.+3. Neither the name of the author nor the names of his contributors+   may be used to endorse or promote products derived from this software+   without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 THE AUTHORS 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.+-}+{-# LANGUAGE+    StandaloneDeriving+  , TypeOperators+  , FlexibleContexts+  , FlexibleInstances+  #-}+module Network.Protocol.Uri.Parser where++import Control.Applicative hiding (empty)+import Control.Category+import Data.Char+import Data.List +import Data.Maybe+import Data.Label+import Network.Protocol.Uri.Data+import Network.Protocol.Uri.Encode+import Network.Protocol.Uri.Printer+import Network.Protocol.Uri.Query+import Prelude hiding ((.), id, mod)+import Safe+import Text.ParserCombinators.Parsec hiding (many, (<|>))++-- | Access the host part of the URI.++host :: Uri :-> String+host = (Bij showHost ((either (const mkHost) id) . parseHost)) `iso` (_host . authority)++-- | Access the path part of the URI. The query will be properly decoded when+-- reading and encoded when writing.++path :: Uri :-> FilePath+path = (Bij (decode . showPath) (either (const mkPath) id . parsePath . encode)) `iso` _path++-- | Access the path and query parts of the URI as a single string. The string+-- will will be properly decoded when reading and encoded when writing.++pathAndQuery :: Uri :-> String+pathAndQuery = values "?" `osi` Lens ((\p q -> [p, q]) <$> idx 0 `for` path <*> idx 1 `for` query)+  where idx = flip (atDef "")+        osi (Bij a b) = iso (Bij b a)++-- | Parse string into a URI and ignore all failures by returning an empty URI+-- when parsing fails. Can be quite useful in situations that parse errors are+-- unlikely.++toUri :: String -> Uri+toUri = either (const mkUri) id . parseUri++-- | Parse string into a URI.++parseUri :: String -> Either ParseError Uri+parseUri = parse pUriReference ""++-- | Parse string into a URI and only accept absolute URIs.++parseAbsoluteUri :: String -> Either ParseError Uri+parseAbsoluteUri = parse pAbsoluteUri ""++-- | Parse string into an authority.++parseAuthority :: String -> Either ParseError Authority+parseAuthority = parse pAuthority ""++-- | Parse string into a path.++parsePath :: String -> Either ParseError Path+parsePath = parse pPath ""++-- | Parse string into a host.++parseHost :: String -> Either ParseError Host+parseHost = parse pHost ""++-- D.2.  Modifications++pAlpha, pDigit, pAlphanum :: CharParser st Char+pAlpha    = letter+pDigit    = digit+pAlphanum = alphaNum++-- 2.3.  Unreserved Characters++pUnreserved :: GenParser Char st Char+pUnreserved  = pAlphanum <|> oneOf "-._~"++pReserved :: GenParser Char st Char+pReserved  = pGenDelims <|> pSubDelims++pGenDelims :: CharParser st Char+pGenDelims = oneOf ":/?#[]@"++pSubDelims :: CharParser st Char+pSubDelims = oneOf "!$&'()*+,;="++-- 2.1.  Percent-Encoding++pPctEncoded :: GenParser Char st String+pPctEncoded = (:) <$> char '%' <*> pHex++pHex :: GenParser Char st String+pHex = (\a b -> a:b:[])+    <$> hexDigit+    <*> hexDigit++-- 3.  Syntax Components++-- With the hier-part integrated.++pUri :: GenParser Char st Uri+pUri = (\a (b,c) d e -> Uri False a b c d e)+  <$> (pScheme <* string ":")+  <*> (q <|> p)+  <*> option "" (string "?" *> pQuery)+  <*> option "" (string "#" *> pFragment)+  where+    q = (,) <$> (string "//" *> pAuthority) <*> pPathAbempty+    p = ((,) mkAuthority) <$> (pPathAbsolute <|> pPathRootless {-<|> pPathEmpty-})++-- 3.1.  Scheme++pScheme :: GenParser Char st String+pScheme = (:) <$> pAlpha <*> many (pAlphanum <|> oneOf "+_.")++-- 3.2.  Authority++pAuthority :: GenParser Char st Authority+pAuthority = Authority+  <$> option mkUserinfo (try (pUserinfo <* string "@"))+  <*> pHost+  <*> option Nothing (string ":" *> pPort)++-- 3.2.1.  User Information++pUserinfo :: GenParser Char st String+pUserinfo = concat <$> many (+      (pure <$> pUnreserved)+  <|> (         pPctEncoded)+  <|> (pure <$> pSubDelims)+  <|> (pure <$> oneOf ":")+  )++-- 3.2.2.  Host++pHost :: GenParser Char st Host+pHost = diff <$> pRegName -- <|> RegName <$> pRegName+  where+    diff  a = either (const (RegName a)) sep (parse pHostname "" a)+    sep   a = if hst a then Hostname (Domain a) else ipreg a+    ipreg a = if ip a then IP (toIP a) else RegName (intercalate "." a)+    hst     = not . all isDigit . headDef "" . dropWhile null . reverse+    ip    a = length a == 4 && length (mapMaybe (either (const Nothing) Just . parse pDecOctet "") a) == 4+    toIP [a, b, c, d] = IPv4 (read a) (read b) (read c) (read d)+    toIP _            = IPv4 0 0 0 0++{-+pfff, ipv6 is sooo not gonna make it..++pIPLiteral = "[" ( IPv6address <|> IPvFuture  ) "]"++pIPvFuture = "v" 1*HEXDIG "." 1*( unreserved <|> subDelims <|> ":" )++pIPv6address =+                                 6( h16 ":" ) ls32+  <|>                       "::" 5( h16 ":" ) ls32+  <|> [               h16 ] "::" 4( h16 ":" ) ls32+  <|> [ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls32+  <|> [ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls32+  <|> [ *3( h16 ":" ) h16 ] "::"    h16 ":"   ls32+  <|> [ *4( h16 ":" ) h16 ] "::"              ls32+  <|> [ *5( h16 ":" ) h16 ] "::"              h16+  <|> [ *6( h16 ":" ) h16 ] "::"++pH16           = 1*4HEXDIG+pLs32          = ( h16 ":" h16 ) <|> IPv4address+-}++pIPv4address :: GenParser Char st [Int]+pIPv4address = (:) <$> pDecOctet <*> (count 3 $ char '.' *> pDecOctet)++pDecOctet :: GenParser Char st Int+pDecOctet = read <$> choice [+    try ((\a b c -> [a,b,c]) <$> char '2' <*> char '5'      <*> oneOf "012345")+  , try ((\a b c -> [a,b,c]) <$> char '2' <*> oneOf "01234" <*> digit)+  , try ((\a b c -> [a,b,c]) <$> char '1' <*> digit         <*> digit)+  , try ((\a b   -> [a,b])   <$>              digit         <*> digit)+  ,     (pure                <$>                                digit)+  ]++pRegName :: GenParser Char st String+pRegName = concat <$> many1 (+      (pure <$> pUnreserved)+  <|>           pPctEncoded+  <|> (pure <$> pSubDelims))++-- Not actually part of the rfc3986, but comptability with the rfc2396.+-- This information can be useful, so why throw away.++pHostname :: GenParser Char st [String]+pHostname = sepBy (option "" pDomainlabel) (string ".")++pDomainlabel :: GenParser Char st String+pDomainlabel = intercalate "-" <$> sepBy1 (some pAlphanum) (string "-")++-- 3.2.3.  Port++pPort :: GenParser Char st (Maybe Port)+pPort = readMay <$> some pDigit++-- 3.4.  Query++pQuery :: GenParser Char st String+pQuery = concat <$> many (pPchar <|> pure <$> oneOf "/?")++-- 3.5.  Fragment++pFragment :: GenParser Char st String+pFragment = concat <$> many (pPchar <|> pure <$> oneOf "/?" )++-- 3.3.  Path++pPath, pPathAbempty, pPathAbsolute, pPathNoscheme, pPathRootless, pPathEmpty :: GenParser Char st Path++pPath =+      try pPathAbsolute -- begins with "/" but not "//"+  <|> try pPathNoscheme -- begins with a nonColon segment+  <|> try pPathRootless -- begins with a segment+  <|> pPathEmpty        -- zero characters++pPathAbempty  = Path . ("":) <$> _pSlashSegments+pPathAbsolute = (char '/' *>) $ Path . ("":) <$> (option [] $ (:) <$> pSegmentNz <*> _pSlashSegments)+pPathNoscheme = Path <$> ((:) <$> pSegmentNzNc <*> _pSlashSegments)+pPathRootless = Path <$> ((:) <$> pSegmentNz    <*> _pSlashSegments)+pPathEmpty    = Path [] <$ string ""++pSegment, pSegmentNz, pSegmentNzNc :: GenParser Char st String+pSegment     = concat <$> many pPchar+pSegmentNz   = concat <$> some pPchar+pSegmentNzNc = concat <$> some (+      (pure <$> pUnreserved)+  <|>           pPctEncoded+  <|> (pure <$> pSubDelims)+  <|> (pure <$> oneOf "@" ))++_pSlashSegments :: GenParser Char st [PathSegment]+_pSlashSegments = (many $ (:) <$> char '/' *> pSegment)+++pPchar :: GenParser Char st String+pPchar = choice+  [ pure <$> pUnreserved+  , pPctEncoded+  , pure <$> pSubDelims+  , pure <$> oneOf ":@"+  ]++-- 4.1.  URI Reference++pUriReference :: GenParser Char st Uri+pUriReference = try pAbsoluteUri <|> pRelativeRef++-- 4.2.  Relative Reference++-- With the relative-part integrated.++pRelativeRef :: GenParser Char st Uri+pRelativeRef = ($)+  <$> (try pRelativePart+  <|> ((Uri True mkScheme mkAuthority)+  <$> (pPathAbsolute <|> pPathRootless <|> pPathEmpty)))+  <*> option "" (string "?" *> pQuery)+  <*> option "" (string "#" *> pFragment)++pRelativePart :: GenParser Char st (Query -> Fragment -> Uri)+pRelativePart = Uri True mkScheme <$> (string "//" *> pAuthority) <*> pPathAbempty++-- 4.3.  Absolute URI++pAbsoluteUri :: GenParser Char st Uri+pAbsoluteUri = pUri
+ src/Network/Protocol/Uri/Path.hs view
@@ -0,0 +1,102 @@+{-+Copyright (c) Sebastiaan Visser 2008++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.+3. Neither the name of the author nor the names of his contributors+   may be used to endorse or promote products derived from this software+   without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 THE AUTHORS 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.+-}+{-# LANGUAGE TypeOperators #-}+module Network.Protocol.Uri.Path where++import Data.List+import Network.Protocol.Mime+import Data.Label++{- | Label to access the extension of a filename. -}++extension :: FilePath :-> Maybe String+extension = lens getExt setExt+  where+    splt     p = (\(a,b) -> (reverse a, reverse b)) $ break (=='.') $ reverse p+    isExt e  p = '/' `elem` e || not ('.' `elem` p)+    getExt   p = let (u, v) = splt p in+                 if isExt u v then Nothing else Just u+    setExt e p = let (u, v) = splt p in+                 (if isExt u v then p else init v) ++ maybe "" ('.':) e++{- |+Try to guess the correct mime type for the input file based on the file+extension.+-}++mimetype :: FilePath -> Maybe String+mimetype p = get extension p >>= mime++{- |+Normalize a path by removing or merging all dot or dot-dot segments and double+slashes. +-}++-- Todo: is this windows-safe?  is it really secure?++normalize :: FilePath -> FilePath+normalize p  = norm_rev (reverse p)+  where+    norm_rev ('/':t) = start_dir 0 "/" t+    norm_rev (    t) = start_dir 0 ""  t++    start_dir n q (".."           ) = rest_dir   n    q  ""+    start_dir n q ('/':t          ) = start_dir  n    q  t+    start_dir n q ('.':'/':t      ) = start_dir  n    q  t+    start_dir n q ('.':'.':'/': t ) = start_dir (n+1) q  t+    start_dir n q (t              ) = rest_dir   n    q  t++    rest_dir  n q  ""+        | n > 0      = foldr (++) q (replicate n "../")+        | null q     = "/"+        | otherwise  = q+    rest_dir  0 q ('/':t ) = start_dir  0   ('/':q)  t+    rest_dir  n q ('/':t ) = start_dir (n-1)     q   t+    rest_dir  0 q (h:t   ) = rest_dir   0   (  h:q)  t+    rest_dir  n q (_:t   ) = rest_dir   n        q   t++{- | Jail a filepath within a jail directory. -}++jail+  :: FilePath         -- ^ Jail directory.+  -> FilePath         -- ^ Filename to jail.+  -> Maybe FilePath+jail jailDir p =+  let nj = normalize jailDir+      np = normalize p in+  if nj `isPrefixOf` np -- && not (".." `isPrefixOf` np)+    then Just np+    else Nothing++{- | Concatenate and normalize two filepaths. -}++(/+) :: FilePath -> FilePath -> FilePath+a /+ b = normalize (a ++ "/" ++ b)+
+ src/Network/Protocol/Uri/Printer.hs view
@@ -0,0 +1,98 @@+{-+Copyright (c) Sebastiaan Visser 2008++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.+3. Neither the name of the author nor the names of his contributors+   may be used to endorse or promote products derived from this software+   without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 THE AUTHORS 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.+-}+module Network.Protocol.Uri.Printer where++import Network.Protocol.Uri.Data++printPath :: Path -> ShowS+printPath (Path ("":xs)) = sc '/' . printPath (Path xs)+printPath (Path xs)      = intersperseS (sc '/') (map ss xs)++showPath :: Path -> String+showPath = flip printPath ""++-- instance Show Path where+--   showsPrec _ (Path ("":xs)) = sc '/' . shows (Path xs)+--   showsPrec _ (Path xs)      = intersperseS (sc '/') (map ss xs)++printIPv4 :: IPv4 -> ShowS+printIPv4 (IPv4 a b c d) = intersperseS (sc '.') (map shows [a, b, c, d])++-- instance Show IPv4 where+--   showsPrec _ (IPv4 a b c d) = intersperseS (sc '.') (map shows [a, b, c, d])++printDomain :: Domain -> ShowS+printDomain (Domain d) = intersperseS (sc '.') (map ss d)++-- instance Show Domain where+--   showsPrec _ (Domain d) = intersperseS (sc '.') (map ss d)++printHost :: Host -> ShowS+printHost (Hostname d) = printDomain d+printHost (IP i)       = printIPv4 i +printHost (RegName r)  = ss r++showHost :: Host -> String+showHost = flip printHost ""++printAuthority :: Authority -> ShowS+printAuthority (Authority u h p) =+    let u' = if null u then id else ss u . ss "@"+        p' = maybe id (\s -> sc ':' . shows s) p+    in u' . printHost h . p'++showAuthority :: Authority -> String+showAuthority = flip printAuthority ""++printUri :: Uri -> ShowS+printUri (Uri _ s a p q f) =+    let s' = if null s then id else ss s . sc ':'+        a' = printAuthority a ""+        p' = printPath p+        q' = if null q then id else sc '?' . ss q+        f' = if null f then id else sc '#' . ss f+        t' = if null a' then id else ss "//"+    in s' . t' . ss a' . p' . q' . f'++showUri :: Uri -> String+showUri = flip printUri ""++ss :: String -> ShowS+ss = showString++sc :: Char -> ShowS+sc = showChar++-- | ShowS version of intersperse.++intersperseS :: ShowS -> [ShowS] -> ShowS+intersperseS _ []     = id+intersperseS s (x:xs) = foldl (\a b -> a.s.b) x xs+
+ src/Network/Protocol/Uri/Query.hs view
@@ -0,0 +1,82 @@+{-+Copyright (c) Sebastiaan Visser 2008++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.+3. Neither the name of the author nor the names of his contributors+   may be used to endorse or promote products derived from this software+   without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 THE AUTHORS 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.+-}+{-# LANGUAGE TypeOperators #-}+module Network.Protocol.Uri.Query where++import Prelude hiding ((.), id)+import Control.Category+import Data.List+import Data.List.Split +import Data.Label+import Network.Protocol.Uri.Data+import Network.Protocol.Uri.Encode++type Parameters = [(String, String)]++-- | Fetch the query parameters form a URI.++queryParams :: Uri :-> Parameters+queryParams = params `iso` _query++-- | Generic lens to parse/print a string as query parameters.++params :: Bijection (->) String Parameters+params = keyValues "&" "=" . (Bij from to) . encoded+  where from = intercalate " " . splitOn "+"+        to   = intercalate "+" . splitOn " "++-- | Generic label for accessing key value pairs encoded in a string.++keyValues :: String -> String -> Bijection (->) String Parameters+keyValues sep eqs = Bij parser printer+  where parser =+            filter (\(a, b) -> not (null a))+          . map (f . splitOn eqs)+          . concat+          . map (splitOn sep)+          . lines+          where f []     = ("", "")+                f [x]    = (trim x, "")+                f (x:xs) = (trim x, trim $ intercalate eqs xs)+        printer = intercalate sep . map (\(a, b) -> a ++ eqs ++ b)++-- | Generic label for accessing lists of values encoded in a string.++values :: String -> Bijection (->) String [String]+values sep = Bij parser printer+  where parser = filter (not . null) . concat . map (map trim . splitOn sep) . lines+        printer = intercalate sep++-- Helper to trim all heading and trailing whitespace.++trim :: String -> String+trim = rev (dropWhile (`elem` " \t\n\r"))+  where rev f = reverse . f . reverse . f+
+ src/Network/Protocol/Uri/Remap.hs view
@@ -0,0 +1,74 @@+{-+Copyright (c) Sebastiaan Visser 2008++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.+3. Neither the name of the author nor the names of his contributors+   may be used to endorse or promote products derived from this software+   without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 THE AUTHORS 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.+-}+module Network.Protocol.Uri.Remap where++import Control.Category+import Data.List +import Data.Label+import Network.Protocol.Uri.Data+import Prelude hiding ((.), id, mod)++-- | Map one URI to another using a URI mapping scheme. A URI mapping scheme is+-- simply a pair of URIs of which only the host part, port number and path will+-- be taken into account when mapping.++remap :: (Uri, Uri) -> Uri -> Maybe Uri+remap (f, t) u =+  let+    ftu = [f, t, u]+    hst = _host . authority+    [h0, h1, h2] = map (get hst)      ftu+    [p0, p1, p2] = map (get port)     ftu+    [s0, s1, s2] = map (get segments) ftu+  in case+     ( remapHost h0 h1 h2+     , remapPort p0 p1 p2+     , remapPath s0 s1 s2+     ) of+    (Just h, Just p, Just s)+      -> Just (set hst h . set port p . set segments s $ u)+    _ -> Nothing+  where+  remapHost (Hostname (Domain a))+            (Hostname (Domain b))   (Hostname (Domain c))          = fmap (Hostname . Domain . (++b)) (a `stripPrefix` reverse c)+  remapHost (Hostname (Domain a)) b (Hostname (Domain c)) | a == c = Just b+  remapHost (RegName a)           b (RegName c)           | a == c = Just b+  remapHost (IP a)                b (IP c)                | a == c = Just b+  remapHost _                     _ _                              = Nothing+  remapPath xs ys zs = fmap (ys++) (xs `stripPrefix` zs)+  remapPort x y z = if x == z then Just y else Nothing++-- from = toUri "http://myhost:8080/ggl"+-- to   = toUri "http://google.com/gapp"+-- testRemap =+--   do let x = remap from to (toUri "http://images.myhost:8080/ggl/search?q=aapjes")+--      print x++
+ src/Network/Shpider/Forms.hs view
@@ -0,0 +1,129 @@+{-+ - Copyright (c) 2009-2010 Johnny Morrice+ -+ - Permission is hereby granted, free of charge, to any person+ - obtaining a copy of this software and associated documentation + - files (the "Software"), to deal in the Software without + - restriction, including without limitation the rights to use, copy, + - modify, merge, publish, distribute, sublicense, and/or sell copies + - of the Software, and to permit persons to whom the Software is + - furnished to do so, subject to the following conditions:+ -+ - The above copyright notice and this permission notice shall be + - included in all copies or substantial portions of the Software.+ -+ - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF+ - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS+ - BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN+ - ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN+ - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+ - SOFTWARE.+ -+ -}+module Network.Shpider.Forms +   (+   Form (..)+   , Method (..)+   , gatherForms+   , fillOutForm+   , allForms+   , toForm+   , mkForm+   )+   where++import Data.Maybe+import Data.Char+import Control.Arrow ( first )+import qualified Data.Map as M+import Text.HTML.TagSoup.Parsec++data Method = GET | POST+   deriving Show++-- | Plain old form: Method, action and inputs.+data Form = +   Form { method :: Method+        , action :: String +        , inputs :: M.Map String String+        }+   deriving Show++-- | Takes a form and fills out the inputs with the given [ ( String , String ) ].+-- It is convienent to use the `pairs` syntax here.+--+-- @+-- f : _ <- `getFormsByAction` \"http:\/\/whatever.com\"+-- `sendForm` $ `fillOutForm` f $ `pairs` $ do+--    \"author\" =: \"Johnny\"+--    \"message\" =: \"Nice syntax dewd.\"+-- @+fillOutForm :: Form -> [ ( String , String ) ] -> Form+fillOutForm = foldl ( \f (n,v) -> f { inputs = M.insert n v $ inputs f } )++-- | The first argument is the action attribute of the form, the second is the method attribute, and the third are the inputs.+mkForm :: String -> Method -> [ ( String , String ) ] -> Form+mkForm a m ps =+   Form { action = a+        , method = m+        , inputs = M.fromList ps+        }++-- | Gets all forms from a list of tags.+gatherForms :: [ Tag String ] -> [ Form ]+gatherForms =+   tParse allForms++-- | The `TagParser` which parses all forms.+allForms :: TagParser String [ Form ]+allForms = do+   fs <- allWholeTags "form"+   return $ mapMaybe toForm fs++-- | A case insensitive lookup for html attributes.+attrLookup :: String -> [ ( String , String ) ] -> Maybe String+attrLookup attr =+   lookup ( map toLower attr ) . map ( first (map toLower) )++-- | Turns a String lowercase.  <rant>In my humble opinion, and considering that a few different packages implement this meager code, this should be in the prelude.</rant>+lowercase :: String -> String +lowercase = map toLower+++toForm :: WholeTag String -> Maybe Form+toForm ( TagOpen _ attrs , innerTags , _ ) = do+   m <- methodLookup attrs+   a <- attrLookup "action" attrs+   let is = tParse ( allOpenTags "input" ) innerTags+       tas = tParse ( allWholeTags "textarea" ) innerTags+   Just $ Form { inputs = M.fromList $ mapMaybe inputNameValue is ++ mapMaybe textAreaNameValue tas+               , action = a+               , method = m+               }++methodLookup attrs = do+   m <- attrLookup "method" attrs+   case lowercase m of+      "get" ->+         Just GET+      "post" ->+         Just POST+      otherwise ->+         Nothing++inputNameValue ( TagOpen _ attrs ) = do+   v <- case attrLookup "value" attrs of+           Nothing ->+              Just ""+           j@(Just _ ) ->+              j+   n <- attrLookup "name" attrs+   Just ( n , v )++textAreaNameValue ( TagOpen _ attrs , inner , _ ) = do+   let v = innerText inner+   n <- attrLookup "name" attrs+   Just ( n , v )+
+ src/Test/Api.hs view
@@ -0,0 +1,16 @@+module Test.Api where++import Web.VKHS.Login+import Web.VKHS.Api+import Text.Printf++-- | Almost working example. Just set correct values for client_id/login/password+print_user_info = do+    let e = env "3213232" "user@example.com" "password" [Photos,Audio,Groups]+    (Right at) <- login e+    (Right ans) <- api e{verbose = Debug} at "users.get" [+          ("uids","911727")+        , ("fields","first_name,last_name,nickname,screen_name")+        , ("name_case","nom")+        ]+    putStrLn ans
+ src/Test/Data.hs view
@@ -0,0 +1,27 @@+module Test.Test where++import Data.Label+import Network.Protocol.Http+import Network.Protocol.Uri+import Network.Protocol.Uri.Query+import Network.Protocol.Cookie as C+import Network.Shpider.Forms++import Text.HTML.TagSoup++tryparse = gatherForms . parseTags ++test_cookies :: Cookies+test_cookies = get setCookies test_headers+++test_headers :: Http Response+test_headers = read "Http {_headline = Response {__status = Found}, _version = Version {_major = 1, _minor = 1}, _headers = Headers {unHeaders = [(\"Server\",\"nginx/1.2.1\"),(\"Date\",\"Wed, 29 Aug 2012 08:35:18 GMT\"),(\"Content-Type\",\"text/html; charset=windows-1251\"),(\"Content-Length\",\"0\"),(\"Connection\",\"keep-alive\"),(\"X-Powered-By\",\"PHP/5.3.3-7+squeeze3\"),(\"Set-Cookie\",\"remixlang=0; expires=Sat, 24-Aug-2013 14:23:04 GMT; path=/; domain=.vk.com\"),(\"Pragma\",\"no-cache\"),(\"Cache-control\",\"no-store\"),(\"Set-Cookie\",\"remixchk=5; expires=Tue, 20-Aug-2013 04:42:57 GMT; path=/; domain=.vk.com\"),(\"Location\",\"https://login.vk.com/?from_host=oauth.vk.com&from_protocol=http&ip_h=670993b49983a18c93&soft=1&to=aHR0cDovL29hdXRoLnZrLmNvbS9vYXV0aC9hdXRob3JpemU/Y2xpZW50X2lkPTMwODIyNjYmc2NvcGU9d2FsbCxncm91cCZyZWRpcmVjdF91cmk9aHR0cDovL29hdXRoLnZrLmNvbS9ibGFuay5odG1sJmRpc3BsYXk9d2FwJnJlc3BvbnNlX3R5cGU9dG9rZW4-\"),(\"Vary\",\"Accept-Encoding\")]}}"++test_body :: String+test_body = "<!DOCTYPE html PUBLIC \"-//WAPFORUM//DTD XHTML Mobile 1.0//EN\" \"http://www.wapforum.org/DTD/xhtml-mobile10.dtd\">\n<html xmlns=\"http://www.w3.org/1999/xhtml\">\n<head>\n<meta http-equiv=\"content-type\" content=\"application/xhtml+xml; charset=UTF-8\" />\n<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\" />\n<title>\208\159\208\190\208\187\209\131\209\135\208\181\208\189\208\184\208\181 \208\180\208\190\209\129\209\130\209\131\208\191\208\176 \208\186 \208\146\208\154\208\190\208\189\209\130\208\176\208\186\209\130\208\181</title>\n<style type=\"text/css\">\nhtml,body {\n  padding: 0px;\n  margin: 0px;\n  font-family: tahoma, arial, verdana, sans-serif, Lucida Sans;\n  text-align: center;\n  background: #f7f7f7;\n  font-size: 12px;\n}\nb {\n  color: #36638E;\n}\n.button_yes {\n  border: 1px solid #3B6798;\n  text-shadow: #45688E 0px 1px 0px;\n  display: inline-block;\n  width: 90px;\n  text-decoration: none;\n  padding: 0px;\n}\n.button_yes input {\n  background-color: #6D8FB3;\n  border: 1px solid #7E9CBC;\n  border-bottom-color: #5C82AB;\n  border-left-color: #5C82AB;\n  border-right-color: #5C82AB;\n  padding: 3px 3px 4px 3px;\n  color: #FFFFFF;\n  margin: 0px;\n  width: 90px;\n  cursor: pointer;\n  font-size: 12px;\n}\n.button_no {\n  border: 1px solid #B8B8B8;\n  border-top: 1px solid #9F9F9F;\n  text-shadow: #FFFFFF 0px 1px 0px;\n  display: inline-block;\n  width: 70px;\n  color: #000000;\n  text-decoration: none;\n}\n.button_no div {\n  background-color: #EAEAEA;\n  border: 1px solid #FFFFFF;\n  border-bottom-color: #DFDFDF;\n  border-left-color: #F4F4F4;\n  border-right-color: #F4F4F4;\n  padding: 3px 3px 4px 3px;\n}\na {\n  color: #2B587A;\n  text-decoration: none;\n}\n</style>\n<script type=\"text/javascript\" language=\"javascript\">\n// <![CDATA[\nif (parent && parent != window) {\n  location.href = 'http://vk.com';\n}\n// ]]>\n</script>\n</head>\n<body>\n<div style=\"background: #5b7fa6; padding: 2px 3px 3px 3px; border-bottom: 1px solid #6f91bb;\">\n<b style=\"color: #FFFFFF;\">\208\159\208\190\208\187\209\131\209\135\208\181\208\189\208\184\208\181 \208\180\208\190\209\129\209\130\209\131\208\191\208\176 \208\186 \208\146\208\154\208\190\208\189\209\130\208\176\208\186\209\130\208\181</b>\n</div>\n<div style=\"border-top: 1px solid #4a6a91; padding:10px;\">\n  <div style=\"background: #ffffff; border: 1px solid #adbbca; padding: 5px;'\">\n    <style>\ninput {\n  border: 1px solid #C0CAD5;\n}\n.label {\n  color: #777777;\n}\n</style>\n\n<form method=\"POST\" action=\"https://login.vk.com/?act=login&soft=1&utf8=1\">\n<input type=\"hidden\" name=\"q\" value=\"1\">\n<input type=\"hidden\" name=\"from_host\" value=\"oauth.vk.com\">\n<input type=\"hidden\" name=\"from_protocol\" value=\"http\">\n<input type=\"hidden\" name=\"ip_h\" value=\"670993b49983a18c93\" />\n<input type=\"hidden\" name=\"to\" value=\"aHR0cDovL29hdXRoLnZrLmNvbS9vYXV0aC9hdXRob3JpemU/Y2xpZW50X2lkPTMwODIyNjYmcmVkaXJlY3RfdXJpPWJsYW5rLmh0bWwmcmVzcG9uc2VfdHlwZT10b2tlbiZzY29wZT04MTkyJnN0YXRlPSZkaXNwbGF5PXdhcA--\">\n<span class=\"label\">\208\162\208\181\208\187\208\181\209\132\208\190\208\189 \208\184\208\187\208\184 e-mail:</span><br />\n<input type=\"text\" name=\"email\"><br />\n<span class=\"label\">\208\159\208\176\209\128\208\190\208\187\209\140:</span><br />\n<input type=\"password\" name=\"pass\">\n<div style=\"padding: 8px 0px 5px 0px\">\n<div class=\"button_yes\">\n  <input type=\"submit\" value=\"\208\146\208\190\208\185\209\130\208\184\" />\n</div>\n<a class=\"button_no\" href=\"https://oauth.vk.com/grant_access?hash=c3ebb24fc8c8def60f&client_id=3082266&settings=8192&redirect_uri=blank.html&cancel=1&state=&token_type=0\">\n  <div>\n  \208\158\209\130\208\188\208\181\208\189\208\176\n  </div>\n</a>\n</form>\n</div>\n  </div>\n  <div style=\"border-top: 1px solid #DDD; margin: 0 1px 7px 1px;\"></div>\n  \n  <a href=\"http://m.vkontakte.ru\">\208\191\208\181\209\128\208\181\208\185\209\130\208\184 \208\186 \209\129\208\176\208\185\209\130\209\131</a>\n</div>\n</body>"+++test_uri :: Uri+test_uri = Uri {_relative = False, _scheme = "http", _authority = Authority {__userinfo = "", __host = Hostname {__domain = Domain {__parts = ["oauth","vk","com"]}}, __port = Nothing}, __path = Path {__segments = ["","blank.html"]}, __query = "", __fragment = "access_token=ab266e7cfb6db4e5ab99513d4aab048f09aab2bab2ba713c89d4d95b37ad4f6&expires_in=86400&user_id=911727"} +
+ src/Test/Debug.hs view
@@ -0,0 +1,20 @@+-- FIXME: in order debug to work+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE OverlappingInstances #-}+{-# LANGUAGE TypeSynonymInstances #-}+{-# LANGUAGE FlexibleInstances #-}++module Debug where++import Control.Monad+import Control.Monad.Trans++class Debug x where+    debug :: (MonadIO m) => x -> m ()++instance (Show x) => Debug x where+    debug = liftIO . putStrLn . show . show++instance Debug String where+    debug = liftIO . putStrLn . show+
+ src/Web/VKHS/Curl.hs view
@@ -0,0 +1,45 @@+{-# LANGUAGE ScopedTypeVariables #-}++module Web.VKHS.Curl (vk_curl) where++import Prelude hiding (catch)+import Control.Exception (catch,bracket)+import Control.Concurrent (threadDelay)+import Control.Monad.Writer+import Data.IORef (newIORef, readIORef, atomicModifyIORef)+import qualified Data.ByteString.UTF8 as U+import Network.Curlhs.Core++import Web.VKHS.Types++import qualified Data.ByteString.Char8 as BS++-- | Generic request sender. Uses delay to prevent application from being+-- blocked for flooding+vk_curl :: Env -> Writer [CURLoption] () -> IO (Either String String)+vk_curl e w = do+    let askE x = return (x e)+    v <- askE verbose+    a <- askE useragent+    d <- askE delay_ms+    do+        buff <- newIORef BS.empty+        bracket (curl_easy_init) (curl_easy_cleanup) $ \curl -> do {+            let +                memwrite n = atomicModifyIORef buff+                    (\o -> (BS.append o n, CURL_WRITEFUNC_OK))+            in+            curl_easy_setopt curl $+                [ CURLOPT_HEADER         True+                , CURLOPT_WRITEFUNCTION (Just $ memwrite)+                , CURLOPT_SSL_VERIFYPEER False+                , CURLOPT_USERAGENT $ BS.pack a+                , CURLOPT_VERBOSE (v == Debug )+                ] ++ (execWriter w);+            curl_easy_perform curl;+            threadDelay (1000 * d); -- delay in microseconds+            b <- readIORef buff ;+            return (Right $ U.toString b) ;+        } `catch`+            (\(e::CURLcode) -> return $ Left ("CURL error: " ++ (show e)))+