]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/qt-console/restore/brestore.cpp
bat: Add new form to run a restore
[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
178    QString q = ".bvfs_lsdir jobid=" + m_jobids + arg;
179    if (m_console->dir_cmd(q, results)) {
180       nb = results.size();
181       FileList->setRowCount(nb);
182       foreach (QString resultline, results) {
183          int col=0;
184          //PathId, FilenameId, fileid, jobid, lstat, path
185          fieldlist = resultline.split("\t");
186          TableItemFormatter item(*FileList, row++);
187          item.setFileType(col++, QString("folder")); // folder or file
188          item.setTextFld(col++, fieldlist.at(5)); // path
189          decode_stat(fieldlist.at(4).toLocal8Bit().data(), 
190                      &statp, &LinkFI);
191          item.setDateFld(col++, statp.st_mtime); // date
192          fieldlist.replace(3, m_jobids);      // use current jobids selection
193          item.widget(1)->setData(Qt::UserRole, fieldlist.join("\t")); // keep info
194       }
195    }
196
197    results.clear();
198    q = ".bvfs_lsfiles jobid=" + m_jobids + arg;
199    if (m_console->dir_cmd(q, results)) {
200       FileList->setRowCount(results.size() + nb);
201       foreach (QString resultline, results) {
202          int col=1;            // skip icon
203          //PathId, FilenameId, fileid, jobid, lstat, name
204          fieldlist = resultline.split("\t");
205          TableItemFormatter item(*FileList, row++);
206          item.setTextFld(col++, fieldlist.at(5)); // name
207          decode_stat(fieldlist.at(4).toLocal8Bit().data(), 
208                      &statp, &LinkFI);
209          item.setBytesFld(col++, QString().setNum(statp.st_size));
210          item.setDateFld(col++, statp.st_mtime);
211          item.widget(1)->setData(Qt::UserRole, fieldlist.join("\t")); // keep info
212       }
213    }
214    FileList->verticalHeader()->hide();
215    FileList->resizeColumnsToContents();
216    FileList->resizeRowsToContents();
217    FileList->setEditTriggers(QAbstractItemView::NoEditTriggers);
218 }
219
220 void bRestore::PgSeltreeWidgetClicked()
221 {
222    if(!m_populated) {
223       setupPage();
224    }
225    if (!isOnceDocked()) {
226       dockPage();
227    }
228 }
229
230 void bRestore::displayFileVersion(QString pathid, QString fnid, 
231                                   QString client, QString filename)
232 {
233    int row=0;
234    struct stat statp;
235    int32_t LinkFI;
236    Freeze frz_rev(*FileRevisions); /* disable updating*/
237    FileRevisions->clearContents();
238    
239    QString q = ".bvfs_versions jobid=" + m_jobids +
240       " pathid=" + pathid + 
241       " fnid=" + fnid + 
242       " client=" + client;
243
244    if (VersionsChk->checkState() == Qt::Checked) {
245       q.append(" versions");
246    }
247
248    QStringList results;
249    QStringList fieldlist;
250    QString tmp;
251    if (m_console->dir_cmd(q, results)) {
252       FileRevisions->setRowCount(results.size());
253       foreach (QString resultline, results) {
254          int col=0;
255          // 0        1          2        3      4    5      6        7
256          //PathId, FilenameId, fileid, jobid, lstat, Md5, VolName, Inchanger
257          fieldlist = resultline.split("\t");
258          TableItemFormatter item(*FileRevisions, row++);
259          item.setInChanger(col++, fieldlist.at(7));    // inchanger
260          item.setTextFld(col++, fieldlist.at(6)); // Volume
261          item.setNumericFld(col++, fieldlist.at(3)); // JobId
262          decode_stat(fieldlist.at(4).toLocal8Bit().data(), 
263                      &statp, &LinkFI);
264          item.setBytesFld(col++, QString().setNum(statp.st_size)); // size
265          item.setDateFld(col++, statp.st_mtime); // date
266          item.setTextFld(col++, fieldlist.at(5)); // chksum
267
268          // Adjust the fieldlist for drag&drop
269          fieldlist.removeLast(); // inchanger
270          fieldlist.removeLast(); // volname
271          fieldlist.removeLast(); // md5
272          fieldlist << m_path + filename;
273          item.widget(1)->setData(Qt::UserRole, fieldlist.join("\t")); // keep info
274       }
275    }
276    FileRevisions->verticalHeader()->hide();
277    FileRevisions->resizeColumnsToContents();
278    FileRevisions->resizeRowsToContents();
279    FileRevisions->setEditTriggers(QAbstractItemView::NoEditTriggers);
280 }
281
282 void bRestore::showInfoForFile(QTableWidgetItem *widget)
283 {
284    m_current = widget;
285    QTableWidgetItem *first = FileList->item(widget->row(), 1);
286    QStringList lst = first->data(Qt::UserRole).toString().split("\t");
287    if (lst.at(1) == "0") {      // no filenameid, should be a path
288       displayFiles(lst.at(0).toLongLong(), lst.at(5));
289    } else {
290       displayFileVersion(lst.at(0), lst.at(1), m_client, lst.at(5));
291    }
292 }
293
294 void bRestore::applyLocation()
295 {
296    displayFiles(0, LocationEntry->text());
297 }
298
299 void bRestore::clearVersions(QTableWidgetItem *item)
300 {
301    if (item != m_current) {
302       FileRevisions->clearContents();
303       FileRevisions->setRowCount(0);
304    }
305    m_current = item ;
306 }
307
308 void bRestore::clearRestoreList()
309 {
310    RestoreList->clearContents();
311    RestoreList->setRowCount(0);
312 }
313
314 void bRestore::runRestore()
315 {
316    bRunRestore *r = new bRunRestore(this);
317    r->setVisible(true);
318 }
319
320 void bRestore::setupPage()
321 {
322    ClientList->addItem("Client list");
323    ClientList->addItems(m_console->client_list);
324    connect(ClientList, SIGNAL(currentIndexChanged(int)), this, SLOT(setClient()));
325    connect(JobList, SIGNAL(currentIndexChanged(int)), this, SLOT(setJob()));
326    connect(FileList, SIGNAL(itemClicked(QTableWidgetItem*)), 
327            this, SLOT(clearVersions(QTableWidgetItem *)));
328    connect(FileList, SIGNAL(itemDoubleClicked(QTableWidgetItem*)), 
329            this, SLOT(showInfoForFile(QTableWidgetItem *)));
330    connect(LocationBp, SIGNAL(pressed()), this, SLOT(applyLocation()));
331    connect(MergeChk, SIGNAL(clicked()), this, SLOT(setJob()));
332    connect(ClearBp, SIGNAL(clicked()), this, SLOT(clearRestoreList()));
333    connect(RestoreBp, SIGNAL(clicked()), this, SLOT(runRestore()));
334    m_populated = true;
335 }
336
337 bRestore::~bRestore()
338 {
339 }
340
341 void bRestoreTable::mousePressEvent(QMouseEvent *event)
342 {
343    QTableWidget::mousePressEvent(event);
344
345    if (event->button() == Qt::LeftButton) {
346       dragStartPosition = event->pos();
347    }
348 }
349
350 // This event permits to send set custom data on drag&drop
351 // Don't forget to call original class if we are not interested
352 void bRestoreTable::mouseMoveEvent(QMouseEvent *event)
353 {
354    int lastrow=-1;
355
356    // Look just for drag&drop
357    if (!(event->buttons() & Qt::LeftButton)) {
358       QTableWidget::mouseMoveEvent(event);
359       return;
360    }
361    if ((event->pos() - dragStartPosition).manhattanLength()
362        < QApplication::startDragDistance())
363    {
364       QTableWidget::mouseMoveEvent(event);
365       return;
366    }
367
368    QList<QTableWidgetItem *> lst = selectedItems();
369    qDebug() << this << " selectedItems: " << lst;
370    if (lst.isEmpty()) {
371       return;
372    }
373
374    QDrag *drag = new QDrag(this);
375    QMimeData *mimeData = new QMimeData;
376    for (int i=0; i < lst.size(); i++) {
377       if (lastrow != lst[i]->row()) {
378          lastrow = lst[i]->row();
379          QTableWidgetItem *it = item(lastrow, 1);
380          mimeData->setText(it->data(Qt::UserRole).toString());
381          break;                  // at this time, we do it one by one
382       }
383    }
384    drag->setMimeData(mimeData);
385    drag->exec();
386 }
387
388 // This event is called when the drag item enters in the destination area
389 void bRestoreTable::dragEnterEvent(QDragEnterEvent *event)
390 {
391    if (event->source() == this) {
392       event->ignore();
393       return;
394    }
395    if (event->mimeData()->hasText()) {
396       event->acceptProposedAction();
397    } else {
398       event->ignore();
399    }
400 }
401
402 // It should not be essential to redefine this event, but it
403 // doesn't work if not defined
404 void bRestoreTable::dragMoveEvent(QDragMoveEvent *event)
405 {
406    if (event->mimeData()->hasText()) {
407       event->acceptProposedAction();
408    } else {
409       event->ignore();
410    }
411 }
412
413 void bRestoreTable::dropEvent(QDropEvent *event)
414 {
415    int col=1;
416    struct stat statp;
417    int32_t LinkFI;
418    if (event->mimeData()->hasText()) {
419       TableItemFormatter item(*this, rowCount());
420       setRowCount(rowCount() + 1);
421       QStringList fields = event->mimeData()->text().split("\t");
422       if (fields.size() != 6) {
423          event->ignore();
424          return;
425       }
426       if (fields.at(1) == "0") {
427          item.setFileType(0, "folder");
428       }
429       item.setTextFld(col++, fields.at(5)); // filename
430       decode_stat(fields.at(4).toLocal8Bit().data(), 
431                   &statp, &LinkFI);
432       item.setBytesFld(col++, QString().setNum(statp.st_size)); // size
433       item.setDateFld(col++, statp.st_mtime); // date
434       item.setNumericFld(col++, fields.at(3)); // jobid
435       item.setNumericFld(col++, fields.at(2)); // fileid
436       item.widget(1)->setData(Qt::UserRole, event->mimeData()->text());
437       event->acceptProposedAction();
438    } else {
439       event->ignore();
440    }
441 }
442
443 void bRunRestore::UFRcb()
444 {
445    if (UseFileRelocationChk->checkState() == Qt::Checked) {
446       WhereEntry->setEnabled(false);
447       UseRegexpChk->setEnabled(true);
448       if (UseRegexpChk->checkState() == Qt::Checked) {
449          AddSuffixEntry->setEnabled(false);
450          AddPrefixEntry->setEnabled(false);
451          StripPrefixEntry->setEnabled(false);
452          WhereRegexpEntry->setEnabled(true);
453       } else {
454          AddSuffixEntry->setEnabled(true);
455          AddPrefixEntry->setEnabled(true);
456          StripPrefixEntry->setEnabled(true);
457          WhereRegexpEntry->setEnabled(false);
458       }
459    } else {
460       WhereEntry->setEnabled(true);
461       AddSuffixEntry->setEnabled(false);
462       AddPrefixEntry->setEnabled(false);
463       StripPrefixEntry->setEnabled(false);
464       UseRegexpChk->setEnabled(false);
465       WhereRegexpEntry->setEnabled(false);
466    }
467 }
468
469 void bRunRestore::useRegexp()
470 {
471    if (UseRegexpChk->checkState() == Qt::Checked) {
472       AddSuffixEntry->setEnabled(false);
473       AddPrefixEntry->setEnabled(false);
474       StripPrefixEntry->setEnabled(false);
475       WhereRegexpEntry->setEnabled(true);
476    } else {
477       AddSuffixEntry->setEnabled(true);
478       AddPrefixEntry->setEnabled(true);
479       StripPrefixEntry->setEnabled(true);
480       WhereRegexpEntry->setEnabled(false);
481    }
482 }
483
484 bRunRestore::bRunRestore(bRestore *parent)
485 {
486    setupUi(this);
487    ClientCb->addItems(parent->console()->client_list);
488    int i = ClientCb->findText(parent->m_client);
489    if (i >= 0) {
490       ClientCb->setCurrentIndex(i);
491    }
492    StorageCb->addItem(QString(""));
493    RestoreCb->addItems(parent->console()->restore_list);
494    WhenEditor->setDateTime(QDateTime::currentDateTime());
495    StorageCb->addItems(parent->console()->storage_list);
496    connect(UseFileRelocationChk, SIGNAL(clicked()), this, SLOT(UFRcb()));
497    connect(UseRegexpChk, SIGNAL(clicked()), this, SLOT(useRegexp()));
498    struct job_defaults jd;
499    jd.job_name = parent->console()->restore_list[0];
500    parent->console()->get_job_defaults(jd);
501    WhereEntry->setText(jd.where);
502 }