Zpět na seznam článků RSS

Správa Apple Notes pomocí IMAP

Poznámky Apple v iOS a OSX využívají pro synchro­nizaci protokol IMAP. Na serveru jsou pak zprávy uloženy ve složce „Notes“ v běžném MIME formátu. Dodržením interních konvencí lze poznámky snadno číst i vytvá­řet.

Formát poznámek

Poznámky jsou uloženy jako běžné MIME1 e-maily ve složce Notes standar­dně s těmito vlastnos­tmi:

Co si z tohoto vybrat?

Pochopi­telně, aby to byla větší prča, Apple tyto konvence nemá veřejně zdokumen­továny. Navíc se implemen­tace v iOS a OSX poněkud liší — Aplikace v iOS například ignoruje UUID, na rozdíl od OSX, který shodu netoleru­je a ostatní poznámky se shodným identifi­kátorem nezobra­zí. OSX také umí řadit poznámky podle data vytvoře­ní, iOS nikoliv.

Oba systémy používají pro zobrazení i editaci jádro e-mailového klienta, aplikace tak bude bez problémů tolerovat i multi­part/mi­xed2 obsah včetně grafiky. Pro následnou bezproblé­movou editaci je ovšem lepší si na divočejší kousky nechat zajít chuť — lidstvo možná prohání roboty po Marsu, ale WYSIWYG, který by zvládl úpravy složitěj­ších HTML dokumen­tů, je zjevně nad jeho síly. Platforma Apple samozřej­mě není výjimkou.

Klíčová a teoretic­ky jediná povinná je tedy hlavička X-Uniform-Type-Identifi­er: com.apple­.mail-note.

Čtení a zápis do Apple Notes v PHP

Pro komunika­ci s IMAP serverem lze použít poněkud obstarož­ní, nicméně funkční a dostupnou­, extenzi imap.3

Následu­jící jednoduchá třída čte a zapisuje do Apple Poznámek na IMAP serveru. Předpoklá­dá monolitic­ký MIME dokument s text/html obsahem a kódováním UTF-8.

class Notes {

   private $resource;
   private $username;
   private $remote;

   /**
    * Connect to IMAP server
    * @param string $remote Server (eg "imap.gmail.com:993/imap/ssl")
    * @param string $username
    * @param string $password
    */
   public function __construct($remote, $username, $password) {
      $this->username = $username;
      $this->remote = $remote;
      $this->resource = imap_open("{{$remote}}Notes", $username, $password);
   }

   /**
    * Get uid-indexed array of notes subject and date
    * @return array
    */
   public function getNotesList() {
      $nmsgs = imap_mailboxmsginfo($this->resource)->Nmsgs;
      $notes = array_reduce(imap_fetch_overview($this->resource, '1:' . $nmsgs),
         function (array $carry, $item) {
            $carry[(int)$item->uid] = array(
               'subject' => imap_utf8($item->subject),
               'date' => new DateTime($item->date));
            return $carry;
         }, array());

      return $notes;
   }

   /**
    * Get note by message UID
    * @param int $uid Message UID
    * @return object
    */
   public function getNote($uid) {
      $body = imap_fetchbody($this->resource, $uid, '1', FT_UID);
      $headers = array_reduce(explode("\r\n", imap_fetchheader($this->resource, $uid, FT_UID)),
         function (array $carry, $item) {
            if (preg_match('/(\S+): (.+)/', $item, $matches))
               $carry[$matches[1]] = $matches[2];

            return $carry;
         }, array());

      return (object)array('headers' => $headers, 'body' => imap_qprint($body));
   }

   /**
    * Delete note by message UID
    * @param int $uid Message UID
    */
   public function deleteNote($uid) {
      imap_delete($this->resource, $uid, FT_UID);
   }

   /**
    * Create or update note
    * @param string $body Note contents
    * @param int $uid Message UID
    */
   public function updateNote($body, $uid = null) {
      $uuid = $created = null;

      if ($uid) {
         $headers = $this->getNote($uid)->headers;
         $uuid = $headers['X-Universally-Unique-Identifier'];
         $created = new DateTime($headers['X-Mail-Created-Date']);
         $this->deleteNote($uid);
      }

      $content = implode("\r\n", $this->createHeaders(strip_tags($body), $uuid, $created));
      $content .= "\r\n\r\n" . imap_8bit($body);

      imap_append($this->resource, "{{$this->remote}}Notes", $content);
   }

   /**
    * Close IMAP connection
    */
   public function __destruct() {
      imap_close($this->resource, CL_EXPUNGE);
   }

   private function createHeaders($subject, $uuid = null, DateTime $created = null) {
      return array(
         'Content-Type: text/html; charset=utf-8',
         'Content-Transfer-Encoding: quoted-printable',
         'Date: ' . date('r'),
         'From: ' . $this->username,
         'Mime-Version: 1.0 (PHP/' . phpversion() . ')',
         'Subject: =?utf-8?Q?' . imap_8bit(mb_substr($subject, 0, 30, 'utf-8')) . '?=',
         'X-Mail-Created-Date: ' . ($created ? $created->format('r') : date('r')),
         'X-Uniform-Type-Identifier: com.apple.mail-note',
         'X-Universally-Unique-Identifier: ' . ($uuid ?: self::createUUID())
      );
   }

   private static function createUUID() {
      $id = str_split(strtoupper(md5(microtime())), 4);
      return "$id[0]$id[1]-$id[2]-$id[3]-$id[4]-$id[5]$id[6]$id[7]";
   }
}

Reference

  1. RFC 2045: MIME Part One: Format of Internet Message Bodies — tools.i­etf.org
  2. RFC 2046: MIME Part Two: Media Types — tools.i­etf.org
  3. PHP – Mail Related Extensi­on: IMAP — php.net

Pokud není uvedeno jinak, podléhá obsah licenci CC BY-NC-ND a ukázkové zdrojové kódy CC BY.
Martin Hozík — , poslední úprava .