diff options
author | joe <joe@jerkface.net> | 2014-02-12 20:35:52 -0500 |
---|---|---|
committer | joe <joe@jerkface.net> | 2014-02-12 20:35:52 -0500 |
commit | 7ccaa169bc2309df7df2db118dd646177867f2b0 (patch) | |
tree | 82b5e8f7e744f7d919b49ebcb0b0654def7d7d32 | |
parent | 5ad7794bcd86a0049d1e62cae2f04f6088a0ef34 (diff) |
Reply to pings with pongs.
-rw-r--r-- | Data/Conduit/Lift.hs | 526 | ||||
-rw-r--r-- | Presence/EventUtil.hs | 34 | ||||
-rw-r--r-- | Presence/Nesting.hs | 79 | ||||
-rw-r--r-- | xmppServer.hs | 64 |
4 files changed, 689 insertions, 14 deletions
diff --git a/Data/Conduit/Lift.hs b/Data/Conduit/Lift.hs new file mode 100644 index 00000000..341fce53 --- /dev/null +++ b/Data/Conduit/Lift.hs | |||
@@ -0,0 +1,526 @@ | |||
1 | |||
2 | |||
3 | {-# LANGUAGE RankNTypes #-} | ||
4 | -- | Allow monad transformers to be run/eval/exec in a section of conduit | ||
5 | -- rather then needing to run across the whole conduit. The circumvents many | ||
6 | -- of the problems with breaking the monad transformer laws. For more | ||
7 | -- information, see the announcement blog post: | ||
8 | -- <http://www.yesodweb.com/blog/2014/01/conduit-transformer-exception> | ||
9 | -- | ||
10 | -- This module was added in conduit 1.0.11. | ||
11 | module Data.Conduit.Lift ( | ||
12 | -- * ErrorT | ||
13 | errorC, | ||
14 | runErrorC, | ||
15 | catchErrorC, | ||
16 | -- liftCatchError, | ||
17 | |||
18 | -- * MaybeT | ||
19 | maybeC, | ||
20 | runMaybeC, | ||
21 | |||
22 | -- * ReaderT | ||
23 | readerC, | ||
24 | runReaderC, | ||
25 | |||
26 | -- * StateT | ||
27 | stateC, | ||
28 | runStateC, | ||
29 | evalStateC, | ||
30 | execStateC, | ||
31 | |||
32 | -- ** Strict | ||
33 | stateSC, | ||
34 | runStateSC, | ||
35 | evalStateSC, | ||
36 | execStateSC, | ||
37 | |||
38 | -- * WriterT | ||
39 | writerC, | ||
40 | runWriterC, | ||
41 | execWriterC, | ||
42 | |||
43 | -- ** Strict | ||
44 | writerSC, | ||
45 | runWriterSC, | ||
46 | execWriterSC, | ||
47 | |||
48 | -- * RWST | ||
49 | rwsC, | ||
50 | runRWSC, | ||
51 | evalRWSC, | ||
52 | execRWSC, | ||
53 | |||
54 | -- ** Strict | ||
55 | rwsSC, | ||
56 | runRWSSC, | ||
57 | evalRWSSC, | ||
58 | execRWSSC, | ||
59 | |||
60 | -- * Utilities | ||
61 | |||
62 | distribute | ||
63 | ) where | ||
64 | |||
65 | import Data.Conduit | ||
66 | import Data.Conduit.Internal (ConduitM (..), Pipe (..)) | ||
67 | |||
68 | import Control.Monad.Morph (hoist, lift, MFunctor(..), ) | ||
69 | import Control.Monad.Trans.Class (MonadTrans(..)) | ||
70 | |||
71 | import Data.Monoid (Monoid(..)) | ||
72 | |||
73 | |||
74 | import qualified Control.Monad.Trans.Error as E | ||
75 | import qualified Control.Monad.Trans.Maybe as M | ||
76 | import qualified Control.Monad.Trans.Reader as R | ||
77 | |||
78 | import qualified Control.Monad.Trans.State.Strict as SS | ||
79 | import qualified Control.Monad.Trans.Writer.Strict as WS | ||
80 | import qualified Control.Monad.Trans.RWS.Strict as RWSS | ||
81 | |||
82 | import qualified Control.Monad.Trans.State.Lazy as SL | ||
83 | import qualified Control.Monad.Trans.Writer.Lazy as WL | ||
84 | import qualified Control.Monad.Trans.RWS.Lazy as RWSL | ||
85 | |||
86 | |||
87 | catAwaitLifted | ||
88 | :: (Monad (t (ConduitM o1 o m)), Monad m, MonadTrans t) => | ||
89 | ConduitM i o1 (t (ConduitM o1 o m)) () | ||
90 | catAwaitLifted = go | ||
91 | where | ||
92 | go = do | ||
93 | x <- lift . lift $ await | ||
94 | case x of | ||
95 | Nothing -> return () | ||
96 | Just x2 -> do | ||
97 | yield x2 | ||
98 | go | ||
99 | |||
100 | catYieldLifted | ||
101 | :: (Monad (t (ConduitM i o1 m)), Monad m, MonadTrans t) => | ||
102 | ConduitM o1 o (t (ConduitM i o1 m)) () | ||
103 | catYieldLifted = go | ||
104 | where | ||
105 | go = do | ||
106 | x <- await | ||
107 | case x of | ||
108 | Nothing -> return () | ||
109 | Just x2 -> do | ||
110 | lift . lift $ yield x2 | ||
111 | go | ||
112 | |||
113 | |||
114 | distribute | ||
115 | :: (Monad (t (ConduitM b o m)), Monad m, Monad (t m), MonadTrans t, | ||
116 | MFunctor t) => | ||
117 | ConduitM b o (t m) () -> t (ConduitM b o m) () | ||
118 | distribute p = catAwaitLifted =$= hoist (hoist lift) p $$ catYieldLifted | ||
119 | |||
120 | -- | Run 'E.ErrorT' in the base monad | ||
121 | -- | ||
122 | -- Since 1.0.11 | ||
123 | errorC | ||
124 | :: (Monad m, Monad (t (E.ErrorT e m)), MonadTrans t, E.Error e, | ||
125 | MFunctor t) => | ||
126 | t m (Either e b) -> t (E.ErrorT e m) b | ||
127 | errorC p = do | ||
128 | x <- hoist lift p | ||
129 | lift $ E.ErrorT (return x) | ||
130 | |||
131 | -- | Run 'E.ErrorT' in the base monad | ||
132 | -- | ||
133 | -- Since 1.0.11 | ||
134 | runErrorC | ||
135 | :: (Monad m, E.Error e) => | ||
136 | ConduitM i o (E.ErrorT e m) r -> ConduitM i o m (Either e r) | ||
137 | runErrorC = | ||
138 | ConduitM . go . unConduitM | ||
139 | where | ||
140 | go (Done r) = Done (Right r) | ||
141 | go (PipeM mp) = PipeM $ do | ||
142 | eres <- E.runErrorT mp | ||
143 | return $ case eres of | ||
144 | Left e -> Done $ Left e | ||
145 | Right p -> go p | ||
146 | go (Leftover p i) = Leftover (go p) i | ||
147 | go (HaveOutput p f o) = HaveOutput (go p) (E.runErrorT f >> return ()) o | ||
148 | go (NeedInput x y) = NeedInput (go . x) (go . y) | ||
149 | {-# INLINABLE runErrorC #-} | ||
150 | |||
151 | -- | Catch an error in the base monad | ||
152 | -- | ||
153 | -- Since 1.0.11 | ||
154 | catchErrorC | ||
155 | :: (Monad m, E.Error e) => | ||
156 | ConduitM i o (E.ErrorT e m) r | ||
157 | -> (e -> ConduitM i o (E.ErrorT e m) r) | ||
158 | -> ConduitM i o (E.ErrorT e m) r | ||
159 | catchErrorC c0 h = | ||
160 | ConduitM $ go $ unConduitM c0 | ||
161 | where | ||
162 | go (Done r) = Done r | ||
163 | go (PipeM mp) = PipeM $ do | ||
164 | eres <- lift $ E.runErrorT mp | ||
165 | return $ case eres of | ||
166 | Left e -> unConduitM $ h e | ||
167 | Right p -> go p | ||
168 | go (Leftover p i) = Leftover (go p) i | ||
169 | go (HaveOutput p f o) = HaveOutput (go p) f o | ||
170 | go (NeedInput x y) = NeedInput (go . x) (go . y) | ||
171 | {-# INLINABLE catchErrorC #-} | ||
172 | |||
173 | -- | Wrap the base monad in 'M.MaybeT' | ||
174 | -- | ||
175 | -- Since 1.0.11 | ||
176 | maybeC | ||
177 | :: (Monad m, Monad (t (M.MaybeT m)), | ||
178 | MonadTrans t, | ||
179 | MFunctor t) => | ||
180 | t m (Maybe b) -> t (M.MaybeT m) b | ||
181 | maybeC p = do | ||
182 | x <- hoist lift p | ||
183 | lift $ M.MaybeT (return x) | ||
184 | {-# INLINABLE maybeC #-} | ||
185 | |||
186 | -- | Run 'M.MaybeT' in the base monad | ||
187 | -- | ||
188 | -- Since 1.0.11 | ||
189 | runMaybeC | ||
190 | :: Monad m => | ||
191 | ConduitM i o (M.MaybeT m) r -> ConduitM i o m (Maybe r) | ||
192 | runMaybeC = | ||
193 | ConduitM . go . unConduitM | ||
194 | where | ||
195 | go (Done r) = Done (Just r) | ||
196 | go (PipeM mp) = PipeM $ do | ||
197 | mres <- M.runMaybeT mp | ||
198 | return $ case mres of | ||
199 | Nothing -> Done Nothing | ||
200 | Just p -> go p | ||
201 | go (Leftover p i) = Leftover (go p) i | ||
202 | go (HaveOutput p c o) = HaveOutput (go p) (M.runMaybeT c >> return ()) o | ||
203 | go (NeedInput x y) = NeedInput (go . x) (go . y) | ||
204 | {-# INLINABLE runMaybeC #-} | ||
205 | |||
206 | -- | Wrap the base monad in 'R.ReaderT' | ||
207 | -- | ||
208 | -- Since 1.0.11 | ||
209 | readerC | ||
210 | :: (Monad m, Monad (t1 (R.ReaderT t m)), | ||
211 | MonadTrans t1, | ||
212 | MFunctor t1) => | ||
213 | (t -> t1 m b) -> t1 (R.ReaderT t m) b | ||
214 | readerC k = do | ||
215 | i <- lift R.ask | ||
216 | hoist lift (k i) | ||
217 | {-# INLINABLE readerC #-} | ||
218 | |||
219 | -- | Run 'R.ReaderT' in the base monad | ||
220 | -- | ||
221 | -- Since 1.0.11 | ||
222 | runReaderC | ||
223 | :: Monad m => | ||
224 | r -> ConduitM i o (R.ReaderT r m) res -> ConduitM i o m res | ||
225 | runReaderC r = hoist (`R.runReaderT` r) | ||
226 | {-# INLINABLE runReaderC #-} | ||
227 | |||
228 | |||
229 | -- | Wrap the base monad in 'SL.StateT' | ||
230 | -- | ||
231 | -- Since 1.0.11 | ||
232 | stateC | ||
233 | :: (Monad m, Monad (t1 (SL.StateT t m)), | ||
234 | MonadTrans t1, | ||
235 | MFunctor t1) => | ||
236 | (t -> t1 m (b, t)) -> t1 (SL.StateT t m) b | ||
237 | stateC k = do | ||
238 | s <- lift SL.get | ||
239 | (r, s') <- hoist lift (k s) | ||
240 | lift (SL.put s') | ||
241 | return r | ||
242 | {-# INLINABLE stateC #-} | ||
243 | |||
244 | thread :: Monad m | ||
245 | => (r -> s -> res) | ||
246 | -> (forall a. t m a -> s -> m (a, s)) | ||
247 | -> s | ||
248 | -> ConduitM i o (t m) r | ||
249 | -> ConduitM i o m res | ||
250 | thread toRes runM s0 = | ||
251 | ConduitM . go s0 . unConduitM | ||
252 | where | ||
253 | go s (Done r) = Done (toRes r s) | ||
254 | go s (PipeM mp) = PipeM $ do | ||
255 | (p, s') <- runM mp s | ||
256 | return $ go s' p | ||
257 | go s (Leftover p i) = Leftover (go s p) i | ||
258 | go s (NeedInput x y) = NeedInput (go s . x) (go s . y) | ||
259 | go s (HaveOutput p f o) = HaveOutput (go s p) (runM f s >> return ()) o | ||
260 | {-# INLINABLE thread #-} | ||
261 | |||
262 | -- | Run 'SL.StateT' in the base monad | ||
263 | -- | ||
264 | -- Since 1.0.11 | ||
265 | runStateC | ||
266 | :: Monad m => | ||
267 | s -> ConduitM i o (SL.StateT s m) r -> ConduitM i o m (r, s) | ||
268 | runStateC = thread (,) SL.runStateT | ||
269 | {-# INLINABLE runStateC #-} | ||
270 | |||
271 | -- | Evaluate 'SL.StateT' in the base monad | ||
272 | -- | ||
273 | -- Since 1.0.11 | ||
274 | evalStateC | ||
275 | :: Monad m => | ||
276 | s -> ConduitM i o (SL.StateT s m) r -> ConduitM i o m r | ||
277 | evalStateC s p = fmap fst $ runStateC s p | ||
278 | {-# INLINABLE evalStateC #-} | ||
279 | |||
280 | -- | Execute 'SL.StateT' in the base monad | ||
281 | -- | ||
282 | -- Since 1.0.11 | ||
283 | execStateC | ||
284 | :: Monad m => | ||
285 | s -> ConduitM i o (SL.StateT s m) r -> ConduitM i o m s | ||
286 | execStateC s p = fmap snd $ runStateC s p | ||
287 | {-# INLINABLE execStateC #-} | ||
288 | |||
289 | |||
290 | -- | Wrap the base monad in 'SS.StateT' | ||
291 | -- | ||
292 | -- Since 1.0.11 | ||
293 | stateSC | ||
294 | :: (Monad m, Monad (t1 (SS.StateT t m)), | ||
295 | MonadTrans t1, | ||
296 | MFunctor t1) => | ||
297 | (t -> t1 m (b, t)) -> t1 (SS.StateT t m) b | ||
298 | stateSC k = do | ||
299 | s <- lift SS.get | ||
300 | (r, s') <- hoist lift (k s) | ||
301 | lift (SS.put s') | ||
302 | return r | ||
303 | {-# INLINABLE stateSC #-} | ||
304 | |||
305 | -- | Run 'SS.StateT' in the base monad | ||
306 | -- | ||
307 | -- Since 1.0.11 | ||
308 | runStateSC | ||
309 | :: Monad m => | ||
310 | s -> ConduitM i o (SS.StateT s m) r -> ConduitM i o m (r, s) | ||
311 | runStateSC = thread (,) SS.runStateT | ||
312 | {-# INLINABLE runStateSC #-} | ||
313 | |||
314 | -- | Evaluate 'SS.StateT' in the base monad | ||
315 | -- | ||
316 | -- Since 1.0.11 | ||
317 | evalStateSC | ||
318 | :: Monad m => | ||
319 | s -> ConduitM i o (SS.StateT s m) r -> ConduitM i o m r | ||
320 | evalStateSC s p = fmap fst $ runStateSC s p | ||
321 | {-# INLINABLE evalStateSC #-} | ||
322 | |||
323 | -- | Execute 'SS.StateT' in the base monad | ||
324 | -- | ||
325 | -- Since 1.0.11 | ||
326 | execStateSC | ||
327 | :: Monad m => | ||
328 | s -> ConduitM i o (SS.StateT s m) r -> ConduitM i o m s | ||
329 | execStateSC s p = fmap snd $ runStateSC s p | ||
330 | {-# INLINABLE execStateSC #-} | ||
331 | |||
332 | |||
333 | -- | Wrap the base monad in 'WL.WriterT' | ||
334 | -- | ||
335 | -- Since 1.0.11 | ||
336 | writerC | ||
337 | :: (Monad m, Monad (t (WL.WriterT w m)), MonadTrans t, Monoid w, | ||
338 | MFunctor t) => | ||
339 | t m (b, w) -> t (WL.WriterT w m) b | ||
340 | writerC p = do | ||
341 | (r, w) <- hoist lift p | ||
342 | lift $ WL.tell w | ||
343 | return r | ||
344 | {-# INLINABLE writerC #-} | ||
345 | |||
346 | -- | Run 'WL.WriterT' in the base monad | ||
347 | -- | ||
348 | -- Since 1.0.11 | ||
349 | runWriterC | ||
350 | :: (Monad m, Monoid w) => | ||
351 | ConduitM i o (WL.WriterT w m) r -> ConduitM i o m (r, w) | ||
352 | runWriterC = thread (,) run mempty | ||
353 | where | ||
354 | run m w = do | ||
355 | (a, w') <- WL.runWriterT m | ||
356 | return (a, w `mappend` w') | ||
357 | {-# INLINABLE runWriterC #-} | ||
358 | |||
359 | -- | Execute 'WL.WriterT' in the base monad | ||
360 | -- | ||
361 | -- Since 1.0.11 | ||
362 | execWriterC | ||
363 | :: (Monad m, Monoid w) => | ||
364 | ConduitM i o (WL.WriterT w m) r -> ConduitM i o m w | ||
365 | execWriterC p = fmap snd $ runWriterC p | ||
366 | {-# INLINABLE execWriterC #-} | ||
367 | |||
368 | |||
369 | -- | Wrap the base monad in 'WS.WriterT' | ||
370 | -- | ||
371 | -- Since 1.0.11 | ||
372 | writerSC | ||
373 | :: (Monad m, Monad (t (WS.WriterT w m)), MonadTrans t, Monoid w, | ||
374 | MFunctor t) => | ||
375 | t m (b, w) -> t (WS.WriterT w m) b | ||
376 | writerSC p = do | ||
377 | (r, w) <- hoist lift p | ||
378 | lift $ WS.tell w | ||
379 | return r | ||
380 | {-# INLINABLE writerSC #-} | ||
381 | |||
382 | -- | Run 'WS.WriterT' in the base monad | ||
383 | -- | ||
384 | -- Since 1.0.11 | ||
385 | runWriterSC | ||
386 | :: (Monad m, Monoid w) => | ||
387 | ConduitM i o (WS.WriterT w m) r -> ConduitM i o m (r, w) | ||
388 | runWriterSC = thread (,) run mempty | ||
389 | where | ||
390 | run m w = do | ||
391 | (a, w') <- WS.runWriterT m | ||
392 | return (a, w `mappend` w') | ||
393 | {-# INLINABLE runWriterSC #-} | ||
394 | |||
395 | -- | Execute 'WS.WriterT' in the base monad | ||
396 | -- | ||
397 | -- Since 1.0.11 | ||
398 | execWriterSC | ||
399 | :: (Monad m, Monoid w) => | ||
400 | ConduitM i o (WS.WriterT w m) r -> ConduitM i o m w | ||
401 | execWriterSC p = fmap snd $ runWriterSC p | ||
402 | {-# INLINABLE execWriterSC #-} | ||
403 | |||
404 | |||
405 | -- | Wrap the base monad in 'RWSL.RWST' | ||
406 | -- | ||
407 | -- Since 1.0.11 | ||
408 | rwsC | ||
409 | :: (Monad m, Monad (t1 (RWSL.RWST t w t2 m)), MonadTrans t1, | ||
410 | Monoid w, MFunctor t1) => | ||
411 | (t -> t2 -> t1 m (b, t2, w)) -> t1 (RWSL.RWST t w t2 m) b | ||
412 | rwsC k = do | ||
413 | i <- lift RWSL.ask | ||
414 | s <- lift RWSL.get | ||
415 | (r, s', w) <- hoist lift (k i s) | ||
416 | lift $ do | ||
417 | RWSL.put s' | ||
418 | RWSL.tell w | ||
419 | return r | ||
420 | {-# INLINABLE rwsC #-} | ||
421 | |||
422 | -- | Run 'RWSL.RWST' in the base monad | ||
423 | -- | ||
424 | -- Since 1.0.11 | ||
425 | runRWSC | ||
426 | :: (Monad m, Monoid w) => | ||
427 | r | ||
428 | -> s | ||
429 | -> ConduitM i o (RWSL.RWST r w s m) res | ||
430 | -> ConduitM i o m (res, s, w) | ||
431 | runRWSC r s0 = thread toRes run (s0, mempty) | ||
432 | where | ||
433 | toRes a (s, w) = (a, s, w) | ||
434 | run m (s, w) = do | ||
435 | (res, s', w') <- RWSL.runRWST m r s | ||
436 | return (res, (s', w `mappend` w')) | ||
437 | {-# INLINABLE runRWSC #-} | ||
438 | |||
439 | -- | Evaluate 'RWSL.RWST' in the base monad | ||
440 | -- | ||
441 | -- Since 1.0.11 | ||
442 | evalRWSC | ||
443 | :: (Monad m, Monoid w) => | ||
444 | r | ||
445 | -> s | ||
446 | -> ConduitM i o (RWSL.RWST r w s m) res | ||
447 | -> ConduitM i o m (res, w) | ||
448 | evalRWSC i s p = fmap f $ runRWSC i s p | ||
449 | where f x = let (r, _, w) = x in (r, w) | ||
450 | {-# INLINABLE evalRWSC #-} | ||
451 | |||
452 | -- | Execute 'RWSL.RWST' in the base monad | ||
453 | -- | ||
454 | -- Since 1.0.11 | ||
455 | execRWSC | ||
456 | :: (Monad m, Monoid w) => | ||
457 | r | ||
458 | -> s | ||
459 | -> ConduitM i o (RWSL.RWST r w s m) res | ||
460 | -> ConduitM i o m (s, w) | ||
461 | execRWSC i s p = fmap f $ runRWSC i s p | ||
462 | where f x = let (_, s2, w2) = x in (s2, w2) | ||
463 | {-# INLINABLE execRWSC #-} | ||
464 | |||
465 | |||
466 | -- | Wrap the base monad in 'RWSS.RWST' | ||
467 | -- | ||
468 | -- Since 1.0.11 | ||
469 | rwsSC | ||
470 | :: (Monad m, Monad (t1 (RWSS.RWST t w t2 m)), MonadTrans t1, | ||
471 | Monoid w, MFunctor t1) => | ||
472 | (t -> t2 -> t1 m (b, t2, w)) -> t1 (RWSS.RWST t w t2 m) b | ||
473 | rwsSC k = do | ||
474 | i <- lift RWSS.ask | ||
475 | s <- lift RWSS.get | ||
476 | (r, s', w) <- hoist lift (k i s) | ||
477 | lift $ do | ||
478 | RWSS.put s' | ||
479 | RWSS.tell w | ||
480 | return r | ||
481 | {-# INLINABLE rwsSC #-} | ||
482 | |||
483 | -- | Run 'RWSS.RWST' in the base monad | ||
484 | -- | ||
485 | -- Since 1.0.11 | ||
486 | runRWSSC | ||
487 | :: (Monad m, Monoid w) => | ||
488 | r | ||
489 | -> s | ||
490 | -> ConduitM i o (RWSS.RWST r w s m) res | ||
491 | -> ConduitM i o m (res, s, w) | ||
492 | runRWSSC r s0 = thread toRes run (s0, mempty) | ||
493 | where | ||
494 | toRes a (s, w) = (a, s, w) | ||
495 | run m (s, w) = do | ||
496 | (res, s', w') <- RWSS.runRWST m r s | ||
497 | return (res, (s', w `mappend` w')) | ||
498 | {-# INLINABLE runRWSSC #-} | ||
499 | |||
500 | -- | Evaluate 'RWSS.RWST' in the base monad | ||
501 | -- | ||
502 | -- Since 1.0.11 | ||
503 | evalRWSSC | ||
504 | :: (Monad m, Monoid w) => | ||
505 | r | ||
506 | -> s | ||
507 | -> ConduitM i o (RWSS.RWST r w s m) res | ||
508 | -> ConduitM i o m (res, w) | ||
509 | evalRWSSC i s p = fmap f $ runRWSSC i s p | ||
510 | where f x = let (r, _, w) = x in (r, w) | ||
511 | {-# INLINABLE evalRWSSC #-} | ||
512 | |||
513 | -- | Execute 'RWSS.RWST' in the base monad | ||
514 | -- | ||
515 | -- Since 1.0.11 | ||
516 | execRWSSC | ||
517 | :: (Monad m, Monoid w) => | ||
518 | r | ||
519 | -> s | ||
520 | -> ConduitM i o (RWSS.RWST r w s m) res | ||
521 | -> ConduitM i o m (s, w) | ||
522 | execRWSSC i s p = fmap f $ runRWSSC i s p | ||
523 | where f x = let (_, s2, w2) = x in (s2, w2) | ||
524 | {-# INLINABLE execRWSSC #-} | ||
525 | |||
526 | |||
diff --git a/Presence/EventUtil.hs b/Presence/EventUtil.hs index e62f8afc..bdea9fa2 100644 --- a/Presence/EventUtil.hs +++ b/Presence/EventUtil.hs | |||
@@ -3,6 +3,7 @@ module EventUtil where | |||
3 | 3 | ||
4 | import Control.Monad | 4 | import Control.Monad |
5 | import Data.XML.Types as XML | 5 | import Data.XML.Types as XML |
6 | import qualified Data.List as List | ||
6 | 7 | ||
7 | getStreamName (EventBeginElement name _) = name | 8 | getStreamName (EventBeginElement name _) = name |
8 | 9 | ||
@@ -21,3 +22,36 @@ elementAttrs _ _ = mzero | |||
21 | streamP name = Name name (Just "http://etherx.jabber.org/streams") (Just "stream") | 22 | streamP name = Name name (Just "http://etherx.jabber.org/streams") (Just "stream") |
22 | 23 | ||
23 | attr name value = (name,[ContentText value]) | 24 | attr name value = (name,[ContentText value]) |
25 | |||
26 | isServerIQOf (EventBeginElement name attrs) testType | ||
27 | | name=="{jabber:server}iq" | ||
28 | && matchAttrib "type" testType attrs | ||
29 | = True | ||
30 | isServerIQOf _ _ = False | ||
31 | |||
32 | matchAttrib name value attrs = | ||
33 | case List.find ( (==name) . fst) attrs of | ||
34 | Just (_,[ContentText x]) | x==value -> True | ||
35 | Just (_,[ContentEntity x]) | x==value -> True | ||
36 | _ -> False | ||
37 | |||
38 | lookupAttrib name attrs = | ||
39 | case List.find ( (==name) . fst) attrs of | ||
40 | Just (_,[ContentText x]) -> Just x | ||
41 | Just (_,[ContentEntity x]) -> Just x | ||
42 | _ -> Nothing | ||
43 | |||
44 | tagAttrs (EventBeginElement _ xs) = xs | ||
45 | tagAttrs _ = [] | ||
46 | |||
47 | |||
48 | {- | ||
49 | iqTypeSet = "set" | ||
50 | iqTypeGet = "get" | ||
51 | iqTypeResult = "result" | ||
52 | iqTypeError = "error" | ||
53 | -} | ||
54 | |||
55 | |||
56 | tagName (EventBeginElement n _) = n | ||
57 | tagName _ = "" | ||
diff --git a/Presence/Nesting.hs b/Presence/Nesting.hs new file mode 100644 index 00000000..24f9baad --- /dev/null +++ b/Presence/Nesting.hs | |||
@@ -0,0 +1,79 @@ | |||
1 | {-# LANGUAGE OverloadedStrings #-} | ||
2 | {-# LANGUAGE FlexibleContexts #-} | ||
3 | module Nesting where | ||
4 | |||
5 | import Data.Conduit | ||
6 | import Data.Conduit.Lift | ||
7 | import Data.XML.Types | ||
8 | import qualified Data.Text as S | ||
9 | import Control.Monad.State | ||
10 | import qualified Data.List as List | ||
11 | |||
12 | type Lang = S.Text | ||
13 | |||
14 | data StrictList a = a :! !(StrictList a) | StrictNil | ||
15 | |||
16 | data XMLState = XMLState { | ||
17 | nestingLevel :: Int, | ||
18 | langStack :: StrictList (Int,Lang) | ||
19 | } | ||
20 | |||
21 | type NestingXML o m a = ConduitM Event o (StateT XMLState m) a | ||
22 | |||
23 | doNestingXML :: Monad m => NestingXML o m r -> ConduitM Event o m r | ||
24 | doNestingXML m = | ||
25 | evalStateC (XMLState 0 StrictNil) (trackNesting =$= m) | ||
26 | |||
27 | nesting :: Monad m => NestingXML o m Int | ||
28 | nesting = lift $ (return . nestingLevel) =<< get | ||
29 | |||
30 | |||
31 | trackNesting :: Monad m => ConduitM Event Event (StateT XMLState m) () | ||
32 | trackNesting = awaitForever doit | ||
33 | where | ||
34 | doit xml = do | ||
35 | XMLState lvl langs <- lift get | ||
36 | lift . put $ case xml of | ||
37 | EventBeginElement _ attrs -> | ||
38 | case lookupLang attrs of | ||
39 | Nothing -> XMLState (lvl+1) langs | ||
40 | Just lang -> XMLState (lvl+1) ( (lvl+1,lang) :! langs) | ||
41 | EventEndElement _ -> | ||
42 | case langs of | ||
43 | (llvl,_) :! ls | llvl==lvl -> XMLState (lvl-1) ls | ||
44 | _ | otherwise -> XMLState (lvl-1) langs | ||
45 | _ -> XMLState lvl langs | ||
46 | yield xml | ||
47 | |||
48 | |||
49 | lookupLang attrs = | ||
50 | case List.find ( (=="xml:lang") . fst) attrs of | ||
51 | Just (_,[ContentText x]) -> Just x | ||
52 | Just (_,[ContentEntity x]) -> Just x | ||
53 | _ -> Nothing | ||
54 | |||
55 | |||
56 | awaitCloser :: Monad m => Int -> NestingXML o m () | ||
57 | awaitCloser lvl = do | ||
58 | fix $ \loop -> do | ||
59 | lvl' <- nesting | ||
60 | when (lvl' >= lvl) $ do | ||
61 | xml <- await | ||
62 | maybe (return ()) (const loop) xml | ||
63 | |||
64 | withXML f = await >>= maybe (return ()) f | ||
65 | |||
66 | nextElement :: Monad m => NestingXML o m (Maybe Event) | ||
67 | nextElement = do | ||
68 | lvl <- nesting | ||
69 | fix $ \loop -> do | ||
70 | xml <- await | ||
71 | case xml of | ||
72 | Nothing -> return Nothing | ||
73 | Just (EventBeginElement _ _) -> do | ||
74 | return xml | ||
75 | Just _ -> do | ||
76 | lvl' <- nesting | ||
77 | if (lvl'>=lvl) then loop | ||
78 | else return Nothing | ||
79 | |||
diff --git a/xmppServer.hs b/xmppServer.hs index 85e0cb5c..f91c20ce 100644 --- a/xmppServer.hs +++ b/xmppServer.hs | |||
@@ -23,14 +23,14 @@ import Data.Conduit.Blaze (builderToByteStringFlush) | |||
23 | import qualified Text.XML.Stream.Render as XML | 23 | import qualified Text.XML.Stream.Render as XML |
24 | import qualified Text.XML.Stream.Parse as XML | 24 | import qualified Text.XML.Stream.Parse as XML |
25 | import Data.XML.Types as XML | 25 | import Data.XML.Types as XML |
26 | import Data.Maybe (catMaybes) | 26 | import Data.Maybe (catMaybes,fromJust) |
27 | import Data.Monoid ( (<>) ) | 27 | import Data.Monoid ( (<>) ) |
28 | import Data.Text (Text) | 28 | import Data.Text (Text) |
29 | import qualified Data.Text as Text (pack) | 29 | import qualified Data.Text as Text (pack) |
30 | 30 | ||
31 | import qualified Control.Concurrent.STM.UpdateStream as Slotted | 31 | import qualified Control.Concurrent.STM.UpdateStream as Slotted |
32 | import ControlMaybe | 32 | import ControlMaybe |
33 | import NestingXML | 33 | import Nesting |
34 | import EventUtil | 34 | import EventUtil |
35 | import Server | 35 | import Server |
36 | 36 | ||
@@ -80,6 +80,17 @@ type WriteCommand = ByteString -> IO Bool | |||
80 | 80 | ||
81 | data Stanza | 81 | data Stanza |
82 | = UnrecognizedStanza { stanzaChan :: TChan (Maybe XML.Event) } | 82 | = UnrecognizedStanza { stanzaChan :: TChan (Maybe XML.Event) } |
83 | | PingStanza { stanzaId :: Maybe Text | ||
84 | , stanzaChan :: TChan (Maybe XML.Event) } | ||
85 | | PongStanza { -- stanzaId :: Maybe Text | ||
86 | stanzaChan :: TChan (Maybe XML.Event) } | ||
87 | |||
88 | copyToChannel f chan = awaitForever copy | ||
89 | where | ||
90 | copy x = do | ||
91 | liftIO . atomically $ writeTChan chan (f x) | ||
92 | yield x | ||
93 | |||
83 | 94 | ||
84 | prettyPrint prefix xs = | 95 | prettyPrint prefix xs = |
85 | liftIO $ | 96 | liftIO $ |
@@ -88,6 +99,19 @@ prettyPrint prefix xs = | |||
88 | =$= CB.lines | 99 | =$= CB.lines |
89 | $$ CL.mapM_ (wlogb . (prefix <>)) | 100 | $$ CL.mapM_ (wlogb . (prefix <>)) |
90 | 101 | ||
102 | grockStanzaIQGet :: Monad m => XML.Event -> NestingXML o m (Maybe (TChan (Maybe Event) -> Stanza)) | ||
103 | grockStanzaIQGet stanza = do | ||
104 | let mid = lookupAttrib "id" (tagAttrs stanza) | ||
105 | -- mfrom = lookupAttrib "from" (tagAttrs stanza) | ||
106 | mtag <- nextElement | ||
107 | flip (maybe $ return Nothing) mtag $ \tag -> do | ||
108 | case tagName tag of | ||
109 | "{urn:xmpp:ping}ping" -> do | ||
110 | return $ Just (PingStanza mid) | ||
111 | _ -> return Nothing | ||
112 | |||
113 | ioWriteChan c v = liftIO . atomically $ writeTChan c v | ||
114 | |||
91 | xmppInbound :: ConnectionKey -> FlagCommand | 115 | xmppInbound :: ConnectionKey -> FlagCommand |
92 | -> Source IO XML.Event | 116 | -> Source IO XML.Event |
93 | -> TChan Stanza | 117 | -> TChan Stanza |
@@ -99,18 +123,30 @@ xmppInbound k pingflag src stanzas output = doNestingXML $ do | |||
99 | whenJust nextElement $ \xml -> do | 123 | whenJust nextElement $ \xml -> do |
100 | withJust (elementAttrs "stream" xml) $ \stream_attrs -> do | 124 | withJust (elementAttrs "stream" xml) $ \stream_attrs -> do |
101 | fix $ \loop -> do | 125 | fix $ \loop -> do |
102 | -- liftIO . wlog $ "waiting for stanza." | 126 | -- liftIO . wlog $ "waiting for stanza." |
103 | chan <- liftIO $ atomically newTChan | 127 | chan <- liftIO $ atomically newTChan |
104 | whenJust nextElement $ \stanza -> do | 128 | whenJust nextElement $ \stanza -> do |
105 | stanza_lvl <- nesting | 129 | stanza_lvl <- nesting |
106 | liftIO . atomically $ writeTChan chan (Just stanza) | 130 | ioWriteChan chan (Just stanza) |
107 | 131 | copyToChannel Just chan =$= do | |
108 | liftIO . atomically $ writeTChan stanzas $ | 132 | dispatch <- |
109 | UnrecognizedStanza chan | 133 | case () of |
110 | doUntilCloser stanza_lvl $ \xml -> do | 134 | _ | stanza `isServerIQOf` "get" -> grockStanzaIQGet stanza |
111 | liftIO . atomically $ writeTChan chan (Just xml) | 135 | _ -> return $ Just UnrecognizedStanza |
112 | liftIO . atomically $ writeTChan chan Nothing | 136 | flip (maybe $ return ()) dispatch $ \dispatch -> |
113 | loop | 137 | case dispatch chan of |
138 | d@(PingStanza {}) -> do | ||
139 | let to = "todo" | ||
140 | from = "todo" | ||
141 | let pong = peerPong (stanzaId d) to from | ||
142 | pongChan <- liftIO $ atomically newTChan | ||
143 | ioWriteChan output (PongStanza pongChan) | ||
144 | mapM_ (ioWriteChan pongChan . Just) pong | ||
145 | ioWriteChan pongChan Nothing | ||
146 | disp -> ioWriteChan stanzas disp | ||
147 | awaitCloser stanza_lvl | ||
148 | ioWriteChan chan Nothing | ||
149 | loop | ||
114 | 150 | ||
115 | 151 | ||
116 | chanContents :: TChan x -> IO [x] | 152 | chanContents :: TChan x -> IO [x] |