How to automatically delete all objects of one user

I just wrote the following quick&dirty hack to delete all objects and folders created by an OXHE User. It can be useful to clean up a context or to just delete all data from one user. The following JAVA file contains the logic which is needed to login/search/delete via the Open-Xchange HTTP API.


package com.openexchange.tools.cleaner;

import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.PutMethod;
import org.apache.commons.httpclient.methods.RequestEntity;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import org.apache.commons.httpclient.protocol.Protocol;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

/**
 *
 * @author cutmasta
 */
public class Start {

    Log LOG = LogFactory.getLog(Start.class);
    private SessionObject so = null;
    private String username = null;
    private String password = null;
    private String host = "localhost";
    private String protocol = "http";
    private static String CONTENT_TYPE_JSCRIPT = "text/javascript";
    private static String CHARSET_JSCRIPT = "UTF-8";
    private String TASK_SEARCH = "tasks";
    private String CONTACT_SEARCH = "contacts";
    private String CALENDAR_SEARCH = "calendar";
    private String INFOSTORE_SEARCH = "infostore";

    public static void main(String[] args) {
        new Start(args);
    }

    private Start(String[] args) {

        if (args.length != 4) {
            LOG.info("Arguments needed: <host> <protocol> <username> <password>");
            LOG.info("Example: test.org https test@sp4.com test");
            exitApp();
        }

        host = args[0].trim();
        protocol = args[1].trim();
        username = args[2].trim();
        password = args[3].trim();

        if (protocol.equals("https")) {
            Protocol easyhttps = new Protocol("https", new EasySSLProtocolSocketFactory(), 443);
            Protocol.registerProtocol("https", easyhttps);
        }

        doLogin();

        try {

            // search all modules and fetch the id arrays
            doSearch();
        } catch (IOException ex) {
            LOG.error(ex);
        } catch (JSONException ex) {
            LOG.error("Error in process", ex);
        }


    }

    public String getSessionID() {
        return getSessionObject().getSessionid();
    }

    public SessionObject getSessionObject() {
        return so;
    }

    public String getUsername() {
        return username;
    }

    public String getPassword() {
        return password;
    }

    public String getHost() {
        return host;
    }

    private void doDeleteCreatedFolders(JSONArray rootfolders, String timestamp) throws JSONException, IOException {

        /* 
         * In this method here we delete all folders        
         * 
         */


        // loop through all root folders
        for (int i = 0; i < rootfolders.length(); i++) {
            
            // for each root/system folder , loop and delete its subfolders
            JSONObject subfolders_of_rootfolder_response = doGetSubFolders(rootfolders.getJSONArray(i).getInt(0));
            JSONArray subfolders_of_rootfolder = subfolders_of_rootfolder_response.getJSONArray("data");            
            for (int b = 0; b < subfolders_of_rootfolder.length(); b++) {
                JSONArray tmp_folder = subfolders_of_rootfolder.getJSONArray(b);
                if (!tmp_folder.getString(0).equalsIgnoreCase("default")) {
                    LOG.debug("Deleting recursive all private folders with array: " + tmp_folder);
                    deleteRecursiveFolder(tmp_folder);
                }
            }

        }



    }

    /**
     * This delete the subfolders of the given folder and the folder itself!
     * @param my_private_folder_array
     * @throws java.io.IOException
     * @throws org.json.JSONException
     */
    private void deleteRecursiveFolder(JSONArray my_private_folder_array) throws IOException, JSONException {

        JSONObject my_private_subfolders_response = doGetSubFolders(my_private_folder_array.getInt(0));

        // only delete if subfolders exists
        //if (my_private_subfolders_response.length() > 0) {
        if (my_private_subfolders_response.getJSONArray("data").length() > 0) {

            LOG.debug(my_private_subfolders_response);

            JSONArray my_private_subfolders = my_private_subfolders_response.getJSONArray("data");

            String my_private_subfolders_timestamp = my_private_subfolders_response.getString("timestamp");

            //LOG.info("Private folders list: " +my_private_subfolders);
            for (int i = 0; i < my_private_subfolders.length(); i++) {
                JSONArray folder_data = my_private_subfolders.getJSONArray(i);
                LOG.debug("Deleting folder with ID " + folder_data.getInt(0) + " and parent ID " + folder_data.getInt(1) + " - Tstamp " + my_private_subfolders_timestamp);
                doDeleteFolder(folder_data.getInt(0), folder_data.getInt(1), my_private_subfolders_timestamp);
            }
        }


        /* Now delete the folder itself , which could contain the subfolders before!
         * We must "get" the folder data because we need the correct timestamp for deleting!
         */
        JSONObject top_level_folder_data = doGetSingleFolder(my_private_folder_array.getInt(0));
        if(!my_private_folder_array.isNull(0) && !my_private_folder_array.isNull(1) && !top_level_folder_data.isNull("timestamp")){
            // execute delete
            doDeleteFolder(my_private_folder_array.getInt(0), my_private_folder_array.getInt(1), top_level_folder_data.getString("timestamp"));
        }   
        
        

    }

