# Source code for aioxmpp.muc.xso

    ########################################################################
    # File name: xso.py
    # This file is part of: aioxmpp
    #
    # LICENSE
    #
    # This program 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.
    #
    # 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
    # Lesser General Public License for more details.
    #
    # You should have received a copy of the GNU Lesser General Public
    # License along with this program.  If not, see
    # <http://www.gnu.org/licenses/>.
    #
    ########################################################################
    import enum

    import aioxmpp.forms
    import aioxmpp.stanza
    import aioxmpp.stringprep
    import aioxmpp.xso as xso

    from aioxmpp.utils import namespaces

    namespaces.xep0045_muc = "http://jabber.org/protocol/muc"
    namespaces.xep0045_muc_user = "http://jabber.org/protocol/muc#user"
    namespaces.xep0045_muc_admin = "http://jabber.org/protocol/muc#admin"
    namespaces.xep0045_muc_owner = "http://jabber.org/protocol/muc#owner"
    namespaces.xep0249_conference = "jabber:x:conference"

    class StatusCode(enum.IntEnum):
        """
        This integer enumeration (see :class:`enum.IntEnum`) is used for the
        status codes defined in :xep:`45`.

        Note that members of this enumeration are equal to their respective integer
        values, making it ideal for backward- and forward-compatible code and a
        replacement for magic numbers.

        .. versionadded:: 0.10

            Before version 0.10, this enum did not exist and the numeric codes
            were used bare. Since this is an :class:`~enum.IntEnum`, it is possible
            to use the named enum members and their numeric codes interchangably.

        .. attribute:: NON_ANONYMOUS
            :annotation: = 100

            Included when entering a room where every user can see every users
            real JID.

        .. attribute:: AFFILIATION_CHANGE
            :annotation: = 101

            Included in out-of-band messages informing about affiliation changes.

        .. attribute:: SHOWING_UNAVAILABLE
            :annotation: = 102

            Inform occupants that room now shows unavailable members.

        .. attribute:: NOT_SHOWING_UNAVAILABLE
            :annotation: = 103

            Inform occupants that room now does not show unavailable members.

        .. attribute:: CONFIG_NON_PRIVACY_RELATED
            :annotation: = 104

            Inform occupants that a non-privacy related configuration change has
            occured.

        .. attribute:: SELF
            :annotation: = 110

            Inform that the stanza refers to the addressee themselves.

        .. attribute:: CONFIG_ROOM_LOGGING
            :annotation: = 170

            Inform that the room is now logged.

        .. attribute:: CONFIG_NO_ROOM_LOGGING
            :annotation: = 171

            Inform that the room is not logged anymore.

        .. attribute:: CONFIG_NON_ANONYMOUS
            :annotation: = 172

            Inform that the room is now not anonymous.

        .. attribute:: CONFIG_SEMI_ANONYMOUS
            :annotation: = 173

            Inform that the room is now semi-anonymous.

        .. attribute:: CREATED
            :annotation: = 201

            Inform that the room was created during the join operation.

        .. attribute:: REMOVED_BANNED
            :annotation: = 301

            Inform that the user was banned from the room.

        .. attribute:: NICKNAME_CHANGE
            :annotation: = 303

            Inform about new nickname.

        .. attribute:: REMOVED_KICKED
            :annotation: = 307

            Inform that the occupant was kicked.

        .. attribute:: REMOVED_AFFILIATION_CHANGE
            :annotation: = 321

            Inform that the occupant was removed from the room due to a change in
            affiliation.

        .. attribute:: REMOVED_NONMEMBER_IN_MEMBERS_ONLY
            :annotation: = 322

            Inform that the occupant was removed from the room because the room was
            changed to members-only and the occupant was not a member.

        .. attribute:: REMOVED_SERVICE_SHUTDOWN
            :annotation: = 332

            Inform that the occupant is being removed because the MUC service is
            being shut down.

        .. attribute:: REMOVED_ERROR
            :annotation: = 333

            Inform that the occupant is being removed because there was an error
            while communicating with them or their server.

        """

        NON_ANONYMOUS = 100
        AFFILIATION_CHANGE = 101
        SHOWING_UNAVAILABLE = 102
        NOT_SHOWING_UNAVAILABLE = 103
        CONFIG_NON_PRIVACY_RELATED = 104
        SELF = 110
        CONFIG_ROOM_LOGGING = 170
        CONFIG_NO_ROOM_LOGGING = 171
        CONFIG_NON_ANONYMOUS = 172
        CONFIG_SEMI_ANONYMOUS = 173
        CREATED = 201
        REMOVED_BANNED = 301
        NICKNAME_CHANGE = 303
        REMOVED_KICKED = 307
        REMOVED_AFFILIATION_CHANGE = 321
        REMOVED_NONMEMBER_IN_MEMBERS_ONLY = 322
        REMOVED_SERVICE_SHUTDOWN = 332
        REMOVED_ERROR = 333

    class History(xso.XSO):
        TAG = (namespaces.xep0045_muc, "history")

        maxchars = xso.Attr(
            "maxchars",
            type_=xso.Integer(),
            default=None,
        )

        maxstanzas = xso.Attr(
            "maxstanzas",
            type_=xso.Integer(),
            default=None,
        )

        seconds = xso.Attr(
            "seconds",
            type_=xso.Integer(),
            default=None,
        )

        since = xso.Attr(
            "since",
            type_=xso.DateTime(),
            default=None,
        )

        def __init__(self, *,
                     maxchars=None, maxstanzas=None, seconds=None, since=None):
            super().__init__()
            self.maxchars = maxchars
            self.maxstanzas = maxstanzas
            self.seconds = seconds
            self.since = since

    class GenericExt(xso.XSO):
        TAG = (namespaces.xep0045_muc, "x")

        history = xso.Child([History])

        password = xso.ChildText(
            (namespaces.xep0045_muc, "password"),
            default=None
        )

    aioxmpp.stanza.Presence.xep0045_muc = xso.Child([
        GenericExt
    ])

    aioxmpp.stanza.Message.xep0045_muc = xso.Child([
        GenericExt
    ])

    class Status(xso.XSO):
        TAG = (namespaces.xep0045_muc_user, "status")

        code = xso.Attr(
            "code",
            type_=xso.EnumCDataType(
                StatusCode,
                xso.Integer(),
                allow_coerce=True,
                pass_unknown=True,
            )
        )

        def __init__(self, code):
            super().__init__()
            self.code = code

    class StatusCodeList(xso.AbstractElementType):
        def unpack(self, item):
            return item.code

        def pack(self, code):
            item = Status(code)
            return item

        def get_xso_types(self):
            return [Status]

    class DestroyNotification(xso.XSO):
        TAG = (namespaces.xep0045_muc_user, "destroy")

        reason = xso.ChildText(
            (namespaces.xep0045_muc_user, "reason"),
            default=None
        )

        jid = xso.Attr(
            "jid",
            type_=xso.JID(),
            default=None
        )

    class Decline(xso.XSO):
        TAG = (namespaces.xep0045_muc_user, "decline")

        from_ = xso.Attr(
            "from",
            type_=xso.JID(),
            default=None
        )

        to = xso.Attr(
            "to",
            type_=xso.JID(),
            default=None
        )

        reason = xso.ChildText(
            (namespaces.xep0045_muc_user, "reason"),
            default=None
        )

    class Invite(xso.XSO):
        TAG = (namespaces.xep0045_muc_user, "invite")

        from_ = xso.Attr(
            "from",
            type_=xso.JID(),
            default=None
        )

        to = xso.Attr(
            "to",
            type_=xso.JID(),
            default=None
        )

        reason = xso.ChildText(
            (namespaces.xep0045_muc_user, "reason"),
            default=None
        )

        password = xso.ChildText(
            (namespaces.xep0045_muc_user, "password"),
            default=None
        )

    class ActorBase(xso.XSO):
        jid = xso.Attr(
            "jid",
            type_=xso.JID(),
            default=None,
        )

        nick = xso.Attr(
            "nick",
            type_=xso.String(aioxmpp.stringprep.resourceprep),
            default=None
        )

    class ItemBase(xso.XSO):
        affiliation = xso.Attr(
            "affiliation",
            validator=xso.RestrictToSet({
                "admin",
                "member",
                "none",
                "outcast",
                "owner",
                None,
            }),
            validate=xso.ValidateMode.ALWAYS,
            default=None,
        )

        jid = xso.Attr(
            "jid",
            type_=xso.JID(),
            default=None,
        )

        nick = xso.Attr(
            "nick",
            type_=xso.String(aioxmpp.stringprep.resourceprep),
            default=None
        )

        role = xso.Attr(
            "role",
            validator=xso.RestrictToSet({
                "moderator",
                "none",
                "participant",
                "visitor",
                None,
            }),
            validate=xso.ValidateMode.ALWAYS,
            default=None,
        )

        def __init__(self,
                     affiliation=None,
                     jid=None,
                     nick=None,
                     role=None,
                     reason=None):
            super().__init__()
            self.affiliation = affiliation
            self.jid = jid
            self.nick = nick
            self.role = role
            self.reason = reason

        @property
        def bare_jid(self):
            """
            Return the bare jid of the item or :data:`None` if no JID is
            given.

            Use this to access the jid unless you really want to know the
            resource. Usually the information given by the resource is
            meaningless (the resource is randomly picked by the server).
            """
            if self.jid:
                return self.jid.bare()
            else:
                return None

    class UserActor(ActorBase):
        TAG = (namespaces.xep0045_muc_user, "actor")

    class Continue(xso.XSO):
        TAG = (namespaces.xep0045_muc_user, "continue")

        thread = xso.Attr(
            "thread",
            type_=aioxmpp.stanza.Thread.identifier.type_,
            default=None,
        )

    class UserItem(ItemBase):
        TAG = (namespaces.xep0045_muc_user, "item")

        actor = xso.Child([UserActor])

        continue_ = xso.Child([Continue])

        reason = xso.ChildText(
            (namespaces.xep0045_muc_user, "reason"),
            default=None
        )

    class UserExt(xso.XSO):
        TAG = (namespaces.xep0045_muc_user, "x")

        status_codes = xso.ChildValueList(
            StatusCodeList(),
            container_type=set
        )

        destroy = xso.Child([DestroyNotification])

        decline = xso.Child([Decline])

        invites = xso.ChildList([Invite])

        items = xso.ChildList([UserItem])

        password = xso.ChildText(
            (namespaces.xep0045_muc_user, "password"),
            default=None
        )

        def __init__(self,
                     status_codes=[],
                     destroy=None,
                     decline=None,
                     invites=[],
                     items=[],
                     password=None):
            super().__init__()
            self.status_codes.update(status_codes)
            self.destroy = destroy
            self.decline = decline
            self.invites.extend(invites)
            self.items.extend(items)
            self.password = password

    aioxmpp.stanza.Presence.xep0045_muc_user = xso.Child([
        UserExt
    ])

    aioxmpp.stanza.Message.xep0045_muc_user = xso.Child([
        UserExt
    ])

    class AdminActor(ActorBase):
        TAG = (namespaces.xep0045_muc_admin, "actor")

    class AdminItem(ItemBase):
        TAG = (namespaces.xep0045_muc_admin, "item")

        actor = xso.Child([AdminActor])

        continue_ = xso.Child([Continue])

        reason = xso.ChildText(
            (namespaces.xep0045_muc_admin, "reason"),
            default=None
        )

    @aioxmpp.stanza.IQ.as_payload_class
    class AdminQuery(xso.XSO):
        TAG = (namespaces.xep0045_muc_admin, "query")

        items = xso.ChildList([AdminItem])

        def __init__(self, *, items=[]):
            super().__init__()
            self.items[:] = items

    class DestroyRequest(xso.XSO):
        TAG = (namespaces.xep0045_muc_owner, "destroy")

        reason = xso.ChildText(
            (namespaces.xep0045_muc_owner, "reason"),
            default=None
        )

        password = xso.ChildText(
            (namespaces.xep0045_muc_owner, "password"),
            default=None
        )

        jid = xso.Attr(
            "jid",
            type_=xso.JID(),
            default=None
        )

    @aioxmpp.stanza.IQ.as_payload_class
    class OwnerQuery(xso.XSO):
        TAG = (namespaces.xep0045_muc_owner, "query")

        destroy = xso.Child([DestroyRequest])

        form = xso.Child([aioxmpp.forms.Data])

        def __init__(self, *, form=None, destroy=None):
            super().__init__()
            self.form = form
            self.destroy = destroy

    class DirectInvite(xso.XSO):
        TAG = namespaces.xep0249_conference, "x"

        # JEP-0045 v1.19 §6.7 allowed a mediated(!) invitation to contain a
        # (what is now) DirectInvite payload where the reason is included as
        # text (and not as attribute).
        #
        # Some servers still emit this for compatibility. We ignore that.
        _ = xso.Text(default=None)

        jid = xso.Attr(
            "jid",
            type_=xso.JID(),
        )

        reason = xso.Attr(
            "reason",
            default=None,
        )

        password = xso.Attr(
            "password",
            default=None,
        )

        continue_ = xso.Attr(
            "continue",
            type_=xso.Bool(),
            default=False,
        )

        thread = xso.Attr(
            "thread",
            default=None,
        )

        def __init__(self, jid, *,
                     reason=None,
                     password=None,
                     continue_=False,
                     thread=None):
            super().__init__()
            self.jid = jid
            self.reason = reason
            self.password = password
            self.continue_ = continue_
            self.thread = thread

    aioxmpp.Message.xep0249_direct_invite = xso.Child([DirectInvite])

    class ConfigurationForm(aioxmpp.forms.Form):
        """
        This is a :xep:`4` form template (see :mod:`aioxmpp.forms`) for MUC
        configuration forms.

        The attribute documentation is auto-generated from :xep:`45`; see there for
        details on the semantics of each field.

        .. versionadded:: 0.7
        """

        FORM_TYPE = 'http://jabber.org/protocol/muc#roomconfig'

        maxhistoryfetch = aioxmpp.forms.TextSingle(
            var='muc#maxhistoryfetch',
            label='Maximum Number of History Messages Returned by Room'
        )

        allowpm = aioxmpp.forms.ListSingle(
            var='muc#roomconfig_allowpm',
            label='Roles that May Send Private Messages'
        )

        allowinvites = aioxmpp.forms.Boolean(
            var='muc#roomconfig_allowinvites',
            label='Whether to Allow Occupants to Invite Others'
        )

        changesubject = aioxmpp.forms.Boolean(
            var='muc#roomconfig_changesubject',
            label='Whether to Allow Occupants to Change Subject'
        )

        enablelogging = aioxmpp.forms.Boolean(
            var='muc#roomconfig_enablelogging',
            label='Whether to Enable Public Logging of Room Conversations'
        )

        getmemberlist = aioxmpp.forms.ListMulti(
            var='muc#roomconfig_getmemberlist',
            label='Roles and Affiliations that May Retrieve Member List'
        )

        lang = aioxmpp.forms.TextSingle(
            var='muc#roomconfig_lang',
            label='Natural Language for Room Discussions'
        )

        pubsub = aioxmpp.forms.TextSingle(
            var='muc#roomconfig_pubsub',
            label='XMPP URI of Associated Publish-Subscribe Node'
        )

        maxusers = aioxmpp.forms.ListSingle(
            var='muc#roomconfig_maxusers',
            label='Maximum Number of Room Occupants'
        )

        membersonly = aioxmpp.forms.Boolean(
            var='muc#roomconfig_membersonly',
            label='Whether to Make Room Members-Only'
        )

        moderatedroom = aioxmpp.forms.Boolean(
            var='muc#roomconfig_moderatedroom',
            label='Whether to Make Room Moderated'
        )

        passwordprotectedroom = aioxmpp.forms.Boolean(
            var='muc#roomconfig_passwordprotectedroom',
            label='Whether a Password is Required to Enter'
        )

        persistentroom = aioxmpp.forms.Boolean(
            var='muc#roomconfig_persistentroom',
            label='Whether to Make Room Persistent'
        )

        presencebroadcast = aioxmpp.forms.ListMulti(
            var='muc#roomconfig_presencebroadcast',
            label='Roles for which Presence is Broadcasted'
        )

        publicroom = aioxmpp.forms.Boolean(
            var='muc#roomconfig_publicroom',
            label='Whether to Allow Public Searching for Room'
        )

        roomadmins = aioxmpp.forms.JIDMulti(
            var='muc#roomconfig_roomadmins',
            label='Full List of Room Admins'
        )

        roomdesc = aioxmpp.forms.TextSingle(
            var='muc#roomconfig_roomdesc',
            label='Short Description of Room'
        )

        roomname = aioxmpp.forms.TextSingle(
            var='muc#roomconfig_roomname',
            label='Natural-Language Room Name'
        )

        roomowners = aioxmpp.forms.JIDMulti(
            var='muc#roomconfig_roomowners',
            label='Full List of Room Owners'
        )

        roomsecret = aioxmpp.forms.TextPrivate(
            var='muc#roomconfig_roomsecret',
            label='The Room Password'
        )

        whois = aioxmpp.forms.ListSingle(
            var='muc#roomconfig_whois',
            label='Affiliations that May Discover Real JIDs of Occupants'
        )

    class InfoForm(aioxmpp.forms.Form):
        FORM_TYPE = 'http://jabber.org/protocol/muc#roominfo'

        maxhistoryfetch = aioxmpp.forms.TextSingle(
            var='muc#maxhistoryfetch',
            label='Maximum Number of History Messages Returned by Room'
        )

        contactjid = aioxmpp.forms.JIDMulti(
            var='muc#roominfo_contactjid',
            label='Contact Addresses (normally, room owner or owners)'
        )

        description = aioxmpp.forms.TextSingle(
            var='muc#roominfo_description',
            label='Short Description of Room'
        )

        lang = aioxmpp.forms.TextSingle(
            var='muc#roominfo_lang',
            label='Natural Language for Room Discussions'
        )

        ldapgroup = aioxmpp.forms.TextSingle(
            var='muc#roominfo_ldapgroup',
            label='An associated LDAP group that defines room membership; this '
            'should be an LDAP Distinguished Name according to an '
            'implementation-specific or deployment-specific definition of a group.'
        )

        logs = aioxmpp.forms.TextSingle(
            var='muc#roominfo_logs',
            label='URL for Archived Discussion Logs'
        )

        occupants = aioxmpp.forms.TextSingle(
            var='muc#roominfo_occupants',
            label='Current Number of Occupants in Room'
        )

        subject = aioxmpp.forms.TextSingle(
            var='muc#roominfo_subject',
            label='Current Discussion Topic'
        )

        subjectmod = aioxmpp.forms.Boolean(
            var='muc#roominfo_subjectmod',
            label='The room subject can be modified by participants'
        )

    class VoiceRequestForm(aioxmpp.forms.Form):
        FORM_TYPE = 'http://jabber.org/protocol/muc#request'

        role = aioxmpp.forms.ListSingle(
            var='muc#role',
            label='Requested role'
        )

        jid = aioxmpp.forms.JIDSingle(
            var='muc#jid',
            label='User ID'
        )

        roomnick = aioxmpp.forms.TextSingle(
            var='muc#roomnick',
            label='Room Nickname'
        )

        request_allow = aioxmpp.forms.Boolean(
            var='muc#request_allow',
            label='Whether to grant voice'
        )