summaryrefslogtreecommitdiffstats
path: root/Regfish.hs
blob: f56f59b5ef0b742298168df7a415b88df1f1ba6b (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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE DeriveDataTypeable #-}


module Regfish
    ( dump
    , login
    , list
    , add
    , edit
    , del
    , RRData(..)
    ) where

import Control.Lens
import Control.Monad.IO.Class
import Data.Default
import qualified Data.Acid as A
import qualified Data.ByteString.Char8 as BS8
import qualified Data.ByteString.Lazy.Char8 as LBS8
import qualified Data.List as L
import qualified Network.HTTP.Client as C
import qualified Network.Wreq as W
import qualified Network.Wreq.Types as W (Postable)
import Regfish.AcidState
import Regfish.Default ()
import Regfish.Parser
import Regfish.Types
import Text.Printf


loginUrl :: Url
loginUrl = "https://www.regfish.de/my/login"


sessionCookieName :: BS8.ByteString
sessionCookieName = "rf51"


sessionToken :: C.CookieJar -> Maybe SessionToken
sessionToken =
    fmap C.cookie_value .
    L.find ((==sessionCookieName) . C.cookie_name) .
    C.destroyCookieJar


responseSessionToken :: Getter (W.Response body) (Maybe SessionToken)
responseSessionToken =
    W.responseCookieJar . to sessionToken

dump :: IO ()
dump =
    withLocalState def $ \qa -> do
        print =<< A.query qa DumpQuery


login :: IO ()
login =
    withLocalState def $ \qa -> do

        cj <- A.query qa CookieJarQuery

        putStrLn $ "session token: " <> show (sessionToken cj)

        putStrLn $ "Logging in..."

        res <- liftIO $ W.post loginUrl
            [ "u" W.:= _username def
            , "p" W.:= _password def
            ]

        putStrLn $ "session token: " <> show (res ^. responseSessionToken)

        A.update qa $ CookieJarUpdate $ res ^. W.responseCookieJar


list :: IO ()
list =
    regfishGet (_allinone def) >>= \case
        Left err -> error err
        Right r -> do
            let body = r ^. W.responseBody
            if isLoginPage $ LBS8.unpack body
                then do
                    -- TODO login and try again (once)
                    error "not logged in"
                else do
                    mapM_ pr =<< readZoneIO body
  where
    pr (RR rrid rrname rrttl x) = case x of
        A rrdata        -> f "A"     rrid rrname rrttl 0 rrdata
        AAAA rrdata     -> f "AAAA"  rrid rrname rrttl 0 rrdata
        ALIAS rrdata    -> f "ALIAS" rrid rrname rrttl 0 rrdata
        CNAME rrdata    -> f "CNAME" rrid rrname rrttl 0 rrdata
        MX rraux rrdata -> f "MX"    rrid rrname rrttl rraux rrdata
        NS rrdata       -> f "NS"    rrid rrname rrttl 0 rrdata
        TXT rrdata      -> f "TXT"   rrid rrname rrttl 0 rrdata
        SRV rrdata      -> f "SRV"   rrid rrname rrttl 0 rrdata

    f :: String -> String -> String -> Integer -> Integer -> String -> IO ()
    f rrtype rrid rrname rrttl rraux rrdata =
        printf "%-8s %-24s\t%s\tin\t%-5s\t%s\n"
                  rrid
                  rrname
                  (if rrttl /= 0 then show rrttl else mempty)
                  rrtype $
                  (if rraux /= 0 then show rraux <> " " else mempty)
                  <> rrdata



regfishGet :: Url -> IO (Either String (W.Response LBS8.ByteString))
regfishGet url =
    withLocalState def $ \qa -> do

        cj <- A.query qa CookieJarQuery

        --putStrLn $ "session token: " <> show (sessionToken cj)

        case sessionToken cj of
            Nothing -> do
                putStrLn "Not logged in."
                -- TODO log in
                return $ Left "not logged in"

            Just _ -> do
                --putStrLn $ "GET " <> url

                let opts = W.defaults & W.cookies .~ Just cj

                res <- liftIO $ W.getWith opts url

                --now <- getCurrentTime

                --putStrLn $ "session token: " <> show (res ^. W.responseCookieJar . to sessionToken)

                let cj' = res ^. W.responseCookieJar
                    --cj'' = C.evictExpiredCookies cj' now

                A.update qa $ CookieJarUpdate cj'

                return $ Right res


regfishPost :: W.Postable p =>
    Url -> p -> IO (Either String (W.Response LBS8.ByteString))
regfishPost url params = do
    withLocalState def $ \qa -> do

        cj <- A.query qa CookieJarQuery

        --putStrLn $ "session token: " <> show (sessionToken cj)

        case sessionToken cj of
            Nothing -> do
                putStrLn "Not logged in."
                -- TODO log in
                return $ Left "not logged in"

            Just _ -> do
                --putStrLn $ "POST " <> url <> " " <> show params

                let opts = W.defaults & W.cookies .~ Just cj

                res <- liftIO $ W.postWith opts url params

                -- TODO check error

                --putStrLn $ "session token: " <> show (res ^. W.responseCookieJar . to sessionToken)

                A.update qa $ CookieJarUpdate $ res ^. W.responseCookieJar

                return $ Right res



-- | We're on a login page, when a form action is `loginUrl`.
isLoginPage :: String -> Bool
isLoginPage =
    (("action=" ++ show loginUrl) `L.isInfixOf`)



undata :: RRData -> (String -> Integer -> String -> a) -> a
undata (A rrdata) f = f "A" 0 rrdata
undata (AAAA rrdata) f = f "AAAA" 0 rrdata
undata (ALIAS rrdata) f = f "ALIAS" 0 rrdata
undata (CNAME rrdata) f = f "CNAME" 0 rrdata
undata (MX rraux rrdata) f = f "MX" rraux rrdata
undata (NS rrdata) f = f "NS" 0 rrdata
undata (TXT rrdata) f = f "TXT" 0 rrdata
undata (SRV rrdata) f = f "SRV" 0 rrdata


add :: RRname -> RRttl -> RRData -> IO (Either String (W.Response LBS8.ByteString))
add rrname rrttl rrdata =
    regfishPost (_allinone def) $ undata rrdata $ getRRAddValidation rrname rrttl

edit :: RRid -> RRname -> RRttl -> RRData -> IO (Either String (W.Response LBS8.ByteString))
edit rrid rrname rrttl rrdata =
    regfishPost (_allinone def) $ undata rrdata $ commitRRChanges rrid rrname rrttl

del :: RRid -> IO (Either String (W.Response LBS8.ByteString))
del rrid =
    regfishPost (_allinone def) $ removeRREntry rrid



removeRREntry :: RRid -> [W.FormParam]
removeRREntry rrid =
    [ "action"  W.:= ("removeRREntry" :: String)
    , "rrid"  W.:= rrid
    ]

commitRRChanges ::
    RRid -> RRname -> RRttl -> RRtype -> RRaux -> RRdata -> [W.FormParam]
commitRRChanges rrid rrname rrttl rrtype rraux rrdata =
    [ "action"  W.:= ("commitRRChanges" :: String)
    , "rrid"  W.:= rrid
    , "type"  W.:= rrtype
    , "name"  W.:= rrname
    , "ttl"   W.:= rrttl
    , "data"  W.:= rrdata
    , "aux"   W.:= rraux
    ]

getRRAddValidation ::
    RRname -> RRttl -> RRtype -> RRaux -> RRdata -> [W.FormParam]
getRRAddValidation rrname rrttl rrtype rraux rrdata =
    [ "action"  W.:= ("getRRAddValidation" :: String)
    , "type"  W.:= rrtype
    , "name"  W.:= rrname
    , "ttl"   W.:= rrttl
    , "data"  W.:= rrdata
    , "aux"   W.:= rraux
    ]