Extension:ContactPage/SiteGround and SES

This page details a potentially common issue users may encounter when running this extension with the hosting provider SiteGround (and possibly others), and provides an option for fixing the issue. This guide should also work for other providers that exhibit the same issue (such as Amazon SES).

IssueEdit

The user sending the form gets an error saying Unknown error in PHP's mail() function if Manual:$wgSMTP has not been configured:

 

If Manual:$wgSMTP has been configured, then the user may get an error saying that the From address is not configured in the server.

CauseEdit

As of Feb. 2022, SiteGround doesn't appear to allow sending emails when the From address is not in the list of email addresses of the corresponding SiteGround account. This is because, technically, MediWiki is spoofing email addresses (if settings are not changed): it sends the contactee's email as the From address, even though it's actually from the account's wiki. Of course, it's only going to the account owner's personal email, and the email is labelled as a Contact (no harm, no foul)... but SiteGround, in its wisdom, still bars it.

Notice that other email functionality (such as a password reset or if the contactee doesn't provide an email address) is not affected because the From address in this case is the same as $wgPasswordSender, which would usually be the same email address in the SiteGround account.

A useful way to check if the email address itself is valid to trigger a password reset for any user. If the reset email sends successfully, then this guide should help you with the ContactPage issue. If you can't send a reset email, then you should look into fixing that first (by following the suggestion at Manual:$wgSMTP, for instance). Similarly, if you can't send an email to a user using Special:EmailUser, the problem probably lies elsewhere.

FixEdit

This will involve changing the code of a file in the ContactPage extension, and was taken from here, and by extension, here. In the wiki root directory, go to the file Extensions/ContactPage/includes/SpecialContact.php. Then, locate the below line:

// Used when user hasn't set an email, or when sending CC email to user.

This would usually be in line 286 or so (the exact line will depend on the ContactPage version you have). Then comment the below lines:

	// Used when user hasn't set an email, or when sending CC email to user
		$contactSender = new MailAddress(
			$config['SenderEmail'] ?: $this->getConfig()->get( 'PasswordSender' ),
			$config['SenderName']
		);

		$replyTo = null;

		$fromAddress = $formData['FromAddress'];
		$fromName = $formData['FromName'];
		if ( !$fromAddress ) {
			// No email address entered, so use $contactSender instead
			$senderAddress = $contactSender;
		} else {

			// T232199 - If the email address is invalid, bail out.
			// Don't allow it to fallback to basically @server.host.name
			if ( !Sanitizer::validateEmail( $fromAddress ) ) {
				return [ 'invalidemailaddress' ];
			}

			// Use user submitted details
			$senderAddress = new MailAddress( $fromAddress, $fromName );
			if ( $this->getConfig()->get( 'UserEmailUseReplyTo' ) ) {
				// Define reply-to address
				$replyTo = $senderAddress;
			}
		}

and replace it with

// Used when user hasn't set an email, when $wgUserEmailUseReplyTo is true,
		// or when sending CC email to user
		$siteAddress = new MailAddress(
			$config['SenderEmail'] ?: $this->getConfig()->get( 'PasswordSender' ),
			$config['SenderName']
		);

		// Initialize the sender to the site address
		$senderAddress = $siteAddress;

		$fromAddress = $formData['FromAddress'];
		$fromName = $formData['FromName'];

		$fromUserAddress = null;
		$replyTo = null;

		if ( $fromAddress ) {
			// T232199 - If the email address is invalid, bail out.
			// Don't allow it to fallback to basically @server.host.name
			if ( !Sanitizer::validateEmail( $fromAddress ) ) {
				return [ 'invalidemailaddress' ];
			}

			// Use user submitted details
			$fromUserAddress = new MailAddress( $fromAddress, $fromName );

			if ( $this->getConfig()->get( 'UserEmailUseReplyTo' ) ) {
				// Define reply-to address
				$replyTo = $fromUserAddress;
			} else {
				// Not using ReplyTo, so set the sender to $fromUserAddress
				$senderAddress = $fromUserAddress;
			}
		}

This fixes the main issue. However, if you stop here, the user will get an error (an exception of type "Error") if they choose the option to send a copy of the message to themselves. To fix that, we need to make some changes to another section of the file. Look for the string (in about line 450 or so)

// if the user requested a copy of this mail, do this now

Then comment the below lines:

// if the user requested a copy of this mail, do this now,
		// unless they are emailing themselves, in which case one copy of the message is sufficient.
		if ( $formData['CCme'] && $fromAddress ) {
			$cc_subject = $this->msg( 'emailccsubject', $contactRecipientUser->getName(), $subject )->text();
			if ( Hooks::run( 'ContactForm',
				[ &$senderAddress, &$contactSender, &$cc_subject, &$text, $this->formType, $formData ] )
			) {
				wfDebug( __METHOD__ . ': sending cc mail from ' . $contactSender->toString() .
					' to ' . $senderAddress->toString() . "\n"
				);
				$ccResult = UserMailer::send( $senderAddress, $contactSender, $cc_subject, $text );
				if ( !$ccResult->isOK() ) {
					// At this stage, the user's CC mail has failed, but their
					// original mail has succeeded. It's unlikely, but still, what to do?
					// We can either show them an error, or we can say everything was fine,
					// or we can say we sort of failed AND sort of succeeded. Of these options,
					// simply saying there was an error is probably best.
					return [ $ccResult->getMessage( 'contactpage-usermailererror', false, $language ) ];
				}
			}
		}

and replace with

// if the user requested a copy of this mail, do this now,
		// unless they are emailing themselves, in which case one copy of the message is sufficient.
		if ( $formData['CCme'] && $fromUserAddress ) {
			$cc_subject = $this->msg( 'emailccsubject', $contactRecipientUser->getName(), $subject )->text();
			if ( Hooks::run( 'ContactForm',
				[ &$fromUserAddress, &$senderAddress, &$cc_subject, &$text, $this->formType, $formData ] )
			) {
				wfDebug( __METHOD__ . ': sending cc mail from ' . $senderAddress->toString() .
					' to ' . $fromUserAddress->toString() . "\n"
				);
				$ccResult = UserMailer::send( $fromUserAddress, $senderAddress, $cc_subject, $text );
				if ( !$ccResult->isOK() ) {
					// At this stage, the user's CC mail has failed, but their
					// original mail has succeeded. It's unlikely, but still, what to do?
					// We can either show them an error, or we can say everything was fine,
					// or we can say we sort of failed AND sort of succeeded. Of these options,
					// simply saying there was an error is probably best.
					return [ $ccResult->getMessage( 'contactpage-usermailererror', false, $language ) ];
				}
			}
		}

That's it. Save the file, and the problem should no longer occur.

What the fix does is forcefully set the From address to the email address in the SiteGround account, while setting the "replyto" to the email the user sets when they contact your wiki. This means that you can still reply to the user despite this change, and the email will go back to the user who sent the contact form.