\documentclass{article}
\usepackage{ulem}
\usepackage{graphicx}
\usepackage{hyperref}
\pagestyle{headings}
\begin{document}
\part{How to use the MIME API}
\section{Creating MIME messages}
Scenario: We have a text block (such as text/calendar data) which needs to be added as a MIME part to an email message. We then need to add a couple of additional headers to this message and finally read it back as a string.

For Message Body:

<pre><code class="language-php">
require\_once 'Horde/MIME/Message.php';
\$message = new MIME\_Message();
\$part = new MIME\_Part('text/plain', \$message\_text);
\$message->addPart(\$part);
</code></pre>
For text/calendar attachment:

<pre><code class="language-php">
\$part = new MIME\_Part('text/calendar', \$text\_calendar\_data);
\$message->addPart(\$part);
</code></pre>
For Headers:

<pre><code class="language-php">
require\_once 'Horde/MIME/Headers.php';
\$headers = new MIME\_Headers();
\$headers->addHeader('Header 1', \$Header\_1\_Value);
\$headers->addHeader('Header 2', \$Header\_2\_Value);
\$headers->addMIMEHeaders(\$message);
</code></pre>
To return the message as a string:

<pre><code class="language-php">
\$string = \$headers->toString() . "\textbackslash\{\}n\textbackslash\{\}n" . \$message->toString();
</code></pre>
\section{Creating and Sending E-Mail with an attachment by using the MIME\_Mail class}
<pre><code class="language-php">
require\_once 'Horde/MIME/Mail.php';

// New Horde MIME\_Mail Object
\$mail = new MIME\_Mail();

// Set the header date
\$mail->addHeader('Date', date('r'));

// Set the from address
\$mail->addHeader('From', 'sender@example.com');

// Set the subject of the mail
\$mail->addHeader('Subject', 'Horde MIME\_Mail example');

// Set the text message body
\$mail->setBody('Example MIME message with an attachment');

// Add the file as an attachment, set the file name and what kind of file it is.
\$mail->addAttachment('/tmp/some\_file.zip', 'some\_file.zip', 'application/x-zip-compressed');

// Add recipients
\$mail->addRecipients('recipient@example.com');

// Get the mail driver
\$mail\_driver = \$conf['mailer']['type'];
\$mail\_params = \$conf['mailer']['params'];
if (\$mail\_driver == 'smtp' \&\& \$mail\_params['auth'] \&\&
    empty(\$mail\_params['username'])) \{
    if (Auth::getAuth()) \{
        \$mail\_params['username'] = Auth::getAuth();
        \$mail\_params['password'] = Auth::getCredential('password');
    \}
\}

// Send the mail
if (!is\_a(\$sent = \$mail->send(\$mail\_driver, \$mail\_params), 'PEAR\_Error')) \{
    print "E-Mail sent\textbackslash\{\}n";
\} else \{
    print "E-Mail not sent\textbackslash\{\}n";
\}
</code></pre>
\section{Parsing a MIME message}
Scenario: We have an existing text string which contains a valid MIME email message.  We need to parse this string in order to read back the body of a specific MIME part with a certain content type.

<pre><code class="language-php">
require\_once 'Horde/MIME/Structure.php';
\$message = MIME\_Structure::parseTextMIMEMessage(\$message\_text);
</code></pre>
If you have access to the mail server, you can also use:

<pre><code class="language-php">
\$structure = @imap\_fetchstructure(\$stream, \$uid, FT\_UID);
\$message = MIME\_Structure::parse(\$structure);
</code></pre>
To determine the structure of the MIME message, e.g. to find out the MIME ID we are looking for:

<pre><code class="language-php">
\$map = \$message->contentTypeMap();
</code></pre>
\texttt{\$map} is an array with key being the MIME IDs and values being the Content Types of that IDs.

To retrieve a certain MIME part of the message:

<pre><code class="language-php">
\$part = \$message->getPart(\$mime\_id);
</code></pre>
The following code snippets require at least Horde 3.2.

To retrieve the body text:

