{-# Language RecordWildCards #-}
module Client.CApi.Exports
(
Glirc_send_message
, glirc_send_message
, Glirc_print
, glirc_print
, Glirc_list_networks
, glirc_list_networks
, Glirc_list_channels
, glirc_list_channels
, Glirc_list_channel_users
, glirc_list_channel_users
, Glirc_my_nick
, glirc_my_nick
, Glirc_user_account
, glirc_user_account
, Glirc_user_channel_modes
, glirc_user_channel_modes
, Glirc_channel_modes
, glirc_channel_modes
, Glirc_channel_masks
, glirc_channel_masks
, Glirc_identifier_cmp
, glirc_identifier_cmp
, Glirc_is_channel
, glirc_is_channel
, Glirc_is_logged_on
, glirc_is_logged_on
, Glirc_mark_seen
, glirc_mark_seen
, Glirc_clear_window
, glirc_clear_window
, Glirc_current_focus
, glirc_current_focus
, Glirc_set_focus
, glirc_set_focus
, Glirc_free_string
, glirc_free_string
, Glirc_free_strings
, glirc_free_strings
, Glirc_inject_chat
, glirc_inject_chat
, Glirc_resolve_path
, glirc_resolve_path
, Glirc_set_timer
, glirc_set_timer
, Glirc_cancel_timer
, glirc_cancel_timer
) where
import Client.CApi (cancelTimer, pushTimer)
import Client.CApi.Types
import Client.Configuration
import Client.Message
import Client.State
import Client.State.Channel
import Client.State.Focus
import Client.State.Network
import Client.State.Window
import Client.UserHost
import Control.Concurrent.MVar
import Control.Exception
import Control.Lens
import Control.Monad (unless)
import Data.Char (chr)
import Data.Foldable (traverse_)
import Data.Functor.Compose
import qualified Data.Map as Map
import Data.Monoid (First(..))
import qualified Data.HashMap.Strict as HashMap
import Data.Text (Text)
import qualified Data.Text as Text
import qualified Data.Text.Foreign as Text
import Data.Time
import Foreign.C
import Foreign.Marshal
import Foreign.Ptr
import Foreign.StablePtr
import Foreign.Storable
import Irc.Identifier
import Irc.RawIrcMsg
import Irc.UserInfo
import Irc.Message
import LensUtils
derefToken :: Ptr () -> IO (MVar (Int, ClientState))
derefToken = deRefStablePtr . castPtrToStablePtr
peekFgnMsg :: FgnMsg -> IO RawIrcMsg
peekFgnMsg FgnMsg{..} =
do let strArray n p = traverse peekFgnStringLen =<<
peekArray (fromIntegral n) p
tagKeys <- strArray fmTagN fmTagKeys
tagVals <- strArray fmTagN fmTagVals
prefixN <- peekFgnStringLen fmPrefixNick
prefixU <- peekFgnStringLen fmPrefixUser
prefixH <- peekFgnStringLen fmPrefixHost
command <- peekFgnStringLen fmCommand
params <- strArray fmParamN fmParams
return RawIrcMsg
{ _msgTags = zipWith TagEntry tagKeys tagVals
, _msgPrefix = if Text.null prefixN
then Nothing
else Just (UserInfo (mkId prefixN) prefixU prefixH)
, _msgCommand = command
, _msgParams = params
}
peekFgnStringLen :: FgnStringLen -> IO Text
peekFgnStringLen (FgnStringLen ptr len) =
Text.peekCStringLen (ptr, fromIntegral len)
type Glirc_send_message =
Ptr () ->
Ptr FgnMsg ->
IO CInt
glirc_send_message :: Glirc_send_message
glirc_send_message token msgPtr =
do mvar <- derefToken token
fgn <- peek msgPtr
msg <- peekFgnMsg fgn
network <- peekFgnStringLen (fmNetwork fgn)
(_,st) <- readMVar mvar
case preview (clientConnection network) st of
Nothing -> return 1
Just cs -> 0 <$ sendMsg cs msg
`catch` \SomeException{} -> return 1
type Glirc_print =
Ptr () ->
MessageCode ->
CString ->
CSize ->
IO CInt
glirc_print :: Glirc_print
glirc_print stab code msgPtr msgLen =
do mvar <- derefToken stab
txt <- peekFgnStringLen (FgnStringLen msgPtr msgLen)
now <- getZonedTime
let con | code == normalMessage = NormalBody
| otherwise = ErrorBody
msg = ClientMessage
{ _msgBody = con txt
, _msgTime = now
, _msgNetwork = Text.empty
}
modifyMVar_ mvar $ \(i,st) ->
do return (i, recordNetworkMessage msg st)
return 0
`catch` \SomeException{} -> return 1
type Glirc_inject_chat =
Ptr () ->
CString ->
CSize ->
CString ->
CSize ->
CString ->
CSize ->
CString ->
CSize ->
IO CInt
glirc_inject_chat :: Glirc_inject_chat
glirc_inject_chat stab netPtr netLen srcPtr srcLen tgtPtr tgtLen msgPtr msgLen =
do mvar <- derefToken stab
net <- peekFgnStringLen (FgnStringLen netPtr netLen)
src <- peekFgnStringLen (FgnStringLen srcPtr srcLen)
tgt <- mkId <$> peekFgnStringLen (FgnStringLen tgtPtr tgtLen)
txt <- peekFgnStringLen (FgnStringLen msgPtr msgLen)
now <- getZonedTime
let msg = ClientMessage
{ _msgBody = IrcBody (Privmsg (parseUserInfo src) tgt txt)
, _msgTime = now
, _msgNetwork = net
}
modifyMVar_ mvar $ \(i, st) ->
do return (i, recordChannelMessage net tgt msg st)
return 0
`catch` \SomeException{} -> return 1
type Glirc_list_networks =
Ptr () ->
IO (Ptr CString)
glirc_list_networks :: Glirc_list_networks
glirc_list_networks stab =
do mvar <- derefToken stab
(_,st) <- readMVar mvar
let networks = views clientConnections HashMap.keys st
strs <- traverse (newCString . Text.unpack) networks
newArray0 nullPtr strs
type Glirc_identifier_cmp =
CString ->
CSize ->
CString ->
CSize ->
IO CInt
glirc_identifier_cmp :: Glirc_identifier_cmp
glirc_identifier_cmp p1 n1 p2 n2 =
do txt1 <- peekFgnStringLen (FgnStringLen p1 n1)
txt2 <- peekFgnStringLen (FgnStringLen p2 n2)
return $! case compare (mkId txt1) (mkId txt2) of
LT -> -1
EQ -> 0
GT -> 1
type Glirc_list_channels =
Ptr () ->
CString ->
CSize ->
IO (Ptr CString)
glirc_list_channels :: Glirc_list_channels
glirc_list_channels stab networkPtr networkLen =
do mvar <- derefToken stab
(_,st) <- readMVar mvar
network <- peekFgnStringLen (FgnStringLen networkPtr networkLen)
case preview (clientConnection network . csChannels) st of
Nothing -> return nullPtr
Just m ->
do strs <- traverse (newCString . Text.unpack . idText) (HashMap.keys m)
newArray0 nullPtr strs
type Glirc_list_channel_users =
Ptr () ->
CString ->
CSize ->
CString ->
CSize ->
IO (Ptr CString)
glirc_list_channel_users :: Glirc_list_channel_users
glirc_list_channel_users stab networkPtr networkLen channelPtr channelLen =
do mvar <- derefToken stab
(_, st) <- readMVar mvar
network <- peekFgnStringLen (FgnStringLen networkPtr networkLen)
channel <- peekFgnStringLen (FgnStringLen channelPtr channelLen)
let mb = preview ( clientConnection network
. csChannels . ix (mkId channel)
. chanUsers
) st
case mb of
Nothing -> return nullPtr
Just m ->
do strs <- traverse (newCString . Text.unpack . idText) (HashMap.keys m)
newArray0 nullPtr strs
type Glirc_my_nick =
Ptr () ->
CString ->
CSize ->
IO CString
glirc_my_nick :: Glirc_my_nick
glirc_my_nick stab networkPtr networkLen =
do mvar <- derefToken stab
(_,st) <- readMVar mvar
network <- peekFgnStringLen (FgnStringLen networkPtr networkLen)
let mb = preview (clientConnection network . csNick) st
case mb of
Nothing -> return nullPtr
Just me -> newCString (Text.unpack (idText me))
type Glirc_user_account =
Ptr () ->
CString ->
CSize ->
CString ->
CSize ->
IO CString
glirc_user_account :: Glirc_user_account
glirc_user_account stab networkPtr networkLen nickPtr nickLen =
do mvar <- derefToken stab
(_,st) <- readMVar mvar
network <- peekFgnStringLen (FgnStringLen networkPtr networkLen)
nick <- peekFgnStringLen (FgnStringLen nickPtr nickLen )
let mb = preview ( clientConnection network
. csUsers . ix (mkId nick)
. uhAccount . filtered (not . Text.null)) st
case mb of
Just acct -> newCString (Text.unpack acct)
_ -> return nullPtr
type Glirc_user_channel_modes =
Ptr () ->
CString ->
CSize ->
CString ->
CSize ->
CString ->
CSize ->
IO CString
glirc_user_channel_modes :: Glirc_user_channel_modes
glirc_user_channel_modes stab netPtr netLen chanPtr chanLen nickPtr nickLen =
do mvar <- derefToken stab
(_,st) <- readMVar mvar
network <- peekFgnStringLen (FgnStringLen netPtr netLen)
chan <- peekFgnStringLen (FgnStringLen chanPtr chanLen )
nick <- peekFgnStringLen (FgnStringLen nickPtr nickLen )
let mb = preview ( clientConnection network
. csChannels . ix (mkId chan)
. chanUsers . ix (mkId nick) ) st
case mb of
Just sigils -> newCString sigils
Nothing -> return nullPtr
type Glirc_channel_modes =
Ptr () ->
CString ->
CSize ->
CString ->
CSize ->
IO (Ptr CString)
glirc_channel_modes :: Glirc_channel_modes
glirc_channel_modes stab netPtr netLen chanPtr chanLen =
do mvar <- derefToken stab
(_,st) <- readMVar mvar
network <- peekFgnStringLen (FgnStringLen netPtr netLen)
chan <- peekFgnStringLen (FgnStringLen chanPtr chanLen )
let mb = preview ( clientConnection network
. csChannels . ix (mkId chan)
. chanModes
) st
case mb of
Nothing -> return nullPtr
Just modeMap ->
do let strings = [ mode : Text.unpack arg | (mode,arg) <- Map.toList modeMap ]
strs <- traverse newCString strings
newArray0 nullPtr strs
type Glirc_channel_masks =
Ptr () ->
CString ->
CSize ->
CString ->
CSize ->
CChar ->
IO (Ptr CString)
glirc_channel_masks :: Glirc_channel_masks
glirc_channel_masks stab netPtr netLen chanPtr chanLen cmode =
do let mode = chr (fromIntegral cmode) :: Char
mvar <- derefToken stab
(_,st) <- readMVar mvar
network <- peekFgnStringLen (FgnStringLen netPtr netLen)
chan <- peekFgnStringLen (FgnStringLen chanPtr chanLen )
let mb = preview ( clientConnection network
. csChannels . ix (mkId chan)
. chanLists . ix mode
) st
case mb of
Nothing -> return nullPtr
Just listMap ->
do strs <- traverse (newCString . Text.unpack) (HashMap.keys listMap)
newArray0 nullPtr strs
type Glirc_mark_seen =
Ptr () ->
CString ->
CSize ->
CString ->
CSize ->
IO ()
glirc_mark_seen :: Glirc_mark_seen
glirc_mark_seen stab networkPtr networkLen channelPtr channelLen =
do network <- peekFgnStringLen (FgnStringLen networkPtr networkLen)
channel <- peekFgnStringLen (FgnStringLen channelPtr channelLen)
let focus
| Text.null network = Unfocused
| Text.null channel = NetworkFocus network
| otherwise = ChannelFocus network (mkId channel)
mvar <- derefToken stab
modifyMVar_ mvar $ \(i,st) ->
let st' = overStrict (clientWindows . ix focus) windowSeen st
in st' `seq` return (i,st')
type Glirc_clear_window =
Ptr () ->
CString ->
CSize ->
CString ->
CSize ->
IO ()
glirc_clear_window :: Glirc_clear_window
glirc_clear_window stab networkPtr networkLen channelPtr channelLen =
do network <- peekFgnStringLen (FgnStringLen networkPtr networkLen)
channel <- peekFgnStringLen (FgnStringLen channelPtr channelLen)
let focus
| Text.null network = Unfocused
| Text.null channel = NetworkFocus network
| otherwise = ChannelFocus network (mkId channel)
mvar <- derefToken stab
modifyMVar_ mvar $ \(i,st) ->
let st' = set (clientWindows . ix focus) emptyWindow st
in st' `seq` return (i,st')
type Glirc_free_string =
CString ->
IO ()
glirc_free_string :: Glirc_free_string
glirc_free_string = free
type Glirc_free_strings =
Ptr CString ->
IO ()
glirc_free_strings :: Glirc_free_strings
glirc_free_strings p =
unless (p == nullPtr) $
do traverse_ free =<< peekArray0 nullPtr p
free p
type Glirc_current_focus =
Ptr () ->
Ptr CString ->
Ptr CSize ->
Ptr CString ->
Ptr CSize ->
IO ()
glirc_current_focus :: Glirc_current_focus
glirc_current_focus stab netP netL tgtP tgtL =
do mvar <- derefToken stab
(_,st) <- readMVar mvar
let (net,tgt) = case view clientFocus st of
Unfocused -> (Text.empty, Text.empty)
NetworkFocus n -> (n , Text.empty)
ChannelFocus n t -> (n , idText t )
exportText netP netL net
exportText tgtP tgtL tgt
type Glirc_set_focus =
Ptr () ->
CString ->
CSize ->
CString ->
CSize ->
IO ()
glirc_set_focus :: Glirc_set_focus
glirc_set_focus stab netP netL tgtP tgtL =
do mvar <- derefToken stab
net <- peekFgnStringLen (FgnStringLen netP netL)
tgt <- peekFgnStringLen (FgnStringLen tgtP tgtL)
let focus
| Text.null net = Unfocused
| Text.null tgt = NetworkFocus net
| otherwise = ChannelFocus net (mkId tgt)
modifyMVar_ mvar $ \(i,st) ->
let st' = changeFocus focus st
in st' `seq` return (i,st')
type Glirc_is_channel =
Ptr () ->
CString ->
CSize ->
CString ->
CSize ->
IO CInt
glirc_is_channel :: Glirc_is_channel
glirc_is_channel stab net netL tgt tgtL =
do mvar <- derefToken stab
(_,st) <- readMVar mvar
network <- peekFgnStringLen (FgnStringLen net netL)
target <- peekFgnStringLen (FgnStringLen tgt tgtL)
case preview (clientConnection network) st of
Just cs | isChannelIdentifier cs (mkId target) -> return 1
_ -> return 0
type Glirc_is_logged_on =
Ptr () ->
CString ->
CSize ->
CString ->
CSize ->
IO CInt
glirc_is_logged_on :: Glirc_is_logged_on
glirc_is_logged_on stab net netL tgt tgtL =
do mvar <- derefToken stab
(_,st) <- readMVar mvar
network <- peekFgnStringLen (FgnStringLen net netL)
target <- peekFgnStringLen (FgnStringLen tgt tgtL)
let online = has (clientConnection network . csUsers . ix (mkId target)) st
return $! if online then 1 else 0
type Glirc_resolve_path =
Ptr () ->
CString ->
CSize ->
IO CString
glirc_resolve_path :: Glirc_resolve_path
glirc_resolve_path stab pathP pathL =
do mvar <- derefToken stab
(_,st) <- readMVar mvar
path <- peekFgnStringLen (FgnStringLen pathP pathL)
let cfgPath = view clientConfigPath st
cxt <- newFilePathContext cfgPath
newCString (resolveFilePath cxt (Text.unpack path))
type Glirc_set_timer =
Ptr () ->
CULong ->
FunPtr TimerCallback ->
Ptr () ->
IO TimerId
glirc_set_timer :: Glirc_set_timer
glirc_set_timer stab millis fun ptr =
do mvar <- derefToken stab
time <- addUTCTime (fromIntegral millis / 1000) <$> getCurrentTime
modifyMVar mvar $ \(i,st) ->
let (timer,st') = st & clientExtensions . esActive . singular (ix i)
%%~ pushTimer time fun ptr
in st' `seq` return ((i,st'), fromIntegral timer)
type Glirc_cancel_timer =
Ptr () ->
TimerId ->
IO (Ptr ())
glirc_cancel_timer :: Glirc_cancel_timer
glirc_cancel_timer stab timerId =
do mvar <- derefToken stab
modifyMVar mvar $ \(i,st) ->
let Compose mb = st & clientExtensions . esActive . ix i
%%~ \ae -> Compose $
do (entry, ae') <- cancelTimer (fromIntegral timerId) ae
return (First (Just entry), ae')
in return $! case mb of
Just (First (Just ptr), st') -> ((i,st'), ptr)
_ -> ((i, st), nullPtr)