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