Fichier joint à un mail via formulaire sans DB

Petit nouveau ! | 6 Messages

30 juil. 2007, 11:22

Bonjour,
Ayant été très agréablement surpris de la pertinence des réponses apportées aux questions posées sur le forum je me permet d'exposer un problème que je n'arrive pas à résoudre (j'ai pourtant parcouru les différents thread qui faisaient référence à ce genre de situation).

Je dispose du code suivant dans mon formulaire:
<tr class="unite">
	<td><img src="images/puceliste.gif" /> <b>CV joint&nbsp;</b>
	</td>
	<td width="70%">
		<center>
		<input type="file" name="fichier" value="Choisir">
		</center>
	</td>
</tr>
Et voici le code contenu dans les balises php toujours dans la même page:
<?php
}
//Si la variable $Nom n'existe pas, cela signifie que le formulaire n'a pas encore été complété. La fonction formulaire() est alors appelée pour l'afficher. Si la variable $Nom existe, les données sont récupérées dans les variables.
if(!isset($nom)) {
        formulaire();
}
else {

	if ( ! empty($fichier)) {
	$fich= $_FILES['fichier']['tmp_name'];
 	$ftaille = $_FILES['fichier']['size'];
 	$ftype = $_FILES['fichier']['type'];
 	$fnom = $_FILES['fichier']['name'];
	
	$destinataire = '[email protected]';
	
	$from_email  = trim(htmlentities($_POST['email']));
	$entetedate  = date("D, j M Y H:i:s +0100"); // Offset horaire
			
	$entetemail  = "From: $from_email \n"; // Adresse expéditeur
	$entetemail .= "Cc: \n"; 
	$entetemail .= "Bcc: \n"; // Copies cachées
	$entetemail .= "Reply-To: $from_email \n"; // Adresse de retour
	$entetemail .= "X-Mailer: PHP/" . phpversion() . "\n" ;
	$entetemail .= "Date: $entetedate";
			
	$message = "Société : ".trim(($_POST['societe']))."\n";
	$message .= "\n";
	$message .= "Expérience dans la sécurité : ".trim(($_POST['experience']))."\n";
	$message .= "\n";
	$message .= "".trim(($_POST['particule']))." ";
	$message .= "".trim(($_POST['nom']))." ";
	$message .= "".trim(($_POST['prenom']))."\n";
	$message .= "\n";
	$message .= "".trim(($_POST['adresse']))."\n";
	$message .= "".trim(($_POST['codepostal']))." ";
	$message .= "".trim(($_POST['ville']))."\n";
	$message .= "\n";
	$message .= "Email : ".trim(($_POST['email']))."\n";
	$message .= "Tel : ".trim(($_POST['telephone']))."\n";
	$message .= "\n";
	$message .= "Message : \n".trim($_POST['message'])."\n";

// ajout d'un texte à la saisie
 	$message .= "\r\nNB: Ajout d'une pièce jointe : $fnom\r\n";
// ouverture du fichier joint upload en lecture
 	$f = fopen($fich, "r") or die("Ouverture du fichier $fich impossible.");
// récupération du code du fichier joint
 	$code = fread($f, $ftaille);
// encodage standard
 	$code = chunk_split(base64_encode($code));
	
	fclose($fich);
	
// dans le cas de l'envoi d'un fichier joint il convient de séparer le corps du message du fichier
// pour cela on utilise un identifiant de séparateur 
// identifiant de séparateur, en principe unique
 	$limite = md5(uniqid(time() ));

// composition de l'en-tête du message : le message textuel suivi du code du fichier
	$msg = "--$limite\r\n";
	$msg .= "Content-Type: multipart/mixed;boundary=$limite\r\n\r\n";
 	$msg .= "--$limite\r\n";
 	$msg .= "Content-type:text/plain;charset=us-ascii\r\n";
 	$msg .= "Content-transfer-encoding:8bit\r\n";
	// Il est indispensable d'introduire une ligne vide entre l'entête et le texte
	$msg .= "\r\n";
 	$msg .= "$message\r\n";

	$msg .= "--$limite\r\n";
	$msg .= "Content-type:$ftype;name=$fnom\r\n";
 	$msg .= "Content-transfer-encoding:base64\r\n\r\n";
	$msg .= "$code\r\n";
 	$msg .= "--$limite--";

function decoder($msg){
       
        $msg = stripslashes($msg); // évitete les anti-slashs d'échappement
        $msg = trim($msg); // évite les '\n', '\r', '\t' etc
        return $msg;
}


	//Vérification champs obligatoires ok
	if (empty($_POST['societe']) || empty($_POST['experience']) || empty($_POST['particule']) || empty($_POST['nom']) || empty($_POST['prenom']) || 
			empty($_POST['adresse']) || empty($_POST['ville']) || empty($_POST['codepostal']) || empty($_POST['email']) || empty($_POST['telephone']) || 
			empty($_POST['message']) ) {
		echo "<p class='red'>Vous n'avez pas compl&eacute;t&eacute; enti&egrave;rement votre formulaire&nbsp;:</p>";
		$erreur=1;
	}
	//Vérification validité du mail
	if (!eregi("^[0-9a-z]([-_.]?[0-9a-z])*@[0-9a-z]([-_.]?[0-9a-z])*\\.[a-z]{2,4}$",$_POST['email']) && ($erreur<>1)){
		echo "<p class='red'>L'adresse e-mail est incorrecte&nbsp;:</p>";
		$erreur=1;
	}
	//Vérification validité du numéro de téléphone
	if (!eregi("^([0-9]){10}$",$_POST['telephone']) && ($erreur <>1)){
		echo "<p class='red'>Le num&eacute;ro de t&eacute;l&eacute;phone est incorrect&nbsp;:</p>";
		$erreur=1;
	}
	}
	else{
		echo "<p class='red'>Fichier joint manquant</p>";
		$erreur=1;
	}
	
	//Si erreur on relance le formulaire
	if ($erreur==1) {
		formulaire($_POST['societe'],$_POST['particule'],$_POST['nom'],$_POST['prenom'],$_POST['adresse'],$_POST['ville'],$_POST['codepostal'],$_POST['telephone'],$_POST['email'],$_POST['experience'],$_POST['message'],"");
	}
	else {
		if( @mail($destinataire, "[RECRUTEMENT]", decoder($msg), $entetemail) ) {
			echo "<p align=\"center\">Message envoy&eacute;<br /></p>";
		} 
		else {
			echo "<p align=\"center\">Echec lors de l'envoi<br /></p>";
		} 
	}
}
?>
J'ai mis plus de vingt lignes pour que tout soit bien compréhensible par tous. Mon problème est donc le suivant, quand je teste un envoi de mail j'obtiens par exemple:

