2 Bacula® - The Network Backup Solution
4 Copyright (C) 2007-2009 Free Software Foundation Europe e.V.
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
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.
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
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.
31 * bRestore Class (Eric's brestore)
33 * Kern Sibbald, January MMVII
39 #include "util/fmtwidgetitem.h"
43 m_name = tr("bRestore");
47 QTreeWidgetItem* thisitem = mainWin->getFromHash(this);
48 thisitem->setIcon(0, QIcon(QString::fromUtf8(":images/browse.png")));
51 RestoreList->setAcceptDrops(true);
54 void bRestore::setClient()
56 Pmsg0(000, "Repopulating client table\n");
57 // Select the same client, don't touch
58 if (m_client == ClientList->currentText()) {
61 m_client = ClientList->currentText();
62 FileList->clearContents();
63 FileRevisions->clearContents();
65 JobList->setEnabled(true);
66 LocationEntry->clear();
70 if (ClientList->currentIndex() < 1) {
71 JobList->setEnabled(false);
75 JobList->addItem("Job list for " + m_client);
78 "SELECT Job.Jobid AS JobId, Job.StartTime AS StartTime,"
79 " Job.Level AS Level,"
81 " FROM Job JOIN Client USING (ClientId)"
83 " Job.JobStatus IN ('T','W') AND Job.Type='B' AND"
84 " Client.Name='" + m_client + "' ORDER BY StartTime DESC" ;
88 if (m_console->sql_cmd(jobQuery, results)) {
89 QStringList fieldlist;
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]));
101 void bRestore::setJob()
103 if (JobList->currentIndex() < 1) {
104 FileList->clearContents();
105 FileList->setRowCount(0);
106 FileRevisions->clearContents();
107 FileRevisions->setRowCount(0);
111 QVariant tmp = JobList->itemData(JobList->currentIndex(), Qt::UserRole);
113 m_jobids = tmp.toString();
114 QString cmd = ".bvfs_get_jobids jobid=" + m_jobids;
115 if (MergeChk->checkState() == Qt::Checked) {
119 m_console->dir_cmd(cmd, results);
121 if (results.size() < 1) {
122 FileList->clearContents();
123 FileList->setRowCount(0);
124 FileRevisions->clearContents();
125 FileRevisions->setRowCount(0);
129 m_jobids = results.at(0);
130 cmd = ".bvfs_update jobid=" + m_jobids;
131 m_console->dir_cmd(cmd, results);
133 Pmsg1(0, "jobids=%s\n", m_jobids.toLocal8Bit().constData());
135 displayFiles(m_pathid, QString(""));
136 Pmsg0(000, "update done\n");
139 extern int decode_stat(char *buf, struct stat *statp, int32_t *LinkFI);
141 void bRestore::displayFiles(int64_t pathid, QString path)
145 QStringList fieldlist;
150 Freeze frz_lst(*FileList); /* disable updating*/
151 Freeze frz_rev(*FileRevisions); /* disable updating*/
152 FileList->clearContents();
153 FileRevisions->clearContents();
154 FileRevisions->setRowCount(0);
157 arg = " pathid=" + QString().setNum(pathid);
163 m_path.remove(QRegExp("[^/]+/$"));
166 } else if (path == "/" && m_path == "") {
169 } else if (path != "/" && path != ".") {
174 arg = " path=\"" + m_path + "\"";
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)) {
183 FileList->setRowCount(nb);
184 foreach (QString resultline, results) {
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(),
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
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(),
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
218 FileList->verticalHeader()->hide();
219 FileList->resizeColumnsToContents();
220 FileList->resizeRowsToContents();
221 FileList->setEditTriggers(QAbstractItemView::NoEditTriggers);
224 void bRestore::PgSeltreeWidgetClicked()
229 if (!isOnceDocked()) {
234 void bRestore::displayFileVersion(QString pathid, QString fnid,
235 QString client, QString filename)
240 Freeze frz_rev(*FileRevisions); /* disable updating*/
241 FileRevisions->clearContents();
243 QString q = ".bvfs_versions jobid=" + m_jobids +
244 " pathid=" + pathid +
248 if (VersionsChk->checkState() == Qt::Checked) {
249 q.append(" versions");
253 QStringList fieldlist;
255 if (m_console->dir_cmd(q, results)) {
256 FileRevisions->setRowCount(results.size());
257 foreach (QString resultline, results) {
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(),
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
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
280 FileRevisions->verticalHeader()->hide();
281 FileRevisions->resizeColumnsToContents();
282 FileRevisions->resizeRowsToContents();
283 FileRevisions->setEditTriggers(QAbstractItemView::NoEditTriggers);
286 void bRestore::showInfoForFile(QTableWidgetItem *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));
294 displayFileVersion(lst.at(0), lst.at(1), m_client, lst.at(5));
298 void bRestore::applyLocation()
300 displayFiles(0, LocationEntry->text());
303 void bRestore::clearVersions(QTableWidgetItem *item)
305 if (item != m_current) {
306 FileRevisions->clearContents();
307 FileRevisions->setRowCount(0);
312 void bRestore::clearRestoreList()
314 RestoreList->clearContents();
315 RestoreList->setRowCount(0);
318 void bRestore::runRestore()
320 bRunRestore *r = new bRunRestore(this);
324 void bRestore::setupPage()
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()));
341 bRestore::~bRestore()
345 void bRestoreTable::mousePressEvent(QMouseEvent *event)
347 QTableWidget::mousePressEvent(event);
349 if (event->button() == Qt::LeftButton) {
350 dragStartPosition = event->pos();
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)
360 // Look just for drag&drop
361 if (!(event->buttons() & Qt::LeftButton)) {
362 QTableWidget::mouseMoveEvent(event);
365 if ((event->pos() - dragStartPosition).manhattanLength()
366 < QApplication::startDragDistance())
368 QTableWidget::mouseMoveEvent(event);
372 QList<QTableWidgetItem *> lst = selectedItems();
373 qDebug() << this << " selectedItems: " << lst;
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
388 drag->setMimeData(mimeData);
392 // This event is called when the drag item enters in the destination area
393 void bRestoreTable::dragEnterEvent(QDragEnterEvent *event)
395 if (event->source() == this) {
399 if (event->mimeData()->hasText()) {
400 event->acceptProposedAction();
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)
410 if (event->mimeData()->hasText()) {
411 event->acceptProposedAction();
417 void bRestoreTable::dropEvent(QDropEvent *event)
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) {
430 if (fields.at(1) == "0") {
431 item.setFileType(0, "folder");
433 item.setTextFld(col++, fields.at(5)); // filename
434 decode_stat(fields.at(4).toLocal8Bit().data(),
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();
447 void bRunRestore::UFRcb()
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);
458 AddSuffixEntry->setEnabled(true);
459 AddPrefixEntry->setEnabled(true);
460 StripPrefixEntry->setEnabled(true);
461 WhereRegexpEntry->setEnabled(false);
464 WhereEntry->setEnabled(true);
465 AddSuffixEntry->setEnabled(false);
466 AddPrefixEntry->setEnabled(false);
467 StripPrefixEntry->setEnabled(false);
468 UseRegexpChk->setEnabled(false);
469 WhereRegexpEntry->setEnabled(false);
473 void bRunRestore::useRegexp()
475 if (UseRegexpChk->checkState() == Qt::Checked) {
476 AddSuffixEntry->setEnabled(false);
477 AddPrefixEntry->setEnabled(false);
478 StripPrefixEntry->setEnabled(false);
479 WhereRegexpEntry->setEnabled(true);
481 AddSuffixEntry->setEnabled(true);
482 AddPrefixEntry->setEnabled(true);
483 StripPrefixEntry->setEnabled(true);
484 WhereRegexpEntry->setEnabled(false);
488 bRunRestore::bRunRestore(bRestore *parent)
492 ClientCb->addItems(parent->console()->client_list);
493 int i = ClientCb->findText(parent->m_client);
495 ClientCb->setCurrentIndex(i);
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);
511 void bRestore::get_info_from_selection(QStringList &fileids,
514 QStringList &findexes)
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);
525 decode_stat(lst.at(4).toLocal8Bit().data(),
528 findexes << lst.at(3) + "," + QString().setNum(LinkFI);
532 jobids << lst.at(3).split(","); // Can have multiple jobids
535 fileids.removeDuplicates();
536 jobids.removeDuplicates();
537 dirids.removeDuplicates();
538 findexes.removeDuplicates();
539 qDebug() << fileids << jobids << dirids << findexes;
542 void bRunRestore::computeVolumeList()
544 brestore->get_info_from_selection(m_fileids, m_jobids, m_dirids, m_findexes);
545 if (m_fileids.size() == 0) {
549 Freeze frz_lst(*TableMedia); /* disable updating*/
551 " SELECT DISTINCT VolumeName, Enabled, InChanger "
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 "
560 " WHERE File.FileId IN (" + m_fileids.join(",") + ") "
561 " AND File.FileIndex >= allmedia.FirstIndex "
562 " AND File.FileIndex <= allmedia.LastIndex ";
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) {
571 //volname, enabled, inchanger
572 fieldlist = resultline.split("\t");
574 TableItemFormatter item(*TableMedia, row++);
575 item.setInChanger(col++, fieldlist.at(2)); // inchanger
576 item.setTextFld(col++, fieldlist.at(0)); // Volume
579 TableMedia->verticalHeader()->hide();
580 TableMedia->resizeColumnsToContents();
581 TableMedia->resizeRowsToContents();
582 TableMedia->setEditTriggers(QAbstractItemView::NoEditTriggers);
585 void bRunRestore::computeRestore()
587 QString q = ".bvfs_restore path=b2123 jobid=" + m_jobids.join(",");
588 if (m_fileids.size() > 0) {
589 q += " fileid=" + m_fileids.join(",");
591 if (m_dirids.size() > 0) {
592 q += " dirid=" + m_dirids.join(",");
594 if (m_findexes.size() > 0) {
595 q += " hardlink=" + m_findexes.join(",");
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);