001: /*
002: * Ssh.java
003: *
004: * Copyright (C) 2002-2004 Peter Graves
005: * $Id: Ssh.java,v 1.5 2004/09/13 00:47:16 piso Exp $
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License
009: * as published by the Free Software Foundation; either version 2
010: * of the License, or (at your option) any later version.
011: *
012: * This program is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
015: * GNU General Public License for more details.
016: *
017: * You should have received a copy of the GNU General Public License
018: * along with this program; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
020: */
021:
022: package org.armedbear.j;
023:
024: import java.io.IOException;
025: import java.io.InputStreamReader;
026: import java.io.OutputStreamWriter;
027: import java.util.ArrayList;
028:
029: public final class Ssh {
030: private InputStreamReader reader;
031: private OutputStreamWriter writer;
032: private String[] cmdarray;
033: private String password;
034: private String errorText;
035: private boolean succeeded;
036:
037: public Ssh() {
038: }
039:
040: public final String getErrorText() {
041: return errorText;
042: }
043:
044: public boolean copy(File source, File destination) {
045: SshFile remote = null;
046: if (source instanceof SshFile)
047: remote = (SshFile) source;
048: else if (destination instanceof SshFile)
049: remote = (SshFile) destination;
050: if (remote == null) {
051: Debug.bug("Ssh.copy no remote file");
052: return false;
053: }
054: String userName = remote.getUserName();
055: password = remote.getPassword();
056: ArrayList list = new ArrayList();
057: list.add("jpty");
058: list.add("scp");
059: list.add("-q");
060: if (remote.getPort() != SshFile.DEFAULT_PORT) {
061: list.add("-P");
062: list.add(String.valueOf(remote.getPort()));
063: }
064: FastStringBuffer sb = new FastStringBuffer();
065: if (source instanceof SshFile) {
066: if (userName != null) {
067: sb.append(userName);
068: sb.append('@');
069: }
070: sb.append(source.getHostName());
071: sb.append(':');
072: }
073: sb.append(escape(source.canonicalPath()));
074: list.add(sb.toString());
075: sb.setLength(0);
076: if (destination instanceof SshFile) {
077: if (userName != null) {
078: sb.append(userName);
079: sb.append('@');
080: }
081: sb.append(destination.getHostName());
082: sb.append(':');
083: }
084: sb.append(escape(destination.canonicalPath()));
085: list.add(sb.toString());
086: String[] array = new String[list.size()];
087: cmdarray = (String[]) list.toArray(array);
088: run();
089: return succeeded;
090: }
091:
092: private static final String safeChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-./\\";
093:
094: // Escapes unsafe characters.
095: private static final String escape(String s) {
096: final int length = s.length();
097: FastStringBuffer sb = new FastStringBuffer(length * 2);
098: for (int i = 0; i < length; i++) {
099: char c = s.charAt(i);
100: if (safeChars.indexOf(c) < 0)
101: sb.append('\\');
102: sb.append(c);
103: }
104: return sb.toString();
105: }
106:
107: public void run() {
108: Process process = null;
109: int result = -1; // Assume error.
110: try {
111: process = Runtime.getRuntime().exec(cmdarray);
112: } catch (Throwable t) {
113: Log.error(t);
114: return;
115: }
116: writer = new OutputStreamWriter(process.getOutputStream());
117: reader = new InputStreamReader(process.getInputStream());
118: SshReaderThread thread = new SshReaderThread();
119: thread.start();
120: try {
121: thread.join();
122: result = process.waitFor();
123: } catch (InterruptedException e) {
124: Log.error(e);
125: }
126: if (result == 0) {
127: errorText = thread.getResponse();
128: if (errorText == null) {
129: succeeded = true;
130: } else {
131: errorText = errorText.trim();
132: if (errorText.length() == 0) {
133: succeeded = true;
134: } else {
135: // No error if response is a single line starting with
136: // "warning:".
137: if (errorText.toLowerCase().startsWith("warning:"))
138: if (errorText.indexOf('\n') < 0)
139: succeeded = true;
140: }
141: }
142: }
143: }
144:
145: private void sendPassword() {
146: if (password != null) {
147: try {
148: writer.write(password);
149: writer.write("\n");
150: writer.flush();
151: } catch (IOException e) {
152: Log.error(e);
153: }
154: } else
155: Debug.bug();
156: }
157:
158: private class SshReaderThread extends Thread {
159: private char[] buf = new char[4096];
160: private boolean done = false;
161: private String response;
162:
163: // If this constructor is private, we run into jikes 1.15 bug #2256.
164: /*private*/SshReaderThread() {
165: }
166:
167: public final String getResponse() {
168: return response;
169: }
170:
171: public void run() {
172: FastStringBuffer sb = new FastStringBuffer();
173: while (true) {
174: final String s = read();
175: if (s == null) {
176: response = sb.toString();
177: return;
178: }
179: if (done) {
180: if (s.length() > 0)
181: sb.append(s);
182: response = sb.toString();
183: return;
184: }
185: if (isPasswordPrompt(s)) {
186: sendPassword();
187: sb.setLength(0);
188: } else
189: sb.append(s);
190: }
191: }
192:
193: private String read() {
194: FastStringBuffer sb = new FastStringBuffer();
195: try {
196: do {
197: int numChars = reader.read(buf, 0, buf.length); // Blocks.
198: if (numChars < 0) {
199: done = true;
200: break;
201: }
202: if (numChars > 0)
203: sb.append(buf, 0, numChars);
204: } while (reader.ready());
205: } catch (Exception e) {
206: Log.error(e);
207: return null;
208: }
209: return sb.toString();
210: }
211: }
212:
213: private boolean isPasswordPrompt(String s) {
214: String trim = s.trim().toLowerCase();
215: if (trim.endsWith("password:"))
216: return true;
217: if (trim.endsWith("response:"))
218: return true;
219: if (trim.startsWith("enter passphrase ") && trim.endsWith(":"))
220: return true;
221: return false;
222: }
223: }
|