KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > fr > jayasoft > ivy > repository > ssh > Scp


1 package fr.jayasoft.ivy.repository.ssh;
2
3 import java.io.BufferedInputStream JavaDoc;
4 import java.io.BufferedOutputStream JavaDoc;
5 import java.io.File JavaDoc;
6 import java.io.FileInputStream JavaDoc;
7 import java.io.FileOutputStream JavaDoc;
8 import java.io.IOException JavaDoc;
9 import java.io.InputStream JavaDoc;
10 import java.io.OutputStream JavaDoc;
11
12 import com.jcraft.jsch.ChannelExec;
13 import com.jcraft.jsch.JSchException;
14 import com.jcraft.jsch.Session;
15 import com.jcraft.jsch.Channel;
16
17 /**
18  * This class is using the scp client to transfer data and information for the
19  * repository. It is based on the SCPClient from the ganymed ssh library from
20  * Christian Plattner. To minimize the dependency to the ssh library and because
21  * I needed some additional functionality, I decided to copy'n'paste the single
22  * class rather than to inherit or delegate it somehow. Nevertheless credit
23  * should go to the original author.
24  *
25  * @author Andreas Sahlbach
26  * @author Christian Plattner, plattner@inf.ethz.ch
27  */

28
29 public class Scp {
30     Session session;
31
32     public class FileInfo {
33         private String JavaDoc filename;
34         private long length;
35         private long lastModified;
36         /**
37          * @param filename The filename to set.
38          */

39         public void setFilename(String JavaDoc filename) {
40             this.filename = filename;
41         }
42         /**
43          * @return Returns the filename.
44          */

45         public String JavaDoc getFilename() {
46             return filename;
47         }
48         /**
49          * @param length The length to set.
50          */

51         public void setLength(long length) {
52             this.length = length;
53         }
54         /**
55          * @return Returns the length.
56          */

57         public long getLength() {
58             return length;
59         }
60         /**
61          * @param lastModified The lastModified to set.
62          */

63         public void setLastModified(long lastModified) {
64             this.lastModified = lastModified;
65         }
66         /**
67          * @return Returns the lastModified.
68          */

69         public long getLastModified() {
70             return lastModified;
71         }
72     }
73
74     public Scp(Session session) {
75         if (session == null)
76             throw new IllegalArgumentException JavaDoc("Cannot accept null argument!");
77         this.session = session;
78     }
79
80     private void readResponse(InputStream JavaDoc is) throws IOException JavaDoc, RemoteScpException {
81         int c = is.read();
82
83         if (c == 0)
84             return;
85
86         if (c == -1)
87             throw new RemoteScpException("Remote scp terminated unexpectedly.");
88
89         if ((c != 1) && (c != 2))
90             throw new RemoteScpException("Remote scp sent illegal error code.");
91
92         if (c == 2)
93             throw new RemoteScpException("Remote scp terminated with error.");
94
95         String JavaDoc err = receiveLine(is);
96         throw new RemoteScpException("Remote scp terminated with error (" + err + ").");
97     }
98
99     private String JavaDoc receiveLine(InputStream JavaDoc is) throws IOException JavaDoc,RemoteScpException {
100         StringBuffer JavaDoc sb = new StringBuffer JavaDoc(30);
101
102         while (true) {
103             /*
104              * This is a random limit - if your path names are longer, then
105              * adjust it
106              */

107
108             if (sb.length() > 8192)
109                 throw new RemoteScpException("Remote scp sent a too long line");
110
111             int c = is.read();
112
113             if (c < 0)
114                 throw new RemoteScpException("Remote scp terminated unexpectedly.");
115
116             if (c == '\n')
117                 break;
118
119             sb.append((char) c);
120
121         }
122         return sb.toString();
123     }
124
125     private void parseCLine(String JavaDoc line, FileInfo fileInfo) throws RemoteScpException {
126         /* Minimum line: "xxxx y z" ---> 8 chars */
127
128         long len;
129
130         if (line.length() < 8)
131             throw new RemoteScpException(
132                     "Malformed C line sent by remote SCP binary, line too short.");
133
134         if ((line.charAt(4) != ' ') || (line.charAt(5) == ' '))
135             throw new RemoteScpException("Malformed C line sent by remote SCP binary.");
136
137         int length_name_sep = line.indexOf(' ', 5);
138
139         if (length_name_sep == -1)
140             throw new RemoteScpException("Malformed C line sent by remote SCP binary.");
141
142         String JavaDoc length_substring = line.substring(5, length_name_sep);
143         String JavaDoc name_substring = line.substring(length_name_sep + 1);
144
145         if ((length_substring.length() <= 0) || (name_substring.length() <= 0))
146             throw new RemoteScpException("Malformed C line sent by remote SCP binary.");
147
148         if ((6 + length_substring.length() + name_substring.length()) != line
149                 .length())
150             throw new RemoteScpException("Malformed C line sent by remote SCP binary.");
151
152         try {
153             len = Long.parseLong(length_substring);
154         } catch (NumberFormatException JavaDoc e) {
155             throw new RemoteScpException(
156                     "Malformed C line sent by remote SCP binary, cannot parse file length.");
157         }
158
159         if (len < 0)
160             throw new RemoteScpException(
161                     "Malformed C line sent by remote SCP binary, illegal file length.");
162
163         fileInfo.setLength(len);
164         fileInfo.setFilename(name_substring);
165     }
166     
167     private void parseTLine(String JavaDoc line, FileInfo fileInfo) throws RemoteScpException {
168         /* Minimum line: "0 0 0 0" ---> 8 chars */
169
170         long modtime;
171         long first_msec;
172         long atime;
173         long second_msec;
174
175         if (line.length() < 8)
176             throw new RemoteScpException(
177                     "Malformed T line sent by remote SCP binary, line too short.");
178
179         int first_msec_begin = line.indexOf(" ")+1;
180         if(first_msec_begin == 0 || first_msec_begin >= line.length())
181             throw new RemoteScpException(
182             "Malformed T line sent by remote SCP binary, line not enough data.");
183        
184         int atime_begin = line.indexOf(" ",first_msec_begin+1)+1;
185         if(atime_begin == 0 || atime_begin >= line.length())
186             throw new RemoteScpException(
187             "Malformed T line sent by remote SCP binary, line not enough data.");
188         
189         int second_msec_begin = line.indexOf(" ",atime_begin+1)+1;
190         if(second_msec_begin == 0 || second_msec_begin >= line.length())
191             throw new RemoteScpException(
192             "Malformed T line sent by remote SCP binary, line not enough data.");
193        
194         try {
195             modtime = Long.parseLong(line.substring(0,first_msec_begin-1));
196             first_msec = Long.parseLong(line.substring(first_msec_begin,atime_begin-1));
197             atime = Long.parseLong(line.substring(atime_begin,second_msec_begin-1));
198             second_msec = Long.parseLong(line.substring(second_msec_begin));
199         } catch (NumberFormatException JavaDoc e) {
200             throw new RemoteScpException(
201                     "Malformed C line sent by remote SCP binary, cannot parse file length.");
202         }
203
204         if (modtime < 0 || first_msec < 0 || atime < 0 || second_msec < 0)
205             throw new RemoteScpException(
206                     "Malformed C line sent by remote SCP binary, illegal file length.");
207
208         fileInfo.setLastModified(modtime);
209     }
210     
211     private void sendBytes(Channel channel, byte[] data, String JavaDoc fileName,
212             String JavaDoc mode) throws IOException JavaDoc,RemoteScpException {
213         OutputStream JavaDoc os = channel.getOutputStream();
214         InputStream JavaDoc is = new BufferedInputStream JavaDoc(channel.getInputStream(), 512);
215
216         try {
217             if(channel.isConnected())
218                 channel.start();
219             else
220                 channel.connect();
221         } catch (JSchException e1) {
222             throw (IOException JavaDoc) new IOException JavaDoc("Channel connection problems").initCause(e1);
223         }
224        
225         readResponse(is);
226
227         String JavaDoc cline = "C" + mode + " " + data.length + " " + fileName + "\n";
228
229         os.write(cline.getBytes());
230         os.flush();
231
232         readResponse(is);
233
234         os.write(data, 0, data.length);
235         os.write(0);
236         os.flush();
237
238         readResponse(is);
239
240         os.write("E\n".getBytes());
241         os.flush();
242     }
243
244     private void sendFile(Channel channel, String JavaDoc localFile, String JavaDoc remoteName, String JavaDoc mode)
245             throws IOException JavaDoc, RemoteScpException {
246         byte[] buffer = new byte[8192];
247
248         OutputStream JavaDoc os = new BufferedOutputStream JavaDoc(channel.getOutputStream(), 40000);
249         InputStream JavaDoc is = new BufferedInputStream JavaDoc(channel.getInputStream(), 512);
250
251         try {
252             if(channel.isConnected())
253                 channel.start();
254             else
255                 channel.connect();
256         } catch (JSchException e1) {
257             throw (IOException JavaDoc) new IOException JavaDoc("Channel connection problems").initCause(e1);
258         }
259
260         readResponse(is);
261
262         File JavaDoc f = new File JavaDoc(localFile);
263         long remain = f.length();
264
265         String JavaDoc cline = "C" + mode + " " + remain + " " + remoteName + "\n";
266
267         os.write(cline.getBytes());
268         os.flush();
269
270         readResponse(is);
271
272         FileInputStream JavaDoc fis = null;
273
274         try {
275             fis = new FileInputStream JavaDoc(f);
276
277             while (remain > 0) {
278                 int trans;
279                 if (remain > buffer.length)
280                     trans = buffer.length;
281                 else
282                     trans = (int) remain;
283
284                 if (fis.read(buffer, 0, trans) != trans)
285                     throw new IOException JavaDoc(
286                             "Cannot read enough from local file "
287                                     + localFile);
288
289                 os.write(buffer, 0, trans);
290
291                 remain -= trans;
292             }
293
294             fis.close();
295         } catch (IOException JavaDoc e) {
296             if (fis != null) {
297                 fis.close();
298             }
299             throw (e);
300         }
301
302         os.write(0);
303         os.flush();
304
305         readResponse(is);
306
307         os.write("E\n".getBytes());
308         os.flush();
309     }
310
311     /**
312      * Receive a file via scp and store it in a stream
313      * @param channel ssh channel to use
314      * @param file to receive from remote
315      * @param target to store file into (if null, get only file info)
316      * @return file information of the file we received
317      * @throws IOException in case of network or protocol trouble
318      * @throws RemoteScpException in case of problems on the target system (connection is fine)
319      */

320     private FileInfo receiveStream(Channel channel, String JavaDoc file, OutputStream JavaDoc targetStream)
321       throws IOException JavaDoc,RemoteScpException {
322         byte[] buffer = new byte[8192];
323
324         OutputStream JavaDoc os = channel.getOutputStream();
325         InputStream JavaDoc is = channel.getInputStream();
326         try {
327             if(channel.isConnected())
328                 channel.start();
329             else
330                 channel.connect();
331         } catch (JSchException e1) {
332             throw (IOException JavaDoc) new IOException JavaDoc("Channel connection problems").initCause(e1);
333         }
334         os.write(0x0);
335         os.flush();
336
337         FileInfo fileInfo = new FileInfo();
338
339         while (true) {
340             int c = is.read();
341             if (c < 0)
342                 throw new RemoteScpException("Remote scp terminated unexpectedly.");
343
344             String JavaDoc line = receiveLine(is);
345
346             if (c == 'T') {
347                 parseTLine(line,fileInfo);
348                 os.write(0x0);
349                 os.flush();
350                 continue;
351             }
352             if ((c == 1) || (c == 2))
353                 throw new RemoteScpException("Remote SCP error: " + line);
354
355             if (c == 'C') {
356                 parseCLine(line,fileInfo);
357                 break;
358             }
359             throw new RemoteScpException("Remote SCP error: " + ((char) c) + line);
360         }
361         if(targetStream != null) {
362             
363             os.write(0x0);
364             os.flush();
365     
366             try {
367                 long remain = fileInfo.getLength();
368     
369                 while (remain > 0) {
370                     int trans;
371                     if (remain > buffer.length)
372                         trans = buffer.length;
373                     else
374                         trans = (int) remain;
375     
376                     int this_time_received = is.read(buffer, 0, trans);
377     
378                     if (this_time_received < 0) {
379                         throw new IOException JavaDoc(
380                                 "Remote scp terminated connection unexpectedly");
381                     }
382     
383                     targetStream.write(buffer, 0, this_time_received);
384     
385                     remain -= this_time_received;
386                 }
387     
388                 targetStream.close();
389             } catch (IOException JavaDoc e) {
390                 if (targetStream != null)
391                     targetStream.close();
392     
393                 throw (e);
394             }
395
396             readResponse(is);
397     
398             os.write(0x0);
399             os.flush();
400         }
401         return fileInfo;
402     }
403
404     /**
405      * Copy a local file to a remote directory, uses mode 0600 when creating the
406      * file on the remote side.
407      *
408      * @param localFile Path and name of local file.
409      * @param remoteTargetDirectory Remote target directory where the file has to end up (optional)
410      * @param remoteName target filename to use
411      * @throws IOException in case of network problems
412      * @throws RemoteScpException in case of problems on the target system (connection ok)
413      */

414     public void put(String JavaDoc localFile, String JavaDoc remoteTargetDirectory, String JavaDoc remoteName)
415     throws IOException JavaDoc, RemoteScpException {
416         put(localFile, remoteTargetDirectory, remoteName, "0600");
417     }
418
419     /**
420      * Create a remote file and copy the contents of the passed byte array into
421      * it. Uses mode 0600 for creating the remote file.
422      *
423      * @param data the data to be copied into the remote file.
424      * @param remoteFileName The name of the file which will be created in the remote target directory.
425      * @param remoteTargetDirectory Remote target directory where the file has to end up (optional)
426      * @throws IOException in case of network problems
427      * @throws RemoteScpException in case of problems on the target system (connection ok)
428      */

429
430     public void put(byte[] data, String JavaDoc remoteFileName, String JavaDoc remoteTargetDirectory)
431     throws IOException JavaDoc,RemoteScpException {
432         put(data, remoteFileName, remoteTargetDirectory, "0600");
433     }
434
435     /**
436      * Create a remote file and copy the contents of the passed byte array into
437      * it. The method use the specified mode when creating the file on the
438      * remote side.
439      *
440      * @param data the data to be copied into the remote file.
441      * @param remoteFileName The name of the file which will be created in the remote target directory.
442      * @param remoteTargetDirectory Remote target directory where the file has to end up (optional)
443      * @param mode a four digit string (e.g., 0644, see "man chmod", "man open")
444      * @throws IOException in case of network problems
445      * @throws RemoteScpException in case of problems on the target system (connection ok)
446      */

447     public void put(byte[] data, String JavaDoc remoteFileName, String JavaDoc remoteTargetDirectory, String JavaDoc mode)
448     throws IOException JavaDoc,RemoteScpException {
449         ChannelExec channel = null;
450
451         if ((remoteFileName == null) || (mode == null))
452             throw new IllegalArgumentException JavaDoc("Null argument.");
453
454         if (mode.length() != 4)
455             throw new IllegalArgumentException JavaDoc("Invalid mode.");
456
457         for (int i = 0; i < mode.length(); i++)
458             if (Character.isDigit(mode.charAt(i)) == false)
459                 throw new IllegalArgumentException JavaDoc("Invalid mode.");
460
461         String JavaDoc cmd = "scp -t ";
462         if(remoteTargetDirectory != null && remoteTargetDirectory.length() > 0) {
463             cmd = cmd + "-d " + remoteTargetDirectory;
464         }
465
466         try {
467             channel = getExecChannel();
468             channel.setCommand(cmd);
469             sendBytes(channel, data, remoteFileName, mode);
470             //channel.disconnect();
471
} catch (JSchException e) {
472             if (channel != null)
473                 channel.disconnect();
474             throw (IOException JavaDoc) new IOException JavaDoc("Error during SCP transfer."+e.getMessage())
475                     .initCause(e);
476         }
477     }
478
479     /**
480      * @return
481      * @throws JSchException
482      */

483     private ChannelExec getExecChannel() throws JSchException {
484         ChannelExec channel;
485         channel = (ChannelExec)session.openChannel("exec");
486         return channel;
487     }
488
489     /**
490      * Copy a local file to a remote site, uses the specified mode when
491      * creating the file on the remote side.
492      *
493      * @param localFile Path and name of local file.
494      * @param remoteTargetDir Remote target directory where the file has to end up (optional)
495      * @param remoteTargetName file name to use on the target system
496      * @param mode a four digit string (e.g., 0644, see "man chmod", "man open")
497      * @throws IOException in case of network problems
498      * @throws RemoteScpException in case of problems on the target system (connection ok)
499      */

500     public void put(String JavaDoc localFile, String JavaDoc remoteTargetDir, String JavaDoc remoteTargetName, String JavaDoc mode)
501     throws IOException JavaDoc,RemoteScpException {
502         ChannelExec channel = null;
503
504         if ((localFile == null) || (remoteTargetName == null) || (mode == null))
505             throw new IllegalArgumentException JavaDoc("Null argument.");
506
507         if (mode.length() != 4)
508             throw new IllegalArgumentException JavaDoc("Invalid mode.");
509
510         for (int i = 0; i < mode.length(); i++)
511             if (Character.isDigit(mode.charAt(i)) == false)
512                 throw new IllegalArgumentException JavaDoc("Invalid mode.");
513            
514         String JavaDoc cmd = "scp -t ";
515         if(remoteTargetDir != null && remoteTargetDir.length() > 0) {
516             cmd = cmd + "-d " + remoteTargetDir;
517         }
518
519         try {
520             channel = getExecChannel();
521             channel.setCommand(cmd);
522             sendFile(channel, localFile, remoteTargetName, mode);
523             channel.disconnect();
524         } catch (JSchException e) {
525             if (channel != null)
526                 channel.disconnect();
527             throw (IOException JavaDoc) new IOException JavaDoc("Error during SCP transfer."+e.getMessage())
528                     .initCause(e);
529         }
530     }
531
532     /**
533      * Download a file from the remote server to a local file.
534      * @param remoteFile Path and name of the remote file.
535      * @param localTarget Local file where to store the data.
536      * @throws IOException in case of network problems
537      * @throws RemoteScpException in case of problems on the target system (connection ok)
538      */

539     public void get(String JavaDoc remoteFile, String JavaDoc localTarget) throws IOException JavaDoc,RemoteScpException {
540         File JavaDoc f = new File JavaDoc(localTarget);
541         FileOutputStream JavaDoc fop = new FileOutputStream JavaDoc(f);
542         get(remoteFile,fop);
543     }
544     
545     /**
546      * Download a file from the remote server into an OutputStream
547      * @param remoteFile Path and name of the remote file.
548      * @param localTarget OutputStream to store the data.
549      * @throws IOException in case of network problems
550      * @throws RemoteScpException in case of problems on the target system (connection ok)
551      */

552     public void get(String JavaDoc remoteFile, OutputStream JavaDoc localTarget) throws IOException JavaDoc,RemoteScpException {
553         ChannelExec channel = null;
554
555         if ((remoteFile == null) || (localTarget == null))
556             throw new IllegalArgumentException JavaDoc("Null argument.");
557
558         String JavaDoc cmd = "scp -p -f "+ remoteFile;
559
560         try {
561             channel = getExecChannel();
562             channel.setCommand(cmd);
563             receiveStream(channel, remoteFile, localTarget);
564             channel.disconnect();
565         } catch (JSchException e) {
566             if (channel != null)
567                 channel.disconnect();
568             throw (IOException JavaDoc) new IOException JavaDoc("Error during SCP transfer."+e.getMessage())
569                     .initCause(e);
570         }
571     }
572     
573     /**
574      * Initiates an SCP sequence but stops after getting fileinformation header
575      * @param remoteFile to get information for
576      * @return the file information got
577      * @throws IOException in case of network problems
578      * @throws RemoteScpException in case of problems on the target system (connection ok)
579      */

580     public FileInfo getFileinfo(String JavaDoc remoteFile) throws IOException JavaDoc,RemoteScpException {
581         ChannelExec channel = null;
582         FileInfo fileInfo = null;
583         
584         if (remoteFile == null)
585             throw new IllegalArgumentException JavaDoc("Null argument.");
586
587         String JavaDoc cmd = "scp -p -f \""+remoteFile+"\"";
588
589         try {
590             channel = getExecChannel();
591             channel.setCommand(cmd);
592             fileInfo = receiveStream(channel, remoteFile, null);
593             channel.disconnect();
594         } catch (JSchException e) {
595             throw (IOException JavaDoc) new IOException JavaDoc("Error during SCP transfer."+e.getMessage())
596             .initCause(e);
597         } finally {
598             if (channel != null)
599               channel.disconnect();
600         }
601         return fileInfo;
602     }
603 }
604
Popular Tags