feed2lj-0.0.2: LjPost.hs
{-# LANGUAGE CPP #-}
-- vim: set fileencoding=utf-8 ts=2 sw=2 expandtab:
module LjPost (readLjSetting, postToLj, isSuccess, lookupLjKey, putLjKey) where
import IO
import Maybe (fromJust, fromMaybe, isNothing)
import Data.Time
import System.Locale (defaultTimeLocale)
import System.Directory (getHomeDirectory)
#ifdef NANOMD5
import Data.ByteString.UTF8 (fromString)
import Data.Digest.OpenSSL.MD5 (md5sum)
#else
-- PUREMD5
import Data.ByteString.Lazy.UTF8 (fromString)
import Data.Digest.Pure.MD5 (md5)
#endif
import Network.Curl
ljFlatUrl = "www.livejournal.com/interface/flat"
ljPassFile = "~/.ljpass"
currentTime = do
t <- getCurrentTime
tz <- getCurrentTimeZone
return $ utcToLocalTime tz t
showTime = formatTime defaultTimeLocale
-- options for various requests,
-- see http://www.livejournal.com/doc/server/ljp.csp.protocol.html
postFlags = [CurlPost True]
quoteOpt ('=':xs) = "%3d" ++ quoteOpt xs
quoteOpt ('&':xs) = "%26" ++ quoteOpt xs
quoteOpt (x:xs) = x : quoteOpt xs
quoteOpt [] = []
getChallengeOpts = CurlPostFields ["mode=getchallenge"] : postFlags
authOpts u p c = [ "user=" ++ quoteOpt u, "auth_method=challenge",
"auth_challenge=" ++ quoteOpt c,
"auth_response=" ++ quoteOpt (evalResponse c p) ]
loginAuthOpts u p c =
CurlPostFields ("mode=login" : (authOpts u p c)) : postFlags
currentTimeOpts :: IO [String]
currentTimeOpts = do
t <- currentTime
let opts = [ "year=%Y", "mon=%m", "day=%d", "hour=%H", "min=%M" ]
return $ map (flip showTime t) opts
postOpts u p c subj msg topts =
CurlPostFields ("mode=postevent" : (authOpts u p c)
++ ["event=" ++ quoteOpt msg, "subject=" ++ quoteOpt subj,
"lineendings=unix", "ver=1"]
++ topts ) : postFlags
-- make an associative list
list2alist :: [a] -> [(a,a)]
list2alist (k:v:rest) = (k,v) : list2alist rest
list2alist _ = []
-- lookup key in flat LJ response
lookupLjKey :: String -> CurlResponse -> Maybe String
lookupLjKey k = ( lookup k . list2alist . lines . respBody )
-- print key k from flat LJ response
putLjKey k r = putStrLn $ show $ lookupLjKey k r
-- evaluate challenge response
evalResponse c p = smd5 ( c ++ (smd5 p) )
#ifdef NANOMD5
where smd5 = md5sum . fromString
#else
-- PUREMD5
where smd5 = show . md5 . fromString
#endif
-- does LJ report success?
isSuccess :: CurlResponse -> Bool
isSuccess = (=="OK") . fromMaybe "" . lookupLjKey "success"
-- read and parse a file with LJ password and username
readPassFile f = do
ljpass <- readFile f
return $ map (\(f,s) -> (f,tail s)) $ map (break (== '=')) $ lines ljpass
expandhome ('~':'/':p) = do h <- getHomeDirectory ; return (h ++ "/" ++ p)
expandhome p = return p
readLjSetting key = do
passfile <- expandhome ljPassFile
s <- readPassFile passfile
return (lookup key s)
-- login into LJ as ljuser/ljpass and post msg with given subj
postToLj ljuser ljpass subj msg = withCurlDo $ do
curl <- initialize
r <- do_curl_ curl ljFlatUrl getChallengeOpts :: IO CurlResponse
if (isSuccess r)
then do
let challenge = fromJust $ lookupLjKey "challenge" r
timeopts <- currentTimeOpts
let opts = postOpts ljuser ljpass challenge subj msg timeopts
r <- do_curl_ curl ljFlatUrl opts :: IO CurlResponse
return r
else return r