summaryrefslogtreecommitdiff
path: root/KeyRing.hs
blob: 7073e43ca19edf936e23f79dbaa9f8f8609ca42e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
{-# LANGUAGE CPP #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE ViewPatterns #-}
module KeyRing where

import System.Environment
import Control.Monad
import Data.Maybe
import Data.Char
import Data.List
import Control.Applicative ( (<$>) )
import System.Directory    ( getHomeDirectory, doesFileExist )
import Control.Arrow       ( first, second )

import DotLock

data HomeDir =
    HomeDir { homevar :: String
            , appdir :: String
            , optfile_alts :: [String]
            }

home = HomeDir
    { homevar = "GNUPGHOME"
    , appdir  = ".gnupg"
    , optfile_alts = ["keys.conf","gpg.conf-2","gpg.conf"]
    }

data InputFile = HomeSec | HomePub | ArgFile FilePath

data KeyRingRuntime = KeyRingRuntime
                        { rtPubring :: FilePath
                        , rtSecring :: FilePath
                        , rtRings :: [FilePath]
                        , rtWallets :: [FilePath]
                        , rtGrip :: Maybe String
                        }

data KeyRingData a = KeyRingData
    { filesToLock :: [InputFile]
    , homeSpec :: Maybe String
    , kaction :: KeyRingRuntime -> IO a
    , keyringFiles :: [FilePath]
    , walletFiles :: [FilePath]
    }

todo = error "unimplemented"

data KikiResult = KikiSuccess | FailedToLock [FilePath]

{-
newtype KeyRing a = KeyRing
    { krAction :: KeyRingData b -> IO a
    }
-}

empty = KeyRingData { filesToLock = []
                    , homeSpec = Nothing
                    , kaction = \KeyRingRuntime {} -> return ()
                    , keyringFiles = []
                    , walletFiles = []
                    }

{-
runKeyRing :: KeyRing () -> IO a
runKeyRing keyring = krAction keyring empty
-}


runKeyRing :: KeyRingData a -> IO KikiResult
runKeyRing keyring = do
    (homedir,secring,pubring,grip0) <- getHomeDir (homeSpec keyring)
    let tolocks = map resolve (filesToLock keyring)
                  where resolve (ArgFile f) = f
                        resolve HomePub     = pubring
                        resolve HomeSec     = secring
    lks <- forM tolocks $ \f -> do
        lk <- dotlock_create f 0
        v <- flip (maybe $ return Nothing) lk $ \lk -> do
                e <- dotlock_take lk (-1)
                if e==0 then return $ Just lk
                        else dotlock_destroy lk >> return Nothing
        return (v,f)
    let (lked, map snd -> failed) = partition (isJust . fst) lks
        ret = if null failed then KikiSuccess else FailedToLock failed

    case ret of
      KikiSuccess -> kaction keyring KeyRingRuntime
                        { rtPubring = pubring
                        , rtSecring = secring
                        , rtRings = secring:pubring:keyringFiles keyring
                        , rtWallets = walletFiles keyring
                        , rtGrip = grip0
                        }
      _ -> return undefined

    forM_ lked $ \(Just lk, fname) -> do dotlock_release lk
                                         dotlock_destroy lk
    return ret

parseOptionFile fname = do
    xs <- fmap lines (readFile fname)
    let ys = filter notComment xs
        notComment ('#':_) = False
        notComment cs      = not (all isSpace cs)
    return ys

getHomeDir protohome = do
        homedir <- envhomedir protohome
        flip (maybe (error "Could not determine home directory."))
             homedir $ \homedir -> do
        -- putStrLn $ "homedir = " ++show homedir
        let secring = homedir ++ "/" ++ "secring.gpg"
            pubring = homedir ++ "/" ++ "pubring.gpg"
        -- putStrLn $ "secring = " ++ show secring
        workingkey <- getWorkingKey homedir
        return (homedir,secring,pubring,workingkey)
 where
    envhomedir opt = do
        gnupghome <- lookupEnv (homevar home) >>=
                  \d -> return $ d >>= guard . (/="") >> d
        homed <- flip fmap getHomeDirectory $
                  \d -> fmap (const d) $ guard (d/="")
        let homegnupg = (++('/':(appdir home))) <$> homed
        let val = (opt `mplus` gnupghome `mplus` homegnupg)
        return $ val
    
    -- TODO: rename this to getGrip
    getWorkingKey homedir = do
        let o = Nothing
            h = Just homedir
        ofile <- fmap listToMaybe . flip (maybe (return [])) h $ \h ->
                let optfiles = map (second ((h++"/")++))
                                   (maybe optfile_alts' (:[]) o')
                    optfile_alts' = zip (False:repeat True) (optfile_alts home)
                    o' = fmap (False,) o
                in filterM (doesFileExist . snd) optfiles
        args <- flip (maybe $ return []) ofile $
            \(forgive,fname) -> parseOptionFile fname
        let config = map (topair . words) args
                        where topair (x:xs) = (x,xs)
        return $ lookup "default-key" config >>= listToMaybe

#if MIN_VERSION_base(4,6,0)
#else
lookupEnv var =
    handleIO_ (return Nothing) $ fmap Just (getEnv var)
#endif