[Openvas-commits] r10532 - in trunk/openvas-manager: . src

scm-commit@wald.intevation.org scm-commit at wald.intevation.org
Mon Mar 14 06:50:02 CET 2011


Author: mattm
Date: 2011-03-14 06:49:56 +0100 (Mon, 14 Mar 2011)
New Revision: 10532

Modified:
   trunk/openvas-manager/CMakeLists.txt
   trunk/openvas-manager/ChangeLog
   trunk/openvas-manager/src/manage.c
   trunk/openvas-manager/src/manage.h
   trunk/openvas-manager/src/manage_sql.c
   trunk/openvas-manager/src/omp.c
   trunk/openvas-manager/src/otp.c
Log:
	Add a trashcan, including new OMP commands EMPTY_TRASHCAN and RESTORE.
	Hence increase database version to 42.  Add database migration from
	version 41 to 42.

	* CMakeLists.txt (OPENVASMD_DATABASE_VERSION): Increase to 42.

	* src/manage_sql.c (resource_t): New type.
	(LOCATION_TABLE, LOCATION_TRASH): New defines.
	(user_owns_trash_uuid, find_trash): New functions.
	(create_tables): Add trash tables.  Add trash location columns to tables
	task_escalators and tasks.
	(migrate_41_to_42): New function.
	(database_migrators): Add migrate_41_to_42.
	(delete_escalator, delete_task, delete_target, delete_config)
	(delete_lsc_credential, delete_agent, delete_schedule)
	(delete_report_format, delete_slave): Add ultimate flag param.  Update callers.
	(init_escalator_iterator, init_escalator_data_iterator)
	(init_task_iterator, init_target_iterator, init_config_iterator)
	(init_lsc_credential_iterator, init_agent_iterator, init_schedule_iterator)
	(init_report_format_iterator, init_report_format_param_iterator)
	(init_slave_iterator): Add trash flag param.  Update callers.
	(init_manage, set_task_requested, delete_report): Add
	TASK_STATUS_DELETE_ULTIMATE_REQUESTED cases.
	(task_config_uuid, task_config_name): Add trash check.
	(task_config_in_trash, task_target_in_trash, task_slave_in_trash)
	(task_escalator_in_trash, task_schedule_in_trash): New functions.
	(add_task_escalator, set_task_escalator): Set location field.
	(request_delete_task): Call delete_task_lock instead of delete_task.
	(request_delete_task_uuid): New function.  Allows OMP DELETE_TASK to pass
	UUID.
	(delete_task_lock): New function.  Calls delete_task inside transaction.
	(delete_trash_tasks, find_trash_task): New functions.
	(find_task): Exclude trash tasks.
	(target_iterator_ssh_trash, target_iterator_smb_trash, trash_target_hosts)
	(trash_target_in_use, trash_config_in_use, trash_lsc_credential_uuid)
	(trash_lsc_credential_name, report_format_trash_global)
	(trash_slave_in_use): New functions.
	(target_in_use, init_schedule_task_iterator, slave_in_use): Take trash
	resources into account.
	(manage_restore, manage_empty_trashcan): New functions.  Trashcan
	specific.

	* src/manage.c: Update iterator calls.
	(run_status_name, run_slave_task, manage_check_current_task): Add
	TASK_STATUS_DELETE_ULTIMATE_REQUESTED case.

	* src/manage.h: Update headers accordingly.
	(task_status_t): Add TASK_STATUS_DELETE_ULTIMATE_REQUESTED.

	* src/omp.c (help_text): Add trashcan commands.
	(delete_agent_data_t, delete_escalator_data_t)
	(delete_lsc_credential_data_t, delete_report_format_data_t)
	(delete_schedule_data_t, delete_slave_data_t, delete_target_data_t)
	(delete_task_data_t): Add ultimate flag.
	(get_agents_data_t, get_configs_data_t, get_escalators_data_t)
	(get_lsc_credentials_data_t, get_report_formats_data_t)
	(get_schedules_data_t, get_targets_data_t, get_tasks_data_t): Add trash
	flag.
	(restore_data_t): New types.
	(restore_data_reset, restore_data_reset): New functions.
	(command_data_t): Add restore field.
	(restore_data): New variable.
	(client_state_t): Add states for trashcan commands.
	(omp_xml_handle_start_element, omp_xml_handle_end_element): Add trashcan
	command handling.  Add ultimate handling to DELETE commands.  Add trash
	handling to GET commands.

	* src/otp.c (process_otp_scanner_input): In SCANNER_TIME_SCAN_END add
	STATUS_DELETE_ULTIMATE_REQUESTED case and call delete_task_lock instead
	of delete_task.

Modified: trunk/openvas-manager/CMakeLists.txt
===================================================================
--- trunk/openvas-manager/CMakeLists.txt	2011-03-11 17:52:55 UTC (rev 10531)
+++ trunk/openvas-manager/CMakeLists.txt	2011-03-14 05:49:56 UTC (rev 10532)
@@ -104,7 +104,7 @@
 
 ## Variables
 
-set (OPENVASMD_DATABASE_VERSION 41)
+set (OPENVASMD_DATABASE_VERSION 42)
 
 if (SYSCONF_INSTALL_DIR)
   set (SYSCONFDIR "${SYSCONF_INSTALL_DIR}")

Modified: trunk/openvas-manager/ChangeLog
===================================================================
--- trunk/openvas-manager/ChangeLog	2011-03-11 17:52:55 UTC (rev 10531)
+++ trunk/openvas-manager/ChangeLog	2011-03-14 05:49:56 UTC (rev 10532)
@@ -1,3 +1,76 @@
+2011-03-09  Matthew Mundell <matthew.mundell at greenbone.net>
+
+	Add a trashcan, including new OMP commands EMPTY_TRASHCAN and RESTORE.
+	Hence increase database version to 42.  Add database migration from
+	version 41 to 42.
+
+	* CMakeLists.txt (OPENVASMD_DATABASE_VERSION): Increase to 42.
+
+	* src/manage_sql.c (resource_t): New type.
+	(LOCATION_TABLE, LOCATION_TRASH): New defines.
+	(user_owns_trash_uuid, find_trash): New functions.
+	(create_tables): Add trash tables.  Add trash location columns to tables
+	task_escalators and tasks.
+	(migrate_41_to_42): New function.
+	(database_migrators): Add migrate_41_to_42.
+	(delete_escalator, delete_task, delete_target, delete_config)
+	(delete_lsc_credential, delete_agent, delete_schedule)
+	(delete_report_format, delete_slave): Add ultimate flag param.  Update callers.
+	(init_escalator_iterator, init_escalator_data_iterator)
+	(init_task_iterator, init_target_iterator, init_config_iterator)
+	(init_lsc_credential_iterator, init_agent_iterator, init_schedule_iterator)
+	(init_report_format_iterator, init_report_format_param_iterator)
+	(init_slave_iterator): Add trash flag param.  Update callers.
+	(init_manage, set_task_requested, delete_report): Add
+	TASK_STATUS_DELETE_ULTIMATE_REQUESTED cases.
+	(task_config_uuid, task_config_name): Add trash check.
+	(task_config_in_trash, task_target_in_trash, task_slave_in_trash)
+	(task_escalator_in_trash, task_schedule_in_trash): New functions.
+	(add_task_escalator, set_task_escalator): Set location field.
+	(request_delete_task): Call delete_task_lock instead of delete_task.
+	(request_delete_task_uuid): New function.  Allows OMP DELETE_TASK to pass
+	UUID.
+	(delete_task_lock): New function.  Calls delete_task inside transaction.
+	(delete_trash_tasks, find_trash_task): New functions.
+	(find_task): Exclude trash tasks.
+	(target_iterator_ssh_trash, target_iterator_smb_trash, trash_target_hosts)
+	(trash_target_in_use, trash_config_in_use, trash_lsc_credential_uuid)
+	(trash_lsc_credential_name, report_format_trash_global)
+	(trash_slave_in_use): New functions.
+	(target_in_use, init_schedule_task_iterator, slave_in_use): Take trash
+	resources into account.
+	(manage_restore, manage_empty_trashcan): New functions.  Trashcan
+	specific.
+
+	* src/manage.c: Update iterator calls.
+	(run_status_name, run_slave_task, manage_check_current_task): Add
+	TASK_STATUS_DELETE_ULTIMATE_REQUESTED case.
+
+	* src/manage.h: Update headers accordingly.
+	(task_status_t): Add TASK_STATUS_DELETE_ULTIMATE_REQUESTED.
+
+	* src/omp.c (help_text): Add trashcan commands.
+	(delete_agent_data_t, delete_escalator_data_t)
+	(delete_lsc_credential_data_t, delete_report_format_data_t)
+	(delete_schedule_data_t, delete_slave_data_t, delete_target_data_t)
+	(delete_task_data_t): Add ultimate flag.
+	(get_agents_data_t, get_configs_data_t, get_escalators_data_t)
+	(get_lsc_credentials_data_t, get_report_formats_data_t)
+	(get_schedules_data_t, get_targets_data_t, get_tasks_data_t): Add trash
+	flag.
+	(restore_data_t): New types.
+	(restore_data_reset, restore_data_reset): New functions.
+	(command_data_t): Add restore field.
+	(restore_data): New variable.
+	(client_state_t): Add states for trashcan commands.
+	(omp_xml_handle_start_element, omp_xml_handle_end_element): Add trashcan
+	command handling.  Add ultimate handling to DELETE commands.  Add trash
+	handling to GET commands.
+
+	* src/otp.c (process_otp_scanner_input): In SCANNER_TIME_SCAN_END add
+	STATUS_DELETE_ULTIMATE_REQUESTED case and call delete_task_lock instead
+	of delete_task.
+
 2011-03-09  Michael Wiegand <michael.wiegand at greenbone.net>
 
 	Post branch version bump.

Modified: trunk/openvas-manager/src/manage.c
===================================================================
--- trunk/openvas-manager/src/manage.c	2011-03-11 17:52:55 UTC (rev 10531)
+++ trunk/openvas-manager/src/manage.c	2011-03-14 05:49:56 UTC (rev 10532)
@@ -406,6 +406,8 @@
   switch (status)
     {
       case TASK_STATUS_DELETE_REQUESTED: return "Delete Requested";
+      case TASK_STATUS_DELETE_ULTIMATE_REQUESTED:
+        return "Ultimate Delete Requested";
       case TASK_STATUS_DONE:             return "Done";
       case TASK_STATUS_NEW:              return "New";
 
@@ -1306,8 +1308,8 @@
 
       if (target_ssh_credential)
         {
-          init_lsc_credential_iterator (&credentials, target_ssh_credential, 1,
-                                        NULL);
+          init_lsc_credential_iterator (&credentials, target_ssh_credential, 0,
+                                        1, NULL);
           if (next (&credentials))
             {
               const char *user, *password;
@@ -1344,8 +1346,8 @@
 
       if (target_smb_credential)
         {
-          init_lsc_credential_iterator (&credentials, target_smb_credential, 1,
-                                        NULL);
+          init_lsc_credential_iterator (&credentials, target_smb_credential, 0,
+                                        1, NULL);
           if (next (&credentials))
             {
               const char *user, *password;
@@ -1385,7 +1387,7 @@
 
       /* Create the target on the slave. */
 
-      init_target_iterator (&targets, target, 1, NULL);
+      init_target_iterator (&targets, target, 0, 1, NULL);
       if (next (&targets))
         {
           const char *hosts;
@@ -1565,6 +1567,7 @@
           case TASK_STATUS_PAUSE_WAITING:
           case TASK_STATUS_RESUME_WAITING:
           case TASK_STATUS_DELETE_REQUESTED:
+          case TASK_STATUS_DELETE_ULTIMATE_REQUESTED:
           case TASK_STATUS_DONE:
           case TASK_STATUS_NEW:
           case TASK_STATUS_REQUESTED:
@@ -2020,7 +2023,7 @@
     {
       iterator_t credentials;
 
-      init_lsc_credential_iterator (&credentials, ssh_credential, 1, NULL);
+      init_lsc_credential_iterator (&credentials, ssh_credential, 0, 1, NULL);
       if (next (&credentials))
         {
           const char *user = lsc_credential_iterator_login (&credentials);
@@ -2103,7 +2106,7 @@
     {
       iterator_t credentials;
 
-      init_lsc_credential_iterator (&credentials, smb_credential, 1, NULL);
+      init_lsc_credential_iterator (&credentials, smb_credential, 0, 1, NULL);
       if (next (&credentials))
         {
           const char *user = lsc_credential_iterator_login (&credentials);
@@ -2511,6 +2514,7 @@
             return 1;
             break;
           case TASK_STATUS_DELETE_REQUESTED:
+          case TASK_STATUS_DELETE_ULTIMATE_REQUESTED:
           case TASK_STATUS_DONE:
           case TASK_STATUS_NEW:
           case TASK_STATUS_REQUESTED:

Modified: trunk/openvas-manager/src/manage.h
===================================================================
--- trunk/openvas-manager/src/manage.h	2011-03-11 17:52:55 UTC (rev 10531)
+++ trunk/openvas-manager/src/manage.h	2011-03-14 05:49:56 UTC (rev 10532)
@@ -152,7 +152,8 @@
   TASK_STATUS_STOP_REQUESTED   = 10,
   TASK_STATUS_STOP_WAITING     = 11,
   TASK_STATUS_STOPPED = 12,
-  TASK_STATUS_INTERNAL_ERROR = 13
+  TASK_STATUS_INTERNAL_ERROR = 13,
+  TASK_STATUS_DELETE_ULTIMATE_REQUESTED = 14
 } task_status_t;
 
 typedef long long int agent_t;
@@ -222,7 +223,7 @@
                   GPtrArray*, escalator_t*);
 
 int
-delete_escalator (escalator_t);
+delete_escalator (const char *, int);
 
 int
 escalator_uuid (escalator_t, char **);
@@ -234,7 +235,7 @@
 escalate (escalator_t, task_t, event_t, const void*);
 
 void
-init_escalator_iterator (iterator_t*, escalator_t, task_t, event_t, int,
+init_escalator_iterator (iterator_t*, escalator_t, task_t, event_t, int, int,
                          const char*);
 
 escalator_t
@@ -286,7 +287,7 @@
 escalator_method_from_name (const char*);
 
 void
-init_escalator_data_iterator (iterator_t *, escalator_t, const char *);
+init_escalator_data_iterator (iterator_t *, escalator_t, int, const char *);
 
 const char*
 escalator_data_iterator_name (iterator_t*);
@@ -322,7 +323,7 @@
 task_count ();
 
 void
-init_task_iterator (iterator_t*, task_t, int, const char*);
+init_task_iterator (iterator_t*, task_t, int, int, const char*);
 
 task_t
 task_iterator_task (iterator_t*);
@@ -357,12 +358,18 @@
 char*
 task_config_name (task_t);
 
+int
+task_config_in_trash (task_t);
+
 void
 set_task_config (task_t, config_t);
 
 target_t
 task_target (task_t);
 
+int
+task_target_in_trash (task_t);
+
 void
 set_task_target (task_t, target_t);
 
@@ -372,6 +379,9 @@
 void
 set_task_slave (task_t, target_t);
 
+int
+task_slave_in_trash (task_t);
+
 char*
 task_description (task_t);
 
@@ -411,6 +421,9 @@
 escalator_t
 task_escalator (task_t);
 
+int
+task_escalator_in_trash (task_t);
+
 void
 add_task_escalator (task_t, escalator_t);
 
@@ -433,6 +446,9 @@
 task_schedule (task_t);
 
 int
+task_schedule_in_trash (task_t);
+
+int
 task_schedule_next_time (task_t);
 
 void
@@ -484,11 +500,18 @@
                     /*@null@*/ /*@only@*/ char*);
 
 int
+request_delete_task_uuid (const char *, int);
+
+int
 request_delete_task (task_t*);
 
 int
-delete_task (task_t);
+delete_task (task_t, int);
 
+/* For otp.c. */
+int
+delete_task_lock (task_t, int);
+
 void
 append_to_task_comment (task_t, const char*, int);
 
@@ -787,10 +810,10 @@
                const char*, target_t*);
 
 int
-delete_target (target_t);
+delete_target (const char*, int);
 
 void
-init_target_iterator (iterator_t*, target_t, int, const char*);
+init_target_iterator (iterator_t*, target_t, int, int, const char*);
 
 target_t
 target_iterator_target (iterator_t*);
@@ -816,6 +839,12 @@
 const char*
 target_iterator_port_range (iterator_t*);
 
+int
+target_iterator_ssh_trash (iterator_t*);
+
+int
+target_iterator_smb_trash (iterator_t*);
+
 char*
 target_uuid (target_t);
 
@@ -825,9 +854,15 @@
 char*
 target_hosts (target_t);
 
+char*
+trash_target_hosts (target_t);
+
 int
 target_in_use (target_t);
 
+int
+trash_target_in_use (target_t);
+
 char*
 target_lsc_credential_name (const char *);
 
@@ -878,7 +913,7 @@
 copy_config (const char*, const char*, config_t, config_t*);
 
 int
-delete_config (config_t);
+delete_config (const char*, int);
 
 gboolean
 find_config (const char*, config_t*);
@@ -890,7 +925,7 @@
 config_nvt_timeout (config_t, const char *);
 
 void
-init_config_iterator (iterator_t*, config_t, int, const char*);
+init_config_iterator (iterator_t*, config_t, int, int, const char*);
 
 config_t
 config_iterator_config (iterator_t*);
@@ -920,6 +955,9 @@
 config_in_use (config_t);
 
 int
+trash_config_in_use (config_t);
+
+int
 config_families_growing (config_t);
 
 int
@@ -1115,7 +1153,7 @@
                        lsc_credential_t*);
 
 int
-delete_lsc_credential (lsc_credential_t);
+delete_lsc_credential (const char *, int);
 
 int
 lsc_credential_packaged (lsc_credential_t);
@@ -1133,7 +1171,8 @@
 set_lsc_credential_password (lsc_credential_t, const char *);
 
 void
-init_lsc_credential_iterator (iterator_t*, lsc_credential_t, int, const char*);
+init_lsc_credential_iterator (iterator_t*, lsc_credential_t, int, int,
+                              const char*);
 
 lsc_credential_t
 lsc_credential_iterator_lsc_credential (iterator_t*);
@@ -1172,8 +1211,14 @@
 lsc_credential_uuid (lsc_credential_t);
 
 char*
+trash_lsc_credential_uuid (lsc_credential_t);
+
+char*
 lsc_credential_name (lsc_credential_t);
 
+char*
+trash_lsc_credential_name (lsc_credential_t);
+
 void
 init_lsc_credential_target_iterator (iterator_t*, lsc_credential_t, int);
 
@@ -1194,7 +1239,7 @@
               const char*, const char*, agent_t*);
 
 int
-delete_agent (agent_t);
+delete_agent (const char *, int);
 
 int
 verify_agent (agent_t);
@@ -1203,7 +1248,7 @@
 agent_uuid (agent_t, char **);
 
 void
-init_agent_iterator (iterator_t*, agent_t, int, const char*);
+init_agent_iterator (iterator_t*, agent_t, int, int, const char*);
 
 const char*
 agent_iterator_uuid (iterator_t*);
@@ -1424,7 +1469,7 @@
                  time_t, schedule_t *);
 
 int
-delete_schedule (schedule_t);
+delete_schedule (const char*, int);
 
 void
 manage_auth_allow_all ();
@@ -1448,7 +1493,7 @@
 schedule_name (schedule_t);
 
 void
-init_schedule_iterator (iterator_t*, schedule_t, int, const char*);
+init_schedule_iterator (iterator_t*, schedule_t, int, int, const char*);
 
 schedule_t
 schedule_iterator_schedule (iterator_t*);
@@ -1520,7 +1565,7 @@
                       array_t *, const char *, report_format_t *);
 
 int
-delete_report_format (report_format_t);
+delete_report_format (const char *, int);
 
 int
 verify_report_format (report_format_t);
@@ -1556,7 +1601,8 @@
 report_format_active (report_format_t);
 
 void
-init_report_format_iterator (iterator_t*, report_format_t, int, const char*);
+init_report_format_iterator (iterator_t*, report_format_t, int, int,
+                             const char*);
 
 report_format_t
 report_format_iterator_report_format (iterator_t*);
@@ -1643,7 +1689,7 @@
 
 void
 init_report_format_param_iterator (iterator_t*, report_format_t, int,
-                                   const char*);
+                                   int, const char*);
 
 report_format_param_t
 report_format_param_iterator_param (iterator_t*);
@@ -1690,10 +1736,10 @@
               const char*, const char*, slave_t*);
 
 int
-delete_slave (slave_t);
+delete_slave (const char*, int);
 
 void
-init_slave_iterator (iterator_t*, slave_t, int, const char*);
+init_slave_iterator (iterator_t*, slave_t, int, int, const char*);
 
 slave_t
 slave_iterator_slave (iterator_t*);
@@ -1740,6 +1786,9 @@
 int
 slave_in_use (slave_t);
 
+int
+trash_slave_in_use (slave_t);
+
 void
 init_slave_task_iterator (iterator_t*, slave_t, int);
 
@@ -1756,6 +1805,15 @@
 manage_schema (gchar *, gchar **, gsize *, gchar **, gchar **);
 
 
+/* Schema. */
+
+int
+manage_restore (const char *);
+
+int
+manage_empty_trashcan ();
+
+
 /* Tags. */
 
 void

Modified: trunk/openvas-manager/src/manage_sql.c
===================================================================
--- trunk/openvas-manager/src/manage_sql.c	2011-03-11 17:52:55 UTC (rev 10531)
+++ trunk/openvas-manager/src/manage_sql.c	2011-03-14 05:49:56 UTC (rev 10532)
@@ -61,6 +61,8 @@
 
 /* Internal types and preprocessor definitions. */
 
+typedef long long int resource_t;
+
 /**
  * @brief Database ROWID of 'Full and fast' config.
  */
@@ -138,6 +140,16 @@
  */
 #define TRUST_UNKNOWN 3
 
+/**
+ * @brief Location of a constituent of a trashcan resource.
+ */
+#define LOCATION_TABLE 0
+
+/**
+ * @brief Location of a constituent of a trashcan resource.
+ */
+#define LOCATION_TRASH 1
+
 
 /* Headers for symbols defined in manage.c which are private to libmanage. */
 
@@ -221,6 +233,9 @@
 static target_t
 duplicate_target (target_t, const char *);
 
+int
+delete_task_lock (task_t, int);
+
 
 /* Variables. */
 
@@ -829,6 +844,33 @@
 /**
  * @brief Test whether a user owns a resource.
  *
+ * @param[in]  resource  Type of resource, for example "task".
+ * @param[in]  uuid      UUID of resource.
+ *
+ * @return 1 if user owns resource, else 0.
+ */
+static int
+user_owns_trash_uuid (const char *resource, const char *uuid)
+{
+  int ret;
+
+  assert (current_credentials.uuid);
+
+  ret = sql_int (0, 0,
+                 "SELECT count(*) FROM %ss_trash"
+                 " WHERE uuid = '%s'"
+                 " AND ((owner IS NULL) OR (owner ="
+                 " (SELECT users.ROWID FROM users WHERE users.uuid = '%s')));",
+                 resource,
+                 uuid,
+                 current_credentials.uuid);
+
+  return ret;
+}
+
+/**
+ * @brief Test whether a user owns a resource.
+ *
  * @param[in]  resource  Type of resource, for example "report_format".
  * @param[in]  field     Field to compare with value.
  * @param[in]  value     Identifier value of resource.
@@ -898,6 +940,47 @@
   array_add (array, g_strdup (string));
 }
 
+/**
+ * @brief Find a resource in the trashcan given a UUID.
+ *
+ * @param[in]   type      Type of resource.
+ * @param[in]   uuid      UUID of resource.
+ * @param[out]  resource  Resource return, 0 if succesfully failed to find resource.
+ *
+ * @return FALSE on success (including if failed to find resource), TRUE on error.
+ */
+static gboolean
+find_trash (const char *type, const char *uuid, resource_t *resource)
+{
+  gchar *quoted_uuid = sql_quote (uuid);
+  if (user_owns_trash_uuid (type, quoted_uuid) == 0)
+    {
+      g_free (quoted_uuid);
+      *resource = 0;
+      return FALSE;
+    }
+  switch (sql_int64 (resource, 0, 0,
+                     "SELECT ROWID FROM %ss_trash WHERE uuid = '%s';",
+                     type,
+                     quoted_uuid))
+    {
+      case 0:
+        break;
+      case 1:        /* Too few rows in result of query. */
+        *resource = 0;
+        break;
+      default:       /* Programming error. */
+        assert (0);
+      case -1:
+        g_free (quoted_uuid);
+        return TRUE;
+        break;
+    }
+
+  g_free (quoted_uuid);
+  return FALSE;
+}
+
 
 /* Creation. */
 
@@ -908,13 +991,21 @@
 create_tables ()
 {
   sql ("CREATE TABLE IF NOT EXISTS agents (id INTEGER PRIMARY KEY, uuid UNIQUE, owner INTEGER, name, comment, installer TEXT, installer_64 TEXT, installer_filename, installer_signature_64 TEXT, installer_trust INTEGER, installer_trust_time, howto_install TEXT, howto_use TEXT);");
+  sql ("CREATE TABLE IF NOT EXISTS agents_trash (id INTEGER PRIMARY KEY, uuid UNIQUE, owner INTEGER, name, comment, installer TEXT, installer_64 TEXT, installer_filename, installer_signature_64 TEXT, installer_trust INTEGER, installer_trust_time, howto_install TEXT, howto_use TEXT);");
   sql ("CREATE TABLE IF NOT EXISTS config_preferences (id INTEGER PRIMARY KEY, config INTEGER, type, name, value);");
+  sql ("CREATE TABLE IF NOT EXISTS config_preferences_trash (id INTEGER PRIMARY KEY, config INTEGER, type, name, value);");
   sql ("CREATE TABLE IF NOT EXISTS configs (id INTEGER PRIMARY KEY, uuid UNIQUE, owner INTEGER, name, nvt_selector, comment, family_count INTEGER, nvt_count INTEGER, families_growing INTEGER, nvts_growing INTEGER);");
+  sql ("CREATE TABLE IF NOT EXISTS configs_trash (id INTEGER PRIMARY KEY, uuid UNIQUE, owner INTEGER, name, nvt_selector, comment, family_count INTEGER, nvt_count INTEGER, families_growing INTEGER, nvts_growing INTEGER);");
   sql ("CREATE TABLE IF NOT EXISTS escalator_condition_data (id INTEGER PRIMARY KEY, escalator INTEGER, name, data);");
+  sql ("CREATE TABLE IF NOT EXISTS escalator_condition_data_trash (id INTEGER PRIMARY KEY, escalator INTEGER, name, data);");
   sql ("CREATE TABLE IF NOT EXISTS escalator_event_data (id INTEGER PRIMARY KEY, escalator INTEGER, name, data);");
+  sql ("CREATE TABLE IF NOT EXISTS escalator_event_data_trash (id INTEGER PRIMARY KEY, escalator INTEGER, name, data);");
   sql ("CREATE TABLE IF NOT EXISTS escalator_method_data (id INTEGER PRIMARY KEY, escalator INTEGER, name, data);");
+  sql ("CREATE TABLE IF NOT EXISTS escalator_method_data_trash (id INTEGER PRIMARY KEY, escalator INTEGER, name, data);");
   sql ("CREATE TABLE IF NOT EXISTS escalators (id INTEGER PRIMARY KEY, uuid UNIQUE, owner INTEGER, name, comment, event INTEGER, condition INTEGER, method INTEGER);");
+  sql ("CREATE TABLE IF NOT EXISTS escalators_trash (id INTEGER PRIMARY KEY, uuid UNIQUE, owner INTEGER, name, comment, event INTEGER, condition INTEGER, method INTEGER);");
   sql ("CREATE TABLE IF NOT EXISTS lsc_credentials (id INTEGER PRIMARY KEY, uuid UNIQUE, owner INTEGER, name, login, password, comment, public_key TEXT, private_key TEXT, rpm TEXT, deb TEXT, exe TEXT);");
+  sql ("CREATE TABLE IF NOT EXISTS lsc_credentials_trash (id INTEGER PRIMARY KEY, uuid UNIQUE, owner INTEGER, name, login, password, comment, public_key TEXT, private_key TEXT, rpm TEXT, deb TEXT, exe TEXT);");
   sql ("CREATE TABLE IF NOT EXISTS meta (id INTEGER PRIMARY KEY, name UNIQUE, value);");
   sql ("CREATE TABLE IF NOT EXISTS notes (id INTEGER PRIMARY KEY, uuid UNIQUE, owner INTEGER, nvt, creation_time, modification_time, text, hosts, port, threat, task INTEGER, result INTEGER);");
   sql ("CREATE TABLE IF NOT EXISTS nvt_preferences (id INTEGER PRIMARY KEY, name, value);");
@@ -930,8 +1021,11 @@
   sql ("CREATE TABLE IF NOT EXISTS report_hosts (id INTEGER PRIMARY KEY, report INTEGER, host, start_time, end_time, attack_state, current_port, max_port);");
   sql ("CREATE INDEX IF NOT EXISTS report_hosts_by_report ON report_hosts (report);");
   sql ("CREATE TABLE IF NOT EXISTS report_format_param_options (id INTEGER PRIMARY KEY, report_format_param, value);");
+  sql ("CREATE TABLE IF NOT EXISTS report_format_param_options_trash (id INTEGER PRIMARY KEY, report_format_param, value);");
   sql ("CREATE TABLE IF NOT EXISTS report_format_params (id INTEGER PRIMARY KEY, report_format, name, type INTEGER, value, type_min, type_max, type_regex, fallback);");
+  sql ("CREATE TABLE IF NOT EXISTS report_format_params_trash (id INTEGER PRIMARY KEY, report_format, name, type INTEGER, value, type_min, type_max, type_regex, fallback);");
   sql ("CREATE TABLE IF NOT EXISTS report_formats (id INTEGER PRIMARY KEY, uuid, owner INTEGER, name, extension, content_type, summary, description, signature, trust INTEGER, trust_time, flags INTEGER);");
+  sql ("CREATE TABLE IF NOT EXISTS report_formats_trash (id INTEGER PRIMARY KEY, uuid, owner INTEGER, name, extension, content_type, summary, description, signature, trust INTEGER, trust_time, flags INTEGER, original_uuid);");
   sql ("CREATE TABLE IF NOT EXISTS report_results (id INTEGER PRIMARY KEY, report INTEGER, result INTEGER);");
   sql ("CREATE INDEX IF NOT EXISTS report_results_by_report ON report_results (report);");
   sql ("CREATE INDEX IF NOT EXISTS report_results_by_result ON report_results (result);");
@@ -940,11 +1034,14 @@
   sql ("CREATE INDEX IF NOT EXISTS results_by_task ON results (task);");
   sql ("CREATE INDEX IF NOT EXISTS results_by_type ON results (type);");
   sql ("CREATE TABLE IF NOT EXISTS schedules (id INTEGER PRIMARY KEY, uuid, owner INTEGER, name, comment, first_time, period, period_months, duration);");
+  sql ("CREATE TABLE IF NOT EXISTS schedules_trash (id INTEGER PRIMARY KEY, uuid, owner INTEGER, name, comment, first_time, period, period_months, duration);");
   sql ("CREATE TABLE IF NOT EXISTS slaves (id INTEGER PRIMARY KEY, uuid, owner INTEGER, name, comment, host, port, login, password);");
+  sql ("CREATE TABLE IF NOT EXISTS slaves_trash (id INTEGER PRIMARY KEY, uuid, owner INTEGER, name, comment, host, port, login, password);");
   sql ("CREATE TABLE IF NOT EXISTS targets (id INTEGER PRIMARY KEY, uuid UNIQUE, owner INTEGER, name, hosts, comment, lsc_credential INTEGER, smb_lsc_credential INTEGER, port_range);");
+  sql ("CREATE TABLE IF NOT EXISTS targets_trash (id INTEGER PRIMARY KEY, uuid UNIQUE, owner INTEGER, name, hosts, comment, lsc_credential INTEGER, smb_lsc_credential INTEGER, port_range, ssh_location INTEGER, smb_location INTEGER);");
   sql ("CREATE TABLE IF NOT EXISTS task_files (id INTEGER PRIMARY KEY, task INTEGER, name, content);");
-  sql ("CREATE TABLE IF NOT EXISTS task_escalators (id INTEGER PRIMARY KEY, task INTEGER, escalator INTEGER);");
-  sql ("CREATE TABLE IF NOT EXISTS tasks   (id INTEGER PRIMARY KEY, uuid, owner INTEGER, name, hidden INTEGER, time, comment, description, run_status INTEGER, start_time, end_time, config INTEGER, target INTEGER, schedule INTEGER, schedule_next_time, slave INTEGER);");
+  sql ("CREATE TABLE IF NOT EXISTS task_escalators (id INTEGER PRIMARY KEY, task INTEGER, escalator INTEGER, escalator_location INTEGER);");
+  sql ("CREATE TABLE IF NOT EXISTS tasks   (id INTEGER PRIMARY KEY, uuid, owner INTEGER, name, hidden INTEGER, time, comment, description, run_status INTEGER, start_time, end_time, config INTEGER, target INTEGER, schedule INTEGER, schedule_next_time, slave INTEGER, config_location INTEGER, target_location INTEGER, schedule_location INTEGER, slave_location INTEGER);");
   sql ("CREATE TABLE IF NOT EXISTS users   (id INTEGER PRIMARY KEY, uuid UNIQUE, name, password);");
 
   sql ("ANALYZE;");
@@ -4396,6 +4493,59 @@
 }
 
 /**
+ * @brief Migrate the database from version 41 to version 42.
+ *
+ * @return 0 success, -1 error.
+ */
+static int
+migrate_41_to_42 ()
+{
+  sql ("BEGIN EXCLUSIVE;");
+
+  /* Require that the database is currently version 41. */
+
+  if (manage_db_version () != 41)
+    {
+      sql ("ROLLBACK;");
+      return -1;
+    }
+
+  /* Update the database. */
+
+  /* Two task tables got trashcan location fields. */
+
+  /** @todo ROLLBACK on failure. */
+
+  sql ("ALTER TABLE tasks ADD column config_location INTEGER;");
+  sql ("ALTER TABLE tasks ADD column target_location INTEGER;");
+  sql ("ALTER TABLE tasks ADD column schedule_location INTEGER;");
+  sql ("ALTER TABLE tasks ADD column slave_location INTEGER;");
+
+  sql ("UPDATE tasks SET"
+       " config_location = " G_STRINGIFY (LOCATION_TABLE) ","
+       " target_location = " G_STRINGIFY (LOCATION_TABLE) ","
+       " schedule_location = " G_STRINGIFY (LOCATION_TABLE) ","
+       " slave_location = " G_STRINGIFY (LOCATION_TABLE) ";");
+
+  /* Ensure that the task_escalators table exists. */
+  sql ("CREATE TABLE IF NOT EXISTS task_escalators"
+       " (id INTEGER PRIMARY KEY, task INTEGER, escalator INTEGER);");
+
+  sql ("ALTER TABLE task_escalators ADD column escalator_location INTEGER;");
+
+  sql ("UPDATE task_escalators"
+       " SET escalator_location = " G_STRINGIFY (LOCATION_TABLE) ";");
+
+  /* Set the database version to 42. */
+
+  set_db_version (42);
+
+  sql ("COMMIT;");
+
+  return 0;
+}
+
+/**
  * @brief Array of database version migrators.
  */
 static migrator_t database_migrators[]
@@ -4441,6 +4591,7 @@
     {39, migrate_38_to_39},
     {40, migrate_39_to_40},
     {41, migrate_40_to_41},
+    {42, migrate_41_to_42},
     /* End marker. */
     {-1, NULL}};
 
