]> git.ipfire.org Git - thirdparty/bacula.git/commitdiff
Add bconsole interface to metaxxx tables and rename Client.Plugin to Client.Plugins
authorEric Bollengier <eric@baculasystems.com>
Wed, 22 Sep 2021 15:00:13 +0000 (17:00 +0200)
committerEric Bollengier <eric@baculasystems.com>
Wed, 6 Sep 2023 07:49:00 +0000 (09:49 +0200)
12 files changed:
bacula/src/cats/bdb.h
bacula/src/cats/bdb_postgresql.h
bacula/src/cats/cats.c
bacula/src/cats/cats.h
bacula/src/cats/postgresql.c
bacula/src/cats/protos.h
bacula/src/cats/sql.c
bacula/src/cats/sql_list.c
bacula/src/cats/sql_update.c
bacula/src/dird/catreq.c
bacula/src/dird/fd_cmds.c
bacula/src/dird/ua_output.c

index ce09305d6d18fb6280d8ecea27696e6cf5e61c32..12719decbacf2aab571a016c0fb46cf7f1654c7a 100644 (file)
@@ -149,6 +149,7 @@ public:
    bool bdb_check_settings(JCR *jcr, int64_t *starttime, int val1, int64_t val2);
    bool bdb_open_batch_connexion(JCR *jcr);
    bool bdb_check_max_connections(JCR *jcr, uint32_t max_concurrent_jobs);
+   virtual const char *search_op(JCR *jcr, const char *table_col, char *value, POOLMEM **esc, POOLMEM **dest);
 
    /* Acl parts for various SQL commands */
    void  free_acl();             /* Used internally, free acls tab */
@@ -279,7 +280,7 @@ public:
    void bdb_list_snapshot_records(JCR *jcr, SNAPSHOT_DBR *sdbr,
               DB_LIST_HANDLER *sendit, void *ctx, e_list_type type);
    void bdb_list_files(JCR *jcr, FILE_DBR *fr, DB_RESULT_HANDLER *sendit, void *ctx);
-
+   void bdb_list_metadata_records(JCR *jcr, META_DBR *meta_r, DB_LIST_HANDLER *sendit, void *ctx, e_list_type type);
    /* sql_update.c */
    bool bdb_update_job_start_record(JCR *jcr, JOB_DBR *jr);
    int  bdb_update_job_end_record(JCR *jcr, JOB_DBR *jr);
index c552c2bc51c6c4f0ce8c7f0ceecb44aff050d56b..a098c9153e38e47580ae6313450d175a0a8252f6 100644 (file)
@@ -59,6 +59,7 @@ public:
    bool sql_batch_start(JCR *jcr);
    bool sql_batch_end(JCR *jcr, const char *error);
    bool sql_batch_insert(JCR *jcr, ATTR_DBR *ar);
+   const char *search_op(JCR *jcr, const char *table_col, char *value, POOLMEM **esc, POOLMEM **dest);
 };
 
 #endif /* __BDB_POSTGRESQL_H_ */
index 8e102be1537d90ef65a02b2173490f6dee07f9c1..eed9b5237aff28731aa330b1c46f59e8ac276db5 100644 (file)
@@ -1,7 +1,7 @@
-/*
+/* 
    Bacula(R) - The Network Backup Solution
 
-   Copyright (C) 2000-2022 Kern Sibbald
+   Copyright (C) 2000-2023 Kern Sibbald
 
    The original author of Bacula is Kern Sibbald, with contributions
    from many others, a complete list can be found in the file AUTHORS.
    This notice must be preserved when any source code is
    conveyed and/or propagated.
 
-   Bacula(R) is a registered trademark of Kern Sibbald.
-*/
-/*
- * Generic catalog class methods.
- *
- * Note: at one point, this file was assembled from parts of other files
- *  by a programmer, and other than "wrapping" in a class, which is a trivial
- *  change for a C++ programmer, nothing substantial was done, yet all the
- *  code was recommitted under this programmer's name.  Consequently, we
- *  undo those changes here.
- */
-
-#include "bacula.h"
-
-#if HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL
-
-#include "cats.h"
+   Bacula(R) is a registered trademark of Kern Sibbald. 
+*/ 
+/* 
+ * Generic catalog class methods. 
+ * 
+ * Note: at one point, this file was assembled from parts of other files 
+ *  by a programmer, and other than "wrapping" in a class, which is a trivial  
+ *  change for a C++ programmer, nothing substantial was done, yet all the  
+ *  code was recommitted under this programmer's name.  Consequently, we  
+ *  undo those changes here.  
+ */ 
+#include "bacula.h" 
+#if HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL 
+#include "cats.h" 
 
 static int dbglvl=100;
