Caldav: start new VCalendar fixICS implementation

git-svn-id: http://svn.code.sf.net/p/davmail/code/trunk@1318 3d1905a2-6b24-0410-a738-b14d5a86fcbd
This commit is contained in:
mguessan 2010-07-29 21:18:59 +00:00
parent 2b92a0946e
commit 3edbd2c44c
4 changed files with 731 additions and 13 deletions

View File

@ -0,0 +1,166 @@
/*
* 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;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
/**
* VCalendar object.
*/
public class VCalendar extends VObject {
protected VObject firstVevent;
protected VObject vTimezone;
protected String email;
/**
* Create VCalendar object from reader;
*
* @param reader stream reader
* @param email current user email
* @throws IOException on error
*/
public VCalendar(BufferedReader reader, String email) throws IOException {
super(reader);
if (!"VCALENDAR".equals(type)) {
throw new IOException("Invalid type: " + type);
}
this.email = email;
}
/**
* Create VCalendar object from reader;
*
* @param vCalendarBody item body
* @param email current user email
* @throws IOException on error
*/
public VCalendar(String vCalendarBody, String email) throws IOException {
this(new ICSBufferedReader(new StringReader(vCalendarBody)), email);
}
@Override
protected void addVObject(VObject vObject) {
super.addVObject(vObject);
if (firstVevent == null && "VEVENT".equals(vObject.type)) {
firstVevent = vObject;
}
if ("VTIMEZONE".equals(vObject.type)) {
vTimezone = vObject;
}
}
protected boolean isAllDay(VObject vObject) {
VProperty dtstart = vObject.getProperty("DTSTART");
return dtstart != null && dtstart.hasParam("DATE");
}
protected boolean hasCdoAllDay(VObject vObject) {
return vObject.getProperty("X-MICROSOFT-CDO-ALLDAYEVENT") != null;
}
protected boolean hasCdoBusyStatus(VObject vObject) {
return vObject.getProperty("X-MICROSOFT-CDO-BUSYSTATUS") != null;
}
protected boolean isCdoAllDay(VObject vObject) {
return "TRUE".equals(vObject.getPropertyValue("X-MICROSOFT-CDO-ALLDAYEVENT"));
}
protected boolean isAppleiCal() {
return getPropertyValue("PRODID").contains("iCal");
}
protected String getOrganizer() {
String organizer = firstVevent.getPropertyValue("ORGANIZER");
if (organizer.startsWith("MAILTO:")) {
return organizer.substring(7);
} else {
return organizer;
}
}
protected String getMethod() {
return getPropertyValue("METHOD");
}
protected void fixVCalendar(boolean fromServer) {
// append missing method
if (getProperty("METHOD") == null) {
setPropertyValue("METHOD", "PUBLISH");
}
// iCal 4 global private flag
if (fromServer) {
setPropertyValue("X-CALENDARSERVER-ACCESS", getCalendarServerAccess());
}
String calendarServerAccess = getPropertyValue("X-CALENDARSERVER-ACCESS");
// TODO: patch timezone for iPhone
// iterate over vObjects
for (VObject vObject : vObjects) {
if ("VEVENT".equals(vObject.type)) {
if (calendarServerAccess != null) {
setPropertyValue("CLASS", getEventClass(calendarServerAccess));
} else if (vObject.getPropertyValue("X-CALENDARSERVER-ACCESS") != null){
setPropertyValue("CLASS", getEventClass(vObject.getPropertyValue("X-CALENDARSERVER-ACCESS")));
}
// add organizer line to all events created in Exchange for active sync
if (vObject.getPropertyValue("ORGANIZER") == null) {
vObject.setPropertyValue("ORGANIZER", "MAILTO:"+email);
}
}
}
}
/**
* Convert X-CALENDARSERVER-ACCESS to CLASS.
*
* @return CLASS value
*/
protected String getEventClass(String calendarServerAccess) {
if ("PRIVATE".equals(calendarServerAccess)) {
return "CONFIDENTIAL";
} else if ("CONFIDENTIAL".equals(calendarServerAccess) || "RESTRICTED".equals(calendarServerAccess)) {
return "PRIVATE";
} else {
return calendarServerAccess;
}
}
/**
* Convert CLASS to X-CALENDARSERVER-ACCESS.
*
* @return X-CALENDARSERVER-ACCESS value
*/
protected String getCalendarServerAccess() {
String eventClass = firstVevent.getPropertyValue("CLASS");
if ("PRIVATE".equals(eventClass)) {
return "CONFIDENTIAL";
} else if ("CONFIDENTIAL".equals(eventClass)) {
return "PRIVATE";
} else {
return eventClass;
}
}
}

