How would I move a message from Gmail Inbox to a label? - php

I'm trying to move messages away from Inbox into Processed label with this code:
$inbox = imap_open($host,$user,$pass) or die('Error: ' . imap_last_error());
if( $emails = imap_search($inbox,'ALL') )
{
foreach($emails as $email_number) {
imap_mail_move($inbox, $email_number, 'Processed') or die('Error');
}
}
imap_expunge($inbox);
imap_close($inbox);
Unfortunately, while the messages get the Processed label, they're still left in Inbox too.
How would I make them go away from Inbox?

Actually... The reason why the emails were left in the inbox was that when imap_mail_move did it's thing, the IDs of all the leftover messages got decremented by one, so when the foreach loop moved to the next message, one message was left behind. This skipping a message repeated for every iteration. That's why it seemed that imap_mail_move was not working.
The solution is to use unique message UIDs instead of potentially repeating IDs:
$inbox = imap_open( $host, $user, $pass );
$emails = imap_search( $inbox, 'ALL', SE_UID );
if( $emails ) {
foreach( $emails as $email_uid ) {
imap_mail_move($inbox, $email_uid, 'processed', CP_UID);
}
}

You have to move the message to the "[Gmail]/All Mail" folder, after you "move it" to a tag folder which is not really a folder as Gmail see's it, just letting Gmail know to add that tag.
So through IMAP:
1) When a message is moved to "[Gmail]/TAG" folder it tells Gmail to add the "TAG" to the message, but does not do any sort of moving of the message.
2) When a message is moved to "[Gmail]/All Mail" folder it tells Gmail to remove it from the Inbox.

#Henno, your diagnosis is correct but you could have simply sorted the emails in descending order.
$inbox = imap_open($host,$user,$pass) or die('Error: ' . imap_last_error());
if( $emails = imap_search($inbox,'ALL') )
{
arsort($emails); //JUST DO ARSORT
foreach($emails as $email_number) {
imap_mail_move($inbox, $email_number, 'Processed') or die('Error');
}
}
imap_expunge($inbox);
imap_close($inbox);

Place this at the end of your file, after you have processed any emails, this will move all found in the inbox, and move them to a folder called 'done'.
$mbox = imap_open('{imap.gmail.com:993/imap/ssl}INBOX', 'emailaddress#gmail.com', 'password');
$countnum = imap_num_msg($mbox);
if($countnum > 0) {
//move the email to our saved folder
$imapresult=imap_mail_move($mbox,'1:'.$countnum,'done');
if($imapresult==false){die(imap_last_error());}
imap_close($mbox,CL_EXPUNGE);
}

use imap_expunge() or imap_close (..., CL_EXPUNGE); but check the return value if true or false if using imap_close (..., CL_EXPUNGE);

Related

PHP - Error in IMAP command STORE: Invalid messageset

I'm using imap with php and I found this error:
Unknown: IMAP protocol error: Error in IMAP command STORE: Invalid messageset (0.001 + 0.000 secs). (errflg=2)
This happens only with some mailboxes (ex. one hosted on misterdomain.eu).
The error occurs at the end of the script, after imap_close().
This is the simple code. If you have any suggest (far to my first problem), is really accepted.
$inbox = imap_open($hostname,$username,$password) or die('Cannot connect: ' . imap_last_error());
$emails = imap_search($inbox,'SINCE "'.date("d-M-y",strtotime("-3 days")).'"',SE_UID);
if($emails) {
rsort($emails);
foreach($emails as $email_number) {
echo "<h1>".$email_number."</h1>";
$overview = imap_fetch_overview($inbox,$email_number, FT_UID);
if($overview[0]->seen)
imap_clearflag_full($inbox,$email_number,"//Seen");
else
imap_clearflag_full($inbox,$email_number,"//Unseen");
$structure = imap_fetchstructure($inbox,$email_number, FT_UID);
if(isset($structure->parts)){
$flattenedParts = flattenParts($structure->parts);
echo "<pre>";
print_r($flattenedParts);
echo "</pre>";
echo "</br>";
getmsg($inbox, $email_number);
echo "<p>".htmlspecialchars_decode(utf8_decode($plainmsg))."</p>";
}else{
$string_email = utf8_decode(imap_body($inbox, $email_number, FT_UID));
$string_email= strip_tags($string_email);
$string_email = html_entity_decode($string_email,ENT_QUOTES);
echo "<p>".$string_email."</p>";
}
}
}
imap_close($inbox);
You are searching and fetching using UIDs, but storing using message sequence numbers. These ways of numbering the messages don't match, so you are sending invalid message numbers to store. Add the approriate ST_UID flag to imap_clearflag_full.
Also, the system flags use backslashes, not forward slashes: '\\Seen'.
\Unseen isn't a defined flag. You probably want to add the \Seen flag instead.

