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