-
 void append_filter(POOLMEM **buf, char *cond)
 {
    if (*buf[0] != '\0') {
@@ -45,122 +45,141 @@ void append_filter(POOLMEM **buf, char *cond)
    pm_strcat(buf, cond);
 }
 
-bool BDB::bdb_match_database(const char *db_driver, const char *db_name,
-                             const char *db_address, int db_port)
+static void append_AND_OR_filter(bool and_or, POOLMEM **buf, char *cond)
 {
-   BDB *mdb = this;
-   bool match;
-
-   if (db_driver) {
-      match = strcasecmp(mdb->m_db_driver, db_driver) == 0 &&
-              bstrcmp(mdb->m_db_name, db_name) &&
-              bstrcmp(mdb->m_db_address, db_address) &&
-              mdb->m_db_port == db_port &&
-              mdb->m_dedicated == false;
+   if (*buf[0] != '\0') {
+      if (and_or) {
+         pm_strcat(buf, " OR ");
+      } else {
+         pm_strcat(buf, " AND ");
+      }
    } else {
-      match = bstrcmp(mdb->m_db_name, db_name) &&
-              bstrcmp(mdb->m_db_address, db_address) &&
-              mdb->m_db_port == db_port &&
-              mdb->m_dedicated == false;
+      if (and_or) {
+         pm_strcpy(buf, " WHERE ( ");
+      } else {
+         pm_strcat(buf, " WHERE ");
+      }
    }
-   return match;
+
+   pm_strcat(buf, cond);
 }
 
-BDB *BDB::bdb_clone_database_connection(JCR *jcr, bool mult_db_connections)
-{
-   BDB *mdb = this;
-   /*
-    * See if its a simple clone e.g. with mult_db_connections set to false
-    * then we just return the calling class pointer.
-    */
-   if (!mult_db_connections) {
-      mdb->m_ref_count++;
-      return mdb;
-   }
-
-   /*
-    * A bit more to do here just open a new session to the database.
-    */
-   return db_init_database(jcr, mdb->m_db_driver, mdb->m_db_name,
+bool BDB::bdb_match_database(const char *db_driver, const char *db_name, 
+                             const char *db_address, int db_port) 
+{ 
+   BDB *mdb = this; 
+   bool match; 
+   if (db_driver) { 
+      match = strcasecmp(mdb->m_db_driver, db_driver) == 0 && 
+              bstrcmp(mdb->m_db_name, db_name) && 
+              bstrcmp(mdb->m_db_address, db_address) && 
+              mdb->m_db_port == db_port && 
+              mdb->m_dedicated == false; 
+   } else { 
+      match = bstrcmp(mdb->m_db_name, db_name) && 
+              bstrcmp(mdb->m_db_address, db_address) && 
+              mdb->m_db_port == db_port && 
+              mdb->m_dedicated == false; 
+   } 
+   return match; 
+} 
+BDB *BDB::bdb_clone_database_connection(JCR *jcr, bool mult_db_connections) 
+{ 
+   BDB *mdb = this; 
+   /* 
+    * See if its a simple clone e.g. with mult_db_connections set to false 
+    * then we just return the calling class pointer. 
+    */ 
+   if (!mult_db_connections) { 
+      mdb->m_ref_count++; 
+      return mdb; 
+   } 
+   /* 
+    * A bit more to do here just open a new session to the database. 
+    */ 
+   return db_init_database(jcr, mdb->m_db_driver, mdb->m_db_name, 
              mdb->m_db_user, mdb->m_db_password, mdb->m_db_address,
              mdb->m_db_port, mdb->m_db_socket,
              mdb->m_db_ssl_mode, mdb->m_db_ssl_key,
              mdb->m_db_ssl_cert, mdb->m_db_ssl_ca,
              mdb->m_db_ssl_capath, mdb->m_db_ssl_cipher,
              true, mdb->m_disabled_batch_insert);
-}
-
-const char *BDB::bdb_get_engine_name(void)
-{
-   BDB *mdb = this;
-   switch (mdb->m_db_driver_type) {
-   case SQL_DRIVER_TYPE_MYSQL:
-      return "MySQL";
-   case SQL_DRIVER_TYPE_POSTGRESQL:
-      return "PostgreSQL";
-   case SQL_DRIVER_TYPE_SQLITE3:
-      return "SQLite3";
-   default:
-      return "Unknown";
-   }
-}
-
-/*
- * Lock database, this can be called multiple times by the same
- * thread without blocking, but must be unlocked the number of
- * times it was locked using db_unlock().
- */
-void BDB::bdb_lock(const char *file, int line)
-{
-   int errstat;
-   BDB *mdb = this;
-
-   if ((errstat = rwl_writelock_p(&mdb->m_lock, file, line)) != 0) {
-      berrno be;
-      e_msg(file, line, M_FATAL, 0, "rwl_writelock failure. stat=%d: ERR=%s\n",
-            errstat, be.bstrerror(errstat));
-   }
-}
-
-/*
- * Unlock the database. This can be called multiple times by the
- * same thread up to the number of times that thread called
- * db_lock()/
- */
-void BDB::bdb_unlock(const char *file, int line)
-{
-   int errstat;
-   BDB *mdb = this;
-
-   if ((errstat = rwl_writeunlock(&mdb->m_lock)) != 0) {
-      berrno be;
-      e_msg(file, line, M_FATAL, 0, "rwl_writeunlock failure. stat=%d: ERR=%s\n",
-            errstat, be.bstrerror(errstat));
-   }
-}
-
-bool BDB::bdb_sql_query(const char *query, int flags)
-{
-   bool retval;
-   BDB *mdb = this;
-
-   bdb_lock();
-   retval = sql_query(query, flags);
-   if (!retval) {
-      Mmsg(mdb->errmsg, _("Query failed: %s: ERR=%s\n"), query, sql_strerror());
-   }
-   bdb_unlock();
-   return retval;
-}
-
-void BDB::print_lock_info(FILE *fp)
-{
-   BDB *mdb = this;
-   if (mdb->m_lock.valid == RWLOCK_VALID) {
-      fprintf(fp, "\tRWLOCK=%p w_active=%i w_wait=%i\n",
-         &mdb->m_lock, mdb->m_lock.w_active, mdb->m_lock.w_wait);
-   }
-}
+} 
+const char *BDB::bdb_get_engine_name(void) 
+{ 
+   BDB *mdb = this; 
+   switch (mdb->m_db_driver_type) { 
+   case SQL_DRIVER_TYPE_MYSQL: 
+      return "MySQL"; 
+   case SQL_DRIVER_TYPE_POSTGRESQL: 
+      return "PostgreSQL"; 
+   case SQL_DRIVER_TYPE_SQLITE3: 
+      return "SQLite3"; 
+   default: 
+      return "Unknown"; 
+   } 
+} 
+/* 
+ * Lock database, this can be called multiple times by the same 
+ * thread without blocking, but must be unlocked the number of 
+ * times it was locked using db_unlock(). 
+ */ 
+void BDB::bdb_lock(const char *file, int line) 
+{ 
+   int errstat; 
+   BDB *mdb = this; 
+   if ((errstat = rwl_writelock_p(&mdb->m_lock, file, line)) != 0) { 
+      berrno be; 
+      e_msg(file, line, M_FATAL, 0, "rwl_writelock failure. stat=%d: ERR=%s\n", 
+            errstat, be.bstrerror(errstat)); 
+   } 
+} 
+/* 
+ * Unlock the database. This can be called multiple times by the 
+ * same thread up to the number of times that thread called 
+ * db_lock()/ 
+ */ 
+void BDB::bdb_unlock(const char *file, int line) 
+{ 
+   int errstat; 
+   BDB *mdb = this; 
+   if ((errstat = rwl_writeunlock(&mdb->m_lock)) != 0) { 
+      berrno be; 
+      e_msg(file, line, M_FATAL, 0, "rwl_writeunlock failure. stat=%d: ERR=%s\n", 
+            errstat, be.bstrerror(errstat)); 
+   } 
+} 
+bool BDB::bdb_sql_query(const char *query, int flags) 
+{ 
+   bool retval; 
+   BDB *mdb = this; 
+   bdb_lock(); 
+   retval = sql_query(query, flags); 
+   if (!retval) { 
+      Mmsg(mdb->errmsg, _("Query failed: %s: ERR=%s\n"), query, sql_strerror()); 
+   } 
+   bdb_unlock(); 
+   return retval; 
+} 
+void BDB::print_lock_info(FILE *fp) 
+{ 
+   BDB *mdb = this; 
+   if (mdb->m_lock.valid == RWLOCK_VALID) { 
+      fprintf(fp, "\tRWLOCK=%p w_active=%i w_wait=%i\n",  
+         &mdb->m_lock, mdb->m_lock.w_active, mdb->m_lock.w_wait); 
+   } 
+} 
 
 bool OBJECT_DBR::parse_plugin_object_string(char **obj_str)
 {
@@ -245,8 +264,7 @@ bool OBJECT_DBR::parse_plugin_object_string(char **obj_str)
    if (!tmp) {
       goto bail_out;
    }
-   val = str_to_uint64(tmp);
-   ObjectCount = (val > 9223372036854775808ULL /*2^63 */) ? 0 : val;
+   ObjectCount = str_to_uint64(*obj_str);
 
    ret = true;
 
@@ -336,7 +354,6 @@ void OBJECT_DBR::create_db_filter(JCR *jcr, POOLMEM **where)
          append_filter(where, tmp.c_str());
       }
    }
-
 }
 
 void parse_restore_object_string(char **r_obj_str, ROBJECT_DBR *robj_r)
@@ -455,6 +472,7 @@ static struct json_sql email_json_v1[] = {
    SAME_KW("EmailImportance", OT_STRING),
    SAME_KW("EmailInternetMessageId", OT_STRING),
    SAME_KW("EmailIsRead", OT_BOOL),
+   SAME_KW("EmailIsDraft", OT_BOOL),
    SAME_KW("EmailTime", OT_STRING),
    SAME_KW("EmailSubject", OT_STRING),
    SAME_KW("EmailTags", OT_STRING),
@@ -633,5 +651,153 @@ bail_out:
    return status;
 }
 
+void META_DBR::get_important_keys(POOLMEM **where)
+{
+   if (bstrcasecmp(Type, "email")) {
+      Mmsg(where, "EmailTenant, EmailOwner, EmailFrom, EmailTo, EmailTime, EmailSubject, FileIndex, JobId");
+   } else {
+      Mmsg(where, "AttachmentEmailId, AttachmentSize, AttachmentName, FileIndex, JobId");
+   }
+}
+
+void META_DBR::get_all_keys(POOLMEM **where)
+{
+   struct json_sql *p;
+   if (bstrcasecmp(Type, "email")) {
+      p = email_json_v1;
+   } else {
+      p = email_attachment_json_v1;
+   }
+
+   Mmsg(where, "JobId,FileIndex");
+   for (int i = 0; p[i].sql_name ; i++) {
+      pm_strcat(where, ",");
+      pm_strcat(where, p[i].sql_name);
+   }
+}
+
+void META_DBR::create_db_filter(JCR *jcr, BDB *db, POOLMEM **where)
+{
+   const char *prefix;
+   bool and_or = false;
+   POOL_MEM esc(PM_MESSAGE), tmp(PM_MESSAGE);
+
+   if (bstrcasecmp(Type, "email")) {
+      prefix = "Email";
+   } else {
+      prefix = "Attachment";
+   }
+
+   if (bstrcasecmp(Type, "email"))
+   {
+      if (all && (*From || *To || *Cc || *Subject || *Tags || *BodyPreview || *Category)) {
+         and_or = true;
+      }
+
+      if (Id[0] != 0) {
+         db->search_op(jcr, "MetaEmail.EmailId", Id, esc.handle(), tmp.handle());
+         append_AND_OR_filter(and_or, where, tmp.c_str());
+      }
+
+      if (From[0] != 0) {
+         db->search_op(jcr, "MetaEmail.EmailFrom", From, esc.handle(), tmp.handle());
+         append_AND_OR_filter(and_or, where, tmp.c_str());
+      }
+
+      if (To[0] != 0) {
+         db->search_op(jcr, "MetaEmail.EmailTo", To, esc.handle(), tmp.handle());
+         append_AND_OR_filter(and_or, where, tmp.c_str());
+      }
+
+      if (Cc[0] != 0) {
+         db->search_op(jcr, "MetaEmail.EmailCc", Cc, esc.handle(), tmp.handle());
+         append_AND_OR_filter(and_or, where, tmp.c_str());
+      }
+
+      if (Subject[0] != 0) {
+         db->search_op(jcr, "MetaEmail.EmailSubject", Subject, esc.handle(), tmp.handle());
+         append_AND_OR_filter(and_or, where, tmp.c_str());
+      }
+
+      if (Tags[0] != 0) {
+         db->search_op(jcr, "MetaEmail.EmailTags", Tags, esc.handle(), tmp.handle());
+         append_AND_OR_filter(and_or, where, tmp.c_str());
+      }
+
+      if (BodyPreview[0] != 0) {
+         db->search_op(jcr, "MetaEmail.EmailBodyPreview", BodyPreview, esc.handle(), tmp.handle());
+         append_AND_OR_filter(and_or, where, tmp.c_str());
+      }
+#if 0
+      if (Category[0] != 0) {
+         db->search_op(jcr, "MetaEmail.EmailCategory", Category, esc.handle(), tmp.handle());
+         append_AND_OR_filter(and_or, where, tmp.c_str());
+      }
+#endif
+
+      if (and_or) {
+         pm_strcat(where, ") ");
+      }
+
+      if (ConversationId[0] != 0) {
+         db_escape_string(jcr, jcr->db, esc.c_str(), ConversationId, strlen(ConversationId));
+         Mmsg(tmp, " MetaEmail.EmailConversationId = '%s'", esc.c_str());
+         append_filter(where, tmp.c_str());
+      }
+
+      if (HasAttachment > 0) {
+         Mmsg(tmp, " MetaEmail.EmailHasAttachment = %d", HasAttachment);
+         append_filter(where, tmp.c_str());
+      }
+
+      if (isDraft > 0) {
+         Mmsg(tmp, " MetaEmail.EmailIsDraft = %d", isDraft);
+         append_filter(where, tmp.c_str());
+      }
+
+      if (isRead > 0) {
+         Mmsg(tmp, " MetaEmail.EmailIsRead = %d", isRead);
+         append_filter(where, tmp.c_str());
+      }
+
+      if (MinTime[0]) {
+         db_escape_string(jcr, jcr->db, esc.c_str(), MinTime, strlen(MinTime));
+         Mmsg(tmp, " MetaEmail.EmailTime >= '%s'", esc.c_str());
+         append_filter(where, tmp.c_str());
+      }
+
+      if (MaxTime[0]) {
+         db_escape_string(jcr, jcr->db, esc.c_str(), MaxTime, strlen(MaxTime));
+         Mmsg(tmp, " MetaEmail.EmailTime <= '%s'", esc.c_str());
+         append_filter(where, tmp.c_str());
+      }
+   } else {
+      if (Id[0] != 0) {
+         db->search_op(jcr, "MetaAttachment.AttachmentEmailId", Id, esc.handle(), tmp.handle());
+         append_AND_OR_filter(and_or, where, tmp.c_str());
+      }
+   }
+
+   if (MinSize > 0) {
+      Mmsg(tmp, " Meta%s.%sSize >= %llu", prefix, prefix, MinSize);
+      append_filter(where, tmp.c_str());
+   }
+
+   if (MaxSize > 0) {
+      Mmsg(tmp, " Meta%s.%sSize <= %llu", prefix, prefix, MaxSize);
+      append_filter(where, tmp.c_str());
+   }
+
+   if (Plugin[0] != 0) {
+      db_escape_string(jcr, jcr->db, esc.c_str(), Plugin, strlen(Plugin));
+      Mmsg(tmp, " Meta%s.Plugin='%s'", prefix, esc.c_str());
+      append_filter(where, tmp.c_str());
+   }
+
+   if (JobId != 0) {
+      Mmsg(tmp, " Meta%s.JobId=%lu", prefix, JobId);
+      append_filter(where, tmp.c_str());
+   }
+}
 
 #endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL */ 
index a8381b010e01e7df66827b6b07f9f7ad05401c3d..74c3d66d5752a7e2e1cb06f1985cffb828331948 100644 (file)
@@ -484,7 +484,7 @@ struct CLIENT_DBR {
    utime_t JobRetention;
    char Name[MAX_NAME_LENGTH];        /* Client name */
    char Uname[MAX_UNAME_LENGTH];      /* Uname for client */
-   char Plugin[MAX_PLUGIN_LENGTH];    /* Plugin list for this client */
+   char Plugins[MAX_PLUGIN_LENGTH];   /* Plugin list for this client */
 };
 
 /* Counter record as in database */
@@ -630,6 +630,57 @@ public:
                 uint64_t *aclbits_extra); /* Extra ACL used */
 };
 
