]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/qt-console/restore/restore.cpp
b3a5a480dc5f9f4d56ed5685b1970f453ca96451
[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
198    if (mainWin->m_miscDebug) {
199       QString msg = QString(tr("In addDirectory cwd \"%1\" newdir \"%2\" fullpath \"%3\"\n"))
200                     .arg(m_cwd)
201                     .arg(newdir)
202                     .arg(fullpath);
203       Pmsg1(dbglvl, "%s\n", msg.toUtf8().data());
204    }
205
206    if (isWin32Path(fullpath)) {
207       Pmsg0(dbglvl, "Windows drive\n");
208       if (fullpath.left(1) == "/") {
209          fullpath.replace(0, 1, "");           /* strip leading / */
210       }
211       /* If drive and not already in add it */
212       if (fullpath.length() == 3 && !m_dirPaths.contains(fullpath)) {
213          QTreeWidgetItem *item = new QTreeWidgetItem(directoryWidget);
214          item->setIcon(0,QIcon(QString::fromUtf8(":images/folder.png")));
215          item->setText(0, fullpath.toUtf8().data());
216          if (mainWin->m_miscDebug) {
217             Pmsg1(dbglvl, "Pre Inserting %s\n",fullpath.toUtf8().data());
218          }
219          m_dirPaths.insert(fullpath, item);
220          m_dirTreeItems.insert(item, fullpath);
221          directoryWidget->setCurrentItem(NULL);
222       }
223    } else {
224       // Unix add / first if not already there 
225       if (m_dirPaths.empty()) {
226          QTreeWidgetItem *item = new QTreeWidgetItem(directoryWidget);
227          item->setIcon(0,QIcon(QString::fromUtf8(":images/folder.png")));
228             
229          QString text("/");
230          item->setText(0, text.toUtf8().data());
231          if (mainWin->m_miscDebug) {
232             Pmsg1(dbglvl, "Pre Inserting %s\n",text.toUtf8().data());
233          }
234          m_dirPaths.insert(text, item);
235          m_dirTreeItems.insert(item, text);
236       }
237    }
238  
239    /* Does it already exist ?? */
240    if (!m_dirPaths.contains(fullpath)) {
241       QTreeWidgetItem *item = NULL;
242       if (isWin32Path(fullpath)) {
243          /* this is the base widget */
244          item = new QTreeWidgetItem(directoryWidget);
245          item->setText(0, fullpath.toUtf8().data());
246          Pmsg1(dbglvl, "Windows: %s\n", fullpath.toUtf8().data());
247          item->setIcon(0,QIcon(QString::fromUtf8(":images/folder.png")));
248       } else {
249          QTreeWidgetItem *parent = m_dirPaths.value(m_cwd);
250          if (parent) {
251             /* new directories to add */
252             item = new QTreeWidgetItem(parent);
253             item->setText(0, newdir.toUtf8().data());
254             item->setIcon(0,QIcon(QString::fromUtf8(":images/folder.png")));
255             directoryWidget->expandItem(parent);
256             Pmsg1(dbglvl, "%s\n", newdir.toUtf8().data());
257          } else {
258             ok = false;
259             if (mainWin->m_miscDebug) {
260                QString msg = QString(tr("In else of if parent cwd \"%1\" newdir \"%2\"\n"))
261                     .arg(m_cwd)
262                     .arg(newdir);
263                Pmsg1(dbglvl, "%s\n", msg.toUtf8().data());
264             }
265          }
266       }
267       /* insert into both forward and reverse hash */
268       if (ok) {
269          if (mainWin->m_miscDebug) {
270             Pmsg1(dbglvl, "Inserting %s\n",fullpath.toUtf8().data());
271          }
272          m_dirPaths.insert(fullpath, item);
273          m_dirTreeItems.insert(item, fullpath);
274       }
275    }
276 }
277
278 /*
279  * Executed when the tree item in the directory pane is changed.  This will
280  * allow us to populate the file pane and make this the cwd.
281  */
282 void restorePage::directoryItemChanged(QTreeWidgetItem *currentitem,
283                                          QTreeWidgetItem * /*previousitem*/)
284 {
285    QString fullpath = m_dirTreeItems.value(currentitem);
286    statusLine->setText("");
287    if (fullpath != "") {
288       cwd(fullpath.toUtf8().data());
289       fillDirectory();
290    }
291 }
292
293 void restorePage::okButtonPushed()
294 {
295    this->hide();
296    m_console->write(m_conn, "done");
297    m_console->notify(m_conn, true);
298    setConsoleCurrent();
299    closeStackPage();
300    mainWin->resetFocus();
301 }
302
303
304 void restorePage::cancelButtonPushed()
305 {
306    this->hide();
307    m_console->write(m_conn, "quit");
308    m_console->displayToPrompt(m_conn);
309    mainWin->set_status(tr("Canceled"));
310    closeStackPage();
311    m_console->notify(m_conn, true);
312    mainWin->resetFocus();
313 }
314
315 void restorePage::fileDoubleClicked(QTreeWidgetItem *item, int column)
316 {
317    char cmd[1000];
318    statusLine->setText("");
319    if (column == 0) {                 /* mark/unmark */
320       mainWin->waitEnter();
321       if (item->data(0, Qt::UserRole).toBool()) {
322          bsnprintf(cmd, sizeof(cmd), "unmark \"%s\"", item->text(1).toUtf8().data());
323          item->setIcon(0, QIcon(QString::fromUtf8(":images/unchecked.png")));
324          item->setData(0, Qt::UserRole, false);
325       } else {
326          bsnprintf(cmd, sizeof(cmd), "mark \"%s\"", item->text(1).toUtf8().data());
327          item->setIcon(0, QIcon(QString::fromUtf8(":images/check.png")));
328          item->setData(0, Qt::UserRole, true);
329       }
330       m_console->write_dir(m_conn, cmd, false);
331       if (m_console->read(m_conn) > 0) {
332          strip_trailing_junk(m_console->msg(m_conn));
333          statusLine->setText(m_console->msg(m_conn));
334       }
335       m_console->displayToPrompt(m_conn);
336       mainWin->waitExit();
337       return;
338    }    
339    /* 
340     * Double clicking other than column 0 means to decend into
341     *  the directory -- or nothing if it is not a directory.
342     */
343    if (item->text(1).endsWith("/")) {
344       QString fullpath = m_cwd + item->text(1);
345       QTreeWidgetItem *item = m_dirPaths.value(fullpath);
346       Pmsg1(dbglvl, "%s\n", fullpath.toUtf8().data());
347       if (item) {
348          directoryWidget->setCurrentItem(item);
349       } else {
350          QString msg = QString("DoubleClick else of item column %1 fullpath %2\n")
351               .arg(column,10)
352               .arg(fullpath);
353          Pmsg1(dbglvl, "%s\n", msg.toUtf8().data());
354       }
355    }
356 }
357
358 /*
359  * If up button pushed, making the parent tree widget current will call fill
360  * directory.
361  */
362 void restorePage::upButtonPushed()
363 {
364    cwd("..");
365    QTreeWidgetItem *item = m_dirPaths.value(m_cwd);
366    if (item) {
367       directoryWidget->setCurrentItem(item);
368    }
369    statusLine->setText("");
370 }
371
372 /*
373  * Mark selected items
374  */
375 void restorePage::markButtonPushed()
376 {
377    mainWin->waitEnter();
378    QList<QTreeWidgetItem *> treeItemList = fileWidget->selectedItems();
379    QTreeWidgetItem *item;
380    char cmd[1000];
381    int count = 0;
382    statusLine->setText("");
383    foreach (item, treeItemList) {
384       count++;
385       bsnprintf(cmd, sizeof(cmd), "mark \"%s\"", item->text(1).toUtf8().data());
386       item->setIcon(0, QIcon(QString::fromUtf8(":images/check.png")));
387       m_console->write_dir(m_conn, cmd, false);
388       if (m_console->read(m_conn) > 0) {
389          strip_trailing_junk(m_console->msg(m_conn));
390          statusLine->setText(m_console->msg(m_conn));
391       }
392       Dmsg1(dbglvl, "cmd=%s\n", cmd);
393       m_console->discardToPrompt(m_conn);
394    }
395    if (count == 0) {
396       mainWin->set_status("Nothing selected, nothing done");
397       statusLine->setText("Nothing selected, nothing done");
398    }
399    mainWin->waitExit();
400 }
401
402 /*
403  * Unmark selected items
404  */
405 void restorePage::unmarkButtonPushed()
406 {
407    mainWin->waitEnter();
408    QList<QTreeWidgetItem *> treeItemList = fileWidget->selectedItems();
409    QTreeWidgetItem *item;
410    char cmd[1000];
411    int count = 0;
412    statusLine->setText("");
413    foreach (item, treeItemList) {
414       count++;
415       bsnprintf(cmd, sizeof(cmd), "unmark \"%s\"", item->text(1).toUtf8().data());
416       item->setIcon(0, QIcon(QString::fromUtf8(":images/unchecked.png")));
417       m_console->write_dir(m_conn, cmd, false);
418       if (m_console->read(m_conn) > 0) {
419          strip_trailing_junk(m_console->msg(m_conn));
420          statusLine->setText(m_console->msg(m_conn));
421       }
422       Dmsg1(dbglvl, "cmd=%s\n", cmd);
423       m_console->discardToPrompt(m_conn);
424    }
425    if (count == 0) {
426       mainWin->set_status(tr("Nothing selected, nothing done"));
427       statusLine->setText(tr("Nothing selected, nothing done"));
428    }
429    mainWin->waitExit();
430 }
431
432 /*
433  * Change current working directory 
434  */
435 bool restorePage::cwd(const char *dir)
436 {
437    int stat;
438    char cd_cmd[MAXSTRING];
439
440    mainWin->waitEnter();
441    statusLine->setText("");
442    bsnprintf(cd_cmd, sizeof(cd_cmd), "cd \"%s\"", dir);
443    Dmsg2(dbglvl, "dir=%s cmd=%s\n", dir, cd_cmd);
444    m_console->write_dir(m_conn, cd_cmd, false);
445    lineEdit->clear();
446    if ((stat = m_console->read(m_conn)) > 0) {
447       m_cwd = m_console->msg(m_conn);
448       lineEdit->insert(m_cwd);
449       Dmsg2(dbglvl, "cwd=%s msg=%s\n", m_cwd.toUtf8().data(), m_console->msg(m_conn));
450    } else {
451       Dmsg1(dbglvl, "stat=%d\n", stat);
452       QMessageBox::critical(this, "Error", tr("cd command failed"), QMessageBox::Ok);
453    }
454    m_console->discardToPrompt(m_conn);
455    mainWin->waitExit();
456    return true;  /* ***FIXME*** return real status */
457 }
458
459 /*
460  * Return cwd when in tree restore mode 
461  */
462 char *restorePage::get_cwd()
463 {
464    int stat;
465    mainWin->waitEnter();
466    m_console->write_dir(m_conn, ".pwd", false);
467    Dmsg0(dbglvl, "send: .pwd\n");
468    if ((stat = m_console->read(m_conn)) > 0) {
469       m_cwd = m_console->msg(m_conn);
470       Dmsg2(dbglvl, "cwd=%s msg=%s\n", m_cwd.toUtf8().data(), m_console->msg(m_conn));
471    } else {
472       Dmsg1(dbglvl, "Something went wrong read stat=%d\n", stat);
473       QMessageBox::critical(this, "Error", tr(".pwd command failed"), QMessageBox::Ok);
474    }
475    m_console->discardToPrompt(m_conn); 
476    mainWin->waitExit();
477    return m_cwd.toUtf8().data();
478 }
479
480 /*
481  * Save user settings associated with this page
482  */
483 void restorePage::writeSettings()
484 {
485    QSettings settings(m_console->m_dir->name(), "bat");
486    settings.beginGroup("RestorePage");
487    settings.setValue(m_splitText, splitter->saveState());
488    settings.endGroup();
489 }
490
491 /*
492  * Read and restore user settings associated with this page
493  */
494 void restorePage::readSettings()
495 {
496    m_splitText = "splitterSizes_2";
497    QSettings settings(m_console->m_dir->name(), "bat");
498    settings.beginGroup("RestorePage");
499    if (settings.contains(m_splitText)) { 
500       splitter->restoreState(settings.value(m_splitText).toByteArray());
501    }
502    settings.endGroup();
503 }