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