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