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