diff --git a/CONTRIBUTORS b/CONTRIBUTORS
new file mode 100644
--- /dev/null
+++ b/CONTRIBUTORS
@@ -0,0 +1,2 @@
+- <harmen@millionmonkeys.nl>
+- Marc Weber <marco-oweber@gmx.de>
diff --git a/Network/AWS/AWSConnection.hs b/Network/AWS/AWSConnection.hs
--- a/Network/AWS/AWSConnection.hs
+++ b/Network/AWS/AWSConnection.hs
@@ -55,5 +55,5 @@
                   ( _, "", "") -> Nothing
                   ( _, "",  _) -> Just (amazonS3Connection ak sk1)
                   ( _,  _,  _) -> Just (amazonS3Connection ak sk0)
-    where getEnvKey s = do catch (getEnv s) (const $ return "")
+    where getEnvKey s = catch (getEnv s) (const $ return "")
 
diff --git a/Network/AWS/Authentication.hs b/Network/AWS/Authentication.hs
--- a/Network/AWS/Authentication.hs
+++ b/Network/AWS/Authentication.hs
@@ -80,19 +80,19 @@
                             uriQuery = s3query a,
                             uriFragment = "" },
               rqMethod = s3operation a,
-              rqHeaders = [Header HdrHost (s3Hostname a)] ++
-                          (headersFromAction a),
+              rqHeaders = Header HdrHost (s3Hostname a) :
+                          headersFromAction a,
               rqBody = (s3body a)
             }
-    where qpath = "/" ++ (s3object a)
+    where qpath = '/' : s3object a
 
 -- | Create 'Header' objects from an action.
 headersFromAction :: S3Action
                   -> [Header]
-headersFromAction a = map (\(k,v) -> case k of
-                                       "Content-Type" -> Header HdrContentType v
-                                       otherwise -> (Header (HdrCustom k)) (mimeEncodeQP v))
-                                            (s3metadata a)
+headersFromAction = map (\(k,v) -> case k of
+                                    "Content-Type" -> Header HdrContentType v
+                                    otherwise -> Header (HdrCustom k) (mimeEncodeQP v))
+                    . s3metadata
 
 -- | Inspect HTTP body, and add a @Content-Length@ header with the
 --   correct length.
@@ -104,7 +104,7 @@
                         -> HTTP.HTTPRequest L.ByteString -- ^ Request to transform
                         -> HTTP.HTTPRequest L.ByteString -- ^ Authenticated request
 addAuthenticationHeader act req = insertHeader HdrAuthorization auth_string req
-    where auth_string = "AWS " ++ (awsAccessKey conn) ++ ":" ++ signature
+    where auth_string = "AWS " ++ awsAccessKey conn ++ ":" ++ signature
           signature = makeSignature conn (stringToSign act req)
           conn = s3conn act
 
@@ -121,9 +121,9 @@
 --   request.
 stringToSign :: S3Action -> HTTP.HTTPRequest L.ByteString -> String
 stringToSign a r =
-    (canonicalizeHeaders r) ++
-    (canonicalizeAmzHeaders r) ++
-    (canonicalizeResource a)
+    canonicalizeHeaders r ++
+    canonicalizeAmzHeaders r ++
+    canonicalizeResource a
 
 -- | Extract header data needed for signing.
 canonicalizeHeaders :: HTTP.HTTPRequest L.ByteString -> String
@@ -136,8 +136,8 @@
               hdr_content_md5 = get_header HdrContentMD5
               hdr_date = get_header HdrDate
               hdr_content_type = get_header HdrContentType
-              get_header h = maybe "" id (findHeader h r)
-              dateOrExpiration = maybe hdr_date id (findHeader HdrExpires r)
+              get_header h = fromMaybe "" (findHeader h r)
+              dateOrExpiration = fromMaybe hdr_date (findHeader HdrExpires r)
 
 -- | Extract @x-amz-*@ headers needed for signing.
 --   find all headers with type HdrCustom that begin with amzHeader
@@ -152,7 +152,7 @@
         amzHeaderKV = map headerToLCKeyValue amzHeaders
         sortedGroupedHeaders = groupHeaders (sortHeaders amzHeaderKV)
         uniqueHeaders = combineHeaders sortedGroupedHeaders
