Статьи

Создание мобильного фото-блога, часть 1

Кажется, что в наши дни все отправляют смс-сообщения на свой мобильный телефон или обновляют статус своей социальной сети каждые 5 минут. Неудивительно, что удобство доступа к Интернету из любого места в любое время сделало обмен сообщениями и фотографиями столь популярными. Я не могу себе представить, чтобы куда-нибудь ехать без моего сотового телефона на случай, если что-нибудь интересное может случиться, и я могу задокументировать это, как будто я был первым репортером на сцене.

Это первая статья из двух частей, в которой я покажу вам, как создать фотоблог как часть вашего личного веб-сайта, который вы можете обновить со своего телефона, просто отправив электронное письмо. Вы напишите скрипт для проверки входящих сообщений в учетной записи электронной почты на наличие новых сообщений с использованием POP3; скрипт извлечет строку темы сообщения, основной текст и вложения и соответственно обновит базу данных. Затем вы можете извлечь информацию из базы данных для отображения в своем блоге, на боковой панели или как вам угодно.

Вместо разработки мобильного приложения, такого как для Android или iPhone, код будет работать на веб-сервере и фактически будет независимым от платформы. Что касается PHP, я буду использовать расширения IMAP и Imagick, поэтому вы должны убедиться, что они установлены до начала работы.

Вопросы безопасности

По соображениям безопасности вам, очевидно, потребуется какой-то тип аутентификации. Для этого сначала нужно настроить личную учетную запись электронной почты для веб-сервера, о котором знают только вы. Это учетная запись, которая будет получать обновления. Затем вам понадобится вторая учетная запись электронной почты для вашего мобильного устройства для отправки обновлений. Я буду использовать личную электронную почту [email protected] и общедоступную электронную почту [email protected] в коде статей.

Уровень безопасности, который вы можете применить к учетной записи электронной почты сервера, зависит от того, какие административные привилегии у вас есть. В лучшем случае вы хотите, чтобы ваш почтовый сервер отклонял всю входящую почту в учетной записи, кроме как с публичного адреса вашего мобильного телефона. В худшем случае вы хотите, по крайней мере, убедиться, что его спам-фильтр установлен на максимально возможную настройку, и добавить свой мобильный адрес в белый список.

В качестве дополнительного уровня безопасности каждому письму, полученному [email protected], будет присвоен уникальный токен. Затем токен будет отправлен на ваш телефон вместе с краткой сводкой сообщения и попросит вас подтвердить. Чтобы обновить свой блог, вам нужно просто отправить электронное письмо, дождаться автоответчика и затем открыть предоставленную ссылку, чтобы подтвердить обновление. Автоответ будет отправлен скриптом, который выполняется каждые несколько минут как задание cron или, если у вас есть доступ к самому почтовому серверу, вы можете выбрать что-то вроде procmail или maildrop .

С этими мерами безопасности вы можете быть уверены, что ничего не будет опубликовано без вашего согласия.

База данных

Для этого проекта вам потребуется как минимум три таблицы: таблица blog_posts, в которой будут храниться идентификатор блога, заголовок блога, основной текст и метка времени; таблица изображений, в которой будут храниться идентификатор изображения, ссылка на идентификатор блога и путь к файлу, где изображение хранится на сервере; и ожидающая таблица, в которой будет храниться идентификатор сообщения, относящийся к номеру сообщения в папке входящих сообщений, уникальный 32-символьный токен MD5 и флаг, указывающий состояние подтверждения.

Это операторы CREATE TABLE для базы данных:

CREATE TABLE blog_posts (
  post_id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
  title VARCHAR(100) NOT NULL,
  body TEXT NOT NULL,
  create_ts TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,

  PRIMARY KEY (post_id)
);

CREATE TABLE images (
  image_id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
  post_id INTEGER UNSIGNED NOT NULL,
  image_path VARCHAR(200) NOT NULL,

  PRIMARY KEY (image_id),
  FOREIGN KEY (post_id) REFERENCES blog_posts(post_id)
);

CREATE TABLE pending (
  message_id INTEGER UNSIGNED NOT NULL,
  token CHAR(32) NOT NULL,
  is_valid ENUM('Y','N') DEFAULT 'N',

  PRIMARY KEY (message_id)
);

Получение электронной почты

Когда дело доходит до получения электронной почты, у вас есть возможность подключения с IMAP или POP3. Основное различие между ними заключается в способе хранения почты. POP3 ожидает, что вы будете хранить почту на своем компьютере, поэтому он предлагает только способ загрузки новой почты, после чего копия с сервера удаляется. IMAP, с другой стороны, больше похож на «веб-почту» в том смысле, что вся почта хранится и сохраняется на сервере, к которому вы обращаетесь удаленно, и поэтому предоставляет дополнительные функции, такие как управление папками. IMAP по понятным причинам является более сложным, что вы можете увидеть сами, сравнив RFC для двух протоколов.

Во всех отношениях POP3 справится с задачей, поскольку вам не нужны никакие специальные функции. PHP предоставляет функции для работы с почтовыми протоколами, включая POP3, с его расширением IMAP.

Документация по многим функциям расширения является пятнистой, и из-за этого их нелегко использовать. К счастью, набор функций- оболочек был написан и размещен в заметках Уайла Барата, предоставленных пользователем, которые предоставляют API, который намного проще в использовании. Следующий класс основан на коде Барата и должен быть скопирован и сохранен как POP3.php:

 <?php
