packages feed

pontarius-xmpp-0.0.3.0: Network/XMPP/JID.hs

-- | Module:      $Header$
--   Description: JabberID (JID) data type and utility functions
--   Copyright:   Copyright © 2010-2011 Jon Kristensen
--   License:     BSD-3
--   
--   Maintainer:  info@pontarius.org
--   Stability:   unstable
--   Portability: portable
--   
--   JIDs are written in the format of `node@server/resource'. An example of a
--   JID is `jonkri@jabber.org'.
--   
--   The node identifier is the part before the `@' character in Jabber IDs.
--   Node names are optional. The server identifier is the part after the `@'
--   character in Jabber IDs (and before the `/' character). The server
--   identifier is the only required field of a JID. The server identifier MAY
--   be an IP address but SHOULD be a fully qualified domain name. The resource
--   identifier is the part after the `/' character in Jabber IDs. Like with
--   node names, the resource identifier is optional.
--   
--   A JID without a resource identifier (i.e. a JID in the form of
--   `node@server') is called `bare JID'. A JID with a resource identifier is
--   called `full JID'.

-- Node identifiers MUST be formatted in such a way so that the Nodeprep profile
-- (see RFC 3920: XMPP Core, Appendix A) of RFC 3454: Preparation of
-- Internationalized Strings (`stringprep') can be applied without failing.
-- Servers MUST and clients SHOULD apply the Nodeprep profile to node names
-- prior to comparing them. Node identifiers MUST NOT be more than 1023 bytes in
-- length.
--
-- If it is a domain name it MUST be an `internationalized domain name' as
-- defined in RFC 3490: Internationalizing Domain Names in Applications (IDNA),
-- to which RFC 3491: Nameprep: A Stringprep Profile for Internationalized
-- Domain Names (IDN) can be applied without failing. Servers MUST and clients
-- SHOULD first apply the Nameprep profile to the domain names prior to
-- comparing them. Like with node names, server identifiers MUST NOT be more
-- than 1023 bytes in length.
--
-- A resource identifier has to be formatted in such a way so that the
-- Resourceprep profile of RFC 3454: Preparation of Internationalized Strings
-- (`stringprep') can be applied without failing. Servers MUST and clients
-- SHOULD first apply the Resourceprep profile (see RFC 3920: XMPP Core,
-- Appendix B) to resource names prior to comparing them. The resource
-- identifier MUST MOT be more than 1023 bytes in length.
--
-- Given the length of the `@' and `/' characters as well as the restrictions
-- imposed on the node, server and resource identifiers, a JID will never be
-- longer than 3071 bytes.

-- TODO: Make the regular expression only match valid JIDs.
-- TODO: Use Perl regular expressions to use non-capturing groups with "(?:"?
-- TODO: Validate the input in the jid and stringToJID functions.

module Network.XMPP.JID  ( JID (jidNode, jidServer, jidResource)
                         , jid
                         , jidIsFull
                         , jidIsBare
                         , stringToJID
                         , jidToString ) where

import Text.Regex.Posix ((=~))


-- JID is a data type that has to be constructed in this module using either jid
-- or stringToJID.

data JID = JID { jidNode :: Maybe String
               , jidServer :: String
               , jidResource :: Maybe String } deriving (Eq, Show)


-- | Simple function to construct a JID. We will add validation to this function
--   in a later release.

jid :: Maybe String -> String -> Maybe String -> JID
jid n s r = JID { jidNode = n, jidServer = s, jidResource = r }


-- | Converts a (JID) String to a JID record.

stringToJID :: String -> Maybe JID

stringToJID string = matchToJID $ string =~ "^(([^@]+)@)?([^/]+)(/(.+))?$"
  where
    matchToJID [[_, _, "", server, _, ""]] =
      Just JID { jidNode = Nothing, jidServer = server, jidResource = Nothing }
    matchToJID [[_, _, node, server, _, ""]] =
      Just JID { jidNode = Just node
               , jidServer = server
               , jidResource = Nothing }
    matchToJID [[_, _, "", server, _, resource]] =
      Just JID { jidNode = Nothing
               , jidServer = server
               , jidResource = Just resource }
    matchToJID [[_, _, node, server, _, resource]] =
      Just JID { jidNode = Just node
               , jidServer = server
               , jidResource = Just resource }
    matchToJID _ = Nothing


-- | Converts a JID to a String.

jidToString :: JID -> String

jidToString JID { jidNode = n, jidServer = s, jidResource = r }
  | n == Nothing && r == Nothing = s
  | r == Nothing                 = let Just n' = n in n' ++ "@" ++ s
  | n == Nothing                 = let Just r' = r in s ++ "/" ++ r'
  | otherwise                    = let Just n' = n; Just r' = r
                                   in n' ++ "@" ++ s ++ "/" ++ r'


-- | JIDs are written in the format of `node@server/resource'. A JID without a
--   resource identifier (i.e. a JID in the form of `server' or `node@server')
--   is called a `bare JID'. A JID with a resource identifier is called `full
--   JID'. This function returns True if the JID is `bare' and False otherwise.

jidIsBare :: JID -> Bool
jidIsBare j | jidResource j == Nothing = True
            | otherwise                = False


-- | JIDs are written in the format of `node@server/resource'. A JID without a
--   resource identifier (i.e. a JID in the form of `server' or `node@server')
--   is called a `bare JID'. A JID with a resource identifier is called `full
--   JID'. This function returns True if the JID is `full' and False otherwise.

-- This function is defined in terms of (not) jidIsBare.

jidIsFull :: JID -> Bool
jidIsFull jid = not $ jidIsBare jid