Merge 0.9.14 changes back to master.
diff --git a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/form/CASTicketField.java b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/form/CASTicketField.java
index 2fb96b0..eb0f68e 100644
--- a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/form/CASTicketField.java
+++ b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/form/CASTicketField.java
@@ -41,6 +41,12 @@
public static final String PARAMETER_NAME = "ticket";
/**
+ * The standard URI name for the CAS login resource.
+ */
+ private static final String CAS_LOGIN_URI = "login";
+
+
+ /**
* The full URI which the field should link to.
*/
private final String authorizationURI;
@@ -57,11 +63,6 @@
* The full URL of the endpoint accepting CAS authentication
* requests.
*
- * @param clientID
- * The ID of the CAS client. This is normally determined ahead of
- * time by the CAS service through some manual credential request
- * procedure.
- *
* @param redirectURI
* The URI that the CAS service should redirect to upon successful
* authentication.
@@ -73,8 +74,16 @@
// Build authorization URI from given values
try {
- this.authorizationURI = authorizationEndpoint
- + "?service=" + URLEncoder.encode(redirectURI, "UTF-8");
+ final StringBuilder sb = new StringBuilder();
+ sb.append(authorizationEndpoint);
+ // user might configure the endpoint with a trailing slash
+ if (sb.charAt(sb.length() - 1) != '/') {
+ sb.append('/');
+ }
+ sb.append(CAS_LOGIN_URI);
+ sb.append("?service=");
+ sb.append(URLEncoder.encode(redirectURI, "UTF-8"));
+ this.authorizationURI = sb.toString();
}
// Java is required to provide UTF-8 support
diff --git a/guacamole-common-js/src/main/webapp/modules/Tunnel.js b/guacamole-common-js/src/main/webapp/modules/Tunnel.js
index 508aa4c..c8f8502 100644
--- a/guacamole-common-js/src/main/webapp/modules/Tunnel.js
+++ b/guacamole-common-js/src/main/webapp/modules/Tunnel.js
@@ -55,6 +55,25 @@
this.sendMessage = function(elements) {};
/**
+ * Changes the stored numeric state of this tunnel, firing the onstatechange
+ * event if the new state is different and a handler has been defined.
+ *
+ * @private
+ * @param {Number} state
+ * The new state of this tunnel.
+ */
+ this.setState = function(state) {
+
+ // Notify only if state changes
+ if (state !== this.state) {
+ this.state = state;
+ if (this.onstatechange)
+ this.onstatechange(state);
+ }
+
+ };
+
+ /**
* The current state of this tunnel.
*
* @type {Number}
@@ -163,8 +182,13 @@
* Whether tunnel requests will be cross-domain, and thus must use CORS
* mechanisms and headers. By default, it is assumed that tunnel requests
* will be made to the same domain.
+ *
+ * @param {Object} [extraTunnelHeaders={}]
+ * Key value pairs containing the header names and values of any additional
+ * headers to be sent in tunnel requests. By default, no extra headers will
+ * be added.
*/
-Guacamole.HTTPTunnel = function(tunnelURL, crossDomain) {
+Guacamole.HTTPTunnel = function(tunnelURL, crossDomain, extraTunnelHeaders) {
/**
* Reference to this HTTP tunnel.
@@ -196,6 +220,32 @@
var receive_timeout = null;
/**
+ * Additional headers to be sent in tunnel requests. This dictionary can be
+ * populated with key/value header pairs to pass information such as authentication
+ * tokens, etc.
+ *
+ * @private
+ */
+ var extraHeaders = extraTunnelHeaders || {};
+
+ /**
+ * Adds the configured additional headers to the given request.
+ *
+ * @param {XMLHttpRequest} request
+ * The request where the configured extra headers will be added.
+ *
+ * @param {Object} headers
+ * The headers to be added to the request.
+ *
+ * @private
+ */
+ function addExtraHeaders(request, headers) {
+ for (var name in headers) {
+ request.setRequestHeader(name, headers[name]);
+ }
+ }
+
+ /**
* Initiates a timeout which, if data is not received, causes the tunnel
* to close with an error.
*
@@ -239,14 +289,11 @@
}
- // Mark as closed
- tunnel.state = Guacamole.Tunnel.State.CLOSED;
-
// Reset output message buffer
sendingMessages = false;
- if (tunnel.onstatechange)
- tunnel.onstatechange(tunnel.state);
+ // Mark as closed
+ tunnel.setState(Guacamole.Tunnel.State.CLOSED);
}
@@ -306,6 +353,7 @@
var message_xmlhttprequest = new XMLHttpRequest();
message_xmlhttprequest.open("POST", TUNNEL_WRITE + tunnel.uuid);
message_xmlhttprequest.withCredentials = withCredentials;
+ addExtraHeaders(message_xmlhttprequest, extraHeaders);
message_xmlhttprequest.setRequestHeader("Content-type", "application/octet-stream");
// Once response received, send next queued event.
@@ -537,6 +585,7 @@
var xmlhttprequest = new XMLHttpRequest();
xmlhttprequest.open("GET", TUNNEL_READ + tunnel.uuid + ":" + (request_id++));
xmlhttprequest.withCredentials = withCredentials;
+ addExtraHeaders(xmlhttprequest, extraHeaders);
xmlhttprequest.send(null);
return xmlhttprequest;
@@ -548,6 +597,9 @@
// Start waiting for connect
reset_timeout();
+ // Mark the tunnel as connecting
+ tunnel.setState(Guacamole.Tunnel.State.CONNECTING);
+
// Start tunnel and connect
var connect_xmlhttprequest = new XMLHttpRequest();
connect_xmlhttprequest.onreadystatechange = function() {
@@ -566,9 +618,8 @@
// Get UUID from response
tunnel.uuid = connect_xmlhttprequest.responseText;
- tunnel.state = Guacamole.Tunnel.State.OPEN;
- if (tunnel.onstatechange)
- tunnel.onstatechange(tunnel.state);
+ // Mark as open
+ tunnel.setState(Guacamole.Tunnel.State.OPEN);
// Start reading data
handleResponse(makeRequest());
@@ -577,6 +628,7 @@
connect_xmlhttprequest.open("POST", TUNNEL_CONNECT, true);
connect_xmlhttprequest.withCredentials = withCredentials;
+ addExtraHeaders(connect_xmlhttprequest, extraHeaders);
connect_xmlhttprequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded; charset=UTF-8");
connect_xmlhttprequest.send(data);
@@ -698,9 +750,7 @@
tunnel.onerror(status);
// Mark as closed
- tunnel.state = Guacamole.Tunnel.State.CLOSED;
- if (tunnel.onstatechange)
- tunnel.onstatechange(tunnel.state);
+ tunnel.setState(Guacamole.Tunnel.State.CLOSED);
socket.close();
@@ -747,6 +797,9 @@
reset_timeout();
+ // Mark the tunnel as connecting
+ tunnel.setState(Guacamole.Tunnel.State.CONNECTING);
+
// Connect socket
socket = new WebSocket(tunnelURL + "?" + data, "guacamole");
@@ -814,9 +867,7 @@
tunnel.uuid = elements[0];
// Tunnel is now open and UUID is available
- tunnel.state = Guacamole.Tunnel.State.OPEN;
- if (tunnel.onstatechange)
- tunnel.onstatechange(tunnel.state);
+ tunnel.setState(Guacamole.Tunnel.State.OPEN);
}
@@ -1040,8 +1091,13 @@
* Whether tunnel requests will be cross-domain, and thus must use CORS
* mechanisms and headers. By default, it is assumed that tunnel requests
* will be made to the same domain.
+ *
+ * @param {Object} [extraTunnelHeaders={}]
+ * Key value pairs containing the header names and values of any additional
+ * headers to be sent in tunnel requests. By default, no extra headers will
+ * be added.
*/
-Guacamole.StaticHTTPTunnel = function StaticHTTPTunnel(url, crossDomain) {
+Guacamole.StaticHTTPTunnel = function StaticHTTPTunnel(url, crossDomain, extraTunnelHeaders) {
/**
* Reference to this Guacamole.StaticHTTPTunnel.
@@ -1060,23 +1116,30 @@
var xhr = null;
/**
- * Changes the stored numeric state of this tunnel, firing the onstatechange
- * event if the new state is different and a handler has been defined.
+ * Additional headers to be sent in tunnel requests. This dictionary can be
+ * populated with key/value header pairs to pass information such as authentication
+ * tokens, etc.
*
* @private
- * @param {Number} state
- * The new state of this tunnel.
*/
- var setState = function setState(state) {
+ var extraHeaders = extraTunnelHeaders || {};
- // Notify only if state changes
- if (state !== tunnel.state) {
- tunnel.state = state;
- if (tunnel.onstatechange)
- tunnel.onstatechange(state);
+ /**
+ * Adds the configured additional headers to the given request.
+ *
+ * @param {XMLHttpRequest} request
+ * The request where the configured extra headers will be added.
+ *
+ * @param {Object} headers
+ * The headers to be added to the request.
+ *
+ * @private
+ */
+ function addExtraHeaders(request, headers) {
+ for (var name in headers) {
+ request.setRequestHeader(name, headers[name]);
}
-
- };
+ }
/**
* Returns the Guacamole protocol status code which most closely
@@ -1133,12 +1196,13 @@
tunnel.disconnect();
// Connection is now starting
- setState(Guacamole.Tunnel.State.CONNECTING);
+ tunnel.setState(Guacamole.Tunnel.State.CONNECTING);
// Start a new connection
xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.withCredentials = !!crossDomain;
+ addExtraHeaders(xhr, extraHeaders);
xhr.responseType = 'text';
xhr.send(null);
@@ -1160,7 +1224,7 @@
if (xhr.readyState === 3 || xhr.readyState === 4) {
// Connection is open
- setState(Guacamole.Tunnel.State.OPEN);
+ tunnel.setState(Guacamole.Tunnel.State.OPEN);
var buffer = xhr.responseText;
var length = buffer.length;
@@ -1200,7 +1264,7 @@
}
// Connection is now closed
- setState(Guacamole.Tunnel.State.CLOSED);
+ tunnel.setState(Guacamole.Tunnel.State.CLOSED);
};