##// END OF EJS Templates
Allow syncing URL attachments
neko259 -
r1706:9abb9926 default
parent child Browse files
Show More
@@ -1,88 +1,91 b''
1 import boards
1 import boards
2 from boards.models import STATUS_ARCHIVE
2 from boards.models import STATUS_ARCHIVE
3 from django.core.files.images import get_image_dimensions
3 from django.core.files.images import get_image_dimensions
4 from django.db import models
4 from django.db import models
5
5
6 from boards import utils
6 from boards import utils
7 from boards.models.attachment.viewers import get_viewers, AbstractViewer, \
7 from boards.models.attachment.viewers import get_viewers, AbstractViewer, \
8 FILE_TYPES_IMAGE
8 FILE_TYPES_IMAGE
9 from boards.utils import get_upload_filename, get_extension, cached_result
9 from boards.utils import get_upload_filename, get_extension, cached_result
10
10
11
11
12 class AttachmentManager(models.Manager):
12 class AttachmentManager(models.Manager):
13 def create_with_hash(self, file):
13 def create_with_hash(self, file):
14 file_hash = utils.get_file_hash(file)
14 file_hash = utils.get_file_hash(file)
15 existing = self.filter(hash=file_hash)
15 existing = self.filter(hash=file_hash)
16 if len(existing) > 0:
16 if len(existing) > 0:
17 attachment = existing[0]
17 attachment = existing[0]
18 else:
18 else:
19 # FIXME Use full mimetype here, need to modify viewers too
19 # FIXME Use full mimetype here, need to modify viewers too
20 file_type = get_extension(file.name)
20 file_type = get_extension(file.name)
21 attachment = self.create(file=file, mimetype=file_type,
21 attachment = self.create(file=file, mimetype=file_type,
22 hash=file_hash)
22 hash=file_hash)
23
23
24 return attachment
24 return attachment
25
25
26 def create_from_url(self, url):
26 def create_from_url(self, url):
27 existing = self.filter(url=url)
27 existing = self.filter(url=url)
28 if len(existing) > 0:
28 if len(existing) > 0:
29 attachment = existing[0]
29 attachment = existing[0]
30 else:
30 else:
31 attachment = self.create(url=url)
31 attachment = self.create(url=url)
32 return attachment
32 return attachment
33
33
34 def get_random_images(self, count, tags=None):
34 def get_random_images(self, count, tags=None):
35 images = self.filter(mimetype__in=FILE_TYPES_IMAGE).exclude(
35 images = self.filter(mimetype__in=FILE_TYPES_IMAGE).exclude(
36 attachment_posts__thread__status=STATUS_ARCHIVE)
36 attachment_posts__thread__status=STATUS_ARCHIVE)
37 if tags is not None:
37 if tags is not None:
38 images = images.filter(attachment_posts__threads__tags__in=tags)
38 images = images.filter(attachment_posts__threads__tags__in=tags)
39 return images.order_by('?')[:count]
39 return images.order_by('?')[:count]
40
40
41
41
42 class Attachment(models.Model):
42 class Attachment(models.Model):
43 objects = AttachmentManager()
43 objects = AttachmentManager()
44
44
45 file = models.FileField(upload_to=get_upload_filename, null=True)
45 file = models.FileField(upload_to=get_upload_filename, null=True)
46 mimetype = models.CharField(max_length=50, null=True)
46 mimetype = models.CharField(max_length=50, null=True)
47 hash = models.CharField(max_length=36, null=True)
47 hash = models.CharField(max_length=36, null=True)
48 alias = models.TextField(unique=True, null=True, blank=True)
48 alias = models.TextField(unique=True, null=True, blank=True)
49 url = models.TextField(null=True, blank=True)
49 url = models.TextField(null=True, blank=True)
50
50
51 def get_view(self):
51 def get_view(self):
52 file_viewer = None
52 file_viewer = None
53 for viewer in get_viewers():
53 for viewer in get_viewers():
54 if viewer.supports(self.mimetype):
54 if viewer.supports(self.mimetype):
55 file_viewer = viewer
55 file_viewer = viewer
56 break
56 break
57 if file_viewer is None:
57 if file_viewer is None:
58 file_viewer = AbstractViewer
58 file_viewer = AbstractViewer
59
59
60 return file_viewer(self.file, self.mimetype, self.hash, self.url).get_view()
60 return file_viewer(self.file, self.mimetype, self.hash, self.url).get_view()
61
61
62 def __str__(self):
62 def __str__(self):
63 return self.url or self.file.url
63 return self.url or self.file.url
64
64
65 def get_random_associated_post(self):
65 def get_random_associated_post(self):
66 posts = boards.models.Post.objects.filter(attachments__in=[self])
66 posts = boards.models.Post.objects.filter(attachments__in=[self])
67 return posts.order_by('?').first()
67 return posts.order_by('?').first()
68
68
69 @cached_result()
69 @cached_result()
70 def get_size(self):
70 def get_size(self):
71 if self.file:
71 if self.file:
72 if self.mimetype in FILE_TYPES_IMAGE:
72 if self.mimetype in FILE_TYPES_IMAGE:
73 return get_image_dimensions(self.file)
73 return get_image_dimensions(self.file)
74 else:
74 else:
75 return 200, 150
75 return 200, 150
76
76
77 def get_thumb_url(self):
77 def get_thumb_url(self):
78 split = self.file.url.rsplit('.', 1)
78 split = self.file.url.rsplit('.', 1)
79 w, h = 200, 150
79 w, h = 200, 150
80 return '%s.%sx%s.%s' % (split[0], w, h, split[1])
80 return '%s.%sx%s.%s' % (split[0], w, h, split[1])
81
81
82 @cached_result()
82 @cached_result()
83 def get_preview_size(self):
83 def get_preview_size(self):
84 if self.mimetype in FILE_TYPES_IMAGE:
84 if self.mimetype in FILE_TYPES_IMAGE:
85 preview_path = self.file.path.replace('.', '.200x150.')
85 preview_path = self.file.path.replace('.', '.200x150.')
86 return get_image_dimensions(preview_path)
86 return get_image_dimensions(preview_path)
87 else:
87 else:
88 return 200, 150
88 return 200, 150
89
90 def is_internal(self):
91 return self.url is None or len(self.url) == 0
@@ -1,302 +1,323 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_OP = 'Load the OP first.'
11 EXCEPTION_OP = 'Load the OP first.'
12 EXCEPTION_DOWNLOAD = 'File was not downloaded.'
12 EXCEPTION_DOWNLOAD = 'File was not downloaded.'
13 EXCEPTION_HASH = 'File hash does not match attachment hash.'
13 EXCEPTION_HASH = 'File hash does not match attachment hash.'
14 EXCEPTION_SIGNATURE = 'Invalid model signature for {}.'
14 EXCEPTION_SIGNATURE = 'Invalid model signature for {}.'
15 EXCEPTION_AUTHOR_SIGNATURE = 'Model {} has no author signature.'
15 EXCEPTION_AUTHOR_SIGNATURE = 'Model {} has no author signature.'
16 ENCODING_UNICODE = 'unicode'
16 ENCODING_UNICODE = 'unicode'
17
17
18 TAG_MODEL = 'model'
18 TAG_MODEL = 'model'
19 TAG_REQUEST = 'request'
19 TAG_REQUEST = 'request'
20 TAG_RESPONSE = 'response'
20 TAG_RESPONSE = 'response'
21 TAG_ID = 'id'
21 TAG_ID = 'id'
22 TAG_STATUS = 'status'
22 TAG_STATUS = 'status'
23 TAG_MODELS = 'models'
23 TAG_MODELS = 'models'
24 TAG_TITLE = 'title'
24 TAG_TITLE = 'title'
25 TAG_TEXT = 'text'
25 TAG_TEXT = 'text'
26 TAG_THREAD = 'thread'
26 TAG_THREAD = 'thread'
27 TAG_PUB_TIME = 'pub-time'
27 TAG_PUB_TIME = 'pub-time'
28 TAG_SIGNATURES = 'signatures'
28 TAG_SIGNATURES = 'signatures'
29 TAG_SIGNATURE = 'signature'
29 TAG_SIGNATURE = 'signature'
30 TAG_CONTENT = 'content'
30 TAG_CONTENT = 'content'
31 TAG_ATTACHMENTS = 'attachments'
31 TAG_ATTACHMENTS = 'attachments'
32 TAG_ATTACHMENT = 'attachment'
32 TAG_ATTACHMENT = 'attachment'
33 TAG_TAGS = 'tags'
33 TAG_TAGS = 'tags'
34 TAG_TAG = 'tag'
34 TAG_TAG = 'tag'
35 TAG_ATTACHMENT_REFS = 'attachment-refs'
35 TAG_ATTACHMENT_REFS = 'attachment-refs'
36 TAG_ATTACHMENT_REF = 'attachment-ref'
36 TAG_ATTACHMENT_REF = 'attachment-ref'
37 TAG_TRIPCODE = 'tripcode'
37 TAG_TRIPCODE = 'tripcode'
38 TAG_VERSION = 'version'
38 TAG_VERSION = 'version'
39
39
40 TYPE_GET = 'get'
40 TYPE_GET = 'get'
41
41
42 ATTR_VERSION = 'version'
42 ATTR_VERSION = 'version'
43 ATTR_TYPE = 'type'
43 ATTR_TYPE = 'type'
44 ATTR_NAME = 'name'
44 ATTR_NAME = 'name'
45 ATTR_VALUE = 'value'
45 ATTR_VALUE = 'value'
46 ATTR_MIMETYPE = 'mimetype'
46 ATTR_MIMETYPE = 'mimetype'
47 ATTR_KEY = 'key'
47 ATTR_KEY = 'key'
48 ATTR_REF = 'ref'
48 ATTR_REF = 'ref'
49 ATTR_URL = 'url'
49 ATTR_URL = 'url'
50 ATTR_ID_TYPE = 'id-type'
50 ATTR_ID_TYPE = 'id-type'
51
51
52 ID_TYPE_MD5 = 'md5'
52 ID_TYPE_MD5 = 'md5'
53 ID_TYPE_URL = 'url'
53
54
54 STATUS_SUCCESS = 'success'
55 STATUS_SUCCESS = 'success'
55
56
56
57
57 logger = logging.getLogger('boards.sync')
58 logger = logging.getLogger('boards.sync')
58
59
59
60
60 class SyncManager:
61 class SyncManager:
61 @staticmethod
62 @staticmethod
62 def generate_response_get(model_list: list):
63 def generate_response_get(model_list: list):
63 response = et.Element(TAG_RESPONSE)
64 response = et.Element(TAG_RESPONSE)
64
65
65 status = et.SubElement(response, TAG_STATUS)
66 status = et.SubElement(response, TAG_STATUS)
66 status.text = STATUS_SUCCESS
67 status.text = STATUS_SUCCESS
67
68
68 models = et.SubElement(response, TAG_MODELS)
69 models = et.SubElement(response, TAG_MODELS)
69
70
70 for post in model_list:
71 for post in model_list:
71 model = et.SubElement(models, TAG_MODEL)
72 model = et.SubElement(models, TAG_MODEL)
72 model.set(ATTR_NAME, 'post')
73 model.set(ATTR_NAME, 'post')
73
74
74 global_id = post.global_id
75 global_id = post.global_id
75
76
76 attachments = post.attachments.all()
77 attachments = post.attachments.all()
77 if global_id.content:
78 if global_id.content:
78 model.append(et.fromstring(global_id.content))
79 model.append(et.fromstring(global_id.content))
79 if len(attachments) > 0:
80 if len(attachments) > 0:
80 attachment_refs = et.SubElement(model, TAG_ATTACHMENT_REFS)
81 internal_attachments = False
81 for file in attachments:
82 for attachment in attachments:
82 SyncManager._attachment_to_xml(
83 if attachment.is_internal():
83 None, attachment_refs, file.file.file,
84 internal_attachments = True
84 file.hash, file.file.url)
85 break
86
87 if internal_attachments:
88 attachment_refs = et.SubElement(model, TAG_ATTACHMENT_REFS)
89 for file in attachments:
90 SyncManager._attachment_to_xml(
91 None, attachment_refs, file)
85 else:
92 else:
86 content_tag = et.SubElement(model, TAG_CONTENT)
93 content_tag = et.SubElement(model, TAG_CONTENT)
87
94
88 tag_id = et.SubElement(content_tag, TAG_ID)
95 tag_id = et.SubElement(content_tag, TAG_ID)
89 global_id.to_xml_element(tag_id)
96 global_id.to_xml_element(tag_id)
90
97
91 title = et.SubElement(content_tag, TAG_TITLE)
98 title = et.SubElement(content_tag, TAG_TITLE)
92 title.text = post.title
99 title.text = post.title
93
100
94 text = et.SubElement(content_tag, TAG_TEXT)
101 text = et.SubElement(content_tag, TAG_TEXT)
95 text.text = post.get_sync_text()
102 text.text = post.get_sync_text()
96
103
97 thread = post.get_thread()
104 thread = post.get_thread()
98 if post.is_opening():
105 if post.is_opening():
99 tag_tags = et.SubElement(content_tag, TAG_TAGS)
106 tag_tags = et.SubElement(content_tag, TAG_TAGS)
100 for tag in thread.get_tags():
107 for tag in thread.get_tags():
101 tag_tag = et.SubElement(tag_tags, TAG_TAG)
108 tag_tag = et.SubElement(tag_tags, TAG_TAG)
102 tag_tag.text = tag.name
109 tag_tag.text = tag.name
103 else:
110 else:
104 tag_thread = et.SubElement(content_tag, TAG_THREAD)
111 tag_thread = et.SubElement(content_tag, TAG_THREAD)
105 thread_id = et.SubElement(tag_thread, TAG_ID)
112 thread_id = et.SubElement(tag_thread, TAG_ID)
106 thread.get_opening_post().global_id.to_xml_element(thread_id)
113 thread.get_opening_post().global_id.to_xml_element(thread_id)
107
114
108 pub_time = et.SubElement(content_tag, TAG_PUB_TIME)
115 pub_time = et.SubElement(content_tag, TAG_PUB_TIME)
109 pub_time.text = str(post.get_pub_time_str())
116 pub_time.text = str(post.get_pub_time_str())
110
117
111 if post.tripcode:
118 if post.tripcode:
112 tripcode = et.SubElement(content_tag, TAG_TRIPCODE)
119 tripcode = et.SubElement(content_tag, TAG_TRIPCODE)
113 tripcode.text = post.tripcode
120 tripcode.text = post.tripcode
114
121
115 if len(attachments) > 0:
122 if len(attachments) > 0:
116 attachments_tag = et.SubElement(content_tag, TAG_ATTACHMENTS)
123 attachments_tag = et.SubElement(content_tag, TAG_ATTACHMENTS)
117 attachment_refs = et.SubElement(model, TAG_ATTACHMENT_REFS)
124
125 internal_attachments = False
126 for attachment in attachments:
127 if attachment.is_internal():
128 internal_attachments = True
129 break
130
131 if internal_attachments:
132 attachment_refs = et.SubElement(model, TAG_ATTACHMENT_REFS)
133 else:
134 attachment_refs = None
118
135
119 for file in attachments:
136 for file in attachments:
120 SyncManager._attachment_to_xml(
137 SyncManager._attachment_to_xml(
121 attachments_tag, attachment_refs, file.file.file,
138 attachments_tag, attachment_refs, file)
122 file.hash, file.file.url)
123 version_tag = et.SubElement(content_tag, TAG_VERSION)
139 version_tag = et.SubElement(content_tag, TAG_VERSION)
124 version_tag.text = str(post.version)
140 version_tag.text = str(post.version)
125
141
126 global_id.content = et.tostring(content_tag, ENCODING_UNICODE)
142 global_id.content = et.tostring(content_tag, ENCODING_UNICODE)
127 global_id.save()
143 global_id.save()
128
144
129 signatures_tag = et.SubElement(model, TAG_SIGNATURES)
145 signatures_tag = et.SubElement(model, TAG_SIGNATURES)
130 post_signatures = global_id.signature_set.all()
146 post_signatures = global_id.signature_set.all()
131 if post_signatures:
147 if post_signatures:
132 signatures = post_signatures
148 signatures = post_signatures
133 else:
149 else:
134 key = KeyPair.objects.get(public_key=global_id.key)
150 key = KeyPair.objects.get(public_key=global_id.key)
135 signature = Signature(
151 signature = Signature(
136 key_type=key.key_type,
152 key_type=key.key_type,
137 key=key.public_key,
153 key=key.public_key,
138 signature=key.sign(global_id.content),
154 signature=key.sign(global_id.content),
139 global_id=global_id,
155 global_id=global_id,
140 )
156 )
141 signature.save()
157 signature.save()
142 signatures = [signature]
158 signatures = [signature]
143 for signature in signatures:
159 for signature in signatures:
144 signature_tag = et.SubElement(signatures_tag, TAG_SIGNATURE)
160 signature_tag = et.SubElement(signatures_tag, TAG_SIGNATURE)
145 signature_tag.set(ATTR_TYPE, signature.key_type)
161 signature_tag.set(ATTR_TYPE, signature.key_type)
146 signature_tag.set(ATTR_VALUE, signature.signature)
162 signature_tag.set(ATTR_VALUE, signature.signature)
147 signature_tag.set(ATTR_KEY, signature.key)
163 signature_tag.set(ATTR_KEY, signature.key)
148
164
149 return et.tostring(response, ENCODING_UNICODE)
165 return et.tostring(response, ENCODING_UNICODE)
150
166
151 @staticmethod
167 @staticmethod
152 @transaction.atomic
168 @transaction.atomic
153 def parse_response_get(response_xml, hostname):
169 def parse_response_get(response_xml, hostname):
154 tag_root = et.fromstring(response_xml)
170 tag_root = et.fromstring(response_xml)
155 tag_status = tag_root.find(TAG_STATUS)
171 tag_status = tag_root.find(TAG_STATUS)
156 if STATUS_SUCCESS == tag_status.text:
172 if STATUS_SUCCESS == tag_status.text:
157 tag_models = tag_root.find(TAG_MODELS)
173 tag_models = tag_root.find(TAG_MODELS)
158 for tag_model in tag_models:
174 for tag_model in tag_models:
159 tag_content = tag_model.find(TAG_CONTENT)
175 tag_content = tag_model.find(TAG_CONTENT)
160
176
161 content_str = et.tostring(tag_content, ENCODING_UNICODE)
177 content_str = et.tostring(tag_content, ENCODING_UNICODE)
162
178
163 tag_id = tag_content.find(TAG_ID)
179 tag_id = tag_content.find(TAG_ID)
164 global_id, exists = GlobalId.from_xml_element(tag_id)
180 global_id, exists = GlobalId.from_xml_element(tag_id)
165 signatures = SyncManager._verify_model(global_id, content_str, tag_model)
181 signatures = SyncManager._verify_model(global_id, content_str, tag_model)
166
182
167 version = int(tag_content.find(TAG_VERSION).text)
183 version = int(tag_content.find(TAG_VERSION).text)
168 is_old = exists and global_id.post.version < version
184 is_old = exists and global_id.post.version < version
169 if exists and not is_old:
185 if exists and not is_old:
170 print('Post with same ID exists and is up to date.')
186 print('Post with same ID exists and is up to date.')
171 else:
187 else:
172 global_id.content = content_str
188 global_id.content = content_str
173 global_id.save()
189 global_id.save()
174 for signature in signatures:
190 for signature in signatures:
175 signature.global_id = global_id
191 signature.global_id = global_id
176 signature.save()
192 signature.save()
177
193
178 title = tag_content.find(TAG_TITLE).text or ''
194 title = tag_content.find(TAG_TITLE).text or ''
179 text = tag_content.find(TAG_TEXT).text or ''
195 text = tag_content.find(TAG_TEXT).text or ''
180 pub_time = tag_content.find(TAG_PUB_TIME).text
196 pub_time = tag_content.find(TAG_PUB_TIME).text
181 tripcode_tag = tag_content.find(TAG_TRIPCODE)
197 tripcode_tag = tag_content.find(TAG_TRIPCODE)
182 if tripcode_tag is not None:
198 if tripcode_tag is not None:
183 tripcode = tripcode_tag.text or ''
199 tripcode = tripcode_tag.text or ''
184 else:
200 else:
185 tripcode = ''
201 tripcode = ''
186
202
187 thread = tag_content.find(TAG_THREAD)
203 thread = tag_content.find(TAG_THREAD)
188 tags = []
204 tags = []
189 if thread:
205 if thread:
190 thread_id = thread.find(TAG_ID)
206 thread_id = thread.find(TAG_ID)
191 op_global_id, exists = GlobalId.from_xml_element(thread_id)
207 op_global_id, exists = GlobalId.from_xml_element(thread_id)
192 if exists:
208 if exists:
193 opening_post = Post.objects.get(global_id=op_global_id)
209 opening_post = Post.objects.get(global_id=op_global_id)
194 else:
210 else:
195 raise SyncException(EXCEPTION_OP)
211 raise SyncException(EXCEPTION_OP)
196 else:
212 else:
197 opening_post = None
213 opening_post = None
198 tag_tags = tag_content.find(TAG_TAGS)
214 tag_tags = tag_content.find(TAG_TAGS)
199 for tag_tag in tag_tags:
215 for tag_tag in tag_tags:
200 tag, created = Tag.objects.get_or_create(
216 tag, created = Tag.objects.get_or_create(
201 name=tag_tag.text)
217 name=tag_tag.text)
202 tags.append(tag)
218 tags.append(tag)
203
219
204 # TODO Check that the replied posts are already present
220 # TODO Check that the replied posts are already present
205 # before adding new ones
221 # before adding new ones
206
222
207 files = []
223 files = []
208 tag_attachments = tag_content.find(TAG_ATTACHMENTS) or list()
224 tag_attachments = tag_content.find(TAG_ATTACHMENTS) or list()
209 tag_refs = tag_model.find(TAG_ATTACHMENT_REFS)
225 tag_refs = tag_model.find(TAG_ATTACHMENT_REFS)
210 for attachment in tag_attachments:
226 for attachment in tag_attachments:
211 tag_ref = tag_refs.find("{}[@ref='{}']".format(
227 tag_ref = tag_refs.find("{}[@ref='{}']".format(
212 TAG_ATTACHMENT_REF, attachment.text))
228 TAG_ATTACHMENT_REF, attachment.text))
213 url = tag_ref.get(ATTR_URL)
229 url = tag_ref.get(ATTR_URL)
214 attached_file = download(hostname + url)
230 attached_file = download(hostname + url)
215 if attached_file is None:
231 if attached_file is None:
216 raise SyncException(EXCEPTION_DOWNLOAD)
232 raise SyncException(EXCEPTION_DOWNLOAD)
217
233
218 hash = get_file_hash(attached_file)
234 hash = get_file_hash(attached_file)
219 if hash != attachment.text:
235 if hash != attachment.text:
220 raise SyncException(EXCEPTION_HASH)
236 raise SyncException(EXCEPTION_HASH)
221
237
222 files.append(attached_file)
238 files.append(attached_file)
223
239
224 if is_old:
240 if is_old:
225 post = global_id.post
241 post = global_id.post
226 Post.objects.update_post(
242 Post.objects.update_post(
227 post, title=title, text=text, pub_time=pub_time,
243 post, title=title, text=text, pub_time=pub_time,
228 tags=tags, files=files, tripcode=tripcode,
244 tags=tags, files=files, tripcode=tripcode,
229 version=version)
245 version=version)
230 logger.debug('Parsed updated post {}'.format(global_id))
246 logger.debug('Parsed updated post {}'.format(global_id))
231 else:
247 else:
232 Post.objects.import_post(
248 Post.objects.import_post(
233 title=title, text=text, pub_time=pub_time,
249 title=title, text=text, pub_time=pub_time,
234 opening_post=opening_post, tags=tags,
250 opening_post=opening_post, tags=tags,
235 global_id=global_id, files=files, tripcode=tripcode,
251 global_id=global_id, files=files, tripcode=tripcode,
236 version=version)
252 version=version)
237 logger.debug('Parsed new post {}'.format(global_id))
253 logger.debug('Parsed new post {}'.format(global_id))
238 else:
254 else:
239 raise SyncException(EXCEPTION_NODE.format(tag_status.text))
255 raise SyncException(EXCEPTION_NODE.format(tag_status.text))
240
256
241 @staticmethod
257 @staticmethod
242 def generate_response_list():
258 def generate_response_list():
243 response = et.Element(TAG_RESPONSE)
259 response = et.Element(TAG_RESPONSE)
244
260
245 status = et.SubElement(response, TAG_STATUS)
261 status = et.SubElement(response, TAG_STATUS)
246 status.text = STATUS_SUCCESS
262 status.text = STATUS_SUCCESS
247
263
248 models = et.SubElement(response, TAG_MODELS)
264 models = et.SubElement(response, TAG_MODELS)
249
265
250 for post in Post.objects.prefetch_related('global_id').all():
266 for post in Post.objects.prefetch_related('global_id').all():
251 tag_model = et.SubElement(models, TAG_MODEL)
267 tag_model = et.SubElement(models, TAG_MODEL)
252 tag_id = et.SubElement(tag_model, TAG_ID)
268 tag_id = et.SubElement(tag_model, TAG_ID)
253 post.global_id.to_xml_element(tag_id)
269 post.global_id.to_xml_element(tag_id)
254 tag_version = et.SubElement(tag_model, TAG_VERSION)
270 tag_version = et.SubElement(tag_model, TAG_VERSION)
255 tag_version.text = str(post.version)
271 tag_version.text = str(post.version)
256
272
257 return et.tostring(response, ENCODING_UNICODE)
273 return et.tostring(response, ENCODING_UNICODE)
258
274
259 @staticmethod
275 @staticmethod
260 def _verify_model(global_id, content_str, tag_model):
276 def _verify_model(global_id, content_str, tag_model):
261 """
277 """
262 Verifies all signatures for a single model.
278 Verifies all signatures for a single model.
263 """
279 """
264
280
265 signatures = []
281 signatures = []
266
282
267 tag_signatures = tag_model.find(TAG_SIGNATURES)
283 tag_signatures = tag_model.find(TAG_SIGNATURES)
268 has_author_signature = False
284 has_author_signature = False
269 for tag_signature in tag_signatures:
285 for tag_signature in tag_signatures:
270 signature_type = tag_signature.get(ATTR_TYPE)
286 signature_type = tag_signature.get(ATTR_TYPE)
271 signature_value = tag_signature.get(ATTR_VALUE)
287 signature_value = tag_signature.get(ATTR_VALUE)
272 signature_key = tag_signature.get(ATTR_KEY)
288 signature_key = tag_signature.get(ATTR_KEY)
273
289
274 if global_id.key_type == signature_type and\
290 if global_id.key_type == signature_type and\
275 global_id.key == signature_key:
291 global_id.key == signature_key:
276 has_author_signature = True
292 has_author_signature = True
277
293
278 signature = Signature(key_type=signature_type,
294 signature = Signature(key_type=signature_type,
279 key=signature_key,
295 key=signature_key,
280 signature=signature_value)
296 signature=signature_value)
281
297
282 if not KeyPair.objects.verify(signature, content_str):
298 if not KeyPair.objects.verify(signature, content_str):
283 raise SyncException(EXCEPTION_SIGNATURE.format(content_str))
299 raise SyncException(EXCEPTION_SIGNATURE.format(content_str))
284
300
285 signatures.append(signature)
301 signatures.append(signature)
286 if not has_author_signature:
302 if not has_author_signature:
287 raise SyncException(EXCEPTION_AUTHOR_SIGNATURE.format(content_str))
303 raise SyncException(EXCEPTION_AUTHOR_SIGNATURE.format(content_str))
288
304
289 return signatures
305 return signatures
290
306
291 @staticmethod
307 @staticmethod
292 def _attachment_to_xml(tag_attachments, tag_refs, file, hash, url):
308 def _attachment_to_xml(tag_attachments, tag_refs, attachment):
293 if tag_attachments is not None:
309 if tag_attachments is not None:
294 mimetype = get_file_mimetype(file)
310 attachment_tag = et.SubElement(tag_attachments, TAG_ATTACHMENT)
295 attachment = et.SubElement(tag_attachments, TAG_ATTACHMENT)
311 if attachment.is_internal():
296 attachment.set(ATTR_MIMETYPE, mimetype)
312 mimetype = get_file_mimetype(attachment.file.file)
297 attachment.set(ATTR_ID_TYPE, ID_TYPE_MD5)
313 attachment_tag.set(ATTR_MIMETYPE, mimetype)
298 attachment.text = hash
314 attachment_tag.set(ATTR_ID_TYPE, ID_TYPE_MD5)
315 attachment_tag.text = attachment.hash
316 else:
317 attachment_tag.set(ATTR_ID_TYPE, ID_TYPE_URL)
318 attachment_tag.text = attachment.url
299
319
300 attachment_ref = et.SubElement(tag_refs, TAG_ATTACHMENT_REF)
320 if tag_refs is not None:
301 attachment_ref.set(ATTR_REF, hash)
321 attachment_ref = et.SubElement(tag_refs, TAG_ATTACHMENT_REF)
302 attachment_ref.set(ATTR_URL, url)
322 attachment_ref.set(ATTR_REF, attachment.hash)
323 attachment_ref.set(ATTR_URL, attachment.url)
General Comments 0
You need to be logged in to leave comments. Login now