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