Sending emails php imap. Extracting emails from mail

Some web applications may require a specific email for the user. In this case, we can write our own own code SquirrelMail or Roundcube email. Regardless of which you choose, knowing how to work with IMAP mail will be helpful.

How to work with IMAP in PHP is described on two pages. On the first page, the necessary functions to connect to mail servers and reading messages. On the second page we will talk about working with email, for example, deleting messages, downloading applications, etc…

To demonstrate functionality, will use code examples that can be used to run your own mail client scripts.

URL parameters to call the required functions:

  • func - the type of function required (for example: read email, view mailbox, delete message)
  • folder - folder name mailbox to connect (for example: Inbox, Sent, Spam)
  • uid - unique email identifier

Parameters can be retrieved with $_GET and switch can be used to invoke the appropriate actions.

Connecting to an IMAP server

To establish a connection to the IMAP server, we use the imap_connect() function, as shown below:

The message path, username and password are the required parameters to connect to the server. You can learn about additional options in the manual.

The mailbox path is a string that identifies the server and the port in curly brackets specifies the name of the desired mail folder.

Here are some lines for the folder inbox postal services:

  • Gmail (imap.gmail.com: 993/imap/ssl) INBOX
  • Yahoo (imap.mail.yahoo.com: 993/imap/ssl) INBOX
  • AOL (imap.aol.com: 993/imap/ssl) INBOX

Some servers do not have SSL enabled, in which case you will need to omit "SSL" from the line. Other servers may use their own certificates, in which you must include "NOVALIDATE-CERT".

With the mail server connection open, we can now take a look at the functions used for the following activities:

  • Display a mailbox folder list in your email account
  • Display a list of email messages in a folder
  • View the contents of an author's email

Email folders

inbox , Sent , Trash and Spam- These folders are visible in almost every email account, and users can often create their own folders. In order to view messages in these folders, we need to change our connection string. For example, apply "INBOX" (Inbox) in the path string earlier. If you need to connect to your spam folder, use something like "Spam" instead. But while it may be listed as Spam to an email account, when viewed through an email client, the real name of the folders in the background may be different. We can list all existing folders in an account using imap_list() .

"; foreach ($folders as $folder) ( $folder = str_replace("(imap.gmail..php?folder=" . $folder . "&func=view">" . $folder . ""; ) echo "";

We must pass the connection handle received from imap_open() as the initial parameter to imap_list() . We also have to pass the path (without the folder, for example "INBOX"). As the third parameter, request all available folders.

Message Notification

Each folder contains a list of available email messages, so let's see how we can create a list of messages in our mailbox.

First you need to get the number of messages available with imap_num_msg() . We can then use the imap_header() function to get information for the header of each message.

Let's say if we want the last 20 emails:

($numMessages - 20); $i--) ( $header = imap_header($imap, $i); $fromInfo = $header->from; $replyInfo = $header->reply_to; $details = array("fromAddr" => (isset($ fromInfo->mailbox) && isset($fromInfo->host)) ?$fromInfo->mailbox ."@" .$fromInfo->host: "", "fromName" => (isset($fromInfo->personal)) ?$fromInfo->personal: "", "replyAddr" => (isset($replyInfo->mailbox) && isset($replyInfo->host)) ?$replyInfo->mailbox . "@" . $replyInfo->host : "", "replyName" => (isset($replyTo->personal)) ? $replyto->personal: "", "subject" => (isset($header->subject)) ? $header->subject : "", "udate" => (isset($header->udate)) ? $header->udate: ""); $uid = imap_uid($imap, $i); echo "

    "; echo "
  • From:" . $details["fromName"]; echo " " . $details["fromAddr"] . "
  • "; echo "
  • Subject:" .$details["subject"] ..php?folder=" . $folder. "&id=" . $uid ..php?folder=" . $folder . "&uid=" . $uid . "&func=delete">Delete
  • "; echo "
"; }

$Imap connections must be opened in the correct folder. We can then loop through the last 20 emails using the number of messages received by imap_num_msg() . The link and email number is given to imap_header() to get header information, which can then be mistaken for interesting information from the sender, email address, name, subject, etc.

Please note that the email number from the total number of messages is not a unique identifier for the message. If you have 100 emails in your mailbox, then the last number will be 100, the previous one will be 99, and so on. If you delete message number 100 and then receive a new message, its number will be 100.

To proceed with the following steps, you need to get a unique identifier for the email. Each email has a unique id called UID which we can get by providing email, the function for number imap_uid() UID is unique and won't change over time.

Viewing the content of a message

Reading email is not really as easy as the previous tasks, so you need to use the Mail Classes developed by Mitul Koradia. The class adapted the following three functions for our example below:

encoding) ( case 3: return imap_base64($text); case 4: return imap_qprint($text); default: return $text; ) ) // multipart if ($structure->type == 1) ( foreach ($structure ->parts as $index => $subStruct) ( $prefix = ""; if ($partNumber) ( $prefix = $partNumber . "."; ) $data = get_part($imap, $uid, $mimetype, $ subStruct, $prefix .($index + 1)); if ($data) ( return $data; ) ) ) ) return false; ) function get_mime_type($structure) ( $primaryMimetype = array("TEXT", "MULTIPART", "MESSAGE", "APPLICATION", "AUDIO", "IMAGE", "VIDEO", "OTHER"); if ($structure ->subtype) ( return $primaryMimetype[(int)$structure->type] . "/" . $structure->subtype; ) return "TEXT/PLAIN"; )

