Handle symlinks properly in the directory browser.
authorDave Page <dpage@pgadmin.org>
Wed, 30 Nov 2016 08:48:35 +0000 (17:48 +0900)
committerDave Page <dpage@pgadmin.org>
Wed, 30 Nov 2016 08:51:33 +0000 (17:51 +0900)
This change properly handles symlinks by expanding any it finds in the path
and issuing a redirect to the canonical location.

Whilst we're here, display links as such on the pages, sort directory listings
so the parent link is always at the top, and avoid unnecessary redirects caused
by missing /'s on URLs.

media/img/ftp/symlink.png [new file with mode: 0644]
pgweb/downloads/views.py
pgweb/util/misc.py
templates/downloads/ftpbrowser.html

diff --git a/media/img/ftp/symlink.png b/media/img/ftp/symlink.png
new file mode 100644 (file)
index 0000000..bff40cf
Binary files /dev/null and b/media/img/ftp/symlink.png differ
index d1ac61b5d0faf5acd4df3d2122d8e9587f2a944f..d15810d7a3bd1ce39ac67ea01c14fb0b798ee6e8 100644 (file)
@@ -1,3 +1,4 @@
+from django.core.urlresolvers import reverse
 from django.shortcuts import render_to_response, get_object_or_404
 from django.http import HttpResponse, Http404, HttpResponseRedirect
 from pgweb.util.decorators import login_required
@@ -40,16 +41,45 @@ def ftpbrowser(request, subpath):
        except Exception, e:
                return HttpServerError("Failed to load ftp site information: %s" % e)
 
-       if not allnodes.has_key(subpath):
-               raise Http404
+       # An incoming subpath may either be canonical, or have one or more elements
+       # present that are actually symlinks. For each element of the path, test to
+       # see if it is present in the pickle. If not, look for a symlink entry with
+       # and if present, replace the original entry with the symlink target.
+       canonpath = ''
+       if subpath != '':
+               parent = ''
+               for d in subpath.split('/'):
+                       # Check if allnodes contains a node matching the path
+                       if allnodes[parent].has_key(d):
+                               if allnodes[parent][d]['t'] == 'd':
+                                       canonpath = os.path.join(canonpath, d)
+                               elif allnodes[parent][d]['t'] == 'l':
+                                       canonpath = os.path.join(canonpath, allnodes[parent][d]['d'])
+                               else:
+                                       # There's a matching node, but it's not a link or a directory
+                                       raise Http404
+
+                               parent = canonpath
+                       else:
+                               # There's no matching node
+                               raise Http404
+
+       # If we wound up with a canonical path that doesn't match the original request,
+       # redirect the user
+       canonpath = canonpath.strip('/')
+       if subpath != canonpath:
+               return HttpResponseRedirect('/ftp/' + canonpath)
 
        node = allnodes[subpath]
        del allnodes
 
        # Add all directories
-       directories = [{'link': k, 'url': k} for k,v in node.items() if v['t'] == 'd']
-       # Add all symlinks (only directoreis supported)
-       directories.extend([{'link': k, 'url': v['d']} for k,v in node.items() if v['t'] == 'l'])
+       directories = [{'link': k, 'url': k, 'type': 'd'} for k,v in node.items() if v['t'] == 'd']
+       # Add all symlinks (only directories supported)
+       directories.extend([{'link': k, 'url': v['d'], 'type': 'l'} for k,v in node.items() if v['t'] == 'l'])
+
+       # A ittle early sorting wouldn't go amiss, so .. ends up at the top
+       directories.sort(key = version_sort, reverse=True)
 
        # Add a link to the parent directory
        if subpath:
@@ -80,7 +110,7 @@ def ftpbrowser(request, subpath):
 
        return render_to_response('downloads/ftpbrowser.html', {
                'basepath': subpath.rstrip('/'),
-               'directories': sorted(directories, key = version_sort, reverse=True),
+               'directories': directories,
                'files': sorted(files),
                'breadcrumbs': breadcrumbs,
                'readme': file_readme,
index d06d24d6eced5487d3a321b11e5244c3f813546c..a7e2c154928d705d2f2a05dcf23e74f7cdb7e01b 100644 (file)
@@ -41,7 +41,7 @@ def version_sort(l):
        """
        map a directory name to a format that will show up sensibly in an ascii sort
        """
-       mkey = l['url']
+       mkey = l['link']
        m = re.match('v([0-9]+)\.([0-9]+)\.([0-9]+)$',l['url'])
        if m:
                mkey = m.group(1) + '%02d' % int(m.group(2)) + '%02d' % int(m.group(3));
index a9b4ce9f1d3f2851dcaf7f13f8b9a16ea15a824d..ad8f68665a50b78ee6df0bb6d691f6b1dd0a5b52 100644 (file)
@@ -9,7 +9,11 @@
 <div>
 <table border="0" cellpadding="0" cellspacing="0" width="90%">
 {%for dir in directories%}
- <tr><td><a href="{{dir.url}}"><img src="/media/img/ftp/folder.png" alt="{{dir.link}}" /></a>&nbsp;<a href="{{dir.url}}">{{dir.link}}</a></td></tr>
+ {% if dir.type == 'd' or dir.link = '[Parent Directory]' %}
+  <tr><td><a href="{{dir.url}}/"><img src="/media/img/ftp/folder.png" alt="{{dir.link}}" /></a>&nbsp;<a href="{{dir.url}}/">{{dir.link}}</a></td></tr>
+ {%else%}
+  <tr><td><a href="{{dir.url}}/"><img src="/media/img/ftp/symlink.png" alt="{{dir.link}} -&gt; {{dir.url}}" /></a>&nbsp;<a href="{{dir.url}}/">{{dir.link}}</a> -&gt; {{dir.url}}</td></tr>
+ {%endif%}
 {%endfor%}
 </table>
 </div>