-    in concat (map (\a -> a ++ "\n") (map showHeader uniqueHeaders))
+    in concatMap (\a -> a ++ "\n") (map showHeader uniqueHeaders)
 
 -- | Give the string representation of a (key,value) header pair.
 --   Uses rules for authenticated headers.
@@ -169,7 +169,7 @@
 
 -- | Combine same-named headers.
 combineHeaders :: [[(String, String)]] -> [(String, String)]
-combineHeaders h = map mergeSameHeaders h
+combineHeaders = map mergeSameHeaders
 
 -- | Headers with same name should have values merged.
 mergeSameHeaders :: [(String, String)] -> (String, String)
@@ -178,11 +178,11 @@
 
 -- | Group headers with the same name.
 groupHeaders :: [(String, String)] -> [[(String, String)]]
-groupHeaders = groupBy (\a b -> (fst a) == (fst b))
+groupHeaders = groupBy (\a b -> fst a == fst b)
 
 -- | Sort by key name.
 sortHeaders :: [(String, String)] -> [(String, String)]
-sortHeaders = sortBy (\a b -> (fst a) `compare` (fst b))
+sortHeaders = sortBy (\a b -> fst a `compare` fst b)
 
 -- | Make 'Header' easier to work with, and lowercase keys.
 headerToLCKeyValue :: Header -> (String, String)
@@ -206,9 +206,9 @@
 -- | Extract resource name, as required for signing.
 canonicalizeResource :: S3Action -> String
 canonicalizeResource a = bucket ++ uri
-    where uri = "/" ++ (s3object a)
+    where uri = '/' : s3object a
           bucket = case (s3bucket a) of
-                     b@(_:_) -> "/" ++ map toLower b
+                     b@(_:_) -> '/' : map toLower b
                      otherwise -> ""
 
 
@@ -217,15 +217,15 @@
              -> String       -- ^ Date string, in RFC 2616 format
              -> HTTP.HTTPRequest L.ByteString-- ^ Request with date header added
 addDateToReq r d = r {HTTP.rqHeaders =
-                          (HTTP.Header HTTP.HdrDate d) : (HTTP.rqHeaders r)}
+                          HTTP.Header HTTP.HdrDate d : HTTP.rqHeaders r}
 
 -- | Add an expiration date to a request.
 addExpirationToReq :: HTTP.HTTPRequest L.ByteString -> String -> HTTP.HTTPRequest L.ByteString
-addExpirationToReq r e = addHeaderToReq r (HTTP.Header HTTP.HdrExpires e)
+addExpirationToReq r = addHeaderToReq r . HTTP.Header HTTP.HdrExpires
 
 -- | Attach an HTTP header to a request.
 addHeaderToReq :: HTTP.HTTPRequest L.ByteString -> Header -> HTTP.HTTPRequest L.ByteString
-addHeaderToReq r h = r {HTTP.rqHeaders = h : (HTTP.rqHeaders r)}
+addHeaderToReq r h = r {HTTP.rqHeaders = h : HTTP.rqHeaders r}
 
 -- | Get hostname to connect to. Needed for european buckets
 s3Hostname :: S3Action -> String
@@ -281,18 +281,18 @@
     let c = (s3conn a)
         srv = (awsHost c)
         pt = (show (awsPort c))
-        accessKeyQuery = "AWSAccessKeyId=" ++ (awsAccessKey c)
+        accessKeyQuery = "AWSAccessKeyId=" ++ awsAccessKey c
         beginQuery = case (s3query a) of
                   "" -> "?"
                   x -> x ++ "&"
