ignore . and .. in directory listing
[qcg-portal.git] / filex / views.py
1 from itertools import islice
2 import mimetypes
3
4 from django.contrib.auth.decorators import login_required
5 from django.core.exceptions import PermissionDenied, SuspiciousOperation
6 from django.http import JsonResponse, StreamingHttpResponse
7 from django.shortcuts import get_object_or_404
8 from django.template.defaultfilters import filesizeformat
9 from django.utils.formats import date_format
10 from django.views.decorators.http import require_POST
11
12 from filex.forms import NewDirForm, RenameForm, FavoriteForm
13 from filex.ftp import FTPOperation, FTPException
14 from filex.models import Favorite
15 from filex.uploadhandler import with_ftp_upload_handler
16
17
18 def check_auth(request):
19     if not request.user.is_authenticated():
20         raise PermissionDenied("Login required!")
21     if not request.session['proxy']:
22         raise PermissionDenied("No proxy found!")
23
24
25 def list_content(request):
26     check_auth(request)
27
28     # TODO data validation
29     host = request.GET.get('host')
30     path = request.GET.get('path')
31     if not host or not path:
32         raise SuspiciousOperation("No path or host given!")
33
34     url = 'gsiftp://' + host + path
35
36     try:
37         listing = FTPOperation(request.session['proxy']).listing(url)
38     except FTPException as e:
39         return JsonResponse({'msg': e.message}, status=400)
40
41     data = []
42     # ignore . and .. from beginning of the listing
43     for item in islice(listing, 2, None):
44         item['size'] = filesizeformat(item['size'])
45         item['date'] = date_format(item['date'], 'CUSTOM_DATETIME_FORMAT')
46
47         data.append(item)
48
49     return JsonResponse(data, safe=False)
50
51
52 def download(request):
53     check_auth(request)
54
55     # TODO data validation
56     host = request.GET.get('host')
57     path = request.GET.get('path')
58     name = request.GET.get('name')
59     if not host or not path or not name:
60         raise SuspiciousOperation("No path or host or name given!")
61
62     url = 'gsiftp://' + host + path + '/' + name
63
64     mime_type, encoding = mimetypes.guess_type(name)
65
66     response = StreamingHttpResponse(FTPOperation(request.session['proxy']).get(url),
67                                      content_type=mime_type or 'application/octet-stream')
68     response['Content-Disposition'] = 'attachment; filename={}'.format(name)
69     # TODO Content-Length (?)
70
71     if encoding:
72         response['Content-Encoding'] = encoding
73
74     return response
75
76
77 @with_ftp_upload_handler
78 def upload(request):
79     # TODO error handling
80     return JsonResponse({'success': True})
81
82
83 def info(request):
84     check_auth(request)
85
86     # TODO data validation
87     host = request.GET.get('host')
88     path = request.GET.get('path')
89     if not host or not path:
90         raise SuspiciousOperation("No path or host given!")
91
92     url = 'gsiftp://' + host + path
93
94     try:
95         return JsonResponse(FTPOperation(request.session['proxy']).info(url))
96     except FTPException as e:
97         status = 400
98         if 'No such file or directory' in e.message:
99             status = 404
100         elif 'Permission denied' in e.message:
101             status = 403
102
103         return JsonResponse({'msg': e.message}, status=status)
104
105
106 @require_POST
107 def delete(request):
108     check_auth(request)
109
110     # TODO data validation
111     host = request.POST.get('host')
112     path = request.POST.get('path')
113     dirs = request.POST.getlist('dirs')
114     files = request.POST.getlist('files')
115     if not host or not path or not (files or dirs):
116         raise SuspiciousOperation("No path or host or files given!")
117
118     url = 'gsiftp://' + host + path + '/'
119     ftp = FTPOperation(request.session['proxy'])
120
121     done, fail = [], {}
122
123     for name in dirs:
124         try:
125             ftp.rmdir(url + name)
126         except FTPException as e:
127             fail[name] = e.message
128         else:
129             done.append(name)
130
131     for name in files:
132         try:
133             ftp.delete(url + name)
134         except FTPException as e:
135             fail[name] = e.message
136         else:
137             done.append(name)
138
139     return JsonResponse({'done': done, 'fail': fail})
140
141
142 @require_POST
143 def mkdir(request):
144     check_auth(request)
145
146     # TODO actual data validation
147     form = NewDirForm(request.POST)
148
149     if form.is_valid():
150         host = form.cleaned_data['host']
151         path = form.cleaned_data['path']
152         name = form.cleaned_data['name']
153
154         url = 'gsiftp://' + host + path + '/' + name
155
156         try:
157             FTPOperation(request.session['proxy']).mkdir(url)
158         except FTPException as e:
159             msg = e.message
160         else:
161             return JsonResponse({'success': True})
162     else:
163         msg = form.errors
164
165     return JsonResponse({'msg': msg}, status=400)
166
167
168 @require_POST
169 def move(request):
170     check_auth(request)
171
172     # TODO actual data validation
173     form = RenameForm(request.POST)
174
175     if form.is_valid():
176         host = form.cleaned_data['host']
177         path = form.cleaned_data['path']
178         src = form.cleaned_data['src']
179         dst = form.cleaned_data['dst']
180
181         src_url = 'gsiftp://' + host + path + '/' + src
182         dst_url = 'gsiftp://' + host + path + '/' + dst
183
184         try:
185             FTPOperation(request.session['proxy']).move(src_url, dst_url)
186         except FTPException as e:
187             msg = e.message
188         else:
189             return JsonResponse({'success': True})
190     else:
191         msg = form.errors
192
193     return JsonResponse({'msg': msg}, status=400)
194
195
196 @require_POST
197 def compress(request):
198     check_auth(request)
199
200     # TODO data validation
201     host = request.POST.get('host')
202     path = request.POST.get('path')
203     files = request.POST.getlist('files')
204     archive = request.POST.get('archive')
205     if not host or not path or not files or not archive:
206         raise SuspiciousOperation("No path or host or files or archive given!")
207
208     server = 'gsiftp://' + host
209
210     try:
211         # consume generator with command output
212         list(FTPOperation(request.session['proxy']).compress(server, path, files, archive))
213     except FTPException as e:
214         return JsonResponse({'msg': e.message}, status=400)
215     else:
216         return JsonResponse({'success': True})
217
218
219 @require_POST
220 def extract(request):
221     check_auth(request)
222
223     # TODO data validation
224     host = request.POST.get('host')
225     archive = request.POST.get('archive')
226     dst = request.POST.get('dst')
227     if not host or not archive or not dst:
228         raise SuspiciousOperation("No path or host or files or archive given!")
229
230     server = 'gsiftp://' + host
231
232     try:
233         # consume generator with command output
234         list(FTPOperation(request.session['proxy']).extract(server, archive, dst))
235     except FTPException as e:
236         return JsonResponse({'msg': e.message}, status=400)
237     else:
238         return JsonResponse({'success': True})
239
240
241 @require_POST
242 @login_required
243 def fav_add(request):
244     data = request.POST.copy()
245     data['owner'] = request.user.id
246
247     form = FavoriteForm(data)
248
249     # TODO check if path exists
250     if form.is_valid():
251         instance = form.save()
252
253         return JsonResponse({'group': 'usr', 'host': instance.host, 'path': instance.path,
254                              'value': instance.host + instance.path})
255
256     return JsonResponse({'msg': form.errors}, status=400)
257
258
259 @require_POST
260 @login_required
261 def fav_delete(request):
262     fav = get_object_or_404(Favorite, owner=request.user, host=request.POST['host'], path=request.POST['path'])
263     fav.delete()
264
265     return JsonResponse({'success': True})