hs-pkpass 0.4 → 0.6
raw patch · 3 files changed
+95/−59 lines, 3 filesdep +binarydep +conduit-extradep +resourcetdep −filesystem-conduitdep ~aesondep ~conduitdep ~old-localenew-uploaderPVP ok
version bump matches the API change (PVP)
Dependencies added: binary, conduit-extra, resourcet, scientific, shakespeare
Dependencies removed: filesystem-conduit
Dependency ranges changed: aeson, conduit, old-locale, random, shakespeare-text, shelly, text, time, uuid
API changes (from Hackage documentation)
- Passbook.Types: altText :: Barcode -> Maybe Text
- Passbook.Types: altitude :: Location -> Maybe Double
- Passbook.Types: associatedStoreIdentifiers :: Pass -> [Text]
- Passbook.Types: authenticationToken :: WebService -> Text
- Passbook.Types: auxiliaryFields :: PassContent -> [PassField]
- Passbook.Types: backFields :: PassContent -> [PassField]
- Passbook.Types: backgroundColor :: Pass -> Maybe RGBColor
- Passbook.Types: barcode :: Pass -> Maybe Barcode
- Passbook.Types: changeMessage :: PassField -> Maybe Text
- Passbook.Types: currencyCode :: PassField -> Maybe Text
- Passbook.Types: dateStyle :: PassField -> Maybe DateTimeStyle
- Passbook.Types: description :: Pass -> Text
- Passbook.Types: foregroundColor :: Pass -> Maybe RGBColor
- Passbook.Types: format :: Barcode -> BarcodeFormat
- Passbook.Types: headerFields :: PassContent -> [PassField]
- Passbook.Types: instance Eq Alignment
- Passbook.Types: instance Eq Barcode
- Passbook.Types: instance Eq BarcodeFormat
- Passbook.Types: instance Eq DateTimeStyle
- Passbook.Types: instance Eq Location
- Passbook.Types: instance Eq NumberStyle
- Passbook.Types: instance Eq Pass
- Passbook.Types: instance Eq PassContent
- Passbook.Types: instance Eq PassField
- Passbook.Types: instance Eq PassType
- Passbook.Types: instance Eq PassValue
- Passbook.Types: instance Eq RGBColor
- Passbook.Types: instance Eq RelevantDate
- Passbook.Types: instance Eq TransitType
- Passbook.Types: instance Eq WebService
- Passbook.Types: instance FromJSON Alignment
- Passbook.Types: instance FromJSON Barcode
- Passbook.Types: instance FromJSON BarcodeFormat
- Passbook.Types: instance FromJSON DateTimeStyle
- Passbook.Types: instance FromJSON Location
- Passbook.Types: instance FromJSON NumberStyle
- Passbook.Types: instance FromJSON Pass
- Passbook.Types: instance FromJSON PassContent
- Passbook.Types: instance FromJSON PassField
- Passbook.Types: instance FromJSON PassType
- Passbook.Types: instance FromJSON PassValue
- Passbook.Types: instance FromJSON RGBColor
- Passbook.Types: instance FromJSON RelevantDate
- Passbook.Types: instance FromJSON TransitType
- Passbook.Types: instance Ord Alignment
- Passbook.Types: instance Ord Barcode
- Passbook.Types: instance Ord BarcodeFormat
- Passbook.Types: instance Ord DateTimeStyle
- Passbook.Types: instance Ord Location
- Passbook.Types: instance Ord NumberStyle
- Passbook.Types: instance Ord Pass
- Passbook.Types: instance Ord PassContent
- Passbook.Types: instance Ord PassField
- Passbook.Types: instance Ord PassType
- Passbook.Types: instance Ord PassValue
- Passbook.Types: instance Ord RGBColor
- Passbook.Types: instance Ord RelevantDate
- Passbook.Types: instance Ord TransitType
- Passbook.Types: instance Ord WebService
- Passbook.Types: instance Read Alignment
- Passbook.Types: instance Read Barcode
- Passbook.Types: instance Read BarcodeFormat
- Passbook.Types: instance Read DateTimeStyle
- Passbook.Types: instance Read Location
- Passbook.Types: instance Read NumberStyle
- Passbook.Types: instance Read Pass
- Passbook.Types: instance Read PassContent
- Passbook.Types: instance Read PassField
- Passbook.Types: instance Read PassType
- Passbook.Types: instance Read PassValue
- Passbook.Types: instance Read RGBColor
- Passbook.Types: instance Read RelevantDate
- Passbook.Types: instance Read TransitType
- Passbook.Types: instance Read WebService
- Passbook.Types: instance Show Alignment
- Passbook.Types: instance Show Barcode
- Passbook.Types: instance Show BarcodeFormat
- Passbook.Types: instance Show DateTimeStyle
- Passbook.Types: instance Show Location
- Passbook.Types: instance Show NumberStyle
- Passbook.Types: instance Show Pass
- Passbook.Types: instance Show PassContent
- Passbook.Types: instance Show PassField
- Passbook.Types: instance Show PassType
- Passbook.Types: instance Show PassValue
- Passbook.Types: instance Show RGBColor
- Passbook.Types: instance Show RelevantDate
- Passbook.Types: instance Show TransitType
- Passbook.Types: instance Show WebService
- Passbook.Types: instance ToJSON Alignment
- Passbook.Types: instance ToJSON Barcode
- Passbook.Types: instance ToJSON BarcodeFormat
- Passbook.Types: instance ToJSON DateTimeStyle
- Passbook.Types: instance ToJSON Location
- Passbook.Types: instance ToJSON Manifest
- Passbook.Types: instance ToJSON NumberStyle
- Passbook.Types: instance ToJSON Pass
- Passbook.Types: instance ToJSON PassContent
- Passbook.Types: instance ToJSON PassField
- Passbook.Types: instance ToJSON PassType
- Passbook.Types: instance ToJSON PassValue
- Passbook.Types: instance ToJSON RGBColor
- Passbook.Types: instance ToJSON RelevantDate
- Passbook.Types: instance ToJSON TransitType
- Passbook.Types: instance Typeable Alignment
- Passbook.Types: instance Typeable Barcode
- Passbook.Types: instance Typeable BarcodeFormat
- Passbook.Types: instance Typeable DateTimeStyle
- Passbook.Types: instance Typeable Location
- Passbook.Types: instance Typeable NumberStyle
- Passbook.Types: instance Typeable Pass
- Passbook.Types: instance Typeable PassContent
- Passbook.Types: instance Typeable PassField
- Passbook.Types: instance Typeable PassType
- Passbook.Types: instance Typeable PassValue
- Passbook.Types: instance Typeable RGBColor
- Passbook.Types: instance Typeable RelevantDate
- Passbook.Types: instance Typeable TransitType
- Passbook.Types: instance Typeable WebService
- Passbook.Types: isRelative :: PassField -> Maybe Bool
- Passbook.Types: key :: PassField -> Text
- Passbook.Types: label :: PassField -> Maybe Text
- Passbook.Types: labelColor :: Pass -> Maybe Text
- Passbook.Types: latitude :: Location -> Double
- Passbook.Types: locations :: Pass -> [Location]
- Passbook.Types: logoText :: Pass -> Maybe Text
- Passbook.Types: longitude :: Location -> Double
- Passbook.Types: message :: Barcode -> Text
- Passbook.Types: messageEncoding :: Barcode -> Text
- Passbook.Types: numberStyle :: PassField -> Maybe NumberStyle
- Passbook.Types: organizationName :: Pass -> Text
- Passbook.Types: passContent :: Pass -> PassType
- Passbook.Types: passTypeIdentifier :: Pass -> Text
- Passbook.Types: primaryFields :: PassContent -> [PassField]
- Passbook.Types: relevantDate :: Pass -> Maybe RelevantDate
- Passbook.Types: relevantText :: Location -> Maybe Text
- Passbook.Types: secondaryFields :: PassContent -> [PassField]
- Passbook.Types: serialNumber :: Pass -> Text
- Passbook.Types: suppressStripShine :: Pass -> Maybe Bool
- Passbook.Types: teamIdentifier :: Pass -> Text
- Passbook.Types: textAlignment :: PassField -> Maybe Alignment
- Passbook.Types: timeStyle :: PassField -> Maybe DateTimeStyle
- Passbook.Types: value :: PassField -> PassValue
- Passbook.Types: webService :: Pass -> Maybe WebService
- Passbook.Types: webServiceURL :: WebService -> Text
+ Passbook.Types: [altText] :: Barcode -> Maybe Text
+ Passbook.Types: [altitude] :: Location -> Maybe Double
+ Passbook.Types: [associatedStoreIdentifiers] :: Pass -> [Text]
+ Passbook.Types: [authenticationToken] :: WebService -> Text
+ Passbook.Types: [auxiliaryFields] :: PassContent -> [PassField]
+ Passbook.Types: [backFields] :: PassContent -> [PassField]
+ Passbook.Types: [backgroundColor] :: Pass -> Maybe RGBColor
+ Passbook.Types: [barcode] :: Pass -> Maybe Barcode
+ Passbook.Types: [changeMessage] :: PassField -> Maybe Text
+ Passbook.Types: [currencyCode] :: PassField -> Maybe Text
+ Passbook.Types: [dateStyle] :: PassField -> Maybe DateTimeStyle
+ Passbook.Types: [description] :: Pass -> Text
+ Passbook.Types: [expirationDate] :: Pass -> Maybe Text
+ Passbook.Types: [foregroundColor] :: Pass -> Maybe RGBColor
+ Passbook.Types: [format] :: Barcode -> BarcodeFormat
+ Passbook.Types: [headerFields] :: PassContent -> [PassField]
+ Passbook.Types: [isRelative] :: PassField -> Maybe Bool
+ Passbook.Types: [key] :: PassField -> Text
+ Passbook.Types: [labelColor] :: Pass -> Maybe Text
+ Passbook.Types: [label] :: PassField -> Maybe Text
+ Passbook.Types: [latitude] :: Location -> Double
+ Passbook.Types: [locations] :: Pass -> [Location]
+ Passbook.Types: [logoText] :: Pass -> Maybe Text
+ Passbook.Types: [longitude] :: Location -> Double
+ Passbook.Types: [messageEncoding] :: Barcode -> Text
+ Passbook.Types: [message] :: Barcode -> Text
+ Passbook.Types: [numberStyle] :: PassField -> Maybe NumberStyle
+ Passbook.Types: [organizationName] :: Pass -> Text
+ Passbook.Types: [passContent] :: Pass -> PassType
+ Passbook.Types: [passTypeIdentifier] :: Pass -> Text
+ Passbook.Types: [primaryFields] :: PassContent -> [PassField]
+ Passbook.Types: [relevantDate] :: Pass -> Maybe RelevantDate
+ Passbook.Types: [relevantText] :: Location -> Maybe Text
+ Passbook.Types: [secondaryFields] :: PassContent -> [PassField]
+ Passbook.Types: [serialNumber] :: Pass -> Text
+ Passbook.Types: [suppressStripShine] :: Pass -> Maybe Bool
+ Passbook.Types: [teamIdentifier] :: Pass -> Text
+ Passbook.Types: [textAlignment] :: PassField -> Maybe Alignment
+ Passbook.Types: [timeStyle] :: PassField -> Maybe DateTimeStyle
+ Passbook.Types: [value] :: PassField -> PassValue
+ Passbook.Types: [voided] :: Pass -> Maybe Bool
+ Passbook.Types: [webServiceURL] :: WebService -> Text
+ Passbook.Types: [webService] :: Pass -> Maybe WebService
+ Passbook.Types: instance Data.Aeson.Types.FromJSON.FromJSON Passbook.Types.Alignment
+ Passbook.Types: instance Data.Aeson.Types.FromJSON.FromJSON Passbook.Types.Barcode
+ Passbook.Types: instance Data.Aeson.Types.FromJSON.FromJSON Passbook.Types.BarcodeFormat
+ Passbook.Types: instance Data.Aeson.Types.FromJSON.FromJSON Passbook.Types.DateTimeStyle
+ Passbook.Types: instance Data.Aeson.Types.FromJSON.FromJSON Passbook.Types.Location
+ Passbook.Types: instance Data.Aeson.Types.FromJSON.FromJSON Passbook.Types.NumberStyle
+ Passbook.Types: instance Data.Aeson.Types.FromJSON.FromJSON Passbook.Types.Pass
+ Passbook.Types: instance Data.Aeson.Types.FromJSON.FromJSON Passbook.Types.PassContent
+ Passbook.Types: instance Data.Aeson.Types.FromJSON.FromJSON Passbook.Types.PassField
+ Passbook.Types: instance Data.Aeson.Types.FromJSON.FromJSON Passbook.Types.PassType
+ Passbook.Types: instance Data.Aeson.Types.FromJSON.FromJSON Passbook.Types.PassValue
+ Passbook.Types: instance Data.Aeson.Types.FromJSON.FromJSON Passbook.Types.RGBColor
+ Passbook.Types: instance Data.Aeson.Types.FromJSON.FromJSON Passbook.Types.RelevantDate
+ Passbook.Types: instance Data.Aeson.Types.FromJSON.FromJSON Passbook.Types.TransitType
+ Passbook.Types: instance Data.Aeson.Types.ToJSON.ToJSON Passbook.Types.Alignment
+ Passbook.Types: instance Data.Aeson.Types.ToJSON.ToJSON Passbook.Types.Barcode
+ Passbook.Types: instance Data.Aeson.Types.ToJSON.ToJSON Passbook.Types.BarcodeFormat
+ Passbook.Types: instance Data.Aeson.Types.ToJSON.ToJSON Passbook.Types.DateTimeStyle
+ Passbook.Types: instance Data.Aeson.Types.ToJSON.ToJSON Passbook.Types.Location
+ Passbook.Types: instance Data.Aeson.Types.ToJSON.ToJSON Passbook.Types.Manifest
+ Passbook.Types: instance Data.Aeson.Types.ToJSON.ToJSON Passbook.Types.NumberStyle
+ Passbook.Types: instance Data.Aeson.Types.ToJSON.ToJSON Passbook.Types.Pass
+ Passbook.Types: instance Data.Aeson.Types.ToJSON.ToJSON Passbook.Types.PassContent
+ Passbook.Types: instance Data.Aeson.Types.ToJSON.ToJSON Passbook.Types.PassField
+ Passbook.Types: instance Data.Aeson.Types.ToJSON.ToJSON Passbook.Types.PassType
+ Passbook.Types: instance Data.Aeson.Types.ToJSON.ToJSON Passbook.Types.PassValue
+ Passbook.Types: instance Data.Aeson.Types.ToJSON.ToJSON Passbook.Types.RGBColor
+ Passbook.Types: instance Data.Aeson.Types.ToJSON.ToJSON Passbook.Types.RelevantDate
+ Passbook.Types: instance Data.Aeson.Types.ToJSON.ToJSON Passbook.Types.TransitType
+ Passbook.Types: instance GHC.Classes.Eq Passbook.Types.Alignment
+ Passbook.Types: instance GHC.Classes.Eq Passbook.Types.Barcode
+ Passbook.Types: instance GHC.Classes.Eq Passbook.Types.BarcodeFormat
+ Passbook.Types: instance GHC.Classes.Eq Passbook.Types.DateTimeStyle
+ Passbook.Types: instance GHC.Classes.Eq Passbook.Types.Location
+ Passbook.Types: instance GHC.Classes.Eq Passbook.Types.NumberStyle
+ Passbook.Types: instance GHC.Classes.Eq Passbook.Types.Pass
+ Passbook.Types: instance GHC.Classes.Eq Passbook.Types.PassContent
+ Passbook.Types: instance GHC.Classes.Eq Passbook.Types.PassField
+ Passbook.Types: instance GHC.Classes.Eq Passbook.Types.PassType
+ Passbook.Types: instance GHC.Classes.Eq Passbook.Types.PassValue
+ Passbook.Types: instance GHC.Classes.Eq Passbook.Types.RGBColor
+ Passbook.Types: instance GHC.Classes.Eq Passbook.Types.RelevantDate
+ Passbook.Types: instance GHC.Classes.Eq Passbook.Types.TransitType
+ Passbook.Types: instance GHC.Classes.Eq Passbook.Types.WebService
+ Passbook.Types: instance GHC.Classes.Ord Passbook.Types.Alignment
+ Passbook.Types: instance GHC.Classes.Ord Passbook.Types.Barcode
+ Passbook.Types: instance GHC.Classes.Ord Passbook.Types.BarcodeFormat
+ Passbook.Types: instance GHC.Classes.Ord Passbook.Types.DateTimeStyle
+ Passbook.Types: instance GHC.Classes.Ord Passbook.Types.Location
+ Passbook.Types: instance GHC.Classes.Ord Passbook.Types.NumberStyle
+ Passbook.Types: instance GHC.Classes.Ord Passbook.Types.Pass
+ Passbook.Types: instance GHC.Classes.Ord Passbook.Types.PassContent
+ Passbook.Types: instance GHC.Classes.Ord Passbook.Types.PassField
+ Passbook.Types: instance GHC.Classes.Ord Passbook.Types.PassType
+ Passbook.Types: instance GHC.Classes.Ord Passbook.Types.PassValue
+ Passbook.Types: instance GHC.Classes.Ord Passbook.Types.RGBColor
+ Passbook.Types: instance GHC.Classes.Ord Passbook.Types.RelevantDate
+ Passbook.Types: instance GHC.Classes.Ord Passbook.Types.TransitType
+ Passbook.Types: instance GHC.Classes.Ord Passbook.Types.WebService
+ Passbook.Types: instance GHC.Read.Read Passbook.Types.Alignment
+ Passbook.Types: instance GHC.Read.Read Passbook.Types.Barcode
+ Passbook.Types: instance GHC.Read.Read Passbook.Types.BarcodeFormat
+ Passbook.Types: instance GHC.Read.Read Passbook.Types.DateTimeStyle
+ Passbook.Types: instance GHC.Read.Read Passbook.Types.Location
+ Passbook.Types: instance GHC.Read.Read Passbook.Types.NumberStyle
+ Passbook.Types: instance GHC.Read.Read Passbook.Types.Pass
+ Passbook.Types: instance GHC.Read.Read Passbook.Types.PassContent
+ Passbook.Types: instance GHC.Read.Read Passbook.Types.PassField
+ Passbook.Types: instance GHC.Read.Read Passbook.Types.PassType
+ Passbook.Types: instance GHC.Read.Read Passbook.Types.PassValue
+ Passbook.Types: instance GHC.Read.Read Passbook.Types.RGBColor
+ Passbook.Types: instance GHC.Read.Read Passbook.Types.RelevantDate
+ Passbook.Types: instance GHC.Read.Read Passbook.Types.TransitType
+ Passbook.Types: instance GHC.Read.Read Passbook.Types.WebService
+ Passbook.Types: instance GHC.Show.Show Passbook.Types.Alignment
+ Passbook.Types: instance GHC.Show.Show Passbook.Types.Barcode
+ Passbook.Types: instance GHC.Show.Show Passbook.Types.BarcodeFormat
+ Passbook.Types: instance GHC.Show.Show Passbook.Types.DateTimeStyle
+ Passbook.Types: instance GHC.Show.Show Passbook.Types.Location
+ Passbook.Types: instance GHC.Show.Show Passbook.Types.NumberStyle
+ Passbook.Types: instance GHC.Show.Show Passbook.Types.Pass
+ Passbook.Types: instance GHC.Show.Show Passbook.Types.PassContent
+ Passbook.Types: instance GHC.Show.Show Passbook.Types.PassField
+ Passbook.Types: instance GHC.Show.Show Passbook.Types.PassType
+ Passbook.Types: instance GHC.Show.Show Passbook.Types.PassValue
+ Passbook.Types: instance GHC.Show.Show Passbook.Types.RGBColor
+ Passbook.Types: instance GHC.Show.Show Passbook.Types.RelevantDate
+ Passbook.Types: instance GHC.Show.Show Passbook.Types.TransitType
+ Passbook.Types: instance GHC.Show.Show Passbook.Types.WebService
- Passbook.Types: Pass :: Text -> Text -> Text -> Text -> Text -> [Text] -> [Location] -> Maybe RelevantDate -> Maybe Barcode -> Maybe RGBColor -> Maybe RGBColor -> Maybe Text -> Maybe Text -> Maybe Bool -> Maybe WebService -> PassType -> Pass
+ Passbook.Types: Pass :: Maybe Text -> Maybe Bool -> Text -> Text -> Text -> Text -> Text -> [Text] -> [Location] -> Maybe RelevantDate -> Maybe Barcode -> Maybe RGBColor -> Maybe RGBColor -> Maybe Text -> Maybe Text -> Maybe Bool -> Maybe WebService -> PassType -> Pass
Files
- Passbook.hs +39/−16
- Passbook/Types.hs +33/−25
- hs-pkpass.cabal +23/−18
Passbook.hs view
@@ -1,5 +1,6 @@ {-# LANGUAGE ExtendedDefaultRules #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE PackageImports #-} {-# OPTIONS_GHC -fno-warn-type-defaults #-} @@ -58,18 +59,23 @@ import Control.Monad (liftM) import Control.Monad.IO.Class (liftIO) import Data.Aeson+import Data.Binary (Word32)+import Data.Bits (shiftR, (.&.)) import qualified Data.ByteString.Lazy as LB+import Data.Char (intToDigit) import Data.Conduit import Data.Conduit.Binary hiding (sinkFile)-import Data.Conduit.Filesystem+import Data.Conduit.Combinators (sinkFile)+import Control.Monad.Trans.Resource import qualified Data.Text as ST import Data.Text.Lazy (Text) import qualified Data.Text.Lazy as LT import Data.UUID import Filesystem.Path (filename)-import Filesystem.Path.CurrentOS (encodeString)+import Filesystem.Path.CurrentOS (encodeString, decodeString) import Passbook.Types import Prelude hiding (FilePath)+import Shelly (FilePath) import Shelly import System.Directory (doesFileExist) import System.Random@@ -139,31 +145,32 @@ let tmp = passOut </> passId lazyId = LT.fromStrict passId cp_r passIn tmp- liftIO $ renderPass (tmp </> "pass.json") pass { serialNumber = passId }+ liftIO $ renderPass (tmp </> ((Shelly.fromText "pass.json") :: FilePath)) pass { serialNumber = passId } signcmd lazyId tmp passOut rm_rf tmp- return (passOut </> LT.append lazyId ".pkpass")+ return (passOut </> LT.unpack (LT.append lazyId ".pkpass")) -- |Helper function to generate a hash genHash :: FilePath -> Sh (Text, Text) genHash file = do rawhash <- run "openssl" ["sha1", toTextIgnore file]- let hash = LT.drop 1 $ LT.dropWhile (/= ' ') rawhash- return (toTextIgnore $ filename file, LT.filter (/= '\n') hash)+ let hash = LT.drop 1 $ LT.dropWhile (/= ' ') (LT.fromStrict rawhash)+ return (LT.fromStrict (toTextIgnore $ encodeString $ filename (decodeString file)), LT.filter (/= '\n') hash) -- |Render JSON and put it in a file saveJSON :: ToJSON a => a -> FilePath -> IO ()-saveJSON json path = LB.writeFile (LT.unpack $ toTextIgnore path) $ encode json+saveJSON json path = LB.writeFile (ST.unpack $ toTextIgnore path) $ encode json -- |Helper function to sign the manifest sslSign :: FilePath -- ^ Certificate -> FilePath -- ^ Key -> FilePath -- ^ Temporary directory containing manifest.json- -> Sh Text+ -> Sh ST.Text sslSign cert key tmp = run "openssl" [ "smime", "-binary" , "-sign" , "-signer", toTextIgnore cert+ , "-certfile", "wwdr.pem" , "-inkey" , toTextIgnore key , "-in", "manifest.json" , "-out", "signature"@@ -230,20 +237,36 @@ let tmp = passOut </> passId passFile = LT.append (LT.fromStrict $ passId) ".pkpass" cp_r passIn tmp- liftIO $ renderPass (tmp </> "pass.json") (pass { serialNumber = passId })+ liftIO $ renderPass (tmp </> Shelly.fromText ("pass.json" :: ST.Text)) (pass { serialNumber = passId }) cd tmp manifest <- liftM Manifest $ pwd >>= ls >>= mapM genHash- liftIO $ saveJSON manifest (tmp </> "manifest.json")+ liftIO $ saveJSON manifest (tmp </> ("manifest.json" :: FilePath)) sslSign cert key tmp- files <- liftM (map (toTextIgnore . filename)) $ ls =<< pwd- run "zip" ((toTextIgnore $ passOut </> passFile) : files)+ files <- liftM (map (\f -> toTextIgnore (encodeString $ filename (decodeString f)))) $ ls =<< pwd+ run "zip" ((toTextIgnore $ passOut </> (LT.unpack passFile)) : files) rm_rf tmp- return (passOut </> passFile)+ return (passOut </> (LT.unpack passFile)) -- |Generates a random UUID for a Pass using "Data.UUID" and "System.Random" genPassId :: IO ST.Text-genPassId = liftM (ST.pack . toString) randomIO+genPassId = liftM (ST.pack . showPassId) randomIO +-- |Shows a UUID without the hyphens+showPassId :: UUID -> String+showPassId uuid = let (w0, w1, w2, w3) = toWords uuid+ in hexw w0 $ hexw' w1 $ hexw' w2 $ hexw w3 ""+ where hexw :: Word32 -> String -> String+ hexw w s = hexn w 28 : hexn w 24 : hexn w 20 : hexn w 16+ : hexn w 12 : hexn w 8 : hexn w 4 : hexn w 0 : s++ hexw' :: Word32 -> String -> String+ hexw' w s = hexn w 28 : hexn w 24 : hexn w 20 : hexn w 16+ : hexn w 12 : hexn w 8 : hexn w 4 : hexn w 0 : s++ hexn :: Word32 -> Int -> Char+ hexn w r = intToDigit $ fromIntegral ((w `shiftR` r) .&. 0xf)++ -- |Render and store a pass.json at the desired location. renderPass :: FilePath -> Pass -> IO () renderPass path pass =@@ -257,7 +280,7 @@ -> Sh () signcmd uuid assetFolder passOut = run_ "signpass" [ "-p", toTextIgnore assetFolder -- The input folder- , "-o", toTextIgnore $ passOut </> LT.append uuid ".pkpass" ] -- Name of the output file+ , "-o", toTextIgnore $ passOut </> LT.unpack (LT.append uuid ".pkpass") ] -- Name of the output file -- |Tries to parse the pass.json file contained in a .pkpass into a valid -- 'Pass'. If Passbook accepts the .pkpass file, this function should never@@ -265,7 +288,7 @@ loadPass :: FilePath -- ^ Location of the .pkpass file -> IO (Maybe Pass) loadPass path = do- archive <- liftM toArchive $ LB.readFile $ encodeString path+ archive <- liftM toArchive $ LB.readFile $ path case findEntryByPath "pass.json" archive of Nothing -> return Nothing Just pass -> return $ decode $ fromEntry pass
Passbook/Types.hs view
@@ -5,6 +5,7 @@ {-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE TemplateHaskell #-}+{-# OPTIONS_GHC -fdefer-type-errors #-} {- |This module provides types and functions for type-safe generation of PassBook's @pass.json@ files. @@ -49,10 +50,12 @@ import qualified Data.HashMap.Strict as HM import Data.Text (Text, pack, unpack) import qualified Data.Text.Lazy as LT+import Data.String (fromString) import Data.Time import Data.Typeable-import System.Locale+import System.Locale hiding (iso8601DateFormat, timeFmt, defaultTimeLocale) import Text.Shakespeare.Text+import Data.Scientific (floatingOrInteger) -- | Auxiliary type to ensure that field values are rendered correctly data PassValue = PassInt Integer@@ -102,11 +105,11 @@ deriving (Eq, Ord, Show, Read, Typeable) -- |Pass field date/time display style-data DateTimeStyle = None -- ^ Corresponds to @NSDateFormatterNoStyle@- | Short -- ^ Corresponds to @NSDateFormatterShortStyle@- | Medium -- ^ Corresponds to @NSDateFormatterMediumStyle@- | Long -- ^ Corresponds to @NSDateFormatterLongStyle@- | Full -- ^ Corresponds to @NSDateFormatterFullStyle@+data DateTimeStyle = None -- ^ Corresponds to @PKDateStyleNone@+ | Short -- ^ Corresponds to @PKDateStyleShort@+ | Medium -- ^ Corresponds to @PKDateStyleMedium@+ | Long -- ^ Corresponds to @PKDateStyleLong@+ | Full -- ^ Corresponds to @PKDateStyleFull@ deriving (Eq, Ord, Show, Read, Typeable) -- |Pass field number display style@@ -169,7 +172,9 @@ -- |A complete pass data Pass = Pass { -- Required keys- description :: Text -- ^ Brief description of the pass (required)+ expirationDate :: Maybe Text+ , voided :: Maybe Bool+ , description :: Text -- ^ Brief description of the pass (required) , organizationName :: Text -- ^ Display name of the organization that signed the pass (required) , passTypeIdentifier :: Text -- ^ Pass type identifier, as issued by Apple (required) , serialNumber :: Text -- ^ Unique serial number for the pass (required)@@ -201,7 +206,7 @@ instance ToJSON Manifest where toJSON (Manifest files) =- let pairs = map (\(f, h) -> LT.toStrict f .= h) files+ let pairs = map (\(f, h) -> (fromString $ unpack $ LT.toStrict f) .= h) files in object pairs -- * JSON instances@@ -210,9 +215,9 @@ -- because Passbook can't deal with null values in JSON. (-:) :: ToJSON a => Text -> Maybe a -> [Pair] -> [Pair] (-:) _ Nothing = id-(-:) key (Just value) = ((key .= value) :)+(-:) key (Just value) = (((fromString $ unpack $ key) .= value) :) -$(deriveToJSON id ''PassContent)+$(deriveToJSON defaultOptions ''PassContent) instance ToJSON Location where toJSON Location{..} =@@ -248,6 +253,8 @@ toJSON Pass{..} = let pairs = ("relevantDate" -: relevantDate) $ ("barcode" -: barcode)+ $ ("expirationDate" -: expirationDate)+ $ ("voided" -: voided) $ ("backgroundColor" -: backgroundColor) $ ("foregroundColor" -: foregroundColor) $ ("labelColor" -: labelColor)@@ -263,7 +270,7 @@ , "teamIdentifier" .= teamIdentifier , "associatedStoreIdentifiers" .= associatedStoreIdentifiers , "locations" .= locations- , passTypeName passContent .= passContent]+ , (fromString $ unpack $ passTypeName passContent) .= passContent] in object pairs -- |Internal helper function to handle Boarding Passes correctly.@@ -313,11 +320,11 @@ toJSON Natural = toJSON ("PKTextAlignment" :: Text) instance ToJSON DateTimeStyle where- toJSON None = toJSON ("NSDateFormatterNoStyle" :: Text)- toJSON Short = toJSON ("NSDateFormatterShortStyle" :: Text)- toJSON Medium = toJSON ("NSDateFormatterMediumStyle" :: Text)- toJSON Long = toJSON ("NSDateFormatterLongStyle" :: Text)- toJSON Full = toJSON ("NSDateFormatterFullStyle" :: Text)+ toJSON None = toJSON ("PKDateStyleNone" :: Text)+ toJSON Short = toJSON ("PKDateStyleShort" :: Text)+ toJSON Medium = toJSON ("PKDateStyleMedium" :: Text)+ toJSON Long = toJSON ("PKDateStyleLong" :: Text)+ toJSON Full = toJSON ("PKDateStyleFull" :: Text) instance ToJSON NumberStyle where toJSON Decimal = toJSON ("PKNumberStyleDecimal" :: Text)@@ -350,7 +357,7 @@ -- |Helper function that parses a 'UTCTime' out of a Text parseJsonDate :: Text -> Maybe UTCTime-parseJsonDate = parseTime defaultTimeLocale timeFormat . unpack+parseJsonDate = parseTimeM True defaultTimeLocale timeFormat . unpack -- * Implementing FromJSON @@ -365,11 +372,11 @@ instance FromJSON DateTimeStyle where parseJSON (String t) = case t of- "NSDateFormatterNoStyle" -> pure None- "NSDateFormatterShortStyle" -> pure Short- "NSDateFormatterMediumStyle" -> pure Medium- "NSDateFormatterLongStyle" -> pure Long- "NSDateFormatterFullStyle" -> pure Full+ "PKDateStyleNone" -> pure None+ "PKDateStyleShort" -> pure Short+ "PKDateStyleMedium" -> pure Medium+ "PKDateStyleLong" -> pure Long+ "PKDateStyleFull" -> pure Full _ -> fail "Could not parse date formatting style" parseJSON _ = mzero @@ -417,8 +424,9 @@ parseJSON _ = mzero instance FromJSON PassValue where- parseJSON (Number (I i)) = pure $ PassInt i- parseJSON (Number (D d)) = pure $ PassDouble d+ parseJSON (Number num) = case floatingOrInteger num of+ Left d -> pure $ PassDouble d+ Right i -> pure $ PassInt i parseJSON (String t) = case parseJsonDate t of Just d -> pure $ PassDate d Nothing -> pure $ PassText t@@ -445,7 +453,7 @@ Nothing -> fail "Could not parse relevant date" parseJSON _ = mzero -$(deriveFromJSON id ''PassContent)+$(deriveFromJSON defaultOptions ''PassContent) -- |Tries to parse a web service parseWebService :: Maybe Text -> Maybe Text -> Maybe WebService
hs-pkpass.cabal view
@@ -1,42 +1,47 @@--- Initial hs-pkpass.cabal generated by cabal init. For further +-- Initial hs-pkpass.cabal generated by cabal init. For further -- documentation, see http://haskell.org/cabal/users-guide/ name: hs-pkpass-version: 0.4+version: 0.6 synopsis: A library for Passbook pass creation & signing description: A Haskell library for type-safe creation of Passbook passes and signing through Apple's signpass tool. homepage: https://github.com/tazjin/hs-pkpass license: BSD3 license-file: LICENSE author: Vincent Ambo-maintainer: geva@humac.com--- copyright: +maintainer: marc@digitallyinduced.com+-- copyright: category: Apple build-type: Simple-cabal-version: >=1.8+cabal-version: >=1.10 source-repository head type: git- location: https://github.com/tazjin/hs-pkpass.git+ location: https://github.com/digitallyinduced/hs-pkpass.git library+ default-language: Haskell2010 exposed-modules: Passbook, Passbook.Types- -- other-modules: + -- other-modules: build-depends: base >= 4 && < 5, directory >= 1 && < 2,- aeson ==0.6.*,- conduit ==0.5.*,- filesystem-conduit ==0.5.*,- text ==0.11.*,- uuid ==1.2.*,- shelly ==0.14.*,- random == 1.0.*,- time ==1.4.*,- old-locale ==1.0.*,- shakespeare-text ==1.0.*,+ aeson >= 0.6,+ conduit >= 0.5,+ conduit-extra,+ resourcet,+ shakespeare,+ text >= 0.11,+ uuid >= 1.2,+ shelly >= 0.14,+ random >= 1.0,+ time >= 1,+ old-locale >= 1.0,+ shakespeare-text >= 1.0, attoparsec >= 0.8.6.1, unordered-containers >= 0.2, zip-archive >= 0.1.1.8, system-filepath < 0.5, bytestring,- transformers >= 0.3+ transformers >= 0.3,+ binary >= 0.5,+ scientific