001/*
002 * Copyright (c) 2009-2012 The Regents of the University of California.
003 * All rights reserved.
004 *
005 * '$Author: crawl $'
006 * '$Date: 2012-11-26 22:19:36 +0000 (Mon, 26 Nov 2012) $' 
007 * '$Revision: 31113 $'
008 * 
009 * Permission is hereby granted, without written agreement and without
010 * license or royalty fees, to use, copy, modify, and distribute this
011 * software and its documentation for any purpose, provided that the above
012 * copyright notice and the following two paragraphs appear in all copies
013 * of this software.
014 *
015 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
016 * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
017 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
018 * THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
019 * SUCH DAMAGE.
020 *
021 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
022 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
023 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
024 * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
025 * CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
026 * ENHANCEMENTS, OR MODIFICATIONS.
027 *
028 */
029
030package org.sdm.spa.actors.transport;
031
032import java.io.ByteArrayOutputStream;
033import java.io.File;
034import java.util.ArrayList;
035import java.util.List;
036
037import org.apache.commons.logging.Log;
038import org.apache.commons.logging.LogFactory;
039import org.kepler.actor.ssh.SshSession;
040import org.kepler.ssh.ExecException;
041import org.kepler.ssh.SshException;
042import org.kepler.ssh.SshExec;
043import org.sdm.spa.actors.transport.vo.ConnectionDetails;
044
045import com.jcraft.jsch.Session;
046
047/**
048 * Copies files using SCP protocol. Uses the SshExec class to run the scp
049 * command.
050 * <P>
051 * Copy operation will overwrite existing files by default. Copy between a local
052 * host and a remote host ignores the variables forcedCleanup, and timeout.
053 * <P>
054 * When transferring files between local machine and a remote machine, if the
055 * remote machine does not have scp in standard path, you can set it using the
056 * variables protocolPathSrc/protocolPathDest.
057 * <P>
058 * When transferring files between two remote machines, the scp command is run
059 * from the source machine. Is scp is not in standard path on source machine,
060 * set the path in protocolPathSrc variable. If this variable is not set, class
061 * attempts to search in the most common locations - /usr/bin, /bin,
062 * /usr/local/bin, . , and ~
063 * <P>
064 * 
065 * @author Chandrika Sivaramakrishnan
066 * 
067 */
068public class ScpCopier extends FileCopierBase {
069
070        // /////////////////////Private Variables/////////////////////////////////
071        private static final Log log = LogFactory.getLog(ScpCopier.class.getName());
072        private static final boolean isDebugging = log.isDebugEnabled();
073        protected SshSession session;
074        protected Session jschSession;
075
076        // //////////////////Protected Methods////////////////////////////////////
077
078        @Override
079        // Anusua Change - Starts
080        /**
081         * Copy file from a remote source to local destination
082         */
083        protected CopyResult copyFrom(ConnectionDetails srcDetails, String srcFile,
084                        String destFile, boolean recursive) {
085                StringBuffer warn = new StringBuffer(100);
086
087                try {
088                        int count = 0;
089                        String srcFileList = "";
090                        SshExec sshObject = new SshExec(srcDetails.getUser(), srcDetails
091                                        .getHost(), srcDetails.getPort());
092                        sshObject.setTimeout(getTimeout(), false, false);
093                        sshObject.setProtocolPath(protocolPathSrc);
094                        sshObject.setcmdLineOptions(cmdLineOptions);
095                        File destFileObj = new File(destFile);
096                        boolean warn_flag = false;
097
098                        // Transfer of multiple files to directory level
099                        if (srcFile.contains(",")) {
100
101                                String[] srcFile_list = srcFile.split(",");
102                                for (int i = 0; i < srcFile_list.length; i++) {
103                                        srcFile_list[i] = srcFile_list[i].trim();
104                                        if (srcFile_list[i].startsWith("/")) {
105                                                if (srcFile_list[i].contains("*")
106                                                                || srcFile_list[i].contains("+")) {
107                                                        // add the list of files matching wildcard to
108                                                        // srcfilelist
109                                                        srcFileList += srcFile_list[i] + " ";
110                                                        // count = sshObject.copyFrom(srcFile_list[i],
111                                                        // destFileObj, recursive);
112                                                } else {
113                                                        //escape spaces and other special characters in filename
114                                                        srcFileList += "\"" + srcFile_list[i] + "\" ";
115                                                }
116                                        } else {
117                                                warn.append(srcFile_list[i] + " ");
118                                                if (!warn_flag)
119                                                        warn_flag = true;
120                                                // Anand: replaced warn_flag with return error statement
121                                                // return new CopyResult(1,"",srcFile_list[i]
122                                                // +" does not contain full path to the file. Please provide full path. ");
123                                        }
124                                }
125                                if (warn_flag) {
126                                        warn
127                                                        .append(" does not contain full path to the source file. Please provide full path. ");
128                                }
129
130                                count = sshObject.copyFrom(srcFileList, destFileObj, recursive);
131
132                        } else { // Anand: single file case
133                                srcFile = srcFile.trim();
134                                if (srcFile.startsWith("/")) {
135                                        // pass the source file in quotes
136                                        if (!(srcFile.contains("*") || srcFile.contains("+"))) {
137                                                srcFile = "\"" + srcFile + "\"";
138                                        }
139                                        count = sshObject.copyFrom(srcFile, destFileObj, recursive);
140                                } else {
141                                        warn
142                                                        .append(srcFile
143                                                                        + " does not contain full path to the file. Please provide full path. ");
144                                }
145                        }
146                        if (count <= 0) {
147                                warn_flag = true;
148                                warn
149                                                .append("\n No files were copied. Please check your input!");
150                        }
151                        // Anusua - end
152                        if (count > 0 && !warn_flag) {
153                                // return success if copyFrom completed without exceptionc
154                                return new CopyResult(0, "", warn.toString());
155                        }
156
157                        return new CopyResult(1, warn.toString(), "");
158
159                } catch (Exception e) {
160                        log.error(e);
161                        return new CopyResult(1, e.getMessage(), warn.toString());
162                }
163
164        }
165
166        @Override
167        protected CopyResult copyTo(String srcFile, ConnectionDetails destDetails,
168                        String destFile, boolean recursive) throws SshException {
169
170                try {
171                        int count = 0;
172                        SshExec sshObject = new SshExec(destDetails.getUser(), destDetails
173                                        .getHost(), destDetails.getPort());
174                        if (isDebugging)
175                                log.debug("SshExec object created");
176                        sshObject.setTimeout(getTimeout(), false, false);
177                        sshObject.setProtocolPath(protocolPathDest);
178                        sshObject.setcmdLineOptions(cmdLineOptions);
179                        
180                        log.debug("******* SCPCopier :: CopyTo called********");
181
182                        
183
184                        // Transfer of multiple files to directory level
185                        if (srcFile.contains(",")) {
186                                log.debug("***** Detected file list *******");
187
188                                String[] srcFile_list = srcFile.split(",");
189                                //Modified by - Chandrika - moved path check to parent  class
190                                List<File> srcFile_List = new ArrayList<File>();
191                                for (int i = 0; i < srcFile_list.length; i++) {
192                                        srcFile_List.add(new File(srcFile_list[i]));
193                                }
194                                //call multiple file copyTo method
195                                count = sshObject.copyTo(srcFile_List, destFile, recursive); 
196                        } else {
197                                // Transfer of single file to directory level or individual file
198                                // level
199                                // The first predicate applies to Windows; the selected offset
200                                // assumes that
201                                // drive identifiers will be 1 character long.
202                                srcFile = srcFile.trim();
203                                
204                                //Modified by Chandrika S - Moved relative path check to parent class
205                                File srcFileObj = new File(srcFile);
206
207                                // Anand: If file exists - initiate copy
208                                count = sshObject.copyTo(srcFileObj, destFile, recursive);
209                        }
210                        
211                        if (count > 0) {
212                                // return success if copyFrom completed without exceptions
213                                return new CopyResult();
214                        }
215                        return new CopyResult(
216                                        1,
217                                        "Unknown Error. No files where copied. Please check the input provided",
218                                        "");
219
220                } catch (Exception e) {
221                        log.error(e);
222                        e.printStackTrace();
223                        return new CopyResult(1, e.getMessage(), "");
224                }
225        }
226
227        @Override
228        protected CopyResult copyRemote(ConnectionDetails srcDetails,
229                        String srcFile, ConnectionDetails destDetails, String destFile,
230                        boolean recursive) throws ExecException {
231                // Anand: Passing list of files in case of remote copy from destination
232                // to source does not work
233                // Reason: When we specify a list of files on remote destination the
234                // command format looks like:
235                // scp srcDetails:/filePath1 srcDetails:/filePath2
236                // /LocalfilePathDestination
237                // for each of the files in the list SCP sends a password request.
238                // the function exectueCommand (which executes commands on remote
239                // machine handles only the
240                // first password request, and the program hangs for preceding password
241                // requests.
242
243                if (isDebugging)
244                        log.debug("remote scp copy");
245                ByteArrayOutputStream cmdStdout = new ByteArrayOutputStream();
246                ByteArrayOutputStream cmdStderr = new ByteArrayOutputStream();
247                String remoteHostStr = "";
248                String srcFileList = "";
249
250                SshExec sshObject = null;
251                if (srcDetails.isConnectionOrigin()) {
252                        sshObject = new SshExec(srcDetails.getUser(), srcDetails.getHost(),
253                                        srcDetails.getPort());
254                        remoteHostStr = destDetails.toString();
255
256                } else {
257                        sshObject = new SshExec(destDetails.getUser(), destDetails
258                                        .getHost(), destDetails.getPort());
259                        remoteHostStr = srcDetails.toString();
260                }
261
262                sshObject.setTimeout(timeout, false, false);
263                sshObject.setForcedCleanUp(forcedCleanup);
264
265                try {
266                        // scp needs pseudo terminal enabled, so that password request is
267                        // sent to
268                        // stdout instead of terminal
269                        sshObject.setPseudoTerminal(true);
270                        StringBuffer cmd = new StringBuffer(100);
271                        String cmdWithPath;
272                        int exitCode = 0;
273
274                        if (srcDetails.isConnectionOrigin()) {
275                                // escape spaces in file names
276                                if (srcFile.contains(",")) {
277                                        // list of files
278                                        String[] srcFile_list = srcFile.split(",");
279                                        for (int i = 0; i < srcFile_list.length; i++) {
280                                                // Anand: wild card cannot be processed in SCP if they
281                                                // are enclosed in ""
282                                                if (srcFile_list[i].contains("*")
283                                                                || srcFile_list[i].contains("*"))
284                                                        srcFileList += srcFile_list[i].trim() + " ";
285                                                else
286                                                        srcFileList += "\"" + srcFile_list[i].trim() + "\""
287                                                                        + " ";
288                                        }
289                                } else {
290                                        // Single file
291                                        if (srcFile.contains("*") || srcFile.contains("*"))
292                                                srcFileList = srcFile.trim() + " ";
293                                        else
294                                                srcFileList = "\"" + srcFile.trim() + "\"" + " ";
295                                }
296
297                                // Anand: Following code can go in function
298                                // buildCommandForRemoteCopy(src, dest);
299                                // just swap src, dest parameters for 2 cases
300                                cmd.append("scp -P ");
301                                cmd.append(destDetails.getPort());
302                                if (recursive) {
303                                        cmd.append(" -r ");
304                                } else {
305                                        cmd.append("  ");
306                                }
307                                cmd.append(cmdLineOptions);
308                                cmd.append("  ");
309                                cmd.append(srcFileList);
310                                cmd.append("  ");
311                                cmd.append(destDetails.getUser());
312                                cmd.append("@");
313                                cmd.append(destDetails.getHost());
314                                cmd.append(":");
315                                cmd.append(destFile);
316                                if (protocolPathSrc.equals("")) {
317                                        cmdWithPath = getCmdWithDefaultPath(cmd);
318                                } else {
319                                        cmdWithPath = protocolPathSrc + cmd;
320                                }
321                                if (isDebugging)
322                                        log.debug("remote copy cmd=" + cmdWithPath);
323
324                                System.out
325                                                .println("*************remote command " + cmdWithPath);
326
327                                exitCode = sshObject.executeCmd(cmdWithPath, cmdStdout,
328                                                cmdStderr, remoteHostStr);
329
330                                log.debug("ExitCode:" + exitCode);
331                                log.debug("stdout:" + cmdStdout);
332                                log.debug("stderr:" + cmdStderr);
333
334                                String message = cmdStderr.toString();
335                                if (message == null || message.trim().equals("")) {
336                                        message = cmdStdout.toString();
337                                }
338                                return new CopyResult(exitCode, message, "");
339                        } else {
340                                // Destination is Origin
341                                // Anand: We need execute each file in a loop
342                                StringBuffer warn = new StringBuffer(100);
343                                boolean warn_flag = false;
344                                String message = "";
345                                if (srcFile.contains(",")) {
346                                        // list of files
347                                        String[] srcFile_list = srcFile.split(",");
348                                        for (int i = 0; i < srcFile_list.length; i++) {
349                                                // skip spaces in file names
350                                                // Anand: look up replaceAll for more info on why to use
351                                                // \\\\ instead of \\
352                                                srcFile_list[i] = srcFile_list[i].trim().replaceAll(
353                                                                " ", "\\\\ ");
354                                                System.out.println("*********************file:"
355                                                                + srcFile_list[i]);
356                                                if (srcFile_list[i].contains("*")
357                                                                || srcFile_list[i].contains("*"))
358                                                        srcFileList = srcDetails.getUser() + "@"
359                                                                        + srcDetails.getHost() + ":"
360                                                                        + srcFile_list[i] + " ";
361                                                else
362                                                        srcFileList = srcDetails.getUser() + "@"
363                                                                        + srcDetails.getHost() + ":" + "\""
364                                                                        + srcFile_list[i] + "\"" + " ";
365                                                cmd = buildSCPRemoteDestinationCommand(srcFileList,
366                                                                destDetails.getPort(), destFile, recursive);
367                                                if (protocolPathDest.equals("")) {
368                                                        cmdWithPath = getCmdWithDefaultPath(cmd);
369                                                } else {
370                                                        cmdWithPath = protocolPathDest + cmd;
371                                                }
372
373                                                if (isDebugging)
374                                                        log.debug("remote copy cmd=" + cmdWithPath);
375
376                                                System.out.println("*************remote command "
377                                                                + cmdWithPath);
378
379                                                exitCode = sshObject.executeCmd(cmdWithPath, cmdStdout,
380                                                                cmdStderr, remoteHostStr);
381
382                                                log.debug("ExitCode:" + exitCode);
383                                                log.debug("stdout:" + cmdStdout);
384                                                log.debug("stderr:" + cmdStderr);
385
386                                                if (exitCode != 0) {
387                                                        warn.append("Could not copy file " + srcFileList
388                                                                        + ".\n");
389                                                        warn_flag = true;
390                                                }
391                                                message = cmdStderr.toString();
392                                                if (message == null || message.trim().equals("")) {
393                                                        message = cmdStdout.toString();
394                                                }
395                                        }
396                                        if (warn_flag)
397                                                return new CopyResult(1, warn.toString(), "");
398                                        else
399                                                return new CopyResult(0, message, "");
400                                } else {
401                                        srcFile = srcFile.trim().replaceAll(" ", "\\\\ ");
402                                        // Anand: single file
403                                        if (srcFile.contains("*") || srcFile.contains("*"))
404                                                srcFileList = srcDetails.getUser() + "@"
405                                                                + srcDetails.getHost() + ":" + srcFile.trim()
406                                                                + " ";
407                                        else
408                                                srcFileList = srcDetails.getUser() + "@"
409                                                                + srcDetails.getHost() + ":" + "\""
410                                                                + srcFile.trim() + "\"" + " ";
411                                        cmd = buildSCPRemoteDestinationCommand(srcFileList,
412                                                        destDetails.getPort(), destFile, recursive);
413                                        if (protocolPathDest.equals("")) {
414                                                cmdWithPath = getCmdWithDefaultPath(cmd);
415                                        } else {
416                                                cmdWithPath = protocolPathDest + cmd;
417                                        }
418                                        // Anand: Code till here goes into function
419
420                                        if (isDebugging)
421                                                log.debug("remote copy cmd=" + cmdWithPath);
422
423                                        System.out.println("*************remote command "
424                                                        + cmdWithPath);
425
426                                        exitCode = sshObject.executeCmd(cmdWithPath, cmdStdout,
427                                                        cmdStderr, remoteHostStr);
428
429                                        log.debug("ExitCode:" + exitCode);
430                                        log.debug("stdout:" + cmdStdout);
431                                        log.debug("stderr:" + cmdStderr);
432
433                                        message = cmdStderr.toString();
434                                        if (message == null || message.trim().equals("")) {
435                                                message = cmdStdout.toString();
436                                        }
437                                        return new CopyResult(exitCode, message, "");
438                                }
439                        }
440
441                } catch (Exception e) {
442                        return new CopyResult(1, e.getMessage(), "");
443                }
444
445        }
446
447        public StringBuffer buildSCPRemoteDestinationCommand(String fileName,
448                        int destPort, String destFile, boolean recursive) {
449                StringBuffer cmd = new StringBuffer(100);
450                cmd.append("scp -P ");
451                cmd.append(destPort);
452                if (recursive) {
453                        cmd.append(" -r ");
454                } else {
455                        cmd.append("  ");
456                }
457                cmd.append(cmdLineOptions);
458                cmd.append("  ");
459                cmd.append(fileName);
460                cmd.append(destFile);
461                return cmd;
462        }
463
464        // NEW METHOD ADDED BY ANUSUA
465        protected boolean isScpPresent(ConnectionDetails srcDetails,
466                        ConnectionDetails destDetails) {
467                StringBuffer cmd = new StringBuffer(100);
468                ByteArrayOutputStream cmdStdout = new ByteArrayOutputStream();
469                ByteArrayOutputStream cmdStderr = new ByteArrayOutputStream();
470                SshExec sshObject = null;
471                String cmdWithPath = null;
472                String remoteHostStr = "";
473                boolean isScpPresent = false;
474                int exitCode = 0;
475
476                if (srcDetails.isLocal() || destDetails.isLocal()) {
477                        isScpPresent = true; // Comment - localhost always has scp api
478                                                                        // present
479                } else {
480                        if ((!(srcDetails.isLocal())) && (!(destDetails.isLocal()))) { // Comment
481                                                                                                                                                        // -
482                                                                                                                                                        // check
483                                                                                                                                                        // for
484                                                                                                                                                        // remote
485                                                                                                                                                        // to
486                                                                                                                                                        // remote
487                                                                                                                                                        // connection
488                                sshObject = new SshExec(srcDetails.getUser(), srcDetails
489                                                .getHost(), srcDetails.getPort());
490                                remoteHostStr = destDetails.toString();
491                                sshObject.setTimeout(1000, false, false);
492                                try {
493                                        cmd.append("which scp");
494                                        cmdWithPath = getCmdWithDefaultPath(cmd);
495
496                                        exitCode = sshObject.executeCmd(cmdWithPath, cmdStdout,
497                                                        cmdStderr, remoteHostStr);
498                                        String message = cmdStderr.toString();
499                                        if (message == null || message.trim().equals("")) {
500                                                message = cmdStdout.toString();
501                                                isScpPresent = true;
502                                        }
503                                } catch (ExecException e) {
504                                        log.error("Exception from execution:" + e);
505                                } catch (Exception e) {
506                                        log.error("Exception from isScpPresent():" + e);
507
508                                }
509                        }
510                }
511
512                return isScpPresent;
513        }
514
515        @Override
516        protected int getDefaultPort() {
517                return 22;
518        }
519
520}