Merge 1.3.0 changes back to master.
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/tunnel/AbstractGuacamoleTunnelService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/tunnel/AbstractGuacamoleTunnelService.java
index 6687eb3..44b65bf 100644
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/tunnel/AbstractGuacamoleTunnelService.java
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/tunnel/AbstractGuacamoleTunnelService.java
@@ -201,87 +201,52 @@
             ModeledConnectionGroup connectionGroup);
 
     /**
-     * Returns a guacamole configuration containing the protocol and parameters
-     * from the given connection. If the ID of an active connection is
-     * provided, that connection will be joined instead of starting a new
-     * primary connection. If tokens are used in the connection parameter
-     * values, credentials from the given user will be substituted
-     * appropriately.
-     *
-     * @param user
-     *     The user whose credentials should be used if necessary.
+     * Returns a GuacamoleConfiguration which connects to the given connection.
+     * If the ID of an active connection is provided, that active connection
+     * will be joined rather than establishing an entirely new connection. If
+     * a sharing profile is provided, the parameters associated with that
+     * sharing profile will be used to define the access provided to the user
+     * accessing the shared connection.
      *
      * @param connection
-     *     The connection whose protocol and parameters should be added to the
-     *     returned configuration.
+     *     The connection that the user is connecting to.
      *
      * @param connectionID
-     *     The ID of the active connection to be joined, as returned by guacd,
-     *     or null if a new primary connection should be established.
-     *
-     * @return
-     *     A GuacamoleConfiguration containing the protocol and parameters from
-     *     the given connection.
-     */
-    private GuacamoleConfiguration getGuacamoleConfiguration(RemoteAuthenticatedUser user,
-            ModeledConnection connection, String connectionID) {
-
-        // Generate configuration from available data
-        GuacamoleConfiguration config = new GuacamoleConfiguration();
-
-        // Join existing active connection, if any
-        if (connectionID != null)
-            config.setConnectionID(connectionID);
-
-        // Set protocol from connection if not joining an active connection
-        else {
-            ConnectionModel model = connection.getModel();
-            config.setProtocol(model.getProtocol());
-        }
-
-        // Set parameters from associated data
-        Collection<ConnectionParameterModel> parameters = connectionParameterMapper.select(connection.getIdentifier());
-        for (ConnectionParameterModel parameter : parameters)
-            config.setParameter(parameter.getName(), parameter.getValue());
-
-        return config;
-        
-    }
-
-    /**
-     * Returns a guacamole configuration which joins the active connection
-     * having the given ID, using the provided sharing profile to restrict the
-     * access provided to the user accessing the shared connection. If tokens
-     * are used in the connection parameter values of the sharing profile,
-     * credentials from the given user will be substituted appropriately.
-     *
-     * @param user
-     *     The user whose credentials should be used if necessary.
+     *     The ID of the active connection being joined, as provided by guacd
+     *     when the original connection was established, or null if a new
+     *     connection should be established instead.
      *
      * @param sharingProfile
      *     The sharing profile whose associated parameters dictate the level
-     *     of access granted to the user joining the connection.
-     *
-     * @param connectionID
-     *     The ID of the connection being joined, as provided by guacd when the
-     *     original connection was established, or null if a new connection
-     *     should be created instead.
+     *     of access granted to the user joining the connection, or null if the
+     *     parameters associated with the connection should be used.
      *
      * @return
-     *     A GuacamoleConfiguration containing the protocol and parameters from
-     *     the given connection.
+     *     A GuacamoleConfiguration defining the requested, possibly shared
+     *     connection.
      */
