Search in sources :

Example 1 with Categories

use of net.fortuna.ical4j.model.property.Categories in project zm-mailbox by Zimbra.

the class DefaultTnefToICalendar method convert.

/* (non-Javadoc)
     * @see com.zimbra.cs.util.tnef.TnefToICalendar#convert(java.io.InputStream, net.fortuna.ical4j.data.ContentHandler)
     */
public boolean convert(MimeMessage mimeMsg, InputStream tnefInput, ContentHandler icalOutput) throws ServiceException {
    boolean conversionSuccessful = false;
    recurDef = null;
    TNEFInputStream tnefStream = null;
    SchedulingViewOfTnef schedView = null;
    Integer sequenceNum = 0;
    icalType = ICALENDAR_TYPE.VEVENT;
    try {
        tnefStream = new TNEFInputStream(tnefInput);
        schedView = new SchedulingViewOfTnef(tnefStream);
        String msgClass = schedView.getMessageClass();
        if (msgClass == null) {
            sLog.debug("Unable to determine Class of TNEF - cannot generate ICALENDER equivalent");
            // throw TNEFtoIcalendarServiceException.NON_CALENDARING_CLASS(msgClass);
            return false;
        }
        icalType = schedView.getIcalType();
        method = null;
        PartStat partstat = null;
        boolean replyWanted = schedView.getResponseRequested();
        Boolean isCounterProposal = schedView.isCounterProposal();
        if (msgClass != null) {
            // just in case.
            if (msgClass.startsWith("IPM.Microsoft Schedule.MtgReq")) {
                method = Method.REQUEST;
                partstat = PartStat.NEEDS_ACTION;
            } else if (msgClass.startsWith("IPM.Microsoft Schedule.MtgRespP")) {
                method = Method.REPLY;
                partstat = PartStat.ACCEPTED;
                replyWanted = false;
            } else if (msgClass.startsWith("IPM.Microsoft Schedule.MtgRespN")) {
                method = Method.REPLY;
                partstat = PartStat.DECLINED;
                replyWanted = false;
            } else if (msgClass.startsWith("IPM.Microsoft Schedule.MtgRespA")) {
                if ((isCounterProposal != null) && isCounterProposal) {
                    method = Method.COUNTER;
                } else {
                    method = Method.REPLY;
                }
                partstat = PartStat.TENTATIVE;
                replyWanted = false;
            } else if (msgClass.startsWith("IPM.Microsoft Schedule.MtgCncl")) {
                method = Method.CANCEL;
                replyWanted = false;
            } else if (msgClass.startsWith("IPM.TaskRequest.Accept")) {
                method = Method.REPLY;
                partstat = PartStat.ACCEPTED;
                replyWanted = false;
            } else if (msgClass.startsWith("IPM.TaskRequest.Decline")) {
                method = Method.REPLY;
                partstat = PartStat.DECLINED;
                replyWanted = false;
            } else if (msgClass.startsWith("IPM.TaskRequest.Update")) {
                method = Method.REPLY;
                // May be overridden?
                partstat = PartStat.IN_PROCESS;
                replyWanted = false;
            } else if (msgClass.startsWith("IPM.TaskRequest")) {
                method = Method.REQUEST;
                partstat = PartStat.NEEDS_ACTION;
                replyWanted = true;
            }
        }
        if (method == null) {
            sLog.debug("Unable to map class %s to ICALENDER", msgClass);
            return false;
        //                throw TNEFtoIcalendarServiceException.NON_CALENDARING_CLASS(msgClass);
        }
        if (icalType == ICALENDAR_TYPE.VTODO) {
            List<?> attaches = (List<?>) schedView.getAttachments();
            if (attaches == null) {
                sLog.debug("Unable to map class %s to ICALENDER - no attachments", msgClass);
                return false;
            }
            schedView = null;
            for (Object obj : attaches) {
                if (obj instanceof Attachment) {
                    Attachment currAttach = (Attachment) obj;
                    MAPIProps attachMPs = currAttach.getMAPIProps();
                    if (attachMPs != null) {
                        MAPIProp attachData = attachMPs.getProp(MAPIProp.PR_ATTACH_DATA_OBJ);
                        if (attachData != null) {
                            Object theVal = attachData.getValue();
                            if ((theVal != null) && (theVal instanceof TNEFInputStream)) {
                                TNEFInputStream tnefSubStream = (TNEFInputStream) theVal;
                                schedView = new SchedulingViewOfTnef(tnefSubStream);
                                break;
                            }
                        }
                    }
                }
            }
            if (schedView == null) {
                sLog.debug("Unable to map class %s to ICALENDER - no properties found for sub-msg", msgClass);
                return false;
            }
        }
        uid = schedView.getIcalUID();
        sequenceNum = schedView.getSequenceNumber();
        boolean reminderSet = schedView.getReminderSet();
        String location = schedView.getLocation();
        Boolean isAllDayEvent = schedView.isAllDayEvent();
        Integer importance = schedView.getMapiImportance();
        Clazz icalClass = schedView.getIcalClass();
        Integer ownerApptId = schedView.getOwnerAppointmentId();
        EnumSet<MeetingTypeFlag> meetingTypeFlags = schedView.getMeetingTypeFlags();
        BusyStatus busyStatus = schedView.getBusyStatus();
        BusyStatus intendedBusyStatus = schedView.getIntendedBusyStatus();
        // For some ICAL properties like TRANSP and STATUS, intendedBusyStatus
        // seems closer to what is intended than straight busyStatus
        BusyStatus bestBusyStatus = intendedBusyStatus;
        if (bestBusyStatus == null) {
            bestBusyStatus = busyStatus;
        }
        // An algorithm is used to choose the values for these
        // TimeZoneDefinitions  - they don't necessarily map to single
        // MAPI properties
        TimeZoneDefinition startTimeTZinfo = schedView.getStartDateTimezoneInfo();
        TimeZoneDefinition endTimeTZinfo = schedView.getEndDateTimezoneInfo();
        TimeZoneDefinition recurrenceTZinfo = schedView.getRecurrenceTimezoneInfo();
        recurDef = schedView.getRecurrenceDefinition(recurrenceTZinfo);
        DateTime icalStartDate = schedView.getStartTime();
        DateTime icalEndDate = schedView.getEndTime();
        DateTime icalDueDate = schedView.getDueDate();
        DateTime icalDateTaskCompleted = schedView.getDateTaskCompleted();
        DateTime icalCreateDate = MapiPropertyId.PidTagCreationTime.getDateTimeAsUTC(schedView);
        DateTime icalLastModDate = MapiPropertyId.PidTagLastModificationTime.getDateTimeAsUTC(schedView);
        DateTime recurrenceIdDateTime = schedView.getRecurrenceIdTime();
        DateTime attendeeCriticalChange = schedView.getAttendeeCriticalChange();
        DateTime ownerCriticalChange = schedView.getOwnerCriticalChange();
        int percentComplete = schedView.getPercentComplete();
        TaskStatus taskStatus = schedView.getTaskStatus();
        TaskMode taskMode = schedView.getTaskMode();
        String mileage = schedView.getMileage();
        String billingInfo = schedView.getBillingInfo();
        String companies = schedView.getCompanies();
        Integer actualEffort = schedView.getActualEffort();
        Integer estimatedEffort = schedView.getEstimatedEffort();
        List<String> categories = schedView.getCategories();
        String descriptionText = null;
        String summary = null;
        if (mimeMsg != null) {
            summary = mimeMsg.getSubject();
            PlainTextFinder finder = new PlainTextFinder();
            finder.accept(mimeMsg);
            descriptionText = finder.getPlainText();
        }
        // RTF might be useful as a basis for X-ALT-DESC if we can find a reliable
        // conversion to HTML
        // String rtfText = schedView.getRTF();
        icalOutput.startCalendar();
        // Results in a 2nd PRODID in iCalendar
        // IcalUtil.addProperty(icalOutput, Property.PRODID,
        //         "Zimbra-TNEF-iCalendar-Converter");
        IcalUtil.addProperty(icalOutput, method);
        if (recurDef != null) {
            String MsCalScale = recurDef.xMicrosoftCalscale();
            if ((MsCalScale == null) || (MsCalScale.equals(""))) {
                IcalUtil.addProperty(icalOutput, CalScale.GREGORIAN);
            } else {
                IcalUtil.addProperty(icalOutput, "X-MICROSOFT-CALSCALE", MsCalScale);
            }
        } else {
            IcalUtil.addProperty(icalOutput, CalScale.GREGORIAN);
        }
        String startTZname = null;
        String endTZname = null;
        String recurTZname = null;
        if (startTimeTZinfo != null) {
            startTZname = startTimeTZinfo.getTimezoneName();
            startTimeTZinfo.addVtimezone(icalOutput);
        }
        if (endTimeTZinfo != null) {
            endTZname = endTimeTZinfo.getTimezoneName();
            if ((startTZname == null) || (!endTZname.equals(startTZname))) {
                endTimeTZinfo.addVtimezone(icalOutput);
            }
        }
        if (recurrenceTZinfo != null) {
            recurTZname = recurrenceTZinfo.getTimezoneName();
            boolean addName = true;
            if ((startTZname != null) && (recurTZname.equals(startTZname))) {
                addName = false;
            }
            if ((endTZname != null) && (recurTZname.equals(endTZname))) {
                addName = false;
            }
            if (addName) {
                recurrenceTZinfo.addVtimezone(icalOutput);
            }
        }
        if (uid == null) {
            sLog.debug("Unable to map class %s to ICALENDER - no suitable value found for UID", msgClass);
            return false;
        }
        icalOutput.startComponent(icalType.toString());
        IcalUtil.addProperty(icalOutput, Property.UID, uid);
        if ((attendeeCriticalChange != null) && (method.equals(Method.REPLY) || method.equals(Method.COUNTER))) {
            dtstamp = new DtStamp(attendeeCriticalChange);
        } else if (ownerCriticalChange != null) {
            dtstamp = new DtStamp(ownerCriticalChange);
        } else {
            DateTime stampTime = new DateTime("20000101T000000Z");
            dtstamp = new DtStamp(stampTime);
        }
        IcalUtil.addProperty(icalOutput, dtstamp);
        IcalUtil.addProperty(icalOutput, Property.CREATED, icalCreateDate, false);
        IcalUtil.addProperty(icalOutput, Property.LAST_MODIFIED, icalLastModDate, false);
        IcalUtil.addProperty(icalOutput, Property.SEQUENCE, sequenceNum, false);
        if ((summary == null) || (summary.length() == 0)) {
            // TNEF_to_iCalendar.pdf Spec requires SUMMARY for certain method types
            if (this.icalType == ICALENDAR_TYPE.VTODO) {
                if (method.equals(Method.REQUEST)) {
                    summary = new String("Task Request");
                } else if (method.equals(Method.REPLY)) {
                    summary = new String("Task Response");
                } else {
                    summary = new String("Task");
                }
            } else {
                if (method.equals(Method.REPLY)) {
                    summary = new String("Response");
                } else if (method.equals(Method.CANCEL)) {
                    summary = new String("Canceled");
                } else if (method.equals(Method.COUNTER)) {
                    summary = new String("Counter Proposal");
                }
            }
        }
        IcalUtil.addProperty(icalOutput, Property.SUMMARY, summary, false);
        IcalUtil.addProperty(icalOutput, Property.LOCATION, location, false);
        IcalUtil.addProperty(icalOutput, Property.DESCRIPTION, descriptionText, false);
        if (method.equals(Method.COUNTER)) {
            IcalUtil.addPropertyFromUtcTimeAndZone(icalOutput, Property.DTSTART, schedView.getProposedStartTime(), startTimeTZinfo, isAllDayEvent);
            IcalUtil.addPropertyFromUtcTimeAndZone(icalOutput, Property.DTEND, schedView.getProposedEndTime(), endTimeTZinfo, isAllDayEvent);
            IcalUtil.addPropertyFromUtcTimeAndZone(icalOutput, "X-MS-OLK-ORIGINALSTART", icalStartDate, startTimeTZinfo, isAllDayEvent);
            IcalUtil.addPropertyFromUtcTimeAndZone(icalOutput, "X-MS-OLK-ORIGINALEND", icalEndDate, endTimeTZinfo, isAllDayEvent);
        } else {
            if (this.icalType == ICALENDAR_TYPE.VTODO) {
                IcalUtil.addFloatingDateProperty(icalOutput, Property.DTSTART, icalStartDate);
                IcalUtil.addFloatingDateProperty(icalOutput, Property.DUE, icalDueDate);
                Status icalStatus = null;
                if (method.equals(Method.CANCEL)) {
                    icalStatus = Status.VTODO_CANCELLED;
                } else if (taskStatus != null) {
                    if (taskStatus.equals(TaskStatus.COMPLETE)) {
                        icalStatus = Status.VTODO_COMPLETED;
                    } else if (taskStatus.equals(TaskStatus.IN_PROGRESS)) {
                        icalStatus = Status.VTODO_IN_PROCESS;
                    }
                }
                IcalUtil.addProperty(icalOutput, icalStatus);
                if (percentComplete != 0) {
                    IcalUtil.addProperty(icalOutput, Property.PERCENT_COMPLETE, percentComplete, false);
                }
                // COMPLETED must be a UTC DATE-TIME according to rfc5545
                IcalUtil.addPropertyFromUtcTimeAndZone(icalOutput, Property.COMPLETED, icalDateTaskCompleted, null, false);
            } else {
                IcalUtil.addPropertyFromUtcTimeAndZone(icalOutput, Property.DTSTART, icalStartDate, startTimeTZinfo, isAllDayEvent);
                IcalUtil.addPropertyFromUtcTimeAndZone(icalOutput, Property.DTEND, icalEndDate, endTimeTZinfo, isAllDayEvent);
            }
        }
        // just the original start date.
        if (recurrenceIdDateTime != null) {
            IcalUtil.addPropertyFromUtcTimeAndZone(icalOutput, Property.RECURRENCE_ID, recurrenceIdDateTime, startTimeTZinfo, isAllDayEvent);
        } else {
            // Outlook messages related to a specific instance still include info on
            // the full recurrence but we don't want to include that.
            addRecurrenceRelatedProps(icalOutput, recurDef, startTimeTZinfo, isAllDayEvent);
        }
        // VTODO REQUEST must have priority according to http://tools.ietf.org/html/rfc5546
        // No harm in always setting it
        Priority priority = Priority.MEDIUM;
        if (importance != null) {
            if (importance == 2) {
                priority = Priority.HIGH;
            } else if (importance == 1) {
                priority = Priority.MEDIUM;
            } else if (importance == 0) {
                priority = Priority.LOW;
            }
        }
        IcalUtil.addProperty(icalOutput, priority);
        IcalUtil.addProperty(icalOutput, icalClass);
        addStatusProperty(icalOutput, bestBusyStatus, meetingTypeFlags);
        addTranspProperty(icalOutput, bestBusyStatus);
        addAttendees(icalOutput, mimeMsg, partstat, replyWanted);
        // Not done as Zimbra doesn't currently support "RESOURCES".
        if (categories != null) {
            CategoryList cl = new CategoryList();
            for (String category : categories) {
                cl.add(category);
            }
            if (cl.size() > 0) {
                Categories myCategories = new Categories(cl);
                IcalUtil.addProperty(icalOutput, myCategories);
            }
        }
        if (taskStatus != null) {
            if (taskStatus.equals(TaskStatus.DEFERRED) || taskStatus.equals(TaskStatus.WAITING_ON_OTHER)) {
                IcalUtil.addProperty(icalOutput, "X-ZIMBRA-TASK-STATUS", taskStatus, false);
            }
        }
        if ((taskMode != null) && (!taskMode.equals(TaskMode.TASK_REQUEST))) {
            IcalUtil.addProperty(icalOutput, "X-ZIMBRA-TASK-MODE", taskMode, false);
        }
        IcalUtil.addProperty(icalOutput, "X-ZIMBRA-MILEAGE", mileage, false);
        IcalUtil.addProperty(icalOutput, "X-ZIMBRA-BILLING-INFO", billingInfo, false);
        IcalUtil.addProperty(icalOutput, "X-ZIMBRA-COMPANIES", companies, false);
        IcalUtil.addProperty(icalOutput, "X-ZIMBRA-ACTUAL-WORK-MINS", actualEffort, false);
        IcalUtil.addProperty(icalOutput, "X-ZIMBRA-TOTAL-WORK-MINS", estimatedEffort, false);
        if (this.icalType == ICALENDAR_TYPE.VEVENT) {
            IcalUtil.addProperty(icalOutput, "X-MICROSOFT-CDO-ALLDAYEVENT", isAllDayEvent ? "TRUE" : "FALSE");
            IcalUtil.addProperty(icalOutput, "X-MICROSOFT-CDO-BUSYSTATUS", busyStatus, false);
            if (method.equals(Method.REQUEST)) {
                IcalUtil.addProperty(icalOutput, "X-MICROSOFT-CDO-INTENDEDSTATUS", intendedBusyStatus, false);
            }
            IcalUtil.addProperty(icalOutput, "X-MICROSOFT-CDO-OWNERAPPTID", ownerApptId, false);
            IcalUtil.addProperty(icalOutput, "X-MICROSOFT-CDO-REPLYTIME", schedView.getAppointmentReplyTime(), false);
            IcalUtil.addProperty(icalOutput, "X-MICROSOFT-CDO-OWNER-CRITICAL-CHANGE", ownerCriticalChange, false);
            Boolean disallowCounter = schedView.isDisallowCounter();
            if (disallowCounter != null) {
                IcalUtil.addProperty(icalOutput, "X-MICROSOFT-CDO-DISALLOW-COUNTER", disallowCounter ? "TRUE" : "FALSE");
            }
        }
        if (reminderSet) {
            addAlarmComponent(icalOutput, schedView.getReminderDelta());
        }
        icalOutput.endComponent(icalType.toString());
        if (recurrenceIdDateTime == null) {
            // If this message primarily relates to a specific instance,
            // exception information is superfluous
            addExceptions(icalOutput, recurDef, recurrenceTZinfo, sequenceNum, ownerApptId, summary, location, isAllDayEvent);
        }
        icalOutput.endCalendar();
        conversionSuccessful = true;
        sLog.info("Calendaring TNEF message mapped to ICALENDAR with UID=%s", uid);
    } catch (ParserException e) {
        sLog.debug("Unexpected ParserException thrown", e);
    } catch (URISyntaxException e) {
        sLog.debug("Unexpected URISyntaxException thrown", e);
    } catch (ParseException e) {
        sLog.debug("Unexpected ParseException thrown", e);
    } catch (MessagingException e) {
        sLog.debug("Unexpected MessagingException thrown", e);
    } catch (NegativeArraySizeException e) {
        sLog.debug("Problem decoding TNEF for ICALENDAR", e);
    } catch (IOException e) {
        sLog.debug("Unexpected IOException thrown", e);
    } catch (UnsupportedTnefCalendaringMsgException e) {
        sLog.debug("Unable to map this message to ICALENDAR", e);
    } catch (TNEFtoIcalendarServiceException e) {
        sLog.debug("Problem encountered mapping this message to ICALENDAR", e);
    } finally {
        try {
            if (tnefStream != null) {
                tnefStream.close();
            }
        } catch (IOException ioe) {
            sLog.debug("Problem encountered closing TNEF stream", ioe);
        }
    }
    return conversionSuccessful;
}
Also used : MAPIProps(net.freeutils.tnef.MAPIProps) TimeZoneDefinition(com.zimbra.cs.util.tnef.mapi.TimeZoneDefinition) Attachment(net.freeutils.tnef.Attachment) BusyStatus(com.zimbra.cs.util.tnef.mapi.BusyStatus) URISyntaxException(java.net.URISyntaxException) TaskMode(com.zimbra.cs.util.tnef.mapi.TaskMode) MAPIProp(net.freeutils.tnef.MAPIProp) DateTime(net.fortuna.ical4j.model.DateTime) DtStamp(net.fortuna.ical4j.model.property.DtStamp) MeetingTypeFlag(com.zimbra.cs.util.tnef.mapi.MeetingTypeFlag) List(java.util.List) ParameterList(net.fortuna.ical4j.model.ParameterList) CategoryList(net.fortuna.ical4j.model.CategoryList) Clazz(net.fortuna.ical4j.model.property.Clazz) UnsupportedTnefCalendaringMsgException(com.zimbra.cs.util.tnef.TNEFtoIcalendarServiceException.UnsupportedTnefCalendaringMsgException) Status(net.fortuna.ical4j.model.property.Status) TaskStatus(com.zimbra.cs.util.tnef.mapi.TaskStatus) BusyStatus(com.zimbra.cs.util.tnef.mapi.BusyStatus) ParserException(net.fortuna.ical4j.data.ParserException) Categories(net.fortuna.ical4j.model.property.Categories) MessagingException(javax.mail.MessagingException) Priority(net.fortuna.ical4j.model.property.Priority) PartStat(net.fortuna.ical4j.model.parameter.PartStat) IOException(java.io.IOException) TNEFInputStream(net.freeutils.tnef.TNEFInputStream) TaskStatus(com.zimbra.cs.util.tnef.mapi.TaskStatus) CategoryList(net.fortuna.ical4j.model.CategoryList) ParseException(java.text.ParseException)

