packages feed

curl-cookiejar (empty) → 0.1.0.0

raw patch · 9 files changed

+449/−0 lines, 9 filesdep +attoparsecdep +basedep +bytestringsetup-changed

Dependencies added: attoparsec, base, bytestring, conduit, conduit-extra, http-client, time

Files

+ ChangeLog.md view
@@ -0,0 +1,6 @@+# Changelog for curl-cookiejar++## 0.1++### 0.1.0.0+* Initial version
+ LICENSE view
@@ -0,0 +1,19 @@+Copyright (c) 2019 Plow Technologies++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.
+ README.md view
@@ -0,0 +1,5 @@+# `curl-cookiejar`: Read cURL/wget cookie jar files++This library provides parsers and pretty-printers which interface the `Cookie`+`CookieJar` types defined by the `http-client` Haskell library with cookie jar+files written and read buy the cURL and wget command line utilities.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ curl-cookiejar.cabal view
@@ -0,0 +1,48 @@+-- This file has been generated from package.yaml by hpack version 0.28.2.+--+-- see: https://github.com/sol/hpack+--+-- hash: 468b6900ea75fad340b259122f0afde0ec31969ba43a9b17b8a3c0aad88f5062++name:           curl-cookiejar+version:        0.1.0.0+synopsis:       Parsing and pretty-printing of cURL/wget cookie jars+description:    See README.md+category:       Web+homepage:       https://github.com/plow-technologies/curl-cookiejar#readme+bug-reports:    https://github.com/plow-technologies/curl-cookiejar/issues+author:         Edward Amsden+maintainer:     edward.amsden@plowtech.net+copyright:      2019 Plow Technologies+license:        MIT+license-file:   LICENSE+build-type:     Simple+cabal-version:  >= 1.10+extra-source-files:+    ChangeLog.md+    README.md++source-repository head+  type: git+  location: https://github.com/plow-technologies/curl-cookiejar++library+  exposed-modules:+      Data.CURL.CookieJar+      Data.CURL.CookieJar.Conduit+      Data.CURL.CookieJar.Parser+      Data.CURL.CookieJar.PrettyPrinter+  other-modules:+      Paths_curl_cookiejar+  hs-source-dirs:+      src+  ghc-options: -Wall -Wincomplete-uni-patterns -Wincomplete-record-updates+  build-depends:+      attoparsec+    , base >=4.10 && <5+    , bytestring+    , conduit+    , conduit-extra+    , http-client+    , time+  default-language: Haskell2010
+ src/Data/CURL/CookieJar.hs view
@@ -0,0 +1,18 @@+module Data.CURL.CookieJar +  ( readCookieJarFile+  , writeCookieJarFile+  , CookieJarHeader(..)+  ) where++import Conduit (runResourceT, runConduit)+import Data.CURL.CookieJar.Conduit+import Data.CURL.CookieJar.PrettyPrinter+import Network.HTTP.Client (CookieJar)++readCookieJarFile :: FilePath -> IO CookieJar+readCookieJarFile path = runResourceT $ runConduit $ readCookieJarFileC path++writeCookieJarFile :: CookieJarHeader -> FilePath -> CookieJar -> IO ()+writeCookieJarFile header path jar = runResourceT $ runConduit $ writeCookieJarFileC header jar path++
+ src/Data/CURL/CookieJar/Conduit.hs view
@@ -0,0 +1,186 @@+{-|+Conduit sources/sinks for Netscape/Mozilla cookie jar format.+|-}+module Data.CURL.CookieJar.Conduit+  (+  -- * Parsing cookie jars+    parseCookiesC+  , parseCookiesEitherC+  , sinkCookieJar+  , sinkCookieJarEither+  , sourceCookieJarCookies+  -- * Pretty-printing cookie jars+  , prettyCookieC+  , prettyCookiesC+  , prettyCookieJarC+  -- * Handle and file IO+  , readCookieJarFileC+  , readCookieJarHandleC+  , sourceCookiesFile+  , sourceCookiesHandle+  , sinkCookiesFile+  , sinkCookiesHandle+  , writeCookieJarFileC+  , writeCookieJarHandleC+  ) where++import Data.ByteString (ByteString)+import Conduit+  ( MonadThrow+  , PrimMonad+  , MonadIO+  , MonadResource+  , mapC+  , ConduitT+  , (.|)+  , yield+  , sourceFile+  , sourceHandle+  , sinkFile+  , sinkHandle+  )+import System.IO (Handle)+import Data.Conduit.List (sourceList, consume)+import Data.Conduit.ByteString.Builder (builderToByteString)+import Data.Conduit.Attoparsec+  ( ParseError, conduitParser, conduitParserEither, sinkParser, sinkParserEither)+import Data.CURL.CookieJar.Parser (cookieParser, cookieJarParser)+import Data.CURL.CookieJar.PrettyPrinter (CookieJarHeader, prettyCookie, prettyCookieJarHeader)+import Network.HTTP.Client (Cookie, CookieJar, createCookieJar, destroyCookieJar)++-- | Parse cookies from a cookie jar+--+-- Note that this does not skip the header+parseCookiesC :: MonadThrow m => ConduitT ByteString Cookie m ()+parseCookiesC = conduitParser cookieParser .| mapC snd++-- | Parse cookies from a cookie jar, returning errors as values+parseCookiesEitherC :: Monad m => ConduitT ByteString (Either ParseError Cookie) m ()+parseCookiesEitherC =  conduitParserEither cookieParser .| mapC (fmap snd)++-- | Parse cookies into a cookie jar, and return the value+sinkCookieJar :: MonadThrow m => ConduitT ByteString o m CookieJar+sinkCookieJar = sinkParser cookieJarParser++-- | Parse cookies into a cookie jar, and return the value, or a parse error+sinkCookieJarEither :: Monad m => ConduitT ByteString o m (Either ParseError CookieJar)+sinkCookieJarEither = sinkParserEither cookieJarParser++-- | Pretty print a single cookie and stream out a chunked ByteString+prettyCookieC+  :: PrimMonad m+  => Cookie+  -> ConduitT i ByteString m ()+prettyCookieC cookie =+  (yield $ prettyCookie cookie) .| builderToByteString++-- | Stream in cookies to pretty-print, along with a header+prettyCookiesC+  :: PrimMonad m+  => CookieJarHeader+  -> ConduitT Cookie ByteString m ()+prettyCookiesC header =+  cookiesAndHeader .| builderToByteString+  where+    cookiesAndHeader = do+      yield $ prettyCookieJarHeader header+      mapC prettyCookie++-- | Pretty-print a cookie jar+prettyCookieJarC+  :: PrimMonad m+  => CookieJarHeader+  -> CookieJar+  -> ConduitT i ByteString m ()+prettyCookieJarC header jar =+  sourceCookieJarCookies jar .| prettyCookiesC header++-- | Read a cookie jar from a file+readCookieJarFileC+  :: MonadThrow m+  => MonadResource m+  => FilePath+  -> ConduitT i o m CookieJar+readCookieJarFileC path = sourceCookiesFile path .| sinkCookieJarCookies++-- | Read a cookie jar from a handle+readCookieJarHandleC+  :: MonadIO m+  => MonadThrow m+  => Handle+  -> ConduitT i o m CookieJar+readCookieJarHandleC handle = sourceCookiesHandle handle .| sinkCookieJarCookies++-- | Source cookies from a cookie jar+sourceCookieJarCookies+  :: Monad m+  => CookieJar+  -> ConduitT i Cookie m ()+sourceCookieJarCookies =+  sourceList . destroyCookieJar++-- | Make a cookie jar from a finite stream of cookies+sinkCookieJarCookies+  :: Monad m+  => ConduitT Cookie o m CookieJar+sinkCookieJarCookies = createCookieJar <$> consume++-- | Source cookies from a cookie jar file+sourceCookiesFile+  :: MonadThrow m+  => MonadResource m+  => FilePath+  -> ConduitT i Cookie m ()+sourceCookiesFile path =+  sourceFile path .| parseCookiesC++-- | Source cookies from a handle+sourceCookiesHandle+  :: MonadThrow m+  => MonadIO m+  => Handle+  -> ConduitT i Cookie m ()+sourceCookiesHandle handle =+  sourceHandle handle .| parseCookiesC++-- | Write cookies to a file+sinkCookiesFile+  :: MonadResource m+  => PrimMonad m+  => CookieJarHeader+  -> FilePath+  -> ConduitT Cookie o m ()+sinkCookiesFile header path =+  prettyCookiesC header .| sinkFile path++-- | Write cookies to a handle+sinkCookiesHandle+  :: MonadIO m+  => PrimMonad m+  => CookieJarHeader+  -> Handle+  -> ConduitT Cookie o m ()+sinkCookiesHandle header handle =+  prettyCookiesC header .| sinkHandle handle++-- | Write a cookie jar to a file+writeCookieJarFileC+  :: MonadResource m+  => PrimMonad m+  => CookieJarHeader+  -> CookieJar+  -> FilePath+  -> ConduitT i o m ()+writeCookieJarFileC header jar path =+  sourceCookieJarCookies jar .| sinkCookiesFile header path++writeCookieJarHandleC+  :: MonadIO m+  => PrimMonad m+  => CookieJarHeader+  -> CookieJar+  -> Handle+  -> ConduitT i o m ()+writeCookieJarHandleC header jar handle =+  sourceCookieJarCookies jar .| sinkCookiesHandle header handle+
+ src/Data/CURL/CookieJar/Parser.hs view
@@ -0,0 +1,82 @@+{-# LANGUAGE ApplicativeDo #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE OverloadedStrings #-}+{-| Attoparsec 'Parser' for a cookie jar, and convenience function to run it |-}+module Data.CURL.CookieJar.Parser+  ( cookieJarParser+  , cookieParser+  , parseCookieJar+  ) where++import Control.Applicative ((<|>))+import Control.Monad (void)+import Data.ByteString (ByteString)+import Data.Char (ord)+import Data.Attoparsec.ByteString.Char8+  ( many'+  , endOfLine+  , endOfLine+  , isEndOfLine+  , takeWhile1+  , decimal+  , skipSpace+  , skipWhile+  , char+  , parseOnly+  , try+  , Parser+  )+import Network.HTTP.Client (Cookie(..), CookieJar, createCookieJar)+import Data.Time.Clock.POSIX (posixSecondsToUTCTime)++-- | Parse a cookie jar in the Netscape/Mozilla format+parseCookieJar :: ByteString -> Either String CookieJar+parseCookieJar = parseOnly cookieJarParser++-- | Parser a cookie jar in the Netscape/Mozilla format+cookieJarParser :: Parser CookieJar+cookieJarParser = createCookieJar <$> many' cookieParser++-- | Parser for one cookie/line in a cookie jar in the Netscape/Mozilla format+-- This will also consume any comment lines preceding the cookie line.+--+-- This parser recognizes the magic prefix @#HttpOnly_# and sets the appropriate+-- field in the Cookie datatype+cookieParser :: Parser Cookie+cookieParser = +  skipSpace *> httpOnlyLine <|> commentLine <|> cookieLine+  where+    httpOnlyLine = try $ "#HttpOnly_" *> cookieLineParser True+    commentLine = "#" *> skipWhile notEndOfLine *> endOfLine *> cookieParser+    cookieLine = cookieLineParser False+    cookieLineParser cookie_http_only = do+      let -- these are the fields not represented by the cookie jar format+        cookie_creation_time = epoch+        cookie_last_access_time = epoch+        cookie_persistent = True+      cookie_domain <- stringField+      tab+      cookie_host_only <- boolField+      tab+      cookie_path <- stringField+      tab+      cookie_secure_only <- boolField+      tab+      cookie_expiry_time <- timeField+      tab+      cookie_name <- stringField+      tab+      cookie_value <- lastField+      (endOfLine <|> pure ())+      pure $ Cookie {..}+      where+        tab = void $ char '\t'+        stringField = takeWhile1 (/= '\t')+        boolField = (True <$ "TRUE") <|> (False <$ "FALSE")+        timeField = posixSecondsToUTCTime <$> fromInteger <$> decimal+        lastField = takeWhile1 (notEndOfLine)+        epoch = posixSecondsToUTCTime 0+      ++notEndOfLine :: Char -> Bool+notEndOfLine = not . isEndOfLine . fromIntegral . ord
+ src/Data/CURL/CookieJar/PrettyPrinter.hs view
@@ -0,0 +1,83 @@+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE OverloadedStrings #-}+{-|+Pretty-printer to cookie jar format for the 'Cookie' and 'CookieJar' types+from http-types.++__WARNING__: This is not a full serialization of the 'Cookie' and 'CookieJar'+types. The Netscape cookie jar format does not support all of the fields.++In particular, the following fields of 'Cookie' are not represented:++  * 'cookie_creation_time'+  * 'cookie_last_access_time'+  * 'cookie_persistent'+  * 'cookie_http_only'+|-}   +module Data.CURL.CookieJar.PrettyPrinter+  ( prettyCookieJar+  , CookieJarHeader(..)+  , prettyCookieJarHeader+  , prettyCookie+  -- |+  -- * Re-export+  -- This is re-exported from 'Data.ByteString.Builder' for convenience+  , toLazyByteString+  ) where++import Data.Semigroup ((<>))+import Network.HTTP.Client (Cookie(..), CookieJar, destroyCookieJar)+import Data.ByteString.Builder+  ( Builder+  , byteString+  , integerDec+  , char7+  , toLazyByteString+  )+import Data.Foldable (foldMap)+import Data.Time.Clock.POSIX (utcTimeToPOSIXSeconds)++-- | Specify what header, if any, to print at the top of the cookie jar+--+-- Some other parsers (like Perl's) require the header to be present+data CookieJarHeader+  = NoHeader+  | HTTPHeader+  | NetscapeHeader++-- | Print a cookie jar in the Netscape/Mozilla format, with optional+-- leading header+prettyCookieJar :: CookieJarHeader -> CookieJar -> Builder+prettyCookieJar header jar =+     prettyCookieJarHeader header+  <> foldMap ((<> "\n") . prettyCookie) (destroyCookieJar jar)++-- | Print a cookie jar header+prettyCookieJarHeader :: CookieJarHeader -> Builder+prettyCookieJarHeader NoHeader = mempty+prettyCookieJarHeader HTTPHeader = "# HTTP Cookie File\n"+prettyCookieJarHeader NetscapeHeader = "# Netscape HTTP Cookie File\n"++-- | Print an individual cookie on a single line+prettyCookie :: Cookie -> Builder+prettyCookie (Cookie {..}) =+     byteString cookie_domain+  <> tab+  <> bool cookie_host_only+  <> tab+  <> byteString cookie_path+  <> tab+  <> bool cookie_secure_only+  <> tab+  <> unixTime cookie_expiry_time+  <> tab+  <> byteString cookie_name+  <> tab+  <> byteString cookie_value+  where+    bool True = "TRUE"+    bool False = "FALSE"+    unixTime = integerDec . round . utcTimeToPOSIXSeconds+    tab = char7 '\t'++