Project

General

Profile

Download (42.8 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
 * firewall_rules.php
4
 *
5
 * part of pfSense (https://www.pfsense.org)
6
 * Copyright (c) 2004-2013 BSD Perimeter
7
 * Copyright (c) 2013-2016 Electric Sheep Fencing
8
 * Copyright (c) 2014-2023 Rubicon Communications, LLC (Netgate)
9
 * All rights reserved.
10
 *
11
 * originally based on m0n0wall (http://m0n0.ch/wall)
12
 * Copyright (c) 2003-2004 Manuel Kasper <mk@neon1.net>.
13
 * All rights reserved.
14
 *
15
 * Licensed under the Apache License, Version 2.0 (the "License");
16
 * you may not use this file except in compliance with the License.
17
 * You may obtain a copy of the License at
18
 *
19
 * http://www.apache.org/licenses/LICENSE-2.0
20
 *
21
 * Unless required by applicable law or agreed to in writing, software
22
 * distributed under the License is distributed on an "AS IS" BASIS,
23
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
24
 * See the License for the specific language governing permissions and
25
 * limitations under the License.
26
 */
27

    
28
##|+PRIV
29
##|*IDENT=page-firewall-rules
30
##|*NAME=Firewall: Rules
31
##|*DESCR=Allow access to the 'Firewall: Rules' page.
32
##|*MATCH=firewall_rules.php*
33
##|-PRIV
34

    
35
require_once("guiconfig.inc");
36
require_once("functions.inc");
37
require_once("filter.inc");
38
require_once("ipsec.inc");
39
require_once("shaper.inc");
40

    
41
$XmoveTitle = gettext("Move checked rules above this one. Shift+Click to move checked rules below.");
42
$ShXmoveTitle = gettext("Move checked rules below this one. Release shift to move checked rules above.");
43

    
44
$shortcut_section = "firewall";
45

    
46
function get_pf_rules($rules, $tracker_start, $tracker_end) {
47

    
48
	if ($rules == NULL || !is_array($rules)) {
49
		return (NULL);
50
	}
51

    
52
	$arr = array();
53
	foreach ($rules as $key => $rule) {
54
		/* key 'error' indicates a call to pfctl_get_rule() returned
55
		 * nonzero. We may have a partial list of rules if that is the case */
56
		if ($key != 'error' &&
57
			$rule['tracker'] >= $tracker_start &&
58
		    $rule['tracker'] <= $tracker_end) {
59
			$arr[] = $rule;
60
		}
61
	}
62

    
63
	if (count($arr) == 0)
64
		return (NULL);
65

    
66
	return ($arr);
67
}
68

    
69
function print_states($tracker_start, $tracker_end = -1) {
70
	global $rulescnt;
71

    
72
	if (empty($tracker_start)) {
73
		return;
74
	}
75

    
76
	if ($tracker_end === -1) {
77
		$tracker_end = $tracker_start;
78
	} elseif ($tracker_end < $tracker_start) {
79
		return;
80
	}
81

    
82
	$rulesid = "";
83
	$bytes = 0;
84
	$states = 0;
85
	$packets = 0;
86
	$evaluations = 0;
87
	$stcreations = 0;
88
	$rules = get_pf_rules($rulescnt, $tracker_start, $tracker_end);
89
	if (is_array($rules)) {
90
		foreach ($rules as $rule) {
91
			$bytes += $rule['bytes'];
92
			$states += $rule['states'];
93
			$packets += $rule['packets'];
94
			$evaluations += $rule['evaluations'];
95
			$stcreations += $rule['state creations'];
96
			if (strlen($rulesid) > 0) {
97
				$rulesid .= ",";
98
			}
99
			$rulesid .= "{$rule['id']}";
100
		}
101
	}
102

    
103
	$trackertext = "Tracking ID: {$tracker_start}";
104
	if ($tracker_end != $tracker_start) {
105
		$trackertext .= '-' . $tracker_end;
106
	}
107
	$trackertext .= "<br />";
108

    
109
	printf("<a href=\"diag_dump_states.php?ruleid=%s\" " .
110
	    "data-toggle=\"popover\" data-trigger=\"hover focus\" " .
111
	    "title=\"%s\" ", $rulesid, gettext("States details"));
112
	printf("data-content=\"{$trackertext}evaluations: %s<br />packets: " .
113
	    "%s<br />bytes: %s<br />states: %s<br />state creations: " .
114
	    "%s\" data-html=\"true\" usepost>",
115
	    format_number($evaluations), format_number($packets),
116
	    format_bytes($bytes), format_number($states),
117
	    format_number($stcreations));
118
	printf("%s/%s</a><br />", format_number($states), format_bytes($bytes));
119
}
120

    
121
function delete_nat_association($id) {
122
	global $config;
123

    
124
	if (!$id || !is_array($config['nat']['rule'])) {
125
		return;
126
	}
127

    
128
	$a_nat = &$config['nat']['rule'];
129

    
130
	foreach ($a_nat as &$natent) {
131
		if ($natent['associated-rule-id'] == $id) {
132
			$natent['associated-rule-id'] = '';
133
		}
134
	}
135
}
136

    
137
init_config_arr(array('filter', 'rule'));
138
filter_rules_sort();
139
$a_filter = &$config['filter']['rule'];
140

    
141
if ($_REQUEST['if']) {
142
	$if = $_REQUEST['if'];
143
}
144

    
145
$ifdescs = get_configured_interface_with_descr();
146

    
147
$iflist = filter_get_interface_list();
148

    
149
if (!$if || !isset($iflist[$if])) {
150
	if ($if != 'any' &&
151
	    $if != 'EthernetRules' &&
152
	    $if != 'FloatingRules') {
153
		/* default to the first configured interface */
154
		$if = array_key_first($ifdescs);
155
	}
156
}
157

    
158
if ($_POST['apply']) {
159
	$retval = 0;
160
	$retval |= filter_configure();
161

    
162
	clear_subsystem_dirty('filter');
163
}
164

    
165
if ($_POST['act'] == "del") {
166
	if ($a_filter[$_POST['id']]) {
167
		if (!empty($a_filter[$_POST['id']]['associated-rule-id'])) {
168
			delete_nat_association($a_filter[$_POST['id']]['associated-rule-id']);
169
		}
170
		unset($a_filter[$_POST['id']]);
171

    
172
		// Update the separators
173
		init_config_arr(array('filter', 'separator', strtolower($if)));
174
		$a_separators = &$config['filter']['separator'][strtolower($if)];
175
		$ridx = abs(ifridx($if, $_POST['id']));	// get rule index within interface
176
		$mvnrows = -1;
177
		move_separators($a_separators, $ridx, $mvnrows);
178

    
179
		if (write_config(gettext("Firewall: Rules - deleted a firewall rule."))) {
180
			mark_subsystem_dirty('filter');
181
		}
182

    
183
		header("Location: firewall_rules.php?if=" . htmlspecialchars($if));
184
		exit;
185
	}
186
}
187

    
188
if (($_POST['act'] == 'killid') &&
189
    (!empty($_POST['tracker'])) &&
190
    (!empty($if))) {
191
	mwexec("/sbin/pfctl -k label -k " . escapeshellarg("id:{$_POST['tracker']}"));
192
	header("Location: firewall_rules.php?if=" . htmlspecialchars($if));
193
	exit;
194
}
195

    
196
// Handle save msg if defined
197
if ($_REQUEST['savemsg']) {
198
	$savemsg = htmlentities($_REQUEST['savemsg']);
199
}
200

    
201
if (isset($_POST['del_x'])) {
202
	if (is_array($_POST['rule']) && count($_POST['rule'])) {
203
		init_config_arr(array('filter', 'separator', strtolower($if)));
204
		$a_separators = &$config['filter']['separator'][strtolower($if)];
205

    
206
		$num_deleted = 0;
207

    
208
		foreach ($_POST['rule'] as $rulei) {
209
			delete_nat_association($a_filter[$rulei]['associated-rule-id']);
210
			unset($a_filter[$rulei]);
211

    
212
			// Update the separators
213
			// As rules are deleted, $ridx has to be decremented or separator position will break
214
			$ridx = ifridx($if, $rulei);
215
			if (count($_POST['rule']) != 1) {
216
				$ridx = ($ridx < 0) ? abs($ridx) : $ridx - $num_deleted;
217
			}
218

    
219
			$mvnrows = -1;
220
			move_separators($a_separators, $ridx, $mvnrows);
221

    
222
			$num_deleted++;
223
		}
224

    
225
		if ($num_deleted) {
226
			if (write_config(gettext("Firewall: Rules - deleted selected firewall rules."))) {
227
				mark_subsystem_dirty('filter');
228
			}
229
		}
230

    
231
		header("Location: firewall_rules.php?if=" . htmlspecialchars($if));
232
		exit;
233
	}
234
} elseif (isset($_POST['toggle_x'])) {
235
	if (is_array($_POST['rule']) && count($_POST['rule'])) {
236
		foreach ($_POST['rule'] as $rulei) {
237
			if (isset($a_filter[$rulei]['disabled'])) {
238
				unset($a_filter[$rulei]['disabled']);
239
			} else {
240
				$a_filter[$rulei]['disabled'] = true;
241
			}
242
		}
243
		if (write_config(gettext("Firewall: Rules - toggle selected firewall rules."))) {
244
			mark_subsystem_dirty('filter');
245
		}
246

    
247
		header("Location: firewall_rules.php?if=" . htmlspecialchars($if));
248
		exit;
249
	}
250
} else if ($_POST['act'] == "toggle") {
251
	if ($a_filter[$_POST['id']]) {
252
		if (isset($a_filter[$_POST['id']]['disabled'])) {
253
			unset($a_filter[$_POST['id']]['disabled']);
254
			$wc_msg = gettext('Firewall: Rules - enabled a firewall rule.');
255
		} else {
256
			$a_filter[$_POST['id']]['disabled'] = true;
257
			$wc_msg = gettext('Firewall: Rules - disabled a firewall rule.');
258
		}
259
		if (write_config($wc_msg)) {
260
			mark_subsystem_dirty('filter');
261
		}
262

    
263
		header("Location: firewall_rules.php?if=" . htmlspecialchars($if));
264
		exit;
265
	}
266
} else if ($_POST['order-store']) {
267
	$updated = false;
268
	$dirty = false;
269

    
270
	/* update rule order, POST[rule] is an array of ordered IDs */
271
	if (is_array($_POST['rule']) && !empty($_POST['rule'])) {
272
		$a_filter_new = array();
273

    
274
		// Include the rules of other interfaces listed in config before this (the selected) interface.
275
		$filteri_before = null;
276
		foreach ($a_filter as $idx => $filterent) {
277
			if (($filterent['interface'] == $if && !isset($filterent['floating'])) || (isset($filterent['floating']) && "FloatingRules" == $if)) {
278
				$filteri_before = $idx;
279
				break;
280
			} else {
281
				$a_filter_new[] = $filterent;
282
			}
283
		}
284

    
285
		// Include the rules of this (the selected) interface.
286
		// If a rule is not in POST[rule], it has been deleted by the user
287
		foreach ($_POST['rule'] as $id) {
288
			$a_filter_new[] = $a_filter[$id];
289
		}
290

    
291
		// Include the rules of other interfaces listed in config after this (the selected) interface.
292
		foreach ($a_filter as $filteri_after => $filterent) {
293
			if ($filteri_before > $filteri_after) {
294
				continue;
295
			}
296
			if (($filterent['interface'] == $if && !isset($filterent['floating'])) || (isset($filterent['floating']) && "FloatingRules" == $if)) {
297
				continue;
298
			} else {
299
				$a_filter_new[] = $filterent;
300
			}
301
		}
302

    
303
		if ($a_filter !== $a_filter_new) {
304
			$a_filter = $a_filter_new;
305
			$dirty = true;
306
		}
307
	}
308

    
309
	$a_separators = &$config['filter']['separator'][strtolower($if)];
310

    
311
	/* update separator order, POST[separator] is an array of ordered IDs */
312
	if (is_array($_POST['separator']) && !empty($_POST['separator'])) {
313
		$new_separator = array();
314
		$idx = 0;
315

    
316
		foreach ($_POST['separator'] as $separator) {
317
			$new_separator['sep' . $idx++] = $separator;
318
		}
319

    
320
		if ($a_separators !== $new_separator) {
321
			$a_separators = $new_separator;
322
			$updated = true;
323
		}
324
	} else if (!empty($a_separators)) {
325
		$a_separators = "";
326
		$updated = true;
327
	}
328

    
329
	if ($updated || $dirty) {
330
		if (write_config(gettext("Firewall: Rules - reordered firewall rules."))) {
331
			if ($dirty) {
332
				mark_subsystem_dirty('filter');
333
			}
334
		}
335
	}
336

    
337
	header("Location: firewall_rules.php?if=" . htmlspecialchars($if));
338
	exit;
339
} elseif (isset($_POST['dstif']) && !empty($_POST['dstif']) &&
340
    isset($iflist[$_POST['dstif']]) && have_ruleint_access($_POST['dstif']) &&
341
    is_array($_POST['rule']) && count($_POST['rule'])) {
342
	$confiflist = get_configured_interface_list();
343
	/* Use this as a starting point and increase as we go, otherwise if the
344
	 * loop runs fast there can be duplicates.
345
	 * https://redmine.pfsense.org/issues/13507 */
346
	$tracker = (int)microtime(true);
347
	foreach ($_POST['rule'] as $rulei) {
348
		$filterent = $a_filter[$rulei];
349
		$filterent['tracker'] = $tracker++;
350
		$filterent['interface'] = $_POST['dstif'];
351
		if (($_POST['convertif'] == 'true') && ($if != $_POST['dstif']) &&
352
		    in_array($_POST['dstif'], $confiflist)) {
353
			if (isset($filterent['source']['network']) &&
354
			    ($filterent['source']['network'] == $if)) {
355
				$filterent['source']['network'] = $_POST['dstif'];
356
			}
357
			if (isset($filterent['destination']['network']) &&
358
			    ($filterent['destination']['network'] == $if)) {
359
				$filterent['destination']['network'] = $_POST['dstif'];
360
			}
361
			if (isset($filterent['source']['network']) &&
362
			    ($filterent['source']['network'] == ($if . 'ip'))) {
363
				$filterent['source']['network'] = $_POST['dstif'] . 'ip';
364
			}
365
			if (isset($filterent['destination']['network']) &&
366
			    ($filterent['destination']['network'] == ($if . 'ip'))) {
367
				$filterent['destination']['network'] = $_POST['dstif'] . 'ip';
368
			}
369
		}
370
		$a_filter[] = $filterent;
371
	}
372
	if (write_config(gettext("Firewall: Rules - copying selected firewall rules."))) {
373
		mark_subsystem_dirty('filter');
374
	}
375

    
376
	header("Location: firewall_rules.php?if=" . htmlspecialchars($_POST['dstif']));
377
	exit;
378
}
379

    
380
$tab_array = array(array(gettext("Floating"), ("FloatingRules" == $if), "firewall_rules.php?if=FloatingRules"));
381

    
382
foreach ($iflist as $ifent => $ifname) {
383
	$tab_array[] = array($ifname, ($ifent == $if), "firewall_rules.php?if={$ifent}");
384
}
385

    
386
foreach ($tab_array as $dtab) {
387
	if ($dtab[1]) {
388
		$bctab = $dtab[0];
389
		break;
390
	}
391
}
392

    
393
$pgtitle = array(gettext("Firewall"), gettext("Rules"), $bctab);
394
$pglinks = array("", "firewall_rules.php", "@self");
395
$shortcut_section = "firewall";
396

    
397
include("head.inc");
398
$nrules = 0;
399

    
400
if ($savemsg) {
401
	print_info_box($savemsg, 'success');
402
}
403

    
404
if ($_POST['apply']) {
405
	print_apply_result_box($retval);
406
}
407

    
408
if (is_subsystem_dirty('filter')) {
409
	print_apply_box(gettext("The firewall rule configuration has been changed.") . "<br />" . gettext("The changes must be applied for them to take effect."));
410
}
411

    
412
display_top_tabs($tab_array, false, 'pills');
413

    
414
$showantilockout = false;
415
$showprivate = false;
416
$showblockbogons = false;
417

    
418
if (!isset($config['system']['webgui']['noantilockout']) &&
419
    (((count($config['interfaces']) > 1) && ($if == 'lan')) ||
420
    ((count($config['interfaces']) == 1) && ($if == 'wan')))) {
421
	$showantilockout = true;
422
}
423

    
424
if (isset($config['interfaces'][$if]['blockpriv'])) {
425
	$showprivate = true;
426
}
427

    
428
if (isset($config['interfaces'][$if]['blockbogons'])) {
429
	$showblockbogons = true;
430
}
431

    
432
if (isset($config['system']['webgui']['roworderdragging'])) {
433
	$rules_header_text = gettext("Rules");
434
} else {
435
	$rules_header_text = gettext("Rules (Drag to Change Order)");
436
}
437

    
438
/* Load the counter data of each pf rule. */
439
$rulescnt = pfSense_get_pf_rules();
440

    
441
// Update this if you add or remove columns!
442
$columns_in_table = 13;
443

    
444
/* Floating rules tab has one extra column
445
 * https://redmine.pfsense.org/issues/10667 */
446
if ($if == "FloatingRules") {
447
	$columns_in_table++;
448
}
449

    
450
?>
451
<!-- Allow table to scroll when dragging outside of the display window -->
452
<style>
453
.table-responsive {
454
    clear: both;
455
    overflow-x: visible;
456
    margin-bottom: 0px;
457
}
458
</style>
459

    
460
<form id="mainform" method="post">
461
	<input name="if" id="if" type="hidden" value="<?=$if?>" />
462
	<input name="dstif" id="dstif" type="hidden" value="" />
463
	<input name="convertif" id="convertif" type="hidden" value="" />
464
	<div class="panel panel-default">
465
		<div class="panel-heading"><h2 class="panel-title"><?=$rules_header_text?></h2></div>
466
		<div id="mainarea" class="table-responsive panel-body">
467
			<table id="ruletable" class="table table-hover table-striped table-condensed" style="overflow-x: 'visible'">
468
				<thead>
469
					<tr>
470
						<th><input type="checkbox" id="selectAll" name="selectAll" /></th>
471
						<th><!-- status icons --></th>
472
						<th><?=gettext("States")?></th>
473
				<?php
474
					if ('FloatingRules' == $if) {
475
				?>
476
						<th><?=gettext("Interfaces")?></th>
477
				<?php
478
					}
479
				?>
480
						<th><?=gettext("Protocol")?></th>
481
						<th><?=gettext("Source")?></th>
482
						<th><?=gettext("Port")?></th>
483
						<th><?=gettext("Destination")?></th>
484
						<th><?=gettext("Port")?></th>
485
						<th><?=gettext("Gateway")?></th>
486
						<th><?=gettext("Queue")?></th>
487
						<th><?=gettext("Schedule")?></th>
488
						<th><?=gettext("Description")?></th>
489
						<th><?=gettext("Actions")?></th>
490
					</tr>
491
				</thead>
492

    
493
<?php if ($showblockbogons || $showantilockout || $showprivate) :
494
?>
495
				<tbody>
496
<?php
497
		// Show the anti-lockout rule if it's enabled, and we are on LAN with an if count > 1, or WAN with an if count of 1.
498
		if ($showantilockout):
499
			$alports = implode('<br />', filter_get_antilockout_ports(true));
500
?>
501
					<tr id="antilockout">
502
						<td></td>
503
						<td title="<?=gettext("traffic is passed")?>"><i class="fa fa-check text-success"></i></td>
504
						<td><?php print_states(intval(ANTILOCKOUT_TRACKER_START), intval(ANTILOCKOUT_TRACKER_END)); ?></td>
505
						<td>*</td>
506
						<td>*</td>
507
						<td>*</td>
508
						<td><?=$iflist[$if];?> Address</td>
509
						<td><?=$alports?></td>
510
						<td>*</td>
511
						<td>*</td>
512
						<td></td>
513
						<td><?=gettext("Anti-Lockout Rule");?></td>
514
						<td>
515
							<a href="system_advanced_admin.php" title="<?=gettext("Settings");?>"><i class="fa fa-cog"></i></a>
516
						</td>
517
					</tr>
518
<?php 	endif;?>
519
<?php 	if ($showprivate): ?>
520
					<tr id="private">
521
						<td></td>
522
						<td title="<?=gettext("traffic is blocked")?>"><i class="fa fa-times text-danger"></i></td>
523
						<td><?php print_states(intval(RFC1918_TRACKER_START), intval(RFC1918_TRACKER_END)); ?></td>
524
						<td>*</td>
525
						<td><?=gettext("RFC 1918 networks");?></td>
526
						<td>*</td>
527
						<td>*</td>
528
						<td>*</td>
529
						<td>*</td>
530
						<td>*</td>
531
						<td></td>
532
						<td><?=gettext("Block private networks");?></td>
533
						<td>
534
							<a href="interfaces.php?if=<?=htmlspecialchars($if)?>" title="<?=gettext("Settings");?>" usepost><i class="fa fa-cog"></i></a>
535
						</td>
536
					</tr>
537
<?php 	endif;?>
538
<?php 	if ($showblockbogons): ?>
539
					<tr id="bogons">
540
						<td></td>
541
						<td title="<?=gettext("traffic is blocked")?>"><i class="fa fa-times text-danger"></i></td>
542
						<td><?php print_states(intval(BOGONS_TRACKER_START), intval(BOGONS_TRACKER_END)); ?></td>
543
						<td>*</td>
544
						<td><?=sprintf(gettext("Reserved%sNot assigned by IANA"), "<br />");?></td>
545
						<td>*</td>
546
						<td>*</td>
547
						<td>*</td>
548
						<td>*</td>
549
						<td>*</td>
550
						<td></td>
551
						<td><?=gettext("Block bogon networks");?></td>
552
						<td>
553
							<a href="interfaces.php?if=<?=htmlspecialchars($if)?>" title="<?=gettext("Settings");?>" usepost><i class="fa fa-cog"></i></a>
554
						</td>
555
					</tr>
556
<?php 	endif;?>
557
			</tbody>
558
<?php endif;?>
559
			<tbody class="user-entries">
560
<?php
561
$nrules = 0;
562
$separators = config_get_path('filter/separator/'.strtolower($if));
563

    
564
// Get a list of separator rows and use it to call the display separator function only for rows which there are separator(s).
565
// More efficient than looping through the list of separators on every row.
566
$seprows = separator_rows($separators);
567

    
568
/* Cache gateway status for this page load.
569
 * See https://redmine.pfsense.org/issues/12174 */
570
$gateways_status = return_gateways_status(true);
571

    
572
foreach ($a_filter as $filteri => $filterent):
573

    
574
	if (($filterent['interface'] == $if && !isset($filterent['floating'])) || (isset($filterent['floating']) && "FloatingRules" == $if)) {
575

    
576
		// Display separator(s) for section beginning at rule n
577
		if ($seprows[$nrules]) {
578
			display_separator($separators, $nrules, $columns_in_table);
579
		}
580
?>
581
					<tr id="fr<?=$nrules;?>" onClick="fr_toggle(<?=$nrules;?>)" ondblclick="document.location='firewall_rules_edit.php?id=<?=$filteri;?>';" <?=(isset($filterent['disabled']) ? ' class="disabled"' : '')?>>
582
						<td>
583
							<input type="checkbox" id="frc<?=$nrules;?>" onClick="fr_toggle(<?=$nrules;?>)" name="rule[]" value="<?=$filteri;?>"/>
584
						</td>
585

    
586
	<?php
587
		if ($filterent['type'] == "block") {
588
			$iconfn = "times text-danger";
589
			$title_text = gettext("traffic is blocked");
590
		} else if ($filterent['type'] == "reject") {
591
			$iconfn = "hand-stop-o text-warning";
592
			$title_text = gettext("traffic is rejected");
593
		} else if ($filterent['type'] == "match") {
594
			$iconfn = "filter";
595
			$title_text = gettext("traffic is matched");
596
		} else {
597
			$iconfn = "check text-success";
598
			$title_text = gettext("traffic is passed");
599
		}
600
	?>
601
						<td title="<?=$title_text?>">
602
							<a href="?if=<?=htmlspecialchars($if);?>&amp;act=toggle&amp;id=<?=$filteri;?>" usepost>
603
								<i class="fa fa-<?=$iconfn?>" title="<?=gettext("click to toggle enabled/disabled status");?>"></i>
604
							</a>
605
	<?php
606
		if ($filterent['quick'] == 'yes') {
607
			print '<i class="fa fa-forward text-success" title="'. gettext("&quot;Quick&quot; rule. Applied immediately on match.") .'" style="cursor: pointer;"></i>';
608
		}
609

    
610
		$isadvset = firewall_check_for_advanced_options($filterent);
611
		if ($isadvset) {
612
			print '<i class="fa fa-cog" title="'. gettext("advanced setting") .': '. $isadvset .'" style="cursor: pointer;"></i>';
613
		}
614

    
615
		if (isset($filterent['log'])) {
616
			print '<i class="fa fa-tasks" title="'. gettext("traffic is logged") .'" style="cursor: pointer;"></i>';
617
		}
618

    
619
		if (isset($filterent['direction']) && ($if == "FloatingRules")) {
620
			if ($filterent['direction'] == 'in') {
621
				print '<i class="fa fa-arrow-circle-o-left" title="'. gettext("direction is in") .'" style="cursor: pointer;"></i>';
622
			} elseif ($filterent['direction'] == 'out') {
623
				print '<i class="fa fa-arrow-circle-o-right" title="'. gettext("direction is out") .'" style="cursor: pointer;"></i>';
624
			}
625
		}
626
	?>
627
						</td>
628
	<?php
629
		$alias = rule_columns_with_alias(
630
			$filterent['source']['address'],
631
			pprint_port($filterent['source']['port']),
632
			$filterent['destination']['address'],
633
			pprint_port($filterent['destination']['port'])
634
		);
635

    
636
		//build Schedule popup box
637
		init_config_arr(array('schedules', 'schedule'));
638
		$a_schedules = &$config['schedules']['schedule'];
639
		$schedule_span_begin = "";
640
		$schedule_span_end = "";
641
		$sched_caption_escaped = "";
642
		$sched_content = "";
643
		$schedstatus = false;
644
		$dayArray = array (gettext('Mon'), gettext('Tues'), gettext('Wed'), gettext('Thur'), gettext('Fri'), gettext('Sat'), gettext('Sun'));
645
		$monthArray = array (gettext('January'), gettext('February'), gettext('March'), gettext('April'), gettext('May'), gettext('June'), gettext('July'), gettext('August'), gettext('September'), gettext('October'), gettext('November'), gettext('December'));
646
		if ($config['schedules']['schedule'] != "" && is_array($config['schedules']['schedule'])) {
647
			$idx = 0;
648
			foreach ($a_schedules as $schedule) {
649
				if (!empty($schedule['name']) &&
650
				    $schedule['name'] == $filterent['sched']) {
651
					$schedstatus = filter_get_time_based_rule_status($schedule);
652

    
653
					foreach ($schedule['timerange'] as $timerange) {
654
						$tempFriendlyTime = "";
655
						$tempID = "";
656
						$firstprint = false;
657
						if ($timerange) {
658
							$dayFriendly = "";
659
							$tempFriendlyTime = "";
660

    
661
							//get hours
662
							$temptimerange = $timerange['hour'];
663
							$temptimeseparator = strrpos($temptimerange, "-");
664

    
665
							$starttime = substr ($temptimerange, 0, $temptimeseparator);
666
							$stoptime = substr ($temptimerange, $temptimeseparator+1);
667

    
668
							if ($timerange['month']) {
669
								$tempmontharray = explode(",", $timerange['month']);
670
								$tempdayarray = explode(",", $timerange['day']);
671
								$arraycounter = 0;
672
								$firstDayFound = false;
673
								$firstPrint = false;
674
								foreach ($tempmontharray as $monthtmp) {
675
									$month = $tempmontharray[$arraycounter];
676
									$day = $tempdayarray[$arraycounter];
677

    
678
									if (!$firstDayFound) {
679
										$firstDay = $day;
680
										$firstmonth = $month;
681
										$firstDayFound = true;
682
									}
683

    
684
									$currentDay = $day;
685
									$nextDay = $tempdayarray[$arraycounter+1];
686
									$currentDay++;
687
									if (($currentDay != $nextDay) || ($tempmontharray[$arraycounter] != $tempmontharray[$arraycounter+1])) {
688
										if ($firstPrint) {
689
											$dayFriendly .= ", ";
690
										}
691
										$currentDay--;
692
										if ($currentDay != $firstDay) {
693
											$dayFriendly .= $monthArray[$firstmonth-1] . " " . $firstDay . " - " . $currentDay ;
694
										} else {
695
											$dayFriendly .=	 $monthArray[$month-1] . " " . $day;
696
										}
697
										$firstDayFound = false;
698
										$firstPrint = true;
699
									}
700
									$arraycounter++;
701
								}
702
							} else {
703
								$tempdayFriendly = $timerange['position'];
704
								$firstDayFound = false;
705
								$tempFriendlyDayArray = explode(",", $tempdayFriendly);
706
								$currentDay = "";
707
								$firstDay = "";
708
								$nextDay = "";
709
								$counter = 0;
710
								foreach ($tempFriendlyDayArray as $day) {
711
									if ($day != "") {
712
										if (!$firstDayFound) {
713
											$firstDay = $tempFriendlyDayArray[$counter];
714
											$firstDayFound = true;
715
										}
716
										$currentDay =$tempFriendlyDayArray[$counter];
717
										//get next day
718
										$nextDay = $tempFriendlyDayArray[$counter+1];
719
										$currentDay++;
720
										if ($currentDay != $nextDay) {
721
											if ($firstprint) {
722
												$dayFriendly .= ", ";
723
											}
724
											$currentDay--;
725
											if ($currentDay != $firstDay) {
726
												$dayFriendly .= $dayArray[$firstDay-1] . " - " . $dayArray[$currentDay-1];
727
											} else {
728
												$dayFriendly .= $dayArray[$firstDay-1];
729
											}
730
											$firstDayFound = false;
731
											$firstprint = true;
732
										}
733
										$counter++;
734
									}
735
								}
736
							}
737
							$timeFriendly = $starttime . " - " . $stoptime;
738
							$description = $timerange['rangedescr'];
739
							$sched_content .= $dayFriendly . "; " . $timeFriendly . "<br />";
740
						}
741
					}
742
					#FIXME
743
					$sched_caption_escaped = str_replace("'", "\'", $schedule['descr']);
744
					$schedule_span_begin = '<a href="/firewall_schedule_edit.php?id=' . $idx . '" data-toggle="popover" data-trigger="hover focus" title="' . $schedule['name'] . '" data-content="' .
745
						$sched_caption_escaped . '" data-html="true">';
746
					$schedule_span_end = "</a>";
747
				}
748
				$idx++;
749
			}
750
		}
751
		$printicon = false;
752
		$alttext = "";
753
		$image = "";
754
		if (!isset($filterent['disabled'])) {
755
			if ($schedstatus) {
756
				if ($filterent['type'] == "block" || $filterent['type'] == "reject") {
757
					$image = "times-circle";
758
					$dispcolor = "text-danger";
759
					$alttext = gettext("Traffic matching this rule is currently being denied");
760
				} else {
761
					$image = "play-circle";
762
					$dispcolor = "text-success";
763
					$alttext = gettext("Traffic matching this rule is currently being allowed");
764
				}
765
				$printicon = true;
766
			} else if ($filterent['sched']) {
767
				if ($filterent['type'] == "block" || $filterent['type'] == "reject") {
768
					$image = "times-circle";
769
				} else {
770
					$image = "play-circle";
771
				}
772
				$alttext = gettext("This rule is not currently active because its period has expired");
773
				$dispcolor = "text-warning";
774
				$printicon = true;
775
			}
776
		}
777
	?>
778
				<td><?php print_states(intval($filterent['tracker'])); ?></td>
779
	<?php
780
		if ($if == 'FloatingRules') {
781
	?>
782
			<td onclick="fr_toggle(<?=$nrules;?>)" id="frd<?=$nrules;?>" ondblclick="document.location='firewall_rules_edit.php?id=<?=$i;?>';">
783
	<?php
784
			if (isset($filterent['interface'])) {
785
				$selected_interfaces = explode(',', $filterent['interface']);
786
				unset($selected_descs);
787
				foreach ($selected_interfaces as $interface) {
788
					if (isset($ifdescs[$interface])) {
789
						$selected_descs[] = $ifdescs[$interface];
790
					} else {
791
						switch ($interface) {
792
						case 'l2tp':
793
							if (config_get_path('l2tp/mode') == 'server') {
794
								$selected_descs[] = 'L2TP VPN';
795
							}
796
							break;
797
						case 'pppoe':
798
							if (is_pppoe_server_enabled()) {
799
								$selected_descs[] = 'PPPoE Server';
800
							}
801
							break;
802
						case 'enc0':
803
							if (ipsec_enabled()) {
804
								$selected_descs[] = 'IPsec';
805
							}
806
							break;
807
						case 'openvpn':
808
							if (!empty(config_get_path('openvpn/openvpn-server', [])) ||
809
							    !empty(config_get_path('openvpn/openvpn-client', []))) {
810
								$selected_descs[] = 'OpenVPN';
811
							}
812
							break;
813
						case 'any':
814
							$selected_descs[] = 'Any';
815
							break;
816
						default:
817
							$selected_descs[] = $interface;
818
							break;
819
						}
820
					}
821
				}
822
				if (!empty($selected_descs)) {
823
					$desclist = '';
824
					$desclength = 0;
825
					foreach ($selected_descs as $desc) {
826
						$desclength += strlen($desc);
827
						if ($desclength > 18) {
828
							$desclist .= ',<br/>';
829
							$desclength = 0;
830
						} elseif ($desclist) {
831
							$desclist .= ', ';
832
							$desclength += 2;
833
						}
834
						$desclist .= $desc;
835
					}
836
					echo $desclist;
837
				}
838
			}
839
	?>
840
			</td>
841
	<?php
842
		}
843
	?>
844
			<td>
845
	<?php
846
		if (isset($filterent['ipprotocol'])) {
847
			switch ($filterent['ipprotocol']) {
848
				case "inet":
849
					echo "IPv4 ";
850
					break;
851
				case "inet6":
852
					echo "IPv6 ";
853
					break;
854
				case "inet46":
855
					echo "IPv4+6 ";
856
					break;
857
			}
858
		} else {
859
			echo "IPv4 ";
860
		}
861

    
862
		if (isset($filterent['protocol'])) {
863
			echo strtoupper($filterent['protocol']);
864

    
865
			if (strtoupper($filterent['protocol']) == "ICMP" && !empty($filterent['icmptype'])) {
866
				// replace each comma-separated icmptype item by its (localised) full description
867
				$t = 	implode(', ',
868
						array_map(
869
						        function($type) {
870
								global $icmptypes;
871
								return $icmptypes[$type]['descrip'];
872
							},
873
							explode(',', $filterent['icmptype'])
874
						)
875
					);
876
				echo sprintf('<br /><div style="cursor:help;padding:1px;line-height:1.1em;max-height:2.5em;max-width:180px;overflow-y:auto;overflow-x:hidden" title="%s:%s%s"><small><u>%s</u></small></div>', gettext('ICMP subtypes'), chr(13), $t, str_replace(',', '</u>, <u>',$filterent['icmptype']));
877
			}
878
		} else {
879
			echo " *";
880
		}
881
	?>
882
						</td>
883
						<td>
884
							<?php if (isset($alias['src'])): ?>
885
								<a href="/firewall_aliases_edit.php?id=<?=$alias['src']?>" data-toggle="popover" data-trigger="hover focus" title="<?=gettext('Alias details')?>" data-content="<?=alias_info_popup($alias['src'])?>" data-html="true">
886
									<?=str_replace('_', '_<wbr>', htmlspecialchars(pprint_address($filterent['source'])))?>
887
								</a>
888
							<?php else: ?>
889
								<?=htmlspecialchars(pprint_address($filterent['source']))?>
890
							<?php endif; ?>
891
						</td>
892
						<td>
893
							<?php if (isset($alias['srcport'])): ?>
894
								<a href="/firewall_aliases_edit.php?id=<?=$alias['srcport']?>" data-toggle="popover" data-trigger="hover focus" title="<?=gettext('Alias details')?>" data-content="<?=alias_info_popup($alias['srcport'])?>" data-html="true">
895
									<?=str_replace('_', '_<wbr>', htmlspecialchars(pprint_port($filterent['source']['port'])))?>
896
								</a>
897
							<?php else: ?>
898
								<?=htmlspecialchars(pprint_port($filterent['source']['port']))?>
899
							<?php endif; ?>
900
						</td>
901
						<td>
902
							<?php if (isset($alias['dst'])): ?>
903
								<a href="/firewall_aliases_edit.php?id=<?=$alias['dst']?>" data-toggle="popover" data-trigger="hover focus" title="<?=gettext('Alias details')?>" data-content="<?=alias_info_popup($alias['dst'])?>" data-html="true">
904
									<?=str_replace('_', '_<wbr>', htmlspecialchars(pprint_address($filterent['destination'])))?>
905
								</a>
906
							<?php else: ?>
907
								<?=htmlspecialchars(pprint_address($filterent['destination']))?>
908
							<?php endif; ?>
909
						</td>
910
						<td>
911
							<?php if (isset($alias['dstport'])): ?>
912
								<a href="/firewall_aliases_edit.php?id=<?=$alias['dstport']?>" data-toggle="popover" data-trigger="hover focus" title="<?=gettext('Alias details')?>" data-content="<?=alias_info_popup($alias['dstport'])?>" data-html="true">
913
									<?=str_replace('_', '_<wbr>', htmlspecialchars(pprint_port($filterent['destination']['port'])))?>
914
								</a>
915
							<?php else: ?>
916
								<?=htmlspecialchars(pprint_port($filterent['destination']['port']))?>
917
							<?php endif; ?>
918
						</td>
919
						<td>
920
							<?php if (isset($filterent['gateway'])): ?>
921
								<?php
922
									/* Cache gateway status for this page load.
923
									 * See https://redmine.pfsense.org/issues/12174 */
924
									if (!is_array($gw_info)) {
925
										$gw_info = array();
926
									}
927
									if (empty($gw_info[$filterent['gateway']])) {
928
										$gw_info[$filterent['gateway']] = gateway_info_popup($filterent['gateway'], $gateways_status);
929
									}
930
								?>
931
								<?php if (!empty($gw_info[$filterent['gateway']])): ?>
932
									<?=$gw_info[$filterent['gateway']]?>
933
								<?php else: ?>
934
									<span>
935
								<?php endif; ?>
936
							<?php else: ?>
937
								<span>
938
							<?php endif; ?>
939
								<?php if (isset($config['interfaces'][$filterent['gateway']]['descr'])): ?>
940
									<?=str_replace('_', '_<wbr>', htmlspecialchars($config['interfaces'][$filterent['gateway']]['descr']))?>
941
								<?php else: ?>
942
									<?=htmlspecialchars(pprint_port($filterent['gateway']))?>
943
								<?php endif; ?>
944
							</span>
945
						</td>
946
						<td>
947
							<?php
948
								if (isset($filterent['ackqueue']) && isset($filterent['defaultqueue'])) {
949
									$desc = str_replace('_', ' ', $filterent['ackqueue']);
950
									echo "<a href=\"firewall_shaper_queues.php?queue={$filterent['ackqueue']}&amp;action=show\">{$desc}</a>";
951
									$desc = str_replace('_', '_<wbr>', $filterent['defaultqueue']);
952
									echo "/<a href=\"firewall_shaper_queues.php?queue={$filterent['defaultqueue']}&amp;action=show\">{$desc}</a>";
953
								} else if (isset($filterent['defaultqueue'])) {
954
									$desc = str_replace('_', '_<wbr>', $filterent['defaultqueue']);
955
									echo "<a href=\"firewall_shaper_queues.php?queue={$filterent['defaultqueue']}&amp;action=show\">{$desc}</a>";
956
								} else {
957
									echo gettext("none");
958
								}
959
							?>
960
						</td>
961
						<td>
962
							<?php if ($printicon) { ?>
963
								<i class="fa fa-<?=$image?> <?=$dispcolor?>" title="<?=$alttext;?>"></i>
964
							<?php } ?>
965
							<?=$schedule_span_begin;?><?=str_replace('_', '_<wbr>', htmlspecialchars($filterent['sched']));?>&nbsp;<?=$schedule_span_end;?>
966
						</td>
967
						<td>
968
							<?=htmlspecialchars($filterent['descr']);?>
969
						</td>
970
						<td class="action-icons">
971
						<!-- <?=(isset($filterent['disabled']) ? 'enable' : 'disable')?> -->
972
							<a	class="fa fa-anchor icon-pointer" id="Xmove_<?=$filteri?>" title="<?=$XmoveTitle?>"></a>
973
							<a href="firewall_rules_edit.php?id=<?=$filteri;?>" class="fa fa-pencil" title="<?=gettext('Edit')?>"></a>
974
							<a href="firewall_rules_edit.php?dup=<?=$filteri;?>" class="fa fa-clone" title="<?=gettext('Copy')?>"></a>
975
<?php if (isset($filterent['disabled'])) {
976
?>
977
							<a href="?act=toggle&amp;if=<?=htmlspecialchars($if);?>&amp;id=<?=$filteri;?>" class="fa fa-check-square-o" title="<?=gettext('Enable')?>" usepost></a>
978
<?php } else {
979
?>
980
							<a href="?act=toggle&amp;if=<?=htmlspecialchars($if);?>&amp;id=<?=$filteri;?>" class="fa fa-ban" title="<?=gettext('Disable')?>" usepost></a>
981
<?php }
982
?>
983
							<a href="?act=del&amp;if=<?=htmlspecialchars($if);?>&amp;id=<?=$filteri;?>" class="fa fa-trash" title="<?=gettext('Delete this rule')?>" usepost></a>
984
<?php if (($filterent['type'] == 'pass') &&
985
	    !empty($filterent['tracker'])): ?>
986
							<a href="?act=killid&amp;if=<?=htmlspecialchars($if);?>&amp;id=<?=$filteri;?>&amp;tracker=<?=$filterent['tracker']?>" class="fa fa-times do-confirm" title="<?=gettext('Kill states on this interface created by this rule')?>" usepost></a>
987
<?php endif; ?>
988
						</td>
989
					</tr>
990
<?php
991
		$nrules++;
992
	}
993
endforeach;
994

    
995
// There can be separator(s) after the last rule listed.
996
foreach ($seprows as $idx => $sep) {
997
	if ($idx >= $nrules) {
998
		display_separator($separators, $idx, $columns_in_table);
999
	}
1000
}
1001
?>
1002
				</tbody>
1003
			</table>
1004
		</div>
1005
	</div>
1006

    
1007
<?php if ($nrules == 0): ?>
1008
	<div class="alert alert-warning" role="alert">
1009
		<p>
1010
		<?php if ($_REQUEST['if'] == "FloatingRules"): ?>
1011
			<?=gettext("No floating rules are currently defined.");?>
1012
		<?php else: ?>
1013
			<?=gettext("No rules are currently defined for this interface");?><br />
1014
			<?=gettext("All incoming connections on this interface will be blocked until pass rules are added.");?>
1015
		<?php endif;?>
1016
			<?=gettext("Click the button to add a new rule.");?>
1017
		</p>
1018
	</div>
1019
<?php endif;?>
1020

    
1021
	<nav class="action-buttons">
1022
		<a href="firewall_rules_edit.php?if=<?=htmlspecialchars($if);?>&amp;after=-1" role="button" class="btn btn-sm btn-success" title="<?=gettext('Add rule to the top of the list')?>">
1023
			<i class="fa fa-level-up icon-embed-btn"></i>
1024
			<?=gettext("Add");?>
1025
		</a>
1026
		<a href="firewall_rules_edit.php?if=<?=htmlspecialchars($if);?>" role="button" class="btn btn-sm btn-success" title="<?=gettext('Add rule to the end of the list')?>">
1027
			<i class="fa fa-level-down icon-embed-btn"></i>
1028
			<?=gettext("Add");?>
1029
		</a>
1030
		<button id="del_x" name="del_x" type="submit" class="btn btn-danger btn-sm" value="<?=gettext("Delete selected rules"); ?>" disabled title="<?=gettext('Delete selected rules')?>">
1031
			<i class="fa fa-trash icon-embed-btn"></i>
1032
			<?=gettext("Delete"); ?>
1033
		</button>
1034
		<button id="toggle_x" name="toggle_x" type="submit" class="btn btn-primary btn-sm" value="<?=gettext("Toggle selected rules"); ?>" disabled title="<?=gettext('Toggle selected rules')?>">
1035
			<i class="fa fa-ban icon-embed-btn"></i>
1036
			<?=gettext("Toggle"); ?>
1037
		</button>
1038
		<?php if ($if != 'FloatingRules'):?>
1039
		<button id="copy_x" name="copy_x" type="button" class="btn btn-primary btn-sm" value="<?=gettext("Copy selected rules"); ?>" disabled title="<?=gettext('Copy selected rules')?>" data-toggle="modal" data-target="#rulescopy">
1040
			<i class="fa fa-clone icon-embed-btn"></i>
1041
			<?=gettext("Copy"); ?>
1042
		</button>
1043
		<?php endif;?>
1044
		<button type="submit" id="order-store" name="order-store" class="btn btn-sm btn-primary" value="store changes" disabled title="<?=gettext('Save rule order')?>">
1045
			<i class="fa fa-save icon-embed-btn"></i>
1046
			<?=gettext("Save")?>
1047
		</button>
1048
		<button type="submit" id="addsep" name="addsep" class="btn btn-sm btn-warning" title="<?=gettext('Add separator')?>">
1049
			<i class="fa fa-plus icon-embed-btn"></i>
1050
			<?=gettext("Separator")?>
1051
		</button>
1052
	</nav>
1053
</form>
1054
<?php
1055
// Create a Modal object to display Rules Copy window
1056
$form = new Form(false);
1057
$modal = new Modal('Copy selected rules', 'rulescopy', true);
1058
$modal->addInput(new Form_Select(
1059
	'copyr_dstif',
1060
	'*Destination Interface',
1061
	$if,
1062
	filter_get_interface_list()
1063
))->setHelp('Select the destination interface where the rules should be copied. Rules will be added after existing rules on that interface.');
1064
$modal->addInput(new Form_Checkbox(
1065
	'copyr_convertif',
1066
	'Convert interface definitions',
1067
	'Enable Interface Address/Net conversion',
1068
	false
1069
))->setHelp('Convert source Interface Address/Net definitions to the destination Interface Address/Net.%1$s' .
1070
	    'For example: LAN Address -> OPT1 Address, or LAN net -> OPT1 net.%1$s' .
1071
	    'Interface groups and some special interfaces (IPsec, OpenVPN), do not support this feature.', '<br />');
1072
$btncopyrules = new Form_Button(
1073
	'copyr',
1074
	'Paste',
1075
	null,
1076
	'fa-clone'
1077
);
1078
$btncopyrules->setAttribute('type','button')->addClass('btn-success');
1079
$btncancelcopyrules = new Form_Button(
1080
	'cancel_copyr',
1081
	'Cancel',
1082
	null,
1083
	'fa-undo'
1084
);
1085
$btncancelcopyrules->setAttribute('type','button')->addClass('btn-warning');
1086
$modal->addInput(new Form_StaticText(
1087
	null,
1088
	$btncopyrules . $btncancelcopyrules
1089
));
1090
$form->add($modal);
1091
print($form);
1092
?>
1093
<div class="infoblock">
1094
	<div class="alert alert-info clearfix" role="alert"><div class="pull-left">
1095
		<dl class="dl-horizontal responsive">
1096
		<!-- Legend -->
1097
			<dt><?=gettext('Legend')?></dt>				<dd></dd>
1098
			<dt><i class="fa fa-check text-success"></i></dt>		<dd><?=gettext("Pass");?></dd>
1099
			<dt><i class="fa fa-filter"></i></dt>	<dd><?=gettext("Match");?></dd>
1100
			<dt><i class="fa fa-times text-danger"></i></dt>	<dd><?=gettext("Block");?></dd>
1101
			<dt><i class="fa fa-hand-stop-o text-warning"></i></dt>		<dd><?=gettext("Reject");?></dd>
1102
			<dt><i class="fa fa-tasks"></i></dt>	<dd> <?=gettext("Log");?></dd>
1103
			<dt><i class="fa fa-cog"></i></dt>		<dd> <?=gettext("Advanced filter");?></dd>
1104
			<dt><i class="fa fa-forward text-success"></i></dt><dd> <?=gettext("&quot;Quick&quot; rule. Applied immediately on match.")?></dd>
1105
		</dl>
1106

    
1107
<?php
1108
	if ("FloatingRules" != $if) {
1109
		print(gettext("Rules are evaluated on a first-match basis (i.e. " .
1110
			"the action of the first rule to match a packet will be executed). ") . '<br />' .
1111
			gettext("This means that if block rules are used, it is important to pay attention " .
1112
			"to the rule order. Everything that isn't explicitly passed is blocked " .
1113
			"by default. "));
1114
	} else {
1115
		print(gettext("Floating rules are evaluated on a first-match basis (i.e. " .
1116
			"the action of the first rule to match a packet will be executed) only " .
1117
			"if the 'quick' option is checked on a rule. Otherwise they will only match if no " .
1118
			"other rules match. Pay close attention to the rule order and options " .
1119
			"chosen. If no rule here matches, the per-interface or default rules are used. "));
1120
	}
1121

    
1122
	printf(gettext('%1$sClick the anchor icon %2$s to move checked rules before the clicked row. Hold down ' .
1123
			'the shift key and click to move the rules after the clicked row.'), '<br /><br />', '<i class="fa fa-anchor"></i>');
1124
?>
1125
	</div>
1126
	</div>
1127
</div>
1128

    
1129
<script type="text/javascript">
1130
//<![CDATA[
1131

    
1132
//Need to create some variables here so that jquery/pfSenseHelpers.js can read them
1133
iface = "<?=strtolower($if)?>";
1134
cncltxt = '<?=gettext("Cancel")?>';
1135
svtxt = '<?=gettext("Save")?>';
1136
svbtnplaceholder = '<?=gettext("Enter a description, Save, then drag to final location.")?>';
1137
configsection = "filter";
1138

    
1139
events.push(function() {
1140

    
1141
	// "Move to here" (anchor) action
1142
	$('[id^=Xmove_]').click(function (event) {
1143

    
1144
		// Prevent click from toggling row
1145
		event.stopImmediatePropagation();
1146

    
1147
		// Save the target rule position
1148
		var anchor_row = $(this).parents("tr:first");
1149

    
1150
		if (event.shiftKey) {
1151
			$($('#ruletable > tbody  > tr').get().reverse()).each(function() {
1152
				ruleid = this.id.slice(2);
1153

    
1154
				if (ruleid && !isNaN(ruleid)) {
1155
					if ($('#frc' + ruleid).prop('checked')) {
1156
						// Move the selected rows, un-select them and add highlight class
1157
						$(this).insertAfter(anchor_row);
1158
						fr_toggle(ruleid, "fr");
1159
						$('#fr' + ruleid).addClass("highlight");
1160
					}
1161
				}
1162
			});
1163
		} else {
1164
			$('#ruletable > tbody  > tr').each(function() {
1165
				ruleid = this.id.slice(2);
1166

    
1167
				if (ruleid && !isNaN(ruleid)) {
1168
					if ($('#frc' + ruleid).prop('checked')) {
1169
						// Move the selected rows, un-select them and add highlight class
1170
						$(this).insertBefore(anchor_row);
1171
						fr_toggle(ruleid, "fr");
1172
						$('#fr' + ruleid).addClass("highlight");
1173
					}
1174
				}
1175
			});
1176
		}
1177

    
1178
		// Temporarily set background color so user can more easily see the moved rules, then fade
1179
		$('.highlight').effect("highlight", {color: "#739b4b;"}, 4000);
1180
		$('#ruletable tr').removeClass("highlight");
1181
		$('#order-store').removeAttr('disabled');
1182
		reindex_rules($(anchor_row).parent('tbody'));
1183
		dirty = true;
1184
	}).mouseover(function(e) {
1185
		var ruleselected = false;
1186

    
1187
		$(this).css("cursor", "default");
1188

    
1189
		// Are any rules currently selected?
1190
		$('[id^=frc]').each(function () {
1191
			if ($(this).prop("checked")) {
1192
				ruleselected = true;
1193
			}
1194
		});
1195

    
1196
		// If so, change the icon to show the insertion point
1197
		if (ruleselected) {
1198
			if (e.shiftKey) {
1199
				$(this).removeClass().addClass("fa fa-lg fa-arrow-down text-danger");
1200
			} else {
1201
				$(this).removeClass().addClass("fa fa-lg fa-arrow-up text-danger");
1202
			}
1203
		}
1204
	}).mouseout(function(e) {
1205
		$(this).removeClass().addClass("fa fa-anchor");
1206
	});
1207

    
1208
<?php if(!isset($config['system']['webgui']['roworderdragging'])): ?>
1209
	// Make rules sortable. Hiding the table before applying sortable, then showing it again is
1210
	// a work-around for very slow sorting on FireFox
1211
	$('table tbody.user-entries').hide();
1212

    
1213
	$('table tbody.user-entries').sortable({
1214
		cursor: 'grabbing',
1215
		scroll: true,
1216
		overflow: 'scroll',
1217
		scrollSensitivity: 100,
1218
		update: function(event, ui) {
1219
			$('#order-store').removeAttr('disabled');
1220
			reindex_rules(ui.item.parent('tbody'));
1221
			dirty = true;
1222
		}
1223
	});
1224

    
1225
	$('table tbody.user-entries').show();
1226
<?php endif; ?>
1227

    
1228
	// Check all of the rule checkboxes so that their values are posted
1229
	$('#order-store').click(function () {
1230
		$('[id^=frc]').prop('checked', true);
1231

    
1232
		// Save the separator bar configuration
1233
		save_separators();
1234

    
1235
		// Suppress the "Do you really want to leave the page" message
1236
		saving = true;
1237
	});
1238

    
1239
	$('[id^=fr]').click(function () {
1240
		buttonsmode('frc', ['del_x', 'toggle_x', 'copy_x']);
1241
	});
1242

    
1243
	// Provide a warning message if the user tries to change page before saving
1244
	$(window).bind('beforeunload', function(){
1245
		if ((!saving && dirty) || newSeparator) {
1246
			return ("<?=gettext('One or more rules have been moved but have not yet been saved')?>");
1247
		} else {
1248
			return undefined;
1249
		}
1250
	});
1251

    
1252
	$(document).on('keyup keydown', function(e){
1253
		if (e.shiftKey) {
1254
			$('[id^=Xmove_]').attr("title", "<?=$ShXmoveTitle?>");
1255
		} else {
1256
			$('[id^=Xmove_]').attr("title", "<?=$XmoveTitle?>");
1257
		}
1258
	});
1259

    
1260
	$('#selectAll').click(function() {
1261
		var checkedStatus = this.checked;
1262
		$('#ruletable tbody tr').find('td:first :checkbox').each(function() {
1263
		$(this).prop('checked', checkedStatus);
1264
		});
1265
		buttonsmode('frc', ['del_x', 'toggle_x', 'copy_x']);
1266
	});
1267

    
1268
	$("#copyr").click(function() {
1269
		$("#rulescopy").modal('hide');
1270
		$("#dstif").val($("#copyr_dstif").val());
1271
		$("#convertif").val($("#copyr_convertif").prop('checked'));
1272
		document.getElementById('mainform').submit();
1273
	});
1274

    
1275
	$("#cancel_copyr").click(function() {
1276
		$("#rulescopy").modal('hide');
1277
	});
1278

    
1279
});
1280
//]]>
1281
</script>
1282

    
1283
<?php include("foot.inc");?>
(50-50/228)