{-

Copyright © 2010-2011 Jon Kristensen.

This file is part of Pontarius XMPP.

Pontarius XMPP is free software: you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option) any
later version.

Pontarius XMPP 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 Lesser General Public License for more
details.

You should have received a copy of the GNU Lesser General Public License along
with Pontarius XMPP. If not, see <http://www.gnu.org/licenses/>.

-}

-- |
-- Module:      $Header$
-- Description: JabberID (JID) data type and utility functions
-- Copyright:   Copyright © 2010-2011 Jon Kristensen
-- License:     LGPL-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
                         , jidIsFull
                         , jidIsBare
                         , stringToJID
                         , jidToString ) where

import Network.XMPP.Types

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


-- | 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