<pre><code class="language-php">
require\_once 'Horde/MIME/Contents.php';
\$contents = new MIME\_Contents(\$message);
\$body\_id = \$contents->findBody();
if (\$body\_id) \{
    \$part = \&\$contents->getMIMEPart(\$body\_id);
    \$body = \$part->transferDecode();
\} else \{
    \$body = 'Could not render body of message.';
\}
</code></pre>
To retrieve the HTML body text if any exists:

<pre><code class="language-php">
require\_once 'Horde/MIME/Contents.php';
\$contents = new MIME\_Contents(\$message);
\$body\_id = \$contents->findBody('html');
if (is\_null(\$body\_id)) \{
    \$body\_id = \$contents->findBody();
\}
if (\$body\_id) \{
    \$part = \&\$contents->getMIMEPart(\$body\_id);
    \$body = \$part->transferDecode();
\} else \{
    \$body = 'Could not render body of message.';
\}
</code></pre>
To retrieve a list of all attachments:

<pre><code class="language-php">
\$attachments = \$contents->getAttachmentContents();
foreach (\$attachments as \$attachment) \{
    echo 'Attachment ' . htmlspecialchars(\$attachment['name']) . ' is ' . strlen(\$attachment['data']) . ' bytes long.';
\}
</code></pre>
If you don't retrieve all attachments as expected, in particular if any attached mail/rfc822 parts are returned empty, apply the patch \texttt{mimeDecode.php.patch} that has been attached to this wiki page to the file \texttt{Mail/mimeDecode.php} in your PEAR directory.

