~singpolyma/network-protocol-xmpp

ref: 2f2cd141c32e115a5aa16834e7f01e0f94c61d49 network-protocol-xmpp/Network/Protocol/XMPP/Client.hs -rw-r--r-- 3.7 KiB
2f2cd141 — John Millikin Cleaned unused and duplicate imports, and added some type declarations. 13 years ago
                                                                                
e17933b3 John Millikin
5055a1d4 John Millikin
e17933b3 John Millikin
1445ab42 John Millikin
2f2cd141 John Millikin
1445ab42 John Millikin
e17933b3 John Millikin
dc0012c4 John Millikin
e17933b3 John Millikin
27bee8c4 John Millikin
1445ab42 John Millikin
27bee8c4 John Millikin
5055a1d4 John Millikin
27bee8c4 John Millikin
5055a1d4 John Millikin
e17933b3 John Millikin
055a7bfb John Millikin
e17933b3 John Millikin
5055a1d4 John Millikin
055a7bfb John Millikin
5055a1d4 John Millikin
e17933b3 John Millikin
400c78ab John Millikin
055a7bfb John Millikin
e17933b3 John Millikin
5055a1d4 John Millikin
055a7bfb John Millikin
27bee8c4 John Millikin
055a7bfb John Millikin
e17933b3 John Millikin
5055a1d4 John Millikin
1445ab42 John Millikin
5055a1d4 John Millikin
2f2cd141 John Millikin
1445ab42 John Millikin
2f2cd141 John Millikin
1445ab42 John Millikin
5055a1d4 John Millikin
e17933b3 John Millikin
27bee8c4 John Millikin
2f2cd141 John Millikin
27bee8c4 John Millikin
1445ab42 John Millikin
5055a1d4 John Millikin
1445ab42 John Millikin
5055a1d4 John Millikin
1445ab42 John Millikin
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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
{- Copyright (C) 2009 John Millikin <jmillikin@gmail.com>
   
   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   any later version.
   
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
   
   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
-}

module Network.Protocol.XMPP.Client (
	 ConnectedClient
	,Client
	,clientConnect
	,clientAuthenticate
	,clientBind
	,clientJID
	,clientServerJID
	,putTree
	,getTree
	) where

import Codec.Binary.Base64.String (encode)
import Network (HostName, PortID, connectTo)
import Text.XML.HXT.Arrow ((>>>))
import qualified Text.XML.HXT.Arrow as A
import Text.XML.HXT.DOM.TypeDefs (XmlTree)
import qualified Text.XML.HXT.DOM.XmlNode as XN

import Network.Protocol.XMPP.JID (JID, jidParse)
import Network.Protocol.XMPP.SASL (Mechanism, bestMechanism)
import qualified Network.Protocol.XMPP.Stream as S
import Network.Protocol.XMPP.Util (mkElement, mkQName)

data ConnectedClient = ConnectedClient JID S.Stream

data Client = Client {
	 clientJID        :: JID
	,clientServerJID  :: JID
	,clientStream     :: S.Stream
	}

type Username = String
type Password = String

clientConnect :: JID -> HostName -> PortID -> IO ConnectedClient
clientConnect jid host port = do
	handle <- connectTo host port
	stream <- S.beginStream jid handle
	return $ ConnectedClient jid stream

clientAuthenticate :: ConnectedClient -> JID -> Username -> Password -> IO Client
clientAuthenticate (ConnectedClient serverJID stream) jid username password = do
	let mechanisms = (advertisedMechanisms . S.streamFeatures) stream
	let saslMechanism = case bestMechanism mechanisms of
		Nothing -> error "No supported SASL mechanism"
		Just m -> m
	
	-- TODO: use detected mechanism
	let saslText = concat [(show jid), "\x00", username, "\x00", password]
	let b64Text = encode saslText
	
	S.putTree stream $ mkElement ("", "auth")
		[ ("", "xmlns", "urn:ietf:params:xml:ns:xmpp-sasl")
		 ,("", "mechanism", "PLAIN")]
		[XN.mkText b64Text]
	
	response <- S.getTree stream
	
	-- TODO: check if response is success or failure
	
	newStream <- S.restartStream stream
	return $ Client serverJID jid newStream

clientBind :: Client -> IO JID
clientBind c = do
	-- Bind
	-- TODO: request specific resource
	-- TODO: set ID to random value, and check bind result for JID
	-- TODO: return JID from server
	putTree c $ mkElement ("", "iq")
		[("", "type", "set")]
		[ mkElement ("", "bind")
		  	[("", "xmlns", "urn:ietf:params:xml:ns:xmpp-bind")]
		  	[]]
	
	bindResult <- getTree c
	let [rawJID] = A.runLA (
		A.deep (A.hasQName (mkQName "urn:ietf:params:xml:ns:xmpp-bind" "jid"))
		>>> A.getChildren
		>>> A.getText) bindResult
	let jid = case jidParse rawJID of
		Just x -> x
		_ -> error "Couldn't parse server's returned JID"
	
	-- Session
	putTree c $ mkElement ("", "iq")
		[("", "type", "set")]
		[mkElement ("", "session")
			[("", "xmlns", "urn:ietf:params:xml:ns:xmpp-session")]
			[]]
	
	getTree c
	
	putTree c $ mkElement ("", "presence") [] []
	getTree c
	return jid

advertisedMechanisms :: [S.StreamFeature] -> [Mechanism]
advertisedMechanisms [] = []
advertisedMechanisms (f:fs) = case f of
	(S.FeatureSASL ms) -> ms
	_ -> advertisedMechanisms fs

-------------------------------------------------------------------------------

putTree :: Client -> XmlTree -> IO ()
putTree = S.putTree . clientStream

getTree :: Client -> IO XmlTree
getTree = S.getTree . clientStream