+
+
+/* Used to search in MetaEmail and MetaAttachment table */
+#define MAX_SEARCH_LENGTH 256
+#define MAX_SEARCH_LENGTH_ESCAPED (MAX_SEARCH_LENGTH * 2 + 1)
+
+class META_DBR: public SMARTALLOC
+{
+public:
+   JobId_t JobId;
+   int64_t MinSize;
+   int64_t MaxSize;
+   int     HasAttachment;
+   int     isDraft;
+   int     isRead;
+   uint64_t offset;
+   uint32_t limit;
+   int     order;
+   int     orderby;             // 0: JobId, FileIndex  1: EmailTime
+   bool    all;
+
+   char    Id[MAX_SEARCH_LENGTH];
+   char    Tenant[MAX_SEARCH_LENGTH];
+   char    Owner[MAX_SEARCH_LENGTH];
+   char    ClientName[MAX_NAME_LENGTH];
+   char    From[MAX_SEARCH_LENGTH];
+   char    To[MAX_SEARCH_LENGTH];
+   char    Cc[MAX_SEARCH_LENGTH];
+   char    Tags[MAX_SEARCH_LENGTH];
+   char    Subject[MAX_SEARCH_LENGTH];
+   char    BodyPreview[MAX_SEARCH_LENGTH];
+   char    Type[16];            // Email or Attachment
+   char    ConversationId[MAX_NAME_LENGTH];
+   char    Category[MAX_SEARCH_LENGTH];
+   char    MinTime[MAX_NAME_LENGTH];
+   char    MaxTime[MAX_NAME_LENGTH];
+   char    Plugin[MAX_NAME_LENGTH];
+   META_DBR(): JobId(0), MinSize(-1), MaxSize(-1), HasAttachment(-1),
+               isDraft(-1), isRead(-1), offset(0), limit(512), order(0), orderby(0), all(false)
+   {
+      *Id = *Tenant = *Owner = 0;
+      *ClientName = *From = *To = *Cc = *Subject = *Tags = 0;
+      *BodyPreview = *Type = *ConversationId = *Category = 0;
+      *MinTime = *MaxTime = *Plugin = 0;
+   };
+   ~META_DBR() {};
+   void get_important_keys(POOLMEM **dest);
+   void get_all_keys(POOLMEM **dest);
+   void create_db_filter(JCR *jcr, BDB *db, POOLMEM **dest);
+};
+
 /* Call back context for getting a 32/64 bit value from the database */
 class db_int64_ctx {
 public:
index aaac8bc35dcad09596c303177d2cebdf5afc9bda..45abf0125c937b6be43ea676d1da66b984f0e04f 100644 (file)
@@ -1186,5 +1186,13 @@ bool BDB_POSTGRESQL::sql_batch_insert(JCR *jcr, ATTR_DBR *ar)
    return true; 
 }
 
