Issues (19)

Security Analysis    no request data  

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.

src/SemanticTasksMailer.php (1 issue)

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
namespace ST;
4
5
use Content;
6
use ContentHandler;
7
use Exception;
8
use IContextSource;
9
use Language;
10
use MediaWiki\Diff\ComplexityException;
11
use MWException;
12
use ParserOutput;
13
use SMW\ApplicationFactory;
14
use SMW\DIWikiPage;
15
use SMWDataItem;
16
use SMWPrintRequest;
17
use Title;
18
use User;
19
use WikiPage;
20
21
if ( !defined( 'MEDIAWIKI' ) ) {
22
	echo 'Not a valid entry point';
23
	exit( 1 );
24
}
25
26
if ( !defined( 'SMW_VERSION' ) ) {
27
	echo 'This extension requires Semantic MediaWiki to be installed.';
28
	exit( 1 );
29
}
30
31
// constants for message type
32
if ( !defined( 'ST_NEWTASK' ) ) {
33
	define( 'ST_NEWTASK', 0 );
34
	define( 'ST_UPDATE', 1 );
35
	define( 'ST_ASSIGNED', 2 );
36
	define( 'ST_CLOSED', 3 );
37
	define( 'ST_UNASSIGNED', 4 );
38
}
39
40
/**
41
 * This class handles the creation and sending of notification emails.
42
 */
