Project

General

Profile

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

    
25
require_once("globals.inc");
26
require_once("functions.inc");
27
require_once("led.inc");
28

    
29
$notice_path = g_get('tmp_path') . '/notices';
30
global $smtp_authentication_mechanisms;
31
$smtp_authentication_mechanisms = array(
32
	'PLAIN' => 'PLAIN',
33
	'LOGIN' => 'LOGIN');
34
/* Other SMTP Authentication Mechanisms that could be supported.
35
 * Note that MD5 is no longer considered secure.
36
 *	'GSSAPI' => 'GSSAPI ' . gettext("Generic Security Services Application Program Interface")
37
 *	'DIGEST-MD5' => 'DIGEST-MD5 ' . gettext("Digest access authentication")
38
 *	'MD5' => 'MD5'
39
 *	'CRAM-MD5' => 'CRAM-MD5'
40
*/
41
global $pushover_sounds;
42
$pushover_sounds = array(
43
	'devicedefault' => 'Device Default',
44
	'pushover' => 'Pushover',
45
	'bike' => 'Bike',
46
	'bugle' => 'Bugle',
47
	'cashregister' => 'Cash Register',
48
	'classical' => 'Classical',
49
	'cosmic' => 'Cosmic',
50
	'falling' => 'Falling',
51
	'gamelan' => 'Gamelan',
52
	'incoming' => 'Incoming',
53
	'intermission' => 'Intermission',
54
	'magic' => 'Magic',
55
	'mechanical' => 'Mechanical',
56
	'pianobar' => 'Piano Bar',
57
	'siren' => 'Siren',
58
	'spacealarm' => 'Space Alarm',
59
	'tugboat' => 'Tug Boat',
60
	'alien' => 'Alien (long)',
61
	'climb' => 'Climb (long)',
62
	'persistent' => 'Persistent (long)',
63
	'echo' => 'Echo (long)',
64
	'updown' => 'Up Down (long)',
65
	'vibrate' => 'Vibrate only',
66
	'none' => 'None (silent)');
67

    
68
/****f* notices/file_notice
69
 * NAME
70
 *   file_notice
71
 * INPUTS
72
 *	 $id, $notice, $category, $url, $priority, $local_only
73
 * RESULT
74
 *   Files a notice and kicks off the various alerts, smtp, telegram, pushover, system log, LED's, etc.
75
 *   If $local_only is true then the notice is not sent to external places (smtp, telegram, pushover)
76
 ******/
77
function file_notice($id, $notice, $category = "General", $url = "", $priority = 1, $local_only = false) {
78
	/*
79
	 * $category - Category that this notice should be displayed under. This can be arbitrary,
80
	 * 	       but a page must be set to receive this messages for it to be displayed.
81
	 *
82
	 * $priority - A notice's priority. Higher numbers indicate greater severity.
83
	 *	       0 = informational, 1 = warning, 2 = error, etc. This may also be arbitrary,
84
	 */
85
	global $notice_path;
86
	if (!$queue = get_notices()) {
87
		$queue = array();
88
	}
89
	$queuekey = time();
90
	$toqueue = array(
91
				'id'		=> htmlentities($id),
92
				'notice'	=> htmlentities($notice),
93
				'url'		=> htmlentities($url),
94
				'category'	=> htmlentities($category),
95
				'priority'	=> htmlentities($priority),
96
			);
97
	while (isset($queue[$queuekey])) {
98
		$queuekey++;
99
	}
100
	$queue[$queuekey] = $toqueue;
101
	$queueout = fopen($notice_path, "w");
102
	if (!$queueout) {
103
		log_error(sprintf(gettext("Could not open %s for writing"), $notice_path));
104
		return;
105
	}
106
	fwrite($queueout, serialize($queue));
107
	fclose($queueout);
108
	log_error(sprintf(gettext("New alert found: %s"), $notice));
109
	/* soekris */
110
	if (file_exists("/dev/led/error")) {
111
		exec("/bin/echo 1 > /dev/led/error");
112
	}
113
	/* wrap & alix */
114
	led_normalize();
115
	led_morse(1, 'sos');
116
	if (!$local_only) {
117
		notify_all_remote($notice);
118
	}
119
	return $queuekey;
120
}
121

    
122
/****f* notices/get_notices
123
 * NAME
124
 *   get_notices
125
 * INPUTS
126
 *	 $category
127
 * RESULT
128
 *   Returns a specific notices text
129
 ******/
