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