Email Attachment from directory is not always the latest using PHPMailer - php

UPDATE #2 - FINAL ANSWER
I figured and you'll see in the comments below that my comparison symbol needed to be changed. So I took the code in Update #1 and simply changed this:
return filemtime($a) < filemtime($b);
to:
return filemtime($a) > filemtime($b);
And that did it!!! Thanks everyone for the dialog.
UPDATE
I've updated my code. Now I'm getting the attachment sent but again, it's not grabbing the latest one. Here's my latest code:
<?php
ini_set('display_errors',1);
ini_set('display_startup_errors',1);
error_reporting(-1);
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
require 'php/PHPMailer2020/src/Exception.php';
require 'php/PHPMailer2020/src/PHPMailer.php';
$dir = "php/sketch-drawings/";
$pics = scandir($dir);
$files = glob($dir . "*.*");
usort($files, function($a, $b){
return filemtime($a) < filemtime($b);
});
foreach ($files as $pics) {
echo '<img src="'. $pics . '">';
}
// SEND EMAIL W/ ATTACHMENT OF LATEST IMAGE
$mail = new PHPMailer();
$mail->Sender = "hello#myemail.com";
$mail->From = "mail#myemail.com";
$mail->FromName = "My Company";
$mail->AddReplyTo( "mail#mycompany.com", "DO NOT REPLY" );
//$mail->AddAddress("info#mycompany.com");
$mail->AddAddress("myemail#gmail.com");
$mail->isHTML(true);
$mail->Subject = "Latest Sketch Image";
$mail->Body = "Latest Image Attached! \n\n Thanks - XYZ Digital.";
$mail->AddAttachment($pics);
if(!$mail->Send()) {
echo 'Email Failed To Send.';
}
else {
echo 'Email Was Successfully Sent.';
echo "</p>";
echo '<script language="javascript">';
echo 'alert("Thanks! Message Sent")';
echo '</script>';
}
$mail->ClearAddresses();
?>
This issue:
The AddAttachment($filepath) returns an attachment, but it's not the most recent one-- I need it to always return the most recent image in the directory.
Here's what I'm doing:
I've created a web application that is basically a sketch pad. When a user taps on "Save" the code snaps a pic of the element saves it to a directory and then sends an email to the pre-defined address with the snap of the canvas as an attachment... and the issue is above.
What I've done:
I've looked all over the place for a solve -- seems as though the only find is around uploading the attachments via a web form--which isn't my issue. My workflow is different and why I'm here asking now. :)
What's not working:
Everything works except for the attachment part. I'm using PHPMailer but for some reason, the attachment is never the latest one. In fact, it seems to always be the same attachment no matter what.
I tried to get the first element in the array doing this (which I echo in the bottom of the code and it returns the right image:
$mail->addAttachment($sketches[0]);
this doesn't work -- it sends the email, but nothing attached.
How in the world, can I adjust my code below to get the latest attachment? I appreciate any help as PHP is not my area of expertise.
See the code below...
<?php // NEW
// DEFINE ERRORS
ini_set('display_errors',1);
ini_set('display_startup_errors',1);
error_reporting(-1);
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
require 'PHPMailer2020/src/Exception.php';
require 'PHPMailer2020/src/PHPMailer.php';
// GET LATEST UPLOAD -- AND HAVE IT STORED IN DOM (WHEN NEEDED)
$path = ('sketch-drawings');
// Sort in descending order
$sketches = scandir($path, 1);
$latest_ctime = 1;
$latest_filename = '';
$d = dir($path);
while (false !== ($entry = $d->read())) {
$filepath = "{$path}/{$entry}";
// could do also other checks than just checking whether the entry is a file
if (is_file($filepath) && filectime($filepath) > $latest_ctime) {
$latest_ctime = filectime($filepath);
$latest_filename = $entry;
}
// SEND EMAIL W/ ATTACHMENT OF LATEST IMAGE
$mail = new PHPMailer();
$mail->Sender = "hello#myemail.com";
$mail->From = "mail#myemail.com";
$mail->FromName = "My Company";
$mail->AddReplyTo( "mail#myemail.com", "DO NOT REPLY" );
//$mail->AddAddress("info#myemail.com");
$mail->AddAddress("myemail#gmail.com");
$mail->isHTML(true);
$mail->Subject = "Latest Sketch Image";
$mail->Body = "Latest Image Attached! \n\n Thanks - XYZ Digital.";
$mail->AddAttachment($filepath);
//$mail->addAttachment($sketches);
if(!$mail->Send()) {
echo 'Email Failed To Send.';
} else {
echo $filepath;
echo "<br><br>";
print $latest_filename;
echo "</p>";
echo "<p>";
print $sketches[0];
echo "</p>";
echo "<p>";
echo 'Email Was Successfully Sent.';
echo "</p>";
echo '<script language="javascript">';
echo 'alert("Thanks! Message Sent")';
echo '</script>';
}
$mail1->ClearAddresses();
}
?>

PHP's dir() function returns items in an indeterminate order. If you want to retrieve files ordered by date, see this answer.

Related

Files corrupted

I have a google apps scripts app that gets Gmail message attachments, posts it to DB using JDBC. then on the server, a PHP script gets the data and puts in into a file and attaches it to an email.
the problem is that the files are corrupt when email arrives
here is the google apps script function that gets the attachment content
function getMessageAttachmentsArray(msg){
var GmailAttachments = msg.GmailMessage.getAttachments();
var validAttachments = [];
var attachmentNames = [];
if(GmailAttachments)
{
for(i in GmailAttachments)
{
var gName = GmailAttachments[i].getName();
attachmentNames.push(gName);
var mimeType = GmailAttachments[i].getContentType();
var size = GmailAttachments[i].getSize();
var content = Utilities.base64Encode(GmailAttachments[i].getDataAsString(), Utilities.Charset.UTF_8);
var push = {"content":content,"mimeType":mimeType,"fileName":gName,"size":size,"id":""};
validAttachments.push(push);
}
}
return [validAttachments, attachmentNames];
}
here is the PHP code that generated the email from the file data:
require_once 'smtpmail/classes/class.phpmailer.php';
$mail = new PHPmailer(true);
$email = $argv[1];
$messageid = $argv[2];
$fax_number = $argv[3];
$attachments = array();
//get the attachments for this email
$Sql = "select * from user_attachments where email = '$email' and messageid like '$messageid%'";
$res = mysql_query($Sql);
while($row = mysql_fetch_array($res)){
$return['filename'] = $row['name'];
$return['mime'] = $row['mime_type'];
$content = base64_decode(str_pad(strtr($row['raw_data'], '-_', '+/'), strlen($row['raw_data']) % 4, '=', STR_PAD_RIGHT));
$temp_file = tempnam(sys_get_temp_dir(), 'Fax');
file_put_contents($temp_file, $content);
$return['file'] = $temp_file;
array_push($attachments, $return);
}
try{
$mail->IsSMTP();
$mail->SMTPDebug = 1;
$mail->SetFrom("example#example.com", "example email");
$mail->Subject = '';
$mail->Body = ' '; //put in a blank body to avoid smtp error
$mail->AddAddress($email);
foreach($attachments as $file){
$mail->AddAttachment($file['file'], $file['filename'], 'base64' ,mime_content_type($file['file']));
}
if($mail->send()){
echo "email to $email sent successfully\n";
}else{
echo "error sending email to $email\n";
}
}catch(phpmailerException $e){
echo $e->errorMessage();
}catch(Exception $e){
echo $e->getMessage();
}
When the message is received it shows the attachments but when downloaded I can not open them and there is a message that the file is corrupt or the file extension does not match the file format
what am I doing wrong?
Thanks in advance.
EDIT:
I tried emailing the attachment without posting to the DB, by posting to the server with UrlFetchApp() and the results are the same. clearly, I am doing something wrong with Base64_encode / decode...
maybe the google apps scripts :
Utilities.base64Encode(GmailAttachments[i].getDataAsString(), Utilities.Charset.UTF_8);
creates a different base64 format than PHP base64_decode expects?
p.s.
I tried also with and without 'str_pad' and I still got the same results.
I changed:
Utilities.base64Encode(GmailAttachments[i].getDataAsString(), Utilities.Charset.UTF_8);
to:
Utilities.base64Encode(GmailAttachments[i].getBytes());
and it works

MimeMailParser Extension IMAP Setup

Currently I have an old script that parses emails, as seen here:
// Accessing the mailbox
$mailbox = imap_open("{imap.gmail.com:993/imap/ssl/novalidate-cert}INBOX", $mailbox, $mailboxPassword);
// Retrieving only unread messages
$mail = imap_search($mailbox, 'UNSEEN');
// If no new messages found aborting the script
if(empty($mail)) die('No unread emails found!');
$total_found = 0;
$skipped = 0;
// Now we loop through messages
foreach ($mail as $key => $val) {
// process everything
}
This works fine other than some encoding issues with Russian (Cyrillic) characters and a few other issues. While I could hunt down all these issues individually, it seems like there are already great mail parsing classes out there. I found this, which I'd like to use as it sounds like this gets suggested often.
The example code provided is with the parser is below.
<?php
require_once('MimeMailParser.class.php');
$path = 'path/to/mail.txt';
$Parser = new MimeMailParser();
$Parser->setPath($path);
$to = $Parser->getHeader('to');
$from = $Parser->getHeader('from');
$subject = $Parser->getHeader('subject');
$text = $Parser->getMessageBody('text');
$html = $Parser->getMessageBody('html');
$attachments = $Parser->getAttachments();
?>
However it seems to need a reference to $path which is confusing me as the emails are not stored in a folder, there pulled from IMAP. Would I add $path = $mail; in the foreach block? If not, what format do I supply the email to the parser in? Do I have to use the same script I already have and save it to a folder?
All the emails are being retrieved from Gmail. I used IMAP but could use POP instead if IMAP wont work.
Based on the suggested answer i tried this code but its just looping through x unread emails and displaying blank data for everything, headers and body?
// Accessing the mailbox
$mailbox = imap_open("{imap.gmail.com:993/imap/ssl/novalidate-cert}INBOX", $mailbox, $mailboxPassword);
// Retrieving only unread messages
$mail = imap_search($mailbox, 'UNSEEN');
// If no new messages found aborting the script
if(empty($mail)) die('No unread emails found!');
$total_found = 0;
$skipped = 0;
// Now we loop through messages
foreach ($mail as $email) {
$Parser = new MimeMailParser();
$Parser->setText($mail);
echo "-----------------------------Start Of Email---------------------------------";
echo "<br /><br /><br /><br />";
$to = $Parser->getHeader('to');
echo "To: " . $to . "<br />";
$from = $Parser->getHeader('from');
echo "From: " . $from . "<br />";
$subject = $Parser->getHeader('subject');
echo "Subject: " . $subject . "<br /><br /><br />";
//$text = $Parser->getMessageBody('text');
$html = $Parser->getMessageBody('html');
echo "Body: " . "<br /><br />" . $html . "<br />";
//$attachments = $Parser->getAttachments();
echo "<br /><br /><br /><br />";
echo "-----------------------------End Of Email---------------------------------";
}
That class has another function to set the message content directly. Just call $Parser->setText($mail) where $mail is the message content in your IMAP foreach loop.

Confirmation code wont copy to phpmailer

Ok, my problem is that when i try to make my confirm link with the random code that i already created, it wont pass to the Confirmation mail. However the confirm code, still inserts to the database without problems. This is my code:
function NewUser()
{
$user = $_POST['user'];
$pass = $_POST['pass'];
$confirm = md5(uniqid(rand(), true));
$success = "INSERT INTO members(user,pass,'$confirm')";
$data = mysql_query ($success)or die(mysql_error());
if($data)
{if($data)
{
SendUserConfirmationEmail($confirm);
echo "<div class ='verdanacenter'><img src='img/bienvenido.png' title='Enhorabuena'/><br><br><br><font face ='verdana'>Welcome <b>$name $lname</b> !!!<br>Success.<br><br>Soon u will receive a confirmation msg to <b>$email</b>";
}
else
{
echo "<div class ='verdanacenter'><img src='img/alerta.png' title='Error'/><br><br><br><font face ='verdana'>Fatal Error !!!";
}
}
function SendUserConfirmationEmail($confirmcode)
{
require 'PHPMailerAutoload.php';
$mail = new PHPMailer();
$mail->isSendmail();
$mail->setFrom('info#rene.org', 'Rene');
$mail->addAddress($_POST['email'],$_POST['name']);
$mail->Subject = 'Welcome';
$mail->IsHTML(true);
$confirmcode = $confirm;
$mail->Body = '<p align="left"> Welcome <b>'.$_POST['name'].'</b>,</p><p align="justify">This is ur confirmation code:</p>
<p align="left">CONFIRMAR,</p>
<p align="left">Regards,<br>
Admin.<br>
www.rene.org</p>';
if (!$mail->send()) {
echo "Mailer Error: " . $mail->ErrorInfo;
} else {
echo "Message sent!";
}
}
The thing is that, when i register, i receive the email, but without the random code ($confimcode). It appears: "http://www.rene.org/confirmar.php?code=" instead of "http://www.rene.org/confirmar.php?code=A23B45423545V6764542543" What am i missing guys? PLEASE HELP.
This is called scope.
The function can't see variables from outside of itself. To pass that code to the function you include it in a parameter:
function SendUserConfirmationEmail($confirmcode){
...
when you call the function you pass your variable:
SendUserConfirmationEmail($confirm);
Anywhere inside your function, that value will be available as $confirmcode
Edit: Also just noticed you have a function inside a function. Don't do that, just make them separate.

Validate Image with exif_imagetype but still able to upload non image files

I am not sure if my exif_imagetype is coded wrong. It send the email just fine with photos attached. But when I try to attach non image files it will still allow me to do so and send the email with the attachment. Please help.
ob_start();
require("class.phpmailer.php");
$photo = $_FILES['photo'];
isset($_POST['submit']);
$active_keys = array();
foreach($_FILES[$photo]['name'] as $key => $filename){
if(!empty($filename)){
$active_keys[] = $key;
} }
foreach($active_keys as $key){
switch(exif_imagetype($_FILES[$photo]['tmp_name'][$key])) {
case IMAGETYPE_JPEG:
case IMAGETYPE_PNG:
break;
default:
echo "{";
echo "$errors: 'This is no photo..'\n";
echo "}";
exit(0);
} }
$message = "some message";
$mail = new PHPMailer();
$mail->From = ('sample#youdomain.net');
$mail->AddAddress=('sample#youdomain.net');
$mail->Subject = "Submitted Photos";
$mail->Body = $message;
$mail->WordWrap = 50;
foreach($_FILES['photo']['tmp_name'] as $photo)
if(!empty($photo)) {
$mail->AddAttachment($photo);
}
$mail->Send();
header("Location: thankyou.php");
exit();
}}
use getimagesize() function before processing anything, it provides better result
example:
if(getimagesize($fullpath_to_file)){
//then do something here
}

PHPMailer exception error

I've written my own Code Igniter model for sending emails. All was fine until recently when I started to get this error:
Fatal error: Cannot redeclare class phpmailerException in /home/mysite/public_html/subdir/application/libraries/phpmailer/class.phpmailer.php on line 2319
I'm using:
CodeIgniter 2
PHPMailer 5.1
I've tried the following to resolve it:
Added "$mail->SMTPDebug = 0" to turn off errors.
Added: "$mail->MailerDebug = false;"
Modified the PHPMailer to only show errors when SMTPDebug is turned on.
Looked for and removed any echo statements
Added try / catch blocks Tried adding / removing: $mail = new PHPMailer(true);
Here is my controller method (company/contact) which calls my model (message_model):
function contact()
{
//Do settings.
$this->options->task='email';
$this->options->change = 'sent';
$this->options->form_validation='';
$this->options->page_title='Contact Us';
//Import library
include_once('application/libraries/recaptcha/recaptchalib.php');//Include recaptcha library.
//Keys for recaptcha, stored in mainconfig file.
$this->options->publickey = $this->config->item('recaptcha_public');
$this->options->privatekey = $this->config->item('recaptcha_private');
//Form validation
$this->form_validation->set_error_delimiters('<div class="error">', '</div>');
$this->form_validation->set_rules('name_field','Name of problem','trim|required|min_length[3]|max_length[100]');
$this->form_validation->set_rules('desc_field','Description','trim|required|min_length[10]|max_length[2000]');
$this->form_validation->set_rules('email_field','Your email address','trim|required|valid_email');
$this->form_validation->set_rules('recaptcha_response_field','captcha field','trim|required|callback__check_recaptcha');
//If valid.
if( $this->form_validation->run() )
{
//Set email contents.
$message="This is a message from the contact form on ".$this->config->item('site_name')."<br /><br />";
$message.=convert_nl($this->input->post('desc_field'));
$message.="<br /><br />Reply to this person by clicking this link: ".$this->input->post('name_field')."<br /><br />";
$options = array('host'=>$this->config->item('email_host'),//mail.fixilink.com
'username'=>$this->config->item('email_username'),
'password'=>$this->config->item('email_password'),
'from_name'=>$this->input->post('name_field'),
'to'=>array($this->config->item('email_to')=>$this->config->item('email_to') ),
'cc'=>$this->config->item('email_cc'),
'full_name'=>$this->input->post('name_field'),
'subject'=>'Email from '.$this->config->item('site_name').' visitor: '.$this->input->post('name_field'),
'message'=>$message,
'word_wrap'=>50,
'format'=>$this->config->item('email_format'),
'phpmailer_folder'=>$this->config->item('phpmailer_folder')
);
//Send email using own email class and phpmailer.
$result = $this->message_model->send_email($options);
//Second email to sender
//Set email contents.
$message="Thank you for your enquiry, we aim to get a reply to you within 2 working days. In the meantime, please do follow us on www.facebook.com/autismworksuk";
$options = array('host'=>$this->config->item('email_host'),//mail.fixilink.com
'username'=>$this->config->item('email_username'),
'password'=>$this->config->item('email_password'),
'from_name'=>$this->input->post('name_field'),
'to'=>$this->input->post('email_field'),
'full_name'=>$this->input->post('name_field'),
'subject'=>'Email from '.$this->config->item('site_name'),
'message'=>$message,
'word_wrap'=>50,
'format'=>$this->config->item('email_format'),
'phpmailer_folder'=>$this->config->item('phpmailer_folder')
);
//Send email using own email class and phpmailer.
$result = $this->message_model->send_email($options);
//Set result.
if($result==-1)
$this->session->set_flashdata('result', ucfirst($this->options->task).' was not '.$this->options->change.' because of a database error.');
elseif($result==0)
$this->session->set_flashdata('result', 'No changes were made.');
else
$this->session->set_flashdata('result', ucfirst($this->options->task).' was '.$this->options->change.' successfully.');
//Redirect to completed controller.
redirect('completed');
}
//Validation failed or first time through loop.
$this->load->view('company/contact_view.php',$this->options);
}
Here is my model's method to send the emails. It used to work but without any changes I can think of now I get an exception error:
function send_email($options=array())
{
if(!$this->_required(array('host','username','password','from_name','to','full_name','subject','message'),$options))//check the required options of email and pass aggainst provided $options.
return false;
$options = $this->_default(array('word_wrap'=>50,'format'=>'html','charset'=>'utf-8'),$options);
try
{
if(isset($options['phpmailer_folder']))
require($options['phpmailer_folder']."/class.phpmailer.php");
else
require("application/libraries/phpmailer/class.phpmailer.php");//Typical CI 2.1 folder.
$mail = new PHPMailer();
$mail->MailerDebug = false;
//Set main fields.
$mail->SetLanguage("en", 'phpmailer/language/');
$mail->IsSMTP();// set mailer to use SMTP
$mail->SMTPDebug = 0;
$mail->Host = $options['host'];
$mail->SMTPAuth = TRUE; // turn on SMTP authentication
$mail->Username = $options['username'];
$mail->Password = $options['password'];
$mail->FromName = $options['from_name'];//WHo is the email from.
$mail->WordWrap = $options['word_wrap'];// Set word wrap to 50 characters default.
$mail->Subject = $options['subject'];
$mail->Body = $options['message'];
$mail->CharSet = $options['charset'];
//From is the username on the server, not sender email.
if(isset($options['from']))
$mail->From = $options['from'];
else
$mail->From = $mail->Username; //Default From email same as smtp user
//Add reply to.
if(isset($options['reply_to']))
$mail->AddReplyTo($options['reply_to'], $options['from']);
if(isset($options['sender']))
$mail->Sender = $options['sender'];
//Add recipients / to field (required)
if(is_array($options['to']))
{
foreach($options['to'] as $to =>$fn)
$mail->AddAddress($to, $fn);
}
else
{
$mail->AddAddress($options['to']); //Email address where you wish to receive/collect those emails.
}
//Add cc to list if exists. Must be an array
if(isset($options['cc']))
{
if(is_array($options['cc']))
{
foreach($options['cc'] as $to =>$fn)
$mail->AddCC($to, $fn);
}
else
{
log_message('debug', '---->CC field must be an array for use with Message_Model.');
}
}
//Add bcc to list if exists. Must be an array
if(isset($options['bcc']))
{
if(is_array($options['bcc']))
{
foreach($options['bcc'] as $to =>$fn)
$mail->AddBCC($to, $fn);
}
else
{
log_message('debug', '---->BCC field must be an array for use with Message_Model.');
}
}
//Alternative text-only body.
if(isset($options['alt_body']))
$mail->AltBody=$options['alt_body'];
else
$mail->AltBody = htmlspecialchars_decode( strip_tags( $options['message'] ),ENT_QUOTES );//Strip out all html and other chars and convert to plain text.
//Plain/html format.
if(isset($options['format']))
{
if($options['format']=='html')
$mail->IsHTML(true); // set email format to HTML
}
//Send email and set result.
$return['message']='';
if(!$mail->Send())
{
$return['message'].= "Message could not be sent.<br />\n";
$return['message'].= "Mailer Error: " . $mail->ErrorInfo."\n";
$return['result'] = 0;
}
else
{
$return['message'].= "Message has been sent successfully.\n";
$return['result'] = 1;
}
}
catch (phpmailerException $e)
{
log_message('error', '---->PHPMailer error: '.$e->errorMessage() );
}
catch (Exception $e)
{
log_message('error', '---->PHPMailer error: '.$e->errorMessage() );
}
return $return;
}
if (!class_exists("phpmailer")) {
require_once('PHPMailer_5.2.2/class.phpmailer.php');
}
This Code will clear this issue 100%..
Basically one of two things is happening:
You are "including" your PHP code twice somewhere, causing the 2nd time to generate the redeclaration error
You are using "phpmailerException" somewhere else, besides your model. Have you tried to do a "find all" in your IDE for ALL calls to "phpmailerException" - perhaps you used this name in another area for another exception?
require_once("class.phpmailer.php") is better.
Mukesh is right that require_once will solve Sift Exchanges answer #1. However, there is not need to check if the class exists as require_once does that.

Categories