1 // Copyright 2005-2019 The Mumble Developers. All rights reserved.
2 // Use of this source code is governed by a BSD-style license
3 // that can be found in the LICENSE file at the root of the
4 // Mumble source tree or at <https://www.mumble.info/LICENSE>.
5 
6 #include "mumble_pch.hpp"
7 
8 #include "ACLEditor.h"
9 
10 #include "ACL.h"
11 #include "Channel.h"
12 #include "ClientUser.h"
13 #include "Database.h"
14 #include "Log.h"
15 #include "ServerHandler.h"
16 #include "User.h"
17 
18 // We define a global macro called 'g'. This can lead to issues when included code uses 'g' as a type or parameter name (like protobuf 3.7 does). As such, for now, we have to make this our last include.
19 #include "Global.h"
20 
ACLGroup(const QString & name)21 ACLGroup::ACLGroup(const QString &name) : Group(NULL, name) {
22 	bInherited = false;
23 }
24 
ACLEditor(int channelparentid,QWidget * p)25 ACLEditor::ACLEditor(int channelparentid, QWidget *p) : QDialog(p) {
26 	// Simple constructor for add channel menu
27 	bAddChannelMode = true;
28 	iChannel = channelparentid;
29 
30 	setupUi(this);
31 
32 	qsbChannelPosition->setRange(INT_MIN, INT_MAX);
33 
34 	setWindowTitle(tr("Mumble - Add channel"));
35 	qtwTab->removeTab(2);
36 	qtwTab->removeTab(1);
37 
38 	// Until I come around implementing it hide the password fields
39 	qleChannelPassword->hide();
40 	qlChannelPassword->hide();
41 
42 	if (g.sh->uiVersion >= 0x010300) {
43 		qsbChannelMaxUsers->setRange(0, INT_MAX);
44 		qsbChannelMaxUsers->setValue(0);
45 		qsbChannelMaxUsers->setSpecialValueText(tr("Default server value"));
46 	} else {
47 		qlChannelMaxUsers->hide();
48 		qsbChannelMaxUsers->hide();
49 	}
50 
51 	qlChannelID->hide();
52 
53 	qleChannelName->setFocus();
54 
55 	pcaPassword = NULL;
56 	adjustSize();
57 }
58 
ACLEditor(int channelid,const MumbleProto::ACL & mea,QWidget * p)59 ACLEditor::ACLEditor(int channelid, const MumbleProto::ACL &mea, QWidget *p) : QDialog(p) {
60 	QLabel *l;
61 
62 	bAddChannelMode = false;
63 
64 	iChannel = channelid;
65 	Channel *pChannel = Channel::get(iChannel);
66 	if (pChannel == NULL) {
67 		g.l->log(Log::Warning, tr("Failed: Invalid channel"));
68 		QDialog::reject();
69 		return;
70 	}
71 
72 	msg = mea;
73 
74 	setupUi(this);
75 
76 	qcbChannelTemporary->hide();
77 
78 	iId = mea.channel_id();
79 	setWindowTitle(tr("Mumble - Edit %1").arg(Channel::get(iId)->qsName));
80 
81 	qlChannelID->setText(tr("ID: %1").arg(iId));
82 
83 	qleChannelName->setText(pChannel->qsName);
84 	if (channelid == 0)
85 		qleChannelName->setEnabled(false);
86 
87 	rteChannelDescription->setText(pChannel->qsDesc);
88 
89 	qsbChannelPosition->setRange(INT_MIN, INT_MAX);
90 	qsbChannelPosition->setValue(pChannel->iPosition);
91 
92 	if (g.sh->uiVersion >= 0x010300) {
93 		qsbChannelMaxUsers->setRange(0, INT_MAX);
94 		qsbChannelMaxUsers->setValue(pChannel->uiMaxUsers);
95 		qsbChannelMaxUsers->setSpecialValueText(tr("Default server value"));
96 	} else {
97 		qlChannelMaxUsers->hide();
98 		qsbChannelMaxUsers->hide();
99 	}
100 
101 	QGridLayout *grid = new QGridLayout(qgbACLpermissions);
102 
103 	l = new QLabel(tr("Deny"), qgbACLpermissions);
104 	grid->addWidget(l, 0, 1);
105 	l = new QLabel(tr("Allow"), qgbACLpermissions);
106 	grid->addWidget(l, 0, 2);
107 
108 	int idx = 1;
109 	for (int i = 0; i < ((iId == 0) ? 30 : 16); ++i) {
110 		ChanACL::Perm perm = static_cast<ChanACL::Perm>(1 << i);
111 		QString name = ChanACL::permName(perm);
112 
113 		if (! name.isEmpty()) {
114 			QCheckBox *qcb;
115 			l = new QLabel(name, qgbACLpermissions);
116 			grid->addWidget(l, idx, 0);
117 			qcb = new QCheckBox(qgbACLpermissions);
118 			qcb->setToolTip(tr("Deny %1").arg(name));
119 			qcb->setWhatsThis(tr("This revokes the %1 privilege. If a privilege is both allowed and denied, it is denied.<br />%2").arg(name).arg(ChanACL::whatsThis(perm)));
120 			connect(qcb, SIGNAL(clicked(bool)), this, SLOT(ACLPermissions_clicked()));
121 			grid->addWidget(qcb, idx, 1);
122 
123 			qlACLDeny << qcb;
124 
125 			qcb = new QCheckBox(qgbACLpermissions);
126 			qcb->setToolTip(tr("Allow %1").arg(name));
127 			qcb->setWhatsThis(tr("This grants the %1 privilege. If a privilege is both allowed and denied, it is denied.<br />%2").arg(name).arg(ChanACL::whatsThis(perm)));
128 			connect(qcb, SIGNAL(clicked(bool)), this, SLOT(ACLPermissions_clicked()));
129 			grid->addWidget(qcb, idx, 2);
130 
131 			qlACLAllow << qcb;
132 
133 			qlPerms << perm;
134 
135 			++idx;
136 		}
137 	}
138 	QSpacerItem *si = new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding);
139 	grid->addItem(si, idx, 0);
140 
141 	connect(qcbGroupAdd->lineEdit(), SIGNAL(returnPressed()), qpbGroupAddAdd, SLOT(animateClick()));
142 	connect(qcbGroupRemove->lineEdit(), SIGNAL(returnPressed()), qpbGroupRemoveAdd, SLOT(animateClick()));
143 
144 	foreach(User *u, ClientUser::c_qmUsers) {
145 		if (u->iId >= 0) {
146 			qhNameCache.insert(u->iId, u->qsName);
147 			qhIDCache.insert(u->qsName.toLower(), u->iId);
148 		}
149 	}
150 
151 	ChanACL *def = new ChanACL(NULL);
152 
153 	def->bApplyHere = true;
154 	def->bApplySubs = true;
155 	def->bInherited = true;
156 	def->iUserId = -1;
157 	def->qsGroup = QLatin1String("all");
158 	def->pAllow = ChanACL::Traverse | ChanACL::Enter | ChanACL::Speak | ChanACL::Whisper | ChanACL::TextMessage;
159 	def->pDeny = (~def->pAllow) & ChanACL::All;
160 
161 	qlACLs << def;
162 
163 	for (int i = 0; i < mea.acls_size(); ++i) {
164 		const MumbleProto::ACL_ChanACL &as = mea.acls(i);
165 
166 		ChanACL *acl = new ChanACL(NULL);
167 		acl->bApplyHere = as.apply_here();
168 		acl->bApplySubs = as.apply_subs();
169 		acl->bInherited = as.inherited();
170 		acl->iUserId = -1;
171 		if (as.has_user_id())
172 			acl->iUserId = as.user_id();
173 		else
174 			acl->qsGroup = u8(as.group());
175 		acl->pAllow = static_cast<ChanACL::Permissions>(as.grant());
176 		acl->pDeny = static_cast<ChanACL::Permissions>(as.deny());
177 
178 		qlACLs << acl;
179 	}
180 
181 	for (int i = 0; i < mea.groups_size(); ++i) {
182 		const MumbleProto::ACL_ChanGroup &gs = mea.groups(i);
183 
184 		ACLGroup *gp = new ACLGroup(u8(gs.name()));
185 		gp->bInherit = gs.inherit();
186 		gp->bInherited = gs.inherited();
187 		gp->bInheritable = gs.inheritable();
188 		for (int j = 0; j < gs.add_size(); ++j)
189 			gp->qsAdd.insert(gs.add(j));
190 		for (int j = 0; j < gs.remove_size(); ++j)
191 			gp->qsRemove.insert(gs.remove(j));
192 		for (int j = 0; j < gs.inherited_members_size(); ++j)
193 			gp->qsTemporary.insert(gs.inherited_members(j));
194 
195 		qlGroups << gp;
196 	}
197 
198 	iUnknown = -2;
199 
200 	numInheritACL = -1;
201 
202 	bInheritACL = mea.inherit_acls();
203 	qcbACLInherit->setChecked(bInheritACL);
204 
205 	foreach(ChanACL *acl, qlACLs) {
206 		if (acl->bInherited)
207 			numInheritACL++;
208 	}
209 
210 	refill(GroupAdd);
211 	refill(GroupRemove);
212 	refill(GroupInherit);
213 	refill(ACLList);
214 	refillGroupNames();
215 
216 	ACLEnableCheck();
217 	groupEnableCheck();
218 
219 	updatePasswordField();
220 
221 	qleChannelName->setFocus();
222 	adjustSize();
223 }
224 
~ACLEditor()225 ACLEditor::~ACLEditor() {
226 	foreach(ChanACL *acl, qlACLs) {
227 		delete acl;
228 	}
229 	foreach(ACLGroup *gp, qlGroups) {
230 		delete gp;
231 	}
232 }
233 
showEvent(QShowEvent * evt)234 void ACLEditor::showEvent(QShowEvent *evt) {
235 	ACLEnableCheck();
236 	QDialog::showEvent(evt);
237 }
238 
accept()239 void ACLEditor::accept() {
240 	Channel *pChannel = Channel::get(iChannel);
241 	if (pChannel == NULL) {
242 		// Channel gone while editing
243 		g.l->log(Log::Warning, tr("Failed: Invalid channel"));
244 		QDialog::reject();
245 		return;
246 	}
247 
248 	if (qleChannelName->text().isEmpty()) {
249 		// Empty channel name
250 		QMessageBox::warning(this, QLatin1String("Mumble"), tr("Channel must have a name"), QMessageBox::Ok);
251 		qleChannelName->setFocus();
252 		return;
253 	}
254 
255 	// Update channel state
256 	if (bAddChannelMode) {
257 		g.sh->createChannel(iChannel, qleChannelName->text(), rteChannelDescription->text(), qsbChannelPosition->value(), qcbChannelTemporary->isChecked(), qsbChannelMaxUsers->value());
258 	} else {
259 		bool needs_update = false;
260 
261 		updatePasswordACL();
262 
263 		MumbleProto::ChannelState mpcs;
264 		mpcs.set_channel_id(pChannel->iId);
265 		if (pChannel->qsName != qleChannelName->text()) {
266 			mpcs.set_name(u8(qleChannelName->text()));
267 			needs_update = true;
268 		}
269 		if (rteChannelDescription->isModified() && (pChannel->qsDesc != rteChannelDescription->text())) {
270 			const QString &descriptionText = rteChannelDescription->text();
271 			mpcs.set_description(u8(descriptionText));
272 			needs_update = true;
273 			g.db->setBlob(sha1(descriptionText), descriptionText.toUtf8());
274 		}
275 		if (pChannel->iPosition != qsbChannelPosition->value()) {
276 			mpcs.set_position(qsbChannelPosition->value());
277 			needs_update = true;
278 		}
279 		if (pChannel->uiMaxUsers != static_cast<unsigned int>(qsbChannelMaxUsers->value())) {
280 			mpcs.set_max_users(qsbChannelMaxUsers->value());
281 			needs_update = true;
282 		}
283 		if (needs_update)
284 			g.sh->sendMessage(mpcs);
285 
286 		// Update ACL
287 		msg.set_inherit_acls(bInheritACL);
288 		msg.clear_acls();
289 		msg.clear_groups();
290 
291 		foreach(ChanACL *acl, qlACLs) {
292 			if (acl->bInherited || (acl->iUserId < -1))
293 				continue;
294 			MumbleProto::ACL_ChanACL *mpa = msg.add_acls();
295 			mpa->set_apply_here(acl->bApplyHere);
296 			mpa->set_apply_subs(acl->bApplySubs);
297 			if (acl->iUserId != -1)
298 				mpa->set_user_id(acl->iUserId);
299 			else
300 				mpa->set_group(u8(acl->qsGroup));
301 			mpa->set_grant(acl->pAllow);
302 			mpa->set_deny(acl->pDeny);
303 		}
304 
305 		foreach(ACLGroup *gp, qlGroups) {
306 			if (gp->bInherited && gp->bInherit && gp->bInheritable && (gp->qsAdd.count() == 0) && (gp->qsRemove.count() == 0))
307 				continue;
308 			MumbleProto::ACL_ChanGroup *mpg = msg.add_groups();
309 			mpg->set_name(u8(gp->qsName));
310 			mpg->set_inherit(gp->bInherit);
311 			mpg->set_inheritable(gp->bInheritable);
312 			foreach(int pid, gp->qsAdd)
313 				if (pid >= 0)
314 					mpg->add_add(pid);
315 			foreach(int pid, gp->qsRemove)
316 				if (pid >= 0)
317 					mpg->add_remove(pid);
318 		}
319 		g.sh->sendMessage(msg);
320 	}
321 	QDialog::accept();
322 }
323 
324 
userName(int pid)325 const QString ACLEditor::userName(int pid) {
326 	if (qhNameCache.contains(pid))
327 		return qhNameCache.value(pid);
328 	else
329 		return QString::fromLatin1("#%1").arg(pid);
330 }
331 
id(const QString & uname)332 int ACLEditor::id(const QString &uname) {
333 	QString name = uname.toLower();
334 	if (qhIDCache.contains(name)) {
335 		return qhIDCache.value(name);
336 	} else {
337 		if (! qhNameWait.contains(name)) {
338 			MumbleProto::QueryUsers mpuq;
339 			mpuq.add_names(u8(name));
340 			g.sh->sendMessage(mpuq);
341 
342 			iUnknown--;
343 			qhNameWait.insert(name, iUnknown);
344 			qhNameCache.insert(iUnknown, name);
345 		}
346 		return qhNameWait.value(name);
347 	}
348 }
349 
returnQuery(const MumbleProto::QueryUsers & mqu)350 void ACLEditor::returnQuery(const MumbleProto::QueryUsers &mqu) {
351 	if (mqu.names_size() != mqu.ids_size())
352 		return;
353 
354 	for (int i = 0; i < mqu.names_size(); ++i) {
355 		int pid = mqu.ids(i);
356 		QString name = u8(mqu.names(i));
357 		QString lname = name.toLower();
358 		qhIDCache.insert(lname, pid);
359 		qhNameCache.insert(pid, name);
360 
361 		if (qhNameWait.contains(lname)) {
362 			int tid = qhNameWait.take(lname);
363 
364 			foreach(ChanACL *acl, qlACLs)
365 				if (acl->iUserId == tid)
366 					acl->iUserId = pid;
367 			foreach(ACLGroup *gp, qlGroups) {
368 				if (gp->qsAdd.remove(tid))
369 					gp->qsAdd.insert(pid);
370 				if (gp->qsRemove.remove(tid))
371 					gp->qsRemove.insert(pid);
372 			}
373 			qhNameCache.remove(tid);
374 		}
375 	}
376 	refillGroupInherit();
377 	refillGroupRemove();
378 	refillGroupAdd();
379 	refillComboBoxes();
380 	refillACL();
381 }
382 
refill(WaitID wid)383 void ACLEditor::refill(WaitID wid) {
384 	switch (wid) {
385 		case ACLList:
386 			refillACL();
387 			break;
388 		case GroupInherit:
389 			refillGroupInherit();
390 			break;
391 		case GroupRemove:
392 			refillGroupRemove();
393 			break;
394 		case GroupAdd:
395 			refillGroupAdd();
396 			break;
397 	}
398 }
399 
refillComboBoxes()400 void ACLEditor::refillComboBoxes() {
401 	QList<QComboBox *> ql;
402 	ql << qcbGroupAdd;
403 	ql << qcbGroupRemove;
404 	ql << qcbACLUser;
405 
406 	QStringList names = qhNameCache.values();
407 	names.sort();
408 
409 	foreach(QComboBox *qcb, ql) {
410 		qcb->clear();
411 		qcb->addItems(names);
412 		qcb->clearEditText();
413 	}
414 }
415 
refillACL()416 void ACLEditor::refillACL() {
417 	int idx = qlwACLs->currentRow();
418 	bool previousinherit = bInheritACL;
419 	bInheritACL = qcbACLInherit->isChecked();
420 
421 	qlwACLs->clear();
422 
423 	bool first = true;
424 
425 	foreach(ChanACL *acl, qlACLs) {
426 		if (first)
427 			first = false;
428 		else if (! bInheritACL && acl->bInherited)
429 			continue;
430 		QString text;
431 		if (acl->iUserId == -1)
432 			text = QString::fromLatin1("@%1").arg(acl->qsGroup);
433 		else
434 			text = userName(acl->iUserId);
435 		QListWidgetItem *item = new QListWidgetItem(text, qlwACLs);
436 		if (acl->bInherited) {
437 			QFont f = item->font();
438 			f.setItalic(true);
439 			item->setFont(f);
440 		}
441 	}
442 	if (bInheritACL && ! previousinherit && (idx != 0))
443 		idx += numInheritACL;
444 	if (! bInheritACL && previousinherit)
445 		idx -= numInheritACL;
446 
447 	qlwACLs->setCurrentRow(idx);
448 }
449 
refillGroupNames()450 void ACLEditor::refillGroupNames() {
451 	QString text = qcbGroupList->currentText().toLower();
452 	QStringList qsl;
453 
454 	foreach(ACLGroup *gp, qlGroups) {
455 		qsl << gp->qsName;
456 	}
457 	qsl.sort();
458 
459 	qcbGroupList->clear();
460 
461 	foreach(QString name, qsl) {
462 		qcbGroupList->addItem(name);
463 	}
464 
465 	int wantindex = qcbGroupList->findText(text, Qt::MatchFixedString);
466 	qcbGroupList->setCurrentIndex(wantindex);
467 }
468 
currentGroup()469 ACLGroup *ACLEditor::currentGroup() {
470 	QString group = qcbGroupList->currentText();
471 
472 	foreach(ACLGroup *gp, qlGroups)
473 		if (gp->qsName == group)
474 			return gp;
475 
476 	group = group.toLower();
477 
478 	foreach(ACLGroup *gp, qlGroups)
479 		if (gp->qsName == group)
480 			return gp;
481 
482 	return NULL;
483 }
484 
currentACL()485 ChanACL *ACLEditor::currentACL() {
486 	int idx = qlwACLs->currentRow();
487 	if (idx < 0)
488 		return NULL;
489 
490 	if (idx && ! bInheritACL)
491 		idx += numInheritACL;
492 	return qlACLs[idx];
493 }
494 
fillWidgetFromSet(QListWidget * qlw,const QSet<int> & qs)495 void ACLEditor::fillWidgetFromSet(QListWidget *qlw, const QSet<int> &qs) {
496 	qlw->clear();
497 
498 	QList<idname> ql;
499 	foreach(int pid, qs) {
500 		ql << idname(userName(pid), pid);
501 	}
502 	qStableSort(ql);
503 	foreach(idname i, ql) {
504 		QListWidgetItem *qlwi = new QListWidgetItem(i.first, qlw);
505 		qlwi->setData(Qt::UserRole, i.second);
506 		if (i.second < 0) {
507 			QFont f = qlwi->font();
508 			f.setItalic(true);
509 			qlwi->setFont(f);
510 		}
511 	}
512 }
513 
refillGroupAdd()514 void ACLEditor::refillGroupAdd() {
515 	ACLGroup *gp = currentGroup();
516 
517 	if (! gp)
518 		return;
519 
520 	fillWidgetFromSet(qlwGroupAdd, gp->qsAdd);
521 }
522 
refillGroupRemove()523 void ACLEditor::refillGroupRemove() {
524 	ACLGroup *gp = currentGroup();
525 	if (! gp)
526 		return;
527 
528 	fillWidgetFromSet(qlwGroupRemove, gp->qsRemove);
529 }
530 
refillGroupInherit()531 void ACLEditor::refillGroupInherit() {
532 	ACLGroup *gp = currentGroup();
533 
534 	if (! gp)
535 		return;
536 
537 	fillWidgetFromSet(qlwGroupInherit, gp->qsTemporary);
538 }
539 
groupEnableCheck()540 void ACLEditor::groupEnableCheck() {
541 	ACLGroup *gp = currentGroup();
542 
543 	bool enabled;
544 	if (! gp)
545 		enabled = false;
546 	else
547 		enabled = gp->bInherit;
548 
549 	qlwGroupRemove->setEnabled(enabled);
550 	qlwGroupInherit->setEnabled(enabled);
551 	qcbGroupRemove->setEnabled(enabled);
552 	qpbGroupRemoveAdd->setEnabled(enabled);
553 	qpbGroupRemoveRemove->setEnabled(enabled);
554 	qpbGroupInheritRemove->setEnabled(enabled);
555 
556 	enabled = (gp != NULL);
557 	qlwGroupAdd->setEnabled(enabled);
558 	qcbGroupAdd->setEnabled(enabled);
559 	qpbGroupAddAdd->setEnabled(enabled);
560 	qpbGroupAddRemove->setEnabled(enabled);
561 	qcbGroupInherit->setEnabled(enabled);
562 	qcbGroupInheritable->setEnabled(enabled);
563 
564 	if (gp) {
565 		qcbGroupInherit->setChecked(gp->bInherit);
566 		qcbGroupInheritable->setChecked(gp->bInheritable);
567 		qcbGroupInherited->setChecked(gp->bInherited);
568 	}
569 }
570 
ACLEnableCheck()571 void ACLEditor::ACLEnableCheck() {
572 	ChanACL *as = currentACL();
573 
574 	bool enabled;
575 	if (! as)
576 		enabled = false;
577 	else
578 		enabled = ! as->bInherited;
579 
580 	qpbACLRemove->setEnabled(enabled);
581 	qpbACLUp->setEnabled(enabled);
582 	qpbACLDown->setEnabled(enabled);
583 	qcbACLApplyHere->setEnabled(enabled);
584 	qcbACLApplySubs->setEnabled(enabled);
585 	qcbACLGroup->setEnabled(enabled);
586 	qcbACLUser->setEnabled(enabled);
587 
588 	for (int idx = 0; idx < qlACLAllow.count(); idx++) {
589 		// Only enable other checkboxes if writeacl isn't set
590 		bool enablethis = enabled && (qlPerms[idx] == ChanACL::Write || !(as && (as->pAllow & ChanACL::Write)) || qlPerms[idx] == ChanACL::Speak);
591 		qlACLAllow[idx]->setEnabled(enablethis);
592 		qlACLDeny[idx]->setEnabled(enablethis);
593 	}
594 
595 	if (as) {
596 		qcbACLApplyHere->setChecked(as->bApplyHere);
597 		qcbACLApplySubs->setChecked(as->bApplySubs);
598 
599 		for (int idx = 0; idx < qlACLAllow.count(); idx++) {
600 			ChanACL::Perm p = qlPerms[idx];
601 			qlACLAllow[idx]->setChecked(as->pAllow & p);
602 			qlACLDeny[idx]->setChecked(as->pDeny & p);
603 		}
604 
605 		qcbACLGroup->clear();
606 		qcbACLGroup->addItem(QString());
607 		qcbACLGroup->addItem(QLatin1String("all"));
608 		qcbACLGroup->addItem(QLatin1String("auth"));
609 		qcbACLGroup->addItem(QLatin1String("in"));
610 		qcbACLGroup->addItem(QLatin1String("sub"));
611 		qcbACLGroup->addItem(QLatin1String("out"));
612 		qcbACLGroup->addItem(QLatin1String("~in"));
613 		qcbACLGroup->addItem(QLatin1String("~sub"));
614 		qcbACLGroup->addItem(QLatin1String("~out"));
615 
616 		foreach(ACLGroup *gs, qlGroups)
617 			qcbACLGroup->addItem(gs->qsName);
618 
619 		if (as->iUserId == -1) {
620 			qcbACLUser->clearEditText();
621 			qcbACLGroup->addItem(as->qsGroup);
622 			qcbACLGroup->setCurrentIndex(qcbACLGroup->findText(as->qsGroup, Qt::MatchExactly));
623 		} else {
624 			qcbACLUser->setEditText(userName(as->iUserId));
625 		}
626 	}
627 	foreach(QAbstractButton *b, qdbbButtons->buttons()) {
628 		QPushButton *qpb = qobject_cast<QPushButton *>(b);
629 		if (qpb) {
630 			qpb->setAutoDefault(false);
631 			qpb->setDefault(false);
632 		}
633 	}
634 }
635 
on_qtwTab_currentChanged(int index)636 void ACLEditor::on_qtwTab_currentChanged(int index) {
637 	if (index == 0) {
638 		// Switched to property tab, update password field
639 		updatePasswordField();
640 	} else if (index == 2) {
641 		// Switched to ACL tab, update ACL list
642 		updatePasswordACL();
643 		refillACL();
644 	}
645 }
646 
updatePasswordField()647 void ACLEditor::updatePasswordField() {
648 	pcaPassword = NULL;
649 	foreach(ChanACL *acl, qlACLs) {
650 		// Check for sth that applies to '#<something>' AND grants 'Enter' AND may grant 'Speak', 'Whisper',
651 		// 'TextMessage', 'Link' but NOTHING else AND does not deny anything, then '<something>' is the password.
652 		if (acl->qsGroup.startsWith(QLatin1Char('#')) &&
653 		        acl->bApplyHere &&
654 		        !acl->bInherited &&
655 		        (acl->pAllow & ChanACL::Enter) &&
656 		        (acl->pAllow == (ChanACL::Enter | ChanACL::Speak | ChanACL::Whisper | ChanACL::TextMessage | ChanACL::LinkChannel) || // Backwards compat with old behaviour that didn't deny traverse
657 		         acl->pAllow == (ChanACL::Enter | ChanACL::Speak | ChanACL::Whisper | ChanACL::TextMessage | ChanACL::LinkChannel | ChanACL::Traverse)) &&
658 		        acl->pDeny == ChanACL::None) {
659 			pcaPassword = acl;
660 		}
661 	}
662 	if (pcaPassword)
663 		qleChannelPassword->setText(pcaPassword->qsGroup.mid(1));
664 	else
665 		qleChannelPassword->clear();
666 
667 }
668 
updatePasswordACL()669 void ACLEditor::updatePasswordACL() {
670 	if (qleChannelPassword->text().isEmpty()) {
671 		// Remove the password if we had one to begin with
672 		if (pcaPassword && qlACLs.removeOne(pcaPassword)) {
673 			delete pcaPassword;
674 
675 			// Search and remove the @all deny ACL
676 			ChanACL *denyall = NULL;
677 			foreach(ChanACL *acl, qlACLs) {
678 				if (acl->qsGroup == QLatin1String("all") &&
679 				        acl->bInherited == false &&
680 				        acl->bApplyHere == true &&
681 				        acl->pAllow == ChanACL::None &&
682 				        (acl->pDeny == (ChanACL::Enter | ChanACL::Speak | ChanACL::Whisper | ChanACL::TextMessage | ChanACL::LinkChannel) || // Backwards compat with old behaviour that didn't deny traverse
683 				         acl->pDeny == (ChanACL::Enter | ChanACL::Speak | ChanACL::Whisper | ChanACL::TextMessage | ChanACL::LinkChannel | ChanACL::Traverse))) {
684 					denyall = acl;
685 				}
686 			}
687 			if (denyall) {
688 				qlACLs.removeOne(denyall);
689 				delete denyall;
690 			}
691 		}
692 	} else {
693 		// Add or Update
694 		if (pcaPassword == NULL || !qlACLs.contains(pcaPassword)) {
695 			pcaPassword = new ChanACL(NULL);
696 			pcaPassword->bApplyHere = true;
697 			pcaPassword->bApplySubs = false;
698 			pcaPassword->bInherited = false;
699 			pcaPassword->pAllow = ChanACL::None;
700 			pcaPassword->pDeny = ChanACL::Enter | ChanACL::Speak | ChanACL::Whisper | ChanACL::TextMessage | ChanACL::LinkChannel | ChanACL::Traverse;
701 			pcaPassword->qsGroup = QLatin1String("all");
702 			qlACLs << pcaPassword;
703 
704 			pcaPassword = new ChanACL(NULL);
705 			pcaPassword->bApplyHere = true;
706 			pcaPassword->bApplySubs = false;
707 			pcaPassword->bInherited = false;
708 			pcaPassword->pAllow = ChanACL::Enter | ChanACL::Speak | ChanACL::Whisper | ChanACL::TextMessage | ChanACL::LinkChannel | ChanACL::Traverse;
709 			pcaPassword->pDeny = ChanACL::None;
710 			pcaPassword->qsGroup = QString(QLatin1String("#%1")).arg(qleChannelPassword->text());
711 			qlACLs << pcaPassword;
712 		} else {
713 			pcaPassword->qsGroup = QString(QLatin1String("#%1")).arg(qleChannelPassword->text());
714 		}
715 	}
716 }
717 
on_qlwACLs_currentRowChanged()718 void ACLEditor::on_qlwACLs_currentRowChanged() {
719 	ACLEnableCheck();
720 }
721 
on_qpbACLAdd_clicked()722 void ACLEditor::on_qpbACLAdd_clicked() {
723 	ChanACL *as = new ChanACL(NULL);
724 	as->bApplyHere = true;
725 	as->bApplySubs = true;
726 	as->bInherited = false;
727 	as->qsGroup = QLatin1String("all");
728 	as->iUserId = -1;
729 	as->pAllow = ChanACL::None;
730 	as->pDeny = ChanACL::None;
731 	qlACLs << as;
732 	refillACL();
733 	qlwACLs->setCurrentRow(qlwACLs->count() - 1);
734 }
735 
on_qpbACLRemove_clicked()736 void ACLEditor::on_qpbACLRemove_clicked() {
737 	ChanACL *as = currentACL();
738 	if (! as || as->bInherited)
739 		return;
740 
741 	qlACLs.removeAll(as);
742 	delete as;
743 	refillACL();
744 }
745 
on_qpbACLUp_clicked()746 void ACLEditor::on_qpbACLUp_clicked() {
747 	ChanACL *as = currentACL();
748 	if (! as || as->bInherited)
749 		return;
750 
751 	int idx = qlACLs.indexOf(as);
752 	if (idx <= numInheritACL + 1)
753 		return;
754 
755 	qlACLs.swap(idx - 1, idx);
756 	qlwACLs->setCurrentRow(qlwACLs->currentRow() - 1);
757 	refillACL();
758 }
759 
on_qpbACLDown_clicked()760 void ACLEditor::on_qpbACLDown_clicked() {
761 	ChanACL *as = currentACL();
762 	if (! as || as->bInherited)
763 		return;
764 
765 	int idx = qlACLs.indexOf(as) + 1;
766 	if (idx >= qlACLs.count())
767 		return;
768 
769 	qlACLs.swap(idx - 1, idx);
770 	qlwACLs->setCurrentRow(qlwACLs->currentRow() + 1);
771 	refillACL();
772 }
773 
on_qcbACLInherit_clicked(bool)774 void ACLEditor::on_qcbACLInherit_clicked(bool) {
775 	refillACL();
776 }
777 
on_qcbACLApplyHere_clicked(bool checked)778 void ACLEditor::on_qcbACLApplyHere_clicked(bool checked) {
779 	ChanACL *as = currentACL();
780 	if (! as || as->bInherited)
781 		return;
782 
783 	as->bApplyHere = checked;
784 }
785 
on_qcbACLApplySubs_clicked(bool checked)786 void ACLEditor::on_qcbACLApplySubs_clicked(bool checked) {
787 	ChanACL *as = currentACL();
788 	if (! as || as->bInherited)
789 		return;
790 
791 	as->bApplySubs = checked;
792 }
793 
on_qcbACLGroup_activated(const QString & text)794 void ACLEditor::on_qcbACLGroup_activated(const QString &text) {
795 	ChanACL *as = currentACL();
796 	if (! as || as->bInherited)
797 		return;
798 
799 	as->iUserId = -1;
800 
801 	if (text.isEmpty()) {
802 		qcbACLGroup->setCurrentIndex(1);
803 		as->qsGroup = QLatin1String("all");
804 	} else {
805 		qcbACLUser->clearEditText();
806 		as->qsGroup = text;
807 	}
808 	refillACL();
809 }
810 
on_qcbACLUser_activated()811 void ACLEditor::on_qcbACLUser_activated() {
812 	QString text = qcbACLUser->currentText();
813 
814 	ChanACL *as = currentACL();
815 	if (! as || as->bInherited)
816 		return;
817 
818 	if (text.isEmpty()) {
819 		as->iUserId = -1;
820 		if (qcbACLGroup->currentIndex() == 0) {
821 			qcbACLGroup->setCurrentIndex(1);
822 			as->qsGroup = QLatin1String("all");
823 		}
824 		refillACL();
825 	} else {
826 		qcbACLGroup->setCurrentIndex(0);
827 		as->iUserId = id(text);
828 		refillACL();
829 	}
830 }
831 
ACLPermissions_clicked()832 void ACLEditor::ACLPermissions_clicked() {
833 	QCheckBox *source = qobject_cast<QCheckBox *>(sender());
834 
835 	ChanACL *as = currentACL();
836 	if (! as || as->bInherited)
837 		return;
838 
839 	int allowed = 0;
840 	int denied = 0;
841 
842 	bool enabled = true;
843 	for (int idx = 0; idx < qlACLAllow.count(); idx++) {
844 		ChanACL::Perm p = qlPerms[idx];
845 		if (qlACLAllow[idx]->isChecked() && qlACLDeny[idx]->isChecked()) {
846 			if (source == qlACLAllow[idx])
847 				qlACLDeny[idx]->setChecked(false);
848 			else
849 				qlACLAllow[idx]->setChecked(false);
850 		}
851 
852 		qlACLAllow[idx]->setEnabled(enabled || p == ChanACL::Speak);
853 		qlACLDeny[idx]->setEnabled(enabled || p == ChanACL::Speak);
854 
855 		if (p == ChanACL::Write && qlACLAllow[idx]->isChecked())
856 			enabled = false;
857 
858 		if (qlACLAllow[idx]->isChecked())
859 			allowed |= p;
860 		if (qlACLDeny[idx]->isChecked())
861 			denied |= p;
862 	}
863 
864 	as->pAllow = static_cast<ChanACL::Permissions>(allowed);
865 	as->pDeny = static_cast<ChanACL::Permissions>(denied);
866 }
867 
on_qcbGroupList_activated(const QString & text)868 void ACLEditor::on_qcbGroupList_activated(const QString &text) {
869 	ACLGroup *gs = currentGroup();
870 	if (text.isEmpty())
871 		return;
872 	if (! gs) {
873 		QString name = text.toLower();
874 		gs = new ACLGroup(name);
875 		gs->bInherited = false;
876 		gs->bInherit = true;
877 		gs->bInheritable = true;
878 		gs->qsName = name;
879 		qlGroups << gs;
880 	}
881 
882 	refillGroupNames();
883 	refillGroupAdd();
884 	refillGroupRemove();
885 	refillGroupInherit();
886 	groupEnableCheck();
887 	qpbGroupAdd->setEnabled(false);
888 }
889 
on_qcbGroupList_editTextChanged(const QString & text)890 void ACLEditor::on_qcbGroupList_editTextChanged(const QString & text) {
891 	qpbGroupAdd->setEnabled(!text.isEmpty());
892 }
893 
on_qpbGroupAdd_clicked()894 void ACLEditor::on_qpbGroupAdd_clicked() {
895 	on_qcbGroupList_activated(qcbGroupList->currentText());
896 }
897 
on_qpbGroupRemove_clicked()898 void ACLEditor::on_qpbGroupRemove_clicked() {
899 	ACLGroup *gs = currentGroup();
900 	if (! gs)
901 		return;
902 
903 	if (gs->bInherited) {
904 		gs->bInheritable = true;
905 		gs->bInherit = true;
906 		gs->qsAdd.clear();
907 		gs->qsRemove.clear();
908 	} else {
909 		qlGroups.removeAll(gs);
910 		delete gs;
911 	}
912 	refillGroupNames();
913 	refillGroupAdd();
914 	refillGroupRemove();
915 	refillGroupInherit();
916 	groupEnableCheck();
917 }
918 
on_qcbGroupInherit_clicked(bool checked)919 void ACLEditor::on_qcbGroupInherit_clicked(bool checked) {
920 	ACLGroup *gs = currentGroup();
921 	if (! gs)
922 		return;
923 
924 	gs->bInherit = checked;
925 	groupEnableCheck();
926 }
927 
on_qcbGroupInheritable_clicked(bool checked)928 void ACLEditor::on_qcbGroupInheritable_clicked(bool checked) {
929 	ACLGroup *gs = currentGroup();
930 	if (! gs)
931 		return;
932 
933 	gs->bInheritable = checked;
934 }
935 
on_qpbGroupAddAdd_clicked()936 void ACLEditor::on_qpbGroupAddAdd_clicked() {
937 	ACLGroup *gs = currentGroup();
938 	QString text = qcbGroupAdd->currentText();
939 
940 	if (! gs)
941 		return;
942 
943 	if (text.isEmpty())
944 		return;
945 
946 	gs->qsAdd << id(text);
947 	refillGroupAdd();
948 	qcbGroupAdd->clearEditText();
949 }
950 
on_qpbGroupAddRemove_clicked()951 void ACLEditor::on_qpbGroupAddRemove_clicked() {
952 	ACLGroup *gs = currentGroup();
953 	if (! gs)
954 		return;
955 
956 	QListWidgetItem *item = qlwGroupAdd->currentItem();
957 	if (! item)
958 		return;
959 
960 	gs->qsAdd.remove(item->data(Qt::UserRole).toInt());
961 	refillGroupAdd();
962 	qcbGroupRemove->clearEditText();
963 }
964 
on_qpbGroupRemoveAdd_clicked()965 void ACLEditor::on_qpbGroupRemoveAdd_clicked() {
966 	QString text = qcbGroupRemove->currentText();
967 
968 	ACLGroup *gs = currentGroup();
969 	if (! gs)
970 		return;
971 
972 	if (text.isEmpty())
973 		return;
974 
975 	gs->qsRemove << id(text);
976 	refillGroupRemove();
977 }
978 
on_qpbGroupRemoveRemove_clicked()979 void ACLEditor::on_qpbGroupRemoveRemove_clicked() {
980 	ACLGroup *gs = currentGroup();
981 	if (! gs)
982 		return;
983 
984 	QListWidgetItem *item = qlwGroupRemove->currentItem();
985 	if (! item)
986 		return;
987 
988 	gs->qsRemove.remove(item->data(Qt::UserRole).toInt());
989 	refillGroupRemove();
990 }
991 
on_qpbGroupInheritRemove_clicked()992 void ACLEditor::on_qpbGroupInheritRemove_clicked() {
993 	ACLGroup *gs = currentGroup();
994 	if (! gs)
995 		return;
996 
997 	QListWidgetItem *item = qlwGroupInherit->currentItem();
998 	if (! item)
999 		return;
1000 
1001 	gs->qsRemove.insert(item->data(Qt::UserRole).toInt());
1002 	refillGroupRemove();
1003 }
1004