@@ -4983,21 +5134,118 @@
 /**
  * @brief Delete an escalator.
  *
- * @param[in]  escalator  Escalator.
+ * @param[in]  escalator_id  UUID of escalator.
+ * @param[in]  ultimate      Whether to remove entirely, or to trashcan.
  *
- * @return 0 success, 1 fail because a task refers to the escalator, -1 error.
+ * @return 0 success, 1 fail because a task refers to the escalator, 2 failed
+ *         to find target, -1 error.
  */
 int
-delete_escalator (escalator_t escalator)
+delete_escalator (const char *escalator_id, int ultimate)
 {
+  escalator_t escalator = 0;
+
   sql ("BEGIN IMMEDIATE;");
+
+  if (find_escalator (escalator_id, &escalator))
+    {
+      sql ("ROLLBACK;");
+      return -1;
+    }
+
+  if (escalator == 0)
+    {
+      if (find_trash ("escalator", escalator_id, &escalator))
+        {
+          sql ("ROLLBACK;");
+          return -1;
+        }
+      if (escalator == 0)
+        {
+          sql ("ROLLBACK;");
+          return 2;
+        }
+      if (ultimate == 0)
+        {
+          /* It's already in the trashcan. */
+          sql ("COMMIT;");
+          return 0;
+        }
+
+      /* Check if it's in use by a task in the trashcan. */
+      if (sql_int (0, 0,
+                   "SELECT count(*) FROM task_escalators"
+                   " WHERE escalator = %llu"
+                   " AND escalator_location = " G_STRINGIFY (LOCATION_TRASH) ";",
+                   escalator))
+        {
+          sql ("ROLLBACK;");
+          return 1;
+        }
+
+      sql ("DELETE FROM escalator_condition_data_trash WHERE escalator = %llu;",
+           escalator);
+      sql ("DELETE FROM escalator_event_data_trash WHERE escalator = %llu;",
+           escalator);
+      sql ("DELETE FROM escalator_method_data_trash WHERE escalator = %llu;",
+           escalator);
+      sql ("DELETE FROM escalators_trash WHERE ROWID = %llu;", escalator);
+      sql ("COMMIT;");
+      return 0;
+    }
+
   if (sql_int (0, 0,
-               "SELECT count(*) FROM task_escalators WHERE escalator = %llu;",
+               "SELECT count(*) FROM task_escalators"
+               " WHERE escalator = %llu"
+               " AND escalator_location = " G_STRINGIFY (LOCATION_TABLE) ";",
+               " AND (SELECT hidden < 2 FROM tasks"
+               "      WHERE ROWID = task_escalators.task);",
                escalator))
     {
       sql ("ROLLBACK;");
       return 1;
     }
+
+  if (ultimate == 0)
+    {
+      escalator_t trash_escalator;
+
+      sql ("INSERT INTO escalators_trash"
+           " (uuid, owner, name, comment, event, condition, method)"
+           " SELECT uuid, owner, name, comment, event, condition, method"
+           " FROM escalators WHERE ROWID = %llu;",
+           escalator);
+
+      trash_escalator = sqlite3_last_insert_rowid (task_db);
+
+      sql ("INSERT INTO escalator_condition_data_trash"
+           " (escalator, name, data)"
+           " SELECT %llu, name, data"
+           " FROM escalator_condition_data WHERE ROWID = %llu;",
+           trash_escalator);
+
+      sql ("INSERT INTO escalator_event_data_trash"
+           " (escalator, name, data)"
+           " SELECT %llu, name, data"
+           " FROM escalator_event_data WHERE ROWID = %llu;",
+           trash_escalator);
+
+      sql ("INSERT INTO escalator_method_data_trash"
+           " (escalator, name, data)"
+           " SELECT %llu, name, data"
+           " FROM escalator_method_data WHERE ROWID = %llu;",
+           trash_escalator);
+
+      /* Update the location of the escalator in any trashcan tasks. */
+      sql ("UPDATE task_escalators"
+           " SET escalator = %llu,"
+           "     escalator_location = " G_STRINGIFY (LOCATION_TRASH)
+           " WHERE escalator = %llu"
+           " AND escalator_location = " G_STRINGIFY (LOCATION_TABLE) ";",
+           trash_escalator,
+           escalator);
+   }
+
   sql ("DELETE FROM escalator_condition_data WHERE escalator = %llu;",
        escalator);
   sql ("DELETE FROM escalator_event_data WHERE escalator = %llu;", escalator);
@@ -5062,30 +5310,44 @@
  * @param[in]  task        Iterate over escalators for this task.  0 for all.
  * @param[in]  event       Iterate over escalators handling this event.  0 for
  *                         all.
+ * @param[in]  trash       Whether to iterate over trashcan escalators.
  * @param[in]  ascending   Whether to sort ascending or descending.
  * @param[in]  sort_field  Field to sort on, or NULL for "ROWID".
  */
 void
 init_escalator_iterator (iterator_t *iterator, escalator_t escalator,
-                         task_t task, event_t event, int ascending,
+                         task_t task, event_t event, int trash, int ascending,
                          const char *sort_field)
 {
   assert (escalator ? task == 0 : (task ? escalator == 0 : 1));
   assert (escalator ? event == 0 : (event ? escalator == 0 : 1));
   assert (event ? task : 1);
   assert (current_credentials.uuid);
+  assert (task ? trash == 0 : 1);
 
   if (escalator)
     init_iterator (iterator,
-                   "SELECT escalators.ROWID, uuid, name, comment,"
+                   "SELECT escalators%s.ROWID, uuid, name, comment,"
                    " 0, event, condition, method,"
                    " (SELECT count(*) > 0 FROM task_escalators"
-                   "  WHERE task_escalators.escalator = escalators.ROWID)"
-                   " FROM escalators"
+                   "  WHERE task_escalators.escalator = escalators%s.ROWID"
+                   "  %s)"
+                   " FROM escalators%s"
                    " WHERE ROWID = %llu"
                    " AND ((owner IS NULL) OR (owner ="
                    " (SELECT ROWID FROM users WHERE users.uuid = '%s')))"
                    " ORDER BY %s %s;",
+                   trash ? "_trash" : "",
+                   trash ? "_trash" : "",
+                   (trash
+                     ? "  AND escalator_location"
+                       "      = " G_STRINGIFY (LOCATION_TRASH)
+                     : "  AND escalator_location"
+                       "      = " G_STRINGIFY (LOCATION_TABLE)
+                       "  AND (SELECT hidden FROM tasks"
+                       "       WHERE ROWID = task_escalators.task)"
+                       "      < 2"), /* Task in table. */
+                   trash ? "_trash" : "",
                    escalator,
                    current_credentials.uuid,
                    sort_field ? sort_field : "escalators.ROWID",
@@ -5107,14 +5369,26 @@
                    ascending ? "ASC" : "DESC");
   else
     init_iterator (iterator,
-                   "SELECT escalators.ROWID, uuid, name, comment,"
+                   "SELECT escalators%s.ROWID, uuid, name, comment,"
                    " 0, event, condition, method,"
                    " (SELECT count(*) > 0 FROM task_escalators"
-                   "  WHERE task_escalators.escalator = escalators.ROWID)"
-                   " FROM escalators"
+                   "  WHERE task_escalators.escalator = escalators%s.ROWID"
+                   "  %s)"
+                   " FROM escalators%s"
                    " WHERE ((owner IS NULL) OR (owner ="
                    " (SELECT ROWID FROM users WHERE users.uuid = '%s')))"
                    " ORDER BY %s %s;",
+                   trash ? "_trash" : "",
+                   trash ? "_trash" : "",
+                   (trash
+                     ? "  AND escalator_location"
+                       "      = " G_STRINGIFY (LOCATION_TRASH)
+                     : "  AND escalator_location"
+                       "      = " G_STRINGIFY (LOCATION_TABLE)
+                       "  AND (SELECT hidden FROM tasks"
+                       "       WHERE ROWID = task_escalators.task)"
+                       "      < 2"), /* Task in table. */
+                   trash ? "_trash" : "",
                    current_credentials.uuid,
                    sort_field ? sort_field : "escalators.ROWID",
                    ascending ? "ASC" : "DESC");
@@ -5235,18 +5509,20 @@
  *
  * @param[in]  iterator   Iterator.
  * @param[in]  escalator  Escalator.
+ * @param[in]  trash      Whether to iterate over trashcan escalator data.
  * @param[in]  table      Type of data: "condition", "event" or "method",
  *                        corresponds to substring of the table to select
  *                        from.
  */
 void
 init_escalator_data_iterator (iterator_t *iterator, escalator_t escalator,
-                              const char *table)
+                              int trash, const char *table)
 {
   init_iterator (iterator,
-                 "SELECT name, data FROM escalator_%s_data"
+                 "SELECT name, data FROM escalator_%s_data%s"
                  " WHERE escalator = %llu;",
                  table,
+                 trash ? "_trash" : "",
                  escalator);
 }
 
@@ -5975,7 +6251,7 @@
 {
   iterator_t escalators;
   tracef ("   EVENT %i on task %llu", event, task);
-  init_escalator_iterator (&escalators, 0, task, event, 1, NULL);
+  init_escalator_iterator (&escalators, 0, task, event, 0, 1, NULL);
   while (next (&escalators))
     {
       escalator_t escalator = escalator_iterator_escalator (&escalators);
@@ -6096,12 +6372,14 @@
  *
  * @param[in]  iterator    Task iterator.
  * @param[in]  task        Task to limit iteration to.  0 for all.
+ * @param[in]  trash       Whether to iterate over trashcan tasks.
  * @param[in]  ascending   Whether to sort ascending or descending.
  * @param[in]  sort_field  Field to sort on, or NULL for "ROWID".
  */
 void
 init_task_iterator (iterator_t* iterator,
                     task_t task,
+                    int trash,
                     int ascending,
                     const char *sort_field)
 {
@@ -6115,9 +6393,11 @@
                        " (SELECT ROWID FROM users"
                        "  WHERE users.uuid = '%s'))"
                        " AND ROWID = %llu"
+                       "%s"
                        " ORDER BY %s %s;",
                        current_credentials.uuid,
                        task,
+                       trash ? " AND hidden = 2" : " AND hidden = 0",
                        sort_field ? sort_field : "ROWID",
                        ascending ? "ASC" : "DESC");
       else
@@ -6125,8 +6405,10 @@
                        "SELECT ROWID, uuid, run_status FROM tasks WHERE owner ="
                        " (SELECT ROWID FROM users"
                        "  WHERE users.uuid = '%s')"
+                       "%s"
                        " ORDER BY %s %s;",
                        current_credentials.uuid,
+                       trash ? " AND hidden = 2" : " AND hidden = 0",
                        sort_field ? sort_field : "ROWID",
                        ascending ? "ASC" : "DESC");
     }
@@ -6136,14 +6418,18 @@
         init_iterator (iterator,
                        "SELECT ROWID, uuid, run_status FROM tasks"
                        " WHERE ROWID = %llu"
+                       "%s"
                        " ORDER BY %s %s;",
                        task,
+                       trash ? " AND hidden = 2" : " AND hidden = 0",
                        sort_field ? sort_field : "ROWID",
                        ascending ? "ASC" : "DESC");
       else
         init_iterator (iterator,
                        "SELECT ROWID, uuid, run_status FROM tasks"
+                       "%s"
                        " ORDER BY %s %s;",
+                       trash ? " WHERE hidden = 2" : " WHERE hidden = 0",
                        sort_field ? sort_field : "ROWID",
                        ascending ? "ASC" : "DESC");
     }
@@ -7030,12 +7316,13 @@
       /* Set requested, paused and running tasks to stopped. */
 
       assert (current_credentials.uuid == NULL);
