Swiftmailer rejecting valid email addresses if and only if submitted in array - php

I'm pulling users' phone numbers from an s2member database and appending the wireless carrier domain name required to send SMS texts via email (Swift Mailer). So to create the array of email addresses I use the following (the function cellmap below just replaces the users' wireless carrier with the correct domain to append):
$matches = array();
if (is_array ($users) && count ($users) > 0) {
foreach ($users as $user) {
$user = new WP_User ($user->ID);
$matches[] = "'" . get_user_field("cell_no", $user->ID) . cellmap(get_user_field("cell_carrier",$user->ID)) . "'";
}
}
The resulting array looks like this, for an example of 4 matched users:
Array
(
[0] => '1234567891#vtext.com'
[1] => '3216549871#vtext.com'
[2] => '9876543211#vtext.com'
[3] => '6543219877#vtext.com'
)
Then I implode to create a string to use as the "To" field for Swift Mailer:
$cell_list = implode(", ", $matches);
which results in this (numbers are made up):
'1234567891#vtext.com', '3216549871#vtext.com', '9876543211#vtext.com', '6543219877#vtext.com'
Now I pass that to Swift Mailer like so:
$outgoing_message = Swift_Message::newInstance('')
->setFrom(array('no-reply#mydomain.com' => 'Mydomain'))
->setTo($cell_list)
->setBody($message);
// Send the message
$result = $mailer->send($outgoing_message);
And I get this error (ignore the phone numbers: they're made up, but correspond to the right ones in practice):
PHP Fatal error: Uncaught exception 'Swift_RfcComplianceException' with message 'Address in mailbox given ['123456789#vtext.com', '987654321#vtext.com', '5555551212#vtext.com', '321654987#vtext.com'] does not comply with RFC 2822, 3.6.2.'
I can successfully send any one of these emails individually via Swift Mailer, but when they appear as an array I always get the above error. I have tried applying trim to the individual addresses and the entire resulting string. A print_r of the array does not show any non-printable characters. I have tried various combinations of ->setTo($cell_list), ->setTo(array('$cell_list')) and anything else that might work, to no avail. I have tried replacing comma with semicolon in the list, and removing the single quotes around each address. As far as I can tell, the string of email addresses is in the exact format as shown in the Swift Mailer documentation. For the life of me I can't figure this out.

According to Swiftmailer documentation, setTo takes either a single email address or an array of email addresses as parameter.
Your code:
$cell_list = implode(", ", $matches);
$outgoing_message = Swift_Message::newInstance('')
->setFrom(array('no-reply#mydomain.com' => 'Mydomain'))
->setTo($cell_list)
->setBody($message);
Using implode() puts all the email addresses in a single string of text.
I suggest not imploding $matches:
$outgoing_message = Swift_Message::newInstance('')
->setFrom(array('no-reply#mydomain.com' => 'Mydomain'))
->setTo($matches)
->setBody($message);

Related

Problems manipulating JSON data in PHP

I'm not that handy with JSON so here goes. I'm receiving Amazon SNS notifications for bouncing email addresses to a listener (in PHP 5.5) which does:
$post = #file_get_contents("php://input");
$object = json_decode($post, true);
This gives me:
Type => Notification
MessageId => #####
TopicArn => #####
Message => {
"notificationType":"Bounce",
"bounce": {
"bounceSubType":"General",
"bounceType":"Permanent",
"bouncedRecipients":[{"status":"5.3.0","action":"failed","diagnosticCode":"smtp; 554 delivery error: dd This user doesn't have a yahoo.com account (testuser#yahoo.com) [0] - mta1217.mail.bf1.yahoo.com","emailAddress":"testuser#yahoo.com"}],
"reportingMTA":"dsn; ######",
"timestamp":"2014-10-27T16:37:42.136Z",
"feedbackId":"######"
},
"mail": {
"timestamp":"2014-10-27T16:37:40.000Z",
"source":"myemail#mydomain.com",
"messageId":"######",
"destination":["testuser#yahoo.com"]
}
}
I was expecting an associative array all the way down but instead it's an array only at the top level and with JSON strings inside. I've tried everything I can think of, including json_decoding further parts of the array, but I'm struggling to access the data in a simple way. What I need is the "destination" email address which should be in $object['Message']['mail']['destination'][0].
Can anyone point out what I'm doing wrong here? Thanks.
It looks like $object['Message'] is also json encoded. Perhaps because it's using some generic container format for service call results. Try this
$post = #file_get_contents("php://input");
$object = json_decode($post, true);
//Message contains a json string
$object['Message'] = json_decode($object['Message'], true);
//Then access the structure using array notation
echo $object['Message']['mail']['destination'][0];

Sorting Imap mailbox by date using ImapMailbox.php

I have a customer support system which creates emails when an email is received. I used to postfix and a special configuration to get a hold of the emails to add extra features.
For example I want to include attachments that were sent from an email. The system doesnt do this , but creates an email with the subject , so I can include the attachments by matching the subjects.
I used ImapMailBox.php to read through the email contents.
It all works fine but I am getting an issue fetching the last email, so I am gettign contents from any other email with the same subject , so I need to fetch the latest email.
$mailboxP = new ImapMailbox('{127.0.0.1:143/novalidate-cert}',POSTFIX_EMAIL,POSTFIX_PASSWORD,ATTACHMENT_DIR, 'utf-8');
foreach($mailbox->searchMails('ALL') as $mailId)
$mail = $mailbox->getMail($mailId);
$mailx=(array)$mail;
$att=$mailx['attachments'];
I have tried using usort to the object $mail , with a function like this
function
mysort($a,$b) {
return strtotime($a->date)-strtotime($b->date);
}
and to the array with a function like this
function mysort($a,$b) {
return strtotime($a['date'])-strtotime($b['date']);
}
I have also tried using imap_sort to $mail and $mailx , but none of this works.
errors I am getting
imap_sort() expects parameter 1 to be resource, array given
imap_sort() expects parameter 1 to be resource, object given
usort() expects parameter 1 to be array, object given
when passing an array I get undefined index date but it defined ..
Can anyone please be kind enough to point me in the right direction.
You can add a function like this on ImapMailbox.php :
public function searchMailsSorted($imapCriteria = 'ALL') {
$this->checkConnection();
$mailsIds =imap_sort($this->mbox,SORTDATE,1,SE_UID,$imapCriteria,$this->serverEncoding);
return $mailsIds ? $mailsIds : array();
}
And then use it in your code like this:
foreach($mailbox->searchMailsSorted('ALL') as $mailId)
{
///insert code here
}
The easiest way is to use Php rsort() function.
<?php
$emailId = rsort($mailbox->searchMails('ALL');
?>

how to handle this: Get all rows with the same email address, operate on ids, email them once

Honestly, I do not even know how to ask this question since I also do not have any plan on where to start.
Say I have the following in mySQL:
id | URL | email |
---|------------|---------|
1 | google.com | a#a.com |
2 | bing.com | a#a.com |
3 | yahoo.com | b#a.com |
My original plan is:
do something on each URL (analyze it), afterwards send an email corresponding to such URL.
that makes:
analyze URL 1, send an email to a#a.com
analyze URL 2, send an email to a#a.com
analyze URL 3, send an email to b#a.com
why should I still do such spamming act when, I can just send an email to a#a.com will all the analysis.
I am trying to be less-spammer as possible, that is instead of , my refined plan then is:
SELECT email FROM table
--> a#a.com, a#a.com, b#a.com
Trim the output such that there will not be duplicate email address
--> a#a.com, b#a.com
Put them in an array or itenary
For each element in array SELECT * FROM table WHERE array()
For each output(the elements), do the analysis
Put the analysis in an array
So say, all analysis is complete
Email the analysis to the parent email.
Am I doing it right?
Is my plan efficient?
Not memory consuming?
Any better plan to handle this?
Fetch all, group by mail, send later. Useful to separate logics.
// Fetch all
$users = $db->fetch('SELECT * FROM table');
// Group by mail
$mails = array();
forach ( $users as $user ) {
$mail = ... analyze stuff with $user->url
$mails[$user->mail][] = $mail;
}
// Send
foreach ( $mails as $address => $content ) {
$subject = count($content) . ' urls analyzed';
$body = implode("\n\n", $content);
mail($address, $subject, $body);
}
Explanation:
The -> are how you pick a property of an object. Most database classes return rows as objects, not associative arrays. (I think most, maybe not.) So if $users is an array of objects, $user will be an object and its mail would be in $user->mail. If they're assoc arrays, $user will be an array and its mail will be in $user['mail']. Both have their advantages.
The 'group by mail' part creates many arrays in $mails, grouped by mail. The fetch order (from the db) doesn't matter, because PHP stacks the results per mail, so $mails would be something like:
array(
'a#a.com' => array(
0 => 'Analytics stuff here...',
1 => 'Analytics stuff here...',
),
'b#a.com' => array(
0 => 'Analytics stuff here...',
),
)
and that's a very neat, compact array with everything you need to send mail. And you could pass it onto another layer of your app. The mail-send-layer, or the html-template-layer, or whatever.
Use ORDER BY to have the entries grouped by emails like so
SELECT * FROM table ORDER BY email
Then inside of each entry loop
$currEmail = $row['email'];
if ($prevEmail != $currEmail) {
// send $mail to $prevMail
$mail = ''; // reset the contents of the e-mail to nothing
}
$mail .= '
// analysis of whatever you are doing
';
$prevEmail = $currEmail;
Don't forget to send the contents of $mail after the loop finishes as well! Since the mail sending condition is triggered on the next row, the loop will finish with a non-empty $mail every time.
This fetches everything with just one query, allows you to "build" your analysis without spamming the same e-mail address with each analysis item, and saves memory, database queries, and processing time by not using arrays or multiple queries.

How to pass a 2 dimensional array value in a swift mailer setTo function

I am getting a 2 dimensional array value as a result after a for loop.The value is $chunk[$i][$j].And when I passed that value into setTo function,
the error showing as
Warning: preg_match() expects parameter 2 to be string, array given in H:\xampp
\htdocs\sngmarket\vendor\swiftmailer\swiftmailer\lib\classes\Swift\Mime\Headers
\MailboxHeader.php line 350.
How do I solve this?.Here my code
$query = $em->createQuery("SELECT DISTINCT u.emailaddress FROM AcmeRegistrationBundle:userlist u");
$grp_emails[] = $query->getResult();
$chunk = array_chunk($grp_emails, 10);
$get_chunk_count = count($chunk);
for($i=0;$i<$get_chunk_count;$i++)
{
$count_inside_count = count($chunk[$i]);
for($j=0;$j<=$count_inside_count;$j++)
{
$mails=$chunk[$i][$j];
$message = \Swift_Message::newInstance()
->setSubject('Hello Email')
->setFrom('marketplace#socialnetgate.com')
->setTo($mails)
->setReturnPath('gowtham#ephronsystems.com')
->setBody('Hello World');
$this->get('mailer')->send($message);
return array();
}
}
I think you are overthinking this.
Have you looked at the documentation on how to send batch emails WITHOUT recipients being aware of each other? In your snippet each email contains up to 10 recipients, which may be better then sending all recipients, but still is pretty bad.
Have a look at Sending emails in batch and also at the plugins to make sure you don't reach the limit of emails you are allowed to send in a certain time frame.

Stripping received email headers in PHP

I have a PHP script which has the source of an email. I aim to split the headers into variables $To and $From
The issue comes when trying to split the to and from strings up. What I need the script to do is take
From: John <john#somesite.com>
To: Susy <susy#mysite.com>, Steven <steven#somesite.com>, Mary <mary#mysite.com>
and return only the from address and the to addresses which are on my site. I.e.
$From = 'john#somesite.com';
$To = array('susy#mysite.com', 'mary#mysite.com');
So the code needs to turn a string of email addresses into an array and then filter out the ones from other sites. It's the first part that is proving difficult because of the different ways an email address can be listed in a header.
Edit
As you've now specified that you have the headers as a string but you actually need to parse the addresses from it, there is no need to reinvent the wheel:
imap_rfc822_parse_headersDocs
imap_rfc822_parse_adrlistDocs
These two functions will do the job for you, the last one will give you an array with objects that have the email addresses pre-parsed, so you can easily take decisions based on the host.
It was not specifically clear to me what your actual problem is from your question.
As long as you are concerned about filtering a string containing one email address (cast it to array) or an array containing one or multiple addresses:
To filter the existing array of email-addresses you can use a simple array mapping function that will set any email that is not matching your site's host to FALSE and then filter the array copy Demo:
$addresses = array(
'mary#mysite.com',
'mary#othersite.com',
);
$myhost = 'mysite.com';
$filtered = array_map(function($email) use ($myhost) {
$host = '#'.$myhost;
$is = substr($email, -strlen($host)) === $host;
return $is ? $email : FALSE;
}, $addresses);
$filtered = array_filter($filtered);
print_r($filtered);
This codes makes the assumption that you have the email addresses already gathered. You have not specified how you parse the headers already in your question, so it's actually unknown with which data you are dealing, so I opted to start from the end of your problem. Let us know if you have more information available.
<?php
$k= "......Subject: Write the program any of your favorite language whenever if you feel
you are free
From: Vinay Kumar <vinaykumarjg#gmail.com>
To: msnjsk#gmail.com, mithunsatish#gmail.com,Susy <susy#mysite.com>, Steven <steven#somesite.com>, Mary <mary#mysite.com>
Content-Type: multipart/alternative; boundary=bcaec53964ec5eed2604acd0e09a
--bcaec53964ec5eed2604acd0e09a
Content-Type: text/plain; charset=ISO-8859-1
.......";
if(preg_match('/From:(?P<text>.+)\r\n/', $k, $matches1))
{
if(preg_match('/(?P<from>([a-z0-9])(([-a-z0-9._])*([a-z0-9]))*\#([a-z0-9])' .'(([a-z0-9-])*([a-z0-9]))+' . '(\.([a-z0-9])([-a-z0-9_-])?([a-z0-9])+)+)/', $matches1['text'],$sender ))
{
print_r($sender['from']);
}
}
if(preg_match('/To:(?P<text>.+)\r\n/', $k, $matches2))
{
if(preg_match_all('/(?P<to>([a-z0-9])(([-a-z0-9._])*([a-z0-9]))*\#([a-z0-9])' .
'(([a-z0-9-])*([a-z0-9]))+' . '(\.([a-z0-9])([-a-z0-9_-])?([a-z0-9])+)+)/', $matches2['text'], $reciever))
{
if(isset($reciever['to']))
{
print_r($reciever['to']);
}
}
}
to get the subject:
if(preg_match('/Subject:(?P<subject>.+)\r\n/', $k, $subject))
{
print_r($subject['subject']);
}
preg_match_all("/<([^><]+)/", $headers, $matches);
print_r($matches[1]);
Output:
Array
(
[0] => john#somesite.com
[1] => susy#mysite.com
[2] => steven#somesite.com
[3] => mary#mysite.com
)
The first one is always From email address.
Live demo

Categories