]> git.sur5r.net Git - bacula/bacula/commitdiff
Add restore wizard to the tray monitor.
authorNorbert Bizet <norbert.bizet@baculasystems.com>
Thu, 2 Nov 2017 14:26:40 +0000 (15:26 +0100)
committerKern Sibbald <kern@sibbald.com>
Sun, 12 Nov 2017 10:25:28 +0000 (11:25 +0100)
26 files changed:
bacula/src/qt-console/bat.pro.mingw64
bacula/src/qt-console/tray-monitor/clientselectwizardpage.cpp [new file with mode: 0644]
bacula/src/qt-console/tray-monitor/clientselectwizardpage.h [new file with mode: 0644]
bacula/src/qt-console/tray-monitor/clientselectwizardpage.ui [new file with mode: 0644]
bacula/src/qt-console/tray-monitor/fileselectwizardpage.cpp [new file with mode: 0644]
bacula/src/qt-console/tray-monitor/fileselectwizardpage.h [new file with mode: 0644]
bacula/src/qt-console/tray-monitor/fileselectwizardpage.ui [new file with mode: 0644]
bacula/src/qt-console/tray-monitor/filesmodel.h [new file with mode: 0644]
bacula/src/qt-console/tray-monitor/jobselectwizardpage.cpp [new file with mode: 0644]
bacula/src/qt-console/tray-monitor/jobselectwizardpage.h [new file with mode: 0644]
bacula/src/qt-console/tray-monitor/jobselectwizardpage.ui [new file with mode: 0644]
bacula/src/qt-console/tray-monitor/jobsmodel.cpp [new file with mode: 0644]
bacula/src/qt-console/tray-monitor/jobsmodel.h [new file with mode: 0644]
bacula/src/qt-console/tray-monitor/restoreoptionswizardpage.cpp [new file with mode: 0644]
bacula/src/qt-console/tray-monitor/restoreoptionswizardpage.h [new file with mode: 0644]
bacula/src/qt-console/tray-monitor/restoreoptionswizardpage.ui [new file with mode: 0644]
bacula/src/qt-console/tray-monitor/restorewizard.cpp [new file with mode: 0644]
bacula/src/qt-console/tray-monitor/restorewizard.h [new file with mode: 0644]
bacula/src/qt-console/tray-monitor/restorewizard.ui [new file with mode: 0644]
bacula/src/qt-console/tray-monitor/task.cpp
bacula/src/qt-console/tray-monitor/task.h
bacula/src/qt-console/tray-monitor/tray-monitor.pro.in
bacula/src/qt-console/tray-monitor/tray-monitor.pro.mingw32.in
bacula/src/qt-console/tray-monitor/tray-monitor.pro.mingw64.in
bacula/src/qt-console/tray-monitor/tray-ui.h
bacula/src/qt-console/tray-monitor/tray_conf.h

