Multi User Chat
===============

Allows configuration of, participation in, and administration of individual
text-based conference rooms.

  * Create a new Room
  * Join a room
  * Manage room invitations
  * Discover MUC support
  * Discover joined rooms
  * Discover room information
  * Start a private chat
  * Manage changes on room subject
  * Manage role modifications
  * Manage affiliation modifications

**XEP related:** [XEP-45](http://www.xmpp.org/extensions/xep-0045.html)

Create a new Room
-----------------

**Description**

Allowed users may create new rooms. There are two types of rooms that you can
create. **Instant rooms** which are available for immediate access and are
automatically created based on some default configuration and **Reserved
rooms** which are manually configured by the room creator before anyone is
allowed to enter.

**Usage**

In order to create a room you will need to first create an instance of
_**MultiUserChat**_.
In order to do so, get a instance of `MultiUserChatManager` and call `getMultiUserChat(String)` to retrieve a `MultiUserChat` instance.
The next step is to send **create(String nickname)** to
the _**MultiUserChat**_ instance where nickname is the nickname to use when
joining the room.

Depending on the type of room that you want to create you will have to use
different configuration forms. In order to create an Instant room just send
**sendConfigurationForm(Form form)** where form is an empty form. But if you
want to create a Reserved room then you should first get the room's
configuration form, complete the form and finally send it back to the server.

**Examples**

In this example we can see how to create an instant room:

```java
// Get the MultiUserChatManager
MultiUserChatManager manager = MultiUserChatManager.getInstanceFor(connection);

// Get a MultiUserChat using MultiUserChatManager
MultiUserChat muc = manager.getMultiUserChat("myroom@conference.jabber.org");

// Create the room
muc.create("testbot");

// Send an empty room configuration form which indicates that we want
// an instant room
muc.sendConfigurationForm(new Form(DataForm.Type.submit));
```

In this example we can see how to create a reserved room. The form is
completed with default values:

```java
// Get the MultiUserChatManager
MultiUserChatManager manager = MultiUserChatManager.getInstanceFor(connection);

// Create a MultiUserChat using an XMPPConnection for a room
MultiUserChat muc = = manager.getMultiUserChat("myroom@conference.jabber.org");

// Create the room
muc.create("testbot");

// Get the the room's configuration form
Form form = muc.getConfigurationForm();
// Create a new form to submit based on the original form
Form submitForm = form.createAnswerForm();
// Add default answers to the form to submit
for (Iterator fields = form.getFields(); fields.hasNext();) {
	FormField field = (FormField) fields.next();
	if (!FormField.type.hidden.equals(field.getType()) && field.getVariable() != null) {
		// Sets the default value as the answer
		submitForm.setDefaultAnswer(field.getVariable());
}
}
// Sets the new owner of the room
List owners = new ArrayList();
owners.add("johndoe@jabber.org");
submitForm.setAnswer("muc#roomconfig_roomowners", owners);
// Send the completed form (with default values) to the server to configure the room
muc.sendConfigurationForm(submitForm);
```

Join a room
-----------

**Description**

Your usual first step in order to send messages to a room is to join the room.
Multi User Chat allows to specify several parameter while joining a room.
Basically you can control the amount of history to receive after joining the
room as well as provide your nickname within the room and a password if the
room is password protected.

**Usage**

In order to join a room you will need to first get an instance of
_**MultiUserChat**_.
In order to do so, get a instance of `MultiUserChatManager` and call `getMultiUserChat(String)` to retrieve a `MultiUserChat` instance.
The next step is to send **join(...)** to the
_**MultiUserChat**_ instance. But first you will have to decide which join
message to send. If you want to just join the room without a password and
without specifying the amount of history to receive then you could use
**join(String nickname)** where nickname if your nickname in the room. In case
the room requires a password in order to join you could then use **join(String
nickname, String password)**. And finally, the most complete way to join a
room is to send **join(String nickname, String password, DiscussionHistory
history, long timeout)** where nickname is your nickname in the room, ,
password is your password to join the room, history is an object that
specifies the amount of history to receive and timeout is the milliseconds to
wait for a response from the server.

**Examples**

In this example we can see how to join a room with a given nickname:

```java
// Get the MultiUserChatManager
MultiUserChatManager manager = MultiUserChatManager.getInstanceFor(connection);

// Create a MultiUserChat using an XMPPConnection for a room
MultiUserChat muc2 = manager.getMultiUserChat("myroom@conference.jabber.org");

// User2 joins the new room
// The room service will decide the amount of history to send
muc2.join("testbot2");
```

In this example we can see how to join a room with a given nickname and
password:

```java
// Get the MultiUserChatManager
MultiUserChatManager manager = MultiUserChatManager.getInstanceFor(connection);

// Create a MultiUserChat using an XMPPConnection for a room
MultiUserChat muc2 = manager.getMultiUserChat("myroom@conference.jabber.org");

// User2 joins the new room using a password
// The room service will decide the amount of history to send
muc2.join("testbot2", "password");
```

In this example we can see how to join a room with a given nickname specifying
the amount of history to receive:

```java
// Get the MultiUserChatManager
MultiUserChatManager manager = MultiUserChatManager.getInstanceFor(connection);

// Create a MultiUserChat using an XMPPConnection for a room
MultiUserChat muc2 = manager.getMultiUserChat("myroom@conference.jabber.org");

// User2 joins the new room using a password and specifying
// the amount of history to receive. In this example we are requesting the last 5 messages.
DiscussionHistory history = new DiscussionHistory();
history.setMaxStanzas(5);
muc2.join("testbot2", "password", history, conn1.getPacketReplyTimeout());
```

Manage room invitations
-----------------------

**Description**

It can be useful to invite another user to a room in which one is an occupant.
Depending on the room's type the invitee could receive a password to use to
join the room and/or be added to the member list if the room is of type
members-only. Smack allows to send room invitations and let potential invitees
to listening for room invitations and inviters to listen for invitees'
rejections.

**Usage**

In order to invite another user to a room you must be already joined to the
room. Once you are joined just send **invite(String participant, String
reason)** to the _**MultiUserChat**_ where participant is the user to invite
to the room (e.g. hecate@shakespeare.lit) and reason is the reason why the
user is being invited.

If potential invitees want to listen for room invitations then the invitee
must add an _**InvitationListener**_ to the _**MultiUserChatManager**_ class. Since
the _**InvitationListener**_ is an _interface_, it is necessary to create a
class that implements this _interface_. If an inviter wants to listen for room
invitation rejections, just add an _**InvitationRejectionListener**_ to the
_**MultiUserChat**_. _**InvitationRejectionListener**_ is also an interface so
you will need to create a class that implements this interface.

**Examples**

In this example we can see how to invite another user to the room and lister
for possible rejections:

```java
// Get the MultiUserChatManager
MultiUserChatManager manager = MultiUserChatManager.getInstanceFor(connection);

// Create a MultiUserChat using an XMPPConnection for a room
MultiUserChat muc2 = manager.getMultiUserChat("myroom@conference.jabber.org");

muc2.join("testbot2");
// User2 listens for invitation rejections
muc2.addInvitationRejectionListener(new InvitationRejectionListener() {
	public void invitationDeclined(String invitee, String reason) {
		// Do whatever you need here...
	}
});
// User2 invites user3 to join to the room
muc2.invite("user3@host.org/Smack", "Meet me in this excellent room");
```

In this example we can see how to listen for room invitations and decline
invitations:

```java
// User3 listens for MUC invitations
MultiUserChatManager.getInstanceFor(connection).addInvitationListener(new InvitationListener() {
	public void invitationReceived(XMPPConnection conn, String room, String inviter, String reason, String password) {
		// Reject the invitation
		MultiUserChat.decline(conn, room, inviter, "I'm busy right now");
	}
});
```

Discover MUC support
--------------------

**Description**

A user may want to discover if one of the user's contacts supports the Multi-
User Chat protocol.

**Usage**

In order to discover if one of the user's contacts supports MUC just send
**isServiceEnabled(String user)** to the
_**MultiUserChatManager**_ class where user is a fully qualified XMPP ID, e.g.
jdoe@example.com. You will receive a boolean indicating whether the user
supports MUC or not.

**Examples**

In this example we can see how to discover support of MUC:

```java
// Discover whether user3@host.org supports MUC or not
boolean supports = MultiUserChatManager.getInstanceFor(connection).isServiceEnabled("user3@host.org/Smack");
```

Discover joined rooms
---------------------

**Description**

A user may also want to query a contact regarding which rooms the contact is
in.

**Usage**

In order to get the rooms where a user is in just send
**getJoinedRooms(String user)** to the
_**MultiUserChatManager**_ class where user is a fully qualified XMPP ID, e.g.
jdoe@example.com. You will get an Iterator of Strings as an answer where each
String represents a room name.

**Examples**

In this example we can see how to get the rooms where a user is in:

```java
// Get the MultiUserChatManager
MultiUserChatManager manager = MultiUserChatManager.getInstanceFor(connection);

// Get the rooms where user3@host.org has joined
List<String> joinedRooms = manager.getJoinedRooms("user3@host.org/Smack");
```

Discover room information
-------------------------

**Description**

A user may need to discover information about a room without having to
actually join the room. The server will provide information only for public
rooms.

**Usage**

In order to discover information about a room just send
**getRoomInfo(String room)** to the
_**MultiUserChatManager**_ class where room is the XMPP ID of the room, e.g.
roomName@conference.myserver. You will get a RoomInfo object that contains the
discovered room information.

**Examples**

In this example we can see how to discover information about a room:

```java
// Get the MultiUserChatManager
MultiUserChatManager manager = MultiUserChatManager.getInstanceFor(connection);

// Discover information about the room roomName@conference.myserver
RoomInfo info = manager.getRoomInfo("roomName@conference.myserver");
System.out.println("Number of occupants:" + info.getOccupantsCount());
System.out.println("Room Subject:" + info.getSubject());
```

Start a private chat
--------------------

**Description**

A room occupant may want to start a private chat with another room occupant
even though they don't know the fully qualified XMPP ID (e.g.
jdoe@example.com) of each other.

