##// END OF EJS Templates
Use python-magic to get file types
neko259 -
r1369:224c8f01 default
parent child Browse files
Show More
@@ -1,43 +1,46 b''
1 import magic
2
1 from django.db import models
3 from django.db import models
2
4
3 from boards import utils
5 from boards import utils
4 from boards.models.attachment.viewers import get_viewers, AbstractViewer
6 from boards.models.attachment.viewers import get_viewers, AbstractViewer
5 from boards.utils import get_upload_filename
7 from boards.utils import get_upload_filename
6
8
7 FILES_DIRECTORY = 'files/'
9 FILES_DIRECTORY = 'files/'
8 FILE_EXTENSION_DELIMITER = '.'
10 FILE_EXTENSION_DELIMITER = '.'
9
11
10
12
11 class AttachmentManager(models.Manager):
13 class AttachmentManager(models.Manager):
12 def create_with_hash(self, file):
14 def create_with_hash(self, file):
13 file_hash = utils.get_file_hash(file)
15 file_hash = utils.get_file_hash(file)
14 existing = self.filter(hash=file_hash)
16 existing = self.filter(hash=file_hash)
15 if len(existing) > 0:
17 if len(existing) > 0:
16 attachment = existing[0]
18 attachment = existing[0]
17 else:
19 else:
18 file_type = file.name.split(FILE_EXTENSION_DELIMITER)[-1].lower()
20 file_type = magic.from_buffer(file.chunks().__next__(), mime=True)\
21 .decode().split('/')[-1]
19 attachment = Attachment.objects.create(
22 attachment = Attachment.objects.create(
20 file=file, mimetype=file_type, hash=file_hash)
23 file=file, mimetype=file_type, hash=file_hash)
21
24
22 return attachment
25 return attachment
23
26
24
27
25 class Attachment(models.Model):
28 class Attachment(models.Model):
26 objects = AttachmentManager()
29 objects = AttachmentManager()
27
30
28 file = models.FileField(upload_to=get_upload_filename)
31 file = models.FileField(upload_to=get_upload_filename)
29 mimetype = models.CharField(max_length=50)
32 mimetype = models.CharField(max_length=50)
30 hash = models.CharField(max_length=36)
33 hash = models.CharField(max_length=36)
31
34
32 def get_view(self):
35 def get_view(self):
33 file_viewer = None
36 file_viewer = None
34 for viewer in get_viewers():
37 for viewer in get_viewers():
35 if viewer.supports(self.mimetype):
38 if viewer.supports(self.mimetype):
36 file_viewer = viewer
39 file_viewer = viewer
37 break
40 break
38 if file_viewer is None:
41 if file_viewer is None:
39 file_viewer = AbstractViewer
42 file_viewer = AbstractViewer
40
43
41 return file_viewer(self.file, self.mimetype).get_view()
44 return file_viewer(self.file, self.mimetype).get_view()
42
45
43
46
@@ -1,138 +1,142 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 from random import random
5 from random import random
6 import time
6 import time
7 import hmac
7 import hmac
8
8
9 from django.core.cache import cache
9 from django.core.cache import cache
10 from django.db.models import Model
10 from django.db.models import Model
11 from django import forms
11 from django import forms
12 from django.utils import timezone
12 from django.utils import timezone
13 from django.utils.translation import ugettext_lazy as _
13 from django.utils.translation import ugettext_lazy as _
14 from portage import os
14 from portage import os
15
15
16 import boards
16 import boards
17 from boards.settings import get_bool
17 from boards.settings import get_bool
18 from neboard import settings
18 from neboard import settings
19
19
20 CACHE_KEY_DELIMITER = '_'
20 CACHE_KEY_DELIMITER = '_'
21 PERMISSION_MODERATE = 'moderation'
21 PERMISSION_MODERATE = 'moderation'
22
22
23 HTTP_FORWARDED = 'HTTP_X_FORWARDED_FOR'
23 HTTP_FORWARDED = 'HTTP_X_FORWARDED_FOR'
24 META_REMOTE_ADDR = 'REMOTE_ADDR'
24 META_REMOTE_ADDR = 'REMOTE_ADDR'
25
25
26 SETTING_MESSAGES = 'Messages'
26 SETTING_MESSAGES = 'Messages'
27 SETTING_ANON_MODE = 'AnonymousMode'
27 SETTING_ANON_MODE = 'AnonymousMode'
28
28
29 ANON_IP = '127.0.0.1'
29 ANON_IP = '127.0.0.1'
30
30
31 UPLOAD_DIRS ={
31 UPLOAD_DIRS ={
32 'PostImage': 'images/',
32 'PostImage': 'images/',
33 'Attachment': 'files/',
33 'Attachment': 'files/',
34 }
34 }
35 FILE_EXTENSION_DELIMITER = '.'
35 FILE_EXTENSION_DELIMITER = '.'
36
36
37
37
38 def is_anonymous_mode():
38 def is_anonymous_mode():
39 return get_bool(SETTING_MESSAGES, SETTING_ANON_MODE)
39 return get_bool(SETTING_MESSAGES, SETTING_ANON_MODE)
40
40
41
41
42 def get_client_ip(request):
42 def get_client_ip(request):
43 if is_anonymous_mode():
43 if is_anonymous_mode():
44 ip = ANON_IP
44 ip = ANON_IP
45 else:
45 else:
46 x_forwarded_for = request.META.get(HTTP_FORWARDED)
46 x_forwarded_for = request.META.get(HTTP_FORWARDED)
47 if x_forwarded_for:
47 if x_forwarded_for:
48 ip = x_forwarded_for.split(',')[-1].strip()
48 ip = x_forwarded_for.split(',')[-1].strip()
49 else:
49 else:
50 ip = request.META.get(META_REMOTE_ADDR)
50 ip = request.META.get(META_REMOTE_ADDR)
51 return ip
51 return ip
52
52
53
53
54 # TODO The output format is not epoch because it includes microseconds
54 # TODO The output format is not epoch because it includes microseconds
55 def datetime_to_epoch(datetime):
55 def datetime_to_epoch(datetime):
56 return int(time.mktime(timezone.localtime(
56 return int(time.mktime(timezone.localtime(
57 datetime,timezone.get_current_timezone()).timetuple())
57 datetime,timezone.get_current_timezone()).timetuple())
58 * 1000000 + datetime.microsecond)
58 * 1000000 + datetime.microsecond)
59
59
60
60
61 def get_websocket_token(user_id='', timestamp=''):
61 def get_websocket_token(user_id='', timestamp=''):
62 """
62 """
63 Create token to validate information provided by new connection.
63 Create token to validate information provided by new connection.
64 """
64 """
65
65
66 sign = hmac.new(settings.CENTRIFUGE_PROJECT_SECRET.encode())
66 sign = hmac.new(settings.CENTRIFUGE_PROJECT_SECRET.encode())
67 sign.update(settings.CENTRIFUGE_PROJECT_ID.encode())
67 sign.update(settings.CENTRIFUGE_PROJECT_ID.encode())
68 sign.update(user_id.encode())
68 sign.update(user_id.encode())
69 sign.update(timestamp.encode())
69 sign.update(timestamp.encode())
70 token = sign.hexdigest()
70 token = sign.hexdigest()
71
71
72 return token
72 return token
73
73
74
74
75 def cached_result(key_method=None):
75 def cached_result(key_method=None):
76 """
76 """
77 Caches method result in the Django's cache system, persisted by object name,
77 Caches method result in the Django's cache system, persisted by object name,
78 object name and model id if object is a Django model.
78 object name and model id if object is a Django model.
79 """
79 """
80 def _cached_result(function):
80 def _cached_result(function):
81 def inner_func(obj, *args, **kwargs):
81 def inner_func(obj, *args, **kwargs):
82 # TODO Include method arguments to the cache key
82 # TODO Include method arguments to the cache key
83 cache_key_params = [obj.__class__.__name__, function.__name__]
83 cache_key_params = [obj.__class__.__name__, function.__name__]
84 if isinstance(obj, Model):
84 if isinstance(obj, Model):
85 cache_key_params.append(str(obj.id))
85 cache_key_params.append(str(obj.id))
86
86
87 if key_method is not None:
87 if key_method is not None:
88 cache_key_params += [str(arg) for arg in key_method(obj)]
88 cache_key_params += [str(arg) for arg in key_method(obj)]
89
89
90 cache_key = CACHE_KEY_DELIMITER.join(cache_key_params)
90 cache_key = CACHE_KEY_DELIMITER.join(cache_key_params)
91
91
92 persisted_result = cache.get(cache_key)
92 persisted_result = cache.get(cache_key)
93 if persisted_result is not None:
93 if persisted_result is not None:
94 result = persisted_result
94 result = persisted_result
95 else:
95 else:
96 result = function(obj, *args, **kwargs)
96 result = function(obj, *args, **kwargs)
97 cache.set(cache_key, result)
97 cache.set(cache_key, result)
98
98
99 return result
99 return result
100
100
101 return inner_func
101 return inner_func
102 return _cached_result
102 return _cached_result
103
103
104
104
105 def is_moderator(request):
105 def is_moderator(request):
106 try:
106 try:
107 moderate = request.user.has_perm(PERMISSION_MODERATE)
107 moderate = request.user.has_perm(PERMISSION_MODERATE)
108 except AttributeError:
108 except AttributeError:
109 moderate = False
109 moderate = False
110
110
111 return moderate
111 return moderate
112
112
113
113
114 def get_file_hash(file) -> str:
114 def get_file_hash(file) -> str:
115 md5 = hashlib.md5()
115 md5 = hashlib.md5()
116 for chunk in file.chunks():
116 for chunk in file.chunks():
117 md5.update(chunk)
117 md5.update(chunk)
118 return md5.hexdigest()
118 return md5.hexdigest()
119
119
120
120
121 def validate_file_size(size: int):
121 def validate_file_size(size: int):
122 max_size = boards.settings.get_int('Forms', 'MaxFileSize')
122 max_size = boards.settings.get_int('Forms', 'MaxFileSize')
123 if size > max_size:
123 if size > max_size:
124 raise forms.ValidationError(
124 raise forms.ValidationError(
125 _('File must be less than %s bytes')
125 _('File must be less than %s bytes')
126 % str(max_size))
126 % str(max_size))
127
127
128
128
129 def get_upload_filename(model_instance, old_filename):
129 def get_upload_filename(model_instance, old_filename):
130 # TODO Use something other than random number in file name
130 # TODO Use something other than random number in file name
131 if hasattr(model_instance, 'file_type'):
132 extension = model_instance.file_type
133 else:
134 extension = old_filename.split(FILE_EXTENSION_DELIMITER)[-1:][0]
131 new_name = '{}{}.{}'.format(
135 new_name = '{}{}.{}'.format(
132 str(int(time.mktime(time.gmtime()))),
136 str(int(time.mktime(time.gmtime()))),
133 str(int(random() * 1000)),
137 str(int(random() * 1000)),
134 old_filename.split(FILE_EXTENSION_DELIMITER)[-1:][0])
138 extension)
135
139
136 directory = UPLOAD_DIRS[type(model_instance).__name__]
140 directory = UPLOAD_DIRS[type(model_instance).__name__]
137
141
138 return os.path.join(directory, new_name)
142 return os.path.join(directory, new_name)
@@ -1,9 +1,10 b''
1 magic
1 pytube
2 pytube
2 requests
3 requests
3 adjacent
4 adjacent
4 django-haystack
5 django-haystack
5 pillow
6 pillow
6 django>=1.8
7 django>=1.8
7 bbcode
8 bbcode
8 django-debug-toolbar
9 django-debug-toolbar
9 pytz
10 pytz
General Comments 0
You need to be logged in to leave comments. Login now