+const char *BDB_POSTGRESQL::search_op(JCR *jcr, const char *table_col, char *value, POOLMEM **esc, POOLMEM **dest)
+{
+   int len = strlen(value);
+   *esc = check_pool_memory_size(*esc, len*2+1);
+   bdb_escape_string(jcr, *esc, value, len);
+   Mmsg(dest, " %s %%> '%s'", table_col, *esc);
+   return *dest;
+}
 
 #endif /* HAVE_POSTGRESQL */
index 28ff00cc153a31ec174de1cf564c42b36fa82526..a598447f7562d7f6f6cb38483d56422d842172ab 100644 (file)
@@ -309,6 +309,8 @@ void bdb_free_restoreobject_record(JCR *jcr, ROBJECT_DBR *rr);
            mdb->bdb_list_files(jcr, filedbr, sendit, ua);
 #define db_list_tag_records(jcr, mdb, tagdbr, sendit, ua, llist)     \
            mdb->bdb_list_tag_records(jcr, tagdbr, sendit, ua, llist);
+#define db_list_metadata_records(jcr, mdb, dbr, sendit, ua, llist)     \
+           mdb->bdb_list_metadata_records(jcr, dbr, sendit, ua, llist);
 
 /* sql_update.c */
 #define db_update_job_start_record(jcr, mdb, jr) \
index b8dcadc7b2f67fad6d449d195d0ef500adfac635..1ad740c2c14db20824dfea8264db8e94de5dbfda 100644 (file)
@@ -1413,6 +1413,15 @@ void TAG_DBR::gen_sql(JCR *jcr, BDB *db,
    *aclbits_extra_ = aclbits_extra;
 }
 
+const char *BDB::search_op(JCR *jcr, const char *table_col, char *value, POOLMEM **esc, POOLMEM **dest)
+{
+   int len = strlen(value);
+   *esc = check_pool_memory_size(*esc, len*2+1);
+   bdb_escape_string(jcr, *esc, (char*)value, len);
+   Mmsg(dest, " %s ILIKE '%%%s%%'", table_col, value);
+   return *dest;
+}
+
 #ifdef COMMUNITY
 bool BDB::bdb_check_settings(JCR *jcr, int64_t *starttime, int val, int64_t val2) 
 {
index d8f4ad16c309f3bd595c5f3f36353c3871a9e4f7..9167a629f446ea5be1c869a9699f4bd28f550dcd 100644 (file)
@@ -1,7 +1,7 @@
 /*
    Bacula(R) - The Network Backup Solution
 
-   Copyright (C) 2000-2022 Kern Sibbald
+   Copyright (C) 2000-2023 Kern Sibbald
 
    The original author of Bacula is Kern Sibbald, with contributions
    from many others, a complete list can be found in the file AUTHORS.
@@ -39,7 +39,7 @@
 /*
  * Submit general SQL query
  */
-int BDB::bdb_list_sql_query(JCR *jcr, const char *title, const char *query, DB_LIST_HANDLER *sendit,
+int BDB::bdb_list_sql_query(JCR *jcr, const char *query, DB_LIST_HANDLER *sendit,
                       void *ctx, int verbose, e_list_type type)
 {
    bdb_lock();
@@ -52,7 +52,7 @@ int BDB::bdb_list_sql_query(JCR *jcr, const char *title, const char *query, DB_L
       return 0;
    }
 
-   list_result(jcr,this, title, sendit, ctx, type);
+   list_result(jcr,this, sendit, ctx, type);
    sql_free_result();
    bdb_unlock();
    return 1;
@@ -100,7 +100,7 @@ void BDB::bdb_list_pool_records(JCR *jcr, POOL_DBR *pdbr,
       return;
    }
 
-   list_result(jcr, this, "pool", sendit, ctx, type);
+   list_result(jcr, this, sendit, ctx, type);
 
    sql_free_result();
    bdb_unlock();
@@ -110,19 +110,19 @@ void BDB::bdb_list_client_records(JCR *jcr, DB_LIST_HANDLER *sendit, void *ctx,
 {
    bdb_lock();
    if (type == VERT_LIST || type == JSON_LIST) {
-      Mmsg(cmd, "SELECT ClientId,Name,Uname,Plugin,AutoPrune,FileRetention,"
+      Mmsg(cmd, "SELECT ClientId,Name,Uname,Plugins,AutoPrune,FileRetention,"
          "JobRetention "
-           "FROM Client %s ORDER BY ClientId", get_acls(DB_ACL_BIT(DB_ACL_RBCLIENT), true));
+           "FROM Client %s ORDER BY ClientId", get_acl(DB_ACL_CLIENT, true));
    } else {
       Mmsg(cmd, "SELECT ClientId,Name,FileRetention,JobRetention "
-           "FROM Client %s ORDER BY ClientId", get_acls(DB_ACL_BIT(DB_ACL_RBCLIENT), true));
+           "FROM Client %s ORDER BY ClientId", get_acl(DB_ACL_CLIENT, true));
    }
    if (!QueryDB(jcr, cmd)) {
       bdb_unlock();
       return;
    }
 
-   list_result(jcr, this, "client", sendit, ctx, type);
+   list_result(jcr, this, sendit, ctx, type);
 
    sql_free_result();
    bdb_unlock();
@@ -144,7 +144,7 @@ void BDB::bdb_list_plugin_object_types(JCR *jcr, DB_LIST_HANDLER *sendit, void *
       return;
    }
 
-   list_result(jcr, this, "objecttype", sendit, ctx, type);
+   list_result(jcr, this, sendit, ctx, type);
 
    sql_free_result();
    bdb_unlock();
@@ -204,7 +204,7 @@ void BDB::bdb_list_plugin_objects(JCR *jcr, OBJECT_DBR *obj_r, DB_LIST_HANDLER *
       return;
    }
 
-   list_result(jcr, this, "object", sendit, ctx, type);
+   list_result(jcr, this, sendit, ctx, type);
 
    sql_free_result();
    bdb_unlock();
@@ -244,7 +244,7 @@ void BDB::bdb_list_plugin_objects_ids(JCR *jcr, char* id_list, DB_LIST_HANDLER *
       return;
    }
 
-   list_result(jcr, this, "object", sendit, ctx, type);
+   list_result(jcr, this, sendit, ctx, type);
 
    sql_free_result();
    bdb_unlock();
@@ -296,7 +296,7 @@ void BDB::bdb_list_restore_objects(JCR *jcr, ROBJECT_DBR *rr, DB_LIST_HANDLER *s
       return;
    }
 
-   list_result(jcr, this, "restoreobject", sendit, ctx, type);
+   list_result(jcr, this, sendit, ctx, type);
 
    sql_free_result();
    bdb_unlock();
@@ -385,7 +385,7 @@ void BDB::bdb_list_media_records(JCR *jcr, MEDIA_DBR *mdbr,
       return;
    }
 
-   list_result(jcr, this, "media", sendit, ctx, type);
+   list_result(jcr, this, sendit, ctx, type);
 
    sql_free_result();
    bdb_unlock();
@@ -396,30 +396,25 @@ void BDB::bdb_list_jobmedia_records(JCR *jcr, uint32_t JobId, char *volume,
 {
    POOL_MEM where2;
    bdb_lock();
-   const char *where_and = "WHERE";
-
    /* Get some extra SQL parameters if needed */
    const char *where = get_acls(DB_ACL_BIT(DB_ACL_JOB)     |
                                 DB_ACL_BIT(DB_ACL_FILESET) |
-                                DB_ACL_BIT(DB_ACL_BCLIENT), true);
+                                DB_ACL_BIT(DB_ACL_CLIENT), (JobId == 0 || volume != NULL));
 
    const char *join = *where ? get_acl_join_filter(DB_ACL_BIT(DB_ACL_JOB)     |
                                                    DB_ACL_BIT(DB_ACL_FILESET) |
-                                                   DB_ACL_BIT(DB_ACL_BCLIENT)) : "";
+                                                   DB_ACL_BIT(DB_ACL_CLIENT)) : "";
 
-   if (*where) {
-      where_and = "AND";
-   }
    if (JobId) {
-      Mmsg(where2, " %s JobMedia.JobId=%lu ", where_and, JobId);
-      where_and = "AND";
+      Mmsg(where2, " WHERE JobMedia.JobId=%lu ", JobId);
    }
+   
    if (volume) {
       POOL_MEM tmp, tmp2;
       int len = strlen(volume);
       tmp.check_size(len*2+1);
       db_escape_string(jcr, this, tmp.c_str(), volume, len);
-      Mmsg(tmp2, " %s Media.VolumeName = '%s' ", where_and, tmp.c_str());
+      Mmsg(tmp2, " %s Media.VolumeName = '%s' ", JobId == 0 ?"WHERE": "AND", tmp.c_str());
       pm_strcat(where2, tmp2.c_str());
    }
 
@@ -430,15 +425,15 @@ void BDB::bdb_list_jobmedia_records(JCR *jcr, uint32_t JobId, char *volume,
            "FROM JobMedia JOIN Media USING (MediaId) %s "
            "%s %s ORDER BY JobMediaId ASC",
            join,
-           where,
-           where2.c_str());
+           where2.c_str(),
+           where);
 
    } else {
       Mmsg(cmd, "SELECT JobId,Media.VolumeName,FirstIndex,LastIndex "
            "FROM JobMedia JOIN Media USING (MediaId) %s %s %s ORDER BY JobMediaId ASC",
            join,
-           where,
-           where2.c_str());
+           where2.c_str(),
+           where);
    }
    Dmsg1(DT_SQL|50, "q=%s\n", cmd);
 
@@ -447,7 +442,7 @@ void BDB::bdb_list_jobmedia_records(JCR *jcr, uint32_t JobId, char *volume,
       return;
    }
 
-   list_result(jcr, this, "jobmedia", sendit, ctx, type);
+   list_result(jcr, this, sendit, ctx, type);
 
    sql_free_result();
    bdb_unlock();
@@ -484,12 +479,13 @@ void BDB::bdb_list_filemedia_records(JCR *jcr, uint32_t JobId, uint32_t FileInde
       return;
    }
 
-   list_result(jcr, this, "filemedia", sendit, ctx, type);
+   list_result(jcr, this, sendit, ctx, type);
 
    sql_free_result();
    bdb_unlock();
 }
 
