1 -- This file is part of Intricacy
2 -- Copyright (C) 2013 Martin Bays <mbays@sdf.org>
4 -- This program is free software: you can redistribute it and/or modify
5 -- it under the terms of version 3 of the GNU General Public License as
6 -- published by the Free Software Foundation, or any later version.
8 -- You should have received a copy of the GNU General Public License
9 -- along with this program. If not, see http://www.gnu.org/licenses/.
11 {-# LANGUAGE LambdaCase #-}
15 import Control
.Applicative
16 import Control
.Concurrent
17 import Control
.Concurrent
.STM
19 import Control
.Monad
.Catch
20 import Control
.Monad
.Trans
.Reader
22 import qualified Data
.ByteString
as BS
23 import qualified Data
.ByteString
.Lazy
as BL
25 import Network
.Simple
.TCP
(connect
, recv
, send
)
26 import System
.Directory
27 import System
.FilePath
36 data FetchedRecord
= FetchedRecord
{fresh
:: Bool, fetchError
:: Maybe String, fetchedRC
:: Maybe RecordContents
}
40 getRecordCached
:: ServerAddr
-> Maybe Auth
-> Maybe (TVar
Bool) -> Bool -> Record
-> IO (TVar FetchedRecord
)
41 getRecordCached saddr _ _ _ _ | nullSaddr saddr
= do
42 newTVarIO
(FetchedRecord
True (Just
"No server set.") Nothing
)
43 getRecordCached saddr auth mflag cOnly rec
= do
44 fromCache
<- withCache saddr
$ getRecord rec
45 let fresh
= isJust fromCache
&& invariantRecord rec
46 tvar
<- newTVarIO
(FetchedRecord fresh Nothing fromCache
)
47 unless (cOnly || fresh
) $ void
$ forkIO
$ getRecordFromServer fromCache tvar
50 getRecordFromServer fromCache tvar
= do
51 let action
= case rec
of
53 let curVersion
= (\(RCUserInfo
(v
,_
)) -> v
) <$> fromCache
54 in GetUserInfo name curVersion
56 resp
<- makeRequest saddr
(ClientRequest
57 protocolVersion
(if needsAuth action
then auth
else Nothing
) action
)
59 ServerError err
-> tellRec
$ FetchedRecord
True (Just err
) fromCache
60 ServerCodenameFree
-> tellRec
$ FetchedRecord
True Nothing Nothing
61 ServerFresh
-> tellRec
$ FetchedRecord
True Nothing fromCache
62 ServedUserInfoDeltas deltas
-> do
63 let Just
(RCUserInfo
(v
,info
)) = fromCache
64 let rc
= RCUserInfo
(v
+length deltas
, applyDeltas info deltas
)
65 withCache saddr
$ putRecord rec rc
66 tellRec
$ FetchedRecord
True Nothing
(Just rc
)
68 let rc
= rcOfServerResp resp
69 withCache saddr
$ putRecord rec rc
70 tellRec
$ FetchedRecord
True Nothing
(Just rc
)
72 tellRec fr
= atomically
$ do
74 case mflag
of {Just flag
-> writeTVar flag
True; _
-> return ()}
76 waitFetchedFresh
:: TVar FetchedRecord
-> IO ()
77 waitFetchedFresh tvar
= atomically
$ readTVar tvar
>>= check
.fresh
79 makeRequest
:: ServerAddr
-> ClientRequest
-> IO ServerResponse
80 makeRequest saddr _ | nullSaddr saddr
=
81 return $ ServerError
"No server set."
82 makeRequest saddr
@(ServerAddr host port
) request
=
83 handle
(return . ServerError
. (show::SomeException
-> String)) $ do
84 connect host
(show port
) makeRequest
'
85 `catchIO`
const (return $ ServerError
$ "Cannot connect to "++saddrStr saddr
++"!")
87 makeRequest
' (sock
,_
) = do
88 send sock
. BL
.toStrict
$ encode request
89 decode
. BL
.fromStrict
<$> recvAll sock
91 recv sock
4096 >>= \case
92 Nothing
-> return BS
.empty
93 Just b
-> BS
.append b
<$> recvAll sock
96 knownServers
:: IO [ServerAddr
]
97 knownServers
= ignoreIOErr
$ do
98 cachedir
<- confFilePath
"cache"
99 saddrstrs
<- getDirectoryContents cachedir
>>= filterM (\dir
->
100 doesFileExist $ cachedir
++[pathSeparator
]++dir
++[pathSeparator
]++"serverInfo")
101 return $ mapMaybe strToSaddr saddrstrs
103 withCache
:: ServerAddr
-> DBM a
-> IO a
104 withCache saddr m
= do
105 cachedir
<- (++pathSeparator
: saddrPath saddr
) <$> confFilePath
"cache"
106 runReaderT m cachedir