The GetBody() function gets the content of the email by passing in its UID and IMAP connection. Inside the function, we call the get_part() function with the content type as text / HTML. Plain text email messages are much easier to read. So we first try to find the HTML content inside the email.

We then read the email structure using the imap_fetchstructure() function. We have changed the library functions to use the UID instead of passing the FT_UID all the time.

Then we get the MIME type of the email message using the get_mime_type() function. There are eight MIME types returned by this function as integers:

  • 0-TEXT
  • 1 - MULTIPART
  • 2 - MESSAGE
  • 3 – APPLICATION
  • 4-AUDIO
  • 5 - IMAGE
  • 6 - VIDEO
  • 7-OTHER

We turn the return into the actual MIME type of the string using arrays of MIME types.

Compound messages can have a large number of subtypes, so we recursively go through all the subtypes using the number part and fetch the email using imap_fetchBody() when the correct one is found using the mime-type.

Then, we use the appropriate decode function according to the message type encoding and return the content. Full list of available encoding types:

  • 0-7bit
  • 1-8bit
  • 2-BINARY
  • 3-BASE64
  • 4 - QUOTED-PRINTABLE
  • 5-OTHER

Conclusion

We've finished reviewing the basics of connecting to an IMAP server, listing messages inside available folders, and finished reading the contents of an email message. On the next page of the post, we will talk about the functions that can be used to implement additional functionality of the mail client, such as deleting messages and processing attachments.

One possible use of the imap functions is to create a mail daemon that will manage the subscription and unsubscribing of users from your mailing list. To accomplish this task, two methods are usually used in mailing lists. The first assumes that the user must go to a certain page and confirm their actions, the second requires sending an email. The second one also requires that the script handler be run regularly by cron daemon?om. Because of this, it is not as popular as the first method.

But, as you can see, the most serious mailings use the second method. Therefore, if you have the opportunity to use crond, use it.

Actually, to understand the functions is not so difficult. A person who previously worked for PHP will easily understand how to work with them. Some difficulties may arise with parsing the headers of letters that the script will process.

The algorithm of the script itself is easy to come up with. The daemon establishes a connection to the mail server and checks for messages on it. If there are no letters, the script stops.
If there are letters, then the headings of the first letter are parsed. The from and subject fields are looked up. If the subject field contains one of the two valid header options (subscription or unsubscribe), then the record corresponding to the value of the from field either becomes active (confirmed) or removed from the table. In both cases, a corresponding notification about the script's actions is sent to the address specified in the from field. The email is then marked for deletion. If the subject does not contain valid subjects, an error notification is sent and the message is also marked for deletion. The script then moves on to the next email.
Having finished parsing all the letters, he clears the box.

I won't bore the reader with flowcharts, so let's get straight to the point. The imap_open function is used to open the mailbox. Because PHP supports multiple protocols, you must explicitly specify which protocol is used to work with the box. In our case, this is POP3 on port 110 (standard). We assign the result of the script execution to the $my_box variable.


Later you will see that this variable will be used in almost all imap functions. Next, check the mailbox for letters. The check is performed by the imap_num_msg function.

$n = imap_num_msg ($my_box );

As a result, the variable $n will contain the number of letters in the mailbox. This number can be either greater than zero or equal to it (if the box is empty).
If there are letters, then in the while loop we parse the letters, sequentially increasing the number of the letter by one. Note that the first letter in the box will have the number 0, as will the first element of the array.
To increase the number of the letter, we assign the value 0 to the variable $m, and then, in the conditions of the loop, increase it by one $m++.

To parse the headers we are interested in, two functions are enough: imap_header and imap_fetch_overview. To perform each of them, in addition to the box, you need to indicate the number of the letter. In our case, inside the loop, it will be equal to the $m variable.

Imap_header returns as a result of execution an object containing comprehensive information about the message header. Among other things, this object contains an array from which contains four values. These are personal, adl, mailbox and host. Of these, we are only interested in mailbox and host. Substituting them, we will get the address from which the letter was sent.


