Issues (2473)

Branch: master

Security Analysis    no vulnerabilities found

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

engine/lib/notification.php (3 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * Adding a New Notification Event
4
 * ===============================
5
 * 1. Register the event with elgg_register_notification_event()
6
 * 
7
 * 2. Register for the notification message plugin hook:
8
 *    'prepare', 'notification:[event name]'. The event name is of the form 
9
 *    [action]:[type]:[subtype]. For example, the publish event for a blog 
10
 *    would be named 'publish:object:blog'.
11
 * 
12
 *    The parameter array for the plugin hook has the keys 'event', 'method',
13
 *    'recipient', and 'language'. The event is an \Elgg\Notifications\Event 
14
 *    object and can provide access to the original object of the event through 
15
 *    the method getObject() and the original actor through getActor().
16
 * 
17
 *    The plugin hook callback modifies and returns a 
18
 *    \Elgg\Notifications\Notification object that holds the message content.
19
 *
20
 *
21
 * Adding a Delivery Method
22
 * =========================
23
 * 1. Register the delivery method name with elgg_register_notification_method()
24
 * 
25
 * 2. Register for the plugin hook for sending notifications:
26
 *    'send', 'notification:[method name]'. It receives the notification object 
27
 *    of the namespace Elgg\Notifications;
28
29
class Notification in the params array with the 
30
 *    key 'notification'. The callback should return a boolean to indicate whether 
31
 *    the message was sent.
32
 * 
33
 * 
34
 * Subscribing a User for Notifications
35
 * ====================================
36
 * Users subscribe to receive notifications based on container and delivery method.
37
 * 
38
 * 
39
 * @package Elgg.Core
40
 * @subpackage Notifications
41
 */
42
43
/**
44
 * Register a notification event
45
 *
46
 * Elgg sends notifications for the items that have been registered with this
47
 * function. For example, if you want notifications to be sent when a bookmark
48
 * has been created or updated, call the function like this:
49
 *
50
 * 	   elgg_register_notification_event('object', 'bookmarks', array('create', 'update'));
51
 *
52
 * @param string $object_type    'object', 'user', 'group', 'site'
53
 * @param string $object_subtype The subtype or name of the entity
54
 * @param array  $actions        Array of actions or empty array for the action event.
55
 *                                An event is usually described by the first string passed
56
 *                                to elgg_trigger_event(). Examples include
57
 *                                'create', 'update', and 'publish'. The default is 'create'.
58
 * @return void
59
 * @since 1.9
60
 */
61
function elgg_register_notification_event($object_type, $object_subtype, array $actions = array()) {
62
	_elgg_services()->notifications->registerEvent($object_type, $object_subtype, $actions);
63
}
64
65
/**
66
 * Unregister a notification event
67
 *
68
 * @param string $object_type    'object', 'user', 'group', 'site'
69
 * @param string $object_subtype The type of the entity
70
 * @return bool
71
 * @since 1.9
72
 */
73
function elgg_unregister_notification_event($object_type, $object_subtype) {
74
	return _elgg_services()->notifications->unregisterEvent($object_type, $object_subtype);
75
}
76
77
/**
78
 * Register a delivery method for notifications
79
 * 
80
 * Register for the 'send', 'notification:[method name]' plugin hook to handle
81
 * sending a notification. A notification object is in the params array for the
82
 * hook with the key 'notification'. See \Elgg\Notifications\Notification.
83
 *
84
 * @param string $name The notification method name
85
 * @return void
86
 * @see elgg_unregister_notification_method()
87
 * @since 1.9
88
 */
89
function elgg_register_notification_method($name) {
90
	_elgg_services()->notifications->registerMethod($name);
91
}
92
93
/**
94
 * Unregister a delivery method for notifications
95
 *
96
 * @param string $name The notification method name
97
 * @return bool
98
 * @see elgg_register_notification_method()
99
 * @since 1.9
100
 */
101
function elgg_unregister_notification_method($name) {
102
	return _elgg_services()->notifications->unregisterMethod($name);
103
}
104
105
/**
106
 * Subscribe a user to notifications about a target entity
107
 *
108
 * @param int    $user_guid   The GUID of the user to subscribe to notifications
109
 * @param string $method      The delivery method of the notifications
110
 * @param int    $target_guid The entity to receive notifications about
111
 * @return bool
112
 * @since 1.9
113
 */
114 View Code Duplication
function elgg_add_subscription($user_guid, $method, $target_guid) {
115
	$methods = _elgg_services()->notifications->getMethods();
116
	$db = _elgg_services()->db;
117
	$subs = new \Elgg\Notifications\SubscriptionsService($db, $methods);
118
	return $subs->addSubscription($user_guid, $method, $target_guid);
119
}
120
121
/**
122
 * Unsubscribe a user to notifications about a target entity
123
 *
124
 * @param int    $user_guid   The GUID of the user to unsubscribe to notifications
125
 * @param string $method      The delivery method of the notifications to stop
126
 * @param int    $target_guid The entity to stop receiving notifications about
127
 * @return bool
128
 * @since 1.9
129
 */
130 View Code Duplication
function elgg_remove_subscription($user_guid, $method, $target_guid) {
131
	$methods = _elgg_services()->notifications->getMethods();
132
	$db = _elgg_services()->db;
133
	$subs = new \Elgg\Notifications\SubscriptionsService($db, $methods);
134
	return $subs->removeSubscription($user_guid, $method, $target_guid);
135
}
136
137
/**
138
 * Get the subscriptions for the content created inside this container.
139
 *
140
 * The return array is of the form:
141
 *
142
 * array(
143
 *     <user guid> => array('email', 'sms', 'ajax'),
144
 * );
145
 *
146
 * @param int $container_guid GUID of the entity acting as a container
147
 * @return array User GUIDs (keys) and their subscription types (values).
148
 * @since 1.9
149
 * @todo deprecate once new subscriptions system has been added
150
 */
151 View Code Duplication
function elgg_get_subscriptions_for_container($container_guid) {
152
	$methods = _elgg_services()->notifications->getMethods();
153
	$db = _elgg_services()->db;
154
	$subs = new \Elgg\Notifications\SubscriptionsService($db, $methods);
155
	return $subs->getSubscriptionsForContainer($container_guid);
156
}
157
158
/**
159
 * Queue a notification event for later handling
160
 *
161
 * Checks to see if this event has been registered for notifications.
162
 * If so, it adds the event to a notification queue.
163
 *
164
 * This function triggers the 'enqueue', 'notification' hook.
165
 *
166
 * @param string   $action The name of the action
167
 * @param string   $type   The type of the object
168
 * @param \ElggData $object The object of the event
169
 * @return void
170
 * @access private
171
 * @since 1.9
172
 */
173
function _elgg_enqueue_notification_event($action, $type, $object) {
174
	_elgg_services()->notifications->enqueueEvent($action, $type, $object);
175
}
176
177
/**
178
 * @access private
179
 */
180
function _elgg_notifications_cron() {
181
	// calculate when we should stop
182
	// @todo make configurable?
183
	$stop_time = time() + 45;
184
	_elgg_services()->notifications->processQueue($stop_time);
185
}
186
187
/**
188
 * Send an email notification
189
 * 
190
 * @param string $hook   Hook name
191
 * @param string $type   Hook type
192
 * @param bool   $result Has anyone sent a message yet?
193
 * @param array  $params Hook parameters
194
 * @return bool
195
 * @access private
196
 */
197
function _elgg_send_email_notification($hook, $type, $result, $params) {
198
	
199
	if ($result === true) {
200
		// assume someone else already sent the message
201
		return;
202
	}
203
	
204
	/* @var \Elgg\Notifications\Notification $message */
205
	$message = $params['notification'];
206
207
	$sender = $message->getSender();
208
	$recipient = $message->getRecipient();
209
210
	if (!$sender) {
211
		return false;
212
	}
213
214
	if (!$recipient || !$recipient->email) {
215
		return false;
216
	}
217
218
	$to = $recipient->email;
219
220
	$site = elgg_get_site_entity();
221
	// If there's an email address, use it - but only if it's not from a user.
222
	if (!($sender instanceof \ElggUser) && $sender->email) {
223
		$from = $sender->email;
224
	} else if ($site->email) {
225
		$from = $site->email;
226
	} else {
227
		// If all else fails, use the domain of the site.
228
		$from = 'noreply@' . $site->getDomain();
229
	}
230
231
	return elgg_send_email($from, $to, $message->subject, $message->body, $params);
232
}
233
234
/**
235
 * Adds default Message-ID header to all e-mails
236
 *
237
 * @param string $hook        Equals to 'email'
238
 * @param string $type        Equals to 'system'
239
 * @param array  $returnvalue Array containing fields: 'to', 'from', 'subject', 'body', 'headers', 'params'
240
 * @param array  $params      The same value as $returnvalue
241
 * 
242
 * @see https://tools.ietf.org/html/rfc5322#section-3.6.4
243
 * 
244
 * @return array
245
 * 
246
 * @access private
247
 */
248
function _elgg_notifications_smtp_default_message_id_header($hook, $type, $returnvalue, $params) {
249
	
250
	if (!is_array($returnvalue) || !is_array($returnvalue['params'])) {
251
		// another hook handler returned a non-array, let's not override it
252
		return;
253
	}
254
	
255
	$hostname = parse_url(elgg_get_site_url(), PHP_URL_HOST);
256
	$url_path = parse_url(elgg_get_site_url(), PHP_URL_PATH);
257
	
258
	$mt = microtime(true);
259
	
260
	$returnvalue['headers']['Message-ID'] = "<{$url_path}.default.{$mt}@{$hostname}>";
261
	
262
	return $returnvalue;
263
}
264
/**
265
 * Adds default thread SMTP headers to group messages correctly.
266
 * Note that it won't be sufficient for some email clients. Ie. Gmail is looking at message subject anyway.
267
 *
268
 * @param string $hook        Equals to 'email'
269
 * @param string $type        Equals to 'system'
270
 * @param array  $returnvalue Array containing fields: 'to', 'from', 'subject', 'body', 'headers', 'params'
271
 * @param array  $params      The same value as $returnvalue
272
 * @return array
273
 * @access private
274
 */
275
function _elgg_notifications_smtp_thread_headers($hook, $type, $returnvalue, $params) {
276
277
	if (!is_array($returnvalue) || !is_array($returnvalue['params'])) {
278
		// another hook handler returned a non-array, let's not override it
279
		return;
280
	}
281
	
282
	$notificationParams = elgg_extract('params', $returnvalue, array());
283
	/** @var \Elgg\Notifications\Notification */
284
	$notification = elgg_extract('notification', $notificationParams);
285
286
	if (!($notification instanceof \Elgg\Notifications\Notification)) {
287
		return $returnvalue;
288
	}
289
290
	$hostname = parse_url(elgg_get_site_url(), PHP_URL_HOST);
291
	$urlPath = parse_url(elgg_get_site_url(), PHP_URL_PATH);
292
293
	$object = elgg_extract('object', $notification->params);
294
	/** @var \Elgg\Notifications\Event $event */
295
	$event = elgg_extract('event', $notification->params);
296
297
	if (($object instanceof \ElggEntity) && ($event instanceof \Elgg\Notifications\Event)) {
298
		if ($event->getAction() === 'create') {
299
			// create event happens once per entity and we need to guarantee message id uniqueness
300
			// and at the same time have thread message id that we don't need to store
301
			$messageId = "<{$urlPath}.entity.{$object->guid}@{$hostname}>";
302
		} else {
303
			$mt = microtime(true);
304
			$messageId = "<{$urlPath}.entity.{$object->guid}.$mt@{$hostname}>";
305
		}
306
		$returnvalue['headers']["Message-ID"] = $messageId;
307
		$container = $object->getContainerEntity();
308
309
		// let's just thread comments by default
310
		if (($container instanceof \ElggEntity) && ($object instanceof \ElggComment)) {
311
312
			$threadMessageId = "<{$urlPath}.entity.{$container->guid}@{$hostname}>";
313
			$returnvalue['headers']['In-Reply-To'] = $threadMessageId;
314
			$returnvalue['headers']['References'] = $threadMessageId;
315
		}
316
	}
317
318
	return $returnvalue;
319
}
320
321
/**
322
 * @access private
323
 */
324 View Code Duplication
function _elgg_notifications_init() {
325
	elgg_register_plugin_hook_handler('cron', 'minute', '_elgg_notifications_cron', 100);
326
	elgg_register_event_handler('all', 'all', '_elgg_enqueue_notification_event');
327
328
	// add email notifications
329
	elgg_register_notification_method('email');
330
	elgg_register_plugin_hook_handler('send', 'notification:email', '_elgg_send_email_notification');
331
	elgg_register_plugin_hook_handler('email', 'system', '_elgg_notifications_smtp_default_message_id_header', 1);
332
	elgg_register_plugin_hook_handler('email', 'system', '_elgg_notifications_smtp_thread_headers');
333
334
	// add ability to set personal notification method
335
	elgg_extend_view('forms/account/settings', 'core/settings/account/notifications');
336
	elgg_register_plugin_hook_handler('usersettings:save', 'user', '_elgg_save_notification_user_settings');
337
}
338
339
/**
340
 * Notify a user via their preferences.
341
 *
342
 * @param mixed  $to               Either a guid or an array of guid's to notify.
343
 * @param int    $from             GUID of the sender, which may be a user, site or object.
344
 * @param string $subject          Message subject.
345
 * @param string $message          Message body.
346
 * @param array  $params           Misc additional parameters specific to various methods.
347
 * @param mixed  $methods_override A string, or an array of strings specifying the delivery
348
 *                                 methods to use - or leave blank for delivery using the
349
 *                                 user's chosen delivery methods.
350
 *
351
 * @return array Compound array of each delivery user/delivery method's success or failure.
352
 * @access private
353
 */
354
function _elgg_notify_user($to, $from, $subject, $message, array $params = null, $methods_override = "") {
355
356
	$notify_service = _elgg_services()->notifications;
357
358
	// Sanitise
359
	if (!is_array($to)) {
360
		$to = array((int)$to);
361
	}
362
	$from = (int)$from;
363
	//$subject = sanitise_string($subject);
364
365
	// Get notification methods
366
	if (($methods_override) && (!is_array($methods_override))) {
367
		$methods_override = array($methods_override);
368
	}
369
370
	$result = array();
371
372
	foreach ($to as $guid) {
373
		// Results for a user are...
374
		$result[$guid] = array();
375
376
		if ($guid) { // Is the guid > 0?
377
			// Are we overriding delivery?
378
			$methods = $methods_override;
379 View Code Duplication
			if (!$methods) {
380
				$tmp = get_user_notification_settings($guid);
381
				$methods = array();
382
				// $tmp may be false. don't cast
383
				if (is_object($tmp)) {
384
					foreach ($tmp as $k => $v) {
0 ignored issues
show
The expression $tmp of type object<stdClass> is not traversable.
Loading history...
385
						// Add method if method is turned on for user!
386
						if ($v) {
387
							$methods[] = $k;
388
						}
389
					}
390
				}
391
			}
392
393
			if ($methods) {
394
				// Deliver
395
				foreach ($methods as $method) {
396
					
397
					$handler = $notify_service->getDeprecatedHandler($method);
398
					/* @var callable $handler */
399
					if (!$handler || !is_callable($handler)) {
400
						elgg_log("No handler registered for the method $method", 'WARNING');
401
						continue;
402
					}
403
404
					elgg_log("Sending message to $guid using $method");
405
406
					// Trigger handler and retrieve result.
407
					try {
408
						$result[$guid][$method] = call_user_func($handler,
409
							$from ? get_entity($from) : null,
410
							get_entity($guid),
411
							$subject,
412
							$message,
413
							$params
414
						);
415
					} catch (Exception $e) {
416
						error_log($e->getMessage());
417
					}
418
				}
419
			}
420
		}
421
	}
422
423
	return $result;
424
}
425
426
427
/**
428
 * Notifications
429
 * This file contains classes and functions which allow plugins to register and send notifications.
430
 *
431
 * There are notification methods which are provided out of the box
432
 * (see notification_init() ). Each method is identified by a string, e.g. "email".
433
 *
434
 * To register an event use register_notification_handler() and pass the method name and a
435
 * handler function.
436
 *
437
 * To send a notification call notify() passing it the method you wish to use combined with a
438
 * number of method specific addressing parameters.
439
 *
440
 * Catch NotificationException to trap errors.
441
 *
442
 * @package Elgg.Core
443
 * @subpackage Notifications
444
 */
445
446
447
/**
448
 * Notify a user via their preferences.
449
 *
450
 * @param mixed  $to               Either a guid or an array of guid's to notify.
451
 * @param int    $from             GUID of the sender, which may be a user, site or object.
452
 * @param string $subject          Message subject.
453
 * @param string $message          Message body.
454
 * @param array  $params           Misc additional parameters specific to various methods.
455
 *
456
 *                                 By default Elgg core supports three parameters, which give
457
 *                                 notification plugins more control over the notifications:
458
 *
459
 *                                 object => null|\ElggEntity|\ElggAnnotation The object that
460
 *                                           is triggering the notification.
461
 *
462
 *                                 action => null|string Word that describes the action that
463
 *                                           is triggering the notification (e.g. "create"
464
 *                                           or "update").
465
 *
466
 *                                 summary => null|string Summary that notification plugins
467
 *                                            can use alongside the notification title and body.
468
 *
469
 * @param mixed  $methods_override A string, or an array of strings specifying the delivery
470
 *                                 methods to use - or leave blank for delivery using the
471
 *                                 user's chosen delivery methods.
472
 *
473
 * @return array Compound array of each delivery user/delivery method's success or failure.
474
 * @throws NotificationException
475
 */
476
function notify_user($to, $from, $subject, $message, array $params = array(), $methods_override = "") {
477
478
	if (!is_array($to)) {
479
		$to = array((int)$to);
480
	}
481
	$from = (int)$from;
482
	$from = get_entity($from) ? $from : elgg_get_site_entity()->guid;
483
	$sender = get_entity($from);
484
	$summary = elgg_extract('summary', $params, '');
485
486
	// Get notification methods
487
	if (($methods_override) && (!is_array($methods_override))) {
488
		$methods_override = array($methods_override);
489
	}
490
491
	$result = array();
492
493
	$available_methods = _elgg_services()->notifications->getMethods();
494
	if (!$available_methods) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $available_methods of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
495
		// There are no notifications methods to use
496
		return $result;
497
	}
498
499
	// temporary backward compatibility for 1.8 and earlier notifications
500
	$event = null;
501
	if (isset($params['object']) && isset($params['action'])) {
502
		$event = new \Elgg\Notifications\Event($params['object'], $params['action'], $sender);
503
	}
504
	$params['event'] = $event;
505
506
	foreach ($to as $guid) {
507
		// Results for a user are...
508
		$result[$guid] = array();
509
510
		if ($guid) { // Is the guid > 0?
511
			// Are we overriding delivery?
512
			$methods = $methods_override;
513 View Code Duplication
			if (!$methods) {
514
				$tmp = (array)get_user_notification_settings($guid);
515
				$methods = array();
516
				foreach ($tmp as $k => $v) {
517
					// Add method if method is turned on for user!
518
					if ($v) {
519
						$methods[] = $k;
520
					}
521
				}
522
			}
523
524
			if ($methods) {
525
				// Deliver
526
				foreach ($methods as $method) {
527
					if (!in_array($method, $available_methods)) {
528
						// This method was available the last time the user saved their
529
						// notification settings. It's however currently disabled.
530
						continue;
531
					}
532
533
					if (_elgg_services()->hooks->hasHandler('send', "notification:$method")) {
534
						// 1.9 style notification handler
535
						$recipient = get_entity($guid);
536
						if (!$recipient) {
537
							continue;
538
						}
539
						$language = $recipient->language;
540
						$notification = new \Elgg\Notifications\Notification($sender, $recipient, $language, $subject, $message, $summary, $params);
541
						$params['notification'] = $notification;
542
						$result[$guid][$method] = _elgg_services()->hooks->trigger('send', "notification:$method", $params, false);
543
					} else {
544
						$result[$guid][$method] = _elgg_notify_user($guid, $from, $subject, $message, $params, array($method));
545
					}
546
				}
547
			}
548
		}
549
	}
550
551
	return $result;
552
}
553
554
/**
555
 * Get the notification settings for a given user.
556
 *
557
 * @param int $user_guid The user id
558
 *
559
 * @return \stdClass|false
560
 */
