Commit | Line | Data |
---|---|---|
63a46810 P |
1 | #!/usr/bin/env python |
2 | # -*- coding: utf-8 -*- | |
3 | """ | |
4 | Outil de partage de fichiers. | |
5 | ||
6 | Depends: libapache2-mod-wsgi | |
7 | ||
8 | Attention : le code n'est pas encore “thread-safe”... | |
9 | """ | |
10 | import os | |
11 | import re | |
12 | import urllib | |
13 | import tarfile | |
14 | ||
15 | ROOT_PATH = '/srv/www/compta' | |
16 | PAGE_TITLE = u'Rapport financier du BAP <em>(%s)</em>' | |
17 | BACKGROUND_URL = 'Bamboo2.png' | |
18 | EXCLUDED_FILES = [ 'description.txt' ] | |
19 | INVALID_NAME = u'%s <span class="alert">(NOM INVALIDE)</span>' | |
20 | ||
21 | html_template_filename = os.path.abspath(__file__.rstrip('.py') + '.tpl') | |
22 | dir_description_filename = 'description.txt' | |
23 | ||
24 | dir_link_template = u"""%s<li class="dir"><a href="javascript:void(0)" onclick="sw('l%s')">%s</a> <em>(%s) <a href="%s">archive du dossier</a></em> %s</li>""" | |
25 | dir_description_template = u""" <em>%s</em>""" | |
26 | begin_content_template = u"""%s<div id="l%s" class="hidden"><ul>""" | |
27 | end_content_template = u"""%s</ul></div>""" | |
28 | file_link_template = u"""%s<li><a href="%s">%s</a> <em>(%s)</em></li>""" | |
29 | ||
30 | def valid_path(path): | |
31 | if os.path.islink('.' + path): | |
32 | return False | |
33 | if not os.path.isdir('.' + path): | |
34 | return False | |
35 | return True | |
36 | ||
37 | def human_size(size): | |
38 | if size <= 1: return "0 octet" | |
39 | if size < 1024: return "%s octets" % size | |
40 | size /= 1024 | |
41 | if size < 1024: return "%s Kio" % size | |
42 | size /= 1024 | |
43 | if size < 1024: return "%s Mio" % size | |
44 | size /= 1024 | |
45 | if size < 1024: return "%s Gio" % size | |
46 | size /= 1024 | |
47 | return "%s Tio" % size | |
48 | ||
49 | def my_cmp(name1, name2): | |
50 | m1 = re.match('[0-9]+', name1) | |
51 | m2 = re.match('[0-9]+', name2) | |
52 | if not m1 or not m2: | |
53 | return cmp(name1, name2) | |
54 | n1 = int(m1.group()) | |
55 | n2 = int(m2.group()) | |
56 | if n1 == n2: | |
57 | return cmp(name1[m1.end():], name2[m2.end():]) | |
58 | if n1 < n2: | |
59 | return -1 | |
60 | return 1 | |
61 | ||
62 | def my_listdir(path): | |
63 | path = '.' + path | |
64 | try: | |
65 | names = os.listdir(path) | |
66 | except OSError: | |
67 | return [], [] | |
68 | dirs = [] | |
69 | files = [] | |
70 | for n in names: | |
71 | p = path + '/' + n | |
72 | if not os.path.islink(p): | |
73 | if os.path.isdir(p): | |
74 | dirs.append(n) | |
75 | if os.path.isfile(p): | |
76 | files.append(n) | |
77 | dirs.sort(cmp=my_cmp) | |
78 | files.sort(cmp=my_cmp) | |
79 | return dirs, files | |
80 | ||
81 | ||
82 | id_number = 0 | |
83 | ||
84 | def dir_content(prefix, root, level=0): | |
85 | dirs, files = my_listdir(root) | |
86 | if not len(dirs) and not len(files): | |
87 | return [], 0 | |
88 | space = u" " * level | |
89 | content = [] | |
90 | file_size_total = 0 | |
91 | for d in dirs: | |
92 | path = root.rstrip('/') + '/' + d | |
93 | sub_dir_content, sub_dir_size = dir_content(prefix, path, level + 1) | |
94 | file_size_total += sub_dir_size | |
95 | if sub_dir_content: | |
96 | global id_number | |
97 | id_number += 1 | |
98 | name = d.replace('_',' ') | |
99 | try: | |
100 | name = name.decode('utf-8') | |
101 | except UnicodeDecodeError: | |
102 | name = INVALID_NAME % name.decode('latin-1') | |
103 | desc_file = '.' + path + '/' + dir_description_filename | |
104 | if os.path.isfile(desc_file): | |
105 | desc = file(desc_file).read().strip().replace('\n',' ') | |
106 | desc = dir_description_template % desc.decode('utf-8') | |
107 | else: | |
108 | desc = '' | |
109 | archive_path = (prefix + path + '.tar').decode('utf-8') | |
110 | archive_size = human_size(sub_dir_size) | |
111 | content.append(dir_link_template % \ | |
112 | (space, id_number, name, archive_size, archive_path, desc)) | |
113 | content.append(begin_content_template % (space, id_number)) | |
114 | content.extend(sub_dir_content) | |
115 | content.append(end_content_template % space) | |
116 | for f in files: | |
117 | path = root.rstrip('/') + '/' + f | |
118 | file_size = os.path.getsize('.' + path) | |
119 | file_size_total += file_size | |
120 | if file_size > 0 and f not in EXCLUDED_FILES: | |
121 | name = f.replace('_',' ') | |
122 | try: | |
123 | name = name.decode('utf-8') | |
124 | except UnicodeDecodeError: | |
125 | name = INVALID_NAME % name.decode('latin-1') | |
126 | content.append( file_link_template % (space, | |
127 | urllib.quote(prefix + path), name, human_size(file_size)) ) | |
128 | return content, file_size_total | |
129 | ||
130 | class FileBuffer(): | |
131 | def __init__(self): | |
132 | self.reset() | |
133 | def reset(self): | |
134 | self._data = [] | |
135 | def write(self, data): | |
136 | self._data.append(data) | |
137 | def read(self): | |
138 | return ''.join(self._data) | |
139 | ||
140 | def tar_generator(path, mode='w|', bufsize=65536): | |
141 | buffer = FileBuffer() | |
142 | #yield 'open()\n' | |
143 | tar = tarfile.open(mode=mode, fileobj=buffer, bufsize=bufsize) | |
144 | for root, dirs, files in os.walk(path): | |
145 | for name in files: | |
146 | tar.add(os.path.join(root, name), recursive=False) | |
147 | #yield '+ file: %s\n' % os.path.join(root, name) | |
148 | yield buffer.read() | |
149 | buffer.reset() | |
150 | tar.close() | |
151 | #yield 'close()\n' | |
152 | yield buffer.read() | |
153 | ||
154 | def application(environ, start_response): | |
155 | global id_number | |
156 | id_number = 0 | |
157 | os.chdir(ROOT_PATH) | |
158 | if False and not environ.get('HTTP_USER'): | |
159 | headers = [('Content-Type', 'text/plain; charset=utf-8'), ] | |
160 | start_response('401 Authorization Required', headers) | |
161 | return ['Authorization Required'] | |
162 | path = environ['PATH_INFO'] | |
163 | if not valid_path(os.path.dirname(path)): | |
164 | headers = [('Content-Type', 'text/plain; charset=utf-8'), ] | |
165 | start_response('404 Not Found', headers) | |
166 | return ['Not Found'] | |
167 | if os.path.isfile('.' + path): | |
168 | if path.endswith('.txt'): | |
169 | mime_type = 'text/plain; charset=utf-8' | |
170 | elif path.endswith('.pdf'): | |
171 | mime_type = 'application/pdf' | |
172 | else: | |
173 | mime_type = 'application/octet-stream' | |
174 | length = str(os.path.getsize('.' + path)) | |
175 | headers = [('Content-Type', mime_type), ('Content-Length', length), ] | |
176 | start_response('200 OK', headers) | |
177 | return file('.' + path) | |
178 | if path.endswith('.tar') and os.path.isdir('.' + path[:-4]): | |
179 | #headers = [('Content-Type', 'text/plain; charset=utf-8'), ] | |
180 | headers = [('Content-Type', 'application/octet-stream'), ] | |
181 | start_response('200 OK', headers) | |
182 | return tar_generator('.' + path[:-4]) | |
183 | content, size = dir_content(environ['SCRIPT_NAME'], environ['PATH_INFO']) | |
184 | environ.update({ | |
185 | 'page_title': PAGE_TITLE % human_size(size), | |
186 | 'background_url': BACKGROUND_URL, | |
187 | 'content': '<ul>\n' + '\n'.join(content) + '\n</ul>', | |
188 | }) | |
189 | html_template = file(html_template_filename).read().decode('utf-8') | |
190 | content = (html_template % environ).encode('utf-8') | |
191 | headers = [('Content-Type', 'text/html; charset=utf-8'), ] | |
192 | start_response('200 OK', headers) | |
193 | return [content] | |
194 | ||
195 | if __name__ == '__main__': | |
196 | # this runs when script is started directly from commandline | |
197 | try: | |
198 | # create a simple WSGI server and run the application | |
199 | from wsgiref import simple_server | |
200 | print "Running test application - point your browser at http://localhost:8000/ ..." | |
201 | httpd = simple_server.WSGIServer(('', 8000), simple_server.WSGIRequestHandler) | |
202 | httpd.set_app(application) | |
203 | httpd.serve_forever() | |
204 | except ImportError: | |
205 | # wsgiref not installed, just output html to stdout | |
206 | for content in application({}, lambda status, headers: None): | |
207 | print content | |
208 |