0e4a55d7d6bfb803ce2174abf9b43c4a494f80ac
[lhc/web/wiklou.git] / includes / specials / SpecialEmailuser.php
1 <?php
2 /**
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 * http://www.gnu.org/copyleft/gpl.html
18 */
19
20 /**
21 * @file
22 * @ingroup SpecialPage
23 */
24
25 class SpecialEmailUser extends UnlistedSpecialPage {
26 protected $mTarget;
27
28 public function __construct(){
29 parent::__construct( 'Emailuser' );
30 }
31
32 protected function getFormFields(){
33 global $wgUser;
34 return array(
35 'From' => array(
36 'type' => 'info',
37 'raw' => 1,
38 'default' => $wgUser->getSkin()->link(
39 $wgUser->getUserPage(),
40 htmlspecialchars( $wgUser->getName() )
41 ),
42 'label-message' => 'emailfrom',
43 'id' => 'mw-emailuser-sender',
44 ),
45 'To' => array(
46 'type' => 'info',
47 'raw' => 1,
48 'default' => $wgUser->getSkin()->link(
49 $this->mTargetObj->getUserPage(),
50 htmlspecialchars( $this->mTargetObj->getName() )
51 ),
52 'label-message' => 'emailto',
53 'id' => 'mw-emailuser-recipient',
54 ),
55 'Target' => array(
56 'name' => 'wpTarget',
57 'type' => 'hidden',
58 'default' => $this->mTargetObj->getName(),
59 ),
60 'Subject' => array(
61 'type' => 'text',
62 'default' => wfMsgExt( 'defemailsubject', array( 'content', 'parsemag' ) ),
63 'label-message' => 'emailsubject',
64 'maxlength' => 200,
65 'size' => 60,
66 'required' => 1,
67 ),
68 'Text' => array(
69 'type' => 'textarea',
70 'rows' => 20,
71 'cols' => 80,
72 'label-message' => 'emailmessage',
73 'required' => 1,
74 ),
75 'CCMe' => array(
76 'type' => 'check',
77 'label-message' => 'emailccme',
78 'default' => $wgUser->getBoolOption( 'ccmeonemails' ),
79 ),
80 );
81 }
82
83 public function execute( $par ) {
84 global $wgRequest, $wgOut, $wgUser;
85 $this->mTarget = is_null( $par )
86 ? $wgRequest->getVal( 'wpTarget', '' )
87 : $par;
88
89 $ret = self::getTarget( $this->mTarget );
90 if( $ret instanceof User ){
91 $this->mTargetObj = $ret;
92 } else {
93 $wgOut->showErrorPage( "{$ret}title", "{$ret}text" );
94 return false;
95 }
96
97 $error = self::getPermissionsError( $wgUser, $wgRequest->getVal( 'wpEditToken' ) );
98 switch ( $error ) {
99 case null:
100 # Wahey!
101 break;
102 case 'badaccess':
103 $wgOut->permissionRequired( 'sendemail' );
104 return;
105 case 'blockedemailuser':
106 $wgOut->blockedPage();
107 return;
108 case 'actionthrottledtext':
109 $wgOut->rateLimited();
110 return;
111 case 'mailnologin':
112 case 'usermaildisabled':
113 $wgOut->showErrorPage( $error, "{$error}text" );
114 return;
115 default:
116 # It's a hook error
117 list( $title, $msg, $params ) = $error;
118 $wgOut->showErrorPage( $title, $msg, $params );
119 return;
120 }
121
122 $form = new HTMLForm( $this->getFormFields() );
123 $form->addPreText( wfMsgExt( 'emailpagetext', 'parseinline' ) );
124 $form->setSubmitText( wfMsg( 'emailsend' ) );
125 $form->setTitle( $this->getTitle() );
126 $form->setSubmitCallback( array( __CLASS__, 'submit' ) );
127 $form->setWrapperLegend( wfMsgExt( 'email-legend', 'parsemag' ) );
128 $form->loadData();
129
130 if( !wfRunHooks( 'EmailUserForm', array( &$form ) ) ){
131 return false;
132 }
133
134 $wgOut->setPagetitle( wfMsg( 'emailpage' ) );
135 $result = $form->show();
136
137 if( $result === true ){
138 $wgOut->setPagetitle( wfMsg( 'emailsent' ) );
139 $wgOut->addWikiMsg( 'emailsenttext' );
140 $wgOut->returnToMain( false, $this->mTargetObj->getUserPage() );
141 }
142 }
143
144 /**
145 * Validate target User
146 *
147 * @param $target String: target user name
148 * @return User object on success or a string on error
149 */
150 public static function getTarget( $target ) {
151 if ( $target == '' ) {
152 wfDebug( "Target is empty.\n" );
153 return 'notarget';
154 }
155
156 $nu = User::newFromName( $target );
157 if( !$nu instanceof User || !$nu->getId() ) {
158 wfDebug( "Target is invalid user.\n" );
159 return 'notarget';
160 } else if ( !$nu->isEmailConfirmed() ) {
161 wfDebug( "User has no valid email.\n" );
162 return 'noemail';
163 } else if ( !$nu->canReceiveEmail() ) {
164 wfDebug( "User does not allow user emails.\n" );
165 return 'nowikiemail';
166 }
167
168 return $nu;
169 }
170
171 /**
172 * Check whether a user is allowed to send email
173 *
174 * @param $user User object
175 * @param $editToken String: edit token
176 * @return null on success or string on error
177 */
178 public static function getPermissionsError( $user, $editToken ) {
179 global $wgEnableEmail, $wgEnableUserEmail;
180 if( !$wgEnableEmail || !$wgEnableUserEmail ){
181 return 'usermaildisabled';
182 }
183
184 if( !$user->isAllowed( 'sendemail' ) ) {
185 return 'badaccess';
186 }
187
188 if( !$user->isEmailConfirmed() ){
189 return 'mailnologin';
190 }
191
192 if( $user->isBlockedFromEmailuser() ) {
193 wfDebug( "User is blocked from sending e-mail.\n" );
194 return "blockedemailuser";
195 }
196
197 if( $user->pingLimiter( 'emailuser' ) ) {
198 wfDebug( "Ping limiter triggered.\n" );
199 return 'actionthrottledtext';
200 }
201
202 $hookErr = false;
203 wfRunHooks( 'UserCanSendEmail', array( &$user, &$hookErr ) );
204 wfRunHooks( 'EmailUserPermissionsErrors', array( $user, $editToken, &$hookErr ) );
205 if ( $hookErr ) {
206 return $hookErr;
207 }
208
209 return null;
210 }
211
212 /**
213 * Really send a mail. Permissions should have been checked using
214 * getPermissionsError(). It is probably also a good
215 * idea to check the edit token and ping limiter in advance.
216 *
217 * @return Mixed: True on success, String on error
218 */
219 public static function submit( $data ) {
220 global $wgUser, $wgUserEmailUseReplyTo;
221
222 $target = self::getTarget( $data['Target'] );
223 if( !$target instanceof User ){
224 return wfMsgExt( $target . 'text', 'parse' );
225 }
226 $to = new MailAddress( $target );
227 $from = new MailAddress( $wgUser );
228 $subject = $data['Subject'];
229 $text = $data['Text'];
230
231 // Add a standard footer and trim up trailing newlines
232 $text = rtrim( $text ) . "\n\n-- \n";
233 $text .= wfMsgExt(
234 'emailuserfooter',
235 array( 'content', 'parsemag' ),
236 array( $from->name, $to->name )
237 );
238
239 $error = '';
240 if( !wfRunHooks( 'EmailUser', array( &$to, &$from, &$subject, &$text, &$error ) ) ) {
241 return $error;
242 }
243
244 if( $wgUserEmailUseReplyTo ) {
245 // Put the generic wiki autogenerated address in the From:
246 // header and reserve the user for Reply-To.
247 //
248 // This is a bit ugly, but will serve to differentiate
249 // wiki-borne mails from direct mails and protects against
250 // SPF and bounce problems with some mailers (see below).
251 global $wgPasswordSender;
252 $mailFrom = new MailAddress( $wgPasswordSender );
253 $replyTo = $from;
254 } else {
255 // Put the sending user's e-mail address in the From: header.
256 //
257 // This is clean-looking and convenient, but has issues.
258 // One is that it doesn't as clearly differentiate the wiki mail
259 // from "directly" sent mails.
260 //
261 // Another is that some mailers (like sSMTP) will use the From
262 // address as the envelope sender as well. For open sites this
263 // can cause mails to be flunked for SPF violations (since the
264 // wiki server isn't an authorized sender for various users'
265 // domains) as well as creating a privacy issue as bounces
266 // containing the recipient's e-mail address may get sent to
267 // the sending user.
268 $mailFrom = $from;
269 $replyTo = null;
270 }
271
272 $mailResult = UserMailer::send( $to, $mailFrom, $subject, $text, $replyTo );
273
274 if( WikiError::isError( $mailResult ) && false ) {
275 return $mailResult->getMessage();
276 } else {
277 // if the user requested a copy of this mail, do this now,
278 // unless they are emailing themselves, in which case one
279 // copy of the message is sufficient.
280 if ( $data['CCMe'] && $to != $from ) {
281 $cc_subject = wfMsg(
282 'emailccsubject',
283 $target->getName(),
284 $subject
285 );
286 wfRunHooks( 'EmailUserCC', array( &$from, &$from, &$cc_subject, &$text ) );
287 $ccResult = UserMailer::send( $from, $from, $cc_subject, $text );
288 if( WikiError::isError( $ccResult ) ) {
289 // At this stage, the user's CC mail has failed, but their
290 // original mail has succeeded. It's unlikely, but still,
291 // what to do? We can either show them an error, or we can
292 // say everything was fine, or we can say we sort of failed
293 // AND sort of succeeded. Of these options, simply saying
294 // there was an error is probably best.
295 return $ccResult->getMessage();
296 }
297 }
298
299 wfRunHooks( 'EmailUserComplete', array( $to, $from, $subject, $text ) );
300 return true;
301 }
302 }
303 }