| /* |
| * 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. |
| */ |
| |
| /** |
| * Provides the ManagedClient class used by the guacClientManager service. |
| */ |
| angular.module('client').factory('ManagedClient', ['$rootScope', '$injector', |
| function defineManagedClient($rootScope, $injector) { |
| |
| // Required types |
| var ClientProperties = $injector.get('ClientProperties'); |
| var ClientIdentifier = $injector.get('ClientIdentifier'); |
| var ClipboardData = $injector.get('ClipboardData'); |
| var ManagedArgument = $injector.get('ManagedArgument'); |
| var ManagedClientState = $injector.get('ManagedClientState'); |
| var ManagedClientThumbnail = $injector.get('ManagedClientThumbnail'); |
| var ManagedDisplay = $injector.get('ManagedDisplay'); |
| var ManagedFilesystem = $injector.get('ManagedFilesystem'); |
| var ManagedFileUpload = $injector.get('ManagedFileUpload'); |
| var ManagedShareLink = $injector.get('ManagedShareLink'); |
| |
| // Required services |
| var $document = $injector.get('$document'); |
| var $q = $injector.get('$q'); |
| var $rootScope = $injector.get('$rootScope'); |
| var $window = $injector.get('$window'); |
| var activeConnectionService = $injector.get('activeConnectionService'); |
| var authenticationService = $injector.get('authenticationService'); |
| var connectionGroupService = $injector.get('connectionGroupService'); |
| var connectionService = $injector.get('connectionService'); |
| var preferenceService = $injector.get('preferenceService'); |
| var requestService = $injector.get('requestService'); |
| var schemaService = $injector.get('schemaService'); |
| var tunnelService = $injector.get('tunnelService'); |
| var guacAudio = $injector.get('guacAudio'); |
| var guacHistory = $injector.get('guacHistory'); |
| var guacImage = $injector.get('guacImage'); |
| var guacVideo = $injector.get('guacVideo'); |
| |
| /** |
| * The minimum amount of time to wait between updates to the client |
| * thumbnail, in milliseconds. |
| * |
| * @type Number |
| */ |
| var THUMBNAIL_UPDATE_FREQUENCY = 5000; |
| |
| /** |
| * Object which serves as a surrogate interface, encapsulating a Guacamole |
| * client while it is active, allowing it to be detached and reattached |
| * from different client views. |
| * |
| * @constructor |
| * @param {ManagedClient|Object} [template={}] |
| * The object whose properties should be copied within the new |
| * ManagedClient. |
| */ |
| var ManagedClient = function ManagedClient(template) { |
| |
| // Use empty object by default |
| template = template || {}; |
| |
| /** |
| * The ID of the connection associated with this client. |
| * |
| * @type String |
| */ |
| this.id = template.id; |
| |
| /** |
| * The actual underlying Guacamole client. |
| * |
| * @type Guacamole.Client |
| */ |
| this.client = template.client; |
| |
| /** |
| * The tunnel being used by the underlying Guacamole client. |
| * |
| * @type Guacamole.Tunnel |
| */ |
| this.tunnel = template.tunnel; |
| |
| /** |
| * The display associated with the underlying Guacamole client. |
| * |
| * @type ManagedDisplay |
| */ |
| this.managedDisplay = template.managedDisplay; |
| |
| /** |
| * The name returned associated with the connection or connection |
| * group in use. |
| * |
| * @type String |
| */ |
| this.name = template.name; |
| |
| /** |
| * The title which should be displayed as the page title for this |
| * client. |
| * |
| * @type String |
| */ |
| this.title = template.title; |
| |
| /** |
| * The name which uniquely identifies the protocol of the connection in |
| * use. If the protocol cannot be determined, such as when a connection |
| * group is in use, this will be null. |
| * |
| * @type {String} |
| */ |
| this.protocol = template.protocol || null; |
| |
| /** |
| * An array of forms describing all known parameters for the connection |
| * in use, including those which may not be editable. |
| * |
| * @type {Form[]} |
| */ |
| this.forms = template.forms || []; |
| |
| /** |
| * The most recently-generated thumbnail for this connection, as |
| * stored within the local connection history. If no thumbnail is |
| * stored, this will be null. |
| * |
| * @type ManagedClientThumbnail |
| */ |
| this.thumbnail = template.thumbnail; |
| |
| /** |
| * The current clipboard contents. |
| * |
| * @type ClipboardData |
| */ |
| this.clipboardData = template.clipboardData || new ClipboardData({ |
| type : 'text/plain', |
| data : '' |
| }); |
| |
| /** |
| * All uploaded files. As files are uploaded, their progress can be |
| * observed through the elements of this array. It is intended that |
| * this array be manipulated externally as needed. |
| * |
| * @type ManagedFileUpload[] |
| */ |
| this.uploads = template.uploads || []; |
| |
| /** |
| * All currently-exposed filesystems. When the Guacamole server exposes |
| * a filesystem object, that object will be made available as a |
| * ManagedFilesystem within this array. |
| * |
| * @type ManagedFilesystem[] |
| */ |
| this.filesystems = template.filesystems || []; |
| |
| /** |
| * All available share links generated for the this ManagedClient via |
| * ManagedClient.createShareLink(). Each resulting share link is stored |
| * under the identifier of its corresponding SharingProfile. |
| * |
| * @type Object.<String, ManagedShareLink> |
| */ |
| this.shareLinks = template.shareLinks || {}; |
| |
| /** |
| * The current state of the Guacamole client (idle, connecting, |
| * connected, terminated with error, etc.). |
| * |
| * @type ManagedClientState |
| */ |
| this.clientState = template.clientState || new ManagedClientState(); |
| |
| /** |
| * Properties associated with the display and behavior of the Guacamole |
| * client. |
| * |
| * @type ClientProperties |
| */ |
| this.clientProperties = template.clientProperties || new ClientProperties(); |
| |
| /** |
| * All editable arguments (connection parameters), stored by their |
| * names. Arguments will only be present within this set if their |
| * current values have been exposed by the server via an inbound "argv" |
| * stream and the server has confirmed that the value may be changed |
| * through a successful "ack" to an outbound "argv" stream. |
| * |
| * @type {Object.<String, ManagedArgument>} |
| */ |
| this.arguments = template.arguments || {}; |
| |
| }; |
| |
| /** |
| * The mimetype of audio data to be sent along the Guacamole connection if |
| * audio input is supported. |
| * |
| * @constant |
| * @type String |
| */ |
| ManagedClient.AUDIO_INPUT_MIMETYPE = 'audio/L16;rate=44100,channels=2'; |
| |
| /** |
| * Returns a promise which resolves with the string of connection |
| * parameters to be passed to the Guacamole client during connection. This |
| * string generally contains the desired connection ID, display resolution, |
| * and supported audio/video/image formats. The returned promise is |
| * guaranteed to resolve successfully. |
| * |
| * @param {ClientIdentifier} identifier |
| * The identifier representing the connection or group to connect to. |
| * |
| * @param {String} [connectionParameters] |
| * Any additional HTTP parameters to pass while connecting. |
| * |
| * @returns {Promise.<String>} |
| * A promise which resolves with the string of connection parameters to |
| * be passed to the Guacamole client, once the string is ready. |
| */ |
| var getConnectString = function getConnectString(identifier, connectionParameters) { |
| |
| var deferred = $q.defer(); |
| |
| // Calculate optimal width/height for display |
| var pixel_density = $window.devicePixelRatio || 1; |
| var optimal_dpi = pixel_density * 96; |
| var optimal_width = $window.innerWidth * pixel_density; |
| var optimal_height = $window.innerHeight * pixel_density; |
| |
| // Build base connect string |
| var connectString = |
| "token=" + encodeURIComponent(authenticationService.getCurrentToken()) |
| + "&GUAC_DATA_SOURCE=" + encodeURIComponent(identifier.dataSource) |
| + "&GUAC_ID=" + encodeURIComponent(identifier.id) |
| + "&GUAC_TYPE=" + encodeURIComponent(identifier.type) |
| + "&GUAC_WIDTH=" + Math.floor(optimal_width) |
| + "&GUAC_HEIGHT=" + Math.floor(optimal_height) |
| + "&GUAC_DPI=" + Math.floor(optimal_dpi) |
| + "&GUAC_TIMEZONE=" + encodeURIComponent(preferenceService.preferences.timezone) |
| + (connectionParameters ? '&' + connectionParameters : ''); |
| |
| // Add audio mimetypes to connect string |
| guacAudio.supported.forEach(function(mimetype) { |
| connectString += "&GUAC_AUDIO=" + encodeURIComponent(mimetype); |
| }); |
| |
| // Add video mimetypes to connect string |
| guacVideo.supported.forEach(function(mimetype) { |
| connectString += "&GUAC_VIDEO=" + encodeURIComponent(mimetype); |
| }); |
| |
| // Add image mimetypes to connect string |
| guacImage.getSupportedMimetypes().then(function supportedMimetypesKnown(mimetypes) { |
| |
| // Add each image mimetype |
| angular.forEach(mimetypes, function addImageMimetype(mimetype) { |
| connectString += "&GUAC_IMAGE=" + encodeURIComponent(mimetype); |
| }); |
| |
| // Connect string is now ready - nothing else is deferred |
| deferred.resolve(connectString); |
| |
| }); |
| |
| return deferred.promise; |
| |
| }; |
| |
| /** |
| * Requests the creation of a new audio stream, recorded from the user's |
| * local audio input device. If audio input is supported by the connection, |
| * an audio stream will be created which will remain open until the remote |
| * desktop requests that it be closed. If the audio stream is successfully |
| * created but is later closed, a new audio stream will automatically be |
| * established to take its place. The mimetype used for all audio streams |
| * produced by this function is defined by |
| * ManagedClient.AUDIO_INPUT_MIMETYPE. |
| * |
| * @param {Guacamole.Client} client |
| * The Guacamole.Client for which the audio stream is being requested. |
| */ |
| var requestAudioStream = function requestAudioStream(client) { |
| |
| // Create new audio stream, associating it with an AudioRecorder |
| var stream = client.createAudioStream(ManagedClient.AUDIO_INPUT_MIMETYPE); |
| var recorder = Guacamole.AudioRecorder.getInstance(stream, ManagedClient.AUDIO_INPUT_MIMETYPE); |
| |
| // If creation of the AudioRecorder failed, simply end the stream |
| if (!recorder) |
| stream.sendEnd(); |
| |
| // Otherwise, ensure that another audio stream is created after this |
| // audio stream is closed |
| else |
| recorder.onclose = requestAudioStream.bind(this, client); |
| |
| }; |
| |
| /** |
| * Creates a new ManagedClient, connecting it to the specified connection |
| * or group. |
| * |
| * @param {String} id |
| * The ID of the connection or group to connect to. This String must be |
| * a valid ClientIdentifier string, as would be generated by |
| * ClientIdentifier.toString(). |
| * |
| * @param {String} [connectionParameters] |
| * Any additional HTTP parameters to pass while connecting. |
| * |
| * @returns {ManagedClient} |
| * A new ManagedClient instance which is connected to the connection or |
| * connection group having the given ID. |
| */ |
| ManagedClient.getInstance = function getInstance(id, connectionParameters) { |
| |
| var tunnel; |
| |
| // If WebSocket available, try to use it. |
| if ($window.WebSocket) |
| tunnel = new Guacamole.ChainedTunnel( |
| new Guacamole.WebSocketTunnel('websocket-tunnel'), |
| new Guacamole.HTTPTunnel('tunnel') |
| ); |
| |
| // If no WebSocket, then use HTTP. |
| else |
| tunnel = new Guacamole.HTTPTunnel('tunnel'); |
| |
| // Get new client instance |
| var client = new Guacamole.Client(tunnel); |
| |
| // Associate new managed client with new client and tunnel |
| var managedClient = new ManagedClient({ |
| id : id, |
| client : client, |
| tunnel : tunnel |
| }); |
| |
| // Fire events for tunnel errors |
| tunnel.onerror = function tunnelError(status) { |
| $rootScope.$apply(function handleTunnelError() { |
| ManagedClientState.setConnectionState(managedClient.clientState, |
| ManagedClientState.ConnectionState.TUNNEL_ERROR, |
| status.code); |
| }); |
| }; |
| |
| // Update connection state as tunnel state changes |
| tunnel.onstatechange = function tunnelStateChanged(state) { |
| $rootScope.$evalAsync(function updateTunnelState() { |
| |
| switch (state) { |
| |
| // Connection is being established |
| case Guacamole.Tunnel.State.CONNECTING: |
| ManagedClientState.setConnectionState(managedClient.clientState, |
| ManagedClientState.ConnectionState.CONNECTING); |
| break; |
| |
| // Connection is established / no longer unstable |
| case Guacamole.Tunnel.State.OPEN: |
| ManagedClientState.setTunnelUnstable(managedClient.clientState, false); |
| break; |
| |
| // Connection is established but misbehaving |
| case Guacamole.Tunnel.State.UNSTABLE: |
| ManagedClientState.setTunnelUnstable(managedClient.clientState, true); |
| break; |
| |
| // Connection has closed |
| case Guacamole.Tunnel.State.CLOSED: |
| ManagedClientState.setConnectionState(managedClient.clientState, |
| ManagedClientState.ConnectionState.DISCONNECTED); |
| break; |
| |
| } |
| |
| }); |
| }; |
| |
| // Update connection state as client state changes |
| client.onstatechange = function clientStateChanged(clientState) { |
| $rootScope.$evalAsync(function updateClientState() { |
| |
| switch (clientState) { |
| |
| // Idle |
| case 0: |
| ManagedClientState.setConnectionState(managedClient.clientState, |
| ManagedClientState.ConnectionState.IDLE); |
| break; |
| |
| // Ignore "connecting" state |
| case 1: // Connecting |
| break; |
| |
| // Connected + waiting |
| case 2: |
| ManagedClientState.setConnectionState(managedClient.clientState, |
| ManagedClientState.ConnectionState.WAITING); |
| break; |
| |
| // Connected |
| case 3: |
| ManagedClientState.setConnectionState(managedClient.clientState, |
| ManagedClientState.ConnectionState.CONNECTED); |
| |
| // Send any clipboard data already provided |
| if (managedClient.clipboardData) |
| ManagedClient.setClipboard(managedClient, managedClient.clipboardData); |
| |
| // Begin streaming audio input if possible |
| requestAudioStream(client); |
| |
| // Update thumbnail with initial display contents |
| ManagedClient.updateThumbnail(managedClient); |
| break; |
| |
| // Update history when disconnecting |
| case 4: // Disconnecting |
| case 5: // Disconnected |
| ManagedClient.updateThumbnail(managedClient); |
| break; |
| |
| } |
| |
| }); |
| }; |
| |
| // Disconnect and update status when the client receives an error |
| client.onerror = function clientError(status) { |
| $rootScope.$apply(function handleClientError() { |
| |
| // Disconnect, if connected |
| client.disconnect(); |
| |
| // Update state |
| ManagedClientState.setConnectionState(managedClient.clientState, |
| ManagedClientState.ConnectionState.CLIENT_ERROR, |
| status.code); |
| |
| }); |
| }; |
| |
| // Automatically update the client thumbnail |
| client.onsync = function syncReceived() { |
| |
| var thumbnail = managedClient.thumbnail; |
| var timestamp = new Date().getTime(); |
| |
| // Update thumbnail if it doesn't exist or is old |
| if (!thumbnail || timestamp - thumbnail.timestamp >= THUMBNAIL_UPDATE_FREQUENCY) { |
| $rootScope.$apply(function updateClientThumbnail() { |
| ManagedClient.updateThumbnail(managedClient); |
| }); |
| } |
| |
| }; |
| |
| // Test for argument mutability whenever an argument value is |
| // received |
| client.onargv = function clientArgumentValueReceived(stream, mimetype, name) { |
| |
| // Ignore arguments which do not use a mimetype currently supported |
| // by the web application |
| if (mimetype !== 'text/plain') |
| return; |
| |
| var reader = new Guacamole.StringReader(stream); |
| |
| // Assemble received data into a single string |
| var value = ''; |
| reader.ontext = function textReceived(text) { |
| value += text; |
| }; |
| |
| // Test mutability once stream is finished, storing the current |
| // value for the argument only if it is mutable |
| reader.onend = function textComplete() { |
| ManagedArgument.getInstance(managedClient, name, value).then(function argumentIsMutable(argument) { |
| managedClient.arguments[name] = argument; |
| }, function ignoreImmutableArguments() {}); |
| }; |
| |
| }; |
| |
| // Handle any received clipboard data |
| client.onclipboard = function clientClipboardReceived(stream, mimetype) { |
| |
| var reader; |
| |
| // If the received data is text, read it as a simple string |
| if (/^text\//.exec(mimetype)) { |
| |
| reader = new Guacamole.StringReader(stream); |
| |
| // Assemble received data into a single string |
| var data = ''; |
| reader.ontext = function textReceived(text) { |
| data += text; |
| }; |
| |
| // Set clipboard contents once stream is finished |
| reader.onend = function textComplete() { |
| $rootScope.$apply(function updateClipboard() { |
| managedClient.clipboardData = new ClipboardData({ |
| type : mimetype, |
| data : data |
| }); |
| }); |
| }; |
| |
| } |
| |
| // Otherwise read the clipboard data as a Blob |
| else { |
| reader = new Guacamole.BlobReader(stream, mimetype); |
| reader.onend = function blobComplete() { |
| $rootScope.$apply(function updateClipboard() { |
| managedClient.clipboardData = new ClipboardData({ |
| type : mimetype, |
| data : reader.getBlob() |
| }); |
| }); |
| }; |
| } |
| |
| }; |
| |
| // Update title when a "name" instruction is received |
| client.onname = function clientNameReceived(name) { |
| $rootScope.$apply(function updateClientTitle() { |
| managedClient.title = name; |
| }); |
| }; |
| |
| // Handle any received files |
| client.onfile = function clientFileReceived(stream, mimetype, filename) { |
| tunnelService.downloadStream(tunnel.uuid, stream, mimetype, filename); |
| }; |
| |
| // Handle any received filesystem objects |
| client.onfilesystem = function fileSystemReceived(object, name) { |
| $rootScope.$apply(function exposeFilesystem() { |
| managedClient.filesystems.push(ManagedFilesystem.getInstance(object, name)); |
| }); |
| }; |
| |
| // Manage the client display |
| managedClient.managedDisplay = ManagedDisplay.getInstance(client.getDisplay()); |
| |
| // Parse connection details from ID |
| var clientIdentifier = ClientIdentifier.fromString(id); |
| |
| // Connect the Guacamole client |
| getConnectString(clientIdentifier, connectionParameters) |
| .then(function connectClient(connectString) { |
| client.connect(connectString); |
| }); |
| |
| // 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; |
| }, requestService.WARN); |
| } |
| |
| // If using a connection group, pull connection name |
| else if (clientIdentifier.type === ClientIdentifier.Types.CONNECTION_GROUP) { |
| connectionGroupService.getConnectionGroup(clientIdentifier.dataSource, clientIdentifier.id) |
| .then(function connectionGroupRetrieved(group) { |
| managedClient.name = managedClient.title = group.name; |
| }, requestService.WARN); |
| } |
| |
| // If using an active connection, pull corresponding connection, then |
| // pull connection name and protocol information from that |
| else if (clientIdentifier.type === ClientIdentifier.Types.ACTIVE_CONNECTION) { |
| activeConnectionService.getActiveConnection(clientIdentifier.dataSource, clientIdentifier.id) |
| .then(function activeConnectionRetrieved(activeConnection) { |
| |
| // 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; |
| }, requestService.WARN); |
| } |
| |
| }, requestService.WARN); |
| } |
| |
| return managedClient; |
| |
| }; |
| |
| /** |
| * Uploads the given file to the server through the given Guacamole client. |
| * The file transfer can be monitored through the corresponding entry in |
| * the uploads array of the given managedClient. |
| * |
| * @param {ManagedClient} managedClient |
| * The ManagedClient through which the file is to be uploaded. |
| * |
| * @param {File} file |
| * The file to upload. |
| * |
| * @param {ManagedFilesystem} [filesystem] |
| * The filesystem to upload the file to, if any. If not specified, the |
| * file will be sent as a generic Guacamole file stream. |
| * |
| * @param {ManagedFilesystem.File} [directory=filesystem.currentDirectory] |
| * The directory within the given filesystem to upload the file to. If |
| * not specified, but a filesystem is given, the current directory of |
| * that filesystem will be used. |
| */ |
| ManagedClient.uploadFile = function uploadFile(managedClient, file, filesystem, directory) { |
| |
| // Use generic Guacamole file streams by default |
| var object = null; |
| var streamName = null; |
| |
| // If a filesystem is given, determine the destination object and stream |
| if (filesystem) { |
| object = filesystem.object; |
| streamName = (directory || filesystem.currentDirectory).streamName + '/' + file.name; |
| } |
| |
| // Start and manage file upload |
| managedClient.uploads.push(ManagedFileUpload.getInstance(managedClient, file, object, streamName)); |
| |
| }; |
| |
| /** |
| * Sends the given clipboard data over the given Guacamole client, setting |
| * the contents of the remote clipboard to the data provided. |
| * |
| * @param {ManagedClient} managedClient |
| * The ManagedClient over which the given clipboard data is to be sent. |
| * |
| * @param {ClipboardData} data |
| * The clipboard data to send. |
| */ |
| ManagedClient.setClipboard = function setClipboard(managedClient, data) { |
| |
| var writer; |
| |
| // Create stream with proper mimetype |
| var stream = managedClient.client.createClipboardStream(data.type); |
| |
| // Send data as a string if it is stored as a string |
| if (typeof data.data === 'string') { |
| writer = new Guacamole.StringWriter(stream); |
| writer.sendText(data.data); |
| writer.sendEnd(); |
| } |
| |
| // Otherwise, assume the data is a File/Blob |
| else { |
| |
| // Write File/Blob asynchronously |
| writer = new Guacamole.BlobWriter(stream); |
| writer.oncomplete = function clipboardSent() { |
| writer.sendEnd(); |
| }; |
| |
| // Begin sending data |
| writer.sendBlob(data.data); |
| |
| } |
| |
| }; |
| |
| /** |
| * Assigns the given value to the connection parameter having the given |
| * name, updating the behavior of the connection in real-time. If the |
| * connection parameter is not editable, this function has no effect. |
| * |
| * @param {ManagedClient} managedClient |
| * The ManagedClient instance associated with the active connection |
| * being modified. |
| * |
| * @param {String} name |
| * The name of the connection parameter to modify. |
| * |
| * @param {String} value |
| * The value to attempt to assign to the given connection parameter. |
| */ |
| ManagedClient.setArgument = function setArgument(managedClient, name, value) { |
| var managedArgument = managedClient.arguments[name]; |
| if (managedArgument && ManagedArgument.setValue(managedArgument, value)) |
| delete managedClient.arguments[name]; |
| }; |
| |
| /** |
| * Retrieves the current values of all editable connection parameters as a |
| * set of name/value pairs suitable for use as the model of a form which |
| * edits those parameters. |
| * |
| * @param {ManagedClient} client |
| * The ManagedClient instance associated with the active connection |
| * whose parameter values are being retrieved. |
| * |
| * @returns {Object.<String, String>} |
| * A new set of name/value pairs containing the current values of all |
| * editable parameters. |
| */ |
| ManagedClient.getArgumentModel = function getArgumentModel(client) { |
| |
| var model = {}; |
| |
| angular.forEach(client.arguments, function addModelEntry(managedArgument) { |
| model[managedArgument.name] = managedArgument.value; |
| }); |
| |
| return model; |
| |
| }; |
| |
| /** |
| * Produces a sharing link for the given ManagedClient using the given |
| * sharing profile. The resulting sharing link, and any required login |
| * information, can be retrieved from the <code>shareLinks</code> property |
| * of the given ManagedClient once the various underlying service calls |
| * succeed. |
| * |
| * @param {ManagedClient} client |
| * The ManagedClient which will be shared via the generated sharing |
| * link. |
| * |
| * @param {SharingProfile} sharingProfile |
| * The sharing profile to use to generate the sharing link. |
| * |
| * @returns {Promise} |
| * A Promise which is resolved once the sharing link has been |
| * successfully generated, and rejected if generating the link fails. |
| */ |
| ManagedClient.createShareLink = function createShareLink(client, sharingProfile) { |
| |
| // Retrieve sharing credentials for the sake of generating a share link |
| var credentialRequest = tunnelService.getSharingCredentials( |
| client.tunnel.uuid, sharingProfile.identifier); |
| |
| // Add a new share link once the credentials are ready |
| credentialRequest.then(function sharingCredentialsReceived(sharingCredentials) { |
| client.shareLinks[sharingProfile.identifier] = |
| ManagedShareLink.getInstance(sharingProfile, sharingCredentials); |
| }, requestService.WARN); |
| |
| return credentialRequest; |
| |
| }; |
| |
| /** |
| * Returns whether the given ManagedClient is being shared. A ManagedClient |
| * is shared if it has any associated share links. |
| * |
| * @param {ManagedClient} client |
| * The ManagedClient to check. |
| * |
| * @returns {Boolean} |
| * true if the ManagedClient has at least one associated share link, |
| * false otherwise. |
| */ |
| ManagedClient.isShared = function isShared(client) { |
| |
| // The connection is shared if at least one share link exists |
| for (var dummy in client.shareLinks) |
| return true; |
| |
| // No share links currently exist |
| return false; |
| |
| }; |
| |
| /** |
| * Store the thumbnail of the given managed client within the connection |
| * history under its associated ID. If the client is not connected, this |
| * function has no effect. |
| * |
| * @param {ManagedClient} managedClient |
| * The client whose history entry should be updated. |
| */ |
| ManagedClient.updateThumbnail = function updateThumbnail(managedClient) { |
| |
| var display = managedClient.client.getDisplay(); |
| |
| // Update stored thumbnail of previous connection |
| if (display && display.getWidth() > 0 && display.getHeight() > 0) { |
| |
| // Get screenshot |
| var canvas = display.flatten(); |
| |
| // Calculate scale of thumbnail (max 320x240, max zoom 100%) |
| var scale = Math.min(320 / canvas.width, 240 / canvas.height, 1); |
| |
| // Create thumbnail canvas |
| var thumbnail = $document[0].createElement("canvas"); |
| thumbnail.width = canvas.width*scale; |
| thumbnail.height = canvas.height*scale; |
| |
| // Scale screenshot to thumbnail |
| var context = thumbnail.getContext("2d"); |
| context.drawImage(canvas, |
| 0, 0, canvas.width, canvas.height, |
| 0, 0, thumbnail.width, thumbnail.height |
| ); |
| |
| // Store updated thumbnail within client |
| managedClient.thumbnail = new ManagedClientThumbnail({ |
| timestamp : new Date().getTime(), |
| canvas : thumbnail |
| }); |
| |
| // Update historical thumbnail |
| guacHistory.updateThumbnail(managedClient.id, thumbnail.toDataURL("image/png")); |
| |
| } |
| |
| }; |
| |
| return ManagedClient; |
| |
| }]); |