index 1765985f8ae029e2ea77aaa545b5ca6effc07027..4812fecaacb87c5eaf56863fc1a4d1b820007dab 100644 (file)
@@ -24,7 +24,7 @@ cross-win32 {
   LIBS       +=  -mwindows -L../win32/release64 -lbacula
 }
 !cross-win32 {
-  LIBS        += -L../lib -lbac -L../findlib -lbacfind -L/opt/local/lib -lssl -lcrypto
+  LIBS        += -L../lib -lbac -L../findlib -lbacfind -lssl -lcrypto
 }
 
 qwt {
diff --git a/bacula/src/qt-console/tray-monitor/clientselectwizardpage.cpp b/bacula/src/qt-console/tray-monitor/clientselectwizardpage.cpp
new file mode 100644 (file)
index 0000000..4b06c29
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+   Bacula(R) - The Network Backup Solution
+
+   Copyright (C) 2000-2017 Kern Sibbald
+
+   The original author of Bacula is Kern Sibbald, with contributions
+   from many others, a complete list can be found in the file AUTHORS.
+
+   You may use this file and others of this release according to the
+   license defined in the LICENSE file, which includes the Affero General
+   Public License, v3.0 ("AGPLv3") and some additional permissions and
+   terms pursuant to its AGPLv3 Section 7.
+
+   This notice must be preserved when any source code is
+   conveyed and/or propagated.
+
+   Bacula(R) is a registered trademark of Kern Sibbald.
+*/
+#include "clientselectwizardpage.h"
+#include "ui_clientselectwizardpage.h"
+#include "common.h"
+ClientSelectWizardPage::ClientSelectWizardPage(QWidget *parent) :
+    QWizardPage(parent),
+    ui(new Ui::ClientSelectWizardPage)
+{
+    ui->setupUi(this);
+
+    registerField("currentClient*", ui->backupClientComboBox);
+}
+
+ClientSelectWizardPage::~ClientSelectWizardPage()
+{
+    delete ui;
+}
+
+void ClientSelectWizardPage::setClients(alist *lst)
+{
+    if (lst && lst->size() > 0) {
+       ui->backupClientComboBox->clear();
+       QStringList list;
+       char *str;
+       foreach_alist(str, lst) {
+          list << QString(str);
+       }
+       ui->backupClientComboBox->addItems(list);
+       ui->backupClientComboBox->setEnabled(true);
+    } else {
+       ui->backupClientComboBox->setEnabled(false);
+    }
+}
diff --git a/bacula/src/qt-console/tray-monitor/clientselectwizardpage.h b/bacula/src/qt-console/tray-monitor/clientselectwizardpage.h
new file mode 100644 (file)
index 0000000..c0a2fd9
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+   Bacula(R) - The Network Backup Solution
+
+   Copyright (C) 2000-2017 Kern Sibbald
+
+   The original author of Bacula is Kern Sibbald, with contributions
+   from many others, a complete list can be found in the file AUTHORS.
+
+   You may use this file and others of this release according to the
+   license defined in the LICENSE file, which includes the Affero General
+   Public License, v3.0 ("AGPLv3") and some additional permissions and
+   terms pursuant to its AGPLv3 Section 7.
+
+   This notice must be preserved when any source code is
+   conveyed and/or propagated.
+
+   Bacula(R) is a registered trademark of Kern Sibbald.
+*/
+#ifndef CLIENTSELECTWIZARDPAGE_H
+#define CLIENTSELECTWIZARDPAGE_H
+
+#include <QWizardPage>
+
+class alist;
+
+namespace Ui {
+class ClientSelectWizardPage;
+}
+
+class ClientSelectWizardPage : public QWizardPage
+{
+    Q_OBJECT
+
+public:
+    explicit ClientSelectWizardPage(QWidget *parent = 0);
+    ~ClientSelectWizardPage();
+
+    void setClients(alist *lst);
+
+private:
+    Ui::ClientSelectWizardPage *ui;
+};
+
+#endif // CLIENTSELECTWIZARDPAGE_H
diff --git a/bacula/src/qt-console/tray-monitor/clientselectwizardpage.ui b/bacula/src/qt-console/tray-monitor/clientselectwizardpage.ui
new file mode 100644 (file)
index 0000000..484d0d9
--- /dev/null
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ClientSelectWizardPage</class>
+ <widget class="QWizardPage" name="ClientSelectWizardPage">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>714</width>
+    <height>330</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>WizardPage</string>
+  </property>
+  <layout class="QHBoxLayout" name="horizontalLayout">
+   <item>
+    <widget class="QLabel" name="backupClientLabel">
+     <property name="enabled">
+      <bool>true</bool>
+     </property>
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+       <horstretch>0</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="minimumSize">
+      <size>
+       <width>0</width>
+       <height>0</height>
+      </size>
+     </property>
+     <property name="contextMenuPolicy">
+      <enum>Qt::DefaultContextMenu</enum>
+     </property>
+     <property name="text">
+      <string>Backup Client:</string>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QComboBox" name="backupClientComboBox">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
+       <horstretch>0</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="minimumSize">
+      <size>
+       <width>595</width>
+       <height>0</height>
+      </size>
+     </property>
+     <property name="accessibleName">
+      <string/>
+     </property>
+     <property name="currentIndex">
+      <number>-1</number>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/bacula/src/qt-console/tray-monitor/fileselectwizardpage.cpp b/bacula/src/qt-console/tray-monitor/fileselectwizardpage.cpp
new file mode 100644 (file)
index 0000000..746b070
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+   Bacula(R) - The Network Backup Solution
+
+   Copyright (C) 2000-2017 Kern Sibbald
+
+   The original author of Bacula is Kern Sibbald, with contributions
+   from many others, a complete list can be found in the file AUTHORS.
+
+   You may use this file and others of this release according to the
+   license defined in the LICENSE file, which includes the Affero General
+   Public License, v3.0 ("AGPLv3") and some additional permissions and
+   terms pursuant to its AGPLv3 Section 7.
+
+   This notice must be preserved when any source code is
+   conveyed and/or propagated.
+
+   Bacula(R) is a registered trademark of Kern Sibbald.
+*/
+#include "fileselectwizardpage.h"
+#include "ui_fileselectwizardpage.h"
+#include "task.h"
+#include "filesmodel.h"
+#include <QStandardItemModel>
+
+FileSelectWizardPage::FileSelectWizardPage(QWidget *parent) :
+    QWizardPage(parent),
+    ui(new Ui::FileSelectWizardPage),
+    currentPathId(0)
+{
+    ui->setupUi(this);
+    registerField("currentFile", this, "currentFile", "currentFileChanged");
+
+    connect(ui->sourceListView, SIGNAL(activated(const QModelIndex&)), this, SLOT(changeCurrentFolder(const QModelIndex&)));
+    ui->sourceListView->setLayoutMode(QListView::Batched);
+    ui->sourceListView->setBatchSize(BATCH_SIZE);
+
+    /* FIXME : need context menu + shortcuts for del */
+    FileDestModel *destModel = new FileDestModel();
+    connect(destModel, SIGNAL(rowsInserted(const QModelIndex &, int, int)), this, SIGNAL(completeChanged()));
+    connect(destModel, SIGNAL(rowsRemoved(const QModelIndex &, int, int)), this, SIGNAL(completeChanged()));
+    ui->destListView->setModel(destModel);
+
+
+}
+
+FileSelectWizardPage::~FileSelectWizardPage()
+{
+    delete ui;
+}
+
+void FileSelectWizardPage::setModels(QStandardItemModel *src_model, QStandardItemModel *dest_model)
+{
+    ui->sourceListView->setModel(src_model);
+    src_model->clear();
+    currentPathId = 0;
+
+    ui->destListView->setModel(dest_model);
+    connect(dest_model, SIGNAL(rowsInserted(const QModelIndex &, int, int)), this, SIGNAL(completeChanged()));
+    connect(dest_model, SIGNAL(rowsRemoved(const QModelIndex &, int, int)), this, SIGNAL(completeChanged()));
+
+}
+
+qulonglong FileSelectWizardPage::currentFile() const
+{
+    return currentPathId;
+}
+
+void FileSelectWizardPage::changeCurrentFolder(const QModelIndex& current)
+{
+    if (current.isValid() && ui->sourceListView->model()) {
+        QStandardItem *item = ((QStandardItemModel *)ui->sourceListView->model())->itemFromIndex(current);
+        if (item && item->data(TypeRole) == TYPEROLE_DIRECTORY) {
+            currentPathId = item->data(PathIdRole).toULongLong();
+            emit currentFileChanged();
+        }
+    }
+}
+
+bool FileSelectWizardPage::isComplete() const
+{
+    return ui->destListView->model() && ui->destListView->model()->rowCount() != 0;
+}
diff --git a/bacula/src/qt-console/tray-monitor/fileselectwizardpage.h b/bacula/src/qt-console/tray-monitor/fileselectwizardpage.h
new file mode 100644 (file)
index 0000000..c054b65
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+   Bacula(R) - The Network Backup Solution
+
+   Copyright (C) 2000-2017 Kern Sibbald
+
+   The original author of Bacula is Kern Sibbald, with contributions
+   from many others, a complete list can be found in the file AUTHORS.
+
+   You may use this file and others of this release according to the
+   license defined in the LICENSE file, which includes the Affero General
+   Public License, v3.0 ("AGPLv3") and some additional permissions and
+   terms pursuant to its AGPLv3 Section 7.
+
+   This notice must be preserved when any source code is
+   conveyed and/or propagated.
+
+   Bacula(R) is a registered trademark of Kern Sibbald.
+*/
+#ifndef FILESELECTWIZARDPAGE_H
+#define FILESELECTWIZARDPAGE_H
+
+#include <QWizardPage>
+
+class QStandardItemModel;
+class QModelIndex;
+
+namespace Ui {
+class FileSelectWizardPage;
+}
+
+class FileSelectWizardPage : public QWizardPage
+{
+    Q_OBJECT
+    Q_PROPERTY(qulonglong currentFile READ currentFile NOTIFY currentFileChanged)
+
+public:
+    explicit FileSelectWizardPage(QWidget *parent = 0);
+    ~FileSelectWizardPage();
+
+    void setModels(QStandardItemModel *src_model, QStandardItemModel *dest_model);
+
+    qulonglong currentFile() const;
+
+    bool isComplete() const;
+
+signals:
+    void currentFileChanged();
+
+protected slots:
+    void changeCurrentFolder(const QModelIndex& current);
+
+private:
+    Ui::FileSelectWizardPage *ui;
+    qulonglong                currentPathId;
+};
+
+#endif // FILESELECTWIZARDPAGE_H
diff --git a/bacula/src/qt-console/tray-monitor/fileselectwizardpage.ui b/bacula/src/qt-console/tray-monitor/fileselectwizardpage.ui
new file mode 100644 (file)
index 0000000..4413c9f
--- /dev/null
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>FileSelectWizardPage</class>
+ <widget class="QWizardPage" name="FileSelectWizardPage">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>739</width>
+    <height>437</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>WizardPage</string>
+  </property>
+  <layout class="QGridLayout" name="gridLayout">
+   <item row="0" column="0">
+    <widget class="QGroupBox" name="groupBox">
+     <property name="title">
+      <string>Drag from left to right</string>
+     </property>
+     <layout class="QHBoxLayout" name="horizontalLayout">
+      <item>
+       <widget class="QListView" name="sourceListView">
+        <property name="editTriggers">
+         <set>QAbstractItemView::NoEditTriggers</set>
+        </property>
+        <property name="showDropIndicator" stdset="0">
+         <bool>false</bool>
+        </property>
+        <property name="dragEnabled">
+         <bool>true</bool>
+        </property>
+        <property name="dragDropMode">
+         <enum>QAbstractItemView::DragOnly</enum>
+        </property>
+        <property name="alternatingRowColors">
+         <bool>true</bool>
+        </property>
+        <property name="selectionMode">
+         <enum>QAbstractItemView::ExtendedSelection</enum>
+        </property>
+        <property name="selectionBehavior">
+         <enum>QAbstractItemView::SelectRows</enum>
+        </property>
+        <property name="layoutMode">
+         <enum>QListView::Batched</enum>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QListView" name="destListView">
+        <property name="acceptDrops">
+         <bool>false</bool>
+        </property>
+        <property name="editTriggers">
+         <set>QAbstractItemView::NoEditTriggers</set>
+        </property>
+        <property name="dragEnabled">
+         <bool>true</bool>
+        </property>
+        <property name="dragDropOverwriteMode">
+         <bool>false</bool>
+        </property>
+        <property name="dragDropMode">
+         <enum>QAbstractItemView::DropOnly</enum>
+        </property>
+        <property name="defaultDropAction">
+         <enum>Qt::CopyAction</enum>
+        </property>
+        <property name="selectionMode">
+         <enum>QAbstractItemView::ExtendedSelection</enum>
+        </property>
+        <property name="selectionBehavior">
+         <enum>QAbstractItemView::SelectRows</enum>
+        </property>
+        <property name="layoutMode">
+         <enum>QListView::Batched</enum>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/bacula/src/qt-console/tray-monitor/filesmodel.h b/bacula/src/qt-console/tray-monitor/filesmodel.h
new file mode 100644 (file)
index 0000000..1940fcf
--- /dev/null
@@ -0,0 +1,197 @@
+/*
+   Bacula(R) - The Network Backup Solution
+
+   Copyright (C) 2000-2017 Kern Sibbald
+
+   The original author of Bacula is Kern Sibbald, with contributions
+   from many others, a complete list can be found in the file AUTHORS.
+
+   You may use this file and others of this release according to the
+   license defined in the LICENSE file, which includes the Affero General
+   Public License, v3.0 ("AGPLv3") and some additional permissions and
+   terms pursuant to its AGPLv3 Section 7.
+
+   This notice must be preserved when any source code is
+   conveyed and/or propagated.
+
+   Bacula(R) is a registered trademark of Kern Sibbald.
+*/
+#ifndef FILESMODEL_H
+#define FILESMODEL_H
+#include <QStandardItemModel>
+#include <QFileIconProvider>
+#include <QMimeData>
+#include "task.h"
+enum {
+    PathIdRole = Qt::UserRole+1,
+    FilenameIdRole = Qt::UserRole+2,
+    FileIdRole = Qt::UserRole+3,
+    JobIdRole = Qt::UserRole+4,
+    LStatRole = Qt::UserRole+5,
+    PathRole = Qt::UserRole+6,
+    TypeRole = Qt::UserRole+7
+};
+
+enum {
+    TYPEROLE_DIRECTORY = 0,
+    TYPEROLE_FILE
+};
+
+class DirectoryItem : public QStandardItem
+{
+public:
+    DirectoryItem() : QStandardItem()
+    {
+        /* explicit set the data, so it can be serialized in Mime Data for D&D */
+        setData(QVariant(TYPEROLE_DIRECTORY), TypeRole);
+    }
+    QVariant data(int role = Qt::UserRole + 1) const
+    {
+        if (role == Qt::DecorationRole) {
+            QFileIconProvider provider;
+            return provider.icon(QFileIconProvider::Folder);
+        }
+
+        return QStandardItem::data(role);
+    }
+};
+
+class FileItem : public QStandardItem
+{
+public:
+    FileItem() : QStandardItem()
+    {
+        /* explicit set the data, so it can be serialized in Mime Data for D&D */
+        setData(QVariant(TYPEROLE_FILE), TypeRole);
+    }
+    enum {
+        FILE_TYPE = UserType +2
+    };
+
+    QVariant data(int role = Qt::UserRole + 1) const
+    {
+        if (role == Qt::DecorationRole) {
+            QFileIconProvider provider;
+            return provider.icon(QFileIconProvider::File);
+        }
+
+        return QStandardItem::data(role);
+    }
+};
+
+#define BATCH_SIZE 100
+class FileSourceModel : public QStandardItemModel
+{
+public:
+    FileSourceModel() : QStandardItemModel(),
+        m_cursor(0),
+        m_batchSize(BATCH_SIZE),
+        m_canFetchMore(true)
+    {}
+
+    bool canFetchMore(const QModelIndex &parent) const
+    {
+        Q_UNUSED(parent)
+        return false/*m_canFetchMore*/;
+    }
+    void fetchMore(const QModelIndex &parent)
+    {
+        Q_UNUSED(parent)
+    }
+
+public slots:
+    void taskComplete(task *t)
+    {
+        // increase cursor
+        // update canfetch
+        // deletelater
+        t->deleteLater();
+    }
+
+private:
+    u_int64_t       m_cursor;
+    u_int64_t       m_batchSize;
+    bool            m_canFetchMore;
+};
+
+class FileDestModel : public QStandardItemModel
+{
+    bool canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const
+    {
+        Q_UNUSED(action)
+        Q_UNUSED(row)
+        Q_UNUSED(column)
+        Q_UNUSED(parent)
+
+        if (data->hasFormat("application/x-qstandarditemmodeldatalist")) {
+            QByteArray encoded = data->data("application/x-qabstractitemmodeldatalist");
+            QDataStream stream(&encoded, QIODevice::ReadOnly);
+
+            while (!stream.atEnd())
+            {
+                int row, col;
+                QMap<int,  QVariant> roleDataMap;
+                stream >> row >> col >> roleDataMap;
+
+                /* do something with the data */
+                int type = roleDataMap[TypeRole].toInt();
+
+                switch(type) {
+                case TYPEROLE_DIRECTORY:
+                case TYPEROLE_FILE:
+                    break;
+                default:
+                    return false;
+                }
+            }
+            return true;
+        }
+        return false;
+    }
+
+    bool dropMimeData(const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent)
+    {
+        Q_UNUSED(action)
+        Q_UNUSED(row)
+        Q_UNUSED(column)
+        Q_UNUSED(parent)
+
+        QByteArray encoded = data->data("application/x-qabstractitemmodeldatalist");
+        QDataStream stream(&encoded, QIODevice::ReadOnly);
+
+        while (!stream.atEnd())
+        {
+            int row, col;
+            QMap<int,  QVariant> roleDataMap;
+            stream >> row >> col >> roleDataMap;
+
+            QStandardItem *item;
+            /* do something with the data */
+            int type = roleDataMap[TypeRole].toInt();
+
+            switch(type) {
+            case TYPEROLE_DIRECTORY:
+                item = new DirectoryItem();
+                break;
+            case TYPEROLE_FILE:
+                item = new FileItem();
+                break;
+            default:
+                return false;
+            }
+
+            item->setData(roleDataMap[PathIdRole], PathIdRole);
+            item->setData(roleDataMap[FilenameIdRole], FilenameIdRole);
+            item->setData(roleDataMap[FileIdRole], FileIdRole);
+            item->setData(roleDataMap[JobIdRole], JobIdRole);
+            item->setData(roleDataMap[LStatRole], LStatRole);
+            item->setData(roleDataMap[PathRole], PathRole);
+            item->setData(roleDataMap[PathRole], Qt::DisplayRole);
+
+            appendRow(item);
+        }
+        return true;
+    }
+};
+
+#endif // FILESMODEL_H
diff --git a/bacula/src/qt-console/tray-monitor/jobselectwizardpage.cpp b/bacula/src/qt-console/tray-monitor/jobselectwizardpage.cpp
new file mode 100644 (file)
index 0000000..66302c7
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+   Bacula(R) - The Network Backup Solution
+
+   Copyright (C) 2000-2017 Kern Sibbald
+
+   The original author of Bacula is Kern Sibbald, with contributions
+   from many others, a complete list can be found in the file AUTHORS.
+
+   You may use this file and others of this release according to the
+   license defined in the LICENSE file, which includes the Affero General
+   Public License, v3.0 ("AGPLv3") and some additional permissions and
+   terms pursuant to its AGPLv3 Section 7.
+
+   This notice must be preserved when any source code is
+   conveyed and/or propagated.
+
+   Bacula(R) is a registered trademark of Kern Sibbald.
+*/
+#include "jobselectwizardpage.h"
+#include "ui_jobselectwizardpage.h"
+#include <QStandardItemModel>
+
+JobSelectWizardPage::JobSelectWizardPage(QWidget *parent) :
+    QWizardPage(parent),
+    ui(new Ui::JobSelectWizardPage)
+{
+    ui->setupUi(this);
+    registerField("currentJob*", this, "currentJob", SIGNAL(currentJobChanged()));
+}
+
+JobSelectWizardPage::~JobSelectWizardPage()
+{
+    delete ui;
+}
+
+void JobSelectWizardPage::setModel(QStandardItemModel *model)
+{
+    ui->BackupTableView->setModel(model);
+
+    connect(ui->BackupTableView->selectionModel(), SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), this, SIGNAL(completeChanged()));
+    connect(ui->BackupTableView->selectionModel(), SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), this, SIGNAL(currentJobChanged()));
+}
+
+bool JobSelectWizardPage::isComplete() const
+{
+    int s = ui->BackupTableView->selectionModel() ?
+                ui->BackupTableView->selectionModel()->selectedRows().size() : 0;
+
+    return (ui->BackupTableView->selectionModel() && s>0);
+}
+
+void JobSelectWizardPage::optimizeSize()
+{
+    ui->BackupTableView->resizeColumnsToContents();
+    ui->BackupTableView->horizontalHeader()->setStretchLastSection(true);
+    ui->BackupTableView->horizontalHeader()->setSortIndicator(0, Qt::AscendingOrder);
+}
+
+u_int64_t JobSelectWizardPage::currentJob() const
+{
+    if (ui->BackupTableView->selectionModel())
+    {
+        QModelIndex idx = ui->BackupTableView->selectionModel()->currentIndex();
+        /* return the JobId (column 0) */
+        QModelIndex idIdx = idx.sibling(idx.row(), 0);
+        return idIdx.data().toULongLong();
+    }
+    return -1;
+}
diff --git a/bacula/src/qt-console/tray-monitor/jobselectwizardpage.h b/bacula/src/qt-console/tray-monitor/jobselectwizardpage.h
new file mode 100644 (file)
index 0000000..d299fa6
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+   Bacula(R) - The Network Backup Solution
+
+   Copyright (C) 2000-2017 Kern Sibbald
+
+   The original author of Bacula is Kern Sibbald, with contributions
+   from many others, a complete list can be found in the file AUTHORS.
+
+   You may use this file and others of this release according to the
+   license defined in the LICENSE file, which includes the Affero General
+   Public License, v3.0 ("AGPLv3") and some additional permissions and
+   terms pursuant to its AGPLv3 Section 7.
+
+   This notice must be preserved when any source code is
+   conveyed and/or propagated.
+
+   Bacula(R) is a registered trademark of Kern Sibbald.
+*/
+#ifndef JOBSELECTWIZARDPAGE_H
+#define JOBSELECTWIZARDPAGE_H
+
+#include <QWizardPage>
+
+class QStandardItemModel;
+class QItemSelection;
+
+namespace Ui {
+class JobSelectWizardPage;
+}
+
+class JobSelectWizardPage : public QWizardPage
+{
+    Q_OBJECT
+    Q_PROPERTY(qlonglong currentJob READ currentJob NOTIFY currentJobChanged)
+
+public:
+    explicit JobSelectWizardPage(QWidget *parent = 0);
+    ~JobSelectWizardPage();
+
+    void setModel(QStandardItemModel *model);
+
+    u_int64_t currentJob() const;
+
+    bool isComplete() const;
+
+signals:
+    void currentJobChanged();
+
+public slots:
+    void optimizeSize();
+
+private:
+    Ui::JobSelectWizardPage *ui;
+    qlonglong               m_jobId;
+};
+
+#endif // JOBSELECTWIZARDPAGE_H
diff --git a/bacula/src/qt-console/tray-monitor/jobselectwizardpage.ui b/bacula/src/qt-console/tray-monitor/jobselectwizardpage.ui
new file mode 100644 (file)
index 0000000..e45c3c8
--- /dev/null
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>JobSelectWizardPage</class>
+ <widget class="QWizardPage" name="JobSelectWizardPage">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>706</width>
+    <height>360</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>WizardPage</string>
+  </property>
+  <layout class="QGridLayout" name="gridLayout">
+   <item row="0" column="0">
+    <widget class="QTableView" name="BackupTableView">
+     <property name="editTriggers">
+      <set>QAbstractItemView::NoEditTriggers</set>
+     </property>
+     <property name="selectionMode">
+      <enum>QAbstractItemView::SingleSelection</enum>
+     </property>
+     <property name="selectionBehavior">
+      <enum>QAbstractItemView::SelectRows</enum>
+     </property>
+     <property name="sortingEnabled">
+      <bool>true</bool>
+     </property>
+     <property name="wordWrap">
+      <bool>false</bool>
+     </property>
+     <property name="cornerButtonEnabled">
+      <bool>false</bool>
+     </property>
+     <attribute name="horizontalHeaderHighlightSections">
+      <bool>false</bool>
+     </attribute>
+     <attribute name="horizontalHeaderStretchLastSection">
+      <bool>true</bool>
+     </attribute>
+     <attribute name="verticalHeaderVisible">
+      <bool>false</bool>
+     </attribute>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/bacula/src/qt-console/tray-monitor/jobsmodel.cpp b/bacula/src/qt-console/tray-monitor/jobsmodel.cpp
new file mode 100644 (file)
index 0000000..1b8610a
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+   Bacula(R) - The Network Backup Solution
+
+   Copyright (C) 2000-2017 Kern Sibbald
+
+   The original author of Bacula is Kern Sibbald, with contributions
+   from many others, a complete list can be found in the file AUTHORS.
+
+   You may use this file and others of this release according to the
+   license defined in the LICENSE file, which includes the Affero General
+   Public License, v3.0 ("AGPLv3") and some additional permissions and
+   terms pursuant to its AGPLv3 Section 7.
+
+   This notice must be preserved when any source code is
+   conveyed and/or propagated.
+
+   Bacula(R) is a registered trademark of Kern Sibbald.
+*/
+#include "jobsmodel.h"
+
+JobsModel::JobsModel(const QList<row_struct>& t, QObject *parent)
+    : QAbstractTableModel(parent)
+    , table(t)
+{
+}
+
+QVariant JobsModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+    if (role != Qt::DisplayRole)
+        return QVariant();
+
+    if (orientation == Qt::Horizontal) {
+        switch (section) {
+        case ID_COLUMN:
+            return tr("Id");
+        case TDATE_COLUMN:
+            return tr("Date");
+        case HASCACHE_COLUMN:
+            return tr("Cache");
+        case NAME_COLUMN:
+            return tr("Name");
+        default:
+            return QVariant();
+        }
+    }
+    return QVariant();
+
+
+    return QVariant();
+}
+
+int JobsModel::rowCount(const QModelIndex &parent) const
+{
+    if (parent.isValid())
+        return 0;
+
+    return table.size();
+}
+
+int JobsModel::columnCount(const QModelIndex &parent) const
+{
+    if (parent.isValid())
+        return 0;
+
+    return NUM_COLUMN;
+}
+
+QVariant JobsModel::data(const QModelIndex &index, int role) const
+{
+    if (!index.isValid())
+        return QVariant();
+
+    if (index.row() >= table.size() || index.row() < 0)
+        return QVariant();
+
+    if (index.column() >= NUM_COLUMN || index.column() < 0)
+        return QVariant();
+
+    if (role == Qt::DisplayRole) {
+        row_struct row = table.at(index.row());
+        switch(index.column())
+        {
+        case 0:
+            return quint64(row.id);
+            break;
+        case 1:
+            return row.tdate;
+            break;
+        case 2:
+            return row.hasCache;
+            break;
+        case 3:
+            return row.name;
+            break;
+        }
+    }
+    return QVariant();
+}
diff --git a/bacula/src/qt-console/tray-monitor/jobsmodel.h b/bacula/src/qt-console/tray-monitor/jobsmodel.h
new file mode 100644 (file)
index 0000000..e47fcca
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+   Bacula(R) - The Network Backup Solution
+
+   Copyright (C) 2000-2017 Kern Sibbald
+
+   The original author of Bacula is Kern Sibbald, with contributions
+   from many others, a complete list can be found in the file AUTHORS.
+
+   You may use this file and others of this release according to the
+   license defined in the LICENSE file, which includes the Affero General
+   Public License, v3.0 ("AGPLv3") and some additional permissions and
+   terms pursuant to its AGPLv3 Section 7.
+
+   This notice must be preserved when any source code is
+   conveyed and/or propagated.
+
+   Bacula(R) is a registered trademark of Kern Sibbald.
+*/
+#ifndef JOBSMODEL_H
+#define JOBSMODEL_H
+
+#include <QAbstractTableModel>
+#include "common.h"
+
+class JobsModel : public QAbstractTableModel
+{
+    Q_OBJECT
+
+public:
+    enum {
+        ID_COLUMN =0,
+        TDATE_COLUMN,
+        HASCACHE_COLUMN,
+        NAME_COLUMN,
+        NUM_COLUMN
+    };
+
+    struct row_struct {
+        uint64_t    id;
+        QDateTime   tdate;
+        QString     hasCache;
+        QString     name;
+    };
+
+    explicit JobsModel(const QList<row_struct>& t, QObject *parent = NULL);
+    // Header:
+    QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
+
+    // Basic functionality:
+    int rowCount(const QModelIndex &parent = QModelIndex()) const;
+    int columnCount(const QModelIndex &parent = QModelIndex()) const;
+
+    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
+
+private:
+    QList<row_struct> table;
+};
+
+#endif // JOBSMODEL_H
diff --git a/bacula/src/qt-console/tray-monitor/restoreoptionswizardpage.cpp b/bacula/src/qt-console/tray-monitor/restoreoptionswizardpage.cpp
new file mode 100644 (file)
index 0000000..ffd01f8
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+   Bacula(R) - The Network Backup Solution
+
+   Copyright (C) 2000-2017 Kern Sibbald
+
+   The original author of Bacula is Kern Sibbald, with contributions
+   from many others, a complete list can be found in the file AUTHORS.
+
+   You may use this file and others of this release according to the
+   license defined in the LICENSE file, which includes the Affero General
+   Public License, v3.0 ("AGPLv3") and some additional permissions and
+   terms pursuant to its AGPLv3 Section 7.
+
+   This notice must be preserved when any source code is
+   conveyed and/or propagated.
+
+   Bacula(R) is a registered trademark of Kern Sibbald.
+*/
+#include "restoreoptionswizardpage.h"
+#include "ui_restoreoptionswizardpage.h"
+#include "common.h"
+#include "filesmodel.h"
+#include "task.h"
+
+RestoreOptionsWizardPage::RestoreOptionsWizardPage(QWidget *parent) :
+    QWizardPage(parent),
+    ui(new Ui::RestoreOptionsWizardPage),
+    dest_model(0),
+    m_res(0)
+
+{
+    ui->setupUi(this);
+
+    registerField("restoreClient*", ui->restoreClientComboxBox);
+    registerField("restoreWhere", ui->whereLineEdit);
+    registerField("restoreReplace*", ui->replaceComboBox);
+    registerField("restoreComment", ui->commentLineEdit);
+}
+
+RestoreOptionsWizardPage::~RestoreOptionsWizardPage()
+{
+    delete ui;
+}
+
+
+void RestoreOptionsWizardPage::fill_defaults(task *t)
+{
+    if (ui->restoreClientComboxBox->isEnabled()) {
+        ui->restoreClientComboxBox->setCurrentIndex(ui->restoreClientComboxBox->findData(t->res->defaults.client));
+    }
+    if (ui->whereLineEdit->isEnabled()) {
+        ui->whereLineEdit->setText(t->res->defaults.where);
+    }
+}
+
+void RestoreOptionsWizardPage::setClients(alist *lst)
+{
+    if (lst && lst->size() > 0) {
+       ui->restoreClientComboxBox->clear();
+       QStringList list;
+       char *str;
+       foreach_alist(str, lst) {
+          list << QString(str);
+       }
+       ui->restoreClientComboxBox->addItems(list);
+       ui->restoreClientComboxBox->setEnabled(true);
+    } else {
+       ui->restoreClientComboxBox->setEnabled(false);
+    }
+
+    /* by default, select the same client as in clientselectwizardPage */
+    /* this is assuming lists are identical and in the same order. It should be the case */
+    ui->restoreClientComboxBox->setCurrentIndex(field("currentClient").toInt());
+}
+
+void RestoreOptionsWizardPage::setModel(QStandardItemModel *model)
+{
+    dest_model = model;
+}
+
+void RestoreOptionsWizardPage::setResmon(RESMON *res)
+{
+    m_res = res;
+}
+extern int decode_stat(char *buf, struct stat *statp, int stat_size, int32_t *LinkFI);
+
+bool RestoreOptionsWizardPage::validatePage()
+{
+    task *t = new task();
+    connect(t, SIGNAL(done(task*)), t, SLOT(deleteLater()));
+
+    t->init(m_res, TASK_RESTORE);
+
+    t->arg = (char*) m_res->clients->get(field("restoreClient").toInt());
+
+    if (!field("restoreWhere").isNull()) {
+        t->arg2 = field("restoreWhere").toString().toUtf8();
+    } else {
+        t->arg2 = NULL;
+    }
+
+    if (!field("restoreComment").isNull()) {
+        t->arg3 = field("restoreComment").toString().toUtf8();
+    } else {
+        t->arg3 = NULL;
+    }
+
+    t->model = dest_model;
+
+    m_res->wrk->queue(t);
+    return true;
+}
diff --git a/bacula/src/qt-console/tray-monitor/restoreoptionswizardpage.h b/bacula/src/qt-console/tray-monitor/restoreoptionswizardpage.h
new file mode 100644 (file)
index 0000000..e503f86
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+   Bacula(R) - The Network Backup Solution
+
+   Copyright (C) 2000-2017 Kern Sibbald
+
+   The original author of Bacula is Kern Sibbald, with contributions
+   from many others, a complete list can be found in the file AUTHORS.
+
+   You may use this file and others of this release according to the
+   license defined in the LICENSE file, which includes the Affero General
+   Public License, v3.0 ("AGPLv3") and some additional permissions and
+   terms pursuant to its AGPLv3 Section 7.
+
+   This notice must be preserved when any source code is
+   conveyed and/or propagated.
+
+   Bacula(R) is a registered trademark of Kern Sibbald.
+*/
+#ifndef RESTOREOPTIONSWIZARDPAGE_H
+#define RESTOREOPTIONSWIZARDPAGE_H
+
+#include <QWizardPage>
+
+class alist;
+class QStandardItemModel;
+class RESMON;
+class task;
+
+namespace Ui {
+class RestoreOptionsWizardPage;
+}
+
+class RestoreOptionsWizardPage : public QWizardPage
+{
+    Q_OBJECT
+
+public:
+    explicit RestoreOptionsWizardPage(QWidget *parent = 0);
+    ~RestoreOptionsWizardPage();
+
+    void setClients(alist *lst);
+    void setModel(QStandardItemModel *model);
+    void setResmon(RESMON *res);
+
+    bool validatePage();
+
+public slots:
+    void fill_defaults(task *t);
+
+private:
+    Ui::RestoreOptionsWizardPage *ui;
+    QStandardItemModel *dest_model;
+    RESMON *m_res;
+};
+
+#endif // RESTOREOPTIONSWIZARDPAGE_H
diff --git a/bacula/src/qt-console/tray-monitor/restoreoptionswizardpage.ui b/bacula/src/qt-console/tray-monitor/restoreoptionswizardpage.ui
new file mode 100644 (file)
index 0000000..54379c1
--- /dev/null
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>RestoreOptionsWizardPage</class>
+ <widget class="QWizardPage" name="RestoreOptionsWizardPage">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>419</width>
+    <height>304</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>WizardPage</string>
+  </property>
+  <layout class="QFormLayout" name="formLayout">
+   <item row="0" column="0">
+    <widget class="QLabel" name="restoreClientLabel">
+     <property name="text">
+      <string>Restore Client:</string>
+     </property>
+    </widget>
+   </item>
+   <item row="0" column="1">
+    <widget class="QComboBox" name="restoreClientComboxBox"/>
+   </item>
+   <item row="1" column="0">
+    <widget class="QLabel" name="whereLabel">
+     <property name="text">
+      <string>Where :</string>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="1">
+    <widget class="QLineEdit" name="whereLineEdit"/>
+   </item>
+   <item row="2" column="0">
+    <widget class="QLabel" name="replaceLabel">
+     <property name="text">
+      <string>Replace :</string>
+     </property>
+    </widget>
+   </item>
+   <item row="2" column="1">
+    <widget class="QComboBox" name="replaceComboBox">
+     <item>
+      <property name="text">
+       <string>never</string>
+      </property>
+     </item>
+     <item>
+      <property name="text">
+       <string>always</string>
+      </property>
+     </item>
+     <item>
+      <property name="text">
+       <string>if newer</string>
+      </property>
+     </item>
+     <item>
+      <property name="text">
+       <string>if older</string>
+      </property>
+     </item>
+    </widget>
+   </item>
+   <item row="3" column="0">
+    <widget class="QLabel" name="commentLabel">
+     <property name="text">
+      <string>Comment :</string>
+     </property>
+    </widget>
+   </item>
+   <item row="3" column="1">
+    <widget class="QLineEdit" name="commentLineEdit"/>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/bacula/src/qt-console/tray-monitor/restorewizard.cpp b/bacula/src/qt-console/tray-monitor/restorewizard.cpp
new file mode 100644 (file)
index 0000000..e25bfdd
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+   Bacula(R) - The Network Backup Solution
+
+   Copyright (C) 2000-2017 Kern Sibbald
+
+   The original author of Bacula is Kern Sibbald, with contributions
+   from many others, a complete list can be found in the file AUTHORS.
+
+   You may use this file and others of this release according to the
+   license defined in the LICENSE file, which includes the Affero General
+   Public License, v3.0 ("AGPLv3") and some additional permissions and
+   terms pursuant to its AGPLv3 Section 7.
+
+   This notice must be preserved when any source code is
+   conveyed and/or propagated.
+
+   Bacula(R) is a registered trademark of Kern Sibbald.
+*/
+#include "restorewizard.h"
+#include "ui_restorewizard.h"
+#include "filesmodel.h"
+#include "task.h"
+#include <QStandardItemModel>
+
+RestoreWizard::RestoreWizard(RESMON *r, QWidget *parent) :
+    QWizard(parent),
+    res(r),
+    ui(new Ui::RestoreWizard),
+    jobs_model(new QStandardItemModel),
+    src_files_model(new FileSourceModel),
+    dest_files_model(new FileDestModel)
+{
+    ui->setupUi(this);
+
+    connect(this, SIGNAL(currentIdChanged(int)), this, SLOT(pageChanged(int)));
+
+    ui->RestWizBackupSelectPage->setModel(jobs_model);
+    ui->RestWiFileSelectionPage->setModels(src_files_model, dest_files_model);
+    connect(ui->RestWiFileSelectionPage, SIGNAL(currentFileChanged()), this, SLOT(fillFilePage()));
+    ui->RestWizAdvancedOptionsPage->setModel(dest_files_model);
+    ui->RestWizAdvancedOptionsPage->setResmon(res);
+
+}
+
+void RestoreWizard::pageChanged(int index)
+{
+    switch(index) {
+    case RW_CLIENT_PAGE:
+        ui->RestWizClientPage->setClients(res->clients);
+        break;
+    case RW_JOB_PAGE:
+        fillJobPage();
+        break;
+    case RW_FILE_PAGE:
+        fillFilePage();
+        break;
+    case RW_ADVANCEDOPTIONS_PAGE:
+        fillOptionsPage();
+        break;
+    default:
+        qDebug( "pageChanged default: %d", index );
+        break;
+    }
+}
+RestoreWizard::~RestoreWizard()
+{
+    delete ui;
+}
+
+void RestoreWizard::fillJobPage()
+{
+    task *t = new task();
+    connect(t, SIGNAL(done(task*)), ui->RestWizBackupSelectPage, SLOT(optimizeSize()), Qt::QueuedConnection);
+    connect(t, SIGNAL(done(task*)), t, SLOT(deleteLater()));
+
+    t->init(res, TASK_LIST_CLIENT_JOBS);
+
+    int idx = field("currentClient").toInt();
+    char *p = (char*) res->clients->get(idx);
+    POOL_MEM info;
+    pm_strcpy(info, p);
+    t->arg = info.c_str();
+    t->model = jobs_model;
+    res->wrk->queue(t);
+}
+
+void RestoreWizard::fillFilePage()
+{
+    task *t = new task();
+    connect(t, SIGNAL(done(task*)), t, SLOT(deleteLater()));
+
+    t->init(res, TASK_LIST_JOB_FILES);
+
+    const char *p = field("currentJob").toString().toUtf8();
+    POOL_MEM info;
+    pm_strcpy(info, p);
+    t->arg = info.c_str();
+
+    t->pathId = field("currentFile").toULongLong();
+    t->model = src_files_model;
+
+    res->wrk->queue(t);
+}
+
+void RestoreWizard::fillOptionsPage()
+{
+    ui->RestWizAdvancedOptionsPage->setClients(res->clients);
+
+    task *t = new task();
+    connect(t, SIGNAL(done(task *)), ui->RestWizAdvancedOptionsPage, SLOT(fill_defaults(task *)), Qt::QueuedConnection);
+    connect(t, SIGNAL(done(task*)), t, SLOT(deleteLater()));
+
+    const char *p = "RestoreFile";/*field("currentJob").toString().toStdString().c_str();*/
+    POOL_MEM info;
+    pm_strcpy(info, p);
+    res->mutex->lock();
+    bfree_and_null(res->defaults.job);
+    res->defaults.job = bstrdup(info.c_str());
+    res->mutex->unlock();
+
+    t->init(res, TASK_DEFAULTS);
+    res->wrk->queue(t);
+}
diff --git a/bacula/src/qt-console/tray-monitor/restorewizard.h b/bacula/src/qt-console/tray-monitor/restorewizard.h
new file mode 100644 (file)
index 0000000..17880ab
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+   Bacula(R) - The Network Backup Solution
+
+   Copyright (C) 2000-2017 Kern Sibbald
+
+   The original author of Bacula is Kern Sibbald, with contributions
+   from many others, a complete list can be found in the file AUTHORS.
+
+   You may use this file and others of this release according to the
+   license defined in the LICENSE file, which includes the Affero General
+   Public License, v3.0 ("AGPLv3") and some additional permissions and
+   terms pursuant to its AGPLv3 Section 7.
+
+   This notice must be preserved when any source code is
+   conveyed and/or propagated.
+
+   Bacula(R) is a registered trademark of Kern Sibbald.
+*/
+#ifndef RESTOREWIZARD_H
+#define RESTOREWIZARD_H
+
+#include <QWizard>
+#include <QModelIndex>
+namespace Ui {
+class RestoreWizard;
+}
+
+class task;
+class RESMON;
+class QStandardItemModel;
+
+class RestoreWizard : public QWizard
+{
+    Q_OBJECT
+
+public:
+    enum {
+        RW_CLIENT_PAGE = 0,
+        RW_JOB_PAGE = 1,
+        RW_FILE_PAGE = 2,
+        RW_ADVANCEDOPTIONS_PAGE = 3
+    };
+
+    explicit RestoreWizard(RESMON *r, QWidget *parent = 0);
+    ~RestoreWizard();
+
+public slots:
+    void pageChanged(int index);
+
+protected slots:
+    void fillJobPage();
+    void fillFilePage();
+    void fillOptionsPage();
+
+private:
+    RESMON            *res;
+    Ui::RestoreWizard *ui;
+
+    QStandardItemModel *jobs_model;
+    QStandardItemModel *src_files_model;
+    QStandardItemModel *dest_files_model;
+};
+
+#endif // RESTOREWIZARD_H
diff --git a/bacula/src/qt-console/tray-monitor/restorewizard.ui b/bacula/src/qt-console/tray-monitor/restorewizard.ui
new file mode 100644 (file)
index 0000000..5c24c03
--- /dev/null
@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>RestoreWizard</class>
+ <widget class="QWizard" name="RestoreWizard">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>853</width>
+    <height>444</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Wizard</string>
+  </property>
+  <property name="autoFillBackground">
+   <bool>false</bool>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>false</bool>
+  </property>
+  <widget class="ClientSelectWizardPage" name="RestWizClientPage">
+   <property name="sizePolicy">
+    <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+     <horstretch>2</horstretch>
+     <verstretch>0</verstretch>
+    </sizepolicy>
+   </property>
+   <property name="autoFillBackground">
+    <bool>false</bool>
+   </property>
+   <property name="title">
+    <string>Restore</string>
+   </property>
+   <property name="subTitle">
+    <string>Select a Client.</string>
+   </property>
+  </widget>
+  <widget class="JobSelectWizardPage" name="RestWizBackupSelectPage">
+   <property name="title">
+    <string>Restore</string>
+   </property>
+   <property name="subTitle">
+    <string>Select Backup to restore.</string>
+   </property>
+  </widget>
+  <widget class="FileSelectWizardPage" name="RestWiFileSelectionPage">
+   <property name="title">
+    <string>Restore</string>
+   </property>
+   <property name="subTitle">
+    <string>Files Selection</string>
+   </property>
+  </widget>
+  <widget class="RestoreOptionsWizardPage" name="RestWizAdvancedOptionsPage">
+   <property name="title">
+    <string>Restore Options</string>
+   </property>
+   <property name="subTitle">
+    <string>Select advanced options for restore and plugins </string>
+   </property>
+  </widget>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>ClientSelectWizardPage</class>
+   <extends>QWizardPage</extends>
+   <header>clientselectwizardpage.h</header>
+   <container>1</container>
+  </customwidget>
+  <customwidget>
+   <class>JobSelectWizardPage</class>
+   <extends>QWizardPage</extends>
+   <header>jobselectwizardpage.h</header>
+   <container>1</container>
+  </customwidget>
+  <customwidget>
+   <class>FileSelectWizardPage</class>
+   <extends>QWizardPage</extends>
+   <header>fileselectwizardpage.h</header>
+   <container>1</container>
+  </customwidget>
+  <customwidget>
+   <class>RestoreOptionsWizardPage</class>
+   <extends>QWizardPage</extends>
+   <header location="global">restoreoptionswizardpage.h</header>
+   <container>1</container>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>RestWizClientPage</sender>
+   <signal>completeChanged()</signal>
+   <receiver>RestoreWizard</receiver>
+   <slot>setFocus()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>424</x>
+     <y>288</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>424</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
index 911ba2f3df28cc44bad46e325a5e47ed690316db..1a53ec3b455fe6df95e41388d08fa65016254e16 100644 (file)
 
 #include "task.h"
 #include "jcr.h"