Image

N'ayant aucune expérience concernant l'envoi de pièce jointe par mail, je suppose que toutes les lignes hors texte central ne devraient pas s'afficher et que mon fichier devrait apparaître comme téléchargeable. Malheureusement je ne comprend pas d'où vient le problème, j'ai pourtant bien suivi les instructions données dans d'autres thread ou tutoriaux.

Avatar du membre
Modérateur PHPfrance
Modérateur PHPfrance | 10684 Messages

30 juil. 2007, 13:23

Pour que le client de messagerie interprête et affiche le mail correctement, il te faut spécifier dans les entêtes du mail qu'il est divisé en plusieurs parties et indiquer le séparateur utilisé :
  $entetemail.= "Content-Type: multipart/mixed;\n"; 
  $entetemail.= " boundary=\"----=$limite\"\n\n"; 
Il faudra un peu réorganiser ton code pour que $limite soit défini avant l'entete :)
Ce n'est pas en améliorant la bougie que l'on a inventé l'ampoule...

Petit nouveau ! | 6 Messages

30 juil. 2007, 14:31

J'ai bien effectué les manipulations, ce qui me donne en code :
 	$limite = md5(uniqid(time()));
	
	$destinataire = '[email protected]';
	
	$from_email  = trim(htmlentities($_POST['email']));
	$entetedate  = date("D, j M Y H:i:s +0100"); // Offset horaire
			
	$entetemail  = "From: $from_email \n"; // Adresse expéditeur
	$entetemail .= "Cc: \n"; 
	$entetemail .= "Bcc: \n"; // Copies cachées
	$entetemail .= "Reply-To: $from_email \n"; // Adresse de retour
	$entetemail .= "X-Mailer: PHP/" . phpversion() . "\n" ;
	$entetemail .= "Date: $entetedate";
	$entetemail .= "Content-Type: multipart/mixed;\n"; // On indique que le mail est divisé en plusieurs parties
  	$entetemail .= "boundary=\"----=$limite\"\n\n";  // On indique quel est le séparateur utilisé
			
	$message = "Société : ".trim(($_POST['societe']))."\n";
	$message .= "\n";
	$message .= "Expérience dans la sécurité : ".trim(($_POST['experience']))."\n";
	$message .= "\n";
	$message .= "".trim(($_POST['particule']))." ";
	$message .= "".trim(($_POST['nom']))." ";
	$message .= "".trim(($_POST['prenom']))."\n";
	$message .= "\n";
	$message .= "".trim(($_POST['adresse']))."\n";
	$message .= "".trim(($_POST['codepostal']))." ";
	$message .= "".trim(($_POST['ville']))."\n";
	$message .= "\n";
	$message .= "Email : ".trim(($_POST['email']))."\n";
	$message .= "Tel : ".trim(($_POST['telephone']))."\n";
	$message .= "\n";
	$message .= "Message : \n".trim($_POST['message'])."\n";

// ajout d'un texte à la saisie
 	$message .= "\r\nNB: Ajout d'une pièce jointe : $fnom\r\n";
// ouverture du fichier joint upload en lecture
 	$f = fopen($fich, "r") or die("Ouverture du fichier $fich impossible.");
// récupération du code du fichier joint
 	$code = fread($f, $ftaille);
// encodage standard
 	$code = chunk_split(base64_encode($code));
	
	fclose($fich);
	
Mais cela ne change rien au mail, ormis la ligne

Code : Tout sélectionner