**Usage**

To create a private chat with another room occupant just send
**createPrivateChat(String participant)** to the _**MultiUserChat**_ that you
used to join the room. The parameter participant is the occupant unique room
JID (e.g. 'darkcave@macbeth.shakespeare.lit/Paul'). You will receive a regular
_**Chat**_ object that you can use to chat with the other room occupant.

**Examples**

In this example we can see how to start a private chat with another room
occupant:

```
// Start a private chat with another participant
Chat chat = muc2.createPrivateChat("myroom@conference.jabber.org/johndoe");
chat.sendMessage("Hello there");
```

Manage changes on room subject
------------------------------

**Description**

A common feature of multi-user chat rooms is the ability to change the subject
within the room. As a default, only users with a role of "moderator" are
allowed to change the subject in a room. Although some rooms may be configured
to allow a mere participant or even a visitor to change the subject.

Every time the room's subject is changed you may want to be notified of the
modification. The new subject could be used to display an in-room message.

**Usage**

In order to modify the room's subject just send **changeSubject(String
subject)** to the _**MultiUserChat**_ that you used to join the room where
subject is the new room's subject. On the other hand, if you want to be
notified whenever the room's subject is modified you should add a
_**SubjectUpdatedListener**_ to the _**MultiUserChat**_ by sending
**addSubjectUpdatedListener(SubjectUpdatedListener listener)** to the
_**MultiUserChat**_. Since the _**SubjectUpdatedListener**_ is an _interface_,
it is necessary to create a class that implements this _interface_.

