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