PHP|Débutant :: Forums

Advertisement

Besoin d'aide ? N'hésitez pas, mais respectez les règles

Vous n'êtes pas identifié(e).

#1 24-05-2010 10:04:35

Sq78
Membre
Inscription : 13-05-2009
Messages : 47

Problème pour l'envoi de pièces jointes par mail

Bonjour,
J'ai un programme qui permet d'envoyer un mail à de multiples correspondants en ajoutant une pièce jointe.
Mon souci est que la pièce jointe ne fonctionne que si je vais la chercher dans le même répertoire que le fichier PHP principal.

Je m'explique.
J'ai un répertoire /fichiers avec un sous répertoire /images
Dans /fichiers j'ai
mail.php
toto.jpg

Dans /fichiers/images j'ai
toto2.jpg

Quand j'envoie le mail avec toto.jpg, je le reçois bien dans ma boite mail
Quand je veux envoyer toto2.jpg, j'ai ce message:
Warning: fopen(toto2.jpg) [function.fopen]: failed to open stream: No such file or directory in
En fait il ne cherche les pièces jointes que dans le répertoire courant.
J'ai du rater quelque chose mais je ne sais pas où...

Voici mon code
Fichier formulaire.php

<fieldset>
<legend> Message aux adhérents </legend>
        <p><label for="pseudo">Nom ou pseudo:</label><input type="text" name="pseudo" value="<?php echo stripslashes($pseudo); ?>" size="40" /></p>
  <p><label for="email">Adresse Email:</label><input type="text" name="email" value="<?php echo stripslashes($email); ?>" size="40" /></p>
  <p><label for="objet">Objet du message:</label><input type="text" name="objet" size="40" value="<?php echo stripslashes($objet); ?>" maxlength="200" /></p>
  <p><label for="fichier">Joindre un fichier:</label><input type="file" name="fichier" size="35"><input type="hidden" name="MAX_FILE_SIZE" value="' . $max_file_size . '"></p>
  <p><label for="message">Message:</label><textarea name="message" cols="100" rows="5"><?php echo nl2br (stripslashes($message)); ?></textarea></p>
 <div style="margin-left: 155px;">
 <input type="button" value="Retour" onClick="location.href='index.php'">
 <input type="submit" name="Ecrire" value="Envoyer" onClick="return verifier_mail()" />
 </div>
</fieldset>

et le fichier mail.php (dans le même répertoire que formulaire.php)

<?php
$date = date("d-m-Y");
$heure = date("H:i");
$pseudo=ucwords($_POST['pseudo']);          // valeur pseudo emetteur
$emetteur=$_POST['email'];            // valeur email emetteur
$objet=stripslashes(ucfirst($_POST['objet']));            // valeur objet du message
$message=nl2br(stripslashes(ucfirst($_POST['message'])));        // valeur du message lui meme
$regex = '[-a-zA-Z0-9!#$%&\'*+\\/=?^_`{|}~]';
$regex_mail = "/^[-+.\w]{1,64}@[-.\w]{1,64}\.[-.\w]{2,6}$/i";

if (!ereg($regex,$pseudo)){
        die('Pseudo non conforme');
        }
elseif (!ereg($regex,$objet)){
     die('Objet non conforme');
        }
elseif (!ereg($regex,$message)){
        die('Message non conforme');
        }
elseif (!preg_match($regex_mail, $emetteur)){
        die('Email non conforme');
        }