+#include "filesmodel.h"
+
 #define dbglvl 10
 int authenticate_daemon(JCR *jcr, MONITOR *monitor, RESMON *res);
 
 static void *handle_task(void *data)
 {
-   task   *t;
-   worker *wrk = (worker *)data;
-   lmgr_init_thread();
-   
-   wrk->set_running();
-   Dmsg0(dbglvl, "Worker started\n");
-
-   while (!wrk->is_quit_state()) {
-      if (wrk->is_wait_state()) {
-         wrk->wait();
-         continue;
-      }   
-      t = (task *)wrk->dequeue();
-      if (!t) {
-         continue;
-      }
-      /* Do the work */
-      switch(t->type) {
-      case TASK_STATUS:
-         t->do_status();
-         break;
-      case TASK_RESOURCES:
-         t->get_resources();
-         break;
-      case TASK_DEFAULTS:
-         t->get_job_defaults();
-         break;
-      case TASK_RUN:
-         t->run_job();
-         break;
-      case TASK_BWLIMIT:
-         t->set_bandwidth();
-         break;
-      case TASK_INFO:
-         t->get_job_info(t->arg2);
-         break;
-      case TASK_DISCONNECT:
-         t->disconnect_bacula();
-         t->mark_as_done();
-         break;
-      default:
-         Mmsg(t->errmsg, "Unknown task");
-         t->mark_as_failed();
-         break;
-      }
-   }
-   Dmsg0(dbglvl, "Worker stoped\n");
-   lmgr_cleanup_thread();
-   return NULL;
+    task   *t;
+    worker *wrk = (worker *)data;
+    lmgr_init_thread();
+
+    wrk->set_running();
+    Dmsg0(dbglvl, "Worker started\n");
+
+    while (!wrk->is_quit_state()) {
+        if (wrk->is_wait_state()) {
+            wrk->wait();
+            continue;
+        }
+        t = (task *)wrk->dequeue();
+        if (!t) {
+            continue;
+        }
+        /* Do the work */
+        switch(t->type) {
+        case TASK_STATUS:
+            t->do_status();
+            break;
+        case TASK_RESOURCES:
+            t->get_resources();
+            break;
+        case TASK_DEFAULTS:
+            t->get_job_defaults();
+            break;
+        case TASK_RUN:
+            t->run_job();
+            break;
+        case TASK_BWLIMIT:
+            t->set_bandwidth();
+            break;
+        case TASK_INFO:
+            t->get_job_info(t->arg2);
+            break;
+        case TASK_DISCONNECT:
+            t->disconnect_bacula();
+            t->mark_as_done();
+            break;
+        case TASK_LIST_CLIENT_JOBS:
+            t->get_client_jobs(t->arg);
+            break;
+        case TASK_LIST_JOB_FILES:
+            t->get_job_files(t->arg, t->pathId);
+            break;
+        case TASK_RESTORE:
+            t->restore(QString("b21234"));
+            break;
+        default:
+            Mmsg(t->errmsg, "Unknown task");
+            t->mark_as_failed();
+            break;
+        }
+    }
+    Dmsg0(dbglvl, "Worker stoped\n");
+    lmgr_cleanup_thread();
+    return NULL;
 }
 
 bool task::set_bandwidth()
