/* * Copyright 2007, MetaDimensional Technologies Inc. * * * This file is part of the RememberTheMilk Java API. * * The RememberTheMilk Java API is free software; you can redistribute it * and/or modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of the * License, or (at your option) any later version. * * The RememberTheMilk Java API is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ package org.weloveastrid.rmilk.api; import java.util.ArrayList; import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Map.Entry; import java.util.Set; import org.w3c.dom.Element; import org.weloveastrid.rmilk.api.data.RtmAuth; import org.weloveastrid.rmilk.api.data.RtmData; import org.weloveastrid.rmilk.api.data.RtmFrob; import org.weloveastrid.rmilk.api.data.RtmList; import org.weloveastrid.rmilk.api.data.RtmLists; import org.weloveastrid.rmilk.api.data.RtmLocation; import org.weloveastrid.rmilk.api.data.RtmTask; import org.weloveastrid.rmilk.api.data.RtmTask.Priority; import org.weloveastrid.rmilk.api.data.RtmTaskList; import org.weloveastrid.rmilk.api.data.RtmTaskNote; import org.weloveastrid.rmilk.api.data.RtmTaskSeries; import org.weloveastrid.rmilk.api.data.RtmTasks; import org.weloveastrid.rmilk.api.data.RtmTimeline; /** * A major part of the RTM API implementation is here. * * @author Will Ross Jun 21, 2007 * @author Edouard Mercier, since 2008.04.15 * @author timsu January 2009 */ @SuppressWarnings("nls") public class ServiceImpl { public final static String SERVER_HOST_NAME = "api.rememberthemilk.com"; //$NON-NLS-1$ public final static int SERVER_PORT_NUMBER = 80; public final static String REST_SERVICE_URL_POSTFIX = "/services/rest/"; //$NON-NLS-1$ private final ApplicationInfo applicationInfo; private final Invoker invoker; private final Prefs prefs; private String currentAuthToken; RtmFrob tempFrob; public ServiceImpl(ApplicationInfo applicationInfo) throws ServiceInternalException { invoker = new Invoker(SERVER_HOST_NAME, SERVER_PORT_NUMBER, REST_SERVICE_URL_POSTFIX, applicationInfo); this.applicationInfo = applicationInfo; prefs = new Prefs(); if (applicationInfo.getAuthToken() != null) { currentAuthToken = applicationInfo.getAuthToken(); } else { currentAuthToken = prefs.getAuthToken(); } } public boolean isServiceAuthorized() throws ServiceException { if (currentAuthToken == null) return false; try { /* RtmAuth auth = */auth_checkToken(currentAuthToken); return true; } catch (ServiceException e) { if (e.getResponseCode() != 98) { throw e; } else { // Bad token. currentAuthToken = null; return false; } } } public String beginAuthorization(RtmAuth.Perms permissions) throws ServiceException { // Instructions from the "User authentication for desktop applications" // section at http://www.rememberthemilk.com/services/api/authentication.rtm tempFrob = auth_getFrob(); return beginAuthorization(tempFrob, permissions); } public String beginAuthorization(RtmFrob frob, RtmAuth.Perms permissions) throws ServiceException { String authBaseUrl = "http://" + SERVER_HOST_NAME + "/services/auth/"; Param[] params = new Param[] { new Param("api_key", applicationInfo.getApiKey()), new Param("perms", permissions.toString()), new Param("frob", frob.getValue()) }; Param sig = new Param("api_sig", invoker.calcApiSig(params)); StringBuilder authUrl = new StringBuilder(authBaseUrl); authUrl.append("?"); for (Param param : params) { authUrl.append(param.getName()).append("=").append(param.getValue()).append("&"); } authUrl.append(sig.getName()).append("=").append(sig.getValue()); return authUrl.toString(); } public String completeAuthorization() throws ServiceException { return completeAuthorization(tempFrob); } public String completeAuthorization(RtmFrob frob) throws ServiceException { currentAuthToken = auth_getToken(frob.getValue()); prefs.setAuthToken(currentAuthToken); return currentAuthToken; } public RtmAuth auth_checkToken(String authToken) throws ServiceException { Element response = invoker.invoke(new Param("method", "rtm.auth.checkToken"), new Param("auth_token", authToken), new Param("api_key", applicationInfo.getApiKey())); return new RtmAuth(response); } public RtmFrob auth_getFrob() throws ServiceException { return new RtmFrob(invoker.invoke(new Param("method", "rtm.auth.getFrob"), new Param("api_key", applicationInfo.getApiKey()))); } public String auth_getToken(String frob) throws ServiceException { Element response = invoker.invoke(new Param("method", "rtm.auth.getToken"), new Param("frob", frob), new Param("api_key", applicationInfo.getApiKey())); return new RtmAuth(response).getToken(); } public void contacts_add() { throw new UnsupportedOperationException("Not supported yet."); } public void contacts_delete() { throw new UnsupportedOperationException("Not supported yet."); } public void contacts_getList() { throw new UnsupportedOperationException("Not supported yet."); } public void groups_add() { throw new UnsupportedOperationException("Not supported yet."); } public void groups_addContact() { throw new UnsupportedOperationException("Not supported yet."); } public void groups_delete() { throw new UnsupportedOperationException("Not supported yet."); } public void groups_getList() { throw new UnsupportedOperationException("Not supported yet."); } public void groups_removeContact() { throw new UnsupportedOperationException("Not supported yet."); } public RtmList lists_add(String timelineId, String listName) throws ServiceException { Element response = invoker.invoke(new Param("method", "rtm.lists.add"), new Param("auth_token", currentAuthToken), new Param("api_key", applicationInfo.getApiKey()), new Param("name", listName), new Param("timeline", timelineId)); return new RtmList(response); } public void lists_archive() { throw new UnsupportedOperationException("Not supported yet."); } public void lists_delete(String timelineId, String listId) throws ServiceException { invoker.invoke(new Param("method", "rtm.lists.delete"), new Param("auth_token", currentAuthToken), new Param("api_key", applicationInfo.getApiKey()), new Param("timeline", timelineId), new Param("list_id", listId)); } public RtmLists lists_getList() throws ServiceException { Element response = invoker.invoke(new Param("method", "rtm.lists.getList"), new Param("auth_token", currentAuthToken), new Param("api_key", applicationInfo.getApiKey())); return new RtmLists(response); } public RtmList lists_getList(String listName) throws ServiceException { RtmLists fullList = lists_getList(); for (Entry entry : fullList.getLists().entrySet()) { if (entry.getValue().getName().equals(listName)) { return entry.getValue(); } } return null; } public void lists_setDefaultList() { throw new UnsupportedOperationException("Not supported yet."); } public RtmList lists_setName(String timelineId, String listId, String newName) throws ServiceException { Element response = invoker.invoke(new Param("method", "rtm.lists.setName"), new Param("timeline", timelineId), new Param("list_id", listId), new Param("name", newName), new Param("auth_token", currentAuthToken), new Param("api_key", applicationInfo.getApiKey())); return new RtmList(response); } public void lists_unarchive() { throw new UnsupportedOperationException("Not supported yet."); } public void reflection_getMethodInfo() { throw new UnsupportedOperationException("Not supported yet."); } public void reflection_getMethods() { throw new UnsupportedOperationException("Not supported yet."); } public void settings_getList() { throw new UnsupportedOperationException("Not supported yet."); } /** * Adds a task, name, to the list specified by list_id. * @param timelineId * @param listId can be null to omit this parameter (assumes Inbox) * @param name * @return * @throws ServiceException */ public RtmTaskSeries tasks_add(String timelineId, String listId, String name) throws ServiceException { Element response; if(listId != null) response = invoker.invoke(new Param("method", "rtm.tasks.add"), new Param("timeline", timelineId), new Param("list_id", listId), new Param("name", name), new Param("auth_token", currentAuthToken), new Param("api_key", applicationInfo.getApiKey())); else response = invoker.invoke(new Param("method", "rtm.tasks.add"), new Param("timeline", timelineId), new Param("name", name), new Param("auth_token", currentAuthToken), new Param("api_key", applicationInfo.getApiKey())); RtmTaskList rtmTaskList = new RtmTaskList(response); if (rtmTaskList.getSeries().size() == 1) { return rtmTaskList.getSeries().get(0); } else if (rtmTaskList.getSeries().size() > 1) { throw new ServiceInternalException("Internal error: more that one task (" + rtmTaskList.getSeries().size() + ") has been created"); } throw new ServiceInternalException("Internal error: no task has been created"); } public void tasks_addTags() { throw new UnsupportedOperationException("Not supported yet."); } public void tasks_complete(String timelineId, String listId, String taskSeriesId, String taskId) throws ServiceException { invoker.invoke(new Param("method", "rtm.tasks.complete"), new Param("timeline", timelineId), new Param("list_id", listId), new Param("taskseries_id", taskSeriesId), new Param("task_id", taskId), new Param("auth_token", currentAuthToken), new Param("api_key", applicationInfo.getApiKey())); } public void tasks_delete(String timelineId, String listId, String taskSeriesId, String taskId) throws ServiceException { invoker.invoke(new Param("method", "rtm.tasks.delete"), new Param("timeline", timelineId), new Param("list_id", listId), new Param("taskseries_id", taskSeriesId), new Param("task_id", taskId), new Param("auth_token", currentAuthToken), new Param("api_key", applicationInfo.getApiKey())); } public RtmTasks tasks_getList(String listId, String filter, Date lastSync) throws ServiceException { Set params = new HashSet(); params.add(new Param("method", "rtm.tasks.getList")); if (listId != null) { params.add(new Param("list_id", listId)); } if (filter != null) { params.add(new Param("filter", filter)); } if (lastSync != null) { params.add(new Param("last_sync", lastSync)); } params.add(new Param("auth_token", currentAuthToken)); params.add(new Param("api_key", applicationInfo.getApiKey())); return new RtmTasks(invoker.invoke(params.toArray(new Param[params.size()]))); } public RtmTaskSeries tasks_getTask(String taskName) throws ServiceException { return tasks_getTask(null, taskName); } public RtmTaskSeries tasks_getTask(String taskSeriesId, String taskName) throws ServiceException { Set params = new HashSet(); params.add(new Param("method", "rtm.tasks.getList")); params.add(new Param("auth_token", currentAuthToken)); params.add(new Param("api_key", applicationInfo.getApiKey())); params.add(new Param("filter", "name:\"" + taskName+"\"")); RtmTasks rtmTasks = new RtmTasks(invoker.invoke(params.toArray(new Param[params.size()]))); return findTask(taskSeriesId, rtmTasks); } private RtmTaskSeries findTask(String taskId, RtmTasks rtmTasks) { for (RtmTaskList list : rtmTasks.getLists()) { for (RtmTaskSeries series : list.getSeries()) { if (taskId != null) { if (series.getId().equals(taskId)) { return series; } } else { return series; } } } return null; } public void tasks_movePriority() { throw new UnsupportedOperationException("Not supported yet."); } public RtmTaskSeries tasks_moveTo(String timelineId, String fromListId, String toListId, String taskSeriesId, String taskId) throws ServiceException { if(fromListId.equals(toListId)) return null; Element elt = invoker.invoke(new Param("method", "rtm.tasks.moveTo"), new Param("timeline", timelineId), new Param("from_list_id", fromListId), new Param("to_list_id", toListId), new Param("taskseries_id", taskSeriesId), new Param("task_id", taskId), new Param("auth_token", currentAuthToken), new Param("api_key", applicationInfo.getApiKey())); RtmTaskList rtmTaskList = new RtmTaskList(elt); return findTask(taskSeriesId, taskId, rtmTaskList); } public void tasks_postpone() { throw new UnsupportedOperationException("Not supported yet."); } public void tasks_removeTags() { throw new UnsupportedOperationException("Not supported yet."); } public void tasks_setDueDate(String timelineId, String listId, String taskSeriesId, String taskId, Date due, boolean hasDueTime) throws ServiceException { final boolean setDueDate = (due != null); if (setDueDate == true) { invoker.invoke(new Param("method", "rtm.tasks.setDueDate"), new Param("timeline", timelineId), new Param("list_id", listId), new Param("taskseries_id", taskSeriesId), new Param("task_id", taskId), new Param("due", due), new Param("has_due_time", hasDueTime ? "1" : "0"), new Param("auth_token", currentAuthToken), new Param("api_key", applicationInfo.getApiKey())); } else { invoker.invoke(new Param("method", "rtm.tasks.setDueDate"), new Param("timeline", timelineId), new Param("list_id", listId), new Param("taskseries_id", taskSeriesId), new Param("task_id", taskId), new Param("auth_token", currentAuthToken), new Param("api_key", applicationInfo.getApiKey())); } } public void tasks_setEstimate(String timelineId, String listId, String taskSeriesId, String taskId, String newEstimate) throws ServiceException { invoker.invoke(new Param("method", "rtm.tasks.setEstimate"), new Param("timeline", timelineId), new Param("list_id", listId), new Param("taskseries_id", taskSeriesId), new Param("task_id", taskId), new Param("estimate", newEstimate), new Param("auth_token", currentAuthToken), new Param("api_key", applicationInfo.getApiKey())); } public void tasks_setName(String timelineId, String listId, String taskSeriesId, String taskId, String newName) throws ServiceException { invoker.invoke(new Param("method", "rtm.tasks.setName"), new Param("timeline", timelineId), new Param("list_id", listId), new Param("taskseries_id", taskSeriesId), new Param("task_id", taskId), new Param("name", newName), new Param("auth_token", currentAuthToken), new Param("api_key", applicationInfo.getApiKey())); } private RtmTaskSeries findTask(String taskSeriesId, String taskId, RtmTaskList rtmTaskList) { for (RtmTaskSeries series : rtmTaskList.getSeries()) { if (series.getId().equals(taskSeriesId) && series.getTask().getId().equals(taskId)) { return series; } } return null; } public void tasks_setPriority(String timelineId, String listId, String taskSeriesId, String taskId, Priority priority) throws ServiceException { invoker.invoke(new Param("method", "rtm.tasks.setPriority"), new Param("timeline", timelineId), new Param("list_id", listId), new Param("taskseries_id", taskSeriesId), new Param("task_id", taskId), new Param("priority", RtmTask.convertPriority(priority)), new Param("auth_token", currentAuthToken), new Param("api_key", applicationInfo.getApiKey())); } public void tasks_setRecurrence(String timelineId, String listId, String taskSeriesId, String taskId, String repeat) throws ServiceException { invoker.invoke(new Param("method", "rtm.tasks.setRecurrence"), new Param("timeline", timelineId), new Param("list_id", listId), new Param("taskseries_id", taskSeriesId), new Param("task_id", taskId), new Param("repeat", repeat), new Param("auth_token", currentAuthToken), new Param("api_key", applicationInfo.getApiKey())); } public void tasks_setTags(String timelineId, String listId, String taskSeriesId, String taskId, String[] tags) throws ServiceException { StringBuilder tagString = new StringBuilder(); if(tags != null) { for(int i = 0; i < tags.length; i++) { tagString.append(tags[i].replace(" ", "_")); if(i < tags.length - 1) tagString.append(","); } } invoker.invoke(new Param("method", "rtm.tasks.setTags"), new Param("timeline", timelineId), new Param("list_id", listId), new Param("taskseries_id", taskSeriesId), new Param("task_id", taskId), new Param("tags", tagString.toString()), new Param("auth_token", currentAuthToken), new Param("api_key", applicationInfo.getApiKey())); } public void tasks_setURL() { throw new UnsupportedOperationException("Not supported yet."); } public void tasks_uncomplete(String timelineId, String listId, String taskSeriesId, String taskId) throws ServiceException { invoker.invoke(new Param("method", "rtm.tasks.uncomplete"), new Param("timeline", timelineId), new Param("list_id", listId), new Param("taskseries_id", taskSeriesId), new Param("task_id", taskId), new Param("auth_token", currentAuthToken), new Param("api_key", applicationInfo.getApiKey())); } public RtmTaskNote tasks_notes_add(String timelineId, String listId, String taskSeriesId, String taskId, String title, String text) throws ServiceException { Element elt = invoker.invoke(new Param("method", "rtm.tasks.notes.add"), new Param("timeline", timelineId), new Param("list_id", listId), new Param("taskseries_id", taskSeriesId), new Param("task_id", taskId), new Param("note_title", title), new Param("note_text", text), new Param("auth_token", currentAuthToken), new Param("api_key", applicationInfo.getApiKey())); return new RtmTaskNote(elt); } public void tasks_notes_delete(String timelineId, String noteId) throws ServiceException { invoker.invoke(new Param("method", "rtm.tasks.notes.delete"), new Param("timeline", timelineId), new Param("note_id", noteId), new Param("auth_token", currentAuthToken), new Param("api_key", applicationInfo.getApiKey())); } public RtmTaskNote tasks_notes_edit(String timelineId, String noteId, String title, String text) throws ServiceException { Element elt = invoker.invoke(new Param("method", "rtm.tasks.notes.edit"), new Param("timeline", timelineId), new Param("note_id", noteId), new Param("note_title", title), new Param("note_text", text), new Param("auth_token", currentAuthToken), new Param("api_key", applicationInfo.getApiKey())); return new RtmTaskNote(elt); } public RtmTaskSeries tasks_setLocation(String timelineId, String listId, String taskSeriesId, String taskId, String locationId) throws ServiceException { Element response = invoker.invoke(new Param("method", "rtm.tasks.setLocation"), new Param("timeline", timelineId), new Param("list_id", listId), new Param("taskseries_id", taskSeriesId), new Param("task_id", taskId), new Param("location_id", locationId), new Param("auth_token", currentAuthToken), new Param("api_key", applicationInfo.getApiKey())); RtmTaskList rtmTaskList = new RtmTaskList(response); return findTask(taskSeriesId, taskId, rtmTaskList); } public RtmTaskSeries tasks_setURL(String timelineId, String listId, String taskSeriesId, String taskId, String url) throws ServiceException { Element response = invoker.invoke(new Param("method", "rtm.tasks.setURL"), new Param("timeline", timelineId), new Param("list_id", listId), new Param("taskseries_id", taskSeriesId), new Param("task_id", taskId), new Param("url", url), new Param("auth_token", currentAuthToken), new Param("api_key", applicationInfo.getApiKey())); RtmTaskList rtmTaskList = new RtmTaskList(response); return findTask(taskSeriesId, taskId, rtmTaskList); } public void test_echo() { throw new UnsupportedOperationException("Not supported yet."); } public void test_login() { throw new UnsupportedOperationException("Not supported yet."); } public void time_convert() { throw new UnsupportedOperationException("Not supported yet."); } public void time_parse() { throw new UnsupportedOperationException("Not supported yet."); } public String timelines_create() throws ServiceException { return new RtmTimeline(invoker.invoke(new Param("method", "rtm.timelines.create"), new Param("auth_token", currentAuthToken), new Param("api_key", applicationInfo.getApiKey()))).getId(); } public void timezones_getList() { throw new UnsupportedOperationException("Not supported yet."); } public void transactions_undo() { throw new UnsupportedOperationException("Not supported yet."); } public List locations_getList() throws ServiceException { Element result = invoker.invoke(new Param("method", "rtm.locations.getList"), new Param("auth_token", currentAuthToken), new Param("api_key", applicationInfo.getApiKey())); List locations = new ArrayList(); for (Element child : RtmData.children(result, "location")) { locations.add(new RtmLocation(child)); } return locations; } }