]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/qt-console/restore/brestore.cpp
Require Qt version 4.8.4
[bacula/bacula] / bacula / src / qt-console / restore / brestore.cpp
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2007-2011 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() : Pages()
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, int stat_size, 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    if (mainWin->m_miscDebug) 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          /*
209           * Note, the next line zaps variable "item", probably
210           *   because the input data in fieldlist is bad.
211           */
212          decode_stat(fieldlist.at(4).toLocal8Bit().data(), &statp, sizeof(statp),  &LinkFI);
213          TableItemFormatter item(*FileList, row++);
214          item.setFileType(col++, QString("folder")); // folder or file
215          item.setTextFld(col++, fieldlist.at(5)); // path
216          item.setBytesFld(col++, QString().setNum(statp.st_size));
217          item.setDateFld(col++, statp.st_mtime); // date
218          fieldlist.replace(3, m_jobids);      // use current jobids selection
219          // keep original info on the first cel that is never empty
220          item.widget(1)->setData(Qt::UserRole, fieldlist.join("\t"));
221       }
222    }
223
224    results.clear();
225    q = ".bvfs_lsfiles jobid=" + m_jobids + arg
226       + " limit=" + limit + " offset=" + offset ;
227    if (m_console->dir_cmd(q, results)) {
228       FileList->setRowCount(results.size() + nb);
229       foreach (QString resultline, results) {
230          int col=1;            // skip icon
231          //PathId, FilenameId, fileid, jobid, lstat, name
232          fieldlist = resultline.split("\t");
233          TableItemFormatter item(*FileList, row++);
234          item.setTextFld(col++, fieldlist.at(5)); // name
235          decode_stat(fieldlist.at(4).toLocal8Bit().data(), 
236                      &statp, sizeof(statp), &LinkFI);
237          item.setBytesFld(col++, QString().setNum(statp.st_size));
238          item.setDateFld(col++, statp.st_mtime);
239          // keep original info on the first cel that is never empty
240          item.widget(1)->setData(Qt::UserRole, fieldlist.join("\t")); // keep info
241       }
242    }
243    FileList->verticalHeader()->hide();
244    FileList->resizeColumnsToContents();
245    FileList->resizeRowsToContents();
246    FileList->setEditTriggers(QAbstractItemView::NoEditTriggers);
247 }
248
249 void bRestore::PgSeltreeWidgetClicked()
250 {
251    if(!m_populated) {
252       setupPage();
253    }
254    if (!isOnceDocked()) {
255       dockPage();
256    }
257 }
258
259 // Display all versions of a file for this client
260 void bRestore::displayFileVersion(QString pathid, QString fnid, 
261                                   QString client, QString filename)
262 {
263    int row=0;
264    struct stat statp;
265    int32_t LinkFI;
266    Freeze frz_rev(*FileRevisions); /* disable updating*/
267    FileRevisions->clearContents();
268    
269    QString q = ".bvfs_versions jobid=" + m_jobids +
270       " pathid=" + pathid + 
271       " fnid=" + fnid + 
272       " client=" + client;
273
274    if (VersionsChk->checkState() == Qt::Checked) {
275       q.append(" versions");
276    }
277
278    QStringList results;
279    QStringList fieldlist;
280    QString tmp;
281    if (m_console->dir_cmd(q, results)) {
282       FileRevisions->setRowCount(results.size());
283       foreach (QString resultline, results) {
284          int col=0;
285          // 0        1          2        3      4    5      6        7
286          //PathId, FilenameId, fileid, jobid, lstat, Md5, VolName, Inchanger
287          fieldlist = resultline.split("\t");
288          TableItemFormatter item(*FileRevisions, row++);
289          item.setInChanger(col++, fieldlist.at(7)); // inchanger
290          item.setTextFld(col++, fieldlist.at(6)); // Volume
291          item.setNumericFld(col++, fieldlist.at(3)); // JobId
292          decode_stat(fieldlist.at(4).toLocal8Bit().data(), 
293                      &statp, sizeof(statp), &LinkFI);
294          item.setBytesFld(col++, QString().setNum(statp.st_size)); // size
295          item.setDateFld(col++, statp.st_mtime); // date
296          item.setTextFld(col++, fieldlist.at(5)); // chksum
297
298          // Adjust the fieldlist for drag&drop
299          fieldlist.removeLast(); // inchanger
300          fieldlist.removeLast(); // volname
301          fieldlist.removeLast(); // md5
302          fieldlist << m_path + filename;
303
304          // keep original info on the first cel that is never empty
305          item.widget(1)->setData(Qt::UserRole, fieldlist.join("\t"));
306       }
307    }
308    FileRevisions->verticalHeader()->hide();
309    FileRevisions->resizeColumnsToContents();
310    FileRevisions->resizeRowsToContents();
311    FileRevisions->setEditTriggers(QAbstractItemView::NoEditTriggers);
312 }
313
314 void bRestore::showInfoForFile(QTableWidgetItem *widget)
315 {
316    m_current = widget;
317    QTableWidgetItem *first = FileList->item(widget->row(), 1);
318    QStringList lst = first->data(Qt::UserRole).toString().split("\t");
319    if (lst.at(1) == "0") {      // no filenameid, should be a path
320       displayFiles(lst.at(0).toLongLong(), lst.at(5));
321    } else {
322       displayFileVersion(lst.at(0), lst.at(1), m_client, lst.at(5));
323    }
324 }
325
326 void bRestore::applyLocation()
327 {
328    displayFiles(0, LocationEntry->text());
329 }
330
331 void bRestore::clearVersions(QTableWidgetItem *item)
332 {
333    if (item != m_current) {
334       FileRevisions->clearContents();
335       FileRevisions->setRowCount(0);
336    }
337    m_current = item ;
338 }
339
340 void bRestore::clearRestoreList()
341 {
342    RestoreList->clearContents();
343    RestoreList->setRowCount(0);
344 }
345
346 void bRestore::runRestore()
347 {
348    bRunRestore *r = new bRunRestore(this);
349    r->setVisible(true);
350 }
351
352 void bRestore::setupPage()
353 {
354    ClientList->addItem("Client list");
355    ClientList->addItems(m_console->client_list);
356    connect(ClientList, SIGNAL(currentIndexChanged(int)), this, SLOT(setClient()));
357    connect(JobList, SIGNAL(currentIndexChanged(int)), this, SLOT(setJob()));
358    connect(FileList, SIGNAL(itemClicked(QTableWidgetItem*)), 
359            this, SLOT(clearVersions(QTableWidgetItem *)));
360    connect(FileList, SIGNAL(itemDoubleClicked(QTableWidgetItem*)), 
361            this, SLOT(showInfoForFile(QTableWidgetItem *)));
362    connect(LocationBp, SIGNAL(pressed()), this, SLOT(applyLocation()));
363    connect(MergeChk, SIGNAL(clicked()), this, SLOT(setJob()));
364    connect(ClearBp, SIGNAL(clicked()), this, SLOT(clearRestoreList()));
365    connect(RestoreBp, SIGNAL(clicked()), this, SLOT(runRestore()));
366    connect(FilterBp, SIGNAL(clicked()), this, SLOT(refreshView()));
367    m_populated = true;
368 }
369
370 bRestore::~bRestore()
371 {
372 }
373
374 // Drag & Drop handling, not so easy...
375 void bRestoreTable::mousePressEvent(QMouseEvent *event)
376 {
377    QTableWidget::mousePressEvent(event);
378
379    if (event->button() == Qt::LeftButton) {
380       dragStartPosition = event->pos();
381    }
382 }
383
384 // This event permits to send set custom data on drag&drop
385 // Don't forget to call original class if we are not interested
386 void bRestoreTable::mouseMoveEvent(QMouseEvent *event)
387 {
388    int lastrow=-1;
389
390    // Look just for drag&drop
391    if (!(event->buttons() & Qt::LeftButton)) {
392       QTableWidget::mouseMoveEvent(event);
393       return;
394    }
395    if ((event->pos() - dragStartPosition).manhattanLength()
396        < QApplication::startDragDistance())
397    {
398       QTableWidget::mouseMoveEvent(event);
399       return;
400    }
401
402    QList<QTableWidgetItem *> lst = selectedItems();
403    if (mainWin->m_miscDebug) qDebug() << this << " selectedItems: " << lst;
404    if (lst.isEmpty()) {
405       return;
406    }
407
408    QDrag *drag = new QDrag(this);
409    QMimeData *mimeData = new QMimeData;
410    for (int i=0; i < lst.size(); i++) {
411       if (lastrow != lst[i]->row()) {
412          lastrow = lst[i]->row();
413          QTableWidgetItem *it = item(lastrow, 1);
414          mimeData->setText(it->data(Qt::UserRole).toString());
415          break;                  // at this time, we do it one by one
416       }
417    }
418    drag->setMimeData(mimeData);
419    drag->exec();
420 }
421
422 // This event is called when the drag item enters in the destination area
423 void bRestoreTable::dragEnterEvent(QDragEnterEvent *event)
424 {
425    if (event->source() == this) {
426       event->ignore();
427       return;
428    }
429    if (event->mimeData()->hasText()) {
430       event->acceptProposedAction();
431    } else {
432       event->ignore();
433    }
434 }
435
436 // It should not be essential to redefine this event, but it
437 // doesn't work if not defined
438 void bRestoreTable::dragMoveEvent(QDragMoveEvent *event)
439 {
440    if (event->mimeData()->hasText()) {
441       event->acceptProposedAction();
442    } else {
443       event->ignore();
444    }
445 }
446
447 // When user releases the button
448 void bRestoreTable::dropEvent(QDropEvent *event)
449 {
450    int col=1;
451    struct stat statp;
452    int32_t LinkFI;
453    if (event->mimeData()->hasText()) {
454       TableItemFormatter item(*this, rowCount());
455       setRowCount(rowCount() + 1);
456       QStringList fields = event->mimeData()->text().split("\t");
457       if (fields.size() != 6) {
458          event->ignore();
459          return;
460       }
461       if (fields.at(1) == "0") {
462          item.setFileType(0, "folder");
463       }
464       item.setTextFld(col++, fields.at(5)); // filename
465       decode_stat(fields.at(4).toLocal8Bit().data(), 
466                   &statp, sizeof(statp), &LinkFI);
467       item.setBytesFld(col++, QString().setNum(statp.st_size)); // size
468       item.setDateFld(col++, statp.st_mtime); // date
469       item.setNumericFld(col++, fields.at(3)); // jobid
470       item.setNumericFld(col++, fields.at(2)); // fileid
471       // keep original info on the first cel that is never empty
472       item.widget(1)->setData(Qt::UserRole, event->mimeData()->text());
473       event->acceptProposedAction();
474    } else {
475       event->ignore();
476    }
477 }
478
479 // Use File Relocation bp
480 void bRunRestore::UFRcb()
481 {
482    if (UseFileRelocationChk->checkState() == Qt::Checked) {
483       WhereEntry->setEnabled(false);
484       UseRegexpChk->setEnabled(true);
485       if (UseRegexpChk->checkState() == Qt::Checked) {
486          AddSuffixEntry->setEnabled(false);
487          AddPrefixEntry->setEnabled(false);
488          StripPrefixEntry->setEnabled(false);
489          WhereRegexpEntry->setEnabled(true);
490       } else {
491          AddSuffixEntry->setEnabled(true);
492          AddPrefixEntry->setEnabled(true);
493          StripPrefixEntry->setEnabled(true);
494          WhereRegexpEntry->setEnabled(false);
495       }
496    } else {
497       WhereEntry->setEnabled(true);
498       AddSuffixEntry->setEnabled(false);
499       AddPrefixEntry->setEnabled(false);
500       StripPrefixEntry->setEnabled(false);
501       UseRegexpChk->setEnabled(false);
502       WhereRegexpEntry->setEnabled(false);
503    }
504 }
505
506 // Expert mode for file relocation
507 void bRunRestore::useRegexp()
508 {
509    if (UseRegexpChk->checkState() == Qt::Checked) {
510       AddSuffixEntry->setEnabled(false);
511       AddPrefixEntry->setEnabled(false);
512       StripPrefixEntry->setEnabled(false);
513       WhereRegexpEntry->setEnabled(true);
514    } else {
515       AddSuffixEntry->setEnabled(true);
516       AddPrefixEntry->setEnabled(true);
517       StripPrefixEntry->setEnabled(true);
518       WhereRegexpEntry->setEnabled(false);
519    }
520 }
521
522 // Display Form to run the restore job
523 bRunRestore::bRunRestore(bRestore *parent)
524 {
525    brestore = parent;
526    setupUi(this);
527    ClientCb->addItems(parent->console()->client_list);
528    int i = ClientCb->findText(parent->m_client);
529    if (i >= 0) {
530       ClientCb->setCurrentIndex(i);
531    }
532    StorageCb->addItem(QString(""));
533    RestoreCb->addItems(parent->console()->restore_list);
534    WhenEditor->setDateTime(QDateTime::currentDateTime());
535    StorageCb->addItems(parent->console()->storage_list);
536    connect(UseFileRelocationChk, SIGNAL(clicked()), this, SLOT(UFRcb()));
537    connect(UseRegexpChk, SIGNAL(clicked()), this, SLOT(useRegexp()));
538    connect(ActionBp, SIGNAL(accepted()), this, SLOT(computeRestore()));
539    // TODO: handle multiple restore job
540    struct job_defaults jd;
541    if (parent->console()->restore_list.size() > 0) {
542       jd.job_name = parent->console()->restore_list[0];
543       brestore->console()->get_job_defaults(jd);
544       WhereEntry->setText(jd.where);
545    }
546    computeVolumeList();
547 }
548
549 void bRestore::get_info_from_selection(QStringList &fileids, 
550                                        QStringList &jobids,
551                                        QStringList &dirids,
552                                        QStringList &findexes)
553 {
554    struct stat statp;
555    int32_t LinkFI;
556    for (int i=0; i < RestoreList->rowCount(); i++) {
557       QTableWidgetItem *item = RestoreList->item(i, 1);
558       QString data = item->data(Qt::UserRole).toString();
559       QStringList lst = data.split("\t");
560       if (lst.at(1) != "0") {   // skip path
561          fileids << lst.at(2);
562          jobids << lst.at(3);
563          decode_stat(lst.at(4).toLocal8Bit().data(), 
564                      &statp, sizeof(statp), &LinkFI);
565          if (LinkFI) {
566             findexes << lst.at(3) + "," + QString().setNum(LinkFI);
567          }
568       } else {
569          dirids << lst.at(0);
570          jobids << lst.at(3).split(","); // Can have multiple jobids
571       }
572    }
573    fileids.removeDuplicates();
574    jobids.removeDuplicates();
575    dirids.removeDuplicates();
576    findexes.removeDuplicates();
577 }
578
579 // To compute volume list with directories, query is much slower
580 void bRunRestore::computeVolumeList()
581 {
582    brestore->get_info_from_selection(m_fileids, m_jobids, m_dirids, m_findexes);
583    if (m_fileids.size() == 0) {
584       return;
585    }
586
587    Freeze frz_lst(*TableMedia); /* disable updating*/
588    QString q =
589 " SELECT DISTINCT VolumeName, Enabled, InChanger "
590  " FROM File, "
591   " ( " // -- Get all media from this job 
592      " SELECT MIN(FirstIndex) AS FirstIndex, MAX(LastIndex) AS LastIndex, "
593             " VolumeName, Enabled, Inchanger "
594        " FROM JobMedia JOIN Media USING (MediaId) "
595       " WHERE JobId IN (" + m_jobids.join(",") + ") "
596       " GROUP BY VolumeName,Enabled,InChanger "
597    " ) AS allmedia "
598   " WHERE File.FileId IN (" + m_fileids.join(",") + ") "
599    " AND File.FileIndex >= allmedia.FirstIndex "
600    " AND File.FileIndex <= allmedia.LastIndex ";
601    int row=0;
602    QStringList results;
603    if (brestore->console()->sql_cmd(q, results)) {
604       QStringList fieldlist;
605       TableMedia->setRowCount(results.size());
606       /* Iterate through the record returned from the query */
607       foreach (QString resultline, results) {
608          // 0        1          2 
609          //volname, enabled, inchanger
610          fieldlist = resultline.split("\t");
611          int col=0;
612          TableItemFormatter item(*TableMedia, row++);
613          item.setInChanger(col++, fieldlist.at(2));    // inchanger
614          item.setTextFld(col++, fieldlist.at(0));      // Volume
615       }
616    }
617    TableMedia->verticalHeader()->hide();
618    TableMedia->resizeColumnsToContents();
619    TableMedia->resizeRowsToContents();
620    TableMedia->setEditTriggers(QAbstractItemView::NoEditTriggers);
621 }
622
623 int64_t bRunRestore::runRestore(QString tablename)
624 {
625    QString q;
626    QString tmp;
627
628    tmp = ClientCb->currentText();
629    if (tmp == "") {
630       return 0;
631    }
632    q = "restore client=" + tmp;
633
634    tmp = CommentEntry->text();
635    if (tmp != "") {
636       tmp.replace("\"", " ");
637       q += " comment=\"" + tmp + "\"";
638    }
639
640    tmp = StorageCb->currentText();
641    if (tmp != "") {
642       q += " storage=" + tmp;
643    }
644
645    if (UseFileRelocationChk->checkState() == Qt::Checked) {
646       if (UseRegexpChk->checkState() == Qt::Checked) {
647          tmp = WhereRegexpEntry->text();
648          if (tmp != "") {
649             tmp.replace("\"", "");
650             q += " regexwhere=\"" + tmp + "\"";
651          }
652       } else {
653          QStringList lst;
654          tmp = StripPrefixEntry->text();
655          if (tmp != "") {
656             tmp.replace("\"", "");
657             lst.append("!" + tmp + "!!i");
658          }
659          tmp = AddPrefixEntry->text();
660          if (tmp != "") {
661             tmp.replace("\"", "");
662             lst.append("!^!" + tmp + "!");
663          }
664          tmp = AddSuffixEntry->text();
665          if (tmp != "") {
666             tmp.replace("\"", "");
667             lst.append("!([^/])$!$1" + tmp + "!");
668          }
669          if (lst.size() > 0) {
670             q += " regexwhere=\"" + lst.join(",") + "\"";
671          }
672       }
673    } else {
674       tmp = WhereEntry->text();
675       if (tmp != "") {
676          tmp.replace("\"", "");
677          q += " where=\"" + tmp + "\"";
678       }
679    }
680
681 //   q += " priority=" + tmp.setNum(PrioritySb->value());
682 //   q += " job=\"" + RestoreCb->currentText() + "\"";
683    q += " file=\"?" + tablename + "\"";
684    q += " when=\"" + WhenEditor->dateTime().toString("yyyy-MM-dd hh:mm:ss") + "\"";
685    q += " done yes";
686    
687    if (mainWin->m_miscDebug) qDebug() << q;
688    QStringList results;
689    if (brestore->console()->dir_cmd(q, results)) {
690       foreach (QString resultline, results) {
691          QStringList fieldlist = resultline.split("=");
692          if (fieldlist.size() == 2) {
693             return fieldlist.at(1).toLongLong();
694          }
695       }
696    }
697    return 0;
698 }
699
700 void bRunRestore::computeRestore()
701 {
702    QString q = ".bvfs_restore path=b2123 jobid=" + m_jobids.join(",");
703    if (m_fileids.size() > 0) {
704       q += " fileid=" + m_fileids.join(",");
705    }
706    if (m_dirids.size() > 0) {
707       q += " dirid=" + m_dirids.join(",");
708    }
709    if (m_findexes.size() > 0) {
710       q += " hardlink=" + m_findexes.join(",");
711    }
712    if (mainWin->m_miscDebug) qDebug() << q;
713
714    QStringList results;
715    if (brestore->console()->dir_cmd(q, results)) {
716       if (results.size() == 1 && results[0] == "OK") {
717          int64_t jobid = runRestore("b2123");
718          if (mainWin->m_miscDebug) qDebug() << "jobid=" << jobid;
719          q = ".bvfs_cleanup path=b2123";
720          brestore->console()->dir_cmd(q, results);
721       }
722    }   
723 }