packages feed

hoodle-extra-0.1: exe/idfilepathdb.hs

{-# LANGUAGE OverloadedStrings #-} 
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE ScopedTypeVariables #-}
-- 
-- uuid,md5hash,filepath map utility  
-- 
import           Control.Applicative ((<$>))
import           Control.Lens
import           Control.Monad.Trans (liftIO)
import           Control.Monad.Trans.Either 
import           Control.Monad.Trans.Resource
import           Data.Attoparsec 
import           Data.Aeson.Parser (json)
import           Data.Aeson.Types  (parseJSON, parseEither)
import           Data.Aeson.Encode.Pretty (encodePretty)
import qualified Data.ByteString.Char8 as B
import qualified Data.ByteString.Lazy as L
import           Data.Conduit (($$+-))
import           Data.Conduit.List (consume)
import           Data.Data
import qualified Data.List as DL 
import qualified Data.Map as M
import           Data.Monoid ((<>))
import           Data.Digest.Pure.MD5
import qualified Network.HTTP.Conduit as N
import           System.Console.CmdArgs
import           System.Directory
import           System.FilePath
import           System.Process
import           System.IO (stdin)
-- 
import           Data.Hoodle.Simple
import           Text.Hoodle.Parse.Attoparsec 
-- 
import           DiffDB

data IdFilePathDB = AllFiles { hoodlehome :: FilePath }
                  | SingleFile { hoodlehome :: FilePath 
                               , singlefilename :: FilePath } 
                  | DBDiff
                  | DBSync { remoteURL :: String 
                           , remoteID :: String 
                           , remotePassword :: String } 
                  deriving (Show,Data,Typeable)

allfiles :: IdFilePathDB 
allfiles = 
  AllFiles { hoodlehome = def &= typ "HOODLEHOME" &= argPos 0 } 

singlefile :: IdFilePathDB 
singlefile = 
  SingleFile { hoodlehome = def &= typ "HOODLEHOME" &= argPos 0 
             , singlefilename = def &= typ "FILEPATH" &= argPos 1
             }
  
dbdiff :: IdFilePathDB 
dbdiff = DBDiff

dbsync :: IdFilePathDB 
dbsync = DBSync { remoteURL = def &= typ "URL" &= argPos 0
                , remoteID = def &= typ "ID" &= argPos 1
                , remotePassword = def &= typ "PASSWORD" &= argPos 2
                }
  
mode :: IdFilePathDB
mode = modes [allfiles, singlefile, dbdiff, dbsync ] 

main :: IO () 
main = do 
  params <- cmdArgs mode
  case params of 
    AllFiles hdir      -> allfilework hdir 
    SingleFile hdir fp -> singlefilework hdir fp 
    DBDiff             -> dbdiffwork
    DBSync url idee pw -> dbsyncwork url idee pw
  
allfilework :: FilePath -> IO ()
allfilework hdir = do 
  homedir <- getHomeDirectory 
  r <- readProcess "find" [homedir </> "Dropbox" </> "hoodle","-name","*.hdl","-print"] "" 
  mapM_ (singlefilework hdir) (lines r)

splitfunc :: String -> (String,(String,String))
splitfunc str = 
  let (str1,rest1) = break (==' ') str 
      (str2,rest2) = break (==' ') (tail rest1)
      str3 = read (tail rest2)
  in (str1,(str2,str3))

    

singlefilework :: FilePath -> FilePath -> IO ()
singlefilework hdir oldfp = do 
  putStrLn ("working for " ++ oldfp)
  homedir <- getHomeDirectory 
  tmpdir <- getTemporaryDirectory 
  let origdbfile = homedir </> "Dropbox" </> "hoodleiddb.dat"
      tmpfile = tmpdir </> "hoodleiddb.dat"
  copyFile origdbfile tmpfile 
  fp <- makeRelative hdir <$> canonicalizePath oldfp 
  str <- readFile tmpfile 
  let assoclst = (map splitfunc . lines) str 
      assocmap = M.fromList assoclst 
      replacefunc n _ = Just n 
  muuid <- checkHoodleIdMd5 oldfp 
  let nmap = case muuid of 
               Nothing -> assocmap 
               Just (uuid,md5str) -> M.alter (replacefunc (md5str,fp)) uuid assocmap
      nstr = (unlines . map (\(x,(y,z))->x ++ " " ++ y ++ " " ++ show z) . M.toList) nmap 
  writeFile origdbfile nstr 
  removeFile tmpfile 

checkHoodleIdMd5 :: FilePath -> IO (Maybe (String,String))
checkHoodleIdMd5 fp = do 
  bstr <- B.readFile fp
  eh <- checkVersionAndGetIfHigherVersion bstr
  case eh of 
    Left str -> print str >> return Nothing 
    Right h -> do
      let idstr = B.unpack (view hoodleID h)
          md5str = show (md5 (L.fromChunks [bstr]))
      return (Just (idstr,md5str))
      

-- | using attoparsec without any built-in xml support 
checkVersionAndGetIfHigherVersion :: B.ByteString -> IO (Either String Hoodle) 
checkVersionAndGetIfHigherVersion bstr = do 
  case parseOnly checkHoodleVersion bstr of 
    Left str -> return (Left str )
    Right v -> do 
      if ( v < "0.1.9999" ) 
        then return (Left "low version") 
        else return (parseOnly hoodle bstr)


dbdiffwork :: IO ()
dbdiffwork = do 
  homedir <- getHomeDirectory 
  let newdbfile = homedir </> "Dropbox" </> "hoodleiddb.dat"
      olddbfile = homedir </> "Dropbox" </> "hoodleiddb.dat.old"
   
  newdbstr <- readFile newdbfile 
  olddbstr <- readFile olddbfile
  let makedb = M.fromList . map splitfunc . lines 
      (newdb,olddb) = (makedb newdbstr, makedb olddbstr) 
  (L.putStrLn . encodePretty) (checkdiff olddb newdb)

dbsyncwork :: String -> String -> String -> IO ()
dbsyncwork url idee pwd = do           
    bstr <- B.hGetContents stdin
    request' <- N.parseUrl (url <> "/auth/page/hashdb/login")
    let crstr = B.pack ("username=" ++ idee ++ "&password=" ++ pwd)
        requestauth = request' 
          { N.method = "POST" 
          , N.requestHeaders = 
              ("Content-Type","application/x-www-form-urlencoded") 
              : N.requestHeaders request'   
          , N.requestBody = N.RequestBodyBS crstr
          } 
    mck <- runResourceT $ N.withManager $ \manager -> do  
             response <- N.http requestauth manager
             return (DL.lookup "Set-Cookie" (N.responseHeaders response))
    case mck of 
      Nothing -> return()
      Just ck -> do 
        request'' <- N.parseUrl (url <> "/syncdb")
        let requesttask = request'' 
              { N.method = "POST"
              , N.requestHeaders = ("Content-Type", "application/json") 
                                   : ("Cookie",ck) 
                                   : N.requestHeaders request''
              , N.requestBody = N.RequestBodyBS bstr
              }
        runResourceT $ N.withManager $ \manager -> do  
          response <- N.http requesttask manager
          content <- N.responseBody response $$+- consume 
          liftIO $ print content