Mercurial > prosody-modules
comparison mod_archive/mod_archive.lua @ 193:18a0e5c0bb62
mod_archive: Archive Management - Retrieving a List of Collections
| author | shinysky<shinysky1986(AT)gmail.com> |
|---|---|
| date | Mon, 05 Jul 2010 14:27:39 +0800 |
| parents | 5e8ea3733dc6 |
| children | 4d9ed6374a1f |
comparison
equal
deleted
inserted
replaced
| 192:12d6316d9eab | 193:18a0e5c0bb62 |
|---|---|
| 6 -- | 6 -- |
| 7 | 7 |
| 8 local st = require "util.stanza"; | 8 local st = require "util.stanza"; |
| 9 local dm = require "util.datamanager"; | 9 local dm = require "util.datamanager"; |
| 10 local jid = require "util.jid"; | 10 local jid = require "util.jid"; |
| 11 local datetime = require "util.datetime"; | |
| 11 | 12 |
| 12 local PREFS_DIR = "archive_prefs"; | 13 local PREFS_DIR = "archive_prefs"; |
| 13 local ARCHIVE_DIR = "archive"; | 14 local ARCHIVE_DIR = "archive"; |
| 14 | 15 |
| 15 module:add_feature("urn:xmpp:archive"); | 16 module:add_feature("urn:xmpp:archive"); |
| 25 return st.deserialize(dm.load(node, host, PREFS_DIR)); | 26 return st.deserialize(dm.load(node, host, PREFS_DIR)); |
| 26 end | 27 end |
| 27 | 28 |
| 28 local function store_prefs(data, node, host) | 29 local function store_prefs(data, node, host) |
| 29 dm.store(node, host, PREFS_DIR, st.preserialize(data)); | 30 dm.store(node, host, PREFS_DIR, st.preserialize(data)); |
| 31 end | |
| 32 | |
| 33 local function os_time() | |
| 34 -- return tostring(os.time(os.date('!*t'))); | |
| 35 return datetime.datetime(); | |
| 36 end | |
| 37 | |
| 38 local function date_parse(s) | |
| 39 local year, month, day, hour, min, sec = s:match("(....)-?(..)-?(..)T(..):(..):(..)Z"); | |
| 40 return os.time({year=year, month=month, day=day, hour=hour, min=min, sec=sec}); | |
| 30 end | 41 end |
| 31 | 42 |
| 32 local function store_msg(msg, node, host, isfrom) | 43 local function store_msg(msg, node, host, isfrom) |
| 33 local body = msg:child_with_name("body"); | 44 local body = msg:child_with_name("body"); |
| 34 local thread = msg:child_with_name("thread"); | 45 local thread = msg:child_with_name("thread"); |
| 35 local data = dm.list_load(node, host, ARCHIVE_DIR); | 46 local data = dm.list_load(node, host, ARCHIVE_DIR); |
| 36 local tag = (isfrom and "from") or "to"; | 47 local tag = (isfrom and "from") or "to"; |
| 37 if data then | 48 if data then |
| 38 for k, v in ipairs(data) do | 49 for k, v in ipairs(data) do |
| 39 -- <chat with='juliet@capulet.com/chamber' | |
| 40 -- start='1469-07-21T02:56:15Z' | |
| 41 -- thread='damduoeg08' | |
| 42 -- subject='She speaks!' | |
| 43 -- version='1'> | |
| 44 -- <from secs='0'><body>Art thou not Romeo, and a Montague?</body></from> | |
| 45 -- <to secs='11'><body>Neither, fair saint, if either thee dislike.</body></to> | |
| 46 -- <from secs='7'><body>How cam'st thou hither, tell me, and wherefore?</body></from> | |
| 47 -- <note utc='1469-07-21T03:04:35Z'>I think she might fancy me.</note> | |
| 48 -- </chat> | |
| 49 local collection = st.deserialize(v); | 50 local collection = st.deserialize(v); |
| 50 if collection.attr["thread"] == thread:get_text() then | 51 if collection.attr["thread"] == thread:get_text() then |
| 51 -- TODO figure out secs | 52 -- TODO figure out secs |
| 52 collection:tag(tag, {secs='1'}):add_child(body); | 53 collection:tag(tag, {secs='1', utc=os_time()}):add_child(body); |
| 53 local ver = tonumber(collection.attr["version"]) + 1; | 54 local ver = tonumber(collection.attr["version"]) + 1; |
| 54 collection.attr["version"] = tostring(ver); | 55 collection.attr["version"] = tostring(ver); |
| 55 data[k] = collection; | 56 data[k] = collection; |
| 56 dm.list_store(node, host, ARCHIVE_DIR, st.preserialize(data)); | 57 dm.list_store(node, host, ARCHIVE_DIR, st.preserialize(data)); |
| 57 return; | 58 return; |
| 58 end | 59 end |
| 59 end | 60 end |
| 60 end | 61 end |
| 61 -- not found, create new collection | 62 -- not found, create new collection |
| 62 -- TODO figure out start time | 63 local utc = os_time(); |
| 63 local collection = st.stanza('chat', {with = isfrom and msg.attr.to or msg.attr.from, start='2010-06-01T09:56:15Z', thread=thread:get_text(), version='0'}); | 64 local collection = st.stanza('chat', {with = isfrom and msg.attr.to or msg.attr.from, start=utc, thread=thread:get_text(), version='0'}); |
| 64 collection:tag(tag, {secs='0'}):add_child(body); | 65 collection:tag(tag, {secs='0', utc=utc}):add_child(body); |
| 65 dm.list_append(node, host, ARCHIVE_DIR, st.preserialize(collection)); | 66 dm.list_append(node, host, ARCHIVE_DIR, st.preserialize(collection)); |
| 66 end | 67 end |
| 67 | 68 |
| 68 local function save_result(collection) | 69 local function save_result(collection) |
| 69 local save = st.stanza('save', {xmlns='urn:xmpp:archive'}); | 70 local save = st.stanza('save', {xmlns='urn:xmpp:archive'}); |
| 183 end | 184 end |
| 184 return true; | 185 return true; |
| 185 end | 186 end |
| 186 | 187 |
| 187 local function itemremove_handler(event) | 188 local function itemremove_handler(event) |
| 188 -- TODO use 'assert' to check imcoming stanza? | 189 -- TODO use 'assert' to check incoming stanza? |
| 189 -- or use pcall() to catch exceptions? | 190 -- or use pcall() to catch exceptions? |
| 190 local origin, stanza = event.origin, event.stanza; | 191 local origin, stanza = event.origin, event.stanza; |
| 191 if stanza.attr.type ~= "set" then | 192 if stanza.attr.type ~= "set" then |
| 192 return false; | 193 return false; |
| 193 end | 194 end |
| 255 store_prefs(data, node, host); | 256 store_prefs(data, node, host); |
| 256 origin.send(st.reply(stanza)); | 257 origin.send(st.reply(stanza)); |
| 257 return true; | 258 return true; |
| 258 end | 259 end |
| 259 | 260 |
| 260 local function chat_handler(event) | 261 ------------------------------------------------------------ |
| 261 module:log("debug", "-- stanza:\n%s", tostring(event.stanza)); | 262 -- Manual Archiving |
| 262 return true; | 263 ------------------------------------------------------------ |
| 263 end | |
| 264 | |
| 265 local function list_handler(event) | |
| 266 module:log("debug", "-- stanza:\n%s", tostring(event.stanza)); | |
| 267 return true; | |
| 268 end | |
| 269 | |
| 270 local function modified_handler(event) | |
| 271 module:log("debug", "-- stanza:\n%s", tostring(event.stanza)); | |
| 272 return true; | |
| 273 end | |
| 274 | |
| 275 local function remove_handler(event) | |
| 276 module:log("debug", "-- stanza:\n%s", tostring(event.stanza)); | |
| 277 return true; | |
| 278 end | |
| 279 | |
| 280 local function retrieve_handler(event) | |
| 281 module:log("debug", "-- stanza:\n%s", tostring(event.stanza)); | |
| 282 return true; | |
| 283 end | |
| 284 | |
| 285 local function save_handler(event) | 264 local function save_handler(event) |
| 286 local origin, stanza = event.origin, event.stanza; | 265 local origin, stanza = event.origin, event.stanza; |
| 287 if stanza.attr.type ~= "set" then | 266 if stanza.attr.type ~= "set" then |
| 288 return false; | 267 return false; |
| 289 end | 268 end |
| 301 -- TODO check if there're duplicates | 280 -- TODO check if there're duplicates |
| 302 for newchild in elem:children() do | 281 for newchild in elem:children() do |
| 303 if type(newchild) == "table" then | 282 if type(newchild) == "table" then |
| 304 if newchild.name == "from" or newchild.name == "to" then | 283 if newchild.name == "from" or newchild.name == "to" then |
| 305 collection:add_child(newchild); | 284 collection:add_child(newchild); |
| 306 elseif newchild.name == "note" or newchild.name == "previous" or newchild.name == "next" or newchild.name == "x" then | 285 elseif newchild.name == "note" or newchild.name == "previous" |
| 286 or newchild.name == "next" or newchild.name == "x" then | |
| 307 local found = false; | 287 local found = false; |
| 308 for i, c in ipairs(collection) do | 288 for i, c in ipairs(collection) do |
| 309 if c.name == newchild.name then | 289 if c.name == newchild.name then |
| 310 found = true; | 290 found = true; |
| 311 collection[i] = newchild; | 291 collection[i] = newchild; |
| 332 elem.attr["version"] = "0"; | 312 elem.attr["version"] = "0"; |
| 333 origin.send(st.reply(stanza):add_child(save_result(elem))); | 313 origin.send(st.reply(stanza):add_child(save_result(elem))); |
| 334 -- TODO check if elem is valid(?) | 314 -- TODO check if elem is valid(?) |
| 335 dm.list_append(node, host, ARCHIVE_DIR, st.preserialize(elem)); | 315 dm.list_append(node, host, ARCHIVE_DIR, st.preserialize(elem)); |
| 336 -- TODO unsuccessful reply | 316 -- TODO unsuccessful reply |
| 317 return true; | |
| 318 end | |
| 319 | |
| 320 ------------------------------------------------------------ | |
| 321 -- Archive Management | |
| 322 ------------------------------------------------------------ | |
| 323 local function filter_with(with, coll_with) | |
| 324 return not with or coll_with:find(with); | |
| 325 end | |
| 326 | |
| 327 local function filter_start(start, coll_start) | |
| 328 return not start or start <= coll_start; | |
| 329 end | |
| 330 | |
| 331 local function filter_end(endtime, coll_start) | |
| 332 return not endtime or endtime >= coll_start; | |
| 333 end | |
| 334 | |
| 335 local function list_handler(event) | |
| 336 local origin, stanza = event.origin, event.stanza; | |
| 337 local node, host = origin.username, origin.host; | |
| 338 local data = dm.list_load(node, host, ARCHIVE_DIR); | |
| 339 local elem = stanza.tags[1]; | |
| 340 local resset = {} | |
| 341 if data then | |
| 342 for k, v in ipairs(data) do | |
| 343 local collection = st.deserialize(v); | |
| 344 local res = filter_with(elem.attr["with"], collection.attr["with"]); | |
| 345 res = res and filter_start(elem.attr["start"], collection.attr["start"]); | |
| 346 res = res and filter_end(elem.attr["end"], collection.attr["start"]); | |
| 347 if res then | |
| 348 table.insert(resset, collection); | |
| 349 end | |
| 350 end | |
| 351 end | |
| 352 local reply = st.reply(stanza):tag('list', {xmlns='urn:xmpp:archive'}); | |
| 353 if table.getn(resset) > 0 then | |
| 354 local max = tonumber(elem.tags[1].tags[1]:get_text()); | |
| 355 -- Assuming result set is sorted. | |
| 356 for i, c in ipairs(resset) do | |
| 357 if i <= max then | |
| 358 local chat = st.stanza('chat', c.attr); | |
| 359 reply:add_child(chat); | |
| 360 else break; end | |
| 361 end | |
| 362 end | |
| 363 origin.send(reply); | |
| 364 return true; | |
| 365 end | |
| 366 | |
| 367 local function retrieve_handler(event) | |
| 368 module:log("debug", "-- stanza:\n%s", tostring(event.stanza)); | |
| 369 return true; | |
| 370 end | |
| 371 | |
| 372 local function remove_handler(event) | |
| 373 module:log("debug", "-- stanza:\n%s", tostring(event.stanza)); | |
| 374 return true; | |
| 375 end | |
| 376 | |
| 377 ------------------------------------------------------------ | |
| 378 -- Replication | |
| 379 ------------------------------------------------------------ | |
| 380 local function modified_handler(event) | |
| 381 module:log("debug", "-- stanza:\n%s", tostring(event.stanza)); | |
| 337 return true; | 382 return true; |
| 338 end | 383 end |
| 339 | 384 |
| 340 ------------------------------------------------------------ | 385 ------------------------------------------------------------ |
| 341 -- Message Handler | 386 -- Message Handler |
| 365 end | 410 end |
| 366 end | 411 end |
| 367 return nil; | 412 return nil; |
| 368 end | 413 end |
| 369 | 414 |
| 415 -- Preferences | |
| 370 module:hook("iq/self/urn:xmpp:archive:pref", preferences_handler); | 416 module:hook("iq/self/urn:xmpp:archive:pref", preferences_handler); |
| 371 module:hook("iq/self/urn:xmpp:archive:itemremove", itemremove_handler); | 417 module:hook("iq/self/urn:xmpp:archive:itemremove", itemremove_handler); |
| 372 module:hook("iq/self/urn:xmpp:archive:sessionremove", sessionremove_handler); | 418 module:hook("iq/self/urn:xmpp:archive:sessionremove", sessionremove_handler); |
| 373 module:hook("iq/self/urn:xmpp:archive:auto", auto_handler); | 419 module:hook("iq/self/urn:xmpp:archive:auto", auto_handler); |
| 374 -- module:hook("iq/self/urn:xmpp:archive:chat", chat_handler); | 420 -- Manual archiving |
| 421 module:hook("iq/self/urn:xmpp:archive:save", save_handler); | |
| 422 -- Archive management | |
| 375 module:hook("iq/self/urn:xmpp:archive:list", list_handler); | 423 module:hook("iq/self/urn:xmpp:archive:list", list_handler); |
| 424 module:hook("iq/self/urn:xmpp:archive:retrieve", retrieve_handler); | |
| 425 module:hook("iq/self/urn:xmpp:archive:remove", remove_handler); | |
| 426 -- Replication | |
| 376 module:hook("iq/self/urn:xmpp:archive:modified", modified_handler); | 427 module:hook("iq/self/urn:xmpp:archive:modified", modified_handler); |
| 377 module:hook("iq/self/urn:xmpp:archive:remove", remove_handler); | |
| 378 module:hook("iq/self/urn:xmpp:archive:retrieve", retrieve_handler); | |
| 379 module:hook("iq/self/urn:xmpp:archive:save", save_handler); | |
| 380 | 428 |
| 381 module:hook("message/full", msg_handler, 10); | 429 module:hook("message/full", msg_handler, 10); |
| 382 module:hook("message/bare", msg_handler, 10); | 430 module:hook("message/bare", msg_handler, 10); |
| 383 | 431 |
| 432 -- FIXME sort collections | |
| 433 |