**Examples**

In this example we can see how to change the room's subject and react whenever
the room's subject is modified:

```
// An occupant wants to be notified every time the room's subject is changed
muc3.addSubjectUpdatedListener(new SubjectUpdatedListener() {
	public void subjectUpdated(String subject, String from) {
		....
	}
});
// A room's owner changes the room's subject
muc2.changeSubject("New Subject");
```

Manage role modifications
-------------------------

**Description**

There are four defined roles that an occupant can have:

  1. Moderator
  2. Participant
  3. Visitor
  4. None (the absence of a role)

These roles are temporary in that they do not persist across a user's visits
to the room and can change during the course of an occupant's visit to the
room.

A moderator is the most powerful occupant within the context of the room, and
can to some extent manage other occupants' roles in the room. A participant
has fewer privileges than a moderator, although he or she always has the right
to speak. A visitor is a more restricted role within the context of a
moderated room, since visitors are not allowed to send messages to all
occupants.

Roles are granted, revoked, and maintained based on the occupant's room
nickname or full JID. Whenever an occupant's role is changed Smack will
trigger specific events.

**Usage**

In order to grant voice (i.e. make someone a _participant_) just send the
message **grantVoice(String nickname)** to _**MultiUserChat**_. Use
**revokeVoice(String nickname)** to revoke the occupant's voice (i.e. make the
occupant a _visitor_).

