blob: 6c7821e5ee60b3d5ef38f33aded8eea054404324 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License") + you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.openmeetings.util.mail;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.apache.openmeetings.util.mail.MailUtil.MAILTO;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URI;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.fortuna.ical4j.data.CalendarOutputter;
import net.fortuna.ical4j.model.Calendar;
import net.fortuna.ical4j.model.ComponentList;
import net.fortuna.ical4j.model.Parameter;
import net.fortuna.ical4j.model.ParameterList;
import net.fortuna.ical4j.model.Property;
import net.fortuna.ical4j.model.PropertyList;
import net.fortuna.ical4j.model.TimeZone;
import net.fortuna.ical4j.model.TimeZoneRegistry;
import net.fortuna.ical4j.model.TimeZoneRegistryFactory;
import net.fortuna.ical4j.model.component.VEvent;
import net.fortuna.ical4j.model.parameter.Cn;
import net.fortuna.ical4j.model.parameter.CuType;
import net.fortuna.ical4j.model.parameter.PartStat;
import net.fortuna.ical4j.model.parameter.Role;
import net.fortuna.ical4j.model.parameter.Rsvp;
import net.fortuna.ical4j.model.parameter.XParameter;
import net.fortuna.ical4j.model.property.Attendee;
import net.fortuna.ical4j.model.property.CalScale;
import net.fortuna.ical4j.model.property.Created;
import net.fortuna.ical4j.model.property.Description;
import net.fortuna.ical4j.model.property.LastModified;
import net.fortuna.ical4j.model.property.Location;
import net.fortuna.ical4j.model.property.Method;
import net.fortuna.ical4j.model.property.Organizer;
import net.fortuna.ical4j.model.property.ProdId;
import net.fortuna.ical4j.model.property.Sequence;
import net.fortuna.ical4j.model.property.Status;
import net.fortuna.ical4j.model.property.Transp;
import net.fortuna.ical4j.model.property.Uid;
import net.fortuna.ical4j.model.property.Version;
import net.fortuna.ical4j.validate.ValidationException;
/**
*
* @author o.becherer, seba.wagner
*
*/
public class IcalHandler {
private static final Logger log = LoggerFactory.getLogger(IcalHandler.class);
public static final TimeZoneRegistry TZ_REGISTRY = TimeZoneRegistryFactory.getInstance().createRegistry();
/** ICal instance */
private final Calendar icsCalendar;
private TimeZone timeZone;
private VEvent meeting;
private List<Property> meetingProperties = new ArrayList<>();
private Method method;
/** Creation of a new Event */
public static final Method ICAL_METHOD_REQUEST = Method.REQUEST;
public static final Method ICAL_METHOD_CANCEL = Method.CANCEL;
public static final Method ICAL_METHOD_REFRESH = Method.REFRESH;
/**
* Constructor
*
* @param method
* (@see IcalHandler) constants
*/
public IcalHandler(Method method) {
this.method = method;
log.debug("Icalhandler method type : {}", method);
icsCalendar = new Calendar(new PropertyList(List.of(
new ProdId("-//Apache Openmeetings//OM Calendar//EN")
, Version.VERSION_2_0
, CalScale.GREGORIAN
, method
)), new ComponentList<>());
}
public IcalHandler createVEvent(ZonedDateTime start, ZonedDateTime end, String name) {
timeZone = TZ_REGISTRY.getTimeZone(start.getZone().getId());
if (timeZone == null) {
throw new NoSuchElementException("Unable to get time zone by id provided: " + start.getZone());
}
meeting = new VEvent(start, end, name);
meetingProperties.addAll(meeting.getProperties());
meetingProperties.addAll(List.of(Transp.OPAQUE, Status.VEVENT_CONFIRMED));
return this;
}
public IcalHandler setCreated(ZonedDateTime date) {
meetingProperties.add(new Created(date.toInstant()));
return this;
}
public IcalHandler setModified(ZonedDateTime date) {
meetingProperties.add(new LastModified((date == null ? ZonedDateTime.now() : date).toInstant()));
return this;
}
public IcalHandler setDescription(String description) {
meetingProperties.add(new Description(description));
return this;
}
public IcalHandler setLocation(String location) {
meetingProperties.add(new Location(location));
return this;
}
public IcalHandler setSequence(int seq) {
meetingProperties.add(new Sequence(seq));
return this;
}
public IcalHandler setUid(String uid) {
meetingProperties.add(new Uid(uid));
return this;
}
private static URI getMailto(String email) {
return URI.create(MAILTO + email);
}
public IcalHandler addOrganizer(String email, String name) {
meetingProperties.add(new Organizer(new ParameterList(List.of(new Cn(name))), getMailto(email)));
return this;
}
public IcalHandler addAttendee(String email, String display, boolean chair) {
List<Parameter> params = new ArrayList<>(List.of(
CuType.INDIVIDUAL
, chair ? Role.CHAIR : Role.REQ_PARTICIPANT
, new XParameter("X-NUM-GUESTS", "0")
, new Cn(display)));
if (Method.CANCEL == method) {
params.add(PartStat.DECLINED);
} else {
params.add(chair ? PartStat.ACCEPTED : PartStat.NEEDS_ACTION);
params.add(Rsvp.TRUE);
}
meetingProperties.add(new Attendee(new ParameterList(params), URI.create(MAILTO + email)));
return this;
}
public IcalHandler build() {
meeting.setPropertyList(new PropertyList(meetingProperties));
icsCalendar.setComponentList(new ComponentList<>(List.of(timeZone.getVTimeZone(), meeting)));
return this;
}
public Method getMethod() {
return method;
}
/**
* Write iCal to File
*
* @param inFilerPath
* - path to '*.ics' file
* @throws Exception
* - in case of error during writing to the file
*/
public void toFile(String inFilerPath) throws Exception {
String filerPath = inFilerPath.endsWith(".ics") ? inFilerPath
: String.format("%s.ics", inFilerPath);
try (FileOutputStream fout = new FileOutputStream(filerPath)) {
CalendarOutputter outputter = new CalendarOutputter();
outputter.output(icsCalendar, fout);
}
}
/**
* Get IcalBody as ByteArray
*
* @return - calendar in ICS format as byte[]
* @throws IOException
* - in case of error during writing to byte array
* @throws ValidationException
* - in case of invalid calendar properties
*/
public byte[] toByteArray() throws IOException {
ByteArrayOutputStream bout = new ByteArrayOutputStream();
CalendarOutputter outputter = new CalendarOutputter();
outputter.output(icsCalendar, bout);
return bout.toByteArray();
}
/**
* Retrieving Data as String
*/
@Override
public String toString() {
if (icsCalendar == null) {
return "";
}
try {
return new String(toByteArray(), UTF_8);
} catch (IOException e) {
log.error("Unexpected error", e);
}
return "";
}
}