@@ -95,7 +106,7 @@ bool task::set_bandwidth()
    }
 
    if (!res->bs || !res->bs->is_open() || res->bs->is_error()) {
-      if (!connect_bacula()) { 
+      if (!connect_bacula()) {
          mark_as_failed();
          return false;
       }
@@ -247,7 +258,7 @@ bool task::read_status_running(RESMON *r)
    char *start, *end;
    struct s_running_job *item = NULL;
    alist *running_jobs = New(alist(10, owned_by_alist));
-   
+
    while (r->bs->recv() >= -1) {
       if (r->bs->msglen < 0 &&
           r->bs->msglen != BNET_CMD_BEGIN &&
@@ -335,7 +346,7 @@ bool task::read_status_running(RESMON *r)
       if (r->bs->is_error()) {
          Mmsg(errmsg, "Got error on the socket communication line");
          goto bail_out;
-      } 
+      }
    }
    if (item) {
       Dmsg1(dbglvl, "Append item %ld\n", item->JobId);
@@ -491,7 +502,7 @@ bool task::read_status_header(RESMON *r)
          Mmsg(errmsg, "Got error on the socket communication line");
          goto bail_out;
 
-      } 
+      }
       r->last_update = time(NULL);
       r->mutex->unlock();
    }
@@ -625,7 +636,7 @@ bool task::get_job_defaults()
    char *p;
 
    if (!res->bs || !res->bs->is_open() || res->bs->is_error()) {
-      if (!connect_bacula()) { 
+      if (!connect_bacula()) {
          goto bail_out;
       }
    }
@@ -638,6 +649,7 @@ bool task::get_job_defaults()
    bfree_and_null(res->defaults.type);
    bfree_and_null(res->defaults.fileset);
    bfree_and_null(res->defaults.catalog);
+   bfree_and_null(res->defaults.where);
 
    tid = start_thread_timer(NULL, pthread_self(), (uint32_t)120);
    if (res->type == R_CLIENT && !res->proxy_sent) {
@@ -664,29 +676,33 @@ bool task::get_job_defaults()
       }
       *p++ = 0;
       if (strcasecmp(curline, "client") == 0) {
-         res->defaults.client = bstrdup(p);
+          res->defaults.client = bstrdup(p);
 
       } else if (strcasecmp(curline, "pool") == 0) {
-         res->defaults.pool = bstrdup(p);
+          res->defaults.pool = bstrdup(p);
 
       } else if (strcasecmp(curline, "storage") == 0) {
-         res->defaults.storage = bstrdup(p);
+          res->defaults.storage = bstrdup(p);
 
       } else if (strcasecmp(curline, "level") == 0) {
-         res->defaults.level = bstrdup(p);
+          res->defaults.level = bstrdup(p);
 
       } else if (strcasecmp(curline, "type") == 0) {
-         res->defaults.type = bstrdup(p);
+          res->defaults.type = bstrdup(p);
 
       } else if (strcasecmp(curline, "fileset") == 0) {
-         res->defaults.fileset = bstrdup(p);
+          res->defaults.fileset = bstrdup(p);
 
       } else if (strcasecmp(curline, "catalog") == 0) {
-         res->defaults.catalog = bstrdup(p);
+          res->defaults.catalog = bstrdup(p);
 
       } else if (strcasecmp(curline, "priority") == 0) {
-         res->defaults.priority = str_to_uint64(p);
+          res->defaults.priority = str_to_uint64(p);
+
+      } else if (strcasecmp(curline, "where") == 0) {
+          res->defaults.where = bstrdup(p);
       }
+
    }
    ret = true;
 bail_out:
