1
2
3
4 """
5 This file is part of the web2py Web Framework
6 Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu>
7 License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
8 """
9
10 import storage
11 import os
12 import re
13 import tarfile
14 import glob
15 import time
16 import datetime
17 from http import HTTP
18 from gzip import open as gzopen
19 from settings import global_settings
20
21
22 __all__ = [
23 'parse_version',
24 'read_file',
25 'write_file',
26 'readlines_file',
27 'up',
28 'abspath',
29 'mktree',
30 'listdir',
31 'recursive_unlink',
32 'cleanpath',
33 'tar',
34 'untar',
35 'tar_compiled',
36 'get_session',
37 'check_credentials',
38 'w2p_pack',
39 'w2p_unpack',
40 'w2p_pack_plugin',
41 'w2p_unpack_plugin',
42 'fix_newlines',
43 'make_fake_file_like_object',
44 ]
45
46 -def parse_version(version = "Version 1.99.0 (2011-09-19 08:23:26)"):
47 re_version = re.compile('[^\d]+ (\d+)\.(\d+)\.(\d+)\s*\((?P<datetime>.+?)\)\s*(?P<type>[a-z]+)?')
48 m = re_version.match(version)
49 a,b,c = int(m.group(1)),int(m.group(2)),int(m.group(3)),
50 s = m.group('type') or 'dev'
51 d = datetime.datetime.strptime(m.group('datetime'),'%Y-%m-%d %H:%M:%S')
52 return (a,b,c,d,s)
53
55 "returns content from filename, making sure to close the file explicitly on exit."
56 f = open(filename, mode)
57 try:
58 return f.read()
59 finally:
60 f.close()
61
63 "writes <value> to filename, making sure to close the file explicitly on exit."
64 f = open(filename, mode)
65 try:
66 return f.write(value)
67 finally:
68 f.close()
69
71 "applies .split('\n') to the output of read_file()"
72 return read_file(filename, mode).split('\n')
73
75 "convert relative path to absolute path based (by default) on applications_parent"
76 path = os.path.join(*relpath)
77 gluon = base.get('gluon', False)
78 if os.path.isabs(path):
79 return path
80 if gluon:
81 return os.path.join(global_settings.gluon_parent, path)
82 return os.path.join(global_settings.applications_parent, path)
83
84
86 head,tail =os.path.split(path)
87 if head:
88 if tail: mktree(head)
89 if not os.path.exists(head):
90 os.mkdir(head)
91
92 -def listdir(
93 path,
94 expression='^.+$',
95 drop=True,
96 add_dirs=False,
97 sort=True,
98 ):
99 """
100 like os.listdir() but you can specify a regex pattern to filter files.
101 if add_dirs is True, the returned items will have the full path.
102 """
103 if path[-1:] != os.path.sep:
104 path = path + os.path.sep
105 if drop:
106 n = len(path)
107 else:
108 n = 0
109 regex = re.compile(expression)
110 items = []
111 for (root, dirs, files) in os.walk(path, topdown=True):
112 for dir in dirs[:]:
113 if dir.startswith('.'):
114 dirs.remove(dir)
115 if add_dirs:
116 items.append(root[n:])
117 for file in sorted(files):
118 if regex.match(file) and not file.startswith('.'):
119 items.append(os.path.join(root, file)[n:])
120 if sort:
121 return sorted(items)
122 else:
123 return items
124
125
127 if os.path.isdir(f):
128 for s in os.listdir(f):
129 recursive_unlink(os.path.join(f,s))
130 os.rmdir(f)
131 elif os.path.isfile(f):
132 os.unlink(f)
133
134
136 """
137 turns any expression/path into a valid filename. replaces / with _ and
138 removes special characters.
139 """
140
141 items = path.split('.')
142 if len(items) > 1:
143 path = re.sub('[^\w\.]+', '_', '_'.join(items[:-1]) + '.'
144 + ''.join(items[-1:]))
145 else:
146 path = re.sub('[^\w\.]+', '_', ''.join(items[-1:]))
147 return path
148
149
151 if not hasattr(tarfile.TarFile, 'extractall'):
152 from tarfile import ExtractError
153
154 class TarFile(tarfile.TarFile):
155
156 def extractall(self, path='.', members=None):
157 """Extract all members from the archive to the current working
158 directory and set owner, modification time and permissions on
159 directories afterwards. `path' specifies a different directory
160 to extract to. `members' is optional and must be a subset of the
161 list returned by getmembers().
162 """
163
164 directories = []
165 if members is None:
166 members = self
167 for tarinfo in members:
168 if tarinfo.isdir():
169
170
171
172
173 try:
174 os.makedirs(os.path.join(path,
175 tarinfo.name), 0777)
176 except EnvironmentError:
177 pass
178 directories.append(tarinfo)
179 else:
180 self.extract(tarinfo, path)
181
182
183
184 directories.sort(lambda a, b: cmp(a.name, b.name))
185 directories.reverse()
186
187
188
189 for tarinfo in directories:
190 path = os.path.join(path, tarinfo.name)
191 try:
192 self.chown(tarinfo, path)
193 self.utime(tarinfo, path)
194 self.chmod(tarinfo, path)
195 except ExtractError, e:
196 if self.errorlevel > 1:
197 raise
198 else:
199 self._dbg(1, 'tarfile: %s' % e)
200
201
202 _cls = TarFile
203 else:
204 _cls = tarfile.TarFile
205
206 tar = _cls(filename, 'r')
207 ret = tar.extractall(path, members)
208 tar.close()
209 return ret
210
211 -def tar(file, dir, expression='^.+$'):
212 """
213 tars dir into file, only tars file that match expression
214 """
215
216 tar = tarfile.TarFile(file, 'w')
217 try:
218 for file in listdir(dir, expression, add_dirs=True):
219 tar.add(os.path.join(dir, file), file, False)
220 finally:
221 tar.close()
222
224 """
225 untar file into dir
226 """
227
228 _extractall(file, dir)
229
230
231 -def w2p_pack(filename, path, compiled=False):
232 filename = abspath(filename)
233 path = abspath(path)
234 tarname = filename + '.tar'
235 if compiled:
236 tar_compiled(tarname, path, '^[\w\.\-]+$')
237 else:
238 tar(tarname, path, '^[\w\.\-]+$')
239 w2pfp = gzopen(filename, 'wb')
240 tarfp = open(tarname, 'rb')
241 w2pfp.write(tarfp.read())
242 w2pfp.close()
243 tarfp.close()
244 os.unlink(tarname)
245
247 filename = abspath(filename)
248 path = abspath(path)
249 if filename[-4:] == '.w2p' or filename[-3:] == '.gz':
250 if filename[-4:] == '.w2p':
251 tarname = filename[:-4] + '.tar'
252 else:
253 tarname = filename[:-3] + '.tar'
254 fgzipped = gzopen(filename, 'rb')
255 tarfile = open(tarname, 'wb')
256 tarfile.write(fgzipped.read())
257 tarfile.close()
258 fgzipped.close()
259 else:
260 tarname = filename
261 untar(tarname, path)
262 if delete_tar:
263 os.unlink(tarname)
264
265
267 """Pack the given plugin into a w2p file.
268 Will match files at:
269 <path>/*/plugin_[name].*
270 <path>/*/plugin_[name]/*
271 """
272 filename = abspath(filename)
273 path = abspath(path)
274 if not filename.endswith('web2py.plugin.%s.w2p' % plugin_name):
275 raise Exception, "Not a web2py plugin name"
276 plugin_tarball = tarfile.open(filename, 'w:gz')
277 try:
278 app_dir = path
279 while app_dir[-1]=='/':
280 app_dir = app_dir[:-1]
281 files1=glob.glob(os.path.join(app_dir,'*/plugin_%s.*' % plugin_name))
282 files2=glob.glob(os.path.join(app_dir,'*/plugin_%s/*' % plugin_name))
283 for file in files1+files2:
284 plugin_tarball.add(file, arcname=file[len(app_dir)+1:])
285 finally:
286 plugin_tarball.close()
287
288
295
296
298 """
299 used to tar a compiled application.
300 the content of models, views, controllers is not stored in the tar file.
301 """
302
303 tar = tarfile.TarFile(file, 'w')
304 for file in listdir(dir, expression, add_dirs=True):
305 filename = os.path.join(dir, file)
306 if os.path.islink(filename):
307 continue
308 if os.path.isfile(filename) and file[-4:] != '.pyc':
309 if file[:6] == 'models':
310 continue
311 if file[:5] == 'views':
312 continue
313 if file[:11] == 'controllers':
314 continue
315 if file[:7] == 'modules':
316 continue
317 tar.add(filename, file, False)
318 tar.close()
319
321 return os.path.dirname(os.path.normpath(path))
322
323
325 """ checks that user is authorized to access other_application"""
326 if request.application == other_application:
327 raise KeyError
328 try:
329 session_id = request.cookies['session_id_' + other_application].value
330 osession = storage.load_storage(os.path.join(
331 up(request.folder), other_application, 'sessions', session_id))
332 except:
333 osession = storage.Storage()
334 return osession
335
336
338 """ checks that user is authorized to access other_application"""
339 if request.env.web2py_runtime_gae:
340 from google.appengine.api import users
341 if users.is_current_user_admin():
342 return True
343 else:
344 login_html = '<a href="%s">Sign in with your google account</a>.' \
345 % users.create_login_url(request.env.path_info)
346 raise HTTP(200, '<html><body>%s</body></html>' % login_html)
347 else:
348 dt = time.time() - expiration
349 s = get_session(request, other_application)
350 return (s.authorized and s.last_time and s.last_time > dt)
351
352
354 regex = re.compile(r'''(\r
355 |\r|
356 )''')
357 for filename in listdir(path, '.*\.(py|html)$', drop=False):
358 rdata = read_file(filename, 'rb')
359 wdata = regex.sub('\n', rdata)
360 if wdata != rdata:
361 write_file(filename, wdata, 'wb')
362
363 -def copystream(
364 src,
365 dest,
366 size,
367 chunk_size=10 ** 5,
368 ):
369 """
370 this is here because I think there is a bug in shutil.copyfileobj
371 """
372 while size > 0:
373 if size < chunk_size:
374 data = src.read(size)
375 else:
376 data = src.read(chunk_size)
377 length = len(data)
378 if length > size:
379 (data, length) = (data[:size], size)
380 size -= length
381 if length == 0:
382 break
383 dest.write(data)
384 if length < chunk_size:
385 break
386 dest.seek(0)
387 return
388
389
391 class LogFile(object):
392 def write(self, value):
393 pass
394 def close(self):
395 pass
396 return LogFile()
397