hoodle-core-0.15: src/Hoodle/Coroutine/Socket.hs
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
-----------------------------------------------------------------------------
-- |
-- Module : Hoodle.Coroutine.Socket
-- Copyright : (c) 2014, 2015 Ian-Woo Kim
--
-- License : BSD3
-- Maintainer : Ian-Woo Kim <ianwookim@gmail.com>
-- Stability : experimental
-- Portability : GHC
--
-----------------------------------------------------------------------------
module Hoodle.Coroutine.Socket where
import Control.Applicative
import Control.Concurrent
import qualified Control.Exception as E
import Control.Lens (view)
import Control.Monad.IO.Class
import Control.Monad.State
import Control.Monad.Trans.Either
import Control.Monad.Trans.Maybe
import Data.Aeson.Parser
import Data.Aeson.Types
import qualified Data.Attoparsec.ByteString as A
import qualified Data.CaseInsensitive as CI
import Data.Monoid ((<>))
import qualified Data.Text as T
import qualified Data.Text.Encoding as TE
import Data.Time.Clock
import qualified Graphics.UI.Gtk as Gtk
import Network.HTTP.Client.Internal (computeCookieString)
import Network.HTTP.Conduit
import qualified Network.WebSockets as WS
import System.Directory
import System.FilePath ((</>))
--
import Hoodle.Coroutine.Dialog
import Hoodle.Coroutine.Hub.Common
import Hoodle.Script.Hook
import Hoodle.Type.Coroutine
import Hoodle.Type.Event
import Hoodle.Type.Hub
import Hoodle.Type.HoodleState
import Hoodle.Type.Synchronization
import Hoodle.Util
--
data Permission = Owner | Editor | Viewer | Stranger deriving (Show,Eq,Ord,Read)
instance ToJSON Permission where
toJSON Owner = String "owner"
toJSON Editor = String "editor"
toJSON Viewer = String "viewer"
toJSON Stranger = String "stranger"
instance FromJSON Permission where
parseJSON (String t) =
case t of
"owner" -> return Owner
"editor" -> return Editor
"viewer" -> return Viewer
"stranger" -> return Stranger
_ -> fail "error in parsing Permission"
parseJSON _ = fail "error in parsing Permission"
data HoodleWSEvent = HWSOpen { hws_permission :: Permission
, hws_fileuuid :: T.Text
, hws_filepath :: T.Text }
| HWSSync { hws_clientuuid :: T.Text
, hws_filesyncstatus :: FileSyncStatus
}
deriving Show
instance ToJSON HoodleWSEvent where
toJSON HWSOpen {..} = object [ "eventType" .= toJSON ("open" :: T.Text)
, "permission" .= toJSON hws_permission
, "fileuuid" .= toJSON hws_fileuuid
, "filepath" .= toJSON hws_filepath ]
toJSON HWSSync {..} = object [ "eventType" .= toJSON ("sync" :: T.Text)
, "clientuuid" .= toJSON hws_clientuuid
, "filesyncstatus" .= toJSON hws_filesyncstatus
]
instance FromJSON HoodleWSEvent where
parseJSON (Object v) = do
typ :: T.Text <- v .: "eventType"
case typ of
"open" -> HWSOpen <$> v .: "permission" <*> v .: "fileuuid" <*> v .: "filepath"
"sync" -> HWSSync <$> v .: "clientuuid" <*> v .: "filesyncstatus"
_ -> fail "error in parsing HoodleWSEvent"
parseJSON _ = fail "error in parsing HoodleWSEvent"
-- |
socketConnect :: MainCoroutine ()
socketConnect = do
xst <- get
r <- runMaybeT $ do
hset <- (MaybeT . return) $ view hookSet xst
hinfo <- (MaybeT . return) (hubInfo hset)
lift (hoodleWSStart hinfo)
case r of
Nothing -> okMessageBox "socket connect not successful" >> return ()
Just _ -> return ()
hoodleWSStart :: HubInfo -> MainCoroutine ()
hoodleWSStart hinfo@HubInfo {..} = do
hdir <- liftIO $ getHomeDirectory
let tokfile = hdir </> ".hoodle.d" </> "token.txt"
prepareToken hinfo tokfile
doIOaction $ \evhandler -> do
forkIO $ (`E.catch` (\(err:: E.SomeException)-> print err >> return ())) $
withHub hinfo tokfile $ \_manager coojar -> do
request2' <- parseUrl ("http://" <> hubSocketURL <> ":" <> show hubSocketPort </> hubSocketPath)
ctime <- liftIO getCurrentTime
let (bstr,_) = computeCookieString request2' coojar ctime True
newheaders = [(CI.mk "Cookie",bstr)]
liftIO $ WS.runClientWith hubSocketURL hubSocketPort hubSocketPath WS.defaultConnectionOptions newheaders $ \conn -> forever $ do
txt <- WS.receiveData conn
runEitherT $ do
v <- hoistEither $ A.parseOnly json (TE.encodeUtf8 txt)
o :: HoodleWSEvent <- hoistEither $ parseEither parseJSON v
lift $ hoodleWSDispatchEvent evhandler hinfo o
return (UsrEv ActionOrdered)
hoodleWSDispatchEvent :: (AllEvent -> IO ()) -> HubInfo -> HoodleWSEvent -> IO ()
hoodleWSDispatchEvent evhandler HubInfo {..} HWSOpen {..} = do
let urlpath = FileUrl (hubFileRoot </> T.unpack hws_filepath)
uuid = read (T.unpack hws_fileuuid)
case hws_permission of
Owner -> (Gtk.postGUIAsync . evhandler . UsrEv) (OpenLink urlpath Nothing)
Editor -> (Gtk.postGUIAsync . evhandler . UsrEv) (OpenShared uuid)
_ -> return ()
hoodleWSDispatchEvent evhandler HubInfo {..} HWSSync {..} =
let clientuuid = read (T.unpack hws_clientuuid)
in (Gtk.postGUIAsync . evhandler . UsrEv) (GotSyncEvent False clientuuid hws_filesyncstatus)