Mercurial > prosody-modules
comparison mod_email_pass/vcard.lib.lua @ 1346:5608dd296d5f
Adding vcard.lib.lua that is necessary for the module
| author | Luis G.F <luisgf@gmail.com> |
|---|---|
| date | Thu, 13 Mar 2014 14:59:36 +0000 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 1345:c60e9943dcb9 | 1346:5608dd296d5f |
|---|---|
| 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 -- Fix folding. | |
| 9 | |
| 10 local st = require "util.stanza"; | |
| 11 local t_insert, t_concat = table.insert, table.concat; | |
| 12 local type = type; | |
| 13 local next, pairs, ipairs = next, pairs, ipairs; | |
| 14 | |
| 15 local from_text, to_text, from_xep54, to_xep54; | |
| 16 | |
| 17 local line_sep = "\n"; | |
| 18 | |
| 19 local vCard_dtd; -- See end of file | |
| 20 | |
| 21 local function fold_line() | |
| 22 error "Not implemented" --TODO | |
| 23 end | |
| 24 local function unfold_line() | |
| 25 error "Not implemented" | |
| 26 -- gsub("\r?\n[ \t]([^\r\n])", "%1"); | |
| 27 end | |
| 28 | |
| 29 local function vCard_esc(s) | |
| 30 return s:gsub("[,:;\\]", "\\%1"):gsub("\n","\\n"); | |
| 31 end | |
| 32 | |
| 33 local function vCard_unesc(s) | |
| 34 return s:gsub("\\?[\\nt:;,]", { | |
| 35 ["\\\\"] = "\\", | |
| 36 ["\\n"] = "\n", | |
| 37 ["\\r"] = "\r", | |
| 38 ["\\t"] = "\t", | |
| 39 ["\\:"] = ":", -- FIXME Shouldn't need to espace : in values, just params | |
| 40 ["\\;"] = ";", | |
| 41 ["\\,"] = ",", | |
| 42 [":"] = "\29", | |
| 43 [";"] = "\30", | |
| 44 [","] = "\31", | |
| 45 }); | |
| 46 end | |
| 47 | |
| 48 local function item_to_xep54(item) | |
| 49 local t = st.stanza(item.name, { xmlns = "vcard-temp" }); | |
| 50 | |
| 51 local prop_def = vCard_dtd[item.name]; | |
| 52 if prop_def == "text" then | |
| 53 t:text(item[1]); | |
| 54 elseif type(prop_def) == "table" then | |
| 55 if prop_def.types and item.TYPE then | |
| 56 if type(item.TYPE) == "table" then | |
| 57 for _,v in pairs(prop_def.types) do | |
| 58 for _,typ in pairs(item.TYPE) do | |
| 59 if typ:upper() == v then | |
| 60 t:tag(v):up(); | |
| 61 break; | |
| 62 end | |
| 63 end | |
| 64 end | |
| 65 else | |
| 66 t:tag(item.TYPE:upper()):up(); | |
| 67 end | |
| 68 end | |
| 69 | |
| 70 if prop_def.props then | |
| 71 for _,v in pairs(prop_def.props) do | |
| 72 if item[v] then | |
| 73 t:tag(v):up(); | |
| 74 end | |
| 75 end | |
| 76 end | |
| 77 | |
| 78 if prop_def.value then | |
| 79 t:tag(prop_def.value):text(item[1]):up(); | |
| 80 elseif prop_def.values then | |
| 81 local prop_def_values = prop_def.values; | |
| 82 local repeat_last = prop_def_values.behaviour == "repeat-last" and prop_def_values[#prop_def_values]; | |
| 83 for i=1,#item do | |
| 84 t:tag(prop_def.values[i] or repeat_last):text(item[i]):up(); | |
| 85 end | |
| 86 end | |
| 87 end | |
| 88 | |
| 89 return t; | |
| 90 end | |
| 91 | |
| 92 local function vcard_to_xep54(vCard) | |
| 93 local t = st.stanza("vCard", { xmlns = "vcard-temp" }); | |
| 94 for i=1,#vCard do | |
| 95 t:add_child(item_to_xep54(vCard[i])); | |
| 96 end | |
| 97 return t; | |
| 98 end | |
| 99 | |
| 100 function to_xep54(vCards) | |
| 101 if not vCards[1] or vCards[1].name then | |
| 102 return vcard_to_xep54(vCards) | |
| 103 else | |
| 104 local t = st.stanza("xCard", { xmlns = "vcard-temp" }); | |
| 105 for i=1,#vCards do | |
| 106 t:add_child(vcard_to_xep54(vCards[i])); | |
| 107 end | |
| 108 return t; | |
| 109 end | |
| 110 end | |
| 111 | |
| 112 function from_text(data) | |
| 113 data = data -- unfold and remove empty lines | |
| 114 :gsub("\r\n","\n") | |
| 115 :gsub("\n ", "") | |
| 116 :gsub("\n\n+","\n"); | |
| 117 local vCards = {}; | |
| 118 local c; -- current item | |
| 119 for line in data:gmatch("[^\n]+") do | |
| 120 local line = vCard_unesc(line); | |
| 121 local name, params, value = line:match("^([-%a]+)(\30?[^\29]*)\29(.*)$"); | |
| 122 value = value:gsub("\29",":"); | |
| 123 if #params > 0 then | |
| 124 local _params = {}; | |
| 125 for k,isval,v in params:gmatch("\30([^=]+)(=?)([^\30]*)") do | |
| 126 k = k:upper(); | |
| 127 local _vt = {}; | |
| 128 for _p in v:gmatch("[^\31]+") do | |
| 129 _vt[#_vt+1]=_p | |
| 130 _vt[_p]=true; | |
| 131 end | |
| 132 if isval == "=" then | |
| 133 _params[k]=_vt; | |
| 134 else | |
| 135 _params[k]=true; | |
| 136 end | |
| 137 end | |
| 138 params = _params; | |
| 139 end | |
| 140 if name == "BEGIN" and value == "VCARD" then | |
| 141 c = {}; | |
| 142 vCards[#vCards+1] = c; | |
| 143 elseif name == "END" and value == "VCARD" then | |
| 144 c = nil; | |
| 145 elseif vCard_dtd[name] then | |
| 146 local dtd = vCard_dtd[name]; | |
| 147 local p = { name = name }; | |
| 148 c[#c+1]=p; | |
| 149 --c[name]=p; | |
| 150 local up = c; | |
| 151 c = p; | |
| 152 if dtd.types then | |
| 153 for _, t in ipairs(dtd.types) do | |
| 154 local t = t:lower(); | |
| 155 if ( params.TYPE and params.TYPE[t] == true) | |
| 156 or params[t] == true then | |
| 157 c.TYPE=t; | |
| 158 end | |
| 159 end | |
| 160 end | |
| 161 if dtd.props then | |
| 162 for _, p in ipairs(dtd.props) do | |
| 163 if params[p] then | |
| 164 if params[p] == true then | |
| 165 c[p]=true; | |
| 166 else | |
| 167 for _, prop in ipairs(params[p]) do | |
| 168 c[p]=prop; | |
| 169 end | |
| 170 end | |
| 171 end | |
| 172 end | |
| 173 end | |
| 174 if dtd == "text" or dtd.value then | |
| 175 t_insert(c, value); | |
| 176 elseif dtd.values then | |
| 177 local value = "\30"..value; | |
| 178 for p in value:gmatch("\30([^\30]*)") do | |
| 179 t_insert(c, p); | |
| 180 end | |
| 181 end | |
| 182 c = up; | |
| 183 end | |
| 184 end | |
| 185 return vCards; | |
| 186 end | |
| 187 | |
| 188 local function item_to_text(item) | |
| 189 local value = {}; | |
| 190 for i=1,#item do | |
| 191 value[i] = vCard_esc(item[i]); | |
| 192 end | |
| 193 value = t_concat(value, ";"); | |
| 194 | |
| 195 local params = ""; | |
| 196 for k,v in pairs(item) do | |
| 197 if type(k) == "string" and k ~= "name" then | |
| 198 params = params .. (";%s=%s"):format(k, type(v) == "table" and t_concat(v,",") or v); | |
| 199 end | |
| 200 end | |
| 201 | |
| 202 return ("%s%s:%s"):format(item.name, params, value) | |
| 203 end | |
| 204 | |
| 205 local function vcard_to_text(vcard) | |
| 206 local t={}; | |
| 207 t_insert(t, "BEGIN:VCARD") | |
| 208 for i=1,#vcard do | |
| 209 t_insert(t, item_to_text(vcard[i])); | |
| 210 end | |
| 211 t_insert(t, "END:VCARD") | |
| 212 return t_concat(t, line_sep); | |
| 213 end | |
| 214 | |
| 215 function to_text(vCards) | |
| 216 if vCards[1] and vCards[1].name then | |
| 217 return vcard_to_text(vCards) | |
| 218 else | |
| 219 local t = {}; | |
| 220 for i=1,#vCards do | |
| 221 t[i]=vcard_to_text(vCards[i]); | |
| 222 end | |
| 223 return t_concat(t, line_sep); | |
| 224 end | |
| 225 end | |
| 226 | |
| 227 local function from_xep54_item(item) | |
| 228 local prop_name = item.name; | |
| 229 local prop_def = vCard_dtd[prop_name]; | |
| 230 | |
| 231 local prop = { name = prop_name }; | |
| 232 | |
| 233 if prop_def == "text" then | |
| 234 prop[1] = item:get_text(); | |
| 235 elseif type(prop_def) == "table" then | |
| 236 if prop_def.value then --single item | |
| 237 prop[1] = item:get_child_text(prop_def.value) or ""; | |
| 238 elseif prop_def.values then --array | |
| 239 local value_names = prop_def.values; | |
| 240 if value_names.behaviour == "repeat-last" then | |
| 241 for i=1,#item.tags do | |
| 242 t_insert(prop, item.tags[i]:get_text() or ""); | |
| 243 end | |
| 244 else | |
| 245 for i=1,#value_names do | |
| 246 t_insert(prop, item:get_child_text(value_names[i]) or ""); | |
| 247 end | |
| 248 end | |
| 249 elseif prop_def.names then | |
| 250 local names = prop_def.names; | |
| 251 for i=1,#names do | |
| 252 if item:get_child(names[i]) then | |
| 253 prop[1] = names[i]; | |
| 254 break; | |
| 255 end | |
| 256 end | |
| 257 end | |
| 258 | |
| 259 if prop_def.props_verbatim then | |
| 260 for k,v in pairs(prop_def.props_verbatim) do | |
| 261 prop[k] = v; | |
| 262 end | |
| 263 end | |
| 264 | |
| 265 if prop_def.types then | |
| 266 local types = prop_def.types; | |
| 267 prop.TYPE = {}; | |
| 268 for i=1,#types do | |
| 269 if item:get_child(types[i]) then | |
| 270 t_insert(prop.TYPE, types[i]:lower()); | |
| 271 end | |
| 272 end | |
| 273 if #prop.TYPE == 0 then | |
| 274 prop.TYPE = nil; | |
| 275 end | |
| 276 end | |
| 277 | |
| 278 -- A key-value pair, within a key-value pair? | |
| 279 if prop_def.props then | |
| 280 local params = prop_def.props; | |
| 281 for i=1,#params do | |
| 282 local name = params[i] | |
| 283 local data = item:get_child_text(name); | |
| 284 if data then | |
| 285 prop[name] = prop[name] or {}; | |
| 286 t_insert(prop[name], data); | |
| 287 end | |
| 288 end | |
| 289 end | |
| 290 else | |
| 291 return nil | |
| 292 end | |
| 293 | |
| 294 return prop; | |
| 295 end | |
| 296 | |
| 297 local function from_xep54_vCard(vCard) | |
| 298 local tags = vCard.tags; | |
| 299 local t = {}; | |
| 300 for i=1,#tags do | |
| 301 t_insert(t, from_xep54_item(tags[i])); | |
| 302 end | |
| 303 return t | |
| 304 end | |
| 305 | |
| 306 function from_xep54(vCard) | |
| 307 if vCard.attr.xmlns ~= "vcard-temp" then | |
| 308 return nil, "wrong-xmlns"; | |
| 309 end | |
| 310 if vCard.name == "xCard" then -- A collection of vCards | |
| 311 local t = {}; | |
| 312 local vCards = vCard.tags; | |
| 313 for i=1,#vCards do | |
| 314 t[i] = from_xep54_vCard(vCards[i]); | |
| 315 end | |
| 316 return t | |
| 317 elseif vCard.name == "vCard" then -- A single vCard | |
| 318 return from_xep54_vCard(vCard) | |
| 319 end | |
| 320 end | |
| 321 | |
| 322 -- This was adapted from http://xmpp.org/extensions/xep-0054.html#dtd | |
| 323 vCard_dtd = { | |
| 324 VERSION = "text", --MUST be 3.0, so parsing is redundant | |
| 325 FN = "text", | |
| 326 N = { | |
| 327 values = { | |
| 328 "FAMILY", | |
| 329 "GIVEN", | |
| 330 "MIDDLE", | |
| 331 "PREFIX", | |
| 332 "SUFFIX", | |
| 333 }, | |
| 334 }, | |
| 335 NICKNAME = "text", | |
| 336 PHOTO = { | |
| 337 props_verbatim = { ENCODING = { "b" } }, | |
| 338 props = { "TYPE" }, | |
| 339 value = "BINVAL", --{ "EXTVAL", }, | |
| 340 }, | |
| 341 BDAY = "text", | |
| 342 ADR = { | |
| 343 types = { | |
| 344 "HOME", | |
| 345 "WORK", | |
| 346 "POSTAL", | |
| 347 "PARCEL", | |
| 348 "DOM", | |
| 349 "INTL", | |
| 350 "PREF", | |
| 351 }, | |
| 352 values = { | |
| 353 "POBOX", | |
| 354 "EXTADD", | |
| 355 "STREET", | |
| 356 "LOCALITY", | |
| 357 "REGION", | |
| 358 "PCODE", | |
| 359 "CTRY", | |
| 360 } | |
| 361 }, | |
| 362 LABEL = { | |
| 363 types = { | |
| 364 "HOME", | |
| 365 "WORK", | |
| 366 "POSTAL", | |
| 367 "PARCEL", | |
| 368 "DOM", | |
| 369 "INTL", | |
| 370 "PREF", | |
| 371 }, | |
| 372 value = "LINE", | |
| 373 }, | |
| 374 TEL = { | |
| 375 types = { | |
| 376 "HOME", | |
| 377 "WORK", | |
| 378 "VOICE", | |
| 379 "FAX", | |
| 380 "PAGER", | |
| 381 "MSG", | |
| 382 "CELL", | |
| 383 "VIDEO", | |
| 384 "BBS", | |
| 385 "MODEM", | |
| 386 "ISDN", | |
| 387 "PCS", | |
| 388 "PREF", | |
| 389 }, | |
| 390 value = "NUMBER", | |
| 391 }, | |
| 392 EMAIL = { | |
| 393 types = { | |
| 394 "HOME", | |
| 395 "WORK", | |
| 396 "INTERNET", | |
| 397 "PREF", | |
| 398 "X400", | |
| 399 }, | |
| 400 value = "USERID", | |
| 401 }, | |
| 402 JABBERID = "text", | |
| 403 MAILER = "text", | |
| 404 TZ = "text", | |
| 405 GEO = { | |
| 406 values = { | |
| 407 "LAT", | |
| 408 "LON", | |
| 409 }, | |
| 410 }, | |
| 411 TITLE = "text", | |
| 412 ROLE = "text", | |
| 413 LOGO = "copy of PHOTO", | |
| 414 AGENT = "text", | |
| 415 ORG = { | |
| 416 values = { | |
| 417 behaviour = "repeat-last", | |
| 418 "ORGNAME", | |
| 419 "ORGUNIT", | |
| 420 } | |
| 421 }, | |
| 422 CATEGORIES = { | |
| 423 values = "KEYWORD", | |
| 424 }, | |
| 425 NOTE = "text", | |
| 426 PRODID = "text", | |
| 427 REV = "text", | |
| 428 SORTSTRING = "text", | |
| 429 SOUND = "copy of PHOTO", | |
| 430 UID = "text", | |
| 431 URL = "text", | |
| 432 CLASS = { | |
| 433 names = { -- The item.name is the value if it's one of these. | |
| 434 "PUBLIC", | |
| 435 "PRIVATE", | |
| 436 "CONFIDENTIAL", | |
| 437 }, | |
| 438 }, | |
| 439 KEY = { | |
| 440 props = { "TYPE" }, | |
| 441 value = "CRED", | |
| 442 }, | |
| 443 DESC = "text", | |
| 444 }; | |
| 445 vCard_dtd.LOGO = vCard_dtd.PHOTO; | |
| 446 vCard_dtd.SOUND = vCard_dtd.PHOTO; | |
| 447 | |
| 448 return { | |
| 449 from_text = from_text; | |
| 450 to_text = to_text; | |
| 451 | |
| 452 from_xep54 = from_xep54; | |
| 453 to_xep54 = to_xep54; | |
| 454 | |
| 455 -- COMPAT: | |
| 456 lua_to_text = to_text; | |
| 457 lua_to_xep54 = to_xep54; | |
| 458 | |
| 459 text_to_lua = from_text; | |
| 460 text_to_xep54 = function (...) return to_xep54(from_text(...)); end; | |
| 461 | |
| 462 xep54_to_lua = from_xep54; | |
| 463 xep54_to_text = function (...) return to_text(from_xep54(...)) end; | |
| 464 }; |
