1import os
2import pathlib
3
4from django.core.exceptions import SuspiciousFileOperation
5
6
7def validate_file_name(name, allow_relative_path=False):
8 # Remove potentially dangerous names
9 if os.path.basename(name) in {"", ".", ".."}:
10 raise SuspiciousFileOperation("Could not derive file name from '%s'" % name)
11
12 if allow_relative_path:
13 # Ensure that name can be treated as a pure posix path, i.e. Unix
14 # style (with forward slashes).
15 path = pathlib.PurePosixPath(str(name).replace("\\", "/"))
16 if path.is_absolute() or ".." in path.parts:
17 raise SuspiciousFileOperation(
18 "Detected path traversal attempt in '%s'" % name
19 )
20 elif name != os.path.basename(name):
21 raise SuspiciousFileOperation("File name '%s' includes path elements" % name)
22
23 return name
24
25
26class FileProxyMixin:
27 """
28 A mixin class used to forward file methods to an underlying file
29 object. The internal file object has to be called "file"::
30
31 class FileProxy(FileProxyMixin):
32 def __init__(self, file):
33 self.file = file
34 """
35
36 encoding = property(lambda self: self.file.encoding)
37 fileno = property(lambda self: self.file.fileno)
38 flush = property(lambda self: self.file.flush)
39 isatty = property(lambda self: self.file.isatty)
40 newlines = property(lambda self: self.file.newlines)
41 read = property(lambda self: self.file.read)
42 readinto = property(lambda self: self.file.readinto)
43 readline = property(lambda self: self.file.readline)
44 readlines = property(lambda self: self.file.readlines)
45 seek = property(lambda self: self.file.seek)
46 tell = property(lambda self: self.file.tell)
47 truncate = property(lambda self: self.file.truncate)
48 write = property(lambda self: self.file.write)
49 writelines = property(lambda self: self.file.writelines)
50
51 @property
52 def closed(self):
53 return not self.file or self.file.closed
54
55 def readable(self):
56 if self.closed:
57 return False
58 if hasattr(self.file, "readable"):
59 return self.file.readable()
60 return True
61
62 def writable(self):
63 if self.closed:
64 return False
65 if hasattr(self.file, "writable"):
66 return self.file.writable()
67 return "w" in getattr(self.file, "mode", "")
68
69 def seekable(self):
70 if self.closed:
71 return False
72 if hasattr(self.file, "seekable"):
73 return self.file.seekable()
74 return True
75
76 def __iter__(self):
77 return iter(self.file)