else {
require_once('config/config.inc.php');
   foreach ($_POST['dest'] as $id_user) {
       $requete = "SELECT Adresse_mail AS email FROM adherents WHERE ID='$id_user'";
       $result=mysql_query($requete) or die('Erreur SQL ! Adherents '.mysql_error());
       while ($ecri_ad = mysql_fetch_object($result)) {
 
//=====Création de la boundary
$boundary = md5(uniqid(microtime(), TRUE));
//==========

//=====Création du header de l'e-mail
$headers = 'From: ASMB Volley-ball <'.$emetteur.'>'."\r\n";
$headers .= 'Mime-Version: 1.0'."\r\n";
$headers .= 'Content-Type: multipart/mixed;boundary='.$boundary."\r\n";
$headers .= "\r\n";
//==========

//=====Ajout du message au format texte
$message = 'Texte affiché par des clients mail ne supportant pas le type MIME.'."\r\n\r\n";
//==========

//=====Ajout du message au format HTML
$message .= '--'.$boundary."\r\n";
$message .= 'Content-type: text/html; charset=iso-8859-1'."\r\n\r\n";
//==========
 
//=====Ajout de la pièce jointe
$file_name = $_FILES['fichier']['name'];
if (!empty($file_name))
{
  $file_type = $_FILES['fichier']['type'];
  $file_size = $_FILES['fichier']['size'];
  $handle = fopen($file_name, 'r') or die ('Fichier '.$file_name.' ne peut pas être ouvert');
  $content = fread($handle, $file_size);
  $content = chunk_split(base64_encode($content));
  fclose($handle);

  $message .= '--'.$boundary."\r\n";
  $message .= 'Content-type:'.$file_type.';name='.$file_name."\r\n";
  $message .= 'Content-transfer-encoding:base64'."\r\n\r\n";
  $message .= $content."\r\n";
}
$message .= '--'.$boundary."\r\n";

if (mail($ecri_ad->email, $objet, $message ,$headers)) {
                        $msg = 'Le '.$date.' à '.$heure.' par '.$emetteur.' -> Envoi réussi du message à '.$ecri_ad->email.'';
                        }else{
                        $msg = 'Le '.$date.' à '.$heure.' par '.$emetteur.' -> Message non envoyé à '.$ecri_ad->email.'';
                        }
                $etat = "mail.txt";
                $inF = fopen($etat,"a");
                fputs($inF,$msg."\n");
                fclose($inF);
                }
        }
}
?>

De plus, j'ai une question qui va paraitre stupide je pense, mais tant pis, je demande.
Quand j'envoie une même pièce jointe à 25 personnes, cette pièce jointe est envoyée 1 seule fois sur le serveur ? parce qu'en regardant mon programme, je me dis que le traitement de la pièce jointe est dans le foreach donc traité à chaque fois ?
Qui pourrait m'éclairer là dessus ?

Merci d'avance
Cdt

Hors ligne

#2 24-05-2010 14:07:03

xTG
GrandGourou
Inscription : 18-06-2009
Messages : 1 127
Site Web

Re : Problème pour l'envoi de pièces jointes par mail

Pour ce que j'ai compris tu upload un fichier que tu veux joindre, cependant ton fichier est encore dans un répertoire temporaire du serveur.
Or je ne pense pas que les fonction fopen et autre puissent aller voir ce qui se trament dans les dossiers temporaires.
Termines l'upload proprement, inclus le fichier puis supprimes le du serveur. ^_-

Hors ligne

#3 24-05-2010 14:13:03

Jc
Membre
Lieu : Zillisheim - Alsace
Inscription : 15-04-2010
Messages : 1 629
Site Web

Re : Problème pour l'envoi de pièces jointes par mail

Bonjour,

Que ton problème est délicat^^. En effet ton code utilise le dépôt de fichiers PHP, et de fait il te faut un contrôle d'erreur et de vérification solide avec une version PHP continuellement à jour pour éviter les failles au niveau sécurité sur tes uploads, même si dans ton cas, à ce qu'il semble être, l'upload est de type local.

Ceci étant dit, avec ce système il faut savoir que tous les fichiers déposés sont stockés temporairement sur ton serveur à l'endroit stipulé par la direcive upload_tmp_dir de ton fichier php.ini et récupérable via $_FILES['fichier']['tmp_name'], vu que l'élément du formulaire correspondant a été appelé 'fichier'.
Il faudra donc dans un premier temps vérifier qu'il soit bien défini et qu'il se situe idéalement en dehors de ton arborescence. De plus les entrées de $_FILES sont stockées avec le nom de la balise <file> de ton formulaire HTML.

