use of com.zimbra.common.calendar.ZCalendar.ICalTok in project zm-mailbox by Zimbra.
the class FreeBusy method parse.
/**
* Create a FreeBusy object from VFREEBUSY ZComponent.
* @param comp
* @return
* @throws ServiceException
*/
public static FreeBusy parse(ZComponent comp) throws ServiceException {
String name = null;
ParsedDateTime dtStart = null;
ParsedDateTime dtEnd = null;
List<Interval> intervals = new ArrayList<Interval>();
TimeZoneMap tzmap = new TimeZoneMap(ICalTimeZone.getUTC());
Iterator<ZProperty> propIter = comp.getPropertyIterator();
while (propIter.hasNext()) {
ZProperty prop = propIter.next();
ICalTok tok = prop.getToken();
if (tok == null)
continue;
switch(tok) {
case DTSTART:
try {
dtStart = ParsedDateTime.parse(prop, tzmap);
} catch (ParseException e) {
throw ServiceException.INVALID_REQUEST("bad DTSTART: " + prop.toString(), e);
}
break;
case DTEND:
try {
dtEnd = ParsedDateTime.parse(prop, tzmap);
} catch (ParseException e) {
throw ServiceException.INVALID_REQUEST("bad DTEND: " + prop.toString(), e);
}
break;
case ORGANIZER:
ZOrganizer att = new ZOrganizer(prop);
name = att.getAddress();
break;
case FREEBUSY:
String fbStatus = IcalXmlStrMap.FBTYPE_FREE;
ZParameter fbType = prop.getParameter(ICalTok.FBTYPE);
if (fbType != null)
fbStatus = IcalXmlStrMap.sFreeBusyMap.toXml(fbType.getValue());
List<String> vals = prop.getValueList();
for (String fbVal : vals) {
Period period;
try {
period = Period.parse(fbVal, ICalTimeZone.getUTC(), tzmap);
} catch (ParseException e) {
throw ServiceException.INVALID_REQUEST("bad period value: " + fbVal, e);
}
intervals.add(new Interval(period.getStart().getUtcTime(), period.getEnd().getUtcTime(), fbStatus));
}
break;
}
}
if (name == null)
throw ServiceException.INVALID_REQUEST("VFREEBUSY missing ORGANIZER", null);
if (dtStart == null || dtEnd == null)
throw ServiceException.INVALID_REQUEST("VFREEBUSY missing DTSTART/DTEND", null);
IntervalList ivalList = new IntervalList(dtStart.getUtcTime(), dtEnd.getUtcTime());
for (Interval ival : intervals) {
ivalList.addInterval(ival);
}
return new FreeBusy(name, ivalList, dtStart.getUtcTime(), dtEnd.getUtcTime());
}
use of com.zimbra.common.calendar.ZCalendar.ICalTok in project zm-mailbox by Zimbra.
the class ScheduleOutbox method handlePost.
@Override
public void handlePost(DavContext ctxt) throws DavException, IOException, ServiceException {
DelegationInfo delegationInfo = new DelegationInfo(ctxt.getRequest().getHeader(DavProtocol.HEADER_ORIGINATOR));
Enumeration<String> recipients = ctxt.getRequest().getHeaders(DavProtocol.HEADER_RECIPIENT);
InputStream in = ctxt.getUpload().getInputStream();
ZCalendar.ZVCalendar vcalendar = ZCalendar.ZCalendarBuilder.build(in, MimeConstants.P_CHARSET_UTF8);
Closeables.closeQuietly(in);
Iterator<ZComponent> iter = vcalendar.getComponentIterator();
ZComponent req = null;
while (iter.hasNext()) {
req = iter.next();
if (req.getTok() != ICalTok.VTIMEZONE)
break;
req = null;
}
if (req == null) {
throw new DavException("empty request", HttpServletResponse.SC_BAD_REQUEST);
}
ZimbraLog.dav.debug("originator: %s", delegationInfo.getOriginator());
boolean isVEventOrVTodo = ICalTok.VEVENT.equals(req.getTok()) || ICalTok.VTODO.equals(req.getTok());
boolean isOrganizerMethod = false, isCancel = false;
if (isVEventOrVTodo) {
String method = vcalendar.getPropVal(ICalTok.METHOD, null);
if (method != null) {
isOrganizerMethod = Invite.isOrganizerMethod(method);
isCancel = ICalTok.CANCEL.toString().equalsIgnoreCase(method);
;
}
// Apple iCal fixup
CalDavUtils.removeAttendeeForOrganizer(req);
}
// Get organizer and list of attendees. (mailto:email values)
ArrayList<String> attendees = new ArrayList<String>();
String organizer = null;
for (Iterator<ZProperty> propsIter = req.getPropertyIterator(); propsIter.hasNext(); ) {
ZProperty prop = propsIter.next();
ICalTok token = prop.getToken();
if (ICalTok.ATTENDEE.equals(token)) {
String val = prop.getValue();
if (val != null) {
attendees.add(val.trim());
}
} else if (ICalTok.ORGANIZER.equals(token)) {
String val = prop.getValue();
if (val != null) {
organizer = val.trim();
String addr = CalDavUtils.stripMailto(organizer);
// Rewrite the alias to primary address
Account acct = Provisioning.getInstance().get(AccountBy.name, addr);
if (acct != null) {
String newAddr = acct.getName();
if (!addr.equals(newAddr)) {
organizer = "mailto:" + newAddr;
prop.setValue(organizer);
}
}
}
}
}
// Apple iCal is very inconsistent about the user's identity when the account has aliases.
if (isVEventOrVTodo && delegationInfo.getOriginator() != null && ctxt.getAuthAccount() != null) {
AccountAddressMatcher acctMatcher = new AccountAddressMatcher(ctxt.getAuthAccount());
if (acctMatcher.matches(delegationInfo.getOriginatorEmail())) {
if (isOrganizerMethod) {
if (organizer != null) {
String organizerEmail = CalDavUtils.stripMailto(organizer);
if (!organizerEmail.equalsIgnoreCase(delegationInfo.getOriginatorEmail()) && acctMatcher.matches(organizerEmail)) {
delegationInfo.setOriginator(organizer);
ZimbraLog.dav.debug("changing originator to %s to match address/alias used in ORGANIZER", delegationInfo.getOriginator());
}
}
} else {
for (String at : attendees) {
String atEmail = CalDavUtils.stripMailto(at);
if (delegationInfo.getOriginatorEmail().equalsIgnoreCase(atEmail)) {
break;
} else if (acctMatcher.matches(atEmail)) {
delegationInfo.setOriginator(at);
ZimbraLog.dav.debug("changing originator to %s to match address/alias used in ATTENDEE", delegationInfo.getOriginator());
break;
}
}
}
}
}
// Get the recipients.
ArrayList<String> rcptArray = new ArrayList<String>();
while (recipients.hasMoreElements()) {
String rcptHdr = recipients.nextElement();
String[] rcpts = null;
if (rcptHdr.indexOf(',') > 0) {
rcpts = rcptHdr.split(",");
} else {
rcpts = new String[] { rcptHdr };
}
for (String rcpt : rcpts) {
if (rcpt != null) {
rcpt = rcpt.trim();
if (rcpt.length() != 0) {
// Workaround for Apple iCal: Ignore attendees with address "invalid:nomail".
if (rcpt.equalsIgnoreCase("invalid:nomail")) {
continue;
}
if (isVEventOrVTodo) {
// iCal can sometimes do that when organizer account has aliases.
if (isOrganizerMethod && rcpt.equalsIgnoreCase(organizer)) {
continue;
}
// as ATTENDEE in the CANCEL component being sent. (iCal does that part correctly, at least.)
if (isCancel) {
boolean isAttendee = false;
// Rcpt must be an attendee of the cancel component.
for (String at : attendees) {
if (rcpt.equalsIgnoreCase(at)) {
isAttendee = true;
break;
}
}
if (!isAttendee) {
ZimbraLog.dav.info("Ignoring non-attendee recipient '%s' of CANCEL request; likely a client bug", rcpt);
continue;
}
}
}
// All checks passed.
rcptArray.add(rcpt);
}
}
}
}
Element scheduleResponse = ctxt.getDavResponse().getTop(DavElements.E_SCHEDULE_RESPONSE);
for (String rcpt : rcptArray) {
ZimbraLog.dav.debug("recipient email: " + rcpt);
Element resp = scheduleResponse.addElement(DavElements.E_CALDAV_RESPONSE);
switch(req.getTok()) {
case VFREEBUSY:
handleFreebusyRequest(ctxt, req, delegationInfo.getOriginator(), rcpt, resp);
break;
case VEVENT:
// records.
if (isOrganizerMethod) {
adjustOrganizer(ctxt, vcalendar, req, delegationInfo);
}
validateRequest(isOrganizerMethod, delegationInfo, organizer, ctxt, req);
handleEventRequest(ctxt, vcalendar, req, delegationInfo, rcpt, resp);
break;
default:
throw new DavException("unrecognized request: " + req.getTok(), HttpServletResponse.SC_BAD_REQUEST);
}
}
}
use of com.zimbra.common.calendar.ZCalendar.ICalTok in project zm-mailbox by Zimbra.
the class ICalTimeZone method fromVTimeZone.
public static ICalTimeZone fromVTimeZone(ZComponent comp, boolean skipLookup, TZID_NAME_ASSIGNMENT_BEHAVIOR tzidNameAssigmentBehavior) throws ServiceException {
String tzid = comp.getPropVal(ICalTok.TZID, null);
if (!(skipLookup || ("GMT".equals(tzid)))) {
ICalTimeZone tz = lookupByTZID(tzid);
if (tz != null) {
return tz;
}
}
ZComponent standard = null;
ZComponent daylight = null;
// allow historical information.
for (Iterator<ZComponent> iter = comp.getComponentIterator(); iter.hasNext(); ) {
ZComponent tzComp = iter.next();
if (tzComp == null)
continue;
ICalTok tok = tzComp.getTok();
if (ICalTok.STANDARD.equals(tok)) {
if (standard == null) {
standard = tzComp;
continue;
} else
standard = moreRecentTzComp(standard, tzComp);
} else if (ICalTok.DAYLIGHT.equals(tok)) {
if (daylight == null) {
daylight = tzComp;
continue;
} else
daylight = moreRecentTzComp(daylight, tzComp);
}
}
// created by Apple iCal; see bug 7335.)
if (standard != null && daylight != null) {
String stdRule = standard.getPropVal(ICalTok.RRULE, null);
String dayRule = daylight.getPropVal(ICalTok.RRULE, null);
// lenient and treat it as if both were null.
if (stdRule == null || dayRule == null) {
String stdStart = standard.getPropVal(ICalTok.DTSTART, null);
String dayStart = daylight.getPropVal(ICalTok.DTSTART, null);
if (stdStart != null && dayStart != null) {
try {
// stdStart = yyyymmddThhmiss
// stdStartMMDD = mmdd
String stdStartMMDD = stdStart.substring(4, 8);
String dayStartMMDD = dayStart.substring(4, 8);
if (stdStartMMDD.equals(dayStartMMDD)) {
standard = moreRecentTzComp(standard, daylight);
daylight = null;
}
} catch (StringIndexOutOfBoundsException e) {
// DTSTART values must have been malformed. Just do
// something reasonable and go on.
standard = moreRecentTzComp(standard, daylight);
daylight = null;
}
}
}
}
// If only DAYLIGHT is given, make it the STANDARD.
if (standard == null) {
standard = daylight;
daylight = null;
}
if (standard == null) {
throw new IllegalArgumentException("VTIMEZONE has neither STANDARD nor DAYLIGHT: TZID=" + tzid);
}
String stddtStart = null;
int stdoffsetTime = 0;
String stdrrule = null;
String stdTzname = null;
stddtStart = standard.getPropVal(ICalTok.DTSTART, null);
String stdtzOffsetTo = standard.getPropVal(ICalTok.TZOFFSETTO, null);
stdoffsetTime = tzOffsetTime(stdtzOffsetTo);
stdTzname = standard.getPropVal(ICalTok.TZNAME, null);
// Check if STANDARD defines a non-DST timezone. If so, DAYLIGHT should be ignored if its
// DTSTART is later than that of STANDARD. (bug 25176)
String stdtzOffsetFrom = standard.getPropVal(ICalTok.TZOFFSETFROM, null);
if (stdtzOffsetFrom != null) {
int tzoffsetFromTime = tzOffsetTime(stdtzOffsetFrom);
if (tzoffsetFromTime == stdoffsetTime && daylight != null) {
ZComponent moreRecent = moreRecentTzComp(standard, daylight);
if (moreRecent == standard)
daylight = null;
}
}
if (daylight != null) {
// Rule is interesting only if daylight savings is in use.
stdrrule = standard.getPropVal(ICalTok.RRULE, null);
}
String daydtStart = null;
int dayoffsetTime = stdoffsetTime;
String dayrrule = null;
String dayTzname = null;
if (daylight != null) {
daydtStart = daylight.getPropVal(ICalTok.DTSTART, null);
String daytzOffsetTo = daylight.getPropVal(ICalTok.TZOFFSETTO, null);
dayoffsetTime = tzOffsetTime(daytzOffsetTo);
dayrrule = daylight.getPropVal(ICalTok.RRULE, null);
dayTzname = daylight.getPropVal(ICalTok.TZNAME, null);
// Check if DAYLIGHT defines a non-DST timezone. If so and its DTSTART is later than
// that of STANDARD, STANDARD should be discarded and DAYLIGHT should be used in its place.
// Such a VTIMEZONE is invalid, but it's a possibility and we should do something reasonable.
// This is the inverse of the case above with non-DST STANDARD with useless DAYLIGHT. (bug 25176)
String daytzOffsetFrom = daylight.getPropVal(ICalTok.TZOFFSETFROM, null);
if (daytzOffsetFrom != null) {
int tzoffsetFromTime = tzOffsetTime(daytzOffsetFrom);
if (tzoffsetFromTime == dayoffsetTime && standard != null) {
ZComponent moreRecent = moreRecentTzComp(standard, daylight);
if (moreRecent == daylight) {
// Make DAYLIGHT the new STANDARD.
standard = daylight;
stdoffsetTime = dayoffsetTime;
stddtStart = daydtStart;
stdrrule = dayrrule;
stdTzname = dayTzname;
daylight = null;
daydtStart = null;
dayrrule = null;
dayTzname = null;
}
}
}
}
ICalTimeZone newTz = new ICalTimeZone(tzid, stdoffsetTime, stddtStart, stdrrule, stdTzname, dayoffsetTime, daydtStart, dayrrule, dayTzname);
if (!skipLookup) {
newTz = lookupByRule(newTz, tzidNameAssigmentBehavior);
}
return newTz;
}
use of com.zimbra.common.calendar.ZCalendar.ICalTok in project zm-mailbox by Zimbra.
the class Invite method lookupMethod.
public static ICalTok lookupMethod(String methodName) {
ICalTok toRet;
// work around livemeeting.com bug
String methodNameUpper = methodName.toUpperCase();
try {
toRet = ICalTok.valueOf(methodNameUpper);
} catch (IllegalArgumentException e) {
toRet = ICalTok.PUBLISH;
// Apple iCal generates non-standard "METHOD:EXPORT".
if (methodNameUpper.compareToIgnoreCase("EXPORT") != 0)
sLog.warn("Invalid METHOD " + methodName + "; assuming PUBLISH", e);
}
switch(toRet) {
case REQUEST:
case PUBLISH:
case REPLY:
case ADD:
case CANCEL:
case REFRESH:
case COUNTER:
case DECLINECOUNTER:
return toRet;
default:
return ICalTok.PUBLISH;
}
}
use of com.zimbra.common.calendar.ZCalendar.ICalTok in project zm-mailbox by Zimbra.
the class Invite method newToVComponent.
public ZComponent newToVComponent(boolean useOutlookCompatAllDayEvents, boolean includePrivateData, boolean includeAttaches) throws ServiceException {
boolean isRequestPublishCancel = ICalTok.REQUEST.equals(mMethod) || ICalTok.PUBLISH.equals(mMethod) || ICalTok.CANCEL.equals(mMethod);
ICalTok compTok;
if (type == MailItem.Type.TASK) {
compTok = ICalTok.VTODO;
useOutlookCompatAllDayEvents = false;
} else {
compTok = ICalTok.VEVENT;
}
ZComponent component = new ZComponent(compTok);
component.addProperty(new ZProperty(ICalTok.UID, getUid()));
IRecurrence recur = getRecurrence();
if (recur != null) {
for (Iterator iter = recur.addRulesIterator(); iter != null && iter.hasNext(); ) {
IRecurrence cur = (IRecurrence) iter.next();
switch(cur.getType()) {
case Recurrence.TYPE_SINGLE_DATES:
if (DebugConfig.enableRdate) {
Recurrence.SingleDates sd = (Recurrence.SingleDates) cur;
RdateExdate rdate = sd.getRdateExdate();
rdate.addAsSeparateProperties(component);
}
break;
case Recurrence.TYPE_REPEATING:
Recurrence.SimpleRepeatingRule srr = (Recurrence.SimpleRepeatingRule) cur;
component.addProperty(new ZProperty(ICalTok.RRULE, srr.getRule().toString()));
break;
}
}
for (Iterator iter = recur.subRulesIterator(); iter != null && iter.hasNext(); ) {
IRecurrence cur = (IRecurrence) iter.next();
switch(cur.getType()) {
case Recurrence.TYPE_SINGLE_DATES:
Recurrence.SingleDates sd = (Recurrence.SingleDates) cur;
RdateExdate exdate = sd.getRdateExdate();
exdate.addAsSeparateProperties(component);
break;
case Recurrence.TYPE_REPEATING:
Recurrence.SimpleRepeatingRule srr = (Recurrence.SimpleRepeatingRule) cur;
component.addProperty(new ZProperty(ICalTok.EXRULE, srr.getRule().toString()));
break;
}
}
}
if (includePrivateData || isPublic()) {
// SUMMARY (aka Name or Subject)
String name = getName();
if (name != null && name.length() > 0)
component.addProperty(new ZProperty(ICalTok.SUMMARY, name));
// DESCRIPTION and X-ALT-DESC;FMTTYPE=text/html
String desc = getDescription();
if (desc != null) {
// Remove Outlook-style *~*~*~ header block. Remove separator plus two newlines.
int delim = desc.indexOf(HEADER_SEPARATOR);
if (delim >= 0) {
desc = desc.substring(delim + HEADER_SEPARATOR.length());
desc = desc.replaceFirst("^\\r?\\n\\r?\\n", "");
}
if (desc.length() > 0)
component.addProperty(new ZProperty(ICalTok.DESCRIPTION, desc));
}
String descHtml = getDescriptionHtml();
if (descHtml != null && descHtml.length() > 0) {
ZProperty altDesc = new ZProperty(ICalTok.X_ALT_DESC, descHtml);
altDesc.addParameter(new ZParameter(ICalTok.FMTTYPE, MimeConstants.CT_TEXT_HTML));
component.addProperty(altDesc);
}
// COMMENT
List<String> comments = getComments();
if (comments != null && !comments.isEmpty()) {
for (String comment : comments) {
component.addProperty(new ZProperty(ICalTok.COMMENT, comment));
}
}
// LOCATION
String location = getLocation();
if (location != null && location.length() > 0)
component.addProperty(new ZProperty(ICalTok.LOCATION, location.toString()));
// ATTENDEES
for (ZAttendee at : getAttendees()) {
component.addProperty(at.toProperty());
}
// PRIORITY
if (mPriority != null)
component.addProperty(new ZProperty(ICalTok.PRIORITY, mPriority));
// PERCENT-COMPLETE
if (isTodo() && mPercentComplete != null)
component.addProperty(new ZProperty(ICalTok.PERCENT_COMPLETE, mPercentComplete));
// COMPLETED
if (isTodo() && mCompleted != 0) {
ParsedDateTime completed = ParsedDateTime.fromUTCTime(mCompleted);
component.addProperty(completed.toProperty(ICalTok.COMPLETED, false));
}
// CATEGORIES
List<String> categories = getCategories();
if (categories != null && !categories.isEmpty()) {
ZProperty catsProp = new ZProperty(ICalTok.CATEGORIES);
catsProp.setValueList(categories);
component.addProperty(catsProp);
}
// CONTACT
List<String> contacts = getContacts();
if (contacts != null && !contacts.isEmpty()) {
for (String contact : contacts) {
component.addProperty(new ZProperty(ICalTok.CONTACT, contact));
}
}
// GEO
if (mGeo != null)
component.addProperty(mGeo.toZProperty());
// VALARMs
for (Alarm alarm : mAlarms) {
ZComponent alarmComp = alarm.toZComponent();
component.addComponent(alarmComp);
}
// x-prop
for (ZProperty xprop : mXProps) {
component.addProperty(xprop);
}
// ORGANIZER
if (hasOrganizer()) {
ZOrganizer organizer = getOrganizer();
ZProperty orgProp = organizer.toProperty();
component.addProperty(orgProp);
// Hack for Outlook 2007 (bug 25777)
if (organizer.hasSentBy() && !ICalTok.REPLY.equals(mMethod) && !ICalTok.COUNTER.equals(mMethod)) {
String sentByParam = orgProp.paramVal(ICalTok.SENT_BY, null);
if (sentByParam != null) {
ZProperty xMsOlkSender = new ZProperty("X-MS-OLK-SENDER");
xMsOlkSender.setValue(sentByParam);
component.addProperty(xMsOlkSender);
}
}
}
}
// DTSTART
ParsedDateTime dtstart = getStartTime();
if (dtstart != null)
component.addProperty(dtstart.toProperty(ICalTok.DTSTART, useOutlookCompatAllDayEvents));
// DTEND or DUE
ParsedDateTime dtend = getEndTime();
if (dtend != null) {
ICalTok prop = ICalTok.DTEND;
if (isTodo())
prop = ICalTok.DUE;
component.addProperty(dtend.toProperty(prop, useOutlookCompatAllDayEvents));
}
// DURATION
ParsedDuration dur = getDuration();
if (dur != null)
component.addProperty(new ZProperty(ICalTok.DURATION, dur.toString()));
// STATUS
String status = getStatus();
String statusIcal = IcalXmlStrMap.sStatusMap.toIcal(status);
if (IcalXmlStrMap.STATUS_ZCO_WAITING.equals(status) || IcalXmlStrMap.STATUS_ZCO_DEFERRED.equals(status)) {
ZParameter param = new ZParameter(ICalTok.X_ZIMBRA_STATUS, statusIcal);
ZProperty prop = new ZProperty(ICalTok.STATUS, ICalTok.IN_PROCESS.toString());
prop.addParameter(param);
component.addProperty(prop);
} else {
component.addProperty(new ZProperty(ICalTok.STATUS, statusIcal));
}
// CLASS
component.addProperty(new ZProperty(ICalTok.CLASS, IcalXmlStrMap.sClassMap.toIcal(getClassProp())));
if (isEvent()) {
// allDay
if (isAllDayEvent())
component.addProperty(new ZProperty(ICalTok.X_MICROSOFT_CDO_ALLDAYEVENT, true));
// Microsoft Outlook compatibility for free-busy status
if (isRequestPublishCancel) {
String outlookFreeBusy = IcalXmlStrMap.sOutlookFreeBusyMap.toIcal(getFreeBusy());
component.addProperty(new ZProperty(ICalTok.X_MICROSOFT_CDO_INTENDEDSTATUS, outlookFreeBusy));
}
// TRANSPARENCY
component.addProperty(new ZProperty(ICalTok.TRANSP, IcalXmlStrMap.sTranspMap.toIcal(getTransparency())));
}
// RECURRENCE-ID
RecurId recurId = getRecurId();
if (recurId != null)
component.addProperty(recurId.toProperty(useOutlookCompatAllDayEvents));
// LAST-MODIFIED
long lastModified = getLastModified();
if (lastModified != 0) {
ParsedDateTime dtLastModified = ParsedDateTime.fromUTCTime(lastModified);
component.addProperty(dtLastModified.toProperty(ICalTok.LAST_MODIFIED, false));
}
// DTSTAMP
ParsedDateTime dtStamp = ParsedDateTime.fromUTCTime(getDTStamp());
component.addProperty(dtStamp.toProperty(ICalTok.DTSTAMP, false));
// SEQUENCE
component.addProperty(new ZProperty(ICalTok.SEQUENCE, getSeqNo()));
// URL
String url = getUrl();
if (url != null && url.length() > 0)
component.addProperty(new ZProperty(ICalTok.URL, url));
if (isLocalOnly())
component.addProperty(new ZProperty(ICalTok.X_ZIMBRA_LOCAL_ONLY, true));
if (includeAttaches) {
addInlineATTACHes(component);
}
return component;
}
Aggregations