summaryrefslogtreecommitdiff
path: root/dht/examples/pwrite.hs
blob: bad6af06d0569bfd831849cb3fdb808100ac8ef5 (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
{-# LANGUAGE CPP #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE OverloadedStrings #-}
import System.Environment
import System.Posix.Files ( getFileStatus, fileMode )
import Data.Bits          ( (.&.) )
import Data.Text          ( Text )
import qualified Data.Text    as Text
import qualified Data.Text.IO as Text
import Control.Applicative
import Control.Monad
import Data.Maybe
import XMPPServer
import Data.Monoid

-- Transforms a string of form language[_territory][.codeset][@modifier]
-- typically used in LC_ locale variables into the BCP 47
-- language codes used in xml:lang attributes.
toBCP47 :: [Char] -> [Char]
toBCP47 lang = map hyphen $ takeWhile (/='.') lang
 where hyphen '_' = '-'
       hyphen  c  =  c


#if MIN_VERSION_base(4,6,0)
#else
lookupEnv k = fmap (lookup k) getEnvironment
#endif

getPreferedLang :: IO Text
getPreferedLang = do
    lang <- do
        lc_all <- lookupEnv "LC_ALL"
        lc_messages <- lookupEnv "LC_MESSAGES"
        lang <- lookupEnv "LANG"
        return $ lc_all `mplus` lc_messages `mplus` lang
    return $ maybe "en" (Text.pack . toBCP47) lang

cimatch :: Text -> Text -> Bool
cimatch w t = Text.toLower w == Text.toLower t

cimatches :: Text -> [Text] -> [Text]
cimatches w ts = dropWhile (not . cimatch w) ts

-- rfc4647 lookup of best match language tag
lookupLang :: [Text] -> [Text] -> Maybe Text
lookupLang (w:ws) tags
    | Text.null w = lookupLang ws tags
    | otherwise   = case cimatches w tags of
                        (t:_) -> Just t
                        []    -> lookupLang (reduce w:ws) tags
 where
    reduce w = Text.concat $ reverse nopriv
        where
            rparts = reverse . init $ Text.groupBy (\_ c -> c/='-') w
            nopriv = dropWhile ispriv rparts
            ispriv t = Text.length t == 2 && Text.head t == '-'

lookupLang [] tags | "" `elem` tags = Just ""
                   | otherwise      = listToMaybe $ tags


messageText :: Stanza -> IO Text
messageText msg = do
    pref <- getPreferedLang
    let m = msgLangMap (stanzaType msg)
        key = lookupLang [pref] (map fst m)
        choice = do
            k <- key
            lookup k m
    flip (maybe $ return "") choice $ \choice -> do
    let subj = fmap ("Subject: " <>) $ msgSubject choice
        ts = catMaybes [subj, msgBody choice]
    return $ Text.intercalate "\n\n" ts

crlf :: Text -> Text
crlf t = Text.unlines $ map cr (Text.lines t)
 where
    cr t | Text.last t == '\r'  = t
         | otherwise            = t <> "\r"

deliverTerminalMessage ::
    forall t t1. t -> Text -> t1 -> Stanza -> IO Bool
deliverTerminalMessage cw tty utmp msg = do
    mode <- fmap fileMode (getFileStatus $ Text.unpack tty)
    let mesgy = mode .&. 0o020 /= 0 -- verify mode g+w
    if not mesgy then return False else do
    text <- do
        t <- messageText msg
        return $ Text.unpack
            $ case stanzaFrom msg of
                Just from -> "\r\n" <> from <> " says...\r\n" <> crlf t <> "\r\n"
                Nothing -> crlf t <> "\r\n"
    writeFile (Text.unpack tty) text
    return True -- return True if a message was delivered

main = do
    args <- getArgs
    let mas = (,) <$> listToMaybe args <*> listToMaybe (drop 1 args)
    flip (maybe $ putStrLn "pwrite user tty") mas $ \(usr,tty) -> do
    bod <- Text.getContents
    stanza <- makeMessage "jabber:client" "nobody" (Text.pack usr) bod
    b <- deliverTerminalMessage () (Text.pack tty) () stanza
    when b $ putStrLn "delivered."
    return ()