/* * � Copyright IBM Corp. 2012 * * Licensed 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 com.ibm.domino.services.calendar.util; import java.io.IOException; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.TimeZone; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; import net.fortuna.ical4j.model.Property; import com.ibm.commons.util.StringUtil; import com.ibm.commons.util.io.json.JsonException; import com.ibm.commons.util.io.json.JsonJavaFactory; import com.ibm.commons.util.io.json.JsonObject; import com.ibm.commons.util.io.json.JsonParser; import com.ibm.domino.calendar.store.Action; import com.ibm.domino.calendar.store.CounterAction; import com.ibm.domino.calendar.store.DeclineAction; import com.ibm.domino.calendar.store.DelegateAction; import com.ibm.domino.calendar.store.RecurrenceRange; import com.ibm.domino.calendar.store.StoreException; import com.ibm.domino.services.calendar.service.CalendarService; import static com.ibm.domino.services.calendar.service.CalendarService.NOTICE_ACTION_ACCEPT; import static com.ibm.domino.services.calendar.service.CalendarService.NOTICE_ACTION_CANCEL; import static com.ibm.domino.services.calendar.service.CalendarService.NOTICE_ACTION_COUNTER; import static com.ibm.domino.services.calendar.service.CalendarService.NOTICE_ACTION_DECLINE; import static com.ibm.domino.services.calendar.service.CalendarService.NOTICE_ACTION_DELEGATE; import static com.ibm.domino.services.calendar.service.CalendarService.NOTICE_ACTION_DELETE; import static com.ibm.domino.services.calendar.service.CalendarService.NOTICE_ACTION_REQUEST_INFO; import static com.ibm.domino.services.calendar.service.CalendarService.NOTICE_ACTION_TENTATIVE; import static com.ibm.domino.services.calendar.service.CalendarService.NOTICE_ACTION_PROCESS_ALL; import static com.ibm.domino.services.calendar.service.CalendarService.NOTICE_ACTION_REMOVE_CANCELLED; public class Utils { private static SimpleDateFormat ISO8601_UTC = getUtcFormatter(); private static SimpleDateFormat ISO8601 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"); // $NON-NLS-1$ private static int CALENDAR_STORE_ERROR_BASE = 1024; /** * Converts a date to an ISO8601 string. * * @param value The date. * @param utc If <code>true</code>, format the time in UTC. If <code>false</code>, * format the time in the local time zone. * @return The ISO8601 string. * @throws IOException */ public static String dateToString(Date value, boolean utc) throws IOException { String result = null; if ( utc ) { result = ISO8601_UTC.format((Date)value); } else { result = ISO8601.format((Date)value); } return result; } public static Date dateFromString(String value) throws ParseException { return ISO8601_UTC.parse(value); } private static SimpleDateFormat getUtcFormatter() { TimeZone tz = TimeZone.getTimeZone("UTC"); // $NON-NLS-1$ SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); //$NON-NLS-1$ formatter.setTimeZone(tz); return formatter; } /** * Translates a URL parameter to an action type. * * @param type * @return */ public static int translateActionType(String type) { int actionType = -1; if ( NOTICE_ACTION_ACCEPT.equalsIgnoreCase(type) ) { // $NON-NLS-1$ actionType = Action.ACTION_ACCEPT; } else if ( NOTICE_ACTION_CANCEL.equalsIgnoreCase(type) ) { // $NON-NLS-1$ actionType = Action.ACTION_CANCEL; } else if ( NOTICE_ACTION_COUNTER.equalsIgnoreCase(type) ) { // $NON-NLS-1$ actionType = Action.ACTION_COUNTER; } else if ( NOTICE_ACTION_DECLINE.equalsIgnoreCase(type) ) { // $NON-NLS-1$ actionType = Action.ACTION_DECLINE; } else if ( NOTICE_ACTION_DELEGATE.equalsIgnoreCase(type) ) { // $NON-NLS-1$ actionType = Action.ACTION_DELEGATE; } else if ( NOTICE_ACTION_DELETE.equalsIgnoreCase(type) ) { // $NON-NLS-1$ actionType = Action.ACTION_DELETE; } else if ( NOTICE_ACTION_REQUEST_INFO.equalsIgnoreCase(type) ) { // $NON-NLS-1$ actionType = Action.ACTION_REQUEST_INFO; } else if ( NOTICE_ACTION_TENTATIVE.equalsIgnoreCase(type) ) { // $NON-NLS-1$ actionType = Action.ACTION_TENTATIVE; } else if ( NOTICE_ACTION_PROCESS_ALL.equalsIgnoreCase(type) ) { // $NON-NLS-1$ actionType = Action.ACTION_PROCESS_ALL; } else if ( NOTICE_ACTION_REMOVE_CANCELLED.equalsIgnoreCase(type) ){ // $NON-NLS-1$ actionType = Action.ACTION_REMOVE_CANCEL; } return actionType; } /** * Translates a URL parameter to a recurrencerange. * * @param range * @return */ public static RecurrenceRange translateRecurrenceRange(String range) { RecurrenceRange rr = RecurrenceRange.THIS_INSTANCE; if(null!= range){ if ( "all".equalsIgnoreCase(range) ) { // $NON-NLS-1$ rr = RecurrenceRange.ALL_INSTANCES; } else if ( "future".equalsIgnoreCase(range) ) { // $NON-NLS-1$ rr = RecurrenceRange.THIS_AND_FUTURE; } else if ( "previous".equalsIgnoreCase(range) ) { // $NON-NLS-1$ rr = RecurrenceRange.THIS_AND_PREVIOUS; } } return rr; } /** * Creates an Action instance. * * @param actionType * @param contentType * @param requestEntity * @return * @throws JsonException * @throws ParseException */ public static Action createAction(int actionType, String contentType, String requestEntity) throws JsonException, ParseException { String comments = null; String delegateTo = null; Date counterStart = null; Date counterEnd = null; Boolean keepInformed = null; if ( StringUtil.isNotEmpty(requestEntity) ) { if ( contentType != null && contentType.startsWith(MediaType.APPLICATION_JSON) ) { Object object = JsonParser.fromJson(JsonJavaFactory.instanceEx, requestEntity); if ( object instanceof JsonObject ) { Object property = ((JsonObject)object).getJsonProperty("comments"); // $NON-NLS-1$ if ( property instanceof String ) { comments = (String)property; } property = ((JsonObject)object).getJsonProperty("delegateTo"); // $NON-NLS-1$ if ( property instanceof String ) { delegateTo = (String)property; } property = ((JsonObject)object).getJsonProperty("counterStart"); // $NON-NLS-1$ if ( property instanceof String ) { counterStart = dateFromString((String)property); } property = ((JsonObject)object).getJsonProperty("counterEnd"); // $NON-NLS-1$ if ( property instanceof String ) { counterEnd = dateFromString((String)property); } property = ((JsonObject)object).getJsonProperty("keepInformed"); // $NON-NLS-1$ if ( property instanceof Boolean ) { keepInformed = (Boolean)property; } } } else { throw new WebApplicationException(CalendarService.createErrorResponse("Unsupported media type.", Status.UNSUPPORTED_MEDIA_TYPE)); // $NLX-Utils.Unsupportedmediatype-1$ } } Action action = null; if ( actionType == Action.ACTION_DELEGATE) { action = new DelegateAction(comments, delegateTo, keepInformed); } else if ( actionType == Action.ACTION_DECLINE) { action = new DeclineAction(comments, keepInformed); } else if ( actionType == Action.ACTION_COUNTER ) { action = new CounterAction(comments, counterStart, counterEnd); } else { action = new Action(actionType, comments); } return action; } /** * Maps a calendar store error to an HTTP status code. * * @param e * @return */ public static Response.Status mapStatus(StoreException e) { Response.Status status = Response.Status.INTERNAL_SERVER_ERROR; switch(e.getErrorCode()) { case StoreException.ERR_BAD_IDENTIFIER: status = Response.Status.BAD_REQUEST; break; case StoreException.ERR_SENDING_NOTICES: status = Response.Status.INTERNAL_SERVER_ERROR; break; case StoreException.ERR_NEWER_VERSION_EXISTS: status = Response.Status.CONFLICT; break; case StoreException.ERR_ACTION_NOT_SUPPORTED: status = Response.Status.BAD_REQUEST; break; case StoreException.ERR_INVITE_NOT_ACCEPTED: status = Response.Status.BAD_REQUEST; break; case StoreException.ERR_PERSONAL_CHANGES: status = Response.Status.CONFLICT; break; case StoreException.ERR_IDENTIFIER_NOT_FOUND: status = Response.Status.NOT_FOUND; break; case StoreException.ERR_ENTRY_EXISTS: status = Response.Status.CONFLICT; break; case StoreException.ERR_BAD_ACTION: status = Response.Status.BAD_REQUEST; break; case StoreException.ERR_INVALID_ICALSTR: status = Response.Status.BAD_REQUEST; break; } return status; } /** * Gets a map of extra properties to add to a JSON error. * * <p>For now, the map holds one name/value pair called "cserror". * * @param e * @return */ public static Map<String, Object> getExtraProps(StoreException e) { Map<String, Object> map = new HashMap<String, Object>(); map.put("cserror", new Integer(CALENDAR_STORE_ERROR_BASE + e.getErrorCode())); // $NON-NLS-1$ return map; } /** * Reads an iCalendar property value and then unescapes it. * * @param property * RFC Java * \n \r\n * \, , * \; ; * \\ \ * @return */ public static String getUnescapedString(Property property) { String value = null; if ( property != null ) { value = property.getValue(); } if ( value != null ) { // Replace all occurences of "\\n" with "\r\n". value = value.replaceAll("(?<!\\\\)\\\\n", "\r\n"); // $NON-NLS-1$ $NON-NLS-2$ value = value.replaceAll("(?<!\\\\)\\\\N", "\r\n"); // $NON-NLS-1$ $NON-NLS-2$ // Unescape all commas. value = value.replaceAll("\\\\,", ","); // Unescape all semicolons value = value.replaceAll("\\\\;", ";"); // Unescape all backslashes. value = value.replaceAll("\\\\\\\\", "\\\\"); } return value; } }