Sanity checking.
authorMichael M Slusarz <slusarz@curecanti.org>
Fri, 27 Mar 2009 21:09:17 +0000 (15:09 -0600)
committerMichael M Slusarz <slusarz@curecanti.org>
Fri, 27 Mar 2009 21:15:09 +0000 (15:15 -0600)
Don't do message operations reliant on UID list if UIDVALIDITY of mailbox
has changed.

imp/docs/CHANGES
imp/lib/Imap.php
imp/lib/Message.php

index 867f7ba..022fa8d 100644 (file)
@@ -2,6 +2,8 @@
 v5.0-git
 --------
 
+[mms] Sanity check - don't do message operations reliant on UID list if
+      UIDVALIDITY of mailbox has changed.
 [mms] Simplify forwarding - always attach entire message.
 [mms] Remember splitbar position on login/refresh (DIMP).
 [mms] Disable some advanced functions if using POP3 driver (caching,
index f5c1cae..63bdd37 100644 (file)
@@ -49,6 +49,13 @@ class IMP_Imap
     protected $_nsdefault;
 
     /**
+     * UIDVALIDITY check cache.
+     *
+     * @var array
+     */
+    protected $_uidvalid = array();
+
+    /**
      * Constructor.
      */
     function __construct()
@@ -262,6 +269,37 @@ class IMP_Imap
     }
 
     /**
+     * Do a UIDVALIDITY check - needed if UIDs are passed between page
+     * accesses.
+     *
+     * @param string $mailbox  The mailbox to check. Must be an IMAP mailbox.
+     *
+     * @throws Horde_Exception
+     */
+    public function checkUidvalidity($mailbox)
+    {
+        // TODO: POP3 also?
+        if ($_SESSION['imp']['protocol'] == 'pop') {
+            return;
+        }
+
+        if (!isset($this->_uidvalid[$mailbox])) {
+            $status = $this->ob->status($mailbox, Horde_Imap_Client::STATUS_UIDVALIDITY);
+            $ptr = &$_SESSION['imp']['cache'];
+            $val = isset($ptr['uidvalid'][$mailbox])
+                ? $ptr['uidvalid'][$mailbox]
+                : null;
+            $ptr['uidvalid'][$mailbox] = $status['uidvalidity'];
+
+            $this->_uidvalid[$mailbox] = (!is_null($val) && ($status['uidvalidity'] != $val));
+        }
+
+        if ($this->_uidvalid[$mailbox]) {
+            throw new Horde_Exception(_("Mailbox structure on server has changed."));
+        }
+    }
+
+    /**
      * Logs an exception from Horde_Imap_Client.
      *
      * @param object Horde_Imap_Client_Exception $e  The exception object.
index 043528e..dffc48d 100644 (file)
@@ -118,23 +118,38 @@ class IMP_Message
         }
 
         foreach ($msgList as $mbox => $msgIndices) {
+            $error = null;
+
             if (($action == 'move') && $GLOBALS['imp_imap']->isReadOnly($mbox)) {
-                $notification->push(sprintf($message, IMP::displayFolder($mbox), IMP::displayFolder($targetMbox)) . ': ' . _("The target directory is read-only."), 'horde.error');
-                $return_value = false;
-                continue;
+                $error =  _("The target directory is read-only.");
+            }
+
+            if (!$error) {
+                try {
+                    $GLOBALS['imp_imap']->checkUidvalidity($mbox);
+                } catch (Horde_Exception $e) {
+                    $error = $e->getMessage();
+                }
             }
 
             /* Attempt to copy/move messages to new mailbox. */