In order to grant moderator privileges to a participant or visitor just send
the message **grantModerator(String nickname)** to _**MultiUserChat**_. Use
**revokeModerator(String nickname)** to revoke the moderator privilege from
the occupant thus making the occupant a participant.

Smack allows you to listen for role modification events. If you are interested
in listening role modification events of any occupant then use the listener
**_ParticipantStatusListener_**. But if you are interested in listening for
your own role modification events, use the listener **_UserStatusListener_**.
Both listeners should be added to the _**MultiUserChat**_ by using
**addParticipantStatusListener(ParticipantStatusListener listener)** or
**addUserStatusListener(UserStatusListener listener)** respectively. These
listeners include several notification events but you may be interested in
just a few of them. Smack provides default implementations for these listeners
avoiding you to implement all the interfaces' methods. The default
implementations are **_DefaultUserStatusListener_** and
**_DefaultParticipantStatusListener_**. Below you will find the sent messages
to the listeners whenever an occupant's role has changed.

These are the triggered events when the role has been upgraded:

| Old | New | Events |
|-----|-----|--------|
| None | Visitor | -- |
| Visitor | Participant | voiceGranted |
| Participant | Moderator | moderatorGranted |
| None | Participant | voiceGranted |
| None | Moderator | voiceGranted + moderatorGranted |
| Visitor | Moderator | voiceGranted + moderatorGranted |

These are the triggered events when the role has been downgraded:

| Old | New | Events |
|-----|-----|--------|
| Moderator | Participant | moderatorRevoked |
| Participant | Visitor | voiceRevoked |
| Visitor | None | kicked |
| Moderator | Visitor | voiceRevoked + moderatorRevoked |
| Moderator | None | kicked |
| Participant | None | kicked |

**Examples**

In this example we can see how to grant voice to a visitor and listen for the
notification events:

```java
// User1 creates a room
muc = manager.getMultiUserChat("myroom@conference.jabber.org");
muc.create("testbot");
// User1 (which is the room owner) configures the room as a moderated room
Form form = muc.getConfigurationForm();
Form answerForm = form.createAnswerForm();
answerForm.setAnswer("muc#roomconfig_moderatedroom", "1");
muc.sendConfigurationForm(answerForm);

// User2 joins the new room (as a visitor)
MultiUserChat muc2 = manager2.getMultiUserChat("myroom@conference.jabber.org");
muc2.join("testbot2");
// User2 will listen for his own "voice" notification events
muc2.addUserStatusListener(new DefaultUserStatusListener() {
	public void voiceGranted() {
		super.voiceGranted();
		...
	}
	public void voiceRevoked() {
		super.voiceRevoked();
		...
	}
});

// User3 joins the new room (as a visitor)
MultiUserChat muc3 = manager3.getMultiUserChat("myroom@conference.jabber.org");
muc3.join("testbot3");
// User3 will lister for other occupants "voice" notification events
muc3.addParticipantStatusListener(new DefaultParticipantStatusListener() {
	public void voiceGranted(String participant) {
		super.voiceGranted(participant);
		...
	}
	public void voiceRevoked(String participant) {
		super.voiceRevoked(participant);
		...
	}
});

// The room's owner grants voice to user2
muc.grantVoice("testbot2");
```

Manage affiliation modifications
--------------------------------

**Description**

There are five defined affiliations that a user can have in relation to a
room:

  1. Owner
  2. Admin
  3. Member
  4. Outcast
  5. None (the absence of an affiliation)

These affiliations are semi-permanent in that they persist across a user's
visits to the room and are not affected by happenings in the room.
Affiliations are granted, revoked, and maintained based on the user's bare
JID.

If a user without a defined affiliation enters a room, the user's affiliation
is defined as "none"; however, this affiliation does not persist across
visits.

Owners and admins are by definition immune from certain actions. Specifically,
an owner or admin cannot be kicked from a room and cannot be banned from a
room. An admin must first lose his or her affiliation (i.e., have an
affiliation of "none" or "member") before such actions could be performed on
them.

