~singpolyma/network-protocol-xmpp

ref: 61061478f5ec964dc8160d2317ee1bd6599f8d88 network-protocol-xmpp/Network/Protocol/XMPP/JID.hs -rw-r--r-- 3.5 KiB
61061478 — John Millikin Started major refactoring effort, starting with proper stringprep support in JID. 13 years ago
                                                                                
61061478 John Millikin
e17933b3 John Millikin
61061478 John Millikin
179ec160 John Millikin
61061478 John Millikin
e17933b3 John Millikin
61061478 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
-- 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/>.

{-# LANGUAGE OverloadedStrings #-}
module Network.Protocol.XMPP.JID
	( JID (..)
	, Node
	, Domain
	, Resource
	
	, strNode
	, strDomain
	, strResource
	
	, parseJID
	, formatJID
	) where
import qualified Data.Text as T
import qualified Text.StringPrep as SP
import Text.NamePrep (namePrepProfile)
import Data.Ranges (single)

newtype Node = Node { strNode :: T.Text }
newtype Domain = Domain { strDomain :: T.Text }
newtype Resource = Resource { strResource :: T.Text }

instance Show Node where
	showsPrec d (Node x) = showParen (d > 10) $
		showString "Node " . shows x

instance Show Domain where
	showsPrec d (Domain x) = showParen (d > 10) $
		showString "Domain " . shows x

instance Show Resource where
	showsPrec d (Resource x) = showParen (d > 10) $
		showString "Resource " . shows x

instance Eq Node where
	(==) = equaling (SP.runStringPrep nodePrep . strNode)

instance Eq Domain where
	(==) = equaling (SP.runStringPrep domainPrep . strDomain)

instance Eq Resource where
	(==) = equaling (SP.runStringPrep resourcePrep . strResource)

data JID = JID
	{ jidNode :: Maybe Node
	, jidDomain :: Domain
	, jidResource :: Maybe Resource
	}
	deriving (Eq)

instance Show JID where
	showsPrec d jid =  showParen (d > 10) $
		showString "JID " . shows (formatJID jid)

parseJID :: T.Text -> Maybe JID
parseJID str = maybeJID where
	(node, postNode) = case T.spanBy (/= '@') str of
		(x, y) -> if T.null y
			then ("", x)
			else (x, T.drop 1 y)
	(domain, resource) = case T.spanBy (/= '/') postNode of
		(x, y) -> if T.null y
			then (x, "")
			else (x, T.drop 1 $ y)
	mNode = if T.null node then Nothing else Just (Node node)
	mResource = if T.null resource then Nothing else Just (Resource resource)
	maybeJID = do
		SP.runStringPrep nodePrep node
		SP.runStringPrep domainPrep domain
		SP.runStringPrep resourcePrep resource
		Just $ JID mNode (Domain domain) mResource

formatJID :: JID -> T.Text
formatJID (JID node (Domain domain) resource) = formatted where
	formatted = T.concat [node', domain, resource']
	node' = maybe "" (\(Node x) -> T.append x "@") node
	resource' = maybe "" (\(Resource x) -> T.append "/" x) resource

nodePrep :: SP.StringPrepProfile
nodePrep = SP.Profile
	{ SP.maps = [SP.b1, SP.b2]
	, SP.shouldNormalize = True
	, SP.prohibited = [ SP.c11, SP.c12, SP.c21, SP.c22
	                  , SP.c3, SP.c4, SP.c5, SP.c6, SP.c7, SP.c8, SP.c9
	                  , map single $ "\"&'/:<>@"
	                  ]
	, SP.shouldCheckBidi = True
	}

domainPrep :: SP.StringPrepProfile
domainPrep = namePrepProfile False

resourcePrep :: SP.StringPrepProfile
resourcePrep = SP.Profile
	{ SP.maps = [SP.b1]
	, SP.shouldNormalize = True
	, SP.prohibited = [ SP.c12, SP.c21, SP.c22
	                  , SP.c3, SP.c4, SP.c5, SP.c6, SP.c7, SP.c8, SP.c9]
	, SP.shouldCheckBidi = True
	}

-- Similar to 'comparing'
equaling :: Eq a => (b -> a) -> b -> b -> Bool
equaling f x y = f x == f y