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