$h = $h -> from ;
a
foreach ($h as $k => $v ) (
$mailbox = $v -> mailbox ;
$host = $v -> host ;
$personal = $v -> personal ;
$email = $mailbox . ? @ ¬ . $ host ;

imap_fetch_overview - will let us know the subject of the email. For the same purposes, imap_header could also be used, but for a number of reasons this may sometimes not work. From the array that this function returns, we only need the subject field


foreach ($s as $k => $v ) (
$subj = $v -> subject ;
}

Our next steps are to extract the email from the database, and if it is there, mark the entire line with this entry as “verified”, or delete it. Suppose that after filling out the mailing form on the site, the subscriber is assigned a status of 0, and after confirming the subscription, it changes to 1.

if ($subj == "SUBSCRIBE" ) (
mysql_query( "UPDATE subscribe SET stat=1 WHERE email=$my_email");

}
mysql_query( "DELETE FROM subscribe WHERE email = $my_email");
$del = imap_delete ($my_box , $m );
}
else(
$del = imap_delete ($my_box , $m );
}

as mentioned above, after performing all the actions, the script clears the box.


This simplest program is just a demonstration of the fact that you can write in PHP not only dynamically changing sites, but also services that are not visible to the user at all. Of course, in terms of shell scripting, PHP is not applicable, unlike its competitor Perl, but nevertheless ...

Listing of the entire program except for the database connection parameters (db.php):

include "db.php" ;
$my_box = imap_open ("(you.pop.host/pop3:110)" , "login" , "password" );
$n = imap_num_msg ($my_box );
$m = 0 ;
$add_text = "

Thank you for confirming your subscription " ;
$add_sbj = "You added!" ;
$del_text = "

Sorry, but this mailbox is in use.
for distribution administration only" ;
$err_sbj = "Error" ;
$headers = From: Subscribe Robot

Xmailer: PHP4

Content-type: text/plain; charset=UTF-8
" ;
if($n != 0 ) (
while($m++< $n ) {
$h = imap_header ($my_box , $m );
$s = imap_fetch_overview ($my_box , $m );
$h = $h -> from ;
foreach ($h as $k => $v ) (
$mailbox = $v -> mailbox ;
$host = $v -> host ;
$personal = $v -> personal ;
$email = $mailbox . "@" . $ host ;
$my_email = mysql_escape_string($email );
}
foreach ($s as $k => $v ) (
$subj = $v -> subject ;
}
if ($subj == "SUBSCRIBE" ) (
mysql_query( "UPDATE table SET stat=1 WHERE email=$my_email");
//print mysql_error();
$del = imap_delete ($my_box , $m );
mail ($email , $add_sbj , $add_text , $headers );
}
elseif ($subj == "UNSUBSCRIBE" ) (
mysql_query( "DELETE FROM table WHERE email = $my_email");
$del = imap_delete ($my_box , $m );
mail ($email , $del_sbj , $del_text , $headers );
}
else(
$del = imap_delete ($open_box , $m );
mail ($email , $err_sbj , $err_text , $headers );
}
}
$clear = imap_expunge($my_box);
}
?>

One possible use of the imap functions is to create a mail daemon that will manage the subscription and unsubscribing of users from your mailing list. To accomplish this task, two methods are usually used in mailing lists. The first assumes that the user must go to a certain page and confirm their actions, the second requires sending an email. The second one also requires the script handler to be run regularly by cron daemon-om. Because of this, it is not as popular as the first method.

But, as you can see, the most serious mailings use the second method. So if you have the option of using cron, use it.

Actually, to understand the functions is not difficult. Some difficulties may arise with parsing the headers of letters that the script will process.

The algorithm of the script itself is easy to come up with. The daemon establishes a connection to the mail server and checks for messages on it. If there are no letters, the script stops. If there are letters, then the headings of the first letter are parsed. The from and subject fields are looked up. If the subject field contains one of the two valid header options (subscription or unsubscribe), then the record corresponding to the value of the from field either becomes active (confirmed) or removed from the table. In both cases, a corresponding notification about the script's actions is sent to the address specified in the from field. The email is then marked for deletion. If the subject does not contain valid subjects, an error notification is sent and the message is also marked for deletion. The script then moves on to the next email. Having finished parsing all the letters, he clears the box.

The imap_open function is used to open the mailbox. Since PHP supports multiple protocols, it is necessary to explicitly specify which protocol is used to work with the box. In our case, this is pop3 on port 110 (standard). We assign the result of the script execution to the $my_box variable.

$my_box = imap_open("(you.pop.host/pop3:110)", "login", "password");

