]> git.sur5r.net Git - bacula/bacula/commitdiff
Add wx-console directory
authorKern Sibbald <kern@sibbald.com>
Tue, 13 Apr 2004 12:50:27 +0000 (12:50 +0000)
committerKern Sibbald <kern@sibbald.com>
Tue, 13 Apr 2004 12:50:27 +0000 (12:50 +0000)
git-svn-id: https://bacula.svn.sourceforge.net/svnroot/bacula/trunk@1194 91ce42f0-d328-0410-95d8-f526ca767f89

26 files changed:
bacula/ReleaseNotes
bacula/src/console/authenticate.c
bacula/src/dird/bacula-dir.conf.in
bacula/src/dird/ua_query.c
bacula/src/lib/smartall.c
bacula/src/stored/spool.c
bacula/src/wx-console/BUILD [new file with mode: 0644]
bacula/src/wx-console/CHANGELOG [new file with mode: 0644]
bacula/src/wx-console/TODO [new file with mode: 0644]
bacula/src/wx-console/authenticate.c [new file with mode: 0644]
bacula/src/wx-console/console_conf.c [new file with mode: 0644]
bacula/src/wx-console/console_conf.h [new file with mode: 0644]
bacula/src/wx-console/console_thread.cpp [new file with mode: 0644]
bacula/src/wx-console/console_thread.h [new file with mode: 0644]
bacula/src/wx-console/csprint.h [new file with mode: 0644]
bacula/src/wx-console/main.cpp [new file with mode: 0644]
bacula/src/wx-console/marked.xpm [new file with mode: 0644]
bacula/src/wx-console/partmarked.xpm [new file with mode: 0644]
bacula/src/wx-console/unmarked.xpm [new file with mode: 0644]
bacula/src/wx-console/wxbmainframe.cpp [new file with mode: 0644]
bacula/src/wx-console/wxbmainframe.h [new file with mode: 0644]
bacula/src/wx-console/wxbrestorepanel.cpp [new file with mode: 0644]
bacula/src/wx-console/wxbrestorepanel.h [new file with mode: 0644]
bacula/src/wx-console/wxbtableparser.cpp [new file with mode: 0644]
bacula/src/wx-console/wxbtableparser.h [new file with mode: 0644]
bacula/src/wx-console/wxwin16x16.xpm [new file with mode: 0644]

index 291a24af301f1e54ef666b0a36401ff11a60ab1f..f6ce8e090545ef31a5fb23699ae4592f3d729f49 100644 (file)
@@ -1,8 +1,16 @@
 
-          Release Notes for Bacula 1.34.0
+          Release Notes for Bacula 1.34.1
 
   Bacula code: Total files = 306 Total lines = 91,131 (*.h *.c *.in)
 
+Changes for 1.34.1:  
+- Autochanger users, please note you must add %d to the end of the
+  changer command line in your Device resource in your bacula-sd.conf
+  file.
+- Fixed a crash in the query command.
+- Removed the schedule from the default restore job.
+Release 1.34.0
 Major Features:
 - Data spooling which reduces tape shoe-shine during Inc backups,         
   and permits multiple simultaneous backups without interleaved blocks.
index 5f5be7bfca5bdc704081e23a64132afb8176be5d..09704476498f92c140a21aae77f6232eddb66b0a 100644 (file)
@@ -11,7 +11,7 @@
  *
  */
 /*
-   Copyright (C) 2000, 2001 Kern Sibbald and John Walker
+   Copyright (C) 2001-2004 Kern Sibbald and John Walker
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
 
-   You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+   You should have received a copy of the GNU General Public
+   License along with this program; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, 
+   MA 02111-1307, USA.
  */
 
 #include "bacula.h"
index f150794933c726f2a90c7cfd90a59885233fd344..25e719882b027b6064f73f1bbe0f0d0ebadd4924 100644 (file)
@@ -68,6 +68,7 @@ Job {
   Client=@hostname@-fd                 
   FileSet="Full Set"                  
   Storage = File                      
+  Pool = Default
   Messages = Standard
   Where = /tmp/bacula-restores
 }
index 970ef5ab52f43cc845c407a39a504e9f1b5c40a9..32f21142664cb99873331d146ceb43c817369ba2 100644 (file)
@@ -32,8 +32,8 @@
 
 extern DIRRES *director;
 
-static char *substitute_prompts(UAContext *ua, 
-                      char *query, char **prompt, int nprompt);
+static POOLMEM *substitute_prompts(UAContext *ua, 
+                      POOLMEM *query, char **prompt, int nprompt);
 
 /*
  * Read a file containing SQL queries and prompt
@@ -50,23 +50,21 @@ static char *substitute_prompts(UAContext *ua,
  */
 int querycmd(UAContext *ua, char *cmd)
 {
-   FILE *fd;
+   FILE *fd = NULL;
    POOLMEM *query = get_pool_memory(PM_MESSAGE);
    char line[1000];
    int i, item, len;
    char *prompt[9];
-   int nprompt;
+   int nprompt = 0;;
    char *query_file = director->query_file;
    
    if (!open_db(ua)) {
-      free_pool_memory(query);
-      return 1;
+      goto bail_out;
    }
    if ((fd=fopen(query_file, "r")) == NULL) {
       bsendmsg(ua, "Could not open %s: ERR=%s\n", query_file,
         strerror(errno));
-      free_pool_memory(query);
-      return 1;
+      goto bail_out;
    }
 
    start_prompt(ua, _("Available queries:\n"));
@@ -77,9 +75,7 @@ int querycmd(UAContext *ua, char *cmd)
       }
    }
    if ((item=do_prompt(ua, "", _("Choose a query"), NULL, 0)) < 0) {
-      fclose(fd);
-      free_pool_memory(query);
-      return 1;
+      goto bail_out;
    }
    rewind(fd);
    i = -1;
@@ -93,15 +89,12 @@ int querycmd(UAContext *ua, char *cmd)
    }
    if (i != item) {
       bsendmsg(ua, _("Could not find query.\n"));
-      fclose(fd);
-      free_pool_memory(query);
-      return 1;
+      goto bail_out;
    }
    query[0] = 0;
    for (i=0; i<9; i++) {
       prompt[i] = NULL;
    }
-   nprompt = 0;
    while (fgets(line, sizeof(line), fd) != NULL) {
       if (line[0] == '#') {
         continue;
@@ -121,11 +114,10 @@ int querycmd(UAContext *ua, char *cmd)
            continue;
         }
       }  
-      query = check_pool_memory_size(query, len + 1);
       if (*query != 0) {
-         strcat(query, " ");
+         pm_strcat(&query, " ");
       }
-      strcat(query, line);
+      pm_strcat(&query, line);
       if (line[len-1] != ';') {
         continue;
       }
@@ -151,6 +143,11 @@ int querycmd(UAContext *ua, char *cmd)
             bsendmsg(ua, "%s\n", query);
         }
    }
