/*
* Copyright 2011 Future Systems
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.krakenapps.script;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.MalformedURLException;
import java.net.NetworkInterface;
import java.net.Socket;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.net.UnknownHostException;
import java.nio.channels.FileChannel;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.Enumeration;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
import java.util.UUID;
import org.krakenapps.ansicode.ClearScreenCode;
import org.krakenapps.ansicode.ClearScreenCode.Option;
import org.krakenapps.ansicode.MoveToCode;
import org.krakenapps.ansicode.SetColorCode;
import org.krakenapps.ansicode.SetColorCode.Color;
import org.krakenapps.api.DirectoryAutoCompleter;
import org.krakenapps.api.PathAutoCompleter;
import org.krakenapps.api.Script;
import org.krakenapps.api.ScriptArgument;
import org.krakenapps.api.ScriptContext;
import org.krakenapps.api.ScriptUsage;
import org.krakenapps.main.Kraken;
import org.krakenapps.pkg.HttpWagon;
import org.osgi.framework.BundleException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.UserInfo;
public class CoreScript implements Script {
private final Logger logger = LoggerFactory.getLogger(CoreScript.class.getName());
private ScriptContext context;
@Override
public void setScriptContext(ScriptContext context) {
this.context = context;
}
@ScriptUsage(description = "tcp scan", arguments = {
@ScriptArgument(name = "ip", type = "string", description = "ip address or host name"),
@ScriptArgument(name = "port", type = "integer", description = "port number"),
@ScriptArgument(name = "timeout", type = "integer", description = "timeout(ms)", optional = true) })
public void tcpscan(String[] args) {
InetSocketAddress endpoint = null;
Socket socket = new Socket();
try {
InetAddress host = InetAddress.getByName(args[0]);
Integer port = Integer.parseInt(args[1]);
Integer timeout = (args.length > 2) ? Integer.parseInt(args[2]) : 3000;
endpoint = new InetSocketAddress(host, port);
context.println("trying to connect " + endpoint);
socket.connect(endpoint, timeout);
context.println("opened");
} catch (UnknownHostException e) {
context.println("invalid host [" + args[0] + "]");
} catch (SocketTimeoutException e) {
context.println("timeout");
} catch (IOException e) {
context.println("not opened: " + e.getMessage());
logger.error("kraken core: cannot connect to " + endpoint, e);
} finally {
try {
socket.close();
} catch (IOException e) {
}
}
}
@ScriptUsage(description = "print file content", arguments = { @ScriptArgument(name = "file path", type = "string", description = "file path", autocompletion = PathAutoCompleter.class) })
public void cat(String[] args) throws IOException {
File dir = (File) context.getSession().getProperty("dir");
File f = canonicalize(dir, args[0]);
if (f == null || !f.exists()) {
context.println("cat: " + f.getName() + ": No such file or directory");
return;
}
FileInputStream is = new FileInputStream(f);
BufferedReader br = null;
try {
br = new BufferedReader(new InputStreamReader(is));
while (true) {
String line = br.readLine();
if (line == null)
break;
context.println(line);
}
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
}
}
}
}
@ScriptUsage(description = "edit file content", arguments = { @ScriptArgument(name = "file path", type = "string", description = "file path", autocompletion = PathAutoCompleter.class) })
public void edit(String[] args) {
try {
File dir = (File) context.getSession().getProperty("dir");
File f = new File(dir, args[0]);
new Editor(context).open(f);
} catch (IOException e) {
context.println(e.getMessage());
}
}
public void wget(String[] args) throws MalformedURLException {
File dir = (File) context.getSession().getProperty("dir");
URL url = new URL(args[0]);
FileOutputStream os = null;
try {
byte[] b = HttpWagon.download(url);
String fileStr = url.getFile();
if (fileStr.indexOf('/') != -1)
fileStr = fileStr.substring(fileStr.lastIndexOf('/') + 1);
File f = new File(dir, fileStr);
os = new FileOutputStream(f);
os.write(b);
context.println("downloaded " + f.getAbsolutePath());
} catch (Exception e) {
context.println(e.getMessage());
} finally {
if (os != null)
try {
os.close();
} catch (IOException e) {
}
}
}
public void pwd(String[] args) throws IOException {
File dir = (File) context.getSession().getProperty("dir");
context.println(dir.getCanonicalPath());
}
@ScriptUsage(description = "copy file", arguments = {
@ScriptArgument(name = "source file path", type = "string", description = "source file path", autocompletion = PathAutoCompleter.class),
@ScriptArgument(name = "destination file path", type = "string", description = "destination file path", autocompletion = PathAutoCompleter.class) })
public void cp(String[] args) throws IOException {
File dir = (File) context.getSession().getProperty("dir");
File from = canonicalize(dir, args[0]);
File to = canonicalize(dir, args[1]);
if (from == null || !from.exists())
context.println("cp: cannot stat '" + args[0] + "': No such file or directory");
else if (to == null)
context.println("cp: cannot stat '" + args[1] + "': No such file or directory");
else {
FileChannel inChannel = new FileInputStream(from).getChannel();
FileChannel outChannel = new FileOutputStream(to).getChannel();
try {
inChannel.transferTo(0, inChannel.size(), outChannel);
} catch (IOException e) {
context.println(e.getMessage());
} finally {
inChannel.close();
outChannel.close();
}
}
}
@ScriptUsage(description = "delete file", arguments = { @ScriptArgument(name = "file path", type = "string", description = "file path", autocompletion = PathAutoCompleter.class) })
public void rm(String[] args) throws IOException {
File dir = (File) context.getSession().getProperty("dir");
for (String token : args) {
File f = canonicalize(dir, token);
if (f == null || !f.exists())
context.println("rm: cannot remove '" + token + "': No such file or directory");
else {
boolean ret = f.delete();
if (!ret)
context.println("rm: permission denied");
}
}
}
@ScriptUsage(description = "move file", arguments = {
@ScriptArgument(name = "source file path", type = "string", description = "source file path", autocompletion = PathAutoCompleter.class),
@ScriptArgument(name = "destination file path", type = "string", description = "destination file path", autocompletion = PathAutoCompleter.class) })
public void mv(String[] args) throws IOException {
File dir = (File) context.getSession().getProperty("dir");
File from = canonicalize(dir, args[0]);
File to = canonicalize(dir, args[1]);
if (from == null)
context.println("mv: cannot stat '" + args[0] + "': No such file or directory");
else if (to == null)
context.println("mv: cannot stat '" + args[1] + "': No such file or directory");
else if (from.equals(to))
context.println("mv: '" + from.getName() + "' and '" + to.getName() + "' are the same file");
else if (!from.exists())
context.println("mv: cannot stat '" + from.getName() + "': No such file or directory");
else {
boolean ret = from.renameTo(to);
if (!ret)
context.println("mv: cannot rename '" + from.getName() + "' to '" + to.getName() + "'");
}
}
@ScriptUsage(description = "list files", arguments = { @ScriptArgument(name = "path", type = "string", description = "path", optional = true, autocompletion = PathAutoCompleter.class) })
public void ls(String[] args) throws IOException {
File dir = (File) context.getSession().getProperty("dir");
List<File> targets = new LinkedList<File>();
if (args.length == 0)
targets.add(dir);
for (String arg : args) {
File target = canonicalize(dir, arg);
if (target != null)
targets.add(target);
}
for (File target : targets) {
if (!target.exists()) {
context.println("ls: cannot access " + target.getName() + " :No such file or directory");
return;
}
if (targets.size() > 1)
context.println(target.getName() + ":");
File[] list = target.listFiles();
if (list == null)
continue;
for (File f : list) {
context.println(formatFileInfo(f));
}
}
}
@ScriptUsage(description = "change directory", arguments = { @ScriptArgument(name = "path", type = "string", description = "absolute or relative directory path", autocompletion = DirectoryAutoCompleter.class) })
public void cd(String[] args) throws IOException {
if (args.length == 0)
return;
File dir = (File) context.getSession().getProperty("dir");
File newDir = canonicalize(dir, args[0]);
if (newDir == null || !(newDir.exists() && newDir.isDirectory()))
context.println("No such file or directory");
else
context.getSession().setProperty("dir", newDir);
}
private File canonicalize(File dir, String path) throws IOException {
if (path.startsWith("/"))
return new File(path).getCanonicalFile();
else
return new File(dir, path).getCanonicalFile();
}
public void date(String[] args) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ssZ");
context.println(dateFormat.format(new Date()));
}
public void ipconfig(String[] args) throws SocketException {
Enumeration<NetworkInterface> it = NetworkInterface.getNetworkInterfaces();
while (it.hasMoreElements()) {
NetworkInterface nic = it.nextElement();
byte[] b = nic.getHardwareAddress();
String mac = "";
if (b != null && b.length != 0)
mac = String.format(" (%02X:%02X:%02X:%02X:%02X:%02X)", b[0], b[1], b[2], b[3], b[4], b[5]);
context.printf("%s%s - %s\n", nic.getName(), mac, nic.getDisplayName());
Enumeration<InetAddress> in = nic.getInetAddresses();
while (in.hasMoreElements()) {
InetAddress addr = in.nextElement();
context.println(" " + addr.getHostAddress());
}
}
}
@ScriptUsage(description = "set foreground and background colors", arguments = {})
public void color(String[] args) {
if (args.length == 0) {
changeColor("07");
return;
}
if (args.length < 2)
return;
String code = args[0];
if (code.length() != 2)
return;
changeColor(code);
}
public void guid(String[] args) {
context.println(UUID.randomUUID().toString());
}
public void shutdown(String[] args) {
if (Kraken.isServiceMode()) {
context.println("You cannot use shutdown command when Kraken is running in service mode.");
}
try {
Kraken.getContext().getBundle(0).stop();
} catch (BundleException e) {
}
}
public void clear(String[] args) {
context.print(new MoveToCode(1, 1));
context.print(new ClearScreenCode(Option.EntireScreen));
}
public void set(String[] args) {
if (args.length == 0) {
Properties props = System.getProperties();
List<String> keys = new ArrayList<String>(props.stringPropertyNames());
Collections.sort(keys);
for (String key : keys) {
context.printf("%s=%s\n", key, props.getProperty(key));
}
} else if (args.length == 1) {
if (!args[0].contains("=")) {
context.printf("%s=%s\n", args[0], System.getProperty(args[0]));
} else {
String[] kv = args[0].split("=", 2);
System.setProperty(kv[0].trim(), kv[1].trim());
context.println("set");
}
} else {
for (String arg : args) {
context.printf("%s=%s\n", arg, System.getProperty(arg));
}
}
}
private String formatFileInfo(File f) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String lastModified = dateFormat.format(new Date(f.lastModified()));
String dir = f.isDirectory() ? "<DIR>" : "";
char r = f.canRead() ? 'r' : '-';
char w = f.canWrite() ? 'w' : '-';
char x = f.canExecute() ? 'x' : '-';
return String.format("%c%c%c %10d\t%s\t%s\t%s", r, w, x, f.length(), lastModified, dir, f.getName());
}
private void changeColor(String code) {
code = code.toUpperCase();
char c1 = code.charAt(0);
char c2 = code.charAt(1);
boolean highIntensity = false;
if (c2 >= '9')
highIntensity = true;
byte b = invert(c1);
byte f = invert(c2);
context.print(new SetColorCode(Color.parse(b), Color.parse(f), highIntensity));
context.print(new MoveToCode(1, 1));
context.print(new ClearScreenCode(Option.EntireScreen));
}
private byte invert(char c) {
final byte[] code = new byte[] { 0, 4, 2, 6, 1, 5, 3, 7, 7 };
byte b = (byte) ((c >= 'A') ? (c - 'A' + 2) : (c - '0'));
if (c == '9')
b = 1;
b = (byte) (b % code.length);
return code[b];
}
@ScriptUsage(description = "scp", arguments = {
@ScriptArgument(name = "file from", type = "string", description = "local path or user@host:path format"),
@ScriptArgument(name = "file to", type = "string", description = "local path or user@host:path format") })
public void scp(String[] args) throws JSchException, IOException {
if (args[0].contains("@"))
scpFrom(args);
else
scpTo(args);
}
private void scpFrom(String[] args) {
// scp from
FileOutputStream fos = null;
try {
String user = args[0].substring(0, args[0].indexOf('@'));
args[0] = args[0].substring(args[0].indexOf('@') + 1);
String host = args[0].substring(0, args[0].indexOf(':'));
String rfile = args[0].substring(args[0].indexOf(':') + 1);
String lfile = args[1];
String prefix = null;
if (new File(lfile).isDirectory()) {
prefix = lfile + File.separator;
}
JSch jsch = new JSch();
Session session = jsch.getSession(user, host, 22);
session.setUserInfo(new MyUserInfo());
session.setConfig("StrictHostKeyChecking", "no");
session.connect();
String command = "scp -f " + rfile;
Channel channel = session.openChannel("exec");
((ChannelExec) channel).setCommand(command);
// get I/O streams for remote scp
OutputStream out = channel.getOutputStream();
InputStream in = channel.getInputStream();
channel.connect();
byte[] buf = new byte[1024];
// send '\0'
buf[0] = 0;
out.write(buf, 0, 1);
out.flush();
while (true) {
int c = checkAck(in);
if (c != 'C') {
break;
}
// read '0644 '
in.read(buf, 0, 5);
long filesize = 0L;
while (true) {
if (in.read(buf, 0, 1) < 0) {
// error
break;
}
if (buf[0] == ' ')
break;
filesize = filesize * 10L + (long) (buf[0] - '0');
}
String file = null;
for (int i = 0;; i++) {
in.read(buf, i, 1);
if (buf[i] == (byte) 0x0a) {
file = new String(buf, 0, i);
break;
}
}
// send '\0'
buf[0] = 0;
out.write(buf, 0, 1);
out.flush();
// read a content of lfile
fos = new FileOutputStream(prefix == null ? lfile : prefix + file);
int foo;
while (true) {
if (buf.length < filesize)
foo = buf.length;
else
foo = (int) filesize;
foo = in.read(buf, 0, foo);
if (foo < 0) {
// error
break;
}
fos.write(buf, 0, foo);
filesize -= foo;
if (filesize == 0L)
break;
}
fos.close();
fos = null;
if (checkAck(in) != 0) {
throw new IllegalStateException();
}
// send '\0'
buf[0] = 0;
out.write(buf, 0, 1);
out.flush();
}
session.disconnect();
} catch (Exception e) {
context.println(e.getMessage());
try {
if (fos != null)
fos.close();
} catch (Exception ee) {
}
}
}
private void scpTo(String[] args) {
FileInputStream fis = null;
try {
String lfile = args[0];
String user = args[1].substring(0, args[1].indexOf('@'));
args[1] = args[1].substring(args[1].indexOf('@') + 1);
String host = args[1].substring(0, args[1].indexOf(':'));
String rfile = args[1].substring(args[1].indexOf(':') + 1);
JSch jsch = new JSch();
Session session = jsch.getSession(user, host, 22);
// username and password will be given via UserInfo interface.
UserInfo ui = new MyUserInfo();
session.setUserInfo(ui);
session.setConfig("StrictHostKeyChecking", "no");
session.connect();
// exec 'scp -t rfile' remotely
String command = "scp -p -t " + rfile;
Channel channel = session.openChannel("exec");
((ChannelExec) channel).setCommand(command);
// get I/O streams for remote scp
OutputStream out = channel.getOutputStream();
InputStream in = channel.getInputStream();
channel.connect();
if (checkAck(in) != 0) {
System.exit(0);
}
// send "C0644 filesize filename", where filename should not include
// '/'
long filesize = (new File(lfile)).length();
command = "C0644 " + filesize + " ";
if (lfile.lastIndexOf('/') > 0) {
command += lfile.substring(lfile.lastIndexOf('/') + 1);
} else {
command += lfile;
}
command += "\n";
out.write(command.getBytes());
out.flush();
if (checkAck(in) != 0) {
System.exit(0);
}
// send a content of lfile
fis = new FileInputStream(lfile);
byte[] buf = new byte[1024];
while (true) {
int len = fis.read(buf, 0, buf.length);
if (len <= 0)
break;
out.write(buf, 0, len); // out.flush();
}
fis.close();
fis = null;
// send '\0'
buf[0] = 0;
out.write(buf, 0, 1);
out.flush();
if (checkAck(in) != 0) {
System.exit(0);
}
out.close();
channel.disconnect();
session.disconnect();
} catch (Exception e) {
System.out.println(e);
try {
if (fis != null)
fis.close();
} catch (Exception ee) {
}
}
}
int checkAck(InputStream in) throws IOException {
int b = in.read();
// b may be 0 for success,
// 1 for error,
// 2 for fatal error,
// -1
if (b == 0)
return b;
if (b == -1)
return b;
if (b == 1 || b == 2) {
StringBuffer sb = new StringBuffer();
int c;
do {
c = in.read();
sb.append((char) c);
} while (c != '\n');
if (b == 1) { // error
context.println(sb.toString());
}
if (b == 2) { // fatal error
context.println(sb.toString());
}
}
return b;
}
private class MyUserInfo implements UserInfo {
private String passphrase;
private String password;
@Override
public String getPassphrase() {
return passphrase;
}
@Override
public String getPassword() {
return password;
}
@Override
public boolean promptPassphrase(String message) {
context.print(message + ": ");
try {
passphrase = context.readPassword();
} catch (InterruptedException e) {
return false;
}
return true;
}
@Override
public boolean promptPassword(String message) {
context.print(message + ": ");
try {
password = context.readPassword();
} catch (InterruptedException e) {
return false;
}
return true;
}
@Override
public boolean promptYesNo(String message) {
return false;
}
@Override
public void showMessage(String message) {
context.println(message);
}
}
}