private void retrieveTasks(UpdateInfo updateInfo, long since, boolean update)
     throws RateLimitReachedException, Exception {
   String key = getKey(updateInfo);
   String urlString = TOODLEDO_TASKS_GET + "?key=" + key + "&after=" + since;
   String tasksJson = restHelper.makeRestCall(connector(), updateInfo.getGuestId(), 1, urlString);
   JSONArray tasksArray = JSONArray.fromObject(tasksJson);
   JSONObject arrayInfo = tasksArray.getJSONObject(0);
   for (int i = 0; i < arrayInfo.getInt("total"); i++) {
     JSONObject task = tasksArray.getJSONObject(i + 1);
     if (task.has("total")) continue;
     long toodledo_id = task.getLong("id");
     if (update) {
       ToodledoTaskFacet oldTask =
           jpaDaoService.findOne(
               "toodledo.task.byToodledoId",
               ToodledoTaskFacet.class,
               updateInfo.getGuestId(),
               toodledo_id);
       if (oldTask != null) jpaDaoService.remove(oldTask.getClass(), oldTask.getId());
     }
     ToodledoTaskFacet taskFacet = new ToodledoTaskFacet();
     taskFacet.guestId = updateInfo.getGuestId();
     taskFacet.toodledo_id = toodledo_id;
     if (task.has("goal")) taskFacet.goal = task.getLong("goal");
     taskFacet.modified = task.getLong("modified");
     taskFacet.completed = task.getLong("completed");
     if (taskFacet.completed != 0) {
       taskFacet.start = taskFacet.modified * 1000;
       taskFacet.end = taskFacet.modified * 1000;
     }
     taskFacet.title = task.getString("title");
     taskFacet.api = connector().value();
     taskFacet.objectType = 1;
     jpaDaoService.persist(taskFacet);
   }
   if (update) {
     urlString = TOODLEDO_TASKS_GET_DELETED + "?key=" + key + "&after=" + since;
     String tasksToDeleteJson =
         restHelper.makeRestCall(connector(), updateInfo.getGuestId(), 1, urlString);
     JSONArray tasksToDeleteArray = JSONArray.fromObject(tasksToDeleteJson);
     arrayInfo = tasksToDeleteArray.getJSONObject(0);
     for (int i = 0; i < arrayInfo.getInt("num"); i++) {
       JSONObject task = tasksToDeleteArray.getJSONObject(i + 1);
       long toodledo_id = task.getLong("id");
       ToodledoTaskFacet taskToDelete =
           jpaDaoService.findOne(
               "toodledo.task.byToodledoId",
               ToodledoTaskFacet.class,
               updateInfo.getGuestId(),
               toodledo_id);
       if (taskToDelete != null)
         jpaDaoService.remove(taskToDelete.getClass(), taskToDelete.getId());
     }
   }
 }
  public String getSessionToken(UpdateInfo updateInfo, String userid)
      throws RateLimitReachedException, Exception {
    String appId = env.get("toodledo.appId");
    String appToken = env.get("toodledo.appToken");
    String signature = hash(userid + appToken);
    String url =
        TOODLEDO_ACCOUNT_TOKEN + "?userid=" + userid + ";appid=" + appId + ";sig=" + signature;
    try {
      String json = restHelper.makeRestCall(connector(), updateInfo.getGuestId(), -1, url);
      JSONObject accountTokenJson = JSONObject.fromObject(json);
      if (accountTokenJson.has("errorCode")) {
        String errorMessage = accountTokenJson.getString("errorDesc");
        throw new RuntimeException("Could not get toodledo session token: " + errorMessage);
      }

      String token = accountTokenJson.getString("token");

      return token;
    } catch (Exception e) {
      throw e;
    }
  }
 private void retrieveGoals(UpdateInfo updateInfo, long since, boolean update)
     throws RateLimitReachedException, Exception {
   String key = getKey(updateInfo);
   String urlString = TOODLEDO_GOALS_GET + "?key=" + key;
   String goalsJson = restHelper.makeRestCall(connector(), updateInfo.getGuestId(), 2, urlString);
   apiDataService.eraseApiData(updateInfo.getGuestId(), connector(), 2);
   JSONArray goalsArray = JSONArray.fromObject(goalsJson);
   for (int i = 0; i < goalsArray.size(); i++) {
     JSONObject goal = goalsArray.getJSONObject(i);
     ToodledoGoalFacet goalFacet = new ToodledoGoalFacet();
     goalFacet.archived = (byte) goal.getInt("archived");
     goalFacet.contributes = goal.getLong("contributes");
     goalFacet.level = goal.getInt("level");
     goalFacet.name = goal.getString("name");
     goalFacet.note = goal.getString("note");
     goalFacet.guestId = updateInfo.getGuestId();
     goalFacet.api = connector().value();
     goalFacet.objectType = 2;
     long now = System.currentTimeMillis();
     goalFacet.timeUpdated = now;
     jpaDaoService.persist(goalFacet);
   }
 }
  public String getToodledoUserid(long guestId, String email, String password)
      throws RateLimitReachedException, Exception {

    String appId = env.get("toodledo.appId");
    String appToken = env.get("toodledo.appToken");
    String signature = hash(email + appToken);

    String url =
        TOODLEDO_ACCOUNT_LOOKUP
            + "?appid="
            + appId
            + ";sig="
            + signature
            + ";email="
            + email
            + ";pass="******"userid");
    if (userid == null) return null;
    return userid;
  }
  private void sync(UpdateInfo updateInfo) throws Exception {
    String userKey = getKey(updateInfo);
    String url = TOODLEDO_ACCOUNT_INFO + "?key=" + userKey;
    String mostRecentJsonString =
        restHelper.makeRestCall(connector(), updateInfo.getGuestId(), -1, url);
    JSONObject mostRecentAccountInfo = JSONObject.fromObject(mostRecentJsonString);
    String lastAccountInfoJsonString =
        guestService.getApiKeyAttribute(updateInfo.getGuestId(), connector(), "lastAccountInfo");
    JSONObject lastSyncAccountInfo = JSONObject.fromObject(lastAccountInfoJsonString);

    if (mostRecentAccountInfo.has("errorCode"))
      throw new Exception(
          "Could not sync with toodledo: " + mostRecentAccountInfo.getString("errorDesc"));
    if (lastAccountInfoJsonString != null) {
      if (mostRecentAccountInfo.getLong("lastedit_goal")
          > lastSyncAccountInfo.getLong("lastedit_goal")) {
        retrieveGoals(updateInfo, lastSyncAccountInfo.getLong("lastedit_goal"), true);
      }
    } else retrieveGoals(updateInfo, 0, false);
    if (lastAccountInfoJsonString != null) {
      long lastTasksUpdateLocal =
          Math.max(
              lastSyncAccountInfo.getLong("lastedit_task"),
              lastSyncAccountInfo.getLong("lastdelete_task"));
      long lastTasksUpdateRemote =
          Math.max(
              mostRecentAccountInfo.getLong("lastedit_task"),
              mostRecentAccountInfo.getLong("lastdelete_task"));
      if (lastTasksUpdateRemote > lastTasksUpdateLocal) {
        retrieveTasks(updateInfo, lastTasksUpdateLocal, true);
      }
    } else {
      retrieveTasks(updateInfo, 0, false);
    }
    guestService.setApiKeyAttribute(
        updateInfo.getGuestId(), connector(), "lastAccountInfo", mostRecentJsonString);
  }