-      init_task_iterator (&tasks, 0, 1, NULL);
+      init_task_iterator (&tasks, 0, 0, 1, NULL);
       while (next (&tasks))
         {
           switch (task_iterator_run_status (&tasks))
             {
               case TASK_STATUS_DELETE_REQUESTED:
+              case TASK_STATUS_DELETE_ULTIMATE_REQUESTED:
               case TASK_STATUS_PAUSE_REQUESTED:
               case TASK_STATUS_PAUSE_WAITING:
               case TASK_STATUS_PAUSED:
@@ -7072,9 +7359,11 @@
            " OR scan_run_status = %u"
            " OR scan_run_status = %u"
            " OR scan_run_status = %u"
+           " OR scan_run_status = %u"
            " OR scan_run_status = %u;",
            TASK_STATUS_STOPPED,
            TASK_STATUS_DELETE_REQUESTED,
+           TASK_STATUS_DELETE_ULTIMATE_REQUESTED,
            TASK_STATUS_PAUSE_REQUESTED,
            TASK_STATUS_PAUSE_WAITING,
            TASK_STATUS_PAUSED,
@@ -7342,6 +7631,11 @@
 char*
 task_config_uuid (task_t task)
 {
+  if (task_config_in_trash (task))
+    return sql_string (0, 0,
+                       "SELECT uuid FROM configs_trash WHERE ROWID ="
+                       " (SELECT config FROM tasks WHERE ROWID = %llu);",
+                       task);
   return sql_string (0, 0,
                      "SELECT uuid FROM configs WHERE ROWID ="
                      " (SELECT config FROM tasks WHERE ROWID = %llu);",
@@ -7358,6 +7652,11 @@
 char*
 task_config_name (task_t task)
 {
+  if (task_config_in_trash (task))
+    return sql_string (0, 0,
+                       "SELECT name FROM configs_trash WHERE ROWID ="
+                       " (SELECT config FROM tasks WHERE ROWID = %llu);",
+                       task);
   return sql_string (0, 0,
                      "SELECT name FROM configs WHERE ROWID ="
                      " (SELECT config FROM tasks WHERE ROWID = %llu);",
@@ -7365,6 +7664,22 @@
 }
 
 /**
+ * @brief Return whether the config of a task is in the trashcan.
+ *
+ * @param[in]  task  Task.
+ *
+ * @return 1 if in trashcan, else 0.
+ */
+int
+task_config_in_trash (task_t task)
+{
+  return sql_int (0, 0,
+                  "SELECT config_location = " G_STRINGIFY (LOCATION_TRASH)
+                  " FROM tasks WHERE ROWID = %llu;",
+                  task);
+}
+
+/**
  * @brief Set the config of a task.
  *
  * @param[in]  task    Task.
@@ -7416,6 +7731,23 @@
 }
 
 /**
+ * @brief Return whether the target of a task is in the trashcan.
+ *
+ * @param[in]  task  Task.
+ *
+ * @return 1 if in trash, else 0.
+ */
+int
+task_target_in_trash (task_t task)
+{
+  return sql_int (0, 0,
+                  "SELECT target_location = " G_STRINGIFY (LOCATION_TRASH)
+                  " FROM tasks WHERE ROWID = %llu;",
+                  task);
+}
+
+
+/**
  * @brief Return the slave of a task.
  *
  * @param[in]  task  Task.
@@ -7455,6 +7787,22 @@
 }
 
 /**
+ * @brief Return whether the slave of a task is in the trashcan.
+ *
+ * @param[in]  task  Task.
+ *
+ * @return 1 if in trash, else 0.
+ */
+int
+task_slave_in_trash (task_t task)
+{
+  return sql_int (0, 0,
+                  "SELECT slave_location = " G_STRINGIFY (LOCATION_TRASH)
+                  " FROM tasks WHERE ROWID = %llu;",
+                  task);
+}
+
+/**
  * @brief Return the description of a task.
  *
  * @param[in]  task  Task.
@@ -7557,7 +7905,8 @@
       || run_status == TASK_STATUS_RESUME_WAITING
       || run_status == TASK_STATUS_STOP_REQUESTED
       || run_status == TASK_STATUS_STOP_WAITING
-      || run_status == TASK_STATUS_DELETE_REQUESTED)
+      || run_status == TASK_STATUS_DELETE_REQUESTED
+      || run_status == TASK_STATUS_DELETE_ULTIMATE_REQUESTED)
     {
       sql ("END;");
       *status = run_status;
@@ -7900,6 +8249,25 @@
 }
 
 /**
+ * @brief Return whether the escalator of a task is in the trashcan.
+ *
+ * Caller must check that there is an escalator on the task.
+ *
+ * @param[in]  task  Task.
+ *
+ * @return 1 if in trashcan, else 0.
+ */
+int
+task_escalator_in_trash (task_t task)
+{
+  return sql_int (0, 0,
+                 "SELECT escalator_location = " G_STRINGIFY (LOCATION_TRASH)
+                 " FROM task_escalators"
+                 " WHERE task = %llu;",
+                 task);
+}
+
+/**
  * @brief Add an escalator to a task.
  *
  * @param[in]  task       Task.
@@ -7908,8 +8276,8 @@
 void
 add_task_escalator (task_t task, escalator_t escalator)
 {
-  sql ("INSERT INTO task_escalators (task, escalator)"
-       " VALUES (%llu, %llu);",
+  sql ("INSERT INTO task_escalators (task, escalator, escalator_location)"
+       " VALUES (%llu, %llu, " G_STRINGIFY (LOCATION_TABLE) ");",
        task,
        escalator);
 }
@@ -7924,8 +8292,8 @@
 set_task_escalator (task_t task, escalator_t escalator)
 {
   sql ("DELETE FROM task_escalators where task = %llu;", task);
-  sql ("INSERT INTO task_escalators (task, escalator)"
-       " VALUES (%llu, %llu);",
+  sql ("INSERT INTO task_escalators (task, escalator, escalator_location)"
+       " VALUES (%llu, %llu, " G_STRINGIFY (LOCATION_TABLE) ");",
        task,
        escalator);
 }
@@ -8191,6 +8559,23 @@
 }
 
 /**
+ * @brief Get whether the task schedule is in the trash.
+ *
+ * @param[in]  task  Task.
+ *
+ * @return 1 if in trash, else 0.
+ */
+int
+task_schedule_in_trash (task_t task)
+{
+  return sql_int (0, 0,
+                  "SELECT schedule_location = " G_STRINGIFY (LOCATION_TRASH)
+                  " FROM tasks"
+                  " WHERE ROWID = %llu;",
+                  task);
+}
+
+/**
  * @brief Get the next time a task with a schedule will run.
  *
  * @param[in]  task  Task.
@@ -11071,7 +11456,8 @@
                " OR scan_run_status = %u OR scan_run_status = %u"
                " OR scan_run_status = %u OR scan_run_status = %u"
                " OR scan_run_status = %u OR scan_run_status = %u"
-               " OR scan_run_status = %u OR scan_run_status = %u);",
+               " OR scan_run_status = %u OR scan_run_status = %u"
+               " OR scan_run_status = %u);",
                report,
                TASK_STATUS_RUNNING,
                TASK_STATUS_PAUSE_REQUESTED,
@@ -11081,6 +11467,7 @@
                TASK_STATUS_RESUME_WAITING,
                TASK_STATUS_REQUESTED,
                TASK_STATUS_DELETE_REQUESTED,
+               TASK_STATUS_DELETE_ULTIMATE_REQUESTED,
                TASK_STATUS_STOP_REQUESTED,
                TASK_STATUS_STOP_WAITING))
     return 2;
@@ -11562,7 +11949,7 @@
   free (uuid);
 
   PRINT (out, "<report_format>");
-  init_report_format_param_iterator (&params, report_format, 1, NULL);
+  init_report_format_param_iterator (&params, report_format, 0, 1, NULL);
   while (next (&params))
     PRINT (out,
            "<param><name>%s</name><value>%s</value></param>",
@@ -12011,7 +12398,7 @@
     /* Setup file names. */
 
     uuid_report = report_uuid (report);
-    init_report_format_iterator (&formats, report_format, 1, NULL);
+    init_report_format_iterator (&formats, report_format, 0, 1, NULL);
     if (next (&formats) == FALSE)
       {
         g_free (xml_file);
@@ -12277,7 +12664,7 @@
     /* Setup file names. */
 
     uuid_report = report_uuid (report);
-    init_report_format_iterator (&formats, report_format, 1, NULL);
+    init_report_format_iterator (&formats, report_format, 0, 1, NULL);
     if (next (&formats) == FALSE)
       {
         g_free (xml_file);
@@ -12783,9 +13170,10 @@
   quoted_comment = comment ? sql_quote ((gchar*) comment) : NULL;
   sql ("INSERT into tasks"
        " (owner, uuid, name, hidden, time, comment, schedule,"
-       "  schedule_next_time, slave)"
+       "  schedule_next_time, slave, config_location, target_location,"
+       "  schedule_location, slave_location)"
        " VALUES ((SELECT ROWID FROM users WHERE users.uuid = '%s'),"
-       "         '%s', '%s', 0, %u, '%s', 0, 0, 0);",
+       "         '%s', '%s', 0, %u, '%s', 0, 0, 0, 0, 0, 0, 0);",
        current_credentials.uuid,
        uuid,
        quoted_name ? quoted_name : "",
@@ -13014,6 +13402,8 @@
  *
  * Stop the task beforehand with \ref stop_task, if it is running.
  *
+ * Used only for CREATE_TASK in omp.c.  Always ultimate.
+ *
  * @param[in]  task_pointer  A pointer to the task.
  *
  * @return 0 if deleted, 1 if delete requested, 2 if task is hidden,
@@ -13023,22 +13413,29 @@
 request_delete_task (task_t* task_pointer)
 {
   task_t task = *task_pointer;
+  int hidden;
 
   tracef ("   request delete task %u\n", task_id (task));
 
-  if (sql_int (0, 0,
-               "SELECT hidden from tasks WHERE ROWID = %llu;",
-               *task_pointer))
+  hidden = sql_int (0, 0,
+                    "SELECT hidden from tasks WHERE ROWID = %llu;",
+                    *task_pointer);
+
+  if (hidden == 1)
     return 2;
 
+  /* Technically the task could be in the trashcan, if someone gets the UUID
+   * with GET_TASKS before the CREATE_TASK finishes, and removes the task.
+   * Pretend it was deleted.  There'll be half a task in the trashcan. */
+  if (hidden == 2)
+    return 0;
+
   if (current_credentials.uuid == NULL) return -1;
 
   switch (stop_task (task))
     {
       case 0:    /* Stopped. */
-        /** @todo Check delete-task return. */
-        delete_task (task);
-        return 0;
+        return delete_task_lock (task, 1);
       case 1:    /* Stop requested. */
         set_task_run_status (task, TASK_STATUS_DELETE_REQUESTED);
         return 1;
@@ -13052,23 +13449,179 @@
   return 0;
 }
 
+static gboolean
+find_trash_task (const char*, task_t*);
+
 /**
+ * @brief Request deletion of a task.
+ *
+ * Stop the task beforehand with \ref stop_task, if it is running.
+ *
+ * @param[in]  task_id   UUID of task.
+ * @param[in]  ultimate  Whether to remove entirely, or to trashcan.
+ *
+ * @return 0 if deleted, 1 if delete requested, 2 if task is hidden, 3 failed
+ *         to find task, -1 if error.
+ */
+int
+request_delete_task_uuid (const char *task_id, int ultimate)
+{
+  task_t task = 0;
+
+  /* Tasks have special handling for the trashcan.  Other resources have trash
+   * tables, like targets_trash.  Tasks are marked as trash in the tasks table
+   * by giving the "hidden" field a value of 2.  This means that the results can
+   * stay in the results table and will still refer to the correct task.  This
+   * should all work because there is already handling of the hidden flag
+   * everywhere else. */
+
+  tracef ("   request delete task %s\n", task_id);
+
+  sql ("BEGIN IMMEDIATE;");
+
+  if (find_task (task_id, &task))
+    {
+      sql ("ROLLBACK;");
+      return -1;
+    }
+
+  if (task == 0)
+    {
+      if (find_trash_task (task_id, &task))
+        {
+          sql ("ROLLBACK;");
+          return -1;
+        }
+      if (task == 0)
+        {
+          sql ("ROLLBACK;");
+          return 3;
+        }
+      if (ultimate == 0)
+        {
+          /* It's already in the trashcan. */
+          sql ("COMMIT;");
+          return 0;
+        }
+
+      if (delete_reports (task))
+        {
+          sql ("ROLLBACK;");
+          return -1;
+        }
+
+      sql ("DELETE FROM results WHERE task = %llu;", task);
+      sql ("DELETE FROM tasks WHERE ROWID = %llu;", task);
+      sql ("DELETE FROM task_escalators WHERE task = %llu;", task);
+      sql ("DELETE FROM task_files WHERE task = %llu;", task);
+      sql ("COMMIT;");
+      return 0;
+    }
+
+  if (sql_int (0, 0,
+               "SELECT hidden from tasks WHERE ROWID = %llu;",
+               task)
+      == 1)
+    {
+      sql ("ROLLBACK;");
+      return 2;
+    }
+
+  if (current_credentials.uuid == NULL)
+    {
+      sql ("ROLLBACK;");
+      return -1;
+    }
+
+  switch (stop_task (task))
+    {
+      case 0:    /* Stopped. */
+        {
+          int ret;
+          ret = delete_task (task, ultimate);
+          if (ret)
+            sql ("ROLLBACK;");
+          else
+            sql ("COMMIT;");
+          return ret;
+        }
+      case 1:    /* Stop requested. */
+        if (ultimate)
+          set_task_run_status (task, TASK_STATUS_DELETE_REQUESTED);
+        else
+          set_task_run_status (task, TASK_STATUS_DELETE_ULTIMATE_REQUESTED);
+        sql ("COMMIT;");
+        return 1;
+      default:   /* Programming error. */
+        assert (0);
+      case -1:   /* Error. */
+        sql ("ROLLBACK;");
+        return -1;
+        break;
+    }
+
+  /*@notreached@*/
+  sql ("COMMIT;");
+  return 0;
+}
+
+/**
  * @brief Complete deletion of a task.
  *
- * @param[in]  task  A pointer to the task.
+ * The caller must do the locking, and must do the hidden check.
  *
+ * The caller must handle the case where the task is already in the trashcan.
+ *
+ * @param[in]  task      The task.
+ * @param[in]  ultimate  Whether to remove entirely, or to trashcan.
+ *
  * @return 0 on success, 1 if task is hidden, -1 on error.
  */
 int
-delete_task (task_t task)
+delete_task (task_t task, int ultimate)
 {
-  char* tsk_uuid;
+  tracef ("   delete task %llu\n", task);
 
-  tracef ("   delete task %u\n", task_id (task));
+  /** @todo Many other places just assert this. */
+  if (current_credentials.uuid == NULL)
+    return -1;
 
+  if (ultimate)
+    {
+      if (delete_reports (task))
+        return -1;
+
+      sql ("DELETE FROM results WHERE task = %llu;", task);
+      sql ("DELETE FROM tasks WHERE ROWID = %llu;", task);
+      sql ("DELETE FROM task_escalators WHERE task = %llu;", task);
+      sql ("DELETE FROM task_files WHERE task = %llu;", task);
+    }
+  else
+    sql ("UPDATE tasks SET hidden = 2 WHERE ROWID = %llu;", task);
+
+  return 0;
+}
+
+/**
+ * @brief Complete deletion of a task.
+ *
+ * This sets up a transaction around the delete.
+ *
+ * @param[in]  task      The task.
+ * @param[in]  ultimate  Whether to remove entirely, or to trashcan.
+ *
+ * @return 0 on success, 1 if task is hidden, -1 on error.
+ */
+int
+delete_task_lock (task_t task, int ultimate)
+{
+  int ret;
+
+  tracef ("   delete task %llu\n", task);
+
   sql ("BEGIN EXCLUSIVE;");
 
-  if (sql_int (0, 0, "SELECT hidden from tasks WHERE ROWID = %llu;", task))
+  if (sql_int (0, 0, "SELECT hidden FROM tasks WHERE ROWID = %llu;", task))
     {
       sql ("ROLLBACK;");
       return -1;
@@ -13081,19 +13634,48 @@
       return -1;
     }
 
-  if (task_uuid (task, &tsk_uuid)
-      || delete_reports (task))
+  ret = delete_task (task, ultimate);
+  if (ret)
+    sql ("ROLLBACK;");
+  else
+    sql ("COMMIT;");
+  return ret;
+}
+
+/**
+ * @brief Delete all trash tasks.
+ *
+ * The caller must do the transaction.
+ *
+ * @param[in]  task      The task.
+ *
+ * @return 0 on success, -1 on error.
+ */
+static int
+delete_trash_tasks ()
+{
+  iterator_t tasks;
+
+  init_task_iterator (&tasks, 0, 1, 1, NULL);
+  while (next (&tasks))
     {
-      sql ("ROLLBACK;");
-      return -1;
+      task_t task;
+
+      task = task_iterator_task (&tasks);
+
+      if (delete_reports (task))
+        {
+          cleanup_iterator (&tasks);
+          return -1;
+        }
+
+      sql ("DELETE FROM results WHERE task = %llu;", task);
+      sql ("DELETE FROM tasks WHERE ROWID = %llu;", task);
+      sql ("DELETE FROM task_escalators WHERE task = %llu;", task);
+      sql ("DELETE FROM task_files WHERE task = %llu;", task);
     }
+  cleanup_iterator (&tasks);
 
-  sql ("DELETE FROM results WHERE task = %llu;", task);
-  sql ("DELETE FROM tasks WHERE ROWID = %llu;", task);
-  sql ("DELETE FROM task_escalators WHERE task = %llu;", task);
-  sql ("DELETE FROM task_files WHERE task = %llu;", task);
-
-  sql ("COMMIT;");
   return 0;
 }
 
@@ -13187,7 +13769,8 @@
       return FALSE;
     }
   switch (sql_int64 (task, 0, 0,
-                     "SELECT ROWID FROM tasks WHERE uuid = '%s';",
+                     "SELECT ROWID FROM tasks WHERE uuid = '%s'"
+                     " AND hidden != 2;",
                      uuid))
     {
       case 0:
@@ -13206,6 +13789,42 @@
 }
 
 /**
+ * @brief Find a task in the trashcan, given an identifier.
+ *
+ * @param[in]   uuid  A task identifier.
+ * @param[out]  task  Task return, 0 if succesfully failed to find task.
+ *
+ * @return FALSE on success (including if failed to find task), TRUE on error.
+ */
+static gboolean
+find_trash_task (const char* uuid, task_t* task)
+{
+  if (user_owns_uuid ("task", uuid) == 0)
+    {
+      *task = 0;
+      return FALSE;
+    }
+  switch (sql_int64 (task, 0, 0,
+                     "SELECT ROWID FROM tasks WHERE uuid = '%s'"
+                     " AND hidden = 2;",
+                     uuid))
+    {
+      case 0:
+        break;
+      case 1:        /* Too few rows in result of query. */
+        *task = 0;
+        break;
+      default:       /* Programming error. */
+        assert (0);
+      case -1:
+        return TRUE;
+        break;
+    }
+
+  return FALSE;
+}
+
+/**
  * @brief Find a report given an identifier.
  *
  * @param[in]   uuid    A report identifier.
@@ -13989,22 +14608,94 @@
 /**
  * @brief Delete a target.
  *
- * @param[in]  target  Target.
+ * @param[in]  target_id  UUID of target.
+ * @param[in]  ultimate   Whether to remove entirely, or to trashcan.
  *
- * @return 0 success, 1 fail because a task refers to the target, -1 error.
+ * @return 0 success, 1 fail because a task refers to the target, 2 failed
+ *         to find target, -1 error.
  */
 int
-delete_target (target_t target)
+delete_target (const char *target_id, int ultimate)
 {
+  target_t target = 0;
+
   sql ("BEGIN IMMEDIATE;");
+
+  if (find_target (target_id, &target))
+    {
+      sql ("ROLLBACK;");
+      return -1;
+    }
+
+  if (target == 0)
+    {
+      if (find_trash ("target", target_id, &target))
+        {
+          sql ("ROLLBACK;");
+          return -1;
+        }
+      if (target == 0)
+        {
+          sql ("ROLLBACK;");
+          return 2;
+        }
+      if (ultimate == 0)
+        {
+          /* It's already in the trashcan. */
+          sql ("COMMIT;");
+          return 0;
+        }
+
+      /* Check if it's in use by a task in the trashcan. */
+      if (sql_int (0, 0,
+                   "SELECT count(*) FROM tasks"
+                   " WHERE target = %llu"
+                   " AND target_location = " G_STRINGIFY (LOCATION_TRASH) ";",
+                   target))
+        {
+          sql ("ROLLBACK;");
+          return 1;
+        }
+
+      sql ("DELETE FROM targets_trash WHERE ROWID = %llu;", target);
+      sql ("COMMIT;");
+      return 0;
+    }
+
   if (sql_int (0, 0,
-               "SELECT count(*) FROM tasks WHERE target = %llu;",
+               "SELECT count(*) FROM tasks"
+               " WHERE target = %llu"
+               " AND target_location = " G_STRINGIFY (LOCATION_TABLE)
+               " AND (hidden = 0 OR hidden = 1);",
                target))
     {
       sql ("ROLLBACK;");
       return 1;
     }
+
+  if (ultimate == 0)
+    {
+      sql ("INSERT INTO targets_trash"
+           " (uuid, owner, name, hosts, comment, lsc_credential,"
+           "  smb_lsc_credential, ssh_location, smb_location)"
+           " SELECT uuid, owner, name, hosts, comment, lsc_credential,"
+           "        smb_lsc_credential, " G_STRINGIFY (LOCATION_TABLE) ", "
+           "      " G_STRINGIFY (LOCATION_TABLE)
+           " FROM targets WHERE ROWID = %llu;",
+           target);
+
+      /* Update the location of the target in any trashcan tasks. */
+      sql ("UPDATE tasks"
+           " SET target = %llu,"
+           "     target_location = " G_STRINGIFY (LOCATION_TRASH)
+           " WHERE target = %llu"
+           " AND target_location = " G_STRINGIFY (LOCATION_TABLE) ";",
+           sqlite3_last_insert_rowid (task_db),
+           target);
+    }
+
   sql ("DELETE FROM targets WHERE ROWID = %llu;", target);
+
   sql ("COMMIT;");
   return 0;
 }
@@ -14014,11 +14705,12 @@
  *
  * @param[in]  iterator    Iterator.
  * @param[in]  target      Target to limit iteration to.  0 for all.
+ * @param[in]  trash       Whether to iterate over trashcan targets.
  * @param[in]  ascending   Whether to sort ascending or descending.
  * @param[in]  sort_field  Field to sort on, or NULL for "ROWID".
  */
 void
-init_target_iterator (iterator_t* iterator, target_t target,
+init_target_iterator (iterator_t* iterator, target_t target, int trash,
                       int ascending, const char* sort_field)
 {
   assert (current_credentials.uuid);
@@ -14026,12 +14718,15 @@
   if (target)
     init_iterator (iterator,
                    "SELECT ROWID, uuid, name, hosts, comment, lsc_credential,"
-                   " smb_lsc_credential, port_range"
-                   " FROM targets"
+                   " smb_lsc_credential, port_range, %s, %s"
+                   " FROM targets%s"
                    " WHERE ROWID = %llu"
                    " AND ((owner IS NULL) OR (owner ="
                    " (SELECT ROWID FROM users WHERE users.uuid = '%s')))"
                    " ORDER BY %s %s;",
+                   trash ? "ssh_location" : "0",
+                   trash ? "smb_location" : "0",
+                   trash ? "_trash" : "",
                    target,
                    current_credentials.uuid,
                    sort_field ? sort_field : "ROWID",
@@ -14039,11 +14734,14 @@
   else
     init_iterator (iterator,
                    "SELECT ROWID, uuid, name, hosts, comment, lsc_credential,"
-                   " smb_lsc_credential, port_range"
-                   " FROM targets"
+                   " smb_lsc_credential, port_range, %s, %s"
+                   " FROM targets%s"
                    " WHERE ((owner IS NULL) OR (owner ="
                    " (SELECT ROWID FROM users WHERE users.uuid = '%s')))"
                    " ORDER BY %s %s;",
+                   trash ? "ssh_location" : "0",
+                   trash ? "smb_location" : "0",
+                   trash ? "_trash" : "",
                    current_credentials.uuid,
                    sort_field ? sort_field : "ROWID",
                    ascending ? "ASC" : "DESC");
@@ -14148,6 +14846,38 @@
 DEF_ACCESS (target_iterator_port_range, 7);
 
 /**
+ * @brief Get the location of the SSH LSC credential from a target iterator.
+ *
+ * @param[in]  iterator  Iterator.
+ *
+ * @return 0 in table, 1 in trash
+ */
+int
+target_iterator_ssh_trash (iterator_t* iterator)
+{
+  int ret;
+  if (iterator->done) return -1;
+  ret = (int) sqlite3_column_int (iterator->stmt, 8);
+  return ret;
+}
+
+/**
+ * @brief Get the location of the SMB LSC credential from a target iterator.
+ *
+ * @param[in]  iterator  Iterator.
+ *
+ * @return 0 in table, 1 in trash
+ */
+int
+target_iterator_smb_trash (iterator_t* iterator)
+{
+  int ret;
+  if (iterator->done) return -1;
+  ret = (int) sqlite3_column_int (iterator->stmt, 9);
+  return ret;
+}
+
+/**
  * @brief Return the UUID of a target.
  *
  * @param[in]  target  Target.
@@ -14194,6 +14924,22 @@
 }
 
 /**
+ * @brief Return the hosts associated with a trashcan target.
+ *
+ * @param[in]  target  Target.
+ *
+ * @return Newly allocated comma separated list of hosts if available,
+ *         else NULL.
+ */
+char*
+trash_target_hosts (target_t target)
+{
+  return sql_string (0, 0,
+                     "SELECT hosts FROM targets_trash WHERE ROWID = %llu;",
+                     target);
+}
+
+/**
  * @brief Return the port range of a target.
  *
  * @param[in]  target  Target.
@@ -14292,7 +15038,7 @@
 }
 
 /**
- * @brief Return whether a target is referenced by a task
+ * @brief Return whether a target is in use by a task.
  *
  * @param[in]  target  Target.
  *
@@ -14302,11 +15048,31 @@
 target_in_use (target_t target)
 {
   return sql_int (0, 0,
-                  "SELECT count(*) FROM tasks WHERE target = %llu;",
+                  "SELECT count(*) FROM tasks"
+                  " WHERE target = %llu"
+                  " AND target_location = " G_STRINGIFY (LOCATION_TABLE)
+                  " AND (hidden = 0 OR hidden = 1);",
                   target);
 }
 
 /**
+ * @brief Return whether a trashcan target is referenced by a task.
+ *
+ * @param[in]  target  Target.
+ *
+ * @return 1 if in use, else 0.
+ */
+int
+trash_target_in_use (target_t target)
+{
+  return sql_int (0, 0,
+                  "SELECT count(*) FROM tasks"
+                  " WHERE target = %llu"
+                  " AND target_location = " G_STRINGIFY (LOCATION_TRASH),
+                  target);
+}
+
+/**
  * @brief Initialise a target task iterator.
  *
  * Iterates over all tasks that use the target.
@@ -15264,36 +16030,122 @@
 /**
  * @brief Delete a config.
  *
- * @param[in]  config  Config.
+ * @param[in]  config    UUID of config.
+ * @param[in]  ultimate  Whether to remove entirely, or to trashcan.
  *
- * @return 0 success, 1 fail because a task refers to the config, -1 error.
+ * @return 0 success, 1 fail because a task refers to the config, 2 failed to
+ *         find config, -1 error.
  */
 int
-delete_config (config_t config)
+delete_config (const char *config_id, int ultimate)
 {
-  if (config == CONFIG_ID_FULL_AND_FAST
-      || config == CONFIG_ID_FULL_AND_FAST_ULTIMATE
-      || config == CONFIG_ID_FULL_AND_VERY_DEEP
-      || config == CONFIG_ID_FULL_AND_VERY_DEEP_ULTIMATE
-      || config == sql_int (0, 0,
-                            "SELECT ROWID FROM configs WHERE name = 'empty';"))
+  config_t config = 0;
+
+  if ((strcmp (config_id, CONFIG_UUID_FULL_AND_FAST_ULTIMATE) == 0)
+      || (strcmp (config_id, CONFIG_UUID_FULL_AND_FAST_ULTIMATE) == 0)
+      || (strcmp (config_id, CONFIG_UUID_FULL_AND_VERY_DEEP) == 0)
+      || (strcmp (config_id, CONFIG_UUID_FULL_AND_VERY_DEEP_ULTIMATE) == 0)
+      || (strcmp (config_id, CONFIG_UUID_EMPTY) == 0))
     return 1;
 
   sql ("BEGIN IMMEDIATE;");
+
+  if (find_config (config_id, &config))
+    {
+      sql ("ROLLBACK;");
+      return -1;
+    }
+
+  if (config == 0)
+    {
+      if (find_trash ("config", config_id, &config))
+        {
+          sql ("ROLLBACK;");
+          return -1;
+        }
+      if (config == 0)
+        {
+          sql ("ROLLBACK;");
+          return 2;
+        }
+      if (ultimate == 0)
+        {
+          /* It's already in the trashcan. */
+          sql ("COMMIT;");
+          return 0;
+        }
+
+      /* Check if it's in use by a task in the trashcan. */
+      if (sql_int (0, 0,
+                   "SELECT count(*) FROM tasks"
+                   " WHERE config = %llu"
+                   " AND config_location = " G_STRINGIFY (LOCATION_TRASH) ";",
+                   config))
+        {
+          sql ("ROLLBACK;");
+          return 1;
+        }
+
+      sql ("DELETE FROM nvt_selectors WHERE name ="
+           " (SELECT nvt_selector FROM configs_trash WHERE ROWID = %llu);",
+           config);
+      sql ("DELETE FROM config_preferences_trash WHERE config = %llu;",
+           config);
+      sql ("DELETE FROM configs_trash WHERE ROWID = %llu;",
+           config);
+      sql ("COMMIT;");
+      return 0;
+    }
+
   if (sql_int (0, 0,
-               "SELECT count(*) FROM tasks WHERE config = %llu;",
+               "SELECT count(*) FROM tasks"
+               " WHERE config = %llu"
+               " AND config_location = " G_STRINGIFY (LOCATION_TABLE)
+               " AND (hidden = 0 OR hidden = 1);",
                config))
     {
       sql ("ROLLBACK;");
       return 1;
     }
-  sql ("DELETE FROM nvt_selectors WHERE name ="
-       " (SELECT nvt_selector FROM configs WHERE ROWID = %llu);",
-       config);
-  sql ("DELETE FROM config_preferences WHERE config = %llu;",
-       config);
-  sql ("DELETE FROM configs WHERE ROWID = %llu;",
-       config);
+
+  if (ultimate)
+    sql ("DELETE FROM nvt_selectors WHERE name ="
+         " (SELECT nvt_selector FROM configs_trash WHERE ROWID = %llu);",
+         config);
+  else
+    {
+      config_t trash_config;
+
+      sql ("INSERT INTO configs_trash"
+           " (uuid, owner, name, nvt_selector, comment, family_count, nvt_count,"
+           "  families_growing, nvts_growing)"
+           " SELECT uuid, owner, name, nvt_selector, comment, family_count,"
+           "        nvt_count, families_growing, nvts_growing"
+           " FROM configs WHERE ROWID = %llu;",
+           config);
+
+      trash_config = sqlite3_last_insert_rowid (task_db);
+
+      sql ("INSERT INTO config_preferences_trash"
+           " (config, type, name, value)"
+           " SELECT %llu, type, name, value"
+           " FROM config_preferences WHERE config = %llu;",
+           trash_config,
+           config);
+
+      /* Update the location of the config in any trashcan tasks. */
+      sql ("UPDATE tasks"
+           " SET config = %llu,"
+           "     config_location = " G_STRINGIFY (LOCATION_TRASH)
+           " WHERE config = %llu"
+           " AND config_location = " G_STRINGIFY (LOCATION_TABLE) ";",
+           trash_config,
+           config);
+    }
+
+  sql ("DELETE FROM config_preferences WHERE config = %llu;", config);
+  sql ("DELETE FROM configs WHERE ROWID = %llu;", config);
+
   sql ("COMMIT;");
   return 0;
 }
@@ -15308,11 +16160,12 @@
  *
  * @param[in]  iterator    Iterator.
  * @param[in]  config      Config.  0 for all.
+ * @param[in]  trash       Whether to iterate over trashcan configs.
  * @param[in]  ascending   Whether to sort ascending or descending.
  * @param[in]  sort_field  Field to sort on, or NULL for "ROWID".
  */
 void
-init_config_iterator (iterator_t* iterator, config_t config,
+init_config_iterator (iterator_t* iterator, config_t config, int trash,
                       int ascending, const char* sort_field)
 
 {
@@ -15322,23 +16175,25 @@
 
   if (config)
     sql = g_strdup_printf ("SELECT " CONFIG_ITERATOR_FIELDS
-                           " FROM configs"
+                           " FROM configs%s"
                            " WHERE ROWID = %llu"
                            " AND ((owner IS NULL) OR (owner ="
                            " (SELECT ROWID FROM users"
                            "  WHERE users.uuid = '%s')))"
                            " ORDER BY %s %s;",
+                           trash ? "_trash" : "",
                            config,
                            current_credentials.uuid,
                            sort_field ? sort_field : "ROWID",
                            ascending ? "ASC" : "DESC");
   else
     sql = g_strdup_printf ("SELECT " CONFIG_ITERATOR_FIELDS
-                           " FROM configs"
+                           " FROM configs%s"
                            " WHERE ((owner IS NULL) OR (owner ="
                            " (SELECT ROWID FROM users"
                            "  WHERE users.uuid = '%s')))"
                            " ORDER BY %s %s;",
+                           trash ? "_trash" : "",
                            current_credentials.uuid,
                            sort_field ? sort_field : "ROWID",
                            ascending ? "ASC" : "DESC");
@@ -15459,11 +16314,31 @@
     return 1;
 
   return sql_int (0, 0,
-                  "SELECT count(*) FROM tasks WHERE config = %llu;",
+                  "SELECT count(*) FROM tasks"
+                  " WHERE config = %llu"
+                  " AND config_location = " G_STRINGIFY (LOCATION_TABLE)
+                  " AND (hidden = 0 OR hidden = 1);",
                   config);
 }
 
 /**
+ * @brief Return whether a trashcan config is referenced by a task.
+ *
+ * @param[in]  config  Config.
+ *
+ * @return 1 if in use, else 0.
+ */
+int
+trash_config_in_use (config_t config)
+{
+  return sql_int (0, 0,
+                  "SELECT count(*) FROM tasks"
+                  " WHERE config = %llu"
+                  " AND config_location = " G_STRINGIFY (LOCATION_TRASH),
+                  config);
+}
+
+/**
  * @brief Initialise a preference iterator.
  *
  * Assume the caller has permission to access the config.
@@ -16599,7 +17474,7 @@
 {
   iterator_t configs;
 
-  init_config_iterator (&configs, config, 1, NULL);
+  init_config_iterator (&configs, config, 0, 1, NULL);
   while (next (&configs))
     update_config_cache (&configs);
   cleanup_iterator (&configs);
@@ -18483,18 +19358,71 @@
 /**
  * @brief Delete an LSC credential.
  *
- * @param[in]  lsc_credential  LSC credential.
+ * @param[in]  lsc_credential_id  UUID of LSC credential.
+ * @param[in]  ultimate           Whether to remove entirely, or to trashcan.
  *
  * @return 0 success, 1 fail because the LSC credential is in use, -1 error.
  */
 int
-delete_lsc_credential (lsc_credential_t lsc_credential)
+delete_lsc_credential (const char *lsc_credential_id, int ultimate)
 {
+  lsc_credential_t lsc_credential = 0;
+
   sql ("BEGIN IMMEDIATE;");
 
+  if (find_lsc_credential (lsc_credential_id, &lsc_credential))
+    {
+      sql ("ROLLBACK;");
+      return -1;
+    }
+
+  if (lsc_credential == 0)
+    {
+      if (find_trash ("lsc_credential", lsc_credential_id, &lsc_credential))
+        {
+          sql ("ROLLBACK;");
+          return -1;
+        }
+      if (lsc_credential == 0)
+        {
+          sql ("ROLLBACK;");
+          return 2;
+        }
+      if (ultimate == 0)
+        {
+          /* It's already in the trashcan. */
+          sql ("COMMIT;");
+          return 0;
+        }
+
+      /* Check if it's in use by a target in the trashcan. */
+      if (sql_int (0, 0,
+                   "SELECT count(*) FROM targets_trash"
+                   " WHERE (lsc_credential = %llu"
+                   "        AND ssh_location = " G_STRINGIFY (LOCATION_TRASH) ")"
+                   " OR (smb_lsc_credential = %llu;",
+                   "     AND smb_location = " G_STRINGIFY (LOCATION_TRASH) ");",
+                   lsc_credential,
+                   lsc_credential))
+        {
+          sql ("ROLLBACK;");
+          return 1;
+        }
+
+      sql ("DELETE FROM lsc_credentials_trash WHERE ROWID = %llu;", lsc_credential);
+      sql ("COMMIT;");
+      return 0;
+    }
+
+
   if (sql_int (0, 0,
                "SELECT count(*) FROM targets"
-               " WHERE lsc_credential = %llu OR smb_lsc_credential = %llu;",
+               " WHERE"
+               " (lsc_credential = %llu"
+               "  AND ssh_location = " G_STRINGIFY (LOCATION_TABLE) ")"
+               " OR"
+               " (smb_lsc_credential = %llu"
+               "  AND smb_location = " G_STRINGIFY (LOCATION_TABLE) ")",
                lsc_credential,
                lsc_credential))
     {
@@ -18502,7 +19430,34 @@
       return 1;
     }
 
+  if (ultimate == 0)
+    {
+      sql ("INSERT INTO lsc_credentials_trash"
+           " (uuid, owner, name, login, password, comment, public_key,"
+           "  private_key, rpm, deb, exe)"
+           " SELECT uuid, owner, name, login, password, comment, public_key,"
+           "  private_key, rpm, deb, exe"
+           " FROM lsc_credentials WHERE ROWID = %llu;",
+           lsc_credential);
+      /* Update the credential references in any trashcan targets.  This
+       * situation is possible if the user restores the credential when the
+       * target is in the trashcan. */
+      sql ("UPDATE targets_trash"
+           " SET ssh_location = " G_STRINGIFY (LOCATION_TRASH) ","
+           "     lsc_credential = %llu"
+           " WHERE lsc_credential = %llu;",
+           sqlite3_last_insert_rowid (task_db),
+           lsc_credential);
+      sql ("UPDATE targets_trash"
+           " SET smb_location = " G_STRINGIFY (LOCATION_TRASH) ","
+           " smb_lsc_credential = %llu"
+           " WHERE smb_lsc_credential = %llu;",
+           sqlite3_last_insert_rowid (task_db),
+           lsc_credential);
+    }
+
   sql ("DELETE FROM lsc_credentials WHERE ROWID = %llu;", lsc_credential);
+
   sql ("COMMIT;");
   return 0;
 }
@@ -18594,13 +19549,14 @@
  *
  * @param[in]  iterator        Iterator.
  * @param[in]  lsc_credential  Single LSC credential to iterate, 0 for all.
+ * @param[in]  trash           Whether to iterate over trashcan credentials.
  * @param[in]  ascending       Whether to sort ascending or descending.
  * @param[in]  sort_field      Field to sort on, or NULL for "ROWID".
  */
 void
 init_lsc_credential_iterator (iterator_t* iterator,
-                              lsc_credential_t lsc_credential, int ascending,
-                              const char* sort_field)
+                              lsc_credential_t lsc_credential, int trash,
+                              int ascending, const char* sort_field)
 {
   assert (current_credentials.uuid);
 
@@ -18608,15 +19564,20 @@
     init_iterator (iterator,
                    "SELECT ROWID, uuid, name, login, password, comment,"
                    " public_key, private_key, rpm, deb, exe,"
-                   " (SELECT count(*) > 0 FROM targets"
-                   "  WHERE lsc_credential = lsc_credentials.ROWID)"
-                   " + (SELECT count(*) > 0 FROM targets"
-                   "    WHERE smb_lsc_credential = lsc_credentials.ROWID)"
-                   " FROM lsc_credentials"
+                   " (SELECT count(*) > 0 FROM targets%s"
+                   "  WHERE lsc_credential = lsc_credentials%s.ROWID)"
+                   " + (SELECT count(*) > 0 FROM targets%s"
+                   "    WHERE smb_lsc_credential = lsc_credentials%s.ROWID)"
+                   " FROM lsc_credentials%s"
                    " WHERE ROWID = %llu"
                    " AND ((owner IS NULL) OR (owner ="
                    " (SELECT ROWID FROM users WHERE users.uuid = '%s')))"
                    " ORDER BY %s %s;",
+                   trash ? "_trash" : "",
+                   trash ? "_trash" : "",
+                   trash ? "_trash" : "",
+                   trash ? "_trash" : "",
+                   trash ? "_trash" : "",
                    lsc_credential,
                    current_credentials.uuid,
                    sort_field ? sort_field : "ROWID",
@@ -18625,14 +19586,19 @@
     init_iterator (iterator,
                    "SELECT ROWID, uuid, name, login, password, comment,"
                    " public_key, private_key, rpm, deb, exe,"
-                   " (SELECT count(*) > 0 FROM targets"
-                   "  WHERE lsc_credential = lsc_credentials.ROWID)"
-                   " + (SELECT count(*) > 0 FROM targets"
-                   "    WHERE smb_lsc_credential = lsc_credentials.ROWID)"
-                   " FROM lsc_credentials"
+                   " (SELECT count(*) > 0 FROM targets%s"
+                   "  WHERE lsc_credential = lsc_credentials%s.ROWID)"
+                   " + (SELECT count(*) > 0 FROM targets%s"
+                   "    WHERE smb_lsc_credential = lsc_credentials%s.ROWID)"
+                   " FROM lsc_credentials%s"
                    " WHERE ((owner IS NULL) OR (owner ="
                    " (SELECT ROWID FROM users WHERE users.uuid = '%s')))"
                    " ORDER BY %s %s;",
+                   trash ? "_trash" : "",
+                   trash ? "_trash" : "",
+                   trash ? "_trash" : "",
+                   trash ? "_trash" : "",
+                   trash ? "_trash" : "",
                    current_credentials.uuid,
                    sort_field ? sort_field : "ROWID",
                    ascending ? "ASC" : "DESC");
@@ -18854,6 +19820,22 @@
 }
 
 /**
+ * @brief Get the UUID of an LSC credential in the trashcan.
+ *
+ * @param[in]  lsc_credential  LSC credential.
+ *
+ * @return UUID.
+ */
+char*
+trash_lsc_credential_uuid (lsc_credential_t lsc_credential)
+{
+  return sql_string (0, 0,
+                     "SELECT uuid FROM lsc_credentials_trash"
+                     " WHERE ROWID = %llu;",
+                     lsc_credential);
+}
+
+/**
  * @brief Get the name of an LSC credential.
  *
  * @param[in]  lsc_credential  LSC credential.
@@ -18869,6 +19851,22 @@
 }
 
 /**
+ * @brief Get the name of an LSC credential in the trashcan.
+ *
+ * @param[in]  lsc_credential  LSC credential.
+ *
+ * @return Name.
+ */
+char*
+trash_lsc_credential_name (lsc_credential_t lsc_credential)
+{
+  return sql_string (0, 0,
+                     "SELECT name FROM lsc_credentials_trash"
+                     " WHERE ROWID = %llu;",
+                     lsc_credential);
+}
+
+/**
  * @brief Initialise an LSC credential target iterator.
  *
  * Iterates over all targets that use the credential.
@@ -19426,14 +20424,64 @@
 /**
  * @brief Delete an agent.
  *
- * @param[in]  agent  Agent.
+ * @param[in]  agent_id   UUID of agent.
+ * @param[in]  ultimate   Whether to remove entirely, or to trashcan.
  *
- * @return 0 success, -1 error.
+ * @return 0 success, 2 failed to find agent, -1 error.
  */
 int
-delete_agent (agent_t agent)
+delete_agent (const char *agent_id, int ultimate)
 {
+  agent_t agent = 0;
+
+  sql ("BEGIN IMMEDIATE;");
+
+  if (find_agent (agent_id, &agent))
+    {
+      sql ("ROLLBACK;");
+      return -1;
+    }
+
+  if (agent == 0)
+    {
+      if (find_trash ("agent", agent_id, &agent))
+        {
+          sql ("ROLLBACK;");
+          return -1;
+        }
+      if (agent == 0)
+        {
+          sql ("ROLLBACK;");
+          return 2;
+        }
+      if (ultimate == 0)
+        {
+          /* It's already in the trashcan. */
+          sql ("COMMIT;");
+          return 0;
+        }
+
+      sql ("DELETE FROM agents_trash WHERE ROWID = %llu;", agent);
+      sql ("COMMIT;");
+      return 0;
+    }
+
+  if (ultimate == 0)
+    {
+      sql ("INSERT INTO agents_trash"
+           " (uuid, owner, name, comment, installer, installer_64,"
+           "  installer_filename, installer_signature_64, installer_trust,"
+           "  installer_trust_time, howto_install, howto_use)"
+           " SELECT"
+           "  uuid, owner, name, comment, installer, installer_64,"
+           "  installer_filename, installer_signature_64, installer_trust,"
+           "  installer_trust_time, howto_install, howto_use"
+           " FROM agents WHERE ROWID = %llu;",
+           agent);
+    }
+
   sql ("DELETE FROM agents WHERE ROWID = %llu;", agent);
+  sql ("COMMIT;");
   return 0;
 }
 
@@ -19452,7 +20500,7 @@
 
   sql ("BEGIN IMMEDIATE;");
 
-  init_agent_iterator (&agents, agent, 1, NULL);
+  init_agent_iterator (&agents, agent, 0, 1, NULL);
   if (next (&agents))
     {
       const char *signature_64;
@@ -19570,11 +20618,12 @@
  *
  * @param[in]  iterator    Iterator.
  * @param[in]  agent       Single agent to iterate, 0 for all.
+ * @param[in]  trash       Whether to iterate over trashcan targets.
  * @param[in]  ascending   Whether to sort ascending or descending.
  * @param[in]  sort_field  Field to sort on, or NULL for "ROWID".
  */
 void
-init_agent_iterator (iterator_t* iterator, agent_t agent,
+init_agent_iterator (iterator_t* iterator, agent_t agent, int trash,
                      int ascending, const char* sort_field)
 {
   assert (current_credentials.uuid);
@@ -19585,11 +20634,12 @@
                    " installer_filename, installer_signature_64,"
                    " installer_trust, installer_trust_time, howto_install,"
                    " howto_use"
-                   " FROM agents"
+                   " FROM agents%s"
                    " WHERE ROWID = %llu"
                    " AND ((owner IS NULL) OR (owner ="
                    " (SELECT ROWID FROM users WHERE users.uuid = '%s')))"
                    " ORDER BY %s %s;",
+                   trash ? "_trash" : "",
                    agent,
                    current_credentials.uuid,
                    sort_field ? sort_field : "ROWID",
@@ -19600,10 +20650,11 @@
                    " installer_filename, installer_signature_64,"
                    " installer_trust, installer_trust_time, howto_install,"
                    " howto_use"
-                   " FROM agents"
+                   " FROM agents%s"
                    " WHERE ((owner IS NULL) OR (owner ="
                    " (SELECT ROWID FROM users WHERE users.uuid = '%s')))"
                    " ORDER BY %s %s;",
+                   trash ? "_trash" : "",
                    current_credentials.uuid,
                    sort_field ? sort_field : "ROWID",
                    ascending ? "ASC" : "DESC");
@@ -20890,21 +21941,91 @@
  * @brief Delete a schedule.
  *
  * @param[in]  schedule  Schedule.
+ * @param[in]  ultimate  Whether to remove entirely, or to trashcan.
  *
  * @return 0 success, 1 fail because a task refers to the schedule, -1 error.
  */
 int
-delete_schedule (schedule_t schedule)
+delete_schedule (const char *schedule_id, int ultimate)
 {
+  schedule_t schedule = 0;
+
   sql ("BEGIN IMMEDIATE;");
+
+  if (find_schedule (schedule_id, &schedule))
+    {
+      sql ("ROLLBACK;");
+      return -1;
+    }
+
+  if (schedule == 0)
+    {
+      if (find_trash ("schedule", schedule_id, &schedule))
+        {
+          sql ("ROLLBACK;");
+          return -1;
+        }
+      if (schedule == 0)
+        {
+          sql ("ROLLBACK;");
+          return 2;
+        }
+      if (ultimate == 0)
+        {
+          /* It's already in the trashcan. */
+          sql ("COMMIT;");
+          return 0;
+        }
+
+      /* Check if it's in use by a task in the trashcan. */
+      if (sql_int (0, 0,
+                   "SELECT count(*) FROM tasks"
+                   " WHERE schedule = %llu"
+                   " AND schedule_location = " G_STRINGIFY (LOCATION_TRASH) ";",
+                   schedule))
+        {
+          sql ("ROLLBACK;");
+          return 1;
+        }
+
+      sql ("DELETE FROM schedules_trash WHERE ROWID = %llu;", schedule);
+      sql ("COMMIT;");
+      return 0;
+    }
+
   if (sql_int (0, 0,
-               "SELECT count(*) FROM tasks WHERE schedule = %llu;",
+               "SELECT count(*) FROM tasks"
+               " WHERE schedule = %llu"
+               " AND schedule_location = " G_STRINGIFY (LOCATION_TABLE)
+               " AND (hidden = 0 OR hidden = 1);",
                schedule))
     {
       sql ("ROLLBACK;");
       return 1;
     }
+
+  if (ultimate == 0)
+    {
+      sql ("INSERT INTO schedules_trash"
+           " (uuid, owner, name, comment, first_time, period, period_months,"
+           "  duration)"
+           " SELECT uuid, owner, name, comment, first_time, period, period_months,"
+           "        duration"
+           " FROM schedules WHERE ROWID = %llu;",
+           schedule);
+
+      /* Update the location of the schedule in any trashcan tasks. */
+      sql ("UPDATE tasks"
+           " SET schedule = %llu,"
+           "     schedule_location = " G_STRINGIFY (LOCATION_TRASH)
+           " WHERE schedule = %llu"
+           " AND schedule_location = " G_STRINGIFY (LOCATION_TABLE) ";",
+           sqlite3_last_insert_rowid (task_db),
+           schedule);
+    }
+
   sql ("DELETE FROM schedules WHERE ROWID = %llu;", schedule);
+
   sql ("COMMIT;");
   return 0;
 }
@@ -21047,19 +22168,38 @@
  *
  * @param[in]  iterator  Iterator.
  * @param[in]  schedule  Single schedule to iterate over, or 0 for all.
+ * @param[in]  trash       Whether to iterate over trashcan targets.
  * @param[in]  ascending   Whether to sort ascending or descending.
  * @param[in]  sort_field  Field to sort on, or NULL for "ROWID".
  */
 void
-init_schedule_iterator (iterator_t* iterator, schedule_t schedule,
+init_schedule_iterator (iterator_t* iterator, schedule_t schedule, int trash,
                         int ascending, const char* sort_field)
 {
-  if (schedule)
+  if (schedule && trash)
     init_iterator (iterator,
                    "SELECT ROWID, uuid, name, comment, first_time,"
                    " period, period_months, duration,"
                    " (SELECT count(*) > 0 FROM tasks"
-                   "  WHERE tasks.schedule = schedules.ROWID)"
+                   "  WHERE tasks.schedule = schedules_trash.ROWID"
+                   "  AND schedule_location = " G_STRINGIFY (LOCATION_TRASH) ")"
+                   " FROM schedules_trash"
+                   " WHERE ROWID = %llu"
+                   " AND ((owner IS NULL) OR (owner ="
+                   " (SELECT ROWID FROM users WHERE users.uuid = '%s')))"
+                   " ORDER BY %s %s;",
+                   schedule,
+                   current_credentials.uuid,
+                   sort_field ? sort_field : "ROWID",
+                   ascending ? "ASC" : "DESC");
+  else if (schedule)
+    init_iterator (iterator,
+                   "SELECT ROWID, uuid, name, comment, first_time,"
+                   " period, period_months, duration,"
+                   " (SELECT count(*) > 0 FROM tasks"
+                   "  WHERE tasks.schedule = schedules.ROWID"
+                   "  AND (hidden = 0 OR hidden = 1)"
+                   "  AND schedule_location = " G_STRINGIFY (LOCATION_TABLE) ")"
                    " FROM schedules"
                    " WHERE ROWID = %llu"
                    " AND ((owner IS NULL) OR (owner ="
@@ -21069,12 +22209,28 @@
                    current_credentials.uuid,
                    sort_field ? sort_field : "ROWID",
                    ascending ? "ASC" : "DESC");
+  else if (trash)
+    init_iterator (iterator,
+                   "SELECT ROWID, uuid, name, comment, first_time,"
+                   " period, period_months, duration,"
+                   " (SELECT count(*) > 0 FROM tasks"
+                   "  WHERE tasks.schedule = schedules_trash.ROWID"
+                   "  AND schedule_location = " G_STRINGIFY (LOCATION_TRASH) ")"
+                   " FROM schedules_trash"
+                   " WHERE ((owner IS NULL) OR (owner ="
+                   " (SELECT ROWID FROM users WHERE users.uuid = '%s')))"
+                   " ORDER BY %s %s;",
+                   current_credentials.uuid,
+                   sort_field ? sort_field : "ROWID",
+                   ascending ? "ASC" : "DESC");
   else
     init_iterator (iterator,
                    "SELECT ROWID, uuid, name, comment, first_time,"
                    " period, period_months, duration,"
                    " (SELECT count(*) > 0 FROM tasks"
-                   "  WHERE tasks.schedule = schedules.ROWID)"
+                   "  WHERE tasks.schedule = schedules.ROWID"
+                   "  AND (hidden = 0 OR hidden = 1)"
+                   "  AND schedule_location = " G_STRINGIFY (LOCATION_TABLE) ")"
                    " FROM schedules"
                    " WHERE ((owner IS NULL) OR (owner ="
                    " (SELECT ROWID FROM users WHERE users.uuid = '%s')))"
@@ -21492,7 +22648,8 @@
 init_schedule_task_iterator (iterator_t* iterator, schedule_t schedule)
 {
   init_iterator (iterator,
-                 "SELECT ROWID, uuid, name FROM tasks WHERE schedule = %llu;",
+                 "SELECT ROWID, uuid, name FROM tasks"
+                 " WHERE schedule = %llu AND hidden = 0;",
                  schedule);
 }
 
@@ -22069,47 +23226,184 @@
 /**
  * @brief Delete a report format.
  *
- * @param[in]  report_format  Report format.
+ * @param[in]  report_format_id  UUID of Report format.
+ * @param[in]  ultimate          Whether to remove entirely, or to trashcan.
  *
- * @return 0 success, -1 error.
+ * @return 0 success, 2 failed to find agent, -1 error.
  */
 int
-delete_report_format (report_format_t report_format)
+delete_report_format (const char *report_format_id, int ultimate)
 {
-  char *uuid;
   gchar *dir;
+  report_format_t report_format, trash_report_format;
 
+  /* This is complicated in two ways
+   *
+   *   - the UUID of a report format is the same every time it is
+   *     imported, so to prevent multiple deletes from producing
+   *     duplicate UUIDs in the trashcan, each report format in the
+   *     trashcan gets a new UUID,
+   *
+   *   - the report format has information on disk on top of the
+   *     info in the db, so the disk information has to be held
+   *     in a special trashcan directory. */
+
   sql ("BEGIN IMMEDIATE;");
 
-  uuid = report_format_uuid (report_format);
-  if (uuid == NULL)
+  /* Look in the "real" table. */
+
+  if (find_report_format (report_format_id, &report_format))
     {
       sql ("ROLLBACK;");
-      return -1;
+      return 3;
     }
 
+  if (report_format == 0)
+    {
+      gchar *report_format_string;
+
+      /* Look in the trashcan. */
+
+      if (find_trash ("report_format", report_format_id, &report_format))
+        {
+          sql ("ROLLBACK;");
+          return -1;
+        }
+      if (report_format == 0)
+        {
+          sql ("ROLLBACK;");
+          return 2;
+        }
+      if (ultimate == 0)
+        {
+          /* It's already in the trashcan. */
+          sql ("COMMIT;");
+          return 0;
+        }
+
+      /* Remove entirely. */
+
+      sql ("DELETE FROM report_formats_trash WHERE ROWID = %llu;",
+           report_format);
+      sql ("DELETE FROM report_format_param_options_trash"
+           " WHERE report_format_param"
+           " IN (SELECT ROWID from report_format_params_trash"
+           "     WHERE report_format = %llu);",
+           report_format);
+      sql ("DELETE FROM report_format_params_trash WHERE report_format = %llu;",
+           report_format);
+
+      /* Remove the dir last, in case any SQL rolls back. */
+      report_format_string = g_strdup_printf ("%llu", report_format);
+      dir = g_build_filename (OPENVAS_SYSCONF_DIR,
+                              "openvasmd",
+                              "report_formats_trash",
+                              report_format_string,
+                              NULL);
+      g_free (report_format_string);
+      if (g_file_test (dir, G_FILE_TEST_EXISTS) && file_utils_rmdir_rf (dir))
+        {
+          g_free (dir);
+          sql ("ROLLBACK;");
+          return -1;
+        }
+      g_free (dir);
+
+      sql ("COMMIT;");
+
+      return 0;
+    }
+
   if (report_format_global (report_format))
     dir = g_build_filename (OPENVAS_DATA_DIR,
                             "openvasmd",
                             "global_report_formats",
-                            uuid,
+                            report_format_id,
                             NULL);
   else
     dir = g_build_filename (OPENVAS_STATE_DIR,
                             "openvasmd",
                             "report_formats",
                             current_credentials.uuid,
-                            uuid,
+                            report_format_id,
                             NULL);
-  free (uuid);
-  if (g_file_test (dir, G_FILE_TEST_EXISTS) && file_utils_rmdir_rf (dir))
+
+  if (ultimate)
     {
-      g_free (dir);
-      sql ("ROLLBACK;");
-      return -1;
+      /* Remove directory. */
+
+      if (g_file_test (dir, G_FILE_TEST_EXISTS) && file_utils_rmdir_rf (dir))
+        {
+          g_free (dir);
+          sql ("ROLLBACK;");
+          return -1;
+        }
     }
-  g_free (dir);
+  else
+    {
+      iterator_t params;
+      gchar *trash_dir;
 
+      /* Move to trash. */
+
+      trash_dir = g_build_filename (OPENVAS_SYSCONF_DIR,
+                                  "openvasmd",
+                                  "report_formats_trash",
+                                  NULL);
+      if (g_mkdir_with_parents (trash_dir, 0755 /* "rwxr-xr-x" */))
+        {
+          g_warning ("%s: failed to create dir %s", __FUNCTION__, dir);
+          g_free (trash_dir);
+          sql ("ROLLBACK;");
+          return -1;
+        }
+      g_free (trash_dir);
+
+      sql ("INSERT INTO report_formats_trash"
+           " (uuid, owner, name, extension, content_type, summary,"
+           "  description, signature, trust, trust_time, flags, original_uuid)"
+           " SELECT"
+           "  make_uuid (), owner, name, extension, content_type, summary,"
+           "  description, signature, trust, trust_time, flags, uuid"
+           " FROM report_formats"
+           " WHERE ROWID = %llu;",
+           report_format);
+
+      trash_report_format = sqlite3_last_insert_rowid (task_db);
+
+      init_report_format_param_iterator (&params, report_format, 0, 1, NULL);
+      while (next (&params))
+        {
+          report_format_param_t param, trash_param;
+
+          param = report_format_param_iterator_param (&params);
+
+          sql ("INSERT INTO report_format_params_trash"
+               " (report_format, name, type, value, type_min, type_max,"
+               "  type_regex, fallback)"
+               " SELECT"
+               "  %llu, name, type, value, type_min, type_max,"
+               "  type_regex, fallback"
+               " FROM report_format_params"
+               " WHERE ROWID = %llu;",
+               trash_report_format,
+               param);
+
+          trash_param = sqlite3_last_insert_rowid (task_db);
+
+          sql ("INSERT INTO report_format_param_options_trash"
+               " (report_format_param, value)"
+               " SELECT %llu, value"
+               " FROM report_format_param_options"
+               " WHERE report_format_param = %llu;",
+               trash_param,
+               param);
+        }
+      cleanup_iterator (&params);
+    }
+
+  /* Remove from "real" tables. */
+
   sql ("DELETE FROM report_formats WHERE ROWID = %llu;", report_format);
   sql ("DELETE FROM report_format_param_options WHERE report_format_param"
        " IN (SELECT ROWID from report_format_params WHERE report_format = %llu);",
@@ -22117,6 +23411,44 @@
   sql ("DELETE FROM report_format_params WHERE report_format = %llu;",
        report_format);
 
+  /* Move the dir last, in case any SQL rolls back. */
+
+  if (ultimate == 0)
+    {
+      gchar *new_dir, *report_format_string;
+
+      report_format_string = g_strdup_printf ("%llu", trash_report_format);
+      new_dir = g_build_filename (OPENVAS_SYSCONF_DIR,
+                                  "openvasmd",
+                                  "report_formats_trash",
+                                  report_format_string,
+                                  NULL);
+      g_free (report_format_string);
+      if (g_file_test (dir, G_FILE_TEST_EXISTS))
+        {
+          if (rename (dir, new_dir))
+            {
+              g_warning ("%s: rename %s to %s: %s\n",
+                         __FUNCTION__, dir, new_dir, strerror (errno));
+              g_free (dir);
+              g_free (new_dir);
+              sql ("ROLLBACK;");
+              return -1;
+            }
+        }
+      else
+        {
+          g_warning ("%s: report dir missing: %s\n",
+                     __FUNCTION__, dir);
+          g_free (dir);
+          g_free (new_dir);
+          sql ("ROLLBACK;");
+          return -1;
+        }
+      g_free (new_dir);
+    }
+  g_free (dir);
+
   sql ("COMMIT;");
 
   return 0;
@@ -22137,7 +23469,7 @@
 
   sql ("BEGIN IMMEDIATE;");
 
-  init_report_format_iterator (&formats, report_format, 1, NULL);
+  init_report_format_iterator (&formats, report_format, 0, 1, NULL);
   if (next (&formats))
     {
       const char *signature;
@@ -22188,6 +23520,7 @@
 
           init_report_format_param_iterator (&params,
                                              report_format,
+                                             0,
                                              1,
                                              NULL);
           while (next (&params))
@@ -22397,6 +23730,22 @@
 }
 
 /**
+ * @brief Return whether a report format is global.
+ *
+ * @param[in]  report_format  Report format.
+ *
+ * @return 1 if global, else 0.
+ */
+static int
+report_format_trash_global (report_format_t report_format)
+{
+  return sql_int (0, 0,
+                  "SELECT owner is NULL FROM report_formats_trash"
+                  " WHERE ROWID = %llu;",
+                  report_format);
+}
+
+/**
  * @brief Return whether a report format is active.
  *
  * @param[in]  report_format  Report format.
@@ -22669,23 +24018,25 @@
  *
  * @param[in]  iterator  Iterator.
  * @param[in]  report_format  Single report_format to iterate over, or 0 for all.
+ * @param[in]  trash       Whether to iterate over trashcan report formats.
  * @param[in]  ascending   Whether to sort ascending or descending.
  * @param[in]  sort_field  Field to sort on, or NULL for "ROWID".
  */
 void
 init_report_format_iterator (iterator_t* iterator, report_format_t report_format,
-                             int ascending, const char* sort_field)
+                             int trash, int ascending, const char* sort_field)
 {
   if (report_format)
     init_iterator (iterator,
                    "SELECT ROWID, uuid, name, extension, content_type,"
                    " summary, description, owner IS NULL, signature, trust,"
                    " trust_time, flags"
-                   " FROM report_formats"
+                   " FROM report_formats%s"
                    " WHERE ROWID = %llu"
                    " AND ((owner IS NULL) OR (owner ="
                    " (SELECT ROWID FROM users WHERE users.uuid = '%s')))"
                    " ORDER BY %s %s;",
+                   trash ? "_trash" : "",
                    report_format,
                    current_credentials.uuid,
                    sort_field ? sort_field : "ROWID",
@@ -22695,10 +24046,11 @@
                    "SELECT ROWID, uuid, name, extension, content_type,"
                    " summary, description, owner is NULL, signature, trust,"
                    " trust_time, flags"
-                   " FROM report_formats"
+                   " FROM report_formats%s"
                    " WHERE ((owner IS NULL) OR (owner ="
                    " (SELECT ROWID FROM users WHERE users.uuid = '%s')))"
                    " ORDER BY %s %s;",
+                   trash ? "_trash" : "",
                    current_credentials.uuid,
                    sort_field ? sort_field : "ROWID",
                    ascending ? "ASC" : "DESC");
@@ -22858,20 +24210,25 @@
  *
  * @param[in]  iterator       Iterator.
  * @param[in]  report_format  Single report_format to iterate over, or 0 for all.
+ * @param[in]  trash          Whether to iterate over trashcan report formats.
  * @param[in]  ascending      Whether to sort ascending or descending.
  * @param[in]  sort_field     Field to sort on, or NULL for "ROWID".
  */
 void
-init_report_format_param_iterator (iterator_t* iterator, report_format_t report_format,
-                                   int ascending, const char* sort_field)
+init_report_format_param_iterator (iterator_t* iterator,
+                                   report_format_t report_format,
+                                   int trash,
+                                   int ascending,
+                                   const char* sort_field)
 {
   if (report_format)
     init_iterator (iterator,
                    "SELECT ROWID, name, value, type, type_min, type_max,"
                    " type_regex, fallback"
-                   " FROM report_format_params"
+                   " FROM report_format_params%s"
                    " WHERE report_format = %llu"
                    " ORDER BY %s %s;",
+                   trash ? "_trash" : "",
                    report_format,
                    sort_field ? sort_field : "ROWID",
                    ascending ? "ASC" : "DESC");
@@ -22879,8 +24236,9 @@
     init_iterator (iterator,
                    "SELECT ROWID, name, value, type, type_min, type_max,"
                    " type_regex, fallback"
-                   " FROM report_format_params"
+                   " FROM report_format_params%s"
                    " ORDER BY %s %s;",
+                   trash ? "_trash" : "",
                    sort_field ? sort_field : "ROWID",
                    ascending ? "ASC" : "DESC");
 }
@@ -23160,21 +24518,90 @@
 /**
  * @brief Delete a slave.
  *
- * @param[in]  slave  Slave.
+ * @param[in]  slave_id  UUID of slave.
+ * @param[in]  ultimate  Whether to remove entirely, or to trashcan.
  *
- * @return 0 success, 1 fail because a task refers to the slave, -1 error.
+ * @return 0 success, 1 fail because a task refers to the slave, 2 failed to
+ *         find agent, -1 error.
  */
 int
-delete_slave (slave_t slave)
+delete_slave (const char *slave_id, int ultimate)
 {
+  slave_t slave = 0;
+
   sql ("BEGIN IMMEDIATE;");
+
+  if (find_slave (slave_id, &slave))
+    {
+      sql ("ROLLBACK;");
+      return -1;
+    }
+
+  if (slave == 0)
+    {
+      if (find_trash ("slave", slave_id, &slave))
+        {
+          sql ("ROLLBACK;");
+          return -1;
+        }
+      if (slave == 0)
+        {
+          sql ("ROLLBACK;");
+          return 2;
+        }
+      if (ultimate == 0)
+        {
+          /* It's already in the trashcan. */
+          sql ("COMMIT;");
+          return 0;
+        }
+
+      /* Check if it's in use by a task in the trashcan. */
+      if (sql_int (0, 0,
+                   "SELECT count(*) FROM tasks"
+                   " WHERE slave = %llu"
+                   " AND slave_location = " G_STRINGIFY (LOCATION_TRASH) ";",
+                   slave))
+        {
+          sql ("ROLLBACK;");
+          return 1;
+        }
+
+      sql ("DELETE FROM slaves_trash WHERE ROWID = %llu;", slave);
+      sql ("COMMIT;");
+      return 0;
+    }
+
   if (sql_int (0, 0,
-               "SELECT count(*) FROM tasks WHERE slave = %llu;",
+               "SELECT count(*) FROM tasks"
+               " WHERE slave = %llu"
+               " AND slave_location = " G_STRINGIFY (LOCATION_TABLE)
+               " AND (hidden = 0 OR hidden = 1);",
                slave))
     {
       sql ("ROLLBACK;");
       return 1;
     }
+
+  if (ultimate == 0)
+    {
+      sql ("INSERT INTO slaves_trash"
+           "  (uuid, owner, name, comment, host, port, login, password)"
+           " SELECT"
+           "  uuid, owner, name, comment, host, port, login, password"
+           " FROM slaves WHERE ROWID = %llu;",
+           slave);
+
+      /* Update the location of the slave in any trashcan tasks. */
+      sql ("UPDATE tasks"
+           " SET slave = %llu,"
+           "     slave_location = " G_STRINGIFY (LOCATION_TRASH)
+           " WHERE slave = %llu"
+           " AND slave_location = " G_STRINGIFY (LOCATION_TABLE) ";",
+           sqlite3_last_insert_rowid (task_db),
+           slave);
+    }
+
   sql ("DELETE FROM slaves WHERE ROWID = %llu;", slave);
   sql ("COMMIT;");
   return 0;
@@ -23185,12 +24612,13 @@
  *
  * @param[in]  iterator    Iterator.
  * @param[in]  slave       Slave to limit iteration to.  0 for all.
+ * @param[in]  trash       Whether to iterate over trashcan report formats.
  * @param[in]  ascending   Whether to sort ascending or descending.
  * @param[in]  sort_field  Field to sort on, or NULL for "ROWID".
  */
 void
-init_slave_iterator (iterator_t* iterator, slave_t slave, int ascending,
-                     const char* sort_field)
+init_slave_iterator (iterator_t* iterator, slave_t slave, int trash,
+                     int ascending, const char* sort_field)
 {
   assert (current_credentials.uuid);
 
@@ -23198,11 +24626,12 @@
     init_iterator (iterator,
                    "SELECT ROWID, uuid, name, comment, host, port, login,"
                    " password"
-                   " FROM slaves"
+                   " FROM slaves%s"
                    " WHERE ROWID = %llu"
                    " AND ((owner IS NULL) OR (owner ="
                    " (SELECT ROWID FROM users WHERE users.uuid = '%s')))"
                    " ORDER BY %s %s;",
+                   trash ? "_trash" : "",
                    slave,
                    current_credentials.uuid,
                    sort_field ? sort_field : "ROWID",
@@ -23211,10 +24640,11 @@
     init_iterator (iterator,
                    "SELECT ROWID, uuid, name, comment, host, port, login,"
                    " password"
-                   " FROM slaves"
+                   " FROM slaves%s"
                    " WHERE ((owner IS NULL) OR (owner ="
                    " (SELECT ROWID FROM users WHERE users.uuid = '%s')))"
                    " ORDER BY %s %s;",
+                   trash ? "_trash" : "",
                    current_credentials.uuid,
                    sort_field ? sort_field : "ROWID",
                    ascending ? "ASC" : "DESC");
@@ -23430,11 +24860,31 @@
 slave_in_use (slave_t slave)
 {
   return sql_int (0, 0,
-                  "SELECT count(*) FROM tasks WHERE slave = %llu;",
+                  "SELECT count(*) FROM tasks"
+                  " WHERE slave = %llu"
+                  " AND slave_location = " G_STRINGIFY (LOCATION_TABLE)
+                  " AND (hidden = 0 OR hidden = 1);",
                   slave);
 }
 
 /**
+ * @brief Return whether a slave is referenced by a task
+ *
+ * @param[in]  slave  Slave.
+ *
+ * @return 1 if in use, else 0.
+ */
+int
+trash_slave_in_use (slave_t slave)
+{
+  return sql_int (0, 0,
+                  "SELECT count(*) FROM tasks"
+                  " WHERE slave = %llu"
+                  " AND slave_location = " G_STRINGIFY (LOCATION_TRASH),
+                  slave);
+}
+
+/**
  * @brief Initialise a slave task iterator.
  *
  * Iterates over all tasks that use the slave.
@@ -23694,6 +25144,581 @@
   }
 }
 
+
+/* Trashcan. */
 
+/**
+ * @brief Restore a resource from the trashcan.
+ *
+ * @param[in]  id  UUID of resource.
+ *
+ * @return 0 success, 1 fail because the resource refers to another resource
+ *         in the trashcan, 2 failed to find resource in trashcan, 3 fail
+ *         because resource with such name exists already, -1 error.
+ */
+int
+manage_restore (const char *id)
+{
+  resource_t resource = 0;
 
+  sql ("BEGIN IMMEDIATE;");
+
+  /* Agent. */
+
+  if (find_trash ("agent", id, &resource))
+    {
+      sql ("ROLLBACK;");
+      return -1;
+    }
+
+  if (resource)
+    {
+      if (sql_int (0, 0,
+                   "SELECT count(*) FROM agents"
+                   " WHERE name ="
+                   " (SELECT name FROM agents_trash WHERE ROWID = %llu);",
+                   resource))
+        {
+          sql ("ROLLBACK;");
+          return 3;
+        }
+
+      sql ("INSERT INTO agents"
+           " (uuid, owner, name, comment, installer, installer_64,"
+           "  installer_filename, installer_signature_64, installer_trust,"
+           "  installer_trust_time, howto_install, howto_use)"
+           " SELECT"
+           "  uuid, owner, name, comment, installer, installer_64,"
+           "  installer_filename, installer_signature_64, installer_trust,"
+           "  installer_trust_time, howto_install, howto_use"
+           " FROM agents_trash WHERE ROWID = %llu;",
+           resource);
+
+      sql ("DELETE FROM agents_trash WHERE ROWID = %llu;", resource);
+      sql ("COMMIT;");
+      return 0;
+    }
+
+  /* Config. */
+
+  if (find_trash ("config", id, &resource))
+    {
+      sql ("ROLLBACK;");
+      return -1;
+    }
+
+  if (resource)
+    {
+      config_t config;
+
+      if (sql_int (0, 0,
+                   "SELECT count(*) FROM configs"
+                   " WHERE name ="
+                   " (SELECT name FROM configs_trash WHERE ROWID = %llu);",
+                   resource))
+        {
+          sql ("ROLLBACK;");
+          return 3;
+        }
+
+      sql ("INSERT INTO configs"
+           " (uuid, owner, name, nvt_selector, comment, family_count, nvt_count,"
+           "  families_growing, nvts_growing)"
+           " SELECT uuid, owner, name, nvt_selector, comment, family_count,"
+           "        nvt_count, families_growing, nvts_growing"
+           " FROM configs_trash WHERE ROWID = %llu;",
+           resource);
+
+      config = sqlite3_last_insert_rowid (task_db);
+
+      sql ("INSERT INTO config_preferences"
+           " (config, type, name, value)"
+           " SELECT %llu, type, name, value"
+           " FROM config_preferences_trash WHERE config = %llu;",
+           config,
+           resource);
+
+      /* Update the config in any trashcan tasks. */
+      sql ("UPDATE tasks"
+           " SET config = %llu,"
+           "     config_location = " G_STRINGIFY (LOCATION_TABLE)
+           " WHERE config = %llu"
+           " AND config_location = " G_STRINGIFY (LOCATION_TRASH),
+           config,
+           resource);
+
+      sql ("DELETE FROM config_preferences_trash WHERE config = %llu;",
+           resource);
+      sql ("DELETE FROM configs_trash WHERE ROWID = %llu;", resource);
+      sql ("COMMIT;");
+      return 0;
+    }
+
+  /* Escalator. */
+
+  if (find_trash ("escalator", id, &resource))
+    {
+      sql ("ROLLBACK;");
+      return -1;
+    }
+
+  if (resource)
+    {
+      escalator_t escalator;
+
+      if (sql_int (0, 0,
+                   "SELECT count(*) FROM escalators"
+                   " WHERE name ="
+                   " (SELECT name FROM escalators_trash WHERE ROWID = %llu);",
+                   resource))
+        {
+          sql ("ROLLBACK;");
+          return 3;
+        }
+
+      sql ("INSERT INTO escalators"
+           " (uuid, owner, name, comment, event, condition, method)"
+           " SELECT uuid, owner, name, comment, event, condition, method"
+           " FROM escalators_trash WHERE ROWID = %llu;",
+           resource);
+
+      escalator = sqlite3_last_insert_rowid (task_db);
+
+      sql ("INSERT INTO escalator_condition_data"
+           " (escalator, name, data)"
+           " SELECT %llu, name, data"
+           " FROM escalator_condition_data_trash WHERE ROWID = %llu;",
+           escalator);
+
+      sql ("INSERT INTO escalator_event_data"
+           " (escalator, name, data)"
+           " SELECT %llu, name, data"
+           " FROM escalator_event_data_trash WHERE ROWID = %llu;",
+           escalator);
+
+      sql ("INSERT INTO escalator_method_data"
+           " (escalator, name, data)"
+           " SELECT %llu, name, data"
+           " FROM escalator_method_data_trash WHERE ROWID = %llu;",
+           escalator);
+
+      /* Update the escalator in any trashcan tasks. */
+      sql ("UPDATE task_escalators"
+           " SET escalator = %llu,"
+           "     escalator_location = " G_STRINGIFY (LOCATION_TABLE)
+           " WHERE escalator = %llu"
+           " AND escalator_location = " G_STRINGIFY (LOCATION_TRASH),
+           escalator,
+           resource);
+
+      sql ("DELETE FROM escalator_condition_data_trash WHERE escalator = %llu;",
+           resource);
+      sql ("DELETE FROM escalator_event_data_trash WHERE escalator = %llu;",
+           resource);
+      sql ("DELETE FROM escalator_method_data_trash WHERE escalator = %llu;",
+           resource);
+      sql ("DELETE FROM escalators_trash WHERE ROWID = %llu;",
+           resource);
+      sql ("COMMIT;");
+      return 0;
+    }
+
+  /* LSC credential. */
+
+  if (find_trash ("lsc_credential", id, &resource))
+    {
+      sql ("ROLLBACK;");
+      return -1;
+    }
+
+  if (resource)
+    {
+      lsc_credential_t credential;
+
+      if (sql_int (0, 0,
+                   "SELECT count(*) FROM lsc_credentials"
+                   " WHERE name ="
+                   " (SELECT name FROM lsc_credentials_trash WHERE ROWID = %llu);",
+                   resource))
+        {
+          sql ("ROLLBACK;");
+          return 3;
+        }
+
+      sql ("INSERT INTO lsc_credentials"
+           " (uuid, owner, name, login, password, comment, public_key,"
+           "  private_key, rpm, deb, exe)"
+           " SELECT uuid, owner, name, login, password, comment, public_key,"
+           "  private_key, rpm, deb, exe"
+           " FROM lsc_credentials_trash WHERE ROWID = %llu;",
+           resource);
+
+      /* Update the credentials in any trashcan targets. */
+      credential = sqlite3_last_insert_rowid (task_db);
+      sql ("UPDATE targets_trash"
+           " SET ssh_location = " G_STRINGIFY (LOCATION_TABLE) ","
+           "     lsc_credential = %llu"
+           " WHERE lsc_credential = %llu"
+           " AND ssh_location = " G_STRINGIFY (LOCATION_TRASH) ";",
+           credential,
+           resource);
+      sql ("UPDATE targets_trash"
+           " SET smb_location = " G_STRINGIFY (LOCATION_TABLE) ","
+           "     smb_lsc_credential = %llu"
+           " WHERE smb_lsc_credential = %llu"
+           " AND smb_location = " G_STRINGIFY (LOCATION_TRASH) ";",
+           credential,
+           resource);
+
+      sql ("DELETE FROM lsc_credentials_trash WHERE ROWID = %llu;", resource);
+      sql ("COMMIT;");
+      return 0;
+    }
+
+  /* Report format. */
+
+  if (find_trash ("report_format", id, &resource))
+    {
+      sql ("ROLLBACK;");
+      return -1;
+    }
+
+  if (resource)
+    {
+      iterator_t params;
+      report_format_t report_format;
+      gchar *dir, *trash_dir, *resource_string;
+      int global;
+      char *trash_uuid;
+
+      if (sql_int (0, 0,
+                   "SELECT count(*) FROM report_formats"
+                   " WHERE name ="
+                   " (SELECT name FROM report_formats_trash WHERE ROWID = %llu);",
+                   resource))
+        {
+          sql ("ROLLBACK;");
+          return 3;
+        }
+
+      /* Move to "real" tables. */
+
+      sql ("INSERT INTO report_formats"
+           " (uuid, owner, name, extension, content_type, summary,"
+           "  description, signature, trust, trust_time, flags)"
+           " SELECT"
+           "  original_uuid, owner, name, extension, content_type, summary,"
+           "  description, signature, trust, trust_time, flags"
+           " FROM report_formats_trash"
+           " WHERE ROWID = %llu;",
+           resource);
+
+      report_format = sqlite3_last_insert_rowid (task_db);
+
+      init_report_format_param_iterator (&params, resource, 1, 1, NULL);
+      while (next (&params))
+        {
+          report_format_param_t param, trash_param;
+
+          trash_param = report_format_param_iterator_param (&params);
+
+          sql ("INSERT INTO report_format_params"
+               " (report_format, name, type, value, type_min, type_max,"
+               "  type_regex, fallback)"
+               " SELECT"
+               "  %llu, name, type, value, type_min, type_max,"
+               "  type_regex, fallback"
+               " FROM report_format_params_trash"
+               " WHERE ROWID = %llu;",
+               report_format,
+               trash_param);
+
+          param = sqlite3_last_insert_rowid (task_db);
+
+          sql ("INSERT INTO report_format_param_options"
+               " (report_format_param, value)"
+               " SELECT %llu, value"
+               " FROM report_format_param_options_trash"
+               " WHERE report_format_param = %llu;",
+               param,
+               trash_param);
+        }
+      cleanup_iterator (&params);
+
+      global = report_format_trash_global (resource);
+
+      trash_uuid = sql_string (0, 0,
+                               "SELECT original_uuid FROM report_formats_trash"
+                               " WHERE ROWID = %llu;",
+                               resource);
+      if (trash_uuid == NULL)
+        abort ();
+
+      /* Remove from trash tables. */
+
+      sql ("DELETE FROM report_formats_trash WHERE ROWID = %llu;",
+           resource);
+      sql ("DELETE FROM report_format_param_options_trash"
+           " WHERE report_format_param"
+           " IN (SELECT ROWID from report_format_params_trash"
+           "     WHERE report_format = %llu);",
+           resource);
+      sql ("DELETE FROM report_format_params_trash WHERE report_format = %llu;",
+           resource);
+
+      /* Move the dir last, in case any SQL rolls back. */
+
+      if (global)
+        dir = g_build_filename (OPENVAS_SYSCONF_DIR,
+                                "openvasmd",
+                                "global_report_formats",
+                                trash_uuid,
+                                NULL);
+      else
+        dir = g_build_filename (OPENVAS_SYSCONF_DIR,
+                                "openvasmd",
+                                "report_formats",
+                                current_credentials.uuid,
+                                trash_uuid,
+                                NULL);
+      free (trash_uuid);
+
+      resource_string = g_strdup_printf ("%llu", resource);
+      trash_dir = g_build_filename (OPENVAS_SYSCONF_DIR,
+                                    "openvasmd",
+                                    "report_formats_trash",
+                                    resource_string,
+                                    NULL);
+      g_free (resource_string);
+      if (g_file_test (trash_dir, G_FILE_TEST_EXISTS))
+        {
+          if (rename (trash_dir, dir))
+            {
+              g_warning ("%s: rename %s to %s: %s\n",
+                         __FUNCTION__, dir, trash_dir, strerror (errno));
+              g_free (dir);
+              g_free (trash_dir);
+              sql ("ROLLBACK;");
+              return -1;
+            }
+        }
+      else
+        {
+          g_warning ("%s: report trash dir missing: %s\n",
+                     __FUNCTION__, trash_dir);
+          g_free (dir);
+          g_free (trash_dir);
+          sql ("ROLLBACK;");
+          return -1;
+        }
+      g_free (dir);
+      g_free (trash_dir);
+
+      sql ("COMMIT;");
+      return 0;
+    }
+
+  /* Schedule. */
+
+  if (find_trash ("schedule", id, &resource))
+    {
+      sql ("ROLLBACK;");
+      return -1;
+    }
+
+  if (resource)
+    {
+      if (sql_int (0, 0,
+                   "SELECT count(*) FROM schedules"
+                   " WHERE name ="
+                   " (SELECT name FROM schedules_trash WHERE ROWID = %llu);",
+                   resource))
+        {
+          sql ("ROLLBACK;");
+          return 3;
+        }
+
+      sql ("INSERT INTO schedules"
+           " (uuid, owner, name, comment, first_time, period, period_months,"
+           "  duration)"
+           " SELECT uuid, owner, name, comment, first_time, period,"
+           "        period_months, duration"
+           " FROM schedules_trash WHERE ROWID = %llu;",
+           resource);
+
+      /* Update the schedule in any trashcan tasks. */
+      sql ("UPDATE tasks"
+           " SET schedule = %llu,"
+           "     schedule_location = " G_STRINGIFY (LOCATION_TABLE)
+           " WHERE schedule = %llu"
+           " AND schedule_location = " G_STRINGIFY (LOCATION_TRASH),
+           sqlite3_last_insert_rowid (task_db),
+           resource);
+
+      sql ("DELETE FROM schedules_trash WHERE ROWID = %llu;", resource);
+      sql ("COMMIT;");
+      return 0;
+    }
+
+  /* Slave. */
+
+  if (find_trash ("slave", id, &resource))
+    {
+      sql ("ROLLBACK;");
+      return -1;
+    }
+
+  if (resource)
+    {
+      if (sql_int (0, 0,
+                   "SELECT count(*) FROM slaves"
+                   " WHERE name ="
+                   " (SELECT name FROM slaves_trash WHERE ROWID = %llu);",
+                   resource))
+        {
+          sql ("ROLLBACK;");
+          return 3;
+        }
+
+      sql ("INSERT INTO slaves"
+           "  (uuid, owner, name, comment, host, port, login, password)"
+           " SELECT"
+           "  uuid, owner, name, comment, host, port, login, password"
+           " FROM slaves_trash WHERE ROWID = %llu;",
+           resource);
+
+      /* Update the slave in any trashcan tasks. */
+      sql ("UPDATE tasks"
+           " SET slave = %llu,"
+           "     slave_location = " G_STRINGIFY (LOCATION_TABLE)
+           " WHERE slave = %llu"
+           " AND slave_location = " G_STRINGIFY (LOCATION_TRASH),
+           sqlite3_last_insert_rowid (task_db),
+           resource);
+
+      sql ("DELETE FROM slaves_trash WHERE ROWID = %llu;", resource);
+      sql ("COMMIT;");
+      return 0;
+    }
+
+  /* Target. */
+
+  if (find_trash ("target", id, &resource))
+    {
+      sql ("ROLLBACK;");
+      return -1;
+    }
+
+  if (resource)
+    {
+      if (sql_int (0, 0,
+                   "SELECT count(*) FROM targets"
+                   " WHERE name ="
+                   " (SELECT name FROM targets_trash WHERE ROWID = %llu);",
+                   resource))
+        {
+          sql ("ROLLBACK;");
+          return 3;
+        }
+
+      /* Check if it's in use by a credential in the trashcan. */
+      if (sql_int (0, 0,
+                   "SELECT ssh_location = " G_STRINGIFY (LOCATION_TRASH)
+                   " OR smb_location = " G_STRINGIFY (LOCATION_TRASH)
+                   " FROM targets_trash WHERE ROWID = %llu;",
+                   resource))
+        {
+          sql ("ROLLBACK;");
+          return 1;
+        }
+
+      sql ("INSERT INTO targets"
+           " (uuid, owner, name, hosts, comment, lsc_credential,"
+           "  smb_lsc_credential)"
+           " SELECT uuid, owner, name, hosts, comment, lsc_credential,"
+           "        smb_lsc_credential"
+           " FROM targets_trash WHERE ROWID = %llu;",
+           resource);
+
+      /* Update the target in any trashcan tasks. */
+      sql ("UPDATE tasks"
+           " SET target = %llu,"
+           "     target_location = " G_STRINGIFY (LOCATION_TABLE)
+           " WHERE target = %llu"
+           " AND target_location = " G_STRINGIFY (LOCATION_TRASH),
+           sqlite3_last_insert_rowid (task_db),
+           resource);
+
+      sql ("DELETE FROM targets_trash WHERE ROWID = %llu;", resource);
+      sql ("COMMIT;");
+      return 0;
+    }
+
+  /* Task. */
+
+  if (find_trash_task (id, &resource))
+    {
+      sql ("ROLLBACK;");
+      return -1;
+    }
+
+  if (resource)
+    {
+      /* Check if it's in use by a resource in the trashcan. */
+      if (sql_int (0, 0,
+                   "SELECT (target_location = " G_STRINGIFY (LOCATION_TRASH) ")"
+                   " OR (config_location = " G_STRINGIFY (LOCATION_TRASH) ")"
+                   " OR (schedule_location = " G_STRINGIFY (LOCATION_TRASH) ")"
+                   " OR (slave_location = " G_STRINGIFY (LOCATION_TRASH) ")"
+                   " OR (SELECT count(*) > 0 FROM task_escalators"
+                   "     WHERE task = tasks.ROWID"
+                   "     AND escalator_location = " G_STRINGIFY (LOCATION_TRASH) ")"
+                   " FROM tasks WHERE ROWID = %llu;",
+                   resource))
+        {
+          sql ("ROLLBACK;");
+          return 1;
+        }
+
+      sql ("UPDATE tasks SET hidden = 0 WHERE ROWID = %llu;", resource);
+      sql ("COMMIT;");
+      return 0;
+    }
+
+  sql ("ROLLBACK;");
+  return 2;
+}
+
+/**
+ * @brief Empty the trashcan.
+ *
+ * @return 0 success, -1 error.
+ */
+int
+manage_empty_trashcan ()
+{
+  sql ("BEGIN IMMEDIATE;");
+  sql ("DELETE FROM agents_trash;");
+  sql ("DELETE FROM nvt_selectors WHERE name IN"
+       " (SELECT nvt_selector FROM configs_trash);");
+  sql ("DELETE FROM config_preferences_trash;");
+  sql ("DELETE FROM configs_trash;");
+  sql ("DELETE FROM escalator_condition_data_trash;");
+  sql ("DELETE FROM escalator_event_data_trash;");
+  sql ("DELETE FROM escalator_method_data_trash;");
+  sql ("DELETE FROM escalators_trash;");
+  sql ("DELETE FROM lsc_credentials_trash;");
+  sql ("DELETE FROM report_formats_trash;");
+  sql ("DELETE FROM schedules_trash;");
+  sql ("DELETE FROM slaves_trash;");
+  sql ("DELETE FROM targets_trash;");
+  if (delete_trash_tasks ())
+    {
+      sql ("ROLLBACK;");
+      return -1;
+    }
+  sql ("COMMIT;");
+  return 0;
+}
+
 #undef DEF_ACCESS

Modified: trunk/openvas-manager/src/omp.c
===================================================================
--- trunk/openvas-manager/src/omp.c	2011-03-11 17:52:55 UTC (rev 10531)
+++ trunk/openvas-manager/src/omp.c	2011-03-14 05:49:56 UTC (rev 10532)
@@ -393,6 +393,7 @@
 "    DELETE_SLAVE           Delete a slave.\n"
 "    DELETE_TARGET          Delete a target.\n"
 "    DELETE_TASK            Delete a task.\n"
+"    EMPTY_TRASHCAN         Empty the trashcan.\n"
 "    GET_AGENTS             Get all agents.\n"
 #if 0
 "    GET_CERTIFICATES       Get all available certificates.\n"
@@ -426,6 +427,7 @@
 "    MODIFY_REPORT_FORMAT   Modify an existing report format.\n"
 "    MODIFY_TASK            Update an existing task.\n"
 "    PAUSE_TASK             Pause a running task.\n"
+"    RESTORE                Restore a resource.\n"
 "    RESUME_OR_START_TASK   Resume task if stopped, else start task.\n"
 "    RESUME_PAUSED_TASK     Resume a paused task.\n"
 "    RESUME_STOPPED_TASK    Resume a stopped task.\n"
@@ -1120,6 +1122,7 @@
 typedef struct
 {
   char *agent_id;   ///< ID of agent to delete.
+  int ultimate;     ///< Boolean.  Whether to remove entirely or to trashcan.
 } delete_agent_data_t;
 
 /**
@@ -1141,6 +1144,7 @@
 typedef struct
 {
   char *config_id;   ///< ID of config to delete.
+  int ultimate;      ///< Boolean.  Whether to remove entirely or to trashcan.
 } delete_config_data_t;
 
 /**
@@ -1162,6 +1166,7 @@
 typedef struct
 {
   char *escalator_id;   ///< ID of escalator to delete.
+  int ultimate;     ///< Boolean.  Whether to remove entirely or to trashcan.
 } delete_escalator_data_t;
 
 /**
@@ -1183,6 +1188,7 @@
 typedef struct
 {
   char *lsc_credential_id;   ///< ID of LSC credential to delete.
+  int ultimate;      ///< Boolean.  Whether to remove entirely or to trashcan.
 } delete_lsc_credential_data_t;
 
 /**
@@ -1267,6 +1273,7 @@
 typedef struct
 {
   char *report_format_id;   ///< ID of report format to delete.
+  int ultimate;     ///< Boolean.  Whether to remove entirely or to trashcan.
 } delete_report_format_data_t;
 
 /**
@@ -1288,6 +1295,7 @@
 typedef struct
 {
   char *schedule_id;   ///< ID of schedule to delete.
+  int ultimate;        ///< Boolean.  Whether to remove entirely or to trashcan.
 } delete_schedule_data_t;
 
 /**
@@ -1309,6 +1317,7 @@
 typedef struct
 {
   char *slave_id;   ///< ID of slave to delete.
+  int ultimate;     ///< Boolean.  Whether to remove entirely or to trashcan.
 } delete_slave_data_t;
 
 /**
@@ -1330,6 +1339,7 @@
 typedef struct
 {
   char *target_id;   ///< ID of target to delete.
+  int ultimate;      ///< Boolean.  Whether to remove entirely or to trashcan.
 } delete_target_data_t;
 
 /**
@@ -1351,6 +1361,7 @@
 typedef struct
 {
   char *task_id;   ///< ID of task to delete.
+  int ultimate;    ///< Boolean.  Whether to remove entirely or to trashcan.
 } delete_task_data_t;
 
 /**
@@ -1375,6 +1386,7 @@
   char *format;          ///< Format requested: "installer", "howto_use", ....
   char *sort_field;      ///< Field to sort results on.
   int sort_order;        ///< Result sort order: 0 descending, else ascending.
+  int trash;             ///< Boolean.  Whether to return agents from trashcan.
 } get_agents_data_t;
 
 /**
@@ -1403,6 +1415,7 @@
   int preferences;       ///< Boolean.  Whether to include config preferences.
   char *sort_field;      ///< Field to sort results on.
   int sort_order;        ///< Result sort order: 0 descending, else ascending.
+  int trash;             ///< Boolean.  Whether to return configs from trashcan.
 } get_configs_data_t;
 
 /**
@@ -1448,6 +1461,7 @@
   char *escalator_id;    ///< ID of single escalator to get.
   char *sort_field;      ///< Field to sort results on.
   int sort_order;        ///< Result sort order: 0 descending, else ascending.
+  int trash;             ///< Boolean.  Whether to return escalators from trashcan.
 } get_escalators_data_t;
 
 /**
@@ -1473,6 +1487,7 @@
   char *lsc_credential_id; ///< Single LSC credential to iterate over.
   char *sort_field;        ///< Field to sort results on.
   int sort_order;          ///< Result sort order: 0 descending, else ascending.
+  int trash;         ///< Boolean.  Whether to return agents from trashcan.
 } get_lsc_credentials_data_t;
 
 /**
@@ -1695,6 +1710,7 @@
   char *sort_field;       ///< Field to sort results on.
   int sort_order;         ///< Result sort order: 0 descending, else ascending.
   char *report_format_id; ///< ID of single report format to get.
+  int trash;              ///< Boolean.  Whether to return agents from trashcan.
 } get_report_formats_data_t;
 
 /**
@@ -1744,10 +1760,11 @@
  */
 typedef struct
 {
-  char *schedule_id;     ///< ID of single schedule to get.
-  char *sort_field;      ///< Field to sort results on.
-  int sort_order;        ///< Result sort order: 0 descending, else ascending.
-  int details;           ///< Boolean.  Whether to include full details.
+  char *schedule_id;   ///< ID of single schedule to get.
+  char *sort_field;    ///< Field to sort results on.
+  int sort_order;      ///< Result sort order: 0 descending, else ascending.
+  int details;         ///< Boolean.  Whether to include full details.
+  int trash;           ///< Boolean.  Whether to return schedules from trashcan.
 } get_schedules_data_t;
 
 /**
@@ -1772,6 +1789,7 @@
   int sort_order;      ///< Result sort order: 0 descending, else ascending.
   char *slave_id;      ///< ID of single slave to get.
   int tasks;           ///< Boolean.  Whether to include tasks that use slave.
+  int trash;           ///< Boolean.  Whether to return agents from trashcan.
 } get_slaves_data_t;
 
 /**
@@ -1823,6 +1841,7 @@
   int sort_order;      ///< Result sort order: 0 descending, else ascending.
   char *target_id;     ///< ID of single target to get.
   int tasks;           ///< Boolean.  Whether to include tasks that use target.
+  int trash;           ///< Boolean.  Whether to return targets from trashcan.
 } get_targets_data_t;
 
 /**
@@ -1874,6 +1893,7 @@
   int rcfile;            ///< Boolean.  Whether to include RC defining task.
   char *sort_field;      ///< Field to sort results on.
   int sort_order;        ///< Result sort order: 0 descending, else ascending.
+  int trash;             ///< Boolean.  Whether to return tasks from trashcan.
 } get_tasks_data_t;
 
 /**
@@ -2153,6 +2173,27 @@
 }
 
 /**
+ * @brief Command data for the restore command.
+ */
+typedef struct
+{
+  char *id;   ///< ID of resource to pause.
+} restore_data_t;
+
+/**
+ * @brief Reset command data.
+ *
+ * @param[in]  data  Command data.
+ */
+static void
+restore_data_reset (restore_data_t *data)
+{
+  free (data->id);
+
+  memset (data, 0, sizeof (restore_data_t));
+}
+
+/**
  * @brief Command data for the resume_or_start_task command.
  */
 typedef struct
@@ -2377,6 +2418,7 @@
   modify_report_format_data_t modify_report_format;   ///< modify_report_format
   modify_task_data_t modify_task;                     ///< modify_task
   pause_task_data_t pause_task;                       ///< pause_task
+  restore_data_t restore;                             ///< restore
   resume_or_start_task_data_t resume_or_start_task;   ///< resume_or_start_task
   resume_paused_task_data_t resume_paused_task;       ///< resume_paused_task
   resume_stopped_task_data_t resume_stopped_task;     ///< resume_stopped_task
@@ -2717,6 +2759,12 @@
  = (pause_task_data_t*) &(command_data.pause_task);
 
 /**
+ * @brief Parser callback data for RESTORE.
+ */
+restore_data_t *restore_data
+ = (restore_data_t*) &(command_data.restore);
+
+/**
  * @brief Parser callback data for RESUME_OR_START_TASK.
  */
 resume_or_start_task_data_t *resume_or_start_task_data
@@ -2957,6 +3005,7 @@
   CLIENT_DELETE_SLAVE,
   CLIENT_DELETE_TASK,
   CLIENT_DELETE_TARGET,
+  CLIENT_EMPTY_TRASHCAN,
   CLIENT_GET_AGENTS,
 #if 0
   CLIENT_GET_CERTIFICATES,
@@ -3035,6 +3084,7 @@
   CLIENT_MODIFY_TASK_SCHEDULE,
   CLIENT_MODIFY_TASK_SLAVE,
   CLIENT_PAUSE_TASK,
+  CLIENT_RESTORE,
   CLIENT_RESUME_OR_START_TASK,
   CLIENT_RESUME_PAUSED_TASK,
   CLIENT_RESUME_STOPPED_TASK,
@@ -3574,28 +3624,53 @@
           }
         else if (strcasecmp ("DELETE_AGENT", element_name) == 0)
           {
+            const gchar* attribute;
             append_attribute (attribute_names, attribute_values,
                               "agent_id", &delete_agent_data->agent_id);
+            if (find_attribute (attribute_names, attribute_values,
+                                "ultimate", &attribute))
+              delete_agent_data->ultimate = strcmp (attribute, "ultimate");
+            else
+              delete_agent_data->ultimate = 0;
             set_client_state (CLIENT_DELETE_AGENT);
           }
         else if (strcasecmp ("DELETE_CONFIG", element_name) == 0)
           {
+            const gchar* attribute;
             append_attribute (attribute_names, attribute_values,
                               "config_id", &delete_config_data->config_id);
+            if (find_attribute (attribute_names, attribute_values,
+                                "ultimate", &attribute))
+              delete_config_data->ultimate = strcmp (attribute, "ultimate");
+            else
+              delete_config_data->ultimate = 0;
             set_client_state (CLIENT_DELETE_CONFIG);
           }
         else if (strcasecmp ("DELETE_ESCALATOR", element_name) == 0)
           {
+            const gchar* attribute;
             append_attribute (attribute_names, attribute_values,
                               "escalator_id",
                               &delete_escalator_data->escalator_id);
+            if (find_attribute (attribute_names, attribute_values,
+                                "ultimate", &attribute))
+              delete_escalator_data->ultimate = strcmp (attribute, "ultimate");
+            else
+              delete_escalator_data->ultimate = 0;
             set_client_state (CLIENT_DELETE_ESCALATOR);
           }
         else if (strcasecmp ("DELETE_LSC_CREDENTIAL", element_name) == 0)
           {
+            const gchar* attribute;
             append_attribute (attribute_names, attribute_values,
                               "lsc_credential_id",
                               &delete_lsc_credential_data->lsc_credential_id);
+            if (find_attribute (attribute_names, attribute_values,
+                                "ultimate", &attribute))
+              delete_lsc_credential_data->ultimate
+               = strcmp (attribute, "ultimate");
+            else
+              delete_lsc_credential_data->ultimate = 0;
             set_client_state (CLIENT_DELETE_LSC_CREDENTIAL);
           }
         else if (strcasecmp ("DELETE_NOTE", element_name) == 0)
@@ -3618,34 +3693,67 @@
           }
         else if (strcasecmp ("DELETE_REPORT_FORMAT", element_name) == 0)
           {
+            const gchar* attribute;
             append_attribute (attribute_names, attribute_values, "report_format_id",
                               &delete_report_format_data->report_format_id);
+            if (find_attribute (attribute_names, attribute_values,
+                                "ultimate", &attribute))
+              delete_report_format_data->ultimate = strcmp (attribute,
+                                                            "ultimate");
+            else
+              delete_report_format_data->ultimate = 0;
             set_client_state (CLIENT_DELETE_REPORT_FORMAT);
           }
         else if (strcasecmp ("DELETE_SCHEDULE", element_name) == 0)
           {
+            const gchar* attribute;
             append_attribute (attribute_names, attribute_values, "schedule_id",
                               &delete_schedule_data->schedule_id);
+            if (find_attribute (attribute_names, attribute_values,
+                                "ultimate", &attribute))
+              delete_schedule_data->ultimate = strcmp (attribute, "ultimate");
+            else
+              delete_schedule_data->ultimate = 0;
             set_client_state (CLIENT_DELETE_SCHEDULE);
           }
         else if (strcasecmp ("DELETE_SLAVE", element_name) == 0)
           {
+            const gchar* attribute;
             append_attribute (attribute_names, attribute_values, "slave_id",
                               &delete_slave_data->slave_id);
+            if (find_attribute (attribute_names, attribute_values,
+                                "ultimate", &attribute))
+              delete_slave_data->ultimate = strcmp (attribute, "ultimate");
+            else
+              delete_slave_data->ultimate = 0;
             set_client_state (CLIENT_DELETE_SLAVE);
           }
         else if (strcasecmp ("DELETE_TARGET", element_name) == 0)
           {
+            const gchar* attribute;
             append_attribute (attribute_names, attribute_values, "target_id",
                               &delete_target_data->target_id);
+            if (find_attribute (attribute_names, attribute_values,
+                                "ultimate", &attribute))
+              delete_target_data->ultimate = strcmp (attribute, "ultimate");
+            else
+              delete_target_data->ultimate = 0;
             set_client_state (CLIENT_DELETE_TARGET);
           }
         else if (strcasecmp ("DELETE_TASK", element_name) == 0)
           {
+            const gchar* attribute;
             append_attribute (attribute_names, attribute_values, "task_id",
                               &delete_task_data->task_id);
+            if (find_attribute (attribute_names, attribute_values,
+                                "ultimate", &attribute))
+              delete_task_data->ultimate = strcmp (attribute, "ultimate");
+            else
+              delete_task_data->ultimate = 0;
             set_client_state (CLIENT_DELETE_TASK);
           }
+        else if (strcasecmp ("EMPTY_TRASHCAN", element_name) == 0)
+          set_client_state (CLIENT_EMPTY_TRASHCAN);
         else if (strcasecmp ("GET_AGENTS", element_name) == 0)
           {
             const gchar* attribute;
@@ -3656,6 +3764,11 @@
             append_attribute (attribute_names, attribute_values, "sort_field",
                               &get_agents_data->sort_field);
             if (find_attribute (attribute_names, attribute_values,
+                                "trash", &attribute))
+              get_agents_data->trash = strcmp (attribute, "0");
+            else
+              get_agents_data->trash = 0;
+            if (find_attribute (attribute_names, attribute_values,
                                 "sort_order", &attribute))
               get_agents_data->sort_order = strcmp (attribute, "descending");
             else
@@ -3676,6 +3789,11 @@
               get_configs_data->families = atoi (attribute);
             else
               get_configs_data->families = 0;
+            if (find_attribute (attribute_names, attribute_values,
+                                "trash", &attribute))
+              get_configs_data->trash = strcmp (attribute, "0");
+            else
+              get_configs_data->trash = 0;
             append_attribute (attribute_names, attribute_values, "sort_field",
                               &get_configs_data->sort_field);
             if (find_attribute (attribute_names, attribute_values,
@@ -3707,6 +3825,11 @@
             append_attribute (attribute_names, attribute_values,
                               "escalator_id",
                               &get_escalators_data->escalator_id);
+            if (find_attribute (attribute_names, attribute_values,
+                                "trash", &attribute))
+              get_escalators_data->trash = strcmp (attribute, "0");
+            else
+              get_escalators_data->trash = 0;
             append_attribute (attribute_names, attribute_values, "sort_field",
                               &get_escalators_data->sort_field);
             if (find_attribute (attribute_names, attribute_values,
@@ -3725,6 +3848,11 @@
                               &get_lsc_credentials_data->lsc_credential_id);
             append_attribute (attribute_names, attribute_values, "format",
                               &get_lsc_credentials_data->format);
+            if (find_attribute (attribute_names, attribute_values,
+                                "trash", &attribute))
+              get_lsc_credentials_data->trash = strcmp (attribute, "0");
+            else
+              get_lsc_credentials_data->trash = 0;
             append_attribute (attribute_names, attribute_values, "sort_field",
                               &get_lsc_credentials_data->sort_field);
             if (find_attribute (attribute_names, attribute_values,
@@ -3981,6 +4109,12 @@
             else
               get_report_formats_data->params = 0;
 
+            if (find_attribute (attribute_names, attribute_values,
+                                "trash", &attribute))
+              get_report_formats_data->trash = strcmp (attribute, "0");
+            else
+              get_report_formats_data->trash = 0;
+
             append_attribute (attribute_names, attribute_values, "sort_field",
                               &get_report_formats_data->sort_field);
 
@@ -4048,6 +4182,12 @@
             else
               get_schedules_data->details = 0;
 
+            if (find_attribute (attribute_names, attribute_values,
+                                "trash", &attribute))
+              get_schedules_data->trash = strcmp (attribute, "0");
+            else
+              get_schedules_data->trash = 0;
+
             append_attribute (attribute_names, attribute_values, "sort_field",
                               &get_schedules_data->sort_field);
 
@@ -4069,6 +4209,11 @@
               get_slaves_data->tasks = strcmp (attribute, "0");
             else
               get_slaves_data->tasks = 0;
+            if (find_attribute (attribute_names, attribute_values,
+                                "trash", &attribute))
+              get_slaves_data->trash = strcmp (attribute, "0");
+            else
+              get_slaves_data->trash = 0;
             append_attribute (attribute_names, attribute_values, "sort_field",
                               &get_slaves_data->sort_field);
             if (find_attribute (attribute_names, attribute_values,
@@ -4108,6 +4253,11 @@
               get_targets_data->tasks = strcmp (attribute, "0");
             else
               get_targets_data->tasks = 0;
+            if (find_attribute (attribute_names, attribute_values,
+                                "trash", &attribute))
+              get_targets_data->trash = strcmp (attribute, "0");
+            else
+              get_targets_data->trash = 0;
             append_attribute (attribute_names, attribute_values, "sort_field",
                               &get_targets_data->sort_field);
             if (find_attribute (attribute_names, attribute_values,
@@ -4146,6 +4296,12 @@
                               &get_tasks_data->sort_field);
 
             if (find_attribute (attribute_names, attribute_values,
+                                "trash", &attribute))
+              get_tasks_data->trash = strcmp (attribute, "0");
+            else
+              get_tasks_data->trash = 0;
+
+            if (find_attribute (attribute_names, attribute_values,
                                 "sort_order", &attribute))
               get_tasks_data->sort_order = strcmp (attribute, "descending");
             else
@@ -4211,6 +4367,12 @@
                               &pause_task_data->task_id);
             set_client_state (CLIENT_PAUSE_TASK);
           }
+        else if (strcasecmp ("RESTORE", element_name) == 0)
+          {
+            append_attribute (attribute_names, attribute_values, "id",
+                              &restore_data->id);
+            set_client_state (CLIENT_RESTORE);
+          }
         else if (strcasecmp ("RESUME_OR_START_TASK", element_name) == 0)
           {
             append_attribute (attribute_names, attribute_values, "task_id",
@@ -6436,6 +6598,21 @@
           }
         break;
 
+      case CLIENT_EMPTY_TRASHCAN:
+        if (send_element_error_to_client ("empty_trashcan", element_name,
+                                          write_to_client,
+                                          write_to_client_data))
+          {
+            error_send_to_client (error);
+            return;
+          }
+        set_client_state (CLIENT_AUTHENTIC);
+        g_set_error (error,
+                     G_MARKUP_ERROR,
+                     G_MARKUP_ERROR_UNKNOWN_ELEMENT,
+                     "Error");
+        break;
+
       case CLIENT_MODIFY_NOTE:
         if (strcasecmp ("HOSTS", element_name) == 0)
           set_client_state (CLIENT_MODIFY_NOTE_HOSTS);
@@ -6571,6 +6748,21 @@
                      "Error");
         break;
 
+      case CLIENT_RESTORE:
+        if (send_element_error_to_client ("restore", element_name,
+                                          write_to_client,
+                                          write_to_client_data))
+          {
+            error_send_to_client (error);
+            return;
+          }
+        set_client_state (CLIENT_AUTHENTIC);
+        g_set_error (error,
+                     G_MARKUP_ERROR,
+                     G_MARKUP_ERROR_UNKNOWN_ELEMENT,
+                     "Error");
+        break;
+
       case CLIENT_RESUME_OR_START_TASK:
         if (send_element_error_to_client ("resume_or_start_task", element_name,
                                           write_to_client,
@@ -8438,26 +8630,10 @@
         assert (strcasecmp ("DELETE_REPORT_FORMAT", element_name) == 0);
         if (delete_report_format_data->report_format_id)
           {
-            report_format_t report_format;
-
-            if (find_report_format (delete_report_format_data->report_format_id,
-                                    &report_format))
-              SEND_TO_CLIENT_OR_FAIL (XML_INTERNAL_ERROR ("delete_report_format"));
-            else if (report_format == 0)
+            switch (delete_report_format
+                     (delete_report_format_data->report_format_id,
+                      delete_report_format_data->ultimate))
               {
-                if (send_find_error_to_client
-                     ("delete_report_format",
-                      "report format",
-                      delete_report_format_data->report_format_id,
-                      write_to_client,
-                      write_to_client_data))
-                  {
-                    error_send_to_client (error);
-                    return;
-                  }
-              }
-            else switch (delete_report_format (report_format))
-              {
                 case 0:
                   SEND_TO_CLIENT_OR_FAIL (XML_OK ("delete_report_format"));
                   break;
@@ -8467,6 +8643,18 @@
                                       "Attempt to delete a hidden report"
                                       " format"));
                   break;
+                case 2:
+                  if (send_find_error_to_client
+                       ("delete_report_format",
+                        "report format",
+                        delete_report_format_data->report_format_id,
+                        write_to_client,
+                        write_to_client_data))
+                    {
+                      error_send_to_client (error);
+                      return;
+                    }
+                  break;
                 default:
                   SEND_TO_CLIENT_OR_FAIL
                    (XML_INTERNAL_ERROR ("delete_report_format"));
@@ -8486,25 +8674,9 @@
         assert (strcasecmp ("DELETE_SCHEDULE", element_name) == 0);
         if (delete_schedule_data->schedule_id)
           {
-            schedule_t schedule;
-
-            if (find_schedule (delete_schedule_data->schedule_id, &schedule))
-              SEND_TO_CLIENT_OR_FAIL (XML_INTERNAL_ERROR ("delete_schedule"));
-            else if (schedule == 0)
+            switch (delete_schedule (delete_schedule_data->schedule_id,
+                                     delete_schedule_data->ultimate))
               {
-                if (send_find_error_to_client
-                     ("delete_schedule",
-                      "schedule",
-                      delete_schedule_data->schedule_id,
-                      write_to_client,
-                      write_to_client_data))
-                  {
-                    error_send_to_client (error);
-                    return;
-                  }
-              }
-            else switch (delete_schedule (schedule))
-              {
                 case 0:
                   SEND_TO_CLIENT_OR_FAIL (XML_OK ("delete_schedule"));
                   g_log ("event schedule", G_LOG_LEVEL_MESSAGE,
@@ -8519,6 +8691,21 @@
                          "Schedule %s could not be deleted",
                          delete_schedule_data->schedule_id);
                   break;
+                case 2:
+                  if (send_find_error_to_client
+                       ("delete_schedule",
+                        "schedule",
+                        delete_schedule_data->schedule_id,
+                        write_to_client,
+                        write_to_client_data))
+                    {
+                      error_send_to_client (error);
+                      return;
+                    }
+                  g_log ("event schedule", G_LOG_LEVEL_MESSAGE,
+                         "Schedule %s could not be deleted",
+                         delete_schedule_data->schedule_id);
+                  break;
                 default:
                   SEND_TO_CLIENT_OR_FAIL
                    (XML_INTERNAL_ERROR ("delete_schedule"));
@@ -8729,6 +8916,7 @@
                                       " status_text=\"" STATUS_OK_TEXT "\">");
               init_report_format_iterator (&report_formats,
                                            report_format,
+                                           get_report_formats_data->trash,
                                            get_report_formats_data->sort_order,
                                            get_report_formats_data->sort_field);
               while (next (&report_formats))
@@ -8760,7 +8948,9 @@
                       iterator_t params;
                       init_report_format_param_iterator
                        (&params,
+                        // FIX ->trash
                         report_format_iterator_report_format (&report_formats),
+                        0,
                         1,
                         NULL);
                       while (next (&params))
@@ -9019,6 +9209,7 @@
 
               init_schedule_iterator (&schedules,
                                       schedule,
+                                      get_schedules_data->trash,
                                       get_schedules_data->sort_order,
                                       get_schedules_data->sort_field);
               buffer_schedules_xml (buffer, &schedules, get_schedules_data->details
@@ -9040,24 +9231,9 @@
         assert (strcasecmp ("DELETE_AGENT", element_name) == 0);
         if (delete_agent_data->agent_id)
           {
-            agent_t agent;
-
-            if (find_agent (delete_agent_data->agent_id, &agent))
-              SEND_TO_CLIENT_OR_FAIL (XML_INTERNAL_ERROR ("delete_agent"));
-            else if (agent == 0)
+            switch (delete_agent (delete_agent_data->agent_id,
+                                  delete_agent_data->ultimate))
               {
-                if (send_find_error_to_client ("delete_agent",
-                                               "agent",
-                                               delete_agent_data->agent_id,
-                                               write_to_client,
-                                               write_to_client_data))
-                  {
-                    error_send_to_client (error);
-                    return;
-                  }
-              }
-            else switch (delete_agent (agent))
-              {
                 case 0:
                   SEND_TO_CLIENT_OR_FAIL (XML_OK ("delete_agent"));
                   break;
@@ -9066,6 +9242,17 @@
                    (XML_ERROR_SYNTAX ("delete_agent",
                                       "Agent is in use"));
                   break;
+                case 2:
+                  if (send_find_error_to_client ("delete_agent",
+                                                 "agent",
+                                                 delete_agent_data->agent_id,
+                                                 write_to_client,
+                                                 write_to_client_data))
+                    {
+                      error_send_to_client (error);
+                      return;
+                    }
+                  break;
                 default:
                   SEND_TO_CLIENT_OR_FAIL
                    (XML_INTERNAL_ERROR ("delete_agent"));
@@ -9084,24 +9271,9 @@
         assert (strcasecmp ("DELETE_CONFIG", element_name) == 0);
         if (delete_config_data->config_id)
           {
-            config_t config = 0;
-
-            if (find_config (delete_config_data->config_id, &config))
-              SEND_TO_CLIENT_OR_FAIL (XML_INTERNAL_ERROR ("delete_config"));
-            else if (config == 0)
+            switch (delete_config (delete_config_data->config_id,
+                                   delete_config_data->ultimate))
               {
-                if (send_find_error_to_client ("delete_config",
-                                               "config",
-                                               delete_config_data->config_id,
-                                               write_to_client,
-                                               write_to_client_data))
-                  {
-                    error_send_to_client (error);
-                    return;
-                  }
-              }
-            else switch (delete_config (config))
-              {
                 case 0:
                   SEND_TO_CLIENT_OR_FAIL (XML_OK ("delete_config"));
                   g_log ("event config", G_LOG_LEVEL_MESSAGE,
@@ -9115,6 +9287,20 @@
                          "Scan config %s could not be deleted",
                          delete_config_data->config_id);
                   break;
+                case 2:
+                  if (send_find_error_to_client ("delete_config",
+                                                 "config",
+                                                 delete_config_data->config_id,
+                                                 write_to_client,
+                                                 write_to_client_data))
+                    {
+                      error_send_to_client (error);
+                      return;
+                    }
+                  g_log ("event config", G_LOG_LEVEL_MESSAGE,
+                         "Scan config %s could not be deleted",
+                         delete_config_data->config_id);
+                  break;
                 default:
                   SEND_TO_CLIENT_OR_FAIL (XML_INTERNAL_ERROR ("delete_config"));
                   g_log ("event config", G_LOG_LEVEL_MESSAGE,
@@ -9134,26 +9320,9 @@
         assert (strcasecmp ("DELETE_ESCALATOR", element_name) == 0);
         if (delete_escalator_data->escalator_id)
           {
-            escalator_t escalator;
-
-            if (find_escalator (delete_escalator_data->escalator_id,
-                                &escalator))
-              SEND_TO_CLIENT_OR_FAIL (XML_INTERNAL_ERROR ("delete_escalator"));
-            else if (escalator == 0)
+            switch (delete_escalator (delete_escalator_data->escalator_id,
+                                      delete_escalator_data->ultimate))
               {
-                if (send_find_error_to_client
-                     ("delete_escalator",
-                      "escalator",
-                      delete_escalator_data->escalator_id,
-                      write_to_client,
-                      write_to_client_data))
-                  {
-                    error_send_to_client (error);
-                    return;
-                  }
-              }
-            else switch (delete_escalator (escalator))
-              {
                 case 0:
                   SEND_TO_CLIENT_OR_FAIL (XML_OK ("delete_escalator"));
                   g_log ("event escalator", G_LOG_LEVEL_MESSAGE,
@@ -9168,6 +9337,21 @@
                          "Escalator %s could not be deleted",
                          delete_escalator_data->escalator_id);
                   break;
+                case 2:
+                  if (send_find_error_to_client
+                       ("delete_escalator",
+                        "escalator",
+                        delete_escalator_data->escalator_id,
+                        write_to_client,
+                        write_to_client_data))
+                    {
+                      error_send_to_client (error);
+                      return;
+                    }
+                  g_log ("event escalator", G_LOG_LEVEL_MESSAGE,
+                         "Escalator %s could not be deleted",
+                         delete_escalator_data->escalator_id);
+                  break;
                 default:
                   SEND_TO_CLIENT_OR_FAIL
                    (XML_INTERNAL_ERROR ("delete_escalator"));
@@ -9188,16 +9372,19 @@
       case CLIENT_DELETE_LSC_CREDENTIAL:
         assert (strcasecmp ("DELETE_LSC_CREDENTIAL", element_name) == 0);
         if (delete_lsc_credential_data->lsc_credential_id)
-          {
-            lsc_credential_t lsc_credential = 0;
-
-            if (find_lsc_credential
-                 (delete_lsc_credential_data->lsc_credential_id,
-                  &lsc_credential))
-              SEND_TO_CLIENT_OR_FAIL
-               (XML_INTERNAL_ERROR ("delete_lsc_credential"));
-            else if (lsc_credential == 0)
-              {
+          switch (delete_lsc_credential
+                   (delete_lsc_credential_data->lsc_credential_id,
+                    delete_lsc_credential_data->ultimate))
+            {
+              case 0:
+                SEND_TO_CLIENT_OR_FAIL (XML_OK ("delete_lsc_credential"));
+                break;
+              case 1:
+                SEND_TO_CLIENT_OR_FAIL
+                 (XML_ERROR_SYNTAX ("delete_lsc_credential",
+                                    "LSC credential is in use"));
+                break;
+              case 2:
                 if (send_find_error_to_client
                      ("delete_lsc_credential",
                       "LSC credential",
@@ -9209,22 +9396,11 @@
                     error_send_to_client (error);
                     return;
                   }
-              }
-            else switch (delete_lsc_credential (lsc_credential))
-              {
-                case 0:
-                  SEND_TO_CLIENT_OR_FAIL (XML_OK ("delete_lsc_credential"));
-                  break;
-                case 1:
-                  SEND_TO_CLIENT_OR_FAIL
-                   (XML_ERROR_SYNTAX ("delete_lsc_credential",
-                                      "LSC credential is in use"));
-                  break;
-                default:
-                  SEND_TO_CLIENT_OR_FAIL
-                   (XML_INTERNAL_ERROR ("delete_lsc_credential"));
-              }
-          }
+                break;
+              default:
+                SEND_TO_CLIENT_OR_FAIL
+                 (XML_INTERNAL_ERROR ("delete_lsc_credential"));
+            }
         else
           SEND_TO_CLIENT_OR_FAIL
            (XML_ERROR_SYNTAX ("delete_lsc_credential",
@@ -9238,24 +9414,9 @@
         assert (strcasecmp ("DELETE_SLAVE", element_name) == 0);
         if (delete_slave_data->slave_id)
           {
-            slave_t slave = 0;
-
-            if (find_slave (delete_slave_data->slave_id, &slave))
-              SEND_TO_CLIENT_OR_FAIL (XML_INTERNAL_ERROR ("delete_slave"));
-            else if (slave == 0)
+            switch (delete_slave (delete_slave_data->slave_id,
+                                  delete_slave_data->ultimate))
               {
-                if (send_find_error_to_client ("delete_slave",
-                                               "slave",
-                                               delete_slave_data->slave_id,
-                                               write_to_client,
-                                               write_to_client_data))
-                  {
-                    error_send_to_client (error);
-                    return;
-                  }
-              }
-            else switch (delete_slave (slave))
-              {
                 case 0:
                   SEND_TO_CLIENT_OR_FAIL (XML_OK ("delete_slave"));
                   g_log ("event slave", G_LOG_LEVEL_MESSAGE,
@@ -9269,6 +9430,17 @@
                          "Slave %s could not be deleted",
                          delete_slave_data->slave_id);
                   break;
+                case 2:
+                  if (send_find_error_to_client ("delete_slave",
+                                                 "slave",
+                                                 delete_slave_data->slave_id,
+                                                 write_to_client,
+                                                 write_to_client_data))
+                    {
+                      error_send_to_client (error);
+                      return;
+                    }
+                  break;
                 default:
                   SEND_TO_CLIENT_OR_FAIL (XML_INTERNAL_ERROR ("delete_slave"));
                   g_log ("event slave", G_LOG_LEVEL_MESSAGE,
@@ -9287,13 +9459,23 @@
       case CLIENT_DELETE_TARGET:
         assert (strcasecmp ("DELETE_TARGET", element_name) == 0);
         if (delete_target_data->target_id)
-          {
-            target_t target = 0;
-
-            if (find_target (delete_target_data->target_id, &target))
-              SEND_TO_CLIENT_OR_FAIL (XML_INTERNAL_ERROR ("delete_target"));
-            else if (target == 0)
-              {
+          switch (delete_target (delete_target_data->target_id,
+                                 delete_target_data->ultimate))
+            {
+              case 0:
+                SEND_TO_CLIENT_OR_FAIL (XML_OK ("delete_target"));
+                g_log ("event target", G_LOG_LEVEL_MESSAGE,
+                       "Target %s has been deleted",
+                       delete_target_data->target_id);
+                break;
+              case 1:
+                SEND_TO_CLIENT_OR_FAIL (XML_ERROR_SYNTAX ("delete_target",
+                                                          "Target is in use"));
+                g_log ("event target", G_LOG_LEVEL_MESSAGE,
+                       "Target %s could not be deleted",
+                       delete_target_data->target_id);
+                break;
+              case 2:
                 if (send_find_error_to_client ("delete_target",
                                                "target",
                                                delete_target_data->target_id,
@@ -9303,29 +9485,16 @@
                     error_send_to_client (error);
                     return;
                   }
-              }
-            else switch (delete_target (target))
-              {
-                case 0:
-                  SEND_TO_CLIENT_OR_FAIL (XML_OK ("delete_target"));
-                  g_log ("event target", G_LOG_LEVEL_MESSAGE,
-                         "Target %s has been deleted",
-                         delete_target_data->target_id);
-                  break;
-                case 1:
-                  SEND_TO_CLIENT_OR_FAIL (XML_ERROR_SYNTAX ("delete_target",
-                                                            "Target is in use"));
-                  g_log ("event target", G_LOG_LEVEL_MESSAGE,
-                         "Target %s could not be deleted",
-                         delete_target_data->target_id);
-                  break;
-                default:
-                  SEND_TO_CLIENT_OR_FAIL (XML_INTERNAL_ERROR ("delete_target"));
-                  g_log ("event target", G_LOG_LEVEL_MESSAGE,
-                         "Target %s could not be deleted",
-                         delete_target_data->target_id);
-              }
-          }
+                g_log ("event target", G_LOG_LEVEL_MESSAGE,
+                       "Target %s could not be deleted",
+                       delete_target_data->target_id);
+                break;
+              default:
+                SEND_TO_CLIENT_OR_FAIL (XML_INTERNAL_ERROR ("delete_target"));
+                g_log ("event target", G_LOG_LEVEL_MESSAGE,
+                       "Target %s could not be deleted",
+                       delete_target_data->target_id);
+            }
         else
           SEND_TO_CLIENT_OR_FAIL
            (XML_ERROR_SYNTAX ("delete_target",
@@ -9337,23 +9506,9 @@
       case CLIENT_DELETE_TASK:
         if (delete_task_data->task_id)
           {
-            task_t task;
-            if (find_task (delete_task_data->task_id, &task))
-              SEND_TO_CLIENT_OR_FAIL (XML_INTERNAL_ERROR ("delete_task"));
-            else if (task == 0)
+            switch (request_delete_task_uuid (delete_task_data->task_id,
+                                              delete_task_data->ultimate))
               {
-                if (send_find_error_to_client ("delete_task",
-                                               "task",
-                                               delete_task_data->task_id,
-                                               write_to_client,
-                                               write_to_client_data))
-                  {
-                    error_send_to_client (error);
-                    return;
-                  }
-              }
-            else switch (request_delete_task (&task))
-              {
                 case 0:    /* Deleted. */
                   SEND_TO_CLIENT_OR_FAIL (XML_OK ("delete_task"));
                   g_log ("event task", G_LOG_LEVEL_MESSAGE,
@@ -9374,6 +9529,18 @@
                          "Task %s could not be deleted",
                          delete_task_data->task_id);
                   break;
+                case 3:  /* Failed to find task. */
+                  if (send_find_error_to_client
+                       ("delete_task",
+                        "task",
+                        delete_task_data->task_id,
+                        write_to_client,
+                        write_to_client_data))
+                    {
+                      error_send_to_client (error);
+                      return;
+                    }
+                  break;
                 default:   /* Programming error. */
                   assert (0);
                 case -1:
@@ -11978,6 +12145,12 @@
           slave_t slave = 0;
           char *tsk_uuid, *name, *description;
 
+          /* @todo Buffer the entire task creation and pass everything to a
+           *       libmanage function, so that libmanage can do the locking
+           *       properly instead of exposing the task_t.  Probably easier
+           *       after removing the option to create a task from an RC
+           *       file. */
+
           assert (strcasecmp ("CREATE_TASK", element_name) == 0);
           assert (create_task_data->task != (task_t) 0);
 
@@ -12350,6 +12523,23 @@
         set_client_state (CLIENT_CREATE_TASK);
         break;
 
+      case CLIENT_EMPTY_TRASHCAN:
+        switch (manage_empty_trashcan ())
+          {
+            case 0:
+              SEND_TO_CLIENT_OR_FAIL (XML_OK ("empty_trashcan"));
+              g_log ("event task", G_LOG_LEVEL_MESSAGE,
+                     "Trashcan has been emptied");
+              break;
+            default:  /* Programming error. */
+              assert (0);
+            case -1:
+              SEND_TO_CLIENT_OR_FAIL (XML_INTERNAL_ERROR ("empty_trashcan"));
+              break;
+          }
+        set_client_state (CLIENT_AUTHENTIC);
+        break;
+
       case CLIENT_MODIFY_NOTE:
         {
           task_t task = 0;
@@ -12675,6 +12865,51 @@
         set_client_state (CLIENT_AUTHENTIC);
         break;
 
+      case CLIENT_RESTORE:
+        if (restore_data->id)
+          {
+            switch (manage_restore (restore_data->id))
+              {
+                case 0:
+                  SEND_TO_CLIENT_OR_FAIL (XML_OK ("restore"));
+                  g_log ("event task", G_LOG_LEVEL_MESSAGE,
+                         "Resource %s has been restored",
+                         restore_data->id);
+                  break;
+                case 1:
+                  SEND_TO_CLIENT_OR_FAIL
+                   (XML_ERROR_SYNTAX ("restore", "Resource is in use"));
+                  break;
+                case 2:
+                  if (send_find_error_to_client ("restore",
+                                                 "resource",
+                                                 restore_data->id,
+                                                 write_to_client,
+                                                 write_to_client_data))
+                    {
+                      error_send_to_client (error);
+                      return;
+                    }
+                  break;
+                case 3:
+                  SEND_TO_CLIENT_OR_FAIL
+                   (XML_ERROR_SYNTAX ("restore",
+                                      "A resource with this name exists"
+                                      " already"));
+                  break;
+                default:  /* Programming error. */
+                  assert (0);
+                case -1:
+                  SEND_TO_CLIENT_OR_FAIL (XML_INTERNAL_ERROR ("restore"));
+                  break;
+              }
+          }
+        else
+          SEND_TO_CLIENT_OR_FAIL (XML_INTERNAL_ERROR ("restore"));
+        restore_data_reset (restore_data);
+        set_client_state (CLIENT_AUTHENTIC);
+        break;
+
       case CLIENT_RESUME_OR_START_TASK:
         if (resume_or_start_task_data->task_id)
           {
@@ -13214,6 +13449,7 @@
                                       " status_text=\"" STATUS_OK_TEXT "\">");
               init_agent_iterator (&agents,
                                    agent,
+                                   get_agents_data->trash,
                                    get_agents_data->sort_order,
                                    get_agents_data->sort_field);
               while (next (&agents))
@@ -13317,6 +13553,14 @@
                   return;
                 }
             }
+          else if ((get_configs_data->export
+                    || get_configs_data->families
+                    || get_configs_data->preferences)
+                   && get_configs_data->trash)
+            SEND_TO_CLIENT_OR_FAIL
+             (XML_ERROR_SYNTAX ("get_configs",
+                                "GET_CONFIGS trash given with export, families"
+                                " or preferences"));
           else
             {
               SEND_TO_CLIENT_OR_FAIL ("<get_configs_response"
@@ -13324,6 +13568,7 @@
                                       " status_text=\"" STATUS_OK_TEXT "\">");
               init_config_iterator (&configs,
                                     request_config,
+                                    get_configs_data->trash,
                                     get_configs_data->sort_order,
                                     get_configs_data->sort_field);
               while (next (&configs))
@@ -13367,12 +13612,16 @@
                                                config_iterator_name (&configs),
                                                config_iterator_comment
                                                 (&configs),
+                                               // FIX these access config table
                                                config_family_count (config),
                                                config_families_growing,
                                                config_nvt_count (config),
                                                config_nvts_growing,
-                                               config_in_use (config));
+                                               get_configs_data->trash
+                                                ? trash_config_in_use (config)
+                                                : config_in_use (config));
 
+                      // FIX skip if ->trash
                       init_config_task_iterator (&tasks,
                                                  config,
                                                  get_configs_data->sort_order);
@@ -13529,7 +13778,12 @@
 
           assert (strcasecmp ("GET_ESCALATORS", element_name) == 0);
 
-          if (get_escalators_data->escalator_id
+          if (get_escalators_data->escalator_id && get_escalators_data->trash)
+            SEND_TO_CLIENT_OR_FAIL
+             (XML_ERROR_SYNTAX ("get_escalators",
+                                "GET_ESCALATORS trash given with"
+                                " escalator_id"));
+          else if (get_escalators_data->escalator_id
               && find_escalator (get_escalators_data->escalator_id, &escalator))
             SEND_TO_CLIENT_OR_FAIL (XML_INTERNAL_ERROR ("get_escalators"));
           else if (get_escalators_data->escalator_id && escalator == 0)
@@ -13555,6 +13809,7 @@
                                        escalator,
                                        (task_t) 0,
                                        (event_t) 0,
+                                       get_escalators_data->trash,
                                        get_escalators_data->sort_order,
                                        get_escalators_data->sort_field);
               while (next (&escalators))
@@ -13579,6 +13834,7 @@
                   init_escalator_data_iterator (&data,
                                                 escalator_iterator_escalator
                                                  (&escalators),
+                                                get_escalators_data->trash,
                                                 "condition");
                   while (next (&data))
                     SENDF_TO_CLIENT_OR_FAIL ("<data>"
@@ -13596,8 +13852,10 @@
                                            event_name (escalator_iterator_event
                                             (&escalators)));
                   init_escalator_data_iterator (&data,
+                                                get_escalators_data->trash,
                                                 escalator_iterator_escalator
                                                  (&escalators),
+
                                                 "event");
                   while (next (&data))
                     SENDF_TO_CLIENT_OR_FAIL ("<data>"
@@ -13618,6 +13876,7 @@
                   init_escalator_data_iterator (&data,
                                                 escalator_iterator_escalator
                                                  (&escalators),
+                                                get_escalators_data->trash,
                                                 "method");
                   while (next (&data))
                     SENDF_TO_CLIENT_OR_FAIL ("<data>"
@@ -13729,6 +13988,7 @@
                                       " status_text=\"" STATUS_OK_TEXT "\">");
               init_lsc_credential_iterator (&credentials,
                                             lsc_credential,
+                                            get_lsc_credentials_data->trash,
                                             get_lsc_credentials_data->sort_order,
                                             get_lsc_credentials_data->sort_field);
               while (next (&credentials))
@@ -13873,7 +14133,11 @@
 
           assert (strcasecmp ("GET_SLAVES", element_name) == 0);
 
-          if (get_slaves_data->slave_id
+          if (get_slaves_data->tasks && get_slaves_data->trash)
+            SEND_TO_CLIENT_OR_FAIL
+             (XML_ERROR_SYNTAX ("get_slave",
+                                "GET_SLAVE tasks given with trash"));
+          else if (get_slaves_data->slave_id
               && find_slave (get_slaves_data->slave_id, &slave))
             SEND_TO_CLIENT_OR_FAIL (XML_INTERNAL_ERROR ("get_slaves"));
           else if (get_slaves_data->slave_id && slave == 0)
@@ -13897,6 +14161,7 @@
                                       " status_text=\"" STATUS_OK_TEXT "\">");
               init_slave_iterator (&slaves,
                                    slave,
+                                   get_slaves_data->trash,
                                    get_slaves_data->sort_order,
                                    get_slaves_data->sort_field);
               while (next (&slaves))
@@ -13914,9 +14179,15 @@
                                            slave_iterator_host (&slaves),
                                            slave_iterator_port (&slaves),
                                            slave_iterator_login (&slaves),
-                                           slave_in_use
-                                            (slave_iterator_slave (&slaves)));
+                                           get_slaves_data->trash
+                                            ? trash_slave_in_use
+                                               (slave_iterator_slave
+                                                 (&slaves))
+                                            : slave_in_use
+                                               (slave_iterator_slave
+                                                 (&slaves)));
 
+                  // FIX prevent if ->trash
                   if (get_slaves_data->tasks)
                     {
                       iterator_t tasks;
@@ -14080,6 +14351,7 @@
                                       " status_text=\"" STATUS_OK_TEXT "\">");
               init_target_iterator (&targets,
                                     target,
+                                    get_targets_data->trash,
                                     get_targets_data->sort_order,
                                     get_targets_data->sort_field);
               while (next (&targets))
@@ -14090,10 +14362,28 @@
 
                   ssh_credential = target_iterator_ssh_credential (&targets);
                   smb_credential = target_iterator_smb_credential (&targets);
-                  ssh_lsc_name = lsc_credential_name (ssh_credential);
-                  ssh_lsc_uuid = lsc_credential_uuid (ssh_credential);
-                  smb_lsc_name = lsc_credential_name (smb_credential);
-                  smb_lsc_uuid = lsc_credential_uuid (smb_credential);
+                  if (get_targets_data->trash
+                      && target_iterator_ssh_trash (&targets))
+                    {
+                      ssh_lsc_name = trash_lsc_credential_name (ssh_credential);
+                      ssh_lsc_uuid = trash_lsc_credential_uuid (ssh_credential);
+                    }
+                  else
+                    {
+                      ssh_lsc_name = lsc_credential_name (ssh_credential);
+                      ssh_lsc_uuid = lsc_credential_uuid (ssh_credential);
+                    }
+                  if (get_targets_data->trash
+                      && target_iterator_smb_trash (&targets))
+                    {
+                      smb_lsc_name = trash_lsc_credential_name (smb_credential);
+                      smb_lsc_uuid = trash_lsc_credential_uuid (smb_credential);
+                    }
+                  else
+                    {
+                      smb_lsc_name = lsc_credential_name (smb_credential);
+                      smb_lsc_uuid = lsc_credential_uuid (smb_credential);
+                    }
                   port_range = target_iterator_port_range (&targets);
 
                   SENDF_TO_CLIENT_OR_FAIL ("<target id=\"%s\">"
@@ -14105,9 +14395,11 @@
                                            "<port_range>%s</port_range>"
                                            "<ssh_lsc_credential id=\"%s\">"
                                            "<name>%s</name>"
+                                           "<trash>%i</trash>"
                                            "</ssh_lsc_credential>"
                                            "<smb_lsc_credential id=\"%s\">"
                                            "<name>%s</name>"
+                                           "<trash>%i</trash>"
                                            "</smb_lsc_credential>",
                                            target_iterator_uuid (&targets),
                                            target_iterator_name (&targets),
@@ -14115,14 +14407,26 @@
                                            manage_max_hosts
                                             (target_iterator_hosts (&targets)),
                                            target_iterator_comment (&targets),
-                                           target_in_use
-                                            (target_iterator_target (&targets)),
+                                           get_targets_data->trash
+                                            ? trash_target_in_use
+                                               (target_iterator_target
+                                                 (&targets))
+                                            : target_in_use
+                                               (target_iterator_target
+                                                 (&targets)),
                                            port_range ? port_range : "",
                                            ssh_lsc_uuid ? ssh_lsc_uuid : "",
                                            ssh_lsc_name ? ssh_lsc_name : "",
+                                           (get_targets_data->trash
+                                             && target_iterator_ssh_trash
+                                                 (&targets)),
                                            smb_lsc_uuid ? smb_lsc_uuid : "",
-                                           smb_lsc_name ? smb_lsc_name : "");
+                                           smb_lsc_name ? smb_lsc_name : "",
+                                           (get_targets_data->trash
+                                             && target_iterator_smb_trash
+                                                 (&targets)));
 
+                  // FIX prevent if get_targets_data->trash
                   if (get_targets_data->tasks)
                     {
                       iterator_t tasks;
@@ -14210,9 +14514,11 @@
 
               init_task_iterator (&tasks,
                                   task,
+                                  get_tasks_data->trash,
                                   get_tasks_data->sort_order,
                                   get_tasks_data->sort_field);
               while (next (&tasks))
+                // FIX prevent if ->trash?
                 if (get_tasks_data->details)
                   {
                     /* The detailed version. */
@@ -14616,7 +14922,7 @@
                     gchar *description64, *last_report_id, *last_report;
                     gchar *second_last_report_id, *second_last_report;
                     report_t running_report;
-                    int maximum_hosts;
+                    int maximum_hosts, target_in_trash, schedule_in_trash;
                     schedule_t schedule;
                     time_t next_time;
                     int debugs, holes, infos, logs, warnings;
@@ -14629,7 +14935,11 @@
                     if (task_uuid (index, &tsk_uuid)) abort ();
 
                     target = task_target (index);
-                    hosts = target ? target_hosts (target) : NULL;
+                    target_in_trash = task_target_in_trash (index);
+                    if (target_in_trash)
+                      hosts = target ? target_hosts (target) : NULL;
+                    else
+                      hosts = target ? trash_target_hosts (target) : NULL;
                     maximum_hosts = hosts ? manage_max_hosts (hosts) : 0;
 
                     slave = task_slave (index);
@@ -14883,11 +15193,13 @@
                       {
                         task_schedule_uuid = schedule_uuid (schedule);
                         task_schedule_name = schedule_name (schedule);
+                        schedule_in_trash = task_schedule_in_trash (index);
                       }
                     else
                       {
                         task_schedule_uuid = (char*) g_strdup ("");
                         task_schedule_name = (char*) g_strdup ("");
+                        schedule_in_trash = 0;
                       }
                     next_time = task_schedule_next_time (index);
                     line = g_strdup_printf ("<task"
@@ -14896,15 +15208,19 @@
                                             "<comment>%s</comment>"
                                             "<config id=\"%s\">"
                                             "<name>%s</name>"
+                                            "<trash>%i</trash>"
                                             "</config>"
                                             "<escalator id=\"%s\">"
                                             "<name>%s</name>"
+                                            "<trash>%i</trash>"
                                             "</escalator>"
                                             "<target id=\"%s\">"
                                             "<name>%s</name>"
+                                            "<trash>%i</trash>"
                                             "</target>"
                                             "<slave id=\"%s\">"
                                             "<name>%s</name>"
+                                            "<trash>%i</trash>"
                                             "</slave>"
                                             "<status>%s</status>"
                                             "<progress>%s</progress>"
@@ -14916,6 +15232,7 @@
                                             "<schedule id=\"%s\">"
                                             "<name>%s</name>"
                                             "<next_time>%s</next_time>"
+                                            "<trash>%i</trash>"
                                             "</schedule>"
                                             "%s%s%s"
                                             "</task>",
@@ -14924,12 +15241,18 @@
                                             comment,
                                             config_uuid ? config_uuid : "",
                                             config ? config : "",
+                                            task_config_in_trash (index),
                                             escalator_uuid ? escalator_uuid : "",
                                             escalator ? escalator : "",
+                                            escalator
+                                             ? task_escalator_in_trash (index)
+                                             : 0,
                                             task_target_uuid ? task_target_uuid : "",
                                             task_target_name ? task_target_name : "",
+                                            target_in_trash,
                                             task_slave_uuid ? task_slave_uuid : "",
                                             task_slave_name ? task_slave_name : "",
+                                            task_slave_in_trash (index),
                                             task_run_status_name (index),
                                             progress_xml,
                                             description64,
@@ -14943,6 +15266,7 @@
                                             (next_time == 0
                                               ? "over"
                                               : ctime_strip_newline (&next_time)),
+                                            schedule_in_trash,
                                             first_report,
                                             last_report,
                                             second_last_report);

Modified: trunk/openvas-manager/src/otp.c
===================================================================
--- trunk/openvas-manager/src/otp.c	2011-03-11 17:52:55 UTC (rev 10531)
+++ trunk/openvas-manager/src/otp.c	2011-03-14 05:49:56 UTC (rev 10532)
@@ -2520,9 +2520,13 @@
                                                  TASK_STATUS_STOPPED);
                             break;
                           case TASK_STATUS_DELETE_REQUESTED:
-                            delete_task (current_scanner_task);
+                            delete_task_lock (current_scanner_task, 0);
                             current_report = (report_t) 0;
                             break;
+                          case TASK_STATUS_DELETE_ULTIMATE_REQUESTED:
+                            delete_task_lock (current_scanner_task, 1);
+                            current_report = (report_t) 0;
+                            break;
                           default:
                             set_task_run_status (current_scanner_task,
                                                  TASK_STATUS_DONE);



More information about the Openvas-commits mailing list