View File

@ -34,7 +34,7 @@ public class VObject {
/**
* Inner VObjects (e.g. VEVENT, VALARM, ...)
*/
ArrayList<VObject> objects;
ArrayList<VObject> vObjects;
/**
* Object base name (VCALENDAR, VEVENT, VCARD...).
*/
@ -77,26 +77,33 @@ public class VObject {
VProperty property = new VProperty(line);
// inner object
if ("BEGIN".equals(property.getKey())) {
addObject(new VObject(property, reader));
addVObject(new VObject(property, reader));
} else {
addProperty(property);
}
}
protected void addObject(VObject object) {
if (objects == null) {
objects = new ArrayList<VObject>();
protected void addVObject(VObject vObject) {
if (vObjects == null) {
vObjects = new ArrayList<VObject>();
}
objects.add(object);
vObjects.add(vObject);
}
protected void addProperty(VProperty property) {
if (properties == null) {
properties = new ArrayList<VProperty>();
if (property.getValue() != null) {
if (properties == null) {
properties = new ArrayList<VProperty>();
}
properties.add(property);
}
properties.add(property);
}
/**
* Write VObject to writer.
*
* @param writer buffered writer
*/
public void writeTo(ICSBufferedWriter writer) {
writer.write("BEGIN:");
writer.writeLine(type);
@ -105,8 +112,8 @@ public class VObject {
writer.writeLine(property.toString());
}
}
if (objects != null) {
for (VObject object : objects) {
if (vObjects != null) {
for (VObject object : vObjects) {
object.writeTo(writer);
}
}
@ -120,7 +127,44 @@ public class VObject {
return writer.toString();
}
/**
* Get VObject properties
*
* @return properties
*/
public List<VProperty> getProperties() {
return properties;
}
public VProperty getProperty(String name) {
if (properties != null) {
for (VProperty property : properties) {
if (property.getKey().equalsIgnoreCase(name)) {
return property;
}
}
}
return null;
}
public String getPropertyValue(String name) {
VProperty property = getProperty(name);
if (property != null) {
return property.getValue();
} else {
return null;
}
}
public void setPropertyValue(String name, String value) {
VProperty property = getProperty(name);
if (property == null) {
property = new VProperty(name, value);
addProperty(property);
} else {
property.setValue(value);
}
}
}

View File

@ -18,7 +18,8 @@
*/
package davmail.exchange;
import java.util.*;
import java.util.ArrayList;
import java.util.List;
/**
* VCard property
@ -44,6 +45,11 @@ public class VProperty {
protected List<Param> params;
protected List<String> values;
public VProperty(String name, String value) {
setKey(name);
setValue(value);
}
/**
* Create VProperty from line.
*
@ -173,6 +179,16 @@ public class VProperty {
return params != null && getParam(paramName) != null && containsIgnoreCase(getParam(paramName).values, paramValue);
}
/**
* 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;
}
protected boolean containsIgnoreCase(List<String> stringCollection, String value) {
for (String collectionValue : stringCollection) {
if (value.equalsIgnoreCase(collectionValue)) {
@ -197,7 +213,7 @@ public class VProperty {
protected Param getParam(String paramName) {
if (params != null) {
for (Param param:params) {
for (Param param : params) {
if (paramName.equals(param.name)) {
return param;
}
@ -206,6 +222,19 @@ public class VProperty {
return null;
}
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));
}
}
protected void addValue(String value) {
if (values == null) {
values = new ArrayList<String>();

View File

@ -0,0 +1,479 @@
/*
* 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;
import davmail.Settings;
import davmail.exception.DavMailException;
import davmail.util.StringUtil;
import junit.framework.TestCase;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
/**
* Test ExchangeSession event conversion.
*/
public class TestExchangeSessionEvent extends TestCase {
String email = "user@company.com";
/*
X-CALENDARSERVER-ACCESS conversion
see http://svn.calendarserver.org/repository/calendarserver/CalendarServer/trunk/doc/Extensions/caldav-privateevents.txt
X-CALENDARSERVER-ACCESS -> CLASS
NONE -> NONE
PUBLIC -> PUBLIC
PRIVATE -> CONFIDENTIAL
CONFIDENTIAL -> PRIVATE
RESTRICTED -> PRIVATE
CLASS -> X-CALENDARSERVER-ACCESS
NONE -> NONE
PUBLIC -> PUBLIC
PRIVATE -> CONFIDENTIAL
CONFIDENTIAL -> PRIVATE
iCal 3 sends X-CALENDARSERVER-ACCESS inside VEVENT, iCal 4 uses global X-CALENDARSERVER-ACCESS
*/
protected String getAllDayLine(String line) throws IOException {
int valueIndex = line.lastIndexOf(':');
int valueEndIndex = line.lastIndexOf('T');
if (valueIndex < 0 || valueEndIndex < 0) {
throw new DavMailException("EXCEPTION_INVALID_ICS_LINE", line);
}
int keyIndex = line.indexOf(';');
if (keyIndex == -1) {
keyIndex = valueIndex;
}
String dateValue = line.substring(valueIndex + 1, valueEndIndex);
String key = line.substring(0, Math.min(keyIndex, valueIndex));
return key + ";VALUE=DATE:" + dateValue;
}
protected String fixTimezoneId(String line, String validTimezoneId) {
return StringUtil.replaceToken(line, "TZID=", ":", validTimezoneId);
}
protected String replaceIcal4Principal(String value) {
if (value.contains("/principals/__uuids__/")) {
return value.replaceAll("/principals/__uuids__/([^/]*)__AT__([^/]*)/", "mailto:$1@$2");
} else {
return value;
}
}
protected void splitExDate(ICSBufferedWriter result, String line) {
int cur = line.lastIndexOf(':') + 1;
String start = line.substring(0, cur);
for (int next = line.indexOf(',', cur); next != -1; next = line.indexOf(',', cur)) {
String val = line.substring(cur, next);
result.writeLine(start + val);
cur = next + 1;
}
result.writeLine(start + line.substring(cur));
}
protected String oldfixICS(String icsBody, boolean fromServer) throws IOException {
// first pass : detect
class AllDayState {
boolean isAllDay;
boolean hasCdoAllDay;
boolean isCdoAllDay;
}
// Convert event class from and to iCal
// See https://trac.calendarserver.org/browser/CalendarServer/trunk/doc/Extensions/caldav-privateevents.txt
boolean isAppleiCal = false;
boolean hasAttendee = false;
boolean hasCdoBusyStatus = false;
// detect ics event with empty timezone (all day from Lightning)
boolean hasTimezone = false;
String transp = null;
String validTimezoneId = null;
String eventClass = null;
String organizer = null;
String action = null;
String method = null;
boolean sound = false;
List<AllDayState> allDayStates = new ArrayList<AllDayState>();
AllDayState currentAllDayState = new AllDayState();
BufferedReader reader = null;
try {
reader = new ICSBufferedReader(new StringReader(icsBody));
String line;
while ((line = reader.readLine()) != null) {
int index = line.indexOf(':');
if (index >= 0) {
String key = line.substring(0, index);
String value = line.substring(index + 1);
if ("DTSTART;VALUE=DATE".equals(key)) {
currentAllDayState.isAllDay = true;
} else if ("X-MICROSOFT-CDO-ALLDAYEVENT".equals(key)) {
currentAllDayState.hasCdoAllDay = true;
currentAllDayState.isCdoAllDay = "TRUE".equals(value);
} else if ("END:VEVENT".equals(line)) {
allDayStates.add(currentAllDayState);
currentAllDayState = new AllDayState();
} else if ("PRODID".equals(key) && line.contains("iCal")) {
// detect iCal created events
isAppleiCal = true;
} else if (isAppleiCal && "X-CALENDARSERVER-ACCESS".equals(key)) {
eventClass = value;
} else if (!isAppleiCal && "CLASS".equals(key)) {
eventClass = value;
} else if ("ACTION".equals(key)) {
action = value;
} else if ("ATTACH;VALUES=URI".equals(key)) {
// This is a marker that this event has an alarm with sound
sound = true;
} else if (key.startsWith("ORGANIZER")) {
if (value.startsWith("MAILTO:")) {
organizer = value.substring(7);
} else {
organizer = value;
}
} else if (key.startsWith("ATTENDEE")) {
hasAttendee = true;
} else if ("TRANSP".equals(key)) {
transp = value;
} else if (line.startsWith("TZID:(GMT") ||
// additional test for Outlook created recurring events
line.startsWith("TZID:GMT ")) {
try {
validTimezoneId = ResourceBundle.getBundle("timezones").getString(value);
} catch (MissingResourceException mre) {
//LOGGER.warn(new BundleMessage("LOG_INVALID_TIMEZONE", value));
}
} else if ("X-MICROSOFT-CDO-BUSYSTATUS".equals(key)) {
hasCdoBusyStatus = true;
} else if ("BEGIN:VTIMEZONE".equals(line)) {
hasTimezone = true;
} else if ("METHOD".equals(key)) {
method = value;
}
}
}
} finally {
if (reader != null) {
reader.close();
}
}
// second pass : fix
int count = 0;
ICSBufferedWriter result = new ICSBufferedWriter();
try {
reader = new ICSBufferedReader(new StringReader(icsBody));
String line;
while ((line = reader.readLine()) != null) {
// remove empty properties
if ("CLASS:".equals(line) || "LOCATION:".equals(line)) {
continue;
}
// fix invalid exchange timezoneid
if (validTimezoneId != null && line.indexOf(";TZID=") >= 0) {
line = fixTimezoneId(line, validTimezoneId);
}
if (!fromServer && "BEGIN:VCALENDAR".equals(line) && method == null) {
result.writeLine(line);
// append missing method
if (method == null) {
result.writeLine("METHOD:PUBLISH");
}
continue;
}
if (fromServer && line.startsWith("PRODID:") && eventClass != null) {
result.writeLine(line);
// set global calendarserver access for iCal 4
if ("PRIVATE".equalsIgnoreCase(eventClass)) {
result.writeLine("X-CALENDARSERVER-ACCESS:CONFIDENTIAL");
} else if ("CONFIDENTIAL".equalsIgnoreCase(eventClass)) {
result.writeLine("X-CALENDARSERVER-ACCESS:PRIVATE");
} else if (eventClass != null) {
result.writeLine("X-CALENDARSERVER-ACCESS:" + eventClass);
}
continue;
}
if (!fromServer && "BEGIN:VEVENT".equals(line) && !hasTimezone) {
result.write("BEGIN:VTIMEZONE\nFAKE:FAKE\nEND:VTIMEZONE\n");
hasTimezone = true;
}
if (!fromServer && currentAllDayState.isAllDay && "X-MICROSOFT-CDO-ALLDAYEVENT:FALSE".equals(line)) {
line = "X-MICROSOFT-CDO-ALLDAYEVENT:TRUE";
} else if (!fromServer && "END:VEVENT".equals(line)) {
if (!hasCdoBusyStatus) {
result.writeLine("X-MICROSOFT-CDO-BUSYSTATUS:" + (!"TRANSPARENT".equals(transp) ? "BUSY" : "FREE"));
}
if (currentAllDayState.isAllDay && !currentAllDayState.hasCdoAllDay) {
result.writeLine("X-MICROSOFT-CDO-ALLDAYEVENT:TRUE");
}
// add organizer line to all events created in Exchange for active sync
if (organizer == null) {
result.writeLine("ORGANIZER:MAILTO:" + email);
}
if (isAppleiCal) {
if ("CONFIDENTIAL".equalsIgnoreCase(eventClass)) {
result.writeLine("CLASS:PRIVATE");
} else if ("PRIVATE".equalsIgnoreCase(eventClass)) {
result.writeLine("CLASS:CONFIDENTIAL");
} else if (eventClass != null) {
result.writeLine("CLASS:" + eventClass);
}
}
} else if (!fromServer && line.startsWith("X-MICROSOFT-CDO-BUSYSTATUS:")) {
line = "X-MICROSOFT-CDO-BUSYSTATUS:" + (!"TRANSPARENT".equals(transp) ? "BUSY" : "FREE");
} else if (!fromServer && !currentAllDayState.isAllDay && "X-MICROSOFT-CDO-ALLDAYEVENT:TRUE".equals(line)) {
line = "X-MICROSOFT-CDO-ALLDAYEVENT:FALSE";
} else if (fromServer && currentAllDayState.isCdoAllDay && line.startsWith("DTSTART") && !line.startsWith("DTSTART;VALUE=DATE")) {
line = getAllDayLine(line);
} else if (fromServer && currentAllDayState.isCdoAllDay && line.startsWith("DTEND") && !line.startsWith("DTEND;VALUE=DATE")) {
line = getAllDayLine(line);
} else if (!fromServer && currentAllDayState.isAllDay && line.startsWith("DTSTART") && line.startsWith("DTSTART;VALUE=DATE")) {
line = "DTSTART;TZID=\"" + "OWATZID" + "\":" + line.substring(19) + "T000000";
} else if (!fromServer && currentAllDayState.isAllDay && line.startsWith("DTEND") && line.startsWith("DTEND;VALUE=DATE")) {
line = "DTEND;TZID=\"" + "OWATZID" + "\":" + line.substring(17) + "T000000";
} else if (line.startsWith("TZID:") && validTimezoneId != null) {
line = "TZID:" + validTimezoneId;
} else if ("BEGIN:VEVENT".equals(line)) {
currentAllDayState = allDayStates.get(count++);
// remove calendarserver access
} else if (line.startsWith("X-CALENDARSERVER-ACCESS:")) {
continue;
} else if (line.startsWith("EXDATE;TZID=") || line.startsWith("EXDATE:")) {
// Apple iCal doesn't support EXDATE with multiple exceptions
// on one line. Split into multiple EXDATE entries (which is
// also legal according to the caldav standard).
splitExDate(result, line);
continue;
} else if (line.startsWith("X-ENTOURAGE_UUID:")) {
// Apple iCal doesn't understand this key, and it's entourage
// specific (i.e. not needed by any caldav client): strip it out
continue;
} else if (fromServer && line.startsWith("ATTENDEE;")
&& (line.indexOf(email) >= 0)) {
// If this is coming from the server, strip out RSVP for this
// user as an attendee where the partstat is something other
// than PARTSTAT=NEEDS-ACTION since the RSVP confuses iCal4 into
// thinking the attendee has not replied
int rsvpSuffix = line.indexOf("RSVP=TRUE;");
int rsvpPrefix = line.indexOf(";RSVP=TRUE");
if (((rsvpSuffix >= 0) || (rsvpPrefix >= 0))
&& (line.indexOf("PARTSTAT=") >= 0)
&& (line.indexOf("PARTSTAT=NEEDS-ACTION") < 0)) {
// Strip out the "RSVP" line from the calendar entry
if (rsvpSuffix >= 0) {
line = line.substring(0, rsvpSuffix) + line.substring(rsvpSuffix + 10);
} else {
line = line.substring(0, rsvpPrefix) + line.substring(rsvpPrefix + 10);
}
}
} else if (line.startsWith("ACTION:")) {
if (fromServer && "DISPLAY".equals(action)
// convert DISPLAY to AUDIO only if user defined an alarm sound
&& Settings.getProperty("davmail.caldavAlarmSound") != null) {
// Convert alarm to audio for iCal
result.writeLine("ACTION:AUDIO");
if (!sound) {
// Add defined sound into the audio alarm
result.writeLine("ATTACH;VALUE=URI:" + Settings.getProperty("davmail.caldavAlarmSound"));
}
continue;
} else if (!fromServer && "AUDIO".equals(action)) {
// Use the alarm action that exchange (and blackberry) understand
// (exchange and blackberry don't understand audio actions)
result.writeLine("ACTION:DISPLAY");
continue;
}
// Don't recognize this type of action: pass it through
} else if (line.startsWith("CLASS:")) {
if (!fromServer && isAppleiCal) {
continue;
} else {
// still set calendarserver access inside event for iCal 3
if ("PRIVATE".equalsIgnoreCase(eventClass)) {
result.writeLine("X-CALENDARSERVER-ACCESS:CONFIDENTIAL");
} else if ("CONFIDENTIAL".equalsIgnoreCase(eventClass)) {
result.writeLine("X-CALENDARSERVER-ACCESS:PRIVATE");
} else {
result.writeLine("X-CALENDARSERVER-ACCESS:" + eventClass);
}
}
// remove organizer line if user is organizer for iPhone
} else if (fromServer && line.startsWith("ORGANIZER") && !hasAttendee) {
continue;
} else if (organizer != null && line.startsWith("ATTENDEE") && line.contains(organizer)) {
// Ignore organizer as attendee
continue;
} else if (!fromServer && line.startsWith("ATTENDEE")) {
line = replaceIcal4Principal(line);
}
result.writeLine(line);
}
} finally {
reader.close();
}
String resultString = result.toString();
return resultString;
}
protected String fixICS(String icsBody, boolean fromServer) throws IOException {
VCalendar vCalendar = new VCalendar(icsBody, email);
vCalendar.fixVCalendar(fromServer);
return vCalendar.toString();
}
public void testNoClass() throws IOException {
String itemBody = "BEGIN:VCALENDAR\n" +
"BEGIN:VEVENT\n" +
"END:VEVENT\n" +
"END:VCALENDAR";
String toServer = fixICS(itemBody, false);
String toClient = fixICS(itemBody, true);
System.out.println(toServer);
System.out.println(toClient);
assertTrue(toServer.indexOf("CLASS") < 0);
assertTrue(toClient.indexOf("CLASS") < 0);
assertTrue(toClient.indexOf("X-CALENDARSERVER-ACCESS") < 0);
}
public void testPublicClass() throws IOException {
String itemBody = "BEGIN:VCALENDAR\n" +
"BEGIN:VEVENT\n" +
"CLASS:PUBLIC\n" +
"END:VEVENT\n" +
"END:VCALENDAR";
String toServer = fixICS(itemBody, false);
String toClient = fixICS(itemBody, true);
System.out.println(toServer);
System.out.println(toClient);
assertTrue(toServer.indexOf("CLASS:PUBLIC") >= 0);
assertTrue(toClient.indexOf("CLASS:PUBLIC") >= 0);
assertTrue(toClient.indexOf("X-CALENDARSERVER-ACCESS:PUBLIC") >= 0);
}
public void testPrivateClass() throws IOException {
String itemBody = "BEGIN:VCALENDAR\n" +
"BEGIN:VEVENT\n" +
"CLASS:PRIVATE\n" +
"END:VEVENT\n" +
"END:VCALENDAR";
String toServer = fixICS(itemBody, false);
String toClient = fixICS(itemBody, true);
System.out.println(toServer);
System.out.println(toClient);
assertTrue(toServer.indexOf("CLASS:PRIVATE") >= 0);
assertTrue(toClient.indexOf("CLASS:PRIVATE") >= 0);
assertTrue(toClient.indexOf("X-CALENDARSERVER-ACCESS:CONFIDENTIAL") >= 0);
}
public void testConfidentialClass() throws IOException {
String itemBody = "BEGIN:VCALENDAR\n" +
"BEGIN:VEVENT\n" +
"CLASS:CONFIDENTIAL\n" +
"END:VEVENT\n" +
"END:VCALENDAR";
String toServer = fixICS(itemBody, false);
String toClient = fixICS(itemBody, true);
System.out.println(toServer);
System.out.println(toClient);
assertTrue(toServer.indexOf("CLASS:CONFIDENTIAL") >= 0);
assertTrue(toClient.indexOf("CLASS:CONFIDENTIAL") >= 0);
assertTrue(toClient.indexOf("X-CALENDARSERVER-ACCESS:PRIVATE") >= 0);
}
public void testCalendarServerAccessPrivate() throws IOException {
String itemBody = "BEGIN:VCALENDAR\n" +
"PRODID:-//Apple Inc.//iCal 4.0.3//EN\n" +
"BEGIN:VEVENT\n" +
"X-CALENDARSERVER-ACCESS:PRIVATE\n" +
"END:VEVENT\n" +
"END:VCALENDAR";
String toServer = fixICS(itemBody, false);
System.out.println(toServer);
assertTrue(toServer.indexOf("CLASS:CONFIDENTIAL") >= 0);
}
public void testCalendarServerAccessConfidential() throws IOException {
String itemBody = "BEGIN:VCALENDAR\n" +
"PRODID:-//Apple Inc.//iCal 4.0.3//EN\n" +
"BEGIN:VEVENT\n" +
"X-CALENDARSERVER-ACCESS:CONFIDENTIAL\n" +
"END:VEVENT\n" +
"END:VCALENDAR";
String toServer = fixICS(itemBody, false);
System.out.println(toServer);
assertTrue(toServer.indexOf("CLASS:PRIVATE") >= 0);
}
public void testCalendarServerAccessPublic() throws IOException {
String itemBody = "BEGIN:VCALENDAR\n" +
"PRODID:-//Apple Inc.//iCal 4.0.3//EN\n" +
"BEGIN:VEVENT\n" +
"X-CALENDARSERVER-ACCESS:PUBLIC\n" +
"END:VEVENT\n" +
"END:VCALENDAR";
String toServer = fixICS(itemBody, false);
System.out.println(toServer);
assertTrue(toServer.indexOf("CLASS:PUBLIC") >= 0);
}
public void testCalendarServerAccessNone() throws IOException {
String itemBody = "BEGIN:VCALENDAR\n" +
"PRODID:-//Apple Inc.//iCal 4.0.3//EN\n" +
"BEGIN:VEVENT\n" +
"END:VEVENT\n" +
"END:VCALENDAR";
String toServer = fixICS(itemBody, false);
System.out.println(toServer);
assertFalse(toServer.contains("CLASS"));
}
public void testNoOrganizer() throws IOException {
String itemBody = "BEGIN:VCALENDAR\n" +
"BEGIN:VEVENT\n" +
"END:VEVENT\n" +
"END:VCALENDAR";
String toServer = fixICS(itemBody, false);
System.out.println(toServer);
assertTrue(toServer.contains("ORGANIZER"));
}
}