##// END OF EJS Templates
Added id-type attribute to attachments to specify a type of hash (currently only md5). Added management command to delete global id caches
neko259 -
r1560:bf42cc40 default
parent child Browse files
Show More
@@ -0,0 +1,20 b''
1 from django.core.management import BaseCommand
2 from django.db import transaction
3
4 from boards.models import GlobalId
5
6 __author__ = 'neko259'
7
8
9 class Command(BaseCommand):
10 help = 'Removes local global ID cache'
11
12 @transaction.atomic
13 def handle(self, *args, **options):
14 count = 0
15 for global_id in GlobalId.objects.all():
16 if global_id.is_local() and global_id.content is not None:
17 global_id.content = None
18 global_id.save()
19 count += 1
20 print('Invalidated {} caches.'.format(count))
@@ -1,14 +1,14 b''
1 STATUS_ACTIVE = 'active'
1 STATUS_ACTIVE = 'active'
2 STATUS_BUMPLIMIT = 'bumplimit'
2 STATUS_BUMPLIMIT = 'bumplimit'
3 STATUS_ARCHIVE = 'archived'
3 STATUS_ARCHIVE = 'archived'
4
4
5
5
6 from boards.models.sync_key import KeyPair
6 from boards.models.signature import GlobalId, Signature
7 from boards.models.signature import GlobalId, Signature
7 from boards.models.sync_key import KeyPair
8 from boards.models.image import PostImage
8 from boards.models.image import PostImage
9 from boards.models.attachment import Attachment
9 from boards.models.attachment import Attachment
10 from boards.models.thread import Thread
10 from boards.models.thread import Thread
11 from boards.models.post import Post
11 from boards.models.post import Post
12 from boards.models.tag import Tag
12 from boards.models.tag import Tag
13 from boards.models.user import Ban
13 from boards.models.user import Ban
14 from boards.models.banner import Banner
14 from boards.models.banner import Banner
@@ -1,276 +1,280 b''
1 import xml.etree.ElementTree as et
1 import xml.etree.ElementTree as et
2
2
3 from boards.models.attachment.downloaders import download
3 from boards.models.attachment.downloaders import download
4 from boards.utils import get_file_mimetype, get_file_hash
4 from boards.utils import get_file_mimetype, get_file_hash
5 from django.db import transaction
5 from django.db import transaction
6 from boards.models import KeyPair, GlobalId, Signature, Post, Tag
6 from boards.models import KeyPair, GlobalId, Signature, Post, Tag
7
7
8 EXCEPTION_NODE = 'Sync node returned an error: {}'
8 EXCEPTION_NODE = 'Sync node returned an error: {}'
9 EXCEPTION_OP = 'Load the OP first'
9 EXCEPTION_OP = 'Load the OP first'
10 EXCEPTION_DOWNLOAD = 'File was not downloaded'
10 EXCEPTION_DOWNLOAD = 'File was not downloaded'
11 EXCEPTION_HASH = 'File hash does not match attachment hash'
11 EXCEPTION_HASH = 'File hash does not match attachment hash'
12 EXCEPTION_SIGNATURE = 'Invalid model signature for {}'
12 EXCEPTION_SIGNATURE = 'Invalid model signature for {}'
13 ENCODING_UNICODE = 'unicode'
13 ENCODING_UNICODE = 'unicode'
14
14
15 TAG_MODEL = 'model'
15 TAG_MODEL = 'model'
16 TAG_REQUEST = 'request'
16 TAG_REQUEST = 'request'
17 TAG_RESPONSE = 'response'
17 TAG_RESPONSE = 'response'
18 TAG_ID = 'id'
18 TAG_ID = 'id'
19 TAG_STATUS = 'status'
19 TAG_STATUS = 'status'
20 TAG_MODELS = 'models'
20 TAG_MODELS = 'models'
21 TAG_TITLE = 'title'
21 TAG_TITLE = 'title'
22 TAG_TEXT = 'text'
22 TAG_TEXT = 'text'
23 TAG_THREAD = 'thread'
23 TAG_THREAD = 'thread'
24 TAG_PUB_TIME = 'pub-time'
24 TAG_PUB_TIME = 'pub-time'
25 TAG_SIGNATURES = 'signatures'
25 TAG_SIGNATURES = 'signatures'
26 TAG_SIGNATURE = 'signature'
26 TAG_SIGNATURE = 'signature'
27 TAG_CONTENT = 'content'
27 TAG_CONTENT = 'content'
28 TAG_ATTACHMENTS = 'attachments'
28 TAG_ATTACHMENTS = 'attachments'
29 TAG_ATTACHMENT = 'attachment'
29 TAG_ATTACHMENT = 'attachment'
30 TAG_TAGS = 'tags'
30 TAG_TAGS = 'tags'
31 TAG_TAG = 'tag'
31 TAG_TAG = 'tag'
32 TAG_ATTACHMENT_REFS = 'attachment-refs'
32 TAG_ATTACHMENT_REFS = 'attachment-refs'
33 TAG_ATTACHMENT_REF = 'attachment-ref'
33 TAG_ATTACHMENT_REF = 'attachment-ref'
34 TAG_TRIPCODE = 'tripcode'
34 TAG_TRIPCODE = 'tripcode'
35
35
36 TYPE_GET = 'get'
36 TYPE_GET = 'get'
37
37
38 ATTR_VERSION = 'version'
38 ATTR_VERSION = 'version'
39 ATTR_TYPE = 'type'
39 ATTR_TYPE = 'type'
40 ATTR_NAME = 'name'
40 ATTR_NAME = 'name'
41 ATTR_VALUE = 'value'
41 ATTR_VALUE = 'value'
42 ATTR_MIMETYPE = 'mimetype'
42 ATTR_MIMETYPE = 'mimetype'
43 ATTR_KEY = 'key'
43 ATTR_KEY = 'key'
44 ATTR_REF = 'ref'
44 ATTR_REF = 'ref'
45 ATTR_URL = 'url'
45 ATTR_URL = 'url'
46 ATTR_ID_TYPE = 'id-type'
47
48 ID_TYPE_MD5 = 'md5'
46
49
47 STATUS_SUCCESS = 'success'
50 STATUS_SUCCESS = 'success'
48
51
49
52
50 class SyncException(Exception):
53 class SyncException(Exception):
51 pass
54 pass
52
55
53
56
54 class SyncManager:
57 class SyncManager:
55 @staticmethod
58 @staticmethod
56 def generate_response_get(model_list: list):
59 def generate_response_get(model_list: list):
57 response = et.Element(TAG_RESPONSE)
60 response = et.Element(TAG_RESPONSE)
58
61
59 status = et.SubElement(response, TAG_STATUS)
62 status = et.SubElement(response, TAG_STATUS)
60 status.text = STATUS_SUCCESS
63 status.text = STATUS_SUCCESS
61
64
62 models = et.SubElement(response, TAG_MODELS)
65 models = et.SubElement(response, TAG_MODELS)
63
66
64 for post in model_list:
67 for post in model_list:
65 model = et.SubElement(models, TAG_MODEL)
68 model = et.SubElement(models, TAG_MODEL)
66 model.set(ATTR_NAME, 'post')
69 model.set(ATTR_NAME, 'post')
67
70
68 global_id = post.global_id
71 global_id = post.global_id
69
72
70 images = post.images.all()
73 images = post.images.all()
71 attachments = post.attachments.all()
74 attachments = post.attachments.all()
72 if global_id.content:
75 if global_id.content:
73 model.append(et.fromstring(global_id.content))
76 model.append(et.fromstring(global_id.content))
74 if len(images) > 0 or len(attachments) > 0:
77 if len(images) > 0 or len(attachments) > 0:
75 attachment_refs = et.SubElement(model, TAG_ATTACHMENT_REFS)
78 attachment_refs = et.SubElement(model, TAG_ATTACHMENT_REFS)
76 for image in images:
79 for image in images:
77 SyncManager._attachment_to_xml(
80 SyncManager._attachment_to_xml(
78 None, attachment_refs, image.image.file,
81 None, attachment_refs, image.image.file,
79 image.hash, image.image.url)
82 image.hash, image.image.url)
80 for file in attachments:
83 for file in attachments:
81 SyncManager._attachment_to_xml(
84 SyncManager._attachment_to_xml(
82 None, attachment_refs, file.file.file,
85 None, attachment_refs, file.file.file,
83 file.hash, file.file.url)
86 file.hash, file.file.url)
84 else:
87 else:
85 content_tag = et.SubElement(model, TAG_CONTENT)
88 content_tag = et.SubElement(model, TAG_CONTENT)
86
89
87 tag_id = et.SubElement(content_tag, TAG_ID)
90 tag_id = et.SubElement(content_tag, TAG_ID)
88 global_id.to_xml_element(tag_id)
91 global_id.to_xml_element(tag_id)
89
92
90 title = et.SubElement(content_tag, TAG_TITLE)
93 title = et.SubElement(content_tag, TAG_TITLE)
91 title.text = post.title
94 title.text = post.title
92
95
93 text = et.SubElement(content_tag, TAG_TEXT)
96 text = et.SubElement(content_tag, TAG_TEXT)
94 text.text = post.get_sync_text()
97 text.text = post.get_sync_text()
95
98
96 thread = post.get_thread()
99 thread = post.get_thread()
97 if post.is_opening():
100 if post.is_opening():
98 tag_tags = et.SubElement(content_tag, TAG_TAGS)
101 tag_tags = et.SubElement(content_tag, TAG_TAGS)
99 for tag in thread.get_tags():
102 for tag in thread.get_tags():
100 tag_tag = et.SubElement(tag_tags, TAG_TAG)
103 tag_tag = et.SubElement(tag_tags, TAG_TAG)
101 tag_tag.text = tag.name
104 tag_tag.text = tag.name
102 else:
105 else:
103 tag_thread = et.SubElement(content_tag, TAG_THREAD)
106 tag_thread = et.SubElement(content_tag, TAG_THREAD)
104 thread_id = et.SubElement(tag_thread, TAG_ID)
107 thread_id = et.SubElement(tag_thread, TAG_ID)
105 thread.get_opening_post().global_id.to_xml_element(thread_id)
108 thread.get_opening_post().global_id.to_xml_element(thread_id)
106
109
107 pub_time = et.SubElement(content_tag, TAG_PUB_TIME)
110 pub_time = et.SubElement(content_tag, TAG_PUB_TIME)
108 pub_time.text = str(post.get_pub_time_str())
111 pub_time.text = str(post.get_pub_time_str())
109
112
110 if post.tripcode:
113 if post.tripcode:
111 tripcode = et.SubElement(content_tag, TAG_TRIPCODE)
114 tripcode = et.SubElement(content_tag, TAG_TRIPCODE)
112 tripcode.text = post.tripcode
115 tripcode.text = post.tripcode
113
116
114 if len(images) > 0 or len(attachments) > 0:
117 if len(images) > 0 or len(attachments) > 0:
115 attachments_tag = et.SubElement(content_tag, TAG_ATTACHMENTS)
118 attachments_tag = et.SubElement(content_tag, TAG_ATTACHMENTS)
116 attachment_refs = et.SubElement(model, TAG_ATTACHMENT_REFS)
119 attachment_refs = et.SubElement(model, TAG_ATTACHMENT_REFS)
117
120
118 for image in images:
121 for image in images:
119 SyncManager._attachment_to_xml(
122 SyncManager._attachment_to_xml(
120 attachments_tag, attachment_refs, image.image.file,
123 attachments_tag, attachment_refs, image.image.file,
121 image.hash, image.image.url)
124 image.hash, image.image.url)
122 for file in attachments:
125 for file in attachments:
123 SyncManager._attachment_to_xml(
126 SyncManager._attachment_to_xml(
124 attachments_tag, attachment_refs, file.file.file,
127 attachments_tag, attachment_refs, file.file.file,
125 file.hash, file.file.url)
128 file.hash, file.file.url)
126
129
127 global_id.content = et.tostring(content_tag, ENCODING_UNICODE)
130 global_id.content = et.tostring(content_tag, ENCODING_UNICODE)
128 global_id.save()
131 global_id.save()
129
132
130 signatures_tag = et.SubElement(model, TAG_SIGNATURES)
133 signatures_tag = et.SubElement(model, TAG_SIGNATURES)
131 post_signatures = global_id.signature_set.all()
134 post_signatures = global_id.signature_set.all()
132 if post_signatures:
135 if post_signatures:
133 signatures = post_signatures
136 signatures = post_signatures
134 else:
137 else:
135 key = KeyPair.objects.get(public_key=global_id.key)
138 key = KeyPair.objects.get(public_key=global_id.key)
136 signature = Signature(
139 signature = Signature(
137 key_type=key.key_type,
140 key_type=key.key_type,
138 key=key.public_key,
141 key=key.public_key,
139 signature=key.sign(global_id.content),
142 signature=key.sign(global_id.content),
140 global_id=global_id,
143 global_id=global_id,
141 )
144 )
142 signature.save()
145 signature.save()
143 signatures = [signature]
146 signatures = [signature]
144 for signature in signatures:
147 for signature in signatures:
145 signature_tag = et.SubElement(signatures_tag, TAG_SIGNATURE)
148 signature_tag = et.SubElement(signatures_tag, TAG_SIGNATURE)
146 signature_tag.set(ATTR_TYPE, signature.key_type)
149 signature_tag.set(ATTR_TYPE, signature.key_type)
147 signature_tag.set(ATTR_VALUE, signature.signature)
150 signature_tag.set(ATTR_VALUE, signature.signature)
148 signature_tag.set(ATTR_KEY, signature.key)
151 signature_tag.set(ATTR_KEY, signature.key)
149
152
150 return et.tostring(response, ENCODING_UNICODE)
153 return et.tostring(response, ENCODING_UNICODE)
151
154
152 @staticmethod
155 @staticmethod
153 @transaction.atomic
156 @transaction.atomic
154 def parse_response_get(response_xml, hostname):
157 def parse_response_get(response_xml, hostname):
155 tag_root = et.fromstring(response_xml)
158 tag_root = et.fromstring(response_xml)
156 tag_status = tag_root.find(TAG_STATUS)
159 tag_status = tag_root.find(TAG_STATUS)
157 if STATUS_SUCCESS == tag_status.text:
160 if STATUS_SUCCESS == tag_status.text:
158 tag_models = tag_root.find(TAG_MODELS)
161 tag_models = tag_root.find(TAG_MODELS)
159 for tag_model in tag_models:
162 for tag_model in tag_models:
160 tag_content = tag_model.find(TAG_CONTENT)
163 tag_content = tag_model.find(TAG_CONTENT)
161
164
162 content_str = et.tostring(tag_content, ENCODING_UNICODE)
165 content_str = et.tostring(tag_content, ENCODING_UNICODE)
163 signatures = SyncManager._verify_model(content_str, tag_model)
166 signatures = SyncManager._verify_model(content_str, tag_model)
164
167
165 tag_id = tag_content.find(TAG_ID)
168 tag_id = tag_content.find(TAG_ID)
166 global_id, exists = GlobalId.from_xml_element(tag_id)
169 global_id, exists = GlobalId.from_xml_element(tag_id)
167
170
168 if exists:
171 if exists:
169 print('Post with same ID already exists')
172 print('Post with same ID already exists')
170 else:
173 else:
171 global_id.content = content_str
174 global_id.content = content_str
172 global_id.save()
175 global_id.save()
173 for signature in signatures:
176 for signature in signatures:
174 signature.global_id = global_id
177 signature.global_id = global_id
175 signature.save()
178 signature.save()
176
179
177 title = tag_content.find(TAG_TITLE).text or ''
180 title = tag_content.find(TAG_TITLE).text or ''
178 text = tag_content.find(TAG_TEXT).text or ''
181 text = tag_content.find(TAG_TEXT).text or ''
179 pub_time = tag_content.find(TAG_PUB_TIME).text
182 pub_time = tag_content.find(TAG_PUB_TIME).text
180 tripcode = tag_content.find(TAG_TRIPCODE).text
183 tripcode = tag_content.find(TAG_TRIPCODE).text
181
184
182 thread = tag_content.find(TAG_THREAD)
185 thread = tag_content.find(TAG_THREAD)
183 tags = []
186 tags = []
184 if thread:
187 if thread:
185 thread_id = thread.find(TAG_ID)
188 thread_id = thread.find(TAG_ID)
186 op_global_id, exists = GlobalId.from_xml_element(thread_id)
189 op_global_id, exists = GlobalId.from_xml_element(thread_id)
187 if exists:
190 if exists:
188 opening_post = Post.objects.get(global_id=op_global_id)
191 opening_post = Post.objects.get(global_id=op_global_id)
189 else:
192 else:
190 raise SyncException(EXCEPTION_OP)
193 raise SyncException(EXCEPTION_OP)
191 else:
194 else:
192 opening_post = None
195 opening_post = None
193 tag_tags = tag_content.find(TAG_TAGS)
196 tag_tags = tag_content.find(TAG_TAGS)
194 for tag_tag in tag_tags:
197 for tag_tag in tag_tags:
195 tag, created = Tag.objects.get_or_create(
198 tag, created = Tag.objects.get_or_create(
196 name=tag_tag.text)
199 name=tag_tag.text)
197 tags.append(tag)
200 tags.append(tag)
198
201
199 # TODO Check that the replied posts are already present
202 # TODO Check that the replied posts are already present
200 # before adding new ones
203 # before adding new ones
201
204
202 files = []
205 files = []
203 tag_attachments = tag_content.find(TAG_ATTACHMENTS) or list()
206 tag_attachments = tag_content.find(TAG_ATTACHMENTS) or list()
204 tag_refs = tag_model.find(TAG_ATTACHMENT_REFS)
207 tag_refs = tag_model.find(TAG_ATTACHMENT_REFS)
205 for attachment in tag_attachments:
208 for attachment in tag_attachments:
206 tag_ref = tag_refs.find("{}[@ref='{}']".format(
209 tag_ref = tag_refs.find("{}[@ref='{}']".format(
207 TAG_ATTACHMENT_REF, attachment.text))
210 TAG_ATTACHMENT_REF, attachment.text))
208 url = tag_ref.get(ATTR_URL)
211 url = tag_ref.get(ATTR_URL)
209 attached_file = download(hostname + url)
212 attached_file = download(hostname + url)
210 if attached_file is None:
213 if attached_file is None:
211 raise SyncException(EXCEPTION_DOWNLOAD)
214 raise SyncException(EXCEPTION_DOWNLOAD)
212
215
213 hash = get_file_hash(attached_file)
216 hash = get_file_hash(attached_file)
214 if hash != attachment.text:
217 if hash != attachment.text:
215 raise SyncException(EXCEPTION_HASH)
218 raise SyncException(EXCEPTION_HASH)
216
219
217 files.append(attached_file)
220 files.append(attached_file)
218
221
219 Post.objects.import_post(
222 Post.objects.import_post(
220 title=title, text=text, pub_time=pub_time,
223 title=title, text=text, pub_time=pub_time,
221 opening_post=opening_post, tags=tags,
224 opening_post=opening_post, tags=tags,
222 global_id=global_id, files=files, tripcode=tripcode)
225 global_id=global_id, files=files, tripcode=tripcode)
223 else:
226 else:
224 raise SyncException(EXCEPTION_NODE.format(tag_status.text))
227 raise SyncException(EXCEPTION_NODE.format(tag_status.text))
225
228
226 @staticmethod
229 @staticmethod
227 def generate_response_pull():
230 def generate_response_pull():
228 response = et.Element(TAG_RESPONSE)
231 response = et.Element(TAG_RESPONSE)
229
232
230 status = et.SubElement(response, TAG_STATUS)
233 status = et.SubElement(response, TAG_STATUS)
231 status.text = STATUS_SUCCESS
234 status.text = STATUS_SUCCESS
232
235
233 models = et.SubElement(response, TAG_MODELS)
236 models = et.SubElement(response, TAG_MODELS)
234
237
235 for post in Post.objects.all():
238 for post in Post.objects.all():
236 tag_id = et.SubElement(models, TAG_ID)
239 tag_id = et.SubElement(models, TAG_ID)
237 post.global_id.to_xml_element(tag_id)
240 post.global_id.to_xml_element(tag_id)
238
241
239 return et.tostring(response, ENCODING_UNICODE)
242 return et.tostring(response, ENCODING_UNICODE)
240
243
241 @staticmethod
244 @staticmethod
242 def _verify_model(content_str, tag_model):
245 def _verify_model(content_str, tag_model):
243 """
246 """
244 Verifies all signatures for a single model.
247 Verifies all signatures for a single model.
245 """
248 """
246
249
247 signatures = []
250 signatures = []
248
251
249 tag_signatures = tag_model.find(TAG_SIGNATURES)
252 tag_signatures = tag_model.find(TAG_SIGNATURES)
250 for tag_signature in tag_signatures:
253 for tag_signature in tag_signatures:
251 signature_type = tag_signature.get(ATTR_TYPE)
254 signature_type = tag_signature.get(ATTR_TYPE)
252 signature_value = tag_signature.get(ATTR_VALUE)
255 signature_value = tag_signature.get(ATTR_VALUE)
253 signature_key = tag_signature.get(ATTR_KEY)
256 signature_key = tag_signature.get(ATTR_KEY)
254
257
255 signature = Signature(key_type=signature_type,
258 signature = Signature(key_type=signature_type,
256 key=signature_key,
259 key=signature_key,
257 signature=signature_value)
260 signature=signature_value)
258
261
259 if not KeyPair.objects.verify(signature, content_str):
262 if not KeyPair.objects.verify(signature, content_str):
260 raise SyncException(EXCEPTION_SIGNATURE.format(content_str))
263 raise SyncException(EXCEPTION_SIGNATURE.format(content_str))
261
264
262 signatures.append(signature)
265 signatures.append(signature)
263
266
264 return signatures
267 return signatures
265
268
266 @staticmethod
269 @staticmethod
267 def _attachment_to_xml(tag_attachments, tag_refs, file, hash, url):
270 def _attachment_to_xml(tag_attachments, tag_refs, file, hash, url):
268 if tag_attachments is not None:
271 if tag_attachments is not None:
269 mimetype = get_file_mimetype(file)
272 mimetype = get_file_mimetype(file)
270 attachment = et.SubElement(tag_attachments, TAG_ATTACHMENT)
273 attachment = et.SubElement(tag_attachments, TAG_ATTACHMENT)
271 attachment.set(ATTR_MIMETYPE, mimetype)
274 attachment.set(ATTR_MIMETYPE, mimetype)
275 attachment.set(ATTR_ID_TYPE, ID_TYPE_MD5)
272 attachment.text = hash
276 attachment.text = hash
273
277
274 attachment_ref = et.SubElement(tag_refs, TAG_ATTACHMENT_REF)
278 attachment_ref = et.SubElement(tag_refs, TAG_ATTACHMENT_REF)
275 attachment_ref.set(ATTR_REF, hash)
279 attachment_ref.set(ATTR_REF, hash)
276 attachment_ref.set(ATTR_URL, url)
280 attachment_ref.set(ATTR_URL, url)
@@ -1,144 +1,150 b''
1 import xml.etree.ElementTree as et
1 import xml.etree.ElementTree as et
2 from django.db import models
2 from django.db import models
3 from boards.models import KeyPair
3
4
4
5
5 TAG_MODEL = 'model'
6 TAG_MODEL = 'model'
6 TAG_REQUEST = 'request'
7 TAG_REQUEST = 'request'
7 TAG_ID = 'id'
8 TAG_ID = 'id'
8
9
9 TYPE_GET = 'get'
10 TYPE_GET = 'get'
10 TYPE_PULL = 'pull'
11 TYPE_PULL = 'pull'
11
12
12 ATTR_VERSION = 'version'
13 ATTR_VERSION = 'version'
13 ATTR_TYPE = 'type'
14 ATTR_TYPE = 'type'
14 ATTR_NAME = 'name'
15 ATTR_NAME = 'name'
15
16
16 ATTR_KEY = 'key'
17 ATTR_KEY = 'key'
17 ATTR_KEY_TYPE = 'type'
18 ATTR_KEY_TYPE = 'type'
18 ATTR_LOCAL_ID = 'local-id'
19 ATTR_LOCAL_ID = 'local-id'
19
20
20
21
21 class GlobalIdManager(models.Manager):
22 class GlobalIdManager(models.Manager):
22 def generate_request_get(self, global_id_list: list):
23 def generate_request_get(self, global_id_list: list):
23 """
24 """
24 Form a get request from a list of ModelId objects.
25 Form a get request from a list of ModelId objects.
25 """
26 """
26
27
27 request = et.Element(TAG_REQUEST)
28 request = et.Element(TAG_REQUEST)
28 request.set(ATTR_TYPE, TYPE_GET)
29 request.set(ATTR_TYPE, TYPE_GET)
29 request.set(ATTR_VERSION, '1.0')
30 request.set(ATTR_VERSION, '1.0')
30
31
31 model = et.SubElement(request, TAG_MODEL)
32 model = et.SubElement(request, TAG_MODEL)
32 model.set(ATTR_VERSION, '1.0')
33 model.set(ATTR_VERSION, '1.0')
33 model.set(ATTR_NAME, 'post')
34 model.set(ATTR_NAME, 'post')
34
35
35 for global_id in global_id_list:
36 for global_id in global_id_list:
36 tag_id = et.SubElement(model, TAG_ID)
37 tag_id = et.SubElement(model, TAG_ID)
37 global_id.to_xml_element(tag_id)
38 global_id.to_xml_element(tag_id)
38
39
39 return et.tostring(request, 'unicode')
40 return et.tostring(request, 'unicode')
40
41
41 def generate_request_pull(self):
42 def generate_request_pull(self):
42 """
43 """
43 Form a pull request from a list of ModelId objects.
44 Form a pull request from a list of ModelId objects.
44 """
45 """
45
46
46 request = et.Element(TAG_REQUEST)
47 request = et.Element(TAG_REQUEST)
47 request.set(ATTR_TYPE, TYPE_PULL)
48 request.set(ATTR_TYPE, TYPE_PULL)
48 request.set(ATTR_VERSION, '1.0')
49 request.set(ATTR_VERSION, '1.0')
49
50
50 model = et.SubElement(request, TAG_MODEL)
51 model = et.SubElement(request, TAG_MODEL)
51 model.set(ATTR_VERSION, '1.0')
52 model.set(ATTR_VERSION, '1.0')
52 model.set(ATTR_NAME, 'post')
53 model.set(ATTR_NAME, 'post')
53
54
54 return et.tostring(request, 'unicode')
55 return et.tostring(request, 'unicode')
55
56
56 def global_id_exists(self, global_id):
57 def global_id_exists(self, global_id):
57 """
58 """
58 Checks if the same global id already exists in the system.
59 Checks if the same global id already exists in the system.
59 """
60 """
60
61
61 return self.filter(key=global_id.key,
62 return self.filter(key=global_id.key,
62 key_type=global_id.key_type,
63 key_type=global_id.key_type,
63 local_id=global_id.local_id).exists()
64 local_id=global_id.local_id).exists()
64
65
65
66
66 class GlobalId(models.Model):
67 class GlobalId(models.Model):
67 """
68 """
68 Global model ID and cache.
69 Global model ID and cache.
69 Key, key type and local ID make a single global identificator of the model.
70 Key, key type and local ID make a single global identificator of the model.
70 Content is an XML cache of the model that can be passed along between nodes
71 Content is an XML cache of the model that can be passed along between nodes
71 without manual serialization each time.
72 without manual serialization each time.
72 """
73 """
73 class Meta:
74 class Meta:
74 app_label = 'boards'
75 app_label = 'boards'
75
76
76 objects = GlobalIdManager()
77 objects = GlobalIdManager()
77
78
78 def __init__(self, *args, **kwargs):
79 def __init__(self, *args, **kwargs):
79 models.Model.__init__(self, *args, **kwargs)
80 models.Model.__init__(self, *args, **kwargs)
80
81
81 if 'key' in kwargs and 'key_type' in kwargs and 'local_id' in kwargs:
82 if 'key' in kwargs and 'key_type' in kwargs and 'local_id' in kwargs:
82 self.key = kwargs['key']
83 self.key = kwargs['key']
83 self.key_type = kwargs['key_type']
84 self.key_type = kwargs['key_type']
84 self.local_id = kwargs['local_id']
85 self.local_id = kwargs['local_id']
85
86
86 key = models.TextField()
87 key = models.TextField()
87 key_type = models.TextField()
88 key_type = models.TextField()
88 local_id = models.IntegerField()
89 local_id = models.IntegerField()
89 content = models.TextField(blank=True, null=True)
90 content = models.TextField(blank=True, null=True)
90
91
91 def __str__(self):
92 def __str__(self):
92 return '%s::%s::%d' % (self.key_type, self.key, self.local_id)
93 return '%s::%s::%d' % (self.key_type, self.key, self.local_id)
93
94
94 def to_xml_element(self, element: et.Element):
95 def to_xml_element(self, element: et.Element):
95 """
96 """
96 Exports global id to an XML element.
97 Exports global id to an XML element.
97 """
98 """
98
99
99 element.set(ATTR_KEY, self.key)
100 element.set(ATTR_KEY, self.key)
100 element.set(ATTR_KEY_TYPE, self.key_type)
101 element.set(ATTR_KEY_TYPE, self.key_type)
101 element.set(ATTR_LOCAL_ID, str(self.local_id))
102 element.set(ATTR_LOCAL_ID, str(self.local_id))
102
103
103 @staticmethod
104 @staticmethod
104 def from_xml_element(element: et.Element):
105 def from_xml_element(element: et.Element):
105 """
106 """
106 Parses XML id tag and gets global id from it.
107 Parses XML id tag and gets global id from it.
107
108
108 Arguments:
109 Arguments:
109 element -- the XML 'id' element
110 element -- the XML 'id' element
110
111
111 Returns:
112 Returns:
112 global_id -- id itself
113 global_id -- id itself
113 exists -- True if the global id was taken from database, False if it
114 exists -- True if the global id was taken from database, False if it
114 did not exist and was created.
115 did not exist and was created.
115 """
116 """
116
117
117 try:
118 try:
118 return GlobalId.objects.get(key=element.get(ATTR_KEY),
119 return GlobalId.objects.get(key=element.get(ATTR_KEY),
119 key_type=element.get(ATTR_KEY_TYPE),
120 key_type=element.get(ATTR_KEY_TYPE),
120 local_id=int(element.get(
121 local_id=int(element.get(
121 ATTR_LOCAL_ID))), True
122 ATTR_LOCAL_ID))), True
122 except GlobalId.DoesNotExist:
123 except GlobalId.DoesNotExist:
123 return GlobalId(key=element.get(ATTR_KEY),
124 return GlobalId(key=element.get(ATTR_KEY),
124 key_type=element.get(ATTR_KEY_TYPE),
125 key_type=element.get(ATTR_KEY_TYPE),
125 local_id=int(element.get(ATTR_LOCAL_ID))), False
126 local_id=int(element.get(ATTR_LOCAL_ID))), False
126
127
128 def is_local(self):
129 """Checks fo the ID is local model's"""
130 return KeyPair.objects.filter(
131 key_type=self.key_type, public_key=self.key).exists()
132
127
133
128 class Signature(models.Model):
134 class Signature(models.Model):
129 class Meta:
135 class Meta:
130 app_label = 'boards'
136 app_label = 'boards'
131
137
132 def __init__(self, *args, **kwargs):
138 def __init__(self, *args, **kwargs):
133 models.Model.__init__(self, *args, **kwargs)
139 models.Model.__init__(self, *args, **kwargs)
134
140
135 if 'key' in kwargs and 'key_type' in kwargs and 'signature' in kwargs:
141 if 'key' in kwargs and 'key_type' in kwargs and 'signature' in kwargs:
136 self.key_type = kwargs['key_type']
142 self.key_type = kwargs['key_type']
137 self.key = kwargs['key']
143 self.key = kwargs['key']
138 self.signature = kwargs['signature']
144 self.signature = kwargs['signature']
139
145
140 key_type = models.TextField()
146 key_type = models.TextField()
141 key = models.TextField()
147 key = models.TextField()
142 signature = models.TextField()
148 signature = models.TextField()
143
149
144 global_id = models.ForeignKey('GlobalId')
150 global_id = models.ForeignKey('GlobalId')
General Comments 0
You need to be logged in to leave comments. Login now