| 1 |
from django.core import urlresolvers |
|---|
| 2 |
from django.http import HttpResponse, HttpResponseRedirect |
|---|
| 3 |
from django.shortcuts import render_to_response |
|---|
| 4 |
from django.template import loader, RequestContext |
|---|
| 5 |
from django.utils.translation import ugettext_lazy as _ |
|---|
| 6 |
from satchmo.shop.models import DownloadLink |
|---|
| 7 |
import mimetypes |
|---|
| 8 |
|
|---|
| 9 |
import os |
|---|
| 10 |
import os.path |
|---|
| 11 |
import re |
|---|
| 12 |
|
|---|
| 13 |
SHA1_RE = re.compile('^[a-f0-9]{40}$') |
|---|
| 14 |
|
|---|
| 15 |
def _validate_key(download_key): |
|---|
| 16 |
""" |
|---|
| 17 |
Helper function to make sure the key is valid and all the other constraints on |
|---|
| 18 |
the download are still valid. |
|---|
| 19 |
Returns a tuple (False,"Error Message", None) or (True, None, dl_product) |
|---|
| 20 |
""" |
|---|
| 21 |
download_key = download_key.lower() |
|---|
| 22 |
if not SHA1_RE.search(download_key): |
|---|
| 23 |
error_message = _("The download key is invalid.") |
|---|
| 24 |
return (False, error_message, None) |
|---|
| 25 |
try: |
|---|
| 26 |
dl_product = DownloadLink.objects.get(key=download_key) |
|---|
| 27 |
except: |
|---|
| 28 |
error_message = _("The download key is invalid.") |
|---|
| 29 |
return (False, error_message, None) |
|---|
| 30 |
valid, msg = dl_product.is_valid() |
|---|
| 31 |
if not valid: |
|---|
| 32 |
return (False, msg, None) |
|---|
| 33 |
else: |
|---|
| 34 |
return (True, None, dl_product) |
|---|
| 35 |
|
|---|
| 36 |
def process(request, download_key): |
|---|
| 37 |
""" |
|---|
| 38 |
Validate that the key is good, then set a session variable. |
|---|
| 39 |
Redirect to the download view. |
|---|
| 40 |
|
|---|
| 41 |
We use this two step process so that we can easily display meaningful feedback |
|---|
| 42 |
to the user. |
|---|
| 43 |
""" |
|---|
| 44 |
valid, msg, dl_product = _validate_key(download_key) |
|---|
| 45 |
if not valid: |
|---|
| 46 |
context = RequestContext(request, {'error_message': msg}) |
|---|
| 47 |
return render_to_response('download.html', context) |
|---|
| 48 |
else: |
|---|
| 49 |
# The key is valid so let's set the session variable and redirect to the |
|---|
| 50 |
# download view |
|---|
| 51 |
request.session['download_key'] = download_key |
|---|
| 52 |
url = urlresolvers.reverse('satchmo_download_send', kwargs= {'download_key': download_key}) |
|---|
| 53 |
context = RequestContext(request, {'download_product': dl_product, |
|---|
| 54 |
'dl_url' : url}) |
|---|
| 55 |
return render_to_response('download.html', context) |
|---|
| 56 |
|
|---|
| 57 |
def send_file(request, download_key): |
|---|
| 58 |
""" |
|---|
| 59 |
After the appropriate session variable has been set, we commence the download. |
|---|
| 60 |
The key is maintained in the url but the session variable is used to control the |
|---|
| 61 |
download in order to maintain security. |
|---|
| 62 |
|
|---|
| 63 |
For this to work, your server must support the X-Sendfile header |
|---|
| 64 |
Lighttpd and Apache should both work with the headers used below. |
|---|
| 65 |
For apache, will need mod_xsendfile |
|---|
| 66 |
For lighttpd, allow-x-send-file must be enabled |
|---|
| 67 |
|
|---|
| 68 |
Also, you must ensure that the directory where the file is stored is protected |
|---|
| 69 |
from users. In lighttpd.conf: |
|---|
| 70 |
$HTTP["url"] =~ "^/static/protected/" { |
|---|
| 71 |
url.access-deny = ("") |
|---|
| 72 |
} |
|---|
| 73 |
""" |
|---|
| 74 |
if not request.session.get('download_key', False): |
|---|
| 75 |
url = urlresolvers.reverse('satchmo_download_process', kwargs = {'download_key': download_key}) |
|---|
| 76 |
return HttpResponseRedirect(url) |
|---|
| 77 |
valid, msg, dl_product = _validate_key(request.session['download_key']) |
|---|
| 78 |
if not valid: |
|---|
| 79 |
url = urlresolvers.reverse('satchmo_download_process', kwargs = {'download_key': request.session['download_key']}) |
|---|
| 80 |
return HttpResponseRedirect(url) |
|---|
| 81 |
file_name = os.path.split(dl_product.downloadable_product.file.path)[1] |
|---|
| 82 |
dl_product.num_attempts += 1 |
|---|
| 83 |
dl_product.save() |
|---|
| 84 |
del request.session['download_key'] |
|---|
| 85 |
response = HttpResponse() |
|---|
| 86 |
response['X-Sendfile'] = dl_product.downloadable_product.file.path |
|---|
| 87 |
response['X-LIGHTTPD-send-file'] = dl_product.downloadable_product.file.path |
|---|
| 88 |
response['Content-Disposition'] = "attachment; filename=%s" % file_name |
|---|
| 89 |
response['Content-length'] = os.stat(dl_product.downloadable_product.file.path).st_size |
|---|
| 90 |
contenttype, encoding = mimetypes.guess_type(file_name) |
|---|
| 91 |
if contenttype: |
|---|
| 92 |
response['Content-type'] = contenttype |
|---|
| 93 |
return response |
|---|