130
function get_notices($category = "all") {
131
	global $g;
132

    
133
	if (file_exists("{$g['tmp_path']}/notices")) {
134
		$queue = unserialize(file_get_contents("{$g['tmp_path']}/notices"));
135
		if (!$queue) {
136
			return false;
137
		}
138
		if ($category != 'all') {
139
			foreach ($queue as $time => $notice) {
140
				if (strtolower($notice['category']) == strtolower($category)) {
141
					$toreturn[$time] = $notice;
142
				}
143
			}
144
			return $toreturn;
145
		} else {
146
			return $queue;
147
		}
148
	} else {
149
		return false;
150
	}
151
}
152

    
153
/****f* notices/close_notice
154
 * NAME
155
 *   close_notice
156
 * INPUTS
157
 *	 $id
158
 * RESULT
159
 *   Removes a notice from the list
160
 ******/
161
function close_notice($id) {
162
	global $notice_path;
163
	require_once("util.inc");
164
	/* soekris */
165
	if (file_exists("/dev/led/error")) {
166
		exec("/bin/echo 0 > /dev/led/error");
167
	}
168
	/* wrap & alix */
169
	led_normalize();
170
	$ids = array();
171
	if (!$notices = get_notices()) {
172
		return;
173
	}
174
	if ($id == "all") {
175
		unlink_if_exists($notice_path);
176
		return;
177
	}
178
	foreach (array_keys($notices) as $time) {
179
		if ($id == $time) {
180
			unset($notices[$id]);
181
			break;
182
		}
183
	}
184
	foreach ($notices as $key => $notice) {
185
		$ids[$key] = $notice['id'];
186
	}
187
	foreach ($ids as $time => $tocheck) {
188
		if ($id == $tocheck) {
189
			unset($notices[$time]);
190
			break;
191
		}
192
	}
193
	if (count($notices) != 0) {
194
		$queueout = fopen($notice_path, "w");
195
		fwrite($queueout, serialize($notices));
196
		fclose($queueout);
197
	} else {
198
		unlink_if_exists($notice_path);
199
	}
200

    
201
	return;
202
}
203

    
204
/****f* notices/dump_xml_notices
205
 * NAME
206
 *   dump_xml_notices
207
 * INPUTS
208
 *	 NONE
209
 * RESULT
210
 *   Outputs notices in XML formatted text
211
 ******/
212
function dump_xml_notices() {
213
	require_once("xmlparse.inc");
214
	global $notice_path, $listtags;
215
	$listtags[] = 'notice';
216
	if (!$notices = get_notices()) {
217
		return;
218
	}
219
	foreach ($notices as $time => $notice) {
220
		$notice['time'] = $time;
221
		$toput['notice'][] = $notice;
222
	}
223
	$xml = dump_xml_config($toput, 'notices');
224
	return $xml;
225
}
226

    
227
/****f* notices/are_notices_pending
228
 * NAME
229
 *   are_notices_pending
230
 * INPUTS
231
 *	 $category to check
232
 * RESULT
233
 *   returns true if notices are pending, false if they are not
234
 ******/