class POP3
{
    private $conn;

    // make connection
    public function __construct($host, $user, $pass, $folder = "INBOX", $port = 110, $useSSL = false) {
        $ssl = ($useSSL) ? "" : "/novalidate-cert";
        $mailbox = sprintf("{%s:%d/pop3%s}%s", $host, $port, $ssl, $folder);
        $this->conn = imap_open($mailbox, $user, $pass);
    }

    // close connection and trigger expunge
    public function __destruct() {
        imap_close($this->conn, CL_EXPUNGE);
    }

    // retrieve a list of messages
    public function listMessages($msgNum = "") {
        $msgList = array();
        if ($msgNum) {
            $range = $msgNum;
        }
        else {
            $info = imap_check($this->conn);
            $range = "1:" . $info->Nmsgs;
        }
        $response = imap_fetch_overview($this->conn, $range);
        foreach ($response as $msg) {
            $msgList[$msg->msgno] = (array)$msg;
        }
        return $msgList;
    }

    // delete a message
    public function deleteMessage($msgNum) {
        return imap_delete($this->conn, $msgNum);
    }

    // parse headers into usable code
    public function parseHeaders($headers) {
        $headers = preg_replace('/rns+/m', "", $headers);
        preg_match_all('/([^: ]+): (.+?(?:rns(?:.+?))*)?rn/m',
            $headers, $matches);
        foreach ($matches[1] as $key => $value) {
            $result[$value] = $matches[2][$key];
        }
        return $result;
    }

    // separate MIME types
    public function mimeToArray($msgNum, $parseHeaders = false) {
        $mail = imap_fetchstructure($this->conn, $msgNum);
        $mail = $this->getParts($msgNum, $mail, 0);
        if ($parseHeaders) {
            $mail[0]["parsed"] = $this->parseHeaders($mail[0]["data"]);
        }
        return $mail;
    }

    // separate mail parts
    public function getParts($msgNum, $part, $prefix) {
        $attachments = array();
        $attachments[$prefix] = $this->decodePart($msgNum, $part, $prefix);

        // multi-part
        if (isset($part->parts)) {
            $prefix = ($prefix) ? $prefix . "." : "";
            foreach ($part->parts as $number => $subpart) {
                $attachments = array_merge($attachments, $this->getParts($msgNum, $subpart, $prefix . ($number + 1)));
            }
        }
        return $attachments;
    }

    // fetch the body of an email with one part
    public function fetchBody($msgNum = "") {
        return imap_fetchbody($this->conn, $msgNum, 1);
    }

    // decode attachments
    public function decodePart($msgNum, $part, $prefix) {
        $attachment = array();

        if ($part->ifdparameters) {
            foreach ($part->dparameters as $obj) {
                $attachment[strtolower($obj->attribute)] = $obj->value;
                if (strtolower($obj->attribute) == "filename") {
                    $attachment["is_attachment"] = true;
                    $attachment["filename"] = $obj->value;
                }
            }
        }

        if ($part->ifparameters) {
            foreach ($part->parameters as $obj) {
                $attachment[strtolower($obj->attribute)] = $obj->value;
                if (strtolower($obj->attribute) == "name") {
                    $attachment["is_attachment"] = true;
                    $attachment["name"] = $obj->value;
                }
            }
        }

        $attachment["data"] = imap_fetchbody($this->conn, $msgNum, $prefix);
        // 3 is base64
        if ($part->encoding == 3) {
            $attachment["data"] = base64_decode($attachment["data"]);
        }
        // 4 is quoted-printable
        elseif ($part->encoding == 4) {
            $attachment["data"] = quoted_printable_decode($attachment["data"]);
        }
        return($attachment);
    }
}

Конструктор класса устанавливает соединение с почтовым сервером. Возможность использования SSL-шифрования также доступна, если у вас есть сертификат. Деструктор закрывает соединение.

Метод listMessages() Возвращенный список содержит не реальное содержимое письма, а адрес отправителя, адрес, строку темы и дату.

Метод deleteMessage() Сообщение не будет фактически удалено, пока соединение не будет закрыто с соответствующим флагом, что делается в деструкторе класса.

Метод parseHeaders()

Метод mimeToArray() Если найдено несколько типов MIME, это хороший признак наличия вложений.

Метод getParts()

Метод fetchBody()

Метод decodeParts()

С помощью этого класса вы можете теперь подключиться к серверу и получить полный список сообщений в папке входящих сообщений следующим образом.

 <?php
require_once "POP3.php";

define("EMAIL_HOST", "pop.liberty2.say");
define("EMAIL_USER", "[email protected]");
define("EMAIL_PASSWD", "********");

$pop3 = new POP3(EMAIL_HOST, EMAIL_USER, EMAIL_PASSWD);
$msgList = $pop3->listMessages();

foreach ($msgList as $msg) {
    echo "<pre>" . print_r($msg, true) . "</pre>";
}

Резюме

Это подводит нас к концу части 1. Пока у вас есть план игры для создания приложения и публикации только тех отправленных вами сообщений. У вас также есть схема базы данных и класс, который позволяет вам подключаться к серверу POP3 и получать сообщения. Обязательно вернитесь к части 2, чтобы увидеть, как эти части сочетаются друг с другом для реализации проекта.

Изображение через Анжелу Уэй / Shutterstock