packages feed

ginsu-0.8.0: Gale/Puff.hs

{-# LANGUAGE TypeSynonymInstances, FlexibleInstances, GeneralizedNewtypeDeriving #-}
module Gale.Puff(
    Puff(..),
    Fragment(..),
    Signature(..),
    Key(..),
    FragmentList,
    HasFragmentList(..),
    Category(Category),
    readPuffs, writePuffs, emptyPuff,
    categoryHead, categoryCell,
    catParseNew, catShowNew,
    subCategory, fragmentData,
    getFragmentString, getFragmentStrings, getFragmentForceStrings, showPuff, showKey, showFragments,
    fragmentString, mergeFrags, getFragmentData, getFragmentTime,
    getAuthor, getSigner, hasFragment, hasFragmentString, emptyKey,
    getAllFragmentForceStrings,
    showSignature,
    f_keySigned,
    f_keyRedirect,
    f_keyMember,
    f_keyExpires,
    f_rsaModulus,
    f_rsaPrivateExponent,
    f_rsaPrivateCoefficient,
    f_rsaPrivatePrime,
    f_rsaPrivatePrimeExponent,
    f_rsaExponent,
    f_rsaBits,
    f_keyOwner,
    f_messageSender,
    f_securitySignature,
    f_messageBody,
    f_messageKeyword,
    f_idTime,
    f_messageId,
    f_questionReceipt,
    f_outGone,
    f_noticePresence,
    f_noticePresence',
    f_securityEncryption,
    f_questionKey,
    f_questionKey',
    f_answerKey',
    f_answerKeyError'
    ) where

import Atom
import Data.Binary
import EIO
import ErrorLog
import GenUtil
import Int(Int32)
import List
import Maybe(isJust)
import PackedString
import System.IO
import System.Time
import RSA

import qualified Data.ByteString.Lazy as LBS
import qualified Data.ByteString as BS

newtype Category = Category (String,String)
    deriving(Binary,Eq,Ord)

instance Show Category where
    show c = catShowNew c

type FragmentList = [(Atom,Fragment)]

data Puff = Puff {
    cats :: [Category],
    signature :: [Signature],
    fragments :: FragmentList
    }
data Fragment =
    FragmentText !PackedString
    | FragmentData !BS.ByteString
    | FragmentTime !ClockTime
    | FragmentInt !Int32
    | FragmentNest FragmentList

data Signature =
    Unverifyable Key
    | Signed Key
    | Encrypted [String]

data Key = Key String FragmentList

emptyKey n = Key n [( f_keyMember, FragmentText nilPS)]

instance Eq Key where
    (==) (Key kn _) (Key kn' _) = kn == kn'
instance Ord Key where
    compare (Key kn _) (Key kn' _) = compare kn kn'


f_answerKeyError' = fromString "answer/key/error"
f_answerKey' = fromString "answer/key"
f_idTime = fromString "id/time"
f_keyExpires = fromString "key.expires"
f_keyMember = fromString "key.member"
f_keyOwner = fromString "key.owner"
f_keyRedirect = fromString "key.redirect"
f_keySigned = fromString "key.signed"
f_messageBody = fromString "message/body"
f_messageId = fromString "message.id"
f_messageKeyword = fromString "message.keyword"
f_messageSender = fromString "message/sender"
f_noticePresence = fromString "notice.presence"
f_noticePresence' = fromString "notice/presence"
f_outGone = fromString "out.gone"
f_questionKey = fromString "question.key"
f_questionKey' = fromString "question/key"
f_questionReceipt = fromString "question.receipt"
f_rsaBits = fromString "rsa.bits"
f_rsaExponent = fromString "rsa.exponent"
f_rsaModulus = fromString "rsa.modulus"
f_rsaPrivateCoefficient = fromString "rsa.private.coefficient"
f_rsaPrivateExponent = fromString "rsa.private.exponent"
f_rsaPrivatePrimeExponent = fromString "rsa.private.prime.exponent"
f_rsaPrivatePrime  = fromString "rsa.private.prime"
f_securityEncryption = fromString "security/encryption"
f_securitySignature = fromString "security/signature"

--------------------
-- Category Routines
--------------------

subCategory :: Category -> Category -> Bool
Category (x1,y1) `subCategory` Category (x2,y2) = x2 `isPrefixOf` x1 && y2 `isSuffixOf` y1

catParseNew :: String -> Category
catParseNew cs = Category (c,(drop 1) d) where
    (c,d) = span (/= '@') cs


catShowNew :: Category -> String
catShowNew (Category (x,"")) = x
catShowNew (Category (x,y)) = x ++ "@" ++ y

---------------------
-- working with puffs
---------------------

getAuthor p = maybe "unknown" id (getSigner p)

getSigner p = ss (signature p) where
	    ss (Signed (Key n _):_) = Just n
	    ss (Unverifyable (Key n _):_) = Just n
	    ss (_:sig) = ss sig
	    ss [] = Nothing

showSignature (Signed (Key n _)) = "Signed by " ++ n
showSignature (Unverifyable (Key n _)) = "Signed by " ++ n ++ " (unverified)"
showSignature (Encrypted xs) = "Encrypted to " ++ concatInter ", " xs

emptyPuff = Puff {cats = [], signature = [], fragments = []}

fragmentData :: Monad m => Atom -> FragmentList -> m BS.ByteString
fragmentData s fl = case lookup s fl of
    Just (FragmentData xs) -> return xs
    _ -> fail $ "fragment not found: " ++ toString s

fragmentString :: Monad m => Atom -> FragmentList -> m PackedString
fragmentString s fl = case lookup s fl of
    Just (FragmentText xs) -> return xs
    _ -> fail $ "fragment not found: " ++ toString s

mergeFrags :: FragmentList -> FragmentList -> FragmentList
mergeFrags fla flb = fla ++ [f|f@(s,_) <- flb, s `notElem` fsts fla]


{-
getFragmentString :: Monad m => Puff -> String -> m String
getFragmentString (Puff {fragments = frags}) s = case lookup s frags of
    Just (FragmentText s) -> return s
    _ -> fail $ "fragment not found: " ++ s
-}

getFragmentStrings :: HasFragmentList fl => fl -> Atom -> [PackedString]
getFragmentStrings fl s = [v|(n,FragmentText v) <- getFragmentList fl, n == s]

getFragmentForceStrings :: HasFragmentList fl => fl -> Atom -> [PackedString]
getFragmentForceStrings fl s = concatMap f [v | (n,v) <- getFragmentList fl, n == s] where
    f (FragmentText t) = [t]
    f (FragmentData _) = []
    f (FragmentTime t) = [packString (show t)]
    f (FragmentInt i) = [packString (show i)]
    f (FragmentNest fl) = getFragmentForceStrings fl s

getAllFragmentForceStrings :: HasFragmentList fl => fl -> [PackedString]
getAllFragmentForceStrings fl  = concatMap f [v | (_,v) <- getFragmentList fl] where
    f (FragmentText t) = [t]
    f (FragmentData _) = []
    f (FragmentTime t) = [packString (show t)]
    f (FragmentInt i) = [packString (show i)]
    f (FragmentNest fl) = getAllFragmentForceStrings fl

showPuff (Puff {cats = s, fragments =  fl, signature = sigs}) = unwords (map catShowNew s) ++ "\n" ++ unlines (map showSignature sigs) ++ indentLines 2 (showFragments fl)
showFragments fl = concatMap (\(n,f) -> toString n ++ ": " ++ show f ++ "\n") fl

instance Show Puff where
    show = showPuff

instance Show Key where
    show = showKey

instance Show Fragment where
    show (FragmentText s) = show s
    --show (FragmentData xs) = "<DATA:" ++ show (rangeSize $ bounds xs)  ++ ":" ++ show (map (chr . fromIntegral) $ elems xs) ++ ">"
    show (FragmentData xs) = "<DATA:" ++ show (BS.length xs)  ++ ":" ++ bsToHex (sha1 $ LBS.fromChunks [xs]) ++ ">"
    show (FragmentTime ct) = show ct
    show (FragmentInt x) = show x
    show (FragmentNest fl) = "Nest:" ++ (indentLines 2 $ showFragments fl)


showKey :: Key -> String
showKey (Key n fl) = "Key: " ++ n ++ "\n" ++ (indentLines 4 $ showFragments fl)

getFragmentData :: (Monad m,HasFragmentList fl) => fl -> Atom -> m BS.ByteString
getFragmentData fl s = case [xs | (s',FragmentData xs) <- getFragmentList fl, s' == s] of
    (s:_) -> return s
    [] -> fail $ "data fragment not found: " ++ toString s

getFragmentString :: (Monad m, HasFragmentList fl)  => fl -> Atom -> m PackedString
getFragmentString fl s = case [xs | (s',FragmentText xs) <- getFragmentList fl, s' == s] of
    (s:_) -> return s
    [] -> fail $ "text fragment not found: " ++ toString s

hasFragmentString fl s = isJust (getFragmentString fl s)

hasFragment fl s = s `elem` fsts (getFragmentList fl)

getFragmentTime :: (Monad m, HasFragmentList fl)  => fl -> Atom -> m ClockTime
getFragmentTime fl s = case [xs | (s',FragmentTime xs) <- getFragmentList fl, s' == s] of
    (s:_) -> return s
    [] -> fail $ "time fragment not found: " ++ toString s

class HasFragmentList a where
    getFragmentList :: a -> FragmentList

instance HasFragmentList FragmentList where
    getFragmentList = id

instance HasFragmentList Puff where
    getFragmentList (Puff {fragments = fl}) = fl

instance HasFragmentList Key where
    getFragmentList (Key _ fl) = fl

instance HasFragmentList (Atom,Fragment) where
    getFragmentList f = [f]

instance HasFragmentList Fragment where
    getFragmentList (FragmentNest fl) = fl
    getFragmentList _ = []

-- flattenFragmentList :: HasFragmentList fl => fl -> FragmentList

----------------
-- Puff Pickling
----------------

writePuffs :: String -> [Puff] -> IO ()
writePuffs fn ps = attempt $ putFile fn ps

readPuffs :: String -> IO [Puff]
readPuffs fn = tryElse [] $ getFile fn


putFile fn a = atomicWrite fn $ \h -> do
    hSetBuffering h (BlockBuffering Nothing)
    LBS.hPut h (encode a)

getFile :: Binary a => FilePath -> IO a
getFile fn = decodeFile fn


categoryHead (Category (h,_)) = h
categoryCell (Category (_,c)) = c


{- Generated by DrIFT (Automatic class derivations for Haskell) -}
{-* Generated by DrIFT : Look, but Don't Touch. *-}

instance Data.Binary.Binary Puff where
    put (Puff aa ab ac) = do
	    Data.Binary.put aa
	    Data.Binary.put ab
	    Data.Binary.put ac
    get = do
    aa <- get
    ab <- get
    ac <- get
    return (Puff aa ab ac)

instance Data.Binary.Binary Fragment where
    put (FragmentText aa) = do
	    Data.Binary.putWord8 0
	    Data.Binary.put aa
    put (FragmentData ab) = do
	    Data.Binary.putWord8 1
	    Data.Binary.put ab
    put (FragmentTime ac) = do
	    Data.Binary.putWord8 2
	    Data.Binary.put ac
    put (FragmentInt ad) = do
	    Data.Binary.putWord8 3
	    Data.Binary.put ad
    put (FragmentNest ae) = do
	    Data.Binary.putWord8 4
	    Data.Binary.put ae
    get = do
	    h <- Data.Binary.getWord8
	    case h of
	      0 -> do
		    aa <- Data.Binary.get
		    return (FragmentText aa)
	      1 -> do
		    ab <- Data.Binary.get
		    return (FragmentData ab)
	      2 -> do
		    ac <- Data.Binary.get
		    return (FragmentTime ac)
	      3 -> do
		    ad <- Data.Binary.get
		    return (FragmentInt ad)
	      4 -> do
		    ae <- Data.Binary.get
		    return (FragmentNest ae)
	      _ -> fail "invalid binary data found"

instance Data.Binary.Binary Signature where
    put (Unverifyable aa) = do
	    Data.Binary.putWord8 0
	    Data.Binary.put aa
    put (Signed ab) = do
	    Data.Binary.putWord8 1
	    Data.Binary.put ab
    put (Encrypted ac) = do
	    Data.Binary.putWord8 2
	    Data.Binary.put ac
    get = do
	    h <- Data.Binary.getWord8
	    case h of
	      0 -> do
		    aa <- Data.Binary.get
		    return (Unverifyable aa)
	      1 -> do
		    ab <- Data.Binary.get
		    return (Signed ab)
	      2 -> do
		    ac <- Data.Binary.get
		    return (Encrypted ac)
	      _ -> fail "invalid binary data found"

instance Data.Binary.Binary Key where
    put (Key aa ab) = do
	    Data.Binary.put aa
	    Data.Binary.put ab
    get = do
    aa <- get
    ab <- get
    return (Key aa ab)

instance Binary ClockTime where
    put (TOD a b) = put a >> put b
    get = do
        x <- get
        y <- get
        return (TOD x y)

--  Imported from other files :-