/*
* Copyright 2012 McEvoy Software Ltd.
*
*/
package io.milton.http.caldav;
import io.milton.http.exceptions.BadRequestException;
import io.milton.http.exceptions.NotAuthorizedException;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import net.fortuna.ical4j.data.CalendarBuilder;
import net.fortuna.ical4j.data.ParserException;
import net.fortuna.ical4j.model.component.VEvent;
/**
* A helper class to parse and format the iCalendar specification. A very simple
* implement to support DefaultCalendarSearchService. You will probably want to
* replace both in a production implementation
*
* To use this, first implement the appropriate interface (E.g. EventResource
* for VEVENT) and then use this class to implement getICalData
*
* Eg
*
* public String getICalData() { ICalFormatter formatter = new ICalFormatter();
* return formatter.formatEvent( this ); }
*
* @author brad
*/
public class ICalFormatter {
public static final String MAILTO = "mailto:";
// Changed T to space. removed trailing Z
private static final String PATTERN_ICAL = "yyyyMMdd HHmmss";
public ICalFormatter() {
}
public void parseEvent(EventResource r, String data) throws IOException, ParserException {
CalendarBuilder builder = new CalendarBuilder();
net.fortuna.ical4j.model.Calendar calendar = builder.build(new ByteArrayInputStream(data.getBytes("UTF-8")));
VEvent ev = event(calendar);
String summary = null;
if (ev.getSummary() != null) {
summary = ev.getSummary().getValue();
}
r.setSummary(summary);
r.setStart(ev.getStartDate().getDate());
Date endDate = null;
if (ev.getEndDate() != null) {
endDate = ev.getEndDate().getDate();
}
r.setEnd(endDate);
}
/**
* Return the attendee mailtos for some sort of ical request
*
* @param data
* @return
*/
public List<String> parseAttendees(String data) {
String[] lines = toLines(data);
List<String> attendees = new ArrayList<String>();
for (String line : lines) {
if (line != null && line.contains(":")) {
int pos = line.indexOf(":");
String key = line.substring(0, pos);
String val = line.substring(pos + 1);
if (key.contains("ATTENDEE")) {
String s = getMailTo(val);
attendees.add(s);
}
}
}
return attendees;
}
public FreeBusyRequest parseFreeBusyRequest(String data) {
FreeBusyRequest r = new FreeBusyRequest();
String[] lines = toLines(data);
List<String> attendees = new ArrayList<String>();
r.setAttendeeMailtos(attendees);
for (String line : lines) {
if (line != null && line.contains(":")) {
int pos = line.indexOf(":");
String key = line.substring(0, pos);
String val = line.substring(pos + 1);
r.getLines().put(key, line);
if (key.equals("DTSTART")) {
Date dt = parseDate(val);
r.setStart(dt);
} else if (key.equals("DTEND")) {
Date dt = parseDate(val);
r.setFinish(dt);
} else if (key.equals("ORGANIZER")) {
String s = getMailTo(val);
System.out.println("org: " + s);
r.setOrganiserMailto(s);
} else if (key.contains("ATTENDEE")) {
System.out.println("found attendee");
String s = getMailTo(val);
System.out.println("attendee: " + s);
attendees.add(s);
r.getAttendeeLines().put(s, line);
}
}
}
return r;
}
public String formatEvent(EventResource r) {
return "BEGIN:VCALENDAR\n"
+ "VERSION:2.0\n"
+ "PRODID:-//MiltonCalDAV//EN\n"
+ "BEGIN:VEVENT\n"
+ "UID:" + r.getUniqueId() + "\n"
+ "DTSTAMP:19970714T170000Z\n"
+ "SUMMARY:" + r.getSummary() + "\n"
+ "DTSTART:" + formatDate(r.getStart()) + "\n"
+ "DTEND:" + formatDate(r.getEnd()) + "\n"
+ "END:VEVENT\n"
+ "END:VCALENDAR";
}
public Date parseDate(String s) {
if (s == null || s.length() == 0) {
return null;
}
s = s.replace("T", " ");
s = s.replace("Z", "");
SimpleDateFormat sdf = new SimpleDateFormat(PATTERN_ICAL);
try {
return sdf.parse(s);
} catch (ParseException ex) {
throw new RuntimeException(s, ex);
}
}
public String formatDate(Date d) {
if (d == null) {
return "";
}
Calendar cal = Calendar.getInstance();
cal.setTime(d);
StringBuilder sb = new StringBuilder();
sb.append(cal.get(Calendar.YEAR)).append("");
sb.append(pad2(cal.get(Calendar.MONTH) + 1));
sb.append(pad2(cal.get(Calendar.DAY_OF_MONTH)));
sb.append('T');
sb.append(pad2(cal.get(Calendar.HOUR_OF_DAY)));
sb.append(pad2(cal.get(Calendar.MINUTE)));
sb.append(pad2(cal.get(Calendar.SECOND)));
sb.append('Z');
String s = sb.toString();
return s;
}
private static String pad2(int i) {
if (i < 10) {
return "0" + i;
} else {
return i + "";
}
}
private String getMailTo(String s) {
// CN="Wilfredo Sanchez Vega":mailto:wilfredo@example.com
int pos = s.lastIndexOf(MAILTO);
if (pos >= 0) {
String m = s.substring(pos + MAILTO.length(), s.length());
return m;
} else {
return null;
}
}
private String[] toLines(String data) {
List<String> lines = new ArrayList<String>();
for (String s : data.split("\n")) {
if (s.startsWith(" ")) {
s = s.trim();
String line = lines.get(lines.size() - 1) + s;
lines.remove(lines.size() - 1);
lines.add(line);
} else {
s = s.trim();
lines.add(s);
}
}
String[] arr = new String[lines.size()];
return lines.toArray(arr);
}
private VEvent event(net.fortuna.ical4j.model.Calendar cal) {
return (VEvent) cal.getComponent("VEVENT");
}
public String buildFreeBusyAttendeeResponse(List<? extends EventResource> events, ICalFormatter.FreeBusyRequest request, String domain, String attendeeMailto) throws NotAuthorizedException, BadRequestException {
Map<String, String> source = request.getLines();
StringBuilder sb = new StringBuilder();
sb.append("BEGIN:VCALENDAR\n");
sb.append("VERSION:2.0\n");
sb.append("PRODID:-//milton.io//CalDAV Server//EN\n");
sb.append("METHOD:REPLY\n");
sb.append("BEGIN:VFREEBUSY\n");
// Copy these lines back verbatim
sb.append(source.get("UID")).append("\n");
sb.append(source.get("DTSTAMP")).append("\n");
sb.append(source.get("DTSTART")).append("\n");
sb.append(source.get("DTEND")).append("\n");
sb.append(source.get("ORGANIZER")).append("\n");
// Output the original attendee line
if( attendeeMailto == null ) {
throw new RuntimeException("attendeeMailto is null");
}
sb.append(request.getAttendeeLines().get(attendeeMailto)).append("\n");
for (EventResource er : events) {
// write the freebusy statement, Eg:
// FREEBUSY;FBTYPE=BUSY:20090602T110000Z/20090602T120000Z
sb.append("FREEBUSY;FBTYPE=BUSY:");
sb.append(formatDate(er.getStart()));
sb.append("/");
sb.append(formatDate(er.getEnd()));
sb.append("\n");
}
sb.append("END:VFREEBUSY\n");
sb.append("END:VCALENDAR\n");
return sb.toString();
}
public class FreeBusyRequest {
private Date start;
private Date finish;
private String organiserMailto;
private List<String> attendeeMailtos;
private final Map<String, String> attendeeLines = new HashMap<String, String>();
private final Map<String, String> lines = new HashMap<String, String>();
public Date getStart() {
return start;
}
public void setStart(Date start) {
this.start = start;
}
public Date getFinish() {
return finish;
}
public void setFinish(Date finish) {
this.finish = finish;
}
public String getOrganiserMailto() {
return organiserMailto;
}
public void setOrganiserMailto(String organiserMailto) {
this.organiserMailto = organiserMailto;
}
public List<String> getAttendeeMailtos() {
return attendeeMailtos;
}
public void setAttendeeMailtos(List<String> attendeeMailtos) {
this.attendeeMailtos = attendeeMailtos;
}
public Map<String, String> getLines() {
return lines;
}
public Map<String, String> getAttendeeLines() {
return attendeeLines;
}
}
}