##// END OF EJS Templates
Do not run validation on files while performing sync
neko259 -
r1833:3c5ca02b default
parent child Browse files
Show More
@@ -1,101 +1,101 b''
1 import re
1 import re
2
2
3 import requests
3 import requests
4 from django.core.files.uploadedfile import TemporaryUploadedFile
4 from django.core.files.uploadedfile import TemporaryUploadedFile
5 from pytube import YouTube
5 from pytube import YouTube
6
6
7 from boards.utils import validate_file_size
7 from boards.utils import validate_file_size
8
8
9 YOUTUBE_VIDEO_FORMAT = 'webm'
9 YOUTUBE_VIDEO_FORMAT = 'webm'
10
10
11 HTTP_RESULT_OK = 200
11 HTTP_RESULT_OK = 200
12
12
13 HEADER_CONTENT_LENGTH = 'content-length'
13 HEADER_CONTENT_LENGTH = 'content-length'
14 HEADER_CONTENT_TYPE = 'content-type'
14 HEADER_CONTENT_TYPE = 'content-type'
15
15
16 FILE_DOWNLOAD_CHUNK_BYTES = 200000
16 FILE_DOWNLOAD_CHUNK_BYTES = 200000
17
17
18 REGEX_YOUTUBE_URL = re.compile(r'https?://((www\.)?youtube\.com/watch\?v=|youtu.be/)[-\w]+')
18 REGEX_YOUTUBE_URL = re.compile(r'https?://((www\.)?youtube\.com/watch\?v=|youtu.be/)[-\w]+')
19 REGEX_MAGNET = re.compile(r'magnet:\?xt=urn:(btih:)?[a-z0-9]{20,50}.*')
19 REGEX_MAGNET = re.compile(r'magnet:\?xt=urn:(btih:)?[a-z0-9]{20,50}.*')
20
20
21 TYPE_URL_ONLY = (
21 TYPE_URL_ONLY = (
22 'application/xhtml+xml',
22 'application/xhtml+xml',
23 'text/html',
23 'text/html',
24 )
24 )
25
25
26
26
27 class Downloader:
27 class Downloader:
28 @staticmethod
28 @staticmethod
29 def handles(url: str) -> bool:
29 def handles(url: str) -> bool:
30 return True
30 return True
31
31
32 @staticmethod
32 @staticmethod
33 def download(url: str):
33 def download(url: str, validate):
34 # Verify content headers
34 # Verify content headers
35 response_head = requests.head(url, verify=False)
35 response_head = requests.head(url, verify=False)
36 content_type = response_head.headers[HEADER_CONTENT_TYPE].split(';')[0]
36 content_type = response_head.headers[HEADER_CONTENT_TYPE].split(';')[0]
37 if content_type in TYPE_URL_ONLY:
37 if validate and content_type in TYPE_URL_ONLY:
38 return None
38 return None
39
39
40 length_header = response_head.headers.get(HEADER_CONTENT_LENGTH)
40 length_header = response_head.headers.get(HEADER_CONTENT_LENGTH)
41 if length_header:
41 if validate and length_header:
42 length = int(length_header)
42 length = int(length_header)
43 validate_file_size(length)
43 validate_file_size(length)
44 # Get the actual content into memory
44 # Get the actual content into memory
45 response = requests.get(url, verify=False, stream=True)
45 response = requests.get(url, verify=False, stream=True)
46
46
47 if response.status_code == HTTP_RESULT_OK:
47 if response.status_code == HTTP_RESULT_OK:
48 # Download file, stop if the size exceeds limit
48 # Download file, stop if the size exceeds limit
49 size = 0
49 size = 0
50
50
51 # Set a dummy file name that will be replaced
51 # Set a dummy file name that will be replaced
52 # anyway, just keep the valid extension
52 # anyway, just keep the valid extension
53 filename = 'file.' + content_type.split('/')[1]
53 filename = 'file.' + content_type.split('/')[1]
54
54
55 file = TemporaryUploadedFile(filename, content_type, 0, None, None)
55 file = TemporaryUploadedFile(filename, content_type, 0, None, None)
56 for chunk in response.iter_content(FILE_DOWNLOAD_CHUNK_BYTES):
56 for chunk in response.iter_content(FILE_DOWNLOAD_CHUNK_BYTES):
57 size += len(chunk)
57 size += len(chunk)
58 validate_file_size(size)
58 validate_file_size(size)
59 file.write(chunk)
59 file.write(chunk)
60
60
61 return file
61 return file
62
62
63
63
64 class YouTubeDownloader(Downloader):
64 class YouTubeDownloader(Downloader):
65 @staticmethod
65 @staticmethod
66 def download(url: str):
66 def download(url: str, validate):
67 yt = YouTube()
67 yt = YouTube()
68 yt.from_url(url)
68 yt.from_url(url)
69 videos = yt.filter(YOUTUBE_VIDEO_FORMAT)
69 videos = yt.filter(YOUTUBE_VIDEO_FORMAT)
70 if len(videos) > 0:
70 if len(videos) > 0:
71 video = videos[0]
71 video = videos[0]
72 return Downloader.download(video.url)
72 return Downloader.download(video.url)
73
73
74 @staticmethod
74 @staticmethod
75 def handles(url: str) -> bool:
75 def handles(url: str) -> bool:
76 return REGEX_YOUTUBE_URL.match(url) is not None
76 return REGEX_YOUTUBE_URL.match(url) is not None
77
77
78
78
79 class NothingDownloader(Downloader):
79 class NothingDownloader(Downloader):
80 @staticmethod
80 @staticmethod
81 def handles(url: str) -> bool:
81 def handles(url: str) -> bool:
82 return REGEX_MAGNET.match(url)
82 return REGEX_MAGNET.match(url)
83
83
84 @staticmethod
84 @staticmethod
85 def download(url: str):
85 def download(url: str, validate):
86 return None
86 return None
87
87
88
88
89 DOWNLOADERS = (
89 DOWNLOADERS = (
90 YouTubeDownloader,
90 YouTubeDownloader,
91 NothingDownloader,
91 NothingDownloader,
92 Downloader,
92 Downloader,
93 )
93 )
94
94
95
95
96 def download(url):
96 def download(url, validate=True):
97 for downloader in DOWNLOADERS:
97 for downloader in DOWNLOADERS:
98 if downloader.handles(url):
98 if downloader.handles(url):
99 return downloader.download(url)
99 return downloader.download(url, validate=validate)
100 raise Exception('No downloader supports this URL.')
100 raise Exception('No downloader supports this URL.')
101
101
@@ -1,331 +1,331 b''
1 import xml.etree.ElementTree as et
1 import xml.etree.ElementTree as et
2 import logging
2 import logging
3
3
4 from boards.abstracts.exceptions import SyncException
4 from boards.abstracts.exceptions import SyncException
5 from boards.models import KeyPair, GlobalId, Signature, Post, Tag
5 from boards.models import KeyPair, GlobalId, Signature, Post, Tag
6 from boards.models.attachment.downloaders import download
6 from boards.models.attachment.downloaders import download
7 from boards.utils import get_file_mimetype, get_file_hash
7 from boards.utils import get_file_mimetype, get_file_hash
8 from django.db import transaction
8 from django.db import transaction
9
9
10 EXCEPTION_NODE = 'Sync node returned an error: {}.'
10 EXCEPTION_NODE = 'Sync node returned an error: {}.'
11 EXCEPTION_DOWNLOAD = 'File was not downloaded.'
11 EXCEPTION_DOWNLOAD = 'File was not downloaded.'
12 EXCEPTION_HASH = 'File hash does not match attachment hash.'
12 EXCEPTION_HASH = 'File hash does not match attachment hash.'
13 EXCEPTION_SIGNATURE = 'Invalid model signature for {}.'
13 EXCEPTION_SIGNATURE = 'Invalid model signature for {}.'
14 EXCEPTION_AUTHOR_SIGNATURE = 'Model {} has no author signature.'
14 EXCEPTION_AUTHOR_SIGNATURE = 'Model {} has no author signature.'
15 ENCODING_UNICODE = 'unicode'
15 ENCODING_UNICODE = 'unicode'
16
16
17 TAG_MODEL = 'model'
17 TAG_MODEL = 'model'
18 TAG_REQUEST = 'request'
18 TAG_REQUEST = 'request'
19 TAG_RESPONSE = 'response'
19 TAG_RESPONSE = 'response'
20 TAG_ID = 'id'
20 TAG_ID = 'id'
21 TAG_STATUS = 'status'
21 TAG_STATUS = 'status'
22 TAG_MODELS = 'models'
22 TAG_MODELS = 'models'
23 TAG_TITLE = 'title'
23 TAG_TITLE = 'title'
24 TAG_TEXT = 'text'
24 TAG_TEXT = 'text'
25 TAG_THREAD = 'thread'
25 TAG_THREAD = 'thread'
26 TAG_PUB_TIME = 'pub-time'
26 TAG_PUB_TIME = 'pub-time'
27 TAG_SIGNATURES = 'signatures'
27 TAG_SIGNATURES = 'signatures'
28 TAG_SIGNATURE = 'signature'
28 TAG_SIGNATURE = 'signature'
29 TAG_CONTENT = 'content'
29 TAG_CONTENT = 'content'
30 TAG_ATTACHMENTS = 'attachments'
30 TAG_ATTACHMENTS = 'attachments'
31 TAG_ATTACHMENT = 'attachment'
31 TAG_ATTACHMENT = 'attachment'
32 TAG_TAGS = 'tags'
32 TAG_TAGS = 'tags'
33 TAG_TAG = 'tag'
33 TAG_TAG = 'tag'
34 TAG_ATTACHMENT_REFS = 'attachment-refs'
34 TAG_ATTACHMENT_REFS = 'attachment-refs'
35 TAG_ATTACHMENT_REF = 'attachment-ref'
35 TAG_ATTACHMENT_REF = 'attachment-ref'
36 TAG_TRIPCODE = 'tripcode'
36 TAG_TRIPCODE = 'tripcode'
37 TAG_VERSION = 'version'
37 TAG_VERSION = 'version'
38
38
39 TYPE_GET = 'get'
39 TYPE_GET = 'get'
40
40
41 ATTR_VERSION = 'version'
41 ATTR_VERSION = 'version'
42 ATTR_TYPE = 'type'
42 ATTR_TYPE = 'type'
43 ATTR_NAME = 'name'
43 ATTR_NAME = 'name'
44 ATTR_VALUE = 'value'
44 ATTR_VALUE = 'value'
45 ATTR_MIMETYPE = 'mimetype'
45 ATTR_MIMETYPE = 'mimetype'
46 ATTR_KEY = 'key'
46 ATTR_KEY = 'key'
47 ATTR_REF = 'ref'
47 ATTR_REF = 'ref'
48 ATTR_URL = 'url'
48 ATTR_URL = 'url'
49 ATTR_ID_TYPE = 'id-type'
49 ATTR_ID_TYPE = 'id-type'
50
50
51 ID_TYPE_MD5 = 'md5'
51 ID_TYPE_MD5 = 'md5'
52 ID_TYPE_URL = 'url'
52 ID_TYPE_URL = 'url'
53
53
54 STATUS_SUCCESS = 'success'
54 STATUS_SUCCESS = 'success'
55
55
56
56
57 logger = logging.getLogger('boards.sync')
57 logger = logging.getLogger('boards.sync')
58
58
59
59
60 class SyncManager:
60 class SyncManager:
61 @staticmethod
61 @staticmethod
62 def generate_response_get(model_list: list):
62 def generate_response_get(model_list: list):
63 response = et.Element(TAG_RESPONSE)
63 response = et.Element(TAG_RESPONSE)
64
64
65 status = et.SubElement(response, TAG_STATUS)
65 status = et.SubElement(response, TAG_STATUS)
66 status.text = STATUS_SUCCESS
66 status.text = STATUS_SUCCESS
67
67
68 models = et.SubElement(response, TAG_MODELS)
68 models = et.SubElement(response, TAG_MODELS)
69
69
70 for post in model_list:
70 for post in model_list:
71 model = et.SubElement(models, TAG_MODEL)
71 model = et.SubElement(models, TAG_MODEL)
72 model.set(ATTR_NAME, 'post')
72 model.set(ATTR_NAME, 'post')
73
73
74 global_id = post.global_id
74 global_id = post.global_id
75
75
76 attachments = post.attachments.all()
76 attachments = post.attachments.all()
77 if global_id.content:
77 if global_id.content:
78 model.append(et.fromstring(global_id.content))
78 model.append(et.fromstring(global_id.content))
79 if len(attachments) > 0:
79 if len(attachments) > 0:
80 internal_attachments = False
80 internal_attachments = False
81 for attachment in attachments:
81 for attachment in attachments:
82 if attachment.is_internal():
82 if attachment.is_internal():
83 internal_attachments = True
83 internal_attachments = True
84 break
84 break
85
85
86 if internal_attachments:
86 if internal_attachments:
87 attachment_refs = et.SubElement(model, TAG_ATTACHMENT_REFS)
87 attachment_refs = et.SubElement(model, TAG_ATTACHMENT_REFS)
88 for file in attachments:
88 for file in attachments:
89 SyncManager._attachment_to_xml(
89 SyncManager._attachment_to_xml(
90 None, attachment_refs, file)
90 None, attachment_refs, file)
91 else:
91 else:
92 content_tag = et.SubElement(model, TAG_CONTENT)
92 content_tag = et.SubElement(model, TAG_CONTENT)
93
93
94 tag_id = et.SubElement(content_tag, TAG_ID)
94 tag_id = et.SubElement(content_tag, TAG_ID)
95 global_id.to_xml_element(tag_id)
95 global_id.to_xml_element(tag_id)
96
96
97 title = et.SubElement(content_tag, TAG_TITLE)
97 title = et.SubElement(content_tag, TAG_TITLE)
98 title.text = post.title
98 title.text = post.title
99
99
100 text = et.SubElement(content_tag, TAG_TEXT)
100 text = et.SubElement(content_tag, TAG_TEXT)
101 text.text = post.get_sync_text()
101 text.text = post.get_sync_text()
102
102
103 thread = post.get_thread()
103 thread = post.get_thread()
104 if post.is_opening():
104 if post.is_opening():
105 tag_tags = et.SubElement(content_tag, TAG_TAGS)
105 tag_tags = et.SubElement(content_tag, TAG_TAGS)
106 for tag in thread.get_tags():
106 for tag in thread.get_tags():
107 tag_tag = et.SubElement(tag_tags, TAG_TAG)
107 tag_tag = et.SubElement(tag_tags, TAG_TAG)
108 tag_tag.text = tag.name
108 tag_tag.text = tag.name
109 else:
109 else:
110 tag_thread = et.SubElement(content_tag, TAG_THREAD)
110 tag_thread = et.SubElement(content_tag, TAG_THREAD)
111 thread_id = et.SubElement(tag_thread, TAG_ID)
111 thread_id = et.SubElement(tag_thread, TAG_ID)
112 thread.get_opening_post().global_id.to_xml_element(thread_id)
112 thread.get_opening_post().global_id.to_xml_element(thread_id)
113
113
114 pub_time = et.SubElement(content_tag, TAG_PUB_TIME)
114 pub_time = et.SubElement(content_tag, TAG_PUB_TIME)
115 pub_time.text = str(post.get_pub_time_str())
115 pub_time.text = str(post.get_pub_time_str())
116
116
117 if post.tripcode:
117 if post.tripcode:
118 tripcode = et.SubElement(content_tag, TAG_TRIPCODE)
118 tripcode = et.SubElement(content_tag, TAG_TRIPCODE)
119 tripcode.text = post.tripcode
119 tripcode.text = post.tripcode
120
120
121 if len(attachments) > 0:
121 if len(attachments) > 0:
122 attachments_tag = et.SubElement(content_tag, TAG_ATTACHMENTS)
122 attachments_tag = et.SubElement(content_tag, TAG_ATTACHMENTS)
123
123
124 internal_attachments = False
124 internal_attachments = False
125 for attachment in attachments:
125 for attachment in attachments:
126 if attachment.is_internal():
126 if attachment.is_internal():
127 internal_attachments = True
127 internal_attachments = True
128 break
128 break
129
129
130 if internal_attachments:
130 if internal_attachments:
131 attachment_refs = et.SubElement(model, TAG_ATTACHMENT_REFS)
131 attachment_refs = et.SubElement(model, TAG_ATTACHMENT_REFS)
132 else:
132 else:
133 attachment_refs = None
133 attachment_refs = None
134
134
135 for file in attachments:
135 for file in attachments:
136 SyncManager._attachment_to_xml(
136 SyncManager._attachment_to_xml(
137 attachments_tag, attachment_refs, file)
137 attachments_tag, attachment_refs, file)
138 version_tag = et.SubElement(content_tag, TAG_VERSION)
138 version_tag = et.SubElement(content_tag, TAG_VERSION)
139 version_tag.text = str(post.version)
139 version_tag.text = str(post.version)
140
140
141 global_id.content = et.tostring(content_tag, ENCODING_UNICODE)
141 global_id.content = et.tostring(content_tag, ENCODING_UNICODE)
142 global_id.save()
142 global_id.save()
143
143
144 signatures_tag = et.SubElement(model, TAG_SIGNATURES)
144 signatures_tag = et.SubElement(model, TAG_SIGNATURES)
145 post_signatures = global_id.signature_set.all()
145 post_signatures = global_id.signature_set.all()
146 if post_signatures:
146 if post_signatures:
147 signatures = post_signatures
147 signatures = post_signatures
148 else:
148 else:
149 key = KeyPair.objects.get(public_key=global_id.key)
149 key = KeyPair.objects.get(public_key=global_id.key)
150 signature = Signature(
150 signature = Signature(
151 key_type=key.key_type,
151 key_type=key.key_type,
152 key=key.public_key,
152 key=key.public_key,
153 signature=key.sign(global_id.content),
153 signature=key.sign(global_id.content),
154 global_id=global_id,
154 global_id=global_id,
155 )
155 )
156 signature.save()
156 signature.save()
157 signatures = [signature]
157 signatures = [signature]
158 for signature in signatures:
158 for signature in signatures:
159 signature_tag = et.SubElement(signatures_tag, TAG_SIGNATURE)
159 signature_tag = et.SubElement(signatures_tag, TAG_SIGNATURE)
160 signature_tag.set(ATTR_TYPE, signature.key_type)
160 signature_tag.set(ATTR_TYPE, signature.key_type)
161 signature_tag.set(ATTR_VALUE, signature.signature)
161 signature_tag.set(ATTR_VALUE, signature.signature)
162 signature_tag.set(ATTR_KEY, signature.key)
162 signature_tag.set(ATTR_KEY, signature.key)
163
163
164 return et.tostring(response, ENCODING_UNICODE)
164 return et.tostring(response, ENCODING_UNICODE)
165
165
166 @staticmethod
166 @staticmethod
167 def parse_response_get(response_xml, hostname):
167 def parse_response_get(response_xml, hostname):
168 tag_root = et.fromstring(response_xml)
168 tag_root = et.fromstring(response_xml)
169 tag_status = tag_root.find(TAG_STATUS)
169 tag_status = tag_root.find(TAG_STATUS)
170 if STATUS_SUCCESS == tag_status.text:
170 if STATUS_SUCCESS == tag_status.text:
171 tag_models = tag_root.find(TAG_MODELS)
171 tag_models = tag_root.find(TAG_MODELS)
172 for tag_model in tag_models:
172 for tag_model in tag_models:
173 SyncManager.parse_post(tag_model, hostname)
173 SyncManager.parse_post(tag_model, hostname)
174 else:
174 else:
175 raise SyncException(EXCEPTION_NODE.format(tag_status.text))
175 raise SyncException(EXCEPTION_NODE.format(tag_status.text))
176
176
177 @staticmethod
177 @staticmethod
178 @transaction.atomic
178 @transaction.atomic
179 def parse_post(tag_model, hostname):
179 def parse_post(tag_model, hostname):
180 tag_content = tag_model.find(TAG_CONTENT)
180 tag_content = tag_model.find(TAG_CONTENT)
181
181
182 content_str = et.tostring(tag_content, ENCODING_UNICODE)
182 content_str = et.tostring(tag_content, ENCODING_UNICODE)
183
183
184 tag_id = tag_content.find(TAG_ID)
184 tag_id = tag_content.find(TAG_ID)
185 global_id, exists = GlobalId.from_xml_element(tag_id)
185 global_id, exists = GlobalId.from_xml_element(tag_id)
186 signatures = SyncManager._verify_model(global_id, content_str, tag_model)
186 signatures = SyncManager._verify_model(global_id, content_str, tag_model)
187
187
188 version = int(tag_content.find(TAG_VERSION).text)
188 version = int(tag_content.find(TAG_VERSION).text)
189 is_old = exists and global_id.post.version < version
189 is_old = exists and global_id.post.version < version
190 if exists and not is_old:
190 if exists and not is_old:
191 print('Post with same ID exists and is up to date.')
191 print('Post with same ID exists and is up to date.')
192 else:
192 else:
193 global_id.content = content_str
193 global_id.content = content_str
194 global_id.save()
194 global_id.save()
195 for signature in signatures:
195 for signature in signatures:
196 signature.global_id = global_id
196 signature.global_id = global_id
197 signature.save()
197 signature.save()
198
198
199 title = tag_content.find(TAG_TITLE).text or ''
199 title = tag_content.find(TAG_TITLE).text or ''
200 text = tag_content.find(TAG_TEXT).text or ''
200 text = tag_content.find(TAG_TEXT).text or ''
201 pub_time = tag_content.find(TAG_PUB_TIME).text
201 pub_time = tag_content.find(TAG_PUB_TIME).text
202 tripcode_tag = tag_content.find(TAG_TRIPCODE)
202 tripcode_tag = tag_content.find(TAG_TRIPCODE)
203 if tripcode_tag is not None:
203 if tripcode_tag is not None:
204 tripcode = tripcode_tag.text or ''
204 tripcode = tripcode_tag.text or ''
205 else:
205 else:
206 tripcode = ''
206 tripcode = ''
207
207
208 thread = tag_content.find(TAG_THREAD)
208 thread = tag_content.find(TAG_THREAD)
209 tags = []
209 tags = []
210 if thread:
210 if thread:
211 thread_id = thread.find(TAG_ID)
211 thread_id = thread.find(TAG_ID)
212 op_global_id, exists = GlobalId.from_xml_element(thread_id)
212 op_global_id, exists = GlobalId.from_xml_element(thread_id)
213 if exists:
213 if exists:
214 opening_post = Post.objects.get(global_id=op_global_id)
214 opening_post = Post.objects.get(global_id=op_global_id)
215 else:
215 else:
216 logger.debug('No thread exists for post {}'.format(global_id))
216 logger.debug('No thread exists for post {}'.format(global_id))
217 else:
217 else:
218 opening_post = None
218 opening_post = None
219 tag_tags = tag_content.find(TAG_TAGS)
219 tag_tags = tag_content.find(TAG_TAGS)
220 for tag_tag in tag_tags:
220 for tag_tag in tag_tags:
221 tag, created = Tag.objects.get_or_create(
221 tag, created = Tag.objects.get_or_create(
222 name=tag_tag.text)
222 name=tag_tag.text)
223 tags.append(tag)
223 tags.append(tag)
224
224
225 # TODO Check that the replied posts are already present
225 # TODO Check that the replied posts are already present
226 # before adding new ones
226 # before adding new ones
227
227
228 files = []
228 files = []
229 urls = []
229 urls = []
230 tag_attachments = tag_content.find(TAG_ATTACHMENTS) or list()
230 tag_attachments = tag_content.find(TAG_ATTACHMENTS) or list()
231 tag_refs = tag_model.find(TAG_ATTACHMENT_REFS)
231 tag_refs = tag_model.find(TAG_ATTACHMENT_REFS)
232 for attachment in tag_attachments:
232 for attachment in tag_attachments:
233 if attachment.get(ATTR_ID_TYPE) == ID_TYPE_URL:
233 if attachment.get(ATTR_ID_TYPE) == ID_TYPE_URL:
234 urls.append(attachment.text)
234 urls.append(attachment.text)
235 else:
235 else:
236 tag_ref = tag_refs.find("{}[@ref='{}']".format(
236 tag_ref = tag_refs.find("{}[@ref='{}']".format(
237 TAG_ATTACHMENT_REF, attachment.text))
237 TAG_ATTACHMENT_REF, attachment.text))
238 url = tag_ref.get(ATTR_URL)
238 url = tag_ref.get(ATTR_URL)
239 attached_file = download(hostname + url)
239 attached_file = download(hostname + url, validate=False)
240 if attached_file is None:
240 if attached_file is None:
241 raise SyncException(EXCEPTION_DOWNLOAD)
241 raise SyncException(EXCEPTION_DOWNLOAD)
242
242
243 hash = get_file_hash(attached_file)
243 hash = get_file_hash(attached_file)
244 if hash != attachment.text:
244 if hash != attachment.text:
245 raise SyncException(EXCEPTION_HASH)
245 raise SyncException(EXCEPTION_HASH)
246
246
247 files.append(attached_file)
247 files.append(attached_file)
248
248
249 if is_old:
249 if is_old:
250 post = global_id.post
250 post = global_id.post
251 Post.objects.update_post(
251 Post.objects.update_post(
252 post, title=title, text=text, pub_time=pub_time,
252 post, title=title, text=text, pub_time=pub_time,
253 tags=tags, files=files, file_urls=urls,
253 tags=tags, files=files, file_urls=urls,
254 tripcode=tripcode, version=version)
254 tripcode=tripcode, version=version)
255 logger.debug('Parsed updated post {}'.format(global_id))
255 logger.debug('Parsed updated post {}'.format(global_id))
256 else:
256 else:
257 Post.objects.import_post(
257 Post.objects.import_post(
258 title=title, text=text, pub_time=pub_time,
258 title=title, text=text, pub_time=pub_time,
259 opening_post=opening_post, tags=tags,
259 opening_post=opening_post, tags=tags,
260 global_id=global_id, files=files,
260 global_id=global_id, files=files,
261 file_urls=urls, tripcode=tripcode,
261 file_urls=urls, tripcode=tripcode,
262 version=version)
262 version=version)
263 logger.debug('Parsed new post {}'.format(global_id))
263 logger.debug('Parsed new post {}'.format(global_id))
264
264
265 @staticmethod
265 @staticmethod
266 def generate_response_list():
266 def generate_response_list():
267 response = et.Element(TAG_RESPONSE)
267 response = et.Element(TAG_RESPONSE)
268
268
269 status = et.SubElement(response, TAG_STATUS)
269 status = et.SubElement(response, TAG_STATUS)
270 status.text = STATUS_SUCCESS
270 status.text = STATUS_SUCCESS
271
271
272 models = et.SubElement(response, TAG_MODELS)
272 models = et.SubElement(response, TAG_MODELS)
273
273
274 for post in Post.objects.prefetch_related('global_id').all():
274 for post in Post.objects.prefetch_related('global_id').all():
275 tag_model = et.SubElement(models, TAG_MODEL)
275 tag_model = et.SubElement(models, TAG_MODEL)
276 tag_id = et.SubElement(tag_model, TAG_ID)
276 tag_id = et.SubElement(tag_model, TAG_ID)
277 post.global_id.to_xml_element(tag_id)
277 post.global_id.to_xml_element(tag_id)
278 tag_version = et.SubElement(tag_model, TAG_VERSION)
278 tag_version = et.SubElement(tag_model, TAG_VERSION)
279 tag_version.text = str(post.version)
279 tag_version.text = str(post.version)
280
280
281 return et.tostring(response, ENCODING_UNICODE)
281 return et.tostring(response, ENCODING_UNICODE)
282
282
283 @staticmethod
283 @staticmethod
284 def _verify_model(global_id, content_str, tag_model):
284 def _verify_model(global_id, content_str, tag_model):
285 """
285 """
286 Verifies all signatures for a single model.
286 Verifies all signatures for a single model.
287 """
287 """
288
288
289 signatures = []
289 signatures = []
290
290
291 tag_signatures = tag_model.find(TAG_SIGNATURES)
291 tag_signatures = tag_model.find(TAG_SIGNATURES)
292 has_author_signature = False
292 has_author_signature = False
293 for tag_signature in tag_signatures:
293 for tag_signature in tag_signatures:
294 signature_type = tag_signature.get(ATTR_TYPE)
294 signature_type = tag_signature.get(ATTR_TYPE)
295 signature_value = tag_signature.get(ATTR_VALUE)
295 signature_value = tag_signature.get(ATTR_VALUE)
296 signature_key = tag_signature.get(ATTR_KEY)
296 signature_key = tag_signature.get(ATTR_KEY)
297
297
298 if global_id.key_type == signature_type and\
298 if global_id.key_type == signature_type and\
299 global_id.key == signature_key:
299 global_id.key == signature_key:
300 has_author_signature = True
300 has_author_signature = True
301
301
302 signature = Signature(key_type=signature_type,
302 signature = Signature(key_type=signature_type,
303 key=signature_key,
303 key=signature_key,
304 signature=signature_value)
304 signature=signature_value)
305
305
306 if not KeyPair.objects.verify(signature, content_str):
306 if not KeyPair.objects.verify(signature, content_str):
307 raise SyncException(EXCEPTION_SIGNATURE.format(content_str))
307 raise SyncException(EXCEPTION_SIGNATURE.format(content_str))
308
308
309 signatures.append(signature)
309 signatures.append(signature)
310 if not has_author_signature:
310 if not has_author_signature:
311 raise SyncException(EXCEPTION_AUTHOR_SIGNATURE.format(content_str))
311 raise SyncException(EXCEPTION_AUTHOR_SIGNATURE.format(content_str))
312
312
313 return signatures
313 return signatures
314
314
315 @staticmethod
315 @staticmethod
316 def _attachment_to_xml(tag_attachments, tag_refs, attachment):
316 def _attachment_to_xml(tag_attachments, tag_refs, attachment):
317 if tag_attachments is not None:
317 if tag_attachments is not None:
318 attachment_tag = et.SubElement(tag_attachments, TAG_ATTACHMENT)
318 attachment_tag = et.SubElement(tag_attachments, TAG_ATTACHMENT)
319 if attachment.is_internal():
319 if attachment.is_internal():
320 mimetype = get_file_mimetype(attachment.file.file)
320 mimetype = get_file_mimetype(attachment.file.file)
321 attachment_tag.set(ATTR_MIMETYPE, mimetype)
321 attachment_tag.set(ATTR_MIMETYPE, mimetype)
322 attachment_tag.set(ATTR_ID_TYPE, ID_TYPE_MD5)
322 attachment_tag.set(ATTR_ID_TYPE, ID_TYPE_MD5)
323 attachment_tag.text = attachment.hash
323 attachment_tag.text = attachment.hash
324 else:
324 else:
325 attachment_tag.set(ATTR_ID_TYPE, ID_TYPE_URL)
325 attachment_tag.set(ATTR_ID_TYPE, ID_TYPE_URL)
326 attachment_tag.text = attachment.url
326 attachment_tag.text = attachment.url
327
327
328 if tag_refs is not None:
328 if tag_refs is not None:
329 attachment_ref = et.SubElement(tag_refs, TAG_ATTACHMENT_REF)
329 attachment_ref = et.SubElement(tag_refs, TAG_ATTACHMENT_REF)
330 attachment_ref.set(ATTR_REF, attachment.hash)
330 attachment_ref.set(ATTR_REF, attachment.hash)
331 attachment_ref.set(ATTR_URL, attachment.file.url)
331 attachment_ref.set(ATTR_URL, attachment.file.url)
General Comments 0
You need to be logged in to leave comments. Login now