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