]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/qt-console/restore/brestore.cpp
bat: more work on brestore panel
[bacula/bacula] / bacula / src / qt-console / restore / brestore.cpp
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2007-2009 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 void bRestore::setClient()
55 {
56    Pmsg0(000, "Repopulating client table\n");
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    if (m_console->sql_cmd(jobQuery, results)) {
89       QStringList fieldlist;
90
91       /* Iterate through the record returned from the query */
92       foreach (QString resultline, results) {
93          fieldlist = resultline.split("\t");
94          job = fieldlist[1] + " " + fieldlist[3] + "(" + fieldlist[2] + ") " + fieldlist[0];
95          JobList->addItem(job, QVariant(fieldlist[0]));
96       }
97    }
98 }
99
100
101 void bRestore::setJob()
102 {
103    if (JobList->currentIndex() < 1) {
104       FileList->clearContents();
105       FileList->setRowCount(0);
106       FileRevisions->clearContents();
107       FileRevisions->setRowCount(0);
108       return ;
109    }
110    QStringList results;
111    QVariant tmp = JobList->itemData(JobList->currentIndex(), Qt::UserRole);
112
113    m_jobids = tmp.toString();
114    QString cmd = ".bvfs_get_jobids jobid=" + m_jobids;
115    if (MergeChk->checkState() == Qt::Checked) {
116       cmd.append(" all");
117    }
118
119    m_console->dir_cmd(cmd, results);
120
121    if (results.size() < 1) {
122       FileList->clearContents();
123       FileList->setRowCount(0);
124       FileRevisions->clearContents();
125       FileRevisions->setRowCount(0);
126       return;
127    }
128
129    m_jobids = results.at(0);
130    cmd = ".bvfs_update jobid=" + m_jobids;
131    m_console->dir_cmd(cmd, results);
132
133    Pmsg1(0, "jobids=%s\n", m_jobids.toLocal8Bit().constData());
134
135    displayFiles(m_pathid, QString(""));
136    Pmsg0(000, "update done\n");
137 }
138
139 extern int decode_stat(char *buf, struct stat *statp, int32_t *LinkFI);
140
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;
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 (pathid > 0) {
157       arg = " pathid=" + QString().setNum(pathid);
158
159       if (path == "..") {
160          if (m_path == "/") {
161             m_path = "";
162          } else {
163             m_path.remove(QRegExp("[^/]+/$"));
164          }
165
166       } else if (path == "/" && m_path == "") {
167          m_path += path;
168
169       } else if (path != "/" && path != ".") {
170          m_path += path;
171       }
172    } else {
173       m_path = path;
174       arg = " path=\"" + m_path + "\"";
175    }
176    LocationEntry->setText(m_path);
177    QString offset = QString().setNum(Offset1Spin->value());
178    QString limit=QString().setNum(Offset2Spin->value() - Offset1Spin->value());
179    QString q = ".bvfs_lsdir jobid=" + m_jobids + arg 
180       + " limit=" + limit + " offset=" + offset ;
181    if (m_console->dir_cmd(q, results)) {
182       nb = results.size();
183       FileList->setRowCount(nb);
184       foreach (QString resultline, results) {
185          int col=0;
186          //PathId, FilenameId, fileid, jobid, lstat, path
187          fieldlist = resultline.split("\t");
188          TableItemFormatter item(*FileList, row++);
189          item.setFileType(col++, QString("folder")); // folder or file
190          item.setTextFld(col++, fieldlist.at(5)); // path
191          decode_stat(fieldlist.at(4).toLocal8Bit().data(), 
192                      &statp, &LinkFI);
193          item.setBytesFld(col++, QString().setNum(statp.st_size));
194          item.setDateFld(col++, statp.st_mtime); // date
195          fieldlist.replace(3, m_jobids);      // use current jobids selection
196          item.widget(1)->setData(Qt::UserRole, fieldlist.join("\t")); // keep info
197       }
198    }
199
200    results.clear();
201    q = ".bvfs_lsfiles jobid=" + m_jobids + arg
202       + " limit=" + limit + " offset=" + offset ;
203    if (m_console->dir_cmd(q, results)) {
204       FileList->setRowCount(results.size() + nb);
205       foreach (QString resultline, results) {
206          int col=1;            // skip icon
207          //PathId, FilenameId, fileid, jobid, lstat, name
208          fieldlist = resultline.split("\t");
209          TableItemFormatter item(*FileList, row++);
210          item.setTextFld(col++, fieldlist.at(5)); // name
211          decode_stat(fieldlist.at(4).toLocal8Bit().data(), 
212                      &statp, &LinkFI);
213          item.setBytesFld(col++, QString().setNum(statp.st_size));
214          item.setDateFld(col++, statp.st_mtime);
215          item.widget(1)->setData(Qt::UserRole, fieldlist.join("\t")); // keep info
216       }
217    }
218    FileList->verticalHeader()->hide();
219    FileList->resizeColumnsToContents();
220    FileList->resizeRowsToContents();
221    FileList->setEditTriggers(QAbstractItemView::NoEditTriggers);
222 }
223
224 void bRestore::PgSeltreeWidgetClicked()
225 {
226    if(!m_populated) {
227       setupPage();
228    }
229    if (!isOnceDocked()) {
230       dockPage();
231    }
232 }
233
234 void bRestore::displayFileVersion(QString pathid, QString fnid, 
235                                   QString client, QString filename)
236 {
237    int row=0;
238    struct stat statp;
239    int32_t LinkFI;
240    Freeze frz_rev(*FileRevisions); /* disable updating*/
241    FileRevisions->clearContents();
242    
243    QString q = ".bvfs_versions jobid=" + m_jobids +
244       " pathid=" + pathid + 
245       " fnid=" + fnid + 
246       " client=" + client;
247
248    if (VersionsChk->checkState() == Qt::Checked) {
249       q.append(" versions");
250    }
251
252    QStringList results;
253    QStringList fieldlist;
254    QString tmp;
255    if (m_console->dir_cmd(q, results)) {
256       FileRevisions->setRowCount(results.size());
257       foreach (QString resultline, results) {
258          int col=0;
259          // 0        1          2        3      4    5      6        7
260          //PathId, FilenameId, fileid, jobid, lstat, Md5, VolName, Inchanger
261          fieldlist = resultline.split("\t");
262          TableItemFormatter item(*FileRevisions, row++);
263          item.setInChanger(col++, fieldlist.at(7));    // inchanger
264          item.setTextFld(col++, fieldlist.at(6)); // Volume
265          item.setNumericFld(col++, fieldlist.at(3)); // JobId
266          decode_stat(fieldlist.at(4).toLocal8Bit().data(), 
267                      &statp, &LinkFI);
268          item.setBytesFld(col++, QString().setNum(statp.st_size)); // size
269          item.setDateFld(col++, statp.st_mtime); // date
270          item.setTextFld(col++, fieldlist.at(5)); // chksum
271
272          // Adjust the fieldlist for drag&drop
273          fieldlist.removeLast(); // inchanger
274          fieldlist.removeLast(); // volname
275          fieldlist.removeLast(); // md5
276          fieldlist << m_path + filename;
277          item.widget(1)->setData(Qt::UserRole, fieldlist.join("\t")); // keep info
278       }
279    }
280    FileRevisions->verticalHeader()->hide();
281    FileRevisions->resizeColumnsToContents();
282    FileRevisions->resizeRowsToContents();
283    FileRevisions->setEditTriggers(QAbstractItemView::NoEditTriggers);
284 }
285
286 void bRestore::showInfoForFile(QTableWidgetItem *widget)
287 {
288    m_current = widget;
289    QTableWidgetItem *first = FileList->item(widget->row(), 1);
290    QStringList lst = first->data(Qt::UserRole).toString().split("\t");
291    if (lst.at(1) == "0") {      // no filenameid, should be a path
292       displayFiles(lst.at(0).toLongLong(), lst.at(5));
293    } else {
294       displayFileVersion(lst.at(0), lst.at(1), m_client, lst.at(5));
295    }
296 }
297
298 void bRestore::applyLocation()
299 {
300    displayFiles(0, LocationEntry->text());
301 }
302
303 void bRestore::clearVersions(QTableWidgetItem *item)
304 {
305    if (item != m_current) {
306       FileRevisions->clearContents();
307       FileRevisions->setRowCount(0);
308    }
309    m_current = item ;
310 }
311
312 void bRestore::clearRestoreList()
313 {
314    RestoreList->clearContents();
315    RestoreList->setRowCount(0);
316 }
317
318 void bRestore::runRestore()
319 {
320    bRunRestore *r = new bRunRestore(this);
321    r->setVisible(true);
322 }
323
324 void bRestore::setupPage()
325 {
326    ClientList->addItem("Client list");
327    ClientList->addItems(m_console->client_list);
328    connect(ClientList, SIGNAL(currentIndexChanged(int)), this, SLOT(setClient()));
329    connect(JobList, SIGNAL(currentIndexChanged(int)), this, SLOT(setJob()));
330    connect(FileList, SIGNAL(itemClicked(QTableWidgetItem*)), 
331            this, SLOT(clearVersions(QTableWidgetItem *)));
332    connect(FileList, SIGNAL(itemDoubleClicked(QTableWidgetItem*)), 
333            this, SLOT(showInfoForFile(QTableWidgetItem *)));
334    connect(LocationBp, SIGNAL(pressed()), this, SLOT(applyLocation()));
335    connect(MergeChk, SIGNAL(clicked()), this, SLOT(setJob()));
336    connect(ClearBp, SIGNAL(clicked()), this, SLOT(clearRestoreList()));
337    connect(RestoreBp, SIGNAL(clicked()), this, SLOT(runRestore()));
338    m_populated = true;
339 }
340
341 bRestore::~bRestore()
342 {
343 }
344
345 void bRestoreTable::mousePressEvent(QMouseEvent *event)
346 {
347    QTableWidget::mousePressEvent(event);
348
349    if (event->button() == Qt::LeftButton) {
350       dragStartPosition = event->pos();
351    }
352 }
353
354 // This event permits to send set custom data on drag&drop
355 // Don't forget to call original class if we are not interested
356 void bRestoreTable::mouseMoveEvent(QMouseEvent *event)
357 {
358    int lastrow=-1;
359
360    // Look just for drag&drop
361    if (!(event->buttons() & Qt::LeftButton)) {
362       QTableWidget::mouseMoveEvent(event);
363       return;
364    }
365    if ((event->pos() - dragStartPosition).manhattanLength()
366        < QApplication::startDragDistance())
367    {
368       QTableWidget::mouseMoveEvent(event);
369       return;
370    }
371
372    QList<QTableWidgetItem *> lst = selectedItems();
373    qDebug() << this << " selectedItems: " << lst;
374    if (lst.isEmpty()) {
375       return;
376    }
377
378    QDrag *drag = new QDrag(this);
379    QMimeData *mimeData = new QMimeData;
380    for (int i=0; i < lst.size(); i++) {
381       if (lastrow != lst[i]->row()) {
382          lastrow = lst[i]->row();
383          QTableWidgetItem *it = item(lastrow, 1);
384          mimeData->setText(it->data(Qt::UserRole).toString());
385          break;                  // at this time, we do it one by one
386       }
387    }
388    drag->setMimeData(mimeData);
389    drag->exec();
390 }
391
392 // This event is called when the drag item enters in the destination area
393 void bRestoreTable::dragEnterEvent(QDragEnterEvent *event)
394 {
395    if (event->source() == this) {
396       event->ignore();
397       return;
398    }
399    if (event->mimeData()->hasText()) {
400       event->acceptProposedAction();
401    } else {
402       event->ignore();
403    }
404 }
405
406 // It should not be essential to redefine this event, but it
407 // doesn't work if not defined
408 void bRestoreTable::dragMoveEvent(QDragMoveEvent *event)
409 {
410    if (event->mimeData()->hasText()) {
411       event->acceptProposedAction();
412    } else {
413       event->ignore();
414    }
415 }
416
417 void bRestoreTable::dropEvent(QDropEvent *event)
418 {
419    int col=1;
420    struct stat statp;
421    int32_t LinkFI;
422    if (event->mimeData()->hasText()) {
423       TableItemFormatter item(*this, rowCount());
424       setRowCount(rowCount() + 1);
425       QStringList fields = event->mimeData()->text().split("\t");
426       if (fields.size() != 6) {
427          event->ignore();
428          return;
429       }
430       if (fields.at(1) == "0") {
431          item.setFileType(0, "folder");
432       }
433       item.setTextFld(col++, fields.at(5)); // filename
434       decode_stat(fields.at(4).toLocal8Bit().data(), 
435                   &statp, &LinkFI);
436       item.setBytesFld(col++, QString().setNum(statp.st_size)); // size
437       item.setDateFld(col++, statp.st_mtime); // date
438       item.setNumericFld(col++, fields.at(3)); // jobid
439       item.setNumericFld(col++, fields.at(2)); // fileid
440       item.widget(1)->setData(Qt::UserRole, event->mimeData()->text());
441       event->acceptProposedAction();
442    } else {
443       event->ignore();
444    }
445 }
446
447 void bRunRestore::UFRcb()
448 {
449    if (UseFileRelocationChk->checkState() == Qt::Checked) {
450       WhereEntry->setEnabled(false);
451       UseRegexpChk->setEnabled(true);
452       if (UseRegexpChk->checkState() == Qt::Checked) {
453          AddSuffixEntry->setEnabled(false);
454          AddPrefixEntry->setEnabled(false);
455          StripPrefixEntry->setEnabled(false);
456          WhereRegexpEntry->setEnabled(true);
457       } else {
458          AddSuffixEntry->setEnabled(true);
459          AddPrefixEntry->setEnabled(true);
460          StripPrefixEntry->setEnabled(true);
461          WhereRegexpEntry->setEnabled(false);
462       }
463    } else {
464       WhereEntry->setEnabled(true);
465       AddSuffixEntry->setEnabled(false);
466       AddPrefixEntry->setEnabled(false);
467       StripPrefixEntry->setEnabled(false);
468       UseRegexpChk->setEnabled(false);
469       WhereRegexpEntry->setEnabled(false);
470    }
471 }
472
473 void bRunRestore::useRegexp()
474 {
475    if (UseRegexpChk->checkState() == Qt::Checked) {
476       AddSuffixEntry->setEnabled(false);
477       AddPrefixEntry->setEnabled(false);
478       StripPrefixEntry->setEnabled(false);
479       WhereRegexpEntry->setEnabled(true);
480    } else {
481       AddSuffixEntry->setEnabled(true);
482       AddPrefixEntry->setEnabled(true);
483       StripPrefixEntry->setEnabled(true);
484       WhereRegexpEntry->setEnabled(false);
485    }
486 }
487
488 bRunRestore::bRunRestore(bRestore *parent)
489 {
490    brestore = parent;
491    setupUi(this);
492    ClientCb->addItems(parent->console()->client_list);
493    int i = ClientCb->findText(parent->m_client);
494    if (i >= 0) {
495       ClientCb->setCurrentIndex(i);
496    }
497    StorageCb->addItem(QString(""));
498    RestoreCb->addItems(parent->console()->restore_list);
499    WhenEditor->setDateTime(QDateTime::currentDateTime());
500    StorageCb->addItems(parent->console()->storage_list);
501    connect(UseFileRelocationChk, SIGNAL(clicked()), this, SLOT(UFRcb()));
502    connect(UseRegexpChk, SIGNAL(clicked()), this, SLOT(useRegexp()));
503    connect(ActionBp, SIGNAL(accepted()), this, SLOT(computeRestore()));
504    struct job_defaults jd;
505    jd.job_name = parent->console()->restore_list[0];
506    brestore->console()->get_job_defaults(jd);
507    WhereEntry->setText(jd.where);
508    computeVolumeList();
509 }
510
511 void bRestore::get_info_from_selection(QStringList &fileids, 
512                                        QStringList &jobids,
513                                        QStringList &dirids,
514                                        QStringList &findexes)
515 {
516    struct stat statp;
517    int32_t LinkFI;
518    for (int i=0; i < RestoreList->rowCount(); i++) {
519       QTableWidgetItem *item = RestoreList->item(i, 1);
520       QString data = item->data(Qt::UserRole).toString();
521       QStringList lst = data.split("\t");
522       if (lst.at(1) != "0") {   // skip path
523          fileids << lst.at(2);
524          jobids << lst.at(3);
525          decode_stat(lst.at(4).toLocal8Bit().data(), 
526                      &statp, &LinkFI);
527          if (LinkFI) {
528             findexes << lst.at(3) + "," + QString().setNum(LinkFI);
529          }
530       } else {
531          dirids << lst.at(0);
532          jobids << lst.at(3).split(","); // Can have multiple jobids
533       }
534    }
535    fileids.removeDuplicates();
536    jobids.removeDuplicates();
537    dirids.removeDuplicates();
538    findexes.removeDuplicates();
539    qDebug() << fileids << jobids << dirids << findexes;
540 }
541
542 void bRunRestore::computeVolumeList()
543 {
544    brestore->get_info_from_selection(m_fileids, m_jobids, m_dirids, m_findexes);
545    if (m_fileids.size() == 0) {
546       return;
547    }
548
549    Freeze frz_lst(*TableMedia); /* disable updating*/
550    QString q =
551 " SELECT DISTINCT VolumeName, Enabled, InChanger "
552  " FROM File, "
553   " ( " // -- Get all media from this job 
554      " SELECT MIN(FirstIndex) AS FirstIndex, MAX(LastIndex) AS LastIndex, "
555             " VolumeName, Enabled, Inchanger "
556        " FROM JobMedia JOIN Media USING (MediaId) "
557       " WHERE JobId IN (" + m_jobids.join(",") + ") "
558       " GROUP BY VolumeName,Enabled,InChanger "
559    " ) AS allmedia "
560   " WHERE File.FileId IN (" + m_fileids.join(",") + ") "
561    " AND File.FileIndex >= allmedia.FirstIndex "
562    " AND File.FileIndex <= allmedia.LastIndex ";
563    int row=0;
564    QStringList results;
565    if (brestore->console()->sql_cmd(q, results)) {
566       QStringList fieldlist;
567       TableMedia->setRowCount(results.size());
568       /* Iterate through the record returned from the query */
569       foreach (QString resultline, results) {
570          // 0        1          2 
571          //volname, enabled, inchanger
572          fieldlist = resultline.split("\t");
573          int col=0;
574          TableItemFormatter item(*TableMedia, row++);
575          item.setInChanger(col++, fieldlist.at(2));    // inchanger
576          item.setTextFld(col++, fieldlist.at(0)); // Volume
577       }
578    }
579    TableMedia->verticalHeader()->hide();
580    TableMedia->resizeColumnsToContents();
581    TableMedia->resizeRowsToContents();
582    TableMedia->setEditTriggers(QAbstractItemView::NoEditTriggers);
583 }
584
585 void bRunRestore::computeRestore()
586 {
587    QString q = ".bvfs_restore path=b2123 jobid=" + m_jobids.join(",");
588    if (m_fileids.size() > 0) {
589       q += " fileid=" + m_fileids.join(",");
590    }
591    if (m_dirids.size() > 0) {
592       q += " dirid=" + m_dirids.join(",");
593    }
594    if (m_findexes.size() > 0) {
595       q += " hardlink=" + m_findexes.join(",");
596    }
597    qDebug() << q;
598
599    QStringList results;
600    if (brestore->console()->dir_cmd(q, results)) {
601       if (results.size() == 1 && results[0] == "OK") {
602          qDebug() << "Run restore!";
603          q = ".bvfs_cleanup path=b2123";
604          brestore->console()->dir_cmd(q, results);
605       }
606    }   
607 }