protected void setClientAllday(VProperty property) {
   if (property != null) {
     // set VALUE=DATE param
     if (!property.hasParam("VALUE")) {
       property.addParam("VALUE", "DATE");
     }
     // remove TZID
     property.removeParam("TZID");
     String value = property.getValue();
     if (value.length() != 8) {
       // try to convert datetime value to date value
       try {
         Calendar calendar = Calendar.getInstance();
         SimpleDateFormat dateParser = new SimpleDateFormat("yyyyMMdd'T'HHmmss");
         calendar.setTime(dateParser.parse(value));
         calendar.add(Calendar.HOUR_OF_DAY, 12);
         SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyyMMdd");
         value = dateFormatter.format(calendar.getTime());
       } catch (ParseException e) {
         LOGGER.warn("Invalid date value in allday event: " + value);
       }
     }
     property.setValue(value);
   }
 }
 protected String getAttendeeStatus() {
   String status = null;
   List<VProperty> attendeeProperties = getFirstVeventProperties("ATTENDEE");
   if (attendeeProperties != null) {
     for (VProperty property : attendeeProperties) {
       String attendeeEmail = getEmailValue(property);
       if (email.equalsIgnoreCase(attendeeEmail) && property.hasParam("PARTSTAT")) {
         // found current user attendee line
         status = property.getParam("PARTSTAT").getValue();
         break;
       }
     }
   }
   return status;
 }
  protected void fixAlarm(VObject vObject, boolean fromServer) {
    if (vObject.vObjects != null) {
      if (Settings.getBooleanProperty("davmail.caldavDisableReminders", false)) {
        ArrayList<VObject> vAlarms = null;
        for (VObject vAlarm : vObject.vObjects) {
          if ("VALARM".equals(vAlarm.type)) {
            if (vAlarms == null) {
              vAlarms = new ArrayList<VObject>();
            }
            vAlarms.add(vAlarm);
          }
        }
        // remove all vAlarms
        if (vAlarms != null) {
          for (VObject vAlarm : vAlarms) {
            vObject.vObjects.remove(vAlarm);
          }
        }

      } else {
        for (VObject vAlarm : vObject.vObjects) {
          if ("VALARM".equals(vAlarm.type)) {
            String action = vAlarm.getPropertyValue("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
              vAlarm.setPropertyValue("ACTION", "AUDIO");

              if (vAlarm.getPropertyValue("ATTACH") == null) {
                // Add defined sound into the audio alarm
                VProperty vProperty =
                    new VProperty("ATTACH", Settings.getProperty("davmail.caldavAlarmSound"));
                vProperty.addParam("VALUE", "URI");
                vAlarm.addProperty(vProperty);
              }

            } else if (!fromServer && "AUDIO".equals(action)) {
              // Use the alarm action that exchange (and blackberry) understand
              // (exchange and blackberry don't understand audio actions)
              vAlarm.setPropertyValue("ACTION", "DISPLAY");
            }
          }
        }
      }
    }
  }
 protected void splitExDate(VObject vObject) {
   List<VProperty> exDateProperties = vObject.getProperties("EXDATE");
   if (exDateProperties != null) {
     for (VProperty property : exDateProperties) {
       String value = property.getValue();
       if (value.indexOf(',') >= 0) {
         // split property
         vObject.removeProperty(property);
         for (String singleValue : value.split(",")) {
           VProperty singleProperty = new VProperty("EXDATE", singleValue);
           singleProperty.setParams(property.getParams());
           vObject.addProperty(singleProperty);
         }
       }
     }
   }
 }
 /**
  * Get email from property value.
  *
  * @param property property
  * @return email value
  */
 public String getEmailValue(VProperty property) {
   if (property == null) {
     return null;
   }
   String propertyValue = property.getValue();
   if (propertyValue != null
       && (propertyValue.startsWith("MAILTO:") || propertyValue.startsWith("mailto:"))) {
     return propertyValue.substring(7);
   } else {
     return propertyValue;
   }
 }
 private void fixTimezone() {
   if (vTimezone != null && vTimezone.vObjects != null && vTimezone.vObjects.size() > 2) {
     VObject standard = null;
     VObject daylight = null;
     for (VObject vObject : vTimezone.vObjects) {
       if ("STANDARD".equals(vObject.type)) {
         if (standard == null
             || (vObject
                     .getPropertyValue("DTSTART")
                     .compareTo(standard.getPropertyValue("DTSTART"))
                 > 0)) {
           standard = vObject;
         }
       }
       if ("DAYLIGHT".equals(vObject.type)) {
         if (daylight == null
             || (vObject
                     .getPropertyValue("DTSTART")
                     .compareTo(daylight.getPropertyValue("DTSTART"))
                 > 0)) {
           daylight = vObject;
         }
       }
     }
     vTimezone.vObjects.clear();
     vTimezone.vObjects.add(standard);
     vTimezone.vObjects.add(daylight);
   }
   // fix 3569922: quick workaround for broken Israeli Timezone issue
   if (vTimezone != null && vTimezone.vObjects != null) {
     for (VObject vObject : vTimezone.vObjects) {
       VProperty rrule = vObject.getProperty("RRULE");
       if (rrule != null
           && rrule.getValues().size() == 3
           && "BYDAY=-2SU".equals(rrule.getValues().get(1))) {
         rrule.getValues().set(1, "BYDAY=4SU");
       }
     }
   }
 }
 protected void setServerAllday(VProperty property) {
   if (vTimezone != null) {
     // set TZID param
     if (!property.hasParam("TZID")) {
       property.addParam("TZID", vTimezone.getPropertyValue("TZID"));
     }
     // remove VALUE
     property.removeParam("VALUE");
     String value = property.getValue();
     if (value.length() != 8) {
       LOGGER.warn("Invalid date value in allday event: " + value);
     }
     property.setValue(property.getValue() + "T000000");
   }
 }
  /**
   * Build recipients value for VCalendar.
   *
   * @param isNotification if true, filter recipients that should receive meeting notifications
   * @return notification/event recipients
   */
  public Recipients getRecipients(boolean isNotification) {

    HashSet<String> attendees = new HashSet<String>();
    HashSet<String> optionalAttendees = new HashSet<String>();

    // get recipients from first VEVENT
    List<VProperty> attendeeProperties = getFirstVeventProperties("ATTENDEE");
    if (attendeeProperties != null) {
      for (VProperty property : attendeeProperties) {
        // exclude current user and invalid values from recipients
        // also exclude no action attendees
        String attendeeEmail = getEmailValue(property);
        if (!email.equalsIgnoreCase(attendeeEmail)
            && attendeeEmail != null
            && attendeeEmail.indexOf('@') >= 0
            // return all attendees for user calendar folder, filter for notifications
            && (!isNotification
                // notify attendee if reply explicitly requested
                || (property.hasParam("RSVP", "TRUE"))
                || (
                // workaround for iCal bug: do not notify if reply explicitly not requested
                !(property.hasParam("RSVP", "FALSE"))
                    && ((property.hasParam("PARTSTAT", "NEEDS-ACTION")
                        // need to include other PARTSTATs participants for CANCEL notifications
                        || property.hasParam("PARTSTAT", "ACCEPTED")
                        || property.hasParam("PARTSTAT", "DECLINED")
                        || property.hasParam("PARTSTAT", "TENTATIVE")))))) {
          if (property.hasParam("ROLE", "OPT-PARTICIPANT")) {
            optionalAttendees.add(attendeeEmail);
          } else {
            attendees.add(attendeeEmail);
          }
        }
      }
    }
    Recipients recipients = new Recipients();
    recipients.organizer = getEmailValue(getFirstVeventProperty("ORGANIZER"));
    recipients.attendees = StringUtil.join(attendees, ", ");
    recipients.optionalAttendees = StringUtil.join(optionalAttendees, ", ");
    return recipients;
  }
 private void fixAttendees(VObject vObject, boolean fromServer) {
   if (vObject.properties != null) {
     for (VProperty property : vObject.properties) {
       if ("ATTENDEE".equalsIgnoreCase(property.getKey())) {
         if (fromServer) {
           // 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
           if (isCurrentUser(property) && property.hasParam("RSVP", "TRUE")) {
             VProperty.Param partstat = property.getParam("PARTSTAT");
             if (partstat == null || !"NEEDS-ACTION".equals(partstat.getValue())) {
               property.removeParam("RSVP");
             }
           }
         } else {
           property.setValue(replaceIcal4Principal(property.getValue()));
         }
       }
     }
   }
 }
 private boolean isCurrentUser(VProperty property) {
   return property.getValue().equalsIgnoreCase("mailto:" + email);
 }
 private void fixTzid(VProperty property) {
   if (property != null && !property.hasParam("TZID")) {
     property.addParam("TZID", vTimezone.getPropertyValue("TZID"));
   }
 }
  protected void fixVCalendar(boolean fromServer) {
    // set iCal 4 global X-CALENDARSERVER-ACCESS from CLASS
    if (fromServer) {
      setPropertyValue("X-CALENDARSERVER-ACCESS", getCalendarServerAccess());
    }

    // iCal 4 global X-CALENDARSERVER-ACCESS
    String calendarServerAccess = getPropertyValue("X-CALENDARSERVER-ACCESS");
    String now = ExchangeSession.getZuluDateFormat().format(new Date());

    // fix method from iPhone
    if (!fromServer && getPropertyValue("METHOD") == null) {
      setPropertyValue("METHOD", "PUBLISH");
    }

    // rename TZID for maximum iCal/iPhone compatibility
    String tzid = null;
    if (fromServer) {
      // get current tzid
      VObject vObject = vTimezone;
      if (vObject != null) {
        String currentTzid = vObject.getPropertyValue("TZID");
        // fix TZID with \n (Exchange 2010 bug)
        if (currentTzid != null && currentTzid.endsWith("\n")) {
          currentTzid = currentTzid.substring(0, currentTzid.length() - 1);
          vObject.setPropertyValue("TZID", currentTzid);
        }
        if (currentTzid != null && currentTzid.indexOf(' ') >= 0) {
          try {
            tzid = ResourceBundle.getBundle("timezones").getString(currentTzid);
            vObject.setPropertyValue("TZID", tzid);
          } catch (MissingResourceException e) {
            LOGGER.debug("Timezone " + currentTzid + " not found in rename table");
          }
        }
      }
    }

    if (!fromServer) {
      fixTimezone();
    }

    // iterate over vObjects
    for (VObject vObject : vObjects) {
      if ("VEVENT".equals(vObject.type)) {
        if (calendarServerAccess != null) {
          vObject.setPropertyValue("CLASS", getEventClass(calendarServerAccess));
          // iCal 3, get X-CALENDARSERVER-ACCESS from local VEVENT
        } else if (vObject.getPropertyValue("X-CALENDARSERVER-ACCESS") != null) {
          vObject.setPropertyValue(
              "CLASS", getEventClass(vObject.getPropertyValue("X-CALENDARSERVER-ACCESS")));
        }
        if (fromServer) {
          // remove organizer line for event without attendees for iPhone
          if (vObject.getProperty("ATTENDEE") == null) {
            vObject.setPropertyValue("ORGANIZER", null);
          }
          // detect allday and update date properties
          if (isCdoAllDay(vObject)) {
            setClientAllday(vObject.getProperty("DTSTART"));
            setClientAllday(vObject.getProperty("DTEND"));
            setClientAllday(vObject.getProperty("RECURRENCE-ID"));
          }
          String cdoBusyStatus = vObject.getPropertyValue("X-MICROSOFT-CDO-BUSYSTATUS");
          if (cdoBusyStatus != null) {
            vObject.setPropertyValue(
                "TRANSP", !"FREE".equals(cdoBusyStatus) ? "OPAQUE" : "TRANSPARENT");
          }

          // Apple iCal doesn't understand this key, and it's entourage
          // specific (i.e. not needed by any caldav client): strip it out
          vObject.removeProperty("X-ENTOURAGE_UUID");

          splitExDate(vObject);

          // remove empty properties
          if ("".equals(vObject.getPropertyValue("LOCATION"))) {
            vObject.removeProperty("LOCATION");
          }
          if ("".equals(vObject.getPropertyValue("DESCRIPTION"))) {
            vObject.removeProperty("DESCRIPTION");
          }
          if ("".equals(vObject.getPropertyValue("CLASS"))) {
            vObject.removeProperty("CLASS");
          }
          // rename TZID
          if (tzid != null) {
            VProperty dtStart = vObject.getProperty("DTSTART");
            if (dtStart != null && dtStart.getParam("TZID") != null) {
              dtStart.setParam("TZID", tzid);
            }
            VProperty dtEnd = vObject.getProperty("DTEND");
            if (dtEnd != null && dtStart.getParam("TZID") != null) {
              dtEnd.setParam("TZID", tzid);
            }
            VProperty reccurrenceId = vObject.getProperty("RECURRENCE-ID");
            if (reccurrenceId != null && reccurrenceId.getParam("TZID") != null) {
              reccurrenceId.setParam("TZID", tzid);
            }
          }
          // remove unsupported attachment reference
          if (vObject.getProperty("ATTACH") != null) {
            List<String> toRemoveValues = null;
            List<String> values = vObject.getProperty("ATTACH").getValues();
            for (String value : values) {
              if (value.indexOf("CID:") >= 0) {
                if (toRemoveValues == null) {
                  toRemoveValues = new ArrayList<String>();
                }
                toRemoveValues.add(value);
              }
            }
            if (toRemoveValues != null) {
              values.removeAll(toRemoveValues);
              if (values.size() == 0) {
                vObject.removeProperty("ATTACH");
              }
            }
          }
        } else {
          // add organizer line to all events created in Exchange for active sync
          String organizer = getEmailValue(vObject.getProperty("ORGANIZER"));
          if (organizer == null) {
            vObject.setPropertyValue("ORGANIZER", "MAILTO:" + email);
          } else if (!email.equalsIgnoreCase(organizer)
              && vObject.getProperty("X-MICROSOFT-CDO-REPLYTIME") == null) {
            vObject.setPropertyValue("X-MICROSOFT-CDO-REPLYTIME", now);
          }
          // set OWA allday flag
          vObject.setPropertyValue(
              "X-MICROSOFT-CDO-ALLDAYEVENT", isAllDay(vObject) ? "TRUE" : "FALSE");
          vObject.setPropertyValue(
              "X-MICROSOFT-CDO-BUSYSTATUS",
              !"TRANSPARENT".equals(vObject.getPropertyValue("TRANSP")) ? "BUSY" : "FREE");

          if (isAllDay(vObject)) {
            // convert date values to outlook compatible values
            setServerAllday(vObject.getProperty("DTSTART"));
            setServerAllday(vObject.getProperty("DTEND"));
          } else {
            fixTzid(vObject.getProperty("DTSTART"));
            fixTzid(vObject.getProperty("DTEND"));
          }
        }

        fixAttendees(vObject, fromServer);

        fixAlarm(vObject, fromServer);
      }
    }
  }
 protected boolean isAllDay(VObject vObject) {
   VProperty dtstart = vObject.getProperty("DTSTART");
   return dtstart != null && dtstart.hasParam("VALUE", "DATE");
 }