Fix escaping null characters in mailbox names.
authorMichael M Slusarz <slusarz@curecanti.org>
Tue, 17 Aug 2010 05:32:22 +0000 (23:32 -0600)
committerMichael M Slusarz <slusarz@curecanti.org>
Tue, 17 Aug 2010 07:10:46 +0000 (01:10 -0600)
The null character can not be used in form elements. However, we use the
null character in mailbox names to indicate mailboxes/elements that do
not live on the IMAP server - necessary because null is the only
character technically not allowed for IMAP mailboxes.  So we need to
manually call IMP::formMbox() when creating forms to escape mailbox
names (and then when reading forms to unescape the names - but most of
the time this will be done automatically by
IMP::setCurrentMailboxInfo()).

13 files changed:
imp/js/folderprefs.js
imp/lib/Dimp.php
imp/lib/IMP.php
imp/lib/Imap/Tree.php
imp/lib/LoginTasks/SystemTask/UpgradeFromImp4.php
imp/lib/Message.php
imp/lib/Prefs/Ui.php
imp/lib/Search.php
imp/mailbox.php
imp/message.php
imp/templates/imp/flist/flist.html
imp/templates/prefs/initialpage.html
imp/templates/prefs/sentmail.html

index cc5cb3f..2566d6f 100644 (file)
@@ -18,7 +18,7 @@ var ImpFolderPrefs = {
             newfolder = $(id + '_new'),
             sel = $(f[f.selectedIndex]);
 
-        if (sel.value == '\0create' && !newfolder) {
+        if (sel.hasClassName('flistCreate') && !newfolder) {
             folder = window.prompt(txt, '');
             if (!folder.empty()) {
                 if (!newfolder) {
@@ -39,7 +39,7 @@ var ImpFolderPrefs = {
             $('sent_mail_folder').setValue(this.sentmail[e.memo.i]);
             if (this.origtext) {
                 $('sent_mail_folder_new').remove();
-                $('sent_mail_folder').down('[value="\0create"]').update(this.origtext);
+                $('sent_mail_folder').down('.flistCreate').update(this.origtext);
                 this.origtext = null;
             }
             break;
index bbb92ad..b7e592b 100644 (file)
@@ -15,7 +15,7 @@
 class IMP_Dimp
 {
     /* String used to separate indexes. */
-    const IDX_SEP = '\0';
+    const IDX_SEP = "\0";
 
     /**
      * Output a dimp-style action (menubar) link.
index 343d9f2..6967580 100644 (file)
@@ -34,8 +34,13 @@ class IMP
     const MAILBOX_START_LASTPAGE = 4;
 
     /* Preferences constants. */
-    const PREF_NO_FOLDER = 'nofolder\0';
-    const PREF_VTRASH = 'vtrash\0';
+    const PREF_DEFAULT = "default\0";
+    const PREF_NO_FOLDER = "nofolder\0";
+    const PREF_VTRASH = "vtrash\0";
+
+    /* Folder list actions. */
+    const NOTEPAD_EDIT = "notepad\0";
+    const TASKLIST_EDIT = "tasklist\0";
 
     /* Sorting constants. */
     const IMAP_SORT_DATE = 100;
@@ -230,7 +235,7 @@ class IMP
                 $mbox_list[] = array(
                     'l' => $GLOBALS['injector']->getInstance('Horde_Text_Filter')->filter($label, 'space2html', array('encode' => true)),
                     'sel' => (!empty($options['selected']) && ($mbox['val'] === $options['selected'])),
-                    'v' => htmlspecialchars($mbox['val'])
+                    'v' => self::formMbox($mbox['val'], true)
                 );
             }
         }
@@ -247,7 +252,7 @@ class IMP
                     $vfolder_list[] = array(
                         'l' => $GLOBALS['injector']->getInstance('Horde_Text_Filter')->filter($val, 'space2html', array('encode' => true)),
                         'sel' => ($vfolder_sel == $id),
-                        'v' => htmlspecialchars($imp_search->createSearchID($id))
+                        'v' => self::formMbox($imp_search->createSearchID($id), true)
                     );
                 }
                 $t->set('vfolder', $vfolder_list);
@@ -265,7 +270,7 @@ class IMP
                     foreach ($tasklists as $id => $tasklist) {
                         $tasklist_list[] = array(
                             'l' => $GLOBALS['injector']->getInstance('Horde_Text_Filter')->filter($tasklist->get('name'), 'space2html', array('encode' => true)),
-                            'v' => '\0tasklist_' . $id
+                            'v' => self::formMbox(self::TASKLIST_EDIT . $id, true)
                         );
                     }
                     $t->set('tasklist', $tasklist_list);
@@ -284,7 +289,7 @@ class IMP
                     foreach ($notepads as $id => $notepad) {
                         $notepad_list[] = array(
                             'l' => $GLOBALS['injector']->getInstance('Horde_Text_Filter')->filter($notepad->get('name'), 'space2html', array('encode' => true)),
-                            'v' => '\0notepad_' . $id
+                            'v' => self::formMbox(self::NOTEPAD_EDIT . $id, true)
                         );
                     }
                     $t->set('notepad', $notepad_list);
@@ -1060,8 +1065,8 @@ class IMP
     {
         if (is_null($mbox)) {
             $mbox = Horde_Util::getFormData('mailbox');
-            self::$mailbox = empty($mbox) ? 'INBOX' : $mbox;
-            self::$thismailbox = Horde_Util::getFormData('thismailbox', $mbox);
+            self::$mailbox = empty($mbox) ? 'INBOX' : self::formMbox($mbox, false);
+            self::$thismailbox = self::formMbox(Horde_Util::getFormData('thismailbox', $mbox), false);
             self::$uid = Horde_Util::getFormData('uid');
         } else {
             self::$mailbox = $mbox;
@@ -1187,4 +1192,22 @@ class IMP
         );
     }
 
+    /**
+     * Converts a mailbox to/from a valid representation that can be used
+     * in a form element.  Needed because null characters (used for various
+     * internal non-IMAP mailbox representations) will not work in form
+     * elements.
+     *
+     * @param string $mbox  The mailbox name.
+     * @param boolean $to   Convert to the form representation?
+     *
+     * @return string  The converted mailbox.
+     */
+    static public function formMbox($mbox, $to)
+    {
+        return $to
+            ? htmlspecialchars(rawurlencode($mbox), ENT_COMPAT, $GLOBALS['registry']->getCharset())
+            : rawurldecode($mbox);
+    }
+
 }
index 9cc4543..1d1b90e 100644 (file)
@@ -42,10 +42,10 @@ class IMP_Imap_Tree
     const NEXT_SHOWSUB = 2;
     const NEXT_NOCHILDREN = 4;
 
-    /* The string used to indicate the base of the tree. This must be null
-     * since this is the only 7-bit character not allowed in IMAP
+    /* The string used to indicate the base of the tree. This must include
+     * null since this is the only 7-bit character not allowed in IMAP
      * mailboxes. */
-    const BASE_ELT = '\0';
+    const BASE_ELT = "base\0";
 
     /* Defines used with folderList(). */
     const FLIST_CONTAINER = 1;
@@ -59,11 +59,11 @@ class IMP_Imap_Tree
 
     /* Add null to folder key since it allows us to sort by name but
      * never conflict with an IMAP mailbox. */
-    const VFOLDER_KEY = 'vfolder\0';
+    const VFOLDER_KEY = "vfolder\0";
 
     /* Defines used with namespace display. */
-    const SHARED_KEY = 'shared\0';
-    const OTHER_KEY = 'other\0';
+    const SHARED_KEY = "shared\0";
+    const OTHER_KEY = "other\0";
 
     /**
      * Tree changed flag.  Set when something in the tree has been altered.
index 5fa604c..28e33fa 100644 (file)
@@ -28,6 +28,7 @@ class IMP_LoginTasks_SystemTask_UpgradeFromImp4 extends Horde_LoginTasks_SystemT
     {
         $this->_upgradeAbookPrefs();
         $this->_upgradeForwardPrefs();
+        $this->_upgradeLoginTasks();
         $this->_upgradeSortPrefs();
         $this->_upgradeVirtualFolders();
     }
@@ -103,6 +104,19 @@ class IMP_LoginTasks_SystemTask_UpgradeFromImp4 extends Horde_LoginTasks_SystemT
     }
 
     /**
+     * Upgrade to the new login tasks preferences.
+     */
+    protected function _upgradeForwardPrefs()
+    {
+        global $prefs;
+
+        if (!$prefs->isDefault('initial_page') &&
+            ($prefs->getValue('initial_page') == 'folders.php')) {
+            $prefs->setValue('initial_page', IMP::PREF_FOLDER_PAGE);
+        }
+    }
+
+    /**
      * Check for old, non-existent sort values. See Bug #7296.
      */
     protected function _upgradeSortPrefs()
index bcfc5fd..98f5783 100644 (file)
@@ -64,15 +64,15 @@ class IMP_Message
 
         /* If the target is a tasklist, handle the move/copy specially. */
         if ($conf['tasklist']['use_tasklist'] &&
-            (strpos($targetMbox, '\0tasklist_') === 0)) {
-            $this->_createTasksOrNotes(str_replace('\0tasklist_', '', $targetMbox), $action, $indices, 'task');
+            (strpos($targetMbox, IMP::TASKLIST_EDIT) === 0)) {
+            $this->_createTasksOrNotes(str_replace(IMP::TASKLIST_EDIT, '', $targetMbox), $action, $indices, 'task');
             return true;
         }
 
         /* If the target is a notepad, handle the move/copy specially. */
         if ($conf['notepad']['use_notepad'] &&
-            (strpos($targetMbox, '\0notepad_') === 0)) {
-            $this->_createTasksOrNotes(str_replace('\0notepad_', '', $targetMbox), $action, $indices, 'note');
+            (strpos($targetMbox, IMP::NOTEPAD_EDIT) === 0)) {
+            $this->_createTasksOrNotes(str_replace(IMP::NOTEPAD_EDIT, '', $targetMbox), $action, $indices, 'note');
             return true;
         }
 
index 377d23c..26e4014 100644 (file)
@@ -421,7 +421,7 @@ class IMP_Prefs_Ui
             return false;
 
         case 'draftsselect':
-            return $this->_updateSpecialFolders('drafts_folder', $ui->vars->drafts, $ui->vars->drafts_folder_new, $ui);
+            return $this->_updateSpecialFolders('drafts_folder', IMP::formMbox($ui->vars->drafts, false), $ui->vars->drafts_folder_new, $ui);
 
         case 'encryptselect':
             return $prefs->setValue('default_encrypt', $ui->vars->default_encrypt);
@@ -431,7 +431,7 @@ class IMP_Prefs_Ui
             return false;
 
         case 'initialpageselect':
-            return $prefs->setValue('initial_page', $ui->vars->initial_page);
+            return $prefs->setValue('initial_page', IMP::formMbox($ui->vars->initial_page, false));
 
         case 'pgpprivatekey':
             $this->_updatePgpPrivateKey($ui);
@@ -462,7 +462,7 @@ class IMP_Prefs_Ui
             return $this->_updateSource($ui);
 
         case 'spamselect':
-            return $this->_updateSpecialFolders('spam_folder', $ui->vars->spam, $ui->vars->spam_new, $ui);
+            return $this->_updateSpecialFolders('spam_folder', IMP::formMbox($ui->vars->spam, false), $ui->vars->spam_new, $ui);
 
         case 'stationerymanagement':
             return $this->_updateStationeryManagement($ui);
@@ -668,7 +668,7 @@ class IMP_Prefs_Ui
 
         $folder = empty($ui->vars->folder)
             ? 'INBOX'
-            : $ui->vars->folder;
+            : IMP::formMbox($ui->vars->folder, false);
 
         try {
             $curr_acl = $acl->getACL($folder);
@@ -683,7 +683,7 @@ class IMP_Prefs_Ui
 
         $t->set('options', IMP::flistSelect(array('selected' => $folder)));
         $t->set('current', sprintf(_("Current access to %s"), IMP::displayFolder($folder)));
-        $t->set('folder', $folder);
+        $t->set('folder', IMP::formMbox($folder, true));
         $t->set('noacl', !count($curr_acl));
         $t->set('maxrule', 1);
 
@@ -763,6 +763,7 @@ class IMP_Prefs_Ui
         }
 
         $acl = $GLOBALS['injector']->getInstance('IMP_Imap_Acl');
+        $folder = IMP::formMbox($ui->vars->folder, false);
         $rights = array_keys($acl->getRights());
 
         $acl_list = $ui->vars->acl;
@@ -777,11 +778,11 @@ class IMP_Prefs_Ui
                 $acl_list[$new_user] = $ui->vars->new_acl;
             } else {
                 try {
-                    $acl->editACL($ui->vars->folder, $new_user, $ui->vars->new_acl);
+                    $acl->editACL($folder, $new_user, $ui->vars->new_acl);
                     if (count($ui->vars->new_acl)) {
-                        $GLOBALS['notification']->push(sprintf(_("User \"%s\" successfully given the specified rights for the folder \"%s\"."), $new_user, IMP::getLabel($ui->vars->folder)), 'horde.success');
+                        $GLOBALS['notification']->push(sprintf(_("User \"%s\" successfully given the specified rights for the folder \"%s\"."), $new_user, IMP::getLabel($folder)), 'horde.success');
                     } else {
-                        $GLOBALS['notification']->push(sprintf(_("All rights on folder \"%s\" successfully removed for user \"%s\"."), IMP::getLabel($ui->vars->folder), $new_user), 'horde.success');
+                        $GLOBALS['notification']->push(sprintf(_("All rights on folder \"%s\" successfully removed for user \"%s\"."), IMP::getLabel($folder), $new_user), 'horde.success');
                     }
                 } catch (IMP_Exception $e) {
                     $GLOBALS['notification']->push($e);
@@ -791,7 +792,7 @@ class IMP_Prefs_Ui
         }
 
         try {
-            $curr_acl = $acl->getACL($ui->vars->folder);
+            $curr_acl = $acl->getACL($folder);
         } catch (IMP_Exception $e) {
             $GLOBALS['notification']->notify($e);
             return;
@@ -833,13 +834,13 @@ class IMP_Prefs_Ui
                  * but ignore c and d rights that are sent for BC reasons (RFC
                  * 4314 [2.1.1]. */
                 $val = array_merge($val, array_diff($curr_acl[$user], array_merge($rights, array('c', 'd'))));
-                $acl->editACL($ui->vars->folder, $user, $val);
+                $acl->editACL($folder, $user, $val);
                 if (!count($val)) {
                     if (isset($curr_acl[$user])) {
-                        $GLOBALS['notification']->push(sprintf(_("All rights on folder \"%s\" successfully removed for user \"%s\"."), IMP::getLabel($ui->vars->folder), $user), 'horde.success');
+                        $GLOBALS['notification']->push(sprintf(_("All rights on folder \"%s\" successfully removed for user \"%s\"."), IMP::getLabel($folder), $user), 'horde.success');
                     }
                 } else {
-                    $GLOBALS['notification']->push(sprintf(_("User \"%s\" successfully given the specified rights for the folder \"%s\"."), $user, IMP::getLabel($ui->vars->folder)), 'horde.success');
+                    $GLOBALS['notification']->push(sprintf(_("User \"%s\" successfully given the specified rights for the folder \"%s\"."), $user, IMP::getLabel($folder)), 'horde.success');
                 }
             } catch (IMP_Exception $e) {
                 $GLOBALS['notification']->push($e);
@@ -1013,7 +1014,8 @@ class IMP_Prefs_Ui
             $t->set('nofolder', true);
         } else {
             $mailbox_selected = $GLOBALS['prefs']->getValue('initial_page');
-            $t->set('folder_sel', $mailbox_selected == 'folders.php');
+            $t->set('folder_page', IMP::formMbox(IMP::PREF_FOLDER_PAGE, true));
+            $t->set('folder_sel', $mailbox_selected == IMP::PREF_FOLDER_PAGE);
             $t->set('flist', IMP::flistSelect(array(
                 'inc_vfolder' => true,
                 'selected' => $mailbox_selected
@@ -1242,6 +1244,7 @@ class IMP_Prefs_Ui
         $t = $GLOBALS['injector']->createInstance('Horde_Template');
         $t->setOption('gettext', true);
 
+        $t->set('default', IMP::formMbox(IMP::PREF_DEFAULT, true));
         $t->set('label', Horde::label('sent_mail_folder', _("Sent mail folder:")));
         $t->set('flist', IMP::flistSelect(array(
             'filter' => array('INBOX'),
@@ -1267,15 +1270,11 @@ class IMP_Prefs_Ui
             return false;
         }
 
-        $sent_mail_folder = $ui->vars->sent_mail_folder;
+        $sent_mail_folder = IMP::formMbox($ui->vars->sent_mail_folder, false);
         if (empty($sent_mail_folder) && $ui->vars->sent_mail_folder_new) {
             $sent_mail_folder = $GLOBALS['injector']->getInstance('IMP_Imap')->getOb()->appendNamespace(Horde_String::convertCharset($ui->vars->sent_mail_folder_new, $GLOBALS['registry']->getCharset(), 'UTF7-IMAP'));
-        } elseif (($sent_mail_folder == "\1default") &&
+        } elseif (($sent_mail_folder == IMP::PREF_DEFAULT) &&
                   ($sm_default = $prefs->getDefault('sent_mail_folder'))) {
-            /* Use \1 as the prefix to differentiate from a IMAP mailbox
-             * potentially named 'default'. \1 is theoretically a valid
-             * IMAP mailbox character, but it is highly unlikely it exists
-             * anywhere (and we can't use \0 in form data). */
             $sent_mail_folder = $GLOBALS['injector']->getInstance('IMP_Imap')->getOb()->appendNamespace($sm_default);
         }
 
@@ -1706,7 +1705,9 @@ class IMP_Prefs_Ui
     {
         global $prefs;
 
-        if ($ui->vars->trash == IMP::PREF_VTRASH) {
+        $trash = IMP::formMbox($ui->vars->trash, false);
+
+        if ($trash == IMP::PREF_VTRASH) {
             if ($prefs->isLocked('use_vtrash')) {
                 return false;
             }
@@ -1718,7 +1719,7 @@ class IMP_Prefs_Ui
                 return false;
             }
 
-            if ($this->_updateSpecialFolders('trash_folder', $ui->vars->trash, $ui->vars->trash_new, $ui)) {
+            if ($this->_updateSpecialFolders('trash_folder', $trash, $ui->vars->trash_new, $ui)) {
                 $prefs->setValue('use_vtrash', 0);
                 $prefs->setDirty('trash_folder', true);
                 return true;
index 74c4987..e5dabe4 100644 (file)
@@ -64,7 +64,7 @@
 class IMP_Search
 {
     /* The mailbox search prefix. */
-    const MBOX_PREFIX = 'impsearch\0';
+    const MBOX_PREFIX = "impsearch\0";
 
     /* The special search mailbox names. */
     const BASIC_SEARCH = 'impbsearch';
index 594d9a5..3de12f5 100644 (file)
@@ -480,7 +480,7 @@ if ($pageOb['msgcount']) {
     }
 
     $n_template->set('mailbox_url', $mailbox_url);
-    $n_template->set('mailbox', htmlspecialchars(IMP::$mailbox));
+    $n_template->set('mailbox', IMP::formMbox(IMP::$mailbox, true));
     if ($pageOb['pagecount'] > 1) {
         $n_template->set('multiple_page', true);
         $n_template->set('pages_first', $pages_first);
@@ -651,7 +651,7 @@ foreach ($headers as $key => $val) {
 
 /* Output the form start. */
 $f_template = $injector->createInstance('Horde_Template');
-$f_template->set('mailbox', htmlspecialchars(IMP::$mailbox));
+$f_template->set('mailbox', IMP::formMbox(IMP::$mailbox, true));
 $f_template->set('mailbox_token', $mailbox_token);
 $f_template->set('mailbox_url', $mailbox_url);
 $f_template->set('sessiontag', Horde_Util::formInput());
index 7e98176..9fa8b46 100644 (file)
@@ -408,7 +408,7 @@ if ($search_mbox) {
 $t_template = $injector->createInstance('Horde_Template');
 $t_template->set('message_url', $message_url);
 $t_template->set('form_input', Horde_Util::formInput());
-$t_template->set('mailbox', htmlspecialchars(IMP::$mailbox));
+$t_template->set('mailbox', IMP::formMbox(IMP::$mailbox, true));
 $t_template->set('thismailbox', htmlspecialchars($mailbox_name));
 $t_template->set('start', htmlspecialchars($msgindex));
 $t_template->set('uid', htmlspecialchars($uid));
@@ -425,7 +425,7 @@ $n_template->set('usepop', $use_pop);
 $n_template->set('id', 1);
 
 if (!$use_pop) {
-    $n_template->set('mailbox', IMP::$mailbox);
+    $n_template->set('mailbox', IMP::formMbox(IMP::$mailbox, true));
 
     $tmp = $imp_flags->getFlagList(IMP::$mailbox);
     $n_template->set('flaglist_set', $tmp['set']);
index 36a9320..4e6a7d8 100644 (file)
@@ -3,7 +3,7 @@
 </if:heading>
 <if:new_mbox>
 <option value="" disabled="disabled">- - - - - - - -</option>
-<option value="&#000;create"><gettext>Create New Mailbox</gettext></option>
+<option class="flistCreate" value=""><gettext>Create New Mailbox</gettext></option>
 <option value="" disabled="disabled">- - - - - - - -</option>
 </if:new_mbox>
 <loop:mbox>
index 88e6bf8..9109717 100644 (file)
@@ -7,7 +7,8 @@
 <if:nofolder>
    <option value="INBOX" selected="selected"><gettext>Inbox</gettext></option>
 <else:nofolder>
-   <option value="folders.php"<if:foldersel> selected="selected"</if:foldersel>><gettext>Folder Navigator</gettext></option>
+   <option value="<tag:folder_page />"<if:foldersel> selected="selected"</if:foldersel>><gettext>Folder Navigator</gettext></option>
+   <option value="" disabled="disabled">- - - - - - - -</option>
    <tag:flist />
 </else:nofolder></if:nofolder>
   </select>
index 54ce6c0..16c6f92 100644 (file)
@@ -5,7 +5,7 @@
  <div>
   <select name="sent_mail_folder" id="sent_mail_folder">
    <option value=""><gettext>None</gettext></option>
-   <option value="&#001;default" selected="selected"><gettext>Use Default Value</gettext></option>
+   <option value="<tag:default />" selected="selected"><gettext>Use Default Value</gettext></option>
    <tag:flist />
   </select>
  </div>