Unset array value if it contains certain word

I want to unset an array value if it contains a certain word in it. What I am doing is that, I am pulling all the emails from gmail inbox and only displaying the emails from the the message body so i can update it in the database. Message body includes few emails as it is for undelivered emails.
When all the emails are displayed, it also displays where the email was sent from which i dont want.
The code is below:
/*=================================== GMAIL ======================================*/
/* connect to gmail */
//$hostname = '{imap.gmail.com:993/imap/ssl}INBOX';
$username = 'username#gmail.com';
$password = 'password';
//$inbox = imap_open($hostname,$username,$password) or die('Cannot connect to Gmail: ' . imap_last_error());
//Which folders or label do you want to access? - Example: INBOX, All Mail, Trash, labelname
//Note: It is case sensitive
$imapmainbox = "INBOX";
//Select messagestatus as ALL or UNSEEN which is the unread email
$messagestatus = "ALL";
//-------------------------------------------------------------------
//Gmail Connection String
$imapaddress = "{imap.gmail.com:993/imap/ssl}";
//Gmail host with folder
$hostname = $imapaddress . $imapmainbox;
//Open the connection
$connection = imap_open($hostname,$username,$password) or die('Cannot connect to Gmail: ' . imap_last_error());
//Grab all the emails inside the inbox
$emails = imap_search($connection,$messagestatus);
//number of emails in the inbox
$totalemails = imap_num_msg($connection);
echo "Total Emails: " . $totalemails . "<br>";
if($emails) {
//sort emails by newest first
rsort($emails);
//loop through every email int he inbox
foreach($emails as $email_number) {
//grab the overview and message
$header = imap_fetch_overview($connection,$email_number,0);
//Because attachments can be problematic this logic will default to skipping the attachments
$message = imap_fetchbody($connection,$email_number,1.1);
if ($message == "") { // no attachments is the usual cause of this
$message = imap_fetchbody($connection, $email_number, 1);
}
//split the header array into variables
$status = ($header[0]->seen ? 'read' : 'unread');
$subject = $header[0]->subject;
$from = $header[0]->from;
$date = $header[0]->date;
preg_match_all("/[\._a-zA-Z0-9-]+#[\._a-zA-Z0-9-]+/i", $message, $matches);
//$matches = array_unique($matches);
// remove some of the emails which i dont want to include
$matches[0] = remove(remove(remove(array_unique($matches[0]) , 'info#example.co.uk') , 'no-reply#example.co.uk'), '131669395#infong353.kundenserver.de') ;
foreach($matches[0] as $email) {
//echo $email . "<br>";
echo '
<div class="alert alert-primary" role="alert">
'.$email.' in <b>'.category_name(cat_id_from_email($email)).'</b> group <br><br>
<a href="index.php?p=gmail&undelivered='.$email.'" class="btn btn-info nounderline" >Undelivered</a>
<a href="index.php?p=gmail&unsubscribers='.$email.'" class="btn btn-warning nounderline" >Unsubscribers</a>
<a href="index.php?p=gmail&delete='.$email.'" class="btn btn-danger nounderline" >Delete</a>
</div>
';
}
}
}
// close the connection
imap_close($connection);
I also want to remove any email address with certain domain names. For example:
if any of the email is xxxxx#example2.com, i want to unset it. So basically any email address with example2.com.
Could anyone help me with this.
I can see two ways; remove from the array before the display loop:
$matches[0] = preg_grep('/example2\.com/', $matches[0], PREG_GREP_INVERT);
Or check in the display loop and just don't display:
foreach($matches[0] as $email) {
if(strpos($email, 'example2.com') !== false) { continue; }
//echo your stuff
}
Depends on if you want to keep the array intact but just not display or modify the array for later use.