-    private GuacamoleConfiguration getGuacamoleConfiguration(RemoteAuthenticatedUser user,
-            ModeledSharingProfile sharingProfile, String connectionID) {
+    private GuacamoleConfiguration getGuacamoleConfiguration(
+            ModeledConnection connection, String connectionID,
+            ModeledSharingProfile sharingProfile) {
+
+        ConnectionModel model = connection.getModel();
 
         // Generate configuration from available data
         GuacamoleConfiguration config = new GuacamoleConfiguration();
+        config.setProtocol(model.getProtocol());
         config.setConnectionID(connectionID);
 
         // Set parameters from associated data
-        Collection<SharingProfileParameterModel> parameters = sharingProfileParameterMapper.select(sharingProfile.getIdentifier());
-        for (SharingProfileParameterModel parameter : parameters)
-            config.setParameter(parameter.getName(), parameter.getValue());
+        if (sharingProfile != null) {
+            Collection<SharingProfileParameterModel> parameters = sharingProfileParameterMapper.select(sharingProfile.getIdentifier());
+            for (SharingProfileParameterModel parameter : parameters)
+                config.setParameter(parameter.getName(), parameter.getValue());
+        }
+        else {
+            Collection<ConnectionParameterModel> parameters = connectionParameterMapper.select(connection.getIdentifier());
+            for (ConnectionParameterModel parameter : parameters)
+                config.setParameter(parameter.getName(), parameter.getValue());
+        }
 
         return config;
 
@@ -488,7 +453,7 @@
             if (activeConnection.isPrimaryConnection()) {
                 activeConnections.put(connection.getIdentifier(), activeConnection);
                 activeConnectionGroups.put(connection.getParentIdentifier(), activeConnection);
-                config = getGuacamoleConfiguration(activeConnection.getUser(), connection, activeConnection.getConnectionID());
+                config = getGuacamoleConfiguration(connection, activeConnection.getConnectionID(), null);
             }
 
             // If we ARE joining an active connection under the restrictions of
@@ -502,8 +467,7 @@
 
                 // Build configuration from the sharing profile and the ID of
                 // the connection being joined
-                config = getGuacamoleConfiguration(activeConnection.getUser(),
-                        activeConnection.getSharingProfile(), connectionID);
+                config = getGuacamoleConfiguration(connection, connectionID, activeConnection.getSharingProfile());
 
             }
 
diff --git a/guacamole-common-js/src/main/webapp/modules/Tunnel.js b/guacamole-common-js/src/main/webapp/modules/Tunnel.js
index 149dce4..1464266 100644
--- a/guacamole-common-js/src/main/webapp/modules/Tunnel.js
+++ b/guacamole-common-js/src/main/webapp/modules/Tunnel.js
@@ -74,6 +74,20 @@
     };
 
     /**
+     * Changes the stored UUID that uniquely identifies this tunnel, firing the
+     * onuuid event if a handler has been defined.
+     *
+     * @private
+     * @param {String} uuid
+     *     The new state of this tunnel.
+     */
+    this.setUUID = function setUUID(uuid) {
+        this.uuid = uuid;
+        if (this.onuuid)
+            this.onuuid(uuid);
+    };
+
+    /**
      * Returns whether this tunnel is currently connected.
      *
      * @returns {Boolean}
@@ -120,6 +134,15 @@
     this.uuid = null;
 
     /**
+     * Fired when the UUID that uniquely identifies this tunnel is known.
+     *
+     * @event
+     * @param {String}
+     *     The UUID uniquely identifying this tunnel.
+     */
+    this.onuuid = null;
+
+    /**
      * Fired whenever an error is encountered by the tunnel.
      * 
      * @event
@@ -706,7 +729,7 @@
             reset_timeout();
 
             // Get UUID from response
-            tunnel.uuid = connect_xmlhttprequest.responseText;
+            tunnel.setUUID(connect_xmlhttprequest.responseText);
 
             // Mark as open
             tunnel.setState(Guacamole.Tunnel.State.OPEN);
@@ -1019,7 +1042,7 @@
 
                         // Associate tunnel UUID if received
                         if (opcode === Guacamole.Tunnel.INTERNAL_DATA_OPCODE)
-                            tunnel.uuid = elements[0];
+                            tunnel.setUUID(elements[0]);
 
                         // Tunnel is now open and UUID is available
                         tunnel.setState(Guacamole.Tunnel.State.OPEN);
@@ -1155,11 +1178,18 @@
          * @private
          */
         function commit_tunnel() {
+
             tunnel.onstatechange = chained_tunnel.onstatechange;
             tunnel.oninstruction = chained_tunnel.oninstruction;
             tunnel.onerror = chained_tunnel.onerror;
-            chained_tunnel.uuid = tunnel.uuid;
+            tunnel.onuuid = chained_tunnel.onuuid;
+
+            // Assign UUID if already known
+            if (tunnel.uuid)
+                chained_tunnel.setUUID(tunnel.uuid);
+
             committedTunnel = tunnel;
+
         }
 
         // Wrap own onstatechange within current tunnel