    private void doDeleteFolder(int folder_id, int parent_id, String tstamp) {

        try {

            PutMethod deleteMethod = new PutMethod(getProtocol() + "://" + getHost() + "/ajax/folders?action=delete&timestamp=" + tstamp + "&session=" + getSessionObject().getSessionid());

            RequestEntity data = new StringRequestEntity("[" + folder_id + "]", CONTENT_TYPE_JSCRIPT, CHARSET_JSCRIPT);
            deleteMethod.setRequestEntity(data);
            getSessionObject().getOxClient().executeMethod(deleteMethod);

            JSONObject responseObject = ResponseTools.reponse2JsonObject(deleteMethod);

            LOG.debug("Delete folder response is: " + responseObject);

        } catch (Exception ex) {
            LOG.error("Error deleting folder with ID " + folder_id + " and parent ID " + parent_id, ex);
        }
    }

    private JSONObject doGetSubFolders(int parent_folder) throws IOException, JSONException {

        // 1 = id
        // 20 = Object ID of the parent folder.
        // 300 = Name of this folder.
        // 301 = Name of the module which implements this folder.
        // 302 = type    // 1 	 private
        //2 	public
        //3 	shared
        // 5 	system folder
        // 304 =  true if this folder has subfolders.
        // 305 = own_rights

        try {


            GetMethod folder_search = new GetMethod(getProtocol() + "://" + getHost() + "/ajax/folders?action=list&columns=1,20,300,301,302,304,305&parent=" + parent_folder + "&session=" + getSessionObject().getSessionid());


            getSessionObject().getOxClient().executeMethod(folder_search);

            JSONObject responseObject = ResponseTools.reponse2JsonObject(folder_search);

            return responseObject;

        } catch (IOException ex) {
            LOG.error("Generic IO error while searching folders", ex);
            throw ex;
        } catch (JSONException ex) {
            LOG.error("JSON error while searching folders", ex);
            throw ex;
        }
    }

    private JSONObject doGetSingleFolder(int folder_id) throws IOException, JSONException {

        // 1 = id
        // 20 = Object ID of the parent folder.
        // 300 = Name of this folder.
        // 301 = Name of the module which implements this folder.
        // 302 = type    // 1 	 private
        //2 	public
        //3 	shared
        // 5 	system folder
        // 304 =  true if this folder has subfolders.
        // 305 = own_rights

        try {


            GetMethod folder_search = new GetMethod(getProtocol() + "://" + getHost() + "/ajax/folders?action=get&id=" + folder_id + "&columns=1,20,300,301,302,304,305&session=" + getSessionObject().getSessionid());


            getSessionObject().getOxClient().executeMethod(folder_search);

            JSONObject responseObject = ResponseTools.reponse2JsonObject(folder_search);

            return responseObject;

        } catch (IOException ex) {
            LOG.error("Generic IO error while searching folders", ex);
            throw ex;
        } catch (JSONException ex) {
            LOG.error("JSON error while searching folders", ex);
            throw ex;
        }
    }

    private void doLogin() {

        LOG.info("Using " + getProtocol() + "://" + getUsername() + "@" + getHost() + " for connections!");

        HttpClient client = new HttpClient();


        PostMethod login_post = new PostMethod(getProtocol() + "://" + getHost() + "/ajax/login?action=login");
        login_post.addParameter("name", getUsername());
        login_post.addParameter("password", getPassword());

        try {

            client.executeMethod(login_post);

            JSONObject loginObject = ResponseTools.reponse2JsonObject(login_post);

            if (loginObject.has("session")) {
                so = new SessionObject();
                getSessionObject().setCookies(client.getState().getCookies());
                getSessionObject().setSessionid((String) loginObject.get("session"));
                getSessionObject().setOxClient(client);
                LOG.info("Login successfull!");
            } else {
                String errmsg = (String) loginObject.get("error");
                LOG.info("Error login into system!\n" + errmsg);
                exitApp();
            }
        } catch (Exception exp) {
            LOG.error("LOGIN ERROR", exp);
        } finally {
            login_post.releaseConnection();
        }
    }

    private void doSearch() throws JSONException, IOException {

        LOG.info("Starting search for objects...");

        JSONObject task_objects = doSearchInModule(TASK_SEARCH);
        JSONObject contact_objects = doSearchInModule(CONTACT_SEARCH);
        JSONObject calendar_objects = doSearchInModule(CALENDAR_SEARCH);
        JSONObject store_objects = doSearchInModule(INFOSTORE_SEARCH);

        LOG.info("Search done!");

        //LOG.info("Found " + task_objects.getJSONArray("data").length() + " task items");
        //LOG.info("Found " + contact_objects.getJSONArray("data").length() + " contact items");
        //LOG.info("Found " + calendar_objects.getJSONArray("data").length() + " calendar items");
        //LOG.info("Found " + store_objects.getJSONArray("data").length() + " infostore items");

        LOG.info("Starting delete process...");

        doDeleteMultiple(task_objects, contact_objects, calendar_objects, store_objects);

        LOG.info("Deleting Objects done!");


        LOG.info("Starting delete process for folders...");

        // search all folders and delete them
        JSONObject rootfolders_response = doGetRootFolders();

        JSONArray rootfolders = rootfolders_response.getJSONArray("data");

        doDeleteCreatedFolders(rootfolders, rootfolders_response.getString("timestamp"));

        LOG.info("Deleting folders done!");

    }