235
function are_notices_pending($category = "all") {
236
	global $notice_path;
237
	if (file_exists($notice_path)) {
238
		return true;
239
	}
240
	return false;
241
}
242

    
243
function notices_sendqueue() {
244
	global $g;
245
	$nothing_done_count = 0;
246
	$messagequeue = array();
247

    
248
	while(true) {
249
		$notifyqueue_lck = lock("notifyqueue", LOCK_EX);
250
		$nothing_done_count++;
251
		$smtpcount = 0;
252
		$messages = array();
253
		if (file_exists("{$g['vardb_path']}/notifyqueue.messages")) {
254
			$messages = unserialize(file_get_contents("{$g['vardb_path']}/notifyqueue.messages"));
255
			$messagequeue = $messages;
256
			$messages['mails']['item'] = array(); // clear all items to be send
257
			file_put_contents("{$g['vardb_path']}/notifyqueue.messages", serialize($messages));
258
			unset($messages);
259
		}
260
		// clear lock before trying to send messages, so new one's can be added
261
		unlock($notifyqueue_lck);
262

    
263
		if (is_array($messagequeue['mails']['item'])) {
264
			$smtpmessage = "";
265
			foreach($messagequeue['mails']['item'] as $mail) {
266
				switch ($mail['type']) {
267
					case 'mail':
268
						$smtpcount++;
269
						$smtpmessage .= "\r\n" . date("G:i:s",$mail['time']) . " " . $mail['msg'];
270
						break;
271
					default:
272
						break;
273
				}
274
			}
275
			if (!empty($smtpmessage)) {
276
				$smtpmessageheader = sprintf(gettext("Notifications in this message: %s"), $smtpcount);
277
				$smtpmessageheader .= "\n" . str_repeat('=', strlen($smtpmessageheader)) . "\n";
278
				$nothing_done_count = 0;
279
				notify_via_smtp($smtpmessageheader . $smtpmessage, true);
280
			}
281
		}
282
		if ($nothing_done_count > 6) {
283
			break;
284
		} else {
285
			sleep(10);
286
		}
287
	}
288
}
289

    
290
function notify_via_queue_add($message, $type='mail') {
291
	global $g;
292
	$mail = array();
293
	$mail['time'] = time();
294
	$mail['type'] = $type;
295
	$mail['msg'] = $message;
296
	$notifyqueue_lck = lock("notifyqueue", LOCK_EX);
297
	$messages = array();
298
	if (file_exists("{$g['vardb_path']}/notifyqueue.messages")) {
299
		$messages = unserialize(file_get_contents("{$g['vardb_path']}/notifyqueue.messages"));
300
	}
301
	if(is_array($messages)) {
302
		$messages['mails']['item'][] = $mail;
303
		file_put_contents("{$g['vardb_path']}/notifyqueue.messages", serialize($messages));
304
	}
305
	unset($messages);
306

    
307
	mwexec_bg('/usr/local/bin/notify_monitor.php');
308
	unlock($notifyqueue_lck);
309
}
310

    
311
/****f* notices/notify_via_smtp
312
 * NAME
313
 *   notify_via_smtp
314
 * INPUTS
315
 *	 notification string to send as an email
316
 * RESULT
317
 *   returns true if message was sent
318
 ******/
