]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/qt-console/restore/brestore.cpp
Allow closing of all tabs + close tab clicked
[bacula/bacula] / bacula / src / qt-console / restore / brestore.cpp
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2007-2010 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version three of the GNU Affero General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU Affero General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of Kern Sibbald.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28  
29 /*
30  *
31  *  bRestore Class  (Eric's brestore)
32  *
33  *   Kern Sibbald, January MMVII
34  *
35  */ 
36
37 #include "bat.h"
38 #include "restore.h"
39 #include "util/fmtwidgetitem.h"
40
41 bRestore::bRestore()
42 {
43    m_name = tr("bRestore");
44    m_client = "";
45    setupUi(this);
46    pgInitialize();
47    QTreeWidgetItem* thisitem = mainWin->getFromHash(this);
48    thisitem->setIcon(0, QIcon(QString::fromUtf8(":images/browse.png")));
49    m_populated = false;
50    m_closeable = false;
51    m_current = NULL;
52    RestoreList->setAcceptDrops(true);
53 }
54
55 // Populate client table and job associated
56 void bRestore::setClient()
57 {
58    // Select the same client, don't touch
59    if (m_client == ClientList->currentText()) {
60       return;
61    }
62    m_client = ClientList->currentText();
63    FileList->clearContents();
64    FileRevisions->clearContents();
65    JobList->clear();
66    JobList->setEnabled(true);
67    LocationEntry->clear();
68    m_path = "";
69    m_pathid = 0;
70
71    if (ClientList->currentIndex() < 1) {
72       JobList->setEnabled(false);
73       return;
74    }
75
76    JobList->addItem("Job list for " + m_client);
77
78    QString jobQuery =
79       "SELECT Job.Jobid AS JobId, Job.StartTime AS StartTime,"
80       " Job.Level AS Level,"
81       " Job.Name AS Name"
82       " FROM Job JOIN Client USING (ClientId)"
83       " WHERE"
84       " Job.JobStatus IN ('T','W') AND Job.Type='B' AND"
85       " Client.Name='" + m_client + "' ORDER BY StartTime DESC" ;
86
87    QString job;
88    QStringList results;
89    QStringList fieldlist;
90    if (m_console->sql_cmd(jobQuery, results)) {
91       /* Iterate through the record returned from the query */
92       foreach (QString resultline, results) {
93          //  0       1          2     3
94          // JobId, StartTime, Level, Name
95          fieldlist = resultline.split("\t");
96          job = fieldlist[1] + " " + fieldlist[3] + "(" + fieldlist[2] + ") " + fieldlist[0];
97          JobList->addItem(job, QVariant(fieldlist[0])); // set also private value
98       }
99    }
100 }
101
102 // Compute job associated and update the job cache if needed
103 void bRestore::setJob()
104 {
105    if (JobList->currentIndex() < 1) {
106       FileList->clearContents();
107       FileList->setRowCount(0);
108       FileRevisions->clearContents();
109       FileRevisions->setRowCount(0);
110       return ;
111    }
112    QStringList results;
113    QVariant tmp = JobList->itemData(JobList->currentIndex(), Qt::UserRole);
114
115    m_jobids = tmp.toString();
116    QString cmd = ".bvfs_get_jobids jobid=" + m_jobids;
117    if (MergeChk->checkState() == Qt::Checked) {
118       cmd.append(" all");
119    }
120
121    m_console->dir_cmd(cmd, results);
122
123    if (results.size() < 1) {
124       FileList->clearContents();
125       FileList->setRowCount(0);
126       FileRevisions->clearContents();
127       FileRevisions->setRowCount(0);
128       return;
129    }
130
131    // TODO: Can take some time if the job contains many dirs
132    m_jobids = results.at(0);
133    cmd = ".bvfs_update jobid=" + m_jobids;
134    m_console->dir_cmd(cmd, results);
135
136    Pmsg1(0, "jobids=%s\n", m_jobids.toLocal8Bit().constData());
137
138    displayFiles(m_pathid, QString(""));
139    Pmsg0(000, "update done\n");
140 }
141
142 extern int decode_stat(char *buf, struct stat *statp, int32_t *LinkFI);
143
144 // refresh button with a filter or limit/offset change
145 void bRestore::refreshView()
146 {
147    displayFiles(m_pathid, m_path);
148 }
149
150 void bRestore::displayFiles(int64_t pathid, QString path)
151 {
152    QString arg;
153    QStringList results;
154    QStringList fieldlist;
155    struct stat statp;
156    int32_t LinkFI;
157    int nb = 0;
158    int row = 0;
159    Freeze frz_lst(*FileList); /* disable updating*/
160    Freeze frz_rev(*FileRevisions); /* disable updating*/
161    FileList->clearContents();
162    FileRevisions->clearContents();
163    FileRevisions->setRowCount(0);
164
165    // If we provide pathid, use it (path can be altered by encoding conversion)
166    if (pathid > 0) {
167       arg = " pathid=" + QString().setNum(pathid);
168
169       // Choose .. update current path to parent dir
170       if (path == "..") {
171          if (m_path == "/") {
172             m_path = "";
173          } else {
174             m_path.remove(QRegExp("[^/]+/$"));
175          }
176
177       } else if (path == "/" && m_path == "") {
178          m_path += path;
179
180       } else if (path != "/" && path != ".") {
181          m_path += path;
182       }
183    } else {
184       m_path = path;
185       arg = " path=\"" + m_path + "\"";
186    }
187
188    // If a filter is set, add it to the current query
189    if (FilterEntry->text() != "") {
190       QString tmp = FilterEntry->text();
191       tmp.replace("\"", ".");   // basic escape of "
192       arg += " pattern=\"" + tmp + "\"";
193    }
194
195    LocationEntry->setText(m_path);
196    QString offset = QString().setNum(Offset1Spin->value());
197    QString limit=QString().setNum(Offset2Spin->value() - Offset1Spin->value());
198    QString q = ".bvfs_lsdir jobid=" + m_jobids + arg 
199       + " limit=" + limit + " offset=" + offset ;
200    qDebug() << q;
201    if (m_console->dir_cmd(q, results)) {
202       nb = results.size();
203       FileList->setRowCount(nb);
204       foreach (QString resultline, results) {
205          int col=0;
206          //PathId, FilenameId, fileid, jobid, lstat, path
207          fieldlist = resultline.split("\t");
208          TableItemFormatter item(*FileList, row++);
209          item.setFileType(col++, QString("folder")); // folder or file
210          item.setTextFld(col++, fieldlist.at(5)); // path
211          decode_stat(fieldlist.at(4).toLocal8Bit().data(), 
212                      &statp, &LinkFI);
213          item.setBytesFld(col++, QString().setNum(statp.st_size));
214          item.setDateFld(col++, statp.st_mtime); // date
215          fieldlist.replace(3, m_jobids);      // use current jobids selection
216          // keep original info on the first cel that is never empty
217          item.widget(1)->setData(Qt::UserRole, fieldlist.join("\t"));
218       }
219    }
220
221    results.clear();
222    q = ".bvfs_lsfiles jobid=" + m_jobids + arg
223       + " limit=" + limit + " offset=" + offset ;
224    if (m_console->dir_cmd(q, results)) {
225       FileList->setRowCount(results.size() + nb);
226       foreach (QString resultline, results) {
227          int col=1;            // skip icon
228          //PathId, FilenameId, fileid, jobid, lstat, name
229          fieldlist = resultline.split("\t");
230          TableItemFormatter item(*FileList, row++);
231          item.setTextFld(col++, fieldlist.at(5)); // name
232          decode_stat(fieldlist.at(4).toLocal8Bit().data(), 
233                      &statp, &LinkFI);
234          item.setBytesFld(col++, QString().setNum(statp.st_size));
235          item.setDateFld(col++, statp.st_mtime);
236          // keep original info on the first cel that is never empty
237          item.widget(1)->setData(Qt::UserRole, fieldlist.join("\t")); // keep info
238       }
239    }
240    FileList->verticalHeader()->hide();
241    FileList->resizeColumnsToContents();
242    FileList->resizeRowsToContents();
243    FileList->setEditTriggers(QAbstractItemView::NoEditTriggers);
244 }
245
246 void bRestore::PgSeltreeWidgetClicked()
247 {
248    if(!m_populated) {
249       setupPage();
250    }
251    if (!isOnceDocked()) {
252       dockPage();
253    }
254 }
255
256 // Display all versions of a file for this client
257 void bRestore::displayFileVersion(QString pathid, QString fnid, 
258                                   QString client, QString filename)
259 {
260    int row=0;
261    struct stat statp;
262    int32_t LinkFI;
263    Freeze frz_rev(*FileRevisions); /* disable updating*/
264    FileRevisions->clearContents();
265    
266    QString q = ".bvfs_versions jobid=" + m_jobids +
267       " pathid=" + pathid + 
268       " fnid=" + fnid + 
269       " client=" + client;
270
271    if (VersionsChk->checkState() == Qt::Checked) {
272       q.append(" versions");
273    }
274
275    QStringList results;
276    QStringList fieldlist;
277    QString tmp;
278    if (m_console->dir_cmd(q, results)) {
279       FileRevisions->setRowCount(results.size());
280       foreach (QString resultline, results) {
281          int col=0;
282          // 0        1          2        3      4    5      6        7
283          //PathId, FilenameId, fileid, jobid, lstat, Md5, VolName, Inchanger
284          fieldlist = resultline.split("\t");
285          TableItemFormatter item(*FileRevisions, row++);
286          item.setInChanger(col++, fieldlist.at(7));    // inchanger
287          item.setTextFld(col++, fieldlist.at(6)); // Volume
288          item.setNumericFld(col++, fieldlist.at(3)); // JobId
289          decode_stat(fieldlist.at(4).toLocal8Bit().data(), 
290                      &statp, &LinkFI);
291          item.setBytesFld(col++, QString().setNum(statp.st_size)); // size
292          item.setDateFld(col++, statp.st_mtime); // date
293          item.setTextFld(col++, fieldlist.at(5)); // chksum
294
295          // Adjust the fieldlist for drag&drop
296          fieldlist.removeLast(); // inchanger
297          fieldlist.removeLast(); // volname
298          fieldlist.removeLast(); // md5
299          fieldlist << m_path + filename;
300
301          // keep original info on the first cel that is never empty
302          item.widget(1)->setData(Qt::UserRole, fieldlist.join("\t"));
303       }
304    }
305    FileRevisions->verticalHeader()->hide();
306    FileRevisions->resizeColumnsToContents();
307    FileRevisions->resizeRowsToContents();
308    FileRevisions->setEditTriggers(QAbstractItemView::NoEditTriggers);
309 }
310
311 void bRestore::showInfoForFile(QTableWidgetItem *widget)
312 {
313    m_current = widget;
314    QTableWidgetItem *first = FileList->item(widget->row(), 1);
315    QStringList lst = first->data(Qt::UserRole).toString().split("\t");
316    if (lst.at(1) == "0") {      // no filenameid, should be a path
317       displayFiles(lst.at(0).toLongLong(), lst.at(5));
318    } else {
319       displayFileVersion(lst.at(0), lst.at(1), m_client, lst.at(5));
320    }
321 }
322
323 void bRestore::applyLocation()
324 {
325    displayFiles(0, LocationEntry->text());
326 }
327
328 void bRestore::clearVersions(QTableWidgetItem *item)
329 {
330    if (item != m_current) {
331       FileRevisions->clearContents();
332       FileRevisions->setRowCount(0);
333    }
334    m_current = item ;
335 }
336
337 void bRestore::clearRestoreList()
338 {
339    RestoreList->clearContents();
340    RestoreList->setRowCount(0);
341 }
342
343 void bRestore::runRestore()
344 {
345    bRunRestore *r = new bRunRestore(this);
346    r->setVisible(true);
347 }
348
349 void bRestore::setupPage()
350 {
351    ClientList->addItem("Client list");
352    ClientList->addItems(m_console->client_list);
353    connect(ClientList, SIGNAL(currentIndexChanged(int)), this, SLOT(setClient()));
354    connect(JobList, SIGNAL(currentIndexChanged(int)), this, SLOT(setJob()));
355    connect(FileList, SIGNAL(itemClicked(QTableWidgetItem*)), 
356            this, SLOT(clearVersions(QTableWidgetItem *)));
357    connect(FileList, SIGNAL(itemDoubleClicked(QTableWidgetItem*)), 
358            this, SLOT(showInfoForFile(QTableWidgetItem *)));
359    connect(LocationBp, SIGNAL(pressed()), this, SLOT(applyLocation()));
360    connect(MergeChk, SIGNAL(clicked()), this, SLOT(setJob()));
361    connect(ClearBp, SIGNAL(clicked()), this, SLOT(clearRestoreList()));
362    connect(RestoreBp, SIGNAL(clicked()), this, SLOT(runRestore()));
363    connect(FilterBp, SIGNAL(clicked()), this, SLOT(refreshView()));
364    m_populated = true;
365 }
366
367 bRestore::~bRestore()
368 {
369 }
370
371 // Drag & Drop handling, not so easy...
372 void bRestoreTable::mousePressEvent(QMouseEvent *event)
373 {
374    QTableWidget::mousePressEvent(event);
375
376    if (event->button() == Qt::LeftButton) {
377       dragStartPosition = event->pos();
378    }
379 }
380
381 // This event permits to send set custom data on drag&drop
382 // Don't forget to call original class if we are not interested
383 void bRestoreTable::mouseMoveEvent(QMouseEvent *event)
384 {
385    int lastrow=-1;
386
387    // Look just for drag&drop
388    if (!(event->buttons() & Qt::LeftButton)) {
389       QTableWidget::mouseMoveEvent(event);
390       return;
391    }
392    if ((event->pos() - dragStartPosition).manhattanLength()
393        < QApplication::startDragDistance())
394    {
395       QTableWidget::mouseMoveEvent(event);
396       return;
397    }
398
399    QList<QTableWidgetItem *> lst = selectedItems();
400    qDebug() << this << " selectedItems: " << lst;
401    if (lst.isEmpty()) {
402       return;
403    }
404
405    QDrag *drag = new QDrag(this);
406    QMimeData *mimeData = new QMimeData;
407    for (int i=0; i < lst.size(); i++) {
408       if (lastrow != lst[i]->row()) {
409          lastrow = lst[i]->row();
410          QTableWidgetItem *it = item(lastrow, 1);
411          mimeData->setText(it->data(Qt::UserRole).toString());
412          break;                  // at this time, we do it one by one
413       }
414    }
415    drag->setMimeData(mimeData);
416    drag->exec();
417 }
418
419 // This event is called when the drag item enters in the destination area
420 void bRestoreTable::dragEnterEvent(QDragEnterEvent *event)
421 {
422    if (event->source() == this) {
423       event->ignore();
424       return;
425    }
426    if (event->mimeData()->hasText()) {
427       event->acceptProposedAction();
428    } else {
429       event->ignore();
430    }
431 }
432
433 // It should not be essential to redefine this event, but it
434 // doesn't work if not defined
435 void bRestoreTable::dragMoveEvent(QDragMoveEvent *event)
436 {
437    if (event->mimeData()->hasText()) {
438       event->acceptProposedAction();
439    } else {
440       event->ignore();
441    }
442 }
443
444 // When user releases the button
445 void bRestoreTable::dropEvent(QDropEvent *event)
446 {
447    int col=1;
448    struct stat statp;
449    int32_t LinkFI;
450    if (event->mimeData()->hasText()) {
451       TableItemFormatter item(*this, rowCount());
452       setRowCount(rowCount() + 1);
453       QStringList fields = event->mimeData()->text().split("\t");
454       if (fields.size() != 6) {
455          event->ignore();
456          return;
457       }
458       if (fields.at(1) == "0") {
459          item.setFileType(0, "folder");
460       }
461       item.setTextFld(col++, fields.at(5)); // filename
462       decode_stat(fields.at(4).toLocal8Bit().data(), 
463                   &statp, &LinkFI);
464       item.setBytesFld(col++, QString().setNum(statp.st_size)); // size
465       item.setDateFld(col++, statp.st_mtime); // date
466       item.setNumericFld(col++, fields.at(3)); // jobid
467       item.setNumericFld(col++, fields.at(2)); // fileid
468       // keep original info on the first cel that is never empty
469       item.widget(1)->setData(Qt::UserRole, event->mimeData()->text());
470       event->acceptProposedAction();
471    } else {
472       event->ignore();
473    }
474 }
475
476 // Use File Relocation bp
477 void bRunRestore::UFRcb()
478 {
479    if (UseFileRelocationChk->checkState() == Qt::Checked) {
480       WhereEntry->setEnabled(false);
481       UseRegexpChk->setEnabled(true);
482       if (UseRegexpChk->checkState() == Qt::Checked) {
483          AddSuffixEntry->setEnabled(false);
484          AddPrefixEntry->setEnabled(false);
485          StripPrefixEntry->setEnabled(false);
486          WhereRegexpEntry->setEnabled(true);
487       } else {
488          AddSuffixEntry->setEnabled(true);
489          AddPrefixEntry->setEnabled(true);
490          StripPrefixEntry->setEnabled(true);
491          WhereRegexpEntry->setEnabled(false);
492       }
493    } else {
494       WhereEntry->setEnabled(true);
495       AddSuffixEntry->setEnabled(false);
496       AddPrefixEntry->setEnabled(false);
497       StripPrefixEntry->setEnabled(false);
498       UseRegexpChk->setEnabled(false);
499       WhereRegexpEntry->setEnabled(false);
500    }
501 }
502
503 // Expert mode for file relocation
504 void bRunRestore::useRegexp()
505 {
506    if (UseRegexpChk->checkState() == Qt::Checked) {
507       AddSuffixEntry->setEnabled(false);
508       AddPrefixEntry->setEnabled(false);
509       StripPrefixEntry->setEnabled(false);
510       WhereRegexpEntry->setEnabled(true);
511    } else {
512       AddSuffixEntry->setEnabled(true);
513       AddPrefixEntry->setEnabled(true);
514       StripPrefixEntry->setEnabled(true);
515       WhereRegexpEntry->setEnabled(false);
516    }
517 }
518
519 // Display Form to run the restore job
520 bRunRestore::bRunRestore(bRestore *parent)
521 {
522    brestore = parent;
523    setupUi(this);
524    ClientCb->addItems(parent->console()->client_list);
525    int i = ClientCb->findText(parent->m_client);
526    if (i >= 0) {
527       ClientCb->setCurrentIndex(i);
528    }
529    StorageCb->addItem(QString(""));
530    RestoreCb->addItems(parent->console()->restore_list);
531    WhenEditor->setDateTime(QDateTime::currentDateTime());
532    StorageCb->addItems(parent->console()->storage_list);
533    connect(UseFileRelocationChk, SIGNAL(clicked()), this, SLOT(UFRcb()));
534    connect(UseRegexpChk, SIGNAL(clicked()), this, SLOT(useRegexp()));
535    connect(ActionBp, SIGNAL(accepted()), this, SLOT(computeRestore()));
536    // TODO: handle multiple restore job
537    struct job_defaults jd;
538    if (parent->console()->restore_list.size() > 0) {
539       jd.job_name = parent->console()->restore_list[0];
540       brestore->console()->get_job_defaults(jd);
541       WhereEntry->setText(jd.where);
542    }
543    computeVolumeList();
544 }
545
546 void bRestore::get_info_from_selection(QStringList &fileids, 
547                                        QStringList &jobids,
548                                        QStringList &dirids,
549                                        QStringList &findexes)
550 {
551    struct stat statp;
552    int32_t LinkFI;
553    for (int i=0; i < RestoreList->rowCount(); i++) {
554       QTableWidgetItem *item = RestoreList->item(i, 1);
555       QString data = item->data(Qt::UserRole).toString();
556       QStringList lst = data.split("\t");
557       if (lst.at(1) != "0") {   // skip path
558          fileids << lst.at(2);
559          jobids << lst.at(3);
560          decode_stat(lst.at(4).toLocal8Bit().data(), 
561                      &statp, &LinkFI);
562          if (LinkFI) {
563             findexes << lst.at(3) + "," + QString().setNum(LinkFI);
564          }
565       } else {
566          dirids << lst.at(0);
567          jobids << lst.at(3).split(","); // Can have multiple jobids
568       }
569    }
570    fileids.removeDuplicates();
571    jobids.removeDuplicates();
572    dirids.removeDuplicates();
573    findexes.removeDuplicates();
574 }
575
576 // To compute volume list with directories, query is much slower
577 void bRunRestore::computeVolumeList()
578 {
579    brestore->get_info_from_selection(m_fileids, m_jobids, m_dirids, m_findexes);
580    if (m_fileids.size() == 0) {
581       return;
582    }
583
584    Freeze frz_lst(*TableMedia); /* disable updating*/
585    QString q =
586 " SELECT DISTINCT VolumeName, Enabled, InChanger "
587  " FROM File, "
588   " ( " // -- Get all media from this job 
589      " SELECT MIN(FirstIndex) AS FirstIndex, MAX(LastIndex) AS LastIndex, "
590             " VolumeName, Enabled, Inchanger "
591        " FROM JobMedia JOIN Media USING (MediaId) "
592       " WHERE JobId IN (" + m_jobids.join(",") + ") "
593       " GROUP BY VolumeName,Enabled,InChanger "
594    " ) AS allmedia "
595   " WHERE File.FileId IN (" + m_fileids.join(",") + ") "
596    " AND File.FileIndex >= allmedia.FirstIndex "
597    " AND File.FileIndex <= allmedia.LastIndex ";
598    int row=0;
599    QStringList results;
600    if (brestore->console()->sql_cmd(q, results)) {
601       QStringList fieldlist;
602       TableMedia->setRowCount(results.size());
603       /* Iterate through the record returned from the query */
604       foreach (QString resultline, results) {
605          // 0        1          2 
606          //volname, enabled, inchanger
607          fieldlist = resultline.split("\t");
608          int col=0;
609          TableItemFormatter item(*TableMedia, row++);
610          item.setInChanger(col++, fieldlist.at(2));    // inchanger
611          item.setTextFld(col++, fieldlist.at(0));      // Volume
612       }
613    }
614    TableMedia->verticalHeader()->hide();
615    TableMedia->resizeColumnsToContents();
616    TableMedia->resizeRowsToContents();
617    TableMedia->setEditTriggers(QAbstractItemView::NoEditTriggers);
618 }
619
620 int64_t bRunRestore::runRestore(QString tablename)
621 {
622    QString q;
623    QString tmp;
624
625    tmp = ClientCb->currentText();
626    if (tmp == "") {
627       return 0;
628    }
629    q = "restore client=" + tmp;
630
631    tmp = CommentEntry->text();
632    if (tmp != "") {
633       tmp.replace("\"", " ");
634       q += " comment=\"" + tmp + "\"";
635    }
636
637    tmp = StorageCb->currentText();
638    if (tmp != "") {
639       q += " storage=" + tmp;
640    }
641
642    if (UseFileRelocationChk->checkState() == Qt::Checked) {
643       if (UseRegexpChk->checkState() == Qt::Checked) {
644          tmp = WhereRegexpEntry->text();
645          if (tmp != "") {
646             tmp.replace("\"", "");
647             q += " regexwhere=\"" + tmp + "\"";
648          }
649       } else {
650          QStringList lst;
651          tmp = StripPrefixEntry->text();
652          if (tmp != "") {
653             tmp.replace("\"", "");
654             lst.append("!" + tmp + "!!i");
655          }
656          tmp = AddPrefixEntry->text();
657          if (tmp != "") {
658             tmp.replace("\"", "");
659             lst.append("!^!" + tmp + "!");
660          }
661          tmp = AddSuffixEntry->text();
662          if (tmp != "") {
663             tmp.replace("\"", "");
664             lst.append("!([^/])$!$1" + tmp + "!");
665          }
666          if (lst.size() > 0) {
667             q += " regexwhere=\"" + lst.join(",") + "\"";
668          }
669       }
670    } else {
671       tmp = WhereEntry->text();
672       if (tmp != "") {
673          tmp.replace("\"", "");
674          q += " where=\"" + tmp + "\"";
675       }
676    }
677
678 //   q += " priority=" + tmp.setNum(PrioritySb->value());
679 //   q += " job=\"" + RestoreCb->currentText() + "\"";
680    q += " file=\"?" + tablename + "\"";
681    q += " when=\"" + WhenEditor->dateTime().toString("yyyy-MM-dd hh:mm:ss") + "\"";
682    q += " done yes";
683    
684    qDebug() << q;
685    QStringList results;
686    if (brestore->console()->dir_cmd(q, results)) {
687       foreach (QString resultline, results) {
688          QStringList fieldlist = resultline.split("=");
689          if (fieldlist.size() == 2) {
690             return fieldlist.at(1).toLongLong();
691          }
692       }
693    }
694    return 0;
695 }
696
697 void bRunRestore::computeRestore()
698 {
699    QString q = ".bvfs_restore path=b2123 jobid=" + m_jobids.join(",");
700    if (m_fileids.size() > 0) {
701       q += " fileid=" + m_fileids.join(",");
702    }
703    if (m_dirids.size() > 0) {
704       q += " dirid=" + m_dirids.join(",");
705    }
706    if (m_findexes.size() > 0) {
707       q += " hardlink=" + m_findexes.join(",");
708    }
709    qDebug() << q;
710
711    QStringList results;
712    if (brestore->console()->dir_cmd(q, results)) {
713       if (results.size() == 1 && results[0] == "OK") {
714          qDebug() << "jobid=" << runRestore("b2123");
715          q = ".bvfs_cleanup path=b2123";
716          brestore->console()->dir_cmd(q, results);
717       }
718    }   
719 }