+
 void BDB::bdb_list_copies_records(JCR *jcr, uint32_t limit, char *JobIds,
                             DB_LIST_HANDLER *sendit, void *ctx, e_list_type type)
 {
@@ -506,8 +502,8 @@ void BDB::bdb_list_copies_records(JCR *jcr, uint32_t limit, char *JobIds,
    }
 
    bdb_lock();
-   const char *where = get_acls(DB_ACL_BIT(DB_ACL_JOB) | DB_ACL_BIT(DB_ACL_BCLIENT), false);
-   const char *join = *where ? get_acl_join_filter(DB_ACL_BIT(DB_ACL_BCLIENT)) : "";
+   const char *where = get_acls(DB_ACL_BIT(DB_ACL_JOB) | DB_ACL_BIT(DB_ACL_CLIENT), false);
+   const char *join = *where ? get_acl_join_filter(DB_ACL_BIT(DB_ACL_CLIENT)) : "";
 
    Mmsg(cmd,
    "SELECT DISTINCT Job.PriorJobId AS JobId, Job.Job, "
@@ -529,7 +525,7 @@ void BDB::bdb_list_copies_records(JCR *jcr, uint32_t limit, char *JobIds,
          sendit(ctx, _("The catalog contains copies as follows:\n"));
       }
 
-      list_result(jcr, this, "copy", sendit, ctx, type);
+      list_result(jcr, this, sendit, ctx, type);
    }
 
    sql_free_result();
@@ -631,7 +627,7 @@ void BDB::bdb_list_events_records(JCR *jcr, EVENTS_DBR *rec,
    if (!QueryDB(jcr, cmd)) {
       goto bail_out;
    }
-   list_result(jcr, this, "event", sendit, ctx, type);
+   list_result(jcr, this, sendit, ctx, type);
 
 bail_out:
    bdb_unlock();
@@ -649,13 +645,12 @@ void BDB::bdb_list_joblog_records(JCR *jcr, uint32_t JobId,
 
    const char *where = get_acls(DB_ACL_BIT(DB_ACL_JOB)     |
                                 DB_ACL_BIT(DB_ACL_FILESET) |
-                                DB_ACL_BIT(DB_ACL_RBCLIENT) 
-                                , false);
+                                DB_ACL_BIT(DB_ACL_CLIENT), false);
 
    const char *join = *where ? get_acl_join_filter(DB_ACL_BIT(DB_ACL_JOB)     |
                                                    DB_ACL_BIT(DB_ACL_FILESET) |
-                                                   DB_ACL_BIT(DB_ACL_RBCLIENT)) : "";
-   
+                                                   DB_ACL_BIT(DB_ACL_CLIENT)) : "";
+
    if (type == VERT_LIST || type == JSON_LIST) {
       Mmsg(cmd, "SELECT Time,LogText FROM Log %s "
            "WHERE Log.JobId=%s %s ORDER BY LogId ASC",
@@ -675,7 +670,7 @@ void BDB::bdb_list_joblog_records(JCR *jcr, uint32_t JobId,
       goto bail_out;
    }
 
-   list_result(jcr, this, "joblog", sendit, ctx, type);
+   list_result(jcr, this, sendit, ctx, type);
 
    sql_free_result();
 
@@ -773,7 +768,7 @@ alist *BDB::bdb_list_job_records(JCR *jcr, JOB_DBR *jr, DB_LIST_HANDLER *sendit,
    pm_strcat(where, where_tmp);
 
    if (*where_tmp) {
-      join = get_acl_join_filter(DB_ACL_BIT(DB_ACL_RBCLIENT)  |
+      join = get_acl_join_filter(DB_ACL_BIT(DB_ACL_CLIENT)  |
                                  DB_ACL_BIT(DB_ACL_FILESET));
    }
 
@@ -785,7 +780,7 @@ alist *BDB::bdb_list_job_records(JCR *jcr, JOB_DBR *jr, DB_LIST_HANDLER *sendit,
            "Job.ClientId,Client.Name as ClientName,JobStatus,SchedTime,"
            "StartTime,EndTime,RealEndTime,JobTDate,"
            "VolSessionId,VolSessionTime,JobFiles,JobBytes,ReadBytes,JobErrors,"
-           "JobMissingFiles,Job.PoolId,Pool.Name as PoolName,PriorJobId,PriorJob,"
+           "JobMissingFiles,Job.PoolId,Pool.Name as PoolName,PriorJobId,"
            "Job.FileSetId,FileSet.FileSet,Job.HasCache,Comment,Reviewed "
            "FROM Job JOIN Client USING (ClientId) LEFT JOIN Pool USING (PoolId) "
            "LEFT JOIN FileSet USING (FileSetId) %s "
@@ -802,13 +797,6 @@ alist *BDB::bdb_list_job_records(JCR *jcr, JOB_DBR *jr, DB_LIST_HANDLER *sendit,
              "FROM Job %s %s ORDER BY StartTime %s,JobId %s %s",
            join, where, order, order, limit);
       break;
-   case LAST_JOBS:
-      Mmsg(cmd,
-           "SELECT JobId,Client1.Name as Client,Job.Name as Name,StartTime,Level as "
-           "JobLevel,JobFiles,JobBytes "
-           "FROM Client AS Client1 JOIN Job USING (ClientId) %s %s AND JobStatus IN ('T','W') "
-           "ORDER BY StartTime %s %s", join, where, order, limit);
-
    default:
       break;
    }
@@ -830,7 +818,7 @@ alist *BDB::bdb_list_job_records(JCR *jcr, JOB_DBR *jr, DB_LIST_HANDLER *sendit,
       }
    }
    sql_data_seek(0);
-   list_result(jcr, this, "job", sendit, ctx, type);
+   list_result(jcr, this, sendit, ctx, type);
    sql_free_result();
    bdb_unlock();
    return list;
@@ -839,13 +827,12 @@ alist *BDB::bdb_list_job_records(JCR *jcr, JOB_DBR *jr, DB_LIST_HANDLER *sendit,
 /*
  * List Job totals
  *
- * TODO: Adapt for JSON
  */
 void BDB::bdb_list_job_totals(JCR *jcr, JOB_DBR *jr, DB_LIST_HANDLER *sendit, void *ctx)
 {
    bdb_lock();
-   const char *where = get_acls(DB_ACL_BIT(DB_ACL_BCLIENT) | DB_ACL_BIT(DB_ACL_JOB), true);
-   const char *join = *where ? get_acl_join_filter(DB_ACL_BIT(DB_ACL_BCLIENT)) : "";
+   const char *where = get_acls(DB_ACL_BIT(DB_ACL_CLIENT) | DB_ACL_BIT(DB_ACL_JOB), true);
+   const char *join = *where ? get_acl_join_filter(DB_ACL_BIT(DB_ACL_CLIENT)) : "";
 
    /* List by Job */
    Mmsg(cmd, "SELECT  count(*) AS Jobs,sum(JobFiles) "
@@ -857,7 +844,7 @@ void BDB::bdb_list_job_totals(JCR *jcr, JOB_DBR *jr, DB_LIST_HANDLER *sendit, vo
       return;
    }
 
-   list_result(jcr, this, "jobtotal", sendit, ctx, HORZ_LIST);
+   list_result(jcr, this, sendit, ctx, HORZ_LIST);
 
    sql_free_result();
 
@@ -871,7 +858,7 @@ void BDB::bdb_list_job_totals(JCR *jcr, JOB_DBR *jr, DB_LIST_HANDLER *sendit, vo
       return;
    }
 
-   list_result(jcr, this, "jobtotal", sendit, ctx, HORZ_LIST);
+   list_result(jcr, this, sendit, ctx, HORZ_LIST);
 
    sql_free_result();
    bdb_unlock();
@@ -901,11 +888,11 @@ void BDB::bdb_list_files_for_job(JCR *jcr, JobId_t jobid, int deleted, DB_LIST_H
    bdb_lock();
    /* Get optional filters for the SQL query */
    const char *where = get_acls(DB_ACL_BIT(DB_ACL_JOB) |
-                                DB_ACL_BIT(DB_ACL_BCLIENT) |
+                                DB_ACL_BIT(DB_ACL_CLIENT) |
                                 DB_ACL_BIT(DB_ACL_FILESET), true);
 
    const char *join = *where ? get_acl_join_filter(DB_ACL_BIT(DB_ACL_JOB) |
-                                                   DB_ACL_BIT(DB_ACL_BCLIENT) |
+                                                   DB_ACL_BIT(DB_ACL_CLIENT) |
                                                    DB_ACL_BIT(DB_ACL_FILESET)) : "";
 
    /*
@@ -990,7 +977,7 @@ void BDB::bdb_list_snapshot_records(JCR *jcr, SNAPSHOT_DBR *sdbr,
    char ed1[50];
 
    bdb_lock();
-   const char *where = get_acl(DB_ACL_BCLIENT, false);
+   const char *where = get_acl(DB_ACL_CLIENT, false);
 
    *filter = 0;
    if (sdbr->Name[0]) {
@@ -1068,7 +1055,7 @@ void BDB::bdb_list_snapshot_records(JCR *jcr, SNAPSHOT_DBR *sdbr,
       goto bail_out;
    }
 
-   list_result(jcr, this, "snapshot", sendit, ctx, type);
+   list_result(jcr, this, sendit, ctx, type);
 
 bail_out:
    sql_free_result();
@@ -1142,60 +1129,79 @@ void BDB::bdb_list_tag_records(JCR *jcr, TAG_DBR *tag, DB_LIST_HANDLER *result_h
          }
       }
       Dmsg1(DT_SQL|50, "q=%s\n", tmp.c_str());
-      bdb_list_sql_query(jcr, "tag", tmp.c_str(), result_handler, ctx, 0, type);
+      bdb_list_sql_query(jcr, tmp.c_str(), result_handler, ctx, 0, type);
    }
    bdb_unlock();
 }
 
-/* List all file records from a job
- * "deleted" values are described just below
+/*
+ * List plugin objects (search is based on object provided)
  */
-void BDB::bdb_list_jobs_for_file(JCR *jcr, const char *client, const char *fname, DB_LIST_HANDLER *sendit, void *ctx,  e_list_type type)
+void BDB::bdb_list_metadata_records(JCR *jcr, META_DBR *meta_r, DB_LIST_HANDLER *sendit, void *ctx, e_list_type type)
 {
-   if (!client || !*client || !fname || !*fname) {
-      return;
+   POOL_MEM esc(PM_MESSAGE), tmp(PM_MESSAGE), where(PM_MESSAGE), join(PM_MESSAGE);
+
+   bdb_lock();
+
+   //TODO add ACL part
+   meta_r->create_db_filter(jcr, this, where.handle());
+   Dmsg1(DT_SQL|50, "where=[%s]\n", where.c_str());
+
+   if (meta_r->ClientName[0] != 0) {
+      bdb_escape_string(jcr, esc.c_str(), meta_r->ClientName, strlen(meta_r->ClientName));
+      Mmsg(tmp, " Client.Name='%s'", esc.c_str());
+      append_filter(where.handle(), tmp.c_str());
+      Mmsg(join, " JOIN Job USING (JobId) JOIN Client USING (ClientId) ");
    }
-   const char *concat="Path.Path||File.Filename";
 
-   if (bdb_get_type_index() == SQL_TYPE_MYSQL) {
-      concat = " CONCAT(Path.Path,File.Filename) ";
+   if (meta_r->orderby == 1) {
+      Mmsg(tmp, " ORDER BY EmailTime %s ", meta_r->order ? "DESC" : "ASC");
+   } else {
+      Mmsg(tmp, " ORDER BY JobId, FileIndex %s ", meta_r->order ? "DESC" : "ASC");
    }
 
-   bdb_lock();
-   /* Get optional filters for the SQL query */
-   const char *where = get_acls(DB_ACL_BIT(DB_ACL_JOB) |
-                                DB_ACL_BIT(DB_ACL_BCLIENT) |
-                                DB_ACL_BIT(DB_ACL_FILESET), false);
+   pm_strcat(where, tmp.c_str());
 
-   const char *join = *where ? get_acl_join_filter(DB_ACL_BIT(DB_ACL_FILESET)) : "";
-   
-   int len = strlen(fname);
-   char *esc = (char *)malloc(len * 2 + 1);
-   bdb_escape_string(jcr, esc, (char *)fname, len);
-
-   len = strlen(client);
-   char *esc2 = (char *)malloc(len * 2 + 1);
-   bdb_escape_string(jcr, esc2, (char *)client, len);
-
-   Mmsg(cmd,    "SELECT Job.JobId as JobId,"
-        "%s as Name, "          // Concat of the filename
-        "StartTime, Type as JobType, JobStatus,JobFiles,JobBytes "
-        "FROM Client JOIN Job USING (ClientId) JOIN File USING (JobId) JOIN Path USING (PathId) %s "
-        "WHERE Client.Name = '%s' "
-        "AND File.FileIndex > 0 "
-        "AND File.Filename='%s' %s ORDER BY StartTime DESC LIMIT 20", concat, join, esc2, esc, where);
-
-   free(esc);
-   free(esc2);
-   Dmsg1(DT_SQL|50, "q=%s\n", cmd);
+   if (meta_r->limit > 0) {
+      Mmsg(tmp, " LIMIT %d ", meta_r->limit);
+      pm_strcat(where, tmp.c_str());
+   }
+
+   if (meta_r->offset > 0) {
+      Mmsg(tmp, " OFFSET %ld ", meta_r->offset);
+      pm_strcat(where, tmp.c_str());
+   }
+
+   switch (type) {
+   case JSON_LIST:
+   case VERT_LIST:
+      meta_r->get_all_keys(tmp.handle());
+      Mmsg(cmd,
+           "SELECT %s "
+           "FROM Meta%s %s %s", tmp.c_str(), meta_r->Type, join.c_str(), where.c_str());
+         break;
+   case HORZ_LIST:
+      meta_r->get_important_keys(tmp.handle());
+         Mmsg(cmd,
+            "SELECT %s "
+              "FROM Meta%s %s %s", tmp.c_str(), meta_r->Type, join.c_str(), where.c_str());
+         break;
+   default:
+         break;
+   }
+   Dmsg1(DT_SQL|50, "%s\n", cmd);
    if (!QueryDB(jcr, cmd)) {
-      goto bail_out;
+      Jmsg(jcr, M_ERROR, 0, _("Query %s failed!\n"), cmd);
+      bdb_unlock();
+      return;
    }
 
-   list_result(jcr, this, "job", sendit, ctx, HORZ_LIST);
-bail_out:
+   list_result(jcr, this, sendit, ctx, type);
+
    sql_free_result();
    bdb_unlock();
+
 }
 
+
 #endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL */
index af93c74596f6a2151fcd75424bf4b12da6ab185c..76a6db1e6c297164d609319997d3a9345b670257 100644 (file)
@@ -217,11 +217,11 @@ int BDB::bdb_update_client_record(JCR *jcr, CLIENT_DBR *cr)
 
    bdb_escape_string(jcr, esc_name, cr->Name, strlen(cr->Name));
    bdb_escape_string(jcr, esc_uname, cr->Uname, strlen(cr->Uname));
-   bdb_escape_string(jcr, esc_plugin, cr->Plugin, strlen(cr->Plugin));
+   bdb_escape_string(jcr, esc_plugin, cr->Plugins, strlen(cr->Plugins));
 
    Mmsg(cmd,
 "UPDATE Client SET AutoPrune=%d,FileRetention=%s,JobRetention=%s,"
-"Uname='%s',Plugin='%s' WHERE Name='%s'",
+"Uname='%s',Plugins='%s' WHERE Name='%s'",
       cr->AutoPrune,
       edit_uint64(cr->FileRetention, ed1),
       edit_uint64(cr->JobRetention, ed2),
index f6a566d0cc419b51595403263a7c3ef0412ba682..0ecfa321c4cc57c7c25e027d018c9e2b13d8d4cd 100644 (file)
@@ -657,7 +657,7 @@ static void update_attribute(JCR *jcr, char *msg, int32_t msglen)
       meta_pkt mp(p);
       META_JSON parser;
       POOL_MEM val;
-      Dmsg3(50, "[metadata plugin] type=%d len=%d buf=[%.*s]\n", mp.type, mp.buf_len, mp.buf);
+      Dmsg4(150, "[metadata plugin] type=%d len=%d buf=[%.*s]\n", mp.type, mp.buf_len, mp.buf_len, mp.buf);
       if (parser.parse(jcr,
                        jcr->db,
                        jcr->wjcr? jcr->wjcr->JobId : jcr->JobId,
@@ -673,7 +673,7 @@ static void update_attribute(JCR *jcr, char *msg, int32_t msglen)
          }
          db_unlock(jcr->db);
       } else {
-         Jmsg1(jcr, M_ERROR, 0, _("Unable to parse Plugin metadata for FileIndex %lld\n"), (int64_t)FileIndex);
+         Jmsg(jcr, M_ERROR, 0, _("Unable to parse Plugin metadata for FileIndex %lld\n"), (int64_t)FileIndex);
          Dmsg1(50, "Unable to parse Plugin metadata err=%s\n", val.c_str());
       }
 
index 7e57b759be52d6fe2d50a28fdc1c4a9994ac8da8..83d014e88bb86fb7069db620f920b26ae5eec25c 100644 (file)
@@ -199,7 +199,7 @@ int connect_to_file_daemon(JCR *jcr, int retry_interval, int max_retry_time,
           char *pos = strchr(fd->msg+strlen(OKjob)+1, ';');
           if (pos) {
              *pos = 0;
-             bstrncpy(cr.Plugin, pos+1, sizeof(cr.Plugin));
+             bstrncpy(cr.Plugins, pos+1, sizeof(cr.Plugins));
           }
           bstrncpy(cr.Uname, fd->msg+strlen(OKjob)+1, sizeof(cr.Uname));
 
index ecf323e354f41a17dc1cc37a54c91ad36ce7aa73..0a84c17a0af28bab279f034f1616c2ba89361dac 100644 (file)
@@ -336,10 +336,14 @@ bail_out:
  *  list objects [type=objecttype job_id=id clientname=n,status=S] - list plugin objects
  *  list pluginrestoreconf jobid=x,y,z [id=k]
  *  list filemedia jobid=x fileindex=z
- *  list metadata type=email [search=<str> where=<from|to|cc|tags|subject|bodypreview|attachement> importance=<str> isread=<yes|no> isdraft=<yes|no> hasattachement=<yes|no> limit=<int> offset=<int> receivedtime=<time> senttime=<time> order=<asc|desc>]
+ *  list metadata type=[email|attachment] from=<str> to=<str> cc=<str> tags=<str> 
+ *             subject=<str> bodypreview=<str> all=<str> minsize=<int> maxsize=<int> 
+ *             importance=<str> isread=<0|1> isdraft=<0|1>
+ *             categories=<str> conversationid=<str> hasattachment=<0|1>
+ *             starttime=<time> endtime=<time>
+ *             limit=<int> offset=<int>  order=<Asc|desc>
+ *             emailid=<str>
  *
- *  list metadata type=email where="(EmailFrom ILIKE '%test%' OR EmailTo ILIKE '%test%' OR EmailCc ILIKE '%test%') AND EmailReceivedtime > '2021-09-01 00:00:00'" limit=10
- *  
  *  Note: keyword "long" is before the first command on the command 
  *    line results in doing a llist (long listing).
  */
@@ -877,7 +881,7 @@ static int do_list_cmd(UAContext *ua, const char *cmd, e_list_type llist)
 
             } else if (strcasecmp(ua->argk[j], NT_("order")) == 0 && ua->argv[j]) {
                /* Other order are tested before */
-               event.order = bstrcasecmp(ua->argv[j], "DESC") == 0;
+               event.order = bstrcasecmp(ua->argv[j], "DESC");
 
             } else if (strcasecmp(ua->argk[j], NT_("days")) == 0 && ua->argv[j]) {
                if (!setup_start_date(ua->argv[j], true, event.start, sizeof(event.start))) {
@@ -910,6 +914,114 @@ static int do_list_cmd(UAContext *ua, const char *cmd, e_list_type llist)
       } else if (strcasecmp(ua->argk[i], NT_("tag")) == 0) {
          return tag_cmd(ua, cmd);
 
+      /* List Emails/Attachments */
+      } else if (strcasecmp(ua->argk[i], NT_("metadata")) == 0) {
+         META_DBR meta_r;
+
+         for (j=i+1; j<ua->argc; j++) {
+            if (strcasecmp(ua->argk[j], NT_("jobid")) == 0 && ua->argv[j]) {
+               if (is_a_number(ua->argv[j]) && acl_access_jobid_ok(ua, ua->argv[j])) {
+                  meta_r.JobId = str_to_uint64(ua->argv[j]);
+                } else {
+                  ua->error_msg(_("Invalid jobid argument\n"));
+                  return 1;
+               }
+
+            } else if (strcasecmp(ua->argk[j], NT_("client")) == 0) {
+               if (!acl_access_ok(ua, Client_ACL, ua->argk[j])) {
+                  ua->error_msg(_("Access to Client=%s not authorized.\n"), ua->argk[j]);
+                  return 0;
+               }
+               bstrncpy(meta_r.ClientName, ua->argv[j], sizeof(meta_r.ClientName));
+
+            } else if (strcasecmp(ua->argk[j], NT_("from")) == 0) {
+               bstrncpy(meta_r.From, ua->argv[j], sizeof(meta_r.From));
+
+            } else if (strcasecmp(ua->argk[j], NT_("emailid")) == 0) {
+               bstrncpy(meta_r.Id, ua->argv[j], sizeof(meta_r.Id));
+
+            } else if (strcasecmp(ua->argk[j], NT_("to")) == 0) {
+               bstrncpy(meta_r.To, ua->argv[j], sizeof(meta_r.To));
+
+            } else if (strcasecmp(ua->argk[j], NT_("cc")) == 0) {
+               bstrncpy(meta_r.Cc, ua->argv[j], sizeof(meta_r.Cc));
+
+            } else if (strcasecmp(ua->argk[j], NT_("tags")) == 0) {
+               bstrncpy(meta_r.Tags, ua->argv[j], sizeof(meta_r.Tags));
+
+            } else if (strcasecmp(ua->argk[j], NT_("subject")) == 0) {
+               bstrncpy(meta_r.Subject, ua->argv[j], sizeof(meta_r.Subject));
+
+            } else if (strcasecmp(ua->argk[j], NT_("bodypreview")) == 0) {
+               bstrncpy(meta_r.BodyPreview, ua->argv[j], sizeof(meta_r.BodyPreview));
+
+            } else if (strcasecmp(ua->argk[j], NT_("type")) == 0) {
+               bstrncpy(meta_r.Type, ua->argv[j], sizeof(meta_r.Type));
+
+            } else if (strcasecmp(ua->argk[j], NT_("conversationid")) == 0) {
+               bstrncpy(meta_r.ConversationId, ua->argv[j], sizeof(meta_r.ConversationId));
+
+            } else if (strcasecmp(ua->argk[j], NT_("category")) == 0) {
+               bstrncpy(meta_r.Category, ua->argv[j], sizeof(meta_r.Category));
+
+            } else if (strcasecmp(ua->argk[j], NT_("all")) == 0) {
+               bstrncpy(meta_r.Tags, ua->argv[j], sizeof(meta_r.Tags));
+               bstrncpy(meta_r.From, ua->argv[j], sizeof(meta_r.From));
+               bstrncpy(meta_r.To, ua->argv[j], sizeof(meta_r.To));
+               bstrncpy(meta_r.Cc, ua->argv[j], sizeof(meta_r.Cc));
+               bstrncpy(meta_r.Subject, ua->argv[j], sizeof(meta_r.Subject));
+               bstrncpy(meta_r.BodyPreview, ua->argv[j], sizeof(meta_r.BodyPreview));
+               bstrncpy(meta_r.Category, ua->argv[j], sizeof(meta_r.Category));
+               meta_r.all = true;
+
+            } else if (strcasecmp(ua->argk[j], NT_("minsize")) == 0) {
+               uint64_t v;
+               if (size_to_uint64(ua->argv[j], strlen(ua->argv[j]), &v)) {
+                  meta_r.MinSize = v;
+               }
+
+            } else if (strcasecmp(ua->argk[j], NT_("maxsize")) == 0) {
+               uint64_t v;
+               if (size_to_uint64(ua->argv[j], strlen(ua->argv[j]), &v)) {
+                  meta_r.MaxSize = v;
+               }
+
+            } else if (strcasecmp(ua->argk[j], NT_("mintime")) == 0) {
+               bstrncpy(meta_r.MinTime, ua->argv[j], sizeof(meta_r.MinTime));
+
+            } else if (strcasecmp(ua->argk[j], NT_("maxtime")) == 0) {
+               bstrncpy(meta_r.MaxTime, ua->argv[j], sizeof(meta_r.MaxTime));
+
+            } else if (strcasecmp(ua->argk[j], NT_("isread")) == 0) {
+               meta_r.isRead = str_to_uint64(ua->argv[j]);
+
+            } else if (strcasecmp(ua->argk[j], NT_("isdraft")) == 0) {
+               meta_r.isDraft = str_to_uint64(ua->argv[j]);
+
+            } else if (strcasecmp(ua->argk[j], NT_("hasattachment")) == 0) {
+               meta_r.HasAttachment = str_to_uint64(ua->argv[j]);
+
+            } else if (strcasecmp(ua->argk[j], NT_("offset")) == 0 && ua->argv[j]) {
+               meta_r.offset = atoi(ua->argv[j]);
+
+            } else if (strcasecmp(ua->argk[j], NT_("limit")) == 0 && ua->argv[j]) {
+               meta_r.limit = atoi(ua->argv[j]);
+
+            } else if (strcasecmp(ua->argk[j], NT_("orderby")) == 0 && ua->argv[j]) {
+               meta_r.orderby = bstrcasecmp(ua->argv[j], "Time") == 1;
+
+            } else if (strcasecmp(ua->argk[j], NT_("order")) == 0 && ua->argv[j]) {
+               /* Other order are tested before */
+               meta_r.order = bstrcasecmp(ua->argv[j], "DESC");
+            }
+         }
+         if (*meta_r.Type == 0) {
+            ua->error_msg(_("Invalid type argument\n"));
+            return 1;
+         }
+         db_list_metadata_records(ua->jcr, ua->db, &meta_r, prtit, ua, llist);
+         return 1;
+
       } else if (strcasecmp(ua->argk[i], NT_("limit")) == 0
                  || strcasecmp(ua->argk[i], NT_("days")) == 0
                  || strcasecmp(ua->argk[i], NT_("hours")) == 0