1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package davmail.exchange;
20
21 import java.util.*;
22
23
24
25
26 public class VProperty {
27
28 protected enum State {
29 KEY, PARAM_NAME, PARAM_VALUE, QUOTED_PARAM_VALUE, VALUE, BACKSLASH
30 }
31
32 protected static final HashSet<String> MULTIVALUED_PROPERTIES = new HashSet<>();
33
34 static {
35 MULTIVALUED_PROPERTIES.add("RESOURCES");
36 MULTIVALUED_PROPERTIES.add("LOCATION");
37 }
38
39 protected static class Param {
40 String name;
41 List<String> values;
42
43 public void addAll(List<String> paramValues) {
44 if (values == null) {
45 values = new ArrayList<>();
46 }
47 values.addAll(paramValues);
48 }
49
50 protected String getValue() {
51 if (values != null && !values.isEmpty()) {
52 return values.get(0);
53 } else {
54 return null;
55 }
56 }
57 }
58
59 protected String key;
60 protected List<Param> params;
61 protected List<String> values;
62
63
64
65
66
67
68
69 public VProperty(String name, String value) {
70 setKey(name);
71 setValue(value);
72 }
73
74
75
76
77
78
79 public VProperty(String line) {
80 if (line != null && !"END:VCARD".equals(line)) {
81 State state = State.KEY;
82 String paramName = null;
83 List<String> paramValues = null;
84 int startIndex = 0;
85 for (int i = 0; i < line.length(); i++) {
86 char currentChar = line.charAt(i);
87 if (state == State.KEY) {
88 if (currentChar == ':') {
89 setKey(line.substring(startIndex, i));
90 state = State.VALUE;
91 startIndex = i + 1;
92 } else if (currentChar == ';') {
93 setKey(line.substring(startIndex, i));
94 state = State.PARAM_NAME;
95 startIndex = i + 1;
96 }
97 } else if (state == State.PARAM_NAME) {
98 if (currentChar == '=') {
99 paramName = line.substring(startIndex, i).toUpperCase();
100 state = State.PARAM_VALUE;
101 paramValues = new ArrayList<>();
102 startIndex = i + 1;
103 } else if (currentChar == ';') {
104
105 paramName = line.substring(startIndex, i).toUpperCase();
106 addParam(paramName);
107 state = State.PARAM_NAME;
108 startIndex = i + 1;
109 } else if (currentChar == ':') {
110
111 paramName = line.substring(startIndex, i).toUpperCase();
112 addParam(paramName);
113 state = State.VALUE;
114 startIndex = i + 1;
115 }
116 } else if (state == State.PARAM_VALUE) {
117 if (currentChar == '"') {
118 state = State.QUOTED_PARAM_VALUE;
119 startIndex = i + 1;
120 } else if (currentChar == ':') {
121 if (startIndex < i) {
122 paramValues = addParamValue(paramValues, line.substring(startIndex, i));
123 }
124 addParam(paramName, paramValues);
125 state = State.VALUE;
126 startIndex = i + 1;
127 } else if (currentChar == ';') {
128 if (startIndex < i) {
129 paramValues = addParamValue(paramValues, line.substring(startIndex, i));
130 }
131 addParam(paramName, paramValues);
132 state = State.PARAM_NAME;
133 startIndex = i + 1;
134 } else if (currentChar == ',') {
135 if (startIndex < i) {
136 paramValues = addParamValue(paramValues, line.substring(startIndex, i));
137 }
138 startIndex = i + 1;
139 }
140 } else if (state == State.QUOTED_PARAM_VALUE) {
141 if (currentChar == '"') {
142 state = State.PARAM_VALUE;
143 paramValues = addParamValue(paramValues, line.substring(startIndex, i));
144 startIndex = i + 1;
145 }
146 } else if (state == State.VALUE) {
147 if (currentChar == '\\') {
148 state = State.BACKSLASH;
149 } else if (currentChar == ';' || (MULTIVALUED_PROPERTIES.contains(key) && currentChar == ',')) {
150 addValue(line.substring(startIndex, i));
151 startIndex = i + 1;
152 }
153 } else if (state == State.BACKSLASH) {
154 state = State.VALUE;
155 }
156 }
157 if (state == State.VALUE) {
158 addValue(line.substring(startIndex));
159 } else {
160 throw new IllegalArgumentException("Invalid property line: " + line);
161 }
162 }
163 }
164
165
166
167
168
169
170 public String getKey() {
171 return key;
172 }
173
174
175
176
177
178
179 public String getValue() {
180 if (values == null || values.isEmpty()) {
181 return null;
182 } else {
183 return values.get(0);
184 }
185 }
186
187
188
189
190
191
192 public List<String> getValues() {
193 return values;
194 }
195
196
197
198
199
200
201 public Map<String, String> getValuesAsMap() {
202 HashMap<String, String> valuesMap = new HashMap<>();
203
204 if (values != null) {
205 for (String value:values) {
206 if (value.contains("=")) {
207 int index = value.indexOf("=");
208 valuesMap.put(value.substring(0, index), value.substring(index+1));
209 }
210 }
211 }
212 return valuesMap;
213 }
214
215
216
217
218
219
220
221
222
223 public boolean hasParam(String paramName, String paramValue) {
224 return params != null && getParam(paramName) != null && containsIgnoreCase(getParam(paramName).values, paramValue);
225 }
226
227
228
229
230
231
232
233 public boolean hasParam(String paramName) {
234 return params != null && getParam(paramName) != null;
235 }
236
237
238
239
240
241
242 public void removeParam(String paramName) {
243 if (params != null) {
244 Param param = getParam(paramName);
245 if (param != null) {
246 params.remove(param);
247 }
248 }
249 }
250
251 protected boolean containsIgnoreCase(List<String> stringCollection, String value) {
252 for (String collectionValue : stringCollection) {
253 if (value.equalsIgnoreCase(collectionValue)) {
254 return true;
255 }
256 }
257 return false;
258 }
259
260
261
262
263
264
265
266
267 protected List<String> addParamValue(List<String> paramValues, String value) {
268 List<String> result = paramValues;
269 if (result == null) {
270 result = new ArrayList<>();
271 }
272 result.add(value);
273 return result;
274 }
275
276 protected void addParam(String paramName) {
277 addParam(paramName, (String) null);
278 }
279
280
281
282
283
284
285
286 public void setParam(String paramName, String paramValue) {
287 Param currentParam = getParam(paramName);
288 if (currentParam != null) {
289 params.remove(currentParam);
290 }
291 addParam(paramName, paramValue);
292 }
293
294
295
296
297
298
299
300 public void addParam(String paramName, String paramValue) {
301 if (paramValue != null) {
302 List<String> paramValues = new ArrayList<>();
303 paramValues.add(paramValue);
304 addParam(paramName, paramValues);
305 }
306 }
307
308 protected void addParam(String paramName, List<String> paramValues) {
309 if (params == null) {
310 params = new ArrayList<>();
311 }
312 Param currentParam = getParam(paramName);
313 if (currentParam == null) {
314 currentParam = new Param();
315 currentParam.name = paramName;
316 params.add(currentParam);
317 }
318 currentParam.addAll(paramValues);
319 }
320
321 protected Param getParam(String paramName) {
322 if (params != null && paramName != null) {
323 for (Param param : params) {
324 if (paramName.equals(param.name)) {
325 return param;
326 }
327 }
328 }
329 return null;
330 }
331
332
333
334
335
336
337 public String getParamValue(String paramName) {
338 Param param = getParam(paramName);
339 if (param != null) {
340 return param.getValue();
341 } else {
342 return null;
343 }
344 }
345
346 protected List<Param> getParams() {
347 return params;
348 }
349
350 protected void setParams(List<Param> params) {
351 this.params = params;
352 }
353
354 protected void setValue(String value) {
355 if (value == null) {
356 values = null;
357 } else {
358 if (values == null) {
359 values = new ArrayList<>();
360 } else {
361 values.clear();
362 }
363 values.add(decodeValue(value));
364 }
365 }
366
367 protected void addValue(String value) {
368 if (values == null) {
369 values = new ArrayList<>();
370 }
371 values.add(decodeValue(value));
372 }
373
374 public void removeValue(String value) {
375 if (values != null) {
376 int index = -1;
377 for (int i=0;i<values.size();i++) {
378 if (value.equals(values.get(i))) {
379 index = i;
380 }
381 }
382 if (index >= 0) {
383 values.remove(index);
384 }
385 }
386 }
387
388
389 protected String decodeValue(String value) {
390 if (value == null || (value.indexOf('\\') < 0 && value.indexOf(',') < 0)) {
391 return value;
392 } else {
393
394 StringBuilder decodedValue = new StringBuilder();
395 for (int i = 0; i < value.length(); i++) {
396 char c = value.charAt(i);
397 if (c == '\\') {
398
399 i++;
400 if (i == value.length()) {
401 break;
402 }
403 c = value.charAt(i);
404 if (c == 'n' || c == 'N') {
405 c = '\n';
406 } else if (c == 'r') {
407 c = '\r';
408 }
409 }
410
411 if (c == ',' &&
412
413 ("N".equals(key) ||
414
415 "NICKNAME".equals(key)
416 )) {
417
418 c = '\n';
419 }
420 decodedValue.append(c);
421 }
422 return decodedValue.toString();
423 }
424 }
425
426
427
428
429
430
431 public void setKey(String key) {
432 int dotIndex = key.indexOf('.');
433 if (dotIndex < 0) {
434 this.key = key;
435 } else {
436 this.key = key.substring(dotIndex + 1);
437 }
438 }
439
440 public String toString() {
441 StringBuilder buffer = new StringBuilder();
442 buffer.append(key);
443 if (params != null) {
444 for (Param param : params) {
445 buffer.append(';').append(param.name);
446 appendParamValues(buffer, param);
447 }
448 }
449 buffer.append(':');
450 if (values != null) {
451 boolean firstValue = true;
452 for (String value : values) {
453 if (firstValue) {
454 firstValue = false;
455 } else if (MULTIVALUED_PROPERTIES.contains(key)) {
456 buffer.append(',');
457 } else {
458 buffer.append(';');
459 }
460 appendMultilineEncodedValue(buffer, value);
461 }
462 }
463 return buffer.toString();
464 }
465
466 protected void appendParamValues(StringBuilder buffer, Param param) {
467 if (param.values != null) {
468 buffer.append('=');
469 boolean first = true;
470 for (String value : param.values) {
471 if (first) {
472 first = false;
473 } else {
474 buffer.append(',');
475 }
476
477 if ("CN".equalsIgnoreCase(param.name)
478
479 || value.indexOf(';') >= 0 || value.indexOf(',') >= 0
480 || value.indexOf('(') >= 0 || value.indexOf('/') >= 0
481 || value.indexOf(':') >= 0) {
482 buffer.append('"').append(value).append('"');
483 } else {
484 buffer.append(value);
485 }
486 }
487 }
488 }
489
490
491
492
493
494
495
496 protected void appendMultilineEncodedValue(StringBuilder buffer, String value) {
497 for (int i = 0; i < value.length(); i++) {
498 char c = value.charAt(i);
499 if (c == '\n') {
500 buffer.append("\\n");
501 } else if (MULTIVALUED_PROPERTIES.contains(key) && c == ',') {
502 buffer.append('\\').append(',');
503
504 } else if (c != '\r') {
505 buffer.append(value.charAt(i));
506 }
507 }
508 }
509
510 }
511