    private JSONArray buildDeleteArray(JSONObject module_objects, String MODULE) throws JSONException {

        JSONArray dat_array = new JSONArray();

        LOG.debug("Building delete array from json object: " + module_objects);

        if (module_objects.has("data") && module_objects.has("timestamp")) {

            String tstamp = module_objects.getString("timestamp");
            JSONArray srv_data_ids = module_objects.getJSONArray("data");
            for (int i = 0; i < srv_data_ids.length(); i++) {
                JSONArray ids = (JSONArray) srv_data_ids.get(i);
                JSONObject js = new JSONObject();
                js.put("module", MODULE);
                js.put("timestamp", tstamp);
                js.put("action", "delete");



                JSONObject del_ids = new JSONObject();
                del_ids.put("id", ids.getInt(0));
                del_ids.put("folder", ids.getInt(1));

                // workaround because infostore expects an array with a 1 json object which contains the ids
                if (MODULE.equals(INFOSTORE_SEARCH)) {
                    // send array with object
                    JSONArray tmp = new JSONArray();
                    tmp.put(del_ids);
                    js.put("data", tmp);
                } else {
                    // send normal id 
                    js.put("data", del_ids);
                }
                dat_array.put(js);
            }
        }


        return dat_array;
    }

    private void doDeleteMultiple(JSONObject task_objects, JSONObject contact_objects, JSONObject calendar_objects, JSONObject store_objects) throws JSONException {

        LOG.info("Deleting tasks...");
        fireDeleteMultiple(buildDeleteArray(task_objects, TASK_SEARCH));
        LOG.info("Deleting contacts...");
        fireDeleteMultiple(buildDeleteArray(contact_objects, CONTACT_SEARCH));
        LOG.info("Deleting appointments...");
        fireDeleteMultiple(buildDeleteArray(calendar_objects, CALENDAR_SEARCH));
        LOG.info("Deleting infostore objects...");
        fireDeleteMultiple(buildDeleteArray(store_objects, INFOSTORE_SEARCH));
    }

    private JSONObject doGetRootFolders() throws IOException, JSONException {

        // 1 = id
        // 20 = Object ID of the parent folder.
        // 300 = Name of this folder.
        // 301 = Name of the module which implements this folder.
        // 302 = type    // 1 	 private
        //2 	public
        //3 	shared
        // 5 	system folder
        // 304 =  true if this folder has subfolders.
        // 305 = own_rights

        try {


            GetMethod folder_search = new GetMethod(getProtocol() + "://" + getHost() + "/ajax/folders?action=root&columns=1,20,300,301,302,304,305&session=" + getSessionObject().getSessionid());


            getSessionObject().getOxClient().executeMethod(folder_search);

            return ResponseTools.reponse2JsonObject(folder_search);

        } catch (IOException ex) {
            LOG.error("Generic IO error while searching folders", ex);
            throw ex;
        } catch (JSONException ex) {
            LOG.error("JSON error while searching folders", ex);
            throw ex;
        }






    }

    private void fireDeleteMultiple(JSONArray del_data_array) {

        try {

            //LOG.info("Sending delete array: " + del_data_array);

            PutMethod deleteMethod = new PutMethod(getProtocol() + "://" + getHost() + "/ajax/multiple?continue=true&session=" + getSessionObject().getSessionid());

            RequestEntity data = new StringRequestEntity(del_data_array.toString(), CONTENT_TYPE_JSCRIPT, CHARSET_JSCRIPT);
            deleteMethod.setRequestEntity(data);
            getSessionObject().getOxClient().executeMethod(deleteMethod);

            JSONArray responseObject = ResponseTools.reponse2JsonArray(deleteMethod);

        //LOG.info(responseObject);

        } catch (Exception ex) {
            LOG.error("Error deleting items", ex);
        }
    }

    private JSONObject doSearchInModule(String MODULE) {

        // FETCH ALL OBJECT IDS FROM ALL FOLDERS 
        // EXCLUDING PUBLIC ADRESSBOOK WHICH HAS ID 6        
        PutMethod searchMethod = new PutMethod(getProtocol() + "://" + getHost() + "/ajax/" + MODULE + "?action=search&columns=1,20&session=" + getSessionObject().getSessionid());

        try {

            RequestEntity data = new StringRequestEntity("{\"pattern\":\"*\"}", CONTENT_TYPE_JSCRIPT, CHARSET_JSCRIPT);
            searchMethod.setRequestEntity(data);
            getSessionObject().getOxClient().executeMethod(searchMethod);

            return ResponseTools.reponse2JsonObject(searchMethod);
        } catch (Exception ex) {
            LOG.error("Error searching " + MODULE, ex);
            return null;
        }
    }

    private void exitApp() {
        System.exit(1);
    }

    public String getProtocol() {
        return protocol;
    }
}