@@ -698,7 +714,7 @@ bail_out:
    } else {
       mark_as_failed();
    }
-   res->mutex->unlock();   
+   res->mutex->unlock();
    return ret;
 }
 
@@ -709,7 +725,7 @@ bool task::get_job_info(const char *level)
    char *p;
 
    if (!res->bs || !res->bs->is_open() || res->bs->is_error()) {
-      if (!connect_bacula()) { 
+      if (!connect_bacula()) {
          goto bail_out;
       }
    }
@@ -784,7 +800,7 @@ bool task::run_job()
    btimer_t *tid = NULL;
 
    if (!res->bs || !res->bs->is_open() || res->bs->is_error()) {
-      if (!connect_bacula()) { 
+      if (!connect_bacula()) {
          goto bail_out;
       }
    }
@@ -820,7 +836,98 @@ bool task::run_job()
    }
    // Close the socket, it's over or we don't want to reuse it
    disconnect_bacula();
-   
+
+bail_out:
+
+   if (tid) {
+      stop_thread_timer(tid);
+   }
+   if (ret) {
+      mark_as_done();
+   } else {
+      mark_as_failed();
+   }
+   return ret;
+}
+
+bool task::get_client_jobs(const char* client)
+{
+   bool ret = false;
+   btimer_t *tid = NULL;
+   int row=0;
+   QStringList headers;
+   headers << tr("JobId") << tr("Job") << tr("Level") << tr("Date") << tr("Files") << tr("Bytes");
+
+   if (!model) {
+       goto bail_out;
+   }
+
+   model->clear();
+   model->setHorizontalHeaderLabels(headers);
+
+   if (!res->bs || !res->bs->is_open() || res->bs->is_error()) {
+      if (!connect_bacula()) {
+         goto bail_out;
+      }
+   }
+
+   tid = start_thread_timer(NULL, pthread_self(), (uint32_t)120);
+
+   if (res->type == R_CLIENT && !res->proxy_sent) {
+      res->proxy_sent = true;
+      res->bs->fsend("proxy\n");
+      while (get_next_line(res)) {
+         if (strncmp(curline, "2000", 4) != 0) {
+            pm_strcpy(errmsg, curline);
+            goto bail_out;
+         }
+         Dmsg2(dbglvl, "<- %d %s\n", res->bs->msglen, curline);
+      }
+   }
+
+   res->bs->fsend(".api 2\n");
+   while (get_next_line(res)) {
+      Dmsg2(dbglvl, "<- %d %s\n", res->bs->msglen, curline);
+   }
+
+   if (res->type == R_DIRECTOR && res->use_setip) {
+      res->bs->fsend("setip\n");
+      while (get_next_line(res)) {
+         Dmsg2(dbglvl, "<- %d %s\n", res->bs->msglen, curline);
+      }
+   }
+
+   res->bs->fsend(".bvfs_get_jobs client=%s\n", client);
+   while (get_next_line(res)) {
+       QString line(curline);
+       QStringList line_lst = line.split(" ", QString::SkipEmptyParts);
+
+       model->setItem(row, 0, new QStandardItem(line_lst[0]));
+
+       model->setItem(row, 1, new QStandardItem(line_lst[3]));
+
+       QDateTime date;
+       date.setTime_t(line_lst[1].toUInt());
+       QStandardItem *dateItem = new QStandardItem();
+       dateItem->setData(date, Qt::DisplayRole);
+       model->setItem(row, 3, dateItem);
+
+       ret = true;
+       ++row;
+   }
+
+   // Close the socket, it's over or we don't want to reuse it
+   disconnect_bacula();
+
+//   // fill extra job info
+//   for (int r=0; r<model->rowCount(); ++r) {
+//       arg = model->item(r, 0)->text().toUtf8();
+//       get_job_info(NULL);
+//       model->setItem(r, 2, new QStandardItem(res->infos.JobLevel));
+//       model->setItem(r, 4, new QStandardItem(res->infos.JobFiles));
+//       model->setItem(r, 5, new QStandardItem(res->infos.JobBytes));
+//   }
+
 bail_out:
 
    if (tid) {
@@ -834,6 +941,381 @@ bail_out:
    return ret;
 }
 
+
+bool task::get_job_files(const char* job, uint64_t pathid)
+{
+    bool ret = false;
+    btimer_t *tid = NULL;
+    QString jobs;
+
+    if (model) {
+        model->clear();
+    }
+
+    if (!res->bs || !res->bs->is_open() || res->bs->is_error()) {
+        if (!connect_bacula()) {
+            goto bail_out;
+        }
+    }
+
+    tid = start_thread_timer(NULL, pthread_self(), (uint32_t)120);
+
+    if (res->type == R_CLIENT && !res->proxy_sent) {
+        res->proxy_sent = true;
+        res->bs->fsend("proxy\n");
+        while (get_next_line(res)) {
+            if (strncmp(curline, "2000", 4) != 0) {
+                pm_strcpy(errmsg, curline);
+                goto bail_out;
+            }
+            Dmsg2(dbglvl, "<- %d %s\n", res->bs->msglen, curline);
+        }
+    }
+
+    if (res->type == R_DIRECTOR && res->use_setip) {
+        res->bs->fsend("setip\n");
+        while (get_next_line(res)) {
+            Dmsg2(dbglvl, "<- %d %s\n", res->bs->msglen, curline);
+        }
+    }
+
+    res->bs->fsend(".api 2\n");
+    while (get_next_line(res)) {
+        Dmsg2(dbglvl, "<- %d %s\n", res->bs->msglen, curline);
+    }
+
+    /* retrieve all job ids*/
+    res->bs->fsend(".bvfs_get_jobids jobid=%s\n", job);
+    while (get_next_line(res)) {
+        jobs = QString(curline);
+        Dmsg2(dbglvl, "<- %d %s\n", res->bs->msglen, curline);
+    }
+
+    /* cache the file set */
+    res->bs->fsend(".bvfs_update jobid=%s\n", jobs.toUtf8());
+    while (get_next_line(res)) {
+        Dmsg2(dbglvl, "<- %d %s\n", res->bs->msglen, curline);
+    }
+
+    if (pathid == 0) {
+        res->bs->fsend(".bvfs_lsdirs jobid=%s path=\"\"\n", jobs.toUtf8());
+    } else {
+        res->bs->fsend(".bvfs_lsdirs jobid=%s pathid=%lld\n", jobs.toUtf8(), pathid);
+    }
+
+    while (get_next_line(res)) {
+        QString line(curline);
+        QStringList line_lst = line.split("\t", QString::KeepEmptyParts);
+        if ((line_lst.size() == 6) && line_lst[5] != ".")
+        {
+            DirectoryItem *d = new DirectoryItem();
+            d->setData(QVariant(line_lst[0]), PathIdRole);
+            d->setData(QVariant(line_lst[1]), FilenameIdRole);
+            d->setData(QVariant(line_lst[2]), FileIdRole);
+            d->setData(QVariant(line_lst[3]), JobIdRole);
+            d->setData(QVariant(line_lst[4]), LStatRole);
+            d->setData(QVariant(line_lst[5]), PathRole);
+            d->setData(QVariant(line_lst[5]), Qt::DisplayRole);
+
+            model->appendRow(d);
+            ret = true;
+        }
+    }
+
+    /* then, request files */
+    if (pathid == 0) {
+        res->bs->fsend(".bvfs_lsfiles jobid=%s path=\"\"\n", jobs.toUtf8());
+    } else {
+        res->bs->fsend(".bvfs_lsfiles jobid=%s pathid=%lld\n", jobs.toUtf8(), pathid);
+    }
+
+    while (get_next_line(res)) {
+        QString line(curline);
+        QStringList line_lst = line.split("\t", QString::SkipEmptyParts);
+        if ((line_lst.size() == 6) && line_lst[5] != ".")
+        {
+            FileItem *f = new FileItem();
+            f->setData(QVariant(line_lst[0]), PathIdRole);
+            f->setData(QVariant(line_lst[1]), FilenameIdRole);
+            f->setData(QVariant(line_lst[2]), FileIdRole);
+            f->setData(QVariant(line_lst[3]), JobIdRole);
+            f->setData(QVariant(line_lst[4]), LStatRole);
+            f->setData(QVariant(line_lst[5]), PathRole);
+            f->setData(QVariant(line_lst[5]), Qt::DisplayRole);
+            model->appendRow(f);
+            ret = true;
+        }
+    }
+
+    // Close the socket, it's over or we don't want to reuse it
+    disconnect_bacula();
+
+bail_out:
+
+    if (tid) {
+        stop_thread_timer(tid);
+    }
+    if (ret) {
+        mark_as_done();
+    } else {
+        mark_as_failed();
+    }
+    return ret;
+}
+
+extern int decode_stat(char *buf, struct stat *statp, int stat_size, int32_t *LinkFI);
+
+bool task::prepare_restore(const QString& tableName)
+{
+    bool ret = false;
+    btimer_t *tid = NULL;
+    QString q;
+    QStringList fileids, jobids, dirids, findexes;
+    struct stat statp;
+    int32_t LinkFI;
+
+    for (int row=0; row < model->rowCount(); ++row) {
+        QModelIndex idx = model->index(row, 0);
+        if (idx.data(TypeRole) == TYPEROLE_FILE) {
+            fileids << idx.data(FileIdRole).toString();
+            jobids << idx.data(JobIdRole).toString();
+            decode_stat(idx.data(LStatRole).toString().toLocal8Bit().data(),
+                        &statp, sizeof(statp), &LinkFI);
+            if (LinkFI) {
+                findexes << idx.data(JobIdRole).toString() + "," + QString().setNum(LinkFI);
+            }
+        } else // TYPEROLE_DIRECTORY
+        {
+            dirids << idx.data(PathIdRole).toString();
+            jobids << idx.data(JobIdRole).toString().split(","); // Can have multiple jobids
+        }
+    }
+
+    fileids.removeDuplicates();
+    jobids.removeDuplicates();
+    dirids.removeDuplicates();
+    findexes.removeDuplicates();
+
+    if (!res->bs || !res->bs->is_open() || res->bs->is_error()) {
+        if (!connect_bacula()) {
+            goto bail_out;
+        }
+    }
+
+    tid = start_thread_timer(NULL, pthread_self(), (uint32_t)120);
+
+    if (res->type == R_CLIENT && !res->proxy_sent) {
+        res->proxy_sent = true;
+        res->bs->fsend("proxy\n");
+        while (get_next_line(res)) {
+            if (strncmp(curline, "2000", 4) != 0) {
+                pm_strcpy(errmsg, curline);
+                goto bail_out;
+            }
+            Dmsg2(dbglvl, "<- %d %s\n", res->bs->msglen, curline);
+        }
+    }
+
+    if (res->type == R_DIRECTOR && res->use_setip) {
+        res->bs->fsend("setip\n");
+        while (get_next_line(res)) {
+            Dmsg2(dbglvl, "<- %d %s\n", res->bs->msglen, curline);
+        }
+    }
+
+    res->bs->fsend(".api 2\n");
+    while (get_next_line(res)) {
+        Dmsg2(dbglvl, "<- %d %s\n", res->bs->msglen, curline);
+    }
+
+    /* retrieve all job ids*/
+    q = QString(".bvfs_restore path=%1 jobid=%2").arg(tableName, jobids.join(","));
+    if (fileids.size() > 0) {
+        q += " fileid=" + fileids.join(",");
+    }
+    if (dirids.size() > 0) {
+        q += " dirid=" + dirids.join(",");
+    }
+    if (findexes.size() > 0) {
+        q += " hardlink=" + findexes.join(",");
+    }
+
+    q += "\n";
+    res->bs->fsend(q.toUtf8());
+    while (get_next_line(res)) {
+        ret = true;
+        Dmsg2(dbglvl, "<- %d %s\n", res->bs->msglen, curline);
+    }
+
+    // Close the socket, it's over or we don't want to reuse it
+    disconnect_bacula();
+
+ bail_out:
+
+    if (tid) {
+       stop_thread_timer(tid);
+    }
+    if (ret) {
+       mark_as_done();
+    } else {
+       mark_as_failed();
+    }
+    return ret;
+}
+
+bool task::run_restore(const QString& tableName)
+{
+    bool ret = false;
+    btimer_t *tid = NULL;
+    QString q;
+
+    if (!res->bs || !res->bs->is_open() || res->bs->is_error()) {
+        if (!connect_bacula()) {
+            goto bail_out;
+        }
+    }
+
+    tid = start_thread_timer(NULL, pthread_self(), (uint32_t)120);
+
+    if (res->type == R_CLIENT && !res->proxy_sent) {
+        res->proxy_sent = true;
+        res->bs->fsend("proxy\n");
+        while (get_next_line(res)) {
+            if (strncmp(curline, "2000", 4) != 0) {
+                pm_strcpy(errmsg, curline);
+                goto bail_out;
+            }
+            Dmsg2(dbglvl, "<- %d %s\n", res->bs->msglen, curline);
+        }
+    }
+
+    if (res->type == R_DIRECTOR && res->use_setip) {
+        res->bs->fsend("setip\n");
+        while (get_next_line(res)) {
+            Dmsg2(dbglvl, "<- %d %s\n", res->bs->msglen, curline);
+        }
+    }
+
+    res->bs->fsend(".api 2\n");
+    while (get_next_line(res)) {
+        Dmsg2(dbglvl, "<- %d %s\n", res->bs->msglen, curline);
+    }
+
+    q = QString("restore client=%1").arg(arg);
+
+    if (arg2) {
+        QString where(arg2);
+        where.replace("\"", "");
+        q += " where=\"" + where + "\"";
+    }
+
+    if (arg3) {
+        QString comment(arg3);
+        comment.replace("\"", " ");
+        q += " comment=\"" + comment+ "\"";
+    }
+
+    q += " file=\"?" + tableName + "\"";
+    q += " done yes\n";
+
+    res->bs->fsend(q.toUtf8());
+    while (get_next_line(res)) {
+        ret = true;
+        // FIXME : report a signal to have a progress feedback
+        Dmsg2(dbglvl, "<- %d %s\n", res->bs->msglen, curline);
+    }
+
+    // Close the socket, it's over or we don't want to reuse it
+    disconnect_bacula();
+
+ bail_out:
+
+    if (tid) {
+       stop_thread_timer(tid);
+    }
+    if (ret) {
+       mark_as_done();
+    } else {
+       mark_as_failed();
+    }
+    return ret;
+}
+
+bool task::clean_restore(const QString& tableName)
+{
+    bool ret = false;
+    btimer_t *tid = NULL;
+    QString q;
+
+    if (!res->bs || !res->bs->is_open() || res->bs->is_error()) {
+        if (!connect_bacula()) {
+            goto bail_out;
+        }
+    }
+
+    tid = start_thread_timer(NULL, pthread_self(), (uint32_t)120);
+
+    if (res->type == R_CLIENT && !res->proxy_sent) {
+        res->proxy_sent = true;
+        res->bs->fsend("proxy\n");
+        while (get_next_line(res)) {
+            if (strncmp(curline, "2000", 4) != 0) {
+                pm_strcpy(errmsg, curline);
+                goto bail_out;
+            }
+            Dmsg2(dbglvl, "<- %d %s\n", res->bs->msglen, curline);
+        }
+    }
+
+    if (res->type == R_DIRECTOR && res->use_setip) {
+        res->bs->fsend("setip\n");
+        while (get_next_line(res)) {
+            Dmsg2(dbglvl, "<- %d %s\n", res->bs->msglen, curline);
+        }
+    }
+
+    res->bs->fsend(".api 2\n");
+    while (get_next_line(res)) {
+        Dmsg2(dbglvl, "<- %d %s\n", res->bs->msglen, curline);
+    }
+
+    q = QString(".bvfs_cleanup path=%1\n").arg(tableName);
+
+    res->bs->fsend(q.toUtf8());
+    while (get_next_line(res)) {
+        ret = true;
+        // FIXME : report a signal to have a progress feedback
+        Dmsg2(dbglvl, "<- %d %s\n", res->bs->msglen, curline);
+    }
+
+    // Close the socket, it's over or we don't want to reuse it
+    disconnect_bacula();
+
+ bail_out:
+
+    if (tid) {
+       stop_thread_timer(tid);
+    }
+    if (ret) {
+       mark_as_done();
+    } else {
+       mark_as_failed();
+    }
+    return ret;
+}
+
+bool task::restore(const QString& tableName)
+{
+    bool ret=prepare_restore(tableName);
+    if (ret) {
+        ret = run_restore(tableName);
+        if (ret) {
+            return clean_restore(tableName);
+        }
+    }
+    return false;
+}
+
 /* Get resources to run a job */
 bool task::get_resources()
 {
@@ -841,7 +1323,7 @@ bool task::get_resources()
    btimer_t *tid = NULL;
 
    if (!res->bs || !res->bs->is_open() || res->bs->is_error()) {
-      if (!connect_bacula()) { 
+      if (!connect_bacula()) {
          goto bail_out;
       }
    }
index 0f529a3c3f076e79ea3d731b5eea601e3043e489..2b9bf6e79bc6fa8b3c977c287d0a70fd085f80b1 100644 (file)
@@ -22,6 +22,7 @@
 
 #include "common.h"
 #include <QtCore/QObject>
+#include <QStandardItemModel>
 #include "tray_conf.h"
 
 enum {
@@ -30,6 +31,8 @@ enum {
    TASK_RESOURCES,
    TASK_QUERY,
    TASK_RUN,
+   TASK_LIST_CLIENT_JOBS,
+   TASK_LIST_JOB_FILES,
    TASK_RESTORE,
    TASK_DEFAULTS,
    TASK_CLOSE,
@@ -44,14 +47,17 @@ class task: public QObject
    Q_OBJECT
 
 public:
-   RESMON   *res;
-   POOLMEM  *errmsg;
-   int       type;
-   bool      status;
-   char     *curline;
-   char     *curend;
-   char     *arg;               /* Argument that can be used by some tasks */
-   char     *arg2;
+   RESMON       *res;
+   POOLMEM      *errmsg;
+   int          type;
+   bool         status;
+   char         *curline;
+   char         *curend;
+   const char   *arg;               /* Argument that can be used by some tasks */
+   const char   *arg2;
+   const char   *arg3;
+   QStandardItemModel   *model;       /* model to fill, depending on context */
+   uint64_t     pathId;
 
    union {
       bool  b;
@@ -94,16 +100,23 @@ public:
    void mark_as_done() {
       status = true;
       emit done(this);
-   };
+   }
    void mark_as_failed() {
       status = false;
       emit done(this);
-   };
+   }
    bool get_resources();
    bool get_next_line(RESMON *res);
    bool get_job_defaults(); /* Look r->defaults.job */
    bool run_job();
    bool get_job_info(const char *level);     /* look r->info */
+   bool get_client_jobs(const char* client);
+   bool get_job_files(const char* job, uint64_t pathId);
+
+   bool prepare_restore(const QString& tableName);
+   bool run_restore(const QString& tableName);
+   bool clean_restore(const QString& tableName);
+   bool restore(const QString& tableName);
 
 signals:
    void done(task *t);
index 8c414a05ad42fa61b68bf3213c25c642f2e0c8b8..265d7e21824da9688e0fc4c31205e10a8a51727e 100644 (file)
@@ -11,7 +11,7 @@
 #
 # CONFIG options for Windows are pulled from win32/qmake.conf
 CONFIG += qt
-#CONFIG += qt debug 
+#CONFIG += qt debug
 
 cross-win32 {
   LIBS       +=  -mwindows -L../../win32/release32 -lbacula
@@ -48,9 +48,8 @@ OBJECTS_DIR  = obj
 UI_DIR       = ui
 
 # Main directory
-HEADERS += tray-monitor.h tray_conf.h tray-ui.h fdstatus.h task.h ../util/fmtwidgetitem.h dirstatus.h conf.h sdstatus.h runjob.h status.h
-SOURCES +=  tray-monitor.cpp tray_conf.cpp fdstatus.cpp task.cpp authenticate.cpp ../util/fmtwidgetitem.cpp dirstatus.cpp sdstatus.cpp conf.cpp runjob.cpp status.cpp common.h
-
-FORMS += fd-monitor.ui dir-monitor.ui sd-monitor.ui main-conf.ui res-conf.ui run.ui
+HEADERS += tray-monitor.h tray_conf.h tray-ui.h fdstatus.h task.h ../util/fmtwidgetitem.h dirstatus.h conf.h sdstatus.h runjob.h status.h restorewizard.h filesmodel.h clientselectwizardpage.h jobselectwizardpage.h fileselectwizardpage.h restoreoptionswizardpage.h
+SOURCES +=  tray-monitor.cpp tray_conf.cpp fdstatus.cpp task.cpp authenticate.cpp ../util/fmtwidgetitem.cpp dirstatus.cpp sdstatus.cpp conf.cpp runjob.cpp status.cpp restorewizard.cpp clientselectwizardpage.cpp jobselectwizardpage.cpp fileselectwizardpage.cpp restoreoptionswizardpage.cpp
+FORMS += fd-monitor.ui dir-monitor.ui sd-monitor.ui main-conf.ui res-conf.ui run.ui restorewizard.ui clientselectwizardpage.ui jobselectwizardpage.ui fileselectwizardpage.ui restoreoptionswizardpage.ui
 
 TRANSLATIONS += ts/tm_fr.ts ts/tm_de.ts ts/tm_ja.ts
index 362fff1b30d42ee84e5b02025a819698c5941d1f..4ac57c2a9d0a6ac66a233da3dc03da7c083c4b0c 100644 (file)
@@ -58,9 +58,8 @@ QMAKE_LIB               = i686-w64-mingw32-ar -ru
 QMAKE_RC                = i686-w64-mingw32-windres
 
 # Main directory
-HEADERS += tray-monitor.h tray_conf.h tray-ui.h fdstatus.h task.h ../util/fmtwidgetitem.h dirstatus.h conf.h sdstatus.h runjob.h status.h
-SOURCES +=  tray-monitor.cpp tray_conf.cpp fdstatus.cpp task.cpp authenticate.cpp ../util/fmtwidgetitem.cpp dirstatus.cpp sdstatus.cpp conf.cpp runjob.cpp status.cpp common.h
-
-FORMS += fd-monitor.ui dir-monitor.ui sd-monitor.ui main-conf.ui res-conf.ui run.ui
+HEADERS += tray-monitor.h tray_conf.h tray-ui.h fdstatus.h task.h ../util/fmtwidgetitem.h dirstatus.h conf.h sdstatus.h runjob.h status.h restorewizard.h filesmodel.h clientselectwizardpage.h jobselectwizardpage.h fileselectwizardpage.h restoreoptionswizardpage.h
+SOURCES +=  tray-monitor.cpp tray_conf.cpp fdstatus.cpp task.cpp authenticate.cpp ../util/fmtwidgetitem.cpp dirstatus.cpp sdstatus.cpp conf.cpp runjob.cpp status.cpp restorewizard.cpp clientselectwizardpage.cpp jobselectwizardpage.cpp fileselectwizardpage.cpp restoreoptionswizardpage.cpp
+FORMS += fd-monitor.ui dir-monitor.ui sd-monitor.ui main-conf.ui res-conf.ui run.ui restorewizard.ui clientselectwizardpage.ui jobselectwizardpage.ui fileselectwizardpage.ui restoreoptionswizardpage.ui
 
 TRANSLATIONS += ts/tm_fr.ts ts/tm_de.ts ts/tm_ja.ts
index 20719ba89078da56fb040a36ac3314a1c159d864..792c6972653a48aacca71416cfc46405a2aba755 100644 (file)
@@ -58,9 +58,8 @@ QMAKE_LIB               = x86_64-w64-mingw32-ar -ru
 QMAKE_RC                = x86_64-w64-mingw32-windres
 
 # Main directory
-HEADERS += tray-monitor.h tray_conf.h tray-ui.h fdstatus.h task.h ../util/fmtwidgetitem.h dirstatus.h conf.h sdstatus.h runjob.h status.h
-SOURCES +=  tray-monitor.cpp tray_conf.cpp fdstatus.cpp task.cpp authenticate.cpp ../util/fmtwidgetitem.cpp dirstatus.cpp sdstatus.cpp conf.cpp runjob.cpp status.cpp common.h
-
-FORMS += fd-monitor.ui dir-monitor.ui sd-monitor.ui main-conf.ui res-conf.ui run.ui
+HEADERS += tray-monitor.h tray_conf.h tray-ui.h fdstatus.h task.h ../util/fmtwidgetitem.h dirstatus.h conf.h sdstatus.h runjob.h status.h restorewizard.h filesmodel.h clientselectwizardpage.h jobselectwizardpage.h fileselectwizardpage.h restoreoptionswizardpage.h
+SOURCES +=  tray-monitor.cpp tray_conf.cpp fdstatus.cpp task.cpp authenticate.cpp ../util/fmtwidgetitem.cpp dirstatus.cpp sdstatus.cpp conf.cpp runjob.cpp status.cpp restorewizard.cpp clientselectwizardpage.cpp jobselectwizardpage.cpp fileselectwizardpage.cpp restoreoptionswizardpage.cpp
+FORMS += fd-monitor.ui dir-monitor.ui sd-monitor.ui main-conf.ui res-conf.ui run.ui restorewizard.ui clientselectwizardpage.ui jobselectwizardpage.ui fileselectwizardpage.ui restoreoptionswizardpage.ui
 
 TRANSLATIONS += ts/tm_fr.ts ts/tm_de.ts ts/tm_ja.ts
index af65c6d8e5bea884adc083f496bae5c8cebc2eaf..1efe2ec30f4e7c1653983cd4cd8d828dbd3bea99 100644 (file)
@@ -49,6 +49,7 @@
 #include "dirstatus.h"
 #include "conf.h"
 #include "runjob.h"
+#include "restorewizard.h"
 
 void display_error(const char *fmt, ...);
 
@@ -67,7 +68,8 @@ public:
     QSpinBox *spinRefresh;
     QTimer *timer;
     bool    have_systray;
-    
+    RestoreWizard *restorewiz;
+
     TrayUI():
     QMainWindow(),
        tabWidget(NULL),
@@ -75,12 +77,13 @@ public:
        tray(NULL),
        spinRefresh(NULL),
        timer(NULL),
-       have_systray(QSystemTrayIcon::isSystemTrayAvailable())
+       have_systray(QSystemTrayIcon::isSystemTrayAvailable()),
+       restorewiz(NULL)
        {
-       };
+       }
 
     ~TrayUI() {
-    };
+    }
     void addTab(RESMON *r)
     {
        QWidget *tab;
@@ -117,7 +120,7 @@ public:
        }
        tabWidget->setUpdatesEnabled(false);
        tabWidget->addTab(tab, t);
-       tabWidget->setUpdatesEnabled(true);
+       tabWidget->setUpdatesEnabled(true);       
     }
     void clearTabs()
     {
@@ -191,6 +194,7 @@ public:
 
         verticalLayout->addLayout(hLayout);
         //QSystemTrayIcon::isSystemTrayAvailable
+
         tray = new QSystemTrayIcon(TrayMonitor);
         QMenu* stmenu = new QMenu(TrayMonitor);
 
@@ -206,17 +210,16 @@ public:
         QAction* actRun = new QAction(QApplication::translate("TrayMonitor",
                                "Run...",
                                 0, QApplication::UnicodeUTF8),TrayMonitor);
-/* Not yet ready
- *      QAction* actRes = new QAction(QApplication::translate("TrayMonitor",
- *                             "Restore...",
- *                              0, QApplication::UnicodeUTF8),TrayMonitor);
- */
+        QAction* actRes = new QAction(QApplication::translate("TrayMonitor",
+                              "Restore...",
+                               0, QApplication::UnicodeUTF8),TrayMonitor);
+
         QAction* actConf = new QAction(QApplication::translate("TrayMonitor",
                                "Configure...",
                                 0, QApplication::UnicodeUTF8),TrayMonitor);
         stmenu->addAction(actShow);
         stmenu->addAction(actRun);
-        //stmenu->addAction(actRes);
+        stmenu->addAction(actRes);
         stmenu->addSeparator();
         stmenu->addAction(actConf);
         stmenu->addSeparator();
@@ -227,7 +230,7 @@ public:
         connect(actRun, SIGNAL(triggered()), this, SLOT(cb_run()));
         connect(actShow, SIGNAL(triggered()), this, SLOT(cb_show()));
         connect(actConf, SIGNAL(triggered()), this, SLOT(cb_conf()));
-        //connect(actRes, SIGNAL(triggered()), this, SLOT(cb_restore()));
+        connect(actRes, SIGNAL(triggered()), this, SLOT(cb_restore()));
         connect(actQuit, SIGNAL(triggered()), this, SLOT(cb_quit()));
         connect(actAbout, SIGNAL(triggered()), this, SLOT(cb_about()));
         connect(spinRefresh, SIGNAL(valueChanged(int)), this, SLOT(cb_refresh(int)));
@@ -367,7 +370,12 @@ private slots:
        if (!dir) {
           return;
        }
+       task *t = new task();
+       connect(t, SIGNAL(done(task *)), this, SLOT(start_restore_wizard(task *)), Qt::QueuedConnection);
+       t->init(dir, TASK_RESOURCES);
+       dir->wrk->queue(t);
     }
+
     void cb_trayIconActivated(QSystemTrayIcon::ActivationReason r) {
        if (r == QSystemTrayIcon::Trigger) {
           cb_show();
@@ -390,13 +398,21 @@ public slots:
     void task_done(task *t) {
        Dmsg0(0, "Task done!\n");
        t->deleteLater();
-    };
+    }
     void run_job(task *t) {
        Dmsg0(0, "Task done!\n");
        RESMON *dir = t->res;
        t->deleteLater();
        new RunJob(dir);
-    };
+    }
+
+    void start_restore_wizard(task *t) {
+        RESMON *dir = t->res;
+        restorewiz = new RestoreWizard(dir);
+        restorewiz->show();
+        t->deleteLater();
+    }
+
 };
 
 
index f2bc4d8d0fe9f1b47fb92475ae554b1a75c47e72..cfd52f056ccf4412bdb009b028208302b0017598 100644 (file)
@@ -30,8 +30,6 @@
 
 #include "common.h"
 
-/* NOTE:  #includes at the end of this file */
-
 /*
  * Resource codes -- they must be sequential for indexing
  */
@@ -141,6 +139,7 @@ struct RESMON {
       char *fileset;
       char *catalog;
       int   priority;
+      char *where;
    } defaults;
 
    /* Information about the job */
@@ -154,6 +153,8 @@ struct RESMON {
    } infos;
 };
 
+Q_DECLARE_METATYPE(RESMON*)
+
 /*
  *   Tray Monitor Resource
  *