1 /*
2  * cluster members dialog implementation
3  *
4  * Copyright (c) 2008 secunet Security Networks AG
5  * Copyright (c) 2008 Adrian-Ken Rueegsegger <rueegsegger@swiss-it.ch>
6  * Copyright (c) 2008 Reto Buerki <buerki@swiss-it.ch>
7  *
8  * This work is dual-licensed under:
9  *
10  * o The terms of the GNU General Public License as published by the Free
11  *   Software Foundation, either version 2 of the License, or (at your option)
12  *   any later version.
13  *
14  * o The terms of NetCitadel End User License Agreement
15  */
16 
17 #include "clusterMembersDialog.h"
18 #include "global.h"
19 #include "utils_no_qt.h"
20 
21 #include "FWWindow.h"
22 #include "Help.h"
23 #include "FWCmdChange.h"
24 
25 #include "fwbuilder/FWObjectDatabase.h"
26 #include "fwbuilder/Cluster.h"
27 #include "fwbuilder/ClusterGroup.h"
28 #include "fwbuilder/Interface.h"
29 #include "fwbuilder/Resources.h"
30 
31 #include <memory>
32 
33 #include <algorithm>
34 
35 #include <qdebug.h>
36 #include <qpixmapcache.h>
37 
38 #include <QHeaderView>
39 #include <QUndoStack>
40 
41 
42 using namespace std;
43 using namespace libfwbuilder;
44 
45 typedef std::list<libfwbuilder::Firewall *> t_fwList;
46 
clusterMembersDialog(QWidget * parent,FWObject * o)47 clusterMembersDialog::clusterMembersDialog(QWidget *parent, FWObject *o)
48     : QDialog(parent), table_update(false)
49 {
50     m_dialog = new Ui::clusterMembersDialog_q;
51     m_dialog->setupUi(this);
52     setWindowModality(Qt::WindowModal);
53 
54     // assign clustergroup object
55     obj = o;
56     FWObject *parent_host = Host::getParentHost(obj);
57     host_os = parent_host->getStr("host_OS").c_str();
58     platform = parent_host->getStr("platform").c_str();
59 
60     // if empty, retry with parent of parent (interface level)
61     if (host_os.isEmpty())
62     {
63         FWObject *parent = NULL;
64         parent = obj->getParent();
65         if (parent == NULL)
66         {
67             throw FWException("clusterMembersDialog: parent is NULL!");
68         }
69         parent = parent->getParent();
70         if (parent == NULL)
71         {
72             throw FWException("clusterMembersDialog: parent is NULL!");
73         }
74         host_os = parent->getStr("host_OS").c_str();
75         platform = parent->getStr("platform").c_str();
76     }
77 
78     string type = obj->getStr("type");
79     enable_master_column = Resources::os_res[host_os.toStdString()]->getResourceBool(
80         "/FWBuilderResources/Target/protocols/" + type + "/needs_master");
81     if (!enable_master_column) m_dialog->fwSelectedTable->hideColumn(2);
82 
83     // prepare lists of firewalls (selected, available)
84     getSelectedMembers();
85     getPossibleMembers();
86 
87     // init views
88     updateAvailableTree();
89     updateSelectedTable();
90 }
91 
~clusterMembersDialog()92 clusterMembersDialog::~clusterMembersDialog()
93 {
94     // clear member lists
95     for (t_memberList::iterator it = available.begin();
96         it != available.end(); it++)
97     {
98         delete *it;
99     }
100     available.clear();
101 
102     for (t_memberList::iterator it = selected.begin();
103         it != selected.end(); it++)
104     {
105         delete *it;
106     }
107     selected.clear();
108 
109     delete m_dialog;
110 }
111 
getSelectedMembers()112 void clusterMembersDialog::getSelectedMembers()
113 {
114     // read in master interface id
115     std::string master_iface = obj->getStr("master_iface");
116 
117     for (FWObjectTypedChildIterator it =
118         obj->findByType(FWObjectReference::TYPENAME);
119         it != it.end(); ++it)
120     {
121 
122         // get fw and interface pointer from interface reference
123         Interface *iface = NULL;
124         iface = Interface::cast(FWReference::cast((*it))->getPointer());
125         assert(iface != NULL);
126         Firewall *fw = Firewall::cast(Host::getParentHost(iface));
127         //Firewall *fw = Firewall::cast(iface->getParentHost());
128 
129         // determine master
130         std::string iface_id = FWObjectDatabase::getStringId(iface->getId());
131         bool master = false;
132         if (iface_id == master_iface)
133         {
134             master = true;
135         }
136         // create ClusterMember object
137         ClusterMember *new_member = createMember(fw, iface, master);
138         if (new_member == NULL)
139         {
140             qWarning() << "clusterMembersDialog: could not create new "
141                 "cluster member";
142             return;
143         }
144         // attach to selected list
145         selected.push_back(new_member);
146     }
147 }
148 
getPossibleMembers()149 void clusterMembersDialog::getPossibleMembers()
150 {
151     t_fwList fwlist;
152 
153     mw->findAllFirewalls(fwlist);
154 
155     Firewall *fw;
156     for (t_fwList::iterator it = fwlist.begin(); it != fwlist.end(); it++)
157     {
158         // does host_OS and platform match?
159         fw = *it;
160         if (fw->getStr("host_OS").c_str() != host_os ||
161             fw->getStr("platform").c_str() != platform)
162         {
163             continue;
164         }
165 
166         // does the firewall provide at least one phys. interface?
167         FWObjectTypedChildIterator iface_i = fw->findByType(Interface::TYPENAME);
168         if (iface_i == iface_i.end())
169         {
170             continue;
171         }
172         else
173         {
174             // previously selected? skip
175             PredFindFw pred;
176             pred.setSearchString(fw->getName().c_str());
177             t_memberList::iterator it = find_if(selected.begin(),
178                                                 selected.end(), pred);
179             if (it != selected.end())
180             {
181                 continue;
182             }
183 
184             // valid member, add to member list
185             ClusterMember *new_member = createMember(fw);
186             if (new_member == NULL)
187             {
188                 qWarning() << "clusterMembersDialog: could not create new "
189                     "cluster member";
190                 return;
191             }
192             available.push_back(new_member);
193         }
194     }
195     fwlist.sort(FWObjectNameCmpPredicate());
196 }
197 
updateSelectedTable()198 void clusterMembersDialog::updateSelectedTable()
199 {
200     table_update = true;
201     m_dialog->fwSelectedTable->setRowCount(selected.size());
202 
203     QTableWidgetItem *item = NULL;
204     int row = 0;
205     for (t_memberList::const_iterator it = selected.begin();
206             it != selected.end(); it++)
207     {
208 
209         // only insert new QTableWidgetItems if none has been set, update text
210         // if selected member text changed.
211 
212         item = m_dialog->fwSelectedTable->item(row, 0);
213         const char *new_text = (*it)->fwobj->getName().c_str();
214         if (item == NULL)
215         {
216             item = new QTableWidgetItem;
217             item->setText(new_text);
218             item->setIcon(QIcon(getIcon((*it)->fwobj)));
219             m_dialog->fwSelectedTable->setItem(row, 0, item);
220         }
221         else if (item->text() != new_text)
222         {
223             item->setText(new_text);
224         }
225 
226         // Column "Interface"
227         item = m_dialog->fwSelectedTable->item(row, 1);
228         new_text = (*it)->iface_cluster->getName().c_str();
229         if (item == NULL)
230         {
231             item = new QTableWidgetItem;
232             item->setText(new_text);
233             item->setIcon(QIcon(getIcon((*it)->iface_cluster)));
234             m_dialog->fwSelectedTable->setItem(row, 1, item);
235         }
236         else if (item->text() != new_text)
237         {
238             item->setText(new_text);
239         }
240 
241         // Column "Master"
242         item = m_dialog->fwSelectedTable->item(row, 2);
243         Qt::CheckState state = (*it)->is_master ? Qt::Checked : Qt::Unchecked;
244         if (item == NULL)
245         {
246             item = new QTableWidgetItem;
247             item->setCheckState(state);
248             m_dialog->fwSelectedTable->setItem(row, 2, item);
249         }
250         else if (item->checkState() != state)
251         {
252             item->setCheckState(state);
253         }
254 
255         row++;
256     }
257 
258     m_dialog->fwSelectedTable->resizeColumnsToContents();
259     m_dialog->fwSelectedTable->horizontalHeader()->setStretchLastSection(true);
260 
261     table_update = false;
262 }
263 
updateAvailableTree()264 void clusterMembersDialog::updateAvailableTree()
265 {
266     QTreeWidgetItem *fwitem;
267 
268     m_dialog->fwAvailableTree->clear();
269     for (t_memberList::const_iterator it = available.begin();
270         it != available.end(); it++)
271     {
272 
273         ClusterMember *member = *it;
274         fwitem = new QTreeWidgetItem;
275         fwitem->setFlags(Qt::ItemIsEnabled);
276         fwitem->setText(0, member->fwobj->getName().c_str());
277         fwitem->setIcon(0, QIcon(getIcon(member->fwobj)));
278 
279         // add interfaces
280         for (t_ifaceList::const_iterator it = member->iface_list.begin();
281             it != member->iface_list.end(); it ++)
282         {
283             QTreeWidgetItem *ifitem;
284             ifitem = new QTreeWidgetItem(fwitem);
285 
286             ifitem->setText(1, (*it)->getName().c_str());
287             ifitem->setIcon(1, QIcon(getIcon(*it)));
288 
289             // add label (if non empty)
290             string label = (*it)->getLabel();
291             if (!label.empty())
292             {
293                 ifitem->setText(2, label.c_str());
294             }
295         }
296         m_dialog->fwAvailableTree->insertTopLevelItem(0, fwitem);
297     }
298 
299     m_dialog->fwAvailableTree->resizeColumnToContents(0);
300     m_dialog->fwAvailableTree->sortByColumn(0, Qt::AscendingOrder);
301     m_dialog->fwAvailableTree->expandAll();
302 }
303 
304 ClusterMember*
createMember(Firewall * fw,Interface * cluster_iface,bool master)305 clusterMembersDialog::createMember(Firewall *fw,
306                                    Interface *cluster_iface, bool master)
307 {
308     if (fw == NULL)
309     {
310         return NULL;
311     }
312 
313     ClusterMember *new_member = new ClusterMember;
314     new_member->fwobj = fw;
315     new_member->is_master = master;
316     if (cluster_iface != NULL)
317     {
318         new_member->iface_cluster = cluster_iface;
319     }
320 
321     list<FWObject*> interfaces = fw->getByTypeDeep(Interface::TYPENAME);
322     interfaces.sort(FWObjectNameCmpPredicate());
323 
324     list<FWObject*>::iterator iface_i;
325     for (iface_i=interfaces.begin(); iface_i != interfaces.end(); ++iface_i)
326     {
327         Interface *iface = Interface::cast(*iface_i);
328         new_member->iface_list.push_back(iface);
329         // init interface mapping table
330         new_member->iface_map[iface->getName().c_str()] = iface;
331     }
332     return new_member;
333 }
334 
swap(t_memberList & from,t_memberList & to,const QString fwname,const QString iface,bool master)335 bool clusterMembersDialog::swap(t_memberList &from, t_memberList &to,
336                                      const QString fwname, const QString iface, bool master)
337 {
338     // move selected fw from 'from' to 'to' member list
339     PredFindFw pred;
340     pred.setSearchString(fwname);
341     t_memberList::iterator it = find_if(from.begin(), from.end(), pred);
342     if (it == from.end())
343     {
344         // not found
345         return false;
346     }
347 
348     ClusterMember *member = *it;
349     from.erase(it);
350 
351     member->iface_cluster = member->iface_map[iface];
352     member->is_master = master;
353     to.push_back(member);
354 
355     return true;
356 }
357 
setMaster(QString fw,bool checked)358 void clusterMembersDialog::setMaster(QString fw, bool checked)
359 {
360     for (t_memberList::const_iterator it = selected.begin();
361         it != selected.end(); it++)
362     {
363         if (QString((*it)->fwobj->getName().c_str()) == fw)
364         {
365             (*it)->is_master = checked;
366         }
367         else
368         {
369             (*it)->is_master = false;
370         }
371     }
372     updateSelectedTable();
373 }
374 
getIcon(FWObject * o)375 QPixmap clusterMembersDialog::getIcon(FWObject *o)
376 {
377     QString icn_file = (":/Icons/" + o->getTypeName() +
378                         "/icon").c_str();
379 
380     QPixmap pm;
381     if (!QPixmapCache::find(icn_file, pm))
382     {
383         pm.load(icn_file);
384         QPixmapCache::insert(icn_file, pm);
385     }
386     return pm;
387 }
388 
invalidate()389 void clusterMembersDialog::invalidate()
390 {
391     // update views
392     updateAvailableTree();
393     updateSelectedTable();
394 
395     // disable <--> buttons, user needs to re-select
396     m_dialog->buttonAdd->setEnabled(false);
397     m_dialog->buttonRemove->setEnabled(false);
398 }
399 
accept()400 void clusterMembersDialog::accept()
401 {
402     ProjectPanel *project = mw->activeProject();
403     std::auto_ptr<FWCmdChange> cmd( new FWCmdChange(project, obj));
404     FWObject* new_state = cmd->getNewState();
405 
406     bool master_found = false;
407     t_memberList::const_iterator it = selected.begin();
408 
409     // remoive all existing references and add new ones
410     list<FWObject*> all_refs = new_state->getByType(FWObjectReference::TYPENAME);
411     for (list<FWObject*>::iterator it=all_refs.begin(); it!=all_refs.end(); ++it)
412         new_state->remove(*it);
413 
414     // add selected interfaces as objref to cluster member group
415     for (it = selected.begin(); it != selected.end(); it++)
416     {
417         new_state->addRef((*it)->iface_cluster);
418         // set master interface ref id
419         if ((*it)->is_master)
420         {
421             master_found = true;
422             std::string masteriface_id =
423                 FWObjectDatabase::getStringId((*it)->iface_cluster->getId());
424             new_state->setStr("master_iface", masteriface_id);
425         }
426     }
427     if (!master_found)
428     {
429         new_state->remStr("master_iface");
430     }
431     emit membersChanged();
432 
433     if (!cmd->getOldState()->cmp(new_state, true))
434         project->undoStack->push(cmd.release());
435 
436     QDialog::accept();
437 }
438 
reject()439 void clusterMembersDialog::reject()
440 {
441     QDialog::reject();
442 }
443 
help()444 void clusterMembersDialog::help()
445 {
446     QString tab_title = m_dialog->tabWidget->tabText(
447                             m_dialog->tabWidget->currentIndex());
448     QString anchor = tab_title.replace('/', '-').replace(' ', '-').toLower();
449     Help *h = Help::getHelpWindow(this);
450     h->setName("Cluster-Member Management");
451     h->setSource(QUrl("clusterMembersDialog.html#" + anchor));
452     h->raise();
453     h->show();
454 }
455 
availableClicked(QTreeWidgetItem * item,int)456 void clusterMembersDialog::availableClicked(QTreeWidgetItem *item, int)
457 {
458     // activate addButton if a specific interface has been selected
459     if (item->text(1).isEmpty())
460     {
461         m_dialog->buttonAdd->setEnabled(false);
462     }
463     else if (!m_dialog->buttonAdd->isEnabled())
464     {
465         m_dialog->buttonAdd->setEnabled(true);
466     }
467 }
468 
selectedClicked(int row,int column)469 void clusterMembersDialog::selectedClicked(int row, int column)
470 {
471     if (fwbdebug)
472     {
473         qDebug() << "clusterMembersDialog: selected (" << row << ", "
474                  << column << ")";
475     }
476 
477     // activate removeButton
478     if (!m_dialog->buttonRemove->isEnabled())
479     {
480         m_dialog->buttonRemove->setEnabled(true);
481     }
482 }
483 
masterSelected(int row,int column)484 void clusterMembersDialog::masterSelected(int row, int column)
485 {
486     if (!table_update)
487     {
488         if (fwbdebug)
489         {
490             qDebug() << "clusterMembersDialog: master is (" << row
491                      << ", " << column << ")";
492         }
493 
494         QList<QTableWidgetItem *> itemlist;
495         itemlist = m_dialog->fwSelectedTable->selectedItems();
496 
497         if (itemlist[2]->checkState() == Qt::Checked)
498         {
499             setMaster(itemlist[0]->text());
500         }
501         else
502         {
503             setMaster(itemlist[0]->text(), false);
504         }
505     }
506 }
507 
firewallAdd()508 void clusterMembersDialog::firewallAdd()
509 {
510     // get selected firewall / interface
511     QList<QTreeWidgetItem *> itemlist;
512     itemlist = m_dialog->fwAvailableTree->selectedItems();
513 
514     // interface should not be empty
515     if (itemlist[0]->text(1).isEmpty())
516     {
517         qWarning() << "clusterMembersDialog: iface is empty, not adding";
518         return;
519     }
520 
521     foreach(QTreeWidgetItem *itm, itemlist)
522     {
523         // move selected fw to selected member list
524         QString fwname = itm->parent()->text(0);
525         QString iface_cluster = itm->text(1);
526         if (!swap(available, selected, fwname, iface_cluster))
527         {
528             // swap failed, this should not happen!
529             qWarning() << "clusterMembersDialog: swap failed for firewall "
530                        << fwname << ", interface: " << iface_cluster;
531             return;
532         }
533     }
534     // invalidate view
535     invalidate();
536 }
537 
firewallRemove()538 void clusterMembersDialog::firewallRemove()
539 {
540     // get selected firewall / interface
541     QList<QTableWidgetItem *> itemlist;
542     itemlist = m_dialog->fwSelectedTable->selectedItems();
543 
544     // move selected fw to available member list
545     QString fwname = itemlist[0]->text();
546     if (!swap(selected, available, fwname, "", false))
547     {
548         // swap failed, this should not happen!
549         qWarning() << "clusterMembersDialog: swap failed for firewall "
550                    << fwname;
551         return;
552     }
553     // invalidate view
554     invalidate();
555 }
556 
557