Index: D:/Projects/External/openfire_3_6_4/src/java/org/jivesoftware/openfire/muc/spi/MultiUserChatServiceImpl.java
===================================================================
--- D:/Projects/External/openfire_3_6_4/src/java/org/jivesoftware/openfire/muc/spi/MultiUserChatServiceImpl.java	(revision 11015)
+++ D:/Projects/External/openfire_3_6_4/src/java/org/jivesoftware/openfire/muc/spi/MultiUserChatServiceImpl.java	(working copy)
@@ -24,6 +24,7 @@
 import org.jivesoftware.openfire.forms.spi.XDataFormImpl;
 import org.jivesoftware.openfire.forms.spi.XFormFieldImpl;
 import org.jivesoftware.openfire.muc.*;
+import org.jivesoftware.openfire.muc.MUCRole.Affiliation;
 import org.jivesoftware.openfire.muc.cluster.GetNumberConnectedUsers;
 import org.jivesoftware.openfire.muc.cluster.OccupantAddedEvent;
 import org.jivesoftware.openfire.muc.cluster.RoomAvailableEvent;
@@ -152,6 +153,15 @@
     private boolean allowToDiscoverLockedRooms = true;
 
     /**
+     * Flag that indicates if the service should provide information about non-public members-only
+     * rooms when handling service discovery requests.
+     * Note: Setting this flag in false is not compliant with the spec. A user may try to join a
+     * non-public members-only room thinking that the room doesn't exist because the user didn't
+     * discover it before.
+     */
+    private boolean allowToDiscoverMembersOnlyRooms = true;
+
+    /**
      * Returns the permission policy for creating rooms. A true value means that not anyone can
      * create a room, only the JIDs listed in <code>allowedToCreate</code> are allowed to create
      * rooms.
@@ -795,6 +805,16 @@
     }
 
     /**
+     * Returns the flag that indicates if the service should provide information about non-public
+     * members-only rooms when handling service discovery requests.
+     *
+     * @return true if the service should provide information about non-public members-only rooms.
+     */
+    public boolean isAllowToDiscoverMembersOnlyRooms() {
+        return allowToDiscoverMembersOnlyRooms;
+    }
+
+    /**
      * Sets the flag that indicates if the service should provide information about locked rooms
      * when handling service discovery requests.
      * Note: Setting this flag in false is not compliant with the spec. A user may try to join a
@@ -809,6 +829,22 @@
                 Boolean.toString(allowToDiscoverLockedRooms));
     }
 
+    /**
+     * Sets the flag that indicates if the service should provide information about non-public
+     * members-only rooms when handling service discovery requests.
+     * Note: Setting this flag in false is not compliant with the spec. A user may try to join a
+     * non-public members-only room thinking that the room doesn't exist because the user didn't
+     * discover it before.
+     *
+     * @param allowToDiscoverMembersOnlyRooms if the service should provide information about
+     *        non-public members-only rooms.
+     */
+    public void setAllowToDiscoverMembersOnlyRooms(boolean allowToDiscoverMembersOnlyRooms) {
+        this.allowToDiscoverMembersOnlyRooms = allowToDiscoverMembersOnlyRooms;
+        MUCPersistenceManager.setProperty(chatServiceName, "discover.membersOnly",
+                Boolean.toString(allowToDiscoverMembersOnlyRooms));
+    }
+
     public boolean isRoomCreationRestricted() {
         return roomCreationRestricted;
     }
@@ -869,6 +905,8 @@
         }
         allowToDiscoverLockedRooms =
                 MUCPersistenceManager.getBooleanProperty(chatServiceName, "discover.locked", true);
+        allowToDiscoverMembersOnlyRooms =
+            MUCPersistenceManager.getBooleanProperty(chatServiceName, "discover.membersOnly", true);
         roomCreationRestricted =
                 MUCPersistenceManager.getBooleanProperty(chatServiceName, "create.anyone", false);
         // Load the list of JIDs that are allowed to create a MUC room
@@ -1137,7 +1175,7 @@
         else if (name != null && node == null) {
             // Answer the identity of a given room
             MUCRoom room = getChatRoom(name);
-            if (room != null && canDiscoverRoom(room)) {
+            if (room != null && canDiscoverRoom(room, senderJID)) {
                 Element identity = DocumentHelper.createElement("identity");
                 identity.addAttribute("category", "conference");
                 identity.addAttribute("name", room.getNaturalLanguageName());
@@ -1178,7 +1216,7 @@
         else if (name != null && node == null) {
             // Answer the features of a given room
             MUCRoom room = getChatRoom(name);
-            if (room != null && canDiscoverRoom(room)) {
+            if (room != null && canDiscoverRoom(room, senderJID)) {
                 features.add("http://jabber.org/protocol/muc");
                 // Always add public since only public rooms can be discovered
                 features.add("muc_public");
@@ -1221,7 +1259,7 @@
         if (name != null && node == null) {
             // Answer the extended info of a given room
             MUCRoom room = getChatRoom(name);
-            if (room != null && canDiscoverRoom(room)) {
+            if (room != null && canDiscoverRoom(room, senderJID)) {
                 XDataFormImpl dataForm = new XDataFormImpl(DataForm.TYPE_RESULT);
 
                 XFormFieldImpl field = new XFormFieldImpl("FORM_TYPE");
@@ -1350,7 +1388,7 @@
 			// Answer all the public rooms as items
 			for (MUCRoom room : rooms.values())
 			{
-				if (canDiscoverRoom(room))
+				if (canDiscoverRoom(room, senderJID))
 				{
 					answer.add(new DiscoItem(room.getRole().getRoleAddress(),
 						room.getNaturalLanguageName(), null, null));
@@ -1360,7 +1398,7 @@
         else if (name != null && node == null) {
             // Answer the room occupants as items if that info is publicly available
             MUCRoom room = getChatRoom(name);
-            if (room != null && canDiscoverRoom(room)) {
+            if (room != null && canDiscoverRoom(room, senderJID)) {
                 for (MUCRole role : room.getOccupants()) {
                     // TODO Should we filter occupants that are invisible (presence is not broadcasted)?
                 	answer.add(new DiscoItem(role.getRoleAddress(), null, null, null));
@@ -1370,12 +1408,23 @@
         return answer.iterator();
     }
 
-    private boolean canDiscoverRoom(MUCRoom room) {
+    private boolean canDiscoverRoom(MUCRoom room, JID senderJID) {
         // Check if locked rooms may be discovered
         if (!allowToDiscoverLockedRooms && room.isLocked()) {
             return false;
         }
-        return room.isPublicRoom();
+        if (!room.isPublicRoom()) {
+            if (!allowToDiscoverMembersOnlyRooms && room.isMembersOnly()) {
+                return false;
+            }
+            Affiliation affiliation = room.getAffiliation(senderJID.toBareJID());
+            if (affiliation != Affiliation.owner
+                    && affiliation != Affiliation.admin
+                    && affiliation != Affiliation.member) {
+                return false;
+            }
+        }
+        return true;
     }
 
     /**