319
function notify_via_smtp($message, $force = false) {
320
	global $config, $g;
321
	if (platform_booting()) {
322
		return;
323
	}
324

    
325
	if (isset($config['notifications']['smtp']['disable']) && !$force) {
326
		return;
327
	}
328

    
329
	/* Do NOT send the same message twice, except if $force is true */
330
	if (!$force && file_exists("/var/db/notices_lastmsg.txt")) {
331
		$lastmsg = trim(file_get_contents("/var/db/notices_lastmsg.txt"));
332
		if ($lastmsg == $message) {
333
			return;
334
		}
335
	}
336

    
337
	/* Store last message sent to avoid spamming */
338
	@file_put_contents("/var/db/notices_lastmsg.txt", $message);
339
	if (!$force) {
340
		notify_via_queue_add($message, 'mail');
341
		$ret = true;
342
	} else {
343
		$ret = send_smtp_message($message, "{$config['system']['hostname']}.{$config['system']['domain']} - Notification", $force);
344
	}
345

    
346
	return $ret;
347
}
348

    
349
function send_smtp_message($message, $subject = "(no subject)", $force = false) {
350
	global $config, $g;
351
	require_once("Mail.php");
352

    
353
	if (isset($config['notifications']['smtp']['disable']) && !$force) {
354
		return;
355
	}
356

    
357
	if (!$config['notifications']['smtp']['ipaddress']) {
358
		return;
359
	}
360

    
361
	if (!$config['notifications']['smtp']['notifyemailaddress']) {
362
		return;
363
	}
364

    
365
	$to = config_get_path('notifications/smtp/notifyemailaddress');
366

    
367
	if (empty($config['notifications']['smtp']['username']) ||
368
	    empty($config['notifications']['smtp']['password'])) {
369
		$auth = false;
370
		$username = '';
371
		$password = '';
372
	} else {
373
		$auth = isset($config['notifications']['smtp']['authentication_mechanism'])
374
		    ? $config['notifications']['smtp']['authentication_mechanism']
375
		    : 'PLAIN';
376
		$username = config_get_path('notifications/smtp/username');
377
		$password = config_get_path('notifications/smtp/password');
378
	}
379

    
380
	$params = array(
381
		'host' => (isset($config['notifications']['smtp']['ssl'])
382
		    ? 'ssl://'
383
		    : '')
384
		    . $config['notifications']['smtp']['ipaddress'],
385
		'port' => empty($config['notifications']['smtp']['port'])
386
		    ? 25
387
		    : $config['notifications']['smtp']['port'],
388
		'auth' => $auth,
389
		'username' => $username,
390
		'password' => $password,
391
		'localhost' => $config['system']['hostname'] . "." .
392
		    $config['system']['domain'],
393
		'timeout' => !empty($config['notifications']['smtp']['timeout'])
394
		    ? $config['notifications']['smtp']['timeout']
395
		    : 20,
396
		'debug' => false,
397
		'persist' => false
398
	);
399

    
400
	if ($config['notifications']['smtp']['sslvalidate'] == "disabled") {
401
		$params['socket_options'] = array(
402
			'ssl' => array(
403
				'verify_peer_name' => false,
404
				'verify_peer' => false
405
		));
406
	}
407

    
408
	if ($config['notifications']['smtp']['fromaddress']) {
409
		$from = config_get_path('notifications/smtp/fromaddress');
410
	} else {
411
		$from = "pfsense@{$config['system']['hostname']}.{$config['system']['domain']}";
412
	}
413

    
414
	$headers = array(
415
		"From"    => $from,
416
		"To"      => $to,
417
		"Subject" => $subject,
418
		"Date"    => date("r")
419
	);
420

    
421
	$error_text = 'Could not send the message to %1$s -- Error: %2$s';
422
	try {
423
		$smtp =& Mail::factory('smtp', $params);
424
		$mail = @$smtp->send($to, $headers, $message);
425

    
426
		if (PEAR::isError($mail)) {
427
			$err_msg = sprintf(gettext($error_text),
428
			    $to, $mail->getMessage());
429
		}
430
	} catch (Exception $e) {
431
		$err_msg = sprintf(gettext($error_text), $to, $e->getMessage());
432
	}
433

    
434
	if (!empty($err_msg)) {
435
		log_error($err_msg);
436
		return($err_msg);
437
	}
438

    
439
	log_error(sprintf(gettext("Message sent to %s OK"), $to));
440
	return;
441
}
442
/****f* notices/notify_via_telegram
443
 * NAME
444
 *   notify_via_telegram
445
 * INPUTS
446
 *	 notification string to send to Telegram via API
447
 * RESULT
448
 *   returns NULL if message was sent
449
 ******/
450

    
451
function notify_via_telegram($message, $force = false) {
452
	global $config;
453
	if ((!isset($config['notifications']['telegram']['enabled']) && (!$force))
454
	    || !$config['notifications']['telegram']['api']
455
	    || !$config['notifications']['telegram']['chatid']) {
456
		if ($force) {
457
			return gettext("Unable to test Telegram notification without both API Key & Chat ID set");
458
		}
459
		return;
460
	}
461

    
462
	$url = "https://api.telegram.org/bot{$config['notifications']['telegram']['api']}/sendMessage?";
463
	$data = array(
464
		"chat_id" => $config['notifications']['telegram']['chatid'],
465
		"text" => "{$config['system']['hostname']}.{$config['system']['domain']}\n{$message}"
466
	);
467
	$result = json_decode(curl_post_notification($url . http_build_query($data)), true);
468
	if (is_array($result)) {
469
		if ($result['ok']) {
470
			unset($err_msg);
471
		} else {
472
			$err_msg = sprintf(gettext("Failed to send Telegram notification. Error received was :{$result['error_code']}: {$result['description']}"));
473
			log_error($err_msg);
474
		}
475
	} else {
476
		$err_msg = gettext("API to Telegram did not return data in expected format!");
477
		log_error($err_msg);
478
	}
479
	return $err_msg;
480
}
481

    
482
/****f* notices/notify_via_pushover
483
 * NAME
484
 *   notify_via_pushover
485
 * INPUTS
486
 *	 notification string to send to Pushover via API
487
 * RESULT
488
 *   returns NULL if message was sent
489
 ******/
