]> git.sur5r.net Git - bacula/bacula/blobdiff - gui/brestore/brestore.pl
added 3 missing )
[bacula/bacula] / gui / brestore / brestore.pl
index 9c0b05ee59d5c8fac581ce2cbab0c8546340c536..bfcbeaba63c3454302aa8a646baa5f7c497cd1b9 100755 (executable)
@@ -1,5 +1,6 @@
 #!/usr/bin/perl -w
-use strict ;
+
+use strict;
 
 # path to your brestore.glade
 my $glade_file = 'brestore.glade' ;
@@ -31,25 +32,36 @@ my $glade_file = 'brestore.glade' ;
 
 =head1 COPYRIGHT
 
-  Copyright (C) 2006 Marc Cousin and Eric Bollengier
+   Bacula® - The Network Backup Solution
 
-  This library is free software; you can redistribute it and/or
-  modify it under the terms of the GNU Lesser General Public
-  License as published by the Free Software Foundation; either
-  version 2 of the License, or (at your option) any later version.
-  This library 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
-  Lesser General Public License for more details.
-  
-  You should have received a copy of the GNU Lesser General Public
-  License along with this library; if not, write to the
-  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-  Boston, MA 02111-1307, USA.
-  
-  Base 64 functions from Karl Hakimian <hakimian@aha.com>
-  Integrally copied from recover.pl from bacula source distribution.
+   Copyright (C) 2000-2006 Free Software Foundation Europe e.V.
+
+   Brestore authors are Marc Cousin and Eric Bollengier.
+   The main author of Bacula is Kern Sibbald, with contributions from
+   many others, a complete list can be found in the file AUTHORS.
+
+   This program is Free Software; you can redistribute it and/or
+   modify it under the terms of version two of the GNU General Public
+   License as published by the Free Software Foundation plus additions
+   that are listed in the file LICENSE.
+
+   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., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA.
+
+   Bacula® is a registered trademark of John Walker.
+   The licensor of Bacula is the Free Software Foundation Europe
+   (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zurich,
+   Switzerland, email:ftf@fsfeurope.org.
+
+   Base 64 functions from Karl Hakimian <hakimian@aha.com>
+   Integrally copied from recover.pl from bacula source distribution.
 
 =cut
 
@@ -58,230 +70,456 @@ use Gtk2::GladeXML;
 use Gtk2::SimpleList;          # easy wrapper for list views
 use Gtk2::Gdk::Keysyms;                # keyboard code constants
 use Data::Dumper qw/Dumper/;
-use DBI;
 my $debug=0;                   # can be on brestore.conf
+our ($VERSION) = ('$Revision$' =~ /(\d+)/);
 
-################################################################
+package Pref;
+use DBI;
+sub new
+{
+    my ($class, $config_file) = @_;
+    
+    my $self = bless {
+       config_file => $config_file,
+       password => '',         # db passwd
+       username => '',         # db username
+       connection_string => '',# db connection string
+       bconsole => 'bconsole', # path and arg to bconsole
+       bsr_dest => '',         # destination url for bsr files
+       debug    => 0,          # debug level 0|1
+       use_ok_bkp_only => 1,   # dont use bad backup
+       bweb     => 'http://localhost/cgi-bin/bweb/bweb.pl', # bweb url
+       see_all_versions => 0,  # display all file versions in FileInfo
+       mozilla  => 'mozilla',  # mozilla bin
+       default_restore_job => 'restore', # regular expression to select default
+                                  # restore job
 
-package DlgFileVersion;
+       # keywords that are used to fill DlgPref
+       chk_keyword =>  [ qw/use_ok_bkp_only debug see_all_versions/ ],
+        entry_keyword => [ qw/username password bweb mozilla
+                         connection_string default_restore_job
+                         bconsole bsr_dest glade_file/],
+    };
 
-sub on_versions_close_clicked
-{
-    my ($self, $widget)=@_;
-    $self->{version}->destroy();
-}
+    $self->read_config();
 
-sub on_selection_button_press_event
-{
-    print STDERR "on_selection_button_press_event()\n";
+    return $self;
 }
 
-sub fileview_data_get
+sub read_config
 {
-    my ($self, $widget, $context, $data, $info, $time,$string) = @_;
+    my ($self) = @_;
 
-    DlgResto::drag_set_info($widget, 
-                           $self->{cwd},
-                           $data);
+    # We read the parameters. They come from the configuration files
+    my $cfgfile ; my $tmpbuffer;
+    if (open FICCFG, $self->{config_file})
+    {
+       while(read FICCFG,$tmpbuffer,4096)
+       {
+           $cfgfile .= $tmpbuffer;
+       }
+       close FICCFG;
+       my $refparams;
+       no strict; # I have no idea of the contents of the file
+       eval '$refparams' . " = $cfgfile";
+       use strict;
+       
+       for my $p (keys %{$refparams}) {
+           $self->{$p} = $refparams->{$p};
+       }
+
+    } else {
+       # TODO : Force dumb default values and display a message
+    }
 }
 
-sub new
+sub write_config
 {
-    my ($class, $dbh, $client, $path, $file) = @_;
-    my $self = bless {
-       cwd       => $path,
-       version   => undef, # main window
-       };
+    my ($self) = @_;
+    
+    $self->{error} = '';
+    my %parameters;
 
-    # we load version widget of $glade_file
-    my $glade_box = Gtk2::GladeXML->new($glade_file, "dlg_version");
+    for my $k (@{ $self->{entry_keyword} }) { 
+       $parameters{$k} = $self->{$k};
+    }
 
-    # Connect signals magically
-    $glade_box->signal_autoconnect_from_package($self);
+    for my $k (@{ $self->{chk_keyword} }) { 
+       $parameters{$k} = $self->{$k};
+    }
 
-    $glade_box->get_widget("version_label")
-       ->set_markup("<b>File revisions : $client:$path/$file</b>");
+    if (open FICCFG,">$self->{config_file}")
+    {
+       print FICCFG Data::Dumper->Dump([\%parameters], [qw($parameters)]);
+       close FICCFG;
+    }
+    else
+    {
+       $self->{error} = "Can't write configuration $!";
+    }
+    return $self->{error};
+}
 
-    my $widget = $glade_box->get_widget('version_fileview');
-    my $fileview = Gtk2::SimpleList->new_from_treeview(
-                  $widget,
-                  'h_name'        => 'hidden',
-                   'h_jobid'       => 'hidden',
-                   'h_type'        => 'hidden',
+sub connect_db
+{
+    my $self = shift ;
 
-                  'InChanger'     => 'pixbuf',
-                  'Volume'        => 'text',
-                  'JobId'         => 'text',
-                  'Size'          => 'text',
-                  'Date'          => 'text',
-                  'MD5'           => 'text',
-                                                      );
-    DlgResto::init_drag_drop($fileview);
+    if ($self->{dbh}) {
+       $self->{dbh}->disconnect() ;
+    }
 
-    my @v = DlgResto::get_all_file_versions($dbh, 
-                                           "$path/", 
-                                           $file,
-                                           $client,
-                                           1);
-    for my $ver (@v) {
-       my (undef,$fn,$jobid,$fileindex,$mtime,$size,$inchanger,$md5,$volname)
-           = @{$ver};
-       my $icon = ($inchanger)?$DlgResto::yesicon:$DlgResto::noicon;
+    delete $self->{dbh};
+    delete $self->{error};
 
-       DlgResto::listview_push($fileview,
-                               $file, $jobid, 'file', 
-                               $icon, $volname, $jobid,DlgResto::human($size),
-                               scalar(localtime($mtime)), $md5);
+    if (not $self->{connection_string})
+    {
+       # The parameters have not been set. Maybe the conf
+       # file is empty for now
+       $self->{error} = "No configuration found for database connection. " .
+                        "Please set this up.";
+       return 0;
     }
-
-    $self->{version} = $glade_box->get_widget('dlg_version');
-    $self->{version}->show();
     
-    return $self;
+    if (not eval {
+       $self->{dbh} = DBI->connect($self->{connection_string}, 
+                                   $self->{username},
+                                   $self->{password})
+       })
+    {
+       $self->{error} = "Can't open bacula database. " . 
+                        "Database connect string '" . 
+                         $self->{connection_string} ."' $!";
+       return 0;
+    }
+    $self->{is_mysql} = ($self->{connection_string} =~ m/dbi:mysql/i);
+    $self->{dbh}->{RowCacheSize}=100;
+    return 1;
 }
 
-sub on_forward_keypress
-{
-    return 0;
+sub go_bweb
+{    
+    my ($self, $url, $msg) = @_;
+
+    unless ($self->{mozilla} and $self->{bweb}) {
+       new DlgWarn("You must install Bweb and set your mozilla bin to $msg");
+       return -1;
+    }
+
+    if ($^O eq 'MSWin32') {
+       system("start /B $self->{mozilla} \"$self->{bweb}$url\"");
+
+    } elsif (!fork()) {
+       system("$self->{mozilla} -remote 'Ping()'");
+       my $cmd = "$self->{mozilla} '$self->{bweb}$url'";
+       if ($? == 0) {
+           $cmd = "$self->{mozilla} -remote 'OpenURL($self->{bweb}$url,new-tab)'" ;
+       }
+       exec($cmd);
+       exit 1;
+    } 
+    return ($? == 0);
 }
 
 1;
+
 ################################################################
-package DlgWarn;
+
+package Bbase;
 
 sub new
 {
-    my ($package, $text) = @_;
+    my ($class, %arg) = @_;
 
-    my $self = bless {};
+    my $self = bless {
+       conf => $arg{conf},
+    }, $class;
 
-    my $glade = Gtk2::GladeXML->new($glade_file, "dlg_warn");
+    return $self;
+}
 
-    # Connect signals magically
-    $glade->signal_autoconnect_from_package($self);
-    $glade->get_widget('label_warn')->set_text($text);
+use Data::Dumper;
 
-    print STDERR "$text\n";
+sub debug
+{
+    my ($self, $what, %arg) = @_;
+    
+    if ($self->{conf}->{debug} and defined $what) {
+       my $level=0;
+       if ($arg{up}) {
+           $level++;
+       }
+       my $line = (caller($level))[2];
+       my $func = (caller($level+1))[3] || 'main';
+       print "$func:$line\t";  
+       if (ref $what) {
+           print Data::Dumper::Dumper($what);
+       } elsif ($arg{md5}) {
+           print "MD5=", md5_base64($what), " str=", $what,"\n";
+       } else {
+           print $what, "\n";
+       }
+    }
+}
 
-    $self->{window} = $glade->get_widget('dlg_warn');
-    $self->{window}->show_all();
-    return $self;
+sub dbh_strcat
+{
+    my ($self, @what) = @_;
+    if ($self->{conf}->{connection_string} =~ /dbi:pg/i) {
+       return join(' || ', @what);
+    } else {
+       return 'CONCAT(' . join(',', @what) . ')' ;
+    }
 }
 
-sub on_close_clicked
+sub dbh_prepare
+{
+    my ($self, $query) = @_;
+    $self->debug($query, up => 1);
+    return $self->{conf}->{dbh}->prepare($query);    
+}
+
+sub dbh_do
+{
+    my ($self, $query) = @_;
+    $self->debug($query, up => 1);
+    return $self->{conf}->{dbh}->do($query);
+}
+
+sub dbh_selectall_arrayref
+{
+    my ($self, $query) = @_;
+    $self->debug($query, up => 1);
+    return $self->{conf}->{dbh}->selectall_arrayref($query);
+}
+
+sub dbh_selectrow_arrayref
+{
+    my ($self, $query) = @_;
+    $self->debug($query, up => 1);
+    return $self->{conf}->{dbh}->selectrow_arrayref($query);
+}
+
+sub dbh
 {
     my ($self) = @_;
-    $self->{window}->destroy();
+    return $self->{conf}->{dbh};
 }
+
 1;
 
 ################################################################
-package BwebConsole;
-use LWP::UserAgent;
-use HTTP::Request::Common;
+# Manage preference
+package DlgPref;
 
+# my $pref = new Pref(config_file => 'brestore.conf');
+# my $dlg = new DlgPref($pref);
+# my $dlg_resto = new DlgResto($pref);
+# $dlg->display($dlg_resto);
 sub new
 {
-    my ($class, %arg) = @_;
+    my ($class, $pref) = @_;
 
     my $self = bless {
-       pref => $arg{pref},     # Pref object
-       timeout => $arg{timeout} || 20,
-       debug   => $arg{debug} || 0,
-       'list_job'     => '',
-       'list_client'  => '',
-       'list_fileset' => '',
-       'list_storage' => '',
-       'run'          => '',
-    };
+       pref => $pref,          # Pref ref
+       dlgresto => undef,      # DlgResto ref
+       };
 
     return $self;
 }
 
-sub prepare
+sub display
 {
-    my ($self, @what) = @_;
-    my $ua = LWP::UserAgent->new();
-    $ua->agent("Brestore ");
-    my $req = POST($self->{pref}->{bconsole},
-                  Content_Type => 'form-data',
-                  Content => [ map { (action => $_) } @what ]);
-    #$req->authorization_basic('eric', 'test');
+    my ($self, $dlgresto) = @_ ;
 
-    my $res = $ua->request($req);
+    unless ($self->{glade}) {
+       $self->{glade} = Gtk2::GladeXML->new($glade_file, "dlg_pref") ;
+       $self->{glade}->signal_autoconnect_from_package($self);
+    }
 
-    if ($res->is_success) {
-       foreach my $l (split(/\n/, $res->content)) {
-           my ($k, $c) = split(/=/,$l,2);
-           $self->{$k} = $c;
-       }
-    } else {
-       $self->{error} = "Can't connect to bweb : " . $res->status_line;
-       new DlgWarn($self->{error});
+    $self->{dlgresto} = $dlgresto;
+
+    my $g = $self->{glade};
+    my $p = $self->{pref};
+
+    for my $k (@{ $p->{entry_keyword} }) {
+       $g->get_widget("entry_$k")->set_text($p->{$k}) ;
+    }
+
+    for my $k (@{ $p->{chk_keyword} }) {
+       $g->get_widget("chkbp_$k")->set_active($p->{$k}) ;
     }
+
+    $g->get_widget("dlg_pref")->show_all() ;
 }
 
-sub run
+sub on_applybutton_clicked
 {
-    my ($self, %arg) = @_;
-
-    my $ua = LWP::UserAgent->new();
-    $ua->agent("Brestore ");
-    my $req = POST($self->{pref}->{bconsole},
-                  Content_Type => 'form-data',
-                  Content => [ job     => $arg{job},
-                               client  => $arg{client},
-                               storage => $arg{storage} || '',
-                               fileset => $arg{fileset} || '',
-                               where   => $arg{where},
-                               replace => $arg{replace},
-                               priority=> $arg{prio}    || '',
-                               action  => 'run',
-                               timeout => 10,
-                               bootstrap => [$arg{bootstrap}],
-                               ]);
-    #$req->authorization_basic('eric', 'test');
+    my ($self) = @_;
+    my $glade = $self->{glade};
+    my $pref  = $self->{pref};
 
-    my $res = $ua->request($req);
+    for my $k (@{ $pref->{entry_keyword} }) {
+       my $w = $glade->get_widget("entry_$k") ;
+       $pref->{$k} = $w->get_text();
+    }
 
-    if ($res->is_success) {
-       foreach my $l (split(/\n/, $res->content)) {
-           my ($k, $c) = split(/=/,$l,2);
-           $self->{$k} = $c;
-       }
-    } 
+    for my $k (@{ $pref->{chk_keyword} }) {
+       my $w = $glade->get_widget("chkbp_$k") ;
+       $pref->{$k} = $w->get_active();
+    }
 
-    if ($self->{run}) {
-       unlink($arg{bootstrap});
-       new DlgWarn("Can't connect to bweb : " . $res->status_line);
-    } 
+    if (!$pref->write_config() && $pref->connect_db()) {
+        $self->{dlgresto}->set_status('Preferences updated');
+       $self->{dlgresto}->init_server_backup_combobox();
+       $self->{dlgresto}->set_status($pref->{error});
 
-    return $self->{run};
+    } else {
+       $self->{dlgresto}->set_status($pref->{error});
+    }
 }
 
-sub list_job
+# Handle prefs ok click (apply/dismiss dialog)
+sub on_okbutton_clicked 
 {
     my ($self) = @_;
-    return sort split(/;/, $self->{'list_job'});
-}
+    $self->on_applybutton_clicked();
 
-sub list_fileset
+    unless ($self->{pref}->{error}) {
+       $self->on_cancelbutton_clicked();
+    }
+}
+sub on_dialog_delete_event
 {
     my ($self) = @_;
-    return sort split(/;/, $self->{'list_fileset'});
+    $self->on_cancelbutton_clicked();
+    1;
 }
 
-sub list_storage
+sub on_cancelbutton_clicked
 {
     my ($self) = @_;
-    return sort split(/;/, $self->{'list_storage'});
+    $self->{glade}->get_widget('dlg_pref')->hide();
+    delete $self->{dlgresto};
 }
-sub list_client
+1;
+
+################################################################
+# Display all file revision in a separated window
+package DlgFileVersion;
+
+sub on_versions_close_clicked
 {
-    my ($self) = @_;
-    return sort split(/;/, $self->{'list_client'});
+    my ($self, $widget)=@_;
+    $self->{version}->destroy();
+}
+
+sub on_selection_button_press_event
+{
+    print STDERR "on_selection_button_press_event()\n";
+}
+
+sub fileview_data_get
+{
+    my ($self, $widget, $context, $data, $info, $time,$string) = @_;
+
+    DlgResto::drag_set_info($widget, 
+                           $self->{pwd},
+                           $data);
+}
+
+#
+# new DlgFileVersion(Bvfs, "client", pathid, fileid, "/path/to/", "filename");
+#
+sub new
+{
+    my ($class, $bvfs, $client, $pathid, $fileid, $path, $fn) = @_;
+    my $self = bless {
+       pwd       => $path,
+       version   => undef, # main window
+       };
+
+    # we load version widget of $glade_file
+    my $glade_box = Gtk2::GladeXML->new($glade_file, "dlg_version");
+
+    # Connect signals magically
+    $glade_box->signal_autoconnect_from_package($self);
+
+    $glade_box->get_widget("version_label")
+       ->set_markup("<b>File revisions : $client:$path$fn</b>");
+
+    my $widget = $glade_box->get_widget('version_fileview');
+    my $fileview = Gtk2::SimpleList->new_from_treeview(
+                  $widget,
+                  'h_pathid'      => 'hidden',
+                  'h_filenameid'  => 'hidden',
+                  'h_name'        => 'hidden',
+                   'h_jobid'       => 'hidden',
+                   'h_type'        => 'hidden',
+
+                  'InChanger'     => 'pixbuf',
+                  'Volume'        => 'text',
+                  'JobId'         => 'text',
+                  'Size'          => 'text',
+                  'Date'          => 'text',
+                  'MD5'           => 'text',
+                                                      );
+    DlgResto::init_drag_drop($fileview);
+
+    my @v = $bvfs->get_all_file_versions($pathid, 
+                                        $fileid,
+                                        $client,
+                                        1);
+    for my $ver (@v) {
+       my (undef,$pid,$fid,$jobid,$fileindex,$mtime,$size,
+           $inchanger,$md5,$volname) = @{$ver};
+       my $icon = ($inchanger)?$DlgResto::yesicon:$DlgResto::noicon;
+
+       DlgResto::listview_push($fileview,$pid,$fid,
+                               $fn, $jobid, 'file', 
+                               $icon, $volname, $jobid,DlgResto::human($size),
+                               scalar(localtime($mtime)), $md5);
+    }
+
+    $self->{version} = $glade_box->get_widget('dlg_version');
+    $self->{version}->show();
+    
+    return $self;
+}
+
+sub on_forward_keypress
+{
+    return 0;
+}
+
+1;
+################################################################
+# Display a warning message
+package DlgWarn;
+
+sub new
+{
+    my ($package, $text) = @_;
+
+    my $self = bless {};
+
+    my $glade = Gtk2::GladeXML->new($glade_file, "dlg_warn");
+
+    # Connect signals magically
+    $glade->signal_autoconnect_from_package($self);
+    $glade->get_widget('label_warn')->set_text($text);
+
+    print STDERR "$text\n";
+
+    $self->{window} = $glade->get_widget('dlg_warn');
+    $self->{window}->show_all();
+    return $self;
 }
 
+sub on_close_clicked
+{
+    my ($self) = @_;
+    $self->{window}->destroy();
+}
 1;
 
 ################################################################
@@ -317,9 +555,9 @@ sub new
 
     my $self = bless {
        bsr_file => $arg{bsr_file}, # /path/to/bsr on director
-       pref     => $arg{pref}, # Pref ref
-       glade => undef,         # GladeXML ref
-       bconsole => undef,      # Bconsole ref
+       pref     => $arg{pref},     # Pref ref
+       glade    => undef,          # GladeXML ref
+       bconsole => undef,          # Bconsole ref
     };
 
     my $console = $self->{bconsole} = get_bconsole($arg{pref});
@@ -411,6 +649,8 @@ sub show_job
 
     my $ret = $self->{pref}->go_bweb("?action=dsp_cur_job;jobid=$jobid;client=$client", "view job status");
 
+    $self->on_cancel_resto_clicked();
+
     if ($ret == -1) {
        my $widget = Gtk2::MessageDialog->new(undef, 'modal', 'info', 'close', 
 "Your job have been submited to bacula.
@@ -418,8 +658,44 @@ To follow it, you must use bconsole (or install/configure bweb)");
        $widget->run;
        $widget->destroy();
     }
+}
 
-    $self->on_cancel_resto_clicked();
+sub on_use_regexp_toggled
+{
+    my ($self,$widget) = @_;
+    my $act = $widget->get_active();
+
+    foreach my $w ('entry_launch_where') {
+       $self->{glade}->get_widget($w)->set_sensitive(!$act);
+    }
+
+    foreach my $w ('entry_add_prefix', 'entry_strip_prefix', 
+                  'entry_add_suffix','entry_rwhere','chk_use_regexp')
+    {
+       $self->{glade}->get_widget($w)->set_sensitive($act);
+    }
+
+    if ($act) { # if we activate file relocation, we reset use_regexp
+       $self->{glade}->get_widget('entry_rwhere')->set_sensitive(0);
+       $self->{glade}->get_widget('chk_use_regexp')->set_active(0);
+    }
+}
+
+
+sub on_use_rwhere_toggled
+{
+    my ($self,$widget) = @_;
+    my $act = $widget->get_active();
+
+    foreach my $w ('entry_rwhere') {
+       $self->{glade}->get_widget($w)->set_sensitive($act);
+    }
+
+    foreach my $w ('entry_add_prefix', 'entry_strip_prefix', 
+                  'entry_add_suffix')
+    {
+       $self->{glade}->get_widget($w)->set_sensitive(!$act);
+    }
 }
 
 sub on_cancel_resto_clicked
@@ -428,6 +704,45 @@ sub on_cancel_resto_clicked
     $self->{glade}->get_widget('dlg_launch')->destroy();
 }
 
+sub get_where
+{
+    my ($self) = @_ ;
+
+    if ($self->{glade}->get_widget('chk_file_relocation')->get_active()) {
+       # using regexp
+       if ($self->{glade}->get_widget('chk_use_regexp')->get_active()) {
+
+           return ('regexwhere', 
+                   $self->{glade}->get_widget('entry_rwhere')->get_active());
+       }
+           
+       # using regexp utils
+       my @ret;
+       my ($strip_prefix, $add_prefix, $add_suffix) = 
+           ($self->{glade}->get_widget('entry_strip_prefix')->get_text(),
+            $self->{glade}->get_widget('entry_add_prefix')->get_text(),
+            $self->{glade}->get_widget('entry_add_suffix')->get_text());
+           
+       if ($strip_prefix) {
+           push @ret,"!$strip_prefix!!i";
+       }
+       
+       if ($add_prefix) {
+           push @ret,"!^!$add_prefix!";
+       }
+
+       if ($add_suffix) {
+           push @ret,"!([^/])\$!\$1$add_suffix!";
+       }
+
+       return ('regexwhere', join(',', @ret));
+
+    } else { # using where
+       return ('where', 
+               $self->{glade}->get_widget('entry_launch_where')->get_text());
+    }
+}
+
 sub on_submit_resto_clicked
 {
     my ($self) = @_ ;
@@ -446,7 +761,8 @@ sub on_submit_resto_clicked
     my $storage = $glade->get_widget('combo_launch_storage')
                               ->get_active_text();
 
-    my $where = $glade->get_widget('entry_launch_where')->get_text();
+    my ($where_cmd, $where) = $self->get_where();
+    print "$where_cmd => $where\n";
 
     my $job = $glade->get_widget('combo_launch_job')
                               ->get_active_text();
@@ -473,7 +789,7 @@ sub on_submit_resto_clicked
                                       client  => $client,
                                       storage => $storage,
                                       fileset => $fileset,
-                                      where   => $where,
+                                      $where_cmd => $where,
                                       replace => $replace,
                                       priority=> $prio,
                                       bootstrap => $r);
@@ -528,6 +844,10 @@ sub copy_bsr
     print "$src => $dst\n"
        if ($debug);
 
+    if (!$dst) {
+        return $src;
+    }
+
     my $ret=0 ;
     my $err ; 
     my $dstfile;
@@ -584,267 +904,17 @@ sub on_about_okbutton_clicked
 1;
 
 ################################################################
-# preference reader
-package Pref;
 
-sub new
-{
-    my ($class, $config_file) = @_;
-    
-    my $self = bless {
-       config_file => $config_file,
-       password => '',         # db passwd
-       username => '',         # db username
-       connection_string => '',# db connection string
-       bconsole => 'bconsole', # path and arg to bconsole
-       bsr_dest => '',         # destination url for bsr files
-       debug    => 0,          # debug level 0|1
-       use_ok_bkp_only => 1,   # dont use bad backup
-       bweb     => 'http://localhost/cgi-bin/bweb/bweb.pl', # bweb url
-       glade_file => $glade_file,
-       see_all_versions => 0,  # display all file versions in FileInfo
-       mozilla  => 'mozilla',  # mozilla bin
-       default_restore_job => 'restore', # regular expression to select default
-                                  # restore job
-
-       # keywords that are used to fill DlgPref
-       chk_keyword =>  [ qw/use_ok_bkp_only debug see_all_versions/ ],
-        entry_keyword => [ qw/username password bweb mozilla
-                         connection_string default_restore_job
-                         bconsole bsr_dest glade_file/],
-    };
-
-    $self->read_config();
-
-    return $self;
-}
-
-sub read_config
-{
-    my ($self) = @_;
-
-    # We read the parameters. They come from the configuration files
-    my $cfgfile ; my $tmpbuffer;
-    if (open FICCFG, $self->{config_file})
-    {
-       while(read FICCFG,$tmpbuffer,4096)
-       {
-           $cfgfile .= $tmpbuffer;
-       }
-       close FICCFG;
-       my $refparams;
-       no strict; # I have no idea of the contents of the file
-       eval '$refparams' . " = $cfgfile";
-       use strict;
-       
-       for my $p (keys %{$refparams}) {
-           $self->{$p} = $refparams->{$p};
-       }
-
-       if (defined $self->{debug}) {
-           $debug = $self->{debug} ;
-       }
-    } else {
-       # TODO : Force dumb default values and display a message
-    }
-}
-
-sub write_config
-{
-    my ($self) = @_;
-    
-    my %parameters;
-
-    for my $k (@{ $self->{entry_keyword} }) { 
-       $parameters{$k} = $self->{$k};
-    }
-
-    for my $k (@{ $self->{chk_keyword} }) { 
-       $parameters{$k} = $self->{$k};
-    }
-
-    if (open FICCFG,">$self->{config_file}")
-    {
-       print FICCFG Data::Dumper->Dump([\%parameters], [qw($parameters)]);
-       close FICCFG;
-    }
-    else
-    {
-       # TODO : Display a message
-    }
-}
-
-sub connect_db
-{
-    my $self = shift ;
-
-    if ($self->{dbh}) {
-       $self->{dbh}->disconnect() ;
-    }
-
-    delete $self->{dbh};
-    delete $self->{error};
-
-    if (not $self->{connection_string})
-    {
-       # The parameters have not been set. Maybe the conf
-       # file is empty for now
-       $self->{error} = "No configuration found for database connection. " .
-                        "Please set this up.";
-       return 0;
-    }
-    
-    if (not eval {
-       $self->{dbh} = DBI->connect($self->{connection_string}, 
-                                   $self->{username},
-                                   $self->{password})
-       })
-    {
-       $self->{error} = "Can't open bacula database. " . 
-                        "Database connect string '" . 
-                         $self->{connection_string} ."' $!";
-       return 0;
-    }
-    $self->{dbh}->{RowCacheSize}=100;
-    return 1;
-}
-
-sub go_bweb
-{    
-    my ($self, $url, $msg) = @_;
-
-    unless ($self->{mozilla} and $self->{bweb}) {
-       new DlgWarn("You must install Bweb and set your mozilla bin to $msg");
-       return -1;
-    }
-
-    if ($^O eq 'MSWin32') {
-       system("start /B $self->{mozilla} \"$self->{bweb}$url\"");
-
-    } elsif (!fork()) {
-       system("$self->{mozilla} -remote 'Ping()'");
-       my $cmd = "$self->{mozilla} '$self->{bweb}$url'";
-       if ($? == 0) {
-           $cmd = "$self->{mozilla} -remote 'OpenURL($self->{bweb}$url,new-tab)'" ;
-       }
-       exec($cmd);
-       exit 1;
-    } 
-    return ($? == 0);
-}
-
-1;
-
-################################################################
-# Manage preference
-package DlgPref;
-
-# my $pref = new Pref(config_file => 'brestore.conf');
-# my $dlg = new DlgPref($pref);
-# my $dlg_resto = new DlgResto($pref);
-# $dlg->display($dlg_resto);
-sub new
-{
-    my ($class, $pref) = @_;
-
-    my $self = bless {
-       pref => $pref,          # Pref ref
-       dlgresto => undef,      # DlgResto ref
-       };
-
-    return $self;
-}
-
-sub display
-{
-    my ($self, $dlgresto) = @_ ;
-
-    unless ($self->{glade}) {
-       $self->{glade} = Gtk2::GladeXML->new($glade_file, "dlg_pref") ;
-       $self->{glade}->signal_autoconnect_from_package($self);
-    }
-
-    $self->{dlgresto} = $dlgresto;
-
-    my $g = $self->{glade};
-    my $p = $self->{pref};
-
-    for my $k (@{ $p->{entry_keyword} }) {
-       $g->get_widget("entry_$k")->set_text($p->{$k}) ;
-    }
-
-    for my $k (@{ $p->{chk_keyword} }) {
-       $g->get_widget("chkbp_$k")->set_active($p->{$k}) ;
-    }
-
-    $g->get_widget("dlg_pref")->show_all() ;
-}
-
-sub on_applybutton_clicked
-{
-    my ($self) = @_;
-    my $glade = $self->{glade};
-    my $pref  = $self->{pref};
-
-    for my $k (@{ $pref->{entry_keyword} }) {
-       my $w = $glade->get_widget("entry_$k") ;
-       $pref->{$k} = $w->get_text();
-    }
-
-    for my $k (@{ $pref->{chk_keyword} }) {
-       my $w = $glade->get_widget("chkbp_$k") ;
-       $pref->{$k} = $w->get_active();
-    }
-
-    $pref->write_config();
-    if ($pref->connect_db()) {
-       $self->{dlgresto}->set_dbh($pref->{dbh});
-        $self->{dlgresto}->set_status('Preferences updated');
-       $self->{dlgresto}->init_server_backup_combobox();
-       $self->{dlgresto}->create_brestore_tables();
-       $self->{dlgresto}->set_status($pref->{error});
-    } else {
-       $self->{dlgresto}->set_status($pref->{error});
-    }
-}
-
-# Handle prefs ok click (apply/dismiss dialog)
-sub on_okbutton_clicked 
-{
-    my ($self) = @_;
-    $self->on_applybutton_clicked();
-
-    unless ($self->{pref}->{error}) {
-       $self->on_cancelbutton_clicked();
-    }
-}
-sub on_dialog_delete_event
-{
-    my ($self) = @_;
-    $self->on_cancelbutton_clicked();
-    1;
-}
-
-sub on_cancelbutton_clicked
-{
-    my ($self) = @_;
-    $self->{glade}->get_widget('dlg_pref')->hide();
-    delete $self->{dlgresto};
-}
-1;
-
-################################################################
-# Main Interface
-
-package DlgResto;
-
-our $diricon;
-our $fileicon;
-our $yesicon;
-our $noicon;
-
-# Kept as is from the perl-gtk example. Draws the pretty icons
-sub render_icons 
+package DlgResto;
+use base qw/Bbase/;
+
+our $diricon;
+our $fileicon;
+our $yesicon;
+our $noicon;
+
+# Kept as is from the perl-gtk example. Draws the pretty icons
+sub render_icons 
 {
     my $self = shift;
     unless ($diricon) {
@@ -855,7 +925,6 @@ sub render_icons
        $noicon   = $self->{mainwin}->render_icon('gtk-no',   $size);
     }
 }
-
 # init combo (and create ListStore object)
 sub init_combo
 {
@@ -897,7 +966,7 @@ sub fill_combo
 # display Mb/Gb/Kb
 sub human
 {
-    my @unit = qw(b Kb Mb Gb Tb);
+    my @unit = qw(B KB MB GB TB);
     my $val = shift;
     my $i=0;
     my $format = '%i %s';
@@ -909,15 +978,41 @@ sub human
     return sprintf($format, $val, $unit[$i]);
 }
 
-sub set_dbh
+sub get_wanted_job_status
 {
-    my ($self, $dbh) = @_;
-    $self->{dbh} = $dbh;
+    my ($ok_only) = @_;
+
+    if ($ok_only) {
+       return "'T'";
+    } else {
+       return "'T', 'A', 'E'";
+    }
 }
 
-sub init_drag_drop
-{
-    my ($fileview) = shift;
+# This sub gives a full list of the EndTimes for a ClientId
+# ( [ 'Date', 'FileSet', 'Type', 'Status', 'JobId'], 
+#   ['Date', 'FileSet', 'Type', 'Status', 'JobId']..)
+sub get_all_endtimes_for_job
+{
+    my ($self, $client, $ok_only)=@_;
+    my $status = get_wanted_job_status($ok_only);
+    my $query = "
+ SELECT Job.EndTime, FileSet.FileSet, Job.Level, Job.JobStatus, Job.JobId
+  FROM Job,Client,FileSet
+  WHERE Job.ClientId=Client.ClientId
+  AND Client.Name = '$client'
+  AND Job.Type = 'B'
+  AND JobStatus IN ($status)
+  AND Job.FileSetId = FileSet.FileSetId
+  ORDER BY EndTime desc";
+    my $result = $self->dbh_selectall_arrayref($query);
+
+    return @$result;
+}
+
+sub init_drag_drop
+{
+    my ($fileview) = shift;
     my $fileview_target_entry = {target => 'STRING',
                                 flags => ['GTK_TARGET_SAME_APP'],
                                 info => 40 };
@@ -935,52 +1030,11 @@ sub init_drag_drop
     }
 }
 
-sub debug
-{
-    my ($self, $what) = @_;
-
-    if ($debug) {
-       if (ref $what) {
-           print Data::Dumper::Dumper($what);
-       } elsif (defined $what) {
-           print "$what\n";
-       }
-    }
-}
-
-sub dbh_prepare
-{
-    my ($self, $query) = @_;
-    $self->debug($query);
-    return $self->{dbh}->prepare($query);    
-}
-
-sub dbh_do
-{
-    my ($self, $query) = @_;
-    $self->debug($query);
-    return $self->{dbh}->do($query);
-}
-
-sub dbh_selectall_arrayref
-{
-    my ($self, $query) = @_;
-    $self->debug($query);
-    return $self->{dbh}->selectall_arrayref($query);
-}
-
-sub dbh_selectrow_arrayref
-{
-    my ($self, $query) = @_;
-    $self->debug($query);
-    return $self->{dbh}->selectrow_arrayref($query);
-}
-
 sub new
 {
     my ($class, $pref) = @_;
     my $self = bless { 
-       pref => $pref,
+       conf => $pref,
        dirtree => undef,
        CurrentJobIds => [],
        location => undef,      # location entry widget
@@ -993,14 +1047,17 @@ sub new
        fileattrib => {},       # cache file
        fileview   => undef,    # fileview widget SimpleList
        fileinfo   => undef,    # fileinfo widget SimpleList
-       cwd   => '/',
+       cwd => '/',
        client_combobox => undef, # client_combobox widget
        restore_backup_combobox => undef, # date combobox widget
        list_client => undef,   # Gtk2::ListStore
         list_backup => undef,   # Gtk2::ListStore
-       cache_ppathid => {},    # 
+       cache_ppathid => {},    #
+       bvfs => undef,          # Bfvs object
     };
 
+    $self->{bvfs} = new Bvfs(conf => $pref);
+
     # load menu (to use handler with self reference)
     my $glade = Gtk2::GladeXML->new($glade_file, "filelist_file_menu");
     $glade->signal_autoconnect_from_package($self);
@@ -1035,6 +1092,8 @@ sub new
     my $widget = $glade->get_widget('fileview');
     my $fileview = $self->{fileview} = Gtk2::SimpleList->new_from_treeview(
                                              $widget,
+                                             'h_pathid'      => 'hidden',
+                                             'h_filenameid'  => 'hidden',
                                              'h_name'        => 'hidden',
                                              'h_jobid'       => 'hidden',
                                              'h_type'        => 'hidden',
@@ -1044,25 +1103,27 @@ sub new
                                              'Size'          => 'text',
                                              'Date'          => 'text');
     init_drag_drop($fileview);
-    $fileview->set_search_column(4); # search on File Name
+    $fileview->set_search_column(6); # search on File Name
 
     # Connect glade-restore_list to Gtk2::SimpleList
     $widget = $glade->get_widget('restorelist');
     my $restore_list = $self->{restore_list} = Gtk2::SimpleList->new_from_treeview(
                                               $widget,
+                                             'h_pathid'      => 'hidden', #0
+                                             'h_filenameid'  => 'hidden',
                                              'h_name'      => 'hidden',
                                               'h_jobid'     => 'hidden',
                                              'h_type'      => 'hidden',
-                                              'h_curjobid'  => 'hidden',
+                                              'h_curjobid'  => 'hidden', #5
 
                                               ''            => 'pixbuf',
                                               'File Name'   => 'text',
                                               'JobId'       => 'text',
                                               'FileIndex'   => 'text',
 
-                                             'Nb Files'    => 'text', #8
-                                              'Size'        => 'text', #9
-                                             'size_b'      => 'hidden', #10
+                                             'Nb Files'    => 'text', #10
+                                              'Size'        => 'text', #11
+                                             'size_b'      => 'hidden', #12
                                              );
 
     my @restore_list_target_table = ({'target' => 'STRING',
@@ -1075,6 +1136,8 @@ sub new
     $widget = $glade->get_widget('infoview');
     my $infoview = $self->{fileinfo} = Gtk2::SimpleList->new_from_treeview(
                   $widget,
+                   'h_pathid'      => 'hidden',
+                  'h_filenameid'  => 'hidden',
                    'h_name'        => 'hidden',
                    'h_jobid'       => 'hidden',
                   'h_type'        => 'hidden',
@@ -1091,9 +1154,10 @@ sub new
     $pref->connect_db() ||  $self->{dlg_pref}->display($self);
 
     if ($pref->{dbh}) {
-       $self->{dbh} = $pref->{dbh};
        $self->init_server_backup_combobox();
-       $self->create_brestore_tables();
+       if ($self->{bvfs}->create_brestore_tables()) {
+           new DlgWarn("brestore can't find brestore_xxx tables on your database. I will create them.");
+       }
     }
 
     $self->set_status($pref->{error});
@@ -1133,40 +1197,6 @@ sub get_all_clients
     return map { $_->[0] } @$result;
 }
 
-sub get_wanted_job_status
-{
-    my ($ok_only) = @_;
-
-    if ($ok_only) {
-       return "'T'";
-    } else {
-       return "'T', 'A', 'E'";
-    }
-}
-
-# This sub gives a full list of the EndTimes for a ClientId
-# ( [ 'Date', 'FileSet', 'Type', 'Status', 'JobId'], 
-#   ['Date', 'FileSet', 'Type', 'Status', 'JobId']..)
-sub get_all_endtimes_for_job
-{
-    my ($dbh, $client, $ok_only)=@_;
-    my $status = get_wanted_job_status($ok_only);
-    my $query = "
- SELECT Job.EndTime, FileSet.FileSet, Job.Level, Job.JobStatus, Job.JobId
-  FROM Job,Client,FileSet
-  WHERE Job.ClientId=Client.ClientId
-  AND Client.Name = '$client'
-  AND Job.Type = 'B'
-  AND JobStatus IN ($status)
-  AND Job.FileSetId = FileSet.FileSetId
-  ORDER BY EndTime desc";
-    print STDERR $query,"\n" if $debug;
-    my $result = $dbh->selectall_arrayref($query);
-
-    return @$result;
-}
-
-
 # init infoview widget
 sub clear_infoview
 {
@@ -1198,22 +1228,26 @@ sub on_estimate_clicked
 
     my $title = "Computing size...\n";
     my $txt="";
+
+    # ($pid,$fid,$name,$jobid,'file',$curjobids,
+    #   undef, undef, undef, $dirfileindex);
     foreach my $entry (@{$self->{restore_list}->{data}})
     {
-       unless ($entry->[9]) {
-           my ($size, $nb) = $self->estimate_restore_size($entry);
-           $entry->[10] = $size;
-           $entry->[9] = human($size);
-           $entry->[8] = $nb;
+       unless ($entry->[11]) {
+           my ($size, $nb) = $self->{bvfs}->estimate_restore_size($entry,\&refresh_screen);
+           $entry->[12] = $size;
+           $entry->[11] = human($size);
+           $entry->[10] = $nb;
        }
 
-       my $name = unpack('u', $entry->[0]);
+       my $name = unpack('u', $entry->[2]);
 
-       $txt .= "\n<i>$name</i> : " . $entry->[8] . " file(s)/" . $entry->[9] ;
+       $txt .= "\n<i>$name</i> : ". $entry->[10] ." file(s)/". $entry->[11] ;
+       $self->debug($title . $txt);
        $widget->set_markup($title . $txt);
        
-       $size_total+=$entry->[10];
-       $nb_total+=$entry->[8];
+       $size_total+=$entry->[12];
+       $nb_total+=$entry->[10];
        refresh_screen();
     }
     
@@ -1224,6 +1258,8 @@ sub on_estimate_clicked
     return 0;
 }
 
+
+
 sub on_gen_bsr_clicked
 {
     my ($self) = @_;
@@ -1272,11 +1308,16 @@ sub on_gen_bsr_clicked
     }
 }
 
+
 use File::Temp qw/tempfile/;
 
 sub on_go_button_clicked 
 {
     my $self = shift;
+    unless (scalar(@{$self->{restore_list}->{data}})) {
+       new DlgWarn("No file to restore");
+       return 0;
+    }
     my $bsr = $self->create_filelist();
     my ($fh, $filename) = tempfile();
     $fh->print($bsr);
@@ -1290,13 +1331,14 @@ sub on_go_button_clicked
     my %a = map { $_ => 1 } ($bsr =~ /Volume="(.+)"/g);
     my $vol = [ keys %a ] ;    # need only one occurrence of each volume
 
-    new DlgLaunch(pref     => $self->{pref},
+    new DlgLaunch(pref     => $self->{conf},
                  volumes  => $vol,
                  bsr_file => $filename,
                  );
 
 }
 
+
 our $client_list_empty = 'Clients list'; 
 our %type_markup = ('F' => '<b>$label F</b>',
                    'D' => '$label D',
@@ -1312,7 +1354,6 @@ sub on_list_client_changed
 {
     my ($self, $widget) = @_;
     return 0 unless defined $self->{fileview};
-    my $dbh = $self->{dbh};
 
     $self->{list_backup}->clear();
 
@@ -1320,9 +1361,22 @@ sub on_list_client_changed
        return 0 ;
     }
 
-    my @endtimes=get_all_endtimes_for_job($dbh, 
-                                         $self->current_client,
-                                         $self->{pref}->{use_ok_bkp_only});
+    $self->{CurrentJobIds} = [
+                             set_job_ids_for_date($self->dbh(),
+                                                  $self->current_client,
+                                                  $self->current_date,
+                                                  $self->{conf}->{use_ok_bkp_only})
+                             ];
+
+    my $fs = $self->{bvfs};
+    $fs->set_curjobids(@{$self->{CurrentJobIds}});
+    $fs->ch_dir($fs->get_root());
+    # refresh_fileview will be done by list_backup_changed
+
+
+    my @endtimes=$self->get_all_endtimes_for_job($self->current_client,
+                                                $self->{conf}->{use_ok_bkp_only});
+
     foreach my $endtime (@endtimes)
     {
        my $i = $self->{list_backup}->append();
@@ -1337,21 +1391,10 @@ sub on_list_client_changed
                                  );
     }
     $self->{restore_backup_combobox}->set_active(0);
-
-    $self->{CurrentJobIds} = [
-                             set_job_ids_for_date($dbh,
-                                                  $self->current_client,
-                                                  $self->current_date,
-                                                  $self->{pref}->{use_ok_bkp_only})
-                             ];
-
-    $self->update_brestore_table(@{$self->{CurrentJobIds}});
-
-    $self->ch_dir('');
-    $self->refresh_fileview();
     0;
 }
 
+
 sub fill_server_list
 {
     my ($dbh, $combo, $list) = @_;
@@ -1374,7 +1417,7 @@ sub fill_server_list
 sub init_server_backup_combobox
 {
     my $self = shift ;
-    fill_server_list($self->{dbh}, 
+    fill_server_list($self->{conf}->{dbh}, 
                     $self->{client_combobox},
                     $self->{list_client}) ;
 }
@@ -1388,7 +1431,7 @@ sub refresh_fileview
     my ($self) = @_;
     my $fileview = $self->{fileview};
     my $client_combobox = $self->{client_combobox};
-    my $cwd = $self->{cwd};
+    my $bvfs = $self->{bvfs};
 
     @{$fileview->{data}} = ();
 
@@ -1401,29 +1444,30 @@ sub refresh_fileview
        return;
     }
 
-    my @list_dirs     = $self->list_dirs($cwd,$client_name);
-    # [ [listfiles.id, listfiles.Name, File.LStat, File.JobId]..]
-    my $files    = $self->list_files($cwd); 
-    print "CWD : $cwd\n" if ($debug);
+    # [ [dirid, dir_basename, File.LStat, jobid]..]
+    my $list_dirs = $bvfs->ls_dirs();
+    # [ [filenameid, listfiles.id, listfiles.Name, File.LStat, File.JobId]..]
+    my $files     = $bvfs->ls_files(); 
     
     my $file_count = 0 ;
     my $total_bytes = 0;
     
     # Add directories to view
-    foreach my $dir_entry (@list_dirs) {
-       #my $time = localtime($self->dir_attrib("$cwd/$dir",'st_mtime'));
-       my $time = localtime(lstat_attrib($dir_entry->[1],'st_mtime'));
-       my $dir = $dir_entry->[0];
+    foreach my $dir_entry (@$list_dirs) {
+       my $time = localtime(Bvfs::lstat_attrib($dir_entry->[2],'st_mtime'));
        $total_bytes += 4096;
        $file_count++;
 
        listview_push($fileview,
-                     $dir,
-                     $self->dir_attrib("$cwd/$dir",'jobid'),
+                     $dir_entry->[0],
+                     0,
+                     $dir_entry->[1],
+                     # TODO: voir ce que l'on met la
+                     $dir_entry->[3],
                      'dir',
 
                      $diricon, 
-                     $dir, 
+                     $dir_entry->[1]
                      "4 Kb", 
                      $time);
     }
@@ -1431,24 +1475,26 @@ sub refresh_fileview
     # Add files to view 
     foreach my $file (@$files) 
     {
-       my $size = file_attrib($file,'st_size');
-       my $time = localtime(file_attrib($file,'st_mtime'));
+       my $size = Bvfs::file_attrib($file,'st_size');
+       my $time = localtime(Bvfs::file_attrib($file,'st_mtime'));
        $total_bytes += $size;
        $file_count++;
-       # $file = [listfiles.id, listfiles.Name, File.LStat, File.JobId]
-
+       # $file = [filenameid,listfiles.id,listfiles.Name,File.LStat,File.JobId]
        listview_push($fileview,
-                     $file->[1],
-                     $file->[3],
+                     $bvfs->{cwdid},
+                     $file->[0],
+                     $file->[2],
+                     $file->[4],
                      'file',
                      
                      $fileicon, 
-                     $file->[1], 
+                     $file->[2], 
                      human($size), $time);
     }
     
     $self->set_status("$file_count files/" . human($total_bytes));
-
+    $self->{cwd} = $self->{bvfs}->pwd();
+    $self->{location}->set_text($self->{cwd});
     # set a decent default selection (makes keyboard nav easy)
     $fileview->select(0);
 }
@@ -1472,18 +1518,13 @@ sub drag_set_info
        # doc ligne 93
         # Ok, we have a corner case :
         # path can be empty
-        my $file;
-        if ($path eq '')
-        {
-           $file = pack("u", $file_info[0]);
-       }
-       else
-       {
-               $file = pack("u", $path . '/' . $file_info[0]);
-       }
-       push @ret, join(" ; ", $file, 
-                       $file_info[1], # $jobid
-                       $file_info[2], # $type
+       my $file = pack("u", $path . $file_info[2]);
+
+       push @ret, join(" ; ", $file,
+                       $file_info[0], # $pathid
+                       $file_info[1], # $filenameid
+                       $file_info[3], # $jobid
+                       $file_info[4], # $type
                        );
     }
 
@@ -1492,6 +1533,8 @@ sub drag_set_info
     $data->set_text($data_get,-1);
 }
 
+
+
 sub fileview_data_get
 {
     my ($self, $widget, $context, $data, $info, $time,$string) = @_;
@@ -1508,24 +1551,26 @@ sub restore_list_data_received
 {
     my ($self, $widget, $context, $x, $y, $data, $info, $time) = @_;
     my @ret;
-
+    $self->debug("start\n");
     if  ($info eq 40 || $info eq 0) # patch for display!=:0
     {
        foreach my $elt (split(/ :: /, $data->data()))
        {
-           
-           my ($file, $jobid, $type) = 
+           my ($file, $pathid, $filenameid, $jobid, $type) = 
                split(/ ; /, $elt);
            $file = unpack("u", $file);
            
-           $self->add_selected_file_to_list($file, $jobid, $type);
+           $self->add_selected_file_to_list($pathid,$filenameid,
+                                            $file, $jobid, $type);
        }
     }
+    $self->debug("end\n");
 }
 
 sub on_back_button_clicked {
     my $self = shift;
-    $self->up_dir();
+    $self->{bvfs}->up_dir();
+    $self->refresh_fileview();
 }
 sub on_location_go_button_clicked 
 {
@@ -1543,20 +1588,7 @@ sub on_bweb_activate
 {
     my $self = shift; 
     $self->set_status("Open bweb on your browser");
-    $self->{pref}->go_bweb('', "go on bweb");
-}
-
-# Change to parent directory
-sub up_dir
-{
-    my $self = shift ;
-    if ($self->{cwd} eq '/')
-    {
-       $self->ch_dir('');
-    }
-    my @dirs = split(/\//, $self->{cwd});
-    pop @dirs;
-    $self->ch_dir(join('/', @dirs));
+    $self->{conf}->go_bweb('', "go on bweb");
 }
 
 # Change the current working directory
@@ -1564,12 +1596,15 @@ sub up_dir
 #
 sub ch_dir 
 {
-    my $self = shift;
-    $self->{cwd} = shift;
-    
-    $self->refresh_fileview();
-    $self->{location}->set_text($self->{cwd});
-    
+    my ($self,$l) = @_;
+
+    my $p = $self->{bvfs}->get_pathid($l);
+    if ($p) {
+       $self->{bvfs}->ch_dir($p);
+       $self->refresh_fileview();
+    } else {
+       $self->set_status("Can't find $l");
+    }
     1;
 }
 
@@ -1617,8 +1652,8 @@ sub listview_get_first
     my ($list) = shift; 
     my @selected = $list->get_selected_indices();
     if (@selected > 0) {
-       my ($name, @other) = @{$list->{data}->[$selected[0]]};
-       return (unpack('u', $name), @other);
+       my ($pid,$fid,$name, @other) = @{$list->{data}->[$selected[0]]};
+       return ($pid,$fid,unpack('u', $name), @other);
     } else {
        return undef;
     }
@@ -1631,20 +1666,19 @@ sub listview_get_all
     my @selected = $list->get_selected_indices();
     my @ret;
     for my $i (@selected) {
-       my ($name, @other) = @{$list->{data}->[$i]};
-       push @ret, [unpack('u', $name), @other];
+       my ($pid,$fid,$name, @other) = @{$list->{data}->[$i]};
+       push @ret, [$pid,$fid,unpack('u', $name), @other];
     } 
     return @ret;
 }
 
-
 sub listview_push
 {
-    my ($list, $name, @other) = @_;
-    push @{$list->{data}}, [pack('u', $name), @other];
+    my ($list, $pid, $fid, $name, @other) = @_;
+    push @{$list->{data}}, [$pid,$fid,pack('u', $name), @other];
 }
 
-#----------------------------------------------------------------------
+#-----------------------------------------------------------------
 # Handle keypress in file-view
 #   * Translates backspace into a 'cd ..' command 
 #   * All other key presses left for GTK
@@ -1657,7 +1691,7 @@ sub on_fileview_key_release_event
        return 0;
     }
     if ($event->keyval == $Gtk2::Gdk::Keysyms{BackSpace}) {
-       $self->up_dir();
+       $self->on_back_button_clicked();
        return 1; # eat keypress
     }
 
@@ -1669,7 +1703,7 @@ sub on_forward_keypress
     return 0;
 }
 
-#----------------------------------------------------------------------
+#-------------------------------------------------------------------
 # Handle double-click (or enter) on file-view
 #   * Translates into a 'cd <dir>' command
 #
@@ -1677,25 +1711,14 @@ sub on_fileview_row_activated
 {
     my ($self, $widget) = @_;
     
-    my ($name, undef, $type, undef) = listview_get_first($widget);
+    my ($pid,$fid,$name, undef, $type, undef) = listview_get_first($widget);
 
     if ($type eq 'dir')
     {
-       if ($self->{cwd} eq '')
-       {
-               $self->ch_dir($name);
-       }
-       elsif ($self->{cwd} eq '/')
-       {
-               $self->ch_dir('/' . $name);
-       }
-       else
-       {
-               $self->ch_dir($self->{cwd} . '/' . $name);
-       }
-
+       $self->{bvfs}->ch_dir($pid);
+       $self->refresh_fileview();
     } else {
-       $self->fill_infoview($self->{cwd}, $name);
+       $self->fill_infoview($pid,$fid,$name);
     }
     
     return 1; # consume event
@@ -1703,22 +1726,21 @@ sub on_fileview_row_activated
 
 sub fill_infoview
 {
-    my ($self, $path, $file) = @_;
+    my ($self, $path, $file, $fn) = @_;
     $self->clear_infoview();
-    my @v = get_all_file_versions($self->{dbh}, 
-                                 "$path/", 
-                                 $file,
-                                 $self->current_client,
-                                 $self->{pref}->{see_all_versions});
+    my @v = $self->{bvfs}->get_all_file_versions($path, 
+                                                $file,
+                                                $self->current_client,
+                                                $self->{conf}->{see_all_versions});
     for my $ver (@v) {
-       my (undef,$fn,$jobid,$fileindex,$mtime,$size,$inchanger,$md5,$volname)
-           = @{$ver};
+       my (undef,$pid,$fid,$jobid,$fileindex,$mtime,
+           $size,$inchanger,$md5,$volname) = @{$ver};
        my $icon = ($inchanger)?$yesicon:$noicon;
 
        $mtime = localtime($mtime) ;
 
-       listview_push($self->{fileinfo},
-                     $file, $jobid, 'file', 
+       listview_push($self->{fileinfo},$pid,$fid,
+                     $fn, $jobid, 'file', 
                      $icon, $volname, $jobid, human($size), $mtime, $md5);
     }
 }
@@ -1741,13 +1763,12 @@ sub on_list_backups_changed
     return 0 unless defined $self->{fileview};
 
     $self->{CurrentJobIds} = [
-                             set_job_ids_for_date($self->{dbh},
+                             set_job_ids_for_date($self->dbh(),
                                                   $self->current_client,
                                                   $self->current_date,
-                                                  $self->{pref}->{use_ok_bkp_only})
+                                                  $self->{conf}->{use_ok_bkp_only})
                              ];
-    $self->update_brestore_table(@{$self->{CurrentJobIds}});
-
+    $self->{bvfs}->set_curjobids(@{$self->{CurrentJobIds}});
     $self->refresh_fileview();
     0;
 }
@@ -1790,11 +1811,11 @@ sub on_see_all_version
     my @lst = listview_get_all($self->{fileview});
 
     for my $i (@lst) {
-       my ($name, undef) = @{$i};
+       my ($pid,$fid,$name, undef) = @{$i};
 
-       new DlgFileVersion($self->{dbh}, 
+       new DlgFileVersion($self->{bvfs}, 
                           $self->current_client, 
-                          $self->{cwd}, $name);
+                          $pid,$fid,$self->{cwd},$name);
     }
 }
 
@@ -1807,7 +1828,7 @@ sub on_right_click_filelist
     my $type = '';
 
     if (@sel == 1) {
-       $type = $sel[0]->[2];   # $type
+       $type = $sel[0]->[4];   # $type
     }
 
     my $w;
@@ -1836,18 +1857,17 @@ sub context_add_to_filelist
 
     foreach my $i (@sel)
     {
-       my ($file, $jobid, $type, undef) = @{$i};
-       $file = $self->{cwd} . '/' . $file;
-       $self->add_selected_file_to_list($file, $jobid, $type);
+       my ($pid, $fid, $file, $jobid, $type, undef) = @{$i};
+       $file = $self->{cwd} . $file;
+       $self->add_selected_file_to_list($pid, $fid, $file, $jobid, $type);
     }
 }
 
 # Adds a file to the filelist
 sub add_selected_file_to_list
 {
-    my ($self, $name, $jobid, $type)=@_;
+    my ($self, $pid, $fid, $name, $jobid, $type)=@_;
 
-    my $dbh = $self->{dbh};
     my $restore_list = $self->{restore_list};
 
     my $curjobids=join(',', @{$self->{CurrentJobIds}});
@@ -1861,16 +1881,16 @@ sub add_selected_file_to_list
        {
                $name .= '/'; # For bacula
        }
-       my $dirfileindex = get_fileindex_from_dir_jobid($dbh,$name,$jobid);
-       listview_push($restore_list, 
+       my $dirfileindex = $self->{bvfs}->get_fileindex_from_dir_jobid($pid,$jobid);
+       listview_push($restore_list,$pid,0, 
                      $name, $jobid, 'dir', $curjobids,
                      $diricon, $name,$curjobids,$dirfileindex);
     }
     elsif ($type eq 'file')
     {
-       my $fileindex = get_fileindex_from_file_jobid($dbh,$name,$jobid);
+       my $fileindex = $self->{bvfs}->get_fileindex_from_file_jobid($pid,$fid,$jobid);
 
-       listview_push($restore_list,
+       listview_push($restore_list,$pid,$fid,
                      $name, $jobid, 'file', $curjobids,
                      $fileicon, $name, $jobid, $fileindex );
     }
@@ -1909,7 +1929,6 @@ sub set_job_ids_for_date
                AND JobStatus IN ($status)
                ORDER BY FileSet, JobTDate DESC";
        
-    print STDERR $query,"\n" if $debug;
     my @CurrentJobIds;
     my $result = $dbh->selectall_arrayref($query);
     my %progress;
@@ -1935,479 +1954,21 @@ sub set_job_ids_for_date
        }
        elsif ($level eq 'F')
        {
-           push @CurrentJobIds,($jobid);
-       }
-
-       my $status = $refrow->[3] ;
-       if ($status eq 'T') {              # good end of job
-           $progress{$fileset} = $level;
-       }
-    }
-    print Data::Dumper::Dumper(\@CurrentJobIds) if $debug;
-
-    return @CurrentJobIds;
-}
-
-# Lists all directories contained inside a directory.
-# Uses the current dir, the client name, and CurrentJobIds for visibility.
-# Returns an array of dirs
-sub list_dirs
-{
-    my ($self,$dir,$client)=@_;
-
-    print "list_dirs(<$dir>, <$client>)\n" if $debug;
-
-    if ($dir ne '' and substr $dir,-1 ne '/')
-    {
-       $dir .= '/'; # In the db, there is a / at the end of the dirs ...
-    }
-
-    my $dbh = $self->{dbh};
-    my $query = "SELECT PathId FROM Path WHERE Path = ?
-                 UNION SELECT PathId FROM brestore_missing_path WHERE PATH = ?";
-    my $sth = $dbh->prepare($query);
-    $sth->execute($dir,$dir);
-    my $result = $sth->fetchrow_arrayref();
-    $sth->finish();
-    my $pathid = $result->[0];
-    my @jobids = @{$self->{CurrentJobIds}};
-    my $jobclause = join (',', @jobids);
-    # Let's retrieve the list of the visible dirs in this dir ...
-    # First, I need the empty filenameid to locate efficiently the dirs in the file table
-    $query = "SELECT FilenameId FROM Filename WHERE Name = ''";
-    $sth = $dbh->prepare($query);
-    $sth->execute();
-    $result = $sth->fetchrow_arrayref();
-    $sth->finish();
-    my $dir_filenameid = $result->[0];
-     
-    # Then we get all the dir entries from File ...
-    # It's ugly because there are records in brestore_missing_path ...
-    $query = "
-SELECT Path, JobId, Lstat FROM(
-    (
-    SELECT Path.Path, lower(Path.Path), 
-           listfile.JobId, listfile.Lstat
-    FROM (
-       SELECT DISTINCT brestore_pathhierarchy.PathId
-       FROM brestore_pathhierarchy
-       JOIN Path 
-           ON (brestore_pathhierarchy.PathId = Path.PathId)
-       JOIN brestore_pathvisibility 
-           ON (brestore_pathhierarchy.PathId = brestore_pathvisibility.PathId)
-       WHERE brestore_pathhierarchy.PPathId = $pathid
-       AND brestore_pathvisibility.jobid IN ($jobclause)) AS listpath
-    JOIN Path ON (listpath.PathId = Path.PathId)
-    LEFT JOIN (
-       SELECT File.PathId, File.JobId, File.Lstat FROM File
-       WHERE File.FilenameId = $dir_filenameid
-       AND File.JobId IN ($jobclause)) AS listfile
-       ON (listpath.PathId = listfile.PathId)
-    UNION
-    SELECT brestore_missing_path.Path, lower(brestore_missing_path.Path), 
-           listfile.JobId, listfile.Lstat
-    FROM (
-       SELECT DISTINCT brestore_pathhierarchy.PathId
-       FROM brestore_pathhierarchy
-       JOIN brestore_missing_path 
-           ON (brestore_pathhierarchy.PathId = brestore_missing_path.PathId)
-       JOIN brestore_pathvisibility 
-           ON (brestore_pathhierarchy.PathId = brestore_pathvisibility.PathId)
-       WHERE brestore_pathhierarchy.PPathId = $pathid
-       AND brestore_pathvisibility.jobid IN ($jobclause)) AS listpath
-    JOIN brestore_missing_path ON (listpath.PathId = brestore_missing_path.PathId)
-    LEFT JOIN (
-       SELECT File.PathId, File.JobId, File.Lstat FROM File
-       WHERE File.FilenameId = $dir_filenameid
-       AND File.JobId IN ($jobclause)) AS listfile
-       ON (listpath.PathId = listfile.PathId))
-ORDER BY 2,3 DESC ) As a";
-    print STDERR "$query\n" if $debug;
-    $sth=$dbh->prepare($query);
-    $sth->execute();
-    $result = $sth->fetchall_arrayref();
-    my @return_list;
-    my $prev_dir='';
-    foreach my $refrow (@{$result})
-    {
-        my $dir = $refrow->[0];
-        my $jobid = $refrow->[1];
-        my $lstat = $refrow->[2];
-        next if ($dir eq $prev_dir);
-        # We have to clean up this dirname ... we only want it's 'basename'
-        my $return_value;
-        if ($dir ne '/')
-        {
-            my @temp = split ('/',$dir);
-            $return_value = pop @temp;
-        }
-        else
-        {
-            $return_value = '/';
-        }
-        my @return_array = ($return_value,$lstat);
-        push @return_list,(\@return_array);
-        $prev_dir = $dir;
-    }
-    return @return_list;    
-}
-
-
-# List all files in a directory. dir as parameter, CurrentJobIds for visibility
-# Returns an array of dirs
-sub list_files
-{
-    my ($self, $dir)=@_;
-    my $dbh = $self->{dbh};
-
-    my $empty = [];
-
-    print "list_files($dir)\n" if $debug;
-
-    if ($dir ne '' and substr $dir,-1 ne '/')
-    {
-       $dir .= '/'; # In the db, there is a / at the end of the dirs ...
-    }
-
-    my $query = "SELECT Path.PathId 
-                    FROM Path 
-                    WHERE Path.Path = '$dir'
-                 UNION 
-                 SELECT brestore_missing_path.PathId 
-                    FROM brestore_missing_path 
-                    WHERE brestore_missing_path.Path = '$dir'";
-    print $query,"\n" if $debug;
-    my @list_pathid=();
-    my $result = $dbh->selectall_arrayref($query);
-    foreach my $refrow (@$result)
-    {
-       push @list_pathid,($refrow->[0]);
-    }
-       
-    if  (@list_pathid == 0)
-    {
-       print "No pathid found for $dir\n" if $debug;
-       return $empty;
-    }
-       
-    my $inlistpath = join (',', @list_pathid);
-    my $inclause = join (',', @{$self->{CurrentJobIds}});
-    if ($inclause eq '')
-    {
-       return $empty;
-    }
-       
-    $query = 
-"SELECT listfiles.id, listfiles.Name, File.LStat, File.JobId
- FROM
-       (SELECT Filename.Name, max(File.FileId) as id
-        FROM File, Filename
-        WHERE File.FilenameId = Filename.FilenameId
-          AND Filename.Name != ''
-          AND File.PathId IN ($inlistpath)
-          AND File.JobId IN ($inclause)
-        GROUP BY Filename.Name
-        ORDER BY Filename.Name) AS listfiles,
-File
-WHERE File.FileId = listfiles.id";
-       
-    print STDERR $query,"\n" if $debug;
-    $result = $dbh->selectall_arrayref($query);
-       
-    return $result;
-}
-
-sub refresh_screen
-{
-    Gtk2->main_iteration while (Gtk2->events_pending);
-}
-
-sub create_brestore_tables
-{
-    my ($self) = @_;
-
-    my $verif = "SELECT 1 FROM brestore_knownjobid LIMIT 1";
-
-    unless ($self->dbh_do($verif)) {
-       new DlgWarn("brestore can't find brestore_xxx tables on your database. I will create them.");
-
-       $self->{error} = "Creating internal brestore tables";
-       my $req = "
-    CREATE TABLE brestore_knownjobid
-    (
-     JobId int4 NOT NULL,
-     CONSTRAINT brestore_knownjobid_pkey PRIMARY KEY (JobId)
-    )";
-       $self->dbh_do($req);
-    }
-    
-    $verif = "SELECT 1 FROM brestore_pathhierarchy LIMIT 1";
-    unless ($self->dbh_do($verif)) {
-       my $req = "
-   CREATE TABLE brestore_pathhierarchy
-   (
-     PathId int4 NOT NULL,
-     PPathId int4 NOT NULL,
-     CONSTRAINT brestore_pathhierarchy_pkey PRIMARY KEY (PathId)
-   )";
-       $self->dbh_do($req);
-
-
-       $req = "CREATE INDEX brestore_pathhierarchy_ppathid 
-                          ON brestore_pathhierarchy (PPathId)";
-       $self->dbh_do($req);
-    }
-    
-    $verif = "SELECT 1 FROM brestore_pathvisibility LIMIT 1";
-    unless ($self->dbh_do($verif)) {
-       my $req = "
-    CREATE TABLE brestore_pathvisibility
-    (
-      PathId int4 NOT NULL,
-      JobId int4 NOT NULL,
-      CONSTRAINT brestore_pathvisibility_pkey PRIMARY KEY (JobId, PathId)
-    )";
-       $self->dbh_do($req);
-
-       $req = "CREATE INDEX brestore_pathvisibility_jobid
-                          ON brestore_pathvisibility (JobId)";
-       $self->dbh_do($req);
-    }
-    
-    $verif = "SELECT 1 FROM brestore_missing_path LIMIT 1";
-    unless ($self->dbh_do($verif)) {
-       my $req = "
-    CREATE TABLE brestore_missing_path
-    (
-      PathId int4 NOT NULL,
-      Path text NOT NULL,
-      CONSTRAINT brestore_missing_path_pkey PRIMARY KEY (PathId)
-    )";
-       $self->dbh_do($req);
-
-       $req = "CREATE INDEX brestore_missing_path_path
-                          ON brestore_missing_path (Path)";
-       $self->dbh_do($req);
-    }
-}
-
-# Recursive function to calculate the visibility of each directory in the cache
-# tree Working with references to save time and memory
-# For each directory, we want to propagate it's visible jobids onto it's
-# parents directory.
-# A tree is visible if
-# - it's been in a backup pointed by the CurrentJobIds
-# - one of it's subdirs is in a backup pointed by the CurrentJobIds
-# In the second case, the directory is visible but has no metadata.
-# We symbolize this with lstat = 1 for this jobid in the cache.
-
-# Input : reference directory
-# Output : visibility of this dir. Has to know visibility of all subdirs
-# to know it's visibility, hence the recursing.
-sub list_visible
-{
-    my ($refdir)=@_;
-       
-    my %visibility;
-    # Get the subdirs array references list
-    my @list_ref_subdirs;
-    while( my (undef,$ref_subdir) = each (%{$refdir->[0]}))
-    {
-       push @list_ref_subdirs,($ref_subdir);
-    }
-
-    # Now lets recurse over these subdirs and retrieve the reference of a hash
-    # containing the jobs where they are visible
-    foreach my $ref_subdir (@list_ref_subdirs)
-    {
-       my $ref_list_jobs = list_visible($ref_subdir);
-       foreach my $jobid (keys %$ref_list_jobs)
-       {
-           $visibility{$jobid}=1;
-       }
-    }
-
-    # Ok. Now, we've got the list of those jobs.  We are going to update our
-    # hash (element 1 of the dir array) containing our jobs Do NOT overwrite
-    # the lstat for the known jobids. Put 1 in the new elements...  But first,
-    # let's store the current jobids
-    my @known_jobids;
-    foreach my $jobid (keys %{$refdir->[1]})
-    {
-       push @known_jobids,($jobid);
-    }
-    
-    # Add the new jobs
-    foreach my $jobid (keys %visibility)
-    {
-       next if ($refdir->[1]->{$jobid});
-       $refdir->[1]->{$jobid} = 1;
-    }
-    # Add the known_jobids to %visibility
-    foreach my $jobid (@known_jobids)
-    {
-       $visibility{$jobid}=1;
-    }
-    return \%visibility;
-}
-
-# Returns the list of media required for a list of jobids.
-# Input : dbh, jobid1, jobid2...
-# Output : reference to array of (joibd, inchanger)
-sub get_required_media_from_jobid
-{
-    my ($dbh, @jobids)=@_;
-    my $inclause = join(',',@jobids);
-    my $query = "
-SELECT DISTINCT JobMedia.MediaId, Media.InChanger 
-FROM JobMedia, Media 
-WHERE JobMedia.MediaId=Media.MediaId 
-AND JobId In ($inclause)
-ORDER BY MediaId";
-    my $result = $dbh->selectall_arrayref($query);
-    return $result;
-}
-
-# Returns the fileindex from dirname and jobid.
-# Input : dbh, dirname, jobid
-# Output : fileindex
-sub get_fileindex_from_dir_jobid
-{
-    my ($dbh, $dirname, $jobid)=@_;
-    my $query;
-    $query = "SELECT File.FileIndex
-               FROM File, Filename, Path
-               WHERE File.FilenameId = Filename.FilenameId
-               AND File.PathId = Path.PathId
-               AND Filename.Name = ''
-               AND Path.Path = '$dirname'
-               AND File.JobId = '$jobid'
-               ";
-               
-    print STDERR $query,"\n" if $debug;
-    my $result = $dbh->selectall_arrayref($query);
-    return $result->[0]->[0];
-}
-
-# Returns the fileindex from filename and jobid.
-# Input : dbh, filename, jobid
-# Output : fileindex
-sub get_fileindex_from_file_jobid
-{
-    my ($dbh, $filename, $jobid)=@_;
-    
-    my @dirs = split(/\//, $filename);
-    $filename=pop(@dirs);
-    my $dirname = join('/', @dirs) . '/';
-    
-    
-    my $query;
-    $query = 
-"SELECT File.FileIndex
- FROM File, Filename, Path
- WHERE File.FilenameId = Filename.FilenameId
-   AND File.PathId = Path.PathId
-   AND Filename.Name = '$filename'
-   AND Path.Path = '$dirname'
-   AND File.JobId = '$jobid'";
-               
-    print STDERR $query,"\n" if $debug;
-    my $result = $dbh->selectall_arrayref($query);
-    return $result->[0]->[0];
-}
-
-
-# Returns list of versions of a file that could be restored
-# returns an array of 
-# ('FILE:',filename,jobid,fileindex,mtime,size,inchanger,md5,volname)
-# It's the same as entries of restore_list (hidden) + mtime and size and inchanger
-# and volname and md5
-# and of course, there will be only one jobid in the array of jobids...
-sub get_all_file_versions
-{
-    my ($dbh,$path,$file,$client,$see_all)=@_;
-    
-    defined $see_all or $see_all=0;
-    
-    my @versions;
-    my $query;
-    $query = 
-"SELECT File.JobId, File.FileIndex, File.Lstat, 
-        File.Md5, Media.VolumeName, Media.InChanger
- FROM File, Filename, Path, Job, Client, JobMedia, Media
- WHERE File.FilenameId = Filename.FilenameId
-   AND File.PathId=Path.PathId
-   AND File.JobId = Job.JobId
-   AND Job.ClientId = Client.ClientId
-   AND Job.JobId = JobMedia.JobId
-   AND File.FileIndex >= JobMedia.FirstIndex
-   AND File.FileIndex <= JobMedia.LastIndex
-   AND JobMedia.MediaId = Media.MediaId
-   AND Path.Path = '$path'
-   AND Filename.Name = '$file'
-   AND Client.Name = '$client'";
-       
-    print STDERR $query if $debug;
-       
-    my $result = $dbh->selectall_arrayref($query);
-       
-    foreach my $refrow (@$result)
-    {
-       my ($jobid, $fileindex, $lstat, $md5, $volname, $inchanger) = @$refrow;
-       my @attribs = parse_lstat($lstat);
-       my $mtime = array_attrib('st_mtime',\@attribs);
-       my $size = array_attrib('st_size',\@attribs);
-               
-       my @list = ('FILE:', $path.$file, $jobid, $fileindex, $mtime, $size,
-                   $inchanger, $md5, $volname);
-       push @versions, (\@list);
-    }
-       
-    # We have the list of all versions of this file.
-    # We'll sort it by mtime desc, size, md5, inchanger desc
-    # the rest of the algorithm will be simpler
-    # ('FILE:',filename,jobid,fileindex,mtime,size,inchanger,md5,volname)
-    @versions = sort { $b->[4] <=> $a->[4] 
-                   || $a->[5] <=> $b->[5] 
-                   || $a->[7] cmp $a->[7] 
-                   || $b->[6] <=> $a->[6]} @versions;
-       
-    my @good_versions;
-    my %allready_seen_by_mtime;
-    my %allready_seen_by_md5;
-    # Now we should create a new array with only the interesting records
-    foreach my $ref (@versions)
-    {  
-       if ($ref->[7])
-       {
-           # The file has a md5. We compare his md5 to other known md5...
-           # We take size into account. It may happen that 2 files
-           # have the same md5sum and are different. size is a supplementary
-           # criterion
-            
-            # If we allready have a (better) version
-           next if ( (not $see_all) 
-                     and $allready_seen_by_md5{$ref->[7] .'-'. $ref->[5]}); 
+           push @CurrentJobIds,($jobid);
+       }
 
-           # we never met this one before...
-           $allready_seen_by_md5{$ref->[7] .'-'. $ref->[5]}=1;
+       my $status = $refrow->[3] ;
+       if ($status eq 'T') {              # good end of job
+           $progress{$fileset} = $level;
        }
-       # Even if it has a md5, we should also work with mtimes
-        # We allready have a (better) version
-       next if ( (not $see_all)
-                 and $allready_seen_by_mtime{$ref->[4] .'-'. $ref->[5]}); 
-       $allready_seen_by_mtime{$ref->[4] .'-'. $ref->[5] . '-' . $ref->[7]}=1;
-       
-       # We reached there. The file hasn't been seen.
-       push @good_versions,($ref);
     }
-       
-    # To be nice with the user, we re-sort good_versions by
-    # inchanger desc, mtime desc
-    @good_versions = sort { $b->[4] <=> $a->[4] 
-                         || $b->[2] <=> $a->[2]} @good_versions;
-       
-    return @good_versions;
+
+    return @CurrentJobIds;
+}
+
+sub refresh_screen
+{
+    Gtk2->main_iteration while (Gtk2->events_pending);
 }
 
 # TODO : bsr must use only good backup or not (see use_ok_bkp_only)
@@ -2455,7 +2016,8 @@ WHERE Job.JobId = JobMedia.JobId
 
        
        # reminder : restore_list looks like this : 
-       # ($name,$jobid,'file',$curjobids, undef, undef, undef, $dirfileindex);
+       # ($pid,$fid,$name,$jobid,'file',$curjobids,
+       #   undef, undef, undef, $dirfileindex);
        
        # Here, we retrieve every file/dir that could be in the restore
        # We do as simple as possible for the SQL engine (no crazy joins,
@@ -2464,17 +2026,20 @@ WHERE Job.JobId = JobMedia.JobId
        my @select_queries;
        foreach my $entry (@{$self->{restore_list}->{data}})
        {
-               if ($entry->[2] eq 'dir')
+               if ($entry->[4] eq 'dir')
                {
-                       my $dir = unpack('u', $entry->[0]);
-                       my $inclause = $entry->[3]; #curjobids
-
+                       my $dirid = $entry->[0];
+                       my $inclause = $entry->[5]; #curjobids
+       
                        my $query = 
 "(SELECT Path.Path, Filename.Name, File.FileIndex, File.JobId
   FROM File, Path, Filename
   WHERE Path.PathId = File.PathId
   AND File.FilenameId = Filename.FilenameId
-  AND Path.Path LIKE '$dir%'
+  AND Path.Path LIKE 
+        (SELECT ". $self->dbh_strcat('Path',"'\%'") ." FROM Path 
+          WHERE PathId IN ($dirid)
+        )
   AND File.JobId IN ($inclause) )";
                        push @select_queries,($query);
                }
@@ -2482,29 +2047,27 @@ WHERE Job.JobId = JobMedia.JobId
                {
                        # It's a file. Great, we allready have most 
                        # of what is needed. Simple and efficient query
-                       my $file = unpack('u', $entry->[0]);
-                       my @file = split '/',$file;
-                       $file = pop @file;
-                       my $dir = join('/',@file);
+                       my $dir = $entry->[0];
+                       my $file = $entry->[1];
                        
-                       my $jobid = $entry->[1];
-                       my $fileindex = $entry->[7];
-                       my $inclause = $entry->[3]; # curjobids
+                       my $jobid = $entry->[3];
+                       my $fileindex = $entry->[9];
+                       my $inclause = $entry->[5]; # curjobids
                        my $query = 
 "(SELECT Path.Path, Filename.Name, File.FileIndex, File.JobId
-  FROM File, Path, Filename
-  WHERE Path.PathId = File.PathId
+  FROM File,Path,Filename
+  WHERE File.PathId = $dir
+  AND File.PathId = Path.PathId
+  AND File.FilenameId = $file
   AND File.FilenameId = Filename.FilenameId
-  AND Path.Path = '$dir/'
-  AND Filename.Name = '$file'
-  AND File.JobId = $jobid)";
+  AND File.JobId = $jobid
+ )
+";
                        push @select_queries,($query);
                }
        }
        $query = join("\nUNION ALL\n",@select_queries) . "\nORDER BY FileIndex\n";
 
-       print STDERR $query,"\n" if $debug;
-       
        #Now we run the query and parse the result...
        # there may be a lot of records, so we better be efficient
        # We use the bind column method, working with references...
@@ -2586,7 +2149,7 @@ WHERE Job.JobId = JobMedia.JobId
        # path, volsessiontime DESC (get the most recent file...)
        # The array rows look like this :
        # complete_path,is_dir,fileindex,
-       # ref->(jobid,VolsessionId,VolsessionTime,File,FirstIndex,
+       #\81 ref->(jobid,VolsessionId,VolsessionTime,File,FirstIndex,
        #       LastIndex,StartBlock-EndBlock,VolIndex,Volumename,MediaType)
        @temp_list = sort {$a->[0] cmp $b->[0]
                         || $b->[3]->[2] <=> $a->[3]->[2]
@@ -2694,11 +2257,11 @@ WHERE Job.JobId = JobMedia.JobId
                       or $prev_volfile ne $volfile)
                {
                        # We have to create a new section in the bsr...
-                       # We print the previous one ... 
+                       #\81Â\81 We print the previous one ... 
                        # (before that, save the current range ...)
                        if ($first_of_current_range != $prev_fileindex)
                        {
-                               # we are in a range
+                               #\81Â\81 we are in a range
                                push @fileindex_ranges,
                                    ("$first_of_current_range-$prev_fileindex");
                        }
@@ -2800,6 +2363,280 @@ sub print_bsr_section
     return $bsr;
 }
 
+1;
+
+################################################################
+
+package Bvfs;
+use base qw/Bbase/;
+
+sub get_pathid
+{
+    my ($self, $dir) = @_;
+    my $query = 
+       "SELECT PathId FROM Path WHERE Path = ?";
+    my $sth = $self->dbh_prepare($query);
+    $sth->execute($dir);
+    my $result = $sth->fetchall_arrayref();
+    $sth->finish();
+    
+    return join(',', map { $_->[0] } @$result);
+}
+
+
+sub update_cache
+{
+    my ($self) = @_;
+
+    $self->{conf}->{dbh}->begin_work();
+
+    my $query = "
+  SELECT JobId from Job 
+   WHERE JobId NOT IN (SELECT JobId FROM brestore_knownjobid) AND JobStatus IN ('T', 'f', 'A') ORDER BY JobId";
+    my $jobs = $self->dbh_selectall_arrayref($query);
+
+    $self->update_brestore_table(map { $_->[0] } @$jobs);
+
+    $self->{conf}->{dbh}->commit();
+    $self->{conf}->{dbh}->begin_work();
+
+    print STDERR "Cleaning path visibility\n";
+    
+    my $nb = $self->dbh_do("
+  DELETE FROM brestore_pathvisibility
+      WHERE NOT EXISTS 
+   (SELECT 1 FROM Job WHERE JobId=brestore_pathvisibility.JobId)");
+
+    print STDERR "$nb rows affected\n";
+    print STDERR "Cleaning known jobid\n";
+
+    $nb = $self->dbh_do("
+  DELETE FROM brestore_knownjobid
+      WHERE NOT EXISTS 
+   (SELECT 1 FROM Job WHERE JobId=brestore_knownjobid.JobId)");
+
+    print STDERR "$nb rows affected\n";
+
+    $self->{conf}->{dbh}->commit();
+}
+
+sub get_root
+{
+    my ($self, $dir) = @_;
+    return $self->get_pathid('');
+}
+
+sub ch_dir
+{
+    my ($self, $pathid) = @_;
+    $self->{cwdid} = $pathid;
+}
+
+sub up_dir
+{
+    my ($self) = @_ ;
+    my $query = "
+  SELECT PPathId 
+    FROM brestore_pathhierarchy 
+   WHERE PathId IN ($self->{cwdid}) ";
+
+    my $all = $self->dbh_selectall_arrayref($query);
+    return unless ($all);      # already at root
+
+    my $dir = join(',', map { $_->[0] } @$all);
+    if ($dir) {
+       $self->ch_dir($dir);
+    }
+}
+
+sub pwd
+{
+    my ($self) = @_;
+    return $self->get_path($self->{cwdid});
+}
+
+sub get_path
+{
+    my ($self, $pathid) = @_;
+    $self->debug("Call with pathid = $pathid");
+    my $query = 
+       "SELECT Path FROM Path WHERE PathId IN (?)";
+
+    my $sth = $self->dbh_prepare($query);
+    $sth->execute($pathid);
+    my $result = $sth->fetchrow_arrayref();
+    $sth->finish();
+    return $result->[0];    
+}
+
+sub set_curjobids
+{
+    my ($self, @jobids) = @_;
+    $self->{curjobids} = join(',', @jobids);
+    $self->update_brestore_table(@jobids);
+}
+
+sub ls_files
+{
+    my ($self) = @_;
+
+    return undef unless ($self->{curjobids});
+
+    my $inclause   = $self->{curjobids};
+    my $inlistpath = $self->{cwdid};
+
+    my $query = 
+"SELECT File.FilenameId, listfiles.id, listfiles.Name, File.LStat, File.JobId
+ FROM
+       (SELECT Filename.Name, max(File.FileId) as id
+        FROM File, Filename
+        WHERE File.FilenameId = Filename.FilenameId
+          AND Filename.Name != ''
+          AND File.PathId IN ($inlistpath)
+          AND File.JobId IN ($inclause)
+        GROUP BY Filename.Name
+        ORDER BY Filename.Name) AS listfiles,
+File
+WHERE File.FileId = listfiles.id";
+       
+    $self->debug($query);
+    my $result = $self->dbh_selectall_arrayref($query);
+    $self->debug($result);
+       
+    return $result;
+}
+
+# return ($dirid,$dir_basename,$lstat,$jobid)
+sub ls_dirs
+{
+    my ($self) = @_;
+
+    return undef unless ($self->{curjobids});
+
+    my $pathid = $self->{cwdid};
+    my $jobclause = $self->{curjobids};
+
+    # Let's retrieve the list of the visible dirs in this dir ...
+    # First, I need the empty filenameid to locate efficiently the dirs in the file table
+    my $query = "SELECT FilenameId FROM Filename WHERE Name = ''";
+    my $sth = $self->dbh_prepare($query);
+    $sth->execute();
+    my $result = $sth->fetchrow_arrayref();
+    $sth->finish();
+    my $dir_filenameid = $result->[0];
+     
+    # Then we get all the dir entries from File ...
+    $query = "
+SELECT PathId, Path, JobId, Lstat FROM (
+    
+    SELECT Path1.PathId, Path1.Path, lower(Path1.Path),
+           listfile1.JobId, listfile1.Lstat
+    FROM (
+        SELECT DISTINCT brestore_pathhierarchy1.PathId
+        FROM brestore_pathhierarchy AS brestore_pathhierarchy1
+        JOIN Path AS Path2
+            ON (brestore_pathhierarchy1.PathId = Path2.PathId)
+        JOIN brestore_pathvisibility AS brestore_pathvisibility1
+            ON (brestore_pathhierarchy1.PathId = brestore_pathvisibility1.PathId)
+        WHERE brestore_pathhierarchy1.PPathId = $pathid
+        AND brestore_pathvisibility1.jobid IN ($jobclause)) AS listpath1
+    JOIN Path AS Path1 ON (listpath1.PathId = Path1.PathId)
+    LEFT JOIN (
+        SELECT File1.PathId, File1.JobId, File1.Lstat FROM File AS File1
+        WHERE File1.FilenameId = $dir_filenameid
+        AND File1.JobId IN ($jobclause)) AS listfile1
+        ON (listpath1.PathId = listfile1.PathId)
+     ) AS A ORDER BY 2,3 DESC
+";
+    $self->debug($query);
+    $sth=$self->dbh_prepare($query);
+    $sth->execute();
+    $result = $sth->fetchall_arrayref();
+    my @return_list;
+    my $prev_dir='';
+    foreach my $refrow (@{$result})
+    {
+       my $dirid = $refrow->[0];
+        my $dir = $refrow->[1];
+        my $lstat = $refrow->[3];
+        my $jobid = $refrow->[2] || 0;
+        next if ($dirid eq $prev_dir);
+        # We have to clean up this dirname ... we only want it's 'basename'
+        my $return_value;
+        if ($dir ne '/')
+        {
+            my @temp = split ('/',$dir);
+            $return_value = pop @temp;
+        }
+        else
+        {
+            $return_value = '/';
+        }
+        my @return_array = ($dirid,$return_value,$lstat,$jobid);
+        push @return_list,(\@return_array);
+        $prev_dir = $dirid;
+    }
+    $self->debug(\@return_list);
+    return \@return_list;    
+}
+
+# Returns the list of media required for a list of jobids.
+# Input : self, jobid1, jobid2...
+# Output : reference to array of (joibd, inchanger)
+sub get_required_media_from_jobid
+{
+    my ($self, @jobids)=@_;
+    my $inclause = join(',',@jobids);
+    my $query = "
+SELECT DISTINCT JobMedia.MediaId, Media.InChanger 
+FROM JobMedia, Media 
+WHERE JobMedia.MediaId=Media.MediaId 
+AND JobId In ($inclause)
+ORDER BY MediaId";
+    my $result = $self->dbh_selectall_arrayref($query);
+    return $result;
+}
+
+# Returns the fileindex from dirname and jobid.
+# Input : self, dirid, jobid
+# Output : fileindex
+sub get_fileindex_from_dir_jobid
+{
+    my ($self, $dirid, $jobid)=@_;
+    my $query;
+    $query = "SELECT File.FileIndex
+               FROM File, Filename
+               WHERE File.FilenameId = Filename.FilenameId
+               AND File.PathId = $dirid
+               AND Filename.Name = ''
+               AND File.JobId = '$jobid'
+               ";
+               
+    $self->debug($query);
+    my $result = $self->dbh_selectall_arrayref($query);
+    return $result->[0]->[0];
+}
+
+# Returns the fileindex from filename and jobid.
+# Input : self, dirid, filenameid, jobid
+# Output : fileindex
+sub get_fileindex_from_file_jobid
+{
+    my ($self, $dirid, $filenameid, $jobid)=@_;
+    
+    my $query;
+    $query = 
+"SELECT File.FileIndex
+ FROM File
+ WHERE File.PathId = $dirid
+   AND File.FilenameId = $filenameid
+   AND File.JobId = $jobid";
+               
+    $self->debug($query);
+    my $result = $self->dbh_selectall_arrayref($query);
+    return $result->[0]->[0];
+}
+
 # This function estimates the size to be restored for an entry of the restore
 # list
 # In : self,reference to the entry
@@ -2807,20 +2644,23 @@ sub print_bsr_section
 sub estimate_restore_size
 {
     # reminder : restore_list looks like this : 
-    # ($name,$jobid,'file',$curjobids, undef, undef, undef, $dirfileindex);
-    my $self=shift;
-    my ($entry)=@_;
+    # ($pid,$fid,$name,$jobid,'file',$curjobids, 
+    #  undef, undef, undef, $dirfileindex);
+    my ($self, $entry, $refresh) = @_;
     my $query;
-    if ($entry->[2] eq 'dir')
+    if ($entry->[4] eq 'dir')
     {
-       my $dir = unpack('u', $entry->[0]);
-       my $inclause = $entry->[3]; #curjobids
+       my $dir = $entry->[0];
+
+       my $inclause = $entry->[5]; #curjobids
        $query = 
 "SELECT Path.Path, File.FilenameId, File.LStat
   FROM File, Path, Job
   WHERE Path.PathId = File.PathId
   AND File.JobId = Job.JobId
-  AND Path.Path LIKE '$dir%'
+  AND Path.Path LIKE 
+        (SELECT " . $self->dbh_strcat('Path',"'\%'") . " FROM Path WHERE PathId IN ($dir)
+        )
   AND File.JobId IN ($inclause)
   ORDER BY Path.Path, File.FilenameId, Job.StartTime DESC";
     }
@@ -2828,25 +2668,21 @@ sub estimate_restore_size
     {
        # It's a file. Great, we allready have most 
        # of what is needed. Simple and efficient query
-       my $file = unpack('u', $entry->[0]);
-       my @file = split '/',$file;
-       $file = pop @file;
-       my $dir = join('/',@file);
+       my $dir = $entry->[0];
+       my $fileid = $entry->[1];
        
-       my $jobid = $entry->[1];
-       my $fileindex = $entry->[7];
-       my $inclause = $entry->[3]; # curjobids
+       my $jobid = $entry->[3];
+       my $fileindex = $entry->[9];
+       my $inclause = $entry->[5]; # curjobids
        $query = 
 "SELECT Path.Path, File.FilenameId, File.Lstat
-  FROM File, Path, Filename
+  FROM File, Path
   WHERE Path.PathId = File.PathId
-  AND Path.Path = '$dir/'
-  AND Filename.Name = '$file'
-  AND File.JobId = $jobid
-  AND Filename.FilenameId = File.FilenameId";
+  AND Path.PathId = $dir
+  AND File.FilenameId = $fileid
+  AND File.JobId = $jobid";
     }
 
-    print STDERR $query,"\n" if $debug;
     my ($path,$nameid,$lstat);
     my $sth = $self->dbh_prepare($query);
     $sth->execute;
@@ -2856,7 +2692,7 @@ sub estimate_restore_size
     my $total_size=0;
     my $total_files=0;
 
-    refresh_screen();
+    &$refresh();
 
     my $rcount=0;
     # We fetch all rows
@@ -2865,27 +2701,121 @@ sub estimate_restore_size
         # Only the latest version of a file
         next if ($nameid eq $old_nameid and $path eq $old_path);
 
-       if ($rcount > 15000) {
-           refresh_screen();
-           $rcount=0;
-       } else {
-           $rcount++;
+       if ($rcount > 15000) {
+           &$refresh();
+           $rcount=0;
+       } else {
+           $rcount++;
+       }
+
+        # We get the size of this file
+        my $size=lstat_attrib($lstat,'st_size');
+        $total_size += $size;
+        $total_files++;
+        $old_path=$path;
+        $old_nameid=$nameid;
+    }
+
+    return ($total_size,$total_files);
+}
+
+# Returns list of versions of a file that could be restored
+# returns an array of 
+# ('FILE:',jobid,fileindex,mtime,size,inchanger,md5,volname)
+# there will be only one jobid in the array of jobids...
+sub get_all_file_versions
+{
+    my ($self,$pathid,$fileid,$client,$see_all)=@_;
+    
+    defined $see_all or $see_all=0;
+    
+    my @versions;
+    my $query;
+    $query = 
+"SELECT File.JobId, File.FileIndex, File.Lstat, 
+        File.Md5, Media.VolumeName, Media.InChanger
+ FROM File, Job, Client, JobMedia, Media
+ WHERE File.FilenameId = $fileid
+   AND File.PathId=$pathid
+   AND File.JobId = Job.JobId
+   AND Job.ClientId = Client.ClientId
+   AND Job.JobId = JobMedia.JobId
+   AND File.FileIndex >= JobMedia.FirstIndex
+   AND File.FileIndex <= JobMedia.LastIndex
+   AND JobMedia.MediaId = Media.MediaId
+   AND Client.Name = '$client'";
+       
+    $self->debug($query);
+       
+    my $result = $self->dbh_selectall_arrayref($query);
+       
+    foreach my $refrow (@$result)
+    {
+       my ($jobid, $fileindex, $lstat, $md5, $volname, $inchanger) = @$refrow;
+       my @attribs = parse_lstat($lstat);
+       my $mtime = array_attrib('st_mtime',\@attribs);
+       my $size = array_attrib('st_size',\@attribs);
+               
+       my @list = ('FILE:',$pathid,$fileid,$jobid,
+                   $fileindex, $mtime, $size, $inchanger,
+                   $md5, $volname);
+       push @versions, (\@list);
+    }
+       
+    # We have the list of all versions of this file.
+    # We'll sort it by mtime desc, size, md5, inchanger desc
+    # the rest of the algorithm will be simpler
+    # ('FILE:',filename,jobid,fileindex,mtime,size,inchanger,md5,volname)
+    @versions = sort { $b->[5] <=> $a->[5] 
+                   || $a->[6] <=> $b->[6] 
+                   || $a->[8] cmp $a->[8] 
+                   || $b->[7] <=> $a->[7]} @versions;
+
+       
+    my @good_versions;
+    my %allready_seen_by_mtime;
+    my %allready_seen_by_md5;
+    # Now we should create a new array with only the interesting records
+    foreach my $ref (@versions)
+    {  
+       if ($ref->[8])
+       {
+           # The file has a md5. We compare his md5 to other known md5...
+           # We take size into account. It may happen that 2 files
+           # have the same md5sum and are different. size is a supplementary
+           # criterion
+            
+            # If we allready have a (better) version
+           next if ( (not $see_all) 
+                     and $allready_seen_by_md5{$ref->[8] .'-'. $ref->[6]}); 
+
+           # we never met this one before...
+           $allready_seen_by_md5{$ref->[8] .'-'. $ref->[6]}=1;
        }
-
-        # We get the size of this file
-        my $size=lstat_attrib($lstat,'st_size');
-        $total_size += $size;
-        $total_files++;
-        $old_path=$path;
-        $old_nameid=$nameid;
+       # Even if it has a md5, we should also work with mtimes
+        # We allready have a (better) version
+       next if ( (not $see_all)
+                 and $allready_seen_by_mtime{$ref->[5] .'-'. $ref->[6]}); 
+       $allready_seen_by_mtime{$ref->[5] .'-'. $ref->[6] . '-' . $ref->[8]}=1;
+       
+       # We reached there. The file hasn't been seen.
+       push @good_versions,($ref);
     }
-    return ($total_size,$total_files);
+       
+    # To be nice with the user, we re-sort good_versions by
+    # inchanger desc, mtime desc
+    @good_versions = sort { $b->[5] <=> $a->[5] 
+                         || $b->[3] <=> $a->[3]} @good_versions;
+       
+    return @good_versions;
 }
 
+
 sub update_brestore_table
 {
     my ($self, @jobs) = @_;
-    my $dbh = $self->{dbh};
+
+    $self->debug(\@jobs);
 
     foreach my $job (sort {$a <=> $b} @jobs)
     {
@@ -2940,43 +2870,37 @@ sub update_brestore_table
                WHERE JobId=$job) AS b
                ON (a.PathId = b.PathId)
        WHERE b.PathId IS NULL)";
-       print STDERR $query,"\n" if ($debug);
+
         my $rows_affected;
-       while (($rows_affected = $dbh->do($query)) and ($rows_affected !~ /^0/))
+       while (($rows_affected = $self->dbh_do($query)) and ($rows_affected !~ /^0/))
        {
            print STDERR "Recursively adding $rows_affected records from $job\n";
        }
        # Job's done
        $query = "INSERT INTO brestore_knownjobid (JobId) VALUES ($job)";
-       $dbh->do($query);
+       $self->dbh_do($query);
     }
 }
 
-sub cleanup_brestore_table
+sub parent_dir
 {
-    my ($self) = @_;
-    my $dbh = $self->{dbh};
-
-    my $query = "SELECT JobId from brestore_knownjobid";
-    my @jobs = @{$dbh->selectall_arrayref($query)};
-
-    foreach my $jobentry (@jobs)
+    my ($path) = @_;
+    # Root Unix case :
+    if ($path eq '/')
     {
-       my $job = $jobentry->[0];
-       $query = "SELECT FileId from File WHERE JobId = $job LIMIT 1";
-       my $result = $dbh->selectall_arrayref($query);
-       if (scalar(@{$result}))
-       {
-           # There are still files for this jobid
-           print STDERR "$job still exists. Not cleaning...\n";
-
-       } else {
-               $query = "DELETE FROM brestore_pathvisibility WHERE JobId = $job";
-               $dbh->do($query);
-               $query = "DELETE FROM brestore_knownjobid WHERE JobId = $job";
-               $dbh->do($query);
-       }
+        return '';
+    }
+    # Root Windows case :
+    if ($path =~ /^[a-z]+:\/$/i)
+    {
+       return '';
     }
+    # Split
+    my @tmp = split('/',$path);
+    # We remove the last ...
+    pop @tmp;
+    my $tmp = join ('/',@tmp) . '/';
+    return $tmp;
 }
 
 sub build_path_hierarchy
@@ -2987,11 +2911,10 @@ sub build_path_hierarchy
     # brestore_pathhierarchy table, then there is no need to calculate all the hierarchy
     while ($path ne '')
     {
-        #print STDERR "$path\n" if $debug;
        if (! $self->{cache_ppathid}->{$pathid})
        {
            my $query = "SELECT PPathId FROM brestore_pathhierarchy WHERE PathId = ?";
-           my $sth2 = $self->{dbh}->prepare_cached($query);
+           my $sth2 = $self->{conf}->{dbh}->prepare_cached($query);
            $sth2->execute($pathid);
            # Do we have a result ?
            if (my $refrow = $sth2->fetchrow_arrayref)
@@ -3011,7 +2934,7 @@ sub build_path_hierarchy
                $self->{cache_ppathid}->{$pathid}= $ppathid;
                
                $query = "INSERT INTO brestore_pathhierarchy (pathid, ppathid) VALUES (?,?)";
-               $sth2 = $self->{dbh}->prepare_cached($query);
+               $sth2 = $self->{conf}->{dbh}->prepare_cached($query);
                $sth2->execute($pathid,$ppathid);
                $sth2->finish();
                $path = $ppath;
@@ -3027,15 +2950,15 @@ sub build_path_hierarchy
     return 1;
 }
 
+
 sub return_pathid_from_path
 {
     my ($self, $path) = @_;
-    my $query = "SELECT PathId FROM Path WHERE Path = ?
-                 UNION
-                 SELECT PathId FROM brestore_missing_path WHERE Path = ?";
+    my $query = "SELECT PathId FROM Path WHERE Path = ?";
+
     #print STDERR $query,"\n" if $debug;
-    my $sth = $self->{dbh}->prepare_cached($query);
-    $sth->execute($path,$path);
+    my $sth = $self->{conf}->{dbh}->prepare_cached($query);
+    $sth->execute($path);
     my $result =$sth->fetchrow_arrayref();
     $sth->finish();
     if (defined $result)
@@ -3043,29 +2966,18 @@ sub return_pathid_from_path
        return $result->[0];
 
     } else {
-        # A bit dirty : we insert into path AND missing_path, to be sure
+        # A bit dirty : we insert into path, and we have to be sure
         # we aren't deleted by a purge. We still need to insert into path to get
         # the pathid, because of mysql
         $query = "INSERT INTO Path (Path) VALUES (?)";
         #print STDERR $query,"\n" if $debug;
-       $sth = $self->{dbh}->prepare_cached($query);
+       $sth = $self->{conf}->{dbh}->prepare_cached($query);
        $sth->execute($path);
        $sth->finish();
         
-       $query = " INSERT INTO brestore_missing_path (PathId,Path)
-                  SELECT PathId,Path FROM Path WHERE Path = ?";
-       #print STDERR $query,"\n" if $debug;
-       $sth = $self->{dbh}->prepare_cached($query);
-       $sth->execute($path);
-       $sth->finish();
-       $query = " DELETE FROM Path WHERE Path = ?";
+       $query = "SELECT PathId FROM Path WHERE Path = ?";
        #print STDERR $query,"\n" if $debug;
-       $sth = $self->{dbh}->prepare_cached($query);
-       $sth->execute($path);
-       $sth->finish();
-       $query = "SELECT PathId FROM brestore_missing_path WHERE Path = ?";
-       #print STDERR $query,"\n" if $debug;
-       $sth = $self->{dbh}->prepare_cached($query);
+       $sth = $self->{conf}->{dbh}->prepare_cached($query);
        $sth->execute($path);
        $result = $sth->fetchrow_arrayref();
        $sth->finish();
@@ -3073,25 +2985,62 @@ sub return_pathid_from_path
     }
 }
 
-sub parent_dir
+
+sub create_brestore_tables
 {
-    my ($path) = @_;
-    # Root Unix case :
-    if ($path eq '/')
-    {
-        return '';
+    my ($self) = @_;
+    my $ret = 0;
+    my $verif = "SELECT 1 FROM brestore_knownjobid LIMIT 1";
+
+    unless ($self->dbh_do($verif)) {
+       $ret=1;
+
+       my $req = "
+    CREATE TABLE brestore_knownjobid
+    (
+     JobId int4 NOT NULL,
+     CONSTRAINT brestore_knownjobid_pkey PRIMARY KEY (JobId)
+    )";
+       $self->dbh_do($req);
     }
-    # Root Windows case :
-    if ($path =~ /^[a-z]+:\/$/i)
-    {
-       return '';
+    
+    $verif = "SELECT 1 FROM brestore_pathhierarchy LIMIT 1";
+    unless ($self->dbh_do($verif)) {
+       $ret=1;
+       my $req = "
+   CREATE TABLE brestore_pathhierarchy
+   (
+     PathId int4 NOT NULL,
+     PPathId int4 NOT NULL,
+     CONSTRAINT brestore_pathhierarchy_pkey PRIMARY KEY (PathId)
+   )";
+       $self->dbh_do($req);
+
+
+       $req = "CREATE INDEX brestore_pathhierarchy_ppathid 
+                          ON brestore_pathhierarchy (PPathId)";
+       $self->dbh_do($req);
     }
-    # Split
-    my @tmp = split('/',$path);
-    # We remove the last ...
-    pop @tmp;
-    my $tmp = join ('/',@tmp) . '/';
-    return $tmp;
+    
+    $verif = "SELECT 1 FROM brestore_pathvisibility LIMIT 1";
+    unless ($self->dbh_do($verif)) {
+       $ret=1;
+       my $req = "
+    CREATE TABLE brestore_pathvisibility
+    (
+      PathId int4 NOT NULL,
+      JobId int4 NOT NULL,
+      Size int8 DEFAULT 0,
+      Files int4 DEFAULT 0,
+      CONSTRAINT brestore_pathvisibility_pkey PRIMARY KEY (JobId, PathId)
+    )";
+       $self->dbh_do($req);
+
+       $req = "CREATE INDEX brestore_pathvisibility_jobid
+                          ON brestore_pathvisibility (JobId)";
+       $self->dbh_do($req);
+    }
+    return $ret;
 }
 
 # Get metadata
@@ -3109,69 +3058,28 @@ sub parent_dir
     }
        
     sub file_attrib
-    {   # $file = [listfiles.id, listfiles.Name, File.LStat, File.JobId]
+    {   # $file = [filenameid,listfiles.id,listfiles.Name, File.LStat, File.JobId]
 
        my ($file, $attrib)=@_;
        
        if (defined $attrib_name_id{$attrib}) {
 
-           my @d = split(' ', $file->[2]) ; # TODO : cache this
+           my @d = split(' ', $file->[3]) ; # TODO : cache this
            
            return from_base64($d[$attrib_name_id{$attrib}]);
 
        } elsif ($attrib eq 'jobid') {
 
-           return $file->[3];
+           return $file->[4];
 
         } elsif ($attrib eq 'name') {
 
-           return $file->[1];
+           return $file->[2];
            
        } else  {
            die "Attribute not known : $attrib.\n";
        }
     }
-
-    # Return the jobid or attribute asked for a dir
-    sub dir_attrib
-    {
-       my ($self,$dir,$attrib)=@_;
-       
-       my @dir = split('/',$dir,-1);
-       my $refdir=$self->{dirtree}->{$self->current_client};
-       
-       if (not defined $attrib_name_id{$attrib} and $attrib ne 'jobid')
-       {
-           die "Attribute not known : $attrib.\n";
-       }
-       # Find the leaf
-       foreach my $subdir (@dir)
-       {
-           $refdir = $refdir->[0]->{$subdir};
-       }
-       
-       # $refdir is now the reference to the dir's array
-       # Is the a jobid in @CurrentJobIds where the lstat is
-       # defined (we'll search in reverse order)
-       foreach my $jobid (reverse(sort {$a <=> $b } @{$self->{CurrentJobIds}}))
-       {
-           if (defined $refdir->[1]->{$jobid} and $refdir->[1]->{$jobid} ne '1')
-           {
-               if ($attrib eq 'jobid')
-               {
-                   return $jobid;
-               }
-               else
-               {
-                   my @attribs = parse_lstat($refdir->[1]->{$jobid});
-                   return $attribs[$attrib_name_id{$attrib}+1];
-               }
-           }
-       }
-
-       return 0; # We cannot get a good attribute.
-                 # This directory is here for the sake of visibility
-    }
     
     sub lstat_attrib
     {
@@ -3246,35 +3154,119 @@ sub parent_dir
     }
 }
 
-
 1;
 
 ################################################################
-
-package Batch;
-use base qw/DlgResto/;
+package BwebConsole;
+use LWP::UserAgent;
+use HTTP::Request::Common;
 
 sub new
 {
-    my ($class, $conf) = @_;
-    my $self = bless {info => $conf}, $class;
+    my ($class, %arg) = @_;
 
-    $self->{dbh} = $conf->{dbh};
+    my $self = bless {
+       pref => $arg{pref},     # Pref object
+       timeout => $arg{timeout} || 20,
+       debug   => $arg{debug} || 0,
+       'list_job'     => '',
+       'list_client'  => '',
+       'list_fileset' => '',
+       'list_storage' => '',
+       'run'          => '',
+    };
 
     return $self;
 }
 
-sub update_cache
+sub prepare
+{
+    my ($self, @what) = @_;
+    my $ua = LWP::UserAgent->new();
+    $ua->agent("Brestore/$VERSION");
+    my $req = POST($self->{pref}->{bconsole},
+                  Content_Type => 'form-data',
+                  Content => [ map { (action => $_) } @what ]);
+    #$req->authorization_basic('eric', 'test');
+
+    my $res = $ua->request($req);
+
+    if ($res->is_success) {
+       foreach my $l (split(/\n/, $res->content)) {
+           my ($k, $c) = split(/=/,$l,2);
+           $self->{$k} = $c;
+       }
+    } else {
+       $self->{error} = "Can't connect to bweb : " . $res->status_line;
+       new DlgWarn($self->{error});
+    }
+}
+
+sub run
+{
+    my ($self, %arg) = @_;
+
+    my $ua = LWP::UserAgent->new();
+    $ua->agent("Brestore/$VERSION");
+    my $req = POST($self->{pref}->{bconsole},
+                  Content_Type => 'form-data',
+                  Content => [ job     => $arg{job},
+                               client  => $arg{client},
+                               storage => $arg{storage} || '',
+                               fileset => $arg{fileset} || '',
+                               where   => $arg{where}   || '',
+                               regexwhere  => $arg{regexwhere}  || '',
+                               priority=> $arg{prio}    || '',
+                               replace => $arg{replace},
+                               action  => 'run',
+                               timeout => 10,
+                               bootstrap => [$arg{bootstrap}],
+                               ]);
+    #$req->authorization_basic('eric', 'test');
+
+    my $res = $ua->request($req);
+
+    if ($res->is_success) {
+       foreach my $l (split(/\n/, $res->content)) {
+           my ($k, $c) = split(/=/,$l,2);
+           $self->{$k} = $c;
+       }
+    } 
+
+    if (!$self->{run}) {
+        new DlgWarn("Can't connect to bweb : " . $res->status_line);
+    } 
+
+    unlink($arg{bootstrap});
+
+    return $self->{run};
+}
+
+sub list_job
 {
     my ($self) = @_;
+    return sort split(/;/, $self->{'list_job'});
+}
 
-    my $query = "SELECT JobId from Job WHERE JobId NOT IN (SELECT JobId FROM brestore_knownjobid) order by JobId";
-    my $jobs = $self->dbh_selectall_arrayref($query);
+sub list_fileset
+{
+    my ($self) = @_;
+    return sort split(/;/, $self->{'list_fileset'});
+}
 
-    $self->update_brestore_table(map { $_->[0] } @$jobs);
+sub list_storage
+{
+    my ($self) = @_;
+    return sort split(/;/, $self->{'list_storage'});
+}
+sub list_client
+{
+    my ($self) = @_;
+    return sort split(/;/, $self->{'list_client'});
 }
 
 1;
+################################################################
 
 package main;
 
@@ -3286,7 +3278,7 @@ sub HELP_MESSAGE
     exit 1;
 }
 
-my $file_conf = "$ENV{HOME}/.brestore.conf" ;
+my $file_conf = (exists $ENV{HOME})? "$ENV{HOME}/.brestore.conf" : undef ;
 my $batch_mod;
 
 GetOptions("conf=s"   => \$file_conf,
@@ -3294,6 +3286,11 @@ GetOptions("conf=s"   => \$file_conf,
           "debug"    => \$debug,
           "help"     => \&HELP_MESSAGE) ;
 
+if (! defined $file_conf) {
+    print STDERR "Could not detect default config and no config file specified\n";
+    HELP_MESSAGE();
+}
+
 my $p = new Pref($file_conf);
 
 if (! -f $file_conf) {
@@ -3301,15 +3298,17 @@ if (! -f $file_conf) {
 }
 
 if ($batch_mod) {
-    my $b = new Batch($p);
+    my $vfs = new Bvfs(conf => $p);
     if ($p->connect_db()) {
-       $b->set_dbh($p->{dbh});
-       $b->update_cache();
+       if ($vfs->create_brestore_tables()) {
+           print "Creating brestore tables\n";
+       }
+       $vfs->update_cache();
     }
     exit (0);
 }
 
-$glade_file = $p->{glade_file};
+$glade_file = $p->{glade_file} || $glade_file;
 
 foreach my $path ('','.','/usr/share/brestore','/usr/local/share/brestore') {
     if (-f "$path/$glade_file") {
@@ -3346,27 +3345,22 @@ Gtk2->main; # Start Gtk2 main loop
 
 exit 0;
 
-
 __END__
+package main;
 
-TODO : 
-
-
-# Code pour trier les colonnes    
-    my $mod = $fileview->get_model();
-    $mod->set_default_sort_func(sub {
-            my ($model, $item1, $item2) = @_;
-            my $a = $model->get($item1, 1);  # récupération de la valeur de la 2ème 
-            my $b = $model->get($item2, 1);  # colonne (indice 1)
-            return $a cmp $b;
-        }
-    );
-    
-    $fileview->set_headers_clickable(1);
-    my $col = $fileview->get_column(1);    # la colonne NOM, colonne numéro 2
-    $col->signal_connect('clicked', sub {
-            my ($colonne, $model) = @_;
-            $model->set_sort_column_id (1, 'ascending');
-        },
-        $mod
-    );
+my $p = new Pref("$ENV{HOME}/.brestore.conf");
+$p->{debug} = 1;
+$p->connect_db() || print $p->{error};
+
+my $bvfs = new Bvfs(conf => $p);
+
+$bvfs->debug($bvfs->get_root());
+$bvfs->ch_dir($bvfs->get_root());
+$bvfs->up_dir();
+$bvfs->set_curjobids(268,178,282,281,279);
+$bvfs->ls_files();
+my $dirs = $bvfs->ls_dirs();
+$bvfs->ch_dir(123496);
+$dirs = $bvfs->ls_dirs();
+$bvfs->ls_files();
+map { $bvfs->debug($_) } $bvfs->get_all_file_versions($bvfs->{cwdid},312433, "exw3srv3", 1);