\section{Using MIME Viewers}
Scenario: Continuing the previous example we want to render the text/calendar part to HTML. \textit{This code currently only works inside of a Horde application because it relies on the registry and Horde's configuration files.}

<pre><code class="language-php">
require HORDE\_BASE . '/config/mime\_drivers.php';
require\_once 'Horde/MIME/Viewer.php';
\$viewer = MIME\_Viewer::factory(\$part);
\$html = \$viewer->render();
</code></pre>
\section{Reading message headers}
\subsection{Using a mail server connection}
Scenario: We want to read a certain message from a certain message on the mail server, for example to search for messages with a known header.

<pre><code class="language-php">
/* Load libraries. */
require\_once 'Horde/MIME/Headers.php';
require\_once 'Horde/NLS.php';

/* Custom MIME\_Headers class because we need to implement \_getStream(). */
class MyHeaders extends MIME\_Headers \{

    function \_getStream()
    \{
        return imap\_open('\{localhost/imap\}INBOX', 'username', 'password');
    \}
\}

/* Get message subject. */
\$headers = new MyHeaders(\$message\_uid);
\$headers->buildHeaders();
\$subject = \$headers->getValue('Subject');
</code></pre>
\subsection{Using a header text}
If the headers can't be received from an IMAP server or already are available as a string, this alternative approach could be used:

<pre><code class="language-php">
\$headerText = 'Return-Path: <john@example.com>
Message-ID: <20080228160832.604925ntqxdo4o00@example.com>
Date: Thu, 28 Feb 2008 16:08:32 +0100
From: John Doe <john@example.com>
To: jane@example.com
Subject: Some Subject';

/* Load libraries. */
require\_once 'Horde/MIME/Structure.php';

/* Get message subject. */
\$headers = MIME\_Structure::parseMIMEHeaders(\$headerText);
\$subject = \$headers['Subject'];
</code></pre>
\section{Complete example script}
This is a complete example script that parses all messages of an INBOX for any CSV attachments, extract these attachments from the messages, saves them in a folder, and deletes the parsed message. The script can also be downloaded from the <a href="https://wiki.horde.org/AttachedFiles">AttachedFiles</a> area of this page.

<pre><code class="language-php">
\#!/usr/bin/php
<?php

/* IMAP/POP3 server settings. */
\$imap\_user = '';
\$imap\_pass = '';
\$imap\_host = 'localhost';
\$imap\_protocol = 'imap/notls';

/* Directory where the CSV attachments get stored WITH trailing slash. */
\$target\_dir = '/home/jan/csv/';

/* Search string for potential messages.
 * Might be optimized by using something like 'SUBJECT "Always the same"'. */
\$imap\_search = 'ALL';

/* Possible mime types of CSV attachments. */
\$csv\_mimetypes = array('text/plain',
                       'text/x-comma-separated-values',
                       'application/csv',
                       'application/vnd.ms-excel');

/* If necessary set the include path to the location where PEAR and the Horde
 * packages are installed. */
// ini\_set('include\_path', '/usr/share/php');

/****************************************************************************/

/**
 * Shows all IMAP errors and exits.
 */
function error\_out()
\{
    \$errors = imap\_errors();
    if (\$errors) \{
        foreach (\$errors as \$error) \{
            echo \$error . "\textbackslash\{\}n";
        \}
    \}
    exit;
\}

/* Load libraries. */
require\_once 'Horde/CLI.php';
require\_once 'Horde/MIME/Structure.php';
require\_once 'Horde/MIME/Headers.php';
require\_once 'Horde/NLS.php';

/* Setup CLI. */
if (!Horde\_CLI::runningFromCLI()) \{
    exit("Must be run from the command line\textbackslash\{\}n");
\}
Horde\_CLI::init();

/* Custom MIME\_Headers class. */
class MyHeaders extends MIME\_Headers \{

    function \_getStream()
    \{
        return \$GLOBALS['imap'];
    \}
\}

/* Open IMAP connection. */
\$imap = imap\_open(sprintf('\{\%s/\%s\}INBOX', \$imap\_host, \$imap\_protocol),
                  \$imap\_user, \$imap\_pass);
if (!\$imap) \{
    error\_out();
\}

/* Regexp for target filename. */
\$regexp = '/\_(\textbackslash\{\}d+).csv\$/';

/* Get all messages from the folder. */
\$messages = imap\_search(\$imap, \$imap\_search, FT\_UID);

foreach (\$messages as \$uid) \{
    \$struct = imap\_fetchstructure(\$imap, \$uid, FT\_UID);

    /* Is this no multipart message? */
    if (\$struct->type != 1) \{
        continue;
    \}

    /* Parse message structure. */
    \$body = imap\_fetchheader(\$imap, \$uid, FT\_UID) .
            imap\_body(\$imap, \$uid, FT\_UID);
    \$message = MIME\_Structure::parseTextMIMEMessage(\$body);
    \$map = \$message->contentTypeMap();

    /* Search for message with possible CSV attachment. */
    foreach (\$map as \$mime\_id => \$mime\_type) \{
        if (!in\_array(\$mime\_type, \$csv\_mimetypes)) \{
            continue;
        \}
        \$mime\_part = \$message->getPart(\$mime\_id);
        \$filename = \$mime\_part->getName(true);
        if (String::lower(substr(\$filename, -4)) != '.csv') \{
            continue;
        \}

        /* CSV file found. */
        \$content = \$mime\_part->transferDecode();

        /* Get message subject. */
        \$headers = new MyHeaders(\$uid);
        \$headers->buildHeaders();
        \$subject = \$headers->getValue('Subject');
        if (empty(\$subject)) \{
            \$subject = \$filename;
        \} else \{
            \$subject .= '.csv';
        \}

        /* Find unique file name. */
        \$filename = preg\_replace('/[\^{}a-z0-9\_\textbackslash\{\}-\textbackslash\{\}.]/i', '\_', \$subject);
        while (file\_exists(\$target\_dir . \$filename)) \{
            if (preg\_match(\$regexp, \$filename, \$match)) \{
                \$filename = preg\_replace(\$regexp, '\_' . (\$match[1] + 1) . '.csv', \$filename);
            \} else \{
                \$filename = substr(\$filename, 0, -4) . '\_1.csv';
            \}
        \}
        
        /* Write CSV file. */
        \$fp = fopen(\$target\_dir . \$filename, 'w');
        if (!\$fp) \{
            exit("Can't open file '" . \$target\_dir . \$filename . "' for writing.\textbackslash\{\}n");
        \}
        fwrite(\$fp, \$content);
        fclose(\$fp);

        /* Delete message. */
        imap\_delete(\$imap, \$uid, FT\_UID);
    \}
\}

/* Purge and close mailbox. */
// imap\_expunge(\$imap);
imap\_close(\$imap);
error\_out();
</code></pre>
\end{document}
