PageRenderTime 48ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 1ms

/includes/specials/SpecialEmailuser.php

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