Project

General

Profile

Download (15.5 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-2021 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['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' => 'Incomming',
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
	if (file_exists("/cf/conf/use_xmlreader")) {
214
		require_once("xmlreader.inc");
215
	} else {
216
		require_once("xmlparse.inc");
217
	}
218
	global $notice_path, $listtags;
219
	$listtags[] = 'notice';
220
	if (!$notices = get_notices()) {
221
		return;
222
	}
223
	foreach ($notices as $time => $notice) {
224
		$notice['time'] = $time;
225
		$toput['notice'][] = $notice;
226
	}
227
	$xml = dump_xml_config($toput, 'notices');
228
	return $xml;
229
}
230

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

    
247
function notices_sendqueue() {
248
	global $g;
249
	$nothing_done_count = 0;
250
	$messagequeue = array();
251

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

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

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

    
311
	mwexec_bg('/usr/local/bin/notify_monitor.php');
312
	unlock($notifyqueue_lck);
313
}
314

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

    
329
	if (isset($config['notifications']['smtp']['disable']) && !$force) {
330
		return;
331
	}
332

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

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

    
350
	return $ret;
351
}
352

    
353
function send_smtp_message($message, $subject = "(no subject)", $force = false) {
354
	global $config, $g;
355
	require_once("Mail.php");
356

    
357
	if (isset($config['notifications']['smtp']['disable']) && !$force) {
358
		return;
359
	}
360

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

    
365
	if (!$config['notifications']['smtp']['notifyemailaddress']) {
366
		return;
367
	}
368

    
369
	$to = $config['notifications']['smtp']['notifyemailaddress'];
370

    
371
	if (empty($config['notifications']['smtp']['username']) ||
372
	    empty($config['notifications']['smtp']['password'])) {
373
		$auth = false;
374
		$username = '';
375
		$password = '';
376
	} else {
377
		$auth = isset($config['notifications']['smtp']['authentication_mechanism'])
378
		    ? $config['notifications']['smtp']['authentication_mechanism']
379
		    : 'PLAIN';
380
		$username = $config['notifications']['smtp']['username'];
381
		$password = $config['notifications']['smtp']['password'];
382
	}
383

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

    
404
	if ($config['notifications']['smtp']['sslvalidate'] == "disabled") {
405
		$params['socket_options'] = array(
406
			'ssl' => array(
407
				'verify_peer_name' => false,
408
				'verify_peer' => false
409
		));
410
	}
411

    
412
	if ($config['notifications']['smtp']['fromaddress']) {
413
		$from = $config['notifications']['smtp']['fromaddress'];
414
	} else {
415
		$from = "pfsense@{$config['system']['hostname']}.{$config['system']['domain']}";
416
	}
417

    
418
	$headers = array(
419
		"From"    => $from,
420
		"To"      => $to,
421
		"Subject" => $subject,
422
		"Date"    => date("r")
423
	);
424

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

    
430
		if (PEAR::isError($mail)) {
431
			$err_msg = sprintf(gettext($error_text),
432
			    $to, $mail->getMessage());
433
		}
434
	} catch (Exception $e) {
435
		$err_msg = sprintf(gettext($error_text), $to, $e->getMessage());
436
	}
437

    
438
	if (!empty($err_msg)) {
439
		log_error($err_msg);
440
		return($err_msg);
441
	}
442

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

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

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

    
486
function curl_post_telegram($url) {
487
	$conn = curl_init($url);
488
	curl_setopt($conn, CURLOPT_SSL_VERIFYPEER, true);
489
	curl_setopt($conn, CURLOPT_FRESH_CONNECT,  true);
490
	curl_setopt($conn, CURLOPT_RETURNTRANSFER, 1);
491
	set_curlproxy($conn);
492
	$curl_post_telegram_data = curl_exec($conn);
493
	curl_close($conn);
494
	return $curl_post_telegram_data;	//json encoded
495
}
496

    
497
/****f* notices/notify_via_pushover
498
 * NAME
499
 *   notify_via_pushover
500
 * INPUTS
501
 *	 notification string to send to Pushover via API
502
 * RESULT
503
 *   returns NULL if message was sent
504
 ******/
505

    
506
function notify_via_pushover($message, $force = false) {
507
	global $config;
508
	if ((!isset($config['notifications']['pushover']['enabled']) && (!$force))
509
	    || !$config['notifications']['pushover']['apikey']
510
	    || !$config['notifications']['pushover']['userkey']) {
511
		if ($force) {
512
			return gettext("Unable to test Pushover notification without both API Key & User Key set");
513
		}
514
		return;
515
	}
516
	if (strcasecmp($config['notifications']['pushover']['sound'], 'devicedefault') == 0) {
517
		unset($config['notifications']['pushover']['sound']);
518
	}
519

    
520
	$url = "https://api.pushover.net/1/messages.json";
521
	$data = array(
522
		"token" => $config['notifications']['pushover']['apikey'],
523
		"user" => $config['notifications']['pushover']['userkey'],
524
		"sound" => $config['notifications']['pushover']['sound'],
525
		"priority" => $config['notifications']['pushover']['priority'],
526
		"retry" => $config['notifications']['pushover']['retry'],
527
		"expire" => $config['notifications']['pushover']['expire'],
528
		"message" => "{$config['system']['hostname']}.{$config['system']['domain']}\n{$message}"
529
	);
530
	$result = json_decode(curl_post_pushover($url, $data), true);
531
	if (is_array($result)) {
532
		if ($result['status']) {
533
			unset($err_msg);
534
		} else {
535
			$err_msg = sprintf(gettext("Failed to send Pushover notification. Error received was: %s"), $result['errors']['0']);
536
			log_error($err_msg);
537
		}
538
	} else {
539
		$err_msg = gettext("Pushover API server did not return data in expected format!");
540
		log_error($err_msg);
541
	}
542
	return $err_msg;
543
}
544

    
545
function curl_post_pushover($url, $data) {
546
	$conn = curl_init($url);
547
	curl_setopt($conn, CURLOPT_POSTFIELDS, $data);
548
	curl_setopt($conn, CURLOPT_SSL_VERIFYPEER, true);
549
	curl_setopt($conn, CURLOPT_FRESH_CONNECT,  true);
550
	curl_setopt($conn, CURLOPT_RETURNTRANSFER, 1);
551
	set_curlproxy($conn);
552
	$curl_post_pushover_data = curl_exec($conn);
553
	curl_close($conn);
554
	return $curl_post_pushover_data;	//json encoded
555
}
556

    
557
/* Notify via remote methods only - not via GUI. */
558
function notify_all_remote($msg) {
559
	notify_via_smtp($msg);
560
	notify_via_telegram($msg);
561
	notify_via_pushover($msg);
562
}
563

    
564
?>
(30-30/61)