GUACAMOLE-1: Refactor org.glyptodon package/groupId to org.apache.

This commit is contained in:
Michael Jumper
2016-03-22 14:05:53 -07:00
parent 2358d88683
commit 6990344697
402 changed files with 1536 additions and 1536 deletions

View File

@@ -0,0 +1,71 @@
/*
* Copyright (C) 2013 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.apache.guacamole;
import org.apache.guacamole.protocol.GuacamoleStatus;
/**
* An exception which is thrown when data has been submitted with an unsupported
* mimetype.
*
* @author Michael Jumper
*/
public class GuacamoleClientBadTypeException extends GuacamoleClientException {
/**
* Creates a new GuacamoleClientBadTypeException with the given message and cause.
*
* @param message A human readable description of the exception that
* occurred.
* @param cause The cause of this exception.
*/
public GuacamoleClientBadTypeException(String message, Throwable cause) {
super(message, cause);
}
/**
* Creates a new GuacamoleClientBadTypeException with the given message.
*
* @param message A human readable description of the exception that
* occurred.
*/
public GuacamoleClientBadTypeException(String message) {
super(message);
}
/**
* Creates a new GuacamoleClientBadTypeException with the given cause.
*
* @param cause The cause of this exception.
*/
public GuacamoleClientBadTypeException(Throwable cause) {
super(cause);
}
@Override
public GuacamoleStatus getStatus() {
return GuacamoleStatus.CLIENT_BAD_TYPE;
}
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright (C) 2013 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.apache.guacamole;
import org.apache.guacamole.protocol.GuacamoleStatus;
/**
* A generic exception thrown when part of the Guacamole API encounters
* an error in the client's request. Such an error, if correctable, usually
* requires correction on the client side, not the server.
*
* @author Michael Jumper
*/
public class GuacamoleClientException extends GuacamoleException {
/**
* Creates a new GuacamoleException with the given message and cause.
*
* @param message A human readable description of the exception that
* occurred.
* @param cause The cause of this exception.
*/
public GuacamoleClientException(String message, Throwable cause) {
super(message, cause);
}
/**
* Creates a new GuacamoleException with the given message.
*
* @param message A human readable description of the exception that
* occurred.
*/
public GuacamoleClientException(String message) {
super(message);
}
/**
* Creates a new GuacamoleException with the given cause.
*
* @param cause The cause of this exception.
*/
public GuacamoleClientException(Throwable cause) {
super(cause);
}
@Override
public GuacamoleStatus getStatus() {
return GuacamoleStatus.CLIENT_BAD_REQUEST;
}
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright (C) 2013 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.apache.guacamole;
import org.apache.guacamole.protocol.GuacamoleStatus;
/**
* An exception which is thrown when the client has sent too much data. This
* usually indicates that a server-side buffer is not large enough to
* accommodate the data, or protocol specifications prohibit data of the size
* received.
*
* @author Michael Jumper
*/
public class GuacamoleClientOverrunException extends GuacamoleClientException {
/**
* Creates a new GuacamoleClientOverrunException with the given message and cause.
*
* @param message A human readable description of the exception that
* occurred.
* @param cause The cause of this exception.
*/
public GuacamoleClientOverrunException(String message, Throwable cause) {
super(message, cause);
}
/**
* Creates a new GuacamoleClientOverrunException with the given message.
*
* @param message A human readable description of the exception that
* occurred.
*/
public GuacamoleClientOverrunException(String message) {
super(message);
}
/**
* Creates a new GuacamoleClientOverrunException with the given cause.
*
* @param cause The cause of this exception.
*/
public GuacamoleClientOverrunException(Throwable cause) {
super(cause);
}
@Override
public GuacamoleStatus getStatus() {
return GuacamoleStatus.CLIENT_OVERRUN;
}
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright (C) 2013 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.apache.guacamole;
import org.apache.guacamole.protocol.GuacamoleStatus;
/**
* An exception which is thrown when the client is taking too long to respond.
*
* @author Michael Jumper
*/
public class GuacamoleClientTimeoutException extends GuacamoleClientException {
/**
* Creates a new GuacamoleClientTimeoutException with the given message and cause.
*
* @param message A human readable description of the exception that
* occurred.
* @param cause The cause of this exception.
*/
public GuacamoleClientTimeoutException(String message, Throwable cause) {
super(message, cause);
}
/**
* Creates a new GuacamoleClientTimeoutException with the given message.
*
* @param message A human readable description of the exception that
* occurred.
*/
public GuacamoleClientTimeoutException(String message) {
super(message);
}
/**
* Creates a new GuacamoleClientTimeoutException with the given cause.
*
* @param cause The cause of this exception.
*/
public GuacamoleClientTimeoutException(Throwable cause) {
super(cause);
}
@Override
public GuacamoleStatus getStatus() {
return GuacamoleStatus.CLIENT_TIMEOUT;
}
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright (C) 2013 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.apache.guacamole;
import org.apache.guacamole.protocol.GuacamoleStatus;
/**
* An exception which is thrown when too many requests have been received
* by the current client, and further requests are being rejected, either
* temporarily or permanently.
*
* @author Michael Jumper
*/
public class GuacamoleClientTooManyException extends GuacamoleClientException {
/**
* Creates a new GuacamoleClientTooManyException with the given message and cause.
*
* @param message A human readable description of the exception that
* occurred.
* @param cause The cause of this exception.
*/
public GuacamoleClientTooManyException(String message, Throwable cause) {
super(message, cause);
}
/**
* Creates a new GuacamoleClientTooManyException with the given message.
*
* @param message A human readable description of the exception that
* occurred.
*/
public GuacamoleClientTooManyException(String message) {
super(message);
}
/**
* Creates a new GuacamoleClientTooManyException with the given cause.
*
* @param cause The cause of this exception.
*/
public GuacamoleClientTooManyException(Throwable cause) {
super(cause);
}
@Override
public GuacamoleStatus getStatus() {
return GuacamoleStatus.CLIENT_TOO_MANY;
}
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright (C) 2014 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.apache.guacamole;
import org.apache.guacamole.protocol.GuacamoleStatus;
/**
* An exception which is thrown when an operation cannot be performed because
* its corresponding connection is closed.
*
* @author Michael Jumper
*/
public class GuacamoleConnectionClosedException extends GuacamoleServerException {
/**
* Creates a new GuacamoleConnectionClosedException with the given message
* and cause.
*
* @param message A human readable description of the exception that
* occurred.
* @param cause The cause of this exception.
*/
public GuacamoleConnectionClosedException(String message, Throwable cause) {
super(message, cause);
}
/**
* Creates a new GuacamoleConnectionClosedException with the given message.
*
* @param message A human readable description of the exception that
* occurred.
*/
public GuacamoleConnectionClosedException(String message) {
super(message);
}
/**
* Creates a new GuacamoleConnectionClosedException with the given cause.
*
* @param cause The cause of this exception.
*/
public GuacamoleConnectionClosedException(Throwable cause) {
super(cause);
}
@Override
public GuacamoleStatus getStatus() {
return GuacamoleStatus.SERVER_ERROR;
}
}

View File

@@ -0,0 +1,77 @@
/*
* Copyright (C) 2013 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.apache.guacamole;
import org.apache.guacamole.protocol.GuacamoleStatus;
/**
* A generic exception thrown when parts of the Guacamole API encounter
* errors.
*
* @author Michael Jumper
*/
public class GuacamoleException extends Exception {
/**
* Creates a new GuacamoleException with the given message and cause.
*
* @param message A human readable description of the exception that
* occurred.
* @param cause The cause of this exception.
*/
public GuacamoleException(String message, Throwable cause) {
super(message, cause);
}
/**
* Creates a new GuacamoleException with the given message.
*
* @param message A human readable description of the exception that
* occurred.
*/
public GuacamoleException(String message) {
super(message);
}
/**
* Creates a new GuacamoleException with the given cause.
*
* @param cause The cause of this exception.
*/
public GuacamoleException(Throwable cause) {
super(cause);
}
/**
* Returns the Guacamole status associated with this exception. This status
* can then be easily translated into an HTTP error code or Guacamole
* protocol error code.
*
* @return The corresponding Guacamole status.
*/
public GuacamoleStatus getStatus() {
return GuacamoleStatus.SERVER_ERROR;
}
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright (C) 2013 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.apache.guacamole;
import org.apache.guacamole.protocol.GuacamoleStatus;
/**
* An exception which is thrown when a resource has been requested, but that
* resource is locked or currently in use, and cannot be accessed by the
* current user.
*
* @author Michael Jumper
*/
public class GuacamoleResourceConflictException extends GuacamoleClientException {
/**
* Creates a new GuacamoleResourceConflictException with the given message and cause.
*
* @param message A human readable description of the exception that
* occurred.
* @param cause The cause of this exception.
*/
public GuacamoleResourceConflictException(String message, Throwable cause) {
super(message, cause);
}
/**
* Creates a new GuacamoleResourceConflictException with the given message.
*
* @param message A human readable description of the exception that
* occurred.
*/
public GuacamoleResourceConflictException(String message) {
super(message);
}
/**
* Creates a new GuacamoleResourceConflictException with the given cause.
*
* @param cause The cause of this exception.
*/
public GuacamoleResourceConflictException(Throwable cause) {
super(cause);
}
@Override
public GuacamoleStatus getStatus() {
return GuacamoleStatus.RESOURCE_CONFLICT;
}
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright (C) 2013 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.apache.guacamole;
import org.apache.guacamole.protocol.GuacamoleStatus;
/**
* A generic exception thrown when part of the Guacamole API fails to find
* a requested resource, such as a configuration or tunnel.
*
* @author Michael Jumper
*/
public class GuacamoleResourceNotFoundException extends GuacamoleClientException {
/**
* Creates a new GuacamoleResourceNotFoundException with the given message
* and cause.
*
* @param message A human readable description of the exception that
* occurred.
* @param cause The cause of this exception.
*/
public GuacamoleResourceNotFoundException(String message, Throwable cause) {
super(message, cause);
}
/**
* Creates a new GuacamoleResourceNotFoundException with the given message.
*
* @param message A human readable description of the exception that
* occurred.
*/
public GuacamoleResourceNotFoundException(String message) {
super(message);
}
/**
* Creates a new GuacamoleResourceNotFoundException with the given cause.
*
* @param cause The cause of this exception.
*/
public GuacamoleResourceNotFoundException(Throwable cause) {
super(cause);
}
@Override
public GuacamoleStatus getStatus() {
return GuacamoleStatus.RESOURCE_NOT_FOUND;
}
}

View File

@@ -0,0 +1,71 @@
/*
* Copyright (C) 2013 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.apache.guacamole;
import org.apache.guacamole.protocol.GuacamoleStatus;
/**
* A security-related exception thrown when parts of the Guacamole API is
* denying access to a resource.
*
* @author Michael Jumper
*/
public class GuacamoleSecurityException extends GuacamoleClientException {
/**
* Creates a new GuacamoleSecurityException with the given message and cause.
*
* @param message A human readable description of the exception that
* occurred.
* @param cause The cause of this exception.
*/
public GuacamoleSecurityException(String message, Throwable cause) {
super(message, cause);
}
/**
* Creates a new GuacamoleSecurityException with the given message.
*
* @param message A human readable description of the exception that
* occurred.
*/
public GuacamoleSecurityException(String message) {
super(message);
}
/**
* Creates a new GuacamoleSecurityException with the given cause.
*
* @param cause The cause of this exception.
*/
public GuacamoleSecurityException(Throwable cause) {
super(cause);
}
@Override
public GuacamoleStatus getStatus() {
return GuacamoleStatus.CLIENT_FORBIDDEN;
}
}

View File

@@ -0,0 +1,71 @@
/*
* Copyright (C) 2013 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.apache.guacamole;
import org.apache.guacamole.protocol.GuacamoleStatus;
/**
* An exception which is thrown when the server is too busy to service the
* request.
*
* @author Michael Jumper
*/
public class GuacamoleServerBusyException extends GuacamoleServerException {
/**
* Creates a new GuacamoleServerBusyException with the given message and cause.
*
* @param message A human readable description of the exception that
* occurred.
* @param cause The cause of this exception.
*/
public GuacamoleServerBusyException(String message, Throwable cause) {
super(message, cause);
}
/**
* Creates a new GuacamoleServerBusyException with the given message.
*
* @param message A human readable description of the exception that
* occurred.
*/
public GuacamoleServerBusyException(String message) {
super(message);
}
/**
* Creates a new GuacamoleServerBusyException with the given cause.
*
* @param cause The cause of this exception.
*/
public GuacamoleServerBusyException(Throwable cause) {
super(cause);
}
@Override
public GuacamoleStatus getStatus() {
return GuacamoleStatus.SERVER_BUSY;
}
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright (C) 2013 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.apache.guacamole;
import org.apache.guacamole.protocol.GuacamoleStatus;
/**
* A generic exception thrown when part of the Guacamole API encounters
* an unexpected, internal error. An internal error, if correctable, would
* require correction on the server side, not the client.
*
* @author Michael Jumper
*/
public class GuacamoleServerException extends GuacamoleException {
/**
* Creates a new GuacamoleServerException with the given message and cause.
*
* @param message A human readable description of the exception that
* occurred.
* @param cause The cause of this exception.
*/
public GuacamoleServerException(String message, Throwable cause) {
super(message, cause);
}
/**
* Creates a new GuacamoleServerException with the given message.
*
* @param message A human readable description of the exception that
* occurred.
*/
public GuacamoleServerException(String message) {
super(message);
}
/**
* Creates a new GuacamoleServerException with the given cause.
*
* @param cause The cause of this exception.
*/
public GuacamoleServerException(Throwable cause) {
super(cause);
}
@Override
public GuacamoleStatus getStatus() {
return GuacamoleStatus.SERVER_ERROR;
}
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright (C) 2013 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.apache.guacamole;
import org.apache.guacamole.protocol.GuacamoleStatus;
/**
* A security-related exception thrown when parts of the Guacamole API is
* denying access to a resource, but access MAY be granted were the user
* authorized (logged in).
*
* @author Michael Jumper
*/
public class GuacamoleUnauthorizedException extends GuacamoleSecurityException {
/**
* Creates a new GuacamoleUnauthorizedException with the given message and cause.
*
* @param message A human readable description of the exception that
* occurred.
* @param cause The cause of this exception.
*/
public GuacamoleUnauthorizedException(String message, Throwable cause) {
super(message, cause);
}
/**
* Creates a new GuacamoleUnauthorizedException with the given message.
*
* @param message A human readable description of the exception that
* occurred.
*/
public GuacamoleUnauthorizedException(String message) {
super(message);
}
/**
* Creates a new GuacamoleUnauthorizedException with the given cause.
*
* @param cause The cause of this exception.
*/
public GuacamoleUnauthorizedException(Throwable cause) {
super(cause);
}
@Override
public GuacamoleStatus getStatus() {
return GuacamoleStatus.CLIENT_UNAUTHORIZED;
}
}

View File

@@ -0,0 +1,71 @@
/*
* Copyright (C) 2013 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.apache.guacamole;
import org.apache.guacamole.protocol.GuacamoleStatus;
/**
* An exception which is thrown when the requested operation is unsupported
* or unimplemented.
*
* @author Michael Jumper
*/
public class GuacamoleUnsupportedException extends GuacamoleServerException {
/**
* Creates a new GuacamoleUnsupportedException with the given message and cause.
*
* @param message A human readable description of the exception that
* occurred.
* @param cause The cause of this exception.
*/
public GuacamoleUnsupportedException(String message, Throwable cause) {
super(message, cause);
}
/**
* Creates a new GuacamoleUnsupportedException with the given message.
*
* @param message A human readable description of the exception that
* occurred.
*/
public GuacamoleUnsupportedException(String message) {
super(message);
}
/**
* Creates a new GuacamoleUnsupportedException with the given cause.
*
* @param cause The cause of this exception.
*/
public GuacamoleUnsupportedException(Throwable cause) {
super(cause);
}
@Override
public GuacamoleStatus getStatus() {
return GuacamoleStatus.UNSUPPORTED;
}
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright (C) 2013 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.apache.guacamole;
import org.apache.guacamole.protocol.GuacamoleStatus;
/**
* An exception which indicates than an upstream server (such as the remote
* desktop) is returning an error or is otherwise unreachable.
*
* @author Michael Jumper
*/
public class GuacamoleUpstreamException extends GuacamoleException {
/**
* Creates a new GuacamoleUpstreamException with the given message and
* cause.
*
* @param message A human readable description of the exception that
* occurred.
* @param cause The cause of this exception.
*/
public GuacamoleUpstreamException(String message, Throwable cause) {
super(message, cause);
}
/**
* Creates a new GuacamoleUpstreamException with the given message.
*
* @param message A human readable description of the exception that
* occurred.
*/
public GuacamoleUpstreamException(String message) {
super(message);
}
/**
* Creates a new GuacamoleUpstreamException with the given cause.
*
* @param cause The cause of this exception.
*/
public GuacamoleUpstreamException(Throwable cause) {
super(cause);
}
@Override
public GuacamoleStatus getStatus() {
return GuacamoleStatus.UPSTREAM_ERROR;
}
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright (C) 2013 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.apache.guacamole;
import org.apache.guacamole.protocol.GuacamoleStatus;
/**
* An exception which indicates than an upstream server (such as the remote
* desktop) is taking too long to respond.
*
* @author Michael Jumper
*/
public class GuacamoleUpstreamTimeoutException extends GuacamoleUpstreamException {
/**
* Creates a new GuacamoleUpstreamException with the given message and
* cause.
*
* @param message A human readable description of the exception that
* occurred.
* @param cause The cause of this exception.
*/
public GuacamoleUpstreamTimeoutException(String message, Throwable cause) {
super(message, cause);
}
/**
* Creates a new GuacamoleUpstreamException with the given message.
*
* @param message A human readable description of the exception that
* occurred.
*/
public GuacamoleUpstreamTimeoutException(String message) {
super(message);
}
/**
* Creates a new GuacamoleUpstreamException with the given cause.
*
* @param cause The cause of this exception.
*/
public GuacamoleUpstreamTimeoutException(Throwable cause) {
super(cause);
}
@Override
public GuacamoleStatus getStatus() {
return GuacamoleStatus.UPSTREAM_TIMEOUT;
}
}

View File

@@ -0,0 +1,75 @@
/*
* Copyright (C) 2013 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.apache.guacamole.io;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.protocol.GuacamoleInstruction;
/**
* Provides abstract and raw character read access to a stream of Guacamole
* instructions.
*
* @author Michael Jumper
*/
public interface GuacamoleReader {
/**
* Returns whether instruction data is available for reading. Note that
* this does not guarantee an entire instruction is available. If a full
* instruction is not available, this function can return true, and a call
* to read() will still block.
*
* @return true if instruction data is available for reading, false
* otherwise.
* @throws GuacamoleException If an error occurs while checking for
* available data.
*/
public boolean available() throws GuacamoleException;
/**
* Reads at least one complete Guacamole instruction, returning a buffer
* containing one or more complete Guacamole instructions and no
* incomplete Guacamole instructions. This function will block until at
* least one complete instruction is available.
*
* @return A buffer containing at least one complete Guacamole instruction,
* or null if no more instructions are available for reading.
* @throws GuacamoleException If an error occurs while reading from the
* stream.
*/
public char[] read() throws GuacamoleException;
/**
* Reads exactly one complete Guacamole instruction and returns the fully
* parsed instruction.
*
* @return The next complete instruction from the stream, fully parsed, or
* null if no more instructions are available for reading.
* @throws GuacamoleException If an error occurs while reading from the
* stream, or if the instruction cannot be
* parsed.
*/
public GuacamoleInstruction readInstruction() throws GuacamoleException;
}

View File

@@ -0,0 +1,71 @@
/*
* Copyright (C) 2013 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.apache.guacamole.io;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.protocol.GuacamoleInstruction;
/**
* Provides abstract and raw character write access to a stream of Guacamole
* instructions.
*
* @author Michael Jumper
*/
public interface GuacamoleWriter {
/**
* Writes a portion of the given array of characters to the Guacamole
* instruction stream. The portion must contain only complete Guacamole
* instructions.
*
* @param chunk An array of characters containing Guacamole instructions.
* @param off The start offset of the portion of the array to write.
* @param len The length of the portion of the array to write.
* @throws GuacamoleException If an error occurred while writing the
* portion of the array specified.
*/
public void write(char[] chunk, int off, int len) throws GuacamoleException;
/**
* Writes the entire given array of characters to the Guacamole instruction
* stream. The array must consist only of complete Guacamole instructions.
*
* @param chunk An array of characters consisting only of complete
* Guacamole instructions.
* @throws GuacamoleException If an error occurred while writing the
* the specified array.
*/
public void write(char[] chunk) throws GuacamoleException;
/**
* Writes the given fully parsed instruction to the Guacamole instruction
* stream.
*
* @param instruction The Guacamole instruction to write.
* @throws GuacamoleException If an error occurred while writing the
* instruction.
*/
public void writeInstruction(GuacamoleInstruction instruction) throws GuacamoleException;
}

View File

@@ -0,0 +1,272 @@
/*
* Copyright (C) 2013 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.apache.guacamole.io;
import java.io.IOException;
import java.io.Reader;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.util.Deque;
import java.util.LinkedList;
import org.apache.guacamole.GuacamoleConnectionClosedException;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.GuacamoleServerException;
import org.apache.guacamole.GuacamoleUpstreamTimeoutException;
import org.apache.guacamole.protocol.GuacamoleInstruction;
/**
* A GuacamoleReader which wraps a standard Java Reader, using that Reader as
* the Guacamole instruction stream.
*
* @author Michael Jumper
*/
public class ReaderGuacamoleReader implements GuacamoleReader {
/**
* Wrapped Reader to be used for all input.
*/
private Reader input;
/**
* Creates a new ReaderGuacamoleReader which will use the given Reader as
* the Guacamole instruction stream.
*
* @param input The Reader to use as the Guacamole instruction stream.
*/
public ReaderGuacamoleReader(Reader input) {
this.input = input;
}
/**
* The location within the received data buffer that parsing should begin
* when more data is read.
*/
private int parseStart;
/**
* The buffer holding all received, unparsed data.
*/
private char[] buffer = new char[20480];
/**
* The number of characters currently used within the data buffer. All
* other characters within the buffer are free space available for
* future reads.
*/
private int usedLength = 0;
@Override
public boolean available() throws GuacamoleException {
try {
return input.ready() || usedLength != 0;
}
catch (IOException e) {
throw new GuacamoleServerException(e);
}
}
@Override
public char[] read() throws GuacamoleException {
try {
// While we're blocking, or input is available
for (;;) {
// Length of element
int elementLength = 0;
// Resume where we left off
int i = parseStart;
// Parse instruction in buffer
while (i < usedLength) {
// Read character
char readChar = buffer[i++];
// If digit, update length
if (readChar >= '0' && readChar <= '9')
elementLength = elementLength * 10 + readChar - '0';
// If not digit, check for end-of-length character
else if (readChar == '.') {
// Check if element present in buffer
if (i + elementLength < usedLength) {
// Get terminator
char terminator = buffer[i + elementLength];
// Move to character after terminator
i += elementLength + 1;
// Reset length
elementLength = 0;
// Continue here if necessary
parseStart = i;
// If terminator is semicolon, we have a full
// instruction.
if (terminator == ';') {
// Copy instruction data
char[] instruction = new char[i];
System.arraycopy(buffer, 0, instruction, 0, i);
// Update buffer
usedLength -= i;
parseStart = 0;
System.arraycopy(buffer, i, buffer, 0, usedLength);
return instruction;
}
// Handle invalid terminator characters
else if (terminator != ',')
throw new GuacamoleServerException("Element terminator of instruction was not ';' nor ','");
}
// Otherwise, read more data
else
break;
}
// Otherwise, parse error
else
throw new GuacamoleServerException("Non-numeric character in element length.");
}
// If past threshold, resize buffer before reading
if (usedLength > buffer.length/2) {
char[] biggerBuffer = new char[buffer.length*2];
System.arraycopy(buffer, 0, biggerBuffer, 0, usedLength);
buffer = biggerBuffer;
}
// Attempt to fill buffer
int numRead = input.read(buffer, usedLength, buffer.length - usedLength);
if (numRead == -1)
return null;
// Update used length
usedLength += numRead;
} // End read loop
}
catch (SocketTimeoutException e) {
throw new GuacamoleUpstreamTimeoutException("Connection to guacd timed out.", e);
}
catch (SocketException e) {
throw new GuacamoleConnectionClosedException("Connection to guacd is closed.", e);
}
catch (IOException e) {
throw new GuacamoleServerException(e);
}
}
@Override
public GuacamoleInstruction readInstruction() throws GuacamoleException {
// Get instruction
char[] instructionBuffer = read();
// If EOF, return EOF
if (instructionBuffer == null)
return null;
// Start of element
int elementStart = 0;
// Build list of elements
Deque<String> elements = new LinkedList<String>();
while (elementStart < instructionBuffer.length) {
// Find end of length
int lengthEnd = -1;
for (int i=elementStart; i<instructionBuffer.length; i++) {
if (instructionBuffer[i] == '.') {
lengthEnd = i;
break;
}
}
// read() is required to return a complete instruction. If it does
// not, this is a severe internal error.
if (lengthEnd == -1)
throw new GuacamoleServerException("Read returned incomplete instruction.");
// Parse length
int length = Integer.parseInt(new String(
instructionBuffer,
elementStart,
lengthEnd - elementStart
));
// Parse element from just after period
elementStart = lengthEnd + 1;
String element = new String(
instructionBuffer,
elementStart,
length
);
// Append element to list of elements
elements.addLast(element);
// Read terminator after element
elementStart += length;
char terminator = instructionBuffer[elementStart];
// Continue reading instructions after terminator
elementStart++;
// If we've reached the end of the instruction
if (terminator == ';')
break;
}
// Pull opcode off elements list
String opcode = elements.removeFirst();
// Create instruction
GuacamoleInstruction instruction = new GuacamoleInstruction(
opcode,
elements.toArray(new String[elements.size()])
);
// Return parsed instruction
return instruction;
}
}

View File

@@ -0,0 +1,86 @@
/*
* Copyright (C) 2013 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.apache.guacamole.io;
import java.io.IOException;
import java.io.Writer;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import org.apache.guacamole.GuacamoleConnectionClosedException;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.GuacamoleServerException;
import org.apache.guacamole.GuacamoleUpstreamTimeoutException;
import org.apache.guacamole.protocol.GuacamoleInstruction;
/**
* A GuacamoleWriter which wraps a standard Java Writer, using that Writer as
* the Guacamole instruction stream.
*
* @author Michael Jumper
*/
public class WriterGuacamoleWriter implements GuacamoleWriter {
/**
* Wrapped Writer to be used for all output.
*/
private Writer output;
/**
* Creates a new WriterGuacamoleWriter which will use the given Writer as
* the Guacamole instruction stream.
*
* @param output The Writer to use as the Guacamole instruction stream.
*/
public WriterGuacamoleWriter(Writer output) {
this.output = output;
}
@Override
public void write(char[] chunk, int off, int len) throws GuacamoleException {
try {
output.write(chunk, off, len);
output.flush();
}
catch (SocketTimeoutException e) {
throw new GuacamoleUpstreamTimeoutException("Connection to guacd timed out.", e);
}
catch (SocketException e) {
throw new GuacamoleConnectionClosedException("Connection to guacd is closed.", e);
}
catch (IOException e) {
throw new GuacamoleServerException(e);
}
}
@Override
public void write(char[] chunk) throws GuacamoleException {
write(chunk, 0, chunk.length);
}
@Override
public void writeInstruction(GuacamoleInstruction instruction) throws GuacamoleException {
write(instruction.toString().toCharArray());
}
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright (C) 2013 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/**
* All classes relating directly to data input or output.
*/
package org.apache.guacamole.io;

View File

@@ -0,0 +1,133 @@
/*
* Copyright (C) 2015 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.apache.guacamole.net;
import java.util.concurrent.locks.ReentrantLock;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.io.GuacamoleReader;
import org.apache.guacamole.io.GuacamoleWriter;
/**
* Base GuacamoleTunnel implementation which synchronizes access to the
* underlying reader and writer with reentrant locks. Implementations need only
* provide the tunnel's UUID and socket.
*
* @author Michael Jumper
*/
public abstract class AbstractGuacamoleTunnel implements GuacamoleTunnel {
/**
* Lock acquired when a read operation is in progress.
*/
private final ReentrantLock readerLock;
/**
* Lock acquired when a write operation is in progress.
*/
private final ReentrantLock writerLock;
/**
* Creates a new GuacamoleTunnel which synchronizes access to the
* Guacamole instruction stream associated with the underlying
* GuacamoleSocket.
*/
public AbstractGuacamoleTunnel() {
readerLock = new ReentrantLock();
writerLock = new ReentrantLock();
}
/**
* Acquires exclusive read access to the Guacamole instruction stream
* and returns a GuacamoleReader for reading from that stream.
*
* @return A GuacamoleReader for reading from the Guacamole instruction
* stream.
*/
@Override
public GuacamoleReader acquireReader() {
readerLock.lock();
return getSocket().getReader();
}
/**
* Relinquishes exclusive read access to the Guacamole instruction
* stream. This function should be called whenever a thread finishes using
* a GuacamoleTunnel's GuacamoleReader.
*/
@Override
public void releaseReader() {
readerLock.unlock();
}
/**
* Returns whether there are threads waiting for read access to the
* Guacamole instruction stream.
*
* @return true if threads are waiting for read access the Guacamole
* instruction stream, false otherwise.
*/
@Override
public boolean hasQueuedReaderThreads() {
return readerLock.hasQueuedThreads();
}
/**
* Acquires exclusive write access to the Guacamole instruction stream
* and returns a GuacamoleWriter for writing to that stream.
*
* @return A GuacamoleWriter for writing to the Guacamole instruction
* stream.
*/
@Override
public GuacamoleWriter acquireWriter() {
writerLock.lock();
return getSocket().getWriter();
}
/**
* Relinquishes exclusive write access to the Guacamole instruction
* stream. This function should be called whenever a thread finishes using
* a GuacamoleTunnel's GuacamoleWriter.
*/
@Override
public void releaseWriter() {
writerLock.unlock();
}
@Override
public boolean hasQueuedWriterThreads() {
return writerLock.hasQueuedThreads();
}
@Override
public void close() throws GuacamoleException {
getSocket().close();
}
@Override
public boolean isOpen() {
return getSocket().isOpen();
}
}

View File

@@ -0,0 +1,104 @@
/*
* Copyright (C) 2015 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.apache.guacamole.net;
import java.util.UUID;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.io.GuacamoleReader;
import org.apache.guacamole.io.GuacamoleWriter;
/**
* GuacamoleTunnel implementation which simply delegates all function calls to
* an underlying GuacamoleTunnel.
*
* @author Michael Jumper
*/
public class DelegatingGuacamoleTunnel implements GuacamoleTunnel {
/**
* The wrapped GuacamoleTunnel.
*/
private final GuacamoleTunnel tunnel;
/**
* Wraps the given tunnel such that all function calls against this tunnel
* will be delegated to it.
*
* @param tunnel
* The GuacamoleTunnel to wrap.
*/
public DelegatingGuacamoleTunnel(GuacamoleTunnel tunnel) {
this.tunnel = tunnel;
}
@Override
public GuacamoleReader acquireReader() {
return tunnel.acquireReader();
}
@Override
public void releaseReader() {
tunnel.releaseReader();
}
@Override
public boolean hasQueuedReaderThreads() {
return tunnel.hasQueuedReaderThreads();
}
@Override
public GuacamoleWriter acquireWriter() {
return tunnel.acquireWriter();
}
@Override
public void releaseWriter() {
tunnel.releaseWriter();
}
@Override
public boolean hasQueuedWriterThreads() {
return tunnel.hasQueuedWriterThreads();
}
@Override
public UUID getUUID() {
return tunnel.getUUID();
}
@Override
public GuacamoleSocket getSocket() {
return tunnel.getSocket();
}
@Override
public void close() throws GuacamoleException {
tunnel.close();
}
@Override
public boolean isOpen() {
return tunnel.isOpen();
}
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright (C) 2013 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.apache.guacamole.net;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.io.GuacamoleReader;
import org.apache.guacamole.io.GuacamoleWriter;
/**
* Provides abstract socket-like access to a Guacamole connection.
*
* @author Michael Jumper
*/
public interface GuacamoleSocket {
/**
* Returns a GuacamoleReader which can be used to read from the
* Guacamole instruction stream associated with the connection
* represented by this GuacamoleSocket.
*
* @return A GuacamoleReader which can be used to read from the
* Guacamole instruction stream.
*/
public GuacamoleReader getReader();
/**
* Returns a GuacamoleWriter which can be used to write to the
* Guacamole instruction stream associated with the connection
* represented by this GuacamoleSocket.
*
* @return A GuacamoleWriter which can be used to write to the
* Guacamole instruction stream.
*/
public GuacamoleWriter getWriter();
/**
* Releases all resources in use by the connection represented by this
* GuacamoleSocket.
*
* @throws GuacamoleException If an error occurs while releasing resources.
*/
public void close() throws GuacamoleException;
/**
* Returns whether this GuacamoleSocket is open and can be used for reading
* and writing.
*
* @return true if this GuacamoleSocket is open, false otherwise.
*/
public boolean isOpen();
}

View File

@@ -0,0 +1,119 @@
/*
* Copyright (C) 2015 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.apache.guacamole.net;
import java.util.UUID;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.io.GuacamoleReader;
import org.apache.guacamole.io.GuacamoleWriter;
/**
* Provides a unique identifier and synchronized access to the GuacamoleReader
* and GuacamoleWriter associated with a GuacamoleSocket.
*
* @author Michael Jumper
*/
public interface GuacamoleTunnel {
/**
* Acquires exclusive read access to the Guacamole instruction stream
* and returns a GuacamoleReader for reading from that stream.
*
* @return A GuacamoleReader for reading from the Guacamole instruction
* stream.
*/
GuacamoleReader acquireReader();
/**
* Relinquishes exclusive read access to the Guacamole instruction
* stream. This function should be called whenever a thread finishes using
* a GuacamoleTunnel's GuacamoleReader.
*/
void releaseReader();
/**
* Returns whether there are threads waiting for read access to the
* Guacamole instruction stream.
*
* @return true if threads are waiting for read access the Guacamole
* instruction stream, false otherwise.
*/
boolean hasQueuedReaderThreads();
/**
* Acquires exclusive write access to the Guacamole instruction stream
* and returns a GuacamoleWriter for writing to that stream.
*
* @return A GuacamoleWriter for writing to the Guacamole instruction
* stream.
*/
GuacamoleWriter acquireWriter();
/**
* Relinquishes exclusive write access to the Guacamole instruction
* stream. This function should be called whenever a thread finishes using
* a GuacamoleTunnel's GuacamoleWriter.
*/
void releaseWriter();
/**
* Returns whether there are threads waiting for write access to the
* Guacamole instruction stream.
*
* @return true if threads are waiting for write access the Guacamole
* instruction stream, false otherwise.
*/
boolean hasQueuedWriterThreads();
/**
* Returns the unique identifier associated with this GuacamoleTunnel.
*
* @return The unique identifier associated with this GuacamoleTunnel.
*/
UUID getUUID();
/**
* Returns the GuacamoleSocket used by this GuacamoleTunnel for reading
* and writing.
*
* @return The GuacamoleSocket used by this GuacamoleTunnel.
*/
GuacamoleSocket getSocket();
/**
* Release all resources allocated to this GuacamoleTunnel.
*
* @throws GuacamoleException if an error occurs while releasing
* resources.
*/
void close() throws GuacamoleException;
/**
* Returns whether this GuacamoleTunnel is open, or has been closed.
*
* @return true if this GuacamoleTunnel is open, false if it is closed.
*/
boolean isOpen();
}

View File

@@ -0,0 +1,150 @@
/*
* Copyright (C) 2013 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.apache.guacamole.net;
import org.apache.guacamole.io.GuacamoleReader;
import org.apache.guacamole.io.ReaderGuacamoleReader;
import org.apache.guacamole.io.WriterGuacamoleWriter;
import org.apache.guacamole.io.GuacamoleWriter;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.SocketTimeoutException;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.GuacamoleServerException;
import org.apache.guacamole.GuacamoleUpstreamTimeoutException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Provides abstract socket-like access to a Guacamole connection over a given
* hostname and port.
*
* @author Michael Jumper
*/
public class InetGuacamoleSocket implements GuacamoleSocket {
/**
* Logger for this class.
*/
private Logger logger = LoggerFactory.getLogger(InetGuacamoleSocket.class);
/**
* The GuacamoleReader this socket should read from.
*/
private GuacamoleReader reader;
/**
* The GuacamoleWriter this socket should write to.
*/
private GuacamoleWriter writer;
/**
* The number of milliseconds to wait for data on the TCP socket before
* timing out.
*/
private static final int SOCKET_TIMEOUT = 15000;
/**
* The TCP socket that the GuacamoleReader and GuacamoleWriter exposed
* by this class should affect.
*/
private Socket sock;
/**
* Creates a new InetGuacamoleSocket which reads and writes instructions
* to the Guacamole instruction stream of the Guacamole proxy server
* running at the given hostname and port.
*
* @param hostname The hostname of the Guacamole proxy server to connect to.
* @param port The port of the Guacamole proxy server to connect to.
* @throws GuacamoleException If an error occurs while connecting to the
* Guacamole proxy server.
*/
public InetGuacamoleSocket(String hostname, int port) throws GuacamoleException {
try {
logger.debug("Connecting to guacd at {}:{}.", hostname, port);
// Get address
SocketAddress address = new InetSocketAddress(
InetAddress.getByName(hostname),
port
);
// Connect with timeout
sock = new Socket();
sock.connect(address, SOCKET_TIMEOUT);
// Set read timeout
sock.setSoTimeout(SOCKET_TIMEOUT);
// On successful connect, retrieve I/O streams
reader = new ReaderGuacamoleReader(new InputStreamReader(sock.getInputStream(), "UTF-8"));
writer = new WriterGuacamoleWriter(new OutputStreamWriter(sock.getOutputStream(), "UTF-8"));
}
catch (SocketTimeoutException e) {
throw new GuacamoleUpstreamTimeoutException("Connection timed out.", e);
}
catch (IOException e) {
throw new GuacamoleServerException(e);
}
}
@Override
public void close() throws GuacamoleException {
try {
logger.debug("Closing socket to guacd.");
sock.close();
}
catch (IOException e) {
throw new GuacamoleServerException(e);
}
}
@Override
public GuacamoleReader getReader() {
return reader;
}
@Override
public GuacamoleWriter getWriter() {
return writer;
}
@Override
public boolean isOpen() {
return !sock.isClosed();
}
}

View File

@@ -0,0 +1,149 @@
/*
* Copyright (C) 2013 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.apache.guacamole.net;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import javax.net.SocketFactory;
import javax.net.ssl.SSLSocketFactory;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.GuacamoleServerException;
import org.apache.guacamole.io.GuacamoleReader;
import org.apache.guacamole.io.GuacamoleWriter;
import org.apache.guacamole.io.ReaderGuacamoleReader;
import org.apache.guacamole.io.WriterGuacamoleWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Provides abstract socket-like access to a Guacamole connection over SSL to
* a given hostname and port.
*
* @author Michael Jumper
*/
public class SSLGuacamoleSocket implements GuacamoleSocket {
/**
* Logger for this class.
*/
private Logger logger = LoggerFactory.getLogger(SSLGuacamoleSocket.class);
/**
* The GuacamoleReader this socket should read from.
*/
private GuacamoleReader reader;
/**
* The GuacamoleWriter this socket should write to.
*/
private GuacamoleWriter writer;
/**
* The number of milliseconds to wait for data on the TCP socket before
* timing out.
*/
private static final int SOCKET_TIMEOUT = 15000;
/**
* The TCP socket that the GuacamoleReader and GuacamoleWriter exposed
* by this class should affect.
*/
private Socket sock;
/**
* Creates a new SSLGuacamoleSocket which reads and writes instructions
* to the Guacamole instruction stream of the Guacamole proxy server
* running at the given hostname and port using SSL.
*
* @param hostname The hostname of the Guacamole proxy server to connect to.
* @param port The port of the Guacamole proxy server to connect to.
* @throws GuacamoleException If an error occurs while connecting to the
* Guacamole proxy server.
*/
public SSLGuacamoleSocket(String hostname, int port) throws GuacamoleException {
// Get factory for SSL sockets
SocketFactory socket_factory = SSLSocketFactory.getDefault();
try {
logger.debug("Connecting to guacd at {}:{} via SSL/TLS.",
hostname, port);
// Get address
SocketAddress address = new InetSocketAddress(
InetAddress.getByName(hostname),
port
);
// Connect with timeout
sock = socket_factory.createSocket();
sock.connect(address, SOCKET_TIMEOUT);
// Set read timeout
sock.setSoTimeout(SOCKET_TIMEOUT);
// On successful connect, retrieve I/O streams
reader = new ReaderGuacamoleReader(new InputStreamReader(sock.getInputStream(), "UTF-8"));
writer = new WriterGuacamoleWriter(new OutputStreamWriter(sock.getOutputStream(), "UTF-8"));
}
catch (IOException e) {
throw new GuacamoleServerException(e);
}
}
@Override
public void close() throws GuacamoleException {
try {
logger.debug("Closing socket to guacd.");
sock.close();
}
catch (IOException e) {
throw new GuacamoleServerException(e);
}
}
@Override
public GuacamoleReader getReader() {
return reader;
}
@Override
public GuacamoleWriter getWriter() {
return writer;
}
@Override
public boolean isOpen() {
return !sock.isClosed();
}
}

View File

@@ -0,0 +1,69 @@
/*
* Copyright (C) 2015 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.apache.guacamole.net;
import java.util.UUID;
/**
* GuacamoleTunnel implementation which uses a provided socket. The UUID of
* the tunnel will be randomly generated.
*
* @author Michael Jumper
*/
public class SimpleGuacamoleTunnel extends AbstractGuacamoleTunnel {
/**
* The UUID associated with this tunnel. Every tunnel must have a
* corresponding UUID such that tunnel read/write requests can be
* directed to the proper tunnel.
*/
private final UUID uuid = UUID.randomUUID();
/**
* The GuacamoleSocket that tunnel should use for communication on
* behalf of the connecting user.
*/
private final GuacamoleSocket socket;
/**
* Creates a new GuacamoleTunnel which synchronizes access to the
* Guacamole instruction stream associated with the given GuacamoleSocket.
*
* @param socket The GuacamoleSocket to provide synchronized access for.
*/
public SimpleGuacamoleTunnel(GuacamoleSocket socket) {
this.socket = socket;
}
@Override
public UUID getUUID() {
return uuid;
}
@Override
public GuacamoleSocket getSocket() {
return socket;
}
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright (C) 2013 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/**
* Classes which apply to network-specific concepts, such as low-level sockets
* and tunnels.
*/
package org.apache.guacamole.net;

View File

@@ -0,0 +1,29 @@
/*
* Copyright (C) 2013 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/**
* All classes which apply generally across the Guacamole web application
* and all other web applications which use the API provided by the
* Guacamole project.
*/
package org.apache.guacamole;

View File

@@ -0,0 +1,249 @@
/*
* Copyright (C) 2013 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.apache.guacamole.protocol;
import java.util.List;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.GuacamoleServerException;
import org.apache.guacamole.io.GuacamoleReader;
import org.apache.guacamole.io.GuacamoleWriter;
import org.apache.guacamole.net.GuacamoleSocket;
/**
* A GuacamoleSocket which pre-configures the connection based on a given
* GuacamoleConfiguration, completing the initial protocol handshake before
* accepting data for read or write.
*
* This is useful for forcing a connection to the Guacamole proxy server with
* a specific configuration while disallowing the client that will be using
* this GuacamoleSocket from manually controlling the initial protocol
* handshake.
*
* @author Michael Jumper
*/
public class ConfiguredGuacamoleSocket implements GuacamoleSocket {
/**
* The wrapped socket.
*/
private GuacamoleSocket socket;
/**
* The configuration to use when performing the Guacamole protocol
* handshake.
*/
private GuacamoleConfiguration config;
/**
* The unique identifier associated with this connection, as determined
* by the "ready" instruction received from the Guacamole proxy.
*/
private String id;
/**
* Waits for the instruction having the given opcode, returning that
* instruction once it has been read. If the instruction is never read,
* an exception is thrown.
*
* @param reader The reader to read instructions from.
* @param opcode The opcode of the instruction we are expecting.
* @return The instruction having the given opcode.
* @throws GuacamoleException If an error occurs while reading, or if
* the expected instruction is not read.
*/
private GuacamoleInstruction expect(GuacamoleReader reader, String opcode)
throws GuacamoleException {
// Wait for an instruction
GuacamoleInstruction instruction = reader.readInstruction();
if (instruction == null)
throw new GuacamoleServerException("End of stream while waiting for \"" + opcode + "\".");
// Ensure instruction has expected opcode
if (!instruction.getOpcode().equals(opcode))
throw new GuacamoleServerException("Expected \"" + opcode + "\" instruction but instead received \"" + instruction.getOpcode() + "\".");
return instruction;
}
/**
* Creates a new ConfiguredGuacamoleSocket which uses the given
* GuacamoleConfiguration to complete the initial protocol handshake over
* the given GuacamoleSocket. A default GuacamoleClientInformation object
* is used to provide basic client information.
*
* @param socket The GuacamoleSocket to wrap.
* @param config The GuacamoleConfiguration to use to complete the initial
* protocol handshake.
* @throws GuacamoleException If an error occurs while completing the
* initial protocol handshake.
*/
public ConfiguredGuacamoleSocket(GuacamoleSocket socket,
GuacamoleConfiguration config) throws GuacamoleException {
this(socket, config, new GuacamoleClientInformation());
}
/**
* Creates a new ConfiguredGuacamoleSocket which uses the given
* GuacamoleConfiguration and GuacamoleClientInformation to complete the
* initial protocol handshake over the given GuacamoleSocket.
*
* @param socket The GuacamoleSocket to wrap.
* @param config The GuacamoleConfiguration to use to complete the initial
* protocol handshake.
* @param info The GuacamoleClientInformation to use to complete the initial
* protocol handshake.
* @throws GuacamoleException If an error occurs while completing the
* initial protocol handshake.
*/
public ConfiguredGuacamoleSocket(GuacamoleSocket socket,
GuacamoleConfiguration config,
GuacamoleClientInformation info) throws GuacamoleException {
this.socket = socket;
this.config = config;
// Get reader and writer
GuacamoleReader reader = socket.getReader();
GuacamoleWriter writer = socket.getWriter();
// Get protocol / connection ID
String select_arg = config.getConnectionID();
if (select_arg == null)
select_arg = config.getProtocol();
// Send requested protocol or connection ID
writer.writeInstruction(new GuacamoleInstruction("select", select_arg));
// Wait for server args
GuacamoleInstruction args = expect(reader, "args");
// Build args list off provided names and config
List<String> arg_names = args.getArgs();
String[] arg_values = new String[arg_names.size()];
for (int i=0; i<arg_names.size(); i++) {
// Retrieve argument name
String arg_name = arg_names.get(i);
// Get defined value for name
String value = config.getParameter(arg_name);
// If value defined, set that value
if (value != null) arg_values[i] = value;
// Otherwise, leave value blank
else arg_values[i] = "";
}
// Send size
writer.writeInstruction(
new GuacamoleInstruction(
"size",
Integer.toString(info.getOptimalScreenWidth()),
Integer.toString(info.getOptimalScreenHeight()),
Integer.toString(info.getOptimalResolution())
)
);
// Send supported audio formats
writer.writeInstruction(
new GuacamoleInstruction(
"audio",
info.getAudioMimetypes().toArray(new String[0])
));
// Send supported video formats
writer.writeInstruction(
new GuacamoleInstruction(
"video",
info.getVideoMimetypes().toArray(new String[0])
));
// Send supported image formats
writer.writeInstruction(
new GuacamoleInstruction(
"image",
info.getImageMimetypes().toArray(new String[0])
));
// Send args
writer.writeInstruction(new GuacamoleInstruction("connect", arg_values));
// Wait for ready, store ID
GuacamoleInstruction ready = expect(reader, "ready");
List<String> ready_args = ready.getArgs();
if (ready_args.isEmpty())
throw new GuacamoleServerException("No connection ID received");
id = ready.getArgs().get(0);
}
/**
* Returns the GuacamoleConfiguration used to configure this
* ConfiguredGuacamoleSocket.
*
* @return The GuacamoleConfiguration used to configure this
* ConfiguredGuacamoleSocket.
*/
public GuacamoleConfiguration getConfiguration() {
return config;
}
/**
* Returns the unique ID associated with the Guacamole connection
* negotiated by this ConfiguredGuacamoleSocket. The ID is provided by
* the "ready" instruction returned by the Guacamole proxy.
*
* @return The ID of the negotiated Guacamole connection.
*/
public String getConnectionID() {
return id;
}
@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();
}
}

View File

@@ -0,0 +1,99 @@
/*
* Copyright (C) 2014 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.apache.guacamole.protocol;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.io.GuacamoleReader;
/**
* GuacamoleReader which applies a given GuacamoleFilter to observe or alter all
* read instructions. Instructions may also be dropped or denied by the the
* filter.
*
* @author Michael Jumper
*/
public class FilteredGuacamoleReader implements GuacamoleReader {
/**
* The wrapped GuacamoleReader.
*/
private final GuacamoleReader reader;
/**
* The filter to apply when reading instructions.
*/
private final GuacamoleFilter filter;
/**
* Wraps the given GuacamoleReader, applying the given filter to all read
* instructions. Future reads will return only instructions which pass
* the filter.
*
* @param reader The GuacamoleReader to wrap.
* @param filter The filter which dictates which instructions are read, and
* how.
*/
public FilteredGuacamoleReader(GuacamoleReader reader, GuacamoleFilter filter) {
this.reader = reader;
this.filter = filter;
}
@Override
public boolean available() throws GuacamoleException {
return reader.available();
}
@Override
public char[] read() throws GuacamoleException {
GuacamoleInstruction filteredInstruction = readInstruction();
if (filteredInstruction == null)
return null;
return filteredInstruction.toString().toCharArray();
}
@Override
public GuacamoleInstruction readInstruction() throws GuacamoleException {
GuacamoleInstruction filteredInstruction;
// Read and filter instructions until no instructions are dropped
do {
// Read next instruction
GuacamoleInstruction unfilteredInstruction = reader.readInstruction();
if (unfilteredInstruction == null)
return null;
// Apply filter
filteredInstruction = filter.filter(unfilteredInstruction);
} while (filteredInstruction == null);
return filteredInstruction;
}
}

View File

@@ -0,0 +1,102 @@
/*
* Copyright (C) 2014 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
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.GuacamoleSocket;
/**
* Implementation of GuacamoleSocket which allows individual instructions to be
* intercepted, overridden, etc.
*
* @author Michael Jumper
*/
public class FilteredGuacamoleSocket implements GuacamoleSocket {
/**
* Wrapped GuacamoleSocket.
*/
private final GuacamoleSocket socket;
/**
* A reader for the wrapped GuacamoleSocket which may be filtered.
*/
private final GuacamoleReader reader;
/**
* A writer for the wrapped GuacamoleSocket which may be filtered.
*/
private final GuacamoleWriter writer;
/**
* Creates a new FilteredGuacamoleSocket which uses the given filters to
* determine whether instructions read/written are allowed through,
* modified, etc. If reads or writes should be unfiltered, simply specify
* null rather than a particular filter.
*
* @param socket The GuacamoleSocket to wrap.
* @param readFilter The GuacamoleFilter to apply to all read instructions,
* if any.
* @param writeFilter The GuacamoleFilter to apply to all written
* instructions, if any.
*/
public FilteredGuacamoleSocket(GuacamoleSocket socket, GuacamoleFilter readFilter, GuacamoleFilter writeFilter) {
this.socket = socket;
// Apply filter to reader
if (readFilter != null)
reader = new FilteredGuacamoleReader(socket.getReader(), readFilter);
else
reader = socket.getReader();
// Apply filter to writer
if (writeFilter != null)
writer = new FilteredGuacamoleWriter(socket.getWriter(), writeFilter);
else
writer = socket.getWriter();
}
@Override
public GuacamoleReader getReader() {
return reader;
}
@Override
public GuacamoleWriter getWriter() {
return writer;
}
@Override
public void close() throws GuacamoleException {
socket.close();
}
@Override
public boolean isOpen() {
return socket.isOpen();
}
}

View File

@@ -0,0 +1,107 @@
/*
* Copyright (C) 2014 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.apache.guacamole.protocol;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.GuacamoleServerException;
import org.apache.guacamole.io.GuacamoleWriter;
/**
* GuacamoleWriter which applies a given GuacamoleFilter to observe or alter
* all written instructions. Instructions may also be dropped or denied by
* the filter.
*
* @author Michael Jumper
*/
public class FilteredGuacamoleWriter implements GuacamoleWriter {
/**
* The wrapped GuacamoleWriter.
*/
private final GuacamoleWriter writer;
/**
* The filter to apply when writing instructions.
*/
private final GuacamoleFilter filter;
/**
* Parser for reading instructions prior to writing, such that they can be
* passed on to the filter.
*/
private final GuacamoleParser parser = new GuacamoleParser();
/**
* Wraps the given GuacamoleWriter, applying the given filter to all written
* instructions. Future writes will only write instructions which pass
* the filter.
*
* @param writer The GuacamoleWriter to wrap.
* @param filter The filter which dictates which instructions are written,
* and how.
*/
public FilteredGuacamoleWriter(GuacamoleWriter writer, GuacamoleFilter filter) {
this.writer = writer;
this.filter = filter;
}
@Override
public void write(char[] chunk, int offset, int length) throws GuacamoleException {
// Write all data in chunk
while (length > 0) {
// Pass as much data through the parser as possible
int parsed;
while ((parsed = parser.append(chunk, offset, length)) != 0) {
offset += parsed;
length -= parsed;
}
// If no instruction is available, it must be incomplete
if (!parser.hasNext())
throw new GuacamoleServerException("Filtered write() contained an incomplete instruction.");
// Write single instruction through filter
writeInstruction(parser.next());
}
}
@Override
public void write(char[] chunk) throws GuacamoleException {
write(chunk, 0, chunk.length);
}
@Override
public void writeInstruction(GuacamoleInstruction instruction) throws GuacamoleException {
// Write instruction only if not dropped
GuacamoleInstruction filteredInstruction = filter.filter(instruction);
if (filteredInstruction != null)
writer.writeInstruction(filteredInstruction);
}
}

View File

@@ -0,0 +1,153 @@
/*
* Copyright (C) 2013 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.apache.guacamole.protocol;
import java.util.ArrayList;
import java.util.List;
/**
* An abstract representation of Guacamole client information, including all
* information required by the Guacamole protocol during the preamble.
*
* @author Michael Jumper
*/
public class GuacamoleClientInformation {
/**
* The optimal screen width requested by the client, in pixels.
*/
private int optimalScreenWidth = 1024;
/**
* The optimal screen height requested by the client, in pixels.
*/
private int optimalScreenHeight = 768;
/**
* The resolution of the optimal dimensions given, in DPI.
*/
private int optimalResolution = 96;
/**
* The list of audio mimetypes reported by the client to be supported.
*/
private final List<String> audioMimetypes = new ArrayList<String>();
/**
* The list of video mimetypes reported by the client to be supported.
*/
private final List<String> videoMimetypes = new ArrayList<String>();
/**
* The list of image mimetypes reported by the client to be supported.
*/
private final List<String> imageMimetypes = new ArrayList<String>();
/**
* Returns the optimal screen width requested by the client, in pixels.
* @return The optimal screen width requested by the client, in pixels.
*/
public int getOptimalScreenWidth() {
return optimalScreenWidth;
}
/**
* Sets the client's optimal screen width.
* @param optimalScreenWidth The optimal screen width of the client.
*/
public void setOptimalScreenWidth(int optimalScreenWidth) {
this.optimalScreenWidth = optimalScreenWidth;
}
/**
* Returns the optimal screen height requested by the client, in pixels.
* @return The optimal screen height requested by the client, in pixels.
*/
public int getOptimalScreenHeight() {
return optimalScreenHeight;
}
/**
* Sets the client's optimal screen height.
* @param optimalScreenHeight The optimal screen height of the client.
*/
public void setOptimalScreenHeight(int optimalScreenHeight) {
this.optimalScreenHeight = optimalScreenHeight;
}
/**
* Returns the resolution of the screen if the optimal width and height are
* used, in DPI.
*
* @return The optimal screen resolution.
*/
public int getOptimalResolution() {
return optimalResolution;
}
/**
* Sets the resolution of the screen if the optimal width and height are
* used, in DPI.
*
* @param optimalResolution The optimal screen resolution in DPI.
*/
public void setOptimalResolution(int optimalResolution) {
this.optimalResolution = optimalResolution;
}
/**
* Returns the list of audio mimetypes supported by the client. To add or
* removed supported mimetypes, the list returned by this function can be
* modified.
*
* @return The set of audio mimetypes supported by the client.
*/
public List<String> getAudioMimetypes() {
return audioMimetypes;
}
/**
* Returns the list of video mimetypes supported by the client. To add or
* removed supported mimetypes, the list returned by this function can be
* modified.
*
* @return The set of video mimetypes supported by the client.
*/
public List<String> getVideoMimetypes() {
return videoMimetypes;
}
/**
* Returns the list of image mimetypes supported by the client. To add or
* removed supported mimetypes, the list returned by this function can be
* modified.
*
* @return
* The set of image mimetypes supported by the client.
*/
public List<String> getImageMimetypes() {
return imageMimetypes;
}
}

View File

@@ -0,0 +1,192 @@
/*
* Copyright (C) 2013 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.apache.guacamole.protocol;
import java.io.Serializable;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* All information necessary to complete the initial protocol handshake of a
* Guacamole session.
*
* @author Michael Jumper
*/
public class GuacamoleConfiguration implements Serializable {
/**
* Identifier unique to this version of GuacamoleConfiguration.
*/
private static final long serialVersionUID = 1L;
/**
* The ID of the connection being joined. If this value is present,
* the protocol need not be specified.
*/
private String connectionID;
/**
* The name of the protocol associated with this configuration.
*/
private String protocol;
/**
* Map of all associated parameter values, indexed by parameter name.
*/
private final Map<String, String> parameters = new HashMap<String, String>();
/**
* Creates a new, blank GuacamoleConfiguration with its protocol, connection
* ID, and parameters unset.
*/
public GuacamoleConfiguration() {
}
/**
* Copies the given GuacamoleConfiguration, creating a new, indepedent
* GuacamoleConfiguration containing the same protocol, connection ID,
* and parameter values, if any.
*
* @param config The GuacamoleConfiguration to copy.
*/
public GuacamoleConfiguration(GuacamoleConfiguration config) {
// Copy protocol and connection ID
protocol = config.getProtocol();
connectionID = config.getConnectionID();
// Copy parameter values
for (String name : config.getParameterNames())
parameters.put(name, config.getParameter(name));
}
/**
* Returns the ID of the connection being joined, if any. If no connection
* is being joined, this returns null, and the protocol must be set.
*
* @return The ID of the connection being joined, or null if no connection
* is being joined.
*/
public String getConnectionID() {
return connectionID;
}
/**
* 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.
*
* @param connectionID The ID of the connection being joined.
*/
public void setConnectionID(String connectionID) {
this.connectionID = connectionID;
}
/**
* Returns 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.
*/
public void setProtocol(String protocol) {
this.protocol = protocol;
}
/**
* Returns the value set for the parameter with the given name, if any.
* @param name The name of the parameter to return the value for.
* @return The value of the parameter with the given name, or null if
* that parameter has not been set.
*/
public String getParameter(String name) {
return parameters.get(name);
}
/**
* Sets the value for the parameter with the given name.
*
* @param name The name of the parameter to set the value for.
* @param value The value to set for the parameter with the given name.
*/
public void setParameter(String name, String value) {
parameters.put(name, value);
}
/**
* Removes the value set for the parameter with the given name.
*
* @param name The name of the parameter to remove the value of.
*/
public void unsetParameter(String name) {
parameters.remove(name);
}
/**
* Returns a set of all currently defined parameter names. Each name
* corresponds to a parameter that has a value set on this
* GuacamoleConfiguration via setParameter().
*
* @return A set of all currently defined parameter names.
*/
public Set<String> getParameterNames() {
return Collections.unmodifiableSet(parameters.keySet());
}
/**
* Returns a map which contains parameter name/value pairs as key/value
* pairs. Changes to this map will affect the parameters stored within
* this configuration.
*
* @return
* A map which contains all parameter name/value pairs as key/value
* pairs.
*/
public Map<String, String> getParameters() {
return parameters;
}
/**
* Replaces all current parameters with the parameters defined within the
* given map. Key/value pairs within the map represent parameter name/value
* pairs.
*
* @param parameters
* A map which contains all parameter name/value pairs as key/value
* pairs.
*/
public void setParameters(Map<String, String> parameters) {
this.parameters.clear();
this.parameters.putAll(parameters);
}
}

View File

@@ -0,0 +1,51 @@
/*
* Copyright (C) 2014 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.apache.guacamole.protocol;
import org.apache.guacamole.GuacamoleException;
/**
* Interface which provides for the filtering of individual instructions. Each
* filtered instruction may be allowed through untouched, modified, replaced,
* dropped, or explicitly denied.
*
* @author Michael Jumper
*/
public interface GuacamoleFilter {
/**
* Applies the filter to the given instruction, returning the original
* instruction, a modified version of the original, or null, depending
* on the implementation.
*
* @param instruction The instruction to filter.
* @return The original instruction, if the instruction is to be allowed,
* a modified version of the instruction, if the instruction is
* to be overridden, or null, if the instruction is to be dropped.
* @throws GuacamoleException If an error occurs filtering the instruction,
* or if the instruction must be explicitly
* denied.
*/
public GuacamoleInstruction filter(GuacamoleInstruction instruction) throws GuacamoleException;
}

View File

@@ -0,0 +1,127 @@
/*
* Copyright (C) 2013 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.apache.guacamole.protocol;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* An abstract representation of a Guacamole instruction, as defined by the
* Guacamole protocol.
*
* @author Michael Jumper
*/
public class GuacamoleInstruction {
/**
* The opcode of this instruction.
*/
private String opcode;
/**
* All arguments of this instruction, in order.
*/
private List<String> args;
/**
* Creates a new GuacamoleInstruction having the given Operation and
* list of arguments values.
*
* @param opcode The opcode of the instruction to create.
* @param args The list of argument values to provide in the new
* instruction if any.
*/
public GuacamoleInstruction(String opcode, String... args) {
this.opcode = opcode;
this.args = Collections.unmodifiableList(Arrays.asList(args));
}
/**
* Creates a new GuacamoleInstruction having the given Operation and
* list of arguments values. The list given will be used to back the
* internal list of arguments and the list returned by getArgs().
*
* @param opcode The opcode of the instruction to create.
* @param args The list of argument values to provide in the new
* instruction if any.
*/
public GuacamoleInstruction(String opcode, List<String> args) {
this.opcode = opcode;
this.args = Collections.unmodifiableList(args);
}
/**
* Returns the opcode associated with this GuacamoleInstruction.
* @return The opcode associated with this GuacamoleInstruction.
*/
public String getOpcode() {
return opcode;
}
/**
* Returns a List of all argument values specified for this
* GuacamoleInstruction. Note that the List returned is immutable.
* Attempts to modify the list will result in exceptions.
*
* @return A List of all argument values specified for this
* GuacamoleInstruction.
*/
public List<String> getArgs() {
return args;
}
/**
* Returns this GuacamoleInstruction in the form it would be sent over the
* Guacamole protocol.
*
* @return This GuacamoleInstruction in the form it would be sent over the
* Guacamole protocol.
*/
@Override
public String toString() {
StringBuilder buff = new StringBuilder();
// Write opcode
buff.append(opcode.length());
buff.append('.');
buff.append(opcode);
// Write argument values
for (String value : args) {
buff.append(',');
buff.append(value.length());
buff.append('.');
buff.append(value);
}
// Write terminator
buff.append(';');
return buff.toString();
}
}

View File

@@ -0,0 +1,244 @@
/*
* Copyright (C) 2014 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.apache.guacamole.protocol;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.GuacamoleServerException;
/**
* Parser for the Guacamole protocol. Arbitrary instruction data is appended,
* and instructions are returned as a result. Invalid instructions result in
* exceptions.
*
* @author Michael Jumper
*/
public class GuacamoleParser implements Iterator<GuacamoleInstruction> {
/**
* The maximum number of characters per instruction.
*/
public static final int INSTRUCTION_MAX_LENGTH = 8192;
/**
* The maximum number of digits to allow per length prefix.
*/
public static final int INSTRUCTION_MAX_DIGITS = 5;
/**
* The maximum number of elements per instruction, including the opcode.
*/
public static final int INSTRUCTION_MAX_ELEMENTS = 64;
/**
* All possible states of the instruction parser.
*/
private enum State {
/**
* The parser is currently waiting for data to complete the length prefix
* of the current element of the instruction.
*/
PARSING_LENGTH,
/**
* The parser has finished reading the length prefix and is currently
* waiting for data to complete the content of the instruction.
*/
PARSING_CONTENT,
/**
* The instruction has been fully parsed.
*/
COMPLETE,
/**
* The instruction cannot be parsed because of a protocol error.
*/
ERROR
}
/**
* The latest parsed instruction, if any.
*/
private GuacamoleInstruction parsedInstruction;
/**
* The parse state of the instruction.
*/
private State state = State.PARSING_LENGTH;
/**
* The length of the current element, if known.
*/
private int elementLength = 0;
/**
* The number of elements currently parsed.
*/
private int elementCount = 0;
/**
* All currently parsed elements.
*/
private final String elements[] = new String[INSTRUCTION_MAX_ELEMENTS];
/**
* Appends data from the given buffer to the current instruction.
*
* @param chunk The buffer containing the data to append.
* @param offset The offset within the buffer where the data begins.
* @param length The length of the data to append.
* @return The number of characters appended, or 0 if complete instructions
* have already been parsed and must be read via next() before
* more data can be appended.
* @throws GuacamoleException If an error occurs while parsing the new data.
*/
public int append(char chunk[], int offset, int length) throws GuacamoleException {
int charsParsed = 0;
// Do not exceed maximum number of elements
if (elementCount == INSTRUCTION_MAX_ELEMENTS && state != State.COMPLETE) {
state = State.ERROR;
throw new GuacamoleServerException("Instruction contains too many elements.");
}
// Parse element length
if (state == State.PARSING_LENGTH) {
int parsedLength = elementLength;
while (charsParsed < length) {
// Pull next character
char c = chunk[offset + charsParsed++];
// If digit, add to length
if (c >= '0' && c <= '9')
parsedLength = parsedLength*10 + c - '0';
// If period, switch to parsing content
else if (c == '.') {
state = State.PARSING_CONTENT;
break;
}
// If not digit, parse error
else {
state = State.ERROR;
throw new GuacamoleServerException("Non-numeric character in element length.");
}
}
// If too long, parse error
if (parsedLength > INSTRUCTION_MAX_LENGTH) {
state = State.ERROR;
throw new GuacamoleServerException("Instruction exceeds maximum length.");
}
// Save length
elementLength = parsedLength;
} // end parse length
// Parse element content, if available
if (state == State.PARSING_CONTENT && charsParsed + elementLength + 1 <= length) {
// Read element
String element = new String(chunk, offset + charsParsed, elementLength);
charsParsed += elementLength;
elementLength = 0;
// Read terminator char following element
char terminator = chunk[offset + charsParsed++];
// Add element to currently parsed elements
elements[elementCount++] = element;
// If semicolon, store end-of-instruction
if (terminator == ';') {
state = State.COMPLETE;
parsedInstruction = new GuacamoleInstruction(elements[0],
Arrays.asList(elements).subList(1, elementCount));
}
// If comma, move on to next element
else if (terminator == ',')
state = State.PARSING_LENGTH;
// Otherwise, parse error
else {
state = State.ERROR;
throw new GuacamoleServerException("Element terminator of instruction was not ';' nor ','");
}
} // end parse content
return charsParsed;
}
/**
* Appends data from the given buffer to the current instruction.
*
* @param chunk The data to append.
* @return The number of characters appended, or 0 if complete instructions
* have already been parsed and must be read via next() before
* more data can be appended.
* @throws GuacamoleException If an error occurs while parsing the new data.
*/
public int append(char chunk[]) throws GuacamoleException {
return append(chunk, 0, chunk.length);
}
@Override
public boolean hasNext() {
return state == State.COMPLETE;
}
@Override
public GuacamoleInstruction next() {
// No instruction to return if not yet complete
if (state != State.COMPLETE)
return null;
// Reset for next instruction.
state = State.PARSING_LENGTH;
elementCount = 0;
elementLength = 0;
return parsedInstruction;
}
@Override
public void remove() {
throw new UnsupportedOperationException("GuacamoleParser does not support remove().");
}
}

View File

@@ -0,0 +1,173 @@
/*
* Copyright (C) 2014 Glyptodon LLC.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.apache.guacamole.protocol;
/**
* All possible statuses returned by various Guacamole instructions, each having
* a corresponding code.
*
* @author Michael Jumper
*/
public enum GuacamoleStatus {
/**
* The operation succeeded.
*/
SUCCESS(200, 1000, 0x0000),
/**
* The requested operation is unsupported.
*/
UNSUPPORTED(501, 1011, 0x0100),
/**
* The operation could not be performed due to an internal failure.
*/
SERVER_ERROR(500, 1011, 0x0200),
/**
* The operation could not be performed as the server is busy.
*/
SERVER_BUSY(503, 1008, 0x0201),
/**
* The operation could not be performed because the upstream server is not
* responding.
*/
UPSTREAM_TIMEOUT(504, 1011, 0x0202),
/**
* The operation was unsuccessful due to an error or otherwise unexpected
* condition of the upstream server.
*/
UPSTREAM_ERROR(502, 1011, 0x0203),
/**
* The operation could not be performed as the requested resource does not
* exist.
*/
RESOURCE_NOT_FOUND(404, 1002, 0x0204),
/**
* The operation could not be performed as the requested resource is already
* in use.
*/
RESOURCE_CONFLICT(409, 1008, 0x0205),
/**
* The operation could not be performed because bad parameters were given.
*/
CLIENT_BAD_REQUEST(400, 1002, 0x0300),
/**
* Permission was denied to perform the operation, as the user is not yet
* authorized (not yet logged in, for example). As HTTP 401 has implications
* for HTTP-specific authorization schemes, this status continues to map to
* HTTP 403 ("Forbidden"). To do otherwise would risk unintended effects.
*/
CLIENT_UNAUTHORIZED(403, 1008, 0x0301),
/**
* Permission was denied to perform the operation, and this operation will
* not be granted even if the user is authorized.
*/
CLIENT_FORBIDDEN(403, 1008, 0x0303),
/**
* The client took too long to respond.
*/
CLIENT_TIMEOUT(408, 1002, 0x0308),
/**
* The client sent too much data.
*/
CLIENT_OVERRUN(413, 1009, 0x030D),
/**
* The client sent data of an unsupported or unexpected type.
*/
CLIENT_BAD_TYPE(415, 1003, 0x030F),
/**
* The operation failed because the current client is already using too
* many resources.
*/
CLIENT_TOO_MANY(429, 1008, 0x031D);
/**
* The most applicable HTTP error code.
*/
private final int http_code;
/**
* The most applicable WebSocket error code.
*/
private final int websocket_code;
/**
* The Guacamole protocol status code.
*/
private final int guac_code;
/**
* Initializes a GuacamoleStatusCode with the given HTTP and Guacamole
* status/error code values.
*
* @param http_code The most applicable HTTP error code.
* @param websocket_code The most applicable WebSocket error code.
* @param guac_code The Guacamole protocol status code.
*/
private GuacamoleStatus(int http_code, int websocket_code, int guac_code) {
this.http_code = http_code;
this.websocket_code = websocket_code;
this.guac_code = guac_code;
}
/**
* Returns the most applicable HTTP error code.
*
* @return The most applicable HTTP error code.
*/
public int getHttpStatusCode() {
return http_code;
}
/**
* Returns the most applicable HTTP error code.
*
* @return The most applicable HTTP error code.
*/
public int getWebSocketCode() {
return websocket_code;
}
/**
* Returns the corresponding Guacamole protocol status code.
*
* @return The corresponding Guacamole protocol status code.
*/
public int getGuacamoleStatusCode() {
return guac_code;
}
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright (C) 2013 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/**
* Classes relating directly to the Guacamole protocol.
*/
package org.apache.guacamole.protocol;

View File

@@ -0,0 +1,78 @@
/*
* Copyright (C) 2015 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.apache.guacamole.servlet;
import org.apache.guacamole.net.DelegatingGuacamoleTunnel;
import org.apache.guacamole.net.GuacamoleTunnel;
/**
* Tracks the last time a particular GuacamoleTunnel was accessed. This
* information is not necessary for tunnels associated with WebSocket
* connections, as each WebSocket connection has its own read thread which
* continuously checks the state of the tunnel and which will automatically
* timeout when the underlying socket times out, but the HTTP tunnel has no
* such thread. Because the HTTP tunnel requires the stream to be split across
* multiple requests, tracking of activity on the tunnel must be performed
* independently of the HTTP requests.
*
* @author Michael Jumper
*/
class GuacamoleHTTPTunnel extends DelegatingGuacamoleTunnel {
/**
* The last time this tunnel was accessed.
*/
private long lastAccessedTime;
/**
* Creates a new GuacamoleHTTPTunnel which wraps the given tunnel.
* Absolutely all function calls on this new GuacamoleHTTPTunnel will be
* delegated to the underlying GuacamoleTunnel.
*
* @param wrappedTunnel
* The GuacamoleTunnel to wrap within this GuacamoleHTTPTunnel.
*/
public GuacamoleHTTPTunnel(GuacamoleTunnel wrappedTunnel) {
super(wrappedTunnel);
}
/**
* Updates this tunnel, marking it as recently accessed.
*/
public void access() {
lastAccessedTime = System.currentTimeMillis();
}
/**
* Returns the time this tunnel was last accessed, as the number of
* milliseconds since midnight January 1, 1970 GMT. Tunnel access must
* be explicitly marked through calls to the access() function.
*
* @return
* The time this tunnel was last accessed.
*/
public long getLastAccessedTime() {
return lastAccessedTime;
}
}

View File

@@ -0,0 +1,213 @@
/*
* Copyright (C) 2015 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.apache.guacamole.servlet;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.net.GuacamoleTunnel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Map-style object which tracks in-use HTTP tunnels, automatically removing
* and closing tunnels which have not been used recently. This class is
* intended for use only within the GuacamoleHTTPTunnelServlet implementation,
* and has no real utility outside that implementation.
*
* @author Michael Jumper
*/
class GuacamoleHTTPTunnelMap {
/**
* Logger for this class.
*/
private static final Logger logger = LoggerFactory.getLogger(GuacamoleHTTPTunnelMap.class);
/**
* The number of seconds to wait between tunnel accesses before timing out
* Note that this will be enforced only within a factor of 2. If a tunnel
* is unused, it will take between TUNNEL_TIMEOUT and TUNNEL_TIMEOUT*2
* seconds before that tunnel is closed and removed.
*/
private static final int TUNNEL_TIMEOUT = 15;
/**
* Executor service which runs the periodic tunnel timeout task.
*/
private final ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
/**
* Map of all tunnels that are using HTTP, indexed by tunnel UUID.
*/
private final ConcurrentMap<String, GuacamoleHTTPTunnel> tunnelMap =
new ConcurrentHashMap<String, GuacamoleHTTPTunnel>();
/**
* Creates a new GuacamoleHTTPTunnelMap which automatically closes and
* removes HTTP tunnels which are no longer in use.
*/
public GuacamoleHTTPTunnelMap() {
// Check for unused tunnels every few seconds
executor.scheduleAtFixedRate(
new TunnelTimeoutTask(TUNNEL_TIMEOUT * 1000l),
TUNNEL_TIMEOUT, TUNNEL_TIMEOUT, TimeUnit.SECONDS);
}
/**
* Task which iterates through all registered tunnels, removing and those
* tunnels which have not been accessed for a given number of milliseconds.
*/
private class TunnelTimeoutTask implements Runnable {
/**
* The maximum amount of time to allow between accesses to any one
* HTTP tunnel, in milliseconds.
*/
private final long tunnelTimeout;
/**
* Creates a new task which automatically closes and removes tunnels
* which have not been accessed for at least the given number of
* milliseconds.
*
* @param tunnelTimeout
* The maximum amount of time to allow between separate tunnel
* read/write requests, in milliseconds.
*/
public TunnelTimeoutTask(long tunnelTimeout) {
this.tunnelTimeout = tunnelTimeout;
}
@Override
public void run() {
// Get current time
long now = System.currentTimeMillis();
// For each tunnel, close and remove any tunnels which have expired
Iterator<Map.Entry<String, GuacamoleHTTPTunnel>> entries = tunnelMap.entrySet().iterator();
while (entries.hasNext()) {
Map.Entry<String, GuacamoleHTTPTunnel> entry = entries.next();
GuacamoleHTTPTunnel tunnel = entry.getValue();
// Get elapsed time since last access
long age = now - tunnel.getLastAccessedTime();
// If tunnel is too old, close and remove it
if (age >= tunnelTimeout) {
// Remove old entry
logger.debug("HTTP tunnel \"{}\" has timed out.", entry.getKey());
entries.remove();
// Attempt to close tunnel
try {
tunnel.close();
}
catch (GuacamoleException e) {
logger.debug("Unable to close expired HTTP tunnel.", e);
}
}
} // end for each tunnel
} // end timeout task run()
}
/**
* Returns the GuacamoleTunnel having the given UUID, wrapped within a
* GuacamoleHTTPTunnel. If the no tunnel having the given UUID is
* available, null is returned.
*
* @param uuid
* The UUID of the tunnel to retrieve.
*
* @return
* The GuacamoleTunnel having the given UUID, wrapped within a
* GuacamoleHTTPTunnel, if such a tunnel exists, or null if there is no
* such tunnel.
*/
public GuacamoleHTTPTunnel get(String uuid) {
// Update the last access time
GuacamoleHTTPTunnel tunnel = tunnelMap.get(uuid);
if (tunnel != null)
tunnel.access();
// Return tunnel, if any
return tunnel;
}
/**
* Registers that a new connection has been established using HTTP via the
* given GuacamoleTunnel.
*
* @param uuid
* The UUID of the tunnel being added (registered).
*
* @param tunnel
* The GuacamoleTunnel being registered, its associated connection
* having just been established via HTTP.
*/
public void put(String uuid, GuacamoleTunnel tunnel) {
tunnelMap.put(uuid, new GuacamoleHTTPTunnel(tunnel));
}
/**
* Removes the GuacamoleTunnel having the given UUID, if such a tunnel
* exists. The original tunnel is returned wrapped within a
* GuacamoleHTTPTunnel.
*
* @param uuid
* The UUID of the tunnel to remove (deregister).
*
* @return
* The GuacamoleTunnel having the given UUID, if such a tunnel exists,
* wrapped within a GuacamoleHTTPTunnel, or null if no such tunnel
* exists and no removal was performed.
*/
public GuacamoleHTTPTunnel remove(String uuid) {
return tunnelMap.remove(uuid);
}
/**
* Shuts down this tunnel map, disallowing future tunnels from being
* registered and reclaiming any resources.
*/
public void shutdown() {
executor.shutdownNow();
}
}

View File

@@ -0,0 +1,526 @@
/*
* Copyright (C) 2015 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.apache.guacamole.servlet;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Writer;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.guacamole.GuacamoleClientException;
import org.apache.guacamole.GuacamoleConnectionClosedException;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.GuacamoleResourceNotFoundException;
import org.apache.guacamole.GuacamoleServerException;
import org.apache.guacamole.io.GuacamoleReader;
import org.apache.guacamole.io.GuacamoleWriter;
import org.apache.guacamole.net.GuacamoleTunnel;
import org.apache.guacamole.protocol.GuacamoleStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A HttpServlet implementing and abstracting the operations required by the
* HTTP implementation of the JavaScript Guacamole client's tunnel.
*
* @author Michael Jumper
*/
public abstract class GuacamoleHTTPTunnelServlet extends HttpServlet {
/**
* Logger for this class.
*/
private final Logger logger = LoggerFactory.getLogger(GuacamoleHTTPTunnelServlet.class);
/**
* Map of absolutely all active tunnels using HTTP, indexed by tunnel UUID.
*/
private final GuacamoleHTTPTunnelMap tunnels = new GuacamoleHTTPTunnelMap();
/**
* The prefix of the query string which denotes a tunnel read operation.
*/
private static final String READ_PREFIX = "read:";
/**
* The prefix of the query string which denotes a tunnel write operation.
*/
private static final String WRITE_PREFIX = "write:";
/**
* The length of the read prefix, in characters.
*/
private static final int READ_PREFIX_LENGTH = READ_PREFIX.length();
/**
* The length of the write prefix, in characters.
*/
private static final int WRITE_PREFIX_LENGTH = WRITE_PREFIX.length();
/**
* The length of every tunnel UUID, in characters.
*/
private static final int UUID_LENGTH = 36;
/**
* Registers the given tunnel such that future read/write requests to that
* tunnel will be properly directed.
*
* @param tunnel
* The tunnel to register.
*/
protected void registerTunnel(GuacamoleTunnel tunnel) {
tunnels.put(tunnel.getUUID().toString(), tunnel);
logger.debug("Registered tunnel \"{}\".", tunnel.getUUID());
}
/**
* Deregisters the given tunnel such that future read/write requests to
* that tunnel will be rejected.
*
* @param tunnel
* The tunnel to deregister.
*/
protected void deregisterTunnel(GuacamoleTunnel tunnel) {
tunnels.remove(tunnel.getUUID().toString());
logger.debug("Deregistered tunnel \"{}\".", tunnel.getUUID());
}
/**
* Returns the tunnel with the given UUID, if it has been registered with
* registerTunnel() and not yet deregistered with deregisterTunnel().
*
* @param tunnelUUID
* The UUID of registered tunnel.
*
* @return
* The tunnel corresponding to the given UUID.
*
* @throws GuacamoleException
* If the requested tunnel does not exist because it has not yet been
* registered or it has been deregistered.
*/
protected GuacamoleTunnel getTunnel(String tunnelUUID)
throws GuacamoleException {
// Pull tunnel from map
GuacamoleTunnel tunnel = tunnels.get(tunnelUUID);
if (tunnel == null)
throw new GuacamoleResourceNotFoundException("No such tunnel.");
return tunnel;
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException {
handleTunnelRequest(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException {
handleTunnelRequest(request, response);
}
/**
* Sends an error on the given HTTP response using the information within
* the given GuacamoleStatus.
*
* @param response
* The HTTP response to use to send the error.
*
* @param guacStatus
* The status to send
*
* @param message
* A human-readable message that can be presented to the user.
*
* @throws ServletException
* If an error prevents sending of the error code.
*/
protected void sendError(HttpServletResponse response,
GuacamoleStatus guacStatus, String message)
throws ServletException {
try {
// If response not committed, send error code and message
if (!response.isCommitted()) {
response.addHeader("Guacamole-Status-Code", Integer.toString(guacStatus.getGuacamoleStatusCode()));
response.addHeader("Guacamole-Error-Message", message);
response.sendError(guacStatus.getHttpStatusCode());
}
}
catch (IOException ioe) {
// If unable to send error at all due to I/O problems,
// rethrow as servlet exception
throw new ServletException(ioe);
}
}
/**
* Dispatches every HTTP GET and POST request to the appropriate handler
* function based on the query string.
*
* @param request
* The HttpServletRequest associated with the GET or POST request
* received.
*
* @param response
* The HttpServletResponse associated with the GET or POST request
* received.
*
* @throws ServletException
* If an error occurs while servicing the request.
*/
protected void handleTunnelRequest(HttpServletRequest request,
HttpServletResponse response) throws ServletException {
try {
String query = request.getQueryString();
if (query == null)
throw new GuacamoleClientException("No query string provided.");
// If connect operation, call doConnect() and return tunnel UUID
// in response.
if (query.equals("connect")) {
GuacamoleTunnel tunnel = doConnect(request);
if (tunnel != null) {
// Register newly-created tunnel
registerTunnel(tunnel);
try {
// Ensure buggy browsers do not cache response
response.setHeader("Cache-Control", "no-cache");
// Send UUID to client
response.getWriter().print(tunnel.getUUID().toString());
}
catch (IOException e) {
throw new GuacamoleServerException(e);
}
}
// Failed to connect
else
throw new GuacamoleResourceNotFoundException("No tunnel created.");
}
// If read operation, call doRead() with tunnel UUID, ignoring any
// characters following the tunnel UUID.
else if(query.startsWith(READ_PREFIX))
doRead(request, response, query.substring(
READ_PREFIX_LENGTH,
READ_PREFIX_LENGTH + UUID_LENGTH));
// If write operation, call doWrite() with tunnel UUID, ignoring any
// characters following the tunnel UUID.
else if(query.startsWith(WRITE_PREFIX))
doWrite(request, response, query.substring(
WRITE_PREFIX_LENGTH,
WRITE_PREFIX_LENGTH + UUID_LENGTH));
// Otherwise, invalid operation
else
throw new GuacamoleClientException("Invalid tunnel operation: " + query);
}
// Catch any thrown guacamole exception and attempt to pass within the
// HTTP response, logging each error appropriately.
catch (GuacamoleClientException e) {
logger.warn("HTTP tunnel request rejected: {}", e.getMessage());
sendError(response, e.getStatus(), e.getMessage());
}
catch (GuacamoleException e) {
logger.error("HTTP tunnel request failed: {}", e.getMessage());
logger.debug("Internal error in HTTP tunnel.", e);
sendError(response, e.getStatus(), "Internal server error.");
}
}
/**
* Called whenever the JavaScript Guacamole client makes a connection
* request via HTTP. It it up to the implementor of this function to define
* what conditions must be met for a tunnel to be configured and returned
* as a result of this connection request (whether some sort of credentials
* must be specified, for example).
*
* @param request
* The HttpServletRequest associated with the connection request
* received. Any parameters specified along with the connection request
* can be read from this object.
*
* @return
* A newly constructed GuacamoleTunnel if successful, null otherwise.
*
* @throws GuacamoleException
* If an error occurs while constructing the GuacamoleTunnel, or if the
* conditions required for connection are not met.
*/
protected abstract GuacamoleTunnel doConnect(HttpServletRequest request)
throws GuacamoleException;
/**
* Called whenever the JavaScript Guacamole client makes a read request.
* This function should in general not be overridden, as it already
* contains a proper implementation of the read operation.
*
* @param request
* The HttpServletRequest associated with the read request received.
*
* @param response
* The HttpServletResponse associated with the write request received.
* Any data to be sent to the client in response to the write request
* should be written to the response body of this HttpServletResponse.
*
* @param tunnelUUID
* The UUID of the tunnel to read from, as specified in the write
* request. This tunnel must have been created by a previous call to
* doConnect().
*
* @throws GuacamoleException
* If an error occurs while handling the read request.
*/
protected void doRead(HttpServletRequest request,
HttpServletResponse response, String tunnelUUID)
throws GuacamoleException {
// Get tunnel, ensure tunnel exists
GuacamoleTunnel tunnel = getTunnel(tunnelUUID);
// Ensure tunnel is open
if (!tunnel.isOpen())
throw new GuacamoleResourceNotFoundException("Tunnel is closed.");
// Obtain exclusive read access
GuacamoleReader reader = tunnel.acquireReader();
try {
// Note that although we are sending text, Webkit browsers will
// buffer 1024 bytes before starting a normal stream if we use
// anything but application/octet-stream.
response.setContentType("application/octet-stream");
response.setHeader("Cache-Control", "no-cache");
// Get writer for response
Writer out = new BufferedWriter(new OutputStreamWriter(
response.getOutputStream(), "UTF-8"));
// Stream data to response, ensuring output stream is closed
try {
// Deregister tunnel and throw error if we reach EOF without
// having ever sent any data
char[] message = reader.read();
if (message == null)
throw new GuacamoleConnectionClosedException("Tunnel reached end of stream.");
// For all messages, until another stream is ready (we send at least one message)
do {
// Get message output bytes
out.write(message, 0, message.length);
// Flush if we expect to wait
if (!reader.available()) {
out.flush();
response.flushBuffer();
}
// No more messages another stream can take over
if (tunnel.hasQueuedReaderThreads())
break;
} while (tunnel.isOpen() && (message = reader.read()) != null);
// Close tunnel immediately upon EOF
if (message == null) {
deregisterTunnel(tunnel);
tunnel.close();
}
// End-of-instructions marker
out.write("0.;");
out.flush();
response.flushBuffer();
}
// Send end-of-stream marker and close tunnel if connection is closed
catch (GuacamoleConnectionClosedException e) {
// Deregister and close
deregisterTunnel(tunnel);
tunnel.close();
// End-of-instructions marker
out.write("0.;");
out.flush();
response.flushBuffer();
}
catch (GuacamoleException e) {
// Deregister and close
deregisterTunnel(tunnel);
tunnel.close();
throw e;
}
// Always close output stream
finally {
out.close();
}
}
catch (IOException e) {
// Log typically frequent I/O error if desired
logger.debug("Error writing to servlet output stream", e);
// Deregister and close
deregisterTunnel(tunnel);
tunnel.close();
}
finally {
tunnel.releaseReader();
}
}
/**
* Called whenever the JavaScript Guacamole client makes a write request.
* This function should in general not be overridden, as it already
* contains a proper implementation of the write operation.
*
* @param request
* The HttpServletRequest associated with the write request received.
* Any data to be written will be specified within the body of this
* request.
*
* @param response
* The HttpServletResponse associated with the write request received.
*
* @param tunnelUUID
* The UUID of the tunnel to write to, as specified in the write
* request. This tunnel must have been created by a previous call to
* doConnect().
*
* @throws GuacamoleException
* If an error occurs while handling the write request.
*/
protected void doWrite(HttpServletRequest request,
HttpServletResponse response, String tunnelUUID)
throws GuacamoleException {
GuacamoleTunnel tunnel = getTunnel(tunnelUUID);
// We still need to set the content type to avoid the default of
// text/html, as such a content type would cause some browsers to
// attempt to parse the result, even though the JavaScript client
// does not explicitly request such parsing.
response.setContentType("application/octet-stream");
response.setHeader("Cache-Control", "no-cache");
response.setContentLength(0);
// Send data
try {
// Get writer from tunnel
GuacamoleWriter writer = tunnel.acquireWriter();
// Get input reader for HTTP stream
Reader input = new InputStreamReader(
request.getInputStream(), "UTF-8");
// Transfer data from input stream to tunnel output, ensuring
// input is always closed
try {
// Buffer
int length;
char[] buffer = new char[8192];
// Transfer data using buffer
while (tunnel.isOpen() &&
(length = input.read(buffer, 0, buffer.length)) != -1)
writer.write(buffer, 0, length);
}
// Close input stream in all cases
finally {
input.close();
}
}
catch (GuacamoleConnectionClosedException e) {
logger.debug("Connection to guacd closed.", e);
}
catch (IOException e) {
// Deregister and close
deregisterTunnel(tunnel);
tunnel.close();
throw new GuacamoleServerException("I/O Error sending data to server: " + e.getMessage(), e);
}
finally {
tunnel.releaseWriter();
}
}
@Override
public void destroy() {
tunnels.shutdown();
}
}
/**
* \example ExampleTunnelServlet.java
*
* A basic example demonstrating extending GuacamoleTunnelServlet and
* implementing doConnect() to configure the Guacamole connection as
* desired.
*/

View File

@@ -0,0 +1,101 @@
/*
* Copyright (C) 2015 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.apache.guacamole.servlet;
import javax.servlet.http.HttpSession;
import org.apache.guacamole.net.GuacamoleTunnel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Provides abstract access to the tunnels associated with a Guacamole session.
*
* @author Michael Jumper
*/
@Deprecated
public class GuacamoleSession {
/**
* Logger for this class.
*/
private final Logger logger = LoggerFactory.getLogger(GuacamoleSession.class);
/**
* Creates a new GuacamoleSession. In prior versions of Guacamole, the
* GuacamoleSession object stored the tunnels associated with a particular
* user's use of the HTTP tunnel. The HTTP tunnel now stores all of these
* tunnels itself, and thus this class is no longer necessary. Its use will
* result in a warning being logged, and its functions will have no effect.
*
* @param session
* The HttpSession that older versions of Guacamole would use as tunnel
* storage. This parameter is now ignored, and the GuacamoleSession
* class overall is deprecated.
*/
public GuacamoleSession(HttpSession session) {
logger.warn("GuacamoleSession is deprecated. It is no longer "
+ "necessary and its use will have no effect.");
}
/**
* Attaches the given tunnel to this GuacamoleSession. The GuacamoleSession
* class is now deprecated, and this function has no effect.
*
* @param tunnel
* The tunnel to attach to this GucacamoleSession.
*/
public void attachTunnel(GuacamoleTunnel tunnel) {
// Deprecated - no effect
}
/**
* Detaches the given tunnel to this GuacamoleSession. The GuacamoleSession
* class is now deprecated, and this function has no effect.
*
* @param tunnel
* The tunnel to detach to this GucacamoleSession.
*/
public void detachTunnel(GuacamoleTunnel tunnel) {
// Deprecated - no effect
}
/**
* Returns the tunnel with the given UUID attached to this GuacamoleSession,
* if any. The GuacamoleSession class is now deprecated, and this function
* has no effect. It will ALWAYS return null.
*
* @param tunnelUUID
* The UUID of an attached tunnel.
*
* @return
* The tunnel corresponding to the given UUID, if attached, or null if
* if no such tunnel is attached.
*/
public GuacamoleTunnel getTunnel(String tunnelUUID) {
// Deprecated - no effect
return null;
}
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright (C) 2013 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/**
* Classes which build upon the Java Servlet API, providing an HTTP-based
* tunnel and session management.
*/
package org.apache.guacamole.servlet;

View File

@@ -0,0 +1,246 @@
/*
* Copyright (C) 2014 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.apache.guacamole.websocket;
import java.io.IOException;
import javax.websocket.CloseReason;
import javax.websocket.CloseReason.CloseCode;
import javax.websocket.Endpoint;
import javax.websocket.EndpointConfig;
import javax.websocket.MessageHandler;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.RemoteEndpoint;
import javax.websocket.Session;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.io.GuacamoleReader;
import org.apache.guacamole.io.GuacamoleWriter;
import org.apache.guacamole.net.GuacamoleTunnel;
import org.apache.guacamole.GuacamoleClientException;
import org.apache.guacamole.GuacamoleConnectionClosedException;
import org.apache.guacamole.protocol.GuacamoleStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A WebSocket implementation of GuacamoleTunnel functionality, compatible with
* the Guacamole.WebSocketTunnel object included with the JavaScript API.
* Messages sent/received are simply chunks of the Guacamole protocol
* instruction stream.
*
* @author Michael Jumper
*/
public abstract class GuacamoleWebSocketTunnelEndpoint extends Endpoint {
/**
* The default, minimum buffer size for instructions.
*/
private static final int BUFFER_SIZE = 8192;
/**
* Logger for this class.
*/
private final Logger logger = LoggerFactory.getLogger(GuacamoleWebSocketTunnelEndpoint.class);
/**
* The underlying GuacamoleTunnel. WebSocket reads/writes will be handled
* as reads/writes to this tunnel.
*/
private GuacamoleTunnel tunnel;
/**
* Sends the given status on the given WebSocket connection and closes the
* connection.
*
* @param session The outbound WebSocket connection to close.
* @param guac_status The status to send.
*/
private void closeConnection(Session session, GuacamoleStatus guac_status) {
try {
CloseCode code = CloseReason.CloseCodes.getCloseCode(guac_status.getWebSocketCode());
String message = Integer.toString(guac_status.getGuacamoleStatusCode());
session.close(new CloseReason(code, message));
}
catch (IOException e) {
logger.debug("Unable to close WebSocket connection.", e);
}
}
/**
* Returns a new tunnel for the given session. How this tunnel is created
* or retrieved is implementation-dependent.
*
* @param session The session associated with the active WebSocket
* connection.
* @param config Configuration information associated with the instance of
* the endpoint created for handling this single connection.
* @return A connected tunnel, or null if no such tunnel exists.
* @throws GuacamoleException If an error occurs while retrieving the
* tunnel, or if access to the tunnel is denied.
*/
protected abstract GuacamoleTunnel createTunnel(Session session, EndpointConfig config)
throws GuacamoleException;
@Override
@OnOpen
public void onOpen(final Session session, EndpointConfig config) {
try {
// Get tunnel
tunnel = createTunnel(session, config);
if (tunnel == null) {
closeConnection(session, GuacamoleStatus.RESOURCE_NOT_FOUND);
return;
}
}
catch (GuacamoleException e) {
logger.error("Creation of WebSocket tunnel to guacd failed: {}", e.getMessage());
logger.debug("Error connecting WebSocket tunnel.", e);
closeConnection(session, e.getStatus());
return;
}
// Manually register message handler
session.addMessageHandler(new MessageHandler.Whole<String>() {
@Override
public void onMessage(String message) {
GuacamoleWebSocketTunnelEndpoint.this.onMessage(message);
}
});
// Prepare read transfer thread
Thread readThread = new Thread() {
/**
* Remote (client) side of this connection
*/
private final RemoteEndpoint.Basic remote = session.getBasicRemote();
@Override
public void run() {
StringBuilder buffer = new StringBuilder(BUFFER_SIZE);
GuacamoleReader reader = tunnel.acquireReader();
char[] readMessage;
try {
try {
// Attempt to read
while ((readMessage = reader.read()) != null) {
// Buffer message
buffer.append(readMessage);
// Flush if we expect to wait or buffer is getting full
if (!reader.available() || buffer.length() >= BUFFER_SIZE) {
remote.sendText(buffer.toString());
buffer.setLength(0);
}
}
// No more data
closeConnection(session, GuacamoleStatus.SUCCESS);
}
// Catch any thrown guacamole exception and attempt
// to pass within the WebSocket connection, logging
// each error appropriately.
catch (GuacamoleClientException e) {
logger.info("WebSocket connection terminated: {}", e.getMessage());
logger.debug("WebSocket connection terminated due to client error.", e);
closeConnection(session, e.getStatus());
}
catch (GuacamoleConnectionClosedException e) {
logger.debug("Connection to guacd closed.", e);
closeConnection(session, GuacamoleStatus.SUCCESS);
}
catch (GuacamoleException e) {
logger.error("Connection to guacd terminated abnormally: {}", e.getMessage());
logger.debug("Internal error during connection to guacd.", e);
closeConnection(session, e.getStatus());
}
}
catch (IOException e) {
logger.debug("I/O error prevents further reads.", e);
}
}
};
readThread.start();
}
@OnMessage
public void onMessage(String message) {
// Ignore inbound messages if there is no associated tunnel
if (tunnel == null)
return;
GuacamoleWriter writer = tunnel.acquireWriter();
try {
// Write received message
writer.write(message.toCharArray());
}
catch (GuacamoleConnectionClosedException e) {
logger.debug("Connection to guacd closed.", e);
}
catch (GuacamoleException e) {
logger.debug("WebSocket tunnel write failed.", e);
}
tunnel.releaseWriter();
}
@Override
@OnClose
public void onClose(Session session, CloseReason closeReason) {
try {
if (tunnel != null)
tunnel.close();
}
catch (GuacamoleException e) {
logger.debug("Unable to close WebSocket tunnel.", e);
}
}
}