├── .gitignore ├── conf ├── aliases.conf ├── keystore.jks ├── log4j.properties └── server.xml ├── lib ├── simpleXML.jar ├── log4j-1.2.16.jar ├── servlet-api-3.0.jar ├── slf4j-api-1.6.1.jar ├── slf4j-log4j12-1.6.1.jar ├── jetty-io-8.0.1.v20110908.jar ├── jetty-http-8.0.1.v20110908.jar ├── jetty-server-8.0.1.v20110908.jar ├── jetty-util-8.0.1.v20110908.jar ├── jetty-websocket-8.0.1.v20110908.jar └── jetty-continuation-8.0.1.v20110908.jar ├── NOTICE ├── java └── de │ └── rwth_aachen │ └── dbis │ └── wsxmppgateway │ ├── WebSocketConstants.java │ ├── connection │ ├── WsConnectionState.java │ └── WsXMPPConnectionHandler.java │ ├── exception │ └── StreamErrorException.java │ ├── error │ └── StreamError.java │ ├── WebSocketXmppGateway.java │ └── XMPPConstants.java ├── www └── testbed.html ├── README └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .classpath 3 | .project 4 | bin 5 | -------------------------------------------------------------------------------- /conf/aliases.conf: -------------------------------------------------------------------------------- 1 | # aliases.conf 2 | googlemail.com=talk.google.com -------------------------------------------------------------------------------- /conf/keystore.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hocken/wxg/HEAD/conf/keystore.jks -------------------------------------------------------------------------------- /lib/simpleXML.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hocken/wxg/HEAD/lib/simpleXML.jar -------------------------------------------------------------------------------- /lib/log4j-1.2.16.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hocken/wxg/HEAD/lib/log4j-1.2.16.jar -------------------------------------------------------------------------------- /lib/servlet-api-3.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hocken/wxg/HEAD/lib/servlet-api-3.0.jar -------------------------------------------------------------------------------- /lib/slf4j-api-1.6.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hocken/wxg/HEAD/lib/slf4j-api-1.6.1.jar -------------------------------------------------------------------------------- /lib/slf4j-log4j12-1.6.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hocken/wxg/HEAD/lib/slf4j-log4j12-1.6.1.jar -------------------------------------------------------------------------------- /lib/jetty-io-8.0.1.v20110908.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hocken/wxg/HEAD/lib/jetty-io-8.0.1.v20110908.jar -------------------------------------------------------------------------------- /lib/jetty-http-8.0.1.v20110908.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hocken/wxg/HEAD/lib/jetty-http-8.0.1.v20110908.jar -------------------------------------------------------------------------------- /lib/jetty-server-8.0.1.v20110908.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hocken/wxg/HEAD/lib/jetty-server-8.0.1.v20110908.jar -------------------------------------------------------------------------------- /lib/jetty-util-8.0.1.v20110908.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hocken/wxg/HEAD/lib/jetty-util-8.0.1.v20110908.jar -------------------------------------------------------------------------------- /lib/jetty-websocket-8.0.1.v20110908.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hocken/wxg/HEAD/lib/jetty-websocket-8.0.1.v20110908.jar -------------------------------------------------------------------------------- /lib/jetty-continuation-8.0.1.v20110908.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hocken/wxg/HEAD/lib/jetty-continuation-8.0.1.v20110908.jar -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | WebSocket XMPP Gateway 2 | Copyright 2011 Christian Hocken, Dominik Renzel, 3 | Chair of Computer Science 5 (Information Systems) at RWTH Aachen University, Germany 4 | 5 | This product includes software developed at 6 | The Apache Software Foundation (http://www.apache.org/). 7 | 8 | This product makes use of SLF4J developed at 9 | Quality Open Software (qos.ch), distributed under the MIT license. 10 | 11 | This software was developed at the 12 | Chair of Computer Science 5 (Information Systems) at RWTH Aachen University, Germany. 13 | -------------------------------------------------------------------------------- /conf/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.rootLogger=DEBUG, file 2 | log4j.logger.org.eclipse.jetty=INFO, file 3 | 4 | log4j.appender.file=org.apache.log4j.DailyRollingFileAppender 5 | log4j.appender.file.File=log/wxg.log 6 | log4j.appender.file.layout=org.apache.log4j.PatternLayout 7 | log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %-5p %x - %m\n 8 | 9 | #log4j.rootLogger=DEBUG, stdout, file 10 | #log4j.logger.org.eclipse.jetty=INFO, stdout 11 | 12 | #log4j.appender.stdout=org.apache.log4j.ConsoleAppender 13 | #log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 14 | #log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %-5p %x - %m\n -------------------------------------------------------------------------------- /conf/server.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8080 8 | 9 | 8090 10 | 11 | 300000 12 | 13 | conf/keystore.jks/ 14 | 15 | 123456 16 | 17 | www 18 | 19 | true 20 | -------------------------------------------------------------------------------- /java/de/rwth_aachen/dbis/wsxmppgateway/WebSocketConstants.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 Christian Hocken, Dominik Renzel, 3 | * Chair of Computer Science 5 (Information Systems) at RWTH Aachen University, Germany. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package de.rwth_aachen.dbis.wsxmppgateway; 18 | 19 | /** 20 | * Constants defined in the WebSockets protocol 21 | * @author Christian Hocken (hocken@dbis.rwth-aachen.de) 22 | */ 23 | public class WebSocketConstants { 24 | 25 | public static final int CLOSE_NORMAL = 1000; 26 | public static final int CLOSE_GOING_DOWN = 1001; 27 | public static final int CLOSE_PROTOCOL_ERROR = 1002; 28 | public static final int CLOSE_WRONG_ENCODING = 1003; 29 | public static final int CLOSE_MESSAGESIZE_EXCEEDED = 1004; 30 | 31 | } 32 | -------------------------------------------------------------------------------- /java/de/rwth_aachen/dbis/wsxmppgateway/connection/WsConnectionState.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 Christian Hocken, Dominik Renzel, 3 | * Chair of Computer Science 5 (Information Systems) at RWTH Aachen University, Germany. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package de.rwth_aachen.dbis.wsxmppgateway.connection; 18 | 19 | /** 20 | * Constants that represent the life cycle of an XMPP connection 21 | * @author Christian Hocken (hocken@dbis.rwth-aachen.de) 22 | */ 23 | public class WsConnectionState { 24 | 25 | public static final int CONNECTED = 0; 26 | public static final int HANDSHAKE_COMPLETE = 100; 27 | public static final int HEADER_READ = 200; 28 | public static final int STREAM_OPENED = 300; 29 | public static final int STREAM_CLOSED = 400; 30 | public static final int DISCONNECTED = 500; 31 | } 32 | -------------------------------------------------------------------------------- /java/de/rwth_aachen/dbis/wsxmppgateway/exception/StreamErrorException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 Christian Hocken, Dominik Renzel, 3 | * Chair of Computer Science 5 (Information Systems) at RWTH Aachen University, Germany. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package de.rwth_aachen.dbis.wsxmppgateway.exception; 18 | 19 | import de.rwth_aachen.dbis.wsxmppgateway.XMPPConstants; 20 | import de.rwth_aachen.dbis.wsxmppgateway.error.StreamError; 21 | 22 | /** 23 | * A throwable wrapper for class {@link StreamError} 24 | * @see XMPPConstants 25 | * @author Patrick Schlebusch (schlebu@dbis.rwth-aachen.de) & Christian Hocken (hocken@dbis.rwth-aachen.de) 26 | */ 27 | public class StreamErrorException extends Exception { 28 | 29 | /** 30 | * generated serial version UID 31 | */ 32 | private static final long serialVersionUID = -8885817580289448845L; 33 | private StreamError streamError; 34 | 35 | /** 36 | * Creates a new stream error exception 37 | * @param code the error code according to {@link XMPPConstants} 38 | */ 39 | public StreamErrorException(int code) { 40 | streamError = new StreamError(code); 41 | } 42 | 43 | /** 44 | * Creates a new stream error exception 45 | * @param code the error code according to {@link XMPPConstants} 46 | * @param message A message that can be passed to the recipient 47 | * @param language The language of the message. Valid values are "de", "en", "fr", etc. 48 | */ 49 | public StreamErrorException(int code, String message, 50 | String language) { 51 | super(message); 52 | streamError = new StreamError(code, message, language); 53 | } 54 | 55 | /** 56 | * Get the wrapped stream error 57 | * @return The wrapped stream error 58 | */ 59 | public StreamError getStreamError() { 60 | return streamError; 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /java/de/rwth_aachen/dbis/wsxmppgateway/error/StreamError.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 Christian Hocken, Dominik Renzel, 3 | * Chair of Computer Science 5 (Information Systems) at RWTH Aachen University, Germany. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package de.rwth_aachen.dbis.wsxmppgateway.error; 18 | 19 | import de.rwth_aachen.dbis.wsxmppgateway.XMPPConstants; 20 | 21 | /** 22 | * StreamError is a wrapper class for an XMPP stream error 23 | * @see XMPPConstants 24 | * @author Patrick Schlebusch (schlebu@dbis.rwth-aachen.de) & Christian Hocken (hocken@dbis.rwth-aachen.de) 25 | */ 26 | public class StreamError { 27 | private int code; 28 | private String message; 29 | private String language; 30 | private String appContent; 31 | 32 | /** 33 | * Creates a new stream error 34 | * @param code the error code according to {@link XMPPConstants} 35 | */ 36 | public StreamError(int code) { 37 | this.code = code; 38 | } 39 | 40 | /** 41 | * Creates a new stream error 42 | * @param code the error code according to {@link XMPPConstants} 43 | * @param message A message that can be passed to the recipient 44 | * @param language The language of the message. Valid values are "de", "en", "fr", etc. 45 | */ 46 | public StreamError(int code, String message, String language) { 47 | this.code = code; 48 | this.message = message; 49 | this.language = language; 50 | } 51 | 52 | /** 53 | * Creates a new stream error 54 | * @param code the error code according to {@link XMPPConstants} 55 | * @param message A message that can be passed to the recipient 56 | * @param language The language of the message. Valid values are "de", "en", "fr", etc. 57 | * @param appContent an additional message filled by XEP handlers 58 | */ 59 | public StreamError(int code, String message, String language, String appContent) { 60 | this(code, message, language); 61 | this.appContent = appContent; 62 | } 63 | 64 | /** 65 | * Get the error code 66 | * @return The error code according to {@link XMPPConstants} 67 | */ 68 | public int getCode() { 69 | return code; 70 | } 71 | 72 | /** 73 | * Get the message 74 | * @return The message if set, otherwise null 75 | */ 76 | public String getMessage() { 77 | return message; 78 | } 79 | 80 | /** 81 | * Get the language of the message 82 | * @return The language of the message if set, otherwise null 83 | */ 84 | public String getLanguage() { 85 | return language; 86 | } 87 | 88 | /** 89 | * Get the application content 90 | * @return The application content if set, otherwise null 91 | */ 92 | public String getAppContent() { 93 | return appContent; 94 | } 95 | 96 | /** 97 | * 98 | * @return true if the message has been set 99 | */ 100 | public boolean hasMessage() { 101 | return (message != null); 102 | } 103 | 104 | /** 105 | * 106 | * @return true if the language of the message has been set 107 | */ 108 | public boolean hasLanguage() { 109 | return (language != null); 110 | } 111 | 112 | /** 113 | * 114 | * @return true if the application content has been set 115 | */ 116 | public boolean hasAppContent() { 117 | return (appContent != null); 118 | } 119 | 120 | } 121 | -------------------------------------------------------------------------------- /www/testbed.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | WebSocket XMPP Gateway (WXG) Testbed 4 | 58 | 68 | 69 |
70 |
71 |
72 | Uri:  73 |
74 | 78 |
79 | 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | WebSocket XMPP Gateway (WXG) - README 2 | ===================================== 3 | WXG (pronounce as "Wixig") is a Java-implementation of a gateway enabling communication 4 | with native XMPP[1] servers over the WebSocket protocol[2] and API[3], as specified in [4]. 5 | 6 | Until now, one of the common techniques in using XMPP in JS-powered Web applications was 7 | to make use of BOSH[5] and XMPP over BOSH[6]. However, with the advent of the WebSocket 8 | protocol and API already widely available in modern Web browsers, the unstable and slow 9 | BOSH technique should be replaced. Currently, most XMPP servers are not equipped with 10 | connectors for receiving and delivering stanzas over the WebSocket protocol as specified in [4]. 11 | 12 | The following scheme shows the basic functionality of WXG: 13 | 14 | Client <----> ws(xmpp) <----> WXG <----> xmpp <----> XMPP Server 15 | 16 | As such, WXG realizes two basic use cases: 17 | 18 | 1) When a client sends an XMPP stanza encapsulated in a WebSocket message, WXG "unpacks" the 19 | stanza and forwards it to the XMPP server natively. 20 | 21 | 2) When an XMPP server directs a stanza to one of its clients, WXG receives the native stanza, 22 | wraps it into a WebSocket message and forwards it to the client. 23 | 24 | In the following we describe how to build, configure, and run WXG. 25 | 26 | Prerequisites 27 | ============= 28 | - for running WXG, an installed Java Runtime Environment is required. 29 | - for building WXG, an installed Java SDK is required. 30 | - for performing automated builds of WXG, an installation of Apache Ant (http://ant.apache.org/) 31 | is required to run the bundled build script. 32 | 33 | Check-out and build WXG 34 | ======================= 35 | 36 | Perform the following steps to build WXG: 37 | 38 | 1) check out a copy of the git repository from github 39 | 40 | git clone git@github.com:hocken/wxg 41 | 42 | 2) change to the root directory of the WXG working copy 43 | 44 | cd wxg 45 | 46 | 3) perform an automated build using Apache Ant 47 | 48 | ant 49 | 50 | The result of the building process is a JAR file wxg.jar 51 | 52 | Configure WXG 53 | ============= 54 | 55 | Perform the following steps to configure WXG: 56 | 57 | 1) change to the conf directory relative to the WXG root directory 58 | 59 | cd ${WXG_HOME}/conf 60 | 61 | 2) edit the configuration file "server.xml" to your needs 62 | 63 | All configurable parameters are documented in the configuration file. 64 | 65 | Run WXG 66 | ======= 67 | 68 | Perform the following steps to run WXG: 69 | 70 | 1) change to the WXG root directory 71 | 72 | 2) run WXG 73 | 74 | java -jar wxg.jar 75 | 76 | Access WXG from client-side 77 | ========================== 78 | 79 | Open a new WebSocket and point it to ws://${WXG_HOSTNAME}:${WXG_PORT} where ${WXG_HOSTNAME} is the IP address 80 | or the hostname of the interface WXG is bound to and ${WXG_PORT} is the port specified in conf/server.xml. 81 | Keep in mind that according to [4] "xmpp" must be registered as subprotocol in the WebSocket object. 82 | WXG waits until the opening stream tag arrives and then reads the "to" attribute. Three options are available 83 | to define the hostname of the XMPP server to which the stream should be forwared: 84 | 85 | 1) Pass the desired hostname and port in the WebSocket URL 86 | Example: ws://localhost:8080/?xmpphost=jabber.com&xmppport=5222 87 | 88 | 2) Use the aliases db in conf/aliases.conf to rewrite the hostname set in the "to" 89 | attribute of the opening stream tag 90 | 91 | 3) Use the hostname set in the "to" attribute of the opening stream tag 92 | 93 | The options are applied in the given order. 94 | 95 | A simple testbed is available in ${WXG_HOME}/www/testbed.html. It can be accessed by pointing the browser 96 | to http://${WXG_HOSTNAME}:${WXG_PORT}/testbed.html (standard config: http://localhost:8080/testbed.html) 97 | 98 | References 99 | ========== 100 | [1] P. Saint-Andre. Extensible Messaging and Presence Protocol (XMPP): Core. RFC3920. Oct 2004. http://www.ietf.org/rfc/rfc3920.txt 101 | [2] I. Fette. The WebSocket protocol. HyBi Working Group Internet Draft. Jun 2011. http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-09 102 | [3] I. Hickson. The WebSocket API. W3C Editor's draft. Jun 2011. http://dev.w3.org/html5/websockets/ 103 | [4] J. Moffit, E. Cestari. An XMPP Sub-protocol for WebSocket. HyBi Working Group Internet Draft. Dec 2010. http://tools.ietf.org/html/draft-moffitt-xmpp-over-websocket-00 104 | [5] I. Paterson, D. Smith, P. Saint-Andre, J. Moffitt. XEP-0124: Bidirectional-streams Over Synchronous HTTP (BOSH). XSF Draft Standard. Jul 2010. http://xmpp.org/extensions/xep-0124.html 105 | [6] I. Paterson, P. Saint-Andre. XEP-0206: XMPP Over BOSH. XSF Draft Standard. Jul 2010. http://xmpp.org/extensions/xep-0206.html 106 | -------------------------------------------------------------------------------- /java/de/rwth_aachen/dbis/wsxmppgateway/WebSocketXmppGateway.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 Christian Hocken, Dominik Renzel, 3 | * Chair of Computer Science 5 (Information Systems) at RWTH Aachen University, Germany. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package de.rwth_aachen.dbis.wsxmppgateway; 18 | 19 | import java.io.FileInputStream; 20 | import java.io.FileNotFoundException; 21 | import java.io.IOException; 22 | import java.util.ArrayList; 23 | import java.util.Collections; 24 | import java.util.Enumeration; 25 | import java.util.HashMap; 26 | import java.util.List; 27 | import java.util.Map; 28 | import java.util.Properties; 29 | 30 | import javax.servlet.http.HttpServletRequest; 31 | 32 | import org.eclipse.jetty.http.ssl.SslContextFactory; 33 | import org.eclipse.jetty.server.Server; 34 | import org.eclipse.jetty.server.handler.ResourceHandler; 35 | import org.eclipse.jetty.server.nio.SelectChannelConnector; 36 | import org.eclipse.jetty.server.ssl.SslSelectChannelConnector; 37 | import org.eclipse.jetty.websocket.WebSocket; 38 | import org.eclipse.jetty.websocket.WebSocketHandler; 39 | import org.slf4j.Logger; 40 | import org.slf4j.LoggerFactory; 41 | 42 | import de.rwth_aachen.dbis.wsxmppgateway.connection.WsXMPPConnectionHandler; 43 | 44 | /** 45 | * This class is the main class of the WebSocket XMPP gateway. It starts a Jetty HTTP servlet server with connectors and handlers. 46 | * @author Christian Hocken 47 | */ 48 | public class WebSocketXmppGateway extends Server { 49 | 50 | //******************** defaults ********************// 51 | private static final String DEFAULT_CONFIG_FILE = "conf/server.xml"; 52 | private static final String DEFAULT_ALIASES_FILE ="conf/aliases.conf"; 53 | private static final int DEFAULT_TIMEOUT = 5*60*1000; //5 min 54 | private static final String DEFAULT_DOC_ROOT = "www"; 55 | private static final boolean DEFAULT_LIST_DIRECTORIES = false; 56 | 57 | //******************** private static configuration switches ********************// 58 | private static String configFile = DEFAULT_CONFIG_FILE; 59 | private static String aliasesFile = DEFAULT_ALIASES_FILE; 60 | 61 | private static String hostname = null; 62 | private static int webSocketPort = -1; 63 | private static int sslWebSocketPort = -1; 64 | private static int timeout = DEFAULT_TIMEOUT; //connection timeout in ms 65 | 66 | private static String keyStorePath = null; 67 | private static String keyStorePassword = null; 68 | 69 | private static String docRoot = DEFAULT_DOC_ROOT; 70 | private static boolean listDirectories = DEFAULT_LIST_DIRECTORIES; 71 | 72 | //******************** private static variables ********************// 73 | //initialize slf4j logging framework 74 | private static final Logger logger = LoggerFactory.getLogger(WebSocketXmppGateway.class); 75 | 76 | //initialize the aliases cache 77 | private static Map aliases = Collections.synchronizedMap(new HashMap()); 78 | 79 | //******************** private instance variables ********************// 80 | //Jetty connectors 81 | private SelectChannelConnector connector; 82 | private SslSelectChannelConnector sslConnector; 83 | 84 | //Jetty handlers 85 | private WebSocketHandler wsHandler; 86 | private ResourceHandler rHandler; 87 | 88 | //List of connected WebSocket clients 89 | private static final List connectedSockets = Collections.synchronizedList(new ArrayList()); 90 | 91 | //instance of the gateway 92 | private static WebSocketXmppGateway gateway = null; 93 | 94 | /** 95 | * Creates WebSocketXmppGateway and adds connectors and handlers as defined in static configuration switches 96 | */ 97 | public WebSocketXmppGateway() { 98 | //add Jetty connectors 99 | if (webSocketPort > -1) { 100 | //create plain channel connector 101 | connector = new SelectChannelConnector(); 102 | connector.setHost(hostname); 103 | connector.setPort(webSocketPort); 104 | addConnector(connector); 105 | } 106 | if(sslWebSocketPort > -1) { 107 | //create ssl channel connector 108 | SslContextFactory sslContextFactory = new SslContextFactory(); 109 | sslContextFactory.setKeyStore(keyStorePath); 110 | sslContextFactory.setKeyStorePassword(keyStorePassword); 111 | sslConnector = new SslSelectChannelConnector(sslContextFactory); 112 | sslConnector.setHost(hostname); 113 | sslConnector.setPort(sslWebSocketPort); 114 | addConnector(sslConnector); 115 | } 116 | 117 | //add Jetty handlers 118 | wsHandler = new WebSocketHandler() { 119 | @Override 120 | public WebSocket doWebSocketConnect(HttpServletRequest request, String subProtocol) { 121 | if ("xmpp".equals(subProtocol.toLowerCase())) 122 | return new WsXMPPConnectionHandler(request); 123 | else { 124 | //TODO 125 | //Exception handling for missing or wrong sub protocol 126 | return null; 127 | } 128 | } 129 | }; 130 | setHandler(wsHandler); 131 | rHandler = new ResourceHandler(); 132 | rHandler.setResourceBase(docRoot); 133 | rHandler.setDirectoriesListed(listDirectories); 134 | wsHandler.setHandler(rHandler); 135 | } 136 | 137 | //******************** static methods ********************// 138 | public static void main(String...args) { 139 | logger.info("Starting WebSocket XMPP gateway"); 140 | //load properties from location specified in configFile 141 | loadProperties(); 142 | //load aliases in cache 143 | loadAliases(); 144 | //init and start gateway 145 | gateway = new WebSocketXmppGateway(); 146 | try { 147 | gateway.start(); 148 | gateway.join(); 149 | } catch (Exception e) { 150 | logger.error("Cannot start WebSocket XMPP gateway", e); 151 | System.exit(1); 152 | } 153 | } 154 | 155 | /** 156 | * load properties from config file 157 | */ 158 | private static void loadProperties() { 159 | Properties prop = new Properties(); 160 | FileInputStream fis; 161 | try { 162 | fis = new FileInputStream(configFile); 163 | prop.loadFromXML(fis); 164 | hostname = prop.getProperty("Host"); 165 | webSocketPort = Integer.parseInt(prop.getProperty("Port", "-1")); 166 | sslWebSocketPort = Integer.parseInt(prop.getProperty("SSLport", "-1")); 167 | timeout = Integer.parseInt(prop.getProperty("Timeout", DEFAULT_TIMEOUT + "")); 168 | keyStorePath = prop.getProperty("Keystore"); 169 | keyStorePassword = prop.getProperty("KeystorePassword"); 170 | docRoot = prop.getProperty("DocRoot", DEFAULT_DOC_ROOT); 171 | listDirectories = "true".equals(prop.getProperty("DirectoryListing", DEFAULT_LIST_DIRECTORIES + "").toLowerCase()); 172 | } catch (Exception e) { 173 | logger.error("Cannot load properties from config file " + configFile,e); 174 | System.exit(1); 175 | } 176 | } 177 | 178 | /** 179 | * load aliases from config file 180 | */ 181 | private static void loadAliases() { 182 | Properties prop = new Properties(); 183 | FileInputStream fis; 184 | try { 185 | fis = new FileInputStream(aliasesFile); 186 | prop.load(fis); 187 | Enumeration e = prop.keys(); 188 | while (e.hasMoreElements()) { 189 | String host = (String)e.nextElement(); //save because properties are always Strings 190 | aliases.put(host.toLowerCase(), prop.getProperty(host)); 191 | } 192 | } catch (FileNotFoundException e) { 193 | logger.error("Cannot load " + aliasesFile + ". File not found!", e); 194 | } catch (IOException e) { 195 | logger.error("Cannot process " + aliasesFile, e); 196 | } 197 | } 198 | 199 | /** 200 | * Get a list of connected WebSockets 201 | * @return a list of connected WebSockets 202 | */ 203 | public static List getConnectedSockets() { 204 | return connectedSockets; 205 | } 206 | 207 | /** 208 | * Get the specified timeout 209 | * @return the specified timeout in ms 210 | */ 211 | public static int getTimeout() { 212 | return timeout; 213 | } 214 | 215 | /** 216 | * Get aliases for hostnames in the "to" attribute of the opening stream tag 217 | * @return aliases for hostnames in the "to" attribute 218 | */ 219 | public static Map getAliases() { 220 | return aliases; 221 | } 222 | 223 | } 224 | -------------------------------------------------------------------------------- /java/de/rwth_aachen/dbis/wsxmppgateway/XMPPConstants.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 Christian Hocken, Dominik Renzel, 3 | * Chair of Computer Science 5 (Information Systems) at RWTH Aachen University, Germany. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package de.rwth_aachen.dbis.wsxmppgateway; 18 | 19 | /** 20 | * Constants defined in the XMPP Core and IM RFCs 21 | * @author Holger Janssen (janssen@dbis.rwth-aachen.de) & Christian Hocken (hocken@dbis.rwth-aachen.de) 22 | */ 23 | public class XMPPConstants { 24 | 25 | public static final String SUPPORTED_VERSION_MAJOR = "1"; 26 | public static final String SUPPORTED_VERSION_MINOR = "0"; 27 | 28 | public static final String XML_CLIENT_NS = "jabber:client"; 29 | public static final String XML_NS_URI = "http://etherx.jabber.org/streams"; 30 | 31 | public static final String XML_SERVER_NS = "jabber:server"; 32 | public static final String XML_DIALBACK_NS = "jabber:server:dialback"; 33 | 34 | public static final String ERROR_NS_URN = "urn:ietf:params:xml:ns:xmpp-streams"; 35 | public static final String STANZA_ERROR_NS_URN = "urn:ietf:params:xml:ns:xmpp-stanzas"; 36 | 37 | 38 | public static final int ERROR_COND_BAD_FORMAT = 1; 39 | public static final int ERROR_COND_BAD_NAMESPACE_PREFIX = 2; 40 | public static final int ERROR_COND_CONFLICT = 3; 41 | public static final int ERROR_COND_CONNECTION_TIMEOUT = 4; 42 | public static final int ERROR_COND_HOST_GONE = 5; 43 | public static final int ERROR_COND_HOST_UNKOWN = 6; 44 | public static final int ERROR_COND_IMPROPER_ADDRESSING = 7; 45 | public static final int ERROR_COND_INTERNAL_SERVER_ERROR = 100; 46 | public static final int ERROR_COND_INVALID_FROM = 8; 47 | public static final int ERROR_COND_INVAILD_ID = 9; 48 | public static final int ERROR_COND_INVALID_NAMESPACE = 10; 49 | public static final int ERROR_COND_INVALID_XML = 11; 50 | public static final int ERROR_COND_NOT_AUTHORIZED = 12; 51 | public static final int ERROR_COND_POLICY_VIOLATION = 13; 52 | public static final int ERROR_COND_REMOTE_CONNECTION_FAILED = 14; 53 | public static final int ERROR_COND_RESOURCE_CONTSTRAINT = 15; 54 | public static final int ERROR_COND_RESTRICTED_XML = 16; 55 | public static final int ERROR_COND_SEE_OTHER_HOST = 17; 56 | public static final int ERROR_COND_SYSTEM_SHUTDOWN = 18; 57 | public static final int ERROR_COND_UNDEFINIED_CONDITION = 19; 58 | public static final int ERROR_COND_UNSUPPORTED_ENCODING = 20; 59 | public static final int ERROR_COND_UNSUPPORTED_STANZA_TYPE = 21; 60 | public static final int ERROR_COND_UNSUPPORTED_VERSION = 22; 61 | public static final int ERROR_COND_XML_NOT_WELL_FORMED = 23; 62 | 63 | 64 | public static final int STANZA_ERROR_COND_BAD_REQUEST = 101; 65 | public static final int STANZA_ERROR_COND_CONFLICT = 102; 66 | public static final int STANZA_ERROR_COND_FEATURE_NOT_IMPLEMENTED = 103; 67 | public static final int STANZA_ERROR_COND_FORBIDDEN = 104; 68 | public static final int STANZA_ERROR_COND_GONE = 105; 69 | public static final int STANZA_ERROR_COND_INTERNAL_SERVER_ERROR = 106; 70 | public static final int STANZA_ERROR_COND_ITEM_NOT_FOUND = 107; 71 | public static final int STANZA_ERROR_COND_JID_MALFORMED = 108; 72 | public static final int STANZA_ERROR_COND_NOT_ACCEPTABLE = 109; 73 | public static final int STANZA_ERROR_COND_NOT_ALLOWED = 110; 74 | public static final int STANZA_ERROR_COND_NOT_AUTHORIZED = 111; 75 | public static final int STANZA_ERROR_COND_PAYMENT_REQUIRED = 112; 76 | public static final int STANZA_ERROR_COND_RECIPIENT_UNAVAILABLE = 113; 77 | public static final int STANZA_ERROR_COND_REDIRECT = 114; 78 | public static final int STANZA_ERROR_COND_REGISTRATION_REQUIRED = 115; 79 | public static final int STANZA_ERROR_COND_REMOTE_SERVER_NOT_FOUND = 116; 80 | public static final int STANZA_ERROR_COND_REMOTE_SERVER_TIMEOUT = 117; 81 | public static final int STANZA_ERROR_COND_RESOURCE_CONSTRAINT = 118; 82 | public static final int STANZA_ERROR_COND_SERVICE_UNAVAILABLE = 119; 83 | public static final int STANZA_ERROR_COND_SUBSCRIPTION_REQUIRED = 120; 84 | public static final int STANZA_ERROR_COND_UNDEFINED_CONDITION = 121; 85 | public static final int STANZA_ERROR_COND_UNEXPECTED_REQUEST = 122 ; 86 | 87 | public static final int STANZA_ERROR_TYPE_CANCEL = 201; 88 | public static final int STANZA_ERROR_TYPE_CONTINUE = 202; 89 | public static final int STANZA_ERROR_TYPE_MODIFY = 203; 90 | public static final int STANZA_ERROR_TYPE_AUTH = 204; 91 | public static final int STANZA_ERROR_TYPE_WAIT = 205; 92 | 93 | public static final int SASL_ERROR_COND_ABORTED = 301; 94 | public static final int SASL_ERROR_COND_INCORRECT_ENCODING = 302; 95 | public static final int SASL_ERROR_COND_INVALID_AUTHZID = 303; 96 | public static final int SASL_ERROR_COND_INVALID_MECHANISM = 304; 97 | public static final int SASL_ERROR_COND_MECHANISM_TOO_WEAK = 305; 98 | public static final int SASL_ERROR_COND_NOT_AUTHORIZED = 306; 99 | public static final int SASL_ERROR_COND_TEMPORARIY_AUTH_FAILURE = 307; 100 | 101 | public static final String URN_SASL = "urn:ietf:params:xml:ns:xmpp-sasl"; 102 | public static final String URN_TLS = "urn:ietf:params:xml:ns:xmpp-tls"; 103 | public static final String URN_BIND = "urn:ietf:params:xml:ns:xmpp-bind"; 104 | public static final String URN_SESSION = "urn:ietf:params:xml:ns:xmpp-session"; 105 | 106 | public static final int DEFAULT_SERVER_PORT = 5269; 107 | public static final int DEFAULT_CLIENT_PORT = 5222; 108 | 109 | 110 | /** 111 | * returns the corresponding xml tag name for the given error condition number. 112 | * (refer to the ERROR_COND_... constants) 113 | * 114 | * @param condition an int 115 | * 116 | * @return a String 117 | * 118 | */ 119 | public static String errorCond2tagName ( int condition ) { 120 | switch ( condition ) { 121 | case ERROR_COND_BAD_FORMAT: return "bad-format"; 122 | case ERROR_COND_BAD_NAMESPACE_PREFIX: return "bad-namespace-prefix"; 123 | case ERROR_COND_CONFLICT: return "conflict"; 124 | case ERROR_COND_CONNECTION_TIMEOUT: return "connection-timeout"; 125 | case ERROR_COND_HOST_GONE: return "host-gone"; 126 | case ERROR_COND_HOST_UNKOWN: return "host-unknown"; 127 | case ERROR_COND_IMPROPER_ADDRESSING: return "improper-addressing"; 128 | case ERROR_COND_INTERNAL_SERVER_ERROR: return "internal-server-error"; 129 | case ERROR_COND_INVALID_FROM: return "invalid-from"; 130 | case ERROR_COND_INVAILD_ID: return "invalid-id"; 131 | case ERROR_COND_INVALID_NAMESPACE: return "invalid-namespace"; 132 | case ERROR_COND_INVALID_XML: return "invalid-xml"; 133 | case ERROR_COND_NOT_AUTHORIZED: return "not-authorized"; 134 | case ERROR_COND_POLICY_VIOLATION: return "policy-violation"; 135 | case ERROR_COND_REMOTE_CONNECTION_FAILED: return "remote-connection-failed"; 136 | case ERROR_COND_RESOURCE_CONTSTRAINT: return "resource-constraint"; 137 | case ERROR_COND_RESTRICTED_XML: return "restricted-xml"; 138 | case ERROR_COND_SEE_OTHER_HOST: return "see-other-host"; 139 | case ERROR_COND_SYSTEM_SHUTDOWN: return "system-shutdown"; 140 | case ERROR_COND_UNDEFINIED_CONDITION: return "undefined-condition"; 141 | case ERROR_COND_UNSUPPORTED_ENCODING: return "unsupported-encoding"; 142 | case ERROR_COND_UNSUPPORTED_STANZA_TYPE: return "unsupported-stanza-type"; 143 | case ERROR_COND_UNSUPPORTED_VERSION: return "unsupported-version"; 144 | case ERROR_COND_XML_NOT_WELL_FORMED: return "xml-not-well-formed"; 145 | default: throw new IllegalArgumentException ( "Unkown Error Condition" ); 146 | } 147 | } 148 | 149 | 150 | /** 151 | * returns the corresponding xml tag name for the given stanza error condition number. 152 | * (refer to the STANZA_ERROR_COND_... constants) 153 | * 154 | * @param condition an int 155 | * 156 | * @return a String 157 | * 158 | */ 159 | public static String stanzaCond2tagName ( int condition ) { 160 | switch ( condition ) { 161 | case STANZA_ERROR_COND_BAD_REQUEST: return "bad-request"; 162 | case STANZA_ERROR_COND_CONFLICT: return "conflict"; 163 | case STANZA_ERROR_COND_FEATURE_NOT_IMPLEMENTED: return "not-implemented"; 164 | case STANZA_ERROR_COND_FORBIDDEN: return "forbiden"; 165 | case STANZA_ERROR_COND_GONE: return "gone"; 166 | case STANZA_ERROR_COND_INTERNAL_SERVER_ERROR: return "internal-server-error"; 167 | case STANZA_ERROR_COND_ITEM_NOT_FOUND: return "item-not-found"; 168 | case STANZA_ERROR_COND_JID_MALFORMED: return "jid-malformed"; 169 | case STANZA_ERROR_COND_NOT_ACCEPTABLE: return "not-acceptable"; 170 | case STANZA_ERROR_COND_NOT_ALLOWED: return "not-allowed"; 171 | case STANZA_ERROR_COND_NOT_AUTHORIZED: return "not-authorized"; 172 | case STANZA_ERROR_COND_PAYMENT_REQUIRED: return "payment-required"; 173 | case STANZA_ERROR_COND_RECIPIENT_UNAVAILABLE: return "recipient-unavailable"; 174 | case STANZA_ERROR_COND_REDIRECT: return "redirect"; 175 | case STANZA_ERROR_COND_REGISTRATION_REQUIRED: return "registration-required"; 176 | case STANZA_ERROR_COND_REMOTE_SERVER_NOT_FOUND: return "remote-server-not-found"; 177 | case STANZA_ERROR_COND_REMOTE_SERVER_TIMEOUT: return "remote-server-timeout"; 178 | case STANZA_ERROR_COND_RESOURCE_CONSTRAINT: return "resource-constraint"; 179 | case STANZA_ERROR_COND_SERVICE_UNAVAILABLE: return "service-unavailable"; 180 | case STANZA_ERROR_COND_SUBSCRIPTION_REQUIRED: return "subscription-required"; 181 | case STANZA_ERROR_COND_UNDEFINED_CONDITION: return "undefinied-condition"; 182 | case STANZA_ERROR_COND_UNEXPECTED_REQUEST: return "unexpected-request"; 183 | 184 | default: throw new IllegalArgumentException ( "Unkown stanza error condition id" ); 185 | } 186 | } 187 | 188 | /** 189 | * returns the corresponding xml type attribute value for the given stanza error condition number. 190 | * (refer to the STANZA_ERROR_TYPE_... constants) 191 | * 192 | * @param type an int 193 | * 194 | * @return a String 195 | * 196 | */ 197 | public static String stanzaCondType2Attribute ( int type ) { 198 | switch ( type ) { 199 | case STANZA_ERROR_TYPE_CANCEL: return "cancel"; 200 | case STANZA_ERROR_TYPE_CONTINUE: return "continue"; 201 | case STANZA_ERROR_TYPE_MODIFY: return "modify"; 202 | case STANZA_ERROR_TYPE_AUTH: return "auth"; 203 | case STANZA_ERROR_TYPE_WAIT: return "wait"; 204 | default: throw new IllegalArgumentException ( "Unkown stanza error type id: " + type ); 205 | } 206 | } 207 | 208 | 209 | 210 | /** 211 | * returns the tag name for an sasl error condition constant (refer to the SASL_ERROR_COND_... constants) 212 | * 213 | * @param error an int 214 | * 215 | * @return a String 216 | * 217 | */ 218 | public static String saslCond2Tag ( int error ) { 219 | switch ( error ) { 220 | case SASL_ERROR_COND_ABORTED: return "aborted"; 221 | case SASL_ERROR_COND_INCORRECT_ENCODING: return "incorrect-encoding"; 222 | case SASL_ERROR_COND_INVALID_AUTHZID: return "invalid-authzid"; 223 | case SASL_ERROR_COND_INVALID_MECHANISM: return "invalid-mechanism"; 224 | case SASL_ERROR_COND_MECHANISM_TOO_WEAK: return "mechanism-too-weak"; 225 | case SASL_ERROR_COND_NOT_AUTHORIZED: return "not-authorized"; 226 | case SASL_ERROR_COND_TEMPORARIY_AUTH_FAILURE: return "temporary-auth-failure"; 227 | default: 228 | throw new IllegalArgumentException ( "Unkown SASL error condiftion" ); 229 | } 230 | } 231 | 232 | } 233 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /java/de/rwth_aachen/dbis/wsxmppgateway/connection/WsXMPPConnectionHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 Christian Hocken, Dominik Renzel, 3 | * Chair of Computer Science 5 (Information Systems) at RWTH Aachen University, Germany. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package de.rwth_aachen.dbis.wsxmppgateway.connection; 18 | 19 | import i5.simpleXML.Element; 20 | import i5.simpleXML.EndOfBufferException; 21 | import i5.simpleXML.TimeoutException; 22 | import i5.simpleXML.XMLNoHeaderException; 23 | import i5.simpleXML.XMLStreamParser; 24 | import i5.simpleXML.XMLSyntaxException; 25 | import i5.simpleXML.XMLWriter; 26 | 27 | import java.io.IOException; 28 | import java.io.InputStream; 29 | import java.io.OutputStream; 30 | import java.net.Socket; 31 | import java.net.UnknownHostException; 32 | import java.util.Enumeration; 33 | 34 | import javax.servlet.http.HttpServletRequest; 35 | 36 | import org.eclipse.jetty.websocket.WebSocket; 37 | import org.slf4j.Logger; 38 | import org.slf4j.LoggerFactory; 39 | 40 | import de.rwth_aachen.dbis.wsxmppgateway.WebSocketConstants; 41 | import de.rwth_aachen.dbis.wsxmppgateway.WebSocketXmppGateway; 42 | import de.rwth_aachen.dbis.wsxmppgateway.XMPPConstants; 43 | import de.rwth_aachen.dbis.wsxmppgateway.error.StreamError; 44 | import de.rwth_aachen.dbis.wsxmppgateway.exception.StreamErrorException; 45 | 46 | 47 | /** 48 | * Connection between client and gateway that has been established via the WebSockets protocol 49 | * @author Christian Hocken (hocken@dbis.rwth-aachen.de) 50 | */ 51 | public class WsXMPPConnectionHandler implements WebSocket, WebSocket.OnFrame, WebSocket.OnTextMessage, WebSocket.OnControl { 52 | 53 | private static final String PARAM_XMPP_HOSTNAME = "xmpphost"; 54 | private static final String PARAM_XMPP_PORT = "xmppport"; 55 | 56 | //initialize slf4j logging framework 57 | private static final Logger logger = LoggerFactory.getLogger(WsXMPPConnectionHandler.class); 58 | 59 | private final HttpServletRequest request; //the HTTP request that performed the upgrade 60 | private FrameConnection connection; //the WebSocket connection to the client 61 | 62 | //xmpp servername and port extracted from the request 63 | private String xmppHostname = null; 64 | private int xmppPort = -1; 65 | 66 | private volatile int iStatus = -1; //stores connection state according to constants in WsConnectionState 67 | 68 | //flags to store gateway behavior 69 | private boolean openingTagSent = false; //true, if gateway has sent an opening stream tag 70 | private boolean closingTagSent = false; //true, if gateway has sent a closing stream tag 71 | 72 | //flags to store client behavior 73 | private boolean clientSentHeader = false; //true, if client has sent an XML header 74 | private boolean clientSentClosingTag = false; //true, if client has sent a closing stream tag 75 | private boolean serverSentClosingTag = false; //true, if XMPP server has sent a closing stream tag 76 | 77 | private WsXMPPProxy wsProxy = null; 78 | 79 | @SuppressWarnings("unused") 80 | private String streamXmlLang = null; //TODO: Use in validity checking 81 | private Element receivedElement; //the stanza lately received from the client 82 | 83 | /** 84 | * Creates a new WebSocket connection handler 85 | * @param request the servlet request that performed the HTTP upgrade 86 | */ 87 | public WsXMPPConnectionHandler(HttpServletRequest request) { 88 | this.request = request; 89 | this.xmppHostname = request.getParameter(PARAM_XMPP_HOSTNAME); 90 | try { 91 | this.xmppPort = Integer.parseInt(request.getParameter(PARAM_XMPP_PORT)); 92 | } 93 | catch (NumberFormatException e) { 94 | this.xmppPort = -1; 95 | } 96 | this.iStatus = WsConnectionState.CONNECTED; 97 | } 98 | 99 | /** 100 | * Send a stanza to the connected client 101 | * @param stanza the stanza to be sent to the client 102 | */ 103 | public void sendStanza(Element stanza) { 104 | sendMessage(stanza.toString(false)); 105 | } 106 | 107 | /** 108 | * Send a message to the connected client 109 | * @param message the message to be sent to the client 110 | */ 111 | public void sendMessage(String message) { 112 | try { 113 | logger.debug(request.getRemoteHost() + " - sending message: " + message); 114 | connection.sendMessage(message); 115 | } catch (IOException e) { 116 | logger.info(request.getRemoteHost() + " - failed to send message", e); 117 | } 118 | } 119 | 120 | /** 121 | * Handle a stream error 122 | * @param streamError the stream error to be sent to the client 123 | */ 124 | public void handleStreamError(StreamError streamError) { 125 | if (closingTagSent || iStatus >= WsConnectionState.DISCONNECTED) 126 | throw new IllegalStateException ( "Cannot send error stanza to an already closed stream!" ); 127 | String xml = ""; 128 | if(!openingTagSent) 129 | xml += "";//TODO Add more data to opening tag (although this case will occur rarely) 130 | xml += ""; 131 | xml += "<" + XMPPConstants.errorCond2tagName(streamError.getCode()) + " xmlns=\"" + XMPPConstants.ERROR_NS_URN + "\" />"; 132 | if (streamError.hasMessage()) { 133 | xml += ""; 137 | } 138 | if (streamError.hasAppContent()) 139 | xml += streamError.getAppContent(); 140 | xml += ""; //stream errors are terminal => close the stream 141 | sendMessage(xml); 142 | closingTagSent = true; 143 | } 144 | 145 | //******************** Methods from WebSocket interfaces ********************// 146 | 147 | @Override 148 | public void onHandshake(FrameConnection connection) { 149 | logger.info(request.getRemoteHost() + " - starting new WebSocket handler"); 150 | this.connection = connection; 151 | iStatus = WsConnectionState.HANDSHAKE_COMPLETE; 152 | } 153 | 154 | @Override 155 | public void onOpen(Connection connection) { 156 | WebSocketXmppGateway.getConnectedSockets().add(this); 157 | } 158 | 159 | @Override 160 | public void onClose(int code, String message) { 161 | logger.info(request.getRemoteHost() + " - client is closing the connection with code " + code + " and message: " + message); 162 | if (!clientSentClosingTag) { 163 | //cleanup 164 | clientSentClosingTag = true; 165 | if(wsProxy != null) 166 | wsProxy.forwardClosingTagToServer(); 167 | } 168 | } 169 | 170 | @Override 171 | public boolean onControl(byte controlCode, byte[] data, int offset, int length) { 172 | //TODO ping?! Not sure if it is handled by Jetty 173 | return false; 174 | } 175 | 176 | @Override 177 | public void onMessage(String data) { 178 | logger.debug(request.getRemoteHost() + " - received message: " + data); 179 | try { 180 | switch (iStatus) { 181 | case WsConnectionState.HANDSHAKE_COMPLETE: 182 | //read XML header, if present 183 | logger.info(request.getRemoteHost() + " - reading XML header"); 184 | int headerEnd = data.indexOf("?>") +1; 185 | if (data.startsWith(" 0) { 186 | clientSentHeader = true; 187 | String header = data.substring(0, headerEnd); 188 | readXmlHeader(header); 189 | data = data.substring(headerEnd + 1).trim(); 190 | } 191 | iStatus = WsConnectionState.HEADER_READ; 192 | if (data.equals("")) //stop if no more data is sent in this message 193 | break; 194 | case WsConnectionState.HEADER_READ: 195 | logger.info(request.getRemoteHost() + " - opening stream"); 196 | data.trim(); 197 | String tagName = data.substring(1, data.indexOf(" ")); 198 | data += ""; //append closing tag to enable parsing 199 | Element root = new Element(data, false); 200 | if (wsProxy == null) { //not null after stream has been reseted 201 | String to = root.getAttribute("to"); 202 | int port = 5222; 203 | //check for alias sent in request 204 | if (xmppHostname != null) { 205 | to = xmppHostname; 206 | if (xmppPort > -1) 207 | port = xmppPort; 208 | } 209 | //check for alias in cache 210 | else if (WebSocketXmppGateway.getAliases().containsKey(to.toLowerCase())) { 211 | to = WebSocketXmppGateway.getAliases().get(to.toLowerCase()); 212 | } 213 | wsProxy = new WsXMPPProxy(to, port, root); 214 | new Thread(wsProxy).start(); 215 | } 216 | wsProxy.forwardOpeningTagToServer(clientSentHeader, root); 217 | iStatus = WsConnectionState.STREAM_OPENED; 218 | break; 219 | case WsConnectionState.STREAM_OPENED: 220 | if ("".equals(data)) { 221 | logger.info(request.getRemoteHost() + " - closing stream"); 222 | clientSentClosingTag = true; 223 | iStatus = WsConnectionState.STREAM_CLOSED; 224 | wsProxy.forwardClosingTagToServer(); 225 | } 226 | else { 227 | //try to parse data 228 | receivedElement = new Element(data); 229 | logger.debug(request.getRemoteHost() + " - forwarding stanza:\n" + receivedElement.toString(false)); 230 | wsProxy.forwardStanzaToServer(receivedElement); 231 | break; 232 | } 233 | case WsConnectionState.STREAM_CLOSED: 234 | //should not be reachable since no messages are received after the stream is closed 235 | //cleanup necessary? 236 | break; 237 | default: 238 | throw new StreamErrorException(XMPPConstants.ERROR_COND_UNDEFINIED_CONDITION, "No WebSockets message expected. The stream has already been closed!", "en"); 239 | } 240 | } catch (XMLSyntaxException e) { 241 | logger.info(request.getRemoteAddr() + " - error during message handling", e); 242 | handleStreamError(new StreamError(XMPPConstants.ERROR_COND_XML_NOT_WELL_FORMED, e.getMessage(), "en")); 243 | } catch(UnknownHostException e) { 244 | logger.info(request.getRemoteAddr() + " - error during message handling", e); 245 | handleStreamError(new StreamError(XMPPConstants.ERROR_COND_HOST_UNKOWN, e.getMessage(), "en")); 246 | } catch(IOException e) { 247 | logger.info(request.getRemoteAddr() + " - error during message handling", e); 248 | handleStreamError(new StreamError(XMPPConstants.ERROR_COND_REMOTE_CONNECTION_FAILED, e.getMessage(), "en")); 249 | } catch (StreamErrorException e) { 250 | logger.info(request.getRemoteAddr() + " - error during message handling", e); 251 | handleStreamError(e.getStreamError()); 252 | } 253 | } 254 | 255 | @Override 256 | public boolean onFrame(byte flags, byte opcode, byte[] data, int offset, int length) { 257 | return false; 258 | } 259 | 260 | /** 261 | * Read the XML header sent by the client and check conformance 262 | * @param data the XML header sent by the client 263 | * @throws XMLSyntaxException 264 | */ 265 | private void readXmlHeader(String data) throws XMLSyntaxException { 266 | //TODO check validity 267 | } 268 | 269 | /** 270 | * Reset the stream after starting TLS encryption and SASL authentication 271 | */ 272 | private void resetStream() { 273 | //reset connection state 274 | iStatus = WsConnectionState.HANDSHAKE_COMPLETE; 275 | } 276 | 277 | /** 278 | * Close the WebSocket connection 279 | */ 280 | private void closeConnection() { 281 | closeConnection(WebSocketConstants.CLOSE_NORMAL); 282 | } 283 | 284 | /** 285 | * Close the WebSocket connection 286 | * @param status the status code to be sent to the client 287 | */ 288 | private void closeConnection(int status) { 289 | connection.disconnect(); 290 | iStatus = WsConnectionState.DISCONNECTED; 291 | WebSocketXmppGateway.getConnectedSockets().remove(this); 292 | } 293 | 294 | //******************** Private class for proxy handling ********************// 295 | 296 | /** 297 | * Connection between gateway and the remote XMPP server 298 | * 299 | * @author Christian Hocken (hocken@dbis.rwth-aachen.de) 300 | */ 301 | private class WsXMPPProxy implements Runnable { 302 | private String hostname; 303 | private int port; 304 | private Socket socket; 305 | private InputStream input; 306 | private OutputStream output; 307 | private XMLStreamParser xmlParser; 308 | private XMLWriter xmlWriter; 309 | 310 | private boolean serverSentHeader = false; 311 | 312 | //stores the status of the connection according to constants in WsConnectionState 313 | private volatile int iProxyStatus = -1; 314 | 315 | /** 316 | * Creates a remote connection to an XMPP server 317 | * @param hostname the hostname of the XMPP server 318 | * @param port the port of the XMPP server 319 | * @param root the opening stream element received from the WebSocket client 320 | * @throws UnknownHostException if the hostname of the XMPP server is unknown 321 | * @throws IOException if the connection can not be established 322 | */ 323 | public WsXMPPProxy(String hostname, int port, Element root) throws UnknownHostException, IOException{ 324 | this.hostname = hostname; 325 | this.port = port; 326 | socket = new Socket(hostname, port); 327 | socket.setSoTimeout(WebSocketXmppGateway.getTimeout()); 328 | input = socket.getInputStream(); 329 | output = socket.getOutputStream(); 330 | xmlParser = new XMLStreamParser(input); 331 | xmlWriter = new XMLWriter(output); 332 | iProxyStatus = WsConnectionState.CONNECTED; 333 | logger.info(getServername(true) + " - opened connection to XMPP server"); 334 | } 335 | 336 | @Override 337 | public void run() { 338 | try { 339 | while (!Thread.currentThread().isInterrupted() && iProxyStatus < WsConnectionState.DISCONNECTED) { 340 | switch (iProxyStatus) { 341 | case WsConnectionState.CONNECTED: 342 | logger.info(getServername(true) + " - reading xml header"); 343 | try { 344 | xmlParser.readHeader( true ); 345 | serverSentHeader = true; 346 | } catch ( XMLNoHeaderException e ) { 347 | xmlParser.setHeaderRead(); 348 | } 349 | iProxyStatus = WsConnectionState.HEADER_READ; 350 | //no break; necessary since root element is expected to be received next 351 | case WsConnectionState.HEADER_READ: 352 | logger.info(getServername(true) + " - opening stream"); 353 | xmlParser.openRoot(true); 354 | Element root = xmlParser.getRoot(); 355 | iProxyStatus = WsConnectionState.STREAM_OPENED; 356 | //forward header and root tag 357 | forwardOpeningTagToClient(serverSentHeader, root); 358 | //no break; necessary since stanzas are expected to be received next 359 | case WsConnectionState.STREAM_OPENED: 360 | Element stanza = xmlParser.getNextElement(true, true); 361 | if (stanza == null && xmlParser.isRootClosed()) { 362 | serverSentClosingTag = true; 363 | forwardClosingTagToClient(); 364 | break; 365 | } 366 | else if (stanza.hasAttribute("xmlns") && stanza.getAttribute("xmlns").equals(XMPPConstants.URN_TLS)) { 367 | //do not forward stanza to client 368 | break; 369 | } 370 | else if (stanza.hasAttribute("xmlns") && stanza.getAttribute("xmlns").equals(XMPPConstants.URN_SASL)) { 371 | if ("success".equals(stanza.getName())) { 372 | resetStream(); 373 | } 374 | } 375 | //forward stanza to client 376 | sendStanza(stanza); 377 | break; 378 | case WsConnectionState.STREAM_CLOSED: 379 | if (!socket.isClosed()) 380 | socket.close(); 381 | iProxyStatus = WsConnectionState.DISCONNECTED; 382 | break; 383 | default: 384 | throw new IllegalStateException("Illegal state (" + iProxyStatus + ") in WebSocket proxy!"); 385 | } 386 | } 387 | 388 | } catch (EndOfBufferException e) { 389 | logger.info(getServername(true) + " - error during message handling", e); 390 | handleStreamError(new StreamError(XMPPConstants.ERROR_COND_REMOTE_CONNECTION_FAILED)); 391 | } catch (XMLSyntaxException e) { 392 | logger.info(getServername(true) + " - error during message handling", e); 393 | handleStreamError( new StreamError(XMPPConstants.ERROR_COND_INVALID_XML, "Syntax error in xml stream!", "en") ); 394 | } catch (TimeoutException e) { 395 | logger.info(getServername(true) + " - error during message handling", e); 396 | handleStreamError( new StreamError(XMPPConstants.ERROR_COND_CONNECTION_TIMEOUT) ); 397 | } catch (IOException e) { 398 | logger.info(getServername(true) + " - error during message handling", e); 399 | handleStreamError(new StreamError(XMPPConstants.ERROR_COND_REMOTE_CONNECTION_FAILED)); 400 | } catch (IllegalStateException e) { 401 | logger.error(getServername(true) + " - error during message handling", e); 402 | handleStreamError(new StreamError(XMPPConstants.ERROR_COND_INTERNAL_SERVER_ERROR)); 403 | } 404 | 405 | //we need to close the connection to the XMPP server when the run loop has stopped 406 | if (!socket.isClosed()) { 407 | try{ 408 | socket.close(); 409 | } catch(IOException e){} 410 | } 411 | iProxyStatus = WsConnectionState.DISCONNECTED; 412 | 413 | //cleanup if an error has occured 414 | if (!serverSentClosingTag) { 415 | serverSentClosingTag = true; 416 | forwardClosingTagToClient(); 417 | } 418 | } 419 | 420 | /** 421 | * Reset the stream after starting TLS encryption and SASL authentication. 422 | * Reopen the XML parser and the XML writer 423 | */ 424 | private void resetStream() { 425 | //reset connection state in client connection 426 | WsXMPPConnectionHandler.this.resetStream(); 427 | //reset XML stream parser 428 | xmlParser = new XMLStreamParser(input); 429 | xmlWriter = new XMLWriter(output); 430 | //reset connection state 431 | iProxyStatus = WsConnectionState.CONNECTED; 432 | } 433 | 434 | /** 435 | * Forward the opening stream tag received from the client to the XMPP server 436 | * @param sendXmlHeader true if an XML header should be sent 437 | * @param root the opening stream tag 438 | * @throws XMLSyntaxException if the passed stream element is malicious 439 | */ 440 | public void forwardOpeningTagToServer(boolean sendXmlHeader, Element root) throws XMLSyntaxException { 441 | synchronized (xmlWriter) { 442 | if ( sendXmlHeader ) 443 | xmlWriter.print( "" ); 444 | xmlWriter.print ( " e = root.getAttributeNames(); e.hasMoreElements(); ) { 446 | String attribute = e.nextElement(); 447 | xmlWriter.printXMLAttributes(attribute, root.getAttribute(attribute)); 448 | } 449 | xmlWriter.print ( ">" ); 450 | xmlWriter.flush(); 451 | } 452 | } 453 | 454 | /** 455 | * Forward the opening stream tag received from the server to the client 456 | * @param sendXmlHeader true if an XML header should be sent 457 | * @param root the opening stream tag 458 | * @throws XMLSyntaxException if the passed stream element is malicious 459 | */ 460 | private void forwardOpeningTagToClient(boolean sendXmlHeader, Element root) throws XMLSyntaxException { 461 | String xml = ""; 462 | if (sendXmlHeader) 463 | xml += ""; 464 | xml += " e = root.getAttributeNames(); e.hasMoreElements(); ) { 466 | String attribute = e.nextElement(); 467 | xml += " " + attribute + "=\"" + root.getAttribute(attribute) + "\""; 468 | } 469 | xml +=">"; 470 | sendMessage(xml); 471 | openingTagSent = true; 472 | } 473 | 474 | /** 475 | * Forward the closing stream tag received from the client to the server. 476 | * If the server is the party that closed the stream the connection to the client and 477 | * to the XMPP server is closed. Otherwise the handler waits for a reply from the server. 478 | */ 479 | public void forwardClosingTagToServer() { 480 | synchronized (xmlWriter) { 481 | xmlWriter.print(""); 482 | xmlWriter.flush(); 483 | } 484 | if (serverSentClosingTag) { 485 | //XMPP server sent initial closing tag. This is the reply of the client -> close connection 486 | iProxyStatus = WsConnectionState.STREAM_CLOSED; 487 | try { 488 | socket.close(); 489 | } catch (IOException e) {} 490 | iProxyStatus = WsConnectionState.DISCONNECTED; 491 | closeConnection(); 492 | } 493 | else { 494 | //client sent initial closing tag -> wait for reply. 495 | } 496 | } 497 | 498 | /** 499 | * Forward the closing stream tag received from the server to the client. 500 | * If the client is the party that closed the stream the connection to the client and 501 | * to the XMPP server is closed.Otherwise the handler waits for a reply from the client. 502 | */ 503 | private void forwardClosingTagToClient() { 504 | sendMessage(""); 505 | closingTagSent = true; 506 | if (clientSentClosingTag) { 507 | //client sent initial closing tag. This is the reply of the XMPP server -> close connection 508 | iProxyStatus = WsConnectionState.STREAM_CLOSED; 509 | try { 510 | socket.close(); 511 | } catch (IOException e) {} 512 | iProxyStatus = WsConnectionState.DISCONNECTED; 513 | closeConnection(); 514 | } 515 | else { 516 | //server sent initial closing tag -> wait for reply. 517 | } 518 | } 519 | 520 | /** 521 | * Forward a stanza received from the client to the XMPP server 522 | * @param stanza The stanza received from the client 523 | */ 524 | public void forwardStanzaToServer(Element stanza) { 525 | synchronized (xmlWriter) { 526 | xmlWriter.print(stanza.toString(false)); 527 | xmlWriter.flush(); 528 | } 529 | } 530 | 531 | /** 532 | * Get the server name of the XMPP server 533 | * @param printDedicatedClient if true, the hostname of the dedicated client is printed in brackets after the XMPP server hostname 534 | * @return the server name of the XMPP server 535 | */ 536 | public String getServername(boolean printDedicatedClient) { 537 | String servername = hostname + ":" + port; 538 | if(printDedicatedClient) 539 | servername += " (" + request.getRemoteHost() + ")"; 540 | return servername; 541 | } 542 | 543 | } 544 | 545 | } 546 | --------------------------------------------------------------------------------