From 21ba529fbecee26c28ade823c8509f2c180706ce Mon Sep 17 00:00:00 2001
From: Chris Church <chris@ninemoreminutes.com>
Date: Wed, 18 Jun 2014 10:01:11 -0500
Subject: [PATCH] Fixes/notes related to slashes in remote paths.

---
 lib/ansible/runner/action_plugins/copy.py      | 12 ++++++------
 lib/ansible/runner/action_plugins/fetch.py     |  2 +-
 lib/ansible/runner/action_plugins/template.py  |  2 +-
 lib/ansible/runner/action_plugins/unarchive.py |  2 +-
 lib/ansible/runner/shell_plugins/powershell.py |  4 ++++
 lib/ansible/runner/shell_plugins/sh.py         |  3 +++
 6 files changed, 16 insertions(+), 9 deletions(-)

diff --git a/lib/ansible/runner/action_plugins/copy.py b/lib/ansible/runner/action_plugins/copy.py
index d84b3f5..c59042f 100644
--- a/lib/ansible/runner/action_plugins/copy.py
+++ b/lib/ansible/runner/action_plugins/copy.py
@@ -136,8 +136,8 @@ class ActionModule(object):
 
             # If it's recursive copy, destination is always a dir,
             # explicitly mark it so (note - copy module relies on this).
-            if not dest.endswith("/"):
-                dest += "/"
+            if not conn.shell.path_has_trailing_slash(dest):
+                dest = conn.shell.join_path(dest, '')
         else:
             source_files.append((source, os.path.basename(source)))
 
@@ -169,10 +169,10 @@ class ActionModule(object):
             # This is kind of optimization - if user told us destination is
             # dir, do path manipulation right away, otherwise we still check
             # for dest being a dir via remote call below.
-            if dest.endswith("/"):  # CCTODO: Fixme for powershell
-                dest_file = os.path.join(dest, source_rel)
+            if conn.shell.path_has_trailing_slash(dest):
+                dest_file = conn.shell.join_path(dest, source_rel)
             else:
-                dest_file = dest
+                dest_file = conn.shell.join_path(dest)
 
             # Attempt to get the remote MD5 Hash.
             remote_md5 = self.runner._remote_md5(conn, tmp_path, dest_file)
@@ -186,7 +186,7 @@ class ActionModule(object):
                     return ReturnData(conn=conn, result=result)
                 else:
                     # Append the relative source location to the destination and retry remote_md5.
-                    dest_file = os.path.join(dest, source_rel) # CCTODO
+                    dest_file = conn.shell.join_path(dest, source_rel)
                     remote_md5 = self.runner._remote_md5(conn, tmp_path, dest_file)
 
             if remote_md5 != '1' and not force:
diff --git a/lib/ansible/runner/action_plugins/fetch.py b/lib/ansible/runner/action_plugins/fetch.py
index 2d7f7e9..1600e88 100644
--- a/lib/ansible/runner/action_plugins/fetch.py
+++ b/lib/ansible/runner/action_plugins/fetch.py
@@ -59,7 +59,7 @@ class ActionModule(object):
         source = os.path.expanduser(source)
 
         if flat:
-            if dest.endswith("/"): # CCTODO
+            if dest.endswith("/"): # CCTODO: Fix path for Windows hosts.
                 # if the path ends with "/", we'll use the source filename as the
                 # destination filename
                 base = os.path.basename(source)
diff --git a/lib/ansible/runner/action_plugins/template.py b/lib/ansible/runner/action_plugins/template.py
index 774a2be..623d173 100644
--- a/lib/ansible/runner/action_plugins/template.py
+++ b/lib/ansible/runner/action_plugins/template.py
@@ -79,7 +79,7 @@ class ActionModule(object):
                 source = utils.path_dwim(self.runner.basedir, source)
 
 
-        if dest.endswith("/"): # CCTODO
+        if dest.endswith("/"): # CCTODO: Fix path for Windows hosts.
             base = os.path.basename(source)
             dest = os.path.join(dest, base)
 
diff --git a/lib/ansible/runner/action_plugins/unarchive.py b/lib/ansible/runner/action_plugins/unarchive.py
index be0070f..16c0bc8 100644
--- a/lib/ansible/runner/action_plugins/unarchive.py
+++ b/lib/ansible/runner/action_plugins/unarchive.py
@@ -54,7 +54,7 @@ class ActionModule(object):
             result = dict(failed=True, msg="src (or content) and dest are required")
             return ReturnData(conn=conn, result=result)
 
-        dest = os.path.expanduser(dest)
+        dest = os.path.expanduser(dest) # CCTODO: Fix path for Windows hosts.
         source = template.template(self.runner.basedir, os.path.expanduser(source), inject)
         if copy:
             if '_original_file' in inject:
diff --git a/lib/ansible/runner/shell_plugins/powershell.py b/lib/ansible/runner/shell_plugins/powershell.py
index 1d6c74b..0310772 100644
--- a/lib/ansible/runner/shell_plugins/powershell.py
+++ b/lib/ansible/runner/shell_plugins/powershell.py
@@ -58,6 +58,10 @@ class ShellModule(object):
     def join_path(self, *args):
         return os.path.join(*args).replace('/', '\\')
 
+    def path_has_trailing_slash(self, path):
+        # Allow Windows paths to be specified using either slash.
+        return path.endswith('/') or path.endswith('\\')
+
     def chmod(self, mode, path):
         return ''
 
diff --git a/lib/ansible/runner/shell_plugins/sh.py b/lib/ansible/runner/shell_plugins/sh.py
index 8b74386..1ee2258 100644
--- a/lib/ansible/runner/shell_plugins/sh.py
+++ b/lib/ansible/runner/shell_plugins/sh.py
@@ -33,6 +33,9 @@ class ShellModule(object):
     def join_path(self, *args):
         return os.path.join(*args)
 
+    def path_has_trailing_slash(self, path):
+        return path.endswith('/')
+
     def chmod(self, mode, path):
         path = pipes.quote(path)
         return 'chmod %s %s' % (mode, path)
--
libgit2 0.26.0