43
class SemanticTasksMailer {
44
45
	private static $user_mailer;
46
47
	/**
48
	 * Mails the assignees when the task is modified
49
	 *
50
	 * @param Assignees $assignees
51
	 * @param WikiPage $article
52
	 * @param User $current_user
53
	 * @param Content $text
54
	 * @param string $summary Unused
55
	 * @param bool $minoredit
56
	 * @param null $watchthis Unused
57
	 * @param null $sectionanchor Unused
58
	 * @param $flags
59
	 * @return boolean
60
	 * @throws ComplexityException
61
	 * @throws MWException
62
	 */
63
	public static function mailAssigneesUpdatedTask( Assignees $assignees, WikiPage $article, User $current_user, $text,
64
			$summary, $minoredit, $watchthis, $sectionanchor, $flags, $revision ) {
65
		if ( $minoredit ) {
66
			return true;
67
		}
68
		$status = ST_UPDATE;
69
		if ( ( $flags & EDIT_NEW ) && !$article->getTitle()->isTalkPage() ) {
70
			$status = ST_NEWTASK;
71
		}
72
73
		return self::mailAssignees( $article, $text, $current_user, $status, $assignees, $revision );
74
	}
75
76
	/**
77
	 *
78
	 * @param WikiPage $article
79
	 * @param Content $content
80
	 * @param User $user
81
	 * @param integer $status
82
	 * @param Assignees $assignees
83
	 * @return boolean
84
	 * @throws ComplexityException
85
	 * @throws MWException
86
	 * @global boolean $wgSemanticTasksNotifyIfUnassigned
87
	 */
88
	static function mailAssignees( WikiPage $article, Content $content, User $user, $status, Assignees $assignees,
89
								   $revision ) {
90
		$text = ContentHandler::getContentText( $content );
91
		$title = $article->getTitle();
92
93
		// Notify those unassigned from this task
94
		global $wgSemanticTasksNotifyIfUnassigned;
95
		if ( $wgSemanticTasksNotifyIfUnassigned ) {
96
			$removedAssignees = $assignees->getRemovedAssignees( $article, $revision );
97
			$removedAssignees = Assignees::getAssigneeAddresses( $removedAssignees );
98
			self::mailNotification( $removedAssignees, $text, $title, $user, ST_UNASSIGNED );
99
		}
100
101
		// Send notification of an assigned task to assignees
102
		// Treat task as new
103
		$newAssignees = $assignees->getNewAssignees( $article, $revision );
104
		$newAssignees = Assignees::getAssigneeAddresses( $newAssignees );
105
		self::mailNotification( $newAssignees, $text, $title, $user, ST_ASSIGNED );
106
107
		$copies = $assignees->getCurrentCarbonCopy( $article, $revision );
108
		$currentStatus = $assignees->getCurrentStatus( $article, $revision );
109
		$oldStatus = $assignees->getSavedStatus();
110
		if ( $currentStatus === "Closed" && $oldStatus !== "Closed" ) {
111
			$close_mailto = Assignees::getAssigneeAddresses( $copies );
112
			self::mailNotification( $close_mailto, $text, $title, $user, ST_CLOSED );
113
		}
114
115
		$currentAssignees = $assignees->getCurrentAssignees( $article, $revision );
116
117
		// Only send group notifications on new tasks
118
		$groups = array();
119
		if ( $status === ST_NEWTASK ) {
120
			$groups = $assignees->getGroupAssignees( $article );
121
		}
122
123
		$mailto = array_merge( $currentAssignees, $copies, $groups );
124
		$mailto = array_unique( $mailto );
125
		$mailto = Assignees::getAssigneeAddresses( $mailto );
126
127
		// Send notifications to assignees, ccs, and groups
128
		self::mailNotification( $mailto, $text, $title, $user, $status );
129
130
		return true;
131
	}
132
133
	/**
134
	 * Sends mail notifications
135
	 *
136
	 * @param array $assignees
137
	 * @param string $text
138
	 * @param Title $title
139
	 * @param User $user
140
	 * @param integer $status
141
	 * @throws MWException
142
	 * @throws ComplexityException
143
	 * @global string $wgSitename
144
	 */
145
	static function mailNotification( array $assignees, $text, Title $title, User $user, $status ) {
146
		global $wgSitename;
147
148
		if ( empty( $assignees ) ) {
149
			return;
150
		}
151
		$title_text = $title->getFullText();
152
		$from = new \MailAddress( $user->getEmail(), $user->getName() );
153
		$link = htmlspecialchars( $title->getFullURL() );
154
155
		/** @todo This should probably be refactored */
156
		if ( $status == ST_NEWTASK ) {
157
			$subject = '[' . $wgSitename . '] ' . wfMessage( 'semantictasks-newtask' )->text() . ' ' .
158
				$title_text;
159
			$message = 'semantictasks-newtask-msg';
160
			$body = wfMessage( $message, $title_text )->text() . " " . $link;
161
			$body .= "\n \n" . wfMessage( 'semantictasks-text-message' )->text() . "\n" . $text;
162
		} elseif ( $status == ST_UPDATE ) {
163
			$subject = '[' . $wgSitename . '] ' . wfMessage( 'semantictasks-taskupdated' )->text() . ' ' .
164
				$title_text;
165
			$message = 'semantictasks-updatedtoyou-msg2';
166
			$body = wfMessage( $message, $title_text )->text() . " " . $link;
167
			$body .= "\n \n" . wfMessage( 'semantictasks-diff-message' )->text() . "\n" .
168
				self::generateDiffBodyTxt( $title );
169
		} elseif ( $status == ST_CLOSED ) {
170
			$subject = '[' . $wgSitename . '] ' . wfMessage( 'semantictasks-taskclosed' )->text() . ' ' .
171
				$title_text;
172
			$message = 'semantictasks-taskclosed-msg';
173
			$body = wfMessage( $message, $title_text )->text() . " " . $link;
174
			$body .= "\n \n" . wfMessage( 'semantictasks-text-message' )->text() . "\n" . $text;
175
		} elseif ( $status == ST_UNASSIGNED ) {
176
			$subject = '[' . $wgSitename . '] ' . wfMessage( 'semantictasks-taskunassigned' )->text() . ' ' .
177
				$title_text;
178
			$message = 'semantictasks-unassignedtoyou-msg2';
179
			$body = wfMessage( $message, $title_text )->text() . " " . $link;
180
			$body .= "\n \n" . wfMessage( 'semantictasks-text-message' )->text() . "\n" . $text;
181
		} else {
182
			// status == ASSIGNED
183
			$subject = '[' . $wgSitename . '] ' . wfMessage( 'semantictasks-taskassigned' )->text() . ' ' .
184
				$title_text;
185
			$message = 'semantictasks-assignedtoyou-msg2';
186
			$body = wfMessage( $message, $title_text )->text() . " " . $link;
187
			$body .= "\n \n" . wfMessage( 'semantictasks-text-message' )->text() . "\n" . $text;
188
		}
189
190
		if (!self::$user_mailer) {
191
			self::$user_mailer = new \ST\UserMailer(new \UserMailer());
192
		}
193
194
		self::$user_mailer->send( $assignees, $from, $subject, $body );
195
	}
196
197
	static function setUserMailer(\ST\UserMailer $user_mailer) {
198
		self::$user_mailer = $user_mailer;
199
	}
200
201
	/**
202
	 * Generates a diff txt
203
	 *
204
	 * Code is similar to DifferenceEngine::generateTextDiffBody
205
	 * @param Title $title
206
	 * @param IContextSource $context
207
	 * @return string
208
	 * @throws ComplexityException
209
	 * @throws MWException
210
	 */
211
	static function generateDiffBodyTxt( Title $title, IContextSource $context = null) {
212
		$revision = \Revision::newFromTitle( $title, 0 );
213
		if ($revision === null) {
214
			return '';
215
		}
216
		/** @todo The first parameter should be a Context. */
217
		$diff = new \DifferenceEngine( $context, $revision->getId(), 'prev' );
218
		// The DifferenceEngine::getDiffBody() method generates html,
219
		// so let's generate the txt diff manually:
220
		global $wgContLang;
221
		$diff->loadText();
222
		$otext = '';
223
		$ntext = '';
224
		if ( version_compare( MW_VERSION, '1.32', '<' ) ) {
225
			$otext = str_replace( "\r\n", "\n", \ContentHandler::getContentText( $diff->mOldContent ) );
226
			$ntext = str_replace( "\r\n", "\n", \ContentHandler::getContentText( $diff->mNewContent ) );
227
		} else {
228
			if ($diff->getOldRevision()) {
229
				$otext = str_replace( "\r\n", "\n", ContentHandler::getContentText( $diff->getOldRevision()->getContent( 'main' ) ) );
230
			}
231
			if ($diff->getNewRevision()) {
232
				$ntext = str_replace( "\r\n", "\n", ContentHandler::getContentText( $diff->getNewRevision()->getContent( 'main' ) ) );
233
			}
234
		}
235
		$ota = explode( "\n", $wgContLang->segmentForDiff( $otext ) );
236
		$nta = explode( "\n", $wgContLang->segmentForDiff( $ntext ) );
237
		// We use here the php diff engine included in MediaWiki
238
		$diffs = new \Diff( $ota, $nta );
239
		// And we ask for a txt formatted diff
240
		$formatter = new \UnifiedDiffFormatter();
241
		$diff_text = $wgContLang->unsegmentForDiff( $formatter->format( $diffs ) );
242
		return $diff_text;
243
	}
244
245
	/**
246
	 * Run by the maintenance script to remind the assignees
247
	 *
248
	 * @return boolean
249
	 * @throws Exception
250
	 * @global string $wgSitename
251
	 * @global Language $wgLang
252
	 */
253
	static function remindAssignees() {
254
		global $wgSitename;
255
		global $stgPropertyReminderAt;
256
		global $stgPropertyAssignedTo;
257
		global $stgPropertyTargetDate;
258
		global $stgPropertyStatus;
259
260
		# Make this equal to midnight. Rational is that if users set today as the Target date with
261
		# reminders set to "0" so that the reminder happens on the deadline, the reminders will go
262
		# out even though now it is after the beginning of today and technically past the
263
		# target date.
264
		$today = wfTimestamp( TS_ISO_8601, strtotime( 'today midnight' ) );
265
266
		# Get tasks where a reminder is called for, whose status is either new or in progress, and
267
		# whose target date is in the future.
268
		$query_string = "[[$stgPropertyReminderAt::+]][[$stgPropertyStatus::New||In Progress]][[$stgPropertyTargetDate::≥ $today]]";
269
		$properties_to_display = array( $stgPropertyReminderAt, $stgPropertyAssignedTo, $stgPropertyTargetDate );
270
271
		$results = Query::getQueryResults( $query_string, $properties_to_display, true );
272
		if ( empty( $results ) ) {
273
			return false;
274
		}
275
276
		while ( $row = $results->getNext() ) {
277
			$task_name = $row[0]->getNextObject()->getTitle();
278
			$subject = '[' . $wgSitename . '] ' . wfMessage( 'semantictasks-reminder' )->text() . $task_name;
279
			// The following doesn't work, maybe because we use a cron job.
280
			// $link = $task_name->getFullURL();
281
			// So let's do it manually
282
			//$link = $wiki_url . $task_name->getPartialURL();
283
			// You know what? Let's try it again.
284
			$link = $task_name->getFullURL();
285
286
			$target_date = $row[3]->getNextObject();
287
			$tg_date = new DateTime( $target_date->getShortHTMLText() );
288
289
			while ( $reminder = $row[1]->getNextObject() ) {
290
				$remind_me_in = $reminder->getShortHTMLText();
291
				$date = new DateTime( 'today midnight' );
292
				$date->modify( "+$remind_me_in day" );
293
294
				if ( $tg_date === $date ) {
295
					global $wgLang;
296
					while ( $task_assignee = $row[2]->getNextObject() ) {
297
						$assignee_username = $task_assignee->getTitle()->getText();
298
						$assignee = User::newFromName( $assignee_username );
299
300
						$body = wfMessage( 'semantictasks-reminder-message2', $task_name,
301
							$wgLang->formatNum( $remind_me_in ), $link )->text();
302
						$assignee->sendMail( $subject, $body );
303
					}
304
				}
305
			}
306
		}
307
		return true;
308
	}
309
310
	/**
311
	 * Prints debugging information. $debugText is what you want to print, $debugVal
312
	 * is the level at which you want to print the information.
313
	 *
314
	 * @global boolean $wgSemanticTasksDebug
315
	 * @param string $debugText
316
	 * @param string $debugArr
317
	 * @access private
318
	 */
319
	static function printDebug( $debugText, $debugArr = null ) {
0 ignored issues
show
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
320
		global $wgSemanticTasksDebug;
321
322
		if ( $wgSemanticTasksDebug ) {
323
			if ( isset( $debugArr ) ) {
324
				$text = $debugText . ' ' . implode( '::', $debugArr );
325
				wfDebugLog( 'semantic-tasks', $text, false );
326
			} else {
327
				wfDebugLog( 'semantic-tasks', $debugText, false );
328
			}
329
		}
330
	}
331
332
}
333