issues with IMAP functions php

I am facing some problem with imap function. Basically What I need to do is to read unseen mails. There will be a url in all the mails, i should fetch that URL and store.
$inbox = imap_open($hostname,$username,$password);
if($inbox)//if **1
{
/* grab emails */
$emails = imap_search($inbox,'UNSEEN');
/* if emails are returned, cycle through each... */
if($emails) //if **2
{
/* put the newest emails on top */
rsort($emails);
/* for every email... */
$varients=array("1","1.1","1.2","2");
foreach($emails as $email_number) //for loop **1
{
$ids [] = $email_number;
foreach($varients as $cur_varient) //for loop **2
{
echo "\n\nstarting with imap function number ". $cur_varient."\n\n";
$overview = imap_fetch_overview($inbox,$email_number,0);//all varients of like subject, date etc.
$from = addslashes(trim($overview[0]->from));
$inboxed_time = addslashes(trim(strtotime($overview[0]->date)));
$message = (imap_body($inbox,$email_number,$cur_varient));
print addslashes(trim($overview[0]->subject));break;
preg_match_all('#\bhttp?://[^,\s()<>]+(?:\([\w\d]+\)|([^,[:punct:]\s]|/))#', $message, $match);
$link_matched = $match[0];
$input = 'unsubscribe.php';
$linkexists = false;
foreach($link_matched as $curlink)
{
if(stripos($curlink, $input) !== false)
{
$linkexists = true;
$unsublink = $curlink;
$unsublink = str_replace('href="', '', $unsublink);
$unsublink = str_replace('"', '', $unsublink);
break;
}
}
if(isset($unsublink))
{
$unsublink = addslashes(trim(($unsublink)));
$thread = 1;
$time = date("Y-m-d H:i:s");
$iQry = " INSERT INTO `SPAMS`.url_queue VALUES(";
$iQry .= " 'default','".$unsublink."','".$thread."','";
$iQry .= "".$from."','".$inboxed_time."',UNIX_TIMESTAMP('".$time."'))";
//mysql_query($iQry);
print $iQry;
}
}//closing for loop **2
}//closing for loop **1
} //closing if **2
// Setting flag from un-seen email to seen on emails ID.
if(isset($ids))
{
imap_setflag_full($inbox,implode(",", $ids), "\\Seen \\Flagged"); //IMPORTANT
}
// colse the connection
imap_expunge($inbox);
imap_close($inbox);
}//closing if **1
I have used all different varients of imap to make sure it will read different types of mails. Now issue is, sometime the URL matched is broken. Only half URL will be fetched(I printed the entire message, saw that half URL is coming to next line). The other issue is, sometimes, the body fetched will not be the one which the current mail contains. It fetched some other mail content.
I am puzzled what to do, so putting my entire code, please help.
You will have to use a regex modifiers to match multiline texts too, or you can strip newlines and such from the body of the emails.
preg_match("/{pattern}/mi",'Testing');
//m for multiline matches and i for case-insensitive matches
Your second issue is a bit different than you'd think, emails have multiple bodies, one for simple texts, one for html, some for attachments (and their order is different in apple products).
https://www.electrictoolbox.com/php-imap-message-parts/
You are probably facing the issue of grabbing the wrong one. My recommendation would be to fetch all of the email bodies, like this:
$overview = imap_fetch_overview($this->connection, $email_number, 0);
$structure = imap_fetchstructure($this->connection, $email_number);
$message = "";
//$parts = [1, 1.1, 1.2, 2];
if (!$structure->parts)//simple email
$message .= "Simple: ". imap_fetchbody($this->connection, $email_number, 0). "<br/>";
else {
foreach ($structure->parts as $partNumber=>$part){
if ($partNumber != 0)
$message .= "Part ".$partNumber.": ". imap_fetchbody($this->connection, $email_number, $partNumber)."<br/>";
}
}