-        expireQuery = "Expires=" ++ (show e)
-        toSign = "GET\n\n\n" ++ (show e) ++ "\n/" ++ (s3bucket a) ++ "/" ++ (s3object a)
-        sigQuery = "Signature=" ++ (urlEncode (makeSignature c toSign))
+        expireQuery = "Expires=" ++ show e
+        toSign = "GET\n\n\n" ++ show e ++ "\n/" ++ s3bucket a ++ "/" ++ s3object a
+        sigQuery = "Signature=" ++ urlEncode (makeSignature c toSign)
         q = beginQuery ++ accessKeyQuery ++ "&" ++
                 expireQuery ++ "&" ++ sigQuery
     in URI { uriScheme = "http:",
-             uriAuthority = Just (URIAuth "" srv (":" ++ pt)),
-             uriPath = "/" ++ (s3bucket a) ++ "/" ++ (s3object a),
+             uriAuthority = Just (URIAuth "" srv (':' : pt)),
+             uriPath = "/" ++ s3bucket a ++ "/" ++ s3object a,
              uriQuery = q,
              uriFragment = ""
            }
@@ -302,8 +302,8 @@
 --   Amazon error messages.
 --   We need the original action in case we get a 307 (temporary redirect)
 createAWSResult :: S3Action -> Result (HTTPResponse L.ByteString) -> IO (AWSResult (HTTPResponse L.ByteString))
-createAWSResult a b = either (handleError) (handleSuccess) b
-    where handleError e = return (Left (NetworkError e))
+createAWSResult a b = either handleError handleSuccess b
+    where handleError = return . Left . NetworkError
           handleSuccess s = case (rspCode s) of
                               (2,_,_) -> return (Right s)
                               -- temporary redirect
@@ -341,7 +341,6 @@
 
 --- mime header encoding
 mimeEncodeQP, mimeDecode :: String -> String
-
 -- | Decode a mime string, we know about quoted printable and base64 encoded UTF-8
 -- S3 may convert quoted printable to base64
 mimeDecode a
diff --git a/Network/AWS/S3Bucket.hs b/Network/AWS/S3Bucket.hs
--- a/Network/AWS/S3Bucket.hs
+++ b/Network/AWS/S3Bucket.hs
@@ -91,7 +91,7 @@
     in
     do res <- Auth.runAction (S3Action aws bucket "" "" [] (L.pack constraint) PUT)
        -- throw away the server response, return () on success
-       return (either (Left) (\_ -> Right ()) res)
+       return (either Left (\_ -> Right ()) res)
 
 -- | Create a new bucket on S3 with the given name.
 createBucket :: AWSConnection -- ^ AWS connection information
@@ -107,7 +107,7 @@
 getBucketLocation aws bucket =
     do res <- Auth.runAction (S3Action aws bucket "?location" "" [] L.empty GET)
        case res of
-         Left x -> do return (Left x)
+         Left x -> return (Left x)
          Right y -> do bs <- parseBucketLocationXML (L.unpack (rspBody y))
                        return (Right bs)
 
@@ -121,7 +121,7 @@
 
 processLocation :: ArrowXml a => a (Data.Tree.NTree.TypeDefs.NTree XNode) String
 processLocation = (text <<< atTag "LocationConstraint")
-                    >>> arr (\x -> x)
+                    >>> arr id
 
 -- | Delete a bucket with the given name on S3.  The bucket must be
 --   empty for deletion to succeed.
@@ -130,7 +130,7 @@
              -> IO (AWSResult ()) -- ^ Server response
 deleteBucket aws bucket =
     do res <- Auth.runAction (S3Action aws bucket "" "" [] L.empty DELETE)
-       return (either (Left) (\_ -> Right ()) res)
+       return (either Left (\_ -> Right ()) res)
 
 -- | Empty a bucket of all objects.  Iterates through all objects
 --   issuing delete commands, so time is proportional to number of
@@ -164,7 +164,7 @@
 listBuckets aws =
     do res <- Auth.runAction (S3Action aws "" "" "" [] L.empty GET)
        case res of
-         Left x -> do return (Left x)
+         Left x -> return (Left x)
          Right y -> do bs <- parseBucketListXML (L.unpack (rspBody y))
                        return (Right bs)
 
@@ -186,10 +186,10 @@
                 }
 
 instance Show ListRequest where
-    show x = "prefix=" ++ (urlEncode (prefix x)) ++ "&" ++
-             "marker=" ++ (urlEncode (marker x)) ++ "&" ++
-             "delimiter=" ++ (urlEncode (delimiter x)) ++ "&" ++
-             "max-keys=" ++ (show (max_keys x))
+    show x = "prefix=" ++ urlEncode (prefix x) ++ "&" ++
+             "marker=" ++ urlEncode (marker x) ++ "&" ++
+             "delimiter=" ++ urlEncode (delimiter x) ++ "&" ++
+             "max-keys=" ++ show (max_keys x)
 
 -- | Result from listing objects.
 data ListResult =
