SpecialEmailuser.php 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. <?php
  2. /**
  3. * @file
  4. * @ingroup SpecialPage
  5. */
  6. /**
  7. * Constructor for Special:Emailuser.
  8. */
  9. function wfSpecialEmailuser( $par ) {
  10. global $wgRequest, $wgUser, $wgOut;
  11. if ( !EmailUserForm::userEmailEnabled() ) {
  12. $wgOut->showErrorPage( 'nosuchspecialpage', 'nospecialpagetext' );
  13. return;
  14. }
  15. $action = $wgRequest->getVal( 'action' );
  16. $target = isset($par) ? $par : $wgRequest->getVal( 'target' );
  17. $targetUser = EmailUserForm::validateEmailTarget( $target );
  18. if ( !( $targetUser instanceof User ) ) {
  19. $wgOut->showErrorPage( $targetUser.'title', $targetUser.'text' );
  20. return;
  21. }
  22. $form = new EmailUserForm( $targetUser,
  23. $wgRequest->getText( 'wpText' ),
  24. $wgRequest->getText( 'wpSubject' ),
  25. $wgRequest->getBool( 'wpCCMe' ) );
  26. if ( $action == 'success' ) {
  27. $form->showSuccess();
  28. return;
  29. }
  30. $error = EmailUserForm::getPermissionsError( $wgUser, $wgRequest->getVal( 'wpEditToken' ) );
  31. if ( $error ) {
  32. switch ( $error ) {
  33. case 'blockedemailuser':
  34. $wgOut->blockedPage();
  35. return;
  36. case 'actionthrottledtext':
  37. $wgOut->rateLimited();
  38. return;
  39. case 'sessionfailure':
  40. $form->showForm();
  41. return;
  42. case 'mailnologin':
  43. $wgOut->showErrorPage( 'mailnologin', 'mailnologintext' );
  44. return;
  45. }
  46. }
  47. if ( "submit" == $action && $wgRequest->wasPosted() ) {
  48. $result = $form->doSubmit();
  49. if ( !is_null( $result ) ) {
  50. $wgOut->addHTML( wfMsg( "usermailererror" ) .
  51. ' ' . htmlspecialchars( $result->getMessage() ) );
  52. } else {
  53. $titleObj = SpecialPage::getTitleFor( "Emailuser" );
  54. $encTarget = wfUrlencode( $form->getTarget()->getName() );
  55. $wgOut->redirect( $titleObj->getFullURL( "target={$encTarget}&action=success" ) );
  56. }
  57. } else {
  58. $form->showForm();
  59. }
  60. }
  61. /**
  62. * Implements the Special:Emailuser web interface, and invokes userMailer for sending the email message.
  63. * @ingroup SpecialPage
  64. */
  65. class EmailUserForm {
  66. var $target;
  67. var $text, $subject;
  68. var $cc_me; // Whether user requested to be sent a separate copy of their email.
  69. /**
  70. * @param User $target
  71. */
  72. function EmailUserForm( $target, $text, $subject, $cc_me ) {
  73. $this->target = $target;
  74. $this->text = $text;
  75. $this->subject = $subject;
  76. $this->cc_me = $cc_me;
  77. }
  78. function showForm() {
  79. global $wgOut, $wgUser;
  80. $skin = $wgUser->getSkin();
  81. $wgOut->setPagetitle( wfMsg( "emailpage" ) );
  82. $wgOut->addWikiMsg( "emailpagetext" );
  83. if ( $this->subject === "" ) {
  84. $this->subject = wfMsgExt( 'defemailsubject', array( 'content', 'parsemag' ) );
  85. }
  86. $titleObj = SpecialPage::getTitleFor( "Emailuser" );
  87. $action = $titleObj->getLocalURL( "target=" .
  88. urlencode( $this->target->getName() ) . "&action=submit" );
  89. $wgOut->addHTML(
  90. Xml::openElement( 'form', array( 'method' => 'post', 'action' => $action, 'id' => 'emailuser' ) ) .
  91. Xml::openElement( 'fieldset' ) .
  92. Xml::element( 'legend', null, wfMsgExt( 'email-legend', 'parsemag' ) ) .
  93. Xml::openElement( 'table', array( 'class' => 'mw-emailuser-table' ) ) .
  94. "<tr>
  95. <td class='mw-label'>" .
  96. Xml::label( wfMsg( 'emailfrom' ), 'emailfrom' ) .
  97. "</td>
  98. <td class='mw-input' id='mw-emailuser-sender'>" .
  99. $skin->link( $wgUser->getUserPage(), htmlspecialchars( $wgUser->getName() ) ) .
  100. "</td>
  101. </tr>
  102. <tr>
  103. <td class='mw-label'>" .
  104. Xml::label( wfMsg( 'emailto' ), 'emailto' ) .
  105. "</td>
  106. <td class='mw-input' id='mw-emailuser-recipient'>" .
  107. $skin->link( $this->target->getUserPage(), htmlspecialchars( $this->target->getName() ) ) .
  108. "</td>
  109. </tr>
  110. <tr>
  111. <td class='mw-label'>" .
  112. Xml::label( wfMsg( 'emailsubject' ), 'wpSubject' ) .
  113. "</td>
  114. <td class='mw-input'>" .
  115. Xml::input( 'wpSubject', 60, $this->subject, array( 'type' => 'text', 'maxlength' => 200 ) ) .
  116. "</td>
  117. </tr>
  118. <tr>
  119. <td class='mw-label'>" .
  120. Xml::label( wfMsg( 'emailmessage' ), 'wpText' ) .
  121. "</td>
  122. <td class='mw-input'>" .
  123. Xml::textarea( 'wpText', $this->text, 80, 20, array( 'id' => 'wpText' ) ) .
  124. "</td>
  125. </tr>
  126. <tr>
  127. <td></td>
  128. <td class='mw-input'>" .
  129. Xml::checkLabel( wfMsg( 'emailccme' ), 'wpCCMe', 'wpCCMe', $wgUser->getBoolOption( 'ccmeonemails' ) ) .
  130. "</td>
  131. </tr>
  132. <tr>
  133. <td></td>
  134. <td class='mw-submit'>" .
  135. Xml::submitButton( wfMsg( 'emailsend' ), array( 'name' => 'wpSend', 'accesskey' => 's' ) ) .
  136. "</td>
  137. </tr>" .
  138. Xml::hidden( 'wpEditToken', $wgUser->editToken() ) .
  139. Xml::closeElement( 'table' ) .
  140. Xml::closeElement( 'fieldset' ) .
  141. Xml::closeElement( 'form' )
  142. );
  143. }
  144. /*
  145. * Really send a mail. Permissions should have been checked using
  146. * EmailUserForm::getPermissionsError. It is probably also a good idea to
  147. * check the edit token and ping limiter in advance.
  148. */
  149. function doSubmit() {
  150. global $wgUser, $wgUserEmailUseReplyTo, $wgSiteName;
  151. $to = new MailAddress( $this->target );
  152. $from = new MailAddress( $wgUser );
  153. $subject = $this->subject;
  154. // Add a standard footer and trim up trailing newlines
  155. $this->text = rtrim($this->text) . "\n\n-- \n" . wfMsgExt( 'emailuserfooter',
  156. array( 'content', 'parsemag' ), array( $from->name, $to->name ) );
  157. if( wfRunHooks( 'EmailUser', array( &$to, &$from, &$subject, &$this->text ) ) ) {
  158. if( $wgUserEmailUseReplyTo ) {
  159. // Put the generic wiki autogenerated address in the From:
  160. // header and reserve the user for Reply-To.
  161. //
  162. // This is a bit ugly, but will serve to differentiate
  163. // wiki-borne mails from direct mails and protects against
  164. // SPF and bounce problems with some mailers (see below).
  165. global $wgPasswordSender;
  166. $mailFrom = new MailAddress( $wgPasswordSender );
  167. $replyTo = $from;
  168. } else {
  169. // Put the sending user's e-mail address in the From: header.
  170. //
  171. // This is clean-looking and convenient, but has issues.
  172. // One is that it doesn't as clearly differentiate the wiki mail
  173. // from "directly" sent mails.
  174. //
  175. // Another is that some mailers (like sSMTP) will use the From
  176. // address as the envelope sender as well. For open sites this
  177. // can cause mails to be flunked for SPF violations (since the
  178. // wiki server isn't an authorized sender for various users'
  179. // domains) as well as creating a privacy issue as bounces
  180. // containing the recipient's e-mail address may get sent to
  181. // the sending user.
  182. $mailFrom = $from;
  183. $replyTo = null;
  184. }
  185. $mailResult = UserMailer::send( $to, $mailFrom, $subject, $this->text, $replyTo );
  186. if( WikiError::isError( $mailResult ) ) {
  187. return $mailResult;
  188. } else {
  189. // if the user requested a copy of this mail, do this now,
  190. // unless they are emailing themselves, in which case one copy of the message is sufficient.
  191. if ($this->cc_me && $to != $from) {
  192. $cc_subject = wfMsg('emailccsubject', $this->target->getName(), $subject);
  193. if( wfRunHooks( 'EmailUser', array( &$from, &$from, &$cc_subject, &$this->text ) ) ) {
  194. $ccResult = UserMailer::send( $from, $from, $cc_subject, $this->text );
  195. if( WikiError::isError( $ccResult ) ) {
  196. // At this stage, the user's CC mail has failed, but their
  197. // original mail has succeeded. It's unlikely, but still, what to do?
  198. // We can either show them an error, or we can say everything was fine,
  199. // or we can say we sort of failed AND sort of succeeded. Of these options,
  200. // simply saying there was an error is probably best.
  201. return $ccResult;
  202. }
  203. }
  204. }
  205. wfRunHooks( 'EmailUserComplete', array( $to, $from, $subject, $this->text ) );
  206. return;
  207. }
  208. }
  209. }
  210. function showSuccess( &$user = null ) {
  211. global $wgOut;
  212. if ( is_null($user) )
  213. $user = $this->target;
  214. $wgOut->setPagetitle( wfMsg( "emailsent" ) );
  215. $wgOut->addWikiMsg( 'emailsenttext' );
  216. $wgOut->returnToMain( false, $user->getUserPage() );
  217. }
  218. function getTarget() {
  219. return $this->target;
  220. }
  221. static function userEmailEnabled() {
  222. global $wgEnableEmail, $wgEnableUserEmail;
  223. return $wgEnableEmail && $wgEnableUserEmail;
  224. }
  225. static function validateEmailTarget ( $target ) {
  226. if ( "" == $target ) {
  227. wfDebug( "Target is empty.\n" );
  228. return "notarget";
  229. }
  230. $nt = Title::newFromURL( $target );
  231. if ( is_null( $nt ) ) {
  232. wfDebug( "Target is invalid title.\n" );
  233. return "notarget";
  234. }
  235. $nu = User::newFromName( $nt->getText() );
  236. if( is_null( $nu ) || !$nu->getId() ) {
  237. wfDebug( "Target is invalid user.\n" );
  238. return "notarget";
  239. } else if ( !$nu->isEmailConfirmed() ) {
  240. wfDebug( "User has no valid email.\n" );
  241. return "noemail";
  242. } else if ( !$nu->canReceiveEmail() ) {
  243. wfDebug( "User does not allow user emails.\n" );
  244. return "nowikiemail";
  245. }
  246. return $nu;
  247. }
  248. static function getPermissionsError ( $user, $editToken ) {
  249. if( !$user->canSendEmail() ) {
  250. wfDebug( "User can't send.\n" );
  251. return "mailnologin";
  252. }
  253. if( $user->isBlockedFromEmailuser() ) {
  254. wfDebug( "User is blocked from sending e-mail.\n" );
  255. return "blockedemailuser";
  256. }
  257. if( $user->pingLimiter( 'emailuser' ) ) {
  258. wfDebug( "Ping limiter triggered.\n" );
  259. return 'actionthrottledtext';
  260. }
  261. if( !$user->matchEditToken( $editToken ) ) {
  262. wfDebug( "Matching edit token failed.\n" );
  263. return 'sessionfailure';
  264. }
  265. return;
  266. }
  267. static function newFromURL( $target, $text, $subject, $cc_me )
  268. {
  269. $nt = Title::newFromURL( $target );
  270. $nu = User::newFromName( $nt->getText() );
  271. return new EmailUserForm( $nu, $text, $subject, $cc_me );
  272. }
  273. }