The member affiliation provides a way for a room owner or admin to specify a
"whitelist" of users who are allowed to enter a members-only room. When a
member enters a members-only room, his or her affiliation does not change, no
matter what his or her role is. The member affiliation also provides a way for
users to effectively register with an open room and thus be permanently
associated with that room in some way (one result may be that the user's
nickname is reserved in the room).

An outcast is a user who has been banned from a room and who is not allowed to
enter the room. Whenever a user's affiliation is changed Smack will trigger
specific events.

**Usage**

In order to grant membership to a room, administrator privileges or owner
priveliges just send **grantMembership(String jid)**, **grantAdmin(String
jid)** or **grantOwnership(String jid)** to _**MultiUserChat**_ respectively.
Use **revokeMembership(String jid)**, **revokeAdmin(String jid)** or
**revokeOwnership(String jid)** to revoke the membership to a room,
administrator privileges or owner priveliges respectively.

In order to ban a user from the room just send the message **banUser(String
jid, String reason)** to _**MultiUserChat**_.

Smack allows you to listen for affiliation modification events. If you are
interested in listening affiliation modification events of any user then use
the listener **_ParticipantStatusListener_**. But if you are interested in
listening for your own affiliation modification events, use the listener
**_UserStatusListener_**. Both listeners should be added to the
_**MultiUserChat**_ by using
**addParticipantStatusListener(ParticipantStatusListener listener)** or
**addUserStatusListener(UserStatusListener listener)** respectively. These
listeners include several notification events but you may be interested in
just a few of them. Smack provides default implementations for these listeners
avoiding you to implement all the interfaces' methods. The default
implementations are **_DefaultUserStatusListener_** and
**_DefaultParticipantStatusListener_**. Below you will find the sent messages
to the listeners whenever a user's affiliation has changed.

These are the triggered events when the affiliation has been upgraded:

| Old | New | Events |
|-----|-----|--------|
| None | Member | membershipGranted |
| Member | Admin | membershipRevoked + adminGranted |
| Admin | Owner | adminRevoked + ownershipGranted |
| None | Admin | adminGranted |
| None | Owner | ownershipGranted |
| Member | Owner | membershipRevoked + ownershipGranted |

These are the triggered events when the affiliation has been downgraded:

| Old | New | Events |
|-----|-----|--------|
| Owner | Admin | ownershipRevoked + adminGranted |
| Admin | Member | adminRevoked + membershipGranted |
| Member | None | membershipRevoked |
| Owner | Member | ownershipRevoked + membershipGranted |
| Owner | None | ownershipRevoked |
| Admin | None | adminRevoked |
| _Anyone_ | Outcast | banned |

**Examples**

In this example we can see how to grant admin privileges to a user and listen
for the notification events:

```java
// User1 creates a room
muc = manager.getMultiUserChat("myroom@conference.jabber.org");
muc.create("testbot");
// User1 (which is the room owner) configures the room as a moderated room
Form form = muc.getConfigurationForm();
Form answerForm = form.createAnswerForm();
answerForm.setAnswer("muc#roomconfig_moderatedroom", "1");
muc.sendConfigurationForm(answerForm);

// User2 joins the new room (as a visitor)
MultiUserChat muc2 = manager2.getMultiUserChat("myroom@conference.jabber.org");
muc2.join("testbot2");
// User2 will listen for his own admin privileges
muc2.addUserStatusListener(new DefaultUserStatusListener() {
	public void membershipRevoked() {
		super.membershipRevoked();
		...
	}
	public void adminGranted() {
		super.adminGranted();
		...
	}
});

// User3 joins the new room (as a visitor)
MultiUserChat muc3 = manager3.getMultiUserChat("myroom@conference.jabber.org");
muc3.join("testbot3");
// User3 will lister for other users admin privileges
muc3.addParticipantStatusListener(new DefaultParticipantStatusListener() {
	public void membershipRevoked(String participant) {
		super.membershipRevoked(participant);
		...
	}
	public void adminGranted(String participant) {
		super.adminGranted(participant);
		...
	}
});
// The room's owner grants admin privileges to user2
muc.grantAdmin("user2@jabber.org");
```