boundary="----=09b708ebd851d5f32feab9398cf4749b"
rajoutée tout en haut. J'ai maintenant une double attribution de variable $limite à boundary dans mon code, celle que l'on voit ci-dessus et celle que l'on trouve dans mon premier post, au niveau des $msg. Dois-je supprimer l'initiale ?

Avatar du membre
Modérateur PHPfrance
Modérateur PHPfrance | 10684 Messages

30 juil. 2007, 17:04

Ah bah un peu qu'il faut la virer l'initiale ! :)

Bon alors pour t'expliquer un peu le principe du comment ça fonctionne : ton mail c'est une simple et grosse chaine de caractères, qu'elle contienne du texte, du html, ou même des fichiers dans le cas de pièces jointes. Pour que ton client de messagerie sache où commence et s'arrête quoi, on défini des frontières (limites ou boundaries, comme tu voudras :)) qui permette de les séparer. On insère pour cela une chaine de caractères (forcément, vu que l'on a que ça :)).

Cette chaine de caractère doit être unique (d'où le uniquid, md5 etc.) pour que le client puisse la confondre avec une autre partie du mail. En effet, si tu utilises la chaine "Salut" comme séparateur, pour peu que quelqu'un dise "Salut" dans le mail, et pof, le client il est perdu :)
Bref, tout ça pour dire qu'on peut mettre n'importe quoi, mais que le uniqid est le plus sur moyen de ne pas tomber sur une chaine présente dans le texte ou dans l'une des pièces jointes.

Oui mais voilà. Puisqu'on peut mettre n'importe quoi, faut informer le client de ce que l'on a utilisé (il ne peut pas l'deviner tout seul). Donc on lui rajoute l'info dans l'entête du mail : "Salut à toi cher client de messagerie, je me permet de t'informer que ce mail est constitué de plusieurs parties et que nous avons pris soin de les séparées par cette chaine --sd654(...)96jg, afin de te permettre de mieux les identier. Nous t'en souhaitons bonne réception et bon courrage pour remettre les morceaux en forme." (Bon je te l'accorde, c'est peut être pas aussi formel, mais l'idée est là ;))

Donc, forcément, si dans l'entête du mail tu lui dis que le séparateur c'est --sd123 et qu'ensuite tu en regénères un nouveau --pk789 que tu utilises pour séparer les parties de ton mail, bah il va pas pouvoir s'y retrouver :)
Ce n'est pas en améliorant la bougie que l'on a inventé l'ampoule...

Petit nouveau ! | 6 Messages

30 juil. 2007, 17:24

Merci pour l'explication simple :wink:
J'ai donc enlevé
 $msg = "--$limite\r\n";
    $msg .= "Content-Type: multipart/mixed;boundary=$limite\r\n\r\n"; 
ainsi que le . pour le $msg suivant.

Ayant rempli CV.txt qui me sert de CV de test par la chaîne "super cv", j'obtiens

Code : Tout sélectionner

NB: Ajout d'une pièce jointe : CV.txt --71e6ed6fd122385f47ea6d4c8bc0db30 Content-type:text/plain;name=CV.txt Content-transfer-encoding:base64 U3VwZXIgY3Y= --71e6ed6fd122385f47ea6d4c8bc0db30--
dans la fin de mon mail; je suppose donc que "super cv" correspond à la chaîne "U3VwZXIgY3Y=". Cependant cela ne permet pas au récepteur du message de pouvoir télécharger ce CV.txt comme toute pièce jointe normale, ce qui est en fait le but recherché!

Sachant que je désirerais obtenir un mail normal juste avec le texte et le fichier joint téléchargeable (comme si j'envoyais un mail de n'importe quel BAL gratuite ou de FAI), pour l'instant je me retrouve avec un affichage de lignes "Content-type", "boundary", etc.. qui polluent la lecture du mail, et mon fichier est sous la forme d'une ligne de texte incompréhensible. Est-ce que je ne serais pas passé à côté d'un truc essentiel?

Avatar du membre
Modérateur PHPfrance
Modérateur PHPfrance | 10684 Messages

30 juil. 2007, 17:57

Parce qu'en plus d'envoyer un mail avec pièce jointe tu veux qu'on puisse la lire ?! T'es drolement exigeant toi ?! ;)

J'ai rejeté un oeil à ton code et réalise que le code que je t'ai donné n'est pas tout à fait correct, en effet, tu utilises "--$limite" et par habitude je t'avais collé "----=$limite" comme valeur.. donc peut être à rectifier...
Ce n'est pas en améliorant la bougie que l'on a inventé l'ampoule...

Petit nouveau ! | 6 Messages

31 juil. 2007, 09:24

Bon en fait le problème restant était tout con, il fallait tester si le serveur était linux ou windows et en fonction de cela donner une valeur précise à la variable $eol servant de retour chariot:
if(strtoupper(substr(PHP_OS,0,3)=='WIN')){
      $eol="\r\n";
      $sol="\n";
    }elseif(strtoupper(substr(PHP_OS,0,3)=='MAC')){
      $eol="\r";
    }else{
      $eol="\n";
    }
Encore merci pour l'aide!