comparison storage.js @ 0:9ee956af41e3

Initial commit
author Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
date Sun, 27 Jun 2010 22:05:12 +0200
parents
children c2954a9e5665
comparison
equal deleted inserted replaced
-1:000000000000 0:9ee956af41e3
1 /*
2 * Copyright (C) 2010 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
3 *
4 * This file is part of PSĜS, a PubSub server written in JavaScript.
5 *
6 * PSĜS is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Affero General Public License as
8 * published by the Free Software Foundation, either version 3 of the
9 * License.
10 *
11 * PSĜS is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Affero General Public License for more details.
15 *
16 * You should have received a copy of the GNU Affero General Public License
17 * along with PSĜS. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 var sys = require('sys');
21 var sha1hex = require('sha1').hex;
22 require('./iso8601');
23 var errors = require('./errors');
24 var config = require('./configuration');
25 var service_configuration = config.service_configuration;
26 var Configuration = config.Configuration;
27 var utils = require('./util');
28 var toBareJID = utils.toBareJID;
29
30 var nodes = require('./nodes');
31 var Node = nodes.Node;
32 var Item = nodes.Item;
33
34 var fs = require('fs');
35
36 var list = {};
37
38 var storage = exports;
39 storage.createNode = function(nodeID, params) {
40 for (var i in list)
41 if (i == nodeID)
42 return errors.owner.create.nodeid_already_exists.n;
43
44 list[nodeID] = new Node(params);
45 storage.save();
46 return errors.success;
47 };
48
49 storage.getMetadata = function(nodeID) {
50 var node = storage.getNode(nodeID);
51 if (typeof node == 'number')
52 return node;
53
54 if (!config.enabled('meta-data'))
55 return {};
56
57 var md = {};
58 for (var i in service_configuration.node_metadata) {
59 if (i == 'FORM_TYPE')
60 continue;
61
62 if (node.configuration[i])
63 md[i] = node.configuration[i];
64 else if (node.metadata[i])
65 md[i] = node.metadata[i];
66 }
67 return md;
68 };
69
70 storage.getConfiguration = function(nodeID) {
71 var node = storage.getNode(nodeID);
72 if (typeof node == 'number')
73 return node;
74
75 if (config.enabled('config-node'))
76 return node.configuration;
77
78 return {}; //FIXME
79 };
80
81 storage.configure = function(nodeID, params) {
82 var node = storage.getNode(nodeID);
83 if (typeof node == 'number')
84 return node;
85
86 if (!config.enabled('config-node'))
87 return 42; //FIXME
88
89 for (var i in params)
90 if (service_configuration.node_config[i])
91 node.configuration[i] = params[i];
92
93 storage.save();
94 return errors.success;
95 };
96
97 storage.configureSubscription = function(nodeID, jid, params) {
98 var node = storage.getNode(nodeID);
99 if (typeof node == 'number')
100 return node;
101
102 if (!config.enabled('subscription-options'))
103 return 42; //FIXME
104
105 var options = node.subscribers[jid].options;
106 if (!options)
107 return 42; //FIXME
108
109 for (var i in params)
110 if (service_configuration.subscribe_options[i])
111 options[i] = params[i];
112
113 storage.save();
114 return errors.success;
115 };
116
117 storage.getChildren = function(node) {
118 var n;
119 var items = {};
120 if (node)
121 n = storage.getNode(node).items;
122 else
123 n = list;
124
125 for (var i in n) {
126 var type;
127 if (n[i].content)
128 type = 'item';
129 else
130 type = 'node';
131 items[i] = type;
132 }
133 return items;
134 };
135
136 storage.getNode = function(nodeID) {
137 if (list[nodeID])
138 return list[nodeID];
139 return errors.sub.subscribe.node_does_not_exist.n;
140 };
141
142 storage.existsNode = function(nodeID) {
143 if (list[nodeID])
144 return true;
145 return false;
146 };
147
148 storage.purgeNode = function(nodeID) {
149 var node = storage.getNode(nodeID);
150 if (typeof n == 'number')
151 return node;
152
153 var notifs = storage.getSubscriptionsFromNodeID(nodeID);
154
155 var item = storage.getLastItem(nodeID);
156 if (typeof item == 'number') {
157 node.items = {};
158 } else {
159 items = node.items[item];
160 node.items = {};
161 node.items[item] = items;
162 }
163
164 storage.save();
165 return notifs;
166 };
167
168 storage.deleteNodeWithRedirect = function(nodeID, uri) {
169 var del = storage.deleteNode(nodeID);
170 if (typeof del == 'number')
171 return del;
172
173 list[nodeID] = uri;
174 storage.save();
175 return errors.success;
176 };
177
178 storage.deleteNode = function(nodeID) {
179 var node = storage.getNode(nodeID);
180 if (typeof n == 'number')
181 return node;
182
183 var notifs = {};
184 for (var i in node.subscribers)
185 notifs[i] = {};
186 for (var i in node.owner)
187 notifs[node.owner[i]] = {};
188 if (config.enabled('publisher-affiliation'))
189 for (var i in node.publisher)
190 notifs[node.publisher[i]] = {};
191 if (config.enabled('publish-only-affiliation'))
192 for (var i in node.publishOnly)
193 notifs[node.publishOnly[i]] = {};
194
195 delete list[nodeID];
196 storage.save();
197 return notifs;
198 };
199
200 storage.setItem = function(nodeID, itemID, content) {
201 var node = storage.getNode(nodeID);
202 if (typeof node == 'number')
203 return node;
204
205 if (typeof itemID != 'string')
206 itemID = utils.makeRandomId();
207
208 i = node.setItem(itemID, content);
209 if (content)
210 i.content = content;
211
212 storage.save();
213 return storage.getSubscriptionsFromNodeID(nodeID)
214 };
215
216 storage.deleteItem = function(nodeID, itemID) {
217 var node = storage.getNode(nodeID);
218 if (typeof node == 'number')
219 return node;
220
221 if (typeof itemID != 'string')
222 return errors.pub.retract.item_or_itemid_required.n;
223
224 item = node.setItem(itemID);
225 if (typeof item == 'number')
226 return item;
227
228 storage.save();
229 return storage.getSubscriptionsFromNodeID(nodeID)
230 };
231
232 storage.getItems = function(nodeID) {
233 var node;
234 if (typeof nodeID == 'string')
235 node = storage.getNode(nodeID);
236 else
237 node = nodeID;
238
239 if (typeof node == 'number')
240 return node;
241
242 return node.items;
243 };
244
245 storage.getLastItem = function(nodeID, number) {
246 var items = storage.getItems(nodeID);
247 if (typeof items == 'number')
248 return items;
249
250 if (items == {})
251 return 42; //FIXME
252
253 if (number) {
254 var last = [];
255 var j = 0;
256 for (var i in items) {
257 last.push({name: i, date: items[i].date, content: items[i].content});
258 j++;
259 }
260 if (j < number)
261 return last;
262
263 var cmp = function(a, b) {
264 return b.date - a.date;
265 }
266 last = last.sort(cmp).slice(0, number);
267 } else {
268 var last;
269 for (var i in items)
270 if ((typeof last == 'undefined') || (items[i].date >= items[last].date))
271 last = i;
272 }
273
274 if (last)
275 return last;
276 return 42; //FIXME
277 };
278
279 storage.existsItem = function(nodeID, itemID) {
280 var items = storage.getItems(nodeID);
281 if (typeof items == 'number')
282 return items;
283
284 for (var i in items)
285 if (i == itemID)
286 return items[i];
287 return false;
288 };
289
290 storage.getItem = function(nodeID, itemID) {
291 if (!storage.existsItem(nodeID, itemID))
292 return 42; //FIXME
293
294 var items = storage.existsItem(nodeID, itemID);
295 if (typeof items == 'number')
296 return items;
297 if (items)
298 return items.content;
299 return errors.item_not_found;
300 };
301
302 storage.getSubscription = function(jid, nodeID) {
303 var subs = {};
304 if (nodeID) {
305 var node = storage.getNode(nodeID);
306 for (var sub in node.subscribers)
307 if (toBareJID(sub) == jid)
308 return node.subscribers[sub]
309 } else {
310 for (var node in list) {
311 for (var sub in list[node].subscribers) {
312 if (toBareJID(sub) == jid)
313 subs[node] = list[node].subscribers[sub];
314 }
315 }
316 }
317 return subs;
318 };
319
320 storage.getSubscriptionsFromNodeID = function(nodeID) {
321 var node;
322 if (typeof nodeID == 'string') {
323 node = storage.getNode(nodeID);
324 if (typeof node == 'number')
325 return node;
326 } else
327 node = nodeID;
328
329 var subs = {};
330 for (var sub in node.subscribers) {
331 if (typeof sub == 'string') {
332 var subscription = {subid: node.subscribers[sub].subid, type: node.subscribers[sub].type};
333 if (node.subscribers[sub].options)
334 subscription.options = node.subscribers[sub].options;
335 subs[sub] = subscription;
336 }
337 }
338 return subs;
339 };
340
341 storage.subscribe = function(nodeID, jid, type, params) {
342 if (!config.enabled('subscribe'))
343 return errors.sub.subscribe.not_supported.n;
344
345 var node = storage.getNode(nodeID);
346 if (typeof node == 'number')
347 return node;
348
349 if (typeof type == 'undefined')
350 type = 'subscribed';
351
352 if (type == 'none') {
353 if (!node.subscribers[jid])
354 return errors.sub.unsubscribe.no_such_subscriber.n;
355 }
356
357 var subid = node.setSubscriber(jid, type, null, params);
358 storage.save();
359 return {subid: subid, type: type};
360 };
361
362 storage.setAffiliation = function(nodeID, jid, affil) {
363 var node = storage.getNode(nodeID);
364 if (typeof node == 'number')
365 return node;
366
367 if (affil == 'owner')
368 node.owner.push(jid);
369 else if (config.enabled('publisher-affiliation') && affil == 'publisher')
370 node.publisher.push(jid);
371 else if (config.enabled('publish-only-affiliation') && affil == 'publish-only')
372 node.publishOnly.push(jid);
373 else if (config.enabled('member-affiliation') && affil == 'member')
374 node.member.push(jid);
375 else if (config.enabled('outcast-affiliation') && affil == 'outcast')
376 node.outcast.push(jid);
377
378 storage.save();
379 return errors.success;
380 };
381
382 storage.getAffiliation = function(jid, nodeID) {
383 var node;
384 if (typeof nodeID == 'string') {
385 node = storage.getNode(nodeID);
386 if (typeof node == 'number')
387 return node;
388 } else
389 node = nodeID;
390
391 for (var affil in node.owner)
392 if (typeof affil == 'string' && node.owner[affil] == jid)
393 return 'owner';
394 if (config.enabled('publisher-affiliation'))
395 for (var affil in node.publisher)
396 if (typeof affil == 'string' && node.publisher[affil] == jid)
397 return 'publisher';
398 if (config.enabled('publish-only-affiliation'))
399 for (var affil in node.publishOnly)
400 if (typeof affil == 'string' && node.publishOnly[affil] == jid)
401 return 'publish-only';
402 if (config.enabled('outcast-affiliation'))
403 for (var affil in node.outcast)
404 if (typeof affil == 'string' && node.outcast[affil] == jid)
405 return 'outcast';
406 return 'none';
407 };
408
409 storage.getAffiliationsFromJID = function(jid) {
410 var affils = {};
411 for (var node in list) {
412 var n = list[node];
413 for (var affil in n.owner)
414 if (typeof affil == 'string' && n.owner[affil] == jid) {
415 affils[node] = 'owner';
416 break;
417 }
418 if (config.enabled('publisher-affiliation'))
419 for (var affil in n.publisher)
420 if (typeof affil == 'string' && n.publisher[affil] == jid) {
421 affils[node] = 'publisher';
422 break;
423 }
424 if (config.enabled('publish-only-affiliation'))
425 for (var affil in n.publishOnly)
426 if (typeof affil == 'string' && n.publishOnly[affil] == jid) {
427 affils[node] = 'publish-only';
428 break;
429 }
430 if (config.enabled('outcast-affiliation'))
431 for (var affil in n.outcast)
432 if (typeof affil == 'string' && n.outcast[affil] == jid) {
433 affils[node] = 'outcast';
434 break;
435 }
436 }
437 return affils;
438 };
439
440 storage.getAffiliationsFromNodeID = function(nodeID) {
441 var node;
442 if (typeof nodeID == 'string') {
443 node = storage.getNode(nodeID);
444 if (typeof node == 'number')
445 return node;
446 } else
447 node = nodeID;
448
449 var affils = {};
450 for (var jid in node.owner)
451 if (typeof jid == 'string')
452 affils[node.owner[jid]] = 'owner';
453 if (config.enabled('publisher-affiliation'))
454 for (var jid in node.publisher)
455 if (typeof jid == 'string')
456 affils[node.publisher[jid]] = 'publisher';
457 if (config.enabled('publish-only-affiliation'))
458 for (var jid in node.publishOnly)
459 if (typeof jid == 'string')
460 affils[node.publishOnly[jid]] = 'publish-only';
461 if (config.enabled('member-affiliation'))
462 for (var jid in node.member)
463 if (typeof jid == 'string')
464 affils[node.member[jid]] = 'member';
465 if (config.enabled('outcast-affiliation'))
466 for (var jid in node.outcast)
467 if (typeof jid == 'string')
468 affils[node.outcast[jid]] = 'outcast';
469 return affils;
470 };
471
472 storage.save = function(file) {
473 function sanitize(o) {
474 var n = {};
475 for (var i in o) {
476 if (i == 'content' || o[i].setISO8601)
477 n[i] = o[i].toString();
478 else if (o[i] instanceof Array)
479 n[i] = o[i];
480 else if (typeof o[i] == 'object')
481 n[i] = sanitize(o[i]);
482 else if (typeof o[i] == 'function')
483 ;
484 else
485 n[i] = o[i];
486 }
487 return n;
488 }
489
490 var data = sanitize(list);
491
492 if (!file)
493 file = 'save.json';
494
495 fs.writeFile(file, sys.inspect(data, null, null));
496 }
497
498 storage.load = function(file) {
499 var xmpp = require('xmpp');
500 function parseStanza(path, content) {
501 var stanza = null;
502 var stream = new xmpp.Stream({
503 stanza: function (stanza) {
504 path[content] = stanza;
505 }
506 });
507 stream.opened = true;
508 stream.data(path[content]);
509 }
510
511 function endParsing(o) {
512 var regexp = /\d{4}-\d\d-\d\dT\d\d:\d\d:\d\dZ/;
513 for (var i in o) {
514 if (typeof o[i] == 'string' && i == 'content')
515 parseStanza(o, i);
516 else if (typeof o[i] == 'string' && regexp(o[i])) {
517 var today = new Date();
518 today.setISO8601(o[i]);
519 o[i] = today;
520 } else if (typeof o[i] == 'object')
521 endParsing(o[i]);
522 }
523 return o;
524 }
525
526 if (!file)
527 file = 'save.json';
528
529 var data = fs.readFileSync(file);
530 var obj = eval('('+data+')');
531 list = endParsing(obj);
532 }
533
534 storage.debug = function() {
535 sys.puts('\033[1;33m' + sys.inspect(list, null, null) + '\033[0m');
536 };