561
function get_user_notification_settings($user_guid = 0) {
562
	$user_guid = (int)$user_guid;
563
564
	if ($user_guid == 0) {
565
		$user_guid = elgg_get_logged_in_user_guid();
566
	}
567
568
	// @todo: there should be a better way now that metadata is cached. E.g. just query for MD names, then
569
	// query user object directly
570
	$all_metadata = elgg_get_metadata(array(
571
		'guid' => $user_guid,
572
		'limit' => 0
573
	));
574
	if ($all_metadata) {
575
		$prefix = "notification:method:";
576
		$return = new \stdClass;
577
578
		foreach ($all_metadata as $meta) {
579
			$name = substr($meta->name, strlen($prefix));
580
			$value = $meta->value;
581
582
			if (strpos($meta->name, $prefix) === 0) {
583
				$return->$name = $value;
584
			}
585
		}
586
587
		return $return;
588
	}
589
590
	return false;
591
}
592
593
/**
594
 * Set a user notification pref.
595
 *
596
 * @param int    $user_guid The user id.
597
 * @param string $method    The delivery method (eg. email)
598
 * @param bool   $value     On(true) or off(false).
599
 *
600
 * @return bool
601
 */
602
function set_user_notification_setting($user_guid, $method, $value) {
603
	$user_guid = (int)$user_guid;
604
	$method = sanitise_string($method);
605
606
	$user = get_entity($user_guid);
607
	if (!$user) {
608
		$user = elgg_get_logged_in_user_entity();
609
	}
610
611
	if (($user) && ($user instanceof \ElggUser)) {
612
		$prefix = "notification:method:$method";
613
		$user->$prefix = $value;
614
		$user->save();
615
616
		return true;
617
	}
618
619
	return false;
620
}
621
622
/**
623
 * Send an email to any email address
624
 *
625
 * @param string $from    Email address or string: "name <email>"
626
 * @param string $to      Email address or string: "name <email>"
627
 * @param string $subject The subject of the message
628
 * @param string $body    The message body
629
 * @param array  $params  Optional parameters (none used in this function)
630
 *
631
 * @return bool
632
 * @throws NotificationException
633
 * @since 1.7.2
634
 */
