La fonction mail() et un FAI qui restreint le port 25

Une solution bien peu élégante... mais fonctionnelle

Ceci est une ARCHIVE, peut-être périmée. Vérifiez bien les compatibilités !

Attention, cette contribution est EN CHANTIER : elle n’est peut-être pas fonctionnelle.

Permet l’envoi de messages mails SPIP sans Sendmail depuis un serveur personnel

Je voulais profiter des fonctions d’interactivité de SPIP depuis bien longtemps. Cependant, hébergeant mon site sur un serveur à la maison, j’étais restreint par un FAI qui bloquait l’accès au port 25, rendant impossible l’envoi de messages avec Sendmail. La solution ici proposée (imparfaite, certes) permettra de contourner ce problème avec une combinaison de Python et PHP.

Nota Spip-contrib

Il ne s’agit pas ici d’une contrib finalisée, prête à installer, et à mettre entre toutes les mains, bien au contraire. Mais cet article « chantier » est quand même publié comme pouvant intéresser certain(e)s. Il n’est bien sûr à utiliser que si vous savez ce que vous faites, vous aurez été prévenu. N’hésitez pas à utiliser le forum de l’article pour faire retour, ou débattre avec l’auteur.

Quelques réactions obtenues via le forum de rédaction de cet article :
-  Je ne peux pas tester mais je conseillerais de remplacer ./ecrire/ par _DIR_RESTREINT histoire que ça marche aussi bien depuis l’espace privé que public.
-  en 1.9.1 « envoyer_mail » est devenue surchargeable sans toucher au core. Et il y a un « plugin _test_/mail_smtp » (cf.Le plugin Mail SMTP qui permet d’utiliser un serveur smtp distant pour les fonctions mail de spip au lieu du serveur local.

Les ingrédients


-  Un serveur LAMP
-  Possibilité d’appels système avec php
-  Un compte Google Mail
-  Python
-  libgmail (0.1.5.1 au moment de l’écriture de cet article)
-  libgmail-docs
-  spip 1.9

L’approche

Bien que d’autres solutions (voir le paragraphe à ce sujet) puissent aussi être mises en place pour ce même problème, voici l’approche adoptée. D’abord, il faudrait repérérer le fichier ecrire/inc/mail.php qui sera modifié. Ensuite, un script en python sera appelé depuis php, se connectera sur le serveur de votre compte de messagerie Google avec les fonctions de la librairie libgmail, et envoyera le message. C’est tout, dites-vous ? Il n’y pas plus à faire !

Implantation

Avant toute modification sur les fichiers de base de Spip, il est sage d’en faire copie : renommez-les par exemple nom.php.orig. Ouvrez-donc le fichier mail.php et ajoutez cette fonction au début :

function gmail_envoi($email, $sujet, $texte){
$sujet = escapeshellarg($sujet);
$handle = popen("./ecrire/inc/./wrappersendmsg.py $email $sujet", 'w');
if (fwrite($handle, $texte) === FALSE) {
       echo "Erreur sur $handle";
       exit;
   }
pclose($handle);
return true;
}

Comme mentionné précédemment, PHP ne fera qu’appeler le script Python qui se chargera de l’envoi. En effet, le projet libgmail n’existe que pour python : nous n’avons donc d’autre alternative que celle-là. Aussi, avez-vous constaté que le programme est invoqué par un appel système. Ma décision sur ce point s’est faite en raison de la difficulté à communiquer avec python, lorsqu’exécuté comme CGI par Apache, depuis PHP sans craindre d’abus d’exécution du script.

La fonction gmail_envoi ouvre un « pipe » sur le programme et passe le texte du message en STDIN, l’adresse du destinataire en argument, et le sujet aussi. Si vous décidez de placer le script ailleurs dans l’arborescence, prenez garde à donner le bon chemin à la fonction popen() : l’exécution de la page prendra un certain temps si le script est bel et bien appelé.

Toujours dans le fichier ecrire/inc/mail.php, repérez la fonction envoyer_mail et la ligne :

return @mail($email, $sujet, $texte, $headers);</code> 
dans le contexte de:
<code>
switch($hebergeur) {
	case 'lycos':
		$queue_mails[] = array(
			'email' => $email,
			'sujet' => $sujet,
			'texte' => $texte,
			'headers' => $headers);
		return true;
	case 'free':
		return false;
	case 'online':
		return @email('webmaster', $email, $sujet, $texte);
	default:
		return @mail($email, $sujet, $texte, $headers);
	}

et replacez-la par :

return gmail_envoi($email, $sujet, $texte);

Considérant que libgmail a été installé correctement suivant les instructions données dans le README et dans la documentation, vous pouvez maintenant essayer le programme suivant directement inspiré de l’exemple donné dans libgmail-doc.

#!/usr/bin/env python
#
# sendmsg.py -- Demo to send a message via Gmail using libgmail
#
# $Revision: 1.4 $ ($Date: 2005/09/18 18:41:48 $)
#
# Author: follower@myrealbox.com
#
# License: GPL 2.0
#
import os
import sys
import logging

# Allow us to run using installed <span class="base64" title="PGNvZGUgY2xhc3M9InNwaXBfY29kZSBzcGlwX2NvZGVfaW5saW5lIiBkaXI9Imx0ciI+bGliZ21haWw8L2NvZGU+"></span> or the one in parent directory.
try:
    import libgmail
    ## Wouldn't this the preffered way?
    ## We shouldn't raise a warning about a normal import
    ##logging.warn("Note: Using currently installed <span class="base64" title="PGNvZGUgY2xhc3M9InNwaXBfY29kZSBzcGlwX2NvZGVfaW5saW5lIiBkaXI9Imx0ciI+bGliZ21haWw8L2NvZGU+"></span> version.")
except ImportError:
    # Urghhh...
    sys.path.insert(1,
                    os.path.realpath(os.path.join(os.path.dirname(__file__),
                                                  os.path.pardir)))

    import libgmail


if __name__ == "__main__":
    import sys
    from getpass import getpass

    try:
        to = sys.argv[1]
        subject = sys.argv[2]
        msg = sys.stdin.read()
    except IndexError:
        print "Usage: %s <to address> <subject>" % sys.argv[0]
        raise SystemExit


    ga = libgmail.GmailAccount("toto@gmail.com", "hackme")

    print "\nPlease wait, logging in..."

    try:
        ga.login()
    except libgmail.GmailLoginFailure:
        print "\nLogin failed. (Wrong username/password?)"
    else:
        print "Log in successful.\n"
        gmsg = libgmail.GmailComposedMessage(to, subject, msg)

        if ga.sendMessage(gmsg):
            print "Message sent <span class="base64" title="PGNvZGUgY2xhc3M9InNwaXBfY29kZSBzcGlwX2NvZGVfaW5saW5lIiBkaXI9Imx0ciI+JXM8L2NvZGU+"></span> successfully." % subject
        else:
            print "Could not send message."

        print "Done."

Le code ci-haut est presque identique à l’exemple sendmsg donné dans la documentation : il n’a été modifié que pour intégrer la fonction sys.stdin.read() et autres petits détails... N’oubliez pas finalement d’adapter toto@gmail.com et hackme à votre adresse et mot de passe de votre compte (à moins que ça ne soit ça !).

Voilà, à présent le tout devrait être fonctionnel. En cas de problèmes, vérifiez :

-  que vous pouvez exécuter un simple appel système depuis php tel que echo exec('whoami');,
-  que le chemin vers le script le est bon,
-  que le nom du script est le bon aussi,
-  que vous pouvez envoyer un message en ligne de commande $echo "test" | ./wrappersendmsg.py toto@foo.com essai.

Autres possibilités

  1. Passez chez un hébergeur professionnel (ce qui n’est pas souhaitable dans le cas présent !)
  2. Utiliser la directive smart_host dans la configuration de Sendmail (que je n’ai pas réussi à utiliser)
  3. Communiquer avec le script python par l’entremise d’un fichier (peu élégant)
  4. Communiquer avec le script par Mysql (trop compliqué pour ce cas)
  5. Coder un wrapper [1] pour le programme en python de sorte qu’il soit exécuté exactement comme la commande sendmail par PHP. Adapter ensuite php.ini et la variable sendmail_path. Notez que ce wrapper devra minimalement accepter les options -t et -i

Si tout s’est déroulé correctement en suivant cet article, vous n’avez maintenant plus de raisons de ne pas vous mettre à l’essai de toutes les fonctionnalités de SPIP.

Le bonheur est dans l’administration de son serveur !

Voir en ligne :

http://libgmail.sourceforge.net/

Notes

[1Voir sur
Wikipédia

Discussion

Une discussion

  • je suis chez free et ma freebox bloque le port 25. Je n’ai pas le droit d’autoriser l’ouverture de ce port car je ne suis pas le propriétaire de cet accès internet et ce dernier ne souhaite pas que je « bidouille » sa freebox.

    Bref, j’ai contourné le problème en utilisant sendmail et le smtp de free. Certe ce smtp est parfois capricieux et il est lent, mais au moins j’ai pu configurer le tout en 1 minute, c’est fonctionnel, et, étant derrière une ip de free, le smtp permet d’envoyer avec n’importe quel adresse d’envoi !

    Grace au logs de sendmail je sais quand le mail n’est pas relayé donc je peux l’envoyer à la main.

    La lenteur a pour symptôme le fait qu’a chaque envoi de mail (pour un inscription par exemple) la page met pas mal de temps avant d’indiquer « mail envoyé »...

    ça reste bien pratique.

    Répondre à ce message

Ajouter un commentaire

Avant de faire part d’un problème sur un plugin X, merci de lire ce qui suit :

  • Désactiver tous les plugins que vous ne voulez pas tester afin de vous assurer que le bug vient bien du plugin X. Cela vous évitera d’écrire sur le forum d’une contribution qui n’est finalement pas en cause.
  • Cherchez et notez les numéros de version de tout ce qui est en place au moment du test :
    • version de SPIP, en bas de la partie privée
    • version du plugin testé et des éventuels plugins nécessités
    • version de PHP (exec=info en partie privée)
    • version de MySQL / SQLite
  • Si votre problème concerne la partie publique de votre site, donnez une URL où le bug est visible, pour que les gens puissent voir par eux-mêmes.
  • En cas de page blanche, merci d’activer l’affichage des erreurs, et d’indiquer ensuite l’erreur qui apparaît.

Merci d’avance pour les personnes qui vous aideront !

Par ailleurs, n’oubliez pas que les contributeurs et contributrices ont une vie en dehors de SPIP.

Qui êtes-vous ?
[Se connecter]

Pour afficher votre trombine avec votre message, enregistrez-la d’abord sur gravatar.com (gratuit et indolore) et n’oubliez pas d’indiquer votre adresse e-mail ici.

Ajoutez votre commentaire ici

Ce champ accepte les raccourcis SPIP {{gras}} {italique} -*liste [texte->url] <quote> <code> et le code HTML <q> <del> <ins>. Pour créer des paragraphes, laissez simplement des lignes vides.

Ajouter un document

Suivre les commentaires : RSS 2.0 | Atom