zappaciato's avatar

Laravel Mailbox

Hi Everyone! I will keep it short. Basically, I need to create a mailbox in Laravel 9. Not only for sending emails (this part seems easier) but also for fetching incoming emails from IMAP server (typical, nothing fancy). Any my questions are as follows:

  1. Are there any packages that you can recommend? I've discovered Laravel Mailbox but the documentation isn't really extensive for a beginner in Laravel. https://beyondco.de/docs/laravel-mailbox/getting-started/introduction
  2. I would love to see some tutorial where somebody does this from scratch. Surprisingly, I can only find tutorials for outbound emailing but I need some examples from inbound/receiving emails in Laravel. It all comes down to understanding the process including the necessity to save emails in the database (Should I do this and what would be the best practice?). Also, CronJob settings and putting it all together to finally fetch all you need from IMAP. That is why it would be useful for me to see an example of such a solution made from scratch.

Thank you for all your help and recommendations.

Kris

0 likes
9 replies
Sinnbeck's avatar

I think handling incoming emails in laravel is such a niche that not many tutorial writers want to spend hours writing it as very few will read it. So your best bet is probably to get started and then ask us whenover you get stuck.

Be aware that that package is meant to be connected to one of the providers in the drivers page. Its not meant to work with your inhouse imap server https://beyondco.de/docs/laravel-mailbox/drivers/drivers

zappaciato's avatar

@Sinnbeck that is well spotted, I mean, the fact it uses Mailgun and SendGrid. I just wanted to find a solution to fetch emails from our polish provider - www.home.pl. All the settings are ready for the email client, that I want to create. Once the email gets fetched I want to create a Task from it and assing it to a user. Like a project manager. I am a beginner, literally, I have had 9 months of experience with Larvel, so apologies for the silly questions.

Am I right in thinking that an IMAP server is something like a Database server where emails are stored? All I need to do is to connect to this server (set it up in .env file, make sure config/mail.php is set correctly) but then how do I establish the connection? I think I also need to register it in AppServiceProvider.php, and in the boot method as well? I am not so sure if I am going in the right direction.

Would you be able to advise how to bit this hard subject?

Thanks for your help!

star101's avatar

Try github.com Webklex/laravel-imap or seedgabo/laravel-imap and let us know how you go.

Snapey's avatar

i prefer to use an inbound mail service, in my case, postmarkapp.com

You tell them the endpoint to call when a new email arrives, and they call you and pass a json object containing all the data and metadata for the email

https://postmarkapp.com/inbound-email

zappaciato's avatar

@Snapey Hi, thanks for all your answers. I am still working on it, so I think I will need some help later so let's not close this thread. As for the postmark, as far as I know, it is paid service. It is worth getting into it if email exchange daily amounts to 300 emails? How could I convince the client to use this service? Perhaps there are some strong arguments for this service besides the fact it would make my work easier a bit. Thanks!

ibrar_1's avatar

@zappaciato did you find any way please let me known as well if you find anything. i am also struggling to get some material from where i can get some help.

Merklin's avatar

Have you looked in PHP imap_open? It's pretty easy to setup a conncetion and read all emails. It supports also email search, filter by date, download attachments if any, etc. Simple example I've used in a projec of mine:

Note: My goal was only to get th attachment file, but it is easy to get the title, content and other mail data and store i to database. You can try just the first part and then dump $emails to see what you get as data.

$hostname = {mailServerName :993/imap/ssl}INBOX';
$username = emailAddress';
$password = emailPassword';

// Connect
$inbox = imap_open($hostname, $username, $password);

//Set date
$date = date('d F Y', strtotime('- 1 day'));
// Filter by date
$emails = imap_search($inbox, 'SINCE "' . $date . '"');

if ($emails) {
    /* put the newest emails on top */
    rsort($emails);

    /* for every email... */
    foreach ($emails as $email_number) {
        /* get mail structure */
        $structure = imap_fetchstructure($inbox, $email_number);
        $header = imap_headerinfo($inbox, $email_number);

        $attachments = [];

        /* if any attachments found... */
        if (isset($structure->parts) && count($structure->parts)) {
            for ($i = 0; $i < count($structure->parts); $i++) {
                $attachments[$i] = [
                    'is_attachment' => false,
                    'filename'      => '',
                    'name'          => '',
                    'attachment'    => '',
                ];

                if ($structure->parts[$i]->ifparameters) {
                    foreach ($structure->parts[$i]->parameters as $object) {
                        if (strtolower($object->attribute) === 'name') {
                            $attachments[$i]['is_attachment'] = true;
                            $attachments[$i]['name'] = $object->value;
                        }
                    }
                }

                $attachments = $this->etoGetAttachments($attachments, $i, $inbox, $email_number, $structure);
            }
        }

        $j = 0;
        /* iterate through each attachment and save it */
        foreach ($attachments as $attachment) {
            if ($attachment['is_attachment'] == 1) {
                $timestamp = strtotime($header->date);
                $filename = $this->etoFileName($attachment, $timestamp . '.' . $j);

                // External class to store the attachment file
                $file->file_name = $filename;
                $file->create($user);
            }
            $j++;
        }
    }
}
}