635
function elgg_send_email($from, $to, $subject, $body, array $params = null) {
636
	global $CONFIG;
637
638
	if (!$from) {
639
		$msg = "Missing a required parameter, '" . 'from' . "'";
640
		throw new \NotificationException($msg);
641
	}
642
643
	if (!$to) {
644
		$msg = "Missing a required parameter, '" . 'to' . "'";
645
		throw new \NotificationException($msg);
646
	}
647
648
	$headers = array(
649
		"Content-Type" => "text/plain; charset=UTF-8; format=flowed",
650
		"MIME-Version" => "1.0",
651
		"Content-Transfer-Encoding" => "8bit",
652
	);
653
654
	// return true/false to stop elgg_send_email() from sending
655
	$mail_params = array(
656
		'to' => $to,
657
		'from' => $from,
658
		'subject' => $subject,
659
		'body' => $body,
660
		'headers' => $headers,
661
		'params' => $params,
662
	);
663
664
	// $mail_params is passed as both params and return value. The former is for backwards
665
	// compatibility. The latter is so handlers can now alter the contents/headers of
666
	// the email by returning the array
667
	$result = elgg_trigger_plugin_hook('email', 'system', $mail_params, $mail_params);
668
	if (is_array($result)) {
669
		foreach (array('to', 'from', 'subject', 'body', 'headers') as $key) {
670
			if (isset($result[$key])) {
671
				${$key} = $result[$key];
672
			}
673
		}
674
	} elseif ($result !== null) {
675
		return $result;
676
	}
677
678
	$header_eol = "\r\n";
679
	if (isset($CONFIG->broken_mta) && $CONFIG->broken_mta) {
680
		// Allow non-RFC 2822 mail headers to support some broken MTAs
681
		$header_eol = "\n";
682
	}
683
684
	// Windows is somewhat broken, so we use just address for to and from
685
	if (strtolower(substr(PHP_OS, 0, 3)) == 'win') {
686
		// strip name from to and from
687
		if (strpos($to, '<')) {
688
			preg_match('/<(.*)>/', $to, $matches);
689
			$to = $matches[1];
690
		}
691
		if (strpos($from, '<')) {
692
			preg_match('/<(.*)>/', $from, $matches);
693
			$from = $matches[1];
694
		}
695
	}
696
697
	// make sure From is set
698
	if (empty($headers['From'])) {
699
		$headers['From'] = $from;
700
	}
701
702
	// stringify headers
703
	$headers_string = '';
704
	foreach ($headers as $key => $value) {
705
		$headers_string .= "$key: $value{$header_eol}";
706
	}
707
708
	// Sanitise subject by stripping line endings
709
	$subject = preg_replace("/(\r\n|\r|\n)/", " ", $subject);
710
	// this is because Elgg encodes everything and matches what is done with body
711
	$subject = html_entity_decode($subject, ENT_QUOTES, 'UTF-8'); // Decode any html entities
712
	if (is_callable('mb_encode_mimeheader')) {
713
		$subject = mb_encode_mimeheader($subject, "UTF-8", "B");
0 ignored issues
show
Are you sure the assignment to $subject is correct as mb_encode_mimeheader($subject, 'UTF-8', 'B') (which targets mb_encode_mimeheader()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
714
	}
715
716
	// Format message
717
	$body = html_entity_decode($body, ENT_QUOTES, 'UTF-8'); // Decode any html entities
718
	$body = elgg_strip_tags($body); // Strip tags from message
719
	$body = preg_replace("/(\r\n|\r)/", "\n", $body); // Convert to unix line endings in body
720
	$body = preg_replace("/^From/", ">From", $body); // Change lines starting with From to >From
721
	$body = wordwrap($body);
722
723
	return mail($to, $subject, $body, $headers_string);
724
}
725
726
/**
727
 * Save personal notification settings - input comes from request
728
 *
729
 * @return void
730
 * @access private
731
 */
732
function _elgg_save_notification_user_settings() {
733
	$method = get_input('method');
734
735
	$current_settings = get_user_notification_settings();
736
737
	$result = false;
738
	foreach ($method as $k => $v) {
739
		// check if setting has changed and skip if not
740
		if ($current_settings->$k == ($v == 'yes')) {
741
			continue;
742
		}
743
744
		$result = set_user_notification_setting(elgg_get_logged_in_user_guid(), $k, ($v == 'yes') ? true : false);
745
746
		if (!$result) {
747
			register_error(elgg_echo('notifications:usersettings:save:fail'));
748
		}
749
	}
750
751
	if ($result) {
752
		system_message(elgg_echo('notifications:usersettings:save:ok'));
753
	}
754
}
755
756
/**
757
 * @access private
758
 */
759
function _elgg_notifications_test($hook, $type, $tests) {
760
	global $CONFIG;
761
	$tests[] = "{$CONFIG->path}engine/tests/ElggCoreDatabaseQueueTest.php";
762
	return $tests;
763
}
764
765
return function(\Elgg\EventsService $events, \Elgg\HooksRegistrationService $hooks) {
766
	$events->registerHandler('init', 'system', '_elgg_notifications_init');
767
768
	$hooks->registerHandler('unit_test', 'system', '_elgg_notifications_test');
769
};
770