-            try {
-                $GLOBALS['imp_imap']->ob->copy($mbox, $targetMbox, array('ids' => $msgIndices, 'move' => $imap_move));
+            if (!$error) {
+                try {
+                    $GLOBALS['imp_imap']->ob->copy($mbox, $targetMbox, array('ids' => $msgIndices, 'move' => $imap_move));
 
-                $imp_mailbox = IMP_Mailbox::singleton($mbox);
-                if (($action == 'move') && $imp_mailbox->isBuilt()) {
-                    $imp_mailbox->removeMsgs(array($mbox => $msgIndices));
+                    $imp_mailbox = IMP_Mailbox::singleton($mbox);
+                    if (($action == 'move') && $imp_mailbox->isBuilt()) {
+                        $imp_mailbox->removeMsgs(array($mbox => $msgIndices));
+                    }
+                } catch (Horde_Imap_Client_Exception $e) {
+                    $error = $e->getMessage();
                 }
-            } catch (Horde_Imap_Client_Exception $e) {
-                $notification->push(sprintf($message, IMP::displayFolder($mbox), IMP::displayFolder($targetMbox)) . ': ' . $e->getMessage(), 'horde.error');
+            }
+
+            if ($error) {
+                $notification->push(sprintf($message, IMP::displayFolder($mbox), IMP::displayFolder($targetMbox)) . ': ' . $error, 'horde.error');
                 $return_value = false;
+                continue;
             }
         }
 
@@ -187,8 +202,22 @@ class IMP_Message
         }
 
         foreach ($msgList as $mbox => $msgIndices) {
+            $error = null;
+
             if ($GLOBALS['imp_imap']->isReadOnly($mbox)) {
-                $notification->push(sprintf(_("There was an error deleting messages from the folder \"%s\". This folder is read-only."), IMP::displayFolder($mbox)), 'horde.error');
+                $error = _("This folder is read-only.");
+            }
+
+            if (!$error) {
+                try {
+                    $GLOBALS['imp_imap']->checkUidvalidity($mbox);
+                } catch (Horde_Exception $e) {
+                    $error = $e->getMessage();
+                }
+            }
+
+            if ($error) {
+                $notification->push(sprintf(_("There was an error deleting messages from the folder \"%s\"."), IMP::displayFolder($mbox)) . ' ' . $error, 'horde.error');
                 $return_value = false;
                 continue;
             }
@@ -449,6 +478,8 @@ class IMP_Message
             throw new Horde_Exception(_("Cannot strip the MIME part as the mailbox is read-only"));
         }
 
+        $GLOBALS['imp_imap']->checkUidvalidity($mbox);
+
         /* Get a local copy of the message. */
         $contents = IMP_Contents::singleton($index . IMP::IDX_SEP . $mbox);
 
@@ -530,8 +561,6 @@ class IMP_Message
      */
     public function flag($flags, $indices, $action = true)
     {
-        global $notification;
-
         if (!($msgList = IMP::parseIndicesList($indices))) {
             return false;
         }
@@ -541,16 +570,31 @@ class IMP_Message
             : array('remove' => $flags);
 
         foreach ($msgList as $mbox => $msgIndices) {
+            $error = null;
+
             if ($GLOBALS['imp_imap']->isReadOnly($mbox)) {
-                $notification->push(sprintf(_("There was an error flagging messages in the folder \"%s\". This folder is read-only."), IMP::displayFolder($mbox)), 'horde.error');
-                continue;
+                $error = _("This folder is read-only.");
             }
 
-            /* Flag/unflag the messages now. */
-            try {
-                $GLOBALS['imp_imap']->ob->store($mbox, array_merge($action_array, array('ids' => $msgIndices)));
-            } catch (Horde_Imap_Client_Exception $e) {
-                $notification->push(sprintf(_("There was an error flagging messages in the folder \"%s\". This is what the server said"), IMP::displayFolder($mbox)) . ': ' . $e->getMessage(), 'horde.error');
+            if (!$error) {
+                try {
+                    $GLOBALS['imp_imap']->checkUidvalidity($mbox);
+                } catch (Horde_Exception $e) {
+                    $error = $e->getMessage();
+                }
+            }
+
+            if (!$error) {
+                /* Flag/unflag the messages now. */
+                try {
+                    $GLOBALS['imp_imap']->ob->store($mbox, array_merge($action_array, array('ids' => $msgIndices)));
+                } catch (Horde_Imap_Client_Exception $e) {
+                    $error = $e->getMessage();
+                }
+            }
+
+            if ($error) {
+                $GLOBALS['notification']->push(sprintf(_("There was an error flagging messages in the folder \"%s\". This folder is read-only."), IMP::displayFolder($mbox)), 'horde.error');
                 return false;
             }
         }
@@ -630,6 +674,16 @@ class IMP_Message
         }
 
         foreach ($process_list as $key => $val) {
+            /* If expunging a particular UID list, need to check
+             * UIDVALIDITY. */
+            if (is_array($val)) {
+                try {
+                    $GLOBALS['imp_imap']->checkUidvalidity($key);
+                } catch (Horde_Exception $e) {
+                    continue;
+                }
+            }
+
             try {
                 $update_list[$key] = $GLOBALS['imp_imap']->ob->expunge($key, array('ids' => is_array($val) ? $val : array(), 'list' => $msg_list));