KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > armedbear > j > SshSession


1 /*
2  * SshSession.java
3  *
4  * Copyright (C) 2002-2003 Peter Graves
5  * $Id: SshSession.java,v 1.14 2004/09/13 00:49:30 piso Exp $
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20  */

21
22 package org.armedbear.j;
23
24 import gnu.regexp.RE;
25 import gnu.regexp.REException;
26 import gnu.regexp.REMatch;
27 import gnu.regexp.UncheckedRE;
28 import java.io.IOException JavaDoc;
29 import java.io.OutputStreamWriter JavaDoc;
30 import java.util.ArrayList JavaDoc;
31 import java.util.List JavaDoc;
32 import javax.swing.SwingUtilities JavaDoc;
33
34 public final class SshSession implements Constants
35 {
36     public static final int DEFAULT_PORT = 22;
37
38     private static final int TRY_AGAIN = 0;
39     private static final int AUTHENTICATED = 1;
40     private static final int PASSWORD = 2;
41     private static final int PASSPHRASE = 3;
42     private static final int YES = 4;
43     private static final int NO = 5;
44
45     private static final String JavaDoc PROMPT = "$ ";
46
47     private static ArrayList JavaDoc sessionList;
48
49     private static CleanupThread cleanupThread;
50
51     private String JavaDoc hostName;
52     private String JavaDoc userName;
53     private String JavaDoc password;
54     private String JavaDoc passphrase;
55     private int port;
56
57     private boolean locked;
58
59     private Process JavaDoc process;
60
61     private OutputStreamWriter JavaDoc stdin;
62     private StdoutThread stdoutThread;
63     private StderrThread stderrThread;
64
65     private FastStringBuffer output = new FastStringBuffer();
66
67     private boolean connected;
68
69     private String JavaDoc loginDirectory;
70
71     private boolean echo;
72
73     // tcsh doesn't like "\cd", so we may change it to "cd" later.
74
private String JavaDoc cd = "\\cd";
75
76     private Buffer outputBuffer;
77
78     private String JavaDoc passwordTitle;
79     private String JavaDoc passwordPrompt;
80
81     private SshSession(SshFile file, boolean locked)
82     {
83         hostName = file.getHostName();
84         Debug.assertTrue(hostName != null);
85         userName = file.getUserName();
86         password = file.getPassword();
87         port = file.getPort();
88         this.locked = locked;
89         register(this);
90     }
91
92     private SshSession(SshSession other, boolean locked)
93     {
94         hostName = other.getHostName();
95         Debug.assertTrue(hostName != null);
96         userName = other.getUserName();
97         password = other.getPassword();
98         passphrase = other.getPassphrase();
99         port = other.getPort();
100         this.locked = locked;
101         register(this);
102     }
103
104     private static synchronized void register(SshSession session)
105     {
106         if (sessionList == null)
107             sessionList = new ArrayList JavaDoc();
108         sessionList.add(session);
109         if (cleanupThread == null) {
110             cleanupThread = new CleanupThread(cleanupRunnable);
111             cleanupThread.start();
112         }
113         Log.debug("leaving register() session count = " + sessionList.size());
114     }
115
116     private static synchronized void unregister(SshSession session)
117     {
118         if (sessionList == null) {
119             Debug.bug();
120             return;
121         }
122         if (!sessionList.contains(session))
123             Debug.bug();
124         sessionList.remove(session);
125         Log.debug("leaving unregister() session count = "
126             + sessionList.size());
127     }
128
129     public static synchronized SshSession getSession(SshFile file)
130     {
131         if (file == null) {
132             Debug.bug();
133             return null;
134         }
135         if (file.getHostName() == null) {
136             Debug.bug();
137             return null;
138         }
139         if (file.getUserName() == null) {
140             Debug.bug();
141             file.setUserName(System.getProperty("user.name"));
142         }
143         SshSession session = lockSession(file);
144         if (session != null)
145             return session;
146         // No idle session found for this file. Try to find a session to clone.
147
session = findSession(file);
148         if (session != null)
149             return new SshSession(session, true);
150         // No session to clone.
151
return new SshSession(file, true);
152     }
153
154     // Called only from synchronized methods.
155
private static SshSession lockSession(SshFile file)
156     {
157         if (sessionList != null) {
158             for (int i = sessionList.size(); i-- > 0;) {
159                 SshSession session = (SshSession) sessionList.get(i);
160                 if (session.getUserName().equals(file.getUserName())) {
161                     if (session.getHostName().equals(file.getHostName())) {
162                         if (session.getPort() == file.getPort()) {
163                             if (session.lock()) {
164                                 return session;
165                             }
166                         }
167                     }
168                 }
169             }
170         }
171         return null;
172     }
173
174     // Called only from synchronized methods.
175
private static SshSession findSession(SshFile file)
176     {
177         if (sessionList != null) {
178             for (int i = sessionList.size(); i-- > 0;) {
179                 SshSession session = (SshSession) sessionList.get(i);
180                 if (session.getUserName().equals(file.getUserName())) {
181                     if (session.getHostName().equals(file.getHostName())) {
182                         if (session.getPort() == file.getPort()) {
183                             return session;
184                         }
185                     }
186                 }
187             }
188         }
189         return null;
190     }
191
192     public final synchronized boolean isLocked()
193     {
194         return locked;
195     }
196
197     private synchronized boolean lock()
198     {
199         if (locked)
200             return false;
201         locked = true;
202         return true;
203     }
204
205     public synchronized void unlock()
206     {
207         if (locked)
208             locked = false;
209         else
210             Debug.bug("SshSession.unlock session was not locked");
211         synchronized (SshSession.class) {
212             if (sessionList != null)
213                 Log.debug("unlock session count = " + sessionList.size());
214             else
215                 Debug.bug("SshSession.unlock no session list");
216         }
217     }
218
219     public final String JavaDoc getHostName()
220     {
221         return hostName;
222     }
223
224     public final String JavaDoc getUserName()
225     {
226         return userName;
227     }
228
229     public final String JavaDoc getPassword()
230     {
231         return password;
232     }
233
234     public final void setPassword(String JavaDoc password)
235     {
236         this.password = password;
237     }
238
239     public final String JavaDoc getPassphrase()
240     {
241         return passphrase;
242     }
243
244     public final int getPort()
245     {
246         return port;
247     }
248
249     public final String JavaDoc getLoginDirectory()
250     {
251         return loginDirectory;
252     }
253
254     public void setOutputBuffer(Buffer buf)
255     {
256         outputBuffer = buf;
257     }
258
259     public boolean isDirectory(String JavaDoc canonicalPath)
260     {
261         SshFile file =
262             new SshFile(hostName, canonicalPath, userName, null, port);
263         if (DirectoryCache.getDirectoryCache().getListing(file) != null)
264             return true;
265         if (!connect())
266             return false;
267         try {
268             return changeDirectory(canonicalPath);
269         }
270         catch (Exception JavaDoc e) {
271             Log.error(e);
272         }
273         // An exception occurred. We're confused. Wait a moment.
274
try {
275             Thread.sleep(1000);
276         }
277         catch (InterruptedException JavaDoc e) {
278             Log.error(e);
279         }
280         // Try again.
281
try {
282             Log.warn("isDirectory() retrying after exception ...");
283             // Send an empty command to flush the channel.
284
command("");
285             return changeDirectory(canonicalPath);
286         }
287         catch (Exception JavaDoc e) {
288             // Another exception. Give up.
289
Log.error(e);
290             return false;
291         }
292     }
293
294     private String JavaDoc stat(String JavaDoc canonicalPath)
295     {
296         FastStringBuffer sb = new FastStringBuffer("stat -t \"");
297         sb.append(canonicalPath);
298         sb.append('"');
299         String JavaDoc cmd = sb.toString();
300         String JavaDoc response = command(cmd);
301         Log.debug(cmd + " " + response);
302         if (response != null && response.startsWith(canonicalPath))
303             return response.substring(canonicalPath.length()).trim();
304         return null;
305     }
306
307     // Determines file type from string returned by stat.
308
private static int getType(String JavaDoc s)
309     {
310         if (s != null) {
311             List JavaDoc tokens = Utilities.tokenize(s);
312             if (tokens.size() == 14) {
313                 String JavaDoc token = (String JavaDoc) tokens.get(2);
314                 Log.debug("token = |" + token + "|");
315                 try {
316                     int n = Integer.parseInt(token, 16);
317                     Log.debug("n = " + Integer.toString(n, 8));
318                     if ((n & 0040000) == 0040000)
319                         return File.TYPE_DIRECTORY;
320                     if ((n & 0120000) == 0120000)
321                         return File.TYPE_LINK;
322                     if ((n & 0100000) == 0100000)
323                         return File.TYPE_FILE;
324                 }
325                 catch (NumberFormatException JavaDoc e) {}
326             }
327         }
328         return File.TYPE_UNKNOWN;
329     }
330
331     // Throws an exception if we don't recognize the response.
332
private boolean changeDirectory(String JavaDoc canonicalPath) throws Exception JavaDoc
333     {
334         FastStringBuffer sb = new FastStringBuffer(cd);
335         sb.append(" \"");
336         sb.append(canonicalPath);
337         sb.append('"');
338         String JavaDoc cmd = sb.toString();
339         Log.debug("changeDirectory cmd = |" + cmd + "|");
340         String JavaDoc response = command(cmd);
341         Log.debug("changeDirectory response = |" + response + "|");
342         if (response == null)
343             throw new Exception JavaDoc(); // Lost connection.
344
if (response.indexOf("Command not found") >= 0) {
345             if (cd.equals("\\cd")) {
346                 // tcsh doesn't like "\cd". Try again with "cd".
347
cd = "cd";
348                 response = command(cd + " \"" + canonicalPath + '"');
349                 if (response == null)
350                     throw new Exception JavaDoc(); // Lost connection.
351
} else
352                 return false;
353         }
354         if (response.equals(PROMPT)) {
355             // No news is good news.
356
Log.debug("changeDirectory succeeded");
357             return true;
358         }
359         String JavaDoc lower = response.toLowerCase();
360         if (lower.indexOf("not a directory") >= 0)
361             return false;
362         else if (lower.indexOf("no such file or directory") >= 0)
363             return false;
364         // If the directory name is very long (> 80 chars or so), bash will
365
// wrap (i.e. mangle) the response. Try to detect that situation.
366
int index = response.indexOf('\r');
367         if (index < 0)
368             index = response.indexOf('\n');
369         if (index >= 0) {
370             String JavaDoc beginning = response.substring(0, index);
371             Log.debug("beginning = |" + beginning + "|");
372             if (cmd.startsWith(beginning)) {
373                 Log.debug("cmd starts with beginning!");
374                 index = response.lastIndexOf((char)8);
375                 if (index >= 0) {
376                     Log.debug("backspace found!");
377                     String JavaDoc end = response.substring(index+1);
378                     Log.debug("end = |" + end + "|");
379                     if (cmd.endsWith(end)) {
380                         Log.debug("cmd ends with end!");
381                         return true;
382                     }
383                 }
384             }
385         }
386         // Unknown response. We must be confused.
387
throw new Exception JavaDoc();
388     }
389
390     public boolean exists(String JavaDoc canonicalPath)
391     {
392         if (connect()) {
393             String JavaDoc response = lsld(canonicalPath);
394             if (response != null && response.length() > 0) {
395                 char c = response.charAt(0);
396                 if (c == 'd' || c == '-')
397                     return true;
398             }
399         }
400         return false;
401     }
402
403     public static String JavaDoc getDirectoryListing(File file)
404     {
405         if (!(file instanceof SshFile)) {
406             Debug.assertTrue(false);
407             return null;
408         }
409         String JavaDoc listing = DirectoryCache.getDirectoryCache().getListing(file);
410         if (listing == null) {
411             SshSession session = getSession((SshFile)file);
412             if (session != null) {
413                 listing = session.retrieveDirectoryListing(file);
414                 session.unlock();
415                 if (listing != null)
416                     DirectoryCache.getDirectoryCache().put(file, listing);
417             }
418         }
419         return listing;
420     }
421
422     public String JavaDoc retrieveDirectoryListing(File file)
423     {
424         if (!(file instanceof SshFile)) {
425             Debug.bug();
426             return null;
427         }
428         if (!isLocked()) {
429             Debug.bug();
430             return null;
431         }
432         if (connect()) {
433             try {
434                 // Do it this way to support symlinks.
435
if (changeDirectory(file.canonicalPath()))
436                     // Change directory succeeded. Do ls -la here.
437
return lsla();
438             }
439             catch (Exception JavaDoc e) {
440                 Log.error(e);
441             }
442         }
443         return null;
444     }
445
446     public synchronized boolean chmod(SshFile file, int permissions)
447     {
448         if (permissions != 0 && connect()) {
449             FastStringBuffer sb = new FastStringBuffer("chmod ");
450             sb.append(Integer.toString(permissions, 8));
451             sb.append(' ');
452             sb.append(file.canonicalPath());
453             final String JavaDoc response = command(sb.toString());
454             // Look for error message in response.
455
if (response != null && response.indexOf("chmod:") < 0) {
456                 // Success! Remove old entry (if any) from directory cache.
457
DirectoryCache.getDirectoryCache().purge(file);
458                 return true;
459             }
460         }
461         return false;
462     }
463
464     public synchronized boolean isConnected()
465     {
466         return connected;
467     }
468
469     public synchronized boolean connect()
470     {
471         if (connected) {
472             Log.debug("SshSession.connect(): already connected");
473             return true;
474         }
475         FastStringBuffer sb = new FastStringBuffer("jpty ssh ");
476         if (userName != null && userName.length() > 0) {
477             sb.append("-l ");
478             sb.append(userName);
479             sb.append(' ');
480         }
481         if (port != DEFAULT_PORT) {
482             sb.append("-p ");
483             sb.append(port);
484             sb.append(' ');
485         }
486         sb.append(hostName);
487         try {
488             process = Runtime.getRuntime().exec(sb.toString());
489         }
490         catch (Throwable JavaDoc t) {
491             Log.error(t);
492             return false;
493         }
494         try {
495             stdin = new OutputStreamWriter JavaDoc(process.getOutputStream());
496             int timeout =
497                 Editor.preferences().getIntegerProperty(Property.SSH_TIMEOUT);
498             Log.debug("ssh timeout is " + timeout + " ms");
499             stdoutThread = new StdoutThread();
500             stdoutThread.setTimeOut(timeout);
501             stderrThread = new StderrThread();
502             stderrThread.setTimeOut(timeout);
503             stdoutThread.start();
504             stderrThread.start();
505         }
506         catch (Throwable JavaDoc t) {
507             Log.error(t);
508             return false;
509         }
510         if (!authenticate()) {
511             killProcess();
512             String JavaDoc message = output.toString().trim();
513             if (message.length() == 0)
514                 message = "Authentication failed";
515             MessageDialog.showMessageDialog(message, "Error");
516             return false;
517         }
518         if (!connected) {
519             killProcess();
520             MessageDialog.showMessageDialog("Lost connection", "Error");
521         }
522         return connected;
523     }
524
525     private void initializeConnection()
526     {
527         boolean oldEcho = echo;
528         echo = true;
529         String JavaDoc response = command("exec /bin/sh");
530         Log.debug("response = |" + response + "|");
531         FastStringBuffer sb = new FastStringBuffer("PS1='");
532         sb.append(PROMPT);
533         sb.append('\'');
534         response = command(sb.toString());
535         Log.debug("response = |" + response + "|");
536         Log.debug("PROMPT = |" + PROMPT + "|");
537         command("unset MAILCHECK");
538         loginDirectory = getCurrentDirectory();
539         connected = (loginDirectory != null);
540         echo = oldEcho;
541     }
542
543     private boolean authenticate()
544     {
545         output.setLength(0);
546         int response;
547         while ((response = checkInitialResponse()) == TRY_AGAIN) {
548             Log.debug("authenticate() TRY_AGAIN");
549             try {
550                 final long TIMEOUT = 30000; // 30 seconds
551
long start = System.currentTimeMillis();
552                 wait(TIMEOUT);
553                 if (System.currentTimeMillis() - start > TIMEOUT)
554                     return false;
555             }
556             catch (InterruptedException JavaDoc e) {
557                 Log.debug("SshSession.connect interrupted!");
558                 return false;
559             }
560         }
561         if (response == AUTHENTICATED) {
562             initializeConnection();
563             return connected;
564         }
565         if (response == PASSWORD)
566             return authenticateWithPassword();
567         if (response == PASSPHRASE)
568             return authenticateWithPassphrase();
569         return false;
570     }
571
572     private boolean authenticateWithPassword()
573     {
574         if (password == null) {
575             password = Netrc.getPassword(hostName, userName);
576             if (password == null) {
577                 if (SwingUtilities.isEventDispatchThread())
578                     getPasswordRunnable.run();
579                 else {
580                     try {
581                         SwingUtilities.invokeAndWait(getPasswordRunnable);
582                     }
583                     catch (Exception JavaDoc e) {
584                         Log.error(e);
585                     }
586                 }
587                 if (password == null) {
588                     killProcess();
589                     return false;
590                 }
591             }
592         }
593         return _authenticate(password);
594     }
595
596     private boolean authenticateWithPassphrase()
597     {
598         if (passphrase == null) {
599             if (SwingUtilities.isEventDispatchThread())
600                 getPassphraseRunnable.run();
601             else {
602                 try {
603                     SwingUtilities.invokeAndWait(getPassphraseRunnable);
604                 }
605                 catch (Exception JavaDoc e) {
606                     Log.error(e);
607                 }
608             }
609             if (passphrase == null) {
610                 killProcess();
611                 return false;
612             }
613         }
614         return _authenticate(passphrase);
615     }
616
617     private boolean _authenticate(String JavaDoc pass)
618     {
619         output.setLength(0);
620         boolean oldEcho = echo;
621         echo = true;
622         sendPass(pass);
623         if (checkAuthenticationResponse()) {
624             Log.debug("authenticate SUCCEEDED!");
625             initializeConnection();
626             echo = oldEcho;
627             return connected;
628         } else {
629             Log.debug("authenticate FAILED!");
630             echo = oldEcho;
631             return false;
632         }
633     }
634
635     private int checkInitialResponse()
636     {
637         final String JavaDoc s = output.toString().trim();
638         String JavaDoc check;
639         int index = s.lastIndexOf("\r\n");
640         if (index >= 0) {
641             check = s.substring(index+2);
642         } else {
643             index = s.lastIndexOf('\n');
644             if (index >=0 )
645                 check = s.substring(index+1);
646             else
647                 check = s;
648         }
649         Log.debug("check = |" + check + "|");
650         String JavaDoc lower = check.toLowerCase();
651         if (lower.indexOf("connection refused") >= 0)
652             return NO;
653         if (lower.endsWith("password:")) {
654             passwordTitle = "Password";
655             passwordPrompt = check;
656             return PASSWORD;
657         }
658         if (s.startsWith("Password:") && lower.endsWith("response:")) {
659             passwordTitle = "Password";
660             passwordPrompt = check;
661             return PASSWORD;
662         }
663         if (lower.startsWith("enter passphrase ") && lower.endsWith(":")) {
664             // We don't want to use the password from .netrc in this situation.
665
password = null;
666             passwordTitle = "Passphrase";
667             passwordPrompt = check;
668             return PASSPHRASE;
669         }
670         RE promptRE = getPromptRE();
671         if (promptRE.getMatch(lower) != null)
672             return AUTHENTICATED;
673         return TRY_AGAIN;
674     }
675
676     private boolean checkAuthenticationResponse()
677     {
678         String JavaDoc s = output.toString();
679         Log.debug("checkAuthenticationResponse output = |" + output + "|");
680         int result = checkResponse(s);
681         Log.debug("checkAuthenticationResponse result = " + result);
682         while (result == TRY_AGAIN) {
683             try {
684                 wait();
685             }
686             catch (InterruptedException JavaDoc e) {
687                 Log.error(e);
688                 return false;
689             }
690             s = output.toString();
691             Log.debug("checkAuthenticationResponse output = |" + output + "|");
692             result = checkResponse(s);
693             Log.debug("checkAuthenticationResponse result = " + result);
694         }
695         Log.debug("checkAuthenticationResponse returning " + (result == YES));
696         return result == YES;
697     }
698
699     // Helper for checkAuthenticationResponse().
700
private int checkResponse(String JavaDoc s)
701     {
702         if (s.toLowerCase().indexOf("denied") >= 0)
703             return NO;
704         String JavaDoc prompt = null;
705         for (int i = s.length(); i-- > 0;) {
706             char c = s.charAt(i);
707             if (c == '\r' || c == '\n') {
708                 prompt = s.substring(i+1);
709                 break;
710             }
711         }
712         if (prompt == null)
713             prompt = s;
714         Log.debug("prompt = |" + reveal(prompt) + "|");
715         RE promptRE = getPromptRE();
716         Debug.assertTrue(promptRE != null);
717         REMatch match = promptRE.getMatch(prompt);
718         if (match != null)
719             return YES;
720         return TRY_AGAIN;
721     }
722
723     private RE _promptRE;
724
725     public RE getPromptRE()
726     {
727         if (_promptRE == null) {
728             try {
729                 _promptRE = new RE(Editor.preferences().
730                     getStringProperty(Property.SSH_PROMPT_PATTERN));
731             }
732             catch (REException e) {
733                 Log.error(e);
734                 _promptRE = new UncheckedRE(DEFAULT_SHELL_PROMPT_PATTERN);
735             }
736         }
737         Debug.assertTrue(_promptRE != null);
738         return _promptRE;
739     }
740
741     private Runnable JavaDoc getPasswordRunnable = new Runnable JavaDoc() {
742         public void run()
743         {
744             final Editor editor = Editor.currentEditor();
745             editor.setDefaultCursor();
746             password = PasswordDialog.showPasswordDialog(editor, passwordPrompt,
747                 passwordTitle);
748             editor.setWaitCursor();
749         }
750     };
751
752     private Runnable JavaDoc getPassphraseRunnable = new Runnable JavaDoc() {
753         public void run()
754         {
755             final Editor editor = Editor.currentEditor();
756             editor.setDefaultCursor();
757             passphrase = PasswordDialog.showPasswordDialog(editor, passwordPrompt,
758                 passwordTitle);
759             editor.setWaitCursor();
760         }
761     };
762
763     private String JavaDoc getCurrentDirectory()
764     {
765         final String JavaDoc s = command("pwd");
766         if (s == null)
767             return null; // Lost connection.
768
int index = s.indexOf("\r\n");
769         if (index < 0)
770             index = s.indexOf('\n');
771         final String JavaDoc dir = index >= 0 ? s.substring(0, index) : s;
772         Log.debug("getCurrentDirectory() returning |" + dir + "|");
773         return dir;
774     }
775
776     private void disconnect()
777     {
778         if (connected) {
779             try {
780                 stdin.write("exit\n");
781             }
782             catch (Exception JavaDoc e) {
783                 Log.error(e);
784             }
785             killProcess();
786         }
787     }
788
789     private void killProcess()
790     {
791         Process JavaDoc p = process; // Avoid races.
792
if (p != null) {
793             try {
794                 Log.debug("calling Process.destroy()");
795                 p.destroy();
796                 Log.debug("calling Process.waitFor()");
797                 p.waitFor();
798             }
799             catch (InterruptedException JavaDoc e) {
800                 Log.error(e);
801             }
802             process = null;
803             synchronized (this) {
804                 if (stdin != null) {
805                     try {
806                         stdin.close();
807                     }
808                     catch (IOException JavaDoc e) {
809                         Log.error(e);
810                     }
811                     stdin = null;
812                 }
813                 if (stdoutThread != null) {
814                     stdoutThread.cancel();
815                     stdoutThread = null;
816                 }
817                 if (stderrThread != null) {
818                     stderrThread.cancel();
819                     stderrThread = null;
820                 }
821             }
822         }
823         connected = false;
824     }
825
826     public synchronized final void dispose()
827     {
828         Log.debug("SshSession.dispose");
829         if (connected)
830             disconnect();
831         unregister(this);
832     }
833
834     private synchronized String JavaDoc command(String JavaDoc cmd)
835     {
836         if (!write(cmd.concat("\n")))
837             return null;
838         output.setLength(0);
839         while (output.length() == 0) {
840             try {
841                 wait();
842             }
843             catch (InterruptedException JavaDoc e) {
844                 Log.error(e);
845             }
846         }
847         String JavaDoc s = output.toString();
848         // Strip echo of original command.
849
if (s.startsWith(cmd)) {
850             int i = cmd.length();
851             while (i < s.length()) {
852                 char c = s.charAt(i);
853                 if (c == '\r' || c == '\n')
854                     ++i;
855                 else
856                     break;
857             }
858             if (i > cmd.length())
859                 s = s.substring(i);
860         }
861         // Strip prompt.
862
int index = s.lastIndexOf("\r\n");
863         if (index >= 0) {
864             s = s.substring(0, index);
865         } else {
866             index = s.lastIndexOf('\n');
867             if (index >= 0)
868                 s = s.substring(0, index);
869         }
870         return s;
871     }
872
873     private static final RE totalRE = new UncheckedRE("\\n?[^0-9]+ [0-9]+");
874
875     private synchronized String JavaDoc lsla()
876     {
877         boolean valid = false;
878         if (!write("\\ls -la\n"))
879             return null;
880         output.setLength(0);
881         String JavaDoc s = null;
882         for (int i = 0; i < 2; i++) {
883             if (i > 0)
884                 Log.debug("lsla retry " + i);
885             try {
886                 wait();
887             }
888             catch (InterruptedException JavaDoc e) {
889                 Log.error(e);
890                 return null;
891             }
892             s = output.toString();
893             REMatch match = totalRE.getMatch(s);
894             Log.debug("match = |" + match + "|");
895             if (match == null) {
896                 Log.error("lsla no \"total\" line");
897                 continue;
898             }
899             s = s.substring(match.getEndIndex());
900             int index = s.indexOf('\n');
901             if (index < 0) {
902                 // Shouldn't happen.
903
Log.error("lsla no '\\n'");
904                 continue;
905             }
906             s = s.substring(index + 1);
907             valid = true;
908             break;
909         }
910         if (!valid) {
911             Log.error("lsla output not valid - returning null");
912             return null;
913         }
914         // Strip prompt.
915
int index = s.lastIndexOf("\r\n");
916         if (index >= 0)
917             s = s.substring(0, index);
918         else {
919             index = s.lastIndexOf('\n');
920             if (index >= 0)
921                 s = s.substring(0, index);
922         }
923         return s;
924     }
925
926     // Do ls -ld on one file or directory.
927
private synchronized String JavaDoc lsld(String JavaDoc path)
928     {
929         Debug.assertTrue(path != null);
930         Debug.assertTrue(path.length() != 0);
931         FastStringBuffer sb = new FastStringBuffer("\\ls -ld \"");
932         sb.append(path);
933         sb.append('"');
934         sb.append('\n');
935         if (!write(sb.toString()))
936             return null;
937         output.setLength(0);
938         while (output.length() == 0){
939             try {
940                 wait();
941             }
942             catch (InterruptedException JavaDoc e) {
943                 Log.error(e);
944             }
945         }
946         String JavaDoc s = output.toString();
947
948         // Strip echo of original command.
949
if (s.startsWith("ls -ld ")) {
950             // Strip through end of line.
951
int index = s.indexOf('\n');
952             if (index < 0)
953                 return null; // Shouldn't happen.
954
s = s.substring(index + 1);
955         }
956
957         // Skip lines starting with '<'.
958
while (s.length() > 0 && s.charAt(0) == '<') {
959             int index = s.indexOf('\n');
960             if (index < 0)
961                 return null; // Shouldn't happen.
962
s = s.substring(index + 1);
963         }
964
965         // Now we've arrived at the line we want. Strip "\r\n" or '\n'.
966
int index = s.indexOf("\r\n");
967         if (index >= 0) {
968             s = s.substring(0, index);
969         } else {
970             index = s.lastIndexOf('\n');
971             if (index >= 0)
972                 s = s.substring(0, index);
973         }
974         return s;
975     }
976
977     // Password or passphrase.
978
private void sendPass(String JavaDoc pass)
979     {
980         Debug.assertTrue(pass != null);
981         try {
982             stdin.write(pass);
983             stdin.write("\n");
984             stdin.flush();
985             if (outputBuffer != null) {
986                 FastStringBuffer sb = new FastStringBuffer("==> ");
987                 for (int i = pass.length(); i-- > 0;)
988                     sb.append('*');
989                 sb.append('\n');
990                 writeToOutputBuffer(sb.toString());
991             }
992         }
993         catch (Exception JavaDoc e) {
994             Log.error(e);
995         }
996     }
997
998     private boolean write(String JavaDoc s)
999     {
1000        try {
1001            if (echo || Editor.preferences().getBooleanProperty(Property.SSH_ECHO))
1002                Log.debug("==> |" + s + "|");
1003            if (outputBuffer != null)
1004                writeToOutputBuffer("==> " + s);
1005            stdin.write(s);
1006            stdin.flush();
1007            return true;
1008        }
1009        catch (IOException JavaDoc e) {
1010            Log.error(e);
1011            killProcess();
1012            return false;
1013        }
1014    }
1015
1016    private void writeToOutputBuffer(final String JavaDoc s)
1017    {
1018        Runnable JavaDoc r = new Runnable JavaDoc() {
1019            public void run()
1020            {
1021                // Avoid race (and NPE) if setOutputBuffer(null) gets called in
1022
// another thread.
1023
final Buffer buf = outputBuffer;
1024                if (buf == null)
1025                    return;
1026                try {
1027                    buf.lockWrite();
1028                }
1029                catch (InterruptedException JavaDoc e) {
1030                    Log.debug(e);
1031                    return;
1032                }
1033                try {
1034                    buf.append(s);
1035                    buf.renumber();
1036                }
1037                finally {
1038                    buf.unlockWrite();
1039                }
1040                for (EditorIterator it = new EditorIterator(); it.hasNext();) {
1041                    Editor ed = it.nextEditor();
1042                    if (ed.getBuffer() == buf) {
1043                        ed.setDot(buf.getEnd());
1044                        ed.moveCaretToDotCol();
1045                        ed.setUpdateFlag(REPAINT);
1046                        ed.updateDisplay();
1047                    }
1048                }
1049            }
1050        };
1051        SwingUtilities.invokeLater(r);
1052    }
1053
1054    public void checkLogin()
1055    {
1056        if (userName == null)
1057            userName = System.getProperty("user.name");
1058        if (password == null) {
1059            for (BufferIterator it = new BufferIterator(); it.hasNext();) {
1060                Buffer buf = it.nextBuffer();
1061                if (buf.getFile() instanceof SshFile) {
1062                    SshFile f = (SshFile) buf.getFile();
1063                    if (f.hostName != null && f.hostName.equals(hostName)) {
1064                        if (f.getUserName() != null && f.getUserName().equals(userName)) {
1065                            password = f.getPassword();
1066                            break;
1067                        }
1068                    }
1069                }
1070            }
1071        }
1072    }
1073
1074    private static synchronized void cleanup()
1075    {
1076        // Walk buffer list in event dispatch thread.
1077
if (!SwingUtilities.isEventDispatchThread()) {
1078            Debug.bug();
1079            return;
1080        }
1081        if (sessionList != null) {
1082            for (int i = sessionList.size(); i-- > 0;) {
1083                SshSession session = (SshSession) sessionList.get(i);
1084                if (session.isLocked())
1085                    continue;
1086                String JavaDoc hostName = session.getHostName();
1087                boolean inUse = false;
1088                for (BufferIterator it = new BufferIterator(); it.hasNext();) {
1089                    Buffer buf = it.nextBuffer();
1090                    if (buf.getFile() instanceof SshFile) {
1091                        if (hostName.equals(buf.getFile().getHostName())) {
1092                            inUse = true;
1093                            break;
1094                        }
1095                    }
1096                }
1097                if (!inUse)
1098                    session.dispose();
1099            }
1100            if (sessionList.size() == 0) {
1101                sessionList = null;
1102                if (cleanupThread != null) {
1103                    cleanupThread.cancel();
1104                    cleanupThread = null;
1105                }
1106            }
1107        }
1108    }
1109
1110    private static final Runnable JavaDoc cleanupRunnable = new Runnable JavaDoc() {
1111        public void run()
1112        {
1113            cleanup();
1114        }
1115    };
1116
1117    private String JavaDoc stdOutFilter(String JavaDoc s)
1118    {
1119        return s;
1120    }
1121
1122    private synchronized void stdOutUpdate(final String JavaDoc s)
1123    {
1124        if (echo || Editor.preferences().getBooleanProperty(Property.SSH_ECHO))
1125            Log.debug("<== |" + s + "|");
1126        if (outputBuffer != null)
1127            writeToOutputBuffer(s);
1128        output.append(s);
1129        notify();
1130    }
1131
1132    private String JavaDoc stdErrFilter(String JavaDoc s)
1133    {
1134        return s;
1135    }
1136
1137    private void stdErrUpdate(final String JavaDoc s)
1138    {
1139        Log.debug("stderr: |" + s + "|");
1140    }
1141
1142    private static String JavaDoc reveal(String JavaDoc s)
1143    {
1144        FastStringBuffer sb = new FastStringBuffer();
1145        final int length = s.length();
1146        for (int i = 0; i < length; i++) {
1147            char c = s.charAt(i);
1148            switch (c) {
1149                case '\r':
1150                    sb.append("\\r");
1151                    break;
1152                case '\n':
1153                    sb.append("\\n");
1154                    break;
1155                case '\t':
1156                    sb.append("\\t");
1157                    break;
1158                default:
1159                    sb.append(c);
1160                    break;
1161            }
1162        }
1163        return sb.toString();
1164    }
1165
1166    class StdoutThread extends ReaderThread
1167    {
1168        // If this constructor is private, we run into jikes 1.15 bug #2256.
1169
StdoutThread()
1170        {
1171            super(process.getInputStream());
1172        }
1173
1174        public String JavaDoc filter(String JavaDoc s)
1175        {
1176            return stdOutFilter(s);
1177        }
1178
1179        public void update(String JavaDoc s)
1180        {
1181            stdOutUpdate(s);
1182        }
1183    }
1184
1185    class StderrThread extends ReaderThread
1186    {
1187        // If this constructor is private, we run into jikes 1.15 bug #2256.
1188
StderrThread()
1189        {
1190            super(process.getErrorStream());
1191        }
1192
1193        public String JavaDoc filter(String JavaDoc s)
1194        {
1195            return stdErrFilter(s);
1196        }
1197
1198        public void update(String JavaDoc s)
1199        {
1200            stdErrUpdate(s);
1201        }
1202    }
1203}
1204
Popular Tags