mirror of
https://github.com/postgres/pgweb.git
synced 2025-07-29 11:59:36 +00:00
261 lines
8.5 KiB
Python
261 lines
8.5 KiB
Python
from django.shortcuts import render_to_response, get_object_or_404
|
|
from django.http import HttpResponse, Http404, HttpResponseRedirect
|
|
from django.contrib.auth.decorators import login_required
|
|
from django.views.decorators.csrf import csrf_exempt
|
|
from django.db import transaction
|
|
from django.conf import settings
|
|
|
|
import os
|
|
import urlparse
|
|
import cPickle as pickle
|
|
|
|
from pgweb.util.decorators import ssl_required, nocache
|
|
from pgweb.util.contexts import NavContext
|
|
from pgweb.util.helpers import simple_form, PgXmlHelper, HttpServerError
|
|
from pgweb.util.misc import get_client_ip, varnish_purge, version_sort
|
|
|
|
from models import Mirror, Category, Product, StackBuilderApp
|
|
from forms import ProductForm
|
|
|
|
#######
|
|
# FTP browser
|
|
#######
|
|
def ftpbrowser(request, subpath):
|
|
if subpath:
|
|
# An actual path has been selected. Fancy!
|
|
|
|
if subpath.find('..') > -1:
|
|
# Just claim it doesn't exist if the user tries to do this
|
|
# type of bad thing
|
|
raise Http404
|
|
subpath = subpath.strip('/')
|
|
else:
|
|
subpath=""
|
|
|
|
# Pickle up the list of things we need
|
|
try:
|
|
f = open(settings.FTP_PICKLE, "rb")
|
|
allnodes = pickle.load(f)
|
|
f.close()
|
|
except Exception, e:
|
|
return HttpServerError("Failed to load ftp site information: %s" % e)
|
|
|
|
if not allnodes.has_key(subpath):
|
|
raise Http404
|
|
|
|
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'])
|
|
|
|
# Add a link to the parent directory
|
|
if subpath:
|
|
directories.insert(0, {'link':'[Parent Directory]', 'url':'..'})
|
|
|
|
# Fetch files
|
|
files = [{'name': k, 'mtime': v['d'], 'size': v['s']} for k,v in node.items() if v['t'] == 'f']
|
|
|
|
breadcrumbs = []
|
|
if subpath:
|
|
breadroot = ""
|
|
for pathpiece in subpath.split('/'):
|
|
if not pathpiece:
|
|
# Trailing slash will give out an empty pathpiece
|
|
continue
|
|
if breadroot:
|
|
breadroot = "%s/%s" % (breadroot, pathpiece)
|
|
else:
|
|
breadroot = pathpiece
|
|
breadcrumbs.append({'name': pathpiece, 'path': breadroot});
|
|
|
|
# Check if there are any "content files" we should render directly on the webpage
|
|
file_readme = (node.has_key('README') and node['README']['t']=='f') and node['README']['c'] or None;
|
|
file_message = (node.has_key('.message') and node['.message']['t']=='f') and node['.message']['c'] or None;
|
|
file_maintainer = (node.has_key('CURRENT_MAINTAINER') and node['CURRENT_MAINTAINER']['t'] == 'f') and node['CURRENT_MAINTAINER']['c'] or None;
|
|
|
|
del node
|
|
|
|
return render_to_response('downloads/ftpbrowser.html', {
|
|
'basepath': subpath.rstrip('/'),
|
|
'directories': sorted(directories, key = version_sort, reverse=True),
|
|
'files': sorted(files),
|
|
'breadcrumbs': breadcrumbs,
|
|
'readme': file_readme,
|
|
'messagefile': file_message,
|
|
'maintainer': file_maintainer,
|
|
}, NavContext(request, 'download'))
|
|
|
|
|
|
# Accept an upload of the ftpsite pickle. This is fairly resource consuming,
|
|
# and not very optimized, but that's why we limit it so that only the ftp
|
|
# server(s) can post it.
|
|
# There is no concurrency check - the ftp site better not send more than one
|
|
# file in parallel.
|
|
@ssl_required
|
|
@csrf_exempt
|
|
def uploadftp(request):
|
|
if request.method != 'PUT':
|
|
return HttpServerError("Invalid method")
|
|
if not request.META['REMOTE_ADDR'] in settings.FTP_MASTERS:
|
|
return HttpServerError("Invalid client address")
|
|
# We have the data in request.raw_post_data. Attempt to load it as
|
|
# a pickle to make sure it's properly formatted
|
|
pickle.loads(request.raw_post_data)
|
|
|
|
# Next, check if it's the same as the current file
|
|
f = open(settings.FTP_PICKLE, "rb")
|
|
x = f.read()
|
|
f.close()
|
|
if x == request.raw_post_data:
|
|
# Don't rewrite the file or purge any data if nothing changed
|
|
return HttpResponse("NOT CHANGED", content_type="text/plain")
|
|
|
|
# File has changed - let's write it!
|
|
f = open("%s.new" % settings.FTP_PICKLE, "wb")
|
|
f.write(request.raw_post_data)
|
|
f.close()
|
|
os.rename("%s.new" % settings.FTP_PICKLE, settings.FTP_PICKLE)
|
|
|
|
# Purge it out of varnish so we start responding right away
|
|
varnish_purge("/ftp")
|
|
|
|
# Finally, indicate to the client that we're happy
|
|
return HttpResponse("OK", content_type="text/plain")
|
|
|
|
@nocache
|
|
def mirrorselect(request, path):
|
|
# We have given up on the advanced mirror network things, and will just
|
|
# redirect this to ftp.mirrors.postgresql.org for now...
|
|
# Since requests hit our internal servers, we're also not going to
|
|
# bother logging them - logging will be handled by those servers
|
|
return HttpResponseRedirect("https://ftp.postgresql.org/pub/%s" % path)
|
|
|
|
# Accesses asking for a specific mirror will keep doing that for now.
|
|
# At some time in the future we might consider hijacking them and sending
|
|
# them to our master mirrors again.
|
|
def _mirror_redirect_internal(request, scheme, host, path):
|
|
# Redirect!
|
|
newurl = "%s://%s/%s" % (scheme, host, path)
|
|
return HttpResponseRedirect(newurl)
|
|
|
|
@nocache
|
|
def mirror_redirect(request, mirrorid, protocol, path):
|
|
try:
|
|
mirror = Mirror.objects.get(pk=mirrorid)
|
|
except Mirror.DoesNotExist:
|
|
raise Http404("Specified mirror not found")
|
|
|
|
return _mirror_redirect_internal(
|
|
request,
|
|
protocol=='h' and 'http' or 'ftp',
|
|
mirror.get_root_path(protocol),
|
|
path,
|
|
)
|
|
|
|
@nocache
|
|
def mirror_redirect_old(request):
|
|
# Version of redirect that takes parameters in the querystring. This is
|
|
# only used by the stackbuilder.
|
|
try:
|
|
if not request.GET['sb'] == "1":
|
|
raise Http404("Page not found, you should be using the new URL format!")
|
|
except:
|
|
raise Http404("Page not found, you should be using the new URL format!")
|
|
|
|
urlpieces = urlparse.urlparse(request.GET['url'])
|
|
if urlpieces.query:
|
|
path = "%s?%s" % (urlpieces.path, urlpieces.query)
|
|
else:
|
|
path = urlpieces.path
|
|
|
|
return _mirror_redirect_internal(
|
|
request,
|
|
urlpieces.scheme,
|
|
urlpieces.netloc,
|
|
path,
|
|
)
|
|
|
|
def mirrors_xml(request):
|
|
# Same as in mirrorselect
|
|
all_mirrors = Mirror.objects.filter(mirror_active=True, mirror_private=False, mirror_dns=True).extra(where=["mirror_last_rsync>(now() - '48 hours'::interval)"]).order_by('country_name', 'mirror_index')
|
|
|
|
resp = HttpResponse(mimetype='text/xml')
|
|
x = PgXmlHelper(resp)
|
|
x.startDocument()
|
|
x.startElement('mirrors', {})
|
|
for m in all_mirrors:
|
|
for protocol in m.get_all_protocols():
|
|
x.startElement('mirror', {})
|
|
x.add_xml_element('country', m.country_name)
|
|
x.add_xml_element('path', m.host_path)
|
|
x.add_xml_element('protocol', protocol)
|
|
x.add_xml_element('hostname', m.get_host_name())
|
|
x.endElement('mirror')
|
|
x.endElement('mirrors')
|
|
x.endDocument()
|
|
return resp
|
|
|
|
#######
|
|
# Product catalogue
|
|
#######
|
|
def categorylist(request):
|
|
categories = Category.objects.all()
|
|
return render_to_response('downloads/categorylist.html', {
|
|
'categories': categories,
|
|
}, NavContext(request, 'download'))
|
|
|
|
def productlist(request, catid, junk=None):
|
|
category = get_object_or_404(Category, pk=catid)
|
|
products = Product.objects.select_related('org','licencetype').filter(category=category, approved=True)
|
|
return render_to_response('downloads/productlist.html', {
|
|
'category': category,
|
|
'products': products,
|
|
'productcount': len(products),
|
|
}, NavContext(request, 'download'))
|
|
|
|
@ssl_required
|
|
@login_required
|
|
def productform(request, itemid):
|
|
return simple_form(Product, itemid, request, ProductForm,
|
|
redirect='/account/edit/products/')
|
|
|
|
#######
|
|
# Stackbuilder
|
|
#######
|
|
def applications_v2_xml(request):
|
|
all_apps = StackBuilderApp.objects.select_related().filter(active=True)
|
|
|
|
resp = HttpResponse(mimetype='text/xml')
|
|
x = PgXmlHelper(resp, skipempty=True)
|
|
x.startDocument()
|
|
x.startElement('applications', {})
|
|
for a in all_apps:
|
|
x.startElement('application', {})
|
|
x.add_xml_element('id', a.textid)
|
|
x.add_xml_element('platform', a.platform)
|
|
x.add_xml_element('secondaryplatform', a.secondaryplatform)
|
|
x.add_xml_element('version', a.version)
|
|
x.add_xml_element('name', a.name)
|
|
x.add_xml_element('description', a.description)
|
|
x.add_xml_element('category', a.category)
|
|
x.add_xml_element('pgversion', a.pgversion)
|
|
x.add_xml_element('edbversion', a.edbversion)
|
|
x.add_xml_element('format', a.format)
|
|
x.add_xml_element('installoptions', a.installoptions)
|
|
x.add_xml_element('upgradeoptions', a.upgradeoptions)
|
|
x.add_xml_element('checksum', a.checksum)
|
|
x.add_xml_element('mirrorpath', a.mirrorpath)
|
|
x.add_xml_element('alturl', a.alturl)
|
|
x.add_xml_element('versionkey', a.versionkey)
|
|
x.add_xml_element('manifesturl', a.manifesturl)
|
|
for dep in a.txtdependencies.split(','):
|
|
x.add_xml_element('dependency', dep)
|
|
x.endElement('application')
|
|
x.endElement('applications')
|
|
x.endDocument()
|
|
return resp
|
|
|