2010-07-29 12:17:46 -04:00
|
|
|
/*
|
|
|
|
* DavMail POP/IMAP/SMTP/CalDav/LDAP Exchange Gateway
|
|
|
|
* Copyright (C) 2010 Mickael Guessant
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
*/
|
|
|
|
package davmail.exchange;
|
|
|
|
|
2010-07-29 17:18:59 -04:00
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.List;
|
2010-07-29 12:17:46 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* VCard property
|
|
|
|
*/
|
|
|
|
public class VProperty {
|
|
|
|
protected static enum State {
|
|
|
|
KEY, PARAM_NAME, PARAM_VALUE, QUOTED_PARAM_VALUE, VALUE, BACKSLASH
|
|
|
|
}
|
|
|
|
|
2010-07-31 18:52:45 -04:00
|
|
|
protected static class Param {
|
2010-07-29 12:17:46 -04:00
|
|
|
String name;
|
|
|
|
List<String> values;
|
|
|
|
|
|
|
|
public void addAll(List<String> paramValues) {
|
|
|
|
if (values == null) {
|
|
|
|
values = new ArrayList<String>();
|
|
|
|
}
|
|
|
|
values.addAll(paramValues);
|
|
|
|
}
|
2010-07-30 11:09:44 -04:00
|
|
|
|
|
|
|
public String getValue() {
|
|
|
|
if (values != null && !values.isEmpty()) {
|
|
|
|
return values.get(0);
|
|
|
|
} else {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
2010-07-29 12:17:46 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
protected String key;
|
|
|
|
protected List<Param> params;
|
|
|
|
protected List<String> values;
|
|
|
|
|
2010-07-31 18:52:45 -04:00
|
|
|
/**
|
|
|
|
* Create VProperty for key and value.
|
|
|
|
*
|
|
|
|
* @param name property name
|
|
|
|
* @param value property value
|
|
|
|
*/
|
2010-07-29 17:18:59 -04:00
|
|
|
public VProperty(String name, String value) {
|
|
|
|
setKey(name);
|
|
|
|
setValue(value);
|
|
|
|
}
|
|
|
|
|
2010-07-29 12:17:46 -04:00
|
|
|
/**
|
|
|
|
* Create VProperty from line.
|
|
|
|
*
|
|
|
|
* @param line card line
|
|
|
|
*/
|
|
|
|
public VProperty(String line) {
|
|
|
|
if (line != null && !"END:VCARD".equals(line)) {
|
|
|
|
State state = State.KEY;
|
|
|
|
String paramName = null;
|
|
|
|
List<String> paramValues = null;
|
|
|
|
int startIndex = 0;
|
|
|
|
for (int i = 0; i < line.length(); i++) {
|
|
|
|
char currentChar = line.charAt(i);
|
|
|
|
if (state == State.KEY) {
|
|
|
|
if (currentChar == ':') {
|
|
|
|
setKey(line.substring(startIndex, i));
|
|
|
|
state = State.VALUE;
|
|
|
|
startIndex = i + 1;
|
|
|
|
} else if (currentChar == ';') {
|
|
|
|
setKey(line.substring(startIndex, i));
|
|
|
|
state = State.PARAM_NAME;
|
|
|
|
startIndex = i + 1;
|
|
|
|
}
|
|
|
|
} else if (state == State.PARAM_NAME) {
|
|
|
|
if (currentChar == '=') {
|
|
|
|
paramName = line.substring(startIndex, i).toUpperCase();
|
|
|
|
state = State.PARAM_VALUE;
|
|
|
|
paramValues = new ArrayList<String>();
|
|
|
|
startIndex = i + 1;
|
|
|
|
} else if (currentChar == ';') {
|
|
|
|
// param with no value
|
|
|
|
paramName = line.substring(startIndex, i).toUpperCase();
|
2010-07-29 20:04:54 -04:00
|
|
|
addParam(paramName);
|
2010-07-29 12:17:46 -04:00
|
|
|
state = State.PARAM_NAME;
|
|
|
|
startIndex = i + 1;
|
|
|
|
} else if (currentChar == ':') {
|
|
|
|
// param with no value
|
|
|
|
paramName = line.substring(startIndex, i).toUpperCase();
|
2010-07-29 20:04:54 -04:00
|
|
|
addParam(paramName);
|
2010-07-29 12:17:46 -04:00
|
|
|
state = State.VALUE;
|
|
|
|
startIndex = i + 1;
|
|
|
|
}
|
|
|
|
} else if (state == State.PARAM_VALUE) {
|
|
|
|
if (currentChar == '"') {
|
|
|
|
state = State.QUOTED_PARAM_VALUE;
|
|
|
|
startIndex = i + 1;
|
|
|
|
} else if (currentChar == ':') {
|
|
|
|
if (startIndex < i) {
|
|
|
|
paramValues.add(line.substring(startIndex, i));
|
|
|
|
}
|
|
|
|
addParam(paramName, paramValues);
|
|
|
|
state = State.VALUE;
|
|
|
|
startIndex = i + 1;
|
|
|
|
} else if (currentChar == ';') {
|
|
|
|
if (startIndex < i) {
|
|
|
|
paramValues.add(line.substring(startIndex, i));
|
|
|
|
}
|
|
|
|
addParam(paramName, paramValues);
|
|
|
|
state = State.PARAM_NAME;
|
|
|
|
startIndex = i + 1;
|
|
|
|
} else if (currentChar == ',') {
|
|
|
|
if (startIndex < i) {
|
|
|
|
paramValues.add(line.substring(startIndex, i));
|
|
|
|
}
|
|
|
|
startIndex = i + 1;
|
|
|
|
}
|
|
|
|
} else if (state == State.QUOTED_PARAM_VALUE) {
|
|
|
|
if (currentChar == '"') {
|
|
|
|
state = State.PARAM_VALUE;
|
|
|
|
paramValues.add(line.substring(startIndex, i));
|
|
|
|
startIndex = i + 1;
|
|
|
|
}
|
|
|
|
} else if (state == State.VALUE) {
|
|
|
|
if (currentChar == '\\') {
|
|
|
|
state = State.BACKSLASH;
|
|
|
|
} else if (currentChar == ';') {
|
|
|
|
addValue(line.substring(startIndex, i));
|
|
|
|
startIndex = i + 1;
|
|
|
|
}
|
|
|
|
} else if (state == State.BACKSLASH) {
|
|
|
|
state = State.VALUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
addValue(line.substring(startIndex));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Property key, without optional parameters (e.g. TEL).
|
|
|
|
*
|
|
|
|
* @return key
|
|
|
|
*/
|
|
|
|
public String getKey() {
|
|
|
|
return key;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Property value.
|
|
|
|
*
|
|
|
|
* @return value
|
|
|
|
*/
|
|
|
|
public String getValue() {
|
|
|
|
if (values == null || values.isEmpty()) {
|
|
|
|
return null;
|
|
|
|
} else {
|
|
|
|
return values.get(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Property values.
|
|
|
|
*
|
|
|
|
* @return values
|
|
|
|
*/
|
|
|
|
public List<String> getValues() {
|
|
|
|
return values;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Test if the property has a param named paramName with given value.
|
|
|
|
*
|
|
|
|
* @param paramName param name
|
|
|
|
* @param paramValue param value
|
|
|
|
* @return true if property has param name and value
|
|
|
|
*/
|
|
|
|
public boolean hasParam(String paramName, String paramValue) {
|
|
|
|
return params != null && getParam(paramName) != null && containsIgnoreCase(getParam(paramName).values, paramValue);
|
|
|
|
}
|
|
|
|
|
2010-07-29 17:18:59 -04:00
|
|
|
/**
|
|
|
|
* Test if the property has a param named paramName.
|
|
|
|
*
|
|
|
|
* @param paramName param name
|
|
|
|
* @return true if property has param name
|
|
|
|
*/
|
|
|
|
public boolean hasParam(String paramName) {
|
|
|
|
return params != null && getParam(paramName) != null;
|
|
|
|
}
|
|
|
|
|
2010-07-31 18:52:45 -04:00
|
|
|
/**
|
|
|
|
* Remove param from property.
|
|
|
|
*
|
|
|
|
* @param paramName param name
|
|
|
|
*/
|
2010-07-30 11:09:44 -04:00
|
|
|
public void removeParam(String paramName) {
|
|
|
|
if (params != null) {
|
|
|
|
Param param = getParam(paramName);
|
|
|
|
if (param != null) {
|
|
|
|
params.remove(param);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-07-29 12:17:46 -04:00
|
|
|
protected boolean containsIgnoreCase(List<String> stringCollection, String value) {
|
|
|
|
for (String collectionValue : stringCollection) {
|
|
|
|
if (value.equalsIgnoreCase(collectionValue)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2010-07-29 20:04:54 -04:00
|
|
|
protected void addParam(String paramName) {
|
2010-07-30 11:09:44 -04:00
|
|
|
addParam(paramName, (String) null);
|
2010-07-29 20:04:54 -04:00
|
|
|
}
|
|
|
|
|
2010-09-13 12:07:49 -04:00
|
|
|
public void addParam(String paramName, String paramValue) {
|
2010-07-31 18:52:45 -04:00
|
|
|
List<String> paramValues = new ArrayList<String>();
|
2010-07-29 20:04:54 -04:00
|
|
|
paramValues.add(paramValue);
|
|
|
|
addParam(paramName, paramValues);
|
|
|
|
}
|
|
|
|
|
2010-07-29 12:17:46 -04:00
|
|
|
protected void addParam(String paramName, List<String> paramValues) {
|
|
|
|
if (params == null) {
|
|
|
|
params = new ArrayList<Param>();
|
|
|
|
}
|
|
|
|
Param currentParam = getParam(paramName);
|
|
|
|
if (currentParam == null) {
|
|
|
|
currentParam = new Param();
|
|
|
|
currentParam.name = paramName;
|
|
|
|
params.add(currentParam);
|
|
|
|
}
|
|
|
|
currentParam.addAll(paramValues);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected Param getParam(String paramName) {
|
|
|
|
if (params != null) {
|
2010-07-29 17:18:59 -04:00
|
|
|
for (Param param : params) {
|
2010-07-29 12:17:46 -04:00
|
|
|
if (paramName.equals(param.name)) {
|
|
|
|
return param;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2010-07-31 18:52:45 -04:00
|
|
|
protected List<Param> getParams() {
|
|
|
|
return params;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected void setParams(List<Param> params) {
|
|
|
|
this.params = params;
|
|
|
|
}
|
|
|
|
|
2010-07-29 17:18:59 -04:00
|
|
|
protected void setValue(String value) {
|
|
|
|
if (value == null) {
|
|
|
|
values = null;
|
|
|
|
} else {
|
|
|
|
if (values == null) {
|
|
|
|
values = new ArrayList<String>();
|
|
|
|
} else {
|
|
|
|
values.clear();
|
|
|
|
}
|
|
|
|
values.add(decodeValue(value));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-07-29 12:17:46 -04:00
|
|
|
protected void addValue(String value) {
|
|
|
|
if (values == null) {
|
|
|
|
values = new ArrayList<String>();
|
|
|
|
}
|
|
|
|
values.add(decodeValue(value));
|
|
|
|
}
|
|
|
|
|
|
|
|
protected String decodeValue(String value) {
|
|
|
|
if (value == null || (value.indexOf('\\') < 0 && value.indexOf(',') < 0)) {
|
|
|
|
return value;
|
|
|
|
} else {
|
|
|
|
// decode value
|
|
|
|
StringBuilder decodedValue = new StringBuilder();
|
|
|
|
for (int i = 0; i < value.length(); i++) {
|
|
|
|
char c = value.charAt(i);
|
|
|
|
if (c == '\\') {
|
|
|
|
//noinspection AssignmentToForLoopParameter
|
2010-09-13 18:40:07 -04:00
|
|
|
i++;
|
|
|
|
if (i == value.length()) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
c = value.charAt(i);
|
2010-07-31 18:52:45 -04:00
|
|
|
if (c == 'n' || c == 'N') {
|
2010-07-29 12:17:46 -04:00
|
|
|
c = '\n';
|
|
|
|
} else if (c == 'r') {
|
|
|
|
c = '\r';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// iPhone encodes category separator
|
|
|
|
if (c == ',' &&
|
|
|
|
// multivalued properties
|
|
|
|
("N".equals(key) ||
|
|
|
|
"ADR".equals(key) ||
|
|
|
|
"CATEGORIES".equals(key) ||
|
|
|
|
"NICKNAME".equals(key)
|
|
|
|
)) {
|
|
|
|
// convert multiple values to multiline values (e.g. street)
|
|
|
|
c = '\n';
|
|
|
|
}
|
|
|
|
decodedValue.append(c);
|
|
|
|
}
|
|
|
|
return decodedValue.toString();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set property key.
|
|
|
|
*
|
|
|
|
* @param key property key
|
|
|
|
*/
|
|
|
|
public void setKey(String key) {
|
|
|
|
int dotIndex = key.indexOf('.');
|
|
|
|
if (dotIndex < 0) {
|
|
|
|
this.key = key;
|
|
|
|
} else {
|
|
|
|
this.key = key.substring(dotIndex + 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public String toString() {
|
|
|
|
StringBuilder buffer = new StringBuilder();
|
|
|
|
buffer.append(key);
|
|
|
|
if (params != null) {
|
|
|
|
for (Param param : params) {
|
|
|
|
buffer.append(';').append(param.name);
|
|
|
|
if (param.values != null) {
|
|
|
|
buffer.append('=');
|
|
|
|
for (String value : param.values) {
|
|
|
|
appendParamValue(buffer, value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
buffer.append(':');
|
|
|
|
if (values != null) {
|
|
|
|
boolean firstValue = true;
|
|
|
|
for (String value : values) {
|
|
|
|
if (firstValue) {
|
|
|
|
firstValue = false;
|
|
|
|
} else {
|
|
|
|
buffer.append(';');
|
|
|
|
}
|
|
|
|
appendMultilineEncodedValue(buffer, value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return buffer.toString();
|
|
|
|
}
|
|
|
|
|
|
|
|
protected void appendParamValue(StringBuilder buffer, String value) {
|
2010-10-22 10:47:32 -04:00
|
|
|
if (value.indexOf(';') >= 0 || value.indexOf(',') >= 0
|
|
|
|
|| value.indexOf('(') >= 0 || value.indexOf('/') >= 0
|
|
|
|
|| value.indexOf(':') >= 0) {
|
2010-07-29 12:17:46 -04:00
|
|
|
buffer.append('"').append(value).append('"');
|
|
|
|
} else {
|
|
|
|
buffer.append(value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Append and encode \n to \\n in value.
|
|
|
|
*
|
|
|
|
* @param buffer line buffer
|
|
|
|
* @param value value
|
|
|
|
*/
|
|
|
|
protected void appendMultilineEncodedValue(StringBuilder buffer, String value) {
|
|
|
|
for (int i = 0; i < value.length(); i++) {
|
|
|
|
char c = value.charAt(i);
|
|
|
|
if (c == '\n') {
|
|
|
|
buffer.append("\\n");
|
|
|
|
} else {
|
|
|
|
buffer.append(value.charAt(i));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|