@@ -213,9 +213,9 @@
             -> IO (AWSResult (IsTruncated, [ListResult])) -- ^ Server response
 listObjects aws bucket lreq =
     do res <- Auth.runAction (S3Action aws bucket ""
-                                           ("?" ++ (show lreq)) [] L.empty GET)
+                                           ('?' : show lreq) [] L.empty GET)
        case res of
-         Left x -> do return (Left x)
+         Left x -> return (Left x)
          Right y -> do let objs = L.unpack (rspBody y)
                        tr <- isListTruncated objs
                        lr <- getListResults objs
@@ -230,9 +230,9 @@
     do let lp_max = lp {max_keys = 1000}
        res <- listObjects aws bucket lp_max
        case res of
-         Left x -> do return (Left x)
+         Left x -> return (Left x)
          Right y -> case y of
-                      (True,lr) -> do let last_result = (key . head . reverse) lr
+                      (True,lr) -> do let last_result = (key . last) lr
                                       next_set <- listAllObjects aws bucket
                                                   (lp_max {marker = last_result})
                                       either (\x -> return (Left x))
@@ -266,7 +266,7 @@
                       (text <<< atTag "LastModified") &&&
                       (text <<< atTag "ETag") &&&
                       (text <<< atTag "Size")) >>>
-                     arr (\(a,(b,(c,d))) -> ListResult a b (unquote . HTTP.urlDecode $ c) (read d))
+                     arr (\(a,(b,(c,d))) -> ListResult a b ((unquote . HTTP.urlDecode) c) (read d))
 
 -- | Remove quote characters from a 'String'.
 unquote :: String -> String
diff --git a/Network/AWS/S3Object.hs b/Network/AWS/S3Object.hs
--- a/Network/AWS/S3Object.hs
+++ b/Network/AWS/S3Object.hs
@@ -55,9 +55,9 @@
                               (obj_name obj)
                               ""
                               (("Content-Type", (content_type obj)) :
-                               (obj_headers obj))
+                               obj_headers obj)
                               (obj_data obj) PUT)
-       return (either (Left) (\_ -> Right ()) res)
+       return (either Left (\_ -> Right ()) res)
 
 -- | Create a pre-signed request URI.  Anyone can use this to request
 --   an object until the specified date.
@@ -67,7 +67,7 @@
                              --   00:00:00 UTC on January 1, 1970
                   -> URI -- ^ URI for the object
 publicUriUntilTime c obj time =
-    let act = S3Action c (obj_bucket obj) (obj_name obj) "" [] (L.empty) GET
+    let act = S3Action c (obj_bucket obj) (obj_name obj) "" [] L.empty GET
     in preSignedURI act time
 
 -- | Create a pre-signed request URI.  Anyone can use this to request
@@ -103,8 +103,8 @@
                                            (obj_name obj)
                                            ""
                                            (obj_headers obj)
-                                           (L.empty) m)
-       return (either (Left) (\x -> Right (populate_obj_from x)) res)
+                                           L.empty m)
+       return (either Left (\x -> Right (populate_obj_from x)) res)
            where
              populate_obj_from x =
                  obj { obj_data = (rspBody x),
@@ -128,6 +128,6 @@
                                                               ""
                                                               (obj_headers obj)
                                                               L.empty DELETE)
-                          return (either (Left) (\_ -> Right ()) res)
+                          return (either Left (\_ -> Right ()) res)
 
 
diff --git a/README b/README
--- a/README
+++ b/README
@@ -1,47 +1,5 @@
-DESCRIPTION
-
 This is the Haskell S3 library (hS3).  It provides an interface to
 Amazon's Simple Storage Service, allowing Haskell developers to
 reliably store and retrieve arbitrary amounts of data from anywhere on
 the Internet.  To learn more about Amazon S3, and sign up for an