Find E-Mails with an exact e-mail address on an IMAP server using PHP

I want to connect to an IMAP-server and find all E-Mails that were sent to abc#server.tld. I tried:
$mbox = imap_open("{imap.server.tld/norsh}", "imap#server.tld", "5ecure3");
$result = imap_search($mbox, "TO \"abc#server.tld\"", SE_UID);
but this also listed e-Mails that were sent e.g. to 123abc#server.tld. Is it somehow possible to do a search for exact matches?
Short answer: you can't. I didn't find anything in RFC 2060 - Internet Message Access Protocol - Version 4rev1 saying that it can be done.
But, there is a workaround. First fetch all emails that contain abc#server.tld, then iterate through the results and select only the exact matches.
$searchEmail = "abc#server.tld";
$emails = imap_search($mbox, "TO $searchEmail");
$exactMatches = array();
foreach ($emails as $email) {
// get email headers
$info = imap_headerinfo($mbox, $email);
// fetch all emails in the TO: header
$toAddresses = array();
foreach ($info->to as $to) {
$toAddresses[] = $to->mailbox . '#' . $to->host;
}
// is there a match?
if (in_array($searchEmail, $toAddresses)) {
$exactMatches[] = $email;
}
}
Now you have all emails matching abc#server.tld in $exactMatches

php imap check if email has attachment

I'm trying to build a small webmail app. When I read all the emails in inbox I want to show for each mail if it has attachments. This works, but the problem is that it takes to long to do that, about 0.5 secs for 1Mb email attach. Multiply that with all emails in inbox that have big attach files :|
My question is: How to check if an email has attach withouth loading the whole email ? Is that possible ?
Bellow is the code I'm using now:
function existAttachment($part)
{
if (isset($part->parts))
{
foreach ($part->parts as $partOfPart)
{
$this->existAttachment($partOfPart);
}
}
else
{
if (isset($part->disposition))
{
if ($part->disposition == 'attachment')
{
echo '<p>' . $part->dparameters[0]->value . '</p>';
// here you can create a link to the file whose name is $part->dparameters[0]->value to download it
return true;
}
}
}
return false;
}
function hasAttachments($msgno)
{
$struct = imap_fetchstructure($this->_connection,$msgno,FT_UID);
$existAttachments = $this->existAttachment($struct);
return $existAttachments;
}
To check whether the email has attachment, use $structure->parts[0]->parts.
$inbox = imap_open($mailserver,$username, $password, null, 1, ['DISABLE_AUTHENTICATOR' => 'PLAIN']) or die(var_dump(imap_errors()));
$unreadEmails = imap_search($inbox, 'UNSEEN');
$email_number = $unreadEmails[0];
$structure = imap_fetchstructure($inbox, $email_number);
if(isset($structure->parts[0]->parts))
{
// has attachment
}else{
// no attachment
}
imap_fetchstructure does fetch the whole email content in order to analyze it. Sadly there is no other way to check for attachment.
Maybe you can use the message size info from imap_headerinfo to get a prediction if the message will have attachments.
Another way is to fetch the emails in an regular interval in the background and store them with their content and UID for later lookup in a database. You need to do that later anyway when you want so search for specific messages. (You do not want to scan the while imap account when searching for "dinner")

Categories