490

    
491
function notify_via_pushover($message, $force = false) {
492
	global $config;
493
	if ((!isset($config['notifications']['pushover']['enabled']) && (!$force))
494
	    || !$config['notifications']['pushover']['apikey']
495
	    || !$config['notifications']['pushover']['userkey']) {
496
		if ($force) {
497
			return gettext("Unable to test Pushover notification without both API Key & User Key set");
498
		}
499
		return;
500
	}
501
	if (strcasecmp($config['notifications']['pushover']['sound'], 'devicedefault') == 0) {
502
		config_del_path('notifications/pushover/sound');
503
	}
504

    
505
	$url = "https://api.pushover.net/1/messages.json";
506
	$data = array(
507
		"token" => $config['notifications']['pushover']['apikey'],
508
		"user" => $config['notifications']['pushover']['userkey'],
509
		"sound" => $config['notifications']['pushover']['sound'],
510
		"priority" => $config['notifications']['pushover']['priority'],
511
		"retry" => $config['notifications']['pushover']['retry'],
512
		"expire" => $config['notifications']['pushover']['expire'],
513
		"message" => "{$config['system']['hostname']}.{$config['system']['domain']}\n{$message}"
514
	);
515
	$result = json_decode(curl_post_notification($url, $data), true);
516
	if (is_array($result)) {
517
		if ($result['status']) {
518
			unset($err_msg);
519
		} else {
520
			$err_msg = sprintf(gettext("Failed to send Pushover notification. Error received was: %s"), $result['errors']['0']);
521
			log_error($err_msg);
522
		}
523
	} else {
524
		$err_msg = gettext("Pushover API server did not return data in expected format!");
525
		log_error($err_msg);
526
	}
527
	return $err_msg;
528
}
529

    
530
/****f* notices/notify_via_slack
531
 * NAME
532
 *   notify_via_slack
533
 * INPUTS
534
 *	 notification string to send to Slack via API
535
 * RESULT
536
 *   returns NULL if message was sent
537
 ******/
538

    
539
function notify_via_slack($message, $force = false) {
540
	global $config;
541
	if ((!isset($config['notifications']['slack']['enabled']) && (!$force))
542
	    || !$config['notifications']['slack']['api']
543
	    || !$config['notifications']['slack']['channel']) {
544
		if ($force) {
545
			return gettext("Unable to test Slack notification without both API Key & Channel set");
546
		}
547
		return;
548
	}
549

    
550
	$url = "https://slack.com/api/chat.postMessage";
551
	$data = array(
552
		"token" => $config['notifications']['slack']['api'],
553
		"channel" => "#" . $config['notifications']['slack']['channel'],
554
		"text" => $message,
555
		"username" => "{$config['system']['hostname']}.{$config['system']['domain']}"
556
	);
557
	$result = json_decode(curl_post_notification($url, $data), true);
558
	if (is_array($result)) {
559
		if ($result['ok']) {
560
			unset($err_msg);
561
		} else {
562
			$err_msg = sprintf(gettext("Failed to send Slack notification. Error received was: %s"), $result['error']);
563
			log_error($err_msg);
564
		}
565
	} else {
566
		$err_msg = gettext("Slack API server did not return data in expected format!");
567
		log_error($err_msg);
568
	}
569
	return $err_msg;
570
}
571

    
572
function curl_post_notification($url, $data = array()) {
573
	$conn = curl_init($url);
574
	if (!empty($data)) {
575
		curl_setopt($conn, CURLOPT_POSTFIELDS, $data);
576
	}
577
	curl_setopt($conn, CURLOPT_SSL_VERIFYPEER, true);
578
	curl_setopt($conn, CURLOPT_FRESH_CONNECT,  true);
579
	curl_setopt($conn, CURLOPT_RETURNTRANSFER, true);
580
	set_curlproxy($conn);
581
	$curl_post_result = curl_exec($conn);
582
	curl_close($conn);
583
	return $curl_post_result;	//json encoded
584
}
585

    
586
/* Notify via remote methods only - not via GUI. */
587
function notify_all_remote($msg) {
588
	notify_via_smtp($msg);
589
	notify_via_telegram($msg);
590
	notify_via_pushover($msg);
591
	notify_via_slack($msg);
592
}
593

    
594
?>
(31-31/61)