Software /
code /
prosody-modules
Comparison
mod_vjud/vcard.lib.lua @ 716:dac33b8f190b
mod_vjud: Depends on vcard lib from verse, so add that.
author | Kim Alvefur <zash@zash.se> |
---|---|
date | Wed, 20 Jun 2012 15:18:48 +0200 |
child | 732:317e142fe6f1 |
comparison
equal
deleted
inserted
replaced
715:b1268d3aa6ce | 716:dac33b8f190b |
---|---|
1 -- Copyright (C) 2011-2012 Kim Alvefur | |
2 -- | |
3 -- This project is MIT/X11 licensed. Please see the | |
4 -- COPYING file in the source package for more information. | |
5 -- | |
6 | |
7 -- TODO | |
8 -- function lua_to_xep54() | |
9 -- function lua_to_text() | |
10 -- replace text_to_xep54() and xep54_to_text() with intermediate lua? | |
11 | |
12 local st = require "util.stanza"; | |
13 local t_insert, t_concat = table.insert, table.concat; | |
14 local type = type; | |
15 local next, pairs, ipairs = next, pairs, ipairs; | |
16 | |
17 local lua_to_text, lua_to_xep54, text_to_lua, text_to_xep54, xep54_to_lua, xep54_to_text; | |
18 local from_text, to_text, from_xep54, to_xep54; --TODO implement these, replace the above | |
19 | |
20 | |
21 local vCard_dtd; | |
22 | |
23 local function vCard_esc(s) | |
24 return s:gsub("[,:;\\]", "\\%1"):gsub("\n","\\n"); | |
25 end | |
26 | |
27 local function vCard_unesc(s) | |
28 return s:gsub("\\?[\\nt:;,]", { | |
29 ["\\\\"] = "\\", | |
30 ["\\n"] = "\n", | |
31 ["\\t"] = "\t", | |
32 ["\\:"] = ":", -- FIXME Shouldn't need to espace : in values, just params | |
33 ["\\;"] = ";", | |
34 ["\\,"] = ",", | |
35 [":"] = "\29", | |
36 [";"] = "\30", | |
37 [","] = "\31", | |
38 }); | |
39 end | |
40 | |
41 function text_to_xep54(data) | |
42 --[[ TODO | |
43 return lua_to_xep54(text_to_lua(data)); | |
44 --]] | |
45 data = data | |
46 :gsub("\r\n","\n") | |
47 :gsub("\n ", "") | |
48 :gsub("\n\n+","\n"); | |
49 local c = st.stanza("xCard", { xmlns = "vcard-temp" }); | |
50 for line in data:gmatch("[^\n]+") do | |
51 local line = vCard_unesc(line); | |
52 local name, params, value = line:match("^([-%a]+)(\30?[^\29]*)\29(.*)$"); | |
53 value = value:gsub("\29",":"); | |
54 if #params > 0 then | |
55 local _params = {}; | |
56 for k,isval,v in params:gmatch("\30([^=]+)(=?)([^\30]*)") do | |
57 k = k:upper(); | |
58 local _vt = {}; | |
59 for _p in v:gmatch("[^\31]+") do | |
60 _vt[#_vt+1]=_p | |
61 _vt[_p]=true; | |
62 end | |
63 if isval == "=" then | |
64 _params[k]=_vt; | |
65 else | |
66 _params[k]=true; | |
67 end | |
68 end | |
69 params = _params; | |
70 end | |
71 if name == "BEGIN" and value == "VCARD" then | |
72 c:tag("vCard", { xmlns = "vcard-temp" }); | |
73 elseif name == "END" and value == "VCARD" then | |
74 c:up(); | |
75 elseif vCard_dtd[name] then | |
76 local dtd = vCard_dtd[name]; | |
77 c:tag(name); | |
78 if dtd.types then | |
79 for _, t in ipairs(dtd.types) do | |
80 if ( params.TYPE and params.TYPE[t] == true) | |
81 or params[t] == true then | |
82 c:tag(t):up(); | |
83 end | |
84 end | |
85 end | |
86 if dtd.props then | |
87 for _, p in ipairs(dtd.props) do | |
88 if params[p] then | |
89 if params[p] == true then | |
90 c:tag(p):up(); | |
91 else | |
92 for _, prop in ipairs(params[p]) do | |
93 c:tag(p):text(prop):up(); | |
94 end | |
95 end | |
96 end | |
97 end | |
98 end | |
99 if dtd == "text" then | |
100 c:text(value); | |
101 elseif dtd.value then | |
102 c:tag(dtd.value):text(value):up(); | |
103 elseif dtd.values then | |
104 local values = dtd.values; | |
105 local i = 1; | |
106 local value = "\30"..value; | |
107 for p in value:gmatch("\30([^\30]*)") do | |
108 c:tag(values[i]):text(p):up(); | |
109 if i < #values then | |
110 i = i + 1; | |
111 end | |
112 end | |
113 end | |
114 c:up(); | |
115 end | |
116 end | |
117 return c; | |
118 end | |
119 | |
120 function text_to_lua(data) --table | |
121 data = data | |
122 :gsub("\r\n","\n") | |
123 :gsub("\n ", "") | |
124 :gsub("\n\n+","\n"); | |
125 local vCards = {}; | |
126 local c; -- current item | |
127 for line in data:gmatch("[^\n]+") do | |
128 local line = vCard_unesc(line); | |
129 local name, params, value = line:match("^([-%a]+)(\30?[^\29]*)\29(.*)$"); | |
130 value = value:gsub("\29",":"); | |
131 if #params > 0 then | |
132 local _params = {}; | |
133 for k,isval,v in params:gmatch("\30([^=]+)(=?)([^\30]*)") do | |
134 k = k:upper(); | |
135 local _vt = {}; | |
136 for _p in v:gmatch("[^\31]+") do | |
137 _vt[#_vt+1]=_p | |
138 _vt[_p]=true; | |
139 end | |
140 if isval == "=" then | |
141 _params[k]=_vt; | |
142 else | |
143 _params[k]=true; | |
144 end | |
145 end | |
146 params = _params; | |
147 end | |
148 if name == "BEGIN" and value == "VCARD" then | |
149 c = {}; | |
150 vCards[#vCards+1] = c; | |
151 elseif name == "END" and value == "VCARD" then | |
152 c = nil; | |
153 elseif vCard_dtd[name] then | |
154 local dtd = vCard_dtd[name]; | |
155 local p = { name = name }; | |
156 c[#c+1]=p; | |
157 --c[name]=p; | |
158 local up = c; | |
159 c = p; | |
160 if dtd.types then | |
161 for _, t in ipairs(dtd.types) do | |
162 local t = t:lower(); | |
163 if ( params.TYPE and params.TYPE[t] == true) | |
164 or params[t] == true then | |
165 c.TYPE=t; | |
166 end | |
167 end | |
168 end | |
169 if dtd.props then | |
170 for _, p in ipairs(dtd.props) do | |
171 if params[p] then | |
172 if params[p] == true then | |
173 c[p]=true; | |
174 else | |
175 for _, prop in ipairs(params[p]) do | |
176 c[p]=prop; | |
177 end | |
178 end | |
179 end | |
180 end | |
181 end | |
182 if dtd == "text" or dtd.value then | |
183 t_insert(c, value); | |
184 elseif dtd.values then | |
185 local value = "\30"..value; | |
186 for p in value:gmatch("\30([^\30]*)") do | |
187 t_insert(c, p); | |
188 end | |
189 end | |
190 c = up; | |
191 end | |
192 end | |
193 return vCards; | |
194 end | |
195 | |
196 function to_text(vcard) | |
197 local t={}; | |
198 t_insert(t, "BEGIN:VCARD") | |
199 for i=1,#vcard do | |
200 t_insert(t, ("%s:%s"):format(vcard[i].name, t_concat(vcard[i], ";"))); | |
201 end | |
202 t_insert(t, "END:VCARD") | |
203 return t_concat(t,"\n"); | |
204 end | |
205 | |
206 local function vCard_prop(item) -- single item staza object to text line | |
207 local prop_name = item.name; | |
208 local prop_def = vCard_dtd[prop_name]; | |
209 if not prop_def then return nil end | |
210 | |
211 local value, params = "", {}; | |
212 | |
213 if prop_def == "text" then | |
214 value = item:get_text(); | |
215 elseif type(prop_def) == "table" then | |
216 if prop_def.value then --single item | |
217 value = item:get_child_text(prop_def.value) or ""; | |
218 elseif prop_def.values then --array | |
219 local value_names = prop_def.values; | |
220 value = {}; | |
221 if value_names.behaviour == "repeat-last" then | |
222 for i=1,#item do | |
223 t_insert(value, item[i]:get_text() or ""); | |
224 end | |
225 else | |
226 for i=1,#value_names do | |
227 t_insert(value, item:get_child_text(value_names[i]) or ""); | |
228 end | |
229 end | |
230 elseif prop_def.names then | |
231 local names = prop_def.names; | |
232 for i=1,#names do | |
233 if item:get_child(names[i]) then | |
234 value = names[i]; | |
235 break; | |
236 end | |
237 end | |
238 end | |
239 | |
240 if prop_def.props_verbatim then | |
241 for k,v in pairs(prop_def.props_verbatim) do | |
242 params[k] = v; | |
243 end | |
244 end | |
245 | |
246 if prop_def.types then | |
247 local types = prop_def.types; | |
248 params.TYPE = {}; | |
249 for i=1,#types do | |
250 if item:get_child(types[i]) then | |
251 t_insert(params.TYPE, types[i]:lower()); | |
252 end | |
253 end | |
254 if #params.TYPE == 0 then | |
255 params.TYPE = nil; | |
256 end | |
257 end | |
258 | |
259 if prop_def.props then | |
260 local props = prop_def.props; | |
261 for i=1,#props do | |
262 local prop = props[i] | |
263 local p = item:get_child_text(prop); | |
264 if p then | |
265 params[prop] = params[prop] or {}; | |
266 t_insert(params[prop], p); | |
267 end | |
268 end | |
269 end | |
270 else | |
271 return nil | |
272 end | |
273 | |
274 if type(value) == "table" then | |
275 for i=1,#value do | |
276 value[i]=vCard_esc(value[i]); | |
277 end | |
278 value = t_concat(value, ";"); | |
279 else | |
280 value = vCard_esc(value); | |
281 end | |
282 | |
283 if next(params) then | |
284 local sparams = ""; | |
285 for k,v in pairs(params) do | |
286 sparams = sparams .. (";%s=%s"):format(k, t_concat(v,",")); | |
287 end | |
288 params = sparams; | |
289 else | |
290 params = ""; | |
291 end | |
292 | |
293 return ("%s%s:%s"):format(item.name, params, value) | |
294 :gsub(("."):rep(75), "%0\r\n "):gsub("\r\n $",""); | |
295 end | |
296 | |
297 function xep54_to_text(vCard) | |
298 --[[ TODO | |
299 return lua_to_text(xep54_to_lua(vCard)) | |
300 --]] | |
301 local r = {}; | |
302 t_insert(r, "BEGIN:VCARD"); | |
303 for i = 1,#vCard do | |
304 local item = vCard[i]; | |
305 if item.name then | |
306 local s = vCard_prop(item); | |
307 if s then | |
308 t_insert(r, s); | |
309 end | |
310 end | |
311 end | |
312 t_insert(r, "END:VCARD"); | |
313 return t_concat(r, "\r\n"); | |
314 end | |
315 | |
316 local function xep54_item_to_lua(item) | |
317 local prop_name = item.name; | |
318 local prop_def = vCard_dtd[prop_name]; | |
319 if not prop_def then return nil end | |
320 | |
321 local prop = { name = prop_name }; | |
322 | |
323 if prop_def == "text" then | |
324 prop[1] = item:get_text(); | |
325 elseif type(prop_def) == "table" then | |
326 if prop_def.value then --single item | |
327 prop[1] = item:get_child_text(prop_def.value) or ""; | |
328 elseif prop_def.values then --array | |
329 local value_names = prop_def.values; | |
330 if value_names.behaviour == "repeat-last" then | |
331 for i=1,#item do | |
332 t_insert(prop, item[i]:get_text() or ""); | |
333 end | |
334 else | |
335 for i=1,#value_names do | |
336 t_insert(prop, item:get_child_text(value_names[i]) or ""); | |
337 end | |
338 end | |
339 elseif prop_def.names then | |
340 local names = prop_def.names; | |
341 for i=1,#names do | |
342 if item:get_child(names[i]) then | |
343 prop[1] = names[i]; | |
344 break; | |
345 end | |
346 end | |
347 end | |
348 | |
349 if prop_def.props_verbatim then | |
350 for k,v in pairs(prop_def.props_verbatim) do | |
351 prop[k] = v; | |
352 end | |
353 end | |
354 | |
355 if prop_def.types then | |
356 local types = prop_def.types; | |
357 prop.TYPE = {}; | |
358 for i=1,#types do | |
359 if item:get_child(types[i]) then | |
360 t_insert(prop.TYPE, types[i]:lower()); | |
361 end | |
362 end | |
363 if #prop.TYPE == 0 then | |
364 prop.TYPE = nil; | |
365 end | |
366 end | |
367 | |
368 -- A key-value pair, within a key-value pair? | |
369 if prop_def.props then | |
370 local params = prop_def.props; | |
371 for i=1,#params do | |
372 local name = params[i] | |
373 local data = item:get_child_text(name); | |
374 if data then | |
375 prop[name] = prop[name] or {}; | |
376 t_insert(prop[name], data); | |
377 end | |
378 end | |
379 end | |
380 else | |
381 return nil | |
382 end | |
383 | |
384 return prop; | |
385 end | |
386 | |
387 local function xep54_vCard_to_lua(vCard) | |
388 local tags = vCard.tags; | |
389 local t = {}; | |
390 for i=1,#tags do | |
391 t[i] = xep54_item_to_lua(tags[i]); | |
392 end | |
393 return t | |
394 end | |
395 | |
396 function xep54_to_lua(vCard) | |
397 if vCard.attr.xmlns ~= "vcard-temp" then | |
398 return false | |
399 end | |
400 if vCard.name == "xCard" then | |
401 local t = {}; | |
402 local vCards = vCard.tags; | |
403 for i=1,#vCards do | |
404 local ti = xep54_vCard_to_lua(vCards[i]); | |
405 t[i] = ti; | |
406 --t[ti.name] = ti; | |
407 end | |
408 return t | |
409 elseif vCard.name == "vCard" then | |
410 return xep54_vCard_to_lua(vCard) | |
411 end | |
412 end | |
413 | |
414 -- This was adapted from http://xmpp.org/extensions/xep-0054.html#dtd | |
415 vCard_dtd = { | |
416 VERSION = "text", --MUST be 3.0, so parsing is redundant | |
417 FN = "text", | |
418 N = { | |
419 values = { | |
420 "FAMILY", | |
421 "GIVEN", | |
422 "MIDDLE", | |
423 "PREFIX", | |
424 "SUFFIX", | |
425 }, | |
426 }, | |
427 NICKNAME = "text", | |
428 PHOTO = { | |
429 props_verbatim = { ENCODING = { "b" } }, | |
430 props = { "TYPE" }, | |
431 value = "BINVAL", --{ "EXTVAL", }, | |
432 }, | |
433 BDAY = "text", | |
434 ADR = { | |
435 types = { | |
436 "HOME", | |
437 "WORK", | |
438 "POSTAL", | |
439 "PARCEL", | |
440 "DOM", | |
441 "INTL", | |
442 "PREF", | |
443 }, | |
444 values = { | |
445 "POBOX", | |
446 "EXTADD", | |
447 "STREET", | |
448 "LOCALITY", | |
449 "REGION", | |
450 "PCODE", | |
451 "CTRY", | |
452 } | |
453 }, | |
454 LABEL = { | |
455 types = { | |
456 "HOME", | |
457 "WORK", | |
458 "POSTAL", | |
459 "PARCEL", | |
460 "DOM", | |
461 "INTL", | |
462 "PREF", | |
463 }, | |
464 value = "LINE", | |
465 }, | |
466 TEL = { | |
467 types = { | |
468 "HOME", | |
469 "WORK", | |
470 "VOICE", | |
471 "FAX", | |
472 "PAGER", | |
473 "MSG", | |
474 "CELL", | |
475 "VIDEO", | |
476 "BBS", | |
477 "MODEM", | |
478 "ISDN", | |
479 "PCS", | |
480 "PREF", | |
481 }, | |
482 value = "NUMBER", | |
483 }, | |
484 EMAIL = { | |
485 types = { | |
486 "HOME", | |
487 "WORK", | |
488 "INTERNET", | |
489 "PREF", | |
490 "X400", | |
491 }, | |
492 value = "USERID", | |
493 }, | |
494 JABBERID = "text", | |
495 MAILER = "text", | |
496 TZ = "text", | |
497 GEO = { | |
498 values = { | |
499 "LAT", | |
500 "LON", | |
501 }, | |
502 }, | |
503 TITLE = "text", | |
504 ROLE = "text", | |
505 LOGO = "copy of PHOTO", | |
506 AGENT = "text", | |
507 ORG = { | |
508 values = { | |
509 behaviour = "repeat-last", | |
510 "ORGNAME", | |
511 "ORGUNIT", | |
512 } | |
513 }, | |
514 CATEGORIES = { | |
515 values = "KEYWORD", | |
516 }, | |
517 NOTE = "text", | |
518 PRODID = "text", | |
519 REV = "text", | |
520 SORTSTRING = "text", | |
521 SOUND = "copy of PHOTO", | |
522 UID = "text", | |
523 URL = "text", | |
524 CLASS = { | |
525 names = { -- The item.name is the value if it's one of these. | |
526 "PUBLIC", | |
527 "PRIVATE", | |
528 "CONFIDENTIAL", | |
529 }, | |
530 }, | |
531 KEY = { | |
532 props = { "TYPE" }, | |
533 value = "CRED", | |
534 }, | |
535 DESC = "text", | |
536 }; | |
537 vCard_dtd.LOGO = vCard_dtd.PHOTO; | |
538 vCard_dtd.SOUND = vCard_dtd.PHOTO; | |
539 | |
540 return { | |
541 text_to_xep54 = text_to_xep54; | |
542 text_to_lua = text_to_lua; | |
543 xep54_to_text = xep54_to_text; | |
544 xep54_to_lua = xep54_to_lua; | |
545 --[[ TODO | |
546 from_text = from_text; | |
547 to_text = to_text; | |
548 from_xep54 = from_xep54; | |
549 to_xep54 = to_xep54; | |
550 --]] | |
551 }; |