From 76b14ec39b95d3f7b09b4cd3766e87c9fdd7fcd6 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 4 Oct 2013 16:40:27 +0200 Subject: mod_pubsub, util.pubsub: Keep track of the order of items --- util/pubsub.lua | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'util') diff --git a/util/pubsub.lua b/util/pubsub.lua index 0dfd196b..e0d428c0 100644 --- a/util/pubsub.lua +++ b/util/pubsub.lua @@ -258,6 +258,7 @@ function service:publish(node, actor, id, item) end node_obj = self.nodes[node]; end + node_obj.data[#node_obj.data + 1] = id; node_obj.data[id] = item; self.events.fire_event("item-published", { node = node, actor = actor, id = id, item = item }); self.config.broadcaster("items", node, node_obj.subscribers, item); @@ -275,6 +276,12 @@ function service:retract(node, actor, id, retract) return false, "item-not-found"; end node_obj.data[id] = nil; + for i, _id in ipairs(node_obj.data) do + if id == _id then + table.remove(node_obj, i); + break; + end + end if retract then self.config.broadcaster("items", node, node_obj.subscribers, retract); end @@ -309,7 +316,7 @@ function service:get_items(node, actor, id) return false, "item-not-found"; end if id then -- Restrict results to a single specific item - return true, { [id] = node_obj.data[id] }; + return true, { id, [id] = node_obj.data[id] }; else return true, node_obj.data; end -- cgit v1.2.3 From 511f7a76a11199b7cd4d13c85f0703f3e7930e3c Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Wed, 30 Oct 2013 17:30:35 -0400 Subject: util.indexedbheap: A priority queue implementation with a reverse index with no per-entry memory allocation. --- util/indexedbheap.lua | 153 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 util/indexedbheap.lua (limited to 'util') diff --git a/util/indexedbheap.lua b/util/indexedbheap.lua new file mode 100644 index 00000000..3cb03037 --- /dev/null +++ b/util/indexedbheap.lua @@ -0,0 +1,153 @@ + +local setmetatable = setmetatable; +local math_floor = math.floor; +local t_remove = table.remove; + +local function _heap_insert(self, item, sync, item2, index) + local pos = #self + 1; + while true do + local half_pos = math_floor(pos / 2); + if half_pos == 0 or item > self[half_pos] then break; end + self[pos] = self[half_pos]; + sync[pos] = sync[half_pos]; + index[sync[pos]] = pos; + pos = half_pos; + end + self[pos] = item; + sync[pos] = item2; + index[item2] = pos; +end + +local function _percolate_up(self, k, sync, index) + local tmp = self[k]; + local tmp_sync = sync[k]; + while k ~= 1 do + local parent = math_floor(k/2); + if tmp < self[parent] then break; end + self[k] = self[parent]; + sync[k] = sync[parent]; + index[sync[k]] = k; + k = parent; + end + self[k] = tmp; + sync[k] = tmp_sync; + index[tmp_sync] = k; + return k; +end + +local function _percolate_down(self, k, sync, index) + local tmp = self[k]; + local tmp_sync = sync[k]; + local size = #self; + local child = 2*k; + while 2*k <= size do + if child ~= size and self[child] > self[child + 1] then + child = child + 1; + end + if tmp > self[child] then + self[k] = self[child]; + sync[k] = sync[child]; + index[sync[k]] = k; + else + break; + end + + k = child; + child = 2*k; + end + self[k] = tmp; + sync[k] = tmp_sync; + index[tmp_sync] = k; + return k; +end + +local function _heap_pop(self, sync, index) + local size = #self; + if size == 0 then return nil; end + + local result = self[1]; + local result_sync = sync[1]; + index[result_sync] = nil; + if size == 1 then + self[1] = nil; + sync[1] = nil; + return result, result_sync; + end + self[1] = t_remove(self); + sync[1] = t_remove(sync); + index[sync[1]] = 1; + + _percolate_down(self, 1, sync, index); + + return result, result_sync; +end + +local indexed_heap = {}; + +function indexed_heap:insert(item, priority, id) + if id == nil then + id = self.current_id; + self.current_id = id + 1; + end + self.items[id] = item; + _heap_insert(self.priorities, priority, self.ids, id, self.index); + return id; +end +function indexed_heap:pop() + local priority, id = _heap_pop(self.priorities, self.ids, self.index); + if id then + local item = self.items[id]; + self.items[id] = nil; + return priority, item, id; + end +end +function indexed_heap:peek() + return self.priorities[1]; +end +function indexed_heap:reprioritize(id, priority) + local k = self.index[id]; + if k == nil then return; end + self.priorities[k] = priority; + + k = _percolate_up(self.priorities, k, self.ids, self.index); + k = _percolate_down(self.priorities, k, self.ids, self.index); +end +function indexed_heap:remove_index(k) + local size = #self.priorities; + + local result = self.priorities[k]; + local result_sync = self.ids[k]; + local item = self.items[result_sync]; + if result == nil then return; end + self.index[result_sync] = nil; + self.items[result_sync] = nil; + + self.priorities[k] = self.priorities[size]; + self.ids[k] = self.ids[size]; + self.index[self.ids[k]] = k; + t_remove(self.priorities); + t_remove(self.ids); + + k = _percolate_up(self.priorities, k, self.ids, self.index); + k = _percolate_down(self.priorities, k, self.ids, self.index); + + return result, item, result_sync; +end +function indexed_heap:remove(id) + return self:remove_index(self.index[id]); +end + +local mt = { __index = indexed_heap }; + +local _M = { + create = function() + return setmetatable({ + ids = {}; -- heap of ids, sync'd with priorities + items = {}; -- map id->items + priorities = {}; -- heap of priorities + index = {}; -- map of id->index of id in ids + current_id = 1.5 + }, mt); + end +}; +return _M; -- cgit v1.2.3 From 495b904d7210537b53736f6fd2330a26527ce89c Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Wed, 30 Oct 2013 17:44:42 -0400 Subject: util.timer: Updated to use util.indexedbheap to provide a more complete API. Timers can now be stopped or rescheduled. Callbacks are now pcall'd. Adding/removing timers from within timer callbacks works better. Optional parameter can be passed when creating timer which gets passed to callback, eliminating the need for closures in various timer uses. Timers are now much more lightweight. --- util/timer.lua | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) (limited to 'util') diff --git a/util/timer.lua b/util/timer.lua index 0e10e144..d7a9b0bf 100644 --- a/util/timer.lua +++ b/util/timer.lua @@ -6,6 +6,8 @@ -- COPYING file in the source package for more information. -- +local indexedbheap = require "util.indexedbheap"; +local log = require "util.logger".init("timer"); local server = require "net.server"; local math_min = math.min local math_huge = math.huge @@ -78,6 +80,60 @@ else end end -add_task = _add_task; +--add_task = _add_task; + +local h = indexedbheap.create(); +local params = {}; +local next_time = nil; +local _id, _callback, _now, _param; +local function _call() return _callback(_now, _id, _param); end +local function _traceback_handler(err) log("error", "Traceback[timer]: %s", traceback(tostring(err), 2)); end +local function _on_timer(now) + local peek; + while true do + peek = h:peek(); + if peek == nil or peek > now then break; end + local _; + _, _callback, _id = h:pop(); + _now = now; + _param = params[id]; + params[id] = nil; + --item(now, id, _param); -- FIXME pcall + local success, err = xpcall(_call, _traceback_handler); + if success and type(err) == "number" then + h:insert(_callback, err + now, _id); -- re-add + end + end + next_time = peek; + if peek ~= nil then + return peek - now; + end +end +function add_task(delay, callback, param) + local current_time = get_time(); + local event_time = current_time + delay; + + local id = h:insert(callback, event_time); + params[id] = param; + if next_time == nil or event_time < next_time then + next_time = event_time; + _add_task(next_time - current_time, on_timer); + end + return id; +end +function stop(id) + params[id] = nil; + return h:remove(id); +end +function reschedule(id, delay) + local current_time = get_time(); + local event_time = current_time + delay; + h:reprioritize(id, delay); + if next_time == nil or event_time < next_time then + next_time = event_time; + _add_task(next_time - current_time, on_timer); + end + return id; +end return _M; -- cgit v1.2.3 From 053117d38dcf80f594cc75d9b4f9fc35d400d567 Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Wed, 30 Oct 2013 17:51:37 -0400 Subject: util.timer: Fix variable name typo. --- util/timer.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'util') diff --git a/util/timer.lua b/util/timer.lua index d7a9b0bf..99aade15 100644 --- a/util/timer.lua +++ b/util/timer.lua @@ -117,7 +117,7 @@ function add_task(delay, callback, param) params[id] = param; if next_time == nil or event_time < next_time then next_time = event_time; - _add_task(next_time - current_time, on_timer); + _add_task(next_time - current_time, _on_timer); end return id; end @@ -131,7 +131,7 @@ function reschedule(id, delay) h:reprioritize(id, delay); if next_time == nil or event_time < next_time then next_time = event_time; - _add_task(next_time - current_time, on_timer); + _add_task(next_time - current_time, _on_timer); end return id; end -- cgit v1.2.3 From c782ef5a75dea9e56a5b890df5306089260a1dcb Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Wed, 30 Oct 2013 17:56:00 -0400 Subject: util.timer: Fix another variable name typo (thanks again zash). --- util/timer.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'util') diff --git a/util/timer.lua b/util/timer.lua index 99aade15..76deaff1 100644 --- a/util/timer.lua +++ b/util/timer.lua @@ -96,8 +96,8 @@ local function _on_timer(now) local _; _, _callback, _id = h:pop(); _now = now; - _param = params[id]; - params[id] = nil; + _param = params[_id]; + params[_id] = nil; --item(now, id, _param); -- FIXME pcall local success, err = xpcall(_call, _traceback_handler); if success and type(err) == "number" then -- cgit v1.2.3 From 33de95fb8a9a209c1645035fb4488c0e7cbc3f1e Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Wed, 30 Oct 2013 17:58:17 -0400 Subject: util.timer: Import all require upvalues. --- util/timer.lua | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'util') diff --git a/util/timer.lua b/util/timer.lua index 76deaff1..451e27d3 100644 --- a/util/timer.lua +++ b/util/timer.lua @@ -15,6 +15,9 @@ local get_time = require "socket".gettime; local t_insert = table.insert; local pairs = pairs; local type = type; +local debug_traceback = debug.traceback; +local tostring = tostring; +local xpcall = xpcall; local data = {}; local new_data = {}; @@ -87,7 +90,7 @@ local params = {}; local next_time = nil; local _id, _callback, _now, _param; local function _call() return _callback(_now, _id, _param); end -local function _traceback_handler(err) log("error", "Traceback[timer]: %s", traceback(tostring(err), 2)); end +local function _traceback_handler(err) log("error", "Traceback[timer]: %s", debug_traceback(tostring(err), 2)); end local function _on_timer(now) local peek; while true do -- cgit v1.2.3 From c84cd87f944f1bf63db2c29d299c66f915e47957 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Wed, 6 Nov 2013 12:56:18 -0500 Subject: util/timer: Re-set params when timer is rescheduled --- util/timer.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'util') diff --git a/util/timer.lua b/util/timer.lua index 451e27d3..23bd6a37 100644 --- a/util/timer.lua +++ b/util/timer.lua @@ -105,6 +105,7 @@ local function _on_timer(now) local success, err = xpcall(_call, _traceback_handler); if success and type(err) == "number" then h:insert(_callback, err + now, _id); -- re-add + params[_id] = _param; end end next_time = peek; -- cgit v1.2.3 From 9c5384c1f1343bf2b8c60623f264869f4f1a0f07 Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Wed, 23 Apr 2014 11:38:34 -0400 Subject: util.indexedbheap: Fix a possible traceback when removing the last item. --- util/indexedbheap.lua | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) (limited to 'util') diff --git a/util/indexedbheap.lua b/util/indexedbheap.lua index 3cb03037..c60861e8 100644 --- a/util/indexedbheap.lua +++ b/util/indexedbheap.lua @@ -113,23 +113,27 @@ function indexed_heap:reprioritize(id, priority) k = _percolate_down(self.priorities, k, self.ids, self.index); end function indexed_heap:remove_index(k) - local size = #self.priorities; - local result = self.priorities[k]; + if result == nil then return; end + local result_sync = self.ids[k]; local item = self.items[result_sync]; - if result == nil then return; end - self.index[result_sync] = nil; - self.items[result_sync] = nil; + local size = #self.priorities; self.priorities[k] = self.priorities[size]; self.ids[k] = self.ids[size]; self.index[self.ids[k]] = k; + t_remove(self.priorities); t_remove(self.ids); - k = _percolate_up(self.priorities, k, self.ids, self.index); - k = _percolate_down(self.priorities, k, self.ids, self.index); + self.index[result_sync] = nil; + self.items[result_sync] = nil; + + if size > k then + k = _percolate_up(self.priorities, k, self.ids, self.index); + k = _percolate_down(self.priorities, k, self.ids, self.index); + end return result, item, result_sync; end -- cgit v1.2.3 From de5aae8a217c09893ab549a6d39cebbe58172e02 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Wed, 28 May 2014 20:12:13 +0200 Subject: util.vcard: Library for parsing vCards --- util/vcard.lua | 454 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 454 insertions(+) create mode 100644 util/vcard.lua (limited to 'util') diff --git a/util/vcard.lua b/util/vcard.lua new file mode 100644 index 00000000..e3005801 --- /dev/null +++ b/util/vcard.lua @@ -0,0 +1,454 @@ +-- Copyright (C) 2011-2014 Kim Alvefur +-- +-- This project is MIT/X11 licensed. Please see the +-- COPYING file in the source package for more information. +-- + +-- TODO +-- Fix folding. + +local st = require "util.stanza"; +local t_insert, t_concat = table.insert, table.concat; +local type = type; +local next, pairs, ipairs = next, pairs, ipairs; + +local from_text, to_text, from_xep54, to_xep54; + +local line_sep = "\n"; + +local vCard_dtd; -- See end of file + +local function fold_line() + error "Not implemented" --TODO +end +local function unfold_line() + error "Not implemented" + -- gsub("\r?\n[ \t]([^\r\n])", "%1"); +end + +local function vCard_esc(s) + return s:gsub("[,:;\\]", "\\%1"):gsub("\n","\\n"); +end + +local function vCard_unesc(s) + return s:gsub("\\?[\\nt:;,]", { + ["\\\\"] = "\\", + ["\\n"] = "\n", + ["\\r"] = "\r", + ["\\t"] = "\t", + ["\\:"] = ":", -- FIXME Shouldn't need to espace : in values, just params + ["\\;"] = ";", + ["\\,"] = ",", + [":"] = "\29", + [";"] = "\30", + [","] = "\31", + }); +end + +local function item_to_xep54(item) + local t = st.stanza(item.name, { xmlns = "vcard-temp" }); + + local prop_def = vCard_dtd[item.name]; + if prop_def == "text" then + t:text(item[1]); + elseif type(prop_def) == "table" then + if prop_def.types and item.TYPE then + if type(item.TYPE) == "table" then + for _,v in pairs(prop_def.types) do + for _,typ in pairs(item.TYPE) do + if typ:upper() == v then + t:tag(v):up(); + break; + end + end + end + else + t:tag(item.TYPE:upper()):up(); + end + end + + if prop_def.props then + for _,v in pairs(prop_def.props) do + if item[v] then + t:tag(v):up(); + end + end + end + + if prop_def.value then + t:tag(prop_def.value):text(item[1]):up(); + elseif prop_def.values then + local prop_def_values = prop_def.values; + local repeat_last = prop_def_values.behaviour == "repeat-last" and prop_def_values[#prop_def_values]; + for i=1,#item do + t:tag(prop_def.values[i] or repeat_last):text(item[i]):up(); + end + end + end + + return t; +end + +local function vcard_to_xep54(vCard) + local t = st.stanza("vCard", { xmlns = "vcard-temp" }); + for i=1,#vCard do + t:add_child(item_to_xep54(vCard[i])); + end + return t; +end + +function to_xep54(vCards) + if not vCards[1] or vCards[1].name then + return vcard_to_xep54(vCards) + else + local t = st.stanza("xCard", { xmlns = "vcard-temp" }); + for i=1,#vCards do + t:add_child(vcard_to_xep54(vCards[i])); + end + return t; + end +end + +function from_text(data) + data = data -- unfold and remove empty lines + :gsub("\r\n","\n") + :gsub("\n ", "") + :gsub("\n\n+","\n"); + local vCards = {}; + local c; -- current item + for line in data:gmatch("[^\n]+") do + local line = vCard_unesc(line); + local name, params, value = line:match("^([-%a]+)(\30?[^\29]*)\29(.*)$"); + value = value:gsub("\29",":"); + if #params > 0 then + local _params = {}; + for k,isval,v in params:gmatch("\30([^=]+)(=?)([^\30]*)") do + k = k:upper(); + local _vt = {}; + for _p in v:gmatch("[^\31]+") do + _vt[#_vt+1]=_p + _vt[_p]=true; + end + if isval == "=" then + _params[k]=_vt; + else + _params[k]=true; + end + end + params = _params; + end + if name == "BEGIN" and value == "VCARD" then + c = {}; + vCards[#vCards+1] = c; + elseif name == "END" and value == "VCARD" then + c = nil; + elseif c and vCard_dtd[name] then + local dtd = vCard_dtd[name]; + local p = { name = name }; + c[#c+1]=p; + --c[name]=p; + local up = c; + c = p; + if dtd.types then + for _, t in ipairs(dtd.types) do + local t = t:lower(); + if ( params.TYPE and params.TYPE[t] == true) + or params[t] == true then + c.TYPE=t; + end + end + end + if dtd.props then + for _, p in ipairs(dtd.props) do + if params[p] then + if params[p] == true then + c[p]=true; + else + for _, prop in ipairs(params[p]) do + c[p]=prop; + end + end + end + end + end + if dtd == "text" or dtd.value then + t_insert(c, value); + elseif dtd.values then + local value = "\30"..value; + for p in value:gmatch("\30([^\30]*)") do + t_insert(c, p); + end + end + c = up; + end + end + return vCards; +end + +local function item_to_text(item) + local value = {}; + for i=1,#item do + value[i] = vCard_esc(item[i]); + end + value = t_concat(value, ";"); + + local params = ""; + for k,v in pairs(item) do + if type(k) == "string" and k ~= "name" then + params = params .. (";%s=%s"):format(k, type(v) == "table" and t_concat(v,",") or v); + end + end + + return ("%s%s:%s"):format(item.name, params, value) +end + +local function vcard_to_text(vcard) + local t={}; + t_insert(t, "BEGIN:VCARD") + for i=1,#vcard do + t_insert(t, item_to_text(vcard[i])); + end + t_insert(t, "END:VCARD") + return t_concat(t, line_sep); +end + +function to_text(vCards) + if vCards[1] and vCards[1].name then + return vcard_to_text(vCards) + else + local t = {}; + for i=1,#vCards do + t[i]=vcard_to_text(vCards[i]); + end + return t_concat(t, line_sep); + end +end + +local function from_xep54_item(item) + local prop_name = item.name; + local prop_def = vCard_dtd[prop_name]; + + local prop = { name = prop_name }; + + if prop_def == "text" then + prop[1] = item:get_text(); + elseif type(prop_def) == "table" then + if prop_def.value then --single item + prop[1] = item:get_child_text(prop_def.value) or ""; + elseif prop_def.values then --array + local value_names = prop_def.values; + if value_names.behaviour == "repeat-last" then + for i=1,#item.tags do + t_insert(prop, item.tags[i]:get_text() or ""); + end + else + for i=1,#value_names do + t_insert(prop, item:get_child_text(value_names[i]) or ""); + end + end + elseif prop_def.names then + local names = prop_def.names; + for i=1,#names do + if item:get_child(names[i]) then + prop[1] = names[i]; + break; + end + end + end + + if prop_def.props_verbatim then + for k,v in pairs(prop_def.props_verbatim) do + prop[k] = v; + end + end + + if prop_def.types then + local types = prop_def.types; + prop.TYPE = {}; + for i=1,#types do + if item:get_child(types[i]) then + t_insert(prop.TYPE, types[i]:lower()); + end + end + if #prop.TYPE == 0 then + prop.TYPE = nil; + end + end + + -- A key-value pair, within a key-value pair? + if prop_def.props then + local params = prop_def.props; + for i=1,#params do + local name = params[i] + local data = item:get_child_text(name); + if data then + prop[name] = prop[name] or {}; + t_insert(prop[name], data); + end + end + end + else + return nil + end + + return prop; +end + +local function from_xep54_vCard(vCard) + local tags = vCard.tags; + local t = {}; + for i=1,#tags do + t_insert(t, from_xep54_item(tags[i])); + end + return t +end + +function from_xep54(vCard) + if vCard.attr.xmlns ~= "vcard-temp" then + return nil, "wrong-xmlns"; + end + if vCard.name == "xCard" then -- A collection of vCards + local t = {}; + local vCards = vCard.tags; + for i=1,#vCards do + t[i] = from_xep54_vCard(vCards[i]); + end + return t + elseif vCard.name == "vCard" then -- A single vCard + return from_xep54_vCard(vCard) + end +end + +-- This was adapted from http://xmpp.org/extensions/xep-0054.html#dtd +vCard_dtd = { + VERSION = "text", --MUST be 3.0, so parsing is redundant + FN = "text", + N = { + values = { + "FAMILY", + "GIVEN", + "MIDDLE", + "PREFIX", + "SUFFIX", + }, + }, + NICKNAME = "text", + PHOTO = { + props_verbatim = { ENCODING = { "b" } }, + props = { "TYPE" }, + value = "BINVAL", --{ "EXTVAL", }, + }, + BDAY = "text", + ADR = { + types = { + "HOME", + "WORK", + "POSTAL", + "PARCEL", + "DOM", + "INTL", + "PREF", + }, + values = { + "POBOX", + "EXTADD", + "STREET", + "LOCALITY", + "REGION", + "PCODE", + "CTRY", + } + }, + LABEL = { + types = { + "HOME", + "WORK", + "POSTAL", + "PARCEL", + "DOM", + "INTL", + "PREF", + }, + value = "LINE", + }, + TEL = { + types = { + "HOME", + "WORK", + "VOICE", + "FAX", + "PAGER", + "MSG", + "CELL", + "VIDEO", + "BBS", + "MODEM", + "ISDN", + "PCS", + "PREF", + }, + value = "NUMBER", + }, + EMAIL = { + types = { + "HOME", + "WORK", + "INTERNET", + "PREF", + "X400", + }, + value = "USERID", + }, + JABBERID = "text", + MAILER = "text", + TZ = "text", + GEO = { + values = { + "LAT", + "LON", + }, + }, + TITLE = "text", + ROLE = "text", + LOGO = "copy of PHOTO", + AGENT = "text", + ORG = { + values = { + behaviour = "repeat-last", + "ORGNAME", + "ORGUNIT", + } + }, + CATEGORIES = { + values = "KEYWORD", + }, + NOTE = "text", + PRODID = "text", + REV = "text", + SORTSTRING = "text", + SOUND = "copy of PHOTO", + UID = "text", + URL = "text", + CLASS = { + names = { -- The item.name is the value if it's one of these. + "PUBLIC", + "PRIVATE", + "CONFIDENTIAL", + }, + }, + KEY = { + props = { "TYPE" }, + value = "CRED", + }, + DESC = "text", +}; +vCard_dtd.LOGO = vCard_dtd.PHOTO; +vCard_dtd.SOUND = vCard_dtd.PHOTO; + +return { + from_text = from_text; + to_text = to_text; + + from_xep54 = from_xep54; + to_xep54 = to_xep54; +}; -- cgit v1.2.3 From e6e3829b31622b39e98271e937d4cb433f6ec911 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Wed, 28 May 2014 21:11:02 +0200 Subject: util.vcard: Add support for converting to vcard4 --- util/vcard.lua | 121 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) (limited to 'util') diff --git a/util/vcard.lua b/util/vcard.lua index e3005801..70c923b8 100644 --- a/util/vcard.lua +++ b/util/vcard.lua @@ -319,6 +319,68 @@ function from_xep54(vCard) end end +local vcard4 = { } + +function vcard4:text(node, params, value) + self:tag(node:lower()) + -- FIXME params + if type(value) == "string" then + self:tag("text"):text(value):up() + elseif vcard4[node] then + vcard4[node](value); + end + self:up(); +end + +function vcard4.N(value) + for i, k in ipairs(vCard_dtd.N.values) do + value:tag(k):text(value[i]):up(); + end +end + +local xmlns_vcard4 = "urn:ietf:params:xml:ns:vcard-4.0" + +local function item_to_vcard4(item) + local typ = item.name:lower(); + local t = st.stanza(typ, { xmlns = xmlns_vcard4 }); + + local prop_def = vCard4_dtd[typ]; + if prop_def == "text" then + t:tag("text"):text(item[1]):up(); + elseif type(prop_def) == "table" then + if prop_def.values then + for i, v in ipairs(prop_def.values) do + t:tag(v:lower()):text(item[i] or ""):up(); + end + else + t:tag("unsupported",{xmlns="http://zash.se/protocol/vcardlib"}) + end + else + t:tag("unsupported",{xmlns="http://zash.se/protocol/vcardlib"}) + end + return t; +end + +local function vcard_to_vcard4xml(vCard) + local t = st.stanza("vcard", { xmlns = xmlns_vcard4 }); + for i=1,#vCard do + t:add_child(item_to_vcard4(vCard[i])); + end + return t; +end + +local function vcards_to_vcard4xml(vCards) + if not vCards[1] or vCards[1].name then + return vcard_to_vcard4xml(vCards) + else + local t = st.stanza("vcards", { xmlns = xmlns_vcard4 }); + for i=1,#vCards do + t:add_child(vcard_to_vcard4xml(vCards[i])); + end + return t; + end +end + -- This was adapted from http://xmpp.org/extensions/xep-0054.html#dtd vCard_dtd = { VERSION = "text", --MUST be 3.0, so parsing is redundant @@ -445,10 +507,69 @@ vCard_dtd = { vCard_dtd.LOGO = vCard_dtd.PHOTO; vCard_dtd.SOUND = vCard_dtd.PHOTO; +vCard4_dtd = { + source = "uri", + kind = "text", + xml = "text", + fn = "text", + n = { + values = { + "family", + "given", + "middle", + "prefix", + "suffix", + }, + }, + nickname = "text", + photo = "uri", + bday = "date-and-or-time", + anniversary = "date-and-or-time", + gender = "text", + adr = { + values = { + "pobox", + "ext", + "street", + "locality", + "region", + "code", + "country", + } + }, + tel = "text", + email = "text", + impp = "uri", + lang = "language-tag", + tz = "text", + geo = "uri", + title = "text", + role = "text", + logo = "uri", + org = "text", + member = "uri", + related = "uri", + categories = "text", + note = "text", + prodid = "text", + rev = "timestamp", + sound = "uri", + uid = "uri", + clientpidmap = "number, uuid", + url = "uri", + version = "text", + key = "uri", + fburl = "uri", + caladruri = "uri", + caluri = "uri", +}; + return { from_text = from_text; to_text = to_text; from_xep54 = from_xep54; to_xep54 = to_xep54; + + to_vcard4 = vcards_to_vcard4xml; }; -- cgit v1.2.3 From ed9c56bd85bb93b747d123b8ba87e4258ba31080 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Wed, 28 May 2014 22:09:32 +0200 Subject: util.vcard: Add missing local declaration --- util/vcard.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'util') diff --git a/util/vcard.lua b/util/vcard.lua index 70c923b8..29a40844 100644 --- a/util/vcard.lua +++ b/util/vcard.lua @@ -17,6 +17,7 @@ local from_text, to_text, from_xep54, to_xep54; local line_sep = "\n"; local vCard_dtd; -- See end of file +local vCard4_dtd; local function fold_line() error "Not implemented" --TODO -- cgit v1.2.3 From bb40012ca2d5a7eb38d86f4cef5488b31f6da0d0 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Wed, 9 Jul 2014 08:23:16 +0200 Subject: util.vcard: Add support for uri types in vcard4 --- util/vcard.lua | 2 ++ 1 file changed, 2 insertions(+) (limited to 'util') diff --git a/util/vcard.lua b/util/vcard.lua index 29a40844..152b0b2d 100644 --- a/util/vcard.lua +++ b/util/vcard.lua @@ -348,6 +348,8 @@ local function item_to_vcard4(item) local prop_def = vCard4_dtd[typ]; if prop_def == "text" then t:tag("text"):text(item[1]):up(); + elseif prop_def == "uri" then + t:tag("uri"):text(item[1]):up(); elseif type(prop_def) == "table" then if prop_def.values then for i, v in ipairs(prop_def.values) do -- cgit v1.2.3 From 15303b0d3a84572090e81dc7c737b575184135b6 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Wed, 10 Sep 2014 16:47:55 +0200 Subject: util.vcard: Turn PHOTO fields into data-uris --- util/vcard.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'util') diff --git a/util/vcard.lua b/util/vcard.lua index 152b0b2d..8aafa24d 100644 --- a/util/vcard.lua +++ b/util/vcard.lua @@ -349,7 +349,11 @@ local function item_to_vcard4(item) if prop_def == "text" then t:tag("text"):text(item[1]):up(); elseif prop_def == "uri" then - t:tag("uri"):text(item[1]):up(); + if item.ENCODING and item.ENCODING[1] == 'b' then + t:tag("uri"):text("data:;base64,"):text(item[1]):up(); + else + t:tag("uri"):text(item[1]):up(); + end elseif type(prop_def) == "table" then if prop_def.values then for i, v in ipairs(prop_def.values) do -- cgit v1.2.3