##// END OF EJS Templates
Added local download links for attachments
neko259 -
r1508:609c3572 decentral
parent child Browse files
Show More
@@ -1,218 +1,233 b''
1 import xml.etree.ElementTree as et
1 import xml.etree.ElementTree as et
2
2
3 from boards.utils import get_file_mimetype
3 from boards.utils import get_file_mimetype
4 from django.db import transaction
4 from django.db import transaction
5 from boards.models import KeyPair, GlobalId, Signature, Post, Tag
5 from boards.models import KeyPair, GlobalId, Signature, Post, Tag
6
6
7 ENCODING_UNICODE = 'unicode'
7 ENCODING_UNICODE = 'unicode'
8
8
9 TAG_MODEL = 'model'
9 TAG_MODEL = 'model'
10 TAG_REQUEST = 'request'
10 TAG_REQUEST = 'request'
11 TAG_RESPONSE = 'response'
11 TAG_RESPONSE = 'response'
12 TAG_ID = 'id'
12 TAG_ID = 'id'
13 TAG_STATUS = 'status'
13 TAG_STATUS = 'status'
14 TAG_MODELS = 'models'
14 TAG_MODELS = 'models'
15 TAG_TITLE = 'title'
15 TAG_TITLE = 'title'
16 TAG_TEXT = 'text'
16 TAG_TEXT = 'text'
17 TAG_THREAD = 'thread'
17 TAG_THREAD = 'thread'
18 TAG_PUB_TIME = 'pub-time'
18 TAG_PUB_TIME = 'pub-time'
19 TAG_SIGNATURES = 'signatures'
19 TAG_SIGNATURES = 'signatures'
20 TAG_SIGNATURE = 'signature'
20 TAG_SIGNATURE = 'signature'
21 TAG_CONTENT = 'content'
21 TAG_CONTENT = 'content'
22 TAG_ATTACHMENTS = 'attachments'
22 TAG_ATTACHMENTS = 'attachments'
23 TAG_ATTACHMENT = 'attachment'
23 TAG_ATTACHMENT = 'attachment'
24 TAG_TAGS = 'tags'
24 TAG_TAGS = 'tags'
25 TAG_TAG = 'tag'
25 TAG_TAG = 'tag'
26 TAG_ATTACHMENT_REFS = 'attachment-refs'
27 TAG_ATTACHMENT_REF = 'attachment-ref'
26
28
27 TYPE_GET = 'get'
29 TYPE_GET = 'get'
28
30
29 ATTR_VERSION = 'version'
31 ATTR_VERSION = 'version'
30 ATTR_TYPE = 'type'
32 ATTR_TYPE = 'type'
31 ATTR_NAME = 'name'
33 ATTR_NAME = 'name'
32 ATTR_VALUE = 'value'
34 ATTR_VALUE = 'value'
33 ATTR_MIMETYPE = 'mimetype'
35 ATTR_MIMETYPE = 'mimetype'
34 ATTR_KEY = 'key'
36 ATTR_KEY = 'key'
37 ATTR_REF = 'ref'
38 ATTR_URL = 'url'
35
39
36 STATUS_SUCCESS = 'success'
40 STATUS_SUCCESS = 'success'
37
41
38
42
39 class SyncManager:
43 class SyncManager:
40 @staticmethod
44 @staticmethod
41 def generate_response_get(model_list: list):
45 def generate_response_get(model_list: list):
42 response = et.Element(TAG_RESPONSE)
46 response = et.Element(TAG_RESPONSE)
43
47
44 status = et.SubElement(response, TAG_STATUS)
48 status = et.SubElement(response, TAG_STATUS)
45 status.text = STATUS_SUCCESS
49 status.text = STATUS_SUCCESS
46
50
47 models = et.SubElement(response, TAG_MODELS)
51 models = et.SubElement(response, TAG_MODELS)
48
52
49 for post in model_list:
53 for post in model_list:
50 model = et.SubElement(models, TAG_MODEL)
54 model = et.SubElement(models, TAG_MODEL)
51 model.set(ATTR_NAME, 'post')
55 model.set(ATTR_NAME, 'post')
52
56
53 content_tag = et.SubElement(model, TAG_CONTENT)
57 content_tag = et.SubElement(model, TAG_CONTENT)
54
58
55 tag_id = et.SubElement(content_tag, TAG_ID)
59 tag_id = et.SubElement(content_tag, TAG_ID)
56 post.global_id.to_xml_element(tag_id)
60 post.global_id.to_xml_element(tag_id)
57
61
58 title = et.SubElement(content_tag, TAG_TITLE)
62 title = et.SubElement(content_tag, TAG_TITLE)
59 title.text = post.title
63 title.text = post.title
60
64
61 text = et.SubElement(content_tag, TAG_TEXT)
65 text = et.SubElement(content_tag, TAG_TEXT)
62 text.text = post.get_sync_text()
66 text.text = post.get_sync_text()
63
67
64 thread = post.get_thread()
68 thread = post.get_thread()
65 if post.is_opening():
69 if post.is_opening():
66 tag_tags = et.SubElement(content_tag, TAG_TAGS)
70 tag_tags = et.SubElement(content_tag, TAG_TAGS)
67 for tag in thread.get_tags():
71 for tag in thread.get_tags():
68 tag_tag = et.SubElement(tag_tags, TAG_TAG)
72 tag_tag = et.SubElement(tag_tags, TAG_TAG)
69 tag_tag.text = tag.name
73 tag_tag.text = tag.name
70 else:
74 else:
71 tag_thread = et.SubElement(content_tag, TAG_THREAD)
75 tag_thread = et.SubElement(content_tag, TAG_THREAD)
72 thread_id = et.SubElement(tag_thread, TAG_ID)
76 thread_id = et.SubElement(tag_thread, TAG_ID)
73 thread.get_opening_post().global_id.to_xml_element(thread_id)
77 thread.get_opening_post().global_id.to_xml_element(thread_id)
74
78
75 pub_time = et.SubElement(content_tag, TAG_PUB_TIME)
79 pub_time = et.SubElement(content_tag, TAG_PUB_TIME)
76 pub_time.text = str(post.get_pub_time_str())
80 pub_time.text = str(post.get_pub_time_str())
77
81
78 images = post.images.all()
82 images = post.images.all()
79 attachments = post.attachments.all()
83 attachments = post.attachments.all()
80 if len(images) > 0 or len(attachments) > 0:
84 if len(images) > 0 or len(attachments) > 0:
81 attachments_tag = et.SubElement(content_tag, TAG_ATTACHMENTS)
85 attachments_tag = et.SubElement(content_tag, TAG_ATTACHMENTS)
86 attachment_refs = et.SubElement(model, TAG_ATTACHMENT_REFS)
87
82 for image in images:
88 for image in images:
83 mimetype = get_file_mimetype(image.image.file)
89 SyncManager._attachment_to_xml(
84 attachment = et.SubElement(attachments_tag, TAG_ATTACHMENT)
90 attachments_tag, attachment_refs, image.image.file,
85 attachment.set(ATTR_MIMETYPE, mimetype)
91 image.hash, image.image.url)
86 attachment.text = image.hash
87 for file in attachments:
92 for file in attachments:
88 mimetype = get_file_mimetype(file.file.file)
93 SyncManager._attachment_to_xml(
89 attachment = et.SubElement(attachments_tag, TAG_ATTACHMENT)
94 attachments_tag, attachment_refs, file.file.file,
90 attachment.set(ATTR_MIMETYPE, mimetype)
95 file.hash, file.file.url)
91 attachment.text = file.hash
92
96
93 signatures_tag = et.SubElement(model, TAG_SIGNATURES)
97 signatures_tag = et.SubElement(model, TAG_SIGNATURES)
94 post_signatures = post.global_id.signature_set.all()
98 post_signatures = post.global_id.signature_set.all()
95 if post_signatures:
99 if post_signatures:
96 signatures = post_signatures
100 signatures = post_signatures
97 # TODO Adding signature to a post is not yet added. For now this
101 # TODO Adding signature to a post is not yet added. For now this
98 # block is useless
102 # block is useless
99 else:
103 else:
100 # TODO Maybe the signature can be computed only once after
104 # TODO Maybe the signature can be computed only once after
101 # the post is added? Need to add some on_save signal queue
105 # the post is added? Need to add some on_save signal queue
102 # and add this there.
106 # and add this there.
103 key = KeyPair.objects.get(public_key=post.global_id.key)
107 key = KeyPair.objects.get(public_key=post.global_id.key)
104 signature = Signature(
108 signature = Signature(
105 key_type=key.key_type,
109 key_type=key.key_type,
106 key=key.public_key,
110 key=key.public_key,
107 signature=key.sign(et.tostring(content_tag, encoding=ENCODING_UNICODE)),
111 signature=key.sign(et.tostring(content_tag, encoding=ENCODING_UNICODE)),
108 global_id=post.global_id,
112 global_id=post.global_id,
109 )
113 )
110 signature.save()
114 signature.save()
111 signatures = [signature]
115 signatures = [signature]
112 for signature in signatures:
116 for signature in signatures:
113 signature_tag = et.SubElement(signatures_tag, TAG_SIGNATURE)
117 signature_tag = et.SubElement(signatures_tag, TAG_SIGNATURE)
114 signature_tag.set(ATTR_TYPE, signature.key_type)
118 signature_tag.set(ATTR_TYPE, signature.key_type)
115 signature_tag.set(ATTR_VALUE, signature.signature)
119 signature_tag.set(ATTR_VALUE, signature.signature)
116 signature_tag.set(ATTR_KEY, signature.key)
120 signature_tag.set(ATTR_KEY, signature.key)
117
121
118 return et.tostring(response, ENCODING_UNICODE)
122 return et.tostring(response, ENCODING_UNICODE)
119
123
120 @staticmethod
124 @staticmethod
121 @transaction.atomic
125 @transaction.atomic
122 def parse_response_get(response_xml):
126 def parse_response_get(response_xml):
123 tag_root = et.fromstring(response_xml)
127 tag_root = et.fromstring(response_xml)
124 tag_status = tag_root.find(TAG_STATUS)
128 tag_status = tag_root.find(TAG_STATUS)
125 if STATUS_SUCCESS == tag_status.text:
129 if STATUS_SUCCESS == tag_status.text:
126 tag_models = tag_root.find(TAG_MODELS)
130 tag_models = tag_root.find(TAG_MODELS)
127 for tag_model in tag_models:
131 for tag_model in tag_models:
128 tag_content = tag_model.find(TAG_CONTENT)
132 tag_content = tag_model.find(TAG_CONTENT)
129
133
130 signatures = SyncManager._verify_model(tag_content, tag_model)
134 signatures = SyncManager._verify_model(tag_content, tag_model)
131
135
132 tag_id = tag_content.find(TAG_ID)
136 tag_id = tag_content.find(TAG_ID)
133 global_id, exists = GlobalId.from_xml_element(tag_id)
137 global_id, exists = GlobalId.from_xml_element(tag_id)
134
138
135 if exists:
139 if exists:
136 print('Post with same ID already exists')
140 print('Post with same ID already exists')
137 else:
141 else:
138 global_id.save()
142 global_id.save()
139 for signature in signatures:
143 for signature in signatures:
140 signature.global_id = global_id
144 signature.global_id = global_id
141 signature.save()
145 signature.save()
142
146
143 title = tag_content.find(TAG_TITLE).text
147 title = tag_content.find(TAG_TITLE).text
144 text = tag_content.find(TAG_TEXT).text
148 text = tag_content.find(TAG_TEXT).text
145 pub_time = tag_content.find(TAG_PUB_TIME).text
149 pub_time = tag_content.find(TAG_PUB_TIME).text
146
150
147 thread = tag_content.find(TAG_THREAD)
151 thread = tag_content.find(TAG_THREAD)
148 tags = []
152 tags = []
149 if thread:
153 if thread:
150 thread_id = thread.find(TAG_ID)
154 thread_id = thread.find(TAG_ID)
151 op_global_id, exists = GlobalId.from_xml_element(thread_id)
155 op_global_id, exists = GlobalId.from_xml_element(thread_id)
152 if exists:
156 if exists:
153 opening_post = Post.objects.get(global_id=op_global_id)
157 opening_post = Post.objects.get(global_id=op_global_id)
154 else:
158 else:
155 raise Exception('Load the OP first')
159 raise Exception('Load the OP first')
156 else:
160 else:
157 opening_post = None
161 opening_post = None
158 tag_tags = tag_content.find(TAG_TAGS)
162 tag_tags = tag_content.find(TAG_TAGS)
159 for tag_tag in tag_tags:
163 for tag_tag in tag_tags:
160 tag, created = Tag.objects.get_or_create(
164 tag, created = Tag.objects.get_or_create(
161 name=tag_tag.text)
165 name=tag_tag.text)
162 tags.append(tag)
166 tags.append(tag)
163
167
164 # TODO Check that the replied posts are already present
168 # TODO Check that the replied posts are already present
165 # before adding new ones
169 # before adding new ones
166
170
167 # TODO Get images
171 # TODO Get images
168
172
169 post = Post.objects.import_post(
173 post = Post.objects.import_post(
170 title=title, text=text, pub_time=pub_time,
174 title=title, text=text, pub_time=pub_time,
171 opening_post=opening_post, tags=tags,
175 opening_post=opening_post, tags=tags,
172 global_id=global_id)
176 global_id=global_id)
173 else:
177 else:
174 # TODO Throw an exception?
178 # TODO Throw an exception?
175 pass
179 pass
176
180
177 @staticmethod
181 @staticmethod
178 def generate_response_pull():
182 def generate_response_pull():
179 response = et.Element(TAG_RESPONSE)
183 response = et.Element(TAG_RESPONSE)
180
184
181 status = et.SubElement(response, TAG_STATUS)
185 status = et.SubElement(response, TAG_STATUS)
182 status.text = STATUS_SUCCESS
186 status.text = STATUS_SUCCESS
183
187
184 models = et.SubElement(response, TAG_MODELS)
188 models = et.SubElement(response, TAG_MODELS)
185
189
186 for post in Post.objects.all():
190 for post in Post.objects.all():
187 tag_id = et.SubElement(models, TAG_ID)
191 tag_id = et.SubElement(models, TAG_ID)
188 post.global_id.to_xml_element(tag_id)
192 post.global_id.to_xml_element(tag_id)
189
193
190 return et.tostring(response, ENCODING_UNICODE)
194 return et.tostring(response, ENCODING_UNICODE)
191
195
192 @staticmethod
196 @staticmethod
193 def _verify_model(tag_content, tag_model):
197 def _verify_model(tag_content, tag_model):
194 """
198 """
195 Verifies all signatures for a single model.
199 Verifies all signatures for a single model.
196 """
200 """
197
201
198 signatures = []
202 signatures = []
199
203
200 tag_signatures = tag_model.find(TAG_SIGNATURES)
204 tag_signatures = tag_model.find(TAG_SIGNATURES)
201 for tag_signature in tag_signatures:
205 for tag_signature in tag_signatures:
202 signature_type = tag_signature.get(ATTR_TYPE)
206 signature_type = tag_signature.get(ATTR_TYPE)
203 signature_value = tag_signature.get(ATTR_VALUE)
207 signature_value = tag_signature.get(ATTR_VALUE)
204 signature_key = tag_signature.get(ATTR_KEY)
208 signature_key = tag_signature.get(ATTR_KEY)
205
209
206 signature = Signature(key_type=signature_type,
210 signature = Signature(key_type=signature_type,
207 key=signature_key,
211 key=signature_key,
208 signature=signature_value)
212 signature=signature_value)
209
213
210 content = et.tostring(tag_content, ENCODING_UNICODE)
214 content = et.tostring(tag_content, ENCODING_UNICODE)
211
215
212 if not KeyPair.objects.verify(
216 if not KeyPair.objects.verify(
213 signature, content):
217 signature, content):
214 raise Exception('Invalid model signature for {}'.format(content))
218 raise Exception('Invalid model signature for {}'.format(content))
215
219
216 signatures.append(signature)
220 signatures.append(signature)
217
221
218 return signatures
222 return signatures
223
224 @staticmethod
225 def _attachment_to_xml(tag_attachments, tag_refs, file, hash, url):
226 mimetype = get_file_mimetype(file)
227 attachment = et.SubElement(tag_attachments, TAG_ATTACHMENT)
228 attachment.set(ATTR_MIMETYPE, mimetype)
229 attachment.text = hash
230
231 attachment_ref = et.SubElement(tag_refs, TAG_ATTACHMENT_REF)
232 attachment_ref.set(ATTR_REF, hash)
233 attachment_ref.set(ATTR_URL, url)
General Comments 0
You need to be logged in to leave comments. Login now