-account, visit <http://aws.amazon.com/s3>.
-
-REQUIREMENTS
-
-* Glasgow Haskell Compiler (GHC).  Other compilers are untested.
-
-* Haskell HTTP Library <http://www.haskell.org/http/>, at least
-  version 4000.0 is required.
-
-* Haskell Cryptographic Library <http://www.haskell.org/crypto/>, at
-  least version 4.1.0 is required.
-
-* Dataenc library <http://hackage.haskell.org/cgi-bin/hackage-scripts/package/dataenc-0.10.2>.
-
-* UTF8-String library <http://code.haskell.org/utf8-string/>.
-
-* Haskell XML Toolbox (HXT) <http://www.fh-wedel.de/~si/HXmlToolbox/>,
-  tested with version 7.3, earlier versions may work.
-
-INSTALLATION
-
-* Configure:
-
-$ runhaskell Setup.hs configure
-
-* Compile:
-
-$ runhaskell Setup.hs build
-
-* Install (as root):
-
-# runhaskell Setup.hs install
-
-ACKNOWLEDGEMENTS
-
-* Authors and contributors to the HTTP, Crypto, and HXT projects.
-
-* Jinesh Varia (Amazon Web Services Evangelist).
-
-* <harmen@millionmonkeys.nl> for UTF-8 header compatibility and support for EU buckets.
-
+account, visit [http://aws.amazon.com/s3].
diff --git a/Tests.hs b/Tests.hs
--- a/Tests.hs
+++ b/Tests.hs
@@ -79,9 +79,11 @@
             -> t           -- ^ Value to return on failure
             -> (b -> IO t) -- ^ Assertions to run on success
             -> IO t
-failOnError r f d = do either (\x -> do assertFailure (show x)
-                                        return f)
-                          (\x -> do d x) r
+failOnError r f d = either
+                    (\x ->
+                         do assertFailure (show x)
+                            return f)
+                    (\x -> d x) r
 
 testCreateBucket :: AWSConnection -> IO String
 testCreateBucket c =
@@ -100,7 +102,9 @@
 testGetBucketLocation :: AWSConnection -> String -> String -> IO ()
 testGetBucketLocation c bucket expectedLocation =
     do r <- getBucketLocation c bucket
-       failOnError r () (\x -> do assertEqual ("Bucket in the " ++ expectedLocation) expectedLocation x)
+       failOnError r () (\x ->
+                         assertEqual ("Bucket in the " ++ expectedLocation)
+                                     expectedLocation x)
 
 testSendObject :: AWSConnection -> S3Object -> IO ()
 testSendObject c o =
@@ -155,11 +159,10 @@
 -- test if a bucket is not present
 testBucketGone :: AWSConnection -> String -> IO ()
 testBucketGone c bucket =
-    do r <- getBucketLocation c bucket
+    getBucketLocation c bucket >>=
        either (\(AWSError code msg) -> assertEqual "Bucket is gone" "NotFound" code)
               (\x -> do assertFailure "Bucket still there, should be gone"
                         return ())
-              r
 
 getConn = do mConn <- amazonS3ConnectionFromEnv
              return (fromJust mConn)
@@ -169,5 +172,5 @@
 headersToIgnore = ["x-amz-id-2", "x-amz-request-id"]
 
 realMetadata :: [(String, b)] -> [(String, b)]
-realMetadata = filter (\x -> (fst x) `notElem` headersToIgnore)
+realMetadata = filter (\x -> fst x `notElem` headersToIgnore)
 
diff --git a/examples/listBuckets.hs b/examples/listBuckets.hs
--- a/examples/listBuckets.hs
+++ b/examples/listBuckets.hs
@@ -26,4 +26,4 @@
 main = do mConn <- amazonS3ConnectionFromEnv
           let conn = fromJust mConn
           res <- listBuckets conn
-          either (putStrLn . show) (mapM_ (putStrLn . bucket_name)) res
+          either print (mapM_ (putStrLn . bucket_name)) res
diff --git a/examples/listObjects.hs b/examples/listObjects.hs
--- a/examples/listObjects.hs
+++ b/examples/listObjects.hs
@@ -28,12 +28,12 @@
           mConn <- amazonS3ConnectionFromEnv
           let conn = fromJust mConn
           res <- listAllObjects conn bucket (ListRequest "" ""  "" 1000)
-          putStrLn (show (ListRequest "" ""  "" 1000))
+          print (ListRequest "" ""  "" 1000)
           either (putStrLn . prettyReqError)
                  (\x -> do putStrLn ("Key list from bucket " ++
                                      bucket ++
                                      " has been retrieved.  Key/Etag follows:")
-                           mapM_ (\x -> putStrLn ((key x) ++
+                           mapM_ (\x -> putStrLn (key x ++
                                                   " " ++
-                                                  (etag x))) x
+                                                  etag x)) x
                  ) res
diff --git a/examples/preSignedRequest.hs b/examples/preSignedRequest.hs
new file mode 100644
--- /dev/null
+++ b/examples/preSignedRequest.hs
@@ -0,0 +1,31 @@
+#!/usr/local/bin/runhaskell
+
+-----------------------------------------------------------------------------
+-- |
+-- Program     :  PreSigned Request
+-- Copyright   :  (c) Greg Heartsfield 2007
+-- License     :  BSD3
+--
+-- Generate a pre-signed object request, suitable for sending to a 3rd party.
+-- Usage:
+--    preSignedRequest.hs bucket-name object-name seconds-request-valid
+--
+-- This requires the following environment variables to be set with
+-- your Amazon keys:
+--   AWS_ACCESS_KEY_ID
+--   AWS_ACCESS_KEY_SECRET
+-----------------------------------------------------------------------------
+
+import Network.AWS.S3Object
+import Network.AWS.AWSConnection
+import System.Environment
+import Data.Maybe
+import qualified Data.ByteString.Lazy.Char8 as L
+
+main = do argv <- getArgs
+          let bucket : key : seconds : xs = argv
+          mConn <- amazonS3ConnectionFromEnv
+          let conn = fromJust mConn
+          let obj = S3Object bucket key "" [] L.empty
+          uri <- (publicUriForSeconds conn obj (read seconds))
+          print uri
diff --git a/hS3.cabal b/hS3.cabal
--- a/hS3.cabal
+++ b/hS3.cabal
@@ -1,13 +1,13 @@
 Name:           hS3
-Version:        0.4
+Version:        0.5
 License:        BSD3
 License-file:   LICENSE
-Cabal-Version: >= 1.2
+Cabal-Version: >= 1.6
 Copyright: 
   Copyright (c) 2008, Greg Heartsfield
 Author:         Greg Heartsfield <scsibug@imap.cc>
 Maintainer:     Greg Heartsfield <scsibug@imap.cc>
-Homepage:       http://gregheartsfield.com/repos/hS3/doc/
+Homepage:       http://gregheartsfield.com/hS3/
 Category:       Network, Web
 Stability:      Alpha
 build-type:     Simple
@@ -25,6 +25,10 @@
                     examples/getObject.hs
                     examples/listObjects.hs
                     examples/uploadFile.hs
+
+source-repository head
+  type:     darcs
+  location: http://gregheartsfield.com/repos/hS3/
 
 Library
 
diff --git a/hS3.hs b/hS3.hs
--- a/hS3.hs
+++ b/hS3.hs
@@ -36,14 +36,15 @@
     ["go", bucket, gkey ] ->
         do c <- withConn $ \g -> getObject g $ S3Object bucket gkey "" [] L.empty
            L.putStr $ obj_data c
+    ["do", bucket, key] ->
+        do withConn $ \g -> deleteObject g $ S3Object bucket key "" [] L.empty
     ["so", bucket, skey ] ->
         (\c ->  withConn $ \g -> sendObject g $ S3Object bucket skey "" [] c)
             =<< L.getContents
     ["los", bucket] ->
         do l <- withConn $ \g -> listObjects g bucket (ListRequest "" "" "" 1000)
-           print l
-    ["lbs"] -> do l <- withConn listBuckets
-                  print l
+           mapM_ (putStrLn . key) (snd l)
+    ["lbs"] -> withConn listBuckets >>= mapM_ (putStrLn . bucket_name)
     _ -> usage
 
 usage :: IO ()