Later you will see that this variable will be used in almost all imap functions. Next, check the mailbox for letters. The check is performed by the imap_num_msg function.

$n = imap_num_msg($my_box);

As a result, the variable $n will contain the number of letters in the mailbox. This number can be either greater than zero or equal to it (if the box is empty). If there are letters, then in the while loop we parse the letters, sequentially increasing the number of the letter by one. Note that the first letter in the box will have the number 0, as will the first element of the array. To increase the number of the letter, we assign the value 0 to the variable $m, and then, in the conditions of the loop, increase it by one $m++.

Two functions are enough to parse the headers we are interested in: imap_header and imap_fetch_overview . To perform each of them, in addition to the box, you need to indicate the number of the letter. In our case, inside the loop, it will be equal to the $m variable.

imap_header returns an object containing exhaustive information about the message header as a result of execution. Among other things, this object contains an array from which contains four values. These are personal, adl, mailbox and host. Of these, we are only interested in mailbox and host. Substituting them, we will get the address from which the letter was sent.

$h = imap_header($my_box, $m); $h = $h->from; foreach ($h as $k => $v) ( $mailbox = $v->mailbox; $host = $v->host; $personal = $v->personal; $email = $mailbox . "@" . $host;

imap_fetch_overview - will allow us to find out the subject of the letter. For the same purposes, imap_header could also be used, but for a number of reasons this may sometimes not work. From the array that this function returns, we only need the subject field

$s = imap_fetch_overview($my_box, $m); foreach ($s as $k => $v) $subj = $v->subject;

Our further actions come down to extracting the email from the database, and if it is there, mark the entire line with this entry as, or delete it. Suppose that after filling out the mailing form on the site, the subscriber is assigned a status of 0, and after confirming the subscription, it changes to 1.

If ($subj == "subscribe") ( mysql_query("update subscribe set stat=1 where email=$my_email"); $del = imap_delete($my_box, $m); mail($email, $add_sbj, $add_text , $headers); ) else if ($subj == "unsubscribe") ( mysql_query("delete from subscribe where email = $my_email"); $del = imap_delete($my_box, $m); mail($email, $ del_sbj, $del_text, $headers); ) else ( $del = imap_delete($my_box, $m); mail($email, $err_sbj, $err_text, $headers); ) after all actions, the script clears the box. $clear = imap_expunge($my_box);

This simplest program is just a demonstration that in PHP you can write not only dynamically changing sites, but also services that are not visible to the user at all.

Listing of the entire program except for the database connection parameters:

include "config.php"; // database connection $my_box = imap_open("(you.pop.host/pop3:110)", "login", "password"); $n = imap_num_msg($my_box); $m = 0; $add_text = "Thank you for confirming your subscription"; $add_sbj = "you added!"; $del_text = "You have been removed from the mailing list. "; $del_sbj = "delete from list"; $err_text = "Sorry, but this mailbox is only used for mailing list administration"; $err_sbj = "error"; $headers = "from: subscribe robot x-mailer: php4 content-type: text/plain; charset=windows-1251 "; if($n != 0) ( while($m++< $n) { $h = imap_header($my_box, $m); $s = imap_fetch_overview($my_box, $m); $h = $h->from; foreach ($h as $k =>$v) ( $mailbox = $v->mailbox; $host = $v->host; $personal = $v->personal; $email = $mailbox . "@" . $host; $my_email = mysql_escape_string($email); ) foreach ($s as $k => $v) $subj = $v->subject; if ($subj == "subscribe") ( mysql_query("update table set stat=1 where email=$my_email"); //print mysql_error(); $del = imap_delete($my_box, $m); mail($ email, $add_sbj, $add_text, $headers); ) else if ($subj == "unsubscribe") ( mysql_query("delete from table where email = $my_email"); $del = imap_delete($my_box, $m) ; mail($email, $del_sbj, $del_text, $headers); ) else ( $del = imap_delete($open_box, $m); mail($email, $err_sbj, $err_text, $headers); ) ) $clear = imap_expunge($my_box); )

The listing is missing some details, such as a possible conversion from win to koi, checking the sender's mailbox, etc. These are already functional excesses that everyone can add as needed.

The other day I got a task to write a small PHP module that would allow me to work with incoming mail. Googling a little, I saw that one of the protocols suits me for this task POP3 and IMAP.
But the choice was obvious that I would use IMAP, as it is more functional and more modern than the POP3 protocol.

Now I had to quickly figure out how to work with the IMAP protocols, how to receive letters from the Yandex/Google mail server.

For more convenient work, I chose the library PhpImap, as it can quickly and easily implement all the tasks I need.

Connecting to a mail server.

Now that we have decided on the choice of protocol and the choice of library, we will try to connect to the mail server.

For PHP to fully work with the IMAP protocol, you need to connect the extension php_imap.dll/imap.so in the php.ini file.

To begin with, let's try to connect to Yandex mail, since I had the least problems with it.

//Include libraries include("/phpImap/Mailbox.php"); include("/phpImap/IncomingMail.php"); //For convenience, let's create constants for connecting to the mail server. define("MAIL_IMAP_SERVER", "imap.yandex.ru"); define("MAIL_IMAP_SERVER_PORT", 993); define("MAIL_IMAP_LOGIN", " "); define("MAIL_IMAP_PASS", "example_pass"); define("MAIL_IMAP_PATH", "(".MAIL_IMAP_SERVER.":".MAIL_IMAP_SERVER_PORT."/imap/ssl)INBOX"); $mailbox = new PhpImap\Mailbox(MAIL_IMAP_PATH, MAIL_IMAP_LOGIN, MAIL_IMAP_PASS, __DIR__); try ( $mailbox->getImapStream(); ) catch (Exception $e) ( die($e->getMessage()); )

How do we see the class constructor Mailbox takes the following arguments:

  • MAIL_IMAP_PATH- Contains the server address (MAIL_IMAP_SERVER), connection port (MAIL_IMAP_SERVER_PORT), connection type (imap) and indicate that the connection will be encrypted (ssl). After the curly brackets, we specify the folder to which we will connect, in this case, to incoming messages (INBOX).
  • MAIL_IMAP_LOGIN- The mailbox to which we will connect.
  • MAIL_IMAP_PASS- Password (most often this is the password from the mailbox).
  • __DIR__- This is the path to the folder where attachments and mail messages will be saved.

After that, we will check if our connection was created through the method getImapStream() if for some reason the connection is not created, then the application throws exceptions with the reason for the unsuccessful connection.

It is important to take into account the fact that in the Yandex mail settings you may have the ability to connect using the IMAP protocol disabled.

Now let's compare the connection to Gmail mail.

Define("MAIL_IMAP_SERVER", "imap.gmail.com"); define("MAIL_IMAP_SERVER_PORT", 993); define("MAIL_IMAP_LOGIN", " "); define("MAIL_IMAP_PASS", "example_pass"); define("MAIL_IMAP_PATH", "(".MAIL_IMAP_SERVER.":".MAIL_IMAP_SERVER_PORT."/imap/ssl)INBOX");

As we can see, it practically does not differ from the previous connection, but most likely you will get an exception when connecting to the server.
This problem is related to the fact that Gmail IMAP protocol disabled by default. You can enable it in the settings in the Forwarding and POP/IMAP tab in the IMAP Access ⇒ Enable IMAP option.

After we have enabled the IMAP protocol, we need to create app password. In order to be able to create it, we need to make a two-factor authorization for this profile. Then you can start creating it. When we create a new password for the application, we will need to enter it into the MAIL_IMAP_PASS constant to connect to the server.

Please note that when creating an application password, you may not be able to connect to the server, this is due to the fact that this password has not yet been fully applied to the Gmail service, this usually takes 5-60 minutes.

Data sampling

After a successful connection, we can execute a request to receive stream messages from the server. For this we will use the method searchMailBox(string $criteria) which is essentially a method wrapper imap_search. It is important to understand here that the $criteria argument is a certain criterion for finding the messages we need, the method itself returns the identifiers of the elements that will later be useful to us to obtain detailed information about the mail message.

$mailsIds = $mailbox->searchMailBox("ALL");

As you may have guessed, here we receive all messages.
And now let's try to deal with other equally important search criteria:

//All messages for 3 days. $mailsIds = $mailbox->searchMailBox("SINCE "".date("d-M-Y",strtotime("-3 day"))."""); //Unread messages for 3 days. $mailsIds = $mailbox->searchMailBox("UNSEEN SINCE "".date("d-M-Y",strtotime("-3 day"))."""); //Search for messages with this match in the TEXT header. $mailsIds = $mailbox->searchMailBox("TEXT "Newsletter""); //Search for messages with this match in the BODY header. $mailsIds = $mailbox->searchMailBox("BODY "Information Message""); //Search by sender's email. $mailsIds = $mailbox->searchMailBox("FROM " ""); //Get messages by SUBJECT header $mailsIds = $mailbox->searchMailBox("SUBJECT "Updates for your phone have been released"");

This example captures the basics of using search criteria well.

Receiving the information

Now that we have an array of message IDs, we are ready to process it:

//Get the ID of the last message from the array. $id = end($mailsIds); //Get an instance of the IncomingMail class object that contains information about the message. $mail = $mailbox->getMail($id); //Get the files attached to this message if it exists. $mail->getAttachments(); //Display messages. echo $mail->textHtml;

So we received messages from our letter and its attachments without any problems.

Additional features.

This library also contains a number of useful methods for more convenient work with mail messages:

We save messages by its id.

$mailbox->saveMail($id,$id.".eml");

Set messages as unread by their id.

$mailbox->markMailAsUnread($id);

Set messages as read by their id.

$mailbox->markMailAsRead($id);

We set a mark on the message by its id.

$mailbox->markMailAsImportant($id);

Delete messages by their id.


E-mail is the most important tool for exchanging information, and if you use it for work, you must have come across a situation: a letter arrives in the mail that contains the data necessary for processing by a script. We will talk about Yandex mail - in this article I will share with you, dear readers, the experience of how to get letters out of the mailbox, we will also analyze the option when the letter has an attached file - how to detect it and eventually download it for further manipulations on it .

I myself faced this task a long time ago, and then, having little experience with Yandex mail programs, I spent a lot of time and nerves to achieve the desired result. My first mistake was that, like many web developers, I began to intensively search for similar examples on the web, but did not use the Yandex help (help) itself. Yes, there is useful information there, although it is very small, but it is important enough for writing this kind of script (more on that below). At that time, it was necessary to write a script, the essence of which was: a letter with a price list of goods in xls format was sent to Yandex mail of the customer once a day, it had to be processed (parsed and compared with data from the database of the online store and, depending on the result, something then update somewhere, disable or enable).

And the first thing we will do before writing the script is to outline our action plan, which will consist of nine points:

  1. Set up mail to gain access through mail protocols;
  2. Let's outline the very structure of the PHP application and decide on the file encoding;
  3. Let's get acquainted with the IMAP mail protocol and its capabilities;
  4. Connect to Yandex mail through the account login and password and track errors at this stage;
  5. Let's process the header of the letter;
  6. We receive and process the body of the letter;
  7. Get and save attached files;
  8. We visualize the work done;
  9. Let's draw conclusions.

The topic is quite voluminous, but I will try to state everything as compactly and clearly as possible. Perhaps let's get started.

Mail setup

Go to your mail and go to the settings, as shown in the screenshot below:



Now we got into the settings for mail through the IMAP and POP3 protocols:


Here, many will see a picture as in the image above, but I have come across, and more than once, when access is disabled. Therefore, if your settings are different, check the boxes as in the screenshot, the main thing for us is to allow access through the IMAP protocol.

Application structure and its encoding

In this example, we will not come up with a complex application structure, since it is not needed, but we will add only what is necessary (I work in the Sublime Text editor):


  • tmp - folder where we will upload attachments from the letter, if any;
  • .htaccess - server side setting if you have an apache server;
  • functions.php - we will add our functions here;
  • main.css - style file;
  • index.php - application entry point;

We will use UTF-8 encoding and therefore immediately fill the .htaccess file with the following lines:

AddDefaultCharset utf-8 AddCharset utf-8 * CharsetSourceEnc utf-8 CharsetDefault utf-8

IMAP protocol

Returning to the first point, it is clear that you can also work with Yandex mail through the POP3 protocol. So why exactly IMAP? Of the two, IMAP is the newer and alternative to POP3, so it has a number of advantages (which can be found on wikipedia), but in our case, the choice was only influenced by the fact that it is newer. Personally, I don’t see much difference what to use for the specific task of receiving a letter. If for some reason you need to use the POP3 protocol, then all the functions that apply to IMAP will work for it.

Connecting to Yandex mail using the IMAP protocol

In order to connect to the mail, we need to know three parameters: mail login, its password and mail server address. If there are no problems with two parameters, then the second one can be found in Yandex. I wrote about this (the problem that I had) above and on the network a lot of examples where the third parameter is not specified correctly and, imagine that errors occur already at the connection stage - this is at least unpleasant. I will not beat around the bush and immediately give a direct link to the Yandex page - mail program settings. Here is what we need to connect:


Now you can go directly to the code itself:

Header("Content-Type: text/html; charset=utf-8"); error_reporting(0); require_once("functions.php"); $mail_login = "yandex_mail"; $mail_password = "mail_password"; $mail_imap = "(imap.yandex.ru:993/imap/ssl)"; // List of file types to consider $mail_filetypes = array("MSWORD"); $connection = imap_open($mail_imap, $mail_login, $mail_password); if(!$connection)( echo("Mail connection failed - ".$mail_login); exit; )else( $msg_num = imap_num_msg($connection); $mails_data = array(); for($i = 1; $ i<= $msg_num; $i++){ /* Работать с каждым письмом из IMAP-потока будем тут */ } } imap_close($connection);

First of all, we additionally specify the UTF-8 encoding using the header and turn off the display of errors. We connect the functions.php file and specify the settings that were discussed above. In the $mail_filetypes array, we specify the file formats that we need. It was decided to do so in order to weed out unnecessary garbage and receive specific files. The connection to the mail is made using the imap_open() function, which returns an IMAP stream on success, and false on failure (but if you enable error display, this is not the case). We finish working with streams using the imap_close () function, passing it a connection indicator. Between these two functions there is an ordinary conditional operator.

If the connection is successful, use imap_num_msg() to find out the number of letters in the mail and add an array into which we will put all the data we need from the stream. This is followed by a cycle in which each letter will be processed by its number (numbering starts from 1) separately.

Email header processing

To get the email header, you need to use the imap_header() function, the second parameter of which is the email number:

// Email header $msg_header = imap_header($connection, $i);

At this stage, we will receive an object from which we will pull the data we need, saving it to the $mails_data array. Here is an example of one of the letters:

This screenshot shows that all data is duplicated, but this does not play a special role, we pull what is more convenient. Much more important is the encoding of the subject line. It can be anything and this moment must be controlled. The same situation is with the body of the letter and with the attached files.

$mails_data[$i]["time"] = time($msg_header->MailDate); $mails_data[$i]["date"] = $msg_header->MailDate; foreach($msg_header->to as $data)( $mails_data[$i]["to"] = $data->mailbox."@".$data->host; ) foreach($msg_header->from as $ data)( $mails_data[$i]["from"] = $data->mailbox."@".$data->host; )

We store in our array: a timestamp, the date of receipt of the letter, the email of the recipient and the sender and proceed to receive the subject of the letter. To do this, we first need to add three functions to the functions.php file:

Function check_utf8($charset)( if(strtolower($charset) != "utf-8")( return false; ) return true; ) function convert_to_utf8($in_charset, $str)( return iconv(strtolower($in_charset), "utf-8", $str); ) function get_imap_title($str)( $mime = imap_mime_header_decode($str); $title = ""; foreach($mime as $key => $m)( if(!check_utf8 ($m->charset))( $title .= convert_to_utf8($m->charset, $m->text); )else( $title .= $m->text; ) ) return $title; )

The names are self-explanatory and, I think, it is worth explaining only the last function. It takes an encoded string and decodes it using imap_mime_header_decode() , which returns an array of objects, each of which has two properties charset (encoding) and text (theme text). Then everything is simple: in a loop, checking the encoding, we convert it to UTF-8 and glue the subject into a single header and return it.

Now let's go back to the index.php file and pull out the last parameter:

$mails_data[$i]["title"] = get_imap_title($msg_header->subject);

This completes the processing of the letter header.

Working with the body of the letter

We continue to gradually form our array with the processed email data, and now we need to use two functions to get the body:

// Message body $msg_structure = imap_fetchstructure($connection, $i); $msg_body = imap_fetchbody($connection, $i, 1);

The first variable $msg_structure contains the message structure - this is an object in which you can find a lot of useful information, an example of a part of this object is presented below:

What is important for solving our problem:

  • type - the primary type of the body of the letter, depending on what comes to us in the mail, it can vary from 0 to 7 (each digit advises its own type of content that is in the body of the letter);
  • encoding - body transfer encoding, varies from 0 to 5 (0 - 7BIT, 1 - 8BIT, 2 - BINARY, 3 - BASE64, 4 - QUOTED-PRINTABLE, 5 - OTHER);
  • parts is an array of letter parts that corresponds to the structure of the object one level up.

Let's take a closer look at the parts property. The first thing to say is that in the zero cell of this array there is information that corresponds exactly to the text of the letter, and starting from the first - to attached files. Also, in each object type is specified and in parameters the encoding is explicit and implicit.

The structure of the letter can be arbitrarily nested, at least I had cases when it reached four or five levels, so in order to irritate it, as they say, we will need to write a recursive function in the future.

The second imap_fetchbody() function fetches a specific part of the email, most often in encoded form.

Now let's add a variable in which we will save the processed version of the body of the letter:

$body = "";

Let's go back to the functions.php file and write a recursive function:

Function recursive_search($structure)( $encoding = ""; if($structure->subtype == "HTML" || $structure->type == 0)( if($structure->parameters->attribute == " charset")( $charset = $structure->parameters->value; ) return array("encoding" => $structure->encoding, "charset" => strtolower($charset), "subtype" => $structure- >subtype); )else( if(isset($structure->parts))( return recursive_search($structure->parts); )else( if($structure->parameters->attribute == "charset")( $ charset = $structure->parameters->value; ) return array("encoding" => $structure->encoding, "charset" => strtolower($charset), "subtype" => $structure->subtype); ) ) )

The recursive_search() function takes one parameter - the structure of the letter, where it sequentially checks the properties and gets three parameters: encoding, charset, subtype. The exit point from the recursion is the absence of the parts property with a zero cell. There is nothing more to explain here, I think it is clear from the code what is happening and how.

Let's add one more function for converting the body of the letter, which we will need in the future:

Function structure_encoding($encoding, $msg_body)( switch((int) $encoding)( case 4: $body = imap_qprint($msg_body); break; case 3: $body = imap_base64($msg_body); break; case 2: $body = imap_binary($msg_body); break; case 1: $body = imap_8bit($msg_body); break; case 0: $body = $msg_body; break; default: $body = ""; break; ) return $body ; )

$recursive_data = recursive_search($msg_structure); if($recursive_data["encoding"] == 0 || $recursive_data["encoding"] == 1)( $body = $msg_body; ) if($recursive_data["encoding"] == 4)( $body = structure_encoding($recursive_data["encoding"], $msg_body); ) if($recursive_data["encoding"] == 3)( $body = structure_encoding($recursive_data["encoding"], $msg_body); ) if($ recursive_data["encoding"] == 2)( $body = structure_encoding($recursive_data["encoding"], $msg_body); ) if(!check_utf8($recursive_data["charset"]))( $body = convert_to_utf8($ recursive_data["charset"], $msg_body); )

After we have received the data from the recursion, we gradually check the transfer encoding and, depending on this, call the structure_encoding () function with the appropriate parameters. In the last conditional operator, we take into account that we are working in UTF-8 and if after all the manipulations we get something different from the encoding, we will re-encode it.

It remains to draw a line:

$mails_data[$i]["body"] = base64_encode($body);

The body of the letter can contain both plain text and HTML markup with its own styles. We encode in BASE64 so that our layout does not go wrong during rendering.

Attached files

Here, we are gradually getting to the end of writing our application:

// Nested files if(isset($msg_structure->parts))( for($j = 1, $f = 2; $j< count($msg_structure->parts); $j++, $f++)( if(in_array($msg_structure->parts[$j]->subtype, $mail_filetypes))( $mails_data[$i]["attachs"][$j]["type"] = $msg_structure->parts[$j]->subtype; $mails_data[$i]["attachs"][$j]["size"] = $msg_structure->parts[$j]->bytes; $mails_data[ $i]["attachs"][$j]["name"] = get_imap_title($msg_structure->parts[$j]->parameters->value); $mails_data[$i]["attachs"][$ j]["file"] = structure_encoding($msg_structure->parts[$j]->encoding, imap_fetchbody($connection, $i, $f)); file_put_contents("tmp/".iconv("utf-8" , "cp1251", $mails_data[$i]["attachs"][$j]["name"]), $mails_data[$i]["attachs"][$j]["file"]); ) ) )

The piece responsible for processing the attached file is much smaller, and now - why exactly. The principle of working with a file is similar to working with the body of a letter, only this stage starts with its presence in the array of the parts property. Do not forget to filter out the unnecessary ones, referring to the list of types. Using the simple file_put_contents() function, we save our file to our server in the tmp folder.

I want to see the result!

In the course of work, we have formed an array with $mails_data data, and for visualization we will already work directly with it. In this article, I used a test letter that I had in the mail, let's see what we got in the end:


This is what kind of array you should get, alas, I had to hide the contents of the file for personal reasons. Now let's move on to our HTML markup:

Yandex Mail |<?php echo($mail_login);?>

Yandex Mail (Inbox) |

Number of letters:

no letters
$mail):?>
Timestamp:
The date:
To whom:
From:
Topic:
Letter in base64:
Attached files:
$attach):?>
Type of:
Size (in bytes):
Name:
Body:

I will not add styles here, since they do not play a special role, as a result:


And on the server in the tmp folder you will have a file.

Conclusion

After completing all the steps from the article, you will achieve the proper result, but everything is not as simple as it might seem - there are pitfalls that must be taken into account. When writing a script for a specific task, it is necessary to follow the encoding at all stages, letters can come from different mails, each of which may have its own nuances. It will also be important to take into account that Yandex mail and their documentation are periodically updated, so various sub-items for working with mail programs may appear. That's all for me, I hope you find this article useful when working with a lower-level version of Yandex mail.

Unable to receive email with attachment
if mail is sent with a file - any - then the text of the letter disappears

help me please

It’s clear ... if you transfer mail from Yandex to Yandex, then everything works out ...
sort of figured it out
but that's why this script does not accept other files besides Word, it's not clear ... there is a MSWORD line next to it, set it to pdf and jpg and png - it reads and saves normally only Word .... something like this

A computer