Site Sections: Satchmo Main | Wiki | Demo Store |

root/satchmo/trunk/satchmo/shop/views/download.py

Revision 1475, 3.8 kB (checked in by chris, 2 months ago)

Improve downloadable product support by trying to guess accurate content-type. Closes #479

  • Property svn:executable set to *
Line 
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
Note: See TracBrowser for help on using the browser.