Ensuite, même si il s'agit d'un formulaire qu'un utilisateur peut utiliser (upload d'un fichier externe sur le serveur) ou utilisé par le serveur vers un utilisateur (upload du fichier source sur le serveur vers le dossier temporaire de dépôt sur le serveur), il reste important, pour protéger le fichier source dans le 2e cas de travailler comme si on était dans le premier cas, c'est à dire d'envoyer le fichier du dossier temporaire et non le fichier à partir du dossier source initial.

Je te conseille donc de placer un code comme suit (faudra l'adapter à ton cas^^ wink)


$fichier="/depots/".$_FILES['fichier']['name'];
...
if (is_uploaded_file($_FILES['fichier']['name'])){
   if (!move_uploaded_file($_FILES['fichier']['tmp_name'],$fichier)){
      echo "Problème: impossible de déplacer le fichier"; exit;
   } else {
      echo "Problème : Attaque possible par le fichier ".$_FILES['fichier']['name']; exit;
  }
}
// Ensuite pour le traitement du contenu de ton fichier
$contenu = file_get_contents($fichier);
$contenu=strip_tags($contenu); // pour nettoyer le contenu du fichier des éventuelles balises HTML ou PHP
file_put_contents($contenu); // et on le réecrit.
fclose($fp);
 

Il ne faut pas oublier aussi au niveau erreur de vérifier le type MIME même si on ne transfère que des fichiers texte (auquel cas on vérifiera que $_FILES['fichier']['type] contient bien text/plain). Il ne s'agit pas d'une erreur de sécurité car le type MIME est déduit par le navigateur de l'utilisateur en utilisant l'extension de fichier, puis est passé au serveur. S'il y avait un intérêt à passer un faux type, rien n'empêche un utilisateur malveillant de le faire.

Cordialement,

EDIT : A partir de ce que j'ai dit tu as aussi la réponse à ta dernière question : Oui le fichier est uploadé une seule fois sur le serveur, il faut donc en tenir compte dans ton code^^. N'oublie pas non plus qu'un dossier temporaire par définition est supprimé automatiquement à la fin du traitement. Le code que je t'ai proposé résouds aussi ce détail.

Dernière modification par Jc (24-05-2010 14:21:50)


POO PHP+Ajax en MVC avec PDO et Bases de données épaisses  : What else?

Hors ligne

#4 24-05-2010 22:07:26

Sq78
Membre
Inscription : 13-05-2009
Messages : 47

Re : Problème pour l'envoi de pièces jointes par mail

Merci pour ta réponse.
En fait, je serais le seul à utiliser ce programme en tant que responsable du site. C'est pour envoyer des fichiers aux adhérents du site, mais en aucun cas, un adhérent peut lui même en envoyer.
Donc tu dis que j'utilise le dépôt de fichier PHP.
Mais y a t'il une solution plus simple à mettre en oeuvre ?
J'avoue que je nage un peu...

Hors ligne

#5 25-05-2010 08:09:02

Jc
Membre
Lieu : Zillisheim - Alsace
Inscription : 15-04-2010
Messages : 1 629
Site Web

Re : Problème pour l'envoi de pièces jointes par mail

Bonjour,

Que tu soies le seul à l'utiliser ou non, ce système procure plein d'avantages, faut juste rester vigilant dans tes traitements php associés et je t'ai donné des clefs justement pour ne rien oublier. Bien que visiblement tu nages un peu, et comme tu es le seul à l'utiliser, je serais toi je garderais tout de même ce système.
Dit moi juste ce qui te pose problème, on essayera d'y pourvoir.


POO PHP+Ajax en MVC avec PDO et Bases de données épaisses  : What else?

Hors ligne

#6 25-05-2010 20:52:31

Jc
Membre
Lieu : Zillisheim - Alsace
Inscription : 15-04-2010
Messages : 1 629
Site Web

Re : Problème pour l'envoi de pièces jointes par mail

Re,

Après réflexion je crois savoir ce qui te perturbe avec ce que je t'ai dit. En effet l'utilisation que tu souhaites en faire (ton code) va un peu à l'encontre du principe du dépôt de fichier.
Pour que t'y voies un peu plus clair je vais commencer au début.

Avec le dépôt de fichier, au lieu que les fichiers aillent du serveur vers le navigateur en utilisant le protocole HTTP, ils vont dans le sens opposé c'est-à-dire du navigateur vers le serveur. Habituellement, cette opération est implémentée avec une interface de formulaire HTML.

Prenons le cas d'une balise form typique de dépôt de fichier


<form  action="depot.php" method="post" enctype="multipart/form-data" />
 

Ce qu'il faut savoir :
1) Le dépôt de fichier ne peut se faire qu'avec la méthode post (dans certains cas put est possible)
2) Une fois la fin du script depopt.php atteint, le fichier sera supprimé automatiquement du dossier temporaire sur le serveur. Si un traitement de sauvegarde du fichier posté n'est pas inclus dans depot.php, il sera perdu.

Pourquoi j'ai mis dans mon exemple de code

jc a écrit :

echo "Problème : Attaque possible par le fichier ".$_FILES['fichier']['name']; exit;

Le contexte de code de cette ligne cherche à savoir si le fichier déposé est bien issu d'un transfert et s'il ne s'agit pas d'un fichier local. Pourquoi? Car un des dangers du dépôt de fichier est qu'une personne mal intentionnée amène un script de dépôt de fichier à traiter un fichier local comme s'il s'agissait d'un fichier transféré. En effet si le fichier local choisi est par exemple /etc/passwd ou le fichier contenant les mots de passe pour les accès à la base de données, la personne aurait accès à toutes les données sensibles de votre site normalement interdites à l'accès.

Conseils les plus importants pour utiliser le dépôt de fichier :
- Faire en sorte que tous les utilisateurs qui ont accès à cette fonction soient des personnes authentifiées à un moment ou un autre et qu'elle soient autorisées.
- Limiter les dépôts de fichier aux adm du site et aux responsables de contenu (par ex)
- Si tous les utilisateurs ont accès au dépôt de fichier penser à utiliser la fonction basename() pour modifier les noms des fichiers qui arrivent sur le serveur. Cette fonction supprime le chemin faisant partie du nom de fichier qui lui est passé en paramètre, qui est une attaque classique utilisée pour déposer un fichier dans un répertoire différent de celui par défaut.

______________

En conséquence, au vu de ce que je viens de dire, il est vrai qu'il est dangereux voire inadapté d'utiliser des fichiers locaux en dépôt pour les envoyer en pièce jointe par mail. Je pense que la fonction a été créee à la base pour avoir une interface d'envoi de mail avec pièce jointe à distance personalisée pour l'adm du site sans avoir les contraintes de taille de pièce jointe que sa boite aux lettres perso lui imposait, le tout sans avoir à s'occuper de gérer l'upload,le stockage et la suppression de fichier au niveau du code sur le serveur, puisque la pièce jointe est automatiquement supprimée après l'envoi du mail.


POO PHP+Ajax en MVC avec PDO et Bases de données épaisses  : What else?

Hors ligne

#7 27-05-2010 21:46:00

Sq78
Membre
Inscription : 13-05-2009
Messages : 47

Re : Problème pour l'envoi de pièces jointes par mail

J'ai lu avec attention ce que tu me dis et je comprends le principe, y compris pour la sécurité.
Entre temps, j'ai continué à chercher sur le web afin de trouver quelques pistes supplémentaires et j'ai réussi ce script.

<?php
$rep = "upload/";
$taillemax = 1024000;
$pseudo=ucwords($_POST['pseudo']);          // valeur pseudo emetteur
$emetteur=$_POST['email'];            // valeur email emetteur
$objet=stripslashes(ucfirst($_POST['objet']));            // valeur objet du message
$message=nl2br(stripslashes($_POST['message']));        // valeur du message lui meme
$regex = '[-a-zA-Z0-9!#$%&\'*+\\/=?^_`{|}~]';
$regex_mail = "/^[-+.\w]{1,64}@[-.\w]{1,64}\.[-.\w]{2,6}$/i";

        if (!ereg($regex,$pseudo)){
                die('Pseudo non conforme');
                }
        elseif (!ereg($regex,$objet)){
        die('Objet non conforme');
                }
        elseif (!ereg($regex,$message)){
                die('Message non conforme');
                }
        elseif (!preg_match($regex_mail, $emetteur)){
                die('Email non conforme');
                }

$piecejointe = "";
if(!$erreur && strlen($_FILES['fichier']['name'])) {
        $fichier = $_FILES['fichier'];
        $name = $fichier['name'];
        $temp = $fichier['tmp_name'];
        $size = $fichier['size'];
        # séquence de caractères
  $destination = $rep.$name;
  # upload du fichier sur le serveur
  if($size > $taillemax)
    $erreur = "Taille du fichier $name > ".(int)($taillemax/1024)." Ko";
  elseif(!@is_uploaded_file($temp))
    $erreur = "Téléchargement du fichier $name impossible";
  elseif(!@move_uploaded_file($temp, $destination))
    $erreur = "Problème de transfert du fichier $name";
  else {

//===== On vérifie maintenant le type de fichier
  if(!function_exists('mime_content_type')) {
    function mime_content_type($fichier) {
    # ajouter autant de combinaisons que souhaitées
    $mime = array(
                '.gif'  => 'image/gif',
                '.jpg'  => 'image/jpeg',
                '.jpeg' => 'image/jpeg',
                '.jpe'  => 'image/jpeg',
                '.bmp'  => 'image/bmp',
                '.png'  => 'image/png',
                '.tif'  => 'image/tiff',
                '.tiff' => 'image/tiff',
                '.swf'  => 'application/x-shockwave-flash',
                '.doc'  => 'application/msword',
                '.xls'  => 'application/vnd.ms-excel',
                '.ppt'  => 'application/vnd.ms-powerpoint',
                '.pdf'  => 'application/pdf',
                '.ps'   => 'application/postscript',
                '.eps'  => 'application/postscript',
                '.rtf'  => 'application/rtf',
                '.zip'  => 'application/zip',
                '.rar'  => 'application/rar',
                '.html' => 'text/html',
                '.htm'  => 'text/html',
                '.txt'  => 'text/plain',
    );
  # par défaut
  if(!$type = $mime[strrchr($fichier,'.')]) $type = "application/octet-stream";
  return $type;
  }
}
$mimetype = mime_content_type($destination);
# lecture et conversion du fichier
if($openf = @fopen($destination, "rb")) {
$fichier = fread($openf, filesize($destination));
@fclose($openf);
# encodage norme RFC 2045
$piecejointe = chunk_split(base64_encode($fichier));
  } else {
    $erreur = "Problème de lecture du fichier $name";
  }
}
}
//===== Ajout du message
if(!$erreur) {
  $message_final = "";
  $message_final .= strip_tags($message);        // valeur du message lui meme
  # en-têtes
  $headers = 'From: ASMB Volley-ball <'.$emetteur.'>'."\r\n";
  # si pièce jointe on ajoute l'en-tête spécifique avec séparateurs
  if(strlen($piecejointe)) {
          $boundary = "/-------".md5(uniqid(rand()))."-------/"; // séparateur
    $headers .= "\nMIME-Version: 1.0\nContent-Type: multipart/mixed; boundary=\"$boundary\"\n";
    $message_final =
      "This is a multi-part message in MIME format.\n--$boundary\n".
      "Content-Type: text/plain; charset=ISO-8859-1\n".
      "Content-Transfer-Encoding: 7bit\n\n".
      "$message_final\n\n--$boundary\n".
      "Content-Type: $mimetype; name=\"$name\"\n".
      "Content-Transfer-Encoding: base64\n".
      "Content-Disposition: attachment; filename=\"$name\"\n\n".
      "$piecejointe\n--".
      $boundary."--\n";
  }
    $message_final =stripslashes($message_final);

//===== On récupère les adresses mail dans la BDD
        require_once('config/config.inc.php');
        foreach ($_POST['dest'] as $id_user) {
        $requete = "SELECT Adresse_mail AS email FROM adherents WHERE ID='$id_user'";
        $result=mysql_query($requete) or die('Erreur SQL ! Adherents '.mysql_error());
        while ($destinataire = mysql_fetch_object($result)) {
    if (mail($destinataire->email, $objet, $message_final, $headers)) {
                        $msg = 'Le '.date("d-m-Y").' à '.date("H:i").'. Expéditeur: '.$emetteur.' -> Envoi réussi à '.$destinataire->email.'. Pièce jointe: '.$name.'';
                        }else{
                        $msg = '*** ECHEC Le '.date("d-m-Y").' à '.date("H:i").'. Expéditeur: '.$emetteur.' -> Envoi vers '.$destinataire->email.'. Pièce jointe: '.$name.'';
                }
                $etat = "mail.txt";
                $inF = fopen($etat,"a");
                fputs($inF,$msg."\n");
                fclose($inF);

//      @unlink($destination);                                  // suppression de la pièce jointe ou non du repertoire d'upload
            }
                }
       }
?>

Il fonctionne parfaitement quand j'envoie les messages dans mon mail orange lu avec outlook par exemple.
Par contre, quand j'envoie ce même message avec la même pièce jointe (un .pdf) dans ma boite gmail, la pièce jointe ressemble à ça.

JVBERi0xLjQKJcfsj6IKNSAwIG9iago8PC9MZW5ndGggNiAwIFIvRmlsdGVyIC9GbGF0ZURlY29k
ZT4+CnN0cmVhbQp4nN1b3XIVNxK+P08xd3smlSP0r1HuwkJRocgmgLO5IHthjDFO2QZ7MZU8wL4A
T5jL+C22NSONWn9zju2TLWqhoDQaTavV6p+vWzqXHSWMd9T9DY2j89VlxwcrxydNbceoEJ1mjBNt
3OsHL0z36P3q+Yp2T+DfyepyNRDh/oyf4PbReffwAD4YOqB+8HZFibVWsvEd66RhhMvOCEEUvD5f
rbv+4NfVRlIKU+luwygZuoM3vp8J9/TMPf1t+o9rS6SwMFBMA1+tv+83DKhJIdfv+w0l3Co1qPUF
an/sN5wwLYVen8bmSW+JYdRwN5ZTIqiR69975no1h+mAAJNaGbY+i18d94LAOPz2Yb8RRMBUen3V
czJQ4DCMQ1O3uMNt/9U16vqtd1IRlKn112jSndgzyoq1QMQexJEUdaupLZjkeAhH3RS1Wdmv2GBg
1n8dPF1tik2advPgq1frv0dRvZs+lBbY/XPiepLeLIV/u53l3Fozr8dYvj6c3r5B379Da7npN5pw
rWwgp1kgWNEJaFem+xpNh6cu2fe7Vk7pqAE5AZ14q1Lmx69T5rEgtnKesgxqwpQM2zDai5P5sx5e
aAmMfO43hnBK7frbHmzGKCowCdT00567qaQGTtavkbjPIgee4UztnKye9JsB1IBJGwZdbNklL5uz
Ymcw2R8i2elLPYhEnKfxczyhJ47eFkLG07wpjdK/tpRJbHFeqEyxIFUddEkwjlrY/o8SbhyZw8Jf
nG53HZ3fdK7WL/uNBD9MYTkf0Ij3oy5wDdtoFPjg0WVGL1Wf7lNz8aMOt2Uzvr6vQxZ3dchBFA97

etc etc...

De plus, le message arrive en spam. Une idée svp ?

Hors ligne

#8 27-05-2010 22:16:45

Sq78
Membre
Inscription : 13-05-2009
Messages : 47

Re : Problème pour l'envoi de pièces jointes par mail

Je pense avoir résolu le problème en remplaçant la ligne

$headers .= "\nMIME-Version: 1.0\nContent-Type: multipart/mixed; boundary=\"$boundary\"\n";

par

$headers .= 'Content-Type: multipart/mixed;'."\n".' boundary="'.$boundary.'"';

Et cette fois j'ai bien reçu le message sous gmail et orange avec la pièce jointe "intacte"

Il ne me reste plus qu'à comprendre ce que j'ai fait....

Hors ligne

Pied de page des forums