+
+bail_out:
+   if (fd) {
+      fclose(fd);
+   }
    free_pool_memory(query);
    for (i=0; i<nprompt; i++) {
       free(prompt[i]);
@@ -159,7 +156,7 @@ int querycmd(UAContext *ua, char *cmd)
 }
 
 static POOLMEM *substitute_prompts(UAContext *ua, 
-                      char *query, char **prompt, int nprompt)
+                      POOLMEM *query, char **prompt, int nprompt)
 {
    char *p, *q, *o;
    POOLMEM *new_query;
index fe8093facb92f38a83acbb1b545ec36bd7fe29fb..c9a333e34bcb4da180381996d782f370a01fbbcb 100644 (file)
@@ -498,6 +498,7 @@ void sm_static(int mode)
  *  so that the memory is allocated through smartalloc.
  */
 
+#ifdef xxx
 void * operator new(size_t size)
 {
 // Dmsg1(000, "new called %d\n", size);
@@ -509,5 +510,6 @@ void operator delete(void *buf)
 // Dmsg1(000, "free called 0x%x\n", buf);
    sm_free(__FILE__, __LINE__, buf);
 }
+#endif
 
 #endif
index e2b2e97784c7ccb06d1dbdc4059fbdecca60e7cc..8c0f92a5dba60d5940b82dc77a2ba0ac360fe2ce 100644 (file)
@@ -160,6 +160,7 @@ static bool close_data_spool_file(JCR *jcr)
    if (spool_stats.data_size < 0) {
       spool_stats.data_size = 0;
    }
+   jcr->dcr->spool_size = 0;
    V(mutex);
 
    make_unique_data_spool_filename(jcr, &name);
@@ -185,18 +186,22 @@ static bool despool_data(DCR *dcr)
    dcr->spooling = false;
    lock_device(dcr->dev);
    dcr->dev_locked = true; 
-   /* Set up a dev structure to read */
+
+   /* Setup a dev structure to read */
    rdev = (DEVICE *)malloc(sizeof(DEVICE));
    memset(rdev, 0, sizeof(DEVICE));
    rdev->dev_name = get_memory(strlen("spool")+1);
    strcpy(rdev->dev_name, "spool");
    rdev->errmsg = get_pool_memory(PM_EMSG);
    *rdev->errmsg = 0;
+   rdev->max_block_size = dcr->dev->max_block_size;
+   rdev->min_block_size = dcr->dev->min_block_size;
    rdev->device = dcr->dev->device;
    rdcr = new_dcr(NULL, rdev);
    rdcr->spool_fd = dcr->spool_fd; 
    rdcr->jcr = jcr;                  /* set a valid jcr */
    block = rdcr->block;
+   Dmsg1(800, "read/write block size = %d\n", block->buf_len);
    lseek(rdcr->spool_fd, 0, SEEK_SET); /* rewind */
 
    for ( ; ok; ) {
diff --git a/bacula/src/wx-console/BUILD b/bacula/src/wx-console/BUILD
new file mode 100644 (file)
index 0000000..a9cf4e0
--- /dev/null
@@ -0,0 +1,20 @@
+General compiling notes:
+ - Fetch latest bacula sources from CVS or bacula-1.34.0 source
+   tarball, configure and build it.
+ - Extract bacula-wx-gui.tar.gz in bacula/src directory.
+
+Windows/cygwin compiling notes:
+ - Build and install wxWindows from sources
+   (http://www.wxwindows.org/).  I'm not sure it's necessary but
+   this is how I get the thing to work.
+ - In bacula/src/wx-console/Makefile.win, modify every
+   occurrence of "J:/cygwin" with the path where cygwin is install
+   on your system
+ ~ Build bacula-wx-gui with "make -f Makefile.win" in bacula/src/wx-console
+
+GTK/Linux compiling notes:
+ - Comment out "new" and "delete" operator definitions in
+   bacula/src/lib/smartall.c (lines 501-511), because these
+   overloads seem to produce segmentation faults with wxWindows, and
+   do "make" again in bacula/src/lib
+ - Build bacula-wx-gui with "make all" in bacula/src/wx-console
diff --git a/bacula/src/wx-console/CHANGELOG b/bacula/src/wx-console/CHANGELOG
new file mode 100644 (file)
index 0000000..041464d
--- /dev/null
@@ -0,0 +1,8 @@
+12-04-2004 :
+ - The source code is now better documented
+ - wxbRestorePanel : Check if a client was selected before
+   entering choose mode
+ - The source code is now right idented (3 spaces by level)
+ - Copyrights changed to Kern Sibbald and John Walker
+ - wxbPanel : super-class access rights problem corrected
+ - wxbRestorePanel : Added "nice" images to indicate restore status
diff --git a/bacula/src/wx-console/TODO b/bacula/src/wx-console/TODO
new file mode 100644 (file)
index 0000000..2d3eebc
--- /dev/null
@@ -0,0 +1,14 @@
+
+wxbRestorePanel : Allow the user to choose where he wants to restore his files
+
+wxbPanel/wxbMainFrame : Add a locking function (for example, deny the user to type something in the console when a restore is in progress)
+
+wxbRestorePanel : Check more carefully which job we just have run.
+
+console_thread : Allow the user to choose his config file.
+
+(old build params : )
+-lgcc -lodbc32 -lwsock32 -lwinspool -lwinmm -lshell32 -lcomctl32 -lctl3d32 -ladvapi32 -lopengl32 -lglu32 -lole32 -loleaut32 -luuid 
+-lkernel32 -lcomdlg32 -lgdi32 -lrpcrt4 -luser32 -lz
+
+-fno-pcc-struct-return -Os -fno-rtti -fno-exceptions
diff --git a/bacula/src/wx-console/authenticate.c b/bacula/src/wx-console/authenticate.c
new file mode 100644 (file)
index 0000000..94babca
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ *
+ *   Bacula UA authentication. Provides authentication with
+ *     the Director.
+ *
+ *     Kern Sibbald, June MMI
+ *
+ *    This routine runs as a thread and must be thread reentrant.
+ *
+ *  Basic tasks done here:
+ *
+ */
+/*
+   Copyright (C) 2001-2004 Kern Sibbald and John Walker
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License
+   as published by the Free Software Foundation; either version 2
+   of the License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, 
+   MA  02111-1307, USA.
+ */
+
+#include "bacula.h"
+#include "console_conf.h"
+#include "jcr.h"
+
+#include "csprint.h"
+
+void senditf(char *fmt, ...);
+void sendit(char *buf); 
+
+/* Commands sent to Director */
+static char hello[]    = "Hello %s calling\n";
+
+/* Response from Director */
+static char OKhello[]   = "1000 OK:";
+
+/* Forward referenced functions */
+
+/*
+ * Authenticate Director
+ */
+int authenticate_director(JCR *jcr, DIRRES *director, CONRES *cons)
+{
+   BSOCK *dir = jcr->dir_bsock;
+   int ssl_need = BNET_SSL_NONE;
+   char bashed_name[MAX_NAME_LENGTH];
+   char *password;
+
+   /* 
+    * Send my name to the Director then do authentication
+    */
+   if (cons) {
+      bstrncpy(bashed_name, cons->hdr.name, sizeof(bashed_name));
+      bash_spaces(bashed_name);
+      password = cons->password;
+   } else {
+      bstrncpy(bashed_name, "*UserAgent*", sizeof(bashed_name));
+      password = director->password;
+   }
+   /* Timeout Hello after 5 mins */
+   btimer_t *tid = start_bsock_timer(dir, 60 * 5);
+   bnet_fsend(dir, hello, bashed_name);
+
+   if (!cram_md5_get_auth(dir, password, ssl_need) || 
+       !cram_md5_auth(dir, password, ssl_need)) {
+      stop_bsock_timer(tid);
+      csprint("Director authorization problem.\nMost likely the passwords do not agree.\n", CS_DATA);  
+      return 0;
+   }
+
+   Dmsg1(6, ">dird: %s", dir->msg);
+   if (bnet_recv(dir) <= 0) {
+      stop_bsock_timer(tid);
+      csprint("Bad response to Hello command: ERR=", CS_DATA);  
+      csprint(bnet_strerror(dir), CS_DATA);
+      csprint("\n", CS_DATA);  
+      return 0;
+   }
+   Dmsg1(10, "<dird: %s", dir->msg);
+   stop_bsock_timer(tid);
+   if (strncmp(dir->msg, OKhello, sizeof(OKhello)-1) != 0) {
+      csprint("Director rejected Hello command\n", CS_DATA);
+      return 0;
+   } else {
+      csprint(dir->msg, CS_DATA);
+   }
+   return 1;
+}
+
diff --git a/bacula/src/wx-console/console_conf.c b/bacula/src/wx-console/console_conf.c
new file mode 100644 (file)
index 0000000..b9d4c72
--- /dev/null
@@ -0,0 +1,266 @@
+/*
+ *   Main configuration file parser for Bacula User Agent
+ *    some parts may be split into separate files such as
+ *    the schedule configuration (sch_config.c).
+ *
+ *   Note, the configuration file parser consists of three parts
+ *
+ *   1. The generic lexical scanner in lib/lex.c and lib/lex.h
+ *
+ *   2. The generic config  scanner in lib/parse_config.c and 
+ *     lib/parse_config.h.
+ *     These files contain the parser code, some utility
+ *     routines, and the common store routines (name, int,
+ *     string).
+ *
+ *   3. The daemon specific file, which contains the Resource
+ *     definitions as well as any specific store routines
+ *     for the resource records.
+ *
+ *     Kern Sibbald, January MM, September MM
+ */
+
+/*
+   Copyright (C) 2000, 2001 Kern Sibbald and John Walker
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License
+   as published by the Free Software Foundation; either version 2
+   of the License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include "bacula.h"
+#include "console_conf.h"
+
+/* Define the first and last resource ID record
+ * types. Note, these should be unique for each
+ * daemon though not a requirement.
+ */
+int r_first = R_FIRST;
+int r_last  = R_LAST;
+
+/* Forward referenced subroutines */
+
+
+/* We build the current resource here as we are
+ * scanning the resource configuration definition,
+ * then move it to allocated memory when the resource
+ * scan is complete.
+ */
+URES res_all;
+int  res_all_size = sizeof(res_all);
+
+/* Definition of records permitted within each
+ * resource with the routine to process the record 
+ * information.
+ */ 
+
+/*  Console "globals" */
+static RES_ITEM cons_items[] = {
+   {"name",        store_name,     ITEM(res_cons.hdr.name), 0, ITEM_REQUIRED, 0},
+   {"description", store_str,      ITEM(res_cons.hdr.desc), 0, 0, 0},
+   {"rcfile",      store_dir,      ITEM(res_cons.rc_file), 0, 0, 0},
+   {"historyfile", store_dir,      ITEM(res_cons.hist_file), 0, 0, 0},
+   {"requiressl",  store_yesno,    ITEM(res_cons.require_ssl), 1, ITEM_DEFAULT, 0},
+   {"password",    store_password, ITEM(res_cons.password), 0, ITEM_REQUIRED, 0},
+   {NULL, NULL, NULL, 0, 0, 0} 
+};
+
+
+/*  Director's that we can contact */
+static RES_ITEM dir_items[] = {
+   {"name",        store_name,     ITEM(res_dir.hdr.name), 0, ITEM_REQUIRED, 0},
+   {"description", store_str,      ITEM(res_dir.hdr.desc), 0, 0, 0},
+   {"dirport",     store_int,      ITEM(res_dir.DIRport),  0, ITEM_DEFAULT, 9101},
+   {"address",     store_str,      ITEM(res_dir.address),  0, 0, 0},
+   {"password",    store_password, ITEM(res_dir.password), 0, ITEM_REQUIRED, 0},
+   {"enablessl",   store_yesno,    ITEM(res_dir.enable_ssl), 1, ITEM_DEFAULT, 0},
+   {NULL, NULL, NULL, 0, 0, 0} 
+};
+
+/* 
+ * This is the master resource definition.  
+ * It must have one item for each of the resources.
+ */
+RES_TABLE resources[] = {
+   {"console",       cons_items,  R_CONSOLE,   NULL},
+   {"director",      dir_items,   R_DIRECTOR,  NULL},
+   {NULL,           NULL,        0,           NULL}
+};
+
+
+/* Dump contents of resource */
+void dump_resource(int type, RES *reshdr, void sendit(void *sock, char *fmt, ...), void *sock)
+{
+   URES *res = (URES *)reshdr;
+   int recurse = 1;
+
+   if (res == NULL) {
+      printf("No record for %d %s\n", type, res_to_str(type));
+      return;
+   }
+   if (type < 0) {                   /* no recursion */
+      type = - type;
+      recurse = 0;
+   }
+   switch (type) {
+      case R_CONSOLE:
+         printf("Console: name=%s rcfile=%s histfile=%s\n", reshdr->name,
+               res->res_cons.rc_file, res->res_cons.hist_file);
+        break;
+      case R_DIRECTOR:
+         printf("Director: name=%s address=%s DIRport=%d\n", reshdr->name, 
+                res->res_dir.address, res->res_dir.DIRport);
+        break;
+      default:
+         printf("Unknown resource type %d\n", type);
+   }
+   if (recurse && res->res_dir.hdr.next) {
+      dump_resource(type, res->res_dir.hdr.next, sendit, sock);
+   }
+}
+
+/* 
+ * Free memory of resource.  
+ * NB, we don't need to worry about freeing any references
+ * to other resources as they will be freed when that 
+ * resource chain is traversed.  Mainly we worry about freeing
+ * allocated strings (names).
+ */
+void free_resource(RES *sres, int type)
+{
+   RES *nres;
+   URES *res = (URES *)sres;
+
+   if (res == NULL)
+      return;
+
+   /* common stuff -- free the resource name */
+   nres = (RES *)res->res_dir.hdr.next;
+   if (res->res_dir.hdr.name) {
+      free(res->res_dir.hdr.name);
+   }
+   if (res->res_dir.hdr.desc) {
+      free(res->res_dir.hdr.desc);
+   }
+
+   switch (type) {
+      case R_CONSOLE:
+        if (res->res_cons.rc_file) {
+           free(res->res_cons.rc_file);
+        }
+        if (res->res_cons.hist_file) {
+           free(res->res_cons.hist_file);
+        }
+      case R_DIRECTOR:
+        if (res->res_dir.address)
+           free(res->res_dir.address);
+        break;
+      default:
+         printf("Unknown resource type %d\n", type);
+   }
+   /* Common stuff again -- free the resource, recurse to next one */
+   free(res);
+   if (nres) {
+      free_resource(nres, type);
+   }
+}
+
+/* Save the new resource by chaining it into the head list for
+ * the resource. If this is pass 2, we update any resource
+ * pointers (currently only in the Job resource).
+ */
+void save_resource(int type, RES_ITEM *items, int pass)
+{
+   URES *res;
+   int rindex = type - r_first;
+   int i, size;    
+   int error = 0;
+
+   /* 
+    * Ensure that all required items are present
+    */
+   for (i=0; items[i].name; i++) {
+      if (items[i].flags & ITEM_REQUIRED) {
+           if (!bit_is_set(i, res_all.res_dir.hdr.item_present)) {  
+               Emsg2(M_ABORT, 0, "%s item is required in %s resource, but not found.\n",
+                items[i].name, resources[rindex]);
+            }
+      }
+   }
+
+   /* During pass 2, we looked up pointers to all the resources
+    * referrenced in the current resource, , now we
+    * must copy their address from the static record to the allocated
+    * record.
+    */
+   if (pass == 2) {
+      switch (type) {
+        /* Resources not containing a resource */
+        case R_CONSOLE:
+        case R_DIRECTOR:
+           break;
+
+        default:
+            Emsg1(M_ERROR, 0, "Unknown resource type %d\n", type);
+           error = 1;
+           break;
+      }
+      /* Note, the resoure name was already saved during pass 1,
+       * so here, we can just release it.
+       */
+      if (res_all.res_dir.hdr.name) {
+        free(res_all.res_dir.hdr.name);
+        res_all.res_dir.hdr.name = NULL;
+      }
+      if (res_all.res_dir.hdr.desc) {
+        free(res_all.res_dir.hdr.desc);
+        res_all.res_dir.hdr.desc = NULL;
+      }
+      return;
+   }
+
+   /* The following code is only executed during pass 1 */
+   switch (type) {
+      case R_CONSOLE:
+        size = sizeof(CONRES);
+        break;
+      case R_DIRECTOR:
+        size = sizeof(DIRRES);
+        break;
+      default:
+         printf("Unknown resource type %d\n", type);
+        error = 1;
+        size = 1;
+        break;
+   }
+   /* Common */
+   if (!error) {
+      res = (URES *)malloc(size);
+      memcpy(res, &res_all, size);
+      if (!resources[rindex].res_head) {
+        resources[rindex].res_head = (RES *)res; /* store first entry */
+      } else {
+        RES *next;
+        for (next=resources[rindex].res_head; next->next; next=next->next) {
+           if (strcmp(next->name, res->res_dir.hdr.name) == 0) {
+              Emsg2(M_ERROR_TERM, 0,
+                  _("Attempt to define second %s resource named \"%s\" is not permitted.\n"),
+                 resources[rindex].name, res->res_dir.hdr.name);
+           }
+        }
+        next->next = (RES *)res;
+         Dmsg2(90, "Inserting %s res: %s\n", res_to_str(type),
+              res->res_dir.hdr.name);
+      }
+   }
+}
diff --git a/bacula/src/wx-console/console_conf.h b/bacula/src/wx-console/console_conf.h
new file mode 100644 (file)
index 0000000..f5bf369
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+   Copyright (C) 2000-2004 Kern Sibbald and John Walker
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License
+   as published by the Free Software Foundation; either version 2
+   of the License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#ifndef CONSOLECONF_H
+#define CONSOLECONF_H
+
+#include "bacula.h"
+#include "jcr.h"
+
+/*
+ * Resource codes -- they must be sequential for indexing   
+ */
+#define R_FIRST                       1001
+
+#define R_CONSOLE                     1001
+#define R_DIRECTOR                    1002
+
+#define R_LAST                        R_DIRECTOR
+
+/*
+ * Some resource attributes
+ */
+#define R_NAME                        1020
+#define R_ADDRESS                     1021
+#define R_PASSWORD                    1022
+#define R_TYPE                        1023
+#define R_BACKUP                      1024
+
+
+/* Definition of the contents of each Resource */
+
+/* Console "globals" */
+struct s_res_con {
+   RES   hdr;
+   char *rc_file;                     /* startup file */
+   char *hist_file;                   /* command history file */
+   int require_ssl;                   /* Require SSL on all connections */
+   char *password;                    /* UA server password */
+};
+typedef struct s_res_con CONRES;
+
+/* Director */
+struct s_res_dir {
+   RES   hdr;
+   int   DIRport;                     /* UA server port */
+   char *address;                     /* UA server address */
+   char *password;                    /* UA server password */
+   int  enable_ssl;                   /* Use SSL */
+};
+typedef struct s_res_dir DIRRES;
+
+
+/* Define the Union of all the above
+ * resource structure definitions.
+ */
+union u_res {
+   struct s_res_dir     res_dir;
+   struct s_res_con     res_cons;
+   RES hdr;
+};
+
+typedef union u_res URES;
+
+#endif // CONSOLECONF_H
diff --git a/bacula/src/wx-console/console_thread.cpp b/bacula/src/wx-console/console_thread.cpp
new file mode 100644 (file)
index 0000000..a4ba832
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ *
+ *    Interaction thread between director and the GUI
+ *
+ *    Nicolas Boichat, April 2004
+ *
+ */
+/*
+   Copyright (C) 2004 Kern Sibbald and John Walker
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License
+   as published by the Free Software Foundation; either version 2
+   of the License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include "console_thread.h" // class's header file
+
+#include <wx/wxprec.h>
+
+#include <wx/thread.h>
+#include <bacula.h>
+#include <jcr.h>
+
+#include "console_conf.h"
+
+#include "csprint.h"
+
+/* Imported functions */
+int authenticate_director(JCR *jcr, DIRRES *director, CONRES *cons);
+
+// class constructor
+console_thread::console_thread() {
+   UA_sock = NULL;
+}
+
+// class destructor
+console_thread::~console_thread() {
+   if (UA_sock) {
+      bnet_sig(UA_sock, BNET_TERMINATE); /* send EOF */
+      bnet_close(UA_sock);
+      UA_sock = NULL;
+   }
+}
+
+/*
+ * Thread entry point
+ */
+void* console_thread::Entry() {
+   csprint("Connecting...\n");
+
+   init_stack_dump();
+   my_name_is(0, NULL, "console");
+   textdomain("bacula-console");
+   init_msg(NULL, NULL);
+
+   /* TODO (#4#): Allow the user to choose his config file. */
+   parse_config("./console.conf");
+
+   LockRes();
+   DIRRES *dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
+   UnlockRes();
+
+   memset(&jcr, 0, sizeof(jcr));
+
+   UA_sock = bnet_connect(&jcr, 5, 15, "Director daemon", dir->address, NULL, dir->DIRport, 0);
+   if (UA_sock == NULL) {
+      csprint("NULL\n");
+      return NULL;
+   }
+
+   csprint("Connected\n");
+
+   jcr.dir_bsock = UA_sock;
+   if (!authenticate_director(&jcr, dir, NULL)) {
+      csprint("ERR=");
+      csprint(UA_sock->msg);
+      return NULL;
+   }
+
+   Write("messages\n");
+
+   int stat;
+
+   /* main loop */
+   while(!TestDestroy()) {   /* Tests if thread has been ended */
+      if ((stat = bnet_recv(UA_sock)) >= 0) {
+         csprint(UA_sock->msg);
+      }
+      else {
+         csprint(NULL, CS_END);
+      }
+
+      if (is_bnet_stop(UA_sock)) {
+         csprint(NULL, CS_END);
+         break;            /* error or term */
+      }
+   }
+
+   csprint("Connection terminated\n");
+
+   return 0;
+}
+
+void console_thread::Write(const char* str) {
+   if (UA_sock) {
+       UA_sock->msglen = strlen(str);
+       pm_strcpy(&UA_sock->msg, str);
+       bnet_send(UA_sock);
+   }
+}
+
+void console_thread::Delete() {
+   Write("quit\n");
+   if (UA_sock) {
+      bnet_sig(UA_sock, BNET_TERMINATE); /* send EOF */
+      bnet_close(UA_sock);
+      UA_sock = NULL;
+   }
+}
+
diff --git a/bacula/src/wx-console/console_thread.h b/bacula/src/wx-console/console_thread.h
new file mode 100644 (file)
index 0000000..f3a9cbc
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ *
+ *    Interaction thread between director and the GUI
+ *
+ *    Nicolas Boichat, April 2004
+ *
+ */
+/*
+   Copyright (C) 2000, 2001 Kern Sibbald and John Walker
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License
+   as published by the Free Software Foundation; either version 2
+   of the License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#ifndef CONSOLE_THREAD_H
+#define CONSOLE_THREAD_H
+
+#include <wx/wxprec.h>
+
+#include <wx/thread.h> // inheriting class's header file
+#include "bacula.h"
+#include "jcr.h"
+
+/**
+ * Console thread, does interaction between bacula routines and the GUI
+ */
+class console_thread : public wxThread
+{
+   public:
+      // class constructor
+      console_thread();
+      // class destructor
+      ~console_thread();
+
+      void* Entry();
+      void Write(const char* str);
+      virtual void Delete();
+   private:
+      BSOCK* UA_sock;
+      JCR jcr;
+};
+
+int pm_cst_strcpy(POOLMEM **pm, const char *str);
+
+#endif // CONSOLE_THREAD_H
+
diff --git a/bacula/src/wx-console/csprint.h b/bacula/src/wx-console/csprint.h
new file mode 100644 (file)
index 0000000..b84d99a
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ *
+ *    csprint header file, used by console_thread to send events back to the GUI.
+ *
+ *    Nicolas Boichat, April 2004
+ *
+ */
+/*
+   Copyright (C) 2000, 2001 Kern Sibbald and John Walker
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License
+   as published by the Free Software Foundation; either version 2
+   of the License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#ifndef CSPRINT_H
+#define CSPRINT_H
+
+#define CS_DATA   1 /* data has been received */
+#define CS_END    2 /* no data to receive anymore */
+#define CS_DEBUG  3 /* used to print debug messages */
+
+/* function called by console_thread to send events back to the GUI */
+void csprint(char* str, int status=CS_DATA);
+
+#endif
diff --git a/bacula/src/wx-console/main.cpp b/bacula/src/wx-console/main.cpp
new file mode 100644 (file)
index 0000000..b0e1349
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ *
+ *    Bacula wx GUI application
+ *
+ *    Nicolas Boichat, April 2004
+ *
+ */
+/*
+   Copyright (C) 2004 Kern Sibbald and John Walker
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License
+   as published by the Free Software Foundation; either version 2
+   of the License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+// ============================================================================
+// declarations
+// ============================================================================
+
+// ----------------------------------------------------------------------------
+// headers
+// ----------------------------------------------------------------------------
+
+#include "wx/wxprec.h"
+
+#ifdef __BORLANDC__
+   #pragma hdrstop
+#endif
+
+// for all others, include the necessary headers (this file is usually all you
+// need because it includes almost all "standard" wxWindows headers)
+#ifndef WX_PRECOMP
+   #include "wx/wx.h"
+#endif
+
+#include "wxbmainframe.h"
+
+#include "csprint.h"
+
+// ----------------------------------------------------------------------------
+// resources
+// ----------------------------------------------------------------------------
+
+
+class MyApp : public wxApp
+{
+public:
+   virtual bool OnInit();
+};
+
+IMPLEMENT_APP(MyApp)
+
+// ============================================================================
+// implementation
+// ============================================================================
+
+// ----------------------------------------------------------------------------
+// the application class
+// ----------------------------------------------------------------------------
+
+// 'Main program' equivalent: the program execution "starts" here
+bool MyApp::OnInit()
+{
+   wxbMainFrame *frame = wxbMainFrame::CreateInstance(_T("Minimal wxWindows App"),
+                         wxPoint(50, 50), wxSize(780, 500));
+
+   frame->Show(TRUE);
+
+   frame->StartConsoleThread();
+
+   csprint("Console started !\n");
+
+   return TRUE;
+}
diff --git a/bacula/src/wx-console/marked.xpm b/bacula/src/wx-console/marked.xpm
new file mode 100644 (file)
index 0000000..7aac244
--- /dev/null
@@ -0,0 +1,39 @@
+/* XPM */
+static char *marked_xpm[] = {
+/* columns rows colors chars-per-pixel */
+"16 16 16 1",
+"  c Gray0",
+". c #bf0000",
+"X c #00bf00",
+"o c #bfbf00",
+"O c #0000bf",
+"+ c #bf00bf",
+"@ c #00bfbf",
+"# c None",
+"$ c #808080",
+"% c Red",
+"& c Green",
+"* c Yellow",
+"= c Blue",
+"- c Magenta",
+"; c Cyan",
+": c Gray100",
+/* pixels */
+"################",
+"################",
+"################",
+"################",
+"############&&&#",
+"###########&&&##",
+"#########&&&&###",
+"########&&&&####",
+"##&&&##&&&&#####",
+"###&&&&&&&######",
+"####&&&&&#######",
+"#####&&&########",
+"######&#########",
+"################",
+"################",
+"################"
+};
+
diff --git a/bacula/src/wx-console/partmarked.xpm b/bacula/src/wx-console/partmarked.xpm
new file mode 100644 (file)
index 0000000..23e1818
--- /dev/null
@@ -0,0 +1,39 @@
+/* XPM */
+static char *partmarked_xpm[] = {
+/* columns rows colors chars-per-pixel */
+"16 16 16 1",
+"  c Gray0",
+". c #bf0000",
+"X c #00bf00",
+"o c #bfbf00",
+"O c #0000bf",
+"+ c #bf00bf",
+"@ c #00bfbf",
+"# c None",
+"$ c #808080",
+"% c Red",
+"& c Green",
+"* c Yellow",
+"= c Blue",
+"- c Magenta",
+"; c Cyan",
+": c Gray100",
+/* pixels */
+"################",
+"################",
+"################",
+"##%%%######%%%##",
+"###%%%####%%%###",
+"####%%%##%%%####",
+"#####%%%%%%#####",
+"######%%%%######",
+"######%%%%######",
+"#####%%%%%%#####",
+"####%%%##%%%####",
+"###%%%####%%%#&&",
+"##%%%######%%&&#",
+"########&&##&&##",
+"#########&&&&###",
+"##########&&####"
+};
+
diff --git a/bacula/src/wx-console/unmarked.xpm b/bacula/src/wx-console/unmarked.xpm
new file mode 100644 (file)
index 0000000..fc624bc
--- /dev/null
@@ -0,0 +1,39 @@
+/* XPM */
+static char *unmarked_xpm[] = {
+/* columns rows colors chars-per-pixel */
+"16 16 16 1",
+"  c Gray0",
+". c #bf0000",
+"X c #00bf00",
+"o c #bfbf00",
+"O c #0000bf",
+"+ c #bf00bf",
+"@ c #00bfbf",
+"# c None",
+"$ c #808080",
+"% c Red",
+"& c Green",
+"* c Yellow",
+"= c Blue",
+"- c Magenta",
+"; c Cyan",
+": c Gray100",
+/* pixels */
+"################",
+"################",
+"################",
+"##%%%######%%%##",
+"###%%%####%%%###",
+"####%%%##%%%####",
+"#####%%%%%%#####",
+"######%%%%######",
+"######%%%%######",
+"#####%%%%%%#####",
+"####%%%##%%%####",
+"###%%%####%%%###",
+"##%%%######%%%##",
+"################",
+"################",
+"################"
+};
+
diff --git a/bacula/src/wx-console/wxbmainframe.cpp b/bacula/src/wx-console/wxbmainframe.cpp
new file mode 100644 (file)
index 0000000..e763fec
--- /dev/null
@@ -0,0 +1,401 @@
+/*
+ *
+ *   Main frame
+ *
+ *    Nicolas Boichat, April 2004
+ *
+ */
+/*
+   Copyright (C) 2004 Kern Sibbald and John Walker
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License
+   as published by the Free Software Foundation; either version 2
+   of the License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include "wxbmainframe.h" // class's header file
+
+#include "wxbrestorepanel.h"
+
+#include "csprint.h"
+
+#include "wxwin16x16.xpm"
+
+// ----------------------------------------------------------------------------
+// event tables and other macros for wxWindows
+// ----------------------------------------------------------------------------
+
+// ----------------------------------------------------------------------------
+// constants
+// ----------------------------------------------------------------------------
+
+// IDs for the controls and the menu commands
+enum
+{
+   // menu items
+   Minimal_Quit = 1,
+
+   // it is important for the id corresponding to the "About" command to have
+   // this standard value as otherwise it won't be handled properly under Mac
+   // (where it is special and put into the "Apple" menu)
+   Minimal_About = wxID_ABOUT,
+   TypeText = 2,
+   Thread = 3
+};
+
+/*
+ *   wxbTHREAD_EVENT declaration, used by csprint
+ */
+BEGIN_DECLARE_EVENT_TYPES()
+   DECLARE_EVENT_TYPE(wxbTHREAD_EVENT,       1)
+END_DECLARE_EVENT_TYPES()
+
+DEFINE_EVENT_TYPE(wxbTHREAD_EVENT)
+
+// the event tables connect the wxWindows events with the functions (event
+// handlers) which process them. It can be also done at run-time, but for the
+// simple menu events like this the static method is much simpler.
+BEGIN_EVENT_TABLE(wxbMainFrame, wxFrame)
+   EVT_MENU(Minimal_Quit,  wxbMainFrame::OnQuit)
+   EVT_MENU(Minimal_About, wxbMainFrame::OnAbout)
+   EVT_TEXT_ENTER(TypeText, wxbMainFrame::OnEnter)
+   EVT_CUSTOM(wxbTHREAD_EVENT, Thread, wxbMainFrame::OnPrint)
+END_EVENT_TABLE()
+
+// ----------------------------------------------------------------------------
+// wxbThreadEvent
+// ----------------------------------------------------------------------------
+
+/*
+ *  wxbThreadEvent constructor
+ */
+wxbThreadEvent::wxbThreadEvent(int id): wxEvent(id, wxbTHREAD_EVENT) {
+   m_eventObject = NULL;
+}
+
+/*
+ *  wxbThreadEvent destructor
+ */
+wxbThreadEvent::~wxbThreadEvent()
+{
+   if (m_eventObject != NULL) {
+      delete m_eventObject;
+   }
+}
+
+/*
+ *  wxbThreadEvent copy constructor
+ */
+wxbThreadEvent::wxbThreadEvent(const wxbThreadEvent& te)
+{
+   this->m_eventType = te.m_eventType;
+   this->m_id = te.m_id;
+   if (te.m_eventObject != NULL) {
+      this->m_eventObject = new wxbPrintObject(*((wxbPrintObject*)te.m_eventObject));
+   }
+   else {
+      this->m_eventObject = NULL;
+   }
+   this->m_skipped = te.m_skipped;
+   this->m_timeStamp = te.m_timeStamp;
+}
+
+/*
+ *  Must be implemented (abstract in wxEvent)
+ */
+wxEvent* wxbThreadEvent::Clone() const
+{
+   return new wxbThreadEvent(*this);
+}
+
+/*
+ *  Gets the wxbPrintObject attached to this event, containing data sent by director
+ */
+wxbPrintObject* wxbThreadEvent::GetEventPrintObject()
+{
+   return (wxbPrintObject*)m_eventObject;
+}
+
+/*
+ *  Sets the wxbPrintObject attached to this event
+ */
+void wxbThreadEvent::SetEventPrintObject(wxbPrintObject* object)
+{
+   m_eventObject = (wxObject*)object;
+}
+
+// ----------------------------------------------------------------------------
+// main frame
+// ----------------------------------------------------------------------------
+
+wxbMainFrame *wxbMainFrame::frame = NULL;
+
+/*
+ *  Singleton constructor
+ */
+wxbMainFrame* wxbMainFrame::CreateInstance(const wxString& title, const wxPoint& pos, const wxSize& size, long style)
+{
+   frame = new wxbMainFrame(title, pos, size, style);
+   return frame;
+}
+
+/*
+ *  Returns singleton instance
+ */
+wxbMainFrame* wxbMainFrame::GetInstance()
+{
+   return frame;
+}
+
+/*
+ *  Private destructor
+ */
+wxbMainFrame::~wxbMainFrame()
+{
+   ct->Delete();
+}
+
+/*
+ *  Private constructor
+ */
+wxbMainFrame::wxbMainFrame(const wxString& title, const wxPoint& pos, const wxSize& size, long style)
+      : wxFrame(NULL, -1, title, pos, size, style)
+{
+   ct = NULL;
+
+   // set the frame icon
+   SetIcon(wxIcon(wxwin16x16_xpm));
+
+#if wxUSE_MENUS
+   // create a menu bar
+   wxMenu *menuFile = new wxMenu;
+
+   // the "About" item should be in the help menu
+   wxMenu *helpMenu = new wxMenu;
+   helpMenu->Append(Minimal_About, _T("&About...\tF1"), _T("Show about dialog"));
+
+   menuFile->Append(Minimal_Quit, _T("E&xit\tAlt-X"), _T("Quit this program"));
+
+   // now append the freshly created menu to the menu bar...
+   wxMenuBar *menuBar = new wxMenuBar();
+   menuBar->Append(menuFile, _T("&File"));
+   menuBar->Append(helpMenu, _T("&Help"));
+
+   // ... and attach this menu bar to the frame
+   SetMenuBar(menuBar);
+#endif // wxUSE_MENUS
+
+#if wxUSE_STATUSBAR
+   // create a status bar just for fun (by default with 1 pane only)
+   CreateStatusBar(2);
+   SetStatusText(_T("Welcome to Bacula wx-GUI!"));
+#endif // wxUSE_STATUSBAR
+
+   wxPanel* global = new wxPanel(this, -1);
+
+   notebook = new wxNotebook(global, -1);
+
+   /* Console */
+
+   wxPanel* consolePanel = new wxPanel(notebook, -1);
+   notebook->AddPage(consolePanel, "Console");
+
+   consoleCtrl = new wxTextCtrl(consolePanel,-1,"",wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE | wxTE_READONLY | wxTE_RICH);
+   consoleCtrl->SetDefaultStyle(wxTextAttr(*wxBLACK, wxNullColour, wxFont(10, wxMODERN, wxNORMAL, wxNORMAL)));
+
+   typeCtrl = new wxTextCtrl(consolePanel,TypeText,"",wxDefaultPosition,wxSize(200,20), wxTE_PROCESS_ENTER);
+
+   wxFlexGridSizer *consoleSizer = new wxFlexGridSizer(2, 1, 0, 0);
+   consoleSizer->AddGrowableCol(0);
+   consoleSizer->AddGrowableRow(0);
+
+   consoleSizer->Add(consoleCtrl, 1, wxEXPAND | wxALL, 0);
+   consoleSizer->Add(typeCtrl, 0, wxEXPAND | wxALL, 0);
+
+   consolePanel->SetAutoLayout( TRUE );
+   consolePanel->SetSizer( consoleSizer );
+   consoleSizer->SetSizeHints( consolePanel );
+
+   // Creates the list of panels which are included in notebook, and that need to receive director information
+
+   panels = new (wxbPanel*)[2];
+   panels[0] = new wxbRestorePanel(notebook);
+   panels[1] = NULL;
+
+   for (int i = 0; panels[i] != NULL; i++) {
+      notebook->AddPage(panels[i], panels[i]->GetTitle());
+   }
+
+   wxBoxSizer* globalSizer = new wxBoxSizer(wxHORIZONTAL);
+
+   globalSizer->Add(new wxNotebookSizer(notebook), 1, wxEXPAND, 0);
+
+   global->SetSizer( globalSizer );
+   globalSizer->SetSizeHints( global );
+
+   wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
+
+   sizer->Add(global, 1, wxEXPAND | wxALL, 0);
+   SetAutoLayout(true);
+   SetSizer( sizer );
+   //sizer->SetSizeHints( this );
+}
+
+/*
+ *  Starts the thread interacting with the director
+ */
+void wxbMainFrame::StartConsoleThread()
+{
+   if (ct != NULL) {
+      ct->Delete();
+   }
+   ct = new console_thread();
+   ct->Create();
+   ct->Run();
+}
+
+// event handlers
+
+void wxbMainFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
+{
+   // TRUE is to force the frame to close
+   Close(TRUE);
+}
+
+void wxbMainFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
+{
+   wxString msg;
+   msg.Printf( _T("Welcome to Bacula wx-GUI.\n (c) 2004 Nicolas Boichat <nicolas@boichat.ch>\n")
+            _T("Version : %s"), wxVERSION_STRING);
+
+   wxMessageBox(msg, _T("About Bacula-wx-GUI"), wxOK | wxICON_INFORMATION, this);
+}
+
+void wxbMainFrame::OnEnter(wxCommandEvent& WXUNUSED(event))
+{
+   wxString str = typeCtrl->GetValue() + "\n";
+   Send(str);
+}
+
+/*
+ *  Called when data is arriving from director
+ */
+void wxbMainFrame::OnPrint(wxbThreadEvent& event) {
+   wxbPrintObject* po = event.GetEventPrintObject();
+
+   Print(po->str, po->status);
+}
+
+/*
+ *  Prints data received from director to the console, and forwards it to the panels
+ */
+void wxbMainFrame::Print(wxString str, int status)
+{
+   // CS_DEBUG is often sent by panels, so resend it to them would cause an infinite loop
+   if (status != CS_DEBUG) {
+      for (int i = 0; panels[i] != NULL; i++) {
+         panels[i]->Print(str, status);
+       }
+   }
+
+   if (status == CS_END) {
+      str = "#";
+   }
+
+   consoleCtrl->SetDefaultStyle(wxTextAttr(*wxBLACK));
+   (*consoleCtrl) << str;
+   consoleCtrl->SetInsertionPointEnd();
+}
+
+/*
+ *  Sends data to the director
+ */
+void wxbMainFrame::Send(wxString str)
+{
+   ct->Write((const char*)str);
+   typeCtrl->SetValue("");
+   consoleCtrl->SetDefaultStyle(wxTextAttr(*wxRED));
+   (*consoleCtrl) << str;
+}
+
+/*
+ *  Used by csprint, which is called by console thread.
+ *
+ *  In GTK and perhaps X11, only the main thread is allowed to interact with
+ *  graphical components, so by firing an event, the main loop will call OnPrint.
+ *
+ *  Calling OnPrint directly from console thread produces "unexpected async replies".
+ */
+void firePrintEvent(wxString str, int status)
+{
+   wxbPrintObject* po = new wxbPrintObject(str, status);
+
+   wxbThreadEvent evt(Thread);
+   evt.SetEventPrintObject(po);
+
+   wxbMainFrame::GetInstance()->AddPendingEvent(evt);
+}
+
+wxString csBuffer; /* Temporary buffer for receiving data from console thread */
+
+/*
+ *  Called by console thread, this function forwards data line by line and end
+ *  signals to the GUI.
+ */
+void csprint(char* str, int status)
+{
+   if (str != 0) {
+      int len = strlen(str);
+      bool allnewline = true;
+      for (int i = 0; i < len; i++) {
+      if (!(allnewline = (str[i] == '\n')))
+      break;
+      }
+
+      if (allnewline) {
+         firePrintEvent(csBuffer << "\n", CS_DATA);
+         csBuffer = "";
+         for (int i = 1; i < len; i++) {
+            firePrintEvent("\n", status);
+         }
+      }
+      else {
+         wxStringTokenizer tkz(str, "\n", wxTOKEN_RET_DELIMS | wxTOKEN_RET_EMPTY | wxTOKEN_RET_EMPTY_ALL);
+
+         while ( tkz.HasMoreTokens() ) {
+            csBuffer << tkz.GetNextToken();
+            if (csBuffer.Length() != 0) {
+               if ((csBuffer.GetChar(csBuffer.Length()-1) == '\n') ||
+                  (csBuffer.GetChar(csBuffer.Length()-1) == '\r')) {
+                  firePrintEvent(csBuffer, status);
+                  csBuffer = "";
+               }
+            }
+         }
+      }
+
+      if (csBuffer == "$ ") { // Restore console
+         firePrintEvent(csBuffer, status);
+         csBuffer = "";
+      }
+   }
+
+   if (status == CS_END) {
+      if (csBuffer.Length() != 0) {
+         firePrintEvent(csBuffer, CS_DATA);
+      }
+      csBuffer = "";
+      firePrintEvent("", CS_END);
+   }
+}
+
diff --git a/bacula/src/wx-console/wxbmainframe.h b/bacula/src/wx-console/wxbmainframe.h
new file mode 100644 (file)
index 0000000..9f9022e
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+   Copyright (C) 2004 Kern Sibbald and John Walker
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License
+   as published by the Free Software Foundation; either version 2
+   of the License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#ifndef WXBMAINFRAME_H
+#define WXBMAINFRAME_H
+
+#include "wx/wxprec.h"
+
+#ifdef __BORLANDC__
+   #pragma hdrstop
+#endif
+
+// for all others, include the necessary headers (this file is usually all you
+// need because it includes almost all "standard" wxWindows headers)
+#ifndef WX_PRECOMP
+   #include "wx/wx.h"
+#endif
+
+#include <wx/textctrl.h>
+#include <wx/tokenzr.h>
+#include <wx/notebook.h>
+
+//#include "bacula.h"
+//#include "console_conf.h"
+
+#include "console_thread.h"
+
+#include "wxbpanel.h"
+
+// ----------------------------------------------------------------------------
+// wxbPrintObject - Used by wxbThreadEvent to contain data sent by director
+// ----------------------------------------------------------------------------
+
+class wxbPrintObject: public wxObject {
+   public:
+      wxString str;
+      int status;
+      wxbPrintObject(wxString str, int status): wxObject() {
+         this->str = str;
+         this->status = status;
+      }
+
+      wxbPrintObject(const wxbPrintObject& pe) {
+         this->str = pe.str;
+         this->status = pe.status;
+      }
+};
+
+// ----------------------------------------------------------------------------
+// wxbThreadEvent - Event used by wxbTHREAD_EVENT
+// ----------------------------------------------------------------------------
+
+class wxbThreadEvent: public wxEvent {
+   public:
+      wxbThreadEvent(int id);
+      ~wxbThreadEvent();
+      wxbThreadEvent(const wxbThreadEvent& te);
+      virtual wxEvent *Clone() const;
+      wxbPrintObject* GetEventPrintObject();
+      void SetEventPrintObject(wxbPrintObject* object);
+};
+
+// Define a new frame type: this is going to be our main frame
+class wxbMainFrame : public wxFrame
+{
+public:
+   /* this class is a singleton */
+   static wxbMainFrame* CreateInstance(const wxString& title, const wxPoint& pos, const wxSize& size, long style = wxDEFAULT_FRAME_STYLE);
+   static wxbMainFrame* GetInstance();
+
+    // event handlers (these functions should _not_ be virtual)
+   void OnQuit(wxCommandEvent& event);
+   void OnAbout(wxCommandEvent& event);
+   void OnEnter(wxCommandEvent& event);
+   void OnPrint(wxbThreadEvent& event);
+
+   /*
+    *  Prints data received from director to the console,
+    *  and forwards it to the panels
+    */
+   void Print(wxString str, int status);
+
+   /* Sends data to the director */
+   void Send(wxString str);
+
+   /*
+    *  Starts the thread interacting with the director
+    */
+   void StartConsoleThread();
+
+private:
+   /* private constructor, singleton */
+   wxbMainFrame(const wxString& title, const wxPoint& pos, const wxSize& size, long style);
+   ~wxbMainFrame();
+
+   wxNotebook *notebook; /* main notebook */
+   wxTextCtrl *typeCtrl; /* wxTextCtrl for console user input */
+   wxTextCtrl *consoleCtrl; /* wxTextCtrl containing graphical console */
+
+   wxbPanel **panels; /* panels array, contained in the notebook, and which need to receive console communication */
+
+   console_thread* ct; /* thread interacting with the director */
+
+   static wxbMainFrame *frame; /* this */
+
+   // any class wishing to process wxWindows events must use this macro
+   DECLARE_EVENT_TABLE()
+};
+
+#endif // WXBMAINFRAME_H
+
diff --git a/bacula/src/wx-console/wxbrestorepanel.cpp b/bacula/src/wx-console/wxbrestorepanel.cpp
new file mode 100644 (file)
index 0000000..1ab4efb
--- /dev/null
@@ -0,0 +1,900 @@
+/*
+   Copyright (C) 2004 Kern Sibbald and John Walker
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License
+   as published by the Free Software Foundation; either version 2
+   of the License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include "wxbrestorepanel.h"
+
+#include "wxbmainframe.h"
+
+#include "csprint.h"
+
+#include <wx/choice.h>
+#include <wx/datetime.h>
+
+#include "unmarked.xpm"
+#include "marked.xpm"
+#include "partmarked.xpm"
+
+/*
+ *  Class which is stored in the tree and in the list to keep informations
+ *  about the element.
+ */
+class wxbTreeItemData : public wxTreeItemData {
+   public:
+      wxbTreeItemData(wxString path, wxString name, int marked);
+      wxbTreeItemData(wxString path, wxString name, wxString marked);
+      ~wxbTreeItemData();
+      wxString GetPath();
+      wxString GetName();
+      //wxbTreeItemData* GetChild(wxString dirname);
+      int GetMarked();
+      void SetMarked(int marked);
+      void SetMarked(wxString marked);
+
+      static int GetMarkedStatus(wxString file);
+   private:
+      wxString* path; /* Full path */
+      wxString* name; /* File name */
+      int marked; /* 0 - Not Marked, 1 - Marked, 2 - Some file under is marked */
+};
+
+wxbTreeItemData::wxbTreeItemData(wxString path, wxString name, int marked): wxTreeItemData() {
+   this->path = new wxString(path);
+   this->name = new wxString(name);
+   this->marked = marked;
+}
+
+wxbTreeItemData::wxbTreeItemData(wxString path, wxString name, wxString marked): wxTreeItemData() {
+   this->path = new wxString(path);
+   this->name = new wxString(name);
+   SetMarked(marked);
+}
+
+wxbTreeItemData::~wxbTreeItemData() {
+   delete path;
+   delete name;
+}
+
+int wxbTreeItemData::GetMarked() {
+   return marked;
+}
+
+void wxbTreeItemData::SetMarked(wxString marked) {
+   if (marked == "*") {
+      this->marked = 1;
+   }
+   else if (marked == "+") {
+      this->marked = 2;
+   }
+   else {
+      this->marked = 0;
+   }
+}
+
+void wxbTreeItemData::SetMarked(int marked) {
+   this->marked = marked;
+}
+
+wxString wxbTreeItemData::GetPath() {
+   return *path;
+}
+
+wxString wxbTreeItemData::GetName() {
+   return *name;
+}
+
+/*wxbTreeItemData* wxbTreeItemData::GetChild(wxString dirname) {
+   int marked = GetMarkedStatus(dirname);
+   return new wxbTreeItemData(path + (marked ? dirname.Mid(1) : dirname), marked);
+}*/
+
+int wxbTreeItemData::GetMarkedStatus(wxString file) {
+   if (file.Length() == 0)
+      return 0;
+   
+   switch (file.GetChar(0)) {
+       case '*':
+          return 1;
+       case '+':
+          return 2;
+       default:
+          return 0;
+    }
+}
+
+// ----------------------------------------------------------------------------
+// event tables and other macros for wxWindows
+// ----------------------------------------------------------------------------
+
+enum
+{
+   RestoreStart = 1,
+   TreeCtrl = 2,
+   ListCtrl = 3,
+   ClientChoice = 4
+};
+
+BEGIN_EVENT_TABLE(wxbRestorePanel, wxPanel)
+   EVT_BUTTON(RestoreStart, wxbRestorePanel::OnStart)
+   EVT_TREE_SEL_CHANGING(TreeCtrl, wxbRestorePanel::OnTreeChanging)
+   EVT_TREE_SEL_CHANGED(TreeCtrl, wxbRestorePanel::OnTreeChanged)
+   EVT_TREE_ITEM_EXPANDING(TreeCtrl, wxbRestorePanel::OnTreeExpanding)
+   EVT_TREE_ITEM_RIGHT_CLICK(TreeCtrl, wxbRestorePanel::OnTreeRightClicked)
+   EVT_LIST_ITEM_RIGHT_CLICK(ListCtrl, wxbRestorePanel::OnListRightClicked)
+   EVT_LIST_ITEM_ACTIVATED(ListCtrl, wxbRestorePanel::OnListActivated)
+   EVT_CHOICE(ClientChoice, wxbRestorePanel::OnClientChoiceChanged)
+END_EVENT_TABLE()
+
+/*
+ *  wxbRestorePanel constructor
+ */
+wxbRestorePanel::wxbRestorePanel(wxWindow* parent): wxbPanel(parent) {
+   imagelist = new wxImageList(16, 16, TRUE, 3);
+   imagelist->Add(wxIcon(unmarked_xpm));
+   imagelist->Add(wxIcon(marked_xpm));
+   imagelist->Add(wxIcon(partmarked_xpm));
+
+   wxFlexGridSizer *sizer = new wxFlexGridSizer(3, 1, 10, 10);
+   sizer->AddGrowableCol(0);
+   sizer->AddGrowableRow(1);
+
+   wxBoxSizer *firstSizer = new wxBoxSizer(wxHORIZONTAL);
+
+   start = new wxButton(this, RestoreStart, "Enter restore mode", wxDefaultPosition, wxSize(150, 30));
+   firstSizer->Add(start, 1, wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 10);
+
+   wxString* elist = new wxString[1];
+
+   clientChoice = new wxChoice(this, ClientChoice, wxDefaultPosition, wxSize(150, 30), 0, elist);
+   firstSizer->Add(clientChoice, 1, wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 10);
+
+   jobChoice = new wxChoice(this, -1, wxDefaultPosition, wxSize(150, 30), 0, elist);
+   firstSizer->Add(jobChoice, 1, wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 10);
+
+   sizer->Add(firstSizer, 1, wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 10);
+
+   wxFlexGridSizer *secondSizer = new wxFlexGridSizer(1, 2, 10, 10);
+
+   tree = new wxTreeCtrl(this, TreeCtrl, wxDefaultPosition, wxSize(250, 10));
+   secondSizer->Add(tree, 1, wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL | wxEXPAND, 10);
+
+   tree->SetImageList(imagelist);
+
+   list = new wxListCtrl(this, ListCtrl, wxDefaultPosition, wxDefaultSize, wxLC_REPORT);
+   secondSizer->Add(list, 1, wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL | wxEXPAND, 10);
+
+   list->SetImageList(imagelist, wxIMAGE_LIST_SMALL);
+
+   wxListItem info;
+   info.SetMask(wxLIST_MASK_TEXT | wxLIST_MASK_FORMAT);
+   info.SetText("M");
+   info.SetAlign(wxLIST_FORMAT_CENTER);
+   list->InsertColumn(0, info);
+   
+   info.SetText("Filename");
+   info.SetAlign(wxLIST_FORMAT_LEFT);
+   list->InsertColumn(1, info);
+
+   info.SetText("Size");
+   info.SetAlign(wxLIST_FORMAT_RIGHT);   
+   list->InsertColumn(2, info);
+
+   info.SetText("Date");
+   info.SetAlign(wxLIST_FORMAT_LEFT);
+   list->InsertColumn(3, info);
+
+   info.SetText("Perm.");
+   info.SetAlign(wxLIST_FORMAT_LEFT);
+   list->InsertColumn(4, info);
+   
+   info.SetText("User");
+   info.SetAlign(wxLIST_FORMAT_RIGHT);
+   list->InsertColumn(5, info);
+   
+   info.SetText("Group");
+   info.SetAlign(wxLIST_FORMAT_RIGHT);
+   list->InsertColumn(6, info);
+
+   secondSizer->AddGrowableCol(1);
+   secondSizer->AddGrowableRow(0);
+
+   sizer->Add(secondSizer, 1, wxEXPAND, 10);
+
+   gauge = new wxGauge(this, -1, 1, wxDefaultPosition, wxSize(200,20));
+
+   sizer->Add(gauge, 1, wxEXPAND, 5);
+   gauge->SetValue(0);
+   gauge->Enable(false);
+
+   SetSizer(sizer);
+   sizer->SetSizeHints(this);
+
+   setStatus(disabled);
+
+   tableParser = NULL;
+
+   jobChoice->Enable(false);
+
+   working = false;
+}
+
+/*
+ *  wxbRestorePanel destructor
+ */
+wxbRestorePanel::~wxbRestorePanel() {
+   delete imagelist;
+}
+
+/*----------------------------------------------------------------------------
+   wxbPanel overloadings
+  ----------------------------------------------------------------------------*/
+
+wxString wxbRestorePanel::GetTitle() {
+   return "Restore";
+}
+
+void wxbRestorePanel::Print(wxString str, int stat) {
+   if (str == "$ ") {
+      ended = true;
+   }
+   else if (status == listing) {
+      if (str.Find("cwd is:") == 0) { // Sometimes cd command result "infiltrate" into listings.
+         return;
+      }
+
+      str.RemoveLast();
+
+      wxString* file = ParseList(str);
+      
+      if (file == NULL)
+            return;
+
+      if (file[8].GetChar(file[8].Length()-1) == '/') {
+         wxString itemStr;
+
+         long cookie;
+         wxTreeItemId currentChild = tree->GetFirstChild(currentTreeItem, cookie);
+
+         bool updated = false;
+
+         while (currentChild.IsOk()) {
+            itemStr = tree->GetItemText(currentChild);
+            if (file[8] == itemStr) {
+               int stat = wxbTreeItemData::GetMarkedStatus(file[6]);
+               if (static_cast<wxbTreeItemData*>(tree->GetItemData(currentChild))->GetMarked() != stat) {
+                  tree->SetItemImage(currentChild, stat, wxTreeItemIcon_Normal);
+                  tree->SetItemImage(currentChild, stat, wxTreeItemIcon_Selected);
+                  static_cast<wxbTreeItemData*>(tree->GetItemData(currentChild))->SetMarked(file[6]);
+               }
+               updated = true;
+               break;
+            }
+            currentChild = tree->GetNextChild(currentTreeItem, cookie);
+         }
+
+         if (!updated) {
+            int img = wxbTreeItemData::GetMarkedStatus(file[6]);
+            tree->AppendItem(currentTreeItem, file[8], img, img, new wxbTreeItemData(file[7], file[8], file[6]));
+         }
+      }
+
+      if (updatelist) {
+         long ind = list->InsertItem(list->GetItemCount(), wxbTreeItemData::GetMarkedStatus(file[6]));
+         long data = (long)(new wxbTreeItemData(file[7], file[8], file[6]));
+         list->SetItemData(ind, data);
+         list->SetItem(ind, 1, file[8]); // filename
+         list->SetItem(ind, 2, file[4]); //Size
+         list->SetItem(ind, 3, file[5]); //date
+         list->SetItem(ind, 4, file[0]); //perm
+         list->SetItem(ind, 5, file[2]); //user
+         list->SetItem(ind, 6, file[3]); //grp
+      }
+
+      delete[] file;
+   }
+   else {
+      if (status == restoring) {
+         int i;
+         //15847 total files; 1 marked to be restored; 1,034 bytes.
+         if ((i = str.Find(" marked to be restored;")) > -1) {
+            int j = str.Find("; ");
+            str.Mid(j+2, i).ToLong(&totfilemessages);
+            //wxbMainFrame::GetInstance()->Print(wxString("TOT(") << totfilemessages << ")\n", CS_DEBUG);
+            return;
+         }
+
+         if ((i = str.Find(" files selected to be restored.")) > -1) {
+            str.Mid(0, i).ToLong(&totfilemessages);
+            //wxbMainFrame::GetInstance()->Print(wxString("TOT(") << totfilemessages << ")\n", CS_DEBUG);
+            return;
+         }
+
+         if ((i = str.Find(" file selected to be restored.")) > -1) {
+            str.Mid(0, i).ToLong(&totfilemessages);
+            //wxbMainFrame::GetInstance()->Print(wxString("TOT(") << totfilemessages << ")\n", CS_DEBUG);
+            return;
+         }
+
+         wxStringTokenizer tkz(str, " ", wxTOKEN_STRTOK);
+
+         wxDateTime datetime;
+
+         //   Date    Time   name:   perm      ?   user   grp      size    date     time
+         //04-Apr-2004 17:19 Tom-fd: -rwx------   1 nicolas  None     514967 2004-03-20 20:03:42  filename
+
+         if (datetime.ParseDate(tkz.GetNextToken()) != NULL) { // Date
+            if (datetime.ParseTime(tkz.GetNextToken()) != NULL) { // Time
+               if (tkz.GetNextToken().Last() == ':') { // name:
+               tkz.GetNextToken(); // perm
+               tkz.GetNextToken(); // ?
+               tkz.GetNextToken(); // user
+               tkz.GetNextToken(); // grp
+               tkz.GetNextToken(); // size
+               if (datetime.ParseDate(tkz.GetNextToken()) != NULL) { //date
+                     if (datetime.ParseTime(tkz.GetNextToken()) != NULL) { //time
+                        filemessages++;
+                        //wxbMainFrame::GetInstance()->Print(wxString("(") << filemessages << ")", CS_DEBUG);
+                        gauge->SetValue(filemessages);
+                     }
+                  }
+               }
+            }
+         }
+      }
+
+      if (tableParser != NULL) {
+         tableParser->Print(str, stat);
+      }
+      if (stat == CS_END) {
+         ended = true;
+      }
+   }
+}
+
+/*----------------------------------------------------------------------------
+   Commands called by events handler
+  ----------------------------------------------------------------------------*/
+
+/* The main button has been clicked */
+void wxbRestorePanel::CmdStart() {
+   if (status == disabled) {
+      CreateAndWaitForParser("list clients\n");
+
+      clientChoice->Clear();
+      for (unsigned int i = 0; i < tableParser->size(); i++) {
+         /*for (unsigned int k = 0; k < (*tableParser)[i].size(); k++) {
+            wxbMainFrame::GetInstance()->Print(wxString() << (*tableParser)[i][k] << ":", CS_DEBUG);
+         }
+         wxbMainFrame::GetInstance()->Print(wxString(";\n"), CS_DEBUG);*/
+         long* j = new long;
+         (*tableParser)[i][0].ToLong(j);
+         clientChoice->Append((*tableParser)[i][1], (void*)j);
+      }
+
+      setStatus(entered);
+   }
+   else if (status == entered) {
+      if (jobChoice->GetStringSelection().Length() < 1) {
+         wxbMainFrame::GetInstance()->SetStatusText("Please select a client.\n");
+         return;
+      }
+      WaitForEnd("restore\n");
+      WaitForEnd("6\n");
+      WaitForEnd(wxString() << jobChoice->GetStringSelection() << "\n");
+      WaitForEnd(wxString() << *((long*)clientChoice->GetClientData(clientChoice->GetSelection())) << "\n");
+      WaitForEnd("unmark *\n");
+      setStatus(choosing);
+      wxTreeItemId root = tree->AddRoot(clientChoice->GetStringSelection(), -1, -1, new wxbTreeItemData("/", clientChoice->GetStringSelection(), 0));
+      tree->Refresh();
+      WaitForList(root, true);
+      wxbMainFrame::GetInstance()->SetStatusText("Right click on a file or on a directory to add it to the restore list.\n");
+      tree->Expand(root);
+   }
+   else if (status == choosing) {
+      setStatus(restoring);
+
+      wxbMainFrame::GetInstance()->SetStatusText("Restoring, please wait...\n");
+
+      totfilemessages = 0;
+      WaitForEnd("estimate\n");
+      WaitForEnd("done\n");
+
+      if (totfilemessages == 0) {
+         wxbMainFrame::GetInstance()->Print("Restore failed : no file selected.\n", CS_DEBUG);
+         wxbMainFrame::GetInstance()->SetStatusText("Restore failed : no file selected.\n");
+         setStatus(finished);
+         return;
+      }
+
+      WaitForEnd("yes\n");
+
+      gauge->SetValue(0);
+      gauge->SetRange(totfilemessages);
+
+      wxString cmd = "list jobid=";
+
+      CreateAndWaitForParser("list jobs\n");
+        /* TODO (#1#): Check more carefully which job we just have run. */
+      cmd << (*tableParser)[tableParser->size()-1][0] << "\n";
+
+      filemessages = 0;
+
+      while (true) {
+         CreateAndWaitForParser(cmd);
+         if ((*tableParser)[0][7] != "C") {
+            break;
+         }
+
+         WaitForEnd("messages\n");
+
+         wxbMainFrame::GetInstance()->SetStatusText(wxString("Restoring, please wait (") << filemessages << " of " << totfilemessages << " files done)...\n");
+
+         time_t start = wxDateTime::Now().GetTicks();
+         while (((wxDateTime::Now().GetTicks())-start) < 3) {
+            wxTheApp->Yield();
+         }
+      }
+
+      WaitForEnd("messages\n");
+
+      gauge->SetValue(totfilemessages);
+
+      if ((*tableParser)[0][7] == "T") {
+         wxbMainFrame::GetInstance()->Print("Restore done successfully.\n", CS_DEBUG);
+         wxbMainFrame::GetInstance()->SetStatusText("Restore done successfully.\n");
+      }
+      else {
+         wxbMainFrame::GetInstance()->Print("Restore failed, please look at messages.\n", CS_DEBUG);
+         wxbMainFrame::GetInstance()->SetStatusText("Restore failed, please look at messages in console.\n");
+      }
+      setStatus(finished);
+   }
+}
+
+/* List jobs for a specified client */
+void wxbRestorePanel::CmdListJobs() {
+   if (status == entered) {
+      jobChoice->Clear();
+      WaitForEnd("query\n");
+      WaitForEnd("6\n");
+      CreateAndWaitForParser(clientChoice->GetString(clientChoice->GetSelection()) + "\n");
+
+      for (int i = tableParser->size()-1; i > -1; i--) {
+         wxString str = (*tableParser)[i][3];
+         wxDateTime datetime;
+         const char* chr;
+         if ( ( (chr = datetime.ParseDate(str.GetData()) ) != NULL ) && ( datetime.ParseTime(++chr) != NULL ) ) {
+            datetime = datetime.GetTicks() + 1;
+            //wxbMainFrame::GetInstance()->Print(wxString("-") << datetime.Format("%Y-%m-%d %H:%M:%S"), CS_DEBUG);
+            jobChoice->Append(datetime.Format("%Y-%m-%d %H:%M:%S"));
+         }
+         /*else {
+         jobChoice->Append("Invalid");
+         }*/
+      }
+
+      jobChoice->SetSelection(0);
+   }
+}
+
+/* List files and directories for a specified tree item */
+void wxbRestorePanel::CmdList(wxTreeItemId item) {
+   if (status == choosing) {
+      list->DeleteAllItems();
+
+      if (!item.IsOk()) {
+         return;
+      }
+      WaitForList(item, (tree->GetSelection() == item));
+    
+      if (list->GetItemCount() > 1) {
+         int firstwidth = list->GetSize().GetWidth(); 
+         for (int i = 2; i < 7; i++) {
+            list->SetColumnWidth(i, wxLIST_AUTOSIZE);
+            firstwidth -= list->GetColumnWidth(i);
+         }
+       
+         list->SetColumnWidth(0, 18);
+         firstwidth -= 18;
+         list->SetColumnWidth(1, wxLIST_AUTOSIZE);
+         if (list->GetColumnWidth(1) < firstwidth) {
+            list->SetColumnWidth(1, firstwidth-20);
+         }
+      }
+   }
+}
+
+/* Mark a treeitem (directory) or a listitem (file or directory) */
+void wxbRestorePanel::CmdMark(wxTreeItemId treeitem, long listitem) {
+   if (status == choosing) {
+      wxbTreeItemData* itemdata = NULL;
+      if (listitem != -1) {
+         itemdata = (wxbTreeItemData*)list->GetItemData(listitem);
+      }
+      else if (treeitem.IsOk()) {
+         itemdata = (wxbTreeItemData*)tree->GetItemData(treeitem);
+      }
+      else {
+         return;
+      }
+
+      if (itemdata == NULL) //Should never happen
+         return;
+
+      wxString dir = itemdata->GetPath();
+      wxString file;
+
+      if (dir != "/") {
+         if (dir.GetChar(dir.Length()-1) == '/') {
+            dir.RemoveLast();
+         }
+
+         int i = dir.Find('/', TRUE);
+         if (i == -1) {
+            file = dir;
+            dir = "/";
+         }
+         else { /* first dir below root */
+            file = dir.Mid(i+1);
+            dir = dir.Mid(0, i+1);
+         }
+      }
+      else {
+         dir = "/";
+         file = "*";
+      }
+
+      WaitForEnd(wxString("cd ") << dir << "\n");
+      WaitForEnd(wxString((itemdata->GetMarked() == 1) ? "unmark " : "mark ") << file << "\n");
+
+      if ((dir == "/") && (file == "*")) {
+            itemdata->SetMarked((itemdata->GetMarked() == 1) ? 0 : 1);
+      }
+
+      if (listitem != -1) {
+         treeitem = tree->GetSelection();
+         UpdateTree(treeitem, true);
+         treeitem = tree->GetItemParent(treeitem);
+      }
+      else {
+         UpdateTree(treeitem, (tree->GetSelection() == treeitem));
+         treeitem = tree->GetItemParent(treeitem);
+      }
+
+      while (treeitem.IsOk()) {
+         WaitForList(treeitem, false);
+         treeitem = tree->GetItemParent(treeitem);
+      }
+      
+      /* Update root marked status */
+      treeitem = tree->GetRootItem();
+      long cookie;
+      wxTreeItemId currentChild = tree->GetFirstChild(treeitem, cookie);
+
+      bool onechildmarked = false;
+      bool onechildunmarked = false;
+
+      while (currentChild.IsOk()) {
+         itemdata = (wxbTreeItemData*)tree->GetItemData(currentChild);
+         switch (itemdata->GetMarked()) {
+         case 0:
+            onechildunmarked = true;
+            break;
+         case 1:
+            onechildmarked = true;
+            break;
+         case 2:
+            onechildmarked = true;
+            onechildunmarked = true;
+            break;
+         }
+         
+         if (onechildmarked && onechildunmarked) {
+            break;
+         }
+         
+         currentChild = tree->GetNextChild(treeitem, cookie);
+      }
+      
+      itemdata = (wxbTreeItemData*)tree->GetItemData(treeitem);
+      if (onechildmarked && onechildunmarked) {
+         itemdata->SetMarked(2);
+         tree->SetItemImage(treeitem, 2, wxTreeItemIcon_Normal);
+         tree->SetItemImage(treeitem, 2, wxTreeItemIcon_Selected);
+      }
+      else if (onechildmarked) {
+         itemdata->SetMarked(1);
+         tree->SetItemImage(treeitem, 1, wxTreeItemIcon_Normal);
+         tree->SetItemImage(treeitem, 1, wxTreeItemIcon_Selected);
+      }
+      else {
+         itemdata->SetMarked(0);
+         tree->SetItemImage(treeitem, 0, wxTreeItemIcon_Normal);
+         tree->SetItemImage(treeitem, 0, wxTreeItemIcon_Selected);
+      }
+   }
+}
+
+/*----------------------------------------------------------------------------
+   General functions
+  ----------------------------------------------------------------------------*/
+
+/* Parse a table in tableParser */
+void wxbRestorePanel::CreateAndWaitForParser(wxString cmd) {
+   if (tableParser != NULL) {
+      delete tableParser;
+   }
+   tableParser = new wxbTableParser();
+
+   wxbMainFrame::GetInstance()->Send(cmd);
+
+   //time_t base = wxDateTime::Now().GetTicks();
+   while (!tableParser->hasFinished()) {
+      //innerThread->Yield();
+      wxTheApp->Yield();
+      //if (base+15 < wxDateTime::Now().GetTicks()) break;
+   }
+}
+
+/* Run a command, and waits until result is fully received. */
+void wxbRestorePanel::WaitForEnd(wxString cmd) {
+   wxbMainFrame::GetInstance()->Send(cmd);
+
+   ended = false;
+
+   //time_t base = wxDateTime::Now().GetTicks();
+   while (!ended) {
+      //innerThread->Yield();
+      wxTheApp->Yield();
+      //if (base+15 < wxDateTime::Now().GetTicks()) break;
+   }
+}
+
+/* Run a dir command, and waits until result is fully received. */
+void wxbRestorePanel::WaitForList(wxTreeItemId item, bool updatelist) {
+   this->updatelist = updatelist;
+   currentTreeItem = item;
+
+   WaitForEnd(wxString("cd \"") << static_cast<wxbTreeItemData*>(tree->GetItemData(currentTreeItem))->GetPath() << "\"\n");
+
+   status = listing;
+
+   if (updatelist)
+      list->DeleteAllItems();
+   WaitForEnd("dir\n");
+
+   tree->Refresh();
+   status = choosing;
+}
+
+/* Parse dir command results. */
+wxString* wxbRestorePanel::ParseList(wxString line) {
+   //drwx------  11 1003    42949672      0  2001-07-30 16:45:14 *filename
+   //+ 10    ++ 4++   10   ++   8  ++   8  + +      19         + *+ ->
+   //0       10  14         24      32       42                  62
+
+   if (line.Length() < 63)
+      return NULL;
+
+   wxString* ret = new wxString[9];
+
+   ret[0] = line.Mid(0, 10).Trim();
+   ret[1] = line.Mid(10, 4).Trim();
+   ret[2] = line.Mid(14, 10).Trim();
+   ret[3] = line.Mid(24, 8).Trim();
+   ret[4] = line.Mid(32, 8).Trim();
+   ret[5] = line.Mid(42, 19).Trim();
+   ret[6] = line.Mid(62, 1);
+   ret[7] = line.Mid(63).Trim();
+
+   if (ret[6] == " ") ret[6] = "";
+
+   if (ret[7].GetChar(ret[7].Length()-1) == '/') {
+      ret[8] = ret[7];
+      ret[8].RemoveLast();
+      ret[8] = ret[7].Mid(ret[8].Find('/', true)+1);
+   }
+   else {
+      ret[8] = ret[7].Mid(ret[7].Find('/', true)+1);
+   }
+
+   return ret;
+}
+
+/* Update a tree item, and all its childs. */
+void wxbRestorePanel::UpdateTree(wxTreeItemId item, bool updatelist) {
+   WaitForList(item, updatelist);
+
+   /* Updated all child which are not collapsed */
+   long cookie;
+   wxTreeItemId currentChild = tree->GetFirstChild(item, cookie);
+
+   while (currentChild.IsOk()) {
+      if (tree->IsExpanded(currentChild))
+         UpdateTree(currentChild, false);
+
+      currentChild = tree->GetNextChild(item, cookie);
+   }
+}
+
+/*----------------------------------------------------------------------------
+   Status function
+  ----------------------------------------------------------------------------*/
+
+/* Set current status by enabling/disabling components */
+void wxbRestorePanel::setStatus(status_enum newstatus) {
+   switch (newstatus) {
+   case finished:
+      tree->DeleteAllItems();
+      list->DeleteAllItems();
+      clientChoice->Clear();
+      jobChoice->Clear();
+      newstatus = disabled;
+   case disabled:
+      start->SetLabel("Enter restore mode");
+      start->Enable(true);
+      clientChoice->Enable(false);
+      jobChoice->Enable(false);
+      tree->Enable(false);
+      list->Enable(false);
+      gauge->Enable(false);
+      break;
+   case entered:
+      gauge->SetValue(0);
+      start->SetLabel("Choose files to restore");
+      clientChoice->Enable(true);
+      jobChoice->Enable(true);
+      tree->Enable(false);
+      list->Enable(false);
+      break;
+   case listing:
+
+      break;
+   case choosing:
+      start->SetLabel("Restore");
+      clientChoice->Enable(false);
+      jobChoice->Enable(false);
+      tree->Enable(true);
+      list->Enable(true);
+      working = false;
+      break;
+   case restoring:
+      start->SetLabel("Restoring...");
+      gauge->Enable(true);
+      gauge->SetValue(0);
+      start->Enable(false);
+      clientChoice->Enable(false);
+      jobChoice->Enable(false);
+      tree->Enable(false);
+      list->Enable(false);
+      working = true;
+      break;
+   }
+   status = newstatus;
+}
+
+/*----------------------------------------------------------------------------
+   Event handling
+  ----------------------------------------------------------------------------*/
+
+void wxbRestorePanel::OnClientChoiceChanged(wxCommandEvent& event) {
+   if (working) {
+      return;
+   }
+   working = true;
+   clientChoice->Enable(false);
+   CmdListJobs();
+   clientChoice->Enable(true);
+   jobChoice->Refresh();
+   working = false;
+}
+
+void wxbRestorePanel::OnStart(wxEvent& WXUNUSED(event)) {
+   if (working) {
+      return;
+   }
+   working = true;
+   CmdStart();
+   working = false;
+}
+
+void wxbRestorePanel::OnTreeChanging(wxTreeEvent& event) {
+   if (working) {
+      event.Veto();
+   }
+}
+
+void wxbRestorePanel::OnTreeExpanding(wxTreeEvent& event) {
+   if (working) {
+      event.Veto();
+      return;
+   }
+   //working = true;
+   //CmdList(event.GetItem());
+   tree->SelectItem(event.GetItem());
+   //working = false;
+}
+
+void wxbRestorePanel::OnTreeChanged(wxTreeEvent& event) {
+   if (working) {
+      return;
+   }
+   working = true;
+   CmdList(event.GetItem());
+   working = false;
+}
+
+void wxbRestorePanel::OnTreeRightClicked(wxTreeEvent& event) {
+   if (working) {
+      event.Skip();
+      return;
+   }
+   working = true;
+   CmdMark(event.GetItem(), -1);
+   event.Skip();
+   tree->Refresh();
+   working = false;
+}
+
+void wxbRestorePanel::OnListRightClicked(wxListEvent& event) {
+   if (working) {
+      event.Skip();
+      return;
+   }
+   working = true;
+   long item = list->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_FOCUSED);
+   CmdMark(wxTreeItemId(), item);
+   event.Skip();
+   tree->Refresh();
+   working = false;
+}
+
+void wxbRestorePanel::OnListActivated(wxListEvent& event) {
+   if (working) {
+      event.Skip();
+      return;
+   }
+   working = true;
+   long item = list->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_FOCUSED);
+   if (item > -1) {
+      wxbTreeItemData* itemdata = (wxbTreeItemData*)list->GetItemData(item);
+      wxString name = itemdata->GetName();
+      event.Skip();
+
+      wxString itemStr;
+
+      long cookie;
+
+      if (name.GetChar(name.Length()-1) == '/') {
+         wxTreeItemId currentChild = tree->GetFirstChild(currentTreeItem, cookie);
+
+         while (currentChild.IsOk()) {
+            wxString name2 = tree->GetItemText(currentChild);
+            if (name2 == name) {
+               working = false;
+               tree->UnselectAll();
+               tree->Expand(currentTreeItem);
+               tree->SelectItem(currentChild);
+               //tree->Refresh();
+               return;
+            }
+            currentChild = tree->GetNextChild(currentTreeItem, cookie);
+         }
+      }
+   }
+   working = false;
+}
diff --git a/bacula/src/wx-console/wxbrestorepanel.h b/bacula/src/wx-console/wxbrestorepanel.h
new file mode 100644 (file)
index 0000000..da335f5
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+   Copyright (C) 2004 Kern Sibbald and John Walker
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License
+   as published by the Free Software Foundation; either version 2
+   of the License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#ifndef WXBRESTOREPANEL_H
+#define WXBRESTOREPANEL_H
+
+#include "wx/wxprec.h"
+
+#ifndef WX_PRECOMP
+   #include "wx/wx.h"
+#endif
+
+#include "wxbtableparser.h"
+
+#include <wx/thread.h>
+#include <wx/listctrl.h>
+#include <wx/treectrl.h>
+#include <wx/gauge.h>
+
+#include "wxbpanel.h"
+
+/*
+ * wxbPanel for restoring files
+ */
+class wxbRestorePanel : public wxbPanel
+{
+   public:
+      wxbRestorePanel(wxWindow* parent);
+      ~wxbRestorePanel();
+
+      /* wxbPanel overloadings */
+      virtual wxString GetTitle();
+      virtual void Print(wxString str, int status);
+
+   private:
+/* Commands called by events handler */
+
+      /* The main button has been clicked */
+      void CmdStart();
+
+       /* List jobs for a specified client */
+      void CmdListJobs();
+
+      /* List files and directories for a specified tree item */
+      void CmdList(wxTreeItemId item);
+
+      /* Mark a treeitem (directory) or a listitem (file or directory) */
+      void CmdMark(wxTreeItemId treeitem, long listitem);
+
+/* General functions and variables */
+      bool ended; /* The last command send has finished */
+
+      long filemessages; /* When restoring, number of files restored */
+      long totfilemessages; /* When restoring, number of files to be restored */
+
+      /* When listing a directory, sets if file list must be updated
+       * (otherwise only the tree structure is updated)
+       */
+      bool updatelist;
+
+      wxbTableParser* tableParser; /* Used to parse tables */
+
+      /* Parse a table in tableParser */
+      void CreateAndWaitForParser(wxString cmd);
+
+      /* Run a command, and waits until result is fully received. */
+      void WaitForEnd(wxString cmd);
+
+      /* Run a dir command, and waits until result is fully received. */
+      void WaitForList(wxTreeItemId item, bool updatelist);
+
+      /* Parse dir command results. */
+      wxString* ParseList(wxString line);
+
+      /* Update a tree item, and all its childs. */
+      void UpdateTree(wxTreeItemId item, bool updatelist);
+
+/* Status related */
+      enum status_enum
+      {
+         disabled,  // The panel is not activated
+         entered,   // The panel is activated
+         choosing,  // The user is choosing files to restore
+         listing,   // Dir listing is in progress
+         restoring, // Bacula is restoring files
+         finished   // Retore done
+      };
+
+      status_enum status;
+
+      /* Set current status by enabling/disabling components */
+      void setStatus(status_enum newstatus);
+
+/* UI related */
+      bool working; // A command is running, discard GUI events
+      wxTreeItemId currentTreeItem; // Currently selected tree item
+
+/* Event handling */
+      void OnStart(wxEvent& WXUNUSED(event));
+      void OnTreeChanging(wxTreeEvent& event);
+      void OnTreeExpanding(wxTreeEvent& event);
+      void OnTreeChanged(wxTreeEvent& event);
+      void OnTreeRightClicked(wxTreeEvent& event);
+      void OnListRightClicked(wxListEvent& event);
+      void OnListActivated(wxListEvent& event);
+      void OnClientChoiceChanged(wxCommandEvent& event);
+
+/* Components */
+      wxImageList* imagelist; //image list for tree and list
+
+      wxButton* start;
+      wxChoice* clientChoice;
+      wxChoice* jobChoice;
+      wxTreeCtrl* tree;
+      wxListCtrl* list;
+      wxGauge* gauge;
+
+      DECLARE_EVENT_TABLE();
+};
+
+#endif // WXBRESTOREPANEL_H
+
diff --git a/bacula/src/wx-console/wxbtableparser.cpp b/bacula/src/wx-console/wxbtableparser.cpp
new file mode 100644 (file)
index 0000000..f5cbf84
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ *
+ *   Class used to parse tables received from director in this format :
+ *
+ *  +---------+---------+-------------------+
+ *  | Header1 | Header2 | ...               |
+ *  +---------+---------+-------------------+
+ *  |  Data11 | Data12  | ...               |
+ *  |  ....   | ...     | ...               |
+ *  +---------+---------+-------------------+
+ *
+ *    Nicolas Boichat, April 2004
+ *
+ */
+/*
+   Copyright (C) 2004 Kern Sibbald and John Walker
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License
+   as published by the Free Software Foundation; either version 2
+   of the License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include "wxbtableparser.h" // class's header file
+
+#include "csprint.h"
+
+#include <wx/tokenzr.h>
+
+#include "wxbmainframe.h"
+
+/*
+ *   wxbTableParser constructor
+ */
+wxbTableParser::wxbTableParser() : wxbTable(5) {
+   separatorNum = 0;
+   tableHeader = wxbTableRow(5);
+}
+
+/*
+ *   wxbTableParser destructor
+ */
+wxbTableParser::~wxbTableParser() {
+
+}
+
+/*
+ *   Returns table header as an array of wxStrings.
+ */
+wxbTableRow* wxbTableParser::GetHeader() {
+   return &tableHeader;
+}
+
+/*
+ *   Receives director information, forwarded by the wxbPanel which
+ *  uses this parser.
+ */
+void wxbTableParser::Print(wxString str, int status) {
+   if ((status == CS_END) && (separatorNum > 0)) {
+      separatorNum = 3;
+   }
+
+   if (separatorNum == 3) return;
+
+   if (str.Length() > 4) {
+      if ((str.GetChar(0) == '+') && (str.GetChar(str.Length()-2) == '+') && (str.GetChar(str.Length()-1) == '\n')) {
+         separatorNum++;
+         return;
+      }
+
+      if ((str.GetChar(0) == '|') && (str.GetChar(str.Length()-2) == '|') && (str.GetChar(str.Length()-1) == '\n')) {
+         str.RemoveLast();
+         wxStringTokenizer tkz(str, "|", wxTOKEN_STRTOK);
+
+         if (separatorNum == 1) {
+            int i = 0;
+            while ( tkz.HasMoreTokens() ) {
+               tableHeader[i++] = tkz.GetNextToken().Trim(true).Trim(false);
+            }
+         }
+         else if (separatorNum == 2) {
+            wxbTableRow tablerow(tableHeader.size());
+            int i = 0;
+            while ( tkz.HasMoreTokens() ) {
+               tablerow[i++] = tkz.GetNextToken().Trim(true).Trim(false);
+            }
+            (*this)[size()] = tablerow;
+         }
+      }
+   }
+}
+
+/*
+ *   Return true table parsing has finished.
+ */
+bool wxbTableParser::hasFinished() {
+   return (separatorNum == 3);
+}
diff --git a/bacula/src/wx-console/wxbtableparser.h b/bacula/src/wx-console/wxbtableparser.h
new file mode 100644 (file)
index 0000000..fb24959
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ *
+ *   Class used to parse tables received from director in this format :
+ *
+ *  +---------+---------+-------------------+
+ *  | Header1 | Header2 | ...               |
+ *  +---------+---------+-------------------+
+ *  |  Data11 | Data12  | ...               |
+ *  |  ....   |  ...    | ...               |
+ *  +---------+---------+-------------------+
+ *
+ *    Nicolas Boichat, April 2004
+ *
+ */
+/*
+   Copyright (C) 2004 Kern Sibbald and John Walker
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License
+   as published by the Free Software Foundation; either version 2
+   of the License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#ifndef WXBTABLEPARSER_H
+#define WXBTABLEPARSER_H
+
+#include "wx/wxprec.h"
+
+#ifdef __BORLANDC__
+   #pragma hdrstop
+#endif
+
+// for all others, include the necessary headers (this file is usually all you
+// need because it includes almost all "standard" wxWindows headers)
+#ifndef WX_PRECOMP
+   #include "wx/wx.h"
+#endif
+
+#include <wx/hashmap.h>
+
+/* int-indexed array of wxString, used for one line */
+WX_DECLARE_HASH_MAP( int, wxString, wxIntegerHash, wxIntegerEqual, wxbTableRow );
+/* int-indexed array of wxbTableRow, contains the whole table */
+WX_DECLARE_HASH_MAP( int, wxbTableRow, wxIntegerHash, wxIntegerEqual, wxbTable );
+
+/*
+ * Class used to parse tables received from director. Data can be accessed with
+ * the operator [].
+ *
+ * Example : wxString elem = parser[3][2]; fetches column 2 of element 3.
+ */
+class wxbTableParser: public wxbTable
+{
+   public:
+      wxbTableParser();
+      ~wxbTableParser();
+
+      /*
+       *   Receives director information, forwarded by the wxbPanel which
+       *  uses this parser.
+       */
+      void Print(wxString str, int status);
+
+      /*
+       *   Return true table parsing has finished.
+       */
+      bool hasFinished();
+
+      /*
+       *   Returns table header as an array of wxStrings.
+       */
+      wxbTableRow* GetHeader();
+   private:
+      wxbTableRow tableHeader;
+
+      /*
+       * 0 - Table has not begun
+       * 1 - first +--+ line obtained, header will follow
+       * 2 - second +--+ line obtained, data will follow
+       * 3 - last +--+ line obtained, table parsing has finished
+       */
+      int separatorNum;
+};
+
+#endif // WXBTABLEPARSER_H
+
diff --git a/bacula/src/wx-console/wxwin16x16.xpm b/bacula/src/wx-console/wxwin16x16.xpm
new file mode 100644 (file)
index 0000000..9964e69
--- /dev/null
@@ -0,0 +1,25 @@
+/* XPM */
+static char *wxwin16x16_xpm[] = {
+"16 16 6 1",
+"      c None",
+".     c #000000",
+"X     c #000084",
+"o     c #FFFFFF",
+"O     c #FFFF00",
+"+     c #FF0000",
+"                ",
+"                ",
+"                ",
+"    .......     ",
+"    .XXXXX.     ",
+"    .oXXXX.     ",
+"    .oXXX.......",
+".....oXXX.OOOOO.",
+".+++.XXXX.oOOOO.",
+".o++......oOOOO.",
+".o++++.  .oOOOO.",
+".o++++.  .OOOOO.",
+".+++++.  .......",
+".......         ",
+"                ",
+"                "};