@@ -9,161 +9,11 @@ | |||
<url>http://maven.apache.org</url> | |||
<properties> | |||
<swagger2.version>2.6.1</swagger2.version> | |||
<spring.version>4.3.6.RELEASE</spring.version> | |||
<freemarker.version>2.3.25-incubating</freemarker.version> | |||
<jsch.version>0.1.54</jsch.version> | |||
<javax.version>7.0</javax.version> | |||
<commons-lang.version>2.6</commons-lang.version> | |||
<commons-io.version>2.4</commons-io.version> | |||
<slf4j.version>1.7.21</slf4j.version> | |||
<fastjson.version>1.2.20</fastjson.version> | |||
<jackson.version>2.8.6</jackson.version> | |||
<codec.version>1.10</codec.version> | |||
<maven.compiler.source>1.8</maven.compiler.source> | |||
<maven.compiler.target>1.8</maven.compiler.target> | |||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | |||
</properties> | |||
<dependencies> | |||
<dependency> | |||
<groupId>org.freemarker</groupId> | |||
<artifactId>freemarker</artifactId> | |||
<version>${freemarker.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.jcraft</groupId> | |||
<artifactId>jsch</artifactId> | |||
<version>${jsch.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>io.springfox</groupId> | |||
<artifactId>springfox-swagger2</artifactId> | |||
<version>${swagger2.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>io.springfox</groupId> | |||
<artifactId>springfox-swagger-ui</artifactId> | |||
<version>${swagger2.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.springframework</groupId> | |||
<artifactId>spring-webmvc</artifactId> | |||
<version>${spring.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.springframework</groupId> | |||
<artifactId>spring-websocket</artifactId> | |||
<version>${spring.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.springframework</groupId> | |||
<artifactId>spring-context-support</artifactId> | |||
<version>${spring.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.springframework</groupId> | |||
<artifactId>spring-test</artifactId> | |||
<version>${spring.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>javax</groupId> | |||
<artifactId>javaee-api</artifactId> | |||
<version>${javax.version}</version> | |||
<scope>provided</scope> | |||
</dependency> | |||
<dependency> | |||
<groupId>commons-lang</groupId> | |||
<artifactId>commons-lang</artifactId> | |||
<version>${commons-lang.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>commons-io</groupId> | |||
<artifactId>commons-io</artifactId> | |||
<version>${commons-io.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>junit</groupId> | |||
<artifactId>junit</artifactId> | |||
<version>4.12</version> | |||
<scope>test</scope> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.springframework</groupId> | |||
<artifactId>spring-aop</artifactId> | |||
<version>4.3.6.RELEASE</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.aspectj</groupId> | |||
<artifactId>aspectjrt</artifactId> | |||
<version>1.8.10</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.springframework</groupId> | |||
<artifactId>spring-aspects</artifactId> | |||
<version>${spring.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.springframework</groupId> | |||
<artifactId>spring-test</artifactId> | |||
<version>${spring.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>ch.qos.logback</groupId> | |||
<artifactId>logback-classic</artifactId> | |||
<version>1.2.3</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.logback-extensions</groupId> | |||
<artifactId>logback-ext-spring</artifactId> | |||
<version>0.1.4</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.alibaba</groupId> | |||
<artifactId>fastjson</artifactId> | |||
<version>${fastjson.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>commons-codec</groupId> | |||
<artifactId>commons-codec</artifactId> | |||
<version>${codec.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.fasterxml.jackson.core</groupId> | |||
<artifactId>jackson-core</artifactId> | |||
<version>${jackson.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.fasterxml.jackson.core</groupId> | |||
<artifactId>jackson-annotations</artifactId> | |||
<version>${jackson.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.fasterxml.jackson.core</groupId> | |||
<artifactId>jackson-databind</artifactId> | |||
<version>${jackson.version}</version> | |||
</dependency> | |||
</dependencies> | |||
<build> | |||
<plugins> | |||
@@ -176,55 +26,6 @@ | |||
</configuration> | |||
</plugin> | |||
<plugin> | |||
<groupId>org.apache.maven.plugins</groupId> | |||
<artifactId>maven-shade-plugin</artifactId> | |||
<version>3.0.0</version> | |||
<executions> | |||
<execution> | |||
<phase>package</phase> | |||
<goals> | |||
<goal>shade</goal> | |||
</goals> | |||
<configuration> | |||
<dependencyReducedPomLocation>${project.build.directory}/dependency-reduced-pom.xml</dependencyReducedPomLocation> | |||
<transformers> | |||
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer"> | |||
<resource>META-INF/spring.handlers</resource> | |||
</transformer> | |||
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer"> | |||
<resource>META-INF/spring.schemas</resource> | |||
</transformer> | |||
</transformers> | |||
<filters> | |||
<filter> | |||
<artifact>*:*</artifact> | |||
<excludes> | |||
<exclude>META-INF/*.SF</exclude> | |||
<exclude>META-INF/*.DSA</exclude> | |||
<exclude>META-INF/*.RSA</exclude> | |||
</excludes> | |||
</filter> | |||
</filters> | |||
</configuration> | |||
</execution> | |||
</executions> | |||
</plugin> | |||
<plugin> | |||
<groupId>org.apache.tomcat.maven</groupId> | |||
<artifactId>tomcat7-maven-plugin</artifactId> | |||
<version>2.2</version> | |||
<configuration> | |||
<address>0.0.0.0</address> | |||
<port>61020</port> | |||
<path>/</path> | |||
<uriEncoding>UTF-8</uriEncoding> | |||
<finalName>webssh</finalName> | |||
<server>tomcat7</server> | |||
</configuration> | |||
</plugin> | |||
<plugin> | |||
<groupId>org.apache.maven.plugins</groupId> | |||
<artifactId>maven-compiler-plugin</artifactId> | |||
@@ -1,25 +0,0 @@ | |||
package com.educoder.bridge.controller; | |||
import org.springframework.web.bind.annotation.ModelAttribute; | |||
import javax.servlet.http.HttpServletRequest; | |||
import javax.servlet.http.HttpServletResponse; | |||
import javax.servlet.http.HttpSession; | |||
/** | |||
* @author lqk | |||
* @version 0.1 | |||
*/ | |||
public class BaseController { | |||
protected HttpServletRequest request; | |||
protected HttpServletResponse response; | |||
protected HttpSession session; | |||
@ModelAttribute | |||
public void setReqAndRes(HttpServletRequest request, HttpServletResponse response) { | |||
this.request = request; | |||
this.response = response; | |||
this.session = request.getSession(); | |||
} | |||
} |
@@ -1,49 +0,0 @@ | |||
package com.educoder.bridge.controller; | |||
import io.swagger.annotations.Api; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import org.springframework.web.bind.annotation.RequestMapping; | |||
import org.springframework.web.bind.annotation.RequestMethod; | |||
import org.springframework.web.bind.annotation.RequestParam; | |||
import org.springframework.web.bind.annotation.RestController; | |||
import org.springframework.web.servlet.ModelAndView; | |||
/** | |||
* @author guange | |||
* | |||
* @date 2017/08/02 | |||
*/ | |||
@Api(value = "提供webssh连接", hidden = true) | |||
@RestController | |||
public class MainController extends BaseController { | |||
private final static Logger logger = LoggerFactory.getLogger(MainController.class); | |||
// | |||
// @RequestMapping(value={"/"}, method= RequestMethod.GET) | |||
// public ModelAndView index(@RequestParam("host")String host, | |||
// @RequestParam("port")int port, | |||
// @RequestParam("username")String username, | |||
// @RequestParam("password")String password, | |||
// @RequestParam("rows")int rows) { | |||
// logger.debug("/ssh: 接收到连接请求, host: {}, port: {}", host, port); | |||
// ModelAndView mv = new ModelAndView(); | |||
// mv.setViewName("index"); | |||
// mv.addObject("host", host); | |||
// mv.addObject("port", port); | |||
// mv.addObject("username", username); | |||
// mv.addObject("password", password); | |||
// mv.addObject("rows", rows); | |||
// mv.addObject("digest", System.currentTimeMillis()); | |||
// return mv; | |||
// } | |||
@RequestMapping(value={"/"}, method= RequestMethod.GET) | |||
public ModelAndView index() { | |||
ModelAndView mv = new ModelAndView(); | |||
mv.setViewName("index"); | |||
mv.addObject("digest", System.currentTimeMillis()); | |||
return mv; | |||
} | |||
} |
@@ -1,38 +0,0 @@ | |||
package com.educoder.bridge.handler; | |||
import com.educoder.bridge.service.JchService; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.web.socket.CloseStatus; | |||
import org.springframework.web.socket.TextMessage; | |||
import org.springframework.web.socket.WebSocketSession; | |||
import org.springframework.web.socket.handler.TextWebSocketHandler; | |||
public class WebsshHandler extends TextWebSocketHandler { | |||
@Autowired | |||
JchService jchService; | |||
@Override | |||
public void afterConnectionEstablished(WebSocketSession wsSession) throws Exception { | |||
super.afterConnectionEstablished(wsSession); | |||
jchService.add(wsSession); | |||
} | |||
/** | |||
* 重写handleTextMessage方法,用于处理从websocket接收到的信息 | |||
*/ | |||
@Override | |||
protected void handleTextMessage(WebSocketSession wsSession, TextMessage message) throws Exception { | |||
jchService.recv(message.getPayload(), wsSession); | |||
super.handleTextMessage(wsSession, message); | |||
} | |||
@Override | |||
public void afterConnectionClosed(WebSocketSession wsSession, CloseStatus status) throws Exception { | |||
super.afterConnectionClosed(wsSession, status); | |||
jchService.closeByWebSocket(wsSession); | |||
} | |||
} |
@@ -1,41 +0,0 @@ | |||
package com.educoder.bridge.model; | |||
public class SSHInfo { | |||
private String host; | |||
private String port; | |||
private String username; | |||
private String password; | |||
public void setHost(String host) { | |||
this.host = host; | |||
} | |||
public void setPort(String port) { | |||
this.port = port; | |||
} | |||
public void setUsername(String username) { | |||
this.username = username; | |||
} | |||
public void setPassword(String password) { | |||
this.password = password; | |||
} | |||
public String getHost() { | |||
return host; | |||
} | |||
public int getPort() { | |||
return Integer.parseInt(port); | |||
} | |||
public String getUsername() { | |||
return username; | |||
} | |||
public String getPassword() { | |||
return password; | |||
} | |||
} |
@@ -1,45 +0,0 @@ | |||
package com.educoder.bridge.model; | |||
import com.jcraft.jsch.ChannelShell; | |||
import org.springframework.web.socket.WebSocketSession; | |||
import java.io.OutputStream; | |||
public class SSHSession { | |||
private WebSocketSession webSocketSession; | |||
private OutputStream outputStream; | |||
private ChannelShell channel; | |||
private SSHInfo SSHInfo; | |||
public SSHInfo getSSHInfo() { | |||
return SSHInfo; | |||
} | |||
public void setSSHInfo(SSHInfo SSHInfo) { | |||
this.SSHInfo = SSHInfo; | |||
} | |||
public ChannelShell getChannel() { | |||
return channel; | |||
} | |||
public void setChannel(ChannelShell channel) { | |||
this.channel = channel; | |||
} | |||
public WebSocketSession getWebSocketSession() { | |||
return webSocketSession; | |||
} | |||
public void setWebSocketSession(WebSocketSession webSocketSession) { | |||
this.webSocketSession = webSocketSession; | |||
} | |||
public OutputStream getOutputStream() { | |||
return outputStream; | |||
} | |||
public void setOutputStream(OutputStream outputStream) { | |||
this.outputStream = outputStream; | |||
} | |||
} |
@@ -1,261 +0,0 @@ | |||
package com.educoder.bridge.service; | |||
import com.alibaba.fastjson.JSONObject; | |||
import com.educoder.bridge.model.SSHInfo; | |||
import com.educoder.bridge.model.SSHSession; | |||
import com.educoder.bridge.utils.Base64Util; | |||
import com.jcraft.jsch.ChannelShell; | |||
import com.jcraft.jsch.JSch; | |||
import com.jcraft.jsch.Session; | |||
import com.jcraft.jsch.UserInfo; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import org.springframework.stereotype.Service; | |||
import org.springframework.web.socket.TextMessage; | |||
import org.springframework.web.socket.WebSocketSession; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.io.OutputStream; | |||
import java.util.Arrays; | |||
import java.util.List; | |||
import java.util.Optional; | |||
import java.util.concurrent.CopyOnWriteArrayList; | |||
import java.util.concurrent.ExecutorService; | |||
import java.util.concurrent.Executors; | |||
@Service | |||
public class JchService { | |||
private static List<SSHSession> sshSessionQueue = new CopyOnWriteArrayList<>(); | |||
private ExecutorService executorService = Executors.newCachedThreadPool(); | |||
private Logger logger = LoggerFactory.getLogger(getClass()); | |||
com.jcraft.jsch.Logger jschLogger = new com.jcraft.jsch.Logger() { | |||
@Override | |||
public boolean isEnabled(int arg0) { | |||
return true; | |||
} | |||
@Override | |||
public void log(int arg0, String arg1) { | |||
if (logger.isTraceEnabled()) { | |||
logger.trace("JSch Log [Level " + arg0 + "]: " + arg1); | |||
} | |||
} | |||
}; | |||
/** | |||
* 在webSocket连接时,初始化一个ssh连接 | |||
* | |||
* @param webSocketSession webSocket连接 | |||
*/ | |||
public void add(WebSocketSession webSocketSession) { | |||
SSHSession sshSession = new SSHSession(); | |||
sshSession.setWebSocketSession(webSocketSession); | |||
sshSessionQueue.add(sshSession); | |||
} | |||
/** | |||
* 处理客户端发过来的数据 | |||
* @param buffer 数据 | |||
* @param webSocketSession webSocket连接 | |||
*/ | |||
public void recv(String buffer, WebSocketSession webSocketSession) { | |||
SSHSession sshSession = null; | |||
try { | |||
logger.debug("webSocketSessionID: {}, 信息: {}", webSocketSession.getId(), buffer); | |||
JSONObject info = JSONObject.parseObject(buffer); | |||
String tp = info.getString("tp"); | |||
sshSession = findByWebSocketSession(webSocketSession); | |||
//初始化连接 | |||
if ("init".equals(tp)) { | |||
// {"tp":"init","data":{"host":"127.0.0.1","port":"41080","username":"root","password":"123123"}} | |||
SSHInfo sshInfo = info.getObject("data", SSHInfo.class); | |||
sshSession.setSSHInfo(sshInfo); | |||
if (sshSession != null) { | |||
SSHSession finalSSHSession = sshSession; | |||
// 新开一个线程建立连接,连接开启之后以一直监听来自客户端的输入 | |||
executorService.execute(() -> { | |||
connectTossh(finalSSHSession); | |||
}); | |||
} | |||
} else if ("client".equals(tp)) { | |||
String data = info.getString("data"); | |||
// 将网页输入的数据传送给后端服务器 | |||
if (sshSession != null) { | |||
transTossh(sshSession.getOutputStream(), data); | |||
} | |||
} | |||
} catch (Exception e) { | |||
logger.error("转发命令到ssh出错: {}", e); | |||
close(sshSession); | |||
} | |||
} | |||
/** | |||
* 将数据传送给服务端作为SSH的输入 | |||
* | |||
* @param outputStream | |||
* @param data | |||
* @throws IOException | |||
*/ | |||
private void transTossh(OutputStream outputStream, String data) throws IOException { | |||
if (outputStream != null) { | |||
outputStream.write(data.getBytes()); | |||
outputStream.flush(); | |||
} | |||
} | |||
/** | |||
* 连接ssh | |||
* | |||
* @param sshSession ssh连接需要的信息 | |||
*/ | |||
private void connectTossh(SSHSession sshSession){ | |||
Session jschSession = null; | |||
SSHInfo SSHInfo = sshSession.getSSHInfo(); | |||
try { | |||
JSch jsch = new JSch(); | |||
JSch.setLogger(jschLogger); | |||
//启动线程 | |||
java.util.Properties config = new java.util.Properties(); | |||
config.put("StrictHostKeyChecking", "no"); | |||
jschSession = jsch.getSession(SSHInfo.getUsername(), SSHInfo.getHost(), SSHInfo.getPort()); | |||
jschSession.setConfig(config); | |||
jschSession.setPassword(SSHInfo.getPassword()); | |||
jschSession.setUserInfo(new UserInfo() { | |||
@Override | |||
public String getPassphrase() { | |||
return null; | |||
} | |||
@Override | |||
public String getPassword() { | |||
return null; | |||
} | |||
@Override | |||
public boolean promptPassword(String s) { | |||
return false; | |||
} | |||
@Override | |||
public boolean promptPassphrase(String s) { | |||
return false; | |||
} | |||
@Override | |||
public boolean promptYesNo(String s) { | |||
return true; | |||
} // Accept all server keys | |||
@Override | |||
public void showMessage(String s) { | |||
} | |||
}); | |||
jschSession.connect(); | |||
ChannelShell channel = (ChannelShell) jschSession.openChannel("shell"); | |||
channel.setPtyType("xterm"); | |||
channel.connect(); | |||
sshSession.setChannel(channel); | |||
InputStream inputStream = channel.getInputStream(); | |||
sshSession.setOutputStream(channel.getOutputStream()); | |||
sshSession.setSSHInfo(SSHInfo); | |||
logger.debug("主机: {} 连接成功!", SSHInfo.getHost()); | |||
// 循环读取,jsch的输入为服务器执行命令之后的返回数据 | |||
byte[] buf = new byte[1024]; | |||
while (true) { | |||
int length = inputStream.read(buf); | |||
if (length < 0) { | |||
close(sshSession); | |||
throw new Exception("读取出错,数据长度:" + length); | |||
} | |||
sendMsg(sshSession.getWebSocketSession(), Arrays.copyOfRange(buf, 0, length)); | |||
} | |||
} catch (Exception e) { | |||
logger.error("ssh连接出错, e: {}", e); | |||
} finally { | |||
logger.info("连接关闭, {}", SSHInfo.getHost()); | |||
if (jschSession != null) { | |||
jschSession.disconnect(); | |||
} | |||
close(sshSession); | |||
} | |||
} | |||
/** | |||
* 发送数据回websocket | |||
* | |||
* @param webSocketSession webSocket连接 | |||
* @param buffer 数据 | |||
* @throws IOException | |||
*/ | |||
public void sendMsg(WebSocketSession webSocketSession, byte[] buffer) throws IOException { | |||
logger.debug("服务端返回的数据: {}", new String(buffer, "UTF-8")); | |||
webSocketSession.sendMessage(new TextMessage(Base64Util.encodeBytes(buffer))); | |||
} | |||
/** | |||
* 通过webSocket连接在队列中找到对应的SSH连接 | |||
* | |||
* @param webSocketSession webSocket连接 | |||
*/ | |||
public SSHSession findByWebSocketSession(WebSocketSession webSocketSession) { | |||
Optional<SSHSession> optional = sshSessionQueue.stream().filter(webscoketObj -> webscoketObj.getWebSocketSession() == webSocketSession).findFirst(); | |||
if (optional.isPresent()) { | |||
return optional.get(); | |||
} | |||
return null; | |||
} | |||
/** | |||
* 关闭ssh和websocket连接 | |||
* | |||
* @param sshSession ssh连接 | |||
*/ | |||
private void close(SSHSession sshSession) { | |||
if (sshSession != null) { | |||
sshSession.getChannel().disconnect(); | |||
try { | |||
sshSession.getWebSocketSession().close(); | |||
sshSession.getOutputStream().close(); | |||
} catch (IOException e) { | |||
logger.error("连接关闭失败!e: {}", e); | |||
} | |||
sshSessionQueue.remove(sshSession); | |||
} | |||
} | |||
/** | |||
* 通过webSocketSession关闭ssh与webSocket连接 | |||
* | |||
* @param webSocketSession | |||
*/ | |||
public void closeByWebSocket(WebSocketSession webSocketSession) { | |||
close(findByWebSocketSession(webSocketSession)); | |||
} | |||
} |
@@ -1,52 +0,0 @@ | |||
package com.educoder.bridge.utils; | |||
import org.apache.commons.codec.binary.Base64; | |||
import java.nio.charset.StandardCharsets; | |||
/** | |||
* Created by guange on 23/02/2017. | |||
*/ | |||
public class Base64Util { | |||
/** | |||
* base64编码 | |||
* | |||
* @param code | |||
* @return | |||
*/ | |||
public static String encode(String code) { | |||
byte[] encode = Base64.encodeBase64URLSafe(code.getBytes(StandardCharsets.UTF_8)); | |||
return new String(encode, StandardCharsets.UTF_8); | |||
} | |||
public static byte[] encodeBytes(byte[] codes) { | |||
return Base64.encodeBase64(codes); | |||
} | |||
/** | |||
* base64解码 | |||
* | |||
* @param code | |||
* @return | |||
*/ | |||
public static String decode(String code) { | |||
byte[] decode = Base64.decodeBase64(code); | |||
return new String(decode, StandardCharsets.UTF_8); | |||
} | |||
/** | |||
* base64再解码,把原本的非URL safe编码转换为URL safe编码 | |||
* | |||
* @param code | |||
* @return | |||
*/ | |||
public static String reencode(String code) { | |||
String str = decode(code); | |||
str = str.replace("\n", "\r\n"); | |||
return encode(str); | |||
} | |||
} |
@@ -1,42 +0,0 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<beans xmlns="http://www.springframework.org/schema/beans" | |||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||
xmlns:context="http://www.springframework.org/schema/context" | |||
xmlns:websocket="http://www.springframework.org/schema/websocket" | |||
xmlns:aop="http://www.springframework.org/schema/aop" | |||
xsi:schemaLocation="http://www.springframework.org/schema/beans | |||
http://www.springframework.org/schema/beans/spring-beans.xsd | |||
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd | |||
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd | |||
http://www.springframework.org/schema/websocket | |||
http://www.springframework.org/schema/websocket/spring-websocket.xsd"> | |||
<aop:aspectj-autoproxy/> | |||
<context:component-scan base-package="com.educoder.bridge.controller"/> | |||
<context:component-scan base-package="com.educoder.bridge.service"/> | |||
<context:component-scan base-package="com.educoder.bridge.handler"/> | |||
<!-- freemaker配置 --> | |||
<bean id="freemarkerConfig" | |||
class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer"> | |||
<property name="templateLoaderPath" value="/WEB-INF/pages/" /> | |||
<property name="freemarkerSettings"> | |||
<props> | |||
<prop key="template_update_delay">0</prop> | |||
<prop key="default_encoding">UTF-8</prop> | |||
<prop key="number_format">0.##########</prop> | |||
<prop key="datetime_format">yyyy-MM-dd HH:mm:ss</prop> | |||
<prop key="classic_compatible">true</prop> | |||
<prop key="template_exception_handler">ignore</prop> | |||
</props> | |||
</property> | |||
</bean> | |||
<!--注册消息处理器,指定WebsshHandler处理消息,并将/ws映射到其中--> | |||
<websocket:handlers allowed-origins="*"> | |||
<websocket:mapping path="/ws" handler="websshHandler"/> | |||
</websocket:handlers> | |||
<bean id="websshHandler" class="com.educoder.bridge.handler.WebsshHandler"/> | |||
</beans> |
@@ -1,47 +0,0 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<configuration> | |||
<property name="log_path" value="/home/ww/test/tomcat-test/logs/"/> | |||
<!-- 打印在标准控制台 --> | |||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> | |||
<encoder> | |||
<!--格式化输出:%d:时间,%thread:线程名,%-5level:级别从左显示5个字符宽度, | |||
%logger{50}:输出日志的类, 50代表包名加类名的总长度限制, %M 方法名 %L 行号 %msg:日志消息,%n是换行符--> | |||
<pattern>%d{MM-dd HH:mm:ss} [%thread] %-5level %logger{30} %M %L - %msg%n</pattern> | |||
</encoder> | |||
<filter class="ch.qos.logback.classic.filter.ThresholdFilter"> | |||
<level>DEBUG</level> | |||
</filter> | |||
</appender> | |||
<!-- 错误信息 --> | |||
<appender name="ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender"> | |||
<Encoding>UTF-8</Encoding> | |||
<encoder> | |||
<pattern>%d{MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{30} %M - %msg%n%L</pattern> | |||
</encoder> | |||
<filter class="ch.qos.logback.classic.filter.ThresholdFilter"> | |||
<level>ERROR</level> | |||
</filter> | |||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> | |||
<fileNamePattern>${log_path}error.%d{MM-dd}.log</fileNamePattern> | |||
</rollingPolicy> | |||
</appender> | |||
<!-- 屏蔽框架输出 --> | |||
<logger name="org.slf4j" level="ERROR"/> | |||
<logger name="org.springframework" level="ERROR"/> | |||
<logger name="io.swagger" level="ERROR"/> | |||
<logger name="ch.qos.logback" level="OFF"/> | |||
<logger name="springfox.documentation" level="ERROR"/> | |||
<!-- 所有的日志同时应用“STDOUT”和“EROOR”的策略 --> | |||
<root> | |||
<level value="DEBUG"/> | |||
<!--<appender-ref ref="TPM"/>--> | |||
<appender-ref ref="ERROR"/> | |||
<appender-ref ref="STDOUT"/> | |||
</root> | |||
</configuration> |
@@ -1,34 +0,0 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<beans xmlns="http://www.springframework.org/schema/beans" | |||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||
xmlns:context="http://www.springframework.org/schema/context" | |||
xmlns:mvc="http://www.springframework.org/schema/mvc" | |||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"> | |||
<!--指明 controller 所在包,并扫描其中的注解--> | |||
<context:component-scan base-package="com.educoder.bridge.controller"/> | |||
<!-- 静态资源(js、image等)的访问 --> | |||
<mvc:default-servlet-handler/> | |||
<!--ViewResolver 视图解析器--> | |||
<!--用于支持freemarker视图解析--> | |||
<!--视图解释器 --> | |||
<bean id="viewResolver" | |||
class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver"> | |||
<property name="suffix"> | |||
<value>.ftl</value> | |||
</property> | |||
<property name="contentType" value="text/html;charset=UTF-8"></property> | |||
</bean> | |||
<!-- 开启注解 --> | |||
<mvc:annotation-driven/> | |||
<bean class="springfox.documentation.swagger2.configuration.Swagger2DocumentationConfiguration" id="swagger2Config"/> | |||
<mvc:resources location="classpath:/META-INF/resources/" mapping="swagger-ui.html"/> | |||
<mvc:resources location="classpath:/META-INF/resources/webjars/" mapping="/webjars/**"/> | |||
</beans> |
@@ -1,61 +0,0 @@ | |||
<!DOCTYPE html> | |||
<html lang="en"> | |||
<head> | |||
<meta charset="utf-8"> | |||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |||
<title>JWebssh</title> | |||
<link rel='shortcut icon' type='image/x-icon' href='/static/image/favicon.ico'/> | |||
<link rel="stylesheet" href="/static/css/pure-min.css"> | |||
<link href="/static/css/xterm.css" rel="stylesheet" type="text/css"/> | |||
<link rel='stylesheet' href='http://fonts.googleapis.com/css?family=PT+Sans:400,700'> | |||
<link rel="stylesheet" href="/static/css/reset.css"> | |||
<link rel="stylesheet" href="/static/css/supersized.css"> | |||
<link rel="stylesheet" href="/static/css/style.css"> | |||
<!-- HTML5 shim, for IE6-8 support of HTML5 elements --> | |||
<!--[if lt IE 9]> | |||
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script> | |||
<![endif]--> | |||
</head> | |||
<body> | |||
<div class="page-container" id="form" name="form" > | |||
<h1>JWebssh</h1> | |||
<form action="" method="post"> | |||
<fieldset> | |||
<input id="terminalRow" name="terminalRow" type="hidden" value=100> | |||
<div class="pure-item"> | |||
<input id="host" name="host" type="text" placeholder="Host" value=${host}> | |||
</div> | |||
<div class="pure-item"> | |||
<input id="port" name="port" type="text" placeholder="Port" value=${port}> | |||
</div> | |||
<div class="pure-item"> | |||
<input id="username" name="username" type="text" placeholder="Username" value=${username}> | |||
</div> | |||
<div class="pure-item"> | |||
<input id="password" name="password" type="password" placeholder="Password" value=${password}> | |||
</div> | |||
<button type="button" onclick="connect()">Connect</button> | |||
</fieldset> | |||
</form> | |||
</div> | |||
<div id="term" align="center"></div> | |||
<!-- Javascript --> | |||
<script src="/static/js/jquerymin.js"></script> | |||
<script src="/static/js/supersized.3.2.7.min.js"></script> | |||
<script src="/static/js/supersized-init.js"></script> | |||
<script src="/static/js/base64.js"></script> | |||
<script src="/static/js/jquerymin.js"></script> | |||
<script src="/static/js/xterm.js"></script> | |||
<script src="/static/js/ws.js"></script> | |||
<script src="/static/js/formvalid.js"></script> | |||
<script src="/static/js/main.js?${digest}"></script> | |||
</body> | |||
</html> |
@@ -1,63 +0,0 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" | |||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" | |||
version="3.1"> | |||
<display-name>educoder bridge</display-name> | |||
<!-- Spring 上下文参数 --> | |||
<context-param> | |||
<param-name>contextConfigLocation</param-name> | |||
<param-value>classpath:applicationContext.xml</param-value> | |||
</context-param> | |||
<listener> | |||
<listener-class> | |||
org.springframework.web.context.ContextLoaderListener | |||
</listener-class> | |||
</listener> | |||
<!-- logback --> | |||
<context-param> | |||
<param-name>logbackConfigLocation</param-name> | |||
<param-value>classpath:logback.xml</param-value> | |||
</context-param> | |||
<listener> | |||
<listener-class>ch.qos.logback.ext.spring.web.LogbackConfigListener</listener-class> | |||
</listener> | |||
<servlet> | |||
<servlet-name>mvc-dispatcher</servlet-name> | |||
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> | |||
<load-on-startup>1</load-on-startup> | |||
</servlet> | |||
<servlet-mapping> | |||
<servlet-name>mvc-dispatcher</servlet-name> | |||
<url-pattern>/</url-pattern> | |||
</servlet-mapping> | |||
<filter> | |||
<filter-name>encodingFilter</filter-name> | |||
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> | |||
<init-param> | |||
<param-name>encoding</param-name> | |||
<param-value>UTF-8</param-value> | |||
</init-param> | |||
<init-param> | |||
<param-name>forceEncoding</param-name> | |||
<param-value>true</param-value> | |||
</init-param> | |||
</filter> | |||
<filter-mapping> | |||
<filter-name>encodingFilter</filter-name> | |||
<url-pattern>/*</url-pattern> | |||
</filter-mapping> | |||
<welcome-file-list> | |||
<welcome-file></welcome-file> | |||
</welcome-file-list> | |||
</web-app> |
@@ -1,2 +0,0 @@ | |||
<!--保留以防后续看swagger UI界面需要--> | |||
<!--<meta http-equiv="refresh" content="0; url=/swagger-ui.html" />--> |
@@ -1,53 +0,0 @@ | |||
.aside { | |||
text-align: center; | |||
background: #1f8dd6; | |||
height: 100px; | |||
color: #fff; | |||
vertical-align: middle; | |||
line-height: 100px; | |||
font-size: 30px | |||
} | |||
#main { | |||
margin-top: 20px; | |||
} | |||
#ratio-group { | |||
float: right; | |||
} | |||
.pure-item { | |||
margin: 0 auto 10px; | |||
width: 300px; | |||
position: relative; | |||
} | |||
.pure-radio { | |||
margin-left: 10px; | |||
} | |||
.pure-item:after { | |||
content: ""; | |||
display: table; | |||
clear: both; | |||
} | |||
.pure-item label { | |||
float: left; | |||
line-height: 34px; | |||
} | |||
.pure-item input { | |||
float: right; | |||
} | |||
.terminal { | |||
float: none; | |||
border: #000 solid 5px; | |||
font-family: "Monaco", "DejaVu Sans Mono", "Liberation Mono", monospace; | |||
font-size: 11px; | |||
color: #f0f0f0; | |||
width: 600px; | |||
background: #000; | |||
box-shadow: rgba(0, 0, 0, 0.8) 2px 2px 20px; | |||
} |
@@ -1,44 +0,0 @@ | |||
/* ------- This is the CSS Reset ------- */ | |||
html, body, div, span, applet, object, iframe, | |||
h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, | |||
abbr, acronym, address, big, cite, code, del, | |||
dfn, em, img, ins, kbd, q, s, samp, small, | |||
strike, strong, sub, sup, tt, var, u, i, center, | |||
dl, dt, dd, ol, ul, li, fieldset, form, label, | |||
legend, table, caption, tbody, tfoot, thead, tr, | |||
th, td, article, aside, canvas, details, embed, | |||
figure, figcaption, footer, header, hgroup, menu, | |||
nav, output, ruby, section, summary, time, mark, audio, video { | |||
margin: 0; | |||
padding: 0; | |||
border: 0; | |||
font-size: 100%; | |||
font: inherit; | |||
vertical-align: baseline; | |||
} | |||
/* ------- HTML5 display-role reset for older browsers ------- */ | |||
article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { | |||
display: block; | |||
} | |||
body { | |||
line-height: 1; | |||
} | |||
ol, ul { | |||
list-style: none; | |||
} | |||
blockquote, q { | |||
quotes: none; | |||
} | |||
blockquote:before, blockquote:after, q:before, q:after { | |||
content: ''; | |||
content: none; | |||
} | |||
table { | |||
border-collapse: collapse; | |||
border-spacing: 0; | |||
} | |||
@@ -1,187 +0,0 @@ | |||
/* | |||
* | |||
* Template Name: Fullscreen Login | |||
* Description: Login Template with Fullscreen Background Slideshow | |||
* Author: Anli Zaimi | |||
* Author URI: http://azmind.com | |||
* | |||
*/ | |||
body { | |||
background: #f8f8f8; | |||
font-family: 'PT Sans', Helvetica, Arial, sans-serif; | |||
text-align: center; | |||
color: #fff; | |||
} | |||
.page-container { | |||
margin: 120px auto 0 auto; | |||
} | |||
h1 { | |||
font-size: 30px; | |||
font-weight: 700; | |||
text-shadow: 0 1px 4px rgba(0,0,0,.2); | |||
} | |||
form { | |||
position: relative; | |||
width: 305px; | |||
margin: 15px auto 0 auto; | |||
text-align: center; | |||
} | |||
input { | |||
width: 270px; | |||
height: 42px; | |||
margin-top: 25px; | |||
padding: 0 15px; | |||
background: #2d2d2d; /* browsers that don't support rgba */ | |||
background: rgba(45,45,45,.15); | |||
-moz-border-radius: 6px; | |||
-webkit-border-radius: 6px; | |||
border-radius: 6px; | |||
border: 1px solid #3d3d3d; /* browsers that don't support rgba */ | |||
border: 1px solid rgba(255,255,255,.15); | |||
-moz-box-shadow: 0 2px 3px 0 rgba(0,0,0,.1) inset; | |||
-webkit-box-shadow: 0 2px 3px 0 rgba(0,0,0,.1) inset; | |||
box-shadow: 0 2px 3px 0 rgba(0,0,0,.1) inset; | |||
font-family: 'PT Sans', Helvetica, Arial, sans-serif; | |||
font-size: 14px; | |||
color: #fff; | |||
text-shadow: 0 1px 2px rgba(0,0,0,.1); | |||
-o-transition: all .2s; | |||
-moz-transition: all .2s; | |||
-webkit-transition: all .2s; | |||
-ms-transition: all .2s; | |||
} | |||
input:-moz-placeholder { color: #fff; } | |||
input:-ms-input-placeholder { color: #fff; } | |||
input::-webkit-input-placeholder { color: #fff; } | |||
input:focus { | |||
outline: none; | |||
-moz-box-shadow: | |||
0 2px 3px 0 rgba(0,0,0,.1) inset, | |||
0 2px 7px 0 rgba(0,0,0,.2); | |||
-webkit-box-shadow: | |||
0 2px 3px 0 rgba(0,0,0,.1) inset, | |||
0 2px 7px 0 rgba(0,0,0,.2); | |||
box-shadow: | |||
0 2px 3px 0 rgba(0,0,0,.1) inset, | |||
0 2px 7px 0 rgba(0,0,0,.2); | |||
} | |||
button { | |||
cursor: pointer; | |||
width: 300px; | |||
height: 44px; | |||
margin-top: 25px; | |||
padding: 0; | |||
background: #ef4300; | |||
-moz-border-radius: 6px; | |||
-webkit-border-radius: 6px; | |||
border-radius: 6px; | |||
border: 1px solid #ff730e; | |||
-moz-box-shadow: | |||
0 15px 30px 0 rgba(255,255,255,.25) inset, | |||
0 2px 7px 0 rgba(0,0,0,.2); | |||
-webkit-box-shadow: | |||
0 15px 30px 0 rgba(255,255,255,.25) inset, | |||
0 2px 7px 0 rgba(0,0,0,.2); | |||
box-shadow: | |||
0 15px 30px 0 rgba(255,255,255,.25) inset, | |||
0 2px 7px 0 rgba(0,0,0,.2); | |||
font-family: 'PT Sans', Helvetica, Arial, sans-serif; | |||
font-size: 14px; | |||
font-weight: 700; | |||
color: #fff; | |||
text-shadow: 0 1px 2px rgba(0,0,0,.1); | |||
-o-transition: all .2s; | |||
-moz-transition: all .2s; | |||
-webkit-transition: all .2s; | |||
-ms-transition: all .2s; | |||
} | |||
button:hover { | |||
-moz-box-shadow: | |||
0 15px 30px 0 rgba(255,255,255,.15) inset, | |||
0 2px 7px 0 rgba(0,0,0,.2); | |||
-webkit-box-shadow: | |||
0 15px 30px 0 rgba(255,255,255,.15) inset, | |||
0 2px 7px 0 rgba(0,0,0,.2); | |||
box-shadow: | |||
0 15px 30px 0 rgba(255,255,255,.15) inset, | |||
0 2px 7px 0 rgba(0,0,0,.2); | |||
} | |||
button:active { | |||
-moz-box-shadow: | |||
0 15px 30px 0 rgba(255,255,255,.15) inset, | |||
0 2px 7px 0 rgba(0,0,0,.2); | |||
-webkit-box-shadow: | |||
0 15px 30px 0 rgba(255,255,255,.15) inset, | |||
0 2px 7px 0 rgba(0,0,0,.2); | |||
box-shadow: | |||
0 5px 8px 0 rgba(0,0,0,.1) inset, | |||
0 1px 4px 0 rgba(0,0,0,.1); | |||
border: 0px solid #ef4300; | |||
} | |||
.error { | |||
display: none; | |||
position: absolute; | |||
top: 27px; | |||
right: -55px; | |||
width: 40px; | |||
height: 40px; | |||
background: #2d2d2d; /* browsers that don't support rgba */ | |||
background: rgba(45,45,45,.25); | |||
-moz-border-radius: 8px; | |||
-webkit-border-radius: 8px; | |||
border-radius: 8px; | |||
} | |||
.error span { | |||
display: inline-block; | |||
margin-left: 2px; | |||
font-size: 40px; | |||
font-weight: 700; | |||
line-height: 40px; | |||
text-shadow: 0 1px 2px rgba(0,0,0,.1); | |||
-o-transform: rotate(45deg); | |||
-moz-transform: rotate(45deg); | |||
-webkit-transform: rotate(45deg); | |||
-ms-transform: rotate(45deg); | |||
} | |||
.connect { | |||
width: 305px; | |||
margin: 35px auto 0 auto; | |||
font-size: 18px; | |||
font-weight: 700; | |||
text-shadow: 0 1px 3px rgba(0,0,0,.2); | |||
} | |||
.connect a { | |||
display: inline-block; | |||
width: 32px; | |||
height: 35px; | |||
margin-top: 15px; | |||
-o-transition: all .2s; | |||
-moz-transition: all .2s; | |||
-webkit-transition: all .2s; | |||
-ms-transition: all .2s; | |||
} | |||
.connect a.facebook { background: url(../image/facebook.png) center center no-repeat; } | |||
.connect a.twitter { background: url(../image/twitter.png) center center no-repeat; } | |||
.connect a:hover { background-position: center bottom; } | |||
@@ -1,34 +0,0 @@ | |||
/* | |||
Supersized - Fullscreen Slideshow jQuery Plugin | |||
Version : 3.2.7 | |||
Site : www.buildinternet.com/project/supersized | |||
Author : Sam Dunn | |||
Company : One Mighty Roar (www.onemightyroar.com) | |||
License : MIT License / GPL License | |||
*/ | |||
* { margin:0; padding:0; } | |||
body { background:#111; height:100%; } | |||
img { border:none; } | |||
#supersized-loader { position:absolute; top:50%; left:50%; z-index:0; width:60px; height:60px; margin:-30px 0 0 -30px; text-indent:-999em; background:url(../image/progress.gif) no-repeat center center;} | |||
#supersized { display:block; position:fixed; left:0; top:0; overflow:hidden; z-index:-999; height:100%; width:100%; } | |||
#supersized img { width:auto; height:auto; position:relative; display:none; outline:none; border:none; } | |||
#supersized.speed img { -ms-interpolation-mode:nearest-neighbor; image-rendering: -moz-crisp-edges; } /*Speed*/ | |||
#supersized.quality img { -ms-interpolation-mode:bicubic; image-rendering: optimizeQuality; } /*Quality*/ | |||
#supersized li { display:block; list-style:none; z-index:-30; position:fixed; overflow:hidden; top:0; left:0; width:100%; height:100%; background:#111; } | |||
#supersized a { width:100%; height:100%; display:block; } | |||
#supersized li.prevslide { z-index:-20; } | |||
#supersized li.activeslide { z-index:-10; } | |||
#supersized li.image-loading { background:#111 url(../image/progress.gif) no-repeat center center; width:100%; height:100%; } | |||
#supersized li.image-loading img{ visibility:hidden; } | |||
#supersized li.prevslide img, #supersized li.activeslide img{ display:inline; } | |||
#supersized img { max-width: none !important } | |||
@@ -1,25 +0,0 @@ | |||
.tooltip{ | |||
position: absolute; | |||
max-width: 300px; | |||
top: 3px; | |||
left: 105%; | |||
padding: 8px 10px; | |||
border-radius: 5px; | |||
color: #fff; | |||
background: #000000; | |||
box-shadow: 0 2px 2px 0 #7F7C7C; | |||
white-space: nowrap; | |||
} | |||
.tooltip:after{ | |||
content: ''; | |||
position: absolute; | |||
top: 35%; | |||
right:100%; | |||
margin-left: 10px; | |||
width: 0; | |||
height: 0; | |||
border: 5px solid transparent; | |||
border-right: 7px #000; | |||
border-left-width: 7px; | |||
} | |||
@@ -1,103 +0,0 @@ | |||
function Base64() { | |||
// private property | |||
_keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; | |||
// public method for encoding | |||
this.encode = function (input) { | |||
var output = ""; | |||
var chr1, chr2, chr3, enc1, enc2, enc3, enc4; | |||
var i = 0; | |||
input = _utf8_encode(input); | |||
while (i < input.length) { | |||
chr1 = input.charCodeAt(i++); | |||
chr2 = input.charCodeAt(i++); | |||
chr3 = input.charCodeAt(i++); | |||
enc1 = chr1 >> 2; | |||
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); | |||
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); | |||
enc4 = chr3 & 63; | |||
if (isNaN(chr2)) { | |||
enc3 = enc4 = 64; | |||
} else if (isNaN(chr3)) { | |||
enc4 = 64; | |||
} | |||
output = output + | |||
_keyStr.charAt(enc1) + _keyStr.charAt(enc2) + | |||
_keyStr.charAt(enc3) + _keyStr.charAt(enc4); | |||
} | |||
return output; | |||
} | |||
// public method for decoding | |||
this.decode = function (input) { | |||
var output = ""; | |||
var chr1, chr2, chr3; | |||
var enc1, enc2, enc3, enc4; | |||
var i = 0; | |||
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); | |||
while (i < input.length) { | |||
enc1 = _keyStr.indexOf(input.charAt(i++)); | |||
enc2 = _keyStr.indexOf(input.charAt(i++)); | |||
enc3 = _keyStr.indexOf(input.charAt(i++)); | |||
enc4 = _keyStr.indexOf(input.charAt(i++)); | |||
chr1 = (enc1 << 2) | (enc2 >> 4); | |||
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); | |||
chr3 = ((enc3 & 3) << 6) | enc4; | |||
output = output + String.fromCharCode(chr1); | |||
if (enc3 != 64) { | |||
output = output + String.fromCharCode(chr2); | |||
} | |||
if (enc4 != 64) { | |||
output = output + String.fromCharCode(chr3); | |||
} | |||
} | |||
output = _utf8_decode(output); | |||
return output; | |||
} | |||
// private method for UTF-8 encoding | |||
_utf8_encode = function (string) { | |||
string = string.replace(/\r\n/g,"\n"); | |||
var utftext = ""; | |||
for (var n = 0; n < string.length; n++) { | |||
var c = string.charCodeAt(n); | |||
if (c < 128) { | |||
utftext += String.fromCharCode(c); | |||
} else if((c > 127) && (c < 2048)) { | |||
utftext += String.fromCharCode((c >> 6) | 192); | |||
utftext += String.fromCharCode((c & 63) | 128); | |||
} else { | |||
utftext += String.fromCharCode((c >> 12) | 224); | |||
utftext += String.fromCharCode(((c >> 6) & 63) | 128); | |||
utftext += String.fromCharCode((c & 63) | 128); | |||
} | |||
} | |||
return utftext; | |||
} | |||
// private method for UTF-8 decoding | |||
_utf8_decode = function (utftext) { | |||
var string = ""; | |||
var i = 0; | |||
var c = c1 = c2 = 0; | |||
while ( i < utftext.length ) { | |||
c = utftext.charCodeAt(i); | |||
if (c < 128) { | |||
string += String.fromCharCode(c); | |||
i++; | |||
} else if((c > 191) && (c < 224)) { | |||
c2 = utftext.charCodeAt(i+1); | |||
string += String.fromCharCode(((c & 31) << 6) | (c2 & 63)); | |||
i += 2; | |||
} else { | |||
c2 = utftext.charCodeAt(i+1); | |||
c3 = utftext.charCodeAt(i+2); | |||
string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); | |||
i += 3; | |||
} | |||
} | |||
return string; | |||
} | |||
} |
@@ -1,234 +0,0 @@ | |||
/* | |||
Jquery | |||
janchie 2010.1 | |||
1.02版 | |||
*/ | |||
var validResult = {}; | |||
var errorMsg = {}; | |||
(function ($) { | |||
$.fn.extend({ | |||
valid: function () { | |||
if (!$(this).is("form")) return; | |||
var items = $.isArray(arguments[0]) ? arguments[0] : [], | |||
isBindSubmit = typeof arguments[1] === "boolean" ? arguments[1] : true, | |||
isAlert = typeof arguments[2] === "boolean" ? arguments[2] : false, | |||
rule = { | |||
"eng": /^[A-Za-z]+$/, | |||
"chn": /^[\u0391-\uFFE5]+$/, | |||
"mail": /\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/, | |||
"url": /^http[s]?:\/\/[A-Za-z0-9]+\.[A-Za-z0-9]+[\/=\?%\-&_~`@[\]\':+!]*([^<>\"\"])*$/, | |||
"currency": /^\d+(\.\d+)?$/, | |||
"number": /^\d+$/, | |||
"int": /^[0-9]{1,30}$/, | |||
"double": /^[-\+]?\d+(\.\d+)?$/, | |||
"username": /^[a-zA-Z]{1}([a-zA-Z0-9]|[._]){3,19}$/, | |||
"password": /^[\w\W]{6,20}$/, | |||
"safe": />|<|,|\[|\]|\{|\}|\?|\/|\+|=|\||\'|\\|\"|:|;|\~|\!|\@|\#|\*|\$|\%|\^|\&|\(|\)|`/i, | |||
"dbc": /[a-zA-Z0-9!@#¥%^&*()_+{}[]|:"';.,/?<>`~ ]/, | |||
"qq": /[1-9][0-9]{4,}/, | |||
"date": /^((((1[6-9]|[2-9]\d)\d{2})-(0?[13578]|1[02])-(0?[1-9]|[12]\d|3[01]))|(((1[6-9]|[2-9]\d)\d{2})-(0?[13456789]|1[012])-(0?[1-9]|[12]\d|30))|(((1[6-9]|[2-9]\d)\d{2})-0?2-(0?[1-9]|1\d|2[0-8]))|(((1[6-9]|[2-9]\d)(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00))-0?2-29-))$/, | |||
"year": /^(19|20)[0-9]{2}$/, | |||
"month": /^(0?[1-9]|1[0-2])$/, | |||
"day": /^((0?[1-9])|((1|2)[0-9])|30|31)$/, | |||
"hour": /^((0?[1-9])|((1|2)[0-3]))$/, | |||
"minute": /^((0?[1-9])|((1|5)[0-9]))$/, | |||
"second": /^((0?[1-9])|((1|5)[0-9]))$/, | |||
"mobile": /^((\(\d{2,3}\))|(\d{3}\-))?13\d{9}$/, | |||
"phone": /^[+]{0,1}(\d){1,3}[ ]?([-]?((\d)|[ ]){1,12})+$/, | |||
"zipcode": /^[1-9]\d{5}$/, | |||
"IDcard": /^((1[1-5])|(2[1-3])|(3[1-7])|(4[1-6])|(5[0-4])|(6[1-5])|71|(8[12])|91)\d{4}((19\d{2}(0[13-9]|1[012])(0[1-9]|[12]\d|30))|(19\d{2}(0[13578]|1[02])31)|(19\d{2}02(0[1-9]|1\d|2[0-8]))|(19([13579][26]|[2468][048]|0[48])0229))\d{3}(\d|X|x)?$/, | |||
"ip": /^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/, | |||
"file": /^[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z0-9]+$/, | |||
"image": /.+\.(jpg|gif|png|bmp)$/i, | |||
"word": /.+\.(doc|rtf|pdf)$/i, | |||
"port": function (port) { | |||
return (!isNaN(port) && port > 0 && port < 65536) ? true : false; | |||
}, | |||
"eq": function (arg1, arg2) { | |||
return arg1 == arg2 ? true : false; | |||
}, | |||
"gt": function (arg1, arg2) { | |||
return arg1 > arg2 ? true : false; | |||
}, | |||
"gte": function (arg1, arg2) { | |||
return arg1 >= arg2 ? true : false; | |||
}, | |||
"lt": function (arg1, arg2) { | |||
return arg1 < arg2 ? true : false; | |||
}, | |||
"lte": function (arg1, arg2) { | |||
return arg1 <= arg2 ? true : false; | |||
} | |||
}, | |||
msgSuffix = { | |||
"eng": "only english welcomed", | |||
"chn": "only chinese welcomed", | |||
"mail": "invalid email format", | |||
"url": "invalid url format", | |||
"currency": "invalid number format", | |||
"number": "only number welcomed", | |||
"int": "only integer welcomed", | |||
"double": "only float welcomed", | |||
"username": "invalid username format,4-20 characters", | |||
"password": "warning, you'd better use 6-20 characters", | |||
"safe": "forbidden special characters", | |||
"dbc": "forbidden full width characters", | |||
"qq": "invalid qq format", | |||
"date": "invalid date format", | |||
"year": "invalid year format", | |||
"month": "invalid month format", | |||
"day": "invalid day format", | |||
"hour": "invalid hour format", | |||
"minute": "invalid minute format", | |||
"second": "invalid second format", | |||
"mobile": "invalid mobile format", | |||
"phone": "invalid phone format", | |||
"zipcode": "invalid zipcode format", | |||
"IDcard": "invalid identity format", | |||
"ip": "invalid ip format", | |||
"port": "invalid port format", | |||
"file": "invalid file format", | |||
"image": "invalid image format", | |||
"word": "invalid word file format", | |||
"eq": "not equal", | |||
"gt": "no greater than", | |||
"gte": "no greater than or equal", | |||
"lt": "no smaller than", | |||
"lte": "no smaller than or equal" | |||
}, | |||
msg = "", formObj = $(this), checkRet = true, isAll, | |||
tipname = function (namestr) { | |||
return "tip_" + namestr.replace(/([a-zA-Z0-9])/g, "-$1"); | |||
}, | |||
typeTest = function () { | |||
var result = true, args = arguments; | |||
if (rule.hasOwnProperty(args[0])) { | |||
var t = rule[args[0]], v = args[1]; | |||
result = args.length > 2 ? t.apply(arguments, [].slice.call(args, 1)) : ($.isFunction(t) ? t(v) : t.test(v)); | |||
} | |||
return result; | |||
}, | |||
showError = function (fieldObj, filedName, warnInfo) { | |||
checkRet = false; | |||
var tipObj = $("#" + tipname(filedName)); | |||
if (tipObj.length > 0) tipObj.remove(); | |||
var tipPosition = fieldObj.next().length > 0 ? fieldObj.nextAll().eq(this.length - 1) : fieldObj.eq(this.length - 1); | |||
//tipPosition.after("<span class='tooltip' id='" + tipname(filedName) + "'> " + warnInfo + " </span>"); | |||
validResult[filedName] = false; | |||
errorMsg[filedName] = warnInfo; | |||
if (isAlert && isAll) msg = warnInfo; | |||
}, | |||
showRight = function (fieldObj, filedName) { | |||
var tipObj = $("#" + tipname(filedName)); | |||
if (tipObj.length > 0) tipObj.remove(); | |||
var tipPosition = fieldObj.next().length > 0 ? fieldObj.nextAll().eq(this.length - 1) : fieldObj.eq(this.length - 1); | |||
//tipPosition.after("<span class='tooltip' id='" + tipname(filedName) + "'>correct</span>"); | |||
validResult[filedName] = true; | |||
}, | |||
findTo = function (objName) { | |||
var find; | |||
$.each(items, function () { | |||
if (this.name == objName && this.simple) { | |||
find = this.simple; | |||
return false; | |||
} | |||
}); | |||
if (!find) find = $("[name='" + objName + "']")[0].name; | |||
return find; | |||
}, | |||
fieldCheck = function (item) { | |||
var i = item, field = $("[name='" + i.name + "']", formObj[0]); | |||
if (!field[0]) return; | |||
var warnMsg, fv = $.trim(field.val()), isRq = typeof i.require === "boolean" ? i.require : true; | |||
if (isRq && ((field.is(":radio") || field.is(":checkbox")) && !field.is(":checked"))) { | |||
warnMsg = i.message || "choice needed"; | |||
showError(field, i.name, warnMsg); | |||
} else if (isRq && fv == "") { | |||
warnMsg = i.message || ( field.is("select") ? "choice needed" : "not none" ); | |||
showError(field, i.name, warnMsg); | |||
} else if (fv != "") { | |||
if (i.min || i.max) { | |||
var len = fv.length, min = i.min || 0, max = i.max; | |||
warnMsg = i.message || (max ? "range" + min + "~" + max + "" : "min length" + min); | |||
if ((max && (len > max || len < min)) || (!max && len < min)) { | |||
showError(field, i.name, warnMsg); | |||
return; | |||
} | |||
} | |||
if (i.type) { | |||
var matchVal = i.to ? $.trim($("[name='" + i.to + "']").val()) : i.value; | |||
var matchRet = matchVal ? typeTest(i.type, fv, matchVal) : typeTest(i.type, fv); | |||
warnMsg = i.message || msgSuffix[i.type]; | |||
if (matchVal) warnMsg += (i.to ? findTo(i.to) + "value" : i.value); | |||
if (!matchRet) showError(field, i.name, warnMsg); | |||
else showRight(field, i.name); | |||
} else { | |||
showRight(field, i.name); | |||
} | |||
} else if (isRq) { | |||
showRight(field, i.name); | |||
} | |||
}, | |||
validate = function () { | |||
$.each(items, function () { | |||
isAll = true; | |||
fieldCheck(this); | |||
}); | |||
if (isAlert && msg != "") { | |||
alert(msg); | |||
msg = ""; | |||
} | |||
return checkRet; | |||
}; | |||
$.each(items, function () { | |||
var field = $("[name='" + this.name + "']", formObj[0]); | |||
if (field.is(":hidden")) return; | |||
var obj = this, toCheck = function () { | |||
isAll = false; | |||
fieldCheck(obj); | |||
}; | |||
if (field.is(":file") || field.is("select")) { | |||
field.change(toCheck); | |||
} else { | |||
field.blur(toCheck); | |||
} | |||
}); | |||
if (isBindSubmit) { | |||
$(this).submit(validate); | |||
} else { | |||
return validate(); | |||
} | |||
} | |||
}); | |||
})(jQuery); |
@@ -1,124 +0,0 @@ | |||
function openTerminal(options) { | |||
//var CONNECT_TIME = 0; // 请求连接次数 | |||
$("#form").hide(); | |||
Rows = parseInt(options.Rows); | |||
var client = new WSSHClient(); | |||
var base64 = new Base64(); | |||
var term = new Terminal({cols: 80, rows: Rows, screenKeys: true, useStyle: true}); | |||
// 发送客户端数据 | |||
term.on('data', function (data) { | |||
console.log("xterm data: "); | |||
console.log(data); | |||
client.sendClientData(data); | |||
}); | |||
term.open(); | |||
$('.terminal').detach().appendTo('#term'); | |||
$("#term").show(); | |||
term.write("Connecting..."); | |||
console.debug(options); | |||
//var interTime = setInterval(client_connect, 1000) | |||
setTimeout(client_connect, 3000); | |||
var intervalId = null; | |||
function client_connect() { | |||
// var TIMEINIT = 0; // 初始化时间 | |||
// var TIMEOUT = 60 * 15; // 超时时间 | |||
var CONNECTED = false; // 是否连接成功过 | |||
console.log("连接中...."); | |||
console.log(options); | |||
client.connect({ | |||
onError: function (error) { | |||
term.write('Error: ' + error + '\r\n'); | |||
console.log('error happened'); | |||
}, | |||
onConnect: function () { | |||
console.log('connection established'); | |||
// 连接上之后发送初始化数据 | |||
client.sendInitData(options); | |||
term.focus(); | |||
}, | |||
onClose: function () { | |||
console.log("连接关闭"); | |||
term.write("\r\nconnection closed"); | |||
if (CONNECTED) { | |||
console.log('connection reset by peer'); | |||
$('term').hide(); | |||
} | |||
}, | |||
// 当收到服务端返回的数据 | |||
onData: function (data) { | |||
if (!CONNECTED) { | |||
console.log("first connected."); | |||
term.write("\r"); //换行 | |||
term.focus(); //焦点移动到框上 | |||
} | |||
/*if(interTime){ | |||
clearInterval(interTime); | |||
}*/ | |||
CONNECTED = true; | |||
data = base64.decode(data); | |||
/* TIMEINIT = 0;*/ | |||
term.write(data); | |||
console.log('get data:' + data); | |||
} | |||
}) | |||
} | |||
} | |||
var charWidth = 6.2; | |||
var charHeight = 15.2; | |||
/** | |||
* for full screen | |||
* @returns {{w: number, h: number}} | |||
*/ | |||
function getTerminalSize() { | |||
var width = window.innerWidth; | |||
var height = window.innerHeight; | |||
return { | |||
w: Math.floor(width / charWidth), | |||
h: Math.floor(height / charHeight) | |||
}; | |||
} | |||
function store(options) { | |||
window.localStorage.host = options.host | |||
window.localStorage.port = options.port | |||
window.localStorage.username = options.username | |||
window.localStorage.ispwd = options.ispwd | |||
window.localStorage.password = options.password | |||
} | |||
function check() { | |||
return validResult["host"] && validResult["port"] && validResult["username"]; | |||
} | |||
function connect() { | |||
var remember = $("#remember").is(":checked") | |||
var options = { | |||
host: $("#host").val(), | |||
port: $("#port").val(), | |||
username: $("#username").val(), | |||
password: $("#password").val(), | |||
Rows: $("#terminalRow").val(), | |||
} | |||
if (remember) { | |||
store(options) | |||
} | |||
if (true) { | |||
openTerminal(options) | |||
} else { | |||
for (var key in validResult) { | |||
if (!validResult[key]) { | |||
alert(errorMsg[key]); | |||
break; | |||
} | |||
} | |||
} | |||
} |
@@ -1,30 +0,0 @@ | |||
jQuery(function($){ | |||
$.supersized({ | |||
// Functionality | |||
slide_interval : 4000, // Length between transitions | |||
transition : 1, // 0-None, 1-Fade, 2-Slide Top, 3-Slide Right, 4-Slide Bottom, 5-Slide Left, 6-Carousel Right, 7-Carousel Left | |||
transition_speed : 1000, // Speed of transition | |||
performance : 1, // 0-Normal, 1-Hybrid speed/quality, 2-Optimizes image quality, 3-Optimizes transition speed // (Only works for Firefox/IE, not Webkit) | |||
// Size & Position | |||
min_width : 0, // Min width allowed (in pixels) | |||
min_height : 0, // Min height allowed (in pixels) | |||
vertical_center : 1, // Vertically center background | |||
horizontal_center : 1, // Horizontally center background | |||
fit_always : 0, // Image will never exceed browser width or height (Ignores min. dimensions) | |||
fit_portrait : 1, // Portrait images will not exceed browser height | |||
fit_landscape : 0, // Landscape images will not exceed browser width | |||
// Components | |||
slide_links : 'blank', // Individual links for each slide (Options: false, 'num', 'name', 'blank') | |||
slides : [ // Slideshow Images | |||
{image : '/static/image/backgrounds/1.jpg'}, | |||
{image : '/static/image/backgrounds/2.jpg'}, | |||
{image : '/static/image/backgrounds/3.jpg'} | |||
] | |||
}); | |||
}); |
@@ -1,67 +0,0 @@ | |||
function WSSHClient() { | |||
}; | |||
WSSHClient.prototype._generateEndpoint = function () { | |||
if (window.location.protocol == 'https:') { | |||
var protocol = 'wss://'; | |||
} else { | |||
var protocol = 'ws://'; | |||
} | |||
var endpoint = protocol + window.location.host + '/ws'; | |||
return endpoint; | |||
}; | |||
WSSHClient.prototype.connect = function (options) { | |||
var endpoint = this._generateEndpoint(); | |||
if (window.WebSocket) { | |||
this._connection = new WebSocket(endpoint); | |||
} | |||
else if (window.MozWebSocket) { | |||
this._connection = MozWebSocket(endpoint); | |||
} | |||
else { | |||
options.onError('WebSocket Not Supported'); | |||
return; | |||
} | |||
this._connection.onopen = function () { | |||
options.onConnect(); | |||
}; | |||
this._connection.onmessage = function (evt) { | |||
var data = evt.data.toString() | |||
options.onData(data); | |||
}; | |||
this._connection.onclose = function (evt) { | |||
options.onClose(); | |||
}; | |||
}; | |||
WSSHClient.prototype.close = function () { | |||
this._connection.close(); | |||
}; | |||
WSSHClient.prototype.send = function (data) { | |||
this._connection.send(JSON.stringify(data)); | |||
}; | |||
WSSHClient.prototype.sendInitData = function (options) { | |||
var data = { | |||
hostname: options.host, | |||
port: options.port, | |||
username: options.username, | |||
password: options.password | |||
}; | |||
this._connection.send(JSON.stringify({"tp": "init", "data": options})) | |||
console.log("发送初始化数据:" + options) | |||
} | |||
WSSHClient.prototype.sendClientData = function (data) { | |||
this._connection.send(JSON.stringify({"tp": "client", "data": data})) | |||
console.log("发送客户端数据:" + data) | |||
} | |||
var client = new WSSHClient(); |