diff --git a/guacamole-common/pom.xml b/guacamole-common/pom.xml
index b8e09d4..f9fb82a 100644
--- a/guacamole-common/pom.xml
+++ b/guacamole-common/pom.xml
@@ -57,14 +57,14 @@
     <build>
         <plugins>
 
-            <!-- Written for 1.6 -->
+            <!-- Written for 1.8 -->
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-compiler-plugin</artifactId>
                 <version>3.3</version>
                 <configuration>
-                    <source>1.6</source>
-                    <target>1.6</target>
+                    <source>1.8</source>
+                    <target>1.8</target>
                     <compilerArgs>
                         <arg>-Xlint:all</arg>
                         <arg>-Werror</arg>
diff --git a/guacamole-common/src/main/java/org/apache/guacamole/net/DelegatingGuacamoleSocket.java b/guacamole-common/src/main/java/org/apache/guacamole/net/DelegatingGuacamoleSocket.java
new file mode 100644
index 0000000..b519629
--- /dev/null
+++ b/guacamole-common/src/main/java/org/apache/guacamole/net/DelegatingGuacamoleSocket.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.guacamole.net;
+
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.io.GuacamoleReader;
+import org.apache.guacamole.io.GuacamoleWriter;
+
+/**
+ * GuacamoleSocket implementation which simply delegates all function calls to
+ * an underlying GuacamoleSocket.
+ */
+public class DelegatingGuacamoleSocket implements GuacamoleSocket {
+
+    /**
+     * The wrapped socket.
+     */
+    private final GuacamoleSocket socket;
+
+    /**
+     * Wraps the given GuacamoleSocket such that all function calls against
+     * this DelegatingGuacamoleSocket will be delegated to it.
+     *
+     * @param socket
+     *     The GuacamoleSocket to wrap.
+     */
+    public DelegatingGuacamoleSocket(GuacamoleSocket socket) {
+        this.socket = socket;
+    }
+
+    /**
+     * Returns the underlying GuacamoleSocket wrapped by this
+     * DelegatingGuacamoleSocket.
+     *
+     * @return
+     *     The GuacamoleSocket wrapped by this DelegatingGuacamoleSocket.
+     */
+    protected GuacamoleSocket getDelegateSocket() {
+        return socket;
+    }
+
+    @Override
+    public String getProtocol() {
+        return socket.getProtocol();
+    }
+
+    @Override
+    public GuacamoleReader getReader() {
+        return socket.getReader();
+    }
+
+    @Override
+    public GuacamoleWriter getWriter() {
+        return socket.getWriter();
+    }
+
+    @Override
+    public void close() throws GuacamoleException {
+        socket.close();
+    }
+
+    @Override
+    public boolean isOpen() {
+        return socket.isOpen();
+    }
+
+}
diff --git a/guacamole-common/src/main/java/org/apache/guacamole/net/GuacamoleSocket.java b/guacamole-common/src/main/java/org/apache/guacamole/net/GuacamoleSocket.java
index 4d084e4..af068fa 100644
--- a/guacamole-common/src/main/java/org/apache/guacamole/net/GuacamoleSocket.java
+++ b/guacamole-common/src/main/java/org/apache/guacamole/net/GuacamoleSocket.java
@@ -30,6 +30,24 @@
 public interface GuacamoleSocket {
 
     /**
+     * Returns the name of the protocol to be used. If the protocol is not
+     * known or the implementation refuses to reveal the underlying protocol,
+     * null is returned.
+     *
+     * <p>Implementations <strong>should</strong> aim to expose the name of the
+     * underlying protocol, such that protocol-specific responses like the
+     * "required" and "argv" instructions can be handled correctly by code
+     * consuming the GuacamoleSocket.
+     *
+     * @return
+     *     The name of the protocol to be used, or null if this information is
+     *     not available.
+     */
+    public default String getProtocol() {
+        return null;
+    }
+
+    /**
      * Returns a GuacamoleReader which can be used to read from the
      * Guacamole instruction stream associated with the connection
      * represented by this GuacamoleSocket.
diff --git a/guacamole-common/src/main/java/org/apache/guacamole/protocol/ConfiguredGuacamoleSocket.java b/guacamole-common/src/main/java/org/apache/guacamole/protocol/ConfiguredGuacamoleSocket.java
index fe4efca..6cf3d7b 100644
--- a/guacamole-common/src/main/java/org/apache/guacamole/protocol/ConfiguredGuacamoleSocket.java
+++ b/guacamole-common/src/main/java/org/apache/guacamole/protocol/ConfiguredGuacamoleSocket.java
@@ -24,6 +24,7 @@
 import org.apache.guacamole.GuacamoleServerException;
 import org.apache.guacamole.io.GuacamoleReader;
 import org.apache.guacamole.io.GuacamoleWriter;
+import org.apache.guacamole.net.DelegatingGuacamoleSocket;
 import org.apache.guacamole.net.GuacamoleSocket;
 
 /**
@@ -36,12 +37,7 @@
  * this GuacamoleSocket from manually controlling the initial protocol
  * handshake.
  */
-public class ConfiguredGuacamoleSocket implements GuacamoleSocket {
-
-    /**
-     * The wrapped socket.
-     */
-    private GuacamoleSocket socket;
+public class ConfiguredGuacamoleSocket extends DelegatingGuacamoleSocket {
 
     /**
      * The configuration to use when performing the Guacamole protocol
@@ -125,7 +121,7 @@
             GuacamoleConfiguration config,
             GuacamoleClientInformation info) throws GuacamoleException {
 
-        this.socket = socket;
+        super(socket);
         this.config = config;
 
         // Get reader and writer
@@ -268,23 +264,8 @@
     }
 
     @Override
-    public GuacamoleWriter getWriter() {
-        return socket.getWriter();
-    }
-
-    @Override
-    public GuacamoleReader getReader() {
-        return socket.getReader();
-    }
-
-    @Override
-    public void close() throws GuacamoleException {
-        socket.close();
-    }
-
-    @Override
-    public boolean isOpen() {
-        return socket.isOpen();
+    public String getProtocol() {
+        return getConfiguration().getProtocol();
     }
 
 }
diff --git a/guacamole-common/src/main/java/org/apache/guacamole/protocol/FailoverGuacamoleSocket.java b/guacamole-common/src/main/java/org/apache/guacamole/protocol/FailoverGuacamoleSocket.java
index 3c64c51..15414c0 100644
--- a/guacamole-common/src/main/java/org/apache/guacamole/protocol/FailoverGuacamoleSocket.java
+++ b/guacamole-common/src/main/java/org/apache/guacamole/protocol/FailoverGuacamoleSocket.java
@@ -28,7 +28,7 @@
 import org.apache.guacamole.GuacamoleUpstreamTimeoutException;
 import org.apache.guacamole.GuacamoleUpstreamUnavailableException;
 import org.apache.guacamole.io.GuacamoleReader;
-import org.apache.guacamole.io.GuacamoleWriter;
+import org.apache.guacamole.net.DelegatingGuacamoleSocket;
 import org.apache.guacamole.net.GuacamoleSocket;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -40,7 +40,7 @@
  * constructor, allowing a different socket to be substituted prior to
  * fulfilling the connection.
  */
-public class FailoverGuacamoleSocket implements GuacamoleSocket {
+public class FailoverGuacamoleSocket extends DelegatingGuacamoleSocket {
 
     /**
      * Logger for this class.
@@ -55,11 +55,6 @@
     private static final int DEFAULT_INSTRUCTION_QUEUE_LIMIT = 131072;
 
     /**
-     * The wrapped socket being used.
-     */
-    private final GuacamoleSocket socket;
-
-    /**
      * Queue of all instructions read while this FailoverGuacamoleSocket was
      * being constructed.
      */
@@ -158,6 +153,8 @@
             final int instructionQueueLimit)
             throws GuacamoleException, GuacamoleUpstreamException {
 
+        super(socket);
+
         int totalQueueSize = 0;
 
         GuacamoleInstruction instruction;
@@ -189,8 +186,6 @@
 
         }
 
-        this.socket = socket;
-
     }
 
     /**
@@ -230,7 +225,7 @@
 
         @Override
         public boolean available() throws GuacamoleException {
-            return !instructionQueue.isEmpty() || socket.getReader().available();
+            return !instructionQueue.isEmpty() || getDelegateSocket().getReader().available();
         }
 
         @Override
@@ -244,7 +239,7 @@
                 return instruction.toString().toCharArray();
             }
 
-            return socket.getReader().read();
+            return getDelegateSocket().getReader().read();
 
         }
 
@@ -258,7 +253,7 @@
             if (!instructionQueue.isEmpty())
                 return instructionQueue.remove();
 
-            return socket.getReader().readInstruction();
+            return getDelegateSocket().getReader().readInstruction();
 
         }
 
@@ -269,19 +264,4 @@
         return queuedReader;
     }
 
-    @Override
-    public GuacamoleWriter getWriter() {
-        return socket.getWriter();
-    }
-
-    @Override
-    public void close() throws GuacamoleException {
-        socket.close();
-    }
-
-    @Override
-    public boolean isOpen() {
-        return socket.isOpen();
-    }
-    
 }
diff --git a/guacamole-common/src/main/java/org/apache/guacamole/protocol/FilteredGuacamoleSocket.java b/guacamole-common/src/main/java/org/apache/guacamole/protocol/FilteredGuacamoleSocket.java
index 5e541d0..c3bfefd 100644
--- a/guacamole-common/src/main/java/org/apache/guacamole/protocol/FilteredGuacamoleSocket.java
+++ b/guacamole-common/src/main/java/org/apache/guacamole/protocol/FilteredGuacamoleSocket.java
@@ -19,21 +19,16 @@
 
 package org.apache.guacamole.protocol;
 
-import org.apache.guacamole.GuacamoleException;
 import org.apache.guacamole.io.GuacamoleReader;
 import org.apache.guacamole.io.GuacamoleWriter;
+import org.apache.guacamole.net.DelegatingGuacamoleSocket;
 import org.apache.guacamole.net.GuacamoleSocket;
 
 /**
  * Implementation of GuacamoleSocket which allows individual instructions to be
  * intercepted, overridden, etc.
  */
-public class FilteredGuacamoleSocket implements GuacamoleSocket {
-
-    /**
-     * Wrapped GuacamoleSocket.
-     */
-    private final GuacamoleSocket socket;
+public class FilteredGuacamoleSocket extends DelegatingGuacamoleSocket {
 
     /**
      * A reader for the wrapped GuacamoleSocket which may be filtered.
@@ -58,7 +53,8 @@
      *                    instructions, if any.
      */
     public FilteredGuacamoleSocket(GuacamoleSocket socket, GuacamoleFilter readFilter, GuacamoleFilter writeFilter) {
-        this.socket = socket;
+
+        super(socket);
 
         // Apply filter to reader
         if (readFilter != null)
@@ -84,14 +80,4 @@
         return writer;
     }
 
-    @Override
-    public void close() throws GuacamoleException {
-        socket.close();
-    }
-
-    @Override
-    public boolean isOpen() {
-        return socket.isOpen();
-    }
-    
 }
diff --git a/guacamole-common/src/main/java/org/apache/guacamole/protocol/GuacamoleConfiguration.java b/guacamole-common/src/main/java/org/apache/guacamole/protocol/GuacamoleConfiguration.java
index 48d7e52..5c96066 100644
--- a/guacamole-common/src/main/java/org/apache/guacamole/protocol/GuacamoleConfiguration.java
+++ b/guacamole-common/src/main/java/org/apache/guacamole/protocol/GuacamoleConfiguration.java
@@ -92,8 +92,7 @@
 
     /**
      * Sets the ID of the connection being joined, if any. If no connection
-     * is being joined, this value must be omitted, and the protocol must be
-     * set instead.
+     * is being joined, this value must be omitted.
      *
      * @param connectionID The ID of the connection being joined.
      */
@@ -103,15 +102,34 @@
 
     /**
      * Returns the name of the protocol to be used.
-     * @return The name of the protocol to be used.
+     *
+     * @return
+     *     The name of the protocol to be used.
      */
     public String getProtocol() {
         return protocol;
     }
 
     /**
-     * Sets the name of the protocol to be used.
-     * @param protocol The name of the protocol to be used.
+     * Sets the name of the protocol to be used. If no connection is being
+     * joined (a new connection is being established), this value must be set.
+     *
+     * <p>If a connection is being joined, <strong>this value should still be
+     * set</strong> to ensure that protocol-specific responses like the
+     * "required" and "argv" instructions can be understood in their proper
+     * context by other code that may consume this GuacamoleConfiguration like
+     * {@link ConfiguredGuacamoleSocket}.
+     *
+     * <p>If this value is unavailable or remains unset, it is still possible
+     * to join an established connection using
+     * {@link #setConnectionID(java.lang.String)}, however protocol-specific
+     * responses like the "required" and "argv" instructions might not be
+     * possible to handle correctly if the underlying protocol is not made
+     * available through some other means to the client receiving those
+     * responses.
+     *
+     * @param protocol
+     *    The name of the protocol to be used.
      */
     public void setProtocol(String protocol) {
         this.protocol = protocol;
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/tunnel/TunnelResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/tunnel/TunnelResource.java
index 5397339..74347b7 100644
--- a/guacamole/src/main/java/org/apache/guacamole/rest/tunnel/TunnelResource.java
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/tunnel/TunnelResource.java
@@ -24,6 +24,7 @@
 import com.google.inject.assistedinject.AssistedInject;
 import javax.ws.rs.Consumes;
 import javax.ws.rs.DefaultValue;
+import javax.ws.rs.GET;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
@@ -31,8 +32,10 @@
 import javax.ws.rs.core.MediaType;
 import org.apache.guacamole.GuacamoleException;
 import org.apache.guacamole.GuacamoleResourceNotFoundException;
+import org.apache.guacamole.environment.Environment;
 import org.apache.guacamole.net.auth.ActiveConnection;
 import org.apache.guacamole.net.auth.UserContext;
+import org.apache.guacamole.protocols.ProtocolInfo;
 import org.apache.guacamole.rest.activeconnection.APIActiveConnection;
 import org.apache.guacamole.rest.directory.DirectoryObjectResource;
 import org.apache.guacamole.rest.directory.DirectoryObjectResourceFactory;
@@ -58,6 +61,12 @@
     private final UserTunnel tunnel;
 
     /**
+     * The Guacamole server environment.
+     */
+    @Inject
+    private Environment environment;
+
+    /**
      * A factory which can be used to create instances of resources representing
      * ActiveConnections.
      */
@@ -107,6 +116,39 @@
     }
 
     /**
+     * Retrieves the underlying protocol used by the connection associated with
+     * this tunnel. If possible, the parameters available for that protocol are
+     * retrieved, as well.
+     *
+     * @return
+     *     A ProtocolInfo object describing the protocol used by the connection
+     *     associated with this tunnel.
+     *
+     * @throws GuacamoleException
+     *     If the protocol used by the connection associated with this tunnel
+     *     cannot be determined.
+     */
+    @GET
+    @Path("protocol")
+    public ProtocolInfo getProtocol() throws GuacamoleException {
+
+        // Pull protocol name from underlying socket
+        String protocol = tunnel.getSocket().getProtocol();
+        if (protocol == null)
+            throw new GuacamoleResourceNotFoundException("Protocol of tunnel is not known/exposed.");
+
+        // If there is no such protocol defined, provide as much info as is
+        // known (just the name)
+        ProtocolInfo info = environment.getProtocol(protocol);
+        if (info == null)
+            return new ProtocolInfo(protocol);
+
+        // All protocol information for this tunnel is known
+        return info;
+
+    }
+
+    /**
      * Intercepts and returns the entire contents of a specific stream.
      *
      * @param streamIndex
diff --git a/guacamole/src/main/webapp/app/client/types/ManagedClient.js b/guacamole/src/main/webapp/app/client/types/ManagedClient.js
index b40a949..12b9f57 100644
--- a/guacamole/src/main/webapp/app/client/types/ManagedClient.js
+++ b/guacamole/src/main/webapp/app/client/types/ManagedClient.js
@@ -386,7 +386,16 @@
                     status.code);
             });
         };
-        
+
+        // Pull protocol-specific information from tunnel once tunnel UUID is
+        // known
+        tunnel.onuuid = function tunnelAssignedUUID(uuid) {
+            tunnelService.getProtocol(uuid).then(function protocolRetrieved(protocol) {
+                managedClient.protocol = protocol.name;
+                managedClient.forms = protocol.connectionForms;
+            }, requestService.WARN);
+        };
+
         // Update connection state as tunnel state changes
         tunnel.onstatechange = function tunnelStateChanged(state) {
             $rootScope.$evalAsync(function updateTunnelState() {
@@ -612,14 +621,9 @@
 
         // If using a connection, pull connection name and protocol information
         if (clientIdentifier.type === ClientIdentifier.Types.CONNECTION) {
-            $q.all({
-                connection : connectionService.getConnection(clientIdentifier.dataSource, clientIdentifier.id),
-                protocols  : schemaService.getProtocols(clientIdentifier.dataSource)
-            })
-            .then(function dataRetrieved(values) {
-                managedClient.name = managedClient.title = values.connection.name;
-                managedClient.protocol = values.connection.protocol;
-                managedClient.forms = values.protocols[values.connection.protocol].connectionForms;
+            connectionService.getConnection(clientIdentifier.dataSource, clientIdentifier.id)
+            .then(function connectionRetrieved(connection) {
+                managedClient.name = managedClient.title = connection.name;
             }, requestService.WARN);
         }
         
@@ -640,14 +644,9 @@
                 // Attempt to retrieve connection details only if the
                 // underlying connection is known
                 if (activeConnection.connectionIdentifier) {
-                    $q.all({
-                        connection : connectionService.getConnection(clientIdentifier.dataSource, activeConnection.connectionIdentifier),
-                        protocols  : schemaService.getProtocols(clientIdentifier.dataSource)
-                    })
-                    .then(function dataRetrieved(values) {
-                        managedClient.name = managedClient.title = values.connection.name;
-                        managedClient.protocol = values.connection.protocol;
-                        managedClient.forms = values.protocols[values.connection.protocol].connectionForms;
+                    connectionService.getConnection(clientIdentifier.dataSource, activeConnection.connectionIdentifier)
+                    .then(function connectionRetrieved(connection) {
+                        managedClient.name = managedClient.title = connection.name;
                     }, requestService.WARN);
                 }
 
diff --git a/guacamole/src/main/webapp/app/rest/services/tunnelService.js b/guacamole/src/main/webapp/app/rest/services/tunnelService.js
index 1dba527..3cfdb35 100644
--- a/guacamole/src/main/webapp/app/rest/services/tunnelService.js
+++ b/guacamole/src/main/webapp/app/rest/services/tunnelService.js
@@ -80,6 +80,36 @@
     };
 
     /**
+     * Makes a request to the REST API to retrieve the underlying protocol of
+     * the connection associated with a particular tunnel, returning a promise
+     * that provides a @link{Protocol} object if successful.
+     *
+     * @param {String} tunnel
+     *     The UUID of the tunnel associated with the Guacamole connection
+     *     whose underlying protocol is being retrieved.
+     *
+     * @returns {Promise.<Protocol>}
+     *     A promise which will resolve with a @link{Protocol} object upon
+     *     success.
+     */
+    service.getProtocol = function getProtocol(tunnel) {
+
+        // Build HTTP parameters set
+        var httpParameters = {
+            token : authenticationService.getCurrentToken()
+        };
+
+        // Retrieve the protocol details of the specified tunnel
+        return requestService({
+            method  : 'GET',
+            url     : 'api/session/tunnels/' + encodeURIComponent(tunnel)
+                        + '/protocol',
+            params  : httpParameters
+        });
+
+    };
+
+    /**
      * Retrieves the set of sharing profiles that the current user can use to
      * share the active connection of the given tunnel.
      *