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}