public
function etoGetAttachments(array $attachments, int $i, $inbox, $email_number, $structure): array
{

    if ($attachments[$i]['is_attachment']) {
        $j = $i + 1;
        $attachments[$i]['attachment'] = imap_fetchbody($inbox, $email_number, '' . $j . '');

        /* 3 = BASE64 encoding */
        if ($structure->parts[$i]->encoding == 3) {
            $attachments[$i]['attachment'] = base64_decode($attachments[$i]['attachment']);
        } /* 4 = QUOTED-PRINTABLE encoding */
        elseif ($structure->parts[$i]->encoding == 4) {
            $attachments[$i]['attachment'] = quoted_printable_decode($attachments[$i]['attachment']);
        }
    }

    return $attachments;
}

/**
 * @param $attachment
 * @param $timestamp
 *
 * @return string
 */
public
function etoFileName($attachment, $timestamp)
{

    $filename1 = $attachment['name'];
    $filename2 = iconv_mime_decode($filename1, ICONV_MIME_DECODE_CONTINUE_ON_ERROR, 'UTF-8');
    //$filename = iconv("UTF-8", "ISO-8859-1//TRANSLIT", $filename);
    $filename3 = transliterator_transliterate('Any-Latin;Latin-ASCII;', $filename2);
    $filename = $timestamp . '.' . $filename3;

    $folder = 'tmp';
    if (!is_dir($folder) && !mkdir($folder) && !is_dir($folder)) {
        throw new RuntimeException(sprintf('Directory "%s" was not created', $folder));
    }

    $this->etoFopen($folder, $filename, $attachment['attachment']);

    return $filename;
}

/**
 * @param string $folder
 * @param string $filename
 * @param string $attachment1
 */
public
function etoFopen(string $folder, string $filename, string $attachment1): void
{

    $fp = fopen(DOL_DOCUMENT_ROOT . '/custom/emailtoorder/' . $folder . '/' . $filename, 'wb+');
    fwrite($fp, $attachment1);
    fclose($fp);
}

/**
 * @param resource $inbox
 *
 * @return string
 */
public
function etoCloseConn($inbox): string
{

    imap_close($inbox);
    array_map('unlink', glob('tmp/*.mobileconfig'));

    return 'all attachment Downloaded';
}

When you have your classes ready you just need a cron job to call the method fetching the emails.

Snapey's avatar

@Merklin THIS is easy?

This is my controller using a feed from postmark. As simple as accepting a form, and of course you can apply validation;

        $inboundmail = InboundMail::create([
            'subject'   => $request->Subject,
            'to'        => $request->OriginalRecipient,
            'from'      => $request->From,
            'prefix'    => $to,
            'team_id'   => $team->id,
            'hash'      => strtolower(Str::after($to,'+')),
            'body'      => $request->HtmlBody ?? $request->TextBody,
        ]);

        event(new MailAdded($inboundmail));
ibrar_1's avatar

I find some kind of breakthrough. but I am not sure about it.

This is my env variable for emails :

  • MAILBOX_DRIVER=log

  • MAIL_DRIVER=log

  • MAIL_MAILER=log

  • MAIL_HOST=smtp.gmail.com

  • MAIL_PORT=587

  • MAIL_USERNAME=[email protected]

  • MAIL_PASSWORD=password

  • MAIL_ENCRYPTION=tls

  • MAIL_FROM_ADDRESS="ibrartariq74@gmail.com"

  • MAIL_FROM_NAME="${APP_NAME}"

This is boot method of app service providers: public function boot() {

    Mailbox::catchAll(function (InboundEmail $email) {
        Log::info('Mailbox boot method called');
        
        Log::info('Received an email!', [
            'subject' => $email->subject(),
            'from' => $email->from(),
            'content' => $email->text(),
        ]);
    });


  
}

This configuration i get from beyond code

now i am sendig email from my application i can see in my log file

Mailbox boot method called

Now the problem is how can i test incoming email to my application with log driver as from beyondcode documentation for local development i can use only log driver so anyone can tell me how can i send email to my application with log driver.

  • should i need to send email from code to using [to,from method in laravel]
  • should i send email from gmail account to that env gmail account

Please or to participate in this conversation.