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 +6/−0
- LICENSE +19/−0
- README.md +5/−0
- Setup.hs +2/−0
- curl-cookiejar.cabal +48/−0
- src/Data/CURL/CookieJar.hs +18/−0
- src/Data/CURL/CookieJar/Conduit.hs +186/−0
- src/Data/CURL/CookieJar/Parser.hs +82/−0
- src/Data/CURL/CookieJar/PrettyPrinter.hs +83/−0
+ 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'++