summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Presence/Server.hs3
-rw-r--r--xmppserver.hs204
2 files changed, 206 insertions, 1 deletions
diff --git a/Presence/Server.hs b/Presence/Server.hs
index c41f047f..5dce323c 100644
--- a/Presence/Server.hs
+++ b/Presence/Server.hs
@@ -54,7 +54,8 @@ doServer port g startCon = runServer2 port (runConn2 g)
54 h <- socketToHandle sock ReadWriteMode 54 h <- socketToHandle sock ReadWriteMode
55 hSetBuffering h NoBuffering 55 hSetBuffering h NoBuffering
56 st'' <- startCon (h .*. st) 56 st'' <- startCon (h .*. st)
57 handle (\(SomeException _) -> return ()) $ fix $ \loop -> do 57 let doException (SomeException e) = Prelude.putStrLn ("\n\nexception: " ++ show e ++ "\n\n")
58 handle doException $ fix $ \loop -> do
58 let continue () = hIsEOF h >>= flip when loop . not 59 let continue () = hIsEOF h >>= flip when loop . not
59 packet <- getPacket h 60 packet <- getPacket h
60 g st'' packet continue 61 g st'' packet continue
diff --git a/xmppserver.hs b/xmppserver.hs
new file mode 100644
index 00000000..26e9c12e
--- /dev/null
+++ b/xmppserver.hs
@@ -0,0 +1,204 @@
1{-# LANGUAGE OverloadedStrings #-}
2{-# LANGUAGE ScopedTypeVariables #-}
3{-# LANGUAGE ViewPatterns #-}
4
5import Data.HList.TypeEqGeneric1()
6import Data.HList.TypeCastGeneric1()
7import ByteStringOperators
8
9import Server
10import Data.ByteString.Lazy.Char8 as L
11 ( ByteString
12 , hPutStrLn
13 , unlines
14 , pack
15 , unpack
16 , init )
17import qualified Data.ByteString.Lazy.Char8 as L
18 ( putStrLn )
19import System.IO
20 ( Handle
21 )
22import Control.Concurrent (forkIO)
23import Control.Concurrent.Chan
24import Data.HList
25import AdaptServer
26import Text.XML.HaXml.Lex (xmlLex)
27import Text.XML.HaXml.Parse (xmlParseWith,element,doctypedecl,processinginstruction,elemOpenTag,elemCloseTag)
28import Text.XML.HaXml.Types as Hax (ElemTag,QName(..),Namespace(..),Element(..),Content(..),AttValue(..))
29import qualified Text.XML.HaXml.Types as Hax (Element)
30import Data.Maybe
31import Debug.Trace
32
33
34
35greet host = L.unlines
36 [ "<?xml version='1.0'?>"
37 , "<stream:stream"
38 , "from='" <++> host <++> "'"
39 , "id='someid'"
40 , "xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0'>"
41 , "<stream:features>"
42 , " <bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/>"
43 {-
44 -- , " <session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>"
45 , " <mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>"
46 -- , " <mechanism>DIGEST-MD5</mechanism>"
47 , " <mechanism>PLAIN</mechanism>"
48 , " </mechanisms> "
49 -}
50 , "</stream:features>"
51 ]
52
53startCon st = do
54 let h = hOccursFst st :: Handle
55 return (ConnectionFinalizer (return ()) .*. st)
56
57iq_query_unavailable host id mjid xmlns = L.unlines $
58 [ "<iq type='error'"
59 , " from='" <++> host <++> "'"
60 , case mjid of Just jid -> " to='" <++> jid <++> "'"
61 Nothing -> ""
62 , " id='" <++> id <++> "'>"
63 , " <query xmlns='" <++> xmlns <++> "'/>"
64 , " <error type='cancel'>"
65 , " <service-unavailable"
66 , " xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>"
67 , " </error>"
68 , "</iq>"
69 ]
70
71tagattrs tag content = Prelude.concatMap (\(CElem (Elem _ a _) _)->a)
72 $ Prelude.filter (bindElem tag) content
73
74bindElem tag (CElem (Elem (N n) _ _) _) | n==tag = True
75bindElem _ _ = False
76
77hasElem tag content =
78 not . Prelude.null . Prelude.filter (bindElem tag) $ content
79
80unattr (AttValue as) = listToMaybe $ Prelude.concatMap left as
81 where left (Left x) = [x]
82 left _ = []
83
84astring (AttValue [Left s]) = [s]
85
86tagcontent tag content = Prelude.concatMap (\(CElem (Elem _ _ c) _)->c)
87 $ Prelude.filter (bindElem tag) content
88
89iqresult host id (Just rsrc) = L.unlines $
90 [ "<iq type='result' id='" <++> id <++> "'>"
91 , "<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'>"
92 , "<jid>" <++> id <++> "@" <++> host <++> "/" <++> rsrc <++> "</jid>"
93 , "</bind>"
94 , "</iq> "
95 ]
96iqresult host id Nothing = L.unlines $
97 [ "<iq type='result'"
98 , " id='" <++> id <++> "'"
99 , " from='" <++> host <++> "'"
100 , " /> "
101 ]
102
103iqresult_info host id mjid = L.unlines $
104 [ "<iq type='result'"
105 , " from='" <++> host <++> "'"
106 , case mjid of Just jid -> " to='" <++> jid <++> "'"
107 Nothing -> ""
108 , " id='" <++> id <++> "'>"
109 , " <query xmlns='http://jabber.org/protocol/disco#info'>"
110 , " <identity"
111 , " category='server'"
112 , " type='im'"
113 , " name='" <++> host <++> "'/>"
114 , " <feature var='http://jabber.org/protocol/disco#info'/>"
115 , " <feature var='http://jabber.org/protocol/disco#items'/>"
116 , " </query>"
117 , "</iq>"
118 ]
119
120iqresponse host (Elem _ attrs content) = do
121 id <- fmap pack (lookup (N "id") attrs >>= unattr)
122 typ <- fmap pack (lookup (N "type") attrs >>= unattr)
123 case typ of
124 "set" -> do
125 let string (CString _ s _) = [s]
126 mplus (do
127 rsrc <- listToMaybe . Prelude.concatMap string . tagcontent "resource" . tagcontent "bind" $ content
128 Just $ iqresult host id (Just (pack rsrc)) )
129 (do
130 guard (hasElem "session" content)
131 Just (iqresult host id Nothing))
132{-
133 - packet: Chunk "<iq type='get' id='purple5da6e3ed' to='localhost'><query xmlns='http://jabber.org/protocol/disco#items'/></iq>" Empty
134 - parsed-packet: Chunk "<iq type='get' id='purple5da6e3ed' to='localhost'><query xmlns='http://jabber.org/protocol/disco#items'/></iq>" Empty
135 - iq-get ([(N "type",get),(N "id",purple5da6e3ed),(N "to",localhost)],[CElem (Elem (N "query") [(N "xmlns",http://jabber.org/protocol/disco#items)] []) file stream at line 1 col 51])
136 - no respones
137 -}
138
139 "get" -> trace ("iq-get "++show (attrs,content)) $ do
140 xmlns <- fmap pack $
141 lookup (N "xmlns") (tagattrs "query" content)
142 >>= listToMaybe . astring
143 Just (iq_query_unavailable host id Nothing xmlns)
144 _ -> Nothing
145
146
147
148doCon st elem cont = do
149 let h = hOccursFst st :: Handle
150 host = "localhost"
151 hsend r = do
152 hPutStrLn h r
153 L.putStrLn $ "SENT:\n" <++> r
154
155 case elem of
156 OpenTag _ ->
157 hsend (greet host)
158 Element e@(Elem (N "iq") _ _) ->
159 case iqresponse host e of
160 Nothing -> trace "no respones" $ return ()
161 Just r -> hsend r
162 _ -> return () -- putStrLn $ "unhandled: "++show v
163
164 putStrLn (show elem)
165 cont ()
166
167instance Show Hax.ElemTag where
168 show _ = "elemtag"
169
170data XmppObject e i t c
171 = Element e
172 | ProcessingInstruction i
173 | OpenTag t
174 | CloseTag c
175 deriving Show
176
177streamName = QN (Namespace {nsPrefix= "stream" , nsURI="http://etherx.jabber.org/streams" }) "stream"
178
179xmppParse ls =
180 case xmlParseWith element ls of
181 (Right e,rs) -> (Right (Element e), rs)
182 (Left _,_) ->
183 case xmlParseWith elemOpenTag ls of
184 (Right e,rs) -> (Right (OpenTag e),rs)
185 (Left _,_) ->
186 case xmlParseWith (elemCloseTag streamName) ls of
187 (Right e,rs) -> (Right (CloseTag e),rs)
188 (Left _,_) ->
189 case xmlParseWith processinginstruction ls of
190 (Right e,rs) -> (Right (ProcessingInstruction e),rs)
191 (Left err,rs) -> (Left err,rs)
192
193
194
195main = do
196 let (start,dopkt) =
197 adaptServer ( xmlLex "stream" . unpack
198 , xmppParse)
199 (startCon,doCon)
200 doServer (5222 .*. HNil)
201 dopkt
202 start
203
204