##// END OF EJS Templates
Use UUID as a unique file name for uploads
neko259 -
r1767:56a71883 default
parent child Browse files
Show More
@@ -1,179 +1,177 b''
1 """
1 """
2 This module contains helper functions and helper classes.
2 This module contains helper functions and helper classes.
3 """
3 """
4 import hashlib
4 import hashlib
5 import uuid
6
5 from boards.abstracts.constants import FILE_DIRECTORY
7 from boards.abstracts.constants import FILE_DIRECTORY
6 from random import random
8 from random import random
7 import time
9 import time
8 import hmac
10 import hmac
9
11
10 from django.core.cache import cache
12 from django.core.cache import cache
11 from django.db.models import Model
13 from django.db.models import Model
12 from django import forms
14 from django import forms
13 from django.template.defaultfilters import filesizeformat
15 from django.template.defaultfilters import filesizeformat
14 from django.utils import timezone
16 from django.utils import timezone
15 from django.utils.translation import ugettext_lazy as _
17 from django.utils.translation import ugettext_lazy as _
16 import magic
18 import magic
17 import os
19 import os
18
20
19 import boards
21 import boards
20 from boards.settings import get_bool
22 from boards.settings import get_bool
21 from neboard import settings
23 from neboard import settings
22
24
23 CACHE_KEY_DELIMITER = '_'
25 CACHE_KEY_DELIMITER = '_'
24
26
25 HTTP_FORWARDED = 'HTTP_X_FORWARDED_FOR'
27 HTTP_FORWARDED = 'HTTP_X_FORWARDED_FOR'
26 META_REMOTE_ADDR = 'REMOTE_ADDR'
28 META_REMOTE_ADDR = 'REMOTE_ADDR'
27
29
28 SETTING_MESSAGES = 'Messages'
30 SETTING_MESSAGES = 'Messages'
29 SETTING_ANON_MODE = 'AnonymousMode'
31 SETTING_ANON_MODE = 'AnonymousMode'
30
32
31 ANON_IP = '127.0.0.1'
33 ANON_IP = '127.0.0.1'
32
34
33 FILE_EXTENSION_DELIMITER = '.'
35 FILE_EXTENSION_DELIMITER = '.'
34
36
35 KNOWN_DOMAINS = (
37 KNOWN_DOMAINS = (
36 'org.ru',
38 'org.ru',
37 )
39 )
38
40
39
41
40 def is_anonymous_mode():
42 def is_anonymous_mode():
41 return get_bool(SETTING_MESSAGES, SETTING_ANON_MODE)
43 return get_bool(SETTING_MESSAGES, SETTING_ANON_MODE)
42
44
43
45
44 def get_client_ip(request):
46 def get_client_ip(request):
45 if is_anonymous_mode():
47 if is_anonymous_mode():
46 ip = ANON_IP
48 ip = ANON_IP
47 else:
49 else:
48 x_forwarded_for = request.META.get(HTTP_FORWARDED)
50 x_forwarded_for = request.META.get(HTTP_FORWARDED)
49 if x_forwarded_for:
51 if x_forwarded_for:
50 ip = x_forwarded_for.split(',')[-1].strip()
52 ip = x_forwarded_for.split(',')[-1].strip()
51 else:
53 else:
52 ip = request.META.get(META_REMOTE_ADDR)
54 ip = request.META.get(META_REMOTE_ADDR)
53 return ip
55 return ip
54
56
55
57
56 # TODO The output format is not epoch because it includes microseconds
58 # TODO The output format is not epoch because it includes microseconds
57 def datetime_to_epoch(datetime):
59 def datetime_to_epoch(datetime):
58 return int(time.mktime(timezone.localtime(
60 return int(time.mktime(timezone.localtime(
59 datetime,timezone.get_current_timezone()).timetuple())
61 datetime,timezone.get_current_timezone()).timetuple())
60 * 1000000 + datetime.microsecond)
62 * 1000000 + datetime.microsecond)
61
63
62
64
63 def get_websocket_token(user_id='', timestamp=''):
65 def get_websocket_token(user_id='', timestamp=''):
64 """
66 """
65 Create token to validate information provided by new connection.
67 Create token to validate information provided by new connection.
66 """
68 """
67
69
68 sign = hmac.new(settings.CENTRIFUGE_PROJECT_SECRET.encode())
70 sign = hmac.new(settings.CENTRIFUGE_PROJECT_SECRET.encode())
69 sign.update(settings.CENTRIFUGE_PROJECT_ID.encode())
71 sign.update(settings.CENTRIFUGE_PROJECT_ID.encode())
70 sign.update(user_id.encode())
72 sign.update(user_id.encode())
71 sign.update(timestamp.encode())
73 sign.update(timestamp.encode())
72 token = sign.hexdigest()
74 token = sign.hexdigest()
73
75
74 return token
76 return token
75
77
76
78
77 # TODO Test this carefully
79 # TODO Test this carefully
78 def cached_result(key_method=None):
80 def cached_result(key_method=None):
79 """
81 """
80 Caches method result in the Django's cache system, persisted by object name,
82 Caches method result in the Django's cache system, persisted by object name,
81 object name, model id if object is a Django model, args and kwargs if any.
83 object name, model id if object is a Django model, args and kwargs if any.
82 """
84 """
83 def _cached_result(function):
85 def _cached_result(function):
84 def inner_func(obj, *args, **kwargs):
86 def inner_func(obj, *args, **kwargs):
85 cache_key_params = [obj.__class__.__name__, function.__name__]
87 cache_key_params = [obj.__class__.__name__, function.__name__]
86
88
87 cache_key_params += args
89 cache_key_params += args
88 for key, value in kwargs:
90 for key, value in kwargs:
89 cache_key_params.append(key + ':' + value)
91 cache_key_params.append(key + ':' + value)
90
92
91 if isinstance(obj, Model):
93 if isinstance(obj, Model):
92 cache_key_params.append(str(obj.id))
94 cache_key_params.append(str(obj.id))
93
95
94 if key_method is not None:
96 if key_method is not None:
95 cache_key_params += [str(arg) for arg in key_method(obj)]
97 cache_key_params += [str(arg) for arg in key_method(obj)]
96
98
97 cache_key = CACHE_KEY_DELIMITER.join(cache_key_params)
99 cache_key = CACHE_KEY_DELIMITER.join(cache_key_params)
98
100
99 persisted_result = cache.get(cache_key)
101 persisted_result = cache.get(cache_key)
100 if persisted_result is not None:
102 if persisted_result is not None:
101 result = persisted_result
103 result = persisted_result
102 else:
104 else:
103 result = function(obj, *args, **kwargs)
105 result = function(obj, *args, **kwargs)
104 if result is not None:
106 if result is not None:
105 cache.set(cache_key, result)
107 cache.set(cache_key, result)
106
108
107 return result
109 return result
108
110
109 return inner_func
111 return inner_func
110 return _cached_result
112 return _cached_result
111
113
112
114
113 def get_file_hash(file) -> str:
115 def get_file_hash(file) -> str:
114 md5 = hashlib.md5()
116 md5 = hashlib.md5()
115 for chunk in file.chunks():
117 for chunk in file.chunks():
116 md5.update(chunk)
118 md5.update(chunk)
117 return md5.hexdigest()
119 return md5.hexdigest()
118
120
119
121
120 def validate_file_size(size: int):
122 def validate_file_size(size: int):
121 max_size = boards.settings.get_int('Forms', 'MaxFileSize')
123 max_size = boards.settings.get_int('Forms', 'MaxFileSize')
122 if size > max_size:
124 if size > max_size:
123 raise forms.ValidationError(
125 raise forms.ValidationError(
124 _('File must be less than %s but is %s.')
126 _('File must be less than %s but is %s.')
125 % (filesizeformat(max_size), filesizeformat(size)))
127 % (filesizeformat(max_size), filesizeformat(size)))
126
128
127
129
128 def get_extension(filename):
130 def get_extension(filename):
129 return filename.split(FILE_EXTENSION_DELIMITER)[-1:][0]
131 return filename.split(FILE_EXTENSION_DELIMITER)[-1:][0]
130
132
131
133
132 def get_upload_filename(model_instance, old_filename):
134 def get_upload_filename(model_instance, old_filename):
133 # TODO Use something other than random number in file name
134 extension = get_extension(old_filename)
135 extension = get_extension(old_filename)
135 new_name = '{}{}.{}'.format(
136 new_name = '{}.{}'.format(uuid.uuid4(), extension)
136 str(int(time.mktime(time.gmtime()))),
137 str(int(random() * 1000)),
138 extension)
139
137
140 return os.path.join(FILE_DIRECTORY, new_name)
138 return os.path.join(FILE_DIRECTORY, new_name)
141
139
142
140
143 def get_file_mimetype(file) -> str:
141 def get_file_mimetype(file) -> str:
144 file_type = magic.from_buffer(file.chunks().__next__(), mime=True)
142 file_type = magic.from_buffer(file.chunks().__next__(), mime=True)
145 if file_type is None:
143 if file_type is None:
146 file_type = 'application/octet-stream'
144 file_type = 'application/octet-stream'
147 elif type(file_type) == bytes:
145 elif type(file_type) == bytes:
148 file_type = file_type.decode()
146 file_type = file_type.decode()
149 return file_type
147 return file_type
150
148
151
149
152 def get_domain(url: str) -> str:
150 def get_domain(url: str) -> str:
153 """
151 """
154 Gets domain from an URL with random number of domain levels.
152 Gets domain from an URL with random number of domain levels.
155 """
153 """
156 domain_parts = url.split('/')
154 domain_parts = url.split('/')
157 if len(domain_parts) >= 2:
155 if len(domain_parts) >= 2:
158 full_domain = domain_parts[2]
156 full_domain = domain_parts[2]
159 else:
157 else:
160 full_domain = ''
158 full_domain = ''
161
159
162 result = full_domain
160 result = full_domain
163 if full_domain:
161 if full_domain:
164 levels = full_domain.split('.')
162 levels = full_domain.split('.')
165 if len(levels) >= 2:
163 if len(levels) >= 2:
166 top = levels[-1]
164 top = levels[-1]
167 second = levels[-2]
165 second = levels[-2]
168
166
169 has_third_level = len(levels) > 2
167 has_third_level = len(levels) > 2
170 if has_third_level:
168 if has_third_level:
171 third = levels[-3]
169 third = levels[-3]
172
170
173 if has_third_level and ('{}.{}'.format(second, top) in KNOWN_DOMAINS):
171 if has_third_level and ('{}.{}'.format(second, top) in KNOWN_DOMAINS):
174 result = '{}.{}.{}'.format(third, second, top)
172 result = '{}.{}.{}'.format(third, second, top)
175 else:
173 else:
176 result = '{}.{}'.format(second, top)
174 result = '{}.{}'.format(second, top)
177
175
178 return result
176 return result
179
177
General Comments 0
You need to be logged in to leave comments. Login now