Aggregations

UnsupportedTnefCalendaringMsgException (com.zimbra.cs.util.tnef.TNEFtoIcalendarServiceException.UnsupportedTnefCalendaringMsgException)1 BusyStatus (com.zimbra.cs.util.tnef.mapi.BusyStatus)1 MeetingTypeFlag (com.zimbra.cs.util.tnef.mapi.MeetingTypeFlag)1 TaskMode (com.zimbra.cs.util.tnef.mapi.TaskMode)1 TaskStatus (com.zimbra.cs.util.tnef.mapi.TaskStatus)1 TimeZoneDefinition (com.zimbra.cs.util.tnef.mapi.TimeZoneDefinition)1 IOException (java.io.IOException)1 URISyntaxException (java.net.URISyntaxException)1 ParseException (java.text.ParseException)1 List (java.util.List)1 MessagingException (javax.mail.MessagingException)1 ParserException (net.fortuna.ical4j.data.ParserException)1 CategoryList (net.fortuna.ical4j.model.CategoryList)1 DateTime (net.fortuna.ical4j.model.DateTime)1 ParameterList (net.fortuna.ical4j.model.ParameterList)1 PartStat (net.fortuna.ical4j.model.parameter.PartStat)1 Categories (net.fortuna.ical4j.model.property.Categories)1 Clazz (net.fortuna.ical4j.model.property.Clazz)1 DtStamp (net.fortuna.ical4j.model.property.DtStamp)1 Priority (net.fortuna.ical4j.model.property.Priority)1