|
1 local t_insert, t_concat = table.insert, table.concat; |
|
2 |
|
3 local vCard_dtd = { |
|
4 VERSION = "ignore", --MUST be 3.0, so parsing is redundant |
|
5 FN = "text", |
|
6 N = { |
|
7 values = { |
|
8 "FAMILY", |
|
9 "GIVEN", |
|
10 "MIDDLE", |
|
11 "PREFIX", |
|
12 "SUFFIX", |
|
13 }, |
|
14 }, |
|
15 NICKNAME = "text", |
|
16 PHOTO = { |
|
17 props_verbatim = { ENCODING = { "b" } }, |
|
18 props = { "TYPE" }, |
|
19 value = "BINVAL", --{ "EXTVAL", }, |
|
20 }, |
|
21 BDAY = "text", |
|
22 ADR = { |
|
23 types = { |
|
24 "HOME", |
|
25 "WORK", |
|
26 "POSTAL", |
|
27 "PARCEL", |
|
28 "DOM", |
|
29 "INTL", |
|
30 "PREF", |
|
31 }, |
|
32 values = { |
|
33 "POBOX", |
|
34 "EXTADD", |
|
35 "STREET", |
|
36 "LOCALITY", |
|
37 "REGION", |
|
38 "PCODE", |
|
39 "CTRY", |
|
40 } |
|
41 }, |
|
42 LABEL = { |
|
43 types = { |
|
44 "HOME", |
|
45 "WORK", |
|
46 "POSTAL", |
|
47 "PARCEL", |
|
48 "DOM", |
|
49 "INTL", |
|
50 "PREF", |
|
51 }, |
|
52 value = "LINE", |
|
53 }, |
|
54 TEL = { |
|
55 types = { |
|
56 "HOME", |
|
57 "WORK", |
|
58 "VOICE", |
|
59 "FAX", |
|
60 "PAGER", |
|
61 "MSG", |
|
62 "CELL", |
|
63 "VIDEO", |
|
64 "BBS", |
|
65 "MODEM", |
|
66 "ISDN", |
|
67 "PCS", |
|
68 "PREF", |
|
69 }, |
|
70 value = "NUMBER", |
|
71 }, |
|
72 EMAIL = { |
|
73 types = { |
|
74 "HOME", |
|
75 "WORK", |
|
76 "INTERNET", |
|
77 "PREF", |
|
78 "X400", |
|
79 }, |
|
80 value = "USERID", |
|
81 }, |
|
82 JABBERID = "text", |
|
83 MAILER = "text", |
|
84 TZ = "text", |
|
85 GEO = { |
|
86 values = { |
|
87 "LAT", |
|
88 "LON", |
|
89 }, |
|
90 }, |
|
91 TITLE = "text", |
|
92 ROLE = "text", |
|
93 LOGO = "copy of PHOTO", |
|
94 AGENT = "text", |
|
95 ORG = { |
|
96 values = { |
|
97 behaviour = "repeat-last", |
|
98 "ORGNAME", |
|
99 "ORGUNIT", |
|
100 } |
|
101 }, |
|
102 CATEGORIES = { |
|
103 values = "KEYWORD", |
|
104 }, |
|
105 NOTE = "text", |
|
106 PRODID = "text", |
|
107 REV = "text", |
|
108 SORTSTRING = "text", |
|
109 SOUND = "copy of PHOTO", |
|
110 UID = "text", |
|
111 URL = "text", |
|
112 CLASS = { |
|
113 names = { -- The item.name is the value if it's one of these. |
|
114 "PUBLIC", |
|
115 "PRIVATE", |
|
116 "CONFIDENTIAL", |
|
117 }, |
|
118 }, |
|
119 KEY = { |
|
120 props = { "TYPE" }, |
|
121 value = "CRED", |
|
122 }, |
|
123 DESC = "text", |
|
124 }; |
|
125 vCard_dtd.LOGO = vCard_dtd.PHOTO; |
|
126 vCard_dtd.SOUND = vCard_dtd.PHOTO; |
|
127 |
|
128 --TODO BINVAL OR EXTVAL |
|
129 |
|
130 local function vCard_esc(s) |
|
131 return s:gsub("[,;\\]", "\\%1"):gsub("\n","\\n"); |
|
132 end |
|
133 |
|
134 local function vCard_prop(item) |
|
135 local prop_name = item.name; |
|
136 local prop_def = vCard_dtd[prop_name]; |
|
137 if not prop_def then return nil end |
|
138 |
|
139 local value, params = "", {}; |
|
140 |
|
141 if prop_def == "text" then |
|
142 value = item:get_text(); |
|
143 elseif type(prop_def) == "table" then |
|
144 if prop_def.value then --single item |
|
145 value = item:get_child_text(prop_def.value) or ""; |
|
146 elseif prop_def.values then --array |
|
147 local value_names = prop_def.values; |
|
148 value = {}; |
|
149 for i=1,#value_names do |
|
150 t_insert(value, item:get_child_text(value_names[i]) or ""); |
|
151 --TODO ORG with >1 sub items |
|
152 end |
|
153 elseif prop_def.names then |
|
154 local names = prop_def.names; |
|
155 for i=1,#names do |
|
156 if item:get_child(names[i]) then |
|
157 value = names[i]; |
|
158 break; |
|
159 end |
|
160 end |
|
161 end |
|
162 |
|
163 if prop_def.props_verbatim then |
|
164 for k,v in pairs(prop_def.props_verbatim) do |
|
165 params[k] = v; |
|
166 end |
|
167 end |
|
168 |
|
169 if prop_def.types then |
|
170 local types = prop_def.types; |
|
171 params.TYPE = {}; |
|
172 for i=1,#types do |
|
173 if item:get_child(types[i]) then |
|
174 t_insert(params.TYPE, types[i]:lower()); |
|
175 end |
|
176 end |
|
177 if #params.TYPE == 0 then |
|
178 params.TYPE = nil; |
|
179 end |
|
180 end |
|
181 |
|
182 if prop_def.props then |
|
183 local props = prop_def.props; |
|
184 for i=1,#props do |
|
185 local prop = props[i] |
|
186 local p = item:get_child_text(prop); |
|
187 if p then |
|
188 params[prop] = params[prop] or {}; |
|
189 t_insert(params[prop], p); |
|
190 end |
|
191 end |
|
192 end |
|
193 else |
|
194 return nil |
|
195 end |
|
196 |
|
197 if type(value) == "table" then |
|
198 for i=1,#value do |
|
199 value[i]=vCard_esc(value[i]); |
|
200 end |
|
201 value = t_concat(value, ";"); |
|
202 else |
|
203 value = vCard_esc(value); |
|
204 end |
|
205 |
|
206 if next(params) then |
|
207 local sparams = ""; |
|
208 for k,v in pairs(params) do |
|
209 sparams = sparams .. (";%s=%s"):format(k, t_concat(v,",")); |
|
210 end |
|
211 params = sparams; |
|
212 else |
|
213 params = ""; |
|
214 end |
|
215 |
|
216 --TODO: Split long lines |
|
217 return ("%s%s:%s"):format(item.name, params, value); |
|
218 end |
|
219 |
|
220 return function (opts, arg) |
|
221 if opts.short_help then |
|
222 print("Fetch someones vCard"); |
|
223 return; |
|
224 end |
|
225 if (#arg == 0 or opts.help) then |
|
226 return 0; |
|
227 end |
|
228 local function on_connect(conn) |
|
229 conn:add_plugin("vcard"); |
|
230 conn:get_vcard(arg[1], function(reply) |
|
231 local vCard = reply:get_child("vCard", "vcard-temp"); |
|
232 if vCard then |
|
233 print("BEGIN:VCARD"); |
|
234 print("VERSION:3.0"); |
|
235 for i = 1,#vCard do |
|
236 local item = vCard[i]; |
|
237 if item.name then |
|
238 local s = vCard_prop(item); |
|
239 if s then print(s); end |
|
240 else |
|
241 conn:debug("Unhandled vCard field: %s", item.name); |
|
242 end |
|
243 end |
|
244 print("END:VCARD"); |
|
245 end |
|
246 verse.quit(); |
|
247 end); |
|
248 end |
|
249 clix_connect(opts, on_connect); |
|
250 end |