]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/qt-console/restore/restore.cpp
bat: Add pattern filter and make restore to start from brestore
[bacula/bacula] / bacula / src / qt-console / restore / restore.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  *  Restore Class 
31  *
32  *   Kern Sibbald, February MMVII
33  *
34  */ 
35
36 #include "bat.h"
37 #include "restore.h"
38
39 static const int dbglvl = 100;
40
41 restorePage::restorePage(int conn)
42 {
43    Dmsg1(dbglvl, "Construcing restorePage Instance connection %i\n", conn);
44    m_conn = conn;
45    QStringList titles;
46
47    setupUi(this);
48    m_name = tr("Restore Select");
49    pgInitialize();
50    QTreeWidgetItem* thisitem = mainWin->getFromHash(this);
51    thisitem->setIcon(0,QIcon(QString::fromUtf8(":images/restore.png")));
52
53    m_console->notify(m_conn, false);          /* this should already be off */
54
55    connect(fileWidget, SIGNAL(itemDoubleClicked(QTreeWidgetItem*, int)), 
56            this, SLOT(fileDoubleClicked(QTreeWidgetItem *, int)));
57    connect(directoryWidget, SIGNAL(
58            currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)),
59            this, SLOT(directoryItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)));
60    connect(upButton, SIGNAL(pressed()), this, SLOT(upButtonPushed()));
61    connect(markButton, SIGNAL(pressed()), this, SLOT(markButtonPushed()));
62    connect(unmarkButton, SIGNAL(pressed()), this, SLOT(unmarkButtonPushed()));
63    connect(okButton, SIGNAL(pressed()), this, SLOT(okButtonPushed()));
64    connect(cancelButton, SIGNAL(pressed()), this, SLOT(cancelButtonPushed()));
65
66    fileWidget->setContextMenuPolicy(Qt::ActionsContextMenu);
67    fileWidget->addAction(actionMark);
68    fileWidget->addAction(actionUnMark);
69    connect(actionMark, SIGNAL(triggered()), this, SLOT(markButtonPushed()));
70    connect(actionUnMark, SIGNAL(triggered()), this, SLOT(unmarkButtonPushed()));
71
72    setFont(m_console->get_font());
73    m_console->displayToPrompt(m_conn);
74
75    titles << tr("Mark") << tr("File") << tr("Mode") << tr("User") 
76           << tr("Group") << tr("Size") << tr("Date");
77    fileWidget->setHeaderLabels(titles);
78
79    get_cwd();
80
81    readSettings();
82    /* wait was entered from pre-restore 
83     * will exit, but will reenter in fillDirectory */
84    mainWin->waitExit();
85    fillDirectory();
86    dockPage();
87    setCurrent();
88    this->show();
89    if (mainWin->m_miscDebug) Pmsg0(000, "Leave restorePage\n");
90 }
91
92 restorePage::~restorePage()
93 {
94    writeSettings();
95 }
96
97 /*
98  * Fill the fileWidget box with the contents of the current directory
99  */
100 void restorePage::fillDirectory()
101 {
102    mainWin->waitEnter();
103    char modes[20], user[20], group[20], size[20], date[30];
104    char marked[10];
105    int pnl, fnl;
106    POOLMEM *file = get_pool_memory(PM_FNAME);
107    POOLMEM *path = get_pool_memory(PM_FNAME);
108
109    fileWidget->clear();
110    m_console->write_dir(m_conn, "dir", false);
111    QList<QTreeWidgetItem *> treeItemList;
112    QStringList item;
113    m_rx.setPattern("has no children\\.$");
114    bool first = true;
115    while (m_console->read(m_conn) > 0) {
116       char *p = m_console->msg(m_conn);
117       char *l;
118       strip_trailing_junk(p);
119       if (*p == '$' || !*p) { continue; }
120       if (first) {
121          if (m_rx.indexIn(QString(p)) != -1) { continue; }
122          first = false;
123       }
124       l = p;
125       skip_nonspaces(&p);             /* permissions */
126       *p++ = 0;
127       bstrncpy(modes, l, sizeof(modes));
128       skip_spaces(&p);
129       skip_nonspaces(&p);             /* link count */
130       *p++ = 0;
131       skip_spaces(&p);
132       l = p;
133       skip_nonspaces(&p);             /* user */
134       *p++ = 0;
135       skip_spaces(&p);
136       bstrncpy(user, l, sizeof(user));
137       l = p;
138       skip_nonspaces(&p);             /* group */
139       *p++ = 0;
140       bstrncpy(group, l, sizeof(group));
141       skip_spaces(&p);
142       l = p;
143       skip_nonspaces(&p);             /* size */
144       *p++ = 0;
145       bstrncpy(size, l, sizeof(size));
146       skip_spaces(&p);
147       l = p;
148       skip_nonspaces(&p);             /* date/time */
149       skip_spaces(&p);
150       skip_nonspaces(&p);
151       *p++ = 0;
152       bstrncpy(date, l, sizeof(date));
153       skip_spaces(&p);
154       if (*p == '*') {
155          bstrncpy(marked, "*", sizeof(marked));
156          p++;
157       } else {
158          bstrncpy(marked, " ", sizeof(marked));
159       }
160       split_path_and_filename(p, &path, &pnl, &file, &fnl);
161       item.clear();
162       item << "" << file << modes << user << group << size << date;
163       if (item[1].endsWith("/")) {
164          addDirectory(item[1]);
165       }
166       QTreeWidgetItem *ti = new QTreeWidgetItem((QTreeWidget *)0, item);
167       ti->setTextAlignment(5, Qt::AlignRight); /* right align size */
168       if (strcmp(marked, "*") == 0) {
169          ti->setIcon(0, QIcon(QString::fromUtf8(":images/check.png")));
170          ti->setData(0, Qt::UserRole, true);
171       } else {
172          ti->setIcon(0, QIcon(QString::fromUtf8(":images/unchecked.png")));
173          ti->setData(0, Qt::UserRole, false);
174       }
175       treeItemList.append(ti);
176    }
177    fileWidget->clear();
178    fileWidget->insertTopLevelItems(0, treeItemList);
179    for (int i=0; i<7; i++) {
180       fileWidget->resizeColumnToContents(i);
181    }
182
183    free_pool_memory(file);
184    free_pool_memory(path);
185    mainWin->waitExit();
186 }
187
188 /*
189  * Function called from fill directory when a directory is found to see if this
190  * directory exists in the directory pane and then add it to the directory pane
191  */
192 void restorePage::addDirectory(QString &newdirr)
193 {
194    QString newdir = newdirr;
195    QString fullpath = m_cwd + newdirr;
196    bool ok = true;
197    bool windrive = false;
198
199    if (mainWin->m_miscDebug) {
200       QString msg = QString(tr("In addDirectory cwd \"%1\" newdir \"%2\" fullpath \"%3\"\n"))
201                     .arg(m_cwd)
202                     .arg(newdir)
203                     .arg(fullpath);
204       Pmsg0(dbglvl, msg.toUtf8().data());
205    }
206
207    if (isWin32Path(newdir)) {
208       /* this is a windows drive */
209       if (mainWin->m_miscDebug) {
210          Pmsg0(dbglvl, "Found windows drive\n");
211       }
212       windrive = true;
213    }
214    
215    if (windrive) {
216       if (fullpath.left(1) == "/") {
217          fullpath.replace(0, 1, "");           /* strip leading / */
218       }
219       /* If drive and not already in add it */
220       if (fullpath.length() == 3 && !m_dirPaths.contains(fullpath)) {
221          QTreeWidgetItem *item = new QTreeWidgetItem(directoryWidget);
222          item->setIcon(0,QIcon(QString::fromUtf8(":images/folder.png")));
223          item->setText(0, fullpath.toUtf8().data());
224          if (mainWin->m_miscDebug) {
225             Pmsg1(dbglvl, "Pre Inserting %s\n",fullpath.toUtf8().data());
226          }
227          m_dirPaths.insert(fullpath, item);
228          m_dirTreeItems.insert(item, fullpath);
229          directoryWidget->setCurrentItem(NULL);
230       }
231    } else {
232       // Unix add / first if not already there 
233       if (m_dirPaths.empty()) {
234          QTreeWidgetItem *item = new QTreeWidgetItem(directoryWidget);
235          item->setIcon(0,QIcon(QString::fromUtf8(":images/folder.png")));
236             
237          QString text("/");
238          item->setText(0, text.toUtf8().data());
239          if (mainWin->m_miscDebug) {
240             Pmsg1(dbglvl, "Pre Inserting %s\n",text.toUtf8().data());
241          }
242          m_dirPaths.insert(text, item);
243          m_dirTreeItems.insert(item, text);
244       }
245    }
246  
247    /* Does it already exist ?? */
248    if (!m_dirPaths.contains(fullpath)) {
249       QTreeWidgetItem *item = NULL;
250       if (windrive) {
251          /* this is the base widget */
252          item = new QTreeWidgetItem(directoryWidget);
253          item->setText(0, fullpath.toUtf8().data());
254          item->setIcon(0,QIcon(QString::fromUtf8(":images/folder.png")));
255       } else {
256          QTreeWidgetItem *parent = m_dirPaths.value(m_cwd);
257          if (parent) {
258             /* new directories to add */
259             item = new QTreeWidgetItem(parent);
260             item->setText(0, newdir.toUtf8().data());
261             item->setIcon(0,QIcon(QString::fromUtf8(":images/folder.png")));
262             directoryWidget->expandItem(parent);
263          } else {
264             ok = false;
265             if (mainWin->m_miscDebug) {
266                QString msg = QString(tr("In else of if parent cwd \"%1\" newdir \"%2\"\n"))
267                     .arg(m_cwd)
268                     .arg(newdir);
269                Pmsg0(dbglvl, msg.toUtf8().data());
270             }
271          }
272       }
273       /* insert into both forward and reverse hash */
274       if (ok) {
275          if (mainWin->m_miscDebug) {
276             Pmsg1(dbglvl, "Inserting %s\n",fullpath.toUtf8().data());
277          }
278          m_dirPaths.insert(fullpath, item);
279          m_dirTreeItems.insert(item, fullpath);
280       }
281    }
282 }
283
284 /*
285  * Executed when the tree item in the directory pane is changed.  This will
286  * allow us to populate the file pane and make this the cwd.
287  */
288 void restorePage::directoryItemChanged(QTreeWidgetItem *currentitem,
289                                          QTreeWidgetItem * /*previousitem*/)
290 {
291    QString fullpath = m_dirTreeItems.value(currentitem);
292    statusLine->setText("");
293    if (fullpath != "") {
294       cwd(fullpath.toUtf8().data());
295       fillDirectory();
296    }
297 }
298
299 void restorePage::okButtonPushed()
300 {
301    this->hide();
302    m_console->write(m_conn, "done");
303    m_console->notify(m_conn, true);
304    setConsoleCurrent();
305    closeStackPage();
306    mainWin->resetFocus();
307 }
308
309
310 void restorePage::cancelButtonPushed()
311 {
312    this->hide();
313    m_console->write(m_conn, "quit");
314    m_console->displayToPrompt(m_conn);
315    mainWin->set_status(tr("Canceled"));
316    closeStackPage();
317    m_console->notify(m_conn, true);
318    mainWin->resetFocus();
319 }
320
321 void restorePage::fileDoubleClicked(QTreeWidgetItem *item, int column)
322 {
323    char cmd[1000];
324    statusLine->setText("");
325    if (column == 0) {                 /* mark/unmark */
326       mainWin->waitEnter();
327       if (item->data(0, Qt::UserRole).toBool()) {
328          bsnprintf(cmd, sizeof(cmd), "unmark \"%s\"", item->text(1).toUtf8().data());
329          item->setIcon(0, QIcon(QString::fromUtf8(":images/unchecked.png")));
330          item->setData(0, Qt::UserRole, false);
331       } else {
332          bsnprintf(cmd, sizeof(cmd), "mark \"%s\"", item->text(1).toUtf8().data());
333          item->setIcon(0, QIcon(QString::fromUtf8(":images/check.png")));
334          item->setData(0, Qt::UserRole, true);
335       }
336       m_console->write_dir(m_conn, cmd, false);
337       if (m_console->read(m_conn) > 0) {
338          strip_trailing_junk(m_console->msg(m_conn));
339          statusLine->setText(m_console->msg(m_conn));
340       }
341       m_console->displayToPrompt(m_conn);
342       mainWin->waitExit();
343       return;
344    }    
345    /* 
346     * Double clicking other than column 0 means to decend into
347     *  the directory -- or nothing if it is not a directory.
348     */
349    if (item->text(1).endsWith("/")) {
350       QString fullpath = m_cwd + item->text(1);
351       QTreeWidgetItem *item = m_dirPaths.value(fullpath);
352       if (item) {
353          directoryWidget->setCurrentItem(item);
354       } else {
355          QString msg = QString("DoubleClick else of item column %1 fullpath %2\n")
356               .arg(column,10)
357               .arg(fullpath);
358          Pmsg0(dbglvl, msg.toUtf8().data());
359       }
360    }
361 }
362
363 /*
364  * If up button pushed, making the parent tree widget current will call fill
365  * directory.
366  */
367 void restorePage::upButtonPushed()
368 {
369    cwd("..");
370    QTreeWidgetItem *item = m_dirPaths.value(m_cwd);
371    if (item) {
372       directoryWidget->setCurrentItem(item);
373    }
374    statusLine->setText("");
375 }
376
377 /*
378  * Mark selected items
379  */
380 void restorePage::markButtonPushed()
381 {
382    mainWin->waitEnter();
383    QList<QTreeWidgetItem *> treeItemList = fileWidget->selectedItems();
384    QTreeWidgetItem *item;
385    char cmd[1000];
386    int count = 0;
387    statusLine->setText("");
388    foreach (item, treeItemList) {
389       count++;
390       bsnprintf(cmd, sizeof(cmd), "mark \"%s\"", item->text(1).toUtf8().data());
391       item->setIcon(0, QIcon(QString::fromUtf8(":images/check.png")));
392       m_console->write_dir(m_conn, cmd, false);
393       if (m_console->read(m_conn) > 0) {
394          strip_trailing_junk(m_console->msg(m_conn));
395          statusLine->setText(m_console->msg(m_conn));
396       }
397       Dmsg1(dbglvl, "cmd=%s\n", cmd);
398       m_console->discardToPrompt(m_conn);
399    }
400    if (count == 0) {
401       mainWin->set_status("Nothing selected, nothing done");
402       statusLine->setText("Nothing selected, nothing done");
403    }
404    mainWin->waitExit();
405 }
406
407 /*
408  * Unmark selected items
409  */
410 void restorePage::unmarkButtonPushed()
411 {
412    mainWin->waitEnter();
413    QList<QTreeWidgetItem *> treeItemList = fileWidget->selectedItems();
414    QTreeWidgetItem *item;
415    char cmd[1000];
416    int count = 0;
417    statusLine->setText("");
418    foreach (item, treeItemList) {
419       count++;
420       bsnprintf(cmd, sizeof(cmd), "unmark \"%s\"", item->text(1).toUtf8().data());
421       item->setIcon(0, QIcon(QString::fromUtf8(":images/unchecked.png")));
422       m_console->write_dir(m_conn, cmd, false);
423       if (m_console->read(m_conn) > 0) {
424          strip_trailing_junk(m_console->msg(m_conn));
425          statusLine->setText(m_console->msg(m_conn));
426       }
427       Dmsg1(dbglvl, "cmd=%s\n", cmd);
428       m_console->discardToPrompt(m_conn);
429    }
430    if (count == 0) {
431       mainWin->set_status(tr("Nothing selected, nothing done"));
432       statusLine->setText(tr("Nothing selected, nothing done"));
433    }
434    mainWin->waitExit();
435 }
436
437 /*
438  * Change current working directory 
439  */
440 bool restorePage::cwd(const char *dir)
441 {
442    int stat;
443    char cd_cmd[MAXSTRING];
444
445    mainWin->waitEnter();
446    statusLine->setText("");
447    bsnprintf(cd_cmd, sizeof(cd_cmd), "cd \"%s\"", dir);
448    Dmsg2(dbglvl, "dir=%s cmd=%s\n", dir, cd_cmd);
449    m_console->write_dir(m_conn, cd_cmd, false);
450    lineEdit->clear();
451    if ((stat = m_console->read(m_conn)) > 0) {
452       m_cwd = m_console->msg(m_conn);
453       lineEdit->insert(m_cwd);
454       Dmsg2(dbglvl, "cwd=%s msg=%s\n", m_cwd.toUtf8().data(), m_console->msg(m_conn));
455    } else {
456       Dmsg1(dbglvl, "stat=%d\n", stat);
457       QMessageBox::critical(this, "Error", tr("cd command failed"), QMessageBox::Ok);
458    }
459    m_console->discardToPrompt(m_conn);
460    mainWin->waitExit();
461    return true;  /* ***FIXME*** return real status */
462 }
463
464 /*
465  * Return cwd when in tree restore mode 
466  */
467 char *restorePage::get_cwd()
468 {
469    int stat;
470    mainWin->waitEnter();
471    m_console->write_dir(m_conn, ".pwd", false);
472    Dmsg0(dbglvl, "send: .pwd\n");
473    if ((stat = m_console->read(m_conn)) > 0) {
474       m_cwd = m_console->msg(m_conn);
475       Dmsg2(dbglvl, "cwd=%s msg=%s\n", m_cwd.toUtf8().data(), m_console->msg(m_conn));
476    } else {
477       Dmsg1(dbglvl, "Something went wrong read stat=%d\n", stat);
478       QMessageBox::critical(this, "Error", tr(".pwd command failed"), QMessageBox::Ok);
479    }
480    m_console->discardToPrompt(m_conn); 
481    mainWin->waitExit();
482    return m_cwd.toUtf8().data();
483 }
484
485 /*
486  * Save user settings associated with this page
487  */
488 void restorePage::writeSettings()
489 {
490    QSettings settings(m_console->m_dir->name(), "bat");
491    settings.beginGroup("RestorePage");
492    settings.setValue(m_splitText, splitter->saveState());
493    settings.endGroup();
494 }
495
496 /*
497  * Read and restore user settings associated with this page
498  */
499 void restorePage::readSettings()
500 {
501    m_splitText = "splitterSizes_2";
502    QSettings settings(m_console->m_dir->name(), "bat");
503    settings.beginGroup("RestorePage");
504    if (settings.contains(m_splitText)) { 
505       splitter->restoreState(settings.value